Skip to content

Commit

Permalink
update build and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fulldecent committed Sep 8, 2024
1 parent ff12f25 commit d365ab8
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 90 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/build-test-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ concurrency:
cancel-in-progress: false

jobs:
# Build using Jekyll, save GitHub Pages artifact and build artifact
# Build using Jekyll, and Node.js, save GitHub Pages artifact and build artifact
build:
name: Jekyll build
runs-on: ubuntu-latest
Expand All @@ -36,10 +36,13 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "yarn"
- name: Setup yarn using corepack
run: corepack enable
- name: Install dependencies
run: npm install xml2js front-matter
run: yarn install --immutable
- name: Generate sitemap
run: node generate-sitemap.js
run: yarn run generate-sitemap
- name: Upload build artifact, ready for GitHub Pages deployment
uses: actions/upload-pages-artifact@v3
with:
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
- name: Prettify code
uses: creyD/[email protected]
with:
prettier_options: --check "**/*.{js,css,html,htm}"
prettier_options: --check "**/*.{js,css,html}"

markdownlint:
runs-on: ubuntu-latest

Expand All @@ -31,8 +31,9 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
node-version: 'lts/*'
cache: 'yarn'
- name: Install markdownlint-cli
run: npm install -g markdownlint-cli
- name: Run markdownlint
run: markdownlint "**/*.md"
run: markdownlint "**/*.md"
16 changes: 12 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# Specific choices in this project

## Build
# For Jekyll
.jekyll-metadata
.jekyll-cache
/build
## For our testing scripts

# For our testing scripts
/cache

# For DS Store
**/.DS_Store

# Yarn Berry
# Ideas: https://github.com/yarnpkg/berry/issues/454
/.yarn

################################################################################
## IMPORT FROM https://github.com/github/gitignore/blob/main/GitHubPages.gitignore
################################################################################
Expand Down
5 changes: 0 additions & 5 deletions .prettierrc

This file was deleted.

10 changes: 10 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This has to be .prettierrc.js, not .prettierrc because of Yarn 4+ and pluggins
// Notes: https://dev.to/javien/how-to-use-prettier-plugin-with-yarn-pnp-in-vscode-4pf8

/** @type {import("prettier").Options} */
module.exports = {
printWidth: 120,
singleQuote: true,
tabWidth: 2,
plugins: [require.resolve('@shopify/prettier-plugin-liquid')],
};
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"redhat.vscode-yaml", // YAML for front matter
"eliostruyf.vscode-front-matter", // Front Matter management
"davidanson.vscode-markdownlint",
"dbaeumer.vscode-eslint"
"dbaeumer.vscode-eslint",
"arcanis.vscode-zipfs"
]
}
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@
"activityBar.background": "#4D210A",
"titleBar.activeBackground": "#6B2F0E",
"titleBar.activeForeground": "#FEFAF7"
}
},
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"prettier.prettierPath": ".yarn/sdks/prettier/index.cjs"
}
16 changes: 14 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
{
"license": "UNLICENSED",
"devDependencies": {
"@shopify/prettier-plugin-liquid": "^1.5.0",
"better-sqlite3": "^9.6.0",
"glob": "^11.0.0",
"html-validate": "^8.21.0",
"prettier": "^3.3.3",
"shell-quote": "^1.8.1"
},
"scripts": {
"test": "node test/fixtures-html-validate-should-fail.mjs && node test/build-html-validate.mjs"
"test": "node test/fixtures-html-validate-should-fail.mjs && node test/build-html-validate.mjs",
"generate-sitemap": "node scripts/generate-sitemap.mjs"
},
"packageManager": "[email protected]"
"packageManager": "[email protected]+sha512.f825273d0689cc9ead3259c14998037662f1dcd06912637b21a450e8da7cfeb4b1965bbee73d16927baa1201054126bc385c6f43ff4aa705c8631d26e12460f1",
"dependencies": {
"front-matter": "^4.0.2",
"xml2js": "^0.6.2"
},
"dependenciesMeta": {
"@shopify/[email protected]": {
"unplugged": true
}
}
}
19 changes: 9 additions & 10 deletions generate-sitemap.js → scripts/generate-sitemap.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const fs = require('fs');
const path = require('path');
const https = require('https');
const { parseString } = require('xml2js'); // Using xml2js for XML parsing
import fs from 'fs';
import path from 'path';
import https from 'https';
import { parseString } from 'xml2js'; // Using xml2js for XML parsing

const site = 'https://fulldecent.github.io/github-pages-template';
const buildFolderPath = path.join(__dirname, 'build');
const sourceFolderPath = path.join(__dirname, 'build');
const sitemapPath = path.join(sourceFolderPath, 'sitemap.xml');
const site = '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

// Function to generate sitemap XML content
Expand Down Expand Up @@ -40,7 +39,7 @@ function getHTMLFiles(dir, fileList) {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
getHTMLFiles(filePath, fileList);
} else if (path.extname(file) === '.html' || path.extname(file) === '.htm') {
} else if (path.extname(file) === '.html') {
const content = fs.readFileSync(filePath, 'utf8');
const experimentMetaTag = content.match(/<meta\s+name=["']experiment["']\s+content=["']true["']\s*\/?>/i);
if (!experimentMetaTag) {
Expand Down Expand Up @@ -98,4 +97,4 @@ function generateAndWriteSitemap(lastmodDate) {
if (err) throw err;
console.log('Sitemap.xml generated successfully!');
});
}
}
45 changes: 24 additions & 21 deletions test/build-html-validate.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Documentation: https://html-validate.org/dev/using-api.html

import { HtmlValidate, formatterFactory } from 'html-validate';
import { glob } from 'glob';
import plugin from './plugin.html-validate.mjs';
const targets = glob.sync('build/**/*.html');
// We prefer to use FileSystemConfigLoader, see https://gitlab.com/html-validate/html-validate/-/issues/230#note_1670756378

// Find and sort all HTML files in the 'build' directory
const targets = glob.sync('build/**/*.html').sort();

// Initialize HtmlValidate instance
const htmlValidate = new HtmlValidate({
extends: ['html-validate:prettier'],
plugins: [plugin],
Expand All @@ -18,26 +19,28 @@ const htmlValidate = new HtmlValidate({
'internal-links': 'error',
},
});

const formatter = formatterFactory('stylish');
var allTestsPassed = true;
let allTestsPassed = true;

const validateTargets = async () => {
for (const target of targets) {
try {
const report = await htmlValidate.validateFile(target);
if (!report.valid) {
console.log(formatter(report.results));
allTestsPassed = false;
} else {
console.log('✅ ' + target);
}
} catch (error) {
console.error(`Error validating ${target}:`, error);
// Validate each target file
for (const target of targets) {
try {
const report = await htmlValidate.validateFile(target);
if (!report.valid) {
console.log(formatter(report.results));
allTestsPassed = false;
} else {
console.log(`✅ ${target}`);
}
} catch (error) {
console.error(`Error validating ${target}:`, error);
allTestsPassed = false;
}
}

process.exit(allTestsPassed ? 0 : 1);
};

validateTargets();
if (allTestsPassed) {
console.log('✨✨ All tests passed! ✨✨');
} else {
process.exit(1);
}
6 changes: 5 additions & 1 deletion test/plugin.html-validate.external-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const USER_AGENT =
// Use your proxy server to check external links
// This URL must accept a query parameter `url` and return the status code and possibly location: header in the response.
// Status code 500 is returned if the server is down or timeout.
const PROXY_URL = null;
const PROXY_URL = 'https://api.PacificMedicalTraining.com/link-check/status';

// html-validate runs check() synchronously, so we can't use async functions like fetch here. Maybe after their
// version 9 release we can use the fetch API and this parallel approach.
Expand Down Expand Up @@ -152,6 +152,10 @@ export default class ExternalLinksRule extends Rule {
domReady({ document }) {
const aElements = document.getElementsByTagName('a');
for (const aElement of aElements) {
if (!aElement.hasAttribute('href')) {
continue;
}

const href = aElement.getAttribute('href').value;
if (!href || !/^https?:\/\//i.test(href)) {
continue;
Expand Down
1 change: 1 addition & 0 deletions test/plugin.html-validate.https-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export default class EnsureHttpsRules extends Rule {
domReady({ document }) {
const aElements = document.getElementsByTagName('a');
for (const aElement of aElements) {
if (!aElement.hasAttribute('href')) continue;
const href = aElement.getAttribute('href').value;
if (!href) continue;
if (href.startsWith('http://')) {
Expand Down
57 changes: 19 additions & 38 deletions test/plugin.html-validate.internal-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Rule } from 'html-validate';
import path from 'path';

export default class CheckInternalLinks extends Rule {
static ALTERNATIVE_EXTENSIONS = ['.html', '.htm', '.php'];
static ALTERNATIVE_EXTENSIONS = ['.html', '.php'];
static EXTERNAL_LINK_PREFIXES = ['https://', 'http://', 'mailto:', 'tel:'];

documentation() {
Expand All @@ -14,35 +14,35 @@ export default class CheckInternalLinks extends Rule {
}

setup() {
// Set up the rule to listen for the "dom:ready" event
this.on('dom:ready', this.domReady.bind(this));
}

checkTheLink(internalLink, element) {
// Decode the internal link
let decodedLink = decodeURIComponent(internalLink);
let decodedLink = internalLink.includes('%') ? decodeURIComponent(internalLink) : internalLink;

// If absolute path then prefix with build
// Remove query string and fragment
decodedLink = decodedLink.split(/[?#]/)[0];

// If absolute path, prefix with the build directory
if (decodedLink.startsWith('/')) {
decodedLink = `${process.cwd()}/build${decodedLink}`;
decodedLink = path.join(process.cwd(), 'build', decodedLink);
}

// Resolve the path
const basePath = path.dirname(element.location.filename);
let resolvedPath = path.resolve(basePath, decodedLink);

// If it's a directory, append index.html
if (fs.existsSync(resolvedPath) && fs.lstatSync(resolvedPath).isDirectory()) {
// Check if it is a directory and append index.html
const isDirectory = fs.existsSync(resolvedPath) && fs.lstatSync(resolvedPath).isDirectory();
if (isDirectory) {
resolvedPath = path.join(resolvedPath, 'index.html');
}

// Pass if url fully matches a file
if (fs.existsSync(resolvedPath)) {
return;
}

// Pass if url matches with any alternative extension
if (CheckInternalLinks.ALTERNATIVE_EXTENSIONS.some((ext) => fs.existsSync(`${resolvedPath}${ext}`))) {
// Pass if the URL matches a file or an alternative extension
if (
fs.existsSync(resolvedPath) ||
CheckInternalLinks.ALTERNATIVE_EXTENSIONS.some((ext) => fs.existsSync(`${resolvedPath}${ext}`))
) {
return;
}

Expand All @@ -56,34 +56,15 @@ export default class CheckInternalLinks extends Rule {
domReady({ document }) {
const elementsWithLink = document.querySelectorAll('[src], [href]');

// Iterate over each anchor element
elementsWithLink.forEach((element) => {
// Get link from src or href attribute
let url = '';
if (element.hasAttribute('src')) {
url = element.getAttribute('src').value;
}
if (element.hasAttribute('href')) {
url = element.getAttribute('href').value;
}

// Remove fragment, if any
if (url.includes('#')) {
url = url.split('#')[0];
}

// Ignore if external link
if (CheckInternalLinks.EXTERNAL_LINK_PREFIXES.some((prefix) => url.startsWith(prefix))) {
return;
}
let url = element.getAttribute('src')?.value || element.getAttribute('href')?.value;

// Ignore if the link is empty
if (url === '') {
// Ignore empty or external links
if (!url || CheckInternalLinks.EXTERNAL_LINK_PREFIXES.some((prefix) => url.startsWith(prefix))) {
return;
}

// Check if the link exists
this.checkTheLink(url, element);
this.checkTheLink(url.split('#')[0], element);
});
}
}

0 comments on commit d365ab8

Please sign in to comment.