recipe-tmlanguage 0.3.3 → 0.3.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.
@@ -0,0 +1,37 @@
1
+ import { buildGrammar, serializeGrammar } from "#grammar";
2
+ import { command, flag } from "@kjanat/dreamcli";
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { dirname, resolve } from "node:path";
6
+ import { cwd } from "node:process";
7
+
8
+ const indentOf = (raw: string): "tab" | number => (raw === "tab" ? "tab" : Number(raw));
9
+ const require = createRequire(import.meta.url);
10
+ const DEFAULT_OUT = `${dirname(require.resolve("#pkg"))}/recipe.tmLanguage.json`;
11
+
12
+ export const generateCmd = command("generate")
13
+ .description("Build the TextMate grammar from the tree-sitter-recipe vocabulary")
14
+ .flag("out", flag.string().alias("o").default(DEFAULT_OUT).describe("Output JSON path"))
15
+ .flag("indent", flag.enum(["tab", "2", "4"]).default("tab").describe("JSON indent"))
16
+ .flag("quiet", flag.boolean().alias("q").default(false).describe("Suppress stats on success"))
17
+ .action(({ flags, out }) => {
18
+ const { grammar, stats } = buildGrammar();
19
+ const serialized = serializeGrammar(grammar, indentOf(flags.indent));
20
+ const outAbs = resolve(cwd(), flags.out);
21
+ mkdirSync(dirname(outAbs), { recursive: true });
22
+ writeFileSync(outAbs, serialized);
23
+ const { json, jsonMode, log } = out;
24
+
25
+ if (jsonMode) {
26
+ json({ ok: true, outPath: outAbs, bytes: serialized.length, stats });
27
+ return;
28
+ }
29
+ if (flags.quiet) return;
30
+
31
+ log(`wrote ${outAbs}`);
32
+ log(` ${stats.topLevelPatterns} top-level patterns · ${serialized.length} bytes`);
33
+ const v = stats.vocab;
34
+ log(
35
+ ` vocab: ${v.frequency} frequency · ${v.timing.single}+${v.timing.multi} timing · ${v.route.single}+${v.route.multi} route · ${v.dispensing.single}+${v.dispensing.multi} dispensing · ${v.forms.single}+${v.forms.multi} forms · ${v.compounding.single}+${v.compounding.multi} compounding · ${v.conditional.single}+${v.conditional.multi} conditional · ${v.warning} warning · ${v.units} units`,
36
+ );
37
+ });
@@ -0,0 +1,53 @@
1
+ import { verify } from "#verifier";
2
+ import { command, flag } from "@kjanat/dreamcli";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, resolve } from "node:path";
5
+ import { cwd, exit } from "node:process";
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const TS_RX_DIR = resolve(dirname(require.resolve("tree-sitter-recipe/package.json")));
9
+ const DEFAULT_FIXTURES_DIR = resolve(TS_RX_DIR, "test/highlight");
10
+ const DEFAULT_ONIG_WASM = require.resolve("vscode-oniguruma/release/onig.wasm");
11
+ const DEFAULT_OUT = `${dirname(require.resolve("#pkg"))}/recipe.tmLanguage.json`;
12
+
13
+ export const verifyCmd = command("verify")
14
+ .description("Tokenize tree-sitter-recipe highlight fixtures and assert scope matches")
15
+ .flag("grammar", flag.string().alias("g").default(DEFAULT_OUT).describe("Path to .tmLanguage.json"))
16
+ .flag("fixtures", flag.string().alias("f").default(DEFAULT_FIXTURES_DIR).describe("Directory of .recipe fixtures"))
17
+ .flag("onig-wasm", flag.string().default(DEFAULT_ONIG_WASM).describe("Path to oniguruma WASM"))
18
+ .flag("max-failures", flag.number().default(40).describe("Max failures to print (0 = all)"))
19
+ .action(async ({ flags, out }) => {
20
+ const result = await verify({
21
+ grammarPath: resolve(cwd(), flags.grammar),
22
+ fixturesDir: resolve(cwd(), flags.fixtures),
23
+ onigWasmPath: resolve(cwd(), flags["onig-wasm"]),
24
+ });
25
+ const failuresLen = result.failures.length;
26
+ const { json, jsonMode, setExitCode, log } = out;
27
+
28
+ if (jsonMode) {
29
+ json(result);
30
+ if (failuresLen > 0) {
31
+ setExitCode(1);
32
+ exit();
33
+ }
34
+ return;
35
+ }
36
+
37
+ log(`${result.pass} / ${result.total} assertions pass`);
38
+ if (failuresLen === 0) return;
39
+
40
+ log("");
41
+ log("── failures ──");
42
+ const limit = flags["max-failures"] === 0 ? failuresLen : flags["max-failures"];
43
+ for (const f of result.failures.slice(0, limit)) {
44
+ const gotStr = f.got
45
+ ? f.got.filter((s) => s !== "source.recipe").join(" · ") || "(root only)"
46
+ : "(no token)";
47
+ log(` ${f.fixture}:${f.line}:${f.col} expected ${f.capture} got [${gotStr}]`);
48
+ }
49
+ if (failuresLen > limit) {
50
+ log(` … +${failuresLen - limit} more`);
51
+ }
52
+ setExitCode(1);
53
+ });
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
- import { dirname, resolve } from "node:path";
5
- import { cwd, exit } from "node:process";
6
- import { fileURLToPath } from "node:url";
7
- import { cli, command, flag } from "dreamcli";
8
3
  import { COUNTERS, NUMBER_WORDS, PERIODS, PERIOD_PLURALS } from "tree-sitter-recipe/grammar/dutch";
9
4
  import { COMPOUNDING, COMPOUNDING_MULTIWORD, CONDITIONAL, CONDITIONAL_MULTIWORD, DISPENSING, DISPENSING_MULTIWORD, FORMS, FORMS_MULTIWORD, FREQUENCY, ROUTE, ROUTE_MULTIWORD, TIMING, TIMING_MULTIWORD, WARNING } from "tree-sitter-recipe/grammar/latin";
10
5
  import { UNITS } from "tree-sitter-recipe/grammar/units";
6
+ import { cli, command, flag } from "@kjanat/dreamcli";
7
+ import { mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
8
+ import { dirname, resolve } from "node:path";
9
+ import { cwd, exit } from "node:process";
11
10
  //#region src/grammar.ts
12
11
  /**
13
12
  * @file Pure grammar builder — imports the tree-sitter-recipe vocabulary and
@@ -45,7 +44,7 @@ const SCOPE = {
45
44
  const REGEX_METACHARS = /[.*+?^${}()|[\]\\]/g;
46
45
  const escapeRegex = (s) => s.replace(REGEX_METACHARS, "\\$&");
47
46
  const alt = (items) => [...new Set(items)].sort((a, b) => b.length - a.length).map(escapeRegex).join("|");
48
- const altMultiword = (items) => [...new Set(items)].sort((a, b) => b.length - a.length).map((s) => s.replace(/\./g, "\\.").replace(/\s+/g, "\\s+")).join("|");
47
+ const altMultiword = (items) => [...new Set(items)].sort((a, b) => b.length - a.length).map((s) => escapeRegex(s).replace(/\s+/g, "\\s+")).join("|");
49
48
  const wb = (pattern) => `(?<![\\w.])(?:${pattern})(?![\\w.])`;
50
49
  function buildGrammar() {
51
50
  const doseMatch = {
@@ -267,6 +266,36 @@ function serializeGrammar(g, indent) {
267
266
  return `${JSON.stringify(g, null, indent === "tab" ? " " : indent)}\n`;
268
267
  }
269
268
  //#endregion
269
+ //#region bin/commands/generate.ts
270
+ const indentOf = (raw) => raw === "tab" ? "tab" : Number(raw);
271
+ const DEFAULT_OUT$1 = `${dirname(createRequire(import.meta.url).resolve("#pkg"))}/recipe.tmLanguage.json`;
272
+ const generateCmd = command("generate").description("Build the TextMate grammar from the tree-sitter-recipe vocabulary").flag("out", flag.string().alias("o").default(DEFAULT_OUT$1).describe("Output JSON path")).flag("indent", flag.enum([
273
+ "tab",
274
+ "2",
275
+ "4"
276
+ ]).default("tab").describe("JSON indent")).flag("quiet", flag.boolean().alias("q").default(false).describe("Suppress stats on success")).action(({ flags, out }) => {
277
+ const { grammar, stats } = buildGrammar();
278
+ const serialized = serializeGrammar(grammar, indentOf(flags.indent));
279
+ const outAbs = resolve(cwd(), flags.out);
280
+ mkdirSync(dirname(outAbs), { recursive: true });
281
+ writeFileSync(outAbs, serialized);
282
+ const { json, jsonMode, log } = out;
283
+ if (jsonMode) {
284
+ json({
285
+ ok: true,
286
+ outPath: outAbs,
287
+ bytes: serialized.length,
288
+ stats
289
+ });
290
+ return;
291
+ }
292
+ if (flags.quiet) return;
293
+ log(`wrote ${outAbs}`);
294
+ log(` ${stats.topLevelPatterns} top-level patterns · ${serialized.length} bytes`);
295
+ const v = stats.vocab;
296
+ log(` vocab: ${v.frequency} frequency · ${v.timing.single}+${v.timing.multi} timing · ${v.route.single}+${v.route.multi} route · ${v.dispensing.single}+${v.dispensing.multi} dispensing · ${v.forms.single}+${v.forms.multi} forms · ${v.compounding.single}+${v.compounding.multi} compounding · ${v.conditional.single}+${v.conditional.multi} conditional · ${v.warning} warning · ${v.units} units`);
297
+ });
298
+ //#endregion
270
299
  //#region src/verifier.ts
271
300
  /**
272
301
  * @file Pure verifier — tokenizes tree-sitter-recipe's own highlight fixtures
@@ -276,9 +305,9 @@ function serializeGrammar(g, indent) {
276
305
  * No CLI concerns here; the caller supplies paths and decides how to present
277
306
  * the result (text table / JSON / exit code).
278
307
  */
279
- const require = createRequire(import.meta.url);
280
- const oniguruma = require("vscode-oniguruma");
281
- const { parseRawGrammar, Registry } = require("vscode-textmate");
308
+ const require$1 = createRequire(import.meta.url);
309
+ const oniguruma = require$1("vscode-oniguruma");
310
+ const { parseRawGrammar, Registry } = require$1("vscode-textmate");
282
311
  const CAPTURE_EXPECTS = {
283
312
  "keyword.directive": "keyword.control.directive",
284
313
  "keyword.repeat": "keyword.other.frequency",
@@ -340,7 +369,7 @@ async function verify(opts) {
340
369
  const rawGrammar = parseRawGrammar(readFileSync(opts.grammarPath, "utf-8"), opts.grammarPath);
341
370
  const grammar = await new Registry({
342
371
  onigLib,
343
- loadGrammar: async () => null
372
+ loadGrammar: () => Promise.resolve(null)
344
373
  }).addGrammar(rawGrammar);
345
374
  const result = {
346
375
  pass: 0,
@@ -380,55 +409,11 @@ async function verify(opts) {
380
409
  return result;
381
410
  }
382
411
  //#endregion
383
- //#region package.json
384
- var version = "0.3.3";
385
- var homepage = "https://github.com/kjanat/recipe-tmlanguage#recipe-tmlanguage";
386
- var repository = {
387
- "type": "git",
388
- "url": "git+https://github.com/kjanat/recipe-tmlanguage.git"
389
- };
390
- //#endregion
391
- //#region bin/recipe-tmlang.ts
392
- /**
393
- * recipe-tmlang — TextMate grammar generator & verifier for recipe-tmlanguage.
394
- *
395
- * Subcommands
396
- * - generate: Build dist/recipe.tmLanguage.json from the tree-sitter-recipe vocab.
397
- * - verify: Tokenize tree-sitter-recipe's highlight fixtures and assert scopes.
398
- *
399
- * Zero manual argparse — argument parsing, help, and completions all come from
400
- * {@link https://github.com/kjanat/dreamcli | DreamCLI}. `--json` is a DreamCLI built-in;
401
- * we branch on {@linkcode Out.jsonMode}.
402
- */
403
- const DEFAULT_OUT = `${resolve(import.meta.dirname, "..")}/recipe.tmLanguage.json`;
404
- const DEFAULT_FIXTURES_DIR = resolve(resolve(dirname(fileURLToPath(import.meta.resolve("tree-sitter-recipe/package.json")))), "test/highlight");
405
- const DEFAULT_ONIG_WASM = fileURLToPath(import.meta.resolve("vscode-oniguruma/release/onig.wasm"));
406
- const indentOf = (raw) => raw === "tab" ? "tab" : Number(raw);
407
- const generate = command("generate").description("Build the TextMate grammar from the tree-sitter-recipe vocabulary").flag("out", flag.string().alias("o").default(DEFAULT_OUT).describe("Output JSON path")).flag("indent", flag.enum([
408
- "tab",
409
- "2",
410
- "4"
411
- ]).default("tab").describe("JSON indent")).flag("quiet", flag.boolean().alias("q").default(false).describe("Suppress stats on success")).action(({ flags, out }) => {
412
- const { grammar, stats } = buildGrammar();
413
- const serialized = serializeGrammar(grammar, indentOf(flags.indent));
414
- const outAbs = resolve(cwd(), flags.out);
415
- mkdirSync(dirname(outAbs), { recursive: true });
416
- writeFileSync(outAbs, serialized);
417
- if (out.jsonMode) {
418
- out.json({
419
- ok: true,
420
- outPath: outAbs,
421
- bytes: serialized.length,
422
- stats
423
- });
424
- return;
425
- }
426
- if (flags.quiet) return;
427
- out.log(`wrote ${outAbs}`);
428
- out.log(` ${stats.topLevelPatterns} top-level patterns · ${serialized.length} bytes`);
429
- const v = stats.vocab;
430
- out.log(` vocab: ${v.frequency} frequency · ${v.timing.single}+${v.timing.multi} timing · ${v.route.single}+${v.route.multi} route · ${v.dispensing.single}+${v.dispensing.multi} dispensing · ${v.forms.single}+${v.forms.multi} forms · ${v.compounding.single}+${v.compounding.multi} compounding · ${v.conditional.single}+${v.conditional.multi} conditional · ${v.warning} warning · ${v.units} units`);
431
- });
412
+ //#region bin/commands/verify.ts
413
+ const require = createRequire(import.meta.url);
414
+ const DEFAULT_FIXTURES_DIR = resolve(resolve(dirname(require.resolve("tree-sitter-recipe/package.json"))), "test/highlight");
415
+ const DEFAULT_ONIG_WASM = require.resolve("vscode-oniguruma/release/onig.wasm");
416
+ const DEFAULT_OUT = `${dirname(require.resolve("#pkg"))}/recipe.tmLanguage.json`;
432
417
  const verifyCmd = command("verify").description("Tokenize tree-sitter-recipe highlight fixtures and assert scope matches").flag("grammar", flag.string().alias("g").default(DEFAULT_OUT).describe("Path to .tmLanguage.json")).flag("fixtures", flag.string().alias("f").default(DEFAULT_FIXTURES_DIR).describe("Directory of .recipe fixtures")).flag("onig-wasm", flag.string().default(DEFAULT_ONIG_WASM).describe("Path to oniguruma WASM")).flag("max-failures", flag.number().default(40).describe("Max failures to print (0 = all)")).action(async ({ flags, out }) => {
433
418
  const result = await verify({
434
419
  grammarPath: resolve(cwd(), flags.grammar),
@@ -436,31 +421,48 @@ const verifyCmd = command("verify").description("Tokenize tree-sitter-recipe hig
436
421
  onigWasmPath: resolve(cwd(), flags["onig-wasm"])
437
422
  });
438
423
  const failuresLen = result.failures.length;
439
- if (out.jsonMode) {
440
- out.json(result);
424
+ const { json, jsonMode, setExitCode, log } = out;
425
+ if (jsonMode) {
426
+ json(result);
441
427
  if (failuresLen > 0) {
442
- out.setExitCode(1);
428
+ setExitCode(1);
443
429
  exit();
444
430
  }
445
431
  return;
446
432
  }
447
- out.log(`${result.pass} / ${result.total} assertions pass`);
433
+ log(`${result.pass} / ${result.total} assertions pass`);
448
434
  if (failuresLen === 0) return;
449
- out.log("");
450
- out.log("── failures ──");
435
+ log("");
436
+ log("── failures ──");
451
437
  const limit = flags["max-failures"] === 0 ? failuresLen : flags["max-failures"];
452
438
  for (const f of result.failures.slice(0, limit)) {
453
439
  const gotStr = f.got ? f.got.filter((s) => s !== "source.recipe").join(" · ") || "(root only)" : "(no token)";
454
- out.log(` ${f.fixture}:${f.line}:${f.col} expected ${f.capture} got [${gotStr}]`);
440
+ log(` ${f.fixture}:${f.line}:${f.col} expected ${f.capture} got [${gotStr}]`);
455
441
  }
456
- if (failuresLen > limit) out.log(` … +${failuresLen - limit} more`);
457
- out.setExitCode(1);
442
+ if (failuresLen > limit) log(` … +${failuresLen - limit} more`);
443
+ setExitCode(1);
458
444
  });
459
- const app = cli("recipe-tmlang").packageJson({
460
- repository,
461
- homepage,
462
- version
463
- }).links().description("TextMate grammar generator & verifier for the recipe DSL").command(generate).command(verifyCmd).completions();
445
+ //#endregion
446
+ //#region bin/recipe-tmlang.ts
447
+ /**
448
+ * recipe-tmlang — TextMate grammar generator & verifier for recipe-tmlanguage.
449
+ *
450
+ * Subcommands
451
+ * - generate: Build dist/recipe.tmLanguage.json from the tree-sitter-recipe vocab.
452
+ * - verify: Tokenize tree-sitter-recipe's highlight fixtures and assert scopes.
453
+ *
454
+ * Zero manual argparse — argument parsing, help, and completions all come from
455
+ * {@link https://github.com/kjanat/dreamcli | DreamCLI}. `--json` is a DreamCLI built-in;
456
+ * we branch on {@linkcode out.jsonMode | https://dreamcli.kjanat.com/reference/symbols/main/Out#jsonmode}.
457
+ */
458
+ const app = cli("recipe-tmlanguage").packageJson({
459
+ repository: {
460
+ "type": "git",
461
+ "url": "git+https://github.com/kjanat/recipe-tmlanguage.git"
462
+ },
463
+ homepage: "https://github.com/kjanat/recipe-tmlanguage#recipe-tmlanguage",
464
+ version: "0.3.4"
465
+ }).links().description("TextMate grammar generator & verifier for the recipe DSL").command(generateCmd).command(verifyCmd).completions();
464
466
  if (import.meta.main) app.run();
465
467
  //#endregion
466
- export { app };
468
+ export {};
@@ -8,98 +8,16 @@
8
8
  *
9
9
  * Zero manual argparse — argument parsing, help, and completions all come from
10
10
  * {@link https://github.com/kjanat/dreamcli | DreamCLI}. `--json` is a DreamCLI built-in;
11
- * we branch on {@linkcode Out.jsonMode}.
11
+ * we branch on {@linkcode out.jsonMode | https://dreamcli.kjanat.com/reference/symbols/main/Out#jsonmode}.
12
12
  */
13
- import { mkdirSync, writeFileSync } from "node:fs";
14
- import { dirname, resolve } from "node:path";
15
- import { cwd, exit } from "node:process";
16
- import { fileURLToPath } from "node:url";
13
+ import { generateCmd } from "#bin/commands/generate";
14
+ import { verifyCmd } from "#bin/commands/verify";
15
+ import { homepage, name, repository, version } from "#pkg" with { type: "json" };
16
+ import { cli } from "@kjanat/dreamcli";
17
17
 
18
- import { cli, command, flag } from "dreamcli";
19
- import type { Out } from "dreamcli";
20
-
21
- import { buildGrammar, serializeGrammar } from "#grammar";
22
- import { verify } from "#verifier";
23
-
24
- import { homepage, repository, version } from "#pkg" with { type: "json" };
25
-
26
- const DEFAULT_OUT = `${resolve(import.meta.dirname, "..")}/recipe.tmLanguage.json`;
27
-
28
- const TS_RX_DIR = resolve(dirname(fileURLToPath(import.meta.resolve("tree-sitter-recipe/package.json"))));
29
- const DEFAULT_FIXTURES_DIR = resolve(TS_RX_DIR, "test/highlight");
30
- const DEFAULT_ONIG_WASM = fileURLToPath(import.meta.resolve("vscode-oniguruma/release/onig.wasm"));
31
-
32
- const indentOf = (raw: string): "tab" | number => (raw === "tab" ? "tab" : Number(raw));
33
-
34
- const generate = command("generate")
35
- .description("Build the TextMate grammar from the tree-sitter-recipe vocabulary")
36
- .flag("out", flag.string().alias("o").default(DEFAULT_OUT).describe("Output JSON path"))
37
- .flag("indent", flag.enum(["tab", "2", "4"]).default("tab").describe("JSON indent"))
38
- .flag("quiet", flag.boolean().alias("q").default(false).describe("Suppress stats on success"))
39
- .action(({ flags, out }) => {
40
- const { grammar, stats } = buildGrammar();
41
- const serialized = serializeGrammar(grammar, indentOf(flags.indent));
42
- const outAbs = resolve(cwd(), flags.out);
43
- mkdirSync(dirname(outAbs), { recursive: true });
44
- writeFileSync(outAbs, serialized);
45
- if (out.jsonMode) {
46
- out.json({ ok: true, outPath: outAbs, bytes: serialized.length, stats });
47
- return;
48
- }
49
- if (flags.quiet) return;
50
-
51
- out.log(`wrote ${outAbs}`);
52
- out.log(` ${stats.topLevelPatterns} top-level patterns · ${serialized.length} bytes`);
53
- const v = stats.vocab;
54
- out.log(
55
- ` vocab: ${v.frequency} frequency · ${v.timing.single}+${v.timing.multi} timing · ${v.route.single}+${v.route.multi} route · ${v.dispensing.single}+${v.dispensing.multi} dispensing · ${v.forms.single}+${v.forms.multi} forms · ${v.compounding.single}+${v.compounding.multi} compounding · ${v.conditional.single}+${v.conditional.multi} conditional · ${v.warning} warning · ${v.units} units`,
56
- );
57
- });
58
-
59
- const verifyCmd = command("verify")
60
- .description("Tokenize tree-sitter-recipe highlight fixtures and assert scope matches")
61
- .flag("grammar", flag.string().alias("g").default(DEFAULT_OUT).describe("Path to .tmLanguage.json"))
62
- .flag("fixtures", flag.string().alias("f").default(DEFAULT_FIXTURES_DIR).describe("Directory of .recipe fixtures"))
63
- .flag("onig-wasm", flag.string().default(DEFAULT_ONIG_WASM).describe("Path to oniguruma WASM"))
64
- .flag("max-failures", flag.number().default(40).describe("Max failures to print (0 = all)"))
65
- .action(async ({ flags, out }) => {
66
- const result = await verify({
67
- grammarPath: resolve(cwd(), flags.grammar),
68
- fixturesDir: resolve(cwd(), flags.fixtures),
69
- onigWasmPath: resolve(cwd(), flags["onig-wasm"]),
70
- });
71
- const failuresLen = result.failures.length;
72
-
73
- if (out.jsonMode) {
74
- out.json(result);
75
- if (failuresLen > 0) {
76
- out.setExitCode(1);
77
- exit();
78
- }
79
- return;
80
- }
81
-
82
- out.log(`${result.pass} / ${result.total} assertions pass`);
83
- if (failuresLen === 0) return;
84
-
85
- out.log("");
86
- out.log("── failures ──");
87
- const limit = flags["max-failures"] === 0 ? failuresLen : flags["max-failures"];
88
- for (const f of result.failures.slice(0, limit)) {
89
- const gotStr = f.got
90
- ? f.got.filter((s) => s !== "source.recipe").join(" · ") || "(root only)"
91
- : "(no token)";
92
- out.log(` ${f.fixture}:${f.line}:${f.col} expected ${f.capture} got [${gotStr}]`);
93
- }
94
- if (failuresLen > limit) {
95
- out.log(` … +${failuresLen - limit} more`);
96
- }
97
- out.setExitCode(1);
98
- });
99
-
100
- export const app = cli("recipe-tmlang").packageJson({ repository, homepage, version }).links()
18
+ const app = cli(name).packageJson({ repository, homepage, version }).links()
101
19
  .description("TextMate grammar generator & verifier for the recipe DSL")
102
- .command(generate)
20
+ .command(generateCmd)
103
21
  .command(verifyCmd)
104
22
  .completions();
105
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recipe-tmlanguage",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "TextMate grammar for the recipe (.recipe) pharmacological notation language.",
5
5
  "keywords": [
6
6
  "dreamcli",
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "type": "module",
23
23
  "imports": {
24
+ "#bin/*": "./bin/*.ts",
24
25
  "#pkg": "./package.json",
25
26
  "#grammar": "./src/grammar.ts",
26
27
  "#verifier": "./src/verifier.ts"
@@ -53,11 +54,12 @@
53
54
  "lint": "biome lint",
54
55
  "prepack": "bun run generate && bun run bundle",
55
56
  "recipe-tmlang": "bun bin/recipe-tmlang.ts",
57
+ "test": "bun test",
56
58
  "typecheck": "tsc --noEmit",
57
59
  "verify": "run recipe-tmlang verify"
58
60
  },
59
61
  "dependencies": {
60
- "dreamcli": "npm:@kjanat/dreamcli@^2.4.0",
62
+ "@kjanat/dreamcli": "^2.4.1",
61
63
  "tree-sitter-recipe": "^0.3.1",
62
64
  "vscode-oniguruma": "^2.0.1",
63
65
  "vscode-textmate": "^9.2.0"
package/src/grammar.ts CHANGED
@@ -74,7 +74,7 @@ const alt = (items: readonly string[]): string =>
74
74
  const altMultiword = (items: readonly string[]): string =>
75
75
  [...new Set(items)]
76
76
  .sort((a, b) => b.length - a.length)
77
- .map((s) => s.replace(/\./g, "\\.").replace(/\s+/g, "\\s+"))
77
+ .map((s) => escapeRegex(s).replace(/\s+/g, "\\s+"))
78
78
  .join("|");
79
79
 
80
80
  // Word boundary that treats `.` as part of the token so `a.c.` doesn't match
package/src/verifier.ts CHANGED
@@ -117,7 +117,7 @@ export async function verify(opts: VerifyOptions): Promise<VerifyResult> {
117
117
  readFileSync(opts.grammarPath, "utf-8"),
118
118
  opts.grammarPath,
119
119
  );
120
- const registry = new Registry({ onigLib, loadGrammar: async () => null });
120
+ const registry = new Registry({ onigLib, loadGrammar: () => Promise.resolve(null) });
121
121
  const grammar = await registry.addGrammar(rawGrammar);
122
122
 
123
123
  const result: VerifyResult = { pass: 0, total: 0, failures: [] };