@bamboocss/parser 1.47.0 → 1.48.0
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/dist/index.cjs +60 -16
- package/dist/index.d.cts +54 -23
- package/dist/index.d.mts +54 -23
- package/dist/index.mjs +60 -16
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -1691,6 +1691,24 @@ var Project = class {
|
|
|
1691
1691
|
sourcePreparations = /* @__PURE__ */ new Map();
|
|
1692
1692
|
/** Paths explicitly removed through this wrapper, until an add/create observes them again. */
|
|
1693
1693
|
removedSourcePaths = /* @__PURE__ */ new Set();
|
|
1694
|
+
/**
|
|
1695
|
+
* Sources a bundler transform owns, rather than members of the checkout.
|
|
1696
|
+
*
|
|
1697
|
+
* A compiler which shares this Project has to park bundler-transformed text somewhere, and
|
|
1698
|
+
* it cannot be the file's own path — that is the canonical source every other reader
|
|
1699
|
+
* resolves through. It parses under a sibling path instead, and that parse resolves its
|
|
1700
|
+
* imports like any other, so the ledger gains an importer no watcher will ever report a
|
|
1701
|
+
* change for. Left in, those facts reach the incremental extraction pass: an auxiliary
|
|
1702
|
+
* importer would be selected as a dependent of the file it shadows and ordered against
|
|
1703
|
+
* inventory members that do not import it.
|
|
1704
|
+
*
|
|
1705
|
+
* Excluded from exactly the three queries that decide what a rebuild re-extracts —
|
|
1706
|
+
* the ledger, the dependent walk and the unresolved-importer set — and from nothing else.
|
|
1707
|
+
* Resolution itself is deliberately untouched: an auxiliary parse must still see the same
|
|
1708
|
+
* modules the real one would, and its forward edges are what a bundler registers as watch
|
|
1709
|
+
* files.
|
|
1710
|
+
*/
|
|
1711
|
+
auxiliarySources = /* @__PURE__ */ new Set();
|
|
1694
1712
|
/** File-tree changes invalidate even successful resolutions (extension precedence can move). */
|
|
1695
1713
|
#fileTreeRevision = 0;
|
|
1696
1714
|
resolutionWork = {
|
|
@@ -1709,6 +1727,7 @@ var Project = class {
|
|
|
1709
1727
|
this.resolutionsByImporter = /* @__PURE__ */ new Map();
|
|
1710
1728
|
this.sourcePreparations = /* @__PURE__ */ new Map();
|
|
1711
1729
|
this.removedSourcePaths = /* @__PURE__ */ new Set();
|
|
1730
|
+
this.auxiliarySources = /* @__PURE__ */ new Set();
|
|
1712
1731
|
this.resolutionWork = {
|
|
1713
1732
|
moduleResolutionsAttempted: 0,
|
|
1714
1733
|
sourceFilesAdded: 0,
|
|
@@ -1718,12 +1737,12 @@ var Project = class {
|
|
|
1718
1737
|
/** Files whose imports may resolve differently after a local file appears. */
|
|
1719
1738
|
getUnresolvedImporters = () => {
|
|
1720
1739
|
this.#assertNotLoading();
|
|
1721
|
-
return [...this.unresolvedImporters].sort();
|
|
1740
|
+
return [...this.unresolvedImporters].filter((importer) => !this.auxiliarySources.has(importer)).sort();
|
|
1722
1741
|
};
|
|
1723
1742
|
/** @internal Immutable resolution facts in importer/AST order. */
|
|
1724
1743
|
getResolutionLedger = () => {
|
|
1725
1744
|
this.#assertNotLoading();
|
|
1726
|
-
return Object.freeze([...this.resolutionsByImporter.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([, entry]) => entry.facts));
|
|
1745
|
+
return Object.freeze([...this.resolutionsByImporter.entries()].filter(([importer]) => !this.auxiliarySources.has(importer)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([, entry]) => entry.facts));
|
|
1727
1746
|
};
|
|
1728
1747
|
/** @internal Every distinct local target represented by the current ledger. */
|
|
1729
1748
|
getResolvedSourceFiles = () => {
|
|
@@ -2052,7 +2071,7 @@ var Project = class {
|
|
|
2052
2071
|
* downstream edges the ledger records. The transaction is published only after the AST and
|
|
2053
2072
|
* its facts agree. A failed or re-entered attempt restores the input AST and can be retried.
|
|
2054
2073
|
*/
|
|
2055
|
-
prepareEffectiveSource = (filePath, sourceFile) => {
|
|
2074
|
+
prepareEffectiveSource = (filePath, sourceFile, hookFilePath = filePath) => {
|
|
2056
2075
|
this.#assertNotLoading();
|
|
2057
2076
|
const sourcePath = this.normalizePath(sourceFile.getFilePath());
|
|
2058
2077
|
const currentText = sourceFile.getFullText();
|
|
@@ -2087,7 +2106,7 @@ var Project = class {
|
|
|
2087
2106
|
const options = {};
|
|
2088
2107
|
try {
|
|
2089
2108
|
const custom = this.options.hooks["parser:before"]?.({
|
|
2090
|
-
filePath,
|
|
2109
|
+
filePath: hookFilePath,
|
|
2091
2110
|
content: currentText,
|
|
2092
2111
|
configure(next) {
|
|
2093
2112
|
const { matchTag, matchTagMode, matchTagProp } = next;
|
|
@@ -2097,7 +2116,7 @@ var Project = class {
|
|
|
2097
2116
|
}
|
|
2098
2117
|
});
|
|
2099
2118
|
assertTransaction(currentText);
|
|
2100
|
-
const transformed = custom ?? this.transformFile(
|
|
2119
|
+
const transformed = custom ?? this.transformFile(hookFilePath, currentText);
|
|
2101
2120
|
assertTransaction(currentText);
|
|
2102
2121
|
if (currentText !== transformed) sourceFile.replaceWithText(transformed);
|
|
2103
2122
|
assertTransaction(transformed);
|
|
@@ -2161,6 +2180,7 @@ var Project = class {
|
|
|
2161
2180
|
const importers = new Set([...this.dependents.get(current) ?? [], ...this.removedDependents.get(current) ?? []]);
|
|
2162
2181
|
for (const importer of [...importers].sort()) {
|
|
2163
2182
|
if (importer === start || seen.has(importer)) continue;
|
|
2183
|
+
if (this.auxiliarySources.has(importer)) continue;
|
|
2164
2184
|
seen.add(importer);
|
|
2165
2185
|
queue.push(importer);
|
|
2166
2186
|
}
|
|
@@ -2298,7 +2318,7 @@ var Project = class {
|
|
|
2298
2318
|
if (error?.code !== "ENOENT") throw error;
|
|
2299
2319
|
}
|
|
2300
2320
|
};
|
|
2301
|
-
addSourceFile = (filePath, content) => {
|
|
2321
|
+
addSourceFile = (filePath, content, options = {}) => {
|
|
2302
2322
|
this.#assertNotLoading();
|
|
2303
2323
|
this.#ensureSourceFiles();
|
|
2304
2324
|
const existing = filePath.includes("/") ? this.project.getSourceFile(filePath) : void 0;
|
|
@@ -2324,14 +2344,25 @@ var Project = class {
|
|
|
2324
2344
|
* may have replaced this file's text through a `parser:before` hook. Such a file no longer
|
|
2325
2345
|
* matches its own source, falls through, and is overwritten exactly as before.
|
|
2326
2346
|
*/
|
|
2327
|
-
if (existing && existing.getFullText() === content)
|
|
2347
|
+
if (existing && existing.getFullText() === content) {
|
|
2348
|
+
this.markAuxiliary(existing.getFilePath(), options.auxiliary);
|
|
2349
|
+
return existing;
|
|
2350
|
+
}
|
|
2328
2351
|
this.invalidateSourcePreparation(filePath, existing);
|
|
2329
2352
|
this.removedSourcePaths.delete(this.normalizePath(filePath));
|
|
2330
2353
|
this.invalidate(!existing, existing?.getFilePath());
|
|
2331
|
-
|
|
2354
|
+
const sourceFile = this.project.createSourceFile(filePath, content, {
|
|
2332
2355
|
overwrite: true,
|
|
2333
2356
|
scriptKind: scriptKindFor(filePath)
|
|
2334
2357
|
});
|
|
2358
|
+
this.markAuxiliary(sourceFile.getFilePath(), options.auxiliary);
|
|
2359
|
+
return sourceFile;
|
|
2360
|
+
};
|
|
2361
|
+
/** Claim or release compiler ownership of one source, in the ledger's own spelling. */
|
|
2362
|
+
markAuxiliary = (filePath, auxiliary) => {
|
|
2363
|
+
const normalized = this.normalizePath(filePath);
|
|
2364
|
+
if (auxiliary) this.auxiliarySources.add(normalized);
|
|
2365
|
+
else this.auxiliarySources.delete(normalized);
|
|
2335
2366
|
};
|
|
2336
2367
|
removeSourceFile = (filePath) => {
|
|
2337
2368
|
this.#assertNotLoading();
|
|
@@ -2342,6 +2373,7 @@ var Project = class {
|
|
|
2342
2373
|
this.invalidateSourcePreparation(filePath, sourceFile);
|
|
2343
2374
|
this.markTargetRemoved(this.normalizePath(sourceFile.getFilePath()), sourceFile);
|
|
2344
2375
|
this.options.parserOptions.encoder.releaseFile(sourceFile.getFilePath());
|
|
2376
|
+
this.auxiliarySources.delete(this.normalizePath(sourceFile.getFilePath()));
|
|
2345
2377
|
return this.project.removeSourceFile(sourceFile);
|
|
2346
2378
|
}
|
|
2347
2379
|
return false;
|
|
@@ -2394,23 +2426,35 @@ var Project = class {
|
|
|
2394
2426
|
this.#assertNotLoading();
|
|
2395
2427
|
return this.options.getFiles;
|
|
2396
2428
|
}
|
|
2397
|
-
|
|
2429
|
+
/**
|
|
2430
|
+
* A dumped encoder is a parse result like any other, so it belongs to whichever encoder the
|
|
2431
|
+
* caller named.
|
|
2432
|
+
*
|
|
2433
|
+
* Restoring into `parserOptions.encoder` regardless is the one place a supplied encoder was
|
|
2434
|
+
* silently ignored, and it stops being a formality once a bundler transform and the
|
|
2435
|
+
* extraction pass share one Project: the transform's parses go to a private clone precisely
|
|
2436
|
+
* so they cannot add rules to the sheet, and a `.json` module in its graph would have
|
|
2437
|
+
* pinned an entire safelist into the emitted encoder from a pass that emits nothing.
|
|
2438
|
+
*/
|
|
2439
|
+
parseJson = (filePath, encoder) => {
|
|
2398
2440
|
this.#assertNotLoading();
|
|
2399
2441
|
const { readFile, parserOptions } = this.options;
|
|
2442
|
+
const target = encoder ?? parserOptions.encoder;
|
|
2400
2443
|
const content = readFile(filePath);
|
|
2401
|
-
|
|
2402
|
-
return new ParserResult(parserOptions).setFilePath(filePath);
|
|
2444
|
+
target.fromJSON(JSON.parse(content));
|
|
2445
|
+
return new ParserResult(parserOptions, encoder).setFilePath(filePath);
|
|
2403
2446
|
};
|
|
2404
|
-
parseSourceFile = (filePath, encoder) => {
|
|
2447
|
+
parseSourceFile = (filePath, encoder, options = {}) => {
|
|
2405
2448
|
this.#assertNotLoading();
|
|
2406
2449
|
const { hooks } = this.options;
|
|
2407
|
-
|
|
2450
|
+
const hookFilePath = options.hookFilePath ?? filePath;
|
|
2451
|
+
if (filePath.endsWith(".json")) return this.parseJson(filePath, encoder);
|
|
2408
2452
|
const sourceFile = this.project.getSourceFile(filePath);
|
|
2409
2453
|
if (!sourceFile) return;
|
|
2410
|
-
const { options } = this.prepareEffectiveSource(filePath, sourceFile);
|
|
2411
|
-
const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", sourceFile.getFilePath(), () => this.parser(sourceFile, encoder,
|
|
2454
|
+
const { options: parserOptions } = this.prepareEffectiveSource(filePath, sourceFile, hookFilePath);
|
|
2455
|
+
const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", sourceFile.getFilePath(), () => this.parser(sourceFile, encoder, parserOptions, this.resolveModule))?.setFilePath(filePath);
|
|
2412
2456
|
hooks["parser:after"]?.({
|
|
2413
|
-
filePath,
|
|
2457
|
+
filePath: hookFilePath,
|
|
2414
2458
|
result
|
|
2415
2459
|
});
|
|
2416
2460
|
return result;
|
package/dist/index.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ import { ResolveModule } from "@bamboocss/extractor";
|
|
|
6
6
|
//#region ../generator/dist/index.d.cts
|
|
7
7
|
//#region src/generator.d.ts
|
|
8
8
|
interface SplitCssArtifact {
|
|
9
|
-
type: 'layer' | '
|
|
9
|
+
type: 'layer' | 'theme';
|
|
10
10
|
name: string;
|
|
11
11
|
file: string;
|
|
12
12
|
code: string;
|
|
@@ -16,12 +16,8 @@ interface SplitCssArtifact {
|
|
|
16
16
|
interface SplitCssResult {
|
|
17
17
|
/** Layer CSS files (reset, global, tokens, utilities) */
|
|
18
18
|
layers: SplitCssArtifact[];
|
|
19
|
-
/** Recipe CSS files */
|
|
20
|
-
recipes: SplitCssArtifact[];
|
|
21
19
|
/** Theme CSS files (not auto-imported) */
|
|
22
20
|
themes: SplitCssArtifact[];
|
|
23
|
-
/** Content for recipes.css */
|
|
24
|
-
recipesIndex: string;
|
|
25
21
|
/** Content for main styles.css */
|
|
26
22
|
index: string;
|
|
27
23
|
}
|
|
@@ -30,7 +26,11 @@ declare class Generator extends Context {
|
|
|
30
26
|
getArtifacts: (ids?: ArtifactId[] | undefined) => import("@bamboocss/types").Artifact[];
|
|
31
27
|
appendCssOfType: (type: CssArtifactType, sheet: Stylesheet) => void;
|
|
32
28
|
appendLayerParams: (sheet: Stylesheet) => void;
|
|
33
|
-
appendBaselineCss: (sheet: Stylesheet
|
|
29
|
+
appendBaselineCss: (sheet: Stylesheet, {
|
|
30
|
+
atomizeRecipes
|
|
31
|
+
}?: {
|
|
32
|
+
atomizeRecipes?: boolean;
|
|
33
|
+
}) => void;
|
|
34
34
|
appendParserCss: (sheet: Stylesheet) => void;
|
|
35
35
|
/**
|
|
36
36
|
* Drop token css variables nothing can reach. Call this only once the sheet holds the
|
|
@@ -199,23 +199,11 @@ declare class Generator extends Context {
|
|
|
199
199
|
* Get CSS for a specific layer from the stylesheet
|
|
200
200
|
*/
|
|
201
201
|
getLayerCss: (sheet: Stylesheet, layer: "reset" | "base" | "tokens" | "recipes" | "utilities") => string;
|
|
202
|
-
/**
|
|
203
|
-
* Get CSS for a specific recipe
|
|
204
|
-
*/
|
|
205
|
-
getRecipeCss: (recipeName: string) => string;
|
|
206
|
-
/**
|
|
207
|
-
* Get all recipe names from the decoder
|
|
208
|
-
*/
|
|
209
|
-
getRecipeNames: () => string[];
|
|
210
202
|
/**
|
|
211
203
|
* Get all split CSS artifacts for the stylesheet
|
|
212
204
|
* Used when --splitting flag is enabled
|
|
213
205
|
*/
|
|
214
|
-
getSplitCssArtifacts: (sheet: Stylesheet
|
|
215
|
-
includeRecipes
|
|
216
|
-
}?: {
|
|
217
|
-
includeRecipes?: boolean;
|
|
218
|
-
}) => SplitCssResult;
|
|
206
|
+
getSplitCssArtifacts: (sheet: Stylesheet) => SplitCssResult;
|
|
219
207
|
getSpec: () => SpecFile[];
|
|
220
208
|
getSpecOfType: <T extends SpecType>(type: T) => T extends "color-palette" | "themes" ? SpecTypeMap[T] | undefined : SpecTypeMap[T];
|
|
221
209
|
} //#endregion
|
|
@@ -439,6 +427,19 @@ interface ResolutionFact {
|
|
|
439
427
|
readonly kind: 'import' | 'export';
|
|
440
428
|
readonly ordinal: number;
|
|
441
429
|
}
|
|
430
|
+
/** @internal How one explicitly supplied source relates to the checkout it is added to. */
|
|
431
|
+
interface AddSourceFileOptions {
|
|
432
|
+
/**
|
|
433
|
+
* This text belongs to a bundler transform rather than to the file at this path.
|
|
434
|
+
*
|
|
435
|
+
* @see `Project.auxiliarySources`
|
|
436
|
+
*/
|
|
437
|
+
auxiliary?: boolean;
|
|
438
|
+
}
|
|
439
|
+
/** @internal Logical identity for hooks when a bundler source needs a synthetic AST path. */
|
|
440
|
+
interface ParseSourceFileOptions {
|
|
441
|
+
hookFilePath?: string;
|
|
442
|
+
}
|
|
442
443
|
/** @internal Exact semantic closure plus missing local paths which can redirect it. */
|
|
443
444
|
interface ResolutionReadSet {
|
|
444
445
|
readonly dependencies: readonly string[];
|
|
@@ -508,6 +509,24 @@ declare class Project {
|
|
|
508
509
|
private sourcePreparations;
|
|
509
510
|
/** Paths explicitly removed through this wrapper, until an add/create observes them again. */
|
|
510
511
|
private removedSourcePaths;
|
|
512
|
+
/**
|
|
513
|
+
* Sources a bundler transform owns, rather than members of the checkout.
|
|
514
|
+
*
|
|
515
|
+
* A compiler which shares this Project has to park bundler-transformed text somewhere, and
|
|
516
|
+
* it cannot be the file's own path — that is the canonical source every other reader
|
|
517
|
+
* resolves through. It parses under a sibling path instead, and that parse resolves its
|
|
518
|
+
* imports like any other, so the ledger gains an importer no watcher will ever report a
|
|
519
|
+
* change for. Left in, those facts reach the incremental extraction pass: an auxiliary
|
|
520
|
+
* importer would be selected as a dependent of the file it shadows and ordered against
|
|
521
|
+
* inventory members that do not import it.
|
|
522
|
+
*
|
|
523
|
+
* Excluded from exactly the three queries that decide what a rebuild re-extracts —
|
|
524
|
+
* the ledger, the dependent walk and the unresolved-importer set — and from nothing else.
|
|
525
|
+
* Resolution itself is deliberately untouched: an auxiliary parse must still see the same
|
|
526
|
+
* modules the real one would, and its forward edges are what a bundler registers as watch
|
|
527
|
+
* files.
|
|
528
|
+
*/
|
|
529
|
+
private auxiliarySources;
|
|
511
530
|
private resolutionWork;
|
|
512
531
|
private resetResolutionState;
|
|
513
532
|
/** Files whose imports may resolve differently after a local file appears. */
|
|
@@ -600,7 +619,9 @@ declare class Project {
|
|
|
600
619
|
getResolutionConfigurationFiles: (filePath: string, targets?: Iterable<string>, previous?: Iterable<string>) => readonly string[];
|
|
601
620
|
createSourceFile: (filePath: string) => SourceFile;
|
|
602
621
|
createSourceFiles: () => void;
|
|
603
|
-
addSourceFile: (filePath: string, content: string) => SourceFile;
|
|
622
|
+
addSourceFile: (filePath: string, content: string, options?: AddSourceFileOptions) => SourceFile;
|
|
623
|
+
/** Claim or release compiler ownership of one source, in the ledger's own spelling. */
|
|
624
|
+
private markAuxiliary;
|
|
604
625
|
removeSourceFile: (filePath: string) => boolean;
|
|
605
626
|
/**
|
|
606
627
|
* The current digest of one exported value, for read verification.
|
|
@@ -613,10 +634,20 @@ declare class Project {
|
|
|
613
634
|
reloadSourceFiles: () => void;
|
|
614
635
|
get readFile(): (filePath: string) => string;
|
|
615
636
|
get getFiles(): () => string[];
|
|
616
|
-
|
|
617
|
-
|
|
637
|
+
/**
|
|
638
|
+
* A dumped encoder is a parse result like any other, so it belongs to whichever encoder the
|
|
639
|
+
* caller named.
|
|
640
|
+
*
|
|
641
|
+
* Restoring into `parserOptions.encoder` regardless is the one place a supplied encoder was
|
|
642
|
+
* silently ignored, and it stops being a formality once a bundler transform and the
|
|
643
|
+
* extraction pass share one Project: the transform's parses go to a private clone precisely
|
|
644
|
+
* so they cannot add rules to the sheet, and a `.json` module in its graph would have
|
|
645
|
+
* pinned an entire safelist into the emitted encoder from a pass that emits nothing.
|
|
646
|
+
*/
|
|
647
|
+
parseJson: (filePath: string, encoder?: ParserOptions["encoder"]) => ParserResult;
|
|
648
|
+
parseSourceFile: (filePath: string, encoder?: ParserOptions["encoder"], options?: ParseSourceFileOptions) => ParserResult | undefined;
|
|
618
649
|
transformFile: (_filePath: string, content: string) => string;
|
|
619
650
|
classify: (fileMap: Map<string, ParserResultInterface>) => import("@bamboocss/types").ClassifyReport;
|
|
620
651
|
}
|
|
621
652
|
//#endregion
|
|
622
|
-
export { ParserResult, Project, ProjectOptions, ResolutionFact, ResolutionReadSet, ResolutionWork, type UnresolvedStyle, findUnresolvedStyles };
|
|
653
|
+
export { AddSourceFileOptions, ParseSourceFileOptions, ParserResult, Project, ProjectOptions, ResolutionFact, ResolutionReadSet, ResolutionWork, type UnresolvedStyle, findUnresolvedStyles };
|
package/dist/index.d.mts
CHANGED
|
@@ -6,7 +6,7 @@ import { ArtifactId, BambooHooks, ConfigTsOptions, CssArtifactType, LoadConfigRe
|
|
|
6
6
|
//#region ../generator/dist/index.d.cts
|
|
7
7
|
//#region src/generator.d.ts
|
|
8
8
|
interface SplitCssArtifact {
|
|
9
|
-
type: 'layer' | '
|
|
9
|
+
type: 'layer' | 'theme';
|
|
10
10
|
name: string;
|
|
11
11
|
file: string;
|
|
12
12
|
code: string;
|
|
@@ -16,12 +16,8 @@ interface SplitCssArtifact {
|
|
|
16
16
|
interface SplitCssResult {
|
|
17
17
|
/** Layer CSS files (reset, global, tokens, utilities) */
|
|
18
18
|
layers: SplitCssArtifact[];
|
|
19
|
-
/** Recipe CSS files */
|
|
20
|
-
recipes: SplitCssArtifact[];
|
|
21
19
|
/** Theme CSS files (not auto-imported) */
|
|
22
20
|
themes: SplitCssArtifact[];
|
|
23
|
-
/** Content for recipes.css */
|
|
24
|
-
recipesIndex: string;
|
|
25
21
|
/** Content for main styles.css */
|
|
26
22
|
index: string;
|
|
27
23
|
}
|
|
@@ -30,7 +26,11 @@ declare class Generator extends Context {
|
|
|
30
26
|
getArtifacts: (ids?: ArtifactId[] | undefined) => import("@bamboocss/types").Artifact[];
|
|
31
27
|
appendCssOfType: (type: CssArtifactType, sheet: Stylesheet) => void;
|
|
32
28
|
appendLayerParams: (sheet: Stylesheet) => void;
|
|
33
|
-
appendBaselineCss: (sheet: Stylesheet
|
|
29
|
+
appendBaselineCss: (sheet: Stylesheet, {
|
|
30
|
+
atomizeRecipes
|
|
31
|
+
}?: {
|
|
32
|
+
atomizeRecipes?: boolean;
|
|
33
|
+
}) => void;
|
|
34
34
|
appendParserCss: (sheet: Stylesheet) => void;
|
|
35
35
|
/**
|
|
36
36
|
* Drop token css variables nothing can reach. Call this only once the sheet holds the
|
|
@@ -199,23 +199,11 @@ declare class Generator extends Context {
|
|
|
199
199
|
* Get CSS for a specific layer from the stylesheet
|
|
200
200
|
*/
|
|
201
201
|
getLayerCss: (sheet: Stylesheet, layer: "reset" | "base" | "tokens" | "recipes" | "utilities") => string;
|
|
202
|
-
/**
|
|
203
|
-
* Get CSS for a specific recipe
|
|
204
|
-
*/
|
|
205
|
-
getRecipeCss: (recipeName: string) => string;
|
|
206
|
-
/**
|
|
207
|
-
* Get all recipe names from the decoder
|
|
208
|
-
*/
|
|
209
|
-
getRecipeNames: () => string[];
|
|
210
202
|
/**
|
|
211
203
|
* Get all split CSS artifacts for the stylesheet
|
|
212
204
|
* Used when --splitting flag is enabled
|
|
213
205
|
*/
|
|
214
|
-
getSplitCssArtifacts: (sheet: Stylesheet
|
|
215
|
-
includeRecipes
|
|
216
|
-
}?: {
|
|
217
|
-
includeRecipes?: boolean;
|
|
218
|
-
}) => SplitCssResult;
|
|
206
|
+
getSplitCssArtifacts: (sheet: Stylesheet) => SplitCssResult;
|
|
219
207
|
getSpec: () => SpecFile[];
|
|
220
208
|
getSpecOfType: <T extends SpecType>(type: T) => T extends "color-palette" | "themes" ? SpecTypeMap[T] | undefined : SpecTypeMap[T];
|
|
221
209
|
} //#endregion
|
|
@@ -439,6 +427,19 @@ interface ResolutionFact {
|
|
|
439
427
|
readonly kind: 'import' | 'export';
|
|
440
428
|
readonly ordinal: number;
|
|
441
429
|
}
|
|
430
|
+
/** @internal How one explicitly supplied source relates to the checkout it is added to. */
|
|
431
|
+
interface AddSourceFileOptions {
|
|
432
|
+
/**
|
|
433
|
+
* This text belongs to a bundler transform rather than to the file at this path.
|
|
434
|
+
*
|
|
435
|
+
* @see `Project.auxiliarySources`
|
|
436
|
+
*/
|
|
437
|
+
auxiliary?: boolean;
|
|
438
|
+
}
|
|
439
|
+
/** @internal Logical identity for hooks when a bundler source needs a synthetic AST path. */
|
|
440
|
+
interface ParseSourceFileOptions {
|
|
441
|
+
hookFilePath?: string;
|
|
442
|
+
}
|
|
442
443
|
/** @internal Exact semantic closure plus missing local paths which can redirect it. */
|
|
443
444
|
interface ResolutionReadSet {
|
|
444
445
|
readonly dependencies: readonly string[];
|
|
@@ -508,6 +509,24 @@ declare class Project {
|
|
|
508
509
|
private sourcePreparations;
|
|
509
510
|
/** Paths explicitly removed through this wrapper, until an add/create observes them again. */
|
|
510
511
|
private removedSourcePaths;
|
|
512
|
+
/**
|
|
513
|
+
* Sources a bundler transform owns, rather than members of the checkout.
|
|
514
|
+
*
|
|
515
|
+
* A compiler which shares this Project has to park bundler-transformed text somewhere, and
|
|
516
|
+
* it cannot be the file's own path — that is the canonical source every other reader
|
|
517
|
+
* resolves through. It parses under a sibling path instead, and that parse resolves its
|
|
518
|
+
* imports like any other, so the ledger gains an importer no watcher will ever report a
|
|
519
|
+
* change for. Left in, those facts reach the incremental extraction pass: an auxiliary
|
|
520
|
+
* importer would be selected as a dependent of the file it shadows and ordered against
|
|
521
|
+
* inventory members that do not import it.
|
|
522
|
+
*
|
|
523
|
+
* Excluded from exactly the three queries that decide what a rebuild re-extracts —
|
|
524
|
+
* the ledger, the dependent walk and the unresolved-importer set — and from nothing else.
|
|
525
|
+
* Resolution itself is deliberately untouched: an auxiliary parse must still see the same
|
|
526
|
+
* modules the real one would, and its forward edges are what a bundler registers as watch
|
|
527
|
+
* files.
|
|
528
|
+
*/
|
|
529
|
+
private auxiliarySources;
|
|
511
530
|
private resolutionWork;
|
|
512
531
|
private resetResolutionState;
|
|
513
532
|
/** Files whose imports may resolve differently after a local file appears. */
|
|
@@ -600,7 +619,9 @@ declare class Project {
|
|
|
600
619
|
getResolutionConfigurationFiles: (filePath: string, targets?: Iterable<string>, previous?: Iterable<string>) => readonly string[];
|
|
601
620
|
createSourceFile: (filePath: string) => SourceFile;
|
|
602
621
|
createSourceFiles: () => void;
|
|
603
|
-
addSourceFile: (filePath: string, content: string) => SourceFile;
|
|
622
|
+
addSourceFile: (filePath: string, content: string, options?: AddSourceFileOptions) => SourceFile;
|
|
623
|
+
/** Claim or release compiler ownership of one source, in the ledger's own spelling. */
|
|
624
|
+
private markAuxiliary;
|
|
604
625
|
removeSourceFile: (filePath: string) => boolean;
|
|
605
626
|
/**
|
|
606
627
|
* The current digest of one exported value, for read verification.
|
|
@@ -613,10 +634,20 @@ declare class Project {
|
|
|
613
634
|
reloadSourceFiles: () => void;
|
|
614
635
|
get readFile(): (filePath: string) => string;
|
|
615
636
|
get getFiles(): () => string[];
|
|
616
|
-
|
|
617
|
-
|
|
637
|
+
/**
|
|
638
|
+
* A dumped encoder is a parse result like any other, so it belongs to whichever encoder the
|
|
639
|
+
* caller named.
|
|
640
|
+
*
|
|
641
|
+
* Restoring into `parserOptions.encoder` regardless is the one place a supplied encoder was
|
|
642
|
+
* silently ignored, and it stops being a formality once a bundler transform and the
|
|
643
|
+
* extraction pass share one Project: the transform's parses go to a private clone precisely
|
|
644
|
+
* so they cannot add rules to the sheet, and a `.json` module in its graph would have
|
|
645
|
+
* pinned an entire safelist into the emitted encoder from a pass that emits nothing.
|
|
646
|
+
*/
|
|
647
|
+
parseJson: (filePath: string, encoder?: ParserOptions["encoder"]) => ParserResult;
|
|
648
|
+
parseSourceFile: (filePath: string, encoder?: ParserOptions["encoder"], options?: ParseSourceFileOptions) => ParserResult | undefined;
|
|
618
649
|
transformFile: (_filePath: string, content: string) => string;
|
|
619
650
|
classify: (fileMap: Map<string, ParserResultInterface>) => import("@bamboocss/types").ClassifyReport;
|
|
620
651
|
}
|
|
621
652
|
//#endregion
|
|
622
|
-
export { ParserResult, Project, ProjectOptions, ResolutionFact, ResolutionReadSet, ResolutionWork, type UnresolvedStyle, findUnresolvedStyles };
|
|
653
|
+
export { AddSourceFileOptions, ParseSourceFileOptions, ParserResult, Project, ProjectOptions, ResolutionFact, ResolutionReadSet, ResolutionWork, type UnresolvedStyle, findUnresolvedStyles };
|
package/dist/index.mjs
CHANGED
|
@@ -1690,6 +1690,24 @@ var Project = class {
|
|
|
1690
1690
|
sourcePreparations = /* @__PURE__ */ new Map();
|
|
1691
1691
|
/** Paths explicitly removed through this wrapper, until an add/create observes them again. */
|
|
1692
1692
|
removedSourcePaths = /* @__PURE__ */ new Set();
|
|
1693
|
+
/**
|
|
1694
|
+
* Sources a bundler transform owns, rather than members of the checkout.
|
|
1695
|
+
*
|
|
1696
|
+
* A compiler which shares this Project has to park bundler-transformed text somewhere, and
|
|
1697
|
+
* it cannot be the file's own path — that is the canonical source every other reader
|
|
1698
|
+
* resolves through. It parses under a sibling path instead, and that parse resolves its
|
|
1699
|
+
* imports like any other, so the ledger gains an importer no watcher will ever report a
|
|
1700
|
+
* change for. Left in, those facts reach the incremental extraction pass: an auxiliary
|
|
1701
|
+
* importer would be selected as a dependent of the file it shadows and ordered against
|
|
1702
|
+
* inventory members that do not import it.
|
|
1703
|
+
*
|
|
1704
|
+
* Excluded from exactly the three queries that decide what a rebuild re-extracts —
|
|
1705
|
+
* the ledger, the dependent walk and the unresolved-importer set — and from nothing else.
|
|
1706
|
+
* Resolution itself is deliberately untouched: an auxiliary parse must still see the same
|
|
1707
|
+
* modules the real one would, and its forward edges are what a bundler registers as watch
|
|
1708
|
+
* files.
|
|
1709
|
+
*/
|
|
1710
|
+
auxiliarySources = /* @__PURE__ */ new Set();
|
|
1693
1711
|
/** File-tree changes invalidate even successful resolutions (extension precedence can move). */
|
|
1694
1712
|
#fileTreeRevision = 0;
|
|
1695
1713
|
resolutionWork = {
|
|
@@ -1708,6 +1726,7 @@ var Project = class {
|
|
|
1708
1726
|
this.resolutionsByImporter = /* @__PURE__ */ new Map();
|
|
1709
1727
|
this.sourcePreparations = /* @__PURE__ */ new Map();
|
|
1710
1728
|
this.removedSourcePaths = /* @__PURE__ */ new Set();
|
|
1729
|
+
this.auxiliarySources = /* @__PURE__ */ new Set();
|
|
1711
1730
|
this.resolutionWork = {
|
|
1712
1731
|
moduleResolutionsAttempted: 0,
|
|
1713
1732
|
sourceFilesAdded: 0,
|
|
@@ -1717,12 +1736,12 @@ var Project = class {
|
|
|
1717
1736
|
/** Files whose imports may resolve differently after a local file appears. */
|
|
1718
1737
|
getUnresolvedImporters = () => {
|
|
1719
1738
|
this.#assertNotLoading();
|
|
1720
|
-
return [...this.unresolvedImporters].sort();
|
|
1739
|
+
return [...this.unresolvedImporters].filter((importer) => !this.auxiliarySources.has(importer)).sort();
|
|
1721
1740
|
};
|
|
1722
1741
|
/** @internal Immutable resolution facts in importer/AST order. */
|
|
1723
1742
|
getResolutionLedger = () => {
|
|
1724
1743
|
this.#assertNotLoading();
|
|
1725
|
-
return Object.freeze([...this.resolutionsByImporter.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([, entry]) => entry.facts));
|
|
1744
|
+
return Object.freeze([...this.resolutionsByImporter.entries()].filter(([importer]) => !this.auxiliarySources.has(importer)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([, entry]) => entry.facts));
|
|
1726
1745
|
};
|
|
1727
1746
|
/** @internal Every distinct local target represented by the current ledger. */
|
|
1728
1747
|
getResolvedSourceFiles = () => {
|
|
@@ -2051,7 +2070,7 @@ var Project = class {
|
|
|
2051
2070
|
* downstream edges the ledger records. The transaction is published only after the AST and
|
|
2052
2071
|
* its facts agree. A failed or re-entered attempt restores the input AST and can be retried.
|
|
2053
2072
|
*/
|
|
2054
|
-
prepareEffectiveSource = (filePath, sourceFile) => {
|
|
2073
|
+
prepareEffectiveSource = (filePath, sourceFile, hookFilePath = filePath) => {
|
|
2055
2074
|
this.#assertNotLoading();
|
|
2056
2075
|
const sourcePath = this.normalizePath(sourceFile.getFilePath());
|
|
2057
2076
|
const currentText = sourceFile.getFullText();
|
|
@@ -2086,7 +2105,7 @@ var Project = class {
|
|
|
2086
2105
|
const options = {};
|
|
2087
2106
|
try {
|
|
2088
2107
|
const custom = this.options.hooks["parser:before"]?.({
|
|
2089
|
-
filePath,
|
|
2108
|
+
filePath: hookFilePath,
|
|
2090
2109
|
content: currentText,
|
|
2091
2110
|
configure(next) {
|
|
2092
2111
|
const { matchTag, matchTagMode, matchTagProp } = next;
|
|
@@ -2096,7 +2115,7 @@ var Project = class {
|
|
|
2096
2115
|
}
|
|
2097
2116
|
});
|
|
2098
2117
|
assertTransaction(currentText);
|
|
2099
|
-
const transformed = custom ?? this.transformFile(
|
|
2118
|
+
const transformed = custom ?? this.transformFile(hookFilePath, currentText);
|
|
2100
2119
|
assertTransaction(currentText);
|
|
2101
2120
|
if (currentText !== transformed) sourceFile.replaceWithText(transformed);
|
|
2102
2121
|
assertTransaction(transformed);
|
|
@@ -2160,6 +2179,7 @@ var Project = class {
|
|
|
2160
2179
|
const importers = new Set([...this.dependents.get(current) ?? [], ...this.removedDependents.get(current) ?? []]);
|
|
2161
2180
|
for (const importer of [...importers].sort()) {
|
|
2162
2181
|
if (importer === start || seen.has(importer)) continue;
|
|
2182
|
+
if (this.auxiliarySources.has(importer)) continue;
|
|
2163
2183
|
seen.add(importer);
|
|
2164
2184
|
queue.push(importer);
|
|
2165
2185
|
}
|
|
@@ -2297,7 +2317,7 @@ var Project = class {
|
|
|
2297
2317
|
if (error?.code !== "ENOENT") throw error;
|
|
2298
2318
|
}
|
|
2299
2319
|
};
|
|
2300
|
-
addSourceFile = (filePath, content) => {
|
|
2320
|
+
addSourceFile = (filePath, content, options = {}) => {
|
|
2301
2321
|
this.#assertNotLoading();
|
|
2302
2322
|
this.#ensureSourceFiles();
|
|
2303
2323
|
const existing = filePath.includes("/") ? this.project.getSourceFile(filePath) : void 0;
|
|
@@ -2323,14 +2343,25 @@ var Project = class {
|
|
|
2323
2343
|
* may have replaced this file's text through a `parser:before` hook. Such a file no longer
|
|
2324
2344
|
* matches its own source, falls through, and is overwritten exactly as before.
|
|
2325
2345
|
*/
|
|
2326
|
-
if (existing && existing.getFullText() === content)
|
|
2346
|
+
if (existing && existing.getFullText() === content) {
|
|
2347
|
+
this.markAuxiliary(existing.getFilePath(), options.auxiliary);
|
|
2348
|
+
return existing;
|
|
2349
|
+
}
|
|
2327
2350
|
this.invalidateSourcePreparation(filePath, existing);
|
|
2328
2351
|
this.removedSourcePaths.delete(this.normalizePath(filePath));
|
|
2329
2352
|
this.invalidate(!existing, existing?.getFilePath());
|
|
2330
|
-
|
|
2353
|
+
const sourceFile = this.project.createSourceFile(filePath, content, {
|
|
2331
2354
|
overwrite: true,
|
|
2332
2355
|
scriptKind: scriptKindFor(filePath)
|
|
2333
2356
|
});
|
|
2357
|
+
this.markAuxiliary(sourceFile.getFilePath(), options.auxiliary);
|
|
2358
|
+
return sourceFile;
|
|
2359
|
+
};
|
|
2360
|
+
/** Claim or release compiler ownership of one source, in the ledger's own spelling. */
|
|
2361
|
+
markAuxiliary = (filePath, auxiliary) => {
|
|
2362
|
+
const normalized = this.normalizePath(filePath);
|
|
2363
|
+
if (auxiliary) this.auxiliarySources.add(normalized);
|
|
2364
|
+
else this.auxiliarySources.delete(normalized);
|
|
2334
2365
|
};
|
|
2335
2366
|
removeSourceFile = (filePath) => {
|
|
2336
2367
|
this.#assertNotLoading();
|
|
@@ -2341,6 +2372,7 @@ var Project = class {
|
|
|
2341
2372
|
this.invalidateSourcePreparation(filePath, sourceFile);
|
|
2342
2373
|
this.markTargetRemoved(this.normalizePath(sourceFile.getFilePath()), sourceFile);
|
|
2343
2374
|
this.options.parserOptions.encoder.releaseFile(sourceFile.getFilePath());
|
|
2375
|
+
this.auxiliarySources.delete(this.normalizePath(sourceFile.getFilePath()));
|
|
2344
2376
|
return this.project.removeSourceFile(sourceFile);
|
|
2345
2377
|
}
|
|
2346
2378
|
return false;
|
|
@@ -2393,23 +2425,35 @@ var Project = class {
|
|
|
2393
2425
|
this.#assertNotLoading();
|
|
2394
2426
|
return this.options.getFiles;
|
|
2395
2427
|
}
|
|
2396
|
-
|
|
2428
|
+
/**
|
|
2429
|
+
* A dumped encoder is a parse result like any other, so it belongs to whichever encoder the
|
|
2430
|
+
* caller named.
|
|
2431
|
+
*
|
|
2432
|
+
* Restoring into `parserOptions.encoder` regardless is the one place a supplied encoder was
|
|
2433
|
+
* silently ignored, and it stops being a formality once a bundler transform and the
|
|
2434
|
+
* extraction pass share one Project: the transform's parses go to a private clone precisely
|
|
2435
|
+
* so they cannot add rules to the sheet, and a `.json` module in its graph would have
|
|
2436
|
+
* pinned an entire safelist into the emitted encoder from a pass that emits nothing.
|
|
2437
|
+
*/
|
|
2438
|
+
parseJson = (filePath, encoder) => {
|
|
2397
2439
|
this.#assertNotLoading();
|
|
2398
2440
|
const { readFile, parserOptions } = this.options;
|
|
2441
|
+
const target = encoder ?? parserOptions.encoder;
|
|
2399
2442
|
const content = readFile(filePath);
|
|
2400
|
-
|
|
2401
|
-
return new ParserResult(parserOptions).setFilePath(filePath);
|
|
2443
|
+
target.fromJSON(JSON.parse(content));
|
|
2444
|
+
return new ParserResult(parserOptions, encoder).setFilePath(filePath);
|
|
2402
2445
|
};
|
|
2403
|
-
parseSourceFile = (filePath, encoder) => {
|
|
2446
|
+
parseSourceFile = (filePath, encoder, options = {}) => {
|
|
2404
2447
|
this.#assertNotLoading();
|
|
2405
2448
|
const { hooks } = this.options;
|
|
2406
|
-
|
|
2449
|
+
const hookFilePath = options.hookFilePath ?? filePath;
|
|
2450
|
+
if (filePath.endsWith(".json")) return this.parseJson(filePath, encoder);
|
|
2407
2451
|
const sourceFile = this.project.getSourceFile(filePath);
|
|
2408
2452
|
if (!sourceFile) return;
|
|
2409
|
-
const { options } = this.prepareEffectiveSource(filePath, sourceFile);
|
|
2410
|
-
const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", sourceFile.getFilePath(), () => this.parser(sourceFile, encoder,
|
|
2453
|
+
const { options: parserOptions } = this.prepareEffectiveSource(filePath, sourceFile, hookFilePath);
|
|
2454
|
+
const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", sourceFile.getFilePath(), () => this.parser(sourceFile, encoder, parserOptions, this.resolveModule))?.setFilePath(filePath);
|
|
2411
2455
|
hooks["parser:after"]?.({
|
|
2412
|
-
filePath,
|
|
2456
|
+
filePath: hookFilePath,
|
|
2413
2457
|
result
|
|
2414
2458
|
});
|
|
2415
2459
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "The static parser for bamboo css",
|
|
5
5
|
"homepage": "https://bamboocss.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,18 +34,18 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ts-morph": "28.0.0",
|
|
36
36
|
"ts-pattern": "5.9.0",
|
|
37
|
-
"@bamboocss/config": "^1.
|
|
38
|
-
"@bamboocss/core": "^1.
|
|
39
|
-
"@bamboocss/extractor": "1.
|
|
40
|
-
"@bamboocss/logger": "1.
|
|
41
|
-
"@bamboocss/shared": "1.
|
|
42
|
-
"@bamboocss/types": "1.
|
|
37
|
+
"@bamboocss/config": "^1.48.0",
|
|
38
|
+
"@bamboocss/core": "^1.48.0",
|
|
39
|
+
"@bamboocss/extractor": "1.48.0",
|
|
40
|
+
"@bamboocss/logger": "1.48.0",
|
|
41
|
+
"@bamboocss/shared": "1.48.0",
|
|
42
|
+
"@bamboocss/types": "1.48.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@typescript/api": "npm:typescript@7.1.0-dev.20260819.1",
|
|
46
|
-
"@bamboocss/generator": "1.
|
|
47
|
-
"@bamboocss/plugin-svelte": "1.
|
|
48
|
-
"@bamboocss/plugin-vue": "1.
|
|
46
|
+
"@bamboocss/generator": "1.48.0",
|
|
47
|
+
"@bamboocss/plugin-svelte": "1.48.0",
|
|
48
|
+
"@bamboocss/plugin-vue": "1.48.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "tsdown src/index.ts --format=esm,cjs --dts",
|