chore: Migrate testing infrastructure lint to Oxlint (no-changelog) (#39942)

This commit is contained in:
Matsu 2026-09-30 10:55:11 +00:00 committed by GitHub
parent 10e484f552
commit 849ededd5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 602 additions and 148 deletions

View File

@ -1,24 +1,6 @@
import { defineConfig } from 'eslint/config';
import { backendConfig } from '@n8n/eslint-config/backend';
export default defineConfig(
backendConfig,
{
ignores: ['coverage/**', 'dist/**'],
},
{
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'objectLiteralProperty',
format: null,
filter: {
regex: '^[a-z]+-[a-z-]+$',
match: true,
},
},
],
},
},
);
export default defineConfig(backendConfig, {
ignores: ['coverage/**', 'dist/**'],
});

View File

@ -0,0 +1,8 @@
import { backendConfig } from '@n8n/oxlint-config/backend';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [backendConfig],
options: { typeAware: true },
ignorePatterns: ['coverage/**'],
});

View File

@ -23,8 +23,8 @@
"test:coverage": "vitest run --coverage",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint . --quiet",
"lint:fix": "eslint . --fix",
"lint": "oxlint --quiet",
"lint:fix": "oxlint --fix",
"typecheck": "tsc --noEmit"
},
"license": "MIT",
@ -40,9 +40,13 @@
"yaml": "catalog:"
},
"devDependencies": {
"@n8n/eslint-config": "workspace:*",
"@n8n/oxlint-config": "workspace:*",
"@types/markdown-it": "catalog:",
"@vitest/coverage-v8": "catalog:",
"fast-check": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"vitest": "catalog:"
}
}

View File

@ -1,30 +1,11 @@
import { defineConfig } from 'eslint/config';
import { backendConfig } from '@n8n/eslint-config/backend';
export default defineConfig(
backendConfig,
{
rules: {
'@typescript-eslint/naming-convention': [
'error',
// Add exception for Docker Compose labels
{
selector: 'objectLiteralProperty',
format: null, // Allow any format
filter: {
regex: '^com\\.docker\\.',
match: true,
},
},
],
},
export default defineConfig(backendConfig, {
files: ['services/keycloak.ts'],
// Test infrastructure that talks to a container it started itself,
// so the guarded client buys nothing here.
rules: {
'n8n-local-rules/no-uncentralized-http': 'off',
},
{
files: ['services/keycloak.ts'],
// Test infrastructure that talks to a container it started itself,
// so the guarded client buys nothing here.
rules: {
'n8n-local-rules/no-uncentralized-http': 'off',
},
},
);
});

View File

@ -0,0 +1,14 @@
import { backendConfig } from '@n8n/oxlint-config/backend';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [backendConfig],
options: { typeAware: true },
overrides: [
{
files: ['services/keycloak.ts'],
jsPlugins: ['@n8n/eslint-config/plugin'],
rules: { 'n8n-local-rules/no-uncentralized-http': 'off' },
},
],
});

View File

@ -27,8 +27,8 @@
"typecheck": "tsc --noEmit",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint . --quiet",
"lint:fix": "eslint . --fix"
"lint": "oxlint --quiet",
"lint:fix": "oxlint --fix"
},
"keywords": [],
"author": "",
@ -47,6 +47,10 @@
"undici": "catalog:undici-v7"
},
"devDependencies": {
"@n8n/eslint-config": "workspace:*",
"@n8n/oxlint-config": "workspace:*",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:typescript",
"vitest": "catalog:"

View File

@ -1,25 +1,6 @@
import { defineConfig } from 'eslint/config';
import { backendConfig } from '@n8n/eslint-config/backend';
export default defineConfig(
backendConfig,
{
ignores: ['coverage/**', 'bin/**'],
},
{
rules: {
'@typescript-eslint/naming-convention': [
'error',
// Allow kebab-case for rule IDs in config objects
{
selector: 'objectLiteralProperty',
format: null,
filter: {
regex: '^[a-z]+-[a-z-]+$', // kebab-case pattern
match: true,
},
},
],
},
},
);
export default defineConfig(backendConfig, {
ignores: ['coverage/**', 'bin/**'],
});

View File

@ -0,0 +1,8 @@
import { backendConfig } from '@n8n/oxlint-config/backend';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [backendConfig],
options: { typeAware: true },
ignorePatterns: ['coverage/**', 'bin/**'],
});

View File

@ -23,8 +23,8 @@
"test:coverage": "vitest run --coverage",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint . --quiet",
"lint:fix": "eslint . --fix",
"lint": "oxlint --quiet",
"lint:fix": "oxlint --fix",
"typecheck": "tsc --noEmit"
},
"keywords": [
@ -46,9 +46,13 @@
"peerDependencies": {
"ts-morph": "catalog:"
},
"devDependencies": {
"devDependencies": {
"@n8n/eslint-config": "workspace:*",
"@n8n/oxlint-config": "workspace:*",
"@types/node": "catalog:",
"@vitest/coverage-v8": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"typescript": "catalog:typescript",
"vitest": "catalog:"
}

View File

@ -1,6 +1,8 @@
import { backendConfig } from '@n8n/eslint-config/backend';
import playwrightPlugin from 'eslint-plugin-playwright';
import { legacyFilenameCaseFiles } from './lint-filename-debt.mjs';
export default [
...backendConfig,
playwrightPlugin.configs['flat/recommended'],
@ -18,50 +20,11 @@ export default [
},
{
rules: {
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/promise-function-async': 'off',
'no-empty-pattern': 'error',
'playwright/expect-expect': 'warn',
'playwright/max-nested-describe': 'warn',
'playwright/no-conditional-in-test': 'error',
'playwright/no-skipped-test': 'warn',
// Allow any naming convention for TestRequirements object properties
// This is specifically for workflow filenames and intercept keys that may not follow camelCase
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: ['camelCase'],
leadingUnderscore: 'allow',
trailingUnderscore: 'allow',
},
{
selector: 'variable',
format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'typeLike',
format: ['PascalCase'],
},
{
selector: 'property',
format: ['camelCase', 'snake_case', 'UPPER_CASE'],
filter: {
// Allow any format for properties in TestRequirements objects (workflow files, intercept keys, etc.)
regex: '^(workflow|intercepts|storage|config)$',
match: false,
},
},
{
selector: 'objectLiteralProperty',
format: null, // Allow any format for object literal properties in TestRequirements
filter: {
// This allows workflow filenames and intercept keys to use any naming convention
regex: '\\.(json|spec\\.ts)$|[a-zA-Z0-9_-]+',
match: true,
},
},
],
'import-x/no-extraneous-dependencies': [
'error',
{
@ -72,10 +35,42 @@ export default [
},
},
{
// Debt: the base layer enforces kebab-case filenames and this package has
// 124 files that predate it. Rename them, then delete this block.
rules: {
'unicorn/filename-case': 'off',
},
files: legacyFilenameCaseFiles,
rules: { 'unicorn/filename-case': 'off' },
},
{
files: [
'fixtures/langsmith.ts',
'fixtures/quarantine.ts',
'tests/cli-workflows/workflow-tests.spec.ts',
'tests/e2e/chat-hub/fixtures.ts',
'tests/e2e/instance-ai/fixtures.ts',
'tests/e2e/instance-ai/instance-ai-workflow-setup.spec.ts',
'tests/e2e/workflows/editor/execution/fixtures.ts',
'tests/framework/consumers.ts',
],
rules: { 'no-empty-pattern': 'off' },
},
{
files: [
'composables/BuilderWizardComposer.ts',
'helpers/ClipboardHelper.ts',
'pages/PublicFormPage.ts',
'pages/SettingsUsersPage.ts',
'pages/SourceControlPushModal.ts',
'reporters/ci-metrics.test.ts',
'services/tag-api-helper.ts',
'services/variables-api-helper.ts',
'tests/cli-workflows/setup-workflow-tests.ts',
'tests/e2e/projects/projects.spec.ts',
'tests/e2e/workflows/editor/expressions/quickjs-engine.spec.ts',
'tests/e2e/workflows/executions/list.spec.ts',
'tests/e2e/workflows/templates/templates.spec.ts',
'tests/evals/_smoke/langsmith-fixture.spec.ts',
'tests/evals/instance-ai/weather-alert.spec.ts',
'tests/infrastructure/benchmarks/ui/executions-list-customer-scale.spec.ts',
'utils/benchmark/kafka-driver.ts',
],
rules: { '@typescript-eslint/promise-function-async': 'off' },
},
];

View File

@ -0,0 +1,130 @@
export const legacyFilenameCaseFiles = [
'Types.ts',
'composables/BuilderWizardComposer.ts',
'composables/CanvasComposer.ts',
'composables/CredentialsComposer.ts',
'composables/DataTablesComposer.ts',
'composables/ExecutionsComposer.ts',
'composables/MfaComposer.ts',
'composables/NodeDetailsViewComposer.ts',
'composables/OidcComposer.ts',
'composables/PartialExecutionComposer.ts',
'composables/ProjectComposer.ts',
'composables/TemplatesComposer.ts',
'composables/TestEntryComposer.ts',
'composables/WorkflowComposer.ts',
'helpers/ClipboardHelper.ts',
'helpers/NavigationHelper.ts',
'helpers/NodeParameterHelper.ts',
'pages/AIAssistantPage.ts',
'pages/AgentBuilderPage.ts',
'pages/AgentSessionsPage.ts',
'pages/BasePage.ts',
'pages/CanvasPage.ts',
'pages/ChatHubChatPage.ts',
'pages/ChatHubPersonalAgentsPage.ts',
'pages/ChatHubSettingsPage.ts',
'pages/ChatHubWorkflowAgentsPage.ts',
'pages/CommunityNodesPage.ts',
'pages/CredentialsPage.ts',
'pages/DataTableDetails.ts',
'pages/DataTableView.ts',
'pages/DemoPage.ts',
'pages/EvaluationComparePage.ts',
'pages/ExecutionsPage.ts',
'pages/InstanceAiPage.ts',
'pages/KeycloakLoginPage.ts',
'pages/MfaLoginPage.ts',
'pages/MfaSetupModal.ts',
'pages/NodeDetailsViewPage.ts',
'pages/NpsSurveyPage.ts',
'pages/OAuthConsentPage.ts',
'pages/ProjectSettingsPage.ts',
'pages/PublicFormPage.ts',
'pages/SecretsProviderSettingsPage.ts',
'pages/SecuritySettingsPage.ts',
'pages/SettingsEnvironmentPage.ts',
'pages/SettingsLogStreamingPage.ts',
'pages/SettingsMcpPage.ts',
'pages/SettingsPersonalPage.ts',
'pages/SettingsSsoPage.ts',
'pages/SettingsUsersPage.ts',
'pages/SidebarPage.ts',
'pages/SignInPage.ts',
'pages/SourceControlPullModal.ts',
'pages/SourceControlPushModal.ts',
'pages/TemplateCredentialSetupPage.ts',
'pages/TemplatesPage.ts',
'pages/VariablesPage.ts',
'pages/VersionsPage.ts',
'pages/WorkerViewPage.ts',
'pages/WorkflowActivationModal.ts',
'pages/WorkflowCredentialSetupModal.ts',
'pages/WorkflowReviewsPage.ts',
'pages/WorkflowSettingsModal.ts',
'pages/WorkflowSharingModal.ts',
'pages/WorkflowsPage.ts',
'pages/n8nPage.ts',
'pages/components/AIBuilderPage.ts',
'pages/components/ActionToggle.ts',
'pages/components/AddResource.ts',
'pages/components/BaseModal.ts',
'pages/components/Breadcrumbs.ts',
'pages/components/BuilderSetupWizard.ts',
'pages/components/ChatAgentCard.ts',
'pages/components/ChatHubCredentialModal.ts',
'pages/components/ChatHubPersonalAgentModal.ts',
'pages/components/ChatHubProviderSettingsModal.ts',
'pages/components/ChatHubSidebar.ts',
'pages/components/ChatHubToolsModal.ts',
'pages/components/CodeNodeEditor.ts',
'pages/components/CommandBar.ts',
'pages/components/ConvertToSubworkflowModal.ts',
'pages/components/CredentialModal.ts',
'pages/components/DeleteSecretsProviderModal.ts',
'pages/components/FloatingUiHelper.ts',
'pages/components/FocusPanel.ts',
'pages/components/InlineExpressionEditor.ts',
'pages/components/InstanceAiSidebar.ts',
'pages/components/InstanceAiWorkflowSetup.ts',
'pages/components/InteractionsPage.ts',
'pages/components/LogsPanel.ts',
'pages/components/ManualChatModal.ts',
'pages/components/NodeCreator.ts',
'pages/components/NodeCredentials.ts',
'pages/components/NotificationsPage.ts',
'pages/components/ProjectHeader.ts',
'pages/components/ProjectTabsComponent.ts',
'pages/components/ResourceCards.ts',
'pages/components/ResourceLocator.ts',
'pages/components/ResourceMoveModal.ts',
'pages/components/RunDataPanel.ts',
'pages/components/SaveChangesModal.ts',
'pages/components/SecretsProviderConnectionModal.ts',
'pages/components/SettingsSidebar.ts',
'pages/components/SetupWorkflowCredentialsModal.ts',
'pages/components/StickyComponent.ts',
'pages/components/TagsManagerModal.ts',
'pages/components/VariableModal.ts',
'pages/components/WorkflowMenu.ts',
'pages/components/WorkflowReviewControls.ts',
'pages/components/dialogLocators.ts',
'pages/components/messageBoxLocators.ts',
'pages/components/nodes/EditFieldsNode.ts',
'tests/e2e/regression/ADO-1338-ndv-missing-input-panel.spec.ts',
'tests/e2e/regression/ADO-2230-ndv-reset-data-pagination.spec.ts',
'tests/e2e/regression/ADO-2372-prevent-clipping-params.spec.ts',
'tests/e2e/regression/ADO-2929-can-load-old-switch-node-workflows.spec.ts',
'tests/e2e/regression/ADO-4462-template-setup-experiment.spec.ts',
'tests/e2e/regression/ADO-5630-execute-button-layout.spec.ts',
'tests/e2e/regression/ADO-5808-stale-node-execution-data.spec.ts',
'tests/e2e/regression/AI-1401-sub-nodes-input-panel.spec.ts',
'tests/e2e/regression/AI-2795-chat-panel-header-height.spec.ts',
'tests/e2e/regression/AI-716-correctly-set-up-agent-model-shows-error.spec.ts',
'tests/e2e/regression/AI-812-partial-execs-broken-when-using-chat-trigger.spec.ts',
'tests/e2e/regression/CAT-2395-node-spacing.spec.ts',
'tests/e2e/regression/CAT-726-canvas-node-connectors-not-rendered-when-nodes-inserted.spec.ts',
'tests/e2e/regression/PAY-4367-node-shifting-cyclic.spec.ts',
'tests/e2e/regression/SUG-121-fields-reset-after-closing-ndv.spec.ts',
'tests/e2e/regression/SUG-38-inline-expression-preview.spec.ts',
];

View File

@ -0,0 +1,208 @@
import { backendConfig } from '@n8n/oxlint-config/backend';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [backendConfig],
options: { typeAware: true },
jsPlugins: ['eslint-plugin-playwright'],
ignorePatterns: [
'playwright-report/**/*',
'ms-playwright-cache/**/*',
'.playwright-browsers/**/*',
'coverage/**/*',
'scripts/**/*',
'janitor.config.mjs',
],
rules: {
'playwright/missing-playwright-await': 'error',
'playwright/no-conditional-in-test': 'error',
'playwright/no-focused-test': 'error',
'playwright/no-networkidle': 'error',
'playwright/no-standalone-expect': 'error',
'playwright/no-unsafe-references': 'error',
'playwright/no-wait-for-navigation': 'error',
'playwright/prefer-web-first-assertions': 'error',
'playwright/valid-describe-callback': 'error',
'playwright/valid-expect': 'error',
'playwright/valid-expect-in-promise': 'error',
'playwright/valid-test-tags': 'error',
'playwright/valid-title': 'error',
'import-x-alias/no-extraneous-dependencies': [
'error',
{
devDependencies: ['**/tests/**', '**/e2e/**', '**/playwright/**'],
optionalDependencies: false,
},
],
},
overrides: [
{
files: [
'Types.ts',
'composables/BuilderWizardComposer.ts',
'composables/CanvasComposer.ts',
'composables/CredentialsComposer.ts',
'composables/DataTablesComposer.ts',
'composables/ExecutionsComposer.ts',
'composables/MfaComposer.ts',
'composables/NodeDetailsViewComposer.ts',
'composables/OidcComposer.ts',
'composables/PartialExecutionComposer.ts',
'composables/ProjectComposer.ts',
'composables/TemplatesComposer.ts',
'composables/TestEntryComposer.ts',
'composables/WorkflowComposer.ts',
'helpers/ClipboardHelper.ts',
'helpers/NavigationHelper.ts',
'helpers/NodeParameterHelper.ts',
'pages/AIAssistantPage.ts',
'pages/AgentBuilderPage.ts',
'pages/AgentSessionsPage.ts',
'pages/BasePage.ts',
'pages/CanvasPage.ts',
'pages/ChatHubChatPage.ts',
'pages/ChatHubPersonalAgentsPage.ts',
'pages/ChatHubSettingsPage.ts',
'pages/ChatHubWorkflowAgentsPage.ts',
'pages/CommunityNodesPage.ts',
'pages/CredentialsPage.ts',
'pages/DataTableDetails.ts',
'pages/DataTableView.ts',
'pages/DemoPage.ts',
'pages/EvaluationComparePage.ts',
'pages/ExecutionsPage.ts',
'pages/InstanceAiPage.ts',
'pages/KeycloakLoginPage.ts',
'pages/MfaLoginPage.ts',
'pages/MfaSetupModal.ts',
'pages/NodeDetailsViewPage.ts',
'pages/NpsSurveyPage.ts',
'pages/OAuthConsentPage.ts',
'pages/ProjectSettingsPage.ts',
'pages/PublicFormPage.ts',
'pages/SecretsProviderSettingsPage.ts',
'pages/SecuritySettingsPage.ts',
'pages/SettingsEnvironmentPage.ts',
'pages/SettingsLogStreamingPage.ts',
'pages/SettingsMcpPage.ts',
'pages/SettingsPersonalPage.ts',
'pages/SettingsSsoPage.ts',
'pages/SettingsUsersPage.ts',
'pages/SidebarPage.ts',
'pages/SignInPage.ts',
'pages/SourceControlPullModal.ts',
'pages/SourceControlPushModal.ts',
'pages/TemplateCredentialSetupPage.ts',
'pages/TemplatesPage.ts',
'pages/VariablesPage.ts',
'pages/VersionsPage.ts',
'pages/WorkerViewPage.ts',
'pages/WorkflowActivationModal.ts',
'pages/WorkflowCredentialSetupModal.ts',
'pages/WorkflowReviewsPage.ts',
'pages/WorkflowSettingsModal.ts',
'pages/WorkflowSharingModal.ts',
'pages/WorkflowsPage.ts',
'pages/n8nPage.ts',
'pages/components/AIBuilderPage.ts',
'pages/components/ActionToggle.ts',
'pages/components/AddResource.ts',
'pages/components/BaseModal.ts',
'pages/components/Breadcrumbs.ts',
'pages/components/BuilderSetupWizard.ts',
'pages/components/ChatAgentCard.ts',
'pages/components/ChatHubCredentialModal.ts',
'pages/components/ChatHubPersonalAgentModal.ts',
'pages/components/ChatHubProviderSettingsModal.ts',
'pages/components/ChatHubSidebar.ts',
'pages/components/ChatHubToolsModal.ts',
'pages/components/CodeNodeEditor.ts',
'pages/components/CommandBar.ts',
'pages/components/ConvertToSubworkflowModal.ts',
'pages/components/CredentialModal.ts',
'pages/components/DeleteSecretsProviderModal.ts',
'pages/components/FloatingUiHelper.ts',
'pages/components/FocusPanel.ts',
'pages/components/InlineExpressionEditor.ts',
'pages/components/InstanceAiSidebar.ts',
'pages/components/InstanceAiWorkflowSetup.ts',
'pages/components/InteractionsPage.ts',
'pages/components/LogsPanel.ts',
'pages/components/ManualChatModal.ts',
'pages/components/NodeCreator.ts',
'pages/components/NodeCredentials.ts',
'pages/components/NotificationsPage.ts',
'pages/components/ProjectHeader.ts',
'pages/components/ProjectTabsComponent.ts',
'pages/components/ResourceCards.ts',
'pages/components/ResourceLocator.ts',
'pages/components/ResourceMoveModal.ts',
'pages/components/RunDataPanel.ts',
'pages/components/SaveChangesModal.ts',
'pages/components/SecretsProviderConnectionModal.ts',
'pages/components/SettingsSidebar.ts',
'pages/components/SetupWorkflowCredentialsModal.ts',
'pages/components/StickyComponent.ts',
'pages/components/TagsManagerModal.ts',
'pages/components/VariableModal.ts',
'pages/components/WorkflowMenu.ts',
'pages/components/WorkflowReviewControls.ts',
'pages/components/dialogLocators.ts',
'pages/components/messageBoxLocators.ts',
'pages/components/nodes/EditFieldsNode.ts',
'tests/e2e/regression/ADO-1338-ndv-missing-input-panel.spec.ts',
'tests/e2e/regression/ADO-2230-ndv-reset-data-pagination.spec.ts',
'tests/e2e/regression/ADO-2372-prevent-clipping-params.spec.ts',
'tests/e2e/regression/ADO-2929-can-load-old-switch-node-workflows.spec.ts',
'tests/e2e/regression/ADO-4462-template-setup-experiment.spec.ts',
'tests/e2e/regression/ADO-5630-execute-button-layout.spec.ts',
'tests/e2e/regression/ADO-5808-stale-node-execution-data.spec.ts',
'tests/e2e/regression/AI-1401-sub-nodes-input-panel.spec.ts',
'tests/e2e/regression/AI-2795-chat-panel-header-height.spec.ts',
'tests/e2e/regression/AI-716-correctly-set-up-agent-model-shows-error.spec.ts',
'tests/e2e/regression/AI-812-partial-execs-broken-when-using-chat-trigger.spec.ts',
'tests/e2e/regression/CAT-2395-node-spacing.spec.ts',
'tests/e2e/regression/CAT-726-canvas-node-connectors-not-rendered-when-nodes-inserted.spec.ts',
'tests/e2e/regression/PAY-4367-node-shifting-cyclic.spec.ts',
'tests/e2e/regression/SUG-121-fields-reset-after-closing-ndv.spec.ts',
'tests/e2e/regression/SUG-38-inline-expression-preview.spec.ts',
],
rules: { 'unicorn/filename-case': 'off' },
},
{
files: [
'fixtures/langsmith.ts',
'fixtures/quarantine.ts',
'tests/cli-workflows/workflow-tests.spec.ts',
'tests/e2e/chat-hub/fixtures.ts',
'tests/e2e/instance-ai/fixtures.ts',
'tests/e2e/instance-ai/instance-ai-workflow-setup.spec.ts',
'tests/e2e/workflows/editor/execution/fixtures.ts',
'tests/framework/consumers.ts',
],
rules: { 'no-empty-pattern': 'off' },
},
{
files: [
'composables/BuilderWizardComposer.ts',
'helpers/ClipboardHelper.ts',
'pages/PublicFormPage.ts',
'pages/SettingsUsersPage.ts',
'pages/SourceControlPushModal.ts',
'reporters/ci-metrics.test.ts',
'services/tag-api-helper.ts',
'services/variables-api-helper.ts',
'tests/cli-workflows/setup-workflow-tests.ts',
'tests/e2e/projects/projects.spec.ts',
'tests/e2e/workflows/editor/expressions/quickjs-engine.spec.ts',
'tests/e2e/workflows/executions/list.spec.ts',
'tests/e2e/workflows/templates/templates.spec.ts',
'tests/evals/_smoke/langsmith-fixture.spec.ts',
'tests/evals/instance-ai/weather-alert.spec.ts',
'tests/infrastructure/benchmarks/ui/executions-list-customer-scale.spec.ts',
'utils/benchmark/kafka-driver.ts',
],
rules: { 'typescript/promise-function-async': 'off' },
},
],
});

View File

@ -48,8 +48,9 @@
"prepare-test-image": "node scripts/build-test-image.mjs",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint . --quiet",
"lint:fix": "eslint . --fix",
"lint": "oxlint --quiet",
"lint:fix": "oxlint --fix",
"lint:verify-config": "node scripts/verify-lint-config.mjs",
"typecheck": "tsc --noEmit",
"janitor": "tsx ../janitor/src/cli.ts",
"janitor:fix": "tsx ../janitor/src/cli.ts --fix --write"
@ -59,6 +60,7 @@
"@axe-core/playwright": "^4.13.0",
"@currents/playwright": "catalog:e2e",
"@n8n/eslint-config": "workspace:*",
"@n8n/oxlint-config": "workspace:*",
"@types/debug": "^4.1.12",
"@memlab/api": "2.0.0",
"@memlab/heap-analysis": "2.0.0",
@ -93,6 +95,8 @@
"nanoid": "catalog:",
"monocart-coverage-reports": "^2.12.0",
"otplib": "^12.0.1",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"ts-morph": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:typescript",

View File

@ -0,0 +1,52 @@
#!/usr/bin/env node
import { createRequire } from 'node:module';
import { dirname, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const packageDirectory = join(dirname(fileURLToPath(import.meta.url)), '..');
const require = createRequire(join(packageDirectory, 'package.json'));
const playwrightPlugin = require('eslint-plugin-playwright');
const oxlintConfig = await import(pathToFileURL(join(packageDirectory, 'oxlint.config.mts')).href);
const { legacyFilenameCaseFiles } = await import(
pathToFileURL(join(packageDirectory, 'lint-filename-debt.mjs')).href
);
const eslintRules = {
...playwrightPlugin.configs['flat/recommended'].rules,
'playwright/no-conditional-in-test': 'error',
};
const errorRuleIds = (rules) =>
Object.entries(rules)
.filter(([id, severity]) => {
const value = Array.isArray(severity) ? severity[0] : severity;
return id.startsWith('playwright/') && (value === 'error' || value === 2);
})
.map(([id]) => id)
.sort();
const expected = errorRuleIds(eslintRules);
const actual = errorRuleIds(oxlintConfig.default.rules ?? {});
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
console.error('Playwright error-rule parity failed.');
console.error(`ESLint: ${expected.join(', ')}`);
console.error(`Oxlint: ${actual.join(', ')}`);
process.exit(1);
}
const filenameCaseOverride = oxlintConfig.default.overrides?.find(
({ rules }) => rules?.['unicorn/filename-case'] === 'off',
);
const oxlintFilenameCaseFiles = [...(filenameCaseOverride?.files ?? [])].sort();
const eslintFilenameCaseFiles = [...legacyFilenameCaseFiles].sort();
if (JSON.stringify(oxlintFilenameCaseFiles) !== JSON.stringify(eslintFilenameCaseFiles)) {
console.error('Playwright filename-case debt parity failed.');
process.exit(1);
}
console.log(`Playwright error-rule parity passed for ${actual.length} rules.`);
console.log(
`Playwright filename-case debt parity passed for ${oxlintFilenameCaseFiles.length} files.`,
);

View File

@ -1,24 +1,6 @@
import { defineConfig } from 'eslint/config';
import { backendConfig } from '@n8n/eslint-config/backend';
export default defineConfig(
backendConfig,
{
ignores: ['coverage/**', 'dist/**'],
},
{
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'objectLiteralProperty',
format: null,
filter: {
regex: '^[a-z]+-[a-z-]+$',
match: true,
},
},
],
},
},
);
export default defineConfig(backendConfig, {
ignores: ['coverage/**', 'dist/**'],
});

View File

@ -0,0 +1,8 @@
import { backendConfig } from '@n8n/oxlint-config/backend';
import { defineConfig } from 'oxlint';
export default defineConfig({
extends: [backendConfig],
options: { typeAware: true },
ignorePatterns: ['coverage/**'],
});

View File

@ -28,8 +28,8 @@
"test:watch": "vitest",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint . --quiet",
"lint:fix": "eslint . --fix",
"lint": "oxlint --quiet",
"lint:fix": "oxlint --fix",
"typecheck": "tsc --noEmit"
},
"license": "MIT",
@ -37,8 +37,12 @@
"ts-morph": "catalog:"
},
"devDependencies": {
"@n8n/eslint-config": "workspace:*",
"@n8n/oxlint-config": "workspace:*",
"@n8n/vitest-config": "workspace:*",
"@vitest/coverage-v8": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"typescript": "catalog:typescript",
"vitest": "catalog:"
}

View File

@ -7769,6 +7769,12 @@ importers:
specifier: 'catalog:'
version: 2.8.3
devDependencies:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../@n8n/eslint-config
'@n8n/oxlint-config':
specifier: workspace:*
version: link:../../@n8n/oxlint-config
'@types/markdown-it':
specifier: 'catalog:'
version: 13.0.9
@ -7778,6 +7784,12 @@ importers:
fast-check:
specifier: 'catalog:'
version: 3.23.2
oxlint:
specifier: 'catalog:'
version: 1.78.0(oxlint-tsgolint@0.21.1)
oxlint-tsgolint:
specifier: 'catalog:'
version: 0.21.1
vitest:
specifier: 'catalog:'
version: 5.0.1(@opentelemetry/api@1.9.1)(@types/node@20.19.41)(@vitest/browser-playwright@5.0.1)(@vitest/coverage-istanbul@5.0.1)(@vitest/coverage-v8@5.0.1)(@vitest/ui@5.0.1)(jsdom@23.0.1(bufferutil@4.0.9)(supports-color@5.5.0)(utf-8-validate@5.0.10))(vite@8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))
@ -7818,6 +7830,18 @@ importers:
specifier: catalog:undici-v7
version: 7.29.1
devDependencies:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../@n8n/eslint-config
'@n8n/oxlint-config':
specifier: workspace:*
version: link:../../@n8n/oxlint-config
oxlint:
specifier: 'catalog:'
version: 1.78.0(oxlint-tsgolint@0.21.1)
oxlint-tsgolint:
specifier: 'catalog:'
version: 0.21.1
tsx:
specifier: 'catalog:'
version: 4.19.3
@ -7849,12 +7873,24 @@ importers:
specifier: 'catalog:'
version: 2.8.3
devDependencies:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../@n8n/eslint-config
'@n8n/oxlint-config':
specifier: workspace:*
version: link:../../@n8n/oxlint-config
'@types/node':
specifier: ^20.17.50
version: 20.19.41
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 5.0.1(@vitest/browser@5.0.1)(vitest@5.0.1)
oxlint:
specifier: 'catalog:'
version: 1.78.0(oxlint-tsgolint@0.21.1)
oxlint-tsgolint:
specifier: 'catalog:'
version: 0.21.1
typescript:
specifier: catalog:typescript
version: 7.0.2
@ -7909,6 +7945,9 @@ importers:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../@n8n/eslint-config
'@n8n/oxlint-config':
specifier: workspace:*
version: link:../../@n8n/oxlint-config
'@n8n/permissions':
specifier: workspace:*
version: link:../../@n8n/permissions
@ -7996,6 +8035,12 @@ importers:
otplib:
specifier: ^12.0.1
version: 12.0.1
oxlint:
specifier: 'catalog:'
version: 1.78.0(oxlint-tsgolint@0.21.1)
oxlint-tsgolint:
specifier: 'catalog:'
version: 0.21.1
ts-morph:
specifier: 'catalog:'
version: 27.0.2
@ -8018,12 +8063,24 @@ importers:
specifier: 'catalog:'
version: 27.0.2
devDependencies:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../@n8n/eslint-config
'@n8n/oxlint-config':
specifier: workspace:*
version: link:../../@n8n/oxlint-config
'@n8n/vitest-config':
specifier: workspace:*
version: link:../../@n8n/vitest-config
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 5.0.1(@vitest/browser@5.0.1)(vitest@5.0.1)
oxlint:
specifier: 'catalog:'
version: 1.78.0(oxlint-tsgolint@0.21.1)
oxlint-tsgolint:
specifier: 'catalog:'
version: 0.21.1
typescript:
specifier: catalog:typescript
version: 7.0.2

View File

@ -41,6 +41,12 @@ Possible approaches:
Audit every package configuration before conversion. Do not copy the full generic naming rule into a new custom implementation.
### Testing infrastructure decisions
- `@n8n/rules-engine`, `@n8n/code-health`, and `@n8n/playwright-janitor`: retire the exemption-only rule-ID selectors. They enforce no positive naming contract.
- `n8n-containers`: retire the Docker label exemption. Object literal keys are data, not identifier contracts.
- `n8n-playwright`: retire the broad style selectors. Workflow names, fixture keys, and spec paths are data, while identifier casing has no runtime contract.
## Retirement exceptions
### `n8n-node-dev`

View File

@ -64,6 +64,7 @@ const JS_PLUGIN_NAMESPACES = new Set([
'lodash',
'unused-imports',
'n8n-local-rules',
'playwright',
]);
function splitId(id) {
@ -232,7 +233,12 @@ for (const id of eslintRules) {
}
const extra = [...oxlintRules].filter((id) => !expected.has(id)).sort();
const isDocumented = ({ id, native: target }) => id in gap || (target !== null && target in gap);
const hasApplicableGap = (id) => {
const entry = gap[id];
return entry !== undefined && (!entry.packages || entry.packages.includes(pkgDir));
};
const isDocumented = ({ id, native: target }) =>
hasApplicableGap(id) || (target !== null && hasApplicableGap(target));
const undocumented = missing.filter((entry) => !isDocumented(entry));
console.log(`${pkgDir}: ${sampled} sample files, ${eslintRules.size} ESLint rules at error`);

View File

@ -378,8 +378,9 @@
"nodes/Aws/__tests__/AwsLambda.node.test.ts"
],
"packages/testing/code-health": ["src/cli.ts", "src/cli/arg-parser.test.ts"],
"packages/testing/containers": ["services/keycloak.ts", "tests/resource-tracker.test.ts"],
"packages/testing/janitor": ["src/cli.test.ts", "src/cli.ts"],
"packages/testing/playwright": ["coverage-options.test.ts"],
"packages/testing/playwright": ["coverage-options.test.ts", "fixtures/base.ts"],
"packages/testing/rules-engine": ["src/ast/ast-rule.ts", "src/ast/imports.test.ts"],
"packages/testing/test-impact": ["src/changes.test.ts", "src/changes.ts"],
"packages/workflow": [

View File

@ -80,6 +80,9 @@
"@n8n/cli#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"@n8n/code-health#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"@n8n/client-oauth2#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
@ -119,6 +122,12 @@
"@n8n/n8n-benchmark#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"@n8n/playwright-janitor#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"@n8n/rules-engine#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"@n8n/scheduler#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
@ -131,6 +140,12 @@
"n8n-core#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"n8n-containers#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"n8n-playwright#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},
"n8n#lint": {
"dependsOn": ["^build", "@n8n/oxlint-config#build"]
},