diff --git a/packages/next/src/build/webpack/config/blocks/css/index.ts b/packages/next/src/build/webpack/config/blocks/css/index.ts
index 99d3f0117e8d2..c62e1e5a27548 100644
--- a/packages/next/src/build/webpack/config/blocks/css/index.ts
+++ b/packages/next/src/build/webpack/config/blocks/css/index.ts
@@ -619,7 +619,11 @@ export const css = curry(async function css(
insert: function (linkTag: HTMLLinkElement) {
if (typeof _N_E_STYLE_LOAD === 'function') {
const { href, onload, onerror } = linkTag
- _N_E_STYLE_LOAD(new URL(href).pathname).then(
+ _N_E_STYLE_LOAD(
+ href.indexOf(window.location.origin) === 0
+ ? new URL(href).pathname
+ : href
+ ).then(
() => onload?.call(linkTag, { type: 'load' } as Event),
() => onerror?.call(linkTag, {} as Event)
)
diff --git a/test/integration/next-dynamic-css-asset-prefix/next.config.js b/test/integration/next-dynamic-css-asset-prefix/next.config.js
new file mode 100644
index 0000000000000..58a0de71a8500
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/next.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ assetPrefix: 'http://localhost:__CDN_PORT__/path-prefix',
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/Component2.jsx b/test/integration/next-dynamic-css-asset-prefix/src/Component2.jsx
new file mode 100644
index 0000000000000..fa1681a821668
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/Component2.jsx
@@ -0,0 +1,27 @@
+import styles from './Component2.module.scss'
+
+export default function Content2() {
+ return (
+
+
Where does it come from?
+
+ Contrary to popular belief, Lorem Ipsum is not simply random text. It
+ has roots in a piece of classical Latin literature from 45 BC, making it
+ over 2000 years old. Richard McClintock, a Latin professor at
+ Hampden-Sydney College in Virginia, looked up one of the more obscure
+ Latin words, consectetur, from a Lorem Ipsum passage, and going through
+ the cites of the word in classical literature, discovered the
+ undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33
+ of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by
+ Cicero, written in 45 BC. This book is a treatise on the theory of
+ ethics, very popular during the Renaissance. The first line of Lorem
+ Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section
+ 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is
+ reproduced below for those interested. Sections 1.10.32 and 1.10.33 from
+ "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their
+ exact original form, accompanied by English versions from the 1914
+ translation by H. Rackham.
+
+
+ )
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/Component2.module.scss b/test/integration/next-dynamic-css-asset-prefix/src/Component2.module.scss
new file mode 100644
index 0000000000000..c899a8eb6492e
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/Component2.module.scss
@@ -0,0 +1,13 @@
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/Content.jsx b/test/integration/next-dynamic-css-asset-prefix/src/Content.jsx
new file mode 100644
index 0000000000000..7b46fb3ede7be
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/Content.jsx
@@ -0,0 +1,29 @@
+import styles from './Content.module.css'
+import Content2 from './Component2'
+
+export default function Content() {
+ return (
+
+
Where does it come from?
+
+ Contrary to popular belief, Lorem Ipsum is not simply random text. It
+ has roots in a piece of classical Latin literature from 45 BC, making it
+ over 2000 years old. Richard McClintock, a Latin professor at
+ Hampden-Sydney College in Virginia, looked up one of the more obscure
+ Latin words, consectetur, from a Lorem Ipsum passage, and going through
+ the cites of the word in classical literature, discovered the
+ undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33
+ of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by
+ Cicero, written in 45 BC. This book is a treatise on the theory of
+ ethics, very popular during the Renaissance. The first line of Lorem
+ Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section
+ 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is
+ reproduced below for those interested. Sections 1.10.32 and 1.10.33 from
+ "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their
+ exact original form, accompanied by English versions from the 1914
+ translation by H. Rackham.
+
+
+
+ )
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/Content.module.css b/test/integration/next-dynamic-css-asset-prefix/src/Content.module.css
new file mode 100644
index 0000000000000..c899a8eb6492e
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/Content.module.css
@@ -0,0 +1,13 @@
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/Content4.module.css b/test/integration/next-dynamic-css-asset-prefix/src/Content4.module.css
new file mode 100644
index 0000000000000..c0fda1fa58a6e
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/Content4.module.css
@@ -0,0 +1,1067 @@
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
+.header {
+ font-style: italic;
+}
+
+.container {
+ background-color: #dddddd;
+ padding: 1rem;
+}
+
+.textContent {
+ color: #666;
+ letter-spacing: -1px;
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/app/layout.tsx b/test/integration/next-dynamic-css-asset-prefix/src/app/layout.tsx
new file mode 100644
index 0000000000000..a14e64fcd5e33
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/app/layout.tsx
@@ -0,0 +1,16 @@
+export const metadata = {
+ title: 'Next.js',
+ description: 'Generated by Next.js',
+}
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/app/test-app/page.tsx b/test/integration/next-dynamic-css-asset-prefix/src/app/test-app/page.tsx
new file mode 100644
index 0000000000000..da2e183be2c37
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/app/test-app/page.tsx
@@ -0,0 +1,20 @@
+'use client'
+
+import React, { useState } from 'react'
+import style from '../../Content4.module.css'
+import { Comp } from '../../inner/k'
+
+export default function Index() {
+ const [s] = useState(true)
+
+ if (s) {
+ return (
+ <>
+
+
+ >
+ )
+ }
+
+ return null
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/inner/k.jsx b/test/integration/next-dynamic-css-asset-prefix/src/inner/k.jsx
new file mode 100644
index 0000000000000..56b2a28adc462
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/inner/k.jsx
@@ -0,0 +1,3 @@
+import dynamic from 'next/dynamic'
+
+export const Comp = dynamic(() => import('../Content'), { ssr: false })
diff --git a/test/integration/next-dynamic-css-asset-prefix/src/pages/index.jsx b/test/integration/next-dynamic-css-asset-prefix/src/pages/index.jsx
new file mode 100644
index 0000000000000..8e13520c289c1
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/src/pages/index.jsx
@@ -0,0 +1,24 @@
+import React, { useState } from 'react'
+import style from '../Content4.module.css'
+import { Comp } from '../inner/k'
+
+export default function Index() {
+ const [s] = useState(true)
+
+ if (s) {
+ return (
+ <>
+
+
+ >
+ )
+ }
+
+ return null
+}
+
+export const getServerSideProps = () => {
+ return {
+ props: {},
+ }
+}
diff --git a/test/integration/next-dynamic-css-asset-prefix/test/index.test.js b/test/integration/next-dynamic-css-asset-prefix/test/index.test.js
new file mode 100644
index 0000000000000..c707a4471d65a
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/test/index.test.js
@@ -0,0 +1,169 @@
+/* eslint-env jest */
+
+import webdriver from 'next-webdriver'
+import { createServer, request } from 'http'
+import { join, resolve } from 'path'
+import {
+ findPort,
+ launchApp,
+ killApp,
+ nextBuild,
+ nextStart,
+ File,
+} from 'next-test-utils'
+
+const appDir = join(__dirname, '..')
+const nextConfig = new File(resolve(__dirname, '../next.config.js'))
+
+let appPort
+let cdnPort
+let app
+let cdn
+
+function runTests() {
+ it('should load a Pages Router page correctly', async () => {
+ const browser = await webdriver(appPort, '/')
+
+ expect(
+ await browser
+ .elementByCss('#__next div:nth-child(2)')
+ .getComputedCss('background-color')
+ ).toContain('221, 221, 221')
+
+ expect(await browser.eval('document.documentElement.innerHTML')).toContain(
+ 'Where does it come from?'
+ )
+ })
+
+ it('should load a App Router page correctly', async () => {
+ const browser = await webdriver(appPort, '/test-app')
+
+ expect(
+ await browser
+ .elementByCss('body div:nth-child(3)')
+ .getComputedCss('background-color')
+ ).toContain('221, 221, 221')
+
+ expect(await browser.eval('document.documentElement.innerHTML')).toContain(
+ 'Where does it come from?'
+ )
+ })
+}
+
+describe('next/dynamic with assetPrefix', () => {
+ ;(process.env.TURBOPACK_BUILD ? describe.skip : describe)(
+ 'development mode',
+ () => {
+ beforeAll(async () => {
+ cdnPort = await findPort()
+ // lightweight http proxy
+ cdn = createServer((clientReq, clientRes) => {
+ const proxyPath = clientReq.url.slice('/path-prefix'.length)
+ const proxyReq = request(
+ {
+ hostname: 'localhost',
+ port: appPort,
+ path: proxyPath,
+ method: clientReq.method,
+ headers: clientReq.headers,
+ },
+ (proxyRes) => {
+ // cdn must be configured to allow requests from this origin
+ proxyRes.headers['Access-Control-Allow-Origin'] =
+ `http://localhost:${appPort}`
+ clientRes.writeHead(proxyRes.statusCode, proxyRes.headers)
+ // [NOTE] if socket doesn't have a handler to error event and if error
+ // event leaks, node.js ends its process with errored exit code.
+ // However, there can be failing socket event while running test
+ // as long as assertion is correct, do not care indiviual socket errors.
+ proxyRes.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientRes.on('error', (e) => {
+ require('console').error(e)
+ })
+
+ proxyRes.pipe(clientRes, { end: true })
+ }
+ )
+
+ proxyReq.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientReq.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientReq.pipe(proxyReq, { end: true })
+ })
+ await new Promise((resolve) => cdn.listen(cdnPort, resolve))
+ nextConfig.replace('__CDN_PORT__', cdnPort)
+ appPort = await findPort()
+ app = await launchApp(appDir, appPort)
+ })
+
+ afterAll(() => killApp(app))
+ afterAll(() => cdn.close())
+ afterAll(() => nextConfig.restore())
+
+ runTests(true)
+ }
+ )
+ ;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
+ 'production mode',
+ () => {
+ beforeAll(async () => {
+ cdnPort = await findPort()
+ // lightweight http proxy
+ cdn = createServer((clientReq, clientRes) => {
+ const proxyPath = clientReq.url.slice('/path-prefix'.length)
+ const proxyReq = request(
+ {
+ hostname: 'localhost',
+ port: appPort,
+ path: proxyPath,
+ method: clientReq.method,
+ headers: clientReq.headers,
+ },
+ (proxyRes) => {
+ // cdn must be configured to allow requests from this origin
+ proxyRes.headers['Access-Control-Allow-Origin'] =
+ `http://localhost:${appPort}`
+ clientRes.writeHead(proxyRes.statusCode, proxyRes.headers)
+ // [NOTE] if socket doesn't have a handler to error event and if error
+ // event leaks, node.js ends its process with errored exit code.
+ // However, there can be failing socket event while running test
+ // as long as assertion is correct, do not care indiviual socket errors.
+ proxyRes.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientRes.on('error', (e) => {
+ require('console').error(e)
+ })
+
+ proxyRes.pipe(clientRes, { end: true })
+ }
+ )
+
+ proxyReq.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientReq.on('error', (e) => {
+ require('console').error(e)
+ })
+ clientReq.pipe(proxyReq, { end: true })
+ })
+ await new Promise((resolve) => cdn.listen(cdnPort, resolve))
+ nextConfig.replace('__CDN_PORT__', cdnPort)
+ await nextBuild(appDir)
+ appPort = await findPort()
+ app = await nextStart(appDir, appPort)
+ })
+
+ afterAll(() => killApp(app))
+ afterAll(() => cdn.close())
+ afterAll(() => nextConfig.restore())
+
+ runTests()
+ }
+ )
+})
diff --git a/test/integration/next-dynamic-css-asset-prefix/tsconfig.json b/test/integration/next-dynamic-css-asset-prefix/tsconfig.json
new file mode 100644
index 0000000000000..ed02d0b66740b
--- /dev/null
+++ b/test/integration/next-dynamic-css-asset-prefix/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "noEmit": true,
+ "incremental": true,
+ "module": "esnext",
+ "esModuleInterop": true,
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "strictNullChecks": true
+ },
+ "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/test/integration/next-dynamic-css/src/app/layout.tsx b/test/integration/next-dynamic-css/src/app/layout.tsx
new file mode 100644
index 0000000000000..a14e64fcd5e33
--- /dev/null
+++ b/test/integration/next-dynamic-css/src/app/layout.tsx
@@ -0,0 +1,16 @@
+export const metadata = {
+ title: 'Next.js',
+ description: 'Generated by Next.js',
+}
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/test/integration/next-dynamic-css/src/app/test-app/page.tsx b/test/integration/next-dynamic-css/src/app/test-app/page.tsx
new file mode 100644
index 0000000000000..da2e183be2c37
--- /dev/null
+++ b/test/integration/next-dynamic-css/src/app/test-app/page.tsx
@@ -0,0 +1,20 @@
+'use client'
+
+import React, { useState } from 'react'
+import style from '../../Content4.module.css'
+import { Comp } from '../../inner/k'
+
+export default function Index() {
+ const [s] = useState(true)
+
+ if (s) {
+ return (
+ <>
+
+
+ >
+ )
+ }
+
+ return null
+}
diff --git a/test/integration/next-dynamic-css/test/index.test.js b/test/integration/next-dynamic-css/test/index.test.js
index 77a4ed94019cd..c3f1603a65551 100644
--- a/test/integration/next-dynamic-css/test/index.test.js
+++ b/test/integration/next-dynamic-css/test/index.test.js
@@ -15,7 +15,7 @@ let appPort
const appDir = join(__dirname, '../')
function runTests() {
- it('should load page correctly', async () => {
+ it('should load a Pages Router page correctly', async () => {
const browser = await webdriver(appPort, '/')
expect(
@@ -28,6 +28,20 @@ function runTests() {
'Where does it come from?'
)
})
+
+ it('should load a App Router page correctly', async () => {
+ const browser = await webdriver(appPort, '/test-app')
+
+ expect(
+ await browser
+ .elementByCss('body div:nth-child(3)')
+ .getComputedCss('background-color')
+ ).toContain('221, 221, 221')
+
+ expect(await browser.eval('document.documentElement.innerHTML')).toContain(
+ 'Where does it come from?'
+ )
+ })
}
describe('next/dynamic', () => {
diff --git a/test/integration/next-dynamic-css/tsconfig.json b/test/integration/next-dynamic-css/tsconfig.json
new file mode 100644
index 0000000000000..ed02d0b66740b
--- /dev/null
+++ b/test/integration/next-dynamic-css/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "noEmit": true,
+ "incremental": true,
+ "module": "esnext",
+ "esModuleInterop": true,
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "strictNullChecks": true
+ },
+ "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}