@gjsify/module 0.3.13 → 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 +227 -208
- package/lib/esm/pnp.js +161 -116
- package/package.json +7 -7
package/lib/esm/index.js
CHANGED
|
@@ -1,238 +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 {
|
|
5
|
-
|
|
5
|
+
import { readJSON, resolve } from "@gjsify/utils";
|
|
6
|
+
|
|
7
|
+
//#region src/index.ts
|
|
6
8
|
const builtinModules = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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"
|
|
48
50
|
];
|
|
49
51
|
function isBuiltin(name) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const n = name.startsWith("node:") ? name.slice(5) : name;
|
|
53
|
+
const base = n.split("/")[0];
|
|
54
|
+
return builtinModules.includes(n) || builtinModules.includes(base);
|
|
53
55
|
}
|
|
56
|
+
/** Walk up from startDir to find the nearest node_modules directory. */
|
|
54
57
|
function findNodeModulesDir(startDir) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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;
|
|
64
67
|
}
|
|
68
|
+
/** Resolve symlinks for a Gio.File, returning the real path. */
|
|
65
69
|
function resolveSymlink(file) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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();
|
|
75
79
|
}
|
|
80
|
+
/** Try appending .js to an extensionless path. Returns the file if found, null otherwise. */
|
|
76
81
|
function tryJsExtension(filePath) {
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
const withJs = Gio.File.new_for_path(filePath + ".js");
|
|
83
|
+
return withJs.query_exists(null) ? withJs : null;
|
|
79
84
|
}
|
|
85
|
+
/** Check if a basename has a file extension. */
|
|
80
86
|
function hasExtension(basename) {
|
|
81
|
-
|
|
87
|
+
return basename.includes(".");
|
|
82
88
|
}
|
|
89
|
+
/** Resolve package.json main/module entry for a directory. */
|
|
83
90
|
function resolvePackageEntry(dirPath) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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;
|
|
95
102
|
}
|
|
103
|
+
/** Convert a file: URL (string or object) to an absolute path. */
|
|
96
104
|
function fileUrlToPath(filenameOrURL) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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);
|
|
108
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
|
+
*/
|
|
109
123
|
function resolveBareViaPnpFromCaller(id, callerDir) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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;
|
|
118
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
|
+
*/
|
|
119
139
|
function resolveInNodeModules(id, callerDir) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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`);
|
|
135
155
|
}
|
|
156
|
+
/** Resolve a module specifier to an absolute file path. */
|
|
136
157
|
function resolveModulePath(id, callerDir) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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;
|
|
165
186
|
}
|
|
187
|
+
/** Load a CJS .js/.cjs file using GJS's legacy imports system. */
|
|
166
188
|
function requireJsFile(filePath, cache) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
+
}
|
|
198
220
|
}
|
|
199
221
|
function createRequire(filenameOrURL) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
req.cache = cache;
|
|
228
|
-
req.extensions = /* @__PURE__ */ Object.create(null);
|
|
229
|
-
req.main = void 0;
|
|
230
|
-
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;
|
|
231
249
|
}
|
|
232
|
-
var
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
index_default as default,
|
|
237
|
-
isBuiltin
|
|
250
|
+
var src_default = {
|
|
251
|
+
builtinModules,
|
|
252
|
+
isBuiltin,
|
|
253
|
+
createRequire
|
|
238
254
|
};
|
|
255
|
+
|
|
256
|
+
//#endregion
|
|
257
|
+
export { builtinModules, createRequire, src_default as default, isBuiltin };
|
package/lib/esm/pnp.js
CHANGED
|
@@ -1,134 +1,179 @@
|
|
|
1
1
|
import Gio from "@girs/gio-2.0";
|
|
2
2
|
import GLib from "@girs/glib-2.0";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
//#region src/pnp.ts
|
|
5
|
+
const manifestCache = new Map();
|
|
6
|
+
/** Walk up from `startDir` looking for the nearest `.pnp.cjs`. */
|
|
4
7
|
function findPnpManifest(startDir) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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;
|
|
12
15
|
}
|
|
16
|
+
/** Read + parse `.pnp.cjs`'s `RAW_RUNTIME_STATE`. Cached. */
|
|
13
17
|
function loadPnpManifest(pnpCjsPath) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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;
|
|
50
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
|
+
*/
|
|
51
66
|
function extractRawRuntimeState(text) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
+
}
|
|
76
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
|
+
*/
|
|
77
99
|
function findPackageOwning(manifest, absolutePath) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
};
|
|
91
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
|
+
*/
|
|
92
124
|
function resolveBareViaPnp(manifest, id, callerPath) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const finalFile = subPath ? baseFile.resolve_relative_path(subPath) : baseFile;
|
|
106
|
-
return finalFile.get_path();
|
|
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();
|
|
107
137
|
}
|
|
138
|
+
/** Split `@scope/foo/sub/path` into `pkgName: '@scope/foo'`, `subPath: 'sub/path'`. */
|
|
108
139
|
function splitSpecifier(id) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
+
};
|
|
119
165
|
}
|
|
120
166
|
function stripLeadingDotSlash(p) {
|
|
121
|
-
|
|
167
|
+
return p.startsWith("./") ? p.slice(2) : p;
|
|
122
168
|
}
|
|
169
|
+
/** Returns `<absolutePath>` minus `<rootDir>/`, with a trailing slash on
|
|
170
|
+
* directory components, matching Yarn's `packageLocation` keys. */
|
|
123
171
|
function relativeFromRoot(rootDir, absolutePath) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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);
|
|
128
176
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
loadPnpManifest,
|
|
133
|
-
resolveBareViaPnp
|
|
134
|
-
};
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
export { findPackageOwning, findPnpManifest, loadPnpManifest, resolveBareViaPnp };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/module",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Node.js module module for Gjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
"module"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@girs/gio-2.0": "
|
|
34
|
-
"@girs/gjs": "
|
|
35
|
-
"@girs/glib-2.0": "
|
|
36
|
-
"@gjsify/utils": "^0.3.
|
|
33
|
+
"@girs/gio-2.0": "2.88.0-4.0.0-rc.9",
|
|
34
|
+
"@girs/gjs": "4.0.0-rc.9",
|
|
35
|
+
"@girs/glib-2.0": "2.88.0-4.0.0-rc.9",
|
|
36
|
+
"@gjsify/utils": "^0.3.14"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@gjsify/cli": "^0.3.
|
|
40
|
-
"@gjsify/unit": "^0.3.
|
|
39
|
+
"@gjsify/cli": "^0.3.14",
|
|
40
|
+
"@gjsify/unit": "^0.3.14",
|
|
41
41
|
"@types/node": "^25.6.0",
|
|
42
42
|
"typescript": "^6.0.3"
|
|
43
43
|
}
|