@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,262 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import type { ZeroIsland } from './options'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Island generation.
|
|
8
|
+
*
|
|
9
|
+
* An island is a full-runtime entry graph built in a separate bundler
|
|
10
|
+
* invocation. The zero graph never imports it; it imports a generated loader
|
|
11
|
+
* that owns the deterministic server placeholder, the shared React handoff, and
|
|
12
|
+
* the async fetch of the separately built island bundle.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const ISLAND_RUNTIME_KEY = '__tamagui_island_runtime__'
|
|
16
|
+
export const ISLAND_REGISTRY_KEY = '__tamagui_islands__'
|
|
17
|
+
|
|
18
|
+
/** Block 3's normalized internal representation, produced at compile time. */
|
|
19
|
+
export interface IslandThemeBridgeLayer {
|
|
20
|
+
inlineValues: {
|
|
21
|
+
values: Record<string, string | number>
|
|
22
|
+
themes?: Record<string, Record<string, string | number>>
|
|
23
|
+
}
|
|
24
|
+
inlineClassName: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface IslandThemeBridge {
|
|
28
|
+
id: string
|
|
29
|
+
name: string
|
|
30
|
+
layers: IslandThemeBridgeLayer[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IslandGenerationInput {
|
|
34
|
+
island: ZeroIsland
|
|
35
|
+
/** Names the integration in the loader's recovery diagnostic. */
|
|
36
|
+
integration: string
|
|
37
|
+
/** Absolute path of the app's tamagui config module. */
|
|
38
|
+
configPath: string
|
|
39
|
+
/** URL the loader fetches the built island bundle from. */
|
|
40
|
+
scriptUrl: string
|
|
41
|
+
/** URL of the one generated CSS artifact both entries load. */
|
|
42
|
+
cssHref: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function writeIfChanged(file: string, content: string) {
|
|
46
|
+
mkdirSync(path.dirname(file), { recursive: true })
|
|
47
|
+
let existing: string | null = null
|
|
48
|
+
try {
|
|
49
|
+
existing = readFileSync(file, 'utf8')
|
|
50
|
+
} catch {
|
|
51
|
+
existing = null
|
|
52
|
+
}
|
|
53
|
+
if (existing !== content) writeFileSync(file, content)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function importPath(from: string, to: string) {
|
|
57
|
+
const relative = path.relative(path.dirname(from), to).replace(/\\/g, '/')
|
|
58
|
+
return relative.startsWith('.') ? relative : `./${relative}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function generateIslandLoaderSource(input: IslandGenerationInput): string {
|
|
62
|
+
return `// generated by @tamagui/static zero-runtime. do not edit.
|
|
63
|
+
import * as React from 'react'
|
|
64
|
+
import * as ReactDOM from 'react-dom'
|
|
65
|
+
import * as JSXRuntime from 'react/jsx-runtime'
|
|
66
|
+
|
|
67
|
+
const ISLAND_ID = ${JSON.stringify(input.island.id)}
|
|
68
|
+
const ISLAND_SRC = ${JSON.stringify(input.scriptUrl)}
|
|
69
|
+
const CSS_HREF = ${JSON.stringify(input.cssHref)}
|
|
70
|
+
const INTEGRATION = ${JSON.stringify(input.integration)}
|
|
71
|
+
|
|
72
|
+
function sharedRuntime() {
|
|
73
|
+
const scope = globalThis
|
|
74
|
+
if (!scope[${JSON.stringify(ISLAND_RUNTIME_KEY)}]) {
|
|
75
|
+
scope[${JSON.stringify(ISLAND_RUNTIME_KEY)}] = {
|
|
76
|
+
react: React,
|
|
77
|
+
reactDom: ReactDOM,
|
|
78
|
+
jsxRuntime: JSXRuntime,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}]) {
|
|
82
|
+
scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}] = {}
|
|
83
|
+
}
|
|
84
|
+
return scope
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The one accepted runtime recovery in zero-runtime mode, and it is loud on
|
|
88
|
+
// purpose. "Unimported" is not decidable at build time for a published zero
|
|
89
|
+
// artifact on Next or Metro, so the loader still guarantees the island has its
|
|
90
|
+
// styles; on a correct build this never runs.
|
|
91
|
+
function ensureZeroStylesheet() {
|
|
92
|
+
const links = document.querySelectorAll('link[rel="stylesheet"]')
|
|
93
|
+
for (let index = 0; index < links.length; index++) {
|
|
94
|
+
const href = links[index].getAttribute('href') || ''
|
|
95
|
+
if (href === CSS_HREF || href.split('?')[0].endsWith(CSS_HREF)) return
|
|
96
|
+
}
|
|
97
|
+
console.error(
|
|
98
|
+
'[tamagui zero-runtime] the page did not link the generated CSS artifact ' +
|
|
99
|
+
CSS_HREF +
|
|
100
|
+
', so island "' +
|
|
101
|
+
ISLAND_ID +
|
|
102
|
+
'" (' +
|
|
103
|
+
INTEGRATION +
|
|
104
|
+
') is loading it at runtime. On a correct build this never happens: the ' +
|
|
105
|
+
'artifact is generated by the build and must be linked by the document ' +
|
|
106
|
+
'that mounts the island. Fix the document, not this recovery.'
|
|
107
|
+
)
|
|
108
|
+
const link = document.createElement('link')
|
|
109
|
+
link.rel = 'stylesheet'
|
|
110
|
+
link.href = CSS_HREF
|
|
111
|
+
link.setAttribute('data-tamagui-zero-css', '1')
|
|
112
|
+
document.head.appendChild(link)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let pending = null
|
|
116
|
+
|
|
117
|
+
function loadIsland() {
|
|
118
|
+
const scope = sharedRuntime()
|
|
119
|
+
const registered = scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}][ISLAND_ID]
|
|
120
|
+
if (registered) return Promise.resolve(registered)
|
|
121
|
+
if (!pending) {
|
|
122
|
+
pending = new Promise((resolve, reject) => {
|
|
123
|
+
ensureZeroStylesheet()
|
|
124
|
+
const script = document.createElement('script')
|
|
125
|
+
script.src = ISLAND_SRC
|
|
126
|
+
script.async = true
|
|
127
|
+
script.onload = () => {
|
|
128
|
+
const mod = scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}][ISLAND_ID]
|
|
129
|
+
if (mod) resolve(mod)
|
|
130
|
+
else reject(new Error('tamagui island ' + ISLAND_ID + ' did not register'))
|
|
131
|
+
}
|
|
132
|
+
script.onerror = () => reject(new Error('tamagui island ' + ISLAND_ID + ' failed to load ' + ISLAND_SRC))
|
|
133
|
+
document.head.appendChild(script)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
return pending
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default function TamaguiIsland(props) {
|
|
140
|
+
const bridgeId = props['data-tamagui-bridge'] || 'root'
|
|
141
|
+
const [Loaded, setLoaded] = React.useState(null)
|
|
142
|
+
|
|
143
|
+
React.useEffect(() => {
|
|
144
|
+
let cancelled = false
|
|
145
|
+
loadIsland().then((mod) => {
|
|
146
|
+
if (!cancelled) setLoaded(() => mod.Island)
|
|
147
|
+
})
|
|
148
|
+
return () => {
|
|
149
|
+
cancelled = true
|
|
150
|
+
}
|
|
151
|
+
}, [])
|
|
152
|
+
|
|
153
|
+
if (!Loaded) {
|
|
154
|
+
return React.createElement('div', {
|
|
155
|
+
'data-tamagui-island': ISLAND_ID,
|
|
156
|
+
'data-tamagui-bridge': bridgeId,
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const forwarded = {}
|
|
161
|
+
for (const key in props) forwarded[key] = props[key]
|
|
162
|
+
forwarded['data-tamagui-island'] = ISLAND_ID
|
|
163
|
+
return React.createElement(Loaded, forwarded)
|
|
164
|
+
}
|
|
165
|
+
`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function generateIslandEntrySource(input: IslandGenerationInput): string {
|
|
169
|
+
const entry = input.island.entry
|
|
170
|
+
return `// generated by @tamagui/static zero-runtime. do not edit.
|
|
171
|
+
import * as React from 'react'
|
|
172
|
+
import { TamaguiProvider, Theme } from 'tamagui'
|
|
173
|
+
import config from ${JSON.stringify(importPath(entry, input.configPath))}
|
|
174
|
+
import IslandRoot from ${JSON.stringify(importPath(entry, input.island.module))}
|
|
175
|
+
|
|
176
|
+
export function Island(props) {
|
|
177
|
+
const bridge = props.__tamaguiBridge
|
|
178
|
+
const inner = {}
|
|
179
|
+
for (const key in props) {
|
|
180
|
+
if (key !== '__tamaguiBridge') inner[key] = props[key]
|
|
181
|
+
}
|
|
182
|
+
let node = React.createElement(IslandRoot, inner)
|
|
183
|
+
const layers = (bridge && bridge.layers) || []
|
|
184
|
+
for (let index = layers.length - 1; index >= 0; index--) {
|
|
185
|
+
node = React.createElement(
|
|
186
|
+
Theme,
|
|
187
|
+
{
|
|
188
|
+
inlineValues: layers[index].inlineValues,
|
|
189
|
+
inlineClassName: layers[index].inlineClassName,
|
|
190
|
+
forceClassName: true,
|
|
191
|
+
},
|
|
192
|
+
node
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
// isSubtreeRoot: the island shares a document with a zero page that already
|
|
196
|
+
// has its own compiled theme spans, so its provider must not write a theme
|
|
197
|
+
// class onto html or body and must carry its own theme on its own node.
|
|
198
|
+
return React.createElement(
|
|
199
|
+
TamaguiProvider,
|
|
200
|
+
{
|
|
201
|
+
config,
|
|
202
|
+
defaultTheme: (bridge && bridge.name) || 'light',
|
|
203
|
+
disableInjectCSS: true,
|
|
204
|
+
isSubtreeRoot: true,
|
|
205
|
+
},
|
|
206
|
+
node
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const scope = globalThis
|
|
211
|
+
if (!scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}]) scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}] = {}
|
|
212
|
+
scope[${JSON.stringify(ISLAND_REGISTRY_KEY)}][${JSON.stringify(input.island.id)}] = { Island }
|
|
213
|
+
`
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function writeIslandModules(input: IslandGenerationInput): void {
|
|
217
|
+
writeIfChanged(input.island.loader, generateIslandLoaderSource(input))
|
|
218
|
+
writeIfChanged(input.island.entry, generateIslandEntrySource(input))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The island child build externalizes React to the handoff the generated loader
|
|
223
|
+
* publishes, so one React instance serves both graphs.
|
|
224
|
+
*
|
|
225
|
+
* Property paths are the source of truth: webpack needs the segments (a dotted
|
|
226
|
+
* string becomes one literal property name), rollup needs the joined expression.
|
|
227
|
+
*/
|
|
228
|
+
export const ISLAND_EXTERNAL_GLOBAL_PATHS: Record<string, string[]> = {
|
|
229
|
+
react: [ISLAND_RUNTIME_KEY, 'react'],
|
|
230
|
+
'react-dom': [ISLAND_RUNTIME_KEY, 'reactDom'],
|
|
231
|
+
'react/jsx-runtime': [ISLAND_RUNTIME_KEY, 'jsxRuntime'],
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const ISLAND_EXTERNAL_GLOBALS: Record<string, string> = Object.fromEntries(
|
|
235
|
+
Object.entries(ISLAND_EXTERNAL_GLOBAL_PATHS).map(([name, segments]) => [
|
|
236
|
+
name,
|
|
237
|
+
`globalThis.${segments.join('.')}`,
|
|
238
|
+
])
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Merges one module's bridge descriptors into the build-wide manifest.
|
|
243
|
+
*
|
|
244
|
+
* A module is transformed once per compilation (webpack builds a server and a
|
|
245
|
+
* client pass over the same source), so a bridge id must replace its previous
|
|
246
|
+
* record rather than append a duplicate.
|
|
247
|
+
*/
|
|
248
|
+
export function mergeIslandBridges(
|
|
249
|
+
target: Map<string, IslandThemeBridge[]>,
|
|
250
|
+
incoming: Map<string, IslandThemeBridge[]>
|
|
251
|
+
): void {
|
|
252
|
+
for (const [islandId, bridges] of incoming) {
|
|
253
|
+
const list = target.get(islandId) ?? []
|
|
254
|
+
for (const bridge of bridges) {
|
|
255
|
+
const index = list.findIndex((existing) => existing.id === bridge.id)
|
|
256
|
+
if (index >= 0) list[index] = bridge
|
|
257
|
+
else list.push(bridge)
|
|
258
|
+
}
|
|
259
|
+
list.sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0))
|
|
260
|
+
target.set(islandId, list)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { zeroConfigDriverMessage } from '@tamagui/compiler-core'
|
|
2
|
+
import type { TamaguiInternalConfig } from '@tamagui/web'
|
|
3
|
+
import glob from 'fast-glob'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
import type { TamaguiOptions } from '../types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `TAMAGUI_RUNTIME` has exactly two integration-owned literal values. The public
|
|
10
|
+
* `experimental.zeroRuntime` option is the author input; this literal is
|
|
11
|
+
* generated output, so an ambient shell value never reaches a build.
|
|
12
|
+
*/
|
|
13
|
+
export type TamaguiRuntimeLiteral = 'full' | 'zero'
|
|
14
|
+
|
|
15
|
+
export type ZeroRuntimeMode = 'off' | 'report' | 'enforce'
|
|
16
|
+
|
|
17
|
+
export interface ZeroIsland {
|
|
18
|
+
/** Stable, deterministic id derived from the island module's root-relative path. */
|
|
19
|
+
id: string
|
|
20
|
+
/** Absolute path of the island root module. */
|
|
21
|
+
module: string
|
|
22
|
+
/** Absolute path of the generated loader the zero graph imports. */
|
|
23
|
+
loader: string
|
|
24
|
+
/** Absolute path of the generated full-runtime entry. */
|
|
25
|
+
entry: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ZeroRuntimeResolved {
|
|
29
|
+
mode: ZeroRuntimeMode
|
|
30
|
+
/** The project being built. The graph gate excludes its own package. */
|
|
31
|
+
root: string
|
|
32
|
+
islandGlobs: string[]
|
|
33
|
+
islands: ZeroIsland[]
|
|
34
|
+
/** Absolute directory holding generated loaders, entries, and receipts. */
|
|
35
|
+
outDir: string
|
|
36
|
+
/** Absolute path of the single generated CSS artifact. */
|
|
37
|
+
cssPath: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// under the project's existing generated-output directory, which the repository
|
|
41
|
+
// already excludes from git and from the formatter
|
|
42
|
+
export const ZERO_OUT_DIRNAME = '.tamagui/zero'
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Island id is the module's basename, which is also the generated loader's
|
|
46
|
+
* filename. Two declared islands may not share a basename.
|
|
47
|
+
*/
|
|
48
|
+
export function islandIdFor(moduleId: string): string {
|
|
49
|
+
return path.basename(moduleId).replace(/\.[jt]sx?$/, '')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type ZeroRuntimeInput = Pick<TamaguiOptions, 'experimental' | 'outputCSS' | 'platform'>
|
|
53
|
+
|
|
54
|
+
export async function resolveZeroRuntime(
|
|
55
|
+
options: ZeroRuntimeInput,
|
|
56
|
+
root: string
|
|
57
|
+
): Promise<ZeroRuntimeResolved> {
|
|
58
|
+
const early = resolveZeroRuntimeEarly(options, root)
|
|
59
|
+
if (early.done) return early.resolved
|
|
60
|
+
return finishZeroRuntime(
|
|
61
|
+
options,
|
|
62
|
+
root,
|
|
63
|
+
early.islandGlobs,
|
|
64
|
+
early.islandGlobs.length
|
|
65
|
+
? await glob(early.islandGlobs, { cwd: root, absolute: true, dot: false })
|
|
66
|
+
: []
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The webpack adapter configures itself synchronously, before any hook runs. */
|
|
71
|
+
export function resolveZeroRuntimeSync(
|
|
72
|
+
options: ZeroRuntimeInput,
|
|
73
|
+
root: string
|
|
74
|
+
): ZeroRuntimeResolved {
|
|
75
|
+
const early = resolveZeroRuntimeEarly(options, root)
|
|
76
|
+
if (early.done) return early.resolved
|
|
77
|
+
return finishZeroRuntime(
|
|
78
|
+
options,
|
|
79
|
+
root,
|
|
80
|
+
early.islandGlobs,
|
|
81
|
+
early.islandGlobs.length
|
|
82
|
+
? glob.sync(early.islandGlobs, { cwd: root, absolute: true, dot: false })
|
|
83
|
+
: []
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveZeroRuntimeEarly(
|
|
88
|
+
options: ZeroRuntimeInput,
|
|
89
|
+
root: string
|
|
90
|
+
):
|
|
91
|
+
| { done: true; resolved: ZeroRuntimeResolved }
|
|
92
|
+
| { done: false; islandGlobs: string[] } {
|
|
93
|
+
const requested = options.experimental?.zeroRuntime
|
|
94
|
+
const outDir = path.join(root, ZERO_OUT_DIRNAME)
|
|
95
|
+
const off: ZeroRuntimeResolved = {
|
|
96
|
+
mode: 'off',
|
|
97
|
+
root,
|
|
98
|
+
islandGlobs: [],
|
|
99
|
+
islands: [],
|
|
100
|
+
outDir,
|
|
101
|
+
cssPath: '',
|
|
102
|
+
}
|
|
103
|
+
if (!requested) return { done: true, resolved: off }
|
|
104
|
+
|
|
105
|
+
if (options.platform === 'native' && requested === 'report') {
|
|
106
|
+
return { done: true, resolved: { ...off, mode: 'report' } }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.platform === 'native') {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`[tamagui zero-runtime] experimental.zeroRuntime is web only. A native build always receives TAMAGUI_RUNTIME="full".`
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// `report` keeps the full runtime, so it owns no generated artifact and needs
|
|
116
|
+
// no outputCSS. It still runs every per-module analysis.
|
|
117
|
+
if (requested !== 'report' && !options.outputCSS) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`[tamagui zero-runtime] experimental.zeroRuntime requires outputCSS. The bundler owns one generated CSS artifact and derives TAMAGUI_DID_OUTPUT_CSS from it.`
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (requested !== 'report' && process.env.TAMAGUI_DOES_SSR_CSS === 'mutates-themes') {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`[tamagui zero-runtime] Rule 4: TAMAGUI_DOES_SSR_CSS="mutates-themes" declares runtime theme mutation. Zero-runtime themes are build-time data. Remove runtime mutation or move that surface to a full-runtime island.`
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
done: false,
|
|
131
|
+
islandGlobs: requested === true || requested === 'report' ? [] : requested.islands,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function finishZeroRuntime(
|
|
136
|
+
options: ZeroRuntimeInput,
|
|
137
|
+
root: string,
|
|
138
|
+
islandGlobs: string[],
|
|
139
|
+
matches: string[]
|
|
140
|
+
): ZeroRuntimeResolved {
|
|
141
|
+
const outDir = path.join(root, ZERO_OUT_DIRNAME)
|
|
142
|
+
// resolveZeroRuntimeEarly already rejected a missing outputCSS in enforce mode
|
|
143
|
+
const outputCSS = (options.outputCSS ?? '') as string
|
|
144
|
+
const islands = [...new Set(matches.map((match) => path.normalize(match)))]
|
|
145
|
+
.sort()
|
|
146
|
+
.map((module): ZeroIsland => {
|
|
147
|
+
const id = islandIdFor(module)
|
|
148
|
+
return {
|
|
149
|
+
id,
|
|
150
|
+
module,
|
|
151
|
+
loader: path.join(outDir, `${id}.loader.js`),
|
|
152
|
+
entry: path.join(outDir, `${id}.entry.js`),
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const seen = new Set<string>()
|
|
157
|
+
for (const island of islands) {
|
|
158
|
+
if (seen.has(island.id)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`[tamagui zero-runtime] two declared islands share the basename "${island.id}". Island ids are their module basename because that is also the generated loader's filename.`
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
seen.add(island.id)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (islandGlobs.length && islands.length === 0) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`[tamagui zero-runtime] experimental.zeroRuntime.islands matched no module for ${JSON.stringify(
|
|
169
|
+
islandGlobs
|
|
170
|
+
)}`
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
mode: options.experimental?.zeroRuntime === 'report' ? 'report' : 'enforce',
|
|
176
|
+
root,
|
|
177
|
+
islandGlobs,
|
|
178
|
+
islands,
|
|
179
|
+
outDir,
|
|
180
|
+
cssPath: !outputCSS
|
|
181
|
+
? ''
|
|
182
|
+
: path.isAbsolute(outputCSS)
|
|
183
|
+
? outputCSS
|
|
184
|
+
: path.resolve(root, outputCSS),
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Rule 5 at config level. A non-CSS driver in the zero entry's own config means
|
|
190
|
+
* every animated component in that graph needs a component animation runtime,
|
|
191
|
+
* which is exactly what the mode removes.
|
|
192
|
+
*/
|
|
193
|
+
export function assertZeroConfigDrivers(config: TamaguiInternalConfig): void {
|
|
194
|
+
// `animationDrivers` is set only for a multi-driver config; a single-driver
|
|
195
|
+
// config resolves to `animations`, and reading only the map would make this
|
|
196
|
+
// check silently unable to fail for the ordinary shape
|
|
197
|
+
const drivers: [string, { outputStyle?: unknown } | undefined][] = (
|
|
198
|
+
config.animationDrivers
|
|
199
|
+
? Object.entries(config.animationDrivers)
|
|
200
|
+
: [['default', config.animations]]
|
|
201
|
+
) as [string, { outputStyle?: unknown } | undefined][]
|
|
202
|
+
for (const [name, driver] of drivers) {
|
|
203
|
+
if (!driver || driver.outputStyle === 'css') continue
|
|
204
|
+
throw new Error(zeroConfigDriverMessage(name, driver.outputStyle))
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type ZeroIntegration = 'vite' | 'next-webpack' | 'metro-web'
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Base and island support are enabled per integration after that integration
|
|
212
|
+
* passes its own receipts. One lagging integration never blocks another.
|
|
213
|
+
*/
|
|
214
|
+
export const ZERO_INTEGRATION_SUPPORT: Record<
|
|
215
|
+
ZeroIntegration,
|
|
216
|
+
{ base: boolean; islands: boolean }
|
|
217
|
+
> = {
|
|
218
|
+
vite: { base: true, islands: true },
|
|
219
|
+
'next-webpack': { base: true, islands: true },
|
|
220
|
+
'metro-web': { base: true, islands: true },
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function assertZeroIntegrationSupport(
|
|
224
|
+
integration: ZeroIntegration,
|
|
225
|
+
resolved: ZeroRuntimeResolved
|
|
226
|
+
): void {
|
|
227
|
+
if (resolved.mode !== 'enforce') return
|
|
228
|
+
const support = ZERO_INTEGRATION_SUPPORT[integration]
|
|
229
|
+
if (!support.base) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`[tamagui zero-runtime] ${integration} has not qualified for zero-runtime output in this version. Use zeroRuntime: "report" or choose a qualified integration.`
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
if (resolved.islands.length && !support.islands) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`[tamagui zero-runtime] ${integration} does not support experimental.zeroRuntime islands in this version. Use zeroRuntime: true without islands or choose an integration with island support.`
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
import type { TamaguiOptions } from '../types'
|
|
6
|
+
import { resolveZeroRuntimeSync } from './options'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Artifact ownership, shared by every integration and by both CSS tiers.
|
|
10
|
+
*
|
|
11
|
+
* A build may claim `TAMAGUI_DID_OUTPUT_CSS='1'` only after the artifact that
|
|
12
|
+
* replaces the stripped JavaScript rules is proven to exist, to match the CSS
|
|
13
|
+
* this build's config generates, and to be loaded by the entry graph. Those are
|
|
14
|
+
* three separate failures with three separate causes, so they are three
|
|
15
|
+
* separate diagnostics.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type GlobalCSSFailureKind = 'missing' | 'stale' | 'unimported'
|
|
19
|
+
|
|
20
|
+
export interface GlobalCSSFailure {
|
|
21
|
+
kind: GlobalCSSFailureKind
|
|
22
|
+
cssPath: string
|
|
23
|
+
message: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const hashCSS = (css: string) =>
|
|
27
|
+
createHash('sha256').update(css).digest('hex').slice(0, 16)
|
|
28
|
+
|
|
29
|
+
const normalize = (value: string) => value.split(/[?#]/, 1)[0]!.replace(/\\/g, '/')
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `TAMAGUI_DOES_SSR_CSS='mutates-themes'` declares that themes are mutated at
|
|
33
|
+
* runtime, which is the one declaration that keeps the runtime theme generator
|
|
34
|
+
* alive. It therefore blocks the compiled-global-CSS claim outright.
|
|
35
|
+
*/
|
|
36
|
+
export const declaresRuntimeThemeMutation = () =>
|
|
37
|
+
process.env.TAMAGUI_DOES_SSR_CSS === 'mutates-themes'
|
|
38
|
+
|
|
39
|
+
export interface GlobalCSSOwnership {
|
|
40
|
+
/** Absolute path of the artifact this build owns. */
|
|
41
|
+
cssPath: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The compiled-global-CSS tier for one web build, or null when the build is not
|
|
46
|
+
* in that tier.
|
|
47
|
+
*
|
|
48
|
+
* Zero mode owns its own combined artifact and derives the flag through
|
|
49
|
+
* `ZeroCSSArtifact`, so it is deliberately excluded here: one tier, one owner.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveGlobalCSSOwnership(
|
|
52
|
+
options: Pick<TamaguiOptions, 'experimental' | 'outputCSS' | 'platform' | 'disable'>,
|
|
53
|
+
root: string
|
|
54
|
+
): GlobalCSSOwnership | null {
|
|
55
|
+
if (options.disable) return null
|
|
56
|
+
if (options.platform === 'native') return null
|
|
57
|
+
if (!options.outputCSS) return null
|
|
58
|
+
if (declaresRuntimeThemeMutation()) return null
|
|
59
|
+
if (resolveZeroRuntimeSync(options, root).mode === 'enforce') return null
|
|
60
|
+
return {
|
|
61
|
+
cssPath: path.isAbsolute(options.outputCSS)
|
|
62
|
+
? options.outputCSS
|
|
63
|
+
: path.resolve(root, options.outputCSS),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The three ways the stripping fact and its replacement asset can diverge.
|
|
69
|
+
*
|
|
70
|
+
* `loadedModuleIds` is the set of module ids the entry graph actually shipped.
|
|
71
|
+
* An integration that publishes the artifact as a static file rather than as a
|
|
72
|
+
* graph module passes the published copy's path instead, which is the same
|
|
73
|
+
* question asked of a different transport.
|
|
74
|
+
*/
|
|
75
|
+
export function checkGlobalCSSArtifact(input: {
|
|
76
|
+
cssPath: string
|
|
77
|
+
expectedCSS: string
|
|
78
|
+
loadedModuleIds: Iterable<string>
|
|
79
|
+
/** How the entry is expected to reach the artifact, printed on failure. */
|
|
80
|
+
importHint: string
|
|
81
|
+
}): GlobalCSSFailure | null {
|
|
82
|
+
const { cssPath, expectedCSS } = input
|
|
83
|
+
let onDisk: string
|
|
84
|
+
try {
|
|
85
|
+
onDisk = readFileSync(cssPath, 'utf8')
|
|
86
|
+
} catch {
|
|
87
|
+
return {
|
|
88
|
+
kind: 'missing',
|
|
89
|
+
cssPath,
|
|
90
|
+
message: `[tamagui] the generated CSS artifact ${cssPath} does not exist, so this build cannot claim TAMAGUI_DID_OUTPUT_CSS. The design-system, :root, font and theme rules would be stripped from JavaScript and put nowhere.`,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (onDisk !== expectedCSS) {
|
|
94
|
+
return {
|
|
95
|
+
kind: 'stale',
|
|
96
|
+
cssPath,
|
|
97
|
+
message: `[tamagui] the generated CSS artifact ${cssPath} is stale: it hashes ${hashCSS(
|
|
98
|
+
onDisk
|
|
99
|
+
)} but this build's config generates ${hashCSS(
|
|
100
|
+
expectedCSS
|
|
101
|
+
)}. Deriving TAMAGUI_DID_OUTPUT_CSS from it would ship rules that do not match the build.`,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const wanted = normalize(cssPath)
|
|
105
|
+
for (const id of input.loadedModuleIds) {
|
|
106
|
+
if (normalize(id) === wanted) return null
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
kind: 'unimported',
|
|
110
|
+
cssPath,
|
|
111
|
+
message: `[tamagui] the generated CSS artifact ${cssPath} exists but the entry graph never loads it. ${input.importHint}`,
|
|
112
|
+
}
|
|
113
|
+
}
|