@tamagui/vite-plugin 3.0.0-beta.922.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/loadTamagui.cjs +55 -9
- package/dist/cjs/plugin.cjs +75 -18
- package/dist/cjs/zeroRuntime.cjs +0 -1
- package/dist/esm/loadTamagui.mjs +54 -9
- package/dist/esm/loadTamagui.mjs.map +1 -1
- package/dist/esm/plugin.mjs +77 -20
- package/dist/esm/plugin.mjs.map +1 -1
- package/dist/esm/zeroRuntime.mjs +0 -1
- package/dist/esm/zeroRuntime.mjs.map +1 -1
- package/package.json +10 -9
- package/src/loadTamagui.ts +90 -14
- package/src/plugin.ts +160 -33
- package/src/zeroRuntime.ts +2 -2
- package/types/loadTamagui.d.ts +17 -1
- package/types/loadTamagui.d.ts.map +1 -1
- package/types/plugin.d.ts +1 -1
- package/types/plugin.d.ts.map +1 -1
- package/types/zeroRuntime.d.ts +1 -1
package/src/loadTamagui.ts
CHANGED
|
@@ -3,10 +3,39 @@ import type { TamaguiProjectInfo } from '@tamagui/static'
|
|
|
3
3
|
import type { TamaguiOptions } from '@tamagui/types'
|
|
4
4
|
import { createRequire } from 'node:module'
|
|
5
5
|
import path from 'node:path'
|
|
6
|
-
import type
|
|
6
|
+
import { runnerImport, type RunnableDevEnvironment } from 'vite'
|
|
7
7
|
|
|
8
8
|
export const TAMAGUI_EVALUATION_ENVIRONMENT = 'tamagui'
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate tamagui.build.ts through Vite instead of esbuild.
|
|
12
|
+
*
|
|
13
|
+
* The esbuild path spawns a long-lived service process, and that process does
|
|
14
|
+
* not survive a Vite dev server restart: the next `tamaguiPlugin()` call, which
|
|
15
|
+
* happens while Vite is rebuilding its config, hits `write EPIPE` and takes the
|
|
16
|
+
* restart down with it. Vite is already running here and already evaluates
|
|
17
|
+
* tamagui.config.ts and every component package through its own module runner,
|
|
18
|
+
* so the build file has no reason to need a second toolchain.
|
|
19
|
+
*
|
|
20
|
+
* `runnerImport` forces `configFile: false`, so loading the build file from
|
|
21
|
+
* inside a plugin that the config file itself creates cannot recurse. Bare
|
|
22
|
+
* imports stay external, matching esbuild's `packages: 'external'`, and the
|
|
23
|
+
* returned dependency list replaces the metafile's inputs for watching.
|
|
24
|
+
*/
|
|
25
|
+
export const viteBuildConfigLoader: Static.BuildConfigLoader = async (
|
|
26
|
+
absolutePath,
|
|
27
|
+
root
|
|
28
|
+
) => {
|
|
29
|
+
const { module, dependencies } = await runnerImport<Record<string, unknown>>(
|
|
30
|
+
absolutePath,
|
|
31
|
+
{ root }
|
|
32
|
+
)
|
|
33
|
+
// runnerImport reports what the entry pulled in but not the entry itself,
|
|
34
|
+
// where esbuild's metafile listed it. the build file is the one thing that
|
|
35
|
+
// must invalidate the config, so put it back.
|
|
36
|
+
return { exports: module, dependencies: [absolutePath, ...dependencies] }
|
|
37
|
+
}
|
|
38
|
+
|
|
10
39
|
const requireFromLoader = createRequire(
|
|
11
40
|
typeof __filename === 'string' ? __filename : import.meta.url
|
|
12
41
|
)
|
|
@@ -59,6 +88,8 @@ export function createViteTamaguiLoader(
|
|
|
59
88
|
let loadPromise: Promise<TamaguiOptions> | null = null
|
|
60
89
|
let loadedOptions: TamaguiOptions | null = null
|
|
61
90
|
let projectPromise: Promise<Static.CompilerProject> | null = null
|
|
91
|
+
let loadingProject: Promise<Static.CompilerProject> | null = null
|
|
92
|
+
const invalidatedFiles = new Set<string>()
|
|
62
93
|
const evaluationDependencies = new Set<string>()
|
|
63
94
|
const stampSources = new Set<string>()
|
|
64
95
|
let generation = 0
|
|
@@ -66,6 +97,7 @@ export function createViteTamaguiLoader(
|
|
|
66
97
|
const normalizeDependency = (id: string) => id.split('?')[0]
|
|
67
98
|
|
|
68
99
|
const captureEvaluationDependencies = (modules: ResolvedEvaluationModule[]) => {
|
|
100
|
+
evaluationDependencies.clear()
|
|
69
101
|
stampSources.clear()
|
|
70
102
|
for (const { id } of modules) {
|
|
71
103
|
const dependency = normalizeDependency(id)
|
|
@@ -93,15 +125,25 @@ export function createViteTamaguiLoader(
|
|
|
93
125
|
if (loadedOptions) return loadedOptions
|
|
94
126
|
if (loadPromise) return loadPromise
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
...optionsIn,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
const pending = Static.loadTamaguiBuildConfigAsync(
|
|
129
|
+
{ ...optionsIn, platform: 'web' },
|
|
130
|
+
viteBuildConfigLoader
|
|
131
|
+
).then(
|
|
132
|
+
(options) => {
|
|
133
|
+
loadedOptions = options
|
|
134
|
+
return options
|
|
135
|
+
},
|
|
136
|
+
(error) => {
|
|
137
|
+
// a build file that failed to evaluate is usually mid-edit. dropping the
|
|
138
|
+
// promise lets the next caller try again instead of every later load in
|
|
139
|
+
// this process replaying the first failure.
|
|
140
|
+
if (loadPromise === pending) loadPromise = null
|
|
141
|
+
throw error
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
loadPromise = pending
|
|
103
145
|
|
|
104
|
-
return
|
|
146
|
+
return pending
|
|
105
147
|
}
|
|
106
148
|
|
|
107
149
|
const resolveAndImport = async (
|
|
@@ -172,19 +214,41 @@ export function createViteTamaguiLoader(
|
|
|
172
214
|
): Promise<Static.CompilerProject> => {
|
|
173
215
|
if (projectPromise) return projectPromise
|
|
174
216
|
|
|
217
|
+
const previous = loadingProject
|
|
218
|
+
const projectGeneration = generation
|
|
219
|
+
const changedFiles = [...invalidatedFiles]
|
|
220
|
+
invalidatedFiles.clear()
|
|
175
221
|
projectPromise = (async () => {
|
|
222
|
+
// evaluations and their artifact writes have one owner across generations.
|
|
223
|
+
// a repaired edit runs after the failed generation has finished reporting.
|
|
224
|
+
try {
|
|
225
|
+
await previous
|
|
226
|
+
} catch {}
|
|
176
227
|
if (!environment) {
|
|
177
228
|
throw new Error(
|
|
178
229
|
`Cannot load Tamagui without the ${TAMAGUI_EVALUATION_ENVIRONMENT} Vite environment`
|
|
179
230
|
)
|
|
180
231
|
}
|
|
181
232
|
|
|
233
|
+
for (const file of changedFiles) {
|
|
234
|
+
const evaluated = environment.runner.evaluatedModules
|
|
235
|
+
const affected = new Set(evaluated.getModulesByFile(file))
|
|
236
|
+
// invalidate importers, retaining unrelated modules and their singletons.
|
|
237
|
+
for (const module of affected) {
|
|
238
|
+
for (const importer of module.importers) {
|
|
239
|
+
const parent = evaluated.getModuleById(importer)
|
|
240
|
+
if (parent) affected.add(parent)
|
|
241
|
+
}
|
|
242
|
+
evaluated.invalidateModule(module)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
182
246
|
let evaluated: EvaluatedProjectModules | null = null
|
|
183
247
|
return Static.loadCompilerProject({
|
|
184
248
|
root: environment.config.root,
|
|
185
249
|
target: 'web',
|
|
186
250
|
options,
|
|
187
|
-
generation: `vite:${
|
|
251
|
+
generation: `vite:${projectGeneration}`,
|
|
188
252
|
hostVersions: vitePluginVersions,
|
|
189
253
|
async load(normalizedOptions) {
|
|
190
254
|
evaluated = await evaluateProjectModules(normalizedOptions)
|
|
@@ -213,7 +277,16 @@ export function createViteTamaguiLoader(
|
|
|
213
277
|
})
|
|
214
278
|
})()
|
|
215
279
|
|
|
216
|
-
|
|
280
|
+
loadingProject = projectPromise
|
|
281
|
+
const pending = projectPromise
|
|
282
|
+
try {
|
|
283
|
+
return await pending
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (projectPromise === pending) projectPromise = null
|
|
286
|
+
throw error
|
|
287
|
+
} finally {
|
|
288
|
+
if (loadingProject === pending) loadingProject = null
|
|
289
|
+
}
|
|
217
290
|
}
|
|
218
291
|
|
|
219
292
|
return {
|
|
@@ -256,9 +329,7 @@ export function createViteTamaguiLoader(
|
|
|
256
329
|
},
|
|
257
330
|
|
|
258
331
|
invalidate(file?: string) {
|
|
259
|
-
if (file
|
|
260
|
-
environment.runner.clearCache()
|
|
261
|
-
}
|
|
332
|
+
if (file) invalidatedFiles.add(normalizeDependency(file))
|
|
262
333
|
generation++
|
|
263
334
|
projectPromise = null
|
|
264
335
|
},
|
|
@@ -273,6 +344,7 @@ export function createViteTamaguiLoader(
|
|
|
273
344
|
|
|
274
345
|
async cleanup() {
|
|
275
346
|
try {
|
|
347
|
+
await loadingProject?.catch(() => {})
|
|
276
348
|
if (ownsEnvironment && environment) {
|
|
277
349
|
await environment.close()
|
|
278
350
|
}
|
|
@@ -282,6 +354,10 @@ export function createViteTamaguiLoader(
|
|
|
282
354
|
loadPromise = null
|
|
283
355
|
loadedOptions = null
|
|
284
356
|
projectPromise = null
|
|
357
|
+
loadingProject = null
|
|
358
|
+
invalidatedFiles.clear()
|
|
359
|
+
evaluationDependencies.clear()
|
|
360
|
+
stampSources.clear()
|
|
285
361
|
}
|
|
286
362
|
},
|
|
287
363
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
createRunnableDevEnvironment,
|
|
14
14
|
defaultClientConditions,
|
|
15
15
|
defaultClientMainFields,
|
|
16
|
-
isRunnableDevEnvironment,
|
|
17
16
|
resolveConfig,
|
|
18
17
|
} from 'vite'
|
|
19
18
|
import type {
|
|
19
|
+
DevEnvironment,
|
|
20
20
|
EnvironmentOptions,
|
|
21
21
|
EnvironmentModuleNode,
|
|
22
22
|
Plugin,
|
|
@@ -26,7 +26,11 @@ import type {
|
|
|
26
26
|
} from 'vite'
|
|
27
27
|
import type { Environment } from 'vite'
|
|
28
28
|
import type { ViteTamaguiLoader } from './loadTamagui'
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
createViteTamaguiLoader,
|
|
31
|
+
TAMAGUI_EVALUATION_ENVIRONMENT,
|
|
32
|
+
viteBuildConfigLoader,
|
|
33
|
+
} from './loadTamagui'
|
|
30
34
|
import {
|
|
31
35
|
createCompilerStatsReport,
|
|
32
36
|
formatCompilerStatsReport,
|
|
@@ -362,6 +366,11 @@ async function createOwnedEvaluationConfig(
|
|
|
362
366
|
configuredEvaluationPackages
|
|
363
367
|
)
|
|
364
368
|
const { createEnvironment: _createEnvironment, ...dev } = environment.dev
|
|
369
|
+
// vite fills `optimizeDeps.esbuildOptions` in itself while resolving, then
|
|
370
|
+
// warns about the deprecated option when it sees it on the way back in. drop
|
|
371
|
+
// it so re-resolving an already-resolved environment stays quiet; rolldown
|
|
372
|
+
// reads `rolldownOptions`, which passes through untouched.
|
|
373
|
+
const { esbuildOptions: _esbuildOptions, ...optimizeDeps } = environment.optimizeDeps
|
|
365
374
|
|
|
366
375
|
// ModuleRunner needs Vite's serve-time core pipeline (especially import
|
|
367
376
|
// analysis), but user plugin selection must remain the already-resolved
|
|
@@ -383,7 +392,7 @@ async function createOwnedEvaluationConfig(
|
|
|
383
392
|
keepProcessEnv: environment.keepProcessEnv,
|
|
384
393
|
define: environment.define,
|
|
385
394
|
resolve,
|
|
386
|
-
optimizeDeps
|
|
395
|
+
optimizeDeps,
|
|
387
396
|
dev: {
|
|
388
397
|
...dev,
|
|
389
398
|
moduleRunnerTransform: true,
|
|
@@ -456,12 +465,41 @@ function addIfInstalled(
|
|
|
456
465
|
type AliasOptions = {
|
|
457
466
|
/** use @tamagui/react-native-web-lite, 'without-animated' for smaller bundle */
|
|
458
467
|
rnwLite?: boolean | 'without-animated'
|
|
459
|
-
/** alias react-native-svg to @tamagui/react-native-svg */
|
|
468
|
+
/** alias react-native-svg to @tamagui/react-native-svg's web entry */
|
|
460
469
|
svg?: boolean
|
|
461
470
|
}
|
|
462
471
|
|
|
463
472
|
type AliasEntry = { find: string | RegExp; replacement: string }
|
|
464
473
|
|
|
474
|
+
function svgWebEntry() {
|
|
475
|
+
// A linked workspace dependency is served directly during development.
|
|
476
|
+
// require.resolve selects CJS, whose named exports browsers cannot import.
|
|
477
|
+
return normalizePath(
|
|
478
|
+
path.join(
|
|
479
|
+
path.dirname(resolve('@tamagui/react-native-svg/package.json')),
|
|
480
|
+
'dist/esm/index.mjs'
|
|
481
|
+
)
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function resolveSvgWeb(source: string) {
|
|
486
|
+
if (source === 'react-native-svg' || source === '@tamagui/react-native-svg') {
|
|
487
|
+
return svgWebEntry()
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// rolldown dependency prebundling uses this resolver rather than basePlugin.resolveId.
|
|
492
|
+
const svgOptimizerPlugin: Plugin = {
|
|
493
|
+
name: 'tamagui-svg-optimizer',
|
|
494
|
+
enforce: 'pre',
|
|
495
|
+
resolveId: {
|
|
496
|
+
filter: { id: /^(?:react-native-svg|@tamagui\/react-native-svg)$/ },
|
|
497
|
+
handler(source) {
|
|
498
|
+
return resolveSvgWeb(source)
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
}
|
|
502
|
+
|
|
465
503
|
/**
|
|
466
504
|
* returns vite-compatible aliases for tamagui
|
|
467
505
|
* use this when you need control over alias ordering in your config
|
|
@@ -470,10 +508,13 @@ export function tamaguiAliases(options: AliasOptions = {}): AliasEntry[] {
|
|
|
470
508
|
const aliases: AliasEntry[] = []
|
|
471
509
|
|
|
472
510
|
if (options.svg) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
511
|
+
// both spellings, because a bare-specifier alias leaves the cjs entry
|
|
512
|
+
// reachable and vite serves that file to the browser untransformed.
|
|
513
|
+
const svg = svgWebEntry()
|
|
514
|
+
aliases.push(
|
|
515
|
+
{ find: 'react-native-svg', replacement: svg },
|
|
516
|
+
{ find: '@tamagui/react-native-svg', replacement: svg }
|
|
517
|
+
)
|
|
477
518
|
}
|
|
478
519
|
|
|
479
520
|
if (options.rnwLite) {
|
|
@@ -549,12 +590,10 @@ function createTamaguiNativePlugin(
|
|
|
549
590
|
rebuildProject = false
|
|
550
591
|
const pending = (async () => {
|
|
551
592
|
projectDependencies.clear()
|
|
552
|
-
const loadedOptions = await Static.loadTamaguiBuildConfigAsync(
|
|
553
|
-
...tamaguiOptionsIn,
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
outputCSS: undefined,
|
|
557
|
-
})
|
|
593
|
+
const loadedOptions = await Static.loadTamaguiBuildConfigAsync(
|
|
594
|
+
{ ...tamaguiOptionsIn, root, platform: 'native', outputCSS: undefined },
|
|
595
|
+
viteBuildConfigLoader
|
|
596
|
+
)
|
|
558
597
|
const options = { ...loadedOptions, root, outputCSS: undefined }
|
|
559
598
|
nativeOptions = options
|
|
560
599
|
for (const dependency of Static.getTamaguiBuildConfigDependencies(loadedOptions)) {
|
|
@@ -869,14 +908,22 @@ export function createTamaguiPlugins({
|
|
|
869
908
|
resolved,
|
|
870
909
|
configuredEvaluationPackages
|
|
871
910
|
)
|
|
872
|
-
|
|
911
|
+
// configuration updates are serialized by the shared loader. the runner
|
|
912
|
+
// must not also re-import its entries on Vite's full-reload event.
|
|
913
|
+
const environment = createRunnableDevEnvironment(name, evaluationConfig, {
|
|
914
|
+
hot: false,
|
|
915
|
+
})
|
|
916
|
+
tamaguiLoader.setEnvironment(environment)
|
|
917
|
+
return environment
|
|
873
918
|
},
|
|
874
919
|
moduleRunnerTransform: true,
|
|
875
920
|
},
|
|
876
921
|
})
|
|
877
922
|
|
|
878
|
-
// start loading immediately but don't block
|
|
879
|
-
|
|
923
|
+
// start loading immediately but don't block. the rejection is swallowed here
|
|
924
|
+
// only so node does not call it unhandled: the loader drops a failed promise,
|
|
925
|
+
// so the first real caller re-runs the load and reports the error itself.
|
|
926
|
+
void tamaguiLoader.loadTamaguiBuildConfig().catch(() => {})
|
|
880
927
|
|
|
881
928
|
// helper to await load when needed
|
|
882
929
|
const ensureLoaded = async () => {
|
|
@@ -897,6 +944,15 @@ export function createTamaguiPlugins({
|
|
|
897
944
|
const transformedModuleIds = new Set<string>()
|
|
898
945
|
const compilerHotUpdateSignatures = new Map<string, string>()
|
|
899
946
|
const compilerHotReloadSignatures = new Map<string, string>()
|
|
947
|
+
// what has already been handed to `server.watcher.add`. chokidar's fsevents
|
|
948
|
+
// backend has no dedupe: every add() of a path builds a fresh listener
|
|
949
|
+
// closure and drops it in one Set per watch root, and nothing ever removes
|
|
950
|
+
// it. re-adding the config's dependencies on each hot update therefore grows
|
|
951
|
+
// that Set forever, and since the root consolidates to the whole project,
|
|
952
|
+
// every filesystem event under it runs the entire Set. measured on a real app
|
|
953
|
+
// after three hours: ~300 dependency files had become 45,228 listeners, each
|
|
954
|
+
// doing a string concat and an indexOf per event, which pinned one core.
|
|
955
|
+
let watchedDependencies = new Set<string>()
|
|
900
956
|
let config: ResolvedConfig
|
|
901
957
|
let server: ViteDevServer
|
|
902
958
|
let zero: ZeroRuntimeController | null = null
|
|
@@ -939,6 +995,9 @@ export function createTamaguiPlugins({
|
|
|
939
995
|
return environment?.name && environment.name !== 'client'
|
|
940
996
|
}
|
|
941
997
|
|
|
998
|
+
const isDevEnvironment = (environment: Environment): environment is DevEnvironment =>
|
|
999
|
+
environment.mode === 'dev'
|
|
1000
|
+
|
|
942
1001
|
function isNative(environment?: Environment) {
|
|
943
1002
|
return (
|
|
944
1003
|
environment?.name && (environment.name === 'ios' || environment.name === 'android')
|
|
@@ -966,15 +1025,23 @@ export function createTamaguiPlugins({
|
|
|
966
1025
|
name: 'tamagui',
|
|
967
1026
|
enforce: 'pre',
|
|
968
1027
|
|
|
1028
|
+
resolveId(source) {
|
|
1029
|
+
if (
|
|
1030
|
+
disableResolveConfig ||
|
|
1031
|
+
tamaguiOptionsIn.platform === 'native' ||
|
|
1032
|
+
isNative(this.environment)
|
|
1033
|
+
) {
|
|
1034
|
+
return
|
|
1035
|
+
}
|
|
1036
|
+
// a framework can enable native environments alongside client and ssr.
|
|
1037
|
+
// web svg resolution must still use the DOM entry in those environments.
|
|
1038
|
+
return resolveSvgWeb(source)
|
|
1039
|
+
},
|
|
1040
|
+
|
|
969
1041
|
configureServer(_server) {
|
|
970
1042
|
server = _server
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
throw new Error(
|
|
974
|
-
`The ${TAMAGUI_EVALUATION_ENVIRONMENT} Vite environment must support ModuleRunner evaluation`
|
|
975
|
-
)
|
|
976
|
-
}
|
|
977
|
-
tamaguiLoader.setEnvironment(evaluationEnvironment)
|
|
1043
|
+
// a new server owns a new watcher, so what the last one knew about is gone
|
|
1044
|
+
watchedDependencies = new Set()
|
|
978
1045
|
},
|
|
979
1046
|
|
|
980
1047
|
async buildEnd() {
|
|
@@ -1050,9 +1117,14 @@ export function createTamaguiPlugins({
|
|
|
1050
1117
|
},
|
|
1051
1118
|
|
|
1052
1119
|
define: {
|
|
1053
|
-
// Config evaluation, report builds, native builds
|
|
1054
|
-
//
|
|
1055
|
-
'
|
|
1120
|
+
// Config evaluation, report builds, and native builds keep ordinary
|
|
1121
|
+
// Tamagui runtime behavior. An island child build keeps the component
|
|
1122
|
+
// runtime but its rules live in the parent's artifact. Vite merges this
|
|
1123
|
+
// hook's result over the child build's own config, so the literal is
|
|
1124
|
+
// owned here.
|
|
1125
|
+
'process.env.TAMAGUI_RUNTIME': JSON.stringify(
|
|
1126
|
+
zeroIslandBuild ? 'island' : 'full'
|
|
1127
|
+
),
|
|
1056
1128
|
// reanimated support
|
|
1057
1129
|
_frameTimestamp: undefined,
|
|
1058
1130
|
_WORKLET: false,
|
|
@@ -1076,7 +1148,6 @@ export function createTamaguiPlugins({
|
|
|
1076
1148
|
resolve('@tamagui/proxy-worm'),
|
|
1077
1149
|
'react-native/Libraries/Utilities/codegenNativeComponent':
|
|
1078
1150
|
resolve('@tamagui/proxy-worm'),
|
|
1079
|
-
'react-native-svg': resolve('@tamagui/react-native-svg'),
|
|
1080
1151
|
...(!useReactNativeWebLite && {
|
|
1081
1152
|
'react-native': resolve('react-native-web'),
|
|
1082
1153
|
}),
|
|
@@ -1146,6 +1217,19 @@ export function createTamaguiPlugins({
|
|
|
1146
1217
|
userConf.optimizeDeps ||= {}
|
|
1147
1218
|
userConf.optimizeDeps.include ||= []
|
|
1148
1219
|
|
|
1220
|
+
if (!disableResolveConfig && tamaguiOptionsIn.platform !== 'native') {
|
|
1221
|
+
userConf.optimizeDeps.rolldownOptions ||= {}
|
|
1222
|
+
const existingPlugins = userConf.optimizeDeps.rolldownOptions.plugins
|
|
1223
|
+
userConf.optimizeDeps.rolldownOptions.plugins = [
|
|
1224
|
+
svgOptimizerPlugin,
|
|
1225
|
+
...(Array.isArray(existingPlugins)
|
|
1226
|
+
? existingPlugins
|
|
1227
|
+
: existingPlugins
|
|
1228
|
+
? [existingPlugins]
|
|
1229
|
+
: []),
|
|
1230
|
+
]
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1149
1233
|
// These dependencies are CJS and break when served directly to the browser
|
|
1150
1234
|
// (`exports`/`module` is not defined). Pre-bundle them before Tamagui's linked
|
|
1151
1235
|
// package graph can expose them as late-discovered transitive dependencies.
|
|
@@ -1214,7 +1298,13 @@ export function createTamaguiPlugins({
|
|
|
1214
1298
|
|
|
1215
1299
|
async buildStart() {
|
|
1216
1300
|
const buildConfig = this.environment.getTopLevelConfig()
|
|
1217
|
-
if (buildConfig.command !== 'build')
|
|
1301
|
+
if (buildConfig.command !== 'build') {
|
|
1302
|
+
if (this.environment.name === 'client') {
|
|
1303
|
+
const dependencies = await tamaguiLoader.ensureFullConfigLoaded()
|
|
1304
|
+
for (const dependency of dependencies) this.addWatchFile(dependency)
|
|
1305
|
+
}
|
|
1306
|
+
return
|
|
1307
|
+
}
|
|
1218
1308
|
|
|
1219
1309
|
const pendingCleanup = buildCleanupPromise
|
|
1220
1310
|
if (pendingCleanup) {
|
|
@@ -1258,7 +1348,21 @@ export function createTamaguiPlugins({
|
|
|
1258
1348
|
if (!tamaguiLoader.isEvaluationDependency(options.file)) {
|
|
1259
1349
|
if (this.environment.name !== 'client') return
|
|
1260
1350
|
const compilerFrontend = getCompilerFrontend(this.environment)
|
|
1261
|
-
|
|
1351
|
+
// the file can vanish between the watcher event and this read (a temp
|
|
1352
|
+
// directory a test wrote and removed): that is the delete it looks
|
|
1353
|
+
// like, not a failure worth vite's error overlay in every open tab
|
|
1354
|
+
let source: string | null = null
|
|
1355
|
+
if (options.type !== 'delete') {
|
|
1356
|
+
try {
|
|
1357
|
+
source = await options.read()
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
if (
|
|
1360
|
+
!(error instanceof Error && 'code' in error && error.code === 'ENOENT')
|
|
1361
|
+
) {
|
|
1362
|
+
throw error
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1262
1366
|
const affectedModules = new Set<EnvironmentModuleNode>()
|
|
1263
1367
|
const compilerHmrRoots = new Set<string>(
|
|
1264
1368
|
compilerFrontend.dependentsOf(options.file)
|
|
@@ -1270,11 +1374,11 @@ export function createTamaguiPlugins({
|
|
|
1270
1374
|
const loadedOptions = await ensureLoaded()
|
|
1271
1375
|
if (!loadedOptions?.disable) {
|
|
1272
1376
|
const invalidatedIds =
|
|
1273
|
-
|
|
1377
|
+
source === null
|
|
1274
1378
|
? (await compilerFrontend.remove(options.file)).invalidatedIds
|
|
1275
1379
|
: await compilerFrontend.update({
|
|
1276
1380
|
id: options.file,
|
|
1277
|
-
source
|
|
1381
|
+
source,
|
|
1278
1382
|
root: config.root,
|
|
1279
1383
|
target: 'web',
|
|
1280
1384
|
environment: this.environment.name,
|
|
@@ -1314,7 +1418,11 @@ export function createTamaguiPlugins({
|
|
|
1314
1418
|
const cssId = getAbsoluteVirtualFileId(`${invalidatedId}${virtualExt}`)
|
|
1315
1419
|
const cssModule = this.environment.moduleGraph.getModuleById(cssId)
|
|
1316
1420
|
if (cssModule) {
|
|
1421
|
+
// returned alongside its importer so vite sends a css-update with a
|
|
1422
|
+
// fresh timestamp; an invalidation alone leaves the browser on the
|
|
1423
|
+
// cached stylesheet while the new class hashes have no rules
|
|
1317
1424
|
this.environment.moduleGraph.invalidateModule(cssModule)
|
|
1425
|
+
affectedModules.add(cssModule)
|
|
1318
1426
|
}
|
|
1319
1427
|
}
|
|
1320
1428
|
}
|
|
@@ -1338,6 +1446,12 @@ export function createTamaguiPlugins({
|
|
|
1338
1446
|
tamaguiLoader.invalidate(options.file)
|
|
1339
1447
|
invalidateCompilerModules()
|
|
1340
1448
|
}
|
|
1449
|
+
const dependencies = await tamaguiLoader.ensureFullConfigLoaded()
|
|
1450
|
+
const unwatched = dependencies.filter((file) => !watchedDependencies.has(file))
|
|
1451
|
+
if (unwatched.length) {
|
|
1452
|
+
for (const file of unwatched) watchedDependencies.add(file)
|
|
1453
|
+
server.watcher.add(unwatched)
|
|
1454
|
+
}
|
|
1341
1455
|
if (
|
|
1342
1456
|
this.environment.name === 'client' &&
|
|
1343
1457
|
compilerHotReloadSignatures.get(options.file) !== signature
|
|
@@ -1406,6 +1520,19 @@ export function createTamaguiPlugins({
|
|
|
1406
1520
|
if (!shouldExtract) return
|
|
1407
1521
|
|
|
1408
1522
|
const [validId] = id.split('?')
|
|
1523
|
+
if (!validId.endsWith(virtualExt)) return
|
|
1524
|
+
// the importer's transform is what fills this module's css. after a hot
|
|
1525
|
+
// update the browser can fetch the css-update before it re-imports the
|
|
1526
|
+
// component, so run (or join) that transform here instead of serving the
|
|
1527
|
+
// rules of the previous edit
|
|
1528
|
+
if (isDevEnvironment(this.environment)) {
|
|
1529
|
+
const importer = this.environment.moduleGraph.getModuleById(
|
|
1530
|
+
validId.slice(0, -virtualExt.length)
|
|
1531
|
+
)
|
|
1532
|
+
if (importer && importer.transformResult == null) {
|
|
1533
|
+
await this.environment.transformRequest(importer.url)
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1409
1536
|
return cssMap.get(validId)
|
|
1410
1537
|
},
|
|
1411
1538
|
}
|
|
@@ -1537,7 +1664,7 @@ export function createTamaguiPlugins({
|
|
|
1537
1664
|
Static.mergeIslandBridges(zero.bridges, zeroResult.bridges)
|
|
1538
1665
|
const moduleCSS = [
|
|
1539
1666
|
wrapExtractedCSS(result.plan.css),
|
|
1540
|
-
...
|
|
1667
|
+
...zeroResult.bridgeCSS.values(),
|
|
1541
1668
|
]
|
|
1542
1669
|
.filter(Boolean)
|
|
1543
1670
|
.join('\n')
|
package/src/zeroRuntime.ts
CHANGED
|
@@ -104,7 +104,7 @@ export async function createZeroRuntimeController(
|
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
106
|
* Builds one island as a separate bundler invocation with
|
|
107
|
-
* `TAMAGUI_RUNTIME='
|
|
107
|
+
* `TAMAGUI_RUNTIME='island'`. React is externalized to the handoff the generated
|
|
108
108
|
* loader publishes, so both graphs share one React instance.
|
|
109
109
|
*/
|
|
110
110
|
export async function buildIsland(input: {
|
|
@@ -124,7 +124,7 @@ export async function buildIsland(input: {
|
|
|
124
124
|
mode: 'production',
|
|
125
125
|
logLevel: 'warn',
|
|
126
126
|
define: {
|
|
127
|
-
|
|
127
|
+
// TAMAGUI_RUNTIME='island' is defined by the plugin's own config hook
|
|
128
128
|
'process.env.TAMAGUI_DID_OUTPUT_CSS': JSON.stringify('1'),
|
|
129
129
|
// the island is a separately built artifact, never a dev asset, so it is
|
|
130
130
|
// always a production build and always uses react/jsx-runtime
|
package/types/loadTamagui.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import Static from '@tamagui/static';
|
|
2
2
|
import type { TamaguiProjectInfo } from '@tamagui/static';
|
|
3
3
|
import type { TamaguiOptions } from '@tamagui/types';
|
|
4
|
-
import type
|
|
4
|
+
import { type RunnableDevEnvironment } from 'vite';
|
|
5
5
|
export declare const TAMAGUI_EVALUATION_ENVIRONMENT = "tamagui";
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate tamagui.build.ts through Vite instead of esbuild.
|
|
8
|
+
*
|
|
9
|
+
* The esbuild path spawns a long-lived service process, and that process does
|
|
10
|
+
* not survive a Vite dev server restart: the next `tamaguiPlugin()` call, which
|
|
11
|
+
* happens while Vite is rebuilding its config, hits `write EPIPE` and takes the
|
|
12
|
+
* restart down with it. Vite is already running here and already evaluates
|
|
13
|
+
* tamagui.config.ts and every component package through its own module runner,
|
|
14
|
+
* so the build file has no reason to need a second toolchain.
|
|
15
|
+
*
|
|
16
|
+
* `runnerImport` forces `configFile: false`, so loading the build file from
|
|
17
|
+
* inside a plugin that the config file itself creates cannot recurse. Bare
|
|
18
|
+
* imports stay external, matching esbuild's `packages: 'external'`, and the
|
|
19
|
+
* returned dependency list replaces the metafile's inputs for watching.
|
|
20
|
+
*/
|
|
21
|
+
export declare const viteBuildConfigLoader: Static.BuildConfigLoader;
|
|
6
22
|
type ResolvedEvaluationModule = {
|
|
7
23
|
moduleName: string;
|
|
8
24
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadTamagui.d.ts","sourceRoot":"","sources":["../src/loadTamagui.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAGpD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"loadTamagui.d.ts","sourceRoot":"","sources":["../src/loadTamagui.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAGpD,OAAO,EAAgB,KAAK,sBAAsB,EAAE,MAAM,MAAM,CAAA;AAEhE,eAAO,MAAM,8BAA8B,YAAY,CAAA;AAEvD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,iBAY1C,CAAA;AAYD,KAAK,wBAAwB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAED,KAAK,uBAAuB,GAAG;IAC7B,MAAM,EAAE,wBAAwB,CAAA;IAChC,UAAU,EAAE,wBAAwB,EAAE,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,IAAI,sBAAsB,GAAG,IAAI,CAAA;IAC/C,aAAa,IAAI,MAAM,CAAA;IACvB,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAA;IAChD,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAAA;IAC1C,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC,CAAA;IAChE,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAA;IACrD,yBAAyB,IAAI,MAAM,EAAE,CAAA;IACrC,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3C,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACjF;;;;OAIG;IACH,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IACnE,sBAAsB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAA;IACjD,cAAc,CAAC,IAAI,EAAE,sBAAsB,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;IACjF,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,sBAAsB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC3C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB,CAAA;AAED,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,iBAAiB,CAuRnB"}
|
package/types/plugin.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type ZeroIslandBuildContext } from './zeroRuntime';
|
|
|
5
5
|
type AliasOptions = {
|
|
6
6
|
/** use @tamagui/react-native-web-lite, 'without-animated' for smaller bundle */
|
|
7
7
|
rnwLite?: boolean | 'without-animated';
|
|
8
|
-
/** alias react-native-svg to @tamagui/react-native-svg */
|
|
8
|
+
/** alias react-native-svg to @tamagui/react-native-svg's web entry */
|
|
9
9
|
svg?: boolean;
|
|
10
10
|
};
|
|
11
11
|
type AliasEntry = {
|
package/types/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,iBAAiB,CAAA;AAgBvE,OAAO,KAAK,EAIV,MAAM,EACN,YAAY,EAGb,MAAM,MAAM,CAAA;AAEb,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAWtD,OAAO,EAQL,KAAK,sBAAsB,EAE5B,MAAM,eAAe,CAAA;AAgatB,KAAK,YAAY,GAAG;IAClB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAA;IACtC,sEAAsE;IACtE,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAED,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAA;AA+BhE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,EAAE,CA4DvE;AAkLD,wBAAgB,mBAAmB,CAAC,gBAAgB,GAAE,cAAmB,GAAG,MAAM,CAejF;AAED,MAAM,MAAM,wBAAwB,GAAG,cAAc,GAAG;IACtD,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,wBAAwB,GAAG;IACpE;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C;;;;OAIG;IACH,eAAe,CAAC,EAAE,sBAAsB,CAAA;CACzC,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,oBAAoB,EACpB,gBAA+B,EAC/B,eAAe,EACf,GAAG,gBAAgB,EACpB,GAAE,4BAAiC,GAAG;IACrC,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,MAAM,EAAE,iBAAiB,CAAA;CAC1B,CAqsCA;AAYD,wBAAgB,aAAa,CAAC,OAAO,GAAE,wBAA6B,GAAG,YAAY,CAElF"}
|
package/types/zeroRuntime.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare const zeroModuleKey: (value: string) => string;
|
|
|
37
37
|
export declare function createZeroRuntimeController(options: TamaguiOptions, root: string, base: string): Promise<ZeroRuntimeController | null>;
|
|
38
38
|
/**
|
|
39
39
|
* Builds one island as a separate bundler invocation with
|
|
40
|
-
* `TAMAGUI_RUNTIME='
|
|
40
|
+
* `TAMAGUI_RUNTIME='island'`. React is externalized to the handoff the generated
|
|
41
41
|
* loader publishes, so both graphs share one React instance.
|
|
42
42
|
*/
|
|
43
43
|
export declare function buildIsland(input: {
|