]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: (wip) setup compiler-sfc
authorEvan You <yyx990803@gmail.com>
Thu, 7 Nov 2019 02:58:15 +0000 (21:58 -0500)
committerEvan You <yyx990803@gmail.com>
Thu, 7 Nov 2019 02:58:15 +0000 (21:58 -0500)
16 files changed:
packages/compiler-sfc/README.md [new file with mode: 0644]
packages/compiler-sfc/api-extractor.json [new file with mode: 0644]
packages/compiler-sfc/package.json [new file with mode: 0644]
packages/compiler-sfc/src/compileStyle.ts [new file with mode: 0644]
packages/compiler-sfc/src/compileTemplate.ts [new file with mode: 0644]
packages/compiler-sfc/src/index.ts [new file with mode: 0644]
packages/compiler-sfc/src/parse.ts [new file with mode: 0644]
packages/compiler-sfc/src/shims.d.ts [new file with mode: 0644]
packages/compiler-sfc/src/stylePluginScoped.ts [new file with mode: 0644]
packages/compiler-sfc/src/stylePluginTrim.ts [new file with mode: 0644]
packages/compiler-sfc/src/stylePreprocessors.ts [new file with mode: 0644]
packages/compiler-sfc/src/templatePluginAssetUrl.ts [new file with mode: 0644]
packages/compiler-sfc/src/templatePluginSrcset.ts [new file with mode: 0644]
packages/compiler-sfc/src/templatePluginUtils.ts [new file with mode: 0644]
rollup.config.js
yarn.lock

diff --git a/packages/compiler-sfc/README.md b/packages/compiler-sfc/README.md
new file mode 100644 (file)
index 0000000..66a6dea
--- /dev/null
@@ -0,0 +1,17 @@
+# @vue/compiler-sfc
+
+> Lower level utilities for compiling Vue single file components
+
+This package contains lower level utilities that you can use if you are writing a plugin / transform for a bundler or module system that compiles Vue single file components into JavaScript. It is used in [vue-loader](https://github.com/vuejs/vue-loader).
+
+The API surface is intentionally minimal - the goal is to reuse as much as possible while being as flexible as possible.
+
+## Why isn't `@vue/compiler-dom` a peerDependency?
+
+Since this package is more often used as a low-level utility, it is usually a transitive dependency in an actual Vue project. It is therefore the responsibility of the higher-level package (e.g. `vue-loader`) to inject `@vue/compiler-dom` via options when calling the `compileTemplate` methods.
+
+Not listing it as a peer depedency also allows tooling authors to use a custom template compiler (built on top of `@vue/compiler-core`) instead of `@vue/compiler-dom`, without having to include it just to fullfil the peer dep requirement.
+
+## API
+
+TODO
diff --git a/packages/compiler-sfc/api-extractor.json b/packages/compiler-sfc/api-extractor.json
new file mode 100644 (file)
index 0000000..305e85f
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "extends": "../../api-extractor.json",
+  "mainEntryPointFilePath": "./dist/packages/<unscopedPackageName>/src/index.d.ts",
+  "dtsRollup": {
+    "untrimmedFilePath": "./dist/<unscopedPackageName>.d.ts"
+  }
+}
\ No newline at end of file
diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json
new file mode 100644 (file)
index 0000000..cf84288
--- /dev/null
@@ -0,0 +1,39 @@
+{
+  "name": "@vue/compiler-sfc",
+  "version": "3.0.0-alpha.1",
+  "description": "@vue/compiler-sfc",
+  "main": "dist/compiler-sfc.cjs.js",
+  "files": [
+    "dist"
+  ],
+  "types": "dist/compiler-sfc.d.ts",
+  "buildOptions": {
+    "prod": false,
+    "formats": [
+      "cjs"
+    ]
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vuejs/vue.git"
+  },
+  "keywords": [
+    "vue"
+  ],
+  "author": "Evan You",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/vuejs/vue/issues"
+  },
+  "homepage": "https://github.com/vuejs/vue/tree/dev/packages/compiler-sfc#readme",
+  "dependencies": {
+    "@vue/compiler-core": "3.0.0-alpha.1",
+    "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-selector-parser": "^6.0.2",
+    "source-map": "^0.7.3"
+  }
+}
diff --git a/packages/compiler-sfc/src/compileStyle.ts b/packages/compiler-sfc/src/compileStyle.ts
new file mode 100644 (file)
index 0000000..0780cf6
--- /dev/null
@@ -0,0 +1,145 @@
+// const postcss = require('postcss')
+import postcss, { ProcessOptions, LazyResult, Result, ResultMap } from 'postcss'
+import trimPlugin from './stylePluginTrim'
+import scopedPlugin from './stylePluginScoped'
+import {
+  processors,
+  StylePreprocessor,
+  StylePreprocessorResults
+} from './stylePreprocessors'
+
+export interface StyleCompileOptions {
+  source: string
+  filename: string
+  id: string
+  map?: object
+  scoped?: boolean
+  trim?: boolean
+  preprocessLang?: string
+  preprocessOptions?: any
+  postcssOptions?: any
+  postcssPlugins?: any[]
+}
+
+export interface AsyncStyleCompileOptions extends StyleCompileOptions {
+  isAsync?: boolean
+}
+
+export interface StyleCompileResults {
+  code: string
+  map: object | void
+  rawResult: LazyResult | Result | undefined
+  errors: string[]
+}
+
+export function compileStyle(
+  options: StyleCompileOptions
+): StyleCompileResults {
+  return doCompileStyle({ ...options, isAsync: false }) as StyleCompileResults
+}
+
+export function compileStyleAsync(
+  options: StyleCompileOptions
+): Promise<StyleCompileResults> {
+  return doCompileStyle({ ...options, isAsync: true }) as Promise<
+    StyleCompileResults
+  >
+}
+
+export function doCompileStyle(
+  options: AsyncStyleCompileOptions
+): StyleCompileResults | Promise<StyleCompileResults> {
+  const {
+    filename,
+    id,
+    scoped = true,
+    trim = true,
+    preprocessLang,
+    postcssOptions,
+    postcssPlugins
+  } = options
+  const preprocessor = preprocessLang && processors[preprocessLang]
+  const preProcessedSource = preprocessor && preprocess(options, preprocessor)
+  const map = preProcessedSource ? preProcessedSource.map : options.map
+  const source = preProcessedSource ? preProcessedSource.code : options.source
+
+  const plugins = (postcssPlugins || []).slice()
+  if (trim) {
+    plugins.push(trimPlugin())
+  }
+  if (scoped) {
+    plugins.push(scopedPlugin(id))
+  }
+
+  const postCSSOptions: ProcessOptions = {
+    ...postcssOptions,
+    to: filename,
+    from: filename
+  }
+  if (map) {
+    postCSSOptions.map = {
+      inline: false,
+      annotation: false,
+      prev: map
+    }
+  }
+
+  let result: LazyResult | undefined
+  let code: string | undefined
+  let outMap: ResultMap | undefined
+
+  const errors: any[] = []
+  if (preProcessedSource && preProcessedSource.errors.length) {
+    errors.push(...preProcessedSource.errors)
+  }
+
+  try {
+    result = postcss(plugins).process(source, postCSSOptions)
+
+    // In async mode, return a promise.
+    if (options.isAsync) {
+      return result
+        .then(result => ({
+          code: result.css || '',
+          map: result.map && result.map.toJSON(),
+          errors,
+          rawResult: result
+        }))
+        .catch(error => ({
+          code: '',
+          map: undefined,
+          errors: [...errors, error.message],
+          rawResult: undefined
+        }))
+    }
+
+    // force synchronous transform (we know we only have sync plugins)
+    code = result.css
+    outMap = result.map
+  } catch (e) {
+    errors.push(e)
+  }
+
+  return {
+    code: code || ``,
+    map: outMap && outMap.toJSON(),
+    errors,
+    rawResult: result
+  }
+}
+
+function preprocess(
+  options: StyleCompileOptions,
+  preprocessor: StylePreprocessor
+): StylePreprocessorResults {
+  return preprocessor.render(
+    options.source,
+    options.map,
+    Object.assign(
+      {
+        filename: options.filename
+      },
+      options.preprocessOptions
+    )
+  )
+}
diff --git a/packages/compiler-sfc/src/compileTemplate.ts b/packages/compiler-sfc/src/compileTemplate.ts
new file mode 100644 (file)
index 0000000..b970d91
--- /dev/null
@@ -0,0 +1,3 @@
+export function compileTemplate() {
+  // TODO
+}
diff --git a/packages/compiler-sfc/src/index.ts b/packages/compiler-sfc/src/index.ts
new file mode 100644 (file)
index 0000000..7c8918b
--- /dev/null
@@ -0,0 +1,16 @@
+// API
+export { parse } from './parse'
+export { compileTemplate } from './compileTemplate'
+export { compileStyle, compileStyleAsync } from './compileStyle'
+
+// Types
+export {
+  SFCParseOptions,
+  SFCDescriptor,
+  SFCBlock,
+  SFCTemplateBlock,
+  SFCScriptBlock,
+  SFCStyleBlock
+} from './parse'
+
+export { StyleCompileOptions, StyleCompileResults } from './compileStyle'
diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts
new file mode 100644 (file)
index 0000000..9cfded6
--- /dev/null
@@ -0,0 +1,137 @@
+import {
+  parse as baseParse,
+  TextModes,
+  NodeTypes,
+  TextNode,
+  ElementNode,
+  SourceLocation
+} from '@vue/compiler-core'
+import { RawSourceMap } from 'source-map'
+
+export interface SFCParseOptions {
+  needMap?: boolean
+  filename?: string
+  sourceRoot?: string
+}
+
+export interface SFCBlock {
+  type: string
+  content: string
+  attrs: Record<string, string | true>
+  loc: SourceLocation
+  map?: RawSourceMap
+  lang?: string
+  src?: string
+}
+
+export interface SFCTemplateBlock extends SFCBlock {
+  type: 'template'
+  functional?: boolean
+}
+
+export interface SFCScriptBlock extends SFCBlock {
+  type: 'script'
+}
+
+export interface SFCStyleBlock extends SFCBlock {
+  type: 'style'
+  scoped?: boolean
+  module?: string | boolean
+}
+
+export interface SFCDescriptor {
+  filename: string
+  template: SFCTemplateBlock | null
+  script: SFCScriptBlock | null
+  styles: SFCStyleBlock[]
+  customBlocks: SFCBlock[]
+}
+
+export function parse(
+  source: string,
+  {
+    needMap = true,
+    filename = 'component.vue',
+    sourceRoot = ''
+  }: SFCParseOptions = {}
+): SFCDescriptor {
+  // TODO check cache
+
+  const sfc: SFCDescriptor = {
+    filename,
+    template: null,
+    script: null,
+    styles: [],
+    customBlocks: []
+  }
+  const ast = baseParse(source, {
+    isNativeTag: () => true,
+    getTextMode: () => TextModes.RAWTEXT
+  })
+
+  ast.children.forEach(node => {
+    if (node.type !== NodeTypes.ELEMENT) {
+      return
+    }
+    switch (node.tag) {
+      case 'template':
+        if (!sfc.template) {
+          sfc.template = createBlock(node) as SFCTemplateBlock
+        } else {
+          // TODO warn duplicate template
+        }
+        break
+      case 'script':
+        if (!sfc.script) {
+          sfc.script = createBlock(node) as SFCScriptBlock
+        } else {
+          // TODO warn duplicate script
+        }
+        break
+      case 'style':
+        sfc.styles.push(createBlock(node) as SFCStyleBlock)
+        break
+      default:
+        sfc.customBlocks.push(createBlock(node))
+        break
+    }
+  })
+
+  if (needMap) {
+    // TODO source map
+  }
+  // TODO set cache
+
+  return sfc
+}
+
+function createBlock(node: ElementNode): SFCBlock {
+  const type = node.tag
+  const text = node.children[0] as TextNode
+  const attrs: Record<string, string | true> = {}
+  const block: SFCBlock = {
+    type,
+    content: text.content,
+    loc: text.loc,
+    attrs
+  }
+  node.props.forEach(p => {
+    if (p.type === NodeTypes.ATTRIBUTE) {
+      attrs[p.name] = p.value ? p.value.content || true : true
+      if (p.name === 'lang') {
+        block.lang = p.value && p.value.content
+      } else if (p.name === 'src') {
+        block.src = p.value && p.value.content
+      } else if (type === 'style') {
+        if (p.name === 'scoped') {
+          ;(block as SFCStyleBlock).scoped = true
+        } else if (p.name === 'module') {
+          ;(block as SFCStyleBlock).module = attrs[p.name]
+        }
+      } else if (type === 'template' && p.name === 'functional') {
+        ;(block as SFCTemplateBlock).functional = true
+      }
+    }
+  })
+  return block
+}
diff --git a/packages/compiler-sfc/src/shims.d.ts b/packages/compiler-sfc/src/shims.d.ts
new file mode 100644 (file)
index 0000000..99a3b66
--- /dev/null
@@ -0,0 +1,3 @@
+declare module 'merge-source-map' {
+  export default function merge(oldMap: object, newMap: object): object
+}
diff --git a/packages/compiler-sfc/src/stylePluginScoped.ts b/packages/compiler-sfc/src/stylePluginScoped.ts
new file mode 100644 (file)
index 0000000..32c132a
--- /dev/null
@@ -0,0 +1,101 @@
+import postcss, { Root } from 'postcss'
+import selectorParser from 'postcss-selector-parser'
+
+export default postcss.plugin('add-id', (options: any) => (root: Root) => {
+  const id: string = options
+  const keyframes = Object.create(null)
+
+  root.each(function rewriteSelector(node: any) {
+    if (!node.selector) {
+      // handle media queries
+      if (node.type === 'atrule') {
+        if (node.name === 'media' || node.name === 'supports') {
+          node.each(rewriteSelector)
+        } else if (/-?keyframes$/.test(node.name)) {
+          // register keyframes
+          keyframes[node.params] = node.params = node.params + '-' + id
+        }
+      }
+      return
+    }
+    node.selector = selectorParser((selectors: any) => {
+      selectors.each((selector: any) => {
+        let node: any = null
+
+        // find the last child node to insert attribute selector
+        selector.each((n: any) => {
+          // ">>>" combinator
+          // and /deep/ alias for >>>, since >>> doesn't work in SASS
+          if (
+            n.type === 'combinator' &&
+            (n.value === '>>>' || n.value === '/deep/')
+          ) {
+            n.value = ' '
+            n.spaces.before = n.spaces.after = ''
+            return false
+          }
+
+          // in newer versions of sass, /deep/ support is also dropped, so add a ::v-deep alias
+          if (n.type === 'pseudo' && n.value === '::v-deep') {
+            n.value = n.spaces.before = n.spaces.after = ''
+            return false
+          }
+
+          if (n.type !== 'pseudo' && n.type !== 'combinator') {
+            node = n
+          }
+        })
+
+        if (node) {
+          node.spaces.after = ''
+        } else {
+          // For deep selectors & standalone pseudo selectors,
+          // the attribute selectors are prepended rather than appended.
+          // So all leading spaces must be eliminated to avoid problems.
+          selector.first.spaces.before = ''
+        }
+
+        selector.insertAfter(
+          node,
+          selectorParser.attribute({
+            attribute: id,
+            value: id,
+            raws: {}
+          })
+        )
+      })
+    }).processSync(node.selector)
+  })
+
+  // If keyframes are found in this <style>, find and rewrite animation names
+  // in declarations.
+  // Caveat: this only works for keyframes and animation rules in the same
+  // <style> element.
+  if (Object.keys(keyframes).length) {
+    root.walkDecls(decl => {
+      // individual animation-name declaration
+      if (/^(-\w+-)?animation-name$/.test(decl.prop)) {
+        decl.value = decl.value
+          .split(',')
+          .map(v => keyframes[v.trim()] || v.trim())
+          .join(',')
+      }
+      // shorthand
+      if (/^(-\w+-)?animation$/.test(decl.prop)) {
+        decl.value = decl.value
+          .split(',')
+          .map(v => {
+            const vals = v.trim().split(/\s+/)
+            const i = vals.findIndex(val => keyframes[val])
+            if (i !== -1) {
+              vals.splice(i, 1, keyframes[vals[i]])
+              return vals.join(' ')
+            } else {
+              return v
+            }
+          })
+          .join(',')
+      }
+    })
+  }
+})
diff --git a/packages/compiler-sfc/src/stylePluginTrim.ts b/packages/compiler-sfc/src/stylePluginTrim.ts
new file mode 100644 (file)
index 0000000..7d2cd9a
--- /dev/null
@@ -0,0 +1,10 @@
+import postcss, { Root } from 'postcss'
+
+export default postcss.plugin('trim', () => (css: Root) => {
+  css.walk(({ type, raws }) => {
+    if (type === 'rule' || type === 'atrule') {
+      if (raws.before) raws.before = '\n'
+      if (raws.after) raws.after = '\n'
+    }
+  })
+})
diff --git a/packages/compiler-sfc/src/stylePreprocessors.ts b/packages/compiler-sfc/src/stylePreprocessors.ts
new file mode 100644 (file)
index 0000000..5e820dd
--- /dev/null
@@ -0,0 +1,113 @@
+import merge from 'merge-source-map'
+
+export interface StylePreprocessor {
+  render(source: string, map?: object, options?: any): StylePreprocessorResults
+}
+
+export interface StylePreprocessorResults {
+  code: string
+  map?: object
+  errors: Array<Error>
+}
+
+// .scss/.sass processor
+const scss: StylePreprocessor = {
+  render(source, map, options) {
+    const nodeSass = require('sass')
+    const finalOptions = Object.assign({}, options, {
+      data: source,
+      file: options.filename,
+      outFile: options.filename,
+      sourceMap: !!map
+    })
+
+    try {
+      const result = nodeSass.renderSync(finalOptions)
+
+      if (map) {
+        return {
+          code: result.css.toString(),
+          map: merge(map, JSON.parse(result.map.toString())),
+          errors: []
+        }
+      }
+
+      return { code: result.css.toString(), errors: [] }
+    } catch (e) {
+      return { code: '', errors: [e] }
+    }
+  }
+}
+
+const sass: StylePreprocessor = {
+  render(source, map, options) {
+    return scss.render(
+      source,
+      map,
+      Object.assign({}, options, { indentedSyntax: true })
+    )
+  }
+}
+
+// .less
+const less: StylePreprocessor = {
+  render(source, map, options) {
+    const nodeLess = require('less')
+
+    let result: any
+    let error: Error | null = null
+    nodeLess.render(
+      source,
+      Object.assign({}, options, { syncImport: true }),
+      (err: Error | null, output: any) => {
+        error = err
+        result = output
+      }
+    )
+
+    if (error) return { code: '', errors: [error] }
+
+    if (map) {
+      return {
+        code: result.css.toString(),
+        map: merge(map, result.map),
+        errors: []
+      }
+    }
+
+    return { code: result.css.toString(), errors: [] }
+  }
+}
+
+// .styl
+const styl: StylePreprocessor = {
+  render(source, map, options) {
+    const nodeStylus = require('stylus')
+    try {
+      const ref = nodeStylus(source)
+      Object.keys(options).forEach(key => ref.set(key, options[key]))
+      if (map) ref.set('sourcemap', { inline: false, comment: false })
+
+      const result = ref.render()
+      if (map) {
+        return {
+          code: result,
+          map: merge(map, ref.sourcemap),
+          errors: []
+        }
+      }
+
+      return { code: result, errors: [] }
+    } catch (e) {
+      return { code: '', errors: [e] }
+    }
+  }
+}
+
+export const processors: Record<string, StylePreprocessor> = {
+  less,
+  sass,
+  scss,
+  styl,
+  stylus: styl
+}
diff --git a/packages/compiler-sfc/src/templatePluginAssetUrl.ts b/packages/compiler-sfc/src/templatePluginAssetUrl.ts
new file mode 100644 (file)
index 0000000..3025bfa
--- /dev/null
@@ -0,0 +1,5 @@
+import { NodeTransform } from '@vue/compiler-core'
+
+export const transformAssetUrl: NodeTransform = () => {
+  // TODO
+}
diff --git a/packages/compiler-sfc/src/templatePluginSrcset.ts b/packages/compiler-sfc/src/templatePluginSrcset.ts
new file mode 100644 (file)
index 0000000..144749c
--- /dev/null
@@ -0,0 +1,5 @@
+import { NodeTransform } from '@vue/compiler-core'
+
+export const transformSrcset: NodeTransform = () => {
+  // TODO
+}
diff --git a/packages/compiler-sfc/src/templatePluginUtils.ts b/packages/compiler-sfc/src/templatePluginUtils.ts
new file mode 100644 (file)
index 0000000..b6134b3
--- /dev/null
@@ -0,0 +1,55 @@
+export interface Attr {
+  name: string
+  value: string
+}
+
+export interface ASTNode {
+  tag: string
+  attrs: Attr[]
+}
+
+import { UrlWithStringQuery, parse as uriParse } from 'url'
+
+// TODO use imports instead
+export function urlToRequire(url: string): string {
+  const returnValue = `"${url}"`
+  // same logic as in transform-require.js
+  const firstChar = url.charAt(0)
+  if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
+    if (firstChar === '~') {
+      const secondChar = url.charAt(1)
+      url = url.slice(secondChar === '/' ? 2 : 1)
+    }
+
+    const uriParts = parseUriParts(url)
+
+    if (!uriParts.hash) {
+      return `require("${url}")`
+    } else {
+      // support uri fragment case by excluding it from
+      // the require and instead appending it as string;
+      // assuming that the path part is sufficient according to
+      // the above caseing(t.i. no protocol-auth-host parts expected)
+      return `require("${uriParts.path}") + "${uriParts.hash}"`
+    }
+  }
+  return returnValue
+}
+
+/**
+ * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
+ * @param urlString an url as a string
+ */
+function parseUriParts(urlString: string): UrlWithStringQuery {
+  // initialize return value
+  const returnValue: UrlWithStringQuery = uriParse('')
+  if (urlString) {
+    // A TypeError is thrown if urlString is not a string
+    // @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
+    if ('string' === typeof urlString) {
+      // check is an uri
+      return uriParse(urlString) // take apart the uri
+    }
+  }
+  return returnValue
+}
index 7c79dc252e4b8da0bdd68c4f8f50ca17ff2c443e..8639c28cd2bf12c56d874790b4806d12381c586f 100644 (file)
@@ -60,7 +60,7 @@ const packageConfigs = process.env.PROD_ONLY
 
 if (process.env.NODE_ENV === 'production') {
   packageFormats.forEach(format => {
-    if (format === 'cjs') {
+    if (format === 'cjs' && packageOptions.prod !== false) {
       packageConfigs.push(createProductionConfig(format))
     }
     if (format === 'global' || format === 'esm-browser') {
@@ -107,7 +107,9 @@ function createConfig(output, plugins = []) {
   // during a single build.
   hasTSChecked = true
 
-  const externals = Object.keys(aliasOptions).filter(p => p !== '@vue/shared')
+  const externals = Object.keys(aliasOptions)
+    .concat(Object.keys(pkg.dependencies || []))
+    .filter(p => p !== '@vue/shared')
 
   return {
     input: resolve(`src/index.ts`),
index 797344a8298053ce623afb990b7b10aee07b27ff..384d1114c20904d7a36675f8eaabd8790ac62de9 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -1649,6 +1649,11 @@ before-after-hook@^2.0.0:
   resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
   integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
 
+bluebird@^3.1.1:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
+  integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
+
 bluebird@^3.5.1:
   version "3.5.2"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a"
@@ -2109,6 +2114,13 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
 
+consolidate@^0.15.1:
+  version "0.15.1"
+  resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
+  integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==
+  dependencies:
+    bluebird "^3.1.1"
+
 conventional-changelog-angular@^5.0.3:
   version "5.0.3"
   resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0"
@@ -2272,6 +2284,11 @@ 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=
 
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
 cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
   version "0.3.4"
   resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
@@ -3380,6 +3397,11 @@ has@^1.0.1:
   dependencies:
     function-bind "^1.1.1"
 
+hash-sum@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
+  integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
+
 hosted-git-info@^2.1.4, hosted-git-info@^2.6.0:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
@@ -3503,6 +3525,11 @@ indent-string@^3.0.0:
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
   integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
 
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
 infer-owner@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
@@ -4885,6 +4912,13 @@ meow@^5.0.0:
     trim-newlines "^2.0.0"
     yargs-parser "^10.0.0"
 
+merge-source-map@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
+  integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==
+  dependencies:
+    source-map "^0.6.1"
+
 merge-stream@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@@ -5797,6 +5831,24 @@ 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-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"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    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"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -7221,6 +7273,11 @@ union-value@^1.0.0:
     is-extendable "^0.1.1"
     set-value "^0.4.3"
 
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
 unique-filename@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"