@animus-ui/extract 0.1.11 → 0.1.12
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/README.md +47 -0
- package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
- package/crates/extract-v2/index.d.ts +17 -98
- package/dist/index.cjs +11 -6
- package/dist/index.mjs +2 -2
- package/dist/pipeline/analyze-project-args.d.ts +0 -12
- package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
- package/dist/pipeline/assemble-stylesheet.d.ts +0 -35
- package/dist/pipeline/assemble-stylesheet.d.ts.map +1 -1
- package/dist/pipeline/asset-placeholders.d.ts +0 -25
- package/dist/pipeline/asset-placeholders.d.ts.map +1 -1
- package/dist/pipeline/content-hash.d.ts +0 -7
- package/dist/pipeline/content-hash.d.ts.map +1 -1
- package/dist/pipeline/core-options.d.ts +27 -93
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/correlate-external-tokens.d.ts +10 -18
- package/dist/pipeline/correlate-external-tokens.d.ts.map +1 -1
- package/dist/pipeline/discover-files.d.ts +1 -9
- package/dist/pipeline/discover-files.d.ts.map +1 -1
- package/dist/pipeline/discover-packages.d.ts +18 -154
- package/dist/pipeline/discover-packages.d.ts.map +1 -1
- package/dist/pipeline/dynamic-prop-config.d.ts +4 -19
- package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +16 -52
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/engine-retirement.d.ts +0 -19
- package/dist/pipeline/engine-retirement.d.ts.map +1 -1
- package/dist/pipeline/error-diagnostics.d.ts +0 -24
- package/dist/pipeline/error-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +9 -8
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/internal-wire.d.ts +2 -0
- package/dist/pipeline/internal-wire.d.ts.map +1 -0
- package/dist/pipeline/manifest-diagnostics.d.ts +32 -37
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/manifest-schema.d.ts +117 -0
- package/dist/pipeline/manifest-schema.d.ts.map +1 -0
- package/dist/pipeline/mdx-preprocessor.d.ts +9 -33
- package/dist/pipeline/mdx-preprocessor.d.ts.map +1 -1
- package/dist/pipeline/path-aliases.d.ts +0 -19
- package/dist/pipeline/path-aliases.d.ts.map +1 -1
- package/dist/pipeline/post-process-css.d.ts +0 -8
- package/dist/pipeline/post-process-css.d.ts.map +1 -1
- package/dist/pipeline/prefix.d.ts +3 -10
- package/dist/pipeline/prefix.d.ts.map +1 -1
- package/dist/pipeline/replacement-plans.d.ts +10 -34
- package/dist/pipeline/replacement-plans.d.ts.map +1 -1
- package/dist/pipeline/resolve-asset.d.ts +4 -7
- package/dist/pipeline/resolve-asset.d.ts.map +1 -1
- package/dist/pipeline/run-analysis.d.ts +15 -27
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/source-corpus.d.ts +25 -0
- package/dist/pipeline/source-corpus.d.ts.map +1 -0
- package/dist/pipeline/source-identity.d.ts +23 -53
- package/dist/pipeline/source-identity.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +48 -53
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/static-css.d.ts +4 -13
- package/dist/pipeline/static-css.d.ts.map +1 -1
- package/dist/pipeline/structural-self-check.d.ts +0 -20
- package/dist/pipeline/structural-self-check.d.ts.map +1 -1
- package/dist/pipeline/svelte-source-adapter.d.ts +5 -12
- package/dist/pipeline/svelte-source-adapter.d.ts.map +1 -1
- package/dist/pipeline/system-config.d.ts +15 -32
- package/dist/pipeline/system-config.d.ts.map +1 -1
- package/dist/pipeline/system-props-module.d.ts +0 -15
- package/dist/pipeline/system-props-module.d.ts.map +1 -1
- package/dist/pipeline/timing-waterfall.d.ts +0 -11
- package/dist/pipeline/timing-waterfall.d.ts.map +1 -1
- package/dist/pipeline/tsconfig-paths.d.ts +2 -3
- package/dist/pipeline/tsconfig-paths.d.ts.map +1 -1
- package/dist/pipeline/unit-fallback.d.ts +0 -5
- package/dist/pipeline/unit-fallback.d.ts.map +1 -1
- package/dist/pipeline/utils.d.ts +1 -1
- package/dist/pipeline/utils.d.ts.map +1 -1
- package/dist/pipeline/watch-keys.d.ts +0 -10
- package/dist/pipeline/watch-keys.d.ts.map +1 -1
- package/dist/{pipeline-lkbOoyAr.mjs → pipeline-DeEENy5C.mjs} +1940 -2263
- package/dist/{pipeline-ojNeABi0.cjs → pipeline-x1yBZ2UP.cjs} +1970 -2269
- package/dist/session/extraction-session.d.ts +110 -316
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session/index.d.ts +6 -20
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/published-set.d.ts +45 -25
- package/dist/session/published-set.d.ts.map +1 -1
- package/dist/session/resolve-packages.d.ts +6 -7
- package/dist/session/resolve-packages.d.ts.map +1 -1
- package/dist/session/session-paths.d.ts +38 -72
- package/dist/session/session-paths.d.ts.map +1 -1
- package/dist/session/singleton.d.ts +32 -71
- package/dist/session/singleton.d.ts.map +1 -1
- package/dist/session/timing.d.ts +0 -5
- package/dist/session/timing.d.ts.map +1 -1
- package/dist/session/turbopack-orchestrator.d.ts +17 -33
- package/dist/session/turbopack-orchestrator.d.ts.map +1 -1
- package/dist/session.cjs +530 -677
- package/dist/session.mjs +517 -672
- package/index-v2.js +0 -9
- package/package.json +9 -8
- package/dist/pipeline/external-keyframes.d.ts +0 -23
- package/dist/pipeline/external-keyframes.d.ts.map +0 -1
|
@@ -15,27 +15,23 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
15
|
}
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
19
19
|
value: mod,
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
22
|
//#endregion
|
|
23
|
+
let node_path = require("node:path");
|
|
24
|
+
node_path = __toESM(node_path);
|
|
23
25
|
let crypto = require("crypto");
|
|
24
26
|
let fs = require("fs");
|
|
25
27
|
let path = require("path");
|
|
26
28
|
let node_fs = require("node:fs");
|
|
27
|
-
let node_path = require("node:path");
|
|
28
|
-
node_path = __toESM(node_path);
|
|
29
29
|
let module$1 = require("module");
|
|
30
30
|
let browserslist = require("browserslist");
|
|
31
31
|
browserslist = __toESM(browserslist);
|
|
32
32
|
let lightningcss = require("lightningcss");
|
|
33
33
|
let _animus_ui_properties = require("@animus-ui/properties");
|
|
34
34
|
//#region pipeline/assemble-stylesheet.ts
|
|
35
|
-
/**
|
|
36
|
-
* The 7 Animus cascade layers. Always prefixed with `anm-` to avoid
|
|
37
|
-
* collision with other frameworks' layer names (e.g., Tailwind's `base`).
|
|
38
|
-
*/
|
|
39
35
|
const ANIMUS_LAYERS = [
|
|
40
36
|
"anm-global",
|
|
41
37
|
"anm-base",
|
|
@@ -45,22 +41,9 @@ const ANIMUS_LAYERS = [
|
|
|
45
41
|
"anm-system",
|
|
46
42
|
"anm-custom"
|
|
47
43
|
];
|
|
48
|
-
/**
|
|
49
|
-
* Build the `@layer` declaration line.
|
|
50
|
-
*
|
|
51
|
-
* When `isCustom` is false (default layers), uses ANIMUS_LAYERS directly.
|
|
52
|
-
* When `isCustom` is true, passes names through as-is — the consumer already
|
|
53
|
-
* wrote the final names including the `anm-` prefixed entries.
|
|
54
|
-
*/
|
|
55
44
|
function buildLayerDeclaration(layers, isCustom) {
|
|
56
45
|
return `@layer ${(isCustom ? layers : [...ANIMUS_LAYERS]).join(", ")};\n`;
|
|
57
46
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Validate that a consumer `layers` array contains all 7 Animus layers
|
|
60
|
-
* in the correct relative order. Consumer layers may be interleaved.
|
|
61
|
-
*
|
|
62
|
-
* @throws Error with descriptive message on violation
|
|
63
|
-
*/
|
|
64
47
|
function validateLayerOrder(layers) {
|
|
65
48
|
const expected = [...ANIMUS_LAYERS];
|
|
66
49
|
let cursor = 0;
|
|
@@ -75,11 +58,6 @@ function validateLayerOrder(layers) {
|
|
|
75
58
|
throw new Error(`[animus-extract] Custom layers have wrong order. Found ${found.join(", ")} but then expected ${missing[0]}. Required order: (${expected.join(" < ")}). You may interleave custom layers but must preserve this subsequence.`);
|
|
76
59
|
}
|
|
77
60
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Strip a leading `@layer ...;` declaration line from CSS if present.
|
|
80
|
-
* The Rust crate embeds this in prod-mode output; we strip it so the
|
|
81
|
-
* shared assembler controls placement.
|
|
82
|
-
*/
|
|
83
61
|
function stripLeadingLayerDeclaration(css) {
|
|
84
62
|
return css.replace(/^@layer\s+[^;{]+;\s*\n?/, "");
|
|
85
63
|
}
|
|
@@ -128,2091 +106,1891 @@ function buildAnalyzeProjectArgs(inputs) {
|
|
|
128
106
|
];
|
|
129
107
|
}
|
|
130
108
|
//#endregion
|
|
131
|
-
//#region pipeline/engine-adapter.ts
|
|
132
|
-
/**
|
|
133
|
-
* Build the engine-agnostic `engineApi()` both plugins call. Returns a function
|
|
134
|
-
* (invoked per call site) so `isV2()` and `loadNativeEngine()` re-evaluate each
|
|
135
|
-
* time — next reads its engine choice and native module dynamically from
|
|
136
|
-
* globalThis.
|
|
137
|
-
*/
|
|
138
|
-
function createV2EngineApi(deps) {
|
|
139
|
-
const { label, isV2, loadNativeEngine, store } = deps;
|
|
140
|
-
return () => {
|
|
141
|
-
if (!isV2()) return loadNativeEngine();
|
|
142
|
-
const native = loadNativeEngine();
|
|
143
|
-
return {
|
|
144
|
-
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
145
|
-
scanKeyframesExports: (entryPath, rootDir) => native.scanKeyframesExports(entryPath, rootDir) ?? null,
|
|
146
|
-
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
147
|
-
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson) => {
|
|
148
|
-
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
149
|
-
const sent = /* @__PURE__ */ new Map();
|
|
150
|
-
for (const entry of JSON.parse(filesJson)) sent.set(entry.path, entry.source);
|
|
151
|
-
store.setSentSources(sent);
|
|
152
|
-
const emitterConfig = emitterConfigJson ? JSON.parse(emitterConfigJson) : {};
|
|
153
|
-
store.setEngine(null);
|
|
154
|
-
const config = {
|
|
155
|
-
runtimeImport: emitterConfig.runtime_import ?? void 0,
|
|
156
|
-
cssModuleId: emitterConfig.css_module_id ?? void 0,
|
|
157
|
-
systemPropsModuleId: emitterConfig.system_props_module_id ?? void 0,
|
|
158
|
-
themeJson: scalesJson,
|
|
159
|
-
variableMapJson,
|
|
160
|
-
contextualVarsJson: contextualVarsJson ?? void 0,
|
|
161
|
-
configJson: propConfigJson,
|
|
162
|
-
groupRegistryJson,
|
|
163
|
-
selectorAliasesJson: selectorAliasesJson ?? void 0,
|
|
164
|
-
conditionAliasesJson: conditionAliasesJson ?? void 0,
|
|
165
|
-
globalStyleBlocksJson: globalStyleBlocksJson ?? void 0,
|
|
166
|
-
keyframesJson: keyframesJson ?? void 0,
|
|
167
|
-
packageResolutionJson: packageResolutionJson ?? void 0,
|
|
168
|
-
pathAliasesJson: pathAliasesJson ?? void 0,
|
|
169
|
-
staticCssJson: staticCssJson ?? void 0,
|
|
170
|
-
externalDirsJson: externalDirsJson ?? void 0,
|
|
171
|
-
transformSourcesJson: transformSourcesJson ?? void 0,
|
|
172
|
-
devMode
|
|
173
|
-
};
|
|
174
|
-
const engine = new native.ExtractEngine(config);
|
|
175
|
-
store.setEngine(engine);
|
|
176
|
-
return engine.analyze(filesJson);
|
|
177
|
-
},
|
|
178
|
-
transformFile: (source, path, _manifest) => {
|
|
179
|
-
const engine = store.getEngine();
|
|
180
|
-
if (!engine) throw new Error(`[${label}] v2 transform before analyze — engine instance not initialized`);
|
|
181
|
-
const sentMap = store.getSentSources();
|
|
182
|
-
if (deps.passThroughUnknownPaths && sentMap && !sentMap.has(path)) return {
|
|
183
|
-
code: source,
|
|
184
|
-
hasComponents: false
|
|
185
|
-
};
|
|
186
|
-
const sent = sentMap?.get(path);
|
|
187
|
-
if (sent !== void 0 && sent !== source && !store.getDriftWarned()) {
|
|
188
|
-
store.setDriftWarned(true);
|
|
189
|
-
console.warn(`[${label}] v2: transform-time source for ${path} differs from analyze-time source — an upstream transform may be reverted`);
|
|
190
|
-
}
|
|
191
|
-
return JSON.parse(engine.transformFile(path));
|
|
192
|
-
},
|
|
193
|
-
clearAnalysisCache: () => {
|
|
194
|
-
const engine = store.getEngine();
|
|
195
|
-
if (engine) engine.clearCache();
|
|
196
|
-
store.setEngine(null);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
//#endregion
|
|
202
|
-
//#region pipeline/engine-retirement.ts
|
|
203
|
-
/**
|
|
204
|
-
* Loud rejection of the retired v1 extraction engine (openspec:
|
|
205
|
-
* retire-extract-v1).
|
|
206
|
-
*
|
|
207
|
-
* v2 is the only engine. Selecting v1 — via a plugin `engine: 'v1'` option or
|
|
208
|
-
* the `ANIMUS_ENGINE=v1` fixture env override — is a build-stopping error, never
|
|
209
|
-
* a silent upgrade to v2 (design D1: a silent flip would let a pinned consumer's
|
|
210
|
-
* build change engines without notice). Both extraction plugins call this before
|
|
211
|
-
* any engine selection so the rejection is uniform and the message is defined
|
|
212
|
-
* once.
|
|
213
|
-
*/
|
|
214
|
-
/** The one canonical retirement message, shared verbatim by both plugins and
|
|
215
|
-
* the env path (tasks.md cross-cutting: define once, reuse). */
|
|
216
|
-
const RETIRED_ENGINE_MESSAGE = "animus: extraction engine 'v1' was retired (openspec: retire-extract-v1) — v2 is the only engine; remove the engine option / ANIMUS_ENGINE override";
|
|
217
|
-
/**
|
|
218
|
-
* Throw the canonical retirement error when v1 is selected through either
|
|
219
|
-
* surface. Accepts the raw engine option as a string because the option type no
|
|
220
|
-
* longer admits `'v1'` — callers cast it so a stale `'v1'` config still fails
|
|
221
|
-
* loud at runtime instead of being silently coerced.
|
|
222
|
-
*/
|
|
223
|
-
function assertNoRetiredEngineSelection(engineOption) {
|
|
224
|
-
if (engineOption === "v1" || process.env.ANIMUS_ENGINE === "v1") throw new Error(RETIRED_ENGINE_MESSAGE);
|
|
225
|
-
}
|
|
226
|
-
//#endregion
|
|
227
109
|
//#region pipeline/content-hash.ts
|
|
228
|
-
/**
|
|
229
|
-
* MD5 content hash used for file-change detection (HMR diffing) by both
|
|
230
|
-
* extraction plugins, and for content-addressing copied asset bytes. The
|
|
231
|
-
* algorithm/encoding is a cross-plugin contract: cache keys written by one
|
|
232
|
-
* build path must compare equal in the next. Accepts a Buffer directly so
|
|
233
|
-
* binary sources hash without an intermediate string copy.
|
|
234
|
-
*/
|
|
235
110
|
function contentHash(source) {
|
|
236
111
|
return (0, crypto.createHash)("md5").update(source).digest("hex");
|
|
237
112
|
}
|
|
238
113
|
//#endregion
|
|
239
|
-
//#region pipeline/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
* the consumer-config singular-authority rule: nothing but branded
|
|
247
|
-
* collections is read from an entry, an entry that fails to evaluate
|
|
248
|
-
* degrades to a coded diagnostic naming it, and a collection whose export
|
|
249
|
-
* name collides keeps the earlier (consumer-first) collection with a coded
|
|
250
|
-
* diagnostic instead of silently reordering names.
|
|
251
|
-
*/
|
|
252
|
-
function mergeExternalKeyframes(scan, consumerKeyframesJson, externalEntries, rootDir) {
|
|
253
|
-
const merged = {};
|
|
254
|
-
if (consumerKeyframesJson) try {
|
|
255
|
-
Object.assign(merged, JSON.parse(consumerKeyframesJson));
|
|
256
|
-
} catch {
|
|
257
|
-
return {
|
|
258
|
-
keyframesJson: consumerKeyframesJson,
|
|
259
|
-
diagnostics: []
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
const diagnostics = [];
|
|
263
|
-
const seenEntries = /* @__PURE__ */ new Set();
|
|
264
|
-
for (const entryPath of externalEntries) {
|
|
265
|
-
if (seenEntries.has(entryPath)) continue;
|
|
266
|
-
seenEntries.add(entryPath);
|
|
267
|
-
let scanned;
|
|
268
|
-
try {
|
|
269
|
-
scanned = scan(entryPath, rootDir);
|
|
270
|
-
} catch (error) {
|
|
271
|
-
diagnostics.push({
|
|
272
|
-
file: entryPath,
|
|
273
|
-
component: "keyframes",
|
|
274
|
-
kind: "warn",
|
|
275
|
-
message: `external package entry failed the keyframes scan — its collections are invisible to extraction: ${String(error)}. Keyframe collections must be reachable from the package's definition entry without evaluating framework re-exports: export them (directly or as a named re-export) from the definition entry, and avoid \`export *\` of framework packages there (${KEYFRAMES_EXTERNAL_ENTRY_FAILED})`,
|
|
276
|
-
code: KEYFRAMES_EXTERNAL_ENTRY_FAILED,
|
|
277
|
-
severity: "warn"
|
|
278
|
-
});
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (!scanned) continue;
|
|
282
|
-
let collections;
|
|
283
|
-
try {
|
|
284
|
-
collections = JSON.parse(scanned);
|
|
285
|
-
} catch {
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
for (const [exportName, collection] of Object.entries(collections)) {
|
|
289
|
-
const existing = merged[exportName];
|
|
290
|
-
if (existing !== void 0) {
|
|
291
|
-
if (JSON.stringify(existing) !== JSON.stringify(collection)) diagnostics.push({
|
|
292
|
-
file: entryPath,
|
|
293
|
-
component: exportName,
|
|
294
|
-
kind: "warn",
|
|
295
|
-
message: `keyframes collection export '${exportName}' collides with an earlier collection of the same name — the earlier one wins (${KEYFRAMES_EXPORT_COLLISION})`,
|
|
296
|
-
code: KEYFRAMES_EXPORT_COLLISION,
|
|
297
|
-
severity: "warn"
|
|
298
|
-
});
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
merged[exportName] = collection;
|
|
302
|
-
}
|
|
114
|
+
//#region pipeline/internal-wire.ts
|
|
115
|
+
function parseInternalWire(json, wire) {
|
|
116
|
+
let parsed;
|
|
117
|
+
try {
|
|
118
|
+
parsed = JSON.parse(json);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw new TypeError(`[animus] ${wire} is not valid JSON. animus produces this wire, so this is an engine bug rather than a configuration error: ${String(error)}`, { cause: error });
|
|
303
121
|
}
|
|
304
|
-
return
|
|
305
|
-
keyframesJson: Object.keys(merged).length > 0 ? JSON.stringify(merged) : null,
|
|
306
|
-
diagnostics
|
|
307
|
-
};
|
|
122
|
+
return parsed;
|
|
308
123
|
}
|
|
309
124
|
//#endregion
|
|
310
|
-
//#region pipeline/
|
|
311
|
-
/** Closed set of driver namespaces a shared config file may carry. */
|
|
312
|
-
const DRIVER_NAMESPACES = [
|
|
313
|
-
"vite",
|
|
314
|
-
"next",
|
|
315
|
-
"cli"
|
|
316
|
-
];
|
|
125
|
+
//#region pipeline/mdx-preprocessor.ts
|
|
317
126
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* collection path (never the source walk), and `.next`/`.animus` are artifact
|
|
321
|
-
* output directories inside the scanned root that must never be re-ingested
|
|
322
|
-
* as source.
|
|
127
|
+
* MDX compiled to scanner-consumable JSX, plus the extension sets every
|
|
128
|
+
* driver imports. `@mdx-js/mdx` is an optional peer, so its import is lazy.
|
|
323
129
|
*/
|
|
324
|
-
const
|
|
325
|
-
"
|
|
326
|
-
".
|
|
327
|
-
".
|
|
130
|
+
const DEFAULT_EXTENSIONS = [
|
|
131
|
+
".ts",
|
|
132
|
+
".tsx",
|
|
133
|
+
".js",
|
|
134
|
+
".jsx",
|
|
135
|
+
".mdx"
|
|
328
136
|
];
|
|
329
137
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
138
|
+
* The one engine-transform file gate every driver imports — the suffixes the
|
|
139
|
+
* engine's parser accepts. `.mdx` is preprocessed to `.tsx` before it.
|
|
332
140
|
*/
|
|
333
|
-
const
|
|
334
|
-
"
|
|
335
|
-
"
|
|
336
|
-
"
|
|
141
|
+
const ENGINE_TRANSFORM_EXTENSIONS = [
|
|
142
|
+
"ts",
|
|
143
|
+
"tsx",
|
|
144
|
+
"js",
|
|
145
|
+
"jsx",
|
|
146
|
+
"mjs"
|
|
337
147
|
];
|
|
148
|
+
const ENGINE_TRANSFORM_RE = new RegExp(`\\.(?:${ENGINE_TRANSFORM_EXTENSIONS.join("|")})$`);
|
|
149
|
+
function isEngineTransformExtension(path) {
|
|
150
|
+
return ENGINE_TRANSFORM_RE.test(path);
|
|
151
|
+
}
|
|
338
152
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
* Composed, never hand-listed: a structural addition must not silently
|
|
342
|
-
* miss this union.
|
|
153
|
+
* Compile MDX to scanner-consumable JSX. `missing-dep` means the consumer
|
|
154
|
+
* must install `@mdx-js/mdx`; hosts warn and skip the file on `error`.
|
|
343
155
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
156
|
+
async function preprocessMdx(source, filename) {
|
|
157
|
+
const mdxMod = await import("@mdx-js/mdx").catch(() => null);
|
|
158
|
+
if (mdxMod === null) return { kind: "missing-dep" };
|
|
159
|
+
try {
|
|
160
|
+
const vfile = await mdxMod.compile(source, {
|
|
161
|
+
outputFormat: "program",
|
|
162
|
+
development: false,
|
|
163
|
+
jsx: true
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
kind: "ok",
|
|
167
|
+
source: `/* @mdx-source: ${filename} */\n${String(vfile)}`
|
|
168
|
+
};
|
|
169
|
+
} catch (err) {
|
|
170
|
+
return {
|
|
171
|
+
kind: "error",
|
|
172
|
+
error: err instanceof Error ? err.message : String(err)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region pipeline/svelte-source-adapter.ts
|
|
178
|
+
const isCompilerReference = (value) => Object(value) === value;
|
|
179
|
+
const isCompilerCallable = (value) => {
|
|
180
|
+
if (!isCompilerReference(value)) return false;
|
|
181
|
+
try {
|
|
182
|
+
Function.prototype.toString.call(value);
|
|
183
|
+
return true;
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
367
186
|
}
|
|
368
187
|
};
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
* mode, name it loudly while the key stays inert as it always was).
|
|
378
|
-
*/
|
|
379
|
-
function assertKnownOptionKeys(raw, ownKeys = [], rejectKeys = [], opts = {}) {
|
|
380
|
-
const surface = (message) => {
|
|
381
|
-
if (opts.onUnknownKey === "warn") {
|
|
382
|
-
opts.warn(message);
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
throw new AnimusConfigError(message);
|
|
386
|
-
};
|
|
387
|
-
const own = new Set(ownKeys);
|
|
388
|
-
for (const key of Object.keys(raw)) {
|
|
389
|
-
if (raw[key] === void 0) continue;
|
|
390
|
-
const rejected = rejectKeys.find((entry) => entry.key === key);
|
|
391
|
-
if (rejected) {
|
|
392
|
-
surface(`Option "${key}" is not supported by this driver: ${rejected.reason}`);
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
if (CORE_OPTION_KEYS.has(key) || DRIVER_NAMESPACES.includes(key) || own.has(key)) continue;
|
|
396
|
-
const nearest = suggestNearest(key);
|
|
397
|
-
surface(`Unknown option "${key}".${nearest ? ` Did you mean "${nearest}"?` : ""} Core keys: ${[...CORE_OPTION_KEYS].join(", ")}. Driver namespaces: ${DRIVER_NAMESPACES.join(", ")}.`);
|
|
188
|
+
const isCompilerRecord = (value) => isCompilerReference(value) && !isCompilerCallable(value);
|
|
189
|
+
const acceptsCompilerPrimitive = (value, read) => {
|
|
190
|
+
if (isCompilerReference(value)) return false;
|
|
191
|
+
try {
|
|
192
|
+
read();
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
398
196
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
197
|
+
};
|
|
198
|
+
const isCompilerString = (value) => acceptsCompilerPrimitive(value, () => String.prototype.valueOf.call(value));
|
|
199
|
+
const isCompilerNumber = (value) => acceptsCompilerPrimitive(value, () => Number.prototype.valueOf.call(value));
|
|
200
|
+
function isNode(value) {
|
|
201
|
+
return isCompilerRecord(value) && isCompilerString(value.type) && isCompilerNumber(value.start) && isCompilerNumber(value.end);
|
|
202
|
+
}
|
|
203
|
+
function childNode(value) {
|
|
204
|
+
return isNode(value) ? value : null;
|
|
205
|
+
}
|
|
206
|
+
function isProgramNode(value) {
|
|
207
|
+
return isNode(value) && Array.isArray(value.body) && value.body.every((child) => isNode(child));
|
|
208
|
+
}
|
|
209
|
+
function isScriptNode(value) {
|
|
210
|
+
return isNode(value) && isProgramNode(value.content);
|
|
211
|
+
}
|
|
212
|
+
function parseSvelteAst(value) {
|
|
213
|
+
if (value.module !== void 0 && value.module !== null && !isScriptNode(value.module)) throw new TypeError("svelte/compiler returned an invalid module script");
|
|
214
|
+
if (value.instance !== void 0 && value.instance !== null && !isScriptNode(value.instance)) throw new TypeError("svelte/compiler returned an invalid instance script");
|
|
215
|
+
if (!isCompilerRecord(value.fragment)) throw new TypeError("svelte/compiler returned an invalid fragment");
|
|
216
|
+
return {
|
|
217
|
+
module: value.module,
|
|
218
|
+
instance: value.instance,
|
|
219
|
+
fragment: value.fragment
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function nodeName(node) {
|
|
223
|
+
return node?.type === "Identifier" && isCompilerString(node.name) ? node.name : null;
|
|
224
|
+
}
|
|
225
|
+
/** Adaptation touches one source at a time, so a last-value memo runs the
|
|
226
|
+
* all-ASCII check once per file rather than per span endpoint. */
|
|
227
|
+
let asciiMemoSource = null;
|
|
228
|
+
let asciiMemoValue = false;
|
|
229
|
+
function isAsciiOnly(source) {
|
|
230
|
+
if (asciiMemoSource !== source) {
|
|
231
|
+
asciiMemoSource = source;
|
|
232
|
+
asciiMemoValue = Buffer.byteLength(source) === source.length;
|
|
405
233
|
}
|
|
234
|
+
return asciiMemoValue;
|
|
406
235
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
key: "system",
|
|
411
|
-
ok: (v) => typeof v === "string",
|
|
412
|
-
expected: "a string path"
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
key: "exclude",
|
|
416
|
-
ok: isStringArray,
|
|
417
|
-
expected: "an array of string patterns"
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
key: "extensions",
|
|
421
|
-
ok: isStringArray,
|
|
422
|
-
expected: "an array of string extensions"
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
key: "strict",
|
|
426
|
-
ok: (v) => typeof v === "boolean",
|
|
427
|
-
expected: "a boolean"
|
|
428
|
-
},
|
|
429
|
-
{
|
|
430
|
-
key: "verbose",
|
|
431
|
-
ok: (v) => typeof v === "boolean",
|
|
432
|
-
expected: "a boolean"
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
key: "minify",
|
|
436
|
-
ok: (v) => typeof v === "boolean",
|
|
437
|
-
expected: "a boolean"
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
key: "prefix",
|
|
441
|
-
ok: (v) => typeof v === "string",
|
|
442
|
-
expected: "a string"
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
key: "targets",
|
|
446
|
-
ok: (v) => typeof v === "string" || isStringArray(v),
|
|
447
|
-
expected: "a string or an array of strings"
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
key: "layers",
|
|
451
|
-
ok: isStringArray,
|
|
452
|
-
expected: "an array of layer names"
|
|
453
|
-
}
|
|
454
|
-
];
|
|
455
|
-
function suggestNearest(key) {
|
|
456
|
-
const lower = key.toLowerCase();
|
|
457
|
-
let best = null;
|
|
458
|
-
let bestScore = 3;
|
|
459
|
-
for (const candidate of CORE_OPTION_KEYS) {
|
|
460
|
-
const score = editDistance(lower, candidate.toLowerCase());
|
|
461
|
-
if (score < bestScore) {
|
|
462
|
-
bestScore = score;
|
|
463
|
-
best = candidate;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return best;
|
|
467
|
-
}
|
|
468
|
-
function editDistance(a, b) {
|
|
469
|
-
const dp = Array.from({ length: a.length + 1 }, (_, i) => [i]);
|
|
470
|
-
for (let j = 1; j <= b.length; j++) dp[0][j] = j;
|
|
471
|
-
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
472
|
-
return dp[a.length][b.length];
|
|
236
|
+
function byteOffset(source, characterOffset) {
|
|
237
|
+
if (isAsciiOnly(source)) return characterOffset;
|
|
238
|
+
return Buffer.byteLength(source.slice(0, characterOffset));
|
|
473
239
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
* match on the path itself OR any ancestor directory excludes it, so a
|
|
479
|
-
* directory-shaped glob (double-star slash "generated") excludes files
|
|
480
|
-
* inside that directory identically for tree discovery (which prunes at
|
|
481
|
-
* the directory) and per-file watch classification (which only ever sees
|
|
482
|
-
* file paths).
|
|
483
|
-
* Plain patterns keep the historical substring semantics over both path
|
|
484
|
-
* forms.
|
|
485
|
-
* A leading `./` is stripped first: relative paths are computed without
|
|
486
|
-
* one, so `./fixtures/**` must mean exactly `fixtures/**` rather than
|
|
487
|
-
* compiling to a regex that can never match.
|
|
488
|
-
*/
|
|
489
|
-
function compilePattern(raw) {
|
|
490
|
-
let pattern = raw.split("\\").join("/");
|
|
491
|
-
while (pattern.startsWith("./")) pattern = pattern.slice(2);
|
|
492
|
-
if (pattern === "") return () => false;
|
|
493
|
-
if (!GLOB_META.test(pattern)) return (full, rel) => full.includes(pattern) || rel.includes(pattern);
|
|
494
|
-
const regex = globToRegExp(pattern);
|
|
495
|
-
return (_full, rel) => {
|
|
496
|
-
const normalized = rel.split("\\").join("/");
|
|
497
|
-
if (regex.test(normalized)) return true;
|
|
498
|
-
for (let slash = normalized.indexOf("/"); slash !== -1; slash = normalized.indexOf("/", slash + 1)) if (regex.test(normalized.slice(0, slash))) return true;
|
|
499
|
-
return false;
|
|
240
|
+
function byteSpan(source, node) {
|
|
241
|
+
return {
|
|
242
|
+
start: byteOffset(source, node.start),
|
|
243
|
+
end: byteOffset(source, node.end)
|
|
500
244
|
};
|
|
501
245
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
let
|
|
505
|
-
let
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
i += 2;
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
out += "[^/]*";
|
|
518
|
-
i += 1;
|
|
519
|
-
}
|
|
520
|
-
else if (ch === "?") {
|
|
521
|
-
out += "[^/]";
|
|
522
|
-
i += 1;
|
|
523
|
-
} else {
|
|
524
|
-
out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
525
|
-
i += 1;
|
|
246
|
+
function sourcePosition(source, characterOffset) {
|
|
247
|
+
let line = 1;
|
|
248
|
+
let lineStart = 0;
|
|
249
|
+
for (let index = 0; index < characterOffset; index += 1) {
|
|
250
|
+
const code = source.charCodeAt(index);
|
|
251
|
+
if (code === 13) {
|
|
252
|
+
if (index + 1 < characterOffset && source.charCodeAt(index + 1) === 10) index += 1;
|
|
253
|
+
line += 1;
|
|
254
|
+
lineStart = index + 1;
|
|
255
|
+
} else if (code === 10 || code === 8232 || code === 8233) {
|
|
256
|
+
line += 1;
|
|
257
|
+
lineStart = index + 1;
|
|
526
258
|
}
|
|
527
259
|
}
|
|
528
|
-
return new RegExp(`^${out}$`);
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Build the effective exclusion matcher. A user list REPLACES the
|
|
532
|
-
* replaceable defaults (`dist`, `.test.`, `.spec.`) — the HEAD driver
|
|
533
|
-
* contract (`options.exclude ?? DEFAULT_EXCLUDE`) — while the structural
|
|
534
|
-
* exclusions (`node_modules`, `.next`, `.animus`) always apply: those
|
|
535
|
-
* protect pipeline invariants, not preferences, so no user list can
|
|
536
|
-
* re-admit them. `extraStructural` joins that never-replaceable set — the
|
|
537
|
-
* channel for driver-owned invariants (a CLI outDir inside the root),
|
|
538
|
-
* which must NOT ride the user list: appending there would flip the
|
|
539
|
-
* replace semantics and silently drop the replaceable defaults.
|
|
540
|
-
*/
|
|
541
|
-
function createExcludeMatcher(userPatterns, extraStructural = []) {
|
|
542
|
-
const merged = [...STRUCTURAL_EXCLUDE];
|
|
543
|
-
for (const pattern of extraStructural) if (!merged.includes(pattern)) merged.push(pattern);
|
|
544
|
-
for (const pattern of userPatterns ?? REPLACEABLE_DEFAULT_EXCLUDE) if (!merged.includes(pattern)) merged.push(pattern);
|
|
545
|
-
const compiled = merged.map((pattern) => [pattern, compilePattern(pattern)]);
|
|
546
|
-
const hits = new Map(merged.map((p) => [p, 0]));
|
|
547
|
-
const explain = (full, rel) => {
|
|
548
|
-
for (const [pattern, test] of compiled) if (test(full, rel)) {
|
|
549
|
-
hits.set(pattern, (hits.get(pattern) ?? 0) + 1);
|
|
550
|
-
return pattern;
|
|
551
|
-
}
|
|
552
|
-
return null;
|
|
553
|
-
};
|
|
554
260
|
return {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
explain,
|
|
558
|
-
stats: () => hits
|
|
261
|
+
line,
|
|
262
|
+
column: characterOffset - lineStart
|
|
559
263
|
};
|
|
560
264
|
}
|
|
561
|
-
|
|
562
|
-
* Resolve the effective mode: the explicit `mode` key wins over every
|
|
563
|
-
* environment-derived signal; otherwise the driver's documented default
|
|
564
|
-
* applies.
|
|
565
|
-
*/
|
|
566
|
-
function resolveMode(explicit, driverDefault) {
|
|
567
|
-
if (explicit === "development" || explicit === "production") return {
|
|
568
|
-
mode: explicit,
|
|
569
|
-
provenance: "explicit"
|
|
570
|
-
};
|
|
265
|
+
function sourceLocation(source, node) {
|
|
571
266
|
return {
|
|
572
|
-
|
|
573
|
-
|
|
267
|
+
start: sourcePosition(source, node.start),
|
|
268
|
+
end: sourcePosition(source, node.end)
|
|
574
269
|
};
|
|
575
270
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
function discoverFiles(dir, rootDir, exclude, extensionsSet) {
|
|
588
|
-
const results = [];
|
|
589
|
-
let entries;
|
|
590
|
-
try {
|
|
591
|
-
entries = (0, fs.readdirSync)(dir, { encoding: "utf8" });
|
|
592
|
-
} catch {
|
|
593
|
-
return results;
|
|
594
|
-
}
|
|
595
|
-
entries.sort();
|
|
596
|
-
const isExcluded = exclude ? (full, rel) => exclude.matches(full, rel) : () => false;
|
|
597
|
-
for (const entry of entries) {
|
|
598
|
-
const fullPath = (0, path.join)(dir, entry);
|
|
599
|
-
if (isExcluded(fullPath, (0, path.relative)(rootDir, fullPath))) continue;
|
|
600
|
-
let stat;
|
|
601
|
-
try {
|
|
602
|
-
stat = (0, fs.statSync)(fullPath);
|
|
603
|
-
} catch {
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
if (stat.isDirectory()) results.push(...discoverFiles(fullPath, rootDir, exclude, extensionsSet));
|
|
607
|
-
else if (extensionsSet.has((0, path.extname)(entry))) results.push(fullPath);
|
|
271
|
+
function createsNestedBindingScope(node) {
|
|
272
|
+
return node.type === "BlockStatement" || node.type === "CatchClause" || node.type === "ClassBody" || node.type === "ClassDeclaration" || node.type === "ClassExpression" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ForStatement" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "StaticBlock" || node.type === "SwitchStatement" || node.type === "TSModuleBlock";
|
|
273
|
+
}
|
|
274
|
+
function walk(node, visit, nestedScopeDepth = 0) {
|
|
275
|
+
visit(node, nestedScopeDepth);
|
|
276
|
+
const childScopeDepth = nestedScopeDepth + (createsNestedBindingScope(node) ? 1 : 0);
|
|
277
|
+
for (const [key, value] of Object.entries(node)) {
|
|
278
|
+
if (key === "loc" || key === "start" || key === "end") continue;
|
|
279
|
+
if (Array.isArray(value)) {
|
|
280
|
+
for (const item of value) if (isNode(item)) walk(item, visit, childScopeDepth);
|
|
281
|
+
} else if (isNode(value)) walk(value, visit, childScopeDepth);
|
|
608
282
|
}
|
|
609
|
-
return results;
|
|
610
283
|
}
|
|
611
|
-
//#endregion
|
|
612
|
-
//#region pipeline/source-identity.ts
|
|
613
|
-
/**
|
|
614
|
-
* SourceId derivation authority + allowlist membership (openspec:
|
|
615
|
-
* external-source-watch-ingestion, design D1/D2/D5).
|
|
616
|
-
*
|
|
617
|
-
* One shared derivation produces the canonical source identity consumed by
|
|
618
|
-
* discovery, watch ingestion, ownership, deletion pruning, and diagnostics
|
|
619
|
-
* — raw event paths are lookup INPUTS only, so event spelling (symlink
|
|
620
|
-
* alias vs canonical, lexical vs realpath) can never fork identity.
|
|
621
|
-
*
|
|
622
|
-
* The containment and volume helpers are pure and path-API-injectable so
|
|
623
|
-
* Windows semantics are unit-testable via `path.win32` on any host; runtime
|
|
624
|
-
* callers use the ambient platform implementation.
|
|
625
|
-
*/
|
|
626
284
|
/**
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
* never claim `/ui-old`, and a cross-drive win32 target (whose relative()
|
|
630
|
-
* result is absolute) is never contained.
|
|
285
|
+
* Svelte `Fragment` containers carry no `start`/`end`, so the `isNode` gate
|
|
286
|
+
* would stop at each one: descend plain objects, visit span-carrying nodes.
|
|
631
287
|
*/
|
|
632
|
-
function
|
|
633
|
-
const
|
|
634
|
-
|
|
288
|
+
function walkFragment(value, visit) {
|
|
289
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
290
|
+
const descend = (current) => {
|
|
291
|
+
if (Array.isArray(current)) {
|
|
292
|
+
for (const item of current) descend(item);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (!isCompilerRecord(current)) return;
|
|
296
|
+
if (seen.has(current)) return;
|
|
297
|
+
seen.add(current);
|
|
298
|
+
if (isNode(current)) visit(current);
|
|
299
|
+
for (const [key, child] of Object.entries(current)) {
|
|
300
|
+
if (key === "loc" || key === "start" || key === "end" || key === "metadata" || key === "parent") continue;
|
|
301
|
+
descend(child);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
descend(value);
|
|
635
305
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
306
|
+
function importBindings(program) {
|
|
307
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const declaration of program.body) {
|
|
309
|
+
if (declaration.type !== "ImportDeclaration" || declaration.importKind === "type" || !Array.isArray(declaration.specifiers)) continue;
|
|
310
|
+
for (const value of declaration.specifiers) {
|
|
311
|
+
if (!isNode(value) || value.importKind === "type") continue;
|
|
312
|
+
const local = childNode(value.local);
|
|
313
|
+
const localName = nodeName(local);
|
|
314
|
+
if (!local || !localName) continue;
|
|
315
|
+
const sourceNode = childNode(declaration.source);
|
|
316
|
+
const importSource = sourceNode && isCompilerString(sourceNode.value) ? sourceNode.value : null;
|
|
317
|
+
if (!importSource) continue;
|
|
318
|
+
const namedImported = importedName(value);
|
|
319
|
+
if (value.type === "ImportSpecifier" && namedImported === null) continue;
|
|
320
|
+
const importedNode = childNode(value.imported);
|
|
321
|
+
const kind = value.type === "ImportSpecifier" ? importedNode?.type === "Literal" ? "string-named" : namedImported === "default" ? "named-default" : "named" : value.type === "ImportDefaultSpecifier" ? "default" : value.type === "ImportNamespaceSpecifier" ? "namespace" : null;
|
|
322
|
+
if (kind) bindings.set(localName, {
|
|
323
|
+
kind,
|
|
324
|
+
source: importSource,
|
|
325
|
+
imported: kind === "default" || kind === "named-default" ? "default" : kind === "namespace" ? "*" : namedImported,
|
|
326
|
+
declaration,
|
|
327
|
+
specifier: value,
|
|
328
|
+
local
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return bindings;
|
|
644
333
|
}
|
|
645
|
-
function
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
334
|
+
function diagnostic(code, message, originalPath, source, node, location) {
|
|
335
|
+
const result = {
|
|
336
|
+
code,
|
|
337
|
+
message,
|
|
338
|
+
originalPath
|
|
339
|
+
};
|
|
340
|
+
if (node) {
|
|
341
|
+
result.span = byteSpan(source, node);
|
|
342
|
+
result.location = location ?? sourceLocation(source, node);
|
|
650
343
|
}
|
|
344
|
+
return result;
|
|
651
345
|
}
|
|
652
|
-
function
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
346
|
+
function parseCompilerError(value) {
|
|
347
|
+
if (!isCompilerRecord(value)) return null;
|
|
348
|
+
const parsed = { positions: Array.isArray(value.position) ? value.position : [] };
|
|
349
|
+
if (isCompilerRecord(value.start)) parsed.start = value.start;
|
|
350
|
+
if (isCompilerRecord(value.end)) parsed.end = value.end;
|
|
351
|
+
return parsed;
|
|
352
|
+
}
|
|
353
|
+
function compilerParseErrorRange(error, source) {
|
|
354
|
+
const candidate = parseCompilerError(error);
|
|
355
|
+
if (candidate === null) return null;
|
|
356
|
+
const positions = candidate.positions;
|
|
357
|
+
const startCharacter = isCompilerNumber(candidate.start?.character) ? candidate.start.character : isCompilerNumber(positions[0]) ? positions[0] : null;
|
|
358
|
+
const endCharacter = isCompilerNumber(candidate.end?.character) ? candidate.end.character : isCompilerNumber(positions[1]) ? positions[1] : startCharacter;
|
|
359
|
+
if (startCharacter === null || endCharacter === null || startCharacter < 0 || endCharacter < startCharacter || endCharacter > source.length) return null;
|
|
360
|
+
const node = {
|
|
361
|
+
type: "SvelteParseError",
|
|
362
|
+
start: startCharacter,
|
|
363
|
+
end: endCharacter
|
|
664
364
|
};
|
|
365
|
+
const fallback = sourceLocation(source, node);
|
|
366
|
+
const point = (value, fallbackPoint) => ({
|
|
367
|
+
line: isCompilerNumber(value?.line) ? value.line : fallbackPoint.line,
|
|
368
|
+
column: isCompilerNumber(value?.column) ? value.column : fallbackPoint.column
|
|
369
|
+
});
|
|
665
370
|
return {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
else return null;
|
|
706
|
-
aliases.set(lexical, resolved);
|
|
707
|
-
aliases.set(canonical, resolved);
|
|
708
|
-
return resolved;
|
|
709
|
-
},
|
|
710
|
-
resolveDeletedSourceId(inputPath) {
|
|
711
|
-
return aliases.get(node_path.default.normalize(inputPath)) ?? null;
|
|
712
|
-
},
|
|
713
|
-
containingExternalRoot(inputPath) {
|
|
714
|
-
const lexical = node_path.default.normalize(inputPath);
|
|
715
|
-
let owner = null;
|
|
716
|
-
for (const record of roots) {
|
|
717
|
-
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
718
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
371
|
+
node,
|
|
372
|
+
location: {
|
|
373
|
+
start: point(candidate.start, fallback.start),
|
|
374
|
+
end: point(candidate.end, fallback.end)
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function calleeParts(node) {
|
|
379
|
+
if (node.type !== "CallExpression") return null;
|
|
380
|
+
const callee = childNode(node.callee);
|
|
381
|
+
if (!callee || callee.type !== "MemberExpression") return null;
|
|
382
|
+
const object = childNode(callee.object);
|
|
383
|
+
const property = childNode(callee.property);
|
|
384
|
+
if (!object || !property) return null;
|
|
385
|
+
if ((nodeName(property) ?? (property.type === "Literal" && property.value === "attrs" ? "attrs" : null)) !== "attrs") return null;
|
|
386
|
+
return {
|
|
387
|
+
object,
|
|
388
|
+
property,
|
|
389
|
+
computed: callee.computed === true,
|
|
390
|
+
optional: callee.optional === true || node.optional === true
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function resolverImportAccess(receiver, bindings) {
|
|
394
|
+
const directName = nodeName(receiver);
|
|
395
|
+
if (directName) {
|
|
396
|
+
const binding = bindings.get(directName);
|
|
397
|
+
if (!binding) return null;
|
|
398
|
+
return {
|
|
399
|
+
binding,
|
|
400
|
+
resolver: receiver,
|
|
401
|
+
displayName: directName,
|
|
402
|
+
request: {
|
|
403
|
+
source: binding.source,
|
|
404
|
+
imported: binding.imported,
|
|
405
|
+
local: directName,
|
|
406
|
+
access: {
|
|
407
|
+
kind: "direct",
|
|
408
|
+
importKind: binding.kind
|
|
409
|
+
}
|
|
719
410
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (receiver.type !== "MemberExpression" || receiver.computed === true || receiver.optional === true) return null;
|
|
414
|
+
const namespaceNode = childNode(receiver.object);
|
|
415
|
+
const memberNode = childNode(receiver.property);
|
|
416
|
+
const namespaceName = nodeName(namespaceNode);
|
|
417
|
+
const memberName = nodeName(memberNode) ?? (memberNode?.type === "Literal" && isCompilerString(memberNode.value) ? memberNode.value : null);
|
|
418
|
+
if (!namespaceNode || !namespaceName || !memberName) return null;
|
|
419
|
+
const binding = bindings.get(namespaceName);
|
|
420
|
+
if (!binding || binding.kind !== "namespace") return null;
|
|
421
|
+
return {
|
|
422
|
+
binding,
|
|
423
|
+
resolver: receiver,
|
|
424
|
+
displayName: `${namespaceName}.${memberName}`,
|
|
425
|
+
request: {
|
|
426
|
+
source: binding.source,
|
|
427
|
+
imported: memberName,
|
|
428
|
+
local: namespaceName,
|
|
429
|
+
access: {
|
|
430
|
+
kind: "namespace-member",
|
|
431
|
+
importKind: "namespace"
|
|
726
432
|
}
|
|
727
|
-
return owner?.canonical ?? null;
|
|
728
433
|
}
|
|
729
434
|
};
|
|
730
435
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Walk up from a resolved package entry file to the nearest directory
|
|
735
|
-
* containing a package.json (the package root). Stops at the filesystem
|
|
736
|
-
* root and returns it if no package.json is found on the way up.
|
|
737
|
-
*/
|
|
738
|
-
function findPackageRoot(absEntryPath) {
|
|
739
|
-
let pkgRoot = (0, path.dirname)(absEntryPath);
|
|
740
|
-
while (pkgRoot !== (0, path.dirname)(pkgRoot) && !(0, fs.existsSync)((0, path.join)(pkgRoot, "package.json"))) pkgRoot = (0, path.dirname)(pkgRoot);
|
|
741
|
-
return pkgRoot;
|
|
436
|
+
function isSupportedResolverAccess(access) {
|
|
437
|
+
return access.binding.kind === "named" && access.request.access.kind === "direct";
|
|
742
438
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
439
|
+
function resolverCallSite(node, bindings) {
|
|
440
|
+
if (node.type !== "CallExpression") return null;
|
|
441
|
+
const parts = calleeParts(node);
|
|
442
|
+
if (parts) {
|
|
443
|
+
const access = resolverImportAccess(parts.object, bindings);
|
|
444
|
+
if (!access) return null;
|
|
445
|
+
return {
|
|
446
|
+
access,
|
|
447
|
+
form: "attrs",
|
|
448
|
+
computed: parts.computed,
|
|
449
|
+
optional: parts.optional
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const callee = childNode(node.callee);
|
|
453
|
+
if (!callee) return null;
|
|
454
|
+
const access = resolverImportAccess(callee, bindings);
|
|
455
|
+
if (!access) return null;
|
|
456
|
+
return {
|
|
457
|
+
access,
|
|
458
|
+
form: "callable",
|
|
459
|
+
computed: callee.type === "MemberExpression" && callee.computed === true,
|
|
460
|
+
optional: node.optional === true || callee.type === "MemberExpression" && callee.optional === true
|
|
461
|
+
};
|
|
760
462
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return discoverFiles(packageDir, packageDir, void 0, extensionsSet).filter((absPath) => !isExcludedPackageRelativePath((0, path.relative)(packageDir, absPath)));
|
|
463
|
+
function argumentSpan(argumentsList, fallback) {
|
|
464
|
+
if (argumentsList.length === 0) return fallback;
|
|
465
|
+
return {
|
|
466
|
+
type: "ArgumentRange",
|
|
467
|
+
start: argumentsList[0].start,
|
|
468
|
+
end: argumentsList[argumentsList.length - 1].end
|
|
469
|
+
};
|
|
769
470
|
}
|
|
770
|
-
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
".
|
|
774
|
-
".
|
|
775
|
-
|
|
776
|
-
".map"
|
|
777
|
-
];
|
|
778
|
-
const isFile = (path$3) => {
|
|
779
|
-
try {
|
|
780
|
-
return (0, fs.statSync)(path$3).isFile();
|
|
781
|
-
} catch {
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
|
-
/**
|
|
786
|
-
* Resolve an absolute-path specifier (a relative `includes` entry already
|
|
787
|
-
* resolved against the system file) without a bundler: the path itself when it
|
|
788
|
-
* is a file, otherwise the extension and directory-index candidates built from
|
|
789
|
-
* the caller's extension set. Used only as a fallback — a bundler resolver that
|
|
790
|
-
* answers first still wins, since it also knows aliases and `.js`→`.ts` mapping.
|
|
791
|
-
*/
|
|
792
|
-
function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
|
|
793
|
-
return [
|
|
794
|
-
absSpecifier,
|
|
795
|
-
...Array.from(extensionsSet, (ext) => absSpecifier + ext),
|
|
796
|
-
...Array.from(extensionsSet, (ext) => (0, path.join)(absSpecifier, `index${ext}`))
|
|
797
|
-
].find(isFile) ?? null;
|
|
471
|
+
function propertyName(node) {
|
|
472
|
+
const key = childNode(node.key);
|
|
473
|
+
if (!key) return null;
|
|
474
|
+
if (key.type === "Identifier" && isCompilerString(key.name)) return key.name;
|
|
475
|
+
if (key.type === "Literal" && isCompilerString(key.value)) return key.value;
|
|
476
|
+
return null;
|
|
798
477
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const segments = specifier.split("/");
|
|
802
|
-
return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
|
|
478
|
+
function isJsxAttributeName(name) {
|
|
479
|
+
return /^[A-Za-z_$][A-Za-z0-9_$-]*$/.test(name);
|
|
803
480
|
}
|
|
804
|
-
function
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if (
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
return resolveAbsolutePathSpecifier((0, path.join)(srcDir, specifier.slice(packageName.length + 1) || "index"), extensionsSet);
|
|
481
|
+
function tsxIncompatibleValue(value) {
|
|
482
|
+
let incompatible = null;
|
|
483
|
+
walk(value, (node) => {
|
|
484
|
+
if (incompatible) return;
|
|
485
|
+
if (node.type === "TSTypeAssertion" || node.type === "ArrowFunctionExpression" && childNode(node.typeParameters) !== null) incompatible = node;
|
|
486
|
+
});
|
|
487
|
+
return incompatible;
|
|
812
488
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
return
|
|
489
|
+
function readProperties(argument, source, originalPath) {
|
|
490
|
+
if (!Array.isArray(argument.properties)) return diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", "Resolver .attrs() requires an object literal with statically named properties.", originalPath, source, argument);
|
|
491
|
+
const properties = [];
|
|
492
|
+
for (const value of argument.properties) {
|
|
493
|
+
if (!isNode(value)) continue;
|
|
494
|
+
if (value.type === "SpreadElement") return diagnostic("SVELTE_ATTRS_SPREAD_UNRESOLVED", "Resolver .attrs() object spreads are unresolved; enumerate properties explicitly.", originalPath, source, value);
|
|
495
|
+
if (value.type !== "Property") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() accepts ordinary object properties only.", originalPath, source, value);
|
|
496
|
+
if (value.computed === true) return diagnostic("SVELTE_ATTRS_COMPUTED_KEY", "Resolver .attrs() computed property names cannot be attributed safely.", originalPath, source, value);
|
|
497
|
+
if (value.method === true || value.kind !== "init") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() methods and accessors cannot be projected as attributes.", originalPath, source, value);
|
|
498
|
+
const key = childNode(value.key);
|
|
499
|
+
const propertyValue = childNode(value.value);
|
|
500
|
+
const name = propertyName(value);
|
|
501
|
+
if (!key || !propertyValue || !name || !isJsxAttributeName(name)) return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() property names must be JSX-compatible identifier or string names.", originalPath, source, value);
|
|
502
|
+
const incompatibleValue = tsxIncompatibleValue(propertyValue);
|
|
503
|
+
if (incompatibleValue) return diagnostic("SVELTE_ATTRS_VALUE_UNSUPPORTED", "Resolver .attrs() values using angle-bracket type assertions or generic arrow parameters cannot be projected into TSX safely.", originalPath, source, incompatibleValue);
|
|
504
|
+
properties.push({
|
|
505
|
+
name,
|
|
506
|
+
property: value,
|
|
507
|
+
key,
|
|
508
|
+
value: propertyValue
|
|
509
|
+
});
|
|
828
510
|
}
|
|
829
|
-
|
|
830
|
-
for (const srcFile of srcFiles) try {
|
|
831
|
-
newestSrcMtime = Math.max(newestSrcMtime, (0, fs.statSync)(srcFile).mtimeMs);
|
|
832
|
-
} catch {}
|
|
833
|
-
return distMtime < newestSrcMtime;
|
|
511
|
+
return properties;
|
|
834
512
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
513
|
+
var VirtualSourceBuilder = class {
|
|
514
|
+
originalSource;
|
|
515
|
+
chunks = [];
|
|
516
|
+
bytes = 0;
|
|
517
|
+
mappings = [];
|
|
518
|
+
constructor(originalSource) {
|
|
519
|
+
this.originalSource = originalSource;
|
|
520
|
+
}
|
|
521
|
+
append(text) {
|
|
522
|
+
this.chunks.push(text);
|
|
523
|
+
this.bytes += Buffer.byteLength(text);
|
|
524
|
+
}
|
|
525
|
+
appendMapped(text, kind, original) {
|
|
526
|
+
const start = this.bytes;
|
|
527
|
+
this.append(text);
|
|
528
|
+
this.mappings.push({
|
|
529
|
+
kind,
|
|
530
|
+
generated: {
|
|
531
|
+
start,
|
|
532
|
+
end: this.bytes
|
|
533
|
+
},
|
|
534
|
+
original: byteSpan(this.originalSource, original)
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
toString() {
|
|
538
|
+
return this.chunks.join("");
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
function importedName(specifier) {
|
|
542
|
+
const imported = childNode(specifier.imported);
|
|
543
|
+
if (!imported) return null;
|
|
544
|
+
if (imported.type === "Identifier" && isCompilerString(imported.name)) return imported.name;
|
|
545
|
+
if (imported.type === "Literal" && isCompilerString(imported.value)) return imported.value;
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
function renderImport(declaration, witnessedBindings, source) {
|
|
549
|
+
if (!Array.isArray(declaration.specifiers)) return null;
|
|
550
|
+
const names = [];
|
|
551
|
+
const locals = [];
|
|
552
|
+
for (const value of declaration.specifiers) {
|
|
553
|
+
if (!isNode(value) || value.type !== "ImportSpecifier") continue;
|
|
554
|
+
const localName = nodeName(childNode(value.local));
|
|
555
|
+
if (!localName || !witnessedBindings.has(localName)) continue;
|
|
556
|
+
names.push(source.slice(value.start, value.end));
|
|
557
|
+
locals.push(localName);
|
|
558
|
+
}
|
|
559
|
+
if (names.length === 0) return null;
|
|
560
|
+
const sourceNode = childNode(declaration.source);
|
|
561
|
+
const sourceText = sourceNode && isCompilerString(sourceNode.raw) ? sourceNode.raw : sourceNode && isCompilerString(sourceNode.value) ? JSON.stringify(sourceNode.value) : null;
|
|
562
|
+
return sourceText ? {
|
|
563
|
+
text: `import { ${names.join(", ")} } from ${sourceText};\n`,
|
|
564
|
+
locals
|
|
565
|
+
} : null;
|
|
566
|
+
}
|
|
567
|
+
/** Memoizes success only: a missing optional peer retries per file, so
|
|
568
|
+
* installing `svelte` mid-session recovers without a restart. */
|
|
569
|
+
let compilerMemo = null;
|
|
570
|
+
async function loadCompiler() {
|
|
571
|
+
if (compilerMemo) return compilerMemo;
|
|
572
|
+
compilerMemo = await import("svelte/compiler").catch(() => null);
|
|
573
|
+
return compilerMemo;
|
|
574
|
+
}
|
|
575
|
+
function projectScope(script, scope, source, originalPath, options, inherited, precomputedOwnBindings) {
|
|
576
|
+
const ownBindings = precomputedOwnBindings ?? importBindings(script.content);
|
|
577
|
+
const bindings = inherited ? new Map([...inherited.bindings, ...ownBindings]) : ownBindings;
|
|
578
|
+
const witnesses = [];
|
|
579
|
+
const diagnostics = [];
|
|
580
|
+
if (bindings.size > 0) walk(script.content, (node, nestedScopeDepth) => {
|
|
581
|
+
const site = resolverCallSite(node, bindings);
|
|
582
|
+
if (!site) return;
|
|
583
|
+
const { access, form } = site;
|
|
584
|
+
const attribution = options.attributeResolver(access.request);
|
|
585
|
+
if (attribution === "other") return;
|
|
586
|
+
const resolverName = access.displayName;
|
|
587
|
+
const callDisplay = form === "attrs" ? `${resolverName}.attrs()` : `${resolverName}()`;
|
|
588
|
+
if (attribution === "unsupported-resolver-form" || !isSupportedResolverAccess(access)) {
|
|
589
|
+
diagnostics.push(diagnostic("SVELTE_ATTRS_IMPORT_UNSUPPORTED", `Resolver '${resolverName}' uses an import/access form that the extraction engine cannot attribute to its exported .asClass() binding.`, originalPath, source, access.resolver));
|
|
590
|
+
return;
|
|
875
591
|
}
|
|
876
|
-
if (
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
specifier,
|
|
880
|
-
outcome: "unresolvable",
|
|
881
|
-
fileCount: 0
|
|
882
|
-
});
|
|
883
|
-
continue;
|
|
592
|
+
if (nestedScopeDepth > 0) {
|
|
593
|
+
diagnostics.push(diagnostic("SVELTE_ATTRS_SCOPE_UNSUPPORTED", `Resolver '${callDisplay}' is inside a nested binding scope; move the call to the top-level script scope so its import identity is unambiguous.`, originalPath, source, access.resolver));
|
|
594
|
+
return;
|
|
884
595
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
packageMap[specifier] = (0, path.relative)(rootDir, srcEntry);
|
|
896
|
-
sourceEntries.set(specifier, srcEntry);
|
|
897
|
-
} else packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
898
|
-
keyframesScanEntries.set(specifier, srcEntry ?? absEntry);
|
|
899
|
-
if (!(0, path.isAbsolute)(specifier)) {
|
|
900
|
-
const packageName = bareSpecifierPackageName(specifier);
|
|
901
|
-
if (packageName !== specifier && !(packageName in packageMap)) {
|
|
902
|
-
const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
|
|
903
|
-
if (rootEntry) {
|
|
904
|
-
packageMap[packageName] = (0, path.relative)(rootDir, rootEntry);
|
|
905
|
-
sourceEntries.set(packageName, rootEntry);
|
|
906
|
-
keyframesScanEntries.set(packageName, rootEntry);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
const pkgFiles = walkPackageSources(srcDir, extensionsSet);
|
|
911
|
-
staleDist = distEntryIsStale(absEntry, srcDir, pkgFiles);
|
|
912
|
-
for (const pkgFile of pkgFiles) {
|
|
913
|
-
const relPath = (0, path.relative)(rootDir, pkgFile);
|
|
914
|
-
if (alreadyIngested(relPath)) {
|
|
915
|
-
fileCount++;
|
|
916
|
-
continue;
|
|
917
|
-
}
|
|
918
|
-
let source;
|
|
919
|
-
try {
|
|
920
|
-
source = (0, fs.readFileSync)(pkgFile, "utf-8");
|
|
921
|
-
} catch (err) {
|
|
922
|
-
onUnreadable(relPath, err);
|
|
923
|
-
continue;
|
|
924
|
-
}
|
|
925
|
-
onSourceRead?.(source, relPath, pkgFile);
|
|
926
|
-
entries.push({
|
|
927
|
-
path: relPath,
|
|
928
|
-
source
|
|
929
|
-
});
|
|
930
|
-
pushed.add(relPath);
|
|
931
|
-
fileOwners[relPath] ??= specifier;
|
|
932
|
-
fileCount++;
|
|
933
|
-
}
|
|
934
|
-
} else {
|
|
935
|
-
const outputDir = (0, path.dirname)(absEntry);
|
|
936
|
-
packageDirs.push(outputDir);
|
|
937
|
-
claimDir(outputDir, specifier);
|
|
938
|
-
onPackageResolved?.(specifier, outputDir);
|
|
939
|
-
packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
940
|
-
keyframesScanEntries.set(specifier, absEntry);
|
|
941
|
-
const outputExtensions = new Set(extensionsSet);
|
|
942
|
-
outputExtensions.add((0, path.extname)(absEntry));
|
|
943
|
-
dirExtensions[outputDir] = [...outputExtensions];
|
|
944
|
-
const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
|
|
945
|
-
const relToOutput = (0, path.relative)(outputDir, file);
|
|
946
|
-
return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
|
|
596
|
+
if (site.computed || site.optional) {
|
|
597
|
+
diagnostics.push(diagnostic("SVELTE_ATTRS_CALLEE_UNSUPPORTED", `Resolver '${resolverName}' must be called directly as ${resolverName}(...) or ${resolverName}.attrs(...).`, originalPath, source, node));
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const argumentsList = Array.isArray(node.arguments) ? node.arguments.filter(isNode) : [];
|
|
601
|
+
if (argumentsList.length === 0) {
|
|
602
|
+
witnesses.push({
|
|
603
|
+
binding: access.binding,
|
|
604
|
+
resolver: access.resolver,
|
|
605
|
+
properties: []
|
|
947
606
|
});
|
|
948
|
-
|
|
949
|
-
for (const outputFile of outputFiles) {
|
|
950
|
-
const outputRelPath = (0, path.relative)(rootDir, outputFile);
|
|
951
|
-
if (alreadyIngested(outputRelPath)) {
|
|
952
|
-
fileCount++;
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
let source;
|
|
956
|
-
try {
|
|
957
|
-
source = (0, fs.readFileSync)(outputFile, "utf-8");
|
|
958
|
-
} catch (err) {
|
|
959
|
-
onUnreadable(outputRelPath, err);
|
|
960
|
-
continue;
|
|
961
|
-
}
|
|
962
|
-
onSourceRead?.(source, outputRelPath, outputFile);
|
|
963
|
-
entries.push({
|
|
964
|
-
path: outputRelPath,
|
|
965
|
-
source
|
|
966
|
-
});
|
|
967
|
-
pushed.add(outputRelPath);
|
|
968
|
-
fileOwners[outputRelPath] ??= specifier;
|
|
969
|
-
fileCount++;
|
|
970
|
-
}
|
|
607
|
+
return;
|
|
971
608
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
609
|
+
if (argumentsList.length !== 1 || argumentsList[0].type !== "ObjectExpression") {
|
|
610
|
+
diagnostics.push(diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", `Resolver '${callDisplay}' accepts no argument or one object literal in Svelte projection.`, originalPath, source, argumentSpan(argumentsList, node)));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const properties = readProperties(argumentsList[0], source, originalPath);
|
|
614
|
+
if (!Array.isArray(properties)) {
|
|
615
|
+
diagnostics.push(properties);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
witnesses.push({
|
|
619
|
+
binding: access.binding,
|
|
620
|
+
resolver: access.resolver,
|
|
621
|
+
properties
|
|
976
622
|
});
|
|
623
|
+
});
|
|
624
|
+
if (diagnostics.length > 0 || witnesses.length === 0) return { diagnostics };
|
|
625
|
+
const witnessedBindings = new Set(witnesses.map((witness) => nodeName(witness.binding.local)));
|
|
626
|
+
const builder = new VirtualSourceBuilder(source);
|
|
627
|
+
const pendingNames = new Set(witnessedBindings);
|
|
628
|
+
const renderDeclarations = (declarations) => {
|
|
629
|
+
for (const declaration of declarations) {
|
|
630
|
+
if (declaration.type !== "ImportDeclaration") continue;
|
|
631
|
+
const rendered = renderImport(declaration, pendingNames, source);
|
|
632
|
+
if (!rendered) continue;
|
|
633
|
+
builder.append(rendered.text);
|
|
634
|
+
for (const local of rendered.locals) pendingNames.delete(local);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
renderDeclarations(script.content.body);
|
|
638
|
+
if (inherited) renderDeclarations(inherited.importDeclarations);
|
|
639
|
+
for (const witness of witnesses) {
|
|
640
|
+
const resolverName = nodeName(witness.resolver);
|
|
641
|
+
builder.append("<");
|
|
642
|
+
builder.appendMapped(resolverName, "resolver", witness.resolver);
|
|
643
|
+
for (const property of witness.properties) {
|
|
644
|
+
builder.append(" ");
|
|
645
|
+
builder.appendMapped(property.name, "attribute", property.key);
|
|
646
|
+
builder.append("={");
|
|
647
|
+
builder.appendMapped(source.slice(property.value.start, property.value.end), "value", property.value);
|
|
648
|
+
builder.append("}");
|
|
649
|
+
}
|
|
650
|
+
builder.append(" />;\n");
|
|
977
651
|
}
|
|
978
652
|
return {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
fileOwners,
|
|
987
|
-
outcomes
|
|
653
|
+
diagnostics: [],
|
|
654
|
+
entry: {
|
|
655
|
+
scope,
|
|
656
|
+
path: `${originalPath}.${scope}.tsx`,
|
|
657
|
+
source: builder.toString(),
|
|
658
|
+
mappings: builder.mappings
|
|
659
|
+
}
|
|
988
660
|
};
|
|
989
661
|
}
|
|
990
|
-
/** First-declared specifier per package dir — the single-value ownership
|
|
991
|
-
* view correlation consumers key on, derived from `dirOwnerSets`. */
|
|
992
|
-
function firstOwners(dirOwnerSets) {
|
|
993
|
-
const owners = {};
|
|
994
|
-
for (const [dir, specifiers] of Object.entries(dirOwnerSets)) if (specifiers.length > 0) owners[dir] = specifiers[0];
|
|
995
|
-
return owners;
|
|
996
|
-
}
|
|
997
662
|
/**
|
|
998
|
-
*
|
|
999
|
-
*
|
|
1000
|
-
* carries no entries, package-map targets, source entries, package dirs, or
|
|
1001
|
-
* ownership for the rejected packages — a package dir is removed only when
|
|
1002
|
-
* EVERY specifier that claimed it is rejected (a dir-level rejection, such
|
|
1003
|
-
* as the cross-volume gate, always rejects them together). Outcome records
|
|
1004
|
-
* are reporting inputs, not membership, and pass through untouched.
|
|
663
|
+
* Project Svelte script scopes into parser-only TSX witnesses from SOURCE,
|
|
664
|
+
* never compiler output. Unsupported call shapes fail the whole source closed.
|
|
1005
665
|
*/
|
|
1006
|
-
function
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
dirOwnerSets[dir] = kept;
|
|
1031
|
-
}
|
|
1032
|
-
const dirExtensions = {};
|
|
1033
|
-
for (const [dir, exts] of Object.entries(collected.dirExtensions)) {
|
|
1034
|
-
if (rejectedDirs.includes(dir)) continue;
|
|
1035
|
-
dirExtensions[dir] = exts;
|
|
666
|
+
async function adaptSvelteSource(source, originalPath, options) {
|
|
667
|
+
const original = {
|
|
668
|
+
path: originalPath,
|
|
669
|
+
hash: contentHash(source)
|
|
670
|
+
};
|
|
671
|
+
const compiler = await loadCompiler();
|
|
672
|
+
if (!compiler) return {
|
|
673
|
+
kind: "missing-dep",
|
|
674
|
+
original,
|
|
675
|
+
dependency: "svelte/compiler"
|
|
676
|
+
};
|
|
677
|
+
let compilerAst;
|
|
678
|
+
try {
|
|
679
|
+
compilerAst = compiler.parse(source, {
|
|
680
|
+
filename: originalPath,
|
|
681
|
+
modern: true
|
|
682
|
+
});
|
|
683
|
+
} catch (error) {
|
|
684
|
+
const range = compilerParseErrorRange(error, source);
|
|
685
|
+
return {
|
|
686
|
+
kind: "error",
|
|
687
|
+
original,
|
|
688
|
+
diagnostics: [diagnostic("SVELTE_PARSE_ERROR", error instanceof Error ? error.message : String(error), originalPath, source, range?.node, range?.location)]
|
|
689
|
+
};
|
|
1036
690
|
}
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
691
|
+
const ast = parseSvelteAst(compilerAst);
|
|
692
|
+
const projections = [];
|
|
693
|
+
const moduleBindings = ast.module ? importBindings(ast.module.content) : void 0;
|
|
694
|
+
const instanceBindings = ast.instance ? importBindings(ast.instance.content) : void 0;
|
|
695
|
+
const moduleScope = ast.module && moduleBindings ? {
|
|
696
|
+
bindings: moduleBindings,
|
|
697
|
+
importDeclarations: ast.module.content.body.filter((declaration) => declaration.type === "ImportDeclaration")
|
|
698
|
+
} : void 0;
|
|
699
|
+
if (ast.module) projections.push(projectScope(ast.module, "module", source, originalPath, options, void 0, moduleBindings));
|
|
700
|
+
if (ast.instance) projections.push(projectScope(ast.instance, "instance", source, originalPath, options, moduleScope, instanceBindings));
|
|
701
|
+
const diagnostics = projections.flatMap((projection) => projection.diagnostics);
|
|
702
|
+
if (ast.fragment) {
|
|
703
|
+
const scriptBindings = instanceBindings ?? /* @__PURE__ */ new Map();
|
|
704
|
+
const fragmentBindings = moduleScope ? new Map([...moduleScope.bindings, ...scriptBindings]) : scriptBindings;
|
|
705
|
+
if (fragmentBindings.size > 0) walkFragment(ast.fragment, (node) => {
|
|
706
|
+
const site = resolverCallSite(node, fragmentBindings);
|
|
707
|
+
if (!site) return;
|
|
708
|
+
if (options.attributeResolver(site.access.request) === "other") return;
|
|
709
|
+
diagnostics.push(diagnostic("SVELTE_ATTRS_TEMPLATE_UNSUPPORTED", `Resolver '${site.access.displayName}' is called inside the template; assign the result to a const in the instance script and spread that instead.`, originalPath, source, node));
|
|
710
|
+
});
|
|
1041
711
|
}
|
|
712
|
+
if (diagnostics.length > 0) return {
|
|
713
|
+
kind: "error",
|
|
714
|
+
original,
|
|
715
|
+
diagnostics
|
|
716
|
+
};
|
|
1042
717
|
return {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
keyframesScanEntries,
|
|
1047
|
-
packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
1048
|
-
dirOwnerSets,
|
|
1049
|
-
dirExtensions,
|
|
1050
|
-
fileOwners,
|
|
1051
|
-
outcomes: collected.outcomes
|
|
718
|
+
kind: "ok",
|
|
719
|
+
original,
|
|
720
|
+
entries: projections.flatMap((projection) => projection.entry ? [projection.entry] : [])
|
|
1052
721
|
};
|
|
1053
722
|
}
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region pipeline/source-ingestion.ts
|
|
725
|
+
function isSerializedSourceEntry(value) {
|
|
726
|
+
if (!(value instanceof Object) || Array.isArray(value)) return false;
|
|
727
|
+
return String(value.path) === value.path && String(value.source) === value.source && (value.hash === void 0 || String(value.hash) === value.hash);
|
|
728
|
+
}
|
|
1054
729
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
*
|
|
1057
|
-
*
|
|
1058
|
-
* Supports four forms:
|
|
1059
|
-
* - Primary: `createSystem(...).extend(identifier)` chain calls
|
|
1060
|
-
* (repeatable, mixable with `.from()` links; each call
|
|
1061
|
-
* contributes one source; a library-bundle identifier
|
|
1062
|
-
* traces its base import)
|
|
1063
|
-
* - Deprecated chain: `createSystem(...).from(identifier)` chain calls
|
|
1064
|
-
* - Deprecated alias: `createSystem({ includes: [identifier, ...] })`
|
|
1065
|
-
* - Legacy: `.includes([identifier, ...])` chain method (RC
|
|
1066
|
-
* migration fallback)
|
|
1067
|
-
*
|
|
1068
|
-
* For each identifier found, traces back to its import declaration and returns
|
|
1069
|
-
* the import specifier: a bare specifier normalized to its package name, a
|
|
1070
|
-
* relative specifier resolved against the system file's directory into an
|
|
1071
|
-
* absolute path (so a sibling package referenced by path contributes discovery
|
|
1072
|
-
* too). Only packages explicitly declared through one of these forms are
|
|
1073
|
-
* treated as external DS dependencies.
|
|
1074
|
-
*
|
|
1075
|
-
* Falls back to empty array if no declaration is found.
|
|
1076
|
-
*/
|
|
1077
|
-
/**
|
|
1078
|
-
* The message for the strict/warn gate over unresolvable includes, or null
|
|
1079
|
-
* when every declared specifier resolved (external-package-file-discovery:
|
|
1080
|
-
* silence is never an outcome — non-strict consumers warn with this line,
|
|
1081
|
-
* strict consumers throw it).
|
|
730
|
+
* Throws on a payload that is not a corpus: an empty corpus is
|
|
731
|
+
* indistinguishable from "no files" and would publish an empty stylesheet.
|
|
1082
732
|
*/
|
|
1083
|
-
function
|
|
1084
|
-
const
|
|
1085
|
-
if (
|
|
1086
|
-
return
|
|
733
|
+
function parseFilesJson(filesJson, context) {
|
|
734
|
+
const candidate = parseInternalWire(filesJson, `${context} filesJson`);
|
|
735
|
+
if (!Array.isArray(candidate) || !candidate.every(isSerializedSourceEntry)) throw new TypeError(`[${context}] analysis files JSON must be an array of {path, source} entries`);
|
|
736
|
+
return candidate;
|
|
1087
737
|
}
|
|
1088
738
|
/**
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
* A stale dist silently skews merged registry content while discovery
|
|
1092
|
-
* compiles the fresh sources, so it surfaces like an unresolvable specifier:
|
|
1093
|
-
* non-strict consumers warn with this line, strict consumers throw it.
|
|
739
|
+
* Advisory diagnostics warn in every mode and never quarantine their file:
|
|
740
|
+
* OXC recovers parse errors the host bundler itself accepts. All else is fatal.
|
|
1094
741
|
*/
|
|
1095
|
-
function
|
|
1096
|
-
|
|
1097
|
-
if (stale.length === 0) return null;
|
|
1098
|
-
return `[animus-extract] stale dist for include specifier(s): ${stale.join(", ")} — dist entry is older than the newest src/ file; rebuild the package(s) before extracting`;
|
|
742
|
+
function isAdvisorySourceDiagnostic(diagnostic) {
|
|
743
|
+
return diagnostic.code === "SOURCE_NATIVE_PARSE_ERROR";
|
|
1099
744
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
745
|
+
const RELATIVE_PROBE_SUFFIXES = [
|
|
746
|
+
"",
|
|
747
|
+
".ts",
|
|
748
|
+
".tsx",
|
|
749
|
+
".js",
|
|
750
|
+
".jsx",
|
|
751
|
+
"/index.ts",
|
|
752
|
+
"/index.tsx",
|
|
753
|
+
"/index.js",
|
|
754
|
+
"/index.jsx"
|
|
755
|
+
];
|
|
756
|
+
function extension(path) {
|
|
757
|
+
const basename = node_path.posix.basename(path);
|
|
758
|
+
const dot = basename.lastIndexOf(".");
|
|
759
|
+
return dot === -1 ? "" : basename.slice(dot).toLowerCase();
|
|
760
|
+
}
|
|
761
|
+
function canonicalResolverPath(path) {
|
|
762
|
+
return node_path.posix.normalize(path.replaceAll("\\", "/"));
|
|
763
|
+
}
|
|
764
|
+
const NODE_NEXT_EXTENSION_MAP = {
|
|
765
|
+
".js": [
|
|
766
|
+
".ts",
|
|
767
|
+
".tsx",
|
|
768
|
+
".jsx"
|
|
769
|
+
],
|
|
770
|
+
".mjs": [".mts"],
|
|
771
|
+
".cjs": [".cts"]
|
|
772
|
+
};
|
|
773
|
+
function resolveRelativeSource(importerPath, specifier, files) {
|
|
774
|
+
if (!specifier.startsWith(".")) return null;
|
|
775
|
+
const base = node_path.posix.normalize(node_path.posix.join(node_path.posix.dirname(canonicalResolverPath(importerPath)), specifier));
|
|
776
|
+
for (const suffix of RELATIVE_PROBE_SUFFIXES) {
|
|
777
|
+
const candidate = `${base}${suffix}`;
|
|
778
|
+
const actualPath = files.get(candidate);
|
|
779
|
+
if (actualPath !== void 0) return actualPath;
|
|
1106
780
|
}
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}
|
|
1133
|
-
if (source.startsWith("/*", pos)) {
|
|
1134
|
-
const close = source.indexOf("*/", pos + 2);
|
|
1135
|
-
pos = close === -1 ? source.length : close + 2;
|
|
1136
|
-
continue;
|
|
1137
|
-
}
|
|
1138
|
-
return pos;
|
|
781
|
+
const explicitExtension = extension(base);
|
|
782
|
+
for (const sourceExtension of NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []) {
|
|
783
|
+
const candidate = `${base.slice(0, -explicitExtension.length)}${sourceExtension}`;
|
|
784
|
+
const actualPath = files.get(candidate);
|
|
785
|
+
if (actualPath !== void 0) return actualPath;
|
|
786
|
+
}
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
var ResolverExportIndex = class {
|
|
790
|
+
facts;
|
|
791
|
+
collisions;
|
|
792
|
+
files;
|
|
793
|
+
resolverBindings = /* @__PURE__ */ new Map();
|
|
794
|
+
/** Both memos live for one ingest only — the index is rebuilt per call,
|
|
795
|
+
* so neither needs invalidation. */
|
|
796
|
+
attributionMemo = /* @__PURE__ */ new Map();
|
|
797
|
+
resolveMemo = /* @__PURE__ */ new Map();
|
|
798
|
+
constructor(facts, analysisPaths) {
|
|
799
|
+
this.facts = facts;
|
|
800
|
+
const pathsByCanonical = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const path of analysisPaths) {
|
|
802
|
+
const canonicalPath = canonicalResolverPath(path);
|
|
803
|
+
const paths = pathsByCanonical.get(canonicalPath) ?? [];
|
|
804
|
+
paths.push(path);
|
|
805
|
+
pathsByCanonical.set(canonicalPath, paths);
|
|
1139
806
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
let cursor = skipTrivia(pos);
|
|
1151
|
-
if (source[cursor] !== ".") return pos;
|
|
1152
|
-
cursor = skipTrivia(cursor + 1);
|
|
1153
|
-
const method = IDENT_START_RE.exec(source.slice(cursor))?.[0];
|
|
1154
|
-
if (method !== "extend" && method !== "from") return pos;
|
|
1155
|
-
cursor = skipTrivia(cursor + method.length);
|
|
1156
|
-
if (source[cursor] !== "(") return pos;
|
|
1157
|
-
cursor = skipTrivia(cursor + 1);
|
|
1158
|
-
const base = IDENT_START_RE.exec(source.slice(cursor))?.[0];
|
|
1159
|
-
if (!base) return pos;
|
|
1160
|
-
cursor = skipTrivia(cursor + base.length);
|
|
1161
|
-
while (source[cursor] === ".") {
|
|
1162
|
-
const afterDot = skipTrivia(cursor + 1);
|
|
1163
|
-
const segment = IDENT_START_RE.exec(source.slice(afterDot))?.[0];
|
|
1164
|
-
if (!segment) break;
|
|
1165
|
-
cursor = skipTrivia(afterDot + segment.length);
|
|
1166
|
-
}
|
|
1167
|
-
if (source[cursor] === ",") cursor = skipTrivia(cursor + 1);
|
|
1168
|
-
if (source[cursor] !== ")") return pos;
|
|
1169
|
-
identifiers.add(base);
|
|
1170
|
-
pos = cursor + 1;
|
|
807
|
+
this.collisions = [...pathsByCanonical].filter(([, paths]) => paths.length > 1).map(([canonicalPath, paths]) => ({
|
|
808
|
+
canonicalPath,
|
|
809
|
+
paths
|
|
810
|
+
}));
|
|
811
|
+
const factPathsByCanonical = /* @__PURE__ */ new Map();
|
|
812
|
+
for (const path of Object.keys(facts.files)) {
|
|
813
|
+
const canonicalPath = canonicalResolverPath(path);
|
|
814
|
+
const paths = factPathsByCanonical.get(canonicalPath) ?? [];
|
|
815
|
+
paths.push(path);
|
|
816
|
+
factPathsByCanonical.set(canonicalPath, paths);
|
|
1171
817
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
return match ? match[1] : null;
|
|
1177
|
-
};
|
|
1178
|
-
const chainRootIdentifiers = /* @__PURE__ */ new Set();
|
|
1179
|
-
const createSystemAnchor = /createSystem\s*\(/g;
|
|
1180
|
-
let anchorMatch;
|
|
1181
|
-
while ((anchorMatch = createSystemAnchor.exec(source)) !== null) {
|
|
1182
|
-
let pos = anchorMatch.index + anchorMatch[0].length;
|
|
1183
|
-
let depth = 1;
|
|
1184
|
-
while (pos < source.length && depth > 0) {
|
|
1185
|
-
const ch = source[pos];
|
|
1186
|
-
if (ch === "(") depth++;
|
|
1187
|
-
else if (ch === ")") depth--;
|
|
1188
|
-
pos++;
|
|
818
|
+
const files = /* @__PURE__ */ new Map();
|
|
819
|
+
for (const [canonicalPath, paths] of pathsByCanonical) {
|
|
820
|
+
const factPaths = factPathsByCanonical.get(canonicalPath) ?? [];
|
|
821
|
+
if (paths.length === 1 && factPaths.length === 1) files.set(canonicalPath, factPaths[0]);
|
|
1189
822
|
}
|
|
1190
|
-
|
|
1191
|
-
const
|
|
1192
|
-
if (bound) chainRootIdentifiers.add(bound);
|
|
823
|
+
this.files = files;
|
|
824
|
+
for (const [path, file] of Object.entries(facts.files)) this.resolverBindings.set(path, new Set(file.chains.filter((chain) => chain.descriptor.terminal === "asClass" && chain.descriptor.extractable && chain.fatalError === null).map((chain) => chain.descriptor.binding)));
|
|
1193
825
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const
|
|
1197
|
-
if (
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
826
|
+
attribute(importerPath, request) {
|
|
827
|
+
const memoKey = `${importerPath}\0${request.source}\0${request.imported}\0${request.access.kind}\0${request.access.importKind}`;
|
|
828
|
+
const memoized = this.attributionMemo.get(memoKey);
|
|
829
|
+
if (memoized !== void 0) return memoized;
|
|
830
|
+
const attribution = this.attributeUncached(importerPath, request);
|
|
831
|
+
this.attributionMemo.set(memoKey, attribution);
|
|
832
|
+
return attribution;
|
|
833
|
+
}
|
|
834
|
+
attributeUncached(importerPath, request) {
|
|
835
|
+
const importedFile = this.resolveSource(importerPath, request.source);
|
|
836
|
+
if (!importedFile) return "other";
|
|
837
|
+
if (this.resolveClassExport(importedFile, request.imported, /* @__PURE__ */ new Set()) === null) return "other";
|
|
838
|
+
return request.access.kind === "direct" && request.access.importKind === "named" ? "resolver" : "unsupported-resolver-form";
|
|
839
|
+
}
|
|
840
|
+
resolveSource(importerPath, specifier) {
|
|
841
|
+
const key = `${importerPath}\0${specifier}`;
|
|
842
|
+
let resolved = this.resolveMemo.get(key);
|
|
843
|
+
if (resolved === void 0) {
|
|
844
|
+
resolved = resolveRelativeSource(importerPath, specifier, this.files);
|
|
845
|
+
this.resolveMemo.set(key, resolved);
|
|
1208
846
|
}
|
|
847
|
+
return resolved;
|
|
1209
848
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
const
|
|
1216
|
-
if (
|
|
1217
|
-
if (
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const
|
|
1221
|
-
if (
|
|
849
|
+
resolveClassExport(path, exportedName, seen) {
|
|
850
|
+
const identity = `${path}\0${exportedName}`;
|
|
851
|
+
if (seen.has(identity)) return null;
|
|
852
|
+
seen.add(identity);
|
|
853
|
+
const file = this.facts.files[path];
|
|
854
|
+
const exported = file?.exports.find((candidate) => candidate.exported === exportedName);
|
|
855
|
+
if (!file || !exported) return null;
|
|
856
|
+
if (exported.source === null) {
|
|
857
|
+
if (exported.local === null) return null;
|
|
858
|
+
if (this.resolverBindings.get(path)?.has(exported.local)) return exported.local;
|
|
859
|
+
const imported = file.imports.find((candidate) => candidate.local === exported.local);
|
|
860
|
+
if (!imported) return null;
|
|
861
|
+
const importedFile = this.resolveSource(path, imported.source);
|
|
862
|
+
return importedFile ? this.resolveClassExport(importedFile, imported.imported, seen) : null;
|
|
1222
863
|
}
|
|
864
|
+
if (exported.original === null) return null;
|
|
865
|
+
const nextFile = this.resolveSource(path, exported.source);
|
|
866
|
+
return nextFile ? this.resolveClassExport(nextFile, exported.original, seen) : null;
|
|
1223
867
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
868
|
+
};
|
|
869
|
+
/**
|
|
870
|
+
* Native and MDX entries must establish the resolver index before any Svelte
|
|
871
|
+
* projection; bare-package and aliased sources stay unattributed by design.
|
|
872
|
+
*/
|
|
873
|
+
async function ingestSourceEntries(rawEntries, options) {
|
|
874
|
+
const mdxAdapter = options.preprocessMdx ?? preprocessMdx;
|
|
875
|
+
const svelteAdapter = options.adaptSvelte ?? adaptSvelteSource;
|
|
876
|
+
const originalEntries = rawEntries.map((entry) => ({
|
|
877
|
+
...entry,
|
|
878
|
+
hash: entry.hash ?? contentHash(entry.source)
|
|
879
|
+
}));
|
|
880
|
+
const rawOriginalPaths = new Set(originalEntries.map((entry) => entry.path));
|
|
881
|
+
const analysisEntries = [];
|
|
882
|
+
const ownership = {};
|
|
883
|
+
const analysisOwner = /* @__PURE__ */ new Map();
|
|
884
|
+
const diagnostics = [];
|
|
885
|
+
const svelteEntries = [];
|
|
886
|
+
const addAnalysisEntry = (originalPath, path, source, hash) => {
|
|
887
|
+
analysisEntries.push({
|
|
888
|
+
path,
|
|
889
|
+
source,
|
|
890
|
+
hash: hash ?? contentHash(source)
|
|
891
|
+
});
|
|
892
|
+
ownership[originalPath].analysisPaths.push(path);
|
|
893
|
+
analysisOwner.set(path, originalPath);
|
|
894
|
+
};
|
|
895
|
+
const addAdaptedEntries = (originalPath, entries) => {
|
|
896
|
+
const pendingPaths = /* @__PURE__ */ new Set();
|
|
897
|
+
for (const entry of entries) {
|
|
898
|
+
const conflictingOriginalPath = rawOriginalPaths.has(entry.path) ? entry.path : analysisOwner.get(entry.path) ?? (pendingPaths.has(entry.path) ? originalPath : null);
|
|
899
|
+
if (conflictingOriginalPath !== null) {
|
|
900
|
+
diagnostics.push({
|
|
901
|
+
code: "SOURCE_ANALYSIS_PATH_COLLISION",
|
|
902
|
+
message: `Generated analysis path '${entry.path}' collides with '${conflictingOriginalPath}'. Rename one of the source files so adapted analysis paths remain unique.`,
|
|
903
|
+
originalPath,
|
|
904
|
+
analysisPath: entry.path,
|
|
905
|
+
conflictingOriginalPath
|
|
906
|
+
});
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
pendingPaths.add(entry.path);
|
|
910
|
+
}
|
|
911
|
+
for (const entry of entries) addAnalysisEntry(originalPath, entry.path, entry.source);
|
|
912
|
+
};
|
|
913
|
+
for (const original of originalEntries) {
|
|
914
|
+
ownership[original.path] = {
|
|
915
|
+
originalPath: original.path,
|
|
916
|
+
originalHash: original.hash,
|
|
917
|
+
analysisPaths: []
|
|
918
|
+
};
|
|
919
|
+
const kind = extension(original.path);
|
|
920
|
+
if (kind === ".svelte") {
|
|
921
|
+
svelteEntries.push(original);
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
if (kind !== ".mdx") {
|
|
925
|
+
addAnalysisEntry(original.path, original.path, original.source, original.hash);
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
const result = await mdxAdapter(original.source, original.path);
|
|
929
|
+
if (result.kind === "missing-dep") {
|
|
930
|
+
diagnostics.push({
|
|
931
|
+
code: "SOURCE_MDX_DEPENDENCY_MISSING",
|
|
932
|
+
message: "Install optional peer dependency '@mdx-js/mdx' to analyze opted-in MDX source.",
|
|
933
|
+
originalPath: original.path
|
|
934
|
+
});
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (result.kind === "error" || result.source === void 0) {
|
|
938
|
+
diagnostics.push({
|
|
939
|
+
code: "SOURCE_MDX_PARSE_ERROR",
|
|
940
|
+
message: result.error ?? "MDX preprocessing produced no source.",
|
|
941
|
+
originalPath: original.path
|
|
942
|
+
});
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
addAdaptedEntries(original.path, [{
|
|
946
|
+
path: `${original.path}.tsx`,
|
|
947
|
+
source: result.source
|
|
948
|
+
}]);
|
|
1231
949
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
}
|
|
1242
|
-
function nodeName(node) {
|
|
1243
|
-
return node?.type === "Identifier" && typeof node.name === "string" ? node.name : null;
|
|
1244
|
-
}
|
|
1245
|
-
/** Adaptation touches one source at a time, so a last-value memo makes the
|
|
1246
|
-
* all-ASCII check O(n) once per file instead of per span endpoint. */
|
|
1247
|
-
let asciiMemoSource = null;
|
|
1248
|
-
let asciiMemoValue = false;
|
|
1249
|
-
function isAsciiOnly(source) {
|
|
1250
|
-
if (asciiMemoSource !== source) {
|
|
1251
|
-
asciiMemoSource = source;
|
|
1252
|
-
asciiMemoValue = Buffer.byteLength(source) === source.length;
|
|
950
|
+
const facts = collectFileFacts(analysisEntries, options);
|
|
951
|
+
for (const [analysisPath, file] of Object.entries(facts.files)) {
|
|
952
|
+
const originalPath = analysisOwner.get(analysisPath) ?? analysisPath;
|
|
953
|
+
for (const message of file.parseDiagnostics) diagnostics.push({
|
|
954
|
+
code: "SOURCE_NATIVE_PARSE_ERROR",
|
|
955
|
+
message,
|
|
956
|
+
originalPath,
|
|
957
|
+
analysisPath
|
|
958
|
+
});
|
|
1253
959
|
}
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
|
|
960
|
+
const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path));
|
|
961
|
+
for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
|
|
962
|
+
const path = paths[index];
|
|
963
|
+
const conflictingPath = paths[(index + 1) % paths.length];
|
|
964
|
+
diagnostics.push({
|
|
965
|
+
code: "SOURCE_RESOLVER_IDENTITY_COLLISION",
|
|
966
|
+
message: `Resolver lookup paths '${path}' and '${conflictingPath}' normalize to the same private identity '${canonicalPath}'. Rename one of the source files so resolver identities remain unique.`,
|
|
967
|
+
originalPath: analysisOwner.get(path) ?? path,
|
|
968
|
+
canonicalPath,
|
|
969
|
+
conflictingOriginalPath: analysisOwner.get(conflictingPath) ?? conflictingPath
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
for (const original of svelteEntries) {
|
|
973
|
+
const result = await svelteAdapter(original.source, original.path, { attributeResolver: (request) => resolvers.attribute(original.path, request) });
|
|
974
|
+
if (result.kind === "missing-dep") {
|
|
975
|
+
diagnostics.push({
|
|
976
|
+
code: "SOURCE_SVELTE_DEPENDENCY_MISSING",
|
|
977
|
+
message: "Install optional peer dependency 'svelte' to analyze opted-in Svelte source.",
|
|
978
|
+
originalPath: original.path
|
|
979
|
+
});
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (result.kind === "error") {
|
|
983
|
+
diagnostics.push(...result.diagnostics);
|
|
984
|
+
continue;
|
|
1278
985
|
}
|
|
986
|
+
addAdaptedEntries(original.path, result.entries);
|
|
1279
987
|
}
|
|
1280
988
|
return {
|
|
1281
|
-
|
|
1282
|
-
|
|
989
|
+
originalEntries,
|
|
990
|
+
analysisEntries,
|
|
991
|
+
ownership,
|
|
992
|
+
diagnostics
|
|
1283
993
|
};
|
|
1284
994
|
}
|
|
1285
|
-
|
|
995
|
+
/**
|
|
996
|
+
* `parseCount` counts the parses performed by THIS call, so a fully-memoized
|
|
997
|
+
* pass reports zero.
|
|
998
|
+
*/
|
|
999
|
+
function collectFileFacts(analysisEntries, options) {
|
|
1000
|
+
const cache = options.factsCache;
|
|
1001
|
+
if (!cache) return JSON.parse(options.extractFacts(JSON.stringify(analysisEntries)));
|
|
1002
|
+
const pending = analysisEntries.filter((entry) => cache.get(entry.path)?.hash !== entry.hash);
|
|
1003
|
+
let parseCount = 0;
|
|
1004
|
+
if (pending.length > 0) {
|
|
1005
|
+
const fresh = JSON.parse(options.extractFacts(JSON.stringify(pending)));
|
|
1006
|
+
parseCount = fresh.parseCount;
|
|
1007
|
+
for (const entry of pending) {
|
|
1008
|
+
const facts = fresh.files[entry.path];
|
|
1009
|
+
if (facts) cache.set(entry.path, {
|
|
1010
|
+
hash: entry.hash,
|
|
1011
|
+
facts
|
|
1012
|
+
});
|
|
1013
|
+
else cache.delete(entry.path);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
const currentPaths = new Set(analysisEntries.map((entry) => entry.path));
|
|
1017
|
+
for (const path of [...cache.keys()]) if (!currentPaths.has(path)) cache.delete(path);
|
|
1018
|
+
const files = {};
|
|
1019
|
+
for (const entry of analysisEntries) {
|
|
1020
|
+
const cached = cache.get(entry.path);
|
|
1021
|
+
if (cached) files[entry.path] = cached.facts;
|
|
1022
|
+
}
|
|
1286
1023
|
return {
|
|
1287
|
-
|
|
1288
|
-
|
|
1024
|
+
files,
|
|
1025
|
+
parseCount
|
|
1289
1026
|
};
|
|
1290
1027
|
}
|
|
1291
|
-
function createsNestedBindingScope(node) {
|
|
1292
|
-
return node.type === "BlockStatement" || node.type === "CatchClause" || node.type === "ClassBody" || node.type === "ClassDeclaration" || node.type === "ClassExpression" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ForStatement" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "StaticBlock" || node.type === "SwitchStatement" || node.type === "TSModuleBlock";
|
|
1293
|
-
}
|
|
1294
|
-
function walk(node, visit, nestedScopeDepth = 0) {
|
|
1295
|
-
visit(node, nestedScopeDepth);
|
|
1296
|
-
const childScopeDepth = nestedScopeDepth + (createsNestedBindingScope(node) ? 1 : 0);
|
|
1297
|
-
for (const [key, value] of Object.entries(node)) {
|
|
1298
|
-
if (key === "loc" || key === "start" || key === "end") continue;
|
|
1299
|
-
if (Array.isArray(value)) {
|
|
1300
|
-
for (const item of value) if (isNode(item)) walk(item, visit, childScopeDepth);
|
|
1301
|
-
} else if (isNode(value)) walk(value, visit, childScopeDepth);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
1028
|
/**
|
|
1305
|
-
*
|
|
1306
|
-
*
|
|
1307
|
-
* gate would stop at each one. Descend through every plain object/array,
|
|
1308
|
-
* visit only span-carrying nodes, and guard against metadata back-references.
|
|
1029
|
+
* Drops each named original and the analysis children it owns. Every corpus
|
|
1030
|
+
* path applies it — skipping it on incremental passes freezes re-analysis.
|
|
1309
1031
|
*/
|
|
1310
|
-
function
|
|
1311
|
-
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
seen.add(current);
|
|
1320
|
-
if (isNode(current)) visit(current);
|
|
1321
|
-
for (const [key, child] of Object.entries(current)) {
|
|
1322
|
-
if (key === "loc" || key === "start" || key === "end" || key === "metadata" || key === "parent") continue;
|
|
1323
|
-
descend(child);
|
|
1324
|
-
}
|
|
1032
|
+
function withoutInvalidOriginals(result, invalidOriginals) {
|
|
1033
|
+
if (invalidOriginals.size === 0) return result;
|
|
1034
|
+
const ownership = Object.fromEntries(Object.entries(result.ownership).filter(([originalPath]) => !invalidOriginals.has(originalPath)));
|
|
1035
|
+
const analysisPaths = new Set(Object.values(ownership).flatMap((owner) => owner.analysisPaths));
|
|
1036
|
+
return {
|
|
1037
|
+
...result,
|
|
1038
|
+
originalEntries: result.originalEntries.filter((entry) => !invalidOriginals.has(entry.path)),
|
|
1039
|
+
analysisEntries: result.analysisEntries.filter((entry) => analysisPaths.has(entry.path)),
|
|
1040
|
+
ownership
|
|
1325
1041
|
};
|
|
1326
|
-
descend(value);
|
|
1327
1042
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
if (!importSource) continue;
|
|
1340
|
-
const namedImported = importedName(value);
|
|
1341
|
-
if (value.type === "ImportSpecifier" && namedImported === null) continue;
|
|
1342
|
-
const importedNode = childNode(value.imported);
|
|
1343
|
-
const kind = value.type === "ImportSpecifier" ? importedNode?.type === "Literal" ? "string-named" : namedImported === "default" ? "named-default" : "named" : value.type === "ImportDefaultSpecifier" ? "default" : value.type === "ImportNamespaceSpecifier" ? "namespace" : null;
|
|
1344
|
-
if (kind) bindings.set(localName, {
|
|
1345
|
-
kind,
|
|
1346
|
-
source: importSource,
|
|
1347
|
-
imported: kind === "default" || kind === "named-default" ? "default" : kind === "namespace" ? "*" : namedImported,
|
|
1348
|
-
declaration,
|
|
1349
|
-
specifier: value,
|
|
1350
|
-
local
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Diagnostics name ANALYSIS paths while ownership is recorded for originals,
|
|
1045
|
+
* so projection is required; the result REPLACES the caller's owner map.
|
|
1046
|
+
*/
|
|
1047
|
+
function projectExternalFileOwners(result, rawOwners) {
|
|
1048
|
+
const projected = {};
|
|
1049
|
+
for (const owner of Object.values(result.ownership)) {
|
|
1050
|
+
const packageOwner = rawOwners[owner.originalPath];
|
|
1051
|
+
if (!packageOwner) continue;
|
|
1052
|
+
projected[owner.originalPath] = packageOwner;
|
|
1053
|
+
for (const analysisPath of owner.analysisPaths) projected[analysisPath] = packageOwner;
|
|
1353
1054
|
}
|
|
1354
|
-
return
|
|
1055
|
+
return projected;
|
|
1355
1056
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
function compilerParseErrorRange(error, source) {
|
|
1368
|
-
if (typeof error !== "object" || error === null) return null;
|
|
1369
|
-
const candidate = error;
|
|
1370
|
-
const positions = Array.isArray(candidate.position) ? candidate.position : [];
|
|
1371
|
-
const startCharacter = typeof candidate.start?.character === "number" ? candidate.start.character : typeof positions[0] === "number" ? positions[0] : null;
|
|
1372
|
-
const endCharacter = typeof candidate.end?.character === "number" ? candidate.end.character : typeof positions[1] === "number" ? positions[1] : startCharacter;
|
|
1373
|
-
if (startCharacter === null || endCharacter === null || startCharacter < 0 || endCharacter < startCharacter || endCharacter > source.length) return null;
|
|
1374
|
-
const node = {
|
|
1375
|
-
type: "SvelteParseError",
|
|
1376
|
-
start: startCharacter,
|
|
1377
|
-
end: endCharacter
|
|
1378
|
-
};
|
|
1379
|
-
const fallback = sourceLocation(source, node);
|
|
1380
|
-
const point = (value, fallbackPoint) => ({
|
|
1381
|
-
line: typeof value?.line === "number" ? value.line : fallbackPoint.line,
|
|
1382
|
-
column: typeof value?.column === "number" ? value.column : fallbackPoint.column
|
|
1383
|
-
});
|
|
1057
|
+
/**
|
|
1058
|
+
* One ingestion policy point for every host: a host supplies only a prefix, a
|
|
1059
|
+
* strict flag, and a warn sink; policy and per-host memos stay here.
|
|
1060
|
+
*/
|
|
1061
|
+
function createSourceIngestor(host) {
|
|
1062
|
+
const factsCache = /* @__PURE__ */ new Map();
|
|
1063
|
+
/** Non-strict dedupe: a quarantined original re-ingests on every later
|
|
1064
|
+
* corpus pass, so re-warning it on each save is noise. */
|
|
1065
|
+
const warnedByOriginal = /* @__PURE__ */ new Map();
|
|
1384
1066
|
return {
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1067
|
+
async ingest(entries) {
|
|
1068
|
+
const extractFacts = host.engineApi().extractFacts;
|
|
1069
|
+
if (extractFacts === void 0) throw new Error(`${host.prefix} native engine does not expose extractFacts required for source adaptation`);
|
|
1070
|
+
return ingestSourceEntries(entries, {
|
|
1071
|
+
extractFacts,
|
|
1072
|
+
factsCache
|
|
1073
|
+
});
|
|
1074
|
+
},
|
|
1075
|
+
surfaceDiagnostics(diagnostics) {
|
|
1076
|
+
const fatal = diagnostics.filter((diagnostic) => !isAdvisorySourceDiagnostic(diagnostic));
|
|
1077
|
+
const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
|
|
1078
|
+
if (diagnostics.length === 0) return invalidOriginals;
|
|
1079
|
+
if (host.strict() && fatal.length > 0) {
|
|
1080
|
+
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`);
|
|
1081
|
+
throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
|
|
1082
|
+
}
|
|
1083
|
+
for (const diagnostic of diagnostics) {
|
|
1084
|
+
const line = `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`;
|
|
1085
|
+
let warned = warnedByOriginal.get(diagnostic.originalPath);
|
|
1086
|
+
if (!warned) {
|
|
1087
|
+
warned = /* @__PURE__ */ new Set();
|
|
1088
|
+
warnedByOriginal.set(diagnostic.originalPath, warned);
|
|
1089
|
+
}
|
|
1090
|
+
if (warned.has(line)) continue;
|
|
1091
|
+
warned.add(line);
|
|
1092
|
+
host.warn(`${host.prefix} ${line}`);
|
|
1093
|
+
}
|
|
1094
|
+
return invalidOriginals;
|
|
1095
|
+
},
|
|
1096
|
+
markPublished(result) {
|
|
1097
|
+
for (const originalPath of Object.keys(result.ownership)) warnedByOriginal.delete(originalPath);
|
|
1389
1098
|
}
|
|
1390
1099
|
};
|
|
1391
1100
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
function resolverImportAccess(receiver, bindings) {
|
|
1408
|
-
const directName = nodeName(receiver);
|
|
1409
|
-
if (directName) {
|
|
1410
|
-
const binding = bindings.get(directName);
|
|
1411
|
-
if (!binding) return null;
|
|
1101
|
+
//#endregion
|
|
1102
|
+
//#region pipeline/engine-adapter.ts
|
|
1103
|
+
/**
|
|
1104
|
+
* One engine API for both plugins. Imports neither the native binding nor any
|
|
1105
|
+
* plugin or bundler — every such dependency arrives injected.
|
|
1106
|
+
*/
|
|
1107
|
+
/**
|
|
1108
|
+
* Returns a function, invoked per call site, so `isV2()` and
|
|
1109
|
+
* `loadNativeEngine()` re-evaluate each time instead of being captured.
|
|
1110
|
+
*/
|
|
1111
|
+
function createV2EngineApi(deps) {
|
|
1112
|
+
const { label, isV2, loadNativeEngine, store } = deps;
|
|
1113
|
+
return () => {
|
|
1114
|
+
if (!isV2()) return loadNativeEngine();
|
|
1115
|
+
const native = loadNativeEngine();
|
|
1412
1116
|
return {
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1117
|
+
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1118
|
+
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1119
|
+
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson) => {
|
|
1120
|
+
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1121
|
+
const sent = /* @__PURE__ */ new Map();
|
|
1122
|
+
for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
|
|
1123
|
+
store.setSentSources(sent);
|
|
1124
|
+
const emitterConfig = emitterConfigJson ? JSON.parse(emitterConfigJson) : {};
|
|
1125
|
+
store.setEngine(null);
|
|
1126
|
+
const config = {
|
|
1127
|
+
runtimeImport: emitterConfig.runtime_import ?? void 0,
|
|
1128
|
+
cssModuleId: emitterConfig.css_module_id ?? void 0,
|
|
1129
|
+
systemPropsModuleId: emitterConfig.system_props_module_id ?? void 0,
|
|
1130
|
+
themeJson: scalesJson,
|
|
1131
|
+
variableMapJson,
|
|
1132
|
+
contextualVarsJson: contextualVarsJson ?? void 0,
|
|
1133
|
+
configJson: propConfigJson,
|
|
1134
|
+
groupRegistryJson,
|
|
1135
|
+
selectorAliasesJson: selectorAliasesJson ?? void 0,
|
|
1136
|
+
conditionAliasesJson: conditionAliasesJson ?? void 0,
|
|
1137
|
+
globalStyleBlocksJson: globalStyleBlocksJson ?? void 0,
|
|
1138
|
+
keyframesJson: keyframesJson ?? void 0,
|
|
1139
|
+
packageResolutionJson: packageResolutionJson ?? void 0,
|
|
1140
|
+
pathAliasesJson: pathAliasesJson ?? void 0,
|
|
1141
|
+
staticCssJson: staticCssJson ?? void 0,
|
|
1142
|
+
externalDirsJson: externalDirsJson ?? void 0,
|
|
1143
|
+
transformSourcesJson: transformSourcesJson ?? void 0,
|
|
1144
|
+
devMode
|
|
1145
|
+
};
|
|
1146
|
+
const engine = new native.ExtractEngine(config);
|
|
1147
|
+
store.setEngine(engine);
|
|
1148
|
+
return engine.analyze(filesJson);
|
|
1149
|
+
},
|
|
1150
|
+
transformFile: (source, path, _manifest) => {
|
|
1151
|
+
const engine = store.getEngine();
|
|
1152
|
+
if (!engine) throw new Error(`[${label}] v2 transform before analyze — engine instance not initialized`);
|
|
1153
|
+
const sentMap = store.getSentSources();
|
|
1154
|
+
if (deps.passThroughUnknownPaths && sentMap && !sentMap.has(path)) return {
|
|
1155
|
+
code: source,
|
|
1156
|
+
hasComponents: false
|
|
1157
|
+
};
|
|
1158
|
+
const sent = sentMap?.get(path);
|
|
1159
|
+
if (sent !== void 0 && sent !== source && !store.getDriftWarned()) {
|
|
1160
|
+
store.setDriftWarned(true);
|
|
1161
|
+
console.warn(`[${label}] v2: transform-time source for ${path} differs from analyze-time source — an upstream transform may be reverted`);
|
|
1423
1162
|
}
|
|
1163
|
+
return JSON.parse(engine.transformFile(path));
|
|
1164
|
+
},
|
|
1165
|
+
clearAnalysisCache: () => {
|
|
1166
|
+
const engine = store.getEngine();
|
|
1167
|
+
if (engine) engine.clearCache();
|
|
1168
|
+
store.setEngine(null);
|
|
1424
1169
|
}
|
|
1425
1170
|
};
|
|
1426
|
-
}
|
|
1427
|
-
if (receiver.type !== "MemberExpression" || receiver.computed === true || receiver.optional === true) return null;
|
|
1428
|
-
const namespaceNode = childNode(receiver.object);
|
|
1429
|
-
const memberNode = childNode(receiver.property);
|
|
1430
|
-
const namespaceName = nodeName(namespaceNode);
|
|
1431
|
-
const memberName = nodeName(memberNode) ?? (memberNode?.type === "Literal" && typeof memberNode.value === "string" ? memberNode.value : null);
|
|
1432
|
-
if (!namespaceNode || !namespaceName || !memberName) return null;
|
|
1433
|
-
const binding = bindings.get(namespaceName);
|
|
1434
|
-
if (!binding || binding.kind !== "namespace") return null;
|
|
1435
|
-
return {
|
|
1436
|
-
binding,
|
|
1437
|
-
resolver: receiver,
|
|
1438
|
-
displayName: `${namespaceName}.${memberName}`,
|
|
1439
|
-
request: {
|
|
1440
|
-
source: binding.source,
|
|
1441
|
-
imported: memberName,
|
|
1442
|
-
local: namespaceName,
|
|
1443
|
-
access: {
|
|
1444
|
-
kind: "namespace-member",
|
|
1445
|
-
importKind: "namespace"
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
1171
|
};
|
|
1449
1172
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1173
|
+
//#endregion
|
|
1174
|
+
//#region pipeline/engine-retirement.ts
|
|
1175
|
+
const RETIRED_ENGINE_MESSAGE = "animus: extraction engine 'v1' was retired (openspec: retire-extract-v1) — v2 is the only engine; remove the engine option / ANIMUS_ENGINE override";
|
|
1176
|
+
function assertNoRetiredEngineSelection(engineOption) {
|
|
1177
|
+
if (engineOption === "v1" || process.env.ANIMUS_ENGINE === "v1") throw new Error(RETIRED_ENGINE_MESSAGE);
|
|
1452
1178
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
if (incompatible) return;
|
|
1499
|
-
if (node.type === "TSTypeAssertion" || node.type === "ArrowFunctionExpression" && childNode(node.typeParameters) !== null) incompatible = node;
|
|
1500
|
-
});
|
|
1501
|
-
return incompatible;
|
|
1502
|
-
}
|
|
1503
|
-
function readProperties(argument, source, originalPath) {
|
|
1504
|
-
if (!Array.isArray(argument.properties)) return diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", "Resolver .attrs() requires an object literal with statically named properties.", originalPath, source, argument);
|
|
1505
|
-
const properties = [];
|
|
1506
|
-
for (const value of argument.properties) {
|
|
1507
|
-
if (!isNode(value)) continue;
|
|
1508
|
-
if (value.type === "SpreadElement") return diagnostic("SVELTE_ATTRS_SPREAD_UNRESOLVED", "Resolver .attrs() object spreads are unresolved; enumerate properties explicitly.", originalPath, source, value);
|
|
1509
|
-
if (value.type !== "Property") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() accepts ordinary object properties only.", originalPath, source, value);
|
|
1510
|
-
if (value.computed === true) return diagnostic("SVELTE_ATTRS_COMPUTED_KEY", "Resolver .attrs() computed property names cannot be attributed safely.", originalPath, source, value);
|
|
1511
|
-
if (value.method === true || value.kind !== "init") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() methods and accessors cannot be projected as attributes.", originalPath, source, value);
|
|
1512
|
-
const key = childNode(value.key);
|
|
1513
|
-
const propertyValue = childNode(value.value);
|
|
1514
|
-
const name = propertyName(value);
|
|
1515
|
-
if (!key || !propertyValue || !name || !isJsxAttributeName(name)) return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() property names must be JSX-compatible identifier or string names.", originalPath, source, value);
|
|
1516
|
-
const incompatibleValue = tsxIncompatibleValue(propertyValue);
|
|
1517
|
-
if (incompatibleValue) return diagnostic("SVELTE_ATTRS_VALUE_UNSUPPORTED", "Resolver .attrs() values using angle-bracket type assertions or generic arrow parameters cannot be projected into TSX safely.", originalPath, source, incompatibleValue);
|
|
1518
|
-
properties.push({
|
|
1519
|
-
name,
|
|
1520
|
-
property: value,
|
|
1521
|
-
key,
|
|
1522
|
-
value: propertyValue
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
return properties;
|
|
1526
|
-
}
|
|
1527
|
-
var VirtualSourceBuilder = class {
|
|
1528
|
-
chunks = [];
|
|
1529
|
-
bytes = 0;
|
|
1530
|
-
mappings = [];
|
|
1531
|
-
constructor(originalSource) {
|
|
1532
|
-
this.originalSource = originalSource;
|
|
1533
|
-
}
|
|
1534
|
-
append(text) {
|
|
1535
|
-
this.chunks.push(text);
|
|
1536
|
-
this.bytes += Buffer.byteLength(text);
|
|
1179
|
+
//#endregion
|
|
1180
|
+
//#region pipeline/core-options.ts
|
|
1181
|
+
const DRIVER_NAMESPACES = [
|
|
1182
|
+
"vite",
|
|
1183
|
+
"next",
|
|
1184
|
+
"cli"
|
|
1185
|
+
];
|
|
1186
|
+
const DRIVER_NAMESPACE_KEYS = new Set(DRIVER_NAMESPACES);
|
|
1187
|
+
/**
|
|
1188
|
+
* Applied unconditionally: `node_modules` belongs to external-package
|
|
1189
|
+
* collection, and `.next`/`.animus` hold output, never source.
|
|
1190
|
+
*/
|
|
1191
|
+
const STRUCTURAL_EXCLUDE = [
|
|
1192
|
+
"node_modules",
|
|
1193
|
+
".next",
|
|
1194
|
+
".animus"
|
|
1195
|
+
];
|
|
1196
|
+
/** Defaults a user `exclude` list replaces; the structural set it cannot. */
|
|
1197
|
+
const REPLACEABLE_DEFAULT_EXCLUDE = [
|
|
1198
|
+
"dist",
|
|
1199
|
+
".test.",
|
|
1200
|
+
".spec."
|
|
1201
|
+
];
|
|
1202
|
+
const DEFAULT_EXCLUDE = [...STRUCTURAL_EXCLUDE, ...REPLACEABLE_DEFAULT_EXCLUDE];
|
|
1203
|
+
const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
1204
|
+
"system",
|
|
1205
|
+
"exclude",
|
|
1206
|
+
"extensions",
|
|
1207
|
+
"strict",
|
|
1208
|
+
"verbose",
|
|
1209
|
+
"prefix",
|
|
1210
|
+
"targets",
|
|
1211
|
+
"minify",
|
|
1212
|
+
"staticCss",
|
|
1213
|
+
"layers",
|
|
1214
|
+
"engine",
|
|
1215
|
+
"mode",
|
|
1216
|
+
"root"
|
|
1217
|
+
]);
|
|
1218
|
+
/** Carries a stable `name` so drivers can map it to their own config-error
|
|
1219
|
+
* surface (the CLI's exit code 2). */
|
|
1220
|
+
var AnimusConfigError = class extends Error {
|
|
1221
|
+
constructor(message) {
|
|
1222
|
+
super(message);
|
|
1223
|
+
this.name = "AnimusConfigError";
|
|
1537
1224
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
}
|
|
1225
|
+
};
|
|
1226
|
+
/**
|
|
1227
|
+
* `ownKeys` is the caller's own top-level surface; `rejectKeys` names core
|
|
1228
|
+
* keys it does not honor, which must fail rather than be silently ignored.
|
|
1229
|
+
*/
|
|
1230
|
+
function assertKnownOptionKeys(raw, ownKeys = [], rejectKeys = [], opts = {}) {
|
|
1231
|
+
const surface = (message) => {
|
|
1232
|
+
if (opts.onUnknownKey === "warn") {
|
|
1233
|
+
opts.warn(message);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
throw new AnimusConfigError(message);
|
|
1237
|
+
};
|
|
1238
|
+
const own = new Set(ownKeys);
|
|
1239
|
+
for (const key of Object.keys(raw)) {
|
|
1240
|
+
if (raw[key] === void 0) continue;
|
|
1241
|
+
const rejected = rejectKeys.find((entry) => entry.key === key);
|
|
1242
|
+
if (rejected) {
|
|
1243
|
+
surface(`Option "${key}" is not supported by this driver: ${rejected.reason}`);
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
if (CORE_OPTION_KEYS.has(key) || DRIVER_NAMESPACE_KEYS.has(key) || own.has(key)) continue;
|
|
1247
|
+
const nearest = suggestNearest(key);
|
|
1248
|
+
surface(`Unknown option "${key}".${nearest ? ` Did you mean "${nearest}"?` : ""} Core keys: ${[...CORE_OPTION_KEYS].join(", ")}. Driver namespaces: ${DRIVER_NAMESPACES.join(", ")}.`);
|
|
1549
1249
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1250
|
+
const mode = raw["mode"];
|
|
1251
|
+
if (mode !== void 0 && mode !== "development" && mode !== "production") throw new AnimusConfigError(`Invalid mode "${String(mode)}" — expected "development" or "production".`);
|
|
1252
|
+
for (const { key, ok, expected } of CORE_VALUE_GATES) {
|
|
1253
|
+
const value = raw[key];
|
|
1254
|
+
if (value === void 0 || ok(value)) continue;
|
|
1255
|
+
throw new AnimusConfigError(`Invalid value for "${key}" — expected ${expected}, got ${JSON.stringify(value)}.`);
|
|
1552
1256
|
}
|
|
1553
|
-
};
|
|
1554
|
-
function importedName(specifier) {
|
|
1555
|
-
const imported = childNode(specifier.imported);
|
|
1556
|
-
if (!imported) return null;
|
|
1557
|
-
if (imported.type === "Identifier" && typeof imported.name === "string") return imported.name;
|
|
1558
|
-
if (imported.type === "Literal" && typeof imported.value === "string") return imported.value;
|
|
1559
|
-
return null;
|
|
1560
1257
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1258
|
+
/**
|
|
1259
|
+
* Options are foreign JS, so a boxed `String`/`Boolean` must not pass: a
|
|
1260
|
+
* boxed value never behaves as the primitive its consumers expect.
|
|
1261
|
+
*/
|
|
1262
|
+
const isString = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
|
|
1263
|
+
const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
|
|
1264
|
+
const isStringArray = (value) => Array.isArray(value) && value.every(isString);
|
|
1265
|
+
const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
|
|
1266
|
+
const CORE_VALUE_GATES = [
|
|
1267
|
+
{
|
|
1268
|
+
key: "system",
|
|
1269
|
+
ok: isString,
|
|
1270
|
+
expected: "a string path"
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
key: "exclude",
|
|
1274
|
+
ok: isStringArray,
|
|
1275
|
+
expected: "an array of string patterns"
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
key: "extensions",
|
|
1279
|
+
ok: isStringArray,
|
|
1280
|
+
expected: "an array of string extensions"
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
key: "strict",
|
|
1284
|
+
ok: isBoolean,
|
|
1285
|
+
expected: "a boolean"
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
key: "verbose",
|
|
1289
|
+
ok: isBoolean,
|
|
1290
|
+
expected: "a boolean"
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
key: "minify",
|
|
1294
|
+
ok: isBoolean,
|
|
1295
|
+
expected: "a boolean"
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
key: "prefix",
|
|
1299
|
+
ok: isString,
|
|
1300
|
+
expected: "a string"
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
key: "targets",
|
|
1304
|
+
ok: isStringOrStringArray,
|
|
1305
|
+
expected: "a string or an array of strings"
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
key: "layers",
|
|
1309
|
+
ok: isStringArray,
|
|
1310
|
+
expected: "an array of layer names"
|
|
1571
1311
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1312
|
+
];
|
|
1313
|
+
function suggestNearest(key) {
|
|
1314
|
+
const lower = key.toLowerCase();
|
|
1315
|
+
let best = null;
|
|
1316
|
+
let bestScore = 3;
|
|
1317
|
+
for (const candidate of CORE_OPTION_KEYS) {
|
|
1318
|
+
const score = editDistance(lower, candidate.toLowerCase());
|
|
1319
|
+
if (score < bestScore) {
|
|
1320
|
+
bestScore = score;
|
|
1321
|
+
best = candidate;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return best;
|
|
1579
1325
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
let
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
compilerMemo = await import("svelte/compiler").catch(() => null);
|
|
1586
|
-
return compilerMemo;
|
|
1326
|
+
function editDistance(a, b) {
|
|
1327
|
+
const dp = Array.from({ length: a.length + 1 }, (_, i) => [i]);
|
|
1328
|
+
for (let j = 1; j <= b.length; j++) dp[0][j] = j;
|
|
1329
|
+
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
1330
|
+
return dp[a.length][b.length];
|
|
1587
1331
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
const
|
|
1601
|
-
if (
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
}
|
|
1605
|
-
if (nestedScopeDepth > 0) {
|
|
1606
|
-
diagnostics.push(diagnostic("SVELTE_ATTRS_SCOPE_UNSUPPORTED", `Resolver '${callDisplay}' is inside a nested binding scope; move the call to the top-level script scope so its import identity is unambiguous.`, originalPath, source, access.resolver));
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
if (site.computed || site.optional) {
|
|
1610
|
-
diagnostics.push(diagnostic("SVELTE_ATTRS_CALLEE_UNSUPPORTED", `Resolver '${resolverName}' must be called directly as ${resolverName}(...) or ${resolverName}.attrs(...).`, originalPath, source, node));
|
|
1611
|
-
return;
|
|
1612
|
-
}
|
|
1613
|
-
const argumentsList = Array.isArray(node.arguments) ? node.arguments.filter(isNode) : [];
|
|
1614
|
-
if (argumentsList.length === 0) {
|
|
1615
|
-
witnesses.push({
|
|
1616
|
-
binding: access.binding,
|
|
1617
|
-
resolver: access.resolver,
|
|
1618
|
-
properties: []
|
|
1619
|
-
});
|
|
1620
|
-
return;
|
|
1621
|
-
}
|
|
1622
|
-
if (argumentsList.length !== 1 || argumentsList[0].type !== "ObjectExpression") {
|
|
1623
|
-
diagnostics.push(diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", `Resolver '${callDisplay}' accepts no argument or one object literal in Svelte projection.`, originalPath, source, argumentSpan(argumentsList, node)));
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
const properties = readProperties(argumentsList[0], source, originalPath);
|
|
1627
|
-
if (!Array.isArray(properties)) {
|
|
1628
|
-
diagnostics.push(properties);
|
|
1629
|
-
return;
|
|
1630
|
-
}
|
|
1631
|
-
witnesses.push({
|
|
1632
|
-
binding: access.binding,
|
|
1633
|
-
resolver: access.resolver,
|
|
1634
|
-
properties
|
|
1635
|
-
});
|
|
1636
|
-
});
|
|
1637
|
-
if (diagnostics.length > 0 || witnesses.length === 0) return { diagnostics };
|
|
1638
|
-
const witnessedBindings = new Set(witnesses.map((witness) => nodeName(witness.binding.local)));
|
|
1639
|
-
const builder = new VirtualSourceBuilder(source);
|
|
1640
|
-
const pendingNames = new Set(witnessedBindings);
|
|
1641
|
-
const renderDeclarations = (declarations) => {
|
|
1642
|
-
for (const declaration of declarations) {
|
|
1643
|
-
if (declaration.type !== "ImportDeclaration") continue;
|
|
1644
|
-
const rendered = renderImport(declaration, pendingNames, source);
|
|
1645
|
-
if (!rendered) continue;
|
|
1646
|
-
builder.append(rendered.text);
|
|
1647
|
-
for (const local of rendered.locals) pendingNames.delete(local);
|
|
1648
|
-
}
|
|
1332
|
+
const GLOB_META = /[*?]/;
|
|
1333
|
+
/**
|
|
1334
|
+
* Globs compile to a RegExp over the root-relative path, matching the path or
|
|
1335
|
+
* any ancestor directory; plain patterns are substrings. `./` is stripped.
|
|
1336
|
+
*/
|
|
1337
|
+
function compilePattern(raw) {
|
|
1338
|
+
let pattern = raw.split("\\").join("/");
|
|
1339
|
+
while (pattern.startsWith("./")) pattern = pattern.slice(2);
|
|
1340
|
+
if (pattern === "") return () => false;
|
|
1341
|
+
if (!GLOB_META.test(pattern)) return (full, rel) => full.includes(pattern) || rel.includes(pattern);
|
|
1342
|
+
const regex = globToRegExp(pattern);
|
|
1343
|
+
return (_full, rel) => {
|
|
1344
|
+
const normalized = rel.split("\\").join("/");
|
|
1345
|
+
if (regex.test(normalized)) return true;
|
|
1346
|
+
for (let slash = normalized.indexOf("/"); slash !== -1; slash = normalized.indexOf("/", slash + 1)) if (regex.test(normalized.slice(0, slash))) return true;
|
|
1347
|
+
return false;
|
|
1649
1348
|
};
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1349
|
+
}
|
|
1350
|
+
/** Minimal glob → RegExp: `**` spans segments, `*`/`?` stay within one. */
|
|
1351
|
+
function globToRegExp(glob) {
|
|
1352
|
+
let out = "";
|
|
1353
|
+
let i = 0;
|
|
1354
|
+
const normalized = glob.split("\\").join("/");
|
|
1355
|
+
while (i < normalized.length) {
|
|
1356
|
+
const ch = normalized[i];
|
|
1357
|
+
if (ch === "*") {
|
|
1358
|
+
if (normalized[i + 1] === "*") {
|
|
1359
|
+
if (normalized[i + 2] === "/") {
|
|
1360
|
+
out += "(?:[^/]+/)*";
|
|
1361
|
+
i += 3;
|
|
1362
|
+
} else {
|
|
1363
|
+
out += ".*";
|
|
1364
|
+
i += 2;
|
|
1365
|
+
}
|
|
1366
|
+
} else {
|
|
1367
|
+
out += "[^/]*";
|
|
1368
|
+
i += 1;
|
|
1369
|
+
}
|
|
1370
|
+
} else if (ch === "?") {
|
|
1371
|
+
out += "[^/]";
|
|
1372
|
+
i += 1;
|
|
1373
|
+
} else {
|
|
1374
|
+
out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
1375
|
+
i += 1;
|
|
1662
1376
|
}
|
|
1663
|
-
builder.append(" />;\n");
|
|
1664
1377
|
}
|
|
1665
|
-
return {
|
|
1666
|
-
diagnostics: [],
|
|
1667
|
-
entry: {
|
|
1668
|
-
scope,
|
|
1669
|
-
path: `${originalPath}.${scope}.tsx`,
|
|
1670
|
-
source: builder.toString(),
|
|
1671
|
-
mappings: builder.mappings
|
|
1672
|
-
}
|
|
1673
|
-
};
|
|
1378
|
+
return new RegExp(`^${out}$`);
|
|
1674
1379
|
}
|
|
1675
1380
|
/**
|
|
1676
|
-
*
|
|
1677
|
-
*
|
|
1678
|
-
* This adapter parses source, never compiler-generated runtime JavaScript.
|
|
1679
|
-
* Module and instance scripts project separately (module bindings are in
|
|
1680
|
-
* scope for the instance script, per Svelte semantics) and contribute only
|
|
1681
|
-
* witnessed named imports plus synthetic JSX attributes for direct resolver
|
|
1682
|
-
* `.attrs()` and callable-string calls. Caller-owned metadata attributes
|
|
1683
|
-
* candidate imports; unsupported call shapes — including resolver calls
|
|
1684
|
-
* written in the template fragment — fail the whole source closed.
|
|
1381
|
+
* A user list replaces only the replaceable defaults. Driver-owned
|
|
1382
|
+
* invariants belong in `extraStructural`; a user list would replace those.
|
|
1685
1383
|
*/
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
1384
|
+
function createExcludeMatcher(userPatterns, extraStructural = []) {
|
|
1385
|
+
const merged = [...STRUCTURAL_EXCLUDE];
|
|
1386
|
+
for (const pattern of extraStructural) if (!merged.includes(pattern)) merged.push(pattern);
|
|
1387
|
+
for (const pattern of userPatterns ?? REPLACEABLE_DEFAULT_EXCLUDE) if (!merged.includes(pattern)) merged.push(pattern);
|
|
1388
|
+
const compiled = merged.map((pattern) => [pattern, compilePattern(pattern)]);
|
|
1389
|
+
const hits = new Map(merged.map((p) => [p, 0]));
|
|
1390
|
+
const explain = (full, rel) => {
|
|
1391
|
+
for (const [pattern, test] of compiled) if (test(full, rel)) {
|
|
1392
|
+
hits.set(pattern, (hits.get(pattern) ?? 0) + 1);
|
|
1393
|
+
return pattern;
|
|
1394
|
+
}
|
|
1395
|
+
return null;
|
|
1690
1396
|
};
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1397
|
+
return {
|
|
1398
|
+
patterns: merged,
|
|
1399
|
+
matches: (full, rel) => explain(full, rel) !== null,
|
|
1400
|
+
explain,
|
|
1401
|
+
stats: () => hits
|
|
1696
1402
|
};
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
});
|
|
1703
|
-
} catch (error) {
|
|
1704
|
-
const range = compilerParseErrorRange(error, source);
|
|
1705
|
-
return {
|
|
1706
|
-
kind: "error",
|
|
1707
|
-
original,
|
|
1708
|
-
diagnostics: [diagnostic("SVELTE_PARSE_ERROR", error instanceof Error ? error.message : String(error), originalPath, source, range?.node, range?.location)]
|
|
1709
|
-
};
|
|
1710
|
-
}
|
|
1711
|
-
const projections = [];
|
|
1712
|
-
const moduleBindings = ast.module ? importBindings(ast.module.content) : void 0;
|
|
1713
|
-
const instanceBindings = ast.instance ? importBindings(ast.instance.content) : void 0;
|
|
1714
|
-
const moduleScope = ast.module && moduleBindings ? {
|
|
1715
|
-
bindings: moduleBindings,
|
|
1716
|
-
importDeclarations: ast.module.content.body.filter((declaration) => declaration.type === "ImportDeclaration")
|
|
1717
|
-
} : void 0;
|
|
1718
|
-
if (ast.module) projections.push(projectScope(ast.module, "module", source, originalPath, options, void 0, moduleBindings));
|
|
1719
|
-
if (ast.instance) projections.push(projectScope(ast.instance, "instance", source, originalPath, options, moduleScope, instanceBindings));
|
|
1720
|
-
const diagnostics = projections.flatMap((projection) => projection.diagnostics);
|
|
1721
|
-
if (ast.fragment) {
|
|
1722
|
-
const scriptBindings = instanceBindings ?? /* @__PURE__ */ new Map();
|
|
1723
|
-
const fragmentBindings = moduleScope ? new Map([...moduleScope.bindings, ...scriptBindings]) : scriptBindings;
|
|
1724
|
-
if (fragmentBindings.size > 0) walkFragment(ast.fragment, (node) => {
|
|
1725
|
-
const site = resolverCallSite(node, fragmentBindings);
|
|
1726
|
-
if (!site) return;
|
|
1727
|
-
if (options.attributeResolver(site.access.request) === "other") return;
|
|
1728
|
-
diagnostics.push(diagnostic("SVELTE_ATTRS_TEMPLATE_UNSUPPORTED", `Resolver '${site.access.displayName}' is called inside the template; assign the result to a const in the instance script and spread that instead.`, originalPath, source, node));
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
if (diagnostics.length > 0) return {
|
|
1732
|
-
kind: "error",
|
|
1733
|
-
original,
|
|
1734
|
-
diagnostics
|
|
1403
|
+
}
|
|
1404
|
+
function resolveMode(explicit, driverDefault) {
|
|
1405
|
+
if (explicit === "development" || explicit === "production") return {
|
|
1406
|
+
mode: explicit,
|
|
1407
|
+
provenance: "explicit"
|
|
1735
1408
|
};
|
|
1736
1409
|
return {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
entries: projections.flatMap((projection) => projection.entry ? [projection.entry] : [])
|
|
1410
|
+
mode: driverDefault(),
|
|
1411
|
+
provenance: "driver-default"
|
|
1740
1412
|
};
|
|
1741
1413
|
}
|
|
1742
1414
|
//#endregion
|
|
1743
|
-
//#region pipeline/
|
|
1415
|
+
//#region pipeline/discover-files.ts
|
|
1416
|
+
function pathCharRank(code) {
|
|
1417
|
+
return code === 47 || code === 92 ? -1 : code;
|
|
1418
|
+
}
|
|
1419
|
+
function compareDiscoveryOrder(a, b) {
|
|
1420
|
+
const shared = Math.min(a.length, b.length);
|
|
1421
|
+
for (let i = 0; i < shared; i++) {
|
|
1422
|
+
const left = pathCharRank(a.charCodeAt(i));
|
|
1423
|
+
const right = pathCharRank(b.charCodeAt(i));
|
|
1424
|
+
if (left !== right) return left - right;
|
|
1425
|
+
}
|
|
1426
|
+
return a.length - b.length;
|
|
1427
|
+
}
|
|
1428
|
+
function discoverFiles(dir, rootDir, exclude, extensionsSet) {
|
|
1429
|
+
return walkFiles(dir, rootDir, exclude, extensionsSet).sort(compareDiscoveryOrder);
|
|
1430
|
+
}
|
|
1431
|
+
function walkFiles(dir, rootDir, exclude, extensionsSet) {
|
|
1432
|
+
const results = [];
|
|
1433
|
+
let entries;
|
|
1434
|
+
try {
|
|
1435
|
+
entries = (0, fs.readdirSync)(dir, { encoding: "utf8" });
|
|
1436
|
+
} catch {
|
|
1437
|
+
return results;
|
|
1438
|
+
}
|
|
1439
|
+
const isExcluded = exclude ? (full, rel) => exclude.matches(full, rel) : () => false;
|
|
1440
|
+
for (const entry of entries) {
|
|
1441
|
+
const fullPath = (0, path.join)(dir, entry);
|
|
1442
|
+
if (isExcluded(fullPath, (0, path.relative)(rootDir, fullPath))) continue;
|
|
1443
|
+
let stat;
|
|
1444
|
+
try {
|
|
1445
|
+
stat = (0, fs.statSync)(fullPath);
|
|
1446
|
+
} catch {
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (stat.isDirectory()) results.push(...walkFiles(fullPath, rootDir, exclude, extensionsSet));
|
|
1450
|
+
else if (extensionsSet.has((0, path.extname)(entry))) results.push(fullPath);
|
|
1451
|
+
}
|
|
1452
|
+
return results;
|
|
1453
|
+
}
|
|
1454
|
+
//#endregion
|
|
1455
|
+
//#region pipeline/source-identity.ts
|
|
1744
1456
|
/**
|
|
1745
|
-
*
|
|
1746
|
-
*
|
|
1747
|
-
* MDX files are first-class consumers of ds-built components but are not
|
|
1748
|
-
* directly parseable by OXC (which the scanner uses for .tsx/.jsx). This
|
|
1749
|
-
* module compiles MDX sources to scanner-consumable JSX using @mdx-js/mdx.
|
|
1750
|
-
*
|
|
1751
|
-
* @mdx-js/mdx is declared as `peerDependenciesMeta.optional` on the
|
|
1752
|
-
* plugin packages that import this module. The dynamic import with
|
|
1753
|
-
* .catch() ensures non-MDX consumers (who configure `extensions` to
|
|
1754
|
-
* exclude .mdx) never trigger the resolution — zero install-footprint
|
|
1755
|
-
* cost for them.
|
|
1756
|
-
*
|
|
1757
|
-
* The `DEFAULT_EXTENSIONS` constant is the shared source of truth for
|
|
1758
|
-
* both `@animus-ui/vite-plugin` and `@animus-ui/next-plugin`. Each plugin
|
|
1759
|
-
* imports it directly; independent redeclaration of default extensions
|
|
1760
|
-
* is considered a regression.
|
|
1457
|
+
* One derivation of source identity for discovery, watch ingestion, ownership
|
|
1458
|
+
* and deletion: event paths are lookup INPUTS only and never fork identity.
|
|
1761
1459
|
*/
|
|
1762
|
-
const DEFAULT_EXTENSIONS = [
|
|
1763
|
-
".ts",
|
|
1764
|
-
".tsx",
|
|
1765
|
-
".js",
|
|
1766
|
-
".jsx",
|
|
1767
|
-
".mdx"
|
|
1768
|
-
];
|
|
1769
1460
|
/**
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
* kits reach the engine, `.mdx` is not — MDX is preprocessed to `.tsx`
|
|
1773
|
-
* before the engine sees it). The single source for the Turbopack rule
|
|
1774
|
-
* glob and the unplugin transform gate; independent redeclaration of this
|
|
1775
|
-
* set is considered a regression — a missed copy silently skips a whole
|
|
1776
|
-
* file class on one bundler family.
|
|
1461
|
+
* True when `target` is `root` or a descendant, decided by `relative()`
|
|
1462
|
+
* shape: string prefixing would let `/ui` claim `/ui-old`.
|
|
1777
1463
|
*/
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
"
|
|
1781
|
-
|
|
1782
|
-
"jsx",
|
|
1783
|
-
"mjs"
|
|
1784
|
-
];
|
|
1464
|
+
function isPathWithinRoot(root, target, pathApi = node_path.default) {
|
|
1465
|
+
const rel = pathApi.relative(root, target);
|
|
1466
|
+
return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
|
|
1467
|
+
}
|
|
1785
1468
|
/**
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
* - Returns `{ kind: 'ok', source }` with JSX-compiled output on success.
|
|
1789
|
-
* - Returns `{ kind: 'missing-dep' }` if @mdx-js/mdx is not resolvable
|
|
1790
|
-
* (consumer needs to install it).
|
|
1791
|
-
* - Returns `{ kind: 'error', error }` on compile failure (e.g. malformed
|
|
1792
|
-
* MDX syntax). Plugins SHALL warn + skip affected files; the build
|
|
1793
|
-
* continues with remaining files.
|
|
1469
|
+
* True when two paths share a platform root (win32 drive letter or UNC
|
|
1470
|
+
* share; always true on posix). Drive letters compare case-insensitively.
|
|
1794
1471
|
*/
|
|
1795
|
-
|
|
1796
|
-
const
|
|
1797
|
-
|
|
1472
|
+
function sharesVolumeRoot(a, b, pathApi = node_path.default) {
|
|
1473
|
+
const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
|
|
1474
|
+
return volumeOf(a) === volumeOf(b);
|
|
1475
|
+
}
|
|
1476
|
+
function canonicalize(p) {
|
|
1798
1477
|
try {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
jsx: true
|
|
1803
|
-
});
|
|
1804
|
-
return {
|
|
1805
|
-
kind: "ok",
|
|
1806
|
-
source: `/* @mdx-source: ${filename} */\n${String(vfile)}`
|
|
1807
|
-
};
|
|
1808
|
-
} catch (err) {
|
|
1809
|
-
return {
|
|
1810
|
-
kind: "error",
|
|
1811
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1812
|
-
};
|
|
1478
|
+
return (0, node_fs.realpathSync)(p);
|
|
1479
|
+
} catch {
|
|
1480
|
+
return null;
|
|
1813
1481
|
}
|
|
1814
1482
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
return
|
|
1483
|
+
function createSourceIdentity(rootDir) {
|
|
1484
|
+
const normalizedRootDir = node_path.default.normalize(rootDir);
|
|
1485
|
+
const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
|
|
1486
|
+
const roots = [];
|
|
1487
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1488
|
+
const owningRecord = (canonicalPath) => {
|
|
1489
|
+
let owner = null;
|
|
1490
|
+
for (const record of roots) {
|
|
1491
|
+
if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
|
|
1492
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1493
|
+
}
|
|
1494
|
+
return owner;
|
|
1495
|
+
};
|
|
1496
|
+
return {
|
|
1497
|
+
rootDir: normalizedRootDir,
|
|
1498
|
+
canonicalRootDir,
|
|
1499
|
+
registerExternalRoot(root) {
|
|
1500
|
+
const asRegistered = node_path.default.normalize(root);
|
|
1501
|
+
const canonical = canonicalize(asRegistered) ?? asRegistered;
|
|
1502
|
+
const existing = roots.find((record) => record.canonical === canonical);
|
|
1503
|
+
if (existing) return existing.canonical;
|
|
1504
|
+
roots.push({
|
|
1505
|
+
asRegistered,
|
|
1506
|
+
canonical
|
|
1507
|
+
});
|
|
1508
|
+
return canonical;
|
|
1509
|
+
},
|
|
1510
|
+
externalRoots() {
|
|
1511
|
+
return roots.map((record) => record.canonical);
|
|
1512
|
+
},
|
|
1513
|
+
resolveSourceId(inputPath) {
|
|
1514
|
+
const lexical = node_path.default.normalize(inputPath);
|
|
1515
|
+
const canonical = canonicalize(lexical);
|
|
1516
|
+
if (canonical === null) return null;
|
|
1517
|
+
const owner = owningRecord(canonical);
|
|
1518
|
+
const ownedBy = (record) => {
|
|
1519
|
+
const pathInRoot = node_path.default.relative(record.canonical, canonical);
|
|
1520
|
+
return {
|
|
1521
|
+
sourceKey: node_path.default.relative(normalizedRootDir, node_path.default.join(record.asRegistered, pathInRoot)),
|
|
1522
|
+
owningRoot: record.canonical,
|
|
1523
|
+
pathInRoot
|
|
1524
|
+
};
|
|
1525
|
+
};
|
|
1526
|
+
const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
|
|
1527
|
+
let resolved;
|
|
1528
|
+
if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
|
|
1529
|
+
const key = node_path.default.relative(canonicalRootDir, canonical);
|
|
1530
|
+
resolved = {
|
|
1531
|
+
sourceKey: key,
|
|
1532
|
+
owningRoot: null,
|
|
1533
|
+
pathInRoot: key
|
|
1534
|
+
};
|
|
1535
|
+
} else if (owner) resolved = ownedBy(owner);
|
|
1536
|
+
else return null;
|
|
1537
|
+
aliases.set(lexical, resolved);
|
|
1538
|
+
aliases.set(canonical, resolved);
|
|
1539
|
+
return resolved;
|
|
1540
|
+
},
|
|
1541
|
+
resolveDeletedSourceId(inputPath) {
|
|
1542
|
+
return aliases.get(node_path.default.normalize(inputPath)) ?? null;
|
|
1543
|
+
},
|
|
1544
|
+
containingExternalRoot(inputPath) {
|
|
1545
|
+
const lexical = node_path.default.normalize(inputPath);
|
|
1546
|
+
let owner = null;
|
|
1547
|
+
for (const record of roots) {
|
|
1548
|
+
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
1549
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1550
|
+
}
|
|
1551
|
+
if (owner) return owner.canonical;
|
|
1552
|
+
const canonical = canonicalize(lexical);
|
|
1553
|
+
if (canonical === null) return null;
|
|
1554
|
+
for (const record of roots) {
|
|
1555
|
+
if (!isPathWithinRoot(record.canonical, canonical)) continue;
|
|
1556
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1557
|
+
}
|
|
1558
|
+
return owner?.canonical ?? null;
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1829
1561
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
".
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
"
|
|
1839
|
-
"
|
|
1562
|
+
//#endregion
|
|
1563
|
+
//#region pipeline/discover-packages.ts
|
|
1564
|
+
function findPackageRoot(absEntryPath) {
|
|
1565
|
+
let pkgRoot = (0, path.dirname)(absEntryPath);
|
|
1566
|
+
while (pkgRoot !== (0, path.dirname)(pkgRoot) && !(0, fs.existsSync)((0, path.join)(pkgRoot, "package.json"))) pkgRoot = (0, path.dirname)(pkgRoot);
|
|
1567
|
+
return pkgRoot;
|
|
1568
|
+
}
|
|
1569
|
+
const PACKAGE_SRC_EXCLUDES = [
|
|
1570
|
+
"dist",
|
|
1571
|
+
"node_modules",
|
|
1572
|
+
".test.",
|
|
1573
|
+
".spec."
|
|
1840
1574
|
];
|
|
1841
|
-
function
|
|
1842
|
-
|
|
1843
|
-
const dot = basename.lastIndexOf(".");
|
|
1844
|
-
return dot === -1 ? "" : basename.slice(dot).toLowerCase();
|
|
1575
|
+
function isExcludedPackageRelativePath(rel) {
|
|
1576
|
+
return PACKAGE_SRC_EXCLUDES.some((pattern) => rel.includes(pattern));
|
|
1845
1577
|
}
|
|
1846
|
-
function
|
|
1847
|
-
return
|
|
1578
|
+
function walkPackageSources(packageDir, extensionsSet) {
|
|
1579
|
+
return discoverFiles(packageDir, packageDir, void 0, extensionsSet).filter((absPath) => !isExcludedPackageRelativePath((0, path.relative)(packageDir, absPath)));
|
|
1848
1580
|
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
".
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1581
|
+
const PACKAGE_OUTPUT_EXCLUDES = [
|
|
1582
|
+
"node_modules",
|
|
1583
|
+
".test.",
|
|
1584
|
+
".spec.",
|
|
1585
|
+
".d.ts",
|
|
1586
|
+
".map"
|
|
1587
|
+
];
|
|
1588
|
+
const isFile = (path$4) => {
|
|
1589
|
+
try {
|
|
1590
|
+
return (0, fs.statSync)(path$4).isFile();
|
|
1591
|
+
} catch {
|
|
1592
|
+
return false;
|
|
1593
|
+
}
|
|
1861
1594
|
};
|
|
1862
|
-
function
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1595
|
+
function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
|
|
1596
|
+
return [
|
|
1597
|
+
absSpecifier,
|
|
1598
|
+
...Array.from(extensionsSet, (ext) => absSpecifier + ext),
|
|
1599
|
+
...Array.from(extensionsSet, (ext) => (0, path.join)(absSpecifier, `index${ext}`))
|
|
1600
|
+
].find(isFile) ?? null;
|
|
1601
|
+
}
|
|
1602
|
+
function bareSpecifierPackageName(specifier) {
|
|
1603
|
+
const segments = specifier.split("/");
|
|
1604
|
+
return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
|
|
1605
|
+
}
|
|
1606
|
+
function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
1607
|
+
if ((0, path.isAbsolute)(specifier)) {
|
|
1608
|
+
const resolved = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
1609
|
+
if (!resolved) return null;
|
|
1610
|
+
return isPathWithinRoot(srcDir, resolved) ? resolved : null;
|
|
1869
1611
|
}
|
|
1870
|
-
const
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1612
|
+
const packageName = bareSpecifierPackageName(specifier);
|
|
1613
|
+
const subpath = specifier.slice(packageName.length + 1);
|
|
1614
|
+
return resolveAbsolutePathSpecifier((0, path.join)(srcDir, subpath || "index"), extensionsSet);
|
|
1615
|
+
}
|
|
1616
|
+
function distEntryIsStale(absEntry, srcDir, srcFiles) {
|
|
1617
|
+
if (isPathWithinRoot(srcDir, absEntry)) return false;
|
|
1618
|
+
let distMtime;
|
|
1619
|
+
try {
|
|
1620
|
+
distMtime = (0, fs.statSync)(absEntry).mtimeMs;
|
|
1621
|
+
} catch {
|
|
1622
|
+
return false;
|
|
1875
1623
|
}
|
|
1876
|
-
|
|
1624
|
+
let newestSrcMtime = -Infinity;
|
|
1625
|
+
for (const srcFile of srcFiles) try {
|
|
1626
|
+
newestSrcMtime = Math.max(newestSrcMtime, (0, fs.statSync)(srcFile).mtimeMs);
|
|
1627
|
+
} catch {}
|
|
1628
|
+
return distMtime < newestSrcMtime;
|
|
1877
1629
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1630
|
+
async function collectExternalPackageSources(opts) {
|
|
1631
|
+
const { specifiers, resolveSpecifier, rootDir, extensionsSet, hasEntry, onSourceRead, onUnreadable, onPackageResolved } = opts;
|
|
1632
|
+
const entries = [];
|
|
1633
|
+
const pushed = /* @__PURE__ */ new Set();
|
|
1634
|
+
const packageMap = {};
|
|
1635
|
+
const sourceEntries = /* @__PURE__ */ new Map();
|
|
1636
|
+
const packageDirs = [];
|
|
1637
|
+
const dirOwnerSets = {};
|
|
1638
|
+
const dirExtensions = {};
|
|
1639
|
+
const fileOwners = {};
|
|
1640
|
+
const outcomes = [];
|
|
1641
|
+
const claimDir = (dir, specifier) => {
|
|
1642
|
+
(dirOwnerSets[dir] ??= []).push(specifier);
|
|
1643
|
+
};
|
|
1644
|
+
const alreadyIngested = (relPath) => hasEntry(relPath) || pushed.has(relPath);
|
|
1645
|
+
for (const specifier of specifiers) {
|
|
1646
|
+
let absEntry;
|
|
1647
|
+
try {
|
|
1648
|
+
absEntry = await resolveSpecifier(specifier);
|
|
1649
|
+
} catch {
|
|
1650
|
+
absEntry = null;
|
|
1895
1651
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
paths.push(path);
|
|
1905
|
-
factPathsByCanonical.set(canonicalPath, paths);
|
|
1652
|
+
if (!absEntry && (0, path.isAbsolute)(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
1653
|
+
if (!absEntry) {
|
|
1654
|
+
outcomes.push({
|
|
1655
|
+
specifier,
|
|
1656
|
+
outcome: "unresolvable",
|
|
1657
|
+
fileCount: 0
|
|
1658
|
+
});
|
|
1659
|
+
continue;
|
|
1906
1660
|
}
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1661
|
+
const pkgRoot = findPackageRoot(absEntry);
|
|
1662
|
+
const srcDir = (0, path.join)(pkgRoot, "src");
|
|
1663
|
+
let fileCount = 0;
|
|
1664
|
+
let staleDist = false;
|
|
1665
|
+
if ((0, fs.existsSync)(srcDir)) {
|
|
1666
|
+
packageDirs.push(srcDir);
|
|
1667
|
+
claimDir(srcDir, specifier);
|
|
1668
|
+
dirExtensions[srcDir] = [...extensionsSet];
|
|
1669
|
+
onPackageResolved?.(specifier, srcDir);
|
|
1670
|
+
const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet);
|
|
1671
|
+
if (srcEntry) {
|
|
1672
|
+
packageMap[specifier] = (0, path.relative)(rootDir, srcEntry);
|
|
1673
|
+
sourceEntries.set(specifier, srcEntry);
|
|
1674
|
+
} else packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
1675
|
+
if (!(0, path.isAbsolute)(specifier)) {
|
|
1676
|
+
const packageName = bareSpecifierPackageName(specifier);
|
|
1677
|
+
if (packageName !== specifier && !(packageName in packageMap)) {
|
|
1678
|
+
const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
|
|
1679
|
+
if (rootEntry) {
|
|
1680
|
+
packageMap[packageName] = (0, path.relative)(rootDir, rootEntry);
|
|
1681
|
+
sourceEntries.set(packageName, rootEntry);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
const pkgFiles = walkPackageSources(srcDir, extensionsSet);
|
|
1686
|
+
staleDist = distEntryIsStale(absEntry, srcDir, pkgFiles);
|
|
1687
|
+
for (const pkgFile of pkgFiles) {
|
|
1688
|
+
const relPath = (0, path.relative)(rootDir, pkgFile);
|
|
1689
|
+
if (alreadyIngested(relPath)) {
|
|
1690
|
+
fileCount++;
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
let source;
|
|
1694
|
+
try {
|
|
1695
|
+
source = (0, fs.readFileSync)(pkgFile, "utf-8");
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
onUnreadable(relPath, err);
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1700
|
+
onSourceRead?.(source, relPath, pkgFile);
|
|
1701
|
+
entries.push({
|
|
1702
|
+
path: relPath,
|
|
1703
|
+
source
|
|
1704
|
+
});
|
|
1705
|
+
pushed.add(relPath);
|
|
1706
|
+
fileOwners[relPath] ??= specifier;
|
|
1707
|
+
fileCount++;
|
|
1708
|
+
}
|
|
1709
|
+
} else {
|
|
1710
|
+
const outputDir = (0, path.dirname)(absEntry);
|
|
1711
|
+
packageDirs.push(outputDir);
|
|
1712
|
+
claimDir(outputDir, specifier);
|
|
1713
|
+
onPackageResolved?.(specifier, outputDir);
|
|
1714
|
+
packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
1715
|
+
const outputExtensions = new Set(extensionsSet);
|
|
1716
|
+
outputExtensions.add((0, path.extname)(absEntry));
|
|
1717
|
+
dirExtensions[outputDir] = [...outputExtensions];
|
|
1718
|
+
const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
|
|
1719
|
+
const relToOutput = (0, path.relative)(outputDir, file);
|
|
1720
|
+
return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
|
|
1721
|
+
});
|
|
1722
|
+
if (!outputFiles.includes(absEntry)) outputFiles.unshift(absEntry);
|
|
1723
|
+
for (const outputFile of outputFiles) {
|
|
1724
|
+
const outputRelPath = (0, path.relative)(rootDir, outputFile);
|
|
1725
|
+
if (alreadyIngested(outputRelPath)) {
|
|
1726
|
+
fileCount++;
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
let source;
|
|
1730
|
+
try {
|
|
1731
|
+
source = (0, fs.readFileSync)(outputFile, "utf-8");
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
onUnreadable(outputRelPath, err);
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
onSourceRead?.(source, outputRelPath, outputFile);
|
|
1737
|
+
entries.push({
|
|
1738
|
+
path: outputRelPath,
|
|
1739
|
+
source
|
|
1740
|
+
});
|
|
1741
|
+
pushed.add(outputRelPath);
|
|
1742
|
+
fileOwners[outputRelPath] ??= specifier;
|
|
1743
|
+
fileCount++;
|
|
1744
|
+
}
|
|
1911
1745
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1746
|
+
outcomes.push({
|
|
1747
|
+
specifier,
|
|
1748
|
+
outcome: staleDist ? "stale-dist" : fileCount > 0 ? "resolved" : "empty",
|
|
1749
|
+
fileCount
|
|
1750
|
+
});
|
|
1914
1751
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1752
|
+
return {
|
|
1753
|
+
entries,
|
|
1754
|
+
packageMap,
|
|
1755
|
+
sourceEntries,
|
|
1756
|
+
packageDirs,
|
|
1757
|
+
dirOwnerSets,
|
|
1758
|
+
dirExtensions,
|
|
1759
|
+
fileOwners,
|
|
1760
|
+
outcomes
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
function firstOwners(dirOwnerSets) {
|
|
1764
|
+
const owners = {};
|
|
1765
|
+
for (const [dir, specifiers] of Object.entries(dirOwnerSets)) if (specifiers.length > 0) owners[dir] = specifiers[0];
|
|
1766
|
+
return owners;
|
|
1767
|
+
}
|
|
1768
|
+
function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
|
|
1769
|
+
if (rejectedSpecifiers.size === 0) return collected;
|
|
1770
|
+
const rejectedDirs = Object.entries(collected.dirOwnerSets).filter(([, specs]) => specs.every((s) => rejectedSpecifiers.has(s))).map(([dir]) => dir);
|
|
1771
|
+
const underRejectedDir = (absPath) => rejectedDirs.some((dir) => isPathWithinRoot(dir, absPath));
|
|
1772
|
+
const targetRejected = (specifier, absTarget) => rejectedSpecifiers.has(specifier) || underRejectedDir(absTarget);
|
|
1773
|
+
const packageMap = {};
|
|
1774
|
+
for (const [specifier, relTarget] of Object.entries(collected.packageMap)) {
|
|
1775
|
+
if (targetRejected(specifier, (0, path.resolve)(rootDir, relTarget))) continue;
|
|
1776
|
+
packageMap[specifier] = relTarget;
|
|
1922
1777
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
if (
|
|
1926
|
-
|
|
1927
|
-
return request.access.kind === "direct" && request.access.importKind === "named" ? "resolver" : "unsupported-resolver-form";
|
|
1778
|
+
const sourceEntries = /* @__PURE__ */ new Map();
|
|
1779
|
+
for (const [specifier, absEntry] of collected.sourceEntries) {
|
|
1780
|
+
if (targetRejected(specifier, absEntry)) continue;
|
|
1781
|
+
sourceEntries.set(specifier, absEntry);
|
|
1928
1782
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
1934
|
-
this.resolveMemo.set(key, resolved);
|
|
1935
|
-
}
|
|
1936
|
-
return resolved;
|
|
1783
|
+
const dirOwnerSets = {};
|
|
1784
|
+
for (const [dir, specs] of Object.entries(collected.dirOwnerSets)) {
|
|
1785
|
+
const kept = specs.filter((s) => !rejectedSpecifiers.has(s));
|
|
1786
|
+
if (kept.length === 0) continue;
|
|
1787
|
+
dirOwnerSets[dir] = kept;
|
|
1937
1788
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
if (
|
|
1941
|
-
|
|
1942
|
-
const file = this.facts.files[path];
|
|
1943
|
-
const exported = file?.exports.find((candidate) => candidate.exported === exportedName);
|
|
1944
|
-
if (!file || !exported) return null;
|
|
1945
|
-
if (exported.source === null) {
|
|
1946
|
-
if (exported.local === null) return null;
|
|
1947
|
-
if (this.resolverBindings.get(path)?.has(exported.local)) return exported.local;
|
|
1948
|
-
const imported = file.imports.find((candidate) => candidate.local === exported.local);
|
|
1949
|
-
if (!imported) return null;
|
|
1950
|
-
const importedFile = this.resolveSource(path, imported.source);
|
|
1951
|
-
return importedFile ? this.resolveClassExport(importedFile, imported.imported, seen) : null;
|
|
1952
|
-
}
|
|
1953
|
-
if (exported.original === null) return null;
|
|
1954
|
-
const nextFile = this.resolveSource(path, exported.source);
|
|
1955
|
-
return nextFile ? this.resolveClassExport(nextFile, exported.original, seen) : null;
|
|
1789
|
+
const dirExtensions = {};
|
|
1790
|
+
for (const [dir, exts] of Object.entries(collected.dirExtensions)) {
|
|
1791
|
+
if (rejectedDirs.includes(dir)) continue;
|
|
1792
|
+
dirExtensions[dir] = exts;
|
|
1956
1793
|
}
|
|
1957
|
-
};
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
...entry,
|
|
1972
|
-
hash: entry.hash ?? contentHash(entry.source)
|
|
1973
|
-
}));
|
|
1974
|
-
const rawOriginalPaths = new Set(originalEntries.map((entry) => entry.path));
|
|
1975
|
-
const analysisEntries = [];
|
|
1976
|
-
const ownership = {};
|
|
1977
|
-
const analysisOwner = /* @__PURE__ */ new Map();
|
|
1978
|
-
const diagnostics = [];
|
|
1979
|
-
const svelteEntries = [];
|
|
1980
|
-
const addAnalysisEntry = (originalPath, path, source, hash) => {
|
|
1981
|
-
analysisEntries.push({
|
|
1982
|
-
path,
|
|
1983
|
-
source,
|
|
1984
|
-
hash: hash ?? contentHash(source)
|
|
1985
|
-
});
|
|
1986
|
-
ownership[originalPath].analysisPaths.push(path);
|
|
1987
|
-
analysisOwner.set(path, originalPath);
|
|
1794
|
+
const fileOwners = {};
|
|
1795
|
+
for (const [relPath, owner] of Object.entries(collected.fileOwners)) {
|
|
1796
|
+
if (rejectedSpecifiers.has(owner)) continue;
|
|
1797
|
+
fileOwners[relPath] = owner;
|
|
1798
|
+
}
|
|
1799
|
+
return {
|
|
1800
|
+
entries: collected.entries.filter((entry) => !rejectedSpecifiers.has(collected.fileOwners[entry.path])),
|
|
1801
|
+
packageMap,
|
|
1802
|
+
sourceEntries,
|
|
1803
|
+
packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
1804
|
+
dirOwnerSets,
|
|
1805
|
+
dirExtensions,
|
|
1806
|
+
fileOwners,
|
|
1807
|
+
outcomes: collected.outcomes
|
|
1988
1808
|
};
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
1809
|
+
}
|
|
1810
|
+
function unresolvableIncludesMessage(outcomes) {
|
|
1811
|
+
const unresolvable = outcomes.filter((record) => record.outcome === "unresolvable").map((record) => record.specifier);
|
|
1812
|
+
if (unresolvable.length === 0) return null;
|
|
1813
|
+
return `[animus-extract] unresolvable include specifier(s): ${unresolvable.join(", ")}`;
|
|
1814
|
+
}
|
|
1815
|
+
/** A stale dist silently skews merged registry content while discovery
|
|
1816
|
+
* compiles the fresh sources; null when no specifier is stale. */
|
|
1817
|
+
function staleDistIncludesMessage(outcomes) {
|
|
1818
|
+
const stale = outcomes.filter((record) => record.outcome === "stale-dist").map((record) => record.specifier);
|
|
1819
|
+
if (stale.length === 0) return null;
|
|
1820
|
+
return `[animus-extract] stale dist for include specifier(s): ${stale.join(", ")} — dist entry is older than the newest src/ file; rebuild the package(s) before extracting`;
|
|
1821
|
+
}
|
|
1822
|
+
function extractSystemFilePackages(systemFilePath) {
|
|
1823
|
+
let source;
|
|
1824
|
+
try {
|
|
1825
|
+
source = (0, fs.readFileSync)(systemFilePath, "utf-8");
|
|
1826
|
+
} catch {
|
|
1827
|
+
return [];
|
|
1828
|
+
}
|
|
1829
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
1830
|
+
const constructorRegex = /createSystem\s*\(\s*\{[^}]*?\bincludes\s*:\s*\[([^\]]*)\]/gs;
|
|
1831
|
+
const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
|
|
1832
|
+
const collectIdentifiers = (regex) => {
|
|
1833
|
+
let match;
|
|
1834
|
+
while ((match = regex.exec(source)) !== null) {
|
|
1835
|
+
const inner = match[1];
|
|
1836
|
+
for (const token of inner.split(",")) {
|
|
1837
|
+
const id = token.trim();
|
|
1838
|
+
if (id && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(id)) identifiers.add(id);
|
|
2002
1839
|
}
|
|
2003
|
-
pendingPaths.add(entry.path);
|
|
2004
1840
|
}
|
|
2005
|
-
for (const entry of entries) addAnalysisEntry(originalPath, entry.path, entry.source);
|
|
2006
1841
|
};
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
code: "SOURCE_MDX_DEPENDENCY_MISSING",
|
|
2026
|
-
message: "Install optional peer dependency '@mdx-js/mdx' to analyze opted-in MDX source.",
|
|
2027
|
-
originalPath: original.path
|
|
2028
|
-
});
|
|
2029
|
-
continue;
|
|
1842
|
+
collectIdentifiers(constructorRegex);
|
|
1843
|
+
collectIdentifiers(chainRegex);
|
|
1844
|
+
const IDENT_START_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*/;
|
|
1845
|
+
const skipTrivia = (from) => {
|
|
1846
|
+
let pos = from;
|
|
1847
|
+
for (;;) {
|
|
1848
|
+
while (pos < source.length && /\s/.test(source[pos])) pos++;
|
|
1849
|
+
if (source.startsWith("//", pos)) {
|
|
1850
|
+
const newline = source.indexOf("\n", pos);
|
|
1851
|
+
pos = newline === -1 ? source.length : newline + 1;
|
|
1852
|
+
continue;
|
|
1853
|
+
}
|
|
1854
|
+
if (source.startsWith("/*", pos)) {
|
|
1855
|
+
const close = source.indexOf("*/", pos + 2);
|
|
1856
|
+
pos = close === -1 ? source.length : close + 2;
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
return pos;
|
|
2030
1860
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
1861
|
+
};
|
|
1862
|
+
const consumeChainLinks = (from) => {
|
|
1863
|
+
let pos = from;
|
|
1864
|
+
for (;;) {
|
|
1865
|
+
let cursor = skipTrivia(pos);
|
|
1866
|
+
if (source[cursor] !== ".") return pos;
|
|
1867
|
+
cursor = skipTrivia(cursor + 1);
|
|
1868
|
+
const method = IDENT_START_RE.exec(source.slice(cursor))?.[0];
|
|
1869
|
+
if (method !== "extend" && method !== "from") return pos;
|
|
1870
|
+
cursor = skipTrivia(cursor + method.length);
|
|
1871
|
+
if (source[cursor] !== "(") return pos;
|
|
1872
|
+
cursor = skipTrivia(cursor + 1);
|
|
1873
|
+
const base = IDENT_START_RE.exec(source.slice(cursor))?.[0];
|
|
1874
|
+
if (!base) return pos;
|
|
1875
|
+
cursor = skipTrivia(cursor + base.length);
|
|
1876
|
+
while (source[cursor] === ".") {
|
|
1877
|
+
const afterDot = skipTrivia(cursor + 1);
|
|
1878
|
+
const segment = IDENT_START_RE.exec(source.slice(afterDot))?.[0];
|
|
1879
|
+
if (!segment) break;
|
|
1880
|
+
cursor = skipTrivia(afterDot + segment.length);
|
|
1881
|
+
}
|
|
1882
|
+
if (source[cursor] === ",") cursor = skipTrivia(cursor + 1);
|
|
1883
|
+
if (source[cursor] !== ")") return pos;
|
|
1884
|
+
identifiers.add(base);
|
|
1885
|
+
pos = cursor + 1;
|
|
2038
1886
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
}
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
|
|
2056
|
-
const path = paths[index];
|
|
2057
|
-
const conflictingPath = paths[(index + 1) % paths.length];
|
|
2058
|
-
diagnostics.push({
|
|
2059
|
-
code: "SOURCE_RESOLVER_IDENTITY_COLLISION",
|
|
2060
|
-
message: `Resolver lookup paths '${path}' and '${conflictingPath}' normalize to the same private identity '${canonicalPath}'. Rename one of the source files so resolver identities remain unique.`,
|
|
2061
|
-
originalPath: analysisOwner.get(path) ?? path,
|
|
2062
|
-
canonicalPath,
|
|
2063
|
-
conflictingOriginalPath: analysisOwner.get(conflictingPath) ?? conflictingPath
|
|
2064
|
-
});
|
|
2065
|
-
}
|
|
2066
|
-
for (const original of svelteEntries) {
|
|
2067
|
-
const result = await svelteAdapter(original.source, original.path, { attributeResolver: (request) => resolvers.attribute(original.path, request) });
|
|
2068
|
-
if (result.kind === "missing-dep") {
|
|
2069
|
-
diagnostics.push({
|
|
2070
|
-
code: "SOURCE_SVELTE_DEPENDENCY_MISSING",
|
|
2071
|
-
message: "Install optional peer dependency 'svelte' to analyze opted-in Svelte source.",
|
|
2072
|
-
originalPath: original.path
|
|
2073
|
-
});
|
|
2074
|
-
continue;
|
|
1887
|
+
};
|
|
1888
|
+
const boundIdentifierBefore = (index) => {
|
|
1889
|
+
const match = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*$/.exec(source.slice(0, index));
|
|
1890
|
+
return match ? match[1] : null;
|
|
1891
|
+
};
|
|
1892
|
+
const chainRootIdentifiers = /* @__PURE__ */ new Set();
|
|
1893
|
+
const createSystemAnchor = /createSystem\s*\(/g;
|
|
1894
|
+
let anchorMatch;
|
|
1895
|
+
while ((anchorMatch = createSystemAnchor.exec(source)) !== null) {
|
|
1896
|
+
let pos = anchorMatch.index + anchorMatch[0].length;
|
|
1897
|
+
let depth = 1;
|
|
1898
|
+
while (pos < source.length && depth > 0) {
|
|
1899
|
+
const ch = source[pos];
|
|
1900
|
+
if (ch === "(") depth++;
|
|
1901
|
+
else if (ch === ")") depth--;
|
|
1902
|
+
pos++;
|
|
2075
1903
|
}
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
1904
|
+
consumeChainLinks(pos);
|
|
1905
|
+
const bound = boundIdentifierBefore(anchorMatch.index);
|
|
1906
|
+
if (bound) chainRootIdentifiers.add(bound);
|
|
1907
|
+
}
|
|
1908
|
+
const scannedRoots = /* @__PURE__ */ new Set();
|
|
1909
|
+
for (;;) {
|
|
1910
|
+
const pendingRoots = [...chainRootIdentifiers].filter((root) => !scannedRoots.has(root));
|
|
1911
|
+
if (pendingRoots.length === 0) break;
|
|
1912
|
+
for (const root of pendingRoots) {
|
|
1913
|
+
scannedRoots.add(root);
|
|
1914
|
+
const rootUse = new RegExp(`(?<![a-zA-Z0-9_$])${root.replace(/\$/g, "\\$")}(?![a-zA-Z0-9_$])`, "g");
|
|
1915
|
+
let useMatch;
|
|
1916
|
+
while ((useMatch = rootUse.exec(source)) !== null) {
|
|
1917
|
+
const afterIdentifier = useMatch.index + useMatch[0].length;
|
|
1918
|
+
if (consumeChainLinks(afterIdentifier) === afterIdentifier) continue;
|
|
1919
|
+
const bound = boundIdentifierBefore(useMatch.index);
|
|
1920
|
+
if (bound) chainRootIdentifiers.add(bound);
|
|
1921
|
+
}
|
|
2079
1922
|
}
|
|
2080
|
-
addAdaptedEntries(original.path, result.entries);
|
|
2081
1923
|
}
|
|
2082
|
-
return
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
function collectFileFacts(analysisEntries, options) {
|
|
2095
|
-
const cache = options.factsCache;
|
|
2096
|
-
if (!cache) return JSON.parse(options.extractFacts(JSON.stringify(analysisEntries)));
|
|
2097
|
-
const pending = analysisEntries.filter((entry) => cache.get(entry.path)?.hash !== entry.hash);
|
|
2098
|
-
let parseCount = 0;
|
|
2099
|
-
if (pending.length > 0) {
|
|
2100
|
-
const fresh = JSON.parse(options.extractFacts(JSON.stringify(pending)));
|
|
2101
|
-
parseCount = fresh.parseCount;
|
|
2102
|
-
for (const entry of pending) {
|
|
2103
|
-
const facts = fresh.files[entry.path];
|
|
2104
|
-
if (facts) cache.set(entry.path, {
|
|
2105
|
-
hash: entry.hash,
|
|
2106
|
-
facts
|
|
2107
|
-
});
|
|
2108
|
-
else cache.delete(entry.path);
|
|
1924
|
+
if (identifiers.size === 0) return [];
|
|
1925
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
1926
|
+
const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
|
|
1927
|
+
let importMatch;
|
|
1928
|
+
while ((importMatch = importRegex.exec(source)) !== null) {
|
|
1929
|
+
const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
|
|
1930
|
+
if (comboDefault) importMap.set(comboDefault, specifier);
|
|
1931
|
+
if (defaultImport) importMap.set(defaultImport, specifier);
|
|
1932
|
+
if (namedImports) for (const binding of namedImports.split(",")) {
|
|
1933
|
+
const parts = binding.trim().split(/\s+as\s+/);
|
|
1934
|
+
const localName = (parts[1] || parts[0]).trim();
|
|
1935
|
+
if (localName) importMap.set(localName, specifier);
|
|
2109
1936
|
}
|
|
2110
1937
|
}
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2113
|
-
const
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
if (
|
|
1938
|
+
const systemFileDir = (0, path.dirname)(systemFilePath);
|
|
1939
|
+
const packages = /* @__PURE__ */ new Set();
|
|
1940
|
+
for (const id of identifiers) {
|
|
1941
|
+
const specifier = importMap.get(id);
|
|
1942
|
+
if (!specifier) continue;
|
|
1943
|
+
if (specifier.startsWith(".")) packages.add((0, path.resolve)(systemFileDir, specifier));
|
|
1944
|
+
else packages.add(specifier);
|
|
2117
1945
|
}
|
|
2118
|
-
return
|
|
2119
|
-
files,
|
|
2120
|
-
parseCount
|
|
2121
|
-
};
|
|
1946
|
+
return Array.from(packages);
|
|
2122
1947
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
return {
|
|
2136
|
-
...result,
|
|
2137
|
-
originalEntries: result.originalEntries.filter((entry) => !invalidOriginals.has(entry.path)),
|
|
2138
|
-
analysisEntries: result.analysisEntries.filter((entry) => analysisPaths.has(entry.path)),
|
|
2139
|
-
ownership
|
|
2140
|
-
};
|
|
1948
|
+
//#endregion
|
|
1949
|
+
//#region pipeline/source-corpus.ts
|
|
1950
|
+
function assembleRawEntries({ fileCache, externalFileOwners }) {
|
|
1951
|
+
const project = [];
|
|
1952
|
+
const external = [];
|
|
1953
|
+
for (const [path, { hash, source }] of fileCache) (externalFileOwners[path] === void 0 ? project : external).push({
|
|
1954
|
+
path,
|
|
1955
|
+
source,
|
|
1956
|
+
hash
|
|
1957
|
+
});
|
|
1958
|
+
project.sort((a, b) => compareDiscoveryOrder(a.path, b.path));
|
|
1959
|
+
return [...project, ...external];
|
|
2141
1960
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
* divergences; see also `enforceExternalTokenContracts`).
|
|
2148
|
-
*/
|
|
2149
|
-
function createSourceIngestor(host) {
|
|
2150
|
-
const factsCache = /* @__PURE__ */ new Map();
|
|
2151
|
-
/** Non-strict warn dedupe: a quarantined-but-retained original re-ingests
|
|
2152
|
-
* on every later corpus pass, and re-warning each save is noise. Keyed by
|
|
2153
|
-
* original path; cleared when that original publishes clean again. */
|
|
2154
|
-
const warnedByOriginal = /* @__PURE__ */ new Map();
|
|
1961
|
+
function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
1962
|
+
let published = {
|
|
1963
|
+
analysisEntries: /* @__PURE__ */ new Map(),
|
|
1964
|
+
ownership: {}
|
|
1965
|
+
};
|
|
2155
1966
|
return {
|
|
2156
|
-
async
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2159
|
-
return
|
|
2160
|
-
extractFacts,
|
|
2161
|
-
factsCache
|
|
2162
|
-
});
|
|
1967
|
+
async prepare(input) {
|
|
1968
|
+
const rawEntries = "fileCache" in input ? assembleRawEntries(input) : input;
|
|
1969
|
+
const ingested = await ingestor.ingest(rawEntries);
|
|
1970
|
+
return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
|
|
2163
1971
|
},
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
const line = `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`;
|
|
2174
|
-
let warned = warnedByOriginal.get(diagnostic.originalPath);
|
|
2175
|
-
if (!warned) {
|
|
2176
|
-
warned = /* @__PURE__ */ new Set();
|
|
2177
|
-
warnedByOriginal.set(diagnostic.originalPath, warned);
|
|
2178
|
-
}
|
|
2179
|
-
if (warned.has(line)) continue;
|
|
2180
|
-
warned.add(line);
|
|
2181
|
-
host.warn(`${host.prefix} ${line}`);
|
|
2182
|
-
}
|
|
2183
|
-
return invalidOriginals;
|
|
1972
|
+
publish(accepted) {
|
|
1973
|
+
ingestor.markPublished(accepted);
|
|
1974
|
+
published = {
|
|
1975
|
+
analysisEntries: new Map(accepted.analysisEntries.map((entry) => [entry.path, {
|
|
1976
|
+
hash: entry.hash,
|
|
1977
|
+
source: entry.source
|
|
1978
|
+
}])),
|
|
1979
|
+
ownership: accepted.ownership
|
|
1980
|
+
};
|
|
2184
1981
|
},
|
|
2185
|
-
|
|
2186
|
-
|
|
1982
|
+
get published() {
|
|
1983
|
+
return published;
|
|
2187
1984
|
}
|
|
2188
1985
|
};
|
|
2189
1986
|
}
|
|
2190
1987
|
//#endregion
|
|
2191
1988
|
//#region pipeline/asset-placeholders.ts
|
|
2192
|
-
/**
|
|
2193
|
-
* Host-side handling of `asset()` placeholders (global-styles-system):
|
|
2194
|
-
* `animus-asset:<specifier>` markers ride through the sandbox and the
|
|
2195
|
-
* emitter byte-exact; each HOST plugin substitutes them with its bundler's
|
|
2196
|
-
* resolved asset URL after extraction. This module owns the shared scanning
|
|
2197
|
-
* and replacement mechanics — resolution and strict gating stay at the
|
|
2198
|
-
* plugin call sites.
|
|
2199
|
-
*/
|
|
2200
|
-
/** Mirrors the producer-side constant in `@animus-ui/system`'s `asset.ts`
|
|
2201
|
-
* (extract must not take a runtime dependency on system); the pairing is
|
|
2202
|
-
* pinned by a contract test in `tests/asset-placeholders.test.ts`. */
|
|
2203
1989
|
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
2204
1990
|
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2205
1991
|
const PREFIX_RE = escapeRegExp(ASSET_PLACEHOLDER_PREFIX);
|
|
2206
|
-
/**
|
|
2207
|
-
* Quoted `url('animus-asset:<specifier>')` form — the emitter always quotes
|
|
2208
|
-
* `url()` values, so a quoted match carries the FULL specifier, including
|
|
2209
|
-
* whitespace and parentheses (D5 pins the placeholder as specifier-verbatim,
|
|
2210
|
-
* so scanning fidelity is the only place such characters can be honored).
|
|
2211
|
-
*/
|
|
2212
1992
|
const QUOTED_PLACEHOLDER_RE = new RegExp(`(['"])${PREFIX_RE}([^'"]*?)\\1`, "g");
|
|
2213
|
-
/** Bare-form fallback: a placeholder up to CSS url()/quote/whitespace. */
|
|
2214
1993
|
const BARE_PLACEHOLDER_RE = new RegExp(`${PREFIX_RE}([^'")\\s]+)`, "g");
|
|
2215
|
-
/** Unique asset specifiers referenced by placeholders, in appearance order. */
|
|
2216
1994
|
function findAssetSpecifiers(css) {
|
|
2217
1995
|
if (!css.includes("animus-asset:")) return [];
|
|
2218
1996
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2223,19 +2001,6 @@ function findAssetSpecifiers(css) {
|
|
|
2223
2001
|
for (const match of remainder.matchAll(BARE_PLACEHOLDER_RE)) seen.add(match[1]);
|
|
2224
2002
|
return [...seen];
|
|
2225
2003
|
}
|
|
2226
|
-
/**
|
|
2227
|
-
* Replace each specifier's placeholder with its substitution. Specifiers
|
|
2228
|
-
* absent from the map keep their placeholder — the caller decides whether
|
|
2229
|
-
* that is a strict failure or a warn-and-emit-literally. A substitution may
|
|
2230
|
-
* itself be a bundler marker (e.g. Vite's `__VITE_ASSET__<ref>__`) that the
|
|
2231
|
-
* host's own asset pipeline resolves to a hashed file name later.
|
|
2232
|
-
*
|
|
2233
|
-
* Each replacement is anchored to a following delimiter (quote, `)`,
|
|
2234
|
-
* whitespace, or end of input) so a specifier that textually prefixes a
|
|
2235
|
-
* longer one can never clobber the longer placeholder; longest-first
|
|
2236
|
-
* ordering additionally lets the longer of two mapped specifiers claim its
|
|
2237
|
-
* occurrences before the shorter runs.
|
|
2238
|
-
*/
|
|
2239
2004
|
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
2240
2005
|
if (urlBySpecifier.size === 0) return css;
|
|
2241
2006
|
if (!css.includes("animus-asset:")) return css;
|
|
@@ -2250,14 +2015,8 @@ function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
|
2250
2015
|
//#endregion
|
|
2251
2016
|
//#region pipeline/resolve-asset.ts
|
|
2252
2017
|
/**
|
|
2253
|
-
*
|
|
2254
|
-
* asset()
|
|
2255
|
-
* an alias such as `@fonts` works in application modules, so
|
|
2256
|
-
* `asset('@fonts/inter.woff2')` must resolve identically — then direct Node
|
|
2257
|
-
* resolution, then the package-root fallback (exports maps rarely list
|
|
2258
|
-
* asset subpaths). Bundler-native resolution (Vite's `this.resolve`) is
|
|
2259
|
-
* still preferred where available; this resolver covers the paths that have
|
|
2260
|
-
* no bundler hook (Next's session, Vite's dev re-analysis).
|
|
2018
|
+
* Asset resolution for hosts with no bundler hook. Host path aliases apply
|
|
2019
|
+
* FIRST, so `asset('@fonts/x')` resolves as the alias does in app modules.
|
|
2261
2020
|
*/
|
|
2262
2021
|
const requireByRoot = /* @__PURE__ */ new Map();
|
|
2263
2022
|
function requireAnchoredAt(rootDir) {
|
|
@@ -2278,23 +2037,20 @@ function packageRootFromEntry(entry) {
|
|
|
2278
2037
|
}
|
|
2279
2038
|
}
|
|
2280
2039
|
const aliasTableCache = /* @__PURE__ */ new Map();
|
|
2040
|
+
/**
|
|
2041
|
+
* A parse failure throws: an empty table would disable every alias-based
|
|
2042
|
+
* `asset()` and ship dangling `url()`s as a successful build.
|
|
2043
|
+
*/
|
|
2281
2044
|
function parseAliasTable(pathAliasesJson) {
|
|
2282
2045
|
const cached = aliasTableCache.get(pathAliasesJson);
|
|
2283
2046
|
if (cached) return cached;
|
|
2284
|
-
|
|
2285
|
-
try {
|
|
2286
|
-
aliases = JSON.parse(pathAliasesJson).aliases ?? [];
|
|
2287
|
-
} catch {
|
|
2288
|
-
aliases = [];
|
|
2289
|
-
}
|
|
2047
|
+
const aliases = parseInternalWire(pathAliasesJson, "pathAliasesJson (the host alias table from buildPathAliasesJson)").aliases ?? [];
|
|
2290
2048
|
aliasTableCache.set(pathAliasesJson, aliases);
|
|
2291
2049
|
return aliases;
|
|
2292
2050
|
}
|
|
2293
2051
|
/**
|
|
2294
|
-
* Map a specifier through the
|
|
2295
|
-
*
|
|
2296
|
-
* pattern first by `buildPathAliasesJson`). Returns an absolute path to an
|
|
2297
|
-
* EXISTING file, or null when no alias claims the specifier.
|
|
2052
|
+
* Map a specifier through the host alias table, whose entries arrive sorted
|
|
2053
|
+
* longest-pattern-first. Returns an absolute path to an EXISTING file.
|
|
2298
2054
|
*/
|
|
2299
2055
|
function resolveThroughPathAliases(specifier, rootDir, pathAliasesJson) {
|
|
2300
2056
|
if (!pathAliasesJson) return null;
|
|
@@ -2311,9 +2067,8 @@ function resolveThroughPathAliases(specifier, rootDir, pathAliasesJson) {
|
|
|
2311
2067
|
return null;
|
|
2312
2068
|
}
|
|
2313
2069
|
/**
|
|
2314
|
-
* Resolve an asset specifier
|
|
2315
|
-
*
|
|
2316
|
-
* Returns null when nothing matches — strict gating stays at the caller.
|
|
2070
|
+
* Resolve an asset specifier: host aliases, then Node resolution anchored at
|
|
2071
|
+
* `rootDir`, then the package root. Strict gating stays at the caller.
|
|
2317
2072
|
*/
|
|
2318
2073
|
function resolveAssetFile(specifier, rootDir, pathAliasesJson) {
|
|
2319
2074
|
const aliased = resolveThroughPathAliases(specifier, rootDir, pathAliasesJson);
|
|
@@ -2344,20 +2099,13 @@ function resolveAssetFile(specifier, rootDir, pathAliasesJson) {
|
|
|
2344
2099
|
//#endregion
|
|
2345
2100
|
//#region pipeline/correlate-external-tokens.ts
|
|
2346
2101
|
/**
|
|
2347
|
-
* Index the loader-captured theme manifests by owning specifier.
|
|
2348
|
-
*
|
|
2349
|
-
* realpath'd before prefix-matching; a module outside every known package dir
|
|
2350
|
-
* (typically the consumer's own theme) contributes nothing.
|
|
2102
|
+
* Index the loader-captured theme manifests by owning specifier. An
|
|
2103
|
+
* unparseable capture throws: an empty index reads as "no source defines it".
|
|
2351
2104
|
*/
|
|
2352
2105
|
function buildSourceTokenIndex(opts) {
|
|
2353
2106
|
const index = /* @__PURE__ */ new Map();
|
|
2354
2107
|
if (!opts.sourceThemeManifestsJson) return index;
|
|
2355
|
-
|
|
2356
|
-
try {
|
|
2357
|
-
manifests = JSON.parse(opts.sourceThemeManifestsJson);
|
|
2358
|
-
} catch {
|
|
2359
|
-
return index;
|
|
2360
|
-
}
|
|
2108
|
+
const manifests = parseInternalWire(opts.sourceThemeManifestsJson, "sourceThemeManifestsJson (the system loader's built-theme capture)");
|
|
2361
2109
|
const realDirOwners = [];
|
|
2362
2110
|
const addOwnerDir = (dir, specifier) => {
|
|
2363
2111
|
if (!realDirOwners.some((entry) => entry.dir === dir)) realDirOwners.push({
|
|
@@ -2388,11 +2136,8 @@ function buildSourceTokenIndex(opts) {
|
|
|
2388
2136
|
return index;
|
|
2389
2137
|
}
|
|
2390
2138
|
/**
|
|
2391
|
-
*
|
|
2392
|
-
*
|
|
2393
|
-
* whose token that source's manifest defines. The messages follow the
|
|
2394
|
-
* standard severity routing at the CALLER (throw under `strict`, warn
|
|
2395
|
-
* otherwise) — this join never reports on its own.
|
|
2139
|
+
* One message per distinct (component, token, specifier) whose source
|
|
2140
|
+
* defines the token. Severity routing stays at the caller.
|
|
2396
2141
|
*/
|
|
2397
2142
|
function correlateExternalTokenDiagnostics(opts) {
|
|
2398
2143
|
const messages = [];
|
|
@@ -2411,11 +2156,8 @@ function correlateExternalTokenDiagnostics(opts) {
|
|
|
2411
2156
|
}
|
|
2412
2157
|
const indexCache = /* @__PURE__ */ new WeakMap();
|
|
2413
2158
|
/**
|
|
2414
|
-
* The
|
|
2415
|
-
*
|
|
2416
|
-
* join them against the engine's candidates, and route the resulting
|
|
2417
|
-
* teaching errors — throw under `strict`, warn otherwise. One entry point
|
|
2418
|
-
* so the two hosts cannot drift on wiring or severity semantics.
|
|
2159
|
+
* The one cross-source token-contract gate both plugins share, so the hosts
|
|
2160
|
+
* cannot drift on wiring or severity.
|
|
2419
2161
|
*/
|
|
2420
2162
|
function enforceExternalTokenContracts(opts) {
|
|
2421
2163
|
const manifestsJson = opts.sourceThemeManifestsJson ?? "";
|
|
@@ -2443,12 +2185,6 @@ function enforceExternalTokenContracts(opts) {
|
|
|
2443
2185
|
}
|
|
2444
2186
|
//#endregion
|
|
2445
2187
|
//#region pipeline/path-aliases.ts
|
|
2446
|
-
/**
|
|
2447
|
-
* Normalize alias pairs: strip a `rootDir` prefix from targets, classify
|
|
2448
|
-
* exact (file) vs prefix (directory) matches, force trailing slashes on
|
|
2449
|
-
* prefix entries, and sort longest pattern first for correct matching
|
|
2450
|
-
* priority. Returns null when no entries survive.
|
|
2451
|
-
*/
|
|
2452
2188
|
function buildPathAliasesJson(pairs, rootDir) {
|
|
2453
2189
|
const entries = [];
|
|
2454
2190
|
for (const { pattern, target, kind } of pairs) {
|
|
@@ -2473,17 +2209,6 @@ function buildPathAliasesJson(pairs, rootDir) {
|
|
|
2473
2209
|
}
|
|
2474
2210
|
//#endregion
|
|
2475
2211
|
//#region pipeline/post-process-css.ts
|
|
2476
|
-
/**
|
|
2477
|
-
* CSS post-processing (spec: css-post-processing) — Lightning CSS
|
|
2478
|
-
* autoprefixing, syntax lowering, and optional minification against
|
|
2479
|
-
* browserslist targets. The single implementation consumed by both the
|
|
2480
|
-
* Vite plugin (virtual-module serve time) and the Next.js plugin
|
|
2481
|
-
* (ExtractionSession artifact emission).
|
|
2482
|
-
*/
|
|
2483
|
-
/**
|
|
2484
|
-
* Resolve browser targets for Lightning CSS.
|
|
2485
|
-
* Priority: explicit config → project browserslist → 'defaults' fallback.
|
|
2486
|
-
*/
|
|
2487
2212
|
function resolveLightningTargets(explicitTargets, rootDir) {
|
|
2488
2213
|
let queries;
|
|
2489
2214
|
if (explicitTargets) queries = Array.isArray(explicitTargets) ? explicitTargets : [explicitTargets];
|
|
@@ -2491,12 +2216,9 @@ function resolveLightningTargets(explicitTargets, rootDir) {
|
|
|
2491
2216
|
const detected = (0, browserslist.default)(void 0, { path: rootDir });
|
|
2492
2217
|
queries = detected.length > 0 ? detected : (0, browserslist.default)("defaults");
|
|
2493
2218
|
}
|
|
2494
|
-
|
|
2219
|
+
const [firstQuery] = queries;
|
|
2220
|
+
return (0, lightningcss.browserslistToTargets)(firstQuery !== void 0 && firstQuery.includes(" ") ? (0, browserslist.default)(queries) : queries);
|
|
2495
2221
|
}
|
|
2496
|
-
/**
|
|
2497
|
-
* Post-process CSS with Lightning CSS: autoprefixing + optional minification.
|
|
2498
|
-
* On failure, returns the original CSS and logs a warning.
|
|
2499
|
-
*/
|
|
2500
2222
|
function postProcessCss(css, opts) {
|
|
2501
2223
|
if (!css) return css;
|
|
2502
2224
|
try {
|
|
@@ -2514,28 +2236,20 @@ function postProcessCss(css, opts) {
|
|
|
2514
2236
|
}
|
|
2515
2237
|
//#endregion
|
|
2516
2238
|
//#region pipeline/manifest-diagnostics.ts
|
|
2517
|
-
/** Stable code for selector forms with no substitutable subject
|
|
2518
|
-
*
|
|
2239
|
+
/** Stable code for selector forms with no substitutable subject; the
|
|
2240
|
+
* extraction engine mints the same string. */
|
|
2519
2241
|
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
2520
|
-
/**
|
|
2521
|
-
*
|
|
2522
|
-
* through the helpers below, never their own regex copies). */
|
|
2242
|
+
/** The one matcher for the engine's unresolved-parent chain-drop message;
|
|
2243
|
+
* consumers match through the exported helpers, never their own copy. */
|
|
2523
2244
|
const UNRESOLVED_PARENT_RE = /chain dropped: could not resolve parent component '([^']+)'/;
|
|
2524
|
-
/** True when the diagnostic reports a chain dropped for an unresolved
|
|
2525
|
-
* parent component. */
|
|
2526
2245
|
function isUnresolvedParentDrop(diagnostic) {
|
|
2527
|
-
return
|
|
2246
|
+
return UNRESOLVED_PARENT_RE.test(diagnostic.message);
|
|
2528
2247
|
}
|
|
2529
|
-
/** The parent binding named by an unresolved-parent drop, or null when the
|
|
2530
|
-
* diagnostic is not one. */
|
|
2531
2248
|
function unresolvedParentName(diagnostic) {
|
|
2532
|
-
if (typeof diagnostic?.message !== "string") return null;
|
|
2533
2249
|
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
2534
2250
|
}
|
|
2535
|
-
/** True when the selector
|
|
2536
|
-
*
|
|
2537
|
-
* ancestor, leading, and repeated subjects all count; a `&` inside a
|
|
2538
|
-
* quoted attribute value does not). */
|
|
2251
|
+
/** True when the selector carries at least one substitutable `&` subject
|
|
2252
|
+
* outside quoted text. */
|
|
2539
2253
|
function hasSelectorSubject(value) {
|
|
2540
2254
|
let quote = null;
|
|
2541
2255
|
let escaped = false;
|
|
@@ -2556,44 +2270,91 @@ function hasSelectorSubject(value) {
|
|
|
2556
2270
|
return false;
|
|
2557
2271
|
}
|
|
2558
2272
|
/**
|
|
2559
|
-
*
|
|
2560
|
-
*
|
|
2561
|
-
* every `&` sits inside quoted text, so there is nothing to anchor the
|
|
2562
|
-
* class to. Ancestor-prefixed and repeated subjects are supported and pass
|
|
2563
|
-
* validation. The system-config boundary is where these must fail loud
|
|
2564
|
-
* (alias values never reach the evaluator's key guard).
|
|
2273
|
+
* Coded diagnostics for selector-shaped alias values with no substitutable
|
|
2274
|
+
* subject; alias values never reach the evaluator's own key guard.
|
|
2565
2275
|
*/
|
|
2566
2276
|
function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
2567
2277
|
if (!selectorAliasesJson) return [];
|
|
2568
|
-
|
|
2569
|
-
try {
|
|
2570
|
-
aliases = JSON.parse(selectorAliasesJson);
|
|
2571
|
-
} catch {
|
|
2572
|
-
return [];
|
|
2573
|
-
}
|
|
2278
|
+
const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
|
|
2574
2279
|
const diagnostics = [];
|
|
2575
|
-
for (const [name, value] of Object.entries(aliases)) {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2280
|
+
for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
|
|
2281
|
+
file: "system",
|
|
2282
|
+
component: name,
|
|
2283
|
+
kind: "warn",
|
|
2284
|
+
message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
|
|
2285
|
+
code: SELECTOR_UNSUPPORTED_SUBJECT,
|
|
2286
|
+
severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
|
|
2287
|
+
});
|
|
2288
|
+
return diagnostics;
|
|
2289
|
+
}
|
|
2290
|
+
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
2291
|
+
/**
|
|
2292
|
+
* A configured source file that could not be read is a LOST INPUT: the
|
|
2293
|
+
* emitted artifacts silently lack whatever that file declared.
|
|
2294
|
+
*/
|
|
2295
|
+
function unreadableSourceDiagnostic(relPath, error) {
|
|
2296
|
+
return {
|
|
2297
|
+
file: relPath,
|
|
2298
|
+
component: "source",
|
|
2299
|
+
kind: "warn",
|
|
2300
|
+
message: `configured source file ${relPath} could not be read and was skipped: ${String(error)}`,
|
|
2301
|
+
code: UNREADABLE_SOURCE_FILE,
|
|
2302
|
+
severity: severityFor(UNREADABLE_SOURCE_FILE)
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
/** The collision entry code minted by the system package's merge. */
|
|
2306
|
+
const VOCABULARY_COLLISION = "animus.vocabulary.collision";
|
|
2307
|
+
/** Minted when a sealed kit with registered vocabulary is consumed through
|
|
2308
|
+
* the deprecated `from()`/`includes:` verbs. */
|
|
2309
|
+
const VOCABULARY_LEGACY_VERB = "animus.vocabulary.legacy-verb";
|
|
2310
|
+
const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
2311
|
+
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
2312
|
+
[UNREADABLE_SOURCE_FILE, "error"],
|
|
2313
|
+
[VOCABULARY_COLLISION, "warn"],
|
|
2314
|
+
[VOCABULARY_LEGACY_VERB, "warn"]
|
|
2315
|
+
]);
|
|
2316
|
+
/** An unlisted code is `warn`: a witness kind from a newer system package
|
|
2317
|
+
* must not fail the strict build of a host that predates its writer. */
|
|
2318
|
+
function severityFor(code) {
|
|
2319
|
+
return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
|
|
2320
|
+
}
|
|
2321
|
+
/**
|
|
2322
|
+
* The one mapper of a sealed system's witness entries for every host: the
|
|
2323
|
+
* loader's evaluation host shims `console`, so the record is the channel.
|
|
2324
|
+
*/
|
|
2325
|
+
function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
|
|
2326
|
+
if (!vocabularyWitnessesJson) return [];
|
|
2327
|
+
const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
|
|
2328
|
+
const diagnostics = [];
|
|
2329
|
+
for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
|
|
2330
|
+
file: "system",
|
|
2331
|
+
component: entry.name ?? "keyframes",
|
|
2332
|
+
kind: "warn",
|
|
2333
|
+
message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
|
|
2334
|
+
code: entry.code,
|
|
2335
|
+
severity: severityFor(entry.code)
|
|
2336
|
+
});
|
|
2337
|
+
else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
|
|
2338
|
+
file: "system",
|
|
2339
|
+
component: "keyframes",
|
|
2340
|
+
kind: "warn",
|
|
2341
|
+
message: `a sealed system (${entry.source ?? `'${entry.verb}' source`}) with registered vocabulary [${(entry.names ?? []).join(", ")}] was consumed through the deprecated '${entry.verb}' verb, which cannot carry it — those collections do NOT reach this consumer; use createSystem().extend(source) (${entry.code})`,
|
|
2342
|
+
code: entry.code,
|
|
2343
|
+
severity: severityFor(entry.code)
|
|
2344
|
+
});
|
|
2345
|
+
else diagnostics.push({
|
|
2346
|
+
file: "system",
|
|
2347
|
+
component: "vocabulary",
|
|
2348
|
+
kind: "warn",
|
|
2349
|
+
message: `unrecognized vocabulary witness entry ${JSON.stringify(entry)} — a newer @animus-ui/system may have recorded a witness kind this host predates${entry.code ? ` (${entry.code})` : ""}`,
|
|
2350
|
+
code: entry.code,
|
|
2351
|
+
severity: severityFor(entry.code)
|
|
2352
|
+
});
|
|
2586
2353
|
return diagnostics;
|
|
2587
2354
|
}
|
|
2588
2355
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
*
|
|
2591
|
-
* Single authoritative copy for both extraction plugins — and the single
|
|
2592
|
-
* strict-escalation policy point: error-severity diagnostics throw one
|
|
2593
|
-
* Error naming every offender when `policy.strict`, and print as warnings
|
|
2594
|
-
* otherwise. Surfaces `bail` (component not extracted), `skip` (component
|
|
2595
|
-
* skipped), and `warn` kinds; unknown kinds stay silent. Printed lines
|
|
2596
|
-
* include the diagnostic code when the message doesn't already carry it.
|
|
2356
|
+
* The single strict-escalation policy point for both extraction plugins:
|
|
2357
|
+
* error-severity diagnostics throw together under `strict`, else warn.
|
|
2597
2358
|
*/
|
|
2598
2359
|
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
2599
2360
|
const errors = [];
|
|
@@ -2615,11 +2376,6 @@ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
|
2615
2376
|
}
|
|
2616
2377
|
//#endregion
|
|
2617
2378
|
//#region pipeline/unit-fallback.ts
|
|
2618
|
-
/**
|
|
2619
|
-
* Append `px` to bare numeric values in CSS declarations for properties
|
|
2620
|
-
* that expect length units. Unitless properties are preserved as-is.
|
|
2621
|
-
* Numbers inside CSS function calls (cubic-bezier, rgb, calc, etc.) are skipped.
|
|
2622
|
-
*/
|
|
2623
2379
|
function applyUnitFallback(css) {
|
|
2624
2380
|
return css.replace(/([a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
|
|
2625
2381
|
if ((0, _animus_ui_properties.isUnitlessProperty)(prop)) return match;
|
|
@@ -2655,13 +2411,14 @@ function applyUnitFallback(css) {
|
|
|
2655
2411
|
}
|
|
2656
2412
|
//#endregion
|
|
2657
2413
|
//#region pipeline/run-analysis.ts
|
|
2658
|
-
/**
|
|
2659
|
-
*
|
|
2660
|
-
* the persistence shape for `.animus/analysis-inputs.json` — an isolated
|
|
2661
|
-
* process can replay the analysis from exactly this object
|
|
2662
|
-
* (spec: next-turbopack-integration).
|
|
2663
|
-
*/
|
|
2414
|
+
/** The named `analyzeProject` input set, and the persistence shape an
|
|
2415
|
+
* isolated process replays the analysis from. */
|
|
2664
2416
|
function buildAnalysisInputs(opts) {
|
|
2417
|
+
const emitterConfig = {
|
|
2418
|
+
runtime_import: opts.emitter.runtimeImport,
|
|
2419
|
+
css_module_id: opts.emitter.cssModuleId
|
|
2420
|
+
};
|
|
2421
|
+
if (opts.emitter.systemPropsModuleId) emitterConfig.system_props_module_id = opts.emitter.systemPropsModuleId;
|
|
2665
2422
|
return {
|
|
2666
2423
|
filesJson: JSON.stringify(opts.fileEntries),
|
|
2667
2424
|
scalesJson: opts.system.scalesJson,
|
|
@@ -2671,11 +2428,7 @@ function buildAnalysisInputs(opts) {
|
|
|
2671
2428
|
groupRegistryJson: opts.system.groupRegistryJson,
|
|
2672
2429
|
packageResolutionJson: JSON.stringify(opts.packageMap),
|
|
2673
2430
|
devMode: opts.devMode,
|
|
2674
|
-
emitterConfigJson: JSON.stringify(
|
|
2675
|
-
runtime_import: opts.emitter.runtimeImport,
|
|
2676
|
-
css_module_id: opts.emitter.cssModuleId,
|
|
2677
|
-
...opts.emitter.systemPropsModuleId ? { system_props_module_id: opts.emitter.systemPropsModuleId } : {}
|
|
2678
|
-
}),
|
|
2431
|
+
emitterConfigJson: JSON.stringify(emitterConfig),
|
|
2679
2432
|
selectorAliasesJson: opts.system.selectorAliasesJson,
|
|
2680
2433
|
globalStyleBlocksJson: opts.system.globalStyleBlocksJson,
|
|
2681
2434
|
pathAliasesJson: opts.pathAliasesJson,
|
|
@@ -2686,17 +2439,13 @@ function buildAnalysisInputs(opts) {
|
|
|
2686
2439
|
externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
|
|
2687
2440
|
};
|
|
2688
2441
|
}
|
|
2689
|
-
/** Whether the loader captured at least one source built-theme manifest. */
|
|
2690
2442
|
function hasSourceThemeManifests(system) {
|
|
2691
|
-
const json = system.sourceThemeManifestsJson;
|
|
2692
|
-
return
|
|
2443
|
+
const json = system.sourceThemeManifestsJson ?? "";
|
|
2444
|
+
return json.length > 0 && json !== "{}";
|
|
2693
2445
|
}
|
|
2694
2446
|
/**
|
|
2695
|
-
* The one analysis invocation both plugins share
|
|
2696
|
-
*
|
|
2697
|
-
* manifest, surface its diagnostics, and resolve the CSS outputs.
|
|
2698
|
-
*
|
|
2699
|
-
* Error handling stays at the call site (strict-mode throw vs warn).
|
|
2447
|
+
* The one analysis invocation both plugins share. Error handling stays at
|
|
2448
|
+
* the call site (strict-mode throw vs warn).
|
|
2700
2449
|
*/
|
|
2701
2450
|
function runProjectAnalysis(engineApi, opts) {
|
|
2702
2451
|
const { analyzeProject } = engineApi();
|
|
@@ -2716,8 +2465,8 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2716
2465
|
return {
|
|
2717
2466
|
manifest,
|
|
2718
2467
|
manifestJson,
|
|
2719
|
-
globalCss: manifest
|
|
2720
|
-
componentCss: applyUnitFallback(manifest
|
|
2468
|
+
globalCss: manifest.sheets.global,
|
|
2469
|
+
componentCss: applyUnitFallback(manifest.css),
|
|
2721
2470
|
inputs,
|
|
2722
2471
|
timings: {
|
|
2723
2472
|
serializeMs,
|
|
@@ -2727,9 +2476,8 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2727
2476
|
};
|
|
2728
2477
|
}
|
|
2729
2478
|
/**
|
|
2730
|
-
* Clear the engine's per-file analysis cache so stale results
|
|
2731
|
-
*
|
|
2732
|
-
* capability (older builds) — the probe is benign.
|
|
2479
|
+
* Clear the engine's per-file analysis cache so stale results never bleed
|
|
2480
|
+
* into a fresh run. Engines without the capability are tolerated.
|
|
2733
2481
|
*/
|
|
2734
2482
|
function clearEngineCache(engineApi) {
|
|
2735
2483
|
try {
|
|
@@ -2738,15 +2486,32 @@ function clearEngineCache(engineApi) {
|
|
|
2738
2486
|
} catch {}
|
|
2739
2487
|
}
|
|
2740
2488
|
//#endregion
|
|
2741
|
-
//#region pipeline/
|
|
2742
|
-
|
|
2743
|
-
|
|
2489
|
+
//#region pipeline/utils.ts
|
|
2490
|
+
function camelToKebab(str) {
|
|
2491
|
+
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
2492
|
+
}
|
|
2744
2493
|
function stableStringify(value) {
|
|
2745
2494
|
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
2746
|
-
if (value
|
|
2495
|
+
if (isKeyedReference(value)) return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
|
|
2747
2496
|
return JSON.stringify(value);
|
|
2748
2497
|
}
|
|
2749
|
-
/**
|
|
2498
|
+
/** Decided by an intrinsic only functions accept, so a spoofed
|
|
2499
|
+
* `Symbol.toStringTag` cannot pass a plain object off as a function. */
|
|
2500
|
+
function isCallable(value) {
|
|
2501
|
+
try {
|
|
2502
|
+
Function.prototype.toString.call(value);
|
|
2503
|
+
return true;
|
|
2504
|
+
} catch {
|
|
2505
|
+
return false;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
function isKeyedReference(value) {
|
|
2509
|
+
return Object(value) === value && !isCallable(value);
|
|
2510
|
+
}
|
|
2511
|
+
//#endregion
|
|
2512
|
+
//#region pipeline/static-css.ts
|
|
2513
|
+
/** The `staticCss` option declares emission the JSX scanner cannot observe
|
|
2514
|
+
* (config-driven variants, spread props); the engine applies it as usage. */
|
|
2750
2515
|
function serializeStaticCss(config) {
|
|
2751
2516
|
if (!config) return null;
|
|
2752
2517
|
const json = stableStringify(config);
|
|
@@ -2754,14 +2519,6 @@ function serializeStaticCss(config) {
|
|
|
2754
2519
|
}
|
|
2755
2520
|
//#endregion
|
|
2756
2521
|
//#region pipeline/prefix.ts
|
|
2757
|
-
/**
|
|
2758
|
-
* Apply namespace prefix to a variable map, CSS variable declarations, and theme JSON.
|
|
2759
|
-
*
|
|
2760
|
-
* Variable map: `{ "colors.ember": "--color-ember" }` -> `{ "colors.ember": "--prefix-color-ember" }`
|
|
2761
|
-
* Variable CSS: `--color-ember: #FF2800` -> `--prefix-color-ember: #FF2800`
|
|
2762
|
-
* `var(--color-ember)` -> `var(--prefix-color-ember)`
|
|
2763
|
-
* Theme JSON: `{ "colors.ember": "var(--color-ember)" }` -> `{ "colors.ember": "var(--prefix-color-ember)" }`
|
|
2764
|
-
*/
|
|
2765
2522
|
function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson) {
|
|
2766
2523
|
if (!prefix) return {
|
|
2767
2524
|
variableMapJson,
|
|
@@ -2792,14 +2549,8 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
|
|
|
2792
2549
|
//#endregion
|
|
2793
2550
|
//#region pipeline/system-config.ts
|
|
2794
2551
|
/**
|
|
2795
|
-
* Load a SystemInstance
|
|
2796
|
-
*
|
|
2797
|
-
* variable map/css (and theme + contextual vars when affected) are
|
|
2798
|
-
* namespaced via the shared `applyPrefix`.
|
|
2799
|
-
*
|
|
2800
|
-
* Error handling stays at the call site — the Vite plugin warns (or throws
|
|
2801
|
-
* in strict mode) and keeps its previous config, the Next plugin lets the
|
|
2802
|
-
* failure propagate.
|
|
2552
|
+
* Load and normalize a SystemInstance; `prefix` namespaces every CSS
|
|
2553
|
+
* variable name. Error handling stays at the call site.
|
|
2803
2554
|
*/
|
|
2804
2555
|
function loadSystemConfig(engineApi, opts) {
|
|
2805
2556
|
const { loadSystemModule } = engineApi();
|
|
@@ -2827,23 +2578,13 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
2827
2578
|
transformSourcesJson: config.transformSources || null,
|
|
2828
2579
|
globalStyleBlocksJson: config.globalStyleBlocks || null,
|
|
2829
2580
|
keyframesJson: config.keyframesBlocks || null,
|
|
2581
|
+
vocabularyWitnessesJson: config.vocabularyWitnesses || null,
|
|
2830
2582
|
dependencies: config.dependencies ?? [],
|
|
2831
2583
|
sourceThemeManifestsJson: config.sourceThemeManifests || null
|
|
2832
2584
|
};
|
|
2833
2585
|
}
|
|
2834
2586
|
//#endregion
|
|
2835
2587
|
//#region pipeline/structural-self-check.ts
|
|
2836
|
-
/**
|
|
2837
|
-
* Structural self-check over an emitted extraction result — the affirmative
|
|
2838
|
-
* post-condition against silent-empty output (openspec:
|
|
2839
|
-
* standalone-extraction-cli, "Silent-empty success is impossible").
|
|
2840
|
-
*
|
|
2841
|
-
* One shared implementation for every driver: the Vite plugin's `verify`
|
|
2842
|
-
* option and the standalone CLI (default-on) both call this; the caller
|
|
2843
|
-
* owns the failure POLICY (warn vs throw vs exit code), never the checks.
|
|
2844
|
-
*/
|
|
2845
|
-
/** Run every structural check; returns human-readable failure lines
|
|
2846
|
-
* (empty = pass). */
|
|
2847
2588
|
function runStructuralSelfCheck(input) {
|
|
2848
2589
|
const failures = [];
|
|
2849
2590
|
if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
@@ -2885,33 +2626,12 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
2885
2626
|
}
|
|
2886
2627
|
//#endregion
|
|
2887
2628
|
//#region pipeline/system-props-module.ts
|
|
2888
|
-
/**
|
|
2889
|
-
* Build the source of the runtime system-props module — served as
|
|
2890
|
-
* `virtual:animus/system-props` by the Vite plugin and written as the
|
|
2891
|
-
* session-scoped `system-props.js` artifact by the extraction session
|
|
2892
|
-
* (`@animus-ui/extract/session`). One generator so the runtimes can never
|
|
2893
|
-
* drift.
|
|
2894
|
-
*
|
|
2895
|
-
* Runtime transform functions for dynamic props are not yet supported
|
|
2896
|
-
* (transforms are resolved at extraction time via boa_engine in Rust), so
|
|
2897
|
-
* `transforms` defaults to an empty object literal.
|
|
2898
|
-
*
|
|
2899
|
-
* `dynamicPropConfig` entries carry the CSS property the slot class declares
|
|
2900
|
-
* — the runtime's unit-fallback decision. Their field order and omission
|
|
2901
|
-
* rules belong to buildDynamicPropConfig.
|
|
2902
|
-
*/
|
|
2903
2629
|
function buildSystemPropsModule(opts) {
|
|
2904
2630
|
const dynamicPropConfig = buildDynamicPropConfig(opts.dynamicProps);
|
|
2905
2631
|
return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${opts.transformsSource ?? "{}"};\n`;
|
|
2906
2632
|
}
|
|
2907
2633
|
//#endregion
|
|
2908
2634
|
//#region pipeline/timing-waterfall.ts
|
|
2909
|
-
/**
|
|
2910
|
-
* Rust-side timing phases as reported in `manifest.timing`, in waterfall
|
|
2911
|
-
* order, with their display labels. Mirrors the timing struct in the
|
|
2912
|
-
* extract-v2 crate — a renamed or added Rust phase is updated here once,
|
|
2913
|
-
* not per plugin.
|
|
2914
|
-
*/
|
|
2915
2635
|
const RUST_TIMING_PHASES = [
|
|
2916
2636
|
["parseAndWalk", "parse+walk"],
|
|
2917
2637
|
["importResolution", "imports"],
|
|
@@ -2925,11 +2645,6 @@ const RUST_TIMING_PHASES = [
|
|
|
2925
2645
|
["cssGeneration", "css-gen"],
|
|
2926
2646
|
["manifestSerialization", "serialize"]
|
|
2927
2647
|
];
|
|
2928
|
-
/**
|
|
2929
|
-
* Format the Rust timing waterfall as log lines. `indent` and `labelWidth`
|
|
2930
|
-
* let each plugin keep its established column layout; the phase table and
|
|
2931
|
-
* the `(N files, M cached)` suffix are shared.
|
|
2932
|
-
*/
|
|
2933
2648
|
function formatRustTimingWaterfall(timing, opts) {
|
|
2934
2649
|
const lines = [];
|
|
2935
2650
|
for (const [key, label] of RUST_TIMING_PHASES) {
|
|
@@ -2943,21 +2658,9 @@ function formatRustTimingWaterfall(timing, opts) {
|
|
|
2943
2658
|
//#endregion
|
|
2944
2659
|
//#region pipeline/tsconfig-paths.ts
|
|
2945
2660
|
/**
|
|
2946
|
-
* tsconfig `paths` → alias pairs for
|
|
2947
|
-
*
|
|
2948
|
-
* from a live bundler config — Turbopack exposes none.
|
|
2949
|
-
*
|
|
2950
|
-
* Semantics (deliberately the TypeScript subset that matters for import
|
|
2951
|
-
* provenance): JSONC tolerated (comments + trailing commas); `extends`
|
|
2952
|
-
* chains followed (string or array; missing parents and cycles skipped);
|
|
2953
|
-
* the NEAREST config declaring `paths` wins wholesale (TS replaces the
|
|
2954
|
-
* object, never merges); targets resolve against the nearest `baseUrl`
|
|
2955
|
-
* (resolved from its declaring config's directory) or, absent one, the
|
|
2956
|
-
* directory of the config declaring `paths`; first target per pattern.
|
|
2957
|
-
* `x/*` patterns become prefix pairs, non-wildcard patterns exact pairs,
|
|
2958
|
-
* bare `*` catch-alls and mid-pattern wildcards are skipped.
|
|
2661
|
+
* tsconfig `paths` → alias pairs, for drivers with no live bundler config.
|
|
2662
|
+
* The nearest config declaring `paths` wins wholesale — TS never merges.
|
|
2959
2663
|
*/
|
|
2960
|
-
/** String-aware JSONC → JSON: strips // and block comments + trailing commas. */
|
|
2961
2664
|
function stripJsonc(text) {
|
|
2962
2665
|
let out = "";
|
|
2963
2666
|
let inString = false;
|
|
@@ -3007,6 +2710,35 @@ function stripJsonc(text) {
|
|
|
3007
2710
|
}
|
|
3008
2711
|
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
3009
2712
|
}
|
|
2713
|
+
/** `Object(value) === value` holds for exactly the blocks and lists
|
|
2714
|
+
* `JSON.parse` produces; the `[object Object]` tag separates the two. */
|
|
2715
|
+
function isJsonBlock(value) {
|
|
2716
|
+
return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
|
|
2717
|
+
}
|
|
2718
|
+
/** A block or a list. A declared `paths` OWNS the setting and blocks its
|
|
2719
|
+
* parents' even when it contributes nothing usable. */
|
|
2720
|
+
function isJsonKeyed(value) {
|
|
2721
|
+
return Object(value) === value;
|
|
2722
|
+
}
|
|
2723
|
+
/** A JSON string, excluding the boxed `String` object, which no path join
|
|
2724
|
+
* accepts. */
|
|
2725
|
+
function isJsonString(value) {
|
|
2726
|
+
return Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
|
|
2727
|
+
}
|
|
2728
|
+
function decodePaths(value) {
|
|
2729
|
+
if (!isJsonKeyed(value)) return null;
|
|
2730
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
2731
|
+
for (const [pattern, targets] of Object.entries(value)) {
|
|
2732
|
+
const [first] = Array.isArray(targets) ? targets : [];
|
|
2733
|
+
if (isJsonString(first)) decoded.set(pattern, first);
|
|
2734
|
+
}
|
|
2735
|
+
return decoded;
|
|
2736
|
+
}
|
|
2737
|
+
/** TypeScript accepts one specifier or an array; a non-string member names
|
|
2738
|
+
* no config and is dropped here. */
|
|
2739
|
+
function decodeExtends(value) {
|
|
2740
|
+
return (Array.isArray(value) ? value : [value]).filter(isJsonString);
|
|
2741
|
+
}
|
|
3010
2742
|
function readConfig(path$2) {
|
|
3011
2743
|
let raw;
|
|
3012
2744
|
try {
|
|
@@ -3014,16 +2746,20 @@ function readConfig(path$2) {
|
|
|
3014
2746
|
} catch {
|
|
3015
2747
|
return null;
|
|
3016
2748
|
}
|
|
2749
|
+
let parsed;
|
|
3017
2750
|
try {
|
|
3018
|
-
|
|
3019
|
-
return {
|
|
3020
|
-
dir: (0, path.dirname)(path$2),
|
|
3021
|
-
compilerOptions: json.compilerOptions ?? {},
|
|
3022
|
-
extends: json.extends
|
|
3023
|
-
};
|
|
2751
|
+
parsed = JSON.parse(stripJsonc(raw));
|
|
3024
2752
|
} catch {
|
|
3025
2753
|
return null;
|
|
3026
2754
|
}
|
|
2755
|
+
const root = isJsonBlock(parsed) ? parsed : {};
|
|
2756
|
+
const compilerOptions = isJsonBlock(root.compilerOptions) ? root.compilerOptions : {};
|
|
2757
|
+
return {
|
|
2758
|
+
dir: (0, path.dirname)(path$2),
|
|
2759
|
+
baseUrl: isJsonString(compilerOptions.baseUrl) ? compilerOptions.baseUrl : null,
|
|
2760
|
+
paths: decodePaths(compilerOptions.paths),
|
|
2761
|
+
extends: decodeExtends(root.extends)
|
|
2762
|
+
};
|
|
3027
2763
|
}
|
|
3028
2764
|
function resolveExtendsTarget(specifier, fromDir) {
|
|
3029
2765
|
if (specifier.startsWith(".") || (0, path.isAbsolute)(specifier)) {
|
|
@@ -3051,15 +2787,14 @@ function loadChain(entryPath) {
|
|
|
3051
2787
|
const visited = /* @__PURE__ */ new Set();
|
|
3052
2788
|
const queue = [entryPath];
|
|
3053
2789
|
while (queue.length > 0) {
|
|
3054
|
-
const
|
|
2790
|
+
const path$3 = queue.shift();
|
|
2791
|
+
const key = (0, path.resolve)(path$3);
|
|
3055
2792
|
if (visited.has(key)) continue;
|
|
3056
2793
|
visited.add(key);
|
|
3057
2794
|
const node = readConfig(key);
|
|
3058
2795
|
if (!node) continue;
|
|
3059
2796
|
chain.push(node);
|
|
3060
|
-
const
|
|
3061
|
-
for (const parent of parents) {
|
|
3062
|
-
if (typeof parent !== "string") continue;
|
|
2797
|
+
for (const parent of node.extends) {
|
|
3063
2798
|
const resolved = resolveExtendsTarget(parent, node.dir);
|
|
3064
2799
|
if (resolved) queue.push(resolved);
|
|
3065
2800
|
}
|
|
@@ -3067,22 +2802,20 @@ function loadChain(entryPath) {
|
|
|
3067
2802
|
return chain;
|
|
3068
2803
|
}
|
|
3069
2804
|
/**
|
|
3070
|
-
*
|
|
3071
|
-
*
|
|
3072
|
-
* (or no usable `paths`) exists.
|
|
2805
|
+
* The project's tsconfig path aliases as `buildPathAliasesJson` pairs; empty
|
|
2806
|
+
* when no readable tsconfig, or no usable `paths`, exists.
|
|
3073
2807
|
*/
|
|
3074
2808
|
function readTsconfigAliasPairs(rootDir) {
|
|
3075
2809
|
const chain = loadChain((0, path.join)(rootDir, "tsconfig.json"));
|
|
3076
2810
|
if (chain.length === 0) return [];
|
|
3077
|
-
const pathsOwner = chain.find((node) => node.
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
const baseOwner = chain.find((node) =>
|
|
3081
|
-
const
|
|
2811
|
+
const pathsOwner = chain.find((node) => node.paths !== null);
|
|
2812
|
+
const paths = pathsOwner?.paths ?? null;
|
|
2813
|
+
if (pathsOwner === void 0 || paths === null) return [];
|
|
2814
|
+
const baseOwner = chain.find((node) => node.baseUrl !== null);
|
|
2815
|
+
const baseUrl = baseOwner?.baseUrl ?? null;
|
|
2816
|
+
const base = baseOwner !== void 0 && baseUrl !== null ? (0, path.resolve)(baseOwner.dir, baseUrl) : pathsOwner.dir;
|
|
3082
2817
|
const pairs = [];
|
|
3083
|
-
for (const [pattern,
|
|
3084
|
-
const target = Array.isArray(targets) ? targets[0] : void 0;
|
|
3085
|
-
if (typeof target !== "string") continue;
|
|
2818
|
+
for (const [pattern, target] of paths) {
|
|
3086
2819
|
const patternStars = pattern.split("*").length - 1;
|
|
3087
2820
|
const targetStars = target.split("*").length - 1;
|
|
3088
2821
|
if (pattern === "*" || patternStars > 1 || targetStars > 1) continue;
|
|
@@ -3106,14 +2839,6 @@ function readTsconfigAliasPairs(rootDir) {
|
|
|
3106
2839
|
}
|
|
3107
2840
|
//#endregion
|
|
3108
2841
|
//#region pipeline/error-diagnostics.ts
|
|
3109
|
-
/**
|
|
3110
|
-
* Throw when the diagnostics carry any `kind: "error"` entry — one
|
|
3111
|
-
* aggregated `Error` naming EVERY offender (component, file, message — one
|
|
3112
|
-
* line per entry, each prefixed `[animus]`). Warning-only diagnostics
|
|
3113
|
-
* (`bail`/`skip`/`warn`, any severity) never trip this gate; their routing
|
|
3114
|
-
* stays with `surfaceManifestDiagnostics`. Callers accept the manifest only
|
|
3115
|
-
* past this point, so no stylesheet from a failing analysis is published.
|
|
3116
|
-
*/
|
|
3117
2842
|
function assertNoErrorDiagnostics(diagnostics) {
|
|
3118
2843
|
const errors = (diagnostics ?? []).filter((diagnostic) => diagnostic.kind === "error");
|
|
3119
2844
|
if (errors.length === 0) return;
|
|
@@ -3123,33 +2848,29 @@ function assertNoErrorDiagnostics(diagnostics) {
|
|
|
3123
2848
|
//#endregion
|
|
3124
2849
|
//#region pipeline/replacement-plans.ts
|
|
3125
2850
|
const snapshotByManifest = /* @__PURE__ */ new WeakMap();
|
|
2851
|
+
/** `null` (a context before its first analysis) snapshots to the empty plan
|
|
2852
|
+
* set — the same state as an empty universe. */
|
|
3126
2853
|
function snapshotFilePlans(manifest) {
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
if (cached) return cached;
|
|
3131
|
-
}
|
|
2854
|
+
if (manifest === null) return /* @__PURE__ */ new Map();
|
|
2855
|
+
const cached = snapshotByManifest.get(manifest);
|
|
2856
|
+
if (cached) return cached;
|
|
3132
2857
|
const entriesByFile = /* @__PURE__ */ new Map();
|
|
3133
|
-
for (const [id, desc] of Object.entries(manifest
|
|
3134
|
-
const
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
list.push([id, desc.replacement ?? ""]);
|
|
3138
|
-
entriesByFile.set(file, list);
|
|
2858
|
+
for (const [id, desc] of Object.entries(manifest.components)) {
|
|
2859
|
+
const list = entriesByFile.get(desc.file) ?? [];
|
|
2860
|
+
list.push([id, desc.replacement]);
|
|
2861
|
+
entriesByFile.set(desc.file, list);
|
|
3139
2862
|
}
|
|
3140
2863
|
const snapshot = /* @__PURE__ */ new Map();
|
|
3141
2864
|
for (const [file, entries] of entriesByFile) {
|
|
3142
2865
|
entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
3143
2866
|
snapshot.set(file, JSON.stringify(entries));
|
|
3144
2867
|
}
|
|
3145
|
-
|
|
2868
|
+
snapshotByManifest.set(manifest, snapshot);
|
|
3146
2869
|
return snapshot;
|
|
3147
2870
|
}
|
|
3148
2871
|
/**
|
|
3149
|
-
*
|
|
3150
|
-
*
|
|
3151
|
-
* `exclude` drops one path (the changed file itself on the edit path — its
|
|
3152
|
-
* own modules are already in the update set).
|
|
2872
|
+
* Paths whose plan changed: absent↔present, membership, or content.
|
|
2873
|
+
* `exclude` drops one path whose own modules are already being updated.
|
|
3153
2874
|
*/
|
|
3154
2875
|
function diffFilePlans(prev, next, opts) {
|
|
3155
2876
|
const changed = /* @__PURE__ */ new Set();
|
|
@@ -3158,33 +2879,15 @@ function diffFilePlans(prev, next, opts) {
|
|
|
3158
2879
|
if (opts?.exclude) changed.delete(opts.exclude);
|
|
3159
2880
|
return [...changed].sort();
|
|
3160
2881
|
}
|
|
3161
|
-
/** Domain prefix keeping
|
|
3162
|
-
*
|
|
2882
|
+
/** Domain prefix keeping this hash disjoint from every other `contentHash`
|
|
2883
|
+
* use in the pipeline. */
|
|
3163
2884
|
const REPLACEMENT_PLANS_DOMAIN = "animus-replacement-plans-v1\0";
|
|
3164
|
-
/**
|
|
3165
|
-
*
|
|
3166
|
-
* so `\0\0` is unreachable from entry serialization alone — a snapshot can
|
|
3167
|
-
* never forge the witness section. */
|
|
2885
|
+
/** Leads with NUL: entries end with `\0` and contain none, so `\0\0` is
|
|
2886
|
+
* unreachable from entries alone and the section cannot be forged. */
|
|
3168
2887
|
const SERVED_DEPENDENCY_DOMAIN = "\0animus-served-dependency-v1\0";
|
|
3169
2888
|
/**
|
|
3170
|
-
*
|
|
3171
|
-
*
|
|
3172
|
-
* `file + '\0' + plan + '\0'` (unambiguous: neither file paths nor the
|
|
3173
|
-
* JSON plan strings contain NUL). Byte-identical plans → identical epoch;
|
|
3174
|
-
* any membership/replacement/absent↔present change moves it.
|
|
3175
|
-
*
|
|
3176
|
-
* `servedDependencyWitness` extends the epoch to content the plans cannot
|
|
3177
|
-
* see but restored modules depend on: transformed modules import the
|
|
3178
|
-
* session's served system-props artifact, so a consumer passes that
|
|
3179
|
-
* artifact's content here and an offline change to it moves the epoch —
|
|
3180
|
-
* invalidating persistent-cache snapshots that reference the old epoch
|
|
3181
|
-
* artifact. Absence and empty-string are distinct values.
|
|
3182
|
-
*
|
|
3183
|
-
* This is THE epoch definition — integrations consume this hash (or the
|
|
3184
|
-
* snapshot diff) and never define their own. Epoch values compare only for
|
|
3185
|
-
* equality (a moved value invalidates), so the serialization is free to
|
|
3186
|
-
* evolve with a domain bump: sessions running different serializations
|
|
3187
|
-
* simply disagree once and rebuild.
|
|
2889
|
+
* THE replacement epoch: integrations consume this hash, never their own.
|
|
2890
|
+
* `servedDependencyWitness` folds in content the plans cannot see.
|
|
3188
2891
|
*/
|
|
3189
2892
|
function hashReplacementPlans(snapshot, servedDependencyWitness) {
|
|
3190
2893
|
const entries = [...snapshot.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
@@ -3194,40 +2897,14 @@ function hashReplacementPlans(snapshot, servedDependencyWitness) {
|
|
|
3194
2897
|
return contentHash(serialized);
|
|
3195
2898
|
}
|
|
3196
2899
|
//#endregion
|
|
3197
|
-
//#region pipeline/utils.ts
|
|
3198
|
-
/** Convert camelCase CSS property names to kebab-case. */
|
|
3199
|
-
function camelToKebab(str) {
|
|
3200
|
-
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
3201
|
-
}
|
|
3202
|
-
//#endregion
|
|
3203
2900
|
//#region pipeline/watch-keys.ts
|
|
3204
|
-
/**
|
|
3205
|
-
* Membership keys for a filesystem path in the system-dependency watch set.
|
|
3206
|
-
*
|
|
3207
|
-
* The loader reports symlink-resolved canonical paths; watcher events carry
|
|
3208
|
-
* lexical absolute paths that may traverse symlinks (bun workspace links) and
|
|
3209
|
-
* — for deletions — may no longer exist, so `realpathSync` on the event path
|
|
3210
|
-
* alone cannot produce the canonical form. Both insertion and lookup go
|
|
3211
|
-
* through this one normalizer: the lexical form plus a canonical form
|
|
3212
|
-
* reconstructed through the nearest existing ancestor.
|
|
3213
|
-
*/
|
|
3214
2901
|
function toWatchKeys(input) {
|
|
3215
2902
|
const lexical = (0, node_path.normalize)(input);
|
|
3216
2903
|
const canonical = realpathThroughNearestExistingAncestor(lexical);
|
|
3217
2904
|
return canonical === lexical ? [lexical] : [lexical, canonical];
|
|
3218
2905
|
}
|
|
3219
|
-
/**
|
|
3220
|
-
*
|
|
3221
|
-
* ancestor, realpath it, and re-append the missing suffix. Handles deleted
|
|
3222
|
-
* files under still-existing (possibly symlinked) directories and atomic
|
|
3223
|
-
* unlink/rename saves. Falls back to the input when no ancestor resolves.
|
|
3224
|
-
*
|
|
3225
|
-
* Known limit: a symlink retargeted AFTER the system load canonicalizes to
|
|
3226
|
-
* the new target, which cannot match the recorded (old-target) dependency —
|
|
3227
|
-
* the membership test then fails open (no reset), identical to not having
|
|
3228
|
-
* this reconstruction at all. Retarget sensitivity is a resolution-input
|
|
3229
|
-
* concern, deliberately out of scope for the watch set.
|
|
3230
|
-
*/
|
|
2906
|
+
/** Canonicalizes a path that may not exist, through its nearest existing
|
|
2907
|
+
* ancestor. A symlink retargeted after load fails open: no match. */
|
|
3231
2908
|
function realpathThroughNearestExistingAncestor(path) {
|
|
3232
2909
|
try {
|
|
3233
2910
|
return (0, node_fs.realpathSync)(path);
|
|
@@ -3287,18 +2964,6 @@ Object.defineProperty(exports, "ENGINE_TRANSFORM_EXTENSIONS", {
|
|
|
3287
2964
|
return ENGINE_TRANSFORM_EXTENSIONS;
|
|
3288
2965
|
}
|
|
3289
2966
|
});
|
|
3290
|
-
Object.defineProperty(exports, "KEYFRAMES_EXPORT_COLLISION", {
|
|
3291
|
-
enumerable: true,
|
|
3292
|
-
get: function() {
|
|
3293
|
-
return KEYFRAMES_EXPORT_COLLISION;
|
|
3294
|
-
}
|
|
3295
|
-
});
|
|
3296
|
-
Object.defineProperty(exports, "KEYFRAMES_EXTERNAL_ENTRY_FAILED", {
|
|
3297
|
-
enumerable: true,
|
|
3298
|
-
get: function() {
|
|
3299
|
-
return KEYFRAMES_EXTERNAL_ENTRY_FAILED;
|
|
3300
|
-
}
|
|
3301
|
-
});
|
|
3302
2967
|
Object.defineProperty(exports, "REPLACEABLE_DEFAULT_EXCLUDE", {
|
|
3303
2968
|
enumerable: true,
|
|
3304
2969
|
get: function() {
|
|
@@ -3317,10 +2982,16 @@ Object.defineProperty(exports, "STRUCTURAL_EXCLUDE", {
|
|
|
3317
2982
|
return STRUCTURAL_EXCLUDE;
|
|
3318
2983
|
}
|
|
3319
2984
|
});
|
|
3320
|
-
Object.defineProperty(exports, "
|
|
2985
|
+
Object.defineProperty(exports, "VOCABULARY_COLLISION", {
|
|
3321
2986
|
enumerable: true,
|
|
3322
2987
|
get: function() {
|
|
3323
|
-
return
|
|
2988
|
+
return VOCABULARY_COLLISION;
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
Object.defineProperty(exports, "VOCABULARY_LEGACY_VERB", {
|
|
2992
|
+
enumerable: true,
|
|
2993
|
+
get: function() {
|
|
2994
|
+
return VOCABULARY_LEGACY_VERB;
|
|
3324
2995
|
}
|
|
3325
2996
|
});
|
|
3326
2997
|
Object.defineProperty(exports, "adaptSvelteSource", {
|
|
@@ -3413,6 +3084,12 @@ Object.defineProperty(exports, "collectExternalPackageSources", {
|
|
|
3413
3084
|
return collectExternalPackageSources;
|
|
3414
3085
|
}
|
|
3415
3086
|
});
|
|
3087
|
+
Object.defineProperty(exports, "compareDiscoveryOrder", {
|
|
3088
|
+
enumerable: true,
|
|
3089
|
+
get: function() {
|
|
3090
|
+
return compareDiscoveryOrder;
|
|
3091
|
+
}
|
|
3092
|
+
});
|
|
3416
3093
|
Object.defineProperty(exports, "contentHash", {
|
|
3417
3094
|
enumerable: true,
|
|
3418
3095
|
get: function() {
|
|
@@ -3425,16 +3102,16 @@ Object.defineProperty(exports, "createExcludeMatcher", {
|
|
|
3425
3102
|
return createExcludeMatcher;
|
|
3426
3103
|
}
|
|
3427
3104
|
});
|
|
3428
|
-
Object.defineProperty(exports, "
|
|
3105
|
+
Object.defineProperty(exports, "createSourceCorpus", {
|
|
3429
3106
|
enumerable: true,
|
|
3430
3107
|
get: function() {
|
|
3431
|
-
return
|
|
3108
|
+
return createSourceCorpus;
|
|
3432
3109
|
}
|
|
3433
3110
|
});
|
|
3434
|
-
Object.defineProperty(exports, "
|
|
3111
|
+
Object.defineProperty(exports, "createSourceIdentity", {
|
|
3435
3112
|
enumerable: true,
|
|
3436
3113
|
get: function() {
|
|
3437
|
-
return
|
|
3114
|
+
return createSourceIdentity;
|
|
3438
3115
|
}
|
|
3439
3116
|
});
|
|
3440
3117
|
Object.defineProperty(exports, "createV2EngineApi", {
|
|
@@ -3515,6 +3192,12 @@ Object.defineProperty(exports, "isAdvisorySourceDiagnostic", {
|
|
|
3515
3192
|
return isAdvisorySourceDiagnostic;
|
|
3516
3193
|
}
|
|
3517
3194
|
});
|
|
3195
|
+
Object.defineProperty(exports, "isEngineTransformExtension", {
|
|
3196
|
+
enumerable: true,
|
|
3197
|
+
get: function() {
|
|
3198
|
+
return isEngineTransformExtension;
|
|
3199
|
+
}
|
|
3200
|
+
});
|
|
3518
3201
|
Object.defineProperty(exports, "isExcludedPackageRelativePath", {
|
|
3519
3202
|
enumerable: true,
|
|
3520
3203
|
get: function() {
|
|
@@ -3539,10 +3222,10 @@ Object.defineProperty(exports, "loadSystemConfig", {
|
|
|
3539
3222
|
return loadSystemConfig;
|
|
3540
3223
|
}
|
|
3541
3224
|
});
|
|
3542
|
-
Object.defineProperty(exports, "
|
|
3225
|
+
Object.defineProperty(exports, "parseFilesJson", {
|
|
3543
3226
|
enumerable: true,
|
|
3544
3227
|
get: function() {
|
|
3545
|
-
return
|
|
3228
|
+
return parseFilesJson;
|
|
3546
3229
|
}
|
|
3547
3230
|
});
|
|
3548
3231
|
Object.defineProperty(exports, "postProcessCss", {
|
|
@@ -3557,6 +3240,12 @@ Object.defineProperty(exports, "preprocessMdx", {
|
|
|
3557
3240
|
return preprocessMdx;
|
|
3558
3241
|
}
|
|
3559
3242
|
});
|
|
3243
|
+
Object.defineProperty(exports, "projectExternalFileOwners", {
|
|
3244
|
+
enumerable: true,
|
|
3245
|
+
get: function() {
|
|
3246
|
+
return projectExternalFileOwners;
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3560
3249
|
Object.defineProperty(exports, "readTsconfigAliasPairs", {
|
|
3561
3250
|
enumerable: true,
|
|
3562
3251
|
get: function() {
|
|
@@ -3623,6 +3312,12 @@ Object.defineProperty(exports, "snapshotFilePlans", {
|
|
|
3623
3312
|
return snapshotFilePlans;
|
|
3624
3313
|
}
|
|
3625
3314
|
});
|
|
3315
|
+
Object.defineProperty(exports, "stableStringify", {
|
|
3316
|
+
enumerable: true,
|
|
3317
|
+
get: function() {
|
|
3318
|
+
return stableStringify;
|
|
3319
|
+
}
|
|
3320
|
+
});
|
|
3626
3321
|
Object.defineProperty(exports, "staleDistIncludesMessage", {
|
|
3627
3322
|
enumerable: true,
|
|
3628
3323
|
get: function() {
|
|
@@ -3653,6 +3348,12 @@ Object.defineProperty(exports, "toWatchKeys", {
|
|
|
3653
3348
|
return toWatchKeys;
|
|
3654
3349
|
}
|
|
3655
3350
|
});
|
|
3351
|
+
Object.defineProperty(exports, "unreadableSourceDiagnostic", {
|
|
3352
|
+
enumerable: true,
|
|
3353
|
+
get: function() {
|
|
3354
|
+
return unreadableSourceDiagnostic;
|
|
3355
|
+
}
|
|
3356
|
+
});
|
|
3656
3357
|
Object.defineProperty(exports, "unresolvableIncludesMessage", {
|
|
3657
3358
|
enumerable: true,
|
|
3658
3359
|
get: function() {
|
|
@@ -3671,15 +3372,15 @@ Object.defineProperty(exports, "validateLayerOrder", {
|
|
|
3671
3372
|
return validateLayerOrder;
|
|
3672
3373
|
}
|
|
3673
3374
|
});
|
|
3674
|
-
Object.defineProperty(exports, "
|
|
3375
|
+
Object.defineProperty(exports, "vocabularyWitnessDiagnostics", {
|
|
3675
3376
|
enumerable: true,
|
|
3676
3377
|
get: function() {
|
|
3677
|
-
return
|
|
3378
|
+
return vocabularyWitnessDiagnostics;
|
|
3678
3379
|
}
|
|
3679
3380
|
});
|
|
3680
|
-
Object.defineProperty(exports, "
|
|
3381
|
+
Object.defineProperty(exports, "walkPackageSources", {
|
|
3681
3382
|
enumerable: true,
|
|
3682
3383
|
get: function() {
|
|
3683
|
-
return
|
|
3384
|
+
return walkPackageSources;
|
|
3684
3385
|
}
|
|
3685
3386
|
});
|