diff --git a/docs/javascript-route-inventory.md b/docs/javascript-route-inventory.md index f3873eaa63..a7e9901842 100644 --- a/docs/javascript-route-inventory.md +++ b/docs/javascript-route-inventory.md @@ -74,6 +74,12 @@ supported by structural or navigation candidates across the supplied sources. This limits framework filesystem noise, while leaving unsupported candidates unknown. The parser shares the existing AST traversal and lexical index. +Literal fragments of computed expressions, module references, and page-shaped +paths owned by Next interpretation do not also become generic route constants. +Selected getter results retain the shared semantic owner's allocation identity; +finite selections can claim their query data, while widened or cyclic selections +keep an unknown in the interpretation that requested them. + Next page conversion receives an explicit source context. For local source files, the shared JSON owner first establishes a declared `next` dependency in the nearest owning package. Inventoried directories then select that package's `app` diff --git a/src/domain/javascript/javascriptRouteScan.ts b/src/domain/javascript/javascriptRouteScan.ts index 9610dea308..6e9937a29e 100644 --- a/src/domain/javascript/javascriptRouteScan.ts +++ b/src/domain/javascript/javascriptRouteScan.ts @@ -550,17 +550,21 @@ export class JavaScriptRouteScan { ? { kind: "source-reference" } : { kind: "source-literal", runtime: "unknown" }, ); + const computed = + (t.isBinaryExpression(expressionParent) && + expressionParent.operator === "+") || + (t.isTemplateLiteral(expressionParent) && + expressionParent.expressions.length > 0); if (next !== undefined && this.#queue(node, value)) this.#nextPaths.push({ value, node, - computed: - (t.isBinaryExpression(expressionParent) && - expressionParent.operator === "+") || - (t.isTemplateLiteral(expressionParent) && - expressionParent.expressions.length > 0), + computed, }); if ( + !computed && + !reference && + next === undefined && !this.#propertyValues.has(node) && routePath(value) !== undefined && this.#queue(node, value) @@ -1164,17 +1168,29 @@ export class JavaScriptRouteScan { visited: new WeakMap(), }; pending.push({ kind: "finish", lookup: work, key, values }); - for (const reference of this.#selectedReferences(work.receiver, work.key)) + for (const reference of this.#selectedReferences( + work.receiver, + work.key, + )) { + if (reference.widenedGetterResult === true) + this.#unknown( + mechanism, + node, + "An incomplete getter selection, including cyclic expansion, cannot establish its complete data sources.", + ); if ( !this.#retainSelection( pending, - reference.node, + // The reference owner identifies the allocation directly; a + // declaration identifier here is not a read before initialization. + reference.ownerInitializer ?? reference.node, reference.path, 0, returned, ) ) return; + } } }