Skip to content
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

tools: enforce errors to not be documented in legacy section #55218

Merged
merged 3 commits into from
Oct 8, 2024
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
114 changes: 57 additions & 57 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2340,6 +2340,17 @@ compiled with ICU support.

A non-context-aware native addon was loaded in a process that disallows them.

<a id="ERR_OPERATION_FAILED"></a>

### `ERR_OPERATION_FAILED`

<!-- YAML
added: v15.0.0
-->

An operation failed. This is typically used to signal the general failure
of an asynchronous operation.

<a id="ERR_OUT_OF_RANGE"></a>

### `ERR_OUT_OF_RANGE`
Expand Down Expand Up @@ -2422,6 +2433,42 @@ Accessing `Object.prototype.__proto__` has been forbidden using
[`Object.setPrototypeOf`][] should be used to get and set the prototype of an
object.

<a id="ERR_QUIC_CONNECTION_FAILED"></a>

### `ERR_QUIC_CONNECTION_FAILED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

Establishing a QUIC connection failed.

<a id="ERR_QUIC_ENDPOINT_CLOSED"></a>

### `ERR_QUIC_ENDPOINT_CLOSED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

A QUIC Endpoint closed with an error.

<a id="ERR_QUIC_OPEN_STREAM_FAILED"></a>

### `ERR_QUIC_OPEN_STREAM_FAILED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

Opening a QUIC stream failed.

<a id="ERR_REQUIRE_CYCLE_MODULE"></a>

### `ERR_REQUIRE_CYCLE_MODULE`
Expand Down Expand Up @@ -2999,6 +3046,16 @@ try {
}
```

<a id="ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING"></a>

### `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`

<!-- YAML
added: v22.6.0
-->

Type stripping is not supported for files descendent of a `node_modules` directory.

<a id="ERR_USE_AFTER_CLOSE"></a>

### `ERR_USE_AFTER_CLOSE`
Expand Down Expand Up @@ -3612,17 +3669,6 @@ Used by the `Node-API` when `Constructor.prototype` is not an object.
A Node.js API was called in an unsupported manner, such as
`Buffer.write(string, encoding, offset[, length])`.

<a id="ERR_OPERATION_FAILED"></a>

### `ERR_OPERATION_FAILED`

<!-- YAML
added: v15.0.0
-->

An operation failed. This is typically used to signal the general failure
of an asynchronous operation.

<a id="ERR_OUTOFMEMORY"></a>

### `ERR_OUTOFMEMORY`
Expand All @@ -3646,42 +3692,6 @@ removed: v10.0.0

The `node:repl` module was unable to parse data from the REPL history file.

<a id="ERR_QUIC_CONNECTION_FAILED"></a>

### `ERR_QUIC_CONNECTION_FAILED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

Establishing a QUIC connection failed.

<a id="ERR_QUIC_ENDPOINT_CLOSED"></a>

### `ERR_QUIC_ENDPOINT_CLOSED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

A QUIC Endpoint closed with an error.

<a id="ERR_QUIC_OPEN_STREAM_FAILED"></a>

### `ERR_QUIC_OPEN_STREAM_FAILED`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

Opening a QUIC stream failed.

<a id="ERR_SOCKET_CANNOT_SEND"></a>

### `ERR_SOCKET_CANNOT_SEND`
Expand Down Expand Up @@ -4073,16 +4083,6 @@ The public key in the certificate SubjectPublicKeyInfo could not be read.

An error occurred trying to allocate memory. This should never happen.

<a id="ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING"></a>

#### `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`

<!-- YAML
added: v22.6.0
-->

Type stripping is not supported for files descendent of a `node_modules` directory.

[ES Module]: esm.md
[ICU]: intl.md#internationalization-support
[JSON Web Key Elliptic Curve Registry]: https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve
Expand Down
5 changes: 0 additions & 5 deletions test/parallel/test-eslint-documented-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ new RuleTester().run('documented-errors', rule, {
message: `"${invalidCode}" is not documented in doc/api/errors.md`,
line: 2
},
{
message:
`doc/api/errors.md does not have an anchor for "${invalidCode}"`,
line: 2
},
]
},
]
Expand Down
92 changes: 71 additions & 21 deletions tools/eslint-rules/documented-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,85 @@ const fs = require('fs');
const path = require('path');
const { isDefiningError } = require('./rules-utils.js');

const doc = fs.readFileSync(path.resolve(__dirname, '../../doc/api/errors.md'),
'utf8');
// Load the errors documentation file once
const docPath = path.resolve(__dirname, '../../doc/api/errors.md');
const doc = fs.readFileSync(docPath, 'utf8');

function isInDoc(code) {
return doc.includes(`### \`${code}\``);
}
// Helper function to parse errors documentation and return a Map
function getErrorsInDoc() {
const lines = doc.split('\n');
let currentHeader;
const errors = new Map();
const codePattern = /^### `([^`]+)`$/;
const anchorPattern = /^<a id="([^"]+)"><\/a>$/;

function includesAnchor(code) {
return doc.includes(`<a id="${code}"></a>`);
}
function parse(line, legacy) {
const error = { legacy };
let code;

const codeMatch = line.match(codePattern);
if (codeMatch) {
error.header = true;
code = codeMatch[1];
}

const anchorMatch = line.match(anchorPattern);
if (anchorMatch) {
error.anchor = true;
code ??= anchorMatch[1];
}

if (!code) return;

// If the code already exists in the Map, merge the new error data
errors.set(code, {
...errors.get(code),
...error,
});
}

for (const line of lines) {
if (line.startsWith('## ')) currentHeader = line.substring(3);
if (currentHeader === 'Node.js error codes') parse(line, false);
if (currentHeader === 'Legacy Node.js error codes') parse(line, true);
}

function errorForNode(node) {
return node.expression.arguments[0].value;
return errors;
}

// Main rule export
module.exports = {
create: function(context) {
create(context) {
const errors = getErrorsInDoc();
return {
ExpressionStatement: function(node) {
if (!isDefiningError(node) || !errorForNode(node)) return;
const code = errorForNode(node);
if (!isInDoc(code)) {
const message = `"${code}" is not documented in doc/api/errors.md`;
context.report({ node, message });
ExpressionStatement(node) {
if (!isDefiningError(node)) return;

const code = node.expression.arguments?.[0]?.value;
if (!code) return;

const err = errors.get(code); // Use Map's get method to retrieve the error

if (!err || !err.header) {
context.report({
node,
message: `"${code}" is not documented in doc/api/errors.md`,
});
if (!err) return;
}
if (!includesAnchor(code)) {
const message =
`doc/api/errors.md does not have an anchor for "${code}"`;
context.report({ node, message });

if (!err.anchor) {
context.report({
node,
message: `doc/api/errors.md does not have an anchor for "${code}"`,
});
}

if (err.legacy) {
context.report({
node,
message: `"${code}" is marked as legacy, yet it is used in lib/.`,
});
}
},
};
Expand Down
Loading