@tamagui/vite-plugin 3.0.0-beta.924.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.
@@ -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 { RunnableDevEnvironment } from 'vite'
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
- loadPromise = Static.loadTamaguiBuildConfigAsync({
97
- ...optionsIn,
98
- platform: 'web',
99
- }).then((options) => {
100
- loadedOptions = options
101
- return options
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 loadPromise
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:${generation}`,
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
- return projectPromise
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 && environment) {
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 { createViteTamaguiLoader, TAMAGUI_EVALUATION_ENVIRONMENT } from './loadTamagui'
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: environment.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
- aliases.push({
474
- find: 'react-native-svg',
475
- replacement: resolve('@tamagui/react-native-svg'),
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
- root,
555
- platform: 'native',
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
- return createRunnableDevEnvironment(name, evaluationConfig)
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
- tamaguiLoader.loadTamaguiBuildConfig()
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
- const evaluationEnvironment = server.environments[TAMAGUI_EVALUATION_ENVIRONMENT]
972
- if (!isRunnableDevEnvironment(evaluationEnvironment)) {
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, and full-runtime
1054
- // island child builds all keep ordinary Tamagui runtime behavior.
1055
- 'process.env.TAMAGUI_RUNTIME': JSON.stringify('full'),
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') return
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
- const source = options.type === 'delete' ? null : await options.read()
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
- options.type === 'delete'
1377
+ source === null
1274
1378
  ? (await compilerFrontend.remove(options.file)).invalidatedIds
1275
1379
  : await compilerFrontend.update({
1276
1380
  id: options.file,
1277
- source: 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
- ...[...zeroResult.bridgeCSS.values()],
1667
+ ...zeroResult.bridgeCSS.values(),
1541
1668
  ]
1542
1669
  .filter(Boolean)
1543
1670
  .join('\n')
@@ -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='full'`. React is externalized to the handoff the generated
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
- 'process.env.TAMAGUI_RUNTIME': JSON.stringify('full'),
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
@@ -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 { RunnableDevEnvironment } from 'vite';
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,EAAE,sBAAsB,EAAE,MAAM,MAAM,CAAA;AAElD,eAAO,MAAM,8BAA8B,YAAY,CAAA;AAYvD,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,CAwOnB"}
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 = {
@@ -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;AAiBvE,OAAO,KAAK,EAGV,MAAM,EACN,YAAY,EAGb,MAAM,MAAM,CAAA;AAEb,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAOtD,OAAO,EAQL,KAAK,sBAAsB,EAE5B,MAAM,eAAe,CAAA;AA2ZtB,KAAK,YAAY,GAAG;IAClB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAA;IACtC,0DAA0D;IAC1D,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;AAEhE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,EAAE,CAyDvE;AAoLD,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,CA6mCA;AAYD,wBAAgB,aAAa,CAAC,OAAO,GAAE,wBAA6B,GAAG,YAAY,CAElF"}
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"}
@@ -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='full'`. React is externalized to the handoff the generated
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: {