Skip to content

Commit

Permalink
Extract CSS Loaders into Separate Files (#10210)
Browse files Browse the repository at this point in the history
  • Loading branch information
Timer authored and ijjk committed Jan 22, 2020
1 parent 6988a2e commit f67f99a
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 124 deletions.
128 changes: 4 additions & 124 deletions packages/next/build/webpack/config/blocks/css/index.ts
@@ -1,10 +1,10 @@
import curry from 'lodash.curry'
import path from 'path'
import webpack, { Configuration } from 'webpack'
import { Configuration } from 'webpack'
import MiniCssExtractPlugin from '../../../plugins/mini-css-extract-plugin'
import { loader, plugin } from '../../helpers'
import { ConfigurationContext, ConfigurationFn, pipe } from '../../utils'
import { getCssModuleLocalIdent } from './getCssModuleLocalIdent'
import { getCssModuleLoader, getGlobalCssLoader } from './loaders'
import {
getCustomDocumentError,
getGlobalImportError,
Expand All @@ -18,62 +18,6 @@ const regexCssAll = /\.css$/
const regexCssGlobal = /(?<!\.module)\.css$/
const regexCssModules = /\.module\.css$/

function getClientStyleLoader({
isDevelopment,
assetPrefix,
}: {
isDevelopment: boolean
assetPrefix: string
}): webpack.RuleSetUseItem {
return isDevelopment
? {
loader: require.resolve('style-loader'),
options: {
// By default, style-loader injects CSS into the bottom
// of <head>. This causes ordering problems between dev
// and prod. To fix this, we render a <noscript> tag as
// an anchor for the styles to be placed before. These
// styles will be applied _before_ <style jsx global>.
insert: function(element: Node) {
// These elements should always exist. If they do not,
// this code should fail.
var anchorElement = document.querySelector(
'#__next_css__DO_NOT_USE__'
)!
var parentNode = anchorElement.parentNode! // Normally <head>

// Each style tag should be placed right before our
// anchor. By inserting before and not after, we do not
// need to track the last inserted element.
parentNode.insertBefore(element, anchorElement)

// Remember: this is development only code.
//
// After styles are injected, we need to remove the
// <style> tags that set `body { display: none; }`.
//
// We use `requestAnimationFrame` as a way to defer
// this operation since there may be multiple style
// tags.
;(self.requestAnimationFrame || setTimeout)(function() {
for (
var x = document.querySelectorAll('[data-next-hide-fouc]'),
i = x.length;
i--;

) {
x[i].parentNode!.removeChild(x[i])
}
})
},
},
}
: {
loader: MiniCssExtractPlugin.loader,
options: { publicPath: `${assetPrefix}/_next/` },
}
}

export const css = curry(async function css(
enabled: boolean,
ctx: ConfigurationContext,
Expand Down Expand Up @@ -145,47 +89,7 @@ export const css = curry(async function css(
include: [ctx.rootDirectory],
exclude: /node_modules/,
},

use: ([
// Add appropriate development more or production mode style
// loader
ctx.isClient &&
getClientStyleLoader({
isDevelopment: ctx.isDevelopment,
assetPrefix: ctx.assetPrefix,
}),

// Resolve CSS `@import`s and `url()`s
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
sourceMap: true,
onlyLocals: ctx.isServer,
modules: {
// Disallow global style exports so we can code-split CSS and
// not worry about loading order.
mode: 'pure',
// Generate a friendly production-ready name so it's
// reasonably understandable. The same name is used for
// development.
// TODO: Consider making production reduce this to a single
// character?
getLocalIdent: getCssModuleLocalIdent,
},
},
},

// Compile CSS
{
loader: require.resolve('postcss-loader'),
options: {
ident: '__nextjs_postcss',
plugins: postCssPlugins,
sourceMap: true,
},
},
] as webpack.RuleSetUseItem[]).filter(Boolean),
use: getCssModuleLoader(ctx, postCssPlugins),
},
],
})
Expand Down Expand Up @@ -228,31 +132,7 @@ export const css = curry(async function css(
sideEffects: true,
test: regexCssGlobal,
issuer: { include: ctx.customAppFile },

use: [
// Add appropriate development more or production mode style
// loader
getClientStyleLoader({
isDevelopment: ctx.isDevelopment,
assetPrefix: ctx.assetPrefix,
}),

// Resolve CSS `@import`s and `url()`s
{
loader: require.resolve('css-loader'),
options: { importLoaders: 1, sourceMap: true },
},

// Compile CSS
{
loader: require.resolve('postcss-loader'),
options: {
ident: '__nextjs_postcss',
plugins: postCssPlugins,
sourceMap: true,
},
},
],
use: getGlobalCssLoader(ctx, postCssPlugins),
},
],
})
Expand Down
58 changes: 58 additions & 0 deletions packages/next/build/webpack/config/blocks/css/loaders/client.ts
@@ -0,0 +1,58 @@
import webpack from 'webpack'
import MiniCssExtractPlugin from '../../../../plugins/mini-css-extract-plugin'

export function getClientStyleLoader({
isDevelopment,
assetPrefix,
}: {
isDevelopment: boolean
assetPrefix: string
}): webpack.RuleSetUseItem {
return isDevelopment
? {
loader: require.resolve('style-loader'),
options: {
// By default, style-loader injects CSS into the bottom
// of <head>. This causes ordering problems between dev
// and prod. To fix this, we render a <noscript> tag as
// an anchor for the styles to be placed before. These
// styles will be applied _before_ <style jsx global>.
insert: function(element: Node) {
// These elements should always exist. If they do not,
// this code should fail.
var anchorElement = document.querySelector(
'#__next_css__DO_NOT_USE__'
)!
var parentNode = anchorElement.parentNode! // Normally <head>

// Each style tag should be placed right before our
// anchor. By inserting before and not after, we do not
// need to track the last inserted element.
parentNode.insertBefore(element, anchorElement)

// Remember: this is development only code.
//
// After styles are injected, we need to remove the
// <style> tags that set `body { display: none; }`.
//
// We use `requestAnimationFrame` as a way to defer
// this operation since there may be multiple style
// tags.
;(self.requestAnimationFrame || setTimeout)(function() {
for (
var x = document.querySelectorAll('[data-next-hide-fouc]'),
i = x.length;
i--;

) {
x[i].parentNode!.removeChild(x[i])
}
})
},
},
}
: {
loader: MiniCssExtractPlugin.loader,
options: { publicPath: `${assetPrefix}/_next/` },
}
}
40 changes: 40 additions & 0 deletions packages/next/build/webpack/config/blocks/css/loaders/global.ts
@@ -0,0 +1,40 @@
import postcss from 'postcss'
import webpack from 'webpack'
import { ConfigurationContext } from '../../../utils'
import { getClientStyleLoader } from './client'

export function getGlobalCssLoader(
ctx: ConfigurationContext,
postCssPlugins: postcss.AcceptedPlugin[]
): webpack.RuleSetUseItem[] {
const loaders: webpack.RuleSetUseItem[] = []

if (ctx.isClient) {
// Add appropriate development more or production mode style
// loader
loaders.push(
getClientStyleLoader({
isDevelopment: ctx.isDevelopment,
assetPrefix: ctx.assetPrefix,
})
)
}

// Resolve CSS `@import`s and `url()`s
loaders.push({
loader: require.resolve('css-loader'),
options: { importLoaders: 1, sourceMap: true },
})

// Compile CSS
loaders.push({
loader: require.resolve('postcss-loader'),
options: {
ident: '__nextjs_postcss',
plugins: postCssPlugins,
sourceMap: true,
},
})

return loaders
}
@@ -0,0 +1,2 @@
export * from './global'
export * from './modules'
56 changes: 56 additions & 0 deletions packages/next/build/webpack/config/blocks/css/loaders/modules.ts
@@ -0,0 +1,56 @@
import postcss from 'postcss'
import webpack from 'webpack'
import { ConfigurationContext } from '../../../utils'
import { getClientStyleLoader } from './client'
import { getCssModuleLocalIdent } from './getCssModuleLocalIdent'

export function getCssModuleLoader(
ctx: ConfigurationContext,
postCssPlugins: postcss.AcceptedPlugin[]
): webpack.RuleSetUseItem[] {
const loaders: webpack.RuleSetUseItem[] = []

if (ctx.isClient) {
// Add appropriate development more or production mode style
// loader
loaders.push(
getClientStyleLoader({
isDevelopment: ctx.isDevelopment,
assetPrefix: ctx.assetPrefix,
})
)
}

// Resolve CSS `@import`s and `url()`s
loaders.push({
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
sourceMap: true,
onlyLocals: ctx.isServer,
modules: {
// Disallow global style exports so we can code-split CSS and
// not worry about loading order.
mode: 'pure',
// Generate a friendly production-ready name so it's
// reasonably understandable. The same name is used for
// development.
// TODO: Consider making production reduce this to a single
// character?
getLocalIdent: getCssModuleLocalIdent,
},
},
})

// Compile CSS
loaders.push({
loader: require.resolve('postcss-loader'),
options: {
ident: '__nextjs_postcss',
plugins: postCssPlugins,
sourceMap: true,
},
})

return loaders
}

0 comments on commit f67f99a

Please sign in to comment.