@hanzogui/static 7.3.1 → 8.0.0
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/dist/check-dep-versions.cjs +1 -1
- package/dist/checkDeps.cjs +10 -10
- package/dist/constants.cjs +2 -2
- package/dist/exports.cjs +2 -0
- package/dist/extractor/bundle.cjs +14 -4
- package/dist/extractor/bundleConfig.cjs +40 -27
- package/dist/extractor/concatClassName.cjs +40 -11
- package/dist/extractor/createExtractor.cjs +137 -30
- package/dist/extractor/extractHelpers.cjs +1 -1
- package/dist/extractor/extractMediaStyle.cjs +5 -5
- package/dist/extractor/extractToClassNames.cjs +52 -4
- package/dist/extractor/extractToNative.cjs +16 -8
- package/dist/extractor/getPrefixLogs.cjs +1 -1
- package/dist/extractor/getStaticBindingsForScope.cjs +2 -2
- package/dist/extractor/loadGui.cjs +52 -46
- package/dist/extractor/regenerateConfig.cjs +17 -17
- package/dist/extractor/watchGuiConfig.cjs +9 -9
- package/dist/getPragmaOptions.cjs +4 -4
- package/dist/registerRequire.cjs +17 -16
- package/package.json +21 -28
- package/src/check-dep-versions.ts +1 -1
- package/src/checkDeps.ts +25 -25
- package/src/constants.ts +2 -2
- package/src/exports.ts +1 -0
- package/src/extractor/bundle.ts +35 -6
- package/src/extractor/bundleConfig.ts +48 -28
- package/src/extractor/concatClassName.ts +37 -20
- package/src/extractor/createExtractor.ts +256 -36
- package/src/extractor/extractHelpers.ts +2 -2
- package/src/extractor/extractMediaStyle.ts +5 -5
- package/src/extractor/extractToClassNames.ts +109 -7
- package/src/extractor/extractToNative.ts +27 -10
- package/src/extractor/getPrefixLogs.ts +1 -1
- package/src/extractor/getStaticBindingsForScope.ts +2 -2
- package/src/extractor/loadGui.ts +66 -51
- package/src/extractor/regenerateConfig.ts +17 -17
- package/src/extractor/watchGuiConfig.ts +14 -9
- package/src/getPragmaOptions.ts +4 -4
- package/src/registerRequire.ts +32 -19
- package/types/constants.d.ts.map +1 -1
- package/types/exports.d.ts +1 -0
- package/types/exports.d.ts.map +1 -1
- package/types/extractor/bundle.d.ts.map +1 -1
- package/types/extractor/bundleConfig.d.ts +1 -1
- package/types/extractor/bundleConfig.d.ts.map +1 -1
- package/types/extractor/createExtractor.d.ts.map +1 -1
- package/types/extractor/extractMediaStyle.d.ts +1 -1
- package/types/extractor/extractMediaStyle.d.ts.map +1 -1
- package/types/extractor/extractToClassNames.d.ts.map +1 -1
- package/types/extractor/extractToNative.d.ts.map +1 -1
- package/types/extractor/loadGui.d.ts +5 -5
- package/types/extractor/loadGui.d.ts.map +1 -1
- package/types/extractor/regenerateConfig.d.ts +3 -3
- package/types/extractor/regenerateConfig.d.ts.map +1 -1
- package/types/extractor/watchGuiConfig.d.ts +1 -1
- package/types/extractor/watchGuiConfig.d.ts.map +1 -1
- package/types/registerRequire.d.ts +1 -1
- package/types/registerRequire.d.ts.map +1 -1
- package/LICENSE +0 -42
|
@@ -98,7 +98,7 @@ export async function extractToClassNames({
|
|
|
98
98
|
tm.mark(`babel-parse`, shouldPrintDebug === 'verbose')
|
|
99
99
|
|
|
100
100
|
const cssMap = new Map<string, { css: string; commentTexts: string[] }>()
|
|
101
|
-
const
|
|
101
|
+
const hanzoguiConfig = extractor.getGui()!
|
|
102
102
|
|
|
103
103
|
const res = await extractor.parse(ast, {
|
|
104
104
|
shouldPrintDebug,
|
|
@@ -170,6 +170,75 @@ export async function extractToClassNames({
|
|
|
170
170
|
originalNodeName
|
|
171
171
|
)
|
|
172
172
|
|
|
173
|
+
// detect static `group="<name>"` literal — runtime side normally emits the
|
|
174
|
+
// container-name/container-type CSS lazily via getSplitStyles (createComponent
|
|
175
|
+
// path), but extracted elements never go through that code, so we emit the
|
|
176
|
+
// equivalent CSS at compile time and add `t_group_<name>` to the className.
|
|
177
|
+
let staticGroupName: string | null = null
|
|
178
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
179
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue
|
|
180
|
+
if (attr.name.name !== 'group') continue
|
|
181
|
+
if (t.isStringLiteral(attr.value)) {
|
|
182
|
+
staticGroupName = attr.value.value
|
|
183
|
+
} else if (
|
|
184
|
+
t.isJSXExpressionContainer(attr.value) &&
|
|
185
|
+
t.isStringLiteral(attr.value.expression)
|
|
186
|
+
) {
|
|
187
|
+
staticGroupName = attr.value.expression.value
|
|
188
|
+
}
|
|
189
|
+
// dynamic group={someProp} falls back to the runtime path.
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
if (staticGroupName) {
|
|
193
|
+
const containerIdentifier = `t_group_${staticGroupName}`
|
|
194
|
+
const containerType =
|
|
195
|
+
(hanzoguiConfig.settings?.webContainerType as string | undefined) ||
|
|
196
|
+
'inline-size'
|
|
197
|
+
const containerSelector = `.${containerIdentifier}`
|
|
198
|
+
if (!cssMap.has(containerSelector)) {
|
|
199
|
+
cssMap.set(containerSelector, {
|
|
200
|
+
css: `${containerSelector} { container-name: ${staticGroupName}; container-type: ${containerType}; }`,
|
|
201
|
+
commentTexts: [comment],
|
|
202
|
+
})
|
|
203
|
+
} else {
|
|
204
|
+
cssMap.get(containerSelector)!.commentTexts.push(comment)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// invariant guard for $group-/$theme- CSS extraction (below): those styles
|
|
209
|
+
// can only be extracted to static @container / theme CSS when a class toggle
|
|
210
|
+
// alone expresses the change. JS animation drivers (reanimated, motion, moti,
|
|
211
|
+
// react-native) interpolate in JS and need the runtime path to read hard
|
|
212
|
+
// values when the element animates — a static class can't drive them. gated
|
|
213
|
+
// on whether THIS element animates rather than on the driver, because motion
|
|
214
|
+
// is isReactNative:false yet still needs JS.
|
|
215
|
+
//
|
|
216
|
+
// in practice createExtractor's deoptProps already de-opts every animated
|
|
217
|
+
// element upstream except CSS-only transitions, which lower to ordinary
|
|
218
|
+
// CSS. it keeps the invariant local and correct regardless of future
|
|
219
|
+
// deoptProps changes — it only ever bails the animated case to runtime,
|
|
220
|
+
// never widens what extracts.
|
|
221
|
+
const hasCSSOnlyAnimationDriver =
|
|
222
|
+
hanzoguiConfig.animations.outputStyle === 'css' && !hanzoguiConfig.animationDrivers
|
|
223
|
+
let elementIsAnimated = false
|
|
224
|
+
for (const attr of jsxPath.node.openingElement.attributes) {
|
|
225
|
+
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue
|
|
226
|
+
const n = attr.name.name
|
|
227
|
+
if (n === 'transition' && hasCSSOnlyAnimationDriver) continue
|
|
228
|
+
if (
|
|
229
|
+
n === 'animation' ||
|
|
230
|
+
n === 'transition' ||
|
|
231
|
+
n === 'animateOnly' ||
|
|
232
|
+
n === 'animatePresence' ||
|
|
233
|
+
n === 'animatedBy' ||
|
|
234
|
+
n === 'enterStyle' ||
|
|
235
|
+
n === 'exitStyle'
|
|
236
|
+
) {
|
|
237
|
+
elementIsAnimated = true
|
|
238
|
+
break
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
173
242
|
function addStyle(style: StyleObject) {
|
|
174
243
|
const identifier = style[StyleObjectIdentifier]
|
|
175
244
|
const rules = style[StyleObjectRules]
|
|
@@ -194,20 +263,46 @@ export async function extractToClassNames({
|
|
|
194
263
|
const property = style[0]
|
|
195
264
|
const mediaName = property.slice(1)
|
|
196
265
|
|
|
197
|
-
// $group- styles
|
|
198
|
-
//
|
|
199
|
-
// In the future, CSS animation drivers could potentially optimize this.
|
|
266
|
+
// $group- styles extract through createMediaStyle which emits @container
|
|
267
|
+
// rules keyed on the parent's `t_group_<name>` className.
|
|
200
268
|
if (mediaName.startsWith('group-')) {
|
|
201
|
-
|
|
269
|
+
// a class toggle can't drive a JS animation driver's interpolation;
|
|
270
|
+
// keep animated elements on the runtime path (matches pre-extraction
|
|
271
|
+
// behavior for the group case).
|
|
272
|
+
if (elementIsAnimated) {
|
|
273
|
+
throw new BailOptimizationError()
|
|
274
|
+
}
|
|
275
|
+
const mediaStyle = createMediaStyle(
|
|
276
|
+
style,
|
|
277
|
+
mediaName,
|
|
278
|
+
extractor.getGui()!.media,
|
|
279
|
+
'group',
|
|
280
|
+
false,
|
|
281
|
+
mediaStylesSeen
|
|
282
|
+
)
|
|
283
|
+
const identifier = addStyle(mediaStyle)
|
|
284
|
+
classNames.push(identifier)
|
|
285
|
+
continue
|
|
202
286
|
}
|
|
203
287
|
|
|
204
288
|
// Check for theme/platform media queries (e.g., $theme-dark, $platform-web)
|
|
205
289
|
const mediaTypeMatch = mediaName.match(/^(theme|platform)-/)
|
|
206
290
|
if (mediaTypeMatch) {
|
|
207
291
|
const mediaType = mediaTypeMatch[1] as 'theme' | 'platform'
|
|
292
|
+
// $theme- values can change at runtime (theme switch); if this
|
|
293
|
+
// element animates, a JS driver must interpolate the change, so keep
|
|
294
|
+
// it on the runtime path. $platform- is build-time static — never gated.
|
|
295
|
+
if (mediaType === 'theme' && elementIsAnimated) {
|
|
296
|
+
throw new BailOptimizationError()
|
|
297
|
+
}
|
|
298
|
+
// createMediaStyle internally calls getGroupPropParts(`theme-` + key)
|
|
299
|
+
// for theme, so we must pass the short key ("dark", not "theme-dark")
|
|
300
|
+
// to avoid double-prefixing. Platform stays as-is (runtime parity).
|
|
301
|
+
const innerKey =
|
|
302
|
+
mediaType === 'theme' ? mediaName.slice('theme-'.length) : mediaName
|
|
208
303
|
const mediaStyle = createMediaStyle(
|
|
209
304
|
style,
|
|
210
|
-
|
|
305
|
+
innerKey,
|
|
211
306
|
extractor.getGui()!.media,
|
|
212
307
|
mediaType,
|
|
213
308
|
false,
|
|
@@ -218,7 +313,7 @@ export async function extractToClassNames({
|
|
|
218
313
|
continue
|
|
219
314
|
}
|
|
220
315
|
|
|
221
|
-
if (mediaName in
|
|
316
|
+
if (mediaName in hanzoguiConfig.media) {
|
|
222
317
|
const mediaStyle = createMediaStyle(
|
|
223
318
|
style,
|
|
224
319
|
mediaName,
|
|
@@ -343,6 +438,13 @@ export async function extractToClassNames({
|
|
|
343
438
|
baseClassNameStr = `font_${baseFontFamily}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
344
439
|
}
|
|
345
440
|
|
|
441
|
+
// add t_group_<name> for elements with a static group="<name>" literal so
|
|
442
|
+
// descendants' @container <name> rules can match (runtime path normally
|
|
443
|
+
// appends this class; extracted elements bypass it).
|
|
444
|
+
if (staticGroupName) {
|
|
445
|
+
baseClassNameStr = `t_group_${staticGroupName}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
446
|
+
}
|
|
447
|
+
|
|
346
448
|
// add is_View or is_Text base class matching runtime behavior
|
|
347
449
|
const baseTypeClass = staticConfig.isText ? 'is_Text' : 'is_View'
|
|
348
450
|
baseClassNameStr = `${baseTypeClass}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
@@ -26,7 +26,7 @@ const importWithStyle = template.ast(`import { _withStableStyle } from '@hanzogu
|
|
|
26
26
|
|
|
27
27
|
const extractor = createExtractor({ platform: 'native' })
|
|
28
28
|
|
|
29
|
-
let
|
|
29
|
+
let hanzoguiBuildOptionsLoaded: GuiOptions | null
|
|
30
30
|
|
|
31
31
|
export function extractToNative(
|
|
32
32
|
sourceFileName: string,
|
|
@@ -63,7 +63,7 @@ export function getBabelPlugin() {
|
|
|
63
63
|
|
|
64
64
|
export function getBabelParseDefinition(options: GuiOptions) {
|
|
65
65
|
return {
|
|
66
|
-
name: '
|
|
66
|
+
name: 'hanzogui',
|
|
67
67
|
|
|
68
68
|
visitor: {
|
|
69
69
|
Program: {
|
|
@@ -106,14 +106,14 @@ export function getBabelParseDefinition(options: GuiOptions) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (!options.config && !options.components) {
|
|
109
|
-
// if no config/components given try and load from the
|
|
110
|
-
|
|
109
|
+
// if no config/components given try and load from the hanzogui.build.ts file
|
|
110
|
+
hanzoguiBuildOptionsLoaded ||= loadGuiBuildConfigSync({})
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const finalOptions = {
|
|
114
114
|
// @ts-ignore just in case they leave it out
|
|
115
115
|
platform: 'native',
|
|
116
|
-
...
|
|
116
|
+
...hanzoguiBuildOptionsLoaded,
|
|
117
117
|
...options,
|
|
118
118
|
} satisfies GuiOptions
|
|
119
119
|
|
|
@@ -207,6 +207,7 @@ export function getBabelParseDefinition(options: GuiOptions) {
|
|
|
207
207
|
const stylesExpr = t.arrayExpression([])
|
|
208
208
|
const hocStylesExpr = t.arrayExpression([])
|
|
209
209
|
const expressions: t.Expression[] = []
|
|
210
|
+
const mediaExpressionIndexes = new Set<number>()
|
|
210
211
|
const finalAttrs: (t.JSXAttribute | t.JSXSpreadAttribute)[] = []
|
|
211
212
|
const themeKeysUsed = new Set<string>()
|
|
212
213
|
|
|
@@ -299,7 +300,22 @@ export function getBabelParseDefinition(options: GuiOptions) {
|
|
|
299
300
|
hasMediaKeys = true
|
|
300
301
|
}
|
|
301
302
|
|
|
302
|
-
|
|
303
|
+
let expression = attr.value.test
|
|
304
|
+
if (
|
|
305
|
+
attr.value.inlineMediaQuery &&
|
|
306
|
+
t.isLogicalExpression(attr.value.test, { operator: '&&' }) &&
|
|
307
|
+
t.isStringLiteral(attr.value.test.left)
|
|
308
|
+
) {
|
|
309
|
+
expression = t.arrayExpression([
|
|
310
|
+
t.stringLiteral(attr.value.inlineMediaQuery),
|
|
311
|
+
t.unaryExpression(
|
|
312
|
+
'!',
|
|
313
|
+
t.unaryExpression('!', attr.value.test.right)
|
|
314
|
+
),
|
|
315
|
+
])
|
|
316
|
+
mediaExpressionIndexes.add(expressions.length)
|
|
317
|
+
}
|
|
318
|
+
expressions.push(expression)
|
|
303
319
|
addStyleExpression(
|
|
304
320
|
t.conditionalExpression(
|
|
305
321
|
t.identifier(`_expressions[${expressions.length - 1}]`),
|
|
@@ -340,7 +356,8 @@ export function getBabelParseDefinition(options: GuiOptions) {
|
|
|
340
356
|
if (
|
|
341
357
|
themeKeysUsed.size ||
|
|
342
358
|
hocStylesExpr.elements.length > 1 ||
|
|
343
|
-
hasDynamicStyle
|
|
359
|
+
hasDynamicStyle ||
|
|
360
|
+
hasMediaKeys
|
|
344
361
|
) {
|
|
345
362
|
if (!hasImportedViewWrapper) {
|
|
346
363
|
root.unshiftContainer('body', importWithStyle)
|
|
@@ -386,8 +403,8 @@ export function getBabelParseDefinition(options: GuiOptions) {
|
|
|
386
403
|
if (expressions.length) {
|
|
387
404
|
// coerce runtime expressions to boolean so they can't be
|
|
388
405
|
// confused with string media keys at runtime
|
|
389
|
-
const safeExpressions = expressions.map((expr) =>
|
|
390
|
-
t.isStringLiteral(expr)
|
|
406
|
+
const safeExpressions = expressions.map((expr, index) =>
|
|
407
|
+
t.isStringLiteral(expr) || mediaExpressionIndexes.has(index)
|
|
391
408
|
? expr
|
|
392
409
|
: t.unaryExpression('!', t.unaryExpression('!', expr))
|
|
393
410
|
)
|
|
@@ -466,7 +483,7 @@ function assertValidTag(node: t.JSXOpeningElement) {
|
|
|
466
483
|
if (node.attributes.find((x) => x.type === 'JSXAttribute' && x.name.name === 'style')) {
|
|
467
484
|
// we can just deopt here instead and log warning
|
|
468
485
|
// need to make onExtractTag have a special catch error or similar
|
|
469
|
-
if (process.env.DEBUG?.startsWith('
|
|
486
|
+
if (process.env.DEBUG?.startsWith('hanzogui')) {
|
|
470
487
|
console.warn('⚠️ Cannot pass style attribute to extracted style')
|
|
471
488
|
}
|
|
472
489
|
}
|
|
@@ -4,6 +4,6 @@ import type { GuiOptions } from '../types'
|
|
|
4
4
|
export function getPrefixLogs(options?: GuiOptions) {
|
|
5
5
|
return (
|
|
6
6
|
options?.prefixLogs ??
|
|
7
|
-
` 🐥 [
|
|
7
|
+
` 🐥 [hanzogui] ${colorString(Color.FgYellow, options?.platform || 'web')}`
|
|
8
8
|
)
|
|
9
9
|
}
|
|
@@ -137,9 +137,9 @@ export async function getStaticBindingsForScope(
|
|
|
137
137
|
} catch (err: any) {
|
|
138
138
|
if (shouldPrintDebug) {
|
|
139
139
|
console.warn(
|
|
140
|
-
` | Skipping partial evaluation of constant file: ${moduleName} (DEBUG=
|
|
140
|
+
` | Skipping partial evaluation of constant file: ${moduleName} (DEBUG=hanzogui for more)`
|
|
141
141
|
)
|
|
142
|
-
} else if (process.env.DEBUG?.startsWith('
|
|
142
|
+
} else if (process.env.DEBUG?.startsWith('hanzogui')) {
|
|
143
143
|
console.info(`Error in partial evaluation`, err.message, err.stack)
|
|
144
144
|
}
|
|
145
145
|
}
|
package/src/extractor/loadGui.ts
CHANGED
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
const getFilledOptions = (propsIn: Partial<GuiOptions>): GuiOptions => ({
|
|
29
29
|
// defaults
|
|
30
30
|
platform: (process.env.GUI_TARGET as any) || 'web',
|
|
31
|
-
config: '
|
|
32
|
-
components: ['
|
|
31
|
+
config: 'hanzogui.config.ts',
|
|
32
|
+
components: ['hanzogui'],
|
|
33
33
|
...(propsIn as Partial<GuiOptions>),
|
|
34
34
|
})
|
|
35
35
|
|
|
@@ -53,7 +53,7 @@ export async function loadGui(
|
|
|
53
53
|
const bundleInfo = await getBundledConfig(props)
|
|
54
54
|
if (!bundleInfo) {
|
|
55
55
|
console.warn(
|
|
56
|
-
`No bundled config generated, maybe an error in bundling. Set DEBUG=
|
|
56
|
+
`No bundled config generated, maybe an error in bundling. Set DEBUG=hanzogui and re-run to get logs.`
|
|
57
57
|
)
|
|
58
58
|
resolvePromise(null)
|
|
59
59
|
return null
|
|
@@ -63,10 +63,10 @@ export async function loadGui(
|
|
|
63
63
|
await generateThemesAndLog(props)
|
|
64
64
|
|
|
65
65
|
// if they accidently pass in a config without createGui called,call it
|
|
66
|
-
const maybeGuiConfig = bundleInfo.
|
|
66
|
+
const maybeGuiConfig = bundleInfo.hanzoguiConfig as GuiInternalConfig
|
|
67
67
|
if (maybeGuiConfig && !maybeGuiConfig.parsed) {
|
|
68
68
|
const { createGui } = requireGuiCore(props.platform || 'web')
|
|
69
|
-
bundleInfo.
|
|
69
|
+
bundleInfo.hanzoguiConfig = createGui(bundleInfo.hanzoguiConfig as any)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (!hasBundledConfigChanged()) {
|
|
@@ -102,7 +102,7 @@ export const generateThemesAndLog = async (options: GuiOptions, force = false) =
|
|
|
102
102
|
const whitespaceBefore = ` `
|
|
103
103
|
colorLog(
|
|
104
104
|
Color.FgYellow,
|
|
105
|
-
`${whitespaceBefore}➡ [
|
|
105
|
+
`${whitespaceBefore}➡ [hanzogui] generated themes: ${relative(
|
|
106
106
|
process.cwd(),
|
|
107
107
|
options.themeBuilder.output
|
|
108
108
|
)}`
|
|
@@ -127,13 +127,13 @@ const lastVersion: Record<string, string> = {}
|
|
|
127
127
|
let esbuildWasmInitialized = false
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* Load
|
|
130
|
+
* Load hanzogui.build.ts config using esbuild-wasm transform
|
|
131
131
|
* Uses WASM to avoid native esbuild service lifecycle issues (EPIPE errors)
|
|
132
132
|
*/
|
|
133
133
|
export async function loadGuiBuildConfigAsync(
|
|
134
|
-
|
|
134
|
+
hanzoguiOptions: Partial<GuiOptions> | undefined
|
|
135
135
|
): Promise<GuiOptions> {
|
|
136
|
-
const buildFilePath =
|
|
136
|
+
const buildFilePath = hanzoguiOptions?.buildFile ?? './hanzogui.build.ts'
|
|
137
137
|
const absolutePath =
|
|
138
138
|
buildFilePath[0] === '.' ? join(process.cwd(), buildFilePath) : buildFilePath
|
|
139
139
|
|
|
@@ -147,7 +147,11 @@ export async function loadGuiBuildConfigAsync(
|
|
|
147
147
|
esbuildWasmInitialized = true
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// use esbuild-wasm.transform to compile
|
|
150
|
+
// use esbuild-wasm.transform to compile this one small config file. note
|
|
151
|
+
// that in node, even esbuild-wasm's transform spawns a long-lived
|
|
152
|
+
// `--service --ping` child via ensureServiceIsRunning, so we tear it down
|
|
153
|
+
// below - otherwise every plugin instance leaks a persistent esbuild
|
|
154
|
+
// process for the lifetime of the dev server.
|
|
151
155
|
const result = await esbuildWasm.transform(source, {
|
|
152
156
|
loader: 'ts',
|
|
153
157
|
format: 'cjs',
|
|
@@ -166,26 +170,36 @@ export async function loadGuiBuildConfigAsync(
|
|
|
166
170
|
throw new Error(`No default export found in ${buildFilePath}: ${out}`)
|
|
167
171
|
}
|
|
168
172
|
|
|
169
|
-
|
|
170
|
-
...
|
|
173
|
+
hanzoguiOptions = {
|
|
174
|
+
...hanzoguiOptions,
|
|
171
175
|
...out,
|
|
172
176
|
}
|
|
173
177
|
} catch (err) {
|
|
174
|
-
console.error(`[
|
|
178
|
+
console.error(`[hanzogui] Error loading ${buildFilePath}:`, err)
|
|
175
179
|
throw err
|
|
180
|
+
} finally {
|
|
181
|
+
// this is a one-shot transform - don't keep the esbuild-wasm service
|
|
182
|
+
// running for the rest of the process. it gets lazily re-spawned if
|
|
183
|
+
// transform is ever called again.
|
|
184
|
+
try {
|
|
185
|
+
esbuildWasm.stop()
|
|
186
|
+
esbuildWasmInitialized = false
|
|
187
|
+
} catch {
|
|
188
|
+
// ok - service may already be gone
|
|
189
|
+
}
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
192
|
|
|
179
|
-
if (!
|
|
193
|
+
if (!hanzoguiOptions) {
|
|
180
194
|
throw new Error(
|
|
181
|
-
`No
|
|
195
|
+
`No hanzogui build options found either via input props or at hanzogui.build.ts`
|
|
182
196
|
)
|
|
183
197
|
}
|
|
184
198
|
|
|
185
199
|
return {
|
|
186
|
-
config: '
|
|
187
|
-
components: ['
|
|
188
|
-
...
|
|
200
|
+
config: 'hanzogui.config.ts',
|
|
201
|
+
components: ['hanzogui', '@hanzogui/core'],
|
|
202
|
+
...hanzoguiOptions,
|
|
189
203
|
} as GuiOptions
|
|
190
204
|
}
|
|
191
205
|
|
|
@@ -193,9 +207,9 @@ export async function loadGuiBuildConfigAsync(
|
|
|
193
207
|
* @deprecated Use loadGuiBuildConfigAsync instead to avoid EPIPE errors
|
|
194
208
|
*/
|
|
195
209
|
export function loadGuiBuildConfigSync(
|
|
196
|
-
|
|
210
|
+
hanzoguiOptions: Partial<GuiOptions> | undefined
|
|
197
211
|
) {
|
|
198
|
-
const buildFilePath =
|
|
212
|
+
const buildFilePath = hanzoguiOptions?.buildFile ?? './hanzogui.build.ts'
|
|
199
213
|
if (fsExtra.existsSync(buildFilePath)) {
|
|
200
214
|
const registered = registerRequire('web')
|
|
201
215
|
try {
|
|
@@ -206,23 +220,23 @@ export function loadGuiBuildConfigSync(
|
|
|
206
220
|
throw new Error(`No default export found in ${buildFilePath}: ${out}`)
|
|
207
221
|
}
|
|
208
222
|
|
|
209
|
-
|
|
210
|
-
...
|
|
223
|
+
hanzoguiOptions = {
|
|
224
|
+
...hanzoguiOptions,
|
|
211
225
|
...out,
|
|
212
226
|
}
|
|
213
227
|
} finally {
|
|
214
228
|
registered.unregister()
|
|
215
229
|
}
|
|
216
230
|
}
|
|
217
|
-
if (!
|
|
231
|
+
if (!hanzoguiOptions) {
|
|
218
232
|
throw new Error(
|
|
219
|
-
`No
|
|
233
|
+
`No hanzogui build options found either via input props or at hanzogui.build.ts`
|
|
220
234
|
)
|
|
221
235
|
}
|
|
222
236
|
return {
|
|
223
|
-
config: '
|
|
224
|
-
components: ['
|
|
225
|
-
...
|
|
237
|
+
config: 'hanzogui.config.ts',
|
|
238
|
+
components: ['hanzogui', '@hanzogui/core'],
|
|
239
|
+
...hanzoguiOptions,
|
|
226
240
|
} as GuiOptions
|
|
227
241
|
}
|
|
228
242
|
|
|
@@ -262,7 +276,7 @@ export function loadGuiSync({
|
|
|
262
276
|
|
|
263
277
|
try {
|
|
264
278
|
// config
|
|
265
|
-
let
|
|
279
|
+
let hanzoguiConfig: GuiInternalConfig | null = null
|
|
266
280
|
if (propsIn.config) {
|
|
267
281
|
const configPath = getGuiConfigPathFromOptionsConfig(propsIn.config)
|
|
268
282
|
const exp = require(configPath)
|
|
@@ -271,9 +285,9 @@ export function loadGuiSync({
|
|
|
271
285
|
throw new Error(`Got a empty / proxied config!`)
|
|
272
286
|
}
|
|
273
287
|
|
|
274
|
-
|
|
288
|
+
hanzoguiConfig = (exp['default'] || exp['config'] || exp) as GuiInternalConfig
|
|
275
289
|
|
|
276
|
-
if (!
|
|
290
|
+
if (!hanzoguiConfig || !hanzoguiConfig.parsed) {
|
|
277
291
|
const confPath = require.resolve(configPath)
|
|
278
292
|
throw new Error(`Can't find valid config in ${confPath}:
|
|
279
293
|
|
|
@@ -281,9 +295,9 @@ export function loadGuiSync({
|
|
|
281
295
|
}
|
|
282
296
|
|
|
283
297
|
// set up core
|
|
284
|
-
if (
|
|
298
|
+
if (hanzoguiConfig) {
|
|
285
299
|
const { createGui } = requireGuiCore(props.platform || 'web')
|
|
286
|
-
createGui(
|
|
300
|
+
createGui(hanzoguiConfig as any)
|
|
287
301
|
}
|
|
288
302
|
}
|
|
289
303
|
|
|
@@ -292,7 +306,7 @@ export function loadGuiSync({
|
|
|
292
306
|
if (!components) {
|
|
293
307
|
throw new Error(`No components loaded`)
|
|
294
308
|
}
|
|
295
|
-
if (process.env.DEBUG === '
|
|
309
|
+
if (process.env.DEBUG === 'hanzogui') {
|
|
296
310
|
console.info(`components`, components)
|
|
297
311
|
}
|
|
298
312
|
|
|
@@ -302,14 +316,14 @@ export function loadGuiSync({
|
|
|
302
316
|
|
|
303
317
|
const info = {
|
|
304
318
|
components,
|
|
305
|
-
|
|
319
|
+
hanzoguiConfig,
|
|
306
320
|
nameToPaths: getNameToPaths(),
|
|
307
321
|
} satisfies GuiProjectInfo
|
|
308
322
|
|
|
309
|
-
if (
|
|
323
|
+
if (hanzoguiConfig) {
|
|
310
324
|
const { outputCSS } = props
|
|
311
325
|
if (outputCSS) {
|
|
312
|
-
writeGuiCSS(outputCSS,
|
|
326
|
+
writeGuiCSS(outputCSS, hanzoguiConfig)
|
|
313
327
|
}
|
|
314
328
|
|
|
315
329
|
regenerateConfigSync(props, info)
|
|
@@ -325,7 +339,7 @@ export function loadGuiSync({
|
|
|
325
339
|
if (err instanceof Error) {
|
|
326
340
|
if (!SHOULD_DEBUG && !forceExports) {
|
|
327
341
|
console.warn(
|
|
328
|
-
`Error loading
|
|
342
|
+
`Error loading hanzogui.config.ts (set DEBUG=hanzogui to see full stack), running hanzogui without custom config`
|
|
329
343
|
)
|
|
330
344
|
console.info(`\n\n ${err.message}\n\n`)
|
|
331
345
|
} else {
|
|
@@ -334,12 +348,12 @@ export function loadGuiSync({
|
|
|
334
348
|
}
|
|
335
349
|
}
|
|
336
350
|
} else {
|
|
337
|
-
console.error(`Error loading
|
|
351
|
+
console.error(`Error loading hanzogui.config.ts`, err)
|
|
338
352
|
}
|
|
339
353
|
|
|
340
354
|
return {
|
|
341
355
|
components: [],
|
|
342
|
-
|
|
356
|
+
hanzoguiConfig: null,
|
|
343
357
|
nameToPaths: {},
|
|
344
358
|
}
|
|
345
359
|
}
|
|
@@ -351,11 +365,11 @@ export function loadGuiSync({
|
|
|
351
365
|
export async function getOptions({
|
|
352
366
|
root = process.cwd(),
|
|
353
367
|
tsconfigPath = 'tsconfig.json',
|
|
354
|
-
|
|
368
|
+
hanzoguiOptions,
|
|
355
369
|
host,
|
|
356
370
|
debug,
|
|
357
371
|
}: Partial<CLIUserOptions> = {}): Promise<CLIResolvedOptions> {
|
|
358
|
-
const dotDir = join(root, '.
|
|
372
|
+
const dotDir = join(root, '.hanzogui')
|
|
359
373
|
let pkgJson = {}
|
|
360
374
|
|
|
361
375
|
try {
|
|
@@ -371,29 +385,30 @@ export async function getOptions({
|
|
|
371
385
|
pkgJson,
|
|
372
386
|
debug,
|
|
373
387
|
tsconfigPath,
|
|
374
|
-
|
|
388
|
+
hanzoguiOptions: {
|
|
375
389
|
platform: (process.env.GUI_TARGET as any) || 'web',
|
|
376
|
-
components: ['
|
|
377
|
-
...
|
|
390
|
+
components: ['hanzogui'],
|
|
391
|
+
...hanzoguiOptions,
|
|
378
392
|
config:
|
|
379
|
-
|
|
380
|
-
(await getDefaultGuiConfigPath(root,
|
|
393
|
+
hanzoguiOptions?.config ??
|
|
394
|
+
(await getDefaultGuiConfigPath(root, hanzoguiOptions?.config)),
|
|
381
395
|
},
|
|
382
396
|
paths: {
|
|
383
397
|
root,
|
|
384
398
|
dotDir,
|
|
385
|
-
conf: join(dotDir, '
|
|
399
|
+
conf: join(dotDir, 'hanzogui.config.json'),
|
|
386
400
|
types: join(dotDir, 'types.json'),
|
|
387
401
|
},
|
|
388
402
|
}
|
|
389
403
|
}
|
|
390
404
|
|
|
391
|
-
export function resolveWebOrNativeSpecificEntry(entry: string) {
|
|
405
|
+
export function resolveWebOrNativeSpecificEntry(entry: string, platform?: string) {
|
|
392
406
|
const workspaceRoot = resolve()
|
|
393
407
|
const resolved = require.resolve(entry, { paths: [workspaceRoot] })
|
|
394
408
|
const ext = extname(resolved)
|
|
395
409
|
const fileName = basename(resolved).replace(ext, '')
|
|
396
|
-
const
|
|
410
|
+
const target = platform || process.env.GUI_TARGET || 'web'
|
|
411
|
+
const specificExt = target === 'web' ? 'web' : 'native'
|
|
397
412
|
const specificFile = join(dirname(resolved), fileName + '.' + specificExt + ext)
|
|
398
413
|
if (fsExtra.existsSync(specificFile)) {
|
|
399
414
|
return specificFile
|
|
@@ -401,7 +416,7 @@ export function resolveWebOrNativeSpecificEntry(entry: string) {
|
|
|
401
416
|
return entry
|
|
402
417
|
}
|
|
403
418
|
|
|
404
|
-
const defaultPaths = ['
|
|
419
|
+
const defaultPaths = ['hanzogui.config.ts', join('src', 'hanzogui.config.ts')]
|
|
405
420
|
let hasWarnedOnce = false
|
|
406
421
|
|
|
407
422
|
async function getDefaultGuiConfigPath(root: string, configPath?: string) {
|
|
@@ -419,7 +434,7 @@ async function getDefaultGuiConfigPath(root: string, configPath?: string) {
|
|
|
419
434
|
|
|
420
435
|
if (!hasWarnedOnce) {
|
|
421
436
|
hasWarnedOnce = true
|
|
422
|
-
console.warn(`Warning: couldn't find
|
|
437
|
+
console.warn(`Warning: couldn't find hanzogui.config.ts in the following paths given configuration "${configPath}":
|
|
423
438
|
${searchPaths.join(`\n `)}
|
|
424
439
|
`)
|
|
425
440
|
}
|