xbintsc 0.3.13 → 0.3.15
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 +22 -22
- package/README.zh-CN.md +20 -19
- package/bin/xbintsc.js +1 -2
- package/dist/src/cli/main.js +4 -1
- package/dist/src/cli/main.js.map +1 -1
- 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 +6 -0
- package/dist/src/codegen/generator/context.js +24 -2
- 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/codegen/generator/tables.d.ts +5 -6
- package/dist/src/codegen/generator/tables.js +5 -6
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.js +15 -3
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/index.d.ts +1 -2
- package/dist/src/driver/index.js +1 -2
- package/dist/src/driver/index.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/driver/paths.d.ts +0 -9
- package/dist/src/driver/paths.js +0 -31
- package/dist/src/driver/paths.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +10 -10
- package/dist/src/driver/toolchain-provider.js +9 -38
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.js +8 -2
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/native.d.ts +2 -3
- package/dist/src/extensions/native.js +2 -3
- package/dist/src/extensions/native.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 +8 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +28 -1
- 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/driver/paths.test.js +1 -9
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +12 -79
- package/dist/tests/driver/toolchain-provider.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/native-extensions.test.d.ts +4 -5
- package/dist/tests/e2e/native-extensions.test.js +6 -7
- package/dist/tests/e2e/native-extensions.test.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 +1 -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 +8 -0
- package/runtime/rt_internal.h +10 -23
- package/runtime/xt_ext.h +0 -1
- package/runtime/xt_io.c +24 -3
- package/runtime/xt_stdlib2/helpers.inc +2 -4
- package/scripts/build-runtime.ts +7 -8
- package/scripts/coverage-runtime.ts +4 -4
- package/scripts/package-release.ts +12 -84
- package/src/cli/main.ts +4 -1
- package/src/codegen/generator/calls/access.ts +15 -0
- package/src/codegen/generator/calls/invocation.ts +8 -2
- package/src/codegen/generator/context.ts +30 -2
- package/src/codegen/generator/expressions/primary.ts +5 -0
- package/src/codegen/generator/tables.ts +5 -6
- package/src/driver/compiler.ts +16 -3
- package/src/driver/index.ts +1 -9
- package/src/driver/modules.ts +251 -26
- package/src/driver/paths.ts +0 -31
- package/src/driver/toolchain-provider.ts +12 -45
- package/src/driver/toolchain.ts +7 -2
- package/src/extensions/native.ts +2 -3
- 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/dist/src/driver/toolchain-download.d.ts +0 -50
- package/dist/src/driver/toolchain-download.js +0 -102
- package/dist/src/driver/toolchain-download.js.map +0 -1
- package/dist/tests/driver/toolchain-download.test.d.ts +0 -1
- package/dist/tests/driver/toolchain-download.test.js +0 -53
- package/dist/tests/driver/toolchain-download.test.js.map +0 -1
- package/scripts/fetch-toolchain.ts +0 -101
- package/src/driver/toolchain-download.ts +0 -135
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);
|
package/src/driver/paths.ts
CHANGED
|
@@ -22,15 +22,6 @@ export function findRuntimeDir(): string {
|
|
|
22
22
|
throw new Error("Unable to locate the xbintsc runtime directory (missing runtime/rt.h)");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function findPackageRoot(): string {
|
|
26
|
-
return resolve(findRuntimeDir(), "..");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** `<package>/vendor` — where a bundled toolchain is assembled (may not exist). */
|
|
30
|
-
export function vendorRootDir(): string {
|
|
31
|
-
return resolve(findPackageRoot(), "vendor");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
25
|
/** Platform-architecture slug, e.g. `darwin-arm64`, `linux-x64`, `win32-x64`. */
|
|
35
26
|
export function platformSlug(): string {
|
|
36
27
|
return `${process.platform}-${process.arch}`;
|
|
@@ -68,25 +59,3 @@ export function releaseArchiveFileName(
|
|
|
68
59
|
export function releaseArchiveExtension(useZstd: boolean): string {
|
|
69
60
|
return useZstd ? ".tar.zst" : ".tar.gz";
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Directory that may hold a toolchain shipped next to the package:
|
|
74
|
-
* `<package>/vendor/<os>-<arch>` (or a generic `vendor/`). Undefined when none
|
|
75
|
-
* is present, in which case the driver falls back to the system PATH.
|
|
76
|
-
*/
|
|
77
|
-
export function findVendorDir(): string | undefined {
|
|
78
|
-
const roots: string[] = [];
|
|
79
|
-
try {
|
|
80
|
-
roots.push(findPackageRoot());
|
|
81
|
-
} catch {
|
|
82
|
-
// `runtime/` may be absent (e.g. a bare `emit` install); vendor is optional.
|
|
83
|
-
}
|
|
84
|
-
roots.push(resolve(here, "../../.."), resolve(here, "../.."), process.cwd());
|
|
85
|
-
for (const root of roots) {
|
|
86
|
-
const specific = join(root, "vendor", platformSlug());
|
|
87
|
-
if (existsSync(specific)) return specific;
|
|
88
|
-
const generic = join(root, "vendor");
|
|
89
|
-
if (existsSync(generic)) return generic;
|
|
90
|
-
}
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* xbintsc shells out to a clang-compatible driver for three jobs: lowering LLVM
|
|
5
5
|
* IR to an object, compiling the C runtime, and linking. This module decides
|
|
6
|
-
* *which* driver to use, in a fixed priority order
|
|
7
|
-
* shadow the system one without touching the driver code:
|
|
6
|
+
* *which* driver to use, in a fixed priority order:
|
|
8
7
|
*
|
|
9
8
|
* 1. an explicit `xbintsc_CLANG` / `xbintsc_TOOLCHAIN`
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
9
|
+
* 2. the system `PATH` (`findClang`)
|
|
10
|
+
*
|
|
11
|
+
* xbintsc does not ship a compiler. Every host must provide a clang-compatible
|
|
12
|
+
* toolchain (see `doc/requirements.md`): the Xcode Command Line Tools on macOS,
|
|
13
|
+
* the distribution's clang/lld on Linux, and LLVM plus the Visual Studio C++
|
|
14
|
+
* build tools (the MSVC ABI) on Windows.
|
|
12
15
|
*
|
|
13
16
|
* Extra linker arguments (for example `-fuse-ld=lld`) can be supplied through
|
|
14
17
|
* `xbintsc_LINKER_ARGS`.
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
|
-
import { existsSync } from "node:fs";
|
|
18
|
-
import { join } from "node:path";
|
|
19
|
-
import { findVendorDir } from "./paths.js";
|
|
20
20
|
import { findClang, realRunner, type Runner } from "./toolchain.js";
|
|
21
21
|
|
|
22
|
-
export type ToolchainSource = "env" | "
|
|
22
|
+
export type ToolchainSource = "env" | "system";
|
|
23
23
|
|
|
24
24
|
export interface ResolvedToolchain {
|
|
25
25
|
/** Path or name of the clang-compatible driver to invoke. */
|
|
@@ -28,17 +28,14 @@ export interface ResolvedToolchain {
|
|
|
28
28
|
readonly source: ToolchainSource;
|
|
29
29
|
/** Extra arguments to pass to the link step. */
|
|
30
30
|
readonly linkerArgs: readonly string[];
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
* environment. Used to point `LD_LIBRARY_PATH` at bundled shared libraries
|
|
34
|
-
* (for example the legacy `libtinfo.so.5` the official Linux build needs).
|
|
35
|
-
*/
|
|
31
|
+
/** Extra environment for the toolchain subprocesses, merged over the current
|
|
32
|
+
* environment. */
|
|
36
33
|
readonly env: Record<string, string>;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
function works(runner: Runner, command: string
|
|
36
|
+
function works(runner: Runner, command: string): boolean {
|
|
40
37
|
try {
|
|
41
|
-
return runner.run(command, ["--version"]
|
|
38
|
+
return runner.run(command, ["--version"]).status === 0;
|
|
42
39
|
} catch {
|
|
43
40
|
return false;
|
|
44
41
|
}
|
|
@@ -50,18 +47,6 @@ function linkerArgs(fallback: readonly string[]): string[] {
|
|
|
50
47
|
return [...fallback];
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
/**
|
|
54
|
-
* Environment overrides for a bundled toolchain. The official Linux build links
|
|
55
|
-
* the removed `libtinfo.so.5` soname, so when we ship it under `vendor/lib/` we
|
|
56
|
-
* prepend that directory to the dynamic linker search path.
|
|
57
|
-
*/
|
|
58
|
-
function vendorEnv(vendor: string): Record<string, string> {
|
|
59
|
-
const libDir = join(vendor, "lib");
|
|
60
|
-
if (process.platform !== "linux" || !existsSync(libDir)) return {};
|
|
61
|
-
const existing = process.env.LD_LIBRARY_PATH;
|
|
62
|
-
return { LD_LIBRARY_PATH: existing ? libDir + ":" + existing : libDir };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
50
|
/** Resolve the toolchain that `build` should use. See the module docblock. */
|
|
66
51
|
export function resolveToolchain(runner: Runner = realRunner): ResolvedToolchain {
|
|
67
52
|
const explicit = process.env.xbintsc_CLANG ?? process.env.xbintsc_TOOLCHAIN;
|
|
@@ -69,23 +54,5 @@ export function resolveToolchain(runner: Runner = realRunner): ResolvedToolchain
|
|
|
69
54
|
return { clang: explicit, source: "env", linkerArgs: linkerArgs([]), env: {} };
|
|
70
55
|
}
|
|
71
56
|
|
|
72
|
-
const vendor = findVendorDir();
|
|
73
|
-
if (vendor) {
|
|
74
|
-
const env = vendorEnv(vendor);
|
|
75
|
-
const names = process.platform === "win32" ? ["clang.exe", "clang"] : ["clang"];
|
|
76
|
-
for (const name of names) {
|
|
77
|
-
const candidate = join(vendor, "bin", name);
|
|
78
|
-
if (existsSync(candidate) && works(runner, candidate, env)) {
|
|
79
|
-
// A bundled toolchain ships `ld.lld`, so prefer lld over any system linker.
|
|
80
|
-
return {
|
|
81
|
-
clang: candidate,
|
|
82
|
-
source: "vendor",
|
|
83
|
-
linkerArgs: linkerArgs(["-fuse-ld=lld"]),
|
|
84
|
-
env,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
57
|
return { clang: findClang(runner), source: "system", linkerArgs: linkerArgs([]), env: {} };
|
|
91
58
|
}
|
package/src/driver/toolchain.ts
CHANGED
|
@@ -64,8 +64,13 @@ export function findClang(runner: Runner = realRunner): string {
|
|
|
64
64
|
"cc",
|
|
65
65
|
].filter((candidate): candidate is string => Boolean(candidate));
|
|
66
66
|
for (const candidate of candidates) {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
let status: number;
|
|
68
|
+
try {
|
|
69
|
+
status = runner.run(candidate, ["--version"]).status;
|
|
70
|
+
} catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (status === 0) return candidate;
|
|
69
74
|
}
|
|
70
75
|
throw new ToolchainError("clang --version", 1, "No C compiler found. Set xbintsc_CLANG to a clang binary.");
|
|
71
76
|
}
|
package/src/extensions/native.ts
CHANGED
|
@@ -18,9 +18,8 @@
|
|
|
18
18
|
* {
|
|
19
19
|
* "name": "mathx",
|
|
20
20
|
* "description": "C++ math helpers",
|
|
21
|
-
* "objects": ["build/
|
|
22
|
-
* "
|
|
23
|
-
* "linkerFlagsByPlatform": { "linux": ["-lstdc++"], "darwin": ["-lc++"] },
|
|
21
|
+
* "objects": ["build/mathx.o"],
|
|
22
|
+
* "linkerFlagsByPlatform": { "linux": ["-lstdc++", "-lm"], "darwin": ["-lc++"], "win32": ["-lmsvcprt"] },
|
|
24
23
|
* "builtins": { "fastAdd": { "symbol": "mathx_add" } },
|
|
25
24
|
* "modules": {
|
|
26
25
|
* "mathx": {
|
|
@@ -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
|
};
|