expo-modules-autolinking 55.0.22 → 55.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,12 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.23 — 2026-05-19
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Disallow devtools plugins to point to `webpageRoot` outside of their own bounds ([#45841](https://github.com/expo/expo/pull/45841) by [@kitten](https://github.com/kitten))
18
+
13
19
  ## 55.0.22 — 2026-05-13
14
20
 
15
21
  ### 🐛 Bug fixes
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.resolveModuleAsync = resolveModuleAsync;
7
7
  exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
8
8
  const path_1 = __importDefault(require("path"));
9
+ const utils_1 = require("../utils");
9
10
  async function resolveModuleAsync(packageName, revision) {
10
11
  const devtoolsConfig = revision.config?.toJSON().devtools;
11
12
  if (devtoolsConfig == null) {
@@ -14,12 +15,19 @@ async function resolveModuleAsync(packageName, revision) {
14
15
  return {
15
16
  packageName,
16
17
  packageRoot: revision.path,
17
- webpageRoot: devtoolsConfig.webpageRoot
18
- ? path_1.default.join(revision.path, devtoolsConfig.webpageRoot)
19
- : undefined,
18
+ webpageRoot: await resolveWebpageRoot(revision.path, devtoolsConfig.webpageRoot),
20
19
  cliExtensions: devtoolsConfig.cliExtensions,
21
20
  };
22
21
  }
22
+ async function resolveWebpageRoot(packageRoot, configuredWebpageRoot) {
23
+ if (!configuredWebpageRoot) {
24
+ return undefined;
25
+ }
26
+ const resolvedWebpageRoot = path_1.default.resolve(packageRoot, configuredWebpageRoot);
27
+ // NOTE(@kitten): Failing realpath-ing, typically due to ENOENT, results in the original value
28
+ const webpageRoot = (await (0, utils_1.maybeRealpath)(resolvedWebpageRoot)) ?? resolvedWebpageRoot;
29
+ return (0, utils_1.isPathInside)(webpageRoot, packageRoot) ? webpageRoot : undefined;
30
+ }
23
31
  async function resolveExtraBuildDependenciesAsync(_projectNativeRoot) {
24
32
  return null;
25
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../src/platforms/devtools.ts"],"names":[],"mappings":";;;;;AAIA,gDAiBC;AAED,gFAIC;AA3BD,gDAAwB;AAIjB,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAyB;IAEzB,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC;IAC1D,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,WAAW;QACX,WAAW,EAAE,QAAQ,CAAC,IAAI;QAC1B,WAAW,EAAE,cAAc,CAAC,WAAW;YACrC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC;YACtD,CAAC,CAAC,SAAS;QACb,aAAa,EAAE,cAAc,CAAC,aAAa;KAC5C,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,kCAAkC,CACtD,kBAA0B;IAE1B,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import path from 'path';\n\nimport type { ExtraDependencies, ModuleDescriptorDevTools, PackageRevision } from '../types';\n\nexport async function resolveModuleAsync(\n packageName: string,\n revision: PackageRevision\n): Promise<ModuleDescriptorDevTools | null> {\n const devtoolsConfig = revision.config?.toJSON().devtools;\n if (devtoolsConfig == null) {\n return null;\n }\n\n return {\n packageName,\n packageRoot: revision.path,\n webpageRoot: devtoolsConfig.webpageRoot\n ? path.join(revision.path, devtoolsConfig.webpageRoot)\n : undefined,\n cliExtensions: devtoolsConfig.cliExtensions,\n };\n}\n\nexport async function resolveExtraBuildDependenciesAsync(\n _projectNativeRoot: string\n): Promise<ExtraDependencies | null> {\n return null;\n}\n"]}
1
+ {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../src/platforms/devtools.ts"],"names":[],"mappings":";;;;;AAKA,gDAeC;AAeD,gFAIC;AAvCD,gDAAwB;AAGxB,oCAAuD;AAEhD,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAyB;IAEzB,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC;IAC1D,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,WAAW;QACX,WAAW,EAAE,QAAQ,CAAC,IAAI;QAC1B,WAAW,EAAE,MAAM,kBAAkB,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC;QAChF,aAAa,EAAE,cAAc,CAAC,aAAa;KAC5C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,WAAmB,EACnB,qBAAyC;IAEzC,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,mBAAmB,GAAG,cAAI,CAAC,OAAO,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC;IAC7E,8FAA8F;IAC9F,MAAM,WAAW,GAAG,CAAC,MAAM,IAAA,qBAAa,EAAC,mBAAmB,CAAC,CAAC,IAAI,mBAAmB,CAAC;IACtF,OAAO,IAAA,oBAAY,EAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1E,CAAC;AAEM,KAAK,UAAU,kCAAkC,CACtD,kBAA0B;IAE1B,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import path from 'path';\n\nimport type { ExtraDependencies, ModuleDescriptorDevTools, PackageRevision } from '../types';\nimport { isPathInside, maybeRealpath } from '../utils';\n\nexport async function resolveModuleAsync(\n packageName: string,\n revision: PackageRevision\n): Promise<ModuleDescriptorDevTools | null> {\n const devtoolsConfig = revision.config?.toJSON().devtools;\n if (devtoolsConfig == null) {\n return null;\n }\n\n return {\n packageName,\n packageRoot: revision.path,\n webpageRoot: await resolveWebpageRoot(revision.path, devtoolsConfig.webpageRoot),\n cliExtensions: devtoolsConfig.cliExtensions,\n };\n}\n\nasync function resolveWebpageRoot(\n packageRoot: string,\n configuredWebpageRoot: string | undefined\n): Promise<string | undefined> {\n if (!configuredWebpageRoot) {\n return undefined;\n }\n const resolvedWebpageRoot = path.resolve(packageRoot, configuredWebpageRoot);\n // NOTE(@kitten): Failing realpath-ing, typically due to ENOENT, results in the original value\n const webpageRoot = (await maybeRealpath(resolvedWebpageRoot)) ?? resolvedWebpageRoot;\n return isPathInside(webpageRoot, packageRoot) ? webpageRoot : undefined;\n}\n\nexport async function resolveExtraBuildDependenciesAsync(\n _projectNativeRoot: string\n): Promise<ExtraDependencies | null> {\n return null;\n}\n"]}
package/build/utils.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare function scanFilesRecursively(parentPath: string, includeDirector
11
11
  export declare const fileExistsAsync: (file: string) => Promise<string | null>;
12
12
  export declare const fastJoin: (from: string, append: string) => string;
13
13
  export declare const maybeRealpath: (target: string) => Promise<string | null>;
14
+ export declare function isPathInside(child: string, parent: string): boolean;
14
15
  export type PackageJson = Record<string, unknown> & {
15
16
  name?: string;
16
17
  version?: string;
package/build/utils.js CHANGED
@@ -7,6 +7,7 @@ exports.loadPackageJson = exports.maybeRealpath = exports.fastJoin = exports.fil
7
7
  exports.listFilesSorted = listFilesSorted;
8
8
  exports.listFilesInDirectories = listFilesInDirectories;
9
9
  exports.scanFilesRecursively = scanFilesRecursively;
10
+ exports.isPathInside = isPathInside;
10
11
  const fs_1 = __importDefault(require("fs"));
11
12
  const path_1 = __importDefault(require("path"));
12
13
  const memoize_1 = require("./memoize");
@@ -84,6 +85,10 @@ const maybeRealpath = async (target) => {
84
85
  }
85
86
  };
86
87
  exports.maybeRealpath = maybeRealpath;
88
+ function isPathInside(child, parent) {
89
+ const relative = path_1.default.relative(parent, child);
90
+ return !!relative && !relative.startsWith('..') && !path_1.default.isAbsolute(relative);
91
+ }
87
92
  exports.loadPackageJson = (0, memoize_1.memoize)(async function loadPackageJson(jsonPath) {
88
93
  try {
89
94
  const packageJsonText = await fs_1.default.promises.readFile(jsonPath, 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport { memoize } from './memoize';\n\n/** List filtered top-level files in `targetPath` (returns absolute paths) */\nexport async function listFilesSorted(\n targetPath: string,\n filter: (basename: string) => boolean\n): Promise<string[]> {\n try {\n // `readdir` isn't guaranteed to be sorted on Windows\n return (await fs.promises.readdir(targetPath, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && filter(entry.name))\n .sort((a, b) => a.name.localeCompare(b.name))\n .map((entry) => path.join(targetPath, entry.name));\n } catch {\n return [];\n }\n}\n\n/** List nested files in top-level directories in `targetPath` (returns relative paths) */\nexport async function listFilesInDirectories(\n targetPath: string,\n filter: (basename: string) => boolean\n): Promise<string[]> {\n return (\n await Promise.all(\n (await fs.promises.readdir(targetPath, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory() && entry.name !== 'node_modules')\n .sort((a, b) => a.name.localeCompare(b.name))\n .map(async (directory) => {\n const entries = await fs.promises.readdir(path.join(targetPath, directory.name), {\n withFileTypes: true,\n });\n return entries\n .filter((entry) => entry.isFile() && filter(entry.name))\n .sort((a, b) => a.name.localeCompare(b.name))\n .map((entry) => path.join(directory.name, entry.name));\n })\n )\n ).flat(1);\n}\n\n/** Iterate folders recursively for files, optionally sorting results and filtering directories */\nexport async function* scanFilesRecursively(\n parentPath: string,\n includeDirectory?: (parentPath: string, name: string) => boolean,\n sort = !fs.opendir\n) {\n const queue = [parentPath];\n let targetPath: string | undefined;\n while (queue.length > 0 && (targetPath = queue.shift()) != null) {\n try {\n const entries = sort\n ? (await fs.promises.readdir(targetPath, { withFileTypes: true })).sort((a, b) =>\n a.name.localeCompare(b.name)\n )\n : await fs.promises.opendir(targetPath);\n for await (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'node_modules') {\n if (!includeDirectory || includeDirectory(targetPath, entry.name)) {\n queue.push(path.join(targetPath, entry.name));\n }\n } else if (entry.isFile()) {\n yield {\n path: path.join(targetPath, entry.name),\n parentPath: targetPath,\n name: entry.name,\n } as const;\n }\n }\n } catch {\n continue;\n }\n }\n}\n\nexport const fileExistsAsync = async (file: string): Promise<string | null> => {\n const stat = await fs.promises.stat(file).catch(() => null);\n return stat?.isFile() ? file : null;\n};\n\nexport const fastJoin: (from: string, append: string) => string =\n path.sep === '/'\n ? (from, append) => `${from}${path.sep}${append}`\n : (from, append) =>\n `${from}${path.sep}${append[0] === '@' ? append.replace('/', path.sep) : append}`;\n\nexport const maybeRealpath = async (target: string): Promise<string | null> => {\n try {\n return await fs.promises.realpath(target);\n } catch {\n return null;\n }\n};\n\nexport type PackageJson = Record<string, unknown> & {\n name?: string;\n version?: string;\n peerDependencies?: Record<string, string>;\n codegenConfig?: Record<string, unknown>;\n};\n\nexport const loadPackageJson = memoize(async function loadPackageJson(\n jsonPath: string\n): Promise<PackageJson | null> {\n try {\n const packageJsonText = await fs.promises.readFile(jsonPath, 'utf8');\n const json = JSON.parse(packageJsonText);\n if (typeof json !== 'object' || json == null) {\n return null;\n }\n return json;\n } catch {\n return null;\n }\n});\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport { memoize } from './memoize';\n\n/** List filtered top-level files in `targetPath` (returns absolute paths) */\nexport async function listFilesSorted(\n targetPath: string,\n filter: (basename: string) => boolean\n): Promise<string[]> {\n try {\n // `readdir` isn't guaranteed to be sorted on Windows\n return (await fs.promises.readdir(targetPath, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && filter(entry.name))\n .sort((a, b) => a.name.localeCompare(b.name))\n .map((entry) => path.join(targetPath, entry.name));\n } catch {\n return [];\n }\n}\n\n/** List nested files in top-level directories in `targetPath` (returns relative paths) */\nexport async function listFilesInDirectories(\n targetPath: string,\n filter: (basename: string) => boolean\n): Promise<string[]> {\n return (\n await Promise.all(\n (await fs.promises.readdir(targetPath, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory() && entry.name !== 'node_modules')\n .sort((a, b) => a.name.localeCompare(b.name))\n .map(async (directory) => {\n const entries = await fs.promises.readdir(path.join(targetPath, directory.name), {\n withFileTypes: true,\n });\n return entries\n .filter((entry) => entry.isFile() && filter(entry.name))\n .sort((a, b) => a.name.localeCompare(b.name))\n .map((entry) => path.join(directory.name, entry.name));\n })\n )\n ).flat(1);\n}\n\n/** Iterate folders recursively for files, optionally sorting results and filtering directories */\nexport async function* scanFilesRecursively(\n parentPath: string,\n includeDirectory?: (parentPath: string, name: string) => boolean,\n sort = !fs.opendir\n) {\n const queue = [parentPath];\n let targetPath: string | undefined;\n while (queue.length > 0 && (targetPath = queue.shift()) != null) {\n try {\n const entries = sort\n ? (await fs.promises.readdir(targetPath, { withFileTypes: true })).sort((a, b) =>\n a.name.localeCompare(b.name)\n )\n : await fs.promises.opendir(targetPath);\n for await (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'node_modules') {\n if (!includeDirectory || includeDirectory(targetPath, entry.name)) {\n queue.push(path.join(targetPath, entry.name));\n }\n } else if (entry.isFile()) {\n yield {\n path: path.join(targetPath, entry.name),\n parentPath: targetPath,\n name: entry.name,\n } as const;\n }\n }\n } catch {\n continue;\n }\n }\n}\n\nexport const fileExistsAsync = async (file: string): Promise<string | null> => {\n const stat = await fs.promises.stat(file).catch(() => null);\n return stat?.isFile() ? file : null;\n};\n\nexport const fastJoin: (from: string, append: string) => string =\n path.sep === '/'\n ? (from, append) => `${from}${path.sep}${append}`\n : (from, append) =>\n `${from}${path.sep}${append[0] === '@' ? append.replace('/', path.sep) : append}`;\n\nexport const maybeRealpath = async (target: string): Promise<string | null> => {\n try {\n return await fs.promises.realpath(target);\n } catch {\n return null;\n }\n};\n\nexport function isPathInside(child: string, parent: string): boolean {\n const relative = path.relative(parent, child);\n return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);\n}\n\nexport type PackageJson = Record<string, unknown> & {\n name?: string;\n version?: string;\n peerDependencies?: Record<string, string>;\n codegenConfig?: Record<string, unknown>;\n};\n\nexport const loadPackageJson = memoize(async function loadPackageJson(\n jsonPath: string\n): Promise<PackageJson | null> {\n try {\n const packageJsonText = await fs.promises.readFile(jsonPath, 'utf8');\n const json = JSON.parse(packageJsonText);\n if (typeof json !== 'object' || json == null) {\n return null;\n }\n return json;\n } catch {\n return null;\n }\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-autolinking",
3
- "version": "55.0.22",
3
+ "version": "55.0.23",
4
4
  "description": "Scripts that autolink Expo modules.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -43,5 +43,5 @@
43
43
  "chalk": "^4.1.0",
44
44
  "commander": "^7.2.0"
45
45
  },
46
- "gitHead": "0675db12d13a5309e3109e8ecf7f6011522194c6"
46
+ "gitHead": "fcb091766242d53248cd3c5949965961dbc5ec1d"
47
47
  }
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
 
3
3
  import type { ExtraDependencies, ModuleDescriptorDevTools, PackageRevision } from '../types';
4
+ import { isPathInside, maybeRealpath } from '../utils';
4
5
 
5
6
  export async function resolveModuleAsync(
6
7
  packageName: string,
@@ -14,13 +15,24 @@ export async function resolveModuleAsync(
14
15
  return {
15
16
  packageName,
16
17
  packageRoot: revision.path,
17
- webpageRoot: devtoolsConfig.webpageRoot
18
- ? path.join(revision.path, devtoolsConfig.webpageRoot)
19
- : undefined,
18
+ webpageRoot: await resolveWebpageRoot(revision.path, devtoolsConfig.webpageRoot),
20
19
  cliExtensions: devtoolsConfig.cliExtensions,
21
20
  };
22
21
  }
23
22
 
23
+ async function resolveWebpageRoot(
24
+ packageRoot: string,
25
+ configuredWebpageRoot: string | undefined
26
+ ): Promise<string | undefined> {
27
+ if (!configuredWebpageRoot) {
28
+ return undefined;
29
+ }
30
+ const resolvedWebpageRoot = path.resolve(packageRoot, configuredWebpageRoot);
31
+ // NOTE(@kitten): Failing realpath-ing, typically due to ENOENT, results in the original value
32
+ const webpageRoot = (await maybeRealpath(resolvedWebpageRoot)) ?? resolvedWebpageRoot;
33
+ return isPathInside(webpageRoot, packageRoot) ? webpageRoot : undefined;
34
+ }
35
+
24
36
  export async function resolveExtraBuildDependenciesAsync(
25
37
  _projectNativeRoot: string
26
38
  ): Promise<ExtraDependencies | null> {
package/src/utils.ts CHANGED
@@ -95,6 +95,11 @@ export const maybeRealpath = async (target: string): Promise<string | null> => {
95
95
  }
96
96
  };
97
97
 
98
+ export function isPathInside(child: string, parent: string): boolean {
99
+ const relative = path.relative(parent, child);
100
+ return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
101
+ }
102
+
98
103
  export type PackageJson = Record<string, unknown> & {
99
104
  name?: string;
100
105
  version?: string;