0.2.0 - Mid migration

This commit is contained in:
Daniel Mason 2022-04-25 14:47:15 +12:00
parent 139e6a915e
commit 7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions

View file

@ -0,0 +1,36 @@
<!--
Please note: This template is *not* optional. Please fill in all fields and
questions, otherwise *the issue may be closed*. Please provide actual technical
information about errors, if an error has occurred.
-->
* Node Version:
* NPM Version:
* postcss-values-parser Version:
This issue is regarding a problem with:
- [ ] Standard CSS
- [ ] LESS
- [ ] SCSS
- [ ] SASS
If you have a large amount of code to share which demonstrates the problem you're experiencing, please provide a link to your
repository rather than pasting code. Otherwise, please paste relevant short snippets below.
```css
// offending or problematic css
```
```js
// any js you'd like to add. this section can be removed if none
```
```
// actual error output, if error was thrown
```
### Expected Behavior
### Actual Behavior
### How can we reproduce the behavior?

View file

@ -0,0 +1,14 @@
<!-- This template is *not* optional. If you remove this template or choose
not to complete it, your PR may be closed without review -->
**Which issue #** if any, does this resolve?
<!-- PRs must be accompanied by related tests -->
Please check one:
- [ ] New tests created for this change
- [ ] Tests updated for this change
---
<!-- add additional comments here -->

585
web/node_modules/postcss-values-parser/API.md generated vendored Normal file
View file

@ -0,0 +1,585 @@
# API Documentation
*Please use only this documented API when working with the parser. Methods
not documented here are subject to change at any point.*
<!-- toc -->
- [`parser` function](#parser-function)
* [`parser.atword([props])`](#parseratwordprops)
* [`parser.colon([props])`](#parsercolonprops)
* [`parser.comma([props])`](#parsercommaprops)
* [`parser.comment([props])`](#parsercommentprops)
* [`parser.func([props])`](#parserfuncprops)
* [`parser.number([props])`](#parsernumberprops)
* [`parser.operator([props])`](#parseroperatorprops)
* [`parser.paren([props])`](#parserparenprops)
* [`parser.string([props])`](#parserstringprops)
* [`parser.value([props])`](#parservalueprops)
* [`parser.word([props])`](#parserwordprops)
* [`parser.unicodeRange([props])`](#parserunicoderangeprops)
- [Node types](#node-types)
* [`node.type`](#nodetype)
* [`node.parent`](#nodeparent)
* [`node.toString()`, `String(node)`, or `'' + node`](#nodetostring-stringnode-or---node)
* [`node.next()` & `node.prev()`](#nodenext--nodeprev)
* [`node.replaceWith(node)`](#nodereplacewithnode)
* [`node.remove()`](#noderemove)
* [`node.clone()`](#nodeclone)
* [`node.raws`](#noderaws)
* [`node.source`](#nodesource)
* [`node.sourceIndex`](#nodesourceindex)
- [Container types](#container-types)
* [`container.nodes`](#containernodes)
* [`container.first` & `container.last`](#containerfirst--containerlast)
* [`container.at(index)`](#containeratindex)
* [`container.index(node)`](#containerindexnode)
* [`container.length`](#containerlength)
* [`container.each(callback)`](#containereachcallback)
* [`container.walk(callback)`](#containerwalkcallback)
* [`container.walk` proxies](#containerwalk-proxies)
* [`container.prepend(node)` & `container.append(node)`](#containerprependnode--containerappendnode)
* [`container.insertBefore(old, new)` & `container.insertAfter(old, new)`](#containerinsertbeforeold-new--containerinsertafterold-new)
* [`container.removeChild(node)`](#containerremovechildnode)
* [`container.removeAll()` or `container.empty()`](#containerremoveall-or-containerempty)
- [Root nodes`](#root-nodes)
- [Value nodes](#value-nodes)
<!-- tocstop -->
## `parser` function
This is the module's main entry point, and returns a `new Parser`.
```js
let parser = require('postcss-values-parser');
let ast = parser(source) // tokenizes the source string
.parse(); // parses the tokens and returns an AST
```
### `parser.atword([props])`
Creates a new AtWord value.
```js
parser.atword({ value: '@foo' });
// → @foo
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.colon([props])`
Creates a new colon Node.
```js
parser.colon({ value: ':' });
// → :
```
Arguments:
* `props (object)`: The new node's properties. If no properties are specified,
the default value of `:` will be used. It's not recommended to deviate from this.
### `parser.comma([props])`
Creates a new comma Node.
```js
parser.comma({ value: ',' });
// → ,
```
Arguments:
* `props (object)`: The new node's properties. If no properties are specified,
the default value of `,` will be used. It's not recommended to deviate from this.
### `parser.comment([props])`
Creates a new comment.
```js
parser.comment({ value: 'Affirmative, Dave. I read you.' });
// → /* Affirmative, Dave. I read you. */
```
```js
parser.comment({ value: 'Affirmative, Dave. I read you.', inline: true });
// → // Affirmative, Dave. I read you.
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.func([props])`
Creates a new function value Container node.
```js
let func = parser.func({ value: 'calc' });
func.append(parser.paren());
func.append(parser.paren({ value: ')' }));
func.toString();
// → calc()
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.number([props])`
Creates a new number Node.
```js
parser.number({ value: 10, unit: 'px' });
// → 10px
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.operator([props])`
Creates a new operator Node.
```js
parser.operator({ value: '+' });
// → +
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.paren([props])`
Creates a new parenthesis Node.
```js
parser.paren();
// → (
parser.paren({ value: ')' });
// → )
```
Arguments:
* `props (object)`: The new node's properties. If no value is specified, the
default value of `(` will be used.
### `parser.string([props])`
Creates a new string node.
```js
parser.string();
// → (empty)
parser.string({ value: 'hello', quote: '"' });
// → "hello"
```
Arguments:
* `props (object)`: The new node's properties. Note: If no `quote` property is
specified, the default value of `'` will be used.
### `parser.value([props])`
Creates a new value Node. This node acts as the container for all values within
the Root node, but can be created for convenience.
### `parser.word([props])`
Creates a new word Node. A `Word` is anything that doesn't fall into one of the
other node types.
```js
let word = parser.word({ value: '#fff' });
// → #fff
word.isHex;
// → true
word.isColor;
// → true
```
Arguments:
* `props (object)`: The new node's properties.
### `parser.unicodeRange([props])`
Creates a new unicode range Node.
```js
parser.unicodeRange({ value: 'U+26' });
// → U+26
```
Arguments:
* `props (object)`: The new node's properties.
## Node types
### `node.type`
A string representation of the node type. It can be one of the following;
`atword`, `colon`, `comma`, `comment`, `func`, `number`, `operator`,
`paren`, `string`, `unicoderange`, `value`, `word`.
```js
parser.word({ value: '#fff' }).type;
// → 'word'
```
### `node.parent`
Returns the parent node.
```js
root.nodes[0].parent === root;
```
### `node.toString()`, `String(node)`, or `'' + node`
Returns a string representation of the node.
```js
let color = parser.word({ value: '#fff' });
console.log(String(color));
// → #fff
```
### `node.next()` & `node.prev()`
Returns the next/previous child of the parent node.
```js
let next = func.next();
if (next && next.type !== 'paren') {
throw new Error('Unclosed function parenthesis!');
}
```
### `node.replaceWith(node)`
Replace a node with another.
```js
let ast = parser('#fff').parse();
let word = ast.first.first;
let atword = parser.atword({ value: '@purple' });
word.replaceWith(atword);
```
Arguments:
* `node`: The node to substitute the original with.
### `node.remove()`
Removes the node from its parent node.
```js
if (node.type === 'word') {
node.remove();
}
```
### `node.clone()`
Returns a copy of a node, detached from any parent containers that the
original might have had.
```js
let word = parser.word({ value: '#fff' });
let cloned = word.clone();
cloned.value = '#fff';
String(cloned);
// → #000
String(word);
// → #fff
```
### `node.raws`
Extra whitespaces around the node will be assigned to `node.raws.before` and
`node.raws.after`. Spaces in this context have no semantic meaning, but may
be useful for inspection:
```css
1px solid black
```
Any space following a node/segement is assigned to the next node's
`raws.before` property, unless the node with the trailing space is the only
node in the set.
```js
let source = 'calc(something about mary)';
let ast = parser(source).parse();
let func = ast.first.first;
let something = func.first.next();
let about = something.next();
something.raws.after;
// → (empty)
about.raws.before;
// → ' '
```
Additionally, any space remaining after the last node in a
set will be assigned to the last non-symbol child's `raws.after` property.
For example:
```js
let source = 'calc(something )';
let ast = parser(source).parse();
let func = ast.first.first;
let something = func.first.next();
something.raws.after;
// → ' '
```
### `node.source`
An object describing the node's start/end, line/column source position.
Within the following CSS, the `.bar` class node ...
```css
.foo,
.bar {}
```
... will contain the following `source` object.
```js
source: {
start: {
line: 2,
column: 3
},
end: {
line: 2,
column: 6
}
}
```
### `node.sourceIndex`
The zero-based index of the node within the original source string.
Within the following CSS, the `.baz` class node will have a `sourceIndex` of `12`.
```css
.foo, .bar, .baz {}
```
## Container types
The `root`, `node`, and `pseudo` nodes have some helper methods for working
with their children.
### `container.nodes`
An array of the container's children.
```js
// Input: h1 h2
nodes.at(0).nodes.length // → 3
nodes.at(0).nodes[0].value // → 'h1'
nodes.at(0).nodes[1].value // → ' '
```
### `container.first` & `container.last`
The first/last child of the container.
```js
node.first === node.nodes[0];
node.last === node.nodes[node.nodes.length - 1];
```
### `container.at(index)`
Returns the node at position `index`.
```js
node.at(0) === node.first;
node.at(0) === node.nodes[0];
```
Arguments:
* `index`: The index of the node to return.
### `container.index(node)`
Return the index of the node within its container.
```js
node.index(node.nodes[2]) // → 2
```
Arguments:
* `node`: A node within the current container.
### `container.length`
Proxy to the length of the container's nodes.
```js
container.length === container.nodes.length
```
### `container.each(callback)`
Iterate the container's immediate children, calling `callback` for each child.
You may return `false` within the callback to break the iteration.
```js
let className;
nodes.each(function (node, index) {
if (node.type === 'class') {
className = node.value;
return false;
}
});
```
Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding
or removing nodes from the container.
Arguments:
* `callback (function)`: A function to call for each node, which receives `node`
and `index` arguments.
### `container.walk(callback)`
Like `container#each`, but will also iterate child nodes as long as they are
`container` types.
```js
nodes.walk(function (node, index) {
// all nodes
});
```
Arguments:
* `callback (function)`: A function to call for each node, which receives `node`
and `index` arguments.
This iterator is safe to use whilst mutating `container.nodes`,
like `container#each`.
### `container.walk` proxies
The container class provides proxy methods for iterating over types of nodes,
so that it is easier to write modules that target specific nodes. Those
methods are:
* `container.walkAtWords`
* `container.walkColons`
* `container.walkCommas`
* `container.walkComments`
* `container.walkFunctionNodes`
* `container.walkNumberNodes`
* `container.walkOperators`
* `container.walkParenthesis`
* `container.walkStringNodes`
* `container.walkUnicodeRanges`
* `container.walkWords`
### `container.prepend(node)` & `container.append(node)`
Add a node to the start/end of the container. Note that doing so will set
the parent property of the node to this container.
```js
let color = parser.word({ value: '#fff' });
node.append(color);
```
Arguments:
* `node`: The node to add.
### `container.insertBefore(old, new)` & `container.insertAfter(old, new)`
Add a node before or after an existing node in a container:
```js
nodes.walk(function (node) {
if (node.type !== 'word') {
let colon = parser.colon();
node.parent.insertAfter(node, colon);
}
});
```
Arguments:
* `old`: The existing node in the container.
* `new`: The new node to add before/after the existing node.
### `container.removeChild(node)`
Remove the node from the container. Note that you can also use
`node.remove()` if you would like to remove just a single node.
```js
node.length // → 2
node.remove(word)
node.length // → 1;
word.parent // undefined
```
Arguments:
* `node`: The node to remove.
### `container.removeAll()` or `container.empty()`
Remove all children from the container.
```js
node.removeAll();
node.length // → 0
```
## Root nodes`
A root node represents the top-level Container for Value nodes. Indeed, all
a root's `toString()` method does is join its node children with a ','.
Other than this, it has no special functionality and acts like a container.
## Value nodes
A Value node represents a single compound node. For example, this
node string `1px solid black`, is represented as three distinct nodes.
It has no special functionality of its own.

22
web/node_modules/postcss-values-parser/LICENSE generated vendored Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) Andrew Powell <andrew@shellscape.org>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

110
web/node_modules/postcss-values-parser/README.md generated vendored Normal file
View file

@ -0,0 +1,110 @@
# postcss-values-parser [![Build Status](https://travis-ci.org/shellscape/postcss-values-parser.svg?branch=master)](https://travis-ci.org/shellscape/postcss-values-parser)
<img align="right" width="95" height="95"
title="Philosophers stone, logo of PostCSS"
src="http://postcss.github.io/postcss/logo.svg">
A CSS property value parser for use with [PostCSS](https://github.com/postcss/postcss),
following the same node, container, and traversal patterns as PostCSS.
## &nbsp;
<p align="center">
<b>:rocket: &nbsp; Are you ready to tackle ES6 and hone your JavaScript Skills?</b> &nbsp; :rocket:<br/>
Check out these outstanding <a href="https://es6.io/">ES6 courses</a> by <a href="https://github.com/wesbos">@wesbos</a>
</p>
---
As with PostCSS and postcss-selector-parser, this parser generates an
[Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree),
(aka "AST") which allows for ease of traversal and granular inspection of each
part of a property's value.
## postcss-values-parser vs. postcss-value-parser
Yeah, it's a tad confusing. The [Lesshint](https://github.com/lesshint/lesshint)
project needed a parser that would allow detailed inspection of property values
to the same degree that PostCSS and [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser)
provided. This was especailly important for the Lesshint project, as it provides
for very granular rules for linting LESS.
[postcss-value-parser](https://github.com/TrySound/postcss-value-parser)
makes a lot of assumption about how values should be parsed and how the resulting
AST should be organized. It was also fairly out of sync with the tokenzing and
traversal patterns and convenience methods found in PostCSS and
postcss-selector-parser.
So we needed an alternative, and drew upon all three projects to put together a
value parser that met and exceeded our needs. The improvements include:
- Written using ES6
- Uses the same Gulp toolchain as PostCSS
- Doesn't strip characters; eg. parenthesis
- Full AST traversal
- AST traversal based on node type
- Simple methods to derive strings from the parsed result
- Follows PostCSS patterns for whitespace between Nodes
- Provides convenience properties for number units, colors, etc.
## Usage
Please see the [API Documentation](API.md) for full usage information.
As with any NPM module, start with the install:
```
npm install postcss-values-parser
```
Using this parser is straightforward and doesn't require callbacks:
```js
const parser = require('postcss-values-parser');
const ast = parser('#fff').parse();
let color = ast // the Root node
.first // the Value node
.first; // a Word node, containing the color value.
```
## Loose Mode
Loose mode was introduced to support adherence to the W3C CSS Specification as
well as the ability to parse noncompliant CSS for variants like LESS, SCSS, and
CSSNext. If you're working with a noncompliant or CSS-like variant, then loose
mode is for you.
For example, the parser
will throw an error by default if `calc` parameters [don't adhere to the spec](https://www.w3.org/TR/css-values/#calc-syntax).
However, with loose mode enabled, the parse will ignore spec rules and succeed.
In-draft features, or CSS features in modules not yet finalized, often cause parser
errors. eg. `url(var(--somevar))`. Loose mode supports parsing of these features.
Loose Mode is enabled by passing an option of `loose: true` to the `parser` method.
```js
const less = 'calc(2+2)'; // not valid per spec, but valid in LESS
const cssnext = 'url(var(--somevar))'; // not valid per spec, but in spec draft
const parser = require('postcss-values-parser');
const ast = parser(less, { loose: true }).parse();
// parse will succeed
```
## Acknowledgements
This project was heavily influenced by [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser)
and utilized many patterns and logical constructs from the project.
Tests and some tokenizing techniques found in [postcss-value-parser](https://github.com/TrySound/postcss-value-parser)
were used.
## Contributing
- `git fork/clone`
- `npm i`
- Before PR'ing, make sure `npm test` still pass. Add test if you're adding features.
When you tweak [API.md](API.md), please run `npm run toc` before PR'ing.

26
web/node_modules/postcss-values-parser/lib/atword.js generated vendored Normal file
View file

@ -0,0 +1,26 @@
'use strict';
const Container = require('./container');
class AtWord extends Container {
constructor (opts) {
super(opts);
this.type = 'atword';
}
toString () {
let quote = this.quoted ? this.raws.quote : '';
return [
this.raws.before,
'@',
// we can't use String() here because it'll try using itself
// as the constructor
String.prototype.toString.call(this.value),
this.raws.after
].join('');
}
}
Container.registerWalker(AtWord);
module.exports = AtWord;

15
web/node_modules/postcss-values-parser/lib/colon.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Colon extends Node {
constructor (opts) {
super(opts);
this.type = 'colon';
}
}
Container.registerWalker(Colon);
module.exports = Colon;

15
web/node_modules/postcss-values-parser/lib/comma.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Comma extends Node {
constructor (opts) {
super(opts);
this.type = 'comma';
}
}
Container.registerWalker(Comma);
module.exports = Comma;

26
web/node_modules/postcss-values-parser/lib/comment.js generated vendored Normal file
View file

@ -0,0 +1,26 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Comment extends Node {
constructor (opts) {
super(opts);
this.type = 'comment';
this.inline = Object(opts).inline || false;
}
toString () {
return [
this.raws.before,
this.inline ? '//' : '/*',
String(this.value),
this.inline ? '' : '*/',
this.raws.after
].join('');
}
};
Container.registerWalker(Comment);
module.exports = Comment;

210
web/node_modules/postcss-values-parser/lib/container.js generated vendored Normal file
View file

@ -0,0 +1,210 @@
'use strict';
const Node = require('./node');
class Container extends Node {
constructor (opts) {
super(opts);
if (!this.nodes) {
this.nodes = [];
}
}
push (child) {
child.parent = this;
this.nodes.push(child);
return this;
}
each (callback) {
if (!this.lastEach) this.lastEach = 0;
if (!this.indexes) this.indexes = { };
this.lastEach += 1;
let id = this.lastEach,
index,
result;
this.indexes[id] = 0;
if (!this.nodes) return undefined;
while (this.indexes[id] < this.nodes.length) {
index = this.indexes[id];
result = callback(this.nodes[index], index);
if (result === false) break;
this.indexes[id] += 1;
}
delete this.indexes[id];
return result;
}
walk (callback) {
return this.each((child, i) => {
let result = callback(child, i);
if (result !== false && child.walk) {
result = child.walk(callback);
}
return result;
});
}
walkType (type, callback) {
if (!type || !callback) {
throw new Error('Parameters {type} and {callback} are required.');
}
// allow users to pass a constructor, or node type string; eg. Word.
const isTypeCallable = typeof type === 'function';
return this.walk((node, index) => {
if (isTypeCallable && node instanceof type || !isTypeCallable && node.type === type) {
return callback.call(this, node, index);
}
});
}
append (node) {
node.parent = this;
this.nodes.push(node);
return this;
}
prepend (node) {
node.parent = this;
this.nodes.unshift(node);
return this;
}
cleanRaws (keepBetween) {
super.cleanRaws(keepBetween);
if (this.nodes) {
for (let node of this.nodes) node.cleanRaws(keepBetween);
}
}
insertAfter (oldNode, newNode) {
let oldIndex = this.index(oldNode),
index;
this.nodes.splice(oldIndex + 1, 0, newNode);
for (let id in this.indexes) {
index = this.indexes[id];
if (oldIndex <= index) {
this.indexes[id] = index + this.nodes.length;
}
}
return this;
}
insertBefore (oldNode, newNode) {
let oldIndex = this.index(oldNode),
index;
this.nodes.splice(oldIndex, 0, newNode);
for (let id in this.indexes) {
index = this.indexes[id];
if (oldIndex <= index) {
this.indexes[id] = index + this.nodes.length;
}
}
return this;
}
removeChild (child) {
child = this.index(child);
this.nodes[child].parent = undefined;
this.nodes.splice(child, 1);
let index;
for (let id in this.indexes) {
index = this.indexes[id];
if (index >= child) {
this.indexes[id] = index - 1;
}
}
return this;
}
removeAll () {
for (let node of this.nodes) node.parent = undefined;
this.nodes = [];
return this;
}
every (condition) {
return this.nodes.every(condition);
}
some (condition) {
return this.nodes.some(condition);
}
index (child) {
if (typeof child === 'number') {
return child;
}
else {
return this.nodes.indexOf(child);
}
}
get first () {
if (!this.nodes) return undefined;
return this.nodes[0];
}
get last () {
if (!this.nodes) return undefined;
return this.nodes[this.nodes.length - 1];
}
toString () {
let result = this.nodes.map(String).join('');
if (this.value) {
result = this.value + result;
}
if (this.raws.before) {
result = this.raws.before + result;
}
if (this.raws.after) {
result += this.raws.after;
}
return result;
}
}
Container.registerWalker = (constructor) => {
let walkerName = 'walk' + constructor.name;
// plural sugar
if (walkerName.lastIndexOf('s') !== walkerName.length - 1) {
walkerName += 's';
}
if (Container.prototype[walkerName]) {
return;
}
// we need access to `this` so we can't use an arrow function
Container.prototype[walkerName] = function (callback) {
return this.walkType(constructor, callback);
};
};
module.exports = Container;

View file

@ -0,0 +1,19 @@
'use strict';
class ParserError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message || 'An error ocurred while parsing.';
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
}
else {
this.stack = (new Error(message)).stack;
}
}
}
module.exports = ParserError;

View file

@ -0,0 +1,19 @@
'use strict';
class TokenizeError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message || 'An error ocurred while tokzenizing.';
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
}
else {
this.stack = (new Error(message)).stack;
}
}
}
module.exports = TokenizeError;

16
web/node_modules/postcss-values-parser/lib/function.js generated vendored Normal file
View file

@ -0,0 +1,16 @@
'use strict';
const Container = require('./container');
class FunctionNode extends Container {
constructor (opts) {
super(opts);
this.type = 'func';
// start off at -1 so we know there haven't been any parens added
this.unbalanced = -1;
}
};
Container.registerWalker(FunctionNode);
module.exports = FunctionNode;

69
web/node_modules/postcss-values-parser/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,69 @@
'use strict';
const Parser = require('./parser');
const AtWord = require('./atword');
const Colon = require('./colon');
const Comma = require('./comma');
const Comment = require('./comment');
const Func = require('./function');
const Num = require('./number');
const Operator = require('./operator');
const Paren = require('./paren');
const Str = require('./string');
const UnicodeRange = require('./unicode-range');
const Value = require('./value');
const Word = require('./word');
let parser = function (source, options) {
return new Parser(source, options);
};
parser.atword = function (opts) {
return new AtWord(opts);
};
parser.colon = function (opts) {
return new Colon(Object.assign({ value: ':' }, opts));
};
parser.comma = function (opts) {
return new Comma(Object.assign({ value: ',' }, opts));
};
parser.comment = function (opts) {
return new Comment(opts);
};
parser.func = function (opts) {
return new Func(opts);
};
parser.number = function (opts) {
return new Num(opts);
};
parser.operator = function (opts) {
return new Operator(opts);
};
parser.paren = function (opts) {
return new Paren(Object.assign({ value: '(' }, opts));
};
parser.string = function (opts) {
return new Str(Object.assign({ quote: '\'' }, opts));
};
parser.value = function (opts) {
return new Value(opts);
};
parser.word = function (opts) {
return new Word(opts);
};
parser.unicodeRange = function (opts) {
return new UnicodeRange(opts);
};
module.exports = parser;

214
web/node_modules/postcss-values-parser/lib/node.js generated vendored Normal file
View file

@ -0,0 +1,214 @@
'use strict';
let cloneNode = function (obj, parent) {
let cloned = new obj.constructor();
for (let i in obj) {
if (!obj.hasOwnProperty(i)) continue;
let value = obj[i],
type = typeof value;
if (i === 'parent' && type === 'object') {
if (parent) cloned[i] = parent;
}
else if (i === 'source') {
cloned[i] = value;
}
else if (value instanceof Array) {
cloned[i] = value.map(j => cloneNode(j, cloned));
}
else if (i !== 'before' && i !== 'after' && i !== 'between' && i !== 'semicolon') {
if (type === 'object' && value !== null) value = cloneNode(value);
cloned[i] = value;
}
}
return cloned;
};
module.exports = class Node {
constructor (defaults) {
defaults = defaults || {};
this.raws = { before: '', after: '' };
for (let name in defaults) {
this[name] = defaults[name];
}
}
remove () {
if (this.parent) {
this.parent.removeChild(this);
}
this.parent = undefined;
return this;
}
toString () {
return [
this.raws.before,
String(this.value),
this.raws.after
].join('');
}
clone (overrides) {
overrides = overrides || {};
let cloned = cloneNode(this);
for (let name in overrides) {
cloned[name] = overrides[name];
}
return cloned;
}
cloneBefore (overrides) {
overrides = overrides || {};
let cloned = this.clone(overrides);
this.parent.insertBefore(this, cloned);
return cloned;
}
cloneAfter (overrides) {
overrides = overrides || {};
let cloned = this.clone(overrides);
this.parent.insertAfter(this, cloned);
return cloned;
}
replaceWith () {
let nodes = Array.prototype.slice.call(arguments);
if (this.parent) {
for (let node of nodes) {
this.parent.insertBefore(this, node);
}
this.remove();
}
return this;
}
moveTo (container) {
this.cleanRaws(this.root() === container.root());
this.remove();
container.append(this);
return this;
}
moveBefore (node) {
this.cleanRaws(this.root() === node.root());
this.remove();
node.parent.insertBefore(node, this);
return this;
}
moveAfter (node) {
this.cleanRaws(this.root() === node.root());
this.remove();
node.parent.insertAfter(node, this);
return this;
}
next () {
let index = this.parent.index(this);
return this.parent.nodes[index + 1];
}
prev () {
let index = this.parent.index(this);
return this.parent.nodes[index - 1];
}
toJSON () {
let fixed = { };
for (let name in this) {
if (!this.hasOwnProperty(name)) continue;
if (name === 'parent') continue;
let value = this[name];
if (value instanceof Array) {
fixed[name] = value.map(i => {
if (typeof i === 'object' && i.toJSON) {
return i.toJSON();
}
else {
return i;
}
});
}
else if (typeof value === 'object' && value.toJSON) {
fixed[name] = value.toJSON();
}
else {
fixed[name] = value;
}
}
return fixed;
}
root () {
let result = this;
while (result.parent) result = result.parent;
return result;
}
cleanRaws (keepBetween) {
delete this.raws.before;
delete this.raws.after;
if (!keepBetween) delete this.raws.between;
}
positionInside (index) {
let string = this.toString(),
column = this.source.start.column,
line = this.source.start.line;
for (let i = 0; i < index; i++) {
if (string[i] === '\n') {
column = 1;
line += 1;
}
else {
column += 1;
}
}
return { line, column };
}
positionBy (opts) {
let pos = this.source.start;
if (Object(opts).index) {
pos = this.positionInside(opts.index);
}
else if (Object(opts).word) {
let index = this.toString().indexOf(opts.word);
if (index !== -1) pos = this.positionInside(index);
}
return pos;
}
};

25
web/node_modules/postcss-values-parser/lib/number.js generated vendored Normal file
View file

@ -0,0 +1,25 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class NumberNode extends Node {
constructor (opts) {
super(opts);
this.type = 'number';
this.unit = Object(opts).unit || '';
}
toString () {
return [
this.raws.before,
String(this.value),
this.unit,
this.raws.after
].join('');
}
};
Container.registerWalker(NumberNode);
module.exports = NumberNode;

15
web/node_modules/postcss-values-parser/lib/operator.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Operator extends Node {
constructor (opts) {
super(opts);
this.type = 'operator';
}
}
Container.registerWalker(Operator);
module.exports = Operator;

16
web/node_modules/postcss-values-parser/lib/paren.js generated vendored Normal file
View file

@ -0,0 +1,16 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Parenthesis extends Node {
constructor (opts) {
super(opts);
this.type = 'paren';
this.parenType = '';
}
}
Container.registerWalker(Parenthesis);
module.exports = Parenthesis;

576
web/node_modules/postcss-values-parser/lib/parser.js generated vendored Normal file
View file

@ -0,0 +1,576 @@
'use strict';
const Root = require('./root');
const Value = require('./value');
const AtWord = require('./atword');
const Colon = require('./colon');
const Comma = require('./comma');
const Comment = require('./comment');
const Func = require('./function');
const Numbr = require('./number');
const Operator = require('./operator');
const Paren = require('./paren');
const Str = require('./string');
const Word = require('./word');
const UnicodeRange = require('./unicode-range');
const tokenize = require('./tokenize');
const flatten = require('flatten');
const indexesOf = require('indexes-of');
const uniq = require('uniq');
const ParserError = require('./errors/ParserError');
function sortAscending (list) {
return list.sort((a, b) => a - b);
}
module.exports = class Parser {
constructor (input, options) {
const defaults = { loose: false };
// cache needs to be an array for values with more than 1 level of function nesting
this.cache = [];
this.input = input;
this.options = Object.assign({}, defaults, options);
this.position = 0;
// we'll use this to keep track of the paren balance
this.unbalanced = 0;
this.root = new Root();
let value = new Value();
this.root.append(value);
this.current = value;
this.tokens = tokenize(input, this.options);
}
parse () {
return this.loop();
}
colon () {
let token = this.currToken;
this.newNode(new Colon({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
comma () {
let token = this.currToken;
this.newNode(new Comma({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
comment () {
let inline = false,
value = this.currToken[1].replace(/\/\*|\*\//g, ''),
node;
if (this.options.loose && value.startsWith("//")) {
value = value.substring(2);
inline = true;
}
node = new Comment({
value: value,
inline: inline,
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[4],
column: this.currToken[5]
}
},
sourceIndex: this.currToken[6]
});
this.newNode(node);
this.position++;
}
error (message, token) {
throw new ParserError(message + ` at line: ${token[2]}, column ${token[3]}`);
}
loop () {
while (this.position < this.tokens.length) {
this.parseTokens();
}
if (!this.current.last && this.spaces) {
this.current.raws.before += this.spaces;
}
else if (this.spaces) {
this.current.last.raws.after += this.spaces;
}
this.spaces = '';
return this.root;
}
operator () {
// if a +|- operator is followed by a non-word character (. is allowed) and
// is preceded by a non-word character. (5+5)
let char = this.currToken[1],
node;
if (char === '+' || char === '-') {
// only inspect if the operator is not the first token, and we're only
// within a calc() function: the only spec-valid place for math expressions
if (!this.options.loose) {
if (this.position > 0) {
if (this.current.type === 'func' && this.current.value === 'calc') {
// allow operators to be proceeded by spaces and opening parens
if (this.prevToken[0] !== 'space' && this.prevToken[0] !== '(') {
this.error('Syntax Error', this.currToken);
}
// valid: calc(1 - +2)
// invalid: calc(1 -+2)
else if (this.nextToken[0] !== 'space' && this.nextToken[0] !== 'word') {
this.error('Syntax Error', this.currToken);
}
// valid: calc(1 - +2)
// valid: calc(-0.5 + 2)
// invalid: calc(1 -2)
else if (this.nextToken[0] === 'word' && this.current.last.type !== 'operator' &&
this.current.last.value !== '(') {
this.error('Syntax Error', this.currToken);
}
}
// if we're not in a function and someone has doubled up on operators,
// or they're trying to perform a calc outside of a calc
// eg. +-4px or 5+ 5, throw an error
else if (this.nextToken[0] === 'space'
|| this.nextToken[0] === 'operator'
|| this.prevToken[0] === 'operator') {
this.error('Syntax Error', this.currToken);
}
}
}
if (!this.options.loose) {
if (this.nextToken[0] === 'word') {
return this.word();
}
}
else {
if ((!this.current.nodes.length || (this.current.last && this.current.last.type === 'operator')) && this.nextToken[0] === 'word') {
return this.word();
}
}
}
node = new Operator({
value: this.currToken[1],
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[2],
column: this.currToken[3]
}
},
sourceIndex: this.currToken[4]
});
this.position ++;
return this.newNode(node);
}
parseTokens () {
switch (this.currToken[0]) {
case 'space':
this.space();
break;
case 'colon':
this.colon();
break;
case 'comma':
this.comma();
break;
case 'comment':
this.comment();
break;
case '(':
this.parenOpen();
break;
case ')':
this.parenClose();
break;
case 'atword':
case 'word':
this.word();
break;
case 'operator':
this.operator();
break;
case 'string':
this.string();
break;
case 'unicoderange':
this.unicodeRange();
break;
default:
this.word();
break;
}
}
parenOpen () {
let unbalanced = 1,
pos = this.position + 1,
token = this.currToken,
last;
// check for balanced parens
while (pos < this.tokens.length && unbalanced) {
let tkn = this.tokens[pos];
if (tkn[0] === '(') {
unbalanced++;
}
if (tkn[0] === ')') {
unbalanced--;
}
pos ++;
}
if (unbalanced) {
this.error('Expected closing parenthesis', token);
}
// ok, all parens are balanced. continue on
last = this.current.last;
if (last && last.type === 'func' && last.unbalanced < 0) {
last.unbalanced = 0; // ok we're ready to add parens now
this.current = last;
}
this.current.unbalanced ++;
this.newNode(new Paren({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
// url functions get special treatment, and anything between the function
// parens get treated as one word, if the contents aren't not a string.
if (this.current.type === 'func' && this.current.unbalanced &&
this.current.value === 'url' && this.currToken[0] !== 'string' &&
this.currToken[0] !== ')' && !this.options.loose) {
let nextToken = this.nextToken,
value = this.currToken[1],
start = {
line: this.currToken[2],
column: this.currToken[3]
};
while (nextToken && nextToken[0] !== ')' && this.current.unbalanced) {
this.position ++;
value += this.currToken[1];
nextToken = this.nextToken;
}
if (this.position !== this.tokens.length - 1) {
// skip the following word definition, or it'll be a duplicate
this.position ++;
this.newNode(new Word({
value,
source: {
start,
end: {
line: this.currToken[4],
column: this.currToken[5]
}
},
sourceIndex: this.currToken[6]
}));
}
}
}
parenClose () {
let token = this.currToken;
this.newNode(new Paren({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
if (this.position >= this.tokens.length - 1 && !this.current.unbalanced) {
return;
}
this.current.unbalanced --;
if (this.current.unbalanced < 0) {
this.error('Expected opening parenthesis', token);
}
if (!this.current.unbalanced && this.cache.length) {
this.current = this.cache.pop();
}
}
space () {
let token = this.currToken;
// Handle space before and after the selector
if (this.position === (this.tokens.length - 1) || this.nextToken[0] === ',' || this.nextToken[0] === ')') {
this.current.last.raws.after += token[1];
this.position ++;
}
else {
this.spaces = token[1];
this.position ++;
}
}
unicodeRange () {
let token = this.currToken;
this.newNode(new UnicodeRange({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
splitWord () {
let nextToken = this.nextToken,
word = this.currToken[1],
rNumber = /^[\+\-]?((\d+(\.\d*)?)|(\.\d+))([eE][\+\-]?\d+)?/,
// treat css-like groupings differently so they can be inspected,
// but don't address them as anything but a word, but allow hex values
// to pass through.
rNoFollow = /^(?!\#([a-z0-9]+))[\#\{\}]/gi,
hasAt, indices;
if (!rNoFollow.test(word)) {
while (nextToken && nextToken[0] === 'word') {
this.position ++;
let current = this.currToken[1];
word += current;
nextToken = this.nextToken;
}
}
hasAt = indexesOf(word, '@');
indices = sortAscending(uniq(flatten([[0], hasAt])));
indices.forEach((ind, i) => {
let index = indices[i + 1] || word.length,
value = word.slice(ind, index),
node;
if (~hasAt.indexOf(ind)) {
node = new AtWord({
value: value.slice(1),
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i]
});
}
else if (rNumber.test(this.currToken[1])) {
let unit = value.replace(rNumber, '');
node = new Numbr({
value: value.replace(unit, ''),
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i],
unit
});
}
else {
node = new (nextToken && nextToken[0] === '(' ? Func : Word)({
value,
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i]
});
if (node.constructor.name === 'Word') {
node.isHex = /^#(.+)/.test(value);
node.isColor = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value);
}
else {
this.cache.push(this.current);
}
}
this.newNode(node);
});
this.position ++;
}
string () {
let token = this.currToken,
value = this.currToken[1],
rQuote = /^(\"|\')/,
quoted = rQuote.test(value),
quote = '',
node;
if (quoted) {
quote = value.match(rQuote)[0];
// set value to the string within the quotes
// quotes are stored in raws
value = value.slice(1, value.length - 1);
}
node = new Str({
value,
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6],
quoted
});
node.raws.quote = quote;
this.newNode(node);
this.position++;
}
word () {
return this.splitWord();
}
newNode (node) {
if (this.spaces) {
node.raws.before += this.spaces;
this.spaces = '';
}
return this.current.append(node);
}
get currToken () {
return this.tokens[this.position];
}
get nextToken () {
return this.tokens[this.position + 1];
}
get prevToken () {
return this.tokens[this.position - 1];
}
};

10
web/node_modules/postcss-values-parser/lib/root.js generated vendored Normal file
View file

@ -0,0 +1,10 @@
'use strict';
const Container = require('./container');
module.exports = class Root extends Container {
constructor (opts) {
super(opts);
this.type = 'root';
}
};

28
web/node_modules/postcss-values-parser/lib/string.js generated vendored Normal file
View file

@ -0,0 +1,28 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class StringNode extends Node {
constructor (opts) {
super(opts);
this.type = 'string';
}
toString () {
let quote = this.quoted ? this.raws.quote : '';
return [
this.raws.before,
quote,
// we can't use String() here because it'll try using itself
// as the constructor
this.value + '',
quote,
this.raws.after
].join('');
}
}
Container.registerWalker(StringNode);
module.exports = StringNode;

394
web/node_modules/postcss-values-parser/lib/tokenize.js generated vendored Normal file
View file

@ -0,0 +1,394 @@
'use strict';
const openBracket = '{'.charCodeAt(0);
const closeBracket = '}'.charCodeAt(0);
const openParen = '('.charCodeAt(0);
const closeParen = ')'.charCodeAt(0);
const singleQuote = '\''.charCodeAt(0);
const doubleQuote = '"'.charCodeAt(0);
const backslash = '\\'.charCodeAt(0);
const slash = '/'.charCodeAt(0);
const period = '.'.charCodeAt(0);
const comma = ','.charCodeAt(0);
const colon = ':'.charCodeAt(0);
const asterisk = '*'.charCodeAt(0);
const minus = '-'.charCodeAt(0);
const plus = '+'.charCodeAt(0);
const pound = '#'.charCodeAt(0);
const newline = '\n'.charCodeAt(0);
const space = ' '.charCodeAt(0);
const feed = '\f'.charCodeAt(0);
const tab = '\t'.charCodeAt(0);
const cr = '\r'.charCodeAt(0);
const at = '@'.charCodeAt(0);
const lowerE = 'e'.charCodeAt(0);
const upperE = 'E'.charCodeAt(0);
const digit0 = '0'.charCodeAt(0);
const digit9 = '9'.charCodeAt(0);
const lowerU = 'u'.charCodeAt(0);
const upperU = 'U'.charCodeAt(0);
const atEnd = /[ \n\t\r\{\(\)'"\\;,/]/g;
const wordEnd = /[ \n\t\r\(\)\{\}\*:;@!&'"\+\|~>,\[\]\\]|\/(?=\*)/g;
const wordEndNum = /[ \n\t\r\(\)\{\}\*:;@!&'"\-\+\|~>,\[\]\\]|\//g;
const alphaNum = /^[a-z0-9]/i;
const unicodeRange = /^[a-f0-9?\-]/i;
const util = require('util');
const TokenizeError = require('./errors/TokenizeError');
module.exports = function tokenize (input, options) {
options = options || {};
let tokens = [],
css = input.valueOf(),
length = css.length,
offset = -1,
line = 1,
pos = 0,
parentCount = 0,
isURLArg = null,
code, next, quote, lines, last, content, escape, nextLine, nextOffset,
escaped, escapePos, nextChar;
function unclosed (what) {
let message = util.format('Unclosed %s at line: %d, column: %d, token: %d', what, line, pos - offset, pos);
throw new TokenizeError(message);
}
function tokenizeError () {
let message = util.format('Syntax error at line: %d, column: %d, token: %d', line, pos - offset, pos);
throw new TokenizeError(message);
}
while (pos < length) {
code = css.charCodeAt(pos);
if (code === newline) {
offset = pos;
line += 1;
}
switch (code) {
case newline:
case space:
case tab:
case cr:
case feed:
next = pos;
do {
next += 1;
code = css.charCodeAt(next);
if (code === newline) {
offset = next;
line += 1;
}
} while (code === space ||
code === newline ||
code === tab ||
code === cr ||
code === feed);
tokens.push(['space', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
break;
case colon:
next = pos + 1;
tokens.push(['colon', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
break;
case comma:
next = pos + 1;
tokens.push(['comma', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
break;
case openBracket:
tokens.push(['{', '{',
line, pos - offset,
line, next - offset,
pos
]);
break;
case closeBracket:
tokens.push(['}', '}',
line, pos - offset,
line, next - offset,
pos
]);
break;
case openParen:
parentCount++;
isURLArg = !isURLArg && parentCount === 1 &&
tokens.length > 0 &&
tokens[tokens.length - 1][0] === "word" &&
tokens[tokens.length - 1][1] === "url";
tokens.push(['(', '(',
line, pos - offset,
line, next - offset,
pos
]);
break;
case closeParen:
parentCount--;
isURLArg = isURLArg && parentCount > 0;
tokens.push([')', ')',
line, pos - offset,
line, next - offset,
pos
]);
break;
case singleQuote:
case doubleQuote:
quote = code === singleQuote ? '\'' : '"';
next = pos;
do {
escaped = false;
next = css.indexOf(quote, next + 1);
if (next === -1) {
unclosed('quote', quote);
}
escapePos = next;
while (css.charCodeAt(escapePos - 1) === backslash) {
escapePos -= 1;
escaped = !escaped;
}
} while (escaped);
tokens.push(['string', css.slice(pos, next + 1),
line, pos - offset,
line, next - offset,
pos
]);
pos = next;
break;
case at:
atEnd.lastIndex = pos + 1;
atEnd.test(css);
if (atEnd.lastIndex === 0) {
next = css.length - 1;
}
else {
next = atEnd.lastIndex - 2;
}
tokens.push(['atword', css.slice(pos, next + 1),
line, pos - offset,
line, next - offset,
pos
]);
pos = next;
break;
case backslash:
next = pos;
code = css.charCodeAt(next + 1);
if (escape && (code !== slash && code !== space &&
code !== newline && code !== tab &&
code !== cr && code !== feed)) {
next += 1;
}
tokens.push(['word', css.slice(pos, next + 1),
line, pos - offset,
line, next - offset,
pos
]);
pos = next;
break;
case plus:
case minus:
case asterisk:
next = pos + 1;
nextChar = css.slice(pos + 1, next + 1);
let prevChar = css.slice(pos - 1, pos);
// if the operator is immediately followed by a word character, then we
// have a prefix of some kind, and should fall-through. eg. -webkit
// look for --* for custom variables
if (code === minus && nextChar.charCodeAt(0) === minus) {
next++;
tokens.push(['word', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
break;
}
tokens.push(['operator', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
break;
default:
if (code === slash && (css.charCodeAt(pos + 1) === asterisk || (options.loose && !isURLArg && css.charCodeAt(pos + 1) === slash))) {
const isStandardComment = css.charCodeAt(pos + 1) === asterisk;
if (isStandardComment) {
next = css.indexOf('*/', pos + 2) + 1;
if (next === 0) {
unclosed('comment', '*/');
}
}
else {
const newlinePos = css.indexOf('\n', pos + 2);
next = newlinePos !== -1 ? newlinePos - 1 : length;
}
content = css.slice(pos, next + 1);
lines = content.split('\n');
last = lines.length - 1;
if (last > 0) {
nextLine = line + last;
nextOffset = next - lines[last].length;
}
else {
nextLine = line;
nextOffset = offset;
}
tokens.push(['comment', content,
line, pos - offset,
nextLine, next - nextOffset,
pos
]);
offset = nextOffset;
line = nextLine;
pos = next;
}
else if (code === pound && !alphaNum.test(css.slice(pos + 1, pos + 2))) {
next = pos + 1;
tokens.push(['#', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
}
else if ((code === lowerU || code === upperU) && css.charCodeAt(pos + 1) === plus) {
next = pos + 2;
do {
next += 1;
code = css.charCodeAt(next);
} while (next < length && unicodeRange.test(css.slice(next, next + 1)));
tokens.push(['unicoderange', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
}
// catch a regular slash, that isn't a comment
else if (code === slash) {
next = pos + 1;
tokens.push(['operator', css.slice(pos, next),
line, pos - offset,
line, next - offset,
pos
]);
pos = next - 1;
}
else {
let regex = wordEnd;
// we're dealing with a word that starts with a number
// those get treated differently
if (code >= digit0 && code <= digit9) {
regex = wordEndNum;
}
regex.lastIndex = pos + 1;
regex.test(css);
if (regex.lastIndex === 0) {
next = css.length - 1;
}
else {
next = regex.lastIndex - 2;
}
// Exponential number notation with minus or plus: 1e-10, 1e+10
if (regex === wordEndNum || code === period) {
let ncode = css.charCodeAt(next),
ncode1 = css.charCodeAt(next + 1),
ncode2 = css.charCodeAt(next + 2);
if (
(ncode === lowerE || ncode === upperE) &&
(ncode1 === minus || ncode1 === plus) &&
(ncode2 >= digit0 && ncode2 <= digit9)
) {
wordEndNum.lastIndex = next + 2;
wordEndNum.test(css);
if (wordEndNum.lastIndex === 0) {
next = css.length - 1;
}
else {
next = wordEndNum.lastIndex - 2;
}
}
}
tokens.push(['word', css.slice(pos, next + 1),
line, pos - offset,
line, next - offset,
pos
]);
pos = next;
}
break;
}
pos ++;
}
return tokens;
};

View file

@ -0,0 +1,15 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class UnicodeRange extends Node {
constructor (opts) {
super(opts);
this.type = 'unicode-range';
}
}
Container.registerWalker(UnicodeRange);
module.exports = UnicodeRange;

11
web/node_modules/postcss-values-parser/lib/value.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
'use strict';
const Container = require('./container');
module.exports = class Value extends Container {
constructor (opts) {
super(opts);
this.type = 'value';
this.unbalanced = 0;
}
};

15
web/node_modules/postcss-values-parser/lib/word.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
'use strict';
const Container = require('./container');
const Node = require('./node');
class Word extends Node {
constructor (opts) {
super(opts);
this.type = 'word';
}
}
Container.registerWalker(Word);
module.exports = Word;

View file

@ -0,0 +1,48 @@
const Node = require('./node');
class Container extends Node {
walk(callback) {
return this.each((child, i) => {
let result = callback(child, i);
if (result !== false && child.walk) {
result = child.walk(callback);
}
return result;
});
}
walkType(type, callback) {
if (!type || !callback) {
throw new Error('Parameters {type} and {callback} are required.');
}
// allow users to pass a constructor, or node type string; eg. Word.
const isTypeCallable = typeof type === 'function';
return this.walk((node, index) => {
if ((isTypeCallable && node instanceof type) || (!isTypeCallable && node.type === type)) {
return callback.call(this, node, index);
}
});
}
}
Container.registerWalker = (constructor) => {
let walkerName = `walk${constructor.name}`;
// plural sugar
if (walkerName.lastIndexOf('s') !== walkerName.length - 1) {
walkerName += 's';
}
if (Container.prototype[walkerName]) {
return;
}
// we need access to `this` so we can't use an arrow function
Container.prototype[walkerName] = function(callback) {
return this.walkType(constructor, callback);
};
};
module.exports = Container;

View file

@ -0,0 +1,576 @@
'use strict';
const Root = require('./root');
const Value = require('./value');
const AtWord = require('./atword');
const Colon = require('./colon');
const Comma = require('./comma');
const Comment = require('./comment');
const Func = require('./function');
const Numbr = require('./number');
const Operator = require('./operator');
const Paren = require('./paren');
const Str = require('./string');
const Word = require('./word');
const UnicodeRange = require('./unicode-range');
const tokenize = require('./tokenize');
const flatten = require('flatten');
const indexesOf = require('indexes-of');
const uniq = require('uniq');
const ParserError = require('./errors/ParserError');
function sortAscending (list) {
return list.sort((a, b) => a - b);
}
module.exports = class Parser {
constructor (input, options) {
const defaults = { loose: false };
// cache needs to be an array for values with more than 1 level of function nesting
this.cache = [];
this.input = input;
this.options = Object.assign({}, defaults, options);
this.position = 0;
// we'll use this to keep track of the paren balance
this.unbalanced = 0;
this.root = new Root();
let value = new Value();
this.root.append(value);
this.current = value;
this.tokens = tokenize(input, this.options);
}
parse () {
return this.loop();
}
colon () {
let token = this.currToken;
this.newNode(new Colon({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
comma () {
let token = this.currToken;
this.newNode(new Comma({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
comment () {
let inline = false,
value = this.currToken[1].replace(/\/\*|\*\//g, ''),
node;
if (this.options.loose && value.startsWith("//")) {
value = value.substring(2);
inline = true;
}
node = new Comment({
value: value,
inline: inline,
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[4],
column: this.currToken[5]
}
},
sourceIndex: this.currToken[6]
});
this.newNode(node);
this.position++;
}
error (message, token) {
throw new ParserError(message + ` at line: ${token[2]}, column ${token[3]}`);
}
loop () {
while (this.position < this.tokens.length) {
this.parseTokens();
}
if (!this.current.last && this.spaces) {
this.current.raws.before += this.spaces;
}
else if (this.spaces) {
this.current.last.raws.after += this.spaces;
}
this.spaces = '';
return this.root;
}
operator () {
// if a +|- operator is followed by a non-word character (. is allowed) and
// is preceded by a non-word character. (5+5)
let char = this.currToken[1],
node;
if (char === '+' || char === '-') {
// only inspect if the operator is not the first token, and we're only
// within a calc() function: the only spec-valid place for math expressions
if (!this.options.loose) {
if (this.position > 0) {
if (this.current.type === 'func' && this.current.value === 'calc') {
// allow operators to be proceeded by spaces and opening parens
if (this.prevToken[0] !== 'space' && this.prevToken[0] !== '(') {
this.error('Syntax Error', this.currToken);
}
// valid: calc(1 - +2)
// invalid: calc(1 -+2)
else if (this.nextToken[0] !== 'space' && this.nextToken[0] !== 'word') {
this.error('Syntax Error', this.currToken);
}
// valid: calc(1 - +2)
// valid: calc(-0.5 + 2)
// invalid: calc(1 -2)
else if (this.nextToken[0] === 'word' && this.current.last.type !== 'operator' &&
this.current.last.value !== '(') {
this.error('Syntax Error', this.currToken);
}
}
// if we're not in a function and someone has doubled up on operators,
// or they're trying to perform a calc outside of a calc
// eg. +-4px or 5+ 5, throw an error
else if (this.nextToken[0] === 'space'
|| this.nextToken[0] === 'operator'
|| this.prevToken[0] === 'operator') {
this.error('Syntax Error', this.currToken);
}
}
}
if (!this.options.loose) {
if (this.nextToken[0] === 'word') {
return this.word();
}
}
else {
if ((!this.current.nodes.length || (this.current.last && this.current.last.type === 'operator')) && this.nextToken[0] === 'word') {
return this.word();
}
}
}
node = new Operator({
value: this.currToken[1],
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[2],
column: this.currToken[3]
}
},
sourceIndex: this.currToken[4]
});
this.position ++;
return this.newNode(node);
}
parseTokens () {
switch (this.currToken[0]) {
case 'space':
this.space();
break;
case 'colon':
this.colon();
break;
case 'comma':
this.comma();
break;
case 'comment':
this.comment();
break;
case '(':
this.parenOpen();
break;
case ')':
this.parenClose();
break;
case 'atword':
case 'word':
this.word();
break;
case 'operator':
this.operator();
break;
case 'string':
this.string();
break;
case 'unicoderange':
this.unicodeRange();
break;
default:
this.word();
break;
}
}
parenOpen () {
let unbalanced = 1,
pos = this.position + 1,
token = this.currToken,
last;
// check for balanced parens
while (pos < this.tokens.length && unbalanced) {
let tkn = this.tokens[pos];
if (tkn[0] === '(') {
unbalanced++;
}
if (tkn[0] === ')') {
unbalanced--;
}
pos ++;
}
if (unbalanced) {
this.error('Expected closing parenthesis', token);
}
// ok, all parens are balanced. continue on
last = this.current.last;
if (last && last.type === 'func' && last.unbalanced < 0) {
last.unbalanced = 0; // ok we're ready to add parens now
this.current = last;
}
this.current.unbalanced ++;
this.newNode(new Paren({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
// url functions get special treatment, and anything between the function
// parens get treated as one word, if the contents aren't not a string.
if (this.current.type === 'func' && this.current.unbalanced &&
this.current.value === 'url' && this.currToken[0] !== 'string' &&
this.currToken[0] !== ')' && !this.options.loose) {
let nextToken = this.nextToken,
value = this.currToken[1],
start = {
line: this.currToken[2],
column: this.currToken[3]
};
while (nextToken && nextToken[0] !== ')' && this.current.unbalanced) {
this.position ++;
value += this.currToken[1];
nextToken = this.nextToken;
}
if (this.position !== this.tokens.length - 1) {
// skip the following word definition, or it'll be a duplicate
this.position ++;
this.newNode(new Word({
value,
source: {
start,
end: {
line: this.currToken[4],
column: this.currToken[5]
}
},
sourceIndex: this.currToken[6]
}));
}
}
}
parenClose () {
let token = this.currToken;
this.newNode(new Paren({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
if (this.position >= this.tokens.length - 1 && !this.current.unbalanced) {
return;
}
this.current.unbalanced --;
if (this.current.unbalanced < 0) {
this.error('Expected opening parenthesis', token);
}
if (!this.current.unbalanced && this.cache.length) {
this.current = this.cache.pop();
}
}
space () {
let token = this.currToken;
// Handle space before and after the selector
if (this.position === (this.tokens.length - 1) || this.nextToken[0] === ',' || this.nextToken[0] === ')') {
this.current.last.raws.after += token[1];
this.position ++;
}
else {
this.spaces = token[1];
this.position ++;
}
}
unicodeRange () {
let token = this.currToken;
this.newNode(new UnicodeRange({
value: token[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position ++;
}
splitWord () {
let nextToken = this.nextToken,
word = this.currToken[1],
rNumber = /^[\+\-]?((\d+(\.\d*)?)|(\.\d+))([eE][\+\-]?\d+)?/,
// treat css-like groupings differently so they can be inspected,
// but don't address them as anything but a word, but allow hex values
// to pass through.
rNoFollow = /^(?!\#([a-z0-9]+))[\#\{\}]/gi,
hasAt, indices;
if (!rNoFollow.test(word)) {
while (nextToken && nextToken[0] === 'word') {
this.position ++;
let current = this.currToken[1];
word += current;
nextToken = this.nextToken;
}
}
hasAt = indexesOf(word, '@');
indices = sortAscending(uniq(flatten([[0], hasAt])));
indices.forEach((ind, i) => {
let index = indices[i + 1] || word.length,
value = word.slice(ind, index),
node;
if (~hasAt.indexOf(ind)) {
node = new AtWord({
value: value.slice(1),
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i]
});
}
else if (rNumber.test(this.currToken[1])) {
let unit = value.replace(rNumber, '');
node = new Numbr({
value: value.replace(unit, ''),
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i],
unit
});
}
else {
node = new (nextToken && nextToken[0] === '(' ? Func : Word)({
value,
source: {
start: {
line: this.currToken[2],
column: this.currToken[3] + ind
},
end: {
line: this.currToken[4],
column: this.currToken[3] + (index - 1)
}
},
sourceIndex: this.currToken[6] + indices[i]
});
if (node.constructor.name === 'Word') {
node.isHex = /^#(.+)/.test(value);
node.isColor = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value);
}
else {
this.cache.push(this.current);
}
}
this.newNode(node);
});
this.position ++;
}
string () {
let token = this.currToken,
value = this.currToken[1],
rQuote = /^(\"|\')/,
quoted = rQuote.test(value),
quote = '',
node;
if (quoted) {
quote = value.match(rQuote)[0];
// set value to the string within the quotes
// quotes are stored in raws
value = value.slice(1, value.length - 1);
}
node = new Str({
value,
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6],
quoted
});
node.raws.quote = quote;
this.newNode(node);
this.position++;
}
word () {
return this.splitWord();
}
newNode (node) {
if (this.spaces) {
node.raws.before += this.spaces;
this.spaces = '';
}
return this.current.append(node);
}
get currToken () {
return this.tokens[this.position];
}
get nextToken () {
return this.tokens[this.position + 1];
}
get prevToken () {
return this.tokens[this.position - 1];
}
};

View file

@ -0,0 +1,416 @@
const openBracket = '{'.charCodeAt(0);
const closeBracket = '}'.charCodeAt(0);
const openParen = '('.charCodeAt(0);
const closeParen = ')'.charCodeAt(0);
const singleQuote = "'".charCodeAt(0);
const doubleQuote = '"'.charCodeAt(0);
const backslash = '\\'.charCodeAt(0);
const slash = '/'.charCodeAt(0);
const period = '.'.charCodeAt(0);
const comma = ','.charCodeAt(0);
const colon = ':'.charCodeAt(0);
const asterisk = '*'.charCodeAt(0);
const minus = '-'.charCodeAt(0);
const plus = '+'.charCodeAt(0);
const pound = '#'.charCodeAt(0);
const newline = '\n'.charCodeAt(0);
const space = ' '.charCodeAt(0);
const feed = '\f'.charCodeAt(0);
const tab = '\t'.charCodeAt(0);
const cr = '\r'.charCodeAt(0);
const at = '@'.charCodeAt(0);
const lowerE = 'e'.charCodeAt(0);
const upperE = 'E'.charCodeAt(0);
const digit0 = '0'.charCodeAt(0);
const digit9 = '9'.charCodeAt(0);
const lowerU = 'u'.charCodeAt(0);
const upperU = 'U'.charCodeAt(0);
const atEnd = /[ \n\t\r\{\(\)'"\\;,/]/g;
const wordEnd = /[ \n\t\r\(\)\{\}\*:;@!&'"\+\|~>,\[\]\\]|\/(?=\*)/g;
const wordEndNum = /[ \n\t\r\(\)\{\}\*:;@!&'"\-\+\|~>,\[\]\\]|\//g;
const alphaNum = /^[a-z0-9]/i;
const unicodeRange = /^[a-f0-9?\-]/i;
const util = require('util');
const TokenizeError = require('./errors/TokenizeError');
module.exports = function tokenize(input, options) {
options = options || {};
const tokens = [];
const css = input.valueOf();
const length = css.length;
let offset = -1;
let line = 1;
let pos = 0;
let parentCount = 0;
let isURLArg = null;
let code;
let next;
let quote;
let lines;
let last;
let content;
let escape;
let nextLine;
let nextOffset;
let escaped;
let escapePos;
let nextChar;
function unclosed(what) {
const message = util.format(
'Unclosed %s at line: %d, column: %d, token: %d',
what,
line,
pos - offset,
pos
);
throw new TokenizeError(message);
}
function tokenizeError() {
const message = util.format(
'Syntax error at line: %d, column: %d, token: %d',
line,
pos - offset,
pos
);
throw new TokenizeError(message);
}
while (pos < length) {
code = css.charCodeAt(pos);
if (code === newline) {
offset = pos;
line += 1;
}
switch (code) {
case newline:
case space:
case tab:
case cr:
case feed:
next = pos;
do {
next += 1;
code = css.charCodeAt(next);
if (code === newline) {
offset = next;
line += 1;
}
} while (
code === space ||
code === newline ||
code === tab ||
code === cr ||
code === feed
);
tokens.push(['space', css.slice(pos, next), line, pos - offset, line, next - offset, pos]);
pos = next - 1;
break;
case colon:
next = pos + 1;
tokens.push(['colon', css.slice(pos, next), line, pos - offset, line, next - offset, pos]);
pos = next - 1;
break;
case comma:
next = pos + 1;
tokens.push(['comma', css.slice(pos, next), line, pos - offset, line, next - offset, pos]);
pos = next - 1;
break;
case openBracket:
tokens.push(['{', '{', line, pos - offset, line, next - offset, pos]);
break;
case closeBracket:
tokens.push(['}', '}', line, pos - offset, line, next - offset, pos]);
break;
case openParen:
parentCount++;
isURLArg =
!isURLArg &&
parentCount === 1 &&
tokens.length > 0 &&
tokens[tokens.length - 1][0] === 'word' &&
tokens[tokens.length - 1][1] === 'url';
tokens.push(['(', '(', line, pos - offset, line, next - offset, pos]);
break;
case closeParen:
parentCount--;
isURLArg = !isURLArg && parentCount === 1;
tokens.push([')', ')', line, pos - offset, line, next - offset, pos]);
break;
case singleQuote:
case doubleQuote:
quote = code === singleQuote ? "'" : '"';
next = pos;
do {
escaped = false;
next = css.indexOf(quote, next + 1);
if (next === -1) {
unclosed('quote', quote);
}
escapePos = next;
while (css.charCodeAt(escapePos - 1) === backslash) {
escapePos -= 1;
escaped = !escaped;
}
} while (escaped);
tokens.push([
'string',
css.slice(pos, next + 1),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next;
break;
case at:
atEnd.lastIndex = pos + 1;
atEnd.test(css);
if (atEnd.lastIndex === 0) {
next = css.length - 1;
} else {
next = atEnd.lastIndex - 2;
}
tokens.push([
'atword',
css.slice(pos, next + 1),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next;
break;
case backslash:
next = pos;
code = css.charCodeAt(next + 1);
if (
escape &&
(code !== slash &&
code !== space &&
code !== newline &&
code !== tab &&
code !== cr &&
code !== feed)
) {
next += 1;
}
tokens.push([
'word',
css.slice(pos, next + 1),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next;
break;
case plus:
case minus:
case asterisk:
next = pos + 1;
nextChar = css.slice(pos + 1, next + 1);
const prevChar = css.slice(pos - 1, pos);
// if the operator is immediately followed by a word character, then we
// have a prefix of some kind, and should fall-through. eg. -webkit
// look for --* for custom variables
if (code === minus && nextChar.charCodeAt(0) === minus) {
next++;
tokens.push(['word', css.slice(pos, next), line, pos - offset, line, next - offset, pos]);
pos = next - 1;
break;
}
tokens.push([
'operator',
css.slice(pos, next),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next - 1;
break;
default:
if (
code === slash &&
(css.charCodeAt(pos + 1) === asterisk ||
(options.loose && !isURLArg && css.charCodeAt(pos + 1) === slash))
) {
const isStandardComment = css.charCodeAt(pos + 1) === asterisk;
if (isStandardComment) {
next = css.indexOf('*/', pos + 2) + 1;
if (next === 0) {
unclosed('comment', '*/');
}
} else {
const newlinePos = css.indexOf('\n', pos + 2);
next = newlinePos !== -1 ? newlinePos - 1 : length;
}
content = css.slice(pos, next + 1);
lines = content.split('\n');
last = lines.length - 1;
if (last > 0) {
nextLine = line + last;
nextOffset = next - lines[last].length;
} else {
nextLine = line;
nextOffset = offset;
}
tokens.push(['comment', content, line, pos - offset, nextLine, next - nextOffset, pos]);
offset = nextOffset;
line = nextLine;
pos = next;
} else if (code === pound && !alphaNum.test(css.slice(pos + 1, pos + 2))) {
next = pos + 1;
tokens.push(['#', css.slice(pos, next), line, pos - offset, line, next - offset, pos]);
pos = next - 1;
} else if ((code === lowerU || code === upperU) && css.charCodeAt(pos + 1) === plus) {
next = pos + 2;
do {
next += 1;
code = css.charCodeAt(next);
} while (next < length && unicodeRange.test(css.slice(next, next + 1)));
tokens.push([
'unicoderange',
css.slice(pos, next),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next - 1;
}
// catch a regular slash, that isn't a comment
else if (code === slash) {
next = pos + 1;
tokens.push([
'operator',
css.slice(pos, next),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next - 1;
} else {
let regex = wordEnd;
// we're dealing with a word that starts with a number
// those get treated differently
if (code >= digit0 && code <= digit9) {
regex = wordEndNum;
}
regex.lastIndex = pos + 1;
regex.test(css);
if (regex.lastIndex === 0) {
next = css.length - 1;
} else {
next = regex.lastIndex - 2;
}
// Exponential number notation with minus or plus: 1e-10, 1e+10
if (regex === wordEndNum || code === period) {
const ncode = css.charCodeAt(next);
const ncode1 = css.charCodeAt(next + 1);
const ncode2 = css.charCodeAt(next + 2);
if (
(ncode === lowerE || ncode === upperE) &&
(ncode1 === minus || ncode1 === plus) &&
(ncode2 >= digit0 && ncode2 <= digit9)
) {
wordEndNum.lastIndex = next + 2;
wordEndNum.test(css);
if (wordEndNum.lastIndex === 0) {
next = css.length - 1;
} else {
next = wordEndNum.lastIndex - 2;
}
}
}
tokens.push([
'word',
css.slice(pos, next + 1),
line,
pos - offset,
line,
next - offset,
pos
]);
pos = next;
}
break;
}
pos++;
}
return tokens;
};

43
web/node_modules/postcss-values-parser/package.json generated vendored Normal file
View file

@ -0,0 +1,43 @@
{
"name": "postcss-values-parser",
"version": "2.0.1",
"description": "A CSS property value parser for use with PostCSS",
"main": "lib/index.js",
"repository": "lesshint/postcss-values-parser",
"author": {
"name": "Andrew Powell (shellscape)",
"email": "andrew@shellscape.org",
"url": "http://shellscape.org"
},
"license": "MIT",
"engines": {
"node": ">=6.14.4"
},
"scripts": {
"test": "gulp"
},
"keywords": [
"css",
"less",
"ast",
"postcss",
"value",
"values",
"parser",
"parsing",
"properties"
],
"dependencies": {
"flatten": "^1.0.2",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
},
"devDependencies": {
"chai": "^4.1.2",
"chai-shallow-deep-equal": "^1.4.0",
"eslint": "^5.6.0",
"gulp": "^3.9.1",
"gulp-eslint": "^5.0.0",
"gulp-mocha": "^6.0.0"
}
}