From 50b097e803e1ac4c4f76e60a70e933dbc00441b4 Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Mon, 29 Apr 2024 16:55:07 +0200
Subject: [PATCH 1/3] Fix next/dynamic with babel and src dir
---
.../next/src/build/babel/loader/get-config.ts | 4 +-
.../babel/plugins/react-loadable-plugin.ts | 4 +-
packages/next/src/build/webpack-config.ts | 2 +-
test/development/basic/next-dynamic.test.ts | 363 ------------------
.../next-dynamic-babel-src-dir.test.ts | 3 +
.../next-dynamic/next-dynamic-babel.test.ts | 2 +
.../next-dynamic-base-path.test.ts | 2 +
.../next-dynamic-custom-document.test.ts | 2 +
.../basic/next-dynamic/next-dynamic.test.ts | 338 ++++++++++++++++
9 files changed, 352 insertions(+), 368 deletions(-)
delete mode 100644 test/development/basic/next-dynamic.test.ts
create mode 100644 test/development/basic/next-dynamic/next-dynamic-babel-src-dir.test.ts
create mode 100644 test/development/basic/next-dynamic/next-dynamic-babel.test.ts
create mode 100644 test/development/basic/next-dynamic/next-dynamic-base-path.test.ts
create mode 100644 test/development/basic/next-dynamic/next-dynamic-custom-document.test.ts
create mode 100644 test/development/basic/next-dynamic/next-dynamic.test.ts
diff --git a/packages/next/src/build/babel/loader/get-config.ts b/packages/next/src/build/babel/loader/get-config.ts
index ed52747491777..7f6421e2ff585 100644
--- a/packages/next/src/build/babel/loader/get-config.ts
+++ b/packages/next/src/build/babel/loader/get-config.ts
@@ -260,7 +260,7 @@ function getFreshConfig(
filename: string,
inputSourceMap?: object | null
) {
- let { isServer, pagesDir, development, hasJsxRuntime, configFile, cwd } =
+ let { isServer, pagesDir, development, hasJsxRuntime, configFile, srcDir } =
loaderOptions
let customConfig: any = configFile
@@ -329,7 +329,7 @@ function getFreshConfig(
supportsTopLevelAwait: true,
isServer,
- cwd,
+ srcDir,
pagesDir,
isDev: development,
hasJsxRuntime,
diff --git a/packages/next/src/build/babel/plugins/react-loadable-plugin.ts b/packages/next/src/build/babel/plugins/react-loadable-plugin.ts
index 9ef7fe50af5bd..8a5d2e5814db8 100644
--- a/packages/next/src/build/babel/plugins/react-loadable-plugin.ts
+++ b/packages/next/src/build/babel/plugins/react-loadable-plugin.ts
@@ -168,9 +168,9 @@ export default function ({
t.binaryExpression(
'+',
t.stringLiteral(
- (state.file.opts.caller?.cwd
+ (state.file.opts.caller?.srcDir
? relativePath(
- state.file.opts.caller.cwd,
+ state.file.opts.caller.srcDir,
state.file.opts.filename
)
: state.file.opts.filename) + ' -> '
diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts
index 2342810063bb7..2d813836accce 100644
--- a/packages/next/src/build/webpack-config.ts
+++ b/packages/next/src/build/webpack-config.ts
@@ -414,7 +414,7 @@ export default async function getBaseWebpackConfig(
isServer: isNodeOrEdgeCompilation,
distDir,
pagesDir,
- cwd: dir,
+ srcDir: path.dirname((appDir || pagesDir)!),
development: dev,
hasReactRefresh: dev && isClient,
hasJsxRuntime: true,
diff --git a/test/development/basic/next-dynamic.test.ts b/test/development/basic/next-dynamic.test.ts
deleted file mode 100644
index 1ad7d8e39c09a..0000000000000
--- a/test/development/basic/next-dynamic.test.ts
+++ /dev/null
@@ -1,363 +0,0 @@
-import { join } from 'path'
-import cheerio from 'cheerio'
-import webdriver from 'next-webdriver'
-import { createNext, FileRef } from 'e2e-utils'
-import { renderViaHTTP, check, hasRedbox } from 'next-test-utils'
-import { NextInstance } from 'e2e-utils'
-
-const customDocumentGipFiles = {
- 'pages/_document.js': `
- import { Html, Main, NextScript, Head } from 'next/document'
-
- export default function Document() {
- return (
-
-
-
-
-
-
-
- )
- }
-
- Document.getInitialProps = (ctx) => {
- return ctx.defaultGetInitialProps(ctx)
- }
- `,
-}
-
-describe.each([
- ['', 'swc'],
- ['/docs', 'swc'],
- ['', 'document.getInitialProps'],
- ['', 'babel'],
-])(
- 'basic next/dynamic usage, basePath: %p with %p compiler',
- (
- basePath: string,
- testCase: 'swc' | 'babel' | 'document.getInitialProps'
- ) => {
- ;(process.env.TURBOPACK && testCase === 'babel' ? describe.skip : describe)(
- testCase,
- () => {
- let next: NextInstance
-
- beforeAll(async () => {
- next = await createNext({
- files: {
- components: new FileRef(
- join(__dirname, 'next-dynamic/components')
- ),
- pages: new FileRef(join(__dirname, 'next-dynamic/pages')),
- ...(testCase === 'document.getInitialProps' &&
- customDocumentGipFiles),
- ...(testCase === 'babel' && {
- '.babelrc': `{ "presets": ["next/babel"] }`,
- }),
- },
- nextConfig: {
- basePath,
- },
- })
- })
- afterAll(() => next.destroy())
-
- async function get$(path, query?: any) {
- const html = await renderViaHTTP(next.url, path, query)
- return cheerio.load(html)
- }
-
- describe('Dynamic import', () => {
- describe('default behavior', () => {
- it('should render dynamic import components', async () => {
- const $ = await get$(basePath + '/dynamic/ssr')
- // Make sure the client side knows it has to wait for the bundle
- expect(
- JSON.parse($('#__NEXT_DATA__').html()).dynamicIds
- ).toContain('pages/dynamic/ssr.js -> ../../components/hello1')
- expect($('body').text()).toMatch(/Hello World 1/)
- })
-
- it('should render dynamic import components using a function as first parameter', async () => {
- const $ = await get$(basePath + '/dynamic/function')
- // Make sure the client side knows it has to wait for the bundle
- expect(
- JSON.parse($('#__NEXT_DATA__').html()).dynamicIds
- ).toContain(
- 'pages/dynamic/function.js -> ../../components/hello1'
- )
- expect($('body').text()).toMatch(/Hello World 1/)
- })
-
- it('should render even there are no physical chunk exists', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/no-chunk'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Welcome, normal/
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Welcome, dynamic/
- )
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
-
- it('should SSR nested dynamic components and skip nonSSR ones', async () => {
- const $ = await get$(basePath + '/dynamic/nested')
- const text = $('#__next').text()
- expect(text).toContain('Nested 1')
- expect(text).toContain('Nested 2')
- expect(text).not.toContain('Browser hydrated')
- })
-
- it('should hydrate nested chunks', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/nested'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Nested 1/
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Nested 2/
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Browser hydrated/
- )
-
- if ((global as any).browserName === 'chrome') {
- const logs = await browser.log('browser')
-
- logs.forEach((logItem) => {
- expect(logItem.message).not.toMatch(
- /Expected server HTML to contain/
- )
- })
- }
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
-
- it('should render the component Head content', async () => {
- let browser
- try {
- browser = await webdriver(next.url, basePath + '/dynamic/head')
- await check(() => browser.elementByCss('body').text(), /test/)
- const backgroundColor = await browser
- .elementByCss('.dynamic-style')
- .getComputedCss('background-color')
- const height = await browser
- .elementByCss('.dynamic-style')
- .getComputedCss('height')
- expect(height).toBe('200px')
- expect(backgroundColor).toMatch(/rgba?\(0, 128, 0/)
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
- })
- describe('ssr:false option', () => {
- it('should not render loading on the server side', async () => {
- const $ = await get$(basePath + '/dynamic/no-ssr')
- expect($('body').html()).not.toContain('"dynamicIds"')
- expect($('body').text()).not.toMatch('loading...')
- })
-
- it('should render the component on client side', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/no-ssr'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /navigator/
- )
- expect(await hasRedbox(browser)).toBe(false)
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
-
- it('should import and render the ESM module correctly on client side', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/no-ssr-esm'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /esm.mjs/
- )
- expect(await hasRedbox(browser)).toBe(false)
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
- })
-
- describe('ssr:true option', () => {
- it('Should render the component on the server side', async () => {
- const $ = await get$(basePath + '/dynamic/ssr-true')
- expect($('body').html()).toContain('"dynamicIds"')
- expect($('p').text()).toBe('Hello World 1')
- })
-
- it('should render the component on client side', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/ssr-true'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Hello World 1/
- )
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
-
- if (!(global as any).isNextDev) {
- it('should not include ssr:false imports to server trace', async () => {
- const trace = JSON.parse(
- await next.readFile(
- '.next/server/pages/dynamic/no-ssr.js.nft.json'
- )
- ) as { files: string[] }
- expect(trace).not.toContain('navigator')
- })
- }
- })
- // Turbopack doesn't have this feature.
- ;(process.env.TURBOPACK ? describe.skip : describe)(
- 'custom chunkfilename',
- () => {
- it('should render the correct filename', async () => {
- const $ = await get$(basePath + '/dynamic/chunkfilename')
- expect($('body').text()).toMatch(/test chunkfilename/)
- expect($('html').html()).toMatch(/hello-world\.js/)
- })
-
- it('should render the component on client side', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/chunkfilename'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /test chunkfilename/
- )
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
- }
- )
-
- describe('custom loading', () => {
- it('should render custom loading on the server side when `ssr:false` and `loading` is provided', async () => {
- const $ = await get$(basePath + '/dynamic/no-ssr-custom-loading')
- expect($('p').text()).toBe('LOADING')
- })
-
- it('should render the component on client side', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/no-ssr-custom-loading'
- )
- await check(
- () => browser.elementByCss('body').text(),
- /Hello World 1/
- )
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
- })
-
- // TODO: Make this test work with Turbopack. Currently the test relies on `chunkFileName` which is not supported by Turbopack.
- ;(process.env.TURBOPACK ? describe.skip : describe)(
- 'Multiple modules',
- () => {
- it('should only include the rendered module script tag', async () => {
- const $ = await get$(basePath + '/dynamic/multiple-modules')
- const html = $('html').html()
- expect(html).toMatch(/hello1\.js/)
- expect(html).not.toMatch(/hello2\.js/)
- })
-
- it('should only load the rendered module in the browser', async () => {
- let browser
- try {
- browser = await webdriver(
- next.url,
- basePath + '/dynamic/multiple-modules'
- )
- const html = await browser.eval(
- 'document.documentElement.innerHTML'
- )
- expect(html).toMatch(/hello1\.js/)
- expect(html).not.toMatch(/hello2\.js/)
- } finally {
- if (browser) {
- await browser.close()
- }
- }
- })
-
- it('should only render one bundle if component is used multiple times', async () => {
- const $ = await get$(basePath + '/dynamic/multiple-modules')
- const html = $('html').html()
- try {
- expect(html.match(/chunks[\\/]hello1\.js/g).length).toBe(1)
- expect(html).not.toMatch(/hello2\.js/)
- } catch (err) {
- console.error(html)
- throw err
- }
- })
- }
- )
- })
- }
- )
- }
-)
diff --git a/test/development/basic/next-dynamic/next-dynamic-babel-src-dir.test.ts b/test/development/basic/next-dynamic/next-dynamic-babel-src-dir.test.ts
new file mode 100644
index 0000000000000..a607ace1509a5
--- /dev/null
+++ b/test/development/basic/next-dynamic/next-dynamic-babel-src-dir.test.ts
@@ -0,0 +1,3 @@
+process.env.TEST_BABEL = '1'
+process.env.TEST_SRC_DIR = '1'
+require('./next-dynamic.test')
diff --git a/test/development/basic/next-dynamic/next-dynamic-babel.test.ts b/test/development/basic/next-dynamic/next-dynamic-babel.test.ts
new file mode 100644
index 0000000000000..06a94c626a5a3
--- /dev/null
+++ b/test/development/basic/next-dynamic/next-dynamic-babel.test.ts
@@ -0,0 +1,2 @@
+process.env.TEST_BABEL = '1'
+require('./next-dynamic.test')
diff --git a/test/development/basic/next-dynamic/next-dynamic-base-path.test.ts b/test/development/basic/next-dynamic/next-dynamic-base-path.test.ts
new file mode 100644
index 0000000000000..67ff2432a2c5c
--- /dev/null
+++ b/test/development/basic/next-dynamic/next-dynamic-base-path.test.ts
@@ -0,0 +1,2 @@
+process.env.TEST_BASE_PATH = '/docs'
+require('./next-dynamic.test')
diff --git a/test/development/basic/next-dynamic/next-dynamic-custom-document.test.ts b/test/development/basic/next-dynamic/next-dynamic-custom-document.test.ts
new file mode 100644
index 0000000000000..21f841200c0a6
--- /dev/null
+++ b/test/development/basic/next-dynamic/next-dynamic-custom-document.test.ts
@@ -0,0 +1,2 @@
+process.env.TEST_CUSTOMIZED_DOCUMENT = '1'
+require('./next-dynamic.test')
diff --git a/test/development/basic/next-dynamic/next-dynamic.test.ts b/test/development/basic/next-dynamic/next-dynamic.test.ts
new file mode 100644
index 0000000000000..be8681198c2cb
--- /dev/null
+++ b/test/development/basic/next-dynamic/next-dynamic.test.ts
@@ -0,0 +1,338 @@
+import { join } from 'path'
+import cheerio from 'cheerio'
+import webdriver from 'next-webdriver'
+import { createNext, FileRef } from 'e2e-utils'
+import { renderViaHTTP, check, hasRedbox } from 'next-test-utils'
+import { NextInstance } from 'e2e-utils'
+
+const customDocumentGipContent = `\
+import { Html, Main, NextScript, Head } from 'next/document'
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+Document.getInitialProps = (ctx) => {
+ return ctx.defaultGetInitialProps(ctx)
+}
+`
+
+// describe.each([
+// ['', 'swc'],
+// ['/docs', 'swc'],
+// ['', 'document.getInitialProps'],
+// ['',]
+// // ['', 'babel'],
+// ])(
+// 'basic next/dynamic usage, basePath: %p with %p',
+// // (
+// // basePath: string,
+// // testCase:
+// // 'swc'
+// // | 'babel'
+// // | 'document.getInitialProps'
+// ) => {
+// // process.env.TURBOPACK && testCase === 'babel' ? describe.skip :
+
+// }
+// )
+
+const basePath = process.env.TEST_BASE_PATH || ''
+const srcPrefix = process.env.TEST_SRC_DIR ? 'src/' : ''
+
+describe('next/dynamic', () => {
+ let next: NextInstance
+
+ beforeAll(async () => {
+ next = await createNext({
+ files: {
+ [`${srcPrefix}/components`]: new FileRef(join(__dirname, 'components')),
+ [`${srcPrefix}/pages`]: new FileRef(join(__dirname, 'pages')),
+ ...(process.env.TEST_CUSTOMIZED_DOCUMENT === '1' && {
+ [`${srcPrefix}/pages/_document.js`]: customDocumentGipContent,
+ }),
+ ...(process.env.TEST_BABEL === '1' && {
+ '.babelrc': `{ "presets": ["next/babel"] }`,
+ }),
+ },
+ nextConfig: {
+ basePath,
+ },
+ })
+ })
+ afterAll(() => next.destroy())
+
+ async function get$(path, query?: any) {
+ const html = await renderViaHTTP(next.url, path, query)
+ return cheerio.load(html)
+ }
+
+ ;(process.env.TURBOPACK && process.env.TEST_BABEL === '1'
+ ? describe.skip
+ : describe)('Dynamic import', () => {
+ describe('default behavior', () => {
+ it('should render dynamic import components', async () => {
+ const $ = await get$(basePath + '/dynamic/ssr')
+ // Make sure the client side knows it has to wait for the bundle
+ expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
+ 'pages/dynamic/ssr.js -> ../../components/hello1'
+ )
+ expect($('body').text()).toMatch(/Hello World 1/)
+ })
+
+ it('should render dynamic import components using a function as first parameter', async () => {
+ const $ = await get$(basePath + '/dynamic/function')
+ // Make sure the client side knows it has to wait for the bundle
+ expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
+ 'pages/dynamic/function.js -> ../../components/hello1'
+ )
+ expect($('body').text()).toMatch(/Hello World 1/)
+ })
+
+ it('should render even there are no physical chunk exists', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/no-chunk')
+ await check(
+ () => browser.elementByCss('body').text(),
+ /Welcome, normal/
+ )
+ await check(
+ () => browser.elementByCss('body').text(),
+ /Welcome, dynamic/
+ )
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+
+ it('should SSR nested dynamic components and skip nonSSR ones', async () => {
+ const $ = await get$(basePath + '/dynamic/nested')
+ const text = $('#__next').text()
+ expect(text).toContain('Nested 1')
+ expect(text).toContain('Nested 2')
+ expect(text).not.toContain('Browser hydrated')
+ })
+
+ it('should hydrate nested chunks', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/nested')
+ await check(() => browser.elementByCss('body').text(), /Nested 1/)
+ await check(() => browser.elementByCss('body').text(), /Nested 2/)
+ await check(
+ () => browser.elementByCss('body').text(),
+ /Browser hydrated/
+ )
+
+ if ((global as any).browserName === 'chrome') {
+ const logs = await browser.log('browser')
+
+ logs.forEach((logItem) => {
+ expect(logItem.message).not.toMatch(
+ /Expected server HTML to contain/
+ )
+ })
+ }
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+
+ it('should render the component Head content', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/head')
+ await check(() => browser.elementByCss('body').text(), /test/)
+ const backgroundColor = await browser
+ .elementByCss('.dynamic-style')
+ .getComputedCss('background-color')
+ const height = await browser
+ .elementByCss('.dynamic-style')
+ .getComputedCss('height')
+ expect(height).toBe('200px')
+ expect(backgroundColor).toMatch(/rgba?\(0, 128, 0/)
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+ })
+ describe('ssr:false option', () => {
+ it('should not render loading on the server side', async () => {
+ const $ = await get$(basePath + '/dynamic/no-ssr')
+ expect($('body').html()).not.toContain('"dynamicIds"')
+ expect($('body').text()).not.toMatch('loading...')
+ })
+
+ it('should render the component on client side', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/no-ssr')
+ await check(() => browser.elementByCss('body').text(), /navigator/)
+ expect(await hasRedbox(browser)).toBe(false)
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+
+ it('should import and render the ESM module correctly on client side', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/no-ssr-esm')
+ await check(() => browser.elementByCss('body').text(), /esm.mjs/)
+ expect(await hasRedbox(browser)).toBe(false)
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+ })
+
+ describe('ssr:true option', () => {
+ it('Should render the component on the server side', async () => {
+ const $ = await get$(basePath + '/dynamic/ssr-true')
+ expect($('body').html()).toContain('"dynamicIds"')
+ expect($('p').text()).toBe('Hello World 1')
+ })
+
+ it('should render the component on client side', async () => {
+ let browser
+ try {
+ browser = await webdriver(next.url, basePath + '/dynamic/ssr-true')
+ await check(
+ () => browser.elementByCss('body').text(),
+ /Hello World 1/
+ )
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+
+ if (!(global as any).isNextDev) {
+ it('should not include ssr:false imports to server trace', async () => {
+ const trace = JSON.parse(
+ await next.readFile('.next/server/pages/dynamic/no-ssr.js.nft.json')
+ ) as { files: string[] }
+ expect(trace).not.toContain('navigator')
+ })
+ }
+ })
+ // Turbopack doesn't have this feature.
+ ;(process.env.TURBOPACK ? describe.skip : describe)(
+ 'custom chunkfilename',
+ () => {
+ it('should render the correct filename', async () => {
+ const $ = await get$(basePath + '/dynamic/chunkfilename')
+ expect($('body').text()).toMatch(/test chunkfilename/)
+ expect($('html').html()).toMatch(/hello-world\.js/)
+ })
+
+ it('should render the component on client side', async () => {
+ let browser
+ try {
+ browser = await webdriver(
+ next.url,
+ basePath + '/dynamic/chunkfilename'
+ )
+ await check(
+ () => browser.elementByCss('body').text(),
+ /test chunkfilename/
+ )
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+ }
+ )
+
+ describe('custom loading', () => {
+ it('should render custom loading on the server side when `ssr:false` and `loading` is provided', async () => {
+ const $ = await get$(basePath + '/dynamic/no-ssr-custom-loading')
+ expect($('p').text()).toBe('LOADING')
+ })
+
+ it('should render the component on client side', async () => {
+ let browser
+ try {
+ browser = await webdriver(
+ next.url,
+ basePath + '/dynamic/no-ssr-custom-loading'
+ )
+ await check(
+ () => browser.elementByCss('body').text(),
+ /Hello World 1/
+ )
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+ })
+
+ // TODO: Make this test work with Turbopack. Currently the test relies on `chunkFileName` which is not supported by Turbopack.
+ ;(process.env.TURBOPACK ? describe.skip : describe)(
+ 'Multiple modules',
+ () => {
+ it('should only include the rendered module script tag', async () => {
+ const $ = await get$(basePath + '/dynamic/multiple-modules')
+ const html = $('html').html()
+ expect(html).toMatch(/hello1\.js/)
+ expect(html).not.toMatch(/hello2\.js/)
+ })
+
+ it('should only load the rendered module in the browser', async () => {
+ let browser
+ try {
+ browser = await webdriver(
+ next.url,
+ basePath + '/dynamic/multiple-modules'
+ )
+ const html = await browser.eval(
+ 'document.documentElement.innerHTML'
+ )
+ expect(html).toMatch(/hello1\.js/)
+ expect(html).not.toMatch(/hello2\.js/)
+ } finally {
+ if (browser) {
+ await browser.close()
+ }
+ }
+ })
+
+ it('should only render one bundle if component is used multiple times', async () => {
+ const $ = await get$(basePath + '/dynamic/multiple-modules')
+ const html = $('html').html()
+ try {
+ expect(html.match(/chunks[\\/]hello1\.js/g).length).toBe(1)
+ expect(html).not.toMatch(/hello2\.js/)
+ } catch (err) {
+ console.error(html)
+ throw err
+ }
+ })
+ }
+ )
+ })
+})
From c17f029090cdf521b42a248a5378f95f113a1984 Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Tue, 30 Apr 2024 00:20:40 +0200
Subject: [PATCH 2/3] fix types
---
packages/next/src/build/babel/loader/types.d.ts | 1 +
packages/next/src/build/webpack-config.ts | 1 +
2 files changed, 2 insertions(+)
diff --git a/packages/next/src/build/babel/loader/types.d.ts b/packages/next/src/build/babel/loader/types.d.ts
index 49dedf35776ec..ddf223ac33ec1 100644
--- a/packages/next/src/build/babel/loader/types.d.ts
+++ b/packages/next/src/build/babel/loader/types.d.ts
@@ -17,4 +17,5 @@ export interface NextBabelLoaderOptions {
caller: any
configFile: string | undefined
cwd: string
+ srcDir: string
}
diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts
index 2d813836accce..f0f83888b809c 100644
--- a/packages/next/src/build/webpack-config.ts
+++ b/packages/next/src/build/webpack-config.ts
@@ -415,6 +415,7 @@ export default async function getBaseWebpackConfig(
distDir,
pagesDir,
srcDir: path.dirname((appDir || pagesDir)!),
+ cwd: dir,
development: dev,
hasReactRefresh: dev && isClient,
hasJsxRuntime: true,
From 06be666a85a910aec0b3fea01b3907d00658a3d2 Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Tue, 30 Apr 2024 00:25:25 +0200
Subject: [PATCH 3/3] clean up test
---
.../basic/next-dynamic/next-dynamic.test.ts | 29 ++++---------------
1 file changed, 6 insertions(+), 23 deletions(-)
diff --git a/test/development/basic/next-dynamic/next-dynamic.test.ts b/test/development/basic/next-dynamic/next-dynamic.test.ts
index be8681198c2cb..95a9c7b5fd530 100644
--- a/test/development/basic/next-dynamic/next-dynamic.test.ts
+++ b/test/development/basic/next-dynamic/next-dynamic.test.ts
@@ -25,26 +25,6 @@ Document.getInitialProps = (ctx) => {
}
`
-// describe.each([
-// ['', 'swc'],
-// ['/docs', 'swc'],
-// ['', 'document.getInitialProps'],
-// ['',]
-// // ['', 'babel'],
-// ])(
-// 'basic next/dynamic usage, basePath: %p with %p',
-// // (
-// // basePath: string,
-// // testCase:
-// // 'swc'
-// // | 'babel'
-// // | 'document.getInitialProps'
-// ) => {
-// // process.env.TURBOPACK && testCase === 'babel' ? describe.skip :
-
-// }
-// )
-
const basePath = process.env.TEST_BASE_PATH || ''
const srcPrefix = process.env.TEST_SRC_DIR ? 'src/' : ''
@@ -59,9 +39,11 @@ describe('next/dynamic', () => {
...(process.env.TEST_CUSTOMIZED_DOCUMENT === '1' && {
[`${srcPrefix}/pages/_document.js`]: customDocumentGipContent,
}),
- ...(process.env.TEST_BABEL === '1' && {
- '.babelrc': `{ "presets": ["next/babel"] }`,
- }),
+ // When it's not turbopack and babel is enabled, we add a .babelrc file.
+ ...(!process.env.TURBOPACK &&
+ process.env.TEST_BABEL === '1' && {
+ '.babelrc': `{ "presets": ["next/babel"] }`,
+ }),
},
nextConfig: {
basePath,
@@ -75,6 +57,7 @@ describe('next/dynamic', () => {
return cheerio.load(html)
}
+ // Turbopack doesn't support babel.
;(process.env.TURBOPACK && process.env.TEST_BABEL === '1'
? describe.skip
: describe)('Dynamic import', () => {