mirror of
https://github.com/IliesAllali/parenthese.git
synced 2026-11-06 23:32:08 +00:00
Landing : pages préchargées au survol d'un lien (speculationrules)
- Règles de préchargement dans l'accueil et les 15 guides : une page du site est préparée quand on survole son lien (appui sur téléphone), et s'ouvre presque tout de suite. Pages seulement, pas les fichiers ; l'application n'est pas concernée. Ignoré par les navigateurs qui ne connaissent pas ces règles. - Une page préchargée ne compte pas de visite avant d'être vraiment ouverte (PostHog attend prerenderingchange), et l'animation du logo se joue à l'ouverture, pas pendant le préchargement. - CSP de la landing (deploy/nginx.conf) : empreinte des règles, et nouvelle empreinte du script du logo. Le mot-clé 'inline-speculation-rules' ne suffisait pas à Chrome. - Test : chaque script en ligne des pages a son empreinte dans la CSP, chaque page a ses règles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c45bcb0463
commit
fcfb7d1494
20 files changed
+121
-5
No files matched your search
+5
-1
@@ -33,12 +33,16 @@ map $host $parenthese_app_csp {
|
|||||||
# script-src 'sha256-...' script en ligne du <head> (animation du logo, dans index.html et en/index.html).
|
# script-src 'sha256-...' script en ligne du <head> (animation du logo, dans index.html et en/index.html).
|
||||||
# S'il change, recalculer l'empreinte : base64(sha256(contenu exact entre
|
# S'il change, recalculer l'empreinte : base64(sha256(contenu exact entre
|
||||||
# <script> et </script>)), sinon l'animation ne se joue plus (sans autre effet).
|
# <script> et </script>)), sinon l'animation ne se joue plus (sans autre effet).
|
||||||
|
# 2e 'sha256-...' règles de préchargement des pages (<script type="speculationrules">, le même
|
||||||
|
# texte dans index.html et chaque guide). Même calcul s'il change, sinon les pages
|
||||||
|
# ne sont plus préchargées (sans autre effet). Le mot-clé 'inline-speculation-rules'
|
||||||
|
# ne suffisait pas à Chrome (essayé le 27/10).
|
||||||
# posthog comme pour l'app
|
# posthog comme pour l'app
|
||||||
# style-src 'unsafe-inline' attributs style du rendu serveur React
|
# style-src 'unsafe-inline' attributs style du rendu serveur React
|
||||||
# frame-src démonstration de l'app en iframe
|
# frame-src démonstration de l'app en iframe
|
||||||
# frame-ancestors 'none' équivalent de X-Frame-Options DENY
|
# frame-ancestors 'none' équivalent de X-Frame-Options DENY
|
||||||
map $host $parenthese_landing_csp {
|
map $host $parenthese_landing_csp {
|
||||||
default "default-src 'self'; script-src 'self' 'sha256-Av5a1/yoW/tQ2ZQWqH5FnaL896Bubcs9ivdFvsKZBrU='; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https://us.i.posthog.com https://us-assets.i.posthog.com; worker-src 'self' blob:; frame-src https://app.parenthese.io; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'";
|
default "default-src 'self'; script-src 'self' 'sha256-v68KpM0KHUkaezvOmKtoCY2RsfbwoEC9EZYD682DCiU=' 'sha256-eflWcNn8MfPvX2YD4hXJnR3x9nRGu0f7tIEq2j/yfPo='; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https://us.i.posthog.com https://us-assets.i.posthog.com; worker-src 'self' blob:; frame-src https://app.parenthese.io; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'";
|
||||||
}
|
}
|
||||||
|
|
||||||
# Réponses de l'API relayées (JSON, médias des familles) : intégrables seulement par l'app (PDF en iframe).
|
# Réponses de l'API relayées (JSON, médias des familles) : intégrables seulement par l'app (PDF en iframe).
|
||||||
|
|||||||
+5
-1
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#FBFAF7" />
|
<meta name="theme-color" content="#FBFAF7" />
|
||||||
<!-- Arrivée du logo : une fois par visite, décidée avant le premier affichage (animation dans src/index.css) -->
|
<!-- Arrivée du logo : une fois par visite, décidée avant le premier affichage (animation dans src/index.css) -->
|
||||||
<script>try{if(!sessionStorage.getItem('pz-logo')&&!matchMedia('(prefers-reduced-motion: reduce)').matches){document.documentElement.classList.add('logo-intro');sessionStorage.setItem('pz-logo','1')}}catch(e){}</script>
|
<script>(function(){function intro(){try{if(!sessionStorage.getItem('pz-logo')&&!matchMedia('(prefers-reduced-motion: reduce)').matches){document.documentElement.classList.add('logo-intro');sessionStorage.setItem('pz-logo','1')}}catch(e){}}if(document.prerendering)document.addEventListener('prerenderingchange',intro,{once:true});else intro()})()</script>
|
||||||
|
|
||||||
<title>Parenthèse : l'arbre généalogique vivant de votre famille</title>
|
<title>Parenthèse : l'arbre généalogique vivant de votre famille</title>
|
||||||
<meta name="description" content="Créez l'arbre généalogique de votre famille en ligne, avec photos, vidéos et voix. Partagez-le en un lien : la famille consulte sans compte et contribue avec un mot de passe." />
|
<meta name="description" content="Créez l'arbre généalogique de votre famille en ligne, avec photos, vidéos et voix. Partagez-le en un lien : la famille consulte sans compte et contribue avec un mot de passe." />
|
||||||
@@ -72,6 +72,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Sans JavaScript, le contenu prérendu reste visible (les révélations au défilement demandent JS) -->
|
<!-- Sans JavaScript, le contenu prérendu reste visible (les révélations au défilement demandent JS) -->
|
||||||
|
|||||||
@@ -51,6 +51,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -49,6 +49,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -63,6 +63,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -65,6 +65,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -54,6 +54,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -35,6 +35,10 @@
|
|||||||
"publisher": { "@type": "Person", "name": "Ilies Allali", "email": "pro.allali.ilies@gmail.com" }
|
"publisher": { "@type": "Person", "name": "Ilies Allali", "email": "pro.allali.ilies@gmail.com" }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -54,6 +54,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -49,6 +49,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -51,6 +51,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -65,6 +65,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -63,6 +63,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -35,6 +35,10 @@
|
|||||||
"publisher": { "@type": "Person", "name": "Ilies Allali", "email": "pro.allali.ilies@gmail.com" }
|
"publisher": { "@type": "Person", "name": "Ilies Allali", "email": "pro.allali.ilies@gmail.com" }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -47,6 +47,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -47,6 +47,10 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Pages du site préparées en avance quand on survole un lien (appui sur téléphone) : elles s'ouvrent presque
|
||||||
|
tout de suite. Seulement les pages, pas les fichiers ; l'application (app.parenthese.io) n'est pas concernée.
|
||||||
|
Un navigateur qui ne connaît pas ces règles les ignore. -->
|
||||||
|
<script type="speculationrules">{"prerender":[{"where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":"/*.*"}}]},"eagerness":"moderate"}]}</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top">
|
<header class="top">
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ import App from './App.jsx'
|
|||||||
import { analytics } from './analytics.js'
|
import { analytics } from './analytics.js'
|
||||||
import { localeFromPath } from './i18n.js'
|
import { localeFromPath } from './i18n.js'
|
||||||
|
|
||||||
// PostHog après le premier rendu : les événements émis avant sont mis en file et envoyés à l'init
|
// PostHog après le premier rendu : les événements émis avant sont mis en file et envoyés à l'init.
|
||||||
|
// Page préchargée (survol d'un lien, speculationrules) : rien ne part avant qu'on l'ouvre vraiment.
|
||||||
const deferInit = () => analytics.init()
|
const deferInit = () => analytics.init()
|
||||||
if ('requestIdleCallback' in window) window.requestIdleCallback(deferInit, { timeout: 3000 })
|
const scheduleInit = () => {
|
||||||
else window.setTimeout(deferInit, 1500)
|
if ('requestIdleCallback' in window) window.requestIdleCallback(deferInit, { timeout: 3000 })
|
||||||
|
else window.setTimeout(deferInit, 1500)
|
||||||
|
}
|
||||||
|
if (document.prerendering) document.addEventListener('prerenderingchange', scheduleInit, { once: true })
|
||||||
|
else scheduleInit()
|
||||||
|
|
||||||
const container = document.getElementById('root')
|
const container = document.getElementById('root')
|
||||||
const app = (
|
const app = (
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Les scripts en ligne de la landing (animation du logo, règles de préchargement des pages) ne passent la CSP de
|
||||||
|
// nginx que par leur empreinte exacte (deploy/nginx.conf). Un script modifié sans recalculer son empreinte ne
|
||||||
|
// casse rien de visible : l'animation ou le préchargement s'arrêtent sans bruit. Ce test le dit tout de suite.
|
||||||
|
import { test } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { createHash } from 'node:crypto'
|
||||||
|
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const landing = fileURLToPath(new URL('..', import.meta.url))
|
||||||
|
const csp = readFileSync(path.join(landing, '../deploy/nginx.conf'), 'utf8').match(/map \$host \$parenthese_landing_csp \{\s*default "([^"]+)"/)[1]
|
||||||
|
const autorisees = new Set([...csp.matchAll(/'sha256-([^']+)'/g)].map((m) => m[1]))
|
||||||
|
const empreinte = (texte) => createHash('sha256').update(texte, 'utf8').digest('base64')
|
||||||
|
|
||||||
|
function pages(dossier) {
|
||||||
|
return readdirSync(dossier).flatMap((nom) => {
|
||||||
|
const chemin = path.join(dossier, nom)
|
||||||
|
if (statSync(chemin).isDirectory()) return pages(chemin)
|
||||||
|
return nom === 'index.html' ? [chemin] : []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const toutes = [path.join(landing, 'index.html'), ...pages(path.join(landing, 'public'))]
|
||||||
|
|
||||||
|
test('chaque script en ligne des pages a son empreinte dans la CSP de la landing', () => {
|
||||||
|
for (const fichier of toutes) {
|
||||||
|
const html = readFileSync(fichier, 'utf8')
|
||||||
|
for (const [, attributs, contenu] of html.matchAll(/<script([^>]*)>([\s\S]*?)<\/script>/g)) {
|
||||||
|
if (/\bsrc=|application\/ld\+json/.test(attributs)) continue
|
||||||
|
assert.ok(autorisees.has(empreinte(contenu)), `${path.relative(landing, fichier)} : script en ligne sans empreinte dans deploy/nginx.conf (${empreinte(contenu)})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chaque page a ses règles de préchargement', () => {
|
||||||
|
for (const fichier of toutes) {
|
||||||
|
assert.match(readFileSync(fichier, 'utf8'), /<script type="speculationrules">/, path.relative(landing, fichier))
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in new issue
Block a user