@multiplatform.one/utils 7.84.3 → 7.84.5

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/dev.cjs CHANGED
@@ -173,7 +173,7 @@ const webConditionOrder = [
173
173
  "require"
174
174
  ];
175
175
  const skippedConditions = new Set(["types", "react-native"]);
176
- const tsSourcePattern = /\.(c|m)?tsx?$/;
176
+ const tsSourcePattern = /\.[cm]?tsx?$/;
177
177
  function collectExportTargets(target, out) {
178
178
  if (typeof target === "string") {
179
179
  out.push(target);
@@ -213,6 +213,26 @@ function declaredExportTarget(pkgDir, target) {
213
213
  const runtime = candidates.find((c) => !c.endsWith(".d.ts"));
214
214
  return runtime ? node_path.default.resolve(pkgDir, runtime) : void 0;
215
215
  }
216
+ function directoryPatternAlias(pkgDir, target) {
217
+ const targets = [];
218
+ collectExportTargets(target, targets);
219
+ const dirTarget = targets.find((t) => t.endsWith("/*"));
220
+ if (!dirTarget) return void 0;
221
+ const dir = node_path.default.resolve(pkgDir, dirTarget.slice(0, -2));
222
+ return node_fs.default.existsSync(dir) && node_fs.default.statSync(dir).isDirectory() ? dir : void 0;
223
+ }
224
+ function subpathAlias(pkgDir, key, target) {
225
+ if (key === "." || key === "./package.json" || !key.startsWith("./")) return void 0;
226
+ if (key.endsWith("/*")) {
227
+ const pattern = key.slice(2, -2);
228
+ if (pattern.includes("*")) return void 0;
229
+ const dir = directoryPatternAlias(pkgDir, target);
230
+ return dir ? [pattern, dir] : void 0;
231
+ }
232
+ if (key.includes("*")) return void 0;
233
+ const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
234
+ return file ? [key.slice(2), file] : void 0;
235
+ }
216
236
  /**
217
237
  * Vite `resolve.alias` entries for one package's `exports` subpaths:
218
238
  * `<name>/<subpath>` → its source file, and a `./dir/*` pattern → the directory.
@@ -221,21 +241,8 @@ function packageSubpathSourceAliases(pkgName, pkgDir, exportsField) {
221
241
  const aliases = {};
222
242
  if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField)) return aliases;
223
243
  for (const [key, target] of Object.entries(exportsField)) {
224
- if (key === "." || key === "./package.json" || !key.startsWith("./")) continue;
225
- if (key.endsWith("/*")) {
226
- const pattern = key.slice(2, -2);
227
- if (pattern.includes("*")) continue;
228
- const targets = [];
229
- collectExportTargets(target, targets);
230
- const dirTarget = targets.find((t) => t.endsWith("/*"));
231
- if (!dirTarget) continue;
232
- const dir = node_path.default.resolve(pkgDir, dirTarget.slice(0, -2));
233
- if (node_fs.default.existsSync(dir) && node_fs.default.statSync(dir).isDirectory()) aliases[`${pkgName}/${pattern}`] = dir;
234
- continue;
235
- }
236
- if (key.includes("*")) continue;
237
- const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
238
- if (file) aliases[`${pkgName}/${key.slice(2)}`] = file;
244
+ const alias = subpathAlias(pkgDir, key, target);
245
+ if (alias) aliases[`${pkgName}/${alias[0]}`] = alias[1];
239
246
  }
240
247
  return aliases;
241
248
  }
package/lib/dev.mjs CHANGED
@@ -145,7 +145,7 @@ const webConditionOrder = [
145
145
  "require"
146
146
  ];
147
147
  const skippedConditions = new Set(["types", "react-native"]);
148
- const tsSourcePattern = /\.(c|m)?tsx?$/;
148
+ const tsSourcePattern = /\.[cm]?tsx?$/;
149
149
  function collectExportTargets(target, out) {
150
150
  if (typeof target === "string") {
151
151
  out.push(target);
@@ -185,6 +185,26 @@ function declaredExportTarget(pkgDir, target) {
185
185
  const runtime = candidates.find((c) => !c.endsWith(".d.ts"));
186
186
  return runtime ? path.resolve(pkgDir, runtime) : void 0;
187
187
  }
188
+ function directoryPatternAlias(pkgDir, target) {
189
+ const targets = [];
190
+ collectExportTargets(target, targets);
191
+ const dirTarget = targets.find((t) => t.endsWith("/*"));
192
+ if (!dirTarget) return void 0;
193
+ const dir = path.resolve(pkgDir, dirTarget.slice(0, -2));
194
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory() ? dir : void 0;
195
+ }
196
+ function subpathAlias(pkgDir, key, target) {
197
+ if (key === "." || key === "./package.json" || !key.startsWith("./")) return void 0;
198
+ if (key.endsWith("/*")) {
199
+ const pattern = key.slice(2, -2);
200
+ if (pattern.includes("*")) return void 0;
201
+ const dir = directoryPatternAlias(pkgDir, target);
202
+ return dir ? [pattern, dir] : void 0;
203
+ }
204
+ if (key.includes("*")) return void 0;
205
+ const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
206
+ return file ? [key.slice(2), file] : void 0;
207
+ }
188
208
  /**
189
209
  * Vite `resolve.alias` entries for one package's `exports` subpaths:
190
210
  * `<name>/<subpath>` → its source file, and a `./dir/*` pattern → the directory.
@@ -193,21 +213,8 @@ function packageSubpathSourceAliases(pkgName, pkgDir, exportsField) {
193
213
  const aliases = {};
194
214
  if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField)) return aliases;
195
215
  for (const [key, target] of Object.entries(exportsField)) {
196
- if (key === "." || key === "./package.json" || !key.startsWith("./")) continue;
197
- if (key.endsWith("/*")) {
198
- const pattern = key.slice(2, -2);
199
- if (pattern.includes("*")) continue;
200
- const targets = [];
201
- collectExportTargets(target, targets);
202
- const dirTarget = targets.find((t) => t.endsWith("/*"));
203
- if (!dirTarget) continue;
204
- const dir = path.resolve(pkgDir, dirTarget.slice(0, -2));
205
- if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) aliases[`${pkgName}/${pattern}`] = dir;
206
- continue;
207
- }
208
- if (key.includes("*")) continue;
209
- const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
210
- if (file) aliases[`${pkgName}/${key.slice(2)}`] = file;
216
+ const alias = subpathAlias(pkgDir, key, target);
217
+ if (alias) aliases[`${pkgName}/${alias[0]}`] = alias[1];
211
218
  }
212
219
  return aliases;
213
220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/utils",
3
- "version": "7.84.3",
3
+ "version": "7.84.5",
4
4
  "description": "utilities for multiplatform.one",
5
5
  "keywords": [
6
6
  "multiplatform.one",
package/src/dev.spec.ts CHANGED
@@ -13,7 +13,9 @@ import {
13
13
  lookupProjectRoot,
14
14
  lookupTamaguiModules,
15
15
  lookupTranspileModules,
16
+ packageSubpathSourceAliases,
16
17
  resolveConfig,
18
+ resolveExportSource,
17
19
  workspaceSourceAliases,
18
20
  } from "./dev";
19
21
 
@@ -213,3 +215,160 @@ describe("workspaceSourceAliases", () => {
213
215
  );
214
216
  });
215
217
  });
218
+
219
+ describe("packageSubpathSourceAliases", () => {
220
+ let pkgDir: string;
221
+
222
+ function touch(rel: string): string {
223
+ const file = path.join(pkgDir, rel);
224
+ fs.mkdirSync(path.dirname(file), { recursive: true });
225
+ fs.writeFileSync(file, "export {};\n");
226
+ return file;
227
+ }
228
+
229
+ beforeAll(() => {
230
+ pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), "mpo-dev-subpaths-"));
231
+ touch("src/a.ts");
232
+ touch("src/b.tsx");
233
+ touch("dist/c.mjs");
234
+ touch("dist/d.mjs");
235
+ touch("src/e.mts");
236
+ touch("styles/f.css");
237
+ fs.mkdirSync(path.join(pkgDir, "src/icons"), { recursive: true });
238
+ fs.mkdirSync(path.join(pkgDir, "dist/assets"), { recursive: true });
239
+ fs.writeFileSync(path.join(pkgDir, "dist/assets/file"), "");
240
+ });
241
+
242
+ afterAll(() => {
243
+ fs.rmSync(pkgDir, { recursive: true, force: true });
244
+ });
245
+
246
+ it.each([undefined, null, "./index.js", ["./a"], 0])(
247
+ "returns no aliases for a non-object exports field (%s)",
248
+ (exportsField) => {
249
+ expect(packageSubpathSourceAliases("@x/p", pkgDir, exportsField)).toEqual({});
250
+ },
251
+ );
252
+
253
+ it("skips the root, package.json and keys that are not ./ subpaths", () => {
254
+ expect(
255
+ packageSubpathSourceAliases("@x/p", pkgDir, {
256
+ ".": "./src/a.ts",
257
+ "./package.json": "./package.json",
258
+ "#internal": "./src/a.ts",
259
+ import: "./src/a.ts",
260
+ }),
261
+ ).toEqual({});
262
+ });
263
+
264
+ it("aliases a file subpath to its source condition first", () => {
265
+ expect(
266
+ packageSubpathSourceAliases("@x/p", pkgDir, {
267
+ "./a": { source: "./src/a.ts", default: "./dist/c.mjs" },
268
+ }),
269
+ ).toEqual({ "@x/p/a": path.join(pkgDir, "src/a.ts") });
270
+ });
271
+
272
+ it("prefers a TypeScript file among the conditions over an earlier built one", () => {
273
+ expect(
274
+ packageSubpathSourceAliases("@x/p", pkgDir, {
275
+ "./b": { import: "./dist/c.mjs", default: "./src/b.tsx" },
276
+ "./e": ["./dist/d.mjs", "./src/e.mts"],
277
+ }),
278
+ ).toEqual({
279
+ "@x/p/b": path.join(pkgDir, "src/b.tsx"),
280
+ "@x/p/e": path.join(pkgDir, "src/e.mts"),
281
+ });
282
+ });
283
+
284
+ it("orders web conditions import, default, require and skips types and react-native", () => {
285
+ expect(
286
+ packageSubpathSourceAliases("@x/p", pkgDir, {
287
+ "./c": {
288
+ types: "./src/a.ts",
289
+ "react-native": "./src/b.tsx",
290
+ require: "./dist/d.mjs",
291
+ import: "./dist/c.mjs",
292
+ },
293
+ }),
294
+ ).toEqual({ "@x/p/c": path.join(pkgDir, "dist/c.mjs") });
295
+ });
296
+
297
+ it("falls back to the first existing file when none is TypeScript", () => {
298
+ expect(packageSubpathSourceAliases("@x/p", pkgDir, { "./f.css": "./styles/f.css" })).toEqual({
299
+ "@x/p/f.css": path.join(pkgDir, "styles/f.css"),
300
+ });
301
+ });
302
+
303
+ it("aliases a missing target to its declared runtime path, never a .d.ts", () => {
304
+ expect(
305
+ packageSubpathSourceAliases("@x/p", pkgDir, {
306
+ "./missing": { types: "./types/missing.d.ts", default: "./dist/missing.mjs" },
307
+ "./types-only": "./types/only.d.ts",
308
+ }),
309
+ ).toEqual({ "@x/p/missing": path.join(pkgDir, "dist/missing.mjs") });
310
+ });
311
+
312
+ it("aliases a ./dir/* pattern to its existing target directory", () => {
313
+ expect(
314
+ packageSubpathSourceAliases("@x/p", pkgDir, {
315
+ "./icons/*": { default: "./src/icons/*" },
316
+ "./assets/*": ["./dist/assets/*"],
317
+ }),
318
+ ).toEqual({
319
+ "@x/p/icons": path.join(pkgDir, "src/icons"),
320
+ "@x/p/assets": path.join(pkgDir, "dist/assets"),
321
+ });
322
+ });
323
+
324
+ it("skips patterns with a missing, non-directory or non-* target, or a nested star", () => {
325
+ expect(
326
+ packageSubpathSourceAliases("@x/p", pkgDir, {
327
+ "./gone/*": "./src/gone/*",
328
+ "./file/*": "./dist/assets/file/*",
329
+ "./flat/*": "./src/a.ts",
330
+ "./a/*/b/*": "./src/*",
331
+ "./mid*dle": "./src/a.ts",
332
+ }),
333
+ ).toEqual({});
334
+ });
335
+ });
336
+
337
+ describe("resolveExportSource", () => {
338
+ it("returns undefined when no candidate exists", () => {
339
+ expect(resolveExportSource(os.tmpdir(), { default: "./nope-mpo.ts" })).toBeUndefined();
340
+ });
341
+
342
+ it("ignores .d.ts candidates and null targets", () => {
343
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mpo-dev-source-"));
344
+ fs.writeFileSync(path.join(dir, "x.d.ts"), "");
345
+ expect(resolveExportSource(dir, { types: null, default: "./x.d.ts" })).toBeUndefined();
346
+ fs.rmSync(dir, { recursive: true, force: true });
347
+ });
348
+ });
349
+
350
+ describe("resolveExportSource TypeScript preference", () => {
351
+ it.each(["x.ts", "x.tsx", "x.cts", "x.mts", "x.ctsx", "x.mtsx"])(
352
+ "prefers %s over an earlier built file",
353
+ (file) => {
354
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mpo-dev-ts-"));
355
+ fs.writeFileSync(path.join(dir, "x.js"), "");
356
+ fs.writeFileSync(path.join(dir, file), "");
357
+ expect(resolveExportSource(dir, ["./x.js", `./${file}`])).toBe(path.join(dir, file));
358
+ fs.rmSync(dir, { recursive: true, force: true });
359
+ },
360
+ );
361
+
362
+ it.each(["x.js", "x.xts", "x.ts.map", "x.jsx"])(
363
+ "keeps the first file when %s is last",
364
+ (file) => {
365
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mpo-dev-ts-"));
366
+ fs.writeFileSync(path.join(dir, "first.mjs"), "");
367
+ fs.writeFileSync(path.join(dir, file), "");
368
+ expect(resolveExportSource(dir, ["./first.mjs", `./${file}`])).toBe(
369
+ path.join(dir, "first.mjs"),
370
+ );
371
+ fs.rmSync(dir, { recursive: true, force: true });
372
+ },
373
+ );
374
+ });
package/src/dev.ts CHANGED
@@ -104,7 +104,7 @@ export type ExportTarget = string | null | ExportTarget[] | { [condition: string
104
104
 
105
105
  const webConditionOrder = ["import", "default", "require"];
106
106
  const skippedConditions = new Set(["types", "react-native"]);
107
- const tsSourcePattern = /\.(c|m)?tsx?$/;
107
+ const tsSourcePattern = /\.[cm]?tsx?$/;
108
108
 
109
109
  function collectExportTargets(target: ExportTarget, out: string[]): void {
110
110
  if (typeof target === "string") {
@@ -153,6 +153,32 @@ function declaredExportTarget(pkgDir: string, target: ExportTarget): string | un
153
153
  return runtime ? path.resolve(pkgDir, runtime) : undefined;
154
154
  }
155
155
 
156
+ function directoryPatternAlias(pkgDir: string, target: ExportTarget): string | undefined {
157
+ const targets: string[] = [];
158
+ collectExportTargets(target, targets);
159
+ const dirTarget = targets.find((t) => t.endsWith("/*"));
160
+ if (!dirTarget) return undefined;
161
+ const dir = path.resolve(pkgDir, dirTarget.slice(0, -2));
162
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory() ? dir : undefined;
163
+ }
164
+
165
+ function subpathAlias(
166
+ pkgDir: string,
167
+ key: string,
168
+ target: ExportTarget,
169
+ ): [string, string] | undefined {
170
+ if (key === "." || key === "./package.json" || !key.startsWith("./")) return undefined;
171
+ if (key.endsWith("/*")) {
172
+ const pattern = key.slice(2, -2);
173
+ if (pattern.includes("*")) return undefined;
174
+ const dir = directoryPatternAlias(pkgDir, target);
175
+ return dir ? [pattern, dir] : undefined;
176
+ }
177
+ if (key.includes("*")) return undefined;
178
+ const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
179
+ return file ? [key.slice(2), file] : undefined;
180
+ }
181
+
156
182
  /**
157
183
  * Vite `resolve.alias` entries for one package's `exports` subpaths:
158
184
  * `<name>/<subpath>` → its source file, and a `./dir/*` pattern → the directory.
@@ -167,23 +193,8 @@ export function packageSubpathSourceAliases(
167
193
  return aliases;
168
194
  }
169
195
  for (const [key, target] of Object.entries(exportsField as Record<string, ExportTarget>)) {
170
- if (key === "." || key === "./package.json" || !key.startsWith("./")) continue;
171
- if (key.endsWith("/*")) {
172
- const pattern = key.slice(2, -2);
173
- if (pattern.includes("*")) continue;
174
- const targets: string[] = [];
175
- collectExportTargets(target, targets);
176
- const dirTarget = targets.find((t) => t.endsWith("/*"));
177
- if (!dirTarget) continue;
178
- const dir = path.resolve(pkgDir, dirTarget.slice(0, -2));
179
- if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
180
- aliases[`${pkgName}/${pattern}`] = dir;
181
- }
182
- continue;
183
- }
184
- if (key.includes("*")) continue;
185
- const file = resolveExportSource(pkgDir, target) ?? declaredExportTarget(pkgDir, target);
186
- if (file) aliases[`${pkgName}/${key.slice(2)}`] = file;
196
+ const alias = subpathAlias(pkgDir, key, target);
197
+ if (alias) aliases[`${pkgName}/${alias[0]}`] = alias[1];
187
198
  }
188
199
  return aliases;
189
200
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,MAAM,WAAW,6BAA6B;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,sBAAsB,CACpC,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,EAAE,GAAU,EAAE,GAAE,6BAAkC,SA0BnD;AAED,wBAAgB,oBAAoB,CAClC,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,EAAE,GAAU,EAAE,GAAE,2BAAgC,SA2BjD;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAQrF;AAGD,wBAAgB,iBAAiB,WAYhC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAyBlG;;;;;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;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqDrF;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAiCvE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CASjD;AAED,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,MAAM,WAAW,6BAA6B;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,sBAAsB,CACpC,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,EAAE,GAAU,EAAE,GAAE,6BAAkC,SA0BnD;AAED,wBAAgB,oBAAoB,CAClC,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,EAAE,GAAU,EAAE,GAAE,2BAAgC,SA2BjD;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAQrF;AAGD,wBAAgB,iBAAiB,WAYhC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAyBlG;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5F;AAyCD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqDrF;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAiCvE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CASjD;AAED,cAAc,QAAQ,CAAC"}