expo-desktop 0.1.32 → 0.1.34

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.
@@ -135,6 +135,9 @@ export function packageManagerExec(packageManager) {
135
135
  command = "npx";
136
136
  args.push("--yes");
137
137
  break;
138
+ case "yarn":
139
+ command = "yarn";
140
+ break;
138
141
  case "pnpm":
139
142
  command = "pnpm";
140
143
  args.push("exec");
@@ -51,6 +51,7 @@ export async function newExpoDesktopProject(args) {
51
51
  },
52
52
  { value: "bun", label: `Bun${platform === "darwin" ? " (recommended)" : ""}` },
53
53
  { value: "pnpm", label: `pnpm${platform === "win32" ? " (recommended)" : ""}` },
54
+ { value: "yarn", label: "yarn" },
54
55
  ],
55
56
  initialValue: platform === "darwin" ? "bun" : platform === "win32" ? "pnpm" : "npm",
56
57
  });
@@ -1,5 +1,6 @@
1
1
  import { log, tasks } from "@clack/prompts";
2
2
  import { type } from "arktype";
3
+ import { glob } from "glob";
3
4
  import { cyan, green, yellow } from "kleur/colors";
4
5
  import fs from "node:fs/promises";
5
6
  import path from "node:path";
@@ -66,11 +67,13 @@ export async function createExpoDesktopApp({ localDev, name, packageManager, tem
66
67
  if (localDev) {
67
68
  await addApplyConfigPluginsScript({ projectPath });
68
69
  }
70
+ const bundleEntryFileCandidates = await getBundleEntryFileCandidates({ projectPath });
69
71
  await applyConfigPlugins({
70
72
  projectRoot: projectPath,
71
73
  displayName: name.displayName,
72
74
  bundleIdentifier: name.rdns.replaceAll("_", "-"),
73
75
  platforms: ["macos", "windows"],
76
+ bundleEntryFileCandidates,
74
77
  });
75
78
  console.log(`${green("◆")} Applied config plugins.\n`);
76
79
  title("Improving the macOS app's gitignore file…", { spacing: 1 });
@@ -100,6 +103,30 @@ export async function createExpoDesktopApp({ localDev, name, packageManager, tem
100
103
  await improveMetroConfig({ projectPath });
101
104
  title("Adding Expo support to the Babel config…", { spacing: 1 });
102
105
  await writeBabelConfig({ projectPath });
106
+ logProjectReady({ cdPath: name.filesafeName, packageManager });
107
+ }
108
+ async function getBundleEntryFileCandidates({ projectPath }) {
109
+ try {
110
+ // Seems to order candidates as:
111
+ // [
112
+ // "index.windows.tsx",
113
+ // "index.windows.ts",
114
+ // "index.windows.jsx",
115
+ // "index.windows.js",
116
+ // "index.tsx",
117
+ // "index.ts",
118
+ // "index.jsx",
119
+ // "index.js",
120
+ // ]
121
+ const candidates = await glob("index?(.windows|.macos|.ios|.android|.native).{ts,tsx,js,jsx}", {
122
+ cwd: projectPath,
123
+ });
124
+ return candidates;
125
+ }
126
+ catch (error) {
127
+ console.warn("Unable to detect an index?(.windows).{ts,tsx,js,jsx} file in the root of the project, so will fall back to a <BundleEntryFile> value to 'index.ts'.", error);
128
+ return ["index.ts"];
129
+ }
103
130
  }
104
131
  async function createExpoApp({ localDev, name, packageManager, versions, }) {
105
132
  // `create-expo-app` aggravatingly reconfigures your workspace to use
@@ -118,10 +145,17 @@ async function createExpoApp({ localDev, name, packageManager, versions, }) {
118
145
  });
119
146
  await gen.next();
120
147
  // `npm create` drops flags meant for create-expo-app unless you add `--`; use
121
- // `npx --yes` instead to forward args correctly and skip prompts.
148
+ // `npx --yes` instead to forward args correctly. Pass `--yes` to
149
+ // create-expo-app for non-interactive template selection; set `CI=true` in the
150
+ // child env so git-init inside an existing repo is skipped too.
122
151
  const command = packageManager === "npm" ? "npx" : packageManager;
152
+ let packageManagerArgs = ["create", "expo-app@latest"];
153
+ if (packageManager === "npm")
154
+ packageManagerArgs = ["--yes", "create-expo-app@latest"];
155
+ if (packageManager === "yarn")
156
+ packageManagerArgs = ["create", "expo-app"];
123
157
  const args = [
124
- ...(packageManager === "npm" ? ["--yes", "create-expo-app"] : ["create", "expo-app"]),
158
+ ...packageManagerArgs,
125
159
  name.filesafeName,
126
160
  "--yes",
127
161
  "--template",
@@ -138,8 +172,15 @@ async function createExpoApp({ localDev, name, packageManager, versions, }) {
138
172
  args,
139
173
  options: {
140
174
  stdio: "inherit",
141
- // Suppresses npx/npm exec "Ok to proceed?" and similar yes/no prompts.
142
- env: { npm_config_yes: "true" },
175
+ env: {
176
+ // Suppresses npx/npm exec "Ok to proceed?" and similar yes/no prompts.
177
+ npm_config_yes: "true",
178
+ // create-expo-app's `--yes` skips template/SDK prompts but not the
179
+ // "Skip initializing a new git repository?" confirm when cwd is
180
+ // inside an existing repo; that path only auto-skips when CI is set.
181
+ // https://github.com/expo/expo/blob/main/packages/create-expo/src/utils/git.ts
182
+ CI: "true",
183
+ },
143
184
  },
144
185
  debugLogDir: projectPath,
145
186
  }),
@@ -287,13 +328,40 @@ async function updatePackageJson({ localDev, name, projectPath, task, versions,
287
328
  if (!packageJson.overrides) {
288
329
  packageJson.overrides = {};
289
330
  }
290
- // react-native-macos and react-native-windows may declare conflicting peer
291
- // dependency ranges to what the Expo template provides.
292
- if (packageJson.dependencies.react) {
293
- packageJson.overrides.react = packageJson.dependencies.react;
331
+ // The "react" version in expo-template-blank-typescript for SDK 54 is
332
+ // 19.1.0, but it needs to be 19.1.4 to satisfy react-native-macos and
333
+ // react-native-windows. We find that the React Native HelloWorld template
334
+ // is more reliable as a source of truth for the best "react" and
335
+ // "react-native" versions to satisfy them, but it's still a best of a bad
336
+ // job.
337
+ let facebookDeps;
338
+ try {
339
+ const packageJson = (await getReactNativePackageJson(versions.minor));
340
+ facebookDeps = {
341
+ react: packageJson.dependencies.react,
342
+ "react-native": packageJson.dependencies["react-native"],
343
+ };
344
+ }
345
+ catch (error) {
346
+ // It's totally possible to get rate-limited by the GitHub API, so we'll
347
+ // avoid failing the entire command just for a bad fetch here.
348
+ console.warn(`Unable to get the ideal dependency versions for "react" and "react-native" from the official React Native HelloWorld template, so will use the ones from the Expo template as-is.`, error);
349
+ }
350
+ if (facebookDeps) {
351
+ packageJson.dependencies.react = facebookDeps.react;
352
+ packageJson.overrides.react = facebookDeps.react;
353
+ packageJson.dependencies["react-native"] = facebookDeps["react-native"];
354
+ packageJson.overrides["react-native"] = facebookDeps["react-native"];
294
355
  }
295
- if (packageJson.dependencies["react-native"]) {
296
- packageJson.overrides["react-native"] = packageJson.dependencies["react-native"];
356
+ else {
357
+ // react-native-macos and react-native-windows may declare conflicting
358
+ // peer dependency ranges to what the Expo template provides.
359
+ if (packageJson.dependencies.react) {
360
+ packageJson.overrides.react = packageJson.dependencies.react;
361
+ }
362
+ if (packageJson.dependencies["react-native"]) {
363
+ packageJson.overrides["react-native"] = packageJson.dependencies["react-native"];
364
+ }
297
365
  }
298
366
  }
299
367
  try {
@@ -305,6 +373,10 @@ async function updatePackageJson({ localDev, name, projectPath, task, versions,
305
373
  console.log(`${green("◆")} Altered package.json.\n`);
306
374
  return { name: nameBefore };
307
375
  }
376
+ async function getReactNativePackageJson(minor) {
377
+ const response = await fetch(`https://raw.githubusercontent.com/facebook/react-native/refs/heads/0.${minor}-stable/private/helloworld/package.json`);
378
+ return await response.json();
379
+ }
308
380
  async function npmInstall({ asNewWorkspace, cwd, packageManager, }) {
309
381
  const command = packageManager;
310
382
  const args = ["install"];
@@ -558,6 +630,7 @@ module.exports = function (api) {
558
630
  */
559
631
  async function addApplyConfigPluginsScript({ projectPath }) {
560
632
  await fs.writeFile(path.resolve(projectPath, "apply-config-plugins.mjs"), `
633
+ import { globSync } from "node:fs";
561
634
  import { createRequire } from "node:module";
562
635
 
563
636
  const require = createRequire(import.meta.url);
@@ -572,8 +645,24 @@ const info = {
572
645
  bundleIdentifier: "com.example.my-app-123",
573
646
  // @ts-expect-error Normally only accepts ios and android
574
647
  platforms: ["macos", "windows"],
648
+ bundleEntryFileCandidates: getBundleEntryFileCandidates({ projectPath: projectRoot }),
575
649
  };
576
650
 
651
+ function getBundleEntryFileCandidates({ projectPath }) {
652
+ try {
653
+ const candidates = globSync("index{.windows,.macos,.ios,.android,.native,}.{ts,tsx,js,jsx}", {
654
+ cwd: projectPath,
655
+ });
656
+ return candidates;
657
+ } catch (error) {
658
+ console.warn(
659
+ "Unable to detect an index?(.windows).{ts,tsx,js,jsx} file in the root of the project, so will fall back to a <BundleEntryFile> value to 'index.ts'.",
660
+ error,
661
+ );
662
+ return ["index.ts"];
663
+ }
664
+ }
665
+
577
666
  const withInternal = (config, internals) => {
578
667
  config._internal = {
579
668
  isDebug: false,
@@ -644,3 +733,36 @@ async function applyConfigPlugins(options) {
644
733
  await applyConfigPlugins(info);
645
734
  `.trim() + "\n", "utf-8");
646
735
  }
736
+ function logProjectReady({ cdPath, packageManager, }) {
737
+ const lines = [
738
+ "✅ Your project is ready!",
739
+ "",
740
+ "To run your project, first navigate to the directory and start up the packager:",
741
+ "",
742
+ `- cd ${cdPath}`,
743
+ `- ${formatRunCommand(packageManager, "start")}`,
744
+ "",
745
+ "… and then run the target platform of your choice:",
746
+ "",
747
+ `- ${formatRunCommand(packageManager, "android")}`,
748
+ `- ${formatRunCommand(packageManager, "ios")}`,
749
+ `- ${formatRunCommand(packageManager, "web")}`,
750
+ `- ${formatRunCommand(packageManager, "macos")}`,
751
+ `- ${formatRunCommand(packageManager, "windows")}`,
752
+ "",
753
+ ];
754
+ log.success(lines.join("\n"), { withGuide: false });
755
+ }
756
+ export function formatRunCommand(packageManager, cmd) {
757
+ switch (packageManager) {
758
+ case "pnpm":
759
+ return `pnpm run ${cmd}`;
760
+ case "yarn":
761
+ return `yarn ${cmd}`;
762
+ case "bun":
763
+ return `bun run ${cmd}`;
764
+ case "npm":
765
+ default:
766
+ return `npm run ${cmd}`;
767
+ }
768
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-desktop",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Best-effort desktop support for Expo",
5
5
  "keywords": [
6
6
  "android",
@@ -37,8 +37,8 @@
37
37
  "@clack/prompts": "^1.2.0",
38
38
  "arktype": "^2.2.0",
39
39
  "citty": "^0.2.2",
40
- "expo-desktop-config-plugins": "^1.1.29",
41
- "expo-desktop-prebuild-config": "^1.0.15",
40
+ "expo-desktop-config-plugins": "^1.1.31",
41
+ "expo-desktop-prebuild-config": "^1.0.17",
42
42
  "glob": "^10.5.0",
43
43
  "kleur": "^4.1.5",
44
44
  "mustache": "^4.2.0",