@tamagui/static 2.7.7 → 3.0.0-beta.643.1
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/cjs/check-dep-versions.cjs +355 -0
- package/dist/cjs/checkDeps.cjs +302 -0
- package/dist/cjs/compiler.cjs +286 -0
- package/dist/cjs/compilerHost.cjs +1695 -0
- package/dist/cjs/constants.cjs +33 -0
- package/dist/cjs/domStructuralPass.cjs +112 -0
- package/dist/cjs/exports.cjs +42 -0
- package/dist/cjs/extractor/addLocalExports.cjs +64 -0
- package/dist/cjs/extractor/bundle.cjs +203 -0
- package/dist/cjs/extractor/bundleConfig.cjs +747 -0
- package/dist/cjs/extractor/concatClassName.cjs +117 -0
- package/dist/cjs/extractor/detectModuleFormat.cjs +54 -0
- package/dist/cjs/extractor/esbuildAliasPlugin.cjs +47 -0
- package/dist/cjs/extractor/esbuildTsconfigPaths.cjs +92 -0
- package/dist/cjs/extractor/getTamaguiConfigPathFromOptionsConfig.cjs +38 -0
- package/dist/cjs/extractor/loadTamagui.cjs +344 -0
- package/dist/cjs/extractor/regenerateConfig.cjs +161 -0
- package/dist/cjs/extractor/watchTamaguiConfig.cjs +64 -0
- package/dist/cjs/getPragmaOptions.cjs +67 -0
- package/dist/cjs/helpers/memoize.cjs +42 -0
- package/dist/cjs/helpers/requireTamaguiCore.cjs +40 -0
- package/dist/cjs/index.cjs +34 -0
- package/dist/cjs/registerRequire.cjs +220 -0
- package/dist/cjs/server.cjs +66 -0
- package/dist/cjs/staticEvaluationIgnoredModules.cjs +86 -0
- package/dist/cjs/types.cjs +17 -0
- package/dist/cjs/zero/artifact.cjs +145 -0
- package/dist/cjs/zero/graph.cjs +209 -0
- package/dist/cjs/zero/identity.cjs +61 -0
- package/dist/cjs/zero/index.cjs +26 -0
- package/dist/cjs/zero/islands.cjs +232 -0
- package/dist/cjs/zero/options.cjs +163 -0
- package/dist/cjs/zero/ownership.cjs +79 -0
- package/dist/cjs/zero/theme.cjs +328 -0
- package/dist/cjs/zero/transform.cjs +203 -0
- package/dist/esm/check-dep-versions.mjs +329 -0
- package/dist/esm/check-dep-versions.mjs.map +1 -0
- package/dist/esm/checkDeps.mjs +279 -0
- package/dist/esm/checkDeps.mjs.map +1 -0
- package/dist/esm/compiler.mjs +255 -0
- package/dist/esm/compiler.mjs.map +1 -0
- package/dist/esm/compilerHost.mjs +1675 -0
- package/dist/esm/compilerHost.mjs.map +1 -0
- package/dist/esm/constants.mjs +7 -0
- package/dist/esm/constants.mjs.map +1 -0
- package/dist/esm/domStructuralPass.mjs +92 -0
- package/dist/esm/domStructuralPass.mjs.map +1 -0
- package/dist/esm/exports.mjs +26 -0
- package/dist/esm/extractor/addLocalExports.mjs +44 -0
- package/dist/esm/extractor/addLocalExports.mjs.map +1 -0
- package/dist/esm/extractor/bundle.mjs +173 -0
- package/dist/esm/extractor/bundle.mjs.map +1 -0
- package/dist/esm/extractor/bundleConfig.mjs +706 -0
- package/dist/esm/extractor/bundleConfig.mjs.map +1 -0
- package/dist/esm/extractor/concatClassName.mjs +96 -0
- package/dist/esm/extractor/concatClassName.mjs.map +1 -0
- package/dist/esm/extractor/detectModuleFormat.mjs +31 -0
- package/dist/esm/extractor/detectModuleFormat.mjs.map +1 -0
- package/dist/esm/extractor/esbuildAliasPlugin.mjs +26 -0
- package/dist/esm/extractor/esbuildAliasPlugin.mjs.map +1 -0
- package/dist/esm/extractor/esbuildTsconfigPaths.mjs +63 -0
- package/dist/esm/extractor/esbuildTsconfigPaths.mjs.map +1 -0
- package/dist/esm/extractor/getTamaguiConfigPathFromOptionsConfig.mjs +18 -0
- package/dist/esm/extractor/getTamaguiConfigPathFromOptionsConfig.mjs.map +1 -0
- package/dist/esm/extractor/loadTamagui.mjs +306 -0
- package/dist/esm/extractor/loadTamagui.mjs.map +1 -0
- package/dist/esm/extractor/regenerateConfig.mjs +131 -0
- package/dist/esm/extractor/regenerateConfig.mjs.map +1 -0
- package/dist/esm/extractor/watchTamaguiConfig.mjs +43 -0
- package/dist/esm/extractor/watchTamaguiConfig.mjs.map +1 -0
- package/dist/esm/getPragmaOptions.mjs +46 -0
- package/dist/esm/getPragmaOptions.mjs.map +1 -0
- package/dist/esm/helpers/memoize.mjs +21 -0
- package/dist/esm/helpers/memoize.mjs.map +1 -0
- package/dist/esm/helpers/requireTamaguiCore.mjs +19 -0
- package/dist/esm/helpers/requireTamaguiCore.mjs.map +1 -0
- package/dist/esm/index.mjs +8 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/registerRequire.mjs +195 -0
- package/dist/esm/registerRequire.mjs.map +1 -0
- package/dist/esm/server.mjs +40 -0
- package/dist/esm/server.mjs.map +1 -0
- package/dist/esm/setup.mjs +0 -0
- package/dist/esm/staticEvaluationIgnoredModules.mjs +61 -0
- package/dist/esm/staticEvaluationIgnoredModules.mjs.map +1 -0
- package/dist/esm/types.mjs +0 -0
- package/dist/esm/zero/artifact.mjs +119 -0
- package/dist/esm/zero/artifact.mjs.map +1 -0
- package/dist/esm/zero/graph.mjs +170 -0
- package/dist/esm/zero/graph.mjs.map +1 -0
- package/dist/esm/zero/identity.mjs +36 -0
- package/dist/esm/zero/identity.mjs.map +1 -0
- package/dist/esm/zero/index.mjs +15 -0
- package/dist/esm/zero/islands.mjs +197 -0
- package/dist/esm/zero/islands.mjs.map +1 -0
- package/dist/esm/zero/options.mjs +129 -0
- package/dist/esm/zero/options.mjs.map +1 -0
- package/dist/esm/zero/ownership.mjs +48 -0
- package/dist/esm/zero/ownership.mjs.map +1 -0
- package/dist/esm/zero/theme.mjs +303 -0
- package/dist/esm/zero/theme.mjs.map +1 -0
- package/dist/esm/zero/transform.mjs +177 -0
- package/dist/esm/zero/transform.mjs.map +1 -0
- package/package.json +33 -43
- package/src/check-dep-versions.ts +2 -2
- package/src/checkDeps.ts +3 -2
- package/src/compiler.ts +425 -0
- package/src/compilerHost.ts +3125 -0
- package/src/constants.ts +0 -5
- package/src/domStructuralPass.ts +170 -0
- package/src/exports.ts +3 -6
- package/src/extractor/addLocalExports.ts +49 -0
- package/src/extractor/bundle.ts +20 -35
- package/src/extractor/bundleConfig.ts +334 -187
- package/src/extractor/concatClassName.ts +13 -20
- package/src/extractor/getTamaguiConfigPathFromOptionsConfig.ts +5 -4
- package/src/extractor/loadTamagui.ts +196 -143
- package/src/extractor/regenerateConfig.ts +65 -13
- package/src/extractor/watchTamaguiConfig.ts +6 -1
- package/src/helpers/requireTamaguiCore.ts +6 -1
- package/src/registerRequire.ts +94 -132
- package/src/staticEvaluationIgnoredModules.ts +74 -0
- package/src/types.ts +2 -107
- package/src/zero/artifact.ts +142 -0
- package/src/zero/graph.ts +359 -0
- package/src/zero/identity.ts +73 -0
- package/src/zero/index.ts +8 -0
- package/src/zero/islands.ts +262 -0
- package/src/zero/options.ts +239 -0
- package/src/zero/ownership.ts +113 -0
- package/src/zero/theme.ts +478 -0
- package/src/zero/transform.ts +320 -0
- package/types/check-dep-versions.d.ts.map +1 -1
- package/types/checkDeps.d.ts.map +1 -1
- package/types/compiler.d.ts +117 -0
- package/types/compiler.d.ts.map +1 -0
- package/types/compilerHost.d.ts +25 -0
- package/types/compilerHost.d.ts.map +1 -0
- package/types/constants.d.ts +0 -1
- package/types/constants.d.ts.map +1 -1
- package/types/domStructuralPass.d.ts +3 -0
- package/types/domStructuralPass.d.ts.map +1 -0
- package/types/exports.d.ts +3 -6
- package/types/exports.d.ts.map +1 -1
- package/types/extractor/addLocalExports.d.ts +3 -0
- package/types/extractor/addLocalExports.d.ts.map +1 -0
- package/types/extractor/bundle.d.ts +66 -65
- package/types/extractor/bundle.d.ts.map +1 -1
- package/types/extractor/bundleConfig.d.ts +19 -4
- package/types/extractor/bundleConfig.d.ts.map +1 -1
- package/types/extractor/esbuildAliasPlugin.d.ts.map +1 -1
- package/types/extractor/getTamaguiConfigPathFromOptionsConfig.d.ts +1 -1
- package/types/extractor/getTamaguiConfigPathFromOptionsConfig.d.ts.map +1 -1
- package/types/extractor/loadTamagui.d.ts +26 -3
- package/types/extractor/loadTamagui.d.ts.map +1 -1
- package/types/extractor/regenerateConfig.d.ts +1 -1
- package/types/extractor/regenerateConfig.d.ts.map +1 -1
- package/types/extractor/watchTamaguiConfig.d.ts.map +1 -1
- package/types/getPragmaOptions.d.ts +1 -1
- package/types/helpers/memoize.d.ts +1 -1
- package/types/helpers/memoize.d.ts.map +1 -1
- package/types/helpers/requireTamaguiCore.d.ts.map +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/registerRequire.d.ts +2 -2
- package/types/registerRequire.d.ts.map +1 -1
- package/types/staticEvaluationIgnoredModules.d.ts +9 -0
- package/types/staticEvaluationIgnoredModules.d.ts.map +1 -0
- package/types/types.d.ts +2 -90
- package/types/types.d.ts.map +1 -1
- package/types/zero/artifact.d.ts +44 -0
- package/types/zero/artifact.d.ts.map +1 -0
- package/types/zero/graph.d.ts +139 -0
- package/types/zero/graph.d.ts.map +1 -0
- package/types/zero/identity.d.ts +33 -0
- package/types/zero/identity.d.ts.map +1 -0
- package/types/zero/index.d.ts +9 -0
- package/types/zero/index.d.ts.map +1 -0
- package/types/zero/islands.d.ts +56 -0
- package/types/zero/islands.d.ts.map +1 -0
- package/types/zero/options.d.ts +58 -0
- package/types/zero/options.d.ts.map +1 -0
- package/types/zero/ownership.d.ts +51 -0
- package/types/zero/ownership.d.ts.map +1 -0
- package/types/zero/theme.d.ts +83 -0
- package/types/zero/theme.d.ts.map +1 -0
- package/types/zero/transform.d.ts +53 -0
- package/types/zero/transform.d.ts.map +1 -0
- package/dist/check-dep-versions.cjs +0 -372
- package/dist/checkDeps.cjs +0 -271
- package/dist/constants.cjs +0 -51
- package/dist/exports.cjs +0 -48
- package/dist/extractor/accessSafe.cjs +0 -48
- package/dist/extractor/babelParse.cjs +0 -56
- package/dist/extractor/buildClassName.cjs +0 -86
- package/dist/extractor/bundle.cjs +0 -234
- package/dist/extractor/bundleConfig.cjs +0 -729
- package/dist/extractor/concatClassName.cjs +0 -129
- package/dist/extractor/createEvaluator.cjs +0 -88
- package/dist/extractor/createExtractor.cjs +0 -1968
- package/dist/extractor/createLogger.cjs +0 -50
- package/dist/extractor/detectModuleFormat.cjs +0 -55
- package/dist/extractor/ensureImportingConcat.cjs +0 -54
- package/dist/extractor/errors.cjs +0 -28
- package/dist/extractor/esbuildAliasPlugin.cjs +0 -56
- package/dist/extractor/esbuildTsconfigPaths.cjs +0 -108
- package/dist/extractor/evaluateAstNode.cjs +0 -128
- package/dist/extractor/extractHelpers.cjs +0 -182
- package/dist/extractor/extractMediaStyle.cjs +0 -169
- package/dist/extractor/extractToClassNames.cjs +0 -475
- package/dist/extractor/extractToNative.cjs +0 -369
- package/dist/extractor/findTopmostFunction.cjs +0 -43
- package/dist/extractor/generatedUid.cjs +0 -58
- package/dist/extractor/getPrefixLogs.cjs +0 -31
- package/dist/extractor/getPropValueFromAttributes.cjs +0 -85
- package/dist/extractor/getSourceModule.cjs +0 -100
- package/dist/extractor/getStaticBindingsForScope.cjs +0 -213
- package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.cjs +0 -41
- package/dist/extractor/hasTopLevelAwait.cjs +0 -62
- package/dist/extractor/hoistClassNames.cjs +0 -76
- package/dist/extractor/literalToAst.cjs +0 -109
- package/dist/extractor/loadFile.cjs +0 -17
- package/dist/extractor/loadTamagui.cjs +0 -373
- package/dist/extractor/logLines.cjs +0 -43
- package/dist/extractor/normalizeTernaries.cjs +0 -100
- package/dist/extractor/propsToFontFamilyCache.cjs +0 -42
- package/dist/extractor/regenerateConfig.cjs +0 -178
- package/dist/extractor/removeUnusedHooks.cjs +0 -108
- package/dist/extractor/timer.cjs +0 -52
- package/dist/extractor/validHTMLAttributes.cjs +0 -79
- package/dist/extractor/watchTamaguiConfig.cjs +0 -70
- package/dist/getPragmaOptions.cjs +0 -73
- package/dist/helpers/memoize.cjs +0 -45
- package/dist/helpers/requireTamaguiCore.cjs +0 -40
- package/dist/index.cjs +0 -42
- package/dist/registerRequire.cjs +0 -209
- package/dist/server.cjs +0 -82
- package/dist/types.cjs +0 -18
- package/dist/worker.cjs +0 -111
- package/src/extractor/accessSafe.ts +0 -22
- package/src/extractor/babelParse.ts +0 -38
- package/src/extractor/buildClassName.ts +0 -76
- package/src/extractor/createEvaluator.ts +0 -82
- package/src/extractor/createExtractor.ts +0 -2970
- package/src/extractor/createLogger.ts +0 -41
- package/src/extractor/ensureImportingConcat.ts +0 -36
- package/src/extractor/errors.ts +0 -1
- package/src/extractor/evaluateAstNode.ts +0 -136
- package/src/extractor/extractHelpers.ts +0 -211
- package/src/extractor/extractMediaStyle.ts +0 -194
- package/src/extractor/extractToClassNames.ts +0 -733
- package/src/extractor/extractToNative.ts +0 -506
- package/src/extractor/findTopmostFunction.ts +0 -24
- package/src/extractor/generatedUid.ts +0 -41
- package/src/extractor/getPrefixLogs.ts +0 -9
- package/src/extractor/getPropValueFromAttributes.ts +0 -95
- package/src/extractor/getSourceModule.ts +0 -96
- package/src/extractor/getStaticBindingsForScope.ts +0 -237
- package/src/extractor/hasTopLevelAwait.ts +0 -28
- package/src/extractor/hoistClassNames.ts +0 -52
- package/src/extractor/literalToAst.ts +0 -85
- package/src/extractor/loadFile.ts +0 -17
- package/src/extractor/logLines.ts +0 -16
- package/src/extractor/normalizeTernaries.ts +0 -89
- package/src/extractor/propsToFontFamilyCache.ts +0 -18
- package/src/extractor/removeUnusedHooks.ts +0 -83
- package/src/extractor/timer.ts +0 -25
- package/src/extractor/validHTMLAttributes.ts +0 -52
- package/src/worker.ts +0 -142
- package/types/extractor/accessSafe.d.ts +0 -3
- package/types/extractor/accessSafe.d.ts.map +0 -1
- package/types/extractor/babelParse.d.ts +0 -5
- package/types/extractor/babelParse.d.ts.map +0 -1
- package/types/extractor/buildClassName.d.ts +0 -7
- package/types/extractor/buildClassName.d.ts.map +0 -1
- package/types/extractor/createEvaluator.d.ts +0 -12
- package/types/extractor/createEvaluator.d.ts.map +0 -1
- package/types/extractor/createExtractor.d.ts +0 -32
- package/types/extractor/createExtractor.d.ts.map +0 -1
- package/types/extractor/createLogger.d.ts +0 -3
- package/types/extractor/createLogger.d.ts.map +0 -1
- package/types/extractor/ensureImportingConcat.d.ts +0 -4
- package/types/extractor/ensureImportingConcat.d.ts.map +0 -1
- package/types/extractor/errors.d.ts +0 -3
- package/types/extractor/errors.d.ts.map +0 -1
- package/types/extractor/evaluateAstNode.d.ts +0 -3
- package/types/extractor/evaluateAstNode.d.ts.map +0 -1
- package/types/extractor/extractHelpers.d.ts +0 -28
- package/types/extractor/extractHelpers.d.ts.map +0 -1
- package/types/extractor/extractMediaStyle.d.ts +0 -11
- package/types/extractor/extractMediaStyle.d.ts.map +0 -1
- package/types/extractor/extractToClassNames.d.ts +0 -25
- package/types/extractor/extractToClassNames.d.ts.map +0 -1
- package/types/extractor/extractToNative.d.ts +0 -13
- package/types/extractor/extractToNative.d.ts.map +0 -1
- package/types/extractor/findTopmostFunction.d.ts +0 -4
- package/types/extractor/findTopmostFunction.d.ts.map +0 -1
- package/types/extractor/generatedUid.d.ts +0 -5
- package/types/extractor/generatedUid.d.ts.map +0 -1
- package/types/extractor/getPrefixLogs.d.ts +0 -3
- package/types/extractor/getPrefixLogs.d.ts.map +0 -1
- package/types/extractor/getPropValueFromAttributes.d.ts +0 -19
- package/types/extractor/getPropValueFromAttributes.d.ts.map +0 -1
- package/types/extractor/getSourceModule.d.ts +0 -16
- package/types/extractor/getSourceModule.d.ts.map +0 -1
- package/types/extractor/getStaticBindingsForScope.d.ts +0 -5
- package/types/extractor/getStaticBindingsForScope.d.ts.map +0 -1
- package/types/extractor/hasTopLevelAwait.d.ts +0 -2
- package/types/extractor/hasTopLevelAwait.d.ts.map +0 -1
- package/types/extractor/hoistClassNames.d.ts +0 -6
- package/types/extractor/hoistClassNames.d.ts.map +0 -1
- package/types/extractor/literalToAst.d.ts +0 -4
- package/types/extractor/literalToAst.d.ts.map +0 -1
- package/types/extractor/loadFile.d.ts +0 -1
- package/types/extractor/loadFile.d.ts.map +0 -1
- package/types/extractor/logLines.d.ts +0 -2
- package/types/extractor/logLines.d.ts.map +0 -1
- package/types/extractor/normalizeTernaries.d.ts +0 -5
- package/types/extractor/normalizeTernaries.d.ts.map +0 -1
- package/types/extractor/propsToFontFamilyCache.d.ts +0 -4
- package/types/extractor/propsToFontFamilyCache.d.ts.map +0 -1
- package/types/extractor/removeUnusedHooks.d.ts +0 -3
- package/types/extractor/removeUnusedHooks.d.ts.map +0 -1
- package/types/extractor/timer.d.ts +0 -5
- package/types/extractor/timer.d.ts.map +0 -1
- package/types/extractor/validHTMLAttributes.d.ts +0 -52
- package/types/extractor/validHTMLAttributes.d.ts.map +0 -1
- package/types/worker.d.ts +0 -46
- package/types/worker.d.ts.map +0 -1
- /package/dist/{setup.cjs → cjs/setup.cjs} +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The cross-build CSS coordinator.
|
|
7
|
+
*
|
|
8
|
+
* A zero-runtime build owns exactly one CSS artifact. It holds the config CSS,
|
|
9
|
+
* the zero entry's compiler atomic CSS, every island child build's atomic CSS,
|
|
10
|
+
* and every theme-bridge class. `TAMAGUI_DID_OUTPUT_CSS` is derived from this
|
|
11
|
+
* object's completeness, never set by an author, so the JavaScript stripping
|
|
12
|
+
* fact and its replacement asset cannot diverge.
|
|
13
|
+
*/
|
|
14
|
+
export class ZeroCSSArtifact {
|
|
15
|
+
#config: string | null = null
|
|
16
|
+
#zero = new Map<string, string>()
|
|
17
|
+
#islands = new Map<string, Map<string, string>>()
|
|
18
|
+
#bridges = new Map<string, string>()
|
|
19
|
+
#expectedIslands: readonly string[] = []
|
|
20
|
+
#cssPath: string
|
|
21
|
+
|
|
22
|
+
constructor(cssPath: string) {
|
|
23
|
+
this.#cssPath = cssPath
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get cssPath() {
|
|
27
|
+
return this.#cssPath
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
expectIslands(ids: readonly string[]) {
|
|
31
|
+
this.#expectedIslands = [...ids].sort()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setConfigCSS(css: string) {
|
|
35
|
+
this.#config = css
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setZeroModuleCSS(moduleId: string, css: string) {
|
|
39
|
+
if (css) this.#zero.set(moduleId, css)
|
|
40
|
+
else this.#zero.delete(moduleId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setIslandModuleCSS(islandId: string, moduleId: string, css: string) {
|
|
44
|
+
let bucket = this.#islands.get(islandId)
|
|
45
|
+
if (!bucket) {
|
|
46
|
+
bucket = new Map()
|
|
47
|
+
this.#islands.set(islandId, bucket)
|
|
48
|
+
}
|
|
49
|
+
if (css) bucket.set(moduleId, css)
|
|
50
|
+
else bucket.delete(moduleId)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** This island's collected rules, in deterministic module order. */
|
|
54
|
+
islandCSS(islandId: string): string[] {
|
|
55
|
+
const bucket = this.#islands.get(islandId)
|
|
56
|
+
if (!bucket) return []
|
|
57
|
+
return [...bucket.keys()].sort().map((moduleId) => bucket.get(moduleId)!)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Marks an island as compiled even when it contributed no atomic rules. */
|
|
61
|
+
markIslandComplete(islandId: string) {
|
|
62
|
+
if (!this.#islands.has(islandId)) this.#islands.set(islandId, new Map())
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Collected zero-graph module CSS, for an integration that persists it. */
|
|
66
|
+
zeroModuleEntries(): [string, string][] {
|
|
67
|
+
return [...this.#zero.entries()].sort(([left], [right]) => (left < right ? -1 : 1))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
bridgeEntries(): [string, string][] {
|
|
71
|
+
return [...this.#bridges.entries()].sort(([left], [right]) => (left < right ? -1 : 1))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setBridgeRules(bridgeId: string, css: string) {
|
|
75
|
+
if (css) this.#bridges.set(bridgeId, css)
|
|
76
|
+
else this.#bridges.delete(bridgeId)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
clearGraphs() {
|
|
80
|
+
this.#zero.clear()
|
|
81
|
+
this.#islands.clear()
|
|
82
|
+
this.#bridges.clear()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The missing pieces that block deriving TAMAGUI_DID_OUTPUT_CSS. */
|
|
86
|
+
missing(): string[] {
|
|
87
|
+
const missing: string[] = []
|
|
88
|
+
if (this.#config === null) missing.push('config CSS')
|
|
89
|
+
for (const id of this.#expectedIslands) {
|
|
90
|
+
if (!this.#islands.has(id)) missing.push(`island ${id}`)
|
|
91
|
+
}
|
|
92
|
+
return missing
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
isComplete() {
|
|
96
|
+
return this.missing().length === 0
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Deterministic order: config, zero atomic, island atomic, bridge classes. */
|
|
100
|
+
css(): string {
|
|
101
|
+
const parts: string[] = []
|
|
102
|
+
if (this.#config) parts.push(this.#config)
|
|
103
|
+
for (const moduleId of [...this.#zero.keys()].sort()) {
|
|
104
|
+
parts.push(this.#zero.get(moduleId)!)
|
|
105
|
+
}
|
|
106
|
+
for (const islandId of [...this.#islands.keys()].sort()) {
|
|
107
|
+
const bucket = this.#islands.get(islandId)!
|
|
108
|
+
for (const moduleId of [...bucket.keys()].sort()) parts.push(bucket.get(moduleId)!)
|
|
109
|
+
}
|
|
110
|
+
for (const bridgeId of [...this.#bridges.keys()].sort()) {
|
|
111
|
+
parts.push(this.#bridges.get(bridgeId)!)
|
|
112
|
+
}
|
|
113
|
+
return parts.join('\n')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
hash(): string {
|
|
117
|
+
return createHash('sha256').update(this.css()).digest('hex').slice(0, 16)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Writes the artifact and returns whether the derived
|
|
122
|
+
* `TAMAGUI_DID_OUTPUT_CSS='1'` claim is now legal.
|
|
123
|
+
*/
|
|
124
|
+
write(): { path: string; hash: string; complete: boolean; missing: string[] } {
|
|
125
|
+
const missing = this.missing()
|
|
126
|
+
const css = this.css()
|
|
127
|
+
mkdirSync(path.dirname(this.#cssPath), { recursive: true })
|
|
128
|
+
let existing: string | null = null
|
|
129
|
+
try {
|
|
130
|
+
existing = readFileSync(this.#cssPath, 'utf8')
|
|
131
|
+
} catch {
|
|
132
|
+
existing = null
|
|
133
|
+
}
|
|
134
|
+
if (existing !== css) writeFileSync(this.#cssPath, css)
|
|
135
|
+
return {
|
|
136
|
+
path: this.#cssPath,
|
|
137
|
+
hash: this.hash(),
|
|
138
|
+
complete: missing.length === 0,
|
|
139
|
+
missing,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { ZERO_FAILURE_FOOTER, type ZeroRule } from '@tamagui/compiler-core'
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The zero-runtime bundle gate.
|
|
7
|
+
*
|
|
8
|
+
* Reference erasure is what removes the modules; this is what proves it. A green
|
|
9
|
+
* build is not evidence, so every integration hands its own resolved module ids
|
|
10
|
+
* and importer chains to this one check and writes a machine-readable receipt.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface ZeroGraphModule {
|
|
14
|
+
id: string
|
|
15
|
+
/** Direct importers of this module inside the same graph. */
|
|
16
|
+
importers?: readonly string[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ZeroForbiddenModule {
|
|
20
|
+
id: string
|
|
21
|
+
/** Shortest importer chain from an entry to the forbidden module. */
|
|
22
|
+
chain: string[]
|
|
23
|
+
/** The package that owns the module, which is what the gate matched on. */
|
|
24
|
+
owner: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ZeroGraphReceipt {
|
|
28
|
+
integration: string
|
|
29
|
+
graph: 'zero' | 'island' | 'negative-control'
|
|
30
|
+
entries: string[]
|
|
31
|
+
moduleCount: number
|
|
32
|
+
tamaguiModules: string[]
|
|
33
|
+
forbidden: ZeroForbiddenModule[]
|
|
34
|
+
cssArtifact: { path: string; hash: string } | null
|
|
35
|
+
identity: string
|
|
36
|
+
gzip?: Record<string, number>
|
|
37
|
+
/**
|
|
38
|
+
* Whether this build restored its artifact from a persisted plan cache
|
|
39
|
+
* instead of rescanning. Only integrations that cache plans set it.
|
|
40
|
+
*/
|
|
41
|
+
plansRestoredFromCache?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The one allowlisted Tamagui client module. Everything else owned by `tamagui`
|
|
46
|
+
* or an `@tamagui/*` package is forbidden in a zero entry graph.
|
|
47
|
+
*/
|
|
48
|
+
const ALLOWED_ZERO_MODULE = /animated-number/
|
|
49
|
+
|
|
50
|
+
/** A module's owning package: the nearest package.json and the name in it. */
|
|
51
|
+
export interface OwningPackage {
|
|
52
|
+
name: string | null
|
|
53
|
+
manifest: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const owningPackageCache = new Map<string, OwningPackage | null>()
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The package that owns a module, read from the nearest package.json.
|
|
60
|
+
*
|
|
61
|
+
* Path matching is not enough: in this monorepo `@tamagui/web` resolves to
|
|
62
|
+
* `code/core/web/dist/...`, which contains no `@tamagui` path segment at all. A
|
|
63
|
+
* gate that greps ids would report a clean graph on a bundle that ships the
|
|
64
|
+
* whole runtime.
|
|
65
|
+
*/
|
|
66
|
+
export function owningPackageOf(id: string): OwningPackage | null {
|
|
67
|
+
const clean = id.split(/[?#]/, 1)[0]!
|
|
68
|
+
if (!clean || clean.startsWith('\0') || !path.isAbsolute(clean)) return null
|
|
69
|
+
return owningPackageOfDir(path.dirname(clean))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The same walk from a directory, for the project root itself. */
|
|
73
|
+
export function owningPackageOfDir(from: string): OwningPackage | null {
|
|
74
|
+
if (!from || !path.isAbsolute(from)) return null
|
|
75
|
+
let dir = from
|
|
76
|
+
const visited: string[] = []
|
|
77
|
+
while (true) {
|
|
78
|
+
const cached = owningPackageCache.get(dir)
|
|
79
|
+
if (cached !== undefined) {
|
|
80
|
+
for (const seen of visited) owningPackageCache.set(seen, cached)
|
|
81
|
+
return cached
|
|
82
|
+
}
|
|
83
|
+
visited.push(dir)
|
|
84
|
+
const manifest = path.join(dir, 'package.json')
|
|
85
|
+
if (existsSync(manifest)) {
|
|
86
|
+
let owner: OwningPackage | null = null
|
|
87
|
+
try {
|
|
88
|
+
owner = {
|
|
89
|
+
name: JSON.parse(readFileSync(manifest, 'utf8')).name ?? null,
|
|
90
|
+
manifest,
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
owner = { name: null, manifest }
|
|
94
|
+
}
|
|
95
|
+
for (const seen of visited) owningPackageCache.set(seen, owner)
|
|
96
|
+
return owner
|
|
97
|
+
}
|
|
98
|
+
const parent = path.dirname(dir)
|
|
99
|
+
if (parent === dir) {
|
|
100
|
+
for (const seen of visited) owningPackageCache.set(seen, null)
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
dir = parent
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* `ownManifest` is the building project's own package.json.
|
|
109
|
+
*
|
|
110
|
+
* A project may legitimately name itself `tamagui` or `@tamagui/something` —
|
|
111
|
+
* this repo's own site and examples do — and then every one of its modules
|
|
112
|
+
* answers this question the same way Tamagui's do. The distinction that
|
|
113
|
+
* actually holds is ownership, not spelling: Tamagui reaches a build as a
|
|
114
|
+
* resolved dependency, so its modules are owned by a different package.json
|
|
115
|
+
* than the one being built. Excluding the project's own manifest keeps the gate
|
|
116
|
+
* a name-free rule rather than a list of names to carve out.
|
|
117
|
+
*/
|
|
118
|
+
export function isTamaguiModuleId(id: string, ownManifest?: string | null): boolean {
|
|
119
|
+
const owner = owningPackageOf(id)
|
|
120
|
+
if (!owner?.name) return false
|
|
121
|
+
if (ownManifest && owner.manifest === ownManifest) return false
|
|
122
|
+
return owner.name === 'tamagui' || owner.name.startsWith('@tamagui/')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function isForbiddenZeroModuleId(
|
|
126
|
+
id: string,
|
|
127
|
+
ownManifest?: string | null
|
|
128
|
+
): boolean {
|
|
129
|
+
if (!isTamaguiModuleId(id, ownManifest)) return false
|
|
130
|
+
return !ALLOWED_ZERO_MODULE.test(id)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function shortestChain(
|
|
134
|
+
target: string,
|
|
135
|
+
entries: readonly string[],
|
|
136
|
+
importersOf: ReadonlyMap<string, readonly string[]>
|
|
137
|
+
): string[] {
|
|
138
|
+
const entrySet = new Set(entries)
|
|
139
|
+
const seen = new Set([target])
|
|
140
|
+
const queue: string[][] = [[target]]
|
|
141
|
+
// an adapter may not expose its entry modules by the same id it uses for the
|
|
142
|
+
// rest of the graph, so fall back to the longest ancestor chain we can walk
|
|
143
|
+
let longest: string[] = [target]
|
|
144
|
+
while (queue.length) {
|
|
145
|
+
const chain = queue.shift()!
|
|
146
|
+
const head = chain[0]!
|
|
147
|
+
if (entrySet.has(head)) return chain
|
|
148
|
+
if (chain.length > longest.length) longest = chain
|
|
149
|
+
for (const importer of importersOf.get(head) ?? []) {
|
|
150
|
+
if (seen.has(importer)) continue
|
|
151
|
+
seen.add(importer)
|
|
152
|
+
queue.push([importer, ...chain])
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return longest
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function checkZeroGraph(input: {
|
|
159
|
+
entries: readonly string[]
|
|
160
|
+
/** The modules that actually shipped in the emitted chunks. */
|
|
161
|
+
modules: readonly ZeroGraphModule[]
|
|
162
|
+
/**
|
|
163
|
+
* Importer edges for the whole resolved graph. A forbidden module's chain
|
|
164
|
+
* usually runs through modules that were merged into another chunk, so the
|
|
165
|
+
* shipped set alone cannot reconstruct it.
|
|
166
|
+
*/
|
|
167
|
+
importerEdges?: ReadonlyMap<string, readonly string[]>
|
|
168
|
+
/**
|
|
169
|
+
* The project being built. Its own package.json is what
|
|
170
|
+
* `isTamaguiModuleId` excludes, so a project named `@tamagui/something` does
|
|
171
|
+
* not report every one of its own modules as a forbidden Tamagui module.
|
|
172
|
+
*
|
|
173
|
+
* It cannot be derived from the entries: webpack's entry for a Next app is
|
|
174
|
+
* `node_modules/next/dist/client/next.js`, which belongs to `next`.
|
|
175
|
+
*/
|
|
176
|
+
root: string
|
|
177
|
+
}): { tamaguiModules: string[]; forbidden: ZeroForbiddenModule[] } {
|
|
178
|
+
const importersOf = new Map<string, readonly string[]>(input.importerEdges)
|
|
179
|
+
for (const module of input.modules) {
|
|
180
|
+
if (!importersOf.has(module.id)) importersOf.set(module.id, module.importers ?? [])
|
|
181
|
+
}
|
|
182
|
+
const ownManifest = input.root
|
|
183
|
+
? (owningPackageOfDir(input.root)?.manifest ?? null)
|
|
184
|
+
: null
|
|
185
|
+
const tamaguiModules = input.modules
|
|
186
|
+
.map((module) => module.id)
|
|
187
|
+
.filter((id) => isTamaguiModuleId(id, ownManifest))
|
|
188
|
+
.sort()
|
|
189
|
+
const forbidden = tamaguiModules
|
|
190
|
+
.filter((id) => isForbiddenZeroModuleId(id, ownManifest))
|
|
191
|
+
.map((id) => ({
|
|
192
|
+
id,
|
|
193
|
+
chain: shortestChain(id, input.entries, importersOf),
|
|
194
|
+
owner: owningPackageOf(id)?.name ?? '',
|
|
195
|
+
}))
|
|
196
|
+
return { tamaguiModules, forbidden }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function formatZeroGraphFailure(receipt: ZeroGraphReceipt): string {
|
|
200
|
+
const lines = [
|
|
201
|
+
`[tamagui zero-runtime] ${receipt.integration} zero entry graph contains ${receipt.forbidden.length} forbidden Tamagui module(s)`,
|
|
202
|
+
'',
|
|
203
|
+
]
|
|
204
|
+
for (const entry of receipt.forbidden) {
|
|
205
|
+
// the package name is what the gate matched on, and the path often does not
|
|
206
|
+
// show it: `@tamagui/web` resolves to `code/core/web/dist/...` here
|
|
207
|
+
lines.push(entry.owner ? `${entry.id} (package ${entry.owner})` : entry.id)
|
|
208
|
+
lines.push(
|
|
209
|
+
entry.chain.length > 1
|
|
210
|
+
? ` ${entry.chain.join('\n -> ')}`
|
|
211
|
+
: ` (this bundler exposed no importer edges for that module)`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
lines.push('')
|
|
215
|
+
lines.push(ZERO_FAILURE_FOOTER)
|
|
216
|
+
return lines.join('\n')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function writeZeroGraphReceipt(
|
|
220
|
+
outDir: string,
|
|
221
|
+
name: string,
|
|
222
|
+
receipt: ZeroGraphReceipt
|
|
223
|
+
): string {
|
|
224
|
+
mkdirSync(outDir, { recursive: true })
|
|
225
|
+
const file = path.join(outDir, `${name}.graph.json`)
|
|
226
|
+
writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`)
|
|
227
|
+
return file
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface ZeroViolationSite {
|
|
231
|
+
file: string
|
|
232
|
+
line: number
|
|
233
|
+
column: number
|
|
234
|
+
rule: ZeroRule
|
|
235
|
+
code: string
|
|
236
|
+
component?: string
|
|
237
|
+
message: string
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* One deterministic order for every integration: normalized file path, source
|
|
242
|
+
* offset, rule, then message. A build that reports its violations in a
|
|
243
|
+
* different order on a different machine is not a receipt anyone can diff.
|
|
244
|
+
*/
|
|
245
|
+
export function sortZeroViolations(
|
|
246
|
+
sites: readonly ZeroViolationSite[]
|
|
247
|
+
): ZeroViolationSite[] {
|
|
248
|
+
return [...sites].sort(
|
|
249
|
+
(left, right) =>
|
|
250
|
+
(left.file < right.file ? -1 : left.file > right.file ? 1 : 0) ||
|
|
251
|
+
left.line - right.line ||
|
|
252
|
+
left.column - right.column ||
|
|
253
|
+
left.rule - right.rule ||
|
|
254
|
+
(left.message < right.message ? -1 : left.message > right.message ? 1 : 0)
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** The compiler collects every violation before failing. */
|
|
259
|
+
export function formatZeroViolations(sites: readonly ZeroViolationSite[]): string {
|
|
260
|
+
const sorted = sortZeroViolations(sites)
|
|
261
|
+
const lines = [
|
|
262
|
+
`[tamagui zero-runtime] build failed with ${sorted.length} violations`,
|
|
263
|
+
'',
|
|
264
|
+
]
|
|
265
|
+
for (const site of sorted) {
|
|
266
|
+
lines.push(`Rule ${site.rule} ${site.code}`)
|
|
267
|
+
lines.push(
|
|
268
|
+
`${site.file}:${site.line}:${site.column}${site.component ? ` ${site.component}` : ''}`
|
|
269
|
+
)
|
|
270
|
+
lines.push(` ${site.message}`)
|
|
271
|
+
lines.push('')
|
|
272
|
+
}
|
|
273
|
+
lines.push(ZERO_FAILURE_FOOTER)
|
|
274
|
+
return lines.join('\n')
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The machine-readable half of the same list. `report` mode writes it and exits
|
|
279
|
+
* successfully; `enforce` writes it and then fails, so the two are comparable.
|
|
280
|
+
*/
|
|
281
|
+
export function writeZeroViolationReport(
|
|
282
|
+
outDir: string,
|
|
283
|
+
name: string,
|
|
284
|
+
report: {
|
|
285
|
+
integration: string
|
|
286
|
+
mode: 'report' | 'enforce'
|
|
287
|
+
violations: readonly ZeroViolationSite[]
|
|
288
|
+
}
|
|
289
|
+
): string {
|
|
290
|
+
mkdirSync(outDir, { recursive: true })
|
|
291
|
+
const file = path.join(outDir, `${name}.violations.json`)
|
|
292
|
+
writeFileSync(
|
|
293
|
+
file,
|
|
294
|
+
`${JSON.stringify(
|
|
295
|
+
{
|
|
296
|
+
integration: report.integration,
|
|
297
|
+
mode: report.mode,
|
|
298
|
+
count: report.violations.length,
|
|
299
|
+
violations: sortZeroViolations(report.violations),
|
|
300
|
+
},
|
|
301
|
+
null,
|
|
302
|
+
2
|
|
303
|
+
)}\n`
|
|
304
|
+
)
|
|
305
|
+
return file
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* An exported `styled()` declarator is only erasable because every importer in
|
|
310
|
+
* this entry graph was itself transformed, and therefore lowered its uses. That
|
|
311
|
+
* is a build-wide fact, so it is checked once here rather than guessed per
|
|
312
|
+
* module.
|
|
313
|
+
*/
|
|
314
|
+
export function erasedExportEscape(input: {
|
|
315
|
+
integration: string
|
|
316
|
+
/** Module ids the zero transform actually ran on. */
|
|
317
|
+
transformed: ReadonlySet<string>
|
|
318
|
+
/** Erased exported binding names, by the module that declared them. */
|
|
319
|
+
erasedExports: ReadonlyMap<string, readonly string[]>
|
|
320
|
+
importersOf: ReadonlyMap<string, readonly string[]>
|
|
321
|
+
}): string | null {
|
|
322
|
+
for (const [moduleId, names] of input.erasedExports) {
|
|
323
|
+
if (names.length === 0) continue
|
|
324
|
+
// An empty importer set here would mean the graph query saw nothing, which
|
|
325
|
+
// is not the same as seeing no importers: this module is in the graph, so
|
|
326
|
+
// its own entry must be there too.
|
|
327
|
+
if (!input.importersOf.has(moduleId)) {
|
|
328
|
+
return `[tamagui zero-runtime] ${input.integration} erased the exported definition(s) ${names.join(
|
|
329
|
+
', '
|
|
330
|
+
)} from ${moduleId}, and then could not find that module in its own graph, so nothing proved the erasure was safe.\n\n${ZERO_FAILURE_FOOTER}`
|
|
331
|
+
}
|
|
332
|
+
const untransformed = (input.importersOf.get(moduleId) ?? []).filter(
|
|
333
|
+
(importer) => !input.transformed.has(importer)
|
|
334
|
+
)
|
|
335
|
+
if (untransformed.length === 0) continue
|
|
336
|
+
return `[tamagui zero-runtime] ${input.integration} erased the exported definition(s) ${names.join(
|
|
337
|
+
', '
|
|
338
|
+
)} from ${moduleId}, but ${untransformed.length} importer(s) in this entry graph were never zero-transformed and may still reference them:\n ${untransformed.join(
|
|
339
|
+
'\n '
|
|
340
|
+
)}\n\n${ZERO_FAILURE_FOOTER}`
|
|
341
|
+
}
|
|
342
|
+
return null
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** UTF-16 offset to 1-based line/column, for diagnostic sites. */
|
|
346
|
+
export function offsetToLineColumn(
|
|
347
|
+
source: string,
|
|
348
|
+
offset: number
|
|
349
|
+
): { line: number; column: number } {
|
|
350
|
+
let line = 1
|
|
351
|
+
let lineStart = 0
|
|
352
|
+
for (let index = 0; index < offset && index < source.length; index++) {
|
|
353
|
+
if (source.charCodeAt(index) === 10) {
|
|
354
|
+
line++
|
|
355
|
+
lineStart = index + 1
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return { line, column: offset - lineStart + 1 }
|
|
359
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LOWERED_MODULE_PLAN_VERSION } from '@tamagui/compiler-core'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
|
|
4
|
+
import type { TamaguiRuntimeLiteral } from './options'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Bumped whenever the zero transform's emitted output changes shape. It rides
|
|
8
|
+
* the lowering plan version so a compiler change also invalidates zero caches.
|
|
9
|
+
*/
|
|
10
|
+
export const ZERO_COMPILER_VERSION = `zero-1/plan-${LOWERED_MODULE_PLAN_VERSION}`
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Every integration records this tuple in its cache and build identity. The CSS
|
|
14
|
+
* path alone is insufficient because the artifact's content can change in place.
|
|
15
|
+
*/
|
|
16
|
+
export interface ZeroArtifactIdentity {
|
|
17
|
+
runtimeLiteral: TamaguiRuntimeLiteral
|
|
18
|
+
target: 'web' | 'native'
|
|
19
|
+
configGeneration: string
|
|
20
|
+
cssHash: string
|
|
21
|
+
compilerVersion: string
|
|
22
|
+
islandEntries: string[]
|
|
23
|
+
bridgeManifestHash: string
|
|
24
|
+
islandOutputHashes: Record<string, string>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function hashZeroIdentity(identity: ZeroArtifactIdentity): string {
|
|
28
|
+
const canonical = {
|
|
29
|
+
runtimeLiteral: identity.runtimeLiteral,
|
|
30
|
+
target: identity.target,
|
|
31
|
+
configGeneration: identity.configGeneration,
|
|
32
|
+
cssHash: identity.cssHash,
|
|
33
|
+
compilerVersion: identity.compilerVersion,
|
|
34
|
+
islandEntries: [...identity.islandEntries].sort(),
|
|
35
|
+
bridgeManifestHash: identity.bridgeManifestHash,
|
|
36
|
+
islandOutputHashes: Object.fromEntries(
|
|
37
|
+
Object.entries(identity.islandOutputHashes).sort(([left], [right]) =>
|
|
38
|
+
left < right ? -1 : left > right ? 1 : 0
|
|
39
|
+
)
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
return createHash('sha256').update(JSON.stringify(canonical)).digest('hex').slice(0, 24)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A key-sorted deep copy.
|
|
47
|
+
*
|
|
48
|
+
* The bridge manifest is compared and hashed across processes and across a
|
|
49
|
+
* persisted cache, and a round trip through JSON reorders object keys. Hashing
|
|
50
|
+
* insertion order makes the same manifest produce two identities depending on
|
|
51
|
+
* whether the build scanned or restored, which is a cache miss that looks like
|
|
52
|
+
* a real change.
|
|
53
|
+
*/
|
|
54
|
+
export function canonicalizeBridgeManifest<T>(manifest: T): T {
|
|
55
|
+
if (Array.isArray(manifest)) {
|
|
56
|
+
return manifest.map((entry) => canonicalizeBridgeManifest(entry)) as T
|
|
57
|
+
}
|
|
58
|
+
if (manifest && typeof manifest === 'object') {
|
|
59
|
+
const sorted: Record<string, unknown> = {}
|
|
60
|
+
for (const key of Object.keys(manifest as Record<string, unknown>).sort()) {
|
|
61
|
+
sorted[key] = canonicalizeBridgeManifest((manifest as Record<string, unknown>)[key])
|
|
62
|
+
}
|
|
63
|
+
return sorted as T
|
|
64
|
+
}
|
|
65
|
+
return manifest
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function hashBridgeManifest(manifest: unknown): string {
|
|
69
|
+
return createHash('sha256')
|
|
70
|
+
.update(JSON.stringify(canonicalizeBridgeManifest(manifest)))
|
|
71
|
+
.digest('hex')
|
|
72
|
+
.slice(0, 16)
|
|
73
|
+
}
|