]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): built-in support for css modules
authorEvan You <yyx990803@gmail.com>
Fri, 24 Apr 2020 13:59:52 +0000 (09:59 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 24 Apr 2020 13:59:52 +0000 (09:59 -0400)
packages/compiler-sfc/__tests__/compileStyle.spec.ts
packages/compiler-sfc/package.json
packages/compiler-sfc/src/compileStyle.ts
yarn.lock

index b4af3c0972db43a70fa8881fd8604cbbaa45f1a2..08d50c1173d00b026f58064ba832fb725b10f7d3 100644 (file)
@@ -1,70 +1,76 @@
-import { compileStyle } from '../src/compileStyle'
+import { compileStyle, compileStyleAsync } from '../src/compileStyle'
 import { mockWarn } from '@vue/shared'
 
-function compile(source: string): string {
-  const res = compileStyle({
-    source,
-    filename: 'test.css',
-    id: 'test'
-  })
-  if (res.errors.length) {
-    res.errors.forEach(err => {
-      console.error(err)
-    })
-    expect(res.errors.length).toBe(0)
-  }
-  return res.code
-}
-
 describe('SFC scoped CSS', () => {
   mockWarn()
 
+  function compileScoped(source: string): string {
+    const res = compileStyle({
+      source,
+      filename: 'test.css',
+      id: 'test',
+      scoped: true
+    })
+    if (res.errors.length) {
+      res.errors.forEach(err => {
+        console.error(err)
+      })
+      expect(res.errors.length).toBe(0)
+    }
+    return res.code
+  }
+
   test('simple selectors', () => {
-    expect(compile(`h1 { color: red; }`)).toMatch(`h1[test] { color: red;`)
-    expect(compile(`.foo { color: red; }`)).toMatch(`.foo[test] { color: red;`)
+    expect(compileScoped(`h1 { color: red; }`)).toMatch(
+      `h1[test] { color: red;`
+    )
+    expect(compileScoped(`.foo { color: red; }`)).toMatch(
+      `.foo[test] { color: red;`
+    )
   })
 
   test('descendent selector', () => {
-    expect(compile(`h1 .foo { color: red; }`)).toMatch(
+    expect(compileScoped(`h1 .foo { color: red; }`)).toMatch(
       `h1 .foo[test] { color: red;`
     )
   })
 
   test('multiple selectors', () => {
-    expect(compile(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
+    expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
       `h1 .foo[test], .bar[test], .baz[test] { color: red;`
     )
   })
 
   test('pseudo class', () => {
-    expect(compile(`.foo:after { color: red; }`)).toMatch(
+    expect(compileScoped(`.foo:after { color: red; }`)).toMatch(
       `.foo[test]:after { color: red;`
     )
   })
 
   test('pseudo element', () => {
-    expect(compile(`::selection { display: none; }`)).toMatch(
+    expect(compileScoped(`::selection { display: none; }`)).toMatch(
       '[test]::selection {'
     )
   })
 
   test('spaces before pseudo element', () => {
-    const code = compile(`.abc, ::selection { color: red; }`)
+    const code = compileScoped(`.abc, ::selection { color: red; }`)
     expect(code).toMatch('.abc[test],')
     expect(code).toMatch('[test]::selection {')
   })
 
   test('::v-deep', () => {
-    expect(compile(`::v-deep(.foo) { color: red; }`)).toMatchInlineSnapshot(`
+    expect(compileScoped(`::v-deep(.foo) { color: red; }`))
+      .toMatchInlineSnapshot(`
       "[test] .foo { color: red;
       }"
     `)
-    expect(compile(`::v-deep(.foo .bar) { color: red; }`))
+    expect(compileScoped(`::v-deep(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       "[test] .foo .bar { color: red;
       }"
     `)
-    expect(compile(`.baz .qux ::v-deep(.foo .bar) { color: red; }`))
+    expect(compileScoped(`.baz .qux ::v-deep(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       ".baz .qux[test] .foo .bar { color: red;
       }"
@@ -72,16 +78,17 @@ describe('SFC scoped CSS', () => {
   })
 
   test('::v-slotted', () => {
-    expect(compile(`::v-slotted(.foo) { color: red; }`)).toMatchInlineSnapshot(`
+    expect(compileScoped(`::v-slotted(.foo) { color: red; }`))
+      .toMatchInlineSnapshot(`
       ".foo[test-s] { color: red;
       }"
     `)
-    expect(compile(`::v-slotted(.foo .bar) { color: red; }`))
+    expect(compileScoped(`::v-slotted(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       ".foo .bar[test-s] { color: red;
       }"
     `)
-    expect(compile(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`))
+    expect(compileScoped(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       ".baz .qux .foo .bar[test-s] { color: red;
       }"
@@ -89,17 +96,18 @@ describe('SFC scoped CSS', () => {
   })
 
   test('::v-global', () => {
-    expect(compile(`::v-global(.foo) { color: red; }`)).toMatchInlineSnapshot(`
+    expect(compileScoped(`::v-global(.foo) { color: red; }`))
+      .toMatchInlineSnapshot(`
       ".foo { color: red;
       }"
     `)
-    expect(compile(`::v-global(.foo .bar) { color: red; }`))
+    expect(compileScoped(`::v-global(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       ".foo .bar { color: red;
       }"
     `)
     // global ignores anything before it
-    expect(compile(`.baz .qux ::v-global(.foo .bar) { color: red; }`))
+    expect(compileScoped(`.baz .qux ::v-global(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
       ".foo .bar { color: red;
       }"
@@ -107,7 +115,7 @@ describe('SFC scoped CSS', () => {
   })
 
   test('media query', () => {
-    expect(compile(`@media print { .foo { color: red }}`))
+    expect(compileScoped(`@media print { .foo { color: red }}`))
       .toMatchInlineSnapshot(`
       "@media print {
       .foo[test] { color: red
@@ -116,7 +124,7 @@ describe('SFC scoped CSS', () => {
   })
 
   test('supports query', () => {
-    expect(compile(`@supports(display: grid) { .foo { display: grid }}`))
+    expect(compileScoped(`@supports(display: grid) { .foo { display: grid }}`))
       .toMatchInlineSnapshot(`
       "@supports(display: grid) {
       .foo[test] { display: grid
@@ -125,7 +133,7 @@ describe('SFC scoped CSS', () => {
   })
 
   test('scoped keyframes', () => {
-    const style = compile(`
+    const style = compileScoped(`
 .anim {
   animation: color 5s infinite, other 5s;
 }
@@ -184,13 +192,7 @@ describe('SFC scoped CSS', () => {
 
   // vue-loader/#1370
   test('spaces after selector', () => {
-    const { code } = compileStyle({
-      source: `.foo , .bar { color: red; }`,
-      filename: 'test.css',
-      id: 'test'
-    })
-
-    expect(code).toMatchInlineSnapshot(`
+    expect(compileScoped(`.foo , .bar { color: red; }`)).toMatchInlineSnapshot(`
       ".foo[test], .bar[test] { color: red;
       }"
     `)
@@ -198,11 +200,12 @@ describe('SFC scoped CSS', () => {
 
   describe('deprecated syntax', () => {
     test('::v-deep as combinator', () => {
-      expect(compile(`::v-deep .foo { color: red; }`)).toMatchInlineSnapshot(`
+      expect(compileScoped(`::v-deep .foo { color: red; }`))
+        .toMatchInlineSnapshot(`
         "[test] .foo { color: red;
         }"
       `)
-      expect(compile(`.bar ::v-deep .foo { color: red; }`))
+      expect(compileScoped(`.bar ::v-deep .foo { color: red; }`))
         .toMatchInlineSnapshot(`
         ".bar[test] .foo { color: red;
         }"
@@ -213,7 +216,7 @@ describe('SFC scoped CSS', () => {
     })
 
     test('>>> (deprecated syntax)', () => {
-      const code = compile(`>>> .foo { color: red; }`)
+      const code = compileScoped(`>>> .foo { color: red; }`)
       expect(code).toMatchInlineSnapshot(`
         "[test] .foo { color: red;
         }"
@@ -224,7 +227,7 @@ describe('SFC scoped CSS', () => {
     })
 
     test('/deep/ (deprecated syntax)', () => {
-      const code = compile(`/deep/ .foo { color: red; }`)
+      const code = compileScoped(`/deep/ .foo { color: red; }`)
       expect(code).toMatchInlineSnapshot(`
         "[test] .foo { color: red;
         }"
@@ -235,3 +238,35 @@ describe('SFC scoped CSS', () => {
     })
   })
 })
+
+describe('SFC CSS modules', () => {
+  test('should include resulting classes object in result', async () => {
+    const result = await compileStyleAsync({
+      source: `.red { color: red }\n.green { color: green }\n:global(.blue) { color: blue }`,
+      filename: `test.css`,
+      id: 'test',
+      modules: true
+    })
+    expect(result.modules).toBeDefined()
+    expect(result.modules!.red).toMatch('_red_')
+    expect(result.modules!.green).toMatch('_green_')
+    expect(result.modules!.blue).toBeUndefined()
+  })
+
+  test('postcss-modules options', async () => {
+    const result = await compileStyleAsync({
+      source: `:local(.foo-bar) { color: red }\n.baz-qux { color: green }`,
+      filename: `test.css`,
+      id: 'test',
+      modules: true,
+      modulesOptions: {
+        scopeBehaviour: 'global',
+        generateScopedName: `[name]__[local]__[hash:base64:5]`,
+        localsConvention: 'camelCaseOnly'
+      }
+    })
+    expect(result.modules).toBeDefined()
+    expect(result.modules!.fooBar).toMatch('__foo-bar__')
+    expect(result.modules!.bazQux).toBeUndefined()
+  })
+})
index 906a1a7934332354bf1afe8de93d5d3ba2615dbe..9fa76a1e13f9fb6aaccdef403dddd2a240a967e3 100644 (file)
     "vue": "3.0.0-beta.3"
   },
   "dependencies": {
-    "@vue/shared": "3.0.0-beta.3",
     "@vue/compiler-core": "3.0.0-beta.3",
     "@vue/compiler-dom": "3.0.0-beta.3",
     "@vue/compiler-ssr": "3.0.0-beta.3",
+    "@vue/shared": "3.0.0-beta.3",
     "consolidate": "^0.15.1",
     "hash-sum": "^2.0.0",
     "lru-cache": "^5.1.1",
     "merge-source-map": "^1.1.0",
-    "postcss": "^7.0.21",
+    "postcss": "^7.0.27",
+    "postcss-modules": "^2.0.0",
     "postcss-selector-parser": "^6.0.2",
     "source-map": "^0.6.1"
   },
index a5c513319f04da4867e6abfff3c86ef9ca3acebb..c0a510c0b5aaf5705f184b2b52696717d8730e22 100644 (file)
@@ -25,6 +25,20 @@ export interface SFCStyleCompileOptions {
 
 export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
   isAsync?: boolean
+  // css modules support, note this requires async so that we can get the
+  // resulting json
+  modules?: boolean
+  // maps to postcss-modules options
+  // https://github.com/css-modules/postcss-modules
+  modulesOptions?: {
+    scopeBehaviour?: 'global' | 'local'
+    globalModulePaths?: string[]
+    generateScopedName?:
+      | string
+      | ((name: string, filename: string, css: string) => string)
+    hashPrefix?: string
+    localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly'
+  }
 }
 
 export interface SFCStyleCompileResults {
@@ -32,6 +46,7 @@ export interface SFCStyleCompileResults {
   map: RawSourceMap | undefined
   rawResult: LazyResult | Result | undefined
   errors: Error[]
+  modules?: Record<string, string>
 }
 
 export function compileStyle(
@@ -44,7 +59,7 @@ export function compileStyle(
 }
 
 export function compileStyleAsync(
-  options: SFCStyleCompileOptions
+  options: SFCAsyncStyleCompileOptions
 ): Promise<SFCStyleCompileResults> {
   return doCompileStyle({ ...options, isAsync: true }) as Promise<
     SFCStyleCompileResults
@@ -57,8 +72,10 @@ export function doCompileStyle(
   const {
     filename,
     id,
-    scoped = true,
+    scoped = false,
     trim = true,
+    modules = false,
+    modulesOptions = {},
     preprocessLang,
     postcssOptions,
     postcssPlugins
@@ -75,6 +92,23 @@ export function doCompileStyle(
   if (scoped) {
     plugins.push(scopedPlugin(id))
   }
+  let cssModules: Record<string, string> | undefined
+  if (modules) {
+    if (options.isAsync) {
+      plugins.push(
+        require('postcss-modules')({
+          ...modulesOptions,
+          getJSON: (cssFileName: string, json: Record<string, string>) => {
+            cssModules = json
+          }
+        })
+      )
+    } else {
+      throw new Error(
+        '`modules` option can only be used with compileStyleAsync().'
+      )
+    }
+  }
 
   const postCSSOptions: ProcessOptions = {
     ...postcssOptions,
@@ -108,6 +142,7 @@ export function doCompileStyle(
           code: result.css || '',
           map: result.map && (result.map.toJSON() as any),
           errors,
+          modules: cssModules,
           rawResult: result
         }))
         .catch(error => ({
index 2fb512b69af40697084aa0227fd6044ccd319b2b..030f655fff61f02024e952ed77bc5c56f37ac94f 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -1319,6 +1319,11 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
+big.js@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
 bluebird@^3.1.1:
   version "3.7.1"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
@@ -2044,6 +2049,27 @@ crypto-random-string@^1.0.0:
   resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
   integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
 
+css-modules-loader-core@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16"
+  integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=
+  dependencies:
+    icss-replace-symbols "1.1.0"
+    postcss "6.0.1"
+    postcss-modules-extract-imports "1.1.0"
+    postcss-modules-local-by-default "1.2.0"
+    postcss-modules-scope "1.1.0"
+    postcss-modules-values "1.3.0"
+
+css-selector-tokenizer@^0.7.0:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz#11e5e27c9a48d90284f22d45061c303d7a25ad87"
+  integrity sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==
+  dependencies:
+    cssesc "^3.0.0"
+    fastparse "^1.1.2"
+    regexpu-core "^4.6.0"
+
 cssesc@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@@ -2281,6 +2307,11 @@ emoji-regex@^8.0.0:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
   integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
 
+emojis-list@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
 end-of-stream@^1.1.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -2596,6 +2627,11 @@ fast-url-parser@1.1.3:
   dependencies:
     punycode "^1.3.2"
 
+fastparse@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
+  integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+
 fastq@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2"
@@ -2747,6 +2783,13 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
+generic-names@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872"
+  integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==
+  dependencies:
+    loader-utils "^1.1.0"
+
 gensync@^1.0.0-beta.1:
   version "1.0.0-beta.1"
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@@ -2983,6 +3026,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"
@@ -3094,6 +3142,11 @@ iconv-lite@0.4.24:
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
+icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+  integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+
 ignore@^4.0.3:
   version "4.0.6"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@@ -4015,6 +4068,11 @@ jsesc@^2.5.1:
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
   integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
 
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
 json-parse-better-errors@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -4042,6 +4100,13 @@ json5@2.x, json5@^2.1.0, json5@^2.1.2:
   dependencies:
     minimist "^1.2.5"
 
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
 jsonfile@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -4216,6 +4281,15 @@ load-json-file@^4.0.0:
     pify "^3.0.0"
     strip-bom "^3.0.0"
 
+loader-utils@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
+  integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^1.0.1"
+
 locate-path@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -4244,6 +4318,11 @@ lodash._reinterpolate@^3.0.0:
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
   integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
 
+lodash.camelcase@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+  integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
+
 lodash.get@^4.0.0:
   version "4.4.2"
   resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -5160,6 +5239,48 @@ posix-character-classes@^0.1.0:
   resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
+postcss-modules-extract-imports@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb"
+  integrity sha1-thTJcgvmgW6u41+zpfqh26agXds=
+  dependencies:
+    postcss "^6.0.1"
+
+postcss-modules-local-by-default@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+  integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-scope@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+  integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-values@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+  integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
+  dependencies:
+    icss-replace-symbols "^1.1.0"
+    postcss "^6.0.1"
+
+postcss-modules@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-2.0.0.tgz#473d0d7326651d8408585c2a154115d5cb36cce0"
+  integrity sha512-eqp+Bva+U2cwQO7dECJ8/V+X+uH1HduNeITB0CPPFAu6d/8LKQ32/j+p9rQ2YL1QytVcrNU0X+fBqgGmQIA1Rw==
+  dependencies:
+    css-modules-loader-core "^1.1.0"
+    generic-names "^2.0.1"
+    lodash.camelcase "^4.3.0"
+    postcss "^7.0.1"
+    string-hash "^1.1.1"
+
 postcss-selector-parser@^6.0.2:
   version "6.0.2"
   resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
@@ -5169,7 +5290,25 @@ postcss-selector-parser@^6.0.2:
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
-postcss@^7.0.21:
+postcss@6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2"
+  integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=
+  dependencies:
+    chalk "^1.1.3"
+    source-map "^0.5.6"
+    supports-color "^3.2.3"
+
+postcss@^6.0.1:
+  version "6.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+  integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
+postcss@^7.0.1, postcss@^7.0.27:
   version "7.0.27"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
   integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
@@ -5531,6 +5670,18 @@ redent@^2.0.0:
     indent-string "^3.0.0"
     strip-indent "^2.0.0"
 
+regenerate-unicode-properties@^8.2.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
+  integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
 regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
@@ -5544,6 +5695,18 @@ regex-not@^1.0.0, regex-not@^1.0.2:
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
+regexpu-core@^4.6.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
+  integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.2.0"
+    regjsgen "^0.5.1"
+    regjsparser "^0.6.4"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.2.0"
+
 registry-auth-token@3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
@@ -5567,6 +5730,18 @@ registry-url@3.1.0, registry-url@^3.0.3:
   dependencies:
     rc "^1.0.1"
 
+regjsgen@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
+  integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
+
+regjsparser@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
+  integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==
+  dependencies:
+    jsesc "~0.5.0"
+
 remove-trailing-separator@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@@ -6136,6 +6311,11 @@ string-argv@^0.3.0:
   resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.0.tgz#0ea99e7257fea5e97a1bfcdfc19cf12d68e6ec6a"
   integrity sha512-NGZHq3nkSXVtGZXTBjFru3MNfoZyIzN25T7BmvdgnSC0LCJczAGLLMQLyjywSIaAoqSemgLzBRHOsnrHbt60+Q==
 
+string-hash@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
+  integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=
+
 string-length@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837"
@@ -6294,7 +6474,14 @@ 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@^5.3.0:
+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"
   integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
@@ -6643,6 +6830,29 @@ uglify-to-browserify@~1.0.0:
   resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
   integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc=
 
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
+  integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
+  integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
+
 union-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"