Skip to content

Commit 96b6c05

Browse files
committed
Error on destructured and non-callable exports in backend files
1 parent 1399c1c commit 96b6c05

2 files changed

Lines changed: 48 additions & 1 deletion

File tree

packages/plugins/apps/src/backend/discovery.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,44 @@ describe('Backend Functions - extractExportedFunctions', () => {
251251
);
252252
});
253253

254+
test('Should throw on destructured variable export', () => {
255+
const ast = program([
256+
{
257+
type: 'ExportNamedDeclaration',
258+
declaration: {
259+
type: 'VariableDeclaration',
260+
kind: 'const' as const,
261+
declarations: [
262+
{
263+
type: 'VariableDeclarator',
264+
id: {
265+
type: 'ObjectPattern',
266+
properties: [
267+
{
268+
type: 'Property',
269+
key: { type: 'Identifier', name: 'a' },
270+
value: { type: 'Identifier', name: 'a' },
271+
kind: 'init' as const,
272+
computed: false,
273+
method: false,
274+
shorthand: true,
275+
},
276+
],
277+
},
278+
init: { type: 'Identifier', name: 'obj' },
279+
},
280+
],
281+
},
282+
specifiers: [],
283+
source: null,
284+
attributes: [],
285+
},
286+
]);
287+
expect(() => extractExportedFunctions(ast, filePath)).toThrow(
288+
'Destructured exports are not supported in backend files',
289+
);
290+
});
291+
254292
test('Should throw on export { x as default }', () => {
255293
const ast = program([
256294
{

packages/plugins/apps/src/backend/discovery.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,28 @@ function isNonCallableInit(init: Expression | null | undefined): boolean {
9393
* Throws when a variable export has a non-callable initializer.
9494
*/
9595
function namesFromDeclaration(decl: Declaration, filePath: string): string[] {
96+
// export function add(a, b) { return a + b; }
9697
if (decl.type === 'FunctionDeclaration' && decl.id) {
9798
return [decl.id.name];
9899
}
99100
if (decl.type === 'VariableDeclaration') {
100101
return decl.declarations.flatMap((d) => {
102+
// export const { a, b } = obj;
103+
// export const [a, b] = arr;
101104
if (d.id.type !== 'Identifier') {
102-
return [];
105+
throw new Error(
106+
`Destructured exports are not supported in backend files. Use individual named exports instead: ${filePath}`,
107+
);
103108
}
109+
// export const VERSION = '1.0'; — non-callable, throws
110+
// export const config = { ... }; — non-callable, throws
104111
if (isNonCallableInit(d.init)) {
105112
throw new Error(
106113
`Non-function export "${d.id.name}" in backend file ${filePath}. Only function exports are supported — use "export function ${d.id.name}(…) { }" instead.`,
107114
);
108115
}
116+
// export const add = (a, b) => a + b;
117+
// export const handler = importedFn; — ambiguous, allowed
109118
return [d.id.name];
110119
});
111120
}

0 commit comments

Comments
 (0)