@ontrails/warden 1.0.0-beta.39 → 1.0.0-beta.41
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 +125 -0
- package/README.md +5 -5
- package/package.json +10 -12
- package/src/cli.ts +49 -44
- package/src/command.ts +2 -2
- package/src/drift.ts +4 -3
- package/src/index.ts +3 -2
- package/src/project-context.ts +3 -3
- package/src/resolve.ts +2 -2
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +12 -16
- package/src/rules/cli-command-route-coherence.ts +1 -1
- package/src/rules/composes-declarations.ts +15 -13
- package/src/rules/context-no-surface-types.ts +4 -3
- package/src/rules/dead-internal-trail.ts +3 -3
- package/src/rules/dead-public-trail.ts +3 -3
- package/src/rules/draft-file-marking.ts +4 -9
- package/src/rules/draft-visible-debt.ts +3 -8
- package/src/rules/duplicate-public-contract.ts +50 -5
- package/src/rules/{contour-exists.ts → entity-exists.ts} +67 -64
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +2 -2
- package/src/rules/example-valid.ts +84 -84
- package/src/rules/fires-declarations.ts +27 -22
- package/src/rules/governed-symbol-residue.ts +339 -32
- package/src/rules/implementation-returns-result.ts +8 -8
- package/src/rules/incomplete-accessor-for-standard-op.ts +15 -15
- package/src/rules/incomplete-crud.ts +42 -40
- package/src/rules/index.ts +9 -6
- package/src/rules/intent-propagation.ts +3 -5
- package/src/rules/layer-field-name-drift.ts +2 -2
- package/src/rules/library-projection-coherence.ts +2 -2
- package/src/rules/metadata.ts +37 -15
- package/src/rules/missing-reconcile.ts +3 -4
- package/src/rules/missing-visibility.ts +2 -1
- package/src/rules/no-destructured-compose.ts +14 -12
- package/src/rules/no-direct-implementation-call.ts +12 -7
- package/src/rules/no-legacy-cli-alias-export.ts +2 -2
- package/src/rules/no-native-error-result.ts +2 -2
- package/src/rules/no-redundant-result-error-wrap.ts +10 -10
- package/src/rules/no-sync-result-assumption.ts +84 -78
- package/src/rules/no-throw-in-detour-recover.ts +2 -2
- package/src/rules/no-throw-in-implementation.ts +13 -7
- package/src/rules/no-top-level-surface.ts +2 -2
- package/src/rules/on-references-exist.ts +3 -3
- package/src/rules/orphaned-signal.ts +4 -5
- package/src/rules/owner-projection-parity.ts +2 -2
- package/src/rules/public-export-example-coverage.ts +10 -4
- package/src/rules/public-internal-deep-imports.ts +2 -3
- package/src/rules/read-intent-fires.ts +4 -3
- package/src/rules/reference-exists.ts +20 -21
- package/src/rules/registry-names.ts +6 -4
- package/src/rules/resolved-import-boundary.ts +1 -1
- package/src/rules/resource-declarations.ts +21 -17
- package/src/rules/resource-exists.ts +4 -2
- package/src/rules/resource-id-grammar.ts +2 -2
- package/src/rules/resource-mock-coverage.ts +2 -2
- package/src/rules/retired-vocabulary.ts +242 -5
- 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/static-resource-accessor-preference.ts +15 -11
- package/src/rules/surface-overlay-coherence.ts +1 -1
- package/src/rules/surface-trailhead-coherence.ts +2 -2
- package/src/rules/trail-fork-coaching.ts +27 -18
- package/src/rules/trail-versioning-source.ts +16 -12
- package/src/rules/trail-versioning-topo.ts +2 -2
- package/src/rules/trailhead-override-divergence.ts +2 -2
- package/src/rules/types.ts +7 -7
- package/src/rules/unmaterialized-activation-source.ts +1 -0
- package/src/rules/unreachable-detour-shadowing.ts +2 -2
- package/src/rules/valid-describe-refs.ts +2 -2
- package/src/rules/warden-export-symmetry.ts +3 -3
- package/src/rules/warden-rules-use-ast.ts +12 -17
- package/src/trails/activation-orphan.trail.ts +5 -5
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +6 -6
- package/src/trails/cli-command-route-coherence.trail.ts +2 -2
- package/src/trails/composes-declarations.trail.ts +1 -1
- package/src/trails/context-no-surface-types.trail.ts +1 -1
- package/src/trails/dead-internal-trail.trail.ts +2 -2
- package/src/trails/dead-public-trail.trail.ts +1 -1
- package/src/trails/deprecation-without-guidance.trail.ts +1 -1
- package/src/trails/duplicate-public-contract.trail.ts +3 -3
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/example-valid.trail.ts +3 -3
- package/src/trails/fires-declarations.trail.ts +1 -1
- package/src/trails/{fork-without-preserved-blaze.trail.ts → fork-without-preserved-implementation.trail.ts} +4 -4
- package/src/trails/implementation-returns-result.trail.ts +1 -1
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +4 -4
- package/src/trails/index.ts +3 -2
- package/src/trails/intent-propagation.trail.ts +2 -2
- package/src/trails/library-projection-coherence.trail.ts +2 -2
- package/src/trails/marker-schema-unsupported.trail.ts +1 -1
- package/src/trails/missing-visibility.trail.ts +1 -1
- package/src/trails/no-destructured-compose.trail.ts +5 -5
- package/src/trails/no-direct-implementation-call.trail.ts +1 -1
- package/src/trails/no-redundant-result-error-wrap.trail.ts +2 -2
- package/src/trails/no-sync-result-assumption.trail.ts +2 -2
- package/src/trails/no-throw-in-detour-recover.trail.ts +1 -1
- package/src/trails/no-throw-in-implementation.trail.ts +1 -1
- package/src/trails/on-references-exist.trail.ts +1 -1
- package/src/trails/pending-force.trail.ts +1 -1
- package/src/trails/permit-governance.trail.ts +2 -2
- package/src/trails/prefer-schema-inference.trail.ts +1 -1
- package/src/trails/public-output-schema.trail.ts +2 -2
- package/src/trails/public-union-output-discriminants.trail.ts +1 -1
- package/src/trails/read-intent-fires.trail.ts +1 -1
- package/src/trails/reference-exists.trail.ts +5 -5
- package/src/trails/resource-declarations.trail.ts +1 -1
- package/src/trails/resource-exists.trail.ts +1 -1
- package/src/trails/run.ts +4 -6
- package/src/trails/scheduled-destroy-intent.trail.ts +2 -2
- package/src/trails/schema.ts +9 -9
- package/src/trails/signal-graph-coaching.trail.ts +2 -2
- package/src/trails/static-resource-accessor-preference.trail.ts +1 -1
- package/src/trails/surface-overlay-coherence.trail.ts +1 -1
- package/src/trails/trail-fork-coaching.trail.ts +1 -1
- package/src/trails/unmaterialized-activation-source.trail.ts +5 -5
- package/src/trails/valid-detour-contract.trail.ts +1 -1
- package/src/trails/version-gap.trail.ts +1 -1
- package/src/trails/version-pinned-compose.trail.ts +1 -1
- package/src/trails/version-without-examples.trail.ts +1 -1
- package/src/trails/warden-rules-use-ast.trail.ts +2 -2
- package/src/trails/webhook-route-collision.trail.ts +2 -2
- package/src/trails/wrap-rule.ts +17 -17
- package/src/ast.ts +0 -137
- package/src/rules/ast.ts +0 -4143
- package/src/rules/contour-ids.ts +0 -15
- package/src/trails/contour-exists.trail.ts +0 -21
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/** Warden-private composition helpers. */
|
|
2
|
+
|
|
3
|
+
import { intentValues } from '@ontrails/core';
|
|
4
|
+
import type { Intent } from '@ontrails/core';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
buildFrameworkNamespaceContext,
|
|
8
|
+
deriveConstString,
|
|
9
|
+
extractBindingName,
|
|
10
|
+
extractTrailDefinition,
|
|
11
|
+
findConfigProperty,
|
|
12
|
+
findTrailDefinitions,
|
|
13
|
+
getStringValue,
|
|
14
|
+
identifierName,
|
|
15
|
+
isStringLiteral,
|
|
16
|
+
walk,
|
|
17
|
+
} from '@ontrails/source';
|
|
18
|
+
import type { AstNode } from '@ontrails/source';
|
|
19
|
+
|
|
20
|
+
/** Collect `const foo = trail('id', ...)` bindings from a parsed file. */
|
|
21
|
+
export const collectNamedTrailIds = (
|
|
22
|
+
ast: AstNode
|
|
23
|
+
): ReadonlyMap<string, string> => {
|
|
24
|
+
const ids = new Map<string, string>();
|
|
25
|
+
const context = buildFrameworkNamespaceContext(ast);
|
|
26
|
+
|
|
27
|
+
walk(ast, (node) => {
|
|
28
|
+
if (node.type !== 'VariableDeclarator') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { id, init } = node as unknown as {
|
|
33
|
+
readonly id?: AstNode;
|
|
34
|
+
readonly init?: AstNode;
|
|
35
|
+
};
|
|
36
|
+
if (!init) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const def = extractTrailDefinition(init, context);
|
|
41
|
+
const name = extractBindingName(id);
|
|
42
|
+
if (def?.kind === 'trail' && name) {
|
|
43
|
+
ids.set(name, def.id);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return ids;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Extract the raw `composes: [...]` array elements from a trail config. */
|
|
51
|
+
export const getComposeElements = (config: AstNode): readonly AstNode[] => {
|
|
52
|
+
const composesProp = findConfigProperty(config, 'composes');
|
|
53
|
+
if (!composesProp) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const arrayNode = composesProp.value;
|
|
58
|
+
if (!arrayNode || (arrayNode as AstNode).type !== 'ArrayExpression') {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const elements = (arrayNode as AstNode)['elements'] as
|
|
63
|
+
| readonly AstNode[]
|
|
64
|
+
| undefined;
|
|
65
|
+
return elements ?? [];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a single `composes: [...]` element to its target trail ID.
|
|
70
|
+
*
|
|
71
|
+
* Handles string literals, identifier references (via `namedTrailIds` map or
|
|
72
|
+
* `const NAME = '...'` resolution), and inline `trail(...)` call expressions.
|
|
73
|
+
*/
|
|
74
|
+
export const deriveComposeElementId = (
|
|
75
|
+
element: AstNode,
|
|
76
|
+
sourceCode: string,
|
|
77
|
+
namedTrailIds: ReadonlyMap<string, string>
|
|
78
|
+
): string | null => {
|
|
79
|
+
if (isStringLiteral(element)) {
|
|
80
|
+
return getStringValue(element);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (element.type === 'Identifier') {
|
|
84
|
+
const name = identifierName(element);
|
|
85
|
+
return name
|
|
86
|
+
? (namedTrailIds.get(name) ?? deriveConstString(name, sourceCode))
|
|
87
|
+
: null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inlineDef = extractTrailDefinition(element);
|
|
91
|
+
return inlineDef?.kind === 'trail' ? inlineDef.id : null;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Collect all trail IDs referenced by a single trail definition's
|
|
96
|
+
* `composes: [...]` array, deduplicated.
|
|
97
|
+
*/
|
|
98
|
+
export const extractDefinitionComposeTargetIds = (
|
|
99
|
+
config: AstNode,
|
|
100
|
+
sourceCode: string,
|
|
101
|
+
namedTrailIds: ReadonlyMap<string, string>
|
|
102
|
+
): readonly string[] => [
|
|
103
|
+
...new Set(
|
|
104
|
+
getComposeElements(config).flatMap((element) => {
|
|
105
|
+
const id = deriveComposeElementId(element, sourceCode, namedTrailIds);
|
|
106
|
+
return id ? [id] : [];
|
|
107
|
+
})
|
|
108
|
+
),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/** Collect all trail IDs referenced by declared `composes: [...]` arrays. */
|
|
112
|
+
export const collectComposeTargetTrailIds = (
|
|
113
|
+
ast: AstNode,
|
|
114
|
+
sourceCode: string
|
|
115
|
+
): ReadonlySet<string> => {
|
|
116
|
+
const ids = new Set<string>();
|
|
117
|
+
const namedTrailIds = collectNamedTrailIds(ast);
|
|
118
|
+
|
|
119
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
120
|
+
if (def.kind !== 'trail') {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const id of extractDefinitionComposeTargetIds(
|
|
125
|
+
def.config,
|
|
126
|
+
sourceCode,
|
|
127
|
+
namedTrailIds
|
|
128
|
+
)) {
|
|
129
|
+
ids.add(id);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return ids;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const INTENT_VALUE_SET = new Set<string>(intentValues);
|
|
137
|
+
const DEFAULT_INTENT: Intent = 'write';
|
|
138
|
+
|
|
139
|
+
const normalizeTrailIntent = (value: string): Intent =>
|
|
140
|
+
INTENT_VALUE_SET.has(value) ? (value as Intent) : DEFAULT_INTENT;
|
|
141
|
+
|
|
142
|
+
const extractTrailIntent = (config: AstNode): Intent => {
|
|
143
|
+
const intentProp = findConfigProperty(config, 'intent');
|
|
144
|
+
if (!intentProp || !isStringLiteral(intentProp.value as AstNode)) {
|
|
145
|
+
return DEFAULT_INTENT;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const value = getStringValue(intentProp.value as AstNode);
|
|
149
|
+
return value ? normalizeTrailIntent(value) : DEFAULT_INTENT;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** Collect the normalized intent for every trail definition in a parsed file. */
|
|
153
|
+
export const collectTrailIntentsById = (
|
|
154
|
+
ast: AstNode
|
|
155
|
+
): ReadonlyMap<string, Intent> => {
|
|
156
|
+
const intents = new Map<string, Intent>();
|
|
157
|
+
|
|
158
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
159
|
+
if (def.kind === 'trail') {
|
|
160
|
+
intents.set(def.id, extractTrailIntent(def.config));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return intents;
|
|
165
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/** Warden-private draft policy helpers. */
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { DRAFT_ID_PREFIX } from '@ontrails/core';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
getStringValue,
|
|
11
|
+
identifierName,
|
|
12
|
+
isStringLiteral,
|
|
13
|
+
walk,
|
|
14
|
+
} from '@ontrails/source';
|
|
15
|
+
import type { AstNode } from '@ontrails/source';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Names of framework constants whose value is a draft-marker prefix literal.
|
|
19
|
+
*
|
|
20
|
+
* String literals that initialize a `const` declaration with one of these
|
|
21
|
+
* names are treated as the framework's own draft-marker declarations, not as
|
|
22
|
+
* draft-id usage. This list is intentionally small and explicit — adding a
|
|
23
|
+
* new framework draft-prefix constant requires updating this set.
|
|
24
|
+
*/
|
|
25
|
+
export const FRAMEWORK_DRAFT_PREFIX_CONSTANT_NAMES: ReadonlySet<string> =
|
|
26
|
+
new Set(['DRAFT_ID_PREFIX', 'DRAFT_FILE_PREFIX']);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Exact string literal value allowed for framework draft-prefix constant
|
|
30
|
+
* declarations. Tightens the exemption so a future framework file cannot
|
|
31
|
+
* redeclare `DRAFT_ID_PREFIX = '_draft.something-else'` and accidentally
|
|
32
|
+
* suppress its own draft-id diagnostic.
|
|
33
|
+
*/
|
|
34
|
+
const FRAMEWORK_DRAFT_PREFIX_LITERAL = DRAFT_ID_PREFIX;
|
|
35
|
+
|
|
36
|
+
interface PackageJsonWithName {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const FRAMEWORK_DRAFT_PREFIX_PACKAGES: ReadonlySet<string> = new Set([
|
|
41
|
+
'@ontrails/core',
|
|
42
|
+
'@ontrails/warden',
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const isPackageJsonWithName = (value: unknown): value is PackageJsonWithName =>
|
|
46
|
+
typeof value === 'object' &&
|
|
47
|
+
value !== null &&
|
|
48
|
+
typeof (value as { name?: unknown }).name === 'string';
|
|
49
|
+
|
|
50
|
+
const readPackageJsonName = (packageJsonPath: string): string | null => {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
53
|
+
return isPackageJsonWithName(parsed) ? parsed.name : null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const frameworkDraftPackageRoot = (filePath: string): string | null => {
|
|
60
|
+
const resolvedPath = resolve(filePath);
|
|
61
|
+
if (basename(resolvedPath) !== 'draft.ts') {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const sourceDir = dirname(resolvedPath);
|
|
66
|
+
if (basename(sourceDir) !== 'src') {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const packageRoot = dirname(sourceDir);
|
|
71
|
+
if (!existsSync(join(packageRoot, 'package.json'))) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return packageRoot;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Fallback exemption when framework files are consumed from a different install path. */
|
|
79
|
+
const isFrameworkDraftPrefixSourceFile = (filePath: string): boolean => {
|
|
80
|
+
const root = frameworkDraftPackageRoot(filePath);
|
|
81
|
+
if (!root) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const packageName = readPackageJsonName(join(root, 'package.json'));
|
|
85
|
+
return (
|
|
86
|
+
packageName !== null && FRAMEWORK_DRAFT_PREFIX_PACKAGES.has(packageName)
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Absolute paths of the two framework files allowed to declare the
|
|
92
|
+
* draft-prefix constants. Anchored against the rule module's own URL so the
|
|
93
|
+
* exemption is scoped to this package's real on-disk location — a consumer
|
|
94
|
+
* repository that happens to declare `const DRAFT_ID_PREFIX = '_draft.leak'`
|
|
95
|
+
* anywhere else cannot hide a genuine leak by matching the identifier name.
|
|
96
|
+
*
|
|
97
|
+
* The two framework files are:
|
|
98
|
+
* - `packages/core/src/draft.ts` (defines `DRAFT_ID_PREFIX`)
|
|
99
|
+
* - `packages/warden/src/draft.ts` (defines `DRAFT_FILE_PREFIX`)
|
|
100
|
+
*/
|
|
101
|
+
const FRAMEWORK_DRAFT_CONSTANT_FILES: ReadonlySet<string> = new Set([
|
|
102
|
+
resolve(
|
|
103
|
+
fileURLToPath(new URL('../../../../core/src/draft.ts', import.meta.url))
|
|
104
|
+
),
|
|
105
|
+
resolve(fileURLToPath(new URL('../../draft.ts', import.meta.url))),
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Collect the source offsets of string literals that initialize a framework
|
|
110
|
+
* draft-prefix constant declaration (e.g. `export const DRAFT_ID_PREFIX =
|
|
111
|
+
* '_draft.'`). Used by draft-awareness rules to skip their own marker
|
|
112
|
+
* constants.
|
|
113
|
+
*
|
|
114
|
+
* Exemption is gated on all three of:
|
|
115
|
+
* 1. The file is one of the two known framework draft files, or its package
|
|
116
|
+
* root `package.json` name is `@ontrails/core` or `@ontrails/warden`.
|
|
117
|
+
* 2. The declaration name is `DRAFT_ID_PREFIX` or `DRAFT_FILE_PREFIX`.
|
|
118
|
+
* 3. The string literal value is exactly `'_draft.'`.
|
|
119
|
+
*
|
|
120
|
+
* A consumer file that reuses one of these identifier names cannot hide a
|
|
121
|
+
* `_draft.*` leak — the path gate rejects it outright.
|
|
122
|
+
*/
|
|
123
|
+
export const collectFrameworkDraftPrefixConstantOffsets = (
|
|
124
|
+
ast: AstNode,
|
|
125
|
+
filePath: string
|
|
126
|
+
): ReadonlySet<number> => {
|
|
127
|
+
const offsets = new Set<number>();
|
|
128
|
+
|
|
129
|
+
const resolvedPath = resolve(filePath);
|
|
130
|
+
if (
|
|
131
|
+
!FRAMEWORK_DRAFT_CONSTANT_FILES.has(resolvedPath) &&
|
|
132
|
+
!isFrameworkDraftPrefixSourceFile(resolvedPath)
|
|
133
|
+
) {
|
|
134
|
+
return offsets;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
walk(ast, (node) => {
|
|
138
|
+
if (node.type !== 'VariableDeclarator') {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { id, init } = node as unknown as {
|
|
143
|
+
readonly id?: AstNode;
|
|
144
|
+
readonly init?: AstNode;
|
|
145
|
+
};
|
|
146
|
+
const name = identifierName(id);
|
|
147
|
+
if (
|
|
148
|
+
!name ||
|
|
149
|
+
!FRAMEWORK_DRAFT_PREFIX_CONSTANT_NAMES.has(name) ||
|
|
150
|
+
!init ||
|
|
151
|
+
!isStringLiteral(init)
|
|
152
|
+
) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (getStringValue(init) !== FRAMEWORK_DRAFT_PREFIX_LITERAL) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
offsets.add(init.start);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return offsets;
|
|
164
|
+
};
|