chore: use airbnb-typescript/base eslint plugin (#420)

This commit is contained in:
Amour1688 2021-04-30 18:29:48 +08:00 committed by GitHub
parent 4cb4d7fcdf
commit a7121f8553
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 541 additions and 479 deletions

View File

@ -24,5 +24,7 @@ jobs:
- ~/.cache/yarn - ~/.cache/yarn
key: v2-dependencies-{{ checksum "yarn.lock" }} key: v2-dependencies-{{ checksum "yarn.lock" }}
- run: yarn lint
# run tests! # run tests!
- run: yarn test - run: yarn test

View File

@ -5,6 +5,7 @@ module.exports = {
ecmaFeatures: { ecmaFeatures: {
jsx: true, jsx: true,
}, },
project: './tsconfig.json',
}, },
env: { env: {
browser: true, browser: true,
@ -15,8 +16,7 @@ module.exports = {
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'import'], plugins: ['@typescript-eslint', 'import'],
extends: [ extends: [
'eslint-config-airbnb-base', 'airbnb-typescript/base',
'plugin:@typescript-eslint/recommended',
], ],
rules: { rules: {
'no-nested-ternary': [0], 'no-nested-ternary': [0],
@ -27,19 +27,8 @@ module.exports = {
'import/no-extraneous-dependencies': [0], 'import/no-extraneous-dependencies': [0],
'consistent-return': [0], 'consistent-return': [0],
'no-bitwise': [0], 'no-bitwise': [0],
'@typescript-eslint/no-use-before-define': [0],
'prefer-destructuring': [2, { array: false }], 'prefer-destructuring': [2, { array: false }],
'import/extensions': [0],
'@typescript-eslint/ban-ts-comment': [0],
'@typescript-eslint/explicit-module-boundary-types': [0],
'@typescript-eslint/no-explicit-any': [0],
'@typescript-eslint/no-non-null-assertion': [0],
'max-len': [0], 'max-len': [0],
}, },
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
}; };

View File

@ -16,9 +16,10 @@
"jsx" "jsx"
], ],
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.4.1",
"eslint": "^7.7.0", "eslint": "^7.7.0",
"eslint-config-airbnb-base": "^14.1.0", "eslint-config-airbnb-typescript": "^12.3.1",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.22.1",
"lerna": "^3.19.0" "lerna": "^3.19.0"
} }
} }

View File

@ -1,382 +0,0 @@
import * as t from '@babel/types';
import { NodePath } from '@babel/traverse';
import { addDefault } from '@babel/helper-module-imports';
import {
createIdentifier,
isDirective,
checkIsComponent,
getTag,
getJSXAttributeName,
walksScope,
transformJSXExpressionContainer,
} from './utils';
import parseDirectives from './parseDirectives';
import { PatchFlags } from './patchFlags';
import { State } from '.';
import { transformJSXElement } from './transform-vue-jsx';
import SlotFlags from './slotFlags';
const xlinkRE = /^xlink([A-Z])/;
const onRE = /^on[^a-z]/;
const isOn = (key: string) => onRE.test(key);
export type Slots = t.Identifier | t.ObjectExpression | null;
const getJSXAttributeValue = (
path: NodePath<t.JSXAttribute>,
state: State,
): (
t.StringLiteral | t.Expression | null
) => {
const valuePath = path.get('value');
if (valuePath.isJSXElement()) {
return transformJSXElement(valuePath, state);
}
if (valuePath.isStringLiteral()) {
return valuePath.node;
}
if (valuePath.isJSXExpressionContainer()) {
return transformJSXExpressionContainer(valuePath);
}
return null;
};
const transformJSXSpreadAttribute = (
nodePath: NodePath,
path: NodePath<t.JSXSpreadAttribute>,
mergeProps: boolean,
args: (t.ObjectProperty | t.Expression | t.SpreadElement)[],
) => {
const argument = path.get('argument') as NodePath<t.ObjectExpression | t.Identifier>;
const properties = t.isObjectExpression(argument.node) ? argument.node.properties : undefined;
if (!properties) {
if (argument.isIdentifier()) {
walksScope(nodePath, (argument.node as t.Identifier).name, SlotFlags.DYNAMIC);
}
args.push(mergeProps ? argument.node : t.spreadElement(argument.node));
} else if (mergeProps) {
args.push(t.objectExpression(properties));
} else {
args.push(...(properties as t.ObjectProperty[]));
}
};
const mergeAsArray = (existing: t.ObjectProperty, incoming: t.ObjectProperty) => {
if (t.isArrayExpression(existing.value)) {
existing.value.elements.push(incoming.value as t.Expression);
} else {
existing.value = t.arrayExpression([
existing.value as t.Expression,
incoming.value as t.Expression,
]);
}
};
const dedupeProperties = (properties: t.ObjectProperty[] = [], mergeProps?: boolean) => {
if (!mergeProps) {
return properties;
}
const knownProps = new Map<string, t.ObjectProperty>();
const deduped: t.ObjectProperty[] = [];
properties.forEach((prop) => {
if (t.isStringLiteral(prop.key)) {
const { value: name } = prop.key;
const existing = knownProps.get(name);
if (existing) {
if (name === 'style' || name === 'class' || name.startsWith('on')) {
mergeAsArray(existing, prop);
}
} else {
knownProps.set(name, prop);
deduped.push(prop);
}
} else {
// v-model target with variable
deduped.push(prop);
}
});
return deduped;
};
/**
* Check if an attribute value is constant
* @param node
* @returns boolean
*/
const isConstant = (
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;
return elements.every((element) => element && isConstant(element));
}
if (t.isObjectExpression(node)) {
return node.properties.every((property) => isConstant((property as any).value));
}
if (t.isLiteral(node)) {
return true;
}
return false;
};
const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
const tag = getTag(path, state);
const isComponent = checkIsComponent(path.get('openingElement'));
const props = path.get('openingElement').get('attributes');
const directives: t.ArrayExpression[] = [];
const dynamicPropNames = new Set<string>();
let slots: Slots = null;
let patchFlag = 0;
if (props.length === 0) {
return {
tag,
isComponent,
slots,
props: t.nullLiteral(),
directives,
patchFlag,
dynamicPropNames,
};
}
let properties: t.ObjectProperty[] = [];
// patchFlag analysis
let hasRef = false;
let hasClassBinding = false;
let hasStyleBinding = false;
let hasHydrationEventBinding = false;
let hasDynamicKeys = false;
const mergeArgs: (t.CallExpression | t.ObjectExpression | t.Identifier)[] = [];
const { mergeProps = true } = state.opts;
props
.forEach((prop) => {
if (prop.isJSXAttribute()) {
let name = getJSXAttributeName(prop);
const attributeValue = getJSXAttributeValue(prop, state);
if (!isConstant(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.add(name);
}
}
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
if (!state.get('transformOn')) {
state.set('transformOn', addDefault(
path,
'@vue/babel-helper-vue-transform-on',
{ nameHint: '_transformOn' },
));
}
mergeArgs.push(t.callExpression(
state.get('transformOn'),
[attributeValue || t.booleanLiteral(true)],
));
return;
}
if (isDirective(name)) {
const {
directive, modifiers, values, args, directiveName,
} = parseDirectives({
tag,
isComponent,
name,
path: prop,
state,
value: attributeValue,
});
if (directiveName === 'slots') {
slots = attributeValue as Slots;
return;
}
if (directive) {
directives.push(t.arrayExpression(directive));
} else if (directiveName === 'html') {
properties.push(t.objectProperty(
t.stringLiteral('innerHTML'),
values[0] as any,
));
dynamicPropNames.add('innerHTML');
} else if (directiveName === 'text') {
properties.push(t.objectProperty(
t.stringLiteral('textContent'),
values[0] as any,
));
dynamicPropNames.add('textContent');
}
if (['models', 'model'].includes(directiveName)) {
values.forEach((value, index) => {
const propName = args[index];
// v-model target with variable
const isDynamic = propName && !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
// must be v-model or v-models and is a component
if (!directive) {
properties.push(
t.objectProperty(t.isNullLiteral(propName)
? t.stringLiteral('modelValue') : propName, value as any, isDynamic),
);
if (!isDynamic) {
dynamicPropNames.add((propName as t.StringLiteral)?.value || 'modelValue');
}
if (modifiers[index]?.size) {
properties.push(
t.objectProperty(
isDynamic
? t.binaryExpression('+', propName, t.stringLiteral('Modifiers'))
: t.stringLiteral(`${(propName as t.StringLiteral)?.value || 'model'}Modifiers`),
t.objectExpression(
[...modifiers[index]].map((modifier) => t.objectProperty(
t.stringLiteral(modifier),
t.booleanLiteral(true),
)),
),
isDynamic,
),
);
}
}
const updateName = isDynamic
? t.binaryExpression('+', t.stringLiteral('onUpdate'), propName)
: t.stringLiteral(`onUpdate:${(propName as t.StringLiteral)?.value || 'modelValue'}`);
properties.push(
t.objectProperty(
updateName,
t.arrowFunctionExpression(
[t.identifier('$event')],
t.assignmentExpression('=', value as any, t.identifier('$event')),
),
isDynamic,
),
);
if (!isDynamic) {
dynamicPropNames.add((updateName as t.StringLiteral).value);
} else {
hasDynamicKeys = true;
}
});
}
} else {
if (name.match(xlinkRE)) {
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
}
properties.push(t.objectProperty(
t.stringLiteral(name),
attributeValue || t.booleanLiteral(true),
));
}
} else {
if (properties.length && mergeProps) {
mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
properties = [];
}
// JSXSpreadAttribute
hasDynamicKeys = true;
transformJSXSpreadAttribute(
path as NodePath,
prop as NodePath<t.JSXSpreadAttribute>,
mergeProps,
mergeProps ? mergeArgs : properties,
);
}
});
// patchFlag analysis
if (hasDynamicKeys) {
patchFlag |= PatchFlags.FULL_PROPS;
} else {
if (hasClassBinding) {
patchFlag |= PatchFlags.CLASS;
}
if (hasStyleBinding) {
patchFlag |= PatchFlags.STYLE;
}
if (dynamicPropNames.size) {
patchFlag |= PatchFlags.PROPS;
}
if (hasHydrationEventBinding) {
patchFlag |= PatchFlags.HYDRATE_EVENTS;
}
}
if (
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS)
&& (hasRef || directives.length > 0)
) {
patchFlag |= PatchFlags.NEED_PATCH;
}
let propsExpression: t.Expression | t.ObjectProperty | t.Literal = t.nullLiteral();
if (mergeArgs.length) {
if (properties.length) {
mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
}
if (mergeArgs.length > 1) {
propsExpression = t.callExpression(
createIdentifier(state, 'mergeProps'),
mergeArgs,
);
} else {
// single no need for a mergeProps call
propsExpression = mergeArgs[0];
}
} else if (properties.length) {
// single no need for spread
if (properties.length === 1 && t.isSpreadElement(properties[0])) {
propsExpression = (properties[0] as unknown as t.SpreadElement).argument;
} else {
propsExpression = t.objectExpression(dedupeProperties(properties, mergeProps));
}
}
return {
tag,
props: propsExpression,
isComponent,
slots,
directives,
patchFlag,
dynamicPropNames,
};
};
export default buildProps;

View File

@ -6,30 +6,9 @@ import { addNamed, isModule, addNamespace } from '@babel/helper-module-imports';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import transformVueJSX from './transform-vue-jsx'; import transformVueJSX from './transform-vue-jsx';
import sugarFragment from './sugar-fragment'; import sugarFragment from './sugar-fragment';
import type { VueJSXPluginOptions, State } from './interface';
export type State = { export { VueJSXPluginOptions };
get: (name: string) => any;
set: (name: string, value: any) => any;
opts: VueJSXPluginOptions;
file: BabelCore.BabelFile
}
export interface VueJSXPluginOptions {
/** transform `on: { click: xx }` to `onClick: xxx` */
transformOn?: boolean;
/** enable optimization or not. */
optimize?: boolean;
/** merge static and dynamic class / style attributes / onXXX handlers */
mergeProps?: boolean;
/** configuring custom elements */
isCustomElement?: (tag: string) => boolean;
/** enable object slots syntax */
enableObjectSlots?: boolean;
/** Replace the function used when compiling JSX expressions */
pragma?: string;
}
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
const hasJSX = (parentPath: NodePath<t.Program>) => { const hasJSX = (parentPath: NodePath<t.Program>) => {
let fileHasJSX = false; let fileHasJSX = false;

View File

@ -0,0 +1,28 @@
import * as t from '@babel/types';
import * as BabelCore from '@babel/core';
export type Slots = t.Identifier | t.ObjectExpression | null;
export type State = {
get: (name: string) => any;
set: (name: string, value: any) => any;
opts: VueJSXPluginOptions;
file: BabelCore.BabelFile
};
export interface VueJSXPluginOptions {
/** transform `on: { click: xx }` to `onClick: xxx` */
transformOn?: boolean;
/** enable optimization or not. */
optimize?: boolean;
/** merge static and dynamic class / style attributes / onXXX handlers */
mergeProps?: boolean;
/** configuring custom elements */
isCustomElement?: (tag: string) => boolean;
/** enable object slots syntax */
enableObjectSlots?: boolean;
/** Replace the function used when compiling JSX expressions */
pragma?: string;
}
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;

View File

@ -1,7 +1,7 @@
import * as t from '@babel/types'; import * as t from '@babel/types';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import { createIdentifier } from './utils'; import { createIdentifier } from './utils';
import { State } from '.'; import type { State } from './interface';
export type Tag = t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression; export type Tag = t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression;

View File

@ -13,7 +13,7 @@ export const enum PatchFlags {
NEED_PATCH = 1 << 9, NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10, DYNAMIC_SLOTS = 1 << 10,
HOISTED = -1, HOISTED = -1,
BAIL = -2 BAIL = -2,
} }
// dev only flag -> name mapping // dev only flag -> name mapping

View File

@ -18,7 +18,7 @@ const enum SlotFlags {
* received. This has to be refined at runtime, when the child's vnode * received. This has to be refined at runtime, when the child's vnode
* is being created (in `normalizeChildren`) * is being created (in `normalizeChildren`)
*/ */
FORWARDED = 3 FORWARDED = 3,
} }
export default SlotFlags; export default SlotFlags;

View File

@ -1,6 +1,6 @@
import * as t from '@babel/types'; import * as t from '@babel/types';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import { State } from '.'; import type { State } from './interface';
import { createIdentifier, FRAGMENT } from './utils'; import { createIdentifier, FRAGMENT } from './utils';
const transformFragment = ( const transformFragment = (

View File

@ -1,5 +1,6 @@
import * as t from '@babel/types'; import * as t from '@babel/types';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import { addDefault } from '@babel/helper-module-imports';
import { import {
createIdentifier, createIdentifier,
transformJSXSpreadChild, transformJSXSpreadChild,
@ -7,10 +8,295 @@ import {
transformJSXExpressionContainer, transformJSXExpressionContainer,
walksScope, walksScope,
buildIIFE, buildIIFE,
isDirective,
checkIsComponent,
getTag,
getJSXAttributeName,
isOn,
isConstant,
dedupeProperties,
transformJSXSpreadAttribute,
} from './utils'; } from './utils';
import buildProps from './buildProps';
import SlotFlags from './slotFlags'; import SlotFlags from './slotFlags';
import { State, ExcludesBoolean } from '.'; import { PatchFlags } from './patchFlags';
import parseDirectives from './parseDirectives';
import type { State, ExcludesBoolean, Slots } from './interface';
const xlinkRE = /^xlink([A-Z])/;
const getJSXAttributeValue = (
path: NodePath<t.JSXAttribute>,
state: State,
): (
t.StringLiteral | t.Expression | null
) => {
const valuePath = path.get('value');
if (valuePath.isJSXElement()) {
return transformJSXElement(valuePath, state);
}
if (valuePath.isStringLiteral()) {
return valuePath.node;
}
if (valuePath.isJSXExpressionContainer()) {
return transformJSXExpressionContainer(valuePath);
}
return null;
};
const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
const tag = getTag(path, state);
const isComponent = checkIsComponent(path.get('openingElement'));
const props = path.get('openingElement').get('attributes');
const directives: t.ArrayExpression[] = [];
const dynamicPropNames = new Set<string>();
let slots: Slots = null;
let patchFlag = 0;
if (props.length === 0) {
return {
tag,
isComponent,
slots,
props: t.nullLiteral(),
directives,
patchFlag,
dynamicPropNames,
};
}
let properties: t.ObjectProperty[] = [];
// patchFlag analysis
let hasRef = false;
let hasClassBinding = false;
let hasStyleBinding = false;
let hasHydrationEventBinding = false;
let hasDynamicKeys = false;
const mergeArgs: (t.CallExpression | t.ObjectExpression | t.Identifier)[] = [];
const { mergeProps = true } = state.opts;
props
.forEach((prop) => {
if (prop.isJSXAttribute()) {
let name = getJSXAttributeName(prop);
const attributeValue = getJSXAttributeValue(prop, state);
if (!isConstant(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.add(name);
}
}
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
if (!state.get('transformOn')) {
state.set('transformOn', addDefault(
path,
'@vue/babel-helper-vue-transform-on',
{ nameHint: '_transformOn' },
));
}
mergeArgs.push(t.callExpression(
state.get('transformOn'),
[attributeValue || t.booleanLiteral(true)],
));
return;
}
if (isDirective(name)) {
const {
directive, modifiers, values, args, directiveName,
} = parseDirectives({
tag,
isComponent,
name,
path: prop,
state,
value: attributeValue,
});
if (directiveName === 'slots') {
slots = attributeValue as Slots;
return;
}
if (directive) {
directives.push(t.arrayExpression(directive));
} else if (directiveName === 'html') {
properties.push(t.objectProperty(
t.stringLiteral('innerHTML'),
values[0] as any,
));
dynamicPropNames.add('innerHTML');
} else if (directiveName === 'text') {
properties.push(t.objectProperty(
t.stringLiteral('textContent'),
values[0] as any,
));
dynamicPropNames.add('textContent');
}
if (['models', 'model'].includes(directiveName)) {
values.forEach((value, index) => {
const propName = args[index];
// v-model target with variable
const isDynamic = propName && !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
// must be v-model or v-models and is a component
if (!directive) {
properties.push(
t.objectProperty(t.isNullLiteral(propName)
? t.stringLiteral('modelValue') : propName, value as any, isDynamic),
);
if (!isDynamic) {
dynamicPropNames.add((propName as t.StringLiteral)?.value || 'modelValue');
}
if (modifiers[index]?.size) {
properties.push(
t.objectProperty(
isDynamic
? t.binaryExpression('+', propName, t.stringLiteral('Modifiers'))
: t.stringLiteral(`${(propName as t.StringLiteral)?.value || 'model'}Modifiers`),
t.objectExpression(
[...modifiers[index]].map((modifier) => t.objectProperty(
t.stringLiteral(modifier),
t.booleanLiteral(true),
)),
),
isDynamic,
),
);
}
}
const updateName = isDynamic
? t.binaryExpression('+', t.stringLiteral('onUpdate'), propName)
: t.stringLiteral(`onUpdate:${(propName as t.StringLiteral)?.value || 'modelValue'}`);
properties.push(
t.objectProperty(
updateName,
t.arrowFunctionExpression(
[t.identifier('$event')],
t.assignmentExpression('=', value as any, t.identifier('$event')),
),
isDynamic,
),
);
if (!isDynamic) {
dynamicPropNames.add((updateName as t.StringLiteral).value);
} else {
hasDynamicKeys = true;
}
});
}
} else {
if (name.match(xlinkRE)) {
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
}
properties.push(t.objectProperty(
t.stringLiteral(name),
attributeValue || t.booleanLiteral(true),
));
}
} else {
if (properties.length && mergeProps) {
mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
properties = [];
}
// JSXSpreadAttribute
hasDynamicKeys = true;
transformJSXSpreadAttribute(
path as NodePath,
prop as NodePath<t.JSXSpreadAttribute>,
mergeProps,
mergeProps ? mergeArgs : properties,
);
}
});
// patchFlag analysis
if (hasDynamicKeys) {
patchFlag |= PatchFlags.FULL_PROPS;
} else {
if (hasClassBinding) {
patchFlag |= PatchFlags.CLASS;
}
if (hasStyleBinding) {
patchFlag |= PatchFlags.STYLE;
}
if (dynamicPropNames.size) {
patchFlag |= PatchFlags.PROPS;
}
if (hasHydrationEventBinding) {
patchFlag |= PatchFlags.HYDRATE_EVENTS;
}
}
if (
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS)
&& (hasRef || directives.length > 0)
) {
patchFlag |= PatchFlags.NEED_PATCH;
}
let propsExpression: t.Expression | t.ObjectProperty | t.Literal = t.nullLiteral();
if (mergeArgs.length) {
if (properties.length) {
mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
}
if (mergeArgs.length > 1) {
propsExpression = t.callExpression(
createIdentifier(state, 'mergeProps'),
mergeArgs,
);
} else {
// single no need for a mergeProps call
propsExpression = mergeArgs[0];
}
} else if (properties.length) {
// single no need for spread
if (properties.length === 1 && t.isSpreadElement(properties[0])) {
propsExpression = (properties[0] as unknown as t.SpreadElement).argument;
} else {
propsExpression = t.objectExpression(dedupeProperties(properties, mergeProps));
}
}
return {
tag,
props: propsExpression,
isComponent,
slots,
directives,
patchFlag,
dynamicPropNames,
};
};
/** /**
* Get children from Array of JSX children * Get children from Array of JSX children
@ -211,8 +497,6 @@ const transformJSXElement = (
]); ]);
}; };
export { transformJSXElement };
export default ({ export default ({
JSXElement: { JSXElement: {
exit(path: NodePath<t.JSXElement>, state: State) { exit(path: NodePath<t.JSXElement>, state: State) {

View File

@ -2,12 +2,12 @@ import * as t from '@babel/types';
import htmlTags from 'html-tags'; import htmlTags from 'html-tags';
import svgTags from 'svg-tags'; import svgTags from 'svg-tags';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import { State } from '.'; import type { State } from './interface';
import SlotFlags from './slotFlags'; import SlotFlags from './slotFlags';
const JSX_HELPER_KEY = 'JSX_HELPER_KEY'; export const JSX_HELPER_KEY = 'JSX_HELPER_KEY';
const FRAGMENT = 'Fragment'; export const FRAGMENT = 'Fragment';
const KEEP_ALIVE = 'KeepAlive'; export const KEEP_ALIVE = 'KeepAlive';
/** /**
* create Identifier * create Identifier
@ -16,7 +16,7 @@ const KEEP_ALIVE = 'KeepAlive';
* @param name string * @param name string
* @returns MemberExpression * @returns MemberExpression
*/ */
const createIdentifier = ( export const createIdentifier = (
state: State, name: string, state: State, name: string,
): t.Identifier | t.MemberExpression => state.get(name)(); ): t.Identifier | t.MemberExpression => state.get(name)();
@ -24,7 +24,7 @@ const createIdentifier = (
* Checks if string is describing a directive * Checks if string is describing a directive
* @param src string * @param src string
*/ */
const isDirective = (src: string): boolean => src.startsWith('v-') export const isDirective = (src: string): boolean => src.startsWith('v-')
|| (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z'); || (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z');
/** /**
@ -32,7 +32,7 @@ const isDirective = (src: string): boolean => src.startsWith('v-')
* @param tag string * @param tag string
* @returns boolean * @returns boolean
*/ */
const shouldTransformedToSlots = (tag: string) => !(tag.endsWith(FRAGMENT) || tag === KEEP_ALIVE); export const shouldTransformedToSlots = (tag: string) => !(tag.endsWith(FRAGMENT) || tag === KEEP_ALIVE);
/** /**
* Check if a Node is a component * Check if a Node is a component
@ -41,7 +41,7 @@ const shouldTransformedToSlots = (tag: string) => !(tag.endsWith(FRAGMENT) || ta
* @param path JSXOpeningElement * @param path JSXOpeningElement
* @returns boolean * @returns boolean
*/ */
const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => { export const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => {
const namePath = path.get('name'); const namePath = path.get('name');
if (namePath.isJSXMemberExpression()) { if (namePath.isJSXMemberExpression()) {
@ -58,7 +58,7 @@ const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => {
* @param path JSXMemberExpression * @param path JSXMemberExpression
* @returns MemberExpression * @returns MemberExpression
*/ */
const transformJSXMemberExpression = ( export const transformJSXMemberExpression = (
path: NodePath<t.JSXMemberExpression>, path: NodePath<t.JSXMemberExpression>,
): t.MemberExpression => { ): t.MemberExpression => {
const objectPath = path.node.object; const objectPath = path.node.object;
@ -78,7 +78,7 @@ const transformJSXMemberExpression = (
* @param state State * @param state State
* @returns Identifier | StringLiteral | MemberExpression | CallExpression * @returns Identifier | StringLiteral | MemberExpression | CallExpression
*/ */
const getTag = ( export const getTag = (
path: NodePath<t.JSXElement>, path: NodePath<t.JSXElement>,
state: State, state: State,
): t.Identifier | t.CallExpression | t.StringLiteral | t.MemberExpression => { ): t.Identifier | t.CallExpression | t.StringLiteral | t.MemberExpression => {
@ -104,7 +104,7 @@ const getTag = (
throw new Error(`getTag: ${namePath.type} is not supported`); throw new Error(`getTag: ${namePath.type} is not supported`);
}; };
const getJSXAttributeName = (path: NodePath<t.JSXAttribute>): string => { export const getJSXAttributeName = (path: NodePath<t.JSXAttribute>): string => {
const nameNode = path.node.name; const nameNode = path.node.name;
if (t.isJSXIdentifier(nameNode)) { if (t.isJSXIdentifier(nameNode)) {
return nameNode.name; return nameNode.name;
@ -118,7 +118,7 @@ const getJSXAttributeName = (path: NodePath<t.JSXAttribute>): string => {
* @param path JSXText * @param path JSXText
* @returns StringLiteral | null * @returns StringLiteral | null
*/ */
const transformJSXText = (path: NodePath<t.JSXText>): t.StringLiteral | null => { export const transformJSXText = (path: NodePath<t.JSXText>): t.StringLiteral | null => {
const { node } = path; const { node } = path;
const lines = node.value.split(/\r\n|\n|\r/); const lines = node.value.split(/\r\n|\n|\r/);
@ -169,7 +169,7 @@ const transformJSXText = (path: NodePath<t.JSXText>): t.StringLiteral | null =>
* @param path JSXExpressionContainer * @param path JSXExpressionContainer
* @returns Expression * @returns Expression
*/ */
const transformJSXExpressionContainer = ( export const transformJSXExpressionContainer = (
path: NodePath<t.JSXExpressionContainer>, path: NodePath<t.JSXExpressionContainer>,
): ( ): (
t.Expression t.Expression
@ -180,11 +180,11 @@ const transformJSXExpressionContainer = (
* @param path JSXSpreadChild * @param path JSXSpreadChild
* @returns SpreadElement * @returns SpreadElement
*/ */
const transformJSXSpreadChild = ( export const transformJSXSpreadChild = (
path: NodePath<t.JSXSpreadChild>, path: NodePath<t.JSXSpreadChild>,
): t.SpreadElement => t.spreadElement(path.get('expression').node); ): t.SpreadElement => t.spreadElement(path.get('expression').node);
const walksScope = (path: NodePath, name: string, slotFlag: SlotFlags): void => { export const walksScope = (path: NodePath, name: string, slotFlag: SlotFlags): void => {
if (path.scope.hasBinding(name) && path.parentPath) { if (path.scope.hasBinding(name) && path.parentPath) {
if (t.isJSXElement(path.parentPath.node)) { if (t.isJSXElement(path.parentPath.node)) {
path.parentPath.setData('slotFlag', slotFlag); path.parentPath.setData('slotFlag', slotFlag);
@ -193,7 +193,7 @@ const walksScope = (path: NodePath, name: string, slotFlag: SlotFlags): void =>
} }
}; };
const buildIIFE = (path: NodePath<t.JSXElement>, children: t.Expression[]) => { export const buildIIFE = (path: NodePath<t.JSXElement>, children: t.Expression[]) => {
const { parentPath } = path; const { parentPath } = path;
if (t.isAssignmentExpression(parentPath)) { if (t.isAssignmentExpression(parentPath)) {
const { left } = parentPath.node as t.AssignmentExpression; const { left } = parentPath.node as t.AssignmentExpression;
@ -221,19 +221,88 @@ const buildIIFE = (path: NodePath<t.JSXElement>, children: t.Expression[]) => {
return children; return children;
}; };
export { const onRE = /^on[^a-z]/;
createIdentifier,
isDirective, export const isOn = (key: string) => onRE.test(key);
checkIsComponent,
transformJSXMemberExpression, const mergeAsArray = (existing: t.ObjectProperty, incoming: t.ObjectProperty) => {
getTag, if (t.isArrayExpression(existing.value)) {
getJSXAttributeName, existing.value.elements.push(incoming.value as t.Expression);
transformJSXText, } else {
transformJSXSpreadChild, existing.value = t.arrayExpression([
transformJSXExpressionContainer, existing.value as t.Expression,
shouldTransformedToSlots, incoming.value as t.Expression,
FRAGMENT, ]);
walksScope, }
buildIIFE, };
JSX_HELPER_KEY,
export const dedupeProperties = (properties: t.ObjectProperty[] = [], mergeProps?: boolean) => {
if (!mergeProps) {
return properties;
}
const knownProps = new Map<string, t.ObjectProperty>();
const deduped: t.ObjectProperty[] = [];
properties.forEach((prop) => {
if (t.isStringLiteral(prop.key)) {
const { value: name } = prop.key;
const existing = knownProps.get(name);
if (existing) {
if (name === 'style' || name === 'class' || name.startsWith('on')) {
mergeAsArray(existing, prop);
}
} else {
knownProps.set(name, prop);
deduped.push(prop);
}
} else {
// v-model target with variable
deduped.push(prop);
}
});
return deduped;
};
/**
* Check if an attribute value is constant
* @param node
* @returns boolean
*/
export const isConstant = (
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;
return elements.every((element) => element && isConstant(element));
}
if (t.isObjectExpression(node)) {
return node.properties.every((property) => isConstant((property as any).value));
}
if (t.isLiteral(node)) {
return true;
}
return false;
};
export const transformJSXSpreadAttribute = (
nodePath: NodePath,
path: NodePath<t.JSXSpreadAttribute>,
mergeProps: boolean,
args: (t.ObjectProperty | t.Expression | t.SpreadElement)[],
) => {
const argument = path.get('argument') as NodePath<t.ObjectExpression | t.Identifier>;
const properties = t.isObjectExpression(argument.node) ? argument.node.properties : undefined;
if (!properties) {
if (argument.isIdentifier()) {
walksScope(nodePath, (argument.node as t.Identifier).name, SlotFlags.DYNAMIC);
}
args.push(mergeProps ? argument.node : t.spreadElement(argument.node));
} else if (mergeProps) {
args.push(t.objectExpression(properties));
} else {
args.push(...(properties as t.ObjectProperty[]));
}
}; };

View File

@ -4,7 +4,6 @@
"rootDirs": ["./src"], "rootDirs": ["./src"],
"outDir": "dist", "outDir": "dist",
"downlevelIteration": true, "downlevelIteration": true,
"types": ["node", "jest"],
"declaration": true "declaration": true
}, },
"include": ["src/**/*", "global.d.ts"], "include": ["src/**/*", "global.d.ts"],

View File

@ -12,10 +12,15 @@
"esModuleInterop": true, "esModuleInterop": true,
"removeComments": false, "removeComments": false,
"jsx": "preserve", "jsx": "preserve",
"lib": ["esnext", "dom"], "lib": [
"types": ["node"] "esnext",
"dom"
],
"types": ["node", "jest"],
}, },
"include": [ "include": [
"global.d.ts" "global.d.ts",
"packages/*/src",
"packages/*/test",
] ]
} }

106
yarn.lock
View File

@ -2167,9 +2167,35 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@typescript-eslint/eslint-plugin@^4.4.1":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.2.tgz#981b26b4076c62a5a55873fbef3fe98f83360c61"
integrity sha512-uiQQeu9tWl3f1+oK0yoAv9lt/KXO24iafxgQTkIYO/kitruILGx3uH+QtIAHqxFV+yIsdnJH+alel9KuE3J15Q==
dependencies:
"@typescript-eslint/experimental-utils" "4.15.2"
"@typescript-eslint/scope-manager" "4.15.2"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
lodash "^4.17.15"
regexpp "^3.0.0"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@4.15.2":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.2.tgz#5efd12355bd5b535e1831282e6cf465b9a71cf36"
integrity sha512-Fxoshw8+R5X3/Vmqwsjc8nRO/7iTysRtDqx6rlfLZ7HbT8TZhPeQqbPjTyk2RheH3L8afumecTQnUc9EeXxohQ==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/scope-manager" "4.15.2"
"@typescript-eslint/types" "4.15.2"
"@typescript-eslint/typescript-estree" "4.15.2"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/experimental-utils@4.17.0": "@typescript-eslint/experimental-utils@4.17.0":
version "4.17.0" version "4.17.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz#762c44aaa1a6a3c05b6d63a8648fb89b89f84c80" resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz#762c44aaa1a6a3c05b6d63a8648fb89b89f84c80"
integrity sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA== integrity sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA==
dependencies: dependencies:
"@types/json-schema" "^7.0.3" "@types/json-schema" "^7.0.3"
@ -2189,9 +2215,27 @@
"@typescript-eslint/typescript-estree" "4.22.0" "@typescript-eslint/typescript-estree" "4.22.0"
debug "^4.1.1" debug "^4.1.1"
"@typescript-eslint/parser@^4.4.1":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.15.2.tgz#c804474321ef76a3955aec03664808f0d6e7872e"
integrity sha512-SHeF8xbsC6z2FKXsaTb1tBCf0QZsjJ94H6Bo51Y1aVEZ4XAefaw5ZAilMoDPlGghe+qtq7XdTiDlGfVTOmvA+Q==
dependencies:
"@typescript-eslint/scope-manager" "4.15.2"
"@typescript-eslint/types" "4.15.2"
"@typescript-eslint/typescript-estree" "4.15.2"
debug "^4.1.1"
"@typescript-eslint/scope-manager@4.15.2":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.15.2.tgz#5725bda656995960ae1d004bfd1cd70320f37f4f"
integrity sha512-Zm0tf/MSKuX6aeJmuXexgdVyxT9/oJJhaCkijv0DvJVT3ui4zY6XYd6iwIo/8GEZGy43cd7w1rFMiCLHbRzAPQ==
dependencies:
"@typescript-eslint/types" "4.15.2"
"@typescript-eslint/visitor-keys" "4.15.2"
"@typescript-eslint/scope-manager@4.17.0": "@typescript-eslint/scope-manager@4.17.0":
version "4.17.0" version "4.17.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d" resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d"
integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw==
dependencies: dependencies:
"@typescript-eslint/types" "4.17.0" "@typescript-eslint/types" "4.17.0"
@ -2199,25 +2243,43 @@
"@typescript-eslint/scope-manager@4.22.0": "@typescript-eslint/scope-manager@4.22.0":
version "4.22.0" version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a" resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a"
integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q== integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==
dependencies: dependencies:
"@typescript-eslint/types" "4.22.0" "@typescript-eslint/types" "4.22.0"
"@typescript-eslint/visitor-keys" "4.22.0" "@typescript-eslint/visitor-keys" "4.22.0"
"@typescript-eslint/types@4.15.2":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.2.tgz#04acf3a2dc8001a88985291744241e732ef22c60"
integrity sha512-r7lW7HFkAarfUylJ2tKndyO9njwSyoy6cpfDKWPX6/ctZA+QyaYscAHXVAfJqtnY6aaTwDYrOhp+ginlbc7HfQ==
"@typescript-eslint/types@4.17.0": "@typescript-eslint/types@4.17.0":
version "4.17.0" version "4.17.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad" resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad"
integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g==
"@typescript-eslint/types@4.22.0": "@typescript-eslint/types@4.22.0":
version "4.22.0" version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6" resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6"
integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA== integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==
"@typescript-eslint/typescript-estree@4.15.2":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.2.tgz#c2f7a1e94f3428d229d5ecff3ead6581ee9b62fa"
integrity sha512-cGR8C2g5SPtHTQvAymEODeqx90pJHadWsgTtx6GbnTWKqsg7yp6Eaya9nFzUd4KrKhxdYTTFBiYeTPQaz/l8bw==
dependencies:
"@typescript-eslint/types" "4.15.2"
"@typescript-eslint/visitor-keys" "4.15.2"
debug "^4.1.1"
globby "^11.0.1"
is-glob "^4.0.1"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@4.17.0": "@typescript-eslint/typescript-estree@4.17.0":
version "4.17.0" version "4.17.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1" resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1"
integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ==
dependencies: dependencies:
"@typescript-eslint/types" "4.17.0" "@typescript-eslint/types" "4.17.0"
@ -2241,9 +2303,17 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@typescript-eslint/visitor-keys@4.15.2":
version "4.15.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.2.tgz#3d1c7979ce75bf6acf9691109bd0d6b5706192b9"
integrity sha512-TME1VgSb7wTwgENN5KVj4Nqg25hP8DisXxNBojM4Nn31rYaNDIocNm5cmjOFfh42n7NVERxWrDFoETO/76ePyg==
dependencies:
"@typescript-eslint/types" "4.15.2"
eslint-visitor-keys "^2.0.0"
"@typescript-eslint/visitor-keys@4.17.0": "@typescript-eslint/visitor-keys@4.17.0":
version "4.17.0" version "4.17.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d" resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d"
integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ==
dependencies: dependencies:
"@typescript-eslint/types" "4.17.0" "@typescript-eslint/types" "4.17.0"
@ -4602,7 +4672,7 @@ escodegen@^1.14.1:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-config-airbnb-base@^14.1.0: eslint-config-airbnb-base@^14.2.0, eslint-config-airbnb-base@^14.2.1:
version "14.2.1" version "14.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e"
integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==
@ -4611,6 +4681,24 @@ eslint-config-airbnb-base@^14.1.0:
object.assign "^4.1.2" object.assign "^4.1.2"
object.entries "^1.1.2" object.entries "^1.1.2"
eslint-config-airbnb-typescript@^12.3.1:
version "12.3.1"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.3.1.tgz#83ab40d76402c208eb08516260d1d6fac8f8acbc"
integrity sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==
dependencies:
"@typescript-eslint/parser" "^4.4.1"
eslint-config-airbnb "^18.2.0"
eslint-config-airbnb-base "^14.2.0"
eslint-config-airbnb@^18.2.0:
version "18.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz#b7fe2b42f9f8173e825b73c8014b592e449c98d9"
integrity sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==
dependencies:
eslint-config-airbnb-base "^14.2.1"
object.assign "^4.1.2"
object.entries "^1.1.2"
eslint-import-resolver-node@^0.3.4: eslint-import-resolver-node@^0.3.4:
version "0.3.4" version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
@ -4627,7 +4715,7 @@ eslint-module-utils@^2.6.0:
debug "^2.6.9" debug "^2.6.9"
pkg-dir "^2.0.0" pkg-dir "^2.0.0"
eslint-plugin-import@^2.20.2: eslint-plugin-import@^2.22.1:
version "2.22.1" version "2.22.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702"
integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==