diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 4f50144b78..c58e929055 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -7,7 +7,6 @@ import { readJson, runCLI, runCLIAsync, - supportUi, uniq, updateFile, } from '@nrwl/e2e/utils'; @@ -87,6 +86,24 @@ forEachCli('nx', () => { const mainPath = `apps/${appName}/pages/index.tsx`; updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath)); + // Update lib to use css modules + updateFile( + `libs/${libName}/src/lib/${libName}.tsx`, + ` + import React from 'react'; + import styles from './style.module.css'; + export function Test() { + return
Hello
; + } + ` + ); + updateFile( + `libs/${libName}/src/lib/style.module.css`, + ` + .container {} + ` + ); + await checkApp(appName, { checkUnitTest: true, checkLint: true, diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts new file mode 100644 index 0000000000..a0758a3d5a --- /dev/null +++ b/packages/next/plugins/with-nx.ts @@ -0,0 +1,91 @@ +const { join } = require('path'); +const { appRootPath } = require('@nrwl/workspace/src/utils/app-root'); +const { workspaceLayout } = require('@nrwl/workspace/src/core/file-utils'); + +function regexEqual(x, y) { + return ( + x instanceof RegExp && + y instanceof RegExp && + x.source === y.source && + x.global === y.global && + x.ignoreCase === y.ignoreCase && + x.multiline === y.multiline + ); +} + +function withNx(nextConfig = {} as any) { + const userWebpack = nextConfig.webpack || ((x) => x); + return { + ...nextConfig, + /* + * Modify the Next.js webpack config to allow workspace libs to use css modules. + * + * Note: This would be easier if Next.js exposes css-loader and sass-loader on `defaultLoaders`. + */ + webpack: (config, { defaultLoaders }) => { + // Include workspace libs in css/sass loaders + const includes = [join(appRootPath, workspaceLayout().libsDir)]; + + const nextCssLoaders = config.module.rules.find( + (rule) => typeof rule.oneOf === 'object' + ); + + // webpack config is not as expected + if (!nextCssLoaders) return config; + + /* + * 1. Modify css loader to enable module support for workspace libs + */ + const nextCssLoader = nextCssLoaders.oneOf.find( + (rule) => + rule.sideEffects === false && regexEqual(rule.test, /\.module\.css$/) + ); + // Might not be found if Next.js webpack config changes in the future + if (nextCssLoader) { + nextCssLoader.issuer.or = nextCssLoader.issuer.and + ? nextCssLoader.issuer.and.concat(includes) + : includes; + delete nextCssLoader.issuer.and; + } + + /* + * 2. Modify sass loader to enable module support for workspace libs + */ + const nextSassLoader = nextCssLoaders.oneOf.find( + (rule) => + rule.sideEffects === false && + regexEqual(rule.test, /\.module\.(scss|sass)$/) + ); + // Might not be found if Next.js webpack config changes in the future + if (nextSassLoader) { + nextSassLoader.issuer.or = nextSassLoader.issuer.and + ? nextSassLoader.issuer.and.concat(includes) + : includes; + delete nextSassLoader.issuer.and; + } + + /* + * 3. Modify error loader to ignore css modules used by workspace libs + */ + const nextErrorCssModuleLoader = nextCssLoaders.oneOf.find( + (rule) => + rule.use && + rule.use.loader === 'error-loader' && + rule.use.options && + (rule.use.options.reason === + 'CSS Modules \u001b[1mcannot\u001b[22m be imported from within \u001b[1mnode_modules\u001b[22m.\n' + + 'Read more: https://err.sh/next.js/css-modules-npm' || + rule.use.options.reason === + 'CSS Modules cannot be imported from within node_modules.\nRead more: https://err.sh/next.js/css-modules-npm') + ); + // Might not be found if Next.js webpack config changes in the future + if (nextErrorCssModuleLoader) { + nextErrorCssModuleLoader.exclude = includes; + } + + return userWebpack(config); + }, + }; +} + +module.exports = withNx; diff --git a/packages/next/src/schematics/application/files/next.config.js__tmpl__ b/packages/next/src/schematics/application/files/next.config.js__tmpl__ index fb13bf5b36..4fa42b1c9d 100644 --- a/packages/next/src/schematics/application/files/next.config.js__tmpl__ +++ b/packages/next/src/schematics/application/files/next.config.js__tmpl__ @@ -1,25 +1,27 @@ +const withNx = require('@nrwl/next/plugins/with-nx'); + <% if (style === 'less') { %> const withLess = require('@zeit/next-less'); -module.exports = withLess({ +module.exports = withLess(withNx({ // Set this to true if you use CSS modules. // See: https://github.com/css-modules/css-modules cssModules: false -}); +})); <% } else if (style === 'styl') { %> const withStylus = require('@zeit/next-stylus'); -module.exports = withStylus({ +module.exports = withStylus(withNx({ // Set this to true if you use CSS modules. // See: https://github.com/css-modules/css-modules cssModules: false -}); +})); <% } else if ( style === 'styled-components' ||style === '@emotion/styled' || style === 'styled-jsx' || style === 'none' ) { %> -module.exports ={}; +module.exports = withNx({}); <% } else { // Defaults to CSS/SASS (which don't need plugin as of Next 9.3) %> -module.exports ={}; +module.exports = withNx({}); <% } %>