mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2025-07-02 10:03:25 +08:00
chore: add tslint (#37)
* chore: tslint * chore: workflow lint Co-authored-by: Amour1688 <lcz_1996@foxmail.com>
This commit is contained in:
@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"dev": "npm run build && webpack-dev-server",
|
||||
"build": "tsc",
|
||||
"lint": "eslint --ext .js src",
|
||||
"lint": "eslint 'src/*.ts'",
|
||||
"test": "npm run build && jest --coverage"
|
||||
},
|
||||
"bugs": {
|
||||
@ -27,6 +27,7 @@
|
||||
"@ant-design-vue/babel-helper-vue-transform-on": "^1.0.0",
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@babel/plugin-syntax-jsx": "^7.0.0",
|
||||
"@babel/traverse": "^7.0.0",
|
||||
"@babel/types": "^7.0.0",
|
||||
"camelcase": "^6.0.0",
|
||||
"html-tags": "^3.1.0",
|
||||
@ -37,6 +38,8 @@
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@rollup/plugin-babel": "^5.0.3",
|
||||
"@types/svg-tags": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
||||
"@typescript-eslint/parser": "^3.6.1",
|
||||
"@vue/compiler-dom": "3.0.0-rc.1",
|
||||
"@vue/test-utils": "2.0.0-beta.0",
|
||||
"babel-jest": "^26.0.1",
|
||||
|
@ -1,7 +1,9 @@
|
||||
import * as t from '@babel/types';
|
||||
import { NodePath } from '@babel/traverse';
|
||||
import { createIdentifier } from './utils';
|
||||
import { State, ExcludesBoolean } from './';
|
||||
import { State, ExcludesBoolean } from '.';
|
||||
|
||||
type Tag = t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression;
|
||||
|
||||
/**
|
||||
* Get JSX element type
|
||||
@ -17,9 +19,8 @@ const getType = (path: NodePath<t.JSXOpeningElement>) => {
|
||||
}
|
||||
return t.isJSXIdentifier(attribute.get('name'))
|
||||
&& (attribute.get('name') as NodePath<t.JSXIdentifier>).get('name') === 'type'
|
||||
&& t.isStringLiteral(attribute.get('value'))
|
||||
},
|
||||
);
|
||||
&& t.isStringLiteral(attribute.get('value'));
|
||||
});
|
||||
|
||||
return typePath ? typePath.get('value.value') : '';
|
||||
};
|
||||
@ -27,22 +28,22 @@ const getType = (path: NodePath<t.JSXOpeningElement>) => {
|
||||
const parseModifiers = (value: t.Expression) => {
|
||||
let modifiers: string[] = [];
|
||||
if (t.isArrayExpression(value)) {
|
||||
modifiers = (value as t.ArrayExpression).elements.map(el => t.isStringLiteral(el) ? el.value : '').filter(Boolean)
|
||||
modifiers = (value as t.ArrayExpression).elements.map((el) => (t.isStringLiteral(el) ? el.value : '')).filter(Boolean);
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
};
|
||||
|
||||
const parseDirectives = (args: {
|
||||
name: string,
|
||||
path: NodePath<t.JSXAttribute>,
|
||||
value: t.StringLiteral | t.Expression | null,
|
||||
state: State,
|
||||
tag: t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression,
|
||||
tag: Tag,
|
||||
isComponent: boolean
|
||||
}) => {
|
||||
const {
|
||||
name, path, value, state, tag, isComponent,
|
||||
} = args
|
||||
} = args;
|
||||
let modifiers: string[] = name.split('_');
|
||||
let arg;
|
||||
let val;
|
||||
@ -93,14 +94,19 @@ const parseDirectives = (args: {
|
||||
};
|
||||
};
|
||||
|
||||
const resolveDirective = (path: NodePath<t.JSXAttribute>, state: State, tag: any, directiveName: string) => {
|
||||
const resolveDirective = (
|
||||
path: NodePath<t.JSXAttribute>,
|
||||
state: State,
|
||||
tag: Tag,
|
||||
directiveName: string,
|
||||
) => {
|
||||
if (directiveName === 'show') {
|
||||
return createIdentifier(state, 'vShow');
|
||||
}
|
||||
if (directiveName === 'model') {
|
||||
let modelToUse;
|
||||
const type = getType(path.parentPath as NodePath<t.JSXOpeningElement>);
|
||||
switch (tag.value) {
|
||||
switch ((tag as t.StringLiteral).value) {
|
||||
case 'select':
|
||||
modelToUse = createIdentifier(state, 'vModelSelect');
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// https://github.com/vuejs/vue-next/blob/master/packages/shared/src/patchFlags.ts
|
||||
// tslint:disable: no-bitwise
|
||||
export const enum PatchFlags {
|
||||
TEXT = 1,
|
||||
CLASS = 1 << 1,
|
||||
@ -17,17 +18,17 @@ export const enum PatchFlags {
|
||||
|
||||
// dev only flag -> name mapping
|
||||
export 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.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
|
||||
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
|
||||
[PatchFlags.HOISTED]: `HOISTED`,
|
||||
[PatchFlags.BAIL]: `BAIL`
|
||||
}
|
||||
[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.DYNAMIC_SLOTS]: 'DYNAMIC_SLOTS',
|
||||
[PatchFlags.NEED_PATCH]: 'NEED_PATCH',
|
||||
[PatchFlags.HOISTED]: 'HOISTED',
|
||||
[PatchFlags.BAIL]: 'BAIL',
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as t from '@babel/types'
|
||||
import * as t from '@babel/types';
|
||||
import { addNamespace } from '@babel/helper-module-imports';
|
||||
import { NodePath } from '@babel/traverse';
|
||||
import { State } from './';
|
||||
import { State } from '.';
|
||||
|
||||
const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXMemberExpression) => {
|
||||
const children = path.get('children') || [];
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from './utils';
|
||||
import parseDirectives from './parseDirectives';
|
||||
import { PatchFlags, PatchFlagNames } from './patchFlags';
|
||||
import { State, ExcludesBoolean } from './';
|
||||
import { State, ExcludesBoolean } from '.';
|
||||
|
||||
const xlinkRE = /^xlink([A-Z])/;
|
||||
const onRE = /^on[^a-z]/;
|
||||
@ -24,7 +24,7 @@ const isOn = (key: string) => onRE.test(key);
|
||||
const transformJSXSpreadAttribute = (
|
||||
nodePath: NodePath,
|
||||
path: NodePath<t.JSXSpreadAttribute>,
|
||||
mergeArgs: (t.ObjectProperty | t.Expression)[]
|
||||
mergeArgs: (t.ObjectProperty | t.Expression)[],
|
||||
) => {
|
||||
const argument = path.get('argument') as NodePath<t.ObjectExpression>;
|
||||
const { properties } = argument.node;
|
||||
@ -40,7 +40,7 @@ const transformJSXSpreadAttribute = (
|
||||
|
||||
const getJSXAttributeValue = (
|
||||
path: NodePath<t.JSXAttribute>,
|
||||
state: State
|
||||
state: State,
|
||||
): (
|
||||
t.StringLiteral | t.Expression | null
|
||||
) => {
|
||||
@ -64,13 +64,13 @@ const getJSXAttributeValue = (
|
||||
* @returns boolean
|
||||
*/
|
||||
const isConstant = (
|
||||
node: t.Expression | t.Identifier | t.Literal | t.SpreadElement | null
|
||||
node: t.Expression | t.Identifier | t.Literal | t.SpreadElement | null,
|
||||
): boolean => {
|
||||
if (t.isIdentifier(node)) {
|
||||
return node.name === 'undefined';
|
||||
}
|
||||
if (t.isArrayExpression(node)) {
|
||||
const elements = node.elements;
|
||||
const { elements } = node;
|
||||
return elements.every((element) => element && isConstant(element));
|
||||
}
|
||||
if (t.isObjectExpression(node)) {
|
||||
@ -193,7 +193,9 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
||||
return;
|
||||
}
|
||||
if (isDirective(name)) {
|
||||
const { directive, modifiers, value, arg, directiveName } = parseDirectives({
|
||||
const {
|
||||
directive, modifiers, value, arg, directiveName,
|
||||
} = parseDirectives({
|
||||
tag,
|
||||
isComponent,
|
||||
name,
|
||||
@ -207,7 +209,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
||||
if (directiveName === 'slots') {
|
||||
slots = attributeValue;
|
||||
return;
|
||||
} else if (directive) {
|
||||
} if (directive) {
|
||||
directives.push(t.arrayExpression(directive));
|
||||
} else {
|
||||
// must be v-model and is a component
|
||||
@ -258,11 +260,16 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
||||
} else {
|
||||
// JSXSpreadAttribute
|
||||
hasDynamicKeys = true;
|
||||
transformJSXSpreadAttribute(path as NodePath, prop as NodePath<t.JSXSpreadAttribute>, mergeArgs);
|
||||
transformJSXSpreadAttribute(
|
||||
path as NodePath,
|
||||
prop as NodePath<t.JSXSpreadAttribute>,
|
||||
mergeArgs,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// patchFlag analysis
|
||||
// tslint:disable: no-bitwise
|
||||
if (hasDynamicKeys) {
|
||||
patchFlag |= PatchFlags.FULL_PROPS;
|
||||
} else {
|
||||
@ -342,49 +349,48 @@ const getChildren = (
|
||||
| t.JSXElement
|
||||
| t.JSXFragment
|
||||
>[],
|
||||
state: State
|
||||
): t.Expression[] =>
|
||||
paths
|
||||
.map((path) => {
|
||||
if (path.isJSXText()) {
|
||||
const transformedText = transformJSXText(path);
|
||||
if (transformedText) {
|
||||
return t.callExpression(createIdentifier(state, 'createTextVNode'), [transformedText]);
|
||||
}
|
||||
return transformedText;
|
||||
state: State,
|
||||
): t.Expression[] => paths
|
||||
.map((path) => {
|
||||
if (path.isJSXText()) {
|
||||
const transformedText = transformJSXText(path);
|
||||
if (transformedText) {
|
||||
return t.callExpression(createIdentifier(state, 'createTextVNode'), [transformedText]);
|
||||
}
|
||||
if (path.isJSXExpressionContainer()) {
|
||||
const expression = transformJSXExpressionContainer(path);
|
||||
return transformedText;
|
||||
}
|
||||
if (path.isJSXExpressionContainer()) {
|
||||
const expression = transformJSXExpressionContainer(path);
|
||||
|
||||
if (t.isIdentifier(expression)) {
|
||||
const { name } = expression as t.Identifier;
|
||||
const { referencePaths } = path.scope.getBinding(name) || {};
|
||||
referencePaths?.forEach(referencePath => {
|
||||
walksScope(referencePath, name);
|
||||
})
|
||||
}
|
||||
|
||||
return expression;
|
||||
if (t.isIdentifier(expression)) {
|
||||
const { name } = expression as t.Identifier;
|
||||
const { referencePaths = [] } = path.scope.getBinding(name) || {};
|
||||
referencePaths.forEach((referencePath) => {
|
||||
walksScope(referencePath, name);
|
||||
});
|
||||
}
|
||||
if (t.isJSXSpreadChild(path)) {
|
||||
return transformJSXSpreadChild(path as NodePath<t.JSXSpreadChild>);
|
||||
}
|
||||
if (path.isCallExpression()) {
|
||||
return path.node;
|
||||
}
|
||||
if (path.isJSXElement()) {
|
||||
return transformJSXElement(path, state);
|
||||
}
|
||||
throw new Error(`getChildren: ${path.type} is not supported`);
|
||||
}).filter(((value: any) => (
|
||||
value !== undefined
|
||||
|
||||
return expression;
|
||||
}
|
||||
if (t.isJSXSpreadChild(path)) {
|
||||
return transformJSXSpreadChild(path as NodePath<t.JSXSpreadChild>);
|
||||
}
|
||||
if (path.isCallExpression()) {
|
||||
return path.node;
|
||||
}
|
||||
if (path.isJSXElement()) {
|
||||
return transformJSXElement(path, state);
|
||||
}
|
||||
throw new Error(`getChildren: ${path.type} is not supported`);
|
||||
}).filter(((value: any) => (
|
||||
value !== undefined
|
||||
&& value !== null
|
||||
&& !t.isJSXEmptyExpression(value)
|
||||
)) as any);
|
||||
)) as any);
|
||||
|
||||
const transformJSXElement = (
|
||||
path: NodePath<t.JSXElement>,
|
||||
state: State
|
||||
state: State,
|
||||
): t.CallExpression => {
|
||||
const children = getChildren(path.get('children'), state);
|
||||
const {
|
||||
@ -394,7 +400,7 @@ const transformJSXElement = (
|
||||
directives,
|
||||
patchFlag,
|
||||
dynamicPropNames,
|
||||
slots
|
||||
slots,
|
||||
} = buildProps(path, state);
|
||||
|
||||
const useOptimate = path.getData('optimize') !== false;
|
||||
@ -420,16 +426,16 @@ const transformJSXElement = (
|
||||
(children.length || slots) ? (
|
||||
isComponent
|
||||
? t.objectExpression([
|
||||
!!children.length && t.objectProperty(
|
||||
t.identifier('default'),
|
||||
t.arrowFunctionExpression([], t.arrayExpression(children))
|
||||
),
|
||||
...(slots ? (
|
||||
t.isObjectExpression(slots)
|
||||
? (slots as any as t.ObjectExpression).properties
|
||||
: [t.spreadElement(slots as any)]
|
||||
) : [])
|
||||
].filter(Boolean as any as ExcludesBoolean))
|
||||
!!children.length && t.objectProperty(
|
||||
t.identifier('default'),
|
||||
t.arrowFunctionExpression([], t.arrayExpression(children)),
|
||||
),
|
||||
...(slots ? (
|
||||
t.isObjectExpression(slots)
|
||||
? (slots as any as t.ObjectExpression).properties
|
||||
: [t.spreadElement(slots as any)]
|
||||
) : []),
|
||||
].filter(Boolean as any as ExcludesBoolean))
|
||||
: t.arrayExpression(children)
|
||||
) : t.nullLiteral(),
|
||||
!!patchFlag && usePatchFlag && (
|
||||
@ -438,7 +444,9 @@ const transformJSXElement = (
|
||||
: t.numericLiteral(PatchFlags.BAIL)
|
||||
),
|
||||
!!dynamicPropNames.size && usePatchFlag
|
||||
&& t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))),
|
||||
&& t.arrayExpression(
|
||||
[...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string)),
|
||||
),
|
||||
].filter(Boolean as any as ExcludesBoolean));
|
||||
|
||||
if (!directives.length) {
|
||||
|
@ -2,16 +2,16 @@ import * as t from '@babel/types';
|
||||
import htmlTags from 'html-tags';
|
||||
import svgTags from 'svg-tags';
|
||||
import { NodePath } from '@babel/traverse';
|
||||
import { State } from './';
|
||||
import { State } from '.';
|
||||
|
||||
/**
|
||||
* create Identifier
|
||||
* @param state
|
||||
* @param id string
|
||||
* @returns MemberExpression
|
||||
* @param id string
|
||||
* @returns MemberExpression
|
||||
*/
|
||||
const createIdentifier = (
|
||||
state: State, id: string
|
||||
state: State, id: string,
|
||||
): t.MemberExpression => t.memberExpression(state.get('vue'), t.identifier(id));
|
||||
|
||||
/**
|
||||
@ -26,8 +26,10 @@ const isDirective = (src: string): boolean => src.startsWith('v-')
|
||||
* @param {*} path JSXIdentifier | JSXMemberExpression | JSXNamespacedName
|
||||
* @returns boolean
|
||||
*/
|
||||
const isFragment = (path: NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>) =>
|
||||
t.isJSXMemberExpression(path)
|
||||
const isFragment = (
|
||||
path:
|
||||
NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>,
|
||||
): boolean => t.isJSXMemberExpression(path)
|
||||
&& (path.node as t.JSXMemberExpression).property.name === 'Fragment';
|
||||
|
||||
/**
|
||||
@ -54,7 +56,9 @@ const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => {
|
||||
* @param path JSXMemberExpression
|
||||
* @returns MemberExpression
|
||||
*/
|
||||
const transformJSXMemberExpression = (path: NodePath<t.JSXMemberExpression>): t.MemberExpression => {
|
||||
const transformJSXMemberExpression = (
|
||||
path: NodePath<t.JSXMemberExpression>,
|
||||
): t.MemberExpression => {
|
||||
const objectPath = path.node.object;
|
||||
const propertyPath = path.node.property;
|
||||
const transformedObject = t.isJSXMemberExpression(objectPath)
|
||||
@ -70,9 +74,12 @@ const transformJSXMemberExpression = (path: NodePath<t.JSXMemberExpression>): t.
|
||||
* Get tag (first attribute for h) from JSXOpeningElement
|
||||
* @param path JSXElement
|
||||
* @param state State
|
||||
* @returns Identifier | StringLiteral | MemberExpression
|
||||
* @returns Identifier | StringLiteral | MemberExpression | CallExpression
|
||||
*/
|
||||
const getTag = (path: NodePath<t.JSXElement>, state: State) => {
|
||||
const getTag = (
|
||||
path: NodePath<t.JSXElement>,
|
||||
state: State,
|
||||
): t.Identifier | t.CallExpression | t.StringLiteral | t.MemberExpression => {
|
||||
const namePath = path.get('openingElement').get('name');
|
||||
if (namePath.isJSXIdentifier()) {
|
||||
const { name } = namePath.node;
|
||||
@ -157,8 +164,9 @@ const transformJSXText = (path: NodePath<t.JSXText>): t.StringLiteral | null =>
|
||||
* @returns Expression
|
||||
*/
|
||||
const transformJSXExpressionContainer = (
|
||||
path: NodePath<t.JSXExpressionContainer>
|
||||
): (t.Expression) => path.get('expression').node as t.Expression;
|
||||
path: NodePath<t.JSXExpressionContainer>,
|
||||
): (t.Expression
|
||||
) => path.get('expression').node as t.Expression;
|
||||
|
||||
/**
|
||||
* Transform JSXSpreadChild
|
||||
@ -166,17 +174,17 @@ const transformJSXExpressionContainer = (
|
||||
* @returns SpreadElement
|
||||
*/
|
||||
const transformJSXSpreadChild = (
|
||||
path: NodePath<t.JSXSpreadChild>
|
||||
path: NodePath<t.JSXSpreadChild>,
|
||||
): t.SpreadElement => t.spreadElement(path.get('expression').node);
|
||||
|
||||
const walksScope = (path: NodePath, name: string) => {
|
||||
const walksScope = (path: NodePath, name: string): void => {
|
||||
if (path.scope.hasBinding(name) && path.parentPath) {
|
||||
if (t.isJSXElement(path.parentPath.node)) {
|
||||
path.parentPath.setData('optimize', false);
|
||||
}
|
||||
walksScope(path.parentPath, name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createIdentifier,
|
||||
|
Reference in New Issue
Block a user