@multiplatform.one/config 7.47.4 → 7.48.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/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { t as createViteConfig } from "./vite-DkidIRYX.js";
1
+ import { t as createViteConfig } from "./vite-B4uRAr9Z.js";
2
2
  import { i as phosphorTokenColorPlugin, n as tamaguiWorkspacePathsFile, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-DR3dud79.js";
3
- import { t as createStorybookViteConfig } from "./storybook-Ba0KCHBo.js";
3
+ import { t as createStorybookViteConfig } from "./storybook-CT-nBzSF.js";
4
4
  import { createVitestConfig } from "./vitest.js";
5
5
 
6
6
  export { createStorybookViteConfig, createViteConfig, createVitestConfig, ensureTamaguiWorkspacePaths, phosphorTokenColorPlugin, tamaguiWorkspacePathsFile };
@@ -1,5 +1,5 @@
1
1
  import { i as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-DR3dud79.js";
2
- import { i as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
2
+ import { o as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DltEXVBc.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
package/lib/storybook.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as createStorybookViteConfig } from "./storybook-Ba0KCHBo.js";
1
+ import { t as createStorybookViteConfig } from "./storybook-CT-nBzSF.js";
2
2
 
3
3
  export { createStorybookViteConfig };
@@ -1,5 +1,5 @@
1
1
  import { i as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-DR3dud79.js";
2
- import { i as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
2
+ import { i as resolveWorkspaceNativeEntry, o as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DltEXVBc.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
@@ -549,8 +549,8 @@ function registerNativeEngineIoBrowserTransports() {
549
549
  });
550
550
  }
551
551
  /**
552
- * Routes workspace (`@multiplatform.one/*`) packages to their `dist/esm/index.native.js`
553
- * build on NATIVE, via vxrn's `globalThis.__vxrnAddNativePlugins` hook.
552
+ * Routes workspace (`@multiplatform.one/*`) packages to their native entry on
553
+ * NATIVE, via vxrn's `globalThis.__vxrnAddNativePlugins` hook.
554
554
  *
555
555
  * Why this is needed: `preferBuiltWorkspaceEntryPlugin` (a Vite plugin, web/SSR only)
556
556
  * hard-resolves every workspace bare specifier to `<root>/dist/esm/index.mjs` — the WEB
@@ -561,8 +561,10 @@ function registerNativeEngineIoBrowserTransports() {
561
561
  * @mixmark-io/domino, …) into Hermes. Each affected package ships a `dist/esm/index.native.js`
562
562
  * (built by tamagui-build, intra-package imports already rewritten to their `.native`
563
563
  * siblings). This resolveId matches both forms — the bare specifier and the already-resolved
564
- * `…/dist/esm/index.mjs` path — and redirects to the native sibling when it exists, so the
565
- * react-native build wins on native. Web/SSR are unaffected (this plugin runs ONLY on native).
564
+ * `…/dist/esm/index.mjs` path — and answers with `resolveWorkspaceNativeEntry`: the native
565
+ * dist when it is current, source when the package was never built, and an error when the
566
+ * dist is older than the source (FZ-7844: a stale components dist shipped `TabBar = void 0`).
567
+ * Web/SSR are unaffected (this plugin runs ONLY on native).
566
568
  */
567
569
  function registerNativeWorkspaceEntries() {
568
570
  const g = globalThis;
@@ -575,24 +577,35 @@ function registerNativeWorkspaceEntries() {
575
577
  } catch {
576
578
  roots = /* @__PURE__ */ new Map();
577
579
  }
580
+ const realRoots = /* @__PURE__ */ new Map();
581
+ for (const root of roots.values()) try {
582
+ realRoots.set(fs.realpathSync(root), root);
583
+ } catch {
584
+ realRoots.set(root, root);
585
+ }
586
+ const distSuffix = "/dist/esm/index.mjs";
587
+ const rootOfDistEntry = (id) => {
588
+ if (!id.endsWith(distSuffix)) return void 0;
589
+ const dir = id.slice(0, -19);
590
+ try {
591
+ return realRoots.get(fs.realpathSync(dir));
592
+ } catch {
593
+ return realRoots.get(dir);
594
+ }
595
+ };
596
+ const entries = /* @__PURE__ */ new Map();
597
+ const entryOf = (root) => {
598
+ if (!entries.has(root)) entries.set(root, resolveWorkspaceNativeEntry(root) ?? null);
599
+ return entries.get(root);
600
+ };
578
601
  g.__vxrnAddNativePlugins.push({
579
602
  name,
580
603
  resolveId(id) {
581
604
  const root = roots.get(id);
582
- if (root) {
583
- const nativeEntry = path.join(root, "dist/esm/index.native.js");
584
- if (fs.existsSync(nativeEntry)) return nativeEntry;
585
- const srcTs = path.join(root, "src/index.ts");
586
- if (fs.existsSync(srcTs)) return srcTs;
587
- const srcTsx = path.join(root, "src/index.tsx");
588
- if (fs.existsSync(srcTsx)) return srcTsx;
589
- return null;
590
- }
591
- if (id.endsWith("/dist/esm/index.mjs")) {
592
- const nativeEntry = `${id.slice(0, -4)}.native.js`;
593
- if (fs.existsSync(nativeEntry)) return nativeEntry;
594
- }
595
- return null;
605
+ if (root) return entryOf(root);
606
+ const distRoot = rootOfDistEntry(id);
607
+ if (!distRoot || !fs.existsSync(path.join(distRoot, "dist/esm/index.native.js"))) return null;
608
+ return entryOf(distRoot);
596
609
  }
597
610
  });
598
611
  }
@@ -751,9 +764,15 @@ function registerNativeWeb3Stub() {
751
764
  function findProjectRoot() {
752
765
  try {
753
766
  const { execSync } = __require("node:child_process");
754
- return execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
767
+ return execSync("git rev-parse --show-toplevel", {
768
+ encoding: "utf8",
769
+ stdio: "pipe"
770
+ }).trim();
755
771
  } catch {
756
- return process.cwd();
772
+ for (let dir = process.cwd();; dir = path.dirname(dir)) {
773
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
774
+ if (path.dirname(dir) === dir) return process.cwd();
775
+ }
757
776
  }
758
777
  }
759
778
  /**
package/lib/vite.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as publicConfigPlugin, r as readPublicConfigKeys, t as createViteConfig } from "./vite-DkidIRYX.js";
1
+ import { n as publicConfigPlugin, r as readPublicConfigKeys, t as createViteConfig } from "./vite-B4uRAr9Z.js";
2
2
  import { i as phosphorTokenColorPlugin } from "./tamaguiWorkspacePaths-DR3dud79.js";
3
- import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
3
+ import { a as staleNativeDistSource, i as resolveWorkspaceNativeEntry, n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DltEXVBc.js";
4
4
 
5
- export { createViteConfig, discoverPublicPackageRoots, phosphorTokenColorPlugin, publicConfigPlugin, publicPackageViteSourceAliases, readPublicConfigKeys };
5
+ export { createViteConfig, discoverPublicPackageRoots, phosphorTokenColorPlugin, publicConfigPlugin, publicPackageViteSourceAliases, readPublicConfigKeys, resolveWorkspaceNativeEntry, staleNativeDistSource };
@@ -184,6 +184,68 @@ function discoverPublicPackageRoots(workspaceRoot) {
184
184
  }
185
185
  return byName;
186
186
  }
187
+ const testOnlySource = /\.(spec|test|stories)\.[cm]?[jt]sx?$/;
188
+ function newestSourceFile(dir) {
189
+ let newest;
190
+ const stack = [dir];
191
+ while (stack.length > 0) {
192
+ const current = stack.pop();
193
+ let entries;
194
+ try {
195
+ entries = fs.readdirSync(current, { withFileTypes: true });
196
+ } catch {
197
+ continue;
198
+ }
199
+ for (const ent of entries) {
200
+ const file = path.join(current, ent.name);
201
+ if (ent.isDirectory()) {
202
+ if (ent.name !== "node_modules" && ent.name !== "__snapshots__") stack.push(file);
203
+ continue;
204
+ }
205
+ if (testOnlySource.test(ent.name)) continue;
206
+ const { mtimeMs } = fs.statSync(file);
207
+ if (!newest || mtimeMs > newest.mtimeMs) newest = {
208
+ file,
209
+ mtimeMs
210
+ };
211
+ }
212
+ }
213
+ return newest;
214
+ }
215
+ /**
216
+ * The source file that makes a package's `dist/esm/index.native.js` stale: one
217
+ * changed after that dist was built. A dist is a snapshot of the last package
218
+ * build, and git rewrites every file a pull or checkout changes, so a source
219
+ * file newer than the dist means the dist no longer matches it. Undefined when
220
+ * the dist is current or absent. Spec, test and story files don't count.
221
+ */
222
+ function staleNativeDistSource(pkgRoot) {
223
+ const dist = path.join(pkgRoot, "dist", "esm", "index.native.js");
224
+ if (!fs.existsSync(dist)) return void 0;
225
+ const builtAt = fs.statSync(dist).mtimeMs;
226
+ const newest = newestSourceFile(path.join(pkgRoot, "src"));
227
+ return newest && newest.mtimeMs > builtAt ? newest.file : void 0;
228
+ }
229
+ /**
230
+ * The entry a NATIVE bundle compiles for a workspace package: its built
231
+ * `dist/esm/index.native.js` when that is current, else its source when it
232
+ * was never built. A STALE dist is refused, never silently used and never
233
+ * swapped for source: One 7.43.0 (FZ-7844) bundled an Oct 21 components dist
234
+ * that predates TabBar next to current app source rendering <TabBar>,
235
+ * rolldown shimmed the export as `TabBar = void 0`, and every tab screen threw
236
+ * "Element type is invalid". Falling back to source instead would change the
237
+ * bundle's shape at publish time (source pulls in modules the native dist
238
+ * never reached), so the bundler stops and says which package to rebuild.
239
+ */
240
+ function resolveWorkspaceNativeEntry(pkgRoot) {
241
+ const dist = path.join(pkgRoot, "dist", "esm", "index.native.js");
242
+ if (fs.existsSync(dist)) {
243
+ const changed = staleNativeDistSource(pkgRoot);
244
+ if (changed) throw new Error(`${path.relative(pkgRoot, dist)} in ${pkgRoot} is older than ${path.relative(pkgRoot, changed)}: the native bundle would ship exports that no longer match the source. Rebuild it first: pnpm --dir ${pkgRoot} build`);
245
+ return dist;
246
+ }
247
+ return [path.join(pkgRoot, "src", "index.ts"), path.join(pkgRoot, "src", "index.tsx")].find((c) => fs.existsSync(c));
248
+ }
187
249
  /**
188
250
  * Main entry file for Vite/Storybook-style “compile from source” aliases.
189
251
  *
@@ -301,4 +363,4 @@ function publicPackageViteSourceAliases(workspaceRoot) {
301
363
  }
302
364
 
303
365
  //#endregion
304
- export { withSingletonDepAliases as i, publicPackageViteSourceAliases as n, resolvePackageMainSource as r, discoverPublicPackageRoots as t };
366
+ export { staleNativeDistSource as a, resolveWorkspaceNativeEntry as i, publicPackageViteSourceAliases as n, withSingletonDepAliases as o, resolvePackageMainSource as r, discoverPublicPackageRoots as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/config",
3
- "version": "7.47.4",
3
+ "version": "7.48.0",
4
4
  "description": "Shared build and test configuration presets for multiplatform.one",
5
5
  "keywords": [
6
6
  "config",
@@ -106,7 +106,7 @@
106
106
  "access": "public"
107
107
  },
108
108
  "dependencies": {
109
- "@multiplatform.one/utils": "^7.47.4",
109
+ "@multiplatform.one/utils": "^7.48.0",
110
110
  "@tamagui/vite-plugin": "2.7.6",
111
111
  "@vitejs/plugin-react": "^6.1.2",
112
112
  "dotenv": "^17.4.2",
package/src/vite.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { lookupTamaguiModules } from "@multiplatform.one/utils/dev";
4
- import { discoverPublicPackageRoots } from "./workspacePublicPackages.js";
4
+ import {
5
+ discoverPublicPackageRoots,
6
+ resolveWorkspaceNativeEntry,
7
+ } from "./workspacePublicPackages.js";
5
8
  import { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
6
9
  import { withSingletonDepAliases } from "./singletonDepAliases.js";
7
10
  import { ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths.js";
@@ -261,10 +264,9 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
261
264
  registerNativeSinglePackageInstances();
262
265
  // preferBuiltWorkspaceEntryPlugin (web-only) hard-resolves every workspace
263
266
  // package to its `dist/esm/index.mjs` (WEB build), and that resolution reaches
264
- // the native graph too — pinning the whole @multiplatform.one/* chain to web
265
- // (dragging @tiptap/prosemirror/html5-qrcode/etc into Hermes). This native-only
266
- // resolveId redirects each workspace web entry to its `dist/esm/index.native.js`
267
- // sibling so native gets the react-native build (the wrappers' native variants).
267
+ // the native graph too. This native-only resolveId sends each workspace
268
+ // package to its `dist/esm/index.native.js` when that build is current, to
269
+ // source when it was never built, and REFUSES a stale one (FZ-7844).
268
270
  registerNativeWorkspaceEntries();
269
271
  // @phosphor-icons/react renders DOM <svg>/<path>, which redboxes Hermes
270
272
  // ("View config getter callback for component `path` must be a function").
@@ -528,6 +530,8 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
528
530
  export {
529
531
  discoverPublicPackageRoots,
530
532
  publicPackageViteSourceAliases,
533
+ resolveWorkspaceNativeEntry,
534
+ staleNativeDistSource,
531
535
  } from "./workspacePublicPackages.js";
532
536
  export {
533
537
  publicConfigPlugin,
@@ -792,8 +796,8 @@ function registerNativeEngineIoBrowserTransports(): void {
792
796
  }
793
797
 
794
798
  /**
795
- * Routes workspace (`@multiplatform.one/*`) packages to their `dist/esm/index.native.js`
796
- * build on NATIVE, via vxrn's `globalThis.__vxrnAddNativePlugins` hook.
799
+ * Routes workspace (`@multiplatform.one/*`) packages to their native entry on
800
+ * NATIVE, via vxrn's `globalThis.__vxrnAddNativePlugins` hook.
797
801
  *
798
802
  * Why this is needed: `preferBuiltWorkspaceEntryPlugin` (a Vite plugin, web/SSR only)
799
803
  * hard-resolves every workspace bare specifier to `<root>/dist/esm/index.mjs` — the WEB
@@ -804,8 +808,10 @@ function registerNativeEngineIoBrowserTransports(): void {
804
808
  * @mixmark-io/domino, …) into Hermes. Each affected package ships a `dist/esm/index.native.js`
805
809
  * (built by tamagui-build, intra-package imports already rewritten to their `.native`
806
810
  * siblings). This resolveId matches both forms — the bare specifier and the already-resolved
807
- * `…/dist/esm/index.mjs` path — and redirects to the native sibling when it exists, so the
808
- * react-native build wins on native. Web/SSR are unaffected (this plugin runs ONLY on native).
811
+ * `…/dist/esm/index.mjs` path — and answers with `resolveWorkspaceNativeEntry`: the native
812
+ * dist when it is current, source when the package was never built, and an error when the
813
+ * dist is older than the source (FZ-7844: a stale components dist shipped `TabBar = void 0`).
814
+ * Web/SSR are unaffected (this plugin runs ONLY on native).
809
815
  */
810
816
  function registerNativeWorkspaceEntries(): void {
811
817
  const g = globalThis as unknown as { __vxrnAddNativePlugins?: unknown[] };
@@ -818,34 +824,41 @@ function registerNativeWorkspaceEntries(): void {
818
824
  } catch {
819
825
  roots = new Map();
820
826
  }
827
+ const realRoots = new Map<string, string>();
828
+ for (const root of roots.values()) {
829
+ try {
830
+ realRoots.set(fs.realpathSync(root), root);
831
+ } catch {
832
+ realRoots.set(root, root);
833
+ }
834
+ }
835
+ const distSuffix = "/dist/esm/index.mjs";
836
+ const rootOfDistEntry = (id: string): string | undefined => {
837
+ if (!id.endsWith(distSuffix)) return undefined;
838
+ const dir = id.slice(0, -distSuffix.length);
839
+ try {
840
+ return realRoots.get(fs.realpathSync(dir));
841
+ } catch {
842
+ return realRoots.get(dir);
843
+ }
844
+ };
845
+ // Walking a package's src/ for the staleness check happens once per root.
846
+ const entries = new Map<string, string | null>();
847
+ const entryOf = (root: string): string | null => {
848
+ if (!entries.has(root)) entries.set(root, resolveWorkspaceNativeEntry(root) ?? null);
849
+ return entries.get(root) as string | null;
850
+ };
821
851
  g.__vxrnAddNativePlugins.push({
822
852
  name,
823
853
  resolveId(id: string): string | null {
824
854
  // (a) bare workspace specifier (e.g. "@multiplatform.one/rich-text")
825
855
  const root = roots.get(id);
826
- if (root) {
827
- // Prefer the built native entry when present (tamagui-build already
828
- // rewrote intra-package imports to their `.native` siblings).
829
- const nativeEntry = path.join(root, "dist/esm/index.native.js");
830
- if (fs.existsSync(nativeEntry)) return nativeEntry;
831
- // Unbuilt workspace package: compile from source instead of letting
832
- // rolldown leave the bare specifier external — Hermes has no module
833
- // mode, so a surviving top-level `import` is unparseable. Mirrors
834
- // preferBuiltWorkspaceEntryPlugin (web) and apps/storybook-expo's
835
- // Metro resolveWorkspaceNativeMain. vxrn's native resolver already
836
- // does platform-extension resolution (.native.tsx/.ios.tsx/…).
837
- const srcTs = path.join(root, "src/index.ts");
838
- if (fs.existsSync(srcTs)) return srcTs;
839
- const srcTsx = path.join(root, "src/index.tsx");
840
- if (fs.existsSync(srcTsx)) return srcTsx;
841
- return null;
842
- }
843
- // (b) already-resolved web entry "<...>/dist/esm/index.mjs" -> ".native.js" sibling
844
- if (id.endsWith("/dist/esm/index.mjs")) {
845
- const nativeEntry = `${id.slice(0, -".mjs".length)}.native.js`;
846
- if (fs.existsSync(nativeEntry)) return nativeEntry;
847
- }
848
- return null;
856
+ if (root) return entryOf(root);
857
+ // (b) already-resolved web entry "<...>/dist/esm/index.mjs" -> its ".native.js"
858
+ // sibling; with no native build the web entry stands, as it always has.
859
+ const distRoot = rootOfDistEntry(id);
860
+ if (!distRoot || !fs.existsSync(path.join(distRoot, "dist/esm/index.native.js"))) return null;
861
+ return entryOf(distRoot);
849
862
  },
850
863
  });
851
864
  }
@@ -1023,9 +1036,15 @@ function registerNativeWeb3Stub(): void {
1023
1036
  function findProjectRoot(): string {
1024
1037
  try {
1025
1038
  const { execSync } = require("node:child_process");
1026
- return execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
1039
+ return execSync("git rev-parse --show-toplevel", { encoding: "utf8", stdio: "pipe" }).trim();
1027
1040
  } catch {
1028
- return process.cwd();
1041
+ // No git (an exported tree, a CI copy): the workspace root is the nearest
1042
+ // pnpm-workspace.yaml. Falling back to the app's cwd found no public/
1043
+ // packages and left the native workspace resolver silently inert.
1044
+ for (let dir = process.cwd(); ; dir = path.dirname(dir)) {
1045
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
1046
+ if (path.dirname(dir) === dir) return process.cwd();
1047
+ }
1029
1048
  }
1030
1049
  }
1031
1050
 
@@ -7,6 +7,7 @@ import {
7
7
  packageSubpathSourceAliases,
8
8
  resolveExportSource,
9
9
  resolvePackageMainSource,
10
+ resolveWorkspaceNativeEntry,
10
11
  publicPackageViteSourceAliases,
11
12
  } from "./workspacePublicPackages";
12
13
 
@@ -214,3 +215,59 @@ describe("packageSubpathSourceAliases", () => {
214
215
  ).toBe(path.join(dir, "src/signer.ts"));
215
216
  });
216
217
  });
218
+
219
+ describe("resolveWorkspaceNativeEntry", () => {
220
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "native-entry-"));
221
+ afterAll(() => fs.rmSync(tmp, { recursive: true, force: true }));
222
+ const at = (file: string, seconds: number) => {
223
+ fs.mkdirSync(path.dirname(file), { recursive: true });
224
+ fs.writeFileSync(file, "");
225
+ fs.utimesSync(file, seconds, seconds);
226
+ };
227
+ const pkg = (name: string, files: Record<string, number>) => {
228
+ const root = path.join(tmp, name);
229
+ for (const [f, seconds] of Object.entries(files)) at(path.join(root, f), seconds);
230
+ return root;
231
+ };
232
+
233
+ it("takes a native dist built after every source file", () => {
234
+ const root = pkg("fresh", {
235
+ "src/index.ts": 100,
236
+ "src/a.tsx": 100,
237
+ "dist/esm/index.native.js": 200,
238
+ });
239
+ expect(resolveWorkspaceNativeEntry(root)).toBe(path.join(root, "dist/esm/index.native.js"));
240
+ });
241
+
242
+ // FZ-7844: an Oct 21 components dist without TabBar shipped next to newer source.
243
+ it("refuses a native dist older than a source file, naming the file", () => {
244
+ const root = pkg("stale", {
245
+ "src/index.ts": 100,
246
+ "src/appShell/TabBar.tsx": 300,
247
+ "dist/esm/index.native.js": 200,
248
+ });
249
+ expect(() => resolveWorkspaceNativeEntry(root)).toThrow(
250
+ /older than src\/appShell\/TabBar\.tsx/,
251
+ );
252
+ });
253
+
254
+ it("does not count spec or story files as source", () => {
255
+ const root = pkg("specs", {
256
+ "src/index.ts": 100,
257
+ "src/a.spec.tsx": 300,
258
+ "src/a.stories.tsx": 300,
259
+ "dist/esm/index.native.js": 200,
260
+ });
261
+ expect(resolveWorkspaceNativeEntry(root)).toBe(path.join(root, "dist/esm/index.native.js"));
262
+ });
263
+
264
+ it("compiles source when the package was never built", () => {
265
+ const root = pkg("unbuilt", { "src/index.ts": 100 });
266
+ expect(resolveWorkspaceNativeEntry(root)).toBe(path.join(root, "src/index.ts"));
267
+ });
268
+
269
+ it("takes a published tarball's native dist, which has no source", () => {
270
+ const root = pkg("tarball", { "dist/esm/index.native.js": 200 });
271
+ expect(resolveWorkspaceNativeEntry(root)).toBe(path.join(root, "dist/esm/index.native.js"));
272
+ });
273
+ });
@@ -30,6 +30,77 @@ export function discoverPublicPackageRoots(workspaceRoot: string): Map<string, s
30
30
  return byName;
31
31
  }
32
32
 
33
+ const testOnlySource = /\.(spec|test|stories)\.[cm]?[jt]sx?$/;
34
+
35
+ function newestSourceFile(dir: string): { file: string; mtimeMs: number } | undefined {
36
+ let newest: { file: string; mtimeMs: number } | undefined;
37
+ const stack = [dir];
38
+ while (stack.length > 0) {
39
+ const current = stack.pop() as string;
40
+ let entries: fs.Dirent[];
41
+ try {
42
+ entries = fs.readdirSync(current, { withFileTypes: true });
43
+ } catch {
44
+ continue;
45
+ }
46
+ for (const ent of entries) {
47
+ const file = path.join(current, ent.name);
48
+ if (ent.isDirectory()) {
49
+ if (ent.name !== "node_modules" && ent.name !== "__snapshots__") stack.push(file);
50
+ continue;
51
+ }
52
+ if (testOnlySource.test(ent.name)) continue;
53
+ const { mtimeMs } = fs.statSync(file);
54
+ if (!newest || mtimeMs > newest.mtimeMs) newest = { file, mtimeMs };
55
+ }
56
+ }
57
+ return newest;
58
+ }
59
+
60
+ /**
61
+ * The source file that makes a package's `dist/esm/index.native.js` stale: one
62
+ * changed after that dist was built. A dist is a snapshot of the last package
63
+ * build, and git rewrites every file a pull or checkout changes, so a source
64
+ * file newer than the dist means the dist no longer matches it. Undefined when
65
+ * the dist is current or absent. Spec, test and story files don't count.
66
+ */
67
+ export function staleNativeDistSource(pkgRoot: string): string | undefined {
68
+ const dist = path.join(pkgRoot, "dist", "esm", "index.native.js");
69
+ if (!fs.existsSync(dist)) return undefined;
70
+ const builtAt = fs.statSync(dist).mtimeMs;
71
+ const newest = newestSourceFile(path.join(pkgRoot, "src"));
72
+ return newest && newest.mtimeMs > builtAt ? newest.file : undefined;
73
+ }
74
+
75
+ /**
76
+ * The entry a NATIVE bundle compiles for a workspace package: its built
77
+ * `dist/esm/index.native.js` when that is current, else its source when it
78
+ * was never built. A STALE dist is refused, never silently used and never
79
+ * swapped for source: One 7.43.0 (FZ-7844) bundled an Oct 21 components dist
80
+ * that predates TabBar next to current app source rendering <TabBar>,
81
+ * rolldown shimmed the export as `TabBar = void 0`, and every tab screen threw
82
+ * "Element type is invalid". Falling back to source instead would change the
83
+ * bundle's shape at publish time (source pulls in modules the native dist
84
+ * never reached), so the bundler stops and says which package to rebuild.
85
+ */
86
+ export function resolveWorkspaceNativeEntry(pkgRoot: string): string | undefined {
87
+ const dist = path.join(pkgRoot, "dist", "esm", "index.native.js");
88
+ if (fs.existsSync(dist)) {
89
+ const changed = staleNativeDistSource(pkgRoot);
90
+ if (changed) {
91
+ throw new Error(
92
+ `${path.relative(pkgRoot, dist)} in ${pkgRoot} is older than ${path.relative(pkgRoot, changed)}: ` +
93
+ "the native bundle would ship exports that no longer match the source. " +
94
+ `Rebuild it first: pnpm --dir ${pkgRoot} build`,
95
+ );
96
+ }
97
+ return dist;
98
+ }
99
+ return [path.join(pkgRoot, "src", "index.ts"), path.join(pkgRoot, "src", "index.tsx")].find((c) =>
100
+ fs.existsSync(c),
101
+ );
102
+ }
103
+
33
104
  /**
34
105
  * Main entry file for Vite/Storybook-style “compile from source” aliases.
35
106
  *
package/types/vite.d.ts CHANGED
@@ -159,7 +159,7 @@ export interface CreateViteConfigOptions {
159
159
  * ```
160
160
  */
161
161
  export declare function createViteConfig(options?: CreateViteConfigOptions): UserConfig;
162
- export { discoverPublicPackageRoots, publicPackageViteSourceAliases, } from "./workspacePublicPackages.js";
162
+ export { discoverPublicPackageRoots, publicPackageViteSourceAliases, resolveWorkspaceNativeEntry, staleNativeDistSource, } from "./workspacePublicPackages.js";
163
163
  export { publicConfigPlugin, readPublicConfigKeys, type PublicConfigPluginOptions, } from "./publicConfig.js";
164
164
  export { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
165
165
  //# sourceMappingURL=vite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,EACJ,OAAO,GACP;QACE;;;WAGG;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEN;;OAEG;IACH,IAAI,CAAC,EACD,OAAO,GACP;QACE;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAEjB;;WAEG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IAEN;;;OAGG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,GAAG,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QAC/B,MAAM,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,CAAC;IAEN;;OAEG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ;;;;;;WAMG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CA6UlF;AAED,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,EACJ,OAAO,GACP;QACE;;;WAGG;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEN;;OAEG;IACH,IAAI,CAAC,EACD,OAAO,GACP;QACE;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAEjB;;WAEG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IAEN;;;OAGG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,GAAG,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QAC/B,MAAM,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,CAAC;IAEN;;OAEG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ;;;;;;WAMG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CA4UlF;AAED,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -7,6 +7,26 @@
7
7
  * `tamagui-workspace-paths.generated.json` (see `scripts/generate-tamagui-workspace-paths.mjs`).
8
8
  */
9
9
  export declare function discoverPublicPackageRoots(workspaceRoot: string): Map<string, string>;
10
+ /**
11
+ * The source file that makes a package's `dist/esm/index.native.js` stale: one
12
+ * changed after that dist was built. A dist is a snapshot of the last package
13
+ * build, and git rewrites every file a pull or checkout changes, so a source
14
+ * file newer than the dist means the dist no longer matches it. Undefined when
15
+ * the dist is current or absent. Spec, test and story files don't count.
16
+ */
17
+ export declare function staleNativeDistSource(pkgRoot: string): string | undefined;
18
+ /**
19
+ * The entry a NATIVE bundle compiles for a workspace package: its built
20
+ * `dist/esm/index.native.js` when that is current, else its source when it
21
+ * was never built. A STALE dist is refused, never silently used and never
22
+ * swapped for source: One 7.43.0 (FZ-7844) bundled an Oct 21 components dist
23
+ * that predates TabBar next to current app source rendering <TabBar>,
24
+ * rolldown shimmed the export as `TabBar = void 0`, and every tab screen threw
25
+ * "Element type is invalid". Falling back to source instead would change the
26
+ * bundle's shape at publish time (source pulls in modules the native dist
27
+ * never reached), so the bundler stops and says which package to rebuild.
28
+ */
29
+ export declare function resolveWorkspaceNativeEntry(pkgRoot: string): string | undefined;
10
30
  /**
11
31
  * Main entry file for Vite/Storybook-style “compile from source” aliases.
12
32
  *
@@ -1 +1 @@
1
- {"version":3,"file":"workspacePublicPackages.d.ts","sourceRoot":"","sources":["../src/workspacePublicPackages.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBrF;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB3E;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAyB3F;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5F;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuB5F"}
1
+ {"version":3,"file":"workspacePublicPackages.d.ts","sourceRoot":"","sources":["../src/workspacePublicPackages.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBrF;AA6BD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB/E;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB3E;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAyB3F;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5F;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuB5F"}