Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAntoine du Hamel <duhamelantoine1995@gmail.com>2022-02-19 20:14:09 +0300
committerGitHub <noreply@github.com>2022-02-19 20:14:09 +0300
commit5d4da62514cdf24c582fd4a8e5f86ef37860e8af (patch)
tree98abbfa5fdb6decb4773ac12b853d0e3b969cf4a /tools
parent3a1a4408022f657d0f4bf13000bc37e1e72bd40a (diff)
tools: lint deprecation codes
Add a rule to make sure deprecation codes are in order. PR-URL: https://github.com/nodejs/node/pull/41992 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/doc/deprecationCodes.mjs92
-rw-r--r--tools/eslint-rules/documented-deprecation-codes.js37
-rw-r--r--tools/eslint-rules/rules-utils.js8
3 files changed, 137 insertions, 0 deletions
diff --git a/tools/doc/deprecationCodes.mjs b/tools/doc/deprecationCodes.mjs
new file mode 100644
index 00000000000..6715bb04b60
--- /dev/null
+++ b/tools/doc/deprecationCodes.mjs
@@ -0,0 +1,92 @@
+import fs from 'fs';
+import { resolve } from 'path';
+import assert from 'assert';
+
+import { unified } from 'unified';
+import remarkParse from 'remark-parse';
+
+const source = resolve(process.argv[2]);
+
+const skipDeprecationComment = /^<!-- md-lint skip-deprecation (DEP\d{4}) -->$/;
+
+const generateDeprecationCode = (codeAsNumber) =>
+ `DEP${codeAsNumber.toString().padStart(4, '0')}`;
+
+const addMarkdownPathToErrorStack = (error, node) => {
+ const { line, column } = node.position.start;
+ const [header, ...lines] = error.stack.split('\n');
+ error.stack =
+ header +
+ `\n at <anonymous> (${source}:${line}:${column})\n` +
+ lines.join('\n');
+ return error;
+};
+
+const testHeading = (headingNode, expectedDeprecationCode) => {
+ try {
+ assert.strictEqual(
+ headingNode?.children[0]?.value.substring(0, 9),
+ `${expectedDeprecationCode}: `,
+ 'Ill-formed or out-of-order deprecation code.'
+ );
+ } catch (e) {
+ throw addMarkdownPathToErrorStack(e, headingNode);
+ }
+};
+
+const testYAMLComment = (commentNode) => {
+ try {
+ assert.match(
+ commentNode?.value?.substring(0, 21),
+ /^<!-- YAML\r?\nchanges:\r?\n/,
+ 'Missing or ill-formed YAML comment.'
+ );
+ } catch (e) {
+ throw addMarkdownPathToErrorStack(e, commentNode);
+ }
+};
+
+const testDeprecationType = (paragraphNode) => {
+ try {
+ assert.strictEqual(
+ paragraphNode?.children[0]?.value?.substring(0, 6),
+ 'Type: ',
+ 'Missing deprecation type.'
+ );
+ } catch (e) {
+ throw addMarkdownPathToErrorStack(e, paragraphNode);
+ }
+};
+
+const tree = unified()
+ .use(remarkParse)
+ .parse(fs.readFileSync(source));
+
+let expectedDeprecationCodeNumber = 0;
+for (let i = 0; i < tree.children.length; i++) {
+ const node = tree.children[i];
+ if (node.type === 'html' && skipDeprecationComment.test(node.value)) {
+ const expectedDeprecationCode =
+ generateDeprecationCode(++expectedDeprecationCodeNumber);
+ const deprecationCodeAsText = node.value.match(skipDeprecationComment)[1];
+
+ try {
+ assert.strictEqual(
+ deprecationCodeAsText,
+ expectedDeprecationCode,
+ 'Deprecation codes are not ordered correctly.'
+ );
+ } catch (e) {
+ throw addMarkdownPathToErrorStack(e, node);
+ }
+ }
+ if (node.type === 'heading' && node.depth === 3) {
+ const expectedDeprecationCode =
+ generateDeprecationCode(++expectedDeprecationCodeNumber);
+
+ testHeading(node, expectedDeprecationCode);
+
+ testYAMLComment(tree.children[i + 1]);
+ testDeprecationType(tree.children[i + 2]);
+ }
+}
diff --git a/tools/eslint-rules/documented-deprecation-codes.js b/tools/eslint-rules/documented-deprecation-codes.js
new file mode 100644
index 00000000000..3317f3c983c
--- /dev/null
+++ b/tools/eslint-rules/documented-deprecation-codes.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const { isDefiningDeprecation } = require('./rules-utils.js');
+
+const patternToMatch = /^DEP\d+$/;
+
+const mdFile = 'doc/api/deprecations.md';
+const doc = fs.readFileSync(path.resolve(__dirname, '../..', mdFile), 'utf8');
+
+function isInDoc(code) {
+ return doc.includes(`### ${code}:`);
+}
+
+function getDeprecationCode(node) {
+ return node.expression.arguments[2].value;
+}
+
+module.exports = {
+ create: function(context) {
+ return {
+ ExpressionStatement: function(node) {
+ if (!isDefiningDeprecation(node) || !getDeprecationCode(node)) return;
+ const code = getDeprecationCode(node);
+ if (!patternToMatch.test(code)) {
+ const message = `"${code}" does not match the expected pattern`;
+ context.report({ node, message });
+ }
+ if (!isInDoc(code)) {
+ const message = `"${code}" is not documented in ${mdFile}`;
+ context.report({ node, message });
+ }
+ },
+ };
+ },
+};
diff --git a/tools/eslint-rules/rules-utils.js b/tools/eslint-rules/rules-utils.js
index 1cba9218a1d..c5362c96cda 100644
--- a/tools/eslint-rules/rules-utils.js
+++ b/tools/eslint-rules/rules-utils.js
@@ -20,6 +20,14 @@ module.exports.isDefiningError = function(node) {
node.expression.arguments.length !== 0;
};
+module.exports.isDefiningDeprecation = function(node) {
+ return node.expression &&
+ node.expression.type === 'CallExpression' &&
+ node.expression.callee &&
+ node.expression.callee.name.endsWith('deprecate') &&
+ node.expression.arguments.length !== 0;
+};
+
/**
* Returns true if any of the passed in modules are used in
* require calls.