@wix/origami-transforms 0.1.7 → 0.1.8

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.8",
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": "09b1c7aec92977ffa2db7c85a87daf406d7628c92600cbe85ed3ef94"
58
58
  }
@@ -1,17 +1,19 @@
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, anything else → horizontal, as in CSS).
7
+ //
8
+ // Parent and child are the JSX tree's, so the tree comes from the item's structure file (see
9
+ // structureFile.js). The stylesheet is the page's, with its carriers already inlined (see
10
+ // ../transform/css/postcssPlugin.js), so every rule is in view.
9
11
 
10
12
  import { basename } from 'node:path'
11
13
  import { LayoutError } from '../transform/layoutError.js'
12
14
  import { literalIdOf, parseStructure, structureFileOf, walk } from './structureFile.js'
13
15
 
14
- const STACK_CONTAINER = 'StackContainerLayout'
16
+ const CONTENT_WRAPPER = 'internal-container-content-wrapper'
15
17
  const BASE = ''
16
18
 
17
19
  // What a stack child may write, per direction. `0` is allowed wherever a length is.
@@ -79,38 +81,63 @@ const conditionOf = rule => {
79
81
  return conditions.join(' / ')
80
82
  }
81
83
 
82
- /** Id → condition → the declarations that rules selecting exactly `#id` make there, in source order. */
83
- const declarationsById = root => {
84
+ // Exports give an element both `#id` and `.id`, and write its CSS on the class.
85
+ const OWN_SELECTOR = /^([#.])([\w-]+)$/
86
+
87
+ const isContentWrapper = node =>
88
+ node.type === 'atrule' && node.name === 'origami-target' && node.params.trim() === CONTENT_WRAPPER
89
+
90
+ /** Id → condition → `.`/`#` → declarations, in source order. With `content`, a content wrapper block counts. */
91
+ const declarationsById = (root, { content = false } = {}) => {
84
92
  const byId = new Map()
85
93
  root.walkRules(rule => {
86
- const ids = rule.selectors.map(selector => /^#([\w-]+)$/.exec(selector.trim())?.[1]).filter(Boolean)
87
- if (!ids.length) return
94
+ const own = rule.selectors.map(selector => OWN_SELECTOR.exec(selector.trim())).filter(Boolean)
95
+ if (!own.length) return
88
96
  const condition = conditionOf(rule)
89
- for (const id of ids) {
97
+ for (const [, kind, id] of own) {
90
98
  const byCondition = byId.get(id) ?? new Map()
91
99
  byId.set(id, byCondition)
92
- const declarations = byCondition.get(condition) ?? []
93
- byCondition.set(condition, declarations)
100
+ const byKind = byCondition.get(condition) ?? { '.': [], '#': [] }
101
+ byCondition.set(condition, byKind)
94
102
  rule.each(node => {
95
- if (node.type === 'decl') declarations.push(node)
103
+ if (node.type === 'decl') byKind[kind].push(node)
104
+ else if (content && isContentWrapper(node)) {
105
+ node.each(inner => {
106
+ if (inner.type === 'decl') byKind[kind].push(inner)
107
+ })
108
+ }
96
109
  })
97
110
  }
98
111
  })
99
112
  return byId
100
113
  }
101
114
 
102
- /** The declarations in effect under `condition`: the base ones, then the condition's own. */
115
+ /**
116
+ * The declarations in effect under `condition`: the base ones, then the condition's own; `#id` beats `.id`, and
117
+ * `!important` beats both.
118
+ */
103
119
  const effective = (byCondition, condition) => {
104
120
  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)
121
+ for (const important of [false, true]) {
122
+ for (const kind of ['.', '#']) {
123
+ for (const key of condition === BASE ? [BASE] : [BASE, condition]) {
124
+ for (const declaration of byCondition?.get(key)?.[kind] ?? []) {
125
+ if (Boolean(declaration.important) === important) result.set(declaration.prop, declaration)
126
+ }
127
+ }
128
+ }
107
129
  }
108
130
  return result
109
131
  }
110
132
 
133
+ const selectorOf = (declaration, id) =>
134
+ declaration.parent.selectors.some(selector => selector.trim() === `#${id}`) ? `#${id}` : `.${id}`
135
+
111
136
  /** 'vertical' or 'horizontal' when these declarations make a stack, else undefined. */
112
137
  const directionOf = declarations => {
113
- if (declarations.get('--container-layout-type')?.value.trim() !== STACK_CONTAINER) return undefined
138
+ if (declarations.get('display')?.value.trim() !== 'flex') return undefined
139
+ const wrap = declarations.get('flex-wrap')?.value.trim()
140
+ if (wrap !== undefined && wrap !== 'nowrap') return undefined
114
141
  return /^column/.test(declarations.get('flex-direction')?.value.trim() ?? '') ? 'vertical' : 'horizontal'
115
142
  }
116
143
 
@@ -135,12 +162,13 @@ export const stackChildrenPlugin = {
135
162
  const parents = parentsIn(structurePath)
136
163
  if (parents.size === 0) return
137
164
  const byId = declarationsById(root)
165
+ const layoutById = declarationsById(root, { content: true })
138
166
 
139
167
  const violations = new Set()
140
168
  let first
141
169
  for (const [child, parent] of parents) {
142
170
  const childRules = byId.get(child)
143
- const parentRules = byId.get(parent)
171
+ const parentRules = layoutById.get(parent)
144
172
  if (childRules === undefined || parentRules === undefined) continue
145
173
  for (const condition of new Set([BASE, ...parentRules.keys(), ...childRules.keys()])) {
146
174
  const direction = directionOf(effective(parentRules, condition))
@@ -156,8 +184,8 @@ export const stackChildrenPlugin = {
156
184
  if (!bad.length) continue
157
185
  first ??= declaration.source
158
186
  violations.add(
159
- `#${child}${where(declaration)}, a ${direction} stack child: ${declaration.prop} must be ` +
160
- `${or(allowed.map(unit => LABELS[unit]))}; found ${bad.join(' ')}.`
187
+ `${selectorOf(declaration, child)}${where(declaration)}, a ${direction} stack child: ` +
188
+ `${declaration.prop} must be ${or(allowed.map(unit => LABELS[unit]))}; found ${bad.join(' ')}.`
161
189
  )
162
190
  }
163
191
  }
@@ -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'