feat: add pragma option and support @jsx annotation (#322)

* add pragma option and support @jsx annotation

* update syntax and remove useless eslint rule

* use BabelCore.BabelFile as type definition of state.file

* update chinese doc
This commit is contained in:
JokcyLou 2021-02-28 18:08:42 +08:00 committed by GitHub
parent 57f670711d
commit ec50fddc8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 0 deletions

View File

@ -22,6 +22,7 @@ module.exports = {
'no-nested-ternary': [0],
'no-param-reassign': [0],
'no-use-before-define': [0],
'no-restricted-syntax': [0],
'no-plusplus': [0],
'import/no-extraneous-dependencies': [0],
'consistent-return': [0],

View File

@ -62,6 +62,14 @@ Default: `true`
使用 `enableObjectSlots` (文档下面会提到)。虽然在 JSX 中比较好使,但是会增加一些 `_isSlot` 的运行时条件判断,这会增加你的项目体积。即使你关闭了 `enableObjectSlots``v-slots` 还是可以使用
#### pragma
Type: `string`
Default: `createVNode`
替换编译JSX表达式的时候使用的函数
## 表达式
### 内容

View File

@ -66,6 +66,14 @@ Default: `true`
Whether to enable `object slots` (mentioned below the document) syntax". It might be useful in JSX, but it will add a lot of `_isSlot` condition expressions which increase your bundle size. And `v-slots` is still available even if `enableObjectSlots` is turned off.
#### pragma
Type: `string`
Default: `createVNode`
Replace the function used when compiling JSX expressions.
## Syntax
### Content

View File

@ -11,6 +11,7 @@ export type State = {
get: (name: string) => any;
set: (name: string, value: any) => any;
opts: VueJSXPluginOptions;
file: BabelCore.BabelFile
}
export interface VueJSXPluginOptions {
@ -24,6 +25,8 @@ export interface VueJSXPluginOptions {
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;
@ -44,6 +47,8 @@ const hasJSX = (parentPath: NodePath<t.Program>) => {
return fileHasJSX;
};
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
export default ({ types }: typeof BabelCore) => ({
name: 'babel-plugin-jsx',
inherits: syntaxJsx,
@ -129,6 +134,21 @@ export default ({ types }: typeof BabelCore) => ({
});
});
}
const { opts: { pragma = '' }, file } = state;
if (pragma) {
state.set('createVNode', () => t.identifier(pragma));
}
if (file.ast.comments) {
for (const comment of file.ast.comments) {
const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
if (jsxMatches) {
state.set('createVNode', () => t.identifier(jsxMatches[1]));
}
}
}
}
},
exit(path: NodePath<t.Program>) {

View File

@ -210,6 +210,11 @@ _withDirectives(_createVNode(\\"select\\", {
}, [_createTextVNode(\\"c\\")])], 8, [\\"onUpdate:modelValue\\"]), [[_vModelSelect, test]]);"
`;
exports[`set pragma to custom: custom 1`] = `
"import { createTextVNode as _createTextVNode } from \\"vue\\";
custom(\\"div\\", null, [_createTextVNode(\\"pragma\\")]);"
`;
exports[`should keep \`import * as Vue from "vue"\`: should keep \`import * as Vue from "vue"\` 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
import * as Vue from 'vue';
@ -239,6 +244,15 @@ _withDirectives(_createVNode(\\"textarea\\", {
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test]]);"
`;
exports[`use "@jsx" comment specify pragma: use "@jsx" comment specify pragma 1`] = `
"import { createTextVNode as _createTextVNode } from \\"vue\\";
/* @jsx custom */
custom(\\"div\\", {
\\"id\\": \\"custom\\"
}, [_createTextVNode(\\"Hello\\")]);"
`;
exports[`use "model" as the prop name: use "model" as the prop name 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent } from \\"vue\\";

View File

@ -163,6 +163,13 @@ const tests: Test[] = [
<Vue.KeepAlive>123</Vue.KeepAlive>
`,
},
{
name: 'use "@jsx" comment specify pragma',
from: `
/* @jsx custom */
<div id="custom">Hello</div>
`,
},
];
tests.forEach((
@ -244,3 +251,22 @@ objectSlotsTests.forEach(({
},
);
});
const pragmaTests = [
{
name: 'custom',
from: '<div>pragma</div>',
},
];
pragmaTests.forEach(({
name, from,
}) => {
test(
`set pragma to ${name}`,
async () => {
expect(await transpile(from, { pragma: 'custom' }))
.toMatchSnapshot(name);
},
);
});