mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
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>
This commit is contained in:
parent
3a1a440802
commit
5d4da62514
@ -1815,6 +1815,10 @@ Type: End-of-Life
|
||||
`runInAsyncIdScope` doesn't emit the `'before'` or `'after'` event and can thus
|
||||
cause a lot of issues. See <https://github.com/nodejs/node/issues/14328>.
|
||||
|
||||
<!-- md-lint skip-deprecation DEP0087 -->
|
||||
|
||||
<!-- md-lint skip-deprecation DEP0088 -->
|
||||
|
||||
### DEP0089: `require('assert')`
|
||||
|
||||
<!-- YAML
|
||||
@ -2255,10 +2259,10 @@ Type: End-of-Life
|
||||
The `crypto._toBuf()` function was not designed to be used by modules outside
|
||||
of Node.js core and was removed.
|
||||
|
||||
### DEP0115: `crypto.prng()`, `crypto.pseudoRandomBytes()`, `crypto.rng()`
|
||||
|
||||
<!--lint disable nodejs-yaml-comments -->
|
||||
|
||||
### DEP0115: `crypto.prng()`, `crypto.pseudoRandomBytes()`, `crypto.rng()`
|
||||
|
||||
<!-- YAML
|
||||
changes:
|
||||
- version: v11.0.0
|
||||
@ -2269,10 +2273,10 @@ changes:
|
||||
with `--pending-deprecation` support.
|
||||
-->
|
||||
|
||||
<!--lint enable nodejs-yaml-comments -->
|
||||
|
||||
Type: Documentation-only (supports [`--pending-deprecation`][])
|
||||
|
||||
<!--lint enable nodejs-yaml-comments -->
|
||||
|
||||
In recent versions of Node.js, there is no difference between
|
||||
[`crypto.randomBytes()`][] and `crypto.pseudoRandomBytes()`. The latter is
|
||||
deprecated along with the undocumented aliases `crypto.prng()` and
|
||||
|
28
test/doctool/test-deprecation-codes.js
Normal file
28
test/doctool/test-deprecation-codes.js
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
const script = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'tools',
|
||||
'doc',
|
||||
'deprecationCodes.mjs'
|
||||
);
|
||||
|
||||
const mdPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'doc',
|
||||
'api',
|
||||
'deprecations.md'
|
||||
);
|
||||
|
||||
const cp = spawn(process.execPath, [script, mdPath], { encoding: 'utf-8', stdio: 'inherit' });
|
||||
|
||||
cp.on('error', (err) => { throw err; });
|
||||
cp.on('exit', (code) => process.exit(code));
|
42
test/parallel/test-eslint-documented-deprecation-codes.js
Normal file
42
test/parallel/test-eslint-documented-deprecation-codes.js
Normal file
@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
if (!common.hasIntl)
|
||||
common.skip('missing Intl');
|
||||
common.skipIfEslintMissing();
|
||||
|
||||
const RuleTester = require('../../tools/node_modules/eslint').RuleTester;
|
||||
const rule = require('../../tools/eslint-rules/documented-deprecation-codes');
|
||||
|
||||
const mdFile = 'doc/api/deprecations.md';
|
||||
|
||||
const invalidCode = 'UNDOCUMENTED INVALID CODE';
|
||||
|
||||
new RuleTester().run('documented-deprecation-codes', rule, {
|
||||
valid: [
|
||||
`
|
||||
deprecate(function() {
|
||||
return this.getHeaders();
|
||||
}, 'OutgoingMessage.prototype._headers is deprecated', 'DEP0066')
|
||||
`,
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `
|
||||
deprecate(function foo(){}, 'bar', '${invalidCode}');
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `"${invalidCode}" does not match the expected pattern`,
|
||||
line: 2
|
||||
},
|
||||
{
|
||||
message: `"${invalidCode}" is not documented in ${mdFile}`,
|
||||
line: 2
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
});
|
92
tools/doc/deprecationCodes.mjs
Normal file
92
tools/doc/deprecationCodes.mjs
Normal file
@ -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]);
|
||||
}
|
||||
}
|
37
tools/eslint-rules/documented-deprecation-codes.js
Normal file
37
tools/eslint-rules/documented-deprecation-codes.js
Normal file
@ -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 });
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user