├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build.js ├── build ├── node.js └── web.js ├── index.html ├── index.js ├── lib ├── parser.js └── stringify.js ├── package-lock.json ├── package.json ├── spec ├── parser.spec.js └── stringify.spec.js └── webify.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # Trigger workflow on pushes to the master branch 7 | pull_request: # Trigger workflow on any pull request 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x, 18.x, 20.x] # Test on multiple Node.js versions 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: 'npm' # Enable caching for npm dependencies 24 | - name: Install dependencies 25 | run: npm install 26 | - name: Run tests 27 | run: npm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # package managers # 2 | #################### 3 | node_modules/ 4 | npm-debug.log 5 | 6 | # generated files # 7 | ###################### 8 | coverage/ 9 | .idea/ 10 | .DS_Store 11 | .DS_Store? 12 | ._* 13 | .Trashes 14 | Icon? 15 | ehthumbs.db 16 | Thumbs.db 17 | .tmp -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | bower_components/ 4 | coverage/ 5 | .idea/ 6 | .DS_Store 7 | .DS_Store? 8 | ._* 9 | .Trashes 10 | Icon? 11 | ehthumbs.db 12 | Thumbs.db 13 | .tmp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rafael Carício 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gradient Parser 2 | 3 | [](https://badge.fury.io/js/gradient-parser) 4 | 5 | ## About 6 | 7 | Parse CSS3 gradient definition and returns AST `object`. 8 | 9 | ## Examples 10 | 11 | ```JavaScript 12 | var gradient = require('gradient-parser'); 13 | var obj = gradient.parse('linear-gradient(30deg, #000, transparent)'); 14 | console.log(JSON.stringify(obj, null, 2)); 15 | ``` 16 | 17 | Results in: 18 | 19 | ```JSON 20 | [ 21 | { 22 | "type": "linear-gradient", 23 | "orientation": { 24 | "type": "angular", 25 | "value": "30" 26 | }, 27 | "colorStops": [ 28 | { 29 | "type": "hex", 30 | "value": "000" 31 | }, 32 | { 33 | "type": "literal", 34 | "value": "transparent" 35 | } 36 | ] 37 | } 38 | ] 39 | ``` 40 | 41 | ## Installation 42 | 43 | Install via npm: 44 | ```bash 45 | npm install gradient-parser 46 | ``` 47 | 48 | Import in Node.js: 49 | ```javascript 50 | const gradient = require('gradient-parser'); 51 | ``` 52 | 53 | For browser usage: 54 | ```html 55 | 56 | ``` 57 | 58 | Or [download the zip](https://github.com/rafaelcaricio/gradient-parser/archive/master.zip) 59 | 60 | ## Development 61 | 62 | ### Project Status 63 | 64 | Gradient-parser has been modernized (as of v1.1.0): 65 | - Removed Bower support in favor of npm exclusively 66 | - Replaced Grunt with a modern build system using esbuild 67 | - Added minification support 68 | - Updated dependencies to specific versions 69 | - Improved npm scripts for better developer experience 70 | 71 | ### Build 72 | 73 | Gradient-parser uses a modern build system with esbuild for building and minification. 74 | 75 | ```bash 76 | # Build both Node.js and web bundles 77 | npm run build 78 | 79 | # Build only Node.js bundle 80 | npm run build:node 81 | 82 | # Build only web bundle 83 | npm run build:web 84 | 85 | # Build minified bundles 86 | npm run build:minify 87 | ``` 88 | 89 | ### Testing 90 | 91 | Run the test suite with: 92 | 93 | ```bash 94 | npm test 95 | ``` 96 | 97 | ### Starting a local server 98 | 99 | You can run a simple HTTP server for development: 100 | 101 | ```bash 102 | npm start 103 | ``` 104 | 105 | ## API 106 | 107 | ### gradient.parse 108 | 109 | Accepts the gradient definitions as it is declared in `background-image` and returns an AST `object`. 110 | 111 | ## AST 112 | 113 | ### Common properties 114 | 115 | All nodes have the following properties. 116 | 117 | #### type 118 | 119 | `String`. The possible values are the ones listed in the Types section bellow. 120 | 121 | ### Types 122 | 123 | The available values of `node.type` are listed below, as well as the available properties of each node (other than the common properties listed above). 124 | 125 | ### linear-gradient 126 | 127 | - orientation: `Object` possible types `directional` or `angular`. 128 | - colorStops: `Array` of color stops of type `literal`, `hex`, `rgb`, or `rgba`. 129 | 130 | ### repeating-linear-gradient 131 | 132 | - orientation: `Object` possible types `directional` or `angular`. 133 | - colorStops: `Array` of color stops of type `literal`, `hex`, `rgb`, or `rgba`. 134 | 135 | ### radial-gradient 136 | 137 | - orientation: `Array` or `undefined`. `Array` of possible types `shape`, `default-radial`. 138 | - colorStops: `Array` of color stops of type `literal`, `hex`, `rgb`, or `rgba`. 139 | 140 | ### repeating-radial-gradient 141 | 142 | - orientation: `Array` or `undefined`. `Array` of possible types `shape`, `default-radial`. 143 | - colorStops: `Array` of color stops of type `literal`, `hex`, `rgb`, or `rgba`. 144 | 145 | ### directional 146 | 147 | - value: `String` possible values `left`, `top`, `bottom`, or `right`. 148 | 149 | ### angular 150 | 151 | - value: `Number` integer number. 152 | 153 | ### literal 154 | 155 | - value: `String` literal name of the color. 156 | 157 | ### hex 158 | 159 | - value: `String` hex value. 160 | 161 | ### rgb 162 | 163 | - value: `Array` of length 3 of `Number`'s. 164 | 165 | ### rgba 166 | 167 | - value: `Array` of length 4 or `Number`'s. 168 | 169 | ### shape 170 | 171 | - style: `Object` or `undefined` possible types `extent-keyword`, `px`, `em`, `%`, or `positioning-keyword`. 172 | - value: `String` possible values `ellipse` or `circle`. 173 | - at: `Object` of attributes: 174 | - x: `Object` possible types `extent-keyword`, `px`, `em`, `%`, or `positioning-keyword`. 175 | - y: `Object` possible types `extent-keyword`, `px`, `em`, `%`, or `positioning-keyword`. 176 | 177 | ### default-radial 178 | 179 | - at: `Object` of attributes: 180 | - x: `Object` possible types `extent-keyword`, `px`, `em`, `%`, or `positioning-keyword`. 181 | - y: `Object` possible types `extent-keyword`, `px`, `em`, `%`, or `positioning-keyword`. 182 | 183 | ### positioning-keyword 184 | 185 | - value: `String` possible values `center`, `left`, `top`, `bottom`, or `right`. 186 | 187 | ### extent-keyword 188 | 189 | - value: `String` possible values `closest-side`, `closest-corner`, `farthest-side`, `farthest-corner`, `contain`, or `cover`. 190 | 191 | ## License 192 | 193 | (The MIT License) 194 | 195 | Copyright (c) 2014-2025 Rafael Caricio rafael@caricio.com 196 | 197 | Permission is hereby granted, free of charge, to any person obtaining 198 | a copy of this software and associated documentation files (the 199 | 'Software'), to deal in the Software without restriction, including 200 | without limitation the rights to use, copy, modify, merge, publish, 201 | distribute, sublicense, and/or sell copies of the Software, and to 202 | permit persons to whom the Software is furnished to do so, subject to 203 | the following conditions: 204 | 205 | The above copyright notice and this permission notice shall be 206 | included in all copies or substantial portions of the Software. 207 | 208 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 209 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 210 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 211 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 212 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 213 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 214 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 215 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const esbuild = require('esbuild'); 4 | 5 | // Determine which build to run based on command-line args 6 | const args = process.argv.slice(2); 7 | const buildNode = args.includes('--node') || !args.length; 8 | const buildWeb = args.includes('--web') || !args.length; 9 | const minify = args.includes('--minify'); 10 | 11 | // Helper to ensure directories exist 12 | const ensureDir = (dirPath) => { 13 | if (!fs.existsSync(dirPath)) { 14 | fs.mkdirSync(dirPath, { recursive: true }); 15 | } 16 | }; 17 | 18 | // Helper function for minification 19 | const minifyCode = async (code, outputFile) => { 20 | const result = await esbuild.transform(code, { 21 | minify: true, 22 | target: 'es6', 23 | }); 24 | 25 | fs.writeFileSync(outputFile, result.code); 26 | return result.code; 27 | }; 28 | 29 | // Make sure the build directory exists 30 | ensureDir(path.join(__dirname, 'build')); 31 | 32 | // Async function to handle potential async operations like minification 33 | async function build() { 34 | // Build for Node.js 35 | if (buildNode) { 36 | console.log('Building node.js bundle...'); 37 | 38 | // Read the source files 39 | const stringifyContent = fs.readFileSync(path.join(__dirname, 'lib', 'stringify.js'), 'utf8'); 40 | const parserContent = fs.readFileSync(path.join(__dirname, 'lib', 'parser.js'), 'utf8'); 41 | const indexContent = fs.readFileSync(path.join(__dirname, 'index.js'), 'utf8'); 42 | 43 | // Concatenate the files 44 | const nodeBundle = `${stringifyContent}\n${parserContent}\n${indexContent}`; 45 | 46 | // Write the bundle 47 | const outputPath = path.join(__dirname, 'build', 'node.js'); 48 | 49 | if (minify) { 50 | await minifyCode(nodeBundle, outputPath); 51 | console.log('✓ Node.js bundle created and minified successfully'); 52 | } else { 53 | fs.writeFileSync(outputPath, nodeBundle); 54 | console.log('✓ Node.js bundle created successfully'); 55 | } 56 | } 57 | 58 | // Build for Web 59 | if (buildWeb) { 60 | console.log('Building web.js bundle...'); 61 | 62 | // Read the source files 63 | const webifyContent = fs.readFileSync(path.join(__dirname, 'webify.js'), 'utf8'); 64 | const parserContent = fs.readFileSync(path.join(__dirname, 'lib', 'parser.js'), 'utf8'); 65 | const stringifyContent = fs.readFileSync(path.join(__dirname, 'lib', 'stringify.js'), 'utf8'); 66 | 67 | // Concatenate the files 68 | const webBundle = `${webifyContent}\n${parserContent}\n${stringifyContent}`; 69 | 70 | // Write the bundle 71 | const outputPath = path.join(__dirname, 'build', 'web.js'); 72 | 73 | if (minify) { 74 | await minifyCode(webBundle, outputPath); 75 | console.log('✓ Web bundle created and minified successfully'); 76 | } else { 77 | fs.writeFileSync(outputPath, webBundle); 78 | console.log('✓ Web bundle created successfully'); 79 | } 80 | } 81 | 82 | // If no arguments were provided, we built both bundles 83 | if (!args.length) { 84 | console.log('✓ All bundles built successfully'); 85 | } 86 | } 87 | 88 | // Execute the build 89 | console.log(`Building with options: ${minify ? 'minify' : 'no minify'} ${buildNode ? 'node' : ''} ${buildWeb ? 'web' : ''}`) 90 | build().catch(err => { 91 | console.error('Build failed:', err); 92 | process.exit(1); 93 | }); 94 | -------------------------------------------------------------------------------- /build/node.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | var GradientParser = (GradientParser || {}); 6 | 7 | GradientParser.stringify = (function() { 8 | 9 | var visitor = { 10 | 11 | 'visit_linear-gradient': function(node) { 12 | return visitor.visit_gradient(node); 13 | }, 14 | 15 | 'visit_repeating-linear-gradient': function(node) { 16 | return visitor.visit_gradient(node); 17 | }, 18 | 19 | 'visit_radial-gradient': function(node) { 20 | return visitor.visit_gradient(node); 21 | }, 22 | 23 | 'visit_repeating-radial-gradient': function(node) { 24 | return visitor.visit_gradient(node); 25 | }, 26 | 27 | 'visit_gradient': function(node) { 28 | var orientation = visitor.visit(node.orientation); 29 | if (orientation) { 30 | orientation += ', '; 31 | } 32 | 33 | return node.type + '(' + orientation + visitor.visit(node.colorStops) + ')'; 34 | }, 35 | 36 | 'visit_shape': function(node) { 37 | var result = node.value, 38 | at = visitor.visit(node.at), 39 | style = visitor.visit(node.style); 40 | 41 | if (style) { 42 | result += ' ' + style; 43 | } 44 | 45 | if (at) { 46 | result += ' at ' + at; 47 | } 48 | 49 | return result; 50 | }, 51 | 52 | 'visit_default-radial': function(node) { 53 | var result = '', 54 | at = visitor.visit(node.at); 55 | 56 | if (at) { 57 | result += at; 58 | } 59 | return result; 60 | }, 61 | 62 | 'visit_extent-keyword': function(node) { 63 | var result = node.value, 64 | at = visitor.visit(node.at); 65 | 66 | if (at) { 67 | result += ' at ' + at; 68 | } 69 | 70 | return result; 71 | }, 72 | 73 | 'visit_position-keyword': function(node) { 74 | return node.value; 75 | }, 76 | 77 | 'visit_position': function(node) { 78 | return visitor.visit(node.value.x) + ' ' + visitor.visit(node.value.y); 79 | }, 80 | 81 | 'visit_%': function(node) { 82 | return node.value + '%'; 83 | }, 84 | 85 | 'visit_em': function(node) { 86 | return node.value + 'em'; 87 | }, 88 | 89 | 'visit_px': function(node) { 90 | return node.value + 'px'; 91 | }, 92 | 93 | 'visit_calc': function(node) { 94 | return 'calc(' + node.value + ')'; 95 | }, 96 | 97 | 'visit_literal': function(node) { 98 | return visitor.visit_color(node.value, node); 99 | }, 100 | 101 | 'visit_hex': function(node) { 102 | return visitor.visit_color('#' + node.value, node); 103 | }, 104 | 105 | 'visit_rgb': function(node) { 106 | return visitor.visit_color('rgb(' + node.value.join(', ') + ')', node); 107 | }, 108 | 109 | 'visit_rgba': function(node) { 110 | return visitor.visit_color('rgba(' + node.value.join(', ') + ')', node); 111 | }, 112 | 113 | 'visit_hsl': function(node) { 114 | return visitor.visit_color('hsl(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%)', node); 115 | }, 116 | 117 | 'visit_hsla': function(node) { 118 | return visitor.visit_color('hsla(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%, ' + node.value[3] + ')', node); 119 | }, 120 | 121 | 'visit_var': function(node) { 122 | return visitor.visit_color('var(' + node.value + ')', node); 123 | }, 124 | 125 | 'visit_color': function(resultColor, node) { 126 | var result = resultColor, 127 | length = visitor.visit(node.length); 128 | 129 | if (length) { 130 | result += ' ' + length; 131 | } 132 | return result; 133 | }, 134 | 135 | 'visit_angular': function(node) { 136 | return node.value + 'deg'; 137 | }, 138 | 139 | 'visit_directional': function(node) { 140 | return 'to ' + node.value; 141 | }, 142 | 143 | 'visit_array': function(elements) { 144 | var result = '', 145 | size = elements.length; 146 | 147 | elements.forEach(function(element, i) { 148 | result += visitor.visit(element); 149 | if (i < size - 1) { 150 | result += ', '; 151 | } 152 | }); 153 | 154 | return result; 155 | }, 156 | 157 | 'visit_object': function(obj) { 158 | if (obj.width && obj.height) { 159 | return visitor.visit(obj.width) + ' ' + visitor.visit(obj.height); 160 | } 161 | return ''; 162 | }, 163 | 164 | 'visit': function(element) { 165 | if (!element) { 166 | return ''; 167 | } 168 | var result = ''; 169 | 170 | if (element instanceof Array) { 171 | return visitor.visit_array(element); 172 | } else if (typeof element === 'object' && !element.type) { 173 | return visitor.visit_object(element); 174 | } else if (element.type) { 175 | var nodeVisitor = visitor['visit_' + element.type]; 176 | if (nodeVisitor) { 177 | return nodeVisitor(element); 178 | } else { 179 | throw Error('Missing visitor visit_' + element.type); 180 | } 181 | } else { 182 | throw Error('Invalid node.'); 183 | } 184 | } 185 | 186 | }; 187 | 188 | return function(root) { 189 | return visitor.visit(root); 190 | }; 191 | })(); 192 | 193 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 194 | // Use of this source code is governed by a BSD-style license that can be 195 | // found in the LICENSE file. 196 | 197 | var GradientParser = (GradientParser || {}); 198 | 199 | GradientParser.parse = (function() { 200 | 201 | var tokens = { 202 | linearGradient: /^(\-(webkit|o|ms|moz)\-)?(linear\-gradient)/i, 203 | repeatingLinearGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-linear\-gradient)/i, 204 | radialGradient: /^(\-(webkit|o|ms|moz)\-)?(radial\-gradient)/i, 205 | repeatingRadialGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-radial\-gradient)/i, 206 | sideOrCorner: /^to (left (top|bottom)|right (top|bottom)|top (left|right)|bottom (left|right)|left|right|top|bottom)/i, 207 | extentKeywords: /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/, 208 | positionKeywords: /^(left|center|right|top|bottom)/i, 209 | pixelValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))px/, 210 | percentageValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))\%/, 211 | emValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))em/, 212 | angleValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))deg/, 213 | radianValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))rad/, 214 | startCall: /^\(/, 215 | endCall: /^\)/, 216 | comma: /^,/, 217 | hexColor: /^\#([0-9a-fA-F]+)/, 218 | literalColor: /^([a-zA-Z]+)/, 219 | rgbColor: /^rgb/i, 220 | rgbaColor: /^rgba/i, 221 | varColor: /^var/i, 222 | calcValue: /^calc/i, 223 | variableName: /^(--[a-zA-Z0-9-,\s\#]+)/, 224 | number: /^(([0-9]*\.[0-9]+)|([0-9]+\.?))/, 225 | hslColor: /^hsl/i, 226 | hslaColor: /^hsla/i, 227 | }; 228 | 229 | var input = ''; 230 | 231 | function error(msg) { 232 | var err = new Error(input + ': ' + msg); 233 | err.source = input; 234 | throw err; 235 | } 236 | 237 | function getAST() { 238 | var ast = matchListDefinitions(); 239 | 240 | if (input.length > 0) { 241 | error('Invalid input not EOF'); 242 | } 243 | 244 | return ast; 245 | } 246 | 247 | function matchListDefinitions() { 248 | return matchListing(matchDefinition); 249 | } 250 | 251 | function matchDefinition() { 252 | return matchGradient( 253 | 'linear-gradient', 254 | tokens.linearGradient, 255 | matchLinearOrientation) || 256 | 257 | matchGradient( 258 | 'repeating-linear-gradient', 259 | tokens.repeatingLinearGradient, 260 | matchLinearOrientation) || 261 | 262 | matchGradient( 263 | 'radial-gradient', 264 | tokens.radialGradient, 265 | matchListRadialOrientations) || 266 | 267 | matchGradient( 268 | 'repeating-radial-gradient', 269 | tokens.repeatingRadialGradient, 270 | matchListRadialOrientations); 271 | } 272 | 273 | function matchGradient(gradientType, pattern, orientationMatcher) { 274 | return matchCall(pattern, function(captures) { 275 | 276 | var orientation = orientationMatcher(); 277 | if (orientation) { 278 | if (!scan(tokens.comma)) { 279 | error('Missing comma before color stops'); 280 | } 281 | } 282 | 283 | return { 284 | type: gradientType, 285 | orientation: orientation, 286 | colorStops: matchListing(matchColorStop) 287 | }; 288 | }); 289 | } 290 | 291 | function matchCall(pattern, callback) { 292 | var captures = scan(pattern); 293 | 294 | if (captures) { 295 | if (!scan(tokens.startCall)) { 296 | error('Missing ('); 297 | } 298 | 299 | var result = callback(captures); 300 | 301 | if (!scan(tokens.endCall)) { 302 | error('Missing )'); 303 | } 304 | 305 | return result; 306 | } 307 | } 308 | 309 | function matchLinearOrientation() { 310 | // Check for standard CSS3 "to" direction 311 | var sideOrCorner = matchSideOrCorner(); 312 | if (sideOrCorner) { 313 | return sideOrCorner; 314 | } 315 | 316 | // Check for legacy single keyword direction (e.g., "right", "top") 317 | var legacyDirection = match('position-keyword', tokens.positionKeywords, 1); 318 | if (legacyDirection) { 319 | // For legacy syntax, we convert to the directional type 320 | return { 321 | type: 'directional', 322 | value: legacyDirection.value 323 | }; 324 | } 325 | 326 | // If neither, check for angle 327 | return matchAngle(); 328 | } 329 | 330 | function matchSideOrCorner() { 331 | return match('directional', tokens.sideOrCorner, 1); 332 | } 333 | 334 | function matchAngle() { 335 | return match('angular', tokens.angleValue, 1) || 336 | match('angular', tokens.radianValue, 1); 337 | } 338 | 339 | function matchListRadialOrientations() { 340 | var radialOrientations, 341 | radialOrientation = matchRadialOrientation(), 342 | lookaheadCache; 343 | 344 | if (radialOrientation) { 345 | radialOrientations = []; 346 | radialOrientations.push(radialOrientation); 347 | 348 | lookaheadCache = input; 349 | if (scan(tokens.comma)) { 350 | radialOrientation = matchRadialOrientation(); 351 | if (radialOrientation) { 352 | radialOrientations.push(radialOrientation); 353 | } else { 354 | input = lookaheadCache; 355 | } 356 | } 357 | } 358 | 359 | return radialOrientations; 360 | } 361 | 362 | function matchRadialOrientation() { 363 | var radialType = matchCircle() || 364 | matchEllipse(); 365 | 366 | if (radialType) { 367 | radialType.at = matchAtPosition(); 368 | } else { 369 | var extent = matchExtentKeyword(); 370 | if (extent) { 371 | radialType = extent; 372 | var positionAt = matchAtPosition(); 373 | if (positionAt) { 374 | radialType.at = positionAt; 375 | } 376 | } else { 377 | // Check for "at" position first, which is a common browser output format 378 | var atPosition = matchAtPosition(); 379 | if (atPosition) { 380 | radialType = { 381 | type: 'default-radial', 382 | at: atPosition 383 | }; 384 | } else { 385 | var defaultPosition = matchPositioning(); 386 | if (defaultPosition) { 387 | radialType = { 388 | type: 'default-radial', 389 | at: defaultPosition 390 | }; 391 | } 392 | } 393 | } 394 | } 395 | 396 | return radialType; 397 | } 398 | 399 | function matchCircle() { 400 | var circle = match('shape', /^(circle)/i, 0); 401 | 402 | if (circle) { 403 | circle.style = matchLength() || matchExtentKeyword(); 404 | } 405 | 406 | return circle; 407 | } 408 | 409 | function matchEllipse() { 410 | var ellipse = match('shape', /^(ellipse)/i, 0); 411 | 412 | if (ellipse) { 413 | ellipse.style = matchPositioning() || matchDistance() || matchExtentKeyword(); 414 | } 415 | 416 | return ellipse; 417 | } 418 | 419 | function matchExtentKeyword() { 420 | return match('extent-keyword', tokens.extentKeywords, 1); 421 | } 422 | 423 | function matchAtPosition() { 424 | if (match('position', /^at/, 0)) { 425 | var positioning = matchPositioning(); 426 | 427 | if (!positioning) { 428 | error('Missing positioning value'); 429 | } 430 | 431 | return positioning; 432 | } 433 | } 434 | 435 | function matchPositioning() { 436 | var location = matchCoordinates(); 437 | 438 | if (location.x || location.y) { 439 | return { 440 | type: 'position', 441 | value: location 442 | }; 443 | } 444 | } 445 | 446 | function matchCoordinates() { 447 | return { 448 | x: matchDistance(), 449 | y: matchDistance() 450 | }; 451 | } 452 | 453 | function matchListing(matcher) { 454 | var captures = matcher(), 455 | result = []; 456 | 457 | if (captures) { 458 | result.push(captures); 459 | while (scan(tokens.comma)) { 460 | captures = matcher(); 461 | if (captures) { 462 | result.push(captures); 463 | } else { 464 | error('One extra comma'); 465 | } 466 | } 467 | } 468 | 469 | return result; 470 | } 471 | 472 | function matchColorStop() { 473 | var color = matchColor(); 474 | 475 | if (!color) { 476 | error('Expected color definition'); 477 | } 478 | 479 | color.length = matchDistance(); 480 | return color; 481 | } 482 | 483 | function matchColor() { 484 | return matchHexColor() || 485 | matchHSLAColor() || 486 | matchHSLColor() || 487 | matchRGBAColor() || 488 | matchRGBColor() || 489 | matchVarColor() || 490 | matchLiteralColor(); 491 | } 492 | 493 | function matchLiteralColor() { 494 | return match('literal', tokens.literalColor, 0); 495 | } 496 | 497 | function matchHexColor() { 498 | return match('hex', tokens.hexColor, 1); 499 | } 500 | 501 | function matchRGBColor() { 502 | return matchCall(tokens.rgbColor, function() { 503 | return { 504 | type: 'rgb', 505 | value: matchListing(matchNumber) 506 | }; 507 | }); 508 | } 509 | 510 | function matchRGBAColor() { 511 | return matchCall(tokens.rgbaColor, function() { 512 | return { 513 | type: 'rgba', 514 | value: matchListing(matchNumber) 515 | }; 516 | }); 517 | } 518 | 519 | function matchVarColor() { 520 | return matchCall(tokens.varColor, function () { 521 | return { 522 | type: 'var', 523 | value: matchVariableName() 524 | }; 525 | }); 526 | } 527 | 528 | function matchHSLColor() { 529 | return matchCall(tokens.hslColor, function() { 530 | // Check for percentage before trying to parse the hue 531 | var lookahead = scan(tokens.percentageValue); 532 | if (lookahead) { 533 | error('HSL hue value must be a number in degrees (0-360) or normalized (-360 to 360), not a percentage'); 534 | } 535 | 536 | var hue = matchNumber(); 537 | scan(tokens.comma); 538 | var captures = scan(tokens.percentageValue); 539 | var sat = captures ? captures[1] : null; 540 | scan(tokens.comma); 541 | captures = scan(tokens.percentageValue); 542 | var light = captures ? captures[1] : null; 543 | if (!sat || !light) { 544 | error('Expected percentage value for saturation and lightness in HSL'); 545 | } 546 | return { 547 | type: 'hsl', 548 | value: [hue, sat, light] 549 | }; 550 | }); 551 | } 552 | 553 | function matchHSLAColor() { 554 | return matchCall(tokens.hslaColor, function() { 555 | var hue = matchNumber(); 556 | scan(tokens.comma); 557 | var captures = scan(tokens.percentageValue); 558 | var sat = captures ? captures[1] : null; 559 | scan(tokens.comma); 560 | captures = scan(tokens.percentageValue); 561 | var light = captures ? captures[1] : null; 562 | scan(tokens.comma); 563 | var alpha = matchNumber(); 564 | if (!sat || !light) { 565 | error('Expected percentage value for saturation and lightness in HSLA'); 566 | } 567 | return { 568 | type: 'hsla', 569 | value: [hue, sat, light, alpha] 570 | }; 571 | }); 572 | } 573 | 574 | function matchPercentage() { 575 | var captures = scan(tokens.percentageValue); 576 | return captures ? captures[1] : null; 577 | } 578 | 579 | function matchVariableName() { 580 | return scan(tokens.variableName)[1]; 581 | } 582 | 583 | function matchNumber() { 584 | return scan(tokens.number)[1]; 585 | } 586 | 587 | function matchDistance() { 588 | return match('%', tokens.percentageValue, 1) || 589 | matchPositionKeyword() || 590 | matchCalc() || 591 | matchLength(); 592 | } 593 | 594 | function matchPositionKeyword() { 595 | return match('position-keyword', tokens.positionKeywords, 1); 596 | } 597 | 598 | function matchCalc() { 599 | return matchCall(tokens.calcValue, function() { 600 | var openParenCount = 1; // Start with the opening parenthesis from calc( 601 | var i = 0; 602 | 603 | // Parse through the content looking for balanced parentheses 604 | while (openParenCount > 0 && i < input.length) { 605 | var char = input.charAt(i); 606 | if (char === '(') { 607 | openParenCount++; 608 | } else if (char === ')') { 609 | openParenCount--; 610 | } 611 | i++; 612 | } 613 | 614 | // If we exited because we ran out of input but still have open parentheses, error 615 | if (openParenCount > 0) { 616 | error('Missing closing parenthesis in calc() expression'); 617 | } 618 | 619 | // Get the content inside the calc() without the last closing paren 620 | var calcContent = input.substring(0, i - 1); 621 | 622 | // Consume the calc expression content 623 | consume(i - 1); // -1 because we don't want to consume the closing parenthesis 624 | 625 | return { 626 | type: 'calc', 627 | value: calcContent 628 | }; 629 | }); 630 | } 631 | 632 | function matchLength() { 633 | return match('px', tokens.pixelValue, 1) || 634 | match('em', tokens.emValue, 1); 635 | } 636 | 637 | function match(type, pattern, captureIndex) { 638 | var captures = scan(pattern); 639 | if (captures) { 640 | return { 641 | type: type, 642 | value: captures[captureIndex] 643 | }; 644 | } 645 | } 646 | 647 | function scan(regexp) { 648 | var captures, 649 | blankCaptures; 650 | 651 | blankCaptures = /^[\n\r\t\s]+/.exec(input); 652 | if (blankCaptures) { 653 | consume(blankCaptures[0].length); 654 | } 655 | 656 | captures = regexp.exec(input); 657 | if (captures) { 658 | consume(captures[0].length); 659 | } 660 | 661 | return captures; 662 | } 663 | 664 | function consume(size) { 665 | input = input.substr(size); 666 | } 667 | 668 | return function(code) { 669 | input = code.toString().trim(); 670 | // Remove trailing semicolon if present 671 | if (input.endsWith(';')) { 672 | input = input.slice(0, -1); 673 | } 674 | return getAST(); 675 | }; 676 | })(); 677 | 678 | exports.parse = GradientParser.parse; 679 | exports.stringify = GradientParser.stringify; 680 | -------------------------------------------------------------------------------- /build/web.js: -------------------------------------------------------------------------------- 1 | var GradientParser = (window.GradientParser || {}); 2 | 3 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. 6 | 7 | var GradientParser = (GradientParser || {}); 8 | 9 | GradientParser.parse = (function() { 10 | 11 | var tokens = { 12 | linearGradient: /^(\-(webkit|o|ms|moz)\-)?(linear\-gradient)/i, 13 | repeatingLinearGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-linear\-gradient)/i, 14 | radialGradient: /^(\-(webkit|o|ms|moz)\-)?(radial\-gradient)/i, 15 | repeatingRadialGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-radial\-gradient)/i, 16 | sideOrCorner: /^to (left (top|bottom)|right (top|bottom)|top (left|right)|bottom (left|right)|left|right|top|bottom)/i, 17 | extentKeywords: /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/, 18 | positionKeywords: /^(left|center|right|top|bottom)/i, 19 | pixelValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))px/, 20 | percentageValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))\%/, 21 | emValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))em/, 22 | angleValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))deg/, 23 | radianValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))rad/, 24 | startCall: /^\(/, 25 | endCall: /^\)/, 26 | comma: /^,/, 27 | hexColor: /^\#([0-9a-fA-F]+)/, 28 | literalColor: /^([a-zA-Z]+)/, 29 | rgbColor: /^rgb/i, 30 | rgbaColor: /^rgba/i, 31 | varColor: /^var/i, 32 | calcValue: /^calc/i, 33 | variableName: /^(--[a-zA-Z0-9-,\s\#]+)/, 34 | number: /^(([0-9]*\.[0-9]+)|([0-9]+\.?))/, 35 | hslColor: /^hsl/i, 36 | hslaColor: /^hsla/i, 37 | }; 38 | 39 | var input = ''; 40 | 41 | function error(msg) { 42 | var err = new Error(input + ': ' + msg); 43 | err.source = input; 44 | throw err; 45 | } 46 | 47 | function getAST() { 48 | var ast = matchListDefinitions(); 49 | 50 | if (input.length > 0) { 51 | error('Invalid input not EOF'); 52 | } 53 | 54 | return ast; 55 | } 56 | 57 | function matchListDefinitions() { 58 | return matchListing(matchDefinition); 59 | } 60 | 61 | function matchDefinition() { 62 | return matchGradient( 63 | 'linear-gradient', 64 | tokens.linearGradient, 65 | matchLinearOrientation) || 66 | 67 | matchGradient( 68 | 'repeating-linear-gradient', 69 | tokens.repeatingLinearGradient, 70 | matchLinearOrientation) || 71 | 72 | matchGradient( 73 | 'radial-gradient', 74 | tokens.radialGradient, 75 | matchListRadialOrientations) || 76 | 77 | matchGradient( 78 | 'repeating-radial-gradient', 79 | tokens.repeatingRadialGradient, 80 | matchListRadialOrientations); 81 | } 82 | 83 | function matchGradient(gradientType, pattern, orientationMatcher) { 84 | return matchCall(pattern, function(captures) { 85 | 86 | var orientation = orientationMatcher(); 87 | if (orientation) { 88 | if (!scan(tokens.comma)) { 89 | error('Missing comma before color stops'); 90 | } 91 | } 92 | 93 | return { 94 | type: gradientType, 95 | orientation: orientation, 96 | colorStops: matchListing(matchColorStop) 97 | }; 98 | }); 99 | } 100 | 101 | function matchCall(pattern, callback) { 102 | var captures = scan(pattern); 103 | 104 | if (captures) { 105 | if (!scan(tokens.startCall)) { 106 | error('Missing ('); 107 | } 108 | 109 | var result = callback(captures); 110 | 111 | if (!scan(tokens.endCall)) { 112 | error('Missing )'); 113 | } 114 | 115 | return result; 116 | } 117 | } 118 | 119 | function matchLinearOrientation() { 120 | // Check for standard CSS3 "to" direction 121 | var sideOrCorner = matchSideOrCorner(); 122 | if (sideOrCorner) { 123 | return sideOrCorner; 124 | } 125 | 126 | // Check for legacy single keyword direction (e.g., "right", "top") 127 | var legacyDirection = match('position-keyword', tokens.positionKeywords, 1); 128 | if (legacyDirection) { 129 | // For legacy syntax, we convert to the directional type 130 | return { 131 | type: 'directional', 132 | value: legacyDirection.value 133 | }; 134 | } 135 | 136 | // If neither, check for angle 137 | return matchAngle(); 138 | } 139 | 140 | function matchSideOrCorner() { 141 | return match('directional', tokens.sideOrCorner, 1); 142 | } 143 | 144 | function matchAngle() { 145 | return match('angular', tokens.angleValue, 1) || 146 | match('angular', tokens.radianValue, 1); 147 | } 148 | 149 | function matchListRadialOrientations() { 150 | var radialOrientations, 151 | radialOrientation = matchRadialOrientation(), 152 | lookaheadCache; 153 | 154 | if (radialOrientation) { 155 | radialOrientations = []; 156 | radialOrientations.push(radialOrientation); 157 | 158 | lookaheadCache = input; 159 | if (scan(tokens.comma)) { 160 | radialOrientation = matchRadialOrientation(); 161 | if (radialOrientation) { 162 | radialOrientations.push(radialOrientation); 163 | } else { 164 | input = lookaheadCache; 165 | } 166 | } 167 | } 168 | 169 | return radialOrientations; 170 | } 171 | 172 | function matchRadialOrientation() { 173 | var radialType = matchCircle() || 174 | matchEllipse(); 175 | 176 | if (radialType) { 177 | radialType.at = matchAtPosition(); 178 | } else { 179 | var extent = matchExtentKeyword(); 180 | if (extent) { 181 | radialType = extent; 182 | var positionAt = matchAtPosition(); 183 | if (positionAt) { 184 | radialType.at = positionAt; 185 | } 186 | } else { 187 | // Check for "at" position first, which is a common browser output format 188 | var atPosition = matchAtPosition(); 189 | if (atPosition) { 190 | radialType = { 191 | type: 'default-radial', 192 | at: atPosition 193 | }; 194 | } else { 195 | var defaultPosition = matchPositioning(); 196 | if (defaultPosition) { 197 | radialType = { 198 | type: 'default-radial', 199 | at: defaultPosition 200 | }; 201 | } 202 | } 203 | } 204 | } 205 | 206 | return radialType; 207 | } 208 | 209 | function matchCircle() { 210 | var circle = match('shape', /^(circle)/i, 0); 211 | 212 | if (circle) { 213 | circle.style = matchLength() || matchExtentKeyword(); 214 | } 215 | 216 | return circle; 217 | } 218 | 219 | function matchEllipse() { 220 | var ellipse = match('shape', /^(ellipse)/i, 0); 221 | 222 | if (ellipse) { 223 | ellipse.style = matchPositioning() || matchDistance() || matchExtentKeyword(); 224 | } 225 | 226 | return ellipse; 227 | } 228 | 229 | function matchExtentKeyword() { 230 | return match('extent-keyword', tokens.extentKeywords, 1); 231 | } 232 | 233 | function matchAtPosition() { 234 | if (match('position', /^at/, 0)) { 235 | var positioning = matchPositioning(); 236 | 237 | if (!positioning) { 238 | error('Missing positioning value'); 239 | } 240 | 241 | return positioning; 242 | } 243 | } 244 | 245 | function matchPositioning() { 246 | var location = matchCoordinates(); 247 | 248 | if (location.x || location.y) { 249 | return { 250 | type: 'position', 251 | value: location 252 | }; 253 | } 254 | } 255 | 256 | function matchCoordinates() { 257 | return { 258 | x: matchDistance(), 259 | y: matchDistance() 260 | }; 261 | } 262 | 263 | function matchListing(matcher) { 264 | var captures = matcher(), 265 | result = []; 266 | 267 | if (captures) { 268 | result.push(captures); 269 | while (scan(tokens.comma)) { 270 | captures = matcher(); 271 | if (captures) { 272 | result.push(captures); 273 | } else { 274 | error('One extra comma'); 275 | } 276 | } 277 | } 278 | 279 | return result; 280 | } 281 | 282 | function matchColorStop() { 283 | var color = matchColor(); 284 | 285 | if (!color) { 286 | error('Expected color definition'); 287 | } 288 | 289 | color.length = matchDistance(); 290 | return color; 291 | } 292 | 293 | function matchColor() { 294 | return matchHexColor() || 295 | matchHSLAColor() || 296 | matchHSLColor() || 297 | matchRGBAColor() || 298 | matchRGBColor() || 299 | matchVarColor() || 300 | matchLiteralColor(); 301 | } 302 | 303 | function matchLiteralColor() { 304 | return match('literal', tokens.literalColor, 0); 305 | } 306 | 307 | function matchHexColor() { 308 | return match('hex', tokens.hexColor, 1); 309 | } 310 | 311 | function matchRGBColor() { 312 | return matchCall(tokens.rgbColor, function() { 313 | return { 314 | type: 'rgb', 315 | value: matchListing(matchNumber) 316 | }; 317 | }); 318 | } 319 | 320 | function matchRGBAColor() { 321 | return matchCall(tokens.rgbaColor, function() { 322 | return { 323 | type: 'rgba', 324 | value: matchListing(matchNumber) 325 | }; 326 | }); 327 | } 328 | 329 | function matchVarColor() { 330 | return matchCall(tokens.varColor, function () { 331 | return { 332 | type: 'var', 333 | value: matchVariableName() 334 | }; 335 | }); 336 | } 337 | 338 | function matchHSLColor() { 339 | return matchCall(tokens.hslColor, function() { 340 | // Check for percentage before trying to parse the hue 341 | var lookahead = scan(tokens.percentageValue); 342 | if (lookahead) { 343 | error('HSL hue value must be a number in degrees (0-360) or normalized (-360 to 360), not a percentage'); 344 | } 345 | 346 | var hue = matchNumber(); 347 | scan(tokens.comma); 348 | var captures = scan(tokens.percentageValue); 349 | var sat = captures ? captures[1] : null; 350 | scan(tokens.comma); 351 | captures = scan(tokens.percentageValue); 352 | var light = captures ? captures[1] : null; 353 | if (!sat || !light) { 354 | error('Expected percentage value for saturation and lightness in HSL'); 355 | } 356 | return { 357 | type: 'hsl', 358 | value: [hue, sat, light] 359 | }; 360 | }); 361 | } 362 | 363 | function matchHSLAColor() { 364 | return matchCall(tokens.hslaColor, function() { 365 | var hue = matchNumber(); 366 | scan(tokens.comma); 367 | var captures = scan(tokens.percentageValue); 368 | var sat = captures ? captures[1] : null; 369 | scan(tokens.comma); 370 | captures = scan(tokens.percentageValue); 371 | var light = captures ? captures[1] : null; 372 | scan(tokens.comma); 373 | var alpha = matchNumber(); 374 | if (!sat || !light) { 375 | error('Expected percentage value for saturation and lightness in HSLA'); 376 | } 377 | return { 378 | type: 'hsla', 379 | value: [hue, sat, light, alpha] 380 | }; 381 | }); 382 | } 383 | 384 | function matchPercentage() { 385 | var captures = scan(tokens.percentageValue); 386 | return captures ? captures[1] : null; 387 | } 388 | 389 | function matchVariableName() { 390 | return scan(tokens.variableName)[1]; 391 | } 392 | 393 | function matchNumber() { 394 | return scan(tokens.number)[1]; 395 | } 396 | 397 | function matchDistance() { 398 | return match('%', tokens.percentageValue, 1) || 399 | matchPositionKeyword() || 400 | matchCalc() || 401 | matchLength(); 402 | } 403 | 404 | function matchPositionKeyword() { 405 | return match('position-keyword', tokens.positionKeywords, 1); 406 | } 407 | 408 | function matchCalc() { 409 | return matchCall(tokens.calcValue, function() { 410 | var openParenCount = 1; // Start with the opening parenthesis from calc( 411 | var i = 0; 412 | 413 | // Parse through the content looking for balanced parentheses 414 | while (openParenCount > 0 && i < input.length) { 415 | var char = input.charAt(i); 416 | if (char === '(') { 417 | openParenCount++; 418 | } else if (char === ')') { 419 | openParenCount--; 420 | } 421 | i++; 422 | } 423 | 424 | // If we exited because we ran out of input but still have open parentheses, error 425 | if (openParenCount > 0) { 426 | error('Missing closing parenthesis in calc() expression'); 427 | } 428 | 429 | // Get the content inside the calc() without the last closing paren 430 | var calcContent = input.substring(0, i - 1); 431 | 432 | // Consume the calc expression content 433 | consume(i - 1); // -1 because we don't want to consume the closing parenthesis 434 | 435 | return { 436 | type: 'calc', 437 | value: calcContent 438 | }; 439 | }); 440 | } 441 | 442 | function matchLength() { 443 | return match('px', tokens.pixelValue, 1) || 444 | match('em', tokens.emValue, 1); 445 | } 446 | 447 | function match(type, pattern, captureIndex) { 448 | var captures = scan(pattern); 449 | if (captures) { 450 | return { 451 | type: type, 452 | value: captures[captureIndex] 453 | }; 454 | } 455 | } 456 | 457 | function scan(regexp) { 458 | var captures, 459 | blankCaptures; 460 | 461 | blankCaptures = /^[\n\r\t\s]+/.exec(input); 462 | if (blankCaptures) { 463 | consume(blankCaptures[0].length); 464 | } 465 | 466 | captures = regexp.exec(input); 467 | if (captures) { 468 | consume(captures[0].length); 469 | } 470 | 471 | return captures; 472 | } 473 | 474 | function consume(size) { 475 | input = input.substr(size); 476 | } 477 | 478 | return function(code) { 479 | input = code.toString().trim(); 480 | // Remove trailing semicolon if present 481 | if (input.endsWith(';')) { 482 | input = input.slice(0, -1); 483 | } 484 | return getAST(); 485 | }; 486 | })(); 487 | 488 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 489 | // Use of this source code is governed by a BSD-style license that can be 490 | // found in the LICENSE file. 491 | 492 | var GradientParser = (GradientParser || {}); 493 | 494 | GradientParser.stringify = (function() { 495 | 496 | var visitor = { 497 | 498 | 'visit_linear-gradient': function(node) { 499 | return visitor.visit_gradient(node); 500 | }, 501 | 502 | 'visit_repeating-linear-gradient': function(node) { 503 | return visitor.visit_gradient(node); 504 | }, 505 | 506 | 'visit_radial-gradient': function(node) { 507 | return visitor.visit_gradient(node); 508 | }, 509 | 510 | 'visit_repeating-radial-gradient': function(node) { 511 | return visitor.visit_gradient(node); 512 | }, 513 | 514 | 'visit_gradient': function(node) { 515 | var orientation = visitor.visit(node.orientation); 516 | if (orientation) { 517 | orientation += ', '; 518 | } 519 | 520 | return node.type + '(' + orientation + visitor.visit(node.colorStops) + ')'; 521 | }, 522 | 523 | 'visit_shape': function(node) { 524 | var result = node.value, 525 | at = visitor.visit(node.at), 526 | style = visitor.visit(node.style); 527 | 528 | if (style) { 529 | result += ' ' + style; 530 | } 531 | 532 | if (at) { 533 | result += ' at ' + at; 534 | } 535 | 536 | return result; 537 | }, 538 | 539 | 'visit_default-radial': function(node) { 540 | var result = '', 541 | at = visitor.visit(node.at); 542 | 543 | if (at) { 544 | result += at; 545 | } 546 | return result; 547 | }, 548 | 549 | 'visit_extent-keyword': function(node) { 550 | var result = node.value, 551 | at = visitor.visit(node.at); 552 | 553 | if (at) { 554 | result += ' at ' + at; 555 | } 556 | 557 | return result; 558 | }, 559 | 560 | 'visit_position-keyword': function(node) { 561 | return node.value; 562 | }, 563 | 564 | 'visit_position': function(node) { 565 | return visitor.visit(node.value.x) + ' ' + visitor.visit(node.value.y); 566 | }, 567 | 568 | 'visit_%': function(node) { 569 | return node.value + '%'; 570 | }, 571 | 572 | 'visit_em': function(node) { 573 | return node.value + 'em'; 574 | }, 575 | 576 | 'visit_px': function(node) { 577 | return node.value + 'px'; 578 | }, 579 | 580 | 'visit_calc': function(node) { 581 | return 'calc(' + node.value + ')'; 582 | }, 583 | 584 | 'visit_literal': function(node) { 585 | return visitor.visit_color(node.value, node); 586 | }, 587 | 588 | 'visit_hex': function(node) { 589 | return visitor.visit_color('#' + node.value, node); 590 | }, 591 | 592 | 'visit_rgb': function(node) { 593 | return visitor.visit_color('rgb(' + node.value.join(', ') + ')', node); 594 | }, 595 | 596 | 'visit_rgba': function(node) { 597 | return visitor.visit_color('rgba(' + node.value.join(', ') + ')', node); 598 | }, 599 | 600 | 'visit_hsl': function(node) { 601 | return visitor.visit_color('hsl(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%)', node); 602 | }, 603 | 604 | 'visit_hsla': function(node) { 605 | return visitor.visit_color('hsla(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%, ' + node.value[3] + ')', node); 606 | }, 607 | 608 | 'visit_var': function(node) { 609 | return visitor.visit_color('var(' + node.value + ')', node); 610 | }, 611 | 612 | 'visit_color': function(resultColor, node) { 613 | var result = resultColor, 614 | length = visitor.visit(node.length); 615 | 616 | if (length) { 617 | result += ' ' + length; 618 | } 619 | return result; 620 | }, 621 | 622 | 'visit_angular': function(node) { 623 | return node.value + 'deg'; 624 | }, 625 | 626 | 'visit_directional': function(node) { 627 | return 'to ' + node.value; 628 | }, 629 | 630 | 'visit_array': function(elements) { 631 | var result = '', 632 | size = elements.length; 633 | 634 | elements.forEach(function(element, i) { 635 | result += visitor.visit(element); 636 | if (i < size - 1) { 637 | result += ', '; 638 | } 639 | }); 640 | 641 | return result; 642 | }, 643 | 644 | 'visit_object': function(obj) { 645 | if (obj.width && obj.height) { 646 | return visitor.visit(obj.width) + ' ' + visitor.visit(obj.height); 647 | } 648 | return ''; 649 | }, 650 | 651 | 'visit': function(element) { 652 | if (!element) { 653 | return ''; 654 | } 655 | var result = ''; 656 | 657 | if (element instanceof Array) { 658 | return visitor.visit_array(element); 659 | } else if (typeof element === 'object' && !element.type) { 660 | return visitor.visit_object(element); 661 | } else if (element.type) { 662 | var nodeVisitor = visitor['visit_' + element.type]; 663 | if (nodeVisitor) { 664 | return nodeVisitor(element); 665 | } else { 666 | throw Error('Missing visitor visit_' + element.type); 667 | } 668 | } else { 669 | throw Error('Invalid node.'); 670 | } 671 | } 672 | 673 | }; 674 | 675 | return function(root) { 676 | return visitor.visit(root); 677 | }; 678 | })(); 679 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports.parse = GradientParser.parse; 2 | exports.stringify = GradientParser.stringify; 3 | -------------------------------------------------------------------------------- /lib/parser.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | var GradientParser = (GradientParser || {}); 6 | 7 | GradientParser.parse = (function() { 8 | 9 | var tokens = { 10 | linearGradient: /^(\-(webkit|o|ms|moz)\-)?(linear\-gradient)/i, 11 | repeatingLinearGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-linear\-gradient)/i, 12 | radialGradient: /^(\-(webkit|o|ms|moz)\-)?(radial\-gradient)/i, 13 | repeatingRadialGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-radial\-gradient)/i, 14 | sideOrCorner: /^to (left (top|bottom)|right (top|bottom)|top (left|right)|bottom (left|right)|left|right|top|bottom)/i, 15 | extentKeywords: /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/, 16 | positionKeywords: /^(left|center|right|top|bottom)/i, 17 | pixelValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))px/, 18 | percentageValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))\%/, 19 | emValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))em/, 20 | angleValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))deg/, 21 | radianValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))rad/, 22 | startCall: /^\(/, 23 | endCall: /^\)/, 24 | comma: /^,/, 25 | hexColor: /^\#([0-9a-fA-F]+)/, 26 | literalColor: /^([a-zA-Z]+)/, 27 | rgbColor: /^rgb/i, 28 | rgbaColor: /^rgba/i, 29 | varColor: /^var/i, 30 | calcValue: /^calc/i, 31 | variableName: /^(--[a-zA-Z0-9-,\s\#]+)/, 32 | number: /^(([0-9]*\.[0-9]+)|([0-9]+\.?))/, 33 | hslColor: /^hsl/i, 34 | hslaColor: /^hsla/i, 35 | }; 36 | 37 | var input = ''; 38 | 39 | function error(msg) { 40 | var err = new Error(input + ': ' + msg); 41 | err.source = input; 42 | throw err; 43 | } 44 | 45 | function getAST() { 46 | var ast = matchListDefinitions(); 47 | 48 | if (input.length > 0) { 49 | error('Invalid input not EOF'); 50 | } 51 | 52 | return ast; 53 | } 54 | 55 | function matchListDefinitions() { 56 | return matchListing(matchDefinition); 57 | } 58 | 59 | function matchDefinition() { 60 | return matchGradient( 61 | 'linear-gradient', 62 | tokens.linearGradient, 63 | matchLinearOrientation) || 64 | 65 | matchGradient( 66 | 'repeating-linear-gradient', 67 | tokens.repeatingLinearGradient, 68 | matchLinearOrientation) || 69 | 70 | matchGradient( 71 | 'radial-gradient', 72 | tokens.radialGradient, 73 | matchListRadialOrientations) || 74 | 75 | matchGradient( 76 | 'repeating-radial-gradient', 77 | tokens.repeatingRadialGradient, 78 | matchListRadialOrientations); 79 | } 80 | 81 | function matchGradient(gradientType, pattern, orientationMatcher) { 82 | return matchCall(pattern, function(captures) { 83 | 84 | var orientation = orientationMatcher(); 85 | if (orientation) { 86 | if (!scan(tokens.comma)) { 87 | error('Missing comma before color stops'); 88 | } 89 | } 90 | 91 | return { 92 | type: gradientType, 93 | orientation: orientation, 94 | colorStops: matchListing(matchColorStop) 95 | }; 96 | }); 97 | } 98 | 99 | function matchCall(pattern, callback) { 100 | var captures = scan(pattern); 101 | 102 | if (captures) { 103 | if (!scan(tokens.startCall)) { 104 | error('Missing ('); 105 | } 106 | 107 | var result = callback(captures); 108 | 109 | if (!scan(tokens.endCall)) { 110 | error('Missing )'); 111 | } 112 | 113 | return result; 114 | } 115 | } 116 | 117 | function matchLinearOrientation() { 118 | // Check for standard CSS3 "to" direction 119 | var sideOrCorner = matchSideOrCorner(); 120 | if (sideOrCorner) { 121 | return sideOrCorner; 122 | } 123 | 124 | // Check for legacy single keyword direction (e.g., "right", "top") 125 | var legacyDirection = match('position-keyword', tokens.positionKeywords, 1); 126 | if (legacyDirection) { 127 | // For legacy syntax, we convert to the directional type 128 | return { 129 | type: 'directional', 130 | value: legacyDirection.value 131 | }; 132 | } 133 | 134 | // If neither, check for angle 135 | return matchAngle(); 136 | } 137 | 138 | function matchSideOrCorner() { 139 | return match('directional', tokens.sideOrCorner, 1); 140 | } 141 | 142 | function matchAngle() { 143 | return match('angular', tokens.angleValue, 1) || 144 | match('angular', tokens.radianValue, 1); 145 | } 146 | 147 | function matchListRadialOrientations() { 148 | var radialOrientations, 149 | radialOrientation = matchRadialOrientation(), 150 | lookaheadCache; 151 | 152 | if (radialOrientation) { 153 | radialOrientations = []; 154 | radialOrientations.push(radialOrientation); 155 | 156 | lookaheadCache = input; 157 | if (scan(tokens.comma)) { 158 | radialOrientation = matchRadialOrientation(); 159 | if (radialOrientation) { 160 | radialOrientations.push(radialOrientation); 161 | } else { 162 | input = lookaheadCache; 163 | } 164 | } 165 | } 166 | 167 | return radialOrientations; 168 | } 169 | 170 | function matchRadialOrientation() { 171 | var radialType = matchCircle() || 172 | matchEllipse(); 173 | 174 | if (radialType) { 175 | radialType.at = matchAtPosition(); 176 | } else { 177 | var extent = matchExtentKeyword(); 178 | if (extent) { 179 | radialType = extent; 180 | var positionAt = matchAtPosition(); 181 | if (positionAt) { 182 | radialType.at = positionAt; 183 | } 184 | } else { 185 | // Check for "at" position first, which is a common browser output format 186 | var atPosition = matchAtPosition(); 187 | if (atPosition) { 188 | radialType = { 189 | type: 'default-radial', 190 | at: atPosition 191 | }; 192 | } else { 193 | var defaultPosition = matchPositioning(); 194 | if (defaultPosition) { 195 | radialType = { 196 | type: 'default-radial', 197 | at: defaultPosition 198 | }; 199 | } 200 | } 201 | } 202 | } 203 | 204 | return radialType; 205 | } 206 | 207 | function matchCircle() { 208 | var circle = match('shape', /^(circle)/i, 0); 209 | 210 | if (circle) { 211 | circle.style = matchLength() || matchExtentKeyword(); 212 | } 213 | 214 | return circle; 215 | } 216 | 217 | function matchEllipse() { 218 | var ellipse = match('shape', /^(ellipse)/i, 0); 219 | 220 | if (ellipse) { 221 | ellipse.style = matchPositioning() || matchDistance() || matchExtentKeyword(); 222 | } 223 | 224 | return ellipse; 225 | } 226 | 227 | function matchExtentKeyword() { 228 | return match('extent-keyword', tokens.extentKeywords, 1); 229 | } 230 | 231 | function matchAtPosition() { 232 | if (match('position', /^at/, 0)) { 233 | var positioning = matchPositioning(); 234 | 235 | if (!positioning) { 236 | error('Missing positioning value'); 237 | } 238 | 239 | return positioning; 240 | } 241 | } 242 | 243 | function matchPositioning() { 244 | var location = matchCoordinates(); 245 | 246 | if (location.x || location.y) { 247 | return { 248 | type: 'position', 249 | value: location 250 | }; 251 | } 252 | } 253 | 254 | function matchCoordinates() { 255 | return { 256 | x: matchDistance(), 257 | y: matchDistance() 258 | }; 259 | } 260 | 261 | function matchListing(matcher) { 262 | var captures = matcher(), 263 | result = []; 264 | 265 | if (captures) { 266 | result.push(captures); 267 | while (scan(tokens.comma)) { 268 | captures = matcher(); 269 | if (captures) { 270 | result.push(captures); 271 | } else { 272 | error('One extra comma'); 273 | } 274 | } 275 | } 276 | 277 | return result; 278 | } 279 | 280 | function matchColorStop() { 281 | var color = matchColor(); 282 | 283 | if (!color) { 284 | error('Expected color definition'); 285 | } 286 | 287 | color.length = matchDistance(); 288 | return color; 289 | } 290 | 291 | function matchColor() { 292 | return matchHexColor() || 293 | matchHSLAColor() || 294 | matchHSLColor() || 295 | matchRGBAColor() || 296 | matchRGBColor() || 297 | matchVarColor() || 298 | matchLiteralColor(); 299 | } 300 | 301 | function matchLiteralColor() { 302 | return match('literal', tokens.literalColor, 0); 303 | } 304 | 305 | function matchHexColor() { 306 | return match('hex', tokens.hexColor, 1); 307 | } 308 | 309 | function matchRGBColor() { 310 | return matchCall(tokens.rgbColor, function() { 311 | return { 312 | type: 'rgb', 313 | value: matchListing(matchNumber) 314 | }; 315 | }); 316 | } 317 | 318 | function matchRGBAColor() { 319 | return matchCall(tokens.rgbaColor, function() { 320 | return { 321 | type: 'rgba', 322 | value: matchListing(matchNumber) 323 | }; 324 | }); 325 | } 326 | 327 | function matchVarColor() { 328 | return matchCall(tokens.varColor, function () { 329 | return { 330 | type: 'var', 331 | value: matchVariableName() 332 | }; 333 | }); 334 | } 335 | 336 | function matchHSLColor() { 337 | return matchCall(tokens.hslColor, function() { 338 | // Check for percentage before trying to parse the hue 339 | var lookahead = scan(tokens.percentageValue); 340 | if (lookahead) { 341 | error('HSL hue value must be a number in degrees (0-360) or normalized (-360 to 360), not a percentage'); 342 | } 343 | 344 | var hue = matchNumber(); 345 | scan(tokens.comma); 346 | var captures = scan(tokens.percentageValue); 347 | var sat = captures ? captures[1] : null; 348 | scan(tokens.comma); 349 | captures = scan(tokens.percentageValue); 350 | var light = captures ? captures[1] : null; 351 | if (!sat || !light) { 352 | error('Expected percentage value for saturation and lightness in HSL'); 353 | } 354 | return { 355 | type: 'hsl', 356 | value: [hue, sat, light] 357 | }; 358 | }); 359 | } 360 | 361 | function matchHSLAColor() { 362 | return matchCall(tokens.hslaColor, function() { 363 | var hue = matchNumber(); 364 | scan(tokens.comma); 365 | var captures = scan(tokens.percentageValue); 366 | var sat = captures ? captures[1] : null; 367 | scan(tokens.comma); 368 | captures = scan(tokens.percentageValue); 369 | var light = captures ? captures[1] : null; 370 | scan(tokens.comma); 371 | var alpha = matchNumber(); 372 | if (!sat || !light) { 373 | error('Expected percentage value for saturation and lightness in HSLA'); 374 | } 375 | return { 376 | type: 'hsla', 377 | value: [hue, sat, light, alpha] 378 | }; 379 | }); 380 | } 381 | 382 | function matchPercentage() { 383 | var captures = scan(tokens.percentageValue); 384 | return captures ? captures[1] : null; 385 | } 386 | 387 | function matchVariableName() { 388 | return scan(tokens.variableName)[1]; 389 | } 390 | 391 | function matchNumber() { 392 | return scan(tokens.number)[1]; 393 | } 394 | 395 | function matchDistance() { 396 | return match('%', tokens.percentageValue, 1) || 397 | matchPositionKeyword() || 398 | matchCalc() || 399 | matchLength(); 400 | } 401 | 402 | function matchPositionKeyword() { 403 | return match('position-keyword', tokens.positionKeywords, 1); 404 | } 405 | 406 | function matchCalc() { 407 | return matchCall(tokens.calcValue, function() { 408 | var openParenCount = 1; // Start with the opening parenthesis from calc( 409 | var i = 0; 410 | 411 | // Parse through the content looking for balanced parentheses 412 | while (openParenCount > 0 && i < input.length) { 413 | var char = input.charAt(i); 414 | if (char === '(') { 415 | openParenCount++; 416 | } else if (char === ')') { 417 | openParenCount--; 418 | } 419 | i++; 420 | } 421 | 422 | // If we exited because we ran out of input but still have open parentheses, error 423 | if (openParenCount > 0) { 424 | error('Missing closing parenthesis in calc() expression'); 425 | } 426 | 427 | // Get the content inside the calc() without the last closing paren 428 | var calcContent = input.substring(0, i - 1); 429 | 430 | // Consume the calc expression content 431 | consume(i - 1); // -1 because we don't want to consume the closing parenthesis 432 | 433 | return { 434 | type: 'calc', 435 | value: calcContent 436 | }; 437 | }); 438 | } 439 | 440 | function matchLength() { 441 | return match('px', tokens.pixelValue, 1) || 442 | match('em', tokens.emValue, 1); 443 | } 444 | 445 | function match(type, pattern, captureIndex) { 446 | var captures = scan(pattern); 447 | if (captures) { 448 | return { 449 | type: type, 450 | value: captures[captureIndex] 451 | }; 452 | } 453 | } 454 | 455 | function scan(regexp) { 456 | var captures, 457 | blankCaptures; 458 | 459 | blankCaptures = /^[\n\r\t\s]+/.exec(input); 460 | if (blankCaptures) { 461 | consume(blankCaptures[0].length); 462 | } 463 | 464 | captures = regexp.exec(input); 465 | if (captures) { 466 | consume(captures[0].length); 467 | } 468 | 469 | return captures; 470 | } 471 | 472 | function consume(size) { 473 | input = input.substr(size); 474 | } 475 | 476 | return function(code) { 477 | input = code.toString().trim(); 478 | // Remove trailing semicolon if present 479 | if (input.endsWith(';')) { 480 | input = input.slice(0, -1); 481 | } 482 | return getAST(); 483 | }; 484 | })(); 485 | -------------------------------------------------------------------------------- /lib/stringify.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Rafael Caricio. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | var GradientParser = (GradientParser || {}); 6 | 7 | GradientParser.stringify = (function() { 8 | 9 | var visitor = { 10 | 11 | 'visit_linear-gradient': function(node) { 12 | return visitor.visit_gradient(node); 13 | }, 14 | 15 | 'visit_repeating-linear-gradient': function(node) { 16 | return visitor.visit_gradient(node); 17 | }, 18 | 19 | 'visit_radial-gradient': function(node) { 20 | return visitor.visit_gradient(node); 21 | }, 22 | 23 | 'visit_repeating-radial-gradient': function(node) { 24 | return visitor.visit_gradient(node); 25 | }, 26 | 27 | 'visit_gradient': function(node) { 28 | var orientation = visitor.visit(node.orientation); 29 | if (orientation) { 30 | orientation += ', '; 31 | } 32 | 33 | return node.type + '(' + orientation + visitor.visit(node.colorStops) + ')'; 34 | }, 35 | 36 | 'visit_shape': function(node) { 37 | var result = node.value, 38 | at = visitor.visit(node.at), 39 | style = visitor.visit(node.style); 40 | 41 | if (style) { 42 | result += ' ' + style; 43 | } 44 | 45 | if (at) { 46 | result += ' at ' + at; 47 | } 48 | 49 | return result; 50 | }, 51 | 52 | 'visit_default-radial': function(node) { 53 | var result = '', 54 | at = visitor.visit(node.at); 55 | 56 | if (at) { 57 | result += at; 58 | } 59 | return result; 60 | }, 61 | 62 | 'visit_extent-keyword': function(node) { 63 | var result = node.value, 64 | at = visitor.visit(node.at); 65 | 66 | if (at) { 67 | result += ' at ' + at; 68 | } 69 | 70 | return result; 71 | }, 72 | 73 | 'visit_position-keyword': function(node) { 74 | return node.value; 75 | }, 76 | 77 | 'visit_position': function(node) { 78 | return visitor.visit(node.value.x) + ' ' + visitor.visit(node.value.y); 79 | }, 80 | 81 | 'visit_%': function(node) { 82 | return node.value + '%'; 83 | }, 84 | 85 | 'visit_em': function(node) { 86 | return node.value + 'em'; 87 | }, 88 | 89 | 'visit_px': function(node) { 90 | return node.value + 'px'; 91 | }, 92 | 93 | 'visit_calc': function(node) { 94 | return 'calc(' + node.value + ')'; 95 | }, 96 | 97 | 'visit_literal': function(node) { 98 | return visitor.visit_color(node.value, node); 99 | }, 100 | 101 | 'visit_hex': function(node) { 102 | return visitor.visit_color('#' + node.value, node); 103 | }, 104 | 105 | 'visit_rgb': function(node) { 106 | return visitor.visit_color('rgb(' + node.value.join(', ') + ')', node); 107 | }, 108 | 109 | 'visit_rgba': function(node) { 110 | return visitor.visit_color('rgba(' + node.value.join(', ') + ')', node); 111 | }, 112 | 113 | 'visit_hsl': function(node) { 114 | return visitor.visit_color('hsl(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%)', node); 115 | }, 116 | 117 | 'visit_hsla': function(node) { 118 | return visitor.visit_color('hsla(' + node.value[0] + ', ' + node.value[1] + '%, ' + node.value[2] + '%, ' + node.value[3] + ')', node); 119 | }, 120 | 121 | 'visit_var': function(node) { 122 | return visitor.visit_color('var(' + node.value + ')', node); 123 | }, 124 | 125 | 'visit_color': function(resultColor, node) { 126 | var result = resultColor, 127 | length = visitor.visit(node.length); 128 | 129 | if (length) { 130 | result += ' ' + length; 131 | } 132 | return result; 133 | }, 134 | 135 | 'visit_angular': function(node) { 136 | return node.value + 'deg'; 137 | }, 138 | 139 | 'visit_directional': function(node) { 140 | return 'to ' + node.value; 141 | }, 142 | 143 | 'visit_array': function(elements) { 144 | var result = '', 145 | size = elements.length; 146 | 147 | elements.forEach(function(element, i) { 148 | result += visitor.visit(element); 149 | if (i < size - 1) { 150 | result += ', '; 151 | } 152 | }); 153 | 154 | return result; 155 | }, 156 | 157 | 'visit_object': function(obj) { 158 | if (obj.width && obj.height) { 159 | return visitor.visit(obj.width) + ' ' + visitor.visit(obj.height); 160 | } 161 | return ''; 162 | }, 163 | 164 | 'visit': function(element) { 165 | if (!element) { 166 | return ''; 167 | } 168 | var result = ''; 169 | 170 | if (element instanceof Array) { 171 | return visitor.visit_array(element); 172 | } else if (typeof element === 'object' && !element.type) { 173 | return visitor.visit_object(element); 174 | } else if (element.type) { 175 | var nodeVisitor = visitor['visit_' + element.type]; 176 | if (nodeVisitor) { 177 | return nodeVisitor(element); 178 | } else { 179 | throw Error('Missing visitor visit_' + element.type); 180 | } 181 | } else { 182 | throw Error('Invalid node.'); 183 | } 184 | } 185 | 186 | }; 187 | 188 | return function(root) { 189 | return visitor.visit(root); 190 | }; 191 | })(); 192 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gradient-parser", 3 | "version": "1.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "gradient-parser", 9 | "version": "1.1.0", 10 | "devDependencies": { 11 | "esbuild": "^0.25.0", 12 | "expect.js": "^0.3.1", 13 | "mocha": "^10.3.0" 14 | }, 15 | "engines": { 16 | "node": ">=0.10.0" 17 | } 18 | }, 19 | "node_modules/@esbuild/aix-ppc64": { 20 | "version": "0.25.0", 21 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 22 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 23 | "cpu": [ 24 | "ppc64" 25 | ], 26 | "dev": true, 27 | "license": "MIT", 28 | "optional": true, 29 | "os": [ 30 | "aix" 31 | ], 32 | "engines": { 33 | "node": ">=18" 34 | } 35 | }, 36 | "node_modules/@esbuild/android-arm": { 37 | "version": "0.25.0", 38 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 39 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 40 | "cpu": [ 41 | "arm" 42 | ], 43 | "dev": true, 44 | "license": "MIT", 45 | "optional": true, 46 | "os": [ 47 | "android" 48 | ], 49 | "engines": { 50 | "node": ">=18" 51 | } 52 | }, 53 | "node_modules/@esbuild/android-arm64": { 54 | "version": "0.25.0", 55 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 56 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 57 | "cpu": [ 58 | "arm64" 59 | ], 60 | "dev": true, 61 | "license": "MIT", 62 | "optional": true, 63 | "os": [ 64 | "android" 65 | ], 66 | "engines": { 67 | "node": ">=18" 68 | } 69 | }, 70 | "node_modules/@esbuild/android-x64": { 71 | "version": "0.25.0", 72 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 73 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "android" 82 | ], 83 | "engines": { 84 | "node": ">=18" 85 | } 86 | }, 87 | "node_modules/@esbuild/darwin-arm64": { 88 | "version": "0.25.0", 89 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 90 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 91 | "cpu": [ 92 | "arm64" 93 | ], 94 | "dev": true, 95 | "license": "MIT", 96 | "optional": true, 97 | "os": [ 98 | "darwin" 99 | ], 100 | "engines": { 101 | "node": ">=18" 102 | } 103 | }, 104 | "node_modules/@esbuild/darwin-x64": { 105 | "version": "0.25.0", 106 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 107 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 108 | "cpu": [ 109 | "x64" 110 | ], 111 | "dev": true, 112 | "license": "MIT", 113 | "optional": true, 114 | "os": [ 115 | "darwin" 116 | ], 117 | "engines": { 118 | "node": ">=18" 119 | } 120 | }, 121 | "node_modules/@esbuild/freebsd-arm64": { 122 | "version": "0.25.0", 123 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 124 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 125 | "cpu": [ 126 | "arm64" 127 | ], 128 | "dev": true, 129 | "license": "MIT", 130 | "optional": true, 131 | "os": [ 132 | "freebsd" 133 | ], 134 | "engines": { 135 | "node": ">=18" 136 | } 137 | }, 138 | "node_modules/@esbuild/freebsd-x64": { 139 | "version": "0.25.0", 140 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 141 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 142 | "cpu": [ 143 | "x64" 144 | ], 145 | "dev": true, 146 | "license": "MIT", 147 | "optional": true, 148 | "os": [ 149 | "freebsd" 150 | ], 151 | "engines": { 152 | "node": ">=18" 153 | } 154 | }, 155 | "node_modules/@esbuild/linux-arm": { 156 | "version": "0.25.0", 157 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 158 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 159 | "cpu": [ 160 | "arm" 161 | ], 162 | "dev": true, 163 | "license": "MIT", 164 | "optional": true, 165 | "os": [ 166 | "linux" 167 | ], 168 | "engines": { 169 | "node": ">=18" 170 | } 171 | }, 172 | "node_modules/@esbuild/linux-arm64": { 173 | "version": "0.25.0", 174 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 175 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 176 | "cpu": [ 177 | "arm64" 178 | ], 179 | "dev": true, 180 | "license": "MIT", 181 | "optional": true, 182 | "os": [ 183 | "linux" 184 | ], 185 | "engines": { 186 | "node": ">=18" 187 | } 188 | }, 189 | "node_modules/@esbuild/linux-ia32": { 190 | "version": "0.25.0", 191 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 192 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 193 | "cpu": [ 194 | "ia32" 195 | ], 196 | "dev": true, 197 | "license": "MIT", 198 | "optional": true, 199 | "os": [ 200 | "linux" 201 | ], 202 | "engines": { 203 | "node": ">=18" 204 | } 205 | }, 206 | "node_modules/@esbuild/linux-loong64": { 207 | "version": "0.25.0", 208 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 209 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 210 | "cpu": [ 211 | "loong64" 212 | ], 213 | "dev": true, 214 | "license": "MIT", 215 | "optional": true, 216 | "os": [ 217 | "linux" 218 | ], 219 | "engines": { 220 | "node": ">=18" 221 | } 222 | }, 223 | "node_modules/@esbuild/linux-mips64el": { 224 | "version": "0.25.0", 225 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 226 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 227 | "cpu": [ 228 | "mips64el" 229 | ], 230 | "dev": true, 231 | "license": "MIT", 232 | "optional": true, 233 | "os": [ 234 | "linux" 235 | ], 236 | "engines": { 237 | "node": ">=18" 238 | } 239 | }, 240 | "node_modules/@esbuild/linux-ppc64": { 241 | "version": "0.25.0", 242 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 243 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 244 | "cpu": [ 245 | "ppc64" 246 | ], 247 | "dev": true, 248 | "license": "MIT", 249 | "optional": true, 250 | "os": [ 251 | "linux" 252 | ], 253 | "engines": { 254 | "node": ">=18" 255 | } 256 | }, 257 | "node_modules/@esbuild/linux-riscv64": { 258 | "version": "0.25.0", 259 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 260 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 261 | "cpu": [ 262 | "riscv64" 263 | ], 264 | "dev": true, 265 | "license": "MIT", 266 | "optional": true, 267 | "os": [ 268 | "linux" 269 | ], 270 | "engines": { 271 | "node": ">=18" 272 | } 273 | }, 274 | "node_modules/@esbuild/linux-s390x": { 275 | "version": "0.25.0", 276 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 277 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 278 | "cpu": [ 279 | "s390x" 280 | ], 281 | "dev": true, 282 | "license": "MIT", 283 | "optional": true, 284 | "os": [ 285 | "linux" 286 | ], 287 | "engines": { 288 | "node": ">=18" 289 | } 290 | }, 291 | "node_modules/@esbuild/linux-x64": { 292 | "version": "0.25.0", 293 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 294 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 295 | "cpu": [ 296 | "x64" 297 | ], 298 | "dev": true, 299 | "license": "MIT", 300 | "optional": true, 301 | "os": [ 302 | "linux" 303 | ], 304 | "engines": { 305 | "node": ">=18" 306 | } 307 | }, 308 | "node_modules/@esbuild/netbsd-arm64": { 309 | "version": "0.25.0", 310 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 311 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 312 | "cpu": [ 313 | "arm64" 314 | ], 315 | "dev": true, 316 | "license": "MIT", 317 | "optional": true, 318 | "os": [ 319 | "netbsd" 320 | ], 321 | "engines": { 322 | "node": ">=18" 323 | } 324 | }, 325 | "node_modules/@esbuild/netbsd-x64": { 326 | "version": "0.25.0", 327 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 328 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 329 | "cpu": [ 330 | "x64" 331 | ], 332 | "dev": true, 333 | "license": "MIT", 334 | "optional": true, 335 | "os": [ 336 | "netbsd" 337 | ], 338 | "engines": { 339 | "node": ">=18" 340 | } 341 | }, 342 | "node_modules/@esbuild/openbsd-arm64": { 343 | "version": "0.25.0", 344 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 345 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 346 | "cpu": [ 347 | "arm64" 348 | ], 349 | "dev": true, 350 | "license": "MIT", 351 | "optional": true, 352 | "os": [ 353 | "openbsd" 354 | ], 355 | "engines": { 356 | "node": ">=18" 357 | } 358 | }, 359 | "node_modules/@esbuild/openbsd-x64": { 360 | "version": "0.25.0", 361 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 362 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 363 | "cpu": [ 364 | "x64" 365 | ], 366 | "dev": true, 367 | "license": "MIT", 368 | "optional": true, 369 | "os": [ 370 | "openbsd" 371 | ], 372 | "engines": { 373 | "node": ">=18" 374 | } 375 | }, 376 | "node_modules/@esbuild/sunos-x64": { 377 | "version": "0.25.0", 378 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 379 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 380 | "cpu": [ 381 | "x64" 382 | ], 383 | "dev": true, 384 | "license": "MIT", 385 | "optional": true, 386 | "os": [ 387 | "sunos" 388 | ], 389 | "engines": { 390 | "node": ">=18" 391 | } 392 | }, 393 | "node_modules/@esbuild/win32-arm64": { 394 | "version": "0.25.0", 395 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 396 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 397 | "cpu": [ 398 | "arm64" 399 | ], 400 | "dev": true, 401 | "license": "MIT", 402 | "optional": true, 403 | "os": [ 404 | "win32" 405 | ], 406 | "engines": { 407 | "node": ">=18" 408 | } 409 | }, 410 | "node_modules/@esbuild/win32-ia32": { 411 | "version": "0.25.0", 412 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 413 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 414 | "cpu": [ 415 | "ia32" 416 | ], 417 | "dev": true, 418 | "license": "MIT", 419 | "optional": true, 420 | "os": [ 421 | "win32" 422 | ], 423 | "engines": { 424 | "node": ">=18" 425 | } 426 | }, 427 | "node_modules/@esbuild/win32-x64": { 428 | "version": "0.25.0", 429 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 430 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 431 | "cpu": [ 432 | "x64" 433 | ], 434 | "dev": true, 435 | "license": "MIT", 436 | "optional": true, 437 | "os": [ 438 | "win32" 439 | ], 440 | "engines": { 441 | "node": ">=18" 442 | } 443 | }, 444 | "node_modules/ansi-colors": { 445 | "version": "4.1.3", 446 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", 447 | "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", 448 | "dev": true, 449 | "license": "MIT", 450 | "engines": { 451 | "node": ">=6" 452 | } 453 | }, 454 | "node_modules/ansi-styles": { 455 | "version": "4.3.0", 456 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 457 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 458 | "dev": true, 459 | "dependencies": { 460 | "color-convert": "^2.0.1" 461 | }, 462 | "engines": { 463 | "node": ">=8" 464 | } 465 | }, 466 | "node_modules/anymatch": { 467 | "version": "3.1.3", 468 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 469 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 470 | "dev": true, 471 | "license": "ISC", 472 | "dependencies": { 473 | "normalize-path": "^3.0.0", 474 | "picomatch": "^2.0.4" 475 | }, 476 | "engines": { 477 | "node": ">= 8" 478 | } 479 | }, 480 | "node_modules/balanced-match": { 481 | "version": "1.0.0", 482 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 483 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 484 | "dev": true 485 | }, 486 | "node_modules/binary-extensions": { 487 | "version": "2.3.0", 488 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 489 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 490 | "dev": true, 491 | "license": "MIT", 492 | "engines": { 493 | "node": ">=8" 494 | }, 495 | "funding": { 496 | "url": "https://github.com/sponsors/sindresorhus" 497 | } 498 | }, 499 | "node_modules/braces": { 500 | "version": "3.0.3", 501 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 502 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 503 | "dev": true, 504 | "license": "MIT", 505 | "dependencies": { 506 | "fill-range": "^7.1.1" 507 | }, 508 | "engines": { 509 | "node": ">=8" 510 | } 511 | }, 512 | "node_modules/browser-stdout": { 513 | "version": "1.3.1", 514 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 515 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 516 | "dev": true 517 | }, 518 | "node_modules/chalk": { 519 | "version": "4.1.0", 520 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 521 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 522 | "dev": true, 523 | "dependencies": { 524 | "ansi-styles": "^4.1.0", 525 | "supports-color": "^7.1.0" 526 | }, 527 | "engines": { 528 | "node": ">=10" 529 | } 530 | }, 531 | "node_modules/chokidar": { 532 | "version": "3.6.0", 533 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 534 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 535 | "dev": true, 536 | "license": "MIT", 537 | "dependencies": { 538 | "anymatch": "~3.1.2", 539 | "braces": "~3.0.2", 540 | "glob-parent": "~5.1.2", 541 | "is-binary-path": "~2.1.0", 542 | "is-glob": "~4.0.1", 543 | "normalize-path": "~3.0.0", 544 | "readdirp": "~3.6.0" 545 | }, 546 | "engines": { 547 | "node": ">= 8.10.0" 548 | }, 549 | "funding": { 550 | "url": "https://paulmillr.com/funding/" 551 | }, 552 | "optionalDependencies": { 553 | "fsevents": "~2.3.2" 554 | } 555 | }, 556 | "node_modules/color-convert": { 557 | "version": "2.0.1", 558 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 559 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 560 | "dev": true, 561 | "dependencies": { 562 | "color-name": "~1.1.4" 563 | }, 564 | "engines": { 565 | "node": ">=7.0.0" 566 | } 567 | }, 568 | "node_modules/color-name": { 569 | "version": "1.1.4", 570 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 571 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 572 | "dev": true 573 | }, 574 | "node_modules/emoji-regex": { 575 | "version": "8.0.0", 576 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 577 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 578 | "dev": true 579 | }, 580 | "node_modules/esbuild": { 581 | "version": "0.25.0", 582 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 583 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 584 | "dev": true, 585 | "hasInstallScript": true, 586 | "license": "MIT", 587 | "bin": { 588 | "esbuild": "bin/esbuild" 589 | }, 590 | "engines": { 591 | "node": ">=18" 592 | }, 593 | "optionalDependencies": { 594 | "@esbuild/aix-ppc64": "0.25.0", 595 | "@esbuild/android-arm": "0.25.0", 596 | "@esbuild/android-arm64": "0.25.0", 597 | "@esbuild/android-x64": "0.25.0", 598 | "@esbuild/darwin-arm64": "0.25.0", 599 | "@esbuild/darwin-x64": "0.25.0", 600 | "@esbuild/freebsd-arm64": "0.25.0", 601 | "@esbuild/freebsd-x64": "0.25.0", 602 | "@esbuild/linux-arm": "0.25.0", 603 | "@esbuild/linux-arm64": "0.25.0", 604 | "@esbuild/linux-ia32": "0.25.0", 605 | "@esbuild/linux-loong64": "0.25.0", 606 | "@esbuild/linux-mips64el": "0.25.0", 607 | "@esbuild/linux-ppc64": "0.25.0", 608 | "@esbuild/linux-riscv64": "0.25.0", 609 | "@esbuild/linux-s390x": "0.25.0", 610 | "@esbuild/linux-x64": "0.25.0", 611 | "@esbuild/netbsd-arm64": "0.25.0", 612 | "@esbuild/netbsd-x64": "0.25.0", 613 | "@esbuild/openbsd-arm64": "0.25.0", 614 | "@esbuild/openbsd-x64": "0.25.0", 615 | "@esbuild/sunos-x64": "0.25.0", 616 | "@esbuild/win32-arm64": "0.25.0", 617 | "@esbuild/win32-ia32": "0.25.0", 618 | "@esbuild/win32-x64": "0.25.0" 619 | } 620 | }, 621 | "node_modules/escalade": { 622 | "version": "3.2.0", 623 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 624 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 625 | "dev": true, 626 | "engines": { 627 | "node": ">=6" 628 | } 629 | }, 630 | "node_modules/expect.js": { 631 | "version": "0.3.1", 632 | "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", 633 | "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", 634 | "dev": true 635 | }, 636 | "node_modules/fill-range": { 637 | "version": "7.1.1", 638 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 639 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 640 | "dev": true, 641 | "license": "MIT", 642 | "dependencies": { 643 | "to-regex-range": "^5.0.1" 644 | }, 645 | "engines": { 646 | "node": ">=8" 647 | } 648 | }, 649 | "node_modules/find-up": { 650 | "version": "5.0.0", 651 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 652 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 653 | "dev": true, 654 | "dependencies": { 655 | "locate-path": "^6.0.0", 656 | "path-exists": "^4.0.0" 657 | }, 658 | "engines": { 659 | "node": ">=10" 660 | } 661 | }, 662 | "node_modules/flat": { 663 | "version": "5.0.2", 664 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 665 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 666 | "dev": true, 667 | "bin": { 668 | "flat": "cli.js" 669 | } 670 | }, 671 | "node_modules/fs.realpath": { 672 | "version": "1.0.0", 673 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 674 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 675 | "dev": true 676 | }, 677 | "node_modules/fsevents": { 678 | "version": "2.3.3", 679 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 680 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 681 | "dev": true, 682 | "hasInstallScript": true, 683 | "license": "MIT", 684 | "optional": true, 685 | "os": [ 686 | "darwin" 687 | ], 688 | "engines": { 689 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 690 | } 691 | }, 692 | "node_modules/get-caller-file": { 693 | "version": "2.0.5", 694 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 695 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 696 | "dev": true, 697 | "engines": { 698 | "node": "6.* || 8.* || >= 10.*" 699 | } 700 | }, 701 | "node_modules/glob-parent": { 702 | "version": "5.1.2", 703 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 704 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 705 | "dev": true, 706 | "license": "ISC", 707 | "dependencies": { 708 | "is-glob": "^4.0.1" 709 | }, 710 | "engines": { 711 | "node": ">= 6" 712 | } 713 | }, 714 | "node_modules/has-flag": { 715 | "version": "4.0.0", 716 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 717 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 718 | "dev": true, 719 | "engines": { 720 | "node": ">=8" 721 | } 722 | }, 723 | "node_modules/he": { 724 | "version": "1.2.0", 725 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 726 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 727 | "dev": true, 728 | "bin": { 729 | "he": "bin/he" 730 | } 731 | }, 732 | "node_modules/inflight": { 733 | "version": "1.0.6", 734 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 735 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 736 | "dev": true, 737 | "dependencies": { 738 | "once": "^1.3.0", 739 | "wrappy": "1" 740 | } 741 | }, 742 | "node_modules/inherits": { 743 | "version": "2.0.4", 744 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 745 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 746 | "dev": true 747 | }, 748 | "node_modules/is-binary-path": { 749 | "version": "2.1.0", 750 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 751 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 752 | "dev": true, 753 | "license": "MIT", 754 | "dependencies": { 755 | "binary-extensions": "^2.0.0" 756 | }, 757 | "engines": { 758 | "node": ">=8" 759 | } 760 | }, 761 | "node_modules/is-extglob": { 762 | "version": "2.1.1", 763 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 764 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 765 | "dev": true, 766 | "engines": { 767 | "node": ">=0.10.0" 768 | } 769 | }, 770 | "node_modules/is-fullwidth-code-point": { 771 | "version": "3.0.0", 772 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 773 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 774 | "dev": true, 775 | "engines": { 776 | "node": ">=8" 777 | } 778 | }, 779 | "node_modules/is-glob": { 780 | "version": "4.0.3", 781 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 782 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 783 | "dev": true, 784 | "license": "MIT", 785 | "dependencies": { 786 | "is-extglob": "^2.1.1" 787 | }, 788 | "engines": { 789 | "node": ">=0.10.0" 790 | } 791 | }, 792 | "node_modules/is-number": { 793 | "version": "7.0.0", 794 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 795 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 796 | "dev": true, 797 | "license": "MIT", 798 | "engines": { 799 | "node": ">=0.12.0" 800 | } 801 | }, 802 | "node_modules/is-plain-obj": { 803 | "version": "2.1.0", 804 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 805 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 806 | "dev": true, 807 | "engines": { 808 | "node": ">=8" 809 | } 810 | }, 811 | "node_modules/is-unicode-supported": { 812 | "version": "0.1.0", 813 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 814 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 815 | "dev": true, 816 | "license": "MIT", 817 | "engines": { 818 | "node": ">=10" 819 | }, 820 | "funding": { 821 | "url": "https://github.com/sponsors/sindresorhus" 822 | } 823 | }, 824 | "node_modules/locate-path": { 825 | "version": "6.0.0", 826 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 827 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 828 | "dev": true, 829 | "dependencies": { 830 | "p-locate": "^5.0.0" 831 | }, 832 | "engines": { 833 | "node": ">=10" 834 | } 835 | }, 836 | "node_modules/log-symbols": { 837 | "version": "4.1.0", 838 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 839 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 840 | "dev": true, 841 | "license": "MIT", 842 | "dependencies": { 843 | "chalk": "^4.1.0", 844 | "is-unicode-supported": "^0.1.0" 845 | }, 846 | "engines": { 847 | "node": ">=10" 848 | }, 849 | "funding": { 850 | "url": "https://github.com/sponsors/sindresorhus" 851 | } 852 | }, 853 | "node_modules/mocha": { 854 | "version": "10.8.2", 855 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", 856 | "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", 857 | "dev": true, 858 | "dependencies": { 859 | "ansi-colors": "^4.1.3", 860 | "browser-stdout": "^1.3.1", 861 | "chokidar": "^3.5.3", 862 | "debug": "^4.3.5", 863 | "diff": "^5.2.0", 864 | "escape-string-regexp": "^4.0.0", 865 | "find-up": "^5.0.0", 866 | "glob": "^8.1.0", 867 | "he": "^1.2.0", 868 | "js-yaml": "^4.1.0", 869 | "log-symbols": "^4.1.0", 870 | "minimatch": "^5.1.6", 871 | "ms": "^2.1.3", 872 | "serialize-javascript": "^6.0.2", 873 | "strip-json-comments": "^3.1.1", 874 | "supports-color": "^8.1.1", 875 | "workerpool": "^6.5.1", 876 | "yargs": "^16.2.0", 877 | "yargs-parser": "^20.2.9", 878 | "yargs-unparser": "^2.0.0" 879 | }, 880 | "bin": { 881 | "_mocha": "bin/_mocha", 882 | "mocha": "bin/mocha.js" 883 | }, 884 | "engines": { 885 | "node": ">= 14.0.0" 886 | } 887 | }, 888 | "node_modules/mocha/node_modules/ansi-regex": { 889 | "version": "5.0.1", 890 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 891 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 892 | "dev": true, 893 | "engines": { 894 | "node": ">=8" 895 | } 896 | }, 897 | "node_modules/mocha/node_modules/argparse": { 898 | "version": "2.0.1", 899 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 900 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 901 | "dev": true, 902 | "license": "Python-2.0" 903 | }, 904 | "node_modules/mocha/node_modules/brace-expansion": { 905 | "version": "2.0.1", 906 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 907 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 908 | "dev": true, 909 | "license": "MIT", 910 | "dependencies": { 911 | "balanced-match": "^1.0.0" 912 | } 913 | }, 914 | "node_modules/mocha/node_modules/cliui": { 915 | "version": "7.0.4", 916 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 917 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 918 | "dev": true, 919 | "dependencies": { 920 | "string-width": "^4.2.0", 921 | "strip-ansi": "^6.0.0", 922 | "wrap-ansi": "^7.0.0" 923 | } 924 | }, 925 | "node_modules/mocha/node_modules/debug": { 926 | "version": "4.4.0", 927 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 928 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 929 | "dev": true, 930 | "license": "MIT", 931 | "dependencies": { 932 | "ms": "^2.1.3" 933 | }, 934 | "engines": { 935 | "node": ">=6.0" 936 | }, 937 | "peerDependenciesMeta": { 938 | "supports-color": { 939 | "optional": true 940 | } 941 | } 942 | }, 943 | "node_modules/mocha/node_modules/diff": { 944 | "version": "5.2.0", 945 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", 946 | "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", 947 | "dev": true, 948 | "license": "BSD-3-Clause", 949 | "engines": { 950 | "node": ">=0.3.1" 951 | } 952 | }, 953 | "node_modules/mocha/node_modules/escape-string-regexp": { 954 | "version": "4.0.0", 955 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 956 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 957 | "dev": true, 958 | "engines": { 959 | "node": ">=10" 960 | } 961 | }, 962 | "node_modules/mocha/node_modules/glob": { 963 | "version": "8.1.0", 964 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 965 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 966 | "deprecated": "Glob versions prior to v9 are no longer supported", 967 | "dev": true, 968 | "dependencies": { 969 | "fs.realpath": "^1.0.0", 970 | "inflight": "^1.0.4", 971 | "inherits": "2", 972 | "minimatch": "^5.0.1", 973 | "once": "^1.3.0" 974 | }, 975 | "engines": { 976 | "node": ">=12" 977 | }, 978 | "funding": { 979 | "url": "https://github.com/sponsors/isaacs" 980 | } 981 | }, 982 | "node_modules/mocha/node_modules/js-yaml": { 983 | "version": "4.1.0", 984 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 985 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 986 | "dev": true, 987 | "license": "MIT", 988 | "dependencies": { 989 | "argparse": "^2.0.1" 990 | }, 991 | "bin": { 992 | "js-yaml": "bin/js-yaml.js" 993 | } 994 | }, 995 | "node_modules/mocha/node_modules/minimatch": { 996 | "version": "5.1.6", 997 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 998 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 999 | "dev": true, 1000 | "license": "ISC", 1001 | "dependencies": { 1002 | "brace-expansion": "^2.0.1" 1003 | }, 1004 | "engines": { 1005 | "node": ">=10" 1006 | } 1007 | }, 1008 | "node_modules/mocha/node_modules/ms": { 1009 | "version": "2.1.3", 1010 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1011 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1012 | "dev": true 1013 | }, 1014 | "node_modules/mocha/node_modules/strip-ansi": { 1015 | "version": "6.0.1", 1016 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1017 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1018 | "dev": true, 1019 | "dependencies": { 1020 | "ansi-regex": "^5.0.1" 1021 | }, 1022 | "engines": { 1023 | "node": ">=8" 1024 | } 1025 | }, 1026 | "node_modules/mocha/node_modules/supports-color": { 1027 | "version": "8.1.1", 1028 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1029 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1030 | "dev": true, 1031 | "dependencies": { 1032 | "has-flag": "^4.0.0" 1033 | }, 1034 | "engines": { 1035 | "node": ">=10" 1036 | } 1037 | }, 1038 | "node_modules/mocha/node_modules/yargs": { 1039 | "version": "16.2.0", 1040 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1041 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1042 | "dev": true, 1043 | "dependencies": { 1044 | "cliui": "^7.0.2", 1045 | "escalade": "^3.1.1", 1046 | "get-caller-file": "^2.0.5", 1047 | "require-directory": "^2.1.1", 1048 | "string-width": "^4.2.0", 1049 | "y18n": "^5.0.5", 1050 | "yargs-parser": "^20.2.2" 1051 | }, 1052 | "engines": { 1053 | "node": ">=10" 1054 | } 1055 | }, 1056 | "node_modules/normalize-path": { 1057 | "version": "3.0.0", 1058 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1059 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1060 | "dev": true, 1061 | "license": "MIT", 1062 | "engines": { 1063 | "node": ">=0.10.0" 1064 | } 1065 | }, 1066 | "node_modules/once": { 1067 | "version": "1.4.0", 1068 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1069 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1070 | "dev": true, 1071 | "dependencies": { 1072 | "wrappy": "1" 1073 | } 1074 | }, 1075 | "node_modules/p-limit": { 1076 | "version": "3.1.0", 1077 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1078 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1079 | "dev": true, 1080 | "dependencies": { 1081 | "yocto-queue": "^0.1.0" 1082 | }, 1083 | "engines": { 1084 | "node": ">=10" 1085 | } 1086 | }, 1087 | "node_modules/p-locate": { 1088 | "version": "5.0.0", 1089 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1090 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1091 | "dev": true, 1092 | "dependencies": { 1093 | "p-limit": "^3.0.2" 1094 | }, 1095 | "engines": { 1096 | "node": ">=10" 1097 | } 1098 | }, 1099 | "node_modules/path-exists": { 1100 | "version": "4.0.0", 1101 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1102 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1103 | "dev": true, 1104 | "engines": { 1105 | "node": ">=8" 1106 | } 1107 | }, 1108 | "node_modules/picomatch": { 1109 | "version": "2.3.1", 1110 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1111 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1112 | "dev": true, 1113 | "engines": { 1114 | "node": ">=8.6" 1115 | }, 1116 | "funding": { 1117 | "url": "https://github.com/sponsors/jonschlinkert" 1118 | } 1119 | }, 1120 | "node_modules/randombytes": { 1121 | "version": "2.1.0", 1122 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1123 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1124 | "dev": true, 1125 | "dependencies": { 1126 | "safe-buffer": "^5.1.0" 1127 | } 1128 | }, 1129 | "node_modules/readdirp": { 1130 | "version": "3.6.0", 1131 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1132 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1133 | "dev": true, 1134 | "license": "MIT", 1135 | "dependencies": { 1136 | "picomatch": "^2.2.1" 1137 | }, 1138 | "engines": { 1139 | "node": ">=8.10.0" 1140 | } 1141 | }, 1142 | "node_modules/require-directory": { 1143 | "version": "2.1.1", 1144 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1145 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1146 | "dev": true, 1147 | "engines": { 1148 | "node": ">=0.10.0" 1149 | } 1150 | }, 1151 | "node_modules/safe-buffer": { 1152 | "version": "5.2.1", 1153 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1154 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1155 | "dev": true 1156 | }, 1157 | "node_modules/serialize-javascript": { 1158 | "version": "6.0.2", 1159 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", 1160 | "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", 1161 | "dev": true, 1162 | "license": "BSD-3-Clause", 1163 | "dependencies": { 1164 | "randombytes": "^2.1.0" 1165 | } 1166 | }, 1167 | "node_modules/string-width": { 1168 | "version": "4.2.3", 1169 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1170 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1171 | "dev": true, 1172 | "dependencies": { 1173 | "emoji-regex": "^8.0.0", 1174 | "is-fullwidth-code-point": "^3.0.0", 1175 | "strip-ansi": "^6.0.1" 1176 | }, 1177 | "engines": { 1178 | "node": ">=8" 1179 | } 1180 | }, 1181 | "node_modules/string-width/node_modules/ansi-regex": { 1182 | "version": "5.0.1", 1183 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1184 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1185 | "dev": true, 1186 | "engines": { 1187 | "node": ">=8" 1188 | } 1189 | }, 1190 | "node_modules/string-width/node_modules/strip-ansi": { 1191 | "version": "6.0.1", 1192 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1193 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1194 | "dev": true, 1195 | "dependencies": { 1196 | "ansi-regex": "^5.0.1" 1197 | }, 1198 | "engines": { 1199 | "node": ">=8" 1200 | } 1201 | }, 1202 | "node_modules/strip-json-comments": { 1203 | "version": "3.1.1", 1204 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1205 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1206 | "dev": true, 1207 | "engines": { 1208 | "node": ">=8" 1209 | } 1210 | }, 1211 | "node_modules/supports-color": { 1212 | "version": "7.2.0", 1213 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1214 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1215 | "dev": true, 1216 | "dependencies": { 1217 | "has-flag": "^4.0.0" 1218 | }, 1219 | "engines": { 1220 | "node": ">=8" 1221 | } 1222 | }, 1223 | "node_modules/to-regex-range": { 1224 | "version": "5.0.1", 1225 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1226 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1227 | "dev": true, 1228 | "license": "MIT", 1229 | "dependencies": { 1230 | "is-number": "^7.0.0" 1231 | }, 1232 | "engines": { 1233 | "node": ">=8.0" 1234 | } 1235 | }, 1236 | "node_modules/workerpool": { 1237 | "version": "6.5.1", 1238 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", 1239 | "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", 1240 | "dev": true, 1241 | "license": "Apache-2.0" 1242 | }, 1243 | "node_modules/wrap-ansi": { 1244 | "version": "7.0.0", 1245 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1246 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1247 | "dev": true, 1248 | "dependencies": { 1249 | "ansi-styles": "^4.0.0", 1250 | "string-width": "^4.1.0", 1251 | "strip-ansi": "^6.0.0" 1252 | }, 1253 | "engines": { 1254 | "node": ">=10" 1255 | }, 1256 | "funding": { 1257 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1258 | } 1259 | }, 1260 | "node_modules/wrap-ansi/node_modules/ansi-regex": { 1261 | "version": "5.0.1", 1262 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1263 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1264 | "dev": true, 1265 | "engines": { 1266 | "node": ">=8" 1267 | } 1268 | }, 1269 | "node_modules/wrap-ansi/node_modules/strip-ansi": { 1270 | "version": "6.0.1", 1271 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1272 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1273 | "dev": true, 1274 | "dependencies": { 1275 | "ansi-regex": "^5.0.1" 1276 | }, 1277 | "engines": { 1278 | "node": ">=8" 1279 | } 1280 | }, 1281 | "node_modules/wrappy": { 1282 | "version": "1.0.2", 1283 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1284 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1285 | "dev": true 1286 | }, 1287 | "node_modules/y18n": { 1288 | "version": "5.0.8", 1289 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1290 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1291 | "dev": true, 1292 | "engines": { 1293 | "node": ">=10" 1294 | } 1295 | }, 1296 | "node_modules/yargs-parser": { 1297 | "version": "20.2.9", 1298 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1299 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1300 | "dev": true, 1301 | "engines": { 1302 | "node": ">=10" 1303 | } 1304 | }, 1305 | "node_modules/yargs-unparser": { 1306 | "version": "2.0.0", 1307 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1308 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1309 | "dev": true, 1310 | "dependencies": { 1311 | "camelcase": "^6.0.0", 1312 | "decamelize": "^4.0.0", 1313 | "flat": "^5.0.2", 1314 | "is-plain-obj": "^2.1.0" 1315 | }, 1316 | "engines": { 1317 | "node": ">=10" 1318 | } 1319 | }, 1320 | "node_modules/yargs-unparser/node_modules/camelcase": { 1321 | "version": "6.2.0", 1322 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", 1323 | "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", 1324 | "dev": true, 1325 | "engines": { 1326 | "node": ">=10" 1327 | } 1328 | }, 1329 | "node_modules/yargs-unparser/node_modules/decamelize": { 1330 | "version": "4.0.0", 1331 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1332 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1333 | "dev": true, 1334 | "engines": { 1335 | "node": ">=10" 1336 | } 1337 | }, 1338 | "node_modules/yocto-queue": { 1339 | "version": "0.1.0", 1340 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1341 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1342 | "dev": true, 1343 | "engines": { 1344 | "node": ">=10" 1345 | } 1346 | } 1347 | }, 1348 | "dependencies": { 1349 | "@esbuild/aix-ppc64": { 1350 | "version": "0.25.0", 1351 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 1352 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 1353 | "dev": true, 1354 | "optional": true 1355 | }, 1356 | "@esbuild/android-arm": { 1357 | "version": "0.25.0", 1358 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 1359 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 1360 | "dev": true, 1361 | "optional": true 1362 | }, 1363 | "@esbuild/android-arm64": { 1364 | "version": "0.25.0", 1365 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 1366 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 1367 | "dev": true, 1368 | "optional": true 1369 | }, 1370 | "@esbuild/android-x64": { 1371 | "version": "0.25.0", 1372 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 1373 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 1374 | "dev": true, 1375 | "optional": true 1376 | }, 1377 | "@esbuild/darwin-arm64": { 1378 | "version": "0.25.0", 1379 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 1380 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 1381 | "dev": true, 1382 | "optional": true 1383 | }, 1384 | "@esbuild/darwin-x64": { 1385 | "version": "0.25.0", 1386 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 1387 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 1388 | "dev": true, 1389 | "optional": true 1390 | }, 1391 | "@esbuild/freebsd-arm64": { 1392 | "version": "0.25.0", 1393 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 1394 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 1395 | "dev": true, 1396 | "optional": true 1397 | }, 1398 | "@esbuild/freebsd-x64": { 1399 | "version": "0.25.0", 1400 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 1401 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 1402 | "dev": true, 1403 | "optional": true 1404 | }, 1405 | "@esbuild/linux-arm": { 1406 | "version": "0.25.0", 1407 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 1408 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 1409 | "dev": true, 1410 | "optional": true 1411 | }, 1412 | "@esbuild/linux-arm64": { 1413 | "version": "0.25.0", 1414 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 1415 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 1416 | "dev": true, 1417 | "optional": true 1418 | }, 1419 | "@esbuild/linux-ia32": { 1420 | "version": "0.25.0", 1421 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 1422 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 1423 | "dev": true, 1424 | "optional": true 1425 | }, 1426 | "@esbuild/linux-loong64": { 1427 | "version": "0.25.0", 1428 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 1429 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 1430 | "dev": true, 1431 | "optional": true 1432 | }, 1433 | "@esbuild/linux-mips64el": { 1434 | "version": "0.25.0", 1435 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 1436 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 1437 | "dev": true, 1438 | "optional": true 1439 | }, 1440 | "@esbuild/linux-ppc64": { 1441 | "version": "0.25.0", 1442 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 1443 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 1444 | "dev": true, 1445 | "optional": true 1446 | }, 1447 | "@esbuild/linux-riscv64": { 1448 | "version": "0.25.0", 1449 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 1450 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 1451 | "dev": true, 1452 | "optional": true 1453 | }, 1454 | "@esbuild/linux-s390x": { 1455 | "version": "0.25.0", 1456 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 1457 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 1458 | "dev": true, 1459 | "optional": true 1460 | }, 1461 | "@esbuild/linux-x64": { 1462 | "version": "0.25.0", 1463 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 1464 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 1465 | "dev": true, 1466 | "optional": true 1467 | }, 1468 | "@esbuild/netbsd-arm64": { 1469 | "version": "0.25.0", 1470 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 1471 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 1472 | "dev": true, 1473 | "optional": true 1474 | }, 1475 | "@esbuild/netbsd-x64": { 1476 | "version": "0.25.0", 1477 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 1478 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 1479 | "dev": true, 1480 | "optional": true 1481 | }, 1482 | "@esbuild/openbsd-arm64": { 1483 | "version": "0.25.0", 1484 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 1485 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 1486 | "dev": true, 1487 | "optional": true 1488 | }, 1489 | "@esbuild/openbsd-x64": { 1490 | "version": "0.25.0", 1491 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 1492 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 1493 | "dev": true, 1494 | "optional": true 1495 | }, 1496 | "@esbuild/sunos-x64": { 1497 | "version": "0.25.0", 1498 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 1499 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 1500 | "dev": true, 1501 | "optional": true 1502 | }, 1503 | "@esbuild/win32-arm64": { 1504 | "version": "0.25.0", 1505 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 1506 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 1507 | "dev": true, 1508 | "optional": true 1509 | }, 1510 | "@esbuild/win32-ia32": { 1511 | "version": "0.25.0", 1512 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 1513 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 1514 | "dev": true, 1515 | "optional": true 1516 | }, 1517 | "@esbuild/win32-x64": { 1518 | "version": "0.25.0", 1519 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 1520 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 1521 | "dev": true, 1522 | "optional": true 1523 | }, 1524 | "ansi-colors": { 1525 | "version": "4.1.3", 1526 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", 1527 | "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", 1528 | "dev": true 1529 | }, 1530 | "ansi-styles": { 1531 | "version": "4.3.0", 1532 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1533 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1534 | "dev": true, 1535 | "requires": { 1536 | "color-convert": "^2.0.1" 1537 | } 1538 | }, 1539 | "anymatch": { 1540 | "version": "3.1.3", 1541 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 1542 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 1543 | "dev": true, 1544 | "requires": { 1545 | "normalize-path": "^3.0.0", 1546 | "picomatch": "^2.0.4" 1547 | } 1548 | }, 1549 | "balanced-match": { 1550 | "version": "1.0.0", 1551 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 1552 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 1553 | "dev": true 1554 | }, 1555 | "binary-extensions": { 1556 | "version": "2.3.0", 1557 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 1558 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 1559 | "dev": true 1560 | }, 1561 | "braces": { 1562 | "version": "3.0.3", 1563 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 1564 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 1565 | "dev": true, 1566 | "requires": { 1567 | "fill-range": "^7.1.1" 1568 | } 1569 | }, 1570 | "browser-stdout": { 1571 | "version": "1.3.1", 1572 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1573 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1574 | "dev": true 1575 | }, 1576 | "chalk": { 1577 | "version": "4.1.0", 1578 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 1579 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 1580 | "dev": true, 1581 | "requires": { 1582 | "ansi-styles": "^4.1.0", 1583 | "supports-color": "^7.1.0" 1584 | } 1585 | }, 1586 | "chokidar": { 1587 | "version": "3.6.0", 1588 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 1589 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 1590 | "dev": true, 1591 | "requires": { 1592 | "anymatch": "~3.1.2", 1593 | "braces": "~3.0.2", 1594 | "fsevents": "~2.3.2", 1595 | "glob-parent": "~5.1.2", 1596 | "is-binary-path": "~2.1.0", 1597 | "is-glob": "~4.0.1", 1598 | "normalize-path": "~3.0.0", 1599 | "readdirp": "~3.6.0" 1600 | } 1601 | }, 1602 | "color-convert": { 1603 | "version": "2.0.1", 1604 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1605 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1606 | "dev": true, 1607 | "requires": { 1608 | "color-name": "~1.1.4" 1609 | } 1610 | }, 1611 | "color-name": { 1612 | "version": "1.1.4", 1613 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1614 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1615 | "dev": true 1616 | }, 1617 | "emoji-regex": { 1618 | "version": "8.0.0", 1619 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1620 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1621 | "dev": true 1622 | }, 1623 | "esbuild": { 1624 | "version": "0.25.0", 1625 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 1626 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 1627 | "dev": true, 1628 | "requires": { 1629 | "@esbuild/aix-ppc64": "0.25.0", 1630 | "@esbuild/android-arm": "0.25.0", 1631 | "@esbuild/android-arm64": "0.25.0", 1632 | "@esbuild/android-x64": "0.25.0", 1633 | "@esbuild/darwin-arm64": "0.25.0", 1634 | "@esbuild/darwin-x64": "0.25.0", 1635 | "@esbuild/freebsd-arm64": "0.25.0", 1636 | "@esbuild/freebsd-x64": "0.25.0", 1637 | "@esbuild/linux-arm": "0.25.0", 1638 | "@esbuild/linux-arm64": "0.25.0", 1639 | "@esbuild/linux-ia32": "0.25.0", 1640 | "@esbuild/linux-loong64": "0.25.0", 1641 | "@esbuild/linux-mips64el": "0.25.0", 1642 | "@esbuild/linux-ppc64": "0.25.0", 1643 | "@esbuild/linux-riscv64": "0.25.0", 1644 | "@esbuild/linux-s390x": "0.25.0", 1645 | "@esbuild/linux-x64": "0.25.0", 1646 | "@esbuild/netbsd-arm64": "0.25.0", 1647 | "@esbuild/netbsd-x64": "0.25.0", 1648 | "@esbuild/openbsd-arm64": "0.25.0", 1649 | "@esbuild/openbsd-x64": "0.25.0", 1650 | "@esbuild/sunos-x64": "0.25.0", 1651 | "@esbuild/win32-arm64": "0.25.0", 1652 | "@esbuild/win32-ia32": "0.25.0", 1653 | "@esbuild/win32-x64": "0.25.0" 1654 | } 1655 | }, 1656 | "escalade": { 1657 | "version": "3.2.0", 1658 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 1659 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 1660 | "dev": true 1661 | }, 1662 | "expect.js": { 1663 | "version": "0.3.1", 1664 | "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", 1665 | "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", 1666 | "dev": true 1667 | }, 1668 | "fill-range": { 1669 | "version": "7.1.1", 1670 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 1671 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 1672 | "dev": true, 1673 | "requires": { 1674 | "to-regex-range": "^5.0.1" 1675 | } 1676 | }, 1677 | "find-up": { 1678 | "version": "5.0.0", 1679 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1680 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1681 | "dev": true, 1682 | "requires": { 1683 | "locate-path": "^6.0.0", 1684 | "path-exists": "^4.0.0" 1685 | } 1686 | }, 1687 | "flat": { 1688 | "version": "5.0.2", 1689 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1690 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1691 | "dev": true 1692 | }, 1693 | "fs.realpath": { 1694 | "version": "1.0.0", 1695 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1696 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1697 | "dev": true 1698 | }, 1699 | "fsevents": { 1700 | "version": "2.3.3", 1701 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1702 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1703 | "dev": true, 1704 | "optional": true 1705 | }, 1706 | "get-caller-file": { 1707 | "version": "2.0.5", 1708 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1709 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1710 | "dev": true 1711 | }, 1712 | "glob-parent": { 1713 | "version": "5.1.2", 1714 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1715 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1716 | "dev": true, 1717 | "requires": { 1718 | "is-glob": "^4.0.1" 1719 | } 1720 | }, 1721 | "has-flag": { 1722 | "version": "4.0.0", 1723 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1724 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1725 | "dev": true 1726 | }, 1727 | "he": { 1728 | "version": "1.2.0", 1729 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1730 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1731 | "dev": true 1732 | }, 1733 | "inflight": { 1734 | "version": "1.0.6", 1735 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1736 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1737 | "dev": true, 1738 | "requires": { 1739 | "once": "^1.3.0", 1740 | "wrappy": "1" 1741 | } 1742 | }, 1743 | "inherits": { 1744 | "version": "2.0.4", 1745 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1746 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1747 | "dev": true 1748 | }, 1749 | "is-binary-path": { 1750 | "version": "2.1.0", 1751 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1752 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1753 | "dev": true, 1754 | "requires": { 1755 | "binary-extensions": "^2.0.0" 1756 | } 1757 | }, 1758 | "is-extglob": { 1759 | "version": "2.1.1", 1760 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1761 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1762 | "dev": true 1763 | }, 1764 | "is-fullwidth-code-point": { 1765 | "version": "3.0.0", 1766 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1767 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1768 | "dev": true 1769 | }, 1770 | "is-glob": { 1771 | "version": "4.0.3", 1772 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1773 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1774 | "dev": true, 1775 | "requires": { 1776 | "is-extglob": "^2.1.1" 1777 | } 1778 | }, 1779 | "is-number": { 1780 | "version": "7.0.0", 1781 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1782 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1783 | "dev": true 1784 | }, 1785 | "is-plain-obj": { 1786 | "version": "2.1.0", 1787 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1788 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1789 | "dev": true 1790 | }, 1791 | "is-unicode-supported": { 1792 | "version": "0.1.0", 1793 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1794 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1795 | "dev": true 1796 | }, 1797 | "locate-path": { 1798 | "version": "6.0.0", 1799 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1800 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1801 | "dev": true, 1802 | "requires": { 1803 | "p-locate": "^5.0.0" 1804 | } 1805 | }, 1806 | "log-symbols": { 1807 | "version": "4.1.0", 1808 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1809 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1810 | "dev": true, 1811 | "requires": { 1812 | "chalk": "^4.1.0", 1813 | "is-unicode-supported": "^0.1.0" 1814 | } 1815 | }, 1816 | "mocha": { 1817 | "version": "10.8.2", 1818 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", 1819 | "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", 1820 | "dev": true, 1821 | "requires": { 1822 | "ansi-colors": "^4.1.3", 1823 | "browser-stdout": "^1.3.1", 1824 | "chokidar": "^3.5.3", 1825 | "debug": "^4.3.5", 1826 | "diff": "^5.2.0", 1827 | "escape-string-regexp": "^4.0.0", 1828 | "find-up": "^5.0.0", 1829 | "glob": "^8.1.0", 1830 | "he": "^1.2.0", 1831 | "js-yaml": "^4.1.0", 1832 | "log-symbols": "^4.1.0", 1833 | "minimatch": "^5.1.6", 1834 | "ms": "^2.1.3", 1835 | "serialize-javascript": "^6.0.2", 1836 | "strip-json-comments": "^3.1.1", 1837 | "supports-color": "^8.1.1", 1838 | "workerpool": "^6.5.1", 1839 | "yargs": "^16.2.0", 1840 | "yargs-parser": "^20.2.9", 1841 | "yargs-unparser": "^2.0.0" 1842 | }, 1843 | "dependencies": { 1844 | "ansi-regex": { 1845 | "version": "5.0.1", 1846 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1847 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1848 | "dev": true 1849 | }, 1850 | "argparse": { 1851 | "version": "2.0.1", 1852 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1853 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1854 | "dev": true 1855 | }, 1856 | "brace-expansion": { 1857 | "version": "2.0.1", 1858 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 1859 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 1860 | "dev": true, 1861 | "requires": { 1862 | "balanced-match": "^1.0.0" 1863 | } 1864 | }, 1865 | "cliui": { 1866 | "version": "7.0.4", 1867 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1868 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1869 | "dev": true, 1870 | "requires": { 1871 | "string-width": "^4.2.0", 1872 | "strip-ansi": "^6.0.0", 1873 | "wrap-ansi": "^7.0.0" 1874 | } 1875 | }, 1876 | "debug": { 1877 | "version": "4.4.0", 1878 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1879 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1880 | "dev": true, 1881 | "requires": { 1882 | "ms": "^2.1.3" 1883 | } 1884 | }, 1885 | "diff": { 1886 | "version": "5.2.0", 1887 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", 1888 | "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", 1889 | "dev": true 1890 | }, 1891 | "escape-string-regexp": { 1892 | "version": "4.0.0", 1893 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1894 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1895 | "dev": true 1896 | }, 1897 | "glob": { 1898 | "version": "8.1.0", 1899 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 1900 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 1901 | "dev": true, 1902 | "requires": { 1903 | "fs.realpath": "^1.0.0", 1904 | "inflight": "^1.0.4", 1905 | "inherits": "2", 1906 | "minimatch": "^5.0.1", 1907 | "once": "^1.3.0" 1908 | } 1909 | }, 1910 | "js-yaml": { 1911 | "version": "4.1.0", 1912 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1913 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1914 | "dev": true, 1915 | "requires": { 1916 | "argparse": "^2.0.1" 1917 | } 1918 | }, 1919 | "minimatch": { 1920 | "version": "5.1.6", 1921 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 1922 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 1923 | "dev": true, 1924 | "requires": { 1925 | "brace-expansion": "^2.0.1" 1926 | } 1927 | }, 1928 | "ms": { 1929 | "version": "2.1.3", 1930 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1931 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1932 | "dev": true 1933 | }, 1934 | "strip-ansi": { 1935 | "version": "6.0.1", 1936 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1937 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1938 | "dev": true, 1939 | "requires": { 1940 | "ansi-regex": "^5.0.1" 1941 | } 1942 | }, 1943 | "supports-color": { 1944 | "version": "8.1.1", 1945 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1946 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1947 | "dev": true, 1948 | "requires": { 1949 | "has-flag": "^4.0.0" 1950 | } 1951 | }, 1952 | "yargs": { 1953 | "version": "16.2.0", 1954 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1955 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1956 | "dev": true, 1957 | "requires": { 1958 | "cliui": "^7.0.2", 1959 | "escalade": "^3.1.1", 1960 | "get-caller-file": "^2.0.5", 1961 | "require-directory": "^2.1.1", 1962 | "string-width": "^4.2.0", 1963 | "y18n": "^5.0.5", 1964 | "yargs-parser": "^20.2.2" 1965 | } 1966 | } 1967 | } 1968 | }, 1969 | "normalize-path": { 1970 | "version": "3.0.0", 1971 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1972 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1973 | "dev": true 1974 | }, 1975 | "once": { 1976 | "version": "1.4.0", 1977 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1978 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1979 | "dev": true, 1980 | "requires": { 1981 | "wrappy": "1" 1982 | } 1983 | }, 1984 | "p-limit": { 1985 | "version": "3.1.0", 1986 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1987 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1988 | "dev": true, 1989 | "requires": { 1990 | "yocto-queue": "^0.1.0" 1991 | } 1992 | }, 1993 | "p-locate": { 1994 | "version": "5.0.0", 1995 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1996 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1997 | "dev": true, 1998 | "requires": { 1999 | "p-limit": "^3.0.2" 2000 | } 2001 | }, 2002 | "path-exists": { 2003 | "version": "4.0.0", 2004 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 2005 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 2006 | "dev": true 2007 | }, 2008 | "picomatch": { 2009 | "version": "2.3.1", 2010 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2011 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2012 | "dev": true 2013 | }, 2014 | "randombytes": { 2015 | "version": "2.1.0", 2016 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 2017 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 2018 | "dev": true, 2019 | "requires": { 2020 | "safe-buffer": "^5.1.0" 2021 | } 2022 | }, 2023 | "readdirp": { 2024 | "version": "3.6.0", 2025 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 2026 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 2027 | "dev": true, 2028 | "requires": { 2029 | "picomatch": "^2.2.1" 2030 | } 2031 | }, 2032 | "require-directory": { 2033 | "version": "2.1.1", 2034 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 2035 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 2036 | "dev": true 2037 | }, 2038 | "safe-buffer": { 2039 | "version": "5.2.1", 2040 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2041 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 2042 | "dev": true 2043 | }, 2044 | "serialize-javascript": { 2045 | "version": "6.0.2", 2046 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", 2047 | "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", 2048 | "dev": true, 2049 | "requires": { 2050 | "randombytes": "^2.1.0" 2051 | } 2052 | }, 2053 | "string-width": { 2054 | "version": "4.2.3", 2055 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2056 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2057 | "dev": true, 2058 | "requires": { 2059 | "emoji-regex": "^8.0.0", 2060 | "is-fullwidth-code-point": "^3.0.0", 2061 | "strip-ansi": "^6.0.1" 2062 | }, 2063 | "dependencies": { 2064 | "ansi-regex": { 2065 | "version": "5.0.1", 2066 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 2067 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 2068 | "dev": true 2069 | }, 2070 | "strip-ansi": { 2071 | "version": "6.0.1", 2072 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2073 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2074 | "dev": true, 2075 | "requires": { 2076 | "ansi-regex": "^5.0.1" 2077 | } 2078 | } 2079 | } 2080 | }, 2081 | "strip-json-comments": { 2082 | "version": "3.1.1", 2083 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2084 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2085 | "dev": true 2086 | }, 2087 | "supports-color": { 2088 | "version": "7.2.0", 2089 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2090 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2091 | "dev": true, 2092 | "requires": { 2093 | "has-flag": "^4.0.0" 2094 | } 2095 | }, 2096 | "to-regex-range": { 2097 | "version": "5.0.1", 2098 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2099 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2100 | "dev": true, 2101 | "requires": { 2102 | "is-number": "^7.0.0" 2103 | } 2104 | }, 2105 | "workerpool": { 2106 | "version": "6.5.1", 2107 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", 2108 | "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", 2109 | "dev": true 2110 | }, 2111 | "wrap-ansi": { 2112 | "version": "7.0.0", 2113 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2114 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2115 | "dev": true, 2116 | "requires": { 2117 | "ansi-styles": "^4.0.0", 2118 | "string-width": "^4.1.0", 2119 | "strip-ansi": "^6.0.0" 2120 | }, 2121 | "dependencies": { 2122 | "ansi-regex": { 2123 | "version": "5.0.1", 2124 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 2125 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 2126 | "dev": true 2127 | }, 2128 | "strip-ansi": { 2129 | "version": "6.0.1", 2130 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2131 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2132 | "dev": true, 2133 | "requires": { 2134 | "ansi-regex": "^5.0.1" 2135 | } 2136 | } 2137 | } 2138 | }, 2139 | "wrappy": { 2140 | "version": "1.0.2", 2141 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2142 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2143 | "dev": true 2144 | }, 2145 | "y18n": { 2146 | "version": "5.0.8", 2147 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2148 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2149 | "dev": true 2150 | }, 2151 | "yargs-parser": { 2152 | "version": "20.2.9", 2153 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 2154 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 2155 | "dev": true 2156 | }, 2157 | "yargs-unparser": { 2158 | "version": "2.0.0", 2159 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 2160 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 2161 | "dev": true, 2162 | "requires": { 2163 | "camelcase": "^6.0.0", 2164 | "decamelize": "^4.0.0", 2165 | "flat": "^5.0.2", 2166 | "is-plain-obj": "^2.1.0" 2167 | }, 2168 | "dependencies": { 2169 | "camelcase": { 2170 | "version": "6.2.0", 2171 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", 2172 | "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", 2173 | "dev": true 2174 | }, 2175 | "decamelize": { 2176 | "version": "4.0.0", 2177 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 2178 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 2179 | "dev": true 2180 | } 2181 | } 2182 | }, 2183 | "yocto-queue": { 2184 | "version": "0.1.0", 2185 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2186 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2187 | "dev": true 2188 | } 2189 | } 2190 | } 2191 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gradient-parser", 3 | "version": "1.1.1", 4 | "description": "Parse CSS3 gradient definitions and return an AST.", 5 | "author": { 6 | "name": "Rafael Carcicio", 7 | "email": "rafael@caricio.com", 8 | "url": "https://github.com/rafaelcaricio" 9 | }, 10 | "homepage": "https://github.com/rafaelcaricio/gradient-parser", 11 | "bugs": { 12 | "url": "https://github.com/rafaelcaricio/gradient-parser/issues" 13 | }, 14 | "licenses": [ 15 | { 16 | "type": "MIT", 17 | "url": "http://rafaelcaricio.mit-license.org" 18 | } 19 | ], 20 | "maintainers": [], 21 | "contributors": [], 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/rafaelcaricio/gradient-parser.git" 25 | }, 26 | "main": "build/node.js", 27 | "scripts": { 28 | "test": "mocha spec/**/*.js", 29 | "build": "node build.js", 30 | "build:node": "node build.js --node", 31 | "build:web": "node build.js --web", 32 | "build:minify": "node build.js --minify", 33 | "start": "python -m SimpleHTTPServer 3000", 34 | "prepublish": "npm run build" 35 | }, 36 | "keywords": [ 37 | "library", 38 | "css3", 39 | "parser" 40 | ], 41 | "devDependencies": { 42 | "expect.js": "^0.3.1", 43 | "esbuild": "^0.25.0", 44 | "mocha": "^10.3.0" 45 | }, 46 | "engines": { 47 | "node": ">=0.10.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spec/parser.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('expect.js'); 4 | var gradients = require('../build/node'); 5 | 6 | 7 | describe('lib/parser.js', function () { 8 | var ast, 9 | subject; 10 | 11 | it('should exist', function () { 12 | expect(typeof gradients.parse).to.equal('function'); 13 | }); 14 | 15 | describe('error cases', function() { 16 | it('one more comma in definitions', function() { 17 | expect(function() { 18 | gradients.parse('-webkit-linear-gradient(red, blue),'); 19 | }).to.throwException(/One extra comma/); 20 | }); 21 | 22 | it('one more comma in colors', function() { 23 | expect(function() { 24 | gradients.parse('-o-linear-gradient(red, blue,)'); 25 | }).to.throwException(/Expected color definition/); 26 | }); 27 | 28 | it('invalid input', function() { 29 | expect(function() { 30 | gradients.parse('linear-gradient(red, blue) aaa'); 31 | }).to.throwException(/Invalid input not EOF/); 32 | }); 33 | 34 | it('missing open call', function() { 35 | expect(function() { 36 | gradients.parse('linear-gradient red, blue'); 37 | }).to.throwException(/Missing \(/); 38 | }); 39 | 40 | it('missing comma before color stops', function() { 41 | expect(function() { 42 | gradients.parse('linear-gradient(to right red, blue)'); 43 | }).to.throwException(/Missing comma before color stops/); 44 | }); 45 | 46 | it('missing color stops', function() { 47 | expect(function() { 48 | gradients.parse('linear-gradient(to right, )'); 49 | }).to.throwException(/Expected color definition/); 50 | }); 51 | 52 | it('missing closing call', function() { 53 | expect(function() { 54 | gradients.parse('linear-gradient(to right, red, blue aaa'); 55 | }).to.throwException(/Missing \)/); 56 | }); 57 | }); 58 | 59 | describe('when parsing a simple definition', function() { 60 | beforeEach(function() { 61 | ast = gradients.parse('linear-gradient(red, blue)'); 62 | subject = ast[0]; 63 | }); 64 | 65 | it('should get the gradient type', function () { 66 | expect(subject.type).to.equal('linear-gradient'); 67 | }); 68 | 69 | it('should get the orientation', function() { 70 | expect(subject.orientation).to.be(undefined); 71 | }); 72 | 73 | describe('colors', function() { 74 | it('should get all colors', function() { 75 | expect(subject.colorStops).to.have.length(2); 76 | }); 77 | 78 | describe('first color', function() { 79 | beforeEach(function() { 80 | subject = subject.colorStops[0]; 81 | }); 82 | 83 | it('should get literal type', function() { 84 | expect(subject.type).to.equal('literal'); 85 | }); 86 | 87 | it('should get the right color', function() { 88 | expect(subject.value).to.equal('red'); 89 | }); 90 | }); 91 | 92 | describe('second color', function() { 93 | beforeEach(function() { 94 | subject = subject.colorStops[1]; 95 | }); 96 | 97 | it('should get literal type', function() { 98 | expect(subject.type).to.equal('literal'); 99 | }); 100 | 101 | it('should get the right color', function() { 102 | expect(subject.value).to.equal('blue'); 103 | }); 104 | }); 105 | }); 106 | }); 107 | 108 | describe('parse all metric values', function() { 109 | [ 110 | 'px', 111 | 'em', 112 | '%' 113 | ].forEach(function(metric) { 114 | describe('parse color stop for metric '+ metric, function() { 115 | beforeEach(function() { 116 | ast = gradients.parse('linear-gradient(blue 10.3' + metric + ', transparent)'); 117 | subject = ast[0]; 118 | }); 119 | 120 | describe('the first color', function() { 121 | beforeEach(function() { 122 | subject = subject.colorStops[0]; 123 | }); 124 | 125 | it('should have the length', function() { 126 | expect(subject.length.type).to.equal(metric); 127 | expect(subject.length.value).to.equal('10.3'); 128 | }); 129 | }); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('parse all linear directional', function() { 135 | [ 136 | {type: 'angular', unparsedValue: '-145deg', value: '-145'}, 137 | {type: 'angular', unparsedValue: '1rad', value: '1'}, 138 | {type: 'directional', unparsedValue: 'to left top', value: 'left top'}, 139 | {type: 'directional', unparsedValue: 'to top left', value: 'top left'}, 140 | {type: 'directional', unparsedValue: 'to top right', value: 'top right'}, 141 | {type: 'directional', unparsedValue: 'to bottom left', value: 'bottom left'}, 142 | {type: 'directional', unparsedValue: 'to bottom right', value: 'bottom right'}, 143 | {type: 'directional', unparsedValue: 'to bottom', value: 'bottom'}, // Test modern syntax 144 | {type: 'directional', unparsedValue: 'bottom', value: 'bottom'} // Test legacy syntax 145 | ].forEach(function(orientation) { 146 | describe('parse orientation ' + orientation.type, function() { 147 | beforeEach(function() { 148 | ast = gradients.parse('linear-gradient(' + orientation.unparsedValue + ', blue, green)'); 149 | subject = ast[0].orientation; 150 | }); 151 | 152 | it('should parse value', function() { 153 | expect(subject.type).to.equal(orientation.type); 154 | expect(subject.value).to.equal(orientation.value); 155 | }); 156 | }); 157 | }); 158 | 159 | it('should correctly parse directional value without "to" keyword (legacy syntax)', function() { 160 | // This uses the legacy syntax without "to" keyword (e.g., "right" instead of "to right") 161 | const parsed = gradients.parse('-webkit-linear-gradient(right, rgb(248, 6, 234) 71%, rgb(202, 74, 208) 78%)'); 162 | let subject = parsed[0]; 163 | 164 | // It should properly identify the orientation as directional "right" 165 | expect(subject.orientation).to.be.an('object'); 166 | expect(subject.orientation.type).to.equal('directional'); 167 | expect(subject.orientation.value).to.equal('right'); 168 | 169 | // And it should have only 2 color stops 170 | expect(subject.colorStops).to.have.length(2); 171 | expect(subject.colorStops[0].type).to.equal('rgb'); 172 | expect(subject.colorStops[0].value).to.eql(['248', '6', '234']); 173 | expect(subject.colorStops[0].length.type).to.equal('%'); 174 | expect(subject.colorStops[0].length.value).to.equal('71'); 175 | 176 | expect(subject.colorStops[1].type).to.equal('rgb'); 177 | expect(subject.colorStops[1].value).to.eql(['202', '74', '208']); 178 | expect(subject.colorStops[1].length.type).to.equal('%'); 179 | expect(subject.colorStops[1].length.value).to.equal('78'); 180 | }); 181 | 182 | // Additional test cases for other legacy directional keywords 183 | it('should correctly parse legacy syntax with "top" direction', function() { 184 | const parsed = gradients.parse('-webkit-linear-gradient(top, #ff0000, #0000ff)'); 185 | let subject = parsed[0]; 186 | 187 | expect(subject.orientation).to.be.an('object'); 188 | expect(subject.orientation.type).to.equal('directional'); 189 | expect(subject.orientation.value).to.equal('top'); 190 | 191 | expect(subject.colorStops).to.have.length(2); 192 | expect(subject.colorStops[0].type).to.equal('hex'); 193 | expect(subject.colorStops[0].value).to.equal('ff0000'); 194 | expect(subject.colorStops[1].type).to.equal('hex'); 195 | expect(subject.colorStops[1].value).to.equal('0000ff'); 196 | }); 197 | 198 | it('should correctly parse "to bottom" direction (modern syntax)', function() { 199 | const parsed = gradients.parse('linear-gradient(to bottom, rgb(0, 91, 154), rgb(230, 193, 61))'); 200 | let subject = parsed[0]; 201 | 202 | expect(subject.orientation).to.be.an('object'); 203 | expect(subject.orientation.type).to.equal('directional'); 204 | expect(subject.orientation.value).to.equal('bottom'); 205 | 206 | expect(subject.colorStops).to.have.length(2); 207 | expect(subject.colorStops[0].type).to.equal('rgb'); 208 | expect(subject.colorStops[0].value).to.eql(['0', '91', '154']); 209 | expect(subject.colorStops[1].type).to.equal('rgb'); 210 | expect(subject.colorStops[1].value).to.eql(['230', '193', '61']); 211 | }); 212 | 213 | it('should correctly parse legacy syntax with "left" direction', function() { 214 | const parsed = gradients.parse('-webkit-linear-gradient(left, rgba(255, 0, 0, 0.5), rgba(0, 0, 255, 0.8))'); 215 | let subject = parsed[0]; 216 | 217 | expect(subject.orientation).to.be.an('object'); 218 | expect(subject.orientation.type).to.equal('directional'); 219 | expect(subject.orientation.value).to.equal('left'); 220 | 221 | expect(subject.colorStops).to.have.length(2); 222 | expect(subject.colorStops[0].type).to.equal('rgba'); 223 | expect(subject.colorStops[0].value).to.eql(['255', '0', '0', '0.5']); 224 | expect(subject.colorStops[1].type).to.equal('rgba'); 225 | expect(subject.colorStops[1].value).to.eql(['0', '0', '255', '0.8']); 226 | }); 227 | 228 | it('should correctly parse legacy syntax with "bottom" direction', function() { 229 | const parsed = gradients.parse('-webkit-linear-gradient(bottom, hsla(0, 100%, 50%, 0.3), hsla(240, 100%, 50%, 0.7))'); 230 | let subject = parsed[0]; 231 | 232 | expect(subject.orientation).to.be.an('object'); 233 | expect(subject.orientation.type).to.equal('directional'); 234 | expect(subject.orientation.value).to.equal('bottom'); 235 | 236 | expect(subject.colorStops).to.have.length(2); 237 | expect(subject.colorStops[0].type).to.equal('hsla'); 238 | expect(subject.colorStops[0].value).to.eql(['0', '100', '50', '0.3']); 239 | expect(subject.colorStops[1].type).to.equal('hsla'); 240 | expect(subject.colorStops[1].value).to.eql(['240', '100', '50', '0.7']); 241 | }); 242 | }); 243 | 244 | describe('parse all color types', function() { 245 | [ 246 | {type: 'literal', unparsedValue: 'red', value: 'red'}, 247 | {type: 'hex', unparsedValue: '#c2c2c2', value: 'c2c2c2'}, 248 | {type: 'rgb', unparsedValue: 'rgb(243, 226, 195)', value: ['243', '226', '195']}, 249 | {type: 'rgba', unparsedValue: 'rgba(243, 226, 195)', value: ['243', '226', '195']}, 250 | {type: 'hsl', unparsedValue: 'hsl(120, 60%, 70%)', value: ['120', '60', '70']}, 251 | {type: 'hsla', unparsedValue: 'hsla(120, 60%, 70%, 0.3)', value: ['120', '60', '70', '0.3']}, 252 | {type: 'hsla', unparsedValue: 'hsla(240, 100%, 50%, 0.5)', value: ['240', '100', '50', '0.5']}, 253 | {type: 'var', unparsedValue: 'var(--color-red)', value: '--color-red'}, 254 | ].forEach(function(color) { 255 | describe('parse color type '+ color.type, function() { 256 | beforeEach(function() { 257 | ast = gradients.parse('linear-gradient(12deg, ' + color.unparsedValue + ', blue, green)'); 258 | subject = ast[0].colorStops[0]; 259 | }); 260 | 261 | it('should parse value', function() { 262 | expect(subject.type).to.equal(color.type); 263 | expect(subject.value).to.eql(color.value); 264 | }); 265 | }); 266 | }); 267 | 268 | describe('error cases for HSL/HSLA', function() { 269 | it('should error on missing percentage for saturation', function() { 270 | expect(function() { 271 | gradients.parse('linear-gradient(hsl(120, 60, 70%))'); 272 | }).to.throwException(/Expected percentage value/); 273 | }); 274 | 275 | it('should error on missing percentage for lightness', function() { 276 | expect(function() { 277 | gradients.parse('linear-gradient(hsl(120, 60%, 70))'); 278 | }).to.throwException(/Expected percentage value/); 279 | }); 280 | 281 | it('should error on percentage for hue', function() { 282 | expect(function() { 283 | gradients.parse('linear-gradient(hsl(120%, 60%, 70%))'); 284 | }).to.throwException(/HSL hue value must be a number in degrees \(0-360\) or normalized \(-360 to 360\), not a percentage/); 285 | }); 286 | }); 287 | }); 288 | 289 | describe('parse linear gradients', function() { 290 | [ 291 | 'linear-gradient', 292 | 'radial-gradient', 293 | 'repeating-linear-gradient', 294 | 'repeating-radial-gradient' 295 | ].forEach(function(gradient) { 296 | describe('parse ' + gradient + ' gradient', function() { 297 | beforeEach(function() { 298 | ast = gradients.parse(gradient + '(red, blue)'); 299 | subject = ast[0]; 300 | }); 301 | 302 | it('should parse the gradient', function() { 303 | expect(subject.type).to.equal(gradient); 304 | }); 305 | }); 306 | }); 307 | }); 308 | 309 | describe('different radial declarations', function() { 310 | [ 311 | 'ellipse farthest-corner', 312 | 'ellipse cover', 313 | 'circle cover', 314 | 'center bottom, ellipse cover', 315 | 'circle at 87.23px -58.3px', 316 | 'farthest-side, red, blue', 317 | 'farthest-corner, red, blue', 318 | 'farthest-corner at 87.23px -58.3px, red, blue' 319 | ].forEach(function(declaration) { 320 | 321 | it('should parse ' + declaration + ' declaration', function() { 322 | ast = gradients.parse('radial-gradient(' + declaration + ', red, blue)'); 323 | }); 324 | 325 | }); 326 | 327 | it('should parse ellipse with dimensions and position', function() { 328 | const gradient = 'repeating-radial-gradient(ellipse 40px 134px at 50% 96%, rgb(0, 165, 223) 0%, rgb(62, 20, 123) 6.6%)'; 329 | const ast = gradients.parse(gradient); 330 | 331 | expect(ast[0].type).to.equal('repeating-radial-gradient'); 332 | expect(ast[0].orientation[0].type).to.equal('shape'); 333 | expect(ast[0].orientation[0].value).to.equal('ellipse'); 334 | 335 | // Check the style (size dimensions) 336 | expect(ast[0].orientation[0].style.type).to.equal('position'); 337 | expect(ast[0].orientation[0].style.value.x.type).to.equal('px'); 338 | expect(ast[0].orientation[0].style.value.x.value).to.equal('40'); 339 | expect(ast[0].orientation[0].style.value.y.type).to.equal('px'); 340 | expect(ast[0].orientation[0].style.value.y.value).to.equal('134'); 341 | 342 | // Check the position 343 | expect(ast[0].orientation[0].at.type).to.equal('position'); 344 | expect(ast[0].orientation[0].at.value.x.type).to.equal('%'); 345 | expect(ast[0].orientation[0].at.value.x.value).to.equal('50'); 346 | expect(ast[0].orientation[0].at.value.y.type).to.equal('%'); 347 | expect(ast[0].orientation[0].at.value.y.value).to.equal('96'); 348 | 349 | // Check the color stops 350 | expect(ast[0].colorStops).to.have.length(2); 351 | expect(ast[0].colorStops[0].type).to.equal('rgb'); 352 | expect(ast[0].colorStops[0].value).to.eql(['0', '165', '223']); 353 | expect(ast[0].colorStops[0].length.type).to.equal('%'); 354 | expect(ast[0].colorStops[0].length.value).to.equal('0'); 355 | 356 | expect(ast[0].colorStops[1].type).to.equal('rgb'); 357 | expect(ast[0].colorStops[1].value).to.eql(['62', '20', '123']); 358 | expect(ast[0].colorStops[1].length.type).to.equal('%'); 359 | expect(ast[0].colorStops[1].length.value).to.equal('6.6'); 360 | }); 361 | 362 | it('should parse full Pride flag gradient', function() { 363 | const gradient = 'repeating-radial-gradient(ellipse 40px 134px at 50% 96%,rgb(0, 165, 223) 0%,rgb(62, 20, 123) 6.6%,rgb(226, 0, 121) 13.2%,rgb(223, 19, 44) 18.8%,rgb(243, 239, 21) 24.1%,rgb(0, 152, 71) 33.3%)'; 364 | const ast = gradients.parse(gradient); 365 | 366 | expect(ast[0].type).to.equal('repeating-radial-gradient'); 367 | expect(ast[0].orientation[0].type).to.equal('shape'); 368 | expect(ast[0].orientation[0].value).to.equal('ellipse'); 369 | 370 | // Check dimensions and position 371 | expect(ast[0].orientation[0].style.type).to.equal('position'); 372 | expect(ast[0].orientation[0].at.type).to.equal('position'); 373 | 374 | // Verify all color stops are present (Pride flag colors) 375 | expect(ast[0].colorStops).to.have.length(6); 376 | 377 | // Check the first and last color stops 378 | expect(ast[0].colorStops[0].type).to.equal('rgb'); 379 | expect(ast[0].colorStops[0].value).to.eql(['0', '165', '223']); 380 | 381 | expect(ast[0].colorStops[5].type).to.equal('rgb'); 382 | expect(ast[0].colorStops[5].value).to.eql(['0', '152', '71']); 383 | expect(ast[0].colorStops[5].length.type).to.equal('%'); 384 | expect(ast[0].colorStops[5].length.value).to.equal('33.3'); 385 | }); 386 | 387 | it('should parse radial-gradient with position only (no shape/extent)', function() { 388 | const gradient = 'radial-gradient(at 57% 50%, rgb(102, 126, 234) 0%, rgb(118, 75, 162) 100%)'; 389 | const ast = gradients.parse(gradient); 390 | 391 | expect(ast[0].type).to.equal('radial-gradient'); 392 | 393 | // Verify the orientation (position only) 394 | expect(ast[0].orientation[0].type).to.equal('default-radial'); 395 | expect(ast[0].orientation[0].at.type).to.equal('position'); 396 | expect(ast[0].orientation[0].at.value.x.type).to.equal('%'); 397 | expect(ast[0].orientation[0].at.value.x.value).to.equal('57'); 398 | expect(ast[0].orientation[0].at.value.y.type).to.equal('%'); 399 | expect(ast[0].orientation[0].at.value.y.value).to.equal('50'); 400 | 401 | // Verify color stops 402 | expect(ast[0].colorStops).to.have.length(2); 403 | expect(ast[0].colorStops[0].type).to.equal('rgb'); 404 | expect(ast[0].colorStops[0].value).to.eql(['102', '126', '234']); 405 | expect(ast[0].colorStops[0].length.type).to.equal('%'); 406 | expect(ast[0].colorStops[0].length.value).to.equal('0'); 407 | 408 | expect(ast[0].colorStops[1].type).to.equal('rgb'); 409 | expect(ast[0].colorStops[1].value).to.eql(['118', '75', '162']); 410 | expect(ast[0].colorStops[1].length.type).to.equal('%'); 411 | expect(ast[0].colorStops[1].length.value).to.equal('100'); 412 | }); 413 | }); 414 | 415 | describe('parse gradient strings with trailing semicolons', function() { 416 | it('should parse linear-gradient with trailing semicolon', function() { 417 | const inputWithSemicolon = 'linear-gradient(red, blue);'; 418 | const ast = gradients.parse(inputWithSemicolon); 419 | expect(ast[0].type).to.equal('linear-gradient'); 420 | expect(ast[0].colorStops).to.have.length(2); 421 | expect(ast[0].colorStops[0].value).to.equal('red'); 422 | expect(ast[0].colorStops[1].value).to.equal('blue'); 423 | }); 424 | 425 | it('should parse radial-gradient with trailing semicolon', function() { 426 | const inputWithSemicolon = 'radial-gradient(circle, red, blue);'; 427 | const ast = gradients.parse(inputWithSemicolon); 428 | expect(ast[0].type).to.equal('radial-gradient'); 429 | expect(ast[0].colorStops).to.have.length(2); 430 | expect(ast[0].colorStops[0].value).to.equal('red'); 431 | expect(ast[0].colorStops[1].value).to.equal('blue'); 432 | }); 433 | 434 | it('should parse complex gradient with trailing semicolon', function() { 435 | const inputWithSemicolon = 'linear-gradient(to right, rgb(22, 234, 174) 0%, rgb(126, 32, 207) 100%);'; 436 | const ast = gradients.parse(inputWithSemicolon); 437 | expect(ast[0].type).to.equal('linear-gradient'); 438 | expect(ast[0].orientation.type).to.equal('directional'); 439 | expect(ast[0].orientation.value).to.equal('right'); 440 | expect(ast[0].colorStops).to.have.length(2); 441 | expect(ast[0].colorStops[0].type).to.equal('rgb'); 442 | expect(ast[0].colorStops[0].length.type).to.equal('%'); 443 | expect(ast[0].colorStops[0].length.value).to.equal('0'); 444 | expect(ast[0].colorStops[1].type).to.equal('rgb'); 445 | expect(ast[0].colorStops[1].length.type).to.equal('%'); 446 | expect(ast[0].colorStops[1].length.value).to.equal('100'); 447 | }); 448 | }); 449 | 450 | describe('parse gradient strings', function() { 451 | it('should parse repeating linear gradient with bottom right direction', function() { 452 | const gradient = 'repeating-linear-gradient(to bottom right,rgb(254, 158, 150) 0%,rgb(172, 79, 115) 100%)'; 453 | const ast = gradients.parse(gradient); 454 | 455 | expect(ast[0].type).to.equal('repeating-linear-gradient'); 456 | expect(ast[0].orientation.type).to.equal('directional'); 457 | expect(ast[0].orientation.value).to.equal('bottom right'); 458 | 459 | expect(ast[0].colorStops).to.have.length(2); 460 | expect(ast[0].colorStops[0].type).to.equal('rgb'); 461 | expect(ast[0].colorStops[0].value).to.eql(['254', '158', '150']); 462 | expect(ast[0].colorStops[0].length.type).to.equal('%'); 463 | expect(ast[0].colorStops[0].length.value).to.equal('0'); 464 | 465 | expect(ast[0].colorStops[1].type).to.equal('rgb'); 466 | expect(ast[0].colorStops[1].value).to.eql(['172', '79', '115']); 467 | expect(ast[0].colorStops[1].length.type).to.equal('%'); 468 | expect(ast[0].colorStops[1].length.value).to.equal('100'); 469 | }); 470 | 471 | describe('parse different color formats', function() { 472 | const testGradients = [ 473 | 'linear-gradient(red, blue)', 474 | 'linear-gradient(red, #00f)', 475 | 'linear-gradient(red, #0000ff)', 476 | 'linear-gradient(red, rgb(0, 0, 255))', 477 | 'linear-gradient(red, rgba(0, 0, 255, 1))', 478 | 'linear-gradient(red, hsl(240, 50%, 100%))', 479 | 'linear-gradient(red, hsla(240, 50%, 100%, 1))' 480 | ]; 481 | 482 | testGradients.forEach(function(gradient) { 483 | it('should parse ' + gradient, function() { 484 | const result = gradients.parse(gradient); 485 | expect(result[0].type).to.equal('linear-gradient'); 486 | expect(result[0].colorStops).to.have.length(2); 487 | expect(result[0].colorStops[0].type).to.equal('literal'); 488 | expect(result[0].colorStops[0].value).to.equal('red'); 489 | }); 490 | }); 491 | }); 492 | 493 | describe('parse calc expressions', function() { 494 | it('should parse linear gradient with calc in color stop position', function() { 495 | const gradient = 'linear-gradient(to right, red calc(10% + 20px), blue 50%)'; 496 | const ast = gradients.parse(gradient); 497 | 498 | expect(ast[0].type).to.equal('linear-gradient'); 499 | expect(ast[0].orientation.type).to.equal('directional'); 500 | expect(ast[0].orientation.value).to.equal('right'); 501 | 502 | expect(ast[0].colorStops).to.have.length(2); 503 | expect(ast[0].colorStops[0].type).to.equal('literal'); 504 | expect(ast[0].colorStops[0].value).to.equal('red'); 505 | expect(ast[0].colorStops[0].length.type).to.equal('calc'); 506 | expect(ast[0].colorStops[0].length.value).to.equal('10% + 20px'); 507 | 508 | expect(ast[0].colorStops[1].type).to.equal('literal'); 509 | expect(ast[0].colorStops[1].value).to.equal('blue'); 510 | expect(ast[0].colorStops[1].length.type).to.equal('%'); 511 | expect(ast[0].colorStops[1].length.value).to.equal('50'); 512 | }); 513 | 514 | it('should parse radial gradient with calc in position', function() { 515 | const gradient = 'radial-gradient(circle at calc(50% + 25px) 50%, red, blue)'; 516 | const ast = gradients.parse(gradient); 517 | 518 | expect(ast[0].type).to.equal('radial-gradient'); 519 | expect(ast[0].orientation[0].type).to.equal('shape'); 520 | expect(ast[0].orientation[0].value).to.equal('circle'); 521 | 522 | // Check the position 523 | expect(ast[0].orientation[0].at.type).to.equal('position'); 524 | expect(ast[0].orientation[0].at.value.x.type).to.equal('calc'); 525 | expect(ast[0].orientation[0].at.value.x.value).to.equal('50% + 25px'); 526 | expect(ast[0].orientation[0].at.value.y.type).to.equal('%'); 527 | expect(ast[0].orientation[0].at.value.y.value).to.equal('50'); 528 | 529 | // Check the color stops 530 | expect(ast[0].colorStops).to.have.length(2); 531 | expect(ast[0].colorStops[0].value).to.equal('red'); 532 | expect(ast[0].colorStops[1].value).to.equal('blue'); 533 | }); 534 | 535 | it('should parse calc expressions with multiple operations', function() { 536 | const gradient = 'linear-gradient(90deg, yellow calc(100% - 50px), green calc(100% - 20px))'; 537 | const ast = gradients.parse(gradient); 538 | 539 | expect(ast[0].type).to.equal('linear-gradient'); 540 | expect(ast[0].orientation.type).to.equal('angular'); 541 | expect(ast[0].orientation.value).to.equal('90'); 542 | 543 | expect(ast[0].colorStops).to.have.length(2); 544 | expect(ast[0].colorStops[0].type).to.equal('literal'); 545 | expect(ast[0].colorStops[0].value).to.equal('yellow'); 546 | expect(ast[0].colorStops[0].length.type).to.equal('calc'); 547 | expect(ast[0].colorStops[0].length.value).to.equal('100% - 50px'); 548 | 549 | expect(ast[0].colorStops[1].type).to.equal('literal'); 550 | expect(ast[0].colorStops[1].value).to.equal('green'); 551 | expect(ast[0].colorStops[1].length.type).to.equal('calc'); 552 | expect(ast[0].colorStops[1].length.value).to.equal('100% - 20px'); 553 | }); 554 | 555 | it('should parse calc expressions with nested parentheses', function() { 556 | const gradient = 'linear-gradient(to bottom, red calc(50% + (25px * 2)), blue)'; 557 | const ast = gradients.parse(gradient); 558 | 559 | expect(ast[0].type).to.equal('linear-gradient'); 560 | expect(ast[0].orientation.type).to.equal('directional'); 561 | expect(ast[0].orientation.value).to.equal('bottom'); 562 | 563 | expect(ast[0].colorStops).to.have.length(2); 564 | expect(ast[0].colorStops[0].type).to.equal('literal'); 565 | expect(ast[0].colorStops[0].value).to.equal('red'); 566 | expect(ast[0].colorStops[0].length.type).to.equal('calc'); 567 | expect(ast[0].colorStops[0].length.value).to.equal('50% + (25px * 2)'); 568 | }); 569 | 570 | it('should parse multiple calc expressions in the same gradient', function() { 571 | const gradient = 'radial-gradient(circle at calc(50% - 10px) calc(50% + 10px), red calc(20% + 10px), blue)'; 572 | const ast = gradients.parse(gradient); 573 | 574 | expect(ast[0].type).to.equal('radial-gradient'); 575 | expect(ast[0].orientation[0].type).to.equal('shape'); 576 | expect(ast[0].orientation[0].value).to.equal('circle'); 577 | 578 | // Check the position 579 | expect(ast[0].orientation[0].at.type).to.equal('position'); 580 | expect(ast[0].orientation[0].at.value.x.type).to.equal('calc'); 581 | expect(ast[0].orientation[0].at.value.x.value).to.equal('50% - 10px'); 582 | expect(ast[0].orientation[0].at.value.y.type).to.equal('calc'); 583 | expect(ast[0].orientation[0].at.value.y.value).to.equal('50% + 10px'); 584 | 585 | // Check the color stops 586 | expect(ast[0].colorStops).to.have.length(2); 587 | expect(ast[0].colorStops[0].type).to.equal('literal'); 588 | expect(ast[0].colorStops[0].value).to.equal('red'); 589 | expect(ast[0].colorStops[0].length.type).to.equal('calc'); 590 | expect(ast[0].colorStops[0].length.value).to.equal('20% + 10px'); 591 | }); 592 | 593 | it('should throw an error for unbalanced parentheses in calc expressions', function() { 594 | // Different test cases throw different errors, so we need to be more specific 595 | expect(function() { 596 | gradients.parse('linear-gradient(to right, red calc(50% + (25px), blue)'); 597 | }).to.throwException(); 598 | 599 | expect(function() { 600 | gradients.parse('radial-gradient(circle at calc(50% + 25px, red, blue)'); 601 | }).to.throwException(/Missing comma before color stops/); 602 | 603 | expect(function() { 604 | gradients.parse('linear-gradient(90deg, yellow calc(100% - (50px - 20px), green)'); 605 | }).to.throwException(); 606 | }); 607 | }); 608 | }); 609 | 610 | }); 611 | -------------------------------------------------------------------------------- /spec/stringify.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('expect.js'); 4 | var gradients = require('../build/node'); 5 | 6 | function pprint(ast) { 7 | console.log(JSON.stringify(ast, true, 2)); 8 | } 9 | 10 | describe('lib/stringify.js', function () { 11 | var subject; 12 | 13 | it('should exist', function () { 14 | expect(typeof gradients.stringify).to.equal('function'); 15 | }); 16 | 17 | describe('serialization', function() { 18 | it('should handle array input without error', function() { 19 | const nodes = [{ 20 | type: 'linear-gradient', 21 | colorStops: [{ type: 'literal', value: 'red' }, { type: 'literal', value: 'blue' }], 22 | orientation: null 23 | }]; 24 | 25 | expect(function() { 26 | const result = gradients.stringify(nodes); 27 | expect(result).to.equal('linear-gradient(red, blue)'); 28 | }).to.not.throwException(); 29 | }); 30 | 31 | 32 | it('if tree is null', function() { 33 | expect(gradients.stringify(null)).to.equal(''); 34 | }); 35 | 36 | it('should serialize a simple gradient', function() { 37 | var gradientDef = 'linear-gradient(black, white)'; 38 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 39 | }); 40 | 41 | it('should serialize gradient with hex', function() { 42 | var gradientDef = 'linear-gradient(#fff, white)'; 43 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 44 | }); 45 | 46 | it('should serialize gradient with var', function() { 47 | var gradientDef = 'linear-gradient(var(--color-black), white)'; 48 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 49 | }); 50 | 51 | it('should serialize gradient with rgb', function() { 52 | var gradientDef = 'linear-gradient(rgb(1, 2, 3), white)'; 53 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 54 | }); 55 | 56 | it('should serialize gradient with rgba', function() { 57 | var gradientDef = 'linear-gradient(rgba(1, 2, 3, .0), white)'; 58 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 59 | }); 60 | 61 | it('should serialize gradient with deg', function() { 62 | var gradientDef = 'linear-gradient(45deg, #fff, transparent)'; 63 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 64 | }); 65 | 66 | it('should serialize gradient with directional', function() { 67 | var gradientDef = 'linear-gradient(to left, #fff, transparent)'; 68 | expect(gradients.stringify(gradients.parse(gradientDef))).to.equal(gradientDef); 69 | }); 70 | 71 | describe('all metric values', function() { 72 | [ 73 | 'px', 74 | 'em', 75 | '%' 76 | ].forEach(function(metric) { 77 | var expectedResult; 78 | 79 | describe('stringify color stop for metric '+ metric, function() { 80 | beforeEach(function() { 81 | expectedResult = 'linear-gradient(blue 10.3' + metric + ', transparent)'; 82 | var ast = gradients.parse(expectedResult); 83 | subject = gradients.stringify(ast); 84 | }); 85 | 86 | it('should result as expected', function() { 87 | expect(subject).to.equal(expectedResult); 88 | }); 89 | 90 | }); 91 | }); 92 | }); 93 | 94 | describe('different radial declarations', function() { 95 | [ 96 | 'ellipse farthest-corner', 97 | 'ellipse cover', 98 | 'circle cover', 99 | 'center bottom, ellipse cover', 100 | 'circle at 87.23px -58.3px', 101 | 'farthest-corner, red, blue', 102 | 'farthest-corner at 87.23px -58.3px, red, blue' 103 | ].forEach(function(declaration) { 104 | 105 | it('should parse ' + declaration + ' declaration', function() { 106 | var expectedResult = 'radial-gradient(' + declaration + ', red, blue)'; 107 | var ast = gradients.parse(expectedResult); 108 | subject = gradients.stringify(ast); 109 | 110 | expect(subject).to.equal(expectedResult); 111 | }); 112 | 113 | }); 114 | }); 115 | 116 | }); 117 | 118 | }); 119 | -------------------------------------------------------------------------------- /webify.js: -------------------------------------------------------------------------------- 1 | var GradientParser = (window.GradientParser || {}); 2 | --------------------------------------------------------------------------------