From fa7c5717db088df946af62fadd361250c31726df Mon Sep 17 00:00:00 2001 From: luckyadam Date: Tue, 29 May 2018 22:25:36 +0800 Subject: [PATCH] =?UTF-8?q?fix(cli):=20rn=E7=BC=96=E8=AF=91=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/taro-cli/package.json | 1 + packages/taro-cli/src/build.js | 4 +- packages/taro-cli/src/rn.js | 391 +++++++++++++++++++++++++++++++++ packages/taro-cli/yarn.lock | 111 +++++++++- 4 files changed, 502 insertions(+), 5 deletions(-) create mode 100644 packages/taro-cli/src/rn.js diff --git a/packages/taro-cli/package.json b/packages/taro-cli/package.json index 539c6d83f7ff..8ed07cde02e3 100644 --- a/packages/taro-cli/package.json +++ b/packages/taro-cli/package.json @@ -30,6 +30,7 @@ "chokidar": "^2.0.3", "commander": "^2.15.0", "cross-spawn": "^6.0.5", + "css-to-react-native-transform": "^1.4.0", "fs-extra": "^5.0.0", "glob": "^7.1.2", "inquirer": "^5.2.0", diff --git a/packages/taro-cli/src/build.js b/packages/taro-cli/src/build.js index 00d71a949eac..1c75bf53304f 100644 --- a/packages/taro-cli/src/build.js +++ b/packages/taro-cli/src/build.js @@ -37,8 +37,8 @@ function buildForH5 ({ watch }) { require('./h5').build({ watch }) } -function buildForRN () { - +function buildForRN ({ watch }) { + require('./rn').build({ watch }) } module.exports = build diff --git a/packages/taro-cli/src/rn.js b/packages/taro-cli/src/rn.js new file mode 100644 index 000000000000..66e5b409bb52 --- /dev/null +++ b/packages/taro-cli/src/rn.js @@ -0,0 +1,391 @@ +const fs = require('fs-extra') +const path = require('path') +const chalk = require('chalk') +const vfs = require('vinyl-fs') +const through2 = require('through2') +const chokidar = require('chokidar') +const babylon = require('babylon') +const traverse = require('babel-traverse').default +const t = require('babel-types') +const generate = require('babel-generator').default +const template = require('babel-template') +const _ = require('lodash') +const transformCSS = require('css-to-react-native-transform').default + +const npmProcess = require('./npm') +const Util = require('./util') +const CONFIG = require('./config') +const babylonConfig = require('./config/babylon') + +const appPath = process.cwd() +const projectConfig = require(path.join(appPath, Util.PROJECT_CONFIG))(_.merge) +const sourceDirName = projectConfig.sourceRoot || CONFIG.SOURCE_DIR +const outputDirName = projectConfig.outputRoot || CONFIG.OUTPUT_DIR +const sourceDir = path.join(appPath, sourceDirName) +const outputDir = path.join(appPath, outputDirName) +const tempDir = '.temp' +const tempPath = path.join(appPath, tempDir) +const entryFilePath = path.join(sourceDir, CONFIG.ENTRY) +const pluginsConfig = projectConfig.plugins || {} + +const isBuildingStyles = {} +let isProduction = false +const styleDenpendencyTree = {} + +const reactImportDefaultName = 'React' +const providerComponentName = 'Provider' +const configStoreFuncName = 'configStore' +const setStoreFuncName = 'setStore' +const styleSheetImportFromRNName = 'StyleSheet' +const importStylesName = 'styles' + +const taroApis = [ + 'getEnv', + 'ENV_TYPE', + 'eventCenter', + 'Events', + 'internal_safe_get', + 'internal_dynamic_recursive' +] + +const PACKAGES = { + '@tarojs/taro': '@tarojs/taro', + '@tarojs/taro-rn': '@tarojs/taro-rn', + '@tarojs/redux': '@tarojs/redux', + '@tarojs/components': '@tarojs/components', + '@tarojs/components-rn': '@tarojs/components-rn', + 'react': 'react', + 'react-native': 'react-native', + 'react-redux': 'react-redux' +} + +function parseJSCode (code, filePath) { + const ast = babylon.parse(code, babylonConfig) + const styleFiles = [] + let taroImportDefaultName + let hasAddReactImportDefaultName = false + let providorImportName + let storeName + let classRenderReturnJSX + + traverse(ast, { + ClassDeclaration (astPath) { + const node = astPath.node + if (!node.superClass) { + return + } + if (node.superClass.type === 'MemberExpression' && + node.superClass.object.name === taroImportDefaultName) { + node.superClass.object.name = reactImportDefaultName + if (node.id === null) { + const renameComponentClassName = '_TaroComponentClass' + astPath.replaceWith( + t.classDeclaration( + t.identifier(renameComponentClassName), + node.superClass, + node.body, + node.decorators || [] + ) + ) + } + } else if (node.superClass.name === 'Component') { + if (node.id === null) { + const renameComponentClassName = '_TaroComponentClass' + astPath.replaceWith( + t.classDeclaration( + t.identifier(renameComponentClassName), + node.superClass, + node.body, + node.decorators || [] + ) + ) + } + } + }, + + ClassMethod (astPath) { + let node = astPath.node + const key = node.key + if (key.name !== 'render') return + astPath.traverse({ + BlockStatement (astPath) { + if (astPath.parent === node) { + node = astPath.node + astPath.traverse({ + ReturnStatement (astPath) { + if (astPath.parent === node) { + astPath.traverse({ + JSXElement (astPath) { + classRenderReturnJSX = generate(astPath.node).code + } + }) + } + } + }) + } + } + }) + }, + + ImportDeclaration (astPath) { + const node = astPath.node + const source = node.source + const value = source.value + const valueExtname = path.extname(value) + const specifiers = node.specifiers + + if (!Util.isNpmPkg(value)) { + if (Util.REG_STYLE.test(valueExtname)) { + const basename = path.basename(value, valueExtname) + const stylePath = path.resolve(path.dirname(filePath), value) + if (styleFiles.indexOf(stylePath) < 0) { + styleFiles.push(stylePath) + } + astPath.replaceWith(t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(_.camelCase(`${basename}_${importStylesName}`)))], + t.stringLiteral(`${path.dirname(value)}/${basename}_styles`)) + ) + } + return + } + if (value === PACKAGES['@tarojs/taro']) { + let specifier = specifiers.find(item => item.type === 'ImportDefaultSpecifier') + if (specifier) { + hasAddReactImportDefaultName = true + taroImportDefaultName = specifier.local.name + specifier.local.name = reactImportDefaultName + } else if (!hasAddReactImportDefaultName) { + hasAddReactImportDefaultName = true + node.specifiers.unshift( + t.importDefaultSpecifier(t.identifier(reactImportDefaultName)) + ) + } + const taroApisSpecifiers = [] + specifiers.forEach((item, index) => { + if (item.imported && taroApis.indexOf(item.imported.name) >= 0) { + taroApisSpecifiers.push(t.importSpecifier(t.identifier(item.local.name), t.identifier(item.imported.name))) + specifiers.splice(index, 1) + } + }) + source.value = PACKAGES['react'] + + if (taroApisSpecifiers.length) { + astPath.insertBefore(t.importDeclaration(taroApisSpecifiers, t.stringLiteral(PACKAGES['@tarojs/taro-rn']))) + } + if (!specifiers.length) { + astPath.remove() + } + } else if (value === PACKAGES['@tarojs/redux']) { + const specifier = specifiers.find(item => { + return t.isImportSpecifier(item) && item.imported.name === providerComponentName + }) + if (specifier) { + providorImportName = specifier.local.name + } else { + providorImportName = providerComponentName + specifiers.push(t.importSpecifier(t.identifier(providerComponentName), t.identifier(providerComponentName))) + } + source.value = PACKAGES['react-redux'] + } + }, + + CallExpression (astPath) { + const node = astPath.node + const callee = node.callee + const calleeName = callee.name + const parentPath = astPath.parentPath + + if (t.isMemberExpression(callee)) { + if (callee.object.name === taroImportDefaultName && callee.property.name === 'render') { + astPath.remove() + } + } else { + if (calleeName === configStoreFuncName) { + if (parentPath.isAssignmentExpression()) { + storeName = parentPath.node.left.name + } else if (parentPath.isVariableDeclarator()) { + storeName = parentPath.node.id.name + } else { + storeName = 'store' + } + } else if (calleeName === setStoreFuncName) { + if (parentPath.isAssignmentExpression() || + parentPath.isExpressionStatement() || + parentPath.isVariableDeclarator()) { + parentPath.remove() + } + } + } + }, + Program: { + exit (astPath) { + const node = astPath.node + const importTaro = template( + `import ${taroImportDefaultName} from '${PACKAGES['@tarojs/taro-rn']}'`, + babylonConfig + )() + astPath.traverse({ + ClassMethod (astPath) { + const node = astPath.node + const key = node.key + if (key.name !== 'render' || filePath !== entryFilePath) return + let funcBody = classRenderReturnJSX + if (providerComponentName && storeName) { + // 使用redux + funcBody = ` + <${providorImportName} store={${storeName}}> + ${funcBody} + ` + } + node.body = template(`{return (${funcBody});}`, babylonConfig)() + } + }) + node.body.unshift(importTaro) + if (filePath === entryFilePath) { + const initNativeApi = template( + `${taroImportDefaultName}.initNativeApi(${taroImportDefaultName})`, + babylonConfig + )() + node.body.push(initNativeApi) + } + } + } + }) + return { + code: generate(ast).code, + styleFiles + } +} + +function parseJSXAttribute (code, filePath) { + const ast = babylon.parse(code, babylonConfig) + traverse(ast, { + JSXElement (astPath) { + const node = astPath.node + const openingElement = node.openingElement + if (openingElement && openingElement.attributes.length) { + const attributes = openingElement.attributes + const newAttributes = [] + let styleAttrs = [] + attributes.forEach(attr => { + const name = attr.name + if (name.name === 'className' || name.name === 'id') { + if (attr.value) { + styleAttrs = styleAttrs.concat(attr.value.value.split(' ')) + } + } else { + newAttributes.push(attr) + } + }) + if (styleAttrs.length) { + styleAttrs = _.uniq(styleAttrs) + const styleArr = styleAttrs.map(item => t.identifier(item)) + attributes.push( + t.jSXAttribute(t.jSXIdentifier('style'), t.jSXExpressionContainer(t.arrayExpression(styleArr))) + ) + } + openingElement.attributes = newAttributes + } + } + }) + + return { + code: generate(ast).code + } +} + +function compileDepStyles (filePath, styleFiles) { + if (isBuildingStyles[filePath]) { + return Promise.resolve({}) + } + isBuildingStyles[filePath] = true + return Promise.all(styleFiles.map(async p => { + const filePath = path.join(p) + const fileExt = path.extname(filePath) + const pluginName = Util.FILE_PROCESSOR_MAP[fileExt] + if (pluginName) { + return npmProcess.callPlugin(pluginName, null, filePath, pluginsConfig[pluginName] || {}) + } + return new Promise((resolve, reject) => { + fs.readFile(filePath, (err, content) => { + if (err) { + return reject(err) + } + resolve({ + css: content + }) + }) + }) + })).then(async resList => { + let resContent = resList.map(res => res.css.toString()).join('\n') + try { + // 处理css文件 + let tempFilePath = filePath.replace(sourceDir, tempPath) + const basename = path.basename(tempFilePath, path.extname(tempFilePath)) + tempFilePath = path.join(path.dirname(tempFilePath), `${basename}_styles.js`) + let styleObject = {} + if (resContent) { + styleObject = transformCSS(resContent) + } + const styleObjectStr = JSON.stringify(styleObject, null, 2) + styleDenpendencyTree[filePath] = { + styleFiles, + styleObject + } + const fileContent = `import { StyleSheet } from 'react-native'\n\nexport default StyleSheet.create(${styleObjectStr})` + fs.ensureDirSync(path.dirname(tempFilePath)) + fs.writeFileSync(tempFilePath, fileContent) + } catch (err) { + console.log(err) + } + }) +} + +function buildTemp () { + fs.emptyDirSync(tempPath) + return new Promise((resolve, reject) => { + vfs.src(path.join(sourceDir, '**')) + .pipe(through2.obj(async function (file, enc, cb) { + if (file.isNull() || file.isStream()) { + return cb(null, file) + } + const filePath = file.path + const content = file.contents.toString() + if (Util.REG_STYLE.test(filePath)) { + return cb() + } + if (Util.REG_SCRIPT.test(filePath)) { + let transformResult = parseJSCode(content, filePath) + const jsCode = transformResult.code + const styleFiles = transformResult.styleFiles + await compileDepStyles(filePath, styleFiles) + transformResult = parseJSXAttribute(content, filePath) + // console.log(transformResult) + file.contents = Buffer.from(jsCode) + } + this.push(file) + cb() + })) + .pipe(vfs.dest(path.join(tempPath))) + .on('end', () => { + resolve() + }) + }) +} + +function watchFiles () { + +} + +async function build ({ watch }) { + isProduction = !watch + fs.ensureDirSync(tempPath) + await buildTemp() + if (watch) { + watchFiles() + } +} + +module.exports = { + build +} diff --git a/packages/taro-cli/yarn.lock b/packages/taro-cli/yarn.lock index 79edf0a6e707..0b518bb78815 100644 --- a/packages/taro-cli/yarn.lock +++ b/packages/taro-cli/yarn.lock @@ -27,6 +27,10 @@ abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" @@ -107,6 +111,10 @@ arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -476,6 +484,10 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + core-js@^2.4.0, core-js@^2.5.0: version "2.5.6" resolved "https://registry.npmjs.org/core-js/-/core-js-2.5.6.tgz#0fe6d45bf3cac3ac364a9d72de7576f4eb221b9d" @@ -494,6 +506,39 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0" + +css-to-react-native-transform@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/css-to-react-native-transform/-/css-to-react-native-transform-1.4.0.tgz#4bb4575e15461f7e117bf15bf10d04ec0f81bb17" + dependencies: + css "^2.2.1" + css-mediaquery "^0.1.2" + css-to-react-native "^2.1.2" + +css-to-react-native@^2.1.2: + version "2.2.0" + resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.0.tgz#d524ef7f39a2747a8914e86563669ba35b7cf2e7" + dependencies: + css-color-keywords "^1.0.0" + fbjs "^0.8.5" + postcss-value-parser "^3.3.0" + +css@^2.2.1: + version "2.2.3" + resolved "https://registry.npmjs.org/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be" + dependencies: + inherits "^2.0.1" + source-map "^0.1.38" + source-map-resolve "^0.5.1" + urix "^0.1.0" + debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -578,6 +623,12 @@ electron-to-chromium@^1.3.45: version "1.3.45" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz#458ac1b1c5c760ce8811a16d2bfbd97ec30bafb8" +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -652,6 +703,18 @@ fast-glob@^2.0.2: merge2 "^1.2.1" micromatch "^3.1.10" +fbjs@^0.8.5: + version "0.8.16" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + figures@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -862,7 +925,7 @@ html@^1.0.0: dependencies: concat-stream "^1.4.7" -iconv-lite@^0.4.17, iconv-lite@^0.4.4: +iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.23" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: @@ -1056,6 +1119,10 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + is-unc-path@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" @@ -1096,6 +1163,13 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -1333,6 +1407,13 @@ nice-try@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + node-pre-gyp@^0.9.0: version "0.9.1" resolved "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz#f11c07516dd92f87199dbc7e1838eab7cd56c9e0" @@ -1534,7 +1615,7 @@ postcss-pxtransform@^0.0.1: dependencies: postcss "^6.0.21" -postcss-value-parser@^3.2.3: +postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" @@ -1554,6 +1635,12 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + pump@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" @@ -1750,6 +1837,10 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -1803,7 +1894,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -source-map-resolve@^0.5.0: +source-map-resolve@^0.5.0, source-map-resolve@^0.5.1: version "0.5.2" resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" dependencies: @@ -1823,6 +1914,12 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" +source-map@^0.1.38: + version "0.1.43" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + dependencies: + amdefine ">=0.0.4" + source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -1995,6 +2092,10 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +ua-parser-js@^0.7.9: + version "0.7.18" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" + unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -2118,6 +2219,10 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +whatwg-fetch@>=0.10.0: + version "2.0.4" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + which@^1.2.9: version "1.3.0" resolved "https://registry.npmjs.org/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"