@appshell/webpack-plugin 1.0.0-alpha.22 → 1.0.0-alpha.24

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.
@@ -11,6 +11,6 @@ export { default as outdated } from './outdated';
11
11
  export { activate, publish } from './publish';
12
12
  export type { PublishOptions, PublishResult } from './publish';
13
13
  export { default as sync } from './sync';
14
- export type { AppshellComposition, AppshellConfig, AppshellConfigRemote, AppshellIndex, AppshellManifest, AppshellRemote, AppshellTemplate, ComparisonResult, ComparisonResults, ComparisonTarget, Metadata, ModuleFederationPluginOptions, PackageSpec, ResolvedRemote, Schema, SharedConfig, SharedModuleSpec, } from './types';
14
+ export type { AppshellComposition, AppshellConfig, AppshellConfigRemote, AppshellIndex, AppshellManifest, AppshellRemote, AppshellTemplate, AppshellTokenUsage, ComparisonResult, ComparisonResults, ComparisonTarget, Metadata, ModuleFederationPluginOptions, PackageSpec, ResolvedRemote, Schema, SharedConfig, SharedModuleSpec, } from './types';
15
15
  export * as utils from './utils';
16
16
  export * as validators from './validators';
@@ -68,6 +68,7 @@ export type AppshellTemplate<TMetadata = Metadata> = {
68
68
  remotes?: Record<string, AppshellConfigRemote<TMetadata>>;
69
69
  module: ModuleFederationPluginOptions;
70
70
  vars?: Record<string, unknown>;
71
+ tokens?: Record<string, AppshellTokenUsage>;
71
72
  overrides?: AppshellOverrides;
72
73
  };
73
74
  /** Appshell manifest types */
@@ -83,10 +84,25 @@ export type AppshellRemote<TMetadata = Metadata> = {
83
84
  export type AppshellOverrides = {
84
85
  vars: Record<string, Record<string, string | number | undefined>>;
85
86
  };
87
+ /**
88
+ * Which design tokens a package's own output reaches for. Observed from the emitted
89
+ * assets rather than declared: the CSS already says it, and a hand-kept list is a copy
90
+ * that drifts the first time someone adds a token and forgets the yaml.
91
+ *
92
+ * `required` is a reference with no fallback — the package has no plan B. `optional` is
93
+ * `var(--appshell-x, something)`, which degrades on its own. That split is read off what
94
+ * the author wrote rather than asked of them.
95
+ */
96
+ export type AppshellTokenUsage = {
97
+ required: string[];
98
+ optional: string[];
99
+ };
86
100
  export type AppshellManifest<TMetadata = Metadata> = {
87
101
  remotes: Record<string, AppshellRemote<TMetadata>>;
88
102
  modules: Record<string, ModuleFederationPluginOptions>;
89
103
  vars: Record<string, Record<string, string | number | undefined>>;
104
+ /** Keyed by federation scope, so a merged manifest still says which package needs what. */
105
+ tokens?: Record<string, AppshellTokenUsage>;
90
106
  overrides?: AppshellOverrides;
91
107
  };
92
108
  export type AppshellIndex = Record<string, string>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The token contract: the complete, fixed vocabulary a package may consume and an
3
+ * Application must supply.
4
+ *
5
+ * Small and stable is the property that matters. A package authors against these names
6
+ * with fallbacks (`var(--appshell-primary, #0af)`) and renders standalone; an Application
7
+ * supplies the values and every package it composes moves together.
8
+ */
9
+ export type Mode = 'light' | 'dark';
10
+ /** Colour roles an Application's *base* supplies — everything not tied to the accent. */
11
+ export declare const BASE_COLOR_ROLES: readonly ["surface", "on-surface", "surface-raised", "on-surface-raised", "text-muted", "border", "danger", "on-danger", "warning", "on-warning", "success", "on-success"];
12
+ /** Colour roles an Application's *accent* supplies. */
13
+ export declare const ACCENT_COLOR_ROLES: readonly ["primary", "on-primary", "secondary", "on-secondary"];
14
+ /**
15
+ * Derived rather than authored. Hover and active are computed from their accent with
16
+ * `color-mix`, and the focus ring is picked per base+accent so it stays visible. A theme
17
+ * may override any of them; none has to be supplied.
18
+ */
19
+ export declare const DERIVED_COLOR_ROLES: readonly ["primary-hover", "primary-active", "secondary-hover", "secondary-active", "focus-ring"];
20
+ /**
21
+ * Named for their role, not their size. A numeric scale would reintroduce exactly the
22
+ * divergence this contract exists to prevent — one author mapping `h1` to `2xl` and
23
+ * another to `xl` is how headings stop matching across a composed page.
24
+ */
25
+ export declare const TYPE_ROLES: readonly ["font-body", "font-mono", "font-size-h1", "font-size-h2", "font-size-h3", "font-size-h4", "font-size-h5", "font-size-h6", "font-size-body", "font-size-small", "line-height-tight", "line-height-normal"];
26
+ /** Genuinely a scale. Nobody wants `--appshell-space-card-padding`. */
27
+ export declare const DIMENSION_ROLES: readonly ["space-xs", "space-sm", "space-md", "space-lg", "space-xl", "radius-sm", "radius-md", "radius-lg"];
28
+ export type BaseColorRole = (typeof BASE_COLOR_ROLES)[number];
29
+ export type AccentColorRole = (typeof ACCENT_COLOR_ROLES)[number];
30
+ export type DerivedColorRole = (typeof DERIVED_COLOR_ROLES)[number];
31
+ export type TypeRole = (typeof TYPE_ROLES)[number];
32
+ export type DimensionRole = (typeof DIMENSION_ROLES)[number];
33
+ export type TokenRole = BaseColorRole | AccentColorRole | DerivedColorRole | TypeRole | DimensionRole;
34
+ export declare const TOKEN_ROLES: readonly TokenRole[];
35
+ export type BaseTokens = Record<BaseColorRole, string>;
36
+ export type AccentTokens = Record<AccentColorRole, string>;
37
+ export type Theme = Record<TokenRole, string>;
38
+ /** The custom property a role is published as. */
39
+ export declare const cssVar: (role: TokenRole) => string;
40
+ /**
41
+ * Pairs the registry checks for contrast. Because every role that carries text has an
42
+ * `on-` partner, a theme cannot express illegible text without failing this list — which
43
+ * is the failure that actually reaches users, and the one a CSS parser alone never catches.
44
+ */
45
+ export declare const TEXT_PAIRS: readonly (readonly [TokenRole, TokenRole])[];
46
+ /** Muted text has no `on-` partner; it is read against both surfaces. */
47
+ export declare const MUTED_AGAINST: readonly TokenRole[];
48
+ /** Non-text, so 3:1 rather than 4.5:1 — WCAG 1.4.11. */
49
+ export declare const NON_TEXT_PAIRS: readonly (readonly [TokenRole, TokenRole])[];
50
+ export declare const AA_TEXT = 4.5;
51
+ export declare const AA_NON_TEXT = 3;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Contrast checking for a theme.
3
+ *
4
+ * Syntax validation on its own earns little: it catches `--appshell-primary: bananas` and
5
+ * misses `--appshell-on-primary: #fff` on `--appshell-primary: #fff`, which is valid CSS
6
+ * and invisible text. Checking the `on-` pairs catches the failure that reaches users, and
7
+ * subsumes syntax validation for free — a value that will not parse cannot be measured.
8
+ */
9
+ import { type Theme, type TokenRole } from './contract';
10
+ type Rgb = [number, number, number];
11
+ /** Returns undefined rather than throwing — an unmeasurable value is reported, not fatal. */
12
+ export declare const parseColor: (value: string) => Rgb | undefined;
13
+ /** WCAG 2.1 contrast ratio, 1–21. */
14
+ export declare const contrastRatio: (a: Rgb, b: Rgb) => number;
15
+ /**
16
+ * A colour as `#rrggbb`.
17
+ *
18
+ * `<meta name="theme-color">` takes a CSS colour, but browser support for `oklch` there
19
+ * is not something to rely on for a hint the page cannot detect the failure of — a
20
+ * value the browser does not understand is simply ignored, silently.
21
+ */
22
+ export declare const toHex: (value: string) => string | undefined;
23
+ export type ContrastFinding = {
24
+ roles: [TokenRole, TokenRole];
25
+ ratio?: number;
26
+ required: number;
27
+ reason: 'below-threshold' | 'unparseable';
28
+ };
29
+ /**
30
+ * Every finding for a theme. Empty means it passes.
31
+ *
32
+ * `color-mix` values are skipped rather than failed: hover and active are derived from
33
+ * roles that are themselves checked, and resolving them needs a browser.
34
+ */
35
+ export declare const validateTheme: (theme: Theme) => ContrastFinding[];
36
+ export declare const describeFinding: ({ roles, ratio, required, reason }: ContrastFinding) => string;
37
+ export {};
@@ -0,0 +1,4 @@
1
+ export * from './contract';
2
+ export { contrastRatio, describeFinding, parseColor, toHex, validateTheme, type ContrastFinding, } from './contrast';
3
+ export { ACCENTS, BASES, FOCUS_RINGS } from './presets';
4
+ export { composeTheme, DEFAULT_TYPE_AND_DIMENSIONS, pinnedMode, toCss, type ColorScheme, type ThemeSelection, } from './theme';
@@ -0,0 +1,4 @@
1
+ import type { AccentTokens, BaseTokens, Mode } from './contract';
2
+ export declare const BASES: Record<string, Record<Mode, BaseTokens>>;
3
+ export declare const ACCENTS: Record<string, AccentTokens>;
4
+ export declare const FOCUS_RINGS: Record<string, Record<string, Record<Mode, string>>>;
@@ -0,0 +1,30 @@
1
+ import { type AccentTokens, type BaseTokens, type Mode, type Theme } from './contract';
2
+ /**
3
+ * `system` follows the viewer's preference. Pinning a scheme is for an application that
4
+ * ships one look on purpose, and it is honoured all the way down: the palette stops
5
+ * varying, the root is stamped so the tokens cannot be swapped underneath it, and the
6
+ * browser is told which scheme to render its own scrollbars and form controls in.
7
+ */
8
+ export type ColorScheme = 'system' | 'light' | 'dark';
9
+ export type ThemeSelection = {
10
+ /** A base preset name, or the base's own token values. */
11
+ base: string | Record<Mode, BaseTokens>;
12
+ /** An accent preset name, or the accent's own token values. */
13
+ accent: string | AccentTokens;
14
+ /** Defaults to `system`. */
15
+ colorScheme?: ColorScheme;
16
+ /** Overrides applied last, so an Application can adjust a preset without forking it. */
17
+ overrides?: Partial<Theme>;
18
+ };
19
+ /** The mode a pinned scheme resolves to, or undefined when it follows the viewer. */
20
+ export declare const pinnedMode: (selection: ThemeSelection) => Mode | undefined;
21
+ /** Type and dimensions do not vary by mode, and no preset currently changes them. */
22
+ export declare const DEFAULT_TYPE_AND_DIMENSIONS: Record<string, string>;
23
+ /** The full token map for one mode. */
24
+ export declare const composeTheme: (selection: ThemeSelection, mode: Mode) => Theme;
25
+ /**
26
+ * Three states, not two: an explicit choice in either direction, and the system default
27
+ * when nothing is stamped on the root. A theme that only handled `prefers-color-scheme`
28
+ * would give a viewer no way to override it.
29
+ */
30
+ export declare const toCss: (selection: ThemeSelection) => string;
@@ -24,6 +24,29 @@ export default class AppshellPlugin {
24
24
  static findModuleFederationPlugin(webpackConfig: WebpackOptionsNormalized): ModuleFederationPluginInstance | undefined;
25
25
  static createTemplate(config: AppshellConfig, plugin: ModuleFederationPluginInstance): AppshellTemplate;
26
26
  static validate(template: AppshellTemplate): boolean;
27
+ /**
28
+ * Which tokens this package's output actually reaches for.
29
+ *
30
+ * Read from the emitted assets rather than declared. The CSS already states it, and a
31
+ * hand-kept list is a second copy that goes stale the first time somebody adds a token
32
+ * and forgets to update it. This cannot drift, because it *is* the usage.
33
+ *
34
+ * A role referenced both with and without a fallback counts as required: one place in
35
+ * the package has nothing to fall back to.
36
+ *
37
+ * The blind spot is a reference built at runtime from a constructed string, which no
38
+ * static scan sees — the same limit Tailwind has with dynamic class names. It fails
39
+ * toward under-reporting, never toward inventing a requirement.
40
+ *
41
+ * Takes file contents rather than webpack assets: by `afterEmit` the compilation has
42
+ * swapped its sources for `SizeOnlySource`, which knows a length and nothing else.
43
+ * The files are on disk by then, which is what the hook means.
44
+ */
45
+ static tokenUsage(sources?: Record<string, string>): {
46
+ required: string[];
47
+ optional: string[];
48
+ unknown: string[];
49
+ };
27
50
  /**
28
51
  * Whether a request is shared, and shared as a singleton. `shared` has four shapes —
29
52
  * an object, an array of names, an array of objects, or a mix — and a bare name shares
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appshell/webpack-plugin",
3
- "version": "1.0.0-alpha.22",
3
+ "version": "1.0.0-alpha.24",
4
4
  "description": "Webpack plugin used to generate a global Appshell configuration for micro-frontends built with Module Federation",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/types/webpack-plugin/src/index.d.ts",
@@ -32,5 +32,8 @@
32
32
  "plugin"
33
33
  ],
34
34
  "license": "MIT",
35
- "gitHead": "6f194cbc3e1c53146b827ca3eb38ece3ccd59797"
35
+ "gitHead": "41f2ce7eee90490c80273dfe4a5df46295730a0e",
36
+ "dependencies": {
37
+ "@appshell/tokens": "^1.0.0-alpha.24"
38
+ }
36
39
  }