mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2024-11-14 07:29:17 +08:00
perf: replace h with createVNode for PatchFlags (#6)
* chore: disable `no-bitwise` in eslint * feat: replace h with createVNode for PatchFlags * fix: vModel modifiers shift * chore: rename v-_model to _model in sugar-v-model * fix: hasRef will not always be false * feat: Check if an attribute value is constant * chore: pass null when children is empty * chore: describe Transform JSX in test * chore: add describe Patch Flags * perf: import compatibleProps when opts.compatibleProps is true * test: add coverage report (#7) * refactor: cjs to esModule Co-authored-by: Haoqun Jiang <haoqunjiang@gmail.com>
This commit is contained in:
parent
88bf7cca93
commit
4c34cf1d5d
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
dist
|
@ -19,5 +19,7 @@ module.exports = {
|
|||||||
'no-use-before-define': [0],
|
'no-use-before-define': [0],
|
||||||
'no-plusplus': [0],
|
'no-plusplus': [0],
|
||||||
'import/no-extraneous-dependencies': [0],
|
'import/no-extraneous-dependencies': [0],
|
||||||
|
'consistent-return': [0],
|
||||||
|
'no-bitwise': [0]
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -102,3 +102,5 @@ dist
|
|||||||
|
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
|
|
||||||
|
dist
|
||||||
|
@ -3,11 +3,12 @@ module.exports = {
|
|||||||
[
|
[
|
||||||
'@babel/env',
|
'@babel/env',
|
||||||
{
|
{
|
||||||
// "modules": "cjs"
|
// modules: 'cjs',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
['./src/index.js', { transformOn: true }],
|
/* eslint-disable-next-line global-require */
|
||||||
|
[require('./dist/index.js'), { transformOn: true }],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -11,9 +11,10 @@
|
|||||||
"url": "git+https://github.com/vueComponent/jsx.git"
|
"url": "git+https://github.com/vueComponent/jsx.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server",
|
"dev": "npm run build && webpack-dev-server",
|
||||||
|
"build": "rollup -c",
|
||||||
"lint": "eslint --ext .js src",
|
"lint": "eslint --ext .js src",
|
||||||
"test": "jest"
|
"test": "jest --coverage"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/vueComponent/jsx/issues"
|
"url": "https://github.com/vueComponent/jsx/issues"
|
||||||
@ -30,12 +31,14 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.9.6",
|
"@babel/core": "^7.9.6",
|
||||||
"@babel/preset-env": "^7.9.6",
|
"@babel/preset-env": "^7.9.6",
|
||||||
|
"@rollup/plugin-babel": "^5.0.3",
|
||||||
"@vue/compiler-dom": "^3.0.0-beta.14",
|
"@vue/compiler-dom": "^3.0.0-beta.14",
|
||||||
"@vue/test-utils": "^2.0.0-alpha.6",
|
"@vue/test-utils": "^2.0.0-alpha.6",
|
||||||
"babel-jest": "^26.0.1",
|
"babel-jest": "^26.0.1",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"jest": "^26.0.1",
|
"jest": "^26.0.1",
|
||||||
"regenerator-runtime": "^0.13.5",
|
"regenerator-runtime": "^0.13.5",
|
||||||
|
"rollup": "^2.13.1",
|
||||||
"vue": "^3.0.0-beta.14",
|
"vue": "^3.0.0-beta.14",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.43.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
|
26
packages/babel-plugin-jsx/rollup.config.js
Normal file
26
packages/babel-plugin-jsx/rollup.config.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import babel from '@rollup/plugin-babel';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'src/index.js',
|
||||||
|
plugins: [
|
||||||
|
babel({
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: {
|
||||||
|
node: 8,
|
||||||
|
},
|
||||||
|
modules: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: 'dist/index.js',
|
||||||
|
format: 'cjs',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -1,9 +1,9 @@
|
|||||||
const syntaxJsx = require('@babel/plugin-syntax-jsx').default;
|
import syntaxJsx from '@babel/plugin-syntax-jsx';
|
||||||
const tranformVueJSX = require('./transform-vue-jsx');
|
import tranformVueJSX from './transform-vue-jsx';
|
||||||
const sugarVModel = require('./sugar-v-model');
|
import sugarVModel from './sugar-v-model';
|
||||||
const sugarFragment = require('./sugar-fragment');
|
import sugarFragment from './sugar-fragment';
|
||||||
|
|
||||||
module.exports = ({ types: t }) => ({
|
export default ({ types: t }) => ({
|
||||||
name: 'babel-plugin-jsx',
|
name: 'babel-plugin-jsx',
|
||||||
inherits: syntaxJsx,
|
inherits: syntaxJsx,
|
||||||
visitor: {
|
visitor: {
|
||||||
|
@ -1,22 +1,30 @@
|
|||||||
const helperModuleImports = require('@babel/helper-module-imports');
|
import { addNamespace } from '@babel/helper-module-imports';
|
||||||
|
|
||||||
const transformFragment = (t, path, { name }) => {
|
const transformFragment = (t, path, Fragment) => {
|
||||||
const children = path.get('children') || [];
|
const children = path.get('children') || [];
|
||||||
return t.jsxElement(
|
return t.jsxElement(
|
||||||
t.jsxOpeningElement(t.jsxIdentifier(name), []),
|
t.jsxOpeningElement(Fragment, []),
|
||||||
t.jsxClosingElement(t.jsxIdentifier(name)),
|
t.jsxClosingElement(Fragment),
|
||||||
children.map(({ node }) => node),
|
children.map(({ node }) => node),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = (t) => ({
|
export default (t) => ({
|
||||||
JSXFragment: {
|
JSXFragment: {
|
||||||
enter(path) {
|
enter(path, state) {
|
||||||
if (!path.vueFragment) {
|
if (!state.get('vue')) {
|
||||||
path.vueFragment = helperModuleImports.addNamed(path, 'Fragment', 'vue');
|
state.set('vue', addNamespace(path, 'vue'));
|
||||||
}
|
}
|
||||||
path.replaceWith(transformFragment(t, path, path.vueFragment));
|
path.replaceWith(
|
||||||
|
transformFragment(
|
||||||
|
t, path,
|
||||||
|
t.jsxMemberExpression(
|
||||||
|
t.jsxIdentifier(state.get('vue').name),
|
||||||
|
t.jsxIdentifier('Fragment'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
const htmlTags = require('html-tags');
|
import camelCase from 'camelcase';
|
||||||
const svgTags = require('svg-tags');
|
import { addNamespace } from '@babel/helper-module-imports';
|
||||||
const camelCase = require('camelcase');
|
import { createIdentifier, checkIsComponent } from './utils';
|
||||||
const { addNamed } = require('@babel/helper-module-imports');
|
|
||||||
|
|
||||||
|
|
||||||
const cachedCamelCase = (() => {
|
const cachedCamelCase = (() => {
|
||||||
const cache = Object.create(null);
|
const cache = Object.create(null);
|
||||||
@ -55,30 +53,11 @@ const getType = (t, path) => {
|
|||||||
return typePath ? typePath.get('value.value').node : '';
|
return typePath ? typePath.get('value.value').node : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a JSXOpeningElement is a component
|
|
||||||
*
|
|
||||||
* @param t
|
|
||||||
* @param path JSXOpeningElement
|
|
||||||
* @returns boolean
|
|
||||||
*/
|
|
||||||
const isComponent = (t, path) => {
|
|
||||||
const name = path.get('name');
|
|
||||||
|
|
||||||
if (t.isJSXMemberExpression(name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tag = name.get('name').node;
|
|
||||||
|
|
||||||
return !htmlTags.includes(tag) && !svgTags.includes(tag);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param t
|
* @param t
|
||||||
* Transform vModel
|
* Transform vModel
|
||||||
*/
|
*/
|
||||||
const getModelDirective = (t, path, value) => {
|
const getModelDirective = (t, path, state, value) => {
|
||||||
const tag = getTagName(path);
|
const tag = getTagName(path);
|
||||||
const type = getType(t, path);
|
const type = getType(t, path);
|
||||||
|
|
||||||
@ -94,7 +73,7 @@ const getModelDirective = (t, path, value) => {
|
|||||||
]),
|
]),
|
||||||
));
|
));
|
||||||
|
|
||||||
if (isComponent(t, path)) {
|
if (checkIsComponent(t, path)) {
|
||||||
addProp(path, t.jsxAttribute(t.jsxIdentifier('modelValue'), t.jsxExpressionContainer(value)));
|
addProp(path, t.jsxAttribute(t.jsxIdentifier('modelValue'), t.jsxExpressionContainer(value)));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -102,35 +81,21 @@ const getModelDirective = (t, path, value) => {
|
|||||||
let modelToUse;
|
let modelToUse;
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case 'select':
|
case 'select':
|
||||||
if (!path.vueVModelSelect) {
|
modelToUse = createIdentifier(t, state, 'vModelSelect');
|
||||||
path.vueVModelSelect = addNamed(path, 'vModelSelect', 'vue');
|
|
||||||
}
|
|
||||||
modelToUse = path.vueVModelSelect;
|
|
||||||
break;
|
break;
|
||||||
case 'textarea':
|
case 'textarea':
|
||||||
if (!path.vueVModelText) {
|
modelToUse = createIdentifier(t, state, 'vModelText');
|
||||||
path.vueVModelText = addNamed(path, 'vModelText', 'vue');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
if (!path.vueVModelCheckbox) {
|
modelToUse = createIdentifier(t, state, 'vModelCheckbox');
|
||||||
path.vueVModelCheckbox = addNamed(path, 'vModelCheckbox', 'vue');
|
|
||||||
}
|
|
||||||
modelToUse = path.vueVModelCheckbox;
|
|
||||||
break;
|
break;
|
||||||
case 'radio':
|
case 'radio':
|
||||||
if (!path.vueVModelRadio) {
|
modelToUse = createIdentifier(t, state, 'vModelRadio');
|
||||||
path.vueVModelRadio = addNamed(path, 'vModelRadio', 'vue');
|
|
||||||
}
|
|
||||||
modelToUse = path.vueVModelRadio;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!path.vueVModelText) {
|
modelToUse = createIdentifier(t, state, 'vModelText');
|
||||||
path.vueVModelText = addNamed(path, 'vModelText', 'vue');
|
|
||||||
}
|
|
||||||
modelToUse = path.vueVModelText;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +120,7 @@ const parseVModel = (t, path) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const modifiers = path.get('name.name').node.split('_');
|
const modifiers = path.get('name.name').node.split('_');
|
||||||
|
modifiers.shift();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modifiers: new Set(modifiers),
|
modifiers: new Set(modifiers),
|
||||||
@ -162,23 +128,27 @@ const parseVModel = (t, path) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = (t) => ({
|
export default (t) => ({
|
||||||
JSXAttribute: {
|
JSXAttribute: {
|
||||||
exit(path) {
|
exit(path, state) {
|
||||||
const parsed = parseVModel(t, path);
|
const parsed = parseVModel(t, path);
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.get('vue')) {
|
||||||
|
state.set('vue', addNamespace(path, 'vue'));
|
||||||
|
}
|
||||||
|
|
||||||
const { modifiers, value } = parsed;
|
const { modifiers, value } = parsed;
|
||||||
|
|
||||||
const parent = path.parentPath;
|
const parent = path.parentPath;
|
||||||
// v-model={xx} --> v-_model={[directive, xx, void 0, { a: true, b: true }]}
|
// v-model={xx} --> v-_model={[directive, xx, void 0, { a: true, b: true }]}
|
||||||
const directive = getModelDirective(t, parent, value);
|
const directive = getModelDirective(t, parent, state, value);
|
||||||
if (directive) {
|
if (directive) {
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
t.jsxAttribute(
|
t.jsxAttribute(
|
||||||
t.jsxIdentifier('v-_model'), // TODO
|
t.jsxIdentifier('_model'), // TODO
|
||||||
t.jsxExpressionContainer(
|
t.jsxExpressionContainer(
|
||||||
t.arrayExpression([
|
t.arrayExpression([
|
||||||
directive,
|
directive,
|
||||||
|
@ -1,69 +1,48 @@
|
|||||||
const htmlTags = require('html-tags');
|
import { addDefault, addNamespace } from '@babel/helper-module-imports';
|
||||||
const svgTags = require('svg-tags');
|
import {
|
||||||
const { addNamed, addDefault } = require('@babel/helper-module-imports');
|
createIdentifier,
|
||||||
|
PatchFlags,
|
||||||
|
PatchFlagNames,
|
||||||
|
isDirective,
|
||||||
|
checkIsComponent,
|
||||||
|
getTag,
|
||||||
|
getJSXAttributeName,
|
||||||
|
transformJSXText,
|
||||||
|
transformJSXExpressionContainer,
|
||||||
|
transformJSXSpreadChild,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
const xlinkRE = /^xlink([A-Z])/;
|
const xlinkRE = /^xlink([A-Z])/;
|
||||||
const eventRE = /^on[A-Z][a-z]+$/;
|
const onRE = /^on[A-Z][a-z]+$/;
|
||||||
const rootAttributes = ['class', 'style'];
|
const rootAttributes = ['class', 'style'];
|
||||||
|
|
||||||
|
const isOn = (key) => onRE.test(key);
|
||||||
|
|
||||||
/**
|
const transformJSXSpreadAttribute = (t, path, mergeArgs) => {
|
||||||
* Checks if string is describing a directive
|
const argument = path.get('argument').node;
|
||||||
* @param src string
|
const { properties } = argument;
|
||||||
*/
|
if (!properties) {
|
||||||
const isDirective = (src) => src.startsWith('v-')
|
return t.spreadElement(argument);
|
||||||
|| (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z');
|
}
|
||||||
|
return t.spreadElement(t.objectExpression(properties.filter((property) => {
|
||||||
/**
|
const { key, value } = property;
|
||||||
* Transform JSXMemberExpression to MemberExpression
|
const name = key.value;
|
||||||
* @param t
|
if (rootAttributes.includes(name)) {
|
||||||
* @param path JSXMemberExpression
|
mergeArgs.push(
|
||||||
* @returns MemberExpression
|
t.objectExpression([
|
||||||
*/
|
t.objectProperty(
|
||||||
const transformJSXMemberExpression = (t, path) => {
|
t.stringLiteral(name),
|
||||||
const objectPath = path.get('object');
|
value,
|
||||||
const propertyPath = path.get('property');
|
),
|
||||||
|
]),
|
||||||
const transformedObject = objectPath.isJSXMemberExpression()
|
);
|
||||||
? transformJSXMemberExpression(t, objectPath)
|
return false;
|
||||||
: objectPath.isJSXIdentifier()
|
|
||||||
? t.identifier(objectPath.node.name)
|
|
||||||
: t.nullLiteral();
|
|
||||||
const transformedProperty = t.identifier(propertyPath.get('name').node);
|
|
||||||
return t.memberExpression(transformedObject, transformedProperty);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get tag (first attribute for h) from JSXOpeningElement
|
|
||||||
* @param t
|
|
||||||
* @param path JSXOpeningElement
|
|
||||||
* @returns Identifier | StringLiteral | MemberExpression
|
|
||||||
*/
|
|
||||||
const getTag = (t, path) => {
|
|
||||||
const namePath = path.get('openingElement').get('name');
|
|
||||||
if (namePath.isJSXIdentifier()) {
|
|
||||||
const { name } = namePath.node;
|
|
||||||
if (path.scope.hasBinding(name) && !htmlTags.includes(name) && !svgTags.includes(name)) {
|
|
||||||
return t.identifier(name);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
return t.stringLiteral(name);
|
})));
|
||||||
}
|
|
||||||
|
|
||||||
if (namePath.isJSXMemberExpression()) {
|
|
||||||
return transformJSXMemberExpression(t, namePath);
|
|
||||||
}
|
|
||||||
throw new Error(`getTag: ${namePath.type} is not supported`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getJSXAttributeName = (t, path) => {
|
const needToMerge = (name) => rootAttributes.includes(name) || isOn(name);
|
||||||
const nameNode = path.node.name;
|
|
||||||
if (t.isJSXIdentifier(nameNode)) {
|
|
||||||
return nameNode.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${nameNode.namespace.name}:${nameNode.name.name}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getJSXAttributeValue = (t, path) => {
|
const getJSXAttributeValue = (t, path) => {
|
||||||
const valuePath = path.get('value');
|
const valuePath = path.get('value');
|
||||||
@ -80,183 +59,191 @@ const getJSXAttributeValue = (t, path) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const transformJSXAttribute = (t, path, state, attributesToMerge, directives) => {
|
/**
|
||||||
let name = getJSXAttributeName(t, path);
|
* Check if an attribute value is constant
|
||||||
const attributeValue = getJSXAttributeValue(t, path);
|
* @param t
|
||||||
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
|
* @param path
|
||||||
const transformOn = addDefault(path, '@ant-design-vue/babel-helper-vue-transform-on', { nameHint: '_transformOn' });
|
* @returns boolean
|
||||||
attributesToMerge.push(t.callExpression(
|
*/
|
||||||
transformOn,
|
const isConstant = (t, path) => {
|
||||||
[attributeValue || t.booleanLiteral(true)],
|
if (t.isIdentifier(path)) {
|
||||||
));
|
return path.name === 'undefined';
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
if (isDirective(name)) {
|
if (t.isArrayExpression(path)) {
|
||||||
const directiveName = name.startsWith('v-')
|
return path.elements.every((element) => isConstant(t, element));
|
||||||
? name.replace('v-', '')
|
|
||||||
: name.replace(`v${name[1]}`, name[1].toLowerCase());
|
|
||||||
if (directiveName === '_model') {
|
|
||||||
directives.push(attributeValue);
|
|
||||||
} else if (directiveName === 'show') {
|
|
||||||
directives.push(t.arrayExpression([
|
|
||||||
state.vShow,
|
|
||||||
attributeValue,
|
|
||||||
]));
|
|
||||||
} else {
|
|
||||||
directives.push(t.arrayExpression([
|
|
||||||
t.callExpression(state.resolveDirective, [
|
|
||||||
t.stringLiteral(directiveName),
|
|
||||||
]),
|
|
||||||
attributeValue,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
if (rootAttributes.includes(name) || eventRE.test(name)) {
|
if (t.isObjectExpression(path)) {
|
||||||
attributesToMerge.push(
|
return path.properties.every((property) => isConstant(t, property.value));
|
||||||
t.objectExpression([
|
|
||||||
t.objectProperty(
|
|
||||||
t.stringLiteral(
|
|
||||||
name,
|
|
||||||
),
|
|
||||||
attributeValue,
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
if (name.match(xlinkRE)) {
|
if (t.isLiteral(path)) {
|
||||||
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.objectProperty(
|
|
||||||
t.stringLiteral(
|
|
||||||
name,
|
|
||||||
),
|
|
||||||
attributeValue || t.booleanLiteral(true),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const transformJSXSpreadAttribute = (t, path, attributesToMerge) => {
|
|
||||||
const argument = path.get('argument').node;
|
|
||||||
const { properties } = argument;
|
|
||||||
if (!properties) {
|
|
||||||
return t.spreadElement(argument);
|
|
||||||
}
|
|
||||||
return t.spreadElement(t.objectExpression(properties.filter((property) => {
|
|
||||||
const { key, value } = property;
|
|
||||||
const name = key.value;
|
|
||||||
if (rootAttributes.includes(name)) {
|
|
||||||
attributesToMerge.push(
|
|
||||||
t.objectExpression([
|
|
||||||
t.objectProperty(
|
|
||||||
t.stringLiteral(name),
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
})));
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const transformAttribute = (t, path, state, attributesToMerge, directives) => (
|
const buildProps = (t, path, state) => {
|
||||||
path.isJSXAttribute()
|
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
||||||
? transformJSXAttribute(t, path, state, attributesToMerge, directives)
|
const props = path.get('openingElement').get('attributes');
|
||||||
: transformJSXSpreadAttribute(t, path, attributesToMerge));
|
const directives = [];
|
||||||
|
if (props.length === 0) {
|
||||||
const getAttributes = (t, path, state, directives) => {
|
return {
|
||||||
const attributes = path.get('openingElement').get('attributes');
|
props: t.nullLiteral(),
|
||||||
if (attributes.length === 0) {
|
directives,
|
||||||
return t.nullLiteral();
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributesToMerge = [];
|
const propsExpression = [];
|
||||||
const attributeArray = [];
|
|
||||||
attributes
|
// patchFlag analysis
|
||||||
.forEach((attribute) => {
|
let patchFlag = 0;
|
||||||
const attr = transformAttribute(t, attribute, state, attributesToMerge, directives);
|
let hasRef = false;
|
||||||
if (attr) {
|
let hasClassBinding = false;
|
||||||
attributeArray.push(attr);
|
let hasStyleBinding = false;
|
||||||
|
let hasHydrationEventBinding = false;
|
||||||
|
let hasDynamicKeys = false;
|
||||||
|
|
||||||
|
const dynamicPropNames = [];
|
||||||
|
const mergeArgs = [];
|
||||||
|
|
||||||
|
props
|
||||||
|
.forEach((prop) => {
|
||||||
|
if (prop.isJSXAttribute()) {
|
||||||
|
let name = getJSXAttributeName(t, prop);
|
||||||
|
|
||||||
|
if (name === '_model') {
|
||||||
|
name = 'onUpdate:modelValue';
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributeValue = getJSXAttributeValue(t, prop);
|
||||||
|
|
||||||
|
if (!isConstant(t, attributeValue) || name === 'ref') {
|
||||||
|
if (
|
||||||
|
!isComponent
|
||||||
|
&& isOn(name)
|
||||||
|
// omit the flag for click handlers becaues hydration gives click
|
||||||
|
// dedicated fast path.
|
||||||
|
&& name.toLowerCase() !== 'onclick'
|
||||||
|
// omit v-model handlers
|
||||||
|
&& name !== 'onUpdate:modelValue'
|
||||||
|
) {
|
||||||
|
hasHydrationEventBinding = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'ref') {
|
||||||
|
hasRef = true;
|
||||||
|
} else if (name === 'class' && !isComponent) {
|
||||||
|
hasClassBinding = true;
|
||||||
|
} else if (name === 'style' && !isComponent) {
|
||||||
|
hasStyleBinding = true;
|
||||||
|
} else if (
|
||||||
|
name !== 'key'
|
||||||
|
&& !isDirective(name)
|
||||||
|
&& name !== 'on'
|
||||||
|
&& !dynamicPropNames.includes(name)
|
||||||
|
) {
|
||||||
|
dynamicPropNames.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
|
||||||
|
const transformOn = addDefault(
|
||||||
|
path,
|
||||||
|
'@ant-design-vue/babel-helper-vue-transform-on',
|
||||||
|
{ nameHint: '_transformOn' },
|
||||||
|
);
|
||||||
|
mergeArgs.push(t.callExpression(
|
||||||
|
transformOn,
|
||||||
|
[attributeValue || t.booleanLiteral(true)],
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isDirective(name) || name === 'onUpdate:modelValue') {
|
||||||
|
if (name === 'onUpdate:modelValue') {
|
||||||
|
directives.push(attributeValue);
|
||||||
|
} else {
|
||||||
|
const directiveName = name.startsWith('v-')
|
||||||
|
? name.replace('v-', '')
|
||||||
|
: name.replace(`v${name[1]}`, name[1].toLowerCase());
|
||||||
|
if (directiveName === 'show') {
|
||||||
|
directives.push(t.arrayExpression([
|
||||||
|
createIdentifier(t, state, 'vShow'),
|
||||||
|
attributeValue,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
directives.push(t.arrayExpression([
|
||||||
|
t.callExpression(createIdentifier(t, state, 'resolveDirective'), [
|
||||||
|
t.stringLiteral(directiveName),
|
||||||
|
]),
|
||||||
|
attributeValue,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (needToMerge(name)) {
|
||||||
|
mergeArgs.push(
|
||||||
|
t.objectExpression([
|
||||||
|
t.objectProperty(
|
||||||
|
t.stringLiteral(
|
||||||
|
name,
|
||||||
|
),
|
||||||
|
attributeValue,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name.match(xlinkRE)) {
|
||||||
|
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
|
||||||
|
}
|
||||||
|
propsExpression.push(t.objectProperty(
|
||||||
|
t.stringLiteral(name),
|
||||||
|
attributeValue || t.booleanLiteral(true),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
hasDynamicKeys = true;
|
||||||
|
propsExpression.push(transformJSXSpreadAttribute(t, prop, mergeArgs));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return t.callExpression(
|
|
||||||
state.mergeProps,
|
|
||||||
[
|
|
||||||
...attributesToMerge,
|
|
||||||
t.objectExpression(attributeArray),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// patchFlag analysis
|
||||||
* Transform JSXText to StringLiteral
|
if (hasDynamicKeys) {
|
||||||
* @param t
|
patchFlag |= PatchFlags.FULL_PROPS;
|
||||||
* @param path JSXText
|
} else {
|
||||||
* @returns StringLiteral
|
if (hasClassBinding) {
|
||||||
*/
|
patchFlag |= PatchFlags.CLASS;
|
||||||
const transformJSXText = (t, path) => {
|
}
|
||||||
const { node } = path;
|
if (hasStyleBinding) {
|
||||||
const lines = node.value.split(/\r\n|\n|\r/);
|
patchFlag |= PatchFlags.STYLE;
|
||||||
|
}
|
||||||
let lastNonEmptyLine = 0;
|
if (dynamicPropNames.length) {
|
||||||
|
patchFlag |= PatchFlags.PROPS;
|
||||||
for (let i = 0; i < lines.length; i++) {
|
}
|
||||||
if (lines[i].match(/[^ \t]/)) {
|
if (hasHydrationEventBinding) {
|
||||||
lastNonEmptyLine = i;
|
patchFlag |= PatchFlags.HYDRATE_EVENTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = '';
|
if (
|
||||||
|
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS)
|
||||||
for (let i = 0; i < lines.length; i++) {
|
&& hasRef
|
||||||
const line = lines[i];
|
) {
|
||||||
|
patchFlag |= PatchFlags.NEED_PATCH;
|
||||||
const isFirstLine = i === 0;
|
|
||||||
const isLastLine = i === lines.length - 1;
|
|
||||||
const isLastNonEmptyLine = i === lastNonEmptyLine;
|
|
||||||
|
|
||||||
// replace rendered whitespace tabs with spaces
|
|
||||||
let trimmedLine = line.replace(/\t/g, ' ');
|
|
||||||
|
|
||||||
// trim whitespace touching a newline
|
|
||||||
if (!isFirstLine) {
|
|
||||||
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim whitespace touching an endline
|
|
||||||
if (!isLastLine) {
|
|
||||||
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmedLine) {
|
|
||||||
if (!isLastNonEmptyLine) {
|
|
||||||
trimmedLine += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
str += trimmedLine;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str !== '' ? t.stringLiteral(str) : null;
|
return {
|
||||||
|
props: mergeArgs.length ? t.callExpression(
|
||||||
|
createIdentifier(t, state, 'mergeProps'),
|
||||||
|
[
|
||||||
|
...mergeArgs,
|
||||||
|
propsExpression.length && t.objectExpression(propsExpression),
|
||||||
|
].filter(Boolean),
|
||||||
|
) : t.objectExpression(propsExpression),
|
||||||
|
directives,
|
||||||
|
patchFlag,
|
||||||
|
dynamicPropNames,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform JSXExpressionContainer to Expression
|
|
||||||
* @param path JSXExpressionContainer
|
|
||||||
* @returns Expression
|
|
||||||
*/
|
|
||||||
const transformJSXExpressionContainer = (path) => path.get('expression').node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform JSXSpreadChild
|
|
||||||
* @param t
|
|
||||||
* @param path JSXSpreadChild
|
|
||||||
* @returns SpreadElement
|
|
||||||
*/
|
|
||||||
const transformJSXSpreadChild = (t, path) => t.spreadElement(path.get('expression').node);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get children from Array of JSX children
|
* Get children from Array of JSX children
|
||||||
* @param t
|
* @param t
|
||||||
@ -289,50 +276,64 @@ const getChildren = (t, paths) => paths
|
|||||||
|
|
||||||
|
|
||||||
const transformJSXElement = (t, path, state) => {
|
const transformJSXElement = (t, path, state) => {
|
||||||
const directives = [];
|
|
||||||
const tag = getTag(t, path);
|
const tag = getTag(t, path);
|
||||||
const children = t.arrayExpression(getChildren(t, path.get('children')));
|
const children = t.arrayExpression(getChildren(t, path.get('children')));
|
||||||
const attributes = getAttributes(t, path, state, directives);
|
const {
|
||||||
const compatibleProps = addDefault(
|
props,
|
||||||
path, '@ant-design-vue/babel-helper-vue-compatible-props', { nameHint: '_compatibleProps' },
|
directives,
|
||||||
);
|
patchFlag,
|
||||||
const h = t.callExpression(state.h, [
|
dynamicPropNames = [],
|
||||||
|
} = buildProps(t, path, state);
|
||||||
|
|
||||||
|
const flagNames = Object.keys(PatchFlagNames)
|
||||||
|
.map(Number)
|
||||||
|
.filter((n) => n > 0 && patchFlag & n)
|
||||||
|
.map((n) => PatchFlagNames[n])
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
||||||
|
const createVNode = t.callExpression(createIdentifier(t, state, 'createVNode'), [
|
||||||
tag,
|
tag,
|
||||||
state.opts.compatibleProps ? t.callExpression(compatibleProps, [attributes]) : attributes,
|
state.opts.compatibleProps ? t.callExpression(addDefault(
|
||||||
!t.isStringLiteral(tag) && !tag.name.includes('Fragment')
|
path, '@ant-design-vue/babel-helper-vue-compatible-props', { nameHint: '_compatibleProps' },
|
||||||
? t.objectExpression([
|
), [props]) : props,
|
||||||
t.objectProperty(
|
children.elements.length
|
||||||
t.identifier('default'),
|
? (
|
||||||
t.callExpression(state.withCtx, [
|
isComponent
|
||||||
t.arrowFunctionExpression(
|
? t.objectExpression([
|
||||||
[],
|
t.objectProperty(
|
||||||
children,
|
t.identifier('default'),
|
||||||
|
t.callExpression(createIdentifier(t, state, 'withCtx'), [
|
||||||
|
t.arrowFunctionExpression(
|
||||||
|
[],
|
||||||
|
children,
|
||||||
|
),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
]),
|
])
|
||||||
),
|
: children
|
||||||
])
|
) : t.nullLiteral(),
|
||||||
: children,
|
patchFlag && t.addComment(t.numericLiteral(patchFlag), 'leading', ` ${flagNames} `),
|
||||||
]);
|
dynamicPropNames.length
|
||||||
|
&& t.arrayExpression(dynamicPropNames.map((name) => t.stringLiteral(name))),
|
||||||
|
].filter(Boolean));
|
||||||
|
|
||||||
if (!directives.length) {
|
if (!directives.length) {
|
||||||
return h;
|
return createVNode;
|
||||||
}
|
}
|
||||||
return t.callExpression(state.withDirectives, [
|
|
||||||
h,
|
return t.callExpression(createIdentifier(t, state, 'withDirectives'), [
|
||||||
|
createVNode,
|
||||||
t.arrayExpression(directives),
|
t.arrayExpression(directives),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const imports = [
|
export default (t) => ({
|
||||||
'h', 'mergeProps', 'withDirectives',
|
|
||||||
'resolveDirective', 'vShow', 'withCtx',
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = (t) => ({
|
|
||||||
JSXElement: {
|
JSXElement: {
|
||||||
exit(path, state) {
|
exit(path, state) {
|
||||||
imports.forEach((m) => {
|
if (!state.get('vue')) {
|
||||||
state[m] = addNamed(path, m, 'vue');
|
state.set('vue', addNamespace(path, 'vue'));
|
||||||
});
|
}
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
transformJSXElement(t, path, state),
|
transformJSXElement(t, path, state),
|
||||||
);
|
);
|
||||||
|
196
packages/babel-plugin-jsx/src/utils.js
Normal file
196
packages/babel-plugin-jsx/src/utils.js
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import htmlTags from 'html-tags';
|
||||||
|
import svgTags from 'svg-tags';
|
||||||
|
|
||||||
|
const PatchFlags = {
|
||||||
|
TEXT: 1,
|
||||||
|
CLASS: 1 << 1,
|
||||||
|
STYLE: 1 << 2,
|
||||||
|
PROPS: 1 << 3,
|
||||||
|
FULL_PROPS: 1 << 4,
|
||||||
|
HYDRATE_EVENTS: 1 << 5,
|
||||||
|
STABLE_FRAGMENT: 1 << 6,
|
||||||
|
KEYED_FRAGMENT: 1 << 7,
|
||||||
|
UNKEYED_FRAGMENT: 1 << 8,
|
||||||
|
NEED_PATCH: 1 << 9,
|
||||||
|
DYNAMIC_SLOTS: 1 << 10,
|
||||||
|
HOISTED: -1,
|
||||||
|
BAIL: -2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// dev only flag -> name mapping
|
||||||
|
const PatchFlagNames = {
|
||||||
|
[PatchFlags.TEXT]: 'TEXT',
|
||||||
|
[PatchFlags.CLASS]: 'CLASS',
|
||||||
|
[PatchFlags.STYLE]: 'STYLE',
|
||||||
|
[PatchFlags.PROPS]: 'PROPS',
|
||||||
|
[PatchFlags.FULL_PROPS]: 'FULL_PROPS',
|
||||||
|
[PatchFlags.HYDRATE_EVENTS]: 'HYDRATE_EVENTS',
|
||||||
|
[PatchFlags.STABLE_FRAGMENT]: 'STABLE_FRAGMENT',
|
||||||
|
[PatchFlags.KEYED_FRAGMENT]: 'KEYED_FRAGMENT',
|
||||||
|
[PatchFlags.UNKEYED_FRAGMENT]: 'UNKEYED_FRAGMENT',
|
||||||
|
[PatchFlags.NEED_PATCH]: 'NEED_PATCH',
|
||||||
|
[PatchFlags.DYNAMIC_SLOTS]: 'DYNAMIC_SLOTS',
|
||||||
|
[PatchFlags.HOISTED]: 'HOISTED',
|
||||||
|
[PatchFlags.BAIL]: 'BAIL',
|
||||||
|
};
|
||||||
|
|
||||||
|
const createIdentifier = (t, state, id) => t.memberExpression(state.get('vue'), t.identifier(id));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if string is describing a directive
|
||||||
|
* @param src string
|
||||||
|
*/
|
||||||
|
const isDirective = (src) => src.startsWith('v-')
|
||||||
|
|| (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a JSXOpeningElement is a component
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* @param path JSXOpeningElement
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
const checkIsComponent = (t, path) => {
|
||||||
|
const namePath = path.get('name');
|
||||||
|
|
||||||
|
if (t.isJSXMemberExpression(namePath)) {
|
||||||
|
return namePath.node.property.name !== 'Fragment'; // For withCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = namePath.get('name').node;
|
||||||
|
|
||||||
|
return !htmlTags.includes(tag) && !svgTags.includes(tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform JSXMemberExpression to MemberExpression
|
||||||
|
* @param t
|
||||||
|
* @param path JSXMemberExpression
|
||||||
|
* @returns MemberExpression
|
||||||
|
*/
|
||||||
|
const transformJSXMemberExpression = (t, path) => {
|
||||||
|
const objectPath = path.get('object');
|
||||||
|
const propertyPath = path.get('property');
|
||||||
|
|
||||||
|
const transformedObject = objectPath.isJSXMemberExpression()
|
||||||
|
? transformJSXMemberExpression(t, objectPath)
|
||||||
|
: objectPath.isJSXIdentifier()
|
||||||
|
? t.identifier(objectPath.node.name)
|
||||||
|
: t.nullLiteral();
|
||||||
|
const transformedProperty = t.identifier(propertyPath.get('name').node);
|
||||||
|
return t.memberExpression(transformedObject, transformedProperty);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tag (first attribute for h) from JSXOpeningElement
|
||||||
|
* @param t
|
||||||
|
* @param path JSXOpeningElement
|
||||||
|
* @returns Identifier | StringLiteral | MemberExpression
|
||||||
|
*/
|
||||||
|
const getTag = (t, path) => {
|
||||||
|
const namePath = path.get('openingElement').get('name');
|
||||||
|
if (namePath.isJSXIdentifier()) {
|
||||||
|
const { name } = namePath.node;
|
||||||
|
if (path.scope.hasBinding(name) && !htmlTags.includes(name) && !svgTags.includes(name)) {
|
||||||
|
return t.identifier(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.stringLiteral(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namePath.isJSXMemberExpression()) {
|
||||||
|
return transformJSXMemberExpression(t, namePath);
|
||||||
|
}
|
||||||
|
throw new Error(`getTag: ${namePath.type} is not supported`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getJSXAttributeName = (t, path) => {
|
||||||
|
const nameNode = path.node.name;
|
||||||
|
if (t.isJSXIdentifier(nameNode)) {
|
||||||
|
return nameNode.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${nameNode.namespace.name}:${nameNode.name.name}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform JSXText to StringLiteral
|
||||||
|
* @param t
|
||||||
|
* @param path JSXText
|
||||||
|
* @returns StringLiteral
|
||||||
|
*/
|
||||||
|
const transformJSXText = (t, path) => {
|
||||||
|
const { node } = path;
|
||||||
|
const lines = node.value.split(/\r\n|\n|\r/);
|
||||||
|
|
||||||
|
let lastNonEmptyLine = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].match(/[^ \t]/)) {
|
||||||
|
lastNonEmptyLine = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
const isFirstLine = i === 0;
|
||||||
|
const isLastLine = i === lines.length - 1;
|
||||||
|
const isLastNonEmptyLine = i === lastNonEmptyLine;
|
||||||
|
|
||||||
|
// replace rendered whitespace tabs with spaces
|
||||||
|
let trimmedLine = line.replace(/\t/g, ' ');
|
||||||
|
|
||||||
|
// trim whitespace touching a newline
|
||||||
|
if (!isFirstLine) {
|
||||||
|
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim whitespace touching an endline
|
||||||
|
if (!isLastLine) {
|
||||||
|
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmedLine) {
|
||||||
|
if (!isLastNonEmptyLine) {
|
||||||
|
trimmedLine += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
str += trimmedLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str !== '' ? t.stringLiteral(str) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform JSXExpressionContainer to Expression
|
||||||
|
* @param path JSXExpressionContainer
|
||||||
|
* @returns Expression
|
||||||
|
*/
|
||||||
|
const transformJSXExpressionContainer = (path) => path.get('expression').node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform JSXSpreadChild
|
||||||
|
* @param t
|
||||||
|
* @param path JSXSpreadChild
|
||||||
|
* @returns SpreadElement
|
||||||
|
*/
|
||||||
|
const transformJSXSpreadChild = (t, path) => t.spreadElement(path.get('expression').node);
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
createIdentifier,
|
||||||
|
isDirective,
|
||||||
|
checkIsComponent,
|
||||||
|
transformJSXMemberExpression,
|
||||||
|
getTag,
|
||||||
|
getJSXAttributeName,
|
||||||
|
transformJSXText,
|
||||||
|
transformJSXSpreadChild,
|
||||||
|
transformJSXExpressionContainer,
|
||||||
|
PatchFlags,
|
||||||
|
PatchFlagNames,
|
||||||
|
};
|
13
packages/babel-plugin-jsx/test/coverage.test.js
Normal file
13
packages/babel-plugin-jsx/test/coverage.test.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { transformSync } from '@babel/core';
|
||||||
|
import preset from '../babel.config';
|
||||||
|
|
||||||
|
test('coverage', () => {
|
||||||
|
const mainTest = fs.readFileSync(path.resolve(__dirname, './index.test.js'));
|
||||||
|
transformSync(mainTest, {
|
||||||
|
babelrc: false,
|
||||||
|
presets: [preset],
|
||||||
|
filename: 'index.test.js',
|
||||||
|
});
|
||||||
|
});
|
@ -1,244 +1,322 @@
|
|||||||
import { shallowMount } from '@vue/test-utils';
|
import { ref } from 'vue';
|
||||||
|
import { shallowMount, mount } from '@vue/test-utils';
|
||||||
|
|
||||||
test('should render with render function', () => {
|
describe('Transform JSX', () => {
|
||||||
const wrapper = shallowMount({
|
test('should render with render function', () => {
|
||||||
render() {
|
const wrapper = shallowMount({
|
||||||
return <div>123</div>;
|
render() {
|
||||||
},
|
return <div>123</div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toBe('123');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render with setup', () => {
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div>123</div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toBe('123');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Extracts attrs', () => {
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div id="hi" dir="ltr" />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.element.id).toBe('hi');
|
||||||
|
expect(wrapper.element.dir).toBe('ltr');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Binds attrs', () => {
|
||||||
|
const id = 'foo';
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div>{id}</div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toBe('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not fallthrough with inheritAttrs: false', () => {
|
||||||
|
const Child = (props) => <div>{props.foo}</div>;
|
||||||
|
|
||||||
|
Child.inheritAttrs = false;
|
||||||
|
|
||||||
|
const wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
return () => (
|
||||||
|
<Child class="parent" foo={1} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toBe('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Fragment', () => {
|
||||||
|
const Child = () => <div>123</div>;
|
||||||
|
|
||||||
|
Child.inheritAttrs = false;
|
||||||
|
|
||||||
|
const wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
return () => (
|
||||||
|
<>
|
||||||
|
<Child />
|
||||||
|
<div>456</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).toBe('<div>123</div><div>456</div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('xlink:href', () => {
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <use xlinkHref={'#name'}></use>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.attributes()['xlink:href']).toBe('#name');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Merge class', () => {
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div class="a" {...{ class: 'b' } } />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toBe('<div class="a b"></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Merge style', () => {
|
||||||
|
const propsA = {
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const propsB = {
|
||||||
|
style: [
|
||||||
|
{
|
||||||
|
color: 'blue',
|
||||||
|
width: '200px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
width: '300px',
|
||||||
|
height: '300px',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div { ...propsA } { ...propsB } />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toBe('<div style="color: blue; width: 300px; height: 300px;"></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JSXSpreadChild', () => {
|
||||||
|
const a = ['1', '2'];
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <div>{[...a]}</div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toBe('12');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('domProps input[value]', () => {
|
||||||
|
const val = 'foo';
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <input type="text" value={val} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toBe('<input type="text">');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('domProps input[checked]', () => {
|
||||||
|
const val = 'foo';
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => <input checked={val} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.$.subTree.props.checked).toBe(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('domProps option[selected]', () => {
|
||||||
|
const val = 'foo';
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
render() {
|
||||||
|
return <option selected={val} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.vm.$.subTree.props.selected).toBe(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('domProps video[muted]', () => {
|
||||||
|
const val = 'foo';
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
render() {
|
||||||
|
return <video muted={val} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.$.subTree.props.muted).toBe(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Spread (single object expression)', () => {
|
||||||
|
const props = {
|
||||||
|
innerHTML: 123,
|
||||||
|
other: '1',
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
render() {
|
||||||
|
return <div {...props}></div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toBe('<div other="1">123</div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Spread (mixed)', async () => {
|
||||||
|
const calls = [];
|
||||||
|
const data = {
|
||||||
|
id: 'hehe',
|
||||||
|
onClick() {
|
||||||
|
calls.push(3);
|
||||||
|
},
|
||||||
|
innerHTML: 2,
|
||||||
|
class: ['a', 'b'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = shallowMount({
|
||||||
|
setup() {
|
||||||
|
return () => (
|
||||||
|
<div
|
||||||
|
href="huhu"
|
||||||
|
{...data}
|
||||||
|
class={{ c: true }}
|
||||||
|
onClick={() => calls.push(4)}
|
||||||
|
hook-insert={() => calls.push(2)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.attributes('id')).toBe('hehe');
|
||||||
|
expect(wrapper.attributes('href')).toBe('huhu');
|
||||||
|
expect(wrapper.text()).toBe('2');
|
||||||
|
expect(wrapper.classes()).toEqual(expect.arrayContaining(['a', 'b', 'c']));
|
||||||
|
|
||||||
|
await wrapper.trigger('click');
|
||||||
|
|
||||||
|
expect(calls).toEqual(expect.arrayContaining([3, 4]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('directive', () => {
|
||||||
|
const calls = [];
|
||||||
|
const customDirective = {
|
||||||
|
mounted() {
|
||||||
|
calls.push(1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(({
|
||||||
|
directives: { custom: customDirective },
|
||||||
|
setup() {
|
||||||
|
return () => (
|
||||||
|
<a
|
||||||
|
v-custom={{
|
||||||
|
value: 123,
|
||||||
|
modifiers: { modifier: true },
|
||||||
|
arg: 'arg',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
const node = wrapper.vm.$.subTree;
|
||||||
|
expect(calls).toEqual(expect.arrayContaining([1]));
|
||||||
|
expect(node.dirs).toHaveLength(1);
|
||||||
});
|
});
|
||||||
expect(wrapper.text()).toBe('123');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render with setup', () => {
|
describe('Patch Flags', () => {
|
||||||
const wrapper = shallowMount({
|
let renders = 0;
|
||||||
setup() {
|
const Child = {
|
||||||
return () => <div>123</div>;
|
setup(props) {
|
||||||
|
return () => {
|
||||||
|
renders++;
|
||||||
|
return <div>{props.text}</div>;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
expect(wrapper.text()).toBe('123');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Extracts attrs', () => {
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <div id="hi" dir="ltr" />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.element.id).toBe('hi');
|
|
||||||
expect(wrapper.element.dir).toBe('ltr');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Binds attrs', () => {
|
|
||||||
const id = 'foo';
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <div>{id}</div>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.text()).toBe('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not fallthrough with inheritAttrs: false', () => {
|
|
||||||
const Child = (props) => <div>{props.foo}</div>;
|
|
||||||
|
|
||||||
Child.inheritAttrs = false;
|
Child.inheritAttrs = false;
|
||||||
|
|
||||||
const wrapper = shallowMount({
|
it('should render when props change', async () => {
|
||||||
setup() {
|
const wrapper = mount({
|
||||||
return () => (
|
setup() {
|
||||||
<Child class="parent" foo={1} />
|
const count = ref(0);
|
||||||
);
|
const inc = () => {
|
||||||
},
|
count.value++;
|
||||||
});
|
};
|
||||||
expect(wrapper.text()).toBe('1');
|
return () => (
|
||||||
});
|
<div onClick={inc}>
|
||||||
|
<Child text={count.value} />
|
||||||
test('Fragment', () => {
|
</div>
|
||||||
const Child = () => <div>123</div>;
|
);
|
||||||
|
|
||||||
Child.inheritAttrs = false;
|
|
||||||
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<>
|
|
||||||
<Child />
|
|
||||||
<div>456</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.html()).toBe('<div>123</div><div>456</div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('xlink:href', () => {
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <use xlinkHref={'#name'}></use>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.attributes()['xlink:href']).toBe('#name');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Merge class', () => {
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <div class="a" {...{ class: 'b' } } />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.html()).toBe('<div class="a b"></div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Merge style', () => {
|
|
||||||
const propsA = {
|
|
||||||
style: {
|
|
||||||
color: 'red',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const propsB = {
|
|
||||||
style: [
|
|
||||||
{
|
|
||||||
color: 'blue',
|
|
||||||
width: '200px',
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
width: '300px',
|
|
||||||
height: '300px',
|
expect(renders).toBe(1);
|
||||||
|
await wrapper.trigger('click');
|
||||||
|
expect(renders).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render with static props', async () => {
|
||||||
|
renders = 0;
|
||||||
|
const wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
const count = ref(0);
|
||||||
|
const inc = () => {
|
||||||
|
count.value++;
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<div onClick={inc}>
|
||||||
|
<Child text={1} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
],
|
});
|
||||||
};
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <div { ...propsA } { ...propsB } />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.html()).toBe('<div style="color: blue; width: 300px; height: 300px;"></div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('JSXSpreadChild', () => {
|
expect(renders).toBe(1);
|
||||||
const a = ['1', '2'];
|
await wrapper.trigger('click');
|
||||||
const wrapper = shallowMount({
|
expect(renders).toBe(1);
|
||||||
setup() {
|
|
||||||
return () => <div>{[...a]}</div>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.text()).toBe('12');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('domProps input[value]', () => {
|
|
||||||
const val = 'foo';
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <input type="text" value={val} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.html()).toBe('<input type="text">');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('domProps input[checked]', () => {
|
|
||||||
const val = 'foo';
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => <input checked={val} />;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.vm.$.subTree.props.checked).toBe(val);
|
it('should not render when props does not change', async () => {
|
||||||
});
|
renders = 0;
|
||||||
|
const wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
const count = ref(0);
|
||||||
|
const s = ref('a');
|
||||||
|
const inc = () => {
|
||||||
|
count.value++;
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<div onClick={inc}>
|
||||||
|
<Child text={s.value} />
|
||||||
|
{count.value}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
test('domProps option[selected]', () => {
|
await wrapper.trigger('click');
|
||||||
const val = 'foo';
|
expect(renders).toBe(1);
|
||||||
const wrapper = shallowMount({
|
|
||||||
render() {
|
|
||||||
return <option selected={val} />;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
expect(wrapper.vm.$.subTree.props.selected).toBe(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('domProps video[muted]', () => {
|
|
||||||
const val = 'foo';
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
render() {
|
|
||||||
return <video muted={val} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.vm.$.subTree.props.muted).toBe(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Spread (single object expression)', () => {
|
|
||||||
const props = {
|
|
||||||
innerHTML: 123,
|
|
||||||
other: '1',
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
render() {
|
|
||||||
return <div {...props}></div>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(wrapper.html()).toBe('<div other="1">123</div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Spread (mixed)', async () => {
|
|
||||||
const calls = [];
|
|
||||||
const data = {
|
|
||||||
id: 'hehe',
|
|
||||||
onClick() {
|
|
||||||
calls.push(3);
|
|
||||||
},
|
|
||||||
innerHTML: 2,
|
|
||||||
class: ['a', 'b'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapper = shallowMount({
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<div
|
|
||||||
href="huhu"
|
|
||||||
{...data}
|
|
||||||
class={{ c: true }}
|
|
||||||
onClick={() => calls.push(4)}
|
|
||||||
hook-insert={() => calls.push(2)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.attributes('id')).toBe('hehe');
|
|
||||||
expect(wrapper.attributes('href')).toBe('huhu');
|
|
||||||
expect(wrapper.text()).toBe('2');
|
|
||||||
expect(wrapper.classes()).toEqual(expect.arrayContaining(['a', 'b', 'c']));
|
|
||||||
|
|
||||||
await wrapper.trigger('click');
|
|
||||||
|
|
||||||
expect(calls).toEqual(expect.arrayContaining([3, 4]));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('directive', () => {
|
|
||||||
const calls = [];
|
|
||||||
const customDirective = {
|
|
||||||
mounted() {
|
|
||||||
calls.push(1);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(({
|
|
||||||
directives: { custom: customDirective },
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<a
|
|
||||||
v-custom={{
|
|
||||||
value: 123,
|
|
||||||
modifiers: { modifier: true },
|
|
||||||
arg: 'arg',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
const node = wrapper.vm.$.subTree;
|
|
||||||
expect(calls).toEqual(expect.arrayContaining([1]));
|
|
||||||
expect(node.dirs).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
58
yarn.lock
58
yarn.lock
@ -144,7 +144,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.10.1"
|
"@babel/types" "^7.10.1"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.1":
|
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.1", "@babel/helper-module-imports@^7.7.4":
|
||||||
version "7.10.1"
|
version "7.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876"
|
||||||
integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==
|
integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==
|
||||||
@ -1886,6 +1886,23 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" ">= 8"
|
"@types/node" ">= 8"
|
||||||
|
|
||||||
|
"@rollup/plugin-babel@^5.0.3":
|
||||||
|
version "5.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.0.3.tgz#8d416865b0da79faf14e07c8d233abe0eac0753d"
|
||||||
|
integrity sha512-NlaPf4E6YFxeOCbqc+A2PTkB1BSy3rfKu6EJuQ1MGhMHpTVvMqKi6Rf0DlwtnEsTNK9LueUgsGEgp5Occ4KDVA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-module-imports" "^7.7.4"
|
||||||
|
"@rollup/pluginutils" "^3.0.8"
|
||||||
|
|
||||||
|
"@rollup/pluginutils@^3.0.8":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||||
|
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "0.0.39"
|
||||||
|
estree-walker "^1.0.1"
|
||||||
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
"@sinonjs/commons@^1.7.0":
|
"@sinonjs/commons@^1.7.0":
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
|
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
|
||||||
@ -1938,6 +1955,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||||
|
|
||||||
|
"@types/estree@0.0.39":
|
||||||
|
version "0.0.39"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||||
|
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||||
|
|
||||||
"@types/events@*":
|
"@types/events@*":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||||
@ -4330,11 +4352,21 @@ estraverse@^5.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
|
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
|
||||||
integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
|
integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
|
||||||
|
|
||||||
|
estree-walker@^0.6.1:
|
||||||
|
version "0.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||||
|
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||||
|
|
||||||
estree-walker@^0.8.1:
|
estree-walker@^0.8.1:
|
||||||
version "0.8.1"
|
version "0.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.8.1.tgz#6230ce2ec9a5cb03888afcaf295f97d90aa52b79"
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.8.1.tgz#6230ce2ec9a5cb03888afcaf295f97d90aa52b79"
|
||||||
integrity sha512-H6cJORkqvrNziu0KX2hqOMAlA2CiuAxHeGJXSIoKA/KLv229Dw806J3II6mKTm5xiDX1At1EXCfsOQPB+tMB+g==
|
integrity sha512-H6cJORkqvrNziu0KX2hqOMAlA2CiuAxHeGJXSIoKA/KLv229Dw806J3II6mKTm5xiDX1At1EXCfsOQPB+tMB+g==
|
||||||
|
|
||||||
|
estree-walker@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
|
||||||
|
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
|
||||||
|
|
||||||
esutils@^2.0.2:
|
esutils@^2.0.2:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||||
@ -7853,7 +7885,7 @@ performance-now@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||||
@ -8594,6 +8626,28 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
|||||||
hash-base "^3.0.0"
|
hash-base "^3.0.0"
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
|
|
||||||
|
rollup-plugin-babel@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb"
|
||||||
|
integrity sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-module-imports" "^7.0.0"
|
||||||
|
rollup-pluginutils "^2.8.1"
|
||||||
|
|
||||||
|
rollup-pluginutils@^2.8.1:
|
||||||
|
version "2.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||||
|
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
|
||||||
|
dependencies:
|
||||||
|
estree-walker "^0.6.1"
|
||||||
|
|
||||||
|
rollup@^2.13.1:
|
||||||
|
version "2.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.13.1.tgz#06ac5be4f85df0b79f23cdfa90de63e78095a984"
|
||||||
|
integrity sha512-EiICynxIO1DTFmFn+/98gfaqCToK2nbjPjHJLuNvpcwc+P035VrXmJxi3JsOhqkdty+0cOEhJ26ceGTY3UPMPQ==
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.1.2"
|
||||||
|
|
||||||
rsvp@^4.8.4:
|
rsvp@^4.8.4:
|
||||||
version "4.8.5"
|
version "4.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||||
|
Loading…
Reference in New Issue
Block a user