@elastic/distillate 0.0.0 → 0.1.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/LICENSE.txt +93 -0
- package/NOTICE.txt +30 -0
- package/README.md +142 -0
- package/THIRD_PARTY_LICENSES.md +768 -0
- package/dist/cjs/collector.js +420 -0
- package/dist/cjs/collector.js.map +1 -0
- package/dist/cjs/css_scan.js +186 -0
- package/dist/cjs/css_scan.js.map +1 -0
- package/dist/cjs/dom_sink.js +40 -0
- package/dist/cjs/dom_sink.js.map +1 -0
- package/dist/cjs/emotion.js +189 -0
- package/dist/cjs/emotion.js.map +1 -0
- package/dist/cjs/engine.js +79 -0
- package/dist/cjs/engine.js.map +1 -0
- package/dist/cjs/environment.js +9 -0
- package/dist/cjs/environment.js.map +1 -0
- package/dist/cjs/hash.js +86 -0
- package/dist/cjs/hash.js.map +1 -0
- package/dist/cjs/idents.js +23 -0
- package/dist/cjs/idents.js.map +1 -0
- package/dist/cjs/index.js +42 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/instance.js +34 -0
- package/dist/cjs/instance.js.map +1 -0
- package/dist/cjs/local_vars.js +100 -0
- package/dist/cjs/local_vars.js.map +1 -0
- package/dist/cjs/minify.js +77 -0
- package/dist/cjs/minify.js.map +1 -0
- package/dist/cjs/names.js +77 -0
- package/dist/cjs/names.js.map +1 -0
- package/dist/cjs/nesting.js +406 -0
- package/dist/cjs/nesting.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/runtime.js +257 -0
- package/dist/cjs/runtime.js.map +1 -0
- package/dist/cjs/styles/authoring.js +169 -0
- package/dist/cjs/styles/authoring.js.map +1 -0
- package/dist/cjs/styles/declarations.js +139 -0
- package/dist/cjs/styles/declarations.js.map +1 -0
- package/dist/cjs/styles/index.js +32 -0
- package/dist/cjs/styles/index.js.map +1 -0
- package/dist/cjs/styles/module.js +233 -0
- package/dist/cjs/styles/module.js.map +1 -0
- package/dist/cjs/styles/registry.js +187 -0
- package/dist/cjs/styles/registry.js.map +1 -0
- package/dist/cjs/styles/types.js +9 -0
- package/dist/cjs/styles/types.js.map +1 -0
- package/dist/cjs/testing.js +37 -0
- package/dist/cjs/testing.js.map +1 -0
- package/dist/cjs/theme.js +249 -0
- package/dist/cjs/theme.js.map +1 -0
- package/dist/cjs/tokens/contextual_var.js +39 -0
- package/dist/cjs/tokens/contextual_var.js.map +1 -0
- package/dist/cjs/tokens/index.js +24 -0
- package/dist/cjs/tokens/index.js.map +1 -0
- package/dist/cjs/tokens/kind.js +14 -0
- package/dist/cjs/tokens/kind.js.map +1 -0
- package/dist/cjs/tokens/light_dark.js +42 -0
- package/dist/cjs/tokens/light_dark.js.map +1 -0
- package/dist/cjs/tokens/scale_token.js +28 -0
- package/dist/cjs/tokens/scale_token.js.map +1 -0
- package/dist/cjs/tokens/theme_token.js +28 -0
- package/dist/cjs/tokens/theme_token.js.map +1 -0
- package/dist/cjs/var_invariant.js +115 -0
- package/dist/cjs/var_invariant.js.map +1 -0
- package/dist/collector.d.ts +84 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +416 -0
- package/dist/collector.js.map +1 -0
- package/dist/css_scan.d.ts +57 -0
- package/dist/css_scan.d.ts.map +1 -0
- package/dist/css_scan.js +180 -0
- package/dist/css_scan.js.map +1 -0
- package/dist/dom_sink.d.ts +27 -0
- package/dist/dom_sink.d.ts.map +1 -0
- package/dist/dom_sink.js +36 -0
- package/dist/dom_sink.js.map +1 -0
- package/dist/emotion.d.ts +47 -0
- package/dist/emotion.d.ts.map +1 -0
- package/dist/emotion.js +184 -0
- package/dist/emotion.js.map +1 -0
- package/dist/engine.d.ts +41 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +75 -0
- package/dist/engine.js.map +1 -0
- package/dist/environment.d.ts +43 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +8 -0
- package/dist/environment.js.map +1 -0
- package/dist/hash.d.ts +2 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +82 -0
- package/dist/hash.js.map +1 -0
- package/dist/idents.d.ts +9 -0
- package/dist/idents.d.ts.map +1 -0
- package/dist/idents.js +19 -0
- package/dist/idents.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/instance.d.ts +16 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +30 -0
- package/dist/instance.js.map +1 -0
- package/dist/local_vars.d.ts +75 -0
- package/dist/local_vars.d.ts.map +1 -0
- package/dist/local_vars.js +92 -0
- package/dist/local_vars.js.map +1 -0
- package/dist/minify.d.ts +11 -0
- package/dist/minify.d.ts.map +1 -0
- package/dist/minify.js +73 -0
- package/dist/minify.js.map +1 -0
- package/dist/names.d.ts +39 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +70 -0
- package/dist/names.js.map +1 -0
- package/dist/nesting.d.ts +59 -0
- package/dist/nesting.d.ts.map +1 -0
- package/dist/nesting.js +400 -0
- package/dist/nesting.js.map +1 -0
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +252 -0
- package/dist/runtime.js.map +1 -0
- package/dist/styles/authoring.d.ts +59 -0
- package/dist/styles/authoring.d.ts.map +1 -0
- package/dist/styles/authoring.js +155 -0
- package/dist/styles/authoring.js.map +1 -0
- package/dist/styles/declarations.d.ts +10 -0
- package/dist/styles/declarations.d.ts.map +1 -0
- package/dist/styles/declarations.js +132 -0
- package/dist/styles/declarations.js.map +1 -0
- package/dist/styles/index.d.ts +7 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +12 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/module.d.ts +5 -0
- package/dist/styles/module.d.ts.map +1 -0
- package/dist/styles/module.js +229 -0
- package/dist/styles/module.js.map +1 -0
- package/dist/styles/registry.d.ts +48 -0
- package/dist/styles/registry.d.ts.map +1 -0
- package/dist/styles/registry.js +182 -0
- package/dist/styles/registry.js.map +1 -0
- package/dist/styles/types.d.ts +211 -0
- package/dist/styles/types.d.ts.map +1 -0
- package/dist/styles/types.js +8 -0
- package/dist/styles/types.js.map +1 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +31 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme.d.ts +85 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +240 -0
- package/dist/theme.js.map +1 -0
- package/dist/tokens/contextual_var.d.ts +35 -0
- package/dist/tokens/contextual_var.d.ts.map +1 -0
- package/dist/tokens/contextual_var.js +33 -0
- package/dist/tokens/contextual_var.js.map +1 -0
- package/dist/tokens/index.d.ts +5 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +11 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/kind.d.ts +2 -0
- package/dist/tokens/kind.d.ts.map +1 -0
- package/dist/tokens/kind.js +10 -0
- package/dist/tokens/kind.js.map +1 -0
- package/dist/tokens/light_dark.d.ts +20 -0
- package/dist/tokens/light_dark.d.ts.map +1 -0
- package/dist/tokens/light_dark.js +37 -0
- package/dist/tokens/light_dark.js.map +1 -0
- package/dist/tokens/scale_token.d.ts +20 -0
- package/dist/tokens/scale_token.d.ts.map +1 -0
- package/dist/tokens/scale_token.js +23 -0
- package/dist/tokens/scale_token.js.map +1 -0
- package/dist/tokens/theme_token.d.ts +20 -0
- package/dist/tokens/theme_token.d.ts.map +1 -0
- package/dist/tokens/theme_token.js +23 -0
- package/dist/tokens/theme_token.js.map +1 -0
- package/dist/var_invariant.d.ts +25 -0
- package/dist/var_invariant.d.ts.map +1 -0
- package/dist/var_invariant.js +108 -0
- package/dist/var_invariant.js.map +1 -0
- package/docs/concepts/authoring.md +115 -0
- package/docs/concepts/collection.md +66 -0
- package/docs/concepts/distillery.md +47 -0
- package/docs/concepts/index.md +18 -0
- package/docs/concepts/naming-and-output.md +123 -0
- package/docs/concepts/ordering.md +61 -0
- package/docs/concepts/single-copy.md +103 -0
- package/docs/concepts/tokens.md +99 -0
- package/docs/docset.yml +53 -0
- package/docs/examples/01-stylesheet.ts +20 -0
- package/docs/examples/02-artifact.ts +26 -0
- package/docs/examples/03-variants.ts +40 -0
- package/docs/examples/04-emotion.ts +33 -0
- package/docs/examples/05-var-invariant.ts +31 -0
- package/docs/examples/06-payload.ts +85 -0
- package/docs/examples/fixture.ts +22 -0
- package/docs/getting-started/index.md +14 -0
- package/docs/getting-started/installation.md +30 -0
- package/docs/getting-started/quick-start.md +61 -0
- package/docs/getting-started/your-first-stylesheet.md +57 -0
- package/docs/guides/compact-artifacts.md +87 -0
- package/docs/guides/emotion-migration.md +60 -0
- package/docs/guides/index.md +17 -0
- package/docs/guides/name-collisions.md +37 -0
- package/docs/guides/non-css-surfaces.md +73 -0
- package/docs/guides/react-renderer.md +158 -0
- package/docs/guides/theming.md +107 -0
- package/docs/guides/var-invariant.md +24 -0
- package/docs/guides/vs-emotion.md +61 -0
- package/docs/index.md +73 -0
- package/docs/logo.svg +7 -0
- package/docs/playground/index.html +12 -0
- package/docs/playground/package.json +23 -0
- package/docs/playground/src/app/app.tsx +109 -0
- package/docs/playground/src/app/css_panel.tsx +119 -0
- package/docs/playground/src/app/editor_panel.tsx +87 -0
- package/docs/playground/src/app/index.ts +8 -0
- package/docs/playground/src/app/monaco_css_editor.tsx +84 -0
- package/docs/playground/src/app/monaco_source_editor.tsx +132 -0
- package/docs/playground/src/app/preview_panel.tsx +99 -0
- package/docs/playground/src/app/tokens_panel.tsx +117 -0
- package/docs/playground/src/app/types.ts +10 -0
- package/docs/playground/src/examples/snippets.test.ts +28 -0
- package/docs/playground/src/examples/snippets.ts +418 -0
- package/docs/playground/src/lib/compile.test.ts +157 -0
- package/docs/playground/src/lib/compile.ts +242 -0
- package/docs/playground/src/lib/demo_environment.test.ts +123 -0
- package/docs/playground/src/lib/demo_environment.ts +140 -0
- package/docs/playground/src/lib/editor_lib.test.ts +39 -0
- package/docs/playground/src/lib/editor_lib.ts +142 -0
- package/docs/playground/src/lib/format_css.test.ts +41 -0
- package/docs/playground/src/lib/format_css.ts +71 -0
- package/docs/playground/src/main.tsx +25 -0
- package/docs/playground/src/setup/chrome.test.ts +47 -0
- package/docs/playground/src/setup/chrome.ts +365 -0
- package/docs/playground/src/setup/monaco_env.ts +64 -0
- package/docs/playground/tsconfig.json +22 -0
- package/docs/playground/vite.config.ts +37 -0
- package/docs/playground/vitest.config.ts +35 -0
- package/docs/playground.md +28 -0
- package/docs/reference/api/emotion.md +372 -0
- package/docs/reference/api/index-1.md +2180 -0
- package/docs/reference/api/index.md +9 -0
- package/docs/reference/api/testing.md +92 -0
- package/docs/reference/api.md +12 -0
- package/docs/reference/entry-points.md +125 -0
- package/docs/reference/environment.md +60 -0
- package/docs/reference/index.md +11 -0
- package/docs/reference/okf-map.md +92 -0
- package/package.json +127 -4
- package/src/collector.ts +526 -0
- package/src/css_scan.ts +263 -0
- package/src/dom_sink.ts +65 -0
- package/src/emotion.ts +311 -0
- package/src/engine.ts +191 -0
- package/src/environment.ts +56 -0
- package/src/hash.ts +111 -0
- package/src/idents.ts +21 -0
- package/src/index.ts +93 -0
- package/src/instance.ts +49 -0
- package/src/local_vars.ts +220 -0
- package/src/minify.ts +87 -0
- package/src/names.ts +129 -0
- package/src/nesting.ts +596 -0
- package/src/runtime.ts +428 -0
- package/src/styles/authoring.ts +244 -0
- package/src/styles/declarations.ts +172 -0
- package/src/styles/index.ts +58 -0
- package/src/styles/module.ts +396 -0
- package/src/styles/registry.ts +261 -0
- package/src/styles/types.ts +283 -0
- package/src/testing.ts +48 -0
- package/src/theme.ts +454 -0
- package/src/tokens/contextual_var.ts +72 -0
- package/src/tokens/index.ts +17 -0
- package/src/tokens/kind.ts +13 -0
- package/src/tokens/light_dark.ts +58 -0
- package/src/tokens/scale_token.ts +41 -0
- package/src/tokens/theme_token.ts +42 -0
- package/src/var_invariant.ts +147 -0
package/src/engine.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { StylesCollector, type StylesCollectorOptions } from './collector';
|
|
9
|
+
import type {
|
|
10
|
+
DistilleryEnvironment,
|
|
11
|
+
DistilleryOptions,
|
|
12
|
+
ThemeVarDefinition,
|
|
13
|
+
} from './environment';
|
|
14
|
+
import { assertCssIdentSegment } from './idents';
|
|
15
|
+
import {
|
|
16
|
+
createStyleNameResolver,
|
|
17
|
+
type StyleNameMode,
|
|
18
|
+
type StyleNameResolver,
|
|
19
|
+
type StyleNameResolverOptions,
|
|
20
|
+
} from './names';
|
|
21
|
+
import { renderStyles, type RenderStylesOptions } from './runtime';
|
|
22
|
+
import {
|
|
23
|
+
createStyleModuleWithEnvironment,
|
|
24
|
+
type PrimitiveStyleAuthoringApi,
|
|
25
|
+
readableVarOwnersFromEnvironment,
|
|
26
|
+
type ResolvedStyles,
|
|
27
|
+
type StyleAuthoringApi,
|
|
28
|
+
StyleRegistry,
|
|
29
|
+
type StylesModule,
|
|
30
|
+
type StylesObject,
|
|
31
|
+
} from './styles';
|
|
32
|
+
import {
|
|
33
|
+
deriveTheme,
|
|
34
|
+
requireThemeVariation,
|
|
35
|
+
resolveThemeValues,
|
|
36
|
+
resolveThemeVariations,
|
|
37
|
+
type ThemeTree,
|
|
38
|
+
type TokensOf,
|
|
39
|
+
type ValuesOf,
|
|
40
|
+
} from './theme';
|
|
41
|
+
|
|
42
|
+
/** One component library's binding: environment, registry, and pre-bound operations. Destructure-safe (no `this`). */
|
|
43
|
+
export interface Distillery<
|
|
44
|
+
TTokens = unknown,
|
|
45
|
+
TTheme extends ThemeTree = ThemeTree,
|
|
46
|
+
> {
|
|
47
|
+
/** Resolved environment (tokens, prefix, derived theme vars). */
|
|
48
|
+
readonly environment: DistilleryEnvironment<TTokens>;
|
|
49
|
+
/** Derived token tree, same object as `environment.tokens`. */
|
|
50
|
+
readonly tokens: TTokens;
|
|
51
|
+
/** Derived theme-var registry, same object as `environment.themeVars`. */
|
|
52
|
+
readonly themeVars: Readonly<Record<string, ThemeVarDefinition>>;
|
|
53
|
+
/** Nested literal values for one scheme. Optional `variation` is a name from `variations`. */
|
|
54
|
+
readonly resolveValues: (
|
|
55
|
+
scheme: 'light' | 'dark',
|
|
56
|
+
variation?: string
|
|
57
|
+
) => ValuesOf<TTheme>;
|
|
58
|
+
/** The module registry accumulating collected handles and rules. */
|
|
59
|
+
readonly registry: StyleRegistry;
|
|
60
|
+
/** When `true`, collectors warn about no-op handles. */
|
|
61
|
+
readonly dev: boolean;
|
|
62
|
+
/** Named handle tree plus tokens. */
|
|
63
|
+
readonly createStyleModule: <TStyles extends StylesObject>(
|
|
64
|
+
name: string,
|
|
65
|
+
factory: (api: StyleAuthoringApi<TTokens>) => TStyles
|
|
66
|
+
) => StylesModule<ResolvedStyles<TStyles>>;
|
|
67
|
+
/** Named handles plus tokens only. */
|
|
68
|
+
readonly primitiveStyles: <TStyles extends StylesObject>(
|
|
69
|
+
name: string,
|
|
70
|
+
factory: (api: PrimitiveStyleAuthoringApi<TTokens>) => TStyles
|
|
71
|
+
) => StylesModule<ResolvedStyles<TStyles>>;
|
|
72
|
+
/** Collector for one render. Class-name resolution collects as a side effect. No-op warnings require `dev: true`; pass `{ warn }` to capture them. */
|
|
73
|
+
readonly artifactCollector: (
|
|
74
|
+
names: StyleNameMode,
|
|
75
|
+
options?: Pick<StylesCollectorOptions, 'warn'>
|
|
76
|
+
) => StylesCollector;
|
|
77
|
+
/** Collector preloaded with every registered module. No-op warnings require `dev: true`; pass `{ warn }` to capture them. */
|
|
78
|
+
readonly stylesheetCollector: (
|
|
79
|
+
names?: StyleNameMode,
|
|
80
|
+
options?: Pick<StylesCollectorOptions, 'warn'>
|
|
81
|
+
) => StylesCollector;
|
|
82
|
+
/** Emits collected CSS for this environment. */
|
|
83
|
+
readonly renderStyles: (
|
|
84
|
+
collector: StylesCollector,
|
|
85
|
+
resolver?: StyleNameResolver,
|
|
86
|
+
options?: RenderStylesOptions
|
|
87
|
+
) => string;
|
|
88
|
+
/** Creates a standalone name resolver using this distillery's prefix. */
|
|
89
|
+
readonly createNameResolver: (
|
|
90
|
+
options: Omit<StyleNameResolverOptions, 'prefix'>
|
|
91
|
+
) => StyleNameResolver;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Binds the engine to one library's prefix and theme tree.
|
|
96
|
+
*
|
|
97
|
+
* @param options Brand prefix, theme scope, and authoring theme tree.
|
|
98
|
+
* @throws If `prefix` is not a CSS identifier segment, if a theme key contains a hyphen, if a `lightDark` value is not a CSS `<color>`, if a named variation disagrees with the base, or if `themeVars` / `sharedVars` claim the same readable custom-property name.
|
|
99
|
+
*/
|
|
100
|
+
export const createDistillery = <const TTheme extends ThemeTree>(
|
|
101
|
+
options: DistilleryOptions<TTheme>
|
|
102
|
+
): Distillery<TokensOf<TTheme>, TTheme> => {
|
|
103
|
+
const {
|
|
104
|
+
prefix,
|
|
105
|
+
themeScope,
|
|
106
|
+
theme,
|
|
107
|
+
sharedVars: sharedVarList,
|
|
108
|
+
variations,
|
|
109
|
+
dev = false,
|
|
110
|
+
} = options;
|
|
111
|
+
assertCssIdentSegment(prefix, 'Distillery prefix');
|
|
112
|
+
const { tokens, themeVars } = deriveTheme(prefix, theme);
|
|
113
|
+
const sharedVars = sharedVarList
|
|
114
|
+
? Object.freeze(new Set(sharedVarList))
|
|
115
|
+
: undefined;
|
|
116
|
+
const resolvedVariations = resolveThemeVariations(
|
|
117
|
+
prefix,
|
|
118
|
+
theme,
|
|
119
|
+
themeVars,
|
|
120
|
+
variations
|
|
121
|
+
);
|
|
122
|
+
const environment: DistilleryEnvironment<TokensOf<TTheme>> = {
|
|
123
|
+
prefix,
|
|
124
|
+
themeScope,
|
|
125
|
+
themeVars: Object.freeze(themeVars),
|
|
126
|
+
tokens,
|
|
127
|
+
...(sharedVars ? { sharedVars } : {}),
|
|
128
|
+
...(Object.keys(resolvedVariations).length > 0
|
|
129
|
+
? { variations: resolvedVariations }
|
|
130
|
+
: {}),
|
|
131
|
+
};
|
|
132
|
+
const registry = new StyleRegistry(
|
|
133
|
+
prefix,
|
|
134
|
+
readableVarOwnersFromEnvironment(environment),
|
|
135
|
+
sharedVars ?? new Set()
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
environment,
|
|
140
|
+
tokens,
|
|
141
|
+
themeVars: environment.themeVars,
|
|
142
|
+
resolveValues: (scheme, variation) =>
|
|
143
|
+
resolveThemeValues(
|
|
144
|
+
theme,
|
|
145
|
+
themeVars,
|
|
146
|
+
scheme,
|
|
147
|
+
variation === undefined
|
|
148
|
+
? undefined
|
|
149
|
+
: requireThemeVariation(environment.variations, variation)
|
|
150
|
+
),
|
|
151
|
+
registry,
|
|
152
|
+
dev,
|
|
153
|
+
createStyleModule: (name, factory) =>
|
|
154
|
+
createStyleModuleWithEnvironment(environment, registry, name, factory),
|
|
155
|
+
primitiveStyles: (name, factory) =>
|
|
156
|
+
createStyleModuleWithEnvironment(
|
|
157
|
+
environment,
|
|
158
|
+
registry,
|
|
159
|
+
name,
|
|
160
|
+
({ css: style, tokens: moduleTokens }) =>
|
|
161
|
+
factory({ style, tokens: moduleTokens })
|
|
162
|
+
),
|
|
163
|
+
artifactCollector: (names, options) =>
|
|
164
|
+
new StylesCollector({
|
|
165
|
+
target: 'artifact',
|
|
166
|
+
names,
|
|
167
|
+
registry,
|
|
168
|
+
prefix,
|
|
169
|
+
dev,
|
|
170
|
+
...(options?.warn ? { warn: options.warn } : {}),
|
|
171
|
+
}),
|
|
172
|
+
stylesheetCollector: (names = 'readable', options) => {
|
|
173
|
+
const collector = new StylesCollector({
|
|
174
|
+
target: 'stylesheet',
|
|
175
|
+
names,
|
|
176
|
+
registry,
|
|
177
|
+
prefix,
|
|
178
|
+
dev,
|
|
179
|
+
...(options?.warn ? { warn: options.warn } : {}),
|
|
180
|
+
});
|
|
181
|
+
for (const module of registry.modules) {
|
|
182
|
+
collector.useAllEntries(module);
|
|
183
|
+
}
|
|
184
|
+
return collector;
|
|
185
|
+
},
|
|
186
|
+
renderStyles: (collector, resolver, renderOptions) =>
|
|
187
|
+
renderStyles(environment, collector, resolver, renderOptions),
|
|
188
|
+
createNameResolver: (resolverOptions) =>
|
|
189
|
+
createStyleNameResolver({ ...resolverOptions, prefix }),
|
|
190
|
+
};
|
|
191
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ResolvedThemeVariation,
|
|
10
|
+
ThemeTree,
|
|
11
|
+
ThemeVariation,
|
|
12
|
+
} from './theme';
|
|
13
|
+
|
|
14
|
+
/** One theme CSS variable. Differing `light`/`dark` fold into `light-dark(...)`. */
|
|
15
|
+
export interface ThemeVarDefinition {
|
|
16
|
+
/** Theme-tree path this definition was derived from. */
|
|
17
|
+
readonly path: string;
|
|
18
|
+
/** Emitted CSS custom property name. Equal to `cssVarName(prefix, path)`. */
|
|
19
|
+
readonly cssVar: `--${string}`;
|
|
20
|
+
/** Value for light color scheme. */
|
|
21
|
+
readonly light: string;
|
|
22
|
+
/** Value for dark color scheme. Equal to `light` for scheme-invariant tokens. */
|
|
23
|
+
readonly dark: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Authoring input to `createDistillery`. */
|
|
27
|
+
export interface DistilleryOptions<TTheme extends ThemeTree = ThemeTree> {
|
|
28
|
+
/** Brand identifier used in readable class and CSS-variable names. Must be a CSS identifier segment. */
|
|
29
|
+
readonly prefix: string;
|
|
30
|
+
/** Selector wrapping emitted theme-var declarations. */
|
|
31
|
+
readonly themeScope: string;
|
|
32
|
+
/** Nested value tree. Strings and `lightDark` leaves become theme vars; `ScaleToken` leaves inline. */
|
|
33
|
+
readonly theme: TTheme;
|
|
34
|
+
/** Named partial variations of `theme`. Declaring a variation does not emit it; name it at `renderStyles`. */
|
|
35
|
+
readonly variations?: Readonly<Record<string, ThemeVariation<TTheme>>>;
|
|
36
|
+
/** Cross-module contextual-var paths. Names are `cssVarName(prefix, path)`. Module-local `vars(...)` groups are not listed here. */
|
|
37
|
+
readonly sharedVars?: readonly `vars/${string}`[];
|
|
38
|
+
/** When `true`, collectors warn about no-op handles. Default `false`. Does not affect the single-copy guard. */
|
|
39
|
+
readonly dev?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Resolved environment: derived `themeVars` / `tokens` plus the bind-time prefix and scope. */
|
|
43
|
+
export interface DistilleryEnvironment<TTokens = unknown> {
|
|
44
|
+
/** Brand identifier used in readable class and CSS-variable names. Must be a CSS identifier segment. */
|
|
45
|
+
readonly prefix: string;
|
|
46
|
+
/** Selector wrapping emitted theme-var declarations. */
|
|
47
|
+
readonly themeScope: string;
|
|
48
|
+
/** Theme token path → definition. Emission sorts paths. */
|
|
49
|
+
readonly themeVars: Readonly<Record<string, ThemeVarDefinition>>;
|
|
50
|
+
/** Cross-module contextual-var paths. Module-local `vars(...)` groups are not listed here. */
|
|
51
|
+
readonly sharedVars?: ReadonlySet<`vars/${string}`>;
|
|
52
|
+
/** Typed token tree, surfaced as `tokens` on the authoring API. */
|
|
53
|
+
readonly tokens: TTokens;
|
|
54
|
+
/** Named variations resolved against `themeVars`. Absent when no `variations` were declared. */
|
|
55
|
+
readonly variations?: Readonly<Record<string, ResolvedThemeVariation>>;
|
|
56
|
+
}
|
package/src/hash.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Content hashing for anonymous emotion `css`/`injectGlobal` modules.
|
|
9
|
+
//
|
|
10
|
+
// Deterministic 64-bit FNV-1a (two 32-bit passes with distinct offset bases)
|
|
11
|
+
// over a canonical serialization of the authored template. Determinism lets
|
|
12
|
+
// server-rendered and client-rendered class names agree without coordination.
|
|
13
|
+
|
|
14
|
+
import type { LocalVarMarker } from './local_vars';
|
|
15
|
+
import { isLocalVarMarker } from './local_vars';
|
|
16
|
+
import { isLocalVarRef } from './local_vars';
|
|
17
|
+
import { isHandleLike } from './nesting';
|
|
18
|
+
import {
|
|
19
|
+
isContextualCssVar,
|
|
20
|
+
isContextualCssVarName,
|
|
21
|
+
isCssToken,
|
|
22
|
+
isScaleToken,
|
|
23
|
+
} from './tokens';
|
|
24
|
+
|
|
25
|
+
const FNV_PRIME = 0x01000193;
|
|
26
|
+
const FNV_OFFSET_A = 0x811c9dc5;
|
|
27
|
+
// A second basis derived from the golden-ratio constant so the two passes
|
|
28
|
+
// disagree, widening the effective hash to 64 bits.
|
|
29
|
+
const FNV_OFFSET_B = (0x811c9dc5 ^ 0x9e3779b9) >>> 0;
|
|
30
|
+
|
|
31
|
+
const fnv1a = (input: string, offset: number): number => {
|
|
32
|
+
let hash = offset;
|
|
33
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
34
|
+
hash ^= input.charCodeAt(i);
|
|
35
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
36
|
+
}
|
|
37
|
+
return hash >>> 0;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const base36 = (value: number): string => value.toString(36).padStart(7, '0');
|
|
41
|
+
|
|
42
|
+
export const contentHash64 = (
|
|
43
|
+
strings: readonly string[],
|
|
44
|
+
values: readonly unknown[]
|
|
45
|
+
): string => {
|
|
46
|
+
const canonical = canonicalize(strings, values);
|
|
47
|
+
return (
|
|
48
|
+
base36(fnv1a(canonical, FNV_OFFSET_A)) +
|
|
49
|
+
base36(fnv1a(canonical, FNV_OFFSET_B))
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Canonical, injective-enough serialization. Template strings and tagged
|
|
54
|
+
// values are interleaved and joined with a NUL sentinel that cannot appear in
|
|
55
|
+
// authored CSS, so distinct templates cannot collide by concatenation.
|
|
56
|
+
const canonicalize = (
|
|
57
|
+
strings: readonly string[],
|
|
58
|
+
values: readonly unknown[]
|
|
59
|
+
): string => {
|
|
60
|
+
const segments: string[] = [];
|
|
61
|
+
strings.forEach((part, index) => {
|
|
62
|
+
segments.push(part);
|
|
63
|
+
if (index < values.length) {
|
|
64
|
+
segments.push(tagValue(values[index]));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return segments.join('\u0000');
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const tagValue = (value: unknown): string => {
|
|
71
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
72
|
+
return `r:${value}`;
|
|
73
|
+
}
|
|
74
|
+
if (isCssToken(value)) {
|
|
75
|
+
return `t:${value.path}`;
|
|
76
|
+
}
|
|
77
|
+
if (isContextualCssVar(value)) {
|
|
78
|
+
return `v:${value.path}`;
|
|
79
|
+
}
|
|
80
|
+
if (isContextualCssVarName(value)) {
|
|
81
|
+
return `n:${value.path}`;
|
|
82
|
+
}
|
|
83
|
+
if (isLocalVarRef(value)) {
|
|
84
|
+
return `l:${value.path}`;
|
|
85
|
+
}
|
|
86
|
+
if (isScaleToken(value)) {
|
|
87
|
+
return `s:${value.value}`;
|
|
88
|
+
}
|
|
89
|
+
if (isLocalVarMarker(value)) {
|
|
90
|
+
return tagMarker(value);
|
|
91
|
+
}
|
|
92
|
+
if (isHandleLike(value)) {
|
|
93
|
+
return `h:${value.key}`;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Unsupported interpolation in css template. Interpolate a theme token, scale token, contextual var, local var (ref/marker), string, number, or a style handle.`
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const tagMarker = (marker: LocalVarMarker): string => {
|
|
101
|
+
if (marker.__kind === 'local-var-default-marker') {
|
|
102
|
+
const entries = marker.defaults
|
|
103
|
+
.map((item) => `${item.key}=${tagValue(item.value)}`)
|
|
104
|
+
.sort();
|
|
105
|
+
return `m:default:${marker.groupId}:${entries.join(',')}`;
|
|
106
|
+
}
|
|
107
|
+
const entries = marker.overrides
|
|
108
|
+
.map((item) => `${item.key}=${tagValue(item.value)}`)
|
|
109
|
+
.sort();
|
|
110
|
+
return `m:override:${marker.groupId}:${entries.join(',')}`;
|
|
111
|
+
};
|
package/src/idents.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Throws if `segment` is not a valid CSS identifier segment.
|
|
10
|
+
*
|
|
11
|
+
* @param segment Value to validate.
|
|
12
|
+
* @param role Human-readable label for the error message (e.g. `'Distillery prefix'`).
|
|
13
|
+
* @throws If `segment` contains characters outside `[A-Za-z_][A-Za-z0-9_-]*`.
|
|
14
|
+
*/
|
|
15
|
+
export const assertCssIdentSegment = (segment: string, role: string): void => {
|
|
16
|
+
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(segment)) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`${role} "${segment}" is not a CSS identifier segment. Use a letter or underscore, then letters, digits, hyphens, or underscores.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createDistillery, type Distillery } from './engine';
|
|
9
|
+
export {
|
|
10
|
+
type DistilleryEnvironment,
|
|
11
|
+
type DistilleryOptions,
|
|
12
|
+
type ThemeVarDefinition,
|
|
13
|
+
} from './environment';
|
|
14
|
+
export {
|
|
15
|
+
resolveThemeValues,
|
|
16
|
+
zipSchemes,
|
|
17
|
+
type DeepPartialTheme,
|
|
18
|
+
type MediaThemeVariation,
|
|
19
|
+
type PathsOf,
|
|
20
|
+
type ResolvedThemeVariation,
|
|
21
|
+
type SchemeValueTree,
|
|
22
|
+
type ThemeTree,
|
|
23
|
+
type ThemeVariation,
|
|
24
|
+
type TokensOf,
|
|
25
|
+
type ValuesOf,
|
|
26
|
+
type Zipped,
|
|
27
|
+
} from './theme';
|
|
28
|
+
export {
|
|
29
|
+
contextualVar,
|
|
30
|
+
cq,
|
|
31
|
+
isContextualCssVar,
|
|
32
|
+
isContextualCssVarName,
|
|
33
|
+
isCssToken,
|
|
34
|
+
isScaleToken,
|
|
35
|
+
lightDark,
|
|
36
|
+
scaleToken,
|
|
37
|
+
themeToken,
|
|
38
|
+
type ContextualCssVar,
|
|
39
|
+
type ContextualCssVarName,
|
|
40
|
+
type CssToken,
|
|
41
|
+
type ScaleToken,
|
|
42
|
+
type SchemePair,
|
|
43
|
+
} from './tokens';
|
|
44
|
+
export {
|
|
45
|
+
createStyleNameResolver,
|
|
46
|
+
cssVarName,
|
|
47
|
+
type StyleNameMode,
|
|
48
|
+
type StyleNameResolver,
|
|
49
|
+
type StyleNameResolverOptions,
|
|
50
|
+
type StyleTarget,
|
|
51
|
+
} from './names';
|
|
52
|
+
export { StylesCollector, type StylesCollectorOptions } from './collector';
|
|
53
|
+
export {
|
|
54
|
+
renderStyles,
|
|
55
|
+
type RenderStylesOptions,
|
|
56
|
+
type ThemeAlternate,
|
|
57
|
+
} from './runtime';
|
|
58
|
+
export {
|
|
59
|
+
combineClassNames,
|
|
60
|
+
container,
|
|
61
|
+
css,
|
|
62
|
+
decls,
|
|
63
|
+
mapDomain,
|
|
64
|
+
media,
|
|
65
|
+
rule,
|
|
66
|
+
StyleRegistry,
|
|
67
|
+
variants,
|
|
68
|
+
type ContextualVarPath,
|
|
69
|
+
type CssValue,
|
|
70
|
+
type CssVarPath,
|
|
71
|
+
type Declarations,
|
|
72
|
+
type DeclarationSegment,
|
|
73
|
+
type DefaultGroupDeps,
|
|
74
|
+
type DefaultKeyDeps,
|
|
75
|
+
type LocalVarDefaultMarker,
|
|
76
|
+
type LocalVarGroup,
|
|
77
|
+
type LocalVarMarker,
|
|
78
|
+
type LocalVarOverrideMarker,
|
|
79
|
+
type LocalVarRef,
|
|
80
|
+
type PrimitiveStyleAuthoringApi,
|
|
81
|
+
type ResolvedStyles,
|
|
82
|
+
type RuleOptions,
|
|
83
|
+
type StyleAuthoringApi,
|
|
84
|
+
type StyleDeps,
|
|
85
|
+
type StyleEntry,
|
|
86
|
+
type StyleHandle,
|
|
87
|
+
type StyleMedia,
|
|
88
|
+
type StyleRegistrySnapshot,
|
|
89
|
+
type StyleRule,
|
|
90
|
+
type StylesModule,
|
|
91
|
+
type StylesObject,
|
|
92
|
+
type StyleSelectorResolver,
|
|
93
|
+
} from './styles';
|
package/src/instance.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
3
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
4
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
5
|
+
* 2.0.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** `globalThis` key for the loaded copy's identity. See the README. */
|
|
9
|
+
export const INSTANCE_KEY: unique symbol = Symbol.for(
|
|
10
|
+
'elastic.distillate.instance'
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
/** Object that may hold the Distillate instance identity, normally `globalThis`. */
|
|
14
|
+
export interface InstanceHost {
|
|
15
|
+
/** Identity token of the first Distillate copy that registered. */
|
|
16
|
+
[INSTANCE_KEY]?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Records this copy's identity and warns if a different token is already registered.
|
|
21
|
+
*
|
|
22
|
+
* @param host Object carrying the marker, normally `globalThis`.
|
|
23
|
+
* @param instanceId This copy's identity token.
|
|
24
|
+
* @param warn Injected so tests need not intercept the console.
|
|
25
|
+
*/
|
|
26
|
+
export const registerDistillateInstance = (
|
|
27
|
+
host: InstanceHost,
|
|
28
|
+
instanceId: string,
|
|
29
|
+
warn: (message: string) => void = (message) => console.warn(message)
|
|
30
|
+
): void => {
|
|
31
|
+
const registered = host[INSTANCE_KEY];
|
|
32
|
+
if (registered === undefined) {
|
|
33
|
+
host[INSTANCE_KEY] = instanceId;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (registered === instanceId) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
warn(
|
|
40
|
+
`@elastic/distillate: a second copy loaded (${instanceId}; already registered ${registered}). ` +
|
|
41
|
+
'The engine keeps module-scope state, so two copies silently break `variants(...)` tree-shaking ' +
|
|
42
|
+
'and inflate emitted CSS. Externalize @elastic/distillate in your bundler so exactly one copy loads.'
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
registerDistillateInstance(
|
|
47
|
+
globalThis as InstanceHost,
|
|
48
|
+
`@elastic/distillate/instance#${Math.random().toString(36).slice(2)}`
|
|
49
|
+
);
|