cvx-kit 0.1.8 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +28 -28
- package/dist/agent-tools.d.mts +5 -5
- package/dist/agent-tools.mjs.map +1 -1
- package/dist/auth.d.mts +39 -289
- package/dist/auth.mjs.map +1 -1
- package/dist/components/approvals/_generated/api.d.mts +3 -4
- package/dist/components/approvals/_generated/api.mjs.map +1 -1
- package/dist/components/approvals/_generated/component.d.mts +1 -2
- package/dist/components/approvals/_generated/dataModel.d.mts +4 -5
- package/dist/components/approvals/_generated/server.d.mts +12 -13
- package/dist/components/approvals/_generated/server.mjs.map +1 -1
- package/dist/components/approvals/audit.d.mts +8 -8
- package/dist/components/approvals/audit.mjs +1 -1
- package/dist/components/approvals/audit.mjs.map +1 -1
- package/dist/components/approvals/client.d.mts +10 -10
- package/dist/components/approvals/client.mjs.map +1 -1
- package/dist/components/approvals/constants.d.mts +30 -30
- package/dist/components/approvals/constants.mjs.map +1 -1
- package/dist/components/approvals/convex.config.mjs.map +1 -1
- package/dist/components/approvals/decisions.d.mts +6 -6
- package/dist/components/approvals/decisions.mjs.map +1 -1
- package/dist/components/approvals/functions.d.mts +3 -3
- package/dist/components/approvals/functions.mjs.map +1 -1
- package/dist/components/approvals/health.d.mts +2 -2
- package/dist/components/approvals/health.mjs.map +1 -1
- package/dist/components/approvals/requests.d.mts +8 -8
- package/dist/components/approvals/requests.mjs.map +1 -1
- package/dist/components/approvals/validators.d.mts +29 -29
- package/dist/components/approvals/validators.mjs +1 -1
- package/dist/components/approvals/validators.mjs.map +1 -1
- package/dist/components/approvals/workflow.d.mts +3 -3
- package/dist/components/approvals/workflow.mjs.map +1 -1
- package/dist/components/approvals/workflow_steps.d.mts +15 -15
- package/dist/components/approvals/workflow_steps.mjs.map +1 -1
- package/dist/components/foundation/_generated/api.d.mts +3 -4
- package/dist/components/foundation/_generated/api.mjs.map +1 -1
- package/dist/components/foundation/_generated/component.d.mts +1 -2
- package/dist/components/foundation/_generated/dataModel.d.mts +4 -5
- package/dist/components/foundation/_generated/server.d.mts +12 -13
- package/dist/components/foundation/_generated/server.mjs.map +1 -1
- package/dist/components/foundation/client.d.mts +13 -13
- package/dist/components/foundation/client.mjs.map +1 -1
- package/dist/components/foundation/convex.config.mjs.map +1 -1
- package/dist/components/foundation/functions.d.mts +2 -2
- package/dist/components/foundation/functions.mjs.map +1 -1
- package/dist/components/foundation/modules/command/command.d.mts +14 -14
- package/dist/components/foundation/modules/command/command.mjs.map +1 -1
- package/dist/components/foundation/modules/command/lifecycle.d.mts +12 -13
- package/dist/components/foundation/modules/command/lifecycle.mjs.map +1 -1
- package/dist/components/foundation/modules/observability/observability.d.mts +4 -4
- package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
- package/dist/components/foundation/modules/query/query.d.mts +5 -5
- package/dist/components/foundation/modules/query/query.mjs.map +1 -1
- package/dist/components/foundation/operation.d.mts +5 -6
- package/dist/components/foundation/operation.mjs.map +1 -1
- package/dist/components/foundation/result.d.mts +6 -6
- package/dist/components/foundation/result.mjs.map +1 -1
- package/dist/components/foundation/telemetry.d.mts +2 -2
- package/dist/components/foundation/telemetry.mjs.map +1 -1
- package/dist/crud.d.mts +6 -5
- package/dist/crud.mjs.map +1 -1
- package/dist/effect.d.mts +3 -2
- package/dist/effect.mjs +1 -1
- package/dist/errors.d.mts +4 -4
- package/dist/errors.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/middleware.d.mts +2 -3
- package/dist/middleware.mjs.map +1 -1
- package/dist/modules/contracts/contract.d.mts +12 -13
- package/dist/modules/contracts/contract.mjs.map +1 -1
- package/dist/modules/contracts/convex.d.mts +3 -5
- package/dist/modules/contracts/convex.mjs.map +1 -1
- package/dist/modules/contracts/errors.d.mts +8 -9
- package/dist/modules/contracts/errors.mjs +1 -1
- package/dist/modules/contracts/errors.mjs.map +1 -1
- package/dist/modules/contracts/exposure.d.mts +13 -14
- package/dist/modules/contracts/exposure.mjs +1 -1
- package/dist/modules/contracts/exposure.mjs.map +1 -1
- package/dist/modules/contracts/idempotency.d.mts +15 -16
- package/dist/modules/contracts/idempotency.mjs +1 -1
- package/dist/modules/contracts/idempotency.mjs.map +1 -1
- package/dist/modules/contracts/standard.d.mts +2 -3
- package/dist/modules/contracts/standard.mjs.map +1 -1
- package/dist/modules/contracts/workflow.d.mts +26 -27
- package/dist/modules/contracts/workflow.mjs.map +1 -1
- package/dist/modules/effect/api-runtime.d.mts +10 -11
- package/dist/modules/effect/api-runtime.mjs +1 -1
- package/dist/modules/effect/api-runtime.mjs.map +1 -1
- package/dist/modules/effect/api-zod.d.mts +2 -3
- package/dist/modules/effect/api-zod.mjs.map +1 -1
- package/dist/modules/effect/api.d.mts +4 -5
- package/dist/modules/effect/api.mjs.map +1 -1
- package/dist/modules/effect/auth.d.mts +43 -0
- package/dist/modules/effect/auth.mjs +2 -0
- package/dist/modules/effect/auth.mjs.map +1 -0
- package/dist/modules/effect/client.d.mts +1 -1
- package/dist/modules/effect/client.mjs +1 -1
- package/dist/modules/effect/command.d.mts +4 -5
- package/dist/modules/effect/command.mjs +1 -1
- package/dist/modules/effect/command.mjs.map +1 -1
- package/dist/modules/effect/crud.d.mts +7 -8
- package/dist/modules/effect/crud.mjs +1 -1
- package/dist/modules/effect/crud.mjs.map +1 -1
- package/dist/modules/effect/errors.d.mts +1 -2
- package/dist/modules/effect/errors.mjs.map +1 -1
- package/dist/modules/effect/foundation.d.mts +3 -4
- package/dist/modules/effect/foundation.mjs.map +1 -1
- package/dist/modules/effect/idempotency.d.mts +2 -3
- package/dist/modules/effect/idempotency.mjs.map +1 -1
- package/dist/modules/effect/normalize.d.mts +2 -3
- package/dist/modules/effect/normalize.mjs.map +1 -1
- package/dist/modules/effect/operation.d.mts +17 -18
- package/dist/modules/effect/operation.mjs.map +1 -1
- package/dist/modules/effect/query.d.mts +3 -4
- package/dist/modules/effect/query.mjs +1 -1
- package/dist/modules/effect/query.mjs.map +1 -1
- package/dist/modules/effect/schema.d.mts +8 -9
- package/dist/modules/effect/schema.mjs.map +1 -1
- package/dist/modules/effect/workflow.d.mts +5 -6
- package/dist/modules/effect/workflow.mjs +1 -1
- package/dist/modules/effect/workflow.mjs.map +1 -1
- package/dist/oxlint/architecture.d.mts +6 -7
- package/dist/oxlint/architecture.mjs +1 -1
- package/dist/oxlint/architecture.mjs.map +1 -1
- package/dist/oxlint/ast.d.mts +22 -23
- package/dist/oxlint/ast.mjs +1 -1
- package/dist/oxlint/ast.mjs.map +1 -1
- package/dist/oxlint/ownership.d.mts +2 -3
- package/dist/oxlint/ownership.mjs +1 -1
- package/dist/oxlint/ownership.mjs.map +1 -1
- package/dist/oxlint.d.mts +4 -4
- package/dist/oxlint.mjs +1 -1
- package/dist/oxlint.mjs.map +1 -1
- package/dist/state-machine.d.mts +1 -2
- package/dist/state-machine.mjs.map +1 -1
- package/dist/tenancy.d.mts +6 -6
- package/dist/tenancy.mjs.map +1 -1
- package/dist/triggers.d.mts +6 -6
- package/dist/triggers.mjs.map +1 -1
- package/dist/webhooks.d.mts +5 -6
- package/dist/webhooks.mjs.map +1 -1
- package/dist/zod-table.d.mts +10 -10
- package/dist/zod-table.mjs.map +1 -1
- package/docs/auth.md +131 -1
- package/docs/effect.md +8 -2
- package/docs/llms.md +41 -20
- package/docs/oxlint.md +1 -1
- package/docs/upgrading.md +9 -1
- package/docs/zod-table.md +1 -1
- package/package.json +21 -15
- package/scripts/test-packed-auth.mjs +72 -0
- package/scripts/test-packed-effect.mjs +2 -1
- package/scripts/test-packed-ids.mjs +2 -2
- package/dist/node_modules/@standard-schema/spec/dist/index.d.mts +0 -79
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architecture.mjs","names":[],"sources":["../../src/oxlint/architecture.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs'\nimport { dirname, relative, resolve, sep } from 'node:path'\nimport { parseSync, Visitor, type Program, type Node, type CallExpression } from 'oxc-parser'\nimport { ResolverFactory } from 'oxc-resolver'\n\nexport interface ArchitectureOptions {\n\tcwd?: string\n\tconvexDir?: string\n}\nexport interface ArchitectureDiagnostic {\n\tcode:\n\t\t| 'missing-root'\n\t\t| 'root-directory'\n\t\t| 'tests'\n\t\t| 'test-location'\n\t\t| 'parse'\n\t\t| 'cycle'\n\t\t| 'symlink'\n\t\t| 'domain-entry'\n\t\t| 'shared-owner'\n\tfile: string\n\tmessage: string\n}\n\n/** Full-project checks, independent of the consumer's test runner. No persistent cache. */\nexport function inspectArchitecture(options: ArchitectureOptions = {}): ArchitectureDiagnostic[] {\n\tconst requestedRoot = resolve(options.cwd ?? process.cwd(), options.convexDir ?? 'convex')\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst diagnostics: ArchitectureDiagnostic[] = []\n\tif (!existsSync(root))\n\t\treturn [\n\t\t\t{ code: 'missing-root', file: '.', message: `Convex directory does not exist: ${root}` },\n\t\t]\n\tconst files = sourceFiles(root, diagnostics)\n\tconst required = new Set<string>()\n\tconst tested = new Set<string>()\n\tconst graph = new Map<string, Set<string>>()\n\tconst dependencies = new Map<string, Set<string>>()\n\tconst resolver = createResolver()\n\tfor (const file of files) {\n\t\tconst filename = resolve(root, file)\n\t\tconst parsed = parseSync(filename, readFileSync(filename, 'utf8'))\n\t\tif (parsed.errors.length) {\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'parse',\n\t\t\t\tfile,\n\t\t\t\tmessage: `Cannot validate ${file}: ${parsed.errors[0].message}`,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\t\tif (testFile(file)) {\n\t\t\tif (!file.includes('__tests__/'))\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tcode: 'test-location',\n\t\t\t\t\tfile,\n\t\t\t\t\tmessage: 'Place tests in a sibling __tests__/ directory.',\n\t\t\t\t})\n\t\t\telse if (dirname(file).endsWith('__tests__') && hasRunnableTest(parsed.program))\n\t\t\t\ttested.add(dirname(dirname(file)))\n\t\t\tcontinue\n\t\t}\n\t\tif (file.split('/').includes('__tests__')) continue\n\t\tif (/^domain\\/[^/]+$/.test(file) && file !== 'domain/table.ts')\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'domain-entry',\n\t\t\t\tfile,\n\t\t\t\tmessage:\n\t\t\t\t\t'Domain source belongs in domain/<module>/ or domain/shared/. Only domain/table.ts assembles module tables.',\n\t\t\t})\n\t\tif (executable(parsed.program) && file !== 'domain/table.ts') required.add(dirname(file))\n\t\tconst owner = moduleOwner(file) ?? ''\n\t\tconst targets = new Set<string>()\n\t\tdependencies.set(file, targets)\n\t\tfor (const source of runtimeImports(parsed.program)) {\n\t\t\tconst target = resolveImport(resolver, filename, source)\n\t\t\tif (!target) continue\n\t\t\tconst targetFile = relative(root, target).split(sep).join('/')\n\t\t\ttargets.add(targetFile)\n\t\t\tconst targetOwner = moduleOwner(targetFile)\n\t\t\tif (owner && targetOwner && owner !== targetOwner) {\n\t\t\t\tconst edges = graph.get(owner) ?? new Set<string>()\n\t\t\t\tgraph.set(owner, edges)\n\t\t\t\tedges.add(targetOwner)\n\t\t\t}\n\t\t}\n\t}\n\tfor (const directory of required) {\n\t\tif (!tested.has(directory))\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'tests',\n\t\t\t\tfile: directory,\n\t\t\t\tmessage: `${directory}/__tests__/ must contain a .test or .spec file with a nonempty, non-skipped test. Parent or child suites do not count.`,\n\t\t\t})\n\t}\n\tdiagnostics.push(...sharedOwnership(dependencies))\n\tfor (const cycle of cycles(graph))\n\t\tdiagnostics.push({\n\t\t\tcode: 'cycle',\n\t\t\tfile: cycle[0],\n\t\t\tmessage: `Module dependency cycle: ${cycle.join(' → ')}.`,\n\t\t})\n\treturn diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code))\n}\n\nexport function createResolver() {\n\treturn new ResolverFactory({\n\t\ttsconfig: 'auto',\n\t\textensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json'],\n\t\textensionAlias: {\n\t\t\t'.js': ['.ts', '.tsx', '.js'],\n\t\t\t'.mjs': ['.mts', '.mjs'],\n\t\t\t'.cjs': ['.cts', '.cjs'],\n\t\t},\n\t\tconditionNames: ['types', 'import', 'node', 'default'],\n\t\tsymlinks: true,\n\t})\n}\n\nexport function resolveImport(resolver: ResolverFactory, filename: string, source: string) {\n\tconst found = resolver.resolveFileSync(filename, source).path\n\tif (found) return found\n\t// Keep path checks useful in unsaved/editor fixtures and before Convex codegen.\n\treturn source.startsWith('.') ? resolve(dirname(filename), source) : null\n}\n\n/** The lint rule runs the project scan on one deterministic source file. */\nexport function architectureAnchor(root: string) {\n\tif (!existsSync(root)) return null\n\tif (existsSync(resolve(root, 'convex.config.ts')))\n\t\treturn realpathSync(resolve(root, 'convex.config.ts'))\n\troot = realpathSync(root)\n\tfor (const file of sourceFiles(root, []))\n\t\tif (!file.includes('__tests__/') && !testFile(file)) return resolve(root, file)\n\treturn null\n}\n\nfunction* sourceFiles(root: string, diagnostics: ArchitectureDiagnostic[]): Generator<string> {\n\tconst allowed = new Set([\n\t\t'_generated',\n\t\t'__tests__',\n\t\t'api',\n\t\t'domain',\n\t\t'application',\n\t\t'migrations',\n\t\t'components',\n\t])\n\tfunction* scan(directory: string): Generator<string> {\n\t\tfor (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t)) {\n\t\t\tconst path = resolve(directory, entry.name)\n\t\t\tconst file = relative(root, path).split(sep).join('/')\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tcode: 'symlink',\n\t\t\t\t\tfile,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t'Keep application source inside its owning directory; symlinked files and folders are not allowed.',\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (directory === root && !allowed.has(entry.name))\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\tcode: 'root-directory',\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tmessage: `Root folder ${entry.name}/ is not allowed. Use api/, domain/, application/, migrations/, components/, __tests__/, or _generated/.`,\n\t\t\t\t\t})\n\t\t\t\tif (\n\t\t\t\t\tentry.name !== '_generated' &&\n\t\t\t\t\tentry.name !== 'node_modules' &&\n\t\t\t\t\t!entry.name.startsWith('.')\n\t\t\t\t)\n\t\t\t\t\tyield* scan(path)\n\t\t\t} else if (/\\.[cm]?[jt]sx?$/.test(entry.name) && !/\\.d\\.[cm]?ts$/.test(entry.name)) yield file\n\t\t}\n\t}\n\tyield* scan(root)\n}\n\nfunction testFile(file: string) {\n\treturn /\\.(?:test|spec)\\.[cm]?[jt]sx?$/.test(file)\n}\n\nfunction executable(program: Program) {\n\treturn program.body.some((statement) => {\n\t\tconst declaration =\n\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\tif (declaration && 'declare' in declaration && declaration.declare) return false\n\t\tif (statement.type === 'ImportDeclaration' || statement.type === 'EmptyStatement') return false\n\t\tif (statement.type === 'ExportNamedDeclaration') {\n\t\t\tif (statement.exportKind === 'type' || !statement.declaration) return false\n\t\t\treturn !['TSInterfaceDeclaration', 'TSTypeAliasDeclaration', 'TSDeclareFunction'].includes(\n\t\t\t\tstatement.declaration.type,\n\t\t\t)\n\t\t}\n\t\treturn ![\n\t\t\t'TSInterfaceDeclaration',\n\t\t\t'TSTypeAliasDeclaration',\n\t\t\t'TSDeclareFunction',\n\t\t\t'ExportAllDeclaration',\n\t\t].includes(statement.type)\n\t})\n}\n\nfunction hasRunnableTest(program: Program) {\n\tconst names = new Map([\n\t\t['test', 'test'],\n\t\t['it', 'it'],\n\t\t['describe', 'describe'],\n\t\t['suite', 'suite'],\n\t])\n\tfor (const statement of program.body) {\n\t\tconst declaration =\n\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\tif (declaration?.type === 'FunctionDeclaration' && declaration.id)\n\t\t\tnames.delete(declaration.id.name)\n\t\tif (declaration?.type === 'VariableDeclaration')\n\t\t\tfor (const item of declaration.declarations) {\n\t\t\t\tif (item.id.type === 'Identifier') names.delete(item.id.name)\n\t\t\t}\n\t\tif (statement.type !== 'ImportDeclaration') continue\n\t\tfor (const specifier of statement.specifiers) {\n\t\t\tnames.delete(specifier.local.name)\n\t\t\tif (\n\t\t\t\t!['vitest', 'vite-plus/test', 'bun:test', 'node:test', '@jest/globals'].includes(\n\t\t\t\t\tstatement.source.value,\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tcontinue\n\t\t\tif (specifier.type === 'ImportNamespaceSpecifier') names.set(specifier.local.name, '*')\n\t\t\telse if (specifier.type === 'ImportDefaultSpecifier') names.set(specifier.local.name, 'test')\n\t\t\telse if (specifier.imported.type === 'Identifier')\n\t\t\t\tnames.set(specifier.local.name, specifier.imported.name)\n\t\t}\n\t}\n\tconst calls: CallExpression[] = []\n\tnew Visitor({\n\t\tCallExpression(node) {\n\t\t\tcalls.push(node)\n\t\t},\n\t}).visit(program)\n\tconst skipped = calls.filter((node) => {\n\t\tconst parts = testCallee(node.callee, names)\n\t\treturn (\n\t\t\t['test', 'it', 'describe', 'suite'].includes(parts[0]) &&\n\t\t\t(parts.some((part) => ['skip', 'todo', 'skipIf', 'runIf'].includes(part)) ||\n\t\t\t\tnode.arguments.some(\n\t\t\t\t\t(argument) =>\n\t\t\t\t\t\targument.type === 'ObjectExpression' &&\n\t\t\t\t\t\targument.properties.some(\n\t\t\t\t\t\t\t(property) =>\n\t\t\t\t\t\t\t\tproperty.type === 'Property' &&\n\t\t\t\t\t\t\t\t['skip', 'todo'].includes(\n\t\t\t\t\t\t\t\t\tproperty.key.type === 'Identifier'\n\t\t\t\t\t\t\t\t\t\t? property.key.name\n\t\t\t\t\t\t\t\t\t\t: property.key.type === 'Literal'\n\t\t\t\t\t\t\t\t\t\t\t? String(property.key.value)\n\t\t\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t\t) &&\n\t\t\t\t\t\t\t\t!(property.value.type === 'Literal' && property.value.value === false),\n\t\t\t\t\t\t),\n\t\t\t\t))\n\t\t)\n\t})\n\treturn calls.some((node) => {\n\t\tconst parts = testCallee(node.callee, names)\n\t\tif (\n\t\t\t!['test', 'it'].includes(parts[0]) ||\n\t\t\tskipped.some((skip) => node.start >= skip.start && node.end <= skip.end)\n\t\t)\n\t\t\treturn false\n\t\t// each(...) is a factory; only the outer call has the test callback.\n\t\treturn node.arguments.some(\n\t\t\t(argument) =>\n\t\t\t\t(argument.type === 'ArrowFunctionExpression' || argument.type === 'FunctionExpression') &&\n\t\t\t\targument.body &&\n\t\t\t\t(argument.body.type !== 'BlockStatement' ||\n\t\t\t\t\targument.body.body.some((statement) => statement.type !== 'EmptyStatement')),\n\t\t)\n\t})\n}\n\nfunction testCallee(node: Node, names: Map<string, string>): string[] {\n\tif (node.type === 'Identifier') {\n\t\tconst name = names.get(node.name)\n\t\treturn name ? [name] : []\n\t}\n\tif (node.type === 'CallExpression') return testCallee(node.callee, names)\n\tif (node.type === 'MemberExpression') {\n\t\tconst base = testCallee(node.object, names)\n\t\tconst property =\n\t\t\t!node.computed && node.property.type === 'Identifier'\n\t\t\t\t? node.property.name\n\t\t\t\t: node.property.type === 'Literal'\n\t\t\t\t\t? String(node.property.value)\n\t\t\t\t\t: ''\n\t\tif (!base.length || !property) return []\n\t\treturn base[0] === '*' ? [property] : [...base, property]\n\t}\n\treturn []\n}\n\nfunction runtimeImports(program: Program) {\n\tconst imports: string[] = []\n\tnew Visitor({\n\t\tImportDeclaration(node) {\n\t\t\tif (\n\t\t\t\tnode.importKind !== 'type' &&\n\t\t\t\t(node.specifiers.length === 0 ||\n\t\t\t\t\tnode.specifiers.some((s) => s.type !== 'ImportSpecifier' || s.importKind !== 'type'))\n\t\t\t)\n\t\t\t\timports.push(node.source.value)\n\t\t},\n\t\tExportNamedDeclaration(node) {\n\t\t\tif (\n\t\t\t\tnode.source &&\n\t\t\t\tnode.exportKind !== 'type' &&\n\t\t\t\tnode.specifiers.some((specifier) => specifier.exportKind !== 'type')\n\t\t\t)\n\t\t\t\timports.push(node.source.value)\n\t\t},\n\t\tExportAllDeclaration(node) {\n\t\t\tif (node.exportKind !== 'type') imports.push(node.source.value)\n\t\t},\n\t\tImportExpression(node) {\n\t\t\tif (node.source.type === 'Literal' && String(node.source.value) === node.source.value)\n\t\t\t\timports.push(String(node.source.value))\n\t\t},\n\t}).visit(program)\n\treturn imports\n}\n\nfunction moduleOwner(file: string) {\n\tconst parts = file.split('/')\n\treturn parts[0] === 'domain' && parts.length >= 3 && parts[1] !== '__tests__'\n\t\t? `domain/${parts[1]}`\n\t\t: null\n}\n\nfunction cycles(graph: Map<string, Set<string>>) {\n\tconst completed = new Set<string>()\n\tconst stack: string[] = []\n\tconst found: string[][] = []\n\tfunction visit(owner: string) {\n\t\tconst index = stack.indexOf(owner)\n\t\tif (index >= 0) {\n\t\t\tfound.push([...stack.slice(index), owner])\n\t\t\treturn\n\t\t}\n\t\tif (completed.has(owner)) return\n\t\tstack.push(owner)\n\t\tfor (const dependency of graph.get(owner) ?? []) visit(dependency)\n\t\tstack.pop()\n\t\tcompleted.add(owner)\n\t}\n\tfor (const owner of graph.keys()) visit(owner)\n\treturn found\n}\n\nfunction sharedOwnership(dependencies: Map<string, Set<string>>): ArchitectureDiagnostic[] {\n\tconst consumers = new Map<string, Set<string>>()\n\tfor (const [file, targets] of dependencies) {\n\t\tconst owner = moduleOwner(file) ?? ''\n\t\tif (!owner || owner === 'domain/shared') continue\n\t\tconst visited = new Set<string>()\n\t\tfunction visit(target: string) {\n\t\t\tif (!target.startsWith('domain/shared/') || visited.has(target)) return\n\t\t\tvisited.add(target)\n\t\t\tconst owners = consumers.get(target) ?? new Set<string>()\n\t\t\towners.add(owner)\n\t\t\tconsumers.set(target, owners)\n\t\t\tfor (const next of dependencies.get(target) ?? []) visit(next)\n\t\t}\n\t\tfor (const target of targets) visit(target)\n\t}\n\tconst diagnostics: ArchitectureDiagnostic[] = []\n\tfor (const [file, owners] of consumers) {\n\t\tif (owners.size === 1)\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'shared-owner',\n\t\t\t\tfile,\n\t\t\t\tmessage: `This shared implementation is used only by ${[...owners][0]}. Move it into that module until another module needs it.`,\n\t\t\t})\n\t}\n\treturn diagnostics\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"architecture.mjs","names":["inspectArchitecture","requestedRoot","resolve","options","root","existsSync","realpathSync","diagnostics","files","sourceFiles","required","tested","graph","dependencies","resolver","createResolver","file","filename","parsed","parseSync","readFileSync","testFile","dirname","hasRunnableTest","executable","owner","moduleOwner","targets","source","runtimeImports","target","resolveImport","targetFile","relative","sep","targetOwner","edges","directory","sharedOwnership","cycle","cycles","a","b","ResolverFactory","architectureAnchor","allowed","scan","entry","readdirSync","path","program","declaration","statement","names","item","specifier","calls","Visitor","node","skipped","parts","testCallee","part","argument","property","skip","name","base","imports","s","completed","stack","found","visit","index","dependency","consumers","visited","owners","next"],"sources":["../../src/oxlint/architecture.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs'\nimport { dirname, relative, resolve, sep } from 'node:path'\nimport { parseSync, Visitor, type Program, type Node, type CallExpression } from 'oxc-parser'\nimport { ResolverFactory } from 'oxc-resolver'\n\nexport interface ArchitectureOptions {\n\tcwd?: string\n\tconvexDir?: string\n}\nexport interface ArchitectureDiagnostic {\n\tcode:\n\t\t| 'missing-root'\n\t\t| 'root-directory'\n\t\t| 'tests'\n\t\t| 'test-location'\n\t\t| 'parse'\n\t\t| 'cycle'\n\t\t| 'symlink'\n\t\t| 'domain-entry'\n\t\t| 'shared-owner'\n\tfile: string\n\tmessage: string\n}\n\n/** Full-project checks, independent of the consumer's test runner. No persistent cache. */\nexport function inspectArchitecture(options: ArchitectureOptions = {}): ArchitectureDiagnostic[] {\n\tconst requestedRoot = resolve(options.cwd ?? process.cwd(), options.convexDir ?? 'convex')\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst diagnostics: ArchitectureDiagnostic[] = []\n\tif (!existsSync(root))\n\t\treturn [\n\t\t\t{ code: 'missing-root', file: '.', message: `Convex directory does not exist: ${root}` },\n\t\t]\n\tconst files = sourceFiles(root, diagnostics)\n\tconst required = new Set<string>()\n\tconst tested = new Set<string>()\n\tconst graph = new Map<string, Set<string>>()\n\tconst dependencies = new Map<string, Set<string>>()\n\tconst resolver = createResolver()\n\tfor (const file of files) {\n\t\tconst filename = resolve(root, file)\n\t\tconst parsed = parseSync(filename, readFileSync(filename, 'utf8'))\n\t\tif (parsed.errors.length) {\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'parse',\n\t\t\t\tfile,\n\t\t\t\tmessage: `Cannot validate ${file}: ${parsed.errors[0].message}`,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\t\tif (testFile(file)) {\n\t\t\tif (!file.includes('__tests__/'))\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tcode: 'test-location',\n\t\t\t\t\tfile,\n\t\t\t\t\tmessage: 'Place tests in a sibling __tests__/ directory.',\n\t\t\t\t})\n\t\t\telse if (dirname(file).endsWith('__tests__') && hasRunnableTest(parsed.program))\n\t\t\t\ttested.add(dirname(dirname(file)))\n\t\t\tcontinue\n\t\t}\n\t\tif (file.split('/').includes('__tests__')) continue\n\t\tif (/^domain\\/[^/]+$/.test(file) && file !== 'domain/table.ts')\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'domain-entry',\n\t\t\t\tfile,\n\t\t\t\tmessage:\n\t\t\t\t\t'Domain source belongs in domain/<module>/ or domain/shared/. Only domain/table.ts assembles module tables.',\n\t\t\t})\n\t\tif (executable(parsed.program) && file !== 'domain/table.ts') required.add(dirname(file))\n\t\tconst owner = moduleOwner(file) ?? ''\n\t\tconst targets = new Set<string>()\n\t\tdependencies.set(file, targets)\n\t\tfor (const source of runtimeImports(parsed.program)) {\n\t\t\tconst target = resolveImport(resolver, filename, source)\n\t\t\tif (!target) continue\n\t\t\tconst targetFile = relative(root, target).split(sep).join('/')\n\t\t\ttargets.add(targetFile)\n\t\t\tconst targetOwner = moduleOwner(targetFile)\n\t\t\tif (owner && targetOwner && owner !== targetOwner) {\n\t\t\t\tconst edges = graph.get(owner) ?? new Set<string>()\n\t\t\t\tgraph.set(owner, edges)\n\t\t\t\tedges.add(targetOwner)\n\t\t\t}\n\t\t}\n\t}\n\tfor (const directory of required) {\n\t\tif (!tested.has(directory))\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'tests',\n\t\t\t\tfile: directory,\n\t\t\t\tmessage: `${directory}/__tests__/ must contain a .test or .spec file with a nonempty, non-skipped test. Parent or child suites do not count.`,\n\t\t\t})\n\t}\n\tdiagnostics.push(...sharedOwnership(dependencies))\n\tfor (const cycle of cycles(graph))\n\t\tdiagnostics.push({\n\t\t\tcode: 'cycle',\n\t\t\tfile: cycle[0],\n\t\t\tmessage: `Module dependency cycle: ${cycle.join(' → ')}.`,\n\t\t})\n\treturn diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code))\n}\n\nexport function createResolver() {\n\treturn new ResolverFactory({\n\t\ttsconfig: 'auto',\n\t\textensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json'],\n\t\textensionAlias: {\n\t\t\t'.js': ['.ts', '.tsx', '.js'],\n\t\t\t'.mjs': ['.mts', '.mjs'],\n\t\t\t'.cjs': ['.cts', '.cjs'],\n\t\t},\n\t\tconditionNames: ['types', 'import', 'node', 'default'],\n\t\tsymlinks: true,\n\t})\n}\n\nexport function resolveImport(resolver: ResolverFactory, filename: string, source: string) {\n\tconst found = resolver.resolveFileSync(filename, source).path\n\tif (found) return found\n\t// Keep path checks useful in unsaved/editor fixtures and before Convex codegen.\n\treturn source.startsWith('.') ? resolve(dirname(filename), source) : null\n}\n\n/** The lint rule runs the project scan on one deterministic source file. */\nexport function architectureAnchor(root: string) {\n\tif (!existsSync(root)) return null\n\tif (existsSync(resolve(root, 'convex.config.ts')))\n\t\treturn realpathSync(resolve(root, 'convex.config.ts'))\n\troot = realpathSync(root)\n\tfor (const file of sourceFiles(root, []))\n\t\tif (!file.includes('__tests__/') && !testFile(file)) return resolve(root, file)\n\treturn null\n}\n\nfunction* sourceFiles(root: string, diagnostics: ArchitectureDiagnostic[]): Generator<string> {\n\tconst allowed = new Set([\n\t\t'_generated',\n\t\t'__tests__',\n\t\t'api',\n\t\t'domain',\n\t\t'application',\n\t\t'migrations',\n\t\t'components',\n\t])\n\tfunction* scan(directory: string): Generator<string> {\n\t\tfor (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t)) {\n\t\t\tconst path = resolve(directory, entry.name)\n\t\t\tconst file = relative(root, path).split(sep).join('/')\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tcode: 'symlink',\n\t\t\t\t\tfile,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t'Keep application source inside its owning directory; symlinked files and folders are not allowed.',\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (directory === root && !allowed.has(entry.name))\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\tcode: 'root-directory',\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tmessage: `Root folder ${entry.name}/ is not allowed. Use api/, domain/, application/, migrations/, components/, __tests__/, or _generated/.`,\n\t\t\t\t\t})\n\t\t\t\tif (\n\t\t\t\t\tentry.name !== '_generated' &&\n\t\t\t\t\tentry.name !== 'node_modules' &&\n\t\t\t\t\t!entry.name.startsWith('.')\n\t\t\t\t)\n\t\t\t\t\tyield* scan(path)\n\t\t\t} else if (/\\.[cm]?[jt]sx?$/.test(entry.name) && !/\\.d\\.[cm]?ts$/.test(entry.name)) yield file\n\t\t}\n\t}\n\tyield* scan(root)\n}\n\nfunction testFile(file: string) {\n\treturn /\\.(?:test|spec)\\.[cm]?[jt]sx?$/.test(file)\n}\n\nfunction executable(program: Program) {\n\treturn program.body.some((statement) => {\n\t\tconst declaration =\n\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\tif (declaration && 'declare' in declaration && declaration.declare) return false\n\t\tif (statement.type === 'ImportDeclaration' || statement.type === 'EmptyStatement') return false\n\t\tif (statement.type === 'ExportNamedDeclaration') {\n\t\t\tif (statement.exportKind === 'type' || !statement.declaration) return false\n\t\t\treturn !['TSInterfaceDeclaration', 'TSTypeAliasDeclaration', 'TSDeclareFunction'].includes(\n\t\t\t\tstatement.declaration.type,\n\t\t\t)\n\t\t}\n\t\treturn ![\n\t\t\t'TSInterfaceDeclaration',\n\t\t\t'TSTypeAliasDeclaration',\n\t\t\t'TSDeclareFunction',\n\t\t\t'ExportAllDeclaration',\n\t\t].includes(statement.type)\n\t})\n}\n\nfunction hasRunnableTest(program: Program) {\n\tconst names = new Map([\n\t\t['test', 'test'],\n\t\t['it', 'it'],\n\t\t['describe', 'describe'],\n\t\t['suite', 'suite'],\n\t])\n\tfor (const statement of program.body) {\n\t\tconst declaration =\n\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\tif (declaration?.type === 'FunctionDeclaration' && declaration.id)\n\t\t\tnames.delete(declaration.id.name)\n\t\tif (declaration?.type === 'VariableDeclaration')\n\t\t\tfor (const item of declaration.declarations) {\n\t\t\t\tif (item.id.type === 'Identifier') names.delete(item.id.name)\n\t\t\t}\n\t\tif (statement.type !== 'ImportDeclaration') continue\n\t\tfor (const specifier of statement.specifiers) {\n\t\t\tnames.delete(specifier.local.name)\n\t\t\tif (\n\t\t\t\t!['vitest', 'vite-plus/test', 'bun:test', 'node:test', '@jest/globals'].includes(\n\t\t\t\t\tstatement.source.value,\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tcontinue\n\t\t\tif (specifier.type === 'ImportNamespaceSpecifier') names.set(specifier.local.name, '*')\n\t\t\telse if (specifier.type === 'ImportDefaultSpecifier') names.set(specifier.local.name, 'test')\n\t\t\telse if (specifier.imported.type === 'Identifier')\n\t\t\t\tnames.set(specifier.local.name, specifier.imported.name)\n\t\t}\n\t}\n\tconst calls: CallExpression[] = []\n\tnew Visitor({\n\t\tCallExpression(node) {\n\t\t\tcalls.push(node)\n\t\t},\n\t}).visit(program)\n\tconst skipped = calls.filter((node) => {\n\t\tconst parts = testCallee(node.callee, names)\n\t\treturn (\n\t\t\t['test', 'it', 'describe', 'suite'].includes(parts[0]) &&\n\t\t\t(parts.some((part) => ['skip', 'todo', 'skipIf', 'runIf'].includes(part)) ||\n\t\t\t\tnode.arguments.some(\n\t\t\t\t\t(argument) =>\n\t\t\t\t\t\targument.type === 'ObjectExpression' &&\n\t\t\t\t\t\targument.properties.some(\n\t\t\t\t\t\t\t(property) =>\n\t\t\t\t\t\t\t\tproperty.type === 'Property' &&\n\t\t\t\t\t\t\t\t['skip', 'todo'].includes(\n\t\t\t\t\t\t\t\t\tproperty.key.type === 'Identifier'\n\t\t\t\t\t\t\t\t\t\t? property.key.name\n\t\t\t\t\t\t\t\t\t\t: property.key.type === 'Literal'\n\t\t\t\t\t\t\t\t\t\t\t? String(property.key.value)\n\t\t\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t\t) &&\n\t\t\t\t\t\t\t\t!(property.value.type === 'Literal' && property.value.value === false),\n\t\t\t\t\t\t),\n\t\t\t\t))\n\t\t)\n\t})\n\treturn calls.some((node) => {\n\t\tconst parts = testCallee(node.callee, names)\n\t\tif (\n\t\t\t!['test', 'it'].includes(parts[0]) ||\n\t\t\tskipped.some((skip) => node.start >= skip.start && node.end <= skip.end)\n\t\t)\n\t\t\treturn false\n\t\t// each(...) is a factory; only the outer call has the test callback.\n\t\treturn node.arguments.some(\n\t\t\t(argument) =>\n\t\t\t\t(argument.type === 'ArrowFunctionExpression' || argument.type === 'FunctionExpression') &&\n\t\t\t\targument.body &&\n\t\t\t\t(argument.body.type !== 'BlockStatement' ||\n\t\t\t\t\targument.body.body.some((statement) => statement.type !== 'EmptyStatement')),\n\t\t)\n\t})\n}\n\nfunction testCallee(node: Node, names: Map<string, string>): string[] {\n\tif (node.type === 'Identifier') {\n\t\tconst name = names.get(node.name)\n\t\treturn name ? [name] : []\n\t}\n\tif (node.type === 'CallExpression') return testCallee(node.callee, names)\n\tif (node.type === 'MemberExpression') {\n\t\tconst base = testCallee(node.object, names)\n\t\tconst property =\n\t\t\t!node.computed && node.property.type === 'Identifier'\n\t\t\t\t? node.property.name\n\t\t\t\t: node.property.type === 'Literal'\n\t\t\t\t\t? String(node.property.value)\n\t\t\t\t\t: ''\n\t\tif (!base.length || !property) return []\n\t\treturn base[0] === '*' ? [property] : [...base, property]\n\t}\n\treturn []\n}\n\nfunction runtimeImports(program: Program) {\n\tconst imports: string[] = []\n\tnew Visitor({\n\t\tImportDeclaration(node) {\n\t\t\tif (\n\t\t\t\tnode.importKind !== 'type' &&\n\t\t\t\t(node.specifiers.length === 0 ||\n\t\t\t\t\tnode.specifiers.some((s) => s.type !== 'ImportSpecifier' || s.importKind !== 'type'))\n\t\t\t)\n\t\t\t\timports.push(node.source.value)\n\t\t},\n\t\tExportNamedDeclaration(node) {\n\t\t\tif (\n\t\t\t\tnode.source &&\n\t\t\t\tnode.exportKind !== 'type' &&\n\t\t\t\tnode.specifiers.some((specifier) => specifier.exportKind !== 'type')\n\t\t\t)\n\t\t\t\timports.push(node.source.value)\n\t\t},\n\t\tExportAllDeclaration(node) {\n\t\t\tif (node.exportKind !== 'type') imports.push(node.source.value)\n\t\t},\n\t\tImportExpression(node) {\n\t\t\tif (node.source.type === 'Literal' && String(node.source.value) === node.source.value)\n\t\t\t\timports.push(String(node.source.value))\n\t\t},\n\t}).visit(program)\n\treturn imports\n}\n\nfunction moduleOwner(file: string) {\n\tconst parts = file.split('/')\n\treturn parts[0] === 'domain' && parts.length >= 3 && parts[1] !== '__tests__'\n\t\t? `domain/${parts[1]}`\n\t\t: null\n}\n\nfunction cycles(graph: Map<string, Set<string>>) {\n\tconst completed = new Set<string>()\n\tconst stack: string[] = []\n\tconst found: string[][] = []\n\tfunction visit(owner: string) {\n\t\tconst index = stack.indexOf(owner)\n\t\tif (index >= 0) {\n\t\t\tfound.push([...stack.slice(index), owner])\n\t\t\treturn\n\t\t}\n\t\tif (completed.has(owner)) return\n\t\tstack.push(owner)\n\t\tfor (const dependency of graph.get(owner) ?? []) visit(dependency)\n\t\tstack.pop()\n\t\tcompleted.add(owner)\n\t}\n\tfor (const owner of graph.keys()) visit(owner)\n\treturn found\n}\n\nfunction sharedOwnership(dependencies: Map<string, Set<string>>): ArchitectureDiagnostic[] {\n\tconst consumers = new Map<string, Set<string>>()\n\tfor (const [file, targets] of dependencies) {\n\t\tconst owner = moduleOwner(file) ?? ''\n\t\tif (!owner || owner === 'domain/shared') continue\n\t\tconst visited = new Set<string>()\n\t\tfunction visit(target: string) {\n\t\t\tif (!target.startsWith('domain/shared/') || visited.has(target)) return\n\t\t\tvisited.add(target)\n\t\t\tconst owners = consumers.get(target) ?? new Set<string>()\n\t\t\towners.add(owner)\n\t\t\tconsumers.set(target, owners)\n\t\t\tfor (const next of dependencies.get(target) ?? []) visit(next)\n\t\t}\n\t\tfor (const target of targets) visit(target)\n\t}\n\tconst diagnostics: ArchitectureDiagnostic[] = []\n\tfor (const [file, owners] of consumers) {\n\t\tif (owners.size === 1)\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: 'shared-owner',\n\t\t\t\tfile,\n\t\t\t\tmessage: `This shared implementation is used only by ${[...owners][0]}. Move it into that module until another module needs it.`,\n\t\t\t})\n\t}\n\treturn diagnostics\n}\n"],"mappings":"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"}
|
package/dist/oxlint/ast.d.mts
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { Context, ESTree, RuleMeta, Visitor } from "@oxlint/plugins";
|
|
3
3
|
//#region src/oxlint/ast.d.ts
|
|
4
|
-
declare const consumerOptions: z.ZodObject<{
|
|
4
|
+
export declare const consumerOptions: z.ZodObject<{
|
|
5
5
|
convexDir: z.ZodDefault<z.ZodString>;
|
|
6
6
|
}, z.core.$strip>;
|
|
7
|
-
declare function appFile(context: Context, filename?: string): string | null;
|
|
8
|
-
declare function imported(context: Context, node: ESTree.Node): {
|
|
7
|
+
export declare function appFile(context: Context, filename?: string): string | null;
|
|
8
|
+
export declare function imported(context: Context, node: ESTree.Node): {
|
|
9
9
|
source: string;
|
|
10
10
|
name: string;
|
|
11
11
|
} | null;
|
|
12
|
-
declare function sourceVisitors(context: Context, check: (node: ESTree.Node, source: string) => void): Visitor;
|
|
13
|
-
declare function isFunctionsImport(context: Context, source: string): boolean;
|
|
14
|
-
declare function typeOnlySource(node: ESTree.Node): boolean;
|
|
15
|
-
declare function isDbAccess(node: ESTree.CallExpression): boolean;
|
|
16
|
-
declare function isExternalCall(context: Context, node: ESTree.CallExpression): boolean;
|
|
17
|
-
declare function containsComponentReference(context: Context, node: ESTree.Node): boolean;
|
|
18
|
-
declare function rootMember(node: ESTree.Node): ESTree.Node;
|
|
19
|
-
declare function memberPath(node: ESTree.Node): string[];
|
|
20
|
-
declare function isTableChain(node: ESTree.Node): boolean;
|
|
21
|
-
declare function isDbWrite(node: ESTree.CallExpression): boolean;
|
|
22
|
-
declare function hasCall(node: ESTree.Node, name: string): boolean;
|
|
23
|
-
declare function metadata(description: string, messages: Record<string, string>): RuleMeta;
|
|
24
|
-
declare function componentRoot(context: Context, filename: string): string | null;
|
|
25
|
-
declare function staticString(node: ESTree.Node | null | undefined): string | null;
|
|
26
|
-
declare function propertyName(node: ESTree.Node): string | null;
|
|
27
|
-
declare function binding(context: Context, node: ESTree.Node): import("@oxlint/plugins").Variable | null;
|
|
28
|
-
declare function exportedNames(program: ESTree.Program): Set<string>;
|
|
29
|
-
declare function exportName(node: ESTree.Node): string | null;
|
|
30
|
-
declare function delegatingBody(node: ESTree.Node): boolean;
|
|
12
|
+
export declare function sourceVisitors(context: Context, check: (node: ESTree.Node, source: string) => void): Visitor;
|
|
13
|
+
export declare function isFunctionsImport(context: Context, source: string): boolean;
|
|
14
|
+
export declare function typeOnlySource(node: ESTree.Node): boolean;
|
|
15
|
+
export declare function isDbAccess(node: ESTree.CallExpression): boolean;
|
|
16
|
+
export declare function isExternalCall(context: Context, node: ESTree.CallExpression): boolean;
|
|
17
|
+
export declare function containsComponentReference(context: Context, node: ESTree.Node): boolean;
|
|
18
|
+
export declare function rootMember(node: ESTree.Node): ESTree.Node;
|
|
19
|
+
export declare function memberPath(node: ESTree.Node): string[];
|
|
20
|
+
export declare function isTableChain(node: ESTree.Node): boolean;
|
|
21
|
+
export declare function isDbWrite(node: ESTree.CallExpression): boolean;
|
|
22
|
+
export declare function hasCall(node: ESTree.Node, name: string): boolean;
|
|
23
|
+
export declare function metadata(description: string, messages: Record<string, string>): RuleMeta;
|
|
24
|
+
export declare function componentRoot(context: Context, filename: string): string | null;
|
|
25
|
+
export declare function staticString(node: ESTree.Node | null | undefined): string | null;
|
|
26
|
+
export declare function propertyName(node: ESTree.Node): string | null;
|
|
27
|
+
export declare function binding(context: Context, node: ESTree.Node): import("@oxlint/plugins").Variable | null;
|
|
28
|
+
export declare function exportedNames(program: ESTree.Program): Set<string>;
|
|
29
|
+
export declare function exportName(node: ESTree.Node): string | null;
|
|
30
|
+
export declare function delegatingBody(node: ESTree.Node): boolean;
|
|
31
31
|
//#endregion
|
|
32
|
-
export { appFile, binding, componentRoot, consumerOptions, containsComponentReference, delegatingBody, exportName, exportedNames, hasCall, imported, isDbAccess, isDbWrite, isExternalCall, isFunctionsImport, isTableChain, memberPath, metadata, propertyName, rootMember, sourceVisitors, staticString, typeOnlySource };
|
|
33
32
|
//# sourceMappingURL=ast.d.mts.map
|
package/dist/oxlint/ast.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createResolver as e,resolveImport as t}from"./architecture.mjs";import{z as n}from"zod";import{dirname as r,relative as i,resolve as a,sep as o}from"node:path";import{existsSync as s,realpathSync as c}from"node:fs";const l=n.object({convexDir:n.string().min(1).default(`convex`)});function u(e,t=e.filename){let n=a(e.cwd,l.parse(e.options[0]??{}).convexDir),r=s(n)?c(n):n,u=i(r,s(t)?c(t):t).split(o).join(`/`);return u===`..`||u.startsWith(`../`)||u.startsWith(`/`)?null:u}function d(e,t){if(t.type===`CallExpression`)return d(e,t.callee);if(t.type===`MemberExpression`){let n=d(e,t.object),r=E(t);return n&&r&&[`*`,`z`,`default`].includes(n.name)?{source:n.source,name:r}:null}if(t.type!==`Identifier`)return null;for(let n of D(e,t)?.defs??[]){if(n.type!==`ImportBinding`||n.parent?.type!==`ImportDeclaration`||n.parent.importKind===`type`)continue;let e=n.node;if(e.type!==`ImportSpecifier`||e.importKind!==`type`)return{source:n.parent.source.value,name:e.type===`ImportSpecifier`?k(e.imported)??``:e.type===`ImportDefaultSpecifier`?`default`:`*`}}return null}function f(e,t){function n(e,n){let r=T(n);r&&t(e,r)}return{ImportDeclaration(e){n(e,e.source)},ExportNamedDeclaration(e){n(e,e.source)},ExportAllDeclaration(e){n(e,e.source)},ImportExpression(e){n(e,e.source)},TSImportType(e){n(e,e.source)},CallExpression(t){t.callee.type===`Identifier`&&t.callee.name===`require`&&!D(e,t.callee)?.defs.length&&n(t,t.arguments[0])}}}function p(n,i){let o=i.startsWith(`.`)?a(r(n.filename),i):t(e(),n.filename,i);return o!==null&&u(n,o)?.replace(/\.[cm]?[jt]s$/,``)===`functions`}function m(e){return e.type===`TSImportType`||e.type===`ImportDeclaration`&&(e.importKind===`type`||e.specifiers.length>0&&e.specifiers.every(e=>e.type===`ImportSpecifier`&&e.importKind===`type`))||(e.type===`ExportNamedDeclaration`||e.type===`ExportAllDeclaration`)&&e.exportKind===`type`}function h(e){return e.callee.type===`MemberExpression`&&e.callee.object.type===`MemberExpression`&&E(e.callee.object)===`db`}function g(e,t){let n=d(e,t.callee);return n&&/^(?:node:)?(?:https?|net|tls|fs)(?:\/|$)|^(?:undici|axios)(?:\/|$)/.test(n.source)?!0:t.callee.type===`Identifier`&&t.callee.name===`fetch`&&!D(e,t.callee)?.defs.length||t.callee.type===`MemberExpression`&&E(t.callee)===`fetch`&&t.callee.object.type===`Identifier`&&t.callee.object.name===`globalThis`&&!D(e,t.callee.object)?.defs.length}function _(e,t){if(t.type===`MemberExpression`){let n=d(e,t.object);return n?.name===`components`&&/_generated\/api/.test(n.source)?!0:_(e,t.object)}return t.type===`ObjectExpression`&&t.properties.some(t=>t.type===`Property`&&_(e,t.value))}function v(e){return e.type===`MemberExpression`?v(e.object):e}function y(e){return e.type===`Identifier`?[e.name]:e.type===`MemberExpression`?[...y(e.object),E(e)??``]:[]}function b(e){return e.type===`MemberExpression`?E(e)===`table`||b(e.object):e.type===`CallExpression`&&b(e.callee)}function x(e){return e.callee.type===`MemberExpression`&&[`insert`,`patch`,`replace`,`delete`].includes(E(e.callee)??``)&&e.callee.object.type===`MemberExpression`&&E(e.callee.object)===`db`}function S(e,t){return e.type===`CallExpression`?e.callee.type===`MemberExpression`&&E(e.callee)===t&&(t!==`query`||E(e.callee.object)===`db`)||S(e.callee,t):e.type===`MemberExpression`&&S(e.object,t)}function C(e,t){return{type:`problem`,docs:{description:e},schema:[],messages:t}}function w(e,t){let n=a(e.cwd,l.parse(e.options[0]??{}).convexDir),r=s(n)?c(n):n,u=i(r,s(t)?c(t):t).split(o).join(`/`).match(/^components\/([^/]+)(?:\/|$)/);return u?a(r,`components`,u[1]):null}function T(e){return e?.type===`Literal`&&`value`in e&&String(e.value)===e.value?String(e.value):e?.type===`TemplateLiteral`&&e.expressions.length===0?e.quasis[0].value.cooked:null}function E(e){return e.type===`MemberExpression`?e.computed?T(e.property):e.property.name:null}function D(e,t){if(t.type!==`Identifier`)return null;let n=e.sourceCode.getScope(t);for(;n;){let e=n.set.get(t.name);if(e)return e;n=n.upper}return null}function O(e){let t
|
|
1
|
+
import{createResolver as e,resolveImport as t}from"./architecture.mjs";import{z as n}from"zod";import{dirname as r,relative as i,resolve as a,sep as o}from"node:path";import{existsSync as s,realpathSync as c}from"node:fs";const l=n.object({convexDir:n.string().min(1).default(`convex`)});function u(e,t=e.filename){let n=a(e.cwd,l.parse(e.options[0]??{}).convexDir),r=s(n)?c(n):n,u=i(r,s(t)?c(t):t).split(o).join(`/`);return u===`..`||u.startsWith(`../`)||u.startsWith(`/`)?null:u}function d(e,t){if(t.type===`CallExpression`)return d(e,t.callee);if(t.type===`MemberExpression`){let n=d(e,t.object),r=E(t);return n&&r&&[`*`,`z`,`default`].includes(n.name)?{source:n.source,name:r}:null}if(t.type!==`Identifier`)return null;for(let n of D(e,t)?.defs??[]){if(n.type!==`ImportBinding`||n.parent?.type!==`ImportDeclaration`||n.parent.importKind===`type`)continue;let e=n.node;if(e.type!==`ImportSpecifier`||e.importKind!==`type`)return{source:n.parent.source.value,name:e.type===`ImportSpecifier`?k(e.imported)??``:e.type===`ImportDefaultSpecifier`?`default`:`*`}}return null}function f(e,t){function n(e,n){let r=T(n);r&&t(e,r)}return{ImportDeclaration(e){n(e,e.source)},ExportNamedDeclaration(e){n(e,e.source)},ExportAllDeclaration(e){n(e,e.source)},ImportExpression(e){n(e,e.source)},TSImportType(e){n(e,e.source)},CallExpression(t){t.callee.type===`Identifier`&&t.callee.name===`require`&&!D(e,t.callee)?.defs.length&&n(t,t.arguments[0])}}}function p(n,i){let o=i.startsWith(`.`)?a(r(n.filename),i):t(e(),n.filename,i);return o!==null&&u(n,o)?.replace(/\.[cm]?[jt]s$/,``)===`functions`}function m(e){return e.type===`TSImportType`||e.type===`ImportDeclaration`&&(e.importKind===`type`||e.specifiers.length>0&&e.specifiers.every(e=>e.type===`ImportSpecifier`&&e.importKind===`type`))||(e.type===`ExportNamedDeclaration`||e.type===`ExportAllDeclaration`)&&e.exportKind===`type`}function h(e){return e.callee.type===`MemberExpression`&&e.callee.object.type===`MemberExpression`&&E(e.callee.object)===`db`}function g(e,t){let n=d(e,t.callee);return n&&/^(?:node:)?(?:https?|net|tls|fs)(?:\/|$)|^(?:undici|axios)(?:\/|$)/.test(n.source)?!0:t.callee.type===`Identifier`&&t.callee.name===`fetch`&&!D(e,t.callee)?.defs.length||t.callee.type===`MemberExpression`&&E(t.callee)===`fetch`&&t.callee.object.type===`Identifier`&&t.callee.object.name===`globalThis`&&!D(e,t.callee.object)?.defs.length}function _(e,t){if(t.type===`MemberExpression`){let n=d(e,t.object);return n?.name===`components`&&/_generated\/api/.test(n.source)?!0:_(e,t.object)}return t.type===`ObjectExpression`&&t.properties.some(t=>t.type===`Property`&&_(e,t.value))}function v(e){return e.type===`MemberExpression`?v(e.object):e}function y(e){return e.type===`Identifier`?[e.name]:e.type===`MemberExpression`?[...y(e.object),E(e)??``]:[]}function b(e){return e.type===`MemberExpression`?E(e)===`table`||b(e.object):e.type===`CallExpression`&&b(e.callee)}function x(e){return e.callee.type===`MemberExpression`&&[`insert`,`patch`,`replace`,`delete`].includes(E(e.callee)??``)&&e.callee.object.type===`MemberExpression`&&E(e.callee.object)===`db`}function S(e,t){return e.type===`CallExpression`?e.callee.type===`MemberExpression`&&E(e.callee)===t&&(t!==`query`||E(e.callee.object)===`db`)||S(e.callee,t):e.type===`MemberExpression`&&S(e.object,t)}function C(e,t){return{type:`problem`,docs:{description:e},schema:[],messages:t}}function w(e,t){let n=a(e.cwd,l.parse(e.options[0]??{}).convexDir),r=s(n)?c(n):n,u=i(r,s(t)?c(t):t).split(o).join(`/`).match(/^components\/([^/]+)(?:\/|$)/);return u?a(r,`components`,u[1]):null}function T(e){return e?.type===`Literal`&&`value`in e&&String(e.value)===e.value?String(e.value):e?.type===`TemplateLiteral`&&e.expressions.length===0?e.quasis[0].value.cooked:null}function E(e){return e.type===`MemberExpression`?e.computed?T(e.property):e.property.name:null}function D(e,t){if(t.type!==`Identifier`)return null;let n=e.sourceCode.getScope(t);for(;n;){let e=n.set.get(t.name);if(e)return e;n=n.upper}return null}function O(e){let t=/* @__PURE__ */ new Set;for(let n of e.body)if(n.type===`ExportNamedDeclaration`&&!n.source)for(let e of n.specifiers)t.add(k(e.local)??``);else n.type===`ExportDefaultDeclaration`&&n.declaration.type===`Identifier`&&t.add(n.declaration.name);return t}function k(e){return e.type===`Identifier`?e.name:T(e)}function A(e){return e.type===`CallExpression`?!0:e.type===`AwaitExpression`?A(e.argument):e.type===`BlockStatement`&&e.body.length>0&&e.body.every(e=>e.type===`ReturnStatement`&&e.argument!==null&&A(e.argument)||e.type===`ExpressionStatement`&&A(e.expression))}export{u as appFile,D as binding,w as componentRoot,l as consumerOptions,_ as containsComponentReference,A as delegatingBody,k as exportName,O as exportedNames,S as hasCall,d as imported,h as isDbAccess,x as isDbWrite,g as isExternalCall,p as isFunctionsImport,b as isTableChain,y as memberPath,C as metadata,E as propertyName,v as rootMember,f as sourceVisitors,T as staticString,m as typeOnlySource};
|
|
2
2
|
//# sourceMappingURL=ast.mjs.map
|
package/dist/oxlint/ast.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.mjs","names":[],"sources":["../../src/oxlint/ast.ts"],"sourcesContent":["import { existsSync, realpathSync } from 'node:fs'\nimport { dirname, relative, resolve, sep } from 'node:path'\nimport type { Context, ESTree, RuleMeta, Scope, Visitor } from '@oxlint/plugins'\nimport { z } from 'zod'\nimport { createResolver, resolveImport } from './architecture'\n\nexport const consumerOptions = z.object({ convexDir: z.string().min(1).default('convex') })\n\nexport function appFile(context: Context, filename = context.filename) {\n\tconst requestedRoot = resolve(\n\t\tcontext.cwd,\n\t\tconsumerOptions.parse(context.options[0] ?? {}).convexDir,\n\t)\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst file = relative(root, existsSync(filename) ? realpathSync(filename) : filename)\n\t\t.split(sep)\n\t\t.join('/')\n\treturn file === '..' || file.startsWith('../') || file.startsWith('/') ? null : file\n}\n\nexport function imported(\n\tcontext: Context,\n\tnode: ESTree.Node,\n): { source: string; name: string } | null {\n\tif (node.type === 'CallExpression') return imported(context, node.callee)\n\tif (node.type === 'MemberExpression') {\n\t\tconst owner = imported(context, node.object)\n\t\tconst name = propertyName(node)\n\t\treturn owner && name && ['*', 'z', 'default'].includes(owner.name)\n\t\t\t? { source: owner.source, name }\n\t\t\t: null\n\t}\n\tif (node.type !== 'Identifier') return null\n\tfor (const def of binding(context, node)?.defs ?? []) {\n\t\tif (\n\t\t\tdef.type !== 'ImportBinding' ||\n\t\t\tdef.parent?.type !== 'ImportDeclaration' ||\n\t\t\tdef.parent.importKind === 'type'\n\t\t)\n\t\t\tcontinue\n\t\tconst specifier = def.node\n\t\tif (specifier.type === 'ImportSpecifier' && specifier.importKind === 'type') continue\n\t\treturn {\n\t\t\tsource: def.parent.source.value,\n\t\t\tname:\n\t\t\t\tspecifier.type === 'ImportSpecifier'\n\t\t\t\t\t? (exportName(specifier.imported) ?? '')\n\t\t\t\t\t: specifier.type === 'ImportDefaultSpecifier'\n\t\t\t\t\t\t? 'default'\n\t\t\t\t\t\t: '*',\n\t\t}\n\t}\n\treturn null\n}\n\nexport function sourceVisitors(\n\tcontext: Context,\n\tcheck: (node: ESTree.Node, source: string) => void,\n): Visitor {\n\tfunction visit(node: ESTree.Node, source: ESTree.Node | null | undefined) {\n\t\tconst text = staticString(source)\n\t\tif (text) check(node, text)\n\t}\n\treturn {\n\t\tImportDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tExportNamedDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tExportAllDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tImportExpression(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tTSImportType(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tCallExpression(node) {\n\t\t\tif (\n\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\tnode.callee.name === 'require' &&\n\t\t\t\t!binding(context, node.callee)?.defs.length\n\t\t\t)\n\t\t\t\tvisit(node, node.arguments[0])\n\t\t},\n\t}\n}\n\nexport function isFunctionsImport(context: Context, source: string) {\n\tconst target = source.startsWith('.')\n\t\t? resolve(dirname(context.filename), source)\n\t\t: resolveImport(createResolver(), context.filename, source)\n\treturn target !== null && appFile(context, target)?.replace(/\\.[cm]?[jt]s$/, '') === 'functions'\n}\n\nexport function typeOnlySource(node: ESTree.Node) {\n\treturn (\n\t\tnode.type === 'TSImportType' ||\n\t\t(node.type === 'ImportDeclaration' &&\n\t\t\t(node.importKind === 'type' ||\n\t\t\t\t(node.specifiers.length > 0 &&\n\t\t\t\t\tnode.specifiers.every(\n\t\t\t\t\t\t(specifier) => specifier.type === 'ImportSpecifier' && specifier.importKind === 'type',\n\t\t\t\t\t)))) ||\n\t\t((node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') &&\n\t\t\tnode.exportKind === 'type')\n\t)\n}\n\nexport function isDbAccess(node: ESTree.CallExpression) {\n\treturn (\n\t\tnode.callee.type === 'MemberExpression' &&\n\t\tnode.callee.object.type === 'MemberExpression' &&\n\t\tpropertyName(node.callee.object) === 'db'\n\t)\n}\n\nexport function isExternalCall(context: Context, node: ESTree.CallExpression) {\n\tconst ref = imported(context, node.callee)\n\tif (ref && /^(?:node:)?(?:https?|net|tls|fs)(?:\\/|$)|^(?:undici|axios)(?:\\/|$)/.test(ref.source))\n\t\treturn true\n\treturn (\n\t\t(node.callee.type === 'Identifier' &&\n\t\t\tnode.callee.name === 'fetch' &&\n\t\t\t!binding(context, node.callee)?.defs.length) ||\n\t\t(node.callee.type === 'MemberExpression' &&\n\t\t\tpropertyName(node.callee) === 'fetch' &&\n\t\t\tnode.callee.object.type === 'Identifier' &&\n\t\t\tnode.callee.object.name === 'globalThis' &&\n\t\t\t!binding(context, node.callee.object)?.defs.length)\n\t)\n}\n\nexport function containsComponentReference(context: Context, node: ESTree.Node): boolean {\n\tif (node.type === 'MemberExpression') {\n\t\tconst owner = imported(context, node.object)\n\t\tif (owner?.name === 'components' && /_generated\\/api/.test(owner.source)) return true\n\t\treturn containsComponentReference(context, node.object)\n\t}\n\tif (node.type === 'ObjectExpression')\n\t\treturn node.properties.some(\n\t\t\t(property) =>\n\t\t\t\tproperty.type === 'Property' && containsComponentReference(context, property.value),\n\t\t)\n\treturn false\n}\n\nexport function rootMember(node: ESTree.Node): ESTree.Node {\n\treturn node.type === 'MemberExpression' ? rootMember(node.object) : node\n}\n\nexport function memberPath(node: ESTree.Node): string[] {\n\tif (node.type === 'Identifier') return [node.name]\n\tif (node.type === 'MemberExpression')\n\t\treturn [...memberPath(node.object), propertyName(node) ?? '']\n\treturn []\n}\n\nexport function isTableChain(node: ESTree.Node): boolean {\n\tif (node.type === 'MemberExpression')\n\t\treturn propertyName(node) === 'table' || isTableChain(node.object)\n\treturn node.type === 'CallExpression' && isTableChain(node.callee)\n}\n\nexport function isDbWrite(node: ESTree.CallExpression) {\n\treturn (\n\t\tnode.callee.type === 'MemberExpression' &&\n\t\t['insert', 'patch', 'replace', 'delete'].includes(propertyName(node.callee) ?? '') &&\n\t\tnode.callee.object.type === 'MemberExpression' &&\n\t\tpropertyName(node.callee.object) === 'db'\n\t)\n}\n\nexport function hasCall(node: ESTree.Node, name: string): boolean {\n\tif (node.type === 'CallExpression')\n\t\treturn (\n\t\t\t(node.callee.type === 'MemberExpression' &&\n\t\t\t\tpropertyName(node.callee) === name &&\n\t\t\t\t(name !== 'query' || propertyName(node.callee.object) === 'db')) ||\n\t\t\thasCall(node.callee, name)\n\t\t)\n\tif (node.type === 'MemberExpression') return hasCall(node.object, name)\n\treturn false\n}\n\nexport function metadata(description: string, messages: Record<string, string>): RuleMeta {\n\treturn { type: 'problem', docs: { description }, schema: [], messages }\n}\n\nexport function componentRoot(context: Context, filename: string) {\n\tconst requestedRoot = resolve(\n\t\tcontext.cwd,\n\t\tconsumerOptions.parse(context.options[0] ?? {}).convexDir,\n\t)\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst file = relative(root, existsSync(filename) ? realpathSync(filename) : filename)\n\t\t.split(sep)\n\t\t.join('/')\n\tconst match = file.match(/^components\\/([^/]+)(?:\\/|$)/)\n\treturn match ? resolve(root, 'components', match[1]) : null\n}\n\nexport function staticString(node: ESTree.Node | null | undefined) {\n\tif (node?.type === 'Literal' && 'value' in node && String(node.value) === node.value)\n\t\treturn String(node.value)\n\tif (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n\t\treturn node.quasis[0].value.cooked\n\treturn null\n}\n\nexport function propertyName(node: ESTree.Node): string | null {\n\tif (node.type !== 'MemberExpression') return null\n\treturn node.computed ? staticString(node.property) : node.property.name\n}\n\nexport function binding(context: Context, node: ESTree.Node) {\n\tif (node.type !== 'Identifier') return null\n\tlet scope: Scope | null = context.sourceCode.getScope(node)\n\twhile (scope) {\n\t\tconst variable = scope.set.get(node.name)\n\t\tif (variable) return variable\n\t\tscope = scope.upper\n\t}\n\treturn null\n}\n\nexport function exportedNames(program: ESTree.Program) {\n\tconst names = new Set<string>()\n\tfor (const statement of program.body) {\n\t\tif (statement.type === 'ExportNamedDeclaration' && !statement.source) {\n\t\t\tfor (const specifier of statement.specifiers) names.add(exportName(specifier.local) ?? '')\n\t\t} else if (\n\t\t\tstatement.type === 'ExportDefaultDeclaration' &&\n\t\t\tstatement.declaration.type === 'Identifier'\n\t\t) {\n\t\t\tnames.add(statement.declaration.name)\n\t\t}\n\t}\n\treturn names\n}\n\nexport function exportName(node: ESTree.Node): string | null {\n\treturn node.type === 'Identifier' ? node.name : staticString(node)\n}\n\nexport function delegatingBody(node: ESTree.Node): boolean {\n\tif (node.type === 'CallExpression') return true\n\tif (node.type === 'AwaitExpression') return delegatingBody(node.argument)\n\tif (node.type === 'BlockStatement')\n\t\treturn (\n\t\t\tnode.body.length > 0 &&\n\t\t\tnode.body.every(\n\t\t\t\t(statement) =>\n\t\t\t\t\t(statement.type === 'ReturnStatement' &&\n\t\t\t\t\t\tstatement.argument !== null &&\n\t\t\t\t\t\tdelegatingBody(statement.argument)) ||\n\t\t\t\t\t(statement.type === 'ExpressionStatement' && delegatingBody(statement.expression)),\n\t\t\t)\n\t\t)\n\treturn false\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ast.mjs","names":["consumerOptions","z","appFile","context","requestedRoot","resolve","root","existsSync","realpathSync","file","relative","filename","sep","imported","node","owner","name","propertyName","def","binding","specifier","exportName","sourceVisitors","visit","text","staticString","source","check","isFunctionsImport","target","dirname","resolveImport","createResolver","typeOnlySource","isDbAccess","isExternalCall","ref","containsComponentReference","property","rootMember","memberPath","isTableChain","isDbWrite","hasCall","metadata","componentRoot","match","scope","variable","exportedNames","names","statement","program","delegatingBody"],"sources":["../../src/oxlint/ast.ts"],"sourcesContent":["import { existsSync, realpathSync } from 'node:fs'\nimport { dirname, relative, resolve, sep } from 'node:path'\nimport type { Context, ESTree, RuleMeta, Scope, Visitor } from '@oxlint/plugins'\nimport { z } from 'zod'\nimport { createResolver, resolveImport } from './architecture'\n\nexport const consumerOptions = z.object({ convexDir: z.string().min(1).default('convex') })\n\nexport function appFile(context: Context, filename = context.filename) {\n\tconst requestedRoot = resolve(\n\t\tcontext.cwd,\n\t\tconsumerOptions.parse(context.options[0] ?? {}).convexDir,\n\t)\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst file = relative(root, existsSync(filename) ? realpathSync(filename) : filename)\n\t\t.split(sep)\n\t\t.join('/')\n\treturn file === '..' || file.startsWith('../') || file.startsWith('/') ? null : file\n}\n\nexport function imported(\n\tcontext: Context,\n\tnode: ESTree.Node,\n): { source: string; name: string } | null {\n\tif (node.type === 'CallExpression') return imported(context, node.callee)\n\tif (node.type === 'MemberExpression') {\n\t\tconst owner = imported(context, node.object)\n\t\tconst name = propertyName(node)\n\t\treturn owner && name && ['*', 'z', 'default'].includes(owner.name)\n\t\t\t? { source: owner.source, name }\n\t\t\t: null\n\t}\n\tif (node.type !== 'Identifier') return null\n\tfor (const def of binding(context, node)?.defs ?? []) {\n\t\tif (\n\t\t\tdef.type !== 'ImportBinding' ||\n\t\t\tdef.parent?.type !== 'ImportDeclaration' ||\n\t\t\tdef.parent.importKind === 'type'\n\t\t)\n\t\t\tcontinue\n\t\tconst specifier = def.node\n\t\tif (specifier.type === 'ImportSpecifier' && specifier.importKind === 'type') continue\n\t\treturn {\n\t\t\tsource: def.parent.source.value,\n\t\t\tname:\n\t\t\t\tspecifier.type === 'ImportSpecifier'\n\t\t\t\t\t? (exportName(specifier.imported) ?? '')\n\t\t\t\t\t: specifier.type === 'ImportDefaultSpecifier'\n\t\t\t\t\t\t? 'default'\n\t\t\t\t\t\t: '*',\n\t\t}\n\t}\n\treturn null\n}\n\nexport function sourceVisitors(\n\tcontext: Context,\n\tcheck: (node: ESTree.Node, source: string) => void,\n): Visitor {\n\tfunction visit(node: ESTree.Node, source: ESTree.Node | null | undefined) {\n\t\tconst text = staticString(source)\n\t\tif (text) check(node, text)\n\t}\n\treturn {\n\t\tImportDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tExportNamedDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tExportAllDeclaration(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tImportExpression(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tTSImportType(node) {\n\t\t\tvisit(node, node.source)\n\t\t},\n\t\tCallExpression(node) {\n\t\t\tif (\n\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\tnode.callee.name === 'require' &&\n\t\t\t\t!binding(context, node.callee)?.defs.length\n\t\t\t)\n\t\t\t\tvisit(node, node.arguments[0])\n\t\t},\n\t}\n}\n\nexport function isFunctionsImport(context: Context, source: string) {\n\tconst target = source.startsWith('.')\n\t\t? resolve(dirname(context.filename), source)\n\t\t: resolveImport(createResolver(), context.filename, source)\n\treturn target !== null && appFile(context, target)?.replace(/\\.[cm]?[jt]s$/, '') === 'functions'\n}\n\nexport function typeOnlySource(node: ESTree.Node) {\n\treturn (\n\t\tnode.type === 'TSImportType' ||\n\t\t(node.type === 'ImportDeclaration' &&\n\t\t\t(node.importKind === 'type' ||\n\t\t\t\t(node.specifiers.length > 0 &&\n\t\t\t\t\tnode.specifiers.every(\n\t\t\t\t\t\t(specifier) => specifier.type === 'ImportSpecifier' && specifier.importKind === 'type',\n\t\t\t\t\t)))) ||\n\t\t((node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') &&\n\t\t\tnode.exportKind === 'type')\n\t)\n}\n\nexport function isDbAccess(node: ESTree.CallExpression) {\n\treturn (\n\t\tnode.callee.type === 'MemberExpression' &&\n\t\tnode.callee.object.type === 'MemberExpression' &&\n\t\tpropertyName(node.callee.object) === 'db'\n\t)\n}\n\nexport function isExternalCall(context: Context, node: ESTree.CallExpression) {\n\tconst ref = imported(context, node.callee)\n\tif (ref && /^(?:node:)?(?:https?|net|tls|fs)(?:\\/|$)|^(?:undici|axios)(?:\\/|$)/.test(ref.source))\n\t\treturn true\n\treturn (\n\t\t(node.callee.type === 'Identifier' &&\n\t\t\tnode.callee.name === 'fetch' &&\n\t\t\t!binding(context, node.callee)?.defs.length) ||\n\t\t(node.callee.type === 'MemberExpression' &&\n\t\t\tpropertyName(node.callee) === 'fetch' &&\n\t\t\tnode.callee.object.type === 'Identifier' &&\n\t\t\tnode.callee.object.name === 'globalThis' &&\n\t\t\t!binding(context, node.callee.object)?.defs.length)\n\t)\n}\n\nexport function containsComponentReference(context: Context, node: ESTree.Node): boolean {\n\tif (node.type === 'MemberExpression') {\n\t\tconst owner = imported(context, node.object)\n\t\tif (owner?.name === 'components' && /_generated\\/api/.test(owner.source)) return true\n\t\treturn containsComponentReference(context, node.object)\n\t}\n\tif (node.type === 'ObjectExpression')\n\t\treturn node.properties.some(\n\t\t\t(property) =>\n\t\t\t\tproperty.type === 'Property' && containsComponentReference(context, property.value),\n\t\t)\n\treturn false\n}\n\nexport function rootMember(node: ESTree.Node): ESTree.Node {\n\treturn node.type === 'MemberExpression' ? rootMember(node.object) : node\n}\n\nexport function memberPath(node: ESTree.Node): string[] {\n\tif (node.type === 'Identifier') return [node.name]\n\tif (node.type === 'MemberExpression')\n\t\treturn [...memberPath(node.object), propertyName(node) ?? '']\n\treturn []\n}\n\nexport function isTableChain(node: ESTree.Node): boolean {\n\tif (node.type === 'MemberExpression')\n\t\treturn propertyName(node) === 'table' || isTableChain(node.object)\n\treturn node.type === 'CallExpression' && isTableChain(node.callee)\n}\n\nexport function isDbWrite(node: ESTree.CallExpression) {\n\treturn (\n\t\tnode.callee.type === 'MemberExpression' &&\n\t\t['insert', 'patch', 'replace', 'delete'].includes(propertyName(node.callee) ?? '') &&\n\t\tnode.callee.object.type === 'MemberExpression' &&\n\t\tpropertyName(node.callee.object) === 'db'\n\t)\n}\n\nexport function hasCall(node: ESTree.Node, name: string): boolean {\n\tif (node.type === 'CallExpression')\n\t\treturn (\n\t\t\t(node.callee.type === 'MemberExpression' &&\n\t\t\t\tpropertyName(node.callee) === name &&\n\t\t\t\t(name !== 'query' || propertyName(node.callee.object) === 'db')) ||\n\t\t\thasCall(node.callee, name)\n\t\t)\n\tif (node.type === 'MemberExpression') return hasCall(node.object, name)\n\treturn false\n}\n\nexport function metadata(description: string, messages: Record<string, string>): RuleMeta {\n\treturn { type: 'problem', docs: { description }, schema: [], messages }\n}\n\nexport function componentRoot(context: Context, filename: string) {\n\tconst requestedRoot = resolve(\n\t\tcontext.cwd,\n\t\tconsumerOptions.parse(context.options[0] ?? {}).convexDir,\n\t)\n\tconst root = existsSync(requestedRoot) ? realpathSync(requestedRoot) : requestedRoot\n\tconst file = relative(root, existsSync(filename) ? realpathSync(filename) : filename)\n\t\t.split(sep)\n\t\t.join('/')\n\tconst match = file.match(/^components\\/([^/]+)(?:\\/|$)/)\n\treturn match ? resolve(root, 'components', match[1]) : null\n}\n\nexport function staticString(node: ESTree.Node | null | undefined) {\n\tif (node?.type === 'Literal' && 'value' in node && String(node.value) === node.value)\n\t\treturn String(node.value)\n\tif (node?.type === 'TemplateLiteral' && node.expressions.length === 0)\n\t\treturn node.quasis[0].value.cooked\n\treturn null\n}\n\nexport function propertyName(node: ESTree.Node): string | null {\n\tif (node.type !== 'MemberExpression') return null\n\treturn node.computed ? staticString(node.property) : node.property.name\n}\n\nexport function binding(context: Context, node: ESTree.Node) {\n\tif (node.type !== 'Identifier') return null\n\tlet scope: Scope | null = context.sourceCode.getScope(node)\n\twhile (scope) {\n\t\tconst variable = scope.set.get(node.name)\n\t\tif (variable) return variable\n\t\tscope = scope.upper\n\t}\n\treturn null\n}\n\nexport function exportedNames(program: ESTree.Program) {\n\tconst names = new Set<string>()\n\tfor (const statement of program.body) {\n\t\tif (statement.type === 'ExportNamedDeclaration' && !statement.source) {\n\t\t\tfor (const specifier of statement.specifiers) names.add(exportName(specifier.local) ?? '')\n\t\t} else if (\n\t\t\tstatement.type === 'ExportDefaultDeclaration' &&\n\t\t\tstatement.declaration.type === 'Identifier'\n\t\t) {\n\t\t\tnames.add(statement.declaration.name)\n\t\t}\n\t}\n\treturn names\n}\n\nexport function exportName(node: ESTree.Node): string | null {\n\treturn node.type === 'Identifier' ? node.name : staticString(node)\n}\n\nexport function delegatingBody(node: ESTree.Node): boolean {\n\tif (node.type === 'CallExpression') return true\n\tif (node.type === 'AwaitExpression') return delegatingBody(node.argument)\n\tif (node.type === 'BlockStatement')\n\t\treturn (\n\t\t\tnode.body.length > 0 &&\n\t\t\tnode.body.every(\n\t\t\t\t(statement) =>\n\t\t\t\t\t(statement.type === 'ReturnStatement' &&\n\t\t\t\t\t\tstatement.argument !== null &&\n\t\t\t\t\t\tdelegatingBody(statement.argument)) ||\n\t\t\t\t\t(statement.type === 'ExpressionStatement' && delegatingBody(statement.expression)),\n\t\t\t)\n\t\t)\n\treturn false\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ESTree } from "@oxlint/plugins";
|
|
2
2
|
//#region src/oxlint/ownership.d.ts
|
|
3
|
-
declare const ownershipRules: {
|
|
3
|
+
export declare const ownershipRules: {
|
|
4
4
|
'project-structure': {
|
|
5
5
|
meta: import("@oxlint/plugins").RuleMeta;
|
|
6
6
|
create(context: import("@oxlint/plugins").Context): {
|
|
@@ -56,7 +56,6 @@ declare const ownershipRules: {
|
|
|
56
56
|
create(context: import("@oxlint/plugins").Context): {};
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
|
-
type OwnershipRuleName = keyof typeof ownershipRules;
|
|
59
|
+
export type OwnershipRuleName = keyof typeof ownershipRules;
|
|
60
60
|
//#endregion
|
|
61
|
-
export { OwnershipRuleName, ownershipRules };
|
|
62
61
|
//# sourceMappingURL=ownership.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{architectureAnchor as e,createResolver as t,inspectArchitecture as n,resolveImport as r}from"./architecture.mjs";import{appFile as i,binding as a,componentRoot as o,consumerOptions as s,containsComponentReference as c,delegatingBody as l,exportedNames as u,imported as d,isDbAccess as f,isDbWrite as p,isExternalCall as m,isFunctionsImport as h,memberPath as g,metadata as _,propertyName as v,rootMember as y,sourceVisitors as b,typeOnlySource as x}from"./ast.mjs";import{basename as S,resolve as C}from"node:path";import{existsSync as w,realpathSync as T}from"node:fs";const E={"project-structure":{meta:_(`Validate the application tree, colocated tests, and module cycles.`,{structure:`{{file}}: {{message}}`}),create(t){let r=s.parse(t.options[0]??{}),i=C(t.cwd,r.convexDir);return(w(t.filename)?T(t.filename):C(t.filename))===e(i)?{Program(e){for(let i of n({cwd:t.cwd,convexDir:r.convexDir}))t.report({node:e,messageId:`structure`,data:{file:i.file,message:i.message}})}}:{}}},"root-wiring-only":{meta:_(`Keep root files limited to configuration, assembly, and component facades.`,{ownership:`Root files only wire configuration and components. Move behavior into domain/<module>/: {{reason}}.`}),create(e){let t=i(e);if(!t||t.includes(`/`))return{};let n=null,r=`business implementation`,a=!1,o=/^(?:convex\.config|auth\.config|vite\.config|vitest\.config|auth|schema|functions|triggers|foundation|http|crons)\.[cm]?[jt]s$/.test(t);function s(e){n??=e}return{Program(e){e.body.some(e=>e.type===`FunctionDeclaration`||e.type===`ExportNamedDeclaration`&&e.declaration?.type===`FunctionDeclaration`)&&s(e)},NewExpression(t){d(e,t.callee)&&t.arguments.some(t=>c(e,t))&&(a=!0)},CallExpression(n){d(e,n.callee)&&n.arguments.some(t=>c(e,t))&&(a=!0),(p(n)||m(e,n)||t===`schema.ts`&&d(e,n.callee)?.name!==`defineSchema`)&&s(n)},ArrowFunctionExpression(e){(!e.body||!l(e.body))&&s(e)},FunctionExpression(e){(!e.body||!l(e.body))&&s(e)},IfStatement:s,SwitchStatement:s,ConditionalExpression:s,ForStatement:s,ForOfStatement:s,WhileStatement:s,"Program:exit"(t){!o&&!a&&(n=t,r=`unknown root file; only declared component facades may add root source files`),n&&e.report({node:n,messageId:`ownership`,data:{reason:r}})}}}},"application-orchestration":{meta:_(`Reserve application/ for thin cross-domain coordination.`,{ownership:`application/ only coordinates two or more domains. Move schemas, providers, storage access, helpers, and business decisions into their owning domain.`}),create(e){if(!i(e)?.startsWith(`application/`))return{};let n
|
|
1
|
+
import{architectureAnchor as e,createResolver as t,inspectArchitecture as n,resolveImport as r}from"./architecture.mjs";import{appFile as i,binding as a,componentRoot as o,consumerOptions as s,containsComponentReference as c,delegatingBody as l,exportedNames as u,imported as d,isDbAccess as f,isDbWrite as p,isExternalCall as m,isFunctionsImport as h,memberPath as g,metadata as _,propertyName as v,rootMember as y,sourceVisitors as b,typeOnlySource as x}from"./ast.mjs";import{basename as S,resolve as C}from"node:path";import{existsSync as w,realpathSync as T}from"node:fs";const E={"project-structure":{meta:_(`Validate the application tree, colocated tests, and module cycles.`,{structure:`{{file}}: {{message}}`}),create(t){let r=s.parse(t.options[0]??{}),i=C(t.cwd,r.convexDir);return(w(t.filename)?T(t.filename):C(t.filename))===e(i)?{Program(e){for(let i of n({cwd:t.cwd,convexDir:r.convexDir}))t.report({node:e,messageId:`structure`,data:{file:i.file,message:i.message}})}}:{}}},"root-wiring-only":{meta:_(`Keep root files limited to configuration, assembly, and component facades.`,{ownership:`Root files only wire configuration and components. Move behavior into domain/<module>/: {{reason}}.`}),create(e){let t=i(e);if(!t||t.includes(`/`))return{};let n=null,r=`business implementation`,a=!1,o=/^(?:convex\.config|auth\.config|vite\.config|vitest\.config|auth|schema|functions|triggers|foundation|http|crons)\.[cm]?[jt]s$/.test(t);function s(e){n??=e}return{Program(e){e.body.some(e=>e.type===`FunctionDeclaration`||e.type===`ExportNamedDeclaration`&&e.declaration?.type===`FunctionDeclaration`)&&s(e)},NewExpression(t){d(e,t.callee)&&t.arguments.some(t=>c(e,t))&&(a=!0)},CallExpression(n){d(e,n.callee)&&n.arguments.some(t=>c(e,t))&&(a=!0),(p(n)||m(e,n)||t===`schema.ts`&&d(e,n.callee)?.name!==`defineSchema`)&&s(n)},ArrowFunctionExpression(e){(!e.body||!l(e.body))&&s(e)},FunctionExpression(e){(!e.body||!l(e.body))&&s(e)},IfStatement:s,SwitchStatement:s,ConditionalExpression:s,ForStatement:s,ForOfStatement:s,WhileStatement:s,"Program:exit"(t){!o&&!a&&(n=t,r=`unknown root file; only declared component facades may add root source files`),n&&e.report({node:n,messageId:`ownership`,data:{reason:r}})}}}},"application-orchestration":{meta:_(`Reserve application/ for thin cross-domain coordination.`,{ownership:`application/ only coordinates two or more domains. Move schemas, providers, storage access, helpers, and business decisions into their owning domain.`}),create(e){if(!i(e)?.startsWith(`application/`))return{};let n=/* @__PURE__ */ new Set,a=t(),o=null;function s(e){o??=e}let c=b(e,(t,o)=>{if(x(t))return;let c=r(a,e.filename,o),l=c&&i(e,c),u=l?.match(/^domain\/([^/]+)\/(?:commands|queries|rules)(?:\.[cm]?[jt]s)?$/);u&&u[1]!==`shared`&&n.add(u[1]),l?.startsWith(`domain/`)&&!u&&!l.startsWith(`domain/shared/`)&&s(t),!l&&![`cvx-kit`,`cvx-kit/components/foundation`,`@convex-dev/workflow`,`convex/values`].includes(o)&&s(t)});return{...c,Program(e){let t=u(e);for(let n of e.body){n.type===`FunctionDeclaration`&&n.id&&!t.has(n.id.name)&&s(n);let e=n.type===`ExportNamedDeclaration`?n.declaration:n;if((e?.type===`TSTypeAliasDeclaration`||e?.type===`TSInterfaceDeclaration`)&&s(n),e?.type===`VariableDeclaration`)for(let r of e.declarations)r.init&&[`ObjectExpression`,`ArrayExpression`,`Literal`].includes(r.init.type)&&s(r),n.type===`VariableDeclaration`&&r.id.type===`Identifier`&&!t.has(r.id.name)&&[`ArrowFunctionExpression`,`FunctionExpression`].includes(r.init?.type??``)&&s(r)}},CallExpression(t){c.CallExpression?.(t),(f(t)||m(e,t))&&s(t);let n=d(e,t.callee);n&&[`zod`,`zod/v4`].includes(n.source)&&s(t)},MemberExpression(t){let r=g(t);if(r.length>=4&&r[1]===`domain`){let i=d(e,y(t));i?.name===`internal`&&/_generated\/api/.test(i.source)&&n.add(r[2])}},NewExpression:s,IfStatement:s,SwitchStatement:s,ConditionalExpression:s,"Program:exit"(t){(n.size<2||o)&&e.report({node:o??t,messageId:`ownership`})}}}},"internal-function-ownership":{meta:_(`Place internal handlers with their owner, never in root, api/, or shared/.`,{ownership:`Declare system/internal handlers in domain/<module>/, cross-domain application/, migrations/, or an isolated component.`}),create(e){let t=i(e)??``;return o(e,e.filename)||/^(?:domain\/(?!shared\/)[^/]+\/|application\/|migrations\/)/.test(t)?{}:{CallExpression(t){if(t.callee.type===`CallExpression`)return;let n=d(e,t.callee);n&&(h(e,n.source)&&/^system(Query|Mutation|Action)$/.test(n.name)||/(?:_generated\/server|convex\/server)/.test(n.source)&&/^internal(Query|Mutation|Action)(Generic)?$/.test(n.name))&&e.report({node:t,messageId:`ownership`})}}}},"domain-file-responsibilities":{meta:_(`Keep invariants pure, reads read-only, shapes declarative, and I/O action-side.`,{ownership:`Respect this domain file responsibility: pure rules, read-only queries, declarative schemas/tables/constants, and external I/O only in actions or integrations.`}),create(e){let n=i(e)??``,o=n===`domain/table.ts`;if(!o&&!/^domain\/[^/]+\//.test(n))return{};let s=S(n).replace(/\.[cm]?[jt]s$/,``),c=s===`rules`,l=s===`queries`,u=[`schema`,`table`,`constants`].includes(s),g=/(?:^|\/)(?:actions|integrations|providers)(?:\/|\.)/.test(n)||/(?:^|_)actions\./.test(n),_=t(),y=null;function C(e){y??=e}let w=b(e,(t,n)=>{if(x(t))return;let a=r(_,e.filename,n),s=a&&i(e,a);o&&n!==`cvx-kit/zod-table`&&!/^domain\/[^/]+\/table(?:\.[cm]?[jt]s)?$/.test(s??``)&&C(t),c&&s&&!/(?:^|\/)(?:rules|constants)(?:\.[cm]?[jt]s)?$/.test(s)&&C(t),(c||l||u)&&!s&&![`zod`,`zod/v4`,`convex/values`,`cvx-kit`,`cvx-kit/zod-table`,`cvx-kit/errors`].includes(n)&&C(t),l&&s&&/(?:^|\/)(?:commands|actions|integrations|providers)(?:[./]|$)/.test(s)&&C(t)});return{...w,Program(e){if(o)for(let t of e.body){if(t.type===`ImportDeclaration`||t.type===`EmptyStatement`)continue;let e=t.type===`ExportNamedDeclaration`?t.declaration:t;(e?.type!==`VariableDeclaration`||e.declarations.some(e=>e.init?.type!==`CallExpression`))&&C(t)}},CallExpression(t){if(w.CallExpression?.(t),o){let n=d(e,t.callee);(n?.source!==`cvx-kit/zod-table`||n.name!==`createModule`)&&C(t)}let n=t.callee.type===`MemberExpression`?v(t.callee):null;(c||u)&&(f(t)||[`runQuery`,`runMutation`,`runAction`,`runAfter`,`runAt`].includes(n??``))&&C(t),l&&(p(t)||[`runMutation`,`runAction`,`runAfter`,`runAt`].includes(n??``))&&C(t);let r=d(e,t.callee);r&&h(e,r.source)&&(c||u||l&&/(?:Mutation|Action)$/.test(r.name))&&C(t),l&&t.callee.type===`MemberExpression`&&v(t.callee.object)===`storage`&&[`store`,`delete`,`generateUploadUrl`].includes(n??``)&&C(t),!g&&m(e,t)&&C(t),c&&t.callee.type===`MemberExpression`&&t.callee.object.type===`Identifier`&&!a(e,t.callee.object)?.defs.length&&(t.callee.object.name===`Date`&&n===`now`||t.callee.object.name===`Math`&&n===`random`)&&C(t)},MemberExpression(e){c&&[`db`,`storage`,`scheduler`,`env`].includes(v(e)??``)&&C(e)},NewExpression(t){(c||l||u)&&(t.callee.type!==`Identifier`||a(e,t.callee)?.defs.length||![`Error`,`TypeError`,`RangeError`,`Set`,`Map`,`RegExp`].includes(t.callee.name))&&C(t)},"Program:exit"(){y&&e.report({node:y,messageId:`ownership`})}}}}};export{E as ownershipRules};
|
|
2
2
|
//# sourceMappingURL=ownership.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ownership.mjs","names":[],"sources":["../../src/oxlint/ownership.ts"],"sourcesContent":["import { existsSync, realpathSync } from 'node:fs'\nimport { basename, resolve } from 'node:path'\nimport type { CreateRule, ESTree } from '@oxlint/plugins'\nimport {\n\tarchitectureAnchor,\n\tinspectArchitecture,\n\tcreateResolver,\n\tresolveImport,\n} from './architecture'\nimport {\n\tconsumerOptions,\n\tappFile,\n\timported,\n\tsourceVisitors,\n\tisFunctionsImport,\n\ttypeOnlySource,\n\tisDbAccess,\n\tisExternalCall,\n\tcontainsComponentReference,\n\trootMember,\n\tmemberPath,\n\tisDbWrite,\n\tmetadata,\n\tcomponentRoot,\n\tpropertyName,\n\tbinding,\n\texportedNames,\n\tdelegatingBody,\n} from './ast'\n\nexport const ownershipRules = {\n\t'project-structure': {\n\t\tmeta: metadata('Validate the application tree, colocated tests, and module cycles.', {\n\t\t\tstructure: '{{file}}: {{message}}',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst settings = consumerOptions.parse(context.options[0] ?? {})\n\t\t\tconst root = resolve(context.cwd, settings.convexDir)\n\t\t\tif (\n\t\t\t\t(existsSync(context.filename)\n\t\t\t\t\t? realpathSync(context.filename)\n\t\t\t\t\t: resolve(context.filename)) !== architectureAnchor(root)\n\t\t\t)\n\t\t\t\treturn {}\n\t\t\treturn {\n\t\t\t\tProgram(node) {\n\t\t\t\t\tfor (const issue of inspectArchitecture({\n\t\t\t\t\t\tcwd: context.cwd,\n\t\t\t\t\t\tconvexDir: settings.convexDir,\n\t\t\t\t\t}))\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'structure',\n\t\t\t\t\t\t\tdata: { file: issue.file, message: issue.message },\n\t\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'root-wiring-only': {\n\t\tmeta: metadata('Keep root files limited to configuration, assembly, and component facades.', {\n\t\t\townership:\n\t\t\t\t'Root files only wire configuration and components. Move behavior into domain/<module>/: {{reason}}.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context)\n\t\t\tif (!file || file.includes('/')) return {}\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tlet reason = 'business implementation'\n\t\t\tlet componentFacade = false\n\t\t\tconst standard =\n\t\t\t\t/^(?:convex\\.config|auth\\.config|vite\\.config|vitest\\.config|auth|schema|functions|triggers|foundation|http|crons)\\.[cm]?[jt]s$/.test(\n\t\t\t\t\tfile,\n\t\t\t\t)\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tProgram(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tnode.body.some(\n\t\t\t\t\t\t\t(statement) =>\n\t\t\t\t\t\t\t\tstatement.type === 'FunctionDeclaration' ||\n\t\t\t\t\t\t\t\t(statement.type === 'ExportNamedDeclaration' &&\n\t\t\t\t\t\t\t\t\tstatement.declaration?.type === 'FunctionDeclaration'),\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tNewExpression(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\timported(context, node.callee) &&\n\t\t\t\t\t\tnode.arguments.some((argument) => containsComponentReference(context, argument))\n\t\t\t\t\t)\n\t\t\t\t\t\tcomponentFacade = true\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\timported(context, node.callee) &&\n\t\t\t\t\t\tnode.arguments.some((argument) => containsComponentReference(context, argument))\n\t\t\t\t\t)\n\t\t\t\t\t\tcomponentFacade = true\n\t\t\t\t\tif (\n\t\t\t\t\t\tisDbWrite(node) ||\n\t\t\t\t\t\tisExternalCall(context, node) ||\n\t\t\t\t\t\t(file === 'schema.ts' && !(imported(context, node.callee)?.name === 'defineSchema'))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tArrowFunctionExpression(node) {\n\t\t\t\t\tif (!node.body || !delegatingBody(node.body)) flag(node)\n\t\t\t\t},\n\t\t\t\tFunctionExpression(node) {\n\t\t\t\t\tif (!node.body || !delegatingBody(node.body)) flag(node)\n\t\t\t\t},\n\t\t\t\tIfStatement: flag,\n\t\t\t\tSwitchStatement: flag,\n\t\t\t\tConditionalExpression: flag,\n\t\t\t\tForStatement: flag,\n\t\t\t\tForOfStatement: flag,\n\t\t\t\tWhileStatement: flag,\n\t\t\t\t'Program:exit'(node) {\n\t\t\t\t\tif (!standard && !componentFacade) {\n\t\t\t\t\t\tissue = node\n\t\t\t\t\t\treason = 'unknown root file; only declared component facades may add root source files'\n\t\t\t\t\t}\n\t\t\t\t\tif (issue) context.report({ node: issue, messageId: 'ownership', data: { reason } })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'application-orchestration': {\n\t\tmeta: metadata('Reserve application/ for thin cross-domain coordination.', {\n\t\t\townership:\n\t\t\t\t'application/ only coordinates two or more domains. Move schemas, providers, storage access, helpers, and business decisions into their owning domain.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tif (!appFile(context)?.startsWith('application/')) return {}\n\t\t\tconst owners = new Set<string>()\n\t\t\tconst resolver = createResolver()\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\tconst sources = sourceVisitors(context, (node, source) => {\n\t\t\t\tif (typeOnlySource(node)) return\n\t\t\t\tconst target = resolveImport(resolver, context.filename, source)\n\t\t\t\tconst path = target && appFile(context, target)\n\t\t\t\tconst match = path?.match(/^domain\\/([^/]+)\\/(?:commands|queries|rules)(?:\\.[cm]?[jt]s)?$/)\n\t\t\t\tif (match && match[1] !== 'shared') owners.add(match[1])\n\t\t\t\tif (path?.startsWith('domain/') && !match && !path.startsWith('domain/shared/')) flag(node)\n\t\t\t\tif (\n\t\t\t\t\t!path &&\n\t\t\t\t\t![\n\t\t\t\t\t\t'cvx-kit',\n\t\t\t\t\t\t'cvx-kit/components/foundation',\n\t\t\t\t\t\t'@convex-dev/workflow',\n\t\t\t\t\t\t'convex/values',\n\t\t\t\t\t].includes(source)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t})\n\t\t\treturn {\n\t\t\t\t...sources,\n\t\t\t\tProgram(node) {\n\t\t\t\t\tconst publicNames = exportedNames(node)\n\t\t\t\t\tfor (const statement of node.body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tstatement.type === 'FunctionDeclaration' &&\n\t\t\t\t\t\t\tstatement.id &&\n\t\t\t\t\t\t\t!publicNames.has(statement.id.name)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t\tconst declaration =\n\t\t\t\t\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdeclaration?.type === 'TSTypeAliasDeclaration' ||\n\t\t\t\t\t\t\tdeclaration?.type === 'TSInterfaceDeclaration'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t\tif (declaration?.type === 'VariableDeclaration')\n\t\t\t\t\t\t\tfor (const item of declaration.declarations) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\titem.init &&\n\t\t\t\t\t\t\t\t\t['ObjectExpression', 'ArrayExpression', 'Literal'].includes(item.init.type)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tflag(item)\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tstatement.type === 'VariableDeclaration' &&\n\t\t\t\t\t\t\t\t\titem.id.type === 'Identifier' &&\n\t\t\t\t\t\t\t\t\t!publicNames.has(item.id.name) &&\n\t\t\t\t\t\t\t\t\t['ArrowFunctionExpression', 'FunctionExpression'].includes(item.init?.type ?? '')\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tflag(item)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tsources.CallExpression?.(node)\n\t\t\t\t\tif (isDbAccess(node) || isExternalCall(context, node)) flag(node)\n\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\tif (ref && ['zod', 'zod/v4'].includes(ref.source)) flag(node)\n\t\t\t\t},\n\t\t\t\tMemberExpression(node) {\n\t\t\t\t\tconst path = memberPath(node)\n\t\t\t\t\tif (path.length >= 4 && path[1] === 'domain') {\n\t\t\t\t\t\tconst ref = imported(context, rootMember(node))\n\t\t\t\t\t\tif (ref?.name === 'internal' && /_generated\\/api/.test(ref.source)) owners.add(path[2])\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tNewExpression: flag,\n\t\t\t\tIfStatement: flag,\n\t\t\t\tSwitchStatement: flag,\n\t\t\t\tConditionalExpression: flag,\n\t\t\t\t'Program:exit'(node) {\n\t\t\t\t\tif (owners.size < 2 || issue)\n\t\t\t\t\t\tcontext.report({ node: issue ?? node, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'internal-function-ownership': {\n\t\tmeta: metadata('Place internal handlers with their owner, never in root, api/, or shared/.', {\n\t\t\townership:\n\t\t\t\t'Declare system/internal handlers in domain/<module>/, cross-domain application/, migrations/, or an isolated component.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context) ?? ''\n\t\t\tif (\n\t\t\t\tcomponentRoot(context, context.filename) ||\n\t\t\t\t/^(?:domain\\/(?!shared\\/)[^/]+\\/|application\\/|migrations\\/)/.test(file)\n\t\t\t)\n\t\t\t\treturn {}\n\t\t\treturn {\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tif (node.callee.type === 'CallExpression') return\n\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\tif (\n\t\t\t\t\t\tref &&\n\t\t\t\t\t\t((isFunctionsImport(context, ref.source) &&\n\t\t\t\t\t\t\t/^system(Query|Mutation|Action)$/.test(ref.name)) ||\n\t\t\t\t\t\t\t(/(?:_generated\\/server|convex\\/server)/.test(ref.source) &&\n\t\t\t\t\t\t\t\t/^internal(Query|Mutation|Action)(Generic)?$/.test(ref.name)))\n\t\t\t\t\t)\n\t\t\t\t\t\tcontext.report({ node, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'domain-file-responsibilities': {\n\t\tmeta: metadata(\n\t\t\t'Keep invariants pure, reads read-only, shapes declarative, and I/O action-side.',\n\t\t\t{\n\t\t\t\townership:\n\t\t\t\t\t'Respect this domain file responsibility: pure rules, read-only queries, declarative schemas/tables/constants, and external I/O only in actions or integrations.',\n\t\t\t},\n\t\t),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context) ?? ''\n\t\t\tconst assembly = file === 'domain/table.ts'\n\t\t\tif (!assembly && !/^domain\\/[^/]+\\//.test(file)) return {}\n\t\t\tconst role = basename(file).replace(/\\.[cm]?[jt]s$/, '')\n\t\t\tconst pure = role === 'rules'\n\t\t\tconst read = role === 'queries'\n\t\t\tconst declarative = ['schema', 'table', 'constants'].includes(role)\n\t\t\tconst actionSide =\n\t\t\t\t/(?:^|\\/)(?:actions|integrations|providers)(?:\\/|\\.)/.test(file) ||\n\t\t\t\t/(?:^|_)actions\\./.test(file)\n\t\t\tconst resolver = createResolver()\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\tconst sources = sourceVisitors(context, (node, source) => {\n\t\t\t\tif (typeOnlySource(node)) return\n\t\t\t\tconst target = resolveImport(resolver, context.filename, source)\n\t\t\t\tconst path = target && appFile(context, target)\n\t\t\t\tif (\n\t\t\t\t\tassembly &&\n\t\t\t\t\tsource !== 'cvx-kit/zod-table' &&\n\t\t\t\t\t!/^domain\\/[^/]+\\/table(?:\\.[cm]?[jt]s)?$/.test(path ?? '')\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t\tif (pure && path && !/(?:^|\\/)(?:rules|constants)(?:\\.[cm]?[jt]s)?$/.test(path)) flag(node)\n\t\t\t\tif (\n\t\t\t\t\t(pure || read || declarative) &&\n\t\t\t\t\t!path &&\n\t\t\t\t\t![\n\t\t\t\t\t\t'zod',\n\t\t\t\t\t\t'zod/v4',\n\t\t\t\t\t\t'convex/values',\n\t\t\t\t\t\t'cvx-kit',\n\t\t\t\t\t\t'cvx-kit/zod-table',\n\t\t\t\t\t\t'cvx-kit/errors',\n\t\t\t\t\t].includes(source)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t\tif (\n\t\t\t\t\tread &&\n\t\t\t\t\tpath &&\n\t\t\t\t\t/(?:^|\\/)(?:commands|actions|integrations|providers)(?:[./]|$)/.test(path)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t})\n\t\t\treturn {\n\t\t\t\t...sources,\n\t\t\t\tProgram(node) {\n\t\t\t\t\tif (!assembly) return\n\t\t\t\t\tfor (const statement of node.body) {\n\t\t\t\t\t\tif (statement.type === 'ImportDeclaration' || statement.type === 'EmptyStatement')\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\tconst declaration =\n\t\t\t\t\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdeclaration?.type !== 'VariableDeclaration' ||\n\t\t\t\t\t\t\tdeclaration.declarations.some((item) => item.init?.type !== 'CallExpression')\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tsources.CallExpression?.(node)\n\t\t\t\t\tif (assembly) {\n\t\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\t\tif (ref?.source !== 'cvx-kit/zod-table' || ref.name !== 'createModule') flag(node)\n\t\t\t\t\t}\n\t\t\t\t\tconst method = node.callee.type === 'MemberExpression' ? propertyName(node.callee) : null\n\t\t\t\t\tif (\n\t\t\t\t\t\t(pure || declarative) &&\n\t\t\t\t\t\t(isDbAccess(node) ||\n\t\t\t\t\t\t\t['runQuery', 'runMutation', 'runAction', 'runAfter', 'runAt'].includes(method ?? ''))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tread &&\n\t\t\t\t\t\t(isDbWrite(node) ||\n\t\t\t\t\t\t\t['runMutation', 'runAction', 'runAfter', 'runAt'].includes(method ?? ''))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tconst constructor = imported(context, node.callee)\n\t\t\t\t\tif (\n\t\t\t\t\t\tconstructor &&\n\t\t\t\t\t\tisFunctionsImport(context, constructor.source) &&\n\t\t\t\t\t\t(pure || declarative || (read && /(?:Mutation|Action)$/.test(constructor.name)))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tread &&\n\t\t\t\t\t\tnode.callee.type === 'MemberExpression' &&\n\t\t\t\t\t\tpropertyName(node.callee.object) === 'storage' &&\n\t\t\t\t\t\t['store', 'delete', 'generateUploadUrl'].includes(method ?? '')\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (!actionSide && isExternalCall(context, node)) flag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tpure &&\n\t\t\t\t\t\tnode.callee.type === 'MemberExpression' &&\n\t\t\t\t\t\tnode.callee.object.type === 'Identifier' &&\n\t\t\t\t\t\t!binding(context, node.callee.object)?.defs.length &&\n\t\t\t\t\t\t((node.callee.object.name === 'Date' && method === 'now') ||\n\t\t\t\t\t\t\t(node.callee.object.name === 'Math' && method === 'random'))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tMemberExpression(node) {\n\t\t\t\t\tif (pure && ['db', 'storage', 'scheduler', 'env'].includes(propertyName(node) ?? ''))\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tNewExpression(node) {\n\t\t\t\t\tif (!(pure || read || declarative)) return\n\t\t\t\t\tif (\n\t\t\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\t\t\t!binding(context, node.callee)?.defs.length &&\n\t\t\t\t\t\t['Error', 'TypeError', 'RangeError', 'Set', 'Map', 'RegExp'].includes(node.callee.name)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\t'Program:exit'() {\n\t\t\t\t\tif (issue) context.report({ node: issue, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n} satisfies Record<string, CreateRule>\n\nexport type OwnershipRuleName = keyof typeof ownershipRules\n"],"mappings":"ikBA8BA,MAAa,EAAiB,CAC7B,oBAAqB,CACpB,KAAM,EAAS,qEAAsE,CACpF,UAAW,uBACZ,CAAC,EACD,OAAO,EAAS,CACf,IAAM,EAAW,EAAgB,MAAM,EAAQ,QAAQ,IAAM,CAAC,CAAC,EACzD,EAAO,EAAQ,EAAQ,IAAK,EAAS,SAAS,EAOpD,OALE,EAAW,EAAQ,QAAQ,EACzB,EAAa,EAAQ,QAAQ,EAC7B,EAAQ,EAAQ,QAAQ,KAAO,EAAmB,CAAI,EAGnD,CACN,QAAQ,EAAM,CACb,IAAK,IAAM,KAAS,EAAoB,CACvC,IAAK,EAAQ,IACb,UAAW,EAAS,SACrB,CAAC,EACA,EAAQ,OAAO,CACd,OACA,UAAW,YACX,KAAM,CAAE,KAAM,EAAM,KAAM,QAAS,EAAM,OAAQ,CAClD,CAAC,CACH,CACD,EAbQ,CAAC,CAcV,CACD,EACA,mBAAoB,CACnB,KAAM,EAAS,6EAA8E,CAC5F,UACC,qGACF,CAAC,EACD,OAAO,EAAS,CACf,IAAM,EAAO,EAAQ,CAAO,EAC5B,GAAI,CAAC,GAAQ,EAAK,SAAS,GAAG,EAAG,MAAO,CAAC,EACzC,IAAI,EAA4B,KAC5B,EAAS,0BACT,EAAkB,GAChB,EACL,iIAAiI,KAChI,CACD,EACD,SAAS,EAAK,EAAmB,CAChC,IAAU,CACX,CACA,MAAO,CACN,QAAQ,EAAM,CAEZ,EAAK,KAAK,KACR,GACA,EAAU,OAAS,uBAClB,EAAU,OAAS,0BACnB,EAAU,aAAa,OAAS,qBACnC,GAEA,EAAK,CAAI,CACX,EACA,cAAc,EAAM,CAElB,EAAS,EAAS,EAAK,MAAM,GAC7B,EAAK,UAAU,KAAM,GAAa,EAA2B,EAAS,CAAQ,CAAC,IAE/E,EAAkB,GACpB,EACA,eAAe,EAAM,CAEnB,EAAS,EAAS,EAAK,MAAM,GAC7B,EAAK,UAAU,KAAM,GAAa,EAA2B,EAAS,CAAQ,CAAC,IAE/E,EAAkB,KAElB,EAAU,CAAI,GACd,EAAe,EAAS,CAAI,GAC3B,IAAS,aAAiB,EAAS,EAAS,EAAK,MAAM,CAAC,EAAE,OAAS,iBAEpE,EAAK,CAAI,CACX,EACA,wBAAwB,EAAM,EACzB,CAAC,EAAK,MAAQ,CAAC,EAAe,EAAK,IAAI,IAAG,EAAK,CAAI,CACxD,EACA,mBAAmB,EAAM,EACpB,CAAC,EAAK,MAAQ,CAAC,EAAe,EAAK,IAAI,IAAG,EAAK,CAAI,CACxD,EACA,YAAa,EACb,gBAAiB,EACjB,sBAAuB,EACvB,aAAc,EACd,eAAgB,EAChB,eAAgB,EAChB,eAAe,EAAM,CAChB,CAAC,GAAY,CAAC,IACjB,EAAQ,EACR,EAAS,gFAEN,GAAO,EAAQ,OAAO,CAAE,KAAM,EAAO,UAAW,YAAa,KAAM,CAAE,QAAO,CAAE,CAAC,CACpF,CACD,CACD,CACD,EACA,4BAA6B,CAC5B,KAAM,EAAS,2DAA4D,CAC1E,UACC,uJACF,CAAC,EACD,OAAO,EAAS,CACf,GAAI,CAAC,EAAQ,CAAO,CAAC,EAAE,WAAW,cAAc,EAAG,MAAO,CAAC,EAC3D,IAAM,EAAS,IAAI,IACb,EAAW,EAAe,EAC5B,EAA4B,KAChC,SAAS,EAAK,EAAmB,CAChC,IAAU,CACX,CACA,IAAM,EAAU,EAAe,GAAU,EAAM,IAAW,CACzD,GAAI,EAAe,CAAI,EAAG,OAC1B,IAAM,EAAS,EAAc,EAAU,EAAQ,SAAU,CAAM,EACzD,EAAO,GAAU,EAAQ,EAAS,CAAM,EACxC,EAAQ,GAAM,MAAM,gEAAgE,EACtF,GAAS,EAAM,KAAO,UAAU,EAAO,IAAI,EAAM,EAAE,EACnD,GAAM,WAAW,SAAS,GAAK,CAAC,GAAS,CAAC,EAAK,WAAW,gBAAgB,GAAG,EAAK,CAAI,EAEzF,CAAC,GACD,CAAC,CACA,UACA,gCACA,uBACA,eACD,CAAC,CAAC,SAAS,CAAM,GAEjB,EAAK,CAAI,CACX,CAAC,EACD,MAAO,CACN,GAAG,EACH,QAAQ,EAAM,CACb,IAAM,EAAc,EAAc,CAAI,EACtC,IAAK,IAAM,KAAa,EAAK,KAAM,CAEjC,EAAU,OAAS,uBACnB,EAAU,IACV,CAAC,EAAY,IAAI,EAAU,GAAG,IAAI,GAElC,EAAK,CAAS,EACf,IAAM,EACL,EAAU,OAAS,yBAA2B,EAAU,YAAc,EAMvE,IAJC,GAAa,OAAS,0BACtB,GAAa,OAAS,2BAEtB,EAAK,CAAS,EACX,GAAa,OAAS,sBACzB,IAAK,IAAM,KAAQ,EAAY,aAE7B,EAAK,MACL,CAAC,mBAAoB,kBAAmB,SAAS,CAAC,CAAC,SAAS,EAAK,KAAK,IAAI,GAE1E,EAAK,CAAI,EAET,EAAU,OAAS,uBACnB,EAAK,GAAG,OAAS,cACjB,CAAC,EAAY,IAAI,EAAK,GAAG,IAAI,GAC7B,CAAC,0BAA2B,oBAAoB,CAAC,CAAC,SAAS,EAAK,MAAM,MAAQ,EAAE,GAEhF,EAAK,CAAI,CAEb,CACD,EACA,eAAe,EAAM,CACpB,EAAQ,iBAAiB,CAAI,GACzB,EAAW,CAAI,GAAK,EAAe,EAAS,CAAI,IAAG,EAAK,CAAI,EAChE,IAAM,EAAM,EAAS,EAAS,EAAK,MAAM,EACrC,GAAO,CAAC,MAAO,QAAQ,CAAC,CAAC,SAAS,EAAI,MAAM,GAAG,EAAK,CAAI,CAC7D,EACA,iBAAiB,EAAM,CACtB,IAAM,EAAO,EAAW,CAAI,EAC5B,GAAI,EAAK,QAAU,GAAK,EAAK,KAAO,SAAU,CAC7C,IAAM,EAAM,EAAS,EAAS,EAAW,CAAI,CAAC,EAC1C,GAAK,OAAS,YAAc,kBAAkB,KAAK,EAAI,MAAM,GAAG,EAAO,IAAI,EAAK,EAAE,CACvF,CACD,EACA,cAAe,EACf,YAAa,EACb,gBAAiB,EACjB,sBAAuB,EACvB,eAAe,EAAM,EAChB,EAAO,KAAO,GAAK,IACtB,EAAQ,OAAO,CAAE,KAAM,GAAS,EAAM,UAAW,WAAY,CAAC,CAChE,CACD,CACD,CACD,EACA,8BAA+B,CAC9B,KAAM,EAAS,6EAA8E,CAC5F,UACC,yHACF,CAAC,EACD,OAAO,EAAS,CACf,IAAM,EAAO,EAAQ,CAAO,GAAK,GAMjC,OAJC,EAAc,EAAS,EAAQ,QAAQ,GACvC,8DAA8D,KAAK,CAAI,EAEhE,CAAC,EACF,CACN,eAAe,EAAM,CACpB,GAAI,EAAK,OAAO,OAAS,iBAAkB,OAC3C,IAAM,EAAM,EAAS,EAAS,EAAK,MAAM,EAExC,IACE,EAAkB,EAAS,EAAI,MAAM,GACtC,kCAAkC,KAAK,EAAI,IAAI,GAC9C,wCAAwC,KAAK,EAAI,MAAM,GACvD,8CAA8C,KAAK,EAAI,IAAI,IAE7D,EAAQ,OAAO,CAAE,OAAM,UAAW,WAAY,CAAC,CACjD,CACD,CACD,CACD,EACA,+BAAgC,CAC/B,KAAM,EACL,kFACA,CACC,UACC,iKACF,CACD,EACA,OAAO,EAAS,CACf,IAAM,EAAO,EAAQ,CAAO,GAAK,GAC3B,EAAW,IAAS,kBAC1B,GAAI,CAAC,GAAY,CAAC,mBAAmB,KAAK,CAAI,EAAG,MAAO,CAAC,EACzD,IAAM,EAAO,EAAS,CAAI,CAAC,CAAC,QAAQ,gBAAiB,EAAE,EACjD,EAAO,IAAS,QAChB,EAAO,IAAS,UAChB,EAAc,CAAC,SAAU,QAAS,WAAW,CAAC,CAAC,SAAS,CAAI,EAC5D,EACL,sDAAsD,KAAK,CAAI,GAC/D,mBAAmB,KAAK,CAAI,EACvB,EAAW,EAAe,EAC5B,EAA4B,KAChC,SAAS,EAAK,EAAmB,CAChC,IAAU,CACX,CACA,IAAM,EAAU,EAAe,GAAU,EAAM,IAAW,CACzD,GAAI,EAAe,CAAI,EAAG,OAC1B,IAAM,EAAS,EAAc,EAAU,EAAQ,SAAU,CAAM,EACzD,EAAO,GAAU,EAAQ,EAAS,CAAM,EAE7C,GACA,IAAW,qBACX,CAAC,0CAA0C,KAAK,GAAQ,EAAE,GAE1D,EAAK,CAAI,EACN,GAAQ,GAAQ,CAAC,gDAAgD,KAAK,CAAI,GAAG,EAAK,CAAI,GAExF,GAAQ,GAAQ,IACjB,CAAC,GACD,CAAC,CACA,MACA,SACA,gBACA,UACA,oBACA,gBACD,CAAC,CAAC,SAAS,CAAM,GAEjB,EAAK,CAAI,EAET,GACA,GACA,gEAAgE,KAAK,CAAI,GAEzE,EAAK,CAAI,CACX,CAAC,EACD,MAAO,CACN,GAAG,EACH,QAAQ,EAAM,CACR,KACL,IAAK,IAAM,KAAa,EAAK,KAAM,CAClC,GAAI,EAAU,OAAS,qBAAuB,EAAU,OAAS,iBAChE,SACD,IAAM,EACL,EAAU,OAAS,yBAA2B,EAAU,YAAc,GAEtE,GAAa,OAAS,uBACtB,EAAY,aAAa,KAAM,GAAS,EAAK,MAAM,OAAS,gBAAgB,IAE5E,EAAK,CAAS,CAChB,CACD,EACA,eAAe,EAAM,CAEpB,GADA,EAAQ,iBAAiB,CAAI,EACzB,EAAU,CACb,IAAM,EAAM,EAAS,EAAS,EAAK,MAAM,GACrC,GAAK,SAAW,qBAAuB,EAAI,OAAS,iBAAgB,EAAK,CAAI,CAClF,CACA,IAAM,EAAS,EAAK,OAAO,OAAS,mBAAqB,EAAa,EAAK,MAAM,EAAI,MAEnF,GAAQ,KACR,EAAW,CAAI,GACf,CAAC,WAAY,cAAe,YAAa,WAAY,OAAO,CAAC,CAAC,SAAS,GAAU,EAAE,IAEpF,EAAK,CAAI,EAET,IACC,EAAU,CAAI,GACd,CAAC,cAAe,YAAa,WAAY,OAAO,CAAC,CAAC,SAAS,GAAU,EAAE,IAExE,EAAK,CAAI,EACV,IAAM,EAAc,EAAS,EAAS,EAAK,MAAM,EAEhD,GACA,EAAkB,EAAS,EAAY,MAAM,IAC5C,GAAQ,GAAgB,GAAQ,uBAAuB,KAAK,EAAY,IAAI,IAE7E,EAAK,CAAI,EAET,GACA,EAAK,OAAO,OAAS,oBACrB,EAAa,EAAK,OAAO,MAAM,IAAM,WACrC,CAAC,QAAS,SAAU,mBAAmB,CAAC,CAAC,SAAS,GAAU,EAAE,GAE9D,EAAK,CAAI,EACN,CAAC,GAAc,EAAe,EAAS,CAAI,GAAG,EAAK,CAAI,EAE1D,GACA,EAAK,OAAO,OAAS,oBACrB,EAAK,OAAO,OAAO,OAAS,cAC5B,CAAC,EAAQ,EAAS,EAAK,OAAO,MAAM,CAAC,EAAE,KAAK,SAC1C,EAAK,OAAO,OAAO,OAAS,QAAU,IAAW,OACjD,EAAK,OAAO,OAAO,OAAS,QAAU,IAAW,WAEnD,EAAK,CAAI,CACX,EACA,iBAAiB,EAAM,CAClB,GAAQ,CAAC,KAAM,UAAW,YAAa,KAAK,CAAC,CAAC,SAAS,EAAa,CAAI,GAAK,EAAE,GAClF,EAAK,CAAI,CACX,EACA,cAAc,EAAM,EACb,GAAQ,GAAQ,KAErB,EAAK,OAAO,OAAS,cACrB,CAAC,EAAQ,EAAS,EAAK,MAAM,CAAC,EAAE,KAAK,QACrC,CAAC,QAAS,YAAa,aAAc,MAAO,MAAO,QAAQ,CAAC,CAAC,SAAS,EAAK,OAAO,IAAI,GAGvF,EAAK,CAAI,EACV,EACA,gBAAiB,CACZ,GAAO,EAAQ,OAAO,CAAE,KAAM,EAAO,UAAW,WAAY,CAAC,CAClE,CACD,CACD,CACD,CACD"}
|
|
1
|
+
{"version":3,"file":"ownership.mjs","names":["ownershipRules","metadata","settings","consumerOptions","context","root","resolve","existsSync","realpathSync","architectureAnchor","issue","inspectArchitecture","file","appFile","reason","componentFacade","standard","flag","node","statement","imported","containsComponentReference","argument","isDbWrite","isExternalCall","delegatingBody","owners","resolver","createResolver","sources","sourceVisitors","typeOnlySource","target","resolveImport","source","path","match","publicNames","exportedNames","declaration","item","isDbAccess","ref","memberPath","rootMember","componentRoot","isFunctionsImport","assembly","role","basename","pure","read","declarative","actionSide","method","propertyName","constructor","binding"],"sources":["../../src/oxlint/ownership.ts"],"sourcesContent":["import { existsSync, realpathSync } from 'node:fs'\nimport { basename, resolve } from 'node:path'\nimport type { CreateRule, ESTree } from '@oxlint/plugins'\nimport {\n\tarchitectureAnchor,\n\tinspectArchitecture,\n\tcreateResolver,\n\tresolveImport,\n} from './architecture'\nimport {\n\tconsumerOptions,\n\tappFile,\n\timported,\n\tsourceVisitors,\n\tisFunctionsImport,\n\ttypeOnlySource,\n\tisDbAccess,\n\tisExternalCall,\n\tcontainsComponentReference,\n\trootMember,\n\tmemberPath,\n\tisDbWrite,\n\tmetadata,\n\tcomponentRoot,\n\tpropertyName,\n\tbinding,\n\texportedNames,\n\tdelegatingBody,\n} from './ast'\n\nexport const ownershipRules = {\n\t'project-structure': {\n\t\tmeta: metadata('Validate the application tree, colocated tests, and module cycles.', {\n\t\t\tstructure: '{{file}}: {{message}}',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst settings = consumerOptions.parse(context.options[0] ?? {})\n\t\t\tconst root = resolve(context.cwd, settings.convexDir)\n\t\t\tif (\n\t\t\t\t(existsSync(context.filename)\n\t\t\t\t\t? realpathSync(context.filename)\n\t\t\t\t\t: resolve(context.filename)) !== architectureAnchor(root)\n\t\t\t)\n\t\t\t\treturn {}\n\t\t\treturn {\n\t\t\t\tProgram(node) {\n\t\t\t\t\tfor (const issue of inspectArchitecture({\n\t\t\t\t\t\tcwd: context.cwd,\n\t\t\t\t\t\tconvexDir: settings.convexDir,\n\t\t\t\t\t}))\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'structure',\n\t\t\t\t\t\t\tdata: { file: issue.file, message: issue.message },\n\t\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'root-wiring-only': {\n\t\tmeta: metadata('Keep root files limited to configuration, assembly, and component facades.', {\n\t\t\townership:\n\t\t\t\t'Root files only wire configuration and components. Move behavior into domain/<module>/: {{reason}}.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context)\n\t\t\tif (!file || file.includes('/')) return {}\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tlet reason = 'business implementation'\n\t\t\tlet componentFacade = false\n\t\t\tconst standard =\n\t\t\t\t/^(?:convex\\.config|auth\\.config|vite\\.config|vitest\\.config|auth|schema|functions|triggers|foundation|http|crons)\\.[cm]?[jt]s$/.test(\n\t\t\t\t\tfile,\n\t\t\t\t)\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tProgram(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tnode.body.some(\n\t\t\t\t\t\t\t(statement) =>\n\t\t\t\t\t\t\t\tstatement.type === 'FunctionDeclaration' ||\n\t\t\t\t\t\t\t\t(statement.type === 'ExportNamedDeclaration' &&\n\t\t\t\t\t\t\t\t\tstatement.declaration?.type === 'FunctionDeclaration'),\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tNewExpression(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\timported(context, node.callee) &&\n\t\t\t\t\t\tnode.arguments.some((argument) => containsComponentReference(context, argument))\n\t\t\t\t\t)\n\t\t\t\t\t\tcomponentFacade = true\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tif (\n\t\t\t\t\t\timported(context, node.callee) &&\n\t\t\t\t\t\tnode.arguments.some((argument) => containsComponentReference(context, argument))\n\t\t\t\t\t)\n\t\t\t\t\t\tcomponentFacade = true\n\t\t\t\t\tif (\n\t\t\t\t\t\tisDbWrite(node) ||\n\t\t\t\t\t\tisExternalCall(context, node) ||\n\t\t\t\t\t\t(file === 'schema.ts' && !(imported(context, node.callee)?.name === 'defineSchema'))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tArrowFunctionExpression(node) {\n\t\t\t\t\tif (!node.body || !delegatingBody(node.body)) flag(node)\n\t\t\t\t},\n\t\t\t\tFunctionExpression(node) {\n\t\t\t\t\tif (!node.body || !delegatingBody(node.body)) flag(node)\n\t\t\t\t},\n\t\t\t\tIfStatement: flag,\n\t\t\t\tSwitchStatement: flag,\n\t\t\t\tConditionalExpression: flag,\n\t\t\t\tForStatement: flag,\n\t\t\t\tForOfStatement: flag,\n\t\t\t\tWhileStatement: flag,\n\t\t\t\t'Program:exit'(node) {\n\t\t\t\t\tif (!standard && !componentFacade) {\n\t\t\t\t\t\tissue = node\n\t\t\t\t\t\treason = 'unknown root file; only declared component facades may add root source files'\n\t\t\t\t\t}\n\t\t\t\t\tif (issue) context.report({ node: issue, messageId: 'ownership', data: { reason } })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'application-orchestration': {\n\t\tmeta: metadata('Reserve application/ for thin cross-domain coordination.', {\n\t\t\townership:\n\t\t\t\t'application/ only coordinates two or more domains. Move schemas, providers, storage access, helpers, and business decisions into their owning domain.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tif (!appFile(context)?.startsWith('application/')) return {}\n\t\t\tconst owners = new Set<string>()\n\t\t\tconst resolver = createResolver()\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\tconst sources = sourceVisitors(context, (node, source) => {\n\t\t\t\tif (typeOnlySource(node)) return\n\t\t\t\tconst target = resolveImport(resolver, context.filename, source)\n\t\t\t\tconst path = target && appFile(context, target)\n\t\t\t\tconst match = path?.match(/^domain\\/([^/]+)\\/(?:commands|queries|rules)(?:\\.[cm]?[jt]s)?$/)\n\t\t\t\tif (match && match[1] !== 'shared') owners.add(match[1])\n\t\t\t\tif (path?.startsWith('domain/') && !match && !path.startsWith('domain/shared/')) flag(node)\n\t\t\t\tif (\n\t\t\t\t\t!path &&\n\t\t\t\t\t![\n\t\t\t\t\t\t'cvx-kit',\n\t\t\t\t\t\t'cvx-kit/components/foundation',\n\t\t\t\t\t\t'@convex-dev/workflow',\n\t\t\t\t\t\t'convex/values',\n\t\t\t\t\t].includes(source)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t})\n\t\t\treturn {\n\t\t\t\t...sources,\n\t\t\t\tProgram(node) {\n\t\t\t\t\tconst publicNames = exportedNames(node)\n\t\t\t\t\tfor (const statement of node.body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tstatement.type === 'FunctionDeclaration' &&\n\t\t\t\t\t\t\tstatement.id &&\n\t\t\t\t\t\t\t!publicNames.has(statement.id.name)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t\tconst declaration =\n\t\t\t\t\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdeclaration?.type === 'TSTypeAliasDeclaration' ||\n\t\t\t\t\t\t\tdeclaration?.type === 'TSInterfaceDeclaration'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t\tif (declaration?.type === 'VariableDeclaration')\n\t\t\t\t\t\t\tfor (const item of declaration.declarations) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\titem.init &&\n\t\t\t\t\t\t\t\t\t['ObjectExpression', 'ArrayExpression', 'Literal'].includes(item.init.type)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tflag(item)\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tstatement.type === 'VariableDeclaration' &&\n\t\t\t\t\t\t\t\t\titem.id.type === 'Identifier' &&\n\t\t\t\t\t\t\t\t\t!publicNames.has(item.id.name) &&\n\t\t\t\t\t\t\t\t\t['ArrowFunctionExpression', 'FunctionExpression'].includes(item.init?.type ?? '')\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tflag(item)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tsources.CallExpression?.(node)\n\t\t\t\t\tif (isDbAccess(node) || isExternalCall(context, node)) flag(node)\n\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\tif (ref && ['zod', 'zod/v4'].includes(ref.source)) flag(node)\n\t\t\t\t},\n\t\t\t\tMemberExpression(node) {\n\t\t\t\t\tconst path = memberPath(node)\n\t\t\t\t\tif (path.length >= 4 && path[1] === 'domain') {\n\t\t\t\t\t\tconst ref = imported(context, rootMember(node))\n\t\t\t\t\t\tif (ref?.name === 'internal' && /_generated\\/api/.test(ref.source)) owners.add(path[2])\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tNewExpression: flag,\n\t\t\t\tIfStatement: flag,\n\t\t\t\tSwitchStatement: flag,\n\t\t\t\tConditionalExpression: flag,\n\t\t\t\t'Program:exit'(node) {\n\t\t\t\t\tif (owners.size < 2 || issue)\n\t\t\t\t\t\tcontext.report({ node: issue ?? node, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'internal-function-ownership': {\n\t\tmeta: metadata('Place internal handlers with their owner, never in root, api/, or shared/.', {\n\t\t\townership:\n\t\t\t\t'Declare system/internal handlers in domain/<module>/, cross-domain application/, migrations/, or an isolated component.',\n\t\t}),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context) ?? ''\n\t\t\tif (\n\t\t\t\tcomponentRoot(context, context.filename) ||\n\t\t\t\t/^(?:domain\\/(?!shared\\/)[^/]+\\/|application\\/|migrations\\/)/.test(file)\n\t\t\t)\n\t\t\t\treturn {}\n\t\t\treturn {\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tif (node.callee.type === 'CallExpression') return\n\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\tif (\n\t\t\t\t\t\tref &&\n\t\t\t\t\t\t((isFunctionsImport(context, ref.source) &&\n\t\t\t\t\t\t\t/^system(Query|Mutation|Action)$/.test(ref.name)) ||\n\t\t\t\t\t\t\t(/(?:_generated\\/server|convex\\/server)/.test(ref.source) &&\n\t\t\t\t\t\t\t\t/^internal(Query|Mutation|Action)(Generic)?$/.test(ref.name)))\n\t\t\t\t\t)\n\t\t\t\t\t\tcontext.report({ node, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n\t'domain-file-responsibilities': {\n\t\tmeta: metadata(\n\t\t\t'Keep invariants pure, reads read-only, shapes declarative, and I/O action-side.',\n\t\t\t{\n\t\t\t\townership:\n\t\t\t\t\t'Respect this domain file responsibility: pure rules, read-only queries, declarative schemas/tables/constants, and external I/O only in actions or integrations.',\n\t\t\t},\n\t\t),\n\t\tcreate(context) {\n\t\t\tconst file = appFile(context) ?? ''\n\t\t\tconst assembly = file === 'domain/table.ts'\n\t\t\tif (!assembly && !/^domain\\/[^/]+\\//.test(file)) return {}\n\t\t\tconst role = basename(file).replace(/\\.[cm]?[jt]s$/, '')\n\t\t\tconst pure = role === 'rules'\n\t\t\tconst read = role === 'queries'\n\t\t\tconst declarative = ['schema', 'table', 'constants'].includes(role)\n\t\t\tconst actionSide =\n\t\t\t\t/(?:^|\\/)(?:actions|integrations|providers)(?:\\/|\\.)/.test(file) ||\n\t\t\t\t/(?:^|_)actions\\./.test(file)\n\t\t\tconst resolver = createResolver()\n\t\t\tlet issue: ESTree.Node | null = null\n\t\t\tfunction flag(node: ESTree.Node) {\n\t\t\t\tissue ??= node\n\t\t\t}\n\t\t\tconst sources = sourceVisitors(context, (node, source) => {\n\t\t\t\tif (typeOnlySource(node)) return\n\t\t\t\tconst target = resolveImport(resolver, context.filename, source)\n\t\t\t\tconst path = target && appFile(context, target)\n\t\t\t\tif (\n\t\t\t\t\tassembly &&\n\t\t\t\t\tsource !== 'cvx-kit/zod-table' &&\n\t\t\t\t\t!/^domain\\/[^/]+\\/table(?:\\.[cm]?[jt]s)?$/.test(path ?? '')\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t\tif (pure && path && !/(?:^|\\/)(?:rules|constants)(?:\\.[cm]?[jt]s)?$/.test(path)) flag(node)\n\t\t\t\tif (\n\t\t\t\t\t(pure || read || declarative) &&\n\t\t\t\t\t!path &&\n\t\t\t\t\t![\n\t\t\t\t\t\t'zod',\n\t\t\t\t\t\t'zod/v4',\n\t\t\t\t\t\t'convex/values',\n\t\t\t\t\t\t'cvx-kit',\n\t\t\t\t\t\t'cvx-kit/zod-table',\n\t\t\t\t\t\t'cvx-kit/errors',\n\t\t\t\t\t].includes(source)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t\tif (\n\t\t\t\t\tread &&\n\t\t\t\t\tpath &&\n\t\t\t\t\t/(?:^|\\/)(?:commands|actions|integrations|providers)(?:[./]|$)/.test(path)\n\t\t\t\t)\n\t\t\t\t\tflag(node)\n\t\t\t})\n\t\t\treturn {\n\t\t\t\t...sources,\n\t\t\t\tProgram(node) {\n\t\t\t\t\tif (!assembly) return\n\t\t\t\t\tfor (const statement of node.body) {\n\t\t\t\t\t\tif (statement.type === 'ImportDeclaration' || statement.type === 'EmptyStatement')\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\tconst declaration =\n\t\t\t\t\t\t\tstatement.type === 'ExportNamedDeclaration' ? statement.declaration : statement\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdeclaration?.type !== 'VariableDeclaration' ||\n\t\t\t\t\t\t\tdeclaration.declarations.some((item) => item.init?.type !== 'CallExpression')\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tflag(statement)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tCallExpression(node) {\n\t\t\t\t\tsources.CallExpression?.(node)\n\t\t\t\t\tif (assembly) {\n\t\t\t\t\t\tconst ref = imported(context, node.callee)\n\t\t\t\t\t\tif (ref?.source !== 'cvx-kit/zod-table' || ref.name !== 'createModule') flag(node)\n\t\t\t\t\t}\n\t\t\t\t\tconst method = node.callee.type === 'MemberExpression' ? propertyName(node.callee) : null\n\t\t\t\t\tif (\n\t\t\t\t\t\t(pure || declarative) &&\n\t\t\t\t\t\t(isDbAccess(node) ||\n\t\t\t\t\t\t\t['runQuery', 'runMutation', 'runAction', 'runAfter', 'runAt'].includes(method ?? ''))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tread &&\n\t\t\t\t\t\t(isDbWrite(node) ||\n\t\t\t\t\t\t\t['runMutation', 'runAction', 'runAfter', 'runAt'].includes(method ?? ''))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tconst constructor = imported(context, node.callee)\n\t\t\t\t\tif (\n\t\t\t\t\t\tconstructor &&\n\t\t\t\t\t\tisFunctionsImport(context, constructor.source) &&\n\t\t\t\t\t\t(pure || declarative || (read && /(?:Mutation|Action)$/.test(constructor.name)))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tread &&\n\t\t\t\t\t\tnode.callee.type === 'MemberExpression' &&\n\t\t\t\t\t\tpropertyName(node.callee.object) === 'storage' &&\n\t\t\t\t\t\t['store', 'delete', 'generateUploadUrl'].includes(method ?? '')\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t\tif (!actionSide && isExternalCall(context, node)) flag(node)\n\t\t\t\t\tif (\n\t\t\t\t\t\tpure &&\n\t\t\t\t\t\tnode.callee.type === 'MemberExpression' &&\n\t\t\t\t\t\tnode.callee.object.type === 'Identifier' &&\n\t\t\t\t\t\t!binding(context, node.callee.object)?.defs.length &&\n\t\t\t\t\t\t((node.callee.object.name === 'Date' && method === 'now') ||\n\t\t\t\t\t\t\t(node.callee.object.name === 'Math' && method === 'random'))\n\t\t\t\t\t)\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tMemberExpression(node) {\n\t\t\t\t\tif (pure && ['db', 'storage', 'scheduler', 'env'].includes(propertyName(node) ?? ''))\n\t\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\tNewExpression(node) {\n\t\t\t\t\tif (!(pure || read || declarative)) return\n\t\t\t\t\tif (\n\t\t\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\t\t\t!binding(context, node.callee)?.defs.length &&\n\t\t\t\t\t\t['Error', 'TypeError', 'RangeError', 'Set', 'Map', 'RegExp'].includes(node.callee.name)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\tflag(node)\n\t\t\t\t},\n\t\t\t\t'Program:exit'() {\n\t\t\t\t\tif (issue) context.report({ node: issue, messageId: 'ownership' })\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n} satisfies Record<string, CreateRule>\n\nexport type OwnershipRuleName = keyof typeof ownershipRules\n"],"mappings":"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"}
|
package/dist/oxlint.d.mts
CHANGED
|
@@ -2,10 +2,10 @@ import { ArchitectureDiagnostic, ArchitectureOptions } from "./oxlint/architectu
|
|
|
2
2
|
import { OwnershipRuleName } from "./oxlint/ownership.mjs";
|
|
3
3
|
import { Plugin, Rule } from "@oxlint/plugins";
|
|
4
4
|
//#region src/oxlint.d.ts
|
|
5
|
-
declare function checkArchitecture(options?: ArchitectureOptions): ArchitectureDiagnostic[];
|
|
6
|
-
type RuleName = OwnershipRuleName | 'component-boundaries' | 'no-component-env' | 'schema-file-boundaries' | 'no-internal-reexports' | 'public-api-first' | 'named-private-helpers' | 'no-raw-builders' | 'no-handwritten-references' | 'domain-import-boundaries' | 'public-functions-in-api' | 'thin-api-adapters' | 'no-inline-enums' | 'no-unbounded-reads' | 'no-manual-timestamps' | 'root-facade-ownership';
|
|
7
|
-
declare const rules: Record<RuleName, Rule>;
|
|
5
|
+
export declare function checkArchitecture(options?: ArchitectureOptions): ArchitectureDiagnostic[];
|
|
6
|
+
export type RuleName = OwnershipRuleName | 'component-boundaries' | 'no-component-env' | 'schema-file-boundaries' | 'no-internal-reexports' | 'public-api-first' | 'named-private-helpers' | 'no-raw-builders' | 'no-handwritten-references' | 'domain-import-boundaries' | 'public-functions-in-api' | 'thin-api-adapters' | 'no-inline-enums' | 'no-unbounded-reads' | 'no-manual-timestamps' | 'root-facade-ownership';
|
|
7
|
+
export declare const rules: Record<RuleName, Rule>;
|
|
8
8
|
declare const plugin: Plugin;
|
|
9
9
|
//#endregion
|
|
10
|
-
export { type ArchitectureDiagnostic, type ArchitectureOptions,
|
|
10
|
+
export { type ArchitectureDiagnostic, type ArchitectureOptions, plugin as default };
|
|
11
11
|
//# sourceMappingURL=oxlint.d.mts.map
|
package/dist/oxlint.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createResolver as e,inspectArchitecture as t,resolveImport as n}from"./oxlint/architecture.mjs";import{appFile as r,binding as i,componentRoot as a,exportName as o,exportedNames as s,hasCall as c,imported as l,isDbWrite as u,isFunctionsImport as d,isTableChain as f,metadata as p,propertyName as m,sourceVisitors as h,staticString as g}from"./oxlint/ast.mjs";import{ownershipRules as _}from"./oxlint/ownership.mjs";import{basename as v,dirname as y,relative as b,sep as x}from"node:path";function S(e={}){return t(e)}const C=T({..._,"root-facade-ownership":{meta:p(`Configure shared kit infrastructure once in its root facade.`,{facade:`Configure {{name}} once in convex/{{file}}; import that facade elsewhere.`}),create(e){if(a(e,e.filename))return{};let t=new Set;function n(n){let i=l(e,n.callee);if(!i)return;let a=null;[`cvx-kit`,`cvx-kit/auth`].includes(i.source)&&i.name===`createAuthFunctions`&&(a=`functions.ts`),[`cvx-kit`,`cvx-kit/triggers`].includes(i.source)&&i.name===`createTriggers`&&(a=`triggers.ts`),[`cvx-kit`,`cvx-kit/components/foundation`].includes(i.source)&&i.name===`Foundation`&&(a=`foundation.ts`),a&&((r(e)!==a||t.has(i.name))&&e.report({node:n,messageId:`facade`,data:{name:i.name,file:a}}),t.add(i.name))}return{CallExpression:n,NewExpression:n}}},"no-raw-builders":{meta:p(`Build host functions through the auth-aware root constructors.`,{builder:`Import raw builders only in convex/functions.ts; use auth*/role*/admin* or system* elsewhere.`}),create(t){if(r(t)===`functions.ts`||a(t,t.filename))return{};let i=e();return{ImportDeclaration(e){if(e.importKind===`type`)return;let a=e.source.value,s=n(i,t.filename,a),c=s&&r(t,s)?.startsWith(`_generated/`)?r(t,s):a;if(!(!/(?:^|\/)_generated\/server(?:\.[cm]?[jt]s)?$/.test(c)&&c!==`convex/server`))for(let n of e.specifiers){if(n.type===`ImportSpecifier`&&n.importKind===`type`)continue;let e=n.type===`ImportSpecifier`?o(n.imported):null;(e===null&&c!==`convex/server`||e&&/^(?:internal)?(?:Query|Mutation|Action)(?:Generic)?$|^(?:query|mutation|action)(?:Generic)?$/.test(e))&&t.report({node:n,messageId:`builder`})}},CallExpression(e){let n=l(t,e.callee);n?.source===`convex/server`&&/^(?:internal)?(?:Query|Mutation|Action)Generic$|^(?:query|mutation|action)Generic$/.test(n.name)&&e.callee.type===`MemberExpression`&&t.report({node:e,messageId:`builder`})}}}},"no-handwritten-references":{meta:p(`Use generated api/internal references.`,{reference:`Use generated api/internal references instead of makeFunctionReference.`}),create(e){return{CallExpression(t){let n=l(e,t.callee);n?.source===`convex/server`&&n.name===`makeFunctionReference`&&e.report({node:t,messageId:`reference`})}}}},"domain-import-boundaries":{meta:p(`Keep api, application, domain, and shared dependencies directed.`,{boundary:`Follow api → application/domain → shared; sibling modules expose only rules.ts and queries.ts.`}),create(t){let i=r(t),a=e();return h(t,(e,o)=>{if(!i)return;let s=n(a,t.filename,o);if(!s)return;let c=r(t,s)?.replace(/\.[cm]?[jt]s$/,``);if(!c){s.split(x).includes(`node_modules`)||t.report({node:e,messageId:`boundary`});return}let l=i.split(`/`),u=c.split(`/`),d=!1;l[0]===`domain`?(d=[`api`,`application`,`migrations`].includes(u[0]),u[0]===`domain`&&l[1]!==u[1]&&u[1]!==`shared`&&l[1]!==`table.ts`&&(d=l[1]===`shared`||![`rules`,`queries`].includes(u.slice(2).join(`/`)))):l[0]===`api`?d=[`api`,`migrations`].includes(u[0])||u[0]===`domain`&&u[1]!==`shared`&&u[1]!==(l.length>2?l[1]:v(i).replace(/\.[cm]?[jt]s$/,``)):l[0]===`application`&&(d=[`api`,`migrations`].includes(u[0])),u[0]===`migrations`&&l[0]!==`migrations`&&(d=!0),[`api`,`domain`,`application`].includes(l[0])&&u.length>1&&![`api`,`domain`,`application`,`_generated`,`components`,`migrations`].includes(u[0])&&(d=!0),d&&t.report({node:e,messageId:`boundary`})})}},"public-functions-in-api":{meta:p(`Expose authenticated public functions only through api/.`,{public:`Declare public auth*/role*/admin* functions in convex/api/<module>.ts.`}),create(e){return r(e)?.startsWith(`api/`)?{}:{CallExpression(t){let n=l(e,t.callee);t.callee.type!==`CallExpression`&&n&&d(e,n.source)&&/^(auth|role|admin)(Query|Mutation|Action)$/.test(n.name)&&e.report({node:t,messageId:`public`})}}}},"thin-api-adapters":{meta:p(`Keep business decisions and writes in domain executors.`,{adapter:`Move business branches, private helpers, and database writes into the owning domain executor.`}),create(e){return r(e)?.startsWith(`api/`)?{IfStatement(t){e.report({node:t,messageId:`adapter`})},SwitchStatement(t){e.report({node:t,messageId:`adapter`})},ConditionalExpression(t){e.report({node:t,messageId:`adapter`})},Program(t){let n=s(t);for(let r of t.body)if(r.type===`FunctionDeclaration`&&r.id&&!n.has(r.id.name)&&e.report({node:r,messageId:`adapter`}),r.type===`VariableDeclaration`)for(let t of r.declarations)t.id.type===`Identifier`&&!n.has(t.id.name)&&[`ArrowFunctionExpression`,`FunctionExpression`].includes(t.init?.type??``)&&e.report({node:t,messageId:`adapter`})},CallExpression(t){u(t)&&e.report({node:t,messageId:`adapter`})}}:{}}},"no-inline-enums":{meta:p(`Keep vocabularies in named constants.ts tuples.`,{vocabulary:`Define a named readonly tuple in constants.ts and pass it to z.enum().`}),create(e){return{CallExpression(t){let n=l(e,t.callee),r=t.arguments[0];for(;r?.type===`TSAsExpression`||r?.type===`TSSatisfiesExpression`;)r=r.expression;n&&/^zod(?:\/v4)?$/.test(n.source)&&n.name===`enum`&&r?.type===`ArrayExpression`&&e.report({node:t,messageId:`vocabulary`})}}}},"no-unbounded-reads":{meta:p(`Bound collection reads through include or pagination.`,{bounded:`Use include(...).execute(limit) or bounded pagination; do not collect() or resolve() an unbounded query.`}),create(e){return a(e,e.filename)?{}:{CallExpression(t){if(t.callee.type!==`MemberExpression`)return;let n=m(t.callee);(n===`collect`&&c(t.callee.object,`query`)||n===`resolve`&&c(t.callee.object,`include`))&&e.report({node:t,messageId:`bounded`})}}}},"no-manual-timestamps":{meta:p(`Let timestamps triggers own createdAt and updatedAt.`,{timestamp:`Remove {{field}} from this write; register the timestamps trigger instead.`}),create(e){return a(e,e.filename)?{}:{CallExpression(t){if(!u(t))return;let n=t.arguments.at(-1);if(n?.type===`ObjectExpression`)for(let t of n.properties){if(t.type!==`Property`)continue;let n=t.computed?g(t.key):o(t.key);(n===`createdAt`||n===`updatedAt`)&&e.report({node:t,messageId:`timestamp`,data:{field:n}})}}}}},"component-boundaries":{meta:p(`Keep portable components isolated and consume their client facades.`,{boundary:`Components may import only their own files and npm dependencies; inject host policy through the client API.`,facade:`Import a component through its client.ts facade, not its private implementation.`}),create(t){let i=a(t,t.filename),o=e();return h(t,(e,s)=>{s.startsWith(`.`)||/^cvx-kit\/components\/[^/]+\//.test(s)&&!/^cvx-kit\/components\/[^/]+\/convex\.config(?:\.js)?$/.test(s)&&t.report({node:e,messageId:`facade`});let c=n(o,t.filename,s);!c||c.split(x).includes(`node_modules`)||(i&&(b(i,c).startsWith(`..${x}`)||c===y(i))?t.report({node:e,messageId:`boundary`}):!i&&a(t,c)&&!/^client(?:\.[cm]?[jt]s)?$/.test(v(c))&&!(r(t)===`convex.config.ts`&&/^convex\.config(?:\.[cm]?[jt]s)?$/.test(v(c)))&&t.report({node:e,messageId:`facade`}))})}},"no-component-env":{meta:p(`Pass environment configuration into portable components.`,{env:`Portable components must receive configuration through arguments; do not read process.env.`}),create(e){if(!a(e,e.filename))return{};function t(t){if(t.type!==`Identifier`)return!1;let n=i(e,t);return t.name===`process`&&!n?.defs.length||n?.defs.some(e=>e.type===`ImportBinding`&&e.parent?.type===`ImportDeclaration`&&[`node:process`,`process`].includes(e.parent.source.value)&&[`ImportDefaultSpecifier`,`ImportNamespaceSpecifier`].includes(e.node.type))}return{ImportDeclaration(t){if([`node:process`,`process`].includes(t.source.value))for(let n of t.specifiers)n.type===`ImportSpecifier`&&o(n.imported)===`env`&&e.report({node:n,messageId:`env`})},MemberExpression(n){if(m(n)!==`env`)return;let r=n.object,a=r.type===`MemberExpression`&&m(r)===`process`&&r.object.type===`Identifier`&&r.object.name===`globalThis`&&!i(e,r.object)?.defs.length;(t(r)||a)&&e.report({node:n,messageId:`env`})},VariableDeclarator(n){if(n.init&&t(n.init)&&n.id.type===`ObjectPattern`)for(let t of n.id.properties)t.type===`Property`&&(t.computed?g(t.key):o(t.key))===`env`&&e.report({node:t,messageId:`env`})}}}},"schema-file-boundaries":{meta:p(`Keep storage shapes and indexes in their documented owners.`,{schema:`{{builder}} belongs in {{file}}.`}),create(e){return{CallExpression(t){let n=l(e,t.callee),i=r(e);if(!i)return;let o=null;n?.source===`convex/server`&&(n.name===`defineSchema`&&i!==`schema.ts`&&!(a(e,e.filename)&&v(i)===`schema.ts`)&&(o=`the root schema.ts (or a component schema.ts)`),n.name===`defineTable`&&!(a(e,e.filename)&&[`schema.ts`,`table.ts`].includes(v(i)))&&(o=`a component schema.ts/table.ts; host tables use zodTable in domain/<module>/schema.ts`)),n&&[`cvx-kit`,`cvx-kit/zod-table`].includes(n.source)&&[`zodTable`,`tenantTable`,`zodVariantTable`].includes(n.name)&&!/^domain\/[^/]+\/schema\.ts$/.test(i)&&(o=`domain/<module>/schema.ts`),o&&e.report({node:t,messageId:`schema`,data:{builder:n?.name??`table`,file:o}}),t.callee.type===`MemberExpression`&&[`index`,`searchIndex`,`vectorIndex`].includes(m(t.callee)??``)&&f(t.callee.object)&&!a(e,e.filename)&&!/^domain\/[^/]+\/table\.ts$/.test(i)&&e.report({node:t,messageId:`schema`,data:{builder:`Table indexes`,file:`domain/<module>/table.ts`}})}}}},"no-internal-reexports":{meta:p(`Import from owning modules directly; consumer apps have no barrels.`,{reexport:`Import from the owning file directly; do not re-export imports inside convex/.`}),create(e){function t(t){if(t.source){e.report({node:t,messageId:`reexport`});return}for(let n of t.type===`ExportNamedDeclaration`?t.specifiers:[])i(e,n.local)?.defs.some(e=>e.type===`ImportBinding`)&&e.report({node:n,messageId:`reexport`})}return{ExportNamedDeclaration:t,ExportAllDeclaration:t,ExportDefaultDeclaration(t){t.declaration.type===`Identifier`&&i(e,t.declaration)?.defs.some(e=>e.type===`ImportBinding`)&&e.report({node:t,messageId:`reexport`})}}}},"public-api-first":{meta:p(`Place private helper implementations after the exported API.`,{order:`Move this private helper below the exported API so the file reads from public purpose to implementation.`}),create(e){return{Program(t){let n=s(t),r=t.body.findLastIndex(e=>e.type.startsWith(`Export`));for(let i of t.body.slice(0,r<0?0:r))i.type===`FunctionDeclaration`&&i.id&&!n.has(i.id.name)&&e.report({node:i,messageId:`order`})}}}},"named-private-helpers":{meta:p(`Use named declarations for top-level private helper functions.`,{helper:`Declare this private helper with function name(...) at the bottom of the file.`}),create(e){return{Program(t){let n=s(t);for(let r of t.body)if(r.type===`VariableDeclaration`)for(let t of r.declarations)t.id.type===`Identifier`&&!n.has(t.id.name)&&(t.init?.type===`ArrowFunctionExpression`||t.init?.type===`FunctionExpression`)&&e.report({node:t,messageId:`helper`})}}}}}),w={meta:{name:`cvx`},rules:C};function T(e){let t={...e},n=Object.keys(e);for(let i of n){let n=e[i];t[i]={meta:{...n.meta,schema:[{type:`object`,properties:{convexDir:{type:`string`,minLength:1}},additionalProperties:!1}]},create(e){let t=r(e);return!t||/(?:^|\/)(?:_generated|__tests__)(?:\/|$)/.test(t)||/\.(?:test|spec)\.[cm]?[jt]s$/.test(t)?{}:n.create(e)}}}return t}export{S as checkArchitecture,w as default,C as rules};
|
|
1
|
+
import{createResolver as e,inspectArchitecture as t,resolveImport as n}from"./oxlint/architecture.mjs";import{appFile as r,binding as i,componentRoot as a,exportName as o,exportedNames as s,hasCall as c,imported as l,isDbWrite as u,isFunctionsImport as d,isTableChain as f,metadata as p,propertyName as m,sourceVisitors as h,staticString as g}from"./oxlint/ast.mjs";import{ownershipRules as _}from"./oxlint/ownership.mjs";import{basename as v,dirname as y,relative as b,sep as x}from"node:path";function S(e={}){return t(e)}const C=T({..._,"root-facade-ownership":{meta:p(`Configure shared kit infrastructure once in its root facade.`,{facade:`Configure {{name}} once in convex/{{file}}; import that facade elsewhere.`}),create(e){if(a(e,e.filename))return{};let t=/* @__PURE__ */ new Set;function n(n){let i=l(e,n.callee);if(!i)return;let a=null;[`cvx-kit`,`cvx-kit/auth`].includes(i.source)&&i.name===`createAuthFunctions`&&(a=`functions.ts`),[`cvx-kit`,`cvx-kit/triggers`].includes(i.source)&&i.name===`createTriggers`&&(a=`triggers.ts`),[`cvx-kit`,`cvx-kit/components/foundation`].includes(i.source)&&i.name===`Foundation`&&(a=`foundation.ts`),a&&((r(e)!==a||t.has(i.name))&&e.report({node:n,messageId:`facade`,data:{name:i.name,file:a}}),t.add(i.name))}return{CallExpression:n,NewExpression:n}}},"no-raw-builders":{meta:p(`Build host functions through the auth-aware root constructors.`,{builder:`Import raw builders only in convex/functions.ts; use auth*/role*/admin* or system* elsewhere.`}),create(t){if(r(t)===`functions.ts`||a(t,t.filename))return{};let i=e();return{ImportDeclaration(e){if(e.importKind===`type`)return;let a=e.source.value,s=n(i,t.filename,a),c=s&&r(t,s)?.startsWith(`_generated/`)?r(t,s):a;if(/(?:^|\/)_generated\/server(?:\.[cm]?[jt]s)?$/.test(c)||c===`convex/server`)for(let n of e.specifiers){if(n.type===`ImportSpecifier`&&n.importKind===`type`)continue;let e=n.type===`ImportSpecifier`?o(n.imported):null;(e===null&&c!==`convex/server`||e&&/^(?:internal)?(?:Query|Mutation|Action)(?:Generic)?$|^(?:query|mutation|action)(?:Generic)?$/.test(e))&&t.report({node:n,messageId:`builder`})}},CallExpression(e){let n=l(t,e.callee);n?.source===`convex/server`&&/^(?:internal)?(?:Query|Mutation|Action)Generic$|^(?:query|mutation|action)Generic$/.test(n.name)&&e.callee.type===`MemberExpression`&&t.report({node:e,messageId:`builder`})}}}},"no-handwritten-references":{meta:p(`Use generated api/internal references.`,{reference:`Use generated api/internal references instead of makeFunctionReference.`}),create(e){return{CallExpression(t){let n=l(e,t.callee);n?.source===`convex/server`&&n.name===`makeFunctionReference`&&e.report({node:t,messageId:`reference`})}}}},"domain-import-boundaries":{meta:p(`Keep api, application, domain, and shared dependencies directed.`,{boundary:`Follow api → application/domain → shared; sibling modules expose only rules.ts and queries.ts.`}),create(t){let i=r(t),a=e();return h(t,(e,o)=>{if(!i)return;let s=n(a,t.filename,o);if(!s)return;let c=r(t,s)?.replace(/\.[cm]?[jt]s$/,``);if(!c){s.split(x).includes(`node_modules`)||t.report({node:e,messageId:`boundary`});return}let l=i.split(`/`),u=c.split(`/`),d=!1;l[0]===`domain`?(d=[`api`,`application`,`migrations`].includes(u[0]),u[0]===`domain`&&l[1]!==u[1]&&u[1]!==`shared`&&l[1]!==`table.ts`&&(d=l[1]===`shared`||![`rules`,`queries`].includes(u.slice(2).join(`/`)))):l[0]===`api`?d=[`api`,`migrations`].includes(u[0])||u[0]===`domain`&&u[1]!==`shared`&&u[1]!==(l.length>2?l[1]:v(i).replace(/\.[cm]?[jt]s$/,``)):l[0]===`application`&&(d=[`api`,`migrations`].includes(u[0])),u[0]===`migrations`&&l[0]!==`migrations`&&(d=!0),[`api`,`domain`,`application`].includes(l[0])&&u.length>1&&![`api`,`domain`,`application`,`_generated`,`components`,`migrations`].includes(u[0])&&(d=!0),d&&t.report({node:e,messageId:`boundary`})})}},"public-functions-in-api":{meta:p(`Expose authenticated public functions only through api/.`,{public:`Declare public auth*/role*/admin* functions in convex/api/<module>.ts.`}),create(e){return r(e)?.startsWith(`api/`)?{}:{CallExpression(t){let n=l(e,t.callee);t.callee.type!==`CallExpression`&&n&&d(e,n.source)&&/^(auth|role|admin)(Query|Mutation|Action)$/.test(n.name)&&e.report({node:t,messageId:`public`})}}}},"thin-api-adapters":{meta:p(`Keep business decisions and writes in domain executors.`,{adapter:`Move business branches, private helpers, and database writes into the owning domain executor.`}),create(e){return r(e)?.startsWith(`api/`)?{IfStatement(t){e.report({node:t,messageId:`adapter`})},SwitchStatement(t){e.report({node:t,messageId:`adapter`})},ConditionalExpression(t){e.report({node:t,messageId:`adapter`})},Program(t){let n=s(t);for(let r of t.body)if(r.type===`FunctionDeclaration`&&r.id&&!n.has(r.id.name)&&e.report({node:r,messageId:`adapter`}),r.type===`VariableDeclaration`)for(let t of r.declarations)t.id.type===`Identifier`&&!n.has(t.id.name)&&[`ArrowFunctionExpression`,`FunctionExpression`].includes(t.init?.type??``)&&e.report({node:t,messageId:`adapter`})},CallExpression(t){u(t)&&e.report({node:t,messageId:`adapter`})}}:{}}},"no-inline-enums":{meta:p(`Keep vocabularies in named constants.ts tuples.`,{vocabulary:`Define a named readonly tuple in constants.ts and pass it to z.enum().`}),create(e){return{CallExpression(t){let n=l(e,t.callee),r=t.arguments[0];for(;r?.type===`TSAsExpression`||r?.type===`TSSatisfiesExpression`;)r=r.expression;n&&/^zod(?:\/v4)?$/.test(n.source)&&n.name===`enum`&&r?.type===`ArrayExpression`&&e.report({node:t,messageId:`vocabulary`})}}}},"no-unbounded-reads":{meta:p(`Bound collection reads through include or pagination.`,{bounded:`Use include(...).execute(limit) or bounded pagination; do not collect() or resolve() an unbounded query.`}),create(e){return a(e,e.filename)?{}:{CallExpression(t){if(t.callee.type!==`MemberExpression`)return;let n=m(t.callee);(n===`collect`&&c(t.callee.object,`query`)||n===`resolve`&&c(t.callee.object,`include`))&&e.report({node:t,messageId:`bounded`})}}}},"no-manual-timestamps":{meta:p(`Let timestamps triggers own createdAt and updatedAt.`,{timestamp:`Remove {{field}} from this write; register the timestamps trigger instead.`}),create(e){return a(e,e.filename)?{}:{CallExpression(t){if(!u(t))return;let n=t.arguments.at(-1);if(n?.type===`ObjectExpression`)for(let t of n.properties){if(t.type!==`Property`)continue;let n=t.computed?g(t.key):o(t.key);(n===`createdAt`||n===`updatedAt`)&&e.report({node:t,messageId:`timestamp`,data:{field:n}})}}}}},"component-boundaries":{meta:p(`Keep portable components isolated and consume their client facades.`,{boundary:`Components may import only their own files and npm dependencies; inject host policy through the client API.`,facade:`Import a component through its client.ts facade, not its private implementation.`}),create(t){let i=a(t,t.filename),o=e();return h(t,(e,s)=>{s.startsWith(`.`)||/^cvx-kit\/components\/[^/]+\//.test(s)&&!/^cvx-kit\/components\/[^/]+\/convex\.config(?:\.js)?$/.test(s)&&t.report({node:e,messageId:`facade`});let c=n(o,t.filename,s);c&&!c.split(x).includes(`node_modules`)&&(i&&(b(i,c).startsWith(`..${x}`)||c===y(i))?t.report({node:e,messageId:`boundary`}):!i&&a(t,c)&&!/^client(?:\.[cm]?[jt]s)?$/.test(v(c))&&!(r(t)===`convex.config.ts`&&/^convex\.config(?:\.[cm]?[jt]s)?$/.test(v(c)))&&t.report({node:e,messageId:`facade`}))})}},"no-component-env":{meta:p(`Pass environment configuration into portable components.`,{env:`Portable components must receive configuration through arguments; do not read process.env.`}),create(e){if(!a(e,e.filename))return{};function t(t){if(t.type!==`Identifier`)return!1;let n=i(e,t);return t.name===`process`&&!n?.defs.length||n?.defs.some(e=>e.type===`ImportBinding`&&e.parent?.type===`ImportDeclaration`&&[`node:process`,`process`].includes(e.parent.source.value)&&[`ImportDefaultSpecifier`,`ImportNamespaceSpecifier`].includes(e.node.type))}return{ImportDeclaration(t){if([`node:process`,`process`].includes(t.source.value))for(let n of t.specifiers)n.type===`ImportSpecifier`&&o(n.imported)===`env`&&e.report({node:n,messageId:`env`})},MemberExpression(n){if(m(n)!==`env`)return;let r=n.object,a=r.type===`MemberExpression`&&m(r)===`process`&&r.object.type===`Identifier`&&r.object.name===`globalThis`&&!i(e,r.object)?.defs.length;(t(r)||a)&&e.report({node:n,messageId:`env`})},VariableDeclarator(n){if(n.init&&t(n.init)&&n.id.type===`ObjectPattern`)for(let t of n.id.properties)t.type===`Property`&&(t.computed?g(t.key):o(t.key))===`env`&&e.report({node:t,messageId:`env`})}}}},"schema-file-boundaries":{meta:p(`Keep storage shapes and indexes in their documented owners.`,{schema:`{{builder}} belongs in {{file}}.`}),create(e){return{CallExpression(t){let n=l(e,t.callee),i=r(e);if(!i)return;let o=null;n?.source===`convex/server`&&(n.name===`defineSchema`&&i!==`schema.ts`&&!(a(e,e.filename)&&v(i)===`schema.ts`)&&(o=`the root schema.ts (or a component schema.ts)`),n.name===`defineTable`&&!(a(e,e.filename)&&[`schema.ts`,`table.ts`].includes(v(i)))&&(o=`a component schema.ts/table.ts; host tables use zodTable in domain/<module>/schema.ts`)),n&&[`cvx-kit`,`cvx-kit/zod-table`].includes(n.source)&&[`zodTable`,`tenantTable`,`zodVariantTable`].includes(n.name)&&!/^domain\/[^/]+\/schema\.ts$/.test(i)&&(o=`domain/<module>/schema.ts`),o&&e.report({node:t,messageId:`schema`,data:{builder:n?.name??`table`,file:o}}),t.callee.type===`MemberExpression`&&[`index`,`searchIndex`,`vectorIndex`].includes(m(t.callee)??``)&&f(t.callee.object)&&!a(e,e.filename)&&!/^domain\/[^/]+\/table\.ts$/.test(i)&&e.report({node:t,messageId:`schema`,data:{builder:`Table indexes`,file:`domain/<module>/table.ts`}})}}}},"no-internal-reexports":{meta:p(`Import from owning modules directly; consumer apps have no barrels.`,{reexport:`Import from the owning file directly; do not re-export imports inside convex/.`}),create(e){function t(t){if(t.source){e.report({node:t,messageId:`reexport`});return}for(let n of t.type===`ExportNamedDeclaration`?t.specifiers:[])i(e,n.local)?.defs.some(e=>e.type===`ImportBinding`)&&e.report({node:n,messageId:`reexport`})}return{ExportNamedDeclaration:t,ExportAllDeclaration:t,ExportDefaultDeclaration(t){t.declaration.type===`Identifier`&&i(e,t.declaration)?.defs.some(e=>e.type===`ImportBinding`)&&e.report({node:t,messageId:`reexport`})}}}},"public-api-first":{meta:p(`Place private helper implementations after the exported API.`,{order:`Move this private helper below the exported API so the file reads from public purpose to implementation.`}),create(e){return{Program(t){let n=s(t),r=t.body.findLastIndex(e=>e.type.startsWith(`Export`));for(let i of t.body.slice(0,r<0?0:r))i.type===`FunctionDeclaration`&&i.id&&!n.has(i.id.name)&&e.report({node:i,messageId:`order`})}}}},"named-private-helpers":{meta:p(`Use named declarations for top-level private helper functions.`,{helper:`Declare this private helper with function name(...) at the bottom of the file.`}),create(e){return{Program(t){let n=s(t);for(let r of t.body)if(r.type===`VariableDeclaration`)for(let t of r.declarations)t.id.type===`Identifier`&&!n.has(t.id.name)&&(t.init?.type===`ArrowFunctionExpression`||t.init?.type===`FunctionExpression`)&&e.report({node:t,messageId:`helper`})}}}}}),w={meta:{name:`cvx`},rules:C};function T(e){let t={...e},n=Object.keys(e);for(let i of n){let n=e[i];t[i]={meta:{...n.meta,schema:[{type:`object`,properties:{convexDir:{type:`string`,minLength:1}},additionalProperties:!1}]},create(e){let t=r(e);return!t||/(?:^|\/)(?:_generated|__tests__)(?:\/|$)/.test(t)||/\.(?:test|spec)\.[cm]?[jt]s$/.test(t)?{}:n.create(e)}}}return t}export{S as checkArchitecture,w as default,C as rules};
|
|
2
2
|
//# sourceMappingURL=oxlint.mjs.map
|