diff --git a/bun.lock b/bun.lock index 637326bb..f0d200d8 100644 --- a/bun.lock +++ b/bun.lock @@ -668,7 +668,7 @@ }, "packages/syntax": { "name": "@wrnexus/syntax", - "version": "0.8.16", + "version": "0.8.17", }, "packages/test": { "name": "@wrnexus/test", diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 17a54b12..1dbb0488 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -2,7 +2,7 @@ "name": "wrnexus", "displayName": "WRNexus Language Support", "description": "Complete WRNexus v0.8.3 language support for typed imports, props, state, outputs, runtime functions, stores, diagnostics, formatting, navigation, and migration assistance.", - "version": "0.8.9", + "version": "0.8.10", "publisher": "wrnexus", "private": true, "license": "SEE LICENSE IN LICENSE", diff --git a/editors/vscode/src/compiler.cjs b/editors/vscode/src/compiler.cjs index 554acfb2..a362c49b 100644 --- a/editors/vscode/src/compiler.cjs +++ b/editors/vscode/src/compiler.cjs @@ -1,6 +1,6 @@ "use strict"; // Generated by scripts/build-editor-compiler.mjs. Do not edit directly. -// WRN editor compiler source hash: 884c9faffb5517c907321484014e00562654ea0ba96e432bec319e0001934446 +// WRN editor compiler source hash: edac5449f97a341e79ee7bf7e7f3676f42bbb4ec6aac4432f5ace9ff88f4f85c // WRN editor compiler generator hash: f8c1094b47e695151205899de28cd63d574356d26ac49219c4a43db0f2391ef5 // Generated with TypeScript: 6.0.3 const __nodeRequire = require; @@ -6227,6 +6227,26 @@ function expandStructuredStateDeclarations(lines, unit) { } }); } +function formatStructuredPropDeclaration(value, unit, depth, printWidth) { + const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value); + if (!match) + return null; + try { + const parsed = JSON.parse(match[2].trim()); + const compact = JSON.stringify(parsed); + const indentation = unit.repeat(depth); + if (indentation.length + match[1].length + compact.length <= printWidth) + return null; + const jsonLines = JSON.stringify(parsed, null, unit).split("\n"); + return [ + `${indentation}${match[1]}${jsonLines[0]}`, + ...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`), + ]; + } + catch { + return null; + } +} function formatWrnPass(source, options = {}) { const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); const printWidth = options.printWidth ?? 100; @@ -6282,6 +6302,12 @@ function formatWrnPass(source, options = {}) { index += 1; continue; } + const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth); + if (structuredProp) { + output.push(...structuredProp); + index += 1; + continue; + } const leadingClosingBraces = countLeadingClosingBraces(value); const closesControlBlock = isControlBlockClose(value); const continuesControlBlock = isControlBlockMiddle(value); diff --git a/editors/vscode/src/extension.bundle.cjs b/editors/vscode/src/extension.bundle.cjs index 6b625bf4..6badb374 100644 --- a/editors/vscode/src/extension.bundle.cjs +++ b/editors/vscode/src/extension.bundle.cjs @@ -1,4 +1,4 @@ -// WRN editor extension source hash: 8e634610c27521e6ccbf4e7293eb4320ecaac29944bde5236bb286ff84422203 +// WRN editor extension source hash: 2f8a27685e497dffd4c27303333c9c34fb7571b0add2e6a781240f7353f1c86b // WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728 "use strict"; var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); diff --git a/editors/vscode/src/language-server.cjs b/editors/vscode/src/language-server.cjs index 451be8b1..4a216c6d 100644 --- a/editors/vscode/src/language-server.cjs +++ b/editors/vscode/src/language-server.cjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -// WRN editor language server source hash: 64ecd72706de345e7e8891089d2c3d1aa5c1c2beaa7cf9b816ba32a9c4ad6c54 +// WRN editor language server source hash: 6d790d593fb074f5e55837db3f757162f0e59e77cd6f8643afd1e93e6ff4a2ef // WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72 // @bun @bun-cjs (function(exports, require, module, __filename, __dirname) {var __create = Object.create; @@ -170722,6 +170722,26 @@ function expandStructuredStateDeclarations(lines, unit) { } }); } +function formatStructuredPropDeclaration(value, unit, depth, printWidth) { + const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value); + if (!match) + return null; + try { + const parsed = JSON.parse(match[2].trim()); + const compact = JSON.stringify(parsed); + const indentation = unit.repeat(depth); + if (indentation.length + match[1].length + compact.length <= printWidth) + return null; + const jsonLines = JSON.stringify(parsed, null, unit).split(` +`); + return [ + `${indentation}${match[1]}${jsonLines[0]}`, + ...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`) + ]; + } catch { + return null; + } +} function formatWrnPass(source, options = {}) { const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); const printWidth = options.printWidth ?? 100; @@ -170778,6 +170798,12 @@ function formatWrnPass(source, options = {}) { index += 1; continue; } + const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth); + if (structuredProp) { + output.push(...structuredProp); + index += 1; + continue; + } const leadingClosingBraces = countLeadingClosingBraces(value); const closesControlBlock = isControlBlockClose(value); const continuesControlBlock = isControlBlockMiddle(value); diff --git a/editors/vscode/test/formatter.test.js b/editors/vscode/test/formatter.test.js index d5f2e3ac..2ea1b5d9 100644 --- a/editors/vscode/test/formatter.test.js +++ b/editors/vscode/test/formatter.test.js @@ -20,6 +20,20 @@ test("preserves nested prop defaults while formatting", () => { assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2 }), formatted); }); +test("formats large typed JSON prop defaults with the bundled formatter", () => { + const source = `component Catalog { + props { + categories: unknown[] = [{"label":"Platform & Core","apps":[{"label":"Home","href":"/home"},{"label":"SSO","href":"/sso"}]},{"label":"Sales","apps":[{"label":"CRM","href":"/crm"}]}] + } + view {
} +}`; + const formatted = formatWrn(source, { insertSpaces: true, tabSize: 2, printWidth: 70 }); + + assert.match(formatted, /categories: unknown\[\] = \[\n \{/); + assert.match(formatted, /"apps": \[\n \{/); + assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2, printWidth: 70 }), formatted); +}); + test("formats native JSON state values with readable indentation", () => { const source = `component Footer { state footerItems = [{"label":"Accessibility","href":"/accessibility","value":"accessibility"},{"label":"Privacy","href":"/privacy","value":"privacy"}] diff --git a/packages/syntax/package.json b/packages/syntax/package.json index 6bf1115b..23de0eef 100644 --- a/packages/syntax/package.json +++ b/packages/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/syntax", - "version": "0.8.16", + "version": "0.8.17", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/syntax/src/formatter.ts b/packages/syntax/src/formatter.ts index 303254a8..cba4bb58 100644 --- a/packages/syntax/src/formatter.ts +++ b/packages/syntax/src/formatter.ts @@ -767,6 +767,28 @@ function expandStructuredStateDeclarations(lines, unit) { }); } +function formatStructuredPropDeclaration(value, unit, depth, printWidth) { + const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec( + value, + ); + if (!match) return null; + + try { + const parsed = JSON.parse(match[2].trim()); + const compact = JSON.stringify(parsed); + const indentation = unit.repeat(depth); + if (indentation.length + match[1].length + compact.length <= printWidth) return null; + + const jsonLines = JSON.stringify(parsed, null, unit).split("\n"); + return [ + `${indentation}${match[1]}${jsonLines[0]}`, + ...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`), + ]; + } catch { + return null; + } +} + function formatWrnPass(source: string, options: FormatWrnOptions = {}): string { const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); @@ -844,6 +866,18 @@ function formatWrnPass(source: string, options: FormatWrnOptions = {}): string { continue; } + const structuredProp = formatStructuredPropDeclaration( + value, + unit, + codeDepth + htmlDepth + controlDepth, + printWidth, + ); + if (structuredProp) { + output.push(...structuredProp); + index += 1; + continue; + } + const leadingClosingBraces = countLeadingClosingBraces(value); const closesControlBlock = isControlBlockClose(value); diff --git a/packages/syntax/test/formatter.test.ts b/packages/syntax/test/formatter.test.ts index 4220f71b..957147be 100644 --- a/packages/syntax/test/formatter.test.ts +++ b/packages/syntax/test/formatter.test.ts @@ -83,3 +83,18 @@ test("preserves balanced compact sibling markup without indentation drift", () = expect(formatted).toContain('
PageAPI
'); expect(formatWrn(formatted, { tabSize: 2, printWidth: 80 })).toBe(formatted); }); + +test("formats large typed JSON prop defaults and remains idempotent", () => { + const source = `component Catalog { + props { + categories: unknown[] = [{"label":"Platform & Core","value":"platform","apps":[{"label":"WRNexus Home / Business Command Center","href":"/products/home","number":"01"},{"label":"SSO & Account","href":"/products/sso","number":"02"}]},{"label":"Sales & Customer","value":"sales","apps":[{"label":"CRM","href":"/products/crm","number":"11"}]}] + } + view {
} +}`; + const formatted = formatWrn(source, options); + + expect(formatted).toContain(`categories: unknown[] = [\n {\n "label": "Platform & Core"`); + expect(formatted).toContain(`"apps": [\n {`); + expect(formatWrn(formatted, options)).toBe(formatted); + expect(() => parse(formatted)).not.toThrow(); +});