chore: add tslint (#37)

* chore: tslint

* chore: workflow lint

Co-authored-by: Amour1688 <lcz_1996@foxmail.com>
This commit is contained in:
xrk
2020-07-19 14:47:24 +08:00
committed by GitHub
parent 6b0b7534a4
commit ee0d544c24
10 changed files with 321 additions and 106 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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',
};

View File

@ -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') || [];

View File

@ -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) {

View File

@ -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,