expo-desktop 0.1.18 → 0.1.19

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/build/cli.js CHANGED
@@ -23,6 +23,11 @@ const main = defineCommand({
23
23
  description: `The ${kleur.bold("display name")} for the app ${grey("(Examples: 'My App 123', '俺のアプリ')")}`,
24
24
  valueHint: "name",
25
25
  },
26
+ "local-dev": {
27
+ type: "boolean",
28
+ description: "An undocumented switch for use during development to skip the questionnaire.",
29
+ hidden: true,
30
+ },
26
31
  rdns: {
27
32
  type: "string",
28
33
  description: `The ${kleur.bold("reverse DNS")} for the app ${grey("(Example: 'com.example.my-app-123')")}`,
@@ -0,0 +1,13 @@
1
+ import fs from "node:fs/promises";
2
+ /**
3
+ * Read the state of a file on first call of the generator, then run the
4
+ * generator again to restore it back to that value that was read.
5
+ */
6
+ export async function* preserveFile({ filePath, enable, }) {
7
+ if (!enable) {
8
+ return;
9
+ }
10
+ const fileBefore = await fs.readFile(filePath, "utf-8");
11
+ yield;
12
+ await fs.writeFile(filePath, fileBefore, "utf-8");
13
+ }
@@ -3,20 +3,21 @@ import { default as kleur } from "kleur";
3
3
  import { green, grey } from "kleur/colors";
4
4
  import { platform } from "node:process";
5
5
  import { title } from "../common/clack.js";
6
- import { createExpoDesktopApp, localDev } from "./create-expo-desktop-app.js";
6
+ import { createExpoDesktopApp } from "./create-expo-desktop-app.js";
7
7
  import { previewFileTree } from "./preview-file-tree.js";
8
8
  import { promptForVersion } from "./prompt-for-version.js";
9
9
  export async function newExpoDesktopProject(args) {
10
- // A dev-time switch for skipping the questions
11
- const skip = localDev;
12
- if (skip) {
10
+ // A switch for skipping the questions
11
+ const localDev = args["local-dev"];
12
+ if (localDev) {
13
13
  await createExpoDesktopApp({
14
+ localDev,
14
15
  name: {
15
16
  displayName: "Your App Display Name",
16
17
  filesafeName: "YourApp456",
17
18
  rdns: "uk.co.birchlabs.your-app-456",
18
19
  },
19
- packageManager: "bun",
20
+ packageManager: "pnpm",
20
21
  templates: {
21
22
  template: args.template,
22
23
  "template-ios": args["template-ios"],
@@ -57,6 +58,7 @@ export async function newExpoDesktopProject(args) {
57
58
  process.exit(0);
58
59
  }
59
60
  await createExpoDesktopApp({
61
+ localDev,
60
62
  name,
61
63
  packageManager,
62
64
  versions,
@@ -11,18 +11,10 @@ import { makePrettySummary } from "../common/arktype.js";
11
11
  import { promisifiedSpawnTask, SPAWN_DEBUG_LOG_GLOB } from "../common/child-process.js";
12
12
  import { title } from "../common/clack.js";
13
13
  import { packageManagerExec } from "../common/npm.js";
14
+ import { preserveFile } from "../common/preserve-file.js";
14
15
  import { applySelectedTemplatesAsync } from "../common/template.js";
15
- /**
16
- * A crude switch to use to help with local development.
17
- *
18
- * - Skips the questionnaire at the start.
19
- * - Installs the local copy of expo-desktop-config-plugins rather than pinning
20
- * to a published release.
21
- * - Adds the apply-config-plugins.mjs script.
22
- */
23
- export const localDev = false;
24
- export async function createExpoDesktopApp({ name, packageManager, templates, versions, }) {
25
- const { projectPath } = await createExpoApp({ name, packageManager, versions });
16
+ export async function createExpoDesktopApp({ localDev, name, packageManager, templates, versions, }) {
17
+ const { projectPath } = await createExpoApp({ localDev, name, packageManager, versions });
26
18
  await appendRootGitignoreSpawnDebugLogs(projectPath);
27
19
  const templateSelection = {
28
20
  // https://github.com/expo/expo/blob/sdk-54/templates/expo-template-blank-typescript
@@ -54,13 +46,14 @@ export async function createExpoDesktopApp({ name, packageManager, templates, ve
54
46
  await updateAppJson({ name, projectPath });
55
47
  title("Altering package.json…", { spacing: 1 });
56
48
  const { name: packageJsonName } = await updatePackageJson({
49
+ localDev,
57
50
  name,
58
51
  projectPath,
59
52
  versions,
60
53
  task: { type: "create" },
61
54
  });
62
55
  title("Installing dependencies…", { spacing: 1 });
63
- await npmInstall({ cwd: projectPath, packageManager });
56
+ await npmInstall({ asNewWorkspace: true, cwd: projectPath, packageManager });
64
57
  await updatePackageJson({
65
58
  name,
66
59
  projectPath,
@@ -100,7 +93,22 @@ export async function createExpoDesktopApp({ name, packageManager, templates, ve
100
93
  await writeBabelConfig({ projectPath });
101
94
  // TODO: Set up Windows app.cpp entrypoint
102
95
  }
103
- async function createExpoApp({ name, packageManager, versions, }) {
96
+ async function createExpoApp({ localDev, name, packageManager, versions, }) {
97
+ // `create-expo-app` aggravatingly reconfigures your workspace to use
98
+ // `nodeLinker: hoisted`, which sucks when creating sample projects inside
99
+ // this monorepo during local dev (even with `--no-install`). So we fight
100
+ // back.
101
+ //
102
+ // For non-local dev, it sounds like we can use `nodeLinker: isolated` as of
103
+ // Expo SDK 54, so I'm tempted to enforce that in created templates, too. But
104
+ // one thing at a time.
105
+ // - https://docs.expo.dev/more/create-expo/#pnpm
106
+ // - https://github.com/expo/expo/blob/222b3b12610d69784bab6c5a188a46ea388f866a/packages/create-expo/src/resolvePackageManager.ts#L109
107
+ const gen = preserveFile({
108
+ enable: localDev,
109
+ filePath: localDev ? path.resolve(import.meta.dirname, "../../../../pnpm-workspace.yaml") : "",
110
+ });
111
+ await gen.next();
104
112
  // `npm create` drops flags meant for create-expo-app unless you add `--`; use
105
113
  // `npx --yes` instead to forward args correctly and skip prompts.
106
114
  const command = packageManager === "npm" ? "npx" : packageManager;
@@ -133,6 +141,9 @@ async function createExpoApp({ name, packageManager, versions, }) {
133
141
  log.error(`Error running ${yellow("create expo-app")}${error instanceof Error ? `: ${error.message}` : "."}`);
134
142
  process.exit(1);
135
143
  }
144
+ finally {
145
+ await gen.next();
146
+ }
136
147
  return { projectPath };
137
148
  }
138
149
  async function appendRootGitignoreSpawnDebugLogs(projectPath) {
@@ -209,7 +220,7 @@ async function updateAppJson({ name, projectPath, }) {
209
220
  }
210
221
  console.log(`${green("◆")} Altered app.json.\n`);
211
222
  }
212
- async function updatePackageJson({ name, projectPath, task, versions, }) {
223
+ async function updatePackageJson({ localDev, name, projectPath, task, versions, }) {
213
224
  const packageJsonPath = path.resolve(projectPath, "package.json");
214
225
  let packageJson;
215
226
  try {
@@ -281,10 +292,41 @@ async function updatePackageJson({ name, projectPath, task, versions, }) {
281
292
  console.log(`${green("◆")} Altered package.json.\n`);
282
293
  return { name: nameBefore };
283
294
  }
284
- async function npmInstall({ cwd, packageManager, }) {
295
+ async function npmInstall({ asNewWorkspace, cwd, packageManager, }) {
285
296
  const command = packageManager;
286
297
  const args = ["install"];
287
298
  console.log(`${cyan("◆")} Running: ${yellow(`${command} ${args.join(" ")}`)}\n`);
299
+ // Unlike npm and bun, pnpm climbs up to install dependencies in the closest
300
+ // ancestor directory if there is one. This is particularly inconvenient
301
+ // during local dev when we're creating samples inside the monorepo.
302
+ if (asNewWorkspace && packageManager === "pnpm") {
303
+ // (1) Ensure a file name pnpm-workspace.yaml exists.
304
+ //
305
+ // (2) Also ensure that it uses nodeLinker: hoisted, as otherwise
306
+ // `:path => "#{config[:reactNativePath]}-macos"` predicts that there will
307
+ // be a react-native-macos directory right beside the react-native
308
+ // directory by just optimistically appending "-macos" on the end, like so:
309
+ // "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.29.0_@react-native-community+cli@20.1.3_typescript@5._941d99d35895d1a3626e14fd9f3b3666/node_modules/react-native" + "-macos"
310
+ //
311
+ // As this is not true with pnpm's default `nodeLinker: isolated`, we
312
+ // need to stick to `nodeLinker: hoisted` until we can rewrite the
313
+ // Podfile script to resolve it properly.
314
+ //
315
+ // This is consistent with what the Expo team do for pnpm and yarn:
316
+ // - https://docs.expo.dev/more/create-expo/#pnpm
317
+ // - https://github.com/expo/expo/blob/222b3b12610d69784bab6c5a188a46ea388f866a/packages/create-expo/src/resolvePackageManager.ts#L109
318
+ try {
319
+ await fs.writeFile(path.resolve(cwd, "pnpm-workspace.yaml"), "nodeLinker: hoisted\n", {
320
+ flag: "wx",
321
+ encoding: "utf-8",
322
+ });
323
+ }
324
+ catch (error) {
325
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") {
326
+ throw error;
327
+ }
328
+ }
329
+ }
288
330
  try {
289
331
  await tasks([
290
332
  promisifiedSpawnTask({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-desktop",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Best-effort desktop support for Expo",
5
5
  "keywords": [
6
6
  "android",
@@ -41,8 +41,8 @@
41
41
  "kleur": "^4.1.5",
42
42
  "mustache": "^4.2.0",
43
43
  "toml": "^4.1.1",
44
- "expo-desktop-config-plugins": "1.1.18",
45
- "expo-desktop-prebuild-config": "1.0.7"
44
+ "expo-desktop-config-plugins": "1.1.19",
45
+ "expo-desktop-prebuild-config": "1.0.8"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@expo/config": "^12.0.13",