@hanzogui/static 2.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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/check-dep-versions.cjs +267 -0
- package/dist/checkDeps.cjs +200 -0
- package/dist/constants.cjs +49 -0
- package/dist/exports.cjs +44 -0
- package/dist/extractor/accessSafe.cjs +46 -0
- package/dist/extractor/babelParse.cjs +54 -0
- package/dist/extractor/buildClassName.cjs +62 -0
- package/dist/extractor/bundle.cjs +185 -0
- package/dist/extractor/bundleConfig.cjs +513 -0
- package/dist/extractor/concatClassName.cjs +80 -0
- package/dist/extractor/createEvaluator.cjs +75 -0
- package/dist/extractor/createExtractor.cjs +1150 -0
- package/dist/extractor/createLogger.cjs +45 -0
- package/dist/extractor/detectModuleFormat.cjs +49 -0
- package/dist/extractor/ensureImportingConcat.cjs +48 -0
- package/dist/extractor/errors.cjs +26 -0
- package/dist/extractor/esbuildAliasPlugin.cjs +44 -0
- package/dist/extractor/esbuildTsconfigPaths.cjs +86 -0
- package/dist/extractor/evaluateAstNode.cjs +83 -0
- package/dist/extractor/extractHelpers.cjs +119 -0
- package/dist/extractor/extractMediaStyle.cjs +128 -0
- package/dist/extractor/extractToClassNames.cjs +321 -0
- package/dist/extractor/extractToNative.cjs +266 -0
- package/dist/extractor/findTopmostFunction.cjs +34 -0
- package/dist/extractor/generatedUid.cjs +47 -0
- package/dist/extractor/getGuiConfigPathFromOptionsConfig.cjs +35 -0
- package/dist/extractor/getPrefixLogs.cjs +29 -0
- package/dist/extractor/getPropValueFromAttributes.cjs +67 -0
- package/dist/extractor/getSourceModule.cjs +64 -0
- package/dist/extractor/getStaticBindingsForScope.cjs +150 -0
- package/dist/extractor/hoistClassNames.cjs +62 -0
- package/dist/extractor/literalToAst.cjs +84 -0
- package/dist/extractor/loadFile.cjs +11 -0
- package/dist/extractor/loadGui.cjs +322 -0
- package/dist/extractor/logLines.cjs +35 -0
- package/dist/extractor/normalizeTernaries.cjs +73 -0
- package/dist/extractor/propsToFontFamilyCache.cjs +38 -0
- package/dist/extractor/regenerateConfig.cjs +150 -0
- package/dist/extractor/removeUnusedHooks.cjs +76 -0
- package/dist/extractor/timer.cjs +43 -0
- package/dist/extractor/validHTMLAttributes.cjs +77 -0
- package/dist/extractor/watchGuiConfig.cjs +58 -0
- package/dist/getPragmaOptions.cjs +58 -0
- package/dist/helpers/memoize.cjs +37 -0
- package/dist/helpers/requireGuiCore.cjs +33 -0
- package/dist/index.cjs +40 -0
- package/dist/registerRequire.cjs +115 -0
- package/dist/server.cjs +75 -0
- package/dist/setup.cjs +0 -0
- package/dist/types.cjs +16 -0
- package/dist/worker.cjs +89 -0
- package/package.json +101 -0
- package/src/check-dep-versions.ts +738 -0
- package/src/checkDeps.ts +371 -0
- package/src/constants.ts +14 -0
- package/src/exports.ts +15 -0
- package/src/extractor/accessSafe.ts +22 -0
- package/src/extractor/babelParse.ts +37 -0
- package/src/extractor/buildClassName.ts +76 -0
- package/src/extractor/bundle.ts +277 -0
- package/src/extractor/bundleConfig.ts +958 -0
- package/src/extractor/concatClassName.ts +105 -0
- package/src/extractor/createEvaluator.ts +82 -0
- package/src/extractor/createExtractor.ts +2665 -0
- package/src/extractor/createLogger.ts +41 -0
- package/src/extractor/detectModuleFormat.ts +42 -0
- package/src/extractor/ensureImportingConcat.ts +36 -0
- package/src/extractor/errors.ts +1 -0
- package/src/extractor/esbuildAliasPlugin.ts +40 -0
- package/src/extractor/esbuildTsconfigPaths.ts +103 -0
- package/src/extractor/evaluateAstNode.ts +136 -0
- package/src/extractor/extractHelpers.ts +211 -0
- package/src/extractor/extractMediaStyle.ts +194 -0
- package/src/extractor/extractToClassNames.ts +631 -0
- package/src/extractor/extractToNative.ts +463 -0
- package/src/extractor/findTopmostFunction.ts +24 -0
- package/src/extractor/generatedUid.ts +41 -0
- package/src/extractor/getGuiConfigPathFromOptionsConfig.ts +24 -0
- package/src/extractor/getPrefixLogs.ts +9 -0
- package/src/extractor/getPropValueFromAttributes.ts +95 -0
- package/src/extractor/getSourceModule.ts +96 -0
- package/src/extractor/getStaticBindingsForScope.ts +237 -0
- package/src/extractor/hoistClassNames.ts +52 -0
- package/src/extractor/literalToAst.ts +85 -0
- package/src/extractor/loadFile.ts +17 -0
- package/src/extractor/loadGui.ts +487 -0
- package/src/extractor/logLines.ts +16 -0
- package/src/extractor/normalizeTernaries.ts +75 -0
- package/src/extractor/propsToFontFamilyCache.ts +18 -0
- package/src/extractor/regenerateConfig.ts +186 -0
- package/src/extractor/removeUnusedHooks.ts +83 -0
- package/src/extractor/timer.ts +25 -0
- package/src/extractor/validHTMLAttributes.ts +52 -0
- package/src/extractor/watchGuiConfig.ts +57 -0
- package/src/getPragmaOptions.ts +57 -0
- package/src/helpers/memoize.ts +24 -0
- package/src/helpers/requireGuiCore.ts +28 -0
- package/src/index.ts +4 -0
- package/src/registerRequire.ts +236 -0
- package/src/server.ts +43 -0
- package/src/setup.ts +0 -0
- package/src/types.ts +109 -0
- package/src/worker.ts +142 -0
- package/types/check-dep-versions.d.ts +37 -0
- package/types/check-dep-versions.d.ts.map +1 -0
- package/types/checkDeps.d.ts +19 -0
- package/types/checkDeps.d.ts.map +1 -0
- package/types/constants.d.ts +6 -0
- package/types/constants.d.ts.map +1 -0
- package/types/exports.d.ts +16 -0
- package/types/exports.d.ts.map +1 -0
- package/types/extractor/accessSafe.d.ts +3 -0
- package/types/extractor/accessSafe.d.ts.map +1 -0
- package/types/extractor/babelParse.d.ts +5 -0
- package/types/extractor/babelParse.d.ts.map +1 -0
- package/types/extractor/buildClassName.d.ts +7 -0
- package/types/extractor/buildClassName.d.ts.map +1 -0
- package/types/extractor/bundle.d.ts +121 -0
- package/types/extractor/bundle.d.ts.map +1 -0
- package/types/extractor/bundleConfig.d.ts +49 -0
- package/types/extractor/bundleConfig.d.ts.map +1 -0
- package/types/extractor/concatClassName.d.ts +8 -0
- package/types/extractor/concatClassName.d.ts.map +1 -0
- package/types/extractor/createEvaluator.d.ts +12 -0
- package/types/extractor/createEvaluator.d.ts.map +1 -0
- package/types/extractor/createExtractor.d.ts +32 -0
- package/types/extractor/createExtractor.d.ts.map +1 -0
- package/types/extractor/createLogger.d.ts +3 -0
- package/types/extractor/createLogger.d.ts.map +1 -0
- package/types/extractor/detectModuleFormat.d.ts +5 -0
- package/types/extractor/detectModuleFormat.d.ts.map +1 -0
- package/types/extractor/ensureImportingConcat.d.ts +4 -0
- package/types/extractor/ensureImportingConcat.d.ts.map +1 -0
- package/types/extractor/errors.d.ts +3 -0
- package/types/extractor/errors.d.ts.map +1 -0
- package/types/extractor/esbuildAliasPlugin.d.ts +18 -0
- package/types/extractor/esbuildAliasPlugin.d.ts.map +1 -0
- package/types/extractor/esbuildTsconfigPaths.d.ts +11 -0
- package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -0
- package/types/extractor/evaluateAstNode.d.ts +3 -0
- package/types/extractor/evaluateAstNode.d.ts.map +1 -0
- package/types/extractor/extractHelpers.d.ts +28 -0
- package/types/extractor/extractHelpers.d.ts.map +1 -0
- package/types/extractor/extractMediaStyle.d.ts +11 -0
- package/types/extractor/extractMediaStyle.d.ts.map +1 -0
- package/types/extractor/extractToClassNames.d.ts +25 -0
- package/types/extractor/extractToClassNames.d.ts.map +1 -0
- package/types/extractor/extractToNative.d.ts +13 -0
- package/types/extractor/extractToNative.d.ts.map +1 -0
- package/types/extractor/findTopmostFunction.d.ts +4 -0
- package/types/extractor/findTopmostFunction.d.ts.map +1 -0
- package/types/extractor/generatedUid.d.ts +5 -0
- package/types/extractor/generatedUid.d.ts.map +1 -0
- package/types/extractor/getGuiConfigPathFromOptionsConfig.d.ts +3 -0
- package/types/extractor/getGuiConfigPathFromOptionsConfig.d.ts.map +1 -0
- package/types/extractor/getPrefixLogs.d.ts +3 -0
- package/types/extractor/getPrefixLogs.d.ts.map +1 -0
- package/types/extractor/getPropValueFromAttributes.d.ts +19 -0
- package/types/extractor/getPropValueFromAttributes.d.ts.map +1 -0
- package/types/extractor/getSourceModule.d.ts +16 -0
- package/types/extractor/getSourceModule.d.ts.map +1 -0
- package/types/extractor/getStaticBindingsForScope.d.ts +5 -0
- package/types/extractor/getStaticBindingsForScope.d.ts.map +1 -0
- package/types/extractor/hoistClassNames.d.ts +6 -0
- package/types/extractor/hoistClassNames.d.ts.map +1 -0
- package/types/extractor/literalToAst.d.ts +4 -0
- package/types/extractor/literalToAst.d.ts.map +1 -0
- package/types/extractor/loadFile.d.ts +1 -0
- package/types/extractor/loadFile.d.ts.map +1 -0
- package/types/extractor/loadGui.d.ts +22 -0
- package/types/extractor/loadGui.d.ts.map +1 -0
- package/types/extractor/logLines.d.ts +2 -0
- package/types/extractor/logLines.d.ts.map +1 -0
- package/types/extractor/normalizeTernaries.d.ts +3 -0
- package/types/extractor/normalizeTernaries.d.ts.map +1 -0
- package/types/extractor/propsToFontFamilyCache.d.ts +4 -0
- package/types/extractor/propsToFontFamilyCache.d.ts.map +1 -0
- package/types/extractor/regenerateConfig.d.ts +9 -0
- package/types/extractor/regenerateConfig.d.ts.map +1 -0
- package/types/extractor/removeUnusedHooks.d.ts +3 -0
- package/types/extractor/removeUnusedHooks.d.ts.map +1 -0
- package/types/extractor/timer.d.ts +5 -0
- package/types/extractor/timer.d.ts.map +1 -0
- package/types/extractor/validHTMLAttributes.d.ts +52 -0
- package/types/extractor/validHTMLAttributes.d.ts.map +1 -0
- package/types/extractor/watchGuiConfig.d.ts +5 -0
- package/types/extractor/watchGuiConfig.d.ts.map +1 -0
- package/types/getPragmaOptions.d.ts +8 -0
- package/types/getPragmaOptions.d.ts.map +1 -0
- package/types/helpers/memoize.d.ts +8 -0
- package/types/helpers/memoize.d.ts.map +1 -0
- package/types/helpers/requireGuiCore.d.ts +3 -0
- package/types/helpers/requireGuiCore.d.ts.map +1 -0
- package/types/index.d.ts +4 -0
- package/types/index.d.ts.map +1 -0
- package/types/registerRequire.d.ts +10 -0
- package/types/registerRequire.d.ts.map +1 -0
- package/types/server.d.ts +3 -0
- package/types/server.d.ts.map +1 -0
- package/types/setup.d.ts +1 -0
- package/types/setup.d.ts.map +1 -0
- package/types/types.d.ts +92 -0
- package/types/types.d.ts.map +1 -0
- package/types/worker.d.ts +46 -0
- package/types/worker.d.ts.map +1 -0
package/src/checkDeps.ts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'
|
|
2
|
+
import { join, relative } from 'node:path'
|
|
3
|
+
import { CDVC } from './check-dep-versions'
|
|
4
|
+
|
|
5
|
+
export enum DEPENDENCY_TYPE {
|
|
6
|
+
dependencies = 'dependencies',
|
|
7
|
+
devDependencies = 'devDependencies',
|
|
8
|
+
optionalDependencies = 'optionalDependencies',
|
|
9
|
+
peerDependencies = 'peerDependencies',
|
|
10
|
+
resolutions = 'resolutions',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Options = {
|
|
14
|
+
depType?: readonly `${DEPENDENCY_TYPE}`[] // Allow strings so the enum type doesn't always have to be used.
|
|
15
|
+
fix?: boolean
|
|
16
|
+
ignoreDep?: readonly string[]
|
|
17
|
+
ignoreDepPattern?: readonly string[]
|
|
18
|
+
ignorePackage?: readonly string[]
|
|
19
|
+
ignorePackagePattern?: readonly string[]
|
|
20
|
+
ignorePath?: readonly string[]
|
|
21
|
+
ignorePathPattern?: readonly string[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// critical packages that must not be duplicated at runtime
|
|
25
|
+
const CRITICAL_PACKAGES = ['@hanzogui/web', '@hanzogui/core', '@hanzo/gui']
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Walks node_modules to find duplicate physical copies of critical hanzo-gui packages.
|
|
29
|
+
* Detects nested node_modules that would cause multiple runtime instances.
|
|
30
|
+
*/
|
|
31
|
+
function checkDuplicateInstalls(root: string): string {
|
|
32
|
+
const nodeModules = join(root, 'node_modules')
|
|
33
|
+
if (!existsSync(nodeModules)) return ''
|
|
34
|
+
|
|
35
|
+
const duplicates = new Map<string, string[]>()
|
|
36
|
+
|
|
37
|
+
for (const pkg of CRITICAL_PACKAGES) {
|
|
38
|
+
const locations = findAllInstances(nodeModules, pkg)
|
|
39
|
+
if (locations.length > 1) {
|
|
40
|
+
// resolve symlinks to find truly distinct copies
|
|
41
|
+
const realPaths = new Set<string>()
|
|
42
|
+
const distinctLocations: string[] = []
|
|
43
|
+
for (const loc of locations) {
|
|
44
|
+
try {
|
|
45
|
+
const real = realpathSync(loc)
|
|
46
|
+
if (!realPaths.has(real)) {
|
|
47
|
+
realPaths.add(real)
|
|
48
|
+
distinctLocations.push(relative(root, loc))
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
distinctLocations.push(relative(root, loc))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (distinctLocations.length > 1) {
|
|
55
|
+
duplicates.set(pkg, distinctLocations)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (duplicates.size === 0) return ''
|
|
61
|
+
|
|
62
|
+
const lines: string[] = [
|
|
63
|
+
'Found duplicate gui installations in node_modules:',
|
|
64
|
+
'',
|
|
65
|
+
'This causes multiple runtime instances, which breaks theme/config detection.',
|
|
66
|
+
'',
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
for (const [pkg, locations] of duplicates) {
|
|
70
|
+
// read versions from each location
|
|
71
|
+
lines.push(` ${pkg}:`)
|
|
72
|
+
for (const loc of locations) {
|
|
73
|
+
const pkgJsonPath = join(root, loc, 'package.json')
|
|
74
|
+
let version = '?'
|
|
75
|
+
try {
|
|
76
|
+
version = JSON.parse(readFileSync(pkgJsonPath, 'utf8')).version
|
|
77
|
+
} catch {}
|
|
78
|
+
lines.push(` ${version} at ${loc}`)
|
|
79
|
+
}
|
|
80
|
+
lines.push('')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lines.push("Fix: run your package manager's dedupe command:")
|
|
84
|
+
lines.push(' bun install (bun auto-dedupes)')
|
|
85
|
+
lines.push(' npx yarn-deduplicate && yarn install')
|
|
86
|
+
lines.push(' npm dedupe')
|
|
87
|
+
lines.push('')
|
|
88
|
+
lines.push("If that doesn't help, delete node_modules and lockfile, then reinstall.")
|
|
89
|
+
|
|
90
|
+
return lines.join('\n')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Recursively find all instances of a package in node_modules.
|
|
95
|
+
* Handles both scoped (@hanzogui/web) and unscoped (gui) packages.
|
|
96
|
+
*/
|
|
97
|
+
function findAllInstances(
|
|
98
|
+
nodeModulesDir: string,
|
|
99
|
+
packageName: string,
|
|
100
|
+
found: string[] = [],
|
|
101
|
+
depth = 0
|
|
102
|
+
): string[] {
|
|
103
|
+
// don't go too deep, typical hoisting issues show up within a few levels
|
|
104
|
+
if (depth > 4 || !existsSync(nodeModulesDir)) return found
|
|
105
|
+
|
|
106
|
+
const pkgDir = join(nodeModulesDir, ...packageName.split('/'))
|
|
107
|
+
if (existsSync(join(pkgDir, 'package.json'))) {
|
|
108
|
+
found.push(pkgDir)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// scan nested node_modules inside direct children
|
|
112
|
+
try {
|
|
113
|
+
const entries = readdirSync(nodeModulesDir)
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
if (entry.startsWith('.')) continue
|
|
116
|
+
|
|
117
|
+
if (entry.startsWith('@')) {
|
|
118
|
+
// scoped packages have another level
|
|
119
|
+
const scopeDir = join(nodeModulesDir, entry)
|
|
120
|
+
try {
|
|
121
|
+
const scopeEntries = readdirSync(scopeDir)
|
|
122
|
+
for (const scopeEntry of scopeEntries) {
|
|
123
|
+
const nested = join(scopeDir, scopeEntry, 'node_modules')
|
|
124
|
+
if (existsSync(nested)) {
|
|
125
|
+
findAllInstances(nested, packageName, found, depth + 1)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {}
|
|
129
|
+
} else {
|
|
130
|
+
const nested = join(nodeModulesDir, entry, 'node_modules')
|
|
131
|
+
if (existsSync(nested)) {
|
|
132
|
+
findAllInstances(nested, packageName, found, depth + 1)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch {}
|
|
137
|
+
|
|
138
|
+
return found
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Checks lockfile for multiple resolved versions of hanzo-gui packages.
|
|
143
|
+
* Supports bun.lock, yarn.lock, and package-lock.json.
|
|
144
|
+
*/
|
|
145
|
+
function checkLockfileDuplicates(root: string): string {
|
|
146
|
+
const bunLock = join(root, 'bun.lock')
|
|
147
|
+
const yarnLock = join(root, 'yarn.lock')
|
|
148
|
+
const npmLock = join(root, 'package-lock.json')
|
|
149
|
+
|
|
150
|
+
if (existsSync(bunLock)) return checkBunLockDuplicates(bunLock)
|
|
151
|
+
if (existsSync(yarnLock)) return checkYarnLockDuplicates(yarnLock)
|
|
152
|
+
if (existsSync(npmLock)) return checkNpmLockDuplicates(npmLock)
|
|
153
|
+
|
|
154
|
+
return ''
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function checkBunLockDuplicates(lockPath: string): string {
|
|
158
|
+
try {
|
|
159
|
+
const content = readFileSync(lockPath, 'utf8')
|
|
160
|
+
const duplicates = new Map<string, Set<string>>()
|
|
161
|
+
const criticalSet = new Set(CRITICAL_PACKAGES)
|
|
162
|
+
|
|
163
|
+
// match patterns like "@hanzogui/web@version" or "gui@version" in resolved entries
|
|
164
|
+
// bun.lock format: "package@version": ["resolved-url", ...]
|
|
165
|
+
const packagePattern = /["'](@gui\/[\w-]+|gui)@([^"'\s,]+)["']/g
|
|
166
|
+
let match: RegExpExecArray | null
|
|
167
|
+
while ((match = packagePattern.exec(content)) !== null) {
|
|
168
|
+
const name = match[1]
|
|
169
|
+
const version = match[2]
|
|
170
|
+
if (version.startsWith('workspace:')) continue
|
|
171
|
+
// only flag critical packages — leaf packages can safely differ
|
|
172
|
+
if (!criticalSet.has(name)) continue
|
|
173
|
+
if (!duplicates.has(name)) duplicates.set(name, new Set())
|
|
174
|
+
duplicates.get(name)!.add(version)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return formatLockfileDuplicates(duplicates, 'bun.lock')
|
|
178
|
+
} catch {
|
|
179
|
+
return ''
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function checkYarnLockDuplicates(lockPath: string): string {
|
|
184
|
+
try {
|
|
185
|
+
const content = readFileSync(lockPath, 'utf8')
|
|
186
|
+
const duplicates = new Map<string, Set<string>>()
|
|
187
|
+
const criticalSet = new Set(CRITICAL_PACKAGES)
|
|
188
|
+
|
|
189
|
+
// yarn.lock format:
|
|
190
|
+
// "@hanzogui/web@^1.0.0":
|
|
191
|
+
// version "1.0.1"
|
|
192
|
+
const entryPattern = /^"?(@gui\/[\w-]+|gui)@[^":\n]+[":]?\s*$/gm
|
|
193
|
+
const versionPattern = /^\s+version\s+"([^"]+)"/gm
|
|
194
|
+
|
|
195
|
+
let entryMatch: RegExpExecArray | null
|
|
196
|
+
while ((entryMatch = entryPattern.exec(content)) !== null) {
|
|
197
|
+
const name = entryMatch[1]
|
|
198
|
+
if (!criticalSet.has(name)) continue
|
|
199
|
+
versionPattern.lastIndex = entryMatch.index
|
|
200
|
+
const verMatch = versionPattern.exec(content)
|
|
201
|
+
if (verMatch) {
|
|
202
|
+
if (!duplicates.has(name)) duplicates.set(name, new Set())
|
|
203
|
+
duplicates.get(name)!.add(verMatch[1])
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return formatLockfileDuplicates(duplicates, 'yarn.lock')
|
|
208
|
+
} catch {
|
|
209
|
+
return ''
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function checkNpmLockDuplicates(lockPath: string): string {
|
|
214
|
+
try {
|
|
215
|
+
const lock = JSON.parse(readFileSync(lockPath, 'utf8'))
|
|
216
|
+
const duplicates = new Map<string, Set<string>>()
|
|
217
|
+
const criticalSet = new Set(CRITICAL_PACKAGES)
|
|
218
|
+
|
|
219
|
+
// package-lock.json v2/v3 uses "packages" map with path keys
|
|
220
|
+
const packages = lock.packages || {}
|
|
221
|
+
for (const [path, info] of Object.entries(packages) as [string, any][]) {
|
|
222
|
+
if (!path) continue // skip root
|
|
223
|
+
const name = info.name || path.split('node_modules/').pop()
|
|
224
|
+
if (!name) continue
|
|
225
|
+
if (!criticalSet.has(name)) continue
|
|
226
|
+
const version = info.version
|
|
227
|
+
if (version) {
|
|
228
|
+
if (!duplicates.has(name)) duplicates.set(name, new Set())
|
|
229
|
+
duplicates.get(name)!.add(version)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return formatLockfileDuplicates(duplicates, 'package-lock.json')
|
|
234
|
+
} catch {
|
|
235
|
+
return ''
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function formatLockfileDuplicates(
|
|
240
|
+
duplicates: Map<string, Set<string>>,
|
|
241
|
+
lockfileName: string
|
|
242
|
+
): string {
|
|
243
|
+
// filter to only packages with multiple versions
|
|
244
|
+
const multiVersion = new Map<string, string[]>()
|
|
245
|
+
for (const [name, versions] of duplicates) {
|
|
246
|
+
if (versions.size > 1) {
|
|
247
|
+
multiVersion.set(name, [...versions].sort())
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (multiVersion.size === 0) return ''
|
|
252
|
+
|
|
253
|
+
const lines: string[] = [`Found multiple resolved versions in ${lockfileName}:`, '']
|
|
254
|
+
|
|
255
|
+
for (const [name, versions] of multiVersion) {
|
|
256
|
+
lines.push(` ${name}: ${versions.join(', ')}`)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
lines.push('')
|
|
260
|
+
lines.push(
|
|
261
|
+
'Multiple versions cause duplicate runtime instances, breaking config/theme detection.'
|
|
262
|
+
)
|
|
263
|
+
lines.push('Fix: ensure all hanzo-gui packages use the same version range, then dedupe.')
|
|
264
|
+
|
|
265
|
+
return lines.join('\n')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Checks that a gui config file exists in common locations.
|
|
270
|
+
*/
|
|
271
|
+
function checkConfigExists(root: string): string {
|
|
272
|
+
const configNames = [
|
|
273
|
+
'gui.config.ts',
|
|
274
|
+
'gui.config.tsx',
|
|
275
|
+
'gui.config.js',
|
|
276
|
+
'gui.config.mjs',
|
|
277
|
+
'gui.config.cjs',
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
const searchDirs = [root, join(root, 'src'), join(root, 'app'), join(root, 'config')]
|
|
281
|
+
|
|
282
|
+
for (const dir of searchDirs) {
|
|
283
|
+
for (const name of configNames) {
|
|
284
|
+
if (existsSync(join(dir, name))) {
|
|
285
|
+
return ''
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// check if gui.build.ts references a config path
|
|
291
|
+
const buildConfigNames = [
|
|
292
|
+
'gui.build.ts',
|
|
293
|
+
'gui.build.js',
|
|
294
|
+
'gui.build.mjs',
|
|
295
|
+
'gui.build.cjs',
|
|
296
|
+
]
|
|
297
|
+
for (const name of buildConfigNames) {
|
|
298
|
+
const buildPath = join(root, name)
|
|
299
|
+
if (existsSync(buildPath)) {
|
|
300
|
+
try {
|
|
301
|
+
const content = readFileSync(buildPath, 'utf8')
|
|
302
|
+
const match = content.match(/config\s*:\s*['"`]([^'"`]+)['"`]/)
|
|
303
|
+
if (match) {
|
|
304
|
+
const configPath = join(root, match[1])
|
|
305
|
+
if (existsSync(configPath)) return ''
|
|
306
|
+
}
|
|
307
|
+
} catch {}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// also check if there's a gui config referenced in package.json
|
|
312
|
+
const pkgJsonPath = join(root, 'package.json')
|
|
313
|
+
if (existsSync(pkgJsonPath)) {
|
|
314
|
+
try {
|
|
315
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
|
|
316
|
+
if (pkg.gui?.config) {
|
|
317
|
+
const configPath = join(root, pkg.gui.config)
|
|
318
|
+
if (existsSync(configPath)) return ''
|
|
319
|
+
}
|
|
320
|
+
} catch {}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// check if this is a monorepo root (has workspaces) - skip config check for root
|
|
324
|
+
if (existsSync(pkgJsonPath)) {
|
|
325
|
+
try {
|
|
326
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
|
|
327
|
+
if (pkg.workspaces) return ''
|
|
328
|
+
} catch {}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return [
|
|
332
|
+
'No gui.config file found.',
|
|
333
|
+
'',
|
|
334
|
+
'Gui requires a config file (e.g. gui.config.ts) that calls createGui().',
|
|
335
|
+
'Without it, components will throw "Can\'t find Hanzo GUI configuration" at runtime.',
|
|
336
|
+
'',
|
|
337
|
+
'See: https://gui.hanzo.ai/docs/core/configuration',
|
|
338
|
+
].join('\n')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export async function checkDeps(root: string) {
|
|
342
|
+
const issues: string[] = []
|
|
343
|
+
|
|
344
|
+
// 1. check for dependency version mismatches across workspace packages
|
|
345
|
+
const workspaceMismatchSummary = new CDVC(root).toMismatchSummary()
|
|
346
|
+
if (workspaceMismatchSummary) issues.push(workspaceMismatchSummary)
|
|
347
|
+
|
|
348
|
+
// 2. check lockfile for duplicate resolved versions of critical packages
|
|
349
|
+
const lockfileSummary = checkLockfileDuplicates(root)
|
|
350
|
+
if (lockfileSummary) issues.push(lockfileSummary)
|
|
351
|
+
|
|
352
|
+
// 3. check for duplicate physical installations in node_modules
|
|
353
|
+
const duplicatesSummary = checkDuplicateInstalls(root)
|
|
354
|
+
if (duplicatesSummary) issues.push(duplicatesSummary)
|
|
355
|
+
|
|
356
|
+
// 4. check that a config file exists
|
|
357
|
+
const configSummary = checkConfigExists(root)
|
|
358
|
+
if (configSummary) issues.push(configSummary)
|
|
359
|
+
|
|
360
|
+
if (issues.length === 0) {
|
|
361
|
+
console.info(`Gui dependencies look good ✅`)
|
|
362
|
+
process.exit(0)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
for (let i = 0; i < issues.length; i++) {
|
|
366
|
+
if (i > 0) console.error('')
|
|
367
|
+
console.error(issues[i])
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
process.exit(1)
|
|
371
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import findCacheDir from 'find-cache-dir'
|
|
2
|
+
|
|
3
|
+
export const CSS_FILE_NAME = '__snack.css'
|
|
4
|
+
|
|
5
|
+
// ENSURE THIS ISNT THE SAME AS THE SEPARATOR USED FOR STYLE KEYS
|
|
6
|
+
export const MEDIA_SEP = '_'
|
|
7
|
+
|
|
8
|
+
// ensure cache dir
|
|
9
|
+
export const cacheDir = findCacheDir({ name: 'hanzo-gui', create: true })
|
|
10
|
+
|
|
11
|
+
export const FAILED_EVAL = Symbol('failed_style_eval')
|
|
12
|
+
|
|
13
|
+
export const SHOULD_DEBUG =
|
|
14
|
+
process.env.DEBUG === '*' || process.env.DEBUG?.startsWith('@hanzo/gui')
|
package/src/exports.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './checkDeps'
|
|
2
|
+
export * from './types'
|
|
3
|
+
export { createExtractor } from './extractor/createExtractor'
|
|
4
|
+
export { literalToAst } from './extractor/literalToAst'
|
|
5
|
+
export * from './constants'
|
|
6
|
+
export * from './extractor/extractToClassNames'
|
|
7
|
+
export * from './extractor/concatClassName'
|
|
8
|
+
export * from './extractor/extractToNative'
|
|
9
|
+
export * from './extractor/extractHelpers'
|
|
10
|
+
export * from './extractor/loadGui'
|
|
11
|
+
export * from './extractor/watchGuiConfig'
|
|
12
|
+
export * from './extractor/createLogger'
|
|
13
|
+
export * from './registerRequire'
|
|
14
|
+
export { detectModuleFormat, clearFormatCache } from './extractor/detectModuleFormat'
|
|
15
|
+
export * from './getPragmaOptions'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as t from '@babel/types'
|
|
2
|
+
|
|
3
|
+
// accessSafe wraps memberExpressions in object/null checks
|
|
4
|
+
// TODO: inject this as a function? this gets pretty repetitive
|
|
5
|
+
export function accessSafe(obj: t.Expression, member: string): t.LogicalExpression {
|
|
6
|
+
return t.logicalExpression(
|
|
7
|
+
'&&',
|
|
8
|
+
t.logicalExpression(
|
|
9
|
+
'&&',
|
|
10
|
+
// typeof obj === 'object
|
|
11
|
+
t.binaryExpression(
|
|
12
|
+
'===',
|
|
13
|
+
t.unaryExpression('typeof', obj),
|
|
14
|
+
t.stringLiteral('object')
|
|
15
|
+
),
|
|
16
|
+
// obj !== null
|
|
17
|
+
t.binaryExpression('!==', obj, t.nullLiteral())
|
|
18
|
+
),
|
|
19
|
+
// obj.member
|
|
20
|
+
t.memberExpression(obj, t.identifier(member), false)
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as babelParser from '@babel/parser'
|
|
2
|
+
import type * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
const plugins: babelParser.ParserPlugin[] = [
|
|
5
|
+
'asyncGenerators',
|
|
6
|
+
'classProperties',
|
|
7
|
+
'dynamicImport',
|
|
8
|
+
'functionBind',
|
|
9
|
+
'jsx',
|
|
10
|
+
'numericSeparator',
|
|
11
|
+
'objectRestSpread',
|
|
12
|
+
'optionalCatchBinding',
|
|
13
|
+
'decorators-legacy',
|
|
14
|
+
'typescript',
|
|
15
|
+
'optionalChaining',
|
|
16
|
+
'nullishCoalescingOperator',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
export const parserOptions: babelParser.ParserOptions = Object.freeze({
|
|
20
|
+
plugins,
|
|
21
|
+
sourceType: 'module',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const parser = babelParser.parse.bind(babelParser)
|
|
25
|
+
|
|
26
|
+
export function babelParse(code: string | Buffer, fileName?: string): t.File {
|
|
27
|
+
const codeString = code.toString()
|
|
28
|
+
try {
|
|
29
|
+
return parser(codeString, parserOptions) as any
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Error parsing babel: ${err} in ${fileName}, code:\n${codeString}\n ${
|
|
33
|
+
(err as any).stack
|
|
34
|
+
}`
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as t from '@babel/types'
|
|
2
|
+
|
|
3
|
+
import type { ClassNameObject } from '../types'
|
|
4
|
+
|
|
5
|
+
type Builder = (
|
|
6
|
+
objects: ClassNameObject[],
|
|
7
|
+
extras?: string
|
|
8
|
+
) => t.Expression | t.StringLiteral | null
|
|
9
|
+
|
|
10
|
+
export const buildClassName: Builder = (objectsIn, extras = '') => {
|
|
11
|
+
let objects = buildClassNameLogic(objectsIn)
|
|
12
|
+
if (!objects) return null
|
|
13
|
+
if (t.isStringLiteral(objects)) {
|
|
14
|
+
// objects.value = objects.value
|
|
15
|
+
objects.value = `${extras} ${objects.value}`
|
|
16
|
+
} else {
|
|
17
|
+
objects = t.binaryExpression('+', t.stringLiteral(extras), objects)
|
|
18
|
+
}
|
|
19
|
+
return objects
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const buildClassNameLogic: Builder = (objects) => {
|
|
23
|
+
return objects.reduce<t.Expression | null>((acc, val) => {
|
|
24
|
+
if (acc == null) {
|
|
25
|
+
if (
|
|
26
|
+
// pass conditional expressions through
|
|
27
|
+
t.isConditionalExpression(val) ||
|
|
28
|
+
// pass non-null literals through
|
|
29
|
+
t.isStringLiteral(val) ||
|
|
30
|
+
t.isNumericLiteral(val)
|
|
31
|
+
) {
|
|
32
|
+
return val
|
|
33
|
+
}
|
|
34
|
+
return t.logicalExpression('||', val, t.stringLiteral(''))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let inner: t.Expression
|
|
38
|
+
if (t.isStringLiteral(val)) {
|
|
39
|
+
if (t.isStringLiteral(acc)) {
|
|
40
|
+
// join adjacent string literals
|
|
41
|
+
return t.stringLiteral(`${acc.value} ${val.value}`)
|
|
42
|
+
}
|
|
43
|
+
inner = t.stringLiteral(` ${val.value}`)
|
|
44
|
+
} else if (t.isLiteral(val)) {
|
|
45
|
+
inner = t.binaryExpression('+', t.stringLiteral(' '), val)
|
|
46
|
+
} else if (t.isConditionalExpression(val) || t.isBinaryExpression(val)) {
|
|
47
|
+
if (t.isStringLiteral(acc)) {
|
|
48
|
+
return t.binaryExpression('+', t.stringLiteral(`${acc.value} `), val)
|
|
49
|
+
}
|
|
50
|
+
inner = t.binaryExpression('+', t.stringLiteral(' '), val)
|
|
51
|
+
} else if (t.isIdentifier(val) || t.isMemberExpression(val)) {
|
|
52
|
+
// identifiers and member expressions make for reasonable ternaries
|
|
53
|
+
inner = t.conditionalExpression(
|
|
54
|
+
val,
|
|
55
|
+
t.binaryExpression('+', t.stringLiteral(' '), val),
|
|
56
|
+
t.stringLiteral('')
|
|
57
|
+
)
|
|
58
|
+
} else {
|
|
59
|
+
if (t.isStringLiteral(acc)) {
|
|
60
|
+
return t.binaryExpression(
|
|
61
|
+
'+',
|
|
62
|
+
t.stringLiteral(`${acc.value} `),
|
|
63
|
+
t.logicalExpression('||', val, t.stringLiteral(''))
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
// use a logical expression for more complex prop values
|
|
67
|
+
inner = t.binaryExpression(
|
|
68
|
+
'+',
|
|
69
|
+
t.stringLiteral(' '),
|
|
70
|
+
t.logicalExpression('||', val, t.stringLiteral(''))
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return t.binaryExpression('+', acc, inner)
|
|
75
|
+
}, null)
|
|
76
|
+
}
|