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

Feature/less styles #23185

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions docs/basic-features/built-in-css-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,39 @@ module.exports = {
}
```

## Less and Stylus Support
## Less Support

To support importing `.less` or `.styl` files you can use the following plugins:
Next.js allows you to import Less using the `.less` extension. You can use component-level Less via CSS Modules and the
`.module.less` extension.

- [@zeit/next-less](https://github.com/vercel/next-plugins/tree/master/packages/next-less)
- [@zeit/next-stylus](https://github.com/vercel/next-plugins/tree/master/packages/next-stylus)

If using the less plugin, don't forget to add a dependency on less as well, otherwise you'll see an error like:
Before you can use Next.js' built-in Less support, be sure to install `less`:

```bash
Error: Cannot find module 'less'
npm install less
```

Less support has the same benefits and restrictions as the built-in CSS suport detailed above.

### Customizing Less Options

If you want to configure the Less compiler you can do so by using the `lessOptions` in `next.config.js`.

For example to add `javascriptEnabled`:

```js
module.exports = {
lessOptions: {
javascriptEnabled: true,
},
}
```

## Stylus Support

To support importing `.styl` files you can use the following plugin:

- [@zeit/next-stylus](https://github.com/vercel/next-plugins/tree/master/packages/next-stylus)

## CSS-in-JS

<details>
Expand Down
19 changes: 19 additions & 0 deletions errors/install-less.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Install `less` to Use Built-In Less Support

#### Why This Error Occurred

Using Next.js' [built-in Less support](https://nextjs.org/docs/basic-features/built-in-css-support##less-support) requires that you bring your own version of Less.

#### Possible Ways to Fix It

Please install the `less` package in your project.

```bash
npm i less
# or
yarn add less
```

### Useful Links

- [Less Support in Documentation](https://nextjs.org/docs/basic-features/built-in-css-support#less-support)
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
"ky": "0.19.1",
"ky-universal": "0.6.0",
"lerna": "4.0.0",
"less": "4.1.1",
"less-loader": "7.3.0",
"lint-staged": "10.1.7",
"lost": "8.3.1",
"minimatch": "3.0.4",
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,7 @@ export default async function getBaseWebpackConfig(
isServer,
assetPrefix: config.assetPrefix || '',
sassOptions: config.sassOptions,
lessOptions: config.lessOptions,
productionBrowserSourceMaps: config.productionBrowserSourceMaps,
future: config.future,
})
Expand Down
63 changes: 58 additions & 5 deletions packages/next/build/webpack/config/blocks/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { getPostCssPlugins } from './plugins'

// RegExps for all Style Sheet variants
const regexLikeCss = /\.(css|scss|sass)$/
const regexLikeCss = /\.(css|scss|sass|less)$/

// RegExps for Style Sheets
const regexCssGlobal = /(?<!\.module)\.css$/
Expand All @@ -24,6 +24,9 @@ const regexCssModules = /\.module\.css$/
const regexSassGlobal = /(?<!\.module)\.(scss|sass)$/
const regexSassModules = /\.module\.(scss|sass)$/

const regexLessGlobal = /(?<!\.module)\.less$/
const regexLessModules = /\.module\.less$/

export const css = curry(async function css(
ctx: ConfigurationContext,
config: webpack.Configuration
Expand All @@ -33,6 +36,7 @@ export const css = curry(async function css(
additionalData: sassAdditionalData,
...sassOptions
} = ctx.sassOptions
const { ...lessOptions } = ctx.lessOptions

const sassPreprocessors: webpack.RuleSetUseItem[] = [
// First, process files with `sass-loader`: this inlines content, and
Expand Down Expand Up @@ -62,6 +66,16 @@ export const css = curry(async function css(
},
]

const lessPreprocessors: webpack.RuleSetUseItem[] = [
{
loader: 'less-loader',
options: {
sourceMap: true,

Choose a reason for hiding this comment

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

source-map should be depended on user parameters.

lessOptions,
},
},
]

const fns: ConfigurationFn[] = [
loader({
oneOf: [
Expand Down Expand Up @@ -149,13 +163,36 @@ export const css = curry(async function css(
],
})
)
fns.push(
loader({
oneOf: [
// Opt-in support for Less.
{
// Less Modules should never have side effects. This setting will
// allow unused Less to be removed from the production build.
// We ensure this by disallowing `:global()` Less at the top-level
// via the `pure` mode in `css-loader`.
sideEffects: false,
// Less Modules are activated via this specific extension.
test: regexLessModules,
// Less Modules are only supported in the user's application. We're
// not yet allowing Less imports _within_ `node_modules`.
issuer: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
use: getCssModuleLoader(ctx, postCssPlugins, lessPreprocessors),
},
],
})
)

// Throw an error for CSS Modules used outside their supported scope
fns.push(
loader({
oneOf: [
{
test: [regexCssModules, regexSassModules],
test: [regexCssModules, regexSassModules, regexLessModules],
use: {
loader: 'error-loader',
options: {
Expand All @@ -172,7 +209,7 @@ export const css = curry(async function css(
loader({
oneOf: [
{
test: [regexCssGlobal, regexSassGlobal],
test: [regexCssGlobal, regexSassGlobal, regexLessGlobal],
use: require.resolve('next/dist/compiled/ignore-loader'),
},
],
Expand Down Expand Up @@ -241,6 +278,22 @@ export const css = curry(async function css(
],
})
)
fns.push(
loader({
oneOf: [
{
// A global Less import always has side effects. Webpack will tree
// shake the Less without this option if the issuer claims to have
// no side-effects.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
test: regexLessGlobal,
issuer: { and: [ctx.customAppFile] },
use: getGlobalCssLoader(ctx, postCssPlugins, lessPreprocessors),
},
],
})
)
}
}

Expand All @@ -249,7 +302,7 @@ export const css = curry(async function css(
loader({
oneOf: [
{
test: [regexCssGlobal, regexSassGlobal],
test: [regexCssGlobal, regexSassGlobal, regexLessGlobal],
issuer: { and: [/node_modules/] },
use: {
loader: 'error-loader',
Expand All @@ -267,7 +320,7 @@ export const css = curry(async function css(
loader({
oneOf: [
{
test: [regexCssGlobal, regexSassGlobal],
test: [regexCssGlobal, regexSassGlobal, regexLessGlobal],
use: {
loader: 'error-loader',
options: {
Expand Down
3 changes: 3 additions & 0 deletions packages/next/build/webpack/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export async function build(
isServer,
assetPrefix,
sassOptions,
lessOptions,
productionBrowserSourceMaps,
future,
}: {
Expand All @@ -22,6 +23,7 @@ export async function build(
isServer: boolean
assetPrefix: string
sassOptions: any
lessOptions: any
productionBrowserSourceMaps: boolean
future: NextConfig['future']
}
Expand All @@ -39,6 +41,7 @@ export async function build(
: assetPrefix
: '',
sassOptions,
lessOptions,
productionBrowserSourceMaps,
future,
}
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/webpack/config/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type ConfigurationContext = {
assetPrefix: string

sassOptions: any
lessOptions: any
productionBrowserSourceMaps: boolean

future: NextConfig['future']
Expand Down
11 changes: 11 additions & 0 deletions packages/next/client/dev/error-overlay/format-webpack-messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ function formatMessage(message) {
lines[1] += '\nLearn more: https://nextjs.org/docs/messages/install-sass'
}

// Add helpful message for users trying to use Less for the first time
if (lines[1] && lines[1].match(/Cannot find module.+less/)) {
// ./file.module.less (<<loader info>>) => ./file.module.less
lines[0] = lines[0].replace(/(.+) \(.+?(?=\?\?).+?\)/, '$1')

lines[1] =
"To use Next.js' built-in Less support, you first need to install `less`.\n"
lines[1] += 'Run `npm i less` or `yarn add less` inside your workspace.\n'
lines[1] += '\nLearn more: https://err.sh/next.js/install-less'
}

message = lines.join('\n')
// Internal stacks are generally useless so we strip them... with the
// exception of stacks containing `webpack:` because they're normally
Expand Down
2 changes: 1 addition & 1 deletion packages/next/compiled/conf/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/next/compiled/find-cache-dir/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/next/next-server/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const defaultConfig: NextConfig = {
},
basePath: '',
sassOptions: {},
lessOptions: {},
trailingSlash: false,
i18n: null,
productionBrowserSourceMaps: false,
Expand Down
5 changes: 5 additions & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,16 @@
},
"peerDependencies": {
"fibers": ">= 3.1.0",
"less": "4.1.1",
"node-sass": "^4.0.0 || ^5.0.0",
"react": "^16.6.0 || ^17",
"react-dom": "^16.6.0 || ^17",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"node-sass": {
"optional": true
},
Expand Down Expand Up @@ -217,6 +221,7 @@
"is-wsl": "2.2.0",
"json5": "2.1.3",
"jsonwebtoken": "8.5.1",
"less-loader": "7.3.0",
"loader-utils": "2.0.0",
"lodash.curry": "4.1.1",
"lru-cache": "5.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
lessOptions: {
modifyVars: {
var: 'red',
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { redText } from './index.module.less'

export default function Home() {
return (
<div id="verify-red" className={redText}>
This text should be red.
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.redText {
color: @var;
}
9 changes: 9 additions & 0 deletions test/integration/less-fixtures/basic-module/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { redText } from './index.module.less'

export default function Home() {
return (
<div id="verify-red" className={redText}>
This text should be red.
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@var: red;
.redText {
color: @var;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import App from 'next/app'
import '../styles/global.less'

class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}

export default MyApp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Home() {
return <div className="red-text">This text should be red.</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@var: red;
.redText {
::placeholder {
color: @var;
}
}

.flex-parsing {
flex: 0 0 calc(50% - var(--vertical-gutter));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'
import App from 'next/app'

class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}

export default MyApp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import '../styles/global.less'

export default function Home() {
return <div className="red-text">This text should be red.</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.red-text {
color: red;
}
5 changes: 5 additions & 0 deletions test/integration/less-fixtures/invalid-global/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import '../styles/global.less'

export default function Home() {
return <div className="red-text">This text should be red.</div>
}
Loading