@gjsify/module 0.4.0 → 0.4.4
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/package.json +45 -42
- package/src/index.spec.ts +0 -386
- package/src/index.ts +0 -453
- package/src/pnp.ts +0 -286
- package/src/test.mts +0 -5
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
package/src/index.ts
DELETED
|
@@ -1,453 +0,0 @@
|
|
|
1
|
-
// Reference: Node.js lib/module.js
|
|
2
|
-
// Reimplemented for GJS using Gio and GLib
|
|
3
|
-
// CJS loading logic adapted from @gjsify/require (c) Andrea Giammarchi - ISC
|
|
4
|
-
|
|
5
|
-
import '@girs/gjs';
|
|
6
|
-
import Gio from '@girs/gio-2.0';
|
|
7
|
-
import GLib from '@girs/glib-2.0';
|
|
8
|
-
import { resolve as resolvePath, readJSON } from '@gjsify/utils';
|
|
9
|
-
import { findPnpManifest, loadPnpManifest, resolveBareViaPnp } from './pnp.js';
|
|
10
|
-
|
|
11
|
-
export const builtinModules = [
|
|
12
|
-
'assert',
|
|
13
|
-
'async_hooks',
|
|
14
|
-
'buffer',
|
|
15
|
-
'child_process',
|
|
16
|
-
'cluster',
|
|
17
|
-
'console',
|
|
18
|
-
'constants',
|
|
19
|
-
'crypto',
|
|
20
|
-
'dgram',
|
|
21
|
-
'diagnostics_channel',
|
|
22
|
-
'dns',
|
|
23
|
-
'domain',
|
|
24
|
-
'events',
|
|
25
|
-
'fs',
|
|
26
|
-
'http',
|
|
27
|
-
'http2',
|
|
28
|
-
'https',
|
|
29
|
-
'inspector',
|
|
30
|
-
'module',
|
|
31
|
-
'net',
|
|
32
|
-
'os',
|
|
33
|
-
'path',
|
|
34
|
-
'perf_hooks',
|
|
35
|
-
'process',
|
|
36
|
-
'punycode',
|
|
37
|
-
'querystring',
|
|
38
|
-
'readline',
|
|
39
|
-
'repl',
|
|
40
|
-
'stream',
|
|
41
|
-
'string_decoder',
|
|
42
|
-
'sys',
|
|
43
|
-
'timers',
|
|
44
|
-
'tls',
|
|
45
|
-
'tty',
|
|
46
|
-
'url',
|
|
47
|
-
'util',
|
|
48
|
-
'v8',
|
|
49
|
-
'vm',
|
|
50
|
-
'wasi',
|
|
51
|
-
'worker_threads',
|
|
52
|
-
'zlib',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
export function isBuiltin(name: string): boolean {
|
|
56
|
-
const n = name.startsWith('node:') ? name.slice(5) : name;
|
|
57
|
-
const base = n.split('/')[0];
|
|
58
|
-
return builtinModules.includes(n) || builtinModules.includes(base);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// --- Private helpers for createRequire ---
|
|
62
|
-
// Resolution logic ported from @gjsify/require, cleaned up for ESM-only use
|
|
63
|
-
|
|
64
|
-
/** Walk up from startDir to find the nearest node_modules directory. */
|
|
65
|
-
function findNodeModulesDir(startDir: string): string | null {
|
|
66
|
-
let dir = Gio.File.new_for_path(startDir);
|
|
67
|
-
while (dir.has_parent(null)) {
|
|
68
|
-
const nodeModules = dir.resolve_relative_path('node_modules');
|
|
69
|
-
if (nodeModules.query_exists(null)) {
|
|
70
|
-
return nodeModules.get_path();
|
|
71
|
-
}
|
|
72
|
-
dir = dir.get_parent()!;
|
|
73
|
-
}
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Resolve symlinks for a Gio.File, returning the real path. */
|
|
78
|
-
function resolveSymlink(file: Gio.File): string {
|
|
79
|
-
const info = file.query_info('standard::', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
|
|
80
|
-
if (info.get_is_symlink()) {
|
|
81
|
-
const target = info.get_symlink_target();
|
|
82
|
-
const parent = file.get_parent();
|
|
83
|
-
if (target && parent) {
|
|
84
|
-
return parent.resolve_relative_path(target).get_path()!;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return file.get_path()!;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Try appending .js to an extensionless path. Returns the file if found, null otherwise. */
|
|
91
|
-
function tryJsExtension(filePath: string): Gio.File | null {
|
|
92
|
-
const withJs = Gio.File.new_for_path(filePath + '.js');
|
|
93
|
-
return withJs.query_exists(null) ? withJs : null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Check if a basename has a file extension. */
|
|
97
|
-
function hasExtension(basename: string): boolean {
|
|
98
|
-
return basename.includes('.');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Resolve package.json main/module entry for a directory. */
|
|
102
|
-
function resolvePackageEntry(dirPath: string): string | null {
|
|
103
|
-
const pkgJsonFile = resolvePath(dirPath, 'package.json');
|
|
104
|
-
if (!pkgJsonFile.query_exists(null)) return null;
|
|
105
|
-
|
|
106
|
-
const pkg = readJSON(pkgJsonFile.get_path()!) as Record<string, unknown>;
|
|
107
|
-
|
|
108
|
-
// `exports` map first — when present, it MUST be the authoritative
|
|
109
|
-
// resolution for the root entry. The legacy `main`/`module` fields are
|
|
110
|
-
// only consulted as a fallback (or when no exports map exists).
|
|
111
|
-
if (pkg['exports'] !== undefined) {
|
|
112
|
-
const resolvedFromExports = resolveExportsMap(pkg['exports'], '.', DEFAULT_EXPORT_CONDITIONS);
|
|
113
|
-
if (resolvedFromExports) {
|
|
114
|
-
const resolved = resolvePath(dirPath, resolvedFromExports);
|
|
115
|
-
if (resolved.query_exists(null)) return resolved.get_path()!;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const main = (pkg['main'] as string | undefined) || (pkg['module'] as string | undefined) || 'index.js';
|
|
120
|
-
const entryFile = resolvePath(dirPath, main);
|
|
121
|
-
|
|
122
|
-
if (entryFile.query_exists(null)) return entryFile.get_path()!;
|
|
123
|
-
|
|
124
|
-
// Try .js extension fallback for extensionless main field
|
|
125
|
-
if (!hasExtension(main)) {
|
|
126
|
-
const withJs = tryJsExtension(entryFile.get_path()!);
|
|
127
|
-
if (withJs) return withJs.get_path()!;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Resolve a `pkg.exports`-map entry into a relative file path.
|
|
135
|
-
*
|
|
136
|
-
* Implements a useful subset of the Node ESM Package Exports spec
|
|
137
|
-
* (https://nodejs.org/api/packages.html#package-entry-points):
|
|
138
|
-
* - shorthand string form (`"exports": "./lib/index.js"`)
|
|
139
|
-
* - object form with `.` + `./<subpath>` keys
|
|
140
|
-
* - conditional resolution against `conditions` in order (first match wins)
|
|
141
|
-
* - `null` value → block (return undefined, caller treats as not-exported)
|
|
142
|
-
*
|
|
143
|
-
* Pattern wildcards (`./shims/*`) and subpath imports (`#internal`) are
|
|
144
|
-
* out of scope for now — the surface we need right now is fixed-key
|
|
145
|
-
* subpaths like `./shims/console-gjs`. Each gap surfaces a clear caller
|
|
146
|
-
* error rather than silent miss.
|
|
147
|
-
*
|
|
148
|
-
* @param exportsField Raw value of `pkg.exports`.
|
|
149
|
-
* @param subpath The requested subpath, including the leading `./`
|
|
150
|
-
* (so `.` for root, `./foo` for `pkg/foo`).
|
|
151
|
-
* @param conditions Ordered condition list — first one that has a value
|
|
152
|
-
* wins. Standard order: `["node", "import", "default"]` for GJS.
|
|
153
|
-
*/
|
|
154
|
-
function resolveExportsMap(
|
|
155
|
-
exportsField: unknown,
|
|
156
|
-
subpath: string,
|
|
157
|
-
conditions: readonly string[],
|
|
158
|
-
): string | undefined {
|
|
159
|
-
if (typeof exportsField === 'string') {
|
|
160
|
-
// Shorthand: `"exports": "./lib/index.js"` applies to `.` only.
|
|
161
|
-
return subpath === '.' ? exportsField : undefined;
|
|
162
|
-
}
|
|
163
|
-
if (exportsField === null || typeof exportsField !== 'object') return undefined;
|
|
164
|
-
const map = exportsField as Record<string, unknown>;
|
|
165
|
-
|
|
166
|
-
// If the map has no `.`-prefixed keys, it's purely a condition map
|
|
167
|
-
// applying to root (`.`) — wrap it under `.` for the lookup.
|
|
168
|
-
const hasSubpathKeys = Object.keys(map).some((k) => k === '.' || k.startsWith('./'));
|
|
169
|
-
const lookup = hasSubpathKeys ? map[subpath] : (subpath === '.' ? map : undefined);
|
|
170
|
-
if (lookup === undefined || lookup === null) return undefined;
|
|
171
|
-
|
|
172
|
-
return pickConditionalValue(lookup, conditions);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Walk a conditional-export node, picking the first matching condition.
|
|
177
|
-
* `lookup` is either a leaf (string/null) or a nested condition map.
|
|
178
|
-
*/
|
|
179
|
-
function pickConditionalValue(lookup: unknown, conditions: readonly string[]): string | undefined {
|
|
180
|
-
if (typeof lookup === 'string') return lookup;
|
|
181
|
-
if (lookup === null || typeof lookup !== 'object') return undefined;
|
|
182
|
-
const map = lookup as Record<string, unknown>;
|
|
183
|
-
// `default` always wins as the final fallback inside the conditions
|
|
184
|
-
// we recognize, but other conditions take precedence in the order the
|
|
185
|
-
// caller provides them (e.g. `node` before `import`).
|
|
186
|
-
for (const cond of conditions) {
|
|
187
|
-
if (cond in map) {
|
|
188
|
-
const resolved = pickConditionalValue(map[cond], conditions);
|
|
189
|
-
if (resolved !== undefined) return resolved;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if ('default' in map && !conditions.includes('default')) {
|
|
193
|
-
return pickConditionalValue(map['default'], conditions);
|
|
194
|
-
}
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const DEFAULT_EXPORT_CONDITIONS: readonly string[] = ['node', 'import', 'default'];
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Extract `<pkgName>` and `<subpath>` from a bare specifier:
|
|
202
|
-
* `lodash` → { pkg: 'lodash', subpath: '.' }
|
|
203
|
-
* `@scope/foo` → { pkg: '@scope/foo', subpath: '.' }
|
|
204
|
-
* `@scope/foo/bar` → { pkg: '@scope/foo', subpath: './bar' }
|
|
205
|
-
* `lodash/fp` → { pkg: 'lodash', subpath: './fp' }
|
|
206
|
-
*/
|
|
207
|
-
function splitBareSpecifier(id: string): { pkg: string; subpath: string } {
|
|
208
|
-
if (id.startsWith('@')) {
|
|
209
|
-
const slash1 = id.indexOf('/');
|
|
210
|
-
if (slash1 === -1) return { pkg: id, subpath: '.' };
|
|
211
|
-
const slash2 = id.indexOf('/', slash1 + 1);
|
|
212
|
-
if (slash2 === -1) return { pkg: id, subpath: '.' };
|
|
213
|
-
return { pkg: id.slice(0, slash2), subpath: '.' + id.slice(slash2) };
|
|
214
|
-
}
|
|
215
|
-
const slash = id.indexOf('/');
|
|
216
|
-
if (slash === -1) return { pkg: id, subpath: '.' };
|
|
217
|
-
return { pkg: id.slice(0, slash), subpath: '.' + id.slice(slash) };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Convert a file: URL (string or object) to an absolute path. */
|
|
221
|
-
function fileUrlToPath(filenameOrURL: string | URL): string {
|
|
222
|
-
// Duck-type URL objects (avoids dependency on global URL which may not exist in GJS)
|
|
223
|
-
if (typeof filenameOrURL === 'object' && filenameOrURL !== null && 'href' in filenameOrURL) {
|
|
224
|
-
const urlObj = filenameOrURL as { href: string; protocol?: string };
|
|
225
|
-
if (urlObj.protocol && urlObj.protocol !== 'file:') {
|
|
226
|
-
throw new TypeError('The URL must use the file: protocol');
|
|
227
|
-
}
|
|
228
|
-
return GLib.filename_from_uri(urlObj.href)[0];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (typeof filenameOrURL === 'string' && filenameOrURL.startsWith('file:')) {
|
|
232
|
-
return GLib.filename_from_uri(filenameOrURL)[0];
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return String(filenameOrURL);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Try resolving a bare specifier through a Yarn PnP manifest (`.pnp.cjs`)
|
|
240
|
-
* sitting above `callerDir`. Returns null when no manifest is found, or when
|
|
241
|
-
* PnP can't resolve the request (e.g. the dep isn't listed in the caller
|
|
242
|
-
* package's `packageDependencies`). Callers fall back to the node_modules walk.
|
|
243
|
-
*/
|
|
244
|
-
function resolveBareViaPnpFromCaller(id: string, callerDir: string): Gio.File | null {
|
|
245
|
-
const pnpPath = findPnpManifest(callerDir);
|
|
246
|
-
if (!pnpPath) return null;
|
|
247
|
-
const manifest = loadPnpManifest(pnpPath);
|
|
248
|
-
if (!manifest) return null;
|
|
249
|
-
const resolved = resolveBareViaPnp(manifest, id, callerDir);
|
|
250
|
-
if (!resolved) return null;
|
|
251
|
-
const file = Gio.File.new_for_path(resolved);
|
|
252
|
-
return file.query_exists(null) ? file : null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Resolve a bare package specifier by walking ALL ancestor node_modules dirs.
|
|
257
|
-
* Mirrors Node.js module resolution: try nearest node_modules first, then each
|
|
258
|
-
* ancestor — so a package in a parent node_modules is found even when a closer
|
|
259
|
-
* node_modules exists but doesn't contain the requested package.
|
|
260
|
-
*/
|
|
261
|
-
function resolveInNodeModules(id: string, callerDir: string): Gio.File {
|
|
262
|
-
const { pkg: pkgName, subpath } = splitBareSpecifier(id);
|
|
263
|
-
let dir = Gio.File.new_for_path(callerDir);
|
|
264
|
-
while (dir.has_parent(null)) {
|
|
265
|
-
const nodeModulesFile = dir.resolve_relative_path('node_modules');
|
|
266
|
-
if (nodeModulesFile.query_exists(null)) {
|
|
267
|
-
const pkgDir = nodeModulesFile.resolve_relative_path(pkgName);
|
|
268
|
-
if (pkgDir.query_exists(null)) {
|
|
269
|
-
// Try the package's `exports` map for the requested subpath
|
|
270
|
-
// (including `.` for the package root). The map is authoritative
|
|
271
|
-
// when present — Node would only fall back to literal-path or
|
|
272
|
-
// `main`/`module` when no `exports` exists.
|
|
273
|
-
const pkgJson = pkgDir.resolve_relative_path('package.json');
|
|
274
|
-
if (pkgJson.query_exists(null)) {
|
|
275
|
-
const manifest = readJSON(pkgJson.get_path()!) as Record<string, unknown>;
|
|
276
|
-
if (manifest['exports'] !== undefined) {
|
|
277
|
-
const relative = resolveExportsMap(manifest['exports'], subpath, DEFAULT_EXPORT_CONDITIONS);
|
|
278
|
-
if (relative !== undefined) {
|
|
279
|
-
const target = pkgDir.resolve_relative_path(relative);
|
|
280
|
-
if (target.query_exists(null)) return target;
|
|
281
|
-
// fall through to literal-path on miss — keeps the
|
|
282
|
-
// behavior conservative when an exports entry points at
|
|
283
|
-
// a non-existent file
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
// Literal-path fallback (subpath used as plain relative path
|
|
288
|
-
// under the package dir, or `.` resolved to the dir itself).
|
|
289
|
-
const candidate = subpath === '.' ? pkgDir : pkgDir.resolve_relative_path(subpath.slice(2));
|
|
290
|
-
if (candidate.query_exists(null)) return candidate;
|
|
291
|
-
const bn = candidate.get_basename();
|
|
292
|
-
if (bn && !hasExtension(bn)) {
|
|
293
|
-
const withJs = tryJsExtension(candidate.get_path()!);
|
|
294
|
-
if (withJs) return withJs;
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
// Original behavior for the no-pkgDir case — literal `node_modules/id`
|
|
298
|
-
// walk, handles edge cases like single-file shims at the root.
|
|
299
|
-
const candidate = nodeModulesFile.resolve_relative_path(id);
|
|
300
|
-
if (candidate.query_exists(null)) return candidate;
|
|
301
|
-
const bn = candidate.get_basename();
|
|
302
|
-
if (bn && !hasExtension(bn)) {
|
|
303
|
-
const withJs = tryJsExtension(candidate.get_path()!);
|
|
304
|
-
if (withJs) return withJs;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
dir = dir.get_parent()!;
|
|
309
|
-
}
|
|
310
|
-
throw new Error(`Cannot find module "${id}" - not found in any node_modules directory`);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/** Resolve a module specifier to an absolute file path. */
|
|
314
|
-
function resolveModulePath(id: string, callerDir: string): string {
|
|
315
|
-
if (isBuiltin(id)) return id;
|
|
316
|
-
|
|
317
|
-
let file: Gio.File;
|
|
318
|
-
|
|
319
|
-
if (id.startsWith('/')) {
|
|
320
|
-
file = resolvePath(id);
|
|
321
|
-
} else if (id.startsWith('.')) {
|
|
322
|
-
file = resolvePath(callerDir, id);
|
|
323
|
-
} else {
|
|
324
|
-
// Bare specifier: try Yarn PnP first (for PnP-built workspaces where no
|
|
325
|
-
// node_modules/ tree exists on disk), then fall back to the standard
|
|
326
|
-
// node_modules walk.
|
|
327
|
-
const pnpFile = resolveBareViaPnpFromCaller(id, callerDir);
|
|
328
|
-
file = pnpFile ?? resolveInNodeModules(id, callerDir);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Extension fallback for absolute/relative paths (bare specifiers handled in resolveInNodeModules)
|
|
332
|
-
if (id.startsWith('/') || id.startsWith('.')) {
|
|
333
|
-
if (!file.query_exists(null)) {
|
|
334
|
-
const basename = file.get_basename();
|
|
335
|
-
if (basename && !hasExtension(basename)) {
|
|
336
|
-
file = tryJsExtension(file.get_path()!) ?? file;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (!file.query_exists(null)) {
|
|
341
|
-
throw new Error(`Cannot find module "${id}"`);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const resolvedPath = resolveSymlink(file);
|
|
346
|
-
|
|
347
|
-
// Directory → resolve via package.json main field
|
|
348
|
-
const basename = file.get_basename();
|
|
349
|
-
if (basename && !hasExtension(basename)) {
|
|
350
|
-
const entry = resolvePackageEntry(resolvedPath);
|
|
351
|
-
if (entry) return entry;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return resolvedPath;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// --- CJS file loading via GJS imports system ---
|
|
358
|
-
// Ported from @gjsify/require (c) Andrea Giammarchi - ISC
|
|
359
|
-
|
|
360
|
-
/** Load a CJS .js/.cjs file using GJS's legacy imports system. */
|
|
361
|
-
function requireJsFile(filePath: string, cache: Record<string, unknown>): unknown {
|
|
362
|
-
if (filePath in cache) return cache[filePath];
|
|
363
|
-
|
|
364
|
-
let file = Gio.File.new_for_path(filePath);
|
|
365
|
-
const dir = file.get_parent()!.get_path()!;
|
|
366
|
-
let basename = file.get_basename()!;
|
|
367
|
-
|
|
368
|
-
if (basename.endsWith('.mjs')) {
|
|
369
|
-
throw new Error(`Cannot require .mjs files. Use import instead. Path: "${filePath}"`);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// GJS can't import .cjs files — copy to .js as workaround
|
|
373
|
-
if (basename.endsWith('.cjs')) {
|
|
374
|
-
const dest = resolvePath(dir, '__gjsify__' + basename.replace(/\.cjs$/, '.js'));
|
|
375
|
-
if (dest.query_exists(null)) dest.delete(null);
|
|
376
|
-
file.copy(dest, Gio.FileCopyFlags.NONE, null, null);
|
|
377
|
-
file = dest;
|
|
378
|
-
basename = file.get_basename()!;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Save and replace global CJS state
|
|
382
|
-
const savedExports = globalThis.exports;
|
|
383
|
-
const savedModule = globalThis.module;
|
|
384
|
-
const moduleObj = { exports: {} };
|
|
385
|
-
globalThis.exports = moduleObj.exports;
|
|
386
|
-
globalThis.module = moduleObj as NodeModule;
|
|
387
|
-
|
|
388
|
-
try {
|
|
389
|
-
// Evaluate the file via GJS imports system
|
|
390
|
-
const { searchPath } = imports;
|
|
391
|
-
searchPath.unshift(dir);
|
|
392
|
-
imports[basename.replace(/\.(js|cjs)$/, '')];
|
|
393
|
-
searchPath.shift();
|
|
394
|
-
|
|
395
|
-
const result = moduleObj.exports;
|
|
396
|
-
cache[filePath] = result;
|
|
397
|
-
return result;
|
|
398
|
-
} finally {
|
|
399
|
-
// Always restore global state, even on error
|
|
400
|
-
globalThis.exports = savedExports;
|
|
401
|
-
globalThis.module = savedModule;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
export function createRequire(filenameOrURL: string | URL): NodeRequire {
|
|
406
|
-
const filename = fileUrlToPath(filenameOrURL);
|
|
407
|
-
|
|
408
|
-
if (!filename.startsWith('/')) {
|
|
409
|
-
throw new TypeError(
|
|
410
|
-
'The argument must be a file URL object, file URL string, or absolute path string. ' +
|
|
411
|
-
`Received "${String(filenameOrURL)}"`
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const callerDir = GLib.path_get_dirname(filename);
|
|
416
|
-
const cache: Record<string, unknown> = Object.create(null);
|
|
417
|
-
|
|
418
|
-
const req = function require(id: string): unknown {
|
|
419
|
-
const resolved = resolveModulePath(id, callerDir);
|
|
420
|
-
if (resolved in cache) return cache[resolved];
|
|
421
|
-
|
|
422
|
-
// JSON files
|
|
423
|
-
if (resolved.endsWith('.json')) {
|
|
424
|
-
const result = readJSON(resolved);
|
|
425
|
-
cache[resolved] = result;
|
|
426
|
-
return result;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Builtin modules can't be required synchronously in ESM
|
|
430
|
-
if (isBuiltin(id)) {
|
|
431
|
-
throw new Error(
|
|
432
|
-
`createRequire: Cannot require builtin module "${id}" synchronously in GJS. ` +
|
|
433
|
-
'Use import instead.'
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// .js/.cjs files via GJS imports system
|
|
438
|
-
return requireJsFile(resolved, cache);
|
|
439
|
-
} as NodeRequire;
|
|
440
|
-
|
|
441
|
-
req.resolve = function resolve(id: string): string {
|
|
442
|
-
return resolveModulePath(id, callerDir);
|
|
443
|
-
} as NodeRequire['resolve'];
|
|
444
|
-
|
|
445
|
-
req.resolve.paths = (_request: string): string[] | null => null;
|
|
446
|
-
req.cache = cache as NodeRequire['cache'];
|
|
447
|
-
req.extensions = Object.create(null) as NodeRequire['extensions'];
|
|
448
|
-
req.main = undefined;
|
|
449
|
-
|
|
450
|
-
return req;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
export default { builtinModules, isBuiltin, createRequire };
|