$
This commit is contained in:
111
node_modules/@riotjs/parser/CHANGELOG.md
generated
vendored
Normal file
111
node_modules/@riotjs/parser/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
# Changes for riot-parser
|
||||
|
||||
### v4.3.1
|
||||
- Improve the inline `<script>` tags check
|
||||
|
||||
### v4.3.0
|
||||
- Add support for inline script tags (`<script src='path/to/the/script'>`)
|
||||
|
||||
### v4.2.1
|
||||
- Fix make sure comments nodes will be generated via tree builder
|
||||
|
||||
### v4.2.0
|
||||
- Add the extraction of comment nodes text
|
||||
- Fix https://github.com/riot/riot/issues/2836
|
||||
|
||||
### v4.1.2
|
||||
- Update generated bundle fixing discrepancy between source files and bundled output
|
||||
|
||||
### v4.1.1
|
||||
- Fix end value of the root node
|
||||
|
||||
### v4.1.0
|
||||
- Add expose the internal constants to the public API
|
||||
|
||||
### v4.0.3
|
||||
- Fix https://github.com/riot/riot/issues/2723 for real this time
|
||||
|
||||
### v4.0.2
|
||||
- Fix parsing of nested svg nodes https://github.com/riot/riot/issues/2723
|
||||
|
||||
### v4.0.1
|
||||
- Fix the creation of the `parts` array in nodes containing expressions
|
||||
|
||||
### v4.0.0
|
||||
- Stable release
|
||||
- Add more tests for the new feautures listed below
|
||||
|
||||
### v4.0.0-rc.2
|
||||
- Fix: support spread attributes together with other attribute expressions on the same DOM node
|
||||
|
||||
### v4.0.0-rc.1
|
||||
- Fix https://github.com/riot/riot/issues/2679
|
||||
- Add support for `<a {href}>` expression attributes shortcuts
|
||||
|
||||
### v0.8.1
|
||||
- Add the `src` folder to the npm publishing files
|
||||
|
||||
### v0.8.0
|
||||
- Add support for the spread attributes `<a {...foo.bar}>`
|
||||
- Fixed the `isCustom` boolean that will be added also to the root nodes
|
||||
|
||||
### v0.6.9
|
||||
- Remove the unecessary PUBLIC_JAVASCRIPT and PRIVATE_JAVASCRIPT nodes
|
||||
|
||||
### v0.5.0
|
||||
- Remove the the useless prefix option
|
||||
- Improve the coverage
|
||||
- Improve the quality of the source code
|
||||
|
||||
### v0.4.0
|
||||
- Add the [`dom-nodes`](https://github.com/riot/dom-nodes) dependecy to improve the output
|
||||
- Add the `isCustom`, `isBoolean`, `isVoid`, `isSelfClosing` and `isRaw` boolean node attributes
|
||||
|
||||
### v0.3.0
|
||||
- Fix treeBuilder issues
|
||||
- Improve coverage
|
||||
- Improve code maintainability
|
||||
|
||||
### v0.2.0
|
||||
- Add `voidTags` to the exports
|
||||
|
||||
### v0.1.0
|
||||
- Enhance the javascript parsing: the javascript node will contain nested nodes containing the private and the public javascript methods
|
||||
- Add the PUBLIC_JAVASCRIPT and PRIVATE_JAVASCRIPT nodes
|
||||
- Change the `attr` to `attributes` and `expr` to `expressions` keys
|
||||
|
||||
### v0.0.6
|
||||
- Tree-builder support for 'if/else/elseif' tags (avoid unexpected closing tag errors).
|
||||
- Fix to text nodes only escaping the fist block of whitespace.
|
||||
|
||||
### v0.0.5
|
||||
- Now, attribute names are lowercased in the builder, only for empty namespaces (i.e. not svg).
|
||||
|
||||
### v0.0.4
|
||||
- Included TEXTAREA as special tag that can contain only raw text and expressions.
|
||||
- For SVG tags, now the `ns` property is the full URI http://www.w3.org/2000/svg.
|
||||
- The `children` property of TAGs is renamed to `nodes`.
|
||||
|
||||
### v0.0.3
|
||||
- The default builder is integrated in this module and injected in the parser.
|
||||
- Only two versions, node CommonJS (transpiled to ES5) and ES6 modules (untranspiled).
|
||||
- The `nodeTypes` property of TagParser is removed, now is in a separated submodule.
|
||||
- Exposing `skipES6TL` to skip ES6 Template Literals.
|
||||
- Reduction of code size, `skipRegex` is imported from npm.
|
||||
- Source files (ES6) are moved to the "lib/" directory.
|
||||
- Remove dependency on `Object.assign`.
|
||||
- Updated devDependencies.
|
||||
|
||||
### v0.0.2 (UNPUBLISHED)
|
||||
- Added suport for SVG en the tests.
|
||||
- Added test/builder/tree-builder2.js as sample.
|
||||
- Support for self-closing script/style tags.
|
||||
- The `replace` property of attributes and text is discarded and there's a new property `unescape` is an array containing the positions of the escape characters (relative to the whole buffer).
|
||||
- Matching literal regexes is a bit faster now.
|
||||
- Fixes incorrect regex that matches literal regexes.
|
||||
|
||||
### v0.0.1
|
||||
- First public release
|
||||
|
||||
# TODO
|
||||
- Support for case sensitive properties in SVG elements.
|
21
node_modules/@riotjs/parser/LICENSE
generated
vendored
Normal file
21
node_modules/@riotjs/parser/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Riot
|
||||
|
||||
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.
|
140
node_modules/@riotjs/parser/README.md
generated
vendored
Normal file
140
node_modules/@riotjs/parser/README.md
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
# parser
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Code Quality][codeclimate-image]][codeclimate-url]
|
||||
[![NPM version][npm-version-image]][npm-url]
|
||||
[![NPM downloads][npm-downloads-image]][npm-url]
|
||||
[![MIT License][license-image]][license-url]
|
||||
[![Coverage Status][coverage-image]][coverage-url]
|
||||
|
||||
Minimal, loose html parser for Riot tags
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
npm i @riotjs/parser --save
|
||||
```
|
||||
|
||||
The package has two modules:
|
||||
|
||||
```js
|
||||
// Use as: parser(options).parse(code, startPosition)
|
||||
const parser = require('@riotjs/parser').default
|
||||
|
||||
// The enum NodeTypes (a plain JS object) that contains the values of the
|
||||
// type property of the nodes emited by tagParser (and more).
|
||||
const nodeTypes = require('@riotjs/parser').nodeTypes
|
||||
```
|
||||
|
||||
ES6 modules export:
|
||||
|
||||
```js
|
||||
import parser, { nodeTypes } from '@riotjs/parser'
|
||||
```
|
||||
|
||||
This parser is a low-level tool that builds a simple array of objects with information about the given html fragment, readed secuencially. It is designed to parse one single tag and not entire html pages, the tag closing the root element ends the parsing.
|
||||
|
||||
There are 3 main node types:
|
||||
|
||||
* Tags - HTMLElements, including SCRIPT and STYLE elements.
|
||||
* Comments - Ignored by default.
|
||||
* Text - Text nodes.
|
||||
|
||||
Opening tags can contain attributes. Text and attribute values can contain expressions.
|
||||
|
||||
There's no support for untagged JavaScript block.
|
||||
|
||||
The value returned by the parser is an object like this:
|
||||
|
||||
```js
|
||||
{
|
||||
data, // String of the given html fragment with no changes.
|
||||
output // Array of objects with information about the parsed tags.
|
||||
}
|
||||
```
|
||||
|
||||
The first element of `output` is the opening tag of the root element.
|
||||
|
||||
The parsing stops when the closing tag of the root is found, so the last node have the ending position.
|
||||
|
||||
|
||||
### Commands
|
||||
|
||||
* Build: `npm run build`
|
||||
* Test: `npm t`
|
||||
* Samples: `npm run samples`
|
||||
|
||||
## Tag names
|
||||
|
||||
Both, html and Riot tag names must start with a 7 bit letter (`[a-zA-Z]`) followed by zero o more ISO-8859-1 characters, except those in `[\x00-\x2F\x7F-\xA0>/]`.
|
||||
|
||||
If the first letter is not found, it becomes simple text.
|
||||
Any non-recognized character ends the tag name (`'/'` behaves like whitespace).
|
||||
|
||||
All the tag names are converted to lower case.
|
||||
|
||||
## Openning Tags
|
||||
|
||||
Start with a `'<'` followed by a [tag name](#tag-names) or the character `'!'` that signals the start of a [comment](#comments), `DOCTYPE` or `CDATA` declaration (last two are parsed as comments).
|
||||
|
||||
Against the html5 specs, tags ending with `'/>'` are preserved as self-closing tags (the builder must handle this).
|
||||
|
||||
## Closing tags
|
||||
|
||||
They are included in the output, except for void or self-closing tags, and its name include the first slash.
|
||||
|
||||
## Attributes
|
||||
|
||||
Accepts all characters as the tag names and more.
|
||||
|
||||
An equal sign (`'='`) separates the name of the value. If there's no name, this `'='` is the first character of the name (yes). The value can be empty.
|
||||
|
||||
One or more slashes (`'/'`) behaves like whitespace. In the name, the slash splits the name generating two attributes, even if the name was quoted.
|
||||
|
||||
The first `>` anywhere in the openning tag ends the attribute list, except if this is in a quoted value.
|
||||
|
||||
All attribute names are converted to lowercase and the unquoted values are trimmed.
|
||||
|
||||
## Comments
|
||||
|
||||
Must start with `'<!--'`. The next following `'-->'` or the end of file ends the comment.
|
||||
|
||||
Comments in short notation, starting with `'<!'` (without `'--'`), ends at the first `'>'`.
|
||||
|
||||
By default, comments are discarted.
|
||||
|
||||
## Expressions
|
||||
|
||||
Expressions may be contained in attribute values or text nodes.
|
||||
The default delimiters are `'{'` and `'}'`.
|
||||
|
||||
There may be more tan one expression as part of one attribute value or text node, or only one replacing the entire value or node.
|
||||
|
||||
When used as the whole attribute value, there's no need to enclose the expression inside quotes, even if the expression contains whitespace.
|
||||
|
||||
Single and double quotes can be nested inside the expression.
|
||||
|
||||
To emit opening (left) brackets as literal text wherever an opening bracket is expected, the bracket must be prefixed with a backslash (the JS escape char `'\'`).
|
||||
This character is preserved in the output, but the parser will add a `replace` property for the attribute or node containing the escaped bracket, whose value is the bracket itself.
|
||||
|
||||
## Options
|
||||
|
||||
* `comments` - Pass `true` to preserve the comments.
|
||||
* `brackets` - Array of two string with the left/right brackets used to extract expressions.
|
||||
|
||||
[travis-image]:https://img.shields.io/travis/riot/parser.svg?style=flat-square
|
||||
[travis-url]:https://travis-ci.org/riot/parser
|
||||
|
||||
[license-image]:http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
|
||||
[license-url]:LICENSE.txt
|
||||
|
||||
[npm-version-image]:http://img.shields.io/npm/v/@riotjs/parser.svg?style=flat-square
|
||||
[npm-downloads-image]:http://img.shields.io/npm/dm/@riotjs/parser.svg?style=flat-square
|
||||
[npm-url]:https://npmjs.org/package/@riotjs/parser
|
||||
|
||||
[coverage-image]:https://img.shields.io/coveralls/riot/parser/master.svg?style=flat-square
|
||||
[coverage-url]:https://coveralls.io/r/riot/parser/?branch=master
|
||||
|
||||
[codeclimate-image]:https://api.codeclimate.com/v1/badges/5db4f1c96a43e3736cf0/maintainability
|
||||
[codeclimate-url]:https://codeclimate.com/github/riot/parser
|
||||
|
1787
node_modules/@riotjs/parser/index.js
generated
vendored
Normal file
1787
node_modules/@riotjs/parser/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
62
node_modules/@riotjs/parser/package.json
generated
vendored
Normal file
62
node_modules/@riotjs/parser/package.json
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "@riotjs/parser",
|
||||
"version": "4.3.1",
|
||||
"description": "The parser for Riot tags",
|
||||
"main": "./index.js",
|
||||
"module": "./src/index.js",
|
||||
"jsnext:main": "./src/index.js",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.2",
|
||||
"npm": ">=3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "nyc mocha ./test/index",
|
||||
"test-debug": "mocha --inspect-brk ./test",
|
||||
"cov": "nyc report --reporter=text-lcov | coveralls",
|
||||
"cov-html": "nyc report --reporter=html",
|
||||
"pretest": "npm run build",
|
||||
"lint": "eslint src test",
|
||||
"build": "rollup -c",
|
||||
"samples": "node ./test/samples.js",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"files": [
|
||||
"*.js",
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"html",
|
||||
"html5",
|
||||
"tag",
|
||||
"parser",
|
||||
"javascript"
|
||||
],
|
||||
"author": {
|
||||
"name": "aMarCruz",
|
||||
"email": "amarcruz@yahoo.com",
|
||||
"url": "https://github.com/aMarCruz"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/riot/parser"
|
||||
},
|
||||
"homepage": "https://github.com/riot/parser",
|
||||
"bugs": "https://github.com/riot/parser/issues",
|
||||
"readme": "https://github.com/riot/parser/blob/master/README.md",
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"coveralls": "^3.1.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-config-riot": "^3.0.0",
|
||||
"mocha": "^8.1.1",
|
||||
"nyc": "^15.1.0",
|
||||
"rollup": "^2.23.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"curri": "^1.0.1",
|
||||
"dom-nodes": "^1.1.3"
|
||||
}
|
||||
}
|
11
node_modules/@riotjs/parser/rollup.config.js
generated
vendored
Normal file
11
node_modules/@riotjs/parser/rollup.config.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
|
||||
export default {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
name: 'parser',
|
||||
format: 'cjs',
|
||||
file: './index.js'
|
||||
},
|
||||
plugins: [resolve()]
|
||||
}
|
16
node_modules/@riotjs/parser/src/constants.js
generated
vendored
Normal file
16
node_modules/@riotjs/parser/src/constants.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export const JAVASCRIPT_OUTPUT_NAME = 'javascript'
|
||||
export const CSS_OUTPUT_NAME = 'css'
|
||||
export const TEMPLATE_OUTPUT_NAME = 'template'
|
||||
|
||||
// Tag names
|
||||
export const JAVASCRIPT_TAG = 'script'
|
||||
export const STYLE_TAG = 'style'
|
||||
export const TEXTAREA_TAG = 'textarea'
|
||||
|
||||
// Boolean attributes
|
||||
export const IS_RAW = 'isRaw'
|
||||
export const IS_SELF_CLOSING = 'isSelfClosing'
|
||||
export const IS_VOID = 'isVoid'
|
||||
export const IS_BOOLEAN = 'isBoolean'
|
||||
export const IS_CUSTOM = 'isCustom'
|
||||
export const IS_SPREAD = 'isSpread'
|
19
node_modules/@riotjs/parser/src/index.js
generated
vendored
Normal file
19
node_modules/@riotjs/parser/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as c from './constants'
|
||||
import * as types from './node-types'
|
||||
import parser from './parser'
|
||||
|
||||
/**
|
||||
* Expose the internal constants
|
||||
*/
|
||||
export const constants = c
|
||||
|
||||
/**
|
||||
* The nodeTypes definition
|
||||
*/
|
||||
export const nodeTypes = types
|
||||
|
||||
/*
|
||||
* Factory function to create instances of the parser
|
||||
*/
|
||||
export default parser
|
||||
|
10
node_modules/@riotjs/parser/src/messages.js
generated
vendored
Normal file
10
node_modules/@riotjs/parser/src/messages.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export const rootTagNotFound = 'Root tag not found.'
|
||||
export const unclosedTemplateLiteral = 'Unclosed ES6 template literal.'
|
||||
export const unexpectedEndOfFile = 'Unexpected end of file.'
|
||||
export const unexpectedNamedTag = 'Unexpected tag <%1>'
|
||||
export const unclosedComment = 'Unclosed comment.'
|
||||
export const unclosedNamedBlock = 'Unclosed "%1" block.'
|
||||
export const duplicatedNamedTag = 'Multiple inline "<%1>" tags are not supported.'
|
||||
export const expectedAndInsteadSaw = 'Expected "</%1>" and instead saw "<%2>".'
|
||||
export const unexpectedCharInExpression = 'Unexpected character %1.'
|
||||
export const unclosedExpression = 'Unclosed expression.'
|
14
node_modules/@riotjs/parser/src/node-types.js
generated
vendored
Normal file
14
node_modules/@riotjs/parser/src/node-types.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Not all the types are handled in this module.
|
||||
*
|
||||
* @enum {number}
|
||||
* @readonly
|
||||
*/
|
||||
export const TAG = 1 /* TAG */
|
||||
export const ATTR = 2 /* ATTR */
|
||||
export const TEXT = 3 /* TEXT */
|
||||
export const CDATA = 4 /* CDATA */
|
||||
export const COMMENT = 8 /* COMMENT */
|
||||
export const DOCUMENT = 9 /* DOCUMENT */
|
||||
export const DOCTYPE = 10 /* DOCTYPE */
|
||||
export const DOCUMENT_FRAGMENT = 11 /* DOCUMENT_FRAGMENT */
|
112
node_modules/@riotjs/parser/src/parser.js
generated
vendored
Normal file
112
node_modules/@riotjs/parser/src/parser.js
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
import {ATTR, TAG} from './node-types'
|
||||
import {rootTagNotFound, unexpectedEndOfFile} from './messages'
|
||||
import attr from './parsers/attribute'
|
||||
import curry from 'curri'
|
||||
import flush from './utils/flush-parser-state'
|
||||
import panic from './utils/panic'
|
||||
import tag from './parsers/tag'
|
||||
import text from './parsers/text'
|
||||
import treeBuilder from './tree-builder'
|
||||
|
||||
/**
|
||||
* Factory for the Parser class, exposing only the `parse` method.
|
||||
* The export adds the Parser class as property.
|
||||
*
|
||||
* @param {Object} options - User Options
|
||||
* @param {Function} customBuilder - Tree builder factory
|
||||
* @returns {Function} Public Parser implementation.
|
||||
*/
|
||||
export default function parser(options, customBuilder) {
|
||||
const state = curry(createParserState)(options, customBuilder || treeBuilder)
|
||||
return {
|
||||
parse: (data) => parse(state(data))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new state object
|
||||
* @param {Object} userOptions - parser options
|
||||
* @param {Function} builder - Tree builder factory
|
||||
* @param {string} data - data to parse
|
||||
* @returns {ParserState} it represents the current parser state
|
||||
*/
|
||||
function createParserState(userOptions, builder, data) {
|
||||
const options = Object.assign({
|
||||
brackets: ['{', '}']
|
||||
}, userOptions)
|
||||
|
||||
return {
|
||||
options,
|
||||
regexCache: {},
|
||||
pos: 0,
|
||||
count: -1,
|
||||
root: null,
|
||||
last: null,
|
||||
scryle: null,
|
||||
builder: builder(data, options),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It creates a raw output of pseudo-nodes with one of three different types,
|
||||
* all of them having a start/end position:
|
||||
*
|
||||
* - TAG -- Opening or closing tags
|
||||
* - TEXT -- Raw text
|
||||
* - COMMENT -- Comments
|
||||
*
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @returns {ParserResult} Result, contains data and output properties.
|
||||
*/
|
||||
function parse(state) {
|
||||
const { data } = state
|
||||
|
||||
walk(state)
|
||||
flush(state)
|
||||
|
||||
if (state.count) {
|
||||
panic(data, state.count > 0 ? unexpectedEndOfFile : rootTagNotFound, state.pos)
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
output: state.builder.get()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser walking recursive function
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @param {string} type - current parsing context
|
||||
* @returns {undefined} void function
|
||||
*/
|
||||
function walk(state, type) {
|
||||
const { data } = state
|
||||
// extend the state adding the tree builder instance and the initial data
|
||||
const length = data.length
|
||||
|
||||
// The "count" property is set to 1 when the first tag is found.
|
||||
// This becomes the root and precedent text or comments are discarded.
|
||||
// So, at the end of the parsing count must be zero.
|
||||
if (state.pos < length && state.count) {
|
||||
walk(state, eat(state, type))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to help iterating on the current parser state
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @param {string} type - current parsing context
|
||||
* @returns {string} parsing context
|
||||
*/
|
||||
function eat(state, type) {
|
||||
switch (type) {
|
||||
case TAG:
|
||||
return tag(state)
|
||||
case ATTR:
|
||||
return attr(state)
|
||||
default:
|
||||
return text(state)
|
||||
}
|
||||
}
|
183
node_modules/@riotjs/parser/src/parsers/attribute.js
generated
vendored
Normal file
183
node_modules/@riotjs/parser/src/parsers/attribute.js
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
import {ATTR, TEXT} from '../node-types'
|
||||
import {ATTR_START, SPREAD_OPERATOR} from '../regex'
|
||||
import {IS_BOOLEAN, IS_SELF_CLOSING, IS_SPREAD} from '../constants'
|
||||
import addToCollection from '../utils/add-to-collection'
|
||||
import execFromPos from '../utils/exec-from-pos'
|
||||
import expr from './expression'
|
||||
import getChunk from '../utils/get-chunk'
|
||||
import {isBoolAttribute} from 'dom-nodes'
|
||||
import memoize from '../utils/memoize'
|
||||
|
||||
const expressionsContentRe = memoize(brackets => RegExp(`(${brackets[0]}[^${brackets[1]}]*?${brackets[1]})`, 'g'))
|
||||
const isSpreadAttribute = name => SPREAD_OPERATOR.test(name)
|
||||
const isAttributeExpression = (name, brackets) => name[0] === brackets[0]
|
||||
const getAttributeEnd = (state, attr) => expr(state, attr, '[>/\\s]', attr.start)
|
||||
|
||||
/**
|
||||
* The more complex parsing is for attributes as it can contain quoted or
|
||||
* unquoted values or expressions.
|
||||
*
|
||||
* @param {ParserStore} state - Parser state
|
||||
* @returns {number} New parser mode.
|
||||
* @private
|
||||
*/
|
||||
export default function attr(state) {
|
||||
const { data, last, pos, root } = state
|
||||
const tag = last // the last (current) tag in the output
|
||||
const _CH = /\S/g // matches the first non-space char
|
||||
const ch = execFromPos(_CH, pos, data)
|
||||
|
||||
switch (true) {
|
||||
case !ch:
|
||||
state.pos = data.length // reaching the end of the buffer with
|
||||
// NodeTypes.ATTR will generate error
|
||||
break
|
||||
case ch[0] === '>':
|
||||
// closing char found. If this is a self-closing tag with the name of the
|
||||
// Root tag, we need decrement the counter as we are changing mode.
|
||||
state.pos = tag.end = _CH.lastIndex
|
||||
if (tag[IS_SELF_CLOSING]) {
|
||||
state.scryle = null // allow selfClosing script/style tags
|
||||
if (root && root.name === tag.name) {
|
||||
state.count-- // "pop" root tag
|
||||
}
|
||||
}
|
||||
return TEXT
|
||||
case ch[0] === '/':
|
||||
state.pos = _CH.lastIndex // maybe. delegate the validation
|
||||
tag[IS_SELF_CLOSING] = true // the next loop
|
||||
break
|
||||
default:
|
||||
delete tag[IS_SELF_CLOSING] // ensure unmark as selfclosing tag
|
||||
setAttribute(state, ch.index, tag)
|
||||
}
|
||||
|
||||
return ATTR
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an attribute and its expressions.
|
||||
*
|
||||
* @param {ParserStore} state - Parser state
|
||||
* @param {number} pos - Starting position of the attribute
|
||||
* @param {Object} tag - Current parent tag
|
||||
* @returns {undefined} void function
|
||||
* @private
|
||||
*/
|
||||
function setAttribute(state, pos, tag) {
|
||||
const { data } = state
|
||||
const expressionContent = expressionsContentRe(state.options.brackets)
|
||||
const re = ATTR_START // (\S[^>/=\s]*)(?:\s*=\s*([^>/])?)? g
|
||||
const start = re.lastIndex = expressionContent.lastIndex = pos // first non-whitespace
|
||||
const attrMatches = re.exec(data)
|
||||
const isExpressionName = isAttributeExpression(attrMatches[1], state.options.brackets)
|
||||
const match = isExpressionName ? [null, expressionContent.exec(data)[1], null] : attrMatches
|
||||
|
||||
if (match) {
|
||||
const end = re.lastIndex
|
||||
const attr = parseAttribute(state, match, start, end, isExpressionName)
|
||||
|
||||
//assert(q && q.type === Mode.TAG, 'no previous tag for the attr!')
|
||||
// Pushes the attribute and shifts the `end` position of the tag (`last`).
|
||||
state.pos = tag.end = attr.end
|
||||
tag.attributes = addToCollection(tag.attributes, attr)
|
||||
}
|
||||
}
|
||||
|
||||
function parseNomalAttribute(state, attr, quote) {
|
||||
const { data } = state
|
||||
let { end } = attr
|
||||
|
||||
if (isBoolAttribute(attr.name)) {
|
||||
attr[IS_BOOLEAN] = true
|
||||
}
|
||||
|
||||
// parse the whole value (if any) and get any expressions on it
|
||||
if (quote) {
|
||||
// Usually, the value's first char (`quote`) is a quote and the lastIndex
|
||||
// (`end`) is the start of the value.
|
||||
let valueStart = end
|
||||
// If it not, this is an unquoted value and we need adjust the start.
|
||||
if (quote !== '"' && quote !== '\'') {
|
||||
quote = '' // first char of value is not a quote
|
||||
valueStart-- // adjust the starting position
|
||||
}
|
||||
|
||||
end = expr(state, attr, quote || '[>/\\s]', valueStart)
|
||||
|
||||
// adjust the bounds of the value and save its content
|
||||
return Object.assign(attr, {
|
||||
value: getChunk(data, valueStart, end),
|
||||
valueStart,
|
||||
end: quote ? ++end : end
|
||||
})
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse expression names <a {href}>
|
||||
* @param {ParserStore} state - Parser state
|
||||
* @param {Object} attr - attribute object parsed
|
||||
* @returns {Object} normalized attribute object
|
||||
*/
|
||||
function parseSpreadAttribute(state, attr) {
|
||||
const end = getAttributeEnd(state, attr)
|
||||
|
||||
return {
|
||||
[IS_SPREAD]: true,
|
||||
start: attr.start,
|
||||
expressions: attr.expressions.map(expr => Object.assign(expr, {
|
||||
text: expr.text.replace(SPREAD_OPERATOR, '').trim()
|
||||
})),
|
||||
end: end
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse expression names <a {href}>
|
||||
* @param {ParserStore} state - Parser state
|
||||
* @param {Object} attr - attribute object parsed
|
||||
* @returns {Object} normalized attribute object
|
||||
*/
|
||||
function parseExpressionNameAttribute(state, attr) {
|
||||
const end = getAttributeEnd(state, attr)
|
||||
|
||||
return {
|
||||
start: attr.start,
|
||||
name: attr.expressions[0].text.trim(),
|
||||
expressions: attr.expressions,
|
||||
end: end
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the attribute values normalising the quotes
|
||||
* @param {ParserStore} state - Parser state
|
||||
* @param {Array} match - results of the attributes regex
|
||||
* @param {number} start - attribute start position
|
||||
* @param {number} end - attribute end position
|
||||
* @param {boolean} isExpressionName - true if the attribute name is an expression
|
||||
* @returns {Object} attribute object
|
||||
*/
|
||||
function parseAttribute(state, match, start, end, isExpressionName) {
|
||||
const attr = {
|
||||
name: match[1],
|
||||
value: '',
|
||||
start,
|
||||
end
|
||||
}
|
||||
|
||||
const quote = match[2] // first letter of value or nothing
|
||||
|
||||
switch (true) {
|
||||
case isSpreadAttribute(attr.name):
|
||||
return parseSpreadAttribute(state, attr)
|
||||
case isExpressionName === true:
|
||||
return parseExpressionNameAttribute(state, attr)
|
||||
default:
|
||||
return parseNomalAttribute(state, attr, quote)
|
||||
}
|
||||
}
|
57
node_modules/@riotjs/parser/src/parsers/comment.js
generated
vendored
Normal file
57
node_modules/@riotjs/parser/src/parsers/comment.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import {COMMENT, TEXT} from '../node-types'
|
||||
import flush from '../utils/flush-parser-state'
|
||||
import panic from '../utils/panic'
|
||||
import {unclosedComment} from '../messages'
|
||||
|
||||
/**
|
||||
* Parses comments in long or short form
|
||||
* (any DOCTYPE & CDATA blocks are parsed as comments).
|
||||
*
|
||||
* @param {ParserState} state - Parser state
|
||||
* @param {string} data - Buffer to parse
|
||||
* @param {number} start - Position of the '<!' sequence
|
||||
* @returns {number} node type id
|
||||
* @private
|
||||
*/
|
||||
export default function comment(state, data, start) {
|
||||
const pos = start + 2 // skip '<!'
|
||||
const isLongComment = data.substr(pos, 2) === '--'
|
||||
const str = isLongComment ? '-->' : '>'
|
||||
const end = data.indexOf(str, pos)
|
||||
|
||||
if (end < 0) {
|
||||
panic(data, unclosedComment, start)
|
||||
}
|
||||
|
||||
pushComment(
|
||||
state,
|
||||
start,
|
||||
end + str.length,
|
||||
data.substring(start, end + str.length)
|
||||
)
|
||||
|
||||
return TEXT
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a comment.
|
||||
*
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @param {number} start - Start position of the tag
|
||||
* @param {number} end - Ending position (last char of the tag)
|
||||
* @param {string} text - Comment content
|
||||
* @returns {undefined} void function
|
||||
* @private
|
||||
*/
|
||||
export function pushComment(state, start, end, text) {
|
||||
state.pos = end
|
||||
if (state.options.comments === true) {
|
||||
flush(state)
|
||||
state.last = {
|
||||
type: COMMENT,
|
||||
start,
|
||||
end,
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
100
node_modules/@riotjs/parser/src/parsers/expression.js
generated
vendored
Normal file
100
node_modules/@riotjs/parser/src/parsers/expression.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import escapeStr from '../utils/escape-str'
|
||||
import exprExtr from '../utils/expr-extr'
|
||||
import panic from '../utils/panic'
|
||||
import pushText from '../utils/push-text'
|
||||
import {unexpectedEndOfFile} from '../messages'
|
||||
/**
|
||||
* Find the end of the attribute value or text node
|
||||
* Extract expressions.
|
||||
* Detect if value have escaped brackets.
|
||||
*
|
||||
* @param {ParserState} state - Parser state
|
||||
* @param {HasExpr} node - Node if attr, info if text
|
||||
* @param {string} endingChars - Ends the value or text
|
||||
* @param {number} start - Starting position
|
||||
* @returns {number} Ending position
|
||||
* @private
|
||||
*/
|
||||
export default function expr(state, node, endingChars, start) {
|
||||
const re = b0re(state, endingChars)
|
||||
|
||||
re.lastIndex = start // reset re position
|
||||
|
||||
const { unescape, expressions, end } = parseExpressions(state, re)
|
||||
|
||||
if (node) {
|
||||
if (unescape) {
|
||||
node.unescape = unescape
|
||||
}
|
||||
if (expressions.length) {
|
||||
node.expressions = expressions
|
||||
}
|
||||
} else {
|
||||
pushText(state, start, end, {expressions, unescape})
|
||||
}
|
||||
|
||||
return end
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a text chunk finding all the expressions in it
|
||||
* @param {ParserState} state - Parser state
|
||||
* @param {RegExp} re - regex to match the expressions contents
|
||||
* @returns {Object} result containing the expression found, the string to unescape and the end position
|
||||
*/
|
||||
function parseExpressions(state, re) {
|
||||
const { data, options } = state
|
||||
const { brackets } = options
|
||||
const expressions = []
|
||||
let unescape, pos, match
|
||||
|
||||
// Anything captured in $1 (closing quote or character) ends the loop...
|
||||
while ((match = re.exec(data)) && !match[1]) {
|
||||
// ...else, we have an opening bracket and maybe an expression.
|
||||
pos = match.index
|
||||
if (data[pos - 1] === '\\') {
|
||||
unescape = match[0] // it is an escaped opening brace
|
||||
} else {
|
||||
const tmpExpr = exprExtr(data, pos, brackets)
|
||||
if (tmpExpr) {
|
||||
expressions.push(tmpExpr)
|
||||
re.lastIndex = tmpExpr.end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Even for text, the parser needs match a closing char
|
||||
if (!match) {
|
||||
panic(data, unexpectedEndOfFile, pos)
|
||||
}
|
||||
|
||||
return {
|
||||
unescape,
|
||||
expressions,
|
||||
end: match.index
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a regex for the given string and the left bracket.
|
||||
* The string is captured in $1.
|
||||
*
|
||||
* @param {ParserState} state - Parser state
|
||||
* @param {string} str - String to search
|
||||
* @returns {RegExp} Resulting regex.
|
||||
* @private
|
||||
*/
|
||||
function b0re(state, str) {
|
||||
const { brackets } = state.options
|
||||
const re = state.regexCache[str]
|
||||
|
||||
if (re) return re
|
||||
|
||||
const b0 = escapeStr(brackets[0])
|
||||
// cache the regex extending the regexCache object
|
||||
Object.assign(state.regexCache, { [str]: new RegExp(`(${str})|${b0}`, 'g') })
|
||||
|
||||
return state.regexCache[str]
|
||||
}
|
49
node_modules/@riotjs/parser/src/parsers/tag.js
generated
vendored
Normal file
49
node_modules/@riotjs/parser/src/parsers/tag.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import {ATTR, TEXT} from '../node-types'
|
||||
import {RE_SCRYLE, TAG_2C, TAG_NAME} from '../regex'
|
||||
import comment from './comment'
|
||||
import execFromPos from '../utils/exec-from-pos'
|
||||
import pushTag from '../utils/push-tag'
|
||||
import pushText from '../utils/push-text'
|
||||
|
||||
/**
|
||||
* Parse the tag following a '<' character, or delegate to other parser
|
||||
* if an invalid tag name is found.
|
||||
*
|
||||
* @param {ParserState} state - Parser state
|
||||
* @returns {number} New parser mode
|
||||
* @private
|
||||
*/
|
||||
export default function tag(state) {
|
||||
const { pos, data } = state // pos of the char following '<'
|
||||
const start = pos - 1 // pos of '<'
|
||||
const str = data.substr(pos, 2) // first two chars following '<'
|
||||
|
||||
switch (true) {
|
||||
case str[0] === '!':
|
||||
return comment(state, data, start)
|
||||
case TAG_2C.test(str):
|
||||
return parseTag(state, start)
|
||||
default:
|
||||
return pushText(state, start, pos) // pushes the '<' as text
|
||||
}
|
||||
}
|
||||
|
||||
function parseTag(state, start) {
|
||||
const { data, pos } = state
|
||||
const re = TAG_NAME // (\/?[^\s>/]+)\s*(>)? g
|
||||
const match = execFromPos(re, pos, data)
|
||||
const end = re.lastIndex
|
||||
const name = match[1].toLowerCase() // $1: tag name including any '/'
|
||||
// script/style block is parsed as another tag to extract attributes
|
||||
if (name in RE_SCRYLE) {
|
||||
state.scryle = name // used by parseText
|
||||
}
|
||||
|
||||
pushTag(state, name, start, end)
|
||||
// only '>' can ends the tag here, the '/' is handled in parseAttribute
|
||||
if (!match[2]) {
|
||||
return ATTR
|
||||
}
|
||||
|
||||
return TEXT
|
||||
}
|
69
node_modules/@riotjs/parser/src/parsers/text.js
generated
vendored
Normal file
69
node_modules/@riotjs/parser/src/parsers/text.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
import {TAG, TEXT} from '../node-types'
|
||||
import {RE_SCRYLE} from '../regex'
|
||||
import {TEXTAREA_TAG} from '../constants'
|
||||
import execFromPos from '../utils/exec-from-pos'
|
||||
import expr from './expression'
|
||||
import panic from '../utils/panic'
|
||||
import pushTag from '../utils/push-tag'
|
||||
import pushText from '../utils/push-text'
|
||||
import {unclosedNamedBlock} from '../messages'
|
||||
|
||||
/**
|
||||
* Parses regular text and script/style blocks ...scryle for short :-)
|
||||
* (the content of script and style is text as well)
|
||||
*
|
||||
* @param {ParserState} state - Parser state
|
||||
* @returns {number} New parser mode.
|
||||
* @private
|
||||
*/
|
||||
export default function text(state) {
|
||||
const { pos, data, scryle } = state
|
||||
|
||||
switch (true) {
|
||||
case typeof scryle === 'string': {
|
||||
const name = scryle
|
||||
const re = RE_SCRYLE[name]
|
||||
const match = execFromPos(re, pos, data)
|
||||
|
||||
if (!match) {
|
||||
panic(data, unclosedNamedBlock.replace('%1', name), pos - 1)
|
||||
}
|
||||
|
||||
const start = match.index
|
||||
const end = re.lastIndex
|
||||
state.scryle = null // reset the script/style flag now
|
||||
// write the tag content, if any
|
||||
if (start > pos) {
|
||||
parseSpecialTagsContent(state, name, match)
|
||||
}
|
||||
// now the closing tag, either </script> or </style>
|
||||
pushTag(state, `/${name}`, start, end)
|
||||
break
|
||||
}
|
||||
case data[pos] === '<':
|
||||
state.pos++
|
||||
return TAG
|
||||
default:
|
||||
expr(state, null, '<', pos)
|
||||
}
|
||||
|
||||
return TEXT
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the text content depending on the name
|
||||
* @param {ParserState} state - Parser state
|
||||
* @param {string} name - one of the tags matched by the RE_SCRYLE regex
|
||||
* @param {Array} match - result of the regex matching the content of the parsed tag
|
||||
* @returns {undefined} void function
|
||||
*/
|
||||
function parseSpecialTagsContent(state, name, match) {
|
||||
const { pos } = state
|
||||
const start = match.index
|
||||
|
||||
if (name === TEXTAREA_TAG) {
|
||||
expr(state, null, match[0], pos)
|
||||
} else {
|
||||
pushText(state, pos, start)
|
||||
}
|
||||
}
|
41
node_modules/@riotjs/parser/src/regex.js
generated
vendored
Normal file
41
node_modules/@riotjs/parser/src/regex.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Matches the start of valid tags names; used with the first 2 chars after the `'<'`.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
export const TAG_2C = /^(?:\/[a-zA-Z]|[a-zA-Z][^\s>/]?)/
|
||||
/**
|
||||
* Matches valid tags names AFTER the validation with `TAG_2C`.
|
||||
* $1: tag name including any `'/'`, $2: non self-closing brace (`>`) w/o attributes.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
export const TAG_NAME = /(\/?[^\s>/]+)\s*(>)?/g
|
||||
/**
|
||||
* Matches an attribute name-value pair (both can be empty).
|
||||
* $1: attribute name, $2: value including any quotes.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
export const ATTR_START = /(\S[^>/=\s]*)(?:\s*=\s*([^>/])?)?/g
|
||||
|
||||
/**
|
||||
* Matches the spread operator
|
||||
* it will be used for the spread attributes
|
||||
* @type {RegExp}
|
||||
*/
|
||||
export const SPREAD_OPERATOR = /\.\.\./
|
||||
/**
|
||||
* Matches the closing tag of a `script` and `style` block.
|
||||
* Used by parseText fo find the end of the block.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
export const RE_SCRYLE = {
|
||||
script: /<\/script\s*>/gi,
|
||||
style: /<\/style\s*>/gi,
|
||||
textarea: /<\/textarea\s*>/gi
|
||||
}
|
||||
|
||||
// Do not touch text content inside this tags
|
||||
export const RAW_TAGS = /^\/?(?:pre|textarea)$/
|
252
node_modules/@riotjs/parser/src/tree-builder.js
generated
vendored
Normal file
252
node_modules/@riotjs/parser/src/tree-builder.js
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/*---------------------------------------------------------------------
|
||||
* Tree builder for the riot tag parser.
|
||||
*
|
||||
* The output has a root property and separate arrays for `html`, `css`,
|
||||
* and `js` tags.
|
||||
*
|
||||
* The root tag is included as first element in the `html` array.
|
||||
* Script tags marked with "defer" are included in `html` instead `js`.
|
||||
*
|
||||
* - Mark SVG tags
|
||||
* - Mark raw tags
|
||||
* - Mark void tags
|
||||
* - Split prefixes from expressions
|
||||
* - Unescape escaped brackets and escape EOLs and backslashes
|
||||
* - Compact whitespace (option `compact`) for non-raw tags
|
||||
* - Create an array `parts` for text nodes and attributes
|
||||
*
|
||||
* Throws on unclosed tags or closing tags without start tag.
|
||||
* Selfclosing and void tags has no nodes[] property.
|
||||
*/
|
||||
import {COMMENT, TAG, TEXT} from './node-types'
|
||||
import {
|
||||
CSS_OUTPUT_NAME,
|
||||
IS_RAW,
|
||||
IS_SELF_CLOSING,
|
||||
IS_VOID,
|
||||
JAVASCRIPT_OUTPUT_NAME,
|
||||
JAVASCRIPT_TAG,
|
||||
STYLE_TAG,
|
||||
TEMPLATE_OUTPUT_NAME
|
||||
} from './constants'
|
||||
import {RAW_TAGS} from './regex'
|
||||
import {duplicatedNamedTag} from './messages'
|
||||
import panic from './utils/panic'
|
||||
|
||||
/**
|
||||
* Escape the carriage return and the line feed from a string
|
||||
* @param {string} string - input string
|
||||
* @returns {string} output string escaped
|
||||
*/
|
||||
function escapeReturn(string) {
|
||||
return string
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
}
|
||||
|
||||
// check whether a tag has the 'src' attribute set like for example `<script src="">`
|
||||
const hasSrcAttribute = node => (node.attributes || []).some(attr => attr.name === 'src')
|
||||
|
||||
/**
|
||||
* Escape double slashes in a string
|
||||
* @param {string} string - input string
|
||||
* @returns {string} output string escaped
|
||||
*/
|
||||
function escapeSlashes(string) {
|
||||
return string.replace(/\\/g, '\\\\')
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the multiple spaces with only one
|
||||
* @param {string} string - input string
|
||||
* @returns {string} string without trailing spaces
|
||||
*/
|
||||
function cleanSpaces(string) {
|
||||
return string.replace(/\s+/g, ' ')
|
||||
}
|
||||
|
||||
const TREE_BUILDER_STRUCT = Object.seal({
|
||||
get() {
|
||||
const store = this.store
|
||||
// The real root tag is in store.root.nodes[0]
|
||||
return {
|
||||
[TEMPLATE_OUTPUT_NAME]: store.root.nodes[0],
|
||||
[CSS_OUTPUT_NAME]: store[STYLE_TAG],
|
||||
[JAVASCRIPT_OUTPUT_NAME]: store[JAVASCRIPT_TAG]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Process the current tag or text.
|
||||
* @param {Object} node - Raw pseudo-node from the parser
|
||||
* @returns {undefined} void function
|
||||
*/
|
||||
push(node) {
|
||||
const store = this.store
|
||||
|
||||
switch (node.type) {
|
||||
case COMMENT:
|
||||
this.pushComment(store, node)
|
||||
break
|
||||
case TEXT:
|
||||
this.pushText(store, node)
|
||||
break
|
||||
case TAG: {
|
||||
const name = node.name
|
||||
const closingTagChar = '/'
|
||||
const [firstChar] = name
|
||||
|
||||
if (firstChar === closingTagChar && !node.isVoid) {
|
||||
this.closeTag(store, node, name)
|
||||
} else if (firstChar !== closingTagChar) {
|
||||
this.openTag(store, node)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
pushComment(store, node) {
|
||||
const parent = store.last
|
||||
|
||||
parent.nodes.push(node)
|
||||
},
|
||||
closeTag(store, node) {
|
||||
const last = store.scryle || store.last
|
||||
|
||||
last.end = node.end
|
||||
|
||||
// update always the root node end position
|
||||
if (store.root.nodes[0]) store.root.nodes[0].end = node.end
|
||||
|
||||
if (store.scryle) {
|
||||
store.scryle = null
|
||||
} else {
|
||||
store.last = store.stack.pop()
|
||||
}
|
||||
},
|
||||
|
||||
openTag(store, node) {
|
||||
const name = node.name
|
||||
const attrs = node.attributes
|
||||
const isCoreTag = (JAVASCRIPT_TAG === name && !hasSrcAttribute(node) || name === STYLE_TAG)
|
||||
|
||||
if (isCoreTag) {
|
||||
// Only accept one of each
|
||||
if (store[name]) {
|
||||
panic(this.store.data, duplicatedNamedTag.replace('%1', name), node.start)
|
||||
}
|
||||
|
||||
store[name] = node
|
||||
store.scryle = store[name]
|
||||
} else {
|
||||
// store.last holds the last tag pushed in the stack and this are
|
||||
// non-void, non-empty tags, so we are sure the `lastTag` here
|
||||
// have a `nodes` property.
|
||||
const lastTag = store.last
|
||||
const newNode = node
|
||||
|
||||
lastTag.nodes.push(newNode)
|
||||
|
||||
if (lastTag[IS_RAW] || RAW_TAGS.test(name)) {
|
||||
node[IS_RAW] = true
|
||||
}
|
||||
|
||||
if (!node[IS_SELF_CLOSING] && !node[IS_VOID]) {
|
||||
store.stack.push(lastTag)
|
||||
newNode.nodes = []
|
||||
store.last = newNode
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs) {
|
||||
this.attrs(attrs)
|
||||
}
|
||||
},
|
||||
attrs(attributes) {
|
||||
attributes.forEach(attr => {
|
||||
if (attr.value) {
|
||||
this.split(attr, attr.value, attr.valueStart, true)
|
||||
}
|
||||
})
|
||||
},
|
||||
pushText(store, node) {
|
||||
const text = node.text
|
||||
const empty = !/\S/.test(text)
|
||||
const scryle = store.scryle
|
||||
if (!scryle) {
|
||||
// store.last always have a nodes property
|
||||
const parent = store.last
|
||||
|
||||
const pack = this.compact && !parent[IS_RAW]
|
||||
if (pack && empty) {
|
||||
return
|
||||
}
|
||||
this.split(node, text, node.start, pack)
|
||||
parent.nodes.push(node)
|
||||
} else if (!empty) {
|
||||
scryle.text = node
|
||||
}
|
||||
},
|
||||
split(node, source, start, pack) {
|
||||
const expressions = node.expressions
|
||||
const parts = []
|
||||
|
||||
if (expressions) {
|
||||
let pos = 0
|
||||
|
||||
expressions.forEach(expr => {
|
||||
const text = source.slice(pos, expr.start - start)
|
||||
const code = expr.text
|
||||
parts.push(this.sanitise(node, text, pack), escapeReturn(escapeSlashes(code).trim()))
|
||||
pos = expr.end - start
|
||||
})
|
||||
|
||||
if (pos < node.end) {
|
||||
parts.push(this.sanitise(node, source.slice(pos), pack))
|
||||
}
|
||||
} else {
|
||||
parts[0] = this.sanitise(node, source, pack)
|
||||
}
|
||||
|
||||
node.parts = parts.filter(p => p) // remove the empty strings
|
||||
},
|
||||
// unescape escaped brackets and split prefixes of expressions
|
||||
sanitise(node, text, pack) {
|
||||
let rep = node.unescape
|
||||
if (rep) {
|
||||
let idx = 0
|
||||
rep = `\\${rep}`
|
||||
while ((idx = text.indexOf(rep, idx)) !== -1) {
|
||||
text = text.substr(0, idx) + text.substr(idx + 1)
|
||||
idx++
|
||||
}
|
||||
}
|
||||
|
||||
text = escapeSlashes(text)
|
||||
|
||||
return pack ? cleanSpaces(text) : escapeReturn(text)
|
||||
}
|
||||
})
|
||||
|
||||
export default function createTreeBuilder(data, options) {
|
||||
const root = {
|
||||
type: TAG,
|
||||
name: '',
|
||||
start: 0,
|
||||
end: 0,
|
||||
nodes: []
|
||||
}
|
||||
|
||||
return Object.assign(Object.create(TREE_BUILDER_STRUCT), {
|
||||
compact: options.compact !== false,
|
||||
store: {
|
||||
last: root,
|
||||
stack: [],
|
||||
scryle: null,
|
||||
root,
|
||||
style: null,
|
||||
script: null,
|
||||
data
|
||||
}
|
||||
})
|
||||
}
|
11
node_modules/@riotjs/parser/src/utils/add-to-collection.js
generated
vendored
Normal file
11
node_modules/@riotjs/parser/src/utils/add-to-collection.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Add an item into a collection, if the collection is not an array
|
||||
* we create one and add the item to it
|
||||
* @param {Array} collection - target collection
|
||||
* @param {*} item - item to add to the collection
|
||||
* @returns {Array} array containing the new item added to it
|
||||
*/
|
||||
export default function addToCollection(collection = [], item) {
|
||||
collection.push(item)
|
||||
return collection
|
||||
}
|
7
node_modules/@riotjs/parser/src/utils/escape-str.js
generated
vendored
Normal file
7
node_modules/@riotjs/parser/src/utils/escape-str.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Escape special characters in a given string, in preparation to create a regex.
|
||||
*
|
||||
* @param {string} str - Raw string
|
||||
* @returns {string} Escaped string.
|
||||
*/
|
||||
export default (str) => str.replace(/(?=[-[\](){^*+?.$|\\])/g, '\\')
|
11
node_modules/@riotjs/parser/src/utils/exec-from-pos.js
generated
vendored
Normal file
11
node_modules/@riotjs/parser/src/utils/exec-from-pos.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Run RegExp.exec starting from a specific position
|
||||
* @param {RegExp} re - regex
|
||||
* @param {number} pos - last index position
|
||||
* @param {string} string - regex target
|
||||
* @returns {Array} regex result
|
||||
*/
|
||||
export default function execFromPos(re, pos, string) {
|
||||
re.lastIndex = pos
|
||||
return re.exec(string)
|
||||
}
|
139
node_modules/@riotjs/parser/src/utils/expr-extr.js
generated
vendored
Normal file
139
node_modules/@riotjs/parser/src/utils/expr-extr.js
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Mini-parser for expressions.
|
||||
* The main pourpose of this module is to find the end of an expression
|
||||
* and return its text without the enclosing brackets.
|
||||
* Does not works with comments, but supports ES6 template strings.
|
||||
*/
|
||||
import skipES6TL, {$_ES6_BQ} from './skip-es6-tl'
|
||||
import {unclosedExpression, unexpectedCharInExpression} from '../messages'
|
||||
import escapeStr from './escape-str'
|
||||
import panic from './panic'
|
||||
import skipRegex from './skip-regex'
|
||||
/**
|
||||
* @exports exprExtr
|
||||
*/
|
||||
const S_SQ_STR = /'[^'\n\r\\]*(?:\\(?:\r\n?|[\S\s])[^'\n\r\\]*)*'/.source
|
||||
/**
|
||||
* Matches double quoted JS strings taking care about nested quotes
|
||||
* and EOLs (escaped EOLs are Ok).
|
||||
*
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
const S_STRING = `${S_SQ_STR}|${S_SQ_STR.replace(/'/g, '"')}`
|
||||
/**
|
||||
* Regex cache
|
||||
*
|
||||
* @type {Object.<string, RegExp>}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
const reBr = {}
|
||||
/**
|
||||
* Makes an optimal regex that matches quoted strings, brackets, backquotes
|
||||
* and the closing brackets of an expression.
|
||||
*
|
||||
* @param {string} b - Closing brackets
|
||||
* @returns {RegExp} - optimized regex
|
||||
*/
|
||||
function _regex(b) {
|
||||
let re = reBr[b]
|
||||
if (!re) {
|
||||
let s = escapeStr(b)
|
||||
if (b.length > 1) {
|
||||
s = `${s}|[`
|
||||
} else {
|
||||
s = /[{}[\]()]/.test(b) ? '[' : `[${s}`
|
||||
}
|
||||
reBr[b] = re = new RegExp(`${S_STRING}|${s}\`/\\{}[\\]()]`, 'g')
|
||||
}
|
||||
return re
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the scopes stack removing or adding closures to it
|
||||
* @param {Array} stack - array stacking the expression closures
|
||||
* @param {string} char - current char to add or remove from the stack
|
||||
* @param {string} idx - matching index
|
||||
* @param {string} code - expression code
|
||||
* @returns {Object} result
|
||||
* @returns {Object} result.char - either the char received or the closing braces
|
||||
* @returns {Object} result.index - either a new index to skip part of the source code,
|
||||
* or 0 to keep from parsing from the old position
|
||||
*/
|
||||
function updateStack(stack, char, idx, code) {
|
||||
let index = 0
|
||||
|
||||
switch (char) {
|
||||
case '[':
|
||||
case '(':
|
||||
case '{':
|
||||
stack.push(char === '[' ? ']' : char === '(' ? ')' : '}')
|
||||
break
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
if (char !== stack.pop()) {
|
||||
panic(code, unexpectedCharInExpression.replace('%1', char), index)
|
||||
}
|
||||
|
||||
if (char === '}' && stack[stack.length - 1] === $_ES6_BQ) {
|
||||
char = stack.pop()
|
||||
}
|
||||
|
||||
index = idx + 1
|
||||
break
|
||||
case '/':
|
||||
index = skipRegex(code, idx)
|
||||
}
|
||||
|
||||
return { char, index }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the code string searching the end of the expression.
|
||||
* It skips braces, quoted strings, regexes, and ES6 template literals.
|
||||
*
|
||||
* @function exprExtr
|
||||
* @param {string} code - Buffer to parse
|
||||
* @param {number} start - Position of the opening brace
|
||||
* @param {[string,string]} bp - Brackets pair
|
||||
* @returns {Object} Expression's end (after the closing brace) or -1
|
||||
* if it is not an expr.
|
||||
*/
|
||||
export default function exprExtr(code, start, bp) {
|
||||
const [openingBraces, closingBraces] = bp
|
||||
const offset = start + openingBraces.length // skips the opening brace
|
||||
const stack = [] // expected closing braces ('`' for ES6 TL)
|
||||
const re = _regex(closingBraces)
|
||||
|
||||
re.lastIndex = offset // begining of the expression
|
||||
|
||||
let end
|
||||
let match
|
||||
|
||||
while (match = re.exec(code)) { // eslint-disable-line
|
||||
const idx = match.index
|
||||
const str = match[0]
|
||||
end = re.lastIndex
|
||||
|
||||
// end the iteration
|
||||
if (str === closingBraces && !stack.length) {
|
||||
return {
|
||||
text: code.slice(offset, idx),
|
||||
start,
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
const { char, index } = updateStack(stack, str[0], idx, code)
|
||||
// update the end value depending on the new index received
|
||||
end = index || end
|
||||
// update the regex last index
|
||||
re.lastIndex = char === $_ES6_BQ ? skipES6TL(code, end, stack) : end
|
||||
}
|
||||
|
||||
if (stack.length) {
|
||||
panic(code, unclosedExpression, end)
|
||||
}
|
||||
}
|
14
node_modules/@riotjs/parser/src/utils/flush-parser-state.js
generated
vendored
Normal file
14
node_modules/@riotjs/parser/src/utils/flush-parser-state.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Outputs the last parsed node. Can be used with a builder too.
|
||||
*
|
||||
* @param {ParserStore} store - Parsing store
|
||||
* @returns {undefined} void function
|
||||
* @private
|
||||
*/
|
||||
export default function flush(store) {
|
||||
const last = store.last
|
||||
store.last = null
|
||||
if (last && store.root) {
|
||||
store.builder.push(last)
|
||||
}
|
||||
}
|
12
node_modules/@riotjs/parser/src/utils/format-error.js
generated
vendored
Normal file
12
node_modules/@riotjs/parser/src/utils/format-error.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function formatError(data, message, pos) {
|
||||
if (!pos) {
|
||||
pos = data.length
|
||||
}
|
||||
// count unix/mac/win eols
|
||||
const line = (data.slice(0, pos).match(/\r\n?|\n/g) || '').length + 1
|
||||
let col = 0
|
||||
while (--pos >= 0 && !/[\r\n]/.test(data[pos])) {
|
||||
++col
|
||||
}
|
||||
return `[${line},${col}]: ${message}`
|
||||
}
|
11
node_modules/@riotjs/parser/src/utils/get-chunk.js
generated
vendored
Normal file
11
node_modules/@riotjs/parser/src/utils/get-chunk.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Get the code chunks from start and end range
|
||||
* @param {string} source - source code
|
||||
* @param {number} start - Start position of the chunk we want to extract
|
||||
* @param {number} end - Ending position of the chunk we need
|
||||
* @returns {string} chunk of code extracted from the source code received
|
||||
* @private
|
||||
*/
|
||||
export default function getChunk(source, start, end) {
|
||||
return source.slice(start, end)
|
||||
}
|
18
node_modules/@riotjs/parser/src/utils/memoize.js
generated
vendored
Normal file
18
node_modules/@riotjs/parser/src/utils/memoize.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Memoization function
|
||||
* @param {Function} fn - function to memoize
|
||||
* @returns {*} return of the function to memoize
|
||||
*/
|
||||
export default function memoize(fn) {
|
||||
const cache = new WeakMap()
|
||||
|
||||
return (...args) => {
|
||||
if (cache.has(args[0])) return cache.get(args[0])
|
||||
|
||||
const ret = fn(...args)
|
||||
|
||||
cache.set(args[0], ret)
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
15
node_modules/@riotjs/parser/src/utils/panic.js
generated
vendored
Normal file
15
node_modules/@riotjs/parser/src/utils/panic.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import formatError from './format-error'
|
||||
|
||||
/**
|
||||
* Custom error handler can be implemented replacing this method.
|
||||
* The `state` object includes the buffer (`data`)
|
||||
* The error position (`loc`) contains line (base 1) and col (base 0).
|
||||
* @param {string} data - string containing the error
|
||||
* @param {string} msg - Error message
|
||||
* @param {number} pos - Position of the error
|
||||
* @returns {undefined} throw an exception error
|
||||
*/
|
||||
export default function panic(data, msg, pos) {
|
||||
const message = formatError(data, msg, pos)
|
||||
throw new Error(message)
|
||||
}
|
48
node_modules/@riotjs/parser/src/utils/push-tag.js
generated
vendored
Normal file
48
node_modules/@riotjs/parser/src/utils/push-tag.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
IS_CUSTOM,
|
||||
IS_VOID
|
||||
} from '../constants'
|
||||
import {isCustom, isVoid} from 'dom-nodes'
|
||||
import {TAG} from '../node-types'
|
||||
import flush from './flush-parser-state'
|
||||
|
||||
/**
|
||||
* Pushes a new *tag* and set `last` to this, so any attributes
|
||||
* will be included on this and shifts the `end`.
|
||||
*
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @param {string} name - Name of the node including any slash
|
||||
* @param {number} start - Start position of the tag
|
||||
* @param {number} end - Ending position (last char of the tag + 1)
|
||||
* @returns {undefined} - void function
|
||||
* @private
|
||||
*/
|
||||
export default function pushTag(state, name, start, end) {
|
||||
const root = state.root
|
||||
const last = { type: TAG, name, start, end }
|
||||
|
||||
if (isCustom(name)) {
|
||||
last[IS_CUSTOM] = true
|
||||
}
|
||||
|
||||
if (isVoid(name)) {
|
||||
last[IS_VOID] = true
|
||||
}
|
||||
|
||||
state.pos = end
|
||||
|
||||
if (root) {
|
||||
if (name === root.name) {
|
||||
state.count++
|
||||
} else if (name === root.close) {
|
||||
state.count--
|
||||
}
|
||||
flush(state)
|
||||
} else {
|
||||
// start with root (keep ref to output)
|
||||
state.root = { name: last.name, close: `/${name}` }
|
||||
state.count = 1
|
||||
}
|
||||
|
||||
state.last = last
|
||||
}
|
42
node_modules/@riotjs/parser/src/utils/push-text.js
generated
vendored
Normal file
42
node_modules/@riotjs/parser/src/utils/push-text.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import {TEXT} from '../node-types'
|
||||
import flush from './flush-parser-state'
|
||||
import getChunk from './get-chunk'
|
||||
|
||||
/**
|
||||
* states text in the last text node, or creates a new one if needed.
|
||||
*
|
||||
* @param {ParserState} state - Current parser state
|
||||
* @param {number} start - Start position of the tag
|
||||
* @param {number} end - Ending position (last char of the tag)
|
||||
* @param {Object} extra - extra properties to add to the text node
|
||||
* @param {RawExpr[]} extra.expressions - Found expressions
|
||||
* @param {string} extra.unescape - Brackets to unescape
|
||||
* @returns {undefined} - void function
|
||||
* @private
|
||||
*/
|
||||
export default function pushText(state, start, end, extra = {}) {
|
||||
const text = getChunk(state.data, start, end)
|
||||
const expressions = extra.expressions
|
||||
const unescape = extra.unescape
|
||||
|
||||
let q = state.last
|
||||
state.pos = end
|
||||
|
||||
if (q && q.type === TEXT) {
|
||||
q.text += text
|
||||
q.end = end
|
||||
} else {
|
||||
flush(state)
|
||||
state.last = q = { type: TEXT, text, start, end }
|
||||
}
|
||||
|
||||
if (expressions && expressions.length) {
|
||||
q.expressions = (q.expressions || []).concat(expressions)
|
||||
}
|
||||
|
||||
if (unescape) {
|
||||
q.unescape = unescape
|
||||
}
|
||||
|
||||
return TEXT
|
||||
}
|
33
node_modules/@riotjs/parser/src/utils/skip-es6-tl.js
generated
vendored
Normal file
33
node_modules/@riotjs/parser/src/utils/skip-es6-tl.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import formatError from './format-error'
|
||||
import {unclosedTemplateLiteral} from '../messages'
|
||||
export const $_ES6_BQ = '`'
|
||||
|
||||
/**
|
||||
* Searches the next backquote that signals the end of the ES6 Template Literal
|
||||
* or the "${" sequence that starts a JS expression, skipping any escaped
|
||||
* character.
|
||||
*
|
||||
* @param {string} code - Whole code
|
||||
* @param {number} pos - The start position of the template
|
||||
* @param {string[]} stack - To save nested ES6 TL count
|
||||
* @returns {number} The end of the string (-1 if not found)
|
||||
*/
|
||||
export default function skipES6TL(code, pos, stack) {
|
||||
// we are in the char following the backquote (`),
|
||||
// find the next unescaped backquote or the sequence "${"
|
||||
const re = /[`$\\]/g
|
||||
let c
|
||||
while (re.lastIndex = pos, re.exec(code)) {
|
||||
pos = re.lastIndex
|
||||
c = code[pos - 1]
|
||||
if (c === '`') {
|
||||
return pos
|
||||
}
|
||||
if (c === '$' && code[pos++] === '{') {
|
||||
stack.push($_ES6_BQ, '}')
|
||||
return pos
|
||||
}
|
||||
// else this is an escaped char
|
||||
}
|
||||
throw formatError(code, unclosedTemplateLiteral, pos)
|
||||
}
|
116
node_modules/@riotjs/parser/src/utils/skip-regex.js
generated
vendored
Normal file
116
node_modules/@riotjs/parser/src/utils/skip-regex.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// forked from https://github.com/aMarCruz/skip-regex
|
||||
|
||||
// safe characters to precced a regex (including `=>`, `**`, and `...`)
|
||||
const beforeReChars = '[{(,;:?=|&!^~>%*/'
|
||||
const beforeReSign = `${beforeReChars}+-`
|
||||
|
||||
// keyword that can preceed a regex (`in` is handled as special case)
|
||||
const beforeReWords = [
|
||||
'case',
|
||||
'default',
|
||||
'do',
|
||||
'else',
|
||||
'in',
|
||||
'instanceof',
|
||||
'prefix',
|
||||
'return',
|
||||
'typeof',
|
||||
'void',
|
||||
'yield'
|
||||
]
|
||||
|
||||
// Last chars of all the beforeReWords elements to speed up the process.
|
||||
const wordsEndChar = beforeReWords.reduce((s, w) => s + w.slice(-1), '')
|
||||
|
||||
// Matches literal regex from the start of the buffer.
|
||||
// The buffer to search must not include line-endings.
|
||||
const RE_LIT_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/
|
||||
|
||||
// Valid characters for JavaScript variable names and literal numbers.
|
||||
const RE_JS_VCHAR = /[$\w]/
|
||||
|
||||
// Match dot characters that could be part of tricky regex
|
||||
const RE_DOT_CHAR = /.*/g
|
||||
|
||||
/**
|
||||
* Searches the position of the previous non-blank character inside `code`,
|
||||
* starting with `pos - 1`.
|
||||
*
|
||||
* @param {string} code - Buffer to search
|
||||
* @param {number} pos - Starting position
|
||||
* @returns {number} Position of the first non-blank character to the left.
|
||||
* @private
|
||||
*/
|
||||
function _prev(code, pos) {
|
||||
while (--pos >= 0 && /\s/.test(code[pos]));
|
||||
return pos
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if the character in the `start` position within `code` can be a regex
|
||||
* and returns the position following this regex or `start+1` if this is not
|
||||
* one.
|
||||
*
|
||||
* NOTE: Ensure `start` points to a slash (this is not checked).
|
||||
*
|
||||
* @function skipRegex
|
||||
* @param {string} code - Buffer to test in
|
||||
* @param {number} start - Position the first slash inside `code`
|
||||
* @returns {number} Position of the char following the regex.
|
||||
*
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
export default function skipRegex(code, start) {
|
||||
let pos = RE_DOT_CHAR.lastIndex = start++
|
||||
|
||||
// `exec()` will extract from the slash to the end of the line
|
||||
// and the chained `match()` will match the possible regex.
|
||||
const match = (RE_DOT_CHAR.exec(code) || ' ')[0].match(RE_LIT_REGEX)
|
||||
|
||||
if (match) {
|
||||
const next = pos + match[0].length // result comes from `re.match`
|
||||
|
||||
pos = _prev(code, pos)
|
||||
let c = code[pos]
|
||||
|
||||
// start of buffer or safe prefix?
|
||||
if (pos < 0 || beforeReChars.includes(c)) {
|
||||
return next
|
||||
}
|
||||
|
||||
// from here, `pos` is >= 0 and `c` is code[pos]
|
||||
if (c === '.') {
|
||||
// can be `...` or something silly like 5./2
|
||||
if (code[pos - 1] === '.') {
|
||||
start = next
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (c === '+' || c === '-') {
|
||||
// tricky case
|
||||
if (code[--pos] !== c || // if have a single operator or
|
||||
(pos = _prev(code, pos)) < 0 || // ...have `++` and no previous token
|
||||
beforeReSign.includes(c = code[pos])) {
|
||||
return next // ...this is a regex
|
||||
}
|
||||
}
|
||||
|
||||
if (wordsEndChar.includes(c)) { // looks like a keyword?
|
||||
const end = pos + 1
|
||||
|
||||
// get the complete (previous) keyword
|
||||
while (--pos >= 0 && RE_JS_VCHAR.test(code[pos]));
|
||||
|
||||
// it is in the allowed keywords list?
|
||||
if (beforeReWords.includes(code.slice(pos + 1, end))) {
|
||||
start = next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start
|
||||
}
|
Reference in New Issue
Block a user