|
@@ -1590,6 +1590,8 @@ export class TreeSitterExtractor {
|
|
|
// — SvelteKit actions). Inline-object arrows reached by the general walker
|
|
// — SvelteKit actions). Inline-object arrows reached by the general walker
|
|
|
// get no override, so they still fall through to the <anonymous> skip below.
|
|
// get no override, so they still fall through to the <anonymous> skip below.
|
|
|
let name = nameOverride ?? extractName(node, this.source, this.extractor);
|
|
let name = nameOverride ?? extractName(node, this.source, this.extractor);
|
|
|
|
|
+ // A CommonJS export assignment names the function it holds — see below.
|
|
|
|
|
+ let commonJsExport = false;
|
|
|
// For arrow functions and function expressions assigned to variables,
|
|
// For arrow functions and function expressions assigned to variables,
|
|
|
// resolve the name from the parent variable_declarator.
|
|
// resolve the name from the parent variable_declarator.
|
|
|
// e.g. `export const useAuth = () => { ... }` — the arrow_function node
|
|
// e.g. `export const useAuth = () => { ... }` — the arrow_function node
|
|
@@ -1605,6 +1607,18 @@ export class TreeSitterExtractor {
|
|
|
if (varName) {
|
|
if (varName) {
|
|
|
name = getNodeText(varName, this.source);
|
|
name = getNodeText(varName, this.source);
|
|
|
}
|
|
}
|
|
|
|
|
+ } else if (parent?.type === 'assignment_expression') {
|
|
|
|
|
+ // `exports.getItems = async (req, res) => {…}` / `module.exports.x =
|
|
|
|
|
+ // function () {…}` — the CommonJS controller style. The function is
|
|
|
|
|
+ // anonymous only syntactically: the export property is the name every
|
|
|
|
|
+ // `router.get('/items', getItems)` resolves. Without a node the handler
|
|
|
|
|
+ // is invisible to callers/impact and its calls attribute to the file
|
|
|
|
|
+ // (#1675). Same treatment `const X = () => {}` already gets.
|
|
|
|
|
+ const exportName = this.commonJsExportName(parent, node);
|
|
|
|
|
+ if (exportName) {
|
|
|
|
|
+ name = exportName;
|
|
|
|
|
+ commonJsExport = true;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if (name === '<anonymous>') {
|
|
if (name === '<anonymous>') {
|
|
@@ -1635,7 +1649,7 @@ export class TreeSitterExtractor {
|
|
|
const docstring = getPrecedingDocstring(node, this.source);
|
|
const docstring = getPrecedingDocstring(node, this.source);
|
|
|
const signature = this.extractor.getSignature?.(node, this.source);
|
|
const signature = this.extractor.getSignature?.(node, this.source);
|
|
|
const visibility = this.extractor.getVisibility?.(node);
|
|
const visibility = this.extractor.getVisibility?.(node);
|
|
|
- const isExported = this.extractor.isExported?.(node, this.source);
|
|
|
|
|
|
|
+ const isExported = commonJsExport || this.extractor.isExported?.(node, this.source);
|
|
|
const isAsync = this.extractor.isAsync?.(node);
|
|
const isAsync = this.extractor.isAsync?.(node);
|
|
|
const isStatic = this.extractor.isStatic?.(node);
|
|
const isStatic = this.extractor.isStatic?.(node);
|
|
|
const returnType = this.extractor.getReturnType?.(node, this.source);
|
|
const returnType = this.extractor.getReturnType?.(node, this.source);
|
|
@@ -5311,6 +5325,33 @@ export class TreeSitterExtractor {
|
|
|
targets.add(target);
|
|
targets.add(target);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * The property a CommonJS export assignment binds a function to —
|
|
|
|
|
+ * `exports.NAME = <node>` or `module.exports.NAME = <node>` — or null for
|
|
|
|
|
+ * any other assignment. JS-family only; the node must be the assignment's
|
|
|
|
|
+ * whole right-hand side.
|
|
|
|
|
+ */
|
|
|
|
|
+ private commonJsExportName(assignment: SyntaxNode, value: SyntaxNode): string | null {
|
|
|
|
|
+ if (
|
|
|
|
|
+ this.language !== 'typescript' &&
|
|
|
|
|
+ this.language !== 'javascript' &&
|
|
|
|
|
+ this.language !== 'tsx' &&
|
|
|
|
|
+ this.language !== 'jsx'
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ const right = getChildByField(assignment, 'right');
|
|
|
|
|
+ if (!right || right.startIndex !== value.startIndex || right.endIndex !== value.endIndex) return null;
|
|
|
|
|
+ const left = getChildByField(assignment, 'left');
|
|
|
|
|
+ if (!left || left.type !== 'member_expression') return null;
|
|
|
|
|
+ const object = getChildByField(left, 'object');
|
|
|
|
|
+ const property = getChildByField(left, 'property');
|
|
|
|
|
+ if (!object || !property || property.type !== 'property_identifier') return null;
|
|
|
|
|
+ const objectText = getNodeText(object, this.source);
|
|
|
|
|
+ if (objectText !== 'exports' && objectText !== 'module.exports') return null;
|
|
|
|
|
+ return getNodeText(property, this.source);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* The declarator name a React handler hook binds an anonymous function to —
|
|
* The declarator name a React handler hook binds an anonymous function to —
|
|
|
* `const NAME = useCallback(<node>, [...])` — or null for any other shape.
|
|
* `const NAME = useCallback(<node>, [...])` — or null for any other shape.
|