expo-desktop 0.1.33 → 0.1.35
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/common/app-json.js
CHANGED
|
@@ -26,6 +26,7 @@ export const AppJson = type({
|
|
|
26
26
|
export const PackageJson = type({
|
|
27
27
|
"name?": "string",
|
|
28
28
|
"scripts?": "Record<string, string>",
|
|
29
|
+
"expo?": "Record<string, unknown.any>",
|
|
29
30
|
"dependencies?": "Record<string, string>",
|
|
30
31
|
"devDependencies?": "Record<string, string>",
|
|
31
32
|
"peerDependencies?": "Record<string, string>",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/** True when the project root has its own `.git` (from create-expo-app's init). */
|
|
5
|
+
export async function hasProjectGitRepositoryAsync(projectPath) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.lstat(path.join(projectPath, ".git"));
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** `git diff --cached --quiet` exits 1 when there are staged changes. */
|
|
18
|
+
export async function hasGitStagedChangesAsync(projectPath) {
|
|
19
|
+
try {
|
|
20
|
+
await runGitAsync(projectPath, ["diff", "--cached", "--quiet"]);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function runGitAsync(projectPath, args) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const cp = spawn(`git ${args.join(" ")}`, {
|
|
30
|
+
cwd: projectPath,
|
|
31
|
+
shell: true,
|
|
32
|
+
stdio: "ignore",
|
|
33
|
+
});
|
|
34
|
+
cp.on("error", reject);
|
|
35
|
+
cp.on("close", (code) => {
|
|
36
|
+
if (code === 0) {
|
|
37
|
+
resolve();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
reject(new Error(`git ${args.join(" ")} exited with code ${code ?? "null"}`));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
package/build/common/npm.js
CHANGED
|
@@ -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
|
});
|
|
@@ -11,6 +11,7 @@ import { applyConfigPlugins } from "../common/apply-config-plugins.js";
|
|
|
11
11
|
import { makePrettySummary } from "../common/arktype.js";
|
|
12
12
|
import { promisifiedSpawnTask, SPAWN_DEBUG_LOG_GLOB } from "../common/child-process.js";
|
|
13
13
|
import { title } from "../common/clack.js";
|
|
14
|
+
import { hasGitStagedChangesAsync, hasProjectGitRepositoryAsync } from "../common/git.js";
|
|
14
15
|
import { packageManagerExec } from "../common/npm.js";
|
|
15
16
|
import { preserveFile } from "../common/preserve-file.js";
|
|
16
17
|
import { applySelectedTemplatesAsync } from "../common/template.js";
|
|
@@ -103,6 +104,11 @@ export async function createExpoDesktopApp({ localDev, name, packageManager, tem
|
|
|
103
104
|
await improveMetroConfig({ projectPath });
|
|
104
105
|
title("Adding Expo support to the Babel config…", { spacing: 1 });
|
|
105
106
|
await writeBabelConfig({ projectPath });
|
|
107
|
+
title("Improving App.tsx…", { spacing: 1 });
|
|
108
|
+
await improveAppTsx({ projectPath });
|
|
109
|
+
title("Committing changes…", { spacing: 1 });
|
|
110
|
+
await commitChanges({ projectPath });
|
|
111
|
+
logProjectReady({ cdPath: name.filesafeName, packageManager });
|
|
106
112
|
}
|
|
107
113
|
async function getBundleEntryFileCandidates({ projectPath }) {
|
|
108
114
|
try {
|
|
@@ -148,8 +154,13 @@ async function createExpoApp({ localDev, name, packageManager, versions, }) {
|
|
|
148
154
|
// create-expo-app for non-interactive template selection; set `CI=true` in the
|
|
149
155
|
// child env so git-init inside an existing repo is skipped too.
|
|
150
156
|
const command = packageManager === "npm" ? "npx" : packageManager;
|
|
157
|
+
let packageManagerArgs = ["create", "expo-app@latest"];
|
|
158
|
+
if (packageManager === "npm")
|
|
159
|
+
packageManagerArgs = ["--yes", "create-expo-app@latest"];
|
|
160
|
+
if (packageManager === "yarn")
|
|
161
|
+
packageManagerArgs = ["create", "expo-app"];
|
|
151
162
|
const args = [
|
|
152
|
-
...
|
|
163
|
+
...packageManagerArgs,
|
|
153
164
|
name.filesafeName,
|
|
154
165
|
"--yes",
|
|
155
166
|
"--template",
|
|
@@ -322,13 +333,58 @@ async function updatePackageJson({ localDev, name, projectPath, task, versions,
|
|
|
322
333
|
if (!packageJson.overrides) {
|
|
323
334
|
packageJson.overrides = {};
|
|
324
335
|
}
|
|
325
|
-
// react
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
336
|
+
// The "react" version in expo-template-blank-typescript for SDK 54 is
|
|
337
|
+
// 19.1.0, but it needs to be 19.1.4 to satisfy react-native-macos and
|
|
338
|
+
// react-native-windows. We find that the React Native HelloWorld template
|
|
339
|
+
// is more reliable as a source of truth for the best "react" and
|
|
340
|
+
// "react-native" versions to satisfy them, but it's still a best of a bad
|
|
341
|
+
// job.
|
|
342
|
+
let facebookDeps;
|
|
343
|
+
try {
|
|
344
|
+
const packageJson = (await getReactNativePackageJson(versions.minor));
|
|
345
|
+
facebookDeps = {
|
|
346
|
+
react: packageJson.dependencies.react,
|
|
347
|
+
"react-native": packageJson.dependencies["react-native"],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
// It's totally possible to get rate-limited by the GitHub API, so we'll
|
|
352
|
+
// avoid failing the entire command just for a bad fetch here.
|
|
353
|
+
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);
|
|
354
|
+
}
|
|
355
|
+
if (facebookDeps) {
|
|
356
|
+
packageJson.dependencies.react = facebookDeps.react;
|
|
357
|
+
packageJson.overrides.react = facebookDeps.react;
|
|
358
|
+
packageJson.dependencies["react-native"] = facebookDeps["react-native"];
|
|
359
|
+
packageJson.overrides["react-native"] = facebookDeps["react-native"];
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
// react-native-macos and react-native-windows may declare conflicting
|
|
363
|
+
// peer dependency ranges to what the Expo template provides.
|
|
364
|
+
if (packageJson.dependencies.react) {
|
|
365
|
+
packageJson.overrides.react = packageJson.dependencies.react;
|
|
366
|
+
}
|
|
367
|
+
if (packageJson.dependencies["react-native"]) {
|
|
368
|
+
packageJson.overrides["react-native"] = packageJson.dependencies["react-native"];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (!packageJson.expo) {
|
|
372
|
+
packageJson.expo = {};
|
|
373
|
+
}
|
|
374
|
+
if (!packageJson.expo.install) {
|
|
375
|
+
packageJson.expo.install = {};
|
|
376
|
+
}
|
|
377
|
+
if (!packageJson.expo.install.exclude) {
|
|
378
|
+
packageJson.expo.install.exclude = [];
|
|
329
379
|
}
|
|
330
|
-
|
|
331
|
-
|
|
380
|
+
// Stop `expo start` erroneously warning about our overrides.
|
|
381
|
+
// - https://github.com/shirakaba/expo-desktop/issues/15
|
|
382
|
+
// - https://docs.expo.dev/versions/latest/config/package-json/#installexclude
|
|
383
|
+
if (!packageJson.expo.install.exclude.includes("react")) {
|
|
384
|
+
packageJson.expo.install.exclude.push("react");
|
|
385
|
+
}
|
|
386
|
+
if (!packageJson.expo.install.exclude.includes("react-native")) {
|
|
387
|
+
packageJson.expo.install.exclude.push("react-native");
|
|
332
388
|
}
|
|
333
389
|
}
|
|
334
390
|
try {
|
|
@@ -340,6 +396,10 @@ async function updatePackageJson({ localDev, name, projectPath, task, versions,
|
|
|
340
396
|
console.log(`${green("◆")} Altered package.json.\n`);
|
|
341
397
|
return { name: nameBefore };
|
|
342
398
|
}
|
|
399
|
+
async function getReactNativePackageJson(minor) {
|
|
400
|
+
const response = await fetch(`https://raw.githubusercontent.com/facebook/react-native/refs/heads/0.${minor}-stable/private/helloworld/package.json`);
|
|
401
|
+
return await response.json();
|
|
402
|
+
}
|
|
343
403
|
async function npmInstall({ asNewWorkspace, cwd, packageManager, }) {
|
|
344
404
|
const command = packageManager;
|
|
345
405
|
const args = ["install"];
|
|
@@ -585,6 +645,93 @@ module.exports = function (api) {
|
|
|
585
645
|
}
|
|
586
646
|
console.log(`\n${green("◆")} Wrote babel.config.js.\n`);
|
|
587
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* Improve the App.tsx so that it's not white-on-white on macOS in dark mode.
|
|
650
|
+
* Here, we simply ensure that it's black-on-white in both light and dark modes.
|
|
651
|
+
*
|
|
652
|
+
* @see https://github.com/shirakaba/expo-desktop/pull/14#issuecomment-4614018796
|
|
653
|
+
* @see https://github.com/expo/expo/blob/sdk-54/templates/expo-template-blank-typescript/App.tsx
|
|
654
|
+
*/
|
|
655
|
+
async function improveAppTsx({ projectPath }) {
|
|
656
|
+
const metroConfigPath = path.resolve(projectPath, "App.tsx");
|
|
657
|
+
console.log(`${cyan("◆")} Overwriting App.tsx…\n`);
|
|
658
|
+
try {
|
|
659
|
+
await fs.writeFile(metroConfigPath, `
|
|
660
|
+
import { StatusBar } from 'expo-status-bar';
|
|
661
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
662
|
+
|
|
663
|
+
export default function App() {
|
|
664
|
+
return (
|
|
665
|
+
<View style={styles.container}>
|
|
666
|
+
<Text style={styles.text}>Open up App.tsx to start working on your app!</Text>
|
|
667
|
+
<StatusBar style="auto" />
|
|
668
|
+
</View>
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const styles = StyleSheet.create({
|
|
673
|
+
container: {
|
|
674
|
+
flex: 1,
|
|
675
|
+
backgroundColor: '#fff',
|
|
676
|
+
alignItems: 'center',
|
|
677
|
+
justifyContent: 'center',
|
|
678
|
+
},
|
|
679
|
+
text: {
|
|
680
|
+
color: '#000',
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
`.trim() + "\n", "utf-8");
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
log.error(`Error improving ${yellow("App.tsx")} file${error instanceof Error ? `: ${error.message}` : "."}`);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
console.log(`\n${green("◆")} Overwrote App.tsx.\n`);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Stage and commit all changes.
|
|
693
|
+
*
|
|
694
|
+
* @see https://github.com/expo/expo/blob/main/packages/create-expo/src/utils/git.ts
|
|
695
|
+
*/
|
|
696
|
+
async function commitChanges({ projectPath }) {
|
|
697
|
+
if (!(await hasProjectGitRepositoryAsync(projectPath))) {
|
|
698
|
+
console.warn(`\n${yellow("⚠")} Skipping git commit: create-expo-app did not initialize a Git repository in this project (this happens when creating inside an existing repo, e.g. in a monorepo).\n`);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
await tasks([
|
|
703
|
+
promisifiedSpawnTask({
|
|
704
|
+
title: "git add",
|
|
705
|
+
command: "git",
|
|
706
|
+
args: ["add", "-A"],
|
|
707
|
+
options: { cwd: projectPath, stdio: "ignore" },
|
|
708
|
+
}),
|
|
709
|
+
]);
|
|
710
|
+
}
|
|
711
|
+
catch (error) {
|
|
712
|
+
log.error(`Error running ${yellow("git add -A")}${error instanceof Error ? `: ${error.message}` : "."}`);
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
if (!(await hasGitStagedChangesAsync(projectPath))) {
|
|
716
|
+
console.log(`\n${green("◆")} Nothing to commit.\n`);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
await tasks([
|
|
721
|
+
promisifiedSpawnTask({
|
|
722
|
+
title: "git commit",
|
|
723
|
+
command: "git",
|
|
724
|
+
args: ["commit", "-m", "Set up with expo-desktop"],
|
|
725
|
+
options: { cwd: projectPath, stdio: "ignore" },
|
|
726
|
+
}),
|
|
727
|
+
]);
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
log.error(`Error running ${yellow("git commit")}${error instanceof Error ? `: ${error.message}` : "."}`);
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
console.log(`\n${green("◆")} Committed changes.\n`);
|
|
734
|
+
}
|
|
588
735
|
/**
|
|
589
736
|
* For now, this is just a convenience script to allow me to run the config
|
|
590
737
|
* plugins again after creating the app, to help with development. But it is
|
|
@@ -696,3 +843,36 @@ async function applyConfigPlugins(options) {
|
|
|
696
843
|
await applyConfigPlugins(info);
|
|
697
844
|
`.trim() + "\n", "utf-8");
|
|
698
845
|
}
|
|
846
|
+
function logProjectReady({ cdPath, packageManager, }) {
|
|
847
|
+
const lines = [
|
|
848
|
+
"✅ Your project is ready!",
|
|
849
|
+
"",
|
|
850
|
+
"To run your project, first navigate to the directory and start up the packager:",
|
|
851
|
+
"",
|
|
852
|
+
`- cd ${cdPath}`,
|
|
853
|
+
`- ${formatRunCommand(packageManager, "start")}`,
|
|
854
|
+
"",
|
|
855
|
+
"… and then run the target platform of your choice:",
|
|
856
|
+
"",
|
|
857
|
+
`- ${formatRunCommand(packageManager, "android")}`,
|
|
858
|
+
`- ${formatRunCommand(packageManager, "ios")}`,
|
|
859
|
+
`- ${formatRunCommand(packageManager, "web")}`,
|
|
860
|
+
`- ${formatRunCommand(packageManager, "macos")}`,
|
|
861
|
+
`- ${formatRunCommand(packageManager, "windows")}`,
|
|
862
|
+
"",
|
|
863
|
+
];
|
|
864
|
+
log.success(lines.join("\n"), { withGuide: false });
|
|
865
|
+
}
|
|
866
|
+
export function formatRunCommand(packageManager, cmd) {
|
|
867
|
+
switch (packageManager) {
|
|
868
|
+
case "pnpm":
|
|
869
|
+
return `pnpm run ${cmd}`;
|
|
870
|
+
case "yarn":
|
|
871
|
+
return `yarn ${cmd}`;
|
|
872
|
+
case "bun":
|
|
873
|
+
return `bun run ${cmd}`;
|
|
874
|
+
case "npm":
|
|
875
|
+
default:
|
|
876
|
+
return `npm run ${cmd}`;
|
|
877
|
+
}
|
|
878
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-desktop",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
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.
|
|
41
|
-
"expo-desktop-prebuild-config": "^1.0.
|
|
40
|
+
"expo-desktop-config-plugins": "^1.1.32",
|
|
41
|
+
"expo-desktop-prebuild-config": "^1.0.18",
|
|
42
42
|
"glob": "^10.5.0",
|
|
43
43
|
"kleur": "^4.1.5",
|
|
44
44
|
"mustache": "^4.2.0",
|