├── src
├── index.js
├── babel-sugar-v-model-v1.1.2-patch.js
├── babel-sugar-inject-h.js
├── babel-sugar-setup-functional.js
└── babel-sugar-setup-ref.js
├── package.json
├── .gitignore
├── README.zh-CN.md
└── README.md
/src/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return {
3 | plugins: [
4 | require('./babel-sugar-inject-h'),
5 | require('./babel-sugar-setup-functional'),
6 | require('./babel-sugar-setup-ref'),
7 | require('./babel-sugar-v-model-v1.1.2-patch')
8 | ]
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "babel-preset-vca-jsx",
3 | "version": "0.3.5",
4 | "description": "Support for automatic import of createElement as h and setup functional component syntax and setup template refs",
5 | "main": "./src/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/luwanquan/babel-preset-vca-jsx.git"
12 | },
13 | "keywords": [
14 | "vue",
15 | "@vue/composition-api",
16 | "jsx",
17 | "babel-preset",
18 | "auto-import",
19 | "createElement",
20 | "h",
21 | "template refs"
22 | ],
23 | "author": "luwanquan",
24 | "license": "ISC",
25 | "bugs": {
26 | "url": "https://github.com/luwanquan/babel-preset-vca-jsx/issues"
27 | },
28 | "homepage": "https://github.com/luwanquan/babel-preset-vca-jsx#readme",
29 | "dependencies": {
30 | "@babel/plugin-syntax-jsx": "^7.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/src/babel-sugar-v-model-v1.1.2-patch.js:
--------------------------------------------------------------------------------
1 |
2 | const autoImportVue = (t, path) => {
3 | const importSource = "vue";
4 | const imports = path.node.body.filter(t.isImportDeclaration);
5 | const imported = imports.find(node => node.source.value === importSource);
6 | const autoImport = () =>
7 | path.unshiftContainer(
8 | "body",
9 | t.importDeclaration(
10 | [t.importDefaultSpecifier(t.identifier("Vue"))],
11 | t.stringLiteral(importSource)
12 | )
13 | );
14 | if (!imported) {
15 | autoImport();
16 | return;
17 | }
18 | const defaultSpecifier = imported.specifiers.find(t.isImportDefaultSpecifier);
19 | if (!defaultSpecifier) {
20 | imported.specifiers.push(t.importDefaultSpecifier(t.identifier("Vue")));
21 | } else if (defaultSpecifier.local.name !== "Vue") {
22 | autoImport();
23 | }
24 | };
25 |
26 | module.exports = ({ types: t }) => {
27 | return {
28 | visitor: {
29 | Program(p) {
30 | p.traverse({
31 | "ObjectMethod|ObjectProperty"(path) {
32 | if (path.node.key.name !== "setup") return;
33 | path.traverse({
34 | JSXAttribute(path) {
35 | const n = path.get("name");
36 | const isInputOrModel = n.node.name === "on-input" || n.node.name === "on-change" || n.node.name === "model";
37 | if (!isInputOrModel) return;
38 | path.traverse({
39 | MemberExpression(path) {
40 | const obj = path.get("object");
41 | const prop = path.get("property");
42 | if (t.isThisExpression(obj) && t.isIdentifier(prop) && prop.node.name === "$set") {
43 | autoImportVue(t, p);
44 | obj.replaceWith(t.identifier("Vue"));
45 | prop.replaceWith(t.identifier("set"));
46 | }
47 | }
48 | });
49 | }
50 | });
51 | }
52 | });
53 | }
54 | }
55 | };
56 | };
57 |
--------------------------------------------------------------------------------
/src/babel-sugar-inject-h.js:
--------------------------------------------------------------------------------
1 | const syntaxJsx = require('@babel/plugin-syntax-jsx').default;
2 |
3 | const importSource = '@vue/composition-api';
4 |
5 | const hasJSX = (t, path) => {
6 | const JSXChecker = {
7 | hasJSX: false
8 | };
9 | path.traverse({
10 | JSXElement() {
11 | this.hasJSX = true
12 | }
13 | }, JSXChecker);
14 | return JSXChecker.hasJSX;
15 | };
16 |
17 | // remove `var h = this.$createElement;` in `setup()`
18 | const remove$createElement = (t, path) => {
19 | path.traverse({
20 | ObjectMethod(p) {
21 | const isSetup = p.node.key.name === 'setup';
22 | if (!isSetup) return;
23 | p.traverse({
24 | VariableDeclaration(varPath) {
25 | varPath.traverse({
26 | MemberExpression(p) {
27 | if (t.isThisExpression(p.node.object) && t.isIdentifier(p.node.property) && p.node.property.name === '$createElement') {
28 | varPath.remove();
29 | }
30 | }
31 | })
32 | }
33 | });
34 | }
35 | });
36 | };
37 |
38 | // auto import `createElement as h` from `@vue/composition-api`
39 | const autoImportH = (t, path) => {
40 | if (hasJSX(t, path)) {
41 | const importNodes = path.get('body').filter(p => p.isImportDeclaration()).map(p => p.node);
42 | const vcaImportNodes = importNodes.filter(p => p.source.value === importSource);
43 | const hasH = vcaImportNodes.some(p => (
44 | p.specifiers.some(s => (
45 | t.isImportSpecifier(s) && s.imported.name === 'createElement' && s.local.name === 'h'
46 | ))
47 | ));
48 | if (!hasH) {
49 | const vcaImportSpecifier = t.importSpecifier(t.identifier('h'), t.identifier('createElement'));
50 | if (vcaImportNodes.length > 0) {
51 | vcaImportNodes[0].specifiers.push(vcaImportSpecifier);
52 | } else {
53 | path.unshiftContainer('body', t.importDeclaration([vcaImportSpecifier], t.stringLiteral(importSource)));
54 | }
55 | }
56 | }
57 | }
58 |
59 | module.exports = ({ types: t }) => {
60 | return {
61 | inherits: syntaxJsx,
62 | visitor: {
63 | Program(path) {
64 | remove$createElement(t, path);
65 | autoImportH(t, path);
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | # babel-preset-vca-jsx
2 | > 支持自动导入`createElement as h`以及`setup`函数式组件语法和`setup`中的模板refs引用
3 |
4 | ## 功能点
5 |
6 | 1. 写`JSX`时自动导入`createElement as h`
7 | 1. 默认只有`setup()`的函数式组件语法
8 | ```javascript
9 | const Hello = (prop, ctx) => {
10 | const state = ref('hello world');
11 | return () =>
{state.value}
;
12 | };
13 | ```
14 | 1. 在`setup()`返回的渲染函数上使用`JSX`分配模板引用
15 | ```javascript
16 | const Hello = createComponent({
17 | setup() {
18 | const root = ref(null);
19 | watch(() => console.log(root.value)); // ...
20 | /*
21 | return () => h('h1', {
22 | ref: root
23 | }, 'hello world!');
24 | */
25 | return () => hello world!
26 | }
27 | });
28 | ```
29 | 1. 修复 [@vue/babel-sugar-v-model@1.1.2](https://github.com/vuejs/jsx/tree/dev/packages/babel-sugar-v-model) 在 `setup()` 中调用 `this` 的问题
30 |
31 |
32 | ## [案例](https://codesandbox.io/s/babel-preset-vca-jsx-example-7k5xs)
33 |
34 | 编译前
35 | ```javascript
36 | import { ref } from '@vue/composition-api';
37 |
38 | const Hello = (prop, ctx) => {
39 | const state = ref('Hello World!');
40 | return () => (
41 | {state.value}
42 | );
43 | };
44 | ```
45 |
46 | 编译后
47 | ```javascript
48 | import { ref, createElement as h } from '@vue/composition-api';
49 |
50 | const Hello = {
51 | setup: (prop, ctx) => {
52 | const state = ref('Hello World!');
53 | return () => {
54 | return h('h1', state.value);
55 | };
56 | }
57 | };
58 | ```
59 |
60 | ## 使用前提
61 |
62 | 已安装`@vue/composition-api`和`@vue/cli-plugin-babel`的项目
63 |
64 |
65 |
66 | ## 如何使用?
67 |
68 | 1. 安装
69 |
70 | ```shell
71 | npm install babel-preset-vca-jsx --save-dev
72 | ```
73 |
74 | 2. 配置 `babel.config.js`
75 |
76 | ```javascript
77 | module.exports = {
78 | presets: [
79 | "vca-jsx",
80 | "@vue/cli-plugin-babel/preset"
81 | ]
82 | };
83 | ```
84 |
85 |
86 | ## 注意
87 |
88 | - 这里需要区分默认的 `functional`组件和基于 `composition-api` 的 `functional`组件的概念
89 |
90 | - 默认的 `functional`组件实质是 `render`函数,`jsx`中的简写写法如下
91 | ``` javascript
92 | const Test = ({ props, children, data, ... }) => {
93 | return Hello World!
;
94 | };
95 | ```
96 | **注:变量名首字母必须为大写,具体回调参数见[详情](https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6)**
97 |
98 | - 基于本插件的 `composition-api functional`实质是 `setup`函数,`jsx`中的简写写法如下
99 | ``` javascript
100 | const Test = (props, { refs, emit, ... }) => {
101 | return () => Hello World!
;
102 | };
103 | ```
104 | **注:与默认`functional`的区别是返回了一个`render`函数**
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # babel-preset-vca-jsx
2 | > Support for automatic import of `createElement as h` and `setup` functional component syntax and `setup` template refs
3 |
4 | ## Feature
5 |
6 | 1. Automatically import `createElement as h` when writing `JSX`
7 | 1. The functional component syntax of the `setup()` by default
8 | ```javascript
9 | const Hello = (prop, ctx) => {
10 | const state = ref('hello world');
11 | return () => {state.value}
;
12 | };
13 | ```
14 | 1. Allocating template refs with `JSX` on the render function returned by `setup()`
15 | ```javascript
16 | const Hello = createComponent({
17 | setup() {
18 | const root = ref(null);
19 | watch(() => console.log(root.value)); // ...
20 | /*
21 | return () => h('h1', {
22 | ref: root
23 | }, 'hello world!');
24 | */
25 | return () => hello world!
26 | }
27 | });
28 | ```
29 | 1. Fixed [@vue/babel-sugar-v-model@1.1.2](https://github.com/vuejs/jsx/tree/dev/packages/babel-sugar-v-model) calling `this` in `setup()`
30 |
31 |
32 | ## [Example](https://codesandbox.io/s/babel-preset-vca-jsx-example-7k5xs)
33 |
34 | Before compiling
35 | ```javascript
36 | import { ref } from '@vue/composition-api';
37 |
38 | const Hello = (prop, ctx) => {
39 | const state = ref('Hello World!');
40 | return () => (
41 | {state.value}
42 | );
43 | };
44 | ```
45 |
46 | After compilation
47 | ```javascript
48 | import { ref, createElement as h } from '@vue/composition-api';
49 |
50 | const Hello = {
51 | setup: (prop, ctx) => {
52 | const state = ref('Hello World!');
53 | return () => {
54 | return h('h1', state.value);
55 | };
56 | }
57 | };
58 | ```
59 |
60 | ## Prerequisite
61 |
62 | Project with `@vue/composition-api` and `@vue/cli-plugin-babel` installed
63 |
64 |
65 |
66 | ## How to use?
67 |
68 | 1. Install
69 |
70 | ```shell
71 | npm install babel-preset-vca-jsx --save-dev
72 | ```
73 |
74 | 1. Config `babel.config.js`
75 |
76 | ```javascript
77 | module.exports = {
78 | presets: [
79 | "vca-jsx",
80 | "@vue/cli-plugin-babel/preset"
81 | ]
82 | };
83 | ```
84 |
85 | ## Note
86 |
87 | - Here we need to distinguish between the default `functional` component and the `composition-api-based` `functional` component.
88 |
89 | - The default `functional` component is essentially the` render` function. The shorthand in `jsx` is as follows
90 | ``` javascript
91 | const Test = ({ props, children, data, ... }) => {
92 | return Hello World!
;
93 | };
94 | ```
95 | **Tips:The first letter of the variable name must be capitalized. For details of the callback parameters, see [Detail](https://vuejs.org/v2/guide/render-function.html)**
96 |
97 | - The `composition-api functional` component based on this plugin is essentially a `setup` function, and the shorthand in `jsx` is as follows
98 | ``` javascript
99 | const Test = (props, { refs, emit, ... }) => {
100 | return () => Hello World!
;
101 | };
102 | ```
103 | **Tips:The difference from the default `functional` is that a` render` function is returned**
104 |
--------------------------------------------------------------------------------
/src/babel-sugar-setup-functional.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert object to functional component
3 | * @param t
4 | * @param path
5 | */
6 | const convertSetupFunctionalComponent = (t, path) => {
7 | const properties = path.node.properties;
8 | const nameProp = properties.find(node => node.key.name === 'name');
9 | const renderProp = properties.find(node => node.key.name === 'render');
10 | const renderBody = renderProp.value.body;
11 | const renderParams = renderProp.value.params;
12 | const name = nameProp && nameProp.value.value;
13 | const params = [...renderParams.slice(renderParams.length && renderParams[0].name === 'h' ? 1 : 0)];
14 |
15 | const props = [
16 | t.objectProperty(t.identifier('setup'), t.arrowFunctionExpression(params, renderBody))
17 | ];
18 | if (name) {
19 | props.unshift(t.objectProperty(t.identifier('name'), t.stringLiteral(name)));
20 | }
21 | path.replaceWith(t.objectExpression(props));
22 | }
23 |
24 | /**
25 | * Check if it's a setup functional componet declarator
26 | * @param t
27 | * @param path
28 | * @returns boolean
29 | */
30 | const isSetupFunctionalComponentDeclarator = (t, path) => {
31 | const properties = path.node.properties;
32 |
33 | const isFunctional = properties.filter(node => {
34 | return (
35 | t.isObjectProperty(node) &&
36 | ((node.key.name === 'functional' && node.value.value === true) || node.key.name === 'render')
37 | );
38 | }).length === 2;
39 |
40 | if (!isFunctional) return false;
41 |
42 | const renderBody = properties.find(node => node.key.name === 'render').value.body;
43 |
44 | const returnStatement = t.isBlockStatement(renderBody) && renderBody.body.find(node => t.isReturnStatement(node));
45 |
46 | return (
47 | t.isArrowFunctionExpression(renderBody) ||
48 | t.isFunctionExpression(renderBody) ||
49 | (returnStatement && t.isArrowFunctionExpression(returnStatement.argument)) ||
50 | (returnStatement && t.isFunctionExpression(returnStatement.argument))
51 | );
52 | }
53 |
54 | module.exports = ({ types: t }) => {
55 | return {
56 | visitor: {
57 | Program(path) {
58 | path.traverse({
59 | ExportDefaultDeclaration(path) {
60 | const declaration = path.get('declaration');
61 | if (
62 | !t.isObjectExpression(declaration) ||
63 | !isSetupFunctionalComponentDeclarator(t, declaration)
64 | ) {
65 | return;
66 | }
67 | convertSetupFunctionalComponent(t, declaration);
68 | },
69 | VariableDeclaration(path) {
70 | if (
71 | path.node.declarations.length !== 1 ||
72 | !t.isVariableDeclarator(path.node.declarations[0]) ||
73 | !t.isObjectExpression(path.node.declarations[0].init)
74 | ) {
75 | return;
76 | }
77 | const declarator = path.get('declarations')[0];
78 | const init = declarator.get('init');
79 | if (!isSetupFunctionalComponentDeclarator(t, init)) {
80 | return;
81 | }
82 | convertSetupFunctionalComponent(t, init);
83 | }
84 | });
85 | }
86 | }
87 | };
88 | }
89 |
--------------------------------------------------------------------------------
/src/babel-sugar-setup-ref.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Definition like `const root = ref(null);`.
3 | * @param {*} t
4 | * @param {*} path
5 | * @returns {boolean}
6 | */
7 | const isRefCallee = (t, path) => {
8 | if (!t.isVariableDeclaration(path)) return;
9 | const declarations = path.get("declarations");
10 | if (declarations.length !== 1) return;
11 | const init = declarations[0].get("init");
12 | if (!t.isCallExpression(init)) return;
13 | return (
14 | init.node.callee.name === "ref" && t.isNullLiteral(init.get("arguments")[0])
15 | );
16 | };
17 |
18 | /**
19 | * Match definedRefs in JSX
20 | * @param {*} t
21 | * @param {*} path ObjectMethod
22 | * @returns {*} `refs` to be processed
23 | */
24 | const matchRefsInJSX = (t, path, definedRefs = []) => {
25 | const refs = [];
26 | path.traverse({
27 | // Definition like `h('h1', { ref: ... })`.
28 | CallExpression(path) {
29 | if (path.node.callee.name !== "h") return;
30 | const [, obj] = path.get("arguments");
31 | if (!obj || !t.isObjectExpression(obj)) return;
32 | const props = obj.get("properties");
33 | const refProp = props.find(path => path.node.key.name === "ref");
34 | if (!refProp) return;
35 | const value = refProp.get("value");
36 | if (t.isIdentifier(value)) {
37 | const ref = value.node.name;
38 | if (definedRefs.indexOf(ref) > -1) {
39 | refs.push(ref);
40 | value.replaceWith(t.stringLiteral(ref));
41 | }
42 | } else if (t.isStringLiteral(value)) {
43 | const ref = value.node.value;
44 | if (definedRefs.indexOf(ref) > -1) {
45 | refs.push(ref);
46 | }
47 | }
48 | },
49 | // Definition like ``.
50 | JSXAttribute(path) {
51 | if (path.node.name.name !== "ref") return;
52 | const value = path.get("value");
53 | if (t.isJSXExpressionContainer(value)) {
54 | const exp = value.get("expression");
55 | if (!exp || !t.isIdentifier(exp)) return;
56 | const ref = exp.node.name;
57 | if (definedRefs.indexOf(ref) > -1) {
58 | refs.push(ref);
59 | value.replaceWith(t.stringLiteral(ref));
60 | }
61 | } else if (t.isStringLiteral(value)) {
62 | const ref = value.node.value;
63 | if (definedRefs.indexOf(ref) > -1) {
64 | refs.push(ref);
65 | }
66 | }
67 | }
68 | });
69 | return refs;
70 | };
71 |
72 | const autoImportVue = (t, path) => {
73 | const importSource = "vue";
74 | const imports = path.node.body.filter(t.isImportDeclaration);
75 | const imported = imports.find(node => node.source.value === importSource);
76 | const autoImport = () =>
77 | path.unshiftContainer(
78 | "body",
79 | t.importDeclaration(
80 | [t.importDefaultSpecifier(t.identifier("Vue"))],
81 | t.stringLiteral(importSource)
82 | )
83 | );
84 | if (!imported) {
85 | autoImport();
86 | return;
87 | }
88 | const defaultSpecifier = imported.specifiers.find(t.isImportDefaultSpecifier);
89 | if (!defaultSpecifier) {
90 | imported.specifiers.push(t.importDefaultSpecifier(t.identifier("Vue")));
91 | } else if (defaultSpecifier.local.name !== "Vue") {
92 | autoImport();
93 | }
94 | };
95 |
96 | /**
97 | * auto import lifeCycle
98 | * @param {*} t
99 | * @param {*} path Program
100 | * @param {*} lifeCycles
101 | */
102 | const autoImportLifeCycle = (t, path, lifeCycles = []) => {
103 | const importSource = "@vue/composition-api";
104 | const imports = path.node.body.filter(t.isImportDeclaration);
105 | const imported = imports.find(node => node.source.value === importSource);
106 | const genImportSpecifiers = list =>
107 | list.map(name => t.importSpecifier(t.identifier(name), t.identifier(name)));
108 | if (!imported) {
109 | path.unshiftContainer(
110 | "body",
111 | t.importDeclaration(
112 | genImportSpecifiers(lifeCycles),
113 | t.stringLiteral(importSource)
114 | )
115 | );
116 | return lifeCycles;
117 | }
118 | const specifiers = imported.specifiers.filter(t.isImportSpecifier);
119 | const names = lifeCycles.map(name => {
120 | const node = specifiers.find(node => name === node.imported.name);
121 | if (node) {
122 | return node.local.name;
123 | } else {
124 | imported.specifiers.push(...genImportSpecifiers([name]));
125 | return name;
126 | }
127 | });
128 | return names;
129 | };
130 |
131 | /**
132 | * inject onMounted(() => {
133 | * Vue.$nextTick(() => {
134 | * ...
135 | * });
136 | * });
137 | * @param {*} t
138 | * @param {*} path ObjectMethod
139 | * @param {*} lifeCycles
140 | * @param {*} refs
141 | */
142 | const injectLifeCycle = (t, path, lifeCycles = [], refs = []) => {
143 | const [prop, ctx] = path.get("params");
144 | const props = ["refs"];
145 | const getProperties = () =>
146 | props.map(val =>
147 | t.objectProperty(t.identifier(val), t.identifier(val), false, true)
148 | );
149 | if (!prop) {
150 | path.node.params.push(t.identifier("_"));
151 | }
152 | if (!ctx) {
153 | path.node.params.push(t.objectPattern(getProperties()));
154 | } else {
155 | if (t.isObjectPattern(ctx)) {
156 | const refsProp = ctx.node.properties.find(
157 | node => node.key.name === props[0]
158 | );
159 | if (!refsProp) {
160 | ctx.node.properties.push(...getProperties());
161 | } else {
162 | props[0] = refsProp.value.name;
163 | }
164 | } else {
165 | props.unshift(ctx.node.name);
166 | }
167 | }
168 |
169 | const identifiers = props.map(val => t.identifier(val));
170 | const statements = refs.map(val => {
171 | return t.expressionStatement(
172 | t.assignmentExpression(
173 | "=",
174 | t.memberExpression(t.identifier(val), t.identifier("value")),
175 | t.logicalExpression(
176 | "||",
177 | t.memberExpression(
178 | identifiers.length > 1
179 | ? t.memberExpression(...identifiers)
180 | : identifiers[0],
181 | t.identifier(val)
182 | ),
183 | t.nullLiteral()
184 | )
185 | )
186 | );
187 | });
188 |
189 | const nextTick = t.expressionStatement(
190 | t.callExpression(
191 | t.memberExpression(t.identifier("Vue"), t.identifier("nextTick")),
192 | [t.arrowFunctionExpression([], t.blockStatement(statements))]
193 | )
194 | );
195 |
196 | const content = lifeCycles.map(val =>
197 | t.expressionStatement(
198 | t.callExpression(t.identifier(val), [
199 | t.arrowFunctionExpression(
200 | [],
201 | t.blockStatement([...statements, nextTick])
202 | )
203 | ])
204 | )
205 | );
206 |
207 | path.get("body").unshiftContainer("body", content);
208 | };
209 |
210 | module.exports = ({ types: t }) => {
211 | return {
212 | visitor: {
213 | Program(p) {
214 | p.traverse({
215 | "ObjectMethod|ObjectProperty"(path) {
216 | if (path.node.key.name !== "setup") return;
217 | let container = path;
218 | if (t.isObjectProperty(path)) {
219 | container = path.get("value");
220 | }
221 | if (!t.isBlockStatement(container.get("body"))) return;
222 | const body = container.get("body").get("body");
223 | const definedRefs = body
224 | .filter(path => isRefCallee(t, path))
225 | .map(path => path.get("declarations")[0].node.id.name);
226 | if (!definedRefs.length) return;
227 |
228 | const refs = matchRefsInJSX(t, container, definedRefs);
229 |
230 | if (!refs.length) return;
231 | autoImportVue(t, p);
232 | const lifeCycles = autoImportLifeCycle(t, p, [
233 | "onMounted",
234 | "onUpdated"
235 | ]);
236 |
237 | injectLifeCycle(t, container, lifeCycles, refs);
238 | }
239 | });
240 | }
241 | }
242 | };
243 | };
244 |
--------------------------------------------------------------------------------