Skip to content

Commit 7ed8b4d

Browse files
authored
chore: enforce code formatting with @Stylistic, mirroring nodejs/node (#63)
* add @Stylistic formatting rules mirroring nodejs/node * format codebase to match @Stylistic rules
1 parent 1fd96ff commit 7ed8b4d

23 files changed

Lines changed: 427 additions & 318 deletions

File tree

eslint.config.js

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,134 @@
1-
import { defineConfig, globalIgnores } from "eslint/config";
2-
import globals from "globals";
1+
import { defineConfig, globalIgnores } from 'eslint/config';
2+
import globals from 'globals';
33
import eslint from '@eslint/js';
44
import tseslint from 'typescript-eslint';
5+
import stylistic from '@stylistic/eslint-plugin';
56

67
export default defineConfig([
7-
globalIgnores(["**/CMakeFiles/**"]),
8+
globalIgnores(['**/CMakeFiles/**', 'build/**']),
89
eslint.configs.recommended,
910
tseslint.configs.recommended,
11+
// Formatting rules mirrored from nodejs/node's eslint.config.mjs.
12+
// nodejs/node uses the JS-only @stylistic/eslint-plugin-js; this repo has
13+
// TypeScript, so we use the unified @stylistic/eslint-plugin (same rules,
14+
// JS + TS aware). Keep this block in sync with upstream when it changes.
15+
{
16+
files: ['**/*.{js,mjs,ts}'],
17+
plugins: { '@stylistic': stylistic },
18+
rules: {
19+
'@stylistic/arrow-parens': 'error',
20+
'@stylistic/arrow-spacing': 'error',
21+
'@stylistic/block-spacing': 'error',
22+
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
23+
'@stylistic/comma-dangle': ['error', 'always-multiline'],
24+
'@stylistic/comma-spacing': 'error',
25+
'@stylistic/comma-style': 'error',
26+
'@stylistic/computed-property-spacing': 'error',
27+
'@stylistic/dot-location': ['error', 'property'],
28+
'@stylistic/eol-last': 'error',
29+
'@stylistic/function-call-spacing': 'error',
30+
'@stylistic/indent': ['error', 2, {
31+
ArrayExpression: 'first',
32+
CallExpression: { arguments: 'first' },
33+
FunctionDeclaration: { parameters: 'first' },
34+
FunctionExpression: { parameters: 'first' },
35+
MemberExpression: 'off',
36+
ObjectExpression: 'first',
37+
SwitchCase: 1,
38+
assignmentOperator: 'off',
39+
}],
40+
'@stylistic/key-spacing': 'error',
41+
'@stylistic/keyword-spacing': 'error',
42+
'@stylistic/linebreak-style': 'error',
43+
'@stylistic/max-len': ['error', {
44+
code: 120,
45+
ignorePattern: '^// Flags:',
46+
ignoreRegExpLiterals: true,
47+
ignoreTemplateLiterals: true,
48+
ignoreUrls: true,
49+
tabWidth: 2,
50+
}],
51+
'@stylistic/new-parens': 'error',
52+
'@stylistic/no-confusing-arrow': 'error',
53+
'@stylistic/no-extra-parens': ['error', 'functions'],
54+
'@stylistic/no-multi-spaces': ['error', { ignoreEOLComments: true }],
55+
'@stylistic/no-multiple-empty-lines': ['error', { max: 2, maxEOF: 0, maxBOF: 0 }],
56+
'@stylistic/no-tabs': 'error',
57+
'@stylistic/no-trailing-spaces': 'error',
58+
'@stylistic/no-whitespace-before-property': 'error',
59+
'@stylistic/object-curly-newline': 'error',
60+
'@stylistic/object-curly-spacing': ['error', 'always'],
61+
'@stylistic/one-var-declaration-per-line': 'error',
62+
'@stylistic/operator-linebreak': ['error', 'after'],
63+
'@stylistic/padding-line-between-statements': [
64+
'error',
65+
{ blankLine: 'always', prev: 'function', next: 'function' },
66+
],
67+
'@stylistic/quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: 'always' }],
68+
'@stylistic/quote-props': ['error', 'consistent'],
69+
'@stylistic/rest-spread-spacing': 'error',
70+
'@stylistic/semi': 'error',
71+
'@stylistic/semi-spacing': 'error',
72+
'@stylistic/space-before-blocks': ['error', 'always'],
73+
'@stylistic/space-before-function-paren': ['error', {
74+
anonymous: 'never',
75+
named: 'never',
76+
asyncArrow: 'always',
77+
}],
78+
'@stylistic/space-in-parens': 'error',
79+
'@stylistic/space-infix-ops': 'error',
80+
'@stylistic/space-unary-ops': 'error',
81+
'@stylistic/spaced-comment': ['error', 'always', {
82+
block: { balanced: true },
83+
exceptions: ['-'],
84+
}],
85+
'@stylistic/template-curly-spacing': 'error',
86+
},
87+
},
1088
{
1189
files: [
12-
"tests/**/*.js",
90+
'tests/**/*.js',
1391
],
1492
languageOptions: {
1593
// Only allow ECMAScript built-ins and CTS harness globals.
1694
// This causes no-undef to flag any runtime-specific API (setTimeout, process, Buffer, etc.).
1795
globals: {
1896
...globals.es2025,
1997
// CTS harness globals
20-
assert: "readonly",
21-
loadAddon: "readonly",
22-
mustCall: "readonly",
23-
mustNotCall: "readonly",
24-
gc: "readonly",
25-
gcUntil: "readonly",
26-
experimentalFeatures: "readonly",
27-
napiVersion: "readonly",
28-
skipTest: "readonly",
98+
assert: 'readonly',
99+
loadAddon: 'readonly',
100+
mustCall: 'readonly',
101+
mustNotCall: 'readonly',
102+
gc: 'readonly',
103+
gcUntil: 'readonly',
104+
experimentalFeatures: 'readonly',
105+
onUncaughtException: 'readonly',
106+
napiVersion: 'readonly',
107+
skipTest: 'readonly',
29108
},
30109
},
31110
rules: {
32-
"no-undef": "error",
33-
"no-restricted-imports": ["error", {
34-
patterns: ["*"],
111+
'no-undef': 'error',
112+
'no-restricted-imports': ['error', {
113+
patterns: ['*'],
35114
}],
36-
"no-restricted-syntax": ["error",
37-
{ selector: "MemberExpression[object.name='globalThis']", message: "Avoid globalThis access in test files — use CTS harness globals instead" },
38-
{ selector: "MemberExpression[object.name='global']", message: "Avoid global access in test files — use CTS harness globals instead" }
115+
'no-restricted-syntax': [
116+
'error',
117+
{
118+
selector: "MemberExpression[object.name='globalThis']",
119+
message: 'Avoid globalThis access in test files — use CTS harness globals instead',
120+
},
121+
{
122+
selector: "MemberExpression[object.name='global']",
123+
message: 'Avoid global access in test files — use CTS harness globals instead',
124+
},
39125
],
40126
},
41127
},
42128
{
43129
files: [
44-
"implementors/**/*.{js,ts}",
45-
"scripts/**/*.{js,mjs}",
130+
'implementors/**/*.{js,ts}',
131+
'scripts/**/*.{js,mjs}',
46132
],
47133
languageOptions: {
48134
globals: {

implementors/node/assert.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
deepStrictEqual,
66
throws,
77
match,
8-
} from "node:assert/strict";
8+
} from 'node:assert/strict';
99

1010
const assert = Object.assign((value, message) => ok(value, message), {
1111
ok: (value, message) => ok(value, message),

implementors/node/features.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Each key corresponds to a NODE_API_EXPERIMENTAL_HAS_* compile-time macro.
33
// Other implementors should set unsupported features to false or omit them.
44

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

77
globalThis.experimentalFeatures = {
88
// node_api_is_sharedarraybuffer and node_api_create_sharedarraybuffer were

implementors/node/gc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Capture the engine-provided gc (Node exposes it under --expose-gc) before
22
// we overwrite globalThis.gc with the harness wrapper below.
33
const engineGc = globalThis.gc;
4-
if (typeof engineGc !== "function") {
4+
if (typeof engineGc !== 'function') {
55
throw new Error(
6-
"Node harness expects globalThis.gc to be available (run with --expose-gc)",
6+
'Node harness expects globalThis.gc to be available (run with --expose-gc)',
77
);
88
}
99

implementors/node/load-addon.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import assert from "node:assert/strict";
2-
import { dlopen } from "node:process";
3-
import { constants } from "node:os";
4-
import path from "node:path";
5-
import fs from "node:fs";
1+
import assert from 'node:assert/strict';
2+
import { dlopen } from 'node:process';
3+
import { constants } from 'node:os';
4+
import path from 'node:path';
5+
import fs from 'node:fs';
66

77
const loadAddon = (addonFileName) => {
8-
assert(typeof addonFileName === "string", "Expected a string as addon filename");
9-
assert(!addonFileName.endsWith(".node"), "Expected addon filename without the .node extension");
10-
const addonPath = path.join(process.cwd(), addonFileName + ".node");
8+
assert(typeof addonFileName === 'string', 'Expected a string as addon filename');
9+
assert(!addonFileName.endsWith('.node'), 'Expected addon filename without the .node extension');
10+
const addonPath = path.join(process.cwd(), addonFileName + '.node');
1111
assert(fs.existsSync(addonPath), `Expected ${addonPath} to exist - did you build the addons?`);
1212
const addon = { exports: {} };
1313
dlopen(addon, addonPath, constants.dlopen.RTLD_NOW);

implementors/node/must-call.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const mustCall = (fn, exact = 1) => {
1313
const entry = {
1414
exact,
1515
actual: 0,
16-
name: fn?.name || "<anonymous>",
16+
name: fn?.name || '<anonymous>',
1717
error: new Error(), // capture call-site stack
1818
};
1919
pendingCalls.push(entry);
@@ -28,11 +28,11 @@ const mustCall = (fn, exact = 1) => {
2828
*/
2929
const mustNotCall = (msg) => {
3030
return () => {
31-
throw new Error(msg || "mustNotCall function was called");
31+
throw new Error(msg || 'mustNotCall function was called');
3232
};
3333
};
3434

35-
process.on("exit", () => {
35+
process.on('exit', () => {
3636
for (const entry of pendingCalls) {
3737
if (entry.actual !== entry.exact) {
3838
entry.error.message =

implementors/node/run-tests.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import path from "node:path";
2-
import { test } from "node:test";
1+
import path from 'node:path';
2+
import { test } from 'node:test';
33

4-
import { listDirectoryEntries, runFileInSubprocess } from "./tests.ts";
4+
import { listDirectoryEntries, runFileInSubprocess } from './tests.ts';
55

6-
const ROOT_PATH = path.resolve(import.meta.dirname, "..", "..");
7-
const TESTS_ROOT_PATH = path.join(ROOT_PATH, "tests");
6+
const ROOT_PATH = path.resolve(import.meta.dirname, '..', '..');
7+
const TESTS_ROOT_PATH = path.join(ROOT_PATH, 'tests');
88

99
function populateSuite(
10-
dir: string
10+
dir: string,
1111
) {
1212
const { directories, files } = listDirectoryEntries(dir);
1313

@@ -20,6 +20,6 @@ function populateSuite(
2020
}
2121
}
2222

23-
populateSuite(path.join(TESTS_ROOT_PATH, "harness"));
24-
populateSuite(path.join(TESTS_ROOT_PATH, "js-native-api"));
25-
populateSuite(path.join(TESTS_ROOT_PATH, "node-api"));
23+
populateSuite(path.join(TESTS_ROOT_PATH, 'harness'));
24+
populateSuite(path.join(TESTS_ROOT_PATH, 'js-native-api'));
25+
populateSuite(path.join(TESTS_ROOT_PATH, 'node-api'));

0 commit comments

Comments
 (0)