nomen-lang 0.6.2 → 0.6.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/core/System/Text/Regex.nm +416 -15
- package/core/docs/System/Text/Regex.md +1 -1
- package/dist/index.mjs +1765 -476
- package/package.json +1 -1
- package/src/index.ts +8 -56
- package/src/init.ts +12 -3
- package/src/test.ts +34 -2
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -310,9 +310,7 @@ function processFolder(folder: string, config: Config, mode: Mode, program_args:
|
|
|
310
310
|
let dirent;
|
|
311
311
|
while ((dirent = dir.readSync()) !== null) {
|
|
312
312
|
if (shouldProcessFile(dirent.name)) {
|
|
313
|
-
|
|
314
|
-
// here would recompile the whole project once per file.
|
|
315
|
-
processFile(path.join(folder, dirent.name), config, mode, program_args, false);
|
|
313
|
+
processFile(path.join(folder, dirent.name), config, mode, program_args);
|
|
316
314
|
// @ts-ignore
|
|
317
315
|
let _ = fs.watch;
|
|
318
316
|
}
|
|
@@ -324,46 +322,7 @@ function shouldProcessFile(filename: string) {
|
|
|
324
322
|
return path.extname(filename) === SUPPORTED_EXTENSION;
|
|
325
323
|
}
|
|
326
324
|
|
|
327
|
-
|
|
328
|
-
* The project's entry file for `file_path`: walk up to the nearest
|
|
329
|
-
* `package.jsonc` and resolve its `entry` field, so any file inside a project
|
|
330
|
-
* is checked with the entry's import context (`import types` resolves
|
|
331
|
-
* against the project's `src/`, not the file's own folder). Returns
|
|
332
|
-
* undefined when the file is standalone, IS the entry, sits in an
|
|
333
|
-
* entry-less project (e.g. a library checkout), or the config is malformed.
|
|
334
|
-
*/
|
|
335
|
-
function project_entry_for(file_path: string): string | undefined {
|
|
336
|
-
let dir = path.dirname(file_path);
|
|
337
|
-
for (let i = 0; i < 20; i++) {
|
|
338
|
-
const config_path = path.join(dir, "package.jsonc");
|
|
339
|
-
if (fs.existsSync(config_path)) {
|
|
340
|
-
try {
|
|
341
|
-
const raw = fs.readFileSync(config_path, "utf8");
|
|
342
|
-
const json = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
343
|
-
const parsed = JSON.parse(json);
|
|
344
|
-
if (parsed.entry) {
|
|
345
|
-
const entry = path.resolve(dir, parsed.entry);
|
|
346
|
-
if (entry !== file_path && fs.existsSync(entry)) return entry;
|
|
347
|
-
}
|
|
348
|
-
} catch {
|
|
349
|
-
// malformed config — keep the file's own context
|
|
350
|
-
}
|
|
351
|
-
return undefined;
|
|
352
|
-
}
|
|
353
|
-
const parent = path.dirname(dir);
|
|
354
|
-
if (parent === dir) return undefined;
|
|
355
|
-
dir = parent;
|
|
356
|
-
}
|
|
357
|
-
return undefined;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function processFile(
|
|
361
|
-
filename: string,
|
|
362
|
-
config: Config,
|
|
363
|
-
mode: Mode,
|
|
364
|
-
program_args: string[],
|
|
365
|
-
anchor_to_project = true,
|
|
366
|
-
) {
|
|
325
|
+
function processFile(filename: string, config: Config, mode: Mode, program_args: string[]) {
|
|
367
326
|
console.log("Processing", filename);
|
|
368
327
|
|
|
369
328
|
const arch = config.arch || "aarch64";
|
|
@@ -374,22 +333,15 @@ function processFile(
|
|
|
374
333
|
config.lib = resolve_lib(resolved);
|
|
375
334
|
}
|
|
376
335
|
|
|
377
|
-
// A `*.test.nm` file keeps its own join context: the test harness
|
|
378
|
-
// anchors at the test's folder and pulls the program's `src/` module in
|
|
379
|
-
// with `main` stripped, which the entry's context would not do.
|
|
380
|
-
const anchor =
|
|
381
|
-
anchor_to_project && !resolved.endsWith(".test.nm")
|
|
382
|
-
? (project_entry_for(resolved) ?? resolved)
|
|
383
|
-
: resolved;
|
|
384
|
-
if (anchor !== resolved) {
|
|
385
|
-
console.log("Anchored to project entry", anchor);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
336
|
let startTime = performance.now();
|
|
389
337
|
|
|
390
|
-
|
|
338
|
+
// Compile the file the user named (or the discovered entry). `join`
|
|
339
|
+
// resolves its imports relative to its own folder and gathers the
|
|
340
|
+
// project's module declarations, so an explicitly-passed `--in` is never
|
|
341
|
+
// silently replaced by the `package.jsonc` `entry`.
|
|
342
|
+
const input = join(resolved, config.lib);
|
|
391
343
|
const library = config.lib ? get_library(config.lib) : undefined;
|
|
392
|
-
const parsed = parse(input, library,
|
|
344
|
+
const parsed = parse(input, library, resolved);
|
|
393
345
|
|
|
394
346
|
let errors = parsed.errors;
|
|
395
347
|
|
package/src/init.ts
CHANGED
|
@@ -63,13 +63,22 @@ nomen test
|
|
|
63
63
|
`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
// Resolve a bundled asset (core/ or NOMEN_AGENTS.md) shipped alongside the
|
|
66
|
+
// Resolve a bundled asset (core/, n/ or NOMEN_AGENTS.md) shipped alongside the
|
|
67
67
|
// CLI. In the published package these sit next to dist/; in the dev tree they
|
|
68
|
-
// live one level up (the repo root), so check both.
|
|
68
|
+
// live one level up (the repo root), so check both. In the dev tree the
|
|
69
|
+
// REPO's live copy wins — a stale `cli/core` bundle left by an old
|
|
70
|
+
// `pnpm build` must not shadow the current `core/` (a silent-wrong-library
|
|
71
|
+
// tar pit; see FOLLOWUP.md, "Stale cli/core asset copy"). In a published
|
|
72
|
+
// install (running from node_modules) the shipped copy wins, so a consumer
|
|
73
|
+
// project's sibling `core/` directory is never picked up.
|
|
69
74
|
export function find_bundled(filename: string): string | undefined {
|
|
70
75
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
71
76
|
const cli_root = path.dirname(here);
|
|
72
|
-
const
|
|
77
|
+
const repo_root = path.join(cli_root, "..");
|
|
78
|
+
const dev_tree = !cli_root.includes("node_modules");
|
|
79
|
+
const candidates = dev_tree
|
|
80
|
+
? [path.join(repo_root, filename), path.join(cli_root, filename)]
|
|
81
|
+
: [path.join(cli_root, filename), path.join(repo_root, filename)];
|
|
73
82
|
for (const c of candidates) {
|
|
74
83
|
if (fs.existsSync(c)) return path.resolve(c);
|
|
75
84
|
}
|
package/src/test.ts
CHANGED
|
@@ -37,6 +37,9 @@ export interface TestFileResult {
|
|
|
37
37
|
fails: FailRecord[];
|
|
38
38
|
benches: BenchRecord[];
|
|
39
39
|
other: string[];
|
|
40
|
+
// "LEAK: N allocation(s)" lines the audits runtime printed at exit. Empty
|
|
41
|
+
// unless the file was run with --audit; a non-empty list fails the file.
|
|
42
|
+
leaks: string[];
|
|
40
43
|
// Set when the compiled binary crashed before finishing (e.g. a segfault
|
|
41
44
|
// in a test). We still surface whatever records it managed to emit.
|
|
42
45
|
crashed?: string;
|
|
@@ -257,6 +260,14 @@ export function parse_records(stdout: string): RunRecord {
|
|
|
257
260
|
return { tests, fails, benches, other };
|
|
258
261
|
}
|
|
259
262
|
|
|
263
|
+
/** Pull the exit-time audit "LEAK: N allocation(s)" lines out of stdout. */
|
|
264
|
+
export function extract_leaks(stdout: string): string[] {
|
|
265
|
+
return stdout
|
|
266
|
+
.split("\n")
|
|
267
|
+
.map((l) => l.trim())
|
|
268
|
+
.filter((l) => l.startsWith("LEAK:"));
|
|
269
|
+
}
|
|
270
|
+
|
|
260
271
|
/**
|
|
261
272
|
* Compile one `*.test.nm` file (with the generated harness) and run it,
|
|
262
273
|
* returning its parsed records and any crash info.
|
|
@@ -281,6 +292,7 @@ export function run_test_file(
|
|
|
281
292
|
fails: [],
|
|
282
293
|
benches: [],
|
|
283
294
|
other: [],
|
|
295
|
+
leaks: [],
|
|
284
296
|
ms: 0,
|
|
285
297
|
};
|
|
286
298
|
|
|
@@ -408,11 +420,21 @@ export function run_test_file(
|
|
|
408
420
|
result.fails = records.fails;
|
|
409
421
|
result.benches = records.benches;
|
|
410
422
|
result.other = records.other;
|
|
423
|
+
// Under --audit the exit-time audit check prints "LEAK: N allocation(s)"
|
|
424
|
+
// to stdout when the malloc/free balance is nonzero. Treat any such line
|
|
425
|
+
// as a failure so a leaked test file cannot stay green.
|
|
426
|
+
result.leaks = audit ? extract_leaks(runStdout) : [];
|
|
427
|
+
// Leak lines are surfaced explicitly above; drop them from the generic
|
|
428
|
+
// stdout dump so they are not printed twice.
|
|
429
|
+
if (result.leaks.length) {
|
|
430
|
+
result.other = result.other.filter((l) => !l.trim().startsWith("LEAK:"));
|
|
431
|
+
}
|
|
411
432
|
result.crashed = crashed;
|
|
412
433
|
if (crashed) result.phase = "run";
|
|
413
|
-
// A file "passes" when it reported no failed asserts
|
|
434
|
+
// A file "passes" when it reported no failed asserts, didn't crash, and
|
|
435
|
+
// (under --audit) leaked nothing.
|
|
414
436
|
const failed = result.fails.length > 0 || result.tests.some((t) => t.failed > 0);
|
|
415
|
-
result.ok = !failed && crashed === undefined;
|
|
437
|
+
result.ok = !failed && crashed === undefined && result.leaks.length === 0;
|
|
416
438
|
|
|
417
439
|
result.ms = performance.now() - start;
|
|
418
440
|
return result;
|
|
@@ -475,6 +497,9 @@ export function report_file(result: TestFileResult): void {
|
|
|
475
497
|
for (const f of result.fails) {
|
|
476
498
|
console.log(` ${C.red("✗")} ${f.test} ${C.dim(">")} ${f.message}`);
|
|
477
499
|
}
|
|
500
|
+
for (const leak of result.leaks) {
|
|
501
|
+
console.log(` ${C.yellow("⚠")} ${C.yellow(leak)}`);
|
|
502
|
+
}
|
|
478
503
|
for (const b of result.benches) {
|
|
479
504
|
console.log(
|
|
480
505
|
` ${C.cyan("⏱")} ${b.label} ${C.dim(`(n=${b.n})`)} ` +
|
|
@@ -540,6 +565,7 @@ export function runTests(root: string, options: RunTestsOptions = {}): boolean {
|
|
|
540
565
|
const totalTests = results.reduce((a, r) => a + r.tests.length, 0);
|
|
541
566
|
const totalFailed = results.reduce((a, r) => a + r.tests.reduce((x, t) => x + t.failed, 0), 0);
|
|
542
567
|
const totalPassed = totalTests - totalFailed;
|
|
568
|
+
const totalLeaks = results.reduce((a, r) => a + r.leaks.length, 0);
|
|
543
569
|
const anyFailed = results.some((r) => !r.ok);
|
|
544
570
|
|
|
545
571
|
console.log("");
|
|
@@ -556,6 +582,12 @@ export function runTests(root: string, options: RunTestsOptions = {}): boolean {
|
|
|
556
582
|
(totalFailed ? ` ${C.dim("|")} ${C.red(`${totalFailed} failed`)}` : "") +
|
|
557
583
|
C.dim(` (${totalTests})`),
|
|
558
584
|
);
|
|
585
|
+
if (totalLeaks) {
|
|
586
|
+
console.log(
|
|
587
|
+
` ${C.dim("Leaks ")} ${C.yellow(`${totalLeaks} reported`)}` +
|
|
588
|
+
C.dim(" (--audit; leaked files count as failures)"),
|
|
589
|
+
);
|
|
590
|
+
}
|
|
559
591
|
console.log(` ${C.dim(" Time ")} ${format_duration(elapsed)}`);
|
|
560
592
|
console.log("");
|
|
561
593
|
|