Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
araujogui committed Feb 17, 2025
1 parent d27fb46 commit 9bad793
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 78 deletions.
7 changes: 4 additions & 3 deletions bin/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ program
'Set the processing target modes'
).choices(availableGenerators)
)
.addOption(new Option('--skip-validation', 'TODO').default(false))
.addOption(new Option('--skip-linting', 'Skip linting').default(false))
.addOption(
new Option('--reporter', 'TODO')
new Option('--reporter', 'Specify the linter reporter')
.choices(Object.keys(reporters))
.default('console')
)
Expand Down Expand Up @@ -98,6 +98,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 @@ -107,7 +109,6 @@ await runGenerators({
version: coerce(version),
// A list of all Node.js major versions with LTS status
releases: await getAllMajors(),
linter,
});

if (linter) {
Expand Down
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 @@ -29,6 +29,7 @@
"prettier": "3.4.2"
},
"dependencies": {
"@actions/core": "^1.11.1",
"commander": "^13.1.0",
"dedent": "^1.5.3",
"github-slugger": "^2.0.0",
Expand Down
78 changes: 27 additions & 51 deletions src/linter/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,46 @@
'use strict';

import reporters from './reporters/index.mjs';
import { invalidChangeVersion } from './rules/invalid-change-version.mjs';
import { missingChangeVersion } from './rules/missing-change-version.mjs';
import { missingIntroducedIn } from './rules/missing-introduced-in.mjs';

/**
*
* Lint issues in ApiDocMetadataEntry entries
*/
export class Linter {
#_hasError = false;
/**
* @type {Array<import('./types.d.ts').LintIssue>}
*/
#issues = [];

/**
* @type {Array<import('./types.d.ts').LintMessage>}
* @type {Array<import('./types.d.ts').LintRule>}
*/
#messages = [];
#rules = [missingIntroducedIn, missingChangeVersion, invalidChangeVersion];

/**
*
* @param {ApiDocMetadataEntry} entry
* @returns {void}
*/
get hasError() {
return this.#_hasError;
lint(entry) {
for (const rule of this.#rules) {
const issues = rule(entry);

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

/**
* @param {import('./types.d.ts').LintMessage} msg
* @param {ApiDocMetadataEntry[]} entries
* @returns {void}
*/
log(msg) {
if (msg.level === 'error') {
this.#_hasError = true;
lintAll(entries) {
for (const entry of entries) {
this.lint(entry);
}

this.#messages.push(msg);
}

/**
Expand All @@ -38,44 +50,8 @@ export class Linter {
report(reporterName) {
const reporter = reporters[reporterName];

for (const message of this.#messages) {
reporter(message);
for (const issue of this.#issues) {
reporter(issue);
}
}

/**
* @param {string} msg
* @param {import('./types.d.ts').LintMessageLocation | undefined} location
*/
info(msg, location) {
this.log({
level: 'info',
msg,
location,
});
}

/**
* @param {string} msg
* @param {import('./types.d.ts').LintMessageLocation | undefined} location
*/
warn(msg, location) {
this.log({
level: 'warn',
msg,
location,
});
}

/**
* @param {string} msg
* @param {import('./types.d.ts').LintMessageLocation | undefined} location
*/
error(msg, location) {
this.log({
level: 'error',
msg,
location,
});
}
}
13 changes: 9 additions & 4 deletions src/linter/reporters/console.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import { styleText } from 'node:util';

/**
* TODO is there a way to grab the parameter type for styleText since the types aren't exported
* @type {Record<import('../types.d.ts').LintLevel, string>}
* @type {Record<import('../types.d.ts').IssueLevel, string>}
*/
const levelToColorMap = {
info: 'gray',
Expand All @@ -17,6 +16,12 @@ const levelToColorMap = {
/**
* @type {import('../types.d.ts').Reporter}
*/
export default msg => {
console.log(styleText(levelToColorMap[msg.level], msg.msg));
export default issue => {
console.log(
styleText(
// @ts-expect-error ForegroundColors is not exported
levelToColorMap[issue.level],
`${issue.message} at ${issue.location.path} (${issue.location.position.start}:${issue.location.position.end})`
)
);
};
17 changes: 14 additions & 3 deletions src/linter/reporters/github.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@

'use strict';

import * as core from '@actions/core';

/**
* GitHub action reporter for
*
* @type {import('../types.d.ts').Reporter}
*/
export default msg => {
// TODO
console.log(msg);
export default issue => {
const actions = {
warn: core.warning,
error: core.error,
info: core.notice,
};

(actions[issue.level] || core.notice)(issue.message, {
file: issue.location.path,
startLine: issue.location.position.start.line,
endLine: issue.location.position.end.line,
});
};
33 changes: 33 additions & 0 deletions src/linter/rules/invalid-change-version.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { validateVersion } from '../utils/semver.mjs';

/**
* Checks if any change version is invalid.
*
* @param {ApiDocMetadataEntry} entry
* @returns {Array<import('../types').LintIssue>}
*/
export const invalidChangeVersion = entry => {
if (entry.changes.length === 0) {
return [];
}

const allVersions = entry.changes
.filter(change => change.version)
.flatMap(change =>
Array.isArray(change.version) ? change.version : [change.version]
);

const invalidVersions = allVersions.filter(
version => !validateVersion(version.substring(1)) // Trim the leading 'v' from the version string
);

return invalidVersions.map(version => ({
level: 'warn',
message: `Invalid version number: ${version}`,
location: {
path: entry.api_doc_source,
line: entry.yaml_position.start.line,
column: entry.yaml_position.start.column,
},
}));
};
23 changes: 23 additions & 0 deletions src/linter/rules/missing-change-version.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Checks if any change version is missing.
*
* @param {ApiDocMetadataEntry} entry
* @returns {Array<import('../types').LintIssue>}
*/
export const missingChangeVersion = entry => {
if (entry.changes.length === 0) {
return [];
}

return entry.changes
.filter(change => !change.version)
.map(() => ({
level: 'warn',
message: 'Missing change version',
location: {
path: entry.api_doc_source,
line: entry.yaml_position.start.line,
column: entry.yaml_position.start.column,
},
}));
};
24 changes: 24 additions & 0 deletions src/linter/rules/missing-introduced-in.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Checks if `introduced_in` field is missing in the API doc entry.
*
* @param {ApiDocMetadataEntry} entry
* @returns {Array<import('../types.d.ts').LintIssue>}
*/
export const missingIntroducedIn = entry => {
// Early return if not a top-level heading or if introduced_in exists
if (entry.heading.depth !== 1 || entry.introduced_in) {
return [];
}

return [
{
level: 'info',
message: 'Missing `introduced_in` field in the API doc entry',
location: {
path: entry.api_doc_source,
// line: entry.yaml_position.start,
// column: entry.yaml_position.end,
},
},
];
};
Loading

0 comments on commit 9bad793

Please sign in to comment.