Skip to content
Open
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
128 changes: 107 additions & 21 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,134 @@
import { defineConfig, globalIgnores } from "eslint/config";
import globals from "globals";
import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import stylistic from '@stylistic/eslint-plugin';

export default defineConfig([
globalIgnores(["**/CMakeFiles/**"]),
globalIgnores(['**/CMakeFiles/**', 'build/**']),
eslint.configs.recommended,
tseslint.configs.recommended,
// Formatting rules mirrored from nodejs/node's eslint.config.mjs.
// nodejs/node uses the JS-only @stylistic/eslint-plugin-js; this repo has
// TypeScript, so we use the unified @stylistic/eslint-plugin (same rules,
// JS + TS aware). Keep this block in sync with upstream when it changes.
{
files: ['**/*.{js,mjs,ts}'],
plugins: { '@stylistic': stylistic },
rules: {
'@stylistic/arrow-parens': 'error',
'@stylistic/arrow-spacing': 'error',
'@stylistic/block-spacing': 'error',
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
'@stylistic/comma-dangle': ['error', 'always-multiline'],
'@stylistic/comma-spacing': 'error',
'@stylistic/comma-style': 'error',
'@stylistic/computed-property-spacing': 'error',
'@stylistic/dot-location': ['error', 'property'],
'@stylistic/eol-last': 'error',
'@stylistic/function-call-spacing': 'error',
'@stylistic/indent': ['error', 2, {
ArrayExpression: 'first',
CallExpression: { arguments: 'first' },
FunctionDeclaration: { parameters: 'first' },
FunctionExpression: { parameters: 'first' },
MemberExpression: 'off',
ObjectExpression: 'first',
SwitchCase: 1,
assignmentOperator: 'off',
}],
'@stylistic/key-spacing': 'error',
'@stylistic/keyword-spacing': 'error',
'@stylistic/linebreak-style': 'error',
'@stylistic/max-len': ['error', {
code: 120,
ignorePattern: '^// Flags:',
ignoreRegExpLiterals: true,
ignoreTemplateLiterals: true,
ignoreUrls: true,
tabWidth: 2,
}],
'@stylistic/new-parens': 'error',
'@stylistic/no-confusing-arrow': 'error',
'@stylistic/no-extra-parens': ['error', 'functions'],
'@stylistic/no-multi-spaces': ['error', { ignoreEOLComments: true }],
'@stylistic/no-multiple-empty-lines': ['error', { max: 2, maxEOF: 0, maxBOF: 0 }],
'@stylistic/no-tabs': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/no-whitespace-before-property': 'error',
'@stylistic/object-curly-newline': 'error',
'@stylistic/object-curly-spacing': ['error', 'always'],
'@stylistic/one-var-declaration-per-line': 'error',
'@stylistic/operator-linebreak': ['error', 'after'],
'@stylistic/padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: 'function', next: 'function' },
],
'@stylistic/quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: 'always' }],
'@stylistic/quote-props': ['error', 'consistent'],
'@stylistic/rest-spread-spacing': 'error',
'@stylistic/semi': 'error',
'@stylistic/semi-spacing': 'error',
'@stylistic/space-before-blocks': ['error', 'always'],
'@stylistic/space-before-function-paren': ['error', {
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
'@stylistic/space-in-parens': 'error',
'@stylistic/space-infix-ops': 'error',
'@stylistic/space-unary-ops': 'error',
'@stylistic/spaced-comment': ['error', 'always', {
block: { balanced: true },
exceptions: ['-'],
}],
'@stylistic/template-curly-spacing': 'error',
},
},
{
files: [
"tests/**/*.js",
'tests/**/*.js',
],
languageOptions: {
// Only allow ECMAScript built-ins and CTS harness globals.
// This causes no-undef to flag any runtime-specific API (setTimeout, process, Buffer, etc.).
globals: {
...globals.es2025,
// CTS harness globals
assert: "readonly",
loadAddon: "readonly",
mustCall: "readonly",
mustNotCall: "readonly",
gc: "readonly",
gcUntil: "readonly",
experimentalFeatures: "readonly",
napiVersion: "readonly",
skipTest: "readonly",
assert: 'readonly',
loadAddon: 'readonly',
mustCall: 'readonly',
mustNotCall: 'readonly',
gc: 'readonly',
gcUntil: 'readonly',
experimentalFeatures: 'readonly',
onUncaughtException: 'readonly',
napiVersion: 'readonly',
skipTest: 'readonly',
},
},
rules: {
"no-undef": "error",
"no-restricted-imports": ["error", {
patterns: ["*"],
'no-undef': 'error',
'no-restricted-imports': ['error', {
patterns: ['*'],
}],
"no-restricted-syntax": ["error",
{ selector: "MemberExpression[object.name='globalThis']", message: "Avoid globalThis access in test files — use CTS harness globals instead" },
{ selector: "MemberExpression[object.name='global']", message: "Avoid global access in test files — use CTS harness globals instead" }
'no-restricted-syntax': [
'error',
{
selector: "MemberExpression[object.name='globalThis']",
message: 'Avoid globalThis access in test files — use CTS harness globals instead',
},
{
selector: "MemberExpression[object.name='global']",
message: 'Avoid global access in test files — use CTS harness globals instead',
},
],
},
},
{
files: [
"implementors/**/*.{js,ts}",
"scripts/**/*.{js,mjs}",
'implementors/**/*.{js,ts}',
'scripts/**/*.{js,mjs}',
],
languageOptions: {
globals: {
Expand Down
2 changes: 1 addition & 1 deletion implementors/node/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
deepStrictEqual,
throws,
match,
} from "node:assert/strict";
} from 'node:assert/strict';

const assert = Object.assign((value, message) => ok(value, message), {
ok: (value, message) => ok(value, message),
Expand Down
2 changes: 1 addition & 1 deletion implementors/node/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Each key corresponds to a NODE_API_EXPERIMENTAL_HAS_* compile-time macro.
// Other implementors should set unsupported features to false or omit them.

const [major, minor, patch] = process.version.slice(1).split(".").map(Number);
const [major, minor, patch] = process.version.slice(1).split('.').map(Number);

globalThis.experimentalFeatures = {
// node_api_is_sharedarraybuffer and node_api_create_sharedarraybuffer were
Expand Down
4 changes: 2 additions & 2 deletions implementors/node/gc.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Capture the engine-provided gc (Node exposes it under --expose-gc) before
// we overwrite globalThis.gc with the harness wrapper below.
const engineGc = globalThis.gc;
if (typeof engineGc !== "function") {
if (typeof engineGc !== 'function') {
throw new Error(
"Node harness expects globalThis.gc to be available (run with --expose-gc)",
'Node harness expects globalThis.gc to be available (run with --expose-gc)',
);
}

Expand Down
16 changes: 8 additions & 8 deletions implementors/node/load-addon.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import assert from "node:assert/strict";
import { dlopen } from "node:process";
import { constants } from "node:os";
import path from "node:path";
import fs from "node:fs";
import assert from 'node:assert/strict';
import { dlopen } from 'node:process';
import { constants } from 'node:os';
import path from 'node:path';
import fs from 'node:fs';

const loadAddon = (addonFileName) => {
assert(typeof addonFileName === "string", "Expected a string as addon filename");
assert(!addonFileName.endsWith(".node"), "Expected addon filename without the .node extension");
const addonPath = path.join(process.cwd(), addonFileName + ".node");
assert(typeof addonFileName === 'string', 'Expected a string as addon filename');
assert(!addonFileName.endsWith('.node'), 'Expected addon filename without the .node extension');
const addonPath = path.join(process.cwd(), addonFileName + '.node');
assert(fs.existsSync(addonPath), `Expected ${addonPath} to exist - did you build the addons?`);
const addon = { exports: {} };
dlopen(addon, addonPath, constants.dlopen.RTLD_NOW);
Expand Down
6 changes: 3 additions & 3 deletions implementors/node/must-call.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const mustCall = (fn, exact = 1) => {
const entry = {
exact,
actual: 0,
name: fn?.name || "<anonymous>",
name: fn?.name || '<anonymous>',
error: new Error(), // capture call-site stack
};
pendingCalls.push(entry);
Expand All @@ -28,11 +28,11 @@ const mustCall = (fn, exact = 1) => {
*/
const mustNotCall = (msg) => {
return () => {
throw new Error(msg || "mustNotCall function was called");
throw new Error(msg || 'mustNotCall function was called');
};
};

process.on("exit", () => {
process.on('exit', () => {
for (const entry of pendingCalls) {
if (entry.actual !== entry.exact) {
entry.error.message =
Expand Down
18 changes: 9 additions & 9 deletions implementors/node/run-tests.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import path from "node:path";
import { test } from "node:test";
import path from 'node:path';
import { test } from 'node:test';

import { listDirectoryEntries, runFileInSubprocess } from "./tests.ts";
import { listDirectoryEntries, runFileInSubprocess } from './tests.ts';

const ROOT_PATH = path.resolve(import.meta.dirname, "..", "..");
const TESTS_ROOT_PATH = path.join(ROOT_PATH, "tests");
const ROOT_PATH = path.resolve(import.meta.dirname, '..', '..');
const TESTS_ROOT_PATH = path.join(ROOT_PATH, 'tests');

function populateSuite(
dir: string
dir: string,
) {
const { directories, files } = listDirectoryEntries(dir);

Expand All @@ -20,6 +20,6 @@ function populateSuite(
}
}

populateSuite(path.join(TESTS_ROOT_PATH, "harness"));
populateSuite(path.join(TESTS_ROOT_PATH, "js-native-api"));
populateSuite(path.join(TESTS_ROOT_PATH, "node-api"));
populateSuite(path.join(TESTS_ROOT_PATH, 'harness'));
populateSuite(path.join(TESTS_ROOT_PATH, 'js-native-api'));
populateSuite(path.join(TESTS_ROOT_PATH, 'node-api'));
Loading
Loading