@wix/origami-transforms 0.1.6 → 0.1.7
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.
|
|
3
|
+
"version": "0.1.7",
|
|
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": "
|
|
57
|
+
"falconPackageHash": "9674cee94f59f7db338de844100c7527c7eeddff5e2d58f750c8f488"
|
|
58
58
|
}
|
|
@@ -4,18 +4,16 @@
|
|
|
4
4
|
// `customEnums.ts`), which the page imports and the bundler inlines. The blocks are removed from the output.
|
|
5
5
|
// Native `if()` can't do it: Firefox doesn't have it, and a var named like its key refers to itself.
|
|
6
6
|
//
|
|
7
|
-
// The instance's component comes from the item's structure file
|
|
7
|
+
// The instance's component comes from the item's structure file (structureFile.js): the element with
|
|
8
8
|
// `id="comp-1"`, and the `@origami-wrappers/…` import it is rendered from. See customEnums.md.
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { realpathSync } from 'node:fs'
|
|
11
11
|
import { createRequire } from 'node:module'
|
|
12
|
-
import { basename, dirname
|
|
13
|
-
import { parseSync } from '@babel/core'
|
|
12
|
+
import { basename, dirname } from 'node:path'
|
|
14
13
|
import postcss from 'postcss'
|
|
15
14
|
import { LayoutError } from '../transform/layoutError.js'
|
|
15
|
+
import { literalIdOf, parseStructure, structureFileOf, walk } from './structureFile.js'
|
|
16
16
|
|
|
17
|
-
const STRUCTURE_FILES = ['structure.tsx', 'structure.jsx']
|
|
18
|
-
const GENERATED = '_generated'
|
|
19
17
|
// `@origami-wrappers/<module>/<symbol>`, and nothing that could resolve outside its package.
|
|
20
18
|
const WRAPPER_IMPORT = /^@origami-wrappers\/[\w-]+\/[\w-]+$/
|
|
21
19
|
// `#id` or `.id` (exports give an instance both), then only what narrows that same element: classes,
|
|
@@ -23,44 +21,21 @@ const WRAPPER_IMPORT = /^@origami-wrappers\/[\w-]+\/[\w-]+$/
|
|
|
23
21
|
// and no pseudo-elements, which the component doesn't read vars from.
|
|
24
22
|
const INSTANCE = /^[#.]([\w-]+)(?!.*::)(?:[.:[][^\s>+~]*)?((?:\s+[^\s>+~]+)*)$/
|
|
25
23
|
|
|
26
|
-
/** The folder holding the structure file a stylesheet is checked against: its own, or its item's for `_generated/`. */
|
|
27
|
-
const structureFolderOf = stylesheetPath => {
|
|
28
|
-
const folder = dirname(stylesheetPath)
|
|
29
|
-
return basename(folder) === GENERATED ? dirname(folder) : folder
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
/** Id → the wrapper import it is rendered from, for elements with a literal `id` and an imported wrapper tag. */
|
|
33
25
|
const importsById = structurePath => {
|
|
34
|
-
const
|
|
35
|
-
babelrc: false,
|
|
36
|
-
configFile: false,
|
|
37
|
-
filename: structurePath,
|
|
38
|
-
parserOpts: { plugins: structurePath.endsWith('.tsx') ? ['jsx', 'typescript'] : ['jsx'] },
|
|
39
|
-
})
|
|
26
|
+
const program = parseStructure(structurePath)
|
|
40
27
|
const sources = new Map()
|
|
41
|
-
for (const node of
|
|
28
|
+
for (const node of program?.body ?? []) {
|
|
42
29
|
if (node.type !== 'ImportDeclaration' || !WRAPPER_IMPORT.test(node.source.value)) continue
|
|
43
30
|
for (const specifier of node.specifiers) sources.set(specifier.local.name, node.source.value)
|
|
44
31
|
}
|
|
45
32
|
const byId = new Map()
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
attribute.type === 'JSXAttribute' &&
|
|
53
|
-
attribute.name.name === 'id' &&
|
|
54
|
-
attribute.value?.type === 'StringLiteral'
|
|
55
|
-
)?.value.value
|
|
56
|
-
const source = sources.get(node.name.name)
|
|
57
|
-
if (id !== undefined && source !== undefined) byId.set(id, source)
|
|
58
|
-
}
|
|
59
|
-
for (const [key, value] of Object.entries(node)) {
|
|
60
|
-
if (key !== 'loc' && value && typeof value === 'object') visit(value)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
visit(ast?.program)
|
|
33
|
+
walk(program, node => {
|
|
34
|
+
if (node.type !== 'JSXOpeningElement' || node.name.type !== 'JSXIdentifier') return
|
|
35
|
+
const id = literalIdOf(node)
|
|
36
|
+
const source = sources.get(node.name.name)
|
|
37
|
+
if (id !== undefined && source !== undefined) byId.set(id, source)
|
|
38
|
+
})
|
|
64
39
|
return byId
|
|
65
40
|
}
|
|
66
41
|
|
|
@@ -221,9 +196,7 @@ export const customEnumsPlugin = {
|
|
|
221
196
|
transform(root, context) {
|
|
222
197
|
const { byFolder, blocks, malformed } = definitionsIn(root)
|
|
223
198
|
if (blocks.length === 0) return
|
|
224
|
-
const structurePath =
|
|
225
|
-
existsSync
|
|
226
|
-
)
|
|
199
|
+
const structurePath = structureFileOf(context.filePath)
|
|
227
200
|
if (structurePath !== undefined) context.addDependency?.(structurePath)
|
|
228
201
|
const instances = structurePath === undefined ? new Map() : enumsById(structurePath, byFolder)
|
|
229
202
|
const keys = new Set([...instances.values()].flatMap(enums => enums.map(({ key }) => key)))
|
|
@@ -3,18 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
// A stack is a parent whose rule declares `--container-layout-type: StackContainerLayout`; its
|
|
5
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
|
|
7
|
-
//
|
|
8
|
-
// rules meet on `id` and `#id`. The stylesheet is the page's, with its
|
|
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
|
|
9
8
|
// carriers already inlined (see ../transform/css/postcssPlugin.js), so every rule is in view.
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { basename, dirname, join } from 'node:path'
|
|
13
|
-
import { parseSync } from '@babel/core'
|
|
10
|
+
import { basename } from 'node:path'
|
|
14
11
|
import { LayoutError } from '../transform/layoutError.js'
|
|
12
|
+
import { literalIdOf, parseStructure, structureFileOf, walk } from './structureFile.js'
|
|
15
13
|
|
|
16
|
-
const STRUCTURE_FILES = ['structure.tsx', 'structure.jsx']
|
|
17
|
-
const GENERATED = '_generated'
|
|
18
14
|
const STACK_CONTAINER = 'StackContainerLayout'
|
|
19
15
|
const BASE = ''
|
|
20
16
|
|
|
@@ -56,46 +52,21 @@ const lengthOf = token => {
|
|
|
56
52
|
return UNIT.exec(token)?.[1].toLowerCase()
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
/** The folder holding the structure file a stylesheet is checked against: its own, or its item's for `_generated/`. */
|
|
60
|
-
const structureFolderOf = stylesheetPath => {
|
|
61
|
-
const folder = dirname(stylesheetPath)
|
|
62
|
-
return basename(folder) === GENERATED ? dirname(folder) : folder
|
|
63
|
-
}
|
|
64
|
-
|
|
65
55
|
/** Child id → its direct parent's id, for elements with a literal `id`. Slot props are not children. */
|
|
66
56
|
const parentsIn = structurePath => {
|
|
67
|
-
const ast = parseSync(readFileSync(structurePath, 'utf8'), {
|
|
68
|
-
babelrc: false,
|
|
69
|
-
configFile: false,
|
|
70
|
-
filename: structurePath,
|
|
71
|
-
parserOpts: { plugins: structurePath.endsWith('.tsx') ? ['jsx', 'typescript'] : ['jsx'] },
|
|
72
|
-
})
|
|
73
57
|
const parents = new Map()
|
|
74
|
-
const idOf = element => {
|
|
75
|
-
const attribute = element.openingElement.attributes.find(
|
|
76
|
-
node => node.type === 'JSXAttribute' && node.name.name === 'id' && node.value?.type === 'StringLiteral'
|
|
77
|
-
)
|
|
78
|
-
return attribute?.value.value
|
|
79
|
-
}
|
|
80
58
|
const directChildren = node =>
|
|
81
59
|
node.children.flatMap(child =>
|
|
82
60
|
child.type === 'JSXElement' ? [child] : child.type === 'JSXFragment' ? directChildren(child) : []
|
|
83
61
|
)
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
const id = idOf(child)
|
|
91
|
-
if (parent !== undefined && id !== undefined) parents.set(id, parent)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
for (const [key, value] of Object.entries(node)) {
|
|
95
|
-
if (key !== 'loc' && value && typeof value === 'object') visit(value)
|
|
62
|
+
walk(parseStructure(structurePath), node => {
|
|
63
|
+
if (node.type !== 'JSXElement') return
|
|
64
|
+
const parent = literalIdOf(node.openingElement)
|
|
65
|
+
for (const child of directChildren(node)) {
|
|
66
|
+
const id = literalIdOf(child.openingElement)
|
|
67
|
+
if (parent !== undefined && id !== undefined) parents.set(id, parent)
|
|
96
68
|
}
|
|
97
|
-
}
|
|
98
|
-
visit(ast?.program)
|
|
69
|
+
})
|
|
99
70
|
return parents
|
|
100
71
|
}
|
|
101
72
|
|
|
@@ -158,9 +129,7 @@ export const stackChildrenPlugin = {
|
|
|
158
129
|
// After mixins copy their bodies in, before spx rewrites --spx() calls away.
|
|
159
130
|
order: 12,
|
|
160
131
|
transform(root, context) {
|
|
161
|
-
const structurePath =
|
|
162
|
-
existsSync
|
|
163
|
-
)
|
|
132
|
+
const structurePath = structureFileOf(context.filePath)
|
|
164
133
|
if (structurePath === undefined) return
|
|
165
134
|
context.addDependency?.(structurePath)
|
|
166
135
|
const parents = parentsIn(structurePath)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// The item's structure file, for CSS plugins that check a stylesheet against its JSX tree: `structure.tsx` (or
|
|
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`.
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
6
|
+
import { basename, dirname, join } from 'node:path'
|
|
7
|
+
import { parseSync } from '@babel/core'
|
|
8
|
+
|
|
9
|
+
const STRUCTURE_FILES = ['structure.tsx', 'structure.jsx']
|
|
10
|
+
const GENERATED = '_generated'
|
|
11
|
+
|
|
12
|
+
/** The structure file `stylesheetPath` is checked against, or undefined when its item has none. */
|
|
13
|
+
export const structureFileOf = stylesheetPath => {
|
|
14
|
+
const folder = dirname(stylesheetPath)
|
|
15
|
+
const item = basename(folder) === GENERATED ? dirname(folder) : folder
|
|
16
|
+
return STRUCTURE_FILES.map(file => join(item, file)).find(existsSync)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The structure file's program. */
|
|
20
|
+
export const parseStructure = structurePath =>
|
|
21
|
+
parseSync(readFileSync(structurePath, 'utf8'), {
|
|
22
|
+
babelrc: false,
|
|
23
|
+
configFile: false,
|
|
24
|
+
filename: structurePath,
|
|
25
|
+
parserOpts: { plugins: structurePath.endsWith('.tsx') ? ['jsx', 'typescript'] : ['jsx'] },
|
|
26
|
+
})?.program
|
|
27
|
+
|
|
28
|
+
/** Calls `visit` on every node under `node`, parents first. */
|
|
29
|
+
export const walk = (node, visit) => {
|
|
30
|
+
if (Array.isArray(node)) return node.forEach(child => walk(child, visit))
|
|
31
|
+
if (!node || typeof node.type !== 'string') return
|
|
32
|
+
visit(node)
|
|
33
|
+
for (const [key, value] of Object.entries(node)) {
|
|
34
|
+
if (key !== 'loc' && value && typeof value === 'object') walk(value, visit)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A JSX opening element's `id`, when it is a string literal. */
|
|
39
|
+
export const literalIdOf = openingElement =>
|
|
40
|
+
openingElement.attributes.find(
|
|
41
|
+
attribute =>
|
|
42
|
+
attribute.type === 'JSXAttribute' && attribute.name.name === 'id' && attribute.value?.type === 'StringLiteral'
|
|
43
|
+
)?.value.value
|