nomen-lang 0.0.7 → 0.0.8

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.
Files changed (118) hide show
  1. package/NOMEN_AGENTS.md +235 -0
  2. package/core/System/Ansi.nm +150 -0
  3. package/core/System/Array.nm +358 -0
  4. package/core/System/BigInt.nm +887 -0
  5. package/core/System/Buffer.nm +526 -0
  6. package/core/System/Channel.nm +198 -0
  7. package/core/System/ClassBuffer.nm +229 -0
  8. package/core/System/Console.nm +211 -0
  9. package/core/System/Controls/Button.nm +138 -0
  10. package/core/System/Controls/CheckBox.nm +158 -0
  11. package/core/System/Controls/Container.nm +1562 -0
  12. package/core/System/Controls/Control.nm +28 -0
  13. package/core/System/Controls/Geometry.nm +101 -0
  14. package/core/System/Controls/Layout.nm +228 -0
  15. package/core/System/Controls/LayoutParams.nm +50 -0
  16. package/core/System/Controls/Text.nm +129 -0
  17. package/core/System/Controls/TextBox.nm +157 -0
  18. package/core/System/Controls/Window.nm +280 -0
  19. package/core/System/Disposable.nm +8 -0
  20. package/core/System/Enumerable.nm +10 -0
  21. package/core/System/Equatable.nm +10 -0
  22. package/core/System/Graph.nm +68 -0
  23. package/core/System/Hashable.nm +13 -0
  24. package/core/System/Init.nm +21 -0
  25. package/core/System/LinkedList.nm +64 -0
  26. package/core/System/List.nm +44 -0
  27. package/core/System/Map.nm +153 -0
  28. package/core/System/Math.nm +54 -0
  29. package/core/System/Mutex.nm +78 -0
  30. package/core/System/Nursery.nm +27 -0
  31. package/core/System/Sendable.nm +9 -0
  32. package/core/System/Set.nm +97 -0
  33. package/core/System/Stream/Directory.nm +176 -0
  34. package/core/System/Stream/File.nm +432 -0
  35. package/core/System/Stream/Http.nm +453 -0
  36. package/core/System/String.nm +154 -0
  37. package/core/System/StringBuilder.nm +249 -0
  38. package/core/System/Stringable.nm +8 -0
  39. package/core/System/Task.nm +263 -0
  40. package/core/System/Test.nm +241 -0
  41. package/core/System/Text/Json.nm +377 -0
  42. package/core/System/Text/JsonTree.nm +187 -0
  43. package/core/System/Text/Regex.nm +655 -0
  44. package/core/System/Time.nm +112 -0
  45. package/core/System/Tree.nm +70 -0
  46. package/core/System/Viewable.nm +27 -0
  47. package/core/System/bool.nm +66 -0
  48. package/core/System/char.nm +42 -0
  49. package/core/System/float.nm +43 -0
  50. package/core/System/int.nm +54 -0
  51. package/core/System/int64.nm +42 -0
  52. package/core/System/int8.nm +42 -0
  53. package/core/System/uint.nm +42 -0
  54. package/core/System/uint64.nm +42 -0
  55. package/core/System/uint8.nm +42 -0
  56. package/core/docs/System/Ansi.md +40 -0
  57. package/core/docs/System/Array.md +16 -0
  58. package/core/docs/System/BigInt.md +30 -0
  59. package/core/docs/System/Buffer.md +31 -0
  60. package/core/docs/System/Channel.md +10 -0
  61. package/core/docs/System/ClassBuffer.md +15 -0
  62. package/core/docs/System/Console.md +13 -0
  63. package/core/docs/System/Controls/Button.md +12 -0
  64. package/core/docs/System/Controls/CheckBox.md +15 -0
  65. package/core/docs/System/Controls/Container.md +82 -0
  66. package/core/docs/System/Controls/Control.md +5 -0
  67. package/core/docs/System/Controls/Geometry.md +26 -0
  68. package/core/docs/System/Controls/Layout.md +37 -0
  69. package/core/docs/System/Controls/LayoutParams.md +13 -0
  70. package/core/docs/System/Controls/Text.md +12 -0
  71. package/core/docs/System/Controls/TextBox.md +13 -0
  72. package/core/docs/System/Controls/Window.md +20 -0
  73. package/core/docs/System/Disposable.md +5 -0
  74. package/core/docs/System/Enumerable.md +5 -0
  75. package/core/docs/System/Graph.md +17 -0
  76. package/core/docs/System/Init.md +9 -0
  77. package/core/docs/System/LinkedList.md +16 -0
  78. package/core/docs/System/List.md +13 -0
  79. package/core/docs/System/Map.md +14 -0
  80. package/core/docs/System/Math.md +11 -0
  81. package/core/docs/System/Mutex.md +10 -0
  82. package/core/docs/System/Nursery.md +5 -0
  83. package/core/docs/System/Sendable.md +5 -0
  84. package/core/docs/System/Set.md +14 -0
  85. package/core/docs/System/Stream/Directory.md +12 -0
  86. package/core/docs/System/Stream/File.md +21 -0
  87. package/core/docs/System/String.md +14 -0
  88. package/core/docs/System/StringBuilder.md +12 -0
  89. package/core/docs/System/Stringable.md +5 -0
  90. package/core/docs/System/Task.md +15 -0
  91. package/core/docs/System/Text/Json.md +12 -0
  92. package/core/docs/System/Text/JsonTree.md +21 -0
  93. package/core/docs/System/Text/Regex.md +12 -0
  94. package/core/docs/System/Time.md +11 -0
  95. package/core/docs/System/Tree.md +18 -0
  96. package/core/docs/System/Viewable.md +5 -0
  97. package/core/docs/System/bool.md +9 -0
  98. package/core/docs/System/char.md +9 -0
  99. package/core/docs/System/float.md +9 -0
  100. package/core/docs/System/int.md +10 -0
  101. package/core/docs/System/int64.md +9 -0
  102. package/core/docs/System/int8.md +9 -0
  103. package/core/docs/System/uint.md +9 -0
  104. package/core/docs/System/uint64.md +9 -0
  105. package/core/docs/System/uint8.md +9 -0
  106. package/core/package.jsonc +4 -0
  107. package/dist/index.mjs +417 -5490
  108. package/package.json +33 -30
  109. package/src/args.ts +171 -0
  110. package/src/index.ts +53 -104
  111. package/src/init.ts +128 -0
  112. package/src/test.ts +6 -2
  113. package/test/fixtures/build/calc.test +0 -0
  114. package/test/fixtures/build/calc.test.c +0 -969
  115. package/test/fixtures/build/main.h +0 -170
  116. package/test/fixtures/calc.test.nm +0 -32
  117. package/tsconfig.json +0 -21
  118. package/vite.config.ts +0 -7
package/package.json CHANGED
@@ -1,31 +1,34 @@
1
1
  {
2
- "name": "nomen-lang",
3
- "version": "0.0.7",
4
- "description": "The CLI for the Nomen programming language.",
5
- "keywords": [],
6
- "license": "ISC",
7
- "author": "",
8
- "bin": {
9
- "nomen": "dist/index.mjs"
10
- },
11
- "type": "module",
12
- "scripts": {
13
- "build": "vp pack",
14
- "dev": "vp pack --watch",
15
- "test": "vp test",
16
- "check": "vp check",
17
- "format": "vp check --fix",
18
- "prepare": "vp config",
19
- "go": "tsx src/index.ts",
20
- "register": "pnpm build && pnpm add -g ."
21
- },
22
- "devDependencies": {
23
- "@types/node": "^26.1.2",
24
- "@types/yargs": "^17.0.35",
25
- "chokidar": "^5.0.0",
26
- "tsx": "^4.23.1",
27
- "typescript": "^7.0.2",
28
- "vite-plus": "catalog:",
29
- "yargs": "^18.1.0"
30
- }
31
- }
2
+ "name": "nomen-lang",
3
+ "version": "0.0.8",
4
+ "description": "The CLI for the Nomen programming language.",
5
+ "keywords": [],
6
+ "license": "ISC",
7
+ "author": "",
8
+ "bin": {
9
+ "nomen": "dist/index.mjs"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "src",
14
+ "core",
15
+ "NOMEN_AGENTS.md"
16
+ ],
17
+ "type": "module",
18
+ "devDependencies": {
19
+ "@types/node": "^26.1.2",
20
+ "chokidar": "^5.0.0",
21
+ "tsx": "^4.23.1",
22
+ "typescript": "^7.0.2",
23
+ "vite-plus": "0.2.7"
24
+ },
25
+ "scripts": {
26
+ "build": "node scripts/bundle-assets.mjs && vp pack",
27
+ "dev": "node scripts/bundle-assets.mjs && vp pack --watch",
28
+ "test": "vp test",
29
+ "check": "vp check",
30
+ "format": "vp check --fix",
31
+ "go": "tsx src/index.ts",
32
+ "register": "pnpm build && pnpm add -g ."
33
+ }
34
+ }
package/src/args.ts ADDED
@@ -0,0 +1,171 @@
1
+ export interface Args {
2
+ command: string | undefined;
3
+ name?: string;
4
+ in?: string;
5
+ out?: string;
6
+ config?: string;
7
+ watch: boolean;
8
+ filter?: string;
9
+ arch: string;
10
+ platform?: string;
11
+ lib?: string;
12
+ audit: boolean;
13
+ audit_runtime?: string;
14
+ check: boolean;
15
+ }
16
+
17
+ // Canonical option name for each accepted long/short spelling.
18
+ const STRING_OPTIONS: Map<string, string> = new Map([
19
+ ["in", "in"],
20
+ ["i", "in"],
21
+ ["out", "out"],
22
+ ["o", "out"],
23
+ ["config", "config"],
24
+ ["c", "config"],
25
+ ["filter", "filter"],
26
+ ["f", "filter"],
27
+ ["arch", "arch"],
28
+ ["a", "arch"],
29
+ ["platform", "platform"],
30
+ ["p", "platform"],
31
+ ["lib", "lib"],
32
+ ["l", "lib"],
33
+ ["audit-runtime", "audit_runtime"],
34
+ ]);
35
+
36
+ const BOOLEAN_OPTIONS: Map<string, string> = new Map([
37
+ ["watch", "watch"],
38
+ ["w", "watch"],
39
+ ["audit", "audit"],
40
+ ["check", "check"],
41
+ ]);
42
+
43
+ const DEFAULTS: Partial<Args> = {
44
+ arch: "aarch64",
45
+ watch: false,
46
+ audit: false,
47
+ check: false,
48
+ };
49
+
50
+ function set(args: Args, key: string, value: string | boolean): void {
51
+ (args as unknown as Record<string, string | boolean>)[key] = value;
52
+ }
53
+
54
+ /** Parse `argv` (excluding the node binary and script path) into an `Args` object. */
55
+ export function parse_args(argv: string[] = process.argv.slice(2)): Args {
56
+ const args: Args = { command: undefined, ...DEFAULTS } as Args;
57
+ const positional: string[] = [];
58
+ let only_positional = false;
59
+
60
+ const next_value = (i: number, spelling: string): string => {
61
+ if (i + 1 >= argv.length) throw new Error(`Option ${spelling} requires a value`);
62
+ return argv[i + 1];
63
+ };
64
+
65
+ for (let i = 0; i < argv.length; i++) {
66
+ const arg = argv[i];
67
+
68
+ if (only_positional) {
69
+ positional.push(arg);
70
+ continue;
71
+ }
72
+
73
+ if (arg === "--") {
74
+ only_positional = true;
75
+ continue;
76
+ }
77
+
78
+ if (arg === "-h" || arg === "--help") {
79
+ print_help();
80
+ process.exit(0);
81
+ }
82
+
83
+ // Long option: --name, --name=value, or --name value
84
+ if (arg.startsWith("--") && arg.length > 2) {
85
+ const eq = arg.indexOf("=");
86
+ const name = eq === -1 ? arg.slice(2) : arg.slice(2, eq);
87
+ const inline = eq === -1 ? undefined : arg.slice(eq + 1);
88
+
89
+ if (STRING_OPTIONS.has(name)) {
90
+ const key = STRING_OPTIONS.get(name)!;
91
+ set(args, key, inline !== undefined ? inline : next_value(i, `--${name}`));
92
+ if (inline === undefined) i++;
93
+ } else if (BOOLEAN_OPTIONS.has(name)) {
94
+ const key = BOOLEAN_OPTIONS.get(name)!;
95
+ set(args, key, inline === undefined ? true : inline !== "false");
96
+ } else {
97
+ throw new Error(`Unknown option: --${name}`);
98
+ }
99
+ continue;
100
+ }
101
+
102
+ // Short option(s): -i value, -ivalue, -i=value, or combined -wv
103
+ if (arg.startsWith("-") && arg.length > 1 && !/^-\d/.test(arg)) {
104
+ const chars = arg.slice(1);
105
+ for (let j = 0; j < chars.length; j++) {
106
+ const c = chars[j];
107
+
108
+ if (c === "h") {
109
+ print_help();
110
+ process.exit(0);
111
+ }
112
+
113
+ if (STRING_OPTIONS.has(c)) {
114
+ const key = STRING_OPTIONS.get(c)!;
115
+ const rest = chars.slice(j + 1);
116
+ if (rest.length > 0) {
117
+ set(args, key, rest.startsWith("=") ? rest.slice(1) : rest);
118
+ } else {
119
+ set(args, key, next_value(i, `-${c}`));
120
+ i++;
121
+ }
122
+ break;
123
+ }
124
+
125
+ if (BOOLEAN_OPTIONS.has(c)) {
126
+ set(args, BOOLEAN_OPTIONS.get(c)!, true);
127
+ continue;
128
+ }
129
+
130
+ throw new Error(`Unknown option: -${c}`);
131
+ }
132
+ continue;
133
+ }
134
+
135
+ positional.push(arg);
136
+ }
137
+
138
+ args.command = positional[0];
139
+ args.name = positional[1];
140
+ return args;
141
+ }
142
+
143
+ export function print_help(): void {
144
+ console.log(
145
+ [
146
+ "Usage:",
147
+ " nomen run --in [file/folder] Parse, check, build and run a program",
148
+ " nomen build --in [file/folder] Parse, check and build (no run)",
149
+ " nomen check --in [file/folder] Parse and check only",
150
+ " nomen format [--in folder] Reformat every .nm file",
151
+ " nomen docs [--in file] Generate markdown documentation",
152
+ " nomen test [--in folder] Discover and run *.test.nm files",
153
+ " nomen init <name> Scaffold a new project in ./<name>",
154
+ " nomen lib-path Print the bundled System library path",
155
+ "",
156
+ "Options:",
157
+ " --in, -i <path> Input file or folder",
158
+ " --out, -o <path> Output file",
159
+ " --config, -c <path> Path to a config file",
160
+ " --watch, -w Watch for file changes",
161
+ " --filter, -f <regex> Only run test files whose path matches this regex",
162
+ " --arch, -a <arch> Target architecture: aarch64 | c (default: aarch64)",
163
+ " --platform, -p <name> Target platform: macos, ios, linux, android, windows, web",
164
+ " --lib, -l <path> Path to System library directory (containing package.jsonc)",
165
+ " --audit Audit the generated program for memory issues",
166
+ " --audit-runtime <path> Path to audit_runtime.c, linked in when --audit is set",
167
+ " --check For `nomen format`: report files that would change",
168
+ " -h, --help Show this help",
169
+ ].join("\n"),
170
+ );
171
+ }
package/src/index.ts CHANGED
@@ -4,16 +4,16 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
6
  import chokidar from "chokidar";
7
- import { hideBin } from "yargs/helpers";
8
- import yargs from "yargs/yargs";
9
7
 
10
8
  import build, { default_platform } from "../../src/build.ts";
11
9
  import { format_source, type FormatOptions } from "../../src/format.ts";
12
10
  import join from "../../src/join.ts";
13
11
  import { get_library } from "../../src/lib.ts";
14
12
  import parse from "../../src/parse.ts";
13
+ import { parse_args, print_help, type Args } from "./args.ts";
15
14
  import { run_docs } from "./docs.ts";
16
15
  import render_errors, { render_warnings } from "./format_errors.ts";
16
+ import { find_bundled, run_init } from "./init.ts";
17
17
  import { runTests } from "./test.ts";
18
18
  import type Config from "./types/Config.ts";
19
19
 
@@ -73,100 +73,48 @@ let build_root: string | undefined;
73
73
 
74
74
  console.log("\n~ NOMEN ~\n");
75
75
 
76
- const parser = yargs(hideBin(process.argv))
77
- .usage(
78
- "Usage:\n" +
79
- " nomen run --in [file/folder] Parse, check, build and run a program\n" +
80
- " nomen build --in [file/folder] Parse, check and build (no run)\n" +
81
- " nomen check --in [file/folder] Parse and check only\n" +
82
- " nomen format [--in folder] Reformat every .nm file\n" +
83
- " nomen docs [--in file] Generate markdown documentation",
84
- )
85
- .command("run", "Parse, check, build and run a program")
86
- .command("build", "Parse, check and build (compile and link, but do not run)")
87
- .command("check", "Parse and check only")
88
- .command("format", "Reformat every .nm file")
89
- .command("docs", "Generate markdown documentation")
90
- .command("test", "Discover and run *.test.nm files with the Tester harness")
91
- .option("in", {
92
- alias: "i",
93
- describe: "Input file or folder",
94
- type: "string",
95
- })
96
- .option("out", {
97
- alias: "o",
98
- describe: "Output file",
99
- type: "string",
100
- })
101
- .option("config", {
102
- alias: "c",
103
- describe: "The path to a config file",
104
- type: "string",
105
- })
106
- .option("watch", {
107
- alias: "w",
108
- describe: "Whether to watch for file changes",
109
- type: "boolean",
110
- })
111
- .option("filter", {
112
- alias: "f",
113
- describe: "Only run test files whose path matches this regex",
114
- type: "string",
115
- })
116
- .option("arch", {
117
- alias: "a",
118
- describe: "Target architecture (aarch64 or c)",
119
- type: "string",
120
- default: "aarch64",
121
- })
122
- .option("platform", {
123
- alias: "p",
124
- describe: "Target platform (macos, ios, linux, android, windows, web)",
125
- type: "string",
126
- })
127
- .option("lib", {
128
- alias: "l",
129
- describe: "Path to System library directory (containing package.jsonc)",
130
- type: "string",
131
- })
132
- .option("audit", {
133
- describe: "Whether to audit the generated program for memory issues",
134
- type: "boolean",
135
- })
136
- .option("audit-runtime", {
137
- describe: "Path to audit_runtime.c, linked in when --audit is set",
138
- type: "string",
139
- })
140
- .option("check", {
141
- describe: "For `nomen format`: report files that would change without writing them",
142
- type: "boolean",
143
- })
144
- .help(true);
145
-
146
- const options = parser.parseSync();
147
-
148
- const command = options._[0];
76
+ const args: Args = parse_args();
77
+ const command = args.command;
149
78
 
150
79
  try {
80
+ // `nomen init <name>` scaffolds a new project under ./<name>.
81
+ if (command === "init") {
82
+ run_init(args.name);
83
+ process.exit(0);
84
+ }
85
+
86
+ // `nomen lib-path` prints the absolute path to the System library
87
+ // bundled with this CLI. Used by tooling (e.g. the VS Code extension)
88
+ // to locate the standard library for projects without a local one.
89
+ if (command === "lib-path") {
90
+ const lib = find_bundled("core");
91
+ if (lib) {
92
+ process.stdout.write(lib + "\n");
93
+ process.exit(0);
94
+ }
95
+ console.log("No bundled System library found alongside the CLI.");
96
+ process.exit(1);
97
+ }
98
+
151
99
  // `nomen docs` generates markdown documentation instead of compiling.
152
100
  if (command === "docs") {
153
- run_docs(typeof options.in === "string" ? options.in : undefined);
101
+ run_docs(args.in);
154
102
  process.exit(0);
155
103
  }
156
104
 
157
105
  // `nomen test` discovers and runs every `*.test.nm` file under --in (or
158
106
  // the cwd), compiling each into a Tester harness and reporting results.
159
107
  if (command === "test") {
160
- const root = options.in ?? process.cwd();
161
- const filter = typeof options.filter === "string" ? new RegExp(options.filter) : undefined;
162
- const arch = (options.arch as string | undefined) ?? "aarch64";
108
+ const root = args.in ?? process.cwd();
109
+ const filter = args.filter ? new RegExp(args.filter) : undefined;
110
+ const arch = args.arch ?? "aarch64";
163
111
  const ok = runTests(root, { arch, filter });
164
112
  process.exit(ok ? 0 : 1);
165
113
  }
166
114
 
167
115
  // `nomen format` re-indents and tidies every .nm file under a folder.
168
116
  if (command === "format") {
169
- const root = options.in ?? process.cwd();
117
+ const root = args.in ?? process.cwd();
170
118
  const format_options = load_format_options(root);
171
119
  const files = collect_nm_files(root);
172
120
  let changed = 0;
@@ -178,7 +126,7 @@ try {
178
126
  continue;
179
127
  }
180
128
  if (result.changed) {
181
- if (!options.check) fs.writeFileSync(file, result.code);
129
+ if (!args.check) fs.writeFileSync(file, result.code);
182
130
  changed += 1;
183
131
  console.log(`Formatted ${file}`);
184
132
  }
@@ -196,59 +144,59 @@ try {
196
144
  else if (command === "check") mode = "check";
197
145
 
198
146
  if (!mode) {
199
- parser.showHelp("log");
147
+ print_help();
200
148
  process.exit(1);
201
149
  }
202
150
 
203
151
  // An explicit --in wins; otherwise discover what to compile from the
204
152
  // working folder — a package.jsonc `entry`, or a lone .nm file.
205
- options.in = options.in ?? resolve_input();
206
- if (!options.in) {
153
+ args.in = args.in ?? resolve_input();
154
+ if (!args.in) {
207
155
  process.exit(0);
208
156
  }
209
157
  // For an explicit --in, build next to whatever was passed (the folder
210
158
  // itself, or the file's folder). Discovery cases set build_root themselves.
211
159
  if (!build_root) {
212
- build_root = fs.lstatSync(options.in).isDirectory() ? options.in : path.dirname(options.in);
160
+ build_root = fs.lstatSync(args.in).isDirectory() ? args.in : path.dirname(args.in);
213
161
  }
214
162
 
215
- if (fs.existsSync(options.in)) {
163
+ if (fs.existsSync(args.in)) {
216
164
  let config: Config = { arch: "aarch64", platform: default_platform() };
217
165
  // Load the config from a file
218
- if (options.config && fs.existsSync(options.config)) {
219
- config = JSON.parse(fs.readFileSync(options.config, "utf-8"));
166
+ if (args.config && fs.existsSync(args.config)) {
167
+ config = JSON.parse(fs.readFileSync(args.config, "utf-8"));
220
168
  }
221
169
  // Overwrite with args
222
- if (options.arch) config.arch = options.arch as "aarch64" | "c";
223
- if (options.platform) config.platform = options.platform as string;
224
- if (options.lib) config.lib = options.lib;
225
- if (options.audit) config.audit = options.audit;
226
- if (options["audit-runtime"]) config.audit_runtime = options["audit-runtime"];
170
+ if (args.arch) config.arch = args.arch as "aarch64" | "c";
171
+ if (args.platform) config.platform = args.platform;
172
+ if (args.lib) config.lib = args.lib;
173
+ if (args.audit) config.audit = args.audit;
174
+ if (args.audit_runtime) config.audit_runtime = args.audit_runtime;
227
175
 
228
176
  // Is the --in path a folder
229
- if (fs.lstatSync(options.in).isDirectory()) {
230
- if (options.watch) {
231
- watchPath(options.in, config, mode);
177
+ if (fs.lstatSync(args.in).isDirectory()) {
178
+ if (args.watch) {
179
+ watchPath(args.in, config, mode);
232
180
  } else {
233
- processFolder(options.in, config, mode);
181
+ processFolder(args.in, config, mode);
234
182
  }
235
183
  } else {
236
184
  // Process the supplied file
237
- const extname = path.extname(options.in);
238
- if (shouldProcessFile(options.in)) {
185
+ const extname = path.extname(args.in);
186
+ if (shouldProcessFile(args.in)) {
239
187
  // NOTE: We get add notifications for all watched files immediately
240
188
  // TODO: Is this the case on Windows etc too?
241
- if (options.watch) {
242
- watchPath(options.in, config, mode);
189
+ if (args.watch) {
190
+ watchPath(args.in, config, mode);
243
191
  } else {
244
- processFile(options.in, config, mode);
192
+ processFile(args.in, config, mode);
245
193
  }
246
194
  } else {
247
195
  console.log("Unsupported file type: " + extname);
248
196
  }
249
197
  }
250
198
  } else {
251
- console.log("Path not found: " + options.in);
199
+ console.log("Path not found: " + args.in);
252
200
  }
253
201
  } catch (err) {
254
202
  console.log("UH", err);
@@ -320,7 +268,8 @@ function resolve_lib(file_path: string): string | undefined {
320
268
  if (parent === dir) break;
321
269
  dir = parent;
322
270
  }
323
- return undefined;
271
+ // Nothing local — fall back to the System library bundled with the CLI.
272
+ return find_bundled("core");
324
273
  }
325
274
 
326
275
  function resolve_audit_runtime(config: Config, input_path: string): string | undefined {
package/src/init.ts ADDED
@@ -0,0 +1,128 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ // A name must be a portable folder name: letters, digits, underscore and
6
+ // hyphen only. Anything else (slashes, spaces, dots) would either break the
7
+ // filesystem layout or produce an invalid package name.
8
+ const NAME_RE = /^[A-Za-z][A-Za-z0-9_-]*$/;
9
+
10
+ const GITIGNORE = `build/
11
+ `;
12
+
13
+ function package_jsonc(name: string): string {
14
+ return `{
15
+ "name": "${name}",
16
+ "entry": "src/main.nm"
17
+ // The System library is resolved automatically from your nomen-lang
18
+ // install. To pin a local checkout instead, uncomment:
19
+ // "imports": { "System": "../core" }
20
+ }
21
+ `;
22
+ }
23
+
24
+ const MAIN_NM = `import System
25
+
26
+ pub func main = (Init init) {
27
+ Console.write("Hello world!\\n")
28
+ }
29
+ `;
30
+
31
+ const TEST_NM = `import System
32
+ import System/Test
33
+
34
+ func add = (int a, int b, out int) => a + b
35
+
36
+ pub func test_smoke = (ref Tester t) {
37
+ t.expect(add(2, 2) == 4, "2 + 2 should equal 4")
38
+ }
39
+ `;
40
+
41
+ function readme(name: string): string {
42
+ return `# ${name}
43
+
44
+ A [Nomen](https://github.com/andrewjk/nomen) project.
45
+
46
+ ## Build
47
+
48
+ \`\`\`bash
49
+ nomen build
50
+ \`\`\`
51
+
52
+ ## Run
53
+
54
+ \`\`\`bash
55
+ nomen run
56
+ \`\`\`
57
+
58
+ ## Test
59
+
60
+ \`\`\`bash
61
+ nomen test
62
+ \`\`\`
63
+ `;
64
+ }
65
+
66
+ // Resolve a bundled asset (core/ or NOMEN_AGENTS.md) shipped alongside the
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.
69
+ export function find_bundled(filename: string): string | undefined {
70
+ const here = path.dirname(fileURLToPath(import.meta.url));
71
+ const cli_root = path.dirname(here);
72
+ const candidates = [path.join(cli_root, filename), path.join(cli_root, "..", filename)];
73
+ for (const c of candidates) {
74
+ if (fs.existsSync(c)) return path.resolve(c);
75
+ }
76
+ return undefined;
77
+ }
78
+
79
+ /**
80
+ * `nomen init <name>`: scaffold a new project under `./<name>` with a sensible
81
+ * starting layout (package.jsonc, src/main.nm, a starter test, README and an
82
+ * AGENTS.md copied from the bundled NOMEN_AGENTS.md template).
83
+ */
84
+ export function run_init(name: string | undefined): void {
85
+ if (!name) {
86
+ console.log("Usage: nomen init <name>");
87
+ process.exit(1);
88
+ }
89
+ if (!NAME_RE.test(name)) {
90
+ console.log(
91
+ `Invalid project name "${name}". Use letters, digits, '-' or '_' (must start with a letter).`,
92
+ );
93
+ process.exit(1);
94
+ }
95
+
96
+ const target = path.resolve(process.cwd(), name);
97
+ if (fs.existsSync(target)) {
98
+ console.log(`Already exists: ${target}`);
99
+ process.exit(1);
100
+ }
101
+
102
+ const agents_template = find_bundled("NOMEN_AGENTS.md");
103
+ if (!agents_template) {
104
+ console.log("Could not locate NOMEN_AGENTS.md template alongside the CLI.");
105
+ process.exit(1);
106
+ }
107
+
108
+ const src = path.join(target, "src");
109
+ const test = path.join(target, "test");
110
+ fs.mkdirSync(src, { recursive: true });
111
+ fs.mkdirSync(test, { recursive: true });
112
+
113
+ fs.writeFileSync(path.join(target, ".gitignore"), GITIGNORE);
114
+ fs.writeFileSync(path.join(target, "package.jsonc"), package_jsonc(name));
115
+ fs.writeFileSync(path.join(src, "main.nm"), MAIN_NM);
116
+ fs.writeFileSync(path.join(test, "smoke.test.nm"), TEST_NM);
117
+ fs.writeFileSync(path.join(target, "README.md"), readme(name));
118
+ fs.writeFileSync(path.join(target, "AGENTS.md"), fs.readFileSync(agents_template));
119
+
120
+ console.log(`Created ${name}/`);
121
+ console.log(` ${name}/.gitignore`);
122
+ console.log(` ${name}/package.jsonc`);
123
+ console.log(` ${name}/src/main.nm`);
124
+ console.log(` ${name}/test/smoke.test.nm`);
125
+ console.log(` ${name}/README.md`);
126
+ console.log(` ${name}/AGENTS.md`);
127
+ console.log(`\nNext: cd ${name} && nomen run`);
128
+ }
package/src/test.ts CHANGED
@@ -7,6 +7,7 @@ import build from "../../src/build.ts";
7
7
  import join from "../../src/join.ts";
8
8
  import { get_library } from "../../src/lib.ts";
9
9
  import parse from "../../src/parse.ts";
10
+ import { find_bundled } from "./init.ts";
10
11
 
11
12
  const RECORD_PREFIX = "\\nomen|";
12
13
 
@@ -504,8 +505,10 @@ function resolve_lib_for(root: string): string | undefined {
504
505
  }
505
506
  }
506
507
  }
508
+ // A bare `<dir>/System` is only the library if it has a package.jsonc;
509
+ // otherwise we'd match macOS's `/System` on the way to the root.
507
510
  const systemDir = path.join(dir, "System");
508
- if (fs.existsSync(systemDir)) return systemDir;
511
+ if (fs.existsSync(path.join(systemDir, "package.jsonc"))) return systemDir;
509
512
  const globalSystem = path.join(dir, "core", "System");
510
513
  if (fs.existsSync(globalSystem)) {
511
514
  return path.join(dir, "core");
@@ -514,5 +517,6 @@ function resolve_lib_for(root: string): string | undefined {
514
517
  if (parent === dir) break;
515
518
  dir = parent;
516
519
  }
517
- return undefined;
520
+ // Nothing local — fall back to the System library bundled with the CLI.
521
+ return find_bundled("core");
518
522
  }
Binary file