@expo/cli 56.1.5 → 56.1.7

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 (94) hide show
  1. package/build/bin/cli +1 -1
  2. package/build/src/api/rest/client.js +27 -12
  3. package/build/src/api/rest/client.js.map +1 -1
  4. package/build/src/api/user/UserSettings.js +17 -4
  5. package/build/src/api/user/UserSettings.js.map +1 -1
  6. package/build/src/events/index.js +1 -1
  7. package/build/src/export/embed/exportEmbedAsync.js +3 -3
  8. package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
  9. package/build/src/export/embed/exportServer.js +1 -1
  10. package/build/src/export/embed/exportServer.js.map +1 -1
  11. package/build/src/export/exportApp.js +1 -1
  12. package/build/src/export/exportApp.js.map +1 -1
  13. package/build/src/export/publicFolder.js +19 -1
  14. package/build/src/export/publicFolder.js.map +1 -1
  15. package/build/src/install/utils/checkPackagesCompatibility.js +32 -16
  16. package/build/src/install/utils/checkPackagesCompatibility.js.map +1 -1
  17. package/build/src/login/index.js +25 -5
  18. package/build/src/login/index.js.map +1 -1
  19. package/build/src/prebuild/resolveTemplate.js +10 -5
  20. package/build/src/prebuild/resolveTemplate.js.map +1 -1
  21. package/build/src/run/android/resolveLaunchProps.js +4 -1
  22. package/build/src/run/android/resolveLaunchProps.js.map +1 -1
  23. package/build/src/start/doctor/dependencies/reactNativeTv.js +149 -0
  24. package/build/src/start/doctor/dependencies/reactNativeTv.js.map +1 -0
  25. package/build/src/start/doctor/dependencies/validateDependenciesVersions.js +28 -3
  26. package/build/src/start/doctor/dependencies/validateDependenciesVersions.js.map +1 -1
  27. package/build/src/start/platforms/AppIdResolver.js +4 -0
  28. package/build/src/start/platforms/AppIdResolver.js.map +1 -1
  29. package/build/src/start/platforms/android/adb.js +16 -15
  30. package/build/src/start/platforms/android/adb.js.map +1 -1
  31. package/build/src/start/server/DevToolsPlugin.js +26 -1
  32. package/build/src/start/server/DevToolsPlugin.js.map +1 -1
  33. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js +57 -22
  34. package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js.map +1 -1
  35. package/build/src/start/server/DevToolsPluginCliExtensionResults.js +29 -0
  36. package/build/src/start/server/DevToolsPluginCliExtensionResults.js.map +1 -1
  37. package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js +15 -5
  38. package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js.map +1 -1
  39. package/build/src/start/server/UrlCreator.js +14 -1
  40. package/build/src/start/server/UrlCreator.js.map +1 -1
  41. package/build/src/start/server/createMCPDevToolsExtensionSchema.js +13 -1
  42. package/build/src/start/server/createMCPDevToolsExtensionSchema.js.map +1 -1
  43. package/build/src/start/server/getStaticRenderFunctions.js +2 -1
  44. package/build/src/start/server/getStaticRenderFunctions.js.map +1 -1
  45. package/build/src/start/server/metro/MetroBundlerDevServer.js +44 -0
  46. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  47. package/build/src/start/server/metro/createServerComponentsMiddleware.js +13 -13
  48. package/build/src/start/server/metro/createServerComponentsMiddleware.js.map +1 -1
  49. package/build/src/start/server/metro/debugging/createDebugMiddleware.js +5 -4
  50. package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
  51. package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js +17 -1
  52. package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js.map +1 -1
  53. package/build/src/start/server/metro/dev-server/createMessageSocket.js +13 -2
  54. package/build/src/start/server/metro/dev-server/createMessageSocket.js.map +1 -1
  55. package/build/src/start/server/metro/dev-server/createMetroMiddleware.js +9 -2
  56. package/build/src/start/server/metro/dev-server/createMetroMiddleware.js.map +1 -1
  57. package/build/src/start/server/metro/instantiateMetro.js +8 -5
  58. package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
  59. package/build/src/start/server/metro/metroErrorInterface.js +5 -2
  60. package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
  61. package/build/src/start/server/metro/router.js +10 -1
  62. package/build/src/start/server/metro/router.js.map +1 -1
  63. package/build/src/start/server/metro/withMetroMultiPlatform.js +8 -0
  64. package/build/src/start/server/metro/withMetroMultiPlatform.js.map +1 -1
  65. package/build/src/start/server/middleware/InterstitialPageMiddleware.js +7 -4
  66. package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
  67. package/build/src/start/server/middleware/OpenMiddleware.js +150 -0
  68. package/build/src/start/server/middleware/OpenMiddleware.js.map +1 -0
  69. package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js +13 -4
  70. package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js.map +1 -1
  71. package/build/src/start/server/middleware/ServeStaticMiddleware.js +2 -9
  72. package/build/src/start/server/middleware/ServeStaticMiddleware.js.map +1 -1
  73. package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js +14 -24
  74. package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js.map +1 -1
  75. package/build/src/start/server/middleware/openHandlers.js +157 -0
  76. package/build/src/start/server/middleware/openHandlers.js.map +1 -0
  77. package/build/src/start/server/webTemplate.js +3 -5
  78. package/build/src/start/server/webTemplate.js.map +1 -1
  79. package/build/src/utils/codesigning.js +6 -0
  80. package/build/src/utils/codesigning.js.map +1 -1
  81. package/build/src/utils/env.js +29 -6
  82. package/build/src/utils/env.js.map +1 -1
  83. package/build/src/utils/net.js +20 -1
  84. package/build/src/utils/net.js.map +1 -1
  85. package/build/src/utils/open.js +2 -5
  86. package/build/src/utils/open.js.map +1 -1
  87. package/build/src/utils/tar.js +2 -2
  88. package/build/src/utils/tar.js.map +1 -1
  89. package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
  90. package/build/src/utils/telemetry/utils/context.js +1 -1
  91. package/build/src/utils/url.js +0 -12
  92. package/build/src/utils/url.js.map +1 -1
  93. package/package.json +22 -22
  94. package/static/loading-page/index.html +10 -2
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get REACT_NATIVE_TVOS_PACKAGE_NAME () {
13
+ return REACT_NATIVE_TVOS_PACKAGE_NAME;
14
+ },
15
+ get correctReactNativeTvVersion () {
16
+ return correctReactNativeTvVersion;
17
+ },
18
+ get isReactNativeTvProjectAsync () {
19
+ return isReactNativeTvProjectAsync;
20
+ },
21
+ get reactNativeTvVersionMatchesBundled () {
22
+ return reactNativeTvVersionMatchesBundled;
23
+ }
24
+ });
25
+ function _jsonfile() {
26
+ const data = /*#__PURE__*/ _interop_require_default(require("@expo/json-file"));
27
+ _jsonfile = function() {
28
+ return data;
29
+ };
30
+ return data;
31
+ }
32
+ function _requireutils() {
33
+ const data = require("@expo/require-utils");
34
+ _requireutils = function() {
35
+ return data;
36
+ };
37
+ return data;
38
+ }
39
+ function _semver() {
40
+ const data = /*#__PURE__*/ _interop_require_default(require("semver"));
41
+ _semver = function() {
42
+ return data;
43
+ };
44
+ return data;
45
+ }
46
+ const _env = require("../../../utils/env");
47
+ const _fetch = require("../../../utils/fetch");
48
+ function _interop_require_default(obj) {
49
+ return obj && obj.__esModule ? obj : {
50
+ default: obj
51
+ };
52
+ }
53
+ const REACT_NATIVE_TVOS_PACKAGE_NAME = 'react-native-tvos';
54
+ const debug = require('debug')('expo:doctor:reactNativeTv');
55
+ const NPM_DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${REACT_NATIVE_TVOS_PACKAGE_NAME}/dist-tags`;
56
+ const LATEST_FALLBACK_SPEC = `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@latest`;
57
+ async function isReactNativeTvProjectAsync(projectRoot) {
58
+ const reactNativePackageJsonPath = (0, _requireutils().resolveFrom)(projectRoot, 'react-native/package.json');
59
+ if (!reactNativePackageJsonPath) {
60
+ return false;
61
+ }
62
+ try {
63
+ const installedPkg = await _jsonfile().default.readAsync(reactNativePackageJsonPath);
64
+ return installedPkg.name === REACT_NATIVE_TVOS_PACKAGE_NAME;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+ async function correctReactNativeTvVersion(bundledReactNativeVersion) {
70
+ const derivedTag = deriveDistTag(bundledReactNativeVersion);
71
+ if (!derivedTag) {
72
+ debug(`Could not derive a react-native-tvos dist-tag from "${bundledReactNativeVersion}"; falling back to @latest`);
73
+ return LATEST_FALLBACK_SPEC;
74
+ }
75
+ // In offline mode skip the npm dist-tags lookup and trust the derived tag —
76
+ // any other CLI code path that needs a network request also bails on
77
+ // `EXPO_OFFLINE` (see `validateDependenciesVersionsAsync`).
78
+ if (_env.env.EXPO_OFFLINE) {
79
+ debug(`EXPO_OFFLINE is set; skipping npm dist-tags lookup for react-native-tvos`);
80
+ return `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@${derivedTag}`;
81
+ }
82
+ const publishedTags = await fetchReactNativeTvDistTagsAsync();
83
+ if (publishedTags.has(derivedTag)) {
84
+ return `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@${derivedTag}`;
85
+ }
86
+ debug(`Derived react-native-tvos dist-tag "${derivedTag}" is not published; falling back to @latest`);
87
+ return LATEST_FALLBACK_SPEC;
88
+ }
89
+ function reactNativeTvVersionMatchesBundled(actualVersion, bundledReactNativeVersion) {
90
+ const actual = _semver().default.coerce(actualVersion);
91
+ const bundled = _semver().default.coerce(bundledReactNativeVersion);
92
+ if (!actual || !bundled) {
93
+ return false;
94
+ }
95
+ return actual.major === bundled.major && actual.minor === bundled.minor;
96
+ }
97
+ function deriveDistTag(reactNativeVersion) {
98
+ if (!reactNativeVersion) {
99
+ return undefined;
100
+ }
101
+ let minVersion = null;
102
+ try {
103
+ minVersion = _semver().default.minVersion(reactNativeVersion);
104
+ } catch {
105
+ minVersion = null;
106
+ }
107
+ if (!minVersion) {
108
+ return undefined;
109
+ }
110
+ if (minVersion.prerelease.length > 0) {
111
+ return 'next';
112
+ }
113
+ return `${minVersion.major}.${minVersion.minor}-stable`;
114
+ }
115
+ async function fetchReactNativeTvDistTagsAsync() {
116
+ let response;
117
+ try {
118
+ response = await (0, _fetch.fetch)(NPM_DIST_TAGS_URL);
119
+ } catch (error) {
120
+ debug(`npm dist-tags lookup threw: ${(error == null ? void 0 : error.message) ?? error}`);
121
+ return new Set();
122
+ }
123
+ // Always read the body to release the underlying stream — even on a non-2xx —
124
+ // before deciding what to do with it. Parse JSON manually so a malformed
125
+ // body never escapes as a rejected promise.
126
+ let body = '';
127
+ try {
128
+ body = await response.text();
129
+ } catch (error) {
130
+ debug(`npm dist-tags body read threw: ${(error == null ? void 0 : error.message) ?? error}`);
131
+ return new Set();
132
+ }
133
+ if (!response.ok) {
134
+ debug(`npm dist-tags lookup failed with status ${response.status}`);
135
+ return new Set();
136
+ }
137
+ let json;
138
+ try {
139
+ json = JSON.parse(body);
140
+ } catch {
141
+ return new Set();
142
+ }
143
+ if (!json || typeof json !== 'object') {
144
+ return new Set();
145
+ }
146
+ return new Set(Object.keys(json));
147
+ }
148
+
149
+ //# sourceMappingURL=reactNativeTv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/start/doctor/dependencies/reactNativeTv.ts"],"sourcesContent":["import JsonFile from '@expo/json-file';\nimport { resolveFrom } from '@expo/require-utils';\nimport semver from 'semver';\n\nimport { env } from '../../../utils/env';\nimport { fetch } from '../../../utils/fetch';\n\nexport const REACT_NATIVE_TVOS_PACKAGE_NAME = 'react-native-tvos';\n\nconst debug = require('debug')('expo:doctor:reactNativeTv') as typeof console.log;\n\nconst NPM_DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${REACT_NATIVE_TVOS_PACKAGE_NAME}/dist-tags`;\n\nconst LATEST_FALLBACK_SPEC = `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@latest`;\n\n/**\n * Detects whether this is a TV project by inspecting the installed\n * `react-native` package's `name` field. When the project's `package.json`\n * aliases `react-native` to `react-native-tvos` (e.g.\n * `\"react-native\": \"npm:react-native-tvos@0.83.0-0\"`), the installed package\n * at `node_modules/react-native/package.json` is the upstream\n * `react-native-tvos` manifest, whose `name` is `\"react-native-tvos\"`.\n *\n * Reading the installed manifest is more reliable than reading the project's\n * `package.json` spec string, which can vary across package managers and\n * lockfile rewrites.\n */\nexport async function isReactNativeTvProjectAsync(projectRoot: string): Promise<boolean> {\n const reactNativePackageJsonPath = resolveFrom(projectRoot, 'react-native/package.json');\n if (!reactNativePackageJsonPath) {\n return false;\n }\n try {\n const installedPkg = await JsonFile.readAsync<{ name?: string }>(reactNativePackageJsonPath);\n return installedPkg.name === REACT_NATIVE_TVOS_PACKAGE_NAME;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns the install spec to use for `react-native-tvos` given the bundled\n * `react-native` version.\n *\n * `react-native-tvos` ships a dist-tag per minor line in the form\n * `<major>.<minor>-stable` and a rolling `next` tag for prereleases. We derive\n * the expected tag from the bundled `react-native` version (stable → `-stable`,\n * prerelease → `next`) and then verify the tag is actually published by\n * fetching the package's `dist-tags` from the npm registry. If the derived tag\n * isn't published (or the registry can't be reached), we fall back to\n * `npm:react-native-tvos@latest` so the install still resolves to something\n * usable.\n */\nexport async function correctReactNativeTvVersion(\n bundledReactNativeVersion: string\n): Promise<string> {\n const derivedTag = deriveDistTag(bundledReactNativeVersion);\n if (!derivedTag) {\n debug(\n `Could not derive a react-native-tvos dist-tag from \"${bundledReactNativeVersion}\"; falling back to @latest`\n );\n return LATEST_FALLBACK_SPEC;\n }\n // In offline mode skip the npm dist-tags lookup and trust the derived tag —\n // any other CLI code path that needs a network request also bails on\n // `EXPO_OFFLINE` (see `validateDependenciesVersionsAsync`).\n if (env.EXPO_OFFLINE) {\n debug(`EXPO_OFFLINE is set; skipping npm dist-tags lookup for react-native-tvos`);\n return `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@${derivedTag}`;\n }\n const publishedTags = await fetchReactNativeTvDistTagsAsync();\n if (publishedTags.has(derivedTag)) {\n return `npm:${REACT_NATIVE_TVOS_PACKAGE_NAME}@${derivedTag}`;\n }\n debug(\n `Derived react-native-tvos dist-tag \"${derivedTag}\" is not published; falling back to @latest`\n );\n return LATEST_FALLBACK_SPEC;\n}\n\n/**\n * Returns true when an installed `react-native` (aliased to `react-native-tvos`)\n * version's `major.minor` lines up with the expected bundled `react-native`\n * version. `react-native-tvos` releases follow the upstream minor versions, so a\n * matching `major.minor` means the TV variant is already current.\n */\nexport function reactNativeTvVersionMatchesBundled(\n actualVersion: string,\n bundledReactNativeVersion: string\n): boolean {\n const actual = semver.coerce(actualVersion);\n const bundled = semver.coerce(bundledReactNativeVersion);\n if (!actual || !bundled) {\n return false;\n }\n return actual.major === bundled.major && actual.minor === bundled.minor;\n}\n\nfunction deriveDistTag(reactNativeVersion: string): string | undefined {\n if (!reactNativeVersion) {\n return undefined;\n }\n let minVersion: semver.SemVer | null = null;\n try {\n minVersion = semver.minVersion(reactNativeVersion);\n } catch {\n minVersion = null;\n }\n if (!minVersion) {\n return undefined;\n }\n if (minVersion.prerelease.length > 0) {\n return 'next';\n }\n return `${minVersion.major}.${minVersion.minor}-stable`;\n}\n\nasync function fetchReactNativeTvDistTagsAsync(): Promise<Set<string>> {\n let response;\n try {\n response = await fetch(NPM_DIST_TAGS_URL);\n } catch (error: any) {\n debug(`npm dist-tags lookup threw: ${error?.message ?? error}`);\n return new Set();\n }\n // Always read the body to release the underlying stream — even on a non-2xx —\n // before deciding what to do with it. Parse JSON manually so a malformed\n // body never escapes as a rejected promise.\n let body = '';\n try {\n body = await response.text();\n } catch (error: any) {\n debug(`npm dist-tags body read threw: ${error?.message ?? error}`);\n return new Set();\n }\n if (!response.ok) {\n debug(`npm dist-tags lookup failed with status ${response.status}`);\n return new Set();\n }\n let json: unknown;\n try {\n json = JSON.parse(body);\n } catch {\n return new Set();\n }\n if (!json || typeof json !== 'object') {\n return new Set();\n }\n return new Set(Object.keys(json as Record<string, unknown>));\n}\n"],"names":["REACT_NATIVE_TVOS_PACKAGE_NAME","correctReactNativeTvVersion","isReactNativeTvProjectAsync","reactNativeTvVersionMatchesBundled","debug","require","NPM_DIST_TAGS_URL","LATEST_FALLBACK_SPEC","projectRoot","reactNativePackageJsonPath","resolveFrom","installedPkg","JsonFile","readAsync","name","bundledReactNativeVersion","derivedTag","deriveDistTag","env","EXPO_OFFLINE","publishedTags","fetchReactNativeTvDistTagsAsync","has","actualVersion","actual","semver","coerce","bundled","major","minor","reactNativeVersion","undefined","minVersion","prerelease","length","response","fetch","error","message","Set","body","text","ok","status","json","JSON","parse","Object","keys"],"mappings":"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"}
@@ -58,6 +58,7 @@ function _subset() {
58
58
  return data;
59
59
  }
60
60
  const _getVersionedPackages = require("./getVersionedPackages");
61
+ const _reactNativeTv = require("./reactNativeTv");
61
62
  const _resolvePackages = require("./resolvePackages");
62
63
  const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../../log"));
63
64
  const _env = require("../../../utils/env");
@@ -157,8 +158,12 @@ async function getVersionedDependenciesAsync(projectRoot, exp, pkg, packagesToCh
157
158
  // read package versions from the file system (node_modules)
158
159
  const packageVersions = await (0, _resolvePackages.resolveAllPackageVersionsAsync)(projectRoot, resolvedPackagesToCheck);
159
160
  debug(`Package versions: %O`, packageVersions);
161
+ // Detect TV projects via the installed `react-native` package's `name`, since
162
+ // `pkg.dependencies['react-native']` can vary across package managers.
163
+ const isReactNativeTvProject = await (0, _reactNativeTv.isReactNativeTvProjectAsync)(projectRoot);
164
+ debug(`react-native-tvos project: %O`, isReactNativeTvProject);
160
165
  // find incorrect dependencies by comparing the actual package versions with the bundled native module version ranges
161
- let incorrectDeps = findIncorrectDependencies(pkg, packageVersions, combinedKnownPackages);
166
+ let incorrectDeps = await findIncorrectDependencies(pkg, packageVersions, combinedKnownPackages, isReactNativeTvProject);
162
167
  debug(`Incorrect dependencies: %O`, incorrectDeps);
163
168
  if (pkg == null ? void 0 : (_pkg_expo = pkg.expo) == null ? void 0 : (_pkg_expo_install = _pkg_expo.install) == null ? void 0 : _pkg_expo_install.exclude) {
164
169
  const packagesToExclude = pkg.expo.install.exclude;
@@ -221,12 +226,32 @@ function getPackagesToCheck(bundledNativeModules, dependencies) {
221
226
  unknown
222
227
  };
223
228
  }
224
- function findIncorrectDependencies(pkg, packageVersions, bundledNativeModules) {
229
+ async function findIncorrectDependencies(pkg, packageVersions, bundledNativeModules, isReactNativeTvProject) {
230
+ // For TV projects, compare the installed `major.minor` against the bundled
231
+ // `react-native` `major.minor` — `react-native-tvos` follows the upstream
232
+ // minor lines via a `<major>.<minor>-stable` dist-tag, so a matching minor
233
+ // means the TV variant is up to date.
234
+ //
235
+ // Resolve the install spec for the TV variant once up front so the inner
236
+ // loop stays synchronous and we don't hit the npm registry per dependency.
237
+ const bundledReactNativeVersion = bundledNativeModules['react-native'];
238
+ const reactNativeTvExpectedVersionOrRange = isReactNativeTvProject && bundledReactNativeVersion ? await (0, _reactNativeTv.correctReactNativeTvVersion)(bundledReactNativeVersion) : undefined;
225
239
  const packages = Object.keys(packageVersions);
226
240
  const incorrectDeps = [];
227
241
  for (const packageName of packages){
228
- const expectedVersionOrRange = bundledNativeModules[packageName];
229
242
  const actualVersion = packageVersions[packageName];
243
+ if (isReactNativeTvProject && packageName === 'react-native') {
244
+ if (bundledReactNativeVersion && reactNativeTvExpectedVersionOrRange && !(0, _reactNativeTv.reactNativeTvVersionMatchesBundled)(actualVersion, bundledReactNativeVersion)) {
245
+ incorrectDeps.push({
246
+ packageName,
247
+ packageType: findDependencyType(pkg, packageName),
248
+ expectedVersionOrRange: reactNativeTvExpectedVersionOrRange,
249
+ actualVersion
250
+ });
251
+ }
252
+ continue;
253
+ }
254
+ const expectedVersionOrRange = bundledNativeModules[packageName];
230
255
  if (isDependencyVersionIncorrect(packageName, actualVersion, expectedVersionOrRange)) {
231
256
  incorrectDeps.push({
232
257
  packageName,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/start/doctor/dependencies/validateDependenciesVersions.ts"],"sourcesContent":["import type { ExpoConfig, PackageJSONConfig } from '@expo/config';\nimport assert from 'assert';\nimport chalk from 'chalk';\nimport npmPackageArg from 'npm-package-arg';\nimport semver from 'semver';\nimport semverRangeSubset from 'semver/ranges/subset';\n\nimport type { BundledNativeModules } from './bundledNativeModules';\nimport { getCombinedKnownVersionsAsync } from './getVersionedPackages';\nimport { resolveAllPackageVersionsAsync } from './resolvePackages';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\n\nconst debug = require('debug')('expo:doctor:dependencies:validate') as typeof console.log;\n\ntype IncorrectDependency = {\n packageName: string;\n packageType: 'dependencies' | 'devDependencies';\n expectedVersionOrRange: string;\n actualVersion: string;\n};\n\ntype DependenciesToCheck = { known: string[]; unknown: string[] };\n\n/**\n * Print a list of incorrect dependency versions.\n * This only checks dependencies when not running in offline mode.\n *\n * @param projectRoot Expo project root.\n * @param exp Expo project config.\n * @param pkg Project's `package.json`.\n * @param packagesToCheck A list of packages to check, if undefined or empty, all will be checked.\n * @returns `true` if there are no incorrect dependencies.\n */\nexport async function validateDependenciesVersionsAsync(\n projectRoot: string,\n exp: Pick<ExpoConfig, 'sdkVersion'>,\n pkg: PackageJSONConfig,\n packagesToCheck?: string[]\n): Promise<boolean | null> {\n if (env.EXPO_OFFLINE) {\n Log.warn('Skipping dependency validation in offline mode');\n return null;\n } else if (env.EXPO_NO_DEPENDENCY_VALIDATION) {\n debug('Dependency validation is disabled through EXPO_NO_DEPENDENCY_VALIDATION=1');\n return null;\n }\n\n const incorrectDeps = await getVersionedDependenciesAsync(projectRoot, exp, pkg, packagesToCheck);\n return logIncorrectDependencies(incorrectDeps);\n}\n\nfunction logInvalidDependency({\n packageName,\n expectedVersionOrRange,\n actualVersion,\n}: IncorrectDependency) {\n Log.warn(\n chalk` {bold ${packageName}}{cyan @}{red ${actualVersion}} - expected version: {green ${expectedVersionOrRange}}`\n );\n}\n\nexport function logIncorrectDependencies(incorrectDeps: IncorrectDependency[]) {\n if (!incorrectDeps.length) {\n return true;\n }\n\n Log.warn(\n chalk`The following packages should be updated for best compatibility with the installed {bold expo} version:`\n );\n incorrectDeps.forEach((dep) => logInvalidDependency(dep));\n\n Log.warn(\n 'Your project may not work correctly until you install the expected versions of the packages.'\n );\n\n return false;\n}\n\n/**\n * Return a list of versioned dependencies for the project SDK version.\n *\n * @param projectRoot Expo project root.\n * @param exp Expo project config.\n * @param pkg Project's `package.json`.\n * @param packagesToCheck A list of packages to check, if undefined or empty, all will be checked.\n * @returns A list of incorrect dependencies.\n */\nexport async function getVersionedDependenciesAsync(\n projectRoot: string,\n exp: Pick<ExpoConfig, 'sdkVersion'>,\n pkg: PackageJSONConfig,\n packagesToCheck?: string[]\n): Promise<IncorrectDependency[]> {\n // This should never happen under normal circumstances since\n // the CLI is versioned in the `expo` package.\n assert(exp.sdkVersion, 'SDK Version is missing');\n\n // Get from both endpoints and combine the known package versions.\n const combinedKnownPackages = await getCombinedKnownVersionsAsync({\n projectRoot,\n sdkVersion: exp.sdkVersion,\n });\n // debug(`Known dependencies: %O`, combinedKnownPackages);\n\n const resolvedDependencies = packagesToCheck?.length\n ? // Diff the provided packages to ensure we only check against installed packages.\n getFilteredObject(packagesToCheck, { ...pkg.dependencies, ...pkg.devDependencies })\n : // If no packages are provided, check against the `package.json` `dependencies` + `devDependencies` object.\n { ...pkg.dependencies, ...pkg.devDependencies };\n debug(`Checking dependencies for ${exp.sdkVersion}: %O`, resolvedDependencies);\n\n // intersection of packages from package.json and bundled native modules\n const { known: resolvedPackagesToCheck, unknown } = getPackagesToCheck(\n combinedKnownPackages,\n resolvedDependencies\n );\n debug(`Comparing known versions: %O`, resolvedPackagesToCheck);\n debug(`Skipping packages that cannot be versioned automatically: %O`, unknown);\n // read package versions from the file system (node_modules)\n const packageVersions = await resolveAllPackageVersionsAsync(\n projectRoot,\n resolvedPackagesToCheck\n );\n debug(`Package versions: %O`, packageVersions);\n // find incorrect dependencies by comparing the actual package versions with the bundled native module version ranges\n let incorrectDeps = findIncorrectDependencies(pkg, packageVersions, combinedKnownPackages);\n debug(`Incorrect dependencies: %O`, incorrectDeps);\n\n if (pkg?.expo?.install?.exclude) {\n const packagesToExclude = pkg.expo.install.exclude;\n\n // Parse the exclude list to ensure we can factor in any specified version ranges\n const parsedPackagesToExclude = packagesToExclude.reduce(\n (acc: Record<string, npmPackageArg.Result>, packageName: string) => {\n const npaResult = npmPackageArg(packageName);\n if (typeof npaResult.name === 'string') {\n acc[npaResult.name] = npaResult;\n } else {\n acc[packageName] = npaResult;\n }\n return acc;\n },\n {}\n );\n\n const incorrectAndExcludedDeps = incorrectDeps\n .filter((dep) => {\n if (parsedPackagesToExclude[dep.packageName]) {\n const { name, raw, rawSpec, type } = parsedPackagesToExclude[dep.packageName];\n const suggestedRange = combinedKnownPackages[name]!;\n\n // If only the package name itself is specified, then we keep it in the exclude list\n if (name === raw) {\n return true;\n } else if (type === 'version') {\n return suggestedRange === rawSpec;\n } else if (type === 'range') {\n // Fall through exclusions if the suggested range is invalid\n if (!semver.validRange(suggestedRange)) {\n debug(\n `Invalid semver range in combined known packages for package ${name} in expo.install.exclude: %O`,\n suggestedRange\n );\n return false;\n }\n\n return semverRangeSubset(suggestedRange, rawSpec);\n } else {\n debug(\n `Unsupported npm package argument type for package ${name} in expo.install.exclude: %O`,\n type\n );\n }\n }\n\n return false;\n })\n .map((dep) => dep.packageName);\n\n debug(\n `Incorrect dependency warnings filtered out by expo.install.exclude: %O`,\n incorrectAndExcludedDeps\n );\n incorrectDeps = incorrectDeps.filter(\n (dep) => !incorrectAndExcludedDeps.includes(dep.packageName)\n );\n }\n\n return incorrectDeps;\n}\n\nfunction getFilteredObject(keys: string[], object: Record<string, string>) {\n return keys.reduce<Record<string, string>>((acc, key) => {\n acc[key] = object[key]!;\n return acc;\n }, {});\n}\n\nfunction getPackagesToCheck(\n bundledNativeModules: BundledNativeModules,\n dependencies?: Record<string, string> | null\n): DependenciesToCheck {\n const dependencyNames = Object.keys(dependencies ?? {});\n const known: string[] = [];\n const unknown: string[] = [];\n for (const dependencyName of dependencyNames) {\n if (dependencyName in bundledNativeModules) {\n known.push(dependencyName);\n } else {\n unknown.push(dependencyName);\n }\n }\n return { known, unknown };\n}\n\nfunction findIncorrectDependencies(\n pkg: PackageJSONConfig,\n packageVersions: Record<string, string>,\n bundledNativeModules: BundledNativeModules\n): IncorrectDependency[] {\n const packages = Object.keys(packageVersions);\n const incorrectDeps: IncorrectDependency[] = [];\n for (const packageName of packages) {\n const expectedVersionOrRange = bundledNativeModules[packageName]!;\n const actualVersion = packageVersions[packageName]!;\n if (isDependencyVersionIncorrect(packageName, actualVersion, expectedVersionOrRange)) {\n incorrectDeps.push({\n packageName,\n packageType: findDependencyType(pkg, packageName),\n expectedVersionOrRange,\n actualVersion,\n });\n }\n }\n return incorrectDeps;\n}\n\nexport function isDependencyVersionIncorrect(\n packageName: string,\n actualVersion: string,\n expectedVersionOrRange?: string\n) {\n if (!expectedVersionOrRange) {\n return false;\n }\n\n // we never want to go backwards with the expo patch version\n if (packageName === 'expo') {\n return semver.ltr(actualVersion, expectedVersionOrRange);\n }\n\n // For all other packages, check if the actual version satisfies the expected range\n const satisfies = semver.satisfies(actualVersion, expectedVersionOrRange, {\n includePrerelease: true,\n });\n\n return !satisfies;\n}\n\nfunction findDependencyType(\n pkg: PackageJSONConfig,\n packageName: string\n): IncorrectDependency['packageType'] {\n if (pkg.devDependencies && packageName in pkg.devDependencies) {\n return 'devDependencies';\n }\n\n return 'dependencies';\n}\n"],"names":["getVersionedDependenciesAsync","isDependencyVersionIncorrect","logIncorrectDependencies","validateDependenciesVersionsAsync","debug","require","projectRoot","exp","pkg","packagesToCheck","env","EXPO_OFFLINE","Log","warn","EXPO_NO_DEPENDENCY_VALIDATION","incorrectDeps","logInvalidDependency","packageName","expectedVersionOrRange","actualVersion","chalk","length","forEach","dep","assert","sdkVersion","combinedKnownPackages","getCombinedKnownVersionsAsync","resolvedDependencies","getFilteredObject","dependencies","devDependencies","known","resolvedPackagesToCheck","unknown","getPackagesToCheck","packageVersions","resolveAllPackageVersionsAsync","findIncorrectDependencies","expo","install","exclude","packagesToExclude","parsedPackagesToExclude","reduce","acc","npaResult","npmPackageArg","name","incorrectAndExcludedDeps","filter","raw","rawSpec","type","suggestedRange","semver","validRange","semverRangeSubset","map","includes","keys","object","key","bundledNativeModules","dependencyNames","Object","dependencyName","push","packages","packageType","findDependencyType","ltr","satisfies","includePrerelease"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../src/start/doctor/dependencies/validateDependenciesVersions.ts"],"sourcesContent":["import type { ExpoConfig, PackageJSONConfig } from '@expo/config';\nimport assert from 'assert';\nimport chalk from 'chalk';\nimport npmPackageArg from 'npm-package-arg';\nimport semver from 'semver';\nimport semverRangeSubset from 'semver/ranges/subset';\n\nimport type { BundledNativeModules } from './bundledNativeModules';\nimport { getCombinedKnownVersionsAsync } from './getVersionedPackages';\nimport {\n correctReactNativeTvVersion,\n isReactNativeTvProjectAsync,\n reactNativeTvVersionMatchesBundled,\n} from './reactNativeTv';\nimport { resolveAllPackageVersionsAsync } from './resolvePackages';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\n\nconst debug = require('debug')('expo:doctor:dependencies:validate') as typeof console.log;\n\nexport type IncorrectDependency = {\n packageName: string;\n packageType: 'dependencies' | 'devDependencies';\n expectedVersionOrRange: string;\n actualVersion: string;\n};\n\ntype DependenciesToCheck = { known: string[]; unknown: string[] };\n\n/**\n * Print a list of incorrect dependency versions.\n * This only checks dependencies when not running in offline mode.\n *\n * @param projectRoot Expo project root.\n * @param exp Expo project config.\n * @param pkg Project's `package.json`.\n * @param packagesToCheck A list of packages to check, if undefined or empty, all will be checked.\n * @returns `true` if there are no incorrect dependencies.\n */\nexport async function validateDependenciesVersionsAsync(\n projectRoot: string,\n exp: Pick<ExpoConfig, 'sdkVersion'>,\n pkg: PackageJSONConfig,\n packagesToCheck?: string[]\n): Promise<boolean | null> {\n if (env.EXPO_OFFLINE) {\n Log.warn('Skipping dependency validation in offline mode');\n return null;\n } else if (env.EXPO_NO_DEPENDENCY_VALIDATION) {\n debug('Dependency validation is disabled through EXPO_NO_DEPENDENCY_VALIDATION=1');\n return null;\n }\n\n const incorrectDeps = await getVersionedDependenciesAsync(projectRoot, exp, pkg, packagesToCheck);\n return logIncorrectDependencies(incorrectDeps);\n}\n\nfunction logInvalidDependency({\n packageName,\n expectedVersionOrRange,\n actualVersion,\n}: IncorrectDependency) {\n Log.warn(\n chalk` {bold ${packageName}}{cyan @}{red ${actualVersion}} - expected version: {green ${expectedVersionOrRange}}`\n );\n}\n\nexport function logIncorrectDependencies(incorrectDeps: IncorrectDependency[]) {\n if (!incorrectDeps.length) {\n return true;\n }\n\n Log.warn(\n chalk`The following packages should be updated for best compatibility with the installed {bold expo} version:`\n );\n incorrectDeps.forEach((dep) => logInvalidDependency(dep));\n\n Log.warn(\n 'Your project may not work correctly until you install the expected versions of the packages.'\n );\n\n return false;\n}\n\n/**\n * Return a list of versioned dependencies for the project SDK version.\n *\n * @param projectRoot Expo project root.\n * @param exp Expo project config.\n * @param pkg Project's `package.json`.\n * @param packagesToCheck A list of packages to check, if undefined or empty, all will be checked.\n * @returns A list of incorrect dependencies.\n */\nexport async function getVersionedDependenciesAsync(\n projectRoot: string,\n exp: Pick<ExpoConfig, 'sdkVersion'>,\n pkg: PackageJSONConfig,\n packagesToCheck?: string[]\n): Promise<IncorrectDependency[]> {\n // This should never happen under normal circumstances since\n // the CLI is versioned in the `expo` package.\n assert(exp.sdkVersion, 'SDK Version is missing');\n\n // Get from both endpoints and combine the known package versions.\n const combinedKnownPackages = await getCombinedKnownVersionsAsync({\n projectRoot,\n sdkVersion: exp.sdkVersion,\n });\n // debug(`Known dependencies: %O`, combinedKnownPackages);\n\n const resolvedDependencies = packagesToCheck?.length\n ? // Diff the provided packages to ensure we only check against installed packages.\n getFilteredObject(packagesToCheck, { ...pkg.dependencies, ...pkg.devDependencies })\n : // If no packages are provided, check against the `package.json` `dependencies` + `devDependencies` object.\n { ...pkg.dependencies, ...pkg.devDependencies };\n debug(`Checking dependencies for ${exp.sdkVersion}: %O`, resolvedDependencies);\n\n // intersection of packages from package.json and bundled native modules\n const { known: resolvedPackagesToCheck, unknown } = getPackagesToCheck(\n combinedKnownPackages,\n resolvedDependencies\n );\n debug(`Comparing known versions: %O`, resolvedPackagesToCheck);\n debug(`Skipping packages that cannot be versioned automatically: %O`, unknown);\n // read package versions from the file system (node_modules)\n const packageVersions = await resolveAllPackageVersionsAsync(\n projectRoot,\n resolvedPackagesToCheck\n );\n debug(`Package versions: %O`, packageVersions);\n // Detect TV projects via the installed `react-native` package's `name`, since\n // `pkg.dependencies['react-native']` can vary across package managers.\n const isReactNativeTvProject = await isReactNativeTvProjectAsync(projectRoot);\n debug(`react-native-tvos project: %O`, isReactNativeTvProject);\n // find incorrect dependencies by comparing the actual package versions with the bundled native module version ranges\n let incorrectDeps = await findIncorrectDependencies(\n pkg,\n packageVersions,\n combinedKnownPackages,\n isReactNativeTvProject\n );\n debug(`Incorrect dependencies: %O`, incorrectDeps);\n\n if (pkg?.expo?.install?.exclude) {\n const packagesToExclude = pkg.expo.install.exclude;\n\n // Parse the exclude list to ensure we can factor in any specified version ranges\n const parsedPackagesToExclude = packagesToExclude.reduce(\n (acc: Record<string, npmPackageArg.Result>, packageName: string) => {\n const npaResult = npmPackageArg(packageName);\n if (typeof npaResult.name === 'string') {\n acc[npaResult.name] = npaResult;\n } else {\n acc[packageName] = npaResult;\n }\n return acc;\n },\n {}\n );\n\n const incorrectAndExcludedDeps = incorrectDeps\n .filter((dep) => {\n if (parsedPackagesToExclude[dep.packageName]) {\n const { name, raw, rawSpec, type } = parsedPackagesToExclude[dep.packageName];\n const suggestedRange = combinedKnownPackages[name]!;\n\n // If only the package name itself is specified, then we keep it in the exclude list\n if (name === raw) {\n return true;\n } else if (type === 'version') {\n return suggestedRange === rawSpec;\n } else if (type === 'range') {\n // Fall through exclusions if the suggested range is invalid\n if (!semver.validRange(suggestedRange)) {\n debug(\n `Invalid semver range in combined known packages for package ${name} in expo.install.exclude: %O`,\n suggestedRange\n );\n return false;\n }\n\n return semverRangeSubset(suggestedRange, rawSpec);\n } else {\n debug(\n `Unsupported npm package argument type for package ${name} in expo.install.exclude: %O`,\n type\n );\n }\n }\n\n return false;\n })\n .map((dep) => dep.packageName);\n\n debug(\n `Incorrect dependency warnings filtered out by expo.install.exclude: %O`,\n incorrectAndExcludedDeps\n );\n incorrectDeps = incorrectDeps.filter(\n (dep) => !incorrectAndExcludedDeps.includes(dep.packageName)\n );\n }\n\n return incorrectDeps;\n}\n\nfunction getFilteredObject(keys: string[], object: Record<string, string>) {\n return keys.reduce<Record<string, string>>((acc, key) => {\n acc[key] = object[key]!;\n return acc;\n }, {});\n}\n\nfunction getPackagesToCheck(\n bundledNativeModules: BundledNativeModules,\n dependencies?: Record<string, string> | null\n): DependenciesToCheck {\n const dependencyNames = Object.keys(dependencies ?? {});\n const known: string[] = [];\n const unknown: string[] = [];\n for (const dependencyName of dependencyNames) {\n if (dependencyName in bundledNativeModules) {\n known.push(dependencyName);\n } else {\n unknown.push(dependencyName);\n }\n }\n return { known, unknown };\n}\n\nasync function findIncorrectDependencies(\n pkg: PackageJSONConfig,\n packageVersions: Record<string, string>,\n bundledNativeModules: BundledNativeModules,\n isReactNativeTvProject: boolean\n): Promise<IncorrectDependency[]> {\n // For TV projects, compare the installed `major.minor` against the bundled\n // `react-native` `major.minor` — `react-native-tvos` follows the upstream\n // minor lines via a `<major>.<minor>-stable` dist-tag, so a matching minor\n // means the TV variant is up to date.\n //\n // Resolve the install spec for the TV variant once up front so the inner\n // loop stays synchronous and we don't hit the npm registry per dependency.\n const bundledReactNativeVersion = bundledNativeModules['react-native'];\n const reactNativeTvExpectedVersionOrRange =\n isReactNativeTvProject && bundledReactNativeVersion\n ? await correctReactNativeTvVersion(bundledReactNativeVersion)\n : undefined;\n\n const packages = Object.keys(packageVersions);\n const incorrectDeps: IncorrectDependency[] = [];\n for (const packageName of packages) {\n const actualVersion = packageVersions[packageName]!;\n\n if (isReactNativeTvProject && packageName === 'react-native') {\n if (\n bundledReactNativeVersion &&\n reactNativeTvExpectedVersionOrRange &&\n !reactNativeTvVersionMatchesBundled(actualVersion, bundledReactNativeVersion)\n ) {\n incorrectDeps.push({\n packageName,\n packageType: findDependencyType(pkg, packageName),\n expectedVersionOrRange: reactNativeTvExpectedVersionOrRange,\n actualVersion,\n });\n }\n continue;\n }\n\n const expectedVersionOrRange = bundledNativeModules[packageName]!;\n if (isDependencyVersionIncorrect(packageName, actualVersion, expectedVersionOrRange)) {\n incorrectDeps.push({\n packageName,\n packageType: findDependencyType(pkg, packageName),\n expectedVersionOrRange,\n actualVersion,\n });\n }\n }\n return incorrectDeps;\n}\n\nexport function isDependencyVersionIncorrect(\n packageName: string,\n actualVersion: string,\n expectedVersionOrRange?: string\n) {\n if (!expectedVersionOrRange) {\n return false;\n }\n\n // we never want to go backwards with the expo patch version\n if (packageName === 'expo') {\n return semver.ltr(actualVersion, expectedVersionOrRange);\n }\n\n // For all other packages, check if the actual version satisfies the expected range\n const satisfies = semver.satisfies(actualVersion, expectedVersionOrRange, {\n includePrerelease: true,\n });\n\n return !satisfies;\n}\n\nfunction findDependencyType(\n pkg: PackageJSONConfig,\n packageName: string\n): IncorrectDependency['packageType'] {\n if (pkg.devDependencies && packageName in pkg.devDependencies) {\n return 'devDependencies';\n }\n\n return 'dependencies';\n}\n"],"names":["getVersionedDependenciesAsync","isDependencyVersionIncorrect","logIncorrectDependencies","validateDependenciesVersionsAsync","debug","require","projectRoot","exp","pkg","packagesToCheck","env","EXPO_OFFLINE","Log","warn","EXPO_NO_DEPENDENCY_VALIDATION","incorrectDeps","logInvalidDependency","packageName","expectedVersionOrRange","actualVersion","chalk","length","forEach","dep","assert","sdkVersion","combinedKnownPackages","getCombinedKnownVersionsAsync","resolvedDependencies","getFilteredObject","dependencies","devDependencies","known","resolvedPackagesToCheck","unknown","getPackagesToCheck","packageVersions","resolveAllPackageVersionsAsync","isReactNativeTvProject","isReactNativeTvProjectAsync","findIncorrectDependencies","expo","install","exclude","packagesToExclude","parsedPackagesToExclude","reduce","acc","npaResult","npmPackageArg","name","incorrectAndExcludedDeps","filter","raw","rawSpec","type","suggestedRange","semver","validRange","semverRangeSubset","map","includes","keys","object","key","bundledNativeModules","dependencyNames","Object","dependencyName","push","bundledReactNativeVersion","reactNativeTvExpectedVersionOrRange","correctReactNativeTvVersion","undefined","packages","reactNativeTvVersionMatchesBundled","packageType","findDependencyType","ltr","satisfies","includePrerelease"],"mappings":"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"}
@@ -15,6 +15,7 @@ function _config() {
15
15
  };
16
16
  return data;
17
17
  }
18
+ const _env = require("../../utils/env");
18
19
  const _errors = require("../../utils/errors");
19
20
  const _obj = require("../../utils/obj");
20
21
  class AppIdResolver {
@@ -24,6 +25,9 @@ class AppIdResolver {
24
25
  this.configProperty = configProperty;
25
26
  }
26
27
  /** Resolve the application ID for the project. */ async getAppIdAsync() {
28
+ if (_env.env.EXPO_RUN_PREFER_APP_CONFIG_ID) {
29
+ return this.getAppIdFromConfigAsync();
30
+ }
27
31
  if (await this.hasNativeProjectAsync()) {
28
32
  return this.getAppIdFromNativeAsync();
29
33
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/platforms/AppIdResolver.ts"],"sourcesContent":["import { getConfig, getProjectConfigDescriptionWithPaths } from '@expo/config';\n\nimport { CommandError, UnimplementedError } from '../../utils/errors';\nimport { get } from '../../utils/obj';\n\n/** Resolves a native app identifier (bundle identifier, package name) from the project files. */\nexport class AppIdResolver {\n constructor(\n protected projectRoot: string,\n /** Platform to use. */\n protected platform: string,\n /** Nested key in the Expo config like `android.package` or `ios.bundleIdentifier`. */\n protected configProperty: string\n ) {}\n\n /** Resolve the application ID for the project. */\n async getAppIdAsync(): Promise<string> {\n if (await this.hasNativeProjectAsync()) {\n return this.getAppIdFromNativeAsync();\n }\n return this.getAppIdFromConfigAsync();\n }\n\n /** Returns `true` if the project has native project code. */\n async hasNativeProjectAsync(): Promise<boolean> {\n throw new UnimplementedError();\n }\n\n /** Return the app ID from the Expo config or assert. */\n async getAppIdFromConfigAsync(): Promise<string> {\n const config = getConfig(this.projectRoot);\n\n const appId = get(config.exp, this.configProperty);\n if (!appId) {\n throw new CommandError(\n 'NO_APP_ID',\n `Required property '${\n this.configProperty\n }' is not found in the project ${getProjectConfigDescriptionWithPaths(\n this.projectRoot,\n config\n )}. This is required to open the app.`\n );\n }\n return appId;\n }\n\n /** Return the app ID from the native project files or null if the app ID cannot be found. */\n async resolveAppIdFromNativeAsync(): Promise<string | null> {\n throw new UnimplementedError();\n }\n\n /** Return the app ID from the native project files or assert. */\n async getAppIdFromNativeAsync(): Promise<string> {\n const appId = await this.resolveAppIdFromNativeAsync();\n if (!appId) {\n throw new CommandError(\n 'NO_APP_ID',\n `Failed to locate the ${this.platform} application identifier in the \"${this.platform}/\" folder. This is required to open the app.`\n );\n }\n return appId;\n }\n}\n"],"names":["AppIdResolver","projectRoot","platform","configProperty","getAppIdAsync","hasNativeProjectAsync","getAppIdFromNativeAsync","getAppIdFromConfigAsync","UnimplementedError","config","getConfig","appId","get","exp","CommandError","getProjectConfigDescriptionWithPaths","resolveAppIdFromNativeAsync"],"mappings":";;;;+BAMaA;;;eAAAA;;;;yBANmD;;;;;;wBAEf;qBAC7B;AAGb,MAAMA;IACX,YACE,AAAUC,WAAmB,EAC7B,qBAAqB,GACrB,AAAUC,QAAgB,EAC1B,oFAAoF,GACpF,AAAUC,cAAsB,CAChC;aALUF,cAAAA;aAEAC,WAAAA;aAEAC,iBAAAA;IACT;IAEH,gDAAgD,GAChD,MAAMC,gBAAiC;QACrC,IAAI,MAAM,IAAI,CAACC,qBAAqB,IAAI;YACtC,OAAO,IAAI,CAACC,uBAAuB;QACrC;QACA,OAAO,IAAI,CAACC,uBAAuB;IACrC;IAEA,2DAA2D,GAC3D,MAAMF,wBAA0C;QAC9C,MAAM,IAAIG,0BAAkB;IAC9B;IAEA,sDAAsD,GACtD,MAAMD,0BAA2C;QAC/C,MAAME,SAASC,IAAAA,mBAAS,EAAC,IAAI,CAACT,WAAW;QAEzC,MAAMU,QAAQC,IAAAA,QAAG,EAACH,OAAOI,GAAG,EAAE,IAAI,CAACV,cAAc;QACjD,IAAI,CAACQ,OAAO;YACV,MAAM,IAAIG,oBAAY,CACpB,aACA,CAAC,mBAAmB,EAClB,IAAI,CAACX,cAAc,CACpB,8BAA8B,EAAEY,IAAAA,8CAAoC,EACnE,IAAI,CAACd,WAAW,EAChBQ,QACA,mCAAmC,CAAC;QAE1C;QACA,OAAOE;IACT;IAEA,2FAA2F,GAC3F,MAAMK,8BAAsD;QAC1D,MAAM,IAAIR,0BAAkB;IAC9B;IAEA,+DAA+D,GAC/D,MAAMF,0BAA2C;QAC/C,MAAMK,QAAQ,MAAM,IAAI,CAACK,2BAA2B;QACpD,IAAI,CAACL,OAAO;YACV,MAAM,IAAIG,oBAAY,CACpB,aACA,CAAC,qBAAqB,EAAE,IAAI,CAACZ,QAAQ,CAAC,gCAAgC,EAAE,IAAI,CAACA,QAAQ,CAAC,4CAA4C,CAAC;QAEvI;QACA,OAAOS;IACT;AACF"}
1
+ {"version":3,"sources":["../../../../src/start/platforms/AppIdResolver.ts"],"sourcesContent":["import { getConfig, getProjectConfigDescriptionWithPaths } from '@expo/config';\n\nimport { env } from '../../utils/env';\nimport { CommandError, UnimplementedError } from '../../utils/errors';\nimport { get } from '../../utils/obj';\n\n/** Resolves a native app identifier (bundle identifier, package name) from the project files. */\nexport class AppIdResolver {\n constructor(\n protected projectRoot: string,\n /** Platform to use. */\n protected platform: string,\n /** Nested key in the Expo config like `android.package` or `ios.bundleIdentifier`. */\n protected configProperty: string\n ) {}\n\n /** Resolve the application ID for the project. */\n async getAppIdAsync(): Promise<string> {\n if (env.EXPO_RUN_PREFER_APP_CONFIG_ID) {\n return this.getAppIdFromConfigAsync();\n }\n if (await this.hasNativeProjectAsync()) {\n return this.getAppIdFromNativeAsync();\n }\n return this.getAppIdFromConfigAsync();\n }\n\n /** Returns `true` if the project has native project code. */\n async hasNativeProjectAsync(): Promise<boolean> {\n throw new UnimplementedError();\n }\n\n /** Return the app ID from the Expo config or assert. */\n async getAppIdFromConfigAsync(): Promise<string> {\n const config = getConfig(this.projectRoot);\n\n const appId = get(config.exp, this.configProperty);\n if (!appId) {\n throw new CommandError(\n 'NO_APP_ID',\n `Required property '${\n this.configProperty\n }' is not found in the project ${getProjectConfigDescriptionWithPaths(\n this.projectRoot,\n config\n )}. This is required to open the app.`\n );\n }\n return appId;\n }\n\n /** Return the app ID from the native project files or null if the app ID cannot be found. */\n async resolveAppIdFromNativeAsync(): Promise<string | null> {\n throw new UnimplementedError();\n }\n\n /** Return the app ID from the native project files or assert. */\n async getAppIdFromNativeAsync(): Promise<string> {\n const appId = await this.resolveAppIdFromNativeAsync();\n if (!appId) {\n throw new CommandError(\n 'NO_APP_ID',\n `Failed to locate the ${this.platform} application identifier in the \"${this.platform}/\" folder. This is required to open the app.`\n );\n }\n return appId;\n }\n}\n"],"names":["AppIdResolver","projectRoot","platform","configProperty","getAppIdAsync","env","EXPO_RUN_PREFER_APP_CONFIG_ID","getAppIdFromConfigAsync","hasNativeProjectAsync","getAppIdFromNativeAsync","UnimplementedError","config","getConfig","appId","get","exp","CommandError","getProjectConfigDescriptionWithPaths","resolveAppIdFromNativeAsync"],"mappings":";;;;+BAOaA;;;eAAAA;;;;yBAPmD;;;;;;qBAE5C;wBAC6B;qBAC7B;AAGb,MAAMA;IACX,YACE,AAAUC,WAAmB,EAC7B,qBAAqB,GACrB,AAAUC,QAAgB,EAC1B,oFAAoF,GACpF,AAAUC,cAAsB,CAChC;aALUF,cAAAA;aAEAC,WAAAA;aAEAC,iBAAAA;IACT;IAEH,gDAAgD,GAChD,MAAMC,gBAAiC;QACrC,IAAIC,QAAG,CAACC,6BAA6B,EAAE;YACrC,OAAO,IAAI,CAACC,uBAAuB;QACrC;QACA,IAAI,MAAM,IAAI,CAACC,qBAAqB,IAAI;YACtC,OAAO,IAAI,CAACC,uBAAuB;QACrC;QACA,OAAO,IAAI,CAACF,uBAAuB;IACrC;IAEA,2DAA2D,GAC3D,MAAMC,wBAA0C;QAC9C,MAAM,IAAIE,0BAAkB;IAC9B;IAEA,sDAAsD,GACtD,MAAMH,0BAA2C;QAC/C,MAAMI,SAASC,IAAAA,mBAAS,EAAC,IAAI,CAACX,WAAW;QAEzC,MAAMY,QAAQC,IAAAA,QAAG,EAACH,OAAOI,GAAG,EAAE,IAAI,CAACZ,cAAc;QACjD,IAAI,CAACU,OAAO;YACV,MAAM,IAAIG,oBAAY,CACpB,aACA,CAAC,mBAAmB,EAClB,IAAI,CAACb,cAAc,CACpB,8BAA8B,EAAEc,IAAAA,8CAAoC,EACnE,IAAI,CAAChB,WAAW,EAChBU,QACA,mCAAmC,CAAC;QAE1C;QACA,OAAOE;IACT;IAEA,2FAA2F,GAC3F,MAAMK,8BAAsD;QAC1D,MAAM,IAAIR,0BAAkB;IAC9B;IAEA,+DAA+D,GAC/D,MAAMD,0BAA2C;QAC/C,MAAMI,QAAQ,MAAM,IAAI,CAACK,2BAA2B;QACpD,IAAI,CAACL,OAAO;YACV,MAAM,IAAIG,oBAAY,CACpB,aACA,CAAC,qBAAqB,EAAE,IAAI,CAACd,QAAQ,CAAC,gCAAgC,EAAE,IAAI,CAACA,QAAQ,CAAC,4CAA4C,CAAC;QAEvI;QACA,OAAOW;IACT;AACF"}
@@ -15,6 +15,9 @@ _export(exports, {
15
15
  get adbArgs () {
16
16
  return adbArgs;
17
17
  },
18
+ get adbShellArgs () {
19
+ return adbShellArgs;
20
+ },
18
21
  get getAdbNameForDeviceIdAsync () {
19
22
  return getAdbNameForDeviceIdAsync;
20
23
  },
@@ -159,7 +162,7 @@ function logUnauthorized(device) {
159
162
  _log.warn(`\nThis computer is not authorized for developing on ${_chalk().default.bold(device.name)}. ${_chalk().default.dim((0, _link.learnMore)('https://expo.fyi/authorize-android-device'))}`);
160
163
  }
161
164
  async function isPackageInstalledAsync(device, androidPackage) {
162
- const packages = await getServer().runAsync(adbArgs(device.pid, 'shell', 'pm', 'list', 'packages', '--user', _env.env.EXPO_ADB_USER, androidPackage));
165
+ const packages = await getServer().runAsync(adbShellArgs(device.pid, 'pm', 'list', 'packages', '--user', _env.env.EXPO_ADB_USER, androidPackage));
163
166
  const lines = packages.split(/\r?\n/);
164
167
  for(let i = 0; i < lines.length; i++){
165
168
  const line = lines[i].trim();
@@ -170,8 +173,7 @@ async function isPackageInstalledAsync(device, androidPackage) {
170
173
  return false;
171
174
  }
172
175
  async function launchActivityAsync(device, { launchActivity, url }) {
173
- const args = [
174
- 'shell',
176
+ const command = [
175
177
  'am',
176
178
  'start',
177
179
  // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack.
@@ -182,13 +184,12 @@ async function launchActivityAsync(device, { launchActivity, url }) {
182
184
  launchActivity
183
185
  ];
184
186
  if (url) {
185
- args.push('-d', url);
187
+ command.push('-d', url);
186
188
  }
187
- return openAsync(adbArgs(device.pid, ...args));
189
+ return openAsync(adbShellArgs(device.pid, ...command));
188
190
  }
189
191
  async function openUrlAsync(device, { url }) {
190
- return openAsync(adbArgs(device.pid, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', // ADB requires ampersands to be escaped.
191
- url.replace(/&/g, String.raw`\&`)));
192
+ return openAsync(adbShellArgs(device.pid, 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', url));
192
193
  }
193
194
  /** Runs a generic command watches for common errors in order to throw with an expected code. */ async function openAsync(args) {
194
195
  const results = await getServer().runAsync(args);
@@ -201,7 +202,7 @@ async function uninstallAsync(device, { appId }) {
201
202
  return await getServer().runAsync(adbArgs(device.pid, 'uninstall', '--user', _env.env.EXPO_ADB_USER, appId));
202
203
  }
203
204
  async function getPackageInfoAsync(device, { appId }) {
204
- return await getServer().runAsync(adbArgs(device.pid, 'shell', 'dumpsys', 'package', appId));
205
+ return await getServer().runAsync(adbShellArgs(device.pid, 'dumpsys', 'package', appId));
205
206
  }
206
207
  async function installAsync(device, { filePath }) {
207
208
  // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error.
@@ -214,6 +215,12 @@ function adbArgs(pid, ...options) {
214
215
  }
215
216
  return args.concat(options);
216
217
  }
218
+ function adbShellArgs(pid, ...command) {
219
+ return adbArgs(pid, 'shell', ...command.map(shellQuote));
220
+ }
221
+ function shellQuote(value) {
222
+ return `'${value.replace(/'/g, `'\\''`)}'`;
223
+ }
217
224
  async function getAttachedDevicesAsync() {
218
225
  const output = await getServer().runAsync([
219
226
  'devices',
@@ -326,13 +333,7 @@ async function getDeviceABIsAsync(device) {
326
333
  ];
327
334
  }
328
335
  async function getPropertyDataForDeviceAsync(device, prop) {
329
- // @ts-ignore
330
- const propCommand = adbArgs(...[
331
- device.pid,
332
- 'shell',
333
- 'getprop',
334
- prop
335
- ].filter(Boolean));
336
+ const propCommand = prop ? adbShellArgs(device.pid, 'getprop', prop) : adbShellArgs(device.pid, 'getprop');
336
337
  try {
337
338
  // Prevent reading as UTF8.
338
339
  const results = await getServer().getFileOutputAsync(propCommand);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/start/platforms/android/adb.ts"],"sourcesContent":["import chalk from 'chalk';\nimport os from 'os';\n\nimport { ADBServer } from './ADBServer';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\nimport { CommandError } from '../../../utils/errors';\nimport { learnMore } from '../../../utils/link';\n\nconst debug = require('debug')('expo:start:platforms:android:adb') as typeof console.log;\n\nexport enum DeviceABI {\n // The arch specific android target platforms are soft-deprecated.\n // Instead of using TargetPlatform as a combination arch + platform\n // the code will be updated to carry arch information in [DarwinArch]\n // and [AndroidArch].\n arm = 'arm',\n arm64 = 'arm64',\n x64 = 'x64',\n x86 = 'x86',\n x8664 = 'x86_64',\n arm64v8a = 'arm64-v8a',\n armeabiV7a = 'armeabi-v7a',\n armeabi = 'armeabi',\n universal = 'universal',\n}\n\n/** Represents a connected Android device. */\nexport type Device = {\n /** Process ID. */\n pid?: string;\n /** Name of the device, also used as the ID for opening devices. */\n name: string;\n /** Is emulator or connected device. */\n type: 'emulator' | 'device';\n /** Is the device booted (emulator). */\n isBooted: boolean;\n /** Is device authorized for developing. https://expo.fyi/authorize-android-device */\n isAuthorized: boolean;\n /** The connection type to ADB, only available when `type: device` */\n connectionType?: 'USB' | 'Network';\n};\n\ntype DeviceContext = Pick<Device, 'pid'>;\n\ntype DeviceProperties = Record<string, string>;\n\nconst CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent';\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_CPU_NAME = 'ro.product.cpu.abi';\n\nconst PROP_CPU_ABI_LIST_NAME = 'ro.product.cpu.abilist';\n\n// Can sometimes be null\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_BOOT_ANIMATION_STATE = 'init.svc.bootanim';\n\nlet _server: ADBServer | null;\n\n/** Return the lazily loaded ADB server instance. */\nexport function getServer() {\n _server ??= new ADBServer();\n return _server;\n}\n\n/** Logs an FYI message about authorizing your device. */\nexport function logUnauthorized(device: Device) {\n Log.warn(\n `\\nThis computer is not authorized for developing on ${chalk.bold(device.name)}. ${chalk.dim(\n learnMore('https://expo.fyi/authorize-android-device')\n )}`\n );\n}\n\n/** Returns true if the provided package name is installed on the provided Android device. */\nexport async function isPackageInstalledAsync(\n device: DeviceContext,\n androidPackage: string\n): Promise<boolean> {\n const packages = await getServer().runAsync(\n adbArgs(\n device.pid,\n 'shell',\n 'pm',\n 'list',\n 'packages',\n '--user',\n env.EXPO_ADB_USER,\n androidPackage\n )\n );\n\n const lines = packages.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim();\n if (line === `package:${androidPackage}`) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.launchActivity Activity to launch `[application identifier]/.[main activity name]`, ex: `com.bacon.app/.MainActivity`\n * @param props.url Optional (dev client) URL to launch\n */\nexport async function launchActivityAsync(\n device: DeviceContext,\n {\n launchActivity,\n url,\n }: {\n launchActivity: string;\n url?: string;\n }\n) {\n const args: string[] = [\n 'shell',\n 'am',\n 'start',\n // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack.\n '-f',\n '0x20000000',\n // Activity to open first: com.bacon.app/.MainActivity\n '-n',\n launchActivity,\n ];\n\n if (url) {\n args.push('-d', url);\n }\n\n return openAsync(adbArgs(device.pid, ...args));\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.url URL to launch.\n */\nexport async function openUrlAsync(\n device: DeviceContext,\n {\n url,\n }: {\n url: string;\n }\n) {\n return openAsync(\n adbArgs(\n device.pid,\n 'shell',\n 'am',\n 'start',\n '-a',\n 'android.intent.action.VIEW',\n '-d',\n // ADB requires ampersands to be escaped.\n url.replace(/&/g, String.raw`\\&`)\n )\n );\n}\n\n/** Runs a generic command watches for common errors in order to throw with an expected code. */\nasync function openAsync(args: string[]): Promise<string> {\n const results = await getServer().runAsync(args);\n if (\n results.includes(CANT_START_ACTIVITY_ERROR) ||\n results.match(/Error: Activity class .* does not exist\\./g)\n ) {\n throw new CommandError('APP_NOT_INSTALLED', results.substring(results.indexOf('Error: ')));\n }\n return results;\n}\n\n/** Uninstall an app given its Android package name. */\nexport async function uninstallAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(\n adbArgs(device.pid, 'uninstall', '--user', env.EXPO_ADB_USER, appId)\n );\n}\n\n/** Get package info from an app based on its Android package name. */\nexport async function getPackageInfoAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(adbArgs(device.pid, 'shell', 'dumpsys', 'package', appId));\n}\n\n/** Install an app on a connected device. */\nexport async function installAsync(device: DeviceContext, { filePath }: { filePath: string }) {\n // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error.\n return await getServer().runAsync(\n adbArgs(device.pid, 'install', '-r', '-d', '--user', env.EXPO_ADB_USER, filePath)\n );\n}\n\n/** Format ADB args with process ID. */\nexport function adbArgs(pid: Device['pid'], ...options: string[]): string[] {\n const args = [];\n if (pid) {\n args.push('-s', pid);\n }\n\n return args.concat(options);\n}\n\n// TODO: This is very expensive for some operations.\nexport async function getAttachedDevicesAsync(): Promise<Device[]> {\n const output = await getServer().runAsync(['devices', '-l']);\n\n const splitItems = output\n .trim()\n .replace(/\\n$/, '')\n .split(os.EOL)\n // Filter ADB trace logs from the output, e.g.\n // adb D 03-06 15:25:53 63677 4018815 adb_client.cpp:393] adb_query: host:devices-l\n // 03-04 12:29:44.557 16415 16415 D adb : commandline.cpp:1646 Using server socket: tcp:172.27.192.1:5037\n // 03-04 12:29:44.557 16415 16415 D adb : adb_client.cpp:160 _adb_connect: host:version\n .filter((line) => !line.match(/\\.cpp:[0-9]+/));\n\n // First line is `\"List of devices attached\"`, remove it\n // @ts-ignore: todo\n const attachedDevices: {\n props: string[];\n type: Device['type'];\n isAuthorized: Device['isAuthorized'];\n isBooted: Device['isBooted'];\n connectionType?: Device['connectionType'];\n }[] = splitItems\n .slice(1, splitItems.length)\n .map((line) => {\n // unauthorized: ['FA8251A00719', 'unauthorized', 'usb:338690048X', 'transport_id:5']\n // authorized: ['FA8251A00719', 'device', 'usb:336592896X', 'product:walleye', 'model:Pixel_2', 'device:walleye', 'transport_id:4']\n // emulator: ['emulator-5554', 'offline', 'transport_id:1']\n const props = line.split(' ').filter(Boolean);\n const type = line.includes('emulator') ? 'emulator' : 'device';\n\n let connectionType;\n if (type === 'device' && line.includes('usb:')) {\n connectionType = 'USB';\n } else if (type === 'device' && line.includes('_adb-tls-connect.')) {\n connectionType = 'Network';\n }\n\n const isBooted = type === 'emulator' || props[1] !== 'offline';\n const isAuthorized =\n connectionType === 'Network'\n ? line.includes('model:') // Network connected devices show `model:<name>` when authorized\n : props[1] !== 'unauthorized';\n\n return { props, type, isAuthorized, isBooted, connectionType };\n })\n .filter(({ props: [pid] }) => !!pid);\n\n const devicePromises = attachedDevices.map<Promise<Device>>(async (props) => {\n const {\n type,\n props: [pid, ...deviceInfo],\n isAuthorized,\n isBooted,\n } = props;\n\n let name: string | null = null;\n\n if (type === 'device') {\n if (isAuthorized) {\n // Possibly formatted like `model:Pixel_2`\n // Transform to `Pixel_2`\n const modelItem = deviceInfo.find((info) => info.includes('model:'));\n if (modelItem) {\n name = modelItem.replace('model:', '');\n }\n }\n // unauthorized devices don't have a name available to read\n if (!name) {\n // Device FA8251A00719\n name = `Device ${pid}`;\n }\n } else {\n // Given an emulator pid, get the emulator name which can be used to start the emulator later.\n name = (await getAdbNameForDeviceIdAsync({ pid })) ?? '';\n }\n\n return props.connectionType\n ? { pid, name, type, isAuthorized, isBooted, connectionType: props.connectionType }\n : { pid, name, type, isAuthorized, isBooted };\n });\n\n return Promise.all(devicePromises);\n}\n\n/**\n * Return the Emulator name for an emulator ID, this can be used to determine if an emulator is booted.\n *\n * @param device.pid a value like `emulator-5554` from `abd devices`\n */\nexport async function getAdbNameForDeviceIdAsync(device: DeviceContext): Promise<string | null> {\n const results = await getServer().runAsync(adbArgs(device.pid, 'emu', 'avd', 'name'));\n\n if (results.match(/could not connect to TCP port .*: Connection refused/)) {\n // Can also occur when the emulator does not exist.\n throw new CommandError('EMULATOR_NOT_FOUND', results);\n }\n\n return sanitizeAdbDeviceName(results) ?? null;\n}\n\nexport async function isDeviceBootedAsync({\n name,\n}: { name?: string } = {}): Promise<Device | null> {\n const devices = await getAttachedDevicesAsync();\n\n if (!name) {\n return devices[0] ?? null;\n }\n\n return devices.find((device) => device.name === name) ?? null;\n}\n\n/**\n * Returns true when a device's splash screen animation has stopped.\n * This can be used to detect when a device is fully booted and ready to use.\n *\n * @param pid\n */\nexport async function isBootAnimationCompleteAsync(pid?: string): Promise<boolean> {\n try {\n const props = await getPropertyDataForDeviceAsync({ pid }, PROP_BOOT_ANIMATION_STATE);\n return !!props[PROP_BOOT_ANIMATION_STATE]?.match(/stopped/);\n } catch {\n return false;\n }\n}\n\n/** Get a list of ABIs for the provided device. */\nexport async function getDeviceABIsAsync(\n device: Pick<Device, 'name' | 'pid'>\n): Promise<DeviceABI[]> {\n const cpuAbiList = (await getPropertyDataForDeviceAsync(device, PROP_CPU_ABI_LIST_NAME))[\n PROP_CPU_ABI_LIST_NAME\n ];\n\n if (cpuAbiList) {\n return cpuAbiList.trim().split(',') as DeviceABI[];\n }\n\n const abi = (await getPropertyDataForDeviceAsync(device, PROP_CPU_NAME))[\n PROP_CPU_NAME\n ] as DeviceABI;\n return [abi];\n}\n\nexport async function getPropertyDataForDeviceAsync(\n device: DeviceContext,\n prop?: string\n): Promise<DeviceProperties> {\n // @ts-ignore\n const propCommand = adbArgs(...[device.pid, 'shell', 'getprop', prop].filter(Boolean));\n try {\n // Prevent reading as UTF8.\n const results = await getServer().getFileOutputAsync(propCommand);\n // Like:\n // [wifi.direct.interface]: [p2p-dev-wlan0]\n // [wifi.interface]: [wlan0]\n\n if (prop) {\n debug(`Property data: (device pid: ${device.pid}, prop: ${prop}, data: ${results})`);\n return {\n [prop]: results,\n };\n }\n const props = parseAdbDeviceProperties(results);\n\n debug(`Parsed data:`, props);\n\n return props;\n } catch (error: any) {\n // TODO: Ensure error has message and not stderr\n throw new CommandError(`Failed to get properties for device (${device.pid}): ${error.message}`);\n }\n}\n\nfunction parseAdbDeviceProperties(devicePropertiesString: string) {\n const properties: DeviceProperties = {};\n const propertyExp = /\\[(.*?)\\]: \\[(.*?)\\]/gm;\n for (const match of devicePropertiesString.matchAll(propertyExp)) {\n properties[match[1]!] = match[2]!;\n }\n return properties;\n}\n\n/**\n * Sanitize the ADB device name to only get the actual device name.\n * On Windows, we need to do \\r, \\n, and \\r\\n filtering to get the name.\n */\nexport function sanitizeAdbDeviceName(deviceName: string) {\n return deviceName\n .trim()\n .split(/[\\r\\n]+/)\n .shift();\n}\n"],"names":["DeviceABI","adbArgs","getAdbNameForDeviceIdAsync","getAttachedDevicesAsync","getDeviceABIsAsync","getPackageInfoAsync","getPropertyDataForDeviceAsync","getServer","installAsync","isBootAnimationCompleteAsync","isDeviceBootedAsync","isPackageInstalledAsync","launchActivityAsync","logUnauthorized","openUrlAsync","sanitizeAdbDeviceName","uninstallAsync","debug","require","CANT_START_ACTIVITY_ERROR","PROP_CPU_NAME","PROP_CPU_ABI_LIST_NAME","PROP_BOOT_ANIMATION_STATE","_server","ADBServer","device","Log","warn","chalk","bold","name","dim","learnMore","androidPackage","packages","runAsync","pid","env","EXPO_ADB_USER","lines","split","i","length","line","trim","launchActivity","url","args","push","openAsync","replace","String","raw","results","includes","match","CommandError","substring","indexOf","appId","filePath","options","concat","output","splitItems","os","EOL","filter","attachedDevices","slice","map","props","Boolean","type","connectionType","isBooted","isAuthorized","devicePromises","deviceInfo","modelItem","find","info","Promise","all","devices","cpuAbiList","abi","prop","propCommand","getFileOutputAsync","parseAdbDeviceProperties","error","message","devicePropertiesString","properties","propertyExp","matchAll","deviceName","shift"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../src/start/platforms/android/adb.ts"],"sourcesContent":["import chalk from 'chalk';\nimport os from 'os';\n\nimport { ADBServer } from './ADBServer';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\nimport { CommandError } from '../../../utils/errors';\nimport { learnMore } from '../../../utils/link';\n\nconst debug = require('debug')('expo:start:platforms:android:adb') as typeof console.log;\n\nexport enum DeviceABI {\n // The arch specific android target platforms are soft-deprecated.\n // Instead of using TargetPlatform as a combination arch + platform\n // the code will be updated to carry arch information in [DarwinArch]\n // and [AndroidArch].\n arm = 'arm',\n arm64 = 'arm64',\n x64 = 'x64',\n x86 = 'x86',\n x8664 = 'x86_64',\n arm64v8a = 'arm64-v8a',\n armeabiV7a = 'armeabi-v7a',\n armeabi = 'armeabi',\n universal = 'universal',\n}\n\n/** Represents a connected Android device. */\nexport type Device = {\n /** Process ID. */\n pid?: string;\n /** Name of the device, also used as the ID for opening devices. */\n name: string;\n /** Is emulator or connected device. */\n type: 'emulator' | 'device';\n /** Is the device booted (emulator). */\n isBooted: boolean;\n /** Is device authorized for developing. https://expo.fyi/authorize-android-device */\n isAuthorized: boolean;\n /** The connection type to ADB, only available when `type: device` */\n connectionType?: 'USB' | 'Network';\n};\n\ntype DeviceContext = Pick<Device, 'pid'>;\n\ntype DeviceProperties = Record<string, string>;\n\nconst CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent';\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_CPU_NAME = 'ro.product.cpu.abi';\n\nconst PROP_CPU_ABI_LIST_NAME = 'ro.product.cpu.abilist';\n\n// Can sometimes be null\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_BOOT_ANIMATION_STATE = 'init.svc.bootanim';\n\nlet _server: ADBServer | null;\n\n/** Return the lazily loaded ADB server instance. */\nexport function getServer() {\n _server ??= new ADBServer();\n return _server;\n}\n\n/** Logs an FYI message about authorizing your device. */\nexport function logUnauthorized(device: Device) {\n Log.warn(\n `\\nThis computer is not authorized for developing on ${chalk.bold(device.name)}. ${chalk.dim(\n learnMore('https://expo.fyi/authorize-android-device')\n )}`\n );\n}\n\n/** Returns true if the provided package name is installed on the provided Android device. */\nexport async function isPackageInstalledAsync(\n device: DeviceContext,\n androidPackage: string\n): Promise<boolean> {\n const packages = await getServer().runAsync(\n adbShellArgs(device.pid, 'pm', 'list', 'packages', '--user', env.EXPO_ADB_USER, androidPackage)\n );\n\n const lines = packages.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim();\n if (line === `package:${androidPackage}`) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.launchActivity Activity to launch `[application identifier]/.[main activity name]`, ex: `com.bacon.app/.MainActivity`\n * @param props.url Optional (dev client) URL to launch\n */\nexport async function launchActivityAsync(\n device: DeviceContext,\n {\n launchActivity,\n url,\n }: {\n launchActivity: string;\n url?: string;\n }\n) {\n const command: string[] = [\n 'am',\n 'start',\n // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack.\n '-f',\n '0x20000000',\n // Activity to open first: com.bacon.app/.MainActivity\n '-n',\n launchActivity,\n ];\n\n if (url) {\n command.push('-d', url);\n }\n\n return openAsync(adbShellArgs(device.pid, ...command));\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.url URL to launch.\n */\nexport async function openUrlAsync(\n device: DeviceContext,\n {\n url,\n }: {\n url: string;\n }\n) {\n return openAsync(\n adbShellArgs(device.pid, 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', url)\n );\n}\n\n/** Runs a generic command watches for common errors in order to throw with an expected code. */\nasync function openAsync(args: string[]): Promise<string> {\n const results = await getServer().runAsync(args);\n if (\n results.includes(CANT_START_ACTIVITY_ERROR) ||\n results.match(/Error: Activity class .* does not exist\\./g)\n ) {\n throw new CommandError('APP_NOT_INSTALLED', results.substring(results.indexOf('Error: ')));\n }\n return results;\n}\n\n/** Uninstall an app given its Android package name. */\nexport async function uninstallAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(\n adbArgs(device.pid, 'uninstall', '--user', env.EXPO_ADB_USER, appId)\n );\n}\n\n/** Get package info from an app based on its Android package name. */\nexport async function getPackageInfoAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(adbShellArgs(device.pid, 'dumpsys', 'package', appId));\n}\n\n/** Install an app on a connected device. */\nexport async function installAsync(device: DeviceContext, { filePath }: { filePath: string }) {\n // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error.\n return await getServer().runAsync(\n adbArgs(device.pid, 'install', '-r', '-d', '--user', env.EXPO_ADB_USER, filePath)\n );\n}\n\n/** Format ADB args with process ID. */\nexport function adbArgs(pid: Device['pid'], ...options: string[]): string[] {\n const args = [];\n if (pid) {\n args.push('-s', pid);\n }\n\n return args.concat(options);\n}\n\n/**\n * `adb shell` concatenates trailing args and runs the result through `sh -c` on\n * the device, so unquoted metacharacters in tainted tokens execute on-device.\n */\nexport function adbShellArgs(pid: Device['pid'], ...command: string[]): string[] {\n return adbArgs(pid, 'shell', ...command.map(shellQuote));\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\n// TODO: This is very expensive for some operations.\nexport async function getAttachedDevicesAsync(): Promise<Device[]> {\n const output = await getServer().runAsync(['devices', '-l']);\n\n const splitItems = output\n .trim()\n .replace(/\\n$/, '')\n .split(os.EOL)\n // Filter ADB trace logs from the output, e.g.\n // adb D 03-06 15:25:53 63677 4018815 adb_client.cpp:393] adb_query: host:devices-l\n // 03-04 12:29:44.557 16415 16415 D adb : commandline.cpp:1646 Using server socket: tcp:172.27.192.1:5037\n // 03-04 12:29:44.557 16415 16415 D adb : adb_client.cpp:160 _adb_connect: host:version\n .filter((line) => !line.match(/\\.cpp:[0-9]+/));\n\n // First line is `\"List of devices attached\"`, remove it\n // @ts-ignore: todo\n const attachedDevices: {\n props: string[];\n type: Device['type'];\n isAuthorized: Device['isAuthorized'];\n isBooted: Device['isBooted'];\n connectionType?: Device['connectionType'];\n }[] = splitItems\n .slice(1, splitItems.length)\n .map((line) => {\n // unauthorized: ['FA8251A00719', 'unauthorized', 'usb:338690048X', 'transport_id:5']\n // authorized: ['FA8251A00719', 'device', 'usb:336592896X', 'product:walleye', 'model:Pixel_2', 'device:walleye', 'transport_id:4']\n // emulator: ['emulator-5554', 'offline', 'transport_id:1']\n const props = line.split(' ').filter(Boolean);\n const type = line.includes('emulator') ? 'emulator' : 'device';\n\n let connectionType;\n if (type === 'device' && line.includes('usb:')) {\n connectionType = 'USB';\n } else if (type === 'device' && line.includes('_adb-tls-connect.')) {\n connectionType = 'Network';\n }\n\n const isBooted = type === 'emulator' || props[1] !== 'offline';\n const isAuthorized =\n connectionType === 'Network'\n ? line.includes('model:') // Network connected devices show `model:<name>` when authorized\n : props[1] !== 'unauthorized';\n\n return { props, type, isAuthorized, isBooted, connectionType };\n })\n .filter(({ props: [pid] }) => !!pid);\n\n const devicePromises = attachedDevices.map<Promise<Device>>(async (props) => {\n const {\n type,\n props: [pid, ...deviceInfo],\n isAuthorized,\n isBooted,\n } = props;\n\n let name: string | null = null;\n\n if (type === 'device') {\n if (isAuthorized) {\n // Possibly formatted like `model:Pixel_2`\n // Transform to `Pixel_2`\n const modelItem = deviceInfo.find((info) => info.includes('model:'));\n if (modelItem) {\n name = modelItem.replace('model:', '');\n }\n }\n // unauthorized devices don't have a name available to read\n if (!name) {\n // Device FA8251A00719\n name = `Device ${pid}`;\n }\n } else {\n // Given an emulator pid, get the emulator name which can be used to start the emulator later.\n name = (await getAdbNameForDeviceIdAsync({ pid })) ?? '';\n }\n\n return props.connectionType\n ? { pid, name, type, isAuthorized, isBooted, connectionType: props.connectionType }\n : { pid, name, type, isAuthorized, isBooted };\n });\n\n return Promise.all(devicePromises);\n}\n\n/**\n * Return the Emulator name for an emulator ID, this can be used to determine if an emulator is booted.\n *\n * @param device.pid a value like `emulator-5554` from `abd devices`\n */\nexport async function getAdbNameForDeviceIdAsync(device: DeviceContext): Promise<string | null> {\n const results = await getServer().runAsync(adbArgs(device.pid, 'emu', 'avd', 'name'));\n\n if (results.match(/could not connect to TCP port .*: Connection refused/)) {\n // Can also occur when the emulator does not exist.\n throw new CommandError('EMULATOR_NOT_FOUND', results);\n }\n\n return sanitizeAdbDeviceName(results) ?? null;\n}\n\nexport async function isDeviceBootedAsync({\n name,\n}: { name?: string } = {}): Promise<Device | null> {\n const devices = await getAttachedDevicesAsync();\n\n if (!name) {\n return devices[0] ?? null;\n }\n\n return devices.find((device) => device.name === name) ?? null;\n}\n\n/**\n * Returns true when a device's splash screen animation has stopped.\n * This can be used to detect when a device is fully booted and ready to use.\n *\n * @param pid\n */\nexport async function isBootAnimationCompleteAsync(pid?: string): Promise<boolean> {\n try {\n const props = await getPropertyDataForDeviceAsync({ pid }, PROP_BOOT_ANIMATION_STATE);\n return !!props[PROP_BOOT_ANIMATION_STATE]?.match(/stopped/);\n } catch {\n return false;\n }\n}\n\n/** Get a list of ABIs for the provided device. */\nexport async function getDeviceABIsAsync(\n device: Pick<Device, 'name' | 'pid'>\n): Promise<DeviceABI[]> {\n const cpuAbiList = (await getPropertyDataForDeviceAsync(device, PROP_CPU_ABI_LIST_NAME))[\n PROP_CPU_ABI_LIST_NAME\n ];\n\n if (cpuAbiList) {\n return cpuAbiList.trim().split(',') as DeviceABI[];\n }\n\n const abi = (await getPropertyDataForDeviceAsync(device, PROP_CPU_NAME))[\n PROP_CPU_NAME\n ] as DeviceABI;\n return [abi];\n}\n\nexport async function getPropertyDataForDeviceAsync(\n device: DeviceContext,\n prop?: string\n): Promise<DeviceProperties> {\n const propCommand = prop\n ? adbShellArgs(device.pid, 'getprop', prop)\n : adbShellArgs(device.pid, 'getprop');\n try {\n // Prevent reading as UTF8.\n const results = await getServer().getFileOutputAsync(propCommand);\n // Like:\n // [wifi.direct.interface]: [p2p-dev-wlan0]\n // [wifi.interface]: [wlan0]\n\n if (prop) {\n debug(`Property data: (device pid: ${device.pid}, prop: ${prop}, data: ${results})`);\n return {\n [prop]: results,\n };\n }\n const props = parseAdbDeviceProperties(results);\n\n debug(`Parsed data:`, props);\n\n return props;\n } catch (error: any) {\n // TODO: Ensure error has message and not stderr\n throw new CommandError(`Failed to get properties for device (${device.pid}): ${error.message}`);\n }\n}\n\nfunction parseAdbDeviceProperties(devicePropertiesString: string) {\n const properties: DeviceProperties = {};\n const propertyExp = /\\[(.*?)\\]: \\[(.*?)\\]/gm;\n for (const match of devicePropertiesString.matchAll(propertyExp)) {\n properties[match[1]!] = match[2]!;\n }\n return properties;\n}\n\n/**\n * Sanitize the ADB device name to only get the actual device name.\n * On Windows, we need to do \\r, \\n, and \\r\\n filtering to get the name.\n */\nexport function sanitizeAdbDeviceName(deviceName: string) {\n return deviceName\n .trim()\n .split(/[\\r\\n]+/)\n .shift();\n}\n"],"names":["DeviceABI","adbArgs","adbShellArgs","getAdbNameForDeviceIdAsync","getAttachedDevicesAsync","getDeviceABIsAsync","getPackageInfoAsync","getPropertyDataForDeviceAsync","getServer","installAsync","isBootAnimationCompleteAsync","isDeviceBootedAsync","isPackageInstalledAsync","launchActivityAsync","logUnauthorized","openUrlAsync","sanitizeAdbDeviceName","uninstallAsync","debug","require","CANT_START_ACTIVITY_ERROR","PROP_CPU_NAME","PROP_CPU_ABI_LIST_NAME","PROP_BOOT_ANIMATION_STATE","_server","ADBServer","device","Log","warn","chalk","bold","name","dim","learnMore","androidPackage","packages","runAsync","pid","env","EXPO_ADB_USER","lines","split","i","length","line","trim","launchActivity","url","command","push","openAsync","args","results","includes","match","CommandError","substring","indexOf","appId","filePath","options","concat","map","shellQuote","value","replace","output","splitItems","os","EOL","filter","attachedDevices","slice","props","Boolean","type","connectionType","isBooted","isAuthorized","devicePromises","deviceInfo","modelItem","find","info","Promise","all","devices","cpuAbiList","abi","prop","propCommand","getFileOutputAsync","parseAdbDeviceProperties","error","message","devicePropertiesString","properties","propertyExp","matchAll","deviceName","shift"],"mappings":"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"}
@@ -8,21 +8,46 @@ Object.defineProperty(exports, "DevToolsPlugin", {
8
8
  return DevToolsPlugin;
9
9
  }
10
10
  });
11
+ function _nodefs() {
12
+ const data = /*#__PURE__*/ _interop_require_default(require("node:fs"));
13
+ _nodefs = function() {
14
+ return data;
15
+ };
16
+ return data;
17
+ }
11
18
  const _DevToolsPluginschema = require("./DevToolsPlugin.schema");
12
19
  const _DevToolsPluginCliExtensionExecutor = require("./DevToolsPluginCliExtensionExecutor");
13
20
  const _DevToolsPluginManager = require("./DevToolsPluginManager");
21
+ const _dir = require("../../utils/dir");
22
+ function _interop_require_default(obj) {
23
+ return obj && obj.__esModule ? obj : {
24
+ default: obj
25
+ };
26
+ }
27
+ const maybeRealpath = (target)=>{
28
+ try {
29
+ return _nodefs().default.realpathSync(target);
30
+ } catch {
31
+ return target;
32
+ }
33
+ };
14
34
  class DevToolsPlugin {
15
35
  constructor(plugin, projectRoot){
16
36
  this.plugin = plugin;
17
37
  this.projectRoot = projectRoot;
18
38
  this._executor = undefined;
19
- // Validate configuration schema
20
39
  const result = _DevToolsPluginschema.PluginSchema.safeParse(plugin);
21
40
  if (!result.success) {
22
41
  throw new Error(`Invalid plugin configuration: ${result.error.message}`, {
23
42
  cause: result.error
24
43
  });
25
44
  }
45
+ if (plugin.webpageRoot != null) {
46
+ const webpageRoot = maybeRealpath(plugin.webpageRoot);
47
+ if (!(0, _dir.isPathInside)(webpageRoot, plugin.packageRoot)) {
48
+ throw new Error(`webpageRoot (${plugin.webpageRoot}) is not inside packageRoot (${plugin.packageRoot}).`);
49
+ }
50
+ }
26
51
  }
27
52
  get packageName() {
28
53
  return this.plugin.packageName;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/server/DevToolsPlugin.ts"],"sourcesContent":["import type { DevToolsPluginInfo } from './DevToolsPlugin.schema';\nimport { PluginSchema } from './DevToolsPlugin.schema';\nimport { DevToolsPluginCliExtensionExecutor } from './DevToolsPluginCliExtensionExecutor';\nimport { DevToolsPluginEndpoint } from './DevToolsPluginManager';\n\n/**\n * Class that represents a DevTools plugin with CLI and/or web extensions\n *\n * Responsibilities:\n * - Validates plugin configuration against schema\n * - Provides access to plugin metadata (name, description\n * , endpoints)\n * - Manages CLI command execution via DevToolsPluginExecutor\n * - Lazily initializes executor when needed\n * - Constructs web endpoint URLs based on server configuration\n */\nexport class DevToolsPlugin {\n constructor(\n private plugin: DevToolsPluginInfo,\n public readonly projectRoot: string\n ) {\n // Validate configuration schema\n const result = PluginSchema.safeParse(plugin);\n if (!result.success) {\n throw new Error(`Invalid plugin configuration: ${result.error.message}`, {\n cause: result.error,\n });\n }\n }\n\n private _executor: DevToolsPluginCliExtensionExecutor | undefined = undefined;\n\n get packageName(): string {\n return this.plugin.packageName;\n }\n\n get packageRoot(): string {\n return this.plugin.packageRoot;\n }\n\n get webpageEndpoint(): string | undefined {\n return this.plugin?.webpageRoot\n ? `${DevToolsPluginEndpoint}/${this.plugin?.packageName}`\n : undefined;\n }\n\n get webpageRoot(): string | undefined {\n return this.plugin?.webpageRoot;\n }\n\n get description(): string {\n return this.plugin.cliExtensions?.description ?? '';\n }\n\n get cliExtensions(): DevToolsPluginInfo['cliExtensions'] {\n return this.plugin.cliExtensions;\n }\n\n get executor(): DevToolsPluginCliExtensionExecutor | undefined {\n if (!this.plugin.cliExtensions?.entryPoint) {\n return undefined;\n }\n\n if (!this._executor) {\n this._executor = new DevToolsPluginCliExtensionExecutor(this.plugin, this.projectRoot);\n }\n\n return this._executor;\n }\n}\n"],"names":["DevToolsPlugin","plugin","projectRoot","_executor","undefined","result","PluginSchema","safeParse","success","Error","error","message","cause","packageName","packageRoot","webpageEndpoint","webpageRoot","DevToolsPluginEndpoint","description","cliExtensions","executor","entryPoint","DevToolsPluginCliExtensionExecutor"],"mappings":";;;;+BAgBaA;;;eAAAA;;;sCAfgB;oDACsB;uCACZ;AAahC,MAAMA;IACX,YACE,AAAQC,MAA0B,EAClC,AAAgBC,WAAmB,CACnC;aAFQD,SAAAA;aACQC,cAAAA;aAWVC,YAA4DC;QATlE,gCAAgC;QAChC,MAAMC,SAASC,kCAAY,CAACC,SAAS,CAACN;QACtC,IAAI,CAACI,OAAOG,OAAO,EAAE;YACnB,MAAM,IAAIC,MAAM,CAAC,8BAA8B,EAAEJ,OAAOK,KAAK,CAACC,OAAO,EAAE,EAAE;gBACvEC,OAAOP,OAAOK,KAAK;YACrB;QACF;IACF;IAIA,IAAIG,cAAsB;QACxB,OAAO,IAAI,CAACZ,MAAM,CAACY,WAAW;IAChC;IAEA,IAAIC,cAAsB;QACxB,OAAO,IAAI,CAACb,MAAM,CAACa,WAAW;IAChC;IAEA,IAAIC,kBAAsC;YACjC,cAC0B;QADjC,OAAO,EAAA,eAAA,IAAI,CAACd,MAAM,qBAAX,aAAae,WAAW,IAC3B,GAAGC,6CAAsB,CAAC,CAAC,GAAE,gBAAA,IAAI,CAAChB,MAAM,qBAAX,cAAaY,WAAW,EAAE,GACvDT;IACN;IAEA,IAAIY,cAAkC;YAC7B;QAAP,QAAO,eAAA,IAAI,CAACf,MAAM,qBAAX,aAAae,WAAW;IACjC;IAEA,IAAIE,cAAsB;YACjB;QAAP,OAAO,EAAA,6BAAA,IAAI,CAACjB,MAAM,CAACkB,aAAa,qBAAzB,2BAA2BD,WAAW,KAAI;IACnD;IAEA,IAAIC,gBAAqD;QACvD,OAAO,IAAI,CAAClB,MAAM,CAACkB,aAAa;IAClC;IAEA,IAAIC,WAA2D;YACxD;QAAL,IAAI,GAAC,6BAAA,IAAI,CAACnB,MAAM,CAACkB,aAAa,qBAAzB,2BAA2BE,UAAU,GAAE;YAC1C,OAAOjB;QACT;QAEA,IAAI,CAAC,IAAI,CAACD,SAAS,EAAE;YACnB,IAAI,CAACA,SAAS,GAAG,IAAImB,sEAAkC,CAAC,IAAI,CAACrB,MAAM,EAAE,IAAI,CAACC,WAAW;QACvF;QAEA,OAAO,IAAI,CAACC,SAAS;IACvB;AACF"}
1
+ {"version":3,"sources":["../../../../src/start/server/DevToolsPlugin.ts"],"sourcesContent":["import fs from 'node:fs';\n\nimport type { DevToolsPluginInfo } from './DevToolsPlugin.schema';\nimport { PluginSchema } from './DevToolsPlugin.schema';\nimport { DevToolsPluginCliExtensionExecutor } from './DevToolsPluginCliExtensionExecutor';\nimport { DevToolsPluginEndpoint } from './DevToolsPluginManager';\nimport { isPathInside } from '../../utils/dir';\n\nconst maybeRealpath = (target: string): string => {\n try {\n return fs.realpathSync(target);\n } catch {\n return target;\n }\n};\n\n/**\n * Class that represents a DevTools plugin with CLI and/or web extensions\n *\n * Responsibilities:\n * - Validates plugin configuration against schema\n * - Provides access to plugin metadata (name, description\n * , endpoints)\n * - Manages CLI command execution via DevToolsPluginExecutor\n * - Lazily initializes executor when needed\n * - Constructs web endpoint URLs based on server configuration\n */\nexport class DevToolsPlugin {\n constructor(\n private plugin: DevToolsPluginInfo,\n public readonly projectRoot: string\n ) {\n const result = PluginSchema.safeParse(plugin);\n if (!result.success) {\n throw new Error(`Invalid plugin configuration: ${result.error.message}`, {\n cause: result.error,\n });\n }\n\n if (plugin.webpageRoot != null) {\n const webpageRoot = maybeRealpath(plugin.webpageRoot);\n if (!isPathInside(webpageRoot, plugin.packageRoot)) {\n throw new Error(\n `webpageRoot (${plugin.webpageRoot}) is not inside packageRoot (${plugin.packageRoot}).`\n );\n }\n }\n }\n\n private _executor: DevToolsPluginCliExtensionExecutor | undefined = undefined;\n\n get packageName(): string {\n return this.plugin.packageName;\n }\n\n get packageRoot(): string {\n return this.plugin.packageRoot;\n }\n\n get webpageEndpoint(): string | undefined {\n return this.plugin?.webpageRoot\n ? `${DevToolsPluginEndpoint}/${this.plugin?.packageName}`\n : undefined;\n }\n\n get webpageRoot(): string | undefined {\n return this.plugin?.webpageRoot;\n }\n\n get description(): string {\n return this.plugin.cliExtensions?.description ?? '';\n }\n\n get cliExtensions(): DevToolsPluginInfo['cliExtensions'] {\n return this.plugin.cliExtensions;\n }\n\n get executor(): DevToolsPluginCliExtensionExecutor | undefined {\n if (!this.plugin.cliExtensions?.entryPoint) {\n return undefined;\n }\n\n if (!this._executor) {\n this._executor = new DevToolsPluginCliExtensionExecutor(this.plugin, this.projectRoot);\n }\n\n return this._executor;\n }\n}\n"],"names":["DevToolsPlugin","maybeRealpath","target","fs","realpathSync","plugin","projectRoot","_executor","undefined","result","PluginSchema","safeParse","success","Error","error","message","cause","webpageRoot","isPathInside","packageRoot","packageName","webpageEndpoint","DevToolsPluginEndpoint","description","cliExtensions","executor","entryPoint","DevToolsPluginCliExtensionExecutor"],"mappings":";;;;+BA2BaA;;;eAAAA;;;;gEA3BE;;;;;;sCAGc;oDACsB;uCACZ;qBACV;;;;;;AAE7B,MAAMC,gBAAgB,CAACC;IACrB,IAAI;QACF,OAAOC,iBAAE,CAACC,YAAY,CAACF;IACzB,EAAE,OAAM;QACN,OAAOA;IACT;AACF;AAaO,MAAMF;IACX,YACE,AAAQK,MAA0B,EAClC,AAAgBC,WAAmB,CACnC;aAFQD,SAAAA;aACQC,cAAAA;aAmBVC,YAA4DC;QAjBlE,MAAMC,SAASC,kCAAY,CAACC,SAAS,CAACN;QACtC,IAAI,CAACI,OAAOG,OAAO,EAAE;YACnB,MAAM,IAAIC,MAAM,CAAC,8BAA8B,EAAEJ,OAAOK,KAAK,CAACC,OAAO,EAAE,EAAE;gBACvEC,OAAOP,OAAOK,KAAK;YACrB;QACF;QAEA,IAAIT,OAAOY,WAAW,IAAI,MAAM;YAC9B,MAAMA,cAAchB,cAAcI,OAAOY,WAAW;YACpD,IAAI,CAACC,IAAAA,iBAAY,EAACD,aAAaZ,OAAOc,WAAW,GAAG;gBAClD,MAAM,IAAIN,MACR,CAAC,aAAa,EAAER,OAAOY,WAAW,CAAC,6BAA6B,EAAEZ,OAAOc,WAAW,CAAC,EAAE,CAAC;YAE5F;QACF;IACF;IAIA,IAAIC,cAAsB;QACxB,OAAO,IAAI,CAACf,MAAM,CAACe,WAAW;IAChC;IAEA,IAAID,cAAsB;QACxB,OAAO,IAAI,CAACd,MAAM,CAACc,WAAW;IAChC;IAEA,IAAIE,kBAAsC;YACjC,cAC0B;QADjC,OAAO,EAAA,eAAA,IAAI,CAAChB,MAAM,qBAAX,aAAaY,WAAW,IAC3B,GAAGK,6CAAsB,CAAC,CAAC,GAAE,gBAAA,IAAI,CAACjB,MAAM,qBAAX,cAAae,WAAW,EAAE,GACvDZ;IACN;IAEA,IAAIS,cAAkC;YAC7B;QAAP,QAAO,eAAA,IAAI,CAACZ,MAAM,qBAAX,aAAaY,WAAW;IACjC;IAEA,IAAIM,cAAsB;YACjB;QAAP,OAAO,EAAA,6BAAA,IAAI,CAAClB,MAAM,CAACmB,aAAa,qBAAzB,2BAA2BD,WAAW,KAAI;IACnD;IAEA,IAAIC,gBAAqD;QACvD,OAAO,IAAI,CAACnB,MAAM,CAACmB,aAAa;IAClC;IAEA,IAAIC,WAA2D;YACxD;QAAL,IAAI,GAAC,6BAAA,IAAI,CAACpB,MAAM,CAACmB,aAAa,qBAAzB,2BAA2BE,UAAU,GAAE;YAC1C,OAAOlB;QACT;QAEA,IAAI,CAAC,IAAI,CAACD,SAAS,EAAE;YACnB,IAAI,CAACA,SAAS,GAAG,IAAIoB,sEAAkC,CAAC,IAAI,CAACtB,MAAM,EAAE,IAAI,CAACC,WAAW;QACvF;QAEA,OAAO,IAAI,CAACC,SAAS;IACvB;AACF"}