@@ -65,12 +65,13 @@ import {
6565} from './plugins/vite-utils'
6666import {
6767 type TransformWrapExportFilter ,
68- extractNames ,
6968 hasDirective ,
7069 transformDirectiveProxyExport ,
70+ transformExpandExportAll ,
7171 transformServerActionServer ,
7272 transformWrapExport ,
7373 findDirectives ,
74+ type TransformExpandExportAllContext ,
7475} from './transforms'
7576import { generateEncryptionKey , toBase64 } from './utils/encryption-utils'
7677import { createRpcServer } from './utils/rpc'
@@ -1376,141 +1377,26 @@ function globalAsyncLocalStoragePlugin(): Plugin[] {
13761377 ]
13771378}
13781379
1379- // Strip TS/JSX so `parseAstAsync` can read the result. Prefer oxc when
1380- // available (Vite 8+); fall back to esbuild for older Vite versions.
1381- async function transformSourceForExportScan (
1382- code : string ,
1383- filename : string ,
1384- ) : Promise < string | undefined > {
1385- const v = vite as Partial < {
1386- transformWithOxc : (
1387- code : string ,
1388- filename : string ,
1389- options ?: { sourcemap ?: boolean } ,
1390- ) => Promise < { code : string } >
1391- transformWithEsbuild : (
1392- code : string ,
1393- filename : string ,
1394- options ?: { sourcemap ?: boolean } ,
1395- ) => Promise < { code : string } >
1396- } >
1397- const transform = v . transformWithOxc ?? v . transformWithEsbuild
1398- if ( ! transform ) return undefined
1399- const result = await transform ( code , filename , { sourcemap : false } )
1400- return result . code
1401- }
1402-
1403- // Recursively collect the named exports of a module (following `export * from`
1404- // chains), so that the RSC `use client`/`use server` proxy transforms can
1405- // expand bare `export *` re-exports into explicit named re-exports before
1406- // proxy generation. The pure proxy transform cannot do this on its own because
1407- // the names live in another file.
1408- async function collectExportNames (
1380+ function createTransformExpandExportAllContext (
14091381 ctx : Rollup . TransformPluginContext ,
1410- resolvedId : string ,
1411- seen : Set < string > ,
1412- ) : Promise < string [ ] > {
1413- if ( seen . has ( resolvedId ) ) return [ ]
1414- seen . add ( resolvedId )
1415-
1416- // Read the source from disk and strip TS/JSX so the AST walk below sees
1417- // standard ESM exports. We don't go through `this.load` /
1418- // `transformRequest` here — in dev they return module-runner output
1419- // (`__vite_ssr_exportName__(...)`) the walker can't read, and on build
1420- // there's no practical benefit over reading the source directly for the
1421- // simple TS/JSX modules we care about.
1422- let moduleCode : string | undefined
1423- try {
1424- const raw = await fs . promises . readFile ( resolvedId , 'utf-8' )
1425- moduleCode = await transformSourceForExportScan ( raw , resolvedId )
1426- } catch {
1427- return [ ]
1428- }
1429- if ( ! moduleCode ) return [ ]
1430-
1431- let ast : Awaited < ReturnType < typeof parseAstAsync > >
1432- try {
1433- ast = await parseAstAsync ( moduleCode )
1434- } catch {
1435- return [ ]
1436- }
1437-
1438- const names : string [ ] = [ ]
1439- for ( const node of ast . body ) {
1440- if ( node . type === 'ExportNamedDeclaration' ) {
1441- if ( node . declaration ) {
1442- if (
1443- node . declaration . type === 'FunctionDeclaration' ||
1444- node . declaration . type === 'ClassDeclaration'
1445- ) {
1446- if ( node . declaration . id ) names . push ( node . declaration . id . name )
1447- } else if ( node . declaration . type === 'VariableDeclaration' ) {
1448- for ( const decl of node . declaration . declarations ) {
1449- names . push ( ...extractNames ( decl . id ) )
1450- }
1451- }
1452- } else {
1453- for ( const spec of node . specifiers ) {
1454- if (
1455- spec . exported . type === 'Identifier' &&
1456- spec . exported . name !== 'default'
1457- ) {
1458- names . push ( spec . exported . name )
1459- }
1460- }
1461- }
1462- } else if ( node . type === 'ExportAllDeclaration' ) {
1463- if ( node . exported ?. type === 'Identifier' ) {
1464- names . push ( node . exported . name )
1465- } else if ( node . source ) {
1466- const subResolved = await ctx . resolve (
1467- node . source . value as string ,
1468- resolvedId ,
1469- )
1470- if ( subResolved ) {
1471- names . push ( ...( await collectExportNames ( ctx , subResolved . id , seen ) ) )
1472- }
1473- }
1474- }
1475- }
1476- return names
1477- }
1478-
1479- async function expandExportAllDeclarations (
1480- ctx : Rollup . TransformPluginContext ,
1481- ast : Awaited < ReturnType < typeof parseAstAsync > > ,
1482- code : string ,
1483- id : string ,
1484- ) : Promise < {
1485- code : string
1486- ast : Awaited < ReturnType < typeof parseAstAsync > >
1487- } | null > {
1488- const targets = ast . body . filter (
1489- ( n ) => n . type === 'ExportAllDeclaration' && ! n . exported ,
1490- )
1491- if ( targets . length === 0 ) return null
1492-
1493- const output = new MagicString ( code )
1494- for ( const node of targets ) {
1495- if ( node . type !== 'ExportAllDeclaration' ) continue
1496- const source = node . source . value as string
1497- const resolved = await ctx . resolve ( source , id )
1498- if ( ! resolved ) continue
1499- const names = await collectExportNames ( ctx , resolved . id , new Set ( ) )
1500- if ( names . length === 0 ) {
1501- output . remove ( node . start , node . end )
1502- } else {
1503- output . update (
1504- node . start ,
1505- node . end ,
1506- `export { ${ names . join ( ', ' ) } } from ${ JSON . stringify ( source ) } ;` ,
1507- )
1508- }
1382+ ) : TransformExpandExportAllContext {
1383+ return {
1384+ resolve : async ( source , importer ) => {
1385+ return ( await ctx . resolve ( source , importer ) ) ?. id
1386+ } ,
1387+ load : async ( id ) => {
1388+ // Read the source from disk and strip TS/JSX so the AST walk sees
1389+ // standard ESM exports. We don't go through `this.load` /
1390+ // `transformRequest` here — in dev they return module-runner output
1391+ // (`__vite_ssr_exportName__(...)`) the walker can't read, and on build
1392+ // there's no practical benefit over reading the source directly for the
1393+ // simple TS/JSX modules we care about.
1394+ const raw = await fs . promises . readFile ( id , 'utf-8' )
1395+ const transform = vite . transformWithOxc ?? vite . transformWithEsbuild
1396+ const result = await transform ( raw , id , { sourcemap : false } )
1397+ return parseAstAsync ( result . code )
1398+ } ,
15091399 }
1510- if ( ! output . hasChanged ( ) ) return null
1511- const newCode = output . toString ( )
1512- const newAst = await parseAstAsync ( newCode )
1513- return { code : newCode , ast : newAst }
15141400}
15151401
15161402function vitePluginUseClient (
@@ -1580,15 +1466,15 @@ function vitePluginUseClient(
15801466 }
15811467 }
15821468
1583- const expanded = await expandExportAllDeclarations (
1584- this ,
1585- ast ,
1469+ const expanded = await transformExpandExportAll ( {
15861470 code,
1587- id ,
1588- )
1471+ ast,
1472+ importer : id ,
1473+ ...createTransformExpandExportAllContext ( this ) ,
1474+ } )
15891475 if ( expanded ) {
15901476 code = expanded . code
1591- ast = expanded . ast
1477+ ast = await parseAstAsync ( code )
15921478 }
15931479
15941480 let importId : string
@@ -2065,15 +1951,15 @@ function vitePluginUseServer(
20651951 }
20661952 let ast = await parseAstAsync ( code )
20671953 if ( hasDirective ( ast . body , 'use server' ) ) {
2068- const expanded = await expandExportAllDeclarations (
2069- this ,
2070- ast ,
1954+ const expanded = await transformExpandExportAll ( {
20711955 code,
2072- id ,
2073- )
1956+ ast,
1957+ importer : id ,
1958+ ...createTransformExpandExportAllContext ( this ) ,
1959+ } )
20741960 if ( expanded ) {
20751961 code = expanded . code
2076- ast = expanded . ast
1962+ ast = await parseAstAsync ( code )
20771963 }
20781964 }
20791965
0 commit comments