create-expo 5.0.1 → 5.1.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.
Files changed (54) hide show
  1. package/build/{205.index.js → 589.index.js} +392 -91
  2. package/build/601.index.js +20 -11
  3. package/build/{223.index.js → 870.index.js} +2 -10
  4. package/build/Examples.d.ts +38 -0
  5. package/build/Template.d.ts +98 -0
  6. package/build/__mocks__/fs.d.ts +1 -0
  7. package/build/__mocks__/ora.d.ts +1 -0
  8. package/build/__tests__/Examples.test.d.ts +1 -0
  9. package/build/__tests__/Template.test.d.ts +1 -0
  10. package/build/__tests__/configureWorkspaces.test.d.ts +1 -0
  11. package/build/__tests__/createAsync.test.d.ts +1 -0
  12. package/build/__tests__/createExpoConfig.test.d.ts +1 -0
  13. package/build/__tests__/createFileTransformer.test.d.ts +1 -0
  14. package/build/__tests__/generateAgentFiles.test.d.ts +1 -0
  15. package/build/__tests__/promptSdkVersion.test.d.ts +1 -0
  16. package/build/__tests__/resolvePackageManager.test.d.ts +1 -0
  17. package/build/__tests__/sanitizeTemplate.test.d.ts +1 -0
  18. package/build/__tests__/telemetry.test.d.ts +1 -0
  19. package/build/cli.d.ts +1 -0
  20. package/build/configureWorkspaces.d.ts +9 -0
  21. package/build/createAsync.d.ts +11 -0
  22. package/build/createExpoConfig.d.ts +38 -0
  23. package/build/createFileTransform.d.ts +5 -0
  24. package/build/error.d.ts +9 -0
  25. package/build/generateAgentFiles.d.ts +1 -0
  26. package/build/index.d.ts +1 -0
  27. package/build/index.js +38 -10
  28. package/build/legacyTemplates.d.ts +7 -0
  29. package/build/log.d.ts +12 -0
  30. package/build/paths.d.ts +2 -0
  31. package/build/promptSdkVersion.d.ts +23 -0
  32. package/build/resolvePackageManager.d.ts +12 -0
  33. package/build/resolveProjectRoot.d.ts +3 -0
  34. package/build/sessionStorage.d.ts +8 -0
  35. package/build/telemetry.d.ts +29 -0
  36. package/build/utils/__tests__/args.test.d.ts +1 -0
  37. package/build/utils/__tests__/array.test.d.ts +1 -0
  38. package/build/utils/__tests__/log.test.d.ts +1 -0
  39. package/build/utils/__tests__/npm.test.d.ts +1 -0
  40. package/build/utils/__tests__/obj.test.d.ts +1 -0
  41. package/build/utils/args.d.ts +33 -0
  42. package/build/utils/array.d.ts +1 -0
  43. package/build/utils/dir.d.ts +1 -0
  44. package/build/utils/env.d.ts +14 -0
  45. package/build/utils/fetch.d.ts +2 -0
  46. package/build/utils/git.d.ts +1 -0
  47. package/build/utils/github.d.ts +2 -0
  48. package/build/utils/log.d.ts +6 -0
  49. package/build/utils/npm.d.ts +52 -0
  50. package/build/utils/obj.d.ts +1 -0
  51. package/build/utils/tar.d.ts +10 -0
  52. package/build/utils/update-check.d.ts +2 -0
  53. package/package.json +31 -21
  54. package/build/517.index.js +0 -1380
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
- exports.id = 205;
3
- exports.ids = [205,601];
2
+ exports.id = 589;
3
+ exports.ids = [589,601];
4
4
  exports.modules = {
5
5
 
6
- /***/ 8205:
6
+ /***/ 589:
7
7
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8
8
 
9
9
 
@@ -12,7 +12,7 @@ __webpack_require__.d(__webpack_exports__, {
12
12
  createAsync: () => (/* binding */ createAsync)
13
13
  });
14
14
 
15
- // UNUSED EXPORTS: logNodeInstallWarning
15
+ // UNUSED EXPORTS: logNodeInstallWarning, setupDependenciesAsync
16
16
 
17
17
  // EXTERNAL MODULE: ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js
18
18
  var source = __webpack_require__(2314);
@@ -25,7 +25,6 @@ var external_path_ = __webpack_require__(6928);
25
25
  var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_);
26
26
  // EXTERNAL MODULE: ../@expo/json-file/build/JsonFile.js
27
27
  var JsonFile = __webpack_require__(3064);
28
- var JsonFile_default = /*#__PURE__*/__webpack_require__.n(JsonFile);
29
28
  // EXTERNAL MODULE: ../../node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/index.js
30
29
  var prompts = __webpack_require__(6133);
31
30
  var prompts_default = /*#__PURE__*/__webpack_require__.n(prompts);
@@ -36,6 +35,67 @@ var index_min = __webpack_require__(9337);
36
35
  // EXTERNAL MODULE: ../../node_modules/.pnpm/ora@3.4.0/node_modules/ora/index.js
37
36
  var ora = __webpack_require__(5344);
38
37
  var ora_default = /*#__PURE__*/__webpack_require__.n(ora);
38
+ // EXTERNAL MODULE: ./src/log.ts
39
+ var log = __webpack_require__(1545);
40
+ ;// CONCATENATED MODULE: ./src/createExpoConfig.ts
41
+
42
+
43
+
44
+ const debug = __webpack_require__(6675)('expo:init:monorepo-config');
45
+ const CREATE_EXPO_CONFIG_NAME = '.create-expo.json';
46
+ /**
47
+ * Read `.create-expo.json` from the project root and immediately
48
+ * delete the file from disk so it doesn't leak into the user's project.
49
+ * Returns the parsed config (or `undefined` if there's no file / it's
50
+ * malformed). This is the "use it once" entry point that template-extraction
51
+ * code paths should call — every downstream consumer takes the parsed
52
+ * object in memory.
53
+ */
54
+ async function consumeMonorepoConfigAsync(projectRoot) {
55
+ const config = await loadMonorepoConfigAsync(projectRoot);
56
+ await external_fs_default().promises
57
+ .rm(external_path_default().join(projectRoot, CREATE_EXPO_CONFIG_NAME), { force: true })
58
+ .catch(() => undefined);
59
+ return config;
60
+ }
61
+ /**
62
+ * Reads `.create-expo.json` from the project root without touching
63
+ * the file. Returns `undefined` if the file is missing, unreadable, or
64
+ * malformed — a missing file is the common case (single-app templates) and
65
+ * shouldn't warn.
66
+ *
67
+ * Prefer `consumeMonorepoConfigAsync` for the create-expo flow so the
68
+ * template-only config doesn't leak into the user's project. This function
69
+ * stays exported for tests and any other read-only callers.
70
+ */
71
+ async function loadMonorepoConfigAsync(projectRoot) {
72
+ const configPath = external_path_default().join(projectRoot, CREATE_EXPO_CONFIG_NAME);
73
+ let contents;
74
+ try {
75
+ contents = await external_fs_default().promises.readFile(configPath, { encoding: 'utf-8' });
76
+ }
77
+ catch (error) {
78
+ if (error?.code !== 'ENOENT') {
79
+ log/* Log */.tG.log(`Could not read ${CREATE_EXPO_CONFIG_NAME}; ignoring: ${error?.message ?? error}`);
80
+ }
81
+ return undefined;
82
+ }
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(contents);
86
+ }
87
+ catch (error) {
88
+ log/* Log */.tG.log(`${CREATE_EXPO_CONFIG_NAME} could not be parsed as JSON; ignoring: ${error?.message ?? error}`);
89
+ return undefined;
90
+ }
91
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
92
+ log/* Log */.tG.log(`${CREATE_EXPO_CONFIG_NAME} did not parse as a JSON object; ignoring.`);
93
+ return undefined;
94
+ }
95
+ debug(`Loaded ${CREATE_EXPO_CONFIG_NAME}`);
96
+ return parsed;
97
+ }
98
+
39
99
  // EXTERNAL MODULE: ../../node_modules/.pnpm/multitars@1.0.2/node_modules/multitars/dist/multitars.mjs
40
100
  var multitars = __webpack_require__(6378);
41
101
  // EXTERNAL MODULE: ../../node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/index.js
@@ -45,7 +105,7 @@ var picomatch_default = /*#__PURE__*/__webpack_require__.n(picomatch);
45
105
 
46
106
 
47
107
 
48
- const debug = __webpack_require__(6675)('expo:init:fileTransform');
108
+ const createFileTransform_debug = __webpack_require__(6675)('expo:init:fileTransform');
49
109
  function sanitizedName(name) {
50
110
  return name
51
111
  .replace(/[\W_]+/g, '')
@@ -87,16 +147,14 @@ function createEntryRenamer(name) {
87
147
  }
88
148
  function createGlobFilter(globPattern, options) {
89
149
  const matcher = picomatch_default()(globPattern, options);
90
- debug('filter: created for pattern %s (%s)', globPattern);
150
+ createFileTransform_debug('filter: created for pattern %s (%s)', globPattern);
91
151
  return (path) => {
92
152
  const included = matcher(path);
93
- debug('filter: %s - %s', included ? 'include' : 'exclude', path);
153
+ createFileTransform_debug('filter: %s - %s', included ? 'include' : 'exclude', path);
94
154
  return included;
95
155
  };
96
156
  }
97
157
 
98
- // EXTERNAL MODULE: ./src/log.ts
99
- var log = __webpack_require__(1545);
100
158
  // EXTERNAL MODULE: ./src/resolvePackageManager.ts
101
159
  var resolvePackageManager = __webpack_require__(9560);
102
160
  // EXTERNAL MODULE: ./src/utils/env.ts
@@ -131,6 +189,57 @@ var external_os_ = __webpack_require__(857);
131
189
  var external_os_default = /*#__PURE__*/__webpack_require__.n(external_os_);
132
190
  // EXTERNAL MODULE: external "stream"
133
191
  var external_stream_ = __webpack_require__(2203);
192
+ ;// CONCATENATED MODULE: ./src/legacyTemplates.ts
193
+
194
+
195
+
196
+ const LEGACY_TEMPLATES = [
197
+ {
198
+ title: 'Default',
199
+ value: 'expo-template-default',
200
+ description: 'includes tools recommended for most app developers',
201
+ },
202
+ {
203
+ title: 'Blank',
204
+ value: 'expo-template-blank',
205
+ description: 'a minimal app as clean as an empty canvas',
206
+ },
207
+ {
208
+ title: 'Blank (TypeScript)',
209
+ value: 'expo-template-blank-typescript',
210
+ description: 'blank app with TypeScript enabled',
211
+ },
212
+ {
213
+ title: 'Navigation (TypeScript)',
214
+ value: 'expo-template-tabs',
215
+ description: 'File-based routing with TypeScript enabled',
216
+ },
217
+ {
218
+ title: 'Blank (Bare)',
219
+ value: 'expo-template-bare-minimum',
220
+ description: 'blank app with the native code exposed (expo prebuild)',
221
+ },
222
+ ];
223
+ const ALIASES = LEGACY_TEMPLATES.map(({ value }) => value);
224
+ async function promptTemplateAsync() {
225
+ if (env.env.CI) {
226
+ throw new Error('Cannot prompt for template in CI');
227
+ }
228
+ const { answer } = await prompts_default()({
229
+ type: 'select',
230
+ name: 'answer',
231
+ message: 'Choose a template:',
232
+ choices: LEGACY_TEMPLATES,
233
+ });
234
+ if (!answer) {
235
+ console.log();
236
+ console.log(source_default() `Specify the template name, example: {cyan --template expo-template-blank}`);
237
+ console.log();
238
+ process.exit(1);
239
+ }
240
+ return answer;
241
+ }
242
+
134
243
  // EXTERNAL MODULE: external "node:crypto"
135
244
  var external_node_crypto_ = __webpack_require__(5217);
136
245
  var external_node_crypto_default = /*#__PURE__*/__webpack_require__.n(external_node_crypto_);
@@ -262,57 +371,6 @@ async function extractAsync(input, output, options) {
262
371
  await extractStream(Readable.toWeb(fs.createReadStream(input)), output, options);
263
372
  }
264
373
 
265
- ;// CONCATENATED MODULE: ./src/legacyTemplates.ts
266
-
267
-
268
-
269
- const LEGACY_TEMPLATES = [
270
- {
271
- title: 'Default',
272
- value: 'expo-template-default',
273
- description: 'includes tools recommended for most app developers',
274
- },
275
- {
276
- title: 'Blank',
277
- value: 'expo-template-blank',
278
- description: 'a minimal app as clean as an empty canvas',
279
- },
280
- {
281
- title: 'Blank (TypeScript)',
282
- value: 'expo-template-blank-typescript',
283
- description: 'blank app with TypeScript enabled',
284
- },
285
- {
286
- title: 'Navigation (TypeScript)',
287
- value: 'expo-template-tabs',
288
- description: 'File-based routing with TypeScript enabled',
289
- },
290
- {
291
- title: 'Blank (Bare)',
292
- value: 'expo-template-bare-minimum',
293
- description: 'blank app with the native code exposed (expo prebuild)',
294
- },
295
- ];
296
- const ALIASES = LEGACY_TEMPLATES.map(({ value }) => value);
297
- async function promptTemplateAsync() {
298
- if (env.env.CI) {
299
- throw new Error('Cannot prompt for template in CI');
300
- }
301
- const { answer } = await prompts_default()({
302
- type: 'select',
303
- name: 'answer',
304
- message: 'Choose a template:',
305
- choices: LEGACY_TEMPLATES,
306
- });
307
- if (!answer) {
308
- console.log();
309
- console.log(source_default() `Specify the template name, example: {cyan --template expo-template-blank}`);
310
- console.log();
311
- process.exit(1);
312
- }
313
- return answer;
314
- }
315
-
316
374
  ;// CONCATENATED MODULE: ./src/utils/npm.ts
317
375
 
318
376
 
@@ -520,6 +578,7 @@ async function downloadAndExtractNpmModuleAsync(npmName, output, props) {
520
578
 
521
579
 
522
580
 
581
+
523
582
  const github_debug = __webpack_require__(6675)('expo:init:github');
524
583
  // See: https://github.com/expo/expo/blob/a5a6eecb082b2c7a7fc9956141738231c7df473f/packages/%40expo/cli/src/prebuild/resolveTemplate.ts#L60-L84
525
584
  async function getGitHubRepoAsync(url) {
@@ -565,9 +624,15 @@ async function extractRemoteGitHubTarballAsync(url, repo, output, props) {
565
624
  const strip = directory.length + 1;
566
625
  // Only extract the relevant (sub)directories, ignoring irrelevant files
567
626
  // The filter auto-ignores dotfiles, unless explicitly included
568
- const filter = createGlobFilter(!directory.length
569
- ? ['*/**', '*/ios/.xcode.env']
570
- : [`*/${directory.join('/')}/**`, `*/${directory.join('/')}/ios/.xcode.env`], {
627
+ const subDirPrefix = directory.length ? `*/${directory.join('/')}` : '*';
628
+ const filter = createGlobFilter([
629
+ `${subDirPrefix}/**`,
630
+ `${subDirPrefix}/ios/.xcode.env`,
631
+ // Templates ship their monorepo config as a root-level dotfile; without
632
+ // an explicit allow-list entry, picomatch's default `dot: false` drops
633
+ // it during extraction so downstream consumers see no config at all.
634
+ `${subDirPrefix}/${CREATE_EXPO_CONFIG_NAME}`,
635
+ ], {
571
636
  // Always ignore the `.xcworkspace` folder
572
637
  ignore: ['**/ios/*.xcworkspace/**'],
573
638
  });
@@ -582,7 +647,7 @@ async function downloadAndExtractGitHubRepositoryAsync(repoUrl, output, props) {
582
647
  const info = await getGitHubRepoAsync(repoUrl);
583
648
  const isValid = await isValidGitHubRepoAsync(info);
584
649
  if (!isValid) {
585
- throw new Error(`Could not to locate repository for "${repoUrl}", ensure this repository exists`);
650
+ throw new Error(`Could not locate repository for "${repoUrl}", ensure this repository exists`);
586
651
  }
587
652
  const url = `https://codeload.github.com/${info.owner}/${info.name}/tar.gz/${info.branch}`;
588
653
  github_debug('Resolved GitHub repository', info);
@@ -604,6 +669,7 @@ async function downloadAndExtractGitHubRepositoryAsync(repoUrl, output, props) {
604
669
 
605
670
 
606
671
 
672
+
607
673
  const Template_debug = __webpack_require__(6675)('expo:init:template');
608
674
  const isMacOS = process.platform === 'darwin';
609
675
  // keep this list in sync with the validation helper in WWW: src/utils/experienceParser.ts
@@ -688,6 +754,11 @@ function resolvePackageModuleId(moduleId) {
688
754
  /**
689
755
  * Extract a template app to a given file path and clean up any properties left over from npm to
690
756
  * prepare it for usage.
757
+ *
758
+ * If the template ships a `.create-expo.json`, its `renamePatterns`
759
+ * field overrides the default rename config used by the HelloWorld
760
+ * find-and-replace pass. The config file is read once and deleted from disk
761
+ * immediately so it can never leak into the user's project.
691
762
  */
692
763
  async function extractAndPrepareTemplateAppAsync(projectRoot, { npmPackage }) {
693
764
  const projectName = external_path_default().basename(projectRoot);
@@ -705,8 +776,12 @@ async function extractAndPrepareTemplateAppAsync(projectRoot, { npmPackage }) {
705
776
  disableCache: type === 'file',
706
777
  });
707
778
  }
779
+ const monorepoConfig = await consumeMonorepoConfigAsync(projectRoot);
708
780
  try {
709
- const files = await getTemplateFilesToRenameAsync({ cwd: projectRoot });
781
+ const files = await getTemplateFilesToRenameAsync({
782
+ cwd: projectRoot,
783
+ renameConfig: monorepoConfig?.renamePatterns,
784
+ });
710
785
  await renameTemplateAppNameAsync({
711
786
  cwd: projectRoot,
712
787
  files,
@@ -758,8 +833,8 @@ function escapeXMLCharacters(original) {
758
833
  * Whitespace is trimmed and whitespace-only lines are ignored.
759
834
  *
760
835
  * If no rename config has been passed directly to
761
- * `getTemplateFilesToRenameAsync()` then this default rename config will be
762
- * used instead.
836
+ * `getTemplateFilesToRenameAsync()`, then this default
837
+ * rename config will be used instead.
763
838
  */
764
839
  const defaultRenameConfig = [
765
840
  // Common
@@ -818,7 +893,9 @@ async function renameTemplateAppNameAsync({ cwd, name, files, }) {
818
893
  const absoluteFilePath = external_path_default().resolve(cwd, file);
819
894
  let contents;
820
895
  try {
821
- contents = await external_fs_default().promises.readFile(absoluteFilePath, { encoding: 'utf-8' });
896
+ contents = await external_fs_default().promises.readFile(absoluteFilePath, {
897
+ encoding: 'utf-8',
898
+ });
822
899
  }
823
900
  catch (error) {
824
901
  throw new Error(`Failed to read template file: "${absoluteFilePath}". Was it removed mid-operation?`, { cause: error });
@@ -874,12 +951,14 @@ async function sanitizeTemplateAsync(projectRoot) {
874
951
  name: projectName,
875
952
  slug: projectName,
876
953
  };
877
- const appFile = new (JsonFile_default())(external_path_default().join(projectRoot, 'app.json'), { default: {} });
954
+ const appFile = new JsonFile["default"](external_path_default().join(projectRoot, 'app.json'), {
955
+ default: {},
956
+ });
878
957
  const appContent = (await appFile.readAsync());
879
958
  const appJson = deepMerge(appContent, 'expo' in appContent ? { expo: defaultConfig } : defaultConfig);
880
959
  await appFile.writeAsync(appJson);
881
960
  Template_debug(`Created app.json:\n%O`, appJson);
882
- const packageFile = new (JsonFile_default())(external_path_default().join(projectRoot, 'package.json'));
961
+ const packageFile = new JsonFile["default"](external_path_default().join(projectRoot, 'package.json'));
883
962
  const packageJson = await packageFile.readAsync();
884
963
  // name and version are required for yarn workspaces (monorepos)
885
964
  const inputName = 'name' in appJson ? appJson.name : appJson.expo.name;
@@ -1024,6 +1103,7 @@ function logNewSection(title) {
1024
1103
 
1025
1104
 
1026
1105
 
1106
+
1027
1107
  const Examples_debug = __webpack_require__(6675)('expo:init:template');
1028
1108
  /** List all existing examples directory from https://github.com/expo/examples. */
1029
1109
  async function listExamplesAsync() {
@@ -1079,7 +1159,14 @@ async function promptExamplesAsync() {
1079
1159
  }
1080
1160
  return answer;
1081
1161
  }
1082
- /** Download and move the selected example from https://github.com/expo/examples. */
1162
+ /**
1163
+ * Download and move the selected example from https://github.com/expo/examples.
1164
+ *
1165
+ * If the example ships a `.create-expo.json`, its `renamePatterns`
1166
+ * field overrides the default rename config used by the HelloWorld
1167
+ * find-and-replace pass. The config file is read once and deleted from disk
1168
+ * immediately so it can never leak into the user's project.
1169
+ */
1083
1170
  async function downloadAndExtractExampleAsync(root, name) {
1084
1171
  const projectName = external_path_default().basename(root);
1085
1172
  const response = await fetch_fetch('https://codeload.github.com/expo/examples/tar.gz/master');
@@ -1097,7 +1184,11 @@ async function downloadAndExtractExampleAsync(root, name) {
1097
1184
  strip: 2,
1098
1185
  filter: (entryName) => entryName.startsWith(prefix),
1099
1186
  });
1100
- const files = await getTemplateFilesToRenameAsync({ cwd: root });
1187
+ const monorepoConfig = await consumeMonorepoConfigAsync(root);
1188
+ const files = await getTemplateFilesToRenameAsync({
1189
+ cwd: root,
1190
+ renameConfig: monorepoConfig?.renamePatterns,
1191
+ });
1101
1192
  await renameTemplateAppNameAsync({
1102
1193
  cwd: root,
1103
1194
  files,
@@ -1123,13 +1214,187 @@ async function sanitizeScriptsAsync(root) {
1123
1214
  ios: 'expo start --ios',
1124
1215
  web: 'expo start --web',
1125
1216
  };
1126
- const packageFile = new (JsonFile_default())(external_path_default().join(root, 'package.json'));
1217
+ const packageFile = new JsonFile["default"](external_path_default().join(root, 'package.json'));
1127
1218
  const packageJson = await packageFile.readAsync();
1128
1219
  const scripts = (packageJson.scripts ?? {});
1129
1220
  packageJson.scripts = { ...defaultScripts, ...scripts };
1130
1221
  await packageFile.writeAsync(packageJson);
1131
1222
  }
1132
1223
 
1224
+ // EXTERNAL MODULE: ../../node_modules/.pnpm/resolve-workspace-root@2.0.1/node_modules/resolve-workspace-root/build/index.js
1225
+ var resolve_workspace_root_build = __webpack_require__(8665);
1226
+ ;// CONCATENATED MODULE: ./src/configureWorkspaces.ts
1227
+
1228
+
1229
+
1230
+ const configureWorkspaces_debug = __webpack_require__(6675)('expo:init:workspaces');
1231
+ const PNPM_WORKSPACE_FILENAME = 'pnpm-workspace.yaml';
1232
+ const WORKSPACE_DEP_FIELDS = [
1233
+ 'dependencies',
1234
+ 'devDependencies',
1235
+ 'peerDependencies',
1236
+ 'optionalDependencies',
1237
+ ];
1238
+ /**
1239
+ * Normalize a monorepo template's workspace-package dependency specs to the
1240
+ * chosen package manager's convention, and — for pnpm — write a
1241
+ * `pnpm-workspace.yaml` listing the workspace packages so they're picked up
1242
+ * regardless of the root `package.json` `workspaces` field.
1243
+ */
1244
+ async function configureWorkspacesAsync(projectRoot, packageManager) {
1245
+ const workspacePatterns = (await (0,resolve_workspace_root_build.getWorkspaceGlobsAsync)(projectRoot)) ?? [];
1246
+ if (!workspacePatterns.length) {
1247
+ configureWorkspaces_debug(`No workspaces declared at ${projectRoot}; skipping workspace configuration`);
1248
+ return;
1249
+ }
1250
+ const memberPaths = await resolveWorkspaceMemberPathsAsync(projectRoot, workspacePatterns);
1251
+ configureWorkspaces_debug(`Found ${memberPaths.length} workspace member(s): ${memberPaths.join(', ')}`);
1252
+ const targetSpec = packageManager === 'npm' ? '*' : 'workspace:*';
1253
+ for (const memberPath of memberPaths) {
1254
+ await normalizeWorkspaceDepsAsync(memberPath, targetSpec);
1255
+ }
1256
+ if (packageManager === 'pnpm') {
1257
+ await ensurePnpmWorkspaceYamlAsync(projectRoot, workspacePatterns);
1258
+ }
1259
+ }
1260
+ async function resolveWorkspaceMemberPathsAsync(projectRoot, patterns) {
1261
+ const members = [];
1262
+ const seen = new Set();
1263
+ for (const pattern of patterns) {
1264
+ const expanded = await expandPatternAsync(projectRoot, pattern);
1265
+ for (const member of expanded) {
1266
+ if (!seen.has(member)) {
1267
+ seen.add(member);
1268
+ members.push(member);
1269
+ }
1270
+ }
1271
+ }
1272
+ return members;
1273
+ }
1274
+ async function expandPatternAsync(projectRoot, pattern) {
1275
+ const segments = pattern.split('/').filter((s) => s.length > 0);
1276
+ if (segments.length === 0) {
1277
+ return [];
1278
+ }
1279
+ // Literal path (no wildcards anywhere) — return it if its package.json exists.
1280
+ if (!segments.some((s) => s.includes('*'))) {
1281
+ const memberDir = external_path_default().resolve(projectRoot, ...segments);
1282
+ return (await packageJsonExistsAsync(memberDir)) ? [memberDir] : [];
1283
+ }
1284
+ // Single trailing wildcard segment — read the parent dir, keep children
1285
+ // whose package.json exists.
1286
+ if (segments[segments.length - 1] === '*' &&
1287
+ !segments.slice(0, -1).some((s) => s.includes('*'))) {
1288
+ const parentDir = external_path_default().resolve(projectRoot, ...segments.slice(0, -1));
1289
+ let entries;
1290
+ try {
1291
+ entries = await external_fs_default().promises.readdir(parentDir, { withFileTypes: true });
1292
+ }
1293
+ catch (error) {
1294
+ configureWorkspaces_debug(`Could not read workspace parent ${parentDir}: ${error?.message ?? error}`);
1295
+ return [];
1296
+ }
1297
+ const matches = [];
1298
+ for (const entry of entries) {
1299
+ if (!entry.isDirectory())
1300
+ continue;
1301
+ const memberDir = external_path_default().join(parentDir, entry.name);
1302
+ if (await packageJsonExistsAsync(memberDir)) {
1303
+ matches.push(memberDir);
1304
+ }
1305
+ }
1306
+ return matches;
1307
+ }
1308
+ configureWorkspaces_debug(`Unsupported workspace pattern "${pattern}" (only literal paths and trailing /* are handled)`);
1309
+ return [];
1310
+ }
1311
+ async function packageJsonExistsAsync(memberDir) {
1312
+ try {
1313
+ const stat = await external_fs_default().promises.stat(external_path_default().join(memberDir, 'package.json'));
1314
+ return stat.isFile();
1315
+ }
1316
+ catch {
1317
+ return false;
1318
+ }
1319
+ }
1320
+ async function normalizeWorkspaceDepsAsync(memberDir, targetSpec) {
1321
+ const pkgPath = external_path_default().join(memberDir, 'package.json');
1322
+ const pkg = await readJsonFileAsync(pkgPath);
1323
+ if (!pkg) {
1324
+ configureWorkspaces_debug(`Skipping ${pkgPath} (not readable)`);
1325
+ return;
1326
+ }
1327
+ let changed = false;
1328
+ for (const field of WORKSPACE_DEP_FIELDS) {
1329
+ const deps = pkg[field];
1330
+ if (!deps) {
1331
+ continue;
1332
+ }
1333
+ for (const [name, spec] of Object.entries(deps)) {
1334
+ if ((spec === 'workspace:*' || spec === '*') && spec !== targetSpec) {
1335
+ deps[name] = targetSpec;
1336
+ changed = true;
1337
+ }
1338
+ }
1339
+ }
1340
+ if (changed) {
1341
+ await external_fs_default().promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
1342
+ configureWorkspaces_debug(`Normalized workspace deps in ${pkgPath} → "${targetSpec}"`);
1343
+ }
1344
+ }
1345
+ async function readJsonFileAsync(filePath) {
1346
+ try {
1347
+ const contents = await external_fs_default().promises.readFile(filePath, 'utf-8');
1348
+ return JSON.parse(contents);
1349
+ }
1350
+ catch {
1351
+ return undefined;
1352
+ }
1353
+ }
1354
+ /**
1355
+ * Ensure `pnpm-workspace.yaml` contains a `packages:` block listing our
1356
+ * workspace patterns. If the file doesn't exist we write it from scratch. If
1357
+ * it does exist (e.g. the template ships its own with extra pnpm settings),
1358
+ * we preserve all existing content and only append `packages:`
1359
+ */
1360
+ async function ensurePnpmWorkspaceYamlAsync(projectRoot, patterns) {
1361
+ if (patterns.length === 0) {
1362
+ return;
1363
+ }
1364
+ const yamlPath = external_path_default().join(projectRoot, PNPM_WORKSPACE_FILENAME);
1365
+ let existing;
1366
+ try {
1367
+ existing = await external_fs_default().promises.readFile(yamlPath, 'utf-8');
1368
+ }
1369
+ catch (error) {
1370
+ if (error?.code !== 'ENOENT') {
1371
+ configureWorkspaces_debug(`Could not read existing ${yamlPath}: ${error?.message ?? error}`);
1372
+ }
1373
+ }
1374
+ const packagesBlock = [
1375
+ 'packages:',
1376
+ ...patterns.map((pattern) => ` - ${quoteIfNeeded(pattern)}`),
1377
+ ].join('\n');
1378
+ if (existing === undefined) {
1379
+ await external_fs_default().promises.writeFile(yamlPath, `${packagesBlock}\n`, 'utf-8');
1380
+ configureWorkspaces_debug(`Wrote ${yamlPath}`);
1381
+ return;
1382
+ }
1383
+ if (/^packages:/m.test(existing)) {
1384
+ configureWorkspaces_debug(`Existing ${yamlPath} already declares packages; leaving it alone`);
1385
+ return;
1386
+ }
1387
+ const needsLeadingNewline = existing.length > 0 && !existing.endsWith('\n');
1388
+ const updated = `${existing}${needsLeadingNewline ? '\n' : ''}${packagesBlock}\n`;
1389
+ await external_fs_default().promises.writeFile(yamlPath, updated, 'utf-8');
1390
+ configureWorkspaces_debug(`Appended packages: to existing ${yamlPath}`);
1391
+ }
1392
+ function quoteIfNeeded(value) {
1393
+ // Quote glob patterns that begin with `*`, `?`, `!`, or `&` so YAML doesn't
1394
+ // mis-parse them as tags or anchors.
1395
+ return /^[!*?&]/.test(value) ? `"${value}"` : value;
1396
+ }
1397
+
1133
1398
  ;// CONCATENATED MODULE: ./src/generateAgentFiles.ts
1134
1399
 
1135
1400
 
@@ -1446,12 +1711,23 @@ async function resolveProjectRootAsync(input) {
1446
1711
 
1447
1712
  // EXTERNAL MODULE: ./src/telemetry.ts + 2 modules
1448
1713
  var telemetry = __webpack_require__(601);
1714
+ // EXTERNAL MODULE: ./src/utils/update-check.ts
1715
+ var update_check = __webpack_require__(7517);
1449
1716
  ;// CONCATENATED MODULE: ./src/utils/git.ts
1450
1717
 
1451
1718
 
1452
1719
 
1453
1720
 
1721
+
1454
1722
  const git_debug = __webpack_require__(6675)('expo:init:git');
1723
+ const getPackageJson = () => {
1724
+ try {
1725
+ return __webpack_require__(8330);
1726
+ }
1727
+ catch {
1728
+ return null;
1729
+ }
1730
+ };
1455
1731
  /** Check if the given directory is inside an existing git repository */
1456
1732
  async function isInsideGitRepoAsync(root) {
1457
1733
  try {
@@ -1500,12 +1776,12 @@ async function initGitRepoAsync(root) {
1500
1776
  // User explicitly chose not to skip, proceed with git init (creates nested repo)
1501
1777
  git_debug(source_default().dim('User chose to initialize git inside existing repo.'));
1502
1778
  }
1503
- const packageJSON = __webpack_require__(392);
1779
+ const packageJSON = getPackageJson();
1504
1780
  // not in git tree, so let's init
1505
1781
  try {
1506
1782
  await spawnAsync_default()('git', ['init'], { stdio: 'ignore', cwd: root });
1507
1783
  await spawnAsync_default()('git', ['add', '-A'], { stdio: 'ignore', cwd: root });
1508
- const commitMsg = `Initial commit\n\nGenerated by ${packageJSON.name} ${packageJSON.version}.`;
1784
+ const commitMsg = `Initial commit\n\nGenerated by ${packageJSON?.name ?? update_check.PACKAGE_NAME} ${packageJSON?.version ?? '0.0.0'}.`;
1509
1785
  await spawnAsync_default()('git', ['commit', '-m', commitMsg], {
1510
1786
  stdio: 'ignore',
1511
1787
  cwd: root,
@@ -1557,6 +1833,7 @@ function withSectionLog(action, message) {
1557
1833
 
1558
1834
 
1559
1835
 
1836
+
1560
1837
  const createAsync_debug = __webpack_require__(6675)('expo:init:create');
1561
1838
  async function resolveProjectRootArgAsync(inputPath, { yes }) {
1562
1839
  if (!inputPath && yes) {
@@ -1573,13 +1850,18 @@ async function resolveProjectRootArgAsync(inputPath, { yes }) {
1573
1850
  async function setupDependenciesAsync(projectRoot, props) {
1574
1851
  const shouldInstall = props.install;
1575
1852
  const packageManager = (0,resolvePackageManager/* resolvePackageManager */._c)();
1853
+ // For monorepo templates: normalize workspace-package dependency specs to
1854
+ // the chosen package manager's convention, and write a `pnpm-workspace.yaml`
1855
+ // when pnpm is the resolved manager. No-op for single-app templates.
1856
+ await configureWorkspacesAsync(projectRoot, packageManager);
1576
1857
  // Configure package manager, which is unrelated to installing or not
1577
1858
  await configureNodeDependenciesAsync(projectRoot, packageManager);
1578
1859
  // Install dependencies
1579
1860
  let podsInstalled = false;
1861
+ let nodeModulesInstalled = false;
1580
1862
  const needsPodsInstalled = await external_fs_default().existsSync(external_path_default().join(projectRoot, 'ios'));
1581
1863
  if (shouldInstall) {
1582
- await installNodeDependenciesAsync(projectRoot, packageManager);
1864
+ nodeModulesInstalled = await installNodeDependenciesAsync(projectRoot, packageManager);
1583
1865
  if (needsPodsInstalled) {
1584
1866
  podsInstalled = await installCocoaPodsAsync(projectRoot);
1585
1867
  }
@@ -1587,7 +1869,8 @@ async function setupDependenciesAsync(projectRoot, props) {
1587
1869
  const cdPath = getChangeDirectoryPath(projectRoot);
1588
1870
  console.log();
1589
1871
  logProjectReady({ cdPath, packageManager });
1590
- if (!shouldInstall) {
1872
+ // The install can also fail without stopping the command, so check the result and not the flag.
1873
+ if (!nodeModulesInstalled) {
1591
1874
  logNodeInstallWarning(cdPath, packageManager, needsPodsInstalled && !podsInstalled);
1592
1875
  }
1593
1876
  }
@@ -1625,7 +1908,11 @@ async function createTemplateAsync(inputPath, props) {
1625
1908
  event: telemetry.AnalyticsEventTypes.CREATE_EXPO_APP,
1626
1909
  properties: { phase: telemetry.AnalyticsEventPhases.ATTEMPT, template: resolvedTemplate },
1627
1910
  });
1628
- await withSectionLog(() => extractAndPrepareTemplateAppAsync(projectRoot, { npmPackage: resolvedTemplate }), {
1911
+ await withSectionLog(async () => {
1912
+ await extractAndPrepareTemplateAppAsync(projectRoot, {
1913
+ npmPackage: resolvedTemplate,
1914
+ });
1915
+ }, {
1629
1916
  pending: source_default().bold('Locating project files.'),
1630
1917
  success: 'Downloaded and extracted project files.',
1631
1918
  error: (error) => `Something went wrong in downloading and extracting the project files: ${error.message}`,
@@ -1699,7 +1986,9 @@ async function createExampleAsync(inputPath, props) {
1699
1986
  event: telemetry.AnalyticsEventTypes.CREATE_EXPO_APP,
1700
1987
  properties: { phase: telemetry.AnalyticsEventPhases.ATTEMPT, example: resolvedExample },
1701
1988
  });
1702
- await withSectionLog(() => downloadAndExtractExampleAsync(projectRoot, resolvedExample), {
1989
+ await withSectionLog(async () => {
1990
+ await downloadAndExtractExampleAsync(projectRoot, resolvedExample);
1991
+ }, {
1703
1992
  pending: source_default().bold('Locating example files...'),
1704
1993
  success: 'Downloaded and extracted example files.',
1705
1994
  error: (error) => `Something went wrong in downloading and extracting the example files: ${error.message}`,
@@ -1739,14 +2028,17 @@ async function configureNodeDependenciesAsync(projectRoot, packageManager) {
1739
2028
  log/* Log */.tG.exception(error);
1740
2029
  }
1741
2030
  }
2031
+ /** Install the node modules. Returns `false` when the package manager failed. */
1742
2032
  async function installNodeDependenciesAsync(projectRoot, packageManager) {
1743
2033
  try {
1744
2034
  await (0,resolvePackageManager/* installDependenciesAsync */.DC)(projectRoot, packageManager, { silent: false });
2035
+ return true;
1745
2036
  }
1746
2037
  catch (error) {
1747
2038
  createAsync_debug(`Error installing node modules: %O`, error);
1748
2039
  log/* Log */.tG.error(`Something went wrong installing JavaScript dependencies. Check your ${packageManager} logs. Continuing to create the app.`);
1749
2040
  log/* Log */.tG.exception(error);
2041
+ return false;
1750
2042
  }
1751
2043
  }
1752
2044
  async function installCocoaPodsAsync(projectRoot) {
@@ -1801,7 +2093,6 @@ __webpack_require__.d(__webpack_exports__, {
1801
2093
 
1802
2094
  // EXTERNAL MODULE: ../@expo/json-file/build/JsonFile.js
1803
2095
  var JsonFile = __webpack_require__(3064);
1804
- var JsonFile_default = /*#__PURE__*/__webpack_require__.n(JsonFile);
1805
2096
  // EXTERNAL MODULE: external "crypto"
1806
2097
  var external_crypto_ = __webpack_require__(6982);
1807
2098
  var external_crypto_default = /*#__PURE__*/__webpack_require__.n(external_crypto_);
@@ -1843,7 +2134,7 @@ const getStateJsonPath = () => external_path_default().join(dotExpoHomeDirectory
1843
2134
 
1844
2135
  function getSession() {
1845
2136
  try {
1846
- return JsonFile_default().read(getStateJsonPath())?.auth ?? null;
2137
+ return JsonFile["default"].read(getStateJsonPath())?.auth ?? null;
1847
2138
  }
1848
2139
  catch (error) {
1849
2140
  if (error.code === 'ENOENT') {
@@ -1855,6 +2146,8 @@ function getSession() {
1855
2146
 
1856
2147
  // EXTERNAL MODULE: ./src/utils/env.ts
1857
2148
  var utils_env = __webpack_require__(4342);
2149
+ // EXTERNAL MODULE: ./src/utils/update-check.ts
2150
+ var update_check = __webpack_require__(7517);
1858
2151
  ;// CONCATENATED MODULE: ./src/telemetry.ts
1859
2152
 
1860
2153
 
@@ -1863,11 +2156,19 @@ var utils_env = __webpack_require__(4342);
1863
2156
 
1864
2157
 
1865
2158
 
1866
- const packageJSON = __webpack_require__(8330);
2159
+
2160
+ const getPackageJson = () => {
2161
+ try {
2162
+ return __webpack_require__(8330);
2163
+ }
2164
+ catch {
2165
+ return null;
2166
+ }
2167
+ };
1867
2168
  const xdlUnifiedWriteKey = '1wabJGd5IiuF9Q8SGlcI90v8WTs';
1868
2169
  const analyticsEndpoint = 'https://cdp.expo.dev/v1/batch';
1869
2170
  const version = '1.0.0';
1870
- const library = packageJSON.name;
2171
+ const library = update_check.PACKAGE_NAME;
1871
2172
  const messageBatch = [];
1872
2173
  let analyticsIdentity = null;
1873
2174
  // jest does not clear global variables inbetween tests so we need this helper
@@ -1924,7 +2225,7 @@ function enqueue(type, message) {
1924
2225
  message.originalTimestamp = new Date();
1925
2226
  }
1926
2227
  if (!message.messageId) {
1927
- // We md5 the messaage to add more randomness. This is primarily meant
2228
+ // We md5 the message to add more randomness. This is primarily meant
1928
2229
  // for use in the browser where the uuid package falls back to Math.random()
1929
2230
  // which is not a great source of randomness.
1930
2231
  // Borrowed from analytics.js (https://github.com/segment-integrations/analytics.js-integration-segmentio/blob/a20d2a2d222aeb3ab2a8c7e72280f1df2618440e/lib/index.js#L255-L256).
@@ -1934,7 +2235,7 @@ function enqueue(type, message) {
1934
2235
  }
1935
2236
  messageBatch.push(message);
1936
2237
  }
1937
- // very barebones implemention...
2238
+ // very barebones implementation...
1938
2239
  // does not support multiple concurrent flushes or large numbers of messages
1939
2240
  async function flushAsync() {
1940
2241
  if (utils_env.env.EXPO_NO_TELEMETRY)
@@ -1960,7 +2261,7 @@ async function flushAsync() {
1960
2261
  await fetch(analyticsEndpoint, request);
1961
2262
  }
1962
2263
  catch {
1963
- // supress errors - likely due to network connectivity or endpoint health
2264
+ // suppress errors - likely due to network connectivity or endpoint health
1964
2265
  }
1965
2266
  // clear array so we don't resend events in subsequent flushes
1966
2267
  messageBatch.splice(0, messageBatch.length);
@@ -1977,7 +2278,7 @@ function getAnalyticsContext() {
1977
2278
  return {
1978
2279
  os: { name: platform, version: external_os_default().release() },
1979
2280
  device: { type: platform, model: platform },
1980
- app: { name: library, version: packageJSON.version },
2281
+ app: { name: library, version: getPackageJson()?.version ?? undefined },
1981
2282
  };
1982
2283
  }
1983
2284
  //#endregion
@@ -1988,7 +2289,7 @@ function uuidv4() {
1988
2289
  return external_crypto_default().randomUUID();
1989
2290
  }
1990
2291
  catch {
1991
- // supress errors due to node 13 or less not having randomUUID
2292
+ // suppress errors due to node 13 or less not having randomUUID
1992
2293
  return null;
1993
2294
  }
1994
2295
  }
@@ -2009,14 +2310,14 @@ async function getAnalyticsIdentityAsync() {
2009
2310
  if (!external_fs_default().existsSync(getStateJsonPath())) {
2010
2311
  external_fs_default().writeFileSync(getStateJsonPath(), JSON.stringify({}));
2011
2312
  }
2012
- const savedDeviceId = await JsonFile_default().getAsync(getStateJsonPath(), 'analyticsDeviceId', null);
2313
+ const savedDeviceId = await JsonFile["default"].getAsync(getStateJsonPath(), 'analyticsDeviceId', null);
2013
2314
  const deviceId = savedDeviceId ?? uuidv4();
2014
2315
  if (!deviceId) {
2015
2316
  // unable to generate an id or load one from disk
2016
2317
  return null;
2017
2318
  }
2018
2319
  if (!savedDeviceId) {
2019
- await JsonFile_default().setAsync(getStateJsonPath(), 'analyticsDeviceId', deviceId);
2320
+ await JsonFile["default"].setAsync(getStateJsonPath(), 'analyticsDeviceId', deviceId);
2020
2321
  }
2021
2322
  const userId = getSession()?.userId ?? null;
2022
2323
  return userId ? { anonymousId: deviceId, userId } : { anonymousId: deviceId };