mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2025-01-27 00:29:10 +08:00
chore: replace namespace imports with named imports (#67)
* feat: Replace namespace imports with named imports (#54) * Add .circleci/config.yml * ci: fix circleci error * fix: specifiers should be merged into a single importDeclaration Co-authored-by: 逆寒 <869732751@qq.com> Co-authored-by: tangjinzhou <415800467@qq.com>
This commit is contained in:
parent
86b5051174
commit
5790b83ed6
28
.circleci/config.yml
Normal file
28
.circleci/config.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
version: 2.1
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
docker:
|
||||||
|
# specify the version you desire here
|
||||||
|
- image: vuejs/ci
|
||||||
|
user: node
|
||||||
|
|
||||||
|
working_directory: /home/node/repo
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
|
||||||
|
# Download and cache dependencies
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- v2-dependencies-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
|
- run: yarn install --pure-lockfile
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
paths:
|
||||||
|
- node_modules
|
||||||
|
- ~/.cache/yarn
|
||||||
|
key: v2-dependencies-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
|
# run tests!
|
||||||
|
- run: yarn test
|
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -1,19 +0,0 @@
|
|||||||
name: test
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
setup:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: install
|
|
||||||
run: yarn install
|
|
||||||
|
|
||||||
- name: lint
|
|
||||||
run: yarn lint
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
run: yarn test
|
|
@ -1,6 +1,9 @@
|
|||||||
import syntaxJsx from '@babel/plugin-syntax-jsx';
|
import syntaxJsx from '@babel/plugin-syntax-jsx';
|
||||||
|
import * as t from '@babel/types';
|
||||||
|
import { NodePath } from '@babel/traverse';
|
||||||
import tranformVueJSX from './transform-vue-jsx';
|
import tranformVueJSX from './transform-vue-jsx';
|
||||||
import sugarFragment from './sugar-fragment';
|
import sugarFragment from './sugar-fragment';
|
||||||
|
import { JSX_HELPER_KEY } from './utils';
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
get: (name: string) => any;
|
get: (name: string) => any;
|
||||||
@ -20,6 +23,50 @@ export default () => ({
|
|||||||
name: 'babel-plugin-jsx',
|
name: 'babel-plugin-jsx',
|
||||||
inherits: syntaxJsx,
|
inherits: syntaxJsx,
|
||||||
visitor: {
|
visitor: {
|
||||||
|
Program: {
|
||||||
|
exit(path: NodePath<t.Program>, state: State) {
|
||||||
|
const helpers: Set<string> = state.get(JSX_HELPER_KEY);
|
||||||
|
if (!helpers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = path.get('body');
|
||||||
|
const specifierNames = new Set<string>();
|
||||||
|
body
|
||||||
|
.filter((nodePath) => t.isImportDeclaration(nodePath.node)
|
||||||
|
&& nodePath.node.source.value === 'vue')
|
||||||
|
.forEach((nodePath) => {
|
||||||
|
let shouldKeep = false;
|
||||||
|
const newSpecifiers = (nodePath.node as t.ImportDeclaration).specifiers
|
||||||
|
.filter((specifier) => {
|
||||||
|
if (t.isImportSpecifier(specifier)) {
|
||||||
|
const { imported, local } = specifier;
|
||||||
|
specifierNames.add(imported.name);
|
||||||
|
return local.name !== imported.name;
|
||||||
|
} if (t.isImportNamespaceSpecifier(specifier)) {
|
||||||
|
// should keep when `import * as Vue from 'vue'`
|
||||||
|
shouldKeep = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newSpecifiers.length) {
|
||||||
|
nodePath.replaceWith(t.importDeclaration(newSpecifiers, t.stringLiteral('vue')));
|
||||||
|
} else if (!shouldKeep) {
|
||||||
|
nodePath.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const importedHelperKeys = new Set([...specifierNames, ...helpers]);
|
||||||
|
const specifiers: t.ImportSpecifier[] = [...importedHelperKeys].map(
|
||||||
|
(imported) => t.importSpecifier(
|
||||||
|
t.identifier(imported), t.identifier(imported),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const expression = t.importDeclaration(specifiers, t.stringLiteral('vue'));
|
||||||
|
path.unshiftContainer('body', expression);
|
||||||
|
},
|
||||||
|
},
|
||||||
...tranformVueJSX(),
|
...tranformVueJSX(),
|
||||||
...sugarFragment(),
|
...sugarFragment(),
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
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 { NodePath } from '@babel/traverse';
|
||||||
import { State } from '.';
|
import { State } from '.';
|
||||||
|
import { createIdentifier, FRAGMENT } from './utils';
|
||||||
|
|
||||||
const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXMemberExpression) => {
|
const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXIdentifier) => {
|
||||||
const children = path.get('children') || [];
|
const children = path.get('children') || [];
|
||||||
return t.jsxElement(
|
return t.jsxElement(
|
||||||
t.jsxOpeningElement(Fragment, []),
|
t.jsxOpeningElement(Fragment, []),
|
||||||
@ -16,16 +16,10 @@ const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXMemberEx
|
|||||||
export default () => ({
|
export default () => ({
|
||||||
JSXFragment: {
|
JSXFragment: {
|
||||||
enter(path: NodePath<t.JSXElement>, state: State) {
|
enter(path: NodePath<t.JSXElement>, state: State) {
|
||||||
if (!state.get('vue')) {
|
|
||||||
state.set('vue', addNamespace(path, 'vue'));
|
|
||||||
}
|
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
transformFragment(
|
transformFragment(
|
||||||
path,
|
path,
|
||||||
t.jsxMemberExpression(
|
t.jsxIdentifier(createIdentifier(state, FRAGMENT).name),
|
||||||
t.jsxIdentifier(state.get('vue').name),
|
|
||||||
t.jsxIdentifier('Fragment'),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import * as t from '@babel/types';
|
import * as t from '@babel/types';
|
||||||
import { NodePath } from '@babel/traverse';
|
import { NodePath } from '@babel/traverse';
|
||||||
import { addNamespace } from '@babel/helper-module-imports';
|
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
transformJSXSpreadChild,
|
transformJSXSpreadChild,
|
||||||
@ -21,11 +20,11 @@ import { State } from '.';
|
|||||||
const getChildren = (
|
const getChildren = (
|
||||||
paths: NodePath<
|
paths: NodePath<
|
||||||
t.JSXText
|
t.JSXText
|
||||||
| t.JSXExpressionContainer
|
| t.JSXExpressionContainer
|
||||||
| t.JSXSpreadChild
|
| t.JSXSpreadChild
|
||||||
| t.JSXElement
|
| t.JSXElement
|
||||||
| t.JSXFragment
|
| t.JSXFragment
|
||||||
>[],
|
>[],
|
||||||
state: State,
|
state: State,
|
||||||
): t.Expression[] => paths
|
): t.Expression[] => paths
|
||||||
.map((path) => {
|
.map((path) => {
|
||||||
@ -61,8 +60,8 @@ const getChildren = (
|
|||||||
throw new Error(`getChildren: ${path.type} is not supported`);
|
throw new Error(`getChildren: ${path.type} is not supported`);
|
||||||
}).filter(((value: any) => (
|
}).filter(((value: any) => (
|
||||||
value !== undefined
|
value !== undefined
|
||||||
&& value !== null
|
&& value !== null
|
||||||
&& !t.isJSXEmptyExpression(value)
|
&& !t.isJSXEmptyExpression(value)
|
||||||
)) as any);
|
)) as any);
|
||||||
|
|
||||||
const transformJSXElement = (
|
const transformJSXElement = (
|
||||||
@ -123,15 +122,11 @@ const transformJSXElement = (
|
|||||||
t.arrayExpression(directives),
|
t.arrayExpression(directives),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { transformJSXElement };
|
export { transformJSXElement };
|
||||||
|
|
||||||
export default () => ({
|
export default () => ({
|
||||||
JSXElement: {
|
JSXElement: {
|
||||||
exit(path: NodePath<t.JSXElement>, state: State) {
|
exit(path: NodePath<t.JSXElement>, state: State) {
|
||||||
if (!state.get('vue')) {
|
|
||||||
state.set('vue', addNamespace(path, 'vue'));
|
|
||||||
}
|
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
transformJSXElement(path, state),
|
transformJSXElement(path, state),
|
||||||
);
|
);
|
||||||
|
@ -5,15 +5,25 @@ import { NodePath } from '@babel/traverse';
|
|||||||
import { State } from '.';
|
import { State } from '.';
|
||||||
import SlotFlags from './slotFlags';
|
import SlotFlags from './slotFlags';
|
||||||
|
|
||||||
|
const JSX_HELPER_KEY = 'JSX_HELPER_KEY';
|
||||||
|
const FRAGMENT = 'Fragment';
|
||||||
/**
|
/**
|
||||||
* create Identifier
|
* create Identifier
|
||||||
|
* @param path NodePath
|
||||||
* @param state
|
* @param state
|
||||||
* @param id string
|
* @param id string
|
||||||
* @returns MemberExpression
|
* @returns MemberExpression
|
||||||
*/
|
*/
|
||||||
const createIdentifier = (
|
const createIdentifier = (
|
||||||
state: State, id: string,
|
state: State, id: string,
|
||||||
): t.MemberExpression => t.memberExpression(state.get('vue'), t.identifier(id));
|
): t.Identifier => {
|
||||||
|
if (!state.get(JSX_HELPER_KEY)) {
|
||||||
|
state.set(JSX_HELPER_KEY, new Set());
|
||||||
|
}
|
||||||
|
const helpers = state.get(JSX_HELPER_KEY);
|
||||||
|
helpers.add(id);
|
||||||
|
return t.identifier(id);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if string is describing a directive
|
* Checks if string is describing a directive
|
||||||
@ -30,8 +40,15 @@ const isDirective = (src: string): boolean => src.startsWith('v-')
|
|||||||
const isFragment = (
|
const isFragment = (
|
||||||
path:
|
path:
|
||||||
NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>,
|
NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>,
|
||||||
): boolean => t.isJSXMemberExpression(path)
|
): boolean => {
|
||||||
&& (path.node as t.JSXMemberExpression).property.name === 'Fragment';
|
if (path.isJSXIdentifier()) {
|
||||||
|
return path.node.name === FRAGMENT;
|
||||||
|
}
|
||||||
|
if (path.isJSXMemberExpression()) {
|
||||||
|
return (path.node as t.JSXMemberExpression).property.name === FRAGMENT;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a Node is a component
|
* Check if a Node is a component
|
||||||
@ -49,7 +66,7 @@ const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => {
|
|||||||
|
|
||||||
const tag = (namePath as NodePath<t.JSXIdentifier>).node.name;
|
const tag = (namePath as NodePath<t.JSXIdentifier>).node.name;
|
||||||
|
|
||||||
return !htmlTags.includes(tag) && !svgTags.includes(tag);
|
return tag !== FRAGMENT && !htmlTags.includes(tag) && !svgTags.includes(tag);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,13 +102,13 @@ const getTag = (
|
|||||||
if (namePath.isJSXIdentifier()) {
|
if (namePath.isJSXIdentifier()) {
|
||||||
const { name } = namePath.node;
|
const { name } = namePath.node;
|
||||||
if (!htmlTags.includes(name) && !svgTags.includes(name)) {
|
if (!htmlTags.includes(name) && !svgTags.includes(name)) {
|
||||||
return path.scope.hasBinding(name)
|
return (name === FRAGMENT
|
||||||
? t.identifier(name)
|
? createIdentifier(state, FRAGMENT)
|
||||||
: (
|
: path.scope.hasBinding(name)
|
||||||
state.opts.isCustomElement?.(name)
|
? t.identifier(name)
|
||||||
|
: state.opts.isCustomElement?.(name)
|
||||||
? t.stringLiteral(name)
|
? t.stringLiteral(name)
|
||||||
: t.callExpression(createIdentifier(state, 'resolveComponent'), [t.stringLiteral(name)])
|
: t.callExpression(createIdentifier(state, 'resolveComponent'), [t.stringLiteral(name)]));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.stringLiteral(name);
|
return t.stringLiteral(name);
|
||||||
@ -171,7 +188,7 @@ const transformJSXText = (path: NodePath<t.JSXText>): t.StringLiteral | null =>
|
|||||||
const transformJSXExpressionContainer = (
|
const transformJSXExpressionContainer = (
|
||||||
path: NodePath<t.JSXExpressionContainer>,
|
path: NodePath<t.JSXExpressionContainer>,
|
||||||
): (t.Expression
|
): (t.Expression
|
||||||
) => path.get('expression').node as t.Expression;
|
) => path.get('expression').node as t.Expression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform JSXSpreadChild
|
* Transform JSXSpreadChild
|
||||||
@ -237,6 +254,8 @@ export {
|
|||||||
transformJSXSpreadChild,
|
transformJSXSpreadChild,
|
||||||
transformJSXExpressionContainer,
|
transformJSXExpressionContainer,
|
||||||
isFragment,
|
isFragment,
|
||||||
|
FRAGMENT,
|
||||||
walksScope,
|
walksScope,
|
||||||
buildIIFE,
|
buildIIFE,
|
||||||
|
JSX_HELPER_KEY,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user