diff --git a/packages/next/build/webpack/config/blocks/css/loaders/file-resolve.ts b/packages/next/build/webpack/config/blocks/css/loaders/file-resolve.ts
new file mode 100644
index 0000000000000..681ecdb72e0fd
--- /dev/null
+++ b/packages/next/build/webpack/config/blocks/css/loaders/file-resolve.ts
@@ -0,0 +1,6 @@
+export function cssFileResolve(url: string, _resourcePath: string) {
+ if (url.startsWith('/')) {
+ return false
+ }
+ return true
+}
diff --git a/packages/next/build/webpack/config/blocks/css/loaders/global.ts b/packages/next/build/webpack/config/blocks/css/loaders/global.ts
index fd708dc981bdb..1013e27b8078c 100644
--- a/packages/next/build/webpack/config/blocks/css/loaders/global.ts
+++ b/packages/next/build/webpack/config/blocks/css/loaders/global.ts
@@ -2,6 +2,7 @@ import postcss from 'postcss'
import webpack from 'webpack'
import { ConfigurationContext } from '../../../utils'
import { getClientStyleLoader } from './client'
+import { cssFileResolve } from './file-resolve'
export function getGlobalCssLoader(
ctx: ConfigurationContext,
@@ -29,6 +30,9 @@ export function getGlobalCssLoader(
sourceMap: true,
// Next.js controls CSS Modules eligibility:
modules: false,
+ url: cssFileResolve,
+ import: (url: string, _: any, resourcePath: string) =>
+ cssFileResolve(url, resourcePath),
},
})
diff --git a/packages/next/build/webpack/config/blocks/css/loaders/modules.ts b/packages/next/build/webpack/config/blocks/css/loaders/modules.ts
index f6dda1a83c382..891a14339b020 100644
--- a/packages/next/build/webpack/config/blocks/css/loaders/modules.ts
+++ b/packages/next/build/webpack/config/blocks/css/loaders/modules.ts
@@ -2,6 +2,7 @@ import postcss from 'postcss'
import webpack from 'webpack'
import { ConfigurationContext } from '../../../utils'
import { getClientStyleLoader } from './client'
+import { cssFileResolve } from './file-resolve'
import { getCssModuleLocalIdent } from './getCssModuleLocalIdent'
export function getCssModuleLoader(
@@ -30,6 +31,9 @@ export function getCssModuleLoader(
sourceMap: true,
// Use CJS mode for backwards compatibility:
esModule: false,
+ url: cssFileResolve,
+ import: (url: string, _: any, resourcePath: string) =>
+ cssFileResolve(url, resourcePath),
modules: {
// Do not transform class names (CJS mode backwards compatibility):
exportLocalsConvention: 'asIs',
diff --git a/test/integration/css-fixtures/unresolved-css-url/global.css b/test/integration/css-fixtures/unresolved-css-url/global.css
new file mode 100644
index 0000000000000..5932829d3997c
--- /dev/null
+++ b/test/integration/css-fixtures/unresolved-css-url/global.css
@@ -0,0 +1,31 @@
+p {
+ background-image: url('/vercel.svg');
+}
+
+p:nth-child(1) {
+ background-image: url(/vercel.svg);
+}
+
+/* p:nth-child(2) {
+ background-image: url('./vercel.svg');
+}
+
+p:nth-child(3) {
+ background-image: url(./vercel.svg);
+} */
+
+p:nth-child(4) {
+ background-image: url('./public/vercel.svg');
+}
+
+p:nth-child(5) {
+ background-image: url(./public/vercel.svg);
+}
+
+div {
+ background-image: url('https://vercel.com/vercel.svg');
+}
+
+div:nth-child(1) {
+ background-image: url('https://vercel.com/vercel.svg');
+}
diff --git a/test/integration/css-fixtures/unresolved-css-url/global.scss b/test/integration/css-fixtures/unresolved-css-url/global.scss
new file mode 100644
index 0000000000000..6baec50d616e9
--- /dev/null
+++ b/test/integration/css-fixtures/unresolved-css-url/global.scss
@@ -0,0 +1,31 @@
+.p {
+ background-image: url('/vercel.svg');
+}
+
+.p:nth-child(1) {
+ background-image: url(/vercel.svg);
+}
+
+// .p:nth-child(2) {
+// background-image: url('./vercel.svg');
+// }
+
+// .p:nth-child(3) {
+// background-image: url(./vercel.svg);
+// }
+
+p:nth-child(4) {
+ background-image: url('./public/vercel.svg');
+}
+
+p:nth-child(5) {
+ background-image: url(./public/vercel.svg);
+}
+
+.div {
+ background-image: url('https://vercel.com/vercel.svg');
+}
+
+.div:nth-child(1) {
+ background-image: url('https://vercel.com/vercel.svg');
+}
diff --git a/test/integration/css-fixtures/unresolved-css-url/pages/_app.js b/test/integration/css-fixtures/unresolved-css-url/pages/_app.js
new file mode 100644
index 0000000000000..9570e55c1a7b7
--- /dev/null
+++ b/test/integration/css-fixtures/unresolved-css-url/pages/_app.js
@@ -0,0 +1,6 @@
+import '../global.css'
+import '../global.scss'
+
+export default function MyApp({ Component, pageProps }) {
+ return
Hello from index
+} diff --git a/test/integration/css-fixtures/unresolved-css-url/pages/another.module.scss b/test/integration/css-fixtures/unresolved-css-url/pages/another.module.scss new file mode 100644 index 0000000000000..50a3569e6698c --- /dev/null +++ b/test/integration/css-fixtures/unresolved-css-url/pages/another.module.scss @@ -0,0 +1,31 @@ +.first { + background-image: url('/vercel.svg'); +} + +.first:nth-child(1) { + background-image: url(/vercel.svg); +} + +// .first:nth-child(2) { +// background-image: url('./vercel.svg'); +// } + +// .first:nth-child(3) { +// background-image: url(./vercel.svg); +// } + +.first:nth-child(4) { + background-image: url('../public/vercel.svg'); +} + +.first:nth-child(5) { + background-image: url(../public/vercel.svg); +} + +.another { + background-image: url('https://vercel.com/vercel.svg'); +} + +.another:nth-child(1) { + background-image: url('https://vercel.com/vercel.svg'); +} diff --git a/test/integration/css-fixtures/unresolved-css-url/pages/index.js b/test/integration/css-fixtures/unresolved-css-url/pages/index.js new file mode 100644 index 0000000000000..b1f8e7e40c4c5 --- /dev/null +++ b/test/integration/css-fixtures/unresolved-css-url/pages/index.js @@ -0,0 +1,5 @@ +import styles from './index.module.css' + +export default function Page() { + returnHello from index
+} diff --git a/test/integration/css-fixtures/unresolved-css-url/pages/index.module.css b/test/integration/css-fixtures/unresolved-css-url/pages/index.module.css new file mode 100644 index 0000000000000..ca4d9b6e8ec8e --- /dev/null +++ b/test/integration/css-fixtures/unresolved-css-url/pages/index.module.css @@ -0,0 +1,31 @@ +.first { + background-image: url('/vercel.svg'); +} + +.first:nth-child(1) { + background-image: url(/vercel.svg); +} + +/* .first:nth-child(2) { + background-image: url('./vercel.svg'); +} + +.first:nth-child(3) { + background-image: url(./vercel.svg); +} */ + +.first:nth-child(4) { + background-image: url('../public/vercel.svg'); +} + +.first:nth-child(5) { + background-image: url(../public/vercel.svg); +} + +.another { + background-image: url('https://vercel.com/vercel.svg'); +} + +.another:nth-child(1) { + background-image: url('https://vercel.com/vercel.svg'); +} diff --git a/test/integration/css-fixtures/unresolved-css-url/public/vercel.svg b/test/integration/css-fixtures/unresolved-css-url/public/vercel.svg new file mode 100644 index 0000000000000..9d8ff846622a3 --- /dev/null +++ b/test/integration/css-fixtures/unresolved-css-url/public/vercel.svg @@ -0,0 +1 @@ + diff --git a/test/integration/css/test/index.test.js b/test/integration/css/test/index.test.js index 191b113856e3d..e0ee392930150 100644 --- a/test/integration/css/test/index.test.js +++ b/test/integration/css/test/index.test.js @@ -1532,4 +1532,37 @@ describe('CSS Support', () => { tests() }) }) + + describe('should handle unresolved files gracefully', () => { + const workDir = join(fixturesDir, 'unresolved-css-url') + + it('should build correctly', async () => { + await remove(join(workDir, '.next')) + const { code } = await nextBuild(workDir) + expect(code).toBe(0) + }) + + it('should have correct file references in CSS output', async () => { + const cssFiles = await readdir(join(workDir, '.next/static/css')) + + for (const file of cssFiles) { + if (file.endsWith('.css.map')) continue + + const content = await readFile( + join(workDir, '.next/static/css', file), + 'utf8' + ) + console.log(file, content) + + // if it is the combined global CSS file there are double the expected + // results + const howMany = content.includes('p{') ? 4 : 2 + + expect(content.match(/\(\/vercel\.svg/g).length).toBe(howMany) + // expect(content.match(/\(vercel\.svg/g).length).toBe(howMany) + expect(content.match(/\(\/_next\/static\/media/g).length).toBe(2) + expect(content.match(/\(https:\/\//g).length).toBe(howMany) + } + }) + }) })