Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CSS modules imports from outside of the root directory #42106

Merged
merged 15 commits into from Oct 31, 2022
Merged
1 change: 1 addition & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -2094,6 +2094,7 @@ export default async function getBaseWebpackConfig(
dev,
})
: new FlightClientEntryPlugin({
appDir,
dev,
isEdgeServer,
})),
Expand Down
47 changes: 31 additions & 16 deletions packages/next/build/webpack/config/blocks/css/index.ts
Expand Up @@ -14,6 +14,7 @@ import {
getFontLoaderImportError,
} from './messages'
import { getPostCssPlugins } from './plugins'
import { WEBPACK_LAYERS } from '../../../../../lib/constants'

// RegExps for all Style Sheet variants
export const regexLikeCss = /\.(css|scss|sass)$/
Expand Down Expand Up @@ -284,16 +285,10 @@ export const css = curry(async function css(
sideEffects: false,
// CSS Modules are activated via this specific extension.
test: regexCssModules,
// CSS Modules are only supported in the user's application. We're
// not yet allowing CSS imports _within_ `node_modules`.
issuer: {
Copy link

@terrymun terrymun Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removal of this line is causing next-transpile-module as well as Nx plugins to fail with Next@13.0.1. Is this something that we can restore?

and: [
{
or: [ctx.rootDirectory, regexClientEntry],
},
],
not: [/node_modules/],
},
// Match CSS modules that are used by the app dir.
issuerLayer: (layer: string) =>
layer === WEBPACK_LAYERS.server ||
layer === WEBPACK_LAYERS.client,
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getCssModuleLoader(ctx, lazyPostCSSInitializer),
Expand All @@ -314,12 +309,10 @@ export const css = curry(async function css(
sideEffects: false,
// Sass Modules are activated via this specific extension.
test: regexSassModules,
// Sass Modules are only supported in the user's application. We're
// not yet allowing Sass imports _within_ `node_modules`.
issuer: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
// Match CSS modules that are used by the app dir.
issuerLayer: (layer: string) =>
layer === WEBPACK_LAYERS.server ||
layer === WEBPACK_LAYERS.client,
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getCssModuleLoader(
Expand Down Expand Up @@ -356,6 +349,15 @@ export const css = curry(async function css(
},
use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
markRemovable({
// For app dir, we can match the issuer layer instead of the issuer.
issuerLayer: (layer: string) =>
layer === WEBPACK_LAYERS.server ||
layer === WEBPACK_LAYERS.client,
sideEffects: false,
test: regexCssModules,
use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)
Expand Down Expand Up @@ -383,6 +385,19 @@ export const css = curry(async function css(
sassPreprocessors
),
}),
markRemovable({
// For app dir, we can match the issuer layer instead of the issuer.
issuerLayer: (layer: string) =>
layer === WEBPACK_LAYERS.server ||
layer === WEBPACK_LAYERS.client,
sideEffects: false,
test: regexSassModules,
use: getCssModuleLoader(
ctx,
lazyPostCSSInitializer,
sassPreprocessors
),
}),
],
})
)
Expand Down
Expand Up @@ -25,6 +25,7 @@ import { traverseModules } from '../utils'

interface Options {
dev: boolean
appDir: string
isEdgeServer: boolean
}

Expand All @@ -40,10 +41,12 @@ const flightCSSManifest: FlightCSSManifest = {}

export class FlightClientEntryPlugin {
dev: boolean
appDir: string
isEdgeServer: boolean

constructor(options: Options) {
this.dev = options.dev
this.appDir = options.appDir
this.isEdgeServer = options.isEdgeServer
}

Expand Down Expand Up @@ -246,7 +249,7 @@ export class FlightClientEntryPlugin {
if (!chunk.name) return
if (!chunk.name.endsWith('/page')) return

const entryName = path.join(compiler.context, chunk.name)
const entryName = path.join(this.appDir, '..', chunk.name)

if (!cssImportsForChunk[entryName]) {
cssImportsForChunk[entryName] = []
Expand Down
11 changes: 10 additions & 1 deletion test/e2e/app-dir/app-alias.test.ts
@@ -1,6 +1,7 @@
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { renderViaHTTP } from 'next-test-utils'
import webdriver from 'next-webdriver'
import path from 'path'

describe('app-dir alias handling', () => {
Expand All @@ -27,6 +28,14 @@ describe('app-dir alias handling', () => {

it('should handle typescript paths alias correctly', async () => {
const html = await renderViaHTTP(next.url, '/button')
expect(html).toContain('<button>click</button>')
expect(html).toContain('click</button>')
})

it('should resolve css imports from outside with src folder presented', async () => {
const browser = await webdriver(next.url, '/button')
const fontSize = await browser
.elementByCss('button')
.getComputedCss('font-size')
expect(fontSize).toBe('50px')
})
})
1 change: 1 addition & 0 deletions test/e2e/app-dir/app-alias/next.config.js
@@ -1,5 +1,6 @@
module.exports = {
experimental: {
appDir: true,
transpileModules: ['ui'],
},
}
4 changes: 3 additions & 1 deletion test/e2e/app-dir/app-alias/ui/button.tsx
@@ -1,3 +1,5 @@
import styles from './style.module.css'

export default function Button(props: any) {
return <button {...props} />
return <button {...props} className={styles.button} />
}
3 changes: 3 additions & 0 deletions test/e2e/app-dir/app-alias/ui/style.module.css
@@ -0,0 +1,3 @@
.button {
font-size: 50px;
}
10 changes: 10 additions & 0 deletions test/e2e/app-dir/rsc-external.test.ts
Expand Up @@ -135,4 +135,14 @@ describe('app dir - rsc external dependency', () => {
expect(result).toMatch(/\.css/)
})
})

it('should handle external css modules', async () => {
const browser = await webdriver(next.url, '/css/modules')

expect(
await browser.eval(
`window.getComputedStyle(document.querySelector('h1')).color`
)
).toBe('rgb(255, 0, 0)')
})
})
2 changes: 1 addition & 1 deletion test/e2e/app-dir/rsc-external/app/css/[...slug]/page.js
@@ -1,4 +1,4 @@
import 'global-css/style.css'
import 'css/style.css'

export default function Page() {
return <div>hello</div>
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/app-dir/rsc-external/app/css/modules/page.js
@@ -0,0 +1,5 @@
import Foo from 'css/module'

export default function Page() {
return <Foo />
}
@@ -0,0 +1,5 @@
import styles from './styles.module.css'

export default function Foo() {
return <h1 className={styles.h1}>Hello</h1>
}
@@ -1,10 +1,11 @@
{
"name": "global-css",
"name": "css",
"type": "module",
"sideEffects": false,
"exports": {
".": "./index.js",
"./style.css": "./style.css",
"./module": "./module.js",
"./package.json": "./package.json"
}
}
@@ -0,0 +1,3 @@
.h1 {
color: red;
}