xbintsc 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/README.md +21 -3
- package/README.zh-CN.md +18 -3
- package/dist/src/codegen/generator/calls/access.js +13 -0
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +8 -2
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +23 -0
- package/dist/src/codegen/generator/context.js +91 -17
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +5 -0
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +7 -0
- package/dist/src/driver/compiler.js +41 -5
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +1 -1
- package/dist/src/driver/modules.js +235 -25
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/extensions/node/fs/fs-ops.d.ts +5 -2
- package/dist/src/extensions/node/fs/fs-ops.js +45 -6
- package/dist/src/extensions/node/fs/fs-ops.js.map +1 -1
- package/dist/src/extensions/node/fs/index.d.ts +6 -2
- package/dist/src/extensions/node/fs/index.js +22 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs-promises/index.d.ts +4 -3
- package/dist/src/extensions/node/fs-promises/index.js +31 -3
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
- package/dist/tests/cli/main.test.js +17 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +93 -2
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/modules.test.js +91 -4
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +240 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +12 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/harness.js +11 -8
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/e2e/node-modules.test.js +1 -1
- package/dist/tests/e2e/node-modules.test.js.map +1 -1
- package/dist/tests/e2e/npm-modules.test.d.ts +9 -0
- package/dist/tests/e2e/npm-modules.test.js +55 -0
- package/dist/tests/e2e/npm-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage.test.d.ts +11 -0
- package/dist/tests/e2e/runtime-coverage.test.js +646 -0
- package/dist/tests/e2e/runtime-coverage.test.js.map +1 -0
- package/package.json +3 -2
- package/runtime/ext_node/buffer/parts/prototype.inc +7 -0
- package/runtime/ext_node/buffer/parts/search.inc +8 -4
- package/runtime/ext_node/child_process/child_process.c +5 -0
- package/runtime/ext_node/fs/constants.c +113 -0
- package/runtime/ext_node/fs/copy_ops.c +145 -0
- package/runtime/ext_node/fs/dir.c +101 -0
- package/runtime/ext_node/fs/fd_ops.c +405 -0
- package/runtime/ext_node/fs/fs_common.h +388 -5
- package/runtime/ext_node/fs/fs_ops.c +100 -139
- package/runtime/ext_node/fs/glob.c +232 -0
- package/runtime/ext_node/fs/link_ops.c +91 -0
- package/runtime/ext_node/fs/meta_ops.c +403 -0
- package/runtime/ext_node/fs/promises.c +275 -60
- package/runtime/ext_node/fs/read_file.c +8 -4
- package/runtime/ext_node/fs/watch.c +52 -0
- package/runtime/ext_node/fs/write_file.c +20 -11
- package/runtime/ext_node/stream/stream.c +19 -2
- package/runtime/rt.h +9 -0
- package/runtime/xt_io.c +21 -0
- package/scripts/coverage-runtime.ts +338 -0
- package/src/codegen/generator/calls/access.ts +15 -0
- package/src/codegen/generator/calls/invocation.ts +8 -2
- package/src/codegen/generator/context.ts +112 -21
- package/src/codegen/generator/expressions/primary.ts +5 -0
- package/src/driver/compiler.ts +42 -5
- package/src/driver/modules.ts +251 -26
- package/src/extensions/node/fs/fs-ops.ts +48 -6
- package/src/extensions/node/fs/index.ts +25 -3
- package/src/extensions/node/fs-promises/index.ts +33 -4
package/src/driver/modules.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
16
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
16
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
17
17
|
import {
|
|
18
18
|
ModifierKind,
|
|
19
19
|
SyntaxKind,
|
|
@@ -55,27 +55,50 @@ export interface BundleResult {
|
|
|
55
55
|
readonly moduleCount: number;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".mts", ".cts", "/index.ts", "/index.tsx"];
|
|
59
|
-
|
|
60
58
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* Source extensions probed for an extension-less specifier. TypeScript suffixes
|
|
60
|
+
* are tried before their JavaScript counterparts so a project that ships both
|
|
61
|
+
* `foo.ts` and a compiled `foo.js` prefers the source it is compiled from.
|
|
64
62
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
64
|
+
/** File extensions a directory `index` entry may use. */
|
|
65
|
+
const INDEX_SUFFIXES = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
66
|
+
/** Conditions consulted, in order, when reading a package `exports` map. */
|
|
67
|
+
const EXPORT_CONDITIONS = ["import", "module", "default", "node", "require"];
|
|
68
|
+
|
|
69
|
+
/** A specifier is a file path (`./x`, `../x`, `/x`) rather than a package. */
|
|
70
|
+
function isRelativeSpecifier(specifier: string): boolean {
|
|
71
|
+
return specifier.startsWith(".") || isAbsolute(specifier);
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a path on disk, tolerating TypeScript's `.js` import convention
|
|
76
|
+
* (`import "./foo.js"` pointing at `foo.ts`) and Node's directory/`index`
|
|
77
|
+
* conventions.
|
|
78
|
+
*/
|
|
79
|
+
function resolvePath(base: string): string | undefined {
|
|
80
|
+
// A JavaScript specifier may refer to a TypeScript source that will emit it
|
|
81
|
+
// (`import "./foo.js"` pointing at `foo.ts`). Probe the matching TypeScript
|
|
82
|
+
// extensions after the literal path: `.js`/`.jsx` -> `.ts`/`.tsx`,
|
|
83
|
+
// `.mjs` -> `.mts`, `.cjs` -> `.cts`.
|
|
74
84
|
const candidates = [base];
|
|
75
|
-
const jsExtension = /\.(?:m|c)?jsx
|
|
85
|
+
const jsExtension = /\.((?:m|c)?jsx?)$/.exec(base);
|
|
76
86
|
if (jsExtension) {
|
|
77
87
|
const stem = base.slice(0, jsExtension.index);
|
|
78
|
-
|
|
88
|
+
switch (jsExtension[1]) {
|
|
89
|
+
case "jsx":
|
|
90
|
+
candidates.push(`${stem}.tsx`, `${stem}.ts`);
|
|
91
|
+
break;
|
|
92
|
+
case "mjs":
|
|
93
|
+
candidates.push(`${stem}.mts`);
|
|
94
|
+
break;
|
|
95
|
+
case "cjs":
|
|
96
|
+
candidates.push(`${stem}.cts`);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
candidates.push(`${stem}.ts`, `${stem}.tsx`);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
79
102
|
}
|
|
80
103
|
for (const candidate of candidates) {
|
|
81
104
|
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
|
|
@@ -84,9 +107,197 @@ function resolveModule(fromDir: string, specifier: string): string | undefined {
|
|
|
84
107
|
const candidate = base + suffix;
|
|
85
108
|
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
|
|
86
109
|
}
|
|
110
|
+
if (existsSync(base) && statSync(base).isDirectory()) return resolveDirectory(base);
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Resolve a directory to its package entry or an `index` file. */
|
|
115
|
+
function resolveDirectory(directory: string): string | undefined {
|
|
116
|
+
const pkg = readPackageJson(directory);
|
|
117
|
+
if (pkg && typeof pkg.main === "string") {
|
|
118
|
+
const main = resolvePath(resolve(directory, pkg.main));
|
|
119
|
+
if (main) return main;
|
|
120
|
+
}
|
|
121
|
+
for (const suffix of INDEX_SUFFIXES) {
|
|
122
|
+
const candidate = join(directory, `index${suffix}`);
|
|
123
|
+
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveModule(fromDir: string, specifier: string): string | undefined {
|
|
129
|
+
return resolvePath(resolve(fromDir, specifier));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface PackageJson {
|
|
133
|
+
readonly main?: string;
|
|
134
|
+
readonly module?: string;
|
|
135
|
+
readonly exports?: unknown;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function readPackageJson(directory: string): PackageJson | undefined {
|
|
139
|
+
const file = join(directory, "package.json");
|
|
140
|
+
if (!existsSync(file) || !statSync(file).isFile()) return undefined;
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(readFileSync(file, "utf8")) as unknown;
|
|
143
|
+
return parsed && typeof parsed === "object" ? (parsed as PackageJson) : undefined;
|
|
144
|
+
} catch {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Split `pkg/sub/path` or `@scope/pkg/sub/path` into package name and subpath. */
|
|
150
|
+
function splitPackageSpecifier(specifier: string): { packageName: string; subpath: string } {
|
|
151
|
+
const parts = specifier.split("/");
|
|
152
|
+
if (specifier.startsWith("@")) {
|
|
153
|
+
return { packageName: parts.slice(0, 2).join("/"), subpath: parts.slice(2).join("/") };
|
|
154
|
+
}
|
|
155
|
+
return { packageName: parts[0] ?? specifier, subpath: parts.slice(1).join("/") };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve a bare specifier against the `node_modules` directories above
|
|
160
|
+
* `fromDir`, following a package's `exports` map and falling back to
|
|
161
|
+
* `module`/`main`/`index`. Packages that only ship CommonJS resolve here too;
|
|
162
|
+
* their `require` calls are then rejected during code generation.
|
|
163
|
+
*/
|
|
164
|
+
function resolveNodePackage(fromDir: string, specifier: string): string | undefined {
|
|
165
|
+
const { packageName, subpath } = splitPackageSpecifier(specifier);
|
|
166
|
+
if (!packageName || packageName.startsWith(".")) return undefined;
|
|
167
|
+
let directory = fromDir;
|
|
168
|
+
for (;;) {
|
|
169
|
+
const packageDir = join(directory, "node_modules", packageName);
|
|
170
|
+
if (existsSync(packageDir) && statSync(packageDir).isDirectory()) {
|
|
171
|
+
const resolved = subpath
|
|
172
|
+
? resolvePackageSubpath(packageDir, subpath)
|
|
173
|
+
: resolvePackageEntry(packageDir);
|
|
174
|
+
if (resolved) return resolved;
|
|
175
|
+
}
|
|
176
|
+
const parent = dirname(directory);
|
|
177
|
+
if (parent === directory) return undefined;
|
|
178
|
+
directory = parent;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function resolvePackageEntry(packageDir: string): string | undefined {
|
|
183
|
+
const pkg = readPackageJson(packageDir);
|
|
184
|
+
if (pkg) {
|
|
185
|
+
const exported = resolveExportTarget(subpathValue(pkg.exports, "."));
|
|
186
|
+
if (exported) {
|
|
187
|
+
const resolved = resolvePath(resolve(packageDir, exported));
|
|
188
|
+
if (resolved) return resolved;
|
|
189
|
+
}
|
|
190
|
+
if (typeof pkg.module === "string") {
|
|
191
|
+
const resolved = resolvePath(resolve(packageDir, pkg.module));
|
|
192
|
+
if (resolved) return resolved;
|
|
193
|
+
}
|
|
194
|
+
if (typeof pkg.main === "string") {
|
|
195
|
+
const resolved = resolvePath(resolve(packageDir, pkg.main));
|
|
196
|
+
if (resolved) return resolved;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return resolveDirectory(packageDir);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function resolvePackageSubpath(packageDir: string, subpath: string): string | undefined {
|
|
203
|
+
const pkg = readPackageJson(packageDir);
|
|
204
|
+
const exported = resolveExportTarget(subpathValue(pkg?.exports, `./${subpath}`));
|
|
205
|
+
if (exported) {
|
|
206
|
+
const resolved = resolvePath(resolve(packageDir, exported));
|
|
207
|
+
if (resolved) return resolved;
|
|
208
|
+
}
|
|
209
|
+
return resolvePath(join(packageDir, subpath));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Pick the export target for a subpath (`"."` or `"./sub"`) from an exports map. */
|
|
213
|
+
function subpathValue(exportsField: unknown, subpath: string): unknown {
|
|
214
|
+
if (exportsField === undefined) return undefined;
|
|
215
|
+
if (typeof exportsField === "string" || Array.isArray(exportsField)) {
|
|
216
|
+
return subpath === "." ? exportsField : undefined;
|
|
217
|
+
}
|
|
218
|
+
if (!exportsField || typeof exportsField !== "object") return undefined;
|
|
219
|
+
const record = exportsField as Record<string, unknown>;
|
|
220
|
+
const keys = Object.keys(record);
|
|
221
|
+
// A map without `.`-prefixed keys is a condition map for the root export.
|
|
222
|
+
if (!keys.some((key) => key.startsWith("."))) {
|
|
223
|
+
return subpath === "." ? exportsField : undefined;
|
|
224
|
+
}
|
|
225
|
+
if (subpath in record) return record[subpath];
|
|
226
|
+
// Wildcard patterns such as `"./*": "./dist/*.js"`.
|
|
227
|
+
for (const key of keys) {
|
|
228
|
+
const star = key.indexOf("*");
|
|
229
|
+
if (star === -1) continue;
|
|
230
|
+
const prefix = key.slice(0, star);
|
|
231
|
+
const suffix = key.slice(star + 1);
|
|
232
|
+
if (subpath.startsWith(prefix) && subpath.endsWith(suffix)) {
|
|
233
|
+
const matched = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
234
|
+
return substituteStar(record[key], matched);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function substituteStar(target: unknown, matched: string): unknown {
|
|
241
|
+
if (typeof target === "string") return target.split("*").join(matched);
|
|
242
|
+
if (Array.isArray(target)) return target.map((entry) => substituteStar(entry, matched));
|
|
243
|
+
if (target && typeof target === "object") {
|
|
244
|
+
const record: Record<string, unknown> = {};
|
|
245
|
+
for (const [key, value] of Object.entries(target)) record[key] = substituteStar(value, matched);
|
|
246
|
+
return record;
|
|
247
|
+
}
|
|
248
|
+
return target;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Pick a runtime string from a (possibly conditional) exports target. */
|
|
252
|
+
function resolveExportTarget(target: unknown): string | undefined {
|
|
253
|
+
if (typeof target === "string") return target;
|
|
254
|
+
if (Array.isArray(target)) {
|
|
255
|
+
for (const entry of target) {
|
|
256
|
+
const resolved = resolveExportTarget(entry);
|
|
257
|
+
if (resolved) return resolved;
|
|
258
|
+
}
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
if (target && typeof target === "object") {
|
|
262
|
+
const record = target as Record<string, unknown>;
|
|
263
|
+
for (const condition of EXPORT_CONDITIONS) {
|
|
264
|
+
if (condition in record) {
|
|
265
|
+
const resolved = resolveExportTarget(record[condition]);
|
|
266
|
+
if (resolved) return resolved;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
87
270
|
return undefined;
|
|
88
271
|
}
|
|
89
272
|
|
|
273
|
+
type DependencyResolution =
|
|
274
|
+
| { readonly kind: "external" }
|
|
275
|
+
| { readonly kind: "file"; readonly path: string }
|
|
276
|
+
| { readonly kind: "missing" };
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Classify an import specifier. Known platform modules (extension modules and
|
|
280
|
+
* `node:` builtins) are left for code generation; relative paths and
|
|
281
|
+
* `node_modules` packages are resolved to a source file to bundle; a relative
|
|
282
|
+
* path that does not exist is an error.
|
|
283
|
+
*/
|
|
284
|
+
function classifyDependency(
|
|
285
|
+
fromDir: string,
|
|
286
|
+
specifier: string,
|
|
287
|
+
externalSpecifiers: ReadonlySet<string>,
|
|
288
|
+
): DependencyResolution {
|
|
289
|
+
if (specifier.startsWith("node:") || externalSpecifiers.has(specifier)) {
|
|
290
|
+
return { kind: "external" };
|
|
291
|
+
}
|
|
292
|
+
const resolved = isRelativeSpecifier(specifier)
|
|
293
|
+
? resolveModule(fromDir, specifier)
|
|
294
|
+
: resolveNodePackage(fromDir, specifier);
|
|
295
|
+
if (resolved) return { kind: "file", path: resolved };
|
|
296
|
+
// A bare specifier that is not a file may still be a platform module the
|
|
297
|
+
// generator knows about (e.g. when the caller passes no extension registry).
|
|
298
|
+
return isRelativeSpecifier(specifier) ? { kind: "missing" } : { kind: "external" };
|
|
299
|
+
}
|
|
300
|
+
|
|
90
301
|
function hasModifier(node: Node, kind: ModifierKind): boolean {
|
|
91
302
|
const modifiers = (node as { modifiers?: { modifierKind: ModifierKind }[] }).modifiers;
|
|
92
303
|
return !!modifiers && modifiers.some((modifier) => modifier.modifierKind === kind);
|
|
@@ -111,7 +322,11 @@ function declarationName(node: Node): Identifier | undefined {
|
|
|
111
322
|
}
|
|
112
323
|
|
|
113
324
|
/** Load, parse and bind the entry module and every module it imports. */
|
|
114
|
-
function loadGraph(
|
|
325
|
+
function loadGraph(
|
|
326
|
+
entryPath: string,
|
|
327
|
+
diagnostics: DiagnosticBag,
|
|
328
|
+
externalSpecifiers: ReadonlySet<string>,
|
|
329
|
+
): ModuleRecord[] | undefined {
|
|
115
330
|
const records = new Map<string, ModuleRecord>();
|
|
116
331
|
const order: ModuleRecord[] = [];
|
|
117
332
|
const visiting = new Set<string>();
|
|
@@ -152,9 +367,9 @@ function loadGraph(entryPath: string, diagnostics: DiagnosticBag): ModuleRecord[
|
|
|
152
367
|
for (const statement of sourceFile.statements) {
|
|
153
368
|
const specifier = moduleSpecifierOf(statement);
|
|
154
369
|
if (!specifier) continue;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (
|
|
370
|
+
const dependency = classifyDependency(dirname(path), specifier, externalSpecifiers);
|
|
371
|
+
if (dependency.kind === "external") continue;
|
|
372
|
+
if (dependency.kind === "missing") {
|
|
158
373
|
diagnostics.error(
|
|
159
374
|
DiagnosticCode.CodegenError,
|
|
160
375
|
`Cannot resolve module '${specifier}' from '${path}'`,
|
|
@@ -164,7 +379,7 @@ function loadGraph(entryPath: string, diagnostics: DiagnosticBag): ModuleRecord[
|
|
|
164
379
|
failed = true;
|
|
165
380
|
continue;
|
|
166
381
|
}
|
|
167
|
-
load(dependency);
|
|
382
|
+
load(dependency.path);
|
|
168
383
|
}
|
|
169
384
|
visiting.delete(path);
|
|
170
385
|
order.push(record);
|
|
@@ -215,14 +430,18 @@ function topLevelSymbols(record: ModuleRecord): SymbolInfo[] {
|
|
|
215
430
|
* Merge every module's statements into a single file. Dependencies come first
|
|
216
431
|
* (post-order over the import graph), the entry module last.
|
|
217
432
|
*/
|
|
218
|
-
export function bundleModules(
|
|
219
|
-
|
|
433
|
+
export function bundleModules(
|
|
434
|
+
entryPath: string,
|
|
435
|
+
diagnostics: DiagnosticBag,
|
|
436
|
+
externalSpecifiers: ReadonlySet<string> = new Set(),
|
|
437
|
+
): BundleResult | undefined {
|
|
438
|
+
const records = loadGraph(entryPath, diagnostics, externalSpecifiers);
|
|
220
439
|
if (!records) return undefined;
|
|
221
440
|
|
|
222
441
|
const byPath = new Map(records.map((record) => [record.path, record]));
|
|
223
442
|
const dependencyOf = (record: ModuleRecord, specifier: string): ModuleRecord | undefined => {
|
|
224
|
-
const
|
|
225
|
-
return
|
|
443
|
+
const resolution = classifyDependency(dirname(record.path), specifier, externalSpecifiers);
|
|
444
|
+
return resolution.kind === "file" ? byPath.get(resolution.path) : undefined;
|
|
226
445
|
};
|
|
227
446
|
|
|
228
447
|
// Phase 1: record every top-level binding and pick a unique final name.
|
|
@@ -328,8 +547,14 @@ export function bundleModules(entryPath: string, diagnostics: DiagnosticBag): Bu
|
|
|
328
547
|
for (const record of records) {
|
|
329
548
|
for (const statement of record.sourceFile.statements) {
|
|
330
549
|
if (statement.kind === SyntaxKind.ImportDeclaration) {
|
|
331
|
-
// External (extension) imports stay in place for
|
|
332
|
-
|
|
550
|
+
// External (extension / built-in) imports stay in place for codegen.
|
|
551
|
+
const declaration = statement as ImportDeclaration;
|
|
552
|
+
const resolution = classifyDependency(
|
|
553
|
+
dirname(record.path),
|
|
554
|
+
declaration.moduleSpecifier.value,
|
|
555
|
+
externalSpecifiers,
|
|
556
|
+
);
|
|
557
|
+
if (resolution.kind === "external") {
|
|
333
558
|
merged.push(statement);
|
|
334
559
|
} else {
|
|
335
560
|
const synthetic = namespaceStatements.get(statement);
|
|
@@ -1,20 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Builtins for the filesystem operations in Node's `fs` module
|
|
3
|
-
*
|
|
2
|
+
* Builtins for the filesystem operations in Node's `fs` module.
|
|
3
|
+
*
|
|
4
|
+
* Every runtime symbol uses the uniform `xt_value fn(int32_t, xt_value *)`
|
|
5
|
+
* ABI. xbintsc has no event loop, so only the synchronous API is exposed
|
|
6
|
+
* (`*Sync`), plus the watcher factories, whose objects never fire events.
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
import type { BuiltinFunction } from "../../registry.js";
|
|
7
10
|
|
|
8
11
|
export const fsOpsBuiltins: Readonly<Record<string, BuiltinFunction>> = {
|
|
12
|
+
// Path / metadata -------------------------------------------------------
|
|
9
13
|
existsSync: { symbol: "xt_node_exists" },
|
|
14
|
+
accessSync: { symbol: "xt_node_access" },
|
|
15
|
+
statSync: { symbol: "xt_node_stat" },
|
|
16
|
+
lstatSync: { symbol: "xt_node_lstat" },
|
|
17
|
+
statfsSync: { symbol: "xt_node_statfs" },
|
|
18
|
+
realpathSync: { symbol: "xt_node_realpath" },
|
|
19
|
+
chmodSync: { symbol: "xt_node_chmod" },
|
|
20
|
+
lchmodSync: { symbol: "xt_node_lchmod" },
|
|
21
|
+
chownSync: { symbol: "xt_node_chown" },
|
|
22
|
+
lchownSync: { symbol: "xt_node_lchown" },
|
|
23
|
+
truncateSync: { symbol: "xt_node_truncate" },
|
|
24
|
+
utimesSync: { symbol: "xt_node_utimes" },
|
|
25
|
+
lutimesSync: { symbol: "xt_node_lutimes" },
|
|
26
|
+
|
|
27
|
+
// Directories / entries -------------------------------------------------
|
|
10
28
|
readdirSync: { symbol: "xt_node_read_dir" },
|
|
11
29
|
mkdirSync: { symbol: "xt_node_mkdir" },
|
|
30
|
+
mkdtempSync: { symbol: "xt_node_mkdtemp" },
|
|
31
|
+
rmdirSync: { symbol: "xt_node_rmdir" },
|
|
12
32
|
rmSync: { symbol: "xt_node_rm" },
|
|
13
33
|
unlinkSync: { symbol: "xt_node_unlink" },
|
|
14
|
-
rmdirSync: { symbol: "xt_node_rmdir" },
|
|
15
34
|
renameSync: { symbol: "xt_node_rename" },
|
|
16
35
|
copyFileSync: { symbol: "xt_node_copy_file" },
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
cpSync: { symbol: "xt_node_cp" },
|
|
37
|
+
linkSync: { symbol: "xt_node_link" },
|
|
38
|
+
symlinkSync: { symbol: "xt_node_symlink" },
|
|
39
|
+
readlinkSync: { symbol: "xt_node_readlink" },
|
|
40
|
+
opendirSync: { symbol: "xt_node_opendir" },
|
|
41
|
+
globSync: { symbol: "xt_node_glob" },
|
|
42
|
+
|
|
43
|
+
// File descriptors ------------------------------------------------------
|
|
44
|
+
openSync: { symbol: "xt_node_open" },
|
|
45
|
+
closeSync: { symbol: "xt_node_close" },
|
|
46
|
+
readSync: { symbol: "xt_node_read" },
|
|
47
|
+
writeSync: { symbol: "xt_node_write" },
|
|
48
|
+
readvSync: { symbol: "xt_node_readv" },
|
|
49
|
+
writevSync: { symbol: "xt_node_writev" },
|
|
50
|
+
fstatSync: { symbol: "xt_node_fstat" },
|
|
51
|
+
fsyncSync: { symbol: "xt_node_fsync" },
|
|
52
|
+
fdatasyncSync: { symbol: "xt_node_fdatasync" },
|
|
53
|
+
ftruncateSync: { symbol: "xt_node_ftruncate" },
|
|
54
|
+
fchmodSync: { symbol: "xt_node_fchmod" },
|
|
55
|
+
fchownSync: { symbol: "xt_node_fchown" },
|
|
56
|
+
futimesSync: { symbol: "xt_node_futimes" },
|
|
57
|
+
|
|
58
|
+
// Watchers (API-shaped, no events) --------------------------------------
|
|
59
|
+
watch: { symbol: "xt_node_watch" },
|
|
60
|
+
watchFile: { symbol: "xt_node_watch_file" },
|
|
61
|
+
unwatchFile: { symbol: "xt_node_unwatch_file" },
|
|
20
62
|
};
|
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node's `fs` module: synchronous file and directory operations backed by
|
|
3
3
|
* `runtime/ext_node/fs`. Asynchronous callbacks are not supported (xbintsc has
|
|
4
|
-
* no event loop), so
|
|
5
|
-
*
|
|
4
|
+
* no event loop), so the API is the `*Sync` surface plus `watch`/`watchFile`
|
|
5
|
+
* (which return API-shaped watchers that never emit).
|
|
6
|
+
*
|
|
7
|
+
* `fs.constants` and `fs.promises` are exposed as value getters, so both
|
|
8
|
+
* `import { constants } from "fs"` and the `fs.constants.F_OK` property form
|
|
9
|
+
* work.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import type { NodeModule } from "../module.js";
|
|
9
13
|
import { resolveFrom } from "../module.js";
|
|
14
|
+
import type { ModuleExports } from "../../registry.js";
|
|
10
15
|
import { readFileBuiltins } from "./read-file.js";
|
|
11
16
|
import { writeFileBuiltins } from "./write-file.js";
|
|
12
17
|
import { fsOpsBuiltins } from "./fs-ops.js";
|
|
13
18
|
import { fsStreamBuiltins } from "./streams.js";
|
|
14
19
|
|
|
20
|
+
const builtins = { ...readFileBuiltins, ...writeFileBuiltins, ...fsOpsBuiltins, ...fsStreamBuiltins };
|
|
21
|
+
|
|
22
|
+
const exports: ModuleExports = {
|
|
23
|
+
...builtins,
|
|
24
|
+
constants: { valueSymbol: "xt_fs_constants" },
|
|
25
|
+
promises: { valueSymbol: "xt_fs_promises" },
|
|
26
|
+
};
|
|
27
|
+
|
|
15
28
|
export const fsModule: NodeModule = {
|
|
16
29
|
name: "fs",
|
|
17
30
|
runtimeSources: () => [
|
|
18
31
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/read_file.c"),
|
|
19
32
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/write_file.c"),
|
|
20
33
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/fs_ops.c"),
|
|
34
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/fd_ops.c"),
|
|
35
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/meta_ops.c"),
|
|
36
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/link_ops.c"),
|
|
37
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/copy_ops.c"),
|
|
38
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/dir.c"),
|
|
39
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/glob.c"),
|
|
40
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/watch.c"),
|
|
41
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/constants.c"),
|
|
21
42
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/streams.c"),
|
|
22
43
|
],
|
|
23
|
-
builtins: () =>
|
|
44
|
+
builtins: () => builtins,
|
|
45
|
+
exports: () => exports,
|
|
24
46
|
};
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* Node's `fs/promises` module.
|
|
3
3
|
*
|
|
4
4
|
* There is no asynchronous I/O scheduler, so each function wraps the matching
|
|
5
|
-
* synchronous `fs` implementation in an already-
|
|
6
|
-
* `writeFile`, `mkdir`, `readdir`, `stat`, `rm`,
|
|
7
|
-
* from `fs/promises
|
|
5
|
+
* synchronous `fs` implementation in an already-settled promise (rejected on
|
|
6
|
+
* failure). `readFile`, `writeFile`, `mkdir`, `readdir`, `stat`, `rm`,
|
|
7
|
+
* `open` (a `FileHandle`), ... are therefore importable from `fs/promises`.
|
|
8
|
+
* `constants` is re-exported as a value getter.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import type { NodeModule } from "../module.js";
|
|
11
12
|
import { resolveFrom } from "../module.js";
|
|
12
|
-
import type { BuiltinFunction } from "../../registry.js";
|
|
13
|
+
import type { BuiltinFunction, ModuleExports } from "../../registry.js";
|
|
13
14
|
|
|
14
15
|
const builtins: Readonly<Record<string, BuiltinFunction>> = {
|
|
15
16
|
readFile: { symbol: "xt_node_p_read_file" },
|
|
@@ -22,10 +23,32 @@ const builtins: Readonly<Record<string, BuiltinFunction>> = {
|
|
|
22
23
|
rmdir: { symbol: "xt_node_p_rmdir" },
|
|
23
24
|
rename: { symbol: "xt_node_p_rename" },
|
|
24
25
|
copyFile: { symbol: "xt_node_p_copy_file" },
|
|
26
|
+
cp: { symbol: "xt_node_p_cp" },
|
|
25
27
|
realpath: { symbol: "xt_node_p_realpath" },
|
|
26
28
|
stat: { symbol: "xt_node_p_stat" },
|
|
27
29
|
lstat: { symbol: "xt_node_p_lstat" },
|
|
30
|
+
statfs: { symbol: "xt_node_p_statfs" },
|
|
28
31
|
access: { symbol: "xt_node_p_access" },
|
|
32
|
+
open: { symbol: "xt_node_p_open" },
|
|
33
|
+
chmod: { symbol: "xt_node_p_chmod" },
|
|
34
|
+
lchmod: { symbol: "xt_node_p_lchmod" },
|
|
35
|
+
chown: { symbol: "xt_node_p_chown" },
|
|
36
|
+
lchown: { symbol: "xt_node_p_lchown" },
|
|
37
|
+
truncate: { symbol: "xt_node_p_truncate" },
|
|
38
|
+
utimes: { symbol: "xt_node_p_utimes" },
|
|
39
|
+
lutimes: { symbol: "xt_node_p_lutimes" },
|
|
40
|
+
link: { symbol: "xt_node_p_link" },
|
|
41
|
+
symlink: { symbol: "xt_node_p_symlink" },
|
|
42
|
+
readlink: { symbol: "xt_node_p_readlink" },
|
|
43
|
+
mkdtemp: { symbol: "xt_node_p_mkdtemp" },
|
|
44
|
+
opendir: { symbol: "xt_node_p_opendir" },
|
|
45
|
+
glob: { symbol: "xt_node_p_glob" },
|
|
46
|
+
watch: { symbol: "xt_node_p_watch" },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const exports: ModuleExports = {
|
|
50
|
+
...builtins,
|
|
51
|
+
constants: { valueSymbol: "xt_fs_constants" },
|
|
29
52
|
};
|
|
30
53
|
|
|
31
54
|
export const fsPromisesModule: NodeModule = {
|
|
@@ -35,7 +58,13 @@ export const fsPromisesModule: NodeModule = {
|
|
|
35
58
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/read_file.c"),
|
|
36
59
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/write_file.c"),
|
|
37
60
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/fs_ops.c"),
|
|
61
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/fd_ops.c"),
|
|
62
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/meta_ops.c"),
|
|
63
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/link_ops.c"),
|
|
64
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/copy_ops.c"),
|
|
65
|
+
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/constants.c"),
|
|
38
66
|
resolveFrom(import.meta.url, "../../../../runtime/ext_node/fs/promises.c"),
|
|
39
67
|
],
|
|
40
68
|
builtins: () => builtins,
|
|
69
|
+
exports: () => exports,
|
|
41
70
|
};
|