w3c-html-validator
Version:
Check the markup validity of HTML files using the W3C validator
83 lines (78 loc) • 3.77 kB
JavaScript
////////////////////////
// w3c-html-validator //
// MIT License //
////////////////////////
// Usage in package.json:
// "scripts": {
// "validate": "html-validator docs flyer.html",
// "all": "html-validator"
// },
//
// Usage from command line:
// $ npm install --save-dev w3c-html-validator
// $ npx html-validator dist #validate all html files in the dist folder
// $ npx html-validator docs flyer.html
//
// Contributors to this project:
// $ cd w3c-html-validator
// $ npm install
// $ npm test
// $ node bin/cli.js spec --continue
// Imports
import { cliArgvUtil } from 'cli-argv-util';
import { globSync } from 'glob';
import { w3cHtmlValidator } from '../dist/w3c-html-validator.js';
import fs from 'fs';
import slash from 'slash';
// Parameters and flags
const validFlags = ['continue', 'delay', 'dry-run', 'exclude', 'ignore', 'ignore-config', 'note', 'quiet', 'trim'];
const cli = cliArgvUtil.parse(validFlags);
const files = cli.params;
const ignore = cli.flagMap.ignore ?? null;
const ignoreConfig = cli.flagMap.ignoreConfig ?? null;
const delay = Number(cli.flagMap.delay) || 500; //default half second debounce pause
const trim = Number(cli.flagMap.trim) || null;
const dryRunMode = cli.flagOn.dryRun || process.env.w3cHtmlValidator === 'dry-run'; //bash: export w3cHtmlValidator=dry-run
// Validator
const globOptions = { ignore: '**/node_modules/**/*' };
const keep = (filename) => !filename.includes('node_modules/');
const readFolder = (folder) => globSync(slash(folder + '**/*.html'), globOptions);
const getAllPaths = () => files.map(file => globSync(slash(file), globOptions)).flat();
const expandFolder = (file) => fs.lstatSync(file).isDirectory() ? readFolder(file + '/') : file;
const getFilenames = () => getAllPaths().map(expandFolder).flat().filter(keep).sort();
const list = files.length ? getFilenames() : readFolder('');
const excludes = cli.flagMap.exclude?.split(',') ?? [];
const filenames = list.filter(name => !excludes.find(exclude => name.includes(exclude)));
const error =
cli.invalidFlag ? cli.invalidFlagMsg :
!filenames.length ? 'No files to validate.' :
cli.flagOn.trim && !trim ? 'Value of "trim" must be a positive whole number.' :
null;
if (error)
throw new Error('[w3c-html-validator] ' + error);
if (dryRunMode)
w3cHtmlValidator.dryRunNotice();
if (filenames.length > 1 && !cli.flagOn.quiet)
w3cHtmlValidator.summary(filenames.length);
const reporterOptions = {
continueOnFail: cli.flagOn.continue,
quiet: cli.flagOn.quiet,
maxMessageLen: trim,
};
const getIgnoreMessages = () => {
const toArray = (text) => text.replace(/\r/g, '').split('\n').map(line => line.trim());
const notComment = (line) => line.length > 1 && !line.startsWith('#');
const readLines = (file) => toArray(fs.readFileSync(file).toString()).filter(notComment);
const rawLines = ignoreConfig ? readLines(ignoreConfig) : [];
if (ignore)
rawLines.push(ignore);
const isRegex = /^\/.*\/$/; //starts and ends with a slash indicating it's a regex
return rawLines.map(line => isRegex.test(line) ? new RegExp(line.slice(1, -1)) : line);
};
const baseOptions = { ignoreMessages: getIgnoreMessages(), dryRun: dryRunMode };
const options = (filename) => ({ filename: filename, ...baseOptions });
const handleResults = (results) => w3cHtmlValidator.reporter(results, reporterOptions);
const getReport = (filename) => w3cHtmlValidator.validate(options(filename)).then(handleResults);
const processFile = (filename, i) => globalThis.setTimeout(() => getReport(filename), i * delay);
filenames.forEach(processFile);