mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-11 22:50:06 +00:00
chore: Migrate phase-one backend packages to Oxlint (no-changelog) (#39360)
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import { backendConfig } from '@n8n/eslint-config/backend';
|
||||
|
||||
export default defineConfig(backendConfig, globalIgnores(['dist/**']), {
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
});
|
||||
export default defineConfig(backendConfig, globalIgnores(['dist/**']));
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
ignorePatterns: ['dist/**'],
|
||||
});
|
||||
@@ -21,8 +21,8 @@
|
||||
"build:unchecked": "tsc --build tsconfig.build.esm.json tsconfig.build.cjs.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc --build tsconfig.build.esm.json tsconfig.build.cjs.json --watch",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test:unit": "vitest run --passWithNoTests",
|
||||
@@ -35,8 +35,11 @@
|
||||
"@n8n/ai-utilities": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import { backendConfig } from '@n8n/eslint-config/backend';
|
||||
|
||||
export default defineConfig(
|
||||
backendConfig,
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-wrapper-object-types': 'warn',
|
||||
},
|
||||
export default defineConfig(backendConfig, {
|
||||
files: ['**/*.test.ts'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-uncaught-json-parse': 'warn',
|
||||
'@typescript-eslint/no-unsafe-return': 'warn',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'@typescript-eslint/unbound-method': 'warn',
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.ts'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-uncaught-json-parse': 'warn',
|
||||
'@typescript-eslint/no-unsafe-return': 'warn',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'@typescript-eslint/unbound-method': 'warn',
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.test.ts'],
|
||||
jsPlugins: ['@n8n/eslint-config/plugin'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-uncaught-json-parse': 'warn',
|
||||
'typescript/no-unsafe-return': 'warn',
|
||||
'typescript/no-unsafe-assignment': 'warn',
|
||||
'typescript/no-unsafe-argument': 'warn',
|
||||
'typescript/unbound-method': 'warn',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -38,11 +38,14 @@
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@types/stream-json": "1.7.8",
|
||||
"@types/yargs-parser": "21.0.0",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -39,7 +39,7 @@ lints and tests here without edits:
|
||||
| Concern | Setup | Mirrors |
|
||||
| --- | --- | --- |
|
||||
| TypeScript | `common.go` + `backend.go`, `lib` es2023, `strictFunctionTypes`, `strictPropertyInitialization` and `useUnknownInCatchVariables` off | `packages/cli/tsconfig.json` |
|
||||
| Lint | `oxlint --type-aware`, then the ESLint guardrails pass. `eslint.config.mjs` is the policy twin that code-health and the guardrails read | `packages/cli/oxlint.config.mts`, `eslint.guardrails.config.mjs` |
|
||||
| Lint | `oxlint --type-aware`. `eslint.config.mjs` is the policy twin that code-health reads | `packages/cli/oxlint.config.mts`, `eslint.config.mjs` |
|
||||
| Tests | Vitest with the decorators config, one fork per file, the same `N8N_USER_FOLDER` and `N8N_ENCRYPTION_KEY` setup | `packages/cli/vitest.config.base.ts`, `test/setup-test-folder.ts` |
|
||||
|
||||
Two things do not carry over on purpose:
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import { functionalGuardrailsConfig } from '@n8n/eslint-config/functional-guardrails';
|
||||
import { localRulesPlugin } from '@n8n/eslint-config/plugin';
|
||||
import packageConfig from './eslint.config.mjs';
|
||||
|
||||
const directivePluginConfigs = packageConfig.flatMap(({ plugins }) => {
|
||||
const externalPlugins = Object.fromEntries(
|
||||
Object.entries(plugins ?? {}).filter(([name]) => name !== 'n8n-local-rules'),
|
||||
);
|
||||
|
||||
return Object.keys(externalPlugins).length === 0 ? [] : [{ plugins: externalPlugins }];
|
||||
});
|
||||
|
||||
const dependencyRuleConfigs = packageConfig.flatMap(({ files, ignores, settings, rules }) => {
|
||||
const rule = rules?.['import-x/no-extraneous-dependencies'];
|
||||
if (!rule) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
...(files ? { files } : {}),
|
||||
...(ignores ? { ignores } : {}),
|
||||
...(settings ? { settings } : {}),
|
||||
rules: { 'import-x/no-extraneous-dependencies': rule },
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
export default defineConfig(
|
||||
globalIgnores([
|
||||
'dist/**',
|
||||
'coverage/**',
|
||||
'eslint.config.mjs',
|
||||
'eslint.guardrails.config.mjs',
|
||||
'oxlint.config.mts',
|
||||
'vitest.config.ts',
|
||||
]),
|
||||
{
|
||||
plugins: { 'n8n-local-rules': localRulesPlugin },
|
||||
},
|
||||
functionalGuardrailsConfig,
|
||||
...directivePluginConfigs,
|
||||
...dependencyRuleConfigs,
|
||||
);
|
||||
@@ -4,10 +4,10 @@ import { defineConfig } from 'oxlint';
|
||||
// Same rule set as packages/cli/oxlint.config.mts, so a file moved from cli lints the same way.
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
ignorePatterns: ['coverage/**', 'eslint.guardrails.config.mjs'],
|
||||
options: { typeAware: true },
|
||||
ignorePatterns: ['coverage/**'],
|
||||
rules: {
|
||||
'n8n-local-rules/misplaced-n8n-typeorm-import': 'error',
|
||||
// The narrow ESLint guardrail config owns no-unsealed-workflow-entity-write.
|
||||
'n8n-local-rules/no-guardrail-disable': [
|
||||
'error',
|
||||
{
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "pnpm run lint:base && pnpm run lint:guardrails",
|
||||
"lint:base": "oxlint --type-aware . --quiet",
|
||||
"lint:fix": "oxlint --type-aware . --fix && pnpm run lint:guardrails",
|
||||
"lint:guardrails": "eslint --config eslint.guardrails.config.mjs . --quiet",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test:unit": "vitest run --passWithNoTests",
|
||||
@@ -30,7 +28,6 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.test.ts'],
|
||||
rules: {
|
||||
'id-denylist': 'warn',
|
||||
'typescript/no-unsafe-return': 'warn',
|
||||
'typescript/no-unsafe-call': 'warn',
|
||||
'typescript/no-unsafe-member-access': 'warn',
|
||||
'typescript/no-unsafe-assignment': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/client-oauth2.ts'],
|
||||
jsPlugins: ['@n8n/eslint-config/plugin'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-uncentralized-http': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write src test",
|
||||
"format:check": "biome ci src test",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -28,10 +28,13 @@
|
||||
"axios": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"https-proxy-agent": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
});
|
||||
@@ -16,8 +16,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write src",
|
||||
"format:check": "biome ci src",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -55,12 +55,15 @@
|
||||
"zod-to-json-schema": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/mime-types": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/yargs-parser": "21.0.0",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.config.ts'],
|
||||
jsPlugins: ['@n8n/eslint-config/plugin'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-untyped-config-class-field': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write src test",
|
||||
"format:check": "biome ci src test",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -29,9 +29,12 @@
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -5,10 +5,15 @@ export default defineConfig(
|
||||
backendConfig,
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-base-to-string': 'warn',
|
||||
'import-x/export': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/redactable.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-base-to-string': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.ts'],
|
||||
rules: {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
overrides: [
|
||||
{
|
||||
files: ['src/redactable.ts'],
|
||||
rules: {
|
||||
'typescript/no-base-to-string': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.ts'],
|
||||
rules: {
|
||||
'no-unused-expressions': 'warn',
|
||||
'typescript/no-unsafe-assignment': 'warn',
|
||||
'typescript/unbound-method': 'warn',
|
||||
'import/no-duplicates': 'warn',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -35,10 +35,13 @@
|
||||
"dist/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/express": "catalog:",
|
||||
"@types/lodash": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vitest": "catalog:",
|
||||
"zod": "catalog:"
|
||||
|
||||
@@ -334,18 +334,6 @@ export const baseConfig = tseslint.config(
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
|
||||
/**
|
||||
* https://www.typescriptlang.org/docs/handbook/enums.html#const-enums
|
||||
*/
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'TSEnumDeclaration:not([const=true])',
|
||||
message:
|
||||
'Do not declare raw enums as it leads to runtime overhead. Use const enum instead. See https://www.typescriptlang.org/docs/handbook/enums.html#const-enums',
|
||||
},
|
||||
],
|
||||
|
||||
// ----------------------------------
|
||||
// no-unused-imports
|
||||
// ----------------------------------
|
||||
|
||||
@@ -31,6 +31,7 @@ export const localRulesPlugin = {
|
||||
'n8n-local-rules/no-type-only-import-in-di': 'error',
|
||||
'n8n-local-rules/no-aws-credential-discovery-imports': 'error',
|
||||
'n8n-local-rules/no-application-error': 'error',
|
||||
'n8n-local-rules/no-raw-enum': 'error',
|
||||
'n8n-local-rules/no-dynamic-regexp': 'warn',
|
||||
'n8n-local-rules/no-restricted-sleep-definition': 'error',
|
||||
'n8n-local-rules/no-restricted-sleep-import': 'error',
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { localRulesPlugin } from '@n8n/eslint-config/plugin';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config({
|
||||
files: ['**/*.ts'],
|
||||
languageOptions: { parser: tseslint.parser },
|
||||
plugins: { 'n8n-local-rules': localRulesPlugin },
|
||||
rules: { 'n8n-local-rules/no-raw-enum': 'error' },
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export enum NumericStatus {
|
||||
Ready,
|
||||
}
|
||||
|
||||
export enum StringStatus {
|
||||
Ready = 'ready',
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
categories: { correctness: 'off' },
|
||||
jsPlugins: ['@n8n/eslint-config/plugin'],
|
||||
rules: { 'n8n-local-rules/no-raw-enum': 'error' },
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
const enum Status {
|
||||
Ready,
|
||||
}
|
||||
|
||||
export const status = Status.Ready;
|
||||
@@ -32,6 +32,7 @@ import { NoUnsealedCredentialsEntityWriteRule } from './no-unsealed-credentials-
|
||||
import { RequireEscapedQueryValuesRule } from './require-escaped-query-values.js';
|
||||
import { NoOnLeaderTakeoverRule } from './no-on-leader-takeover.js';
|
||||
import { NoEncryptionGuardrailDisableRule } from './no-encryption-guardrail-disable.js';
|
||||
import { NoRawEnumRule } from './no-raw-enum.js';
|
||||
|
||||
export const rules = {
|
||||
'no-uncaught-json-parse': NoUncaughtJsonParseRule,
|
||||
@@ -67,4 +68,5 @@ export const rules = {
|
||||
'require-escaped-query-values': RequireEscapedQueryValuesRule,
|
||||
'no-on-leader-takeover': NoOnLeaderTakeoverRule,
|
||||
'no-encryption-guardrail-disable': NoEncryptionGuardrailDisableRule,
|
||||
'no-raw-enum': NoRawEnumRule,
|
||||
} satisfies Record<string, AnyRuleModule>;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { RuleTester } from '@typescript-eslint/rule-tester';
|
||||
import { NoRawEnumRule } from './no-raw-enum.js';
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
ruleTester.run('no-raw-enum', NoRawEnumRule, {
|
||||
valid: [
|
||||
'type Status = "ready" | "running";',
|
||||
'const enum Status { Ready, Running }',
|
||||
{
|
||||
code: 'declare const enum Status { Ready, Running }',
|
||||
filename: 'types.d.ts',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'enum Status { Ready, Running }',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 1 }],
|
||||
},
|
||||
{
|
||||
code: 'enum Status { Ready = "ready", Running = "running" }',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 1 }],
|
||||
},
|
||||
{
|
||||
code: 'enum Status {}',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 1 }],
|
||||
},
|
||||
{
|
||||
code: 'export enum Status { Ready }',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 8 }],
|
||||
},
|
||||
{
|
||||
code: 'declare enum Status { Ready }',
|
||||
filename: 'types.d.ts',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 1 }],
|
||||
},
|
||||
{
|
||||
code: 'declare namespace Api { enum Status { Ready } }',
|
||||
filename: 'types.d.ts',
|
||||
errors: [{ messageId: 'noRawEnum', line: 1, column: 25 }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
|
||||
export const NoRawEnumRule = ESLintUtils.RuleCreator.withoutDocs({
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Require const enums to avoid runtime enum objects.',
|
||||
},
|
||||
messages: {
|
||||
noRawEnum:
|
||||
'Do not declare raw enums as it leads to runtime overhead. Use const enum instead. See https://www.typescriptlang.org/docs/handbook/enums.html#const-enums',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
return {
|
||||
TSEnumDeclaration(node) {
|
||||
if (!node.const) context.report({ node, messageId: 'noRawEnum' });
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -4,9 +4,5 @@ import { backendConfig } from '@n8n/eslint-config/backend';
|
||||
export default defineConfig(backendConfig, {
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
|
||||
// TODO: Remove this
|
||||
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write src test",
|
||||
"format:check": "biome ci src test",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -28,9 +28,12 @@
|
||||
"libmime": "5.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/libmime": "^5.3.0",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
ignorePatterns: ['electron-builder.config.js', 'scripts/**'],
|
||||
});
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.renderer.json && tsc -p tsconfig.build.json && pnpm copy:renderer && pnpm copy:assets",
|
||||
"build:unchecked": "tsc -p tsconfig.renderer.json --noCheck && tsc -p tsconfig.build.json --noCheck && pnpm copy:renderer && pnpm copy:assets",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint": "oxlint --quiet",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"copy:assets": "node scripts/copy-assets.js",
|
||||
"copy:renderer": "node scripts/copy-renderer.js",
|
||||
@@ -29,12 +29,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/eslint-config": "workspace:*",
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"concurrently": "^8.2.0",
|
||||
"electron-builder": "^26.9.0",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"rimraf": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { CreateCheckFn, CustomRegex } from '../types';
|
||||
* Includes global and region-specific types (US, UK, Spain, Italy, etc.).
|
||||
* These map to regex patterns for detection.
|
||||
*/
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
// eslint-disable-next-line n8n-local-rules/no-raw-enum
|
||||
export enum PIIEntity {
|
||||
// Global
|
||||
CREDIT_CARD = 'CREDIT_CARD',
|
||||
|
||||
@@ -223,6 +223,7 @@ export const baseConfig = defineConfig({
|
||||
'n8n-local-rules/no-json-parse-json-stringify': 'error',
|
||||
'n8n-local-rules/no-restricted-sleep-definition': 'error',
|
||||
'n8n-local-rules/no-restricted-sleep-import': 'error',
|
||||
'n8n-local-rules/no-raw-enum': 'error',
|
||||
'n8n-local-rules/no-type-only-import-in-di': 'error',
|
||||
'n8n-local-rules/no-unsealed-credentials-entity-write': 'error',
|
||||
'n8n-local-rules/no-unsealed-workflow-entity-write': 'error',
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
ignorePatterns: ['stryker.config.mjs', 'vitest.stryker.config.ts', '.stryker-tmp/**'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['./src/**/*.ts'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: ['@n8n/db', '@n8n/di', '@n8n/typeorm'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"mutate": "node ../../../scripts/mutation-health/mutate.mjs --package-dir packages/@n8n/scheduler",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
@@ -37,6 +37,7 @@
|
||||
"n8n-workflow": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@stryker-mutator/core": "catalog:",
|
||||
@@ -44,6 +45,8 @@
|
||||
"@types/luxon": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"fast-check": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.config.ts'],
|
||||
jsPlugins: ['@n8n/eslint-config/plugin'],
|
||||
rules: {
|
||||
'n8n-local-rules/no-untyped-config-class-field': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -9,8 +9,8 @@
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write src test",
|
||||
"format:check": "biome ci src test",
|
||||
"lint": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "oxlint --quiet",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
@@ -26,9 +26,12 @@
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable n8n-local-rules/no-raw-enum */
|
||||
/* We want the runtime overhead here */
|
||||
|
||||
/**
|
||||
@@ -19,8 +19,7 @@ export enum Facility {
|
||||
User = 1,
|
||||
Mail = 2,
|
||||
System = 3,
|
||||
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
|
||||
Daemon = 3,
|
||||
Daemon = System,
|
||||
Auth = 4,
|
||||
Syslog = 5,
|
||||
Lpr = 6,
|
||||
|
||||
@@ -30,7 +30,7 @@ cannot read (TypeScript error TS2748). A plain `enum` emits a regular
|
||||
`dist` across the package boundary, and it preserves the nominal enum-member
|
||||
types the rest of the front end relies on (so relocating a value such as `VIEWS`
|
||||
here is behavior-preserving). The repo lint default prefers `const enum`, so the
|
||||
`no-restricted-syntax` rule is relaxed for these files in `eslint.config.mjs`.
|
||||
`no-raw-enum` rule is relaxed for these files in `eslint.config.mjs`.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ export default defineConfig(
|
||||
// `VIEWS` is declared as a plain `enum` (not a `const enum`) so this package's
|
||||
// `dist` stays consumable by `isolatedModules` downstream packages (a `const
|
||||
// enum` emits an ambient const enum → TS2748). See the doc comment in views.ts.
|
||||
// - `no-restricted-syntax` otherwise bans raw enums in favor of `const enum`.
|
||||
// - `no-raw-enum` otherwise bans raw enums in favor of `const enum`.
|
||||
// - view identifiers are UPPER_CASE by long-standing app convention.
|
||||
files: ['src/views.ts'],
|
||||
rules: {
|
||||
'no-restricted-syntax': 'off',
|
||||
'n8n-local-rules/no-raw-enum': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
* The repo's default lint bans raw enums in favor of `const enum` for runtime
|
||||
* overhead; that guidance is inverted here because `const enum` is precisely what
|
||||
* breaks dist consumption. The `no-restricted-syntax` rule (and the camelCase
|
||||
* breaks dist consumption. The `no-raw-enum` rule (and the camelCase
|
||||
* naming-convention rule, since view identifiers are UPPER_CASE by convention)
|
||||
* are relaxed for this file in `eslint.config.mjs`.
|
||||
*
|
||||
|
||||
@@ -34,7 +34,7 @@ interface SessionOption {
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
enum PreviewLayout {
|
||||
const enum PreviewLayout {
|
||||
Docked = 'docked',
|
||||
Fullpage = 'fullpage',
|
||||
}
|
||||
|
||||
Generated
+90
-3
@@ -1315,12 +1315,21 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../ai-utilities
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
'@n8n/vitest-config':
|
||||
specifier: workspace:*
|
||||
version: link:../vitest-config
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -1640,6 +1649,9 @@ importers:
|
||||
specifier: 21.1.1
|
||||
version: 21.1.1
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -1655,6 +1667,12 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.9(@vitest/[email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -1764,9 +1782,6 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.9(@vitest/[email protected])([email protected])
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.29.0([email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
@@ -1974,6 +1989,9 @@ importers:
|
||||
specifier: 1.18.0
|
||||
version: 1.18.0(patch_hash=149e256a2a7b632497650b32816716ced972ab02a5ab00fbd8a5158a51722c49)([email protected]([email protected]))([email protected])
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -1989,6 +2007,12 @@ importers:
|
||||
nock:
|
||||
specifier: 'catalog:'
|
||||
version: 14.0.14
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -2176,6 +2200,9 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 3.23.3([email protected])
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -2194,6 +2221,12 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.9(@vitest/[email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -2219,6 +2252,9 @@ importers:
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -2228,6 +2264,12 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.9(@vitest/[email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -2418,6 +2460,9 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../workflow
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -2430,6 +2475,12 @@ importers:
|
||||
'@types/lodash':
|
||||
specifier: 'catalog:'
|
||||
version: 4.17.17
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -2822,6 +2873,9 @@ importers:
|
||||
specifier: 5.4.2
|
||||
version: 5.4.2
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -2831,6 +2885,12 @@ importers:
|
||||
'@types/libmime':
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -3007,6 +3067,9 @@ importers:
|
||||
'@n8n/eslint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -3025,6 +3088,12 @@ importers:
|
||||
electron-builder:
|
||||
specifier: ^26.9.0
|
||||
version: 26.9.0([email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
rimraf:
|
||||
specifier: 'catalog:'
|
||||
version: 6.0.1
|
||||
@@ -3804,6 +3873,9 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../workflow
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -3825,6 +3897,12 @@ importers:
|
||||
fast-check:
|
||||
specifier: 'catalog:'
|
||||
version: 3.23.2
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
@@ -3878,6 +3956,9 @@ importers:
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
@@ -3887,6 +3968,12 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.9(@vitest/[email protected])([email protected])
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0([email protected])
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
|
||||
@@ -21,7 +21,6 @@ These rules come from the shared base or backend layers. Resolve these first bec
|
||||
|
||||
| Priority | Rule | Blocker | Suggested next action |
|
||||
| --- | --- | --- | --- |
|
||||
| P1 | `no-restricted-syntax` | Oxlint has no AST-selector rule. The base layer uses it for the raw-enum restriction. | Add a purpose-built syntax rule for each active selector. Start with the raw-enum rule. |
|
||||
| P1 | `typescript/consistent-type-imports` | Oxlint does not honor the `emitDecoratorMetadata` exception. Its fixer can remove imports that DI needs at runtime. | Wait for upstream support or add a tested shim that skips decorated files. Do not enable the current fixer. |
|
||||
| P1 | `typescript/prefer-optional-chain` | The native nursery rule changes guarded undeclared globals incorrectly. | Re-test after the rule leaves nursery or after the upstream fixer issue is resolved. |
|
||||
| P2 | `import/export` | A native rule exists, but it is still in the nursery category. | Run parity samples and enable it after it becomes stable. |
|
||||
|
||||
@@ -7,13 +7,6 @@
|
||||
"rule id when one exists but we still do not enable it. `replacement` records",
|
||||
"the intended migration path for each gap."
|
||||
],
|
||||
"no-restricted-syntax": {
|
||||
"layer": "base",
|
||||
"native": null,
|
||||
"reason": "oxlint 1.78 has no AST-selector rule and no equivalent. It is an ESLint core rule, so a jsPlugin cannot host it either: `Rule 'no-restricted-syntax' not found in plugin 'eslint'`. base uses it for the raw-enum ban; editor-ui uses it for store-migration ratchets, and editor-ui keeps ESLint.",
|
||||
"impact": "The const-enum ban is unenforced in migrated packages.",
|
||||
"replacement": "Add a native selector rule, or replace each selector use with a purpose-built syntactic rule."
|
||||
},
|
||||
"no-octal": {
|
||||
"layer": "base",
|
||||
"native": null,
|
||||
@@ -43,6 +36,13 @@
|
||||
"replacement": "Wait for the upstream emitDecoratorMetadata option, or use an approved shim that suppresses decorated files.",
|
||||
"upstream": "https://github.com/oxc-project/oxc/issues/13609"
|
||||
},
|
||||
"frontend/no-restricted-syntax": {
|
||||
"layer": "frontend",
|
||||
"native": null,
|
||||
"reason": "editor-ui uses package-specific AST selectors for workflow-store and modal-migration ratchets. The frontend keeps ESLint for these selectors.",
|
||||
"impact": "None for the backend migration.",
|
||||
"replacement": "Replace each selector with a focused rule before an Oxlint-only frontend migration."
|
||||
},
|
||||
"frontend/vue-template-rules": {
|
||||
"layer": "frontend",
|
||||
"native": null,
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
* local rules would read as missing. Those are read from the config objects
|
||||
* instead, which is weaker: it describes what we declared, not what oxlint
|
||||
* resolved. A fixture probe is the only way to prove they fire.
|
||||
* - `--print-config` resolves the config, not a file, so `overrides` do not
|
||||
* appear. Both sides are therefore compared as a union across the package's
|
||||
* sample files.
|
||||
* - `--print-config` does not apply overrides to its top-level rule table. Both
|
||||
* sides are therefore compared as a union across the package's sample files.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { dirname, join, matchesGlob, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
@@ -114,6 +113,16 @@ function isEnabled(severity) {
|
||||
return value === 'deny' || value === 'error' || value === 2;
|
||||
}
|
||||
|
||||
const matchesPattern = (file, pattern) => matchesGlob(file, pattern.replace(/^\.\//, ''));
|
||||
|
||||
function overrideMatchesSample({ files = [], excludeFiles = [], ignores = [] }) {
|
||||
return sampledFiles.some(
|
||||
(file) =>
|
||||
(files.length === 0 || files.some((pattern) => matchesPattern(file, pattern))) &&
|
||||
![...excludeFiles, ...ignores].some((pattern) => matchesPattern(file, pattern)),
|
||||
);
|
||||
}
|
||||
|
||||
/** Native rules oxlint actually resolved for this package. */
|
||||
function resolvedNativeRules() {
|
||||
const out = spawnSync('pnpm', ['exec', 'oxlint', '--print-config'], {
|
||||
@@ -126,7 +135,12 @@ function resolvedNativeRules() {
|
||||
throw new Error(`oxlint --print-config failed: ${(out.stderr || out.stdout).trim()}`);
|
||||
}
|
||||
const parsed = JSON.parse(out.stdout.slice(start));
|
||||
return Object.entries(parsed.rules ?? {})
|
||||
return [
|
||||
...Object.entries(parsed.rules ?? {}),
|
||||
...(parsed.overrides ?? [])
|
||||
.filter(overrideMatchesSample)
|
||||
.flatMap(({ rules }) => Object.entries(rules ?? {})),
|
||||
]
|
||||
.filter(([, severity]) => isEnabled(severity))
|
||||
.map(([id]) => id);
|
||||
}
|
||||
@@ -138,7 +152,7 @@ function resolvedNativeRules() {
|
||||
async function declaredJsPluginRules() {
|
||||
const mod = await import(pathToFileURL(join(root, pkgDir, 'oxlint.config.mts')).href);
|
||||
const declared = new Map();
|
||||
const overrideOnly = new Set();
|
||||
const overrideOnly = new Map();
|
||||
const plugins = new Set();
|
||||
|
||||
const visit = (config) => {
|
||||
@@ -151,7 +165,11 @@ async function declaredJsPluginRules() {
|
||||
// An override that turns a rule off still leaves it enforced elsewhere
|
||||
// in the package, and this comparison is a union.
|
||||
if (!isEnabled(severity)) continue;
|
||||
if (!declared.has(id)) overrideOnly.add(id);
|
||||
if (!isEnabled(declared.get(id)) || overrideOnly.has(id)) {
|
||||
const overrides = overrideOnly.get(id) ?? [];
|
||||
overrides.push(override);
|
||||
overrideOnly.set(id, overrides);
|
||||
}
|
||||
declared.set(id, severity);
|
||||
}
|
||||
}
|
||||
@@ -164,12 +182,6 @@ async function declaredJsPluginRules() {
|
||||
return { ids, overrideOnly, plugins: [...plugins] };
|
||||
}
|
||||
|
||||
// The test-file override is only comparable when a test file was sampled:
|
||||
// ESLint resolves its config per file, oxlint's --print-config does not resolve
|
||||
// overrides at all, so without a test sample there is nothing to compare and an
|
||||
// override-only rule would read as unrequested.
|
||||
const isTestSample = (key) => /(\.test\.ts|\/__tests__\/|^test\/)/.test(key.split('|')[1] ?? '');
|
||||
|
||||
const native = oxlintRuleIds();
|
||||
const gap = JSON.parse(readFileSync(gapFile, 'utf8'));
|
||||
|
||||
@@ -177,11 +189,11 @@ const gap = JSON.parse(readFileSync(gapFile, 'utf8'));
|
||||
const snapshot = JSON.parse(readFileSync(resolve(eslintFile), 'utf8'));
|
||||
const eslintRules = new Set();
|
||||
let sampled = 0;
|
||||
let sampledTestFile = false;
|
||||
const sampledFiles = [];
|
||||
for (const [key, entry] of Object.entries(snapshot)) {
|
||||
if (!key.startsWith(`${pkgDir}|`) || !entry.rules) continue;
|
||||
sampled++;
|
||||
if (isTestSample(key)) sampledTestFile = true;
|
||||
sampledFiles.push(key.split('|')[1]);
|
||||
for (const id of Object.keys(entry.rules)) eslintRules.add(id);
|
||||
}
|
||||
if (sampled === 0) {
|
||||
@@ -191,12 +203,13 @@ if (sampled === 0) {
|
||||
|
||||
const { ids: jsPluginIds, overrideOnly, plugins } = await declaredJsPluginRules();
|
||||
|
||||
const comparable = (id) => !overrideOnly.has(id) || sampledTestFile;
|
||||
const comparable = (id) => {
|
||||
const overrides = overrideOnly.get(id);
|
||||
return !overrides || overrides.some(overrideMatchesSample);
|
||||
};
|
||||
|
||||
const oxlintRules = new Set(
|
||||
[...resolvedNativeRules(), ...jsPluginIds]
|
||||
.filter(comparable)
|
||||
.map((id) => canonical(id, native)),
|
||||
[...resolvedNativeRules(), ...jsPluginIds].filter(comparable).map((id) => canonical(id, native)),
|
||||
);
|
||||
|
||||
const matched = [];
|
||||
@@ -225,7 +238,9 @@ const undocumented = missing.filter((entry) => !isDocumented(entry));
|
||||
console.log(`${pkgDir}: ${sampled} sample files, ${eslintRules.size} ESLint rules at error`);
|
||||
console.log(` jsPlugins declared: ${plugins.join(', ') || 'none'}`);
|
||||
console.log(` matched: ${matched.length}`);
|
||||
console.log(` missing in oxlint: ${missing.length} (${missing.length - undocumented.length} documented)`);
|
||||
console.log(
|
||||
` missing in oxlint: ${missing.length} (${missing.length - undocumented.length} documented)`,
|
||||
);
|
||||
console.log(` extra in oxlint: ${extra.length}`);
|
||||
|
||||
for (const id of extra) console.log(` EXTRA ${id}`);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"src/dns/__tests__/dns-resolver.test.ts",
|
||||
"src/dns/dns-resolver.ts"
|
||||
],
|
||||
"packages/@n8n/backend-services": ["src/index.ts", "test/setup-test-folder.ts"],
|
||||
"packages/@n8n/backend-test-utils": ["src/db/projects.ts"],
|
||||
"packages/@n8n/benchmark": ["src/commands/list.ts"],
|
||||
"packages/@n8n/blob-storage": [
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const fixtureDirectory = fileURLToPath(
|
||||
new URL('../../packages/@n8n/eslint-config/src/rules/fixtures/raw-enum/', import.meta.url),
|
||||
);
|
||||
const oxlintPackageDirectory = fileURLToPath(
|
||||
new URL('../../packages/@n8n/oxlint-config/', import.meta.url),
|
||||
);
|
||||
|
||||
const run = (command, args, cwd) => {
|
||||
try {
|
||||
return execFileSync(command, args, { cwd, encoding: 'utf8' });
|
||||
} catch (error) {
|
||||
if (error.status === 1 && typeof error.stdout === 'string') return error.stdout;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const eslint = JSON.parse(
|
||||
run(
|
||||
'pnpm',
|
||||
['exec', 'eslint', '--config', 'eslint.config.mjs', '--format', 'json', '.'],
|
||||
fixtureDirectory,
|
||||
),
|
||||
);
|
||||
const oxlint = JSON.parse(
|
||||
run(
|
||||
'pnpm',
|
||||
[
|
||||
'exec',
|
||||
'oxlint',
|
||||
'--config',
|
||||
`${fixtureDirectory}/oxlint.config.mts`,
|
||||
'--format',
|
||||
'json',
|
||||
fixtureDirectory,
|
||||
],
|
||||
oxlintPackageDirectory,
|
||||
),
|
||||
);
|
||||
|
||||
const eslintDiagnostics = eslint
|
||||
.flatMap(({ messages }) => messages)
|
||||
.filter(({ ruleId }) => ruleId === 'n8n-local-rules/no-raw-enum')
|
||||
.map(({ ruleId, message, line, column }) => ({ ruleId, message, line, column }));
|
||||
|
||||
const oxlintDiagnostics = oxlint.diagnostics
|
||||
.filter(({ code }) => code === 'n8n-local-rules(no-raw-enum)')
|
||||
.map(({ code, message, labels }) => ({
|
||||
ruleId: code.replace('(', '/').replace(')', ''),
|
||||
message,
|
||||
line: labels[0].span.line,
|
||||
column: labels[0].span.column,
|
||||
}));
|
||||
|
||||
assert.deepEqual(oxlintDiagnostics, eslintDiagnostics);
|
||||
assert.equal(eslintDiagnostics.length, 2);
|
||||
|
||||
console.log('Raw-enum ESLint/Oxlint bridge parity passed.');
|
||||
+39
-3
@@ -57,15 +57,51 @@
|
||||
"lint": {
|
||||
"dependsOn": ["^build", "@n8n/eslint-config#build"]
|
||||
},
|
||||
// oxlint spike packages (DEVP-584). They run oxlint instead of ESLint, so
|
||||
// Oxlint packages run Oxlint instead of ESLint, so
|
||||
// they need the oxlint layer built, not the ESLint one.
|
||||
"@n8n/errors#lint": {
|
||||
"@n8n/ai-node-sdk#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/backend-common#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/backend-services#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/client-oauth2#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/computer-use#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/config#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/crdt#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/decorators#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/di#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/crdt#lint": {
|
||||
"@n8n/errors#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/imap#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/local-gateway#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/scheduler#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"@n8n/syslog-client#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
"n8n#lint": {
|
||||
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
|
||||
},
|
||||
// The n8n-nodes-base lint script also validates load-options methods
|
||||
|
||||
Reference in New Issue
Block a user