rastack 0.0.24 → 0.0.26

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/rastack-design.d.ts +17 -0
  3. package/dist/rastack-design.js +134 -0
  4. package/dist/rastack-tokens.d.ts +15 -0
  5. package/dist/rastack-tokens.js +43 -0
  6. package/dist/rastack-wasm-build.d.ts +1 -1
  7. package/dist/rastack-wasm-build.js +3 -3
  8. package/dist/rastack.d.ts +2 -0
  9. package/dist/rastack.js +12 -0
  10. package/dist/tokens/color.d.ts +22 -0
  11. package/dist/tokens/color.js +70 -0
  12. package/dist/tokens/compile.d.ts +34 -0
  13. package/dist/tokens/compile.js +103 -0
  14. package/dist/tokens/css.d.ts +30 -0
  15. package/dist/tokens/css.js +102 -0
  16. package/dist/tokens/define.d.ts +71 -0
  17. package/dist/tokens/define.js +98 -0
  18. package/dist/tokens/index.d.ts +17 -0
  19. package/dist/tokens/index.js +33 -0
  20. package/dist/tokens/resolve.d.ts +40 -0
  21. package/dist/tokens/resolve.js +135 -0
  22. package/dist/tokens/studio.d.ts +21 -0
  23. package/dist/tokens/studio.js +328 -0
  24. package/dist/tokens/theme.d.ts +55 -0
  25. package/dist/tokens/theme.js +139 -0
  26. package/dist/tokens/ts.d.ts +15 -0
  27. package/dist/tokens/ts.js +73 -0
  28. package/dist/tokens/types.d.ts +92 -0
  29. package/dist/tokens/types.js +35 -0
  30. package/hooks/query/delete.ts +3 -1
  31. package/jest.config.cjs +6 -0
  32. package/package.json +3 -2
  33. package/src/rastack-design.ts +117 -0
  34. package/src/rastack-tokens.ts +46 -0
  35. package/src/rastack-wasm-build.ts +3 -3
  36. package/src/rastack.ts +12 -0
  37. package/src/tokens/color.ts +74 -0
  38. package/src/tokens/compile.ts +85 -0
  39. package/src/tokens/css.ts +138 -0
  40. package/src/tokens/define.ts +128 -0
  41. package/src/tokens/index.ts +18 -0
  42. package/src/tokens/resolve.ts +170 -0
  43. package/src/tokens/studio.ts +357 -0
  44. package/src/tokens/theme.ts +180 -0
  45. package/src/tokens/ts.ts +80 -0
  46. package/src/tokens/types.ts +125 -0
  47. package/test/tokens.spec.ts +302 -0
  48. package/theme/index.ts +9 -0
  49. package/theme/provider.tsx +157 -0
  50. package/tokens.ts +9 -0
  51. package/tsconfig.json +3 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `rastack/tokens` — the TypeScript-first authoring DSL for design tokens.
3
+ *
4
+ * You author your design system in TypeScript; the compiler
5
+ * (`rastack tokens`) emits the canonical DTCG JSON plus ready-to-use CSS custom
6
+ * properties and a typed theme object. This mirrors the resource DSL
7
+ * (`rastack/define`): TypeScript is the single source of truth.
8
+ *
9
+ * ```ts
10
+ * import { defineTokens, t, ref } from "rastack/tokens";
11
+ *
12
+ * export default defineTokens({
13
+ * tokens: {
14
+ * color: {
15
+ * brand: { 500: t.color("#4F6BFF"), 600: t.color("#3B54E8") },
16
+ * text: t.color(ref("color.brand.500")), // alias
17
+ * },
18
+ * space: { sm: t.dimension(8), md: t.dimension(16) },
19
+ * radius: { md: t.dimension(10) },
20
+ * font: { sans: t.fontFamily(["Inter", "system-ui", "sans-serif"]) },
21
+ * },
22
+ * modes: {
23
+ * light: { color: { bg: t.color("#FFFFFF") } },
24
+ * dark: { color: { bg: t.color("#06080F") } },
25
+ * },
26
+ * defaultMode: "light",
27
+ * });
28
+ * ```
29
+ *
30
+ * The builders are thin: each returns a plain `{ $type, $value }` DTCG token, so
31
+ * a token tree authored with `t.*` is byte-identical to one hand-written as
32
+ * JSON. You can freely mix the two.
33
+ */
34
+ import { DesignToken, ShadowValue, ThemedTokens, TokenDocument } from "./types";
35
+ /** An alias reference to another token: `ref("color.brand.500")` → `"{color.brand.500}"`. */
36
+ export declare function ref(path: string): string;
37
+ /** True when a string is a DTCG alias (`"{a.b.c}"`). */
38
+ export declare function isAlias(value: unknown): value is string;
39
+ /** Strip the braces from an alias: `"{a.b}"` → `"a.b"`. */
40
+ export declare function aliasTarget(value: string): string;
41
+ /**
42
+ * Typed token builders. Each accepts a literal value or an alias produced by
43
+ * {@link ref}, plus an optional `$description`.
44
+ */
45
+ export declare const t: {
46
+ /** A colour: any CSS colour string (or an alias). */
47
+ color: (value: string, description?: string) => DesignToken;
48
+ /**
49
+ * A dimension — spacing, sizing, radius. A bare `number` is treated as `px`
50
+ * (the common case); pass a string for other units (`"1rem"`, `"50%"`).
51
+ */
52
+ dimension: (value: string | number, description?: string) => DesignToken;
53
+ /** A font-family stack. */
54
+ fontFamily: (value: string | string[], description?: string) => DesignToken;
55
+ /** A font weight (numeric `700` or keyword `"bold"`). */
56
+ fontWeight: (value: number | string, description?: string) => DesignToken;
57
+ /** A duration — `"200ms"`, or a bare `number` of milliseconds. */
58
+ duration: (value: string | number, description?: string) => DesignToken;
59
+ /** A cubic-bezier easing curve: `[x1, y1, x2, y2]`. */
60
+ cubicBezier: (value: [number, number, number, number], description?: string) => DesignToken;
61
+ /** A raw number token (line-height, z-index, opacity, …). */
62
+ number: (value: number, description?: string) => DesignToken;
63
+ /** A shadow (or a stack of shadows) → a CSS `box-shadow`. */
64
+ shadow: (value: ShadowValue | ShadowValue[], description?: string) => DesignToken;
65
+ };
66
+ /**
67
+ * Author a themeable token set. Accepts either a bare token tree or the full
68
+ * `{ tokens, modes, defaultMode }` shape, and always normalises to
69
+ * {@link ThemedTokens}.
70
+ */
71
+ export declare function defineTokens(input: TokenDocument | ThemedTokens): ThemedTokens;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * `rastack/tokens` — the TypeScript-first authoring DSL for design tokens.
4
+ *
5
+ * You author your design system in TypeScript; the compiler
6
+ * (`rastack tokens`) emits the canonical DTCG JSON plus ready-to-use CSS custom
7
+ * properties and a typed theme object. This mirrors the resource DSL
8
+ * (`rastack/define`): TypeScript is the single source of truth.
9
+ *
10
+ * ```ts
11
+ * import { defineTokens, t, ref } from "rastack/tokens";
12
+ *
13
+ * export default defineTokens({
14
+ * tokens: {
15
+ * color: {
16
+ * brand: { 500: t.color("#4F6BFF"), 600: t.color("#3B54E8") },
17
+ * text: t.color(ref("color.brand.500")), // alias
18
+ * },
19
+ * space: { sm: t.dimension(8), md: t.dimension(16) },
20
+ * radius: { md: t.dimension(10) },
21
+ * font: { sans: t.fontFamily(["Inter", "system-ui", "sans-serif"]) },
22
+ * },
23
+ * modes: {
24
+ * light: { color: { bg: t.color("#FFFFFF") } },
25
+ * dark: { color: { bg: t.color("#06080F") } },
26
+ * },
27
+ * defaultMode: "light",
28
+ * });
29
+ * ```
30
+ *
31
+ * The builders are thin: each returns a plain `{ $type, $value }` DTCG token, so
32
+ * a token tree authored with `t.*` is byte-identical to one hand-written as
33
+ * JSON. You can freely mix the two.
34
+ */
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.t = void 0;
37
+ exports.ref = ref;
38
+ exports.isAlias = isAlias;
39
+ exports.aliasTarget = aliasTarget;
40
+ exports.defineTokens = defineTokens;
41
+ /** An alias reference to another token: `ref("color.brand.500")` → `"{color.brand.500}"`. */
42
+ function ref(path) {
43
+ return `{${path}}`;
44
+ }
45
+ /** True when a string is a DTCG alias (`"{a.b.c}"`). */
46
+ function isAlias(value) {
47
+ return typeof value === "string" && /^\{[^{}]+\}$/.test(value);
48
+ }
49
+ /** Strip the braces from an alias: `"{a.b}"` → `"a.b"`. */
50
+ function aliasTarget(value) {
51
+ return value.slice(1, -1);
52
+ }
53
+ function token(type, value, description) {
54
+ const out = { $type: type, $value: value };
55
+ if (description)
56
+ out.$description = description;
57
+ return out;
58
+ }
59
+ /**
60
+ * Typed token builders. Each accepts a literal value or an alias produced by
61
+ * {@link ref}, plus an optional `$description`.
62
+ */
63
+ exports.t = {
64
+ /** A colour: any CSS colour string (or an alias). */
65
+ color: (value, description) => token("color", value, description),
66
+ /**
67
+ * A dimension — spacing, sizing, radius. A bare `number` is treated as `px`
68
+ * (the common case); pass a string for other units (`"1rem"`, `"50%"`).
69
+ */
70
+ dimension: (value, description) => token("dimension", typeof value === "number" ? `${value}px` : value, description),
71
+ /** A font-family stack. */
72
+ fontFamily: (value, description) => token("fontFamily", value, description),
73
+ /** A font weight (numeric `700` or keyword `"bold"`). */
74
+ fontWeight: (value, description) => token("fontWeight", value, description),
75
+ /** A duration — `"200ms"`, or a bare `number` of milliseconds. */
76
+ duration: (value, description) => token("duration", typeof value === "number" ? `${value}ms` : value, description),
77
+ /** A cubic-bezier easing curve: `[x1, y1, x2, y2]`. */
78
+ cubicBezier: (value, description) => token("cubicBezier", value, description),
79
+ /** A raw number token (line-height, z-index, opacity, …). */
80
+ number: (value, description) => token("number", value, description),
81
+ /** A shadow (or a stack of shadows) → a CSS `box-shadow`. */
82
+ shadow: (value, description) => token("shadow", value, description),
83
+ };
84
+ /**
85
+ * Author a themeable token set. Accepts either a bare token tree or the full
86
+ * `{ tokens, modes, defaultMode }` shape, and always normalises to
87
+ * {@link ThemedTokens}.
88
+ */
89
+ function defineTokens(input) {
90
+ if (isThemed(input)) {
91
+ return { modes: {}, ...input };
92
+ }
93
+ return { tokens: input, modes: {} };
94
+ }
95
+ function isThemed(input) {
96
+ return (typeof input.tokens === "object" &&
97
+ input.tokens !== null);
98
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `rastack/tokens` core — the design-system framework.
3
+ *
4
+ * A TypeScript-first authoring DSL over the **W3C Design Tokens (DTCG)**
5
+ * standard, a resolver (alias + `$type` inheritance + cycle detection), and
6
+ * pure emitters (CSS custom properties, a typed theme). Everything here is
7
+ * filesystem- and React-free, so it builds for the browser and is what
8
+ * `rastack/theme` runs at runtime. The `rastack tokens` CLI adds file IO on top
9
+ * (see `tools/src/tokens/compile.ts`).
10
+ */
11
+ export * from "./types";
12
+ export * from "./define";
13
+ export * from "./theme";
14
+ export * from "./color";
15
+ export * from "./resolve";
16
+ export * from "./css";
17
+ export * from "./ts";
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * `rastack/tokens` core — the design-system framework.
4
+ *
5
+ * A TypeScript-first authoring DSL over the **W3C Design Tokens (DTCG)**
6
+ * standard, a resolver (alias + `$type` inheritance + cycle detection), and
7
+ * pure emitters (CSS custom properties, a typed theme). Everything here is
8
+ * filesystem- and React-free, so it builds for the browser and is what
9
+ * `rastack/theme` runs at runtime. The `rastack tokens` CLI adds file IO on top
10
+ * (see `tools/src/tokens/compile.ts`).
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ __exportStar(require("./types"), exports);
28
+ __exportStar(require("./define"), exports);
29
+ __exportStar(require("./theme"), exports);
30
+ __exportStar(require("./color"), exports);
31
+ __exportStar(require("./resolve"), exports);
32
+ __exportStar(require("./css"), exports);
33
+ __exportStar(require("./ts"), exports);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Token resolution — the compiler's core pass.
3
+ *
4
+ * Given a DTCG token tree, produce a flat, ordered list of {@link ResolvedToken}
5
+ * with:
6
+ * - **`$type` inheritance** — a token inherits the nearest ancestor group's
7
+ * `$type` when it doesn't declare its own (a DTCG rule).
8
+ * - **alias resolution** — a `"{a.b.c}"` value is followed to its source,
9
+ * transitively, with cycle detection. The immediate alias target is kept
10
+ * (`aliasOf`) so emitters can preserve the reference.
11
+ * - **stable ordering** — depth-first in author order, so output diffs are
12
+ * minimal.
13
+ */
14
+ import { ResolvedToken, ThemedTokens, TokenDocument } from "./types";
15
+ /**
16
+ * Resolve a token document into ordered, alias-free {@link ResolvedToken}s.
17
+ * Throws on a dangling alias or an alias cycle — the token equivalent of the
18
+ * resource compiler's dangling-FK / circular-dependency checks.
19
+ */
20
+ export declare function resolveTokens(doc: TokenDocument): ResolvedToken[];
21
+ /** Deep-merge `override` onto `base`, returning a new document (base untouched). */
22
+ export declare function mergeTokens(base: TokenDocument, override: TokenDocument): TokenDocument;
23
+ /** The resolved base plus, per mode, the resolved base-merged-with-that-mode. */
24
+ export interface ResolvedTheme {
25
+ base: ResolvedToken[];
26
+ modes: Record<string, ResolvedToken[]>;
27
+ defaultMode?: string;
28
+ }
29
+ /**
30
+ * Resolve a themed token set: the base tree, and every mode as the base
31
+ * deep-merged with that mode's overrides. Callers diff a mode against the base
32
+ * to emit only what changed.
33
+ */
34
+ export declare function resolveTheme(themed: ThemedTokens): ResolvedTheme;
35
+ /**
36
+ * The CSS custom-property name for a token path.
37
+ * `color.brand.500` → `--color-brand-500`. Segments are lower-cased and any
38
+ * non-word character becomes `-`, so numeric and camelCase keys stay valid.
39
+ */
40
+ export declare function cssVarName(path: string, prefix?: string): string;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ /**
3
+ * Token resolution — the compiler's core pass.
4
+ *
5
+ * Given a DTCG token tree, produce a flat, ordered list of {@link ResolvedToken}
6
+ * with:
7
+ * - **`$type` inheritance** — a token inherits the nearest ancestor group's
8
+ * `$type` when it doesn't declare its own (a DTCG rule).
9
+ * - **alias resolution** — a `"{a.b.c}"` value is followed to its source,
10
+ * transitively, with cycle detection. The immediate alias target is kept
11
+ * (`aliasOf`) so emitters can preserve the reference.
12
+ * - **stable ordering** — depth-first in author order, so output diffs are
13
+ * minimal.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.resolveTokens = resolveTokens;
17
+ exports.mergeTokens = mergeTokens;
18
+ exports.resolveTheme = resolveTheme;
19
+ exports.cssVarName = cssVarName;
20
+ const define_1 = require("./define");
21
+ const types_1 = require("./types");
22
+ /** Metadata keys on a group that are not children. */
23
+ const META_KEYS = new Set(["$type", "$description", "$value", "$extensions"]);
24
+ /** Flatten a token tree to raw entries, applying group `$type` inheritance. */
25
+ function flatten(doc) {
26
+ const entries = [];
27
+ const walk = (node, prefix, inheritedType) => {
28
+ const groupType = node?.$type ?? inheritedType;
29
+ for (const key of Object.keys(node)) {
30
+ if (META_KEYS.has(key))
31
+ continue;
32
+ const child = node[key];
33
+ const path = [...prefix, key];
34
+ if ((0, types_1.isToken)(child)) {
35
+ entries.push({
36
+ path: path.join("."),
37
+ token: child,
38
+ ownType: child.$type ?? groupType,
39
+ });
40
+ }
41
+ else if (child && typeof child === "object") {
42
+ walk(child, path, groupType);
43
+ }
44
+ }
45
+ };
46
+ walk(doc, [], undefined);
47
+ return entries;
48
+ }
49
+ /**
50
+ * Resolve a token document into ordered, alias-free {@link ResolvedToken}s.
51
+ * Throws on a dangling alias or an alias cycle — the token equivalent of the
52
+ * resource compiler's dangling-FK / circular-dependency checks.
53
+ */
54
+ function resolveTokens(doc) {
55
+ const raw = flatten(doc);
56
+ const byPath = new Map(raw.map((e) => [e.path, e]));
57
+ // Resolve one path to its literal value + effective type, following aliases.
58
+ const resolveValue = (path, seen) => {
59
+ const entry = byPath.get(path);
60
+ if (!entry) {
61
+ throw new Error(`Token "${seen[seen.length - 1] ?? path}" aliases unknown token "${path}".`);
62
+ }
63
+ const value = entry.token.$value;
64
+ if ((0, define_1.isAlias)(value)) {
65
+ const target = (0, define_1.aliasTarget)(value);
66
+ if (seen.includes(target)) {
67
+ throw new Error(`Alias cycle detected: ${[...seen, target].join(" → ")}.`);
68
+ }
69
+ const resolved = resolveValue(target, [...seen, target]);
70
+ // A pure alias borrows its target's type when it has none of its own.
71
+ return { value: resolved.value, type: entry.ownType ?? resolved.type };
72
+ }
73
+ return { value, type: entry.ownType };
74
+ };
75
+ return raw.map(({ path, token }) => {
76
+ const value = token.$value;
77
+ const resolved = resolveValue(path, [path]);
78
+ const out = {
79
+ path,
80
+ type: resolved.type,
81
+ value: resolved.value,
82
+ };
83
+ if ((0, define_1.isAlias)(value))
84
+ out.aliasOf = (0, define_1.aliasTarget)(value);
85
+ if (token.$description)
86
+ out.$description = token.$description;
87
+ return out;
88
+ });
89
+ }
90
+ /** Deep-merge `override` onto `base`, returning a new document (base untouched). */
91
+ function mergeTokens(base, override) {
92
+ const out = Array.isArray(base) ? [...base] : { ...base };
93
+ for (const key of Object.keys(override)) {
94
+ const o = override[key];
95
+ const b = out[key];
96
+ if (o &&
97
+ b &&
98
+ typeof o === "object" &&
99
+ typeof b === "object" &&
100
+ !(0, types_1.isToken)(o) &&
101
+ !(0, types_1.isToken)(b)) {
102
+ out[key] = mergeTokens(b, o);
103
+ }
104
+ else {
105
+ out[key] = o;
106
+ }
107
+ }
108
+ return out;
109
+ }
110
+ /**
111
+ * Resolve a themed token set: the base tree, and every mode as the base
112
+ * deep-merged with that mode's overrides. Callers diff a mode against the base
113
+ * to emit only what changed.
114
+ */
115
+ function resolveTheme(themed) {
116
+ const base = resolveTokens(themed.tokens);
117
+ const modes = {};
118
+ for (const [name, overrides] of Object.entries(themed.modes ?? {})) {
119
+ modes[name] = resolveTokens(mergeTokens(themed.tokens, overrides));
120
+ }
121
+ return { base, modes, defaultMode: themed.defaultMode };
122
+ }
123
+ /**
124
+ * The CSS custom-property name for a token path.
125
+ * `color.brand.500` → `--color-brand-500`. Segments are lower-cased and any
126
+ * non-word character becomes `-`, so numeric and camelCase keys stay valid.
127
+ */
128
+ function cssVarName(path, prefix = "") {
129
+ const body = path
130
+ .split(".")
131
+ .map((s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase())
132
+ .join("-")
133
+ .replace(/[^a-z0-9-]+/g, "-");
134
+ return `--${prefix ? `${prefix}-` : ""}${body}`;
135
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The design studio page — a self-contained, dependency-free HTML app that
3
+ * `rastack design` serves. It **showcases** the design system (colour swatches,
4
+ * type ramp, spacing, radii, shadows, and live component previews built from the
5
+ * tokens themselves) and lets you **edit** token values in place — with a live
6
+ * preview — then save them back to the source, or export the DTCG JSON / CSS.
7
+ *
8
+ * Pure: given a themed doc it returns an HTML string, so it renders identically
9
+ * whether served by the CLI or snapshotted in a test. The page talks to the CLI
10
+ * server over two tiny endpoints: `POST /save` (persist) and it embeds
11
+ * everything else it needs inline.
12
+ */
13
+ import { ThemedTokens } from "./types";
14
+ export interface StudioOptions {
15
+ prefix?: string;
16
+ /** Whether the running server accepts `POST /save` (false for module sources). */
17
+ editable?: boolean;
18
+ title?: string;
19
+ }
20
+ /** Render the full design-studio HTML document for a themed token set. */
21
+ export declare function renderStudio(themed: ThemedTokens, options?: StudioOptions): string;