├── .gitignore ├── .prettierignore ├── .travis.yml ├── .vscode └── settings.json ├── README.md ├── demo ├── index.js ├── string-literal-regex.js ├── string-literal.js ├── tag-regex.js └── tag.js ├── package.json ├── prettier.config.js ├── src ├── library │ ├── combine.ts │ ├── index.ts │ ├── regex-tools.ts │ ├── tsconfig.json │ └── tslint.json ├── test │ ├── test.ts │ ├── tsconfig.json │ └── tslint.json └── tsconfig.all.json ├── test ├── mocha.js └── mocha.opts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Installable packages 2 | 3 | node_modules/ 4 | 5 | # Configuration 6 | 7 | .config/ 8 | 9 | # Building artifacts 10 | 11 | bld/ 12 | dist/ 13 | .cache/ 14 | *.tgz 15 | 16 | # Debugging outputs 17 | 18 | *.log 19 | 20 | # Testing files 21 | 22 | /test.* 23 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10' 4 | - '11' 5 | before_script: 6 | - yarn build 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.tabSize": 2, 4 | "editor.insertSpaces": true, 5 | "files.eol": "\n", 6 | "files.insertFinalNewline": true, 7 | "files.trimTrailingWhitespace": true, 8 | "typescript.tsdk": "node_modules/typescript/lib", 9 | "tslint.autoFixOnSave": true, 10 | "tslint.configFile": "node_modules/@magicspace/configs/tslint-vscode.js" 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Regular Expression Tools [![Build Status](https://travis-ci.org/vilic/regex-tools.svg)](https://travis-ci.org/vilic/regex-tools) 2 | 3 | I use regular expressions to do a lot of things, and managing a long regular expression could be painful. 4 | So I write this simple tool to manage long and complex regular expressions such as regular expressions of lex parser. 5 | 6 | ### Install 7 | 8 | ```sh 9 | npm install regex-tools --save-dev 10 | ``` 11 | 12 | ### Create Options File 13 | 14 | Create a `test-regex.js` file and write something like this. 15 | 16 | ```javascript 17 | exports.options = { 18 | name: 'test', 19 | operation: 'combine', 20 | target: 'target.js', // support *.ts file, too 21 | flags: 'g', 22 | regexes: [//], 23 | }; 24 | ``` 25 | 26 | ### Source File 27 | 28 | Create a `target.js` and mark related code with `/* /$test/ */` (`$` followed by the name you configured in options file). 29 | 30 | ```typescript 31 | let testRegex = /* /$test/ */ /./; 32 | 33 | let groups = testRegex.exec(''); 34 | 35 | /* /$test/ */ 36 | let text = groups[0]; 37 | 38 | ''.replace(testRegex, function(/* /$test/ */ text) { 39 | return text; 40 | }); 41 | ``` 42 | 43 | ### A Basic Task 44 | 45 | Create a task and run it. 46 | 47 | ```javascript 48 | let Gulp = require('gulp'); 49 | let RegexTools = require('regex-tools'); 50 | 51 | let glob = require('glob'); 52 | 53 | Gulp.task('update-regex', function() { 54 | let optionsFiles = glob.sync('*-regex.js'); 55 | 56 | optionsFiles.forEach(function(path) { 57 | RegexTools.process(path); 58 | }); 59 | }); 60 | ``` 61 | 62 | After that, `target.js` should look like: 63 | 64 | ```javascript 65 | let testRegex = /* /$test/ */ /<(\w+)\d*>/g; 66 | 67 | let groups = testRegex.exec(''); 68 | 69 | /* /$test/ */ 70 | let text = groups[0]; 71 | let term = groups[1]; 72 | 73 | ''.replace(testRegex, function(/* /$test/ */ text, term) { 74 | return text; 75 | }); 76 | ``` 77 | 78 | Problem solved! You may checkout demo for relatively more complex examples. 79 | 80 | ## API References 81 | 82 | An options file is a node module that exports options as default. The exported default value could be either `RegexToolsOptions` or `RegexToolsOptions[]`. 83 | 84 | And here's related type declarations: 85 | 86 | ```typescript 87 | interface RegexToolsOptions { 88 | /** name that will match related tag in target source file. */ 89 | name: string; 90 | /** target source file. */ 91 | target: string; 92 | /** only "combine" so far. */ 93 | operation?: string; 94 | flags?: string; 95 | regexes: NestedRegexes; 96 | } 97 | 98 | type Lookahead = boolean | '=' | '!'; 99 | 100 | interface NestedRegexOptions { 101 | /** captured group name. */ 102 | name?: string; 103 | /** whether to use `|`, default to false. */ 104 | or?: boolean; 105 | /** whether to capture, default to false if `name` is not provided, otherwise true. */ 106 | capture?: boolean; 107 | /** lookahead, `true` or `"="` for positive and `"!"` for negative. */ 108 | lookahead?: Lookahead; 109 | /** ?, *, +, *?, +?, {1}, {1,}, {1,2} */ 110 | repeat?: string; 111 | regexes: RegExp | NestedRegexArray | NestedRegexOptions; 112 | } 113 | 114 | interface NestedRegexArray 115 | extends Array {} 116 | 117 | type NestedRegexes = NestedRegexArray | NestedRegexOptions; 118 | ``` 119 | 120 | ## Back Reference Tracking 121 | 122 | If you are using back reference, it will keep the index updated. That means you should write back references relative to the current part of regular expression. 123 | 124 | The options below will result in `/(distraction)(["'])\2/`. 125 | 126 | ```js 127 | exports.options = { 128 | name: 'test', 129 | operation: 'combine', 130 | target: 'target.js', 131 | regexes: [/(distraction)/, /(["'])\1/], 132 | }; 133 | ``` 134 | 135 | However as `\[number]` can also be a char with code in octal form, it's kind of complex to deal with. Please avoid writing a char (instead of a back reference) in this form. 136 | 137 | ## Named References 138 | 139 | Another way to deal with back references is to use named references provided by the tool. 140 | 141 | ```js 142 | exports.options = { 143 | name: 'test', 144 | operation: 'combine', 145 | target: 'target.js', 146 | regexes: [/($quote:["'])/, /.*?/, /($quote)/], 147 | }; 148 | ``` 149 | 150 | When generating group/parameter/enumerator list, the name of named reference (as well as name specified in `NestedRegexOptions`) will be exported according to its capture index. 151 | 152 | For groups and enumerator list, if you don't want some of them to show up, you may add a `~` between `$` and name. Such as `($~quote:["'])`. 153 | 154 | ## Tips 155 | 156 | When updating group array aliases, the index starts at either 0 or 1, depending on your code. 157 | 158 | You may also use `require('regex-tools').combine()` for more flexible usage, please check out source code (as it's typed) for more information. 159 | 160 | # License 161 | 162 | MIT License. 163 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Path = require('path'); 4 | 5 | const RegexTools = require('../bld/library'); 6 | 7 | const names = ['tag', 'string-literal']; 8 | 9 | for (let name of names) { 10 | let sourceFilePath = Path.join(__dirname, name + '.js'); 11 | let regexFilePath = Path.join(__dirname, name + '-regex.js'); 12 | 13 | console.log(Array(80).join('=')); 14 | console.log('SOURCE FILE', sourceFilePath); 15 | console.log(Array(80).join('=')); 16 | console.log(); 17 | // remove the second argument (skipWrite) to actually update target source files. 18 | console.log(RegexTools.process(regexFilePath, true)); 19 | } 20 | -------------------------------------------------------------------------------- /demo/string-literal-regex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample of a regular expression that matches string literal (ES5). 3 | * https://github.com/vilic/regex-tools 4 | */ 5 | 6 | 'use strict'; 7 | 8 | let quote = /($~quote:["'])/; 9 | let matchQuote = /($quote)/; 10 | 11 | let singleEscapeChar = /['"\\bfnrtv]/; 12 | let escapeChar = /['"\\bfnrtv\dxu]/; 13 | let nonEscapeChar = /[^'"\\bfnrtv\dxu\r\n\u2028\u2029]/; 14 | 15 | let hexEscapeSequence = /x[\da-fA-F]{2}/; 16 | let unicodeEscapeSequence = /u[\da-fA-F]{4}/; 17 | 18 | let zeroNotFollowedByDigit = /0(?!\d)/; 19 | 20 | let charEscapeSequence = { 21 | regexes: [singleEscapeChar, nonEscapeChar], 22 | or: true, 23 | }; 24 | 25 | let slashEscapeSequence = [ 26 | /\\/, 27 | { 28 | regexes: [ 29 | charEscapeSequence, 30 | zeroNotFollowedByDigit, 31 | hexEscapeSequence, 32 | unicodeEscapeSequence, 33 | ], 34 | or: true, 35 | }, 36 | ]; 37 | 38 | let lineContinuation = /\\(?:\r?\n|\r(?!\n)|[\u2028\u2029])/; 39 | 40 | let unescapedStringChar = /(?!($quote)|[\\\r\n\u2028\u2029])[\s\S]/; 41 | 42 | let optionalStringChars = { 43 | regexes: [unescapedStringChar, slashEscapeSequence, lineContinuation], 44 | or: true, 45 | repeat: '*', 46 | }; 47 | 48 | let stringLiteral = [quote, optionalStringChars, matchQuote]; 49 | 50 | module.exports = { 51 | name: 'stringLiteral', 52 | operation: 'combine', 53 | target: 'string-literal.js', 54 | flags: 'g', 55 | regexes: stringLiteral, 56 | }; 57 | -------------------------------------------------------------------------------- /demo/string-literal.js: -------------------------------------------------------------------------------- 1 | let stringLiteralRegex = /* /$stringLiteral/ */ /./; 2 | -------------------------------------------------------------------------------- /demo/tag-regex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let tagOpen = /]*)/; 5 | 6 | let spaces = /\s+/; 7 | let optionalSpaces = /\s*/; 8 | 9 | let attributeName = /[^=\s]+/; 10 | let equalSign = /=/; 11 | let quotedValue = /($quote:["'])(?:(?!($quote))[\s\S])*($quote)/; 12 | let unquotedValue = /\S+/; 13 | 14 | let attribute = [ 15 | attributeName, 16 | { 17 | regexes: [ 18 | optionalSpaces, 19 | equalSign, 20 | optionalSpaces, 21 | { 22 | regexes: [quotedValue, unquotedValue], 23 | or: true, 24 | }, 25 | ], 26 | repeat: '?', 27 | }, 28 | ]; 29 | 30 | let tagClose = /\/?>/; 31 | 32 | let tag = [ 33 | tagOpen, 34 | tagName, 35 | { 36 | regexes: [spaces, attribute], 37 | repeat: '*', 38 | }, 39 | optionalSpaces, 40 | tagClose, 41 | ]; 42 | 43 | module.exports = { 44 | name: 'tag', 45 | operation: 'combine', 46 | target: 'tag.js', 47 | flags: 'g', 48 | regexes: tag, 49 | }; 50 | -------------------------------------------------------------------------------- /demo/tag.js: -------------------------------------------------------------------------------- 1 | // run `node index.js` under demo directory to see what changes. 2 | 3 | let tagRegex = /* /$tag/ */ /./; 4 | 5 | let html = ''; 6 | let groups = tagRegex.exec(html); 7 | 8 | /* /$tag/ */ 9 | let text = groups[0]; 10 | 11 | html = html.replace(tagRegex, function(/* /$tag/ */ text) { 12 | return text; 13 | }); 14 | 15 | html = html.replace(tagRegex, function(/* /$tag/ */ text) { 16 | return text; 17 | }); 18 | 19 | html = html.replace(tagRegex, function(/* /$tag/ */ text, foo) { 20 | return text; 21 | }); 22 | 23 | html = html.replace(tagRegex, function(/* /$tag/ */ text, foo, bar) { 24 | return text; 25 | }); 26 | 27 | html = html.replace(tagRegex, function(/* /$tag/ */ text, foo, bar) { 28 | return text; 29 | }); 30 | 31 | console.log(groups); 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "regex-tools", 3 | "version": "0.3.8", 4 | "description": "Tools for managing long regular expressions.", 5 | "keywords": [ 6 | "regular expression", 7 | "regexp", 8 | "regex", 9 | "generate", 10 | "maintain" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/vilic/regex-tools" 15 | }, 16 | "author": "vilicvane", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/vilic/regex-tools/issues" 20 | }, 21 | "homepage": "https://github.com/vilic/regex-tools", 22 | "scripts": { 23 | "build": "rimraf bld && tsc --build src/tsconfig.all.json", 24 | "lint:library": "tslint -p src/library", 25 | "lint:test": "tslint -p src/test", 26 | "test:library": "yarn lint:library && mocha", 27 | "test:test": "yarn lint:test", 28 | "test": "yarn test:library && yarn test:test" 29 | }, 30 | "main": "bld/library/index.js", 31 | "types": "bld/library/index.d.ts", 32 | "files": [ 33 | "src/library/**/*.ts", 34 | "bld/library" 35 | ], 36 | "dependencies": { 37 | "tslang": "^0.1.11" 38 | }, 39 | "devDependencies": { 40 | "@magicspace/configs": "^0.1.46", 41 | "@types/chai": "^4.1.7", 42 | "@types/mocha": "^5.2.5", 43 | "@types/node": "^10.12.18", 44 | "chai": "^4.2.0", 45 | "mocha": "^5.2.0", 46 | "prettier": "^1.15.3", 47 | "rimraf": "^2.6.2", 48 | "source-map-support": "^0.5.9", 49 | "tslint": "^5.12.0", 50 | "tslint-language-service": "^0.9.9", 51 | "typescript": "^3.2.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@magicspace/configs/prettier'); 2 | -------------------------------------------------------------------------------- /src/library/combine.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Regular Expression Tools 3 | * https://github.com/vilic/regex-tools 4 | */ 5 | 6 | import {Dict} from 'tslang'; 7 | 8 | export type Lookahead = boolean | '=' | '!'; 9 | 10 | export interface NestedRegexOptions { 11 | name?: string; 12 | or?: boolean; 13 | capture?: boolean; 14 | lookahead?: Lookahead; 15 | repeat?: string; 16 | regexes: Regexes; 17 | } 18 | 19 | export interface NestedRegexArray extends Array {} 20 | 21 | export type NestedRegexes = NestedRegexArray | NestedRegexOptions; 22 | 23 | export type Regexes = RegExp | NestedRegexes; 24 | 25 | const GROUP_REGEX = /\(\$(~?[\w$][\w\d$]*)(?:(:)(?!\?)|\)(?=(\d)?))|(\(\?)|(\()|(\))|(\|)|(\[)|(\])|\\(\d+)|\\.|./g; 26 | 27 | export class CombinedResult { 28 | constructor( 29 | public combined: string, 30 | public groupNames: string[], 31 | public groupNameToIndex: Dict, 32 | public groupNameHideMap: Dict, 33 | ) {} 34 | 35 | getRegexLiteral(flags?: string): string { 36 | let literal = `/${this.combined.replace( 37 | /\\.|(\/)/g, 38 | (m: string, g1: string) => (g1 ? '\\/' : m), 39 | )}/`; 40 | return literal + (flags || ''); 41 | } 42 | 43 | getParametersSnippet({ 44 | typed = false, 45 | matchName = 'text', 46 | separator = ', ', 47 | }): string { 48 | let names = [matchName].concat(this.groupNames); 49 | 50 | if (typed) { 51 | return names.map(name => `${name}: string`).join(separator); 52 | } else { 53 | return names.join(separator); 54 | } 55 | } 56 | 57 | getGroupAliasDeclarationsSnippet({ 58 | arrayName = 'groups', 59 | useLet = true, 60 | newLine = '\n', 61 | lineIndent = '', 62 | matchName = '', 63 | } = {}): string { 64 | let lines: string[] = []; 65 | 66 | if (matchName) { 67 | lines.push(`${useLet ? 'let' : 'var'} ${matchName} = ${arrayName}[0];`); 68 | } 69 | 70 | let hideMap = this.groupNameHideMap; 71 | 72 | for (let [index, name] of this.groupNames.entries()) { 73 | if (!(name in hideMap)) { 74 | lines.push( 75 | `${useLet ? 'let' : 'var'} ${name} = ${arrayName}[${index + 1}];`, 76 | ); 77 | } 78 | } 79 | 80 | return lines.join(newLine + lineIndent); 81 | } 82 | 83 | getEnumDeclaration({ 84 | useConst = false, 85 | name = 'ExecGroup', 86 | newLine = '\n', 87 | lineIndent = '', 88 | indent = ' ', 89 | } = {}): string { 90 | let lines: string[] = []; 91 | 92 | let hideMap = this.groupNameHideMap; 93 | let skipped = true; 94 | 95 | // skipped 0 as it starts from 1. 96 | 97 | for (let [index, name] of this.groupNames.entries()) { 98 | if (name in hideMap) { 99 | skipped = true; 100 | } else if (skipped) { 101 | skipped = false; 102 | lines.push(`${name} = ${index + 1}`); 103 | } else { 104 | lines.push(`${name}`); 105 | } 106 | } 107 | 108 | return ( 109 | `${useConst ? 'const ' : ''}enum ${name} {${newLine}` + 110 | `${lineIndent + indent}${lines.join( 111 | `,${newLine}${lineIndent}${indent}`, 112 | )}${newLine}` + 113 | `${lineIndent}}` 114 | ); 115 | } 116 | } 117 | 118 | export function combine(regexes: NestedRegexes): CombinedResult { 119 | let groupCount = 0; 120 | let groupNameToIndex: Dict = {}; 121 | let groupNameHideMap: Dict = {}; 122 | 123 | let regexIndex = 0; 124 | 125 | let combined = processRegexes(regexes, true) || '(?:)'; 126 | 127 | let groupNames: string[] = []; 128 | 129 | for (let i = 0; i < groupCount; i++) { 130 | groupNames.push(`g${i + 1}`); 131 | } 132 | 133 | for (let name of Object.keys(groupNameToIndex)) { 134 | groupNames[groupNameToIndex[name] - 1] = name.replace( 135 | /-([a-z])/gi, 136 | (_m: string, g1: string) => g1.toUpperCase(), 137 | ); 138 | } 139 | 140 | return new CombinedResult( 141 | combined, 142 | groupNames, 143 | groupNameToIndex, 144 | groupNameHideMap, 145 | ); 146 | 147 | function processRegexes(regexes: NestedRegexes, upperOr: boolean): string { 148 | let name: string | undefined; 149 | let regexArray: NestedRegexArray; 150 | let or: boolean; 151 | let capture: boolean; 152 | let lookahead: Lookahead; 153 | let repeat: string; 154 | 155 | if (Array.isArray(regexes)) { 156 | regexArray = regexes; 157 | or = false; 158 | capture = false; 159 | lookahead = false; 160 | repeat = ''; 161 | } else { 162 | name = regexes.name; 163 | let optionRegexes = regexes.regexes; 164 | 165 | if (Array.isArray(optionRegexes)) { 166 | regexArray = optionRegexes; 167 | } else { 168 | regexArray = [optionRegexes]; 169 | } 170 | 171 | or = !!regexes.or; 172 | capture = !!name || !!regexes.capture; 173 | lookahead = regexes.lookahead === true ? '=' : regexes.lookahead || false; 174 | repeat = regexes.repeat || ''; 175 | 176 | if (!/^(?:\?\??|[+*]\??|\{\d+,\d*\}\??|\{\d+\})?$/.test(repeat)) { 177 | throw new Error(`Invalid repeat option "${repeat}"`); 178 | } 179 | } 180 | 181 | if (capture) { 182 | groupCount++; 183 | 184 | if (name) { 185 | groupNameToIndex[name] = groupCount; 186 | } 187 | } 188 | 189 | let combined = regexArray 190 | .map(regex => { 191 | if (regex instanceof RegExp) { 192 | return processPartialRegex(regex, or); 193 | } else { 194 | return processRegexes(regex, or); 195 | } 196 | }) 197 | .join(or ? '|' : ''); 198 | 199 | combined = capture 200 | ? `(${combined})` 201 | : (repeat && 202 | !/^(?:\\u.{4}|\\x.{2}|\\.|\[(?:\\.|[^\]])+\]|.)$/.test(combined)) || 203 | (!upperOr && or && regexArray.length > 1) 204 | ? `(?:${combined})` 205 | : combined; 206 | 207 | combined += repeat; 208 | 209 | if (lookahead) { 210 | combined = `(?${lookahead}${combined})`; 211 | } 212 | 213 | return combined; 214 | } 215 | 216 | /** 217 | * divide and conquer 218 | */ 219 | function processPartialRegex(regex: RegExp, upperOr: boolean): string { 220 | regexIndex++; 221 | 222 | let regexStr = regex.source; 223 | 224 | if (regexStr === '(?:)') { 225 | regexStr = ''; 226 | } 227 | 228 | // syntax test 229 | try { 230 | // tslint:disable-next-line:no-unused-expression 231 | new RegExp(regexStr); 232 | } catch (e) { 233 | e.message = `${e.message.replace(/^.+:\s?/, '')} in regex #${regexIndex}`; 234 | throw e; 235 | } 236 | 237 | // abc($name:) ($name) 238 | let partialGroupCount = 0; 239 | 240 | let sBraOpen = false; 241 | let bracketDepth = 0; 242 | // whether has | outside a group 243 | let hasOrOutside = false; 244 | 245 | let partialRegexStr = regexStr.replace( 246 | GROUP_REGEX, 247 | ( 248 | text: string, 249 | groupName: string, 250 | groupNameColon: string, 251 | digitFollowsBR: string, 252 | braWithQ: string, 253 | bra: string, 254 | ket: string, 255 | or: string, 256 | sBra: string, 257 | sKet: string, 258 | brNumber: string, 259 | ) => { 260 | if (groupName) { 261 | if (sBraOpen) { 262 | // throw new Error(`Group name can not be in a character class "[...]" in regex #${regexIndex}`); 263 | return text; 264 | } 265 | 266 | if (groupNameColon) { 267 | let toHide = groupName.charAt(0) === '~'; 268 | 269 | if (toHide) { 270 | groupName = groupName.substr(1); 271 | } 272 | 273 | let originalGroupName = groupName; 274 | let suffixNumber = 2; 275 | 276 | while (groupName in groupNameToIndex) { 277 | groupName = originalGroupName + suffixNumber++; 278 | } 279 | 280 | if (toHide) { 281 | groupNameHideMap[groupName] = undefined; 282 | } 283 | 284 | bracketDepth++; 285 | partialGroupCount++; 286 | groupNameToIndex[groupName] = groupCount + partialGroupCount; 287 | return '('; 288 | } else if (groupName in groupNameToIndex) { 289 | let index = groupNameToIndex[groupName]; 290 | return digitFollowsBR ? `(?:\\${index})` : `\\${index}`; 291 | } else { 292 | throw new Error( 293 | `Undefined group name "${groupName}" in regex #${regexIndex}`, 294 | ); 295 | } 296 | } 297 | 298 | if (braWithQ) { 299 | if (!sBraOpen) { 300 | bracketDepth++; 301 | } 302 | 303 | return text; 304 | } 305 | 306 | if (bra) { 307 | if (!sBraOpen) { 308 | bracketDepth++; 309 | partialGroupCount++; 310 | } 311 | 312 | return text; 313 | } 314 | 315 | if (ket) { 316 | if (!sBraOpen) { 317 | bracketDepth--; 318 | } 319 | 320 | return text; 321 | } 322 | 323 | if (or) { 324 | if (!hasOrOutside && !sBraOpen && bracketDepth === 0) { 325 | hasOrOutside = true; 326 | } 327 | 328 | return text; 329 | } 330 | 331 | if (sBra) { 332 | if (!sBraOpen) { 333 | sBraOpen = true; 334 | } 335 | 336 | return text; 337 | } 338 | 339 | if (sKet) { 340 | if (sBraOpen) { 341 | sBraOpen = false; 342 | } 343 | 344 | return text; 345 | } 346 | 347 | if (brNumber) { 348 | let index = Number(brNumber); 349 | index += groupCount; 350 | return `\\${index}`; 351 | } 352 | 353 | return text; 354 | }, 355 | ); 356 | 357 | groupCount += partialGroupCount; 358 | 359 | return !upperOr && hasOrOutside 360 | ? `(?:${partialRegexStr})` 361 | : partialRegexStr; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/library/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Regular Expression Tools 3 | * https://github.com/vilic/regex-tools 4 | */ 5 | 6 | export * from './combine'; 7 | export * from './regex-tools'; 8 | -------------------------------------------------------------------------------- /src/library/regex-tools.ts: -------------------------------------------------------------------------------- 1 | import * as FS from 'fs'; 2 | import * as Path from 'path'; 3 | 4 | import {Dict} from 'tslang'; 5 | 6 | import {CombinedResult, NestedRegexes, combine} from './combine'; 7 | 8 | export interface RegexToolsOptions { 9 | name: string; 10 | target: string; 11 | operation: 'combine'; 12 | flags?: string; 13 | regexes: NestedRegexes; 14 | } 15 | 16 | interface OutputCache { 17 | original: string; 18 | text: string; 19 | } 20 | 21 | interface TextStyle { 22 | indent: string; 23 | newLine: string; 24 | } 25 | 26 | const REGEX_LITERAL_REGEX = /(\/(?:[^\r\n\u2028\u2029*/\[\\]|\\[^\r\n\u2028\u2029]|\[(?:[^\r\n\u2028\u2029\]\\]|\\[^\r\n\u2028\u2029])*\])(?:[^\r\n\u2028\u2029/\[\\]|\\[^\r\n\u2028\u2029]|\[(?:[^\r\n\u2028\u2029\]\\]|\\[^\r\n\u2028\u2029])*\])*\/[gimy]{0,4})/; 27 | const PARAMS_REGEX = /(\((?:(\s*)([\w$][\w\d$]*)(?:\s*:\s*string)?)?(?:(\s*,\s*)[^)]*\S)?(\s*)\))/; 28 | const GROUPS_REGEX = /(var|let|const)\s+([\w$][\w\d$]*)\s*=\s*($groupName:[\w$][\w\d$]*)\s*\[\s*(\d+)\s*\]\s*;(?:\s*(?:var|let)\s+[\w$][\w\d$]*\s*=\s*($groupName)\s*\[\s*\d+\s*\]\s*;)*/; 29 | const ENUM_REGEX = /(const\s+)?enum\s+([\w$][\w\d$]*)\s*\{[^}]*\}/; 30 | 31 | export function process(path: string, skipWrite = false): string | string[] { 32 | path = Path.resolve(path); 33 | let dir = Path.dirname(path); 34 | 35 | let module = require(path); 36 | 37 | let regexToolsOptions: RegexToolsOptions | RegexToolsOptions[] = 38 | module.default || module.options || module; 39 | 40 | let optionGroups = Array.isArray(regexToolsOptions) 41 | ? regexToolsOptions 42 | : [regexToolsOptions]; 43 | 44 | let cacheMap: Dict = {}; 45 | let targets: string[] = []; 46 | 47 | for (let options of optionGroups) { 48 | let {name, target, operation, flags, regexes} = options; 49 | 50 | target = Path.resolve(dir, target); 51 | targets.push(target); 52 | 53 | let cache = cacheMap[target]; 54 | 55 | let text: string; 56 | 57 | if (cache) { 58 | text = cache.text; 59 | } else { 60 | text = FS.readFileSync(target, 'utf-8'); 61 | 62 | cache = cacheMap[target] = { 63 | original: text, 64 | text, 65 | }; 66 | } 67 | 68 | let {newLine, indent} = detectTextStyle(text); 69 | 70 | let result: CombinedResult; 71 | 72 | switch (operation) { 73 | case undefined: 74 | case 'combine': 75 | result = combine(regexes); 76 | break; 77 | default: 78 | continue; 79 | } 80 | 81 | let matcherCommentRegex = new RegExp( 82 | `([ \\t]*)(/\\*\\s*/\\$${name}/\\s*\\*/\\s*)`, 83 | ); 84 | 85 | let matcherRegex = eval( 86 | combine([ 87 | matcherCommentRegex, 88 | { 89 | regexes: [ 90 | REGEX_LITERAL_REGEX, 91 | PARAMS_REGEX, 92 | GROUPS_REGEX, 93 | ENUM_REGEX, 94 | ], 95 | or: true, 96 | }, 97 | ]).getRegexLiteral('g'), 98 | ) as RegExp; 99 | 100 | let updatedText = text.replace( 101 | matcherRegex, 102 | ( 103 | text: string, 104 | lineIndent: string, 105 | prefix: string, 106 | literal: string, 107 | params: string, 108 | whitespacesBeforeParams: string, 109 | firstParamName: string, 110 | separatorBetweenParams: string, 111 | whitespacesAfterParams: string, 112 | groupDeclarationsKeyword: string, 113 | firstGroupName: string, 114 | groupArrayName: string, 115 | firstGroupIndex: string, 116 | constEnum: string, 117 | enumName: string, 118 | ) => { 119 | if (literal) { 120 | return `${lineIndent}${prefix}${result.getRegexLiteral(flags)}`; 121 | } else if (params) { 122 | let separator = whitespacesBeforeParams 123 | ? `,${whitespacesBeforeParams}` 124 | : separatorBetweenParams || ', '; 125 | 126 | return `${lineIndent}${prefix}(${whitespacesBeforeParams}${result.getParametersSnippet( 127 | { 128 | typed: /\.ts$/i.test(target), 129 | matchName: firstParamName, 130 | separator, 131 | }, 132 | )}${whitespacesAfterParams})`; 133 | } else if (groupDeclarationsKeyword) { 134 | return `${lineIndent}${prefix}${result.getGroupAliasDeclarationsSnippet( 135 | { 136 | useLet: groupDeclarationsKeyword === 'let', 137 | arrayName: groupArrayName, 138 | newLine, 139 | lineIndent, 140 | matchName: firstGroupIndex === '0' ? firstGroupName : undefined, 141 | }, 142 | )}`; 143 | } else if (enumName) { 144 | return `${lineIndent}${prefix}${result.getEnumDeclaration({ 145 | useConst: !!constEnum, 146 | name: enumName, 147 | newLine, 148 | lineIndent, 149 | indent, 150 | })}`; 151 | } else { 152 | return text; 153 | } 154 | }, 155 | ); 156 | 157 | if (updatedText !== text) { 158 | cache.text = updatedText; 159 | } 160 | } 161 | 162 | if (!skipWrite) { 163 | for (let path of Object.keys(cacheMap)) { 164 | let cache = cacheMap[path]; 165 | 166 | if (cache.text !== cache.original) { 167 | FS.writeFileSync(path, cache.text); 168 | } 169 | } 170 | } 171 | 172 | let updatedTexts = targets.map(target => cacheMap[target].text); 173 | 174 | return Array.isArray(regexToolsOptions) ? updatedTexts : updatedTexts[0]; 175 | } 176 | 177 | function detectTextStyle(text: string): TextStyle { 178 | let indentSpaces: string[] = text.match(/^[ \t]+/gm) || []; 179 | let tabCount = 0; 180 | 181 | let lastIndentLength: number | undefined; 182 | 183 | let lengthToCount: number[] = []; 184 | 185 | for (let indentSpace of indentSpaces) { 186 | if (/\t/.test(indentSpace)) { 187 | tabCount++; 188 | } else { 189 | let length = indentSpace.length; 190 | 191 | if (lastIndentLength !== undefined && length > lastIndentLength) { 192 | let indentDiff = length - lastIndentLength; 193 | lengthToCount[indentDiff] = (lengthToCount[indentDiff] || 0) + 1; 194 | } 195 | 196 | lastIndentLength = length; 197 | } 198 | } 199 | 200 | let indent: string; 201 | 202 | if (tabCount < indentSpaces.length / 2) { 203 | let indentInfos = lengthToCount 204 | .map((count, length) => ({ 205 | count, 206 | length, 207 | })) 208 | .sort((a, b) => b.count - a.count); 209 | 210 | if (indentInfos.length) { 211 | indent = Array(indentInfos[0].length + 1).join(' '); 212 | } else { 213 | indent = ' '; 214 | } 215 | } else { 216 | indent = '\t'; 217 | } 218 | 219 | let newLine = 220 | (text.match(/\r/g) || []).length / (text.match(/\n/g) || []).length < 0.5 221 | ? '\n' 222 | : '\r\n'; 223 | 224 | return { 225 | indent, 226 | newLine, 227 | }; 228 | } 229 | -------------------------------------------------------------------------------- /src/library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@magicspace/configs/tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | 6 | "types": ["node"], 7 | 8 | "declaration": true, 9 | "declarationMap": true, 10 | 11 | "rootDir": ".", 12 | "outDir": "../../bld/library" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/library/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@magicspace/configs/tslint-prettier" 3 | } 4 | -------------------------------------------------------------------------------- /src/test/test.ts: -------------------------------------------------------------------------------- 1 | import {Dict} from 'tslang'; 2 | 3 | import * as RegexTools from '../library'; 4 | 5 | interface CombineTestCase { 6 | regexes: RegexTools.NestedRegexes; 7 | expect: RegExp; 8 | } 9 | 10 | let caseCategoryMap: Dict> = { 11 | '(?:...) wrapping': { 12 | 'should not wrap root |': [ 13 | { 14 | regexes: { 15 | regexes: [/abc/, /def/], 16 | or: true, 17 | }, 18 | expect: /abc|def/, 19 | }, 20 | ], 21 | 'should wrap combined regex that has | if its upper has no |': [ 22 | { 23 | regexes: [ 24 | /biu/, 25 | { 26 | regexes: [/abc/, /def/], 27 | or: true, 28 | }, 29 | /pia/, 30 | ], 31 | expect: /biu(?:abc|def)pia/, 32 | }, 33 | ], 34 | 'should not wrap combined regex that has | if its upper has |': [ 35 | { 36 | regexes: { 37 | regexes: [ 38 | /biu/, 39 | { 40 | regexes: [/abc/, /def/], 41 | or: true, 42 | }, 43 | /pia/, 44 | ], 45 | or: true, 46 | }, 47 | expect: /biu|abc|def|pia/, 48 | }, 49 | ], 50 | "should not wrap combined regex that has | if it's already been wrapped": [ 51 | { 52 | regexes: [ 53 | /biu/, 54 | { 55 | regexes: [/abc/, /def/], 56 | or: true, 57 | capture: true, 58 | }, 59 | /pia/, 60 | ], 61 | expect: /biu(abc|def)pia/, 62 | }, 63 | { 64 | regexes: [ 65 | /biu/, 66 | { 67 | regexes: [/(abc|def)/], 68 | or: true, 69 | }, 70 | /pia/, 71 | ], 72 | expect: /biu(abc|def)pia/, 73 | }, 74 | ], 75 | 'should wrap or not wrap combined regex that has | based on conditions': [ 76 | { 77 | regexes: [ 78 | /biu/, 79 | { 80 | regexes: [ 81 | /abc/, 82 | /def/, 83 | { 84 | regexes: [/ghi/, /jkl/], 85 | or: true, 86 | }, 87 | ], 88 | or: true, 89 | }, 90 | /pia/, 91 | ], 92 | expect: /biu(?:abc|def|ghi|jkl)pia/, 93 | }, 94 | ], 95 | 'should wrap single regex that has root | and its upper has no |': [ 96 | { 97 | regexes: [/biu/, /abc|def/, /pia/], 98 | expect: /biu(?:abc|def)pia/, 99 | }, 100 | ], 101 | 'should not wrap single regex that has root | but its upper has no |': [ 102 | { 103 | regexes: [/biu/, /abc|def/, /pia/], 104 | expect: /biu(?:abc|def)pia/, 105 | }, 106 | ], 107 | 'should wrap regex that has repeat pattern': [ 108 | { 109 | regexes: { 110 | regexes: [/biu/, /pia/], 111 | repeat: '+', 112 | }, 113 | expect: /(?:biupia)+/, 114 | }, 115 | ], 116 | 'should not wrap zero-length pattern': [ 117 | { 118 | regexes: [/biu/, /(?:)/], 119 | expect: /biu/, 120 | }, 121 | { 122 | regexes: [], 123 | expect: /(?:)/, 124 | }, 125 | ], 126 | 'should not wrap one-char repeat pattern': [ 127 | { 128 | regexes: { 129 | regexes: [/[biu]/], 130 | repeat: '+', 131 | }, 132 | expect: /[biu]+/, 133 | }, 134 | { 135 | regexes: { 136 | regexes: [/[bi[\]u]/], 137 | repeat: '+', 138 | }, 139 | expect: /[bi[\]u]+/, 140 | }, 141 | { 142 | regexes: { 143 | regexes: [/!/], 144 | repeat: '+', 145 | }, 146 | expect: /!+/, 147 | }, 148 | { 149 | regexes: { 150 | regexes: [/\n/], 151 | repeat: '+', 152 | }, 153 | expect: /\n+/, 154 | }, 155 | { 156 | regexes: { 157 | regexes: [/\u0000/], 158 | repeat: '+', 159 | }, 160 | expect: /\u0000+/, 161 | }, 162 | { 163 | regexes: { 164 | regexes: [/\x00/], 165 | repeat: '+', 166 | }, 167 | expect: /\x00+/, 168 | }, 169 | ], 170 | }, 171 | 'group capturing': { 172 | 'should capture group that has name option': [ 173 | { 174 | regexes: { 175 | name: 'abc', 176 | regexes: [ 177 | /abc/, 178 | /def/, 179 | { 180 | regexes: [/ghi/, /jkl/], 181 | or: true, 182 | }, 183 | ], 184 | }, 185 | expect: /(abcdef(?:ghi|jkl))/, 186 | }, 187 | { 188 | regexes: [ 189 | { 190 | name: 'abc', 191 | regexes: /def/, 192 | }, 193 | ], 194 | expect: /(def)/, 195 | }, 196 | ], 197 | 'should capture group that has capture option true': [ 198 | { 199 | regexes: { 200 | regexes: [ 201 | /abc/, 202 | /def/, 203 | { 204 | regexes: [/ghi/, /jkl/], 205 | or: true, 206 | }, 207 | ], 208 | capture: true, 209 | }, 210 | expect: /(abcdef(?:ghi|jkl))/, 211 | }, 212 | { 213 | regexes: [ 214 | { 215 | regexes: /def/, 216 | capture: true, 217 | }, 218 | ], 219 | expect: /(def)/, 220 | }, 221 | ], 222 | }, 223 | 'group matching': { 224 | 'should increase back reference number if it has other groups captured before': [ 225 | { 226 | regexes: [/a($name:b)c(def)/, /([abc])\1/], 227 | expect: /a(b)c(def)([abc])\3/, 228 | }, 229 | ], 230 | 'should handle back reference by named group properly': [ 231 | { 232 | regexes: [/a($name:b)c(def)/, /([abc])($name)/], 233 | expect: /a(b)c(def)([abc])\1/, 234 | }, 235 | { 236 | regexes: [ 237 | /a($name:b)c(def)/, 238 | { 239 | regexes: [/xxx/, /yyy/, [/zzz/, /vvv($name)/]], 240 | or: true, 241 | }, 242 | ], 243 | expect: /a(b)c(def)(?:xxx|yyy|zzzvvv\1)/, 244 | }, 245 | ], 246 | "should handle back reference number that's greater than 9": [ 247 | { 248 | regexes: [/()()/, /()()()()()()()()()()(...)\11/], 249 | expect: /()()()()()()()()()()()()(...)\13/, 250 | }, 251 | { 252 | regexes: [ 253 | /()()()()()()()()()()($name:...)/, 254 | { 255 | regexes: [/abc/, /($name)/], 256 | or: true, 257 | }, 258 | ], 259 | expect: /()()()()()()()()()()(...)(?:abc|\11)/, 260 | }, 261 | { 262 | regexes: [ 263 | /()()()()()()()()()()($name:...)/, 264 | { 265 | regexes: [/abc/, /($name)123/], 266 | or: true, 267 | }, 268 | ], 269 | expect: /()()()()()()()()()()(...)(?:abc|(?:\11)123)/, 270 | }, 271 | ], 272 | }, 273 | lookahead: { 274 | 'should handle lookahead': [ 275 | { 276 | regexes: { 277 | regexes: [/abc/, /def/], 278 | lookahead: true, 279 | }, 280 | expect: /(?=abcdef)/, 281 | }, 282 | { 283 | regexes: { 284 | regexes: [/abc/, /def/], 285 | lookahead: '=', 286 | }, 287 | expect: /(?=abcdef)/, 288 | }, 289 | ], 290 | 'should handle negative lookahead': [ 291 | { 292 | regexes: { 293 | regexes: [/abc/, /def/], 294 | lookahead: '!', 295 | }, 296 | expect: /(?!abcdef)/, 297 | }, 298 | ], 299 | 'should handle lookahead that captures': [ 300 | { 301 | regexes: [ 302 | { 303 | name: 'abc', 304 | regexes: /def/, 305 | lookahead: '=', 306 | }, 307 | ], 308 | expect: /(?=(def))/, 309 | }, 310 | ], 311 | }, 312 | }; 313 | 314 | for (let caseCategoryName of Object.keys(caseCategoryMap)) { 315 | describe(caseCategoryName, () => { 316 | let caseCategory = caseCategoryMap[caseCategoryName]; 317 | 318 | for (let caseName of Object.keys(caseCategory)) { 319 | it(caseName, () => { 320 | for (let testCase of caseCategory[caseName]) { 321 | RegexTools.combine(testCase.regexes).combined.should.equal( 322 | testCase.expect.source, 323 | ); 324 | } 325 | }); 326 | } 327 | }); 328 | } 329 | -------------------------------------------------------------------------------- /src/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@magicspace/configs/tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | 6 | "types": ["mocha", "chai"], 7 | 8 | "rootDir": ".", 9 | "outDir": "../../bld/test" 10 | }, 11 | "references": [ 12 | { 13 | "path": "../library" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/test/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@magicspace/configs/tslint-prettier", 4 | "@magicspace/configs/tslint-override-dev" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/tsconfig.all.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [], 3 | "references": [{"path": "library"}, {"path": "test"}] 4 | } 5 | -------------------------------------------------------------------------------- /test/mocha.js: -------------------------------------------------------------------------------- 1 | require('source-map-support/register'); 2 | require('chai').should(); 3 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require ./test/mocha 2 | --recursive 3 | 4 | bld/test 5 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@magicspace/configs@^0.1.46": 6 | version "0.1.46" 7 | resolved "http://registry.npm.taobao.org/@magicspace/configs/download/@magicspace/configs-0.1.46.tgz#57b5324068a7eac9fcd0af2be0c8ba3f8bbd0435" 8 | integrity sha1-V7UyQGin6sn80K8r4Mi6P4u9BDU= 9 | dependencies: 10 | "@magicspace/tslint-rules" "^0.1.41" 11 | tslint-config-prettier "^1.10.0" 12 | 13 | "@magicspace/tslint-rules@^0.1.41": 14 | version "0.1.41" 15 | resolved "http://registry.npm.taobao.org/@magicspace/tslint-rules/download/@magicspace/tslint-rules-0.1.41.tgz#200e36f56cadd89d40d81697956ff88786565bae" 16 | integrity sha1-IA429Wyt2J1A2BaXlW/4h4ZWW64= 17 | dependencies: 18 | lodash "^4.17.10" 19 | module-lens "^0.1.3" 20 | resolve "^1.8.1" 21 | tslang "^0.1.9" 22 | tsutils "^2.28.0" 23 | 24 | "@types/chai@^4.1.7": 25 | version "4.1.7" 26 | resolved "http://registry.npm.taobao.org/@types/chai/download/@types/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" 27 | integrity sha1-G44zthqMCcvh+FEzBxuqDb+fpxo= 28 | 29 | "@types/mocha@^5.2.5": 30 | version "5.2.5" 31 | resolved "http://registry.npm.taobao.org/@types/mocha/download/@types/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" 32 | integrity sha1-ikrM/EA8EkoLr+ip/GGgXsEDIHM= 33 | 34 | "@types/node@^10.12.18": 35 | version "10.12.18" 36 | resolved "http://registry.npm.taobao.org/@types/node/download/@types/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" 37 | integrity sha1-HTynZHGJFVhPzZ9jRGIbdnJmXGc= 38 | 39 | ansi-regex@^2.0.0: 40 | version "2.1.1" 41 | resolved "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 42 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 43 | 44 | ansi-styles@^2.2.1: 45 | version "2.2.1" 46 | resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 47 | integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= 48 | 49 | ansi-styles@^3.2.1: 50 | version "3.2.1" 51 | resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 52 | integrity sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0= 53 | dependencies: 54 | color-convert "^1.9.0" 55 | 56 | argparse@^1.0.7: 57 | version "1.0.10" 58 | resolved "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 59 | integrity sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE= 60 | dependencies: 61 | sprintf-js "~1.0.2" 62 | 63 | assertion-error@^1.1.0: 64 | version "1.1.0" 65 | resolved "http://registry.npm.taobao.org/assertion-error/download/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 66 | integrity sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs= 67 | 68 | babel-code-frame@^6.22.0: 69 | version "6.26.0" 70 | resolved "http://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 71 | integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= 72 | dependencies: 73 | chalk "^1.1.3" 74 | esutils "^2.0.2" 75 | js-tokens "^3.0.2" 76 | 77 | balanced-match@^1.0.0: 78 | version "1.0.0" 79 | resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 80 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 81 | 82 | brace-expansion@^1.1.7: 83 | version "1.1.11" 84 | resolved "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 85 | integrity sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0= 86 | dependencies: 87 | balanced-match "^1.0.0" 88 | concat-map "0.0.1" 89 | 90 | browser-stdout@1.3.1: 91 | version "1.3.1" 92 | resolved "http://registry.npm.taobao.org/browser-stdout/download/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 93 | integrity sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA= 94 | 95 | buffer-from@^1.0.0: 96 | version "1.1.1" 97 | resolved "http://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 98 | integrity sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8= 99 | 100 | builtin-modules@^1.1.1: 101 | version "1.1.1" 102 | resolved "http://registry.npm.taobao.org/builtin-modules/download/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 103 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 104 | 105 | caller-id@^0.1.0: 106 | version "0.1.0" 107 | resolved "http://registry.npm.taobao.org/caller-id/download/caller-id-0.1.0.tgz#59bdac0893d12c3871408279231f97458364f07b" 108 | integrity sha1-Wb2sCJPRLDhxQIJ5Ix+XRYNk8Hs= 109 | dependencies: 110 | stack-trace "~0.0.7" 111 | 112 | chai@^4.2.0: 113 | version "4.2.0" 114 | resolved "http://registry.npm.taobao.org/chai/download/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" 115 | integrity sha1-dgqnLPION5XoSxKHfODoNzeqKeU= 116 | dependencies: 117 | assertion-error "^1.1.0" 118 | check-error "^1.0.2" 119 | deep-eql "^3.0.1" 120 | get-func-name "^2.0.0" 121 | pathval "^1.1.0" 122 | type-detect "^4.0.5" 123 | 124 | chalk@^1.1.3: 125 | version "1.1.3" 126 | resolved "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 127 | integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= 128 | dependencies: 129 | ansi-styles "^2.2.1" 130 | escape-string-regexp "^1.0.2" 131 | has-ansi "^2.0.0" 132 | strip-ansi "^3.0.0" 133 | supports-color "^2.0.0" 134 | 135 | chalk@^2.3.0: 136 | version "2.4.1" 137 | resolved "http://registry.npm.taobao.org/chalk/download/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 138 | integrity sha1-GMSasWoDe26wFSzIPjRxM4IVtm4= 139 | dependencies: 140 | ansi-styles "^3.2.1" 141 | escape-string-regexp "^1.0.5" 142 | supports-color "^5.3.0" 143 | 144 | check-error@^1.0.2: 145 | version "1.0.2" 146 | resolved "http://registry.npm.taobao.org/check-error/download/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 147 | integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= 148 | 149 | color-convert@^1.9.0: 150 | version "1.9.3" 151 | resolved "http://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 152 | integrity sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg= 153 | dependencies: 154 | color-name "1.1.3" 155 | 156 | color-name@1.1.3: 157 | version "1.1.3" 158 | resolved "http://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 159 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 160 | 161 | commander@2.15.1: 162 | version "2.15.1" 163 | resolved "http://registry.npm.taobao.org/commander/download/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" 164 | integrity sha1-30boZ9D8Kuxmo0ZitAapzK//Ww8= 165 | 166 | commander@^2.12.1: 167 | version "2.19.0" 168 | resolved "http://registry.npm.taobao.org/commander/download/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" 169 | integrity sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So= 170 | 171 | concat-map@0.0.1: 172 | version "0.0.1" 173 | resolved "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 174 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 175 | 176 | debug@3.1.0: 177 | version "3.1.0" 178 | resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 179 | integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE= 180 | dependencies: 181 | ms "2.0.0" 182 | 183 | deep-eql@^3.0.1: 184 | version "3.0.1" 185 | resolved "http://registry.npm.taobao.org/deep-eql/download/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 186 | integrity sha1-38lARACtHI/gI+faHfHBR8S0RN8= 187 | dependencies: 188 | type-detect "^4.0.0" 189 | 190 | diff@3.5.0, diff@^3.2.0: 191 | version "3.5.0" 192 | resolved "http://registry.npm.taobao.org/diff/download/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 193 | integrity sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI= 194 | 195 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 196 | version "1.0.5" 197 | resolved "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 198 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 199 | 200 | esprima@^4.0.0: 201 | version "4.0.1" 202 | resolved "http://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 203 | integrity sha1-E7BM2z5sXRnfkatph6hpVhmwqnE= 204 | 205 | esutils@^2.0.2: 206 | version "2.0.2" 207 | resolved "http://registry.npm.taobao.org/esutils/download/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 208 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 209 | 210 | fs.realpath@^1.0.0: 211 | version "1.0.0" 212 | resolved "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 213 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 214 | 215 | get-func-name@^2.0.0: 216 | version "2.0.0" 217 | resolved "http://registry.npm.taobao.org/get-func-name/download/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 218 | integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= 219 | 220 | glob@7.1.2: 221 | version "7.1.2" 222 | resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 223 | integrity sha1-wZyd+aAocC1nhhI4SmVSQExjbRU= 224 | dependencies: 225 | fs.realpath "^1.0.0" 226 | inflight "^1.0.4" 227 | inherits "2" 228 | minimatch "^3.0.4" 229 | once "^1.3.0" 230 | path-is-absolute "^1.0.0" 231 | 232 | glob@^7.0.5, glob@^7.1.1: 233 | version "7.1.3" 234 | resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 235 | integrity sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE= 236 | dependencies: 237 | fs.realpath "^1.0.0" 238 | inflight "^1.0.4" 239 | inherits "2" 240 | minimatch "^3.0.4" 241 | once "^1.3.0" 242 | path-is-absolute "^1.0.0" 243 | 244 | growl@1.10.5: 245 | version "1.10.5" 246 | resolved "http://registry.npm.taobao.org/growl/download/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" 247 | integrity sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4= 248 | 249 | has-ansi@^2.0.0: 250 | version "2.0.0" 251 | resolved "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 252 | integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= 253 | dependencies: 254 | ansi-regex "^2.0.0" 255 | 256 | has-flag@^3.0.0: 257 | version "3.0.0" 258 | resolved "http://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 259 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 260 | 261 | he@1.1.1: 262 | version "1.1.1" 263 | resolved "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 264 | integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= 265 | 266 | inflight@^1.0.4: 267 | version "1.0.6" 268 | resolved "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 269 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 270 | dependencies: 271 | once "^1.3.0" 272 | wrappy "1" 273 | 274 | inherits@2: 275 | version "2.0.3" 276 | resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 277 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 278 | 279 | js-tokens@^3.0.2: 280 | version "3.0.2" 281 | resolved "http://registry.npm.taobao.org/js-tokens/download/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 282 | integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= 283 | 284 | js-yaml@^3.7.0: 285 | version "3.12.0" 286 | resolved "http://registry.npm.taobao.org/js-yaml/download/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 287 | integrity sha1-6u1lbsg0TxD1J8a/obbiJE3hZ9E= 288 | dependencies: 289 | argparse "^1.0.7" 290 | esprima "^4.0.0" 291 | 292 | lodash@^4.17.10, lodash@^4.17.11: 293 | version "4.17.11" 294 | resolved "http://registry.npm.taobao.org/lodash/download/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 295 | integrity sha1-s56mIp72B+zYniyN8SU2iRysm40= 296 | 297 | minimatch@3.0.4, minimatch@^3.0.4: 298 | version "3.0.4" 299 | resolved "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 300 | integrity sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM= 301 | dependencies: 302 | brace-expansion "^1.1.7" 303 | 304 | minimist@0.0.8: 305 | version "0.0.8" 306 | resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 307 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 308 | 309 | mkdirp@0.5.1: 310 | version "0.5.1" 311 | resolved "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 312 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 313 | dependencies: 314 | minimist "0.0.8" 315 | 316 | mocha@^5.2.0: 317 | version "5.2.0" 318 | resolved "http://registry.npm.taobao.org/mocha/download/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" 319 | integrity sha1-bYrlCPWRZ/lA8rWzxKYSrlDJCuY= 320 | dependencies: 321 | browser-stdout "1.3.1" 322 | commander "2.15.1" 323 | debug "3.1.0" 324 | diff "3.5.0" 325 | escape-string-regexp "1.0.5" 326 | glob "7.1.2" 327 | growl "1.10.5" 328 | he "1.1.1" 329 | minimatch "3.0.4" 330 | mkdirp "0.5.1" 331 | supports-color "5.4.0" 332 | 333 | mock-require@^2.0.2: 334 | version "2.0.2" 335 | resolved "http://registry.npm.taobao.org/mock-require/download/mock-require-2.0.2.tgz#1eaa71aad23013773d127dc7e91a3fbb4837d60d" 336 | integrity sha1-HqpxqtIwE3c9En3H6Ro/u0g31g0= 337 | dependencies: 338 | caller-id "^0.1.0" 339 | 340 | module-lens@^0.1.3: 341 | version "0.1.3" 342 | resolved "http://registry.npm.taobao.org/module-lens/download/module-lens-0.1.3.tgz#4f078a653fdb9987421dac1ac0cdf786ff836417" 343 | integrity sha1-TweKZT/bmYdCHawawM33hv+DZBc= 344 | dependencies: 345 | lodash "^4.17.11" 346 | 347 | ms@2.0.0: 348 | version "2.0.0" 349 | resolved "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 350 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 351 | 352 | once@^1.3.0: 353 | version "1.4.0" 354 | resolved "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 355 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 356 | dependencies: 357 | wrappy "1" 358 | 359 | path-is-absolute@^1.0.0: 360 | version "1.0.1" 361 | resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 362 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 363 | 364 | path-parse@^1.0.6: 365 | version "1.0.6" 366 | resolved "http://registry.npm.taobao.org/path-parse/download/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 367 | integrity sha1-1i27VnlAXXLEc37FhgDp3c8G0kw= 368 | 369 | pathval@^1.1.0: 370 | version "1.1.0" 371 | resolved "http://registry.npm.taobao.org/pathval/download/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 372 | integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= 373 | 374 | prettier@^1.15.3: 375 | version "1.15.3" 376 | resolved "http://registry.npm.taobao.org/prettier/download/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" 377 | integrity sha1-H+qsW90YEje1Tb5l2HTgKhRyeGo= 378 | 379 | resolve@^1.3.2, resolve@^1.8.1: 380 | version "1.9.0" 381 | resolved "http://registry.npm.taobao.org/resolve/download/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" 382 | integrity sha1-oUxv36j5Kn3x2ZbLcQX6dEZY6gY= 383 | dependencies: 384 | path-parse "^1.0.6" 385 | 386 | rimraf@^2.6.2: 387 | version "2.6.2" 388 | resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 389 | integrity sha1-LtgVDSShbqhlHm1u8PR8QVjOejY= 390 | dependencies: 391 | glob "^7.0.5" 392 | 393 | semver@^5.3.0: 394 | version "5.6.0" 395 | resolved "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" 396 | integrity sha1-fnQlb7qknHWqfHogXMInmcrIAAQ= 397 | 398 | source-map-support@^0.5.9: 399 | version "0.5.9" 400 | resolved "http://registry.npm.taobao.org/source-map-support/download/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" 401 | integrity sha1-QbyVOyU0Jn6i1gW8z6e/oxEc7V8= 402 | dependencies: 403 | buffer-from "^1.0.0" 404 | source-map "^0.6.0" 405 | 406 | source-map@^0.6.0: 407 | version "0.6.1" 408 | resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 409 | integrity sha1-dHIq8y6WFOnCh6jQu95IteLxomM= 410 | 411 | sprintf-js@~1.0.2: 412 | version "1.0.3" 413 | resolved "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 414 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 415 | 416 | stack-trace@~0.0.7: 417 | version "0.0.10" 418 | resolved "http://registry.npm.taobao.org/stack-trace/download/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" 419 | integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= 420 | 421 | strip-ansi@^3.0.0: 422 | version "3.0.1" 423 | resolved "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 424 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 425 | dependencies: 426 | ansi-regex "^2.0.0" 427 | 428 | supports-color@5.4.0: 429 | version "5.4.0" 430 | resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 431 | integrity sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q= 432 | dependencies: 433 | has-flag "^3.0.0" 434 | 435 | supports-color@^2.0.0: 436 | version "2.0.0" 437 | resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 438 | integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= 439 | 440 | supports-color@^5.3.0: 441 | version "5.5.0" 442 | resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 443 | integrity sha1-4uaaRKyHcveKHsCzW2id9lMO/I8= 444 | dependencies: 445 | has-flag "^3.0.0" 446 | 447 | tslang@^0.1.11, tslang@^0.1.9: 448 | version "0.1.11" 449 | resolved "http://registry.npm.taobao.org/tslang/download/tslang-0.1.11.tgz#8ec5599ce2662aa25fdaadbbf88b9b2655f56a19" 450 | integrity sha1-jsVZnOJmKqJf2q27+IubJlX1ahk= 451 | 452 | tslib@^1.8.0, tslib@^1.8.1: 453 | version "1.9.3" 454 | resolved "http://registry.npm.taobao.org/tslib/download/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" 455 | integrity sha1-1+TdeSRdhUKMTX5IIqeZF5VMooY= 456 | 457 | tslint-config-prettier@^1.10.0: 458 | version "1.17.0" 459 | resolved "http://registry.npm.taobao.org/tslint-config-prettier/download/tslint-config-prettier-1.17.0.tgz#946ed6117f98f3659a65848279156d87628c33dc" 460 | integrity sha1-lG7WEX+Y82WaZYSCeRVth2KMM9w= 461 | 462 | tslint-language-service@^0.9.9: 463 | version "0.9.9" 464 | resolved "http://registry.npm.taobao.org/tslint-language-service/download/tslint-language-service-0.9.9.tgz#f546dc38483979e6fb3cfa59584ad8525b3ad4da" 465 | integrity sha1-9UbcOEg5eeb7PPpZWErYUls61No= 466 | dependencies: 467 | mock-require "^2.0.2" 468 | 469 | tslint@^5.12.0: 470 | version "5.12.0" 471 | resolved "http://registry.npm.taobao.org/tslint/download/tslint-5.12.0.tgz#47f2dba291ed3d580752d109866fb640768fca36" 472 | integrity sha1-R/LbopHtPVgHUtEJhm+2QHaPyjY= 473 | dependencies: 474 | babel-code-frame "^6.22.0" 475 | builtin-modules "^1.1.1" 476 | chalk "^2.3.0" 477 | commander "^2.12.1" 478 | diff "^3.2.0" 479 | glob "^7.1.1" 480 | js-yaml "^3.7.0" 481 | minimatch "^3.0.4" 482 | resolve "^1.3.2" 483 | semver "^5.3.0" 484 | tslib "^1.8.0" 485 | tsutils "^2.27.2" 486 | 487 | tsutils@^2.27.2, tsutils@^2.28.0: 488 | version "2.29.0" 489 | resolved "http://registry.npm.taobao.org/tsutils/download/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" 490 | integrity sha1-MrSIUBRnrL7dS4VJhnOggSrKC5k= 491 | dependencies: 492 | tslib "^1.8.1" 493 | 494 | type-detect@^4.0.0, type-detect@^4.0.5: 495 | version "4.0.8" 496 | resolved "http://registry.npm.taobao.org/type-detect/download/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 497 | integrity sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw= 498 | 499 | typescript@^3.2.2: 500 | version "3.2.2" 501 | resolved "http://registry.npm.taobao.org/typescript/download/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" 502 | integrity sha1-/oEBxGqhI/g1NSPr3PVzDCrkk+U= 503 | 504 | wrappy@1: 505 | version "1.0.2" 506 | resolved "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 507 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 508 | --------------------------------------------------------------------------------