diff --git a/lint/plugin.js b/lint/plugin.js index 4277f7365..a49da002c 100644 --- a/lint/plugin.js +++ b/lint/plugin.js @@ -8,6 +8,25 @@ function importSource(node) { return typeof node.source?.value === 'string' ? node.source.value : null } +function createProgramFilenameRule({ description, check }) { + return { + meta: { + docs: { description } + }, + create(context) { + const file = normalizedFilename(context) + const message = check(file) + if (!message) return {} + + return { + Program(node) { + context.report({ node, message }) + } + } + } + } +} + function createImportSourceRule({ description, applies = () => true, @@ -306,24 +325,6 @@ const noTypeofWindowCheck = { } } -const legacyAppImportPrefixes = [ - '@/ai/', - '@/automation/', - '@/stores/', - '@/composables/use-canvas', - '@/composables/use-canvas-input', - '@/composables/use-collab', - '@/composables/use-keyboard' -] - -const noLegacyAppImports = createImportSourceRule({ - description: - 'Disallow imports from removed app/store compatibility shims — import canonical src/app modules instead', - check: (source) => - legacyAppImportPrefixes.some((prefix) => source === prefix || source.startsWith(prefix)) && - `Import from the canonical src/app module instead of legacy shim '${source}'.` -}) - const noVueSelfPackageImports = createImportSourceRule({ description: 'Disallow @open-pencil/vue self-imports inside the Vue SDK — use #vue/* aliases', applies: (file) => file.includes('/packages/vue/src/'), @@ -342,36 +343,6 @@ const noCrossPackageSourceImports = createImportSourceRule({ `Use workspace package exports or package-local aliases instead of cross-package source import '${source}'.` }) -const noStaleViteAppPaths = { - meta: { - docs: { - description: 'Disallow stale Vite config paths to removed top-level app folders' - } - }, - create(context) { - const file = normalizedFilename(context) - if (!file.endsWith('/vite.config.ts')) return {} - - function reportIfStale(node, value) { - if (typeof value !== 'string') return - if (!value.includes('src/automation') && !value.includes('src/shims')) return - context.report({ - node, - message: 'Use current src/app/* paths instead of removed src/automation or src/shims paths.' - }) - } - - return { - Literal(node) { - reportIfStale(node, node.value) - }, - TemplateElement(node) { - reportIfStale(node, node.value?.raw) - } - } - } -} - function createParentRelativeImportRule({ description, applies, message, minDepth = 1 }) { return { meta: { @@ -437,27 +408,6 @@ const noCliParentRelativeImports = createParentRelativeImportRule({ message: 'Use the #cli/* package-local alias instead of parent-relative CLI imports.' }) -const noRootDemoModule = { - meta: { - docs: { - description: 'Disallow root src/demo.ts — keep demo builders under src/app/demo' - } - }, - create(context) { - const file = normalizedFilename(context) - if (!file.endsWith('/src/demo.ts')) return {} - - return { - Program(node) { - context.report({ - node, - message: 'Move demo document builders under src/app/demo instead of root src/demo.ts.' - }) - } - } - } -} - function createExactCoreBarrelImportRule({ description, applies, message }) { return createImportSourceRule({ description, @@ -517,13 +467,6 @@ const noInlinePromptConstants = { } } -const noLegacyAppSupportImports = createImportSourceRule({ - description: 'Disallow imports from legacy top-level app support folders', - check: (source) => - /^@\/(?:utils|engine)\//.test(source) && - 'Move app support imports to cohesive src/app modules instead of using legacy @/utils/* or @/engine/* paths.' -}) - const noAppVueCoreBarrelImports = createExactCoreBarrelImportRule({ description: 'Disallow app and Vue SDK imports from @open-pencil/core root barrel — use domain subpaths', @@ -582,59 +525,6 @@ const noDirectStorageAccess = { } } -const noLegacyShimFiles = { - meta: { - docs: { - description: 'Disallow recreating removed compatibility shim files' - } - }, - create(context) { - const file = normalizedFilename(context) - const blocked = [ - '/src/stores/editor.ts', - '/src/stores/tabs.ts', - '/src/composables/use-canvas.ts', - '/src/composables/use-canvas-input.ts', - '/src/composables/use-collab.ts', - '/src/composables/use-keyboard.ts' - ] - const blockedDirs = ['/src/ai/', '/src/automation/'] - const isBlocked = - blocked.some((suffix) => file.endsWith(suffix)) || - blockedDirs.some((segment) => file.includes(segment)) - - if (!isBlocked) return {} - - return { - Program(node) { - context.report({ - node, - message: 'Do not recreate removed compatibility shims; use the canonical src/app module.' - }) - } - } - } -} - -const noLegacyTestAppImports = createImportSourceRule({ - description: 'Disallow tests importing removed top-level app modules', - applies: (file) => file.includes('/tests/'), - includeExports: true, - check: (source) => - /^(?:\.\.\/)+src\/(?:ai|automation|composables|stores|utils|engine)\//.test(source) && - 'Use the current src/app/* module path instead of a removed top-level app module.' -}) - -const noTestCoreSourceImports = createImportSourceRule({ - description: 'Disallow tests importing non-vendored core internals by relative source path', - applies: (file) => file.includes('/tests/'), - includeExports: true, - includeDynamic: true, - check: (source) => - /^(?:\.\.\/)+packages\/core\/src\/(?!kiwi\/kiwi-schema)/.test(source) && - 'Use #core/* instead of a relative packages/core/src import in tests.' -}) - const noBroadDoubleCast = { meta: { docs: { @@ -1206,54 +1096,28 @@ const noFunctionAliasImports = { } } -const noTopLevelPrefixedTestFiles = { - meta: { - docs: { - description: 'Disallow top-level test files that encode domains as filename prefixes' - } - }, - create(context) { - const file = normalizedFilename(context) +const noTopLevelPrefixedTestFiles = createProgramFilenameRule({ + description: 'Disallow top-level test files that encode domains as filename prefixes', + check(file) { const match = file.match(/\/tests\/(engine|e2e)\/([^/]+-[^/]+\.(?:test|spec)\.ts)$/) - if (!match) return {} - - return { - Program(node) { - context.report({ - node, - message: `Move '${match[2]}' under a domain folder instead of encoding the domain as a filename prefix.` - }) - } - } + if (!match) return false + return `Move '${match[2]}' under a domain folder instead of encoding the domain as a filename prefix.` } -} +}) -const noFlatKiwiModules = { - meta: { - docs: { - description: 'Disallow flat top-level Kiwi modules — group code under Kiwi subdomains' - } - }, - create(context) { - const file = normalizedFilename(context) +const noFlatKiwiModules = createProgramFilenameRule({ + description: 'Disallow flat top-level Kiwi modules — group code under Kiwi subdomains', + check(file) { const marker = '/packages/core/src/kiwi/' const start = file.indexOf(marker) - if (start === -1) return {} + if (start === -1) return false const relativePath = file.slice(start + marker.length) - if (relativePath.includes('/') || relativePath === 'index.ts') return {} + if (relativePath.includes('/') || relativePath === 'index.ts') return false - return { - Program(node) { - context.report({ - node, - message: - 'Move Kiwi modules under binary/, fig/, node-change/, instance-overrides/, or kiwi-schema/ instead of adding flat top-level files.' - }) - } - } + return 'Move Kiwi modules under binary/, fig/, node-change/, instance-overrides/, or kiwi-schema/ instead of adding flat top-level files.' } -} +}) const plugin = { meta: { name: 'open-pencil' }, @@ -1265,29 +1129,22 @@ const plugin = { 'no-raw-console-format': noRawConsoleFormat, 'no-silent-catch': noSilentCatch, 'no-typeof-window-check': noTypeofWindowCheck, - 'no-legacy-app-imports': noLegacyAppImports, 'no-vue-self-package-imports': noVueSelfPackageImports, 'no-cross-package-source-imports': noCrossPackageSourceImports, 'no-deep-parent-relative-imports': noDeepParentRelativeImports, 'no-core-parent-relative-imports': noCoreParentRelativeImports, 'no-mcp-parent-relative-imports': noMcpParentRelativeImports, 'no-vue-parent-relative-imports': noVueParentRelativeImports, - 'no-stale-vite-app-paths': noStaleViteAppPaths, 'no-cli-parent-relative-imports': noCliParentRelativeImports, - 'no-root-demo-module': noRootDemoModule, 'no-mcp-core-barrel-imports': noMcpCoreBarrelImports, 'no-cli-core-barrel-imports': noCliCoreBarrelImports, 'no-script-core-barrel-imports': noScriptCoreBarrelImports, 'no-core-self-package-imports': noCoreSelfPackageImports, 'no-inline-prompt-constants': noInlinePromptConstants, - 'no-legacy-app-support-imports': noLegacyAppSupportImports, 'no-app-vue-core-barrel-imports': noAppVueCoreBarrelImports, 'no-app-imports-in-packages': noAppImportsInPackages, 'no-core-framework-imports': noCoreFrameworkImports, 'no-direct-storage-access': noDirectStorageAccess, - 'no-legacy-shim-files': noLegacyShimFiles, - 'no-legacy-test-app-imports': noLegacyTestAppImports, - 'no-test-core-source-imports': noTestCoreSourceImports, 'no-broad-double-cast': noBroadDoubleCast, 'no-unknown-record-double-cast': noUnknownRecordDoubleCast, 'no-ts-suppression-comments': noTsSuppressionComments, diff --git a/oxlint.json b/oxlint.json index be5b9ef85..b51f9fb9f 100644 --- a/oxlint.json +++ b/oxlint.json @@ -113,13 +113,11 @@ "open-pencil/no-raw-console-format": "off", "open-pencil/no-silent-catch": "error", "open-pencil/no-typeof-window-check": "error", - "open-pencil/no-legacy-app-imports": "error", "open-pencil/no-vue-self-package-imports": "error", "open-pencil/no-cross-package-source-imports": "error", "open-pencil/no-app-imports-in-packages": "error", "open-pencil/no-core-framework-imports": "error", "open-pencil/no-direct-storage-access": "error", - "open-pencil/no-legacy-shim-files": "error", "open-pencil/no-deep-parent-relative-imports": "error", "open-pencil/no-broad-double-cast": "error", "open-pencil/no-unknown-record-double-cast": "error", @@ -137,18 +135,13 @@ "open-pencil/no-core-parent-relative-imports": "error", "open-pencil/no-mcp-parent-relative-imports": "error", "open-pencil/no-vue-parent-relative-imports": "error", - "open-pencil/no-stale-vite-app-paths": "error", "open-pencil/no-cli-parent-relative-imports": "error", - "open-pencil/no-root-demo-module": "error", "open-pencil/no-mcp-core-barrel-imports": "error", "open-pencil/no-cli-core-barrel-imports": "error", "open-pencil/no-script-core-barrel-imports": "error", "open-pencil/no-core-self-package-imports": "error", "open-pencil/no-inline-prompt-constants": "error", - "open-pencil/no-legacy-app-support-imports": "error", "open-pencil/no-app-vue-core-barrel-imports": "error", - "open-pencil/no-legacy-test-app-imports": "error", - "open-pencil/no-test-core-source-imports": "error", "open-pencil/no-direct-graph-emitter-subscriptions": "error", "open-pencil/no-on-unmounted-in-composition-roots": "error", "open-pencil/no-composable-state-wrappers": "error",