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 +} diff --git a/test/integration/css-fixtures/unresolved-css-url/pages/another.js b/test/integration/css-fixtures/unresolved-css-url/pages/another.js new file mode 100644 index 0000000000000..f3e309a269f79 --- /dev/null +++ b/test/integration/css-fixtures/unresolved-css-url/pages/another.js @@ -0,0 +1,5 @@ +import styles from './another.module.scss' + +export default function Page() { + 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() { + return

Hello 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) + } + }) + }) })