dispersa 0.2.0 → 0.3.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/README.md +44 -18
- package/dist/builders.cjs +119 -12
- package/dist/builders.cjs.map +1 -1
- package/dist/builders.d.cts +5 -5
- package/dist/builders.d.ts +5 -5
- package/dist/builders.js +119 -12
- package/dist/builders.js.map +1 -1
- package/dist/filters.cjs.map +1 -1
- package/dist/filters.d.cts +13 -16
- package/dist/filters.d.ts +13 -16
- package/dist/filters.js.map +1 -1
- package/dist/{index-CePv_bgv.d.cts → index-BkvV7Z54.d.cts} +4 -4
- package/dist/{index-BP52gB00.d.ts → index-DJ_UHSQG.d.ts} +4 -4
- package/dist/index.cjs +119 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +119 -12
- package/dist/index.js.map +1 -1
- package/dist/preprocessors.d.cts +2 -2
- package/dist/preprocessors.d.ts +2 -2
- package/dist/renderers.d.cts +6 -6
- package/dist/renderers.d.ts +6 -6
- package/dist/transforms.d.cts +2 -2
- package/dist/transforms.d.ts +2 -2
- package/dist/{types-DM5faYUn.d.cts → types-BAv39mum.d.cts} +1 -1
- package/dist/{types-C1GpiJ6q.d.ts → types-Bc0kA7De.d.cts} +10 -10
- package/dist/{types-C1GpiJ6q.d.cts → types-Bc0kA7De.d.ts} +10 -10
- package/dist/{types-Cl-1UYGD.d.ts → types-BzNcG-rI.d.ts} +1 -1
- package/dist/{types-DJH6_4U9.d.ts → types-CZb19kiq.d.ts} +1 -1
- package/dist/{types-DbufGPrb.d.cts → types-CussyWwe.d.cts} +1 -1
- package/dist/{types-DdPWYkgh.d.ts → types-CzHa7YkW.d.ts} +1 -1
- package/dist/{types-BDY1xBmD.d.cts → types-DWKq-eJj.d.cts} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,11 +118,11 @@ for (const output of result.outputs) {
|
|
|
118
118
|
}
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
> For file-based tokens, define JSON files and reference them with `$ref` in your resolver document. See the [`
|
|
121
|
+
> For file-based tokens, define JSON files and reference them with `$ref` in your resolver document. See the [`typescript-starter` example](./examples/typescript-starter/) for a complete setup.
|
|
122
122
|
|
|
123
123
|
## Output formats
|
|
124
124
|
|
|
125
|
-
Dispersa ships
|
|
125
|
+
Dispersa ships five builder functions. Each returns an `OutputConfig` that can be passed to `build()`.
|
|
126
126
|
|
|
127
127
|
### `css(config)`
|
|
128
128
|
|
|
@@ -139,6 +139,7 @@ Renders CSS custom properties.
|
|
|
139
139
|
| `minify` | `boolean` | `false` | Minify output |
|
|
140
140
|
| `transforms` | `Transform[]` | -- | Per-output transforms |
|
|
141
141
|
| `filters` | `Filter[]` | -- | Per-output filters |
|
|
142
|
+
| `hooks` | `LifecycleHooks` | -- | Per-output lifecycle hooks |
|
|
142
143
|
|
|
143
144
|
### `json(config)`
|
|
144
145
|
|
|
@@ -154,6 +155,7 @@ Renders JSON output.
|
|
|
154
155
|
| `minify` | `boolean` | -- | Minify output |
|
|
155
156
|
| `transforms` | `Transform[]` | -- | Per-output transforms |
|
|
156
157
|
| `filters` | `Filter[]` | -- | Per-output filters |
|
|
158
|
+
| `hooks` | `LifecycleHooks` | -- | Per-output lifecycle hooks |
|
|
157
159
|
|
|
158
160
|
### `js(config)`
|
|
159
161
|
|
|
@@ -170,6 +172,25 @@ Renders JavaScript/TypeScript modules.
|
|
|
170
172
|
| `minify` | `boolean` | -- | Minify output |
|
|
171
173
|
| `transforms` | `Transform[]` | -- | Per-output transforms |
|
|
172
174
|
| `filters` | `Filter[]` | -- | Per-output filters |
|
|
175
|
+
| `hooks` | `LifecycleHooks` | -- | Per-output lifecycle hooks |
|
|
176
|
+
|
|
177
|
+
### `tailwind(config)`
|
|
178
|
+
|
|
179
|
+
Renders Tailwind CSS v4 `@theme` blocks.
|
|
180
|
+
|
|
181
|
+
| Option | Type | Default | Description |
|
|
182
|
+
| --------------- | ------------------------------ | ---------- | -------------------------------------------- |
|
|
183
|
+
| `name` | `string` | -- | Unique output identifier |
|
|
184
|
+
| `file` | `string \| function` | -- | Output path (supports `{modifier}` patterns) |
|
|
185
|
+
| `preset` | `'bundle' \| 'standalone'` | `'bundle'` | Output preset |
|
|
186
|
+
| `includeImport` | `boolean` | -- | Include `@import "tailwindcss"` directive |
|
|
187
|
+
| `namespace` | `string` | -- | Prefix for CSS variable names |
|
|
188
|
+
| `selector` | `string \| SelectorFunction` | `':root'` | CSS selector |
|
|
189
|
+
| `mediaQuery` | `string \| MediaQueryFunction` | -- | Media query wrapper |
|
|
190
|
+
| `minify` | `boolean` | `false` | Minify output |
|
|
191
|
+
| `transforms` | `Transform[]` | -- | Per-output transforms |
|
|
192
|
+
| `filters` | `Filter[]` | -- | Per-output filters |
|
|
193
|
+
| `hooks` | `LifecycleHooks` | -- | Per-output lifecycle hooks |
|
|
173
194
|
|
|
174
195
|
### Experimental: native platform outputs
|
|
175
196
|
|
|
@@ -743,15 +764,16 @@ const dispersa = new Dispersa(options?: DispersaOptions)
|
|
|
743
764
|
|
|
744
765
|
### Subpath exports
|
|
745
766
|
|
|
746
|
-
| Export | Description
|
|
747
|
-
| ------------------------ |
|
|
748
|
-
| `dispersa` | `Dispersa` class, builder functions (`css`, `json`, `js`), types |
|
|
749
|
-
| `dispersa/transforms` | Built-in transform factories
|
|
750
|
-
| `dispersa/filters` | Built-in filter factories
|
|
751
|
-
| `dispersa/builders` | Output builder functions
|
|
752
|
-
| `dispersa/renderers` | Renderer types, `defineRenderer`, and `outputTree` helper
|
|
753
|
-
| `dispersa/preprocessors` | Preprocessor type
|
|
754
|
-
| `dispersa/errors` | Error classes (`DispersaError`, `TokenReferenceError`, etc.)
|
|
767
|
+
| Export | Description |
|
|
768
|
+
| ------------------------ | ---------------------------------------------------------------------------- |
|
|
769
|
+
| `dispersa` | `Dispersa` class, builder functions (`css`, `json`, `js`, `tailwind`), types |
|
|
770
|
+
| `dispersa/transforms` | Built-in transform factories |
|
|
771
|
+
| `dispersa/filters` | Built-in filter factories |
|
|
772
|
+
| `dispersa/builders` | Output builder functions |
|
|
773
|
+
| `dispersa/renderers` | Renderer types, `defineRenderer`, and `outputTree` helper |
|
|
774
|
+
| `dispersa/preprocessors` | Preprocessor type |
|
|
775
|
+
| `dispersa/errors` | Error classes (`DispersaError`, `TokenReferenceError`, etc.) |
|
|
776
|
+
| `dispersa/config` | `defineConfig` helper for CLI config files |
|
|
755
777
|
|
|
756
778
|
Everything outside these entry points is internal and not a stable API contract.
|
|
757
779
|
|
|
@@ -774,13 +796,17 @@ Resolver -> Preprocessors -> $ref resolution -> Parse/flatten -> Alias resolutio
|
|
|
774
796
|
|
|
775
797
|
See [`examples/`](./examples/) for complete working projects. Suggested learning path:
|
|
776
798
|
|
|
777
|
-
| Example
|
|
778
|
-
|
|
|
779
|
-
| [`
|
|
780
|
-
| [`
|
|
781
|
-
| [`
|
|
782
|
-
| [`
|
|
783
|
-
| [`
|
|
799
|
+
| Example | Focus |
|
|
800
|
+
| ------------------------------------------------------ | --------------------------------------------------------- |
|
|
801
|
+
| [`typescript-starter`](./examples/typescript-starter/) | Programmatic build script with themed CSS |
|
|
802
|
+
| [`cli-starter`](./examples/cli-starter/) | Config-file workflow using the dispersa CLI |
|
|
803
|
+
| [`in-memory`](./examples/in-memory/) | In-memory mode with inline tokens |
|
|
804
|
+
| [`custom-plugins`](./examples/custom-plugins/) | Custom transforms, filters, and renderers |
|
|
805
|
+
| [`multi-format`](./examples/multi-format/) | Multi-modifier system with all output formats |
|
|
806
|
+
| [`multi-brand`](./examples/multi-brand/) | Multi-brand, multi-platform at scale |
|
|
807
|
+
| [`multi-platform`](./examples/multi-platform/) | CSS, Tailwind, iOS, and Android from one set |
|
|
808
|
+
| [`split-by-type`](./examples/split-by-type/) | Filtered outputs split by token category |
|
|
809
|
+
| [`atlassian-semantic`](./examples/atlassian-semantic/) | Semantic tokens with density, motion, and theme modifiers |
|
|
784
810
|
|
|
785
811
|
## License
|
|
786
812
|
|
package/dist/builders.cjs
CHANGED
|
@@ -210,32 +210,55 @@ function filterTokensBySource(tokens, expectedSource) {
|
|
|
210
210
|
}
|
|
211
211
|
return filtered;
|
|
212
212
|
}
|
|
213
|
-
function
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
function filterTokensFromSets(tokens) {
|
|
214
|
+
const filtered = {};
|
|
215
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
216
|
+
const hasModifierSource = typeof token._sourceModifier === "string" && token._sourceModifier !== "";
|
|
217
|
+
if (!hasModifierSource) {
|
|
218
|
+
filtered[name] = token;
|
|
219
|
+
}
|
|
217
220
|
}
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
return filtered;
|
|
222
|
+
}
|
|
223
|
+
function resolveBaseFileName(fileName, defaults) {
|
|
224
|
+
if (typeof fileName === "function") {
|
|
225
|
+
return fileName({ ...defaults, _base: "true" });
|
|
226
|
+
}
|
|
227
|
+
if (/\{.+?\}/.test(fileName)) {
|
|
228
|
+
const baseInputs = Object.fromEntries(Object.keys(defaults).map((k) => [k, "base"]));
|
|
229
|
+
return collapseBaseSegments(interpolatePattern(fileName, baseInputs));
|
|
230
|
+
}
|
|
231
|
+
const extMatch = fileName.match(/(\.[^.]+)$/);
|
|
232
|
+
const extension = extMatch ? extMatch[1] : "";
|
|
233
|
+
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
234
|
+
return `${baseName}-base${extension}`;
|
|
235
|
+
}
|
|
236
|
+
function collapseBaseSegments(value) {
|
|
237
|
+
let result = value;
|
|
238
|
+
let previous = "";
|
|
239
|
+
while (result !== previous) {
|
|
240
|
+
previous = result;
|
|
241
|
+
result = result.replace(/\bbase([/-])base\b/, "base");
|
|
220
242
|
}
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
function interpolatePattern(pattern, modifierInputs) {
|
|
246
|
+
let result = pattern;
|
|
221
247
|
for (const [key, value] of Object.entries(modifierInputs)) {
|
|
222
248
|
result = result.replaceAll(`{${key}}`, value);
|
|
223
249
|
}
|
|
224
250
|
return result;
|
|
225
251
|
}
|
|
226
|
-
function resolveFileName(fileName, modifierInputs
|
|
252
|
+
function resolveFileName(fileName, modifierInputs) {
|
|
227
253
|
if (typeof fileName === "function") {
|
|
228
254
|
return fileName(modifierInputs);
|
|
229
255
|
}
|
|
230
256
|
if (/\{.+?\}/.test(fileName)) {
|
|
231
|
-
return interpolatePattern(fileName, modifierInputs
|
|
257
|
+
return interpolatePattern(fileName, modifierInputs);
|
|
232
258
|
}
|
|
233
259
|
const extMatch = fileName.match(/(\.[^.]+)$/);
|
|
234
260
|
const extension = extMatch ? extMatch[1] : "";
|
|
235
261
|
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
236
|
-
if (modifierName !== void 0 && context !== void 0) {
|
|
237
|
-
return `${baseName}-${modifierName}-${context}${extension}`;
|
|
238
|
-
}
|
|
239
262
|
const modifierSuffix = Object.entries(modifierInputs).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}-${value}`).join("-");
|
|
240
263
|
if (modifierSuffix) {
|
|
241
264
|
return `${baseName}-${modifierSuffix}${extension}`;
|
|
@@ -1350,6 +1373,33 @@ function collectRemainder(tokens, included) {
|
|
|
1350
1373
|
}
|
|
1351
1374
|
return result;
|
|
1352
1375
|
}
|
|
1376
|
+
function buildSetLayerBlocks(tokens, resolver) {
|
|
1377
|
+
const blocks = [];
|
|
1378
|
+
const included = /* @__PURE__ */ new Set();
|
|
1379
|
+
const addBlock = (key, blockTokens, description) => {
|
|
1380
|
+
if (Object.keys(blockTokens).length === 0) {
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
for (const k of Object.keys(blockTokens)) {
|
|
1384
|
+
included.add(k);
|
|
1385
|
+
}
|
|
1386
|
+
blocks.push({ key, description, tokens: blockTokens });
|
|
1387
|
+
};
|
|
1388
|
+
for (const item of resolver.resolutionOrder) {
|
|
1389
|
+
const ref = item.$ref;
|
|
1390
|
+
if (typeof ref !== "string" || !ref.startsWith("#/sets/")) {
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
const setName = ref.slice("#/sets/".length);
|
|
1394
|
+
addBlock(
|
|
1395
|
+
`Set: ${setName}`,
|
|
1396
|
+
collectSetTokens(tokens, setName, included),
|
|
1397
|
+
resolver.sets?.[setName]?.description
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
addBlock("Unattributed", collectRemainder(tokens, included));
|
|
1401
|
+
return blocks;
|
|
1402
|
+
}
|
|
1353
1403
|
function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
|
|
1354
1404
|
const blocks = [];
|
|
1355
1405
|
const included = /* @__PURE__ */ new Set();
|
|
@@ -2070,6 +2120,10 @@ var CssRenderer = class _CssRenderer {
|
|
|
2070
2120
|
throw new ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
|
|
2071
2121
|
}
|
|
2072
2122
|
const files = {};
|
|
2123
|
+
const baseResult = await this.buildModifierBaseFile(context, options);
|
|
2124
|
+
if (baseResult) {
|
|
2125
|
+
files[baseResult.fileName] = baseResult.content;
|
|
2126
|
+
}
|
|
2073
2127
|
for (const [modifierName, modifierDef] of Object.entries(context.resolver.modifiers)) {
|
|
2074
2128
|
for (const contextValue of Object.keys(modifierDef.contexts)) {
|
|
2075
2129
|
const result = await this.buildModifierContextFile(
|
|
@@ -2085,6 +2139,59 @@ var CssRenderer = class _CssRenderer {
|
|
|
2085
2139
|
}
|
|
2086
2140
|
return { kind: "outputTree", files };
|
|
2087
2141
|
}
|
|
2142
|
+
async buildModifierBaseFile(context, options) {
|
|
2143
|
+
const basePermutation = context.permutations.find(
|
|
2144
|
+
({ modifierInputs }) => this.isBasePermutation(modifierInputs, context.meta.defaults)
|
|
2145
|
+
);
|
|
2146
|
+
if (!basePermutation) {
|
|
2147
|
+
return void 0;
|
|
2148
|
+
}
|
|
2149
|
+
const setTokens = filterTokensFromSets(basePermutation.tokens);
|
|
2150
|
+
if (Object.keys(setTokens).length === 0) {
|
|
2151
|
+
return void 0;
|
|
2152
|
+
}
|
|
2153
|
+
const setBlocks = buildSetLayerBlocks(setTokens, context.resolver);
|
|
2154
|
+
if (setBlocks.length === 0) {
|
|
2155
|
+
return void 0;
|
|
2156
|
+
}
|
|
2157
|
+
const modifiers = context.resolver.modifiers;
|
|
2158
|
+
const firstModifierName = Object.keys(modifiers)[0] ?? "";
|
|
2159
|
+
const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
|
|
2160
|
+
const baseModifierInputs = { ...context.meta.defaults };
|
|
2161
|
+
const selector = resolveSelector(
|
|
2162
|
+
options.selector,
|
|
2163
|
+
firstModifierName,
|
|
2164
|
+
firstModifierContext,
|
|
2165
|
+
true,
|
|
2166
|
+
baseModifierInputs
|
|
2167
|
+
);
|
|
2168
|
+
const mediaQuery = resolveMediaQuery(
|
|
2169
|
+
options.mediaQuery,
|
|
2170
|
+
firstModifierName,
|
|
2171
|
+
firstModifierContext,
|
|
2172
|
+
true,
|
|
2173
|
+
baseModifierInputs
|
|
2174
|
+
);
|
|
2175
|
+
const referenceTokens = basePermutation.tokens;
|
|
2176
|
+
const cssBlocks = [];
|
|
2177
|
+
for (const block of setBlocks) {
|
|
2178
|
+
const cleanTokens = stripInternalMetadata(block.tokens);
|
|
2179
|
+
const css2 = await this.formatTokens(cleanTokens, {
|
|
2180
|
+
selector,
|
|
2181
|
+
mediaQuery,
|
|
2182
|
+
minify: options.minify ?? false,
|
|
2183
|
+
preserveReferences: options.preserveReferences ?? false,
|
|
2184
|
+
referenceTokens
|
|
2185
|
+
});
|
|
2186
|
+
const header = block.description ? `/* ${block.key} */
|
|
2187
|
+
/* ${block.description} */` : `/* ${block.key} */`;
|
|
2188
|
+
cssBlocks.push(`${header}
|
|
2189
|
+
${css2}`);
|
|
2190
|
+
}
|
|
2191
|
+
const content = cssBlocks.join("\n");
|
|
2192
|
+
const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
|
|
2193
|
+
return { fileName, content };
|
|
2194
|
+
}
|
|
2088
2195
|
collectTokensForModifierContext(modifierName, contextValue, permutations) {
|
|
2089
2196
|
const expectedSource = `${modifierName}-${contextValue}`;
|
|
2090
2197
|
let tokensFromSource = {};
|
|
@@ -2131,7 +2238,7 @@ var CssRenderer = class _CssRenderer {
|
|
|
2131
2238
|
preserveReferences: options.preserveReferences ?? false,
|
|
2132
2239
|
referenceTokens
|
|
2133
2240
|
});
|
|
2134
|
-
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs
|
|
2241
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
2135
2242
|
outputName: context.output.name,
|
|
2136
2243
|
extension: "css",
|
|
2137
2244
|
modifierInputs,
|