@multiplatform.one/config 7.6.3 → 7.7.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 +1 -1
- package/lib/{vite-W9hIeg2_.js → vite-Rv5b8Wxp.js} +108 -1
- package/lib/vite.js +1 -1
- package/lint.mjs +69 -0
- package/package.json +2 -2
- package/src/baseTsconfigTypes.spec.ts +42 -0
- package/src/emittingTsconfigPaths.spec.ts +85 -0
- package/src/lint.spec.ts +67 -0
- package/src/tamaguiWorkspacePaths.spec.ts +34 -0
- package/src/vite.ts +151 -1
- package/tsconfig/base.json +10 -1
- package/types/vite.d.ts.map +1 -1
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as createViteConfig } from "./vite-
|
|
1
|
+
import { t as createViteConfig } from "./vite-Rv5b8Wxp.js";
|
|
2
2
|
import { t as createStorybookViteConfig } from "./storybook-BnfZmzIL.js";
|
|
3
3
|
import { n as tamaguiWorkspacePathsFile, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
|
|
4
4
|
import { createVitestConfig } from "./vitest.js";
|
|
@@ -92,6 +92,7 @@ function createViteConfig(options = {}) {
|
|
|
92
92
|
vitePlugins.push(ssrReactNativeAliasPlugin());
|
|
93
93
|
vitePlugins.push(clientBrokenEsmPlugin());
|
|
94
94
|
registerNativeEngineIoBrowserTransports();
|
|
95
|
+
registerNativeSinglePackageInstances();
|
|
95
96
|
registerNativeWorkspaceEntries();
|
|
96
97
|
registerNativePhosphorIcons();
|
|
97
98
|
if (nativeFixes?.protoShadow !== false) registerNativeProtoShadowFix();
|
|
@@ -276,6 +277,107 @@ function clientBrokenEsmPlugin() {
|
|
|
276
277
|
* This re-asserts the browser redirect with no node_modules patch. Web is
|
|
277
278
|
* unaffected (engine.io is SSR-externalized and the web client already maps `browser`).
|
|
278
279
|
*/
|
|
280
|
+
/**
|
|
281
|
+
* Collapse duplicate PHYSICAL copies of the same package@version to one path
|
|
282
|
+
* in the NATIVE graph, so React (and every other stateful singleton) exists
|
|
283
|
+
* exactly once in the Hermes bundle.
|
|
284
|
+
*
|
|
285
|
+
* The workspace's node_modules is a MIXED install. `.npmrc` says
|
|
286
|
+
* `node-linker=hoisted`, so the root `node_modules/<pkg>` entries are real
|
|
287
|
+
* directories; but an earlier isolated install left a `node_modules/.pnpm`
|
|
288
|
+
* virtual store plus per-package symlink farms behind — e.g. each package's
|
|
289
|
+
* own `node_modules/react` still points at
|
|
290
|
+
* `../../../node_modules/.pnpm/react@19.2.5/node_modules/react`.
|
|
291
|
+
* Both trees are fully populated with byte-identical copies, and which one an
|
|
292
|
+
* importer reaches depends only on where that importer sits on disk:
|
|
293
|
+
*
|
|
294
|
+
* apps/one/app -> node_modules/.pnpm/react@19.2.5/node_modules/react
|
|
295
|
+
* public/forms/src -> node_modules/react (no local symlink)
|
|
296
|
+
*
|
|
297
|
+
* Rolldown resolves symlinks, so the two land as two DIFFERENT absolute paths
|
|
298
|
+
* and it bundles both. React 19's `useContext` is
|
|
299
|
+
* `ReactSharedInternals.H.useContext(Context)` — `H` is the dispatcher the
|
|
300
|
+
* renderer installs during render, and it is only ever set on the copy the
|
|
301
|
+
* renderer imported. A component from the other copy therefore reads `H` as
|
|
302
|
+
* null and the app dies with "Cannot read property 'useContext' of null".
|
|
303
|
+
* That is the whole bug; the same split had also duplicated react-native (427
|
|
304
|
+
* modules each), @tamagui/web, @react-navigation/core, one, scheduler and 58
|
|
305
|
+
* more packages.
|
|
306
|
+
*
|
|
307
|
+
* The rule: an id that resolves inside the virtual store is rewritten to the
|
|
308
|
+
* hoisted root copy when the root copy exists, carries the SAME version, and
|
|
309
|
+
* actually has the file. Version equality is the safety guard — genuine
|
|
310
|
+
* multi-version installs (two @babel/runtime, two viem) keep both copies.
|
|
311
|
+
*
|
|
312
|
+
* On a correctly installed tree this is a no-op by construction: `hoisted`
|
|
313
|
+
* leaves no `.pnpm` store to match, and `isolated` makes the root entry a
|
|
314
|
+
* symlink INTO the store, which realpaths back to the same directory (checked
|
|
315
|
+
* explicitly). It only fires on the mixed tree, and it is cheap because only
|
|
316
|
+
* BARE specifiers are inspected — once a package's entry is canonical, its own
|
|
317
|
+
* relative imports resolve inside the canonical copy for free.
|
|
318
|
+
*/
|
|
319
|
+
function registerNativeSinglePackageInstances() {
|
|
320
|
+
const g = globalThis;
|
|
321
|
+
const name = "vxrn-single-package-instances";
|
|
322
|
+
g.__vxrnAddNativePlugins = g.__vxrnAddNativePlugins ?? [];
|
|
323
|
+
if (g.__vxrnAddNativePlugins.some((p) => p?.name === name)) return;
|
|
324
|
+
const rootModules = path.join(findProjectRoot(), "node_modules");
|
|
325
|
+
const storePath = /[\\/]node_modules[\\/]\.pnpm[\\/][^\\/]+[\\/]node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)(?:[\\/](.*))?$/;
|
|
326
|
+
function versionOf(dir) {
|
|
327
|
+
try {
|
|
328
|
+
const raw = fs.readFileSync(path.join(dir, "package.json"), "utf8");
|
|
329
|
+
return JSON.parse(raw).version ?? null;
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const hoisted = /* @__PURE__ */ new Map();
|
|
335
|
+
function hoistedTwin(pkg, storeDir) {
|
|
336
|
+
if (!hoisted.has(pkg)) {
|
|
337
|
+
const rootDir = path.join(rootModules, pkg);
|
|
338
|
+
let twin = null;
|
|
339
|
+
if (fs.existsSync(rootDir)) {
|
|
340
|
+
const rootVersion = versionOf(rootDir);
|
|
341
|
+
const storeVersion = versionOf(storeDir);
|
|
342
|
+
if (rootVersion && rootVersion === storeVersion) try {
|
|
343
|
+
if (fs.realpathSync(rootDir) !== fs.realpathSync(storeDir)) twin = rootDir;
|
|
344
|
+
} catch {
|
|
345
|
+
twin = null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
hoisted.set(pkg, twin);
|
|
349
|
+
}
|
|
350
|
+
return hoisted.get(pkg) ?? null;
|
|
351
|
+
}
|
|
352
|
+
const seen = /* @__PURE__ */ new Map();
|
|
353
|
+
g.__vxrnAddNativePlugins.push({
|
|
354
|
+
name,
|
|
355
|
+
async resolveId(id, importer) {
|
|
356
|
+
if (!id || id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return null;
|
|
357
|
+
const key = `${id} ${importer ?? ""}`;
|
|
358
|
+
const cached = seen.get(key);
|
|
359
|
+
if (cached !== void 0) return cached;
|
|
360
|
+
let out = null;
|
|
361
|
+
try {
|
|
362
|
+
const resolved = await this.resolve(id, importer, { skipSelf: true });
|
|
363
|
+
const match = resolved?.external ? null : resolved?.id?.match(storePath);
|
|
364
|
+
if (resolved && match) {
|
|
365
|
+
const pkg = match[1].split(path.sep).join("/");
|
|
366
|
+
const rest = match[2] ?? "";
|
|
367
|
+
const twin = hoistedTwin(pkg, resolved.id.slice(0, resolved.id.length - (rest ? rest.length + 1 : 0)));
|
|
368
|
+
if (twin) {
|
|
369
|
+
const candidate = rest ? path.join(twin, rest) : twin;
|
|
370
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) out = candidate;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
out = null;
|
|
375
|
+
}
|
|
376
|
+
seen.set(key, out);
|
|
377
|
+
return out;
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
279
381
|
function registerNativeEngineIoBrowserTransports() {
|
|
280
382
|
const g = globalThis;
|
|
281
383
|
const name = "vxrn-engineio-browser-transports";
|
|
@@ -322,7 +424,12 @@ function registerNativeWorkspaceEntries() {
|
|
|
322
424
|
const root = roots.get(id);
|
|
323
425
|
if (root) {
|
|
324
426
|
const nativeEntry = path.join(root, "dist/esm/index.native.js");
|
|
325
|
-
|
|
427
|
+
if (fs.existsSync(nativeEntry)) return nativeEntry;
|
|
428
|
+
const srcTs = path.join(root, "src/index.ts");
|
|
429
|
+
if (fs.existsSync(srcTs)) return srcTs;
|
|
430
|
+
const srcTsx = path.join(root, "src/index.tsx");
|
|
431
|
+
if (fs.existsSync(srcTsx)) return srcTsx;
|
|
432
|
+
return null;
|
|
326
433
|
}
|
|
327
434
|
if (id.endsWith("/dist/esm/index.mjs")) {
|
|
328
435
|
const nativeEntry = `${id.slice(0, -4)}.native.js`;
|
package/lib/vite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as createViteConfig } from "./vite-
|
|
1
|
+
import { t as createViteConfig } from "./vite-Rv5b8Wxp.js";
|
|
2
2
|
import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-COicQSj4.js";
|
|
3
3
|
|
|
4
4
|
export { createViteConfig, discoverPublicPackageRoots, publicPackageViteSourceAliases };
|
package/lint.mjs
CHANGED
|
@@ -778,6 +778,65 @@ function checkUndeclaredDrawings() {
|
|
|
778
778
|
return { violations, ok: true };
|
|
779
779
|
}
|
|
780
780
|
|
|
781
|
+
/**
|
|
782
|
+
* MPO-109 WORKSPACE PATHS SHADOW — `compilerOptions.paths` REPLACES the
|
|
783
|
+
* inherited map, it never merges with it. `tsconfig.base.json` extends the
|
|
784
|
+
* generated `tamagui-workspace-paths.generated.json`, so one local entry in a
|
|
785
|
+
* package tsconfig hides every `@multiplatform.one/*` mapping from that
|
|
786
|
+
* program and the whole package fails to resolve its own workspace. That is
|
|
787
|
+
* where apps/one's 578 typecheck errors came from, and features, apps/storybook,
|
|
788
|
+
* apps/storybook-expo, apps/uxpin and public/keycloak each carried the same
|
|
789
|
+
* override.
|
|
790
|
+
*
|
|
791
|
+
* The fix for a deep-import alias is the package's own `exports` map, which the
|
|
792
|
+
* generator reads. Escape (rare, for a program that deliberately does not
|
|
793
|
+
* extend the base): `// workspace-paths-escape:` with a reason on the line
|
|
794
|
+
* above `"paths"`.
|
|
795
|
+
*/
|
|
796
|
+
function checkTsconfigWorkspacePaths() {
|
|
797
|
+
/** @type {string[]} */
|
|
798
|
+
const violations = [];
|
|
799
|
+
/** @type {string[]} */
|
|
800
|
+
const files = [];
|
|
801
|
+
const featuresTsconfig = join(FEATURES_DIR, "tsconfig.json");
|
|
802
|
+
if (existsSync(featuresTsconfig)) files.push(featuresTsconfig);
|
|
803
|
+
for (const dir of [APPS_DIR, PACKAGES_DIR, PUBLIC_DIR]) {
|
|
804
|
+
if (!existsSync(dir)) continue;
|
|
805
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
806
|
+
if (!entry.isDirectory()) continue;
|
|
807
|
+
const candidate = join(dir, entry.name, "tsconfig.json");
|
|
808
|
+
if (existsSync(candidate)) files.push(candidate);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
for (const file of files) {
|
|
812
|
+
const src = readFileSync(file, "utf8");
|
|
813
|
+
// Whole-line comments only: a `//` inside a string (the $schema URL) must
|
|
814
|
+
// survive, and these files are JSONC, not JSON5.
|
|
815
|
+
const stripped = src.replace(/^[ \t]*\/\/.*$/gm, "");
|
|
816
|
+
let doc;
|
|
817
|
+
try {
|
|
818
|
+
doc = JSON.parse(stripped);
|
|
819
|
+
} catch {
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
const paths = doc?.compilerOptions?.paths;
|
|
823
|
+
if (!paths || typeof paths !== "object") continue;
|
|
824
|
+
const extendsField = doc.extends;
|
|
825
|
+
const extendsBase = Array.isArray(extendsField)
|
|
826
|
+
? extendsField.some((e) => typeof e === "string" && e.includes("tsconfig.base"))
|
|
827
|
+
: typeof extendsField === "string" && extendsField.includes("tsconfig.base");
|
|
828
|
+
if (!extendsBase) continue;
|
|
829
|
+
const decl = src.match(/^[ \t]*"paths"\s*:/m);
|
|
830
|
+
const index = decl?.index ?? 0;
|
|
831
|
+
const before = src.slice(0, index);
|
|
832
|
+
if (/workspace-paths-escape:/.test(before.split("\n").slice(-3).join("\n"))) continue;
|
|
833
|
+
violations.push(
|
|
834
|
+
`${relative(ROOT, file)}:${lineAt(src, index)} — declares \`compilerOptions.paths\` (${Object.keys(paths).join(", ")}) while extending tsconfig.base`,
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
return violations;
|
|
838
|
+
}
|
|
839
|
+
|
|
781
840
|
/**
|
|
782
841
|
* Run structural convention checks over a consumer tree.
|
|
783
842
|
*
|
|
@@ -791,6 +850,7 @@ export function runConventionChecks(options = {}) {
|
|
|
791
850
|
const sizeRecipeEscape = checkSizeRecipeEscape();
|
|
792
851
|
const storyShape = checkStoryFileShape();
|
|
793
852
|
const undeclaredDrawings = checkUndeclaredDrawings();
|
|
853
|
+
const tsconfigPaths = checkTsconfigWorkspacePaths();
|
|
794
854
|
const tokens = checkTransitionTokens();
|
|
795
855
|
const styleTokens = checkStyleTokens();
|
|
796
856
|
const themeNames = checkThemeNames();
|
|
@@ -857,6 +917,15 @@ export function runConventionChecks(options = {}) {
|
|
|
857
917
|
console.error("");
|
|
858
918
|
}
|
|
859
919
|
|
|
920
|
+
if (tsconfigPaths.length) {
|
|
921
|
+
failed = true;
|
|
922
|
+
console.error(
|
|
923
|
+
"Convention (MPO-109 WORKSPACE PATHS SHADOW): `compilerOptions.paths` REPLACES the map tsconfig.base.json inherits from tamagui-workspace-paths.generated.json, so one local entry hides every `@multiplatform.one/*` mapping from that program. Declare the alias in the target package's `exports` (the generator reads it) instead. Escape with `// workspace-paths-escape: <reason>` above the key.\n",
|
|
924
|
+
);
|
|
925
|
+
for (const msg of tsconfigPaths) console.error(` ${msg}`);
|
|
926
|
+
console.error("");
|
|
927
|
+
}
|
|
928
|
+
|
|
860
929
|
if (tokens.violations.length) {
|
|
861
930
|
failed = true;
|
|
862
931
|
console.error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/config",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.0",
|
|
4
4
|
"description": "Shared build and test configuration presets for multiplatform.one",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"vite-plugin-external": "^6.2.2",
|
|
103
103
|
"vite-plugin-i18next-loader": "^3.1.3",
|
|
104
104
|
"vitest": "^4.1.5",
|
|
105
|
-
"@multiplatform.one/utils": "7.
|
|
105
|
+
"@multiplatform.one/utils": "7.7.0"
|
|
106
106
|
},
|
|
107
107
|
"devDependencies": {
|
|
108
108
|
"tsdown": "^0.21.10",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared `types` array is load-bearing — MPO-109.
|
|
3
|
+
*
|
|
4
|
+
* `public/config/tsconfig/base.json` sets `compilerOptions.types`, which turns
|
|
5
|
+
* OFF automatic @types inclusion for every program in the workspace. Anything
|
|
6
|
+
* missing from that list is simply absent, with no error at the point of loss.
|
|
7
|
+
*
|
|
8
|
+
* `chai` is the one that bit. @vitest/expect declares
|
|
9
|
+
*
|
|
10
|
+
* interface Assertion<T> extends VitestAssertion<Chai.Assertion, T>, ...
|
|
11
|
+
*
|
|
12
|
+
* so with no global `Chai` namespace the base interface collapses and takes
|
|
13
|
+
* `.not` and the jest-dom matchers with it. Measured on 2026-09-04 against
|
|
14
|
+
* origin/main cb157dd0f: public/components 421 -> 26, public/forms 343 -> 4,
|
|
15
|
+
* features 168 -> 138, public/utils 4 -> 0, and no package got worse.
|
|
16
|
+
*
|
|
17
|
+
* Same shape as the workspace `paths` map: a config key that REPLACES rather
|
|
18
|
+
* than merges, quietly dropping what it did not enumerate.
|
|
19
|
+
*/
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { describe, expect, it } from "vitest";
|
|
23
|
+
|
|
24
|
+
const baseTsconfig = path.resolve(__dirname, "../tsconfig/base.json");
|
|
25
|
+
|
|
26
|
+
/** JSONC: whole-line `//` comments only, plus trailing commas. */
|
|
27
|
+
function readJsonc(file: string): any {
|
|
28
|
+
const src = fs.readFileSync(file, "utf8");
|
|
29
|
+
return JSON.parse(src.replace(/^[ \t]*\/\/.*$/gm, "").replace(/,(\s*[}\]])/g, "$1"));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("base tsconfig types (MPO-109)", () => {
|
|
33
|
+
it("keeps chai in the explicit types array", () => {
|
|
34
|
+
const types = readJsonc(baseTsconfig)?.compilerOptions?.types;
|
|
35
|
+
expect(types).toContain("chai");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("keeps the jest-dom vitest matchers alongside it", () => {
|
|
39
|
+
const types = readJsonc(baseTsconfig)?.compilerOptions?.types;
|
|
40
|
+
expect(types).toContain("@testing-library/jest-dom/vitest");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emitting programs must not inherit the workspace `paths` map — MPO-109.
|
|
3
|
+
*
|
|
4
|
+
* `tsconfig.base.json` extends `tamagui-workspace-paths.generated.json`, which
|
|
5
|
+
* points every `@multiplatform.one/*` specifier at a sibling's repo-relative
|
|
6
|
+
* `src/`. That is right for a typecheck program and wrong for one that emits
|
|
7
|
+
* declarations under a `rootDir`: a `paths` hit makes the sibling source a file
|
|
8
|
+
* OF THAT PROGRAM, so `rootDir` rejects it —
|
|
9
|
+
*
|
|
10
|
+
* TS6059 File 'public/utils/src/dev.ts' is not under rootDir '.../src'
|
|
11
|
+
* TS6307 ... is not listed within the file list of project ...
|
|
12
|
+
*
|
|
13
|
+
* Every emitting package clears the map and resolves siblings through their
|
|
14
|
+
* exports `source` condition instead (node_modules-external under
|
|
15
|
+
* preserveSymlinks, so `rootDir` never sees them). The three vite plugins had
|
|
16
|
+
* no build config at all and compiled straight off the base, which is how
|
|
17
|
+
* `@multiplatform.one/utils/dev` broke `build-gnome`. The bare-name form was
|
|
18
|
+
* already reachable on main, so this guard exists to stop the next one.
|
|
19
|
+
*/
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { describe, expect, it } from "vitest";
|
|
23
|
+
|
|
24
|
+
const root = path.resolve(__dirname, "../../..");
|
|
25
|
+
const publicDir = path.join(root, "public");
|
|
26
|
+
|
|
27
|
+
/** JSONC: whole-line `//` comments only, plus trailing commas. */
|
|
28
|
+
function readJsonc(file: string): any {
|
|
29
|
+
const src = fs.readFileSync(file, "utf8");
|
|
30
|
+
return JSON.parse(src.replace(/^[ \t]*\/\/.*$/gm, "").replace(/,(\s*[}\]])/g, "$1"));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function extendsBase(doc: any): boolean {
|
|
34
|
+
const value = doc?.extends;
|
|
35
|
+
const list = Array.isArray(value) ? value : [value];
|
|
36
|
+
return list.some((entry) => typeof entry === "string" && entry.includes("tsconfig.base"));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The config each package's `build` script actually hands to tsc. */
|
|
40
|
+
function buildConfigs(): { pkg: string; file: string; doc: any }[] {
|
|
41
|
+
const found: { pkg: string; file: string; doc: any }[] = [];
|
|
42
|
+
for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) {
|
|
43
|
+
if (!entry.isDirectory()) continue;
|
|
44
|
+
const pkgJson = path.join(publicDir, entry.name, "package.json");
|
|
45
|
+
if (!fs.existsSync(pkgJson)) continue;
|
|
46
|
+
const build = JSON.parse(fs.readFileSync(pkgJson, "utf8"))?.scripts?.build;
|
|
47
|
+
if (typeof build !== "string") continue;
|
|
48
|
+
const named = build.match(/tsc\s+(?:-p|--project)\s+(\S+)/)?.[1];
|
|
49
|
+
const file = path.join(publicDir, entry.name, named ?? "tsconfig.json");
|
|
50
|
+
if (!/\btsc\b/.test(build) || !fs.existsSync(file)) continue;
|
|
51
|
+
found.push({ pkg: entry.name, file, doc: readJsonc(file) });
|
|
52
|
+
}
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe("emitting tsconfigs (MPO-109)", () => {
|
|
57
|
+
it("finds the build configs", () => {
|
|
58
|
+
expect(buildConfigs().length).toBeGreaterThan(10);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("never lets a rootDir-constrained emitting program inherit the workspace paths map", () => {
|
|
62
|
+
const offenders = buildConfigs()
|
|
63
|
+
.filter(({ doc }) => {
|
|
64
|
+
const co = doc?.compilerOptions ?? {};
|
|
65
|
+
if (co.noEmit === true || co.rootDir === undefined) return false;
|
|
66
|
+
if (!extendsBase(doc)) return false;
|
|
67
|
+
// Clearing the inherited map is the fix; `{}` counts.
|
|
68
|
+
return co.paths === undefined;
|
|
69
|
+
})
|
|
70
|
+
.map(({ pkg, file }) => `${pkg} (${path.relative(root, file)})`);
|
|
71
|
+
expect(offenders).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("gives every such program the exports `source` condition to resolve siblings with", () => {
|
|
75
|
+
const missing = buildConfigs()
|
|
76
|
+
.filter(({ doc }) => {
|
|
77
|
+
const co = doc?.compilerOptions ?? {};
|
|
78
|
+
if (co.noEmit === true || co.rootDir === undefined) return false;
|
|
79
|
+
if (!extendsBase(doc)) return false;
|
|
80
|
+
return !(co.customConditions ?? []).includes("source");
|
|
81
|
+
})
|
|
82
|
+
.map(({ pkg }) => pkg);
|
|
83
|
+
expect(missing).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
});
|
package/src/lint.spec.ts
CHANGED
|
@@ -51,6 +51,73 @@ describe("runConventionChecks", () => {
|
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
describe("workspace paths shadow (MPO-109)", () => {
|
|
55
|
+
function run(root: string) {
|
|
56
|
+
const error = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
57
|
+
const log = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
58
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
59
|
+
const code = runConventionChecks({ roots: { root } });
|
|
60
|
+
const messages = error.mock.calls.map((call) => String(call[0])).join("\n");
|
|
61
|
+
error.mockRestore();
|
|
62
|
+
log.mockRestore();
|
|
63
|
+
warn.mockRestore();
|
|
64
|
+
return { code, messages };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
it("fails an app tsconfig that declares paths while extending tsconfig.base", () => {
|
|
68
|
+
const root = makeTree();
|
|
69
|
+
writeFileSync(
|
|
70
|
+
join(root, "apps", "demo", "tsconfig.json"),
|
|
71
|
+
`{
|
|
72
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
73
|
+
"extends": "../../tsconfig.base.json",
|
|
74
|
+
"compilerOptions": {
|
|
75
|
+
"paths": { "@/*": ["./*"] }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`,
|
|
79
|
+
);
|
|
80
|
+
const { code, messages } = run(root);
|
|
81
|
+
expect(code).toBe(1);
|
|
82
|
+
expect(messages).toContain("WORKSPACE PATHS SHADOW");
|
|
83
|
+
expect(messages).toContain("apps/demo/tsconfig.json");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("passes with no paths, with an escape comment, and when the config does not extend the base", () => {
|
|
87
|
+
const root = makeTree();
|
|
88
|
+
writeFileSync(
|
|
89
|
+
join(root, "apps", "demo", "tsconfig.json"),
|
|
90
|
+
`{
|
|
91
|
+
"extends": "../../tsconfig.base.json",
|
|
92
|
+
"compilerOptions": { "noEmit": true }
|
|
93
|
+
}
|
|
94
|
+
`,
|
|
95
|
+
);
|
|
96
|
+
mkdirSync(join(root, "features", "escaped"), { recursive: true });
|
|
97
|
+
mkdirSync(join(root, "public", "standalone"), { recursive: true });
|
|
98
|
+
writeFileSync(
|
|
99
|
+
join(root, "public", "standalone", "tsconfig.json"),
|
|
100
|
+
`{
|
|
101
|
+
"compilerOptions": { "paths": { "@/*": ["./*"] } }
|
|
102
|
+
}
|
|
103
|
+
`,
|
|
104
|
+
);
|
|
105
|
+
mkdirSync(join(root, "packages", "escaped"), { recursive: true });
|
|
106
|
+
writeFileSync(
|
|
107
|
+
join(root, "packages", "escaped", "tsconfig.json"),
|
|
108
|
+
`{
|
|
109
|
+
"extends": "../../tsconfig.base.json",
|
|
110
|
+
"compilerOptions": {
|
|
111
|
+
// workspace-paths-escape: this program deliberately owns its own map
|
|
112
|
+
"paths": { "@/*": ["./*"] }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
`,
|
|
116
|
+
);
|
|
117
|
+
expect(run(root).code).toBe(0);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
54
121
|
describe("story file shape (MPO-93)", () => {
|
|
55
122
|
function storyTree() {
|
|
56
123
|
const root = makeTree();
|
|
@@ -70,6 +70,40 @@ describe("ensureTamaguiWorkspacePaths", () => {
|
|
|
70
70
|
expect(notices[0]).toContain("was missing");
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
it("emits the subpath exports a package declares (MPO-109)", () => {
|
|
74
|
+
const { root, configDir } = fakeRepo(fs.readFileSync(realGenerator, "utf8"));
|
|
75
|
+
fs.mkdirSync(path.join(root, "public/widget/src/desk"), { recursive: true });
|
|
76
|
+
fs.writeFileSync(path.join(root, "public/widget/src/dev.ts"), "export const dev = 1;\n");
|
|
77
|
+
fs.writeFileSync(
|
|
78
|
+
path.join(root, "public/widget/src/desk/Shell.tsx"),
|
|
79
|
+
"export const Shell = 1;\n",
|
|
80
|
+
);
|
|
81
|
+
fs.writeFileSync(
|
|
82
|
+
path.join(root, "public/widget/package.json"),
|
|
83
|
+
JSON.stringify({
|
|
84
|
+
name: "@fake/widget",
|
|
85
|
+
exports: {
|
|
86
|
+
".": { source: "./src/index.ts" },
|
|
87
|
+
// condition object: the declared `source`, never the gitignored types/
|
|
88
|
+
"./dev": { source: "./src/dev.ts", types: "./types/dev.d.ts" },
|
|
89
|
+
// wildcard string target: an app deep-imports through this prefix
|
|
90
|
+
"./src/*": "./src/*",
|
|
91
|
+
// declared but absent on disk — must not reach the map
|
|
92
|
+
"./ghost": { source: "./src/ghost.ts" },
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
const generatedPath = path.join(configDir, tamaguiWorkspacePathsFile);
|
|
97
|
+
const result = ensureTamaguiWorkspacePaths({ configPackageDir: configDir, log: () => {} });
|
|
98
|
+
expect(result.status).toBe("generated");
|
|
99
|
+
const { paths } = JSON.parse(fs.readFileSync(generatedPath, "utf8")).compilerOptions;
|
|
100
|
+
expect(paths["@fake/widget"]).toEqual(["../../widget/src/index.ts"]);
|
|
101
|
+
expect(paths["@fake/widget/dev"]).toEqual(["../../widget/src/dev.ts"]);
|
|
102
|
+
expect(paths["@fake/widget/src/*"]).toEqual(["../../widget/src/*"]);
|
|
103
|
+
expect(paths).not.toHaveProperty("@fake/widget/ghost");
|
|
104
|
+
expect(paths).not.toHaveProperty("@fake/widget/package.json");
|
|
105
|
+
});
|
|
106
|
+
|
|
73
107
|
it("leaves an existing file alone and stays silent", () => {
|
|
74
108
|
const { configDir } = fakeRepo(fs.readFileSync(realGenerator, "utf8"));
|
|
75
109
|
const generatedPath = path.join(configDir, tamaguiWorkspacePathsFile);
|
package/src/vite.ts
CHANGED
|
@@ -224,6 +224,15 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
|
|
|
224
224
|
// This re-asserts the package's own browser redirect (-> browser transports that
|
|
225
225
|
// use RN's global WebSocket/XMLHttpRequest) with NO node_modules patch.
|
|
226
226
|
registerNativeEngineIoBrowserTransports();
|
|
227
|
+
// A mixed pnpm install (hoisted root dirs + a leftover .pnpm store) puts
|
|
228
|
+
// two byte-identical copies of react, react-native and 62 other packages
|
|
229
|
+
// on disk, and which one an importer reaches depends only on where that
|
|
230
|
+
// importer sits. Two Reacts in one Hermes bundle means the renderer arms
|
|
231
|
+
// the dispatcher on one copy while components read it as null from the
|
|
232
|
+
// other — "Cannot read property 'useContext' of null" on first render.
|
|
233
|
+
// It delegates through `this.resolve`, so it canonicalizes whatever the
|
|
234
|
+
// rest of the chain produces, wherever it sits in the plugin order.
|
|
235
|
+
registerNativeSinglePackageInstances();
|
|
227
236
|
// preferBuiltWorkspaceEntryPlugin (web-only) hard-resolves every workspace
|
|
228
237
|
// package to its `dist/esm/index.mjs` (WEB build), and that resolution reaches
|
|
229
238
|
// the native graph too — pinning the whole @multiplatform.one/* chain to web
|
|
@@ -562,6 +571,134 @@ function clientBrokenEsmPlugin(): Plugin {
|
|
|
562
571
|
* This re-asserts the browser redirect with no node_modules patch. Web is
|
|
563
572
|
* unaffected (engine.io is SSR-externalized and the web client already maps `browser`).
|
|
564
573
|
*/
|
|
574
|
+
/**
|
|
575
|
+
* Collapse duplicate PHYSICAL copies of the same package@version to one path
|
|
576
|
+
* in the NATIVE graph, so React (and every other stateful singleton) exists
|
|
577
|
+
* exactly once in the Hermes bundle.
|
|
578
|
+
*
|
|
579
|
+
* The workspace's node_modules is a MIXED install. `.npmrc` says
|
|
580
|
+
* `node-linker=hoisted`, so the root `node_modules/<pkg>` entries are real
|
|
581
|
+
* directories; but an earlier isolated install left a `node_modules/.pnpm`
|
|
582
|
+
* virtual store plus per-package symlink farms behind — e.g. each package's
|
|
583
|
+
* own `node_modules/react` still points at
|
|
584
|
+
* `../../../node_modules/.pnpm/react@19.2.5/node_modules/react`.
|
|
585
|
+
* Both trees are fully populated with byte-identical copies, and which one an
|
|
586
|
+
* importer reaches depends only on where that importer sits on disk:
|
|
587
|
+
*
|
|
588
|
+
* apps/one/app -> node_modules/.pnpm/react@19.2.5/node_modules/react
|
|
589
|
+
* public/forms/src -> node_modules/react (no local symlink)
|
|
590
|
+
*
|
|
591
|
+
* Rolldown resolves symlinks, so the two land as two DIFFERENT absolute paths
|
|
592
|
+
* and it bundles both. React 19's `useContext` is
|
|
593
|
+
* `ReactSharedInternals.H.useContext(Context)` — `H` is the dispatcher the
|
|
594
|
+
* renderer installs during render, and it is only ever set on the copy the
|
|
595
|
+
* renderer imported. A component from the other copy therefore reads `H` as
|
|
596
|
+
* null and the app dies with "Cannot read property 'useContext' of null".
|
|
597
|
+
* That is the whole bug; the same split had also duplicated react-native (427
|
|
598
|
+
* modules each), @tamagui/web, @react-navigation/core, one, scheduler and 58
|
|
599
|
+
* more packages.
|
|
600
|
+
*
|
|
601
|
+
* The rule: an id that resolves inside the virtual store is rewritten to the
|
|
602
|
+
* hoisted root copy when the root copy exists, carries the SAME version, and
|
|
603
|
+
* actually has the file. Version equality is the safety guard — genuine
|
|
604
|
+
* multi-version installs (two @babel/runtime, two viem) keep both copies.
|
|
605
|
+
*
|
|
606
|
+
* On a correctly installed tree this is a no-op by construction: `hoisted`
|
|
607
|
+
* leaves no `.pnpm` store to match, and `isolated` makes the root entry a
|
|
608
|
+
* symlink INTO the store, which realpaths back to the same directory (checked
|
|
609
|
+
* explicitly). It only fires on the mixed tree, and it is cheap because only
|
|
610
|
+
* BARE specifiers are inspected — once a package's entry is canonical, its own
|
|
611
|
+
* relative imports resolve inside the canonical copy for free.
|
|
612
|
+
*/
|
|
613
|
+
function registerNativeSinglePackageInstances(): void {
|
|
614
|
+
const g = globalThis as unknown as { __vxrnAddNativePlugins?: unknown[] };
|
|
615
|
+
const name = "vxrn-single-package-instances";
|
|
616
|
+
g.__vxrnAddNativePlugins = g.__vxrnAddNativePlugins ?? [];
|
|
617
|
+
if (g.__vxrnAddNativePlugins.some((p) => (p as { name?: string })?.name === name)) return;
|
|
618
|
+
|
|
619
|
+
const rootModules = path.join(findProjectRoot(), "node_modules");
|
|
620
|
+
// <...>/node_modules/.pnpm/<pkg>@<ver>_<peerhash>/node_modules/<name>[/<rest>]
|
|
621
|
+
const storePath =
|
|
622
|
+
/[\\/]node_modules[\\/]\.pnpm[\\/][^\\/]+[\\/]node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)(?:[\\/](.*))?$/;
|
|
623
|
+
|
|
624
|
+
function versionOf(dir: string): string | null {
|
|
625
|
+
try {
|
|
626
|
+
const raw = fs.readFileSync(path.join(dir, "package.json"), "utf8");
|
|
627
|
+
return (JSON.parse(raw) as { version?: string }).version ?? null;
|
|
628
|
+
} catch {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// package name -> hoisted root dir that is a safe stand-in, or null
|
|
634
|
+
const hoisted = new Map<string, string | null>();
|
|
635
|
+
function hoistedTwin(pkg: string, storeDir: string): string | null {
|
|
636
|
+
if (!hoisted.has(pkg)) {
|
|
637
|
+
const rootDir = path.join(rootModules, pkg);
|
|
638
|
+
let twin: string | null = null;
|
|
639
|
+
if (fs.existsSync(rootDir)) {
|
|
640
|
+
const rootVersion = versionOf(rootDir);
|
|
641
|
+
const storeVersion = versionOf(storeDir);
|
|
642
|
+
// Same package, same version => identical library code, and the two
|
|
643
|
+
// dirs are genuinely distinct (not one symlinked onto the other).
|
|
644
|
+
if (rootVersion && rootVersion === storeVersion) {
|
|
645
|
+
try {
|
|
646
|
+
if (fs.realpathSync(rootDir) !== fs.realpathSync(storeDir)) twin = rootDir;
|
|
647
|
+
} catch {
|
|
648
|
+
twin = null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
hoisted.set(pkg, twin);
|
|
653
|
+
}
|
|
654
|
+
return hoisted.get(pkg) ?? null;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const seen = new Map<string, string | null>();
|
|
658
|
+
|
|
659
|
+
g.__vxrnAddNativePlugins.push({
|
|
660
|
+
name,
|
|
661
|
+
async resolveId(
|
|
662
|
+
this: {
|
|
663
|
+
resolve: (
|
|
664
|
+
id: string,
|
|
665
|
+
importer: string | undefined,
|
|
666
|
+
opts: { skipSelf: boolean },
|
|
667
|
+
) => Promise<{ id: string; external?: boolean | string } | null>;
|
|
668
|
+
},
|
|
669
|
+
id: string,
|
|
670
|
+
importer?: string,
|
|
671
|
+
): Promise<string | null> {
|
|
672
|
+
// Bare specifiers only: a package entry. Relative/absolute ids inside an
|
|
673
|
+
// already-canonical package resolve within that copy on their own.
|
|
674
|
+
if (!id || id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return null;
|
|
675
|
+
const key = `${id} ${importer ?? ""}`;
|
|
676
|
+
const cached = seen.get(key);
|
|
677
|
+
if (cached !== undefined) return cached;
|
|
678
|
+
|
|
679
|
+
let out: string | null = null;
|
|
680
|
+
try {
|
|
681
|
+
const resolved = await this.resolve(id, importer, { skipSelf: true });
|
|
682
|
+
const match = resolved?.external ? null : resolved?.id?.match(storePath);
|
|
683
|
+
if (resolved && match) {
|
|
684
|
+
const pkg = match[1].split(path.sep).join("/");
|
|
685
|
+
const rest = match[2] ?? "";
|
|
686
|
+
const storeDir = resolved.id.slice(0, resolved.id.length - (rest ? rest.length + 1 : 0));
|
|
687
|
+
const twin = hoistedTwin(pkg, storeDir);
|
|
688
|
+
if (twin) {
|
|
689
|
+
const candidate = rest ? path.join(twin, rest) : twin;
|
|
690
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) out = candidate;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
out = null;
|
|
695
|
+
}
|
|
696
|
+
seen.set(key, out);
|
|
697
|
+
return out;
|
|
698
|
+
},
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
565
702
|
function registerNativeEngineIoBrowserTransports(): void {
|
|
566
703
|
const g = globalThis as unknown as { __vxrnAddNativePlugins?: unknown[] };
|
|
567
704
|
const name = "vxrn-engineio-browser-transports";
|
|
@@ -619,8 +756,21 @@ function registerNativeWorkspaceEntries(): void {
|
|
|
619
756
|
// (a) bare workspace specifier (e.g. "@multiplatform.one/rich-text")
|
|
620
757
|
const root = roots.get(id);
|
|
621
758
|
if (root) {
|
|
759
|
+
// Prefer the built native entry when present (tamagui-build already
|
|
760
|
+
// rewrote intra-package imports to their `.native` siblings).
|
|
622
761
|
const nativeEntry = path.join(root, "dist/esm/index.native.js");
|
|
623
|
-
|
|
762
|
+
if (fs.existsSync(nativeEntry)) return nativeEntry;
|
|
763
|
+
// Unbuilt workspace package: compile from source instead of letting
|
|
764
|
+
// rolldown leave the bare specifier external — Hermes has no module
|
|
765
|
+
// mode, so a surviving top-level `import` is unparseable. Mirrors
|
|
766
|
+
// preferBuiltWorkspaceEntryPlugin (web) and apps/storybook-expo's
|
|
767
|
+
// Metro resolveWorkspaceNativeMain. vxrn's native resolver already
|
|
768
|
+
// does platform-extension resolution (.native.tsx/.ios.tsx/…).
|
|
769
|
+
const srcTs = path.join(root, "src/index.ts");
|
|
770
|
+
if (fs.existsSync(srcTs)) return srcTs;
|
|
771
|
+
const srcTsx = path.join(root, "src/index.tsx");
|
|
772
|
+
if (fs.existsSync(srcTsx)) return srcTsx;
|
|
773
|
+
return null;
|
|
624
774
|
}
|
|
625
775
|
// (b) already-resolved web entry "<...>/dist/esm/index.mjs" -> ".native.js" sibling
|
|
626
776
|
if (id.endsWith("/dist/esm/index.mjs")) {
|
package/tsconfig/base.json
CHANGED
|
@@ -28,7 +28,16 @@
|
|
|
28
28
|
"skipLibCheck": true,
|
|
29
29
|
"strictNullChecks": true,
|
|
30
30
|
"target": "ES2020",
|
|
31
|
-
|
|
31
|
+
// An explicit `types` array turns OFF automatic @types inclusion, so this
|
|
32
|
+
// list is the whole set of ambient packages every program in the workspace
|
|
33
|
+
// gets. `chai` is not decoration: @vitest/expect declares
|
|
34
|
+
// interface Assertion<T> extends VitestAssertion<Chai.Assertion, T>, ...
|
|
35
|
+
// and without the global `Chai` namespace that base interface collapses,
|
|
36
|
+
// taking `.not` and the jest-dom matchers with it. That is where MPO-109's
|
|
37
|
+
// ~800 TS2339 came from (public/components 421 -> 26, public/forms
|
|
38
|
+
// 343 -> 4, features 168 -> 138, public/utils 4 -> 0). Do not trim this
|
|
39
|
+
// list without recounting.
|
|
40
|
+
"types": ["node", "react", "vite/client", "@testing-library/jest-dom/vitest", "chai"],
|
|
32
41
|
"lib": ["DOM", "DOM.Iterable", "ESNext"]
|
|
33
42
|
},
|
|
34
43
|
"typeAcquisition": {
|
package/types/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;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,
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;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,CA4SlF;AAED,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC"}
|