@wix/origami-transforms 0.1.7 → 0.1.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-transforms",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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": "9674cee94f59f7db338de844100c7527c7eeddff5e2d58f750c8f488"
57
+ "falconPackageHash": "3225c4d857f7c4821fc78ea579b9a416bd05abd58f4bf0b0ac7906c0"
58
58
  }
@@ -1,17 +1,20 @@
1
1
  // Checks the CSS of a stack's direct children (rule S2-STACK-001). It changes nothing: every violation in the
2
2
  // stylesheet is collected, and the build fails once with all of them.
3
3
  //
4
- // A stack is a parent whose rule declares `--container-layout-type: StackContainerLayout`; its
5
- // `flex-direction` says which way it stacks (column → vertical, anything else → horizontal, as in
6
- // CSS). Parent and child are the JSX tree's, so the tree comes from the item's structure file (see
7
- // structureFile.js). The stylesheet is the page's, with its
8
- // carriers already inlined (see ../transform/css/postcssPlugin.js), so every rule is in view.
4
+ // A stack is a parent laid out with a non-wrapping flex, as DM derives it; an internal container writes its
5
+ // layout in its `@origami-target internal-container-content-wrapper` block. `flex-direction` says which way
6
+ // it stacks (column → vertical, row or none → horizontal). Any other value, such as `inherit` or `var(--d)`,
7
+ // fails DM's import, so its children aren't checked here.
8
+ //
9
+ // Parent and child are the JSX tree's, so the tree comes from the item's structure file (see
10
+ // structureFile.js). The stylesheet is the page's, with its carriers already inlined (see
11
+ // ../transform/css/postcssPlugin.js), so every rule is in view.
9
12
 
10
13
  import { basename } from 'node:path'
11
14
  import { LayoutError } from '../transform/layoutError.js'
12
15
  import { literalIdOf, parseStructure, structureFileOf, walk } from './structureFile.js'
13
16
 
14
- const STACK_CONTAINER = 'StackContainerLayout'
17
+ const CONTENT_WRAPPER = 'internal-container-content-wrapper'
15
18
  const BASE = ''
16
19
 
17
20
  // What a stack child may write, per direction. `0` is allowed wherever a length is.
@@ -79,39 +82,68 @@ const conditionOf = rule => {
79
82
  return conditions.join(' / ')
80
83
  }
81
84
 
82
- /** Id → condition → the declarations that rules selecting exactly `#id` make there, in source order. */
83
- const declarationsById = root => {
85
+ // Exports give an element both `#id` and `.id`, and write its CSS on the class.
86
+ const OWN_SELECTOR = /^([#.])([\w-]+)$/
87
+
88
+ const isContentWrapper = node =>
89
+ node.type === 'atrule' && node.name === 'origami-target' && node.params.trim() === CONTENT_WRAPPER
90
+
91
+ /** Id → condition → `.`/`#` → declarations, in source order. With `content`, a content wrapper block counts. */
92
+ const declarationsById = (root, { content = false } = {}) => {
84
93
  const byId = new Map()
85
94
  root.walkRules(rule => {
86
- const ids = rule.selectors.map(selector => /^#([\w-]+)$/.exec(selector.trim())?.[1]).filter(Boolean)
87
- if (!ids.length) return
95
+ const own = rule.selectors.map(selector => OWN_SELECTOR.exec(selector.trim())).filter(Boolean)
96
+ if (!own.length) return
88
97
  const condition = conditionOf(rule)
89
- for (const id of ids) {
98
+ for (const [, kind, id] of own) {
90
99
  const byCondition = byId.get(id) ?? new Map()
91
100
  byId.set(id, byCondition)
92
- const declarations = byCondition.get(condition) ?? []
93
- byCondition.set(condition, declarations)
101
+ const byKind = byCondition.get(condition) ?? { '.': [], '#': [] }
102
+ byCondition.set(condition, byKind)
94
103
  rule.each(node => {
95
- if (node.type === 'decl') declarations.push(node)
104
+ if (node.type === 'decl') byKind[kind].push(node)
105
+ else if (content && isContentWrapper(node)) {
106
+ node.each(inner => {
107
+ if (inner.type === 'decl') byKind[kind].push(inner)
108
+ })
109
+ }
96
110
  })
97
111
  }
98
112
  })
99
113
  return byId
100
114
  }
101
115
 
102
- /** The declarations in effect under `condition`: the base ones, then the condition's own. */
116
+ /**
117
+ * The declarations in effect under `condition`: the base ones, then the condition's own; `#id` beats `.id`, and
118
+ * `!important` beats both.
119
+ */
103
120
  const effective = (byCondition, condition) => {
104
121
  const result = new Map()
105
- for (const key of condition === BASE ? [BASE] : [BASE, condition]) {
106
- for (const declaration of byCondition?.get(key) ?? []) result.set(declaration.prop, declaration)
122
+ for (const important of [false, true]) {
123
+ for (const kind of ['.', '#']) {
124
+ for (const key of condition === BASE ? [BASE] : [BASE, condition]) {
125
+ for (const declaration of byCondition?.get(key)?.[kind] ?? []) {
126
+ if (Boolean(declaration.important) === important) result.set(declaration.prop, declaration)
127
+ }
128
+ }
129
+ }
107
130
  }
108
131
  return result
109
132
  }
110
133
 
134
+ const selectorOf = (declaration, id) =>
135
+ declaration.parent.selectors.some(selector => selector.trim() === `#${id}`) ? `#${id}` : `.${id}`
136
+
137
+ // The flex-direction values DM's import accepts, matched as written.
138
+ const DIRECTIONS = { row: 'horizontal', 'row-reverse': 'horizontal', column: 'vertical', 'column-reverse': 'vertical' }
139
+
111
140
  /** 'vertical' or 'horizontal' when these declarations make a stack, else undefined. */
112
141
  const directionOf = declarations => {
113
- if (declarations.get('--container-layout-type')?.value.trim() !== STACK_CONTAINER) return undefined
114
- return /^column/.test(declarations.get('flex-direction')?.value.trim() ?? '') ? 'vertical' : 'horizontal'
142
+ if (declarations.get('display')?.value.trim() !== 'flex') return undefined
143
+ const wrap = declarations.get('flex-wrap')?.value.trim()
144
+ if (wrap !== undefined && wrap !== 'nowrap') return undefined
145
+ const direction = declarations.get('flex-direction')?.value.trim() ?? 'row'
146
+ return Object.hasOwn(DIRECTIONS, direction) ? DIRECTIONS[direction] : undefined
115
147
  }
116
148
 
117
149
  /** ` (layout.css:12)`: the carrier the declaration was written in, which the bundler doesn't name. */
@@ -135,12 +167,13 @@ export const stackChildrenPlugin = {
135
167
  const parents = parentsIn(structurePath)
136
168
  if (parents.size === 0) return
137
169
  const byId = declarationsById(root)
170
+ const layoutById = declarationsById(root, { content: true })
138
171
 
139
172
  const violations = new Set()
140
173
  let first
141
174
  for (const [child, parent] of parents) {
142
175
  const childRules = byId.get(child)
143
- const parentRules = byId.get(parent)
176
+ const parentRules = layoutById.get(parent)
144
177
  if (childRules === undefined || parentRules === undefined) continue
145
178
  for (const condition of new Set([BASE, ...parentRules.keys(), ...childRules.keys()])) {
146
179
  const direction = directionOf(effective(parentRules, condition))
@@ -156,8 +189,8 @@ export const stackChildrenPlugin = {
156
189
  if (!bad.length) continue
157
190
  first ??= declaration.source
158
191
  violations.add(
159
- `#${child}${where(declaration)}, a ${direction} stack child: ${declaration.prop} must be ` +
160
- `${or(allowed.map(unit => LABELS[unit]))}; found ${bad.join(' ')}.`
192
+ `${selectorOf(declaration, child)}${where(declaration)}, a ${direction} stack child: ` +
193
+ `${declaration.prop} must be ${or(allowed.map(unit => LABELS[unit]))}; found ${bad.join(' ')}.`
161
194
  )
162
195
  }
163
196
  }
@@ -1,6 +1,6 @@
1
1
  // The item's structure file, for CSS plugins that check a stylesheet against its JSX tree: `structure.tsx` (or
2
2
  // `.jsx`) next to the stylesheet, or one folder up when the stylesheet is the generated one in `_generated/`.
3
- // Elements and rules meet on a literal `id` and `#id`.
3
+ // Elements and rules meet on a literal `id` and `#id` (or `.id`).
4
4
 
5
5
  import { existsSync, readFileSync } from 'node:fs'
6
6
  import { basename, dirname, join } from 'node:path'