UNPKG

website-auditfy

Version:

> Tool for validate your project on SEO, HTML, CSS, JS, TS, Performance, Security and A11Y

362 lines (359 loc) 15.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const ora_1 = __importDefault(require("ora")); const chalk_1 = __importDefault(require("chalk")); const seo_1 = require("./modules/seo/seo"); const a11y_1 = require("./modules/a11y/a11y"); const performance_1 = require("./modules/perfomance/performance"); const message_enum_1 = require("./enum/message.enum"); const html_1 = require("./modules/html/html"); const css_1 = require("./modules/css/css"); const cheerio = __importStar(require("cheerio")); const conventional_cli_1 = require("conventional-cli"); const promises_1 = require("fs/promises"); const node_http_1 = __importDefault(require("node:http")); const serve_handler_1 = __importDefault(require("serve-handler")); const chrome_launcher_1 = require("chrome-launcher"); const lighthouse_1 = __importDefault(require("lighthouse")); const security_module_1 = require("./modules/security/security.module"); const html_validate_1 = require("html-validate"); const source_model_1 = require("./models/source.model"); const javascript_module_1 = require("./modules/javascript/javascript.module"); const option_model_1 = require("./models/option.model"); const json_file_utils_1 = require("./utils/json-file.utils"); const default_1 = require("./config/default"); const toolName = 'website-auditfy'; const packageJson = json_file_utils_1.JsonFileUtils.parseFile(json_file_utils_1.JsonFileUtils.getPackageJsonPath()); const versionOption = new option_model_1.OptionModel({ required: false, longName: 'version', shortName: 'v', beta: false, type: conventional_cli_1.ArgumentTypes.undefined, description: "Print current version" }); const sourceOption = new option_model_1.OptionModel({ required: true, longName: 'source', shortName: 's', beta: false, type: conventional_cli_1.ArgumentTypes.path, values: [ 'relative path', 'absolute path', 'URL' ], description: 'URL or Path to the HTML file to audit', additionalDescription: '' }); const helpOption = new option_model_1.OptionModel({ required: false, longName: 'help', shortName: 'h', beta: false, type: conventional_cli_1.ArgumentTypes.undefined, description: `Print help` }); const configOption = new option_model_1.OptionModel({ required: false, longName: 'config', shortName: 'c', beta: false, type: conventional_cli_1.ArgumentTypes.path, values: [ 'relative path', 'absolute path', ], description: 'Path to the JSON config file', additionalDescription: '' }); const docs = { name: toolName, usage: '[options]', description: 'Simple CLI tools for check SEO, HTML, CSS, JS, TS, Performance, Security and A11Y', examples: ` Current version: ${packageJson.version} Examples: $ ${toolName} path/to/index.html -c ./path/to/config.json $ ${toolName} https://github.com ` }; const program = new commander_1.Command(); program .name(docs.name) .description(docs.description) .argument(sourceOption.getFlag(), sourceOption.getDescription()) .option(configOption.getFlag(), configOption.getDescription(), configOption.default) .helpOption(helpOption.getFlag(), helpOption.getDescription()) .version(packageJson.version, versionOption.getFlag(), versionOption.getDescription()) .addHelpText(`afterAll`, docs.examples) .action(run); program.parse(process.argv); function run(path, options) { return __awaiter(this, void 0, void 0, function* () { const spinner = (0, ora_1.default)('Running audits...').start(); const currentDataLogger = Date.now(); try { const source = source_model_1.SourceModel.create(path); const dom = yield getCheerioDOM(source); const lighthouse = yield getLightHouseResult(source); const htmlValidator = yield getHtmlValidatorResult(source); const staticModules = [ seo_1.SeoAudit, css_1.CssAudit, a11y_1.A11yAudit, html_1.HtmlAudit, security_module_1.SecurityModule, performance_1.PerformanceAudit, javascript_module_1.JsAuditModule, // TypescriptAuditModule, ]; const urlModules = [ seo_1.SeoAudit, a11y_1.A11yAudit, html_1.HtmlAudit, security_module_1.SecurityModule, performance_1.PerformanceAudit ]; const modules = source.isURL ? urlModules : staticModules; const config = options.config ? json_file_utils_1.JsonFileUtils.parseFile(options.config) : default_1.config; const results = yield modules.reduce((result, module) => __awaiter(this, void 0, void 0, function* () { const res = yield result; const instance = new module(source, dom, lighthouse, htmlValidator, config); res[instance.name] = yield instance.check(); return result; }), Promise.resolve({})); spinner.succeed('Audit completed!'); console.log(chalk_1.default.green.bold('\nAudit Report')); for (const [section, result] of Object.entries(results)) { console.log(`\n${chalk_1.default.cyan(section.toUpperCase())}`); if (result.length === 0 || result.every(x => x.type === message_enum_1.MessageType.passed)) { console.log(`- ${chalk_1.default.green('✔')} all tests are passed`); } else { result.forEach((r) => { console.log(`- ${r.type === message_enum_1.MessageType.passed ? chalk_1.default.green('✔') : r.type === message_enum_1.MessageType.warning ? chalk_1.default.yellow('⚠') : chalk_1.default.red('✘')} ${r.message}`); }); } } if (options.output) { const fs = yield Promise.resolve().then(() => __importStar(require('fs/promises'))); yield fs.writeFile(options.output, JSON.stringify(results, null, 2)); console.log(`\nResults saved to ${options.output}`); } console.log(`\n Total working time: ${(Date.now() - currentDataLogger) / 1000}s`); const auditHasError = Object.values(results) .reduce((list, item) => { list.push(...item); return list; }, []) .some(x => x.type === message_enum_1.MessageType.error); process.exit(auditHasError ? 1 : 0); } catch (error) { spinner.fail('Audit failed.'); console.error(error); process.exit(2); } }); } function getCheerioDOM(source) { return __awaiter(this, void 0, void 0, function* () { return source.isURL ? yield cheerio.fromURL(source.url) : cheerio.load(yield (0, promises_1.readFile)(source.file.relativePath, 'utf-8')); }); } function getHtmlValidatorResult(source) { return __awaiter(this, void 0, void 0, function* () { const htmlValidate = new html_validate_1.HtmlValidate({ rules: { // Security 'require-csp-nonce': "warn", 'require-sri': "warn", // End Security // Style Block 'attr-case': "warn", 'attr-pattern': "warn", 'attr-quotes': "warn", 'attribute-boolean-style': "warn", 'attribute-empty-style': "warn", 'class-pattern': "warn", 'doctype-style': "warn", 'element-case': "warn", 'id-pattern': "warn", 'name-pattern': "warn", 'no-implicit-close': "warn", 'no-implicit-input-type': "warn", 'no-inline-style': "warn", 'no-self-closing': "warn", 'no-style-tag': "warn", 'no-trailing-whitespace': "warn", 'prefer-button': "warn", 'prefer-tbody': "warn", 'void-style': "warn", // End Style Block // Accessibility 'area-alt': "warn", 'aria-hidden-body': "warn", 'aria-label-misuse': "warn", 'empty-heading': "warn", 'empty-title': "warn", 'hidden-focusable': "warn", 'input-missing-label': "warn", 'meta-refresh': "warn", 'multiple-labeled-controls': "warn", 'no-abstract-role': "warn", 'no-autoplay': "warn", 'no-implicit-button-type': "warn", 'no-redundant-aria-label': "warn", 'no-redundant-role': "warn", 'prefer-native-element': "warn", 'svg-focusable': "warn", 'tel-non-breaking': "warn", 'text-content': "warn", 'unique-landmark': "warn", 'wcag/h30': "warn", 'wcag/h36': "warn", 'wcag/h37': "warn", 'wcag/h63': "warn", 'wcag/h67': "warn", 'wcag/h71': "warn", // End Accessibility // Deprecated 'deprecated': "warn", 'deprecated-rule': "warn", 'no-conditional-comment': "warn", 'no-deprecated-attr': "warn", // End Deprecated // Document 'allowed-links': "warn", 'doctype-html': "warn", 'heading-level': "warn", // - ?? 'missing-doctype': "warn", 'no-dup-id': "warn", 'no-missing-references': "warn", 'no-utf8-bom': "warn", // End Document // Uncategorized 'no-unknown-elements': "warn", 'no-unused-disable': "warn", // End Uncategorized // Content model 'attribute-allowed-values': "warn", 'attribute-misuse': "warn", 'element-permitted-content': "warn", 'element-permitted-occurrences': "warn", 'element-permitted-order': "warn", 'element-permitted-parent': "warn", 'element-required-ancestor': "warn", 'element-required-attributes': "warn", 'element-required-content': "warn", 'input-attributes': "warn", 'no-multiple-main': "warn", 'script-element': "warn", 'void-content': "warn", // End Content model // HTML Syntax and concepts 'attr-delimiter': "warn", 'attr-spacing': "warn", 'close-attr': "warn", 'close-order': "warn", 'element-name': "warn", 'form-dup-name': "warn", 'map-dup-name': "warn", 'map-id-name': "warn", 'no-dup-attr': "warn", 'no-dup-class': "warn", 'no-raw-characters': "warn", 'no-redundant-for': "warn", 'script-type': "warn", 'unrecognized-char-ref': "warn", 'valid-autocomplete': "warn", 'valid-id': "warn", // End HTML Syntax and concepts }, }); let result; if (source.isURL) { const website = yield fetch(source.url); const data = yield website.text(); result = yield htmlValidate.validateString(data); } else { result = yield htmlValidate.validateFile(source.file.relativePath); } return result.results; }); } function getLightHouseResult(source) { return __awaiter(this, void 0, void 0, function* () { const PORT = 9900; const path = source.isURL ? source.url : `http://localhost:${PORT}/${source.file.filename}`; const dir = source.isURL ? process.cwd() : source.file.dir; const server = node_http_1.default.createServer((req, res) => { return (0, serve_handler_1.default)(req, res, { public: dir }); }); // @ts-ignore yield new Promise(resolve => server.listen(PORT, resolve)); const chrome = yield (0, chrome_launcher_1.launch)({ chromeFlags: ['--headless'] }); const result = yield (0, lighthouse_1.default)(path, { port: chrome.port, output: 'json', logLevel: 'error', }); (0, chrome_launcher_1.killAll)(); server.close(); debugger; return result || {}; }); } //# sourceMappingURL=index.js.map