Mirror: The magical sticky regex-based parser generator 🧙

Merge pull request #6 from kitten/feat/jit-runtime

Reimplement with JIT codegen for runtime-only support

authored by kitten.sh and committed by GitHub c5cf8d17 1d76d269

+47 -18
README.md
··· 10 10 <br /> 11 11 </div> 12 12 13 - Leveraging the power of sticky regexes and Babel code generation, `reghex` allows 13 + Leveraging the power of sticky regexes and JS code generation, `reghex` allows 14 14 you to code parsers quickly, by surrounding regular expressions with a regex-like 15 15 [DSL](https://en.wikipedia.org/wiki/Domain-specific_language). 16 16 ··· 30 30 npm install --save reghex 31 31 ``` 32 32 33 - ##### 2. Add the plugin to your Babel configuration (`.babelrc`, `babel.config.js`, or `package.json:babel`) 33 + ##### 2. Add the plugin to your Babel configuration _(optional)_ 34 + 35 + In your `.babelrc`, `babel.config.js`, or `package.json:babel` add: 34 36 35 37 ```json 36 38 { ··· 41 43 Alternatively, you can set up [`babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros) and 42 44 import `reghex` from `"reghex/macro"` instead. 43 45 46 + This step is **optional**. `reghex` can also generate its optimised JS code during runtime. 47 + This will only incur a tiny parsing cost on initialisation, but due to the JIT of modern 48 + JS engines there won't be any difference in performance between pre-compiled and compiled 49 + versions otherwise. 50 + 51 + Since the `reghex` runtime is rather small, for larger grammars it may even make sense not 52 + to precompile the matchers at all. For this case you may pass the `{ "codegen": false }` 53 + option to the Babel plugin, which will minify the `reghex` matcher templates without 54 + precompiling them. 55 + 44 56 ##### 3. Have fun writing parsers! 45 57 46 58 ```js 47 - import match, { parse } from 'reghex'; 59 + import { match, parse } from 'reghex'; 48 60 49 61 const name = match('name')` 50 62 ${/\w+/} ··· 99 111 100 112 ## Authoring Guide 101 113 102 - You can write "matchers" by importing the default import from `reghex` and 114 + You can write "matchers" by importing the `match` import from `reghex` and 103 115 using it to write a matcher expression. 104 116 105 117 ```js 106 - import match from 'reghex'; 118 + import { match } from 'reghex'; 107 119 108 120 const name = match('name')` 109 121 ${/\w+/} 110 122 `; 111 123 ``` 112 124 113 - As can be seen above, the `match` function, which is what we've called the 114 - default import, is called with a "node name" and is then called as a tagged 115 - template. This template is our **parsing definition**. 125 + As can be seen above, the `match` function, is called with a "node name" and 126 + is then called as a tagged template. This template is our **parsing definition**. 116 127 117 128 `reghex` functions only with its Babel plugin, which will detect `match('name')` 118 129 and replace the entire tag with a parsing function, which may then look like ··· 161 172 Let's extend our original example; 162 173 163 174 ```js 164 - import match from 'reghex'; 175 + import { match } from 'reghex'; 165 176 166 177 const name = match('name')` 167 178 ${/\w+/} ··· 193 204 */ 194 205 ``` 195 206 207 + Furthermore, interpolations don't have to just be RegHex matchers. They can 208 + also be functions returning matchers or completely custom matching functions. 209 + This is useful when your DSL becomes _self-referential_, i.e. when one matchers 210 + start referencing each other forming a loop. To fix this we can create a 211 + function that returns our root matcher: 212 + 213 + ```js 214 + import { match } from 'reghex'; 215 + 216 + const value = match('value')` 217 + (${/\w+/} | ${() => root})+ 218 + `; 219 + 220 + const root = match('root')` 221 + ${/root/}+ ${value} 222 + `; 223 + ``` 224 + 196 225 ### Regex-like DSL 197 226 198 227 We've seen in the previous examples that matchers are authored using tagged ··· 208 237 in the parsed string. This is just one feature of the regex-like DSL. The 209 238 available operators are the following: 210 239 211 - | Operator | Example | Description | 212 - | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 213 - | `?` | `${/1/}?` | An **optional** may be used to make an interpolation optional. This means that the interpolation may or may not match. | 214 - | `*` | `${/1/}*` | A **star** can be used to match an arbitrary amount of interpolation or none at all. This means that the interpolation may repeat itself or may not be matched at all. | 215 - | `+` | `${/1/}+` | A **plus** is used like `*` and must match one or more times. When the matcher doesn't match, that's considered a failing case, since the match isn't optional. | 216 - | `\|` | `${/1/} \| ${/2/}` | An **alternation** can be used to match either one thing or another, falling back when the first interpolation fails. | 217 - | `()` | `(${/1/} ${/2/})+` | A **group** can be used to apply one of the other operators to an entire group of interpolations. | 240 + | Operator | Example | Description | 241 + | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 242 + | `?` | `${/1/}?` | An **optional** may be used to make an interpolation optional. This means that the interpolation may or may not match. | 243 + | `*` | `${/1/}*` | A **star** can be used to match an arbitrary amount of interpolation or none at all. This means that the interpolation may repeat itself or may not be matched at all. | 244 + | `+` | `${/1/}+` | A **plus** is used like `*` and must match one or more times. When the matcher doesn't match, that's considered a failing case, since the match isn't optional. | 245 + | `\|` | `${/1/} \| ${/2/}` | An **alternation** can be used to match either one thing or another, falling back when the first interpolation fails. | 246 + | `()` | `(${/1/} ${/2/})+` | A **group** can be used to apply one of the other operators to an entire group of interpolations. | 218 247 | `(?: )` | `(?: ${/1/})` | A **non-capturing group** is like a regular group, but the interpolations matched inside it don't appear in the parser's output. | 219 - | `(?= )` | `(?= ${/1/})` | A **positive lookahead** checks whether interpolations match, and if so continues the matcher without changing the input. If it matches, it's essentially ignored. | 248 + | `(?= )` | `(?= ${/1/})` | A **positive lookahead** checks whether interpolations match, and if so continues the matcher without changing the input. If it matches, it's essentially ignored. | 220 249 | `(?! )` | `(?! ${/1/})` | A **negative lookahead** checks whether interpolations _don't_ match, and if so continues the matcher without changing the input. If the interpolations do match the matcher is aborted. | 221 250 222 251 We can combine and compose these operators to create more complex matchers. ··· 320 349 matched, which would cause other matchers to treat it like a mismatch! 321 350 322 351 ```js 323 - import match, { parse } from 'reghex'; 352 + import { match, parse } from 'reghex'; 324 353 325 354 const name = match('name')((x) => { 326 355 return x[0] !== 'tim' ? x : undefined;
+3 -2
package.json
··· 1 1 { 2 2 "name": "reghex", 3 - "version": "1.0.2", 3 + "version": "2.0.0-beta.4", 4 4 "description": "The magical sticky regex-based parser generator 🧙", 5 5 "author": "Phil Pluckthun <phil@kitten.sh>", 6 6 "license": "MIT", ··· 48 48 "url": "https://github.com/kitten/reghex/issues" 49 49 }, 50 50 "devDependencies": { 51 + "@ampproject/rollup-plugin-closure-compiler": "^0.26.0", 51 52 "@babel/core": "7.9.6", 52 53 "@babel/plugin-transform-modules-commonjs": "^7.9.6", 53 - "@babel/plugin-transform-object-assign": "^7.8.3", 54 54 "@rollup/plugin-buble": "^0.21.3", 55 55 "@rollup/plugin-commonjs": "^11.1.0", 56 56 "@rollup/plugin-node-resolve": "^7.1.3", 57 + "@rollup/pluginutils": "^4.1.0", 57 58 "babel-jest": "^26.0.1", 58 59 "babel-plugin-closure-elimination": "^1.3.1", 59 60 "husky": "^4.2.5",
+29 -12
rollup.config.js
··· 2 2 import resolve from '@rollup/plugin-node-resolve'; 3 3 import buble from '@rollup/plugin-buble'; 4 4 import babel from 'rollup-plugin-babel'; 5 + import compiler from '@ampproject/rollup-plugin-closure-compiler'; 6 + 7 + import simplifyJSTags from './scripts/simplify-jstags-plugin.js'; 5 8 6 9 const plugins = [ 7 10 commonjs({ ··· 18 21 transforms: { 19 22 unicodeRegExp: false, 20 23 dangerousForOf: true, 21 - dangerousTaggedTemplateString: true, 24 + templateString: false, 22 25 }, 23 - objectAssign: 'Object.assign', 24 26 exclude: 'node_modules/**', 25 27 }), 26 28 babel({ ··· 28 30 extensions: ['ts', 'tsx', 'js'], 29 31 exclude: 'node_modules/**', 30 32 presets: [], 31 - plugins: [ 32 - '@babel/plugin-transform-object-assign', 33 - 'babel-plugin-closure-elimination', 34 - ], 33 + plugins: ['babel-plugin-closure-elimination'], 35 34 }), 36 35 ]; 37 36 ··· 47 46 freeze: false, 48 47 strict: false, 49 48 format, 49 + plugins: [ 50 + simplifyJSTags(), 51 + compiler({ 52 + formatting: 'PRETTY_PRINT', 53 + compilation_level: 'SIMPLE_OPTIMIZATIONS', 54 + }), 55 + ], 50 56 }); 51 57 52 - export default { 53 - input: { 54 - core: './src/core.js', 55 - babel: './src/babel/plugin.js', 56 - macro: './src/babel/macro.js', 57 - }, 58 + const base = { 58 59 onwarn: () => {}, 59 60 external: () => false, 60 61 treeshake: { ··· 63 64 plugins, 64 65 output: [output('cjs', '.js'), output('esm', '.mjs')], 65 66 }; 67 + 68 + export default [ 69 + { 70 + ...base, 71 + input: { 72 + core: './src/core.js', 73 + }, 74 + }, 75 + { 76 + ...base, 77 + input: { 78 + babel: './src/babel/plugin.js', 79 + macro: './src/babel/macro.js', 80 + }, 81 + }, 82 + ];
+54
scripts/simplify-jstags-plugin.js
··· 1 + import { transformSync as transform } from '@babel/core'; 2 + import { createFilter } from '@rollup/pluginutils'; 3 + 4 + const simplifyJSTags = ({ types: t }) => ({ 5 + visitor: { 6 + TaggedTemplateExpression(path) { 7 + if (path.node.tag.name !== 'js') return; 8 + 9 + const expressions = path.node.quasi.expressions; 10 + 11 + const quasis = path.node.quasi.quasis.map((x) => 12 + x.value.cooked 13 + .replace(/\s*[=(){},;:!]\s*/g, (x) => x.trim()) 14 + .replace(/\s+/g, ' ') 15 + .replace(/^\s+$/g, '') 16 + ); 17 + 18 + const concat = expressions.reduceRight( 19 + (prev, node, i) => 20 + t.binaryExpression( 21 + '+', 22 + t.stringLiteral(quasis[i]), 23 + t.binaryExpression('+', node, prev) 24 + ), 25 + t.stringLiteral(quasis[quasis.length - 1]) 26 + ); 27 + 28 + path.replaceWith(concat); 29 + }, 30 + }, 31 + }); 32 + 33 + function simplifyJSTagsPlugin(opts = {}) { 34 + const filter = createFilter(opts.include, opts.exclude, { 35 + resolve: false, 36 + }); 37 + 38 + return { 39 + name: 'cleanup', 40 + 41 + renderChunk(code, chunk) { 42 + if (!filter(chunk.fileName)) { 43 + return null; 44 + } 45 + 46 + return transform(code, { 47 + plugins: [simplifyJSTags], 48 + babelrc: false, 49 + }); 50 + }, 51 + }; 52 + } 53 + 54 + export default simplifyJSTagsPlugin;
+162 -110
src/babel/__snapshots__/plugin.test.js.snap
··· 8 8 var _node_expression = (0, _reghex._pattern)(1), 9 9 _node_expression2 = (0, _reghex._pattern)(2); 10 10 11 - const node = function _node(state) { 12 - var last_index = state.index; 13 - var match, 14 - node = []; 11 + const node = function (state) { 12 + var index_1 = state.index; 13 + var node = []; 14 + var match; 15 15 16 16 if (match = (0, _reghex._exec)(state, _node_expression)) { 17 17 node.push(match); 18 18 } else { 19 - state.index = last_index; 19 + state.index = index_1; 20 20 return; 21 21 } 22 22 23 23 if (match = (0, _reghex._exec)(state, _node_expression2)) { 24 24 node.push(match); 25 25 } else { 26 - state.index = last_index; 26 + state.index = index_1; 27 27 return; 28 28 } 29 29 30 - return (0, _reghex.tag)(node, 'node'); 30 + node.tag = 'node'; 31 + return node; 31 32 };" 32 33 `; 33 34 35 + exports[`works while only minifying 1`] = ` 36 + "import { match } from 'reghex/macro'; 37 + const node = match('node')([\\"\\", \\"+|\\", \\"+(\\", \\"(\\", \\"?\\", \\"))*\\"], 1, 2, 3, 4, 5);" 38 + `; 39 + 34 40 exports[`works with local recursion 1`] = ` 35 - "import { tag, _exec, _substr, _pattern } from 'reghex'; 41 + "import { match as m, tag, _exec, _pattern } from 'reghex'; 42 + 43 + var _inner_expression = _pattern(/inner/); 36 44 37 - const inner = function _inner(state) { 38 - var last_index = state.index; 39 - var match, 40 - node = []; 45 + const inner = function (state) { 46 + var index_1 = state.index; 47 + var node = []; 48 + var match; 41 49 42 - if (match = _substr(state, \\"inner\\")) { 50 + if (match = _exec(state, _inner_expression)) { 43 51 node.push(match); 44 52 } else { 45 - state.index = last_index; 53 + state.index = index_1; 46 54 return; 47 55 } 48 56 49 - return tag(node, 'inner'); 57 + node.tag = 'inner'; 58 + return node; 50 59 }; 51 60 52 - const node = function _node(state) { 53 - var last_index = state.index; 54 - var match, 55 - node = []; 61 + const node = function (state) { 62 + var index_1 = state.index; 63 + var node = []; 64 + var match; 56 65 57 66 if (match = inner(state)) { 58 67 node.push(match); 59 68 } else { 60 - state.index = last_index; 69 + state.index = index_1; 61 70 return; 62 71 } 63 72 64 - return tag(node, 'node'); 73 + node.tag = 'node'; 74 + return node; 65 75 };" 66 76 `; 67 77 68 78 exports[`works with non-capturing groups 1`] = ` 69 - "import { _exec, _substr, _pattern, tag as _tag } from 'reghex'; 79 + "import { match, _exec, _pattern, tag as _tag } from 'reghex'; 70 80 71 81 var _node_expression = _pattern(1), 72 82 _node_expression2 = _pattern(2), 73 83 _node_expression3 = _pattern(3); 74 84 75 - const node = function _node(state) { 76 - var last_index = state.index; 77 - var match, 78 - node = []; 85 + const node = function (state) { 86 + var index_1 = state.index; 87 + var node = []; 88 + var match; 79 89 80 90 if (match = _exec(state, _node_expression)) { 81 91 node.push(match); 82 92 } else { 83 - state.index = last_index; 93 + state.index = index_1; 84 94 return; 85 95 } 86 96 87 - var length_0 = node.length; 97 + var length_2 = node.length; 88 98 89 - alternation_1: { 90 - block_1: { 91 - var index_1 = state.index; 99 + alternation_3: { 100 + block_3: { 101 + var index_3 = state.index; 92 102 93 103 if (match = _exec(state, _node_expression2)) { 94 104 node.push(match); 95 105 } else { 96 - node.length = length_0; 97 - state.index = index_1; 98 - break block_1; 106 + state.index = index_3; 107 + node.length = length_2; 108 + break block_3; 99 109 } 100 110 101 - break alternation_1; 111 + break alternation_3; 102 112 } 103 113 104 - loop_1: for (var iter_1 = 0; true; iter_1++) { 105 - var index_1 = state.index; 114 + loop_3: for (var count_3 = 0; true; count_3++) { 115 + var index_3 = state.index; 106 116 107 117 if (!_exec(state, _node_expression3)) { 108 - if (iter_1) { 109 - state.index = index_1; 110 - break loop_1; 111 - } 118 + if (count_3) { 119 + state.index = index_3; 120 + break loop_3; 121 + } else {} 112 122 113 - node.length = length_0; 114 - state.index = last_index; 123 + state.index = index_1; 124 + node.length = length_2; 115 125 return; 116 126 } 117 127 } 118 128 } 119 129 120 - return _tag(node, 'node'); 130 + node.tag = 'node'; 131 + return node; 132 + };" 133 + `; 134 + 135 + exports[`works with self-referential thunks 1`] = ` 136 + "import { match, tag, _exec, _pattern } from 'reghex'; 137 + 138 + const inner = function (state) { 139 + var index_1 = state.index; 140 + var node = []; 141 + var match; 142 + 143 + if (match = node(state)) { 144 + node.push(match); 145 + } else { 146 + state.index = index_1; 147 + return; 148 + } 149 + 150 + node.tag = 'inner'; 151 + return node; 152 + }; 153 + 154 + const node = function (state) { 155 + var index_1 = state.index; 156 + var node = []; 157 + var match; 158 + 159 + if (match = inner(state)) { 160 + node.push(match); 161 + } else { 162 + state.index = index_1; 163 + return; 164 + } 165 + 166 + node.tag = 'node'; 167 + return node; 121 168 };" 122 169 `; 123 170 124 171 exports[`works with standard features 1`] = ` 125 - "import { _exec, _substr, _pattern, tag as _tag } from \\"reghex\\"; 172 + "import { match, _exec, _pattern, tag as _tag } from \\"reghex\\"; 126 173 127 174 var _node_expression = _pattern(1), 128 175 _node_expression2 = _pattern(2), ··· 130 177 _node_expression4 = _pattern(4), 131 178 _node_expression5 = _pattern(5); 132 179 133 - const node = function _node(state) { 134 - var last_index = state.index; 135 - var match, 136 - node = []; 180 + const node = function (state) { 181 + var index_1 = state.index; 182 + var node = []; 183 + var match; 137 184 138 - block_0: { 139 - var index_0 = state.index; 185 + alternation_2: { 186 + block_2: { 187 + var index_2 = state.index; 140 188 141 - loop_0: for (var iter_0 = 0; true; iter_0++) { 142 - var index_0 = state.index; 189 + loop_2: for (var count_2 = 0; true; count_2++) { 190 + var index_2 = state.index; 143 191 144 - if (match = _exec(state, _node_expression)) { 145 - node.push(match); 146 - } else { 147 - if (iter_0) { 148 - state.index = index_0; 149 - break loop_0; 150 - } 192 + if (match = _exec(state, _node_expression)) { 193 + node.push(match); 194 + } else { 195 + if (count_2) { 196 + state.index = index_2; 197 + break loop_2; 198 + } else {} 151 199 152 - state.index = index_0; 153 - break block_0; 200 + state.index = index_2; 201 + break block_2; 202 + } 154 203 } 204 + 205 + break alternation_2; 155 206 } 156 207 157 - return _tag(node, 'node'); 158 - } 208 + loop_2: for (var count_2 = 0; true; count_2++) { 209 + var index_2 = state.index; 159 210 160 - loop_0: for (var iter_0 = 0; true; iter_0++) { 161 - var index_0 = state.index; 211 + if (match = _exec(state, _node_expression2)) { 212 + node.push(match); 213 + } else { 214 + if (count_2) { 215 + state.index = index_2; 216 + break loop_2; 217 + } else {} 162 218 163 - if (match = _exec(state, _node_expression2)) { 164 - node.push(match); 165 - } else { 166 - if (iter_0) { 167 - state.index = index_0; 168 - break loop_0; 219 + state.index = index_1; 220 + return; 169 221 } 170 - 171 - state.index = last_index; 172 - return; 173 222 } 174 - } 175 223 176 - loop_0: while (true) { 177 - var index_0 = state.index; 178 - var length_0 = node.length; 224 + loop_2: while (true) { 225 + var index_2 = state.index; 226 + var length_2 = node.length; 179 227 180 - if (match = _exec(state, _node_expression3)) { 181 - node.push(match); 182 - } else { 183 - node.length = length_0; 184 - state.index = index_0; 185 - break loop_0; 186 - } 228 + if (match = _exec(state, _node_expression3)) { 229 + node.push(match); 230 + } else { 231 + state.index = index_2; 232 + node.length = length_2; 233 + break loop_2; 234 + } 187 235 188 - var index_2 = state.index; 236 + var index_4 = state.index; 189 237 190 - if (match = _exec(state, _node_expression4)) { 191 - node.push(match); 192 - } else { 193 - state.index = index_2; 194 - } 238 + if (match = _exec(state, _node_expression4)) { 239 + node.push(match); 240 + } else { 241 + state.index = index_4; 242 + } 195 243 196 - if (match = _exec(state, _node_expression5)) { 197 - node.push(match); 198 - } else { 199 - node.length = length_0; 200 - state.index = index_0; 201 - break loop_0; 244 + if (match = _exec(state, _node_expression5)) { 245 + node.push(match); 246 + } else { 247 + state.index = index_2; 248 + node.length = length_2; 249 + break loop_2; 250 + } 202 251 } 203 252 } 204 253 205 - return _tag(node, 'node'); 254 + node.tag = 'node'; 255 + return node; 206 256 };" 207 257 `; 208 258 209 259 exports[`works with transform functions 1`] = ` 210 - "import { _exec, _substr, _pattern, tag as _tag } from 'reghex'; 260 + "import { match, _exec, _pattern, tag as _tag } from 'reghex'; 211 261 212 262 var _inner_transform = x => x; 213 263 214 - const first = function _inner(state) { 215 - var last_index = state.index; 216 - var match, 217 - node = []; 218 - return _inner_transform(_tag(node, 'inner')); 264 + const first = function (state) { 265 + var index_1 = state.index; 266 + var node = []; 267 + var match; 268 + node.tag = 'inner'; 269 + return _inner_transform(node); 219 270 }; 220 271 221 272 const transform = x => x; 222 273 223 - const second = function _node(state) { 224 - var last_index = state.index; 225 - var match, 226 - node = []; 227 - return transform(_tag(node, 'node')); 274 + const second = function (state) { 275 + var index_1 = state.index; 276 + var node = []; 277 + var match; 278 + node.tag = 'node'; 279 + return transform(node); 228 280 };" 229 281 `;
+3 -40
src/babel/__tests__/suite.js src/core.test.js
··· 1 - import * as reghex from '../../..'; 2 - import * as types from '@babel/types'; 3 - import { transform } from '@babel/core'; 4 - import { makeHelpers } from '../transform'; 5 - 6 - const match = (name) => (quasis, ...expressions) => { 7 - const helpers = makeHelpers(types); 8 - 9 - let str = ''; 10 - for (let i = 0; i < quasis.length; i++) { 11 - str += quasis[i]; 12 - if (i < expressions.length) str += '${' + expressions[i].toString() + '}'; 13 - } 14 - 15 - const template = `(function () { return match('${name}')\`${str}\`; })()`; 16 - 17 - const testPlugin = () => ({ 18 - visitor: { 19 - TaggedTemplateExpression(path) { 20 - helpers.transformMatch(path); 21 - }, 22 - }, 23 - }); 24 - 25 - const { code } = transform(template, { 26 - babelrc: false, 27 - presets: [], 28 - plugins: [testPlugin], 29 - }); 30 - 31 - const argKeys = Object.keys(reghex).filter((x) => { 32 - return x.startsWith('_') || x === 'tag'; 33 - }); 34 - 35 - const args = argKeys.map((key) => reghex[key]); 36 - return new Function(...argKeys, 'return ' + code)(...args); 37 - }; 1 + import { match } from './core'; 38 2 39 3 const expectToParse = (node, input, result, lastIndex = 0) => { 40 4 const state = { input, index: 0 }; 41 - expect(node(state)).toEqual( 42 - result === undefined ? result : reghex.tag(result, 'node') 43 - ); 5 + if (result) result.tag = 'node'; 6 + expect(node(state)).toEqual(result); 44 7 45 8 // NOTE: After parsing we expect the current index to exactly match the 46 9 // sum amount of matched characters
-411
src/babel/generator.js
··· 1 - let t; 2 - let ids = {}; 3 - 4 - export function initGenerator(_ids, _t) { 5 - ids = _ids; 6 - t = _t; 7 - } 8 - 9 - /** var id = state.index; */ 10 - class AssignIndexNode { 11 - constructor(id) { 12 - this.id = id; 13 - } 14 - 15 - statement() { 16 - const member = t.memberExpression(ids.state, t.identifier('index')); 17 - return t.variableDeclaration('var', [ 18 - t.variableDeclarator(this.id, member), 19 - ]); 20 - } 21 - } 22 - 23 - /** state.index = id; */ 24 - class RestoreIndexNode { 25 - constructor(id) { 26 - this.id = id; 27 - } 28 - 29 - statement() { 30 - const expression = t.assignmentExpression( 31 - '=', 32 - t.memberExpression(ids.state, t.identifier('index')), 33 - this.id 34 - ); 35 - 36 - return t.expressionStatement(expression); 37 - } 38 - } 39 - 40 - /** var id = node.length; */ 41 - class AssignLengthNode { 42 - constructor(id) { 43 - this.id = id; 44 - } 45 - 46 - statement() { 47 - return t.variableDeclaration('var', [ 48 - t.variableDeclarator( 49 - this.id, 50 - t.memberExpression(ids.node, t.identifier('length')) 51 - ), 52 - ]); 53 - } 54 - } 55 - 56 - /** node.length = id; */ 57 - class RestoreLengthNode { 58 - constructor(id) { 59 - this.id = id; 60 - } 61 - 62 - statement() { 63 - const expression = t.assignmentExpression( 64 - '=', 65 - t.memberExpression(ids.node, t.identifier('length')), 66 - this.id 67 - ); 68 - 69 - return t.expressionStatement(expression); 70 - } 71 - } 72 - 73 - /** return; break id; */ 74 - class AbortNode { 75 - constructor(id) { 76 - this.id = id || null; 77 - } 78 - 79 - statement() { 80 - const statement = this.id ? t.breakStatement(this.id) : t.returnStatement(); 81 - return statement; 82 - } 83 - } 84 - 85 - /** if (condition) { return; break id; } */ 86 - class AbortConditionNode { 87 - constructor(condition, opts) { 88 - this.condition = condition || null; 89 - 90 - this.abort = opts.abort; 91 - this.abortCondition = opts.abortCondition || null; 92 - this.restoreIndex = opts.restoreIndex; 93 - } 94 - 95 - statement() { 96 - return t.ifStatement( 97 - this.condition, 98 - t.blockStatement( 99 - [this.restoreIndex.statement(), this.abort.statement()].filter(Boolean) 100 - ), 101 - this.abortCondition ? this.abortCondition.statement() : null 102 - ); 103 - } 104 - } 105 - 106 - /** Generates a full matcher for an expression */ 107 - class ExpressionNode { 108 - constructor(ast, depth, opts) { 109 - this.ast = ast; 110 - this.depth = depth || 0; 111 - this.capturing = !!opts.capturing; 112 - this.restoreIndex = opts.restoreIndex; 113 - this.restoreLength = opts.restoreLength || null; 114 - this.abortCondition = opts.abortCondition || null; 115 - this.abort = opts.abort || null; 116 - } 117 - 118 - statements() { 119 - const execMatch = this.ast.expression; 120 - const assignMatch = t.assignmentExpression('=', ids.match, execMatch); 121 - 122 - const successNodes = t.blockStatement([ 123 - t.expressionStatement( 124 - t.callExpression(t.memberExpression(ids.node, t.identifier('push')), [ 125 - ids.match, 126 - ]) 127 - ), 128 - ]); 129 - 130 - const abortNodes = t.blockStatement( 131 - [ 132 - this.abortCondition && this.abortCondition.statement(), 133 - this.abort && this.restoreLength && this.restoreLength.statement(), 134 - this.restoreIndex && this.restoreIndex.statement(), 135 - this.abort && this.abort.statement(), 136 - ].filter(Boolean) 137 - ); 138 - 139 - return [ 140 - !this.capturing 141 - ? t.ifStatement(t.unaryExpression('!', execMatch), abortNodes) 142 - : t.ifStatement(assignMatch, successNodes, abortNodes), 143 - ]; 144 - } 145 - } 146 - 147 - /** Generates a full matcher for a group */ 148 - class GroupNode { 149 - constructor(ast, depth, opts) { 150 - this.ast = ast; 151 - this.depth = depth || 0; 152 - if (ast.sequence.length === 1) { 153 - return new ExpressionNode(ast.sequence[0], depth, opts); 154 - } 155 - 156 - const lengthId = t.identifier(`length_${depth}`); 157 - const childOpts = { 158 - ...opts, 159 - capturing: !!opts.capturing && !!ast.capturing, 160 - }; 161 - 162 - this.assignLength = null; 163 - if (!childOpts.restoreLength && childOpts.capturing) { 164 - this.assignLength = new AssignLengthNode(lengthId); 165 - childOpts.restoreLength = new RestoreLengthNode(lengthId); 166 - } 167 - 168 - this.alternation = new AlternationNode(ast.sequence, depth + 1, childOpts); 169 - } 170 - 171 - statements() { 172 - return [ 173 - this.assignLength && this.assignLength.statement(), 174 - ...this.alternation.statements(), 175 - ].filter(Boolean); 176 - } 177 - } 178 - 179 - /** Generates looping logic around another group or expression matcher */ 180 - class QuantifierNode { 181 - constructor(ast, depth, opts) { 182 - const { quantifier } = ast; 183 - this.ast = ast; 184 - this.depth = depth || 0; 185 - 186 - const invertId = t.identifier(`invert_${this.depth}`); 187 - const loopId = t.identifier(`loop_${this.depth}`); 188 - const iterId = t.identifier(`iter_${this.depth}`); 189 - const indexId = t.identifier(`index_${this.depth}`); 190 - const ChildNode = ast.type === 'group' ? GroupNode : ExpressionNode; 191 - const childOpts = { ...opts }; 192 - 193 - this.assignIndex = null; 194 - this.restoreIndex = null; 195 - this.blockId = null; 196 - this.abort = null; 197 - 198 - if (ast.type === 'group' && !!ast.lookahead) { 199 - this.restoreIndex = new RestoreIndexNode(indexId); 200 - this.assignIndex = new AssignIndexNode(indexId); 201 - } 202 - 203 - if (ast.type === 'group' && ast.lookahead === 'negative') { 204 - childOpts.abort = new AbortNode(invertId); 205 - childOpts.restoreIndex = this.restoreIndex; 206 - this.restoreIndex = opts.restoreIndex; 207 - this.blockId = invertId; 208 - this.abort = opts.abort; 209 - } 210 - 211 - if (quantifier && !quantifier.singular && quantifier.required) { 212 - childOpts.abortCondition = new AbortConditionNode(iterId, { 213 - ...opts, 214 - restoreIndex: new RestoreIndexNode(indexId), 215 - abort: new AbortNode(loopId), 216 - }); 217 - } else if (quantifier && !quantifier.singular) { 218 - childOpts.restoreLength = null; 219 - childOpts.restoreIndex = new RestoreIndexNode(indexId); 220 - childOpts.abort = new AbortNode(loopId); 221 - childOpts.abortCondition = null; 222 - } else if (quantifier && !quantifier.required) { 223 - childOpts.restoreIndex = new RestoreIndexNode(indexId); 224 - childOpts.abortCondition = null; 225 - childOpts.abort = null; 226 - } 227 - 228 - this.childNode = new ChildNode(ast, depth, childOpts); 229 - } 230 - 231 - statements() { 232 - const { quantifier } = this.ast; 233 - const loopId = t.identifier(`loop_${this.depth}`); 234 - const iterId = t.identifier(`iter_${this.depth}`); 235 - const indexId = t.identifier(`index_${this.depth}`); 236 - const assignIndex = new AssignIndexNode(indexId); 237 - 238 - let statements; 239 - if (quantifier && !quantifier.singular && quantifier.required) { 240 - statements = [ 241 - t.labeledStatement( 242 - loopId, 243 - t.forStatement( 244 - t.variableDeclaration('var', [ 245 - t.variableDeclarator(iterId, t.numericLiteral(0)), 246 - ]), 247 - t.booleanLiteral(true), 248 - t.updateExpression('++', iterId), 249 - t.blockStatement([ 250 - assignIndex.statement(), 251 - ...this.childNode.statements(), 252 - ]) 253 - ) 254 - ), 255 - ]; 256 - } else if (quantifier && !quantifier.singular) { 257 - statements = [ 258 - t.labeledStatement( 259 - loopId, 260 - t.whileStatement( 261 - t.booleanLiteral(true), 262 - t.blockStatement([ 263 - assignIndex.statement(), 264 - ...this.childNode.statements(), 265 - ]) 266 - ) 267 - ), 268 - ]; 269 - } else if (quantifier && !quantifier.required) { 270 - statements = [assignIndex.statement(), ...this.childNode.statements()]; 271 - } else { 272 - statements = this.childNode.statements(); 273 - } 274 - 275 - if (this.blockId && this.assignIndex && this.restoreIndex) { 276 - statements = [ 277 - t.labeledStatement( 278 - this.blockId, 279 - t.blockStatement( 280 - [ 281 - this.assignIndex.statement(), 282 - ...statements, 283 - this.restoreIndex.statement(), 284 - this.abort.statement(), 285 - ].filter(Boolean) 286 - ) 287 - ), 288 - ].filter(Boolean); 289 - } else if (this.assignIndex && this.restoreIndex) { 290 - statements.unshift(this.assignIndex.statement()); 291 - statements.push(this.restoreIndex.statement()); 292 - } 293 - 294 - return statements; 295 - } 296 - } 297 - 298 - /** Generates a matcher of a sequence of sub-matchers for a single sequence */ 299 - class SequenceNode { 300 - constructor(ast, depth, opts) { 301 - this.ast = ast; 302 - this.depth = depth || 0; 303 - 304 - const indexId = t.identifier(`index_${depth}`); 305 - const blockId = t.identifier(`block_${this.depth}`); 306 - 307 - this.returnStatement = opts.returnStatement; 308 - this.assignIndex = ast.alternation ? new AssignIndexNode(indexId) : null; 309 - 310 - this.quantifiers = ast.sequence.map((childAst) => { 311 - return new QuantifierNode(childAst, depth, { 312 - ...opts, 313 - restoreIndex: ast.alternation 314 - ? new RestoreIndexNode(indexId) 315 - : opts.restoreIndex, 316 - abortCondition: ast.alternation ? null : opts.abortCondition, 317 - abort: ast.alternation ? new AbortNode(blockId) : opts.abort, 318 - }); 319 - }); 320 - } 321 - 322 - statements() { 323 - const blockId = t.identifier(`block_${this.depth}`); 324 - const alternationId = t.identifier(`alternation_${this.depth}`); 325 - const statements = this.quantifiers.reduce((block, node) => { 326 - block.push(...node.statements()); 327 - return block; 328 - }, []); 329 - 330 - if (!this.ast.alternation) { 331 - return statements; 332 - } 333 - 334 - const abortNode = 335 - this.depth === 0 ? this.returnStatement : t.breakStatement(alternationId); 336 - 337 - return [ 338 - t.labeledStatement( 339 - blockId, 340 - t.blockStatement([ 341 - this.assignIndex && this.assignIndex.statement(), 342 - ...statements, 343 - abortNode, 344 - ]) 345 - ), 346 - ]; 347 - } 348 - } 349 - 350 - /** Generates matchers for sequences with (or without) alternations */ 351 - class AlternationNode { 352 - constructor(ast, depth, opts) { 353 - this.ast = ast; 354 - this.depth = depth || 0; 355 - this.sequences = []; 356 - for (let current = ast; current; current = current.alternation) { 357 - this.sequences.push(new SequenceNode(current, depth, opts)); 358 - } 359 - } 360 - 361 - statements() { 362 - if (this.sequences.length === 1) { 363 - return this.sequences[0].statements(); 364 - } 365 - 366 - const statements = []; 367 - for (let i = 0; i < this.sequences.length; i++) { 368 - statements.push(...this.sequences[i].statements()); 369 - } 370 - 371 - if (this.depth === 0) { 372 - return statements; 373 - } 374 - 375 - const alternationId = t.identifier(`alternation_${this.depth}`); 376 - return [t.labeledStatement(alternationId, t.blockStatement(statements))]; 377 - } 378 - } 379 - 380 - export class RootNode { 381 - constructor(ast, nameNode, transformNode) { 382 - const indexId = t.identifier('last_index'); 383 - const node = t.callExpression(ids.tag, [ids.node, nameNode]); 384 - 385 - this.returnStatement = t.returnStatement( 386 - transformNode ? t.callExpression(transformNode, [node]) : node 387 - ); 388 - 389 - this.assignIndex = new AssignIndexNode(indexId); 390 - this.node = new AlternationNode(ast, 0, { 391 - returnStatement: this.returnStatement, 392 - restoreIndex: new RestoreIndexNode(indexId, true), 393 - restoreLength: null, 394 - abortCondition: null, 395 - abort: new AbortNode(), 396 - capturing: true, 397 - }); 398 - } 399 - 400 - statements() { 401 - return [ 402 - this.assignIndex.statement(), 403 - t.variableDeclaration('var', [ 404 - t.variableDeclarator(ids.match), 405 - t.variableDeclarator(ids.node, t.arrayExpression()), 406 - ]), 407 - ...this.node.statements(), 408 - this.returnStatement, 409 - ]; 410 - } 411 - }
+2 -2
src/babel/macro.js
··· 1 1 import { createMacro } from 'babel-plugin-macros'; 2 2 import { makeHelpers } from './transform'; 3 3 4 - function reghexMacro({ references, babel: { types: t } }) { 5 - const helpers = makeHelpers(t); 4 + function reghexMacro({ references, babel }) { 5 + const helpers = makeHelpers(babel); 6 6 const defaultRefs = references.default || []; 7 7 8 8 defaultRefs.forEach((ref) => {
+8 -3
src/babel/plugin.js
··· 1 1 import { makeHelpers } from './transform'; 2 2 3 - export default function reghexPlugin({ types }) { 3 + export default function reghexPlugin(babel, opts = {}) { 4 4 let helpers; 5 5 6 6 return { 7 7 name: 'reghex', 8 8 visitor: { 9 9 Program() { 10 - helpers = makeHelpers(types); 10 + helpers = makeHelpers(babel); 11 11 }, 12 12 ImportDeclaration(path) { 13 + if (opts.codegen === false) return; 13 14 helpers.updateImport(path); 14 15 }, 15 16 TaggedTemplateExpression(path) { 16 17 if (helpers.isMatch(path) && helpers.getMatchImport(path)) { 17 - helpers.transformMatch(path); 18 + if (opts.codegen === false) { 19 + helpers.minifyMatch(path); 20 + } else { 21 + helpers.transformMatch(path); 22 + } 18 23 } 19 24 }, 20 25 },
+44 -7
src/babel/plugin.test.js
··· 3 3 4 4 it('works with standard features', () => { 5 5 const code = ` 6 - import match from 'reghex/macro'; 6 + import { match } from 'reghex/macro'; 7 7 8 8 const node = match('node')\` 9 9 \${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )* ··· 16 16 ).toMatchSnapshot(); 17 17 }); 18 18 19 + it('works while only minifying', () => { 20 + const code = ` 21 + import { match } from 'reghex/macro'; 22 + 23 + const node = match('node')\` 24 + \${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )* 25 + \`; 26 + `; 27 + 28 + expect( 29 + transform(code, { 30 + babelrc: false, 31 + presets: [], 32 + plugins: [[reghexPlugin, { codegen: false }]], 33 + }).code 34 + ).toMatchSnapshot(); 35 + }); 36 + 19 37 it('works with local recursion', () => { 20 38 // NOTE: A different default name is allowed 21 39 const code = ` 22 - import match_rec, { tag } from 'reghex'; 40 + import { match as m, tag } from 'reghex'; 23 41 24 - const inner = match_rec('inner')\` 42 + const inner = m('inner')\` 25 43 \${/inner/} 26 44 \`; 27 45 28 - const node = match_rec('node')\` 46 + const node = m('node')\` 47 + \${inner} 48 + \`; 49 + `; 50 + 51 + expect( 52 + transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] }) 53 + .code 54 + ).toMatchSnapshot(); 55 + }); 56 + 57 + it('works with self-referential thunks', () => { 58 + const code = ` 59 + import { match, tag } from 'reghex'; 60 + 61 + const inner = match('inner')\` 62 + \${() => node} 63 + \`; 64 + 65 + const node = match('node')\` 29 66 \${inner} 30 67 \`; 31 68 `; ··· 38 75 39 76 it('works with transform functions', () => { 40 77 const code = ` 41 - import match from 'reghex'; 78 + import { match } from 'reghex'; 42 79 43 80 const first = match('inner', x => x)\`\`; 44 81 ··· 54 91 55 92 it('works with non-capturing groups', () => { 56 93 const code = ` 57 - import match from 'reghex'; 94 + import { match } from 'reghex'; 58 95 59 96 const node = match('node')\` 60 97 \${1} (\${2} | (?: \${3})+) ··· 69 106 70 107 it('works together with @babel/plugin-transform-modules-commonjs', () => { 71 108 const code = ` 72 - import match from 'reghex'; 109 + import { match } from 'reghex'; 73 110 74 111 const node = match('node')\` 75 112 \${1} \${2}
-17
src/babel/sharedIds.js
··· 2 2 constructor(t) { 3 3 this.t = t; 4 4 this.execId = t.identifier('_exec'); 5 - this.substrId = t.identifier('_substr'); 6 5 this.patternId = t.identifier('_pattern'); 7 6 this.tagId = t.identifier('tag'); 8 7 } 9 8 10 - get node() { 11 - return this.t.identifier('node'); 12 - } 13 - 14 - get match() { 15 - return this.t.identifier('match'); 16 - } 17 - 18 - get state() { 19 - return this.t.identifier('state'); 20 - } 21 - 22 9 get exec() { 23 10 return this.t.identifier(this.execId.name); 24 - } 25 - 26 - get substr() { 27 - return this.t.identifier(this.substrId.name); 28 11 } 29 12 30 13 get pattern() {
+58 -48
src/babel/transform.js
··· 1 1 import { parse } from '../parser'; 2 + import { astRoot } from '../codegen'; 2 3 import { SharedIds } from './sharedIds'; 3 - import { initGenerator, RootNode } from './generator'; 4 4 5 - export function makeHelpers(t) { 5 + export function makeHelpers({ types: t, template }) { 6 6 const regexPatternsRe = /^[()\[\]|.+?*]|[^\\][()\[\]|.+?*$^]|\\[wdsWDS]/; 7 7 const importSourceRe = /reghex$|^reghex\/macro/; 8 8 const importName = 'reghex'; 9 9 const ids = new SharedIds(t); 10 - initGenerator(ids, t); 11 10 12 11 let _hasUpdatedImport = false; 13 12 ··· 18 17 if (!importSourceRe.test(path.node.source.value)) return; 19 18 _hasUpdatedImport = true; 20 19 21 - const defaultSpecifierIndex = path.node.specifiers.findIndex((node) => { 22 - return t.isImportDefaultSpecifier(node); 23 - }); 24 - 25 - if (defaultSpecifierIndex > -1) { 26 - path.node.specifiers.splice(defaultSpecifierIndex, 1); 27 - } 28 - 29 20 if (path.node.source.value !== importName) { 30 21 path.node.source = t.stringLiteral(importName); 31 22 } ··· 36 27 t.identifier('_exec') 37 28 ), 38 29 t.importSpecifier( 39 - (ids.substrId = path.scope.generateUidIdentifier('substr')), 40 - t.identifier('_substr') 41 - ), 42 - t.importSpecifier( 43 30 (ids.patternId = path.scope.generateUidIdentifier('pattern')), 44 31 t.identifier('_pattern') 45 32 ) ··· 48 35 const tagImport = path.node.specifiers.find((node) => { 49 36 return t.isImportSpecifier(node) && node.imported.name === 'tag'; 50 37 }); 38 + 51 39 if (!tagImport) { 52 40 path.node.specifiers.push( 53 41 t.importSpecifier( ··· 87 75 binding.kind !== 'module' || 88 76 !t.isImportDeclaration(binding.path.parent) || 89 77 !importSourceRe.test(binding.path.parent.source.value) || 90 - !t.isImportDefaultSpecifier(binding.path.node) 78 + !t.isImportSpecifier(binding.path.node) 91 79 ) { 92 80 return null; 93 81 } ··· 119 107 const hoistedExpressions = path.node.quasi.expressions.map( 120 108 (expression, i) => { 121 109 if ( 110 + t.isArrowFunctionExpression(expression) && 111 + t.isIdentifier(expression.body) 112 + ) { 113 + expression = expression.body; 114 + } else if ( 115 + (t.isFunctionExpression(expression) || 116 + t.isArrowFunctionExpression(expression)) && 117 + t.isBlockStatement(expression.body) && 118 + expression.body.body.length === 1 && 119 + t.isReturnStatement(expression.body.body[0]) && 120 + t.isIdentifier(expression.body.body[0].argument) 121 + ) { 122 + expression = expression.body.body[0].argument; 123 + } 124 + 125 + if ( 122 126 t.isIdentifier(expression) && 123 127 path.scope.hasBinding(expression.name) 124 128 ) { ··· 127 131 const matchPath = binding.path.get('init'); 128 132 if (this.isMatch(matchPath)) return expression; 129 133 } 130 - } else if ( 131 - t.isRegExpLiteral(expression) && 132 - !regexPatternsRe.test(expression.pattern) 133 - ) { 134 - // NOTE: This is an optimisation path, where the pattern regex is inlined 135 - // and has determined to be "simple" enough to be turned into a string 136 - return t.stringLiteral( 137 - expression.pattern.replace(/\\./g, (x) => x[1]) 138 - ); 139 134 } 140 135 141 136 const id = path.scope.generateUidIdentifier( ··· 160 155 } 161 156 162 157 return hoistedExpressions.map((id) => { 163 - // Use _substr helper instead if the expression is a string 164 - if (t.isStringLiteral(id)) { 165 - return t.callExpression(ids.substr, [ids.state, id]); 166 - } 167 - 168 - // Directly call expression if it's sure to be another matcher 169 158 const binding = path.scope.getBinding(id.name); 170 159 if (binding && t.isVariableDeclarator(binding.path.node)) { 171 160 const matchPath = binding.path.get('init'); 172 - if (this.isMatch(matchPath)) { 173 - return t.callExpression(id, [ids.state]); 174 - } 161 + if (this.isMatch(matchPath)) return `${id.name}(state)`; 175 162 } 176 163 177 - return t.callExpression(ids.exec, [ids.state, id]); 164 + const input = t.isStringLiteral(id) 165 + ? JSON.stringify(id.value) 166 + : id.name; 167 + return `${ids.exec.name}(state, ${input})`; 178 168 }); 179 169 }, 180 170 181 171 _prepareTransform(path) { 182 172 const transformNode = path.node.tag.arguments[1]; 173 + 183 174 if (!transformNode) return null; 184 - if (t.isIdentifier(transformNode)) return transformNode; 175 + if (t.isIdentifier(transformNode)) return transformNode.name; 185 176 186 177 const matchName = this.getMatchName(path); 187 178 const id = path.scope.generateUidIdentifier(`${matchName}_transform`); ··· 190 181 path 191 182 .getStatementParent() 192 183 .insertBefore(t.variableDeclaration('var', [declarator])); 193 - return id; 184 + 185 + return id.name; 186 + }, 187 + 188 + minifyMatch(path) { 189 + if (!path.node.tag.arguments.length) { 190 + throw path 191 + .get('tag') 192 + .buildCodeFrameError( 193 + 'match() must at least be called with a node name' 194 + ); 195 + } 196 + 197 + const quasis = path.node.quasi.quasis.map((x) => 198 + t.stringLiteral(x.value.cooked.replace(/\s*/g, '')) 199 + ); 200 + const expressions = path.node.quasi.expressions; 201 + const transform = this._prepareTransform(path); 202 + 203 + path.replaceWith( 204 + t.callExpression(path.node.tag, [ 205 + t.arrayExpression(quasis), 206 + ...expressions, 207 + ]) 208 + ); 194 209 }, 195 210 196 211 transformMatch(path) { ··· 202 217 ); 203 218 } 204 219 205 - const matchName = this.getMatchName(path); 206 - const nameNode = path.node.tag.arguments[0]; 220 + const name = path.node.tag.arguments[0]; 207 221 const quasis = path.node.quasi.quasis.map((x) => x.value.cooked); 208 222 209 223 const expressions = this._prepareExpressions(path); 210 - const transformNode = this._prepareTransform(path); 224 + const transform = this._prepareTransform(path); 211 225 212 226 let ast; 213 227 try { ··· 217 231 throw path.get('quasi').buildCodeFrameError(error.message); 218 232 } 219 233 220 - const generator = new RootNode(ast, nameNode, transformNode); 221 - const body = t.blockStatement(generator.statements()); 222 - const matchFunctionId = path.scope.generateUidIdentifier(matchName); 223 - const matchFunction = t.functionExpression( 224 - matchFunctionId, 225 - [ids.state], 226 - body 234 + const code = astRoot(ast, '%%name%%', transform && '%%transform%%'); 235 + 236 + path.replaceWith( 237 + template.expression(code)(transform ? { name, transform } : { name }) 227 238 ); 228 - path.replaceWith(matchFunction); 229 239 }, 230 240 }; 231 241 }
+252
src/codegen.js
··· 1 + const _state = 'state'; 2 + const _match = 'match'; 3 + const _node = 'node'; 4 + 5 + function js(/* arguments */) { 6 + let body = arguments[0][0]; 7 + for (let i = 1; i < arguments.length; i++) 8 + body = body + arguments[i] + arguments[0][i]; 9 + return body.trim(); 10 + } 11 + 12 + const newOpts = (prev, next) => ({ 13 + index: next.index != null ? next.index : prev.index, 14 + length: next.length != null ? next.length : prev.length, 15 + onAbort: next.onAbort != null ? next.onAbort : prev.onAbort, 16 + abort: next.abort != null ? next.abort : prev.abort, 17 + capture: next.capture != null ? next.capture : prev.capture, 18 + }); 19 + 20 + const assignIndex = (depth) => 21 + depth ? js`var index_${depth} = ${_state}.index;` : ''; 22 + 23 + const restoreIndex = (depth) => 24 + depth ? js`${_state}.index = index_${depth};` : ''; 25 + 26 + const abortOnCondition = (condition, hooks) => js` 27 + if (${condition}) { 28 + ${restoreIndex(opts.index)} 29 + ${opts.abort || ''} 30 + } else { 31 + ${opts.onAbort || ''} 32 + } 33 + `; 34 + 35 + const astExpression = (ast, depth, opts) => { 36 + const restoreLength = 37 + opts.length && 38 + opts.abort && 39 + js` 40 + ${_node}.length = length_${opts.length}; 41 + `; 42 + 43 + const abort = js` 44 + ${opts.onAbort || ''} 45 + ${restoreIndex(opts.index)} 46 + ${restoreLength || ''} 47 + ${opts.abort || ''} 48 + `; 49 + 50 + if (!opts.capture) { 51 + return js` 52 + if (!(${ast.expression})) { 53 + ${abort} 54 + } 55 + `; 56 + } 57 + 58 + return js` 59 + if (${_match} = ${ast.expression}) { 60 + ${_node}.push(${_match}); 61 + } else { 62 + ${abort} 63 + } 64 + `; 65 + }; 66 + 67 + const astGroup = (ast, depth, opts) => { 68 + const capture = !!opts.capture && !ast.capture; 69 + 70 + let group = ''; 71 + if (!opts.length && capture) { 72 + return js` 73 + ${js`var length_${depth} = ${_node}.length;`} 74 + ${astSequence( 75 + ast.sequence, 76 + depth + 1, 77 + newOpts(opts, { 78 + length: depth, 79 + capture, 80 + }) 81 + )} 82 + `; 83 + } 84 + 85 + return astSequence( 86 + ast.sequence, 87 + depth + 1, 88 + newOpts(opts, { 89 + capture, 90 + }) 91 + ); 92 + }; 93 + 94 + const astChild = (ast, depth, opts) => 95 + ast.expression ? astExpression(ast, depth, opts) : astGroup(ast, depth, opts); 96 + 97 + const astRepeating = (ast, depth, opts) => { 98 + const label = `loop_${depth}`; 99 + const count = `count_${depth}`; 100 + return js` 101 + ${label}: for (var ${count} = 0; true; ${count}++) { 102 + ${assignIndex(depth)} 103 + ${astChild( 104 + ast, 105 + depth, 106 + newOpts(opts, { 107 + onAbort: js` 108 + if (${count}) { 109 + ${restoreIndex(depth)} 110 + break ${label}; 111 + } else { 112 + ${opts.onAbort || ''} 113 + } 114 + `, 115 + }) 116 + )} 117 + } 118 + `; 119 + }; 120 + 121 + const astMultiple = (ast, depth, opts) => { 122 + const label = `loop_${depth}`; 123 + return js` 124 + ${label}: while (true) { 125 + ${assignIndex(depth)} 126 + ${astChild( 127 + ast, 128 + depth, 129 + newOpts(opts, { 130 + length: 0, 131 + index: depth, 132 + abort: js`break ${label};`, 133 + onAbort: '', 134 + }) 135 + )} 136 + } 137 + `; 138 + }; 139 + 140 + const astOptional = (ast, depth, opts) => js` 141 + ${assignIndex(depth)} 142 + ${astChild( 143 + ast, 144 + depth, 145 + newOpts(opts, { 146 + index: depth, 147 + abort: '', 148 + onAbort: '', 149 + }) 150 + )} 151 + `; 152 + 153 + const astQuantifier = (ast, depth, opts) => { 154 + const { index, abort } = opts; 155 + const label = `invert_${depth}`; 156 + 157 + if (ast.capture === '!') { 158 + opts = newOpts(opts, { 159 + index: depth, 160 + abort: js`break ${label};`, 161 + }); 162 + } 163 + 164 + let child; 165 + if (ast.quantifier === '+') { 166 + child = astRepeating(ast, depth, opts); 167 + } else if (ast.quantifier === '*') child = astMultiple(ast, depth, opts); 168 + else if (ast.quantifier === '?') child = astOptional(ast, depth, opts); 169 + else child = astChild(ast, depth, opts); 170 + 171 + if (ast.capture === '!') { 172 + return js` 173 + ${label}: { 174 + ${assignIndex(depth)} 175 + ${child} 176 + ${restoreIndex(index)} 177 + ${abort} 178 + } 179 + `; 180 + } else if (ast.capture === '=') { 181 + return js` 182 + ${assignIndex(depth)} 183 + ${child} 184 + ${restoreIndex(depth)} 185 + `; 186 + } else { 187 + return child; 188 + } 189 + }; 190 + 191 + const astSequence = (ast, depth, opts) => { 192 + const alternation = ast.alternation ? `alternation_${depth}` : ''; 193 + 194 + let body = ''; 195 + for (; ast; ast = ast.alternation) { 196 + const block = `block_${depth}`; 197 + 198 + let childOpts = opts; 199 + if (ast.alternation) { 200 + childOpts = newOpts(opts, { 201 + index: depth, 202 + abort: js`break ${block};`, 203 + onAbort: '', 204 + }); 205 + } 206 + 207 + let sequence = ''; 208 + for (let i = 0; i < ast.length; i++) 209 + sequence += astQuantifier(ast[i], depth, childOpts); 210 + 211 + if (!ast.alternation) { 212 + body += sequence; 213 + } else { 214 + body += js` 215 + ${block}: { 216 + ${assignIndex(depth)} 217 + ${sequence} 218 + break ${alternation}; 219 + } 220 + `; 221 + } 222 + } 223 + 224 + if (!alternation) return body; 225 + 226 + return js` 227 + ${alternation}: { 228 + ${body} 229 + } 230 + `; 231 + }; 232 + 233 + const astRoot = (ast, name, transform) => js` 234 + (function (${_state}) { 235 + ${assignIndex(1)} 236 + var ${_node} = []; 237 + var ${_match}; 238 + 239 + ${astSequence(ast, 2, { 240 + index: 1, 241 + length: 0, 242 + onAbort: '', 243 + abort: js`return;`, 244 + capture: true, 245 + })} 246 + 247 + ${_node}.tag = ${name}; 248 + return ${transform ? js`(${transform})(${_node})` : _node}; 249 + }) 250 + `; 251 + 252 + export { astRoot };
+29 -31
src/core.js
··· 1 + import { astRoot } from './codegen'; 2 + import { parse as parseDSL } from './parser'; 3 + 1 4 const isStickySupported = typeof /./g.sticky === 'boolean'; 2 5 3 6 export const _pattern = (input) => { 4 7 if (typeof input === 'function') return input; 5 - 6 8 const source = typeof input !== 'string' ? input.source : input; 7 9 return isStickySupported 8 10 ? new RegExp(source, 'y') 9 - : new RegExp(`^(?:${source})`, 'g'); 11 + : new RegExp(source + '|()', 'g'); 10 12 }; 11 13 12 - export const _substr = (state, pattern) => { 13 - const end = state.index + pattern.length; 14 - const sub = state.input.slice(state.index, end); 15 - if (sub === pattern) { 16 - state.index = end; 17 - return sub; 14 + export const _exec = (state, pattern) => { 15 + let match; 16 + 17 + if (typeof pattern === 'function') { 18 + if (!pattern.length) pattern = pattern(); 19 + return pattern(state); 18 20 } 19 - }; 20 21 21 - export const _exec = (state, pattern) => { 22 - if (typeof pattern === 'function') return pattern(); 22 + pattern.lastIndex = state.index; 23 23 24 - let match; 25 24 if (isStickySupported) { 26 - pattern.lastIndex = state.index; 27 - if (pattern.test(state.input)) { 25 + if (pattern.test(state.input)) 28 26 match = state.input.slice(state.index, pattern.lastIndex); 29 - state.index = pattern.lastIndex; 30 - } 31 27 } else { 32 - pattern.lastIndex = 0; 33 - if (pattern.test(state.input.slice(state.index))) { 34 - const lastIndex = state.index + pattern.lastIndex; 35 - match = state.input.slice(state.index, lastIndex); 36 - state.index = lastIndex; 37 - } 28 + match = pattern.exec(state.input)[0] || match; 38 29 } 39 30 31 + state.index = pattern.lastIndex; 40 32 return match; 41 33 }; 42 34 43 - export const tag = (array, tag) => { 44 - array.tag = tag; 45 - return array; 46 - }; 47 - 48 35 export const parse = (pattern) => (input) => { 49 36 const state = { input, index: 0 }; 50 37 return pattern(state); 51 38 }; 52 39 53 - export const match = (_name) => { 54 - throw new TypeError( 55 - 'This match() function was not transformed. ' + 56 - 'Ensure that the Babel plugin is set up correctly and try again.' 40 + export const match = (name, transform) => (quasis, ...expressions) => { 41 + const ast = parseDSL( 42 + quasis, 43 + expressions.map((expression, i) => 44 + typeof expression === 'function' && expression.length 45 + ? `_${i}(state)` 46 + : `_e(state, _${i})` 47 + ) 57 48 ); 49 + 50 + const makeMatcher = new Function( 51 + '_e,_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','), 52 + 'return ' + astRoot(ast, '_n', transform ? '_t' : null) 53 + ); 54 + 55 + return makeMatcher(_exec, name, transform, ...expressions.map(_pattern)); 58 56 };
+27 -77
src/parser.js
··· 1 + const syntaxError = (char) => { 2 + throw new SyntaxError('Unexpected token "' + char + '"'); 3 + }; 4 + 1 5 export const parse = (quasis, expressions) => { 2 6 let quasiIndex = 0; 3 7 let stackIndex = 0; 4 8 5 9 const sequenceStack = []; 6 - const rootSequence = { 7 - type: 'sequence', 8 - sequence: [], 9 - alternation: null, 10 - }; 10 + const rootSequence = []; 11 11 12 12 let currentGroup = null; 13 13 let lastMatch; 14 14 let currentSequence = rootSequence; 15 15 16 - while (stackIndex < quasis.length + expressions.length) { 16 + for ( 17 + let quasiIndex = 0, stackIndex = 0; 18 + stackIndex < quasis.length + expressions.length; 19 + stackIndex++ 20 + ) { 17 21 if (stackIndex % 2 !== 0) { 18 - const expression = expressions[stackIndex++ >> 1]; 19 - 20 - currentSequence.sequence.push({ 21 - type: 'expression', 22 - expression, 23 - quantifier: null, 22 + currentSequence.push({ 23 + expression: expressions[stackIndex++ >> 1], 24 24 }); 25 25 } 26 26 27 27 const quasi = quasis[stackIndex >> 1]; 28 - while (quasiIndex < quasi.length) { 28 + for (quasiIndex = 0; quasiIndex < quasi.length; ) { 29 29 const char = quasi[quasiIndex++]; 30 - 31 30 if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { 32 - continue; 33 - } else if (char === '|' && currentSequence.sequence.length > 0) { 34 - currentSequence = currentSequence.alternation = { 35 - type: 'sequence', 36 - sequence: [], 37 - alternation: null, 38 - }; 39 - 40 - continue; 41 - } else if (char === ')' && currentSequence.sequence.length > 0) { 31 + } else if (char === '|' && currentSequence.length) { 32 + currentSequence = currentSequence.alternation = []; 33 + } else if (char === ')' && currentSequence.length) { 42 34 currentGroup = null; 43 35 currentSequence = sequenceStack.pop(); 44 - if (currentSequence) continue; 36 + if (!currentSequence) syntaxError(char); 45 37 } else if (char === '(') { 46 - currentGroup = { 47 - type: 'group', 48 - sequence: { 49 - type: 'sequence', 50 - sequence: [], 51 - alternation: null, 52 - }, 53 - capturing: true, 54 - lookahead: null, 55 - quantifier: null, 56 - }; 57 - 58 38 sequenceStack.push(currentSequence); 59 - currentSequence.sequence.push(currentGroup); 39 + currentSequence.push((currentGroup = { sequence: [] })); 60 40 currentSequence = currentGroup.sequence; 61 - continue; 62 - } else if ( 63 - char === '?' && 64 - currentSequence.sequence.length === 0 && 65 - currentGroup 66 - ) { 41 + } else if (char === '?' && !currentSequence.length && currentGroup) { 67 42 const nextChar = quasi[quasiIndex++]; 68 - if (!nextChar) { 69 - throw new SyntaxError('Unexpected end of input after ' + char); 70 - } 71 - 72 - if (nextChar === ':') { 73 - currentGroup.capturing = false; 74 - continue; 75 - } else if (nextChar === '=') { 76 - currentGroup.capturing = false; 77 - currentGroup.lookahead = 'positive'; 78 - continue; 79 - } else if (nextChar === '!') { 80 - currentGroup.capturing = false; 81 - currentGroup.lookahead = 'negative'; 82 - continue; 43 + if (nextChar === ':' || nextChar === '=' || nextChar === '!') { 44 + currentGroup.capture = nextChar; 45 + } else { 46 + syntaxError(char); 83 47 } 84 48 } else if ( 85 49 (char === '?' || char === '+' || char === '*') && 86 - (lastMatch = 87 - currentSequence.sequence[currentSequence.sequence.length - 1]) 50 + (lastMatch = currentSequence[currentSequence.length - 1]) 88 51 ) { 89 - if (lastMatch.type === 'group' && lastMatch.lookahead) { 90 - throw new SyntaxError('Unexpected quantifier on lookahead group'); 91 - } 92 - 93 - lastMatch.quantifier = { 94 - type: 'quantifier', 95 - required: char === '+', 96 - singular: char === '?', 97 - }; 98 - 99 - continue; 52 + lastMatch.quantifier = char; 53 + } else { 54 + syntaxError(char); 100 55 } 101 - 102 - throw new SyntaxError('Unexpected token ' + char); 103 56 } 104 - 105 - stackIndex++; 106 - quasiIndex = 0; 107 57 } 108 58 109 59 return rootSequence;
+34 -113
src/parser.test.js
··· 2 2 3 3 const parseTag = (quasis, ...expressions) => parse(quasis, expressions); 4 4 5 - it('supports parsing expressions', () => { 6 - expect(parseTag`${1}`).toEqual({ 7 - type: 'sequence', 8 - sequence: [ 9 - { 10 - type: 'expression', 11 - expression: 1, 12 - quantifier: null, 13 - }, 14 - ], 15 - alternation: null, 16 - }); 17 - }); 18 - 19 5 it('supports parsing expressions with quantifiers', () => { 20 6 let ast; 21 7 22 8 ast = parseTag`${1}?`; 23 - expect(ast).toHaveProperty('sequence.0.type', 'expression'); 24 - expect(ast).toHaveProperty('sequence.0.quantifier', { 25 - type: 'quantifier', 26 - required: false, 27 - singular: true, 28 - }); 9 + expect(ast).toHaveProperty('0.quantifier', '?'); 29 10 30 11 ast = parseTag`${1}+`; 31 - expect(ast).toHaveProperty('sequence.0.type', 'expression'); 32 - expect(ast).toHaveProperty('sequence.0.quantifier', { 33 - type: 'quantifier', 34 - required: true, 35 - singular: false, 36 - }); 12 + expect(ast).toHaveProperty('0.quantifier', '+'); 37 13 38 14 ast = parseTag`${1}*`; 39 - expect(ast).toHaveProperty('sequence.0.type', 'expression'); 40 - expect(ast).toHaveProperty('sequence.0.quantifier', { 41 - type: 'quantifier', 42 - required: false, 43 - singular: false, 44 - }); 15 + expect(ast).toHaveProperty('0.quantifier', '*'); 45 16 }); 46 17 47 18 it('supports top-level alternations', () => { 48 19 let ast; 49 20 50 21 ast = parseTag`${1} | ${2}`; 51 - expect(ast).toHaveProperty('sequence.length', 1); 52 - expect(ast).toHaveProperty('sequence.0.type', 'expression'); 53 - expect(ast).toHaveProperty('sequence.0.expression', 1); 54 - expect(ast).toHaveProperty('alternation.type', 'sequence'); 55 - expect(ast).toHaveProperty('alternation.sequence.0.expression', 2); 22 + expect(ast).toHaveProperty('length', 1); 23 + expect(ast).toHaveProperty('0.expression', 1); 24 + expect(ast).toHaveProperty('alternation.0.expression', 2); 56 25 57 26 ast = parseTag`${1}? | ${2}?`; 58 - expect(ast).toHaveProperty('sequence.0.quantifier.type', 'quantifier'); 59 - expect(ast).toHaveProperty( 60 - 'alternation.sequence.0.quantifier.type', 61 - 'quantifier' 62 - ); 27 + expect(ast).toHaveProperty('0.quantifier', '?'); 63 28 }); 64 29 65 30 it('supports groups with quantifiers', () => { 66 31 let ast; 67 32 68 33 ast = parseTag`(${1} ${2})`; 69 - expect(ast).toHaveProperty('sequence.length', 1); 70 - expect(ast).toHaveProperty('sequence.0.type', 'group'); 71 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 2); 72 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.0.expression', 1); 73 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.1.expression', 2); 34 + expect(ast).toHaveProperty('length', 1); 35 + expect(ast).toHaveProperty('0.sequence.length', 2); 36 + expect(ast).toHaveProperty('0.sequence.0.expression', 1); 37 + expect(ast).toHaveProperty('0.sequence.1.expression', 2); 74 38 75 39 ast = parseTag`(${1} ${2}?)?`; 76 - expect(ast).toHaveProperty('sequence.length', 1); 77 - expect(ast).toHaveProperty('sequence.0.type', 'group'); 78 - expect(ast).toHaveProperty('sequence.0.quantifier.type', 'quantifier'); 79 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.0.quantifier', null); 80 - expect(ast).toHaveProperty( 81 - 'sequence.0.sequence.sequence.1.quantifier.type', 82 - 'quantifier' 83 - ); 40 + expect(ast).toHaveProperty('length', 1); 41 + expect(ast).toHaveProperty('0.quantifier', '?'); 42 + expect(ast).toHaveProperty('0.sequence.0.quantifier', undefined); 84 43 }); 85 44 86 45 it('supports non-capturing groups', () => { 87 46 const ast = parseTag`(?: ${1})`; 88 - expect(ast).toHaveProperty('sequence.length', 1); 89 - expect(ast).toHaveProperty('sequence.0.type', 'group'); 90 - expect(ast).toHaveProperty('sequence.0.capturing', false); 91 - expect(ast).toHaveProperty('sequence.0.lookahead', null); 92 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1); 47 + expect(ast).toHaveProperty('length', 1); 48 + expect(ast).toHaveProperty('0.capture', ':'); 49 + expect(ast).toHaveProperty('0.sequence.length', 1); 93 50 }); 94 51 95 52 it('supports positive lookahead groups', () => { 96 53 const ast = parseTag`(?= ${1})`; 97 - expect(ast).toHaveProperty('sequence.length', 1); 98 - expect(ast).toHaveProperty('sequence.0.type', 'group'); 99 - expect(ast).toHaveProperty('sequence.0.capturing', false); 100 - expect(ast).toHaveProperty('sequence.0.lookahead', 'positive'); 101 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1); 54 + expect(ast).toHaveProperty('length', 1); 55 + expect(ast).toHaveProperty('0.capture', '='); 56 + expect(ast).toHaveProperty('0.sequence.length', 1); 102 57 }); 103 58 104 59 it('supports negative lookahead groups', () => { 105 60 const ast = parseTag`(?! ${1})`; 106 - expect(ast).toHaveProperty('sequence.length', 1); 107 - expect(ast).toHaveProperty('sequence.0.type', 'group'); 108 - expect(ast).toHaveProperty('sequence.0.capturing', false); 109 - expect(ast).toHaveProperty('sequence.0.lookahead', 'negative'); 110 - expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1); 111 - }); 112 - 113 - it('throws when a quantifier is combined with a lookahead', () => { 114 - expect(() => parseTag`(?! ${1})+`).toThrow(); 115 - expect(() => parseTag`(?! ${1})?`).toThrow(); 116 - expect(() => parseTag`(?! ${1})*`).toThrow(); 61 + expect(ast).toHaveProperty('length', 1); 62 + expect(ast).toHaveProperty('0.capture', '!'); 63 + expect(ast).toHaveProperty('0.sequence.length', 1); 117 64 }); 118 65 119 66 it('supports groups with alternates', () => { 120 67 expect(parseTag`(${1} | ${2}) ${3}`).toMatchInlineSnapshot(` 121 - Object { 122 - "alternation": null, 123 - "sequence": Array [ 124 - Object { 125 - "capturing": true, 126 - "lookahead": null, 127 - "quantifier": null, 128 - "sequence": Object { 129 - "alternation": Object { 130 - "alternation": null, 131 - "sequence": Array [ 132 - Object { 133 - "expression": 2, 134 - "quantifier": null, 135 - "type": "expression", 136 - }, 137 - ], 138 - "type": "sequence", 139 - }, 140 - "sequence": Array [ 141 - Object { 142 - "expression": 1, 143 - "quantifier": null, 144 - "type": "expression", 145 - }, 146 - ], 147 - "type": "sequence", 68 + Array [ 69 + Object { 70 + "sequence": Array [ 71 + Object { 72 + "expression": 1, 148 73 }, 149 - "type": "group", 150 - }, 151 - Object { 152 - "expression": 3, 153 - "quantifier": null, 154 - "type": "expression", 155 - }, 156 - ], 157 - "type": "sequence", 158 - } 74 + ], 75 + }, 76 + Object { 77 + "expression": 3, 78 + }, 79 + ] 159 80 `); 160 81 });
+178 -22
yarn.lock
··· 2 2 # yarn lockfile v1 3 3 4 4 5 + "@ampproject/remapping@0.2.0": 6 + version "0.2.0" 7 + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-0.2.0.tgz#07290a5c0f5eac8a4c33d38aa0d15a3416db432e" 8 + integrity sha512-a4EztS9/GOVQjX5Ol+Iz33TFhaXvYBF7aB6D8+Qz0/SCIxOm3UNRhGZiwcCuJ8/Ifc6NCogp3S48kc5hFxRpUw== 9 + dependencies: 10 + "@jridgewell/resolve-uri" "1.0.0" 11 + sourcemap-codec "1.4.8" 12 + 13 + "@ampproject/rollup-plugin-closure-compiler@^0.26.0": 14 + version "0.26.0" 15 + resolved "https://registry.yarnpkg.com/@ampproject/rollup-plugin-closure-compiler/-/rollup-plugin-closure-compiler-0.26.0.tgz#69f8265e5fdbf3e26905eaaedc60cb5982bd6be0" 16 + integrity sha512-wuHzGE6BDhDR0L7nUPlpQDPGiGnMw+b0B+cDPG0S5TatOmFNQva8KSNdBHan3L9RbvNyYXOXicuCrZtSoBfrBg== 17 + dependencies: 18 + "@ampproject/remapping" "0.2.0" 19 + acorn "7.2.0" 20 + acorn-walk "7.1.1" 21 + estree-walker "2.0.1" 22 + google-closure-compiler "20200517.0.0" 23 + magic-string "0.25.7" 24 + uuid "8.1.0" 25 + 5 26 "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": 6 27 version "7.8.3" 7 28 resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" ··· 229 250 "@babel/helper-simple-access" "^7.8.3" 230 251 babel-plugin-dynamic-import-node "^2.3.3" 231 252 232 - "@babel/plugin-transform-object-assign@^7.8.3": 233 - version "7.8.3" 234 - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.8.3.tgz#dc3b8dd50ef03837868a37b7df791f64f288538e" 235 - integrity sha512-i3LuN8tPDqUCRFu3dkzF2r1Nx0jp4scxtm7JxtIqI9he9Vk20YD+/zshdzR9JLsoBMlJlNR82a62vQExNEVx/Q== 236 - dependencies: 237 - "@babel/helper-plugin-utils" "^7.8.3" 238 - 239 253 "@babel/template@^7.3.3", "@babel/template@^7.8.3", "@babel/template@^7.8.6": 240 254 version "7.8.6" 241 255 resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" ··· 463 477 "@types/yargs" "^15.0.0" 464 478 chalk "^4.0.0" 465 479 480 + "@jridgewell/resolve-uri@1.0.0": 481 + version "1.0.0" 482 + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz#3fdf5798f0b49e90155896f6291df186eac06c83" 483 + integrity sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA== 484 + 466 485 "@rollup/plugin-buble@^0.21.3": 467 486 version "0.21.3" 468 487 resolved "https://registry.yarnpkg.com/@rollup/plugin-buble/-/plugin-buble-0.21.3.tgz#1649a915b1d051a4f430d40e7734a7f67a69b33e" ··· 505 524 estree-walker "^1.0.1" 506 525 picomatch "^2.2.2" 507 526 527 + "@rollup/pluginutils@^4.1.0": 528 + version "4.1.0" 529 + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" 530 + integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ== 531 + dependencies: 532 + estree-walker "^2.0.1" 533 + picomatch "^2.2.2" 534 + 508 535 "@samverschueren/stream-to-observable@^0.3.0": 509 536 version "0.3.0" 510 537 resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" ··· 670 697 resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" 671 698 integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== 672 699 673 - acorn-walk@^7.1.1: 700 + acorn-walk@7.1.1, acorn-walk@^7.1.1: 674 701 version "7.1.1" 675 702 resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" 676 703 integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== 677 704 705 + acorn@7.2.0, acorn@^7.1.1: 706 + version "7.2.0" 707 + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" 708 + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== 709 + 678 710 acorn@^6.4.1: 679 711 version "6.4.1" 680 712 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" 681 713 integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== 682 714 683 - acorn@^7.1.1: 684 - version "7.2.0" 685 - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" 686 - integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== 687 - 688 715 aggregate-error@^3.0.0: 689 716 version "3.0.1" 690 717 resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" ··· 1028 1055 resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 1029 1056 integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= 1030 1057 1031 - chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: 1058 + chalk@2.x, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: 1032 1059 version "2.4.2" 1033 1060 resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 1034 1061 integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== ··· 1102 1129 strip-ansi "^6.0.0" 1103 1130 wrap-ansi "^6.2.0" 1104 1131 1132 + clone-buffer@^1.0.0: 1133 + version "1.0.0" 1134 + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" 1135 + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= 1136 + 1137 + clone-stats@^1.0.0: 1138 + version "1.0.0" 1139 + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" 1140 + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= 1141 + 1105 1142 clone@^1.0.2: 1106 1143 version "1.0.4" 1107 1144 resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" 1108 1145 integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= 1109 1146 1147 + clone@^2.1.1: 1148 + version "2.1.2" 1149 + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" 1150 + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= 1151 + 1152 + cloneable-readable@^1.0.0: 1153 + version "1.1.3" 1154 + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" 1155 + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== 1156 + dependencies: 1157 + inherits "^2.0.1" 1158 + process-nextick-args "^2.0.0" 1159 + readable-stream "^2.3.5" 1160 + 1110 1161 co@^4.6.0: 1111 1162 version "4.6.0" 1112 1163 resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" ··· 1193 1244 resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 1194 1245 integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= 1195 1246 1196 - core-util-is@1.0.2: 1247 + core-util-is@1.0.2, core-util-is@~1.0.0: 1197 1248 version "1.0.2" 1198 1249 resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 1199 1250 integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= ··· 1461 1512 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 1462 1513 integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 1463 1514 1515 + estree-walker@2.0.1, estree-walker@^2.0.1: 1516 + version "2.0.1" 1517 + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.1.tgz#f8e030fb21cefa183b44b7ad516b747434e7a3e0" 1518 + integrity sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg== 1519 + 1464 1520 estree-walker@^0.6.1: 1465 1521 version "0.6.1" 1466 1522 resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" ··· 1743 1799 resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 1744 1800 integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 1745 1801 1802 + google-closure-compiler-java@^20200517.0.0: 1803 + version "20200517.0.0" 1804 + resolved "https://registry.yarnpkg.com/google-closure-compiler-java/-/google-closure-compiler-java-20200517.0.0.tgz#778370c22273c9085f4cf959ce063f8f112c02ac" 1805 + integrity sha512-JVZBiyyXwcYi6Yc3lO6dF2hMLJA4OzPm4/mgsem/tF1vk2HsWTnL3GTaBsPB2ENVZp0hoqsd4KgpPiG9ssNWxw== 1806 + 1807 + google-closure-compiler-js@^20200517.0.0: 1808 + version "20200517.0.0" 1809 + resolved "https://registry.yarnpkg.com/google-closure-compiler-js/-/google-closure-compiler-js-20200517.0.0.tgz#9cb0861f764073d1c4d3b7453b74073ccb1ecfb1" 1810 + integrity sha512-dz6dOUHx5nhdIqMRXacAYS8aJfLvw4IKxGg28Hq/zeeDPHlX3P3iBK20NgFDfT8zdushThymtMqChSy7C5eyfA== 1811 + 1812 + google-closure-compiler-linux@^20200517.0.0: 1813 + version "20200517.0.0" 1814 + resolved "https://registry.yarnpkg.com/google-closure-compiler-linux/-/google-closure-compiler-linux-20200517.0.0.tgz#2b9ecb634130060174aff5c52329a694ea4be68b" 1815 + integrity sha512-S5xPh6TtP+ESzZrmQLcDDqtZAsCVTbdI4VS98wQlN6IMZTd94nAnOCg9mrxQNAgop2t4sdsv/KuH0BGPUWEZ+w== 1816 + 1817 + google-closure-compiler-osx@^20200517.0.0: 1818 + version "20200517.0.0" 1819 + resolved "https://registry.yarnpkg.com/google-closure-compiler-osx/-/google-closure-compiler-osx-20200517.0.0.tgz#9394e9a2fd97e3729fc3bd2abcffff6aab2cfcaa" 1820 + integrity sha512-FWIcsKqLllLjdOBZd7azijVaObydgRd0obVNi63eUfC5MX6T4qxKumGCyor2UCNY6by2ESz+PlGqCFzFhZ6b2g== 1821 + 1822 + google-closure-compiler-windows@^20200517.0.0: 1823 + version "20200517.0.0" 1824 + resolved "https://registry.yarnpkg.com/google-closure-compiler-windows/-/google-closure-compiler-windows-20200517.0.0.tgz#c5cdde438c29458666a83358567b12072924ed6c" 1825 + integrity sha512-UXhjRGwS8deTkRla/riyVq3psscgMuw78lepEPtq5NgbumgJzY2+IQP9q+4MVOfJW58Rv0JUWKAFOnBBSZWcAQ== 1826 + 1827 + google-closure-compiler@20200517.0.0: 1828 + version "20200517.0.0" 1829 + resolved "https://registry.yarnpkg.com/google-closure-compiler/-/google-closure-compiler-20200517.0.0.tgz#6c47f99fc1be59bd4f9e23c5a8f2e66d64b54143" 1830 + integrity sha512-80W9zBS9Ajk1T5InWCfsoPohDmo5T1AAyw1rHh5+dgb/jPgwC65KhY+oJozTncf+/7tyQHJXozTARwhSlBUcMg== 1831 + dependencies: 1832 + chalk "2.x" 1833 + google-closure-compiler-java "^20200517.0.0" 1834 + google-closure-compiler-js "^20200517.0.0" 1835 + minimist "1.x" 1836 + vinyl "2.x" 1837 + vinyl-sourcemaps-apply "^0.2.0" 1838 + optionalDependencies: 1839 + google-closure-compiler-linux "^20200517.0.0" 1840 + google-closure-compiler-osx "^20200517.0.0" 1841 + google-closure-compiler-windows "^20200517.0.0" 1842 + 1746 1843 graceful-fs@^4.1.2, graceful-fs@^4.2.4: 1747 1844 version "4.2.4" 1748 1845 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" ··· 1907 2004 once "^1.3.0" 1908 2005 wrappy "1" 1909 2006 1910 - inherits@2: 2007 + inherits@2, inherits@^2.0.1, inherits@~2.0.3: 1911 2008 version "2.0.4" 1912 2009 resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 1913 2010 integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ··· 2104 2201 dependencies: 2105 2202 is-docker "^2.0.0" 2106 2203 2107 - isarray@1.0.0: 2204 + isarray@1.0.0, isarray@~1.0.0: 2108 2205 version "1.0.0" 2109 2206 resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 2110 2207 integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= ··· 2759 2856 slice-ansi "^4.0.0" 2760 2857 wrap-ansi "^6.2.0" 2761 2858 2762 - magic-string@^0.25.0, magic-string@^0.25.2, magic-string@^0.25.7: 2859 + magic-string@0.25.7, magic-string@^0.25.0, magic-string@^0.25.2, magic-string@^0.25.7: 2763 2860 version "0.25.7" 2764 2861 resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" 2765 2862 integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== ··· 2853 2950 dependencies: 2854 2951 brace-expansion "^1.1.7" 2855 2952 2856 - minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: 2953 + minimist@1.x, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: 2857 2954 version "1.2.5" 2858 2955 resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 2859 2956 integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== ··· 3241 3338 ansi-styles "^4.0.0" 3242 3339 react-is "^16.12.0" 3243 3340 3341 + process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: 3342 + version "2.0.1" 3343 + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 3344 + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 3345 + 3244 3346 prompts@^2.0.1: 3245 3347 version "2.3.2" 3246 3348 resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" ··· 3305 3407 parse-json "^5.0.0" 3306 3408 type-fest "^0.6.0" 3307 3409 3410 + readable-stream@^2.3.5: 3411 + version "2.3.7" 3412 + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 3413 + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 3414 + dependencies: 3415 + core-util-is "~1.0.0" 3416 + inherits "~2.0.3" 3417 + isarray "~1.0.0" 3418 + process-nextick-args "~2.0.0" 3419 + safe-buffer "~5.1.1" 3420 + string_decoder "~1.1.1" 3421 + util-deprecate "~1.0.1" 3422 + 3308 3423 regenerate-unicode-properties@^8.0.2: 3309 3424 version "8.2.0" 3310 3425 resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" ··· 3364 3479 resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 3365 3480 integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= 3366 3481 3482 + replace-ext@^1.0.0: 3483 + version "1.0.1" 3484 + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" 3485 + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== 3486 + 3367 3487 request-promise-core@1.1.3: 3368 3488 version "1.1.3" 3369 3489 resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" ··· 3504 3624 resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 3505 3625 integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 3506 3626 3507 - safe-buffer@~5.1.1: 3627 + safe-buffer@~5.1.0, safe-buffer@~5.1.1: 3508 3628 version "5.1.2" 3509 3629 resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 3510 3630 integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== ··· 3704 3824 resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 3705 3825 integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= 3706 3826 3707 - source-map@^0.5.0, source-map@^0.5.6: 3827 + source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6: 3708 3828 version "0.5.7" 3709 3829 resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 3710 3830 integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= ··· 3719 3839 resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" 3720 3840 integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== 3721 3841 3722 - sourcemap-codec@^1.4.4: 3842 + sourcemap-codec@1.4.8, sourcemap-codec@^1.4.4: 3723 3843 version "1.4.8" 3724 3844 resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" 3725 3845 integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== ··· 3860 3980 dependencies: 3861 3981 define-properties "^1.1.3" 3862 3982 es-abstract "^1.17.5" 3983 + 3984 + string_decoder@~1.1.1: 3985 + version "1.1.1" 3986 + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 3987 + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 3988 + dependencies: 3989 + safe-buffer "~5.1.0" 3863 3990 3864 3991 stringify-object@^3.3.0: 3865 3992 version "3.3.0" ··· 4126 4253 resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" 4127 4254 integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== 4128 4255 4256 + util-deprecate@~1.0.1: 4257 + version "1.0.2" 4258 + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 4259 + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 4260 + 4261 + uuid@8.1.0: 4262 + version "8.1.0" 4263 + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" 4264 + integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== 4265 + 4129 4266 uuid@^3.3.2: 4130 4267 version "3.4.0" 4131 4268 resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" ··· 4161 4298 assert-plus "^1.0.0" 4162 4299 core-util-is "1.0.2" 4163 4300 extsprintf "^1.2.0" 4301 + 4302 + vinyl-sourcemaps-apply@^0.2.0: 4303 + version "0.2.1" 4304 + resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" 4305 + integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= 4306 + dependencies: 4307 + source-map "^0.5.1" 4308 + 4309 + vinyl@2.x: 4310 + version "2.2.1" 4311 + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" 4312 + integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== 4313 + dependencies: 4314 + clone "^2.1.1" 4315 + clone-buffer "^1.0.0" 4316 + clone-stats "^1.0.0" 4317 + cloneable-readable "^1.0.0" 4318 + remove-trailing-separator "^1.0.1" 4319 + replace-ext "^1.0.0" 4164 4320 4165 4321 w3c-hr-time@^1.0.2: 4166 4322 version "1.0.2"