@pitlane/theme 0.2.0 → 0.3.1
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/CHANGELOG.md +127 -0
- package/README.md +66 -28
- package/dist/default.d.mts +605 -0
- package/dist/default.mjs +584 -0
- package/dist/dtcg.d.mts +127 -0
- package/dist/dtcg.mjs +468 -0
- package/dist/index.d.mts +76 -348
- package/dist/index.mjs +54 -458
- package/dist/schema-CRP607Pg.d.mts +320 -0
- package/dist/schema-JmfNnMzr.mjs +572 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +2 -0
- package/dist/theme-CaHfWYnM.d.mts +280 -0
- package/dist/theme-iDUjQhE0.mjs +253 -0
- package/package.json +58 -48
package/dist/dtcg.d.mts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { i as SchemaTag } from "./schema-CRP607Pg.mjs";
|
|
2
|
+
import { l as ThemeInit, r as ThemeResult, t as ThemeComponent } from "./theme-CaHfWYnM.mjs";
|
|
3
|
+
//#region src/dtcg.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A JSON-compatible W3C Design Tokens Community Group document.
|
|
6
|
+
*
|
|
7
|
+
* The DTCG specification permits arbitrary extension keys, so this
|
|
8
|
+
* deliberately remains open rather than attempting to model every
|
|
9
|
+
* standardized and vendor extension.
|
|
10
|
+
*/
|
|
11
|
+
type DTCGDocument = Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* The runtime-shaped init {@link fromDTCG} derives from a DTCG document.
|
|
14
|
+
*
|
|
15
|
+
* Its `schema` and `tokens` use the broad {@link ThemeInit} member types: a
|
|
16
|
+
* JSON document cannot produce the literal information required to brand a
|
|
17
|
+
* token accessor. Generate a checked-in schema and token module from the
|
|
18
|
+
* document when a typed accessor is required.
|
|
19
|
+
*/
|
|
20
|
+
interface DTCGThemeInit {
|
|
21
|
+
/** The derived runtime schema tree. */
|
|
22
|
+
schema: object;
|
|
23
|
+
/** The bare token values described by {@link schema}. */
|
|
24
|
+
tokens: object;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Pitlane's extension payload for a value DTCG cannot represent.
|
|
28
|
+
*
|
|
29
|
+
* The extension is stored at `$extensions["tools.pitlane"]`. The surrounding
|
|
30
|
+
* node deliberately has no `$value`, making it a valid DTCG group that a
|
|
31
|
+
* strict consumer can ignore rather than a token carrying an invalid surrogate
|
|
32
|
+
* value.
|
|
33
|
+
*/
|
|
34
|
+
interface PitlaneDTCGExtension {
|
|
35
|
+
/** The source schema tag, including Pitlane-only `any` and `scale`. */
|
|
36
|
+
type: SchemaTag;
|
|
37
|
+
/** The original authoring value, with exact accessor references reversed. */
|
|
38
|
+
value: unknown;
|
|
39
|
+
/** Why the value has no standard DTCG representation. */
|
|
40
|
+
reason: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* DTCG documents exported from a theme.
|
|
44
|
+
*/
|
|
45
|
+
interface DTCGExport {
|
|
46
|
+
/** The theme's base document. */
|
|
47
|
+
document: DTCGDocument;
|
|
48
|
+
/** One partial document of token overrides for each theme mode. */
|
|
49
|
+
modes: Record<string, DTCGDocument>;
|
|
50
|
+
/** The number of base and mode values preserved only in Pitlane extensions. */
|
|
51
|
+
inexpressible: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Converts a W3C DTCG 2025.10 document into the schema tree and bare token
|
|
55
|
+
* tree {@link createTheme} accepts.
|
|
56
|
+
*
|
|
57
|
+
* A token resolves its type in DTCG order: its own `$type`, the resolved type
|
|
58
|
+
* of a whole-value `{reference}`, then the nearest ancestor group's `$type`.
|
|
59
|
+
* `$description`, `$deprecated`, and `$extensions` are intentionally discarded
|
|
60
|
+
* until schema factories gain a metadata options bag. Typography tokens are not
|
|
61
|
+
* supported because one typography value would need several custom properties.
|
|
62
|
+
*
|
|
63
|
+
* Runtime JSON cannot preserve literal paths, so using this init with
|
|
64
|
+
* {@link createTheme} produces an unbranded accessor. Generate TypeScript from
|
|
65
|
+
* the result for the typed path.
|
|
66
|
+
*
|
|
67
|
+
* @param document - A DTCG document using `$value`, `$type`, and dotted references
|
|
68
|
+
* @returns A runtime {@link ThemeInit}-shaped schema and token tree
|
|
69
|
+
* @throws ThemeError for malformed groups, unsupported typography, unknown types,
|
|
70
|
+
* unresolvable references, cycles, or malformed structured DTCG values
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { createTheme } from "@pitlane/theme";
|
|
75
|
+
* import { fromDTCG } from "@pitlane/theme/dtcg";
|
|
76
|
+
*
|
|
77
|
+
* let imported = fromDTCG({
|
|
78
|
+
* color: {
|
|
79
|
+
* $type: "color",
|
|
80
|
+
* white: { $value: "#fff" },
|
|
81
|
+
* page: { $value: "{color.white}" },
|
|
82
|
+
* },
|
|
83
|
+
* });
|
|
84
|
+
* let theme = createTheme(imported);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
declare function fromDTCG(document: DTCGDocument): DTCGThemeInit;
|
|
88
|
+
/**
|
|
89
|
+
* Exports a compiled theme, a published theme component, or the init behind
|
|
90
|
+
* either one to W3C DTCG 2025.10 documents.
|
|
91
|
+
*
|
|
92
|
+
* An export places `$type` on the lowest group whose complete descendant set
|
|
93
|
+
* has one expressible type; standard tokens below that group omit the redundant
|
|
94
|
+
* `$type`. Mixed groups put `$type` on their standard token leaves instead.
|
|
95
|
+
* Exact `var(--token-name)` values are converted back to `{token.path}` using
|
|
96
|
+
* the collector's variable-name map.
|
|
97
|
+
*
|
|
98
|
+
* DTCG cannot represent all CSS values this package accepts. Inexpressible
|
|
99
|
+
* values are preserved in `$extensions["tools.pitlane"]` as
|
|
100
|
+
* `{ type, value, reason }`, on an extension-only node with no `$value`; this
|
|
101
|
+
* keeps the standard document valid while preserving the original value for a
|
|
102
|
+
* caller that reports or handles the loss. `inexpressible` counts those nodes in
|
|
103
|
+
* the base document and every mode document.
|
|
104
|
+
*
|
|
105
|
+
* DTCG descriptions, deprecation metadata, and unrelated extensions are not in
|
|
106
|
+
* the authoring format and cannot be recovered.
|
|
107
|
+
*
|
|
108
|
+
* @param theme - A {@link ThemeResult}, its {@link ThemeComponent}, or its init
|
|
109
|
+
* @returns The base document, one partial document per mode, and the lossy-value count
|
|
110
|
+
* @throws ThemeError when the supplied init has an invalid token/schema structure
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import { createTheme } from "@pitlane/theme";
|
|
115
|
+
* import { toDTCG } from "@pitlane/theme/dtcg";
|
|
116
|
+
* import * as s from "@pitlane/theme/schema";
|
|
117
|
+
*
|
|
118
|
+
* let theme = createTheme({
|
|
119
|
+
* schema: { color: s.color() },
|
|
120
|
+
* tokens: { color: { white: "#fff" } },
|
|
121
|
+
* });
|
|
122
|
+
* let { document, inexpressible } = toDTCG(theme);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function toDTCG<T>(theme: ThemeInit | ThemeComponent<T> | ThemeResult<T>): DTCGExport;
|
|
126
|
+
//#endregion
|
|
127
|
+
export { DTCGDocument, DTCGExport, DTCGThemeInit, PitlaneDTCGExtension, fromDTCG, toDTCG };
|
package/dist/dtcg.mjs
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { D as referenceTarget, S as ThemeError, T as kebabSegment, b as transition, c as duration, d as gradient, f as group, i as border, l as easing, m as number, o as color, s as dimension, u as font, v as shadow, w as collectTokens, x as serializeValue, y as stroke } from "./schema-JmfNnMzr.mjs";
|
|
2
|
+
//#region src/brands.ts
|
|
3
|
+
/**
|
|
4
|
+
* The twelve DTCG token types, in canonical order.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
const TOKEN_TYPES = [
|
|
9
|
+
"color",
|
|
10
|
+
"dimension",
|
|
11
|
+
"duration",
|
|
12
|
+
"fontFamily",
|
|
13
|
+
"fontWeight",
|
|
14
|
+
"number",
|
|
15
|
+
"cubicBezier",
|
|
16
|
+
"shadow",
|
|
17
|
+
"border",
|
|
18
|
+
"transition",
|
|
19
|
+
"gradient",
|
|
20
|
+
"strokeStyle"
|
|
21
|
+
];
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/dtcg.ts
|
|
24
|
+
const DTCG_ALIAS_RE = /^\{([^{}]+)\}$/;
|
|
25
|
+
/** The token a DTCG `{a.b.c}` alias names, or `null`. */
|
|
26
|
+
function dtcgAlias(value) {
|
|
27
|
+
if (typeof value !== "string") return null;
|
|
28
|
+
let match = DTCG_ALIAS_RE.exec(value);
|
|
29
|
+
return match ? match[1] : null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Rewrites every DTCG alias in a value to its `var()` reference, including the
|
|
33
|
+
* ones inside a composite's sub-values. The authoring format has one reference
|
|
34
|
+
* form, so braces must not survive past this module.
|
|
35
|
+
*/
|
|
36
|
+
function convertAliases(value, key, varNameFor) {
|
|
37
|
+
let alias = dtcgAlias(value);
|
|
38
|
+
if (alias !== null) {
|
|
39
|
+
let varName = varNameFor(alias);
|
|
40
|
+
if (varName === void 0) throw new ThemeError(`"${key}" references unknown token "${alias}"`);
|
|
41
|
+
return `var(${varName})`;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(value)) return value.map((item) => convertAliases(item, key, varNameFor));
|
|
44
|
+
if (typeof value === "object" && value !== null) {
|
|
45
|
+
let out = {};
|
|
46
|
+
for (let [field, item] of Object.entries(value)) out[field] = convertAliases(item, key, varNameFor);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
const ACCESSOR_REFERENCE_RE = /^var\((--[a-z0-9-]+)\)$/;
|
|
52
|
+
const DIMENSION_RE = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|rem)$/;
|
|
53
|
+
const DURATION_RE = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(ms|s)$/;
|
|
54
|
+
const CUBIC_BEZIER_RE = /^cubic-bezier\(\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*\)$/;
|
|
55
|
+
const HEX_RE = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
56
|
+
/**
|
|
57
|
+
* Converts a W3C DTCG 2025.10 document into the schema tree and bare token
|
|
58
|
+
* tree {@link createTheme} accepts.
|
|
59
|
+
*
|
|
60
|
+
* A token resolves its type in DTCG order: its own `$type`, the resolved type
|
|
61
|
+
* of a whole-value `{reference}`, then the nearest ancestor group's `$type`.
|
|
62
|
+
* `$description`, `$deprecated`, and `$extensions` are intentionally discarded
|
|
63
|
+
* until schema factories gain a metadata options bag. Typography tokens are not
|
|
64
|
+
* supported because one typography value would need several custom properties.
|
|
65
|
+
*
|
|
66
|
+
* Runtime JSON cannot preserve literal paths, so using this init with
|
|
67
|
+
* {@link createTheme} produces an unbranded accessor. Generate TypeScript from
|
|
68
|
+
* the result for the typed path.
|
|
69
|
+
*
|
|
70
|
+
* @param document - A DTCG document using `$value`, `$type`, and dotted references
|
|
71
|
+
* @returns A runtime {@link ThemeInit}-shaped schema and token tree
|
|
72
|
+
* @throws ThemeError for malformed groups, unsupported typography, unknown types,
|
|
73
|
+
* unresolvable references, cycles, or malformed structured DTCG values
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { createTheme } from "@pitlane/theme";
|
|
78
|
+
* import { fromDTCG } from "@pitlane/theme/dtcg";
|
|
79
|
+
*
|
|
80
|
+
* let imported = fromDTCG({
|
|
81
|
+
* color: {
|
|
82
|
+
* $type: "color",
|
|
83
|
+
* white: { $value: "#fff" },
|
|
84
|
+
* page: { $value: "{color.white}" },
|
|
85
|
+
* },
|
|
86
|
+
* });
|
|
87
|
+
* let theme = createTheme(imported);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
function fromDTCG(document) {
|
|
91
|
+
let entries = /* @__PURE__ */ new Map();
|
|
92
|
+
let root = walkDocument(record(document, "$root"), [], void 0, entries);
|
|
93
|
+
let tags = resolveTags(entries);
|
|
94
|
+
let varNames = variableNames(entries.values());
|
|
95
|
+
let context = importReferenceContext(entries, tags, varNames);
|
|
96
|
+
return {
|
|
97
|
+
schema: schemaTree(root, tags),
|
|
98
|
+
tokens: tokenTree(root, tags, context, (key) => varNames.get(key))
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Exports a compiled theme, a published theme component, or the init behind
|
|
103
|
+
* either one to W3C DTCG 2025.10 documents.
|
|
104
|
+
*
|
|
105
|
+
* An export places `$type` on the lowest group whose complete descendant set
|
|
106
|
+
* has one expressible type; standard tokens below that group omit the redundant
|
|
107
|
+
* `$type`. Mixed groups put `$type` on their standard token leaves instead.
|
|
108
|
+
* Exact `var(--token-name)` values are converted back to `{token.path}` using
|
|
109
|
+
* the collector's variable-name map.
|
|
110
|
+
*
|
|
111
|
+
* DTCG cannot represent all CSS values this package accepts. Inexpressible
|
|
112
|
+
* values are preserved in `$extensions["tools.pitlane"]` as
|
|
113
|
+
* `{ type, value, reason }`, on an extension-only node with no `$value`; this
|
|
114
|
+
* keeps the standard document valid while preserving the original value for a
|
|
115
|
+
* caller that reports or handles the loss. `inexpressible` counts those nodes in
|
|
116
|
+
* the base document and every mode document.
|
|
117
|
+
*
|
|
118
|
+
* DTCG descriptions, deprecation metadata, and unrelated extensions are not in
|
|
119
|
+
* the authoring format and cannot be recovered.
|
|
120
|
+
*
|
|
121
|
+
* @param theme - A {@link ThemeResult}, its {@link ThemeComponent}, or its init
|
|
122
|
+
* @returns The base document, one partial document per mode, and the lossy-value count
|
|
123
|
+
* @throws ThemeError when the supplied init has an invalid token/schema structure
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { createTheme } from "@pitlane/theme";
|
|
128
|
+
* import { toDTCG } from "@pitlane/theme/dtcg";
|
|
129
|
+
* import * as s from "@pitlane/theme/schema";
|
|
130
|
+
*
|
|
131
|
+
* let theme = createTheme({
|
|
132
|
+
* schema: { color: s.color() },
|
|
133
|
+
* tokens: { color: { white: "#fff" } },
|
|
134
|
+
* });
|
|
135
|
+
* let { document, inexpressible } = toDTCG(theme);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function toDTCG(theme) {
|
|
139
|
+
let init = themeInit(theme);
|
|
140
|
+
let schema = init.schema;
|
|
141
|
+
let baseEntries = collectTokens(init.tokens, schema);
|
|
142
|
+
let base = exportDocument(baseEntries, baseEntries);
|
|
143
|
+
let modes = {};
|
|
144
|
+
let inexpressible = base.inexpressible;
|
|
145
|
+
for (let [name, mode] of Object.entries(init.modes ?? {})) {
|
|
146
|
+
let exported = exportDocument(collectTokens(mode.tokens, schema), baseEntries);
|
|
147
|
+
modes[name] = exported.document;
|
|
148
|
+
inexpressible += exported.inexpressible;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
document: base.document,
|
|
152
|
+
modes,
|
|
153
|
+
inexpressible
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function walkDocument(node, path, inheritedType, entries) {
|
|
157
|
+
let key = path.length === 0 ? "$root" : path.join(".");
|
|
158
|
+
let ownType = validateType(node.$type, key);
|
|
159
|
+
let effectiveType = ownType ?? inheritedType;
|
|
160
|
+
let group = {
|
|
161
|
+
ownType,
|
|
162
|
+
children: {}
|
|
163
|
+
};
|
|
164
|
+
for (let [childName, child] of Object.entries(node)) {
|
|
165
|
+
if (childName.startsWith("$")) continue;
|
|
166
|
+
let childPath = [...path, childName];
|
|
167
|
+
let childKey = childPath.join(".");
|
|
168
|
+
validateName(childName, childKey);
|
|
169
|
+
let childRecord = record(child, childKey);
|
|
170
|
+
if ("$value" in childRecord) {
|
|
171
|
+
let token = {
|
|
172
|
+
key: childKey,
|
|
173
|
+
path: childPath,
|
|
174
|
+
ownTag: validateType(childRecord.$type, childKey),
|
|
175
|
+
inheritedType: effectiveType,
|
|
176
|
+
value: childRecord.$value
|
|
177
|
+
};
|
|
178
|
+
group.children[childName] = token;
|
|
179
|
+
entries.set(childKey, token);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
group.children[childName] = walkDocument(childRecord, childPath, effectiveType, entries);
|
|
183
|
+
}
|
|
184
|
+
return group;
|
|
185
|
+
}
|
|
186
|
+
function resolveTags(entries) {
|
|
187
|
+
let tags = /* @__PURE__ */ new Map();
|
|
188
|
+
for (let key of entries.keys()) tags.set(key, resolveTag(key, entries, []));
|
|
189
|
+
return tags;
|
|
190
|
+
}
|
|
191
|
+
function resolveTag(key, entries, chain) {
|
|
192
|
+
if (chain.includes(key)) throw new ThemeError(`Alias cycle: ${[...chain, key].join(" → ")}`);
|
|
193
|
+
let entry = entries.get(key);
|
|
194
|
+
if (entry === void 0) throw new ThemeError(`"${chain[chain.length - 1] ?? key}" references unknown token "${key}"`);
|
|
195
|
+
if (entry.ownTag !== void 0) return entry.ownTag;
|
|
196
|
+
let alias = dtcgAlias(entry.value);
|
|
197
|
+
if (alias !== null) return resolveTag(alias, entries, [...chain, key]);
|
|
198
|
+
if (entry.inheritedType !== void 0) return entry.inheritedType;
|
|
199
|
+
throw new ThemeError(`"${key}" has no resolvable $type`);
|
|
200
|
+
}
|
|
201
|
+
function variableNames(entries) {
|
|
202
|
+
let byVarName = /* @__PURE__ */ new Map();
|
|
203
|
+
let byKey = /* @__PURE__ */ new Map();
|
|
204
|
+
for (let entry of entries) {
|
|
205
|
+
let varName = `--${entry.path.map(kebabSegment).join("-")}`;
|
|
206
|
+
let existing = byVarName.get(varName);
|
|
207
|
+
if (existing !== void 0) throw new ThemeError(`Tokens "${existing.key}" and "${entry.key}" both produce the CSS variable ${varName}`);
|
|
208
|
+
byVarName.set(varName, entry);
|
|
209
|
+
byKey.set(entry.key, varName);
|
|
210
|
+
}
|
|
211
|
+
return byKey;
|
|
212
|
+
}
|
|
213
|
+
function importReferenceContext(entries, tags, varNameByKey) {
|
|
214
|
+
return { varRefFor(key, from, expected) {
|
|
215
|
+
let target = entries.get(key);
|
|
216
|
+
if (target === void 0) throw new ThemeError(`"${from}" references unknown token "${key}"`);
|
|
217
|
+
let type = tags.get(target.key);
|
|
218
|
+
if (type !== expected) throw new ThemeError(`"${from}" references "${key}" of type "${type}" where "${expected}" is required`);
|
|
219
|
+
return `var(${varNameByKey.get(target.key)})`;
|
|
220
|
+
} };
|
|
221
|
+
}
|
|
222
|
+
function schemaTree(root, tags) {
|
|
223
|
+
let schema = {};
|
|
224
|
+
for (let [key, child] of Object.entries(root.children)) {
|
|
225
|
+
if (isRawToken(child)) {
|
|
226
|
+
schema[key] = schemaFor(tags.get(child.key));
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
let childSchema = groupSchema(child, root.ownType, tags);
|
|
230
|
+
if (childSchema === void 0) {
|
|
231
|
+
if (root.ownType === void 0) continue;
|
|
232
|
+
schema[key] = schemaFor(root.ownType);
|
|
233
|
+
} else if (child.ownType === void 0 && root.ownType !== void 0) schema[key] = group(schemaFor(root.ownType), childSchema);
|
|
234
|
+
else schema[key] = childSchema;
|
|
235
|
+
}
|
|
236
|
+
return schema;
|
|
237
|
+
}
|
|
238
|
+
function groupSchema(group$1, inherited, tags) {
|
|
239
|
+
let effective = group$1.ownType ?? inherited;
|
|
240
|
+
let children = {};
|
|
241
|
+
for (let [key, child] of Object.entries(group$1.children)) {
|
|
242
|
+
if (isRawToken(child)) {
|
|
243
|
+
let tag = tags.get(child.key);
|
|
244
|
+
if (tag !== effective) children[key] = schemaFor(tag);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
let childSchema = groupSchema(child, effective, tags);
|
|
248
|
+
if (childSchema !== void 0) children[key] = childSchema;
|
|
249
|
+
}
|
|
250
|
+
if (group$1.ownType === void 0) return Object.keys(children).length === 0 ? void 0 : children;
|
|
251
|
+
let self = schemaFor(group$1.ownType);
|
|
252
|
+
return Object.keys(children).length === 0 ? self : group(self, children);
|
|
253
|
+
}
|
|
254
|
+
function tokenTree(group, tags, context, varNameFor) {
|
|
255
|
+
let tokens = {};
|
|
256
|
+
for (let [key, child] of Object.entries(group.children)) if (isRawToken(child)) tokens[key] = authorValue(child, tags.get(child.key), context, varNameFor);
|
|
257
|
+
else tokens[key] = tokenTree(child, tags, context, varNameFor);
|
|
258
|
+
return tokens;
|
|
259
|
+
}
|
|
260
|
+
function authorValue(entry, tag, context, varNameFor) {
|
|
261
|
+
let value = convertAliases(entry.value, entry.key, varNameFor);
|
|
262
|
+
if (isTokenValue(value)) return value;
|
|
263
|
+
return serializeValue(tag, value, context, entry.key);
|
|
264
|
+
}
|
|
265
|
+
function schemaFor(tag) {
|
|
266
|
+
switch (tag) {
|
|
267
|
+
case "color": return color();
|
|
268
|
+
case "dimension": return dimension();
|
|
269
|
+
case "duration": return duration();
|
|
270
|
+
case "number": return number();
|
|
271
|
+
case "cubicBezier": return easing();
|
|
272
|
+
case "shadow": return shadow();
|
|
273
|
+
case "border": return border();
|
|
274
|
+
case "transition": return transition();
|
|
275
|
+
case "gradient": return gradient();
|
|
276
|
+
case "strokeStyle": return stroke();
|
|
277
|
+
case "fontFamily": return font.family();
|
|
278
|
+
case "fontWeight": return font.weight();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function exportDocument(entries, referenceEntries) {
|
|
282
|
+
let byVarName = new Map(referenceEntries.map((entry) => [entry.varName, entry]));
|
|
283
|
+
let root = { children: /* @__PURE__ */ new Map() };
|
|
284
|
+
let inexpressible = 0;
|
|
285
|
+
for (let entry of entries) {
|
|
286
|
+
let node = root;
|
|
287
|
+
for (let [index, key] of entry.path.entries()) {
|
|
288
|
+
let child = node.children.get(key);
|
|
289
|
+
if (child === void 0) {
|
|
290
|
+
child = { children: /* @__PURE__ */ new Map() };
|
|
291
|
+
node.children.set(key, child);
|
|
292
|
+
}
|
|
293
|
+
node = child;
|
|
294
|
+
if (index === entry.path.length - 1) {
|
|
295
|
+
let encoded = encodeToken(entry, byVarName);
|
|
296
|
+
node.token = encoded;
|
|
297
|
+
if (encoded.extension !== void 0) inexpressible += 1;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
document: emitDocument(root),
|
|
303
|
+
inexpressible
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function encodeToken(entry, byVarName) {
|
|
307
|
+
let value = reverseAccessorReference(entry.value, byVarName);
|
|
308
|
+
if (entry.kind === "untyped") return extensionToken("any", value, "Tokens declared with s.any() have no DTCG type");
|
|
309
|
+
let type = entry.kind === "scale" ? "dimension" : entry.type;
|
|
310
|
+
let encoded = encodeValue(type, value);
|
|
311
|
+
if (encoded === void 0) return extensionToken(entry.kind === "scale" ? "scale" : entry.type, value, entry.kind === "scale" ? "A scale token exports only its base dimension, not multiplier results" : inexpressibleReason(type));
|
|
312
|
+
return {
|
|
313
|
+
type,
|
|
314
|
+
value: encoded
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function encodeValue(type, value) {
|
|
318
|
+
if (referenceTarget(value) !== null || dtcgAlias(value) !== null) return value;
|
|
319
|
+
switch (type) {
|
|
320
|
+
case "color": return typeof value === "string" ? encodeHexColor(value) : void 0;
|
|
321
|
+
case "dimension": return typeof value === "string" ? encodeMeasure(value, DIMENSION_RE) : void 0;
|
|
322
|
+
case "duration": return typeof value === "string" ? encodeMeasure(value, DURATION_RE) : void 0;
|
|
323
|
+
case "fontFamily": return isFontFamily(value) ? value : void 0;
|
|
324
|
+
case "fontWeight": return typeof value === "number" || typeof value === "string" ? value : void 0;
|
|
325
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
326
|
+
case "cubicBezier": return encodeCubicBezier(value);
|
|
327
|
+
case "strokeStyle": return typeof value === "string" ? value : void 0;
|
|
328
|
+
case "shadow":
|
|
329
|
+
case "border":
|
|
330
|
+
case "transition":
|
|
331
|
+
case "gradient": return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function encodeHexColor(value) {
|
|
335
|
+
let match = HEX_RE.exec(value);
|
|
336
|
+
if (match === null) return void 0;
|
|
337
|
+
let hex = match[1];
|
|
338
|
+
let pairs = hex.length <= 4 ? (hex.match(/./g) ?? []).map((part) => `${part}${part}`) : hex.match(/.{2}/g);
|
|
339
|
+
let components = pairs.slice(0, 3).map((part) => Number.parseInt(part, 16) / 255);
|
|
340
|
+
let alpha = pairs[3] === void 0 ? void 0 : Number.parseInt(pairs[3], 16) / 255;
|
|
341
|
+
return {
|
|
342
|
+
colorSpace: "srgb",
|
|
343
|
+
components,
|
|
344
|
+
...alpha === void 0 ? {} : { alpha },
|
|
345
|
+
hex: value
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function encodeMeasure(value, expression) {
|
|
349
|
+
let match = expression.exec(value);
|
|
350
|
+
if (match === null) return void 0;
|
|
351
|
+
let amount = Number(match[1]);
|
|
352
|
+
return Number.isFinite(amount) ? {
|
|
353
|
+
value: amount,
|
|
354
|
+
unit: match[2]
|
|
355
|
+
} : void 0;
|
|
356
|
+
}
|
|
357
|
+
function encodeCubicBezier(value) {
|
|
358
|
+
if (Array.isArray(value) && value.length === 4 && value.every((part) => typeof part === "number" && Number.isFinite(part))) return value;
|
|
359
|
+
if (typeof value !== "string") return void 0;
|
|
360
|
+
let match = CUBIC_BEZIER_RE.exec(value);
|
|
361
|
+
if (match === null) return void 0;
|
|
362
|
+
let parts = match.slice(1).map(Number);
|
|
363
|
+
return parts.every(Number.isFinite) ? parts : void 0;
|
|
364
|
+
}
|
|
365
|
+
function extensionToken(type, value, reason) {
|
|
366
|
+
return { extension: {
|
|
367
|
+
type,
|
|
368
|
+
value,
|
|
369
|
+
reason
|
|
370
|
+
} };
|
|
371
|
+
}
|
|
372
|
+
function emitDocument(node, inheritedType) {
|
|
373
|
+
let type = uniformType(node);
|
|
374
|
+
let effectiveType = type ?? inheritedType;
|
|
375
|
+
let document = {};
|
|
376
|
+
if (type !== void 0 && type !== inheritedType) document.$type = type;
|
|
377
|
+
for (let [key, child] of node.children) {
|
|
378
|
+
if (child.token !== void 0) {
|
|
379
|
+
if (child.token.extension !== void 0) {
|
|
380
|
+
document[key] = { $extensions: { "tools.pitlane": child.token.extension } };
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
let token = { $value: child.token.value };
|
|
384
|
+
if (child.token.type !== effectiveType) token.$type = child.token.type;
|
|
385
|
+
document[key] = token;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
document[key] = emitDocument(child, effectiveType);
|
|
389
|
+
}
|
|
390
|
+
return document;
|
|
391
|
+
}
|
|
392
|
+
function uniformType(node) {
|
|
393
|
+
let uniform;
|
|
394
|
+
let isUniform = true;
|
|
395
|
+
let found = false;
|
|
396
|
+
function visit(child) {
|
|
397
|
+
if (!isUniform) return;
|
|
398
|
+
if (child.token !== void 0) {
|
|
399
|
+
let type = child.token.type;
|
|
400
|
+
if (type === void 0) {
|
|
401
|
+
isUniform = false;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (uniform !== void 0 && uniform !== type) {
|
|
405
|
+
isUniform = false;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
uniform = type;
|
|
409
|
+
found = true;
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
for (let nested of child.children.values()) visit(nested);
|
|
413
|
+
}
|
|
414
|
+
for (let child of node.children.values()) visit(child);
|
|
415
|
+
return isUniform && found ? uniform : void 0;
|
|
416
|
+
}
|
|
417
|
+
function reverseAccessorReference(value, byVarName) {
|
|
418
|
+
if (typeof value !== "string") return value;
|
|
419
|
+
let match = ACCESSOR_REFERENCE_RE.exec(value);
|
|
420
|
+
if (match === null) return value;
|
|
421
|
+
let target = byVarName.get(match[1]);
|
|
422
|
+
return target === void 0 ? value : `{${target.key}}`;
|
|
423
|
+
}
|
|
424
|
+
function themeInit(theme) {
|
|
425
|
+
if (typeof theme === "function") return theme.$theme;
|
|
426
|
+
if ("Theme" in theme) return theme.Theme.$theme;
|
|
427
|
+
return theme;
|
|
428
|
+
}
|
|
429
|
+
function validateType(value, key) {
|
|
430
|
+
if (value === void 0) return void 0;
|
|
431
|
+
if (value === "typography") throw new ThemeError(`"${key}": typography tokens are not supported in v1`);
|
|
432
|
+
if (!TOKEN_TYPES.includes(value)) throw new ThemeError(`"${key}" has unknown $type ${JSON.stringify(value)}`);
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
435
|
+
function validateName(name, key) {
|
|
436
|
+
if (/[.{}]/.test(name)) throw new ThemeError(`Token or group name "${key}" contains characters reserved by references (".", "{", "}")`);
|
|
437
|
+
}
|
|
438
|
+
function record(value, key) {
|
|
439
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ThemeError(`"${key}" is neither a group nor a token`);
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
function isRawToken(value) {
|
|
443
|
+
return "key" in value;
|
|
444
|
+
}
|
|
445
|
+
function isTokenValue(value) {
|
|
446
|
+
return typeof value === "string" || typeof value === "number" || Array.isArray(value) && value.every((part) => typeof part === "string" || typeof part === "number");
|
|
447
|
+
}
|
|
448
|
+
function isFontFamily(value) {
|
|
449
|
+
return typeof value === "string" || Array.isArray(value) && value.every((part) => typeof part === "string");
|
|
450
|
+
}
|
|
451
|
+
function inexpressibleReason(type) {
|
|
452
|
+
switch (type) {
|
|
453
|
+
case "color": return "Only hexadecimal CSS colors can be represented as DTCG color values";
|
|
454
|
+
case "dimension": return "Only px and rem dimensions can be represented as DTCG dimension values";
|
|
455
|
+
case "duration": return "Only ms and s durations can be represented as DTCG duration values";
|
|
456
|
+
case "fontFamily":
|
|
457
|
+
case "fontWeight":
|
|
458
|
+
case "number":
|
|
459
|
+
case "cubicBezier":
|
|
460
|
+
case "strokeStyle": return "The value is outside the DTCG value grammar";
|
|
461
|
+
case "shadow":
|
|
462
|
+
case "border":
|
|
463
|
+
case "transition":
|
|
464
|
+
case "gradient": return "CSS composite text has no DTCG representation";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
//#endregion
|
|
468
|
+
export { fromDTCG, toDTCG };
|