dispersa 1.1.0 → 1.2.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/builders.cjs.map +1 -1
- package/dist/builders.js.map +1 -1
- package/dist/filters.cjs.map +1 -1
- package/dist/filters.js.map +1 -1
- package/dist/index.cjs +141 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +141 -98
- package/dist/index.js.map +1 -1
- package/dist/lint.cjs +28 -0
- package/dist/lint.cjs.map +1 -1
- package/dist/lint.d.cts +13 -0
- package/dist/lint.d.ts +13 -0
- package/dist/lint.js +28 -0
- package/dist/lint.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -164,8 +164,39 @@ function getPureAliasReferenceName(value) {
|
|
|
164
164
|
const match = /^\{([^}]+)\}$/.exec(value);
|
|
165
165
|
return match?.[1]?.trim();
|
|
166
166
|
}
|
|
167
|
+
function rewriteRootReferences(value) {
|
|
168
|
+
if (typeof value === "string") {
|
|
169
|
+
return ROOT_REF_PATTERN.test(value) ? value.replace(ROOT_REF_PATTERN, "}") : value;
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
let changed = false;
|
|
173
|
+
const mapped = value.map((item) => {
|
|
174
|
+
const rewritten = rewriteRootReferences(item);
|
|
175
|
+
if (rewritten !== item) {
|
|
176
|
+
changed = true;
|
|
177
|
+
}
|
|
178
|
+
return rewritten;
|
|
179
|
+
});
|
|
180
|
+
return changed ? mapped : value;
|
|
181
|
+
}
|
|
182
|
+
if (typeof value === "object" && value !== null) {
|
|
183
|
+
let changed = false;
|
|
184
|
+
const result = {};
|
|
185
|
+
for (const [k, v] of Object.entries(value)) {
|
|
186
|
+
const rewritten = rewriteRootReferences(v);
|
|
187
|
+
if (rewritten !== v) {
|
|
188
|
+
changed = true;
|
|
189
|
+
}
|
|
190
|
+
result[k] = rewritten;
|
|
191
|
+
}
|
|
192
|
+
return changed ? result : value;
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
var ROOT_REF_PATTERN;
|
|
167
197
|
var init_token_utils = __esm({
|
|
168
198
|
"src/shared/utils/token-utils.ts"() {
|
|
199
|
+
ROOT_REF_PATTERN = /\.\$root\}/g;
|
|
169
200
|
}
|
|
170
201
|
});
|
|
171
202
|
|
|
@@ -4150,9 +4181,12 @@ var BuildOrchestrator = class {
|
|
|
4150
4181
|
resolver,
|
|
4151
4182
|
config.transforms,
|
|
4152
4183
|
config.preprocessors,
|
|
4153
|
-
config.filters
|
|
4154
|
-
config.lint
|
|
4184
|
+
config.filters
|
|
4155
4185
|
);
|
|
4186
|
+
if (config.lint?.enabled) {
|
|
4187
|
+
const tokenSets = permutations2.map((p) => p.tokens);
|
|
4188
|
+
await this.pipeline.runLintOnPermutations(tokenSets, config.lint);
|
|
4189
|
+
}
|
|
4156
4190
|
return this.executeBuild(buildPath, config, permutations2, resolver);
|
|
4157
4191
|
}
|
|
4158
4192
|
const permutations = await Promise.all(
|
|
@@ -4162,12 +4196,15 @@ var BuildOrchestrator = class {
|
|
|
4162
4196
|
modifierInputs,
|
|
4163
4197
|
config.transforms,
|
|
4164
4198
|
config.preprocessors,
|
|
4165
|
-
config.filters
|
|
4166
|
-
config.lint
|
|
4199
|
+
config.filters
|
|
4167
4200
|
);
|
|
4168
4201
|
return { tokens, modifierInputs: resolvedInputs };
|
|
4169
4202
|
})
|
|
4170
4203
|
);
|
|
4204
|
+
if (config.lint?.enabled) {
|
|
4205
|
+
const tokenSets = permutations.map((p) => p.tokens);
|
|
4206
|
+
await this.pipeline.runLintOnPermutations(tokenSets, config.lint);
|
|
4207
|
+
}
|
|
4171
4208
|
return this.executeBuild(buildPath, config, permutations, resolver);
|
|
4172
4209
|
}
|
|
4173
4210
|
/**
|
|
@@ -4775,6 +4812,34 @@ var LintRunner = class {
|
|
|
4775
4812
|
this.pluginLoader.clearCache();
|
|
4776
4813
|
this.resolvedConfig = null;
|
|
4777
4814
|
}
|
|
4815
|
+
/**
|
|
4816
|
+
* Run lint on multiple token sets with deduplication
|
|
4817
|
+
*
|
|
4818
|
+
* When running lint across multiple permutations (e.g., light/dark themes),
|
|
4819
|
+
* the same issue may appear multiple times. This method deduplicates issues
|
|
4820
|
+
* by ruleId, tokenName, and message.
|
|
4821
|
+
*
|
|
4822
|
+
* Use this for both standalone lint and build lint to ensure identical output.
|
|
4823
|
+
*
|
|
4824
|
+
* @param tokenSets - Array of resolved token sets to lint
|
|
4825
|
+
* @returns Combined lint result with deduplicated issues
|
|
4826
|
+
*/
|
|
4827
|
+
async runMultiple(tokenSets) {
|
|
4828
|
+
const results = await Promise.all(tokenSets.map((tokens) => this.run(tokens)));
|
|
4829
|
+
const allIssues = results.flatMap((result) => result.issues);
|
|
4830
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4831
|
+
const deduplicated = [];
|
|
4832
|
+
for (const issue of allIssues) {
|
|
4833
|
+
const key = `${issue.ruleId}:${issue.tokenName}:${issue.message}`;
|
|
4834
|
+
if (!seen.has(key)) {
|
|
4835
|
+
seen.add(key);
|
|
4836
|
+
deduplicated.push(issue);
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
const errorCount = deduplicated.filter((i) => i.severity === "error").length;
|
|
4840
|
+
const warningCount = deduplicated.filter((i) => i.severity === "warn").length;
|
|
4841
|
+
return { issues: deduplicated, errorCount, warningCount };
|
|
4842
|
+
}
|
|
4778
4843
|
};
|
|
4779
4844
|
|
|
4780
4845
|
// src/shared/constants.ts
|
|
@@ -5923,17 +5988,35 @@ var ResolutionEngine = class {
|
|
|
5923
5988
|
/**
|
|
5924
5989
|
* Merge two token objects (later values override earlier ones)
|
|
5925
5990
|
*/
|
|
5926
|
-
mergeTokens(target, source) {
|
|
5991
|
+
mergeTokens(target, source, path7 = []) {
|
|
5927
5992
|
const result = { ...target };
|
|
5928
5993
|
for (const [key, value] of Object.entries(source)) {
|
|
5929
|
-
const
|
|
5930
|
-
|
|
5994
|
+
const currentPath = [...path7, key];
|
|
5995
|
+
const isSourceGroup = typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value);
|
|
5996
|
+
const targetValue = target[key];
|
|
5997
|
+
const isTargetGroup = typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue) && !("$value" in targetValue);
|
|
5998
|
+
if (targetValue !== void 0 && !key.startsWith("$")) {
|
|
5999
|
+
if (isSourceGroup && !isTargetGroup) {
|
|
6000
|
+
this.validationHandler.warn(
|
|
6001
|
+
`Token '${currentPath.join(".")}' is being replaced by a group. This may cause loss of existing children.`
|
|
6002
|
+
);
|
|
6003
|
+
} else if (!isSourceGroup && isTargetGroup) {
|
|
6004
|
+
const targetHasChildren = Object.keys(targetValue).some((k) => !k.startsWith("$"));
|
|
6005
|
+
if (targetHasChildren) {
|
|
6006
|
+
this.validationHandler.warn(
|
|
6007
|
+
`Group '${currentPath.join(".")}' is being replaced by a token. Existing children will be lost.`
|
|
6008
|
+
);
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
if (!isSourceGroup) {
|
|
5931
6013
|
result[key] = value;
|
|
5932
6014
|
continue;
|
|
5933
6015
|
}
|
|
5934
6016
|
result[key] = this.mergeTokens(
|
|
5935
6017
|
result[key] ?? {},
|
|
5936
|
-
value
|
|
6018
|
+
value,
|
|
6019
|
+
currentPath
|
|
5937
6020
|
);
|
|
5938
6021
|
}
|
|
5939
6022
|
return result;
|
|
@@ -5973,6 +6056,7 @@ var ResolutionEngine = class {
|
|
|
5973
6056
|
|
|
5974
6057
|
// src/build/pipeline/token-pipeline.ts
|
|
5975
6058
|
init_errors();
|
|
6059
|
+
init_token_utils();
|
|
5976
6060
|
init_validation_handler();
|
|
5977
6061
|
init_errors();
|
|
5978
6062
|
|
|
@@ -6434,40 +6518,12 @@ var TokenParser = class {
|
|
|
6434
6518
|
};
|
|
6435
6519
|
|
|
6436
6520
|
// src/build/pipeline/token-pipeline.ts
|
|
6437
|
-
var ROOT_REF_PATTERN = /\.\$root\}/g;
|
|
6438
|
-
function rewriteRootReferences(value) {
|
|
6439
|
-
if (typeof value === "string") {
|
|
6440
|
-
return ROOT_REF_PATTERN.test(value) ? value.replace(ROOT_REF_PATTERN, "}") : value;
|
|
6441
|
-
}
|
|
6442
|
-
if (Array.isArray(value)) {
|
|
6443
|
-
let changed = false;
|
|
6444
|
-
const mapped = value.map((item) => {
|
|
6445
|
-
const rewritten = rewriteRootReferences(item);
|
|
6446
|
-
if (rewritten !== item) changed = true;
|
|
6447
|
-
return rewritten;
|
|
6448
|
-
});
|
|
6449
|
-
return changed ? mapped : value;
|
|
6450
|
-
}
|
|
6451
|
-
if (typeof value === "object" && value !== null) {
|
|
6452
|
-
let changed = false;
|
|
6453
|
-
const result = {};
|
|
6454
|
-
for (const [k, v] of Object.entries(value)) {
|
|
6455
|
-
const rewritten = rewriteRootReferences(v);
|
|
6456
|
-
if (rewritten !== v) changed = true;
|
|
6457
|
-
result[k] = rewritten;
|
|
6458
|
-
}
|
|
6459
|
-
return changed ? result : value;
|
|
6460
|
-
}
|
|
6461
|
-
return value;
|
|
6462
|
-
}
|
|
6463
6521
|
var TokenPipeline = class {
|
|
6464
6522
|
options;
|
|
6465
6523
|
validationHandler;
|
|
6466
6524
|
resolverLoader;
|
|
6467
6525
|
tokenParser;
|
|
6468
6526
|
aliasResolver;
|
|
6469
|
-
lintRunner = null;
|
|
6470
|
-
lintConfigCache = null;
|
|
6471
6527
|
constructor(options = {}) {
|
|
6472
6528
|
this.options = options;
|
|
6473
6529
|
this.validationHandler = new ValidationHandler(options.validation);
|
|
@@ -6487,22 +6543,22 @@ var TokenPipeline = class {
|
|
|
6487
6543
|
* 6. Parse and flatten token structure
|
|
6488
6544
|
* 7. Resolve alias references
|
|
6489
6545
|
* 8. Strip $root from token names/paths (DTCG structural mechanism, transparent in output)
|
|
6490
|
-
* 9.
|
|
6491
|
-
* 10. Apply
|
|
6492
|
-
* 11. Apply transforms (if provided) — runs on the already-filtered token set
|
|
6546
|
+
* 9. Apply filters (if provided) — runs first to remove tokens before transforms
|
|
6547
|
+
* 10. Apply transforms (if provided) — runs on the already-filtered token set
|
|
6493
6548
|
*
|
|
6494
6549
|
* Each stage is explicitly typed to ensure correct order and prevent temporal coupling.
|
|
6495
6550
|
*
|
|
6551
|
+
* Note: Linting is handled separately via runLintOnPermutations() to enable
|
|
6552
|
+
* deduplication across multiple permutations.
|
|
6553
|
+
*
|
|
6496
6554
|
* @param resolver - Either a file path (string) or an inline ResolverDocument object
|
|
6497
6555
|
* @param modifierInputs - Modifier values for this permutation
|
|
6498
6556
|
* @param transformList - Optional transforms to apply
|
|
6499
6557
|
* @param preprocessorList - Optional preprocessors to apply
|
|
6500
6558
|
* @param filterList - Optional filters to apply before transforms
|
|
6501
|
-
* @
|
|
6502
|
-
* @returns Final tokens, resolution engine, and lint result
|
|
6559
|
+
* @returns Final tokens and resolution engine
|
|
6503
6560
|
*/
|
|
6504
|
-
async resolve(resolver, modifierInputs, transformList, preprocessorList, filterList
|
|
6505
|
-
const effectiveLintConfig = lintConfig ?? this.options.lint;
|
|
6561
|
+
async resolve(resolver, modifierInputs, transformList, preprocessorList, filterList) {
|
|
6506
6562
|
const stage1 = await this.loadResolver(resolver);
|
|
6507
6563
|
const engine = this.createEngine(stage1);
|
|
6508
6564
|
const result = await this.runPipelineStages(
|
|
@@ -6510,14 +6566,12 @@ var TokenPipeline = class {
|
|
|
6510
6566
|
modifierInputs,
|
|
6511
6567
|
preprocessorList,
|
|
6512
6568
|
filterList,
|
|
6513
|
-
transformList
|
|
6514
|
-
effectiveLintConfig
|
|
6569
|
+
transformList
|
|
6515
6570
|
);
|
|
6516
6571
|
return {
|
|
6517
6572
|
tokens: result.tokens,
|
|
6518
6573
|
resolutionEngine: result.resolutionEngine,
|
|
6519
|
-
modifierInputs: result.modifierInputs
|
|
6520
|
-
lintResult: result.lintResult
|
|
6574
|
+
modifierInputs: result.modifierInputs
|
|
6521
6575
|
};
|
|
6522
6576
|
}
|
|
6523
6577
|
/**
|
|
@@ -6527,15 +6581,14 @@ var TokenPipeline = class {
|
|
|
6527
6581
|
* `resolveAllPermutations()` (parallel permutations) to keep the
|
|
6528
6582
|
* stage sequence defined in exactly one place.
|
|
6529
6583
|
*/
|
|
6530
|
-
async runPipelineStages(engine, modifierInputs, preprocessorList, filterList, transformList
|
|
6584
|
+
async runPipelineStages(engine, modifierInputs, preprocessorList, filterList, transformList) {
|
|
6531
6585
|
const rawTokens = await this.resolveTokens(engine, modifierInputs);
|
|
6532
6586
|
const preprocessed = await this.preprocessTokens(rawTokens, preprocessorList);
|
|
6533
6587
|
const refResolved = await this.resolveReferences(preprocessed);
|
|
6534
6588
|
const flattened = this.flattenTokens(refResolved);
|
|
6535
6589
|
const aliasResolved = this.resolveAliases(flattened);
|
|
6536
6590
|
const rootStripped = this.stripRootTokenNames(aliasResolved);
|
|
6537
|
-
const
|
|
6538
|
-
const filtered = this.applyFilterStage(linted, filterList);
|
|
6591
|
+
const filtered = this.applyFilterStage(rootStripped, filterList);
|
|
6539
6592
|
return this.applyTransformStage(filtered, transformList);
|
|
6540
6593
|
}
|
|
6541
6594
|
/**
|
|
@@ -6649,45 +6702,8 @@ var TokenPipeline = class {
|
|
|
6649
6702
|
return { ...stage, aliasResolvedTokens: result };
|
|
6650
6703
|
}
|
|
6651
6704
|
/**
|
|
6652
|
-
* Stage 9: Run lint rules against tokens
|
|
6653
|
-
*
|
|
6654
|
-
* Linting runs after alias resolution and $root stripping but before
|
|
6655
|
-
* filters and transforms. This ensures rules see the full token set
|
|
6656
|
-
* with resolved values.
|
|
6657
|
-
*/
|
|
6658
|
-
async runLintStage(stage, lintConfig) {
|
|
6659
|
-
if (!lintConfig?.enabled) {
|
|
6660
|
-
return stage;
|
|
6661
|
-
}
|
|
6662
|
-
if (!this.lintRunner || !this.isLintConfigEqual(this.lintConfigCache, lintConfig)) {
|
|
6663
|
-
this.lintRunner = new LintRunner({
|
|
6664
|
-
plugins: lintConfig.plugins,
|
|
6665
|
-
rules: lintConfig.rules,
|
|
6666
|
-
onWarn: (msg) => this.validationHandler.warn(msg)
|
|
6667
|
-
});
|
|
6668
|
-
this.lintConfigCache = lintConfig;
|
|
6669
|
-
}
|
|
6670
|
-
const lintResult = await this.lintRunner.run(stage.aliasResolvedTokens);
|
|
6671
|
-
if (lintResult.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6672
|
-
throw new LintError(lintResult.issues);
|
|
6673
|
-
}
|
|
6674
|
-
for (const issue of lintResult.issues.filter((i) => i.severity === "warn")) {
|
|
6675
|
-
this.validationHandler.warn(
|
|
6676
|
-
`[${issue.ruleId}] ${issue.message} (token: ${issue.tokenName})`
|
|
6677
|
-
);
|
|
6678
|
-
}
|
|
6679
|
-
return { ...stage, lintResult };
|
|
6680
|
-
}
|
|
6681
|
-
isLintConfigEqual(a, b) {
|
|
6682
|
-
if (!a || !b) return false;
|
|
6683
|
-
if (a.enabled !== b.enabled) return false;
|
|
6684
|
-
if (a.failOnError !== b.failOnError) return false;
|
|
6685
|
-
if (a.plugins !== b.plugins) return false;
|
|
6686
|
-
if (a.rules !== b.rules) return false;
|
|
6687
|
-
return true;
|
|
6688
|
-
}
|
|
6689
6705
|
/**
|
|
6690
|
-
* Stage
|
|
6706
|
+
* Stage 9: Apply filters to tokens after alias resolution and $root stripping
|
|
6691
6707
|
*/
|
|
6692
6708
|
applyFilterStage(stage, filterList) {
|
|
6693
6709
|
let tokens = stage.aliasResolvedTokens;
|
|
@@ -6697,7 +6713,7 @@ var TokenPipeline = class {
|
|
|
6697
6713
|
return { ...stage, aliasResolvedTokens: tokens };
|
|
6698
6714
|
}
|
|
6699
6715
|
/**
|
|
6700
|
-
* Stage
|
|
6716
|
+
* Stage 10: Apply transforms to the filtered token set
|
|
6701
6717
|
*/
|
|
6702
6718
|
applyTransformStage(stage, transformList) {
|
|
6703
6719
|
let tokens = stage.aliasResolvedTokens;
|
|
@@ -6731,10 +6747,8 @@ var TokenPipeline = class {
|
|
|
6731
6747
|
* @param transformList - Optional transforms to apply
|
|
6732
6748
|
* @param preprocessorList - Optional preprocessors to apply
|
|
6733
6749
|
* @param filterList - Optional filters to apply before transforms
|
|
6734
|
-
* @param lintConfig - Optional lint configuration for this run
|
|
6735
6750
|
*/
|
|
6736
|
-
async resolveAllPermutations(resolver, transformList, preprocessorList, filterList
|
|
6737
|
-
const effectiveLintConfig = lintConfig ?? this.options.lint;
|
|
6751
|
+
async resolveAllPermutations(resolver, transformList, preprocessorList, filterList) {
|
|
6738
6752
|
const stage1 = await this.loadResolver(resolver);
|
|
6739
6753
|
const discoveryEngine = this.createEngine(stage1);
|
|
6740
6754
|
const permutationInputs = discoveryEngine.resolutionEngine.generatePermutations();
|
|
@@ -6747,17 +6761,39 @@ var TokenPipeline = class {
|
|
|
6747
6761
|
modifierInputs,
|
|
6748
6762
|
preprocessorList,
|
|
6749
6763
|
filterList,
|
|
6750
|
-
transformList
|
|
6751
|
-
effectiveLintConfig
|
|
6764
|
+
transformList
|
|
6752
6765
|
);
|
|
6753
6766
|
return {
|
|
6754
6767
|
tokens: result.tokens,
|
|
6755
|
-
modifierInputs: result.modifierInputs
|
|
6756
|
-
lintResult: result.lintResult
|
|
6768
|
+
modifierInputs: result.modifierInputs
|
|
6757
6769
|
};
|
|
6758
6770
|
})
|
|
6759
6771
|
);
|
|
6760
6772
|
}
|
|
6773
|
+
/**
|
|
6774
|
+
* Run lint on tokens from multiple permutations with deduplication
|
|
6775
|
+
*
|
|
6776
|
+
* This runs lint once on all permutation token sets combined and deduplicates
|
|
6777
|
+
* issues. Use this after resolveAllPermutations() for consistent lint behavior.
|
|
6778
|
+
*/
|
|
6779
|
+
async runLintOnPermutations(permutationTokens, lintConfig) {
|
|
6780
|
+
if (!lintConfig.enabled) {
|
|
6781
|
+
return { issues: [], errorCount: 0, warningCount: 0 };
|
|
6782
|
+
}
|
|
6783
|
+
const runner = new LintRunner({
|
|
6784
|
+
plugins: lintConfig.plugins,
|
|
6785
|
+
rules: lintConfig.rules,
|
|
6786
|
+
onWarn: (msg) => this.validationHandler.warn(msg)
|
|
6787
|
+
});
|
|
6788
|
+
const result = await runner.runMultiple(permutationTokens);
|
|
6789
|
+
for (const issue of result.issues.filter((i) => i.severity === "warn")) {
|
|
6790
|
+
this.validationHandler.warn(`[${issue.ruleId}] ${issue.message} (token: ${issue.tokenName})`);
|
|
6791
|
+
}
|
|
6792
|
+
if (result.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6793
|
+
throw new LintError(result.issues);
|
|
6794
|
+
}
|
|
6795
|
+
return result;
|
|
6796
|
+
}
|
|
6761
6797
|
/**
|
|
6762
6798
|
* Apply preprocessors to raw tokens
|
|
6763
6799
|
*/
|
|
@@ -6847,12 +6883,19 @@ async function resolveTokens(resolver, modifierInputs = {}, validation) {
|
|
|
6847
6883
|
async function lint(options) {
|
|
6848
6884
|
const { resolver, modifierInputs = {}, validation, ...lintConfig } = options;
|
|
6849
6885
|
const pipeline = createPipeline({ validation });
|
|
6850
|
-
const tokens = await resolvePipeline(pipeline, resolver, modifierInputs);
|
|
6851
6886
|
const runner = new LintRunner({
|
|
6852
6887
|
...lintConfig,
|
|
6853
6888
|
failOnError: lintConfig.failOnError ?? true
|
|
6854
6889
|
});
|
|
6855
|
-
|
|
6890
|
+
let tokenSets;
|
|
6891
|
+
if (Object.keys(modifierInputs).length > 0) {
|
|
6892
|
+
const { tokens } = await pipeline.resolve(resolver, modifierInputs);
|
|
6893
|
+
tokenSets = [tokens];
|
|
6894
|
+
} else {
|
|
6895
|
+
const permutations = await pipeline.resolveAllPermutations(resolver);
|
|
6896
|
+
tokenSets = permutations.map((p) => p.tokens);
|
|
6897
|
+
}
|
|
6898
|
+
const result = await runner.runMultiple(tokenSets);
|
|
6856
6899
|
if (result.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6857
6900
|
throw new LintError(result.issues);
|
|
6858
6901
|
}
|