CSS Support Customization (#9502)

* CSS Support Customization

* Sort imports

* Correct PostCSS plugin loading

* Add css customization test

* Test "bad" css configuration

* Add load config test

* adjust spacing

* adjust spacing 2x

* Only allow config through JSON

* Support excluding false plugins

* Test tailwind css behavior

* Test plugin exclusion

* Fix unit test

* Fix config file

* Remove more variants

* Update test cases
This commit is contained in:
Joe Haddad 2019-11-25 16:52:29 -05:00 committed by GitHub
parent 8357f46413
commit 4656f52d15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 613 additions and 25 deletions

View file

@ -52,6 +52,7 @@
"clone": "2.1.2",
"coveralls": "3.0.3",
"cross-spawn": "6.0.5",
"escape-string-regexp": "2.0.0",
"eslint": "6.6.0",
"eslint-plugin-react": "7.16.0",
"eslint-plugin-react-hooks": "2.2.0",
@ -71,6 +72,8 @@
"node-fetch": "2.6.0",
"node-notifier": "5.4.0",
"node-sass": "4.12.0",
"postcss-short-size": "4.0.0",
"postcss-trolling": "0.1.7",
"pre-commit": "1.2.2",
"prettier": "1.19.1",
"react": "16.10.2",
@ -80,6 +83,7 @@
"request-promise-core": "1.1.2",
"rimraf": "2.6.3",
"shell-quote": "1.7.2",
"tailwindcss": "1.1.3",
"taskr": "1.1.0",
"tree-kill": "1.2.1",
"typescript": "3.7.2",

View file

@ -14,6 +14,7 @@ import {
PAGES_DIR_ALIAS,
} from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { findConfig } from '../lib/find-config'
import { resolveRequest } from '../lib/resolve-request'
import {
CLIENT_STATIC_FILES_RUNTIME_MAIN,
@ -89,6 +90,105 @@ function getOptimizedAliases(isServer: boolean): { [pkg: string]: string } {
}
}
async function getPostCssPlugins(dir: string): Promise<unknown[]> {
function load(plugins: { [key: string]: object | false }): unknown[] {
return Object.keys(plugins)
.map(pkg => {
const options = plugins[pkg]
if (options === false) {
return false
}
const pluginPath = resolveRequest(pkg, `${dir}/`)
if (options == null || Object.keys(options).length === 0) {
return require(pluginPath)
}
return require(pluginPath)(options)
})
.filter(Boolean)
}
const config = await findConfig<{ plugins: { [key: string]: object } }>(
dir,
'postcss'
)
let target: unknown[]
if (!config) {
target = load({
[require.resolve('postcss-flexbugs-fixes')]: {},
[require.resolve('postcss-preset-env')]: {
autoprefixer: {
// Disable legacy flexbox support
flexbox: 'no-2009',
},
// Enable CSS features that have shipped to the
// web platform, i.e. in 2+ browsers unflagged.
stage: 3,
},
})
} else {
const plugins = config.plugins
if (plugins == null || typeof plugins !== 'object') {
throw new Error(
`Your custom PostCSS configuration must export a \`plugins\` key.`
)
}
const invalidKey = Object.keys(config).find(key => key !== 'plugins')
if (invalidKey) {
console.warn(
`${chalk.yellow.bold(
'Warning'
)}: Your PostCSS configuration defines a field which is not supported (\`${invalidKey}\`). ` +
`Please remove this configuration value.`
)
}
// These plugins cannot be enabled by the user because they'll conflict with
// `css-loader`'s behavior to make us compatible with webpack.
;[
'postcss-modules-values',
'postcss-modules-scope',
'postcss-modules-extract-imports',
'postcss-modules-local-by-default',
].forEach(plugin => {
if (!plugins.hasOwnProperty(plugin)) {
return
}
console.warn(
`${chalk.yellow.bold('Warning')}: Please remove the ${chalk.underline(
plugin
)} plugin from your PostCSS configuration. ` +
`This plugin is automatically configured by Next.js.`
)
delete plugins[plugin]
})
// Next.js doesn't support CSS Modules yet. When we do, we should respect the
// options passed to this plugin (even though we need to remove the plugin
// itself).
if (plugins['postcss-modules']) {
delete plugins['postcss-modules']
console.warn(
`${chalk.yellow.bold(
'Warning'
)}: Next.js does not support CSS Modules (yet). The ${chalk.underline(
'postcss-modules'
)} plugin will have no effect.`
)
}
target = load(plugins as { [key: string]: object })
}
return target
}
export default async function getBaseWebpackConfig(
dir: string,
{
@ -325,12 +425,25 @@ export default async function getBaseWebpackConfig(
/node_modules[/\\]/.test(module.identifier())
)
},
name(module: { libIdent: Function }): string {
return crypto
.createHash('sha1')
.update(module.libIdent({ context: dir }))
.digest('hex')
.substring(0, 8)
name(module: {
type: string
libIdent?: Function
updateHash: (hash: crypto.Hash) => void
}): string {
const hash = crypto.createHash('sha1')
if (module.type === `css/mini-extract`) {
module.updateHash(hash)
} else {
if (!module.libIdent) {
throw new Error(
`Encountered unknown module type: ${module.type}. Please open an issue.`
)
}
hash.update(module.libIdent({ context: dir }))
}
return hash.digest('hex').substring(0, 8)
},
priority: 30,
minChunks: 1,
@ -387,6 +500,10 @@ export default async function getBaseWebpackConfig(
customAppFile = path.resolve(path.join(pagesDir, customAppFile))
}
const postCssPlugins: unknown[] = config.experimental.css
? await getPostCssPlugins(dir)
: []
let webpackConfig: webpack.Configuration = {
devtool,
mode: webpackMode,
@ -717,20 +834,7 @@ export default async function getBaseWebpackConfig(
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
// Make Flexbox behave like the spec cross-browser.
require('postcss-flexbugs-fixes'),
// Run Autoprefixer and compile new CSS features.
require('postcss-preset-env')({
autoprefixer: {
// Disable legacy flexbox support
flexbox: 'no-2009',
},
// Enable CSS features that have shipped to the
// web platform, i.e. in 2+ browsers unflagged.
stage: 3,
}),
],
plugins: postCssPlugins,
sourceMap: true,
},
},

View file

@ -0,0 +1,39 @@
import findUp from 'find-up'
import fs from 'fs'
import JSON5 from 'json5'
type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>
}
// We'll allow configuration to be typed, but we force everything provided to
// become optional. We do not perform any schema validation. We should maybe
// force all the types to be `unknown` as well.
export async function findConfig<T>(
directory: string,
key: string
): Promise<RecursivePartial<T> | null> {
// `package.json` configuration always wins. Let's check that first.
const packageJsonPath = await findUp('package.json', { cwd: directory })
if (packageJsonPath) {
const packageJson = require(packageJsonPath)
if (packageJson[key] != null && typeof packageJson[key] === 'object') {
return packageJson[key]
}
}
// If we didn't find the configuration in `package.json`, we should look for
// known filenames. The /rc$/ version of this file does not support YAML
// like some configuration loaders.
const filePath = await findUp(`.${key}rc.json`, {
cwd: directory,
})
if (filePath) {
// We load JSON contents with JSON5 to allow users to comment in their
// configuration file. This pattern was popularized by TypeScript.
const fileContents = fs.readFileSync(filePath, 'utf8')
return JSON5.parse(fileContents)
}
return null
}

View file

@ -96,6 +96,7 @@
"is-docker": "2.0.0",
"is-wsl": "2.1.1",
"jest-worker": "24.9.0",
"json5": "2.1.1",
"launch-editor": "2.2.1",
"loader-utils": "1.2.3",
"lru-cache": "5.1.1",
@ -153,6 +154,7 @@
"@types/etag": "1.8.0",
"@types/find-up": "2.1.1",
"@types/fresh": "0.5.0",
"@types/json5": "0.0.30",
"@types/loader-utils": "1.1.3",
"@types/lru-cache": "5.1.0",
"@types/mini-css-extract-plugin": "0.8.0",

View file

@ -0,0 +1,13 @@
{
"sourceMap": false,
"plugins": {
"postcss-modules-extract-imports": {},
"postcss-modules-local-by-default": {},
"postcss-modules-scope": {},
"postcss-modules-values": {},
"postcss-modules": {},
// Test a non-standard feature that wouldn't be normally enabled
"postcss-short-size": {}
}
}

View file

@ -0,0 +1,12 @@
import React from 'react'
import App from 'next/app'
import '../styles/global.css'
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
export default MyApp

View file

@ -0,0 +1,3 @@
export default function Home() {
return <div />
}

View file

@ -0,0 +1,4 @@
/* this should be transformed to width/height */
.video {
max-size: 400px 300px;
}

View file

@ -0,0 +1,11 @@
{
// Use comments to test JSON5 support
"plugins": {
// Test a non-standard feature that wouldn't be normally enabled
"postcss-short-size": {
// Add a prefix to test that configuration is passed
"prefix": "xyz"
},
"postcss-trolling": false
}
}

View file

@ -0,0 +1,12 @@
import React from 'react'
import App from 'next/app'
import '../styles/global.css'
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
export default MyApp

View file

@ -0,0 +1,3 @@
export default function Home() {
return <div />
}

View file

@ -0,0 +1,11 @@
/* this should pass through untransformed */
@media (480px <= width < 768px) {
::placeholder {
color: green;
}
}
/* this should be transformed to width/height */
.video {
-xyz-max-size: 400px 300px;
}

View file

@ -0,0 +1,7 @@
{
"postcss": {
"plugins": {
"tailwindcss": {}
}
}
}

View file

@ -0,0 +1,5 @@
import '../styles/global.css'
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

View file

@ -0,0 +1,3 @@
export default function Home() {
return <div />
}

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -15,6 +15,7 @@ import {
renderViaHTTP,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
import escapeStringRegexp from 'escape-string-regexp'
import cheerio from 'cheerio'
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
@ -218,6 +219,119 @@ describe('CSS Support', () => {
})
})
describe('CSS Customization', () => {
const appDir = join(fixturesDir, 'custom-configuration')
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(
`"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"`
)
// 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,gCACE,cACE,WACF,CACF,CAGA,OACE,eAA0B,CAA1B,gBACF",
"sourcesContent": Array [
"/* this should pass through untransformed */
@media (480px <= width < 768px) {
::placeholder {
color: green;
}
}
/* this should be transformed to width/height */
.video {
-xyz-max-size: 400px 300px;
}
",
],
"version": 3,
}
`)
})
})
describe('Bad CSS Customization', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should build successfully', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
expect(stderr).toMatch(/field which is not supported.*?sourceMap/)
;[
'postcss-modules-values',
'postcss-modules-scope',
'postcss-modules-extract-imports',
'postcss-modules-local-by-default',
].forEach(plugin => {
expect(stderr).toMatch(
new RegExp(`Please remove the.*?${escapeStringRegexp(plugin)}`)
)
})
})
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(`".video{max-width:400px;max-height:300px}"`)
// 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)
})
})
// Tests css ordering
describe('Multi Global Support (reversed)', () => {
const appDir = join(fixturesDir, 'multi-global-reversed')
@ -649,4 +763,41 @@ describe('CSS Support', () => {
}
})
})
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)
})
})
})

View file

@ -0,0 +1,28 @@
/* eslint-env jest */
import { findConfig } from 'next/dist/lib/find-config'
import { join } from 'path'
const fixtureDir = join(__dirname, 'fixtures')
describe('find config', () => {
it('should resolve rc.json', async () => {
const config = await findConfig(join(fixtureDir, 'config-json'), 'test')
expect(config).toEqual({ foo: 'bar' })
})
it('should resolve package.json', async () => {
const config = await findConfig(
join(fixtureDir, 'config-package-json'),
'test'
)
expect(config).toEqual({ foo: 'bar' })
})
it('should resolve down', async () => {
const config = await findConfig(
join(fixtureDir, 'config-down/one/two/three/'),
'test'
)
expect(config).toEqual({ foo: 'bar' })
})
})

View file

@ -0,0 +1 @@
{ "foo": "bar" }

View file

@ -0,0 +1 @@
{ "foo": "bar" }

View file

@ -0,0 +1,5 @@
{
"test": {
"foo": "bar"
}
}

176
yarn.lock
View file

@ -2644,6 +2644,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
"@types/json5@0.0.30":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818"
integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==
"@types/loader-utils@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-1.1.3.tgz#82b9163f2ead596c68a8c03e450fbd6e089df401"
@ -3724,6 +3729,19 @@ autodll-webpack-plugin@0.4.2:
webpack-merge "^4.1.0"
webpack-sources "^1.0.1"
autoprefixer@^9.4.5:
version "9.7.2"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.2.tgz#26cf729fbb709323b40171a874304884dcceffed"
integrity sha512-LCAfcdej1182uVvPOZnytbq61AhnOZ/4JelDaJGDeNwewyU1AMaNthcHsyz1NRjTmd2FkurMckLWfkHg3Z//KA==
dependencies:
browserslist "^4.7.3"
caniuse-lite "^1.0.30001010"
chalk "^2.4.2"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^7.0.23"
postcss-value-parser "^4.0.2"
autoprefixer@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47"
@ -4136,7 +4154,7 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.3, browserslist@^4.6.4, browserslist@^4.7.1:
browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.3, browserslist@^4.6.4, browserslist@^4.7.1, browserslist@^4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.3.tgz#02341f162b6bcc1e1028e30624815d4924442dc3"
integrity sha512-jWvmhqYpx+9EZm/FxcZSbUZyDEvDTLDi3nSAKbzEkyWvtI0mNSmUosey+5awDW1RUlrgXbQb5A6qY1xQH9U6MQ==
@ -4232,7 +4250,7 @@ bytes@3.0.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
bytes@3.1.0:
bytes@3.1.0, bytes@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
@ -4343,6 +4361,11 @@ camel-case@^3.0.0:
no-case "^2.2.0"
upper-case "^1.1.1"
camelcase-css@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@ -6228,6 +6251,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escape-string-regexp@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escodegen@^1.9.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510"
@ -7103,6 +7131,15 @@ fs-extra@7.0.1, fs-extra@^7.0.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-minipass@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
@ -7640,6 +7677,11 @@ has-ansi@^2.0.0:
dependencies:
ansi-regex "^2.0.0"
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@ -9200,7 +9242,7 @@ jest-worker@^24.6.0:
merge-stream "^1.0.1"
supports-color "^6.1.0"
js-base64@^2.1.8:
js-base64@^2.1.8, js-base64@^2.1.9:
version "2.5.1"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
@ -9315,6 +9357,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6"
integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
dependencies:
minimist "^1.2.0"
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
@ -9805,6 +9854,11 @@ lodash.templatesettings@^4.0.0:
dependencies:
lodash._reinterpolate "~3.0.0"
lodash.toarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=
lodash.unescape@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
@ -10534,6 +10588,13 @@ no-case@^2.2.0, no-case@^2.3.2:
dependencies:
lower-case "^1.1.1"
node-emoji@^1.8.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==
dependencies:
lodash.toarray "^4.4.0"
node-fetch-npm@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
@ -10775,6 +10836,11 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.3.0.tgz#9c49e10fc1876aeb76dba88bf1b2b5d9fa57b2ee"
integrity sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==
normalize.css@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
npm-bundled@^1.0.1:
version "1.0.6"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
@ -11890,6 +11956,16 @@ postcss-font-variant@^4.0.0:
dependencies:
postcss "^7.0.2"
postcss-functions@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e"
integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=
dependencies:
glob "^7.1.2"
object-assign "^4.1.1"
postcss "^6.0.9"
postcss-value-parser "^3.3.0"
postcss-gap-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715"
@ -11913,6 +11989,14 @@ postcss-initial@^3.0.0:
lodash.template "^4.5.0"
postcss "^7.0.2"
postcss-js@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9"
integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==
dependencies:
camelcase-css "^2.0.1"
postcss "^7.0.18"
postcss-lab-function@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e"
@ -12080,6 +12164,14 @@ postcss-modules-values@^3.0.0:
icss-utils "^4.0.0"
postcss "^7.0.6"
postcss-nested@^4.1.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.1.tgz#4bc2e5b35e3b1e481ff81e23b700da7f82a8b248"
integrity sha512-AMayXX8tS0HCp4O4lolp4ygj9wBn32DJWXvG6gCv+ZvJrEa00GUxJcJEEzMh87BIe6FrWdYkpR2cuyqHKrxmXw==
dependencies:
postcss "^7.0.21"
postcss-selector-parser "^6.0.2"
postcss-nesting@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052"
@ -12320,6 +12412,13 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
indexes-of "^1.0.1"
uniq "^1.0.1"
postcss-short-size@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/postcss-short-size/-/postcss-short-size-4.0.0.tgz#66da8f2d816b2a7334abcc951f67fb12d394b219"
integrity sha512-/RRw2JFj+7xmX+GO5HIp9+sVNZTm+bXWKwHlxxXcxEJDPng/dNeWGXIdExbuknqgDeDX/lv5YfgjD2g5TUtUtA==
dependencies:
postcss "^7.0.5"
postcss-svgo@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
@ -12330,6 +12429,14 @@ postcss-svgo@^4.0.2:
postcss-value-parser "^3.0.0"
svgo "^1.0.0"
postcss-trolling@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/postcss-trolling/-/postcss-trolling-0.1.7.tgz#26a08dee0bac2a6fbc50578101976264f62ec8bb"
integrity sha1-JqCN7gusKm+8UFeBAZdiZPYuyLs=
dependencies:
object-assign "^4.0.1"
postcss "^5.0.10"
postcss-unique-selectors@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
@ -12344,7 +12451,7 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
postcss-value-parser@^4.0.0:
postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9"
integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==
@ -12358,7 +12465,17 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
postcss@^6.0.1, postcss@^6.0.23:
postcss@^5.0.10:
version "5.2.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==
dependencies:
chalk "^1.1.3"
js-base64 "^2.1.9"
source-map "^0.5.6"
supports-color "^3.2.3"
postcss@^6.0.1, postcss@^6.0.23, postcss@^6.0.9:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
@ -12376,6 +12493,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5:
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^7.0.11, postcss@^7.0.21, postcss@^7.0.23:
version "7.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.23.tgz#9f9759fad661b15964f3cfc3140f66f1e05eadc1"
integrity sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.18, postcss@^7.0.2, postcss@^7.0.6:
version "7.0.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233"
@ -12429,6 +12555,11 @@ pretty-format@^24.8.0:
ansi-styles "^3.2.0"
react-is "^16.8.4"
pretty-hrtime@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
private@^0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@ -12944,6 +13075,14 @@ redent@^2.0.0:
indent-string "^3.0.0"
strip-indent "^2.0.0"
reduce-css-calc@^2.1.6:
version "2.1.7"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2"
integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==
dependencies:
css-unit-converter "^1.1.1"
postcss-value-parser "^3.3.0"
reflect.ownkeys@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
@ -14342,6 +14481,13 @@ supports-color@^2.0.0:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
dependencies:
has-flag "^1.0.0"
supports-color@^5.3.0, supports-color@^5.4.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@ -14417,6 +14563,26 @@ tagged-versions@1.3.0:
child-process-promise "^2.1.3"
semver "^5.3.0"
tailwindcss@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.1.3.tgz#ad154f78e1e44060e32e3ed44b27287c2be126a6"
integrity sha512-8sa/QO+blnu3WXUylsgvYZlUbBpVH36QeGuZxgSGqp1dF3g4AGe1azt8FsO8i8Hfe9Oyvwhx3iSjRDak3nngeQ==
dependencies:
autoprefixer "^9.4.5"
bytes "^3.0.0"
chalk "^2.4.1"
fs-extra "^8.0.0"
lodash "^4.17.11"
node-emoji "^1.8.1"
normalize.css "^8.0.1"
postcss "^7.0.11"
postcss-functions "^3.0.0"
postcss-js "^2.0.0"
postcss-nested "^4.1.1"
postcss-selector-parser "^6.0.0"
pretty-hrtime "^1.0.3"
reduce-css-calc "^2.1.6"
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"