Skip to content

Commit

Permalink
fix regex and add ut
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed May 19, 2023
1 parent 6e90798 commit ee95d48
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 18 deletions.
20 changes: 20 additions & 0 deletions packages/next/src/build/webpack/loaders/get-module-build-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@ import type {
} from '../../analysis/get-page-static-info'
import { webpack } from 'next/dist/compiled/webpack/webpack'

export function extractCjsExports(source: string) {
// In case the client entry is a CJS module, we need to parse all the exports
// to make sure that the flight manifest plugin can correctly generate the
// manifest.
// TODO: Currently SWC wraps CJS exports with `_export(exports, { ... })`,
// which is tricky to statically analyze. But since the shape is known, we
// use a regex to extract the exports as a workaround. See:
// https://github.com/swc-project/swc/blob/5629e6b5291b416c8316587b67b5e83d011a8c22/crates/swc_ecma_transforms_module/src/util.rs#L295

const matchExportObject = source.match(
/(?<=_export\(exports, {)(.*?)(?=}\);)/gs
)

if (matchExportObject) {
// Match the property name with format <property>: function() ...
return matchExportObject[0].match(/\b\w+(?=:)/g)
}
return null
}

/**
* A getter for module build info that casts to the type it should have.
* We also expose here types to make easier to use it.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getRSCModuleInformation } from '../../analysis/get-page-static-info'
import { getModuleBuildInfo } from './get-module-build-info'
import { getModuleBuildInfo, extractCjsExports } from './get-module-build-info'

export default async function transformSource(
this: any,
Expand All @@ -18,22 +18,8 @@ export default async function transformSource(
buildInfo.rsc = getRSCModuleInformation(source, false)

if (buildInfo.rsc.isClientRef) {
// In case the client entry is a CJS module, we need to parse all the exports
// to make sure that the flight manifest plugin can correctly generate the
// manifest.
// TODO: Currently SWC wraps CJS exports with `_export(exports, { ... })`,
// which is tricky to statically analyze. But since the shape is known, we
// use a regex to extract the exports as a workaround. See:
// https://github.com/swc-project/swc/blob/5629e6b5291b416c8316587b67b5e83d011a8c22/crates/swc_ecma_transforms_module/src/util.rs#L295
const matchExportObject = source.match(/\n_export\(exports, {([^}]*)}\)/m)
if (matchExportObject) {
const matches: string[] = []
const matchExports = matchExportObject[1].matchAll(/([^\s]+):/g)
for (const match of matchExports) {
matches.push(match[1])
}
buildInfo.rsc.clientRefs = matches
}
const exportNames = extractCjsExports(source)
if (exportNames) buildInfo.rsc.clientRefs = exportNames
}

// This is a server action entry module in the client layer. We need to attach
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/app-dir/rsc-basic/rsc-basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ createNextDescribe(
({ next, isNextDev, isNextStart }) => {
if (isNextDev) {
it('should have correct client references keys in manifest', async () => {
await next.render('/')
// Check that the client-side manifest is correct before any requests
const clientReferenceManifest = JSON.parse(
await next.readFile('.next/server/client-reference-manifest.json')
Expand All @@ -44,7 +45,7 @@ createNextDescribe(
clientReferenceManifest.clientModules
)
clientModulesNames.every((name) => {
const [_, key] = name.split('#')
const [, key] = name.split('#')
return key === undefined || key === '' || key === 'default'
})
})
Expand Down
30 changes: 30 additions & 0 deletions test/unit/get-module-build-info.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { extractCjsExports } from 'next/dist/build/webpack/loaders/get-module-build-info'

describe('extractCjsExports', () => {
it('should extract exports', () => {
const exportNames = extractCjsExports(`
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getServerActionDispatcher: function() {
return getServerActionDispatcher;
},
urlToUrlWithoutFlightMarker: function() {
return urlToUrlWithoutFlightMarker;
},
default: function() {
return AppRouter;
}
});
`)
expect(exportNames).toEqual([
'getServerActionDispatcher',
'urlToUrlWithoutFlightMarker',
'default',
])
})
})

0 comments on commit ee95d48

Please sign in to comment.