@ontrails/warden 1.0.0-beta.4 → 1.0.0-beta.42
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 +837 -13
- package/README.md +133 -26
- package/bin/warden.ts +51 -0
- package/package.json +27 -6
- package/src/adapter-check.ts +136 -0
- package/src/cli.ts +1570 -105
- package/src/command.ts +986 -0
- package/src/config.ts +193 -0
- package/src/draft.ts +22 -0
- package/src/drift.ts +233 -23
- package/src/fix.ts +126 -0
- package/src/formatters.ts +78 -13
- package/src/guide.ts +245 -0
- package/src/index.ts +247 -15
- package/src/project-context.ts +446 -0
- package/src/project-rules.ts +290 -0
- package/src/resolve.ts +531 -0
- package/src/rules/activation-orphan.ts +97 -0
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +150 -0
- package/src/rules/cli-command-route-coherence.ts +177 -0
- package/src/rules/composes-declarations.ts +839 -0
- package/src/rules/context-no-surface-types.ts +79 -15
- package/src/rules/dead-internal-trail.ts +161 -0
- package/src/rules/dead-public-trail.ts +258 -0
- package/src/rules/draft-file-marking.ts +155 -0
- package/src/rules/draft-visible-debt.ts +82 -0
- package/src/rules/duplicate-exported-symbol.ts +211 -0
- package/src/rules/duplicate-public-contract.ts +137 -0
- package/src/rules/entity-exists.ts +254 -0
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +290 -0
- package/src/rules/example-valid.ts +395 -0
- package/src/rules/fires-declarations.ts +740 -0
- package/src/rules/governed-symbol-residue.ts +438 -0
- package/src/rules/implementation-returns-result.ts +1409 -166
- package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
- package/src/rules/incomplete-crud.ts +583 -0
- package/src/rules/index.ts +277 -10
- package/src/rules/intent-propagation.ts +125 -0
- package/src/rules/layer-field-name-drift.ts +102 -0
- package/src/rules/library-projection-coherence.ts +100 -0
- package/src/rules/metadata.ts +871 -0
- package/src/rules/missing-reconcile.ts +97 -0
- package/src/rules/missing-visibility.ts +111 -0
- package/src/rules/no-destructured-compose.ts +196 -0
- package/src/rules/no-dev-permit-in-source.ts +99 -0
- package/src/rules/no-direct-implementation-call.ts +12 -7
- package/src/rules/no-legacy-cli-alias-export.ts +247 -0
- package/src/rules/no-legacy-layer-imports.ts +211 -0
- package/src/rules/no-native-error-result.ts +118 -0
- package/src/rules/no-redundant-result-error-wrap.ts +384 -0
- package/src/rules/no-retired-cross-vocabulary.ts +204 -0
- package/src/rules/no-sync-result-assumption.ts +1141 -98
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +15 -8
- package/src/rules/no-top-level-surface.ts +371 -0
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +149 -0
- package/src/rules/owner-projection-parity.ts +143 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/public-export-example-coverage.ts +573 -0
- package/src/rules/public-internal-deep-imports.ts +456 -0
- package/src/rules/public-output-schema.ts +29 -0
- package/src/rules/public-union-output-discriminants.ts +150 -0
- package/src/rules/read-intent-fires.ts +188 -0
- package/src/rules/reference-exists.ts +97 -0
- package/src/rules/registry-names.ts +167 -0
- package/src/rules/resolved-import-boundary.ts +146 -0
- package/src/rules/resource-declarations.ts +697 -0
- package/src/rules/resource-exists.ts +181 -0
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/resource-mock-coverage.ts +115 -0
- package/src/rules/retired-vocabulary.ts +633 -0
- package/src/rules/scan.ts +38 -25
- package/src/rules/scheduled-destroy-intent.ts +44 -0
- package/src/rules/signal-graph-coaching.ts +220 -0
- package/src/rules/source/composition.ts +165 -0
- package/src/rules/source/drafts.ts +164 -0
- package/src/rules/source/entities.ts +618 -0
- package/src/rules/source/pragmas.ts +45 -0
- package/src/rules/source/resources.ts +64 -0
- package/src/rules/source/signals.ts +397 -0
- package/src/rules/source/stores.ts +310 -0
- package/src/rules/specs.ts +9 -5
- package/src/rules/static-resource-accessor-preference.ts +653 -0
- package/src/rules/surface-overlay-coherence.ts +262 -0
- package/src/rules/surface-trailhead-coherence.ts +366 -0
- package/src/rules/trail-fork-coaching.ts +625 -0
- package/src/rules/trail-versioning-source.ts +1076 -0
- package/src/rules/trail-versioning-topo.ts +172 -0
- package/src/rules/trailhead-override-divergence.ts +356 -0
- package/src/rules/types.ts +354 -8
- package/src/rules/unmaterialized-activation-source.ts +85 -0
- package/src/rules/unreachable-detour-shadowing.ts +339 -0
- package/src/rules/valid-describe-refs.ts +162 -32
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +540 -0
- package/src/rules/warden-rules-use-ast.ts +1109 -0
- package/src/rules/webhook-route-collision.ts +306 -0
- package/src/trails/activation-orphan.trail.ts +84 -0
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/cli-command-route-coherence.trail.ts +47 -0
- package/src/trails/composes-declarations.trail.ts +22 -0
- package/src/trails/context-no-surface-types.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/dead-public-trail.trail.ts +31 -0
- package/src/trails/deprecation-without-guidance.trail.ts +21 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/duplicate-exported-symbol.trail.ts +48 -0
- package/src/trails/duplicate-public-contract.trail.ts +47 -0
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/error-mapping-completeness.trail.ts +30 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +23 -0
- package/src/trails/fork-without-preserved-implementation.trail.ts +31 -0
- package/src/trails/governed-symbol-residue.trail.ts +24 -0
- package/src/trails/implementation-returns-result.trail.ts +20 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +89 -0
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/layer-field-name-drift.trail.ts +39 -0
- package/src/trails/library-projection-coherence.trail.ts +43 -0
- package/src/trails/marker-schema-unsupported.trail.ts +23 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-destructured-compose.trail.ts +44 -0
- package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
- package/src/trails/no-direct-implementation-call.trail.ts +16 -0
- package/src/trails/no-legacy-cli-alias-export.trail.ts +41 -0
- package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
- package/src/trails/no-native-error-result.trail.ts +18 -0
- package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
- package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
- package/src/trails/no-sync-result-assumption.trail.ts +19 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/no-throw-in-implementation.trail.ts +20 -0
- package/src/trails/no-top-level-surface.trail.ts +43 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/owner-projection-parity.trail.ts +26 -0
- package/src/trails/pending-force.trail.ts +21 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/prefer-schema-inference.trail.ts +21 -0
- package/src/trails/public-export-example-coverage.trail.ts +16 -0
- package/src/trails/public-internal-deep-imports.trail.ts +94 -0
- package/src/trails/public-output-schema.trail.ts +55 -0
- package/src/trails/public-union-output-discriminants.trail.ts +33 -0
- package/src/trails/read-intent-fires.trail.ts +20 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/resolved-import-boundary.trail.ts +109 -0
- package/src/trails/resource-declarations.trail.ts +25 -0
- package/src/trails/resource-exists.trail.ts +27 -0
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/resource-mock-coverage.trail.ts +40 -0
- package/src/trails/run.ts +160 -0
- package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
- package/src/trails/schema.ts +237 -0
- package/src/trails/signal-graph-coaching.trail.ts +77 -0
- package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
- package/src/trails/surface-overlay-coherence.trail.ts +24 -0
- package/src/trails/surface-trailhead-coherence.trail.ts +25 -0
- package/src/trails/topo.ts +6 -0
- package/src/trails/trail-fork-coaching.trail.ts +42 -0
- package/src/trails/trailhead-override-divergence.trail.ts +47 -0
- package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-describe-refs.trail.ts +18 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/version-gap.trail.ts +35 -0
- package/src/trails/version-pinned-compose.trail.ts +23 -0
- package/src/trails/version-without-examples.trail.ts +38 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +64 -0
- package/src/trails/webhook-route-collision.trail.ts +50 -0
- package/src/trails/wrap-rule.ts +224 -0
- package/src/workspaces.ts +199 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/cli.d.ts +0 -46
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -218
- package/dist/cli.js.map +0 -1
- package/dist/drift.d.ts +0 -26
- package/dist/drift.d.ts.map +0 -1
- package/dist/drift.js +0 -27
- package/dist/drift.js.map +0 -1
- package/dist/formatters.d.ts +0 -29
- package/dist/formatters.d.ts.map +0 -1
- package/dist/formatters.js +0 -87
- package/dist/formatters.js.map +0 -1
- package/dist/index.d.ts +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -26
- package/dist/index.js.map +0 -1
- package/dist/rules/ast.d.ts +0 -41
- package/dist/rules/ast.d.ts.map +0 -1
- package/dist/rules/ast.js +0 -161
- package/dist/rules/ast.js.map +0 -1
- package/dist/rules/context-no-surface-types.d.ts +0 -12
- package/dist/rules/context-no-surface-types.d.ts.map +0 -1
- package/dist/rules/context-no-surface-types.js +0 -96
- package/dist/rules/context-no-surface-types.js.map +0 -1
- package/dist/rules/implementation-returns-result.d.ts +0 -13
- package/dist/rules/implementation-returns-result.d.ts.map +0 -1
- package/dist/rules/implementation-returns-result.js +0 -277
- package/dist/rules/implementation-returns-result.js.map +0 -1
- package/dist/rules/index.d.ts +0 -15
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js +0 -34
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/no-direct-impl-in-route.d.ts +0 -12
- package/dist/rules/no-direct-impl-in-route.d.ts.map +0 -1
- package/dist/rules/no-direct-impl-in-route.js +0 -47
- package/dist/rules/no-direct-impl-in-route.js.map +0 -1
- package/dist/rules/no-direct-implementation-call.d.ts +0 -12
- package/dist/rules/no-direct-implementation-call.d.ts.map +0 -1
- package/dist/rules/no-direct-implementation-call.js +0 -39
- package/dist/rules/no-direct-implementation-call.js.map +0 -1
- package/dist/rules/no-sync-result-assumption.d.ts +0 -6
- package/dist/rules/no-sync-result-assumption.d.ts.map +0 -1
- package/dist/rules/no-sync-result-assumption.js +0 -98
- package/dist/rules/no-sync-result-assumption.js.map +0 -1
- package/dist/rules/no-throw-in-detour-target.d.ts +0 -12
- package/dist/rules/no-throw-in-detour-target.d.ts.map +0 -1
- package/dist/rules/no-throw-in-detour-target.js +0 -87
- package/dist/rules/no-throw-in-detour-target.js.map +0 -1
- package/dist/rules/no-throw-in-implementation.d.ts +0 -9
- package/dist/rules/no-throw-in-implementation.d.ts.map +0 -1
- package/dist/rules/no-throw-in-implementation.js +0 -34
- package/dist/rules/no-throw-in-implementation.js.map +0 -1
- package/dist/rules/prefer-schema-inference.d.ts +0 -7
- package/dist/rules/prefer-schema-inference.d.ts.map +0 -1
- package/dist/rules/prefer-schema-inference.js +0 -86
- package/dist/rules/prefer-schema-inference.js.map +0 -1
- package/dist/rules/scan.d.ts +0 -8
- package/dist/rules/scan.d.ts.map +0 -1
- package/dist/rules/scan.js +0 -32
- package/dist/rules/scan.js.map +0 -1
- package/dist/rules/specs.d.ts +0 -29
- package/dist/rules/specs.d.ts.map +0 -1
- package/dist/rules/specs.js +0 -192
- package/dist/rules/specs.js.map +0 -1
- package/dist/rules/structure.d.ts +0 -13
- package/dist/rules/structure.d.ts.map +0 -1
- package/dist/rules/structure.js +0 -142
- package/dist/rules/structure.js.map +0 -1
- package/dist/rules/types.d.ts +0 -52
- package/dist/rules/types.d.ts.map +0 -1
- package/dist/rules/types.js +0 -2
- package/dist/rules/types.js.map +0 -1
- package/dist/rules/valid-describe-refs.d.ts +0 -7
- package/dist/rules/valid-describe-refs.d.ts.map +0 -1
- package/dist/rules/valid-describe-refs.js +0 -51
- package/dist/rules/valid-describe-refs.js.map +0 -1
- package/dist/rules/valid-detour-refs.d.ts +0 -6
- package/dist/rules/valid-detour-refs.d.ts.map +0 -1
- package/dist/rules/valid-detour-refs.js +0 -116
- package/dist/rules/valid-detour-refs.js.map +0 -1
- package/src/__tests__/cli.test.ts +0 -198
- package/src/__tests__/drift.test.ts +0 -74
- package/src/__tests__/formatters.test.ts +0 -157
- package/src/__tests__/implementation-returns-result.test.ts +0 -129
- package/src/__tests__/no-direct-implementation-call.test.ts +0 -83
- package/src/__tests__/no-sync-result-assumption.test.ts +0 -85
- package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
- package/src/__tests__/prefer-schema-inference.test.ts +0 -84
- package/src/__tests__/rules.test.ts +0 -227
- package/src/__tests__/valid-describe-refs.test.ts +0 -60
- package/src/rules/ast.ts +0 -213
- package/src/rules/no-direct-impl-in-route.ts +0 -81
- package/src/rules/no-throw-in-detour-target.ts +0 -150
- package/src/rules/valid-detour-refs.ts +0 -187
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-context helpers shared by the Warden runner and resolver-backed
|
|
3
|
+
* rules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { realpathSync } from 'node:fs';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
collectImportResolutionsForFile,
|
|
11
|
+
createWardenResolver,
|
|
12
|
+
normalizePath,
|
|
13
|
+
} from './resolve.js';
|
|
14
|
+
import type {
|
|
15
|
+
WardenImportResolution,
|
|
16
|
+
WardenResolverOptions,
|
|
17
|
+
} from './resolve.js';
|
|
18
|
+
import {
|
|
19
|
+
getNodeBodyStatements,
|
|
20
|
+
getNodeDeclaration,
|
|
21
|
+
getNodeDeclarations,
|
|
22
|
+
getNodeExported,
|
|
23
|
+
getNodeExportKind,
|
|
24
|
+
getNodeId,
|
|
25
|
+
getNodeLocal,
|
|
26
|
+
getNodeName,
|
|
27
|
+
getNodeSource,
|
|
28
|
+
getNodeSpecifiers,
|
|
29
|
+
getNodeValue,
|
|
30
|
+
isDeclarationWithId,
|
|
31
|
+
isExportNamedDeclaration,
|
|
32
|
+
isVariableDeclaration,
|
|
33
|
+
offsetToLine,
|
|
34
|
+
parse,
|
|
35
|
+
} from '@ontrails/source';
|
|
36
|
+
import type { AstNode } from '@ontrails/source';
|
|
37
|
+
import type { WardenExportedSymbolDefinition } from './rules/types.js';
|
|
38
|
+
import { collectPublicWorkspaces } from './workspaces.js';
|
|
39
|
+
import type { WardenPublicWorkspace } from './workspaces.js';
|
|
40
|
+
|
|
41
|
+
const ONTRAILS_DOCUMENTATION_SPECIFIER_PATTERN =
|
|
42
|
+
/@ontrails\/[a-z0-9-]+(?:\/[A-Za-z0-9._~-]+)+/g;
|
|
43
|
+
|
|
44
|
+
const normalizeRealPath = (path: string): string => {
|
|
45
|
+
try {
|
|
46
|
+
return normalizePath(realpathSync(path));
|
|
47
|
+
} catch {
|
|
48
|
+
return normalizePath(resolve(path));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const setResolutionsForFile = (
|
|
53
|
+
resolutionsByFile: Map<string, readonly WardenImportResolution[]>,
|
|
54
|
+
sourceFilePath: string,
|
|
55
|
+
resolutions: readonly WardenImportResolution[]
|
|
56
|
+
): void => {
|
|
57
|
+
const normalizedFilePath =
|
|
58
|
+
resolutions[0]?.importerPath ?? normalizeRealPath(sourceFilePath);
|
|
59
|
+
resolutionsByFile.set(normalizedFilePath, resolutions);
|
|
60
|
+
if (normalizedFilePath !== sourceFilePath) {
|
|
61
|
+
resolutionsByFile.set(sourceFilePath, resolutions);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export interface WardenProjectContextSourceFile {
|
|
66
|
+
readonly filePath: string;
|
|
67
|
+
readonly kind: 'documentation' | 'text' | 'typescript';
|
|
68
|
+
readonly sourceCode: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const collectDocumentationImportSpecifiers = (
|
|
72
|
+
sourceCode: string
|
|
73
|
+
): readonly { readonly importSource: string; readonly line: number }[] => {
|
|
74
|
+
const specifiers: { importSource: string; line: number }[] = [];
|
|
75
|
+
for (const match of sourceCode.matchAll(
|
|
76
|
+
ONTRAILS_DOCUMENTATION_SPECIFIER_PATTERN
|
|
77
|
+
)) {
|
|
78
|
+
if (match.index === undefined) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
specifiers.push({
|
|
82
|
+
importSource: match[0],
|
|
83
|
+
line: offsetToLine(sourceCode, match.index),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return specifiers;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const exportAliasesForWorkspaces = (
|
|
90
|
+
workspaces: ReadonlyMap<string, WardenPublicWorkspace>
|
|
91
|
+
): Record<string, string[]> => {
|
|
92
|
+
const aliases: Record<string, string[]> = {};
|
|
93
|
+
for (const workspace of workspaces.values()) {
|
|
94
|
+
for (const [specifier, target] of Object.entries(
|
|
95
|
+
workspace.exportTargets ?? {}
|
|
96
|
+
)) {
|
|
97
|
+
aliases[`${specifier}$`] = [target];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return aliases;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const resolveOptionsWithWorkspaceAliases = (
|
|
104
|
+
publicWorkspaces: ReadonlyMap<string, WardenPublicWorkspace> | undefined,
|
|
105
|
+
resolveOptions: WardenResolverOptions['resolveOptions'] | undefined
|
|
106
|
+
): WardenResolverOptions['resolveOptions'] => {
|
|
107
|
+
if (!publicWorkspaces) {
|
|
108
|
+
return resolveOptions;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const workspaceAliases = exportAliasesForWorkspaces(publicWorkspaces);
|
|
112
|
+
return {
|
|
113
|
+
...resolveOptions,
|
|
114
|
+
alias: {
|
|
115
|
+
...workspaceAliases,
|
|
116
|
+
...resolveOptions?.alias,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const collectProjectImportResolutions = ({
|
|
122
|
+
publicWorkspaces,
|
|
123
|
+
resolveOptions,
|
|
124
|
+
rootDir,
|
|
125
|
+
sourceFiles,
|
|
126
|
+
}: {
|
|
127
|
+
readonly publicWorkspaces?: ReadonlyMap<string, WardenPublicWorkspace>;
|
|
128
|
+
readonly resolveOptions?: WardenResolverOptions['resolveOptions'];
|
|
129
|
+
readonly rootDir: string;
|
|
130
|
+
readonly sourceFiles: readonly WardenProjectContextSourceFile[];
|
|
131
|
+
}): ReadonlyMap<string, readonly WardenImportResolution[]> => {
|
|
132
|
+
const resolver = createWardenResolver({
|
|
133
|
+
resolveOptions: resolveOptionsWithWorkspaceAliases(
|
|
134
|
+
publicWorkspaces,
|
|
135
|
+
resolveOptions
|
|
136
|
+
),
|
|
137
|
+
rootDir,
|
|
138
|
+
});
|
|
139
|
+
const resolutionsByFile = new Map<
|
|
140
|
+
string,
|
|
141
|
+
readonly WardenImportResolution[]
|
|
142
|
+
>();
|
|
143
|
+
|
|
144
|
+
for (const sourceFile of sourceFiles) {
|
|
145
|
+
if (sourceFile.kind !== 'typescript') {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const resolutions = collectImportResolutionsForFile({
|
|
149
|
+
filePath: sourceFile.filePath,
|
|
150
|
+
resolver,
|
|
151
|
+
sourceCode: sourceFile.sourceCode,
|
|
152
|
+
});
|
|
153
|
+
if (resolutions.length > 0) {
|
|
154
|
+
setResolutionsForFile(
|
|
155
|
+
resolutionsByFile,
|
|
156
|
+
sourceFile.filePath,
|
|
157
|
+
resolutions
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return resolutionsByFile;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export const collectProjectDocumentationImportResolutions = ({
|
|
166
|
+
publicWorkspaces: providedPublicWorkspaces,
|
|
167
|
+
rootDir,
|
|
168
|
+
sourceFiles,
|
|
169
|
+
}: {
|
|
170
|
+
readonly publicWorkspaces?: ReadonlyMap<string, WardenPublicWorkspace>;
|
|
171
|
+
readonly rootDir: string;
|
|
172
|
+
readonly sourceFiles: readonly WardenProjectContextSourceFile[];
|
|
173
|
+
}): ReadonlyMap<string, readonly WardenImportResolution[]> => {
|
|
174
|
+
const publicWorkspaces =
|
|
175
|
+
providedPublicWorkspaces ?? collectPublicWorkspaces(rootDir);
|
|
176
|
+
const resolver = createWardenResolver({
|
|
177
|
+
resolveOptions: { alias: exportAliasesForWorkspaces(publicWorkspaces) },
|
|
178
|
+
rootDir,
|
|
179
|
+
});
|
|
180
|
+
const resolutionsByFile = new Map<
|
|
181
|
+
string,
|
|
182
|
+
readonly WardenImportResolution[]
|
|
183
|
+
>();
|
|
184
|
+
|
|
185
|
+
for (const sourceFile of sourceFiles) {
|
|
186
|
+
if (sourceFile.kind !== 'documentation') {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const resolutions = collectDocumentationImportSpecifiers(
|
|
190
|
+
sourceFile.sourceCode
|
|
191
|
+
).map((specifier) =>
|
|
192
|
+
resolver.resolveImport(
|
|
193
|
+
sourceFile.filePath,
|
|
194
|
+
specifier.importSource,
|
|
195
|
+
specifier.line
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
if (resolutions.length > 0) {
|
|
199
|
+
setResolutionsForFile(
|
|
200
|
+
resolutionsByFile,
|
|
201
|
+
sourceFile.filePath,
|
|
202
|
+
resolutions
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return resolutionsByFile;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const exportedKindForDeclaration = (
|
|
211
|
+
declaration: AstNode
|
|
212
|
+
): WardenExportedSymbolDefinition['kind'] | null => {
|
|
213
|
+
if (declaration.type === 'ClassDeclaration') {
|
|
214
|
+
return 'class';
|
|
215
|
+
}
|
|
216
|
+
if (declaration.type === 'FunctionDeclaration') {
|
|
217
|
+
return 'function';
|
|
218
|
+
}
|
|
219
|
+
if (
|
|
220
|
+
declaration.type === 'EnumDeclaration' ||
|
|
221
|
+
declaration.type === 'TSEnumDeclaration'
|
|
222
|
+
) {
|
|
223
|
+
return 'enum';
|
|
224
|
+
}
|
|
225
|
+
if (
|
|
226
|
+
declaration.type === 'InterfaceDeclaration' ||
|
|
227
|
+
declaration.type === 'TSInterfaceDeclaration'
|
|
228
|
+
) {
|
|
229
|
+
return 'interface';
|
|
230
|
+
}
|
|
231
|
+
if (declaration.type === 'TSTypeAliasDeclaration') {
|
|
232
|
+
return 'type';
|
|
233
|
+
}
|
|
234
|
+
if (isVariableDeclaration(declaration)) {
|
|
235
|
+
return 'const';
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const publicExportTargetWorkspacesByPath = (
|
|
241
|
+
publicWorkspaces: ReadonlyMap<string, WardenPublicWorkspace>
|
|
242
|
+
): ReadonlyMap<string, WardenPublicWorkspace> => {
|
|
243
|
+
const workspacesByTargetPath = new Map<string, WardenPublicWorkspace>();
|
|
244
|
+
for (const workspace of publicWorkspaces.values()) {
|
|
245
|
+
for (const target of Object.values(workspace.exportTargets ?? {})) {
|
|
246
|
+
workspacesByTargetPath.set(normalizeRealPath(target), workspace);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return workspacesByTargetPath;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const readNameNode = (node: AstNode | undefined): string | null => {
|
|
253
|
+
if (!node) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
if (node.type === 'Identifier') {
|
|
257
|
+
return getNodeName(node) ?? null;
|
|
258
|
+
}
|
|
259
|
+
if (node.type === 'Literal' || node.type === 'StringLiteral') {
|
|
260
|
+
const value = getNodeValue(node);
|
|
261
|
+
return typeof value === 'string' ? value : null;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const exportedSpecifierKind = (
|
|
267
|
+
statement: AstNode,
|
|
268
|
+
specifier: AstNode
|
|
269
|
+
): WardenExportedSymbolDefinition['kind'] =>
|
|
270
|
+
getNodeExportKind(statement) === 'type' ||
|
|
271
|
+
getNodeExportKind(specifier) === 'type'
|
|
272
|
+
? 'type'
|
|
273
|
+
: 'export';
|
|
274
|
+
|
|
275
|
+
const reexportedDefinitions = ({
|
|
276
|
+
filePath,
|
|
277
|
+
sourceCode,
|
|
278
|
+
statement,
|
|
279
|
+
workspace,
|
|
280
|
+
}: {
|
|
281
|
+
readonly filePath: string;
|
|
282
|
+
readonly sourceCode: string;
|
|
283
|
+
readonly statement: AstNode;
|
|
284
|
+
readonly workspace: WardenPublicWorkspace;
|
|
285
|
+
}): readonly WardenExportedSymbolDefinition[] => {
|
|
286
|
+
const specifiers = getNodeSpecifiers(statement) ?? [];
|
|
287
|
+
return specifiers.flatMap((specifier) => {
|
|
288
|
+
if (specifier.type !== 'ExportSpecifier') {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
const exported = getNodeExported(specifier);
|
|
292
|
+
const local = getNodeLocal(specifier);
|
|
293
|
+
const name = readNameNode(exported) ?? readNameNode(local);
|
|
294
|
+
return name
|
|
295
|
+
? [
|
|
296
|
+
{
|
|
297
|
+
filePath,
|
|
298
|
+
kind: exportedSpecifierKind(statement, specifier),
|
|
299
|
+
line: offsetToLine(sourceCode, specifier.start),
|
|
300
|
+
name,
|
|
301
|
+
workspaceName: workspace.name,
|
|
302
|
+
workspaceRoot: workspace.rootDir,
|
|
303
|
+
} satisfies WardenExportedSymbolDefinition,
|
|
304
|
+
]
|
|
305
|
+
: [];
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const namedDeclarationDefinitions = ({
|
|
310
|
+
declaration,
|
|
311
|
+
filePath,
|
|
312
|
+
sourceCode,
|
|
313
|
+
workspace,
|
|
314
|
+
}: {
|
|
315
|
+
readonly declaration: AstNode;
|
|
316
|
+
readonly filePath: string;
|
|
317
|
+
readonly sourceCode: string;
|
|
318
|
+
readonly workspace: WardenPublicWorkspace;
|
|
319
|
+
}): readonly WardenExportedSymbolDefinition[] => {
|
|
320
|
+
const kind = exportedKindForDeclaration(declaration);
|
|
321
|
+
if (!kind) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (isVariableDeclaration(declaration)) {
|
|
326
|
+
return getNodeDeclarations(declaration).flatMap((declarator) => {
|
|
327
|
+
const name = getNodeName(getNodeId(declarator));
|
|
328
|
+
return name
|
|
329
|
+
? [
|
|
330
|
+
{
|
|
331
|
+
filePath,
|
|
332
|
+
kind,
|
|
333
|
+
line: offsetToLine(sourceCode, declarator.start),
|
|
334
|
+
name,
|
|
335
|
+
workspaceName: workspace.name,
|
|
336
|
+
workspaceRoot: workspace.rootDir,
|
|
337
|
+
} satisfies WardenExportedSymbolDefinition,
|
|
338
|
+
]
|
|
339
|
+
: [];
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!isDeclarationWithId(declaration)) {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const name = getNodeName(getNodeId(declaration));
|
|
348
|
+
return name
|
|
349
|
+
? [
|
|
350
|
+
{
|
|
351
|
+
filePath,
|
|
352
|
+
kind,
|
|
353
|
+
line: offsetToLine(sourceCode, declaration.start),
|
|
354
|
+
name,
|
|
355
|
+
workspaceName: workspace.name,
|
|
356
|
+
workspaceRoot: workspace.rootDir,
|
|
357
|
+
} satisfies WardenExportedSymbolDefinition,
|
|
358
|
+
]
|
|
359
|
+
: [];
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const collectExportedSymbolDefinitionsForFile = (
|
|
363
|
+
sourceFile: WardenProjectContextSourceFile,
|
|
364
|
+
publicExportTargetWorkspaces: ReadonlyMap<string, WardenPublicWorkspace>
|
|
365
|
+
): readonly WardenExportedSymbolDefinition[] => {
|
|
366
|
+
if (sourceFile.kind !== 'typescript') {
|
|
367
|
+
return [];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const workspace = publicExportTargetWorkspaces.get(
|
|
371
|
+
normalizeRealPath(sourceFile.filePath)
|
|
372
|
+
);
|
|
373
|
+
if (!workspace) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const ast = parse(sourceFile.filePath, sourceFile.sourceCode);
|
|
378
|
+
if (!ast) {
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return getNodeBodyStatements(ast).flatMap((statement) => {
|
|
383
|
+
if (!isExportNamedDeclaration(statement)) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
if (getNodeSource(statement) || getNodeSpecifiers(statement)?.length) {
|
|
387
|
+
return reexportedDefinitions({
|
|
388
|
+
filePath: sourceFile.filePath,
|
|
389
|
+
sourceCode: sourceFile.sourceCode,
|
|
390
|
+
statement,
|
|
391
|
+
workspace,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
const declaration = getNodeDeclaration(statement);
|
|
395
|
+
return declaration
|
|
396
|
+
? namedDeclarationDefinitions({
|
|
397
|
+
declaration,
|
|
398
|
+
filePath: sourceFile.filePath,
|
|
399
|
+
sourceCode: sourceFile.sourceCode,
|
|
400
|
+
workspace,
|
|
401
|
+
})
|
|
402
|
+
: [];
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
export const collectProjectExportedSymbolDefinitions = ({
|
|
407
|
+
publicWorkspaces: providedPublicWorkspaces,
|
|
408
|
+
rootDir,
|
|
409
|
+
sourceFiles,
|
|
410
|
+
}: {
|
|
411
|
+
readonly publicWorkspaces?: ReadonlyMap<string, WardenPublicWorkspace>;
|
|
412
|
+
readonly rootDir: string;
|
|
413
|
+
readonly sourceFiles: readonly WardenProjectContextSourceFile[];
|
|
414
|
+
}): ReadonlyMap<string, readonly WardenExportedSymbolDefinition[]> => {
|
|
415
|
+
const publicWorkspaces =
|
|
416
|
+
providedPublicWorkspaces ?? collectPublicWorkspaces(rootDir);
|
|
417
|
+
const publicExportTargetWorkspaces =
|
|
418
|
+
publicExportTargetWorkspacesByPath(publicWorkspaces);
|
|
419
|
+
const definitionsByName = new Map<string, WardenExportedSymbolDefinition[]>();
|
|
420
|
+
|
|
421
|
+
for (const sourceFile of sourceFiles) {
|
|
422
|
+
for (const definition of collectExportedSymbolDefinitionsForFile(
|
|
423
|
+
sourceFile,
|
|
424
|
+
publicExportTargetWorkspaces
|
|
425
|
+
)) {
|
|
426
|
+
const existing = definitionsByName.get(definition.name) ?? [];
|
|
427
|
+
existing.push(definition);
|
|
428
|
+
definitionsByName.set(definition.name, existing);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return new Map(
|
|
433
|
+
[...definitionsByName.entries()].map(([name, definitions]) => [
|
|
434
|
+
name,
|
|
435
|
+
definitions.toSorted(
|
|
436
|
+
(left, right) =>
|
|
437
|
+
left.workspaceName.localeCompare(right.workspaceName) ||
|
|
438
|
+
left.filePath.localeCompare(right.filePath) ||
|
|
439
|
+
left.line - right.line
|
|
440
|
+
),
|
|
441
|
+
])
|
|
442
|
+
);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
export { collectPublicWorkspaces };
|
|
446
|
+
export type { WardenPublicWorkspace } from './workspaces.js';
|