@gjsify/module 0.3.12 → 0.3.14

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/lib/esm/index.js CHANGED
@@ -1,226 +1,257 @@
1
+ import { findPnpManifest, loadPnpManifest, resolveBareViaPnp } from "./pnp.js";
1
2
  import "@girs/gjs";
2
3
  import Gio from "@girs/gio-2.0";
3
4
  import GLib from "@girs/glib-2.0";
4
- import { resolve as resolvePath, readJSON } from "@gjsify/utils";
5
+ import { readJSON, resolve } from "@gjsify/utils";
6
+
7
+ //#region src/index.ts
5
8
  const builtinModules = [
6
- "assert",
7
- "async_hooks",
8
- "buffer",
9
- "child_process",
10
- "cluster",
11
- "console",
12
- "constants",
13
- "crypto",
14
- "dgram",
15
- "diagnostics_channel",
16
- "dns",
17
- "domain",
18
- "events",
19
- "fs",
20
- "http",
21
- "http2",
22
- "https",
23
- "inspector",
24
- "module",
25
- "net",
26
- "os",
27
- "path",
28
- "perf_hooks",
29
- "process",
30
- "punycode",
31
- "querystring",
32
- "readline",
33
- "repl",
34
- "stream",
35
- "string_decoder",
36
- "sys",
37
- "timers",
38
- "tls",
39
- "tty",
40
- "url",
41
- "util",
42
- "v8",
43
- "vm",
44
- "wasi",
45
- "worker_threads",
46
- "zlib"
9
+ "assert",
10
+ "async_hooks",
11
+ "buffer",
12
+ "child_process",
13
+ "cluster",
14
+ "console",
15
+ "constants",
16
+ "crypto",
17
+ "dgram",
18
+ "diagnostics_channel",
19
+ "dns",
20
+ "domain",
21
+ "events",
22
+ "fs",
23
+ "http",
24
+ "http2",
25
+ "https",
26
+ "inspector",
27
+ "module",
28
+ "net",
29
+ "os",
30
+ "path",
31
+ "perf_hooks",
32
+ "process",
33
+ "punycode",
34
+ "querystring",
35
+ "readline",
36
+ "repl",
37
+ "stream",
38
+ "string_decoder",
39
+ "sys",
40
+ "timers",
41
+ "tls",
42
+ "tty",
43
+ "url",
44
+ "util",
45
+ "v8",
46
+ "vm",
47
+ "wasi",
48
+ "worker_threads",
49
+ "zlib"
47
50
  ];
48
51
  function isBuiltin(name) {
49
- const n = name.startsWith("node:") ? name.slice(5) : name;
50
- const base = n.split("/")[0];
51
- return builtinModules.includes(n) || builtinModules.includes(base);
52
+ const n = name.startsWith("node:") ? name.slice(5) : name;
53
+ const base = n.split("/")[0];
54
+ return builtinModules.includes(n) || builtinModules.includes(base);
52
55
  }
56
+ /** Walk up from startDir to find the nearest node_modules directory. */
53
57
  function findNodeModulesDir(startDir) {
54
- let dir = Gio.File.new_for_path(startDir);
55
- while (dir.has_parent(null)) {
56
- const nodeModules = dir.resolve_relative_path("node_modules");
57
- if (nodeModules.query_exists(null)) {
58
- return nodeModules.get_path();
59
- }
60
- dir = dir.get_parent();
61
- }
62
- return null;
58
+ let dir = Gio.File.new_for_path(startDir);
59
+ while (dir.has_parent(null)) {
60
+ const nodeModules = dir.resolve_relative_path("node_modules");
61
+ if (nodeModules.query_exists(null)) {
62
+ return nodeModules.get_path();
63
+ }
64
+ dir = dir.get_parent();
65
+ }
66
+ return null;
63
67
  }
68
+ /** Resolve symlinks for a Gio.File, returning the real path. */
64
69
  function resolveSymlink(file) {
65
- const info = file.query_info("standard::", Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
66
- if (info.get_is_symlink()) {
67
- const target = info.get_symlink_target();
68
- const parent = file.get_parent();
69
- if (target && parent) {
70
- return parent.resolve_relative_path(target).get_path();
71
- }
72
- }
73
- return file.get_path();
70
+ const info = file.query_info("standard::", Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
71
+ if (info.get_is_symlink()) {
72
+ const target = info.get_symlink_target();
73
+ const parent = file.get_parent();
74
+ if (target && parent) {
75
+ return parent.resolve_relative_path(target).get_path();
76
+ }
77
+ }
78
+ return file.get_path();
74
79
  }
80
+ /** Try appending .js to an extensionless path. Returns the file if found, null otherwise. */
75
81
  function tryJsExtension(filePath) {
76
- const withJs = Gio.File.new_for_path(filePath + ".js");
77
- return withJs.query_exists(null) ? withJs : null;
82
+ const withJs = Gio.File.new_for_path(filePath + ".js");
83
+ return withJs.query_exists(null) ? withJs : null;
78
84
  }
85
+ /** Check if a basename has a file extension. */
79
86
  function hasExtension(basename) {
80
- return basename.includes(".");
87
+ return basename.includes(".");
81
88
  }
89
+ /** Resolve package.json main/module entry for a directory. */
82
90
  function resolvePackageEntry(dirPath) {
83
- const pkgJsonFile = resolvePath(dirPath, "package.json");
84
- if (!pkgJsonFile.query_exists(null)) return null;
85
- const pkg = readJSON(pkgJsonFile.get_path());
86
- const main = pkg.main || pkg.module || "index.js";
87
- const entryFile = resolvePath(dirPath, main);
88
- if (entryFile.query_exists(null)) return entryFile.get_path();
89
- if (!hasExtension(main)) {
90
- const withJs = tryJsExtension(entryFile.get_path());
91
- if (withJs) return withJs.get_path();
92
- }
93
- return null;
91
+ const pkgJsonFile = resolve(dirPath, "package.json");
92
+ if (!pkgJsonFile.query_exists(null)) return null;
93
+ const pkg = readJSON(pkgJsonFile.get_path());
94
+ const main = pkg.main || pkg.module || "index.js";
95
+ const entryFile = resolve(dirPath, main);
96
+ if (entryFile.query_exists(null)) return entryFile.get_path();
97
+ if (!hasExtension(main)) {
98
+ const withJs = tryJsExtension(entryFile.get_path());
99
+ if (withJs) return withJs.get_path();
100
+ }
101
+ return null;
94
102
  }
103
+ /** Convert a file: URL (string or object) to an absolute path. */
95
104
  function fileUrlToPath(filenameOrURL) {
96
- if (typeof filenameOrURL === "object" && filenameOrURL !== null && "href" in filenameOrURL) {
97
- const urlObj = filenameOrURL;
98
- if (urlObj.protocol && urlObj.protocol !== "file:") {
99
- throw new TypeError("The URL must use the file: protocol");
100
- }
101
- return GLib.filename_from_uri(urlObj.href)[0];
102
- }
103
- if (typeof filenameOrURL === "string" && filenameOrURL.startsWith("file:")) {
104
- return GLib.filename_from_uri(filenameOrURL)[0];
105
- }
106
- return String(filenameOrURL);
105
+ if (typeof filenameOrURL === "object" && filenameOrURL !== null && "href" in filenameOrURL) {
106
+ const urlObj = filenameOrURL;
107
+ if (urlObj.protocol && urlObj.protocol !== "file:") {
108
+ throw new TypeError("The URL must use the file: protocol");
109
+ }
110
+ return GLib.filename_from_uri(urlObj.href)[0];
111
+ }
112
+ if (typeof filenameOrURL === "string" && filenameOrURL.startsWith("file:")) {
113
+ return GLib.filename_from_uri(filenameOrURL)[0];
114
+ }
115
+ return String(filenameOrURL);
107
116
  }
117
+ /**
118
+ * Try resolving a bare specifier through a Yarn PnP manifest (`.pnp.cjs`)
119
+ * sitting above `callerDir`. Returns null when no manifest is found, or when
120
+ * PnP can't resolve the request (e.g. the dep isn't listed in the caller
121
+ * package's `packageDependencies`). Callers fall back to the node_modules walk.
122
+ */
123
+ function resolveBareViaPnpFromCaller(id, callerDir) {
124
+ const pnpPath = findPnpManifest(callerDir);
125
+ if (!pnpPath) return null;
126
+ const manifest = loadPnpManifest(pnpPath);
127
+ if (!manifest) return null;
128
+ const resolved = resolveBareViaPnp(manifest, id, callerDir);
129
+ if (!resolved) return null;
130
+ const file = Gio.File.new_for_path(resolved);
131
+ return file.query_exists(null) ? file : null;
132
+ }
133
+ /**
134
+ * Resolve a bare package specifier by walking ALL ancestor node_modules dirs.
135
+ * Mirrors Node.js module resolution: try nearest node_modules first, then each
136
+ * ancestor — so a package in a parent node_modules is found even when a closer
137
+ * node_modules exists but doesn't contain the requested package.
138
+ */
108
139
  function resolveInNodeModules(id, callerDir) {
109
- let dir = Gio.File.new_for_path(callerDir);
110
- while (dir.has_parent(null)) {
111
- const nodeModulesFile = dir.resolve_relative_path("node_modules");
112
- if (nodeModulesFile.query_exists(null)) {
113
- const candidate = nodeModulesFile.resolve_relative_path(id);
114
- if (candidate.query_exists(null)) return candidate;
115
- const bn = candidate.get_basename();
116
- if (bn && !hasExtension(bn)) {
117
- const withJs = tryJsExtension(candidate.get_path());
118
- if (withJs) return withJs;
119
- }
120
- }
121
- dir = dir.get_parent();
122
- }
123
- throw new Error(`Cannot find module "${id}" - not found in any node_modules directory`);
140
+ let dir = Gio.File.new_for_path(callerDir);
141
+ while (dir.has_parent(null)) {
142
+ const nodeModulesFile = dir.resolve_relative_path("node_modules");
143
+ if (nodeModulesFile.query_exists(null)) {
144
+ const candidate = nodeModulesFile.resolve_relative_path(id);
145
+ if (candidate.query_exists(null)) return candidate;
146
+ const bn = candidate.get_basename();
147
+ if (bn && !hasExtension(bn)) {
148
+ const withJs = tryJsExtension(candidate.get_path());
149
+ if (withJs) return withJs;
150
+ }
151
+ }
152
+ dir = dir.get_parent();
153
+ }
154
+ throw new Error(`Cannot find module "${id}" - not found in any node_modules directory`);
124
155
  }
156
+ /** Resolve a module specifier to an absolute file path. */
125
157
  function resolveModulePath(id, callerDir) {
126
- if (isBuiltin(id)) return id;
127
- let file;
128
- if (id.startsWith("/")) {
129
- file = resolvePath(id);
130
- } else if (id.startsWith(".")) {
131
- file = resolvePath(callerDir, id);
132
- } else {
133
- file = resolveInNodeModules(id, callerDir);
134
- }
135
- if (id.startsWith("/") || id.startsWith(".")) {
136
- if (!file.query_exists(null)) {
137
- const basename2 = file.get_basename();
138
- if (basename2 && !hasExtension(basename2)) {
139
- file = tryJsExtension(file.get_path()) ?? file;
140
- }
141
- }
142
- if (!file.query_exists(null)) {
143
- throw new Error(`Cannot find module "${id}"`);
144
- }
145
- }
146
- const resolvedPath = resolveSymlink(file);
147
- const basename = file.get_basename();
148
- if (basename && !hasExtension(basename)) {
149
- const entry = resolvePackageEntry(resolvedPath);
150
- if (entry) return entry;
151
- }
152
- return resolvedPath;
158
+ if (isBuiltin(id)) return id;
159
+ let file;
160
+ if (id.startsWith("/")) {
161
+ file = resolve(id);
162
+ } else if (id.startsWith(".")) {
163
+ file = resolve(callerDir, id);
164
+ } else {
165
+ const pnpFile = resolveBareViaPnpFromCaller(id, callerDir);
166
+ file = pnpFile ?? resolveInNodeModules(id, callerDir);
167
+ }
168
+ if (id.startsWith("/") || id.startsWith(".")) {
169
+ if (!file.query_exists(null)) {
170
+ const basename = file.get_basename();
171
+ if (basename && !hasExtension(basename)) {
172
+ file = tryJsExtension(file.get_path()) ?? file;
173
+ }
174
+ }
175
+ if (!file.query_exists(null)) {
176
+ throw new Error(`Cannot find module "${id}"`);
177
+ }
178
+ }
179
+ const resolvedPath = resolveSymlink(file);
180
+ const basename = file.get_basename();
181
+ if (basename && !hasExtension(basename)) {
182
+ const entry = resolvePackageEntry(resolvedPath);
183
+ if (entry) return entry;
184
+ }
185
+ return resolvedPath;
153
186
  }
187
+ /** Load a CJS .js/.cjs file using GJS's legacy imports system. */
154
188
  function requireJsFile(filePath, cache) {
155
- if (filePath in cache) return cache[filePath];
156
- let file = Gio.File.new_for_path(filePath);
157
- const dir = file.get_parent().get_path();
158
- let basename = file.get_basename();
159
- if (basename.endsWith(".mjs")) {
160
- throw new Error(`Cannot require .mjs files. Use import instead. Path: "${filePath}"`);
161
- }
162
- if (basename.endsWith(".cjs")) {
163
- const dest = resolvePath(dir, "__gjsify__" + basename.replace(/\.cjs$/, ".js"));
164
- if (dest.query_exists(null)) dest.delete(null);
165
- file.copy(dest, Gio.FileCopyFlags.NONE, null, null);
166
- file = dest;
167
- basename = file.get_basename();
168
- }
169
- const savedExports = globalThis.exports;
170
- const savedModule = globalThis.module;
171
- const moduleObj = { exports: {} };
172
- globalThis.exports = moduleObj.exports;
173
- globalThis.module = moduleObj;
174
- try {
175
- const { searchPath } = imports;
176
- searchPath.unshift(dir);
177
- imports[basename.replace(/\.(js|cjs)$/, "")];
178
- searchPath.shift();
179
- const result = moduleObj.exports;
180
- cache[filePath] = result;
181
- return result;
182
- } finally {
183
- globalThis.exports = savedExports;
184
- globalThis.module = savedModule;
185
- }
189
+ if (filePath in cache) return cache[filePath];
190
+ let file = Gio.File.new_for_path(filePath);
191
+ const dir = file.get_parent().get_path();
192
+ let basename = file.get_basename();
193
+ if (basename.endsWith(".mjs")) {
194
+ throw new Error(`Cannot require .mjs files. Use import instead. Path: "${filePath}"`);
195
+ }
196
+ if (basename.endsWith(".cjs")) {
197
+ const dest = resolve(dir, "__gjsify__" + basename.replace(/\.cjs$/, ".js"));
198
+ if (dest.query_exists(null)) dest.delete(null);
199
+ file.copy(dest, Gio.FileCopyFlags.NONE, null, null);
200
+ file = dest;
201
+ basename = file.get_basename();
202
+ }
203
+ const savedExports = globalThis.exports;
204
+ const savedModule = globalThis.module;
205
+ const moduleObj = { exports: {} };
206
+ globalThis.exports = moduleObj.exports;
207
+ globalThis.module = moduleObj;
208
+ try {
209
+ const { searchPath } = imports;
210
+ searchPath.unshift(dir);
211
+ imports[basename.replace(/\.(js|cjs)$/, "")];
212
+ searchPath.shift();
213
+ const result = moduleObj.exports;
214
+ cache[filePath] = result;
215
+ return result;
216
+ } finally {
217
+ globalThis.exports = savedExports;
218
+ globalThis.module = savedModule;
219
+ }
186
220
  }
187
221
  function createRequire(filenameOrURL) {
188
- const filename = fileUrlToPath(filenameOrURL);
189
- if (!filename.startsWith("/")) {
190
- throw new TypeError(
191
- `The argument must be a file URL object, file URL string, or absolute path string. Received "${String(filenameOrURL)}"`
192
- );
193
- }
194
- const callerDir = GLib.path_get_dirname(filename);
195
- const cache = /* @__PURE__ */ Object.create(null);
196
- const req = function require2(id) {
197
- const resolved = resolveModulePath(id, callerDir);
198
- if (resolved in cache) return cache[resolved];
199
- if (resolved.endsWith(".json")) {
200
- const result = readJSON(resolved);
201
- cache[resolved] = result;
202
- return result;
203
- }
204
- if (isBuiltin(id)) {
205
- throw new Error(
206
- `createRequire: Cannot require builtin module "${id}" synchronously in GJS. Use import instead.`
207
- );
208
- }
209
- return requireJsFile(resolved, cache);
210
- };
211
- req.resolve = function resolve(id) {
212
- return resolveModulePath(id, callerDir);
213
- };
214
- req.resolve.paths = (_request) => null;
215
- req.cache = cache;
216
- req.extensions = /* @__PURE__ */ Object.create(null);
217
- req.main = void 0;
218
- return req;
222
+ const filename = fileUrlToPath(filenameOrURL);
223
+ if (!filename.startsWith("/")) {
224
+ throw new TypeError("The argument must be a file URL object, file URL string, or absolute path string. " + `Received "${String(filenameOrURL)}"`);
225
+ }
226
+ const callerDir = GLib.path_get_dirname(filename);
227
+ const cache = Object.create(null);
228
+ const req = function require(id) {
229
+ const resolved = resolveModulePath(id, callerDir);
230
+ if (resolved in cache) return cache[resolved];
231
+ if (resolved.endsWith(".json")) {
232
+ const result = readJSON(resolved);
233
+ cache[resolved] = result;
234
+ return result;
235
+ }
236
+ if (isBuiltin(id)) {
237
+ throw new Error(`createRequire: Cannot require builtin module "${id}" synchronously in GJS. ` + "Use import instead.");
238
+ }
239
+ return requireJsFile(resolved, cache);
240
+ };
241
+ req.resolve = function resolve(id) {
242
+ return resolveModulePath(id, callerDir);
243
+ };
244
+ req.resolve.paths = (_request) => null;
245
+ req.cache = cache;
246
+ req.extensions = Object.create(null);
247
+ req.main = undefined;
248
+ return req;
219
249
  }
220
- var index_default = { builtinModules, isBuiltin, createRequire };
221
- export {
222
- builtinModules,
223
- createRequire,
224
- index_default as default,
225
- isBuiltin
250
+ var src_default = {
251
+ builtinModules,
252
+ isBuiltin,
253
+ createRequire
226
254
  };
255
+
256
+ //#endregion
257
+ export { builtinModules, createRequire, src_default as default, isBuiltin };
package/lib/esm/pnp.js ADDED
@@ -0,0 +1,179 @@
1
+ import Gio from "@girs/gio-2.0";
2
+ import GLib from "@girs/glib-2.0";
3
+
4
+ //#region src/pnp.ts
5
+ const manifestCache = new Map();
6
+ /** Walk up from `startDir` looking for the nearest `.pnp.cjs`. */
7
+ function findPnpManifest(startDir) {
8
+ let dir = Gio.File.new_for_path(startDir);
9
+ while (dir.has_parent(null)) {
10
+ const candidate = dir.resolve_relative_path(".pnp.cjs");
11
+ if (candidate.query_exists(null)) return candidate.get_path();
12
+ dir = dir.get_parent();
13
+ }
14
+ return null;
15
+ }
16
+ /** Read + parse `.pnp.cjs`'s `RAW_RUNTIME_STATE`. Cached. */
17
+ function loadPnpManifest(pnpCjsPath) {
18
+ const cached = manifestCache.get(pnpCjsPath);
19
+ if (cached !== undefined) return cached;
20
+ const file = Gio.File.new_for_path(pnpCjsPath);
21
+ let text;
22
+ try {
23
+ const [ok, bytes] = file.load_contents(null);
24
+ if (!ok) {
25
+ manifestCache.set(pnpCjsPath, null);
26
+ return null;
27
+ }
28
+ text = new TextDecoder().decode(bytes);
29
+ } catch {
30
+ manifestCache.set(pnpCjsPath, null);
31
+ return null;
32
+ }
33
+ const state = extractRawRuntimeState(text);
34
+ if (!state) {
35
+ manifestCache.set(pnpCjsPath, null);
36
+ return null;
37
+ }
38
+ const rootDir = GLib.path_get_dirname(pnpCjsPath);
39
+ const packages = new Map();
40
+ const locatorsByLocation = new Map();
41
+ for (const [name, store] of state.packageRegistryData) {
42
+ const inner = new Map();
43
+ for (const [reference, info] of store) {
44
+ inner.set(reference, info);
45
+ if (!info.discardFromLookup) {
46
+ locatorsByLocation.set(info.packageLocation, {
47
+ name,
48
+ reference
49
+ });
50
+ }
51
+ }
52
+ packages.set(name, inner);
53
+ }
54
+ const manifest = {
55
+ rootDir,
56
+ packages,
57
+ locatorsByLocation
58
+ };
59
+ manifestCache.set(pnpCjsPath, manifest);
60
+ return manifest;
61
+ }
62
+ /**
63
+ * Strip the line-continuations (`\\\n`) Yarn writes around the JSON literal
64
+ * and JSON.parse the result. Returns null if we can't find the literal.
65
+ */
66
+ function extractRawRuntimeState(text) {
67
+ const start = text.indexOf("const RAW_RUNTIME_STATE =");
68
+ if (start < 0) return null;
69
+ const openQuote = text.indexOf("'", start);
70
+ if (openQuote < 0) return null;
71
+ let i = openQuote + 1;
72
+ while (i < text.length) {
73
+ const ch = text.charCodeAt(i);
74
+ if (ch === 92) {
75
+ i += 2;
76
+ continue;
77
+ }
78
+ if (ch === 39) {
79
+ break;
80
+ }
81
+ i++;
82
+ }
83
+ if (i >= text.length) return null;
84
+ let raw = text.slice(openQuote + 1, i);
85
+ raw = raw.replace(/\\\n/g, "");
86
+ try {
87
+ return JSON.parse(raw);
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * Find which package owns `absolutePath`. Returns the locator + its info, or
94
+ * null when the path isn't covered by any package in the manifest.
95
+ *
96
+ * Uses longest-prefix-match against `packageLocation` entries (Yarn does the
97
+ * same in `findPackageLocator`).
98
+ */
99
+ function findPackageOwning(manifest, absolutePath) {
100
+ const relPath = relativeFromRoot(manifest.rootDir, absolutePath);
101
+ if (relPath === null) return null;
102
+ let bestMatch = null;
103
+ for (const candidateLocation of manifest.locatorsByLocation.keys()) {
104
+ if (relPath.startsWith(candidateLocation) && (bestMatch === null || candidateLocation.length > bestMatch.length)) {
105
+ bestMatch = candidateLocation;
106
+ }
107
+ }
108
+ if (bestMatch === null) return null;
109
+ const locator = manifest.locatorsByLocation.get(bestMatch);
110
+ const info = manifest.packages.get(locator.name)?.get(locator.reference);
111
+ if (!info) return null;
112
+ return {
113
+ locator,
114
+ info
115
+ };
116
+ }
117
+ /**
118
+ * Resolve a bare specifier through PnP. Returns the absolute on-disk path,
119
+ * or null when the request can't be resolved this way.
120
+ *
121
+ * `id` is a bare specifier like `@scope/foo` or `@scope/foo/bar/baz.js`.
122
+ * `callerPath` is the absolute path of the file doing the require.
123
+ */
124
+ function resolveBareViaPnp(manifest, id, callerPath) {
125
+ const owner = findPackageOwning(manifest, callerPath);
126
+ if (!owner) return null;
127
+ const { pkgName, subPath } = splitSpecifier(id);
128
+ const dep = owner.info.packageDependencies?.find(([name]) => name === pkgName);
129
+ if (!dep) return null;
130
+ const [, reference] = dep;
131
+ if (reference === null) return null;
132
+ const target = manifest.packages.get(pkgName)?.get(reference);
133
+ if (!target) return null;
134
+ const baseFile = target.packageLocation.startsWith("/") ? Gio.File.new_for_path(target.packageLocation) : Gio.File.new_for_path(manifest.rootDir).resolve_relative_path(stripLeadingDotSlash(target.packageLocation));
135
+ const finalFile = subPath ? baseFile.resolve_relative_path(subPath) : baseFile;
136
+ return finalFile.get_path();
137
+ }
138
+ /** Split `@scope/foo/sub/path` into `pkgName: '@scope/foo'`, `subPath: 'sub/path'`. */
139
+ function splitSpecifier(id) {
140
+ if (id.startsWith("@")) {
141
+ const slash1 = id.indexOf("/");
142
+ if (slash1 < 0) return {
143
+ pkgName: id,
144
+ subPath: ""
145
+ };
146
+ const slash2 = id.indexOf("/", slash1 + 1);
147
+ if (slash2 < 0) return {
148
+ pkgName: id,
149
+ subPath: ""
150
+ };
151
+ return {
152
+ pkgName: id.slice(0, slash2),
153
+ subPath: id.slice(slash2 + 1)
154
+ };
155
+ }
156
+ const slash = id.indexOf("/");
157
+ if (slash < 0) return {
158
+ pkgName: id,
159
+ subPath: ""
160
+ };
161
+ return {
162
+ pkgName: id.slice(0, slash),
163
+ subPath: id.slice(slash + 1)
164
+ };
165
+ }
166
+ function stripLeadingDotSlash(p) {
167
+ return p.startsWith("./") ? p.slice(2) : p;
168
+ }
169
+ /** Returns `<absolutePath>` minus `<rootDir>/`, with a trailing slash on
170
+ * directory components, matching Yarn's `packageLocation` keys. */
171
+ function relativeFromRoot(rootDir, absolutePath) {
172
+ if (absolutePath === rootDir) return "./";
173
+ const prefix = rootDir.endsWith("/") ? rootDir : rootDir + "/";
174
+ if (!absolutePath.startsWith(prefix)) return null;
175
+ return "./" + absolutePath.slice(prefix.length);
176
+ }
177
+
178
+ //#endregion
179
+ export { findPackageOwning, findPnpManifest, loadPnpManifest, resolveBareViaPnp };