This commit is contained in:
lalBi94
2023-03-05 13:23:23 +01:00
commit 7bc56c09b5
14034 changed files with 1834369 additions and 0 deletions

22
node_modules/@riotjs/compiler/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 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.

112
node_modules/@riotjs/compiler/README.md generated vendored Normal file
View File

@@ -0,0 +1,112 @@
[![Build Status][ci-image]][ci-url]
[![Issue Count][codeclimate-image]][codeclimate-url]
[![Coverage Status][coverage-image]][coverage-url]
[![NPM version][npm-version-image]][npm-url]
[![NPM downloads][npm-downloads-image]][npm-url]
[![MIT License][license-image]][license-url]
## Important
This compiler will not work with older Riot.js versions.
It's designed to work with Riot.js > 4.0.0.
For Riot.js < 4.0.0 please check the [v3](https://github.com/riot/compiler/tree/v3) branch
## Installation
```
npm i @riotjs/compiler -D
```
## Usage
The riot compiler can compile only strings:
```js
import { compile } from '@riotjs/compiler'
const { code, map } = compile('<p>{hello}</p>')
```
You can compile your tags also using the new `registerPreprocessor` and `registerPostprocessor` APIs for example:
```js
import { compiler, registerPreprocessor, registerPostprocessor } from '@riotjs/compiler'
import pug from 'pug'
import buble from 'buble'
// process your tag template before it will be compiled
registerPreprocessor('template', 'pug', function(code, { options }) {
const { file } = options
console.log('your file path is:', file)
return {
code: pug.render(code),
// no sourcemap here
map: null
}
})
// your compiler output will pass from here
registerPostprocessor(function(code, { options }) {
const { file } = options
console.log('your file path is:', file)
// notice that buble.transform returns {code, map}
return buble.transform(code)
})
const { code, map } = compile('<p>{hello}</p>', {
// specify the template preprocessor
template: 'pug'
})
```
## API
### compile(string, options)
#### @returns `{ code, map }` output that can be used by Riot.js
- *string*: is your tag source code
- *options*: the options should contain the `file` key identifying the source of the string to compile and
the `template` preprocessor to use as string
Note: specific preprocessors like the `css` or the `javascript` ones can be enabled simply specifying the `type` attribute
in the tag source code for example
```html
<my-tag>
<style type='scss'>
// ...
</style>
</my-tag>
```
### registerPreprocessor(type, id, preprocessorFn)
#### @returns `Object` containing all the preprocessors registered
- *type*: either one of `template` `css` or `javascript`
- *id*: unique preprocessor identifier
- *preprocessorFn*: function receiving the code as first argument and the current options as second
### registerPostprocessor(postprocessorFn)
#### @returns `Set` containing all the postprocessors registered
- *postprocessorFn*: function receiving the compiler output as first argument and the current options as second
### generateTemplateFunctionFromString(string, parserOptions)
#### @returns `string` with the code to execute the @riotjs/bindings `template` function
### generateSlotsFromString(string, parserOptions)
#### @returns `string` with the code to generate components slots in runtime
[ci-image]:https://img.shields.io/github/workflow/status/riot/compiler/test?style=flat-square
[ci-url]:https://github.com/riot/compiler/actions
[license-image]: https://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
[license-url]: LICENSE
[npm-version-image]: https://img.shields.io/npm/v/@riotjs/compiler.svg?style=flat-square
[npm-downloads-image]: https://img.shields.io/npm/dm/@riotjs/compiler.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@riotjs/compiler
[coverage-image]: https://img.shields.io/coveralls/riot/compiler/main.svg?style=flat-square
[coverage-url]: https://coveralls.io/r/riot/compiler?branch=main
[codeclimate-image]: https://api.codeclimate.com/v1/badges/37de24282e8d113bb0cc/maintainability
[codeclimate-url]: https://codeclimate.com/github/riot/compiler

55
node_modules/@riotjs/compiler/compiler.d.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import {RawSourceMap} from 'source-map'
export type CompilerOptions = {
template?: string
file?: string
scopedCss?: boolean
}
export type CompilerOutput = {
code: string
map: RawSourceMap
}
export type CompilerOutputFragments = {
template: object
css: object
javascript: object
}
export type PreProcessorOutput = {
code: string,
map?: RawSourceMap
}
export type PreProcessorMeta = {
tagName: string,
fragments: CompilerOutputFragments,
options: CompilerOptions,
source: string
}
export type ProcessorFunction = (code: string, meta: PreProcessorMeta) => PreProcessorOutput
export type PreProcessorsMap = {
template: Map<string, ProcessorFunction>
javascript: Map<string, ProcessorFunction>
css: Map<string, ProcessorFunction>
}
export type PostProcessorsMap = Map<string, ProcessorFunction>
export type PreProcessorType = 'template' | 'javascript' | 'css'
// public API
export function generateTemplateFunctionFromString(source: string, parserOptions: any): string
export function generateSlotsFromString(source: string, parserOptions: any): string
export function compile(source: string, options?: CompilerOptions): CompilerOutput
export function registerPreprocessor(
type: PreProcessorType,
name: string,
fn: ProcessorFunction
): PreProcessorsMap
export function registerPostprocessor(fn: ProcessorFunction): PostProcessorsMap

File diff suppressed because one or more lines are too long

20882
node_modules/@riotjs/compiler/dist/compiler.essential.js generated vendored Normal file

File diff suppressed because one or more lines are too long

41148
node_modules/@riotjs/compiler/dist/compiler.js generated vendored Normal file

File diff suppressed because one or more lines are too long

4955
node_modules/@riotjs/compiler/dist/index.esm.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

4968
node_modules/@riotjs/compiler/dist/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
Copyright (c) 2009-2011, Mozilla Foundation and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the Mozilla Foundation nor the names of project
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,100 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
/**
* A data structure which is a combination of an array and a set. Adding a new
* member is O(1), testing for membership is O(1), and finding the index of an
* element is O(1). Removing elements from the set is not supported. Only
* strings are supported for membership.
*/
class ArraySet {
constructor() {
this._array = [];
this._set = new Map();
}
/**
* Static method for creating ArraySet instances from an existing array.
*/
static fromArray(aArray, aAllowDuplicates) {
const set = new ArraySet();
for (let i = 0, len = aArray.length; i < len; i++) {
set.add(aArray[i], aAllowDuplicates);
}
return set;
}
/**
* Return how many unique items are in this ArraySet. If duplicates have been
* added, than those do not count towards the size.
*
* @returns Number
*/
size() {
return this._set.size;
}
/**
* Add the given string to this set.
*
* @param String aStr
*/
add(aStr, aAllowDuplicates) {
const isDuplicate = this.has(aStr);
const idx = this._array.length;
if (!isDuplicate || aAllowDuplicates) {
this._array.push(aStr);
}
if (!isDuplicate) {
this._set.set(aStr, idx);
}
}
/**
* Is the given string a member of this set?
*
* @param String aStr
*/
has(aStr) {
return this._set.has(aStr);
}
/**
* What is the index of the given string in the array?
*
* @param String aStr
*/
indexOf(aStr) {
const idx = this._set.get(aStr);
if (idx >= 0) {
return idx;
}
throw new Error('"' + aStr + '" is not in the set.');
}
/**
* What is the element at the given index?
*
* @param Number aIdx
*/
at(aIdx) {
if (aIdx >= 0 && aIdx < this._array.length) {
return this._array[aIdx];
}
throw new Error("No element indexed by " + aIdx);
}
/**
* Returns the array representation of this set (which has the proper indices
* indicated by indexOf). Note that this is a copy of the internal array used
* for storing the members so that no one can mess with internal state.
*/
toArray() {
return this._array.slice();
}
}
exports.ArraySet = ArraySet;

View File

@@ -0,0 +1,111 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*
* Based on the Base 64 VLQ implementation in Closure Compiler:
* https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
*
* Copyright 2011 The Closure Compiler Authors. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const base64 = require("./base64");
// A single base 64 digit can contain 6 bits of data. For the base 64 variable
// length quantities we use in the source map spec, the first bit is the sign,
// the next four bits are the actual value, and the 6th bit is the
// continuation bit. The continuation bit tells us whether there are more
// digits in this value following this digit.
//
// Continuation
// | Sign
// | |
// V V
// 101011
const VLQ_BASE_SHIFT = 5;
// binary: 100000
const VLQ_BASE = 1 << VLQ_BASE_SHIFT;
// binary: 011111
const VLQ_BASE_MASK = VLQ_BASE - 1;
// binary: 100000
const VLQ_CONTINUATION_BIT = VLQ_BASE;
/**
* Converts from a two-complement value to a value where the sign bit is
* placed in the least significant bit. For example, as decimals:
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
*/
function toVLQSigned(aValue) {
return aValue < 0
? ((-aValue) << 1) + 1
: (aValue << 1) + 0;
}
/**
* Converts to a two-complement value from a value where the sign bit is
* placed in the least significant bit. For example, as decimals:
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
*/
// eslint-disable-next-line no-unused-vars
function fromVLQSigned(aValue) {
const isNegative = (aValue & 1) === 1;
const shifted = aValue >> 1;
return isNegative
? -shifted
: shifted;
}
/**
* Returns the base 64 VLQ encoded value.
*/
exports.encode = function base64VLQ_encode(aValue) {
let encoded = "";
let digit;
let vlq = toVLQSigned(aValue);
do {
digit = vlq & VLQ_BASE_MASK;
vlq >>>= VLQ_BASE_SHIFT;
if (vlq > 0) {
// There are still more digits in this value, so we must make sure the
// continuation bit is marked.
digit |= VLQ_CONTINUATION_BIT;
}
encoded += base64.encode(digit);
} while (vlq > 0);
return encoded;
};

View File

@@ -0,0 +1,18 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const intToCharMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
/**
* Encode an integer in the range of 0 to 63 to a single base 64 digit.
*/
exports.encode = function(number) {
if (0 <= number && number < intToCharMap.length) {
return intToCharMap[number];
}
throw new TypeError("Must be between 0 and 63: " + number);
};

View File

@@ -0,0 +1,107 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
exports.GREATEST_LOWER_BOUND = 1;
exports.LEAST_UPPER_BOUND = 2;
/**
* Recursive implementation of binary search.
*
* @param aLow Indices here and lower do not contain the needle.
* @param aHigh Indices here and higher do not contain the needle.
* @param aNeedle The element being searched for.
* @param aHaystack The non-empty array being searched.
* @param aCompare Function which takes two elements and returns -1, 0, or 1.
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
*/
function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) {
// This function terminates when one of the following is true:
//
// 1. We find the exact element we are looking for.
//
// 2. We did not find the exact element, but we can return the index of
// the next-closest element.
//
// 3. We did not find the exact element, and there is no next-closest
// element than the one we are searching for, so we return -1.
const mid = Math.floor((aHigh - aLow) / 2) + aLow;
const cmp = aCompare(aNeedle, aHaystack[mid], true);
if (cmp === 0) {
// Found the element we are looking for.
return mid;
} else if (cmp > 0) {
// Our needle is greater than aHaystack[mid].
if (aHigh - mid > 1) {
// The element is in the upper half.
return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias);
}
// The exact needle element was not found in this haystack. Determine if
// we are in termination case (3) or (2) and return the appropriate thing.
if (aBias == exports.LEAST_UPPER_BOUND) {
return aHigh < aHaystack.length ? aHigh : -1;
}
return mid;
}
// Our needle is less than aHaystack[mid].
if (mid - aLow > 1) {
// The element is in the lower half.
return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias);
}
// we are in termination case (3) or (2) and return the appropriate thing.
if (aBias == exports.LEAST_UPPER_BOUND) {
return mid;
}
return aLow < 0 ? -1 : aLow;
}
/**
* This is an implementation of binary search which will always try and return
* the index of the closest element if there is no exact hit. This is because
* mappings between original and generated line/col pairs are single points,
* and there is an implicit region between each of them, so a miss just means
* that you aren't on the very start of a region.
*
* @param aNeedle The element you are looking for.
* @param aHaystack The array that is being searched.
* @param aCompare A function which takes the needle and an element in the
* array and returns -1, 0, or 1 depending on whether the needle is less
* than, equal to, or greater than the element, respectively.
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
* Defaults to 'binarySearch.GREATEST_LOWER_BOUND'.
*/
exports.search = function search(aNeedle, aHaystack, aCompare, aBias) {
if (aHaystack.length === 0) {
return -1;
}
let index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack,
aCompare, aBias || exports.GREATEST_LOWER_BOUND);
if (index < 0) {
return -1;
}
// We have found either the exact element, or the next-closest element than
// the one we are searching for. However, there may be more than one such
// element. Make sure we always return the smallest of these.
while (index - 1 >= 0) {
if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) {
break;
}
--index;
}
return index;
};

View File

@@ -0,0 +1,80 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2014 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const util = require("./util");
/**
* Determine whether mappingB is after mappingA with respect to generated
* position.
*/
function generatedPositionAfter(mappingA, mappingB) {
// Optimized for most common case
const lineA = mappingA.generatedLine;
const lineB = mappingB.generatedLine;
const columnA = mappingA.generatedColumn;
const columnB = mappingB.generatedColumn;
return lineB > lineA || lineB == lineA && columnB >= columnA ||
util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0;
}
/**
* A data structure to provide a sorted view of accumulated mappings in a
* performance conscious manner. It trades a negligible overhead in general
* case for a large speedup in case of mappings being added in order.
*/
class MappingList {
constructor() {
this._array = [];
this._sorted = true;
// Serves as infimum
this._last = {generatedLine: -1, generatedColumn: 0};
}
/**
* Iterate through internal items. This method takes the same arguments that
* `Array.prototype.forEach` takes.
*
* NOTE: The order of the mappings is NOT guaranteed.
*/
unsortedForEach(aCallback, aThisArg) {
this._array.forEach(aCallback, aThisArg);
}
/**
* Add the given source mapping.
*
* @param Object aMapping
*/
add(aMapping) {
if (generatedPositionAfter(this._last, aMapping)) {
this._last = aMapping;
this._array.push(aMapping);
} else {
this._sorted = false;
this._array.push(aMapping);
}
}
/**
* Returns the flat, sorted array of mappings. The mappings are sorted by
* generated position.
*
* WARNING: This method returns internal data without copying, for
* performance. The return value must NOT be mutated, and should be treated as
* an immutable borrow. If you want to take ownership, you must make your own
* copy.
*/
toArray() {
if (!this._sorted) {
this._array.sort(util.compareByGeneratedPositionsInflated);
this._sorted = true;
}
return this._array;
}
}
exports.MappingList = MappingList;

Binary file not shown.

View File

@@ -0,0 +1,49 @@
/* Determine browser vs node environment by testing the default top level context. Solution courtesy of: https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser */
const isBrowserEnvironment = (function() {
// eslint-disable-next-line no-undef
return (typeof window !== "undefined") && (this === window);
}).call();
if (isBrowserEnvironment) {
// Web version of reading a wasm file into an array buffer.
let mappingsWasm = null;
module.exports = function readWasm() {
if (typeof mappingsWasm === "string") {
return fetch(mappingsWasm)
.then(response => response.arrayBuffer());
}
if (mappingsWasm instanceof ArrayBuffer) {
return Promise.resolve(mappingsWasm);
}
throw new Error("You must provide the string URL or ArrayBuffer contents " +
"of lib/mappings.wasm by calling " +
"SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
"before using SourceMapConsumer");
};
module.exports.initialize = input => mappingsWasm = input;
} else {
// Node version of reading a wasm file into an array buffer.
const fs = require("fs");
const path = require("path");
module.exports = function readWasm() {
return new Promise((resolve, reject) => {
const wasmPath = path.join(__dirname, "mappings.wasm");
fs.readFile(wasmPath, null, (error, data) => {
if (error) {
reject(error);
return;
}
resolve(data.buffer);
});
});
};
module.exports.initialize = _ => {
console.debug("SourceMapConsumer.initialize is a no-op when running in node.js");
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const base64VLQ = require("./base64-vlq");
const util = require("./util");
const ArraySet = require("./array-set").ArraySet;
const MappingList = require("./mapping-list").MappingList;
/**
* An instance of the SourceMapGenerator represents a source map which is
* being built incrementally. You may pass an object with the following
* properties:
*
* - file: The filename of the generated source.
* - sourceRoot: A root for all relative URLs in this source map.
*/
class SourceMapGenerator {
constructor(aArgs) {
if (!aArgs) {
aArgs = {};
}
this._file = util.getArg(aArgs, "file", null);
this._sourceRoot = util.getArg(aArgs, "sourceRoot", null);
this._skipValidation = util.getArg(aArgs, "skipValidation", false);
this._sources = new ArraySet();
this._names = new ArraySet();
this._mappings = new MappingList();
this._sourcesContents = null;
}
/**
* Creates a new SourceMapGenerator based on a SourceMapConsumer
*
* @param aSourceMapConsumer The SourceMap.
*/
static fromSourceMap(aSourceMapConsumer) {
const sourceRoot = aSourceMapConsumer.sourceRoot;
const generator = new SourceMapGenerator({
file: aSourceMapConsumer.file,
sourceRoot
});
aSourceMapConsumer.eachMapping(function(mapping) {
const newMapping = {
generated: {
line: mapping.generatedLine,
column: mapping.generatedColumn
}
};
if (mapping.source != null) {
newMapping.source = mapping.source;
if (sourceRoot != null) {
newMapping.source = util.relative(sourceRoot, newMapping.source);
}
newMapping.original = {
line: mapping.originalLine,
column: mapping.originalColumn
};
if (mapping.name != null) {
newMapping.name = mapping.name;
}
}
generator.addMapping(newMapping);
});
aSourceMapConsumer.sources.forEach(function(sourceFile) {
let sourceRelative = sourceFile;
if (sourceRoot !== null) {
sourceRelative = util.relative(sourceRoot, sourceFile);
}
if (!generator._sources.has(sourceRelative)) {
generator._sources.add(sourceRelative);
}
const content = aSourceMapConsumer.sourceContentFor(sourceFile);
if (content != null) {
generator.setSourceContent(sourceFile, content);
}
});
return generator;
}
/**
* Add a single mapping from original source line and column to the generated
* source's line and column for this source map being created. The mapping
* object should have the following properties:
*
* - generated: An object with the generated line and column positions.
* - original: An object with the original line and column positions.
* - source: The original source file (relative to the sourceRoot).
* - name: An optional original token name for this mapping.
*/
addMapping(aArgs) {
const generated = util.getArg(aArgs, "generated");
const original = util.getArg(aArgs, "original", null);
let source = util.getArg(aArgs, "source", null);
let name = util.getArg(aArgs, "name", null);
if (!this._skipValidation) {
this._validateMapping(generated, original, source, name);
}
if (source != null) {
source = String(source);
if (!this._sources.has(source)) {
this._sources.add(source);
}
}
if (name != null) {
name = String(name);
if (!this._names.has(name)) {
this._names.add(name);
}
}
this._mappings.add({
generatedLine: generated.line,
generatedColumn: generated.column,
originalLine: original != null && original.line,
originalColumn: original != null && original.column,
source,
name
});
}
/**
* Set the source content for a source file.
*/
setSourceContent(aSourceFile, aSourceContent) {
let source = aSourceFile;
if (this._sourceRoot != null) {
source = util.relative(this._sourceRoot, source);
}
if (aSourceContent != null) {
// Add the source content to the _sourcesContents map.
// Create a new _sourcesContents map if the property is null.
if (!this._sourcesContents) {
this._sourcesContents = Object.create(null);
}
this._sourcesContents[util.toSetString(source)] = aSourceContent;
} else if (this._sourcesContents) {
// Remove the source file from the _sourcesContents map.
// If the _sourcesContents map is empty, set the property to null.
delete this._sourcesContents[util.toSetString(source)];
if (Object.keys(this._sourcesContents).length === 0) {
this._sourcesContents = null;
}
}
}
/**
* Applies the mappings of a sub-source-map for a specific source file to the
* source map being generated. Each mapping to the supplied source file is
* rewritten using the supplied source map. Note: The resolution for the
* resulting mappings is the minimium of this map and the supplied map.
*
* @param aSourceMapConsumer The source map to be applied.
* @param aSourceFile Optional. The filename of the source file.
* If omitted, SourceMapConsumer's file property will be used.
* @param aSourceMapPath Optional. The dirname of the path to the source map
* to be applied. If relative, it is relative to the SourceMapConsumer.
* This parameter is needed when the two source maps aren't in the same
* directory, and the source map to be applied contains relative source
* paths. If so, those relative source paths need to be rewritten
* relative to the SourceMapGenerator.
*/
applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
let sourceFile = aSourceFile;
// If aSourceFile is omitted, we will use the file property of the SourceMap
if (aSourceFile == null) {
if (aSourceMapConsumer.file == null) {
throw new Error(
"SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, " +
'or the source map\'s "file" property. Both were omitted.'
);
}
sourceFile = aSourceMapConsumer.file;
}
const sourceRoot = this._sourceRoot;
// Make "sourceFile" relative if an absolute Url is passed.
if (sourceRoot != null) {
sourceFile = util.relative(sourceRoot, sourceFile);
}
// Applying the SourceMap can add and remove items from the sources and
// the names array.
const newSources = this._mappings.toArray().length > 0
? new ArraySet()
: this._sources;
const newNames = new ArraySet();
// Find mappings for the "sourceFile"
this._mappings.unsortedForEach(function(mapping) {
if (mapping.source === sourceFile && mapping.originalLine != null) {
// Check if it can be mapped by the source map, then update the mapping.
const original = aSourceMapConsumer.originalPositionFor({
line: mapping.originalLine,
column: mapping.originalColumn
});
if (original.source != null) {
// Copy mapping
mapping.source = original.source;
if (aSourceMapPath != null) {
mapping.source = util.join(aSourceMapPath, mapping.source);
}
if (sourceRoot != null) {
mapping.source = util.relative(sourceRoot, mapping.source);
}
mapping.originalLine = original.line;
mapping.originalColumn = original.column;
if (original.name != null) {
mapping.name = original.name;
}
}
}
const source = mapping.source;
if (source != null && !newSources.has(source)) {
newSources.add(source);
}
const name = mapping.name;
if (name != null && !newNames.has(name)) {
newNames.add(name);
}
}, this);
this._sources = newSources;
this._names = newNames;
// Copy sourcesContents of applied map.
aSourceMapConsumer.sources.forEach(function(srcFile) {
const content = aSourceMapConsumer.sourceContentFor(srcFile);
if (content != null) {
if (aSourceMapPath != null) {
srcFile = util.join(aSourceMapPath, srcFile);
}
if (sourceRoot != null) {
srcFile = util.relative(sourceRoot, srcFile);
}
this.setSourceContent(srcFile, content);
}
}, this);
}
/**
* A mapping can have one of the three levels of data:
*
* 1. Just the generated position.
* 2. The Generated position, original position, and original source.
* 3. Generated and original position, original source, as well as a name
* token.
*
* To maintain consistency, we validate that any new mapping being added falls
* in to one of these categories.
*/
_validateMapping(aGenerated, aOriginal, aSource, aName) {
// When aOriginal is truthy but has empty values for .line and .column,
// it is most likely a programmer error. In this case we throw a very
// specific error message to try to guide them the right way.
// For example: https://github.com/Polymer/polymer-bundler/pull/519
if (aOriginal && typeof aOriginal.line !== "number" && typeof aOriginal.column !== "number") {
throw new Error(
"original.line and original.column are not numbers -- you probably meant to omit " +
"the original mapping entirely and only map the generated position. If so, pass " +
"null for the original mapping instead of an object with empty or null values."
);
}
if (aGenerated && "line" in aGenerated && "column" in aGenerated
&& aGenerated.line > 0 && aGenerated.column >= 0
&& !aOriginal && !aSource && !aName) {
// Case 1.
} else if (aGenerated && "line" in aGenerated && "column" in aGenerated
&& aOriginal && "line" in aOriginal && "column" in aOriginal
&& aGenerated.line > 0 && aGenerated.column >= 0
&& aOriginal.line > 0 && aOriginal.column >= 0
&& aSource) {
// Cases 2 and 3.
} else {
throw new Error("Invalid mapping: " + JSON.stringify({
generated: aGenerated,
source: aSource,
original: aOriginal,
name: aName
}));
}
}
/**
* Serialize the accumulated mappings in to the stream of base 64 VLQs
* specified by the source map format.
*/
_serializeMappings() {
let previousGeneratedColumn = 0;
let previousGeneratedLine = 1;
let previousOriginalColumn = 0;
let previousOriginalLine = 0;
let previousName = 0;
let previousSource = 0;
let result = "";
let next;
let mapping;
let nameIdx;
let sourceIdx;
const mappings = this._mappings.toArray();
for (let i = 0, len = mappings.length; i < len; i++) {
mapping = mappings[i];
next = "";
if (mapping.generatedLine !== previousGeneratedLine) {
previousGeneratedColumn = 0;
while (mapping.generatedLine !== previousGeneratedLine) {
next += ";";
previousGeneratedLine++;
}
} else if (i > 0) {
if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) {
continue;
}
next += ",";
}
next += base64VLQ.encode(mapping.generatedColumn
- previousGeneratedColumn);
previousGeneratedColumn = mapping.generatedColumn;
if (mapping.source != null) {
sourceIdx = this._sources.indexOf(mapping.source);
next += base64VLQ.encode(sourceIdx - previousSource);
previousSource = sourceIdx;
// lines are stored 0-based in SourceMap spec version 3
next += base64VLQ.encode(mapping.originalLine - 1
- previousOriginalLine);
previousOriginalLine = mapping.originalLine - 1;
next += base64VLQ.encode(mapping.originalColumn
- previousOriginalColumn);
previousOriginalColumn = mapping.originalColumn;
if (mapping.name != null) {
nameIdx = this._names.indexOf(mapping.name);
next += base64VLQ.encode(nameIdx - previousName);
previousName = nameIdx;
}
}
result += next;
}
return result;
}
_generateSourcesContent(aSources, aSourceRoot) {
return aSources.map(function(source) {
if (!this._sourcesContents) {
return null;
}
if (aSourceRoot != null) {
source = util.relative(aSourceRoot, source);
}
const key = util.toSetString(source);
return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
? this._sourcesContents[key]
: null;
}, this);
}
/**
* Externalize the source map.
*/
toJSON() {
const map = {
version: this._version,
sources: this._sources.toArray(),
names: this._names.toArray(),
mappings: this._serializeMappings()
};
if (this._file != null) {
map.file = this._file;
}
if (this._sourceRoot != null) {
map.sourceRoot = this._sourceRoot;
}
if (this._sourcesContents) {
map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
}
return map;
}
/**
* Render the source map being generated to a string.
*/
toString() {
return JSON.stringify(this.toJSON());
}
}
SourceMapGenerator.prototype._version = 3;
exports.SourceMapGenerator = SourceMapGenerator;

View File

@@ -0,0 +1,404 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const SourceMapGenerator = require("./source-map-generator").SourceMapGenerator;
const util = require("./util");
// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
// operating systems these days (capturing the result).
const REGEX_NEWLINE = /(\r?\n)/;
// Newline character code for charCodeAt() comparisons
const NEWLINE_CODE = 10;
// Private symbol for identifying `SourceNode`s when multiple versions of
// the source-map library are loaded. This MUST NOT CHANGE across
// versions!
const isSourceNode = "$$$isSourceNode$$$";
/**
* SourceNodes provide a way to abstract over interpolating/concatenating
* snippets of generated JavaScript source code while maintaining the line and
* column information associated with the original source code.
*
* @param aLine The original line number.
* @param aColumn The original column number.
* @param aSource The original source's filename.
* @param aChunks Optional. An array of strings which are snippets of
* generated JS, or other SourceNodes.
* @param aName The original identifier.
*/
class SourceNode {
constructor(aLine, aColumn, aSource, aChunks, aName) {
this.children = [];
this.sourceContents = {};
this.line = aLine == null ? null : aLine;
this.column = aColumn == null ? null : aColumn;
this.source = aSource == null ? null : aSource;
this.name = aName == null ? null : aName;
this[isSourceNode] = true;
if (aChunks != null) this.add(aChunks);
}
/**
* Creates a SourceNode from generated code and a SourceMapConsumer.
*
* @param aGeneratedCode The generated code
* @param aSourceMapConsumer The SourceMap for the generated code
* @param aRelativePath Optional. The path that relative sources in the
* SourceMapConsumer should be relative to.
*/
static fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
// The SourceNode we want to fill with the generated code
// and the SourceMap
const node = new SourceNode();
// All even indices of this array are one line of the generated code,
// while all odd indices are the newlines between two adjacent lines
// (since `REGEX_NEWLINE` captures its match).
// Processed fragments are accessed by calling `shiftNextLine`.
const remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
let remainingLinesIndex = 0;
const shiftNextLine = function() {
const lineContents = getNextLine();
// The last line of a file might not have a newline.
const newLine = getNextLine() || "";
return lineContents + newLine;
function getNextLine() {
return remainingLinesIndex < remainingLines.length ?
remainingLines[remainingLinesIndex++] : undefined;
}
};
// We need to remember the position of "remainingLines"
let lastGeneratedLine = 1, lastGeneratedColumn = 0;
// The generate SourceNodes we need a code range.
// To extract it current and last mapping is used.
// Here we store the last mapping.
let lastMapping = null;
let nextLine;
aSourceMapConsumer.eachMapping(function(mapping) {
if (lastMapping !== null) {
// We add the code from "lastMapping" to "mapping":
// First check if there is a new line in between.
if (lastGeneratedLine < mapping.generatedLine) {
// Associate first line with "lastMapping"
addMappingWithCode(lastMapping, shiftNextLine());
lastGeneratedLine++;
lastGeneratedColumn = 0;
// The remaining code is added without mapping
} else {
// There is no new line in between.
// Associate the code between "lastGeneratedColumn" and
// "mapping.generatedColumn" with "lastMapping"
nextLine = remainingLines[remainingLinesIndex] || "";
const code = nextLine.substr(0, mapping.generatedColumn -
lastGeneratedColumn);
remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
lastGeneratedColumn);
lastGeneratedColumn = mapping.generatedColumn;
addMappingWithCode(lastMapping, code);
// No more remaining code, continue
lastMapping = mapping;
return;
}
}
// We add the generated code until the first mapping
// to the SourceNode without any mapping.
// Each line is added as separate string.
while (lastGeneratedLine < mapping.generatedLine) {
node.add(shiftNextLine());
lastGeneratedLine++;
}
if (lastGeneratedColumn < mapping.generatedColumn) {
nextLine = remainingLines[remainingLinesIndex] || "";
node.add(nextLine.substr(0, mapping.generatedColumn));
remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
lastGeneratedColumn = mapping.generatedColumn;
}
lastMapping = mapping;
}, this);
// We have processed all mappings.
if (remainingLinesIndex < remainingLines.length) {
if (lastMapping) {
// Associate the remaining code in the current line with "lastMapping"
addMappingWithCode(lastMapping, shiftNextLine());
}
// and add the remaining lines without any mapping
node.add(remainingLines.splice(remainingLinesIndex).join(""));
}
// Copy sourcesContent into SourceNode
aSourceMapConsumer.sources.forEach(function(sourceFile) {
const content = aSourceMapConsumer.sourceContentFor(sourceFile);
if (content != null) {
if (aRelativePath != null) {
sourceFile = util.join(aRelativePath, sourceFile);
}
node.setSourceContent(sourceFile, content);
}
});
return node;
function addMappingWithCode(mapping, code) {
if (mapping === null || mapping.source === undefined) {
node.add(code);
} else {
const source = aRelativePath
? util.join(aRelativePath, mapping.source)
: mapping.source;
node.add(new SourceNode(mapping.originalLine,
mapping.originalColumn,
source,
code,
mapping.name));
}
}
}
/**
* Add a chunk of generated JS to this source node.
*
* @param aChunk A string snippet of generated JS code, another instance of
* SourceNode, or an array where each member is one of those things.
*/
add(aChunk) {
if (Array.isArray(aChunk)) {
aChunk.forEach(function(chunk) {
this.add(chunk);
}, this);
} else if (aChunk[isSourceNode] || typeof aChunk === "string") {
if (aChunk) {
this.children.push(aChunk);
}
} else {
throw new TypeError(
"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
);
}
return this;
}
/**
* Add a chunk of generated JS to the beginning of this source node.
*
* @param aChunk A string snippet of generated JS code, another instance of
* SourceNode, or an array where each member is one of those things.
*/
prepend(aChunk) {
if (Array.isArray(aChunk)) {
for (let i = aChunk.length - 1; i >= 0; i--) {
this.prepend(aChunk[i]);
}
} else if (aChunk[isSourceNode] || typeof aChunk === "string") {
this.children.unshift(aChunk);
} else {
throw new TypeError(
"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
);
}
return this;
}
/**
* Walk over the tree of JS snippets in this node and its children. The
* walking function is called once for each snippet of JS and is passed that
* snippet and the its original associated source's line/column location.
*
* @param aFn The traversal function.
*/
walk(aFn) {
let chunk;
for (let i = 0, len = this.children.length; i < len; i++) {
chunk = this.children[i];
if (chunk[isSourceNode]) {
chunk.walk(aFn);
} else if (chunk !== "") {
aFn(chunk, { source: this.source,
line: this.line,
column: this.column,
name: this.name });
}
}
}
/**
* Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
* each of `this.children`.
*
* @param aSep The separator.
*/
join(aSep) {
let newChildren;
let i;
const len = this.children.length;
if (len > 0) {
newChildren = [];
for (i = 0; i < len - 1; i++) {
newChildren.push(this.children[i]);
newChildren.push(aSep);
}
newChildren.push(this.children[i]);
this.children = newChildren;
}
return this;
}
/**
* Call String.prototype.replace on the very right-most source snippet. Useful
* for trimming whitespace from the end of a source node, etc.
*
* @param aPattern The pattern to replace.
* @param aReplacement The thing to replace the pattern with.
*/
replaceRight(aPattern, aReplacement) {
const lastChild = this.children[this.children.length - 1];
if (lastChild[isSourceNode]) {
lastChild.replaceRight(aPattern, aReplacement);
} else if (typeof lastChild === "string") {
this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
} else {
this.children.push("".replace(aPattern, aReplacement));
}
return this;
}
/**
* Set the source content for a source file. This will be added to the SourceMapGenerator
* in the sourcesContent field.
*
* @param aSourceFile The filename of the source file
* @param aSourceContent The content of the source file
*/
setSourceContent(aSourceFile, aSourceContent) {
this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
}
/**
* Walk over the tree of SourceNodes. The walking function is called for each
* source file content and is passed the filename and source content.
*
* @param aFn The traversal function.
*/
walkSourceContents(aFn) {
for (let i = 0, len = this.children.length; i < len; i++) {
if (this.children[i][isSourceNode]) {
this.children[i].walkSourceContents(aFn);
}
}
const sources = Object.keys(this.sourceContents);
for (let i = 0, len = sources.length; i < len; i++) {
aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
}
}
/**
* Return the string representation of this source node. Walks over the tree
* and concatenates all the various snippets together to one string.
*/
toString() {
let str = "";
this.walk(function(chunk) {
str += chunk;
});
return str;
}
/**
* Returns the string representation of this source node along with a source
* map.
*/
toStringWithSourceMap(aArgs) {
const generated = {
code: "",
line: 1,
column: 0
};
const map = new SourceMapGenerator(aArgs);
let sourceMappingActive = false;
let lastOriginalSource = null;
let lastOriginalLine = null;
let lastOriginalColumn = null;
let lastOriginalName = null;
this.walk(function(chunk, original) {
generated.code += chunk;
if (original.source !== null
&& original.line !== null
&& original.column !== null) {
if (lastOriginalSource !== original.source
|| lastOriginalLine !== original.line
|| lastOriginalColumn !== original.column
|| lastOriginalName !== original.name) {
map.addMapping({
source: original.source,
original: {
line: original.line,
column: original.column
},
generated: {
line: generated.line,
column: generated.column
},
name: original.name
});
}
lastOriginalSource = original.source;
lastOriginalLine = original.line;
lastOriginalColumn = original.column;
lastOriginalName = original.name;
sourceMappingActive = true;
} else if (sourceMappingActive) {
map.addMapping({
generated: {
line: generated.line,
column: generated.column
}
});
lastOriginalSource = null;
sourceMappingActive = false;
}
for (let idx = 0, length = chunk.length; idx < length; idx++) {
if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
generated.line++;
generated.column = 0;
// Mappings end at eol
if (idx + 1 === length) {
lastOriginalSource = null;
sourceMappingActive = false;
} else if (sourceMappingActive) {
map.addMapping({
source: original.source,
original: {
line: original.line,
column: original.column
},
generated: {
line: generated.line,
column: generated.column
},
name: original.name
});
}
} else {
generated.column++;
}
}
});
this.walkSourceContents(function(sourceFile, sourceContent) {
map.setSourceContent(sourceFile, sourceContent);
});
return { code: generated.code, map };
}
}
exports.SourceNode = SourceNode;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,107 @@
const readWasm = require("../lib/read-wasm");
/**
* Provide the JIT with a nice shape / hidden class.
*/
function Mapping() {
this.generatedLine = 0;
this.generatedColumn = 0;
this.lastGeneratedColumn = null;
this.source = null;
this.originalLine = null;
this.originalColumn = null;
this.name = null;
}
let cachedWasm = null;
module.exports = function wasm() {
if (cachedWasm) {
return cachedWasm;
}
const callbackStack = [];
cachedWasm = readWasm().then(buffer => {
return WebAssembly.instantiate(buffer, {
env: {
mapping_callback(
generatedLine,
generatedColumn,
hasLastGeneratedColumn,
lastGeneratedColumn,
hasOriginal,
source,
originalLine,
originalColumn,
hasName,
name
) {
const mapping = new Mapping();
// JS uses 1-based line numbers, wasm uses 0-based.
mapping.generatedLine = generatedLine + 1;
mapping.generatedColumn = generatedColumn;
if (hasLastGeneratedColumn) {
// JS uses inclusive last generated column, wasm uses exclusive.
mapping.lastGeneratedColumn = lastGeneratedColumn - 1;
}
if (hasOriginal) {
mapping.source = source;
// JS uses 1-based line numbers, wasm uses 0-based.
mapping.originalLine = originalLine + 1;
mapping.originalColumn = originalColumn;
if (hasName) {
mapping.name = name;
}
}
callbackStack[callbackStack.length - 1](mapping);
},
start_all_generated_locations_for() { console.time("all_generated_locations_for"); },
end_all_generated_locations_for() { console.timeEnd("all_generated_locations_for"); },
start_compute_column_spans() { console.time("compute_column_spans"); },
end_compute_column_spans() { console.timeEnd("compute_column_spans"); },
start_generated_location_for() { console.time("generated_location_for"); },
end_generated_location_for() { console.timeEnd("generated_location_for"); },
start_original_location_for() { console.time("original_location_for"); },
end_original_location_for() { console.timeEnd("original_location_for"); },
start_parse_mappings() { console.time("parse_mappings"); },
end_parse_mappings() { console.timeEnd("parse_mappings"); },
start_sort_by_generated_location() { console.time("sort_by_generated_location"); },
end_sort_by_generated_location() { console.timeEnd("sort_by_generated_location"); },
start_sort_by_original_location() { console.time("sort_by_original_location"); },
end_sort_by_original_location() { console.timeEnd("sort_by_original_location"); },
}
});
}).then(Wasm => {
return {
exports: Wasm.instance.exports,
withMappingCallback: (mappingCallback, f) => {
callbackStack.push(mappingCallback);
try {
f();
} finally {
callbackStack.pop();
}
}
};
}).then(null, e => {
cachedWasm = null;
throw e;
});
return cachedWasm;
};

View File

@@ -0,0 +1,91 @@
{
"name": "source-map",
"description": "Generates and consumes source maps",
"version": "0.7.4",
"homepage": "https://github.com/mozilla/source-map",
"author": "Nick Fitzgerald <nfitzgerald@mozilla.com>",
"contributors": [
"Tobias Koppers <tobias.koppers@googlemail.com>",
"Duncan Beevers <duncan@dweebd.com>",
"Stephen Crane <scrane@mozilla.com>",
"Ryan Seddon <seddon.ryan@gmail.com>",
"Miles Elam <miles.elam@deem.com>",
"Mihai Bazon <mihai.bazon@gmail.com>",
"Michael Ficarra <github.public.email@michael.ficarra.me>",
"Todd Wolfson <todd@twolfson.com>",
"Alexander Solovyov <alexander@solovyov.net>",
"Felix Gnass <fgnass@gmail.com>",
"Conrad Irwin <conrad.irwin@gmail.com>",
"usrbincc <usrbincc@yahoo.com>",
"David Glasser <glasser@davidglasser.net>",
"Chase Douglas <chase@newrelic.com>",
"Evan Wallace <evan.exe@gmail.com>",
"Heather Arthur <fayearthur@gmail.com>",
"Hugh Kennedy <hughskennedy@gmail.com>",
"David Glasser <glasser@davidglasser.net>",
"Simon Lydell <simon.lydell@gmail.com>",
"Jmeas Smith <jellyes2@gmail.com>",
"Michael Z Goddard <mzgoddard@gmail.com>",
"azu <azu@users.noreply.github.com>",
"John Gozde <john@gozde.ca>",
"Adam Kirkton <akirkton@truefitinnovation.com>",
"Chris Montgomery <christopher.montgomery@dowjones.com>",
"J. Ryan Stinnett <jryans@gmail.com>",
"Jack Herrington <jherrington@walmartlabs.com>",
"Chris Truter <jeffpalentine@gmail.com>",
"Daniel Espeset <daniel@danielespeset.com>",
"Jamie Wong <jamie.lf.wong@gmail.com>",
"Eddy Bruël <ejpbruel@mozilla.com>",
"Hawken Rives <hawkrives@gmail.com>",
"Gilad Peleg <giladp007@gmail.com>",
"djchie <djchie.dev@gmail.com>",
"Gary Ye <garysye@gmail.com>",
"Nicolas Lalevée <nicolas.lalevee@hibnet.org>"
],
"repository": {
"type": "git",
"url": "http://github.com/mozilla/source-map.git"
},
"main": "./source-map.js",
"types": "./source-map.d.ts",
"files": [
"source-map.js",
"source-map.d.ts",
"lib/",
"dist/source-map.js"
],
"engines": {
"node": ">= 8"
},
"license": "BSD-3-Clause",
"scripts": {
"lint": "eslint *.js lib/ test/",
"prebuild": "npm run lint",
"build": "webpack --color",
"pretest": "npm run build",
"test": "node test/run-tests.js",
"precoverage": "npm run build",
"coverage": "nyc node test/run-tests.js",
"setup": "mkdir -p coverage && cp -n .waiting.html coverage/index.html || true",
"dev:live": "live-server --port=4103 --ignorePattern='(js|css|png)$' coverage",
"dev:watch": "watch 'npm run coverage' lib/ test/",
"predev": "npm run setup",
"dev": "npm-run-all -p --silent dev:*",
"clean": "rm -rf coverage .nyc_output",
"toc": "doctoc --title '## Table of Contents' README.md && doctoc --title '## Table of Contents' CONTRIBUTING.md"
},
"devDependencies": {
"doctoc": "^1.3.1",
"eslint": "^4.19.1",
"live-server": "^1.2.0",
"npm-run-all": "^4.1.2",
"nyc": "^11.7.1",
"watch": "^1.0.2",
"webpack": "^4.9.1",
"webpack-cli": "^3.1"
},
"nyc": {
"reporter": "html"
},
"typings": "source-map"
}

View File

@@ -0,0 +1,369 @@
// Type definitions for source-map 0.7
// Project: https://github.com/mozilla/source-map
// Definitions by: Morten Houston Ludvigsen <https://github.com/MortenHoustonLudvigsen>,
// Ron Buckton <https://github.com/rbuckton>,
// John Vilk <https://github.com/jvilk>
// Definitions: https://github.com/mozilla/source-map
export type SourceMapUrl = string;
export interface StartOfSourceMap {
file?: string;
sourceRoot?: string;
skipValidation?: boolean;
}
export interface RawSourceMap {
version: number;
sources: string[];
names: string[];
sourceRoot?: string;
sourcesContent?: string[];
mappings: string;
file: string;
}
export interface RawIndexMap extends StartOfSourceMap {
version: number;
sections: RawSection[];
}
export interface RawSection {
offset: Position;
map: RawSourceMap;
}
export interface Position {
line: number;
column: number;
}
export interface NullablePosition {
line: number | null;
column: number | null;
lastColumn: number | null;
}
export interface MappedPosition {
source: string;
line: number;
column: number;
name?: string;
}
export interface NullableMappedPosition {
source: string | null;
line: number | null;
column: number | null;
name: string | null;
}
export interface MappingItem {
source: string;
generatedLine: number;
generatedColumn: number;
originalLine: number;
originalColumn: number;
name: string;
}
export interface Mapping {
generated: Position;
original: Position;
source: string;
name?: string;
}
export interface CodeWithSourceMap {
code: string;
map: SourceMapGenerator;
}
export interface SourceMapConsumer {
/**
* Compute the last column for each generated mapping. The last column is
* inclusive.
*/
computeColumnSpans(): void;
/**
* Returns the original source, line, and column information for the generated
* source's line and column positions provided. The only argument is an object
* with the following properties:
*
* - line: The line number in the generated source.
* - column: The column number in the generated source.
* - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
* 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
* Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
*
* and an object is returned with the following properties:
*
* - source: The original source file, or null.
* - line: The line number in the original source, or null.
* - column: The column number in the original source, or null.
* - name: The original identifier, or null.
*/
originalPositionFor(generatedPosition: Position & { bias?: number }): NullableMappedPosition;
/**
* Returns the generated line and column information for the original source,
* line, and column positions provided. The only argument is an object with
* the following properties:
*
* - source: The filename of the original source.
* - line: The line number in the original source.
* - column: The column number in the original source.
* - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
* 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
* Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
*
* and an object is returned with the following properties:
*
* - line: The line number in the generated source, or null.
* - column: The column number in the generated source, or null.
*/
generatedPositionFor(originalPosition: MappedPosition & { bias?: number }): NullablePosition;
/**
* Returns all generated line and column information for the original source,
* line, and column provided. If no column is provided, returns all mappings
* corresponding to a either the line we are searching for or the next
* closest line that has any mappings. Otherwise, returns all mappings
* corresponding to the given line and either the column we are searching for
* or the next closest column that has any offsets.
*
* The only argument is an object with the following properties:
*
* - source: The filename of the original source.
* - line: The line number in the original source.
* - column: Optional. the column number in the original source.
*
* and an array of objects is returned, each with the following properties:
*
* - line: The line number in the generated source, or null.
* - column: The column number in the generated source, or null.
*/
allGeneratedPositionsFor(originalPosition: MappedPosition): NullablePosition[];
/**
* Return true if we have the source content for every source in the source
* map, false otherwise.
*/
hasContentsOfAllSources(): boolean;
/**
* Returns the original source content. The only argument is the url of the
* original source file. Returns null if no original source content is
* available.
*/
sourceContentFor(source: string, returnNullOnMissing?: boolean): string | null;
/**
* Iterate over each mapping between an original source/line/column and a
* generated line/column in this source map.
*
* @param callback
* The function that is called with each mapping.
* @param context
* Optional. If specified, this object will be the value of `this` every
* time that `aCallback` is called.
* @param order
* Either `SourceMapConsumer.GENERATED_ORDER` or
* `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
* iterate over the mappings sorted by the generated file's line/column
* order or the original's source/line/column order, respectively. Defaults to
* `SourceMapConsumer.GENERATED_ORDER`.
*/
eachMapping(callback: (mapping: MappingItem) => void, context?: any, order?: number): void;
/**
* Free this source map consumer's associated wasm data that is manually-managed.
* Alternatively, you can use SourceMapConsumer.with to avoid needing to remember to call destroy.
*/
destroy(): void;
}
export interface SourceMapConsumerConstructor {
prototype: SourceMapConsumer;
GENERATED_ORDER: number;
ORIGINAL_ORDER: number;
GREATEST_LOWER_BOUND: number;
LEAST_UPPER_BOUND: number;
new (rawSourceMap: RawSourceMap, sourceMapUrl?: SourceMapUrl): Promise<BasicSourceMapConsumer>;
new (rawSourceMap: RawIndexMap, sourceMapUrl?: SourceMapUrl): Promise<IndexedSourceMapConsumer>;
new (rawSourceMap: RawSourceMap | RawIndexMap | string, sourceMapUrl?: SourceMapUrl): Promise<BasicSourceMapConsumer | IndexedSourceMapConsumer>;
/**
* Create a BasicSourceMapConsumer from a SourceMapGenerator.
*
* @param sourceMap
* The source map that will be consumed.
*/
fromSourceMap(sourceMap: SourceMapGenerator, sourceMapUrl?: SourceMapUrl): Promise<BasicSourceMapConsumer>;
/**
* Construct a new `SourceMapConsumer` from `rawSourceMap` and `sourceMapUrl`
* (see the `SourceMapConsumer` constructor for details. Then, invoke the `async
* function f(SourceMapConsumer) -> T` with the newly constructed consumer, wait
* for `f` to complete, call `destroy` on the consumer, and return `f`'s return
* value.
*
* You must not use the consumer after `f` completes!
*
* By using `with`, you do not have to remember to manually call `destroy` on
* the consumer, since it will be called automatically once `f` completes.
*
* ```js
* const xSquared = await SourceMapConsumer.with(
* myRawSourceMap,
* null,
* async function (consumer) {
* // Use `consumer` inside here and don't worry about remembering
* // to call `destroy`.
*
* const x = await whatever(consumer);
* return x * x;
* }
* );
*
* // You may not use that `consumer` anymore out here; it has
* // been destroyed. But you can use `xSquared`.
* console.log(xSquared);
* ```
*/
with<T>(rawSourceMap: RawSourceMap | RawIndexMap | string, sourceMapUrl: SourceMapUrl | null | undefined, callback: (consumer: BasicSourceMapConsumer | IndexedSourceMapConsumer) => Promise<T> | T): Promise<T>;
}
export const SourceMapConsumer: SourceMapConsumerConstructor;
export interface BasicSourceMapConsumer extends SourceMapConsumer {
file: string;
sourceRoot: string;
sources: string[];
sourcesContent: string[];
}
export interface BasicSourceMapConsumerConstructor {
prototype: BasicSourceMapConsumer;
new (rawSourceMap: RawSourceMap | string): Promise<BasicSourceMapConsumer>;
/**
* Create a BasicSourceMapConsumer from a SourceMapGenerator.
*
* @param sourceMap
* The source map that will be consumed.
*/
fromSourceMap(sourceMap: SourceMapGenerator): Promise<BasicSourceMapConsumer>;
}
export const BasicSourceMapConsumer: BasicSourceMapConsumerConstructor;
export interface IndexedSourceMapConsumer extends SourceMapConsumer {
sources: string[];
}
export interface IndexedSourceMapConsumerConstructor {
prototype: IndexedSourceMapConsumer;
new (rawSourceMap: RawIndexMap | string): Promise<IndexedSourceMapConsumer>;
}
export const IndexedSourceMapConsumer: IndexedSourceMapConsumerConstructor;
export class SourceMapGenerator {
constructor(startOfSourceMap?: StartOfSourceMap);
/**
* Creates a new SourceMapGenerator based on a SourceMapConsumer
*
* @param sourceMapConsumer The SourceMap.
*/
static fromSourceMap(sourceMapConsumer: SourceMapConsumer): SourceMapGenerator;
/**
* Add a single mapping from original source line and column to the generated
* source's line and column for this source map being created. The mapping
* object should have the following properties:
*
* - generated: An object with the generated line and column positions.
* - original: An object with the original line and column positions.
* - source: The original source file (relative to the sourceRoot).
* - name: An optional original token name for this mapping.
*/
addMapping(mapping: Mapping): void;
/**
* Set the source content for a source file.
*/
setSourceContent(sourceFile: string, sourceContent: string): void;
/**
* Applies the mappings of a sub-source-map for a specific source file to the
* source map being generated. Each mapping to the supplied source file is
* rewritten using the supplied source map. Note: The resolution for the
* resulting mappings is the minimium of this map and the supplied map.
*
* @param sourceMapConsumer The source map to be applied.
* @param sourceFile Optional. The filename of the source file.
* If omitted, SourceMapConsumer's file property will be used.
* @param sourceMapPath Optional. The dirname of the path to the source map
* to be applied. If relative, it is relative to the SourceMapConsumer.
* This parameter is needed when the two source maps aren't in the same
* directory, and the source map to be applied contains relative source
* paths. If so, those relative source paths need to be rewritten
* relative to the SourceMapGenerator.
*/
applySourceMap(sourceMapConsumer: SourceMapConsumer, sourceFile?: string, sourceMapPath?: string): void;
toString(): string;
toJSON(): RawSourceMap;
}
export class SourceNode {
children: SourceNode[];
sourceContents: any;
line: number;
column: number;
source: string;
name: string;
constructor();
constructor(
line: number | null,
column: number | null,
source: string | null,
chunks?: Array<(string | SourceNode)> | SourceNode | string,
name?: string
);
static fromStringWithSourceMap(
code: string,
sourceMapConsumer: SourceMapConsumer,
relativePath?: string
): SourceNode;
add(chunk: Array<(string | SourceNode)> | SourceNode | string): SourceNode;
prepend(chunk: Array<(string | SourceNode)> | SourceNode | string): SourceNode;
setSourceContent(sourceFile: string, sourceContent: string): void;
walk(fn: (chunk: string, mapping: MappedPosition) => void): void;
walkSourceContents(fn: (file: string, content: string) => void): void;
join(sep: string): SourceNode;
replaceRight(pattern: string, replacement: string): SourceNode;
toString(): string;
toStringWithSourceMap(startOfSourceMap?: StartOfSourceMap): CodeWithSourceMap;
}

View File

@@ -0,0 +1,8 @@
/*
* Copyright 2009-2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE.txt or:
* http://opensource.org/licenses/BSD-3-Clause
*/
exports.SourceMapGenerator = require("./lib/source-map-generator").SourceMapGenerator;
exports.SourceMapConsumer = require("./lib/source-map-consumer").SourceMapConsumer;
exports.SourceNode = require("./lib/source-node").SourceNode;

82
node_modules/@riotjs/compiler/package.json generated vendored Normal file
View File

@@ -0,0 +1,82 @@
{
"name": "@riotjs/compiler",
"version": "6.3.2",
"description": "Compiler for riot .tag files",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"jsnext:main": "dist/index.esm.js",
"types": "./compiler.d.ts",
"files": [
"dist",
"src",
"compiler.d.ts"
],
"scripts": {
"lint": "eslint src/ test/ build/",
"cov": "nyc report --reporter=lcov",
"cov-html": "nyc report --reporter=html",
"build": "rollup -c build/rollup.node.config.js && rollup -c build/rollup.browser.config.js && rollup -c build/rollup.essential.config.js",
"postest": "npm run cov-html",
"test-types": "tsc -p test",
"test-runtime": "karma start test/karma.conf.js",
"test": "nyc mocha -r esm test/*.spec.js test/**/*.spec.js && npm run test-types",
"debug": "mocha --inspect --inspect-brk -r esm test/*.spec.js test/**/*.spec.js",
"prepublishOnly": "npm run build && npm run lint && npm run test && npm run test-runtime"
},
"repository": {
"type": "git",
"url": "git+https://github.com/riot/compiler.git"
},
"keywords": [
"riot",
"Riot.js",
"components",
"custom components",
"custom elements",
"compiler"
],
"devDependencies": {
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@riotjs/dom-bindings": "^6.0.3",
"@rollup/plugin-alias": "^3.1.9",
"@rollup/plugin-commonjs": "^22.0.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"acorn": "^8.8.0",
"chai": "^4.3.6",
"eslint": "^8.21.0",
"eslint-config-riot": "^3.0.0",
"esm": "^3.2.25",
"karma": "^6.4.0",
"karma-chrome-launcher": "^3.1.1",
"karma-mocha": "^2.0.1",
"mocha": "^8.4.0",
"node-sass": "^7.0.1",
"nyc": "^15.1.0",
"pug": "^3.0.2",
"rollup": "^2.77.2",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-visualizer": "^5.7.1",
"shelljs": "^0.8.5",
"typescript": "^4.7.4"
},
"author": "Gianluca Guarini <gianluca.guarini@gmail.com> (http://gianlucaguarini.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/riot/compiler/issues"
},
"homepage": "https://github.com/riot/compiler#readme",
"dependencies": {
"@babel/parser": "^7.18.10",
"@riotjs/parser": "^4.3.1",
"@riotjs/util": "2.1.1",
"cssesc": "^3.0.0",
"cumpa": "^1.0.1",
"curri": "^1.0.1",
"dom-nodes": "^1.1.3",
"globals": "^13.17.0",
"recast": "^0.20.5",
"source-map": "^0.7.4"
}
}

7
node_modules/@riotjs/compiler/src/constants.js generated vendored Normal file
View File

@@ -0,0 +1,7 @@
export const TAG_LOGIC_PROPERTY = 'exports'
export const TAG_CSS_PROPERTY = 'css'
export const TAG_TEMPLATE_PROPERTY = 'template'
export const TAG_NAME_PROPERTY = 'name'
export const RIOT_MODULE_ID = 'riot'
export const RIOT_INTERFACE_WRAPPER_NAME = 'RiotComponentWrapper'
export const RIOT_TAG_INTERFACE_NAME = 'RiotComponent'

View File

@@ -0,0 +1,124 @@
import {builders, types} from '../../utils/build-types'
import {TAG_CSS_PROPERTY} from '../../constants'
import cssEscape from 'cssesc'
import getPreprocessorTypeByAttribute from '../../utils/get-preprocessor-type-by-attribute'
import preprocess from '../../utils/preprocess-node'
/**
* Matches valid, multiline JavaScript comments in almost all its forms.
* @const {RegExp}
* @static
*/
const R_MLCOMMS = /\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//g
/**
* Source for creating regexes matching valid quoted, single-line JavaScript strings.
* It recognizes escape characters, including nested quotes and line continuation.
* @const {string}
*/
const S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source
/**
* Matches CSS selectors, excluding those beginning with '@' and quoted strings.
* @const {RegExp}
*/
const CSS_SELECTOR = RegExp(`([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|${S_LINESTR}`, 'g')
/**
* Parses styles enclosed in a "scoped" tag
* The "css" string is received without comments or surrounding spaces.
*
* @param {string} tag - Tag name of the root element
* @param {string} css - The CSS code
* @returns {string} CSS with the styles scoped to the root element
*/
function scopedCSS(tag, css) {
const host = ':host'
const selectorsBlacklist = ['from', 'to']
return css.replace(CSS_SELECTOR, function(m, p1, p2) {
// skip quoted strings
if (!p2) return m
// we have a selector list, parse each individually
p2 = p2.replace(/[^,]+/g, function(sel) {
const s = sel.trim()
// skip selectors already using the tag name
if (s.indexOf(tag) === 0) {
return sel
}
// skips the keywords and percents of css animations
if (!s || selectorsBlacklist.indexOf(s) > -1 || s.slice(-1) === '%') {
return sel
}
// replace the `:host` pseudo-selector, where it is, with the root tag name;
// if `:host` was not included, add the tag name as prefix, and mirror all
// `[data-is]`
if (s.indexOf(host) < 0) {
return `${tag} ${s},[is="${tag}"] ${s}`
} else {
return `${s.replace(host, tag)},${
s.replace(host, `[is="${tag}"]`)}`
}
})
// add the danling bracket char and return the processed selector list
return p1 ? `${p1} ${p2}` : p2
})
}
/**
* Remove comments, compact and trim whitespace
* @param { string } code - compiled css code
* @returns { string } css code normalized
*/
function compactCss(code) {
return code.replace(R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
}
const escapeBackslashes = s => s.replace(/\\/g, '\\\\')
const escapeIdentifier = identifier => escapeBackslashes(cssEscape(identifier, {
isIdentifier: true
}))
/**
* Generate the component css
* @param { Object } sourceNode - node generated by the riot compiler
* @param { string } source - original component source code
* @param { Object } meta - compilation meta information
* @param { AST } ast - current AST output
* @returns { AST } the AST generated
*/
export default function css(sourceNode, source, meta, ast) {
const preprocessorName = getPreprocessorTypeByAttribute(sourceNode)
const { options } = meta
const preprocessorOutput = preprocess('css', preprocessorName, meta, sourceNode.text)
const normalizedCssCode = compactCss(preprocessorOutput.code)
const escapedCssIdentifier = escapeIdentifier(meta.tagName)
const cssCode = (options.scopedCss ?
scopedCSS(escapedCssIdentifier, escapeBackslashes(normalizedCssCode)) :
escapeBackslashes(normalizedCssCode)
).trim()
types.visit(ast, {
visitProperty(path) {
if (path.value.key.name === TAG_CSS_PROPERTY) {
path.value.value = builders.templateLiteral(
[builders.templateElement({ raw: cssCode, cooked: '' }, false)],
[]
)
return false
}
this.traverse(path)
}
})
return ast
}

View File

@@ -0,0 +1,73 @@
import {
addComponentInterfaceToExportedObject,
createDefaultExportFromLegacySyntax,
extendTagProperty,
filterNonExportDefaultStatements, findAllExportNamedDeclarations,
findAllImportDeclarations, findComponentInterface,
findExportDefaultStatement,
getProgramBody
} from './utils'
import addLinesOffset from '../../utils/add-lines-offset'
import generateAST from '../../utils/generate-ast'
import getPreprocessorTypeByAttribute from '../../utils/get-preprocessor-type-by-attribute'
import isEmptySourcemap from '../../utils/is-empty-sourcemap'
import {isNil} from '@riotjs/util/checks'
import {isThisExpressionStatement} from '../../utils/ast-nodes-checks'
import preprocess from '../../utils/preprocess-node'
import sourcemapToJSON from '../../utils/sourcemap-as-json'
/**
* Generate the component javascript logic
* @param { Object } sourceNode - node generated by the riot compiler
* @param { string } source - original component source code
* @param { Object } meta - compilation meta information
* @param { AST } ast - current AST output
* @returns { AST } the AST generated
*/
export default function javascript(sourceNode, source, meta, ast) {
const preprocessorName = getPreprocessorTypeByAttribute(sourceNode)
const javascriptNode = addLinesOffset(sourceNode.text.text, source, sourceNode)
const { options } = meta
const preprocessorOutput = preprocess('javascript', preprocessorName, meta, {
...sourceNode,
text: javascriptNode
})
const inputSourceMap = sourcemapToJSON(preprocessorOutput.map)
const generatedAst = generateAST(preprocessorOutput.code, {
sourceFileName: options.file,
inputSourceMap: isEmptySourcemap(inputSourceMap) ? null : inputSourceMap
})
const generatedAstBody = getProgramBody(generatedAst)
const exportDefaultNode = findExportDefaultStatement(generatedAstBody)
const isLegacyRiotSyntax = isNil(exportDefaultNode)
const outputBody = getProgramBody(ast)
const componentInterface = findComponentInterface(generatedAstBody)
// throw in case of mixed component exports
if (exportDefaultNode && generatedAstBody.some(isThisExpressionStatement))
throw new Error('You can\t use "export default {}" and root this statements in the same component')
// add to the ast the "private" javascript content of our tag script node
outputBody.unshift(
...(
// for the legacy riot syntax we need to move all the import and (named) export statements outside of the function body
isLegacyRiotSyntax ?
[...findAllImportDeclarations(generatedAstBody), ...findAllExportNamedDeclarations(generatedAstBody)] :
// modern riot syntax will hoist all the private stuff outside of the export default statement
filterNonExportDefaultStatements(generatedAstBody)
))
// create the public component export properties from the root this statements
if (isLegacyRiotSyntax) extendTagProperty(
ast,
createDefaultExportFromLegacySyntax(generatedAstBody)
)
// convert the export default adding its content to the component property exported
if (exportDefaultNode) extendTagProperty(ast, exportDefaultNode)
return componentInterface ?
// add the component interface to the component object exported
addComponentInterfaceToExportedObject(ast, componentInterface) :
ast
}

View File

@@ -0,0 +1,172 @@
import {RIOT_INTERFACE_WRAPPER_NAME, RIOT_MODULE_ID, RIOT_TAG_INTERFACE_NAME, TAG_LOGIC_PROPERTY} from '../../constants'
import {builders, types} from '../../utils/build-types'
import {
isExportDefaultStatement,
isExportNamedDeclaration,
isImportDeclaration, isInterfaceDeclaration,
isThisExpressionStatement,
isTypeAliasDeclaration
} from '../../utils/ast-nodes-checks'
import compose from 'cumpa'
/**
* Find the export default statement
* @param { Array } body - tree structure containing the program code
* @returns { Object } node containing only the code of the export default statement
*/
export function findExportDefaultStatement(body) {
return body.find(isExportDefaultStatement)
}
/**
* Find all import declarations
* @param { Array } body - tree structure containing the program code
* @returns { Array } array containing all the import declarations detected
*/
export function findAllImportDeclarations(body) {
return body.filter(isImportDeclaration)
}
/**
* Find all the named export declarations
* @param { Array } body - tree structure containing the program code
* @returns { Array } array containing all the named export declarations detected
*/
export function findAllExportNamedDeclarations(body) {
return body.filter(isExportNamedDeclaration)
}
/**
* Filter all the import declarations
* @param { Array } body - tree structure containing the program code
* @returns { Array } array containing all the ast expressions without the import declarations
*/
export function filterOutAllImportDeclarations(body) {
return body.filter(n => !isImportDeclaration(n))
}
/**
* Filter all the export declarations
* @param { Array } body - tree structure containing the program code
* @returns { Array } array containing all the ast expressions without the export declarations
*/
export function filterOutAllExportDeclarations(body) {
return body.filter(n => !isExportNamedDeclaration(n) || isExportDefaultStatement(n))
}
/**
* Find the component interface exported
* @param { Array } body - tree structure containing the program code
* @returns { Object|null } the object referencing the component interface if found
*/
export function findComponentInterface(body) {
const exportNamedDeclarations = body.filter(isExportNamedDeclaration).map(n => n.declaration)
const types = exportNamedDeclarations.filter(isTypeAliasDeclaration)
const interfaces = exportNamedDeclarations.filter(isInterfaceDeclaration)
const isRiotComponentTypeName = ({ typeName }) => typeName && typeName.name ? typeName.name === RIOT_TAG_INTERFACE_NAME : false
const extendsRiotComponent = ({ expression }) => expression.name === RIOT_TAG_INTERFACE_NAME
return types.find(
node => (node.typeAnnotation.types && node.typeAnnotation.types.some(isRiotComponentTypeName)) || isRiotComponentTypeName(node.typeAnnotation)
) || interfaces.find(
node => node.extends && node.extends.some(extendsRiotComponent)
)
}
/**
* Add the component interface to the export declaration
* @param { Object } ast - ast object generated by recast
* @param { Object } componentInterface - the component typescript interface
* @returns { Object } the component object exported combined with the riot typescript interfaces
*/
export function addComponentInterfaceToExportedObject(ast, componentInterface) {
const body = getProgramBody(ast)
const RiotComponentWrapperImportSpecifier = builders.importSpecifier(
builders.identifier(RIOT_INTERFACE_WRAPPER_NAME)
)
const componentInterfaceName = componentInterface.id.name
const riotImportDeclaration = findAllImportDeclarations(body).find(node => node.source.value === RIOT_MODULE_ID)
const exportDefaultStatement = body.find(isExportDefaultStatement)
const objectExport = exportDefaultStatement.declaration
// add the RiotComponentWrapper to this component imports
if (riotImportDeclaration) {
riotImportDeclaration.specifiers.push(RiotComponentWrapperImportSpecifier)
} else {
// otherwise create the whole import statement from riot
body.unshift(0, builders.importDeclaration(
[RiotComponentWrapperImportSpecifier],
builders.stringLiteral(RIOT_MODULE_ID)
))
}
// override the object export adding the types detected
exportDefaultStatement.declaration = builders.tsAsExpression(
objectExport,
builders.tsTypeReference(
builders.identifier(RIOT_INTERFACE_WRAPPER_NAME),
builders.tsTypeParameterInstantiation(
[builders.tsTypeReference(builders.identifier(componentInterfaceName))]
)
)
)
return ast
}
/**
* Create the default export declaration interpreting the old riot syntax relying on "this" statements
* @param { Array } body - tree structure containing the program code
* @returns { Object } ExportDefaultDeclaration
*/
export function createDefaultExportFromLegacySyntax(body) {
return builders.exportDefaultDeclaration(
builders.functionDeclaration(
builders.identifier(TAG_LOGIC_PROPERTY),
[],
builders.blockStatement([
...compose(filterOutAllImportDeclarations, filterOutAllExportDeclarations)(body),
builders.returnStatement(builders.thisExpression())
])
)
)
}
/**
* Find all the code in an ast program except for the export default statements
* @param { Array } body - tree structure containing the program code
* @returns { Array } array containing all the program code except the export default expressions
*/
export function filterNonExportDefaultStatements(body) {
return body.filter(node => !isExportDefaultStatement(node) && !isThisExpressionStatement(node))
}
/**
* Get the body of the AST structure
* @param { Object } ast - ast object generated by recast
* @returns { Array } array containing the program code
*/
export function getProgramBody(ast) {
return ast.body || ast.program.body
}
/**
* Extend the AST adding the new tag method containing our tag sourcecode
* @param { Object } ast - current output ast
* @param { Object } exportDefaultNode - tag export default node
* @returns { Object } the output ast having the "tag" key extended with the content of the export default
*/
export function extendTagProperty(ast, exportDefaultNode) {
types.visit(ast, {
visitProperty(path) {
if (path.value.key.name === TAG_LOGIC_PROPERTY) {
path.value.value = exportDefaultNode.declaration
return false
}
this.traverse(path)
}
})
return ast
}

View File

@@ -0,0 +1,110 @@
import {
BINDING_CONDITION_KEY,
BINDING_EVALUATE_KEY,
BINDING_GET_KEY_KEY,
BINDING_INDEX_NAME_KEY,
BINDING_ITEM_NAME_KEY,
BINDING_TYPES,
BINDING_TYPE_KEY,
EACH_BINDING_TYPE
} from '../constants'
import {
createASTFromExpression,
createSelectorProperties,
createTemplateProperty,
getAttributeExpression,
getName,
toScopedFunction
} from '../utils'
import {findEachAttribute, findIfAttribute, findKeyAttribute} from '../find'
import {isExpressionStatement, isSequenceExpression} from '../../../utils/ast-nodes-checks'
import {nullNode, simplePropertyNode} from '../../../utils/custom-ast-nodes'
import {builders} from '../../../utils/build-types'
import compose from 'cumpa'
import {createNestedBindings} from '../builder'
import generateJavascript from '../../../utils/generate-javascript'
import {panic} from '@riotjs/util/misc'
const getEachItemName = expression => isSequenceExpression(expression.left) ? expression.left.expressions[0] : expression.left
const getEachIndexName = expression => isSequenceExpression(expression.left) ? expression.left.expressions[1] : null
const getEachValue = expression => expression.right
const nameToliteral = compose(builders.literal, getName)
const generateEachItemNameKey = expression => simplePropertyNode(
BINDING_ITEM_NAME_KEY,
compose(nameToliteral, getEachItemName)(expression)
)
const generateEachIndexNameKey = expression => simplePropertyNode(
BINDING_INDEX_NAME_KEY,
compose(nameToliteral, getEachIndexName)(expression)
)
const generateEachEvaluateKey = (expression, eachExpression, sourceFile, sourceCode) => simplePropertyNode(
BINDING_EVALUATE_KEY,
compose(
e => toScopedFunction(e, sourceFile, sourceCode),
e => ({
...eachExpression,
text: generateJavascript(e).code
}),
getEachValue
)(expression)
)
/**
* Get the each expression properties to create properly the template binding
* @param { DomBinding.Expression } eachExpression - original each expression data
* @param { string } sourceFile - original tag file
* @param { string } sourceCode - original tag source code
* @returns { Array } AST nodes that are needed to build an each binding
*/
export function generateEachExpressionProperties(eachExpression, sourceFile, sourceCode) {
const ast = createASTFromExpression(eachExpression, sourceFile, sourceCode)
const body = ast.program.body
const firstNode = body[0]
if (!isExpressionStatement(firstNode)) {
panic(`The each directives supported should be of type "ExpressionStatement",you have provided a "${firstNode.type}"`)
}
const { expression } = firstNode
return [
generateEachItemNameKey(expression),
generateEachIndexNameKey(expression),
generateEachEvaluateKey(expression, eachExpression, sourceFile, sourceCode)
]
}
/**
* Transform a RiotParser.Node.Tag into an each binding
* @param { RiotParser.Node.Tag } sourceNode - tag containing the each attribute
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } an each binding node
*/
export default function createEachBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
const [ifAttribute, eachAttribute, keyAttribute] = [
findIfAttribute,
findEachAttribute,
findKeyAttribute
].map(f => f(sourceNode))
const attributeOrNull = attribute => attribute ? toScopedFunction(getAttributeExpression(attribute), sourceFile, sourceCode) : nullNode()
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(BINDING_TYPES),
builders.identifier(EACH_BINDING_TYPE),
false
)
),
simplePropertyNode(BINDING_GET_KEY_KEY, attributeOrNull(keyAttribute)),
simplePropertyNode(BINDING_CONDITION_KEY, attributeOrNull(ifAttribute)),
createTemplateProperty(createNestedBindings(sourceNode, sourceFile, sourceCode, selectorAttribute)),
...createSelectorProperties(selectorAttribute),
...compose(generateEachExpressionProperties, getAttributeExpression)(eachAttribute)
])
}

View File

@@ -0,0 +1,43 @@
import {
BINDING_EVALUATE_KEY,
BINDING_TYPES,
BINDING_TYPE_KEY,
IF_BINDING_TYPE
} from '../constants'
import {
createSelectorProperties,
createTemplateProperty,
toScopedFunction
} from '../utils'
import {builders} from '../../../utils/build-types'
import {createNestedBindings} from '../builder'
import {findIfAttribute} from '../find'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
/**
* Transform a RiotParser.Node.Tag into an if binding
* @param { RiotParser.Node.Tag } sourceNode - tag containing the if attribute
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { stiring } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } an if binding node
*/
export default function createIfBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
const ifAttribute = findIfAttribute(sourceNode)
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(BINDING_TYPES),
builders.identifier(IF_BINDING_TYPE),
false
)
),
simplePropertyNode(
BINDING_EVALUATE_KEY,
toScopedFunction(ifAttribute.expressions[0], sourceFile, sourceCode)
),
...createSelectorProperties(selectorAttribute),
createTemplateProperty(createNestedBindings(sourceNode, sourceFile, sourceCode, selectorAttribute))
])
}

View File

@@ -0,0 +1,53 @@
import {createAttributeExpressions, createExpression} from '../expressions/index'
import {
createSelectorProperties,
getChildrenNodes
} from '../utils'
import {hasExpressions, isRemovableNode, isRootNode, isTextNode} from '../checks'
import {BINDING_EXPRESSIONS_KEY} from '../constants'
import {builders} from '../../../utils/build-types'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
/**
* Create the text node expressions
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @returns {Array} array containing all the text node expressions
*/
function createTextNodeExpressions(sourceNode, sourceFile, sourceCode) {
const childrenNodes = getChildrenNodes(sourceNode)
return childrenNodes
.filter(isTextNode)
.filter(hasExpressions)
.map(node => createExpression(
node,
sourceFile,
sourceCode,
childrenNodes.indexOf(node),
sourceNode
))
}
/**
* Add a simple binding to a riot parser node
* @param { RiotParser.Node.Tag } sourceNode - tag containing the if attribute
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } an each binding node
*/
export default function createSimpleBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
return builders.objectExpression([
// root or removable nodes do not need selectors
...(isRemovableNode(sourceNode) || isRootNode(sourceNode) ? [] : createSelectorProperties(selectorAttribute)),
simplePropertyNode(
BINDING_EXPRESSIONS_KEY,
builders.arrayExpression([
...createTextNodeExpressions(sourceNode, sourceFile, sourceCode),
...createAttributeExpressions(sourceNode, sourceFile, sourceCode)
])
)
])
}

View File

@@ -0,0 +1,60 @@
import {
BINDING_ATTRIBUTES_KEY,
BINDING_NAME_KEY,
BINDING_TYPES,
BINDING_TYPE_KEY,
DEFAULT_SLOT_NAME,
NAME_ATTRIBUTE,
SLOT_BINDING_TYPE
} from '../constants'
import {
createBindingAttributes,
createSelectorProperties,
getName,
getNodeAttributes
} from '../utils'
import {builders} from '../../../utils/build-types'
import {findAttribute} from '../find'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
/**
* Transform a RiotParser.Node.Tag of type slot into a slot binding
* @param { RiotParser.Node.Tag } sourceNode - slot node
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } a slot binding node
*/
export default function createSlotBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
const slotNameAttribute = findAttribute(NAME_ATTRIBUTE, sourceNode)
const slotName = slotNameAttribute ? slotNameAttribute.value : DEFAULT_SLOT_NAME
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(BINDING_TYPES),
builders.identifier(SLOT_BINDING_TYPE),
false
)
),
simplePropertyNode(
BINDING_ATTRIBUTES_KEY,
createBindingAttributes(
{
...sourceNode,
// filter the name attribute
attributes: getNodeAttributes(sourceNode)
.filter(attribute => getName(attribute) !== NAME_ATTRIBUTE)
},
selectorAttribute,
sourceFile,
sourceCode
)
),
simplePropertyNode(
BINDING_NAME_KEY,
builders.literal(slotName)
),
...createSelectorProperties(selectorAttribute)
])
}

View File

@@ -0,0 +1,129 @@
import {
BINDING_ATTRIBUTES_KEY,
BINDING_BINDINGS_KEY,
BINDING_EVALUATE_KEY,
BINDING_GET_COMPONENT_KEY,
BINDING_HTML_KEY,
BINDING_ID_KEY,
BINDING_SLOTS_KEY,
BINDING_TYPES,
BINDING_TYPE_KEY,
GET_COMPONENT_FN,
SLOT_ATTRIBUTE,
TAG_BINDING_TYPE
} from '../constants'
import {
createBindingAttributes,
createCustomNodeNameEvaluationFunction,
createNestedRootNode,
createSelectorProperties,
getChildrenNodes,
getNodeAttributes
} from '../utils'
import build from '../builder'
import {builders} from '../../../utils/build-types'
import compose from 'cumpa'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
/**
* Find the slots in the current component and group them under the same id
* @param {RiotParser.Node.Tag} sourceNode - the custom tag
* @returns {Object} object containing all the slots grouped by name
*/
function groupSlots(sourceNode) {
return getChildrenNodes(sourceNode).reduce((acc, node) => {
const slotAttribute = findSlotAttribute(node)
if (slotAttribute) {
acc[slotAttribute.value] = node
} else {
acc.default = createNestedRootNode({
nodes: [...getChildrenNodes(acc.default), node]
})
}
return acc
}, {
default: null
})
}
/**
* Create the slot entity to pass to the riot-dom bindings
* @param {string} id - slot id
* @param {RiotParser.Node.Tag} sourceNode - slot root node
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @returns {AST.Node} ast node containing the slot object properties
*/
function buildSlot(id, sourceNode, sourceFile, sourceCode) {
const cloneNode = {
...sourceNode,
attributes: getNodeAttributes(sourceNode)
}
const [html, bindings] = build(cloneNode, sourceFile, sourceCode)
return builders.objectExpression([
simplePropertyNode(BINDING_ID_KEY, builders.literal(id)),
simplePropertyNode(BINDING_HTML_KEY, builders.literal(html)),
simplePropertyNode(BINDING_BINDINGS_KEY, builders.arrayExpression(bindings))
])
}
/**
* Create the AST array containing the slots
* @param { RiotParser.Node.Tag } sourceNode - the custom tag
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns {AST.ArrayExpression} array containing the attributes to bind
*/
export function createSlotsArray(sourceNode, sourceFile, sourceCode) {
return builders.arrayExpression([
...compose(
slots => slots.map(([key, value]) => buildSlot(key, value, sourceFile, sourceCode)),
slots => slots.filter(([, value]) => value),
Object.entries,
groupSlots
)(sourceNode)
])
}
/**
* Find the slot attribute if it exists
* @param {RiotParser.Node.Tag} sourceNode - the custom tag
* @returns {RiotParser.Node.Attr|undefined} the slot attribute found
*/
function findSlotAttribute(sourceNode) {
return getNodeAttributes(sourceNode).find(attribute => attribute.name === SLOT_ATTRIBUTE)
}
/**
* Transform a RiotParser.Node.Tag into a tag binding
* @param { RiotParser.Node.Tag } sourceNode - the custom tag
* @param { string } selectorAttribute - attribute needed to select the target node
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns { AST.Node } tag binding node
*/
export default function createTagBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(BINDING_TYPES),
builders.identifier(TAG_BINDING_TYPE),
false
)
),
simplePropertyNode(BINDING_GET_COMPONENT_KEY, builders.identifier(GET_COMPONENT_FN)),
simplePropertyNode(
BINDING_EVALUATE_KEY,
createCustomNodeNameEvaluationFunction(sourceNode, sourceFile, sourceCode)
),
simplePropertyNode(BINDING_SLOTS_KEY, createSlotsArray(sourceNode, sourceFile, sourceCode)),
simplePropertyNode(
BINDING_ATTRIBUTES_KEY,
createBindingAttributes(sourceNode, selectorAttribute, sourceFile, sourceCode)
),
...createSelectorProperties(selectorAttribute)
])
}

View File

@@ -0,0 +1,180 @@
import {
cloneNodeWithoutSelectorAttribute,
closeTag, createBindingSelector,
createNestedRootNode,
getChildrenNodes,
getNodeAttributes,
nodeToString
} from './utils'
import {
hasEachAttribute, hasIfAttribute,
hasItsOwnTemplate,
isCustomNode, isRemovableNode,
isRootNode,
isSlotNode,
isStaticNode,
isTagNode,
isTextNode,
isVoidNode
} from './checks'
import cloneDeep from '../../utils/clone-deep'
import eachBinding from './bindings/each'
import ifBinding from './bindings/if'
import {panic} from '@riotjs/util/misc'
import simpleBinding from './bindings/simple'
import slotBinding from './bindings/slot'
import tagBinding from './bindings/tag'
const BuildingState = Object.freeze({
html: [],
bindings: [],
parent: null
})
/**
* Nodes having bindings should be cloned and new selector properties should be added to them
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} bindingsSelector - temporary string to identify the current node
* @returns {RiotParser.Node} the original node parsed having the new binding selector attribute
*/
function createBindingsTag(sourceNode, bindingsSelector) {
if (!bindingsSelector) return sourceNode
return {
...sourceNode,
// inject the selector bindings into the node attributes
attributes: [{
name: bindingsSelector,
value: bindingsSelector
}, ...getNodeAttributes(sourceNode)]
}
}
/**
* Create a generic dynamic node (text or tag) and generate its bindings
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @param {BuildingState} state - state representing the current building tree state during the recursion
* @returns {Array} array containing the html output and bindings for the current node
*/
function createDynamicNode(sourceNode, sourceFile, sourceCode, state) {
switch (true) {
case isTextNode(sourceNode):
// text nodes will not have any bindings
return [nodeToString(sourceNode), []]
default:
return createTagWithBindings(sourceNode, sourceFile, sourceCode, state)
}
}
/**
* Create only a dynamic tag node with generating a custom selector and its bindings
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @param {BuildingState} state - state representing the current building tree state during the recursion
* @returns {Array} array containing the html output and bindings for the current node
*/
function createTagWithBindings(sourceNode, sourceFile, sourceCode) {
const bindingsSelector = isRootNode(sourceNode) ? null : createBindingSelector()
const cloneNode = createBindingsTag(sourceNode, bindingsSelector)
const tagOpeningHTML = nodeToString(cloneNode)
switch (true) {
case hasEachAttribute(cloneNode):
// EACH bindings have prio 1
return [tagOpeningHTML, [eachBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
case hasIfAttribute(cloneNode):
// IF bindings have prio 2
return [tagOpeningHTML, [ifBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
case isCustomNode(cloneNode):
// TAG bindings have prio 3
return [tagOpeningHTML, [tagBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
case isSlotNode(cloneNode):
// slot tag
return [tagOpeningHTML, [slotBinding(cloneNode, bindingsSelector)]]
default:
// this node has expressions bound to it
return [tagOpeningHTML, [simpleBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
}
}
/**
* Parse a node trying to extract its template and bindings
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @param {BuildingState} state - state representing the current building tree state during the recursion
* @returns {Array} array containing the html output and bindings for the current node
*/
function parseNode(sourceNode, sourceFile, sourceCode, state) {
// static nodes have no bindings
if (isStaticNode(sourceNode)) return [nodeToString(sourceNode), []]
return createDynamicNode(sourceNode, sourceFile, sourceCode, state)
}
/**
* Create the tag binding
* @param { RiotParser.Node.Tag } sourceNode - tag containing the each attribute
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @param { string } selector - binding selector
* @returns { Array } array with only the tag binding AST
*/
export function createNestedBindings(sourceNode, sourceFile, sourceCode, selector) {
const mightBeARiotComponent = isCustomNode(sourceNode)
const node = cloneNodeWithoutSelectorAttribute(sourceNode, selector)
return mightBeARiotComponent ? [null, [
tagBinding(
node,
null,
sourceFile,
sourceCode
)]
] : build(createNestedRootNode(node), sourceFile, sourceCode)
}
/**
* Build the template and the bindings
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @param {BuildingState} state - state representing the current building tree state during the recursion
* @returns {Array} array containing the html output and the dom bindings
*/
export default function build(
sourceNode,
sourceFile,
sourceCode,
state
) {
if (!sourceNode) panic('Something went wrong with your tag DOM parsing, your tag template can\'t be created')
const [nodeHTML, nodeBindings] = parseNode(sourceNode, sourceFile, sourceCode, state)
const childrenNodes = getChildrenNodes(sourceNode)
const canRenderNodeHTML = isRemovableNode(sourceNode) === false
const currentState = { ...cloneDeep(BuildingState), ...state }
// mutate the original arrays
canRenderNodeHTML && currentState.html.push(...nodeHTML)
currentState.bindings.push(...nodeBindings)
// do recursion if
// this tag has children and it has no special directives bound to it
if (childrenNodes.length && !hasItsOwnTemplate(sourceNode)) {
childrenNodes.forEach(node => build(node, sourceFile, sourceCode, { parent: sourceNode, ...currentState }))
}
// close the tag if it's not a void one
if (canRenderNodeHTML && isTagNode(sourceNode) && !isVoidNode(sourceNode)) {
currentState.html.push(closeTag(sourceNode))
}
return [
currentState.html.join(''),
currentState.bindings
]
}

View File

@@ -0,0 +1,231 @@
import {
IS_CUSTOM_NODE,
IS_SPREAD_ATTRIBUTE,
IS_VOID_NODE,
PROGRESS_TAG_NODE_NAME, SLOT_ATTRIBUTE,
SLOT_TAG_NODE_NAME,
TEMPLATE_TAG_NODE_NAME
} from './constants'
import {findAttribute, findEachAttribute, findIfAttribute, findIsAttribute, findKeyAttribute} from './find'
import {
getName,
getNodeAttributes
} from './utils'
import {isBrowserAPI, isBuiltinAPI, isNewExpression, isRaw} from '../../utils/ast-nodes-checks'
import compose from 'cumpa'
import {isNil} from '@riotjs/util/checks'
import {nodeTypes} from '@riotjs/parser'
import {types} from '../../utils/build-types'
/**
* True if the node has not expression set nor bindings directives
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only if it's a static node that doesn't need bindings or expressions
*/
export function isStaticNode(node) {
return [
hasExpressions,
findEachAttribute,
findIfAttribute,
isCustomNode,
isSlotNode
].every(test => !test(node))
}
/**
* Check if a node should be rendered in the final component HTML
* For example slot <template slot="content"> tags not using `each` or `if` directives can be removed
* see also https://github.com/riot/riot/issues/2888
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true if we can remove this tag from the component rendered HTML
*/
export function isRemovableNode(node) {
return isTemplateNode(node) && !isNil(findAttribute(SLOT_ATTRIBUTE, node)) && !hasEachAttribute(node) && !hasIfAttribute(node)
}
/**
* Check if a node name is part of the browser or builtin javascript api or it belongs to the current scope
* @param { types.NodePath } path - containing the current node visited
* @returns {boolean} true if it's a global api variable
*/
export function isGlobal({ scope, node }) {
// recursively find the identifier of this AST path
if (node.object) {
return isGlobal({ node: node.object, scope })
}
return Boolean(
isRaw(node) ||
isBuiltinAPI(node) ||
isBrowserAPI(node) ||
isNewExpression(node) ||
isNodeInScope(scope, node)
)
}
/**
* Checks if the identifier of a given node exists in a scope
* @param {Scope} scope - scope where to search for the identifier
* @param {types.Node} node - node to search for the identifier
* @returns {boolean} true if the node identifier is defined in the given scope
*/
function isNodeInScope(scope, node) {
const traverse = (isInScope = false) => {
types.visit(node, {
visitIdentifier(path) {
if (scope.lookup(getName(path.node))) {
isInScope = true
}
this.abort()
}
})
return isInScope
}
return traverse()
}
/**
* True if the node has the isCustom attribute set
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true if either it's a riot component or a custom element
*/
export function isCustomNode(node) {
return !!(node[IS_CUSTOM_NODE] || hasIsAttribute(node))
}
/**
* True the node is <slot>
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true if it's a slot node
*/
export function isSlotNode(node) {
return node.name === SLOT_TAG_NODE_NAME
}
/**
* True if the node has the isVoid attribute set
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true if the node is self closing
*/
export function isVoidNode(node) {
return !!node[IS_VOID_NODE]
}
/**
* True if the riot parser did find a tag node
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for the tag nodes
*/
export function isTagNode(node) {
return node.type === nodeTypes.TAG
}
/**
* True if the riot parser did find a text node
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for the text nodes
*/
export function isTextNode(node) {
return node.type === nodeTypes.TEXT
}
/**
* True if the node parsed is the root one
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for the root nodes
*/
export function isRootNode(node) {
return node.isRoot
}
/**
* True if the attribute parsed is of type spread one
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true if the attribute node is of type spread
*/
export function isSpreadAttribute(node) {
return node[IS_SPREAD_ATTRIBUTE]
}
/**
* True if the node is an attribute and its name is "value"
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for value attribute nodes
*/
export function isValueAttribute(node) {
return node.name === 'value'
}
/**
* True if the DOM node is a progress tag
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true for the progress tags
*/
export function isProgressNode(node) {
return node.name === PROGRESS_TAG_NODE_NAME
}
/**
* True if the DOM node is a <template> tag
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true for the progress tags
*/
export function isTemplateNode(node) {
return node.name === TEMPLATE_TAG_NODE_NAME
}
/**
* True if the node is an attribute and a DOM handler
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for dom listener attribute nodes
*/
export const isEventAttribute = (() => {
const EVENT_ATTR_RE = /^on/
return node => EVENT_ATTR_RE.test(node.name)
})()
/**
* Check if a string is an html comment
* @param {string} string - test string
* @returns {boolean} true if html comment
*/
export function isCommentString(string) {
return string.trim().indexOf('<!') === 0
}
/**
* True if the node has expressions or expression attributes
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} ditto
*/
export function hasExpressions(node) {
return !!(
node.expressions ||
// has expression attributes
(getNodeAttributes(node).some(attribute => hasExpressions(attribute))) ||
// has child text nodes with expressions
(node.nodes && node.nodes.some(node => isTextNode(node) && hasExpressions(node)))
)
}
/**
* True if the node is a directive having its own template
* @param {RiotParser.Node} node - riot parser node
* @returns {boolean} true only for the IF EACH and TAG bindings
*/
export function hasItsOwnTemplate(node) {
return [
findEachAttribute,
findIfAttribute,
isCustomNode
].some(test => test(node))
}
export const hasIfAttribute = compose(Boolean, findIfAttribute)
export const hasEachAttribute = compose(Boolean, findEachAttribute)
export const hasIsAttribute = compose(Boolean, findIsAttribute)
export const hasKeyAttribute = compose(Boolean, findKeyAttribute)

View File

@@ -0,0 +1,64 @@
import {constants} from '@riotjs/parser'
export const BINDING_TYPES = 'bindingTypes'
export const EACH_BINDING_TYPE = 'EACH'
export const IF_BINDING_TYPE = 'IF'
export const TAG_BINDING_TYPE = 'TAG'
export const SLOT_BINDING_TYPE = 'SLOT'
export const EXPRESSION_TYPES = 'expressionTypes'
export const ATTRIBUTE_EXPRESSION_TYPE = 'ATTRIBUTE'
export const VALUE_EXPRESSION_TYPE = 'VALUE'
export const TEXT_EXPRESSION_TYPE = 'TEXT'
export const EVENT_EXPRESSION_TYPE = 'EVENT'
export const TEMPLATE_FN = 'template'
export const SCOPE = '_scope'
export const GET_COMPONENT_FN = 'getComponent'
// keys needed to create the DOM bindings
export const BINDING_SELECTOR_KEY = 'selector'
export const BINDING_GET_COMPONENT_KEY = 'getComponent'
export const BINDING_TEMPLATE_KEY = 'template'
export const BINDING_TYPE_KEY = 'type'
export const BINDING_REDUNDANT_ATTRIBUTE_KEY = 'redundantAttribute'
export const BINDING_CONDITION_KEY = 'condition'
export const BINDING_ITEM_NAME_KEY = 'itemName'
export const BINDING_GET_KEY_KEY = 'getKey'
export const BINDING_INDEX_NAME_KEY = 'indexName'
export const BINDING_EVALUATE_KEY = 'evaluate'
export const BINDING_NAME_KEY = 'name'
export const BINDING_SLOTS_KEY = 'slots'
export const BINDING_EXPRESSIONS_KEY = 'expressions'
export const BINDING_CHILD_NODE_INDEX_KEY = 'childNodeIndex'
// slots keys
export const BINDING_BINDINGS_KEY = 'bindings'
export const BINDING_ID_KEY = 'id'
export const BINDING_HTML_KEY = 'html'
export const BINDING_ATTRIBUTES_KEY = 'attributes'
// DOM directives
export const IF_DIRECTIVE = 'if'
export const EACH_DIRECTIVE = 'each'
export const KEY_ATTRIBUTE = 'key'
export const SLOT_ATTRIBUTE = 'slot'
export const NAME_ATTRIBUTE = 'name'
export const IS_DIRECTIVE = 'is'
// Misc
export const DEFAULT_SLOT_NAME = 'default'
export const TEXT_NODE_EXPRESSION_PLACEHOLDER = ' '
export const BINDING_SELECTOR_PREFIX = 'expr'
export const SLOT_TAG_NODE_NAME = 'slot'
export const PROGRESS_TAG_NODE_NAME = 'progress'
export const TEMPLATE_TAG_NODE_NAME = 'template'
// Riot Parser constants
export const IS_RAW_NODE = constants.IS_RAW
export const IS_VOID_NODE = constants.IS_VOID
export const IS_CUSTOM_NODE = constants.IS_CUSTOM
export const IS_BOOLEAN_ATTRIBUTE = constants.IS_BOOLEAN
export const IS_SPREAD_ATTRIBUTE = constants.IS_SPREAD

View File

@@ -0,0 +1,36 @@
import {
ATTRIBUTE_EXPRESSION_TYPE,
BINDING_EVALUATE_KEY,
BINDING_NAME_KEY,
BINDING_TYPE_KEY,
EXPRESSION_TYPES
} from '../constants'
import {nullNode, simplePropertyNode} from '../../../utils/custom-ast-nodes'
import {builders} from '../../../utils/build-types'
import {createAttributeEvaluationFunction} from '../utils'
import {isSpreadAttribute} from '../checks'
/**
* Create a simple attribute expression
* @param {RiotParser.Node.Attr} sourceNode - the custom tag
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @returns {AST.Node} object containing the expression binding keys
*/
export default function createAttributeExpression(sourceNode, sourceFile, sourceCode) {
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(EXPRESSION_TYPES),
builders.identifier(ATTRIBUTE_EXPRESSION_TYPE),
false
)
),
simplePropertyNode(BINDING_NAME_KEY, isSpreadAttribute(sourceNode) ? nullNode() : builders.literal(sourceNode.name)),
simplePropertyNode(
BINDING_EVALUATE_KEY,
createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
)
])
}

View File

@@ -0,0 +1,34 @@
import {
BINDING_EVALUATE_KEY,
BINDING_NAME_KEY,
BINDING_TYPE_KEY,
EVENT_EXPRESSION_TYPE,
EXPRESSION_TYPES
} from '../constants'
import {builders} from '../../../utils/build-types'
import {createAttributeEvaluationFunction} from '../utils'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
/**
* Create a simple event expression
* @param {RiotParser.Node.Attr} sourceNode - attribute containing the event handlers
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @returns {AST.Node} object containing the expression binding keys
*/
export default function createEventExpression(sourceNode, sourceFile, sourceCode) {
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(EXPRESSION_TYPES),
builders.identifier(EVENT_EXPRESSION_TYPE),
false
)
),
simplePropertyNode(BINDING_NAME_KEY, builders.literal(sourceNode.name)),
simplePropertyNode(
BINDING_EVALUATE_KEY,
createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
)
])
}

View File

@@ -0,0 +1,34 @@
import {isEventAttribute, isProgressNode, isTextNode, isValueAttribute} from '../checks'
import attributeExpression from './attribute'
import eventExpression from './event'
import {findDynamicAttributes} from '../find'
import {hasValueAttribute} from 'dom-nodes'
import textExpression from './text'
import valueExpression from './value'
export function createExpression(sourceNode, sourceFile, sourceCode, childNodeIndex, parentNode) {
switch (true) {
case isTextNode(sourceNode):
return textExpression(sourceNode, sourceFile, sourceCode, childNodeIndex)
// progress nodes value attributes will be rendered as attributes
// see https://github.com/riot/compiler/issues/122
case isValueAttribute(sourceNode) && hasValueAttribute(parentNode.name) && !isProgressNode(parentNode):
return valueExpression(sourceNode, sourceFile, sourceCode)
case isEventAttribute(sourceNode):
return eventExpression(sourceNode, sourceFile, sourceCode)
default:
return attributeExpression(sourceNode, sourceFile, sourceCode)
}
}
/**
* Create the attribute expressions
* @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @returns {Array} array containing all the attribute expressions
*/
export function createAttributeExpressions(sourceNode, sourceFile, sourceCode) {
return findDynamicAttributes(sourceNode)
.map(attribute => createExpression(attribute, sourceFile, sourceCode, 0, sourceNode))
}

View File

@@ -0,0 +1,105 @@
import {
BINDING_CHILD_NODE_INDEX_KEY,
BINDING_EVALUATE_KEY,
BINDING_TYPE_KEY,
EXPRESSION_TYPES,
TEXT_EXPRESSION_TYPE
} from '../constants'
import {createArrayString, transformExpression, wrapASTInFunctionWithScope} from '../utils'
import {nullNode,simplePropertyNode} from '../../../utils/custom-ast-nodes'
import {builders} from '../../../utils/build-types'
import encodeHTMLEntities from '../../../utils/html-entities/encode'
import {isCommentString} from '../checks'
import {isLiteral} from '../../../utils/ast-nodes-checks'
import trimEnd from '../../../utils/trim-end'
import trimStart from '../../../utils/trim-start'
import unescapeChar from '../../../utils/unescape-char'
/**
* Generate the pure immutable string chunks from a RiotParser.Node.Text
* @param {RiotParser.Node.Text} node - riot parser text node
* @param {string} sourceCode sourceCode - source code
* @returns {Array} array containing the immutable string chunks
*/
function generateLiteralStringChunksFromNode(node, sourceCode) {
return node.expressions.reduce((chunks, expression, index) => {
const start = index ? node.expressions[index - 1].end : node.start
const string = encodeHTMLEntities(
sourceCode.substring(start, expression.start)
)
// trimStart the first string
chunks.push(index === 0 ? trimStart(string) : string)
// add the tail to the string
if (index === node.expressions.length - 1)
chunks.push(
encodeHTMLEntities(
trimEnd(sourceCode.substring(expression.end, node.end))
)
)
return chunks
}, [])
// comments are not supported here
.filter(str => !isCommentString(str))
.map(str => node.unescape ? unescapeChar(str, node.unescape) : str)
}
/**
* Simple bindings might contain multiple expressions like for example: "{foo} and {bar}"
* This helper aims to merge them in a template literal if it's necessary
* @param {RiotParser.Node} node - riot parser node
* @param {string} sourceFile - original tag file
* @param {string} sourceCode - original tag source code
* @returns { Object } a template literal expression object
*/
export function mergeNodeExpressions(node, sourceFile, sourceCode) {
if (node.parts.length === 1)
return transformExpression(node.expressions[0], sourceFile, sourceCode)
const pureStringChunks = generateLiteralStringChunksFromNode(node, sourceCode)
const stringsArray = pureStringChunks.reduce((acc, str, index) => {
const expr = node.expressions[index]
return [
...acc,
builders.literal(str),
expr ? transformExpression(expr, sourceFile, sourceCode) : nullNode()
]
}, [])
// filter the empty literal expressions
.filter(expr => !isLiteral(expr) || expr.value)
return createArrayString(stringsArray)
}
/**
* Create a text expression
* @param {RiotParser.Node.Text} sourceNode - text node to parse
* @param {string} sourceFile - source file path
* @param {string} sourceCode - original source
* @param {number} childNodeIndex - position of the child text node in its parent children nodes
* @returns {AST.Node} object containing the expression binding keys
*/
export default function createTextExpression(sourceNode, sourceFile, sourceCode, childNodeIndex) {
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(EXPRESSION_TYPES),
builders.identifier(TEXT_EXPRESSION_TYPE),
false
)
),
simplePropertyNode(
BINDING_CHILD_NODE_INDEX_KEY,
builders.literal(childNodeIndex)
),
simplePropertyNode(
BINDING_EVALUATE_KEY,
wrapASTInFunctionWithScope(
mergeNodeExpressions(sourceNode, sourceFile, sourceCode)
)
)
])
}

View File

@@ -0,0 +1,25 @@
import {
BINDING_EVALUATE_KEY,
BINDING_TYPE_KEY,
EXPRESSION_TYPES,
VALUE_EXPRESSION_TYPE
} from '../constants'
import {builders} from '../../../utils/build-types'
import {createAttributeEvaluationFunction} from '../utils'
import {simplePropertyNode} from '../../../utils/custom-ast-nodes'
export default function createValueExpression(sourceNode, sourceFile, sourceCode) {
return builders.objectExpression([
simplePropertyNode(BINDING_TYPE_KEY,
builders.memberExpression(
builders.identifier(EXPRESSION_TYPES),
builders.identifier(VALUE_EXPRESSION_TYPE),
false
)
),
simplePropertyNode(
BINDING_EVALUATE_KEY,
createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
)
])
}

View File

@@ -0,0 +1,47 @@
import {EACH_DIRECTIVE, IF_DIRECTIVE, IS_DIRECTIVE, KEY_ATTRIBUTE} from './constants'
import {getName, getNodeAttributes} from './utils'
import {hasExpressions} from './checks'
/**
* Find the attribute node
* @param { string } name - name of the attribute we want to find
* @param { riotParser.nodeTypes.TAG } node - a tag node
* @returns { riotParser.nodeTypes.ATTR } attribute node
*/
export function findAttribute(name, node) {
return node.attributes && node.attributes.find(attr => getName(attr) === name)
}
export function findIfAttribute(node) {
return findAttribute(IF_DIRECTIVE, node)
}
export function findEachAttribute(node) {
return findAttribute(EACH_DIRECTIVE, node)
}
export function findKeyAttribute(node) {
return findAttribute(KEY_ATTRIBUTE, node)
}
export function findIsAttribute(node) {
return findAttribute(IS_DIRECTIVE, node)
}
/**
* Find all the node attributes that are not expressions
* @param {RiotParser.Node} node - riot parser node
* @returns {Array} list of all the static attributes
*/
export function findStaticAttributes(node) {
return getNodeAttributes(node).filter(attribute => !hasExpressions(attribute))
}
/**
* Find all the node attributes that have expressions
* @param {RiotParser.Node} node - riot parser node
* @returns {Array} list of all the dynamic attributes
*/
export function findDynamicAttributes(node) {
return getNodeAttributes(node).filter(hasExpressions)
}

View File

@@ -0,0 +1,61 @@
import {callTemplateFunction, createRootNode, createTemplateDependenciesInjectionWrapper} from './utils'
import {TAG_TEMPLATE_PROPERTY} from '../../constants'
import build from './builder'
import {types} from '../../utils/build-types'
/**
* Create the content of the template function
* @param { RiotParser.Node } sourceNode - node generated by the riot compiler
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @returns {AST.BlockStatement} the content of the template function
*/
function createTemplateFunctionContent(sourceNode, sourceFile, sourceCode) {
return callTemplateFunction(
...build(
createRootNode(sourceNode),
sourceFile,
sourceCode
)
)
}
/**
* Extend the AST adding the new template property containing our template call to render the component
* @param { Object } ast - current output ast
* @param { string } sourceFile - source file path
* @param { string } sourceCode - original source
* @param { RiotParser.Node } sourceNode - node generated by the riot compiler
* @returns { Object } the output ast having the "template" key
*/
function extendTemplateProperty(ast, sourceFile, sourceCode, sourceNode) {
types.visit(ast, {
visitProperty(path) {
if (path.value.key.name === TAG_TEMPLATE_PROPERTY) {
path.value.value = createTemplateDependenciesInjectionWrapper(
createTemplateFunctionContent(sourceNode, sourceFile, sourceCode)
)
return false
}
this.traverse(path)
}
})
return ast
}
/**
* Generate the component template logic
* @param { RiotParser.Node } sourceNode - node generated by the riot compiler
* @param { string } source - original component source code
* @param { Object } meta - compilation meta information
* @param { AST } ast - current AST output
* @returns { AST } the AST generated
*/
export default function template(sourceNode, source, meta, ast) {
const { options } = meta
return extendTemplateProperty(ast, options.file, source, sourceNode)
}

File diff suppressed because it is too large Load Diff

205
node_modules/@riotjs/compiler/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,205 @@
import {TAG_CSS_PROPERTY, TAG_LOGIC_PROPERTY, TAG_NAME_PROPERTY, TAG_TEMPLATE_PROPERTY} from './constants'
import {callTemplateFunction, createTemplateDependenciesInjectionWrapper} from './generators/template/utils'
import {nullNode, simplePropertyNode} from './utils/custom-ast-nodes'
import {register as registerPostproc, execute as runPostprocessors} from './postprocessors'
import {register as registerPreproc, execute as runPreprocessor} from './preprocessors'
import build from './generators/template/builder'
import {builders} from './utils/build-types'
import compose from 'cumpa'
import {createSlotsArray} from './generators/template/bindings/tag'
import cssGenerator from './generators/css'
import curry from 'curri'
import generateJavascript from './utils/generate-javascript'
import hasHTMLOutsideRootNode from './utils/has-html-outside-root-node'
import isEmptyArray from './utils/is-empty-array'
import isEmptySourcemap from './utils/is-empty-sourcemap'
import javascriptGenerator from './generators/javascript'
import riotParser from '@riotjs/parser'
import sourcemapAsJSON from './utils/sourcemap-as-json'
import templateGenerator from './generators/template'
const DEFAULT_OPTIONS = {
template: 'default',
file: '[unknown-source-file]',
scopedCss: true
}
/**
* Create the initial AST
* @param {string} tagName - the name of the component we have compiled
* @returns { AST } the initial AST
*
* @example
* // the output represents the following string in AST
*/
export function createInitialInput({ tagName }) {
/*
generates
export default {
${TAG_CSS_PROPERTY}: null,
${TAG_LOGIC_PROPERTY}: null,
${TAG_TEMPLATE_PROPERTY}: null
}
*/
return builders.program([
builders.exportDefaultDeclaration(
builders.objectExpression([
simplePropertyNode(TAG_CSS_PROPERTY, nullNode()),
simplePropertyNode(TAG_LOGIC_PROPERTY, nullNode()),
simplePropertyNode(TAG_TEMPLATE_PROPERTY, nullNode()),
simplePropertyNode(TAG_NAME_PROPERTY, builders.literal(tagName))
])
)]
)
}
/**
* Make sure the input sourcemap is valid otherwise we ignore it
* @param {SourceMapGenerator} map - preprocessor source map
* @returns {Object} sourcemap as json or nothing
*/
function normaliseInputSourceMap(map) {
const inputSourceMap = sourcemapAsJSON(map)
return isEmptySourcemap(inputSourceMap) ? null : inputSourceMap
}
/**
* Override the sourcemap content making sure it will always contain the tag source code
* @param {Object} map - sourcemap as json
* @param {string} source - component source code
* @returns {Object} original source map with the "sourcesContent" property overridden
*/
function overrideSourcemapContent(map, source) {
return {
...map,
sourcesContent: [source]
}
}
/**
* Create the compilation meta object
* @param { string } source - source code of the tag we will need to compile
* @param { string } options - compiling options
* @returns {Object} meta object
*/
function createMeta(source, options) {
return {
tagName: null,
fragments: null,
options: {
...DEFAULT_OPTIONS,
...options
},
source
}
}
/**
* Parse a string to simply get its template AST
* @param { string } source - string to parse
* @param { Object } options - parser options
* @returns {Object} riot parser template output
*/
const parseSimpleString = (source, options) => {
const { parse } = riotParser(options)
return parse(source).output.template
}
/**
* Generate the component slots creation function from the root node
* @param { string } source - component outer html
* @param { Object } parserOptions - riot parser options
* @returns { string } content of the function that can be used to crate the slots in runtime
*/
export function generateSlotsFromString(source, parserOptions) {
return compose(
({ code }) => code,
generateJavascript,
createTemplateDependenciesInjectionWrapper,
createSlotsArray
)(parseSimpleString(source, parserOptions), DEFAULT_OPTIONS.file, source)
}
/**
* Generate the Riot.js binding template function from a template string
* @param { string } source - template string
* @param { Object } parserOptions - riot parser options
* @returns { string } Riot.js bindings template function generated
*/
export function generateTemplateFunctionFromString(source, parserOptions) {
return compose(
({ code }) => code,
generateJavascript,
callTemplateFunction
)(
...build(
parseSimpleString(source, parserOptions),
DEFAULT_OPTIONS.file,
source
)
)
}
/**
* Generate the output code source together with the sourcemap
* @param { string } source - source code of the tag we will need to compile
* @param { Object } opts - compiling options
* @returns { Output } object containing output code and source map
*/
export function compile(source, opts = {}) {
const meta = createMeta(source, opts)
const { options } = meta
const { code, map } = runPreprocessor('template', options.template, meta, source)
const { parse } = riotParser(options)
const { template, css, javascript } = parse(code).output
// see also https://github.com/riot/compiler/issues/130
if (hasHTMLOutsideRootNode(template || css || javascript, code, parse)) {
throw new Error('Multiple HTML root nodes are not supported')
}
// extend the meta object with the result of the parsing
Object.assign(meta, {
tagName: template.name,
fragments: { template, css, javascript }
})
return compose(
result => ({ ...result, meta }),
result => runPostprocessors(result, meta),
result => ({
...result,
map: overrideSourcemapContent(result.map, source)
}),
ast => meta.ast = ast && generateJavascript(ast, {
sourceMapName: `${options.file}.map`,
inputSourceMap: normaliseInputSourceMap(map)
}),
hookGenerator(templateGenerator, template, code, meta),
hookGenerator(javascriptGenerator, javascript, code, meta),
hookGenerator(cssGenerator, css, code, meta)
)(createInitialInput(meta))
}
/**
* Prepare the riot parser node transformers
* @param { Function } transformer - transformer function
* @param { Object } sourceNode - riot parser node
* @param { string } source - component source code
* @param { Object } meta - compilation meta information
* @returns { function(): Promise<Output> } Function what resolves to object containing output code and source map
*/
function hookGenerator(transformer, sourceNode, source, meta) {
const hasContent = sourceNode && (sourceNode.text || !isEmptyArray(sourceNode.nodes) || !isEmptyArray(sourceNode.attributes))
return hasContent ? curry(transformer)(sourceNode, source, meta) : result => result
}
// This function can be used to register new preprocessors
// a preprocessor can target either only the css or javascript nodes
// or the complete tag source file ('template')
export const registerPreprocessor = registerPreproc
// This function can allow you to register postprocessors that will parse the output code
// here we can run prettifiers, eslint fixes...
export const registerPostprocessor = registerPostproc

53
node_modules/@riotjs/compiler/src/postprocessors.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
import composeSourcemaps from './utils/compose-sourcemaps'
import { createOutput } from './transformer'
import {panic} from '@riotjs/util/misc'
export const postprocessors = new Set()
/**
* Register a postprocessor that will be used after the parsing and compilation of the riot tags
* @param { Function } postprocessor - transformer that will receive the output code ans sourcemap
* @returns { Set } the postprocessors collection
*/
export function register(postprocessor) {
if (postprocessors.has(postprocessor)) {
panic(`This postprocessor "${postprocessor.name || postprocessor.toString()}" was already registered`)
}
postprocessors.add(postprocessor)
return postprocessors
}
/**
* Unregister a postprocessor
* @param { Function } postprocessor - possibly a postprocessor previously registered
* @returns { Set } the postprocessors collection
*/
export function unregister(postprocessor) {
if (!postprocessors.has(postprocessor)) {
panic(`This postprocessor "${postprocessor.name || postprocessor.toString()}" was never registered`)
}
postprocessors.delete(postprocessor)
return postprocessors
}
/**
* Exec all the postprocessors in sequence combining the sourcemaps generated
* @param { Output } compilerOutput - output generated by the compiler
* @param { Object } meta - compiling meta information
* @returns { Output } object containing output code and source map
*/
export function execute(compilerOutput, meta) {
return Array.from(postprocessors).reduce(function(acc, postprocessor) {
const { code, map } = acc
const output = postprocessor(code, meta)
return {
code: output.code,
map: composeSourcemaps(map, output.map)
}
}, createOutput(compilerOutput, meta))
}

72
node_modules/@riotjs/compiler/src/preprocessors.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import {panic} from '@riotjs/util/misc'
import { transform } from './transformer'
/**
* Parsers that can be registered by users to preparse components fragments
* @type { Object }
*/
export const preprocessors = Object.freeze({
javascript: new Map(),
css: new Map(),
template: new Map().set('default', code => ({ code }))
})
// throw a processor type error
function preprocessorTypeError(type) {
panic(`No preprocessor of type "${type}" was found, please make sure to use one of these: 'javascript', 'css' or 'template'`)
}
// throw an error if the preprocessor was not registered
function preprocessorNameNotFoundError(name) {
panic(`No preprocessor named "${name}" was found, are you sure you have registered it?'`)
}
/**
* Register a custom preprocessor
* @param { string } type - preprocessor type either 'js', 'css' or 'template'
* @param { string } name - unique preprocessor id
* @param { Function } preprocessor - preprocessor function
* @returns { Map } - the preprocessors map
*/
export function register(type, name, preprocessor) {
if (!type) panic('Please define the type of preprocessor you want to register \'javascript\', \'css\' or \'template\'')
if (!name) panic('Please define a name for your preprocessor')
if (!preprocessor) panic('Please provide a preprocessor function')
if (!preprocessors[type]) preprocessorTypeError(type)
if (preprocessors[type].has(name)) panic(`The preprocessor ${name} was already registered before`)
preprocessors[type].set(name, preprocessor)
return preprocessors
}
/**
* Register a custom preprocessor
* @param { string } type - preprocessor type either 'js', 'css' or 'template'
* @param { string } name - unique preprocessor id
* @returns { Map } - the preprocessors map
*/
export function unregister(type, name) {
if (!type) panic('Please define the type of preprocessor you want to unregister \'javascript\', \'css\' or \'template\'')
if (!name) panic('Please define the name of the preprocessor you want to unregister')
if (!preprocessors[type]) preprocessorTypeError(type)
if (!preprocessors[type].has(name)) preprocessorNameNotFoundError(name)
preprocessors[type].delete(name)
return preprocessors
}
/**
* Exec the compilation of a preprocessor
* @param { string } type - preprocessor type either 'js', 'css' or 'template'
* @param { string } name - unique preprocessor id
* @param { Object } meta - preprocessor meta information
* @param { string } source - source code
* @returns { Output } object containing a sourcemap and a code string
*/
export function execute(type, name, meta, source) {
if (!preprocessors[type]) preprocessorTypeError(type)
if (!preprocessors[type].has(name)) preprocessorNameNotFoundError(name)
return transform(preprocessors[type].get(name), meta, source)
}

Some files were not shown because too many files have changed in this diff Show More