From c994ee96e6d23d2c7b931b264d8fa2eefe690b3f Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Thu, 1 Oct 2026 18:49:04 -0300 Subject: [PATCH] fix: emit non-literal const values as @default text instead of [object Object] Fixes #18 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + examples/source/testConst.bs | 3 ++ src/convert-brighterscript-docs.spec.ts | 57 +++++++++++++++++++++++++ src/convert-brighterscript-docs.ts | 34 +++++++++++---- 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e192207..9cc76b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Enum and const values containing BrightScript hex literals (`&hFF0000FF`) are now normalized to valid JS syntax (`0xFF0000FF`) before being emitted, instead of breaking jsdoc's parser ([#14](https://github.com/markwpearce/brighterscript-jsdocs-plugin/issues/14)) - Class fields with an object literal default value (`foo = {}`) no longer crash with `this.getLookupTable is not a function` (fixed upstream in `brighterscript@1.0.0-alpha.56`) ([#17](https://github.com/markwpearce/brighterscript-jsdocs-plugin/issues/17)) +- Constants with a non-literal value (eg. `1.0 / 30.0`, `-1`, or a reference to another constant) no longer emit `[object Object]` and break jsdoc's parser; the value's source text is used as the `@default` instead ([#18](https://github.com/markwpearce/brighterscript-jsdocs-plugin/issues/18)) ### Added diff --git a/examples/source/testConst.bs b/examples/source/testConst.bs index 1fabd86..5724ce8 100644 --- a/examples/source/testConst.bs +++ b/examples/source/testConst.bs @@ -2,4 +2,7 @@ const PI = 3.14159265359; namespace Alpha const ROKU = "roku"; + + ' Longest time step a single frame can count + const MAX_STEP_SECONDS = 1.0 / 30.0 end namespace \ No newline at end of file diff --git a/src/convert-brighterscript-docs.spec.ts b/src/convert-brighterscript-docs.spec.ts index 2ad7c9e..c2d8d34 100644 --- a/src/convert-brighterscript-docs.spec.ts +++ b/src/convert-brighterscript-docs.spec.ts @@ -556,6 +556,63 @@ describe('convertBrighterscriptDocs', () => { var MY_CONSTANT = 0xFF0000FF; `); }); + + it('uses the source text as the default for non-literal constant values', () => { + expectOutput(cbd.convertBrighterscriptDocs(` + namespace alpha + const MAX_STEP = 1.0 / 30.0 + const NEG = -1 + const REF = MAX_STEP + end namespace + `), ` + /** + * @global + * @namespace alpha + */ + var alpha = {}; + + /** + * @memberof! alpha + * @readonly + * @constant + * @default 1.0 / 30.0 + */ + var MAX_STEP = undefined; + alpha.MAX_STEP = MAX_STEP; + /** + * @memberof! alpha + * @readonly + * @constant + * @default -1 + */ + var NEG = undefined; + alpha.NEG = NEG; + /** + * @memberof! alpha + * @readonly + * @constant + * @default MAX_STEP + */ + var REF = undefined; + alpha.REF = REF; + `); + }); + + it('collapses multi-line non-literal constant values onto one line', () => { + expectOutput(cbd.convertBrighterscriptDocs(` + const ITEMS = [ + 1, + 2 + ] + `), ` + /** + * @readonly + * @constant + * @default [ 1, 2 ] + */ + var ITEMS = undefined; + `); + }); }); describe('comment adjacency', () => { diff --git a/src/convert-brighterscript-docs.ts b/src/convert-brighterscript-docs.ts index dd34dff..4906875 100644 --- a/src/convert-brighterscript-docs.ts +++ b/src/convert-brighterscript-docs.ts @@ -56,6 +56,23 @@ export function resetCreatedCache() { let parserLines: string[] = []; +/** + * Gets the original source text of a node, with any line breaks collapsed to single spaces + */ +function getSourceText(node?: bs.AstNode): string { + const range = node?.location?.range; + if (!range) { + return ''; + } + const lines = parserLines.slice(range.start.line, range.end.line + 1); + if (lines.length === 0) { + return ''; + } + lines[lines.length - 1] = lines[lines.length - 1].slice(0, range.end.character); + lines[0] = lines[0].slice(range.start.character); + return lines.map(line => line.trim()).filter(line => line).join(' '); +} + /** * Gets the type name for the given type * Defaults to "dynamic" if it can't decide @@ -305,10 +322,7 @@ function processFunction(func: bs.FunctionStatement | bs.InterfaceMethodStatemen let paramLine = ` * @param {${paramType}} `; if (param.defaultValue?.location?.range) { - let start = param.defaultValue.location.range.start; - let end = param.defaultValue.location.range.end; - let defaultValue = parserLines[start.line].slice(start.character, end.character); - paramLine += `[${paramName}=${defaultValue}]`; + paramLine += `[${paramName}=${getSourceText(param.defaultValue)}]`; } else { paramLine += paramName; @@ -579,13 +593,17 @@ function processConst(constStatement: bs.ConstStatement, moduleName = '', namesp } commentLines.push(' * @readonly'); commentLines.push(' * @constant'); - commentLines.push(' * @default'); - commentLines.push(' */'); - output.push(...commentLines); - let valueOutput = {}; + // Literals are emitted as JS so jsdoc infers the default; anything else (eg. `1.0 / 30.0`) may not + // be valid JS, so put its source text on the @default tag instead + let valueOutput = 'undefined'; if (bs.isLiteralExpression(constStatement.value)) { + commentLines.push(' * @default'); valueOutput = normalizeBrightScriptNumericLiteral(constStatement.value.tokens.value.text); + } else { + commentLines.push(` * @default ${getSourceText(constStatement.value).replace(/\*\//g, '*\\/')}`); } + commentLines.push(' */'); + output.push(...commentLines); output.push(`var ${constStatement.name} = ${valueOutput};`); if (namespaceName) {