This commit is contained in:
21
node_modules/@emmetio/abbreviation/LICENSE
generated
vendored
21
node_modules/@emmetio/abbreviation/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Sergey Chikuyonok <serge.che@gmail.com>
|
||||
|
||||
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.
|
||||
76
node_modules/@emmetio/abbreviation/README.md
generated
vendored
76
node_modules/@emmetio/abbreviation/README.md
generated
vendored
@@ -1,76 +0,0 @@
|
||||
# Emmet markup abbreviation parser
|
||||
|
||||
Parses given Emmet *markup* abbreviation into AST. Parsing is performed in two steps: first it tokenizes given abbreviation (useful for syntax highlighting in editors) and then tokens are analyzed and converted into AST nodes as plain, JSON-serializable objects.
|
||||
|
||||
Note that AST tree in most cases cannot be used directly for output: for example, AST node produced from `.foo.bar` element misses element name and contains two `class` attributes with `foo` and `bar` values (not a single `class` with `foo bar` value).
|
||||
|
||||
## Usage
|
||||
|
||||
You can install it via npm:
|
||||
|
||||
```bash
|
||||
npm install @emmetio/abbreviation
|
||||
```
|
||||
|
||||
Then add it into your project:
|
||||
|
||||
```js
|
||||
import parse from '@emmetio/abbreviation';
|
||||
|
||||
const tree = parse('div#foo>span.bar*3');
|
||||
/* {
|
||||
type: 'Abbreviation',
|
||||
children: [{
|
||||
type: 'AbbreviationNode',
|
||||
name: 'div',
|
||||
attributes: [...],
|
||||
children: [...]
|
||||
}]
|
||||
} */
|
||||
|
||||
```
|
||||
The returned tree contains `AbbreviationNode` items: a node with name, attributes and/or text content. E.g. an element that can be represented somehow. Repeated and grouped nodes like `a>(b+c)*3` are automatically converted and duplicated as distinct `AbbreviationNode` with distinct `.repeat` property which identifies node in repeating sequence.
|
||||
|
||||
## Abbreviation syntax
|
||||
|
||||
Emmet abbreviation element has the following basic parts:
|
||||
|
||||
```
|
||||
name.class#id[attributes?, ...]{text value}*repeater/
|
||||
```
|
||||
|
||||
* `name` — element name, like `div`, `span` etc. Stored as `node.name` property.
|
||||
* `[attributes]` — list of attributes. Each attribute is stored as [`AbbreviationAttribute`](/src/types.ts) instance and can be accessed by `node.getAttribute(name)`. Each attribute can be written in different formats:
|
||||
* `attr` — attribute with empty value.
|
||||
* `attr=value` — attribute with value. The `value` may contain any character except space or `]`.
|
||||
* `attr="value"` or `attr='value'` — attribute with value in quotes. Quotes are automatically removed. Expression values like `attr={value}` are supported and can be identified by `valueType: "expression"` property.
|
||||
* `attr.` — boolean attribute, e.g. attribute without value, like `required` in `<input>`.
|
||||
* `!attr` – implicit attribute, will be outputted if its value is not empty. Used as a placeholder to preserve attribute order in output.
|
||||
* `./non/attr/value` — value for default attribute. In other words, anything that doesn’t match a attribute name characters. Can be a single- or double-quotted as well. Default attribute is stored with `null` as name and should be used later, for example, to resolve predefined attributes.
|
||||
* `.class` — shorthand for `class` attribute. Note that an element can have multiple classes, like `.class1.class2.class3`.
|
||||
* `#id` — shorthand for `id` attribute.
|
||||
* `{text}` — node’s text content
|
||||
* `*N` — element repeater, tells parser to create `N` copies of given node.
|
||||
* `/` — optional self-closing operator. Marks element with `node.selfClosing = true`.
|
||||
|
||||
### Operators
|
||||
|
||||
Each element of abbreviation must be separated with any of these operators:
|
||||
|
||||
```
|
||||
elem1+elem2>elem3
|
||||
```
|
||||
|
||||
* `+` — sibling operator, adds next element as a next sibling of current element in tree.
|
||||
* `>` — child operator, adds next element as a child of current element.
|
||||
* `^` — climb-up operator, adds next element as a child of current element’s parent node. Multiple climb-up operators are allowed, each operator moves one level up by tree.
|
||||
|
||||
### Groups
|
||||
|
||||
A set of elements could be grouped using `()`, mostly for repeating and for easier elements nesting:
|
||||
|
||||
```
|
||||
a>(b>c+d)*4+(e+f)
|
||||
```
|
||||
|
||||
Groups can be optionally concatenated with `+` operator.
|
||||
8
node_modules/@emmetio/abbreviation/dist/convert.d.ts
generated
vendored
8
node_modules/@emmetio/abbreviation/dist/convert.d.ts
generated
vendored
@@ -1,8 +0,0 @@
|
||||
import { TokenGroup } from './parser/index.js';
|
||||
import { Abbreviation, ParserOptions } from './types.js';
|
||||
/**
|
||||
* Converts given token-based abbreviation into simplified and unrolled node-based
|
||||
* abbreviation
|
||||
*/
|
||||
export default function convert(abbr: TokenGroup, options?: ParserOptions): Abbreviation;
|
||||
export declare function isGroup(node: any): node is TokenGroup;
|
||||
1245
node_modules/@emmetio/abbreviation/dist/index.cjs
generated
vendored
1245
node_modules/@emmetio/abbreviation/dist/index.cjs
generated
vendored
File diff suppressed because it is too large
Load Diff
1
node_modules/@emmetio/abbreviation/dist/index.cjs.map
generated
vendored
1
node_modules/@emmetio/abbreviation/dist/index.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
12
node_modules/@emmetio/abbreviation/dist/index.d.ts
generated
vendored
12
node_modules/@emmetio/abbreviation/dist/index.d.ts
generated
vendored
@@ -1,12 +0,0 @@
|
||||
import parse, { type TokenGroup } from './parser/index.js';
|
||||
import tokenize, { getToken, type AllTokens } from './tokenizer/index.js';
|
||||
import convert from './convert.js';
|
||||
import type { ParserOptions } from './types.js';
|
||||
export { parse, tokenize, getToken, convert };
|
||||
export * from './tokenizer/tokens.js';
|
||||
export * from './types.js';
|
||||
export type MarkupAbbreviation = TokenGroup;
|
||||
/**
|
||||
* Parses given abbreviation into node tree
|
||||
*/
|
||||
export default function parseAbbreviation(abbr: string | AllTokens[], options?: ParserOptions): import("./types.js").Abbreviation;
|
||||
1237
node_modules/@emmetio/abbreviation/dist/index.js
generated
vendored
1237
node_modules/@emmetio/abbreviation/dist/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1
node_modules/@emmetio/abbreviation/dist/index.js.map
generated
vendored
1
node_modules/@emmetio/abbreviation/dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
17
node_modules/@emmetio/abbreviation/dist/parser/TokenScanner.d.ts
generated
vendored
17
node_modules/@emmetio/abbreviation/dist/parser/TokenScanner.d.ts
generated
vendored
@@ -1,17 +0,0 @@
|
||||
import { AllTokens } from '../tokenizer/index.js';
|
||||
export interface TokenScanner {
|
||||
tokens: AllTokens[];
|
||||
start: number;
|
||||
pos: number;
|
||||
size: number;
|
||||
}
|
||||
type TestFn = (token?: AllTokens) => boolean;
|
||||
export default function tokenScanner(tokens: AllTokens[]): TokenScanner;
|
||||
export declare function peek(scanner: TokenScanner): AllTokens | undefined;
|
||||
export declare function next(scanner: TokenScanner): AllTokens | undefined;
|
||||
export declare function slice(scanner: TokenScanner, from?: number, to?: number): AllTokens[];
|
||||
export declare function readable(scanner: TokenScanner): boolean;
|
||||
export declare function consume(scanner: TokenScanner, test: TestFn): boolean;
|
||||
export declare function error(scanner: TokenScanner, message: string, token?: AllTokens | undefined): Error;
|
||||
export declare function consumeWhile(scanner: TokenScanner, test: TestFn): boolean;
|
||||
export {};
|
||||
31
node_modules/@emmetio/abbreviation/dist/parser/index.d.ts
generated
vendored
31
node_modules/@emmetio/abbreviation/dist/parser/index.d.ts
generated
vendored
@@ -1,31 +0,0 @@
|
||||
import type { NameToken, ValueToken, Repeater, AllTokens, BracketType, Bracket, Operator, OperatorType, Quote } from '../tokenizer/index.js';
|
||||
import type { ParserOptions } from '../types.js';
|
||||
export type TokenStatement = TokenElement | TokenGroup;
|
||||
export interface TokenAttribute {
|
||||
name?: ValueToken[];
|
||||
value?: ValueToken[];
|
||||
expression?: boolean;
|
||||
/**
|
||||
* Indicates that current attribute was repeated multiple times in a row.
|
||||
* Used to alter output of multiple shorthand attributes like `..` (double class)
|
||||
*/
|
||||
multiple?: boolean;
|
||||
}
|
||||
export interface TokenElement {
|
||||
type: 'TokenElement';
|
||||
name?: NameToken[];
|
||||
attributes?: TokenAttribute[];
|
||||
value?: ValueToken[];
|
||||
repeat?: Repeater;
|
||||
selfClose: boolean;
|
||||
elements: TokenStatement[];
|
||||
}
|
||||
export interface TokenGroup {
|
||||
type: 'TokenGroup';
|
||||
elements: TokenStatement[];
|
||||
repeat?: Repeater;
|
||||
}
|
||||
export default function abbreviation(abbr: AllTokens[], options?: ParserOptions): TokenGroup;
|
||||
export declare function isBracket(token: AllTokens | undefined, context?: BracketType, isOpen?: boolean): token is Bracket;
|
||||
export declare function isOperator(token: AllTokens | undefined, type?: OperatorType): token is Operator;
|
||||
export declare function isQuote(token: AllTokens | undefined, isSingle?: boolean): token is Quote;
|
||||
6
node_modules/@emmetio/abbreviation/dist/stringify.d.ts
generated
vendored
6
node_modules/@emmetio/abbreviation/dist/stringify.d.ts
generated
vendored
@@ -1,6 +0,0 @@
|
||||
import { ValueToken } from './tokenizer/tokens.js';
|
||||
import { ConvertState } from './types.js';
|
||||
/**
|
||||
* Converts given value token to string
|
||||
*/
|
||||
export default function stringify(token: ValueToken, state: ConvertState): string;
|
||||
13
node_modules/@emmetio/abbreviation/dist/tokenizer/index.d.ts
generated
vendored
13
node_modules/@emmetio/abbreviation/dist/tokenizer/index.d.ts
generated
vendored
@@ -1,13 +0,0 @@
|
||||
import Scanner from '@emmetio/scanner';
|
||||
import type { BracketType, AllTokens } from './tokens.js';
|
||||
export * from './tokens.js';
|
||||
type Context = {
|
||||
[ctx in BracketType]: number;
|
||||
} & {
|
||||
quote: number;
|
||||
};
|
||||
export default function tokenize(source: string): AllTokens[];
|
||||
/**
|
||||
* Returns next token from given scanner, if possible
|
||||
*/
|
||||
export declare function getToken(scanner: Scanner, ctx: Context): AllTokens | undefined;
|
||||
63
node_modules/@emmetio/abbreviation/dist/tokenizer/tokens.d.ts
generated
vendored
63
node_modules/@emmetio/abbreviation/dist/tokenizer/tokens.d.ts
generated
vendored
@@ -1,63 +0,0 @@
|
||||
export type OperatorType = 'child' | 'sibling' | 'climb' | 'class' | 'id' | 'close' | 'equal';
|
||||
export type BracketType = 'group' | 'attribute' | 'expression';
|
||||
export type AllTokens = Bracket | Field | Literal | Operator | Quote | Repeater | RepeaterNumber | RepeaterPlaceholder | WhiteSpace;
|
||||
export type NameToken = Literal | RepeaterNumber;
|
||||
export type ValueToken = Literal | Quote | Bracket | Field | RepeaterPlaceholder | RepeaterNumber;
|
||||
export interface Token {
|
||||
type: string;
|
||||
/** Location of token start in source */
|
||||
start?: number;
|
||||
/** Location of token end in source */
|
||||
end?: number;
|
||||
}
|
||||
export interface Repeater extends Token {
|
||||
type: 'Repeater';
|
||||
/** How many times context element should be repeated */
|
||||
count: number;
|
||||
/** Position of context element in its repeating sequence */
|
||||
value: number;
|
||||
/** Repeater is implicit, e.g. repeated by the amount of text lines selected by user */
|
||||
implicit: boolean;
|
||||
}
|
||||
export interface RepeaterNumber extends Token {
|
||||
type: 'RepeaterNumber';
|
||||
/** Size of repeater content, e.g. the amount consequent numbering characters */
|
||||
size: number;
|
||||
/** Should output numbering in reverse order? */
|
||||
reverse: boolean;
|
||||
/** Base value to start numbering from */
|
||||
base: number;
|
||||
/** Parent offset from which numbering should be used */
|
||||
parent: number;
|
||||
}
|
||||
export interface RepeaterPlaceholder extends Token {
|
||||
type: 'RepeaterPlaceholder';
|
||||
/** Value to insert instead of placeholder */
|
||||
value?: string;
|
||||
}
|
||||
export interface Field extends Token {
|
||||
type: 'Field';
|
||||
index?: number;
|
||||
name: string;
|
||||
}
|
||||
export interface Operator extends Token {
|
||||
type: 'Operator';
|
||||
operator: OperatorType;
|
||||
}
|
||||
export interface Bracket extends Token {
|
||||
type: 'Bracket';
|
||||
open: boolean;
|
||||
context: BracketType;
|
||||
}
|
||||
export interface Quote extends Token {
|
||||
type: 'Quote';
|
||||
single: boolean;
|
||||
}
|
||||
export interface Literal extends Token {
|
||||
type: 'Literal';
|
||||
value: string;
|
||||
}
|
||||
export interface WhiteSpace extends Token {
|
||||
type: 'WhiteSpace';
|
||||
value: string;
|
||||
}
|
||||
53
node_modules/@emmetio/abbreviation/dist/tokenizer/utils.d.ts
generated
vendored
53
node_modules/@emmetio/abbreviation/dist/tokenizer/utils.d.ts
generated
vendored
@@ -1,53 +0,0 @@
|
||||
import type Scanner from '@emmetio/scanner';
|
||||
export declare const enum Chars {
|
||||
/** `{` character */
|
||||
CurlyBracketOpen = 123,
|
||||
/** `}` character */
|
||||
CurlyBracketClose = 125,
|
||||
/** `\\` character */
|
||||
Escape = 92,
|
||||
/** `=` character */
|
||||
Equals = 61,
|
||||
/** `[` character */
|
||||
SquareBracketOpen = 91,
|
||||
/** `]` character */
|
||||
SquareBracketClose = 93,
|
||||
/** `*` character */
|
||||
Asterisk = 42,
|
||||
/** `#` character */
|
||||
Hash = 35,
|
||||
/** `$` character */
|
||||
Dollar = 36,
|
||||
/** `-` character */
|
||||
Dash = 45,
|
||||
/** `.` character */
|
||||
Dot = 46,
|
||||
/** `/` character */
|
||||
Slash = 47,
|
||||
/** `:` character */
|
||||
Colon = 58,
|
||||
/** `!` character */
|
||||
Excl = 33,
|
||||
/** `@` character */
|
||||
At = 64,
|
||||
/** `_` character */
|
||||
Underscore = 95,
|
||||
/** `(` character */
|
||||
RoundBracketOpen = 40,
|
||||
/** `)` character */
|
||||
RoundBracketClose = 41,
|
||||
/** `+` character */
|
||||
Sibling = 43,
|
||||
/** `>` character */
|
||||
Child = 62,
|
||||
/** `^` character */
|
||||
Climb = 94,
|
||||
/** `'` character */
|
||||
SingleQuote = 39,
|
||||
/** `""` character */
|
||||
DoubleQuote = 34
|
||||
}
|
||||
/**
|
||||
* If consumes escape character, sets current stream range to escaped value
|
||||
*/
|
||||
export declare function escaped(scanner: Scanner): boolean;
|
||||
56
node_modules/@emmetio/abbreviation/dist/types.d.ts
generated
vendored
56
node_modules/@emmetio/abbreviation/dist/types.d.ts
generated
vendored
@@ -1,56 +0,0 @@
|
||||
import { Field, Repeater } from './tokenizer/index.js';
|
||||
export interface ParserOptions {
|
||||
/** Text strings to insert into implicitly repeated elements */
|
||||
text?: string | string[];
|
||||
/** Variable values for `${var}` tokens */
|
||||
variables?: {
|
||||
[name: string]: string;
|
||||
};
|
||||
/** Max amount of repeated elements in abbreviation */
|
||||
maxRepeat?: number;
|
||||
/** Enabled JSX parsing mode */
|
||||
jsx?: boolean;
|
||||
/** Enable inserting text into href attribute of links */
|
||||
href?: boolean;
|
||||
}
|
||||
export interface ConvertState {
|
||||
inserted: boolean;
|
||||
text?: string | string[];
|
||||
cleanText?: string | string[];
|
||||
repeatGuard: number;
|
||||
/** Context repeaters, e.g. all actual repeaters from parent */
|
||||
repeaters: Repeater[];
|
||||
getText(pos?: number): string;
|
||||
getVariable(name: string): string;
|
||||
}
|
||||
export type Value = string | Field;
|
||||
export type AttributeType = 'raw' | 'singleQuote' | 'doubleQuote' | 'expression';
|
||||
export interface Abbreviation {
|
||||
type: 'Abbreviation';
|
||||
children: AbbreviationNode[];
|
||||
}
|
||||
export interface AbbreviationNode {
|
||||
type: 'AbbreviationNode';
|
||||
name?: string;
|
||||
value?: Value[];
|
||||
repeat?: Repeater;
|
||||
attributes?: AbbreviationAttribute[];
|
||||
children: AbbreviationNode[];
|
||||
/** Indicates current element is self-closing, e.g. should not contain closing pair */
|
||||
selfClosing?: boolean;
|
||||
}
|
||||
export interface AbbreviationAttribute {
|
||||
name?: string;
|
||||
value?: Value[];
|
||||
/** Indicates type of value stored in `.value` property */
|
||||
valueType: AttributeType;
|
||||
/** Attribute is boolean (e.g.name equals value) */
|
||||
boolean?: boolean;
|
||||
/** Attribute is implied (e.g.must be outputted only if contains non-null value) */
|
||||
implied?: boolean;
|
||||
/**
|
||||
* Internal property that indicates that given attribute was specified
|
||||
* more than once as a shorthand. E.g. `..` is a multiple `class` attribute
|
||||
*/
|
||||
multiple?: boolean;
|
||||
}
|
||||
54
node_modules/@emmetio/abbreviation/package.json
generated
vendored
54
node_modules/@emmetio/abbreviation/package.json
generated
vendored
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/abbreviation",
|
||||
"version": "2.3.3",
|
||||
"description": "Emmet standalone abbreviation parser",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"clean": "rimraf ./dist",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"keywords": [
|
||||
"emmet",
|
||||
"abbreviation"
|
||||
],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emmetio/scanner": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^10.0.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.11.18",
|
||||
"mocha": "^10.2.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"rollup": "^3.9.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/emmet.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/emmet/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/emmet#readme",
|
||||
"mocha": {
|
||||
"loader": "ts-node/esm",
|
||||
"spec": "./test/*.ts"
|
||||
},
|
||||
"gitHead": "fce2127ece65adbb293a40aa0577e4558658c559"
|
||||
}
|
||||
21
node_modules/@emmetio/css-abbreviation/LICENSE
generated
vendored
21
node_modules/@emmetio/css-abbreviation/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Sergey Chikuyonok <serge.che@gmail.com>
|
||||
|
||||
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.
|
||||
39
node_modules/@emmetio/css-abbreviation/README.md
generated
vendored
39
node_modules/@emmetio/css-abbreviation/README.md
generated
vendored
@@ -1,39 +0,0 @@
|
||||
# Emmet stylesheet abbreviation parser
|
||||
|
||||
Parses given Emmet *stylesheet* abbreviation into AST. Parsing is performed in two steps: first it tokenizes given abbreviation (useful for syntax highlighting in editors) and then tokens are analyzed and converted into AST nodes as plain, JSON-serializable objects.
|
||||
|
||||
Unlike in [markup abbreviations](/packages/abbreviation), elements in stylesheet abbreviations cannot be nested and contain attributes, but allow embedded values in element names.
|
||||
|
||||
## Usage
|
||||
|
||||
You can install it via npm:
|
||||
|
||||
```bash
|
||||
npm install @emmetio/css-abbreviation
|
||||
```
|
||||
|
||||
Then add it into your project:
|
||||
|
||||
```js
|
||||
import parse from '@emmetio/css-abbreviation';
|
||||
|
||||
const props = parse('p10+poa');
|
||||
/* [{
|
||||
name: 'p',
|
||||
value: [{ type: 'CSSValue', value: [...] }],
|
||||
important: false
|
||||
}, {
|
||||
name: 'poa',
|
||||
value: [],
|
||||
important: false
|
||||
}] */
|
||||
```
|
||||
The returned result is an array of `CSSProperty` items: a node with name and values.
|
||||
|
||||
## Abbreviation syntax
|
||||
|
||||
Emmet stylesheet abbreviation element may start with name and followed by values, optionally chained with `-` delimiter. In most cases, actual CSS properties doesn’t have numbers in their names (or at least they are not used in abbreviation shortcuts) so a number right after alpha characters is considered as *embedded value*, as well as colors starting with `#` character: `p10`, `bg#fc0` etc. If implicit name/value boundary can’t be identified, you should use `-` as value separator: `m-a`, `p10-20` etc.
|
||||
|
||||
### Operators
|
||||
|
||||
Since CSS properties can’t be nested, the only available operator is `+`.
|
||||
687
node_modules/@emmetio/css-abbreviation/dist/index.cjs
generated
vendored
687
node_modules/@emmetio/css-abbreviation/dist/index.cjs
generated
vendored
@@ -1,687 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var Scanner = require('@emmetio/scanner');
|
||||
|
||||
exports.OperatorType = void 0;
|
||||
(function (OperatorType) {
|
||||
OperatorType["Sibling"] = "+";
|
||||
OperatorType["Important"] = "!";
|
||||
OperatorType["ArgumentDelimiter"] = ",";
|
||||
OperatorType["ValueDelimiter"] = "-";
|
||||
OperatorType["PropertyDelimiter"] = ":";
|
||||
})(exports.OperatorType || (exports.OperatorType = {}));
|
||||
|
||||
var Chars;
|
||||
(function (Chars) {
|
||||
/** `#` character */
|
||||
Chars[Chars["Hash"] = 35] = "Hash";
|
||||
/** `$` character */
|
||||
Chars[Chars["Dollar"] = 36] = "Dollar";
|
||||
/** `-` character */
|
||||
Chars[Chars["Dash"] = 45] = "Dash";
|
||||
/** `.` character */
|
||||
Chars[Chars["Dot"] = 46] = "Dot";
|
||||
/** `:` character */
|
||||
Chars[Chars["Colon"] = 58] = "Colon";
|
||||
/** `,` character */
|
||||
Chars[Chars["Comma"] = 44] = "Comma";
|
||||
/** `!` character */
|
||||
Chars[Chars["Excl"] = 33] = "Excl";
|
||||
/** `@` character */
|
||||
Chars[Chars["At"] = 64] = "At";
|
||||
/** `%` character */
|
||||
Chars[Chars["Percent"] = 37] = "Percent";
|
||||
/** `_` character */
|
||||
Chars[Chars["Underscore"] = 95] = "Underscore";
|
||||
/** `(` character */
|
||||
Chars[Chars["RoundBracketOpen"] = 40] = "RoundBracketOpen";
|
||||
/** `)` character */
|
||||
Chars[Chars["RoundBracketClose"] = 41] = "RoundBracketClose";
|
||||
/** `{` character */
|
||||
Chars[Chars["CurlyBracketOpen"] = 123] = "CurlyBracketOpen";
|
||||
/** `}` character */
|
||||
Chars[Chars["CurlyBracketClose"] = 125] = "CurlyBracketClose";
|
||||
/** `+` character */
|
||||
Chars[Chars["Sibling"] = 43] = "Sibling";
|
||||
/** `'` character */
|
||||
Chars[Chars["SingleQuote"] = 39] = "SingleQuote";
|
||||
/** `"` character */
|
||||
Chars[Chars["DoubleQuote"] = 34] = "DoubleQuote";
|
||||
/** `t` character */
|
||||
Chars[Chars["Transparent"] = 116] = "Transparent";
|
||||
/** `/` character */
|
||||
Chars[Chars["Slash"] = 47] = "Slash";
|
||||
})(Chars || (Chars = {}));
|
||||
|
||||
function tokenize(abbr, isValue) {
|
||||
let brackets = 0;
|
||||
let token;
|
||||
const scanner = new Scanner(abbr);
|
||||
const tokens = [];
|
||||
while (!scanner.eof()) {
|
||||
token = getToken(scanner, brackets === 0 && !isValue);
|
||||
if (!token) {
|
||||
throw scanner.error('Unexpected character');
|
||||
}
|
||||
if (token.type === 'Bracket') {
|
||||
if (!brackets && token.open) {
|
||||
mergeTokens(scanner, tokens);
|
||||
}
|
||||
brackets += token.open ? 1 : -1;
|
||||
if (brackets < 0) {
|
||||
throw scanner.error('Unexpected bracket', token.start);
|
||||
}
|
||||
}
|
||||
tokens.push(token);
|
||||
// Forcibly consume next operator after unit-less numeric value or color:
|
||||
// next dash `-` must be used as value delimiter
|
||||
if (shouldConsumeDashAfter(token) && (token = operator(scanner))) {
|
||||
tokens.push(token);
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
/**
|
||||
* Returns next token from given scanner, if possible
|
||||
*/
|
||||
function getToken(scanner, short) {
|
||||
return field(scanner)
|
||||
|| customProperty(scanner)
|
||||
|| numberValue(scanner)
|
||||
|| colorValue(scanner)
|
||||
|| stringValue(scanner)
|
||||
|| bracket(scanner)
|
||||
|| operator(scanner)
|
||||
|| whiteSpace(scanner)
|
||||
|| literal(scanner, short);
|
||||
}
|
||||
function field(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dollar) && scanner.eat(Chars.CurlyBracketOpen)) {
|
||||
scanner.start = scanner.pos;
|
||||
let index;
|
||||
let name = '';
|
||||
if (scanner.eatWhile(Scanner.isNumber)) {
|
||||
// It’s a field
|
||||
index = Number(scanner.current());
|
||||
name = scanner.eat(Chars.Colon) ? consumePlaceholder(scanner) : '';
|
||||
}
|
||||
else if (Scanner.isAlpha(scanner.peek())) {
|
||||
// It’s a variable
|
||||
name = consumePlaceholder(scanner);
|
||||
}
|
||||
if (scanner.eat(Chars.CurlyBracketClose)) {
|
||||
return {
|
||||
type: 'Field',
|
||||
index, name,
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
throw scanner.error('Expecting }');
|
||||
}
|
||||
// If we reached here then there’s no valid field here, revert
|
||||
// back to starting position
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes a placeholder: value right after `:` in field. Could be empty
|
||||
*/
|
||||
function consumePlaceholder(stream) {
|
||||
const stack = [];
|
||||
stream.start = stream.pos;
|
||||
while (!stream.eof()) {
|
||||
if (stream.eat(Chars.CurlyBracketOpen)) {
|
||||
stack.push(stream.pos);
|
||||
}
|
||||
else if (stream.eat(Chars.CurlyBracketClose)) {
|
||||
if (!stack.length) {
|
||||
stream.pos--;
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
else {
|
||||
stream.pos++;
|
||||
}
|
||||
}
|
||||
if (stack.length) {
|
||||
stream.pos = stack.pop();
|
||||
throw stream.error(`Expecting }`);
|
||||
}
|
||||
return stream.current();
|
||||
}
|
||||
/**
|
||||
* Consumes literal from given scanner
|
||||
* @param short Use short notation for consuming value.
|
||||
* The difference between “short” and “full” notation is that first one uses
|
||||
* alpha characters only and used for extracting keywords from abbreviation,
|
||||
* while “full” notation also supports numbers and dashes
|
||||
*/
|
||||
function literal(scanner, short) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(isIdentPrefix)) {
|
||||
// SCSS or LESS variable
|
||||
// NB a bit dirty hack: if abbreviation starts with identifier prefix,
|
||||
// consume alpha characters only to allow embedded variables
|
||||
scanner.eatWhile(start ? isKeyword : isLiteral$1);
|
||||
}
|
||||
else if (scanner.eat(Scanner.isAlphaWord)) {
|
||||
scanner.eatWhile(short ? isLiteral$1 : isKeyword);
|
||||
}
|
||||
else {
|
||||
// Allow dots only at the beginning of literal
|
||||
scanner.eat(Chars.Dot);
|
||||
scanner.eatWhile(isLiteral$1);
|
||||
}
|
||||
if (start !== scanner.pos) {
|
||||
scanner.start = start;
|
||||
return createLiteral(scanner, scanner.start = start);
|
||||
}
|
||||
}
|
||||
function createLiteral(scanner, start = scanner.start, end = scanner.pos) {
|
||||
return {
|
||||
type: 'Literal',
|
||||
value: scanner.substring(start, end),
|
||||
start,
|
||||
end
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Consumes numeric CSS value (number with optional unit) from current stream,
|
||||
* if possible
|
||||
*/
|
||||
function numberValue(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeNumber(scanner)) {
|
||||
scanner.start = start;
|
||||
const rawValue = scanner.current();
|
||||
// eat unit, which can be a % or alpha word
|
||||
scanner.start = scanner.pos;
|
||||
scanner.eat(Chars.Percent) || scanner.eatWhile(Scanner.isAlphaWord);
|
||||
return {
|
||||
type: 'NumberValue',
|
||||
value: Number(rawValue),
|
||||
rawValue,
|
||||
unit: scanner.current(),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes quoted string value from given scanner
|
||||
*/
|
||||
function stringValue(scanner) {
|
||||
const ch = scanner.peek();
|
||||
const start = scanner.pos;
|
||||
let finished = false;
|
||||
if (Scanner.isQuote(ch)) {
|
||||
scanner.pos++;
|
||||
while (!scanner.eof()) {
|
||||
// Do not throw error on malformed string
|
||||
if (scanner.eat(ch)) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return {
|
||||
type: 'StringValue',
|
||||
value: scanner.substring(start + 1, scanner.pos - (finished ? 1 : 0)),
|
||||
quote: ch === Chars.SingleQuote ? 'single' : 'double',
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes a color token from given string
|
||||
*/
|
||||
function colorValue(scanner) {
|
||||
// supported color variations:
|
||||
// #abc → #aabbccc
|
||||
// #0 → #000000
|
||||
// #fff.5 → rgba(255, 255, 255, 0.5)
|
||||
// #t → transparent
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Hash)) {
|
||||
const valueStart = scanner.pos;
|
||||
let color = '';
|
||||
let alpha = '';
|
||||
if (scanner.eatWhile(isHex)) {
|
||||
color = scanner.substring(valueStart, scanner.pos);
|
||||
alpha = colorAlpha(scanner);
|
||||
}
|
||||
else if (scanner.eat(Chars.Transparent)) {
|
||||
color = '0';
|
||||
alpha = colorAlpha(scanner) || '0';
|
||||
}
|
||||
else {
|
||||
alpha = colorAlpha(scanner);
|
||||
}
|
||||
if (color || alpha || scanner.eof()) {
|
||||
const { r, g, b, a } = parseColor(color, alpha);
|
||||
return {
|
||||
type: 'ColorValue',
|
||||
r, g, b, a,
|
||||
raw: scanner.substring(start + 1, scanner.pos),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Consumed # but no actual value: invalid color value, treat it as literal
|
||||
return createLiteral(scanner, start);
|
||||
}
|
||||
}
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes alpha value of color: `.1`
|
||||
*/
|
||||
function colorAlpha(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dot)) {
|
||||
scanner.start = start;
|
||||
if (scanner.eatWhile(Scanner.isNumber)) {
|
||||
return scanner.current();
|
||||
}
|
||||
return '1';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Consumes white space characters as string literal from given scanner
|
||||
*/
|
||||
function whiteSpace(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eatWhile(Scanner.isSpace)) {
|
||||
return {
|
||||
type: 'WhiteSpace',
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes custom CSS property: --foo-bar
|
||||
*/
|
||||
function customProperty(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dash) && scanner.eat(Chars.Dash)) {
|
||||
scanner.start = start;
|
||||
scanner.eatWhile(isKeyword);
|
||||
return {
|
||||
type: 'CustomProperty',
|
||||
value: scanner.current(),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes bracket from given scanner
|
||||
*/
|
||||
function bracket(scanner) {
|
||||
const ch = scanner.peek();
|
||||
if (isBracket$1(ch)) {
|
||||
return {
|
||||
type: 'Bracket',
|
||||
open: ch === Chars.RoundBracketOpen,
|
||||
start: scanner.pos++,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes operator from given scanner
|
||||
*/
|
||||
function operator(scanner) {
|
||||
const op = operatorType(scanner.peek());
|
||||
if (op) {
|
||||
return {
|
||||
type: 'Operator',
|
||||
operator: op,
|
||||
start: scanner.pos++,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Eats number value from given stream
|
||||
* @return Returns `true` if number was consumed
|
||||
*/
|
||||
function consumeNumber(stream) {
|
||||
const start = stream.pos;
|
||||
stream.eat(Chars.Dash);
|
||||
const afterNegative = stream.pos;
|
||||
const hasDecimal = stream.eatWhile(Scanner.isNumber);
|
||||
const prevPos = stream.pos;
|
||||
if (stream.eat(Chars.Dot)) {
|
||||
// It’s perfectly valid to have numbers like `1.`, which enforces
|
||||
// value to float unit type
|
||||
const hasFloat = stream.eatWhile(Scanner.isNumber);
|
||||
if (!hasDecimal && !hasFloat) {
|
||||
// Lone dot
|
||||
stream.pos = prevPos;
|
||||
}
|
||||
}
|
||||
// Edge case: consumed dash only: not a number, bail-out
|
||||
if (stream.pos === afterNegative) {
|
||||
stream.pos = start;
|
||||
}
|
||||
return stream.pos !== start;
|
||||
}
|
||||
function isIdentPrefix(code) {
|
||||
return code === Chars.At || code === Chars.Dollar;
|
||||
}
|
||||
/**
|
||||
* If given character is an operator, returns it’s type
|
||||
*/
|
||||
function operatorType(ch) {
|
||||
return (ch === Chars.Sibling && exports.OperatorType.Sibling)
|
||||
|| (ch === Chars.Excl && exports.OperatorType.Important)
|
||||
|| (ch === Chars.Comma && exports.OperatorType.ArgumentDelimiter)
|
||||
|| (ch === Chars.Colon && exports.OperatorType.PropertyDelimiter)
|
||||
|| (ch === Chars.Dash && exports.OperatorType.ValueDelimiter)
|
||||
|| void 0;
|
||||
}
|
||||
/**
|
||||
* Check if given code is a hex value (/0-9a-f/)
|
||||
*/
|
||||
function isHex(code) {
|
||||
return Scanner.isNumber(code) || Scanner.isAlpha(code, 65, 70); // A-F
|
||||
}
|
||||
function isKeyword(code) {
|
||||
return Scanner.isAlphaNumericWord(code) || code === Chars.Dash;
|
||||
}
|
||||
function isBracket$1(code) {
|
||||
return code === Chars.RoundBracketOpen || code === Chars.RoundBracketClose;
|
||||
}
|
||||
function isLiteral$1(code) {
|
||||
return Scanner.isAlphaWord(code) || code === Chars.Percent || code === Chars.Slash;
|
||||
}
|
||||
/**
|
||||
* Parses given color value from abbreviation into RGBA format
|
||||
*/
|
||||
function parseColor(value, alpha) {
|
||||
let r = '0';
|
||||
let g = '0';
|
||||
let b = '0';
|
||||
let a = Number(alpha != null && alpha !== '' ? alpha : 1);
|
||||
if (value === 't') {
|
||||
a = 0;
|
||||
}
|
||||
else {
|
||||
switch (value.length) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
r = g = b = value + value;
|
||||
break;
|
||||
case 2:
|
||||
r = g = b = value;
|
||||
break;
|
||||
case 3:
|
||||
r = value[0] + value[0];
|
||||
g = value[1] + value[1];
|
||||
b = value[2] + value[2];
|
||||
break;
|
||||
default:
|
||||
value += value;
|
||||
r = value.slice(0, 2);
|
||||
g = value.slice(2, 4);
|
||||
b = value.slice(4, 6);
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: parseInt(r, 16),
|
||||
g: parseInt(g, 16),
|
||||
b: parseInt(b, 16),
|
||||
a
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Check if scanner reader must consume dash after given token.
|
||||
* Used in cases where user must explicitly separate numeric values
|
||||
*/
|
||||
function shouldConsumeDashAfter(token) {
|
||||
return token.type === 'ColorValue' || (token.type === 'NumberValue' && !token.unit);
|
||||
}
|
||||
/**
|
||||
* Merges last adjacent tokens into a single literal.
|
||||
* This function is used to overcome edge case when function name was parsed
|
||||
* as a list of separate tokens. For example, a `scale3d()` value will be
|
||||
* parsed as literal and number tokens (`scale` and `3d`) which is a perfectly
|
||||
* valid abbreviation but undesired result. This function will detect last adjacent
|
||||
* literal and number values and combine them into single literal
|
||||
*/
|
||||
function mergeTokens(scanner, tokens) {
|
||||
let start = 0;
|
||||
let end = 0;
|
||||
while (tokens.length) {
|
||||
const token = last(tokens);
|
||||
if (token.type === 'Literal' || token.type === 'NumberValue') {
|
||||
start = token.start;
|
||||
if (!end) {
|
||||
end = token.end;
|
||||
}
|
||||
tokens.pop();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start !== end) {
|
||||
tokens.push(createLiteral(scanner, start, end));
|
||||
}
|
||||
}
|
||||
function last(arr) {
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
|
||||
function tokenScanner(tokens) {
|
||||
return {
|
||||
tokens,
|
||||
start: 0,
|
||||
pos: 0,
|
||||
size: tokens.length
|
||||
};
|
||||
}
|
||||
function peek(scanner) {
|
||||
return scanner.tokens[scanner.pos];
|
||||
}
|
||||
function readable(scanner) {
|
||||
return scanner.pos < scanner.size;
|
||||
}
|
||||
function consume(scanner, test) {
|
||||
if (test(peek(scanner))) {
|
||||
scanner.pos++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function error(scanner, message, token = peek(scanner)) {
|
||||
if (token && token.start != null) {
|
||||
message += ` at ${token.start}`;
|
||||
}
|
||||
const err = new Error(message);
|
||||
err['pos'] = token && token.start;
|
||||
return err;
|
||||
}
|
||||
|
||||
function parser(tokens, options = {}) {
|
||||
const scanner = tokenScanner(tokens);
|
||||
const result = [];
|
||||
let property;
|
||||
while (readable(scanner)) {
|
||||
if (property = consumeProperty(scanner, options)) {
|
||||
result.push(property);
|
||||
}
|
||||
else if (!consume(scanner, isSiblingOperator)) {
|
||||
throw error(scanner, 'Unexpected token');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Consumes single CSS property
|
||||
*/
|
||||
function consumeProperty(scanner, options) {
|
||||
let name;
|
||||
let important = false;
|
||||
let valueFragment;
|
||||
const value = [];
|
||||
const token = peek(scanner);
|
||||
const valueMode = !!options.value;
|
||||
if (!valueMode && isLiteral(token) && !isFunctionStart(scanner)) {
|
||||
scanner.pos++;
|
||||
name = token.value;
|
||||
// Consume any following value delimiter after property name
|
||||
consume(scanner, isValueDelimiter);
|
||||
}
|
||||
// Skip whitespace right after property name, if any
|
||||
if (valueMode) {
|
||||
consume(scanner, isWhiteSpace);
|
||||
}
|
||||
while (readable(scanner)) {
|
||||
if (consume(scanner, isImportant)) {
|
||||
important = true;
|
||||
}
|
||||
else if (valueFragment = consumeValue(scanner, valueMode)) {
|
||||
value.push(valueFragment);
|
||||
}
|
||||
else if (!consume(scanner, isFragmentDelimiter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name || value.length || important) {
|
||||
return { name, value, important };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes single value fragment, e.g. all value tokens before comma
|
||||
*/
|
||||
function consumeValue(scanner, inArgument) {
|
||||
const result = [];
|
||||
let token;
|
||||
let args;
|
||||
while (readable(scanner)) {
|
||||
token = peek(scanner);
|
||||
if (isValue(token)) {
|
||||
scanner.pos++;
|
||||
if (isLiteral(token) && (args = consumeArguments(scanner))) {
|
||||
result.push({
|
||||
type: 'FunctionCall',
|
||||
name: token.value,
|
||||
arguments: args
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.push(token);
|
||||
}
|
||||
}
|
||||
else if (isValueDelimiter(token) || (inArgument && isWhiteSpace(token))) {
|
||||
scanner.pos++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result.length
|
||||
? { type: 'CSSValue', value: result }
|
||||
: void 0;
|
||||
}
|
||||
function consumeArguments(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consume(scanner, isOpenBracket)) {
|
||||
const args = [];
|
||||
let value;
|
||||
while (readable(scanner) && !consume(scanner, isCloseBracket)) {
|
||||
if (value = consumeValue(scanner, true)) {
|
||||
args.push(value);
|
||||
}
|
||||
else if (!consume(scanner, isWhiteSpace) && !consume(scanner, isArgumentDelimiter)) {
|
||||
throw error(scanner, 'Unexpected token');
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
function isLiteral(token) {
|
||||
return token && token.type === 'Literal';
|
||||
}
|
||||
function isBracket(token, open) {
|
||||
return token && token.type === 'Bracket' && (open == null || token.open === open);
|
||||
}
|
||||
function isOpenBracket(token) {
|
||||
return isBracket(token, true);
|
||||
}
|
||||
function isCloseBracket(token) {
|
||||
return isBracket(token, false);
|
||||
}
|
||||
function isWhiteSpace(token) {
|
||||
return token && token.type === 'WhiteSpace';
|
||||
}
|
||||
function isOperator(token, operator) {
|
||||
return token && token.type === 'Operator' && (!operator || token.operator === operator);
|
||||
}
|
||||
function isSiblingOperator(token) {
|
||||
return isOperator(token, exports.OperatorType.Sibling);
|
||||
}
|
||||
function isArgumentDelimiter(token) {
|
||||
return isOperator(token, exports.OperatorType.ArgumentDelimiter);
|
||||
}
|
||||
function isFragmentDelimiter(token) {
|
||||
return isArgumentDelimiter(token);
|
||||
}
|
||||
function isImportant(token) {
|
||||
return isOperator(token, exports.OperatorType.Important);
|
||||
}
|
||||
function isValue(token) {
|
||||
return token.type === 'StringValue'
|
||||
|| token.type === 'ColorValue'
|
||||
|| token.type === 'NumberValue'
|
||||
|| token.type === 'Literal'
|
||||
|| token.type === 'Field'
|
||||
|| token.type === 'CustomProperty';
|
||||
}
|
||||
function isValueDelimiter(token) {
|
||||
return isOperator(token, exports.OperatorType.PropertyDelimiter)
|
||||
|| isOperator(token, exports.OperatorType.ValueDelimiter);
|
||||
}
|
||||
function isFunctionStart(scanner) {
|
||||
const t1 = scanner.tokens[scanner.pos];
|
||||
const t2 = scanner.tokens[scanner.pos + 1];
|
||||
return t1 && t2 && isLiteral(t1) && t2.type === 'Bracket';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses given abbreviation into property set
|
||||
*/
|
||||
function parse(abbr, options) {
|
||||
try {
|
||||
const tokens = typeof abbr === 'string' ? tokenize(abbr, options && options.value) : abbr;
|
||||
return parser(tokens, options);
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof Scanner.ScannerError && typeof abbr === 'string') {
|
||||
err.message += `\n${abbr}\n${'-'.repeat(err.pos)}^`;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
exports.default = parse;
|
||||
exports.getToken = getToken;
|
||||
exports.parser = parser;
|
||||
exports.tokenize = tokenize;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
node_modules/@emmetio/css-abbreviation/dist/index.cjs.map
generated
vendored
1
node_modules/@emmetio/css-abbreviation/dist/index.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
10
node_modules/@emmetio/css-abbreviation/dist/index.d.ts
generated
vendored
10
node_modules/@emmetio/css-abbreviation/dist/index.d.ts
generated
vendored
@@ -1,10 +0,0 @@
|
||||
import tokenize, { getToken, type AllTokens } from './tokenizer/index.js';
|
||||
import parser, { type CSSProperty, type ParseOptions } from './parser/index.js';
|
||||
export { tokenize, getToken, parser };
|
||||
export * from './tokenizer/tokens.js';
|
||||
export type { CSSProperty, CSSValue, ParseOptions, FunctionCall, Value } from './parser/index.js';
|
||||
export type CSSAbbreviation = CSSProperty[];
|
||||
/**
|
||||
* Parses given abbreviation into property set
|
||||
*/
|
||||
export default function parse(abbr: string | AllTokens[], options?: ParseOptions): CSSAbbreviation;
|
||||
680
node_modules/@emmetio/css-abbreviation/dist/index.js
generated
vendored
680
node_modules/@emmetio/css-abbreviation/dist/index.js
generated
vendored
@@ -1,680 +0,0 @@
|
||||
import Scanner, { isNumber, isAlpha, isAlphaWord, isQuote, isSpace, isAlphaNumericWord, ScannerError } from '@emmetio/scanner';
|
||||
|
||||
var OperatorType;
|
||||
(function (OperatorType) {
|
||||
OperatorType["Sibling"] = "+";
|
||||
OperatorType["Important"] = "!";
|
||||
OperatorType["ArgumentDelimiter"] = ",";
|
||||
OperatorType["ValueDelimiter"] = "-";
|
||||
OperatorType["PropertyDelimiter"] = ":";
|
||||
})(OperatorType || (OperatorType = {}));
|
||||
|
||||
var Chars;
|
||||
(function (Chars) {
|
||||
/** `#` character */
|
||||
Chars[Chars["Hash"] = 35] = "Hash";
|
||||
/** `$` character */
|
||||
Chars[Chars["Dollar"] = 36] = "Dollar";
|
||||
/** `-` character */
|
||||
Chars[Chars["Dash"] = 45] = "Dash";
|
||||
/** `.` character */
|
||||
Chars[Chars["Dot"] = 46] = "Dot";
|
||||
/** `:` character */
|
||||
Chars[Chars["Colon"] = 58] = "Colon";
|
||||
/** `,` character */
|
||||
Chars[Chars["Comma"] = 44] = "Comma";
|
||||
/** `!` character */
|
||||
Chars[Chars["Excl"] = 33] = "Excl";
|
||||
/** `@` character */
|
||||
Chars[Chars["At"] = 64] = "At";
|
||||
/** `%` character */
|
||||
Chars[Chars["Percent"] = 37] = "Percent";
|
||||
/** `_` character */
|
||||
Chars[Chars["Underscore"] = 95] = "Underscore";
|
||||
/** `(` character */
|
||||
Chars[Chars["RoundBracketOpen"] = 40] = "RoundBracketOpen";
|
||||
/** `)` character */
|
||||
Chars[Chars["RoundBracketClose"] = 41] = "RoundBracketClose";
|
||||
/** `{` character */
|
||||
Chars[Chars["CurlyBracketOpen"] = 123] = "CurlyBracketOpen";
|
||||
/** `}` character */
|
||||
Chars[Chars["CurlyBracketClose"] = 125] = "CurlyBracketClose";
|
||||
/** `+` character */
|
||||
Chars[Chars["Sibling"] = 43] = "Sibling";
|
||||
/** `'` character */
|
||||
Chars[Chars["SingleQuote"] = 39] = "SingleQuote";
|
||||
/** `"` character */
|
||||
Chars[Chars["DoubleQuote"] = 34] = "DoubleQuote";
|
||||
/** `t` character */
|
||||
Chars[Chars["Transparent"] = 116] = "Transparent";
|
||||
/** `/` character */
|
||||
Chars[Chars["Slash"] = 47] = "Slash";
|
||||
})(Chars || (Chars = {}));
|
||||
|
||||
function tokenize(abbr, isValue) {
|
||||
let brackets = 0;
|
||||
let token;
|
||||
const scanner = new Scanner(abbr);
|
||||
const tokens = [];
|
||||
while (!scanner.eof()) {
|
||||
token = getToken(scanner, brackets === 0 && !isValue);
|
||||
if (!token) {
|
||||
throw scanner.error('Unexpected character');
|
||||
}
|
||||
if (token.type === 'Bracket') {
|
||||
if (!brackets && token.open) {
|
||||
mergeTokens(scanner, tokens);
|
||||
}
|
||||
brackets += token.open ? 1 : -1;
|
||||
if (brackets < 0) {
|
||||
throw scanner.error('Unexpected bracket', token.start);
|
||||
}
|
||||
}
|
||||
tokens.push(token);
|
||||
// Forcibly consume next operator after unit-less numeric value or color:
|
||||
// next dash `-` must be used as value delimiter
|
||||
if (shouldConsumeDashAfter(token) && (token = operator(scanner))) {
|
||||
tokens.push(token);
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
/**
|
||||
* Returns next token from given scanner, if possible
|
||||
*/
|
||||
function getToken(scanner, short) {
|
||||
return field(scanner)
|
||||
|| customProperty(scanner)
|
||||
|| numberValue(scanner)
|
||||
|| colorValue(scanner)
|
||||
|| stringValue(scanner)
|
||||
|| bracket(scanner)
|
||||
|| operator(scanner)
|
||||
|| whiteSpace(scanner)
|
||||
|| literal(scanner, short);
|
||||
}
|
||||
function field(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dollar) && scanner.eat(Chars.CurlyBracketOpen)) {
|
||||
scanner.start = scanner.pos;
|
||||
let index;
|
||||
let name = '';
|
||||
if (scanner.eatWhile(isNumber)) {
|
||||
// It’s a field
|
||||
index = Number(scanner.current());
|
||||
name = scanner.eat(Chars.Colon) ? consumePlaceholder(scanner) : '';
|
||||
}
|
||||
else if (isAlpha(scanner.peek())) {
|
||||
// It’s a variable
|
||||
name = consumePlaceholder(scanner);
|
||||
}
|
||||
if (scanner.eat(Chars.CurlyBracketClose)) {
|
||||
return {
|
||||
type: 'Field',
|
||||
index, name,
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
throw scanner.error('Expecting }');
|
||||
}
|
||||
// If we reached here then there’s no valid field here, revert
|
||||
// back to starting position
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes a placeholder: value right after `:` in field. Could be empty
|
||||
*/
|
||||
function consumePlaceholder(stream) {
|
||||
const stack = [];
|
||||
stream.start = stream.pos;
|
||||
while (!stream.eof()) {
|
||||
if (stream.eat(Chars.CurlyBracketOpen)) {
|
||||
stack.push(stream.pos);
|
||||
}
|
||||
else if (stream.eat(Chars.CurlyBracketClose)) {
|
||||
if (!stack.length) {
|
||||
stream.pos--;
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
else {
|
||||
stream.pos++;
|
||||
}
|
||||
}
|
||||
if (stack.length) {
|
||||
stream.pos = stack.pop();
|
||||
throw stream.error(`Expecting }`);
|
||||
}
|
||||
return stream.current();
|
||||
}
|
||||
/**
|
||||
* Consumes literal from given scanner
|
||||
* @param short Use short notation for consuming value.
|
||||
* The difference between “short” and “full” notation is that first one uses
|
||||
* alpha characters only and used for extracting keywords from abbreviation,
|
||||
* while “full” notation also supports numbers and dashes
|
||||
*/
|
||||
function literal(scanner, short) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(isIdentPrefix)) {
|
||||
// SCSS or LESS variable
|
||||
// NB a bit dirty hack: if abbreviation starts with identifier prefix,
|
||||
// consume alpha characters only to allow embedded variables
|
||||
scanner.eatWhile(start ? isKeyword : isLiteral$1);
|
||||
}
|
||||
else if (scanner.eat(isAlphaWord)) {
|
||||
scanner.eatWhile(short ? isLiteral$1 : isKeyword);
|
||||
}
|
||||
else {
|
||||
// Allow dots only at the beginning of literal
|
||||
scanner.eat(Chars.Dot);
|
||||
scanner.eatWhile(isLiteral$1);
|
||||
}
|
||||
if (start !== scanner.pos) {
|
||||
scanner.start = start;
|
||||
return createLiteral(scanner, scanner.start = start);
|
||||
}
|
||||
}
|
||||
function createLiteral(scanner, start = scanner.start, end = scanner.pos) {
|
||||
return {
|
||||
type: 'Literal',
|
||||
value: scanner.substring(start, end),
|
||||
start,
|
||||
end
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Consumes numeric CSS value (number with optional unit) from current stream,
|
||||
* if possible
|
||||
*/
|
||||
function numberValue(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeNumber(scanner)) {
|
||||
scanner.start = start;
|
||||
const rawValue = scanner.current();
|
||||
// eat unit, which can be a % or alpha word
|
||||
scanner.start = scanner.pos;
|
||||
scanner.eat(Chars.Percent) || scanner.eatWhile(isAlphaWord);
|
||||
return {
|
||||
type: 'NumberValue',
|
||||
value: Number(rawValue),
|
||||
rawValue,
|
||||
unit: scanner.current(),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes quoted string value from given scanner
|
||||
*/
|
||||
function stringValue(scanner) {
|
||||
const ch = scanner.peek();
|
||||
const start = scanner.pos;
|
||||
let finished = false;
|
||||
if (isQuote(ch)) {
|
||||
scanner.pos++;
|
||||
while (!scanner.eof()) {
|
||||
// Do not throw error on malformed string
|
||||
if (scanner.eat(ch)) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return {
|
||||
type: 'StringValue',
|
||||
value: scanner.substring(start + 1, scanner.pos - (finished ? 1 : 0)),
|
||||
quote: ch === Chars.SingleQuote ? 'single' : 'double',
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes a color token from given string
|
||||
*/
|
||||
function colorValue(scanner) {
|
||||
// supported color variations:
|
||||
// #abc → #aabbccc
|
||||
// #0 → #000000
|
||||
// #fff.5 → rgba(255, 255, 255, 0.5)
|
||||
// #t → transparent
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Hash)) {
|
||||
const valueStart = scanner.pos;
|
||||
let color = '';
|
||||
let alpha = '';
|
||||
if (scanner.eatWhile(isHex)) {
|
||||
color = scanner.substring(valueStart, scanner.pos);
|
||||
alpha = colorAlpha(scanner);
|
||||
}
|
||||
else if (scanner.eat(Chars.Transparent)) {
|
||||
color = '0';
|
||||
alpha = colorAlpha(scanner) || '0';
|
||||
}
|
||||
else {
|
||||
alpha = colorAlpha(scanner);
|
||||
}
|
||||
if (color || alpha || scanner.eof()) {
|
||||
const { r, g, b, a } = parseColor(color, alpha);
|
||||
return {
|
||||
type: 'ColorValue',
|
||||
r, g, b, a,
|
||||
raw: scanner.substring(start + 1, scanner.pos),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Consumed # but no actual value: invalid color value, treat it as literal
|
||||
return createLiteral(scanner, start);
|
||||
}
|
||||
}
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes alpha value of color: `.1`
|
||||
*/
|
||||
function colorAlpha(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dot)) {
|
||||
scanner.start = start;
|
||||
if (scanner.eatWhile(isNumber)) {
|
||||
return scanner.current();
|
||||
}
|
||||
return '1';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Consumes white space characters as string literal from given scanner
|
||||
*/
|
||||
function whiteSpace(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eatWhile(isSpace)) {
|
||||
return {
|
||||
type: 'WhiteSpace',
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes custom CSS property: --foo-bar
|
||||
*/
|
||||
function customProperty(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(Chars.Dash) && scanner.eat(Chars.Dash)) {
|
||||
scanner.start = start;
|
||||
scanner.eatWhile(isKeyword);
|
||||
return {
|
||||
type: 'CustomProperty',
|
||||
value: scanner.current(),
|
||||
start,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
scanner.pos = start;
|
||||
}
|
||||
/**
|
||||
* Consumes bracket from given scanner
|
||||
*/
|
||||
function bracket(scanner) {
|
||||
const ch = scanner.peek();
|
||||
if (isBracket$1(ch)) {
|
||||
return {
|
||||
type: 'Bracket',
|
||||
open: ch === Chars.RoundBracketOpen,
|
||||
start: scanner.pos++,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes operator from given scanner
|
||||
*/
|
||||
function operator(scanner) {
|
||||
const op = operatorType(scanner.peek());
|
||||
if (op) {
|
||||
return {
|
||||
type: 'Operator',
|
||||
operator: op,
|
||||
start: scanner.pos++,
|
||||
end: scanner.pos
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Eats number value from given stream
|
||||
* @return Returns `true` if number was consumed
|
||||
*/
|
||||
function consumeNumber(stream) {
|
||||
const start = stream.pos;
|
||||
stream.eat(Chars.Dash);
|
||||
const afterNegative = stream.pos;
|
||||
const hasDecimal = stream.eatWhile(isNumber);
|
||||
const prevPos = stream.pos;
|
||||
if (stream.eat(Chars.Dot)) {
|
||||
// It’s perfectly valid to have numbers like `1.`, which enforces
|
||||
// value to float unit type
|
||||
const hasFloat = stream.eatWhile(isNumber);
|
||||
if (!hasDecimal && !hasFloat) {
|
||||
// Lone dot
|
||||
stream.pos = prevPos;
|
||||
}
|
||||
}
|
||||
// Edge case: consumed dash only: not a number, bail-out
|
||||
if (stream.pos === afterNegative) {
|
||||
stream.pos = start;
|
||||
}
|
||||
return stream.pos !== start;
|
||||
}
|
||||
function isIdentPrefix(code) {
|
||||
return code === Chars.At || code === Chars.Dollar;
|
||||
}
|
||||
/**
|
||||
* If given character is an operator, returns it’s type
|
||||
*/
|
||||
function operatorType(ch) {
|
||||
return (ch === Chars.Sibling && OperatorType.Sibling)
|
||||
|| (ch === Chars.Excl && OperatorType.Important)
|
||||
|| (ch === Chars.Comma && OperatorType.ArgumentDelimiter)
|
||||
|| (ch === Chars.Colon && OperatorType.PropertyDelimiter)
|
||||
|| (ch === Chars.Dash && OperatorType.ValueDelimiter)
|
||||
|| void 0;
|
||||
}
|
||||
/**
|
||||
* Check if given code is a hex value (/0-9a-f/)
|
||||
*/
|
||||
function isHex(code) {
|
||||
return isNumber(code) || isAlpha(code, 65, 70); // A-F
|
||||
}
|
||||
function isKeyword(code) {
|
||||
return isAlphaNumericWord(code) || code === Chars.Dash;
|
||||
}
|
||||
function isBracket$1(code) {
|
||||
return code === Chars.RoundBracketOpen || code === Chars.RoundBracketClose;
|
||||
}
|
||||
function isLiteral$1(code) {
|
||||
return isAlphaWord(code) || code === Chars.Percent || code === Chars.Slash;
|
||||
}
|
||||
/**
|
||||
* Parses given color value from abbreviation into RGBA format
|
||||
*/
|
||||
function parseColor(value, alpha) {
|
||||
let r = '0';
|
||||
let g = '0';
|
||||
let b = '0';
|
||||
let a = Number(alpha != null && alpha !== '' ? alpha : 1);
|
||||
if (value === 't') {
|
||||
a = 0;
|
||||
}
|
||||
else {
|
||||
switch (value.length) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
r = g = b = value + value;
|
||||
break;
|
||||
case 2:
|
||||
r = g = b = value;
|
||||
break;
|
||||
case 3:
|
||||
r = value[0] + value[0];
|
||||
g = value[1] + value[1];
|
||||
b = value[2] + value[2];
|
||||
break;
|
||||
default:
|
||||
value += value;
|
||||
r = value.slice(0, 2);
|
||||
g = value.slice(2, 4);
|
||||
b = value.slice(4, 6);
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: parseInt(r, 16),
|
||||
g: parseInt(g, 16),
|
||||
b: parseInt(b, 16),
|
||||
a
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Check if scanner reader must consume dash after given token.
|
||||
* Used in cases where user must explicitly separate numeric values
|
||||
*/
|
||||
function shouldConsumeDashAfter(token) {
|
||||
return token.type === 'ColorValue' || (token.type === 'NumberValue' && !token.unit);
|
||||
}
|
||||
/**
|
||||
* Merges last adjacent tokens into a single literal.
|
||||
* This function is used to overcome edge case when function name was parsed
|
||||
* as a list of separate tokens. For example, a `scale3d()` value will be
|
||||
* parsed as literal and number tokens (`scale` and `3d`) which is a perfectly
|
||||
* valid abbreviation but undesired result. This function will detect last adjacent
|
||||
* literal and number values and combine them into single literal
|
||||
*/
|
||||
function mergeTokens(scanner, tokens) {
|
||||
let start = 0;
|
||||
let end = 0;
|
||||
while (tokens.length) {
|
||||
const token = last(tokens);
|
||||
if (token.type === 'Literal' || token.type === 'NumberValue') {
|
||||
start = token.start;
|
||||
if (!end) {
|
||||
end = token.end;
|
||||
}
|
||||
tokens.pop();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start !== end) {
|
||||
tokens.push(createLiteral(scanner, start, end));
|
||||
}
|
||||
}
|
||||
function last(arr) {
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
|
||||
function tokenScanner(tokens) {
|
||||
return {
|
||||
tokens,
|
||||
start: 0,
|
||||
pos: 0,
|
||||
size: tokens.length
|
||||
};
|
||||
}
|
||||
function peek(scanner) {
|
||||
return scanner.tokens[scanner.pos];
|
||||
}
|
||||
function readable(scanner) {
|
||||
return scanner.pos < scanner.size;
|
||||
}
|
||||
function consume(scanner, test) {
|
||||
if (test(peek(scanner))) {
|
||||
scanner.pos++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function error(scanner, message, token = peek(scanner)) {
|
||||
if (token && token.start != null) {
|
||||
message += ` at ${token.start}`;
|
||||
}
|
||||
const err = new Error(message);
|
||||
err['pos'] = token && token.start;
|
||||
return err;
|
||||
}
|
||||
|
||||
function parser(tokens, options = {}) {
|
||||
const scanner = tokenScanner(tokens);
|
||||
const result = [];
|
||||
let property;
|
||||
while (readable(scanner)) {
|
||||
if (property = consumeProperty(scanner, options)) {
|
||||
result.push(property);
|
||||
}
|
||||
else if (!consume(scanner, isSiblingOperator)) {
|
||||
throw error(scanner, 'Unexpected token');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Consumes single CSS property
|
||||
*/
|
||||
function consumeProperty(scanner, options) {
|
||||
let name;
|
||||
let important = false;
|
||||
let valueFragment;
|
||||
const value = [];
|
||||
const token = peek(scanner);
|
||||
const valueMode = !!options.value;
|
||||
if (!valueMode && isLiteral(token) && !isFunctionStart(scanner)) {
|
||||
scanner.pos++;
|
||||
name = token.value;
|
||||
// Consume any following value delimiter after property name
|
||||
consume(scanner, isValueDelimiter);
|
||||
}
|
||||
// Skip whitespace right after property name, if any
|
||||
if (valueMode) {
|
||||
consume(scanner, isWhiteSpace);
|
||||
}
|
||||
while (readable(scanner)) {
|
||||
if (consume(scanner, isImportant)) {
|
||||
important = true;
|
||||
}
|
||||
else if (valueFragment = consumeValue(scanner, valueMode)) {
|
||||
value.push(valueFragment);
|
||||
}
|
||||
else if (!consume(scanner, isFragmentDelimiter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name || value.length || important) {
|
||||
return { name, value, important };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes single value fragment, e.g. all value tokens before comma
|
||||
*/
|
||||
function consumeValue(scanner, inArgument) {
|
||||
const result = [];
|
||||
let token;
|
||||
let args;
|
||||
while (readable(scanner)) {
|
||||
token = peek(scanner);
|
||||
if (isValue(token)) {
|
||||
scanner.pos++;
|
||||
if (isLiteral(token) && (args = consumeArguments(scanner))) {
|
||||
result.push({
|
||||
type: 'FunctionCall',
|
||||
name: token.value,
|
||||
arguments: args
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.push(token);
|
||||
}
|
||||
}
|
||||
else if (isValueDelimiter(token) || (inArgument && isWhiteSpace(token))) {
|
||||
scanner.pos++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result.length
|
||||
? { type: 'CSSValue', value: result }
|
||||
: void 0;
|
||||
}
|
||||
function consumeArguments(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consume(scanner, isOpenBracket)) {
|
||||
const args = [];
|
||||
let value;
|
||||
while (readable(scanner) && !consume(scanner, isCloseBracket)) {
|
||||
if (value = consumeValue(scanner, true)) {
|
||||
args.push(value);
|
||||
}
|
||||
else if (!consume(scanner, isWhiteSpace) && !consume(scanner, isArgumentDelimiter)) {
|
||||
throw error(scanner, 'Unexpected token');
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
function isLiteral(token) {
|
||||
return token && token.type === 'Literal';
|
||||
}
|
||||
function isBracket(token, open) {
|
||||
return token && token.type === 'Bracket' && (open == null || token.open === open);
|
||||
}
|
||||
function isOpenBracket(token) {
|
||||
return isBracket(token, true);
|
||||
}
|
||||
function isCloseBracket(token) {
|
||||
return isBracket(token, false);
|
||||
}
|
||||
function isWhiteSpace(token) {
|
||||
return token && token.type === 'WhiteSpace';
|
||||
}
|
||||
function isOperator(token, operator) {
|
||||
return token && token.type === 'Operator' && (!operator || token.operator === operator);
|
||||
}
|
||||
function isSiblingOperator(token) {
|
||||
return isOperator(token, OperatorType.Sibling);
|
||||
}
|
||||
function isArgumentDelimiter(token) {
|
||||
return isOperator(token, OperatorType.ArgumentDelimiter);
|
||||
}
|
||||
function isFragmentDelimiter(token) {
|
||||
return isArgumentDelimiter(token);
|
||||
}
|
||||
function isImportant(token) {
|
||||
return isOperator(token, OperatorType.Important);
|
||||
}
|
||||
function isValue(token) {
|
||||
return token.type === 'StringValue'
|
||||
|| token.type === 'ColorValue'
|
||||
|| token.type === 'NumberValue'
|
||||
|| token.type === 'Literal'
|
||||
|| token.type === 'Field'
|
||||
|| token.type === 'CustomProperty';
|
||||
}
|
||||
function isValueDelimiter(token) {
|
||||
return isOperator(token, OperatorType.PropertyDelimiter)
|
||||
|| isOperator(token, OperatorType.ValueDelimiter);
|
||||
}
|
||||
function isFunctionStart(scanner) {
|
||||
const t1 = scanner.tokens[scanner.pos];
|
||||
const t2 = scanner.tokens[scanner.pos + 1];
|
||||
return t1 && t2 && isLiteral(t1) && t2.type === 'Bracket';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses given abbreviation into property set
|
||||
*/
|
||||
function parse(abbr, options) {
|
||||
try {
|
||||
const tokens = typeof abbr === 'string' ? tokenize(abbr, options && options.value) : abbr;
|
||||
return parser(tokens, options);
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof ScannerError && typeof abbr === 'string') {
|
||||
err.message += `\n${abbr}\n${'-'.repeat(err.pos)}^`;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export { OperatorType, parse as default, getToken, parser, tokenize };
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/@emmetio/css-abbreviation/dist/index.js.map
generated
vendored
1
node_modules/@emmetio/css-abbreviation/dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
17
node_modules/@emmetio/css-abbreviation/dist/parser/TokenScanner.d.ts
generated
vendored
17
node_modules/@emmetio/css-abbreviation/dist/parser/TokenScanner.d.ts
generated
vendored
@@ -1,17 +0,0 @@
|
||||
import type { AllTokens } from '../tokenizer/index.js';
|
||||
export interface TokenScanner {
|
||||
tokens: AllTokens[];
|
||||
start: number;
|
||||
pos: number;
|
||||
size: number;
|
||||
}
|
||||
type TestFn = (token?: AllTokens) => boolean;
|
||||
export default function tokenScanner(tokens: AllTokens[]): TokenScanner;
|
||||
export declare function peek(scanner: TokenScanner): AllTokens | undefined;
|
||||
export declare function next(scanner: TokenScanner): AllTokens | undefined;
|
||||
export declare function slice(scanner: TokenScanner, from?: number, to?: number): AllTokens[];
|
||||
export declare function readable(scanner: TokenScanner): boolean;
|
||||
export declare function consume(scanner: TokenScanner, test: TestFn): boolean;
|
||||
export declare function error(scanner: TokenScanner, message: string, token?: AllTokens | undefined): Error;
|
||||
export declare function consumeWhile(scanner: TokenScanner, test: TestFn): boolean;
|
||||
export {};
|
||||
23
node_modules/@emmetio/css-abbreviation/dist/parser/index.d.ts
generated
vendored
23
node_modules/@emmetio/css-abbreviation/dist/parser/index.d.ts
generated
vendored
@@ -1,23 +0,0 @@
|
||||
import type { StringValue, NumberValue, ColorValue, Literal, AllTokens, Field, CustomProperty } from '../tokenizer/tokens.js';
|
||||
export type Value = StringValue | NumberValue | ColorValue | Literal | FunctionCall | Field | CustomProperty;
|
||||
export interface FunctionCall {
|
||||
type: 'FunctionCall';
|
||||
name: string;
|
||||
arguments: CSSValue[];
|
||||
}
|
||||
export interface CSSValue {
|
||||
type: 'CSSValue';
|
||||
value: Value[];
|
||||
}
|
||||
export interface CSSProperty {
|
||||
name?: string;
|
||||
value: CSSValue[];
|
||||
important: boolean;
|
||||
/** Snippet matched with current property */
|
||||
snippet?: any;
|
||||
}
|
||||
export interface ParseOptions {
|
||||
/** Consumes given abbreviation tokens as value */
|
||||
value?: boolean;
|
||||
}
|
||||
export default function parser(tokens: AllTokens[], options?: ParseOptions): CSSProperty[];
|
||||
8
node_modules/@emmetio/css-abbreviation/dist/tokenizer/index.d.ts
generated
vendored
8
node_modules/@emmetio/css-abbreviation/dist/tokenizer/index.d.ts
generated
vendored
@@ -1,8 +0,0 @@
|
||||
import { default as Scanner } from '@emmetio/scanner';
|
||||
import type { AllTokens, Literal, NumberValue, ColorValue, WhiteSpace, Operator, Bracket, StringValue, Field, CustomProperty } from './tokens.js';
|
||||
export * from './tokens.js';
|
||||
export default function tokenize(abbr: string, isValue?: boolean): AllTokens[];
|
||||
/**
|
||||
* Returns next token from given scanner, if possible
|
||||
*/
|
||||
export declare function getToken(scanner: Scanner, short?: boolean): Bracket | Literal | Operator | WhiteSpace | ColorValue | NumberValue | StringValue | CustomProperty | Field | undefined;
|
||||
58
node_modules/@emmetio/css-abbreviation/dist/tokenizer/tokens.d.ts
generated
vendored
58
node_modules/@emmetio/css-abbreviation/dist/tokenizer/tokens.d.ts
generated
vendored
@@ -1,58 +0,0 @@
|
||||
export type AllTokens = Bracket | Literal | Operator | WhiteSpace | ColorValue | NumberValue | StringValue | CustomProperty | Field;
|
||||
export declare const enum OperatorType {
|
||||
Sibling = "+",
|
||||
Important = "!",
|
||||
ArgumentDelimiter = ",",
|
||||
ValueDelimiter = "-",
|
||||
PropertyDelimiter = ":"
|
||||
}
|
||||
export interface Token {
|
||||
type: string;
|
||||
/** Location of token start in source */
|
||||
start?: number;
|
||||
/** Location of token end in source */
|
||||
end?: number;
|
||||
}
|
||||
export interface Operator extends Token {
|
||||
type: 'Operator';
|
||||
operator: OperatorType;
|
||||
}
|
||||
export interface Bracket extends Token {
|
||||
type: 'Bracket';
|
||||
open: boolean;
|
||||
}
|
||||
export interface Literal extends Token {
|
||||
type: 'Literal';
|
||||
value: string;
|
||||
}
|
||||
export interface CustomProperty extends Token {
|
||||
type: 'CustomProperty';
|
||||
value: string;
|
||||
}
|
||||
export interface NumberValue extends Token {
|
||||
type: 'NumberValue';
|
||||
value: number;
|
||||
unit: string;
|
||||
rawValue: string;
|
||||
}
|
||||
export interface ColorValue extends Token {
|
||||
type: 'ColorValue';
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
raw: string;
|
||||
}
|
||||
export interface StringValue extends Token {
|
||||
type: 'StringValue';
|
||||
value: string;
|
||||
quote: 'single' | 'double';
|
||||
}
|
||||
export interface WhiteSpace extends Token {
|
||||
type: 'WhiteSpace';
|
||||
}
|
||||
export interface Field extends Token {
|
||||
type: 'Field';
|
||||
index?: number;
|
||||
name: string;
|
||||
}
|
||||
40
node_modules/@emmetio/css-abbreviation/dist/tokenizer/utils.d.ts
generated
vendored
40
node_modules/@emmetio/css-abbreviation/dist/tokenizer/utils.d.ts
generated
vendored
@@ -1,40 +0,0 @@
|
||||
export declare const enum Chars {
|
||||
/** `#` character */
|
||||
Hash = 35,
|
||||
/** `$` character */
|
||||
Dollar = 36,
|
||||
/** `-` character */
|
||||
Dash = 45,
|
||||
/** `.` character */
|
||||
Dot = 46,
|
||||
/** `:` character */
|
||||
Colon = 58,
|
||||
/** `,` character */
|
||||
Comma = 44,
|
||||
/** `!` character */
|
||||
Excl = 33,
|
||||
/** `@` character */
|
||||
At = 64,
|
||||
/** `%` character */
|
||||
Percent = 37,
|
||||
/** `_` character */
|
||||
Underscore = 95,
|
||||
/** `(` character */
|
||||
RoundBracketOpen = 40,
|
||||
/** `)` character */
|
||||
RoundBracketClose = 41,
|
||||
/** `{` character */
|
||||
CurlyBracketOpen = 123,
|
||||
/** `}` character */
|
||||
CurlyBracketClose = 125,
|
||||
/** `+` character */
|
||||
Sibling = 43,
|
||||
/** `'` character */
|
||||
SingleQuote = 39,
|
||||
/** `"` character */
|
||||
DoubleQuote = 34,
|
||||
/** `t` character */
|
||||
Transparent = 116,
|
||||
/** `/` character */
|
||||
Slash = 47
|
||||
}
|
||||
52
node_modules/@emmetio/css-abbreviation/package.json
generated
vendored
52
node_modules/@emmetio/css-abbreviation/package.json
generated
vendored
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/css-abbreviation",
|
||||
"version": "2.1.8",
|
||||
"description": "Parses Emmet CSS abbreviation into AST tree",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -wc",
|
||||
"clean": "rimraf ./dist",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/emmet.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/emmet/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/emmet#readme",
|
||||
"dependencies": {
|
||||
"@emmetio/scanner": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^10.0.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.11.18",
|
||||
"mocha": "^10.2.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"rollup": "^3.9.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"mocha": {
|
||||
"loader": "ts-node/esm",
|
||||
"spec": "./test/*.ts"
|
||||
},
|
||||
"gitHead": "fce2127ece65adbb293a40aa0577e4558658c559"
|
||||
}
|
||||
21
node_modules/@emmetio/css-parser/LICENSE
generated
vendored
21
node_modules/@emmetio/css-parser/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Emmet.io
|
||||
|
||||
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.
|
||||
2
node_modules/@emmetio/css-parser/README.md
generated
vendored
2
node_modules/@emmetio/css-parser/README.md
generated
vendored
@@ -1,2 +0,0 @@
|
||||
# css-parser
|
||||
CSS/LESS/SCSS fast and minimalistic parser
|
||||
1620
node_modules/@emmetio/css-parser/dist/css-parser.cjs.js
generated
vendored
1620
node_modules/@emmetio/css-parser/dist/css-parser.cjs.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1593
node_modules/@emmetio/css-parser/dist/css-parser.es.js
generated
vendored
1593
node_modules/@emmetio/css-parser/dist/css-parser.es.js
generated
vendored
File diff suppressed because it is too large
Load Diff
887
node_modules/@emmetio/css-parser/dist/css-parser.js
generated
vendored
887
node_modules/@emmetio/css-parser/dist/css-parser.js
generated
vendored
@@ -1,887 +0,0 @@
|
||||
(function (exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
class StreamReader {
|
||||
constructor(string, start, end) {
|
||||
if (end == null && typeof string === 'string') {
|
||||
end = string.length;
|
||||
}
|
||||
|
||||
this.string = string;
|
||||
this.pos = this.start = start || 0;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eof() {
|
||||
return this.pos >= this.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
* @param {Point} start
|
||||
* @param {Point} end
|
||||
* @return {StreamReader}
|
||||
*/
|
||||
limit(start, end) {
|
||||
return new this.constructor(this.string, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
* @returns {Number}
|
||||
*/
|
||||
peek() {
|
||||
return this.string.charCodeAt(this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
* @returns {Number}
|
||||
*/
|
||||
next() {
|
||||
if (this.pos < this.string.length) {
|
||||
return this.string.charCodeAt(this.pos++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
* @param {Number|Function} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eat(match) {
|
||||
const ch = this.peek();
|
||||
const ok = typeof match === 'function' ? match(ch) : ch === match;
|
||||
|
||||
if (ok) {
|
||||
this.next();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
* @param {Object} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eatWhile(match) {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) {}
|
||||
return this.pos !== start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
* @param {Number} n
|
||||
*/
|
||||
backUp(n) {
|
||||
this.pos -= (n || 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
* @returns {String}
|
||||
*/
|
||||
current() {
|
||||
return this.substring(this.start, this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns substring for given range
|
||||
* @param {Number} start
|
||||
* @param {Number} [end]
|
||||
* @return {String}
|
||||
*/
|
||||
substring(start, end) {
|
||||
return this.string.slice(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
* @param {String} message
|
||||
* @return {Error}
|
||||
*/
|
||||
error(message) {
|
||||
const err = new Error(`${message} at char ${this.pos + 1}`);
|
||||
err.originalMessage = message;
|
||||
err.pos = this.pos;
|
||||
err.string = this.string;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
class Container {
|
||||
constructor() {
|
||||
this.children = [];
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
get firstChild() {
|
||||
return this.children[0];
|
||||
}
|
||||
|
||||
get nextSibling() {
|
||||
const ix = this.index();
|
||||
return ix !== -1 ? this.parent.children[ix + 1] : null;
|
||||
}
|
||||
|
||||
get previousSibling() {
|
||||
const ix = this.index();
|
||||
return ix !== -1 ? this.parent.children[ix - 1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current element’s index in parent list of child nodes
|
||||
* @return {Number}
|
||||
*/
|
||||
index() {
|
||||
return this.parent ? this.parent.children.indexOf(this) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds given node as a child
|
||||
* @param {Node} node
|
||||
* @return {Node} Current node
|
||||
*/
|
||||
add(node) {
|
||||
if (node) {
|
||||
node.remove();
|
||||
this.children.push(node);
|
||||
node.parent = this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current node from its parent
|
||||
* @return {Node} Current node
|
||||
*/
|
||||
remove() {
|
||||
if (this.parent) {
|
||||
const ix = this.index();
|
||||
if (ix !== -1) {
|
||||
this.parent.children.splice(ix, 1);
|
||||
this.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class Stylesheet extends Container {
|
||||
constructor() {
|
||||
super();
|
||||
this.type = 'stylesheet';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node’s start position in stream
|
||||
* @return {*}
|
||||
*/
|
||||
get start() {
|
||||
const node = this.firstChild;
|
||||
return node && node.start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node’s end position in stream
|
||||
* @return {*}
|
||||
*/
|
||||
get end() {
|
||||
const node = this.children[this.children.length - 1];
|
||||
return node && node.end;
|
||||
}
|
||||
}
|
||||
|
||||
class Token {
|
||||
/**
|
||||
* @param {StreamReader} stream
|
||||
* @param {String} type Token type
|
||||
* @param {Object} [start] Tokens’ start position in `stream`
|
||||
* @param {Object} [end] Tokens’ end position in `stream`
|
||||
*/
|
||||
constructor(stream, type, start, end) {
|
||||
this.stream = stream;
|
||||
this.start = start != null ? start : stream.start;
|
||||
this.end = end != null ? end : stream.pos;
|
||||
this.type = type;
|
||||
|
||||
this._props = null;
|
||||
this._value = null;
|
||||
this._items = null;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this._items ? this._items.length : 0;
|
||||
}
|
||||
|
||||
add(item) {
|
||||
if (item) {
|
||||
if (!this._items) {
|
||||
this._items = [item];
|
||||
} else {
|
||||
this._items.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(item) {
|
||||
if (this._items) {
|
||||
const ix = this._items.indexOf(item);
|
||||
if (ix !== -1 ) {
|
||||
this._items.splice(ix, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
item(i) {
|
||||
return this._items && this._items[i];
|
||||
}
|
||||
|
||||
property(name, value) {
|
||||
if (typeof value !== 'undefined') {
|
||||
// set property value
|
||||
if (!this._props) {
|
||||
this._props = {};
|
||||
}
|
||||
|
||||
this._props[name] = value;
|
||||
}
|
||||
|
||||
return this._props && this._props[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns token textual representation
|
||||
* @return {String}
|
||||
*/
|
||||
toString() {
|
||||
return `${this.valueOf()} [${this.start}, ${this.end}]`;
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
if (this._value === null) {
|
||||
this._value = this.stream.substring(this.start, this.end);
|
||||
}
|
||||
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes tokens that matches given criteria from start and end of given list
|
||||
* @param {Token[]} tokens
|
||||
* @param {Function} test
|
||||
* @return {Token[]}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Trims formatting tokens (whitespace and comments) from the beginning and end
|
||||
* of given token list
|
||||
* @param {Token[]} tokens
|
||||
* @return {Token[]}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Check if given token is a formatting one (whitespace or comment)
|
||||
* @param {Token} token
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Consumes string char-by-char from given stream
|
||||
* @param {StreamReader} stream
|
||||
* @param {String} string
|
||||
* @return {Boolean} Returns `true` if string was completely consumed
|
||||
*/
|
||||
function eatString(stream, string) {
|
||||
const start = stream.pos;
|
||||
|
||||
for (let i = 0, il = string.length; i < il; i++) {
|
||||
if (!stream.eat(string.charCodeAt(i))) {
|
||||
stream.pos = start;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function consumeWhile(stream, match) {
|
||||
const start = stream.pos;
|
||||
if (stream.eatWhile(match)) {
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns type of given token
|
||||
* @param {Token} token
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
|
||||
function last(arr) {
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
|
||||
function createRule(stream, tokens, content) {
|
||||
if (!tokens.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let ix = 0;
|
||||
const name = tokens[ix++];
|
||||
|
||||
if (name.type === 'at-keyword') {
|
||||
let expression;
|
||||
if (ix < tokens.length) {
|
||||
expression = tokens[ix++];
|
||||
expression.type = 'expression';
|
||||
expression.end = last(tokens).end;
|
||||
} else {
|
||||
expression = new Token(stream, 'expression', name.end, name.end);
|
||||
}
|
||||
|
||||
return new AtRule(stream, name, expression, content);
|
||||
} else {
|
||||
name.end = last(tokens).end;
|
||||
}
|
||||
|
||||
return new Rule(stream, name, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents CSS rule
|
||||
* @type {Node}
|
||||
*/
|
||||
class Rule extends Container {
|
||||
/**
|
||||
* @param {StreamReader} stream
|
||||
* @param {Token} name Rule’s name token
|
||||
* @param {Token} content Rule’s content token
|
||||
*/
|
||||
constructor(stream, name, content) {
|
||||
super();
|
||||
this.type = 'rule';
|
||||
this.stream = stream;
|
||||
this.nameToken = name;
|
||||
this.contentToken = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node name
|
||||
* @return {String}
|
||||
*/
|
||||
get name() {
|
||||
return valueOf(this.nameToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node’s start position in stream
|
||||
* @return {*}
|
||||
*/
|
||||
get start() {
|
||||
return this.nameToken && this.nameToken.start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node’s end position in stream
|
||||
* @return {*}
|
||||
*/
|
||||
get end() {
|
||||
const token = this.contentToken || this.nameToken;
|
||||
return token && token.end;
|
||||
}
|
||||
}
|
||||
|
||||
class AtRule extends Rule {
|
||||
constructor(stream, name, expression, content) {
|
||||
super(stream, name, content);
|
||||
this.type = 'at-rule';
|
||||
this.expressionToken = expression;
|
||||
}
|
||||
|
||||
get expressions() {
|
||||
return valueOf(this.expressionToken);
|
||||
}
|
||||
}
|
||||
|
||||
function valueOf(token) {
|
||||
return token && token.valueOf();
|
||||
}
|
||||
|
||||
function createProperty(stream, tokens, terminator) {
|
||||
// NB in LESS, fragmented properties without value like `.foo.bar;` must be
|
||||
// treated like mixin call
|
||||
if (!tokens.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let separator, value, ix = 0;
|
||||
const name = tokens[ix++];
|
||||
|
||||
if (ix < tokens.length) {
|
||||
value = tokens[ix++];
|
||||
value.type = 'value';
|
||||
value.end = last(tokens).end;
|
||||
}
|
||||
|
||||
if (name && value) {
|
||||
separator = new Token(stream, 'separator', name.end, value.start);
|
||||
}
|
||||
|
||||
return new Property(
|
||||
stream,
|
||||
name,
|
||||
value,
|
||||
separator,
|
||||
terminator
|
||||
);
|
||||
}
|
||||
|
||||
class Property {
|
||||
constructor(stream, name, value, separator, terminator) {
|
||||
this.type = 'property';
|
||||
this.stream = stream;
|
||||
this.nameToken = name;
|
||||
this.valueToken = value;
|
||||
|
||||
this.separatorToken = separator;
|
||||
this.terminatorToken = terminator;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return valueOf$1(this.nameToken);
|
||||
}
|
||||
|
||||
get value() {
|
||||
return valueOf$1(this.valueToken);
|
||||
}
|
||||
|
||||
get separator() {
|
||||
return valueOf$1(this.separatorToken);
|
||||
}
|
||||
|
||||
get terminator() {
|
||||
return valueOf$1(this.terminatorToken);
|
||||
}
|
||||
|
||||
get start() {
|
||||
const token = this.nameToken || this.separatorToken || this.valueToken
|
||||
|| this.terminatorToken;
|
||||
return token && token.start;
|
||||
}
|
||||
|
||||
get end() {
|
||||
const token = this.terminatorToken || this.valueToken
|
||||
|| this.separatorToken || this.nameToken;
|
||||
return token && token.end;
|
||||
}
|
||||
}
|
||||
|
||||
function valueOf$1(token) {
|
||||
return token && token.valueOf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods for consuming quoted values
|
||||
*/
|
||||
|
||||
const SINGLE_QUOTE = 39; // '
|
||||
const DOUBLE_QUOTE = 34; // "
|
||||
|
||||
function isQuote(code) {
|
||||
return code === SINGLE_QUOTE || code === DOUBLE_QUOTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given code is a number
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNumber(code) {
|
||||
return code > 47 && code < 58;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
* @param {Number} code
|
||||
* @param {Number} [from]
|
||||
* @param {Number} [to]
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isAlpha(code, from, to) {
|
||||
from = from || 65; // A
|
||||
to = to || 90; // Z
|
||||
code &= ~32; // quick hack to convert any char code to uppercase char code
|
||||
|
||||
return code >= from && code <= to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isWhiteSpace(code) {
|
||||
return code === 32 /* space */
|
||||
|| code === 9 /* tab */
|
||||
|| code === 160; /* non-breaking space */
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is a space
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isSpace(code) {
|
||||
return isWhiteSpace(code)
|
||||
|| code === 10 /* LF */
|
||||
|| code === 13; /* CR */
|
||||
}
|
||||
|
||||
const HYPHEN = 45;
|
||||
const UNDERSCORE = 95;
|
||||
|
||||
function ident(stream) {
|
||||
return eatIdent(stream) && new Token(stream, 'ident');
|
||||
}
|
||||
|
||||
function eatIdent(stream) {
|
||||
const start = stream.pos;
|
||||
|
||||
stream.eat(HYPHEN);
|
||||
if (stream.eat(isIdentStart)) {
|
||||
stream.eatWhile(isIdent);
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
|
||||
stream.pos = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isIdentStart(code) {
|
||||
return code === UNDERSCORE || code === HYPHEN || isAlpha(code) || code >= 128;
|
||||
}
|
||||
|
||||
function isIdent(code) {
|
||||
return isNumber(code) || isIdentStart(code);
|
||||
}
|
||||
|
||||
function prefixed(stream, tokenType, prefix, body, allowEmptyBody) {
|
||||
const start = stream.pos;
|
||||
|
||||
if (stream.eat(prefix)) {
|
||||
const bodyToken = body(stream, start);
|
||||
if (bodyToken || allowEmptyBody) {
|
||||
stream.start = start;
|
||||
return new Token(stream, tokenType, start).add(bodyToken);
|
||||
}
|
||||
}
|
||||
|
||||
stream.pos = start;
|
||||
}
|
||||
|
||||
const AT = 64; // @
|
||||
|
||||
/**
|
||||
* Consumes at-keyword from given stream
|
||||
*/
|
||||
function atKeyword(stream) {
|
||||
return prefixed(stream, 'at-keyword', AT, ident);
|
||||
}
|
||||
|
||||
function eatString$1(stream, asToken) {
|
||||
let ch = stream.peek(), pos;
|
||||
|
||||
if (isQuote(ch)) {
|
||||
stream.start = stream.pos;
|
||||
stream.next();
|
||||
const quote = ch;
|
||||
const valueStart = stream.pos;
|
||||
|
||||
while (!stream.eof()) {
|
||||
pos = stream.pos;
|
||||
if (stream.eat(quote) || stream.eat(isNewline)) {
|
||||
// found end of string or newline without preceding '\',
|
||||
// which is not allowed (don’t throw error, for now)
|
||||
break;
|
||||
} else if (stream.eat(92 /* \ */)) {
|
||||
// backslash allows newline in string
|
||||
stream.eat(isNewline);
|
||||
}
|
||||
|
||||
stream.next();
|
||||
}
|
||||
|
||||
// Either reached EOF or explicitly stopped at string end
|
||||
// NB use extra `asToken` param to return boolean instead of token to reduce
|
||||
// memory allocations and improve performance
|
||||
if (asToken) {
|
||||
const token = new Token(stream, 'string');
|
||||
token.add(new Token(stream, 'unquoted', valueStart, pos));
|
||||
token.property('quote', quote);
|
||||
return token;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isNewline(code) {
|
||||
return code === 10 /* LF */ || code === 13 /* CR */;
|
||||
}
|
||||
|
||||
const ASTERISK = 42;
|
||||
const SLASH = 47;
|
||||
|
||||
/**
|
||||
* Consumes comment from given stream: either multi-line or single-line
|
||||
* @param {StreamReader} stream
|
||||
* @return {CommentToken}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
function eatComment(stream) {
|
||||
return eatSingleLineComment(stream) || eatMultiLineComment(stream);
|
||||
}
|
||||
|
||||
function eatSingleLineComment(stream) {
|
||||
const start = stream.pos;
|
||||
|
||||
if (stream.eat(SLASH) && stream.eat(SLASH)) {
|
||||
// single-line comment, consume till the end of line
|
||||
stream.start = start;
|
||||
while (!stream.eof()) {
|
||||
if (isLineBreak(stream.next())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
stream.pos = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
function eatMultiLineComment(stream) {
|
||||
const start = stream.pos;
|
||||
|
||||
if (stream.eat(SLASH) && stream.eat(ASTERISK)) {
|
||||
while (!stream.eof()) {
|
||||
if (stream.next() === ASTERISK && stream.eat(SLASH)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
|
||||
stream.pos = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isLineBreak(code) {
|
||||
return code === 10 /* LF */ || code === 13 /* CR */;
|
||||
}
|
||||
|
||||
function eatWhitespace(stream) {
|
||||
return consumeWhile(stream, isSpace);
|
||||
}
|
||||
|
||||
function eatUnquoted(stream) {
|
||||
return consumeWhile(stream, isUnquoted);
|
||||
}
|
||||
|
||||
function isUnquoted(code) {
|
||||
return !isNaN(code) && !isQuote(code) && !isSpace(code)
|
||||
&& code !== 40 /* ( */ && code !== 41 /* ) */ && code !== 92 /* \ */
|
||||
&& !isNonPrintable(code);
|
||||
}
|
||||
|
||||
function isNonPrintable(code) {
|
||||
return (code >= 0 && code <= 8) || code === 11
|
||||
|| (code >= 14 && code <= 31) || code === 127;
|
||||
}
|
||||
|
||||
function eatUrl(stream) {
|
||||
const start = stream.pos;
|
||||
|
||||
if (eatString(stream, 'url(')) {
|
||||
eatWhitespace(stream);
|
||||
eatString$1(stream) || eatUnquoted(stream);
|
||||
eatWhitespace(stream);
|
||||
stream.eat(41); // )
|
||||
stream.start = start;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stream.pos = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
const LBRACE = 40; // (
|
||||
const RBRACE = 41; // )
|
||||
const PROP_DELIMITER = 58; // :
|
||||
const PROP_TERMINATOR = 59; // ;
|
||||
const RULE_START = 123; // {
|
||||
const RULE_END = 125; // }
|
||||
|
||||
function parseStylesheet(source) {
|
||||
const stream = typeof source === 'string' ? new StreamReader(source) : source;
|
||||
const root = new Stylesheet();
|
||||
let ctx = root, child, accum, token;
|
||||
let tokens = [];
|
||||
const flush = () => {
|
||||
if (accum) {
|
||||
tokens.push(accum);
|
||||
accum = null;
|
||||
}
|
||||
};
|
||||
|
||||
while (!stream.eof()) {
|
||||
if (eatWhitespace(stream) || eatComment(stream)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stream.start = stream.pos;
|
||||
|
||||
if (stream.eatWhile(PROP_DELIMITER)) {
|
||||
// Property delimiter can be either a real property delimiter or a
|
||||
// part of pseudo-selector.
|
||||
if (!tokens.length) {
|
||||
if (accum) {
|
||||
// No consumed tokens yet but pending token: most likely it’s
|
||||
// a CSS property
|
||||
flush();
|
||||
} else {
|
||||
// No consumend or accumulated token, seems like a start of
|
||||
// pseudo-selector, e.g. `::slotted`
|
||||
accum = new Token(stream, 'preparse');
|
||||
}
|
||||
}
|
||||
// Skip delimiter if there are already consumend tokens: most likely
|
||||
// it’s a part of pseudo-selector
|
||||
} else if (stream.eat(RULE_END)) {
|
||||
flush();
|
||||
|
||||
// Finalize context section
|
||||
ctx.add(createProperty(stream, tokens));
|
||||
|
||||
if (ctx.type !== 'stylesheet') {
|
||||
// In case of invalid stylesheet with redundant `}`,
|
||||
// don’t modify root section.
|
||||
ctx.contentToken.end = stream.pos;
|
||||
ctx = ctx.parent;
|
||||
}
|
||||
|
||||
tokens.length = 0;
|
||||
} else if (stream.eat(PROP_TERMINATOR)) {
|
||||
flush();
|
||||
ctx.add(createProperty(stream, tokens, new Token(stream, 'termintator')));
|
||||
tokens.length = 0;
|
||||
} else if (stream.eat(RULE_START)) {
|
||||
flush();
|
||||
child = createRule(stream, tokens, new Token(stream, 'body'));
|
||||
ctx.add(child);
|
||||
ctx = child;
|
||||
tokens.length = 0;
|
||||
} else if (token = atKeyword(stream)) {
|
||||
// Explictly consume @-tokens since it defines how rule or property
|
||||
// should be pre-parsed
|
||||
flush();
|
||||
tokens.push(token);
|
||||
} else if (eatUrl(stream) || eatBraces(stream) || eatString$1(stream) || stream.next()) {
|
||||
// NB explicitly consume `url()` token since it may contain
|
||||
// an unquoted url like `http://example.com` which interferes
|
||||
// with single-line comment
|
||||
accum = accum || new Token(stream, 'preparse');
|
||||
accum.end = stream.pos;
|
||||
} else {
|
||||
throw new Error(`Unexpected end-of-stream at ${stream.pos}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (accum) {
|
||||
tokens.push(accum);
|
||||
}
|
||||
|
||||
// Finalize all the rest properties
|
||||
ctx.add(createProperty(stream, tokens));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes content inside round braces. Mostly used to skip `;` token inside
|
||||
* expressions since in LESS it is also used to separate function arguments
|
||||
* @param {StringReader} stream
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function eatBraces(stream) {
|
||||
if (stream.eat(LBRACE)) {
|
||||
let stack = 1;
|
||||
|
||||
while (!stream.eof()) {
|
||||
if (stream.eat(RBRACE)) {
|
||||
stack--;
|
||||
if (!stack) {
|
||||
break;
|
||||
}
|
||||
} else if (stream.eat(LBRACE)) {
|
||||
stack++;
|
||||
} else {
|
||||
eatUrl(stream) || eatString$1(stream) || eatComment(stream) || stream.next();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exports['default'] = parseStylesheet;
|
||||
|
||||
}((this.cssParser = this.cssParser || {})));
|
||||
1224
node_modules/@emmetio/css-parser/dist/html-matcher.cjs.js
generated
vendored
1224
node_modules/@emmetio/css-parser/dist/html-matcher.cjs.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1214
node_modules/@emmetio/css-parser/dist/html-matcher.es.js
generated
vendored
1214
node_modules/@emmetio/css-parser/dist/html-matcher.es.js
generated
vendored
File diff suppressed because it is too large
Load Diff
36
node_modules/@emmetio/css-parser/package.json
generated
vendored
36
node_modules/@emmetio/css-parser/package.json
generated
vendored
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/css-parser",
|
||||
"version": "0.4.0",
|
||||
"description": "CSS/LESS/SCSS fast and minimalistic parser",
|
||||
"main": "./dist/css-parser.cjs.js",
|
||||
"module": "./dist/css-parser.es.js",
|
||||
"dependencies": {
|
||||
"@emmetio/stream-reader": "^2.2.0",
|
||||
"@emmetio/stream-reader-utils": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
|
||||
"babel-register": "^6.24.1",
|
||||
"mocha": "^3.2.0",
|
||||
"rollup": "^0.41.6",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"rollup-watch": "^3.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -wc",
|
||||
"prepublish": "npm run test && npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/css-parser.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/css-parser/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/css-parser#readme"
|
||||
}
|
||||
21
node_modules/@emmetio/html-matcher/LICENSE
generated
vendored
21
node_modules/@emmetio/html-matcher/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Emmet.io
|
||||
|
||||
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.
|
||||
50
node_modules/@emmetio/html-matcher/README.md
generated
vendored
50
node_modules/@emmetio/html-matcher/README.md
generated
vendored
@@ -1,50 +0,0 @@
|
||||
# Small and fast HTML matcher
|
||||
|
||||
Finds matching opening and closing tag pair for given location in HTML/XML source:
|
||||
|
||||
```js
|
||||
import match from '@emmetio/html-matcher';
|
||||
|
||||
const content = '<div><a href="http://emmet.io">Example</a></div>';
|
||||
|
||||
// Find tag pair at character 35
|
||||
const tag = match(content, 35);
|
||||
|
||||
console.log(tag.name); // Name of matched tag: "a"
|
||||
console.log(tag.open); // Range of opening tag: [5, 31]
|
||||
console.log(tag.end); // Range of closing tag: [38, 42]
|
||||
|
||||
// List of attributes found in opening tag
|
||||
console.log(tag.attributes);
|
||||
```
|
||||
|
||||
By default, matcher works in HTML, which means if it finds tag name which is known to be empty (for example, `<img>`) it will not search for it’s closing part. However, such behavior might be unexpected for XML syntaxes where all tags should be either self-closed or provide closing part. In this case, you should pass `xml: true` option to properly handle XML mode:
|
||||
|
||||
```js
|
||||
import match from '@emmetio/html-matcher';
|
||||
|
||||
const content = '<div><img>Caption</img></div>';
|
||||
const html = match(content, 8);
|
||||
const xml = match(content, 8, { xml: true });
|
||||
|
||||
console.log(html.name); // "img"
|
||||
console.log(html.open); // [5, 10]
|
||||
console.log(html.close); // undefined
|
||||
|
||||
console.log(xml.name); // "img"
|
||||
console.log(xml.open); // [5, 10]
|
||||
console.log(xml.close); // [17, 23]
|
||||
```
|
||||
|
||||
## Special tags
|
||||
|
||||
In HTML, some tags has special meaning. For example, a `<script>` tag: its contents should be completely ignored until we find closing `</script>` tag. But, if `<script>` tag contains unknown `type` attribute value, we should consider it as a regular tag. By default, matcher understands `script` and `style` tags as “special” but you can override them with `special` option:
|
||||
|
||||
```js
|
||||
import match from '@emmetio/html-matcher';
|
||||
|
||||
// Treat `<foo-bar>` tag as ”special”: skip its content until `</foo-bar>`. Note that this option overwrites default value with `['script', 'style']` value
|
||||
match('...', 10, { special: { 'foo-bar': null } });
|
||||
```
|
||||
|
||||
The `special` option is an object where key is a tag name and value is an array of `type` attribute values which, if present in tag, will make it special. If array is not provided, all instances of tag with given name will be considered as special.
|
||||
29
node_modules/@emmetio/html-matcher/dist/attributes.d.ts
generated
vendored
29
node_modules/@emmetio/html-matcher/dist/attributes.d.ts
generated
vendored
@@ -1,29 +0,0 @@
|
||||
import Scanner from '@emmetio/scanner';
|
||||
export interface AttributeToken {
|
||||
name: string;
|
||||
value?: string;
|
||||
nameStart: number;
|
||||
nameEnd: number;
|
||||
valueStart?: number;
|
||||
valueEnd?: number;
|
||||
}
|
||||
/**
|
||||
* Parses given string as list of HTML attributes.
|
||||
* @param src A fragment to parse. If `name` argument is provided, it must be an
|
||||
* opening tag (`<a foo="bar">`), otherwise it should be a fragment between element
|
||||
* name and tag closing angle (`foo="bar"`)
|
||||
* @param name Tag name
|
||||
*/
|
||||
export default function attributes(src: string, name?: string): AttributeToken[];
|
||||
/**
|
||||
* Consumes attribute name from given scanner context
|
||||
*/
|
||||
export declare function attributeName(scanner: Scanner): boolean;
|
||||
/**
|
||||
* Consumes attribute value
|
||||
*/
|
||||
export declare function attributeValue(scanner: Scanner): true | undefined;
|
||||
/**
|
||||
* Returns clean (unquoted) value of `name` attribute
|
||||
*/
|
||||
export declare function getAttributeValue(attrs: AttributeToken[], name: string): string | undefined;
|
||||
641
node_modules/@emmetio/html-matcher/dist/html-matcher.cjs.js
generated
vendored
641
node_modules/@emmetio/html-matcher/dist/html-matcher.cjs.js
generated
vendored
@@ -1,641 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var Scanner = require('@emmetio/scanner');
|
||||
var Scanner__default = _interopDefault(Scanner);
|
||||
|
||||
const defaultOptions = {
|
||||
xml: false,
|
||||
allTokens: false,
|
||||
special: {
|
||||
style: null,
|
||||
script: ['', 'text/javascript', 'application/x-javascript', 'javascript', 'typescript', 'ts', 'coffee', 'coffeescript']
|
||||
},
|
||||
empty: ['img', 'meta', 'link', 'br', 'base', 'hr', 'area', 'wbr', 'col', 'embed', 'input', 'param', 'source', 'track']
|
||||
};
|
||||
/** Options for `Scanner` utils */
|
||||
const opt = { throws: false };
|
||||
function createOptions(options = {}) {
|
||||
return Object.assign(Object.assign({}, defaultOptions), options);
|
||||
}
|
||||
/**
|
||||
* Converts given string into array of character codes
|
||||
*/
|
||||
function toCharCodes(str) {
|
||||
return str.split('').map(ch => ch.charCodeAt(0));
|
||||
}
|
||||
/**
|
||||
* Consumes array of character codes from given scanner
|
||||
*/
|
||||
function consumeArray(scanner, codes) {
|
||||
const start = scanner.pos;
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
if (!scanner.eat(codes[i])) {
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Consumes section from given string which starts with `open` character codes
|
||||
* and ends with `close` character codes
|
||||
* @return Returns `true` if section was consumed
|
||||
*/
|
||||
function consumeSection(scanner, open, close, allowUnclosed) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, open)) {
|
||||
// consumed `<!--`, read next until we find ending part or reach the end of input
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, close)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos++;
|
||||
}
|
||||
// unclosed section is allowed
|
||||
if (allowUnclosed) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
// unable to find section, revert to initial position
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given character can be used as a start of tag name or attribute
|
||||
*/
|
||||
function nameStartChar(ch) {
|
||||
// Limited XML spec: https://www.w3.org/TR/xml/#NT-NameStartChar
|
||||
return Scanner.isAlpha(ch) || ch === 58 /* Colon */ || ch === 95 /* Underscore */
|
||||
|| (ch >= 0xC0 && ch <= 0xD6)
|
||||
|| (ch >= 0xD8 && ch <= 0xF6)
|
||||
|| (ch >= 0xF8 && ch <= 0x2FF)
|
||||
|| (ch >= 0x370 && ch <= 0x37D)
|
||||
|| (ch >= 0x37F && ch <= 0x1FFF);
|
||||
}
|
||||
/**
|
||||
* Check if given character can be used in a tag or attribute name
|
||||
*/
|
||||
function nameChar(ch) {
|
||||
// Limited XML spec: https://www.w3.org/TR/xml/#NT-NameChar
|
||||
return nameStartChar(ch) || ch === 45 /* Dash */ || ch === 46 /* Dot */ || Scanner.isNumber(ch)
|
||||
|| ch === 0xB7
|
||||
|| (ch >= 0x0300 && ch <= 0x036F);
|
||||
}
|
||||
/**
|
||||
* Consumes identifier from given scanner
|
||||
*/
|
||||
function ident(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(nameStartChar)) {
|
||||
scanner.eatWhile(nameChar);
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given code is tag terminator
|
||||
*/
|
||||
function isTerminator(code) {
|
||||
return code === 62 /* RightAngle */ || code === 47 /* Slash */;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is valid unquoted value
|
||||
*/
|
||||
function isUnquoted(code) {
|
||||
return !isNaN(code) && !Scanner.isQuote(code) && !Scanner.isSpace(code) && !isTerminator(code);
|
||||
}
|
||||
/**
|
||||
* Consumes paired tokens (like `[` and `]`) with respect of nesting and embedded
|
||||
* quoted values
|
||||
* @return `true` if paired token was consumed
|
||||
*/
|
||||
function consumePaired(scanner) {
|
||||
return Scanner.eatPair(scanner, 60 /* LeftAngle */, 62 /* RightAngle */, opt)
|
||||
|| Scanner.eatPair(scanner, 40 /* LeftRound */, 41 /* RightRound */, opt)
|
||||
|| Scanner.eatPair(scanner, 91 /* LeftSquare */, 93 /* RightSquare */, opt)
|
||||
|| Scanner.eatPair(scanner, 123 /* LeftCurly */, 125 /* RightCurly */, opt);
|
||||
}
|
||||
/**
|
||||
* Returns unquoted value of given string
|
||||
*/
|
||||
function getUnquotedValue(value) {
|
||||
// Trim quotes
|
||||
if (Scanner.isQuote(value.charCodeAt(0))) {
|
||||
value = value.slice(1);
|
||||
}
|
||||
if (Scanner.isQuote(value.charCodeAt(value.length - 1))) {
|
||||
value = value.slice(0, -1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses given string as list of HTML attributes.
|
||||
* @param src A fragment to parse. If `name` argument is provided, it must be an
|
||||
* opening tag (`<a foo="bar">`), otherwise it should be a fragment between element
|
||||
* name and tag closing angle (`foo="bar"`)
|
||||
* @param name Tag name
|
||||
*/
|
||||
function attributes(src, name) {
|
||||
const result = [];
|
||||
let start = 0;
|
||||
let end = src.length;
|
||||
if (name) {
|
||||
start = name.length + 1;
|
||||
end -= src.slice(-2) === '/>' ? 2 : 1;
|
||||
}
|
||||
const scanner = new Scanner__default(src, start, end);
|
||||
while (!scanner.eof()) {
|
||||
scanner.eatWhile(Scanner.isSpace);
|
||||
if (attributeName(scanner)) {
|
||||
const token = {
|
||||
name: scanner.current(),
|
||||
nameStart: scanner.start,
|
||||
nameEnd: scanner.pos
|
||||
};
|
||||
if (scanner.eat(61 /* Equals */) && attributeValue(scanner)) {
|
||||
token.value = scanner.current();
|
||||
token.valueStart = scanner.start;
|
||||
token.valueEnd = scanner.pos;
|
||||
}
|
||||
result.push(token);
|
||||
}
|
||||
else {
|
||||
// Do not break on invalid attributes: we are not validating parser
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Consumes attribute name from given scanner context
|
||||
*/
|
||||
function attributeName(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(42 /* Asterisk */) || scanner.eat(35 /* Hash */)) {
|
||||
// Angular-style directives: `<section *ngIf="showSection">`, `<video #movieplayer ...>`
|
||||
ident(scanner);
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
// Attribute name could be a regular name or expression:
|
||||
// React-style – `<div {...props}>`
|
||||
// Angular-style – `<div [ng-for]>` or `<div *ng-for>`
|
||||
return consumePaired(scanner) || ident(scanner);
|
||||
}
|
||||
/**
|
||||
* Consumes attribute value
|
||||
*/
|
||||
function attributeValue(scanner) {
|
||||
// Supported attribute values are quoted, React-like expressions (`{foo}`)
|
||||
// or unquoted literals
|
||||
return Scanner.eatQuoted(scanner, opt) || consumePaired(scanner) || unquoted(scanner);
|
||||
}
|
||||
/**
|
||||
* Returns clean (unquoted) value of `name` attribute
|
||||
*/
|
||||
function getAttributeValue(attrs, name) {
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const attr = attrs[i];
|
||||
if (attr.name === name) {
|
||||
return attr.value && getUnquotedValue(attr.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes unquoted value
|
||||
*/
|
||||
function unquoted(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eatWhile(isUnquoted)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const cdataOpen = toCharCodes('<![CDATA[');
|
||||
const cdataClose = toCharCodes(']]>');
|
||||
const commentOpen = toCharCodes('<!--');
|
||||
const commentClose = toCharCodes('-->');
|
||||
const piStart = toCharCodes('<?');
|
||||
const piEnd = toCharCodes('?>');
|
||||
const erbStart = toCharCodes('<%');
|
||||
const erbEnd = toCharCodes('%>');
|
||||
/**
|
||||
* Performs fast scan of given source code: for each tag found it invokes callback
|
||||
* with tag name, its type (open, close, self-close) and range in original source.
|
||||
* Unlike regular scanner, fast scanner doesn’t provide info about attributes to
|
||||
* reduce object allocations hence increase performance.
|
||||
* If `callback` returns `false`, scanner stops parsing.
|
||||
* @param special List of “special” HTML tags which should be ignored. Most likely
|
||||
* it’s a "script" and "style" tags.
|
||||
*/
|
||||
function scan(source, callback, options) {
|
||||
const scanner = new Scanner__default(source);
|
||||
const special = options ? options.special : null;
|
||||
const allTokens = options ? options.allTokens : false;
|
||||
let type;
|
||||
let name;
|
||||
let nameStart;
|
||||
let nameEnd;
|
||||
let nameCodes;
|
||||
let found = false;
|
||||
let piName = null;
|
||||
while (!scanner.eof()) {
|
||||
const start = scanner.pos;
|
||||
if (cdata(scanner)) {
|
||||
if (allTokens && callback('#cdata', 4 /* CData */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (comment(scanner)) {
|
||||
if (allTokens && callback('#comment', 6 /* Comment */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (erb(scanner)) {
|
||||
if (allTokens && callback('#erb', 7 /* ERB */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (piName = processingInstruction(scanner)) {
|
||||
if (allTokens && callback(piName, 5 /* ProcessingInstruction */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (scanner.eat(60 /* LeftAngle */)) {
|
||||
// Maybe a tag name?
|
||||
type = scanner.eat(47 /* Slash */) ? 2 /* Close */ : 1 /* Open */;
|
||||
nameStart = scanner.pos;
|
||||
if (ident(scanner)) {
|
||||
// Consumed tag name
|
||||
nameEnd = scanner.pos;
|
||||
if (type !== 2 /* Close */) {
|
||||
skipAttributes(scanner);
|
||||
scanner.eatWhile(Scanner.isSpace);
|
||||
if (scanner.eat(47 /* Slash */)) {
|
||||
type = 3 /* SelfClose */;
|
||||
}
|
||||
}
|
||||
if (scanner.eat(62 /* RightAngle */)) {
|
||||
// Tag properly closed
|
||||
name = scanner.substring(nameStart, nameEnd);
|
||||
if (callback(name, type, start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
if (type === 1 /* Open */ && special && isSpecial(special, name, source, start, scanner.pos)) {
|
||||
// Found opening tag of special element: we should skip
|
||||
// scanner contents until we find closing tag
|
||||
nameCodes = toCharCodes(name);
|
||||
found = false;
|
||||
while (!scanner.eof()) {
|
||||
if (consumeClosing(scanner, nameCodes)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
scanner.pos++;
|
||||
}
|
||||
if (found && callback(name, 2 /* Close */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Skips attributes in current tag context
|
||||
*/
|
||||
function skipAttributes(scanner) {
|
||||
while (!scanner.eof()) {
|
||||
scanner.eatWhile(Scanner.isSpace);
|
||||
if (attributeName(scanner)) {
|
||||
if (scanner.eat(61 /* Equals */)) {
|
||||
attributeValue(scanner);
|
||||
}
|
||||
}
|
||||
else if (isTerminator(scanner.peek())) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes closing tag with given name from scanner
|
||||
*/
|
||||
function consumeClosing(scanner, name) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(60 /* LeftAngle */) && scanner.eat(47 /* Slash */) && consumeArray(scanner, name) && scanner.eat(62 /* RightAngle */)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Consumes CDATA from given scanner
|
||||
*/
|
||||
function cdata(scanner) {
|
||||
return consumeSection(scanner, cdataOpen, cdataClose, true);
|
||||
}
|
||||
/**
|
||||
* Consumes comments from given scanner
|
||||
*/
|
||||
function comment(scanner) {
|
||||
return consumeSection(scanner, commentOpen, commentClose, true);
|
||||
}
|
||||
/**
|
||||
* Consumes processing instruction from given scanner. If consumed, returns
|
||||
* processing instruction name
|
||||
*/
|
||||
function processingInstruction(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, piStart) && ident(scanner)) {
|
||||
const name = scanner.current();
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, piEnd)) {
|
||||
break;
|
||||
}
|
||||
Scanner.eatQuoted(scanner) || scanner.pos++;
|
||||
}
|
||||
scanner.start = start;
|
||||
return name;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Consumes ERB-style entity: `<% ... %>` or `<%= ... %>`
|
||||
*/
|
||||
function erb(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, erbStart)) {
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, erbEnd)) {
|
||||
break;
|
||||
}
|
||||
Scanner.eatQuoted(scanner) || scanner.pos++;
|
||||
}
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given tag name should be considered as special
|
||||
*/
|
||||
function isSpecial(special, name, source, start, end) {
|
||||
if (name in special) {
|
||||
const typeValues = special[name];
|
||||
if (!Array.isArray(typeValues)) {
|
||||
return true;
|
||||
}
|
||||
const attrs = attributes(source.substring(start + name.length + 1, end - 1));
|
||||
return typeValues.includes(getAttributeValue(attrs, 'type') || '');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matched tag for given `pos` location in XML/HTML `source`
|
||||
*/
|
||||
function match(source, pos, opt) {
|
||||
// Since we expect large input document, we’ll use pooling technique
|
||||
// for storing tag data to reduce memory pressure and improve performance
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
let result = null;
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 1 /* Open */ && isSelfClose(name, options)) {
|
||||
// Found empty element in HTML mode, mark is as self-closing
|
||||
type = 3 /* SelfClose */;
|
||||
}
|
||||
if (type === 1 /* Open */) {
|
||||
// Allocate tag object from pool
|
||||
stack.push(allocTag(pool, name, start, end));
|
||||
}
|
||||
else if (type === 3 /* SelfClose */) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched given self-closing tag
|
||||
result = {
|
||||
name,
|
||||
attributes: getAttributes(source, start, end, name),
|
||||
open: [start, end]
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const tag = last(stack);
|
||||
if (tag && tag.name === name) {
|
||||
// Matching closing tag found
|
||||
if (tag.start < pos && pos < end) {
|
||||
result = {
|
||||
name,
|
||||
attributes: getAttributes(source, tag.start, tag.end, name),
|
||||
open: [tag.start, tag.end],
|
||||
close: [start, end]
|
||||
};
|
||||
return false;
|
||||
}
|
||||
else if (stack.length) {
|
||||
// Release tag object for further re-use
|
||||
releaseTag(pool, stack.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in outward direction
|
||||
*/
|
||||
function balancedOutward(source, pos, opt) {
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
const result = [];
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 2 /* Close */) {
|
||||
const tag = last(stack);
|
||||
if (tag && tag.name === name) { // XXX check for invalid tag names?
|
||||
// Matching closing tag found, check if matched pair is a candidate
|
||||
// for outward balancing
|
||||
if (tag.start < pos && pos < end) {
|
||||
result.push({
|
||||
name,
|
||||
open: [tag.start, tag.end],
|
||||
close: [start, end]
|
||||
});
|
||||
}
|
||||
// Release tag object for further re-use
|
||||
releaseTag(pool, stack.pop());
|
||||
}
|
||||
}
|
||||
else if (type === 3 /* SelfClose */ || isSelfClose(name, options)) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched self-closed tag
|
||||
result.push({ name, open: [start, end] });
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(allocTag(pool, name, start, end));
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in inward direction
|
||||
*/
|
||||
function balancedInward(source, pos, opt) {
|
||||
// Collecting tags for inward balancing is a bit trickier: we have to store
|
||||
// first child of every matched tag until we find the one that matches given
|
||||
// location
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
const result = [];
|
||||
const alloc = (name, start, end) => {
|
||||
if (pool.length) {
|
||||
const tag = pool.pop();
|
||||
tag.name = name;
|
||||
tag.ranges.push(start, end);
|
||||
return tag;
|
||||
}
|
||||
return { name, ranges: [start, end] };
|
||||
};
|
||||
const release = (tag) => {
|
||||
tag.ranges.length = 0;
|
||||
tag.firstChild = void 0;
|
||||
pool.push(tag);
|
||||
};
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 2 /* Close */) {
|
||||
if (!stack.length) {
|
||||
// Some sort of lone closing tag, ignore it
|
||||
return;
|
||||
}
|
||||
let tag = last(stack);
|
||||
if (tag.name === name) { // XXX check for invalid tag names?
|
||||
// Matching closing tag found, check if matched pair is a candidate
|
||||
// for outward balancing
|
||||
if (tag.ranges[0] <= pos && pos <= end) {
|
||||
result.push({
|
||||
name,
|
||||
open: tag.ranges.slice(0, 2),
|
||||
close: [start, end]
|
||||
});
|
||||
while (tag.firstChild) {
|
||||
const child = tag.firstChild;
|
||||
const res = {
|
||||
name: child.name,
|
||||
open: child.ranges.slice(0, 2)
|
||||
};
|
||||
if (child.ranges.length > 2) {
|
||||
res.close = child.ranges.slice(2, 4);
|
||||
}
|
||||
result.push(res);
|
||||
release(tag);
|
||||
tag = child;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
stack.pop();
|
||||
const parent = last(stack);
|
||||
if (parent && !parent.firstChild) {
|
||||
// No first child in parent node: store current tag
|
||||
tag.ranges.push(start, end);
|
||||
parent.firstChild = tag;
|
||||
}
|
||||
else {
|
||||
release(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 3 /* SelfClose */ || isSelfClose(name, options)) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched self-closed tag, no need to look further
|
||||
result.push({ name, open: [start, end] });
|
||||
return false;
|
||||
}
|
||||
const parent = last(stack);
|
||||
if (parent && !parent.firstChild) {
|
||||
parent.firstChild = alloc(name, start, end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(alloc(name, start, end));
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
function allocTag(pool, name, start, end) {
|
||||
if (pool.length) {
|
||||
const tag = pool.pop();
|
||||
tag.name = name;
|
||||
tag.start = start;
|
||||
tag.end = end;
|
||||
return tag;
|
||||
}
|
||||
return { name, start, end };
|
||||
}
|
||||
function releaseTag(pool, tag) {
|
||||
pool.push(tag);
|
||||
}
|
||||
/**
|
||||
* Returns parsed attributes from given source
|
||||
*/
|
||||
function getAttributes(source, start, end, name) {
|
||||
const tokens = attributes(source.slice(start, end), name);
|
||||
tokens.forEach(attr => {
|
||||
attr.nameStart += start;
|
||||
attr.nameEnd += start;
|
||||
if (attr.value != null) {
|
||||
attr.valueStart += start;
|
||||
attr.valueEnd += start;
|
||||
}
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
/**
|
||||
* Check if given tag is self-close for current parsing context
|
||||
*/
|
||||
function isSelfClose(name, options) {
|
||||
return !options.xml && options.empty.includes(name);
|
||||
}
|
||||
function last(arr) {
|
||||
return arr.length ? arr[arr.length - 1] : null;
|
||||
}
|
||||
|
||||
exports.attributes = attributes;
|
||||
exports.balancedInward = balancedInward;
|
||||
exports.balancedOutward = balancedOutward;
|
||||
exports.createOptions = createOptions;
|
||||
exports.default = match;
|
||||
exports.scan = scan;
|
||||
//# sourceMappingURL=html-matcher.cjs.js.map
|
||||
1
node_modules/@emmetio/html-matcher/dist/html-matcher.cjs.js.map
generated
vendored
1
node_modules/@emmetio/html-matcher/dist/html-matcher.cjs.js.map
generated
vendored
File diff suppressed because one or more lines are too long
630
node_modules/@emmetio/html-matcher/dist/html-matcher.es.js
generated
vendored
630
node_modules/@emmetio/html-matcher/dist/html-matcher.es.js
generated
vendored
@@ -1,630 +0,0 @@
|
||||
import Scanner, { eatPair, isAlpha, isNumber, isQuote, isSpace, eatQuoted } from '@emmetio/scanner';
|
||||
|
||||
const defaultOptions = {
|
||||
xml: false,
|
||||
allTokens: false,
|
||||
special: {
|
||||
style: null,
|
||||
script: ['', 'text/javascript', 'application/x-javascript', 'javascript', 'typescript', 'ts', 'coffee', 'coffeescript']
|
||||
},
|
||||
empty: ['img', 'meta', 'link', 'br', 'base', 'hr', 'area', 'wbr', 'col', 'embed', 'input', 'param', 'source', 'track']
|
||||
};
|
||||
/** Options for `Scanner` utils */
|
||||
const opt = { throws: false };
|
||||
function createOptions(options = {}) {
|
||||
return Object.assign(Object.assign({}, defaultOptions), options);
|
||||
}
|
||||
/**
|
||||
* Converts given string into array of character codes
|
||||
*/
|
||||
function toCharCodes(str) {
|
||||
return str.split('').map(ch => ch.charCodeAt(0));
|
||||
}
|
||||
/**
|
||||
* Consumes array of character codes from given scanner
|
||||
*/
|
||||
function consumeArray(scanner, codes) {
|
||||
const start = scanner.pos;
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
if (!scanner.eat(codes[i])) {
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Consumes section from given string which starts with `open` character codes
|
||||
* and ends with `close` character codes
|
||||
* @return Returns `true` if section was consumed
|
||||
*/
|
||||
function consumeSection(scanner, open, close, allowUnclosed) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, open)) {
|
||||
// consumed `<!--`, read next until we find ending part or reach the end of input
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, close)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos++;
|
||||
}
|
||||
// unclosed section is allowed
|
||||
if (allowUnclosed) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
// unable to find section, revert to initial position
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given character can be used as a start of tag name or attribute
|
||||
*/
|
||||
function nameStartChar(ch) {
|
||||
// Limited XML spec: https://www.w3.org/TR/xml/#NT-NameStartChar
|
||||
return isAlpha(ch) || ch === 58 /* Colon */ || ch === 95 /* Underscore */
|
||||
|| (ch >= 0xC0 && ch <= 0xD6)
|
||||
|| (ch >= 0xD8 && ch <= 0xF6)
|
||||
|| (ch >= 0xF8 && ch <= 0x2FF)
|
||||
|| (ch >= 0x370 && ch <= 0x37D)
|
||||
|| (ch >= 0x37F && ch <= 0x1FFF);
|
||||
}
|
||||
/**
|
||||
* Check if given character can be used in a tag or attribute name
|
||||
*/
|
||||
function nameChar(ch) {
|
||||
// Limited XML spec: https://www.w3.org/TR/xml/#NT-NameChar
|
||||
return nameStartChar(ch) || ch === 45 /* Dash */ || ch === 46 /* Dot */ || isNumber(ch)
|
||||
|| ch === 0xB7
|
||||
|| (ch >= 0x0300 && ch <= 0x036F);
|
||||
}
|
||||
/**
|
||||
* Consumes identifier from given scanner
|
||||
*/
|
||||
function ident(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(nameStartChar)) {
|
||||
scanner.eatWhile(nameChar);
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given code is tag terminator
|
||||
*/
|
||||
function isTerminator(code) {
|
||||
return code === 62 /* RightAngle */ || code === 47 /* Slash */;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is valid unquoted value
|
||||
*/
|
||||
function isUnquoted(code) {
|
||||
return !isNaN(code) && !isQuote(code) && !isSpace(code) && !isTerminator(code);
|
||||
}
|
||||
/**
|
||||
* Consumes paired tokens (like `[` and `]`) with respect of nesting and embedded
|
||||
* quoted values
|
||||
* @return `true` if paired token was consumed
|
||||
*/
|
||||
function consumePaired(scanner) {
|
||||
return eatPair(scanner, 60 /* LeftAngle */, 62 /* RightAngle */, opt)
|
||||
|| eatPair(scanner, 40 /* LeftRound */, 41 /* RightRound */, opt)
|
||||
|| eatPair(scanner, 91 /* LeftSquare */, 93 /* RightSquare */, opt)
|
||||
|| eatPair(scanner, 123 /* LeftCurly */, 125 /* RightCurly */, opt);
|
||||
}
|
||||
/**
|
||||
* Returns unquoted value of given string
|
||||
*/
|
||||
function getUnquotedValue(value) {
|
||||
// Trim quotes
|
||||
if (isQuote(value.charCodeAt(0))) {
|
||||
value = value.slice(1);
|
||||
}
|
||||
if (isQuote(value.charCodeAt(value.length - 1))) {
|
||||
value = value.slice(0, -1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses given string as list of HTML attributes.
|
||||
* @param src A fragment to parse. If `name` argument is provided, it must be an
|
||||
* opening tag (`<a foo="bar">`), otherwise it should be a fragment between element
|
||||
* name and tag closing angle (`foo="bar"`)
|
||||
* @param name Tag name
|
||||
*/
|
||||
function attributes(src, name) {
|
||||
const result = [];
|
||||
let start = 0;
|
||||
let end = src.length;
|
||||
if (name) {
|
||||
start = name.length + 1;
|
||||
end -= src.slice(-2) === '/>' ? 2 : 1;
|
||||
}
|
||||
const scanner = new Scanner(src, start, end);
|
||||
while (!scanner.eof()) {
|
||||
scanner.eatWhile(isSpace);
|
||||
if (attributeName(scanner)) {
|
||||
const token = {
|
||||
name: scanner.current(),
|
||||
nameStart: scanner.start,
|
||||
nameEnd: scanner.pos
|
||||
};
|
||||
if (scanner.eat(61 /* Equals */) && attributeValue(scanner)) {
|
||||
token.value = scanner.current();
|
||||
token.valueStart = scanner.start;
|
||||
token.valueEnd = scanner.pos;
|
||||
}
|
||||
result.push(token);
|
||||
}
|
||||
else {
|
||||
// Do not break on invalid attributes: we are not validating parser
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Consumes attribute name from given scanner context
|
||||
*/
|
||||
function attributeName(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(42 /* Asterisk */) || scanner.eat(35 /* Hash */)) {
|
||||
// Angular-style directives: `<section *ngIf="showSection">`, `<video #movieplayer ...>`
|
||||
ident(scanner);
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
// Attribute name could be a regular name or expression:
|
||||
// React-style – `<div {...props}>`
|
||||
// Angular-style – `<div [ng-for]>` or `<div *ng-for>`
|
||||
return consumePaired(scanner) || ident(scanner);
|
||||
}
|
||||
/**
|
||||
* Consumes attribute value
|
||||
*/
|
||||
function attributeValue(scanner) {
|
||||
// Supported attribute values are quoted, React-like expressions (`{foo}`)
|
||||
// or unquoted literals
|
||||
return eatQuoted(scanner, opt) || consumePaired(scanner) || unquoted(scanner);
|
||||
}
|
||||
/**
|
||||
* Returns clean (unquoted) value of `name` attribute
|
||||
*/
|
||||
function getAttributeValue(attrs, name) {
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const attr = attrs[i];
|
||||
if (attr.name === name) {
|
||||
return attr.value && getUnquotedValue(attr.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes unquoted value
|
||||
*/
|
||||
function unquoted(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eatWhile(isUnquoted)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const cdataOpen = toCharCodes('<![CDATA[');
|
||||
const cdataClose = toCharCodes(']]>');
|
||||
const commentOpen = toCharCodes('<!--');
|
||||
const commentClose = toCharCodes('-->');
|
||||
const piStart = toCharCodes('<?');
|
||||
const piEnd = toCharCodes('?>');
|
||||
const erbStart = toCharCodes('<%');
|
||||
const erbEnd = toCharCodes('%>');
|
||||
/**
|
||||
* Performs fast scan of given source code: for each tag found it invokes callback
|
||||
* with tag name, its type (open, close, self-close) and range in original source.
|
||||
* Unlike regular scanner, fast scanner doesn’t provide info about attributes to
|
||||
* reduce object allocations hence increase performance.
|
||||
* If `callback` returns `false`, scanner stops parsing.
|
||||
* @param special List of “special” HTML tags which should be ignored. Most likely
|
||||
* it’s a "script" and "style" tags.
|
||||
*/
|
||||
function scan(source, callback, options) {
|
||||
const scanner = new Scanner(source);
|
||||
const special = options ? options.special : null;
|
||||
const allTokens = options ? options.allTokens : false;
|
||||
let type;
|
||||
let name;
|
||||
let nameStart;
|
||||
let nameEnd;
|
||||
let nameCodes;
|
||||
let found = false;
|
||||
let piName = null;
|
||||
while (!scanner.eof()) {
|
||||
const start = scanner.pos;
|
||||
if (cdata(scanner)) {
|
||||
if (allTokens && callback('#cdata', 4 /* CData */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (comment(scanner)) {
|
||||
if (allTokens && callback('#comment', 6 /* Comment */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (erb(scanner)) {
|
||||
if (allTokens && callback('#erb', 7 /* ERB */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (piName = processingInstruction(scanner)) {
|
||||
if (allTokens && callback(piName, 5 /* ProcessingInstruction */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (scanner.eat(60 /* LeftAngle */)) {
|
||||
// Maybe a tag name?
|
||||
type = scanner.eat(47 /* Slash */) ? 2 /* Close */ : 1 /* Open */;
|
||||
nameStart = scanner.pos;
|
||||
if (ident(scanner)) {
|
||||
// Consumed tag name
|
||||
nameEnd = scanner.pos;
|
||||
if (type !== 2 /* Close */) {
|
||||
skipAttributes(scanner);
|
||||
scanner.eatWhile(isSpace);
|
||||
if (scanner.eat(47 /* Slash */)) {
|
||||
type = 3 /* SelfClose */;
|
||||
}
|
||||
}
|
||||
if (scanner.eat(62 /* RightAngle */)) {
|
||||
// Tag properly closed
|
||||
name = scanner.substring(nameStart, nameEnd);
|
||||
if (callback(name, type, start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
if (type === 1 /* Open */ && special && isSpecial(special, name, source, start, scanner.pos)) {
|
||||
// Found opening tag of special element: we should skip
|
||||
// scanner contents until we find closing tag
|
||||
nameCodes = toCharCodes(name);
|
||||
found = false;
|
||||
while (!scanner.eof()) {
|
||||
if (consumeClosing(scanner, nameCodes)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
scanner.pos++;
|
||||
}
|
||||
if (found && callback(name, 2 /* Close */, scanner.start, scanner.pos) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Skips attributes in current tag context
|
||||
*/
|
||||
function skipAttributes(scanner) {
|
||||
while (!scanner.eof()) {
|
||||
scanner.eatWhile(isSpace);
|
||||
if (attributeName(scanner)) {
|
||||
if (scanner.eat(61 /* Equals */)) {
|
||||
attributeValue(scanner);
|
||||
}
|
||||
}
|
||||
else if (isTerminator(scanner.peek())) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
scanner.pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Consumes closing tag with given name from scanner
|
||||
*/
|
||||
function consumeClosing(scanner, name) {
|
||||
const start = scanner.pos;
|
||||
if (scanner.eat(60 /* LeftAngle */) && scanner.eat(47 /* Slash */) && consumeArray(scanner, name) && scanner.eat(62 /* RightAngle */)) {
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Consumes CDATA from given scanner
|
||||
*/
|
||||
function cdata(scanner) {
|
||||
return consumeSection(scanner, cdataOpen, cdataClose, true);
|
||||
}
|
||||
/**
|
||||
* Consumes comments from given scanner
|
||||
*/
|
||||
function comment(scanner) {
|
||||
return consumeSection(scanner, commentOpen, commentClose, true);
|
||||
}
|
||||
/**
|
||||
* Consumes processing instruction from given scanner. If consumed, returns
|
||||
* processing instruction name
|
||||
*/
|
||||
function processingInstruction(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, piStart) && ident(scanner)) {
|
||||
const name = scanner.current();
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, piEnd)) {
|
||||
break;
|
||||
}
|
||||
eatQuoted(scanner) || scanner.pos++;
|
||||
}
|
||||
scanner.start = start;
|
||||
return name;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Consumes ERB-style entity: `<% ... %>` or `<%= ... %>`
|
||||
*/
|
||||
function erb(scanner) {
|
||||
const start = scanner.pos;
|
||||
if (consumeArray(scanner, erbStart)) {
|
||||
while (!scanner.eof()) {
|
||||
if (consumeArray(scanner, erbEnd)) {
|
||||
break;
|
||||
}
|
||||
eatQuoted(scanner) || scanner.pos++;
|
||||
}
|
||||
scanner.start = start;
|
||||
return true;
|
||||
}
|
||||
scanner.pos = start;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given tag name should be considered as special
|
||||
*/
|
||||
function isSpecial(special, name, source, start, end) {
|
||||
if (name in special) {
|
||||
const typeValues = special[name];
|
||||
if (!Array.isArray(typeValues)) {
|
||||
return true;
|
||||
}
|
||||
const attrs = attributes(source.substring(start + name.length + 1, end - 1));
|
||||
return typeValues.includes(getAttributeValue(attrs, 'type') || '');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matched tag for given `pos` location in XML/HTML `source`
|
||||
*/
|
||||
function match(source, pos, opt) {
|
||||
// Since we expect large input document, we’ll use pooling technique
|
||||
// for storing tag data to reduce memory pressure and improve performance
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
let result = null;
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 1 /* Open */ && isSelfClose(name, options)) {
|
||||
// Found empty element in HTML mode, mark is as self-closing
|
||||
type = 3 /* SelfClose */;
|
||||
}
|
||||
if (type === 1 /* Open */) {
|
||||
// Allocate tag object from pool
|
||||
stack.push(allocTag(pool, name, start, end));
|
||||
}
|
||||
else if (type === 3 /* SelfClose */) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched given self-closing tag
|
||||
result = {
|
||||
name,
|
||||
attributes: getAttributes(source, start, end, name),
|
||||
open: [start, end]
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const tag = last(stack);
|
||||
if (tag && tag.name === name) {
|
||||
// Matching closing tag found
|
||||
if (tag.start < pos && pos < end) {
|
||||
result = {
|
||||
name,
|
||||
attributes: getAttributes(source, tag.start, tag.end, name),
|
||||
open: [tag.start, tag.end],
|
||||
close: [start, end]
|
||||
};
|
||||
return false;
|
||||
}
|
||||
else if (stack.length) {
|
||||
// Release tag object for further re-use
|
||||
releaseTag(pool, stack.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in outward direction
|
||||
*/
|
||||
function balancedOutward(source, pos, opt) {
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
const result = [];
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 2 /* Close */) {
|
||||
const tag = last(stack);
|
||||
if (tag && tag.name === name) { // XXX check for invalid tag names?
|
||||
// Matching closing tag found, check if matched pair is a candidate
|
||||
// for outward balancing
|
||||
if (tag.start < pos && pos < end) {
|
||||
result.push({
|
||||
name,
|
||||
open: [tag.start, tag.end],
|
||||
close: [start, end]
|
||||
});
|
||||
}
|
||||
// Release tag object for further re-use
|
||||
releaseTag(pool, stack.pop());
|
||||
}
|
||||
}
|
||||
else if (type === 3 /* SelfClose */ || isSelfClose(name, options)) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched self-closed tag
|
||||
result.push({ name, open: [start, end] });
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(allocTag(pool, name, start, end));
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in inward direction
|
||||
*/
|
||||
function balancedInward(source, pos, opt) {
|
||||
// Collecting tags for inward balancing is a bit trickier: we have to store
|
||||
// first child of every matched tag until we find the one that matches given
|
||||
// location
|
||||
const pool = [];
|
||||
const stack = [];
|
||||
const options = createOptions(opt);
|
||||
const result = [];
|
||||
const alloc = (name, start, end) => {
|
||||
if (pool.length) {
|
||||
const tag = pool.pop();
|
||||
tag.name = name;
|
||||
tag.ranges.push(start, end);
|
||||
return tag;
|
||||
}
|
||||
return { name, ranges: [start, end] };
|
||||
};
|
||||
const release = (tag) => {
|
||||
tag.ranges.length = 0;
|
||||
tag.firstChild = void 0;
|
||||
pool.push(tag);
|
||||
};
|
||||
scan(source, (name, type, start, end) => {
|
||||
if (type === 2 /* Close */) {
|
||||
if (!stack.length) {
|
||||
// Some sort of lone closing tag, ignore it
|
||||
return;
|
||||
}
|
||||
let tag = last(stack);
|
||||
if (tag.name === name) { // XXX check for invalid tag names?
|
||||
// Matching closing tag found, check if matched pair is a candidate
|
||||
// for outward balancing
|
||||
if (tag.ranges[0] <= pos && pos <= end) {
|
||||
result.push({
|
||||
name,
|
||||
open: tag.ranges.slice(0, 2),
|
||||
close: [start, end]
|
||||
});
|
||||
while (tag.firstChild) {
|
||||
const child = tag.firstChild;
|
||||
const res = {
|
||||
name: child.name,
|
||||
open: child.ranges.slice(0, 2)
|
||||
};
|
||||
if (child.ranges.length > 2) {
|
||||
res.close = child.ranges.slice(2, 4);
|
||||
}
|
||||
result.push(res);
|
||||
release(tag);
|
||||
tag = child;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
stack.pop();
|
||||
const parent = last(stack);
|
||||
if (parent && !parent.firstChild) {
|
||||
// No first child in parent node: store current tag
|
||||
tag.ranges.push(start, end);
|
||||
parent.firstChild = tag;
|
||||
}
|
||||
else {
|
||||
release(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 3 /* SelfClose */ || isSelfClose(name, options)) {
|
||||
if (start < pos && pos < end) {
|
||||
// Matched self-closed tag, no need to look further
|
||||
result.push({ name, open: [start, end] });
|
||||
return false;
|
||||
}
|
||||
const parent = last(stack);
|
||||
if (parent && !parent.firstChild) {
|
||||
parent.firstChild = alloc(name, start, end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(alloc(name, start, end));
|
||||
}
|
||||
}, options);
|
||||
stack.length = pool.length = 0;
|
||||
return result;
|
||||
}
|
||||
function allocTag(pool, name, start, end) {
|
||||
if (pool.length) {
|
||||
const tag = pool.pop();
|
||||
tag.name = name;
|
||||
tag.start = start;
|
||||
tag.end = end;
|
||||
return tag;
|
||||
}
|
||||
return { name, start, end };
|
||||
}
|
||||
function releaseTag(pool, tag) {
|
||||
pool.push(tag);
|
||||
}
|
||||
/**
|
||||
* Returns parsed attributes from given source
|
||||
*/
|
||||
function getAttributes(source, start, end, name) {
|
||||
const tokens = attributes(source.slice(start, end), name);
|
||||
tokens.forEach(attr => {
|
||||
attr.nameStart += start;
|
||||
attr.nameEnd += start;
|
||||
if (attr.value != null) {
|
||||
attr.valueStart += start;
|
||||
attr.valueEnd += start;
|
||||
}
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
/**
|
||||
* Check if given tag is self-close for current parsing context
|
||||
*/
|
||||
function isSelfClose(name, options) {
|
||||
return !options.xml && options.empty.includes(name);
|
||||
}
|
||||
function last(arr) {
|
||||
return arr.length ? arr[arr.length - 1] : null;
|
||||
}
|
||||
|
||||
export default match;
|
||||
export { attributes, balancedInward, balancedOutward, createOptions, scan };
|
||||
//# sourceMappingURL=html-matcher.es.js.map
|
||||
1
node_modules/@emmetio/html-matcher/dist/html-matcher.es.js.map
generated
vendored
1
node_modules/@emmetio/html-matcher/dist/html-matcher.es.js.map
generated
vendored
File diff suppressed because one or more lines are too long
38
node_modules/@emmetio/html-matcher/dist/index.d.ts
generated
vendored
38
node_modules/@emmetio/html-matcher/dist/index.d.ts
generated
vendored
@@ -1,38 +0,0 @@
|
||||
import { ScannerOptions } from './utils';
|
||||
import scan from './scan';
|
||||
import attributes, { AttributeToken } from './attributes';
|
||||
export { scan, attributes, AttributeToken };
|
||||
export { createOptions, ScannerOptions, ElementType, FastScanCallback } from './utils';
|
||||
declare type TagRange = [number, number];
|
||||
export interface MatchedTag {
|
||||
/** Name of matched tag */
|
||||
name: string;
|
||||
/** List of tag attributes */
|
||||
attributes: AttributeToken[];
|
||||
/** Range of opening tag */
|
||||
open: TagRange;
|
||||
/** Range of closing tag. If absent, tag is self-closing */
|
||||
close?: TagRange;
|
||||
}
|
||||
export interface BalancedTag {
|
||||
/** Name of balanced tag */
|
||||
name: string;
|
||||
/** Range of opening tag */
|
||||
open: TagRange;
|
||||
/** Range of closing tag. If absent, tag is self-closing */
|
||||
close?: TagRange;
|
||||
}
|
||||
/**
|
||||
* Finds matched tag for given `pos` location in XML/HTML `source`
|
||||
*/
|
||||
export default function match(source: string, pos: number, opt?: Partial<ScannerOptions>): MatchedTag | null;
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in outward direction
|
||||
*/
|
||||
export declare function balancedOutward(source: string, pos: number, opt?: Partial<ScannerOptions>): BalancedTag[];
|
||||
/**
|
||||
* Returns balanced tag model: a list of all XML/HTML tags that could possibly match
|
||||
* given location when moving in inward direction
|
||||
*/
|
||||
export declare function balancedInward(source: string, pos: number, opt?: Partial<ScannerOptions>): BalancedTag[];
|
||||
11
node_modules/@emmetio/html-matcher/dist/scan.d.ts
generated
vendored
11
node_modules/@emmetio/html-matcher/dist/scan.d.ts
generated
vendored
@@ -1,11 +0,0 @@
|
||||
import { FastScanCallback, ScannerOptions } from './utils';
|
||||
/**
|
||||
* Performs fast scan of given source code: for each tag found it invokes callback
|
||||
* with tag name, its type (open, close, self-close) and range in original source.
|
||||
* Unlike regular scanner, fast scanner doesn’t provide info about attributes to
|
||||
* reduce object allocations hence increase performance.
|
||||
* If `callback` returns `false`, scanner stops parsing.
|
||||
* @param special List of “special” HTML tags which should be ignored. Most likely
|
||||
* it’s a "script" and "style" tags.
|
||||
*/
|
||||
export default function scan(source: string, callback: FastScanCallback, options?: ScannerOptions): void;
|
||||
123
node_modules/@emmetio/html-matcher/dist/utils.d.ts
generated
vendored
123
node_modules/@emmetio/html-matcher/dist/utils.d.ts
generated
vendored
@@ -1,123 +0,0 @@
|
||||
import Scanner from '@emmetio/scanner';
|
||||
export declare type FastScanCallback = (name: string, type: ElementType, start: number, end: number) => false | any;
|
||||
export declare const enum ElementType {
|
||||
Open = 1,
|
||||
Close = 2,
|
||||
SelfClose = 3,
|
||||
CData = 4,
|
||||
ProcessingInstruction = 5,
|
||||
Comment = 6,
|
||||
ERB = 7
|
||||
}
|
||||
export interface SpecialType {
|
||||
[tagName: string]: string[] | null;
|
||||
}
|
||||
export declare const enum Chars {
|
||||
/** `-` character */
|
||||
Dash = 45,
|
||||
/** `.` character */
|
||||
Dot = 46,
|
||||
/** `/` character */
|
||||
Slash = 47,
|
||||
/** `:` character */
|
||||
Colon = 58,
|
||||
/** `<` character */
|
||||
LeftAngle = 60,
|
||||
/** `>` character */
|
||||
RightAngle = 62,
|
||||
/** `(` character */
|
||||
LeftRound = 40,
|
||||
/** `)` character */
|
||||
RightRound = 41,
|
||||
/** `[` character */
|
||||
LeftSquare = 91,
|
||||
/** `]` character */
|
||||
RightSquare = 93,
|
||||
/** `{` character */
|
||||
LeftCurly = 123,
|
||||
/** `}` character */
|
||||
RightCurly = 125,
|
||||
/** `_` character */
|
||||
Underscore = 95,
|
||||
/** `=` character */
|
||||
Equals = 61,
|
||||
/** `*` character */
|
||||
Asterisk = 42,
|
||||
/** `#` character */
|
||||
Hash = 35
|
||||
}
|
||||
export interface ScannerOptions {
|
||||
/**
|
||||
* Parses given source as XML document. It alters how should-be-empty
|
||||
* elements are treated: for example, in XML mode parser will try to locate
|
||||
* closing pair for `<br>` tag
|
||||
*/
|
||||
xml: boolean;
|
||||
/**
|
||||
* List of tags that should have special parsing rules, e.g. should not parse
|
||||
* inner content and skip to closing tag. Key is a tag name that should be
|
||||
* considered special and value is either empty (always mark element as special)
|
||||
* or list of `type` attribute values, which, if present with one of this value,
|
||||
* make element special
|
||||
*/
|
||||
special: SpecialType;
|
||||
/**
|
||||
* List of elements that should be treated as empty (e.g. without closing tag)
|
||||
* in non-XML syntax
|
||||
*/
|
||||
empty: string[];
|
||||
/**
|
||||
* If enabled, scanner callback will receive XML tokes, including comment, cdata
|
||||
* and processing instructions. If disabled, only tags are emitted
|
||||
*/
|
||||
allTokens: boolean;
|
||||
}
|
||||
/** Options for `Scanner` utils */
|
||||
export declare const opt: {
|
||||
throws: boolean;
|
||||
};
|
||||
export declare function createOptions(options?: Partial<ScannerOptions>): ScannerOptions;
|
||||
/**
|
||||
* Converts given string into array of character codes
|
||||
*/
|
||||
export declare function toCharCodes(str: string): number[];
|
||||
/**
|
||||
* Consumes array of character codes from given scanner
|
||||
*/
|
||||
export declare function consumeArray(scanner: Scanner, codes: number[]): boolean;
|
||||
/**
|
||||
* Consumes section from given string which starts with `open` character codes
|
||||
* and ends with `close` character codes
|
||||
* @return Returns `true` if section was consumed
|
||||
*/
|
||||
export declare function consumeSection(scanner: Scanner, open: number[], close: number[], allowUnclosed?: boolean): boolean;
|
||||
/**
|
||||
* Check if given character can be used as a start of tag name or attribute
|
||||
*/
|
||||
export declare function nameStartChar(ch: number): boolean;
|
||||
/**
|
||||
* Check if given character can be used in a tag or attribute name
|
||||
*/
|
||||
export declare function nameChar(ch: number): boolean;
|
||||
/**
|
||||
* Consumes identifier from given scanner
|
||||
*/
|
||||
export declare function ident(scanner: Scanner): boolean;
|
||||
/**
|
||||
* Check if given code is tag terminator
|
||||
*/
|
||||
export declare function isTerminator(code: number): boolean;
|
||||
/**
|
||||
* Check if given character code is valid unquoted value
|
||||
*/
|
||||
export declare function isUnquoted(code: number): boolean;
|
||||
/**
|
||||
* Consumes paired tokens (like `[` and `]`) with respect of nesting and embedded
|
||||
* quoted values
|
||||
* @return `true` if paired token was consumed
|
||||
*/
|
||||
export declare function consumePaired(scanner: Scanner): boolean;
|
||||
/**
|
||||
* Returns unquoted value of given string
|
||||
*/
|
||||
export declare function getUnquotedValue(value: string): string;
|
||||
47
node_modules/@emmetio/html-matcher/package.json
generated
vendored
47
node_modules/@emmetio/html-matcher/package.json
generated
vendored
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/html-matcher",
|
||||
"version": "1.3.0",
|
||||
"description": "Minimalistic and ultra-fast HTML parser & matcher",
|
||||
"main": "./dist/html-matcher.cjs.js",
|
||||
"module": "./dist/html-matcher.es.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"dependencies": {
|
||||
"@emmetio/scanner": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^12.7.5",
|
||||
"mocha": "^7.1.1",
|
||||
"rollup": "^2.6.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-typescript2": "^0.27.0",
|
||||
"ts-node": "^8.8.2",
|
||||
"tslint": "^6.1.1",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"lint": "tslint ./src/*.ts",
|
||||
"build": "rollup -c",
|
||||
"clean": "rm -rf ./dist",
|
||||
"prepare": "npm run lint && npm test && npm run clean && npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/html-matcher.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/html-matcher/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/html-matcher#readme",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"mocha": {
|
||||
"require": "ts-node/register",
|
||||
"spec": "./test/*.ts"
|
||||
}
|
||||
}
|
||||
21
node_modules/@emmetio/scanner/LICENSE
generated
vendored
21
node_modules/@emmetio/scanner/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Sergey Chikuyonok <serge.che@gmail.com>
|
||||
|
||||
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.
|
||||
49
node_modules/@emmetio/scanner/package.json
generated
vendored
49
node_modules/@emmetio/scanner/package.json
generated
vendored
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/scanner",
|
||||
"version": "1.0.4",
|
||||
"description": "Scans given text character-by-character",
|
||||
"main": "./scanner.cjs",
|
||||
"module": "./scanner.js",
|
||||
"types": "./scanner.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"import": "./scanner.js",
|
||||
"require": "./scanner.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"clean": "rimraf ./scanner.* ./*.d.ts",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/stream-reader.git"
|
||||
},
|
||||
"keywords": [
|
||||
"emmet",
|
||||
"stream",
|
||||
"scanner"
|
||||
],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/emmet/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/emmet#readme",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^10.0.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.11.18",
|
||||
"mocha": "^10.2.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"rollup": "^3.9.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"mocha": {
|
||||
"loader": "ts-node/esm",
|
||||
"spec": "./test/*.ts"
|
||||
},
|
||||
"gitHead": "fce2127ece65adbb293a40aa0577e4558658c559"
|
||||
}
|
||||
253
node_modules/@emmetio/scanner/scanner.cjs
generated
vendored
253
node_modules/@emmetio/scanner/scanner.cjs
generated
vendored
@@ -1,253 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
const defaultQuotedOptions = {
|
||||
escape: 92,
|
||||
throws: false
|
||||
};
|
||||
/**
|
||||
* Check if given code is a number
|
||||
*/
|
||||
function isNumber(code) {
|
||||
return code > 47 && code < 58;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
*/
|
||||
function isAlpha(code, from, to) {
|
||||
from = from || 65; // A
|
||||
to = to || 90; // Z
|
||||
code &= ~32; // quick hack to convert any char code to uppercase char code
|
||||
return code >= from && code <= to;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
*/
|
||||
function isAlphaNumeric(code) {
|
||||
return isNumber(code) || isAlpha(code);
|
||||
}
|
||||
function isAlphaNumericWord(code) {
|
||||
return isNumber(code) || isAlphaWord(code);
|
||||
}
|
||||
function isAlphaWord(code) {
|
||||
return code === 95 /* _ */ || isAlpha(code);
|
||||
}
|
||||
/**
|
||||
* Check for Umlauts i.e. ä, Ä, ö, Ö, ü and Ü
|
||||
*/
|
||||
function isUmlaut(code) {
|
||||
return code === 196
|
||||
|| code == 214
|
||||
|| code === 220
|
||||
|| code === 228
|
||||
|| code === 246
|
||||
|| code === 252;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a white-space character: a space character
|
||||
* or line breaks
|
||||
*/
|
||||
function isWhiteSpace(code) {
|
||||
return code === 32 /* space */
|
||||
|| code === 9 /* tab */
|
||||
|| code === 160; /* non-breaking space */
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a space character
|
||||
*/
|
||||
function isSpace(code) {
|
||||
return isWhiteSpace(code)
|
||||
|| code === 10 /* LF */
|
||||
|| code === 13; /* CR */
|
||||
}
|
||||
/**
|
||||
* Consumes 'single' or "double"-quoted string from given string, if possible
|
||||
* @return `true` if quoted string was consumed. The contents of quoted string
|
||||
* will be available as `stream.current()`
|
||||
*/
|
||||
function eatQuoted(stream, options) {
|
||||
options = Object.assign(Object.assign({}, defaultQuotedOptions), options);
|
||||
const start = stream.pos;
|
||||
const quote = stream.peek();
|
||||
if (stream.eat(isQuote)) {
|
||||
while (!stream.eof()) {
|
||||
switch (stream.next()) {
|
||||
case quote:
|
||||
stream.start = start;
|
||||
return true;
|
||||
case options.escape:
|
||||
stream.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we’re here then stream wasn’t properly consumed.
|
||||
// Revert stream and decide what to do
|
||||
stream.pos = start;
|
||||
if (options.throws) {
|
||||
throw stream.error('Unable to consume quoted string');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a quote character
|
||||
*/
|
||||
function isQuote(code) {
|
||||
return code === 39 /* ' */ || code === 34 /* " */;
|
||||
}
|
||||
/**
|
||||
* Eats paired characters substring, for example `(foo)` or `[bar]`
|
||||
* @param open Character code of pair opening
|
||||
* @param close Character code of pair closing
|
||||
* @return Returns `true` if character pair was successfully consumed, it’s
|
||||
* content will be available as `stream.current()`
|
||||
*/
|
||||
function eatPair(stream, open, close, options) {
|
||||
options = Object.assign(Object.assign({}, defaultQuotedOptions), options);
|
||||
const start = stream.pos;
|
||||
if (stream.eat(open)) {
|
||||
let stack = 1;
|
||||
let ch;
|
||||
while (!stream.eof()) {
|
||||
if (eatQuoted(stream, options)) {
|
||||
continue;
|
||||
}
|
||||
ch = stream.next();
|
||||
if (ch === open) {
|
||||
stack++;
|
||||
}
|
||||
else if (ch === close) {
|
||||
stack--;
|
||||
if (!stack) {
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ch === options.escape) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
// If we’re here then paired character can’t be consumed
|
||||
stream.pos = start;
|
||||
if (options.throws) {
|
||||
throw stream.error(`Unable to find matching pair for ${String.fromCharCode(open)}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
class Scanner {
|
||||
constructor(str, start, end) {
|
||||
if (end == null && typeof str === 'string') {
|
||||
end = str.length;
|
||||
}
|
||||
this.string = str;
|
||||
this.pos = this.start = start || 0;
|
||||
this.end = end || 0;
|
||||
}
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
*/
|
||||
eof() {
|
||||
return this.pos >= this.end;
|
||||
}
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
*/
|
||||
limit(start, end) {
|
||||
return new Scanner(this.string, start, end);
|
||||
}
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
*/
|
||||
peek() {
|
||||
return this.string.charCodeAt(this.pos);
|
||||
}
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
*/
|
||||
next() {
|
||||
if (this.pos < this.string.length) {
|
||||
return this.string.charCodeAt(this.pos++);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
*/
|
||||
eat(match) {
|
||||
const ch = this.peek();
|
||||
const ok = typeof match === 'function' ? match(ch) : ch === match;
|
||||
if (ok) {
|
||||
this.next();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
*/
|
||||
eatWhile(match) {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) { /* */ }
|
||||
return this.pos !== start;
|
||||
}
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
*/
|
||||
backUp(n) {
|
||||
this.pos -= (n || 1);
|
||||
}
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
*/
|
||||
current() {
|
||||
return this.substring(this.start, this.pos);
|
||||
}
|
||||
/**
|
||||
* Returns substring for given range
|
||||
*/
|
||||
substring(start, end) {
|
||||
return this.string.slice(start, end);
|
||||
}
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
*/
|
||||
error(message, pos = this.pos) {
|
||||
return new ScannerError(`${message} at ${pos + 1}`, pos, this.string);
|
||||
}
|
||||
}
|
||||
class ScannerError extends Error {
|
||||
constructor(message, pos, str) {
|
||||
super(message);
|
||||
this.pos = pos;
|
||||
this.string = str;
|
||||
}
|
||||
}
|
||||
|
||||
exports.ScannerError = ScannerError;
|
||||
exports.default = Scanner;
|
||||
exports.eatPair = eatPair;
|
||||
exports.eatQuoted = eatQuoted;
|
||||
exports.isAlpha = isAlpha;
|
||||
exports.isAlphaNumeric = isAlphaNumeric;
|
||||
exports.isAlphaNumericWord = isAlphaNumericWord;
|
||||
exports.isAlphaWord = isAlphaWord;
|
||||
exports.isNumber = isNumber;
|
||||
exports.isQuote = isQuote;
|
||||
exports.isSpace = isSpace;
|
||||
exports.isUmlaut = isUmlaut;
|
||||
exports.isWhiteSpace = isWhiteSpace;
|
||||
//# sourceMappingURL=scanner.cjs.map
|
||||
1
node_modules/@emmetio/scanner/scanner.cjs.map
generated
vendored
1
node_modules/@emmetio/scanner/scanner.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
71
node_modules/@emmetio/scanner/scanner.d.ts
generated
vendored
71
node_modules/@emmetio/scanner/scanner.d.ts
generated
vendored
@@ -1,71 +0,0 @@
|
||||
export * from './utils.js';
|
||||
type MatchFn = (ch: number) => boolean;
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
export default class Scanner {
|
||||
/** Current string */
|
||||
string: string;
|
||||
/** Current scanner position */
|
||||
pos: number;
|
||||
/** Lower range limit where string reader is available */
|
||||
start: number;
|
||||
/** Upper range limit where string reader is available */
|
||||
end: number;
|
||||
constructor(str: string, start?: number, end?: number);
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
*/
|
||||
eof(): boolean;
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
*/
|
||||
limit(start?: number, end?: number): Scanner;
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
*/
|
||||
peek(): number;
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
*/
|
||||
next(): number | undefined;
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
*/
|
||||
eat(match: number | MatchFn): boolean;
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
*/
|
||||
eatWhile(match: number | MatchFn): boolean;
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
*/
|
||||
backUp(n: number): void;
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
*/
|
||||
current(): string;
|
||||
/**
|
||||
* Returns substring for given range
|
||||
*/
|
||||
substring(start: number, end?: number): string;
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
*/
|
||||
error(message: string, pos?: number): ScannerError;
|
||||
}
|
||||
export declare class ScannerError extends Error {
|
||||
pos: number;
|
||||
string: string;
|
||||
constructor(message: string, pos: number, str: string);
|
||||
}
|
||||
237
node_modules/@emmetio/scanner/scanner.js
generated
vendored
237
node_modules/@emmetio/scanner/scanner.js
generated
vendored
@@ -1,237 +0,0 @@
|
||||
const defaultQuotedOptions = {
|
||||
escape: 92,
|
||||
throws: false
|
||||
};
|
||||
/**
|
||||
* Check if given code is a number
|
||||
*/
|
||||
function isNumber(code) {
|
||||
return code > 47 && code < 58;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
*/
|
||||
function isAlpha(code, from, to) {
|
||||
from = from || 65; // A
|
||||
to = to || 90; // Z
|
||||
code &= ~32; // quick hack to convert any char code to uppercase char code
|
||||
return code >= from && code <= to;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
*/
|
||||
function isAlphaNumeric(code) {
|
||||
return isNumber(code) || isAlpha(code);
|
||||
}
|
||||
function isAlphaNumericWord(code) {
|
||||
return isNumber(code) || isAlphaWord(code);
|
||||
}
|
||||
function isAlphaWord(code) {
|
||||
return code === 95 /* _ */ || isAlpha(code);
|
||||
}
|
||||
/**
|
||||
* Check for Umlauts i.e. ä, Ä, ö, Ö, ü and Ü
|
||||
*/
|
||||
function isUmlaut(code) {
|
||||
return code === 196
|
||||
|| code == 214
|
||||
|| code === 220
|
||||
|| code === 228
|
||||
|| code === 246
|
||||
|| code === 252;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a white-space character: a space character
|
||||
* or line breaks
|
||||
*/
|
||||
function isWhiteSpace(code) {
|
||||
return code === 32 /* space */
|
||||
|| code === 9 /* tab */
|
||||
|| code === 160; /* non-breaking space */
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a space character
|
||||
*/
|
||||
function isSpace(code) {
|
||||
return isWhiteSpace(code)
|
||||
|| code === 10 /* LF */
|
||||
|| code === 13; /* CR */
|
||||
}
|
||||
/**
|
||||
* Consumes 'single' or "double"-quoted string from given string, if possible
|
||||
* @return `true` if quoted string was consumed. The contents of quoted string
|
||||
* will be available as `stream.current()`
|
||||
*/
|
||||
function eatQuoted(stream, options) {
|
||||
options = Object.assign(Object.assign({}, defaultQuotedOptions), options);
|
||||
const start = stream.pos;
|
||||
const quote = stream.peek();
|
||||
if (stream.eat(isQuote)) {
|
||||
while (!stream.eof()) {
|
||||
switch (stream.next()) {
|
||||
case quote:
|
||||
stream.start = start;
|
||||
return true;
|
||||
case options.escape:
|
||||
stream.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we’re here then stream wasn’t properly consumed.
|
||||
// Revert stream and decide what to do
|
||||
stream.pos = start;
|
||||
if (options.throws) {
|
||||
throw stream.error('Unable to consume quoted string');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if given character code is a quote character
|
||||
*/
|
||||
function isQuote(code) {
|
||||
return code === 39 /* ' */ || code === 34 /* " */;
|
||||
}
|
||||
/**
|
||||
* Eats paired characters substring, for example `(foo)` or `[bar]`
|
||||
* @param open Character code of pair opening
|
||||
* @param close Character code of pair closing
|
||||
* @return Returns `true` if character pair was successfully consumed, it’s
|
||||
* content will be available as `stream.current()`
|
||||
*/
|
||||
function eatPair(stream, open, close, options) {
|
||||
options = Object.assign(Object.assign({}, defaultQuotedOptions), options);
|
||||
const start = stream.pos;
|
||||
if (stream.eat(open)) {
|
||||
let stack = 1;
|
||||
let ch;
|
||||
while (!stream.eof()) {
|
||||
if (eatQuoted(stream, options)) {
|
||||
continue;
|
||||
}
|
||||
ch = stream.next();
|
||||
if (ch === open) {
|
||||
stack++;
|
||||
}
|
||||
else if (ch === close) {
|
||||
stack--;
|
||||
if (!stack) {
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ch === options.escape) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
// If we’re here then paired character can’t be consumed
|
||||
stream.pos = start;
|
||||
if (options.throws) {
|
||||
throw stream.error(`Unable to find matching pair for ${String.fromCharCode(open)}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
class Scanner {
|
||||
constructor(str, start, end) {
|
||||
if (end == null && typeof str === 'string') {
|
||||
end = str.length;
|
||||
}
|
||||
this.string = str;
|
||||
this.pos = this.start = start || 0;
|
||||
this.end = end || 0;
|
||||
}
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
*/
|
||||
eof() {
|
||||
return this.pos >= this.end;
|
||||
}
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
*/
|
||||
limit(start, end) {
|
||||
return new Scanner(this.string, start, end);
|
||||
}
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
*/
|
||||
peek() {
|
||||
return this.string.charCodeAt(this.pos);
|
||||
}
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
*/
|
||||
next() {
|
||||
if (this.pos < this.string.length) {
|
||||
return this.string.charCodeAt(this.pos++);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
*/
|
||||
eat(match) {
|
||||
const ch = this.peek();
|
||||
const ok = typeof match === 'function' ? match(ch) : ch === match;
|
||||
if (ok) {
|
||||
this.next();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
*/
|
||||
eatWhile(match) {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) { /* */ }
|
||||
return this.pos !== start;
|
||||
}
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
*/
|
||||
backUp(n) {
|
||||
this.pos -= (n || 1);
|
||||
}
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
*/
|
||||
current() {
|
||||
return this.substring(this.start, this.pos);
|
||||
}
|
||||
/**
|
||||
* Returns substring for given range
|
||||
*/
|
||||
substring(start, end) {
|
||||
return this.string.slice(start, end);
|
||||
}
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
*/
|
||||
error(message, pos = this.pos) {
|
||||
return new ScannerError(`${message} at ${pos + 1}`, pos, this.string);
|
||||
}
|
||||
}
|
||||
class ScannerError extends Error {
|
||||
constructor(message, pos, str) {
|
||||
super(message);
|
||||
this.pos = pos;
|
||||
this.string = str;
|
||||
}
|
||||
}
|
||||
|
||||
export { ScannerError, Scanner as default, eatPair, eatQuoted, isAlpha, isAlphaNumeric, isAlphaNumericWord, isAlphaWord, isNumber, isQuote, isSpace, isUmlaut, isWhiteSpace };
|
||||
//# sourceMappingURL=scanner.js.map
|
||||
1
node_modules/@emmetio/scanner/scanner.js.map
generated
vendored
1
node_modules/@emmetio/scanner/scanner.js.map
generated
vendored
File diff suppressed because one or more lines are too long
53
node_modules/@emmetio/scanner/utils.d.ts
generated
vendored
53
node_modules/@emmetio/scanner/utils.d.ts
generated
vendored
@@ -1,53 +0,0 @@
|
||||
import type Scanner from './scanner.js';
|
||||
interface QuotedOptions {
|
||||
/** A character code of quote-escape symbol */
|
||||
escape?: number;
|
||||
/** Throw error if quotes string can’t be properly consumed */
|
||||
throws?: boolean;
|
||||
}
|
||||
/**
|
||||
* Check if given code is a number
|
||||
*/
|
||||
export declare function isNumber(code: number): boolean;
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
*/
|
||||
export declare function isAlpha(code: number, from?: number, to?: number): boolean;
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
*/
|
||||
export declare function isAlphaNumeric(code: number): boolean;
|
||||
export declare function isAlphaNumericWord(code: number): boolean;
|
||||
export declare function isAlphaWord(code: number): boolean;
|
||||
/**
|
||||
* Check for Umlauts i.e. ä, Ä, ö, Ö, ü and Ü
|
||||
*/
|
||||
export declare function isUmlaut(code: number): boolean;
|
||||
/**
|
||||
* Check if given character code is a white-space character: a space character
|
||||
* or line breaks
|
||||
*/
|
||||
export declare function isWhiteSpace(code: number): boolean;
|
||||
/**
|
||||
* Check if given character code is a space character
|
||||
*/
|
||||
export declare function isSpace(code: number): boolean;
|
||||
/**
|
||||
* Consumes 'single' or "double"-quoted string from given string, if possible
|
||||
* @return `true` if quoted string was consumed. The contents of quoted string
|
||||
* will be available as `stream.current()`
|
||||
*/
|
||||
export declare function eatQuoted(stream: Scanner, options?: QuotedOptions): boolean;
|
||||
/**
|
||||
* Check if given character code is a quote character
|
||||
*/
|
||||
export declare function isQuote(code: number): boolean;
|
||||
/**
|
||||
* Eats paired characters substring, for example `(foo)` or `[bar]`
|
||||
* @param open Character code of pair opening
|
||||
* @param close Character code of pair closing
|
||||
* @return Returns `true` if character pair was successfully consumed, it’s
|
||||
* content will be available as `stream.current()`
|
||||
*/
|
||||
export declare function eatPair(stream: Scanner, open: number, close: number, options?: QuotedOptions): boolean;
|
||||
export {};
|
||||
21
node_modules/@emmetio/stream-reader-utils/LICENSE
generated
vendored
21
node_modules/@emmetio/stream-reader-utils/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Emmet.io
|
||||
|
||||
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.
|
||||
167
node_modules/@emmetio/stream-reader-utils/dist/stream-reader-utils.cjs.js
generated
vendored
167
node_modules/@emmetio/stream-reader-utils/dist/stream-reader-utils.cjs.js
generated
vendored
@@ -1,167 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
/**
|
||||
* Methods for consuming quoted values
|
||||
*/
|
||||
|
||||
const SINGLE_QUOTE = 39; // '
|
||||
const DOUBLE_QUOTE = 34; // "
|
||||
|
||||
const defaultOptions = {
|
||||
escape: 92, // \ character
|
||||
throws: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Consumes 'single' or "double"-quoted string from given string, if possible
|
||||
* @param {StreamReader} stream
|
||||
* @param {Number} options.escape A character code of quote-escape symbol
|
||||
* @param {Boolean} options.throws Throw error if quotes string can’t be properly consumed
|
||||
* @return {Boolean} `true` if quoted string was consumed. The contents
|
||||
* of quoted string will be availabe as `stream.current()`
|
||||
*/
|
||||
var eatQuoted = function(stream, options) {
|
||||
options = options ? Object.assign({}, defaultOptions, options) : defaultOptions;
|
||||
const start = stream.pos;
|
||||
const quote = stream.peek();
|
||||
|
||||
if (stream.eat(isQuote)) {
|
||||
while (!stream.eof()) {
|
||||
switch (stream.next()) {
|
||||
case quote:
|
||||
stream.start = start;
|
||||
return true;
|
||||
|
||||
case options.escape:
|
||||
stream.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we’re here then stream wasn’t properly consumed.
|
||||
// Revert stream and decide what to do
|
||||
stream.pos = start;
|
||||
|
||||
if (options.throws) {
|
||||
throw stream.error('Unable to consume quoted string');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
function isQuote(code) {
|
||||
return code === SINGLE_QUOTE || code === DOUBLE_QUOTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given code is a number
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNumber(code) {
|
||||
return code > 47 && code < 58;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
* @param {Number} code
|
||||
* @param {Number} [from]
|
||||
* @param {Number} [to]
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isAlpha(code, from, to) {
|
||||
from = from || 65; // A
|
||||
to = to || 90; // Z
|
||||
code &= ~32; // quick hack to convert any char code to uppercase char code
|
||||
|
||||
return code >= from && code <= to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isAlphaNumeric(code) {
|
||||
return isNumber(code) || isAlpha(code);
|
||||
}
|
||||
|
||||
function isWhiteSpace(code) {
|
||||
return code === 32 /* space */
|
||||
|| code === 9 /* tab */
|
||||
|| code === 160; /* non-breaking space */
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is a space
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isSpace(code) {
|
||||
return isWhiteSpace(code)
|
||||
|| code === 10 /* LF */
|
||||
|| code === 13; /* CR */
|
||||
}
|
||||
|
||||
const defaultOptions$1 = {
|
||||
escape: 92, // \ character
|
||||
throws: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Eats paired characters substring, for example `(foo)` or `[bar]`
|
||||
* @param {StreamReader} stream
|
||||
* @param {Number} open Character code of pair openinig
|
||||
* @param {Number} close Character code of pair closing
|
||||
* @param {Object} [options]
|
||||
* @return {Boolean} Returns `true` if chacarter pair was successfully
|
||||
* consumed, it’s content will be available as `stream.current()`
|
||||
*/
|
||||
function eatPair(stream, open, close, options) {
|
||||
options = options ? Object.assign({}, defaultOptions$1, options) : defaultOptions$1;
|
||||
const start = stream.pos;
|
||||
|
||||
if (stream.eat(open)) {
|
||||
let stack = 1, ch;
|
||||
|
||||
while (!stream.eof()) {
|
||||
if (eatQuoted(stream, options)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ch = stream.next();
|
||||
if (ch === open) {
|
||||
stack++;
|
||||
} else if (ch === close) {
|
||||
stack--;
|
||||
if (!stack) {
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
} else if (ch === options.escape) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
|
||||
// If we’re here then paired character can’t be consumed
|
||||
stream.pos = start;
|
||||
|
||||
if (options.throws) {
|
||||
throw stream.error(`Unable to find matching pair for ${String.fromCharCode(open)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exports.eatQuoted = eatQuoted;
|
||||
exports.isQuote = isQuote;
|
||||
exports.isAlpha = isAlpha;
|
||||
exports.isNumber = isNumber;
|
||||
exports.isAlphaNumeric = isAlphaNumeric;
|
||||
exports.isSpace = isSpace;
|
||||
exports.isWhiteSpace = isWhiteSpace;
|
||||
exports.eatPair = eatPair;
|
||||
156
node_modules/@emmetio/stream-reader-utils/dist/stream-reader-utils.es.js
generated
vendored
156
node_modules/@emmetio/stream-reader-utils/dist/stream-reader-utils.es.js
generated
vendored
@@ -1,156 +0,0 @@
|
||||
/**
|
||||
* Methods for consuming quoted values
|
||||
*/
|
||||
|
||||
const SINGLE_QUOTE = 39; // '
|
||||
const DOUBLE_QUOTE = 34; // "
|
||||
|
||||
const defaultOptions = {
|
||||
escape: 92, // \ character
|
||||
throws: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Consumes 'single' or "double"-quoted string from given string, if possible
|
||||
* @param {StreamReader} stream
|
||||
* @param {Number} options.escape A character code of quote-escape symbol
|
||||
* @param {Boolean} options.throws Throw error if quotes string can’t be properly consumed
|
||||
* @return {Boolean} `true` if quoted string was consumed. The contents
|
||||
* of quoted string will be availabe as `stream.current()`
|
||||
*/
|
||||
var eatQuoted = function(stream, options) {
|
||||
options = options ? Object.assign({}, defaultOptions, options) : defaultOptions;
|
||||
const start = stream.pos;
|
||||
const quote = stream.peek();
|
||||
|
||||
if (stream.eat(isQuote)) {
|
||||
while (!stream.eof()) {
|
||||
switch (stream.next()) {
|
||||
case quote:
|
||||
stream.start = start;
|
||||
return true;
|
||||
|
||||
case options.escape:
|
||||
stream.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we’re here then stream wasn’t properly consumed.
|
||||
// Revert stream and decide what to do
|
||||
stream.pos = start;
|
||||
|
||||
if (options.throws) {
|
||||
throw stream.error('Unable to consume quoted string');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
function isQuote(code) {
|
||||
return code === SINGLE_QUOTE || code === DOUBLE_QUOTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given code is a number
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNumber(code) {
|
||||
return code > 47 && code < 58;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha code (letter through A to Z)
|
||||
* @param {Number} code
|
||||
* @param {Number} [from]
|
||||
* @param {Number} [to]
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isAlpha(code, from, to) {
|
||||
from = from || 65; // A
|
||||
to = to || 90; // Z
|
||||
code &= ~32; // quick hack to convert any char code to uppercase char code
|
||||
|
||||
return code >= from && code <= to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is alpha-numeric (letter through A to Z or number)
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isAlphaNumeric(code) {
|
||||
return isNumber(code) || isAlpha(code);
|
||||
}
|
||||
|
||||
function isWhiteSpace(code) {
|
||||
return code === 32 /* space */
|
||||
|| code === 9 /* tab */
|
||||
|| code === 160; /* non-breaking space */
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given character code is a space
|
||||
* @param {Number} code
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isSpace(code) {
|
||||
return isWhiteSpace(code)
|
||||
|| code === 10 /* LF */
|
||||
|| code === 13; /* CR */
|
||||
}
|
||||
|
||||
const defaultOptions$1 = {
|
||||
escape: 92, // \ character
|
||||
throws: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Eats paired characters substring, for example `(foo)` or `[bar]`
|
||||
* @param {StreamReader} stream
|
||||
* @param {Number} open Character code of pair openinig
|
||||
* @param {Number} close Character code of pair closing
|
||||
* @param {Object} [options]
|
||||
* @return {Boolean} Returns `true` if chacarter pair was successfully
|
||||
* consumed, it’s content will be available as `stream.current()`
|
||||
*/
|
||||
function eatPair(stream, open, close, options) {
|
||||
options = options ? Object.assign({}, defaultOptions$1, options) : defaultOptions$1;
|
||||
const start = stream.pos;
|
||||
|
||||
if (stream.eat(open)) {
|
||||
let stack = 1, ch;
|
||||
|
||||
while (!stream.eof()) {
|
||||
if (eatQuoted(stream, options)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ch = stream.next();
|
||||
if (ch === open) {
|
||||
stack++;
|
||||
} else if (ch === close) {
|
||||
stack--;
|
||||
if (!stack) {
|
||||
stream.start = start;
|
||||
return true;
|
||||
}
|
||||
} else if (ch === options.escape) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
|
||||
// If we’re here then paired character can’t be consumed
|
||||
stream.pos = start;
|
||||
|
||||
if (options.throws) {
|
||||
throw stream.error(`Unable to find matching pair for ${String.fromCharCode(open)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export { eatQuoted, isQuote, isAlpha, isNumber, isAlphaNumeric, isSpace, isWhiteSpace, eatPair };
|
||||
34
node_modules/@emmetio/stream-reader-utils/package.json
generated
vendored
34
node_modules/@emmetio/stream-reader-utils/package.json
generated
vendored
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/stream-reader-utils",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "dist/stream-reader-utils.cjs.js",
|
||||
"module": "dist/stream-reader-utils.es.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@emmetio/stream-reader": "^2.0.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.0",
|
||||
"babel-register": "^6.24.0",
|
||||
"mocha": "^3.2.0",
|
||||
"rollup": "^0.41.6",
|
||||
"rollup-watch": "^3.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -wc"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/stream-reader-utils.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/stream-reader-utils/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/stream-reader-utils#readme"
|
||||
}
|
||||
21
node_modules/@emmetio/stream-reader/LICENSE
generated
vendored
21
node_modules/@emmetio/stream-reader/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Emmet.io
|
||||
|
||||
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.
|
||||
130
node_modules/@emmetio/stream-reader/dist/stream-reader.cjs.js
generated
vendored
130
node_modules/@emmetio/stream-reader/dist/stream-reader.cjs.js
generated
vendored
@@ -1,130 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
class StreamReader {
|
||||
constructor(string, start, end) {
|
||||
if (end == null && typeof string === 'string') {
|
||||
end = string.length;
|
||||
}
|
||||
|
||||
this.string = string;
|
||||
this.pos = this.start = start || 0;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eof() {
|
||||
return this.pos >= this.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
* @param {Point} start
|
||||
* @param {Point} end
|
||||
* @return {StreamReader}
|
||||
*/
|
||||
limit(start, end) {
|
||||
return new this.constructor(this.string, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
* @returns {Number}
|
||||
*/
|
||||
peek() {
|
||||
return this.string.charCodeAt(this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
* @returns {Number}
|
||||
*/
|
||||
next() {
|
||||
if (this.pos < this.string.length) {
|
||||
return this.string.charCodeAt(this.pos++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
* @param {Number|Function} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eat(match) {
|
||||
const ch = this.peek();
|
||||
const ok = typeof match === 'function' ? match(ch) : ch === match;
|
||||
|
||||
if (ok) {
|
||||
this.next();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
* @param {Object} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eatWhile(match) {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) {}
|
||||
return this.pos !== start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
* @param {Number} n
|
||||
*/
|
||||
backUp(n) {
|
||||
this.pos -= (n || 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
* @returns {String}
|
||||
*/
|
||||
current() {
|
||||
return this.substring(this.start, this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns substring for given range
|
||||
* @param {Number} start
|
||||
* @param {Number} [end]
|
||||
* @return {String}
|
||||
*/
|
||||
substring(start, end) {
|
||||
return this.string.slice(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
* @param {String} message
|
||||
* @return {Error}
|
||||
*/
|
||||
error(message) {
|
||||
const err = new Error(`${message} at char ${this.pos + 1}`);
|
||||
err.originalMessage = message;
|
||||
err.pos = this.pos;
|
||||
err.string = this.string;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StreamReader;
|
||||
128
node_modules/@emmetio/stream-reader/dist/stream-reader.es.js
generated
vendored
128
node_modules/@emmetio/stream-reader/dist/stream-reader.es.js
generated
vendored
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* A streaming, character code-based string reader
|
||||
*/
|
||||
class StreamReader {
|
||||
constructor(string, start, end) {
|
||||
if (end == null && typeof string === 'string') {
|
||||
end = string.length;
|
||||
}
|
||||
|
||||
this.string = string;
|
||||
this.pos = this.start = start || 0;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eof() {
|
||||
return this.pos >= this.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given `start` and `end`
|
||||
* range. E.g. its `eof()` method will look at `end` property, not actual
|
||||
* stream end
|
||||
* @param {Point} start
|
||||
* @param {Point} end
|
||||
* @return {StreamReader}
|
||||
*/
|
||||
limit(start, end) {
|
||||
return new this.constructor(this.string, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character code in the stream without advancing it.
|
||||
* Will return NaN at the end of the file.
|
||||
* @returns {Number}
|
||||
*/
|
||||
peek() {
|
||||
return this.string.charCodeAt(this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character in the stream and advances it.
|
||||
* Also returns <code>undefined</code> when no more characters are available.
|
||||
* @returns {Number}
|
||||
*/
|
||||
next() {
|
||||
if (this.pos < this.string.length) {
|
||||
return this.string.charCodeAt(this.pos++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
* the given argument, it is consumed and returned.
|
||||
* Otherwise, `false` is returned.
|
||||
* @param {Number|Function} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eat(match) {
|
||||
const ch = this.peek();
|
||||
const ok = typeof match === 'function' ? match(ch) : ch === match;
|
||||
|
||||
if (ok) {
|
||||
this.next();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly calls <code>eat</code> with the given argument, until it
|
||||
* fails. Returns <code>true</code> if any characters were eaten.
|
||||
* @param {Object} match
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
eatWhile(match) {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) {}
|
||||
return this.pos !== start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
* @param {Number} n
|
||||
*/
|
||||
backUp(n) {
|
||||
this.pos -= (n || 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string between the start of the current token and the
|
||||
* current stream position.
|
||||
* @returns {String}
|
||||
*/
|
||||
current() {
|
||||
return this.substring(this.start, this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns substring for given range
|
||||
* @param {Number} start
|
||||
* @param {Number} [end]
|
||||
* @return {String}
|
||||
*/
|
||||
substring(start, end) {
|
||||
return this.string.slice(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
* @param {String} message
|
||||
* @return {Error}
|
||||
*/
|
||||
error(message) {
|
||||
const err = new Error(`${message} at char ${this.pos + 1}`);
|
||||
err.originalMessage = message;
|
||||
err.pos = this.pos;
|
||||
err.string = this.string;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
export default StreamReader;
|
||||
33
node_modules/@emmetio/stream-reader/package.json
generated
vendored
33
node_modules/@emmetio/stream-reader/package.json
generated
vendored
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "@emmetio/stream-reader",
|
||||
"version": "2.2.0",
|
||||
"description": "Reads text as stream",
|
||||
"main": "dist/stream-reader.cjs.js",
|
||||
"module": "dist/stream-reader.es.js",
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"prepublish": "npm run test && npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emmetio/stream-reader.git"
|
||||
},
|
||||
"keywords": [
|
||||
"emmet",
|
||||
"stream",
|
||||
"reader"
|
||||
],
|
||||
"author": "Sergey Chikuyonok <serge.che@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/emmetio/stream-reader/issues"
|
||||
},
|
||||
"homepage": "https://github.com/emmetio/stream-reader#readme",
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.18.0",
|
||||
"babel-register": "^6.18.0",
|
||||
"mocha": "^3.2.0",
|
||||
"rollup": "^0.41.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user