@woodsportal/hubspot-kit 5.0.0-beta.2 → 5.0.0-beta.4
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/CHANGELOG.md +0 -6
- package/package.json +2 -2
- package/scripts/tailwind/prefix-plugin.js +182 -131
- package/scripts/tailwind/tailwind-content.js +28 -29
- package/scripts/vite/create-dev-server-config.mjs +3 -14
- package/scripts/vite/portal-build-shared.js +3 -5
- package/src/dev-module-config/__tests__/fixtures/golden-module.fixture.ts +0 -2
- package/src/dev-module-config/__tests__/flatten-visible-fields.test.ts +1 -34
- package/src/dev-module-config/__tests__/module-config-transform.test.ts +0 -27
- package/src/dev-module-config/runtime/module-config-runtime.ts +1 -3
- package/src/dev-module-config/schema/validate-module-config.ts +0 -2
- package/src/dev-module-config/transform/build-hubspot-data.ts +0 -14
- package/src/dev-module-config/ui/FieldRenderer.tsx +0 -2
- package/src/dev-module-config/ui/ModuleConfigAnimatedPresence.tsx +2 -4
- package/src/dev-module-config/ui/ModuleConfigShell.tsx +4 -16
- package/src/dev-module-config/ui/editors/MenuOptionsEditor.tsx +0 -34
- package/src/dev-module-config/ui/hubspot/HsOccurrenceGroupEditor.tsx +11 -16
- package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +23 -27
- package/src/routing/build-api-routes.ts +15 -45
- package/templates/module-hybrid-cdn/src/assets/css/main.prod.css +3 -0
- package/scripts/tailwind/prefix-plugin.test.mjs +0 -110
- package/src/dev-module-config/__tests__/build-api-routes-custom-menu.test.ts +0 -32
- package/src/dev-module-config/__tests__/custom-menu.test.ts +0 -77
- package/src/dev-module-config/__tests__/drill-down-panels.test.ts +0 -65
- package/src/dev-module-config/schema/custom-menu.ts +0 -124
- package/src/dev-module-config/ui/navigation/drill-down-panels.ts +0 -48
- package/src/routing/custom-menu-path.ts +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Unreleased
|
|
4
|
-
|
|
5
|
-
### CSS prefix
|
|
6
|
-
- **Auto-prefix** — class string bindings ending in `_CLASS` (e.g. `CARDS_GRID_CLASS`) are prefixed the same as `*DynamicClassName*`
|
|
7
|
-
- **Auto-prefix** — JSX props ending in `ClassName` (`containerClassName`, `pageLinkClassName`, …) and helpers ending in `Class` (`paginationItemBaseClass`, `navBtnClass`) are prefixed
|
|
8
|
-
|
|
9
3
|
## [1.0.44] - 2026-08-06
|
|
10
4
|
|
|
11
5
|
### Portal auth scaffold
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woodsportal/hubspot-kit",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.4",
|
|
4
4
|
"packageManager": "pnpm@10.12.1",
|
|
5
5
|
"description": "WoodsCLI — HubSpot CMS dev kit by WoodsPortal. wp CLI, Vite presets, module-config overlay, and theme/module build pipelines.",
|
|
6
6
|
"type": "module",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"build": "tsup",
|
|
53
53
|
"dev": "tsup --watch",
|
|
54
54
|
"typecheck": "tsc --noEmit",
|
|
55
|
-
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/cli/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/wp-cli-smoke.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs'
|
|
55
|
+
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/cli/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/wp-cli-smoke.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
|
|
56
56
|
"test:examples": "node examples/smoke/run-matrix.mjs --tier static",
|
|
57
57
|
"test:dist-pack": "node --test scripts/dist-cli-pack-smoke.test.mjs",
|
|
58
58
|
"lint": "eslint src",
|
|
@@ -7,49 +7,15 @@ import { prefix } from './prefix.env.js'
|
|
|
7
7
|
const prefix1 = `${prefix}:`
|
|
8
8
|
const prefix2 = `${prefix}\:`
|
|
9
9
|
|
|
10
|
-
/** Bindings rewritten even when not in `className=`: `*DynamicClassName*`, `_CLASS`, `*ClassName`, `*Class`. */
|
|
11
|
-
export function isClassBindingName(name) {
|
|
12
|
-
if (!name) return false
|
|
13
|
-
return (
|
|
14
|
-
name.includes('DynamicClassName') ||
|
|
15
|
-
name.endsWith('_CLASS') ||
|
|
16
|
-
/(?:ClassName|Class)$/.test(name)
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** JSX / object keys: `className`, `containerClassName`, `pageLinkClassName`, … */
|
|
21
|
-
export function isClassAttrName(name) {
|
|
22
|
-
if (!name) return false
|
|
23
|
-
return name === 'className' || name.endsWith('ClassName')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function jsxAttrName(attr) {
|
|
27
|
-
if (t.isJSXIdentifier(attr.name)) return attr.name.name
|
|
28
|
-
return ''
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function objectKeyName(key) {
|
|
32
|
-
if (t.isIdentifier(key)) return key.name
|
|
33
|
-
if (t.isStringLiteral(key)) return key.value
|
|
34
|
-
return ''
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Vite serve ids often include `?t=` / `?v=` — strip those before extension checks. */
|
|
38
|
-
function filePathFromId(id) {
|
|
39
|
-
return (id.split('?')[0] ?? id).replaceAll('\\', '/')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
10
|
export default function TailwindPrefixPlugin() {
|
|
43
11
|
return {
|
|
44
12
|
name: 'vite-tailwind-prefix',
|
|
45
13
|
enforce: 'pre',
|
|
46
14
|
transform(code, id) {
|
|
47
|
-
const filename = filePathFromId(id)
|
|
48
|
-
|
|
49
15
|
// Handle CSS files: prefix custom class selectors safely
|
|
50
|
-
if (
|
|
16
|
+
if (id.endsWith('.css')) {
|
|
51
17
|
// don't need to add prefix to override.style.css
|
|
52
|
-
if (
|
|
18
|
+
if (id.endsWith('override.style.css')) return code;
|
|
53
19
|
|
|
54
20
|
|
|
55
21
|
const lines = code.split('\n')
|
|
@@ -95,7 +61,7 @@ export default function TailwindPrefixPlugin() {
|
|
|
95
61
|
}
|
|
96
62
|
|
|
97
63
|
|
|
98
|
-
if (!
|
|
64
|
+
if (!id.endsWith('.tsx') && !id.endsWith('.ts')) return null
|
|
99
65
|
|
|
100
66
|
try {
|
|
101
67
|
const ast = parse(code, {
|
|
@@ -129,84 +95,23 @@ export default function TailwindPrefixPlugin() {
|
|
|
129
95
|
.join(' ')
|
|
130
96
|
}
|
|
131
97
|
|
|
132
|
-
const
|
|
133
|
-
if (!expr) return expr
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
t.isTSAsExpression(expr) ||
|
|
137
|
-
t.isTSTypeAssertion(expr) ||
|
|
138
|
-
t.isTSSatisfiesExpression(expr) ||
|
|
139
|
-
t.isTSNonNullExpression(expr) ||
|
|
140
|
-
t.isParenthesizedExpression(expr)
|
|
141
|
-
) {
|
|
142
|
-
expr.expression = prefixExpr(expr.expression)
|
|
143
|
-
return expr
|
|
144
|
-
}
|
|
145
|
-
|
|
98
|
+
const prefixStringExpr = (expr) => {
|
|
146
99
|
if (t.isStringLiteral(expr)) {
|
|
147
100
|
return t.stringLiteral(prefixClasses(expr.value))
|
|
148
101
|
}
|
|
149
|
-
|
|
150
|
-
if (t.isTemplateLiteral(expr)) {
|
|
151
|
-
const quasis = expr.quasis.map((quasi, i) => {
|
|
152
|
-
let classes = prefixClasses(quasi.value.raw)
|
|
153
|
-
if (i < expr.expressions.length) classes += ' '
|
|
154
|
-
if (i > 0 && !classes.startsWith(' ')) classes = ' ' + classes
|
|
155
|
-
return t.templateElement({ raw: classes, cooked: classes })
|
|
156
|
-
})
|
|
157
|
-
const expressions = expr.expressions.map((inner) => prefixExpr(inner))
|
|
158
|
-
return t.templateLiteral(quasis, expressions)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
102
|
if (t.isConditionalExpression(expr)) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (t.isLogicalExpression(expr)) {
|
|
168
|
-
if (t.isStringLiteral(expr.left) || t.isTemplateLiteral(expr.left)) {
|
|
169
|
-
expr.left = prefixExpr(expr.left)
|
|
170
|
-
}
|
|
171
|
-
expr.right = prefixExpr(expr.right)
|
|
172
|
-
return expr
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (t.isObjectExpression(expr)) {
|
|
176
|
-
expr.properties.forEach((prop) => {
|
|
177
|
-
if (!t.isObjectProperty(prop) || prop.computed) return
|
|
178
|
-
prop.value = prefixExpr(prop.value)
|
|
179
|
-
})
|
|
180
|
-
return expr
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (t.isArrayExpression(expr)) {
|
|
184
|
-
expr.elements = expr.elements.map((el) => {
|
|
185
|
-
if (!el || t.isSpreadElement(el)) return el
|
|
186
|
-
return prefixExpr(el)
|
|
187
|
-
})
|
|
188
|
-
return expr
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (t.isCallExpression(expr)) {
|
|
192
|
-
expr.arguments = expr.arguments.map((arg) => {
|
|
193
|
-
if (t.isSpreadElement(arg)) return arg
|
|
194
|
-
return prefixExpr(arg)
|
|
195
|
-
})
|
|
196
|
-
return expr
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr)) {
|
|
200
|
-
if (!t.isBlockStatement(expr.body)) {
|
|
201
|
-
expr.body = prefixExpr(expr.body)
|
|
202
|
-
}
|
|
203
|
-
return expr
|
|
103
|
+
return t.conditionalExpression(
|
|
104
|
+
expr.test,
|
|
105
|
+
prefixStringExpr(expr.consequent),
|
|
106
|
+
prefixStringExpr(expr.alternate),
|
|
107
|
+
)
|
|
204
108
|
}
|
|
205
|
-
|
|
206
109
|
return expr
|
|
207
110
|
}
|
|
208
111
|
|
|
209
112
|
traverse.default(ast, {
|
|
113
|
+
// ✅ Handle variables whose name includes "DynamicClassName"
|
|
114
|
+
|
|
210
115
|
ReturnStatement(path) {
|
|
211
116
|
const fn = path.getFunctionParent()
|
|
212
117
|
if (!fn) return
|
|
@@ -222,37 +127,186 @@ export default function TailwindPrefixPlugin() {
|
|
|
222
127
|
fnName = parent.id.name
|
|
223
128
|
}
|
|
224
129
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
130
|
+
const isClassHelper =
|
|
131
|
+
fnName.includes('DynamicClassName') ||
|
|
132
|
+
/(?:ClassName|Class)$/.test(fnName)
|
|
133
|
+
if (!isClassHelper) return
|
|
134
|
+
|
|
135
|
+
if (t.isStringLiteral(path.node.argument)) {
|
|
136
|
+
path.node.argument = t.stringLiteral(prefixClasses(path.node.argument.value))
|
|
137
|
+
} else if (t.isConditionalExpression(path.node.argument)) {
|
|
138
|
+
path.node.argument = prefixStringExpr(path.node.argument)
|
|
228
139
|
}
|
|
229
140
|
},
|
|
230
141
|
|
|
231
142
|
JSXAttribute(path) {
|
|
232
|
-
if (
|
|
143
|
+
if (path.node.name.name !== 'className') return
|
|
144
|
+
|
|
233
145
|
const value = path.node.value
|
|
146
|
+
|
|
147
|
+
// String literal: className="..."
|
|
234
148
|
if (t.isStringLiteral(value)) {
|
|
235
149
|
path.node.value = t.stringLiteral(prefixClasses(value.value))
|
|
236
|
-
return
|
|
237
150
|
}
|
|
238
|
-
|
|
239
|
-
|
|
151
|
+
// Template literal: className={`...`}
|
|
152
|
+
else if (
|
|
153
|
+
t.isJSXExpressionContainer(value) &&
|
|
154
|
+
t.isTemplateLiteral(value.expression)
|
|
155
|
+
) {
|
|
156
|
+
const quasis = value.expression.quasis.map((quasi, i) => {
|
|
157
|
+
let classes = prefixClasses(quasi.value.raw)
|
|
158
|
+
|
|
159
|
+
if (i < value.expression.expressions.length) classes += ' '
|
|
160
|
+
if (i > 0 && !classes.startsWith(' ')) classes = ' ' + classes
|
|
161
|
+
|
|
162
|
+
return t.templateElement({ raw: classes, cooked: classes })
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// 🔥 also fix expressions inside ${ ... }
|
|
166
|
+
const expressions = value.expression.expressions.map((expr) => {
|
|
167
|
+
if (t.isStringLiteral(expr)) {
|
|
168
|
+
return t.stringLiteral(prefixClasses(expr.value))
|
|
169
|
+
}
|
|
170
|
+
if (t.isLogicalExpression(expr)) {
|
|
171
|
+
if (t.isStringLiteral(expr.right)) {
|
|
172
|
+
return t.logicalExpression(
|
|
173
|
+
expr.operator,
|
|
174
|
+
expr.left,
|
|
175
|
+
t.stringLiteral(prefixClasses(expr.right.value)),
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (t.isConditionalExpression(expr)) {
|
|
180
|
+
const newConsequent = t.isStringLiteral(expr.consequent)
|
|
181
|
+
? t.stringLiteral(prefixClasses(expr.consequent.value))
|
|
182
|
+
: expr.consequent
|
|
183
|
+
const newAlternate = t.isStringLiteral(expr.alternate)
|
|
184
|
+
? t.stringLiteral(prefixClasses(expr.alternate.value))
|
|
185
|
+
: expr.alternate
|
|
186
|
+
return t.conditionalExpression(
|
|
187
|
+
expr.test,
|
|
188
|
+
newConsequent,
|
|
189
|
+
newAlternate,
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
return expr
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
path.node.value = t.jsxExpressionContainer(
|
|
196
|
+
t.templateLiteral(quasis, expressions),
|
|
197
|
+
)
|
|
240
198
|
}
|
|
241
|
-
},
|
|
242
199
|
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
200
|
+
// className={condition ? "..." : "..."}
|
|
201
|
+
else if (
|
|
202
|
+
t.isJSXExpressionContainer(value) &&
|
|
203
|
+
t.isConditionalExpression(value.expression)
|
|
204
|
+
) {
|
|
205
|
+
const expr = value.expression
|
|
206
|
+
path.node.value = t.jsxExpressionContainer(
|
|
207
|
+
t.conditionalExpression(
|
|
208
|
+
expr.test,
|
|
209
|
+
prefixStringExpr(expr.consequent),
|
|
210
|
+
prefixStringExpr(expr.alternate),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
}
|
|
249
214
|
|
|
215
|
+
// Other expressions (e.g., clsx())
|
|
216
|
+
else if (
|
|
217
|
+
t.isJSXExpressionContainer(value) &&
|
|
218
|
+
t.isCallExpression(value.expression)
|
|
219
|
+
) {
|
|
220
|
+
const args = value.expression.arguments.map((arg) => {
|
|
221
|
+
if (t.isStringLiteral(arg))
|
|
222
|
+
return t.stringLiteral(prefixClasses(arg.value))
|
|
223
|
+
return arg
|
|
224
|
+
})
|
|
225
|
+
path.node.value = t.jsxExpressionContainer(
|
|
226
|
+
t.callExpression(value.expression.callee, args),
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
},
|
|
250
230
|
VariableDeclarator(path) {
|
|
251
231
|
const id = path.node.id
|
|
252
232
|
const init = path.node.init
|
|
253
|
-
if (!t.isIdentifier(id)
|
|
254
|
-
|
|
255
|
-
|
|
233
|
+
if (!t.isIdentifier(id)) return
|
|
234
|
+
|
|
235
|
+
// 🔹 only affect DynamicClassName variables
|
|
236
|
+
if (!id.name.includes('DynamicClassName')) return
|
|
237
|
+
if (!init) return
|
|
238
|
+
|
|
239
|
+
const prefixString = (expr) =>
|
|
240
|
+
t.isStringLiteral(expr)
|
|
241
|
+
? t.stringLiteral(prefixClasses(expr.value))
|
|
242
|
+
: expr
|
|
243
|
+
|
|
244
|
+
const prefixObject = (objExpr) => {
|
|
245
|
+
if (!t.isObjectExpression(objExpr)) return objExpr
|
|
246
|
+
objExpr.properties.forEach((prop) => {
|
|
247
|
+
if (t.isObjectProperty(prop)) {
|
|
248
|
+
if (t.isStringLiteral(prop.value)) {
|
|
249
|
+
prop.value = t.stringLiteral(
|
|
250
|
+
prefixClasses(prop.value.value),
|
|
251
|
+
)
|
|
252
|
+
} else if (t.isTemplateLiteral(prop.value)) {
|
|
253
|
+
const quasis = prop.value.quasis.map((quasi) => {
|
|
254
|
+
const raw = prefixClasses(quasi.value.raw)
|
|
255
|
+
return t.templateElement({ raw, cooked: raw })
|
|
256
|
+
})
|
|
257
|
+
prop.value = t.templateLiteral(
|
|
258
|
+
quasis,
|
|
259
|
+
prop.value.expressions,
|
|
260
|
+
)
|
|
261
|
+
} else if (t.isObjectExpression(prop.value)) {
|
|
262
|
+
prop.value = prefixObject(prop.value) // recurse
|
|
263
|
+
} else if (t.isConditionalExpression(prop.value)) {
|
|
264
|
+
prop.value = t.conditionalExpression(
|
|
265
|
+
prop.value.test,
|
|
266
|
+
prefixString(prop.value.consequent),
|
|
267
|
+
prefixString(prop.value.alternate),
|
|
268
|
+
)
|
|
269
|
+
} else if (t.isLogicalExpression(prop.value)) {
|
|
270
|
+
const right = t.isStringLiteral(prop.value.right)
|
|
271
|
+
? t.stringLiteral(prefixClasses(prop.value.right.value))
|
|
272
|
+
: prop.value.right
|
|
273
|
+
prop.value = t.logicalExpression(
|
|
274
|
+
prop.value.operator,
|
|
275
|
+
prop.value.left,
|
|
276
|
+
right,
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
return objExpr
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (t.isStringLiteral(init)) {
|
|
285
|
+
path.node.init = t.stringLiteral(prefixClasses(init.value))
|
|
286
|
+
} else if (t.isTemplateLiteral(init)) {
|
|
287
|
+
const quasis = init.quasis.map((quasi) => {
|
|
288
|
+
const raw = prefixClasses(quasi.value.raw)
|
|
289
|
+
return t.templateElement({ raw, cooked: raw })
|
|
290
|
+
})
|
|
291
|
+
path.node.init = t.templateLiteral(quasis, init.expressions)
|
|
292
|
+
} else if (t.isObjectExpression(init)) {
|
|
293
|
+
path.node.init = prefixObject(init)
|
|
294
|
+
} else if (t.isConditionalExpression(init)) {
|
|
295
|
+
path.node.init = t.conditionalExpression(
|
|
296
|
+
init.test,
|
|
297
|
+
prefixString(init.consequent),
|
|
298
|
+
prefixString(init.alternate),
|
|
299
|
+
)
|
|
300
|
+
} else if (t.isLogicalExpression(init)) {
|
|
301
|
+
const right = t.isStringLiteral(init.right)
|
|
302
|
+
? t.stringLiteral(prefixClasses(init.right.value))
|
|
303
|
+
: init.right
|
|
304
|
+
path.node.init = t.logicalExpression(
|
|
305
|
+
init.operator,
|
|
306
|
+
init.left,
|
|
307
|
+
right,
|
|
308
|
+
)
|
|
309
|
+
}
|
|
256
310
|
},
|
|
257
311
|
})
|
|
258
312
|
|
|
@@ -268,11 +322,8 @@ export default function TailwindPrefixPlugin() {
|
|
|
268
322
|
|
|
269
323
|
// Handles:
|
|
270
324
|
// ✅ className="..."
|
|
271
|
-
// ✅
|
|
272
|
-
// ✅
|
|
273
|
-
// ✅
|
|
274
|
-
// ✅
|
|
275
|
-
// ✅
|
|
276
|
-
// ✅ const fooDynamicClassName = "..." | {...} | cond ? ... : ...
|
|
277
|
-
// ✅ const BUTTON_CLASS / CARDS_GRID_CLASS = "..."
|
|
278
|
-
// ✅ const paginationItemBaseClass / navBtnClass = `...`
|
|
325
|
+
// ✅ className={\`...\`}with${"foo"}inside
|
|
326
|
+
// ✅className={cond ? "a" : "b"}
|
|
327
|
+
// ✅className={cond && "foo"}
|
|
328
|
+
// ✅className={clsx("foo", cond && "bar")}
|
|
329
|
+
// ✅const fooDynamicClassName = "..." | {...} | cond ? ... : ...`
|
|
@@ -115,9 +115,9 @@ function prefixQuotedStringsInExpression(expr) {
|
|
|
115
115
|
|
|
116
116
|
// Handle className
|
|
117
117
|
function prefixClassesInCode(code) {
|
|
118
|
-
// 1) Plain string: className="..."
|
|
119
|
-
code = code.replace(/
|
|
120
|
-
return
|
|
118
|
+
// 1) Plain string: className="..."
|
|
119
|
+
code = code.replace(/className\s*=\s*["']([^"']*)["']/g, (_, classStr) => {
|
|
120
|
+
return `className="${prefixStaticClasses(classStr)}"`
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
// code = code.replace(/className\s*=\s*["']([^"']*)["']/g, (_, classStr) => {
|
|
@@ -128,8 +128,8 @@ function prefixClassesInCode(code) {
|
|
|
128
128
|
// return `className="${prefixStaticClasses(classStr)}"`
|
|
129
129
|
// })
|
|
130
130
|
|
|
131
|
-
// 2) Template literal: className={`...`}
|
|
132
|
-
code = code.replace(/
|
|
131
|
+
// 2) Template literal: className={`...`}
|
|
132
|
+
code = code.replace(/className\s*=\s*{\s*`([\s\S]*?)`\s*}/g, (_, tplBody) => {
|
|
133
133
|
const parts = tplBody.split(/(\$\{[\s\S]*?\})/g)
|
|
134
134
|
|
|
135
135
|
// const rebuilt = parts
|
|
@@ -161,13 +161,13 @@ function prefixClassesInCode(code) {
|
|
|
161
161
|
.join(' ')
|
|
162
162
|
.replace(/\s+/g, ' ') // collapse multiple spaces
|
|
163
163
|
|
|
164
|
-
return
|
|
164
|
+
return `className={\`${rebuilt.trim()}\`}`
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
-
// 3) Ternary expressions: className={cond ? "..." : "..."}
|
|
167
|
+
// 3) Ternary expressions: className={cond ? "..." : "..."}
|
|
168
168
|
code = code.replace(
|
|
169
|
-
/
|
|
170
|
-
(match,
|
|
169
|
+
/className\s*=\s*{([^{};]+?\?[^:]+:[^}]+)}/g,
|
|
170
|
+
(match, inner) => {
|
|
171
171
|
const ternaryMatch = inner.match(
|
|
172
172
|
/^(.*?\?)\s*(['"][^'"]*['"])\s*:\s*(['"][^'"]*['"])\s*$/,
|
|
173
173
|
)
|
|
@@ -183,43 +183,42 @@ function prefixClassesInCode(code) {
|
|
|
183
183
|
(_, q, cls) => `${q}${prefixStaticClasses(cls)}${q}`,
|
|
184
184
|
)
|
|
185
185
|
|
|
186
|
-
return
|
|
186
|
+
return `className={${condition} ${prefixedTrue} : ${prefixedFalse}}`
|
|
187
187
|
},
|
|
188
188
|
)
|
|
189
189
|
|
|
190
|
-
// 4) Variables with "DynamicClassName"
|
|
190
|
+
// 4) Variables with "DynamicClassName" (supports TypeScript annotations like `: any`)
|
|
191
191
|
code = code.replace(
|
|
192
|
-
/(const|let|var)\s+([A-Za-z0-9_]*
|
|
193
|
-
(match, decl, varName, typeAnnotation, quote, value) => {
|
|
194
|
-
const typeSuffix = typeAnnotation ? `: ${typeAnnotation.trim()}` : ''
|
|
195
|
-
if (quote === '`') {
|
|
196
|
-
return `${decl} ${varName}${typeSuffix} = \`${prefixTemplateLiteralBody(value)}\``
|
|
197
|
-
}
|
|
198
|
-
return `${decl} ${varName}${typeSuffix} = ${quote}${prefixStaticClasses(value)}${quote}`
|
|
199
|
-
},
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
// 4b) DynamicClassName / _CLASS object or ternary values (semicolon-terminated)
|
|
203
|
-
code = code.replace(
|
|
204
|
-
/(const|let|var)\s+([A-Za-z0-9_]*(?:DynamicClassName[A-Za-z0-9_]*|_CLASS))\s*(?::\s*([^=]+))?\s*=\s*(\{[\s\S]*?\}|.+\?.+:.+);/g,
|
|
192
|
+
/(const|let|var)\s+([A-Za-z0-9_]*DynamicClassName[A-Za-z0-9_]*)\s*(?::\s*([^=]+))?\s*=\s*([\s\S]*?);/g,
|
|
205
193
|
(match, decl, varName, typeAnnotation, value) => {
|
|
206
194
|
const typeSuffix = typeAnnotation ? `: ${typeAnnotation.trim()}` : ''
|
|
207
195
|
const trimmed = value.trim()
|
|
208
|
-
if (/^\{[\s\S]*\}$/.test(trimmed)
|
|
196
|
+
if (/^\{[\s\S]*\}$/.test(trimmed)) {
|
|
197
|
+
return `${decl} ${varName}${typeSuffix} = ${prefixValueStrings(value)};`
|
|
198
|
+
}
|
|
199
|
+
if (/\?.*:/.test(trimmed)) {
|
|
209
200
|
return `${decl} ${varName}${typeSuffix} = ${prefixValueStrings(value)};`
|
|
210
201
|
}
|
|
202
|
+
if (/^["'`]/.test(trimmed)) {
|
|
203
|
+
if (trimmed.startsWith('`')) {
|
|
204
|
+
const body = trimmed.slice(1, -1)
|
|
205
|
+
return `${decl} ${varName}${typeSuffix} = \`${prefixTemplateLiteralBody(body)}\`;`
|
|
206
|
+
}
|
|
207
|
+
const cls = trimmed.slice(1, -1)
|
|
208
|
+
return `${decl} ${varName}${typeSuffix} = "${prefixStaticClasses(cls)}";`
|
|
209
|
+
}
|
|
211
210
|
return match
|
|
212
211
|
},
|
|
213
212
|
)
|
|
214
213
|
|
|
215
|
-
// 5) Handle classNames(...) or clsx(...)
|
|
214
|
+
// 5) Handle classNames(...) or clsx(...)
|
|
216
215
|
code = code.replace(
|
|
217
|
-
/
|
|
218
|
-
(match,
|
|
216
|
+
/className\s*=\s*{?\s*(classNames|clsx)\(([\s\S]*?)\)}?/g,
|
|
217
|
+
(match, fnName, args) => {
|
|
219
218
|
const transformedArgs = args.replace(/(['"])(.*?)\1/g, (_, q, cls) => {
|
|
220
219
|
return `${q}${prefixStaticClasses(cls)}${q}`
|
|
221
220
|
})
|
|
222
|
-
return
|
|
221
|
+
return `className={${fnName}(${transformedArgs})}`
|
|
223
222
|
},
|
|
224
223
|
)
|
|
225
224
|
|
|
@@ -48,8 +48,7 @@ export async function createDevServerViteConfig(options = {}) {
|
|
|
48
48
|
])
|
|
49
49
|
|
|
50
50
|
return defineConfig(async ({ mode, command }) => {
|
|
51
|
-
const
|
|
52
|
-
const prefixPlugins = usePrefix
|
|
51
|
+
const prefixPlugins = isPrefix(mode)
|
|
53
52
|
? [
|
|
54
53
|
(await import('../tailwind/prefix-plugin.js')).default(),
|
|
55
54
|
(await import('../tailwind/tailwind-content-plugin.js')).default(),
|
|
@@ -62,20 +61,10 @@ export async function createDevServerViteConfig(options = {}) {
|
|
|
62
61
|
bootstrapShimResolver({ bootstrapShim, bootstrapGreenfieldShim, kitRoot }),
|
|
63
62
|
resolveFromProjectRoot(projectRoot, kitRoot),
|
|
64
63
|
...(command === 'serve' ? [sdkLogBridge(), moduleConfigStore(projectRoot)] : []),
|
|
65
|
-
// Prefix must run before React / compiler so JSX className is still in the source.
|
|
66
|
-
...prefixPlugins,
|
|
67
64
|
...(hasTanStackRoutes ? [TanStackRouterVite({ autoCodeSplitting: false })] : []),
|
|
68
|
-
viteReact(
|
|
69
|
-
usePrefix
|
|
70
|
-
? {
|
|
71
|
-
babel: {
|
|
72
|
-
// Match production: compiler rewrites className after the prefix pass.
|
|
73
|
-
plugins: [],
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
: createDevReactPluginOptions(),
|
|
77
|
-
),
|
|
65
|
+
viteReact(createDevReactPluginOptions()),
|
|
78
66
|
tailwindcss(),
|
|
67
|
+
...prefixPlugins,
|
|
79
68
|
],
|
|
80
69
|
resolve: {
|
|
81
70
|
dedupe: [
|
|
@@ -135,16 +135,11 @@ function prodDefaultDataStub(mode) {
|
|
|
135
135
|
|
|
136
136
|
export function portalPlugins(mode, { codeSplitting = false, routeFileIgnorePattern } = {}) {
|
|
137
137
|
const routeIgnore = routeFileIgnorePattern ?? (isPrefix(mode) ? 'dev\\.' : undefined)
|
|
138
|
-
const prefixPlugins = isPrefix(mode)
|
|
139
|
-
? [tailwindPrefixPlugin(), TailwindContentPlugin()]
|
|
140
|
-
: []
|
|
141
138
|
return [
|
|
142
139
|
prodDevModuleConfigRouteStub(mode),
|
|
143
140
|
prodDevHubSpotLiveStub(mode),
|
|
144
141
|
prodSdkTerminalLogSinksStub(mode),
|
|
145
142
|
prodDefaultDataStub(mode),
|
|
146
|
-
// Prefix must run before React / compiler so JSX className is still in the source.
|
|
147
|
-
...prefixPlugins,
|
|
148
143
|
TanStackRouterVite({
|
|
149
144
|
autoCodeSplitting: codeSplitting,
|
|
150
145
|
...(routeIgnore ? { routeFileIgnorePattern: routeIgnore } : {}),
|
|
@@ -157,6 +152,9 @@ export function portalPlugins(mode, { codeSplitting = false, routeFileIgnorePatt
|
|
|
157
152
|
},
|
|
158
153
|
}),
|
|
159
154
|
tailwindcss(),
|
|
155
|
+
...(isPrefix(mode)
|
|
156
|
+
? [tailwindPrefixPlugin(), TailwindContentPlugin()]
|
|
157
|
+
: []),
|
|
160
158
|
]
|
|
161
159
|
}
|
|
162
160
|
|
|
@@ -16,7 +16,6 @@ export const goldenModuleFixture: ModuleFieldValues = {
|
|
|
16
16
|
menu_options: {
|
|
17
17
|
hubspot_defined_object: true,
|
|
18
18
|
hubspot_custom_object: false,
|
|
19
|
-
custom_menu: true,
|
|
20
19
|
objects: [
|
|
21
20
|
{
|
|
22
21
|
tab_name: 'Contacts',
|
|
@@ -27,7 +26,6 @@ export const goldenModuleFixture: ModuleFieldValues = {
|
|
|
27
26
|
},
|
|
28
27
|
],
|
|
29
28
|
hubspot_custom_objects: [],
|
|
30
|
-
custom_menus: [],
|
|
31
29
|
},
|
|
32
30
|
other_options: {
|
|
33
31
|
show_company_name: true,
|
|
@@ -4,7 +4,7 @@ import fieldsSchema from './fixtures/fields.json'
|
|
|
4
4
|
import { goldenModuleFixture } from './fixtures/golden-module.fixture'
|
|
5
5
|
import type { HubSpotFieldDefinition } from '../schema/types'
|
|
6
6
|
import { HUBSPOT_ONLY_FIELD_NAMES, MODULE_CONFIG_UI_HIDDEN_FIELD_NAMES } from '../schema/types'
|
|
7
|
-
import { flattenVisibleFields
|
|
7
|
+
import { flattenVisibleFields } from '../schema/visibility'
|
|
8
8
|
|
|
9
9
|
const goldenFields = fieldsSchema as Array<HubSpotFieldDefinition>
|
|
10
10
|
|
|
@@ -105,36 +105,3 @@ describe('flattenVisibleFields', () => {
|
|
|
105
105
|
})
|
|
106
106
|
})
|
|
107
107
|
})
|
|
108
|
-
|
|
109
|
-
describe('isFieldVisible nested repeater items', () => {
|
|
110
|
-
const buttonNameField: HubSpotFieldDefinition = {
|
|
111
|
-
name: 'button_name',
|
|
112
|
-
label: 'Button Name',
|
|
113
|
-
type: 'text',
|
|
114
|
-
visibility: {
|
|
115
|
-
controlling_field_path: 'home.dashboard_cards.properties.property_value_show_as',
|
|
116
|
-
controlling_value_regex: 'button',
|
|
117
|
-
operator: 'EQUAL',
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
it('shows fields from nested Display Properties item values', () => {
|
|
122
|
-
expect(
|
|
123
|
-
isFieldVisible(buttonNameField, {
|
|
124
|
-
moduleValues: {},
|
|
125
|
-
repeaterPath: 'home.dashboard_cards.properties',
|
|
126
|
-
itemValues: { property_value_show_as: 'button' },
|
|
127
|
-
}),
|
|
128
|
-
).toBe(true)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('hides nested fields when the controlling item value does not match', () => {
|
|
132
|
-
expect(
|
|
133
|
-
isFieldVisible(buttonNameField, {
|
|
134
|
-
moduleValues: {},
|
|
135
|
-
repeaterPath: 'home.dashboard_cards.properties',
|
|
136
|
-
itemValues: { property_value_show_as: 'simpleText' },
|
|
137
|
-
}),
|
|
138
|
-
).toBe(false)
|
|
139
|
-
})
|
|
140
|
-
})
|
|
@@ -41,33 +41,6 @@ describe('buildHubSpotDataFromFields', () => {
|
|
|
41
41
|
expect(built.enableDashboardCards).toBe(true)
|
|
42
42
|
expect(built.sidebarMenuOptions.length).toBeGreaterThan(0)
|
|
43
43
|
})
|
|
44
|
-
|
|
45
|
-
it('maps enabled custom menu items into sidebarMenuOptions', () => {
|
|
46
|
-
const built = buildHubSpotDataFromFields({
|
|
47
|
-
...goldenModuleFixture,
|
|
48
|
-
menu_options: {
|
|
49
|
-
...(goldenModuleFixture.menu_options as Record<string, unknown>),
|
|
50
|
-
custom_menu: true,
|
|
51
|
-
custom_menus: [
|
|
52
|
-
{
|
|
53
|
-
menu_name: 'Help Center',
|
|
54
|
-
menu_url: 'help.example.com',
|
|
55
|
-
icon_svg_code: '<svg></svg>',
|
|
56
|
-
select_option: 'iframe',
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
expect(built.sidebarMenuOptions).toContainEqual(
|
|
63
|
-
expect.objectContaining({
|
|
64
|
-
label: 'Help Center',
|
|
65
|
-
menuUrl: 'https://help.example.com',
|
|
66
|
-
selectOption: 'iframe',
|
|
67
|
-
objectType: 'CUSTOM_MENU',
|
|
68
|
-
}),
|
|
69
|
-
)
|
|
70
|
-
})
|
|
71
44
|
})
|
|
72
45
|
|
|
73
46
|
describe('buildCssVarsFromFields', () => {
|