"use strict"; const assert = require("node:assert"); const { test } = require("node:test"); const { readFileSync } = require("node:fs"); const { join } = require("node:path"); const grammarPath = join(__dirname, "../syntaxes/wrn.tmLanguage.json"); const grammar = JSON.parse(readFileSync(grammarPath, "utf8")); test("the grammar knows the apis block", () => { assert.ok(grammar.repository["apis-block"], "apis should appear as a block keyword"); }); test("client keeps its highlighting where it is still valid", () => { // client state {}, runtime = "client", and client function all survive. // Only the client {} data block was removed. assert.match(JSON.stringify(grammar), /client/, "client must still be matched"); assert.match( JSON.stringify(grammar), /shared/, "the shared function modifier must still be matched", ); }); // --- Structural assertions: these fail if the rule they name is deleted, --- // --- even though the substrings "apis"/"client" remain elsewhere in the --- // --- file (e.g. inside unrelated pattern names or comments). --- /** * vscode-textmate is not a dependency of this repo (confirmed via * `require.resolve`), so real tokenization is unavailable here. Instead we * pull each rule's own `begin`/`match` regex out of the parsed grammar * object and exercise it directly with the JS regex engine. The grammar's * patterns use only `\b`, character classes, and `(?<=...)` lookbehind -- * all supported by native JS regexes -- so this is a faithful proxy for * "does this specific rule match this specific source line" without * needing an Oniguruma-backed tokenizer. */ function ruleByTopLevelBlockName(name) { const entry = grammar.repository[name]; assert.ok(entry, `repository.${name} should exist`); return entry; } function topLevelBlockIncludes(name) { return grammar.repository.blocks.patterns.some((p) => p.include === `#${name}`); } test("blocks includes the apis-block pattern", () => { assert.ok(topLevelBlockIncludes("apis-block"), "#blocks must include #apis-block"); }); test("the apis-block begin pattern matches the container keyword", () => { const rule = ruleByTopLevelBlockName("apis-block"); const beginRe = new RegExp(rule.begin); assert.ok(beginRe.test("apis {"), "apis-block begin should match 'apis {'"); assert.ok(!beginRe.test("ssr {"), "apis-block begin must not match 'ssr {'"); assert.ok(!beginRe.test("client {"), "apis-block begin must not match 'client {'"); }); test("the apis-block declares an entry pattern for ", () => { const rule = ruleByTopLevelBlockName("apis-block"); const entryPattern = (rule.patterns || []).find( (p) => p.name === "apis-entry" || (p.begin && /GET\|POST/.test(p.begin)), ); assert.ok(entryPattern, "apis-block should contain an entry declaration pattern"); const entryRe = new RegExp(entryPattern.begin || entryPattern.match); assert.ok( entryRe.test("searchUsers POST /api/users {"), "the entry pattern should match ' {'", ); }); test("the ssr/client data-block pattern (mode-block) is gone", () => { assert.strictEqual( grammar.repository["mode-block"], undefined, "mode-block (the removed ssr {}/client {} data-block rule) must be deleted from the repository", ); assert.ok( !topLevelBlockIncludes("mode-block"), "#blocks must no longer include the removed #mode-block pattern", ); }); // --- Regression coverage: constructs that must NOT be broken by this change --- test("REGRESSION: client state {} still highlights via grouped-state-block", () => { const rule = ruleByTopLevelBlockName("grouped-state-block"); const beginRe = new RegExp(rule.begin); assert.ok( beginRe.test("client state {"), "grouped-state-block must still match 'client state {'", ); }); test('REGRESSION: runtime = "client" still highlights via execution-decl', () => { const rule = ruleByTopLevelBlockName("execution-decl"); const beginRe = new RegExp(rule.begin); assert.ok( beginRe.test('runtime = "client"'), "execution-decl must still match 'runtime = \"client\"'", ); }); test("REGRESSION: the client function modifier still highlights via functions-block", () => { const rule = ruleByTopLevelBlockName("functions-block"); const modifierPattern = rule.patterns.find( (p) => (p.match && /storage\.modifier\.async/.test("")) || p.match, ); const matchRe = new RegExp( rule.patterns.find( (p) => p.captures && p.captures["1"]?.name === "storage.modifier.runtime.wrn", ).match, ); assert.ok( matchRe.test("client async function go(): Promise {"), "functions-block must still recognize the 'client' runtime modifier on a function", ); }); test("REGRESSION: the client function modifier also highlights via v060-keywords", () => { const rule = ruleByTopLevelBlockName("v060-keywords"); const modifierPattern = rule.patterns.find((p) => p.name === "storage.modifier.runtime.wrn"); assert.ok(modifierPattern, "v060-keywords should have a storage.modifier.runtime.wrn rule"); const matchRe = new RegExp(modifierPattern.match); assert.ok( matchRe.test("client function go"), "v060-keywords must still recognize 'client' before 'function'", ); assert.ok( matchRe.test("client state"), "v060-keywords must still recognize 'client' before 'state'", ); });