@multiplatform.one/config 7.28.1 → 7.29.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/README.md CHANGED
@@ -69,10 +69,11 @@ and needs no rules block of its own (MPO-23):
69
69
 
70
70
  oxlint resolves `extends` as a file path, not a package name, hence the
71
71
  `node_modules` prefix. The preset loads the plugin, turns
72
- `no-hex-literals` and `no-raw-typography` on as errors, turns
73
- `no-chrome-props`, `no-raw-geometry` and `no-child-margin` on as warnings
74
- while their burndowns run, and exempts specs and `tests/` trees, which assert
75
- computed colours, from all but `no-raw-typography`. A project that wants its
72
+ `no-hex-literals`, `no-raw-typography` and `no-parameter-properties` on as
73
+ errors, turns `no-chrome-props`, `no-raw-geometry` and `no-child-margin` on
74
+ as warnings while their burndowns run, and exempts specs and `tests/` trees,
75
+ which assert computed colours, from all but `no-raw-typography` and
76
+ `no-parameter-properties`. A project that wants its
76
77
  own levels keeps the older one-line form,
77
78
  `{ "jsPlugins": ["@multiplatform.one/config/oxlint"] }`, and writes its own
78
79
  rules block (mpo's own `oxlintrc.json` turns `no-hex-literals` on for
@@ -101,6 +102,15 @@ exempted the 73 files carrying 133 hits when it was switched on is empty, and
101
102
  `scripts/child-margin-ratchet.spec.mjs` holds its ceiling at 0, so a file
102
103
  added back to it fails the spec.
103
104
 
105
+ `no-parameter-properties` (MPO-455) fails a TypeScript constructor
106
+ parameter property, `constructor(readonly hash: Hash)` and its `private`,
107
+ `protected`, `public` and `override` spellings. vxrn's native dev bundle
108
+ lowers a class to a function and can leave the modifier on that function's
109
+ parameter, and rolldown then refuses the whole bundle with `'readonly'
110
+ modifier cannot appear on a parameter`. Declare the field and assign it in
111
+ the constructor. mpo's own `oxlintrc.json` runs it as an error over the
112
+ whole tree.
113
+
104
114
  ### Lint (node-script checks)
105
115
 
106
116
  CSS-in-apps, features-twin, silent token no-ops, the LC-65 size-recipe
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { t as createViteConfig } from "./vite-CS7fRWSI.js";
2
- import { t as createStorybookViteConfig } from "./storybook-BqMQy98f.js";
3
- import { n as tamaguiWorkspacePathsFile, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
1
+ import { t as createViteConfig } from "./vite-DZwt8mk9.js";
2
+ import { n as tamaguiWorkspacePathsFile, r as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-2mDESDbT.js";
3
+ import { t as createStorybookViteConfig } from "./storybook-CWDetyRO.js";
4
4
  import { createVitestConfig } from "./vitest.js";
5
5
 
6
- export { createStorybookViteConfig, createViteConfig, createVitestConfig, ensureTamaguiWorkspacePaths, tamaguiWorkspacePathsFile };
6
+ export { createStorybookViteConfig, createViteConfig, createVitestConfig, ensureTamaguiWorkspacePaths, phosphorTokenColorPlugin, tamaguiWorkspacePathsFile };
@@ -1,5 +1,5 @@
1
- import { i as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B9ho3BjU.js";
2
- import { t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
1
+ import { r as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-2mDESDbT.js";
2
+ import { i as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
@@ -185,7 +185,11 @@ function createStorybookViteConfig(options = {}) {
185
185
  "@react-navigation/native-stack"
186
186
  ]
187
187
  },
188
- plugins: [nodeAsyncHooksStub(), ...options.plugins || []]
188
+ plugins: [
189
+ nodeAsyncHooksStub(),
190
+ phosphorTokenColorPlugin(),
191
+ ...options.plugins || []
192
+ ]
189
193
  };
190
194
  }
191
195
  /**
package/lib/storybook.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as createStorybookViteConfig } from "./storybook-BqMQy98f.js";
1
+ import { t as createStorybookViteConfig } from "./storybook-CWDetyRO.js";
2
2
 
3
3
  export { createStorybookViteConfig };
@@ -0,0 +1,161 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { execFileSync } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ //#region src/phosphorTokenColor.ts
7
+ const virtualPrefix = "\0mpo-phosphor-token-icon-base:";
8
+ const iconBaseFilter = /IconBase\.es\.js$/;
9
+ const phosphorLibFile = /\/@phosphor-icons\/react\/dist\/lib\/IconBase\.es\.js$/;
10
+ const virtualIdFilter = new RegExp(`^${virtualPrefix}`);
11
+ /**
12
+ * Where a caller's `color` becomes a paint. Tamagui tokens ("$color9",
13
+ * "$red11") resolve through the theme in scope, as the native IconBase shim
14
+ * does (MPO-67); anything else passes through untouched. A token the theme
15
+ * does not carry takes the theme's ink, the LC-84 fallback resolveGlyphPaint
16
+ * uses, instead of reaching `fill` as an invalid paint that draws black. The
17
+ * rule is inlined, not imported from @multiplatform.one/theme: this module is
18
+ * bundled into the optimized phosphor chunk, where a workspace import would
19
+ * freeze a stale second copy of the theme package.
20
+ */
21
+ function tokenColorIconBaseSource(originalIconBase) {
22
+ return `import { createElement, forwardRef } from "react";
23
+ import { useTheme } from "@tamagui/core";
24
+ import IconBase from ${JSON.stringify(originalIconBase)};
25
+
26
+ const isToken = (color) => typeof color === "string" && color.charAt(0) === "$";
27
+
28
+ const paintOf = (entry) => {
29
+ const value = entry != null && typeof entry === "object" ? entry.val : entry;
30
+ return typeof value === "string" && value.length > 0 && !isToken(value) ? value : undefined;
31
+ };
32
+
33
+ const ThemeColorIconBase = forwardRef(function ThemeColorIconBase(props, ref) {
34
+ const theme = useTheme() || {};
35
+ const color =
36
+ paintOf(theme[props.color.slice(1)]) ??
37
+ paintOf(theme.color) ??
38
+ paintOf(theme.color12) ??
39
+ paintOf(theme.color11);
40
+ return createElement(IconBase, { ...props, ref, color });
41
+ });
42
+
43
+ const TokenColorIconBase = forwardRef(function TokenColorIconBase(props, ref) {
44
+ return createElement(isToken(props.color) ? ThemeColorIconBase : IconBase, { ...props, ref });
45
+ });
46
+ TokenColorIconBase.displayName = "IconBase";
47
+
48
+ export default TokenColorIconBase;
49
+ `;
50
+ }
51
+ function toPosix(file) {
52
+ return file.replace(/\\/g, "/");
53
+ }
54
+ function resolvePhosphorIconBase(id, importer) {
55
+ if (!importer || importer.startsWith("\0")) return null;
56
+ if (!toPosix(importer).includes("/@phosphor-icons/react/dist/")) return null;
57
+ const target = toPosix(id.startsWith(".") ? path.resolve(path.dirname(importer), id) : id);
58
+ return phosphorLibFile.test(target) ? virtualPrefix + target : null;
59
+ }
60
+ function loadTokenColorIconBase(id) {
61
+ return id.startsWith(virtualPrefix) ? tokenColorIconBaseSource(id.slice(30)) : null;
62
+ }
63
+ function rolldownTokenColorPlugin() {
64
+ return {
65
+ name: "mpo-phosphor-token-color:deps",
66
+ resolveId: {
67
+ filter: { id: iconBaseFilter },
68
+ handler: resolvePhosphorIconBase
69
+ },
70
+ load: {
71
+ filter: { id: virtualIdFilter },
72
+ handler: loadTokenColorIconBase
73
+ }
74
+ };
75
+ }
76
+ /**
77
+ * Resolves Tamagui tokens handed to `@phosphor-icons/react` glyphs on web
78
+ * (MPO-450), the web twin of apps/storybook-expo's native IconBase shim.
79
+ *
80
+ * Every phosphor icon renders through `dist/lib/IconBase.es.js`, which writes
81
+ * its `color` prop straight into `<svg fill>`. Callers across the catalog pass
82
+ * Tamagui tokens there, and a browser drops "$color9" as a paint, so the glyph
83
+ * fell back to black and vanished on the dark scheme. This swaps that one
84
+ * module for a wrapper that resolves the token first, so no call site has to.
85
+ *
86
+ * The swap runs in the plugin pipeline (build, vitest with phosphor inlined)
87
+ * and inside the dev dep optimizer, which bundles phosphor with rolldown and
88
+ * never consults Vite plugins for a dependency's relative imports.
89
+ */
90
+ function phosphorTokenColorPlugin() {
91
+ return {
92
+ name: "mpo-phosphor-token-color",
93
+ enforce: "pre",
94
+ configEnvironment() {
95
+ return { optimizeDeps: { rolldownOptions: { plugins: [rolldownTokenColorPlugin()] } } };
96
+ },
97
+ resolveId: {
98
+ filter: { id: iconBaseFilter },
99
+ handler: resolvePhosphorIconBase
100
+ },
101
+ load: {
102
+ filter: { id: virtualIdFilter },
103
+ handler: loadTokenColorIconBase
104
+ }
105
+ };
106
+ }
107
+
108
+ //#endregion
109
+ //#region src/tamaguiWorkspacePaths.ts
110
+ const tamaguiWorkspacePathsFile = "tsconfig/tamagui-workspace-paths.generated.json";
111
+ const generatorFile = "../../scripts/generate-tamagui-workspace-paths.mjs";
112
+ function thisPackageDir() {
113
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
114
+ }
115
+ function ensureTamaguiWorkspacePaths(options = {}) {
116
+ const { configPackageDir = thisPackageDir(), log = console.warn } = options;
117
+ const generatedPath = path.resolve(configPackageDir, tamaguiWorkspacePathsFile);
118
+ const generatorPath = path.resolve(configPackageDir, generatorFile);
119
+ if (fs.existsSync(generatedPath)) return {
120
+ generatedPath,
121
+ generatorPath,
122
+ status: "present"
123
+ };
124
+ if (!fs.existsSync(generatorPath)) return {
125
+ generatedPath,
126
+ generatorPath,
127
+ status: "no-generator"
128
+ };
129
+ try {
130
+ execFileSync(process.execPath, [generatorPath], { stdio: [
131
+ "ignore",
132
+ "ignore",
133
+ "pipe"
134
+ ] });
135
+ } catch (error) {
136
+ const message = error instanceof Error ? error.message : String(error);
137
+ log?.(`[@multiplatform.one/config] could not regenerate ${tamaguiWorkspacePathsFile}: ${message}`);
138
+ return {
139
+ generatedPath,
140
+ generatorPath,
141
+ status: "failed"
142
+ };
143
+ }
144
+ if (!fs.existsSync(generatedPath)) {
145
+ log?.(`[@multiplatform.one/config] ${path.basename(generatorPath)} ran but did not write ${tamaguiWorkspacePathsFile}`);
146
+ return {
147
+ generatedPath,
148
+ generatorPath,
149
+ status: "failed"
150
+ };
151
+ }
152
+ log?.(`[@multiplatform.one/config] ${tamaguiWorkspacePathsFile} was missing (the root prepare script did not run; a global ignore-scripts skips it). Regenerated it. Run \`pnpm generate:tamagui-paths\` after install to skip this step.`);
153
+ return {
154
+ generatedPath,
155
+ generatorPath,
156
+ status: "generated"
157
+ };
158
+ }
159
+
160
+ //#endregion
161
+ export { tamaguiWorkspacePathsFile as n, phosphorTokenColorPlugin as r, ensureTamaguiWorkspacePaths as t };
@@ -1,5 +1,5 @@
1
- import { i as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B9ho3BjU.js";
2
- import { t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
1
+ import { r as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-2mDESDbT.js";
2
+ import { i as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
@@ -229,6 +229,7 @@ function createViteConfig(options = {}) {
229
229
  if (publicConfigKeys.length > 0) bakePublicConfig(publicConfigKeys);
230
230
  const vitePlugins = [];
231
231
  vitePlugins.push(preferBuiltWorkspaceEntryPlugin());
232
+ vitePlugins.push(phosphorTokenColorPlugin());
232
233
  if (publicConfigFile) vitePlugins.push(publicConfigPlugin({
233
234
  file: publicConfigFile,
234
235
  projectRoot,
package/lib/vite.js CHANGED
@@ -1,4 +1,5 @@
1
- import { n as publicConfigPlugin, r as readPublicConfigKeys, t as createViteConfig } from "./vite-CS7fRWSI.js";
2
- import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B9ho3BjU.js";
1
+ import { n as publicConfigPlugin, r as readPublicConfigKeys, t as createViteConfig } from "./vite-DZwt8mk9.js";
2
+ import { r as phosphorTokenColorPlugin } from "./tamaguiWorkspacePaths-2mDESDbT.js";
3
+ import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-ClSmVngx.js";
3
4
 
4
- export { createViteConfig, discoverPublicPackageRoots, publicConfigPlugin, publicPackageViteSourceAliases, readPublicConfigKeys };
5
+ export { createViteConfig, discoverPublicPackageRoots, phosphorTokenColorPlugin, publicConfigPlugin, publicPackageViteSourceAliases, readPublicConfigKeys };
package/lib/vitest.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
1
+ import { r as phosphorTokenColorPlugin, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-2mDESDbT.js";
2
2
  import Module from "node:module";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
@@ -55,7 +55,7 @@ function createVitestConfig(options = {}) {
55
55
  "browser"
56
56
  ], include, exclude, passWithNoTests } = options;
57
57
  const rootNodeModules = _rootNodeModules || path.resolve(process.cwd(), "../../node_modules");
58
- const plugins = [react({ jsxRuntime: "automatic" })];
58
+ const plugins = [react({ jsxRuntime: "automatic" }), phosphorTokenColorPlugin()];
59
59
  if (replaceReactNative) plugins.push({
60
60
  name: "replace-react-native",
61
61
  transform(code, id) {
@@ -131,6 +131,7 @@ function createVitestConfig(options = {}) {
131
131
  /@tanstack\//,
132
132
  "use-sync-external-store",
133
133
  "react-i18next",
134
+ "@phosphor-icons/react",
134
135
  ...inlineDeps
135
136
  ],
136
137
  ...externalDeps.length > 0 ? { external: [pinReactRequireSetup, ...externalDeps] } : { external: [pinReactRequireSetup] },
package/oxlint.json CHANGED
@@ -5,7 +5,8 @@
5
5
  "mpo-conventions/no-raw-typography": "error",
6
6
  "mpo-conventions/no-chrome-props": "warn",
7
7
  "mpo-conventions/no-raw-geometry": "warn",
8
- "mpo-conventions/no-child-margin": "warn"
8
+ "mpo-conventions/no-child-margin": "warn",
9
+ "mpo-conventions/no-parameter-properties": "error"
9
10
  },
10
11
  "overrides": [
11
12
  {
package/oxlint.mjs CHANGED
@@ -32,6 +32,12 @@
32
32
  * JSX. The outermost element of a tree is left alone (a file cannot see
33
33
  * whether its root is a screen's), and element names the spec's
34
34
  * `## Slot margins` SLOT-MARGIN row lists are the declared slots (MPO-23).
35
+ * mpo-conventions/no-parameter-properties — ban TypeScript constructor
36
+ * parameter properties (`constructor(readonly hash: Hash)`). vxrn's
37
+ * native dev bundle lowers a class to a function and can leave the
38
+ * modifier on that function's parameter, and rolldown then refuses the
39
+ * whole bundle with `'readonly' modifier cannot appear on a parameter`
40
+ * (MPO-455). Declare the field and assign it in the constructor.
35
41
  */
36
42
 
37
43
  import { isDrawingFile, readDrawingRegistry } from "./drawing-registry.mjs";
@@ -615,6 +621,29 @@ export const plugin = {
615
621
  };
616
622
  },
617
623
  },
624
+ "no-parameter-properties": {
625
+ meta: {
626
+ type: "problem",
627
+ docs: {
628
+ description:
629
+ "Ban TypeScript constructor parameter properties, which vxrn's native dev bundle can leave on a lowered function parameter (MPO-455).",
630
+ },
631
+ schema: [],
632
+ messages: {
633
+ parameterProperty:
634
+ "Constructor parameter property {{name}}. vxrn's native bundle can leave its modifier on a function parameter and rolldown then refuses the whole bundle. Declare the field and assign it in the constructor.",
635
+ },
636
+ },
637
+ create(context) {
638
+ return {
639
+ TSParameterProperty(node) {
640
+ const parameter = node.parameter ?? {};
641
+ const name = parameter.name ?? parameter.left?.name ?? "parameter";
642
+ context.report({ node, messageId: "parameterProperty", data: { name } });
643
+ },
644
+ };
645
+ },
646
+ },
618
647
  },
619
648
  };
620
649
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/config",
3
- "version": "7.28.1",
3
+ "version": "7.29.0",
4
4
  "description": "Shared build and test configuration presets for multiplatform.one",
5
5
  "keywords": [
6
6
  "config",
@@ -99,7 +99,7 @@
99
99
  "access": "public"
100
100
  },
101
101
  "dependencies": {
102
- "@multiplatform.one/utils": "7.28.1",
102
+ "@multiplatform.one/utils": "7.29.0",
103
103
  "@tamagui/vite-plugin": "2.7.6",
104
104
  "@vitejs/plugin-react": "^6.0.1",
105
105
  "dotenv": "^17.4.2",
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { createStorybookViteConfig, type CreateStorybookViteConfigOptions } from "./storybook";
2
2
  export { createViteConfig, type CreateViteConfigOptions } from "./vite";
3
3
  export { createVitestConfig, type CreateVitestConfigOptions } from "./vitest";
4
+ export { phosphorTokenColorPlugin } from "./phosphorTokenColor";
4
5
  export {
5
6
  ensureTamaguiWorkspacePaths,
6
7
  tamaguiWorkspacePathsFile,
@@ -62,4 +62,34 @@ describe("oxlint preset (MPO-23 2A)", () => {
62
62
  expect(out).toContain("Swatch.tsx");
63
63
  expect(out).not.toContain("Swatch.spec.tsx");
64
64
  });
65
+
66
+ // MPO-455: vxrn's native dev bundle left `readonly` on the parameter of the
67
+ // function it lowered an Error subclass to, and rolldown refused the bundle.
68
+ it("fails a constructor parameter property of every modifier and passes declared fields", () => {
69
+ const out = lint({
70
+ "public/demo/src/errors.ts": [
71
+ "class Base {}",
72
+ "export class Pending extends Base {",
73
+ " constructor(readonly hash: string, private chainId = 1, protected nonce?: number, public override tag?: string) {",
74
+ " super();",
75
+ " }",
76
+ "}",
77
+ "",
78
+ ].join("\n"),
79
+ "public/demo/src/fields.ts": [
80
+ "export class Declared {",
81
+ " readonly hash: string;",
82
+ " constructor(hash: string) {",
83
+ " this.hash = hash;",
84
+ " }",
85
+ "}",
86
+ "",
87
+ ].join("\n"),
88
+ });
89
+ for (const name of ["hash", "chainId", "nonce", "tag"]) {
90
+ expect(out).toContain(`Constructor parameter property ${name}.`);
91
+ }
92
+ expect(out.match(/mpo-conventions\(no-parameter-properties\)/g)).toHaveLength(4);
93
+ expect(out).not.toContain("fields.ts");
94
+ });
65
95
  });
@@ -0,0 +1,90 @@
1
+ /**
2
+ * phosphorTokenColorPlugin (MPO-450): the one place web phosphor glyphs get a
3
+ * Tamagui token resolved to a paint. The render proof lives in
4
+ * public/components/src/icons/phosphorTokenColor.spec.tsx; these hold the
5
+ * swap to phosphor's own IconBase and to every web config factory.
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+ import { resolveConfig, type Plugin } from "vite";
9
+ import { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
10
+ import { createStorybookViteConfig } from "./storybook.js";
11
+ import { createViteConfig } from "./vite.js";
12
+ import { createVitestConfig } from "./vitest.js";
13
+
14
+ const dist = "/repo/node_modules/@phosphor-icons/react/dist";
15
+ const iconBase = `${dist}/lib/IconBase.es.js`;
16
+
17
+ type Handler = (id: string, importer?: string) => string | null;
18
+
19
+ function hook(plugin: { resolveId?: unknown; load?: unknown }, name: "resolveId" | "load") {
20
+ const value = plugin[name] as { handler: Handler } | Handler;
21
+ return typeof value === "function" ? value : value.handler;
22
+ }
23
+
24
+ function hasPlugin(plugins: unknown): boolean {
25
+ return (plugins as unknown[])
26
+ .flat(Infinity)
27
+ .some((p) => (p as { name?: string } | null)?.name === "mpo-phosphor-token-color");
28
+ }
29
+
30
+ describe("phosphorTokenColorPlugin", () => {
31
+ const plugin = phosphorTokenColorPlugin();
32
+ const resolveId = hook(plugin, "resolveId");
33
+ const load = hook(plugin, "load") as (id: string) => string | null;
34
+
35
+ it("swaps the IconBase every csr glyph and the barrel import", () => {
36
+ const fromIcon = resolveId("../lib/IconBase.es.js", `${dist}/csr/Heart.es.js`);
37
+ const fromBarrel = resolveId("./lib/IconBase.es.js", `${dist}/index.es.js?v=1a2b`);
38
+ expect(fromIcon?.startsWith("\0mpo-phosphor-token-icon-base:")).toBe(true);
39
+ expect(fromIcon?.endsWith(iconBase)).toBe(true);
40
+ expect(fromBarrel).toBe(fromIcon);
41
+ });
42
+
43
+ it("leaves other importers, the ssr base and its own wrapper alone", () => {
44
+ expect(resolveId("../lib/IconBase.es.js", "/repo/src/icons/csr/Heart.es.js")).toBeNull();
45
+ expect(resolveId("../lib/SSRBase.es.js", `${dist}/ssr/Heart.es.js`)).toBeNull();
46
+ const wrapper = resolveId("../lib/IconBase.es.js", `${dist}/csr/Heart.es.js`) ?? "";
47
+ expect(resolveId(iconBase, wrapper)).toBeNull();
48
+ });
49
+
50
+ it("loads a wrapper that resolves tokens through the theme and renders the original", () => {
51
+ const wrapper = resolveId("../lib/IconBase.es.js", `${dist}/csr/Heart.es.js`) ?? "";
52
+ const code = load(wrapper) ?? "";
53
+ expect(code).toContain(`import IconBase from ${JSON.stringify(iconBase)}`);
54
+ expect(code).toContain('import { useTheme } from "@tamagui/core"');
55
+ expect(load("/repo/src/other.ts")).toBeNull();
56
+ });
57
+
58
+ it("reaches the dev dep optimizer, which bundles phosphor without Vite plugins", () => {
59
+ const configEnvironment = plugin.configEnvironment as unknown as () => {
60
+ optimizeDeps: { rolldownOptions: { plugins: Array<Plugin> } };
61
+ };
62
+ const [depPlugin] = configEnvironment().optimizeDeps.rolldownOptions.plugins;
63
+ const depResolve = hook(depPlugin, "resolveId");
64
+ expect(depResolve("../lib/IconBase.es.js", `${dist}/csr/Heart.es.js`)).toBe(
65
+ resolveId("../lib/IconBase.es.js", `${dist}/csr/Heart.es.js`),
66
+ );
67
+ });
68
+
69
+ it("lands in the client and ssr optimizers once Vite resolves the config", async () => {
70
+ const config = await resolveConfig(
71
+ { configFile: false, logLevel: "silent", plugins: [phosphorTokenColorPlugin()] },
72
+ "serve",
73
+ );
74
+ for (const name of ["client", "ssr"]) {
75
+ const plugins = (config.environments[name]?.optimizeDeps.rolldownOptions?.plugins ??
76
+ []) as Array<{ name?: string }>;
77
+ expect(plugins.map((p) => p?.name)).toContain("mpo-phosphor-token-color:deps");
78
+ }
79
+ });
80
+
81
+ it("is carried by every web config factory", () => {
82
+ expect(hasPlugin(createViteConfig({ tamagui: false }).plugins)).toBe(true);
83
+ expect(hasPlugin(createStorybookViteConfig().plugins)).toBe(true);
84
+ const vitest = createVitestConfig({ tamaguiConfig: false, dedupeReact: false });
85
+ expect(hasPlugin(vitest.plugins)).toBe(true);
86
+ const inline = (vitest as { test: { server: { deps: { inline: unknown[] } } } }).test.server
87
+ .deps.inline;
88
+ expect(inline).toContain("@phosphor-icons/react");
89
+ });
90
+ });
@@ -0,0 +1,112 @@
1
+ import path from "node:path";
2
+ import type { Plugin } from "vite";
3
+
4
+ const virtualPrefix = "\0mpo-phosphor-token-icon-base:";
5
+ const iconBaseFilter = /IconBase\.es\.js$/;
6
+ const phosphorLibFile = /\/@phosphor-icons\/react\/dist\/lib\/IconBase\.es\.js$/;
7
+ const virtualIdFilter = new RegExp(`^${virtualPrefix}`);
8
+
9
+ /**
10
+ * Where a caller's `color` becomes a paint. Tamagui tokens ("$color9",
11
+ * "$red11") resolve through the theme in scope, as the native IconBase shim
12
+ * does (MPO-67); anything else passes through untouched. A token the theme
13
+ * does not carry takes the theme's ink, the LC-84 fallback resolveGlyphPaint
14
+ * uses, instead of reaching `fill` as an invalid paint that draws black. The
15
+ * rule is inlined, not imported from @multiplatform.one/theme: this module is
16
+ * bundled into the optimized phosphor chunk, where a workspace import would
17
+ * freeze a stale second copy of the theme package.
18
+ */
19
+ function tokenColorIconBaseSource(originalIconBase: string): string {
20
+ return `import { createElement, forwardRef } from "react";
21
+ import { useTheme } from "@tamagui/core";
22
+ import IconBase from ${JSON.stringify(originalIconBase)};
23
+
24
+ const isToken = (color) => typeof color === "string" && color.charAt(0) === "$";
25
+
26
+ const paintOf = (entry) => {
27
+ const value = entry != null && typeof entry === "object" ? entry.val : entry;
28
+ return typeof value === "string" && value.length > 0 && !isToken(value) ? value : undefined;
29
+ };
30
+
31
+ const ThemeColorIconBase = forwardRef(function ThemeColorIconBase(props, ref) {
32
+ const theme = useTheme() || {};
33
+ const color =
34
+ paintOf(theme[props.color.slice(1)]) ??
35
+ paintOf(theme.color) ??
36
+ paintOf(theme.color12) ??
37
+ paintOf(theme.color11);
38
+ return createElement(IconBase, { ...props, ref, color });
39
+ });
40
+
41
+ const TokenColorIconBase = forwardRef(function TokenColorIconBase(props, ref) {
42
+ return createElement(isToken(props.color) ? ThemeColorIconBase : IconBase, { ...props, ref });
43
+ });
44
+ TokenColorIconBase.displayName = "IconBase";
45
+
46
+ export default TokenColorIconBase;
47
+ `;
48
+ }
49
+
50
+ function toPosix(file: string): string {
51
+ return file.replace(/\\/g, "/");
52
+ }
53
+
54
+ function resolvePhosphorIconBase(id: string, importer: string | undefined): string | null {
55
+ if (!importer || importer.startsWith("\0")) return null;
56
+ const importerPath = toPosix(importer);
57
+ if (!importerPath.includes("/@phosphor-icons/react/dist/")) return null;
58
+ const target = toPosix(id.startsWith(".") ? path.resolve(path.dirname(importer), id) : id);
59
+ return phosphorLibFile.test(target) ? virtualPrefix + target : null;
60
+ }
61
+
62
+ function loadTokenColorIconBase(id: string): string | null {
63
+ return id.startsWith(virtualPrefix)
64
+ ? tokenColorIconBaseSource(id.slice(virtualPrefix.length))
65
+ : null;
66
+ }
67
+
68
+ function rolldownTokenColorPlugin() {
69
+ return {
70
+ name: "mpo-phosphor-token-color:deps",
71
+ resolveId: {
72
+ filter: { id: iconBaseFilter },
73
+ handler: resolvePhosphorIconBase,
74
+ },
75
+ load: {
76
+ filter: { id: virtualIdFilter },
77
+ handler: loadTokenColorIconBase,
78
+ },
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Resolves Tamagui tokens handed to `@phosphor-icons/react` glyphs on web
84
+ * (MPO-450), the web twin of apps/storybook-expo's native IconBase shim.
85
+ *
86
+ * Every phosphor icon renders through `dist/lib/IconBase.es.js`, which writes
87
+ * its `color` prop straight into `<svg fill>`. Callers across the catalog pass
88
+ * Tamagui tokens there, and a browser drops "$color9" as a paint, so the glyph
89
+ * fell back to black and vanished on the dark scheme. This swaps that one
90
+ * module for a wrapper that resolves the token first, so no call site has to.
91
+ *
92
+ * The swap runs in the plugin pipeline (build, vitest with phosphor inlined)
93
+ * and inside the dev dep optimizer, which bundles phosphor with rolldown and
94
+ * never consults Vite plugins for a dependency's relative imports.
95
+ */
96
+ export function phosphorTokenColorPlugin(): Plugin {
97
+ return {
98
+ name: "mpo-phosphor-token-color",
99
+ enforce: "pre",
100
+ configEnvironment() {
101
+ return { optimizeDeps: { rolldownOptions: { plugins: [rolldownTokenColorPlugin()] } } };
102
+ },
103
+ resolveId: {
104
+ filter: { id: iconBaseFilter },
105
+ handler: resolvePhosphorIconBase,
106
+ },
107
+ load: {
108
+ filter: { id: virtualIdFilter },
109
+ handler: loadTokenColorIconBase,
110
+ },
111
+ };
112
+ }
package/src/storybook.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import type { Plugin, UserConfig } from "vite";
4
+ import { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
4
5
  import { withSingletonDepAliases } from "./singletonDepAliases.js";
5
6
  import { unexportedDepAliases } from "./unexportedDepAliases.js";
6
7
  import { discoverPublicPackageRoots, resolvePackageMainSource } from "./workspacePublicPackages.js";
@@ -229,7 +230,7 @@ export function createStorybookViteConfig(
229
230
  "@react-navigation/native-stack",
230
231
  ],
231
232
  },
232
- plugins: [nodeAsyncHooksStub(), ...(options.plugins || [])],
233
+ plugins: [nodeAsyncHooksStub(), phosphorTokenColorPlugin(), ...(options.plugins || [])],
233
234
  };
234
235
  }
235
236
 
package/src/vite.ts CHANGED
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { lookupTamaguiModules } from "@multiplatform.one/utils/dev";
4
4
  import { discoverPublicPackageRoots } from "./workspacePublicPackages.js";
5
+ import { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
5
6
  import { withSingletonDepAliases } from "./singletonDepAliases.js";
6
7
  import { ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths.js";
7
8
  import {
@@ -229,6 +230,7 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
229
230
 
230
231
  const vitePlugins: UserConfig["plugins"] = [];
231
232
  vitePlugins.push(preferBuiltWorkspaceEntryPlugin());
233
+ vitePlugins.push(phosphorTokenColorPlugin());
232
234
  if (publicConfigFile) {
233
235
  vitePlugins.push(
234
236
  publicConfigPlugin({ file: publicConfigFile, projectRoot, keys: inlinePublicConfigKeys }),
@@ -529,6 +531,7 @@ export {
529
531
  readPublicConfigKeys,
530
532
  type PublicConfigPluginOptions,
531
533
  } from "./publicConfig.js";
534
+ export { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
532
535
 
533
536
  /**
534
537
  * Aliases react-native to react-native-web in the SSR environment.
package/src/vitest.ts CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
5
5
  import { tamaguiPlugin } from "@tamagui/vite-plugin";
6
6
  import react from "@vitejs/plugin-react";
7
7
  import type { UserConfig } from "vite";
8
+ import { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
8
9
  import { ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths.js";
9
10
 
10
11
  const pinReactRequireSetup = path.resolve(
@@ -187,6 +188,7 @@ export function createVitestConfig(options: CreateVitestConfigOptions = {}): Use
187
188
  react({
188
189
  jsxRuntime: "automatic",
189
190
  }),
191
+ phosphorTokenColorPlugin(),
190
192
  ];
191
193
 
192
194
  // Replace react-native imports with react-native-web
@@ -309,6 +311,10 @@ export function createVitestConfig(options: CreateVitestConfigOptions = {}): Use
309
311
  // Native ESM bypasses both the Vite React alias and the CJS require
310
312
  // pin. Transform react-i18next so useTranslation shares the renderer.
311
313
  "react-i18next",
314
+ // Loaded natively, phosphor's IconBase never reaches the plugin
315
+ // pipeline, so a spec would see the raw token where the browser
316
+ // sees the resolved paint.
317
+ "@phosphor-icons/react",
312
318
  ...inlineDeps,
313
319
  ],
314
320
  ...(externalDeps.length > 0
package/types/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createStorybookViteConfig, type CreateStorybookViteConfigOptions } from "./storybook";
2
2
  export { createViteConfig, type CreateViteConfigOptions } from "./vite";
3
3
  export { createVitestConfig, type CreateVitestConfigOptions } from "./vitest";
4
+ export { phosphorTokenColorPlugin } from "./phosphorTokenColor";
4
5
  export { ensureTamaguiWorkspacePaths, tamaguiWorkspacePathsFile, type EnsureTamaguiWorkspacePathsOptions, type EnsureTamaguiWorkspacePathsResult, } from "./tamaguiWorkspacePaths";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,KAAK,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,QAAQ,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,KAAK,kCAAkC,EACvC,KAAK,iCAAiC,GACvC,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,KAAK,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,QAAQ,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,KAAK,kCAAkC,EACvC,KAAK,iCAAiC,GACvC,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Plugin } from "vite";
2
+ /**
3
+ * Resolves Tamagui tokens handed to `@phosphor-icons/react` glyphs on web
4
+ * (MPO-450), the web twin of apps/storybook-expo's native IconBase shim.
5
+ *
6
+ * Every phosphor icon renders through `dist/lib/IconBase.es.js`, which writes
7
+ * its `color` prop straight into `<svg fill>`. Callers across the catalog pass
8
+ * Tamagui tokens there, and a browser drops "$color9" as a paint, so the glyph
9
+ * fell back to black and vanished on the dark scheme. This swaps that one
10
+ * module for a wrapper that resolves the token first, so no call site has to.
11
+ *
12
+ * The swap runs in the plugin pipeline (build, vitest with phosphor inlined)
13
+ * and inside the dev dep optimizer, which bundles phosphor with rolldown and
14
+ * never consults Vite plugins for a dependency's relative imports.
15
+ */
16
+ export declare function phosphorTokenColorPlugin(): Plugin;
17
+ //# sourceMappingURL=phosphorTokenColor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phosphorTokenColor.d.ts","sourceRoot":"","sources":["../src/phosphorTokenColor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAgFnC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAgBjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"storybook.d.ts","sourceRoot":"","sources":["../src/storybook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAM/C,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,UAAU,CAqLZ"}
1
+ {"version":3,"file":"storybook.d.ts","sourceRoot":"","sources":["../src/storybook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAO/C,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,UAAU,CAqLZ"}
package/types/vite.d.ts CHANGED
@@ -161,4 +161,5 @@ export interface CreateViteConfigOptions {
161
161
  export declare function createViteConfig(options?: CreateViteConfigOptions): UserConfig;
162
162
  export { discoverPublicPackageRoots, publicPackageViteSourceAliases, } from "./workspacePublicPackages.js";
163
163
  export { publicConfigPlugin, readPublicConfigKeys, type PublicConfigPluginOptions, } from "./publicConfig.js";
164
+ export { phosphorTokenColorPlugin } from "./phosphorTokenColor.js";
164
165
  //# sourceMappingURL=vite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAcA,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,CAyUlF;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"}
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,CA0UlF;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 +1 @@
1
- {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAqDvC,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE/B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;IAE7C;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,UAAU,CAoNtF"}
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAsDvC,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE/B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;IAE7C;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,UAAU,CAyNtF"}
@@ -1,58 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { execFileSync } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
5
-
6
- //#region src/tamaguiWorkspacePaths.ts
7
- const tamaguiWorkspacePathsFile = "tsconfig/tamagui-workspace-paths.generated.json";
8
- const generatorFile = "../../scripts/generate-tamagui-workspace-paths.mjs";
9
- function thisPackageDir() {
10
- return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- }
12
- function ensureTamaguiWorkspacePaths(options = {}) {
13
- const { configPackageDir = thisPackageDir(), log = console.warn } = options;
14
- const generatedPath = path.resolve(configPackageDir, tamaguiWorkspacePathsFile);
15
- const generatorPath = path.resolve(configPackageDir, generatorFile);
16
- if (fs.existsSync(generatedPath)) return {
17
- generatedPath,
18
- generatorPath,
19
- status: "present"
20
- };
21
- if (!fs.existsSync(generatorPath)) return {
22
- generatedPath,
23
- generatorPath,
24
- status: "no-generator"
25
- };
26
- try {
27
- execFileSync(process.execPath, [generatorPath], { stdio: [
28
- "ignore",
29
- "ignore",
30
- "pipe"
31
- ] });
32
- } catch (error) {
33
- const message = error instanceof Error ? error.message : String(error);
34
- log?.(`[@multiplatform.one/config] could not regenerate ${tamaguiWorkspacePathsFile}: ${message}`);
35
- return {
36
- generatedPath,
37
- generatorPath,
38
- status: "failed"
39
- };
40
- }
41
- if (!fs.existsSync(generatedPath)) {
42
- log?.(`[@multiplatform.one/config] ${path.basename(generatorPath)} ran but did not write ${tamaguiWorkspacePathsFile}`);
43
- return {
44
- generatedPath,
45
- generatorPath,
46
- status: "failed"
47
- };
48
- }
49
- log?.(`[@multiplatform.one/config] ${tamaguiWorkspacePathsFile} was missing (the root prepare script did not run; a global ignore-scripts skips it). Regenerated it. Run \`pnpm generate:tamagui-paths\` after install to skip this step.`);
50
- return {
51
- generatedPath,
52
- generatorPath,
53
- status: "generated"
54
- };
55
- }
56
-
57
- //#endregion
58
- export { tamaguiWorkspacePathsFile as n, ensureTamaguiWorkspacePaths as t };