diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 349f74d..47a0af7 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -48,6 +48,10 @@ jobs: run: yarn install --immutable - name: Generate sitemap + env: + # For repository named username.github.io, use https://username.github.io + # For other repositories, use https://username.github.io/repository-name + SITE_URL: ${{ github.event.repository.name == format('{0}.github.io', github.repository_owner) && format('https://{0}.github.io', github.repository_owner) || format('https://{0}.github.io/{1}', github.repository_owner, github.event.repository.name) }} run: yarn run generate-sitemap - name: Upload build artifact, ready for GitHub Pages deployment uses: actions/upload-pages-artifact@v3 diff --git a/.htmlvalidate.mjs b/.htmlvalidate.mjs index 56a5106..ed20daa 100644 --- a/.htmlvalidate.mjs +++ b/.htmlvalidate.mjs @@ -4,6 +4,20 @@ export default defineConfig({ plugins: ["/test/plugin.html-validate.mjs"], extends: ["html-validate:prettier", "/test/plugin.html-validate.mjs:recommended"], rules: { + "allowed-links": [ + "error", + { + allowExternal: { + exclude: ["\\\\?utm_source=chatgpt.com", ".htm[l]?$"], + }, + allowRelative: { + exclude: [".htm[l]?$"], + }, + allowAbsolute: { + exclude: [".htm[l]?$"], + }, + }, + ], "pacific-medical-training/mailto-awesome": "error", "pacific-medical-training/external-links": "error", "pacific-medical-training/no-jquery": "error", diff --git a/package.json b/package.json index a78362a..790799e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "structured-data-testing-tool": "^4.5.0" }, "scripts": { - "test": "yarn node test/fixtures-html-validate-should-fail.mjs && yarn node test/fixtures-structured-data-should-fail.mjs && yarn node test/build-html-validate.mjs && yarn node test/dirty-words-checker.mjs && yarn node test/dirty-file-paths-checker.mjs && yarn node test/build-structured-data-validate.mjs", + "test": "yarn node test/fixtures-html-validate-should-fail.mjs && yarn node test/fixtures-structured-data-should-fail.mjs && yarn node test/build-html-validate.mjs && yarn node test/dirty-file-paths-checker.mjs && yarn node test/build-structured-data-validate.mjs", "test-structured-data": "yarn node test/build-structured-data-validate.mjs", "lint": "yarn prettier-check && yarn markdownlint-check", "lint-fix": "yarn prettier-fix && yarn markdownlint-fix", diff --git a/scripts/generate-sitemap.mjs b/scripts/generate-sitemap.mjs index 2e9c758..abf2744 100644 --- a/scripts/generate-sitemap.mjs +++ b/scripts/generate-sitemap.mjs @@ -3,7 +3,9 @@ import path from "path"; import https from "https"; import { parseString } from "xml2js"; // Using xml2js for XML parsing -const site = "https://www.acls.net"; +// Site URL configuration - can be overridden via SITE_URL environment variable +// For GitHub Pages, this should typically be https://username.github.io/repository-name +const site = process.env.SITE_URL || "https://www.acls.net"; const buildFolderPath = path.join(process.cwd(), "build"); const sitemapPath = path.join(buildFolderPath, "sitemap.xml"); const daysThreshold = 30; // Number of days to compare for updating lastmod diff --git a/test/dirty-words-checker.mjs b/test/dirty-words-checker.mjs deleted file mode 100644 index 0b3336d..0000000 --- a/test/dirty-words-checker.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { glob } from "glob"; - -const CONFIG_FILE = path.join(process.cwd(), "test", "dirty-words.json"); -const BUILD_DIR = path.join(process.cwd(), "build"); - -// Load and parse the configuration file -function loadConfig() { - try { - const configContent = fs.readFileSync(CONFIG_FILE, "utf-8"); - return JSON.parse(configContent); - } catch (error) { - console.error("Error loading configuration:", error.message); - process.exit(1); - } -} - -// Check all files in the build directory -function findTargetFiles() { - return glob - .sync("**/*", { - cwd: BUILD_DIR, - nocase: true, - dot: false, - }) - .filter((file) => { - const fullPath = path.join(BUILD_DIR, file); - return fs.lstatSync(fullPath).isFile(); - }); -} - -// Check a single file against all patterns -function checkFile(filePath, patterns) { - const fullPath = path.join(BUILD_DIR, filePath); - const content = fs.readFileSync(fullPath, "utf-8"); - const violations = []; - - patterns.forEach((pattern) => { - const flags = pattern.ignoreCase ? "gi" : "g"; - const regex = new RegExp(pattern.regexp, flags); - - const matches = content.match(regex); - if (matches) { - violations.push({ - pattern: pattern, - matches: matches, - count: matches.length, - }); - } - }); - - return violations; -} - -console.log("🧪 Testing files for dirty words"); - -const config = loadConfig(); -const files = findTargetFiles(); -let hasErrors = false; - -files.forEach((file) => { - const violations = checkFile(file, config); - - if (violations.length > 0) { - hasErrors = true; - console.log(`❌ ${file}:`); - - violations.forEach((violation) => { - const { pattern, matches, count } = violation; - console.error(` ${pattern.severity.toUpperCase()}: ${pattern.note}`); - console.log(` Found ${count} matches:`); - matches.forEach((match) => { - console.log(` "${match}"`); - }); - }); - } -}); - -if (hasErrors) { - console.error("\n❌ Dirty words check failed"); - process.exit(1); -} else { - console.log("✨ All files passed dirty words check!\n"); -} diff --git a/test/dirty-words.json b/test/dirty-words.json deleted file mode 100644 index 5c2a6fa..0000000 --- a/test/dirty-words.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "regexp": "\\?utm_source=chatgpt.com", - "severity": "error", - "ignoreCase": true, - "note": "Copied a link from Chat GPT without reviewing it" - }, - { - "regexp": "<\\s*title\\s*>[^<]*\\&amp;[^<]*", - "severity": "error", - "ignoreCase": true, - "note": "Double-escaped HTML quoting" - }, - { - "regexp": "href\\s*=\\s*\"[^:]+\\.htm[l]?\"", - "severity": "error", - "ignoreCase": true, - "note": "Linking to an HTML page with the extension .html or .htm" - } -] diff --git a/test/fixtures/required-results.json b/test/fixtures/required-results.json index b0b9eae..3944351 100644 --- a/test/fixtures/required-results.json +++ b/test/fixtures/required-results.json @@ -13,6 +13,18 @@ } ], "test/fixtures/external-link-broken.html": [ + { + "ruleId": "allowed-links", + "severity": 2, + "message": "External link to this destination is not allowed by current configuration", + "offset": 204, + "line": 9, + "column": 14, + "size": 66, + "selector": "html > body > a:nth-child(1)", + "ruleUrl": "https://html-validate.org/rules/allowed-links.html", + "context": "external" + }, { "ruleId": "pacific-medical-training/external-links", "severity": 2, @@ -59,6 +71,18 @@ } ], "test/fixtures/internal-link-broken.html": [ + { + "ruleId": "allowed-links", + "severity": 2, + "message": "Absolute link to this destination is not allowed by current configuration", + "offset": 204, + "line": 9, + "column": 14, + "size": 36, + "selector": "html > body > a", + "ruleUrl": "https://html-validate.org/rules/allowed-links.html", + "context": "absolute" + }, { "ruleId": "pacific-medical-training/internal-links", "severity": 2,