diff --git a/packages/plugin-rsc/src/transforms/wrap-export.test.ts b/packages/plugin-rsc/src/transforms/wrap-export.test.ts index 976d511a9..22b51b87c 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.test.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.test.ts @@ -259,11 +259,10 @@ export default function d() {} }) expect(result).toMatchInlineSnapshot(` " - let a = 0; + export const a = 0; let b = function() {} let c = () => {} function d() {} - export { a }; b = /* #__PURE__ */ $$wrap(b, "", "b"); export { b }; c = /* #__PURE__ */ $$wrap(c, "", "c"); @@ -282,13 +281,7 @@ export default () => {} const result = await testTransform(input, { filter: (_name, meta) => !!(meta.isFunction && meta.declName), }) - expect(result).toMatchInlineSnapshot(` - " - const $$default = () => {} - ; - export { $$default as default }; - " - `) + expect(result).toMatchInlineSnapshot(`false`) }) test('filter defaultExportIdentifierName', async () => { @@ -329,12 +322,10 @@ export const tags = [] expect(result.exportNames).toEqual(['action']) expect(result.output.toString()).toMatchInlineSnapshot(` "let action = async () => {} - let metadata = {} - let tags = [] + export const metadata = {} + export const tags = [] action = /* #__PURE__ */ $$wrap(action, "action"); export { action }; - export { metadata }; - export { tags }; " `) }) @@ -353,9 +344,8 @@ export default async function Page() {} expect(result.exportNames).toEqual(['default']) expect(result.output.toString()).toMatchInlineSnapshot(` " - let revalidate = 1; + export const revalidate = 1; async function Page() {} - export { revalidate }; ; const $$wrap_Page = /* #__PURE__ */ $$wrap(Page, "default"); export { $$wrap_Page as default }; @@ -363,20 +353,30 @@ export default async function Page() {} `) }) - test('filtered default exports are not validated or reported', async () => { - const input = `export default 1;` + test.each([ + `export function action() {}`, + `export class Action {}`, + `export const action = 1`, + `const action = 1; export { action }`, + `export { action } from './dep'`, + `export default function action() {}`, + `export default function () {}`, + `export default class {}`, + `export default action`, + `export default 1`, + ])('fully filtered export is unchanged: %s', async (input) => { const ast = await parseAstAsync(input) const result = transformWrapExport(input, ast, { - runtime: (value, name) => `$$wrap(${value}, ${JSON.stringify(name)})`, + runtime: () => { + throw new Error('runtime called for filtered export') + }, rejectNonAsyncFunction: true, filter: () => false, }) + expect(result.exportNames).toEqual([]) - expect(result.output.toString()).toMatchInlineSnapshot(` - "const $$default = 1;; - export { $$default as default }; - " - `) + expect(result.output.hasChanged()).toBe(false) + expect(result.output.toString()).toBe(input) }) test('unknown identifier exports remain eligible for wrapping', async () => { diff --git a/packages/plugin-rsc/src/transforms/wrap-export.ts b/packages/plugin-rsc/src/transforms/wrap-export.ts index 25b0320d4..30e699296 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.ts @@ -33,17 +33,18 @@ export function transformWrapExport( const toAppend: string[] = [] const filter = options.filter ?? (() => true) + function filterExports(exports: T[]) { + return exports.map((item) => ({ + ...item, + shouldWrap: filter(item.exportName, item.meta), + })) + } + function wrapSimple( start: number, end: number, - exports: ModuleExportEntry[], + filteredExports: (ModuleExportEntry & { shouldWrap: boolean })[], ) { - const filteredExports = exports.map((item) => { - return { - ...item, - shouldWrap: filter(item.exportName, item.meta), - } - }) exportNames.push( ...filteredExports .filter((item) => item.shouldWrap) @@ -78,9 +79,10 @@ export function transformWrapExport( function wrapExport( name: string, exportName: string, - meta: ModuleExportMeta = {}, + meta: ModuleExportMeta, + shouldWrap: boolean, ) { - if (!filter(exportName, meta)) { + if (!shouldWrap) { toAppend.push(`export { ${name} as ${exportName} }`) return } @@ -98,12 +100,29 @@ export function transformWrapExport( for (const group of scanModuleExports(viteAst)) { if (group.type === 'declaration') { - const [entry] = group.exports - if (filter(entry.exportName, entry.meta)) { - validateNonAsyncFunction(options, group.declaration) - } - wrapSimple(group.node.start, group.declaration.start, group.exports) + const exports = filterExports(group.exports) + if (!exports.some((entry) => entry.shouldWrap)) continue + + validateNonAsyncFunction(options, group.declaration) + wrapSimple(group.node.start, group.declaration.start, exports) } else if (group.type === 'variable-declaration') { + const exports: (ModuleExportEntry & { shouldWrap: boolean })[] = [] + let shouldWrap = false + for (const declarator of group.declarators) { + const declaratorExports = filterExports(declarator.exports) + exports.push(...declaratorExports) + const shouldWrapDeclarator = declaratorExports.some( + (entry) => entry.shouldWrap, + ) + if (shouldWrapDeclarator) { + shouldWrap = true + } + if (declarator.node.init && shouldWrapDeclarator) { + validateNonAsyncFunction(options, declarator.node.init) + } + } + if (!shouldWrap) continue + if (group.declaration.kind === 'const') { output.update( group.declaration.start, @@ -111,23 +130,14 @@ export function transformWrapExport( 'let', ) } - const exports: ModuleExportEntry[] = [] - for (const declarator of group.declarators) { - exports.push(...declarator.exports) - if ( - declarator.node.init && - declarator.exports.some(({ exportName, meta }) => - filter(exportName, meta), - ) - ) { - validateNonAsyncFunction(options, declarator.node.init) - } - } wrapSimple(group.node.start, group.declaration.start, exports) } else if (group.type === 'specifiers') { + const exports = filterExports(group.exports) + if (!exports.some((entry) => entry.shouldWrap)) continue + if (group.node.source) { output.remove(group.node.start, group.node.end) - for (const entry of group.exports) { + for (const entry of exports) { tinyassert(entry.node.local.type === 'Identifier') if (entry.node.exported.type !== 'Identifier') { throw Object.assign( @@ -142,11 +152,12 @@ export function transformWrapExport( `$$import_${entry.localName}`, entry.exportName, entry.meta, + entry.shouldWrap, ) } } else { output.remove(group.node.start, group.node.end) - for (const entry of group.exports) { + for (const entry of exports) { tinyassert(entry.node.local.type === 'Identifier') if (entry.node.exported.type !== 'Identifier') { throw Object.assign( @@ -154,7 +165,12 @@ export function transformWrapExport( { pos: entry.node.exported.start }, ) } - wrapExport(entry.localName, entry.exportName, entry.meta) + wrapExport( + entry.localName, + entry.exportName, + entry.meta, + entry.shouldWrap, + ) } } } else if (group.type === 'export-all') { @@ -167,6 +183,9 @@ export function transformWrapExport( }) } } else if (group.type === 'default') { + const meta = group.meta + if (!filter('default', meta)) continue + const localName = group.localName ?? '$$default' if (group.kind === 'named-declaration') { // preserve name scope for `function foo() {}` and `class Foo {}` @@ -190,11 +209,8 @@ export function transformWrapExport( 'const $$default = ', ) } - const meta = group.meta - if (filter('default', meta)) { - validateNonAsyncFunction(options, group.node.declaration) - } - wrapExport(localName, 'default', meta) + validateNonAsyncFunction(options, group.node.declaration) + wrapExport(localName, 'default', meta, true) } }