@wix/origami-site-runtime 1.0.8 → 1.0.9
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/origami-site-runtime",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Vite development and build helpers for wix-import static sites",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,6 +19,7 @@
|
|
|
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-fonts": "1.0.0",
|
|
22
23
|
"@wix/origami-runtime": "0.11.0",
|
|
23
24
|
"postcss": "^8.5.3",
|
|
24
25
|
"postcss-custom-media": "^12.0.1",
|
|
@@ -64,5 +65,5 @@
|
|
|
64
65
|
"./default-error-404": "./src/default-error-404.jsx"
|
|
65
66
|
},
|
|
66
67
|
"type": "module",
|
|
67
|
-
"falconPackageHash": "
|
|
68
|
+
"falconPackageHash": "641cce00719388c9e1fbc0fd6cab982b52a23c4daf02f37771b70615"
|
|
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
|
+
}
|
package/src/cssPlugins/index.js
CHANGED
|
@@ -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([
|
|
22
|
+
export const cssPlugins = Object.freeze([
|
|
23
|
+
customMediaPlugin,
|
|
24
|
+
fontsPlugin,
|
|
25
|
+
mixinsPlugin,
|
|
26
|
+
stackChildrenPlugin,
|
|
27
|
+
layoutPlugin,
|
|
28
|
+
spxPlugin,
|
|
29
|
+
])
|
|
@@ -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)
|
|
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
|
}
|