@wix/origami-site-runtime 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,12 +28,12 @@ cannot hide an illegally placed target. Diagnostics retain the original node sou
28
28
  `@origami-target internal-container-content-wrapper` routes to
29
29
  `#id > .id-overflow-wrapper > .id-container`; `internal-container-overflow-wrapper`
30
30
  routes to `#id > .id-overflow-wrapper`. Names come from the pure
31
- `@wix/origami-runtime/layout-contract` entry point. Root sizing, item placement, visibility
31
+ `@wix/origami-system-components/layout-contract` entry point. Root sizing, item placement, visibility
32
32
  and ordinary positioning remain on the ID selector. Background, border and pinned-layer
33
33
  CSS comes from `@wix/origami-system-components/InternalContainer.css`, imported by the maintained
34
- `@wix/origami-runtime/components/InternalContainer` component. The component preserves focus and
34
+ `@wix/origami-system-components/InternalContainer` component. The component preserves focus and
35
35
  wrapper flags. Ref support is deferred
36
- (see the [runtime decision](../origami-runtime/README.md#decision-defer-ref-support-2026-09-24)).
36
+ (see the [system-components decision](../origami-system-components/README.md#decision-defer-ref-support-2026-09-24)).
37
37
 
38
38
  Only explicit targets in site-owned layout.css carriers opt in. Sources of inlined nodes
39
39
  are checked too, including dependency CSS. Targets in style/global-style CSS, mixins,
@@ -62,10 +62,10 @@ half-pixel floor; fixed values retain fractional pixels; zero is zero. See the
62
62
 
63
63
  ## Release and limitations
64
64
 
65
- Generated structures import Page, Section, HeaderSection and FooterSection from
66
- `@wix/origami-system-components` subpaths, InternalContainer from `@wix/origami-runtime`,
65
+ Generated structures import Page, Section, HeaderSection, FooterSection and InternalContainer from
66
+ `@wix/origami-system-components` subpaths,
67
67
  and other editor components from fetched wrappers. The package dependency order is
68
- `@wix/origami-system-components` → `@wix/origami-runtime` → `@wix/origami-site-runtime`;
68
+ `@wix/origami-system-components` → `@wix/origami-site-runtime`;
69
69
  publish the required entry points before generator/CLI releases that consume them. Existing dependency
70
70
  entries, including local/file runtime overrides, are preserved when component dependencies are added.
71
71
  Packed tests verify the compiler and pure layout contract outside the workspace with only
@@ -84,5 +84,5 @@ limitations, not claims of Thunderbolt parity; they need a separate producer/run
84
84
  responsive SPX and grid/flex, nested fractional fixed values, max widths/alignment, overflow,
85
85
  visibility, focus/pointer mechanics, HMR and production build. Install Playwright Chromium or
86
86
  set `CHROME_CHANNEL=chrome` to use installed Chrome.
87
- - Build, lint and test the origami runtime and site generator when changing their contract or
87
+ - Build, lint and test system-components and the site generator when changing their contract or
88
88
  minimum versions. Static producer fixture provenance lives in test/fixtures/css/README.md.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/origami-site-runtime",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Vite development and build helpers for wix-import static sites",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -19,7 +19,8 @@
19
19
  "@babel/types": "^7.25.2",
20
20
  "@wix/builder-error-pages": "^1.1.6",
21
21
  "@wix/builder-piler-components": "^1.6.7",
22
- "@wix/origami-runtime": "0.11.0",
22
+ "@wix/origami-fonts": "1.0.0",
23
+ "@wix/origami-system-components": "1.5.0",
23
24
  "postcss": "^8.5.3",
24
25
  "postcss-custom-media": "^12.0.1",
25
26
  "react": "^18.3.1",
@@ -64,5 +65,5 @@
64
65
  "./default-error-404": "./src/default-error-404.jsx"
65
66
  },
66
67
  "type": "module",
67
- "falconPackageHash": "6816322c222999dcbe8c51a591cff934a894bfc9b63294e23c90b7a7"
68
+ "falconPackageHash": "0990c03591853c63cfb1830321eeb582aefc540da51c85616b51d879"
68
69
  }
@@ -0,0 +1,93 @@
1
+ // Checks the theme's font tokens (`--wst-heading-1-font: normal normal 700 --spx(88)/1em benzin`). It changes
2
+ // nothing. The editor reads the family at the end of each one as one Wix font id and fails the import otherwise, so
3
+ // the build fails, with every such token listed, unless the family is exactly the id of a font in the Wix font
4
+ // catalog bundled in @wix/origami-fonts: no quotes, commas or fallbacks, the same case. A legacy font, which the
5
+ // editor no longer offers, counts as not in the catalog. `origami search-font` finds the right id.
6
+
7
+ import { basename } from 'node:path'
8
+ import { fonts } from '@wix/origami-fonts'
9
+ import { LayoutError } from '../transform/layoutError.js'
10
+
11
+ const FONT_TOKEN = /^--wst-[\w-]+-font$/
12
+ // The size in a `font` shorthand, with its optional line height: the family follows it. The number's quantifiers
13
+ // never overlap, so a long run of digits that is not a size fails in linear time.
14
+ const SIZE = /^(?:--spx\(.*\)|calc\(.*\)|(?:\d+(?:\.\d*)?|\.\d+)(?:px|em|rem|%|vw|vh|pt)?)(?:\/\S+)?$/i
15
+ const FONT_IDS = new Set(fonts.filter(font => font.permissions !== 'legacy').map(font => font.id))
16
+
17
+ /** A value split on top-level whitespace, so `--spx(16)/1.3em` stays one token. */
18
+ const tokens = value => {
19
+ const result = []
20
+ let depth = 0
21
+ let current = ''
22
+ for (const char of value.trim()) {
23
+ if (char === '(') depth++
24
+ else if (char === ')') depth--
25
+ if (depth === 0 && /\s/.test(char)) {
26
+ if (current) result.push(current)
27
+ current = ''
28
+ } else current += char
29
+ }
30
+ if (current) result.push(current)
31
+ return result
32
+ }
33
+
34
+ /** A font shorthand's family as written; undefined for `var(…)`. */
35
+ const familyOf = value => {
36
+ if (/var\(/.test(value)) return undefined
37
+ const parts = tokens(value)
38
+ // The last size: the repo's TS lib (ES2022) has no findLastIndex.
39
+ let size = parts.length - 1
40
+ while (size >= 0 && !SIZE.test(parts[size])) size--
41
+ if (size === -1 || size === parts.length - 1) return undefined
42
+ return parts.slice(size + 1).join(' ')
43
+ }
44
+
45
+ /** ` (theme.css:12)`: the carrier the declaration was written in, which the bundler doesn't name. */
46
+ const where = declaration => {
47
+ const file = declaration.source?.input.file
48
+ const line = declaration.source?.start?.line
49
+ return file ? ` (${basename(file)}${line ? `:${line}` : ''})` : ''
50
+ }
51
+
52
+ /**
53
+ * Where `source` starts, as LayoutError's location, leaving out what it doesn't know.
54
+ * @param {import('postcss').Declaration['source']} source
55
+ */
56
+ const locationOf = source => {
57
+ /** @type {{ file?: string, line?: number, column?: number }} */
58
+ const location = {}
59
+ if (source?.input.file !== undefined) location.file = source.input.file
60
+ if (source?.start !== undefined) {
61
+ location.line = source.start.line
62
+ location.column = source.start.column
63
+ }
64
+ return location
65
+ }
66
+
67
+ /** @type {import('../transform/types.js').CssTransformPlugin} */
68
+ export const fontsPlugin = {
69
+ name: 'fonts',
70
+ // Only checks; it reads the tokens as authored, before spx rewrites the sizes the family follows.
71
+ order: 5,
72
+ transform(root) {
73
+ /** @type {import('postcss').Declaration[]} */
74
+ const declarations = []
75
+ root.walkDecls(FONT_TOKEN, declaration => {
76
+ declarations.push(declaration)
77
+ })
78
+ const violations = []
79
+ let first
80
+ for (const declaration of declarations) {
81
+ const family = familyOf(declaration.value)
82
+ if (family === undefined || FONT_IDS.has(family)) continue
83
+ first ??= declaration.source
84
+ violations.push(
85
+ `${declaration.prop}${where(declaration)} must name exactly one Wix font id from the catalog; found ` +
86
+ `${family}. Run \`origami search-font --font <name>\` for the id.`
87
+ )
88
+ }
89
+ if (violations.length) {
90
+ throw new LayoutError(violations, locationOf(first))
91
+ }
92
+ },
93
+ }
@@ -7,15 +7,23 @@
7
7
  // stage guarantees a plugin sees. The order matters once: mixin bodies are copied into their calling rules before
8
8
  // layout routes declarations to wrappers and SPX converts calls using the browser's active settings. stack-children
9
9
  // only checks, and runs between mixins and layout: it needs the mixins' declarations and the stack rules as authored,
10
- // before layout compiles them away.
10
+ // before layout compiles them away. fonts only checks the theme's font tokens, as authored.
11
11
  //
12
12
  // `@layer` needs no plugin: cascade layers have been in every browser since March 2022.
13
13
 
14
14
  import { customMediaPlugin } from './customMedia.js'
15
+ import { fontsPlugin } from './fonts.js'
15
16
  import { mixinsPlugin } from './mixins.js'
16
17
  import { layoutPlugin } from './layout/index.js'
17
18
  import { spxPlugin } from './spx/index.js'
18
19
  import { stackChildrenPlugin } from './stackChildren.js'
19
20
 
20
21
  /** @type {readonly import('../transform/types.js').CssTransformPlugin[]} */
21
- export const cssPlugins = Object.freeze([customMediaPlugin, mixinsPlugin, stackChildrenPlugin, layoutPlugin, spxPlugin])
22
+ export const cssPlugins = Object.freeze([
23
+ customMediaPlugin,
24
+ fontsPlugin,
25
+ mixinsPlugin,
26
+ stackChildrenPlugin,
27
+ layoutPlugin,
28
+ spxPlugin,
29
+ ])
@@ -1,5 +1,5 @@
1
1
  import postcss from 'postcss'
2
- import { buildContainerClasses } from '@wix/origami-runtime/layout-contract'
2
+ import { buildContainerClasses } from '@wix/origami-system-components/layout-contract'
3
3
  import { CONTENT } from './contract.js'
4
4
  import { isLayoutSource, parseTargets } from './parser.js'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { basename } from 'node:path'
2
- import { isValidContainerId } from '@wix/origami-runtime/layout-contract'
2
+ import { isValidContainerId } from '@wix/origami-system-components/layout-contract'
3
3
  import { relativeToSite } from '../../routing/siteLayout.js'
4
4
  import { CONTENT, OVERFLOW, targets, displays } from './contract.js'
5
5
 
@@ -1,18 +1,26 @@
1
1
  // Runs the selected CSS plugins over one shared Root, in order. Parsing, stringifying and source
2
2
  // maps belong to PostCSS, which drives this stage — the only thing added here is attributing a
3
3
  // failure to the plugin and the file that caused it. A LayoutError is the author's, so it passes through.
4
+ //
5
+ // A LayoutError comes from a plugin that only checks, so the Root is as it was and the later plugins still run:
6
+ // the author gets every plugin's violations in one error, at the first one's position, not one plugin's per build.
4
7
 
5
8
  import { LayoutError } from '../layoutError.js'
6
9
 
7
10
  const reason = cause => (cause instanceof Error ? cause.message : String(cause))
8
11
 
9
12
  export const runCssPlugins = ({ plugins, root, context }) => {
13
+ /** @type {LayoutError[]} */
14
+ const layoutErrors = []
10
15
  for (const phase of ['validate', 'transform']) {
11
16
  for (const plugin of plugins) {
12
17
  try {
13
18
  plugin[phase]?.(root, context)
14
19
  } catch (cause) {
15
- if (cause instanceof LayoutError) throw cause
20
+ if (cause instanceof LayoutError) {
21
+ layoutErrors.push(cause)
22
+ continue
23
+ }
16
24
  // Tooling such as Vite's error overlay prints only the message, so the reason goes in it as well. A
17
25
  // plugin may throw any value, so it is read without assuming an Error.
18
26
  throw new Error(
@@ -22,4 +30,11 @@ export const runCssPlugins = ({ plugins, root, context }) => {
22
30
  }
23
31
  }
24
32
  }
33
+ if (layoutErrors.length === 1) throw layoutErrors[0]
34
+ if (layoutErrors.length > 1) {
35
+ throw new LayoutError(
36
+ layoutErrors.flatMap(error => error.violations),
37
+ layoutErrors[0].location
38
+ )
39
+ }
25
40
  }
@@ -12,6 +12,8 @@ export class LayoutError extends Error {
12
12
  super(violations.length === 1 ? violations[0] : `\n ${violations.join('\n ')}`)
13
13
  this.name = 'LayoutError'
14
14
  this.stack = ''
15
+ this.violations = violations
16
+ this.location = location
15
17
  Object.assign(this, location)
16
18
  }
17
19
  }