@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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/check-dep-versions.cjs +267 -0
  4. package/dist/checkDeps.cjs +200 -0
  5. package/dist/constants.cjs +49 -0
  6. package/dist/exports.cjs +44 -0
  7. package/dist/extractor/accessSafe.cjs +46 -0
  8. package/dist/extractor/babelParse.cjs +54 -0
  9. package/dist/extractor/buildClassName.cjs +62 -0
  10. package/dist/extractor/bundle.cjs +185 -0
  11. package/dist/extractor/bundleConfig.cjs +513 -0
  12. package/dist/extractor/concatClassName.cjs +80 -0
  13. package/dist/extractor/createEvaluator.cjs +75 -0
  14. package/dist/extractor/createExtractor.cjs +1150 -0
  15. package/dist/extractor/createLogger.cjs +45 -0
  16. package/dist/extractor/detectModuleFormat.cjs +49 -0
  17. package/dist/extractor/ensureImportingConcat.cjs +48 -0
  18. package/dist/extractor/errors.cjs +26 -0
  19. package/dist/extractor/esbuildAliasPlugin.cjs +44 -0
  20. package/dist/extractor/esbuildTsconfigPaths.cjs +86 -0
  21. package/dist/extractor/evaluateAstNode.cjs +83 -0
  22. package/dist/extractor/extractHelpers.cjs +119 -0
  23. package/dist/extractor/extractMediaStyle.cjs +128 -0
  24. package/dist/extractor/extractToClassNames.cjs +321 -0
  25. package/dist/extractor/extractToNative.cjs +266 -0
  26. package/dist/extractor/findTopmostFunction.cjs +34 -0
  27. package/dist/extractor/generatedUid.cjs +47 -0
  28. package/dist/extractor/getGuiConfigPathFromOptionsConfig.cjs +35 -0
  29. package/dist/extractor/getPrefixLogs.cjs +29 -0
  30. package/dist/extractor/getPropValueFromAttributes.cjs +67 -0
  31. package/dist/extractor/getSourceModule.cjs +64 -0
  32. package/dist/extractor/getStaticBindingsForScope.cjs +150 -0
  33. package/dist/extractor/hoistClassNames.cjs +62 -0
  34. package/dist/extractor/literalToAst.cjs +84 -0
  35. package/dist/extractor/loadFile.cjs +11 -0
  36. package/dist/extractor/loadGui.cjs +322 -0
  37. package/dist/extractor/logLines.cjs +35 -0
  38. package/dist/extractor/normalizeTernaries.cjs +73 -0
  39. package/dist/extractor/propsToFontFamilyCache.cjs +38 -0
  40. package/dist/extractor/regenerateConfig.cjs +150 -0
  41. package/dist/extractor/removeUnusedHooks.cjs +76 -0
  42. package/dist/extractor/timer.cjs +43 -0
  43. package/dist/extractor/validHTMLAttributes.cjs +77 -0
  44. package/dist/extractor/watchGuiConfig.cjs +58 -0
  45. package/dist/getPragmaOptions.cjs +58 -0
  46. package/dist/helpers/memoize.cjs +37 -0
  47. package/dist/helpers/requireGuiCore.cjs +33 -0
  48. package/dist/index.cjs +40 -0
  49. package/dist/registerRequire.cjs +115 -0
  50. package/dist/server.cjs +75 -0
  51. package/dist/setup.cjs +0 -0
  52. package/dist/types.cjs +16 -0
  53. package/dist/worker.cjs +89 -0
  54. package/package.json +101 -0
  55. package/src/check-dep-versions.ts +738 -0
  56. package/src/checkDeps.ts +371 -0
  57. package/src/constants.ts +14 -0
  58. package/src/exports.ts +15 -0
  59. package/src/extractor/accessSafe.ts +22 -0
  60. package/src/extractor/babelParse.ts +37 -0
  61. package/src/extractor/buildClassName.ts +76 -0
  62. package/src/extractor/bundle.ts +277 -0
  63. package/src/extractor/bundleConfig.ts +958 -0
  64. package/src/extractor/concatClassName.ts +105 -0
  65. package/src/extractor/createEvaluator.ts +82 -0
  66. package/src/extractor/createExtractor.ts +2665 -0
  67. package/src/extractor/createLogger.ts +41 -0
  68. package/src/extractor/detectModuleFormat.ts +42 -0
  69. package/src/extractor/ensureImportingConcat.ts +36 -0
  70. package/src/extractor/errors.ts +1 -0
  71. package/src/extractor/esbuildAliasPlugin.ts +40 -0
  72. package/src/extractor/esbuildTsconfigPaths.ts +103 -0
  73. package/src/extractor/evaluateAstNode.ts +136 -0
  74. package/src/extractor/extractHelpers.ts +211 -0
  75. package/src/extractor/extractMediaStyle.ts +194 -0
  76. package/src/extractor/extractToClassNames.ts +631 -0
  77. package/src/extractor/extractToNative.ts +463 -0
  78. package/src/extractor/findTopmostFunction.ts +24 -0
  79. package/src/extractor/generatedUid.ts +41 -0
  80. package/src/extractor/getGuiConfigPathFromOptionsConfig.ts +24 -0
  81. package/src/extractor/getPrefixLogs.ts +9 -0
  82. package/src/extractor/getPropValueFromAttributes.ts +95 -0
  83. package/src/extractor/getSourceModule.ts +96 -0
  84. package/src/extractor/getStaticBindingsForScope.ts +237 -0
  85. package/src/extractor/hoistClassNames.ts +52 -0
  86. package/src/extractor/literalToAst.ts +85 -0
  87. package/src/extractor/loadFile.ts +17 -0
  88. package/src/extractor/loadGui.ts +487 -0
  89. package/src/extractor/logLines.ts +16 -0
  90. package/src/extractor/normalizeTernaries.ts +75 -0
  91. package/src/extractor/propsToFontFamilyCache.ts +18 -0
  92. package/src/extractor/regenerateConfig.ts +186 -0
  93. package/src/extractor/removeUnusedHooks.ts +83 -0
  94. package/src/extractor/timer.ts +25 -0
  95. package/src/extractor/validHTMLAttributes.ts +52 -0
  96. package/src/extractor/watchGuiConfig.ts +57 -0
  97. package/src/getPragmaOptions.ts +57 -0
  98. package/src/helpers/memoize.ts +24 -0
  99. package/src/helpers/requireGuiCore.ts +28 -0
  100. package/src/index.ts +4 -0
  101. package/src/registerRequire.ts +236 -0
  102. package/src/server.ts +43 -0
  103. package/src/setup.ts +0 -0
  104. package/src/types.ts +109 -0
  105. package/src/worker.ts +142 -0
  106. package/types/check-dep-versions.d.ts +37 -0
  107. package/types/check-dep-versions.d.ts.map +1 -0
  108. package/types/checkDeps.d.ts +19 -0
  109. package/types/checkDeps.d.ts.map +1 -0
  110. package/types/constants.d.ts +6 -0
  111. package/types/constants.d.ts.map +1 -0
  112. package/types/exports.d.ts +16 -0
  113. package/types/exports.d.ts.map +1 -0
  114. package/types/extractor/accessSafe.d.ts +3 -0
  115. package/types/extractor/accessSafe.d.ts.map +1 -0
  116. package/types/extractor/babelParse.d.ts +5 -0
  117. package/types/extractor/babelParse.d.ts.map +1 -0
  118. package/types/extractor/buildClassName.d.ts +7 -0
  119. package/types/extractor/buildClassName.d.ts.map +1 -0
  120. package/types/extractor/bundle.d.ts +121 -0
  121. package/types/extractor/bundle.d.ts.map +1 -0
  122. package/types/extractor/bundleConfig.d.ts +49 -0
  123. package/types/extractor/bundleConfig.d.ts.map +1 -0
  124. package/types/extractor/concatClassName.d.ts +8 -0
  125. package/types/extractor/concatClassName.d.ts.map +1 -0
  126. package/types/extractor/createEvaluator.d.ts +12 -0
  127. package/types/extractor/createEvaluator.d.ts.map +1 -0
  128. package/types/extractor/createExtractor.d.ts +32 -0
  129. package/types/extractor/createExtractor.d.ts.map +1 -0
  130. package/types/extractor/createLogger.d.ts +3 -0
  131. package/types/extractor/createLogger.d.ts.map +1 -0
  132. package/types/extractor/detectModuleFormat.d.ts +5 -0
  133. package/types/extractor/detectModuleFormat.d.ts.map +1 -0
  134. package/types/extractor/ensureImportingConcat.d.ts +4 -0
  135. package/types/extractor/ensureImportingConcat.d.ts.map +1 -0
  136. package/types/extractor/errors.d.ts +3 -0
  137. package/types/extractor/errors.d.ts.map +1 -0
  138. package/types/extractor/esbuildAliasPlugin.d.ts +18 -0
  139. package/types/extractor/esbuildAliasPlugin.d.ts.map +1 -0
  140. package/types/extractor/esbuildTsconfigPaths.d.ts +11 -0
  141. package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -0
  142. package/types/extractor/evaluateAstNode.d.ts +3 -0
  143. package/types/extractor/evaluateAstNode.d.ts.map +1 -0
  144. package/types/extractor/extractHelpers.d.ts +28 -0
  145. package/types/extractor/extractHelpers.d.ts.map +1 -0
  146. package/types/extractor/extractMediaStyle.d.ts +11 -0
  147. package/types/extractor/extractMediaStyle.d.ts.map +1 -0
  148. package/types/extractor/extractToClassNames.d.ts +25 -0
  149. package/types/extractor/extractToClassNames.d.ts.map +1 -0
  150. package/types/extractor/extractToNative.d.ts +13 -0
  151. package/types/extractor/extractToNative.d.ts.map +1 -0
  152. package/types/extractor/findTopmostFunction.d.ts +4 -0
  153. package/types/extractor/findTopmostFunction.d.ts.map +1 -0
  154. package/types/extractor/generatedUid.d.ts +5 -0
  155. package/types/extractor/generatedUid.d.ts.map +1 -0
  156. package/types/extractor/getGuiConfigPathFromOptionsConfig.d.ts +3 -0
  157. package/types/extractor/getGuiConfigPathFromOptionsConfig.d.ts.map +1 -0
  158. package/types/extractor/getPrefixLogs.d.ts +3 -0
  159. package/types/extractor/getPrefixLogs.d.ts.map +1 -0
  160. package/types/extractor/getPropValueFromAttributes.d.ts +19 -0
  161. package/types/extractor/getPropValueFromAttributes.d.ts.map +1 -0
  162. package/types/extractor/getSourceModule.d.ts +16 -0
  163. package/types/extractor/getSourceModule.d.ts.map +1 -0
  164. package/types/extractor/getStaticBindingsForScope.d.ts +5 -0
  165. package/types/extractor/getStaticBindingsForScope.d.ts.map +1 -0
  166. package/types/extractor/hoistClassNames.d.ts +6 -0
  167. package/types/extractor/hoistClassNames.d.ts.map +1 -0
  168. package/types/extractor/literalToAst.d.ts +4 -0
  169. package/types/extractor/literalToAst.d.ts.map +1 -0
  170. package/types/extractor/loadFile.d.ts +1 -0
  171. package/types/extractor/loadFile.d.ts.map +1 -0
  172. package/types/extractor/loadGui.d.ts +22 -0
  173. package/types/extractor/loadGui.d.ts.map +1 -0
  174. package/types/extractor/logLines.d.ts +2 -0
  175. package/types/extractor/logLines.d.ts.map +1 -0
  176. package/types/extractor/normalizeTernaries.d.ts +3 -0
  177. package/types/extractor/normalizeTernaries.d.ts.map +1 -0
  178. package/types/extractor/propsToFontFamilyCache.d.ts +4 -0
  179. package/types/extractor/propsToFontFamilyCache.d.ts.map +1 -0
  180. package/types/extractor/regenerateConfig.d.ts +9 -0
  181. package/types/extractor/regenerateConfig.d.ts.map +1 -0
  182. package/types/extractor/removeUnusedHooks.d.ts +3 -0
  183. package/types/extractor/removeUnusedHooks.d.ts.map +1 -0
  184. package/types/extractor/timer.d.ts +5 -0
  185. package/types/extractor/timer.d.ts.map +1 -0
  186. package/types/extractor/validHTMLAttributes.d.ts +52 -0
  187. package/types/extractor/validHTMLAttributes.d.ts.map +1 -0
  188. package/types/extractor/watchGuiConfig.d.ts +5 -0
  189. package/types/extractor/watchGuiConfig.d.ts.map +1 -0
  190. package/types/getPragmaOptions.d.ts +8 -0
  191. package/types/getPragmaOptions.d.ts.map +1 -0
  192. package/types/helpers/memoize.d.ts +8 -0
  193. package/types/helpers/memoize.d.ts.map +1 -0
  194. package/types/helpers/requireGuiCore.d.ts +3 -0
  195. package/types/helpers/requireGuiCore.d.ts.map +1 -0
  196. package/types/index.d.ts +4 -0
  197. package/types/index.d.ts.map +1 -0
  198. package/types/registerRequire.d.ts +10 -0
  199. package/types/registerRequire.d.ts.map +1 -0
  200. package/types/server.d.ts +3 -0
  201. package/types/server.d.ts.map +1 -0
  202. package/types/setup.d.ts +1 -0
  203. package/types/setup.d.ts.map +1 -0
  204. package/types/types.d.ts +92 -0
  205. package/types/types.d.ts.map +1 -0
  206. package/types/worker.d.ts +46 -0
  207. package/types/worker.d.ts.map +1 -0
@@ -0,0 +1,41 @@
1
+ import { basename } from 'node:path'
2
+
3
+ import type { GuiOptions } from '../types'
4
+ import { getPrefixLogs } from './getPrefixLogs'
5
+
6
+ export function createLogger(sourcePath: string, options: GuiOptions) {
7
+ const shouldLogTiming = options.logTimings ?? true
8
+ const start = Date.now()
9
+ const mem =
10
+ process.env.HANZO_GUI_SHOW_MEMORY_USAGE && shouldLogTiming
11
+ ? process.memoryUsage()
12
+ : null
13
+
14
+ return (res) => {
15
+ if (!shouldLogTiming) {
16
+ return
17
+ }
18
+
19
+ const memUsed = mem
20
+ ? Math.round(((process.memoryUsage().heapUsed - mem.heapUsed) / 1024 / 1204) * 10) /
21
+ 10
22
+ : 0
23
+ const path = basename(sourcePath || '')
24
+ .replace(/\.[jt]sx?$/, '')
25
+ .slice(0, 22)
26
+ .trim()
27
+ .padEnd(24)
28
+
29
+ const numOptimized = `${res.optimized + res.styled}`.padStart(3)
30
+ const numFound = `${res.found + res.styled}`.padStart(3)
31
+ const numFlattened = `${res.flattened}`.padStart(3)
32
+ const memory = memUsed ? ` ${memUsed}MB` : ''
33
+ const timing = Date.now() - start
34
+ const timingStr = `${timing}ms`.padStart(6)
35
+ const pre = getPrefixLogs(options)
36
+ const memStr = memory ? `(${memory})` : ''
37
+ console.info(
38
+ `${pre} ${path} · ${numFound} found · ${numOptimized} opt · ${numFlattened} flat ${timingStr} ${memStr}`
39
+ )
40
+ }
41
+ }
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { dirname, extname, join } from 'node:path'
3
+
4
+ type ModuleFormat = 'cjs' | 'esm'
5
+
6
+ // cache per directory to avoid repeated fs reads
7
+ const formatCache = new Map<string, ModuleFormat>()
8
+
9
+ export function detectModuleFormat(filePath: string): ModuleFormat {
10
+ const ext = extname(filePath)
11
+
12
+ // definitive by extension
13
+ if (ext === '.mjs') return 'esm'
14
+ if (ext === '.cjs') return 'cjs'
15
+
16
+ // walk up to find nearest package.json with "type" field
17
+ let dir = dirname(filePath)
18
+ while (true) {
19
+ if (formatCache.has(dir)) {
20
+ return formatCache.get(dir)!
21
+ }
22
+
23
+ try {
24
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'))
25
+ const format: ModuleFormat = pkg.type === 'module' ? 'esm' : 'cjs'
26
+ formatCache.set(dir, format)
27
+ return format
28
+ } catch {
29
+ // no package.json or malformed, keep walking
30
+ }
31
+
32
+ const parent = dirname(dir)
33
+ if (parent === dir) break
34
+ dir = parent
35
+ }
36
+
37
+ return 'cjs'
38
+ }
39
+
40
+ export function clearFormatCache() {
41
+ formatCache.clear()
42
+ }
@@ -0,0 +1,36 @@
1
+ import type { NodePath } from '@babel/traverse'
2
+ import * as t from '@babel/types'
3
+
4
+ const importConcatPkg = '@hanzogui/helpers'
5
+
6
+ export function ensureImportingConcat(path: NodePath<t.Program>) {
7
+ const bodyPath = path.get('body')
8
+ const imported: NodePath<t.ImportDeclaration> | undefined = bodyPath.find(
9
+ (x) => x.isImportDeclaration() && x.node.source.value === importConcatPkg
10
+ ) as any
11
+ const importSpecifier = t.importSpecifier(
12
+ t.identifier('concatClassName'),
13
+ t.identifier('concatClassName')
14
+ )
15
+
16
+ if (!imported) {
17
+ path.node.body.push(
18
+ t.importDeclaration([importSpecifier], t.stringLiteral(importConcatPkg))
19
+ )
20
+ return
21
+ }
22
+
23
+ const specifiers = imported.node.specifiers
24
+ const alreadyImported = specifiers.some(
25
+ (x) =>
26
+ t.isImportSpecifier(x) &&
27
+ t.isIdentifier(x.imported) &&
28
+ x.imported.name === 'concatClassName'
29
+ )
30
+
31
+ if (!alreadyImported) {
32
+ specifiers.push(
33
+ t.importSpecifier(t.identifier('concatClassName'), t.identifier('concatClassName'))
34
+ )
35
+ }
36
+ }
@@ -0,0 +1 @@
1
+ export class BailOptimizationError extends Error {}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * alias plugin
3
+ * @description
4
+ * config example:
5
+ * ```
6
+ * {
7
+ * '@lib': '/some/absolute/path'
8
+ * }
9
+ * ```
10
+ * then `import { something } from '@hanzogui/core'` will be transformed to
11
+ * `import { something } from '/some/absolute/path/xxx'`
12
+ * @param {object} config
13
+ */
14
+ export const esbuildAliasPlugin = (config) => {
15
+ const alias = config && Object.keys(config)
16
+ return {
17
+ name: 'path-alias',
18
+
19
+ setup(build) {
20
+ if (!alias || !alias.length) {
21
+ return
22
+ }
23
+ const main = (k, args) => {
24
+ const targetPath = config[k].replace(/\/$/, '')
25
+ return {
26
+ path: targetPath,
27
+ }
28
+ }
29
+
30
+ alias.forEach((k) => {
31
+ build.onResolve({ filter: new RegExp(`^.*${k}$`) }, (args) => {
32
+ return main(k, args)
33
+ })
34
+ build.onResolve({ filter: new RegExp(`^.*\\/${k}\\/.*$`) }, (args) => {
35
+ return main(k, args)
36
+ })
37
+ })
38
+ },
39
+ }
40
+ }
@@ -0,0 +1,103 @@
1
+ import type { Plugin } from 'esbuild'
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import {
5
+ findConfigFile,
6
+ nodeModuleNameResolver,
7
+ parseJsonConfigFileContent,
8
+ readConfigFile,
9
+ sys,
10
+ } from 'typescript'
11
+
12
+ const name = 'tsconfig-paths'
13
+
14
+ interface Tsconfig {
15
+ compilerOptions?: {
16
+ baseUrl?: string
17
+ paths?: Record<string, string[]>
18
+ }
19
+ }
20
+
21
+ export function TsconfigPathsPlugin(): Plugin {
22
+ const compilerOptions = loadCompilerOptionsFromTsconfig()
23
+
24
+ return {
25
+ name,
26
+ setup({ onResolve }) {
27
+ onResolve({ filter: /.*/ }, (args) => {
28
+ // skip @gui packages - they should be externalized, not resolved via tsconfig
29
+ if (args.path.startsWith('@hanzogui/')) {
30
+ return null
31
+ }
32
+
33
+ const paths = compilerOptions.paths || {}
34
+ const hasMatchingPath = Object.keys(paths).some((p) =>
35
+ new RegExp(p.replace('*', '\\w*')).test(args.path)
36
+ )
37
+
38
+ if (!hasMatchingPath) {
39
+ return null
40
+ }
41
+
42
+ const { resolvedModule } = nodeModuleNameResolver(
43
+ args.path,
44
+ args.importer,
45
+ compilerOptions,
46
+ sys
47
+ )
48
+
49
+ if (!resolvedModule) {
50
+ return null
51
+ }
52
+
53
+ const { resolvedFileName } = resolvedModule
54
+
55
+ if (!resolvedFileName || resolvedFileName.endsWith('.d.ts')) {
56
+ return null
57
+ }
58
+
59
+ return {
60
+ path: resolvedFileName,
61
+ }
62
+ })
63
+ },
64
+ }
65
+ }
66
+
67
+ export function loadCompilerOptionsFromTsconfig(tsconfig?: Tsconfig | string) {
68
+ if (!tsconfig) {
69
+ const configPath =
70
+ findConfigFile(process.cwd(), sys.fileExists, 'tsconfig.json') ||
71
+ findConfigFile(process.cwd(), sys.fileExists, 'jsconfig.json')
72
+
73
+ if (configPath) {
74
+ return parseTsconfig(configPath)
75
+ }
76
+ return {}
77
+ }
78
+
79
+ if (typeof tsconfig === 'string') {
80
+ if (fs.existsSync(tsconfig)) {
81
+ return parseTsconfig(tsconfig)
82
+ } else {
83
+ throw new Error(`Specified tsconfig file not found: ${tsconfig}`)
84
+ }
85
+ }
86
+
87
+ const baseDir = process.cwd()
88
+ const parsed = parseJsonConfigFileContent(tsconfig, sys, baseDir)
89
+ return parsed.options
90
+ }
91
+
92
+ function parseTsconfig(configFilePath: string) {
93
+ const configFile = readConfigFile(configFilePath, sys.readFile)
94
+ if (configFile.error) {
95
+ throw new Error(
96
+ `Error reading tsconfig file '${configFilePath}': ${configFile.error.messageText}`
97
+ )
98
+ }
99
+
100
+ const baseDir = path.dirname(configFilePath)
101
+ const parsed = parseJsonConfigFileContent(configFile.config, sys, baseDir)
102
+ return parsed.options
103
+ }
@@ -0,0 +1,136 @@
1
+ import * as t from '@babel/types'
2
+
3
+ export function evaluateAstNode(
4
+ exprNode: t.Node | undefined | null,
5
+ evalFn?: (node: t.Node) => any,
6
+ shouldPrintDebug?: boolean | 'verbose'
7
+ ): any {
8
+ if (exprNode === undefined) {
9
+ return undefined
10
+ }
11
+
12
+ // null === boolean true (at least in our use cases for jsx eval)
13
+ if (exprNode === null) {
14
+ return true
15
+ }
16
+
17
+ if (t.isJSXExpressionContainer(exprNode)) {
18
+ return evaluateAstNode(exprNode.expression)
19
+ }
20
+
21
+ // loop through ObjectExpression keys
22
+ if (t.isObjectExpression(exprNode)) {
23
+ const ret: Record<string, any> = {}
24
+ for (let idx = -1, len = exprNode.properties.length; ++idx < len; ) {
25
+ const value = exprNode.properties[idx]
26
+
27
+ if (!t.isObjectProperty(value)) {
28
+ throw new Error('evaluateAstNode can only evaluate object properties')
29
+ }
30
+
31
+ let key: string | number | null | undefined | boolean
32
+ if (value.computed) {
33
+ if (typeof evalFn !== 'function') {
34
+ throw new Error(
35
+ 'evaluateAstNode does not support computed keys unless an eval function is provided'
36
+ )
37
+ }
38
+ key = evaluateAstNode(value.key, evalFn)
39
+ } else if (t.isIdentifier(value.key)) {
40
+ key = value.key.name
41
+ } else if (t.isStringLiteral(value.key) || t.isNumericLiteral(value.key)) {
42
+ key = value.key.value
43
+ } else {
44
+ throw new Error('Unsupported key type: ' + value.key.type)
45
+ }
46
+
47
+ if (typeof key !== 'string' && typeof key !== 'number') {
48
+ throw new Error('key must be either a string or a number')
49
+ }
50
+
51
+ ret[key] = evaluateAstNode(value.value, evalFn)
52
+ }
53
+ return ret
54
+ }
55
+
56
+ if (t.isArrayExpression(exprNode)) {
57
+ return exprNode.elements.map((x) => {
58
+ return evaluateAstNode(x as any, evalFn)
59
+ })
60
+ }
61
+
62
+ if (t.isUnaryExpression(exprNode) && exprNode.operator === '-') {
63
+ const ret = evaluateAstNode(exprNode.argument, evalFn)
64
+ if (ret == null) {
65
+ return null
66
+ }
67
+ return -ret
68
+ }
69
+
70
+ if (t.isTemplateLiteral(exprNode)) {
71
+ if (typeof evalFn !== 'function') {
72
+ throw new Error(
73
+ 'evaluateAstNode does not support template literals unless an eval function is provided'
74
+ )
75
+ }
76
+
77
+ let ret = ''
78
+ for (let idx = -1, len = exprNode.quasis.length; ++idx < len; ) {
79
+ const quasi = exprNode.quasis[idx]
80
+ const expr = exprNode.expressions[idx]
81
+ ret += quasi.value.raw
82
+ if (expr) {
83
+ ret += evaluateAstNode(expr, evalFn)
84
+ }
85
+ }
86
+ return ret
87
+ }
88
+
89
+ // In the interest of representing the "evaluated" prop
90
+ // as the user intended, we support negative null. Why not.
91
+ if (t.isNullLiteral(exprNode)) {
92
+ return null
93
+ }
94
+
95
+ if (
96
+ t.isNumericLiteral(exprNode) ||
97
+ t.isStringLiteral(exprNode) ||
98
+ t.isBooleanLiteral(exprNode)
99
+ ) {
100
+ // In the interest of representing the "evaluated" prop
101
+ // as the user intended, we support negative null. Why not.
102
+ return exprNode.value
103
+ }
104
+
105
+ if (t.isBinaryExpression(exprNode)) {
106
+ if (exprNode.operator === '+') {
107
+ return (
108
+ evaluateAstNode(exprNode.left, evalFn) + evaluateAstNode(exprNode.right, evalFn)
109
+ )
110
+ }
111
+ if (exprNode.operator === '-') {
112
+ return (
113
+ evaluateAstNode(exprNode.left, evalFn) - evaluateAstNode(exprNode.right, evalFn)
114
+ )
115
+ }
116
+ if (exprNode.operator === '*') {
117
+ return (
118
+ evaluateAstNode(exprNode.left, evalFn) * evaluateAstNode(exprNode.right, evalFn)
119
+ )
120
+ }
121
+ if (exprNode.operator === '/') {
122
+ return (
123
+ evaluateAstNode(exprNode.left, evalFn) / evaluateAstNode(exprNode.right, evalFn)
124
+ )
125
+ }
126
+ }
127
+
128
+ // if we've made it this far, the value has to be evaluated
129
+ if (typeof evalFn !== 'function') {
130
+ throw new Error(
131
+ 'evaluateAstNode does not support non-literal values unless an eval function is provided'
132
+ )
133
+ }
134
+
135
+ return evalFn(exprNode)
136
+ }
@@ -0,0 +1,211 @@
1
+ import { basename } from 'node:path'
2
+
3
+ import generate from '@babel/generator'
4
+ import type { NodePath } from '@babel/traverse'
5
+ import * as t from '@babel/types'
6
+ import findRoot from 'find-root'
7
+
8
+ import { memoize } from '../helpers/memoize'
9
+ import type { ExtractedAttr, GuiOptionsWithFileInfo, Ternary } from '../types'
10
+
11
+ // import { astToLiteral } from './literalToAst'
12
+
13
+ export function isPresent<T extends object>(
14
+ input: null | void | undefined | T
15
+ ): input is T {
16
+ return input != null
17
+ }
18
+
19
+ export function isSimpleSpread(node: t.JSXSpreadAttribute) {
20
+ return t.isIdentifier(node.argument) || t.isMemberExpression(node.argument)
21
+ }
22
+
23
+ export const attrStr = (attr?: ExtractedAttr) => {
24
+ return !attr
25
+ ? ''
26
+ : attr.type === 'attr'
27
+ ? getNameAttr(attr.value)
28
+ : attr.type === 'ternary'
29
+ ? `...${ternaryStr(attr.value)}`
30
+ : `${attr.type}(${objToStr(attr.value)})`
31
+ }
32
+
33
+ export const objToStr = (obj: any, spacer = ', ') => {
34
+ if (!obj) {
35
+ return `${obj}`
36
+ }
37
+ return `{${Object.entries(obj)
38
+ .map(
39
+ ([k, v]) =>
40
+ `${k}:${
41
+ Array.isArray(v)
42
+ ? `[...]`
43
+ : v && typeof v === 'object'
44
+ ? `${objToStr(v, ',')}`
45
+ : JSON.stringify(v)
46
+ }`
47
+ )
48
+ .join(spacer)}}`
49
+ }
50
+
51
+ const getNameAttr = (attr: t.JSXAttribute | t.JSXSpreadAttribute) => {
52
+ if (t.isJSXSpreadAttribute(attr)) {
53
+ return `...${attr.argument['name']}`
54
+ }
55
+ return 'name' in attr ? attr.name.name : `unknown-${attr['type']}`
56
+ }
57
+
58
+ export const ternaryStr = (x: Ternary) => {
59
+ const conditional = t.isIdentifier(x.test)
60
+ ? x.test.name
61
+ : t.isMemberExpression(x.test)
62
+ ? [x.test.object['name'], x.test.property['name']]
63
+ : // @ts-ignore
64
+ generate(x.test as any).code
65
+ return [
66
+ 'ternary(',
67
+ conditional,
68
+ isFilledObj(x.consequent) ? ` ? ${objToStr(x.consequent)}` : ' ? 🚫',
69
+ isFilledObj(x.alternate) ? ` : ${objToStr(x.alternate)}` : ' : 🚫',
70
+ ')',
71
+ ]
72
+ .flat()
73
+ .join('')
74
+ }
75
+
76
+ const isFilledObj = (obj: any) => obj && Object.keys(obj).length
77
+
78
+ export function findComponentName(scope) {
79
+ const componentName = ''
80
+ let cur = scope.path
81
+ while (cur.parentPath && !t.isProgram(cur.parentPath.parent)) {
82
+ cur = cur.parentPath
83
+ }
84
+ let node = cur.parent
85
+ if (t.isExportNamedDeclaration(node)) {
86
+ node = node.declaration
87
+ }
88
+ if (t.isVariableDeclaration(node)) {
89
+ const [dec] = node.declarations
90
+ if (t.isVariableDeclarator(dec) && t.isIdentifier(dec.id)) {
91
+ return dec.id.name
92
+ }
93
+ }
94
+ if (t.isFunctionDeclaration(node)) {
95
+ return node.id?.name
96
+ }
97
+ return componentName
98
+ }
99
+
100
+ export function isValidThemeHook(
101
+ props: GuiOptionsWithFileInfo,
102
+ jsxPath: NodePath<t.JSXElement>,
103
+ n: t.MemberExpression,
104
+ sourcePath?: string
105
+ ) {
106
+ if (!t.isIdentifier(n.object) || !t.isIdentifier(n.property)) return false
107
+ const bindings = jsxPath.scope.getAllBindings()
108
+ const binding = bindings[n.object.name]
109
+ if (!binding?.path) return false
110
+ if (!binding.path.isVariableDeclarator()) return false
111
+ const init = binding.path.node.init
112
+ if (!init || !t.isCallExpression(init)) return false
113
+ if (!t.isIdentifier(init.callee)) return false
114
+ // TODO could support renaming useTheme by looking up import first
115
+ if (init.callee.name !== 'useTheme') return false
116
+ const importNode = binding.scope.getBinding('useTheme')?.path.parent
117
+ if (!t.isImportDeclaration(importNode)) return false
118
+ if (sourcePath && !isValidImport(props, sourcePath)) {
119
+ return false
120
+ }
121
+ return true
122
+ }
123
+
124
+ export const isInsideComponentPackage = (
125
+ props: GuiOptionsWithFileInfo,
126
+ moduleName: string
127
+ ) => {
128
+ return getValidComponentsPaths(props).some((path) => {
129
+ return moduleName.startsWith(path)
130
+ })
131
+ }
132
+
133
+ export const isComponentPackage = (
134
+ props: GuiOptionsWithFileInfo,
135
+ srcName: string
136
+ ) => {
137
+ return getValidComponentsPaths(props).some((path) => {
138
+ return srcName.startsWith(path)
139
+ })
140
+ }
141
+
142
+ export function getValidComponent(
143
+ props: GuiOptionsWithFileInfo,
144
+ moduleName: string,
145
+ componentName: string
146
+ ) {
147
+ // must be uppercase of course
148
+ if (componentName[0].toUpperCase() !== componentName[0]) {
149
+ return false
150
+ }
151
+
152
+ for (const loaded of props.allLoadedComponents) {
153
+ if (!loaded) continue
154
+ const isInModule = moduleName === '*' || moduleName.startsWith(loaded.moduleName)
155
+ const foundComponent = loaded.nameToInfo[componentName]
156
+ if (isInModule && foundComponent) {
157
+ return foundComponent
158
+ }
159
+ }
160
+
161
+ return null
162
+ }
163
+
164
+ export const isValidModule = (props: GuiOptionsWithFileInfo, moduleName: string) => {
165
+ if (typeof moduleName !== 'string') {
166
+ throw new Error(`No module name`)
167
+ }
168
+ const isLocal = moduleName.startsWith('.')
169
+ return {
170
+ isLocal,
171
+ isValid: isLocal
172
+ ? isInsideComponentPackage(props, moduleName)
173
+ : isComponentPackage(props, moduleName),
174
+ }
175
+ }
176
+
177
+ export const getValidImport = (
178
+ props: GuiOptionsWithFileInfo,
179
+ moduleName: string,
180
+ componentName?: string
181
+ ) => {
182
+ const { isValid, isLocal } = isValidModule(props, moduleName)
183
+ if (!isValid || !componentName) {
184
+ return null
185
+ }
186
+ return getValidComponent(props, isLocal ? '*' : moduleName, componentName) || null
187
+ }
188
+
189
+ export const isValidImport = (
190
+ props: GuiOptionsWithFileInfo,
191
+ moduleName: string,
192
+ componentName?: string
193
+ ) => {
194
+ if (!componentName) {
195
+ return isValidModule(props, moduleName).isValid
196
+ }
197
+ return Boolean(getValidImport(props, moduleName, componentName))
198
+ }
199
+
200
+ const getValidComponentPackages = memoize((props: GuiOptionsWithFileInfo) => {
201
+ // just always look for `gui` and `@hanzogui/core`
202
+ return [...new Set(['@hanzogui/core', '@hanzo/gui', ...(props.components || [])])]
203
+ })
204
+
205
+ export const getValidComponentsPaths = memoize((props: GuiOptionsWithFileInfo) => {
206
+ return getValidComponentPackages(props).flatMap((pkg) => {
207
+ const root = findRoot(pkg)
208
+ const based = basename(root)
209
+ return [based, pkg].filter(Boolean)
210
+ })
211
+ })