dispersa 1.0.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 +41 -1
- 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 +41 -1
- package/dist/lint.js.map +1 -1
- package/package.json +9 -9
package/dist/index.cjs
CHANGED
|
@@ -191,8 +191,39 @@ function getPureAliasReferenceName(value) {
|
|
|
191
191
|
const match = /^\{([^}]+)\}$/.exec(value);
|
|
192
192
|
return match?.[1]?.trim();
|
|
193
193
|
}
|
|
194
|
+
function rewriteRootReferences(value) {
|
|
195
|
+
if (typeof value === "string") {
|
|
196
|
+
return ROOT_REF_PATTERN.test(value) ? value.replace(ROOT_REF_PATTERN, "}") : value;
|
|
197
|
+
}
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
let changed = false;
|
|
200
|
+
const mapped = value.map((item) => {
|
|
201
|
+
const rewritten = rewriteRootReferences(item);
|
|
202
|
+
if (rewritten !== item) {
|
|
203
|
+
changed = true;
|
|
204
|
+
}
|
|
205
|
+
return rewritten;
|
|
206
|
+
});
|
|
207
|
+
return changed ? mapped : value;
|
|
208
|
+
}
|
|
209
|
+
if (typeof value === "object" && value !== null) {
|
|
210
|
+
let changed = false;
|
|
211
|
+
const result = {};
|
|
212
|
+
for (const [k, v] of Object.entries(value)) {
|
|
213
|
+
const rewritten = rewriteRootReferences(v);
|
|
214
|
+
if (rewritten !== v) {
|
|
215
|
+
changed = true;
|
|
216
|
+
}
|
|
217
|
+
result[k] = rewritten;
|
|
218
|
+
}
|
|
219
|
+
return changed ? result : value;
|
|
220
|
+
}
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
var ROOT_REF_PATTERN;
|
|
194
224
|
var init_token_utils = __esm({
|
|
195
225
|
"src/shared/utils/token-utils.ts"() {
|
|
226
|
+
ROOT_REF_PATTERN = /\.\$root\}/g;
|
|
196
227
|
}
|
|
197
228
|
});
|
|
198
229
|
|
|
@@ -4177,9 +4208,12 @@ var BuildOrchestrator = class {
|
|
|
4177
4208
|
resolver,
|
|
4178
4209
|
config.transforms,
|
|
4179
4210
|
config.preprocessors,
|
|
4180
|
-
config.filters
|
|
4181
|
-
config.lint
|
|
4211
|
+
config.filters
|
|
4182
4212
|
);
|
|
4213
|
+
if (config.lint?.enabled) {
|
|
4214
|
+
const tokenSets = permutations2.map((p) => p.tokens);
|
|
4215
|
+
await this.pipeline.runLintOnPermutations(tokenSets, config.lint);
|
|
4216
|
+
}
|
|
4183
4217
|
return this.executeBuild(buildPath, config, permutations2, resolver);
|
|
4184
4218
|
}
|
|
4185
4219
|
const permutations = await Promise.all(
|
|
@@ -4189,12 +4223,15 @@ var BuildOrchestrator = class {
|
|
|
4189
4223
|
modifierInputs,
|
|
4190
4224
|
config.transforms,
|
|
4191
4225
|
config.preprocessors,
|
|
4192
|
-
config.filters
|
|
4193
|
-
config.lint
|
|
4226
|
+
config.filters
|
|
4194
4227
|
);
|
|
4195
4228
|
return { tokens, modifierInputs: resolvedInputs };
|
|
4196
4229
|
})
|
|
4197
4230
|
);
|
|
4231
|
+
if (config.lint?.enabled) {
|
|
4232
|
+
const tokenSets = permutations.map((p) => p.tokens);
|
|
4233
|
+
await this.pipeline.runLintOnPermutations(tokenSets, config.lint);
|
|
4234
|
+
}
|
|
4198
4235
|
return this.executeBuild(buildPath, config, permutations, resolver);
|
|
4199
4236
|
}
|
|
4200
4237
|
/**
|
|
@@ -4802,6 +4839,34 @@ var LintRunner = class {
|
|
|
4802
4839
|
this.pluginLoader.clearCache();
|
|
4803
4840
|
this.resolvedConfig = null;
|
|
4804
4841
|
}
|
|
4842
|
+
/**
|
|
4843
|
+
* Run lint on multiple token sets with deduplication
|
|
4844
|
+
*
|
|
4845
|
+
* When running lint across multiple permutations (e.g., light/dark themes),
|
|
4846
|
+
* the same issue may appear multiple times. This method deduplicates issues
|
|
4847
|
+
* by ruleId, tokenName, and message.
|
|
4848
|
+
*
|
|
4849
|
+
* Use this for both standalone lint and build lint to ensure identical output.
|
|
4850
|
+
*
|
|
4851
|
+
* @param tokenSets - Array of resolved token sets to lint
|
|
4852
|
+
* @returns Combined lint result with deduplicated issues
|
|
4853
|
+
*/
|
|
4854
|
+
async runMultiple(tokenSets) {
|
|
4855
|
+
const results = await Promise.all(tokenSets.map((tokens) => this.run(tokens)));
|
|
4856
|
+
const allIssues = results.flatMap((result) => result.issues);
|
|
4857
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4858
|
+
const deduplicated = [];
|
|
4859
|
+
for (const issue of allIssues) {
|
|
4860
|
+
const key = `${issue.ruleId}:${issue.tokenName}:${issue.message}`;
|
|
4861
|
+
if (!seen.has(key)) {
|
|
4862
|
+
seen.add(key);
|
|
4863
|
+
deduplicated.push(issue);
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
const errorCount = deduplicated.filter((i) => i.severity === "error").length;
|
|
4867
|
+
const warningCount = deduplicated.filter((i) => i.severity === "warn").length;
|
|
4868
|
+
return { issues: deduplicated, errorCount, warningCount };
|
|
4869
|
+
}
|
|
4805
4870
|
};
|
|
4806
4871
|
|
|
4807
4872
|
// src/shared/constants.ts
|
|
@@ -5950,17 +6015,35 @@ var ResolutionEngine = class {
|
|
|
5950
6015
|
/**
|
|
5951
6016
|
* Merge two token objects (later values override earlier ones)
|
|
5952
6017
|
*/
|
|
5953
|
-
mergeTokens(target, source) {
|
|
6018
|
+
mergeTokens(target, source, path7 = []) {
|
|
5954
6019
|
const result = { ...target };
|
|
5955
6020
|
for (const [key, value] of Object.entries(source)) {
|
|
5956
|
-
const
|
|
5957
|
-
|
|
6021
|
+
const currentPath = [...path7, key];
|
|
6022
|
+
const isSourceGroup = typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value);
|
|
6023
|
+
const targetValue = target[key];
|
|
6024
|
+
const isTargetGroup = typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue) && !("$value" in targetValue);
|
|
6025
|
+
if (targetValue !== void 0 && !key.startsWith("$")) {
|
|
6026
|
+
if (isSourceGroup && !isTargetGroup) {
|
|
6027
|
+
this.validationHandler.warn(
|
|
6028
|
+
`Token '${currentPath.join(".")}' is being replaced by a group. This may cause loss of existing children.`
|
|
6029
|
+
);
|
|
6030
|
+
} else if (!isSourceGroup && isTargetGroup) {
|
|
6031
|
+
const targetHasChildren = Object.keys(targetValue).some((k) => !k.startsWith("$"));
|
|
6032
|
+
if (targetHasChildren) {
|
|
6033
|
+
this.validationHandler.warn(
|
|
6034
|
+
`Group '${currentPath.join(".")}' is being replaced by a token. Existing children will be lost.`
|
|
6035
|
+
);
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
if (!isSourceGroup) {
|
|
5958
6040
|
result[key] = value;
|
|
5959
6041
|
continue;
|
|
5960
6042
|
}
|
|
5961
6043
|
result[key] = this.mergeTokens(
|
|
5962
6044
|
result[key] ?? {},
|
|
5963
|
-
value
|
|
6045
|
+
value,
|
|
6046
|
+
currentPath
|
|
5964
6047
|
);
|
|
5965
6048
|
}
|
|
5966
6049
|
return result;
|
|
@@ -6000,6 +6083,7 @@ var ResolutionEngine = class {
|
|
|
6000
6083
|
|
|
6001
6084
|
// src/build/pipeline/token-pipeline.ts
|
|
6002
6085
|
init_errors();
|
|
6086
|
+
init_token_utils();
|
|
6003
6087
|
init_validation_handler();
|
|
6004
6088
|
init_errors();
|
|
6005
6089
|
|
|
@@ -6461,40 +6545,12 @@ var TokenParser = class {
|
|
|
6461
6545
|
};
|
|
6462
6546
|
|
|
6463
6547
|
// src/build/pipeline/token-pipeline.ts
|
|
6464
|
-
var ROOT_REF_PATTERN = /\.\$root\}/g;
|
|
6465
|
-
function rewriteRootReferences(value) {
|
|
6466
|
-
if (typeof value === "string") {
|
|
6467
|
-
return ROOT_REF_PATTERN.test(value) ? value.replace(ROOT_REF_PATTERN, "}") : value;
|
|
6468
|
-
}
|
|
6469
|
-
if (Array.isArray(value)) {
|
|
6470
|
-
let changed = false;
|
|
6471
|
-
const mapped = value.map((item) => {
|
|
6472
|
-
const rewritten = rewriteRootReferences(item);
|
|
6473
|
-
if (rewritten !== item) changed = true;
|
|
6474
|
-
return rewritten;
|
|
6475
|
-
});
|
|
6476
|
-
return changed ? mapped : value;
|
|
6477
|
-
}
|
|
6478
|
-
if (typeof value === "object" && value !== null) {
|
|
6479
|
-
let changed = false;
|
|
6480
|
-
const result = {};
|
|
6481
|
-
for (const [k, v] of Object.entries(value)) {
|
|
6482
|
-
const rewritten = rewriteRootReferences(v);
|
|
6483
|
-
if (rewritten !== v) changed = true;
|
|
6484
|
-
result[k] = rewritten;
|
|
6485
|
-
}
|
|
6486
|
-
return changed ? result : value;
|
|
6487
|
-
}
|
|
6488
|
-
return value;
|
|
6489
|
-
}
|
|
6490
6548
|
var TokenPipeline = class {
|
|
6491
6549
|
options;
|
|
6492
6550
|
validationHandler;
|
|
6493
6551
|
resolverLoader;
|
|
6494
6552
|
tokenParser;
|
|
6495
6553
|
aliasResolver;
|
|
6496
|
-
lintRunner = null;
|
|
6497
|
-
lintConfigCache = null;
|
|
6498
6554
|
constructor(options = {}) {
|
|
6499
6555
|
this.options = options;
|
|
6500
6556
|
this.validationHandler = new ValidationHandler(options.validation);
|
|
@@ -6514,22 +6570,22 @@ var TokenPipeline = class {
|
|
|
6514
6570
|
* 6. Parse and flatten token structure
|
|
6515
6571
|
* 7. Resolve alias references
|
|
6516
6572
|
* 8. Strip $root from token names/paths (DTCG structural mechanism, transparent in output)
|
|
6517
|
-
* 9.
|
|
6518
|
-
* 10. Apply
|
|
6519
|
-
* 11. Apply transforms (if provided) — runs on the already-filtered token set
|
|
6573
|
+
* 9. Apply filters (if provided) — runs first to remove tokens before transforms
|
|
6574
|
+
* 10. Apply transforms (if provided) — runs on the already-filtered token set
|
|
6520
6575
|
*
|
|
6521
6576
|
* Each stage is explicitly typed to ensure correct order and prevent temporal coupling.
|
|
6522
6577
|
*
|
|
6578
|
+
* Note: Linting is handled separately via runLintOnPermutations() to enable
|
|
6579
|
+
* deduplication across multiple permutations.
|
|
6580
|
+
*
|
|
6523
6581
|
* @param resolver - Either a file path (string) or an inline ResolverDocument object
|
|
6524
6582
|
* @param modifierInputs - Modifier values for this permutation
|
|
6525
6583
|
* @param transformList - Optional transforms to apply
|
|
6526
6584
|
* @param preprocessorList - Optional preprocessors to apply
|
|
6527
6585
|
* @param filterList - Optional filters to apply before transforms
|
|
6528
|
-
* @
|
|
6529
|
-
* @returns Final tokens, resolution engine, and lint result
|
|
6586
|
+
* @returns Final tokens and resolution engine
|
|
6530
6587
|
*/
|
|
6531
|
-
async resolve(resolver, modifierInputs, transformList, preprocessorList, filterList
|
|
6532
|
-
const effectiveLintConfig = lintConfig ?? this.options.lint;
|
|
6588
|
+
async resolve(resolver, modifierInputs, transformList, preprocessorList, filterList) {
|
|
6533
6589
|
const stage1 = await this.loadResolver(resolver);
|
|
6534
6590
|
const engine = this.createEngine(stage1);
|
|
6535
6591
|
const result = await this.runPipelineStages(
|
|
@@ -6537,14 +6593,12 @@ var TokenPipeline = class {
|
|
|
6537
6593
|
modifierInputs,
|
|
6538
6594
|
preprocessorList,
|
|
6539
6595
|
filterList,
|
|
6540
|
-
transformList
|
|
6541
|
-
effectiveLintConfig
|
|
6596
|
+
transformList
|
|
6542
6597
|
);
|
|
6543
6598
|
return {
|
|
6544
6599
|
tokens: result.tokens,
|
|
6545
6600
|
resolutionEngine: result.resolutionEngine,
|
|
6546
|
-
modifierInputs: result.modifierInputs
|
|
6547
|
-
lintResult: result.lintResult
|
|
6601
|
+
modifierInputs: result.modifierInputs
|
|
6548
6602
|
};
|
|
6549
6603
|
}
|
|
6550
6604
|
/**
|
|
@@ -6554,15 +6608,14 @@ var TokenPipeline = class {
|
|
|
6554
6608
|
* `resolveAllPermutations()` (parallel permutations) to keep the
|
|
6555
6609
|
* stage sequence defined in exactly one place.
|
|
6556
6610
|
*/
|
|
6557
|
-
async runPipelineStages(engine, modifierInputs, preprocessorList, filterList, transformList
|
|
6611
|
+
async runPipelineStages(engine, modifierInputs, preprocessorList, filterList, transformList) {
|
|
6558
6612
|
const rawTokens = await this.resolveTokens(engine, modifierInputs);
|
|
6559
6613
|
const preprocessed = await this.preprocessTokens(rawTokens, preprocessorList);
|
|
6560
6614
|
const refResolved = await this.resolveReferences(preprocessed);
|
|
6561
6615
|
const flattened = this.flattenTokens(refResolved);
|
|
6562
6616
|
const aliasResolved = this.resolveAliases(flattened);
|
|
6563
6617
|
const rootStripped = this.stripRootTokenNames(aliasResolved);
|
|
6564
|
-
const
|
|
6565
|
-
const filtered = this.applyFilterStage(linted, filterList);
|
|
6618
|
+
const filtered = this.applyFilterStage(rootStripped, filterList);
|
|
6566
6619
|
return this.applyTransformStage(filtered, transformList);
|
|
6567
6620
|
}
|
|
6568
6621
|
/**
|
|
@@ -6676,45 +6729,8 @@ var TokenPipeline = class {
|
|
|
6676
6729
|
return { ...stage, aliasResolvedTokens: result };
|
|
6677
6730
|
}
|
|
6678
6731
|
/**
|
|
6679
|
-
* Stage 9: Run lint rules against tokens
|
|
6680
|
-
*
|
|
6681
|
-
* Linting runs after alias resolution and $root stripping but before
|
|
6682
|
-
* filters and transforms. This ensures rules see the full token set
|
|
6683
|
-
* with resolved values.
|
|
6684
|
-
*/
|
|
6685
|
-
async runLintStage(stage, lintConfig) {
|
|
6686
|
-
if (!lintConfig?.enabled) {
|
|
6687
|
-
return stage;
|
|
6688
|
-
}
|
|
6689
|
-
if (!this.lintRunner || !this.isLintConfigEqual(this.lintConfigCache, lintConfig)) {
|
|
6690
|
-
this.lintRunner = new LintRunner({
|
|
6691
|
-
plugins: lintConfig.plugins,
|
|
6692
|
-
rules: lintConfig.rules,
|
|
6693
|
-
onWarn: (msg) => this.validationHandler.warn(msg)
|
|
6694
|
-
});
|
|
6695
|
-
this.lintConfigCache = lintConfig;
|
|
6696
|
-
}
|
|
6697
|
-
const lintResult = await this.lintRunner.run(stage.aliasResolvedTokens);
|
|
6698
|
-
if (lintResult.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6699
|
-
throw new exports.LintError(lintResult.issues);
|
|
6700
|
-
}
|
|
6701
|
-
for (const issue of lintResult.issues.filter((i) => i.severity === "warn")) {
|
|
6702
|
-
this.validationHandler.warn(
|
|
6703
|
-
`[${issue.ruleId}] ${issue.message} (token: ${issue.tokenName})`
|
|
6704
|
-
);
|
|
6705
|
-
}
|
|
6706
|
-
return { ...stage, lintResult };
|
|
6707
|
-
}
|
|
6708
|
-
isLintConfigEqual(a, b) {
|
|
6709
|
-
if (!a || !b) return false;
|
|
6710
|
-
if (a.enabled !== b.enabled) return false;
|
|
6711
|
-
if (a.failOnError !== b.failOnError) return false;
|
|
6712
|
-
if (a.plugins !== b.plugins) return false;
|
|
6713
|
-
if (a.rules !== b.rules) return false;
|
|
6714
|
-
return true;
|
|
6715
|
-
}
|
|
6716
6732
|
/**
|
|
6717
|
-
* Stage
|
|
6733
|
+
* Stage 9: Apply filters to tokens after alias resolution and $root stripping
|
|
6718
6734
|
*/
|
|
6719
6735
|
applyFilterStage(stage, filterList) {
|
|
6720
6736
|
let tokens = stage.aliasResolvedTokens;
|
|
@@ -6724,7 +6740,7 @@ var TokenPipeline = class {
|
|
|
6724
6740
|
return { ...stage, aliasResolvedTokens: tokens };
|
|
6725
6741
|
}
|
|
6726
6742
|
/**
|
|
6727
|
-
* Stage
|
|
6743
|
+
* Stage 10: Apply transforms to the filtered token set
|
|
6728
6744
|
*/
|
|
6729
6745
|
applyTransformStage(stage, transformList) {
|
|
6730
6746
|
let tokens = stage.aliasResolvedTokens;
|
|
@@ -6758,10 +6774,8 @@ var TokenPipeline = class {
|
|
|
6758
6774
|
* @param transformList - Optional transforms to apply
|
|
6759
6775
|
* @param preprocessorList - Optional preprocessors to apply
|
|
6760
6776
|
* @param filterList - Optional filters to apply before transforms
|
|
6761
|
-
* @param lintConfig - Optional lint configuration for this run
|
|
6762
6777
|
*/
|
|
6763
|
-
async resolveAllPermutations(resolver, transformList, preprocessorList, filterList
|
|
6764
|
-
const effectiveLintConfig = lintConfig ?? this.options.lint;
|
|
6778
|
+
async resolveAllPermutations(resolver, transformList, preprocessorList, filterList) {
|
|
6765
6779
|
const stage1 = await this.loadResolver(resolver);
|
|
6766
6780
|
const discoveryEngine = this.createEngine(stage1);
|
|
6767
6781
|
const permutationInputs = discoveryEngine.resolutionEngine.generatePermutations();
|
|
@@ -6774,17 +6788,39 @@ var TokenPipeline = class {
|
|
|
6774
6788
|
modifierInputs,
|
|
6775
6789
|
preprocessorList,
|
|
6776
6790
|
filterList,
|
|
6777
|
-
transformList
|
|
6778
|
-
effectiveLintConfig
|
|
6791
|
+
transformList
|
|
6779
6792
|
);
|
|
6780
6793
|
return {
|
|
6781
6794
|
tokens: result.tokens,
|
|
6782
|
-
modifierInputs: result.modifierInputs
|
|
6783
|
-
lintResult: result.lintResult
|
|
6795
|
+
modifierInputs: result.modifierInputs
|
|
6784
6796
|
};
|
|
6785
6797
|
})
|
|
6786
6798
|
);
|
|
6787
6799
|
}
|
|
6800
|
+
/**
|
|
6801
|
+
* Run lint on tokens from multiple permutations with deduplication
|
|
6802
|
+
*
|
|
6803
|
+
* This runs lint once on all permutation token sets combined and deduplicates
|
|
6804
|
+
* issues. Use this after resolveAllPermutations() for consistent lint behavior.
|
|
6805
|
+
*/
|
|
6806
|
+
async runLintOnPermutations(permutationTokens, lintConfig) {
|
|
6807
|
+
if (!lintConfig.enabled) {
|
|
6808
|
+
return { issues: [], errorCount: 0, warningCount: 0 };
|
|
6809
|
+
}
|
|
6810
|
+
const runner = new LintRunner({
|
|
6811
|
+
plugins: lintConfig.plugins,
|
|
6812
|
+
rules: lintConfig.rules,
|
|
6813
|
+
onWarn: (msg) => this.validationHandler.warn(msg)
|
|
6814
|
+
});
|
|
6815
|
+
const result = await runner.runMultiple(permutationTokens);
|
|
6816
|
+
for (const issue of result.issues.filter((i) => i.severity === "warn")) {
|
|
6817
|
+
this.validationHandler.warn(`[${issue.ruleId}] ${issue.message} (token: ${issue.tokenName})`);
|
|
6818
|
+
}
|
|
6819
|
+
if (result.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6820
|
+
throw new exports.LintError(result.issues);
|
|
6821
|
+
}
|
|
6822
|
+
return result;
|
|
6823
|
+
}
|
|
6788
6824
|
/**
|
|
6789
6825
|
* Apply preprocessors to raw tokens
|
|
6790
6826
|
*/
|
|
@@ -6874,12 +6910,19 @@ async function resolveTokens(resolver, modifierInputs = {}, validation) {
|
|
|
6874
6910
|
async function lint(options) {
|
|
6875
6911
|
const { resolver, modifierInputs = {}, validation, ...lintConfig } = options;
|
|
6876
6912
|
const pipeline = createPipeline({ validation });
|
|
6877
|
-
const tokens = await resolvePipeline(pipeline, resolver, modifierInputs);
|
|
6878
6913
|
const runner = new LintRunner({
|
|
6879
6914
|
...lintConfig,
|
|
6880
6915
|
failOnError: lintConfig.failOnError ?? true
|
|
6881
6916
|
});
|
|
6882
|
-
|
|
6917
|
+
let tokenSets;
|
|
6918
|
+
if (Object.keys(modifierInputs).length > 0) {
|
|
6919
|
+
const { tokens } = await pipeline.resolve(resolver, modifierInputs);
|
|
6920
|
+
tokenSets = [tokens];
|
|
6921
|
+
} else {
|
|
6922
|
+
const permutations = await pipeline.resolveAllPermutations(resolver);
|
|
6923
|
+
tokenSets = permutations.map((p) => p.tokens);
|
|
6924
|
+
}
|
|
6925
|
+
const result = await runner.runMultiple(tokenSets);
|
|
6883
6926
|
if (result.errorCount > 0 && lintConfig.failOnError !== false) {
|
|
6884
6927
|
throw new exports.LintError(result.issues);
|
|
6885
6928
|
}
|