Skip to content

feat: create linter system #191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
permissions:
contents: read

env:
FORCE_COLOR: 1

jobs:
build:
runs-on: ubuntu-latest
Expand Down
42 changes: 39 additions & 3 deletions bin/cli.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

import { resolve } from 'node:path';
import { argv } from 'node:process';
import { argv, exit } from 'node:process';

import { Command, Option } from 'commander';

Expand All @@ -12,6 +12,9 @@ import generators from '../src/generators/index.mjs';
import createMarkdownLoader from '../src/loaders/markdown.mjs';
import createMarkdownParser from '../src/parsers/markdown.mjs';
import createNodeReleases from '../src/releases.mjs';
import createLinter from '../src/linter/index.mjs';
import reporters from '../src/linter/reporters/index.mjs';
import rules from '../src/linter/rules/index.mjs';

const availableGenerators = Object.keys(generators);

Expand Down Expand Up @@ -50,6 +53,19 @@ program
'Set the processing target modes'
).choices(availableGenerators)
)
.addOption(
new Option('--disable-rule [rule...]', 'Disable a specific linter rule')
.choices(Object.keys(rules))
.default([])
)
.addOption(
new Option('--lint-dry-run', 'Run linter in dry-run mode').default(false)
)
.addOption(
new Option('-r, --reporter [reporter]', 'Specify the linter reporter')
.choices(Object.keys(reporters))
.default('console')
)
.parse(argv);

/**
Expand All @@ -60,13 +76,27 @@ program
* @property {string} output Specifies the directory where output files will be saved.
* @property {Target[]} target Specifies the generator target mode.
* @property {string} version Specifies the target Node.js version.
* @property {string} changelog Specifies the path to the Node.js CHANGELOG.md file
* @property {string} changelog Specifies the path to the Node.js CHANGELOG.md file.
* @property {string[]} disableRule Specifies the linter rules to disable.
* @property {boolean} lintDryRun Specifies whether the linter should run in dry-run mode.
* @property {keyof reporters} reporter Specifies the linter reporter.
*
* @name ProgramOptions
* @type {Options}
* @description The return type for values sent to the program from the CLI.
*/
const { input, output, target = [], version, changelog } = program.opts();
const {
input,
output,
target = [],
version,
changelog,
disableRule,
lintDryRun,
reporter,
} = program.opts();

const linter = createLinter(lintDryRun, disableRule);

const { loadFiles } = createMarkdownLoader();
const { parseApiDocs } = createMarkdownParser();
Expand All @@ -80,6 +110,8 @@ const { runGenerators } = createGenerator(parsedApiDocs);
// Retrieves Node.js release metadata from a given Node.js version and CHANGELOG.md file
const { getAllMajors } = createNodeReleases(changelog);

linter.lintAll(parsedApiDocs);

await runGenerators({
// A list of target modes for the API docs parser
generators: target,
Expand All @@ -92,3 +124,7 @@ await runGenerators({
// A list of all Node.js major versions with LTS status
releases: await getAllMajors(),
});

linter.report(reporter);

exit(Number(linter.hasError()));
66 changes: 66 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"dependencies": {
"acorn": "^8.14.0",
"@actions/core": "^1.11.1",
"commander": "^13.1.0",
"estree-util-visit": "^2.0.0",
"dedent": "^1.5.3",
Expand Down
56 changes: 45 additions & 11 deletions src/constants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,45 @@ export const DOC_SLUG_ENVIRONMENT = 'environment-variables-1';
// JavaScript globals types within the MDN JavaScript docs
// @see DOC_MDN_BASE_URL_JS_GLOBALS
export const DOC_TYPES_MAPPING_GLOBALS = {
...Object.fromEntries([
'AggregateError', 'Array', 'ArrayBuffer', 'DataView', 'Date', 'Error',
'EvalError', 'Function', 'Map', 'NaN', 'Object', 'Promise', 'Proxy', 'RangeError',
'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', 'Symbol',
'TypeError', 'URIError', 'WeakMap', 'WeakSet',

'TypedArray',
'Float32Array', 'Float64Array',
'Int8Array', 'Int16Array', 'Int32Array',
'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array',
].map(e => [e, e])),
...Object.fromEntries(
[
'AggregateError',
'Array',
'ArrayBuffer',
'DataView',
'Date',
'Error',
'EvalError',
'Function',
'Map',
'NaN',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'RegExp',
'Set',
'SharedArrayBuffer',
'SyntaxError',
'Symbol',
'TypeError',
'URIError',
'WeakMap',
'WeakSet',

'TypedArray',
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
].map(e => [e, e])
),
bigint: 'BigInt',
'WebAssembly.Instance': 'WebAssembly/Instance',
};
Expand Down Expand Up @@ -392,3 +420,9 @@ export const DOC_TYPES_MAPPING_OTHER = {
Response: `${DOC_MDN_BASE_URL}/API/Response`,
Request: `${DOC_MDN_BASE_URL}/API/Request`,
};

export const LINT_MESSAGES = {
missingIntroducedIn: "Missing 'introduced_in' field in the API doc entry",
missingChangeVersion: 'Missing version field in the API doc entry',
invalidChangeVersion: 'Invalid version number: {{version}}',
};
51 changes: 51 additions & 0 deletions src/linter/engine.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

/**
* Creates a linter engine instance to validate ApiDocMetadataEntry entries
*
* @param {import('./types').LintRule} rules Lint rules to validate the entries against
*/
const createLinterEngine = rules => {
/**
* Validates a ApiDocMetadataEntry entry against all defined rules
*
* @param {ApiDocMetadataEntry} entry
* @returns {import('./types').LintIssue[]}
*/
const lint = entry => {
const issues = [];

for (const rule of rules) {
const ruleIssues = rule(entry);

if (ruleIssues.length > 0) {
issues.push(...ruleIssues);
}
}

return issues;
};

/**
* Validates an array of ApiDocMetadataEntry entries against all defined rules
*
* @param {ApiDocMetadataEntry[]} entries
* @returns {import('./types').LintIssue[]}
*/
const lintAll = entries => {
const issues = [];

for (const entry of entries) {
issues.push(...lint(entry));
}

return issues;
};

return {
lint,
lintAll,
};
};

export default createLinterEngine;
Loading
Loading