From 39d36a6e78e6316861092fe369722f98d04cd3a9 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Thu, 1 Oct 2026 21:43:13 -0300 Subject: [PATCH] fix: don't repeat parent name for nested namespaces namespace.name is already fully qualified, so prefixing the parent gave BGE.BGE.Foo. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + src/convert-brighterscript-docs.spec.ts | 40 ++++++++++++++++++++++ src/convert-brighterscript-docs.ts | 45 +++++++++---------------- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468411f..848eabc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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)) - Multi-line `@param` and `@returns` descriptions now stay with their tag instead of being appended to the function's description. Lines following one of those tags (up to the next tag or a blank comment line) are treated as part of its description ([#11](https://github.com/markwpearce/brighterscript-jsdocs-plugin/issues/11)) +- Nested namespaces (a `namespace` block inside another) no longer repeat the parent's name, which produced paths like `BGE.BGE.Model3dOps` and a stray `BGE/BGE` namespace page ([markwpearce/brighterscript-game-engine#268](https://github.com/markwpearce/brighterscript-game-engine/issues/268)) ### Added diff --git a/src/convert-brighterscript-docs.spec.ts b/src/convert-brighterscript-docs.spec.ts index 13bea73..025a6c2 100644 --- a/src/convert-brighterscript-docs.spec.ts +++ b/src/convert-brighterscript-docs.spec.ts @@ -339,6 +339,46 @@ describe('convertBrighterscriptDocs', () => { BGE.Debug.Alpha.Beta.DebugWindow = DebugWindow; `); }); + + it('does not repeat the parent name for nested namespaces', () => { + expectOutput(cbd.convertBrighterscriptDocs(` + namespace BGE + namespace Math.Inner + function foo() + end function + end namespace + end namespace + `), ` + /** + * @global + * @namespace BGE + */ + var BGE = {}; + + /** + * @global + * @namespace BGE/Math + * @alias BGE.Math + */ + BGE.Math = {}; + + /** + * @global + * @namespace BGE/Math/Inner + * @alias BGE.Math.Inner + */ + BGE.Math.Inner = {}; + + /** + * @function + * @memberof! BGE/Math/Inner + * @returns {dynamic} + */ + function foo () { }; + + BGE.Math.Inner.foo = foo; + `); + }); }); describe('enums', () => { diff --git a/src/convert-brighterscript-docs.ts b/src/convert-brighterscript-docs.ts index c60ecbb..063b0ba 100644 --- a/src/convert-brighterscript-docs.ts +++ b/src/convert-brighterscript-docs.ts @@ -550,51 +550,36 @@ function processClass(klass: bs.ClassStatement, moduleName = '', namespaceName = * @param parentNamespaceName [parentNamespaceName=""] the namespace this namespace is in * @returns the jsdoc string for the namespace provided */ -function processNamespace(namespace: bs.NamespaceStatement, moduleName = '', parentNamespaceName = ''): string { +function processNamespace(namespace: bs.NamespaceStatement, moduleName = ''): string { const output: string[] = []; + // namespace.name is already the fully qualified name (including any enclosing namespaces) const namespaceParts = namespace.name.split('.'); - const namespaceNames: string[] = []; - let namespaceNameChain = ''; + let namespaceName = ''; for (const namespacePart of namespaceParts) { - if (namespaceNameChain.length > 0) { - namespaceNameChain += '.'; - } - namespaceNameChain += namespacePart; - namespaceNames.push(namespaceNameChain); - } - let index = 0; - for (const namespaceName of namespaceNames) { - let subNamespace = namespaceName; - if (parentNamespaceName) { - subNamespace = parentNamespaceName + '.' + namespaceName; - } - if (!namespacesCreated.includes(subNamespace.toLowerCase())) { + const isRoot = namespaceName.length === 0; + namespaceName = isRoot ? namespacePart : namespaceName + '.' + namespacePart; + if (!namespacesCreated.includes(namespaceName.toLowerCase())) { // have not created this namespace yet let commentLines = convertCommentTextToJsDocLines(getCommentLines(namespace)); commentLines.push(` * @global`); - commentLines.push(` * @namespace ${subNamespace.replace(/\./g, '/')}`); - if (subNamespace.includes('.')) { - commentLines.push(` * @alias ${subNamespace}`); + commentLines.push(` * @namespace ${namespaceName.replace(/\./g, '/')}`); + if (!isRoot) { + commentLines.push(` * @alias ${namespaceName}`); } commentLines.push(' */'); output.push(...commentLines); - if (parentNamespaceName || index > 0) { - output.push(`${subNamespace} = {};\n`); + if (isRoot) { + output.push(`var ${namespaceName} = {};\n`); } else { - output.push(`var ${subNamespace} = {};\n`); + output.push(`${namespaceName} = {};\n`); } - namespacesCreated.push(subNamespace.toLowerCase()); + namespacesCreated.push(namespaceName.toLowerCase()); } - index++; - } - let totalNamespace = namespace.name; - if (parentNamespaceName) { - totalNamespace = parentNamespaceName + '.' + totalNamespace; } - output.push(processStatements(namespace.body.statements, moduleName, totalNamespace)); + output.push(processStatements(namespace.body.statements, moduleName, namespace.name)); return output.join('\n'); } @@ -723,7 +708,7 @@ function processStatements(statements: bs.Statement[], moduleName = '', namespac } else if (bs.isClassStatement(statement)) { output.push(processClass(statement, moduleName, namespaceName)); } else if (bs.isNamespaceStatement(statement)) { - output.push(processNamespace(statement, moduleName, namespaceName)); + output.push(processNamespace(statement, moduleName)); } else if (bs.isEnumStatement(statement)) { output.push(processEnum(statement, moduleName, namespaceName)); } else if (bs.isConstStatement(statement)) {