diff --git a/docs/basic-features/built-in-css-support.md b/docs/basic-features/built-in-css-support.md index 1bc6a1274659c..62425b01e4919 100644 --- a/docs/basic-features/built-in-css-support.md +++ b/docs/basic-features/built-in-css-support.md @@ -157,10 +157,22 @@ export default HelloWorld Please see the [styled-jsx documentation](https://github.com/zeit/styled-jsx) for more examples. -## Sass, Less, and Stylus Support +## Sass Support -To support importing `.scss`, `.less` or `.styl` files you can use the following plugins: +Next.js allows you to import Sass using both the `.scss` and `.sass` extensions. +You can use component-level Sass via CSS Modules and the `.module.scss` or `.module.sass` extension. + +Before you can use Next.js' built-in Sass support, be sure to install [`sass`](https://github.com/sass/sass): + +```bash +npm install sass +``` + +Sass support has the same benefits and restrictions as the built-in CSS support detailed above. + +## Less and Stylus Support + +To support importing `.less` or `.styl` files you can use the following plugins: -- [@zeit/next-sass](https://github.com/zeit/next-plugins/tree/master/packages/next-sass) - [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less) - [@zeit/next-stylus](https://github.com/zeit/next-plugins/tree/master/packages/next-stylus) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 777213d13dc68..ab38642e45bf4 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -834,6 +834,7 @@ export default async function getBaseWebpackConfig( isDevelopment: dev, isServer, hasSupportCss: !!config.experimental.css, + hasSupportScss: !!config.experimental.scss, assetPrefix: config.assetPrefix || '', }) diff --git a/packages/next/build/webpack/config/blocks/css/index.ts b/packages/next/build/webpack/config/blocks/css/index.ts index 3a7625eb2a4b4..0ba6ec7051db3 100644 --- a/packages/next/build/webpack/config/blocks/css/index.ts +++ b/packages/next/build/webpack/config/blocks/css/index.ts @@ -1,6 +1,6 @@ import curry from 'lodash.curry' import path from 'path' -import { Configuration } from 'webpack' +import webpack, { Configuration } from 'webpack' import MiniCssExtractPlugin from '../../../plugins/mini-css-extract-plugin' import { loader, plugin } from '../../helpers' import { ConfigurationContext, ConfigurationFn, pipe } from '../../utils' @@ -13,13 +13,20 @@ import { } from './messages' import { getPostCssPlugins } from './plugins' -// RegExps for Stylesheets -const regexCssAll = /\.css$/ +// RegExps for all Style Sheet variants +const regexLikeCss = /\.(css|scss|sass)$/ + +// RegExps for Style Sheets const regexCssGlobal = /(? { @@ -35,6 +37,6 @@ export async function build( : '', } - const fn = pipe(base(ctx), css(hasSupportCss, ctx)) + const fn = pipe(base(ctx), css(hasSupportCss, hasSupportScss, ctx)) return fn(config) } diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 57a50d0df8b0b..564742f40bf6c 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -42,6 +42,7 @@ const defaultConfig: { [key: string]: any } = { (os.cpus() || { length: 1 }).length) - 1 ), css: true, + scss: false, documentMiddleware: false, granularChunks: true, modern: false, diff --git a/packages/next/package.json b/packages/next/package.json index 6203148e6577b..0a8d9f2f432b6 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -122,6 +122,8 @@ "raw-body": "2.4.0", "react-error-overlay": "5.1.6", "react-is": "16.8.6", + "resolve-url-loader": "3.1.1", + "sass-loader": "8.0.2", "send": "0.17.1", "source-map": "0.6.1", "string-hash": "1.1.3", diff --git a/test/integration/css-fixtures/nm-module/node_modules/example/index.module.css b/test/integration/css-fixtures/nm-module/node_modules/example/index.module.css index f77fe0ef0bdbe..08a38e09ef8ea 100644 --- a/test/integration/css-fixtures/nm-module/node_modules/example/index.module.css +++ b/test/integration/css-fixtures/nm-module/node_modules/example/index.module.css @@ -1,3 +1,3 @@ .redText { - color: 'red'; + color: red; } diff --git a/test/integration/css-modules/test/index.test.js b/test/integration/css-modules/test/index.test.js index 3c0d865fd44dc..e990a2e04624c 100644 --- a/test/integration/css-modules/test/index.test.js +++ b/test/integration/css-modules/test/index.test.js @@ -286,7 +286,7 @@ describe('Valid CSS Module Usage from within node_modules', () => { const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".example_redText__1rb5g{color:\\"red\\"}"` + `".example_redText__1rb5g{color:red}"` ) }) }) diff --git a/test/integration/scss-fixtures/3rd-party-module/pages/index.js b/test/integration/scss-fixtures/3rd-party-module/pages/index.js new file mode 100644 index 0000000000000..57f5707a4c221 --- /dev/null +++ b/test/integration/scss-fixtures/3rd-party-module/pages/index.js @@ -0,0 +1,5 @@ +import { foo } from './index.module.scss' + +export default function Home() { + return
+} diff --git a/test/integration/scss-fixtures/3rd-party-module/pages/index.module.scss b/test/integration/scss-fixtures/3rd-party-module/pages/index.module.scss new file mode 100644 index 0000000000000..99d63206009a1 --- /dev/null +++ b/test/integration/scss-fixtures/3rd-party-module/pages/index.module.scss @@ -0,0 +1,17 @@ +.foo { + position: relative; +} + +.foo :global(.bar), +.foo :global(.baz) { + height: 100%; + overflow: hidden; +} + +.foo :global(.lol) { + width: 80%; +} + +.foo > :global(.lel) { + width: 80%; +} diff --git a/test/integration/scss-fixtures/basic-module/pages/index.js b/test/integration/scss-fixtures/basic-module/pages/index.js new file mode 100644 index 0000000000000..7b42859a67e32 --- /dev/null +++ b/test/integration/scss-fixtures/basic-module/pages/index.js @@ -0,0 +1,9 @@ +import { redText } from './index.module.scss' + +export default function Home() { + return ( +
+ This text should be red. +
+ ) +} diff --git a/test/integration/scss-fixtures/basic-module/pages/index.module.scss b/test/integration/scss-fixtures/basic-module/pages/index.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/basic-module/pages/index.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/compilation-and-prefixing/pages/_app.js b/test/integration/scss-fixtures/compilation-and-prefixing/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/compilation-and-prefixing/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/compilation-and-prefixing/pages/index.js b/test/integration/scss-fixtures/compilation-and-prefixing/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/compilation-and-prefixing/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/compilation-and-prefixing/styles/global.scss b/test/integration/scss-fixtures/compilation-and-prefixing/styles/global.scss new file mode 100644 index 0000000000000..2cc5c559f728a --- /dev/null +++ b/test/integration/scss-fixtures/compilation-and-prefixing/styles/global.scss @@ -0,0 +1,6 @@ +$var: red; +.redText { + ::placeholder { + color: $var; + } +} diff --git a/test/integration/scss-fixtures/composes-basic/pages/index.js b/test/integration/scss-fixtures/composes-basic/pages/index.js new file mode 100644 index 0000000000000..0af8621c4649a --- /dev/null +++ b/test/integration/scss-fixtures/composes-basic/pages/index.js @@ -0,0 +1,9 @@ +import { subClass } from './index.module.scss' + +export default function Home() { + return ( +
+ This text should be yellow on blue. +
+ ) +} diff --git a/test/integration/scss-fixtures/composes-basic/pages/index.module.scss b/test/integration/scss-fixtures/composes-basic/pages/index.module.scss new file mode 100644 index 0000000000000..e3c6430bd6f70 --- /dev/null +++ b/test/integration/scss-fixtures/composes-basic/pages/index.module.scss @@ -0,0 +1,10 @@ +$var: red; +.className { + background: $var; + color: yellow; +} + +.subClass { + composes: className; + background: blue; +} diff --git a/test/integration/scss-fixtures/composes-external/pages/index.js b/test/integration/scss-fixtures/composes-external/pages/index.js new file mode 100644 index 0000000000000..0af8621c4649a --- /dev/null +++ b/test/integration/scss-fixtures/composes-external/pages/index.js @@ -0,0 +1,9 @@ +import { subClass } from './index.module.scss' + +export default function Home() { + return ( +
+ This text should be yellow on blue. +
+ ) +} diff --git a/test/integration/scss-fixtures/composes-external/pages/index.module.scss b/test/integration/scss-fixtures/composes-external/pages/index.module.scss new file mode 100644 index 0000000000000..196db5d888917 --- /dev/null +++ b/test/integration/scss-fixtures/composes-external/pages/index.module.scss @@ -0,0 +1,5 @@ +$var: blue; +.subClass { + composes: className from './other.scss'; + background: $var; +} diff --git a/test/integration/scss-fixtures/composes-external/pages/other.scss b/test/integration/scss-fixtures/composes-external/pages/other.scss new file mode 100644 index 0000000000000..382dd3097baa0 --- /dev/null +++ b/test/integration/scss-fixtures/composes-external/pages/other.scss @@ -0,0 +1,5 @@ +$var: red; +.className { + background: $var; + color: yellow; +} diff --git a/test/integration/scss-fixtures/dev-module/pages/index.js b/test/integration/scss-fixtures/dev-module/pages/index.js new file mode 100644 index 0000000000000..7b42859a67e32 --- /dev/null +++ b/test/integration/scss-fixtures/dev-module/pages/index.js @@ -0,0 +1,9 @@ +import { redText } from './index.module.scss' + +export default function Home() { + return ( +
+ This text should be red. +
+ ) +} diff --git a/test/integration/scss-fixtures/dev-module/pages/index.module.scss b/test/integration/scss-fixtures/dev-module/pages/index.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/dev-module/pages/index.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/hmr-module/pages/index.js b/test/integration/scss-fixtures/hmr-module/pages/index.js new file mode 100644 index 0000000000000..18a31282f756c --- /dev/null +++ b/test/integration/scss-fixtures/hmr-module/pages/index.js @@ -0,0 +1,15 @@ +import { redText } from './index.module.scss' + +function Home() { + return ( + <> +
+ This text should be red. +
+
+ + + ) +} + +export default Home diff --git a/test/integration/scss-fixtures/hmr-module/pages/index.module.scss b/test/integration/scss-fixtures/hmr-module/pages/index.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/hmr-module/pages/index.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.js b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.js new file mode 100644 index 0000000000000..4a5b22813be5a --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.js @@ -0,0 +1 @@ +require('./index.scss') diff --git a/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.mjs b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.mjs new file mode 100644 index 0000000000000..63810a6819f33 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.mjs @@ -0,0 +1 @@ +import './index.scss' diff --git a/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.scss b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.scss new file mode 100644 index 0000000000000..f77fe0ef0bdbe --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/index.scss @@ -0,0 +1,3 @@ +.redText { + color: 'red'; +} diff --git a/test/integration/scss-fixtures/invalid-global-module/node_modules/example/package.json b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/package.json new file mode 100644 index 0000000000000..ab5f2cd4a1cf7 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-module/node_modules/example/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "main": "index" +} diff --git a/test/integration/scss-fixtures/invalid-global-module/pages/index.js b/test/integration/scss-fixtures/invalid-global-module/pages/index.js new file mode 100644 index 0000000000000..9a79166027dd7 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-module/pages/index.js @@ -0,0 +1,7 @@ +import 'example' + +function Home() { + return
This should fail at build time.
+} + +export default Home diff --git a/test/integration/scss-fixtures/invalid-global-with-app/pages/_app.js b/test/integration/scss-fixtures/invalid-global-with-app/pages/_app.js new file mode 100644 index 0000000000000..68b8ee24d7fd5 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-with-app/pages/_app.js @@ -0,0 +1,11 @@ +import React from 'react' +import App from 'next/app' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/invalid-global-with-app/pages/index.js b/test/integration/scss-fixtures/invalid-global-with-app/pages/index.js new file mode 100644 index 0000000000000..38d46eb2a45eb --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-with-app/pages/index.js @@ -0,0 +1,5 @@ +import '../styles/global.scss' + +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/invalid-global-with-app/styles/global.scss b/test/integration/scss-fixtures/invalid-global-with-app/styles/global.scss new file mode 100644 index 0000000000000..8e1524022baf8 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global-with-app/styles/global.scss @@ -0,0 +1,3 @@ +.red-text { + color: red; +} diff --git a/test/integration/scss-fixtures/invalid-global/pages/index.js b/test/integration/scss-fixtures/invalid-global/pages/index.js new file mode 100644 index 0000000000000..38d46eb2a45eb --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global/pages/index.js @@ -0,0 +1,5 @@ +import '../styles/global.scss' + +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/invalid-global/styles/global.scss b/test/integration/scss-fixtures/invalid-global/styles/global.scss new file mode 100644 index 0000000000000..8e1524022baf8 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-global/styles/global.scss @@ -0,0 +1,3 @@ +.red-text { + color: red; +} diff --git a/test/integration/scss-fixtures/invalid-module-document/pages/_document.js b/test/integration/scss-fixtures/invalid-module-document/pages/_document.js new file mode 100644 index 0000000000000..a14aca67a844d --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module-document/pages/_document.js @@ -0,0 +1,23 @@ +import Document, { Head, Html, Main, NextScript } from 'next/document' +import styles from '../styles.module.scss' + +class MyDocument extends Document { + static async getInitialProps(ctx) { + const initialProps = await Document.getInitialProps(ctx) + return { ...initialProps } + } + + render() { + return ( + + + +
+ + + + ) + } +} + +export default MyDocument diff --git a/test/integration/scss-fixtures/invalid-module-document/pages/index.js b/test/integration/scss-fixtures/invalid-module-document/pages/index.js new file mode 100644 index 0000000000000..08fe48e9b56c8 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module-document/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
Hello
+} diff --git a/test/integration/scss-fixtures/invalid-module-document/styles.module.scss b/test/integration/scss-fixtures/invalid-module-document/styles.module.scss new file mode 100644 index 0000000000000..8e1524022baf8 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module-document/styles.module.scss @@ -0,0 +1,3 @@ +.red-text { + color: red; +} diff --git a/test/integration/scss-fixtures/invalid-module/node_modules/example/index.js b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.js new file mode 100644 index 0000000000000..a8023d4c7c1bf --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.js @@ -0,0 +1 @@ +module.exports = require('./index.module.scss') diff --git a/test/integration/scss-fixtures/invalid-module/node_modules/example/index.mjs b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.mjs new file mode 100644 index 0000000000000..f72c6d4acfbe2 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.mjs @@ -0,0 +1 @@ +export * from './index.module.scss' diff --git a/test/integration/scss-fixtures/invalid-module/node_modules/example/index.module.scss b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.module.scss new file mode 100644 index 0000000000000..f77fe0ef0bdbe --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module/node_modules/example/index.module.scss @@ -0,0 +1,3 @@ +.redText { + color: 'red'; +} diff --git a/test/integration/scss-fixtures/invalid-module/node_modules/example/package.json b/test/integration/scss-fixtures/invalid-module/node_modules/example/package.json new file mode 100644 index 0000000000000..ab5f2cd4a1cf7 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module/node_modules/example/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "main": "index" +} diff --git a/test/integration/scss-fixtures/invalid-module/pages/index.js b/test/integration/scss-fixtures/invalid-module/pages/index.js new file mode 100644 index 0000000000000..83fa4e1381b64 --- /dev/null +++ b/test/integration/scss-fixtures/invalid-module/pages/index.js @@ -0,0 +1,7 @@ +import * as classes from 'example' + +function Home() { + return
This should fail at build time {JSON.stringify(classes)}.
+} + +export default Home diff --git a/test/integration/scss-fixtures/multi-global-reversed/pages/_app.js b/test/integration/scss-fixtures/multi-global-reversed/pages/_app.js new file mode 100644 index 0000000000000..dc344a9bd9d27 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global-reversed/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global2.scss' +import '../styles/global1.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/multi-global-reversed/pages/index.js b/test/integration/scss-fixtures/multi-global-reversed/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global-reversed/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/multi-global-reversed/styles/global1.scss b/test/integration/scss-fixtures/multi-global-reversed/styles/global1.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/multi-global-reversed/styles/global1.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-global-reversed/styles/global2.scss b/test/integration/scss-fixtures/multi-global-reversed/styles/global2.scss new file mode 100644 index 0000000000000..3dd5eeaf6db92 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global-reversed/styles/global2.scss @@ -0,0 +1,4 @@ +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-global/pages/_app.js b/test/integration/scss-fixtures/multi-global/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/multi-global/pages/index.js b/test/integration/scss-fixtures/multi-global/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/multi-global/styles/global1.scss b/test/integration/scss-fixtures/multi-global/styles/global1.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/multi-global/styles/global1.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-global/styles/global2.scss b/test/integration/scss-fixtures/multi-global/styles/global2.scss new file mode 100644 index 0000000000000..3dd5eeaf6db92 --- /dev/null +++ b/test/integration/scss-fixtures/multi-global/styles/global2.scss @@ -0,0 +1,4 @@ +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-module/pages/blue.js b/test/integration/scss-fixtures/multi-module/pages/blue.js new file mode 100644 index 0000000000000..1c45c34e41a84 --- /dev/null +++ b/test/integration/scss-fixtures/multi-module/pages/blue.js @@ -0,0 +1,20 @@ +import Link from 'next/link' +import { blueText } from './blue.module.scss' + +export default function Blue() { + return ( + <> +
+ This text should be blue. +
+
+ + Red + +
+ + None + + + ) +} diff --git a/test/integration/scss-fixtures/multi-module/pages/blue.module.scss b/test/integration/scss-fixtures/multi-module/pages/blue.module.scss new file mode 100644 index 0000000000000..a227734ba1db1 --- /dev/null +++ b/test/integration/scss-fixtures/multi-module/pages/blue.module.scss @@ -0,0 +1,4 @@ +$var: blue; +.blueText { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-module/pages/none.js b/test/integration/scss-fixtures/multi-module/pages/none.js new file mode 100644 index 0000000000000..f26c2d80b3ae0 --- /dev/null +++ b/test/integration/scss-fixtures/multi-module/pages/none.js @@ -0,0 +1,19 @@ +import Link from 'next/link' + +export default function None() { + return ( + <> +
+ This text should be black. +
+
+ + Red + +
+ + Blue + + + ) +} diff --git a/test/integration/scss-fixtures/multi-module/pages/red.js b/test/integration/scss-fixtures/multi-module/pages/red.js new file mode 100644 index 0000000000000..af8b699159986 --- /dev/null +++ b/test/integration/scss-fixtures/multi-module/pages/red.js @@ -0,0 +1,20 @@ +import Link from 'next/link' +import { redText } from './red.module.scss' + +export default function Red() { + return ( + <> +
+ This text should be red. +
+
+ + Blue + +
+ + None + + + ) +} diff --git a/test/integration/scss-fixtures/multi-module/pages/red.module.scss b/test/integration/scss-fixtures/multi-module/pages/red.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/multi-module/pages/red.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-page/pages/_app.js b/test/integration/scss-fixtures/multi-page/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/multi-page/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/multi-page/pages/page1.js b/test/integration/scss-fixtures/multi-page/pages/page1.js new file mode 100644 index 0000000000000..94b9d0f24ca37 --- /dev/null +++ b/test/integration/scss-fixtures/multi-page/pages/page1.js @@ -0,0 +1,14 @@ +import Link from 'next/link' +export default function Page1() { + return ( + <> +
This text should be red.
+
+ +
+ + Switch page + + + ) +} diff --git a/test/integration/scss-fixtures/multi-page/pages/page2.js b/test/integration/scss-fixtures/multi-page/pages/page2.js new file mode 100644 index 0000000000000..e8e49a87514dc --- /dev/null +++ b/test/integration/scss-fixtures/multi-page/pages/page2.js @@ -0,0 +1,14 @@ +import Link from 'next/link' +export default function Page2() { + return ( + <> +
This text should be blue.
+
+ +
+ + Switch page + + + ) +} diff --git a/test/integration/scss-fixtures/multi-page/styles/global1.scss b/test/integration/scss-fixtures/multi-page/styles/global1.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/multi-page/styles/global1.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/multi-page/styles/global2.scss b/test/integration/scss-fixtures/multi-page/styles/global2.scss new file mode 100644 index 0000000000000..3dd5eeaf6db92 --- /dev/null +++ b/test/integration/scss-fixtures/multi-page/styles/global2.scss @@ -0,0 +1,4 @@ +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/nested-global/pages/_app.js b/test/integration/scss-fixtures/nested-global/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/nested-global/pages/index.js b/test/integration/scss-fixtures/nested-global/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/nested-global/styles/global1.scss b/test/integration/scss-fixtures/nested-global/styles/global1.scss new file mode 100644 index 0000000000000..096036b10f2dd --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/styles/global1.scss @@ -0,0 +1,6 @@ +@import './global1b.scss'; + +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/nested-global/styles/global1b.scss b/test/integration/scss-fixtures/nested-global/styles/global1b.scss new file mode 100644 index 0000000000000..e288c3ca0d12e --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/styles/global1b.scss @@ -0,0 +1,5 @@ +$var: purple; +.red-text { + color: $var; + font-weight: bolder; +} diff --git a/test/integration/scss-fixtures/nested-global/styles/global2.scss b/test/integration/scss-fixtures/nested-global/styles/global2.scss new file mode 100644 index 0000000000000..4b659d369c561 --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/styles/global2.scss @@ -0,0 +1,6 @@ +@import './global2b.scss'; + +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/nested-global/styles/global2b.scss b/test/integration/scss-fixtures/nested-global/styles/global2b.scss new file mode 100644 index 0000000000000..cc1554c3c8334 --- /dev/null +++ b/test/integration/scss-fixtures/nested-global/styles/global2b.scss @@ -0,0 +1,5 @@ +$var: orange; +.blue-text { + color: $var; + font-weight: bolder; +} diff --git a/test/integration/scss-fixtures/next.config.js b/test/integration/scss-fixtures/next.config.js new file mode 100644 index 0000000000000..b97156a83b09e --- /dev/null +++ b/test/integration/scss-fixtures/next.config.js @@ -0,0 +1,11 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, + }, + experimental: { scss: true }, + webpack(cfg) { + cfg.devtool = 'source-map' + return cfg + }, +} diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.js b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.js new file mode 100644 index 0000000000000..7d1fdcb8879d9 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.js @@ -0,0 +1,3 @@ +const message = 'Why hello there' + +module.exports = { message } diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.mjs b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.mjs new file mode 100644 index 0000000000000..a81498ed3ad53 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.mjs @@ -0,0 +1 @@ +export const message = 'Why hello there' diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.module.scss b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.module.scss new file mode 100644 index 0000000000000..d2d4babe01664 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/index.module.scss @@ -0,0 +1,7 @@ +@import 'other2.scss'; + +$var: blue; +.subClass { + composes: className from './other.scss'; + background: $var; +} diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other.scss b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other.scss new file mode 100644 index 0000000000000..eb0f82f7aacb4 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other.scss @@ -0,0 +1,7 @@ +@import 'other3.scss'; + +$var: red; +.className { + background: $var; + color: yellow; +} diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other2.scss b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other2.scss new file mode 100644 index 0000000000000..7525334516ec4 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other2.scss @@ -0,0 +1,4 @@ +$var: red; +.other2 { + color: $var; +} diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other3.scss b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other3.scss new file mode 100644 index 0000000000000..ecbdacff019d8 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/other3.scss @@ -0,0 +1,4 @@ +$var: violet; +.other3 { + color: $var; +} diff --git a/test/integration/scss-fixtures/nm-module-nested/node_modules/example/package.json b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/package.json new file mode 100644 index 0000000000000..ab5f2cd4a1cf7 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/node_modules/example/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "main": "index" +} diff --git a/test/integration/scss-fixtures/nm-module-nested/pages/index.js b/test/integration/scss-fixtures/nm-module-nested/pages/index.js new file mode 100644 index 0000000000000..897696bd01572 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module-nested/pages/index.js @@ -0,0 +1,12 @@ +import * as data from 'example' +import * as classes from 'example/index.module.scss' + +function Home() { + return ( +
+ {JSON.stringify(data)} {JSON.stringify(classes)} +
+ ) +} + +export default Home diff --git a/test/integration/scss-fixtures/nm-module/node_modules/example/index.js b/test/integration/scss-fixtures/nm-module/node_modules/example/index.js new file mode 100644 index 0000000000000..7d1fdcb8879d9 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module/node_modules/example/index.js @@ -0,0 +1,3 @@ +const message = 'Why hello there' + +module.exports = { message } diff --git a/test/integration/scss-fixtures/nm-module/node_modules/example/index.mjs b/test/integration/scss-fixtures/nm-module/node_modules/example/index.mjs new file mode 100644 index 0000000000000..a81498ed3ad53 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module/node_modules/example/index.mjs @@ -0,0 +1 @@ +export const message = 'Why hello there' diff --git a/test/integration/scss-fixtures/nm-module/node_modules/example/index.module.scss b/test/integration/scss-fixtures/nm-module/node_modules/example/index.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/nm-module/node_modules/example/index.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/nm-module/node_modules/example/package.json b/test/integration/scss-fixtures/nm-module/node_modules/example/package.json new file mode 100644 index 0000000000000..ab5f2cd4a1cf7 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module/node_modules/example/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "main": "index" +} diff --git a/test/integration/scss-fixtures/nm-module/pages/index.js b/test/integration/scss-fixtures/nm-module/pages/index.js new file mode 100644 index 0000000000000..897696bd01572 --- /dev/null +++ b/test/integration/scss-fixtures/nm-module/pages/index.js @@ -0,0 +1,12 @@ +import * as data from 'example' +import * as classes from 'example/index.module.scss' + +function Home() { + return ( +
+ {JSON.stringify(data)} {JSON.stringify(classes)} +
+ ) +} + +export default Home diff --git a/test/integration/scss-fixtures/npm-import-bad/pages/_app.js b/test/integration/scss-fixtures/npm-import-bad/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-bad/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/npm-import-bad/pages/index.js b/test/integration/scss-fixtures/npm-import-bad/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-bad/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/npm-import-bad/styles/global.scss b/test/integration/scss-fixtures/npm-import-bad/styles/global.scss new file mode 100644 index 0000000000000..bc9b10bfd0877 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-bad/styles/global.scss @@ -0,0 +1 @@ +@import 'nprogress/nprogress.css'; diff --git a/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.js b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.js new file mode 100644 index 0000000000000..7d1fdcb8879d9 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.js @@ -0,0 +1,3 @@ +const message = 'Why hello there' + +module.exports = { message } diff --git a/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.mjs b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.mjs new file mode 100644 index 0000000000000..a81498ed3ad53 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/index.mjs @@ -0,0 +1 @@ +export const message = 'Why hello there' diff --git a/test/integration/scss-fixtures/npm-import-nested/node_modules/example/other.scss b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/other.scss new file mode 100644 index 0000000000000..b2a7622386198 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/other.scss @@ -0,0 +1,4 @@ +$var: blue; +.other { + color: $var; +} diff --git a/test/integration/scss-fixtures/npm-import-nested/node_modules/example/package.json b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/package.json new file mode 100644 index 0000000000000..ab5f2cd4a1cf7 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "main": "index" +} diff --git a/test/integration/scss-fixtures/npm-import-nested/node_modules/example/test.scss b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/test.scss new file mode 100644 index 0000000000000..2db9b0627d1ef --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/node_modules/example/test.scss @@ -0,0 +1,6 @@ +@import 'other.scss'; + +$var: red; +.test { + color: $var; +} diff --git a/test/integration/scss-fixtures/npm-import-nested/pages/_app.js b/test/integration/scss-fixtures/npm-import-nested/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/npm-import-nested/pages/index.js b/test/integration/scss-fixtures/npm-import-nested/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/npm-import-nested/styles/global.scss b/test/integration/scss-fixtures/npm-import-nested/styles/global.scss new file mode 100644 index 0000000000000..fb61b329db5f7 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import-nested/styles/global.scss @@ -0,0 +1 @@ +@import '~example/test.scss'; diff --git a/test/integration/scss-fixtures/npm-import/pages/_app.js b/test/integration/scss-fixtures/npm-import/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/npm-import/pages/index.js b/test/integration/scss-fixtures/npm-import/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/npm-import/styles/global.scss b/test/integration/scss-fixtures/npm-import/styles/global.scss new file mode 100644 index 0000000000000..a369dafd92cc4 --- /dev/null +++ b/test/integration/scss-fixtures/npm-import/styles/global.scss @@ -0,0 +1 @@ +@import '~nprogress/nprogress.css'; diff --git a/test/integration/scss-fixtures/prod-module/pages/index.js b/test/integration/scss-fixtures/prod-module/pages/index.js new file mode 100644 index 0000000000000..7b42859a67e32 --- /dev/null +++ b/test/integration/scss-fixtures/prod-module/pages/index.js @@ -0,0 +1,9 @@ +import { redText } from './index.module.scss' + +export default function Home() { + return ( +
+ This text should be red. +
+ ) +} diff --git a/test/integration/scss-fixtures/prod-module/pages/index.module.scss b/test/integration/scss-fixtures/prod-module/pages/index.module.scss new file mode 100644 index 0000000000000..eb8bb05c73aca --- /dev/null +++ b/test/integration/scss-fixtures/prod-module/pages/index.module.scss @@ -0,0 +1,4 @@ +$var: red; +.redText { + color: $var; +} diff --git a/test/integration/scss-fixtures/single-global-src/src/pages/_app.js b/test/integration/scss-fixtures/single-global-src/src/pages/_app.js new file mode 100644 index 0000000000000..13558a96f8e8a --- /dev/null +++ b/test/integration/scss-fixtures/single-global-src/src/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/single-global-src/src/pages/index.js b/test/integration/scss-fixtures/single-global-src/src/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/single-global-src/src/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/single-global-src/styles/global.scss b/test/integration/scss-fixtures/single-global-src/styles/global.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/single-global-src/styles/global.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/single-global/pages/_app.js b/test/integration/scss-fixtures/single-global/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/single-global/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/single-global/pages/index.js b/test/integration/scss-fixtures/single-global/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/single-global/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/single-global/styles/global.scss b/test/integration/scss-fixtures/single-global/styles/global.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/single-global/styles/global.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/unused/pages/index.js b/test/integration/scss-fixtures/unused/pages/index.js new file mode 100644 index 0000000000000..b3ba78da2d5e1 --- /dev/null +++ b/test/integration/scss-fixtures/unused/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
+} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/assets/light.svg b/test/integration/scss-fixtures/url-global-asset-prefix-1/assets/light.svg new file mode 100644 index 0000000000000..0194cbce06561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/assets/light.svg @@ -0,0 +1,17 @@ + + + + Logotype - White + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/next.config.js b/test/integration/scss-fixtures/url-global-asset-prefix-1/next.config.js new file mode 100644 index 0000000000000..177a5d7903f16 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/next.config.js @@ -0,0 +1,5 @@ +const config = require('../next.config.js') +module.exports = { + ...config, + assetPrefix: '/foo/', +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/_app.js b/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/index.js b/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark.svg b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark2.svg b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark2.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/dark2.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global1.scss b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global1.scss new file mode 100644 index 0000000000000..24f6145a7a6fa --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global1.scss @@ -0,0 +1,5 @@ +$var: red; +.red-text { + color: $var; + background-image: url('./dark.svg') url(dark2.svg); +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2.scss b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2.scss new file mode 100644 index 0000000000000..4b659d369c561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2.scss @@ -0,0 +1,6 @@ +@import './global2b.scss'; + +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2b.scss b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2b.scss new file mode 100644 index 0000000000000..0fe57600718f5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-1/styles/global2b.scss @@ -0,0 +1,6 @@ +$var: orange; +.blue-text { + color: $var; + font-weight: bolder; + background-image: url(../assets/light.svg); +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/assets/light.svg b/test/integration/scss-fixtures/url-global-asset-prefix-2/assets/light.svg new file mode 100644 index 0000000000000..0194cbce06561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/assets/light.svg @@ -0,0 +1,17 @@ + + + + Logotype - White + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/next.config.js b/test/integration/scss-fixtures/url-global-asset-prefix-2/next.config.js new file mode 100644 index 0000000000000..72ff6da7d9adf --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/next.config.js @@ -0,0 +1,5 @@ +const config = require('../next.config.js') +module.exports = { + ...config, + assetPrefix: '/foo', +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/_app.js b/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/index.js b/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark.svg b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark2.svg b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark2.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/dark2.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global1.scss b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global1.scss new file mode 100644 index 0000000000000..24f6145a7a6fa --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global1.scss @@ -0,0 +1,5 @@ +$var: red; +.red-text { + color: $var; + background-image: url('./dark.svg') url(dark2.svg); +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2.scss b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2.scss new file mode 100644 index 0000000000000..4b659d369c561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2.scss @@ -0,0 +1,6 @@ +@import './global2b.scss'; + +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2b.scss b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2b.scss new file mode 100644 index 0000000000000..0fe57600718f5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global-asset-prefix-2/styles/global2b.scss @@ -0,0 +1,6 @@ +$var: orange; +.blue-text { + color: $var; + font-weight: bolder; + background-image: url(../assets/light.svg); +} diff --git a/test/integration/scss-fixtures/url-global/assets/light.svg b/test/integration/scss-fixtures/url-global/assets/light.svg new file mode 100644 index 0000000000000..0194cbce06561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/assets/light.svg @@ -0,0 +1,17 @@ + + + + Logotype - White + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global/pages/_app.js b/test/integration/scss-fixtures/url-global/pages/_app.js new file mode 100644 index 0000000000000..29beb9fdb3cc5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/pages/_app.js @@ -0,0 +1,13 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global1.scss' +import '../styles/global2.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/url-global/pages/index.js b/test/integration/scss-fixtures/url-global/pages/index.js new file mode 100644 index 0000000000000..5cbac8a153d77 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/url-global/styles/dark.svg b/test/integration/scss-fixtures/url-global/styles/dark.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/styles/dark.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global/styles/dark2.svg b/test/integration/scss-fixtures/url-global/styles/dark2.svg new file mode 100644 index 0000000000000..bdae3d6587872 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/styles/dark2.svg @@ -0,0 +1,17 @@ + + + + Logotype - Black + Created with Sketch. + + + + + + + + + + + + diff --git a/test/integration/scss-fixtures/url-global/styles/global1.scss b/test/integration/scss-fixtures/url-global/styles/global1.scss new file mode 100644 index 0000000000000..24f6145a7a6fa --- /dev/null +++ b/test/integration/scss-fixtures/url-global/styles/global1.scss @@ -0,0 +1,5 @@ +$var: red; +.red-text { + color: $var; + background-image: url('./dark.svg') url(dark2.svg); +} diff --git a/test/integration/scss-fixtures/url-global/styles/global2.scss b/test/integration/scss-fixtures/url-global/styles/global2.scss new file mode 100644 index 0000000000000..4b659d369c561 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/styles/global2.scss @@ -0,0 +1,6 @@ +@import './global2b.scss'; + +$var: blue; +.blue-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/url-global/styles/global2b.scss b/test/integration/scss-fixtures/url-global/styles/global2b.scss new file mode 100644 index 0000000000000..0fe57600718f5 --- /dev/null +++ b/test/integration/scss-fixtures/url-global/styles/global2b.scss @@ -0,0 +1,6 @@ +$var: orange; +.blue-text { + color: $var; + font-weight: bolder; + background-image: url(../assets/light.svg); +} diff --git a/test/integration/scss-fixtures/valid-and-invalid-global/pages/_app.js b/test/integration/scss-fixtures/valid-and-invalid-global/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/valid-and-invalid-global/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/valid-and-invalid-global/pages/index.js b/test/integration/scss-fixtures/valid-and-invalid-global/pages/index.js new file mode 100644 index 0000000000000..38d46eb2a45eb --- /dev/null +++ b/test/integration/scss-fixtures/valid-and-invalid-global/pages/index.js @@ -0,0 +1,5 @@ +import '../styles/global.scss' + +export default function Home() { + return
This text should be red.
+} diff --git a/test/integration/scss-fixtures/valid-and-invalid-global/styles/global.scss b/test/integration/scss-fixtures/valid-and-invalid-global/styles/global.scss new file mode 100644 index 0000000000000..da7b3d1417b1b --- /dev/null +++ b/test/integration/scss-fixtures/valid-and-invalid-global/styles/global.scss @@ -0,0 +1,4 @@ +$var: red; +.red-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/with-styled-jsx/pages/_app.js b/test/integration/scss-fixtures/with-styled-jsx/pages/_app.js new file mode 100644 index 0000000000000..b89fe16feb731 --- /dev/null +++ b/test/integration/scss-fixtures/with-styled-jsx/pages/_app.js @@ -0,0 +1,12 @@ +import React from 'react' +import App from 'next/app' +import '../styles/global.scss' + +class MyApp extends App { + render() { + const { Component, pageProps } = this.props + return + } +} + +export default MyApp diff --git a/test/integration/scss-fixtures/with-styled-jsx/pages/index.js b/test/integration/scss-fixtures/with-styled-jsx/pages/index.js new file mode 100644 index 0000000000000..ed9aa0621925e --- /dev/null +++ b/test/integration/scss-fixtures/with-styled-jsx/pages/index.js @@ -0,0 +1,12 @@ +export default function Home() { + return ( + <> +
This text should be green.
+ + + ) +} diff --git a/test/integration/scss-fixtures/with-styled-jsx/styles/global.scss b/test/integration/scss-fixtures/with-styled-jsx/styles/global.scss new file mode 100644 index 0000000000000..30a5d888c5011 --- /dev/null +++ b/test/integration/scss-fixtures/with-styled-jsx/styles/global.scss @@ -0,0 +1,4 @@ +$var: red; +.my-text { + color: $var; +} diff --git a/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/_app.js b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/_app.js new file mode 100644 index 0000000000000..0e40312aad8ac --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/_app.js @@ -0,0 +1,5 @@ +import '../styles/global.scss' + +export default function MyApp({ Component, pageProps }) { + return +} diff --git a/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/index.js b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/index.js new file mode 100644 index 0000000000000..ee6a89480a706 --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/pages/index.js @@ -0,0 +1,38 @@ +import Link from 'next/link' + +const links = [ + { href: 'https://github.com/zeit/next.js', label: 'GitHub' }, + { href: 'https://nextjs.org/docs', label: 'Docs' }, +] + +function Nav() { + return ( + + ) +} + +export default () => ( +
+
+) diff --git a/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/postcss.config.js b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/postcss.config.js new file mode 100644 index 0000000000000..b39d3d7ff9d14 --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/postcss.config.js @@ -0,0 +1,13 @@ +const path = require('path') +module.exports = { + plugins: [ + 'tailwindcss', + [ + '@fullhuman/postcss-purgecss', + { + content: [path.join(__dirname, './pages/**/*.{js,jsx,ts,tsx}')], + defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || [], + }, + ], + ], +} diff --git a/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/styles/global.scss b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/styles/global.scss new file mode 100644 index 0000000000000..b5c61c956711f --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss-and-purgecss/styles/global.scss @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/test/integration/scss-fixtures/with-tailwindcss/package.json b/test/integration/scss-fixtures/with-tailwindcss/package.json new file mode 100644 index 0000000000000..50375a78eb20b --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss/package.json @@ -0,0 +1,7 @@ +{ + "postcss": { + "plugins": { + "tailwindcss": {} + } + } +} diff --git a/test/integration/scss-fixtures/with-tailwindcss/pages/_app.js b/test/integration/scss-fixtures/with-tailwindcss/pages/_app.js new file mode 100644 index 0000000000000..0e40312aad8ac --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss/pages/_app.js @@ -0,0 +1,5 @@ +import '../styles/global.scss' + +export default function MyApp({ Component, pageProps }) { + return +} diff --git a/test/integration/scss-fixtures/with-tailwindcss/pages/index.js b/test/integration/scss-fixtures/with-tailwindcss/pages/index.js new file mode 100644 index 0000000000000..b3ba78da2d5e1 --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss/pages/index.js @@ -0,0 +1,3 @@ +export default function Home() { + return
+} diff --git a/test/integration/scss-fixtures/with-tailwindcss/styles/global.scss b/test/integration/scss-fixtures/with-tailwindcss/styles/global.scss new file mode 100644 index 0000000000000..b5c61c956711f --- /dev/null +++ b/test/integration/scss-fixtures/with-tailwindcss/styles/global.scss @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/test/integration/scss-modules/test/index.test.js b/test/integration/scss-modules/test/index.test.js new file mode 100644 index 0000000000000..751ef2e07889c --- /dev/null +++ b/test/integration/scss-modules/test/index.test.js @@ -0,0 +1,383 @@ +/* eslint-env jest */ +/* global jasmine */ +import { join } from 'path' +import { remove, readFile, readdir } from 'fs-extra' +import { + nextBuild, + nextStart, + findPort, + killApp, + launchApp, + waitFor, + renderViaHTTP, +} from 'next-test-utils' +import cheerio from 'cheerio' +import webdriver from 'next-webdriver' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 + +const fixturesDir = join(__dirname, '../../scss-fixtures') + +describe('Basic SCSS Module Support', () => { + const appDir = join(fixturesDir, 'basic-module') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".index_redText__2VIiM{color:red}"` + ) + }) + + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) + + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + expect($('#verify-red').attr('class')).toMatchInlineSnapshot( + `"index_redText__2VIiM"` + ) + }) +}) + +describe('3rd Party CSS Module Support', () => { + const appDir = join(fixturesDir, '3rd-party-module') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".index_foo__9_fxH{position:relative}.index_foo__9_fxH .bar,.index_foo__9_fxH .baz{height:100%;overflow:hidden}.index_foo__9_fxH .lol,.index_foo__9_fxH>.lel{width:80%}"` + ) + }) + + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) + + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + expect($('#verify-div').attr('class')).toMatchInlineSnapshot( + `"index_foo__9_fxH"` + ) + }) +}) + +describe('Has CSS Module in computed styles in Development', () => { + const appDir = join(fixturesDir, 'dev-module') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + }) +}) + +describe('Has CSS Module in computed styles in Production', () => { + const appDir = join(fixturesDir, 'prod-module') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + }) +}) + +xdescribe('Can hot reload CSS Module without losing state', () => { + const appDir = join(fixturesDir, 'hmr-module') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + // FIXME: this is broken + it('should update CSS color without remounting ', async () => { + const browser = await webdriver(appPort, '/') + + const desiredText = 'hello world' + await browser.elementById('text-input').type(desiredText) + expect(await browser.elementById('text-input').getValue()).toBe(desiredText) + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + const cssFile = new File(join(appDir, 'pages/index.module.scss')) + try { + cssFile.replace('color: red', 'color: purple') + await waitFor(2000) // wait for HMR + + const refreshedColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(refreshedColor).toMatchInlineSnapshot(`"rgb(128, 0, 128)"`) + + // ensure text remained + expect(await browser.elementById('text-input').getValue()).toBe( + desiredText + ) + } finally { + cssFile.restore() + } + }) +}) + +describe('Invalid CSS Module Usage in node_modules', () => { + const appDir = join(fixturesDir, 'invalid-module') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('node_modules/example/index.module.scss') + expect(stderr).toMatch( + /CSS Modules.*cannot.*be imported from within.*node_modules/ + ) + expect(stderr).toMatch(/Location:.*node_modules[\\/]example[\\/]index\.mjs/) + }) +}) + +describe('Invalid CSS Module Usage in node_modules', () => { + const appDir = join(fixturesDir, 'invalid-global-module') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('node_modules/example/index.scss') + expect(stderr).toMatch( + /Global CSS.*cannot.*be imported from within.*node_modules/ + ) + expect(stderr).toMatch(/Location:.*node_modules[\\/]example[\\/]index\.mjs/) + }) +}) + +describe('Valid CSS Module Usage from within node_modules', () => { + const appDir = join(fixturesDir, 'nm-module') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it(`should've prerendered with relevant data`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) + + const cssPreload = $('#nm-div') + expect(cssPreload.text()).toMatchInlineSnapshot( + `"{\\"message\\":\\"Why hello there\\"} {\\"redText\\":\\"example_redText__1hNNA\\"}"` + ) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".example_redText__1hNNA{color:red}"` + ) + }) +}) + +describe('Valid Nested CSS Module Usage from within node_modules', () => { + const appDir = join(fixturesDir, 'nm-module-nested') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it(`should've prerendered with relevant data`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) + + const cssPreload = $('#nm-div') + expect(cssPreload.text()).toMatchInlineSnapshot( + `"{\\"message\\":\\"Why hello there\\"} {\\"other2\\":\\"example_other2__1pnJV\\",\\"subClass\\":\\"example_subClass__2EbKX other_className__E6nd8\\"}"` + ) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".other_other3__ZPN-Y{color:violet}.other_className__E6nd8{background:red;color:#ff0}.example_other2__1pnJV{color:red}.example_subClass__2EbKX{background:#00f}"` + ) + }) +}) + +describe('CSS Module Composes Usage (Basic)', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-basic') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".index_className__2O8Wt{background:red;color:#ff0}.index_subClass__3e6Re{background:#00f}"` + ) + }) +}) + +describe('CSS Module Composes Usage (External)', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-external') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( + `".other_className__2VTl4{background:red;color:#ff0}.index_subClass__3e6Re{background:#00f}"` + ) + }) +}) diff --git a/test/integration/scss/test/index.test.js b/test/integration/scss/test/index.test.js new file mode 100644 index 0000000000000..aca8a733f9951 --- /dev/null +++ b/test/integration/scss/test/index.test.js @@ -0,0 +1,813 @@ +/* eslint-env jest */ +/* global jasmine */ +import 'flat-map-polyfill' +import { join } from 'path' +import { readdir, readFile, remove } from 'fs-extra' +import { + findPort, + nextBuild, + nextStart, + launchApp, + killApp, + File, + waitFor, + renderViaHTTP, +} from 'next-test-utils' +import webdriver from 'next-webdriver' +import cheerio from 'cheerio' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 + +const fixturesDir = join(__dirname, '../..', 'scss-fixtures') + +describe('SCSS Support', () => { + describe('Basic Global Support', () => { + const appDir = join(fixturesDir, 'single-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) + }) + + describe('Basic Global Support with src/ dir', () => { + const appDir = join(fixturesDir, 'single-global-src') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) + }) + + describe('Multi Global Support', () => { + const appDir = join(fixturesDir, 'multi-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".red-text{color:red}.blue-text{color:#00f}"`) + }) + }) + + describe('Nested @import() Global Support', () => { + const appDir = join(fixturesDir, 'nested-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".red-text{color:purple;font-weight:bolder;color:red}.blue-text{color:orange;font-weight:bolder;color:#00f}"` + ) + }) + }) + + describe('CSS Compilation and Prefixing', () => { + const appDir = join(fixturesDir, 'compilation-and-prefixing') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".redText ::-webkit-input-placeholder{color:red}.redText ::-moz-placeholder{color:red}.redText :-ms-input-placeholder{color:red}.redText ::-ms-input-placeholder{color:red}.redText ::placeholder{color:red}"` + ) + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter(f => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + const cssMapContent = ( + await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') + ).trim() + + const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) + expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` + Object { + "mappings": "AACA,qCAEI,SAHK,CACT,4BAEI,SAHK,CACT,gCAEI,SAHK,CACT,iCAEI,SAHK,CACT,uBAEI,SAHK", + "sourcesContent": Array [ + "$var: red; + .redText { + ::placeholder { + color: $var; + } + } + ", + ], + "version": 3, + } + `) + }) + }) + + // Tests css ordering + describe('Multi Global Support (reversed)', () => { + const appDir = join(fixturesDir, 'multi-global-reversed') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".blue-text{color:#00f}.red-text{color:red}"`) + }) + }) + + describe('Invalid CSS in _document', () => { + const appDir = join(fixturesDir, 'invalid-module-document') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('styles.module.scss') + expect(stderr).toMatch( + /CSS.*cannot.*be imported within.*pages[\\/]_document\.js/ + ) + expect(stderr).toMatch(/Location:.*pages[\\/]_document\.js/) + }) + }) + + describe('Invalid Global CSS', () => { + const appDir = join(fixturesDir, 'invalid-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('styles/global.scss') + expect(stderr).toMatch( + /Please move all global CSS imports.*?pages(\/|\\)_app/ + ) + expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/) + }) + }) + + describe('Invalid Global CSS with Custom App', () => { + const appDir = join(fixturesDir, 'invalid-global-with-app') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('styles/global.scss') + expect(stderr).toMatch( + /Please move all global CSS imports.*?pages(\/|\\)_app/ + ) + expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/) + }) + }) + + describe('Valid and Invalid Global CSS with Custom App', () => { + const appDir = join(fixturesDir, 'valid-and-invalid-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail to build', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + }) + expect(stderr).toContain('Failed to compile') + expect(stderr).toContain('styles/global.scss') + expect(stderr).toContain('Please move all global CSS imports') + expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/) + }) + }) + + describe('Can hot reload CSS without losing state', () => { + const appDir = join(fixturesDir, 'multi-page') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should update CSS color without remounting ', async () => { + let browser + try { + browser = await webdriver(appPort, '/page1') + + const desiredText = 'hello world' + await browser.elementById('text-input').type(desiredText) + expect(await browser.elementById('text-input').getValue()).toBe( + desiredText + ) + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.red-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + const cssFile = new File(join(appDir, 'styles/global1.scss')) + try { + cssFile.replace('$var: red', '$var: purple') + await waitFor(2000) // wait for HMR + + const refreshedColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.red-text')).color` + ) + expect(refreshedColor).toMatchInlineSnapshot(`"rgb(128, 0, 128)"`) + + // ensure text remained + expect(await browser.elementById('text-input').getValue()).toBe( + desiredText + ) + } finally { + cssFile.restore() + } + } finally { + if (browser) { + await browser.close() + } + } + }) + }) + + describe('Has CSS in computed styles in Development', () => { + const appDir = join(fixturesDir, 'multi-page') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have CSS for page', async () => { + let browser + try { + browser = await webdriver(appPort, '/page2') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.blue-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + } finally { + if (browser) { + await browser.close() + } + } + }) + }) + + describe('Body is not hidden when unused in Development', () => { + const appDir = join(fixturesDir, 'unused') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have body visible', async () => { + let browser + try { + browser = await webdriver(appPort, '/') + const currentDisplay = await browser.eval( + `window.getComputedStyle(document.querySelector('body')).display` + ) + expect(currentDisplay).toBe('block') + } finally { + if (browser) { + await browser.close() + } + } + }) + }) + + describe('Body is not hidden when broken in Development', () => { + const appDir = join(fixturesDir, 'unused') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have body visible', async () => { + const pageFile = new File(join(appDir, 'pages/index.js')) + let browser + try { + pageFile.replace('
', '
') + await waitFor(2000) // wait for recompile + + browser = await webdriver(appPort, '/') + const currentDisplay = await browser.eval( + `window.getComputedStyle(document.querySelector('body')).display` + ) + expect(currentDisplay).toBe('block') + } finally { + pageFile.restore() + if (browser) { + await browser.close() + } + } + }) + }) + + describe('Has CSS in computed styles in Production', () => { + const appDir = join(fixturesDir, 'multi-page') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/page2') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.blue-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + }) + + it(`should've preloaded the CSS file and injected it in `, async () => { + const content = await renderViaHTTP(appPort, '/page2') + const $ = cheerio.load(content) + + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + /* ensure CSS preloaded first */ + const allPreloads = [].slice.call($('link[rel="preload"]')) + const styleIndexes = allPreloads.flatMap((p, i) => + p.attribs.as === 'style' ? i : [] + ) + expect(styleIndexes).toEqual([0]) + }) + }) + + describe('CSS URL via `file-loader`', () => { + const appDir = join(fixturesDir, 'url-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/_next\/static\/media\/dark\.[a-z0-9]{32}\.svg\) url\(\/_next\/static\/media\/dark2\.[a-z0-9]{32}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/_next\/static\/media\/light\.[a-z0-9]{32}\.svg\);color:#00f\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map(fileName => + /^(.+?)\..{32}\.(.+?)$/ + .exec(fileName) + .slice(1) + .join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + Array [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) + }) + + describe('CSS URL via `file-loader` and asset prefix (1)', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-1') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-z0-9]{32}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-z0-9]{32}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-z0-9]{32}\.svg\);color:#00f\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map(fileName => + /^(.+?)\..{32}\.(.+?)$/ + .exec(fileName) + .slice(1) + .join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + Array [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) + }) + + describe('CSS URL via `file-loader` and asset prefix (2)', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-2') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-z0-9]{32}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-z0-9]{32}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-z0-9]{32}\.svg\);color:#00f\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map(fileName => + /^(.+?)\..{32}\.(.+?)$/ + .exec(fileName) + .slice(1) + .join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + Array [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) + }) + + describe('Good CSS Import from node_modules', () => { + const appDir = join(fixturesDir, 'npm-import') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(/nprogress/) + }) + }) + + describe('Good Nested CSS Import from node_modules', () => { + const appDir = join(fixturesDir, 'npm-import-nested') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".other{color:#00f}.test{color:red}"`) + }) + }) + + describe('Bad CSS Import from node_modules', () => { + const appDir = join(fixturesDir, 'npm-import-bad') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) + + expect(stderr).toMatch(/Can't resolve '[^']*?nprogress[^']*?'/) + expect(stderr).toMatch(/Build error occurred/) + }) + }) + + describe('Ordering with styled-jsx (dev)', () => { + const appDir = join(fixturesDir, 'with-styled-jsx') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have the correct color (css ordering)', async () => { + const browser = await webdriver(appPort, '/') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.my-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 128, 0)"`) + }) + }) + + describe('Ordering with styled-jsx (prod)', () => { + const appDir = join(fixturesDir, 'with-styled-jsx') + + let appPort + let app + beforeAll(async () => { + await remove(join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have the correct color (css ordering)', async () => { + const browser = await webdriver(appPort, '/') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.my-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 128, 0)"`) + }) + }) + + describe('Basic Tailwind CSS', () => { + const appDir = join(fixturesDir, 'with-tailwindcss') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent).toMatch(/object-right-bottom/) // look for tailwind's CSS + expect(cssContent).not.toMatch(/tailwind/) // ensure @tailwind was removed + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter(f => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + }) + }) + + describe('Tailwind and Purge CSS', () => { + const appDir = join(fixturesDir, 'with-tailwindcss-and-purgecss') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should build successfully', async () => { + await nextBuild(appDir) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter(f => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect(cssContent).not.toMatch(/object-right-bottom/) // this was unused and should be gone + expect(cssContent).toMatch(/text-blue-500/) // this was used + expect(cssContent).not.toMatch(/tailwind/) // ensure @tailwind was removed + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter(f => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index 46d1480b54c51..907b2f185d5e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3031,6 +3031,17 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== +adjust-sourcemap-loader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" + integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== + dependencies: + assert "1.4.1" + camelcase "5.0.0" + loader-utils "1.2.3" + object-path "0.11.4" + regex-parser "2.2.10" + after-all-results@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0" @@ -3237,6 +3248,11 @@ args@4.0.0: leven "2.1.0" mri "1.1.0" +arity-n@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" + integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -3359,6 +3375,13 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assert@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + dependencies: + util "0.10.3" + assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -4090,6 +4113,11 @@ camelcase@5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + camelcase@^2.0.0, camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -4105,11 +4133,6 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -4434,6 +4457,15 @@ clone-deep@^0.3.0: kind-of "^3.2.2" shallow-clone "^0.1.2" +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -4589,6 +4621,13 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compose-function@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" + integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= + dependencies: + arity-n "^1.0.4" + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -4792,6 +4831,11 @@ convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +convert-source-map@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -5135,7 +5179,7 @@ css-what@^3.2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== -css@2.2.4: +css@2.2.4, css@^2.0.0: version "2.2.4" resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== @@ -5291,6 +5335,14 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" @@ -5845,6 +5897,24 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-promise@^4.0.3, es6-promise@^4.1.1: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -5857,6 +5927,14 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escape-goat@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-1.3.0.tgz#bf3ee8ad1e488fbba404b084b2e4a55e09231c64" @@ -6235,6 +6313,13 @@ express@^4.16.3: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -10111,6 +10196,11 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -10559,6 +10649,11 @@ object-keys@~0.4.0: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= +object-path@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" + integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -12049,6 +12144,15 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" +postcss@7.0.21: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + postcss@^5.0.10: version "5.2.18" resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" @@ -12722,6 +12826,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regex-parser@2.2.10: + version "2.2.10" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" + integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -13029,6 +13138,22 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-url-loader@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0" + integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== + dependencies: + adjust-sourcemap-loader "2.0.0" + camelcase "5.3.1" + compose-function "3.0.3" + convert-source-map "1.7.0" + es6-iterator "2.0.3" + loader-utils "1.2.3" + postcss "7.0.21" + rework "1.0.1" + rework-visit "1.0.0" + source-map "0.6.1" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -13103,6 +13228,19 @@ reusify@^1.0.0: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rework-visit@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= + +rework@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= + dependencies: + convert-source-map "^0.3.3" + css "^2.0.0" + rewrite-imports@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/rewrite-imports/-/rewrite-imports-1.4.0.tgz#198ebb73f59cfee8d214516c774b6aeef2c54a6e" @@ -13260,6 +13398,17 @@ sass-loader@6.0.6: lodash.tail "^4.1.1" pify "^3.0.0" +sass-loader@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" + integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== + dependencies: + clone-deep "^4.0.1" + loader-utils "^1.2.3" + neo-async "^2.6.1" + schema-utils "^2.6.1" + semver "^6.3.0" + sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -13290,6 +13439,14 @@ schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.6.0: ajv "^6.10.2" ajv-keywords "^3.4.1" +schema-utils@^2.6.1: + version "2.6.4" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" + integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -13447,6 +13604,13 @@ shallow-clone@^0.1.2: lazy-cache "^0.2.3" mixin-object "^2.0.1" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -14662,6 +14826,16 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"