@wix/origami-transforms 0.1.9 → 0.1.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.
@@ -1,2 +1,3 @@
1
+ export function removeCustomEnums(root: any): any;
1
2
  /** @type {import('../transform/types.js').CssTransformPlugin} */
2
3
  export const customEnumsPlugin: import("../transform/types.js").CssTransformPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/origami-transforms",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Babel and PostCSS checks and transforms for Wix origami page files.",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -54,5 +54,5 @@
54
54
  },
55
55
  "type": "module",
56
56
  "types": "./dist/index.d.ts",
57
- "falconPackageHash": "3225c4d857f7c4821fc78ea579b9a416bd05abd58f4bf0b0ac7906c0"
57
+ "falconPackageHash": "7e56e4dabc968e17605f0b4d379f65ad4b9222d482fb82ddc70a9cbe"
58
58
  }
@@ -1,19 +1,21 @@
1
1
  // Resolves a component's custom enums at build time. A site sets only the enum's value on an instance,
2
2
  // `#comp-1 { --labelAlign: center }`, and this writes the vars that option applies in its place, from the
3
3
  // `@custom-enum` blocks in the component wrapper's `custom-enums.css` (see component-wrapper-server's
4
- // `customEnums.ts`), which the page imports and the bundler inlines. The blocks are removed from the output.
4
+ // `customEnums.ts`), read from the wrapper's folder: the item's JSX imports the wrapper's CSS, so the blocks aren't in
5
+ // the stylesheet. A stylesheet that `@import`s the wrapper's CSS has them inlined; those are used and removed.
5
6
  // Native `if()` can't do it: Firefox doesn't have it, and a var named like its key refers to itself.
6
7
  //
7
8
  // The instance's component comes from the item's structure file (structureFile.js): the element with
8
9
  // `id="comp-1"`, and the `@origami-wrappers/…` import it is rendered from. See customEnums.md.
9
10
 
10
- import { realpathSync } from 'node:fs'
11
+ import { existsSync, readFileSync, realpathSync } from 'node:fs'
11
12
  import { createRequire } from 'node:module'
12
- import { basename, dirname } from 'node:path'
13
+ import { basename, dirname, join } from 'node:path'
13
14
  import postcss from 'postcss'
14
15
  import { LayoutError } from '../transform/layoutError.js'
15
16
  import { literalIdOf, parseStructure, structureFileOf, walk } from './structureFile.js'
16
17
 
18
+ const CUSTOM_ENUMS_CSS = 'custom-enums.css'
17
19
  // `@origami-wrappers/<module>/<symbol>`, and nothing that could resolve outside its package.
18
20
  const WRAPPER_IMPORT = /^@origami-wrappers\/[\w-]+\/[\w-]+$/
19
21
  // `#id` or `.id` (exports give an instance both), then only what narrows that same element: classes,
@@ -49,10 +51,10 @@ const real = path => {
49
51
  }
50
52
 
51
53
  /**
52
- * Id → the enums of every wrapper instance; none for one whose component has no custom enums. An instance's enums
53
- * are the `@custom-enum` blocks from its wrapper's folder.
54
+ * Id → the enums of every wrapper instance; none for one whose component has no custom enums. `enumsOf` gives a
55
+ * wrapper folder's enums.
54
56
  */
55
- const enumsById = (structurePath, byFolder) => {
57
+ const enumsById = (structurePath, enumsOf) => {
56
58
  const require = createRequire(structurePath)
57
59
  const byId = new Map()
58
60
  for (const [id, source] of importsById(structurePath)) {
@@ -62,7 +64,7 @@ const enumsById = (structurePath, byFolder) => {
62
64
  } catch {
63
65
  folder = undefined
64
66
  }
65
- byId.set(id, (folder !== undefined && byFolder.get(folder)) || [])
67
+ byId.set(id, folder === undefined ? [] : enumsOf(folder))
66
68
  }
67
69
  return byId
68
70
  }
@@ -89,7 +91,7 @@ const fromDependency = declaration => declaration.source?.input.file?.split(/[\\
89
91
 
90
92
  /**
91
93
  * The `@custom-enum` blocks in the stylesheet: wrapper folder → its enums, with each option as the enum's full var
92
- * set; the blocks themselves, to remove; and the ones that can't be read.
94
+ * set; the blocks themselves; and the ones that can't be read.
93
95
  *
94
96
  * A block sits in its element's rule, `.menu`, or `.menu .menu-input` for an inline element. A var an option doesn't
95
97
  * set has the default option's value, or `inherit` when the default doesn't set it either, which is what an instance
@@ -188,17 +190,42 @@ const inlineRule = (declaration, instance, { inline, editorSelector }, options,
188
190
  inserted.set(outer, node)
189
191
  }
190
192
 
193
+ /** Removes the stylesheet's `@custom-enum` blocks, which only this plugin reads. */
194
+ export const removeCustomEnums = root =>
195
+ root.walkAtRules('custom-enum', block => {
196
+ if (block.parent) block.parent.raws.semicolon = true
197
+ block.remove()
198
+ })
199
+
191
200
  /** @type {import('../transform/types.js').CssTransformPlugin} */
192
201
  export const customEnumsPlugin = {
193
202
  name: 'custom-enums',
194
203
  // After mixins copy their bodies in, before presets and layout move declarations out of the instance's rule.
195
204
  order: 11,
196
205
  transform(root, context) {
197
- const { byFolder, blocks, malformed } = definitionsIn(root)
198
- if (blocks.length === 0) return
206
+ const inlined = definitionsIn(root)
199
207
  const structurePath = structureFileOf(context.filePath)
208
+ if (structurePath === undefined && inlined.blocks.length === 0) return
200
209
  if (structurePath !== undefined) context.addDependency?.(structurePath)
201
- const instances = structurePath === undefined ? new Map() : enumsById(structurePath, byFolder)
210
+ const malformed = [...inlined.malformed]
211
+ // A wrapper's enums are the blocks inlined from its folder, or else those in the folder's custom-enums.css.
212
+ const readFolders = new Map()
213
+ const enumsOf = folder => {
214
+ if (inlined.byFolder.has(folder)) return inlined.byFolder.get(folder)
215
+ if (!readFolders.has(folder)) {
216
+ const file = join(folder, CUSTOM_ENUMS_CSS)
217
+ let enums = []
218
+ if (existsSync(file)) {
219
+ context.addDependency?.(file)
220
+ const definitions = definitionsIn(postcss.parse(readFileSync(file, 'utf8'), { from: file }))
221
+ malformed.push(...definitions.malformed)
222
+ enums = definitions.byFolder.get(folder) ?? []
223
+ }
224
+ readFolders.set(folder, enums)
225
+ }
226
+ return readFolders.get(folder)
227
+ }
228
+ const instances = structurePath === undefined ? new Map() : enumsById(structurePath, enumsOf)
202
229
  const keys = new Set([...instances.values()].flatMap(enums => enums.map(({ key }) => key)))
203
230
 
204
231
  /** @type {import('postcss').Declaration[]} */
@@ -282,10 +309,7 @@ export const customEnumsPlugin = {
282
309
  })
283
310
  }
284
311
 
285
- for (const block of blocks) {
286
- if (block.parent) block.parent.raws.semicolon = true
287
- block.remove()
288
- }
312
+ removeCustomEnums(root)
289
313
 
290
314
  // A rule with a selector list is split, one rule per selector, so each instance gets its own option.
291
315
  for (const rule of new Set(declarations.map(ruleOf))) {
@@ -57,15 +57,17 @@ the defaults:
57
57
  }
58
58
  ```
59
59
 
60
- The page's generated stylesheet `@import`s each used wrapper's `defaults.css`, which imports this file, and
61
- the bundler inlines it, so the blocks are in the document the plugin gets. The plugin reads them and removes
62
- them from the output. Without the plugin, browsers ignore the unknown at-rule and the defaults still apply.
60
+ The item's JSX imports each used wrapper's `defaults.css`, which imports this file, so the browser loads
61
+ it ahead of the site's stylesheets, and the blocks are not in the document the plugin gets. The plugin reads
62
+ them from the wrapper's folder instead. The CSS stage removes them from the wrapper's own CSS, which runs no
63
+ other plugin; browsers would ignore the unknown at-rule anyway, and the defaults still apply. A stylesheet
64
+ that `@import`s `defaults.css` has the blocks inlined: the plugin reads those and removes them from the output.
63
65
  An inline element's block sits in `{editor selector} {inline selector}` (`.menu .menu-input`).
64
66
 
65
67
  The item's structure file (next to the stylesheet, or one folder up for `_generated/`) says which
66
68
  component each `id` is: the element `<Menu id="comp-1" />` and its `@origami-wrappers/…` import. The
67
- import resolves to the wrapper's folder, and the blocks inlined from that folder are its enums. The plugin
68
- declares the structure file, so editing it re-runs the stylesheet.
69
+ import resolves to the wrapper's folder, and that folder's `custom-enums.css` holds its enums. The plugin
70
+ declares the structure file and each `custom-enums.css` it reads, so editing either re-runs the stylesheet.
69
71
 
70
72
  Each option is written as the full set of the enum's vars: the default's value wherever the option itself is
71
73
  silent, or `inherit` when the default is silent too (what an instance that picks no option gets). So a mobile
@@ -8,6 +8,7 @@
8
8
  // stylesheet the plugins see is the whole page: a rule declared in one carrier and a rule that
9
9
  // depends on it in another arrive in the same document.
10
10
 
11
+ import { removeCustomEnums } from '../../cssPlugins/customEnums.js'
11
12
  import { runCssPlugins } from './runPlugins.js'
12
13
 
13
14
  const DEPENDENCY_DIRECTORY = 'node_modules/'
@@ -34,7 +35,12 @@ export const createCssPostcssPlugin = ({ selectPlugins, contextFor, failures })
34
35
  if (from === undefined) return
35
36
 
36
37
  const fileContext = contextFor(from)
37
- if (!isSiteOwned(fileContext.relativePath)) return
38
+ // A dependency's stylesheet runs no plugins. It only loses a wrapper's `@custom-enum` blocks, which the
39
+ // custom-enums plugin reads from the wrapper's folder and browsers would skip.
40
+ if (!isSiteOwned(fileContext.relativePath)) {
41
+ removeCustomEnums(root)
42
+ return
43
+ }
38
44
 
39
45
  const plugins = selectPlugins(fileContext)
40
46
  if (!plugins.length) return