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
JavaScript
;
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