From 1861a54c1e1c6593eaa31a0c5bf1645d0777915d Mon Sep 17 00:00:00 2001 From: akimyou Date: Wed, 2 Sep 2020 18:34:24 +0800 Subject: [PATCH] feat plugin-vue-enhance --- plugins/plugin-vue/.gitignore | 1 + plugins/plugin-vue/package.json | 13 +- plugins/plugin-vue/plugin-tsx-jsx.js | 22 ++ plugins/plugin-vue/plugin.js | 24 +- plugins/plugin-vue/src/script-compilers.js | 39 +++ .../__snapshots__/plugin-tsx-jsx.test.js.snap | 24 ++ .../plugin-vue-ts-tsx-jsx.test.js.snap | 69 +++++ .../test/__snapshots__/plugin.test.js.snap | 253 ++++++++++++++++++ .../script-compilers.test.js.snap | 46 ++++ .../plugin-vue/test/plugin-tsx-jsx.test.js | 32 +++ .../test/plugin-vue-ts-tsx-jsx.test.js | 44 +++ plugins/plugin-vue/test/plugin.test.js | 94 +++++++ .../plugin-vue/test/script-compilers.test.js | 33 +++ plugins/plugin-vue/test/stubs/JsxContent.jsx | 12 + plugins/plugin-vue/test/stubs/TsContent.ts | 8 + plugins/plugin-vue/test/stubs/TsxContent.tsx | 13 + plugins/plugin-vue/test/stubs/VueContent.vue | 60 +++++ .../test/stubs/VueContentErrorStyle.vue | 12 + .../test/stubs/VueContentErrorTpl.vue | 7 + .../plugin-vue/test/stubs/VueContentJsx.vue | 14 + .../test/stubs/VueContentOnlyTpl.vue | 3 + .../test/stubs/VueContentStyleScoped.vue | 13 + .../plugin-vue/test/stubs/VueContentTs.vue | 14 + .../plugin-vue/test/stubs/VueContentTsx.vue | 15 ++ plugins/plugin-vue/test/stubs/tsconfig.json | 7 + 25 files changed, 865 insertions(+), 7 deletions(-) create mode 100644 plugins/plugin-vue/.gitignore create mode 100644 plugins/plugin-vue/plugin-tsx-jsx.js create mode 100644 plugins/plugin-vue/src/script-compilers.js create mode 100644 plugins/plugin-vue/test/__snapshots__/plugin-tsx-jsx.test.js.snap create mode 100644 plugins/plugin-vue/test/__snapshots__/plugin-vue-ts-tsx-jsx.test.js.snap create mode 100644 plugins/plugin-vue/test/__snapshots__/plugin.test.js.snap create mode 100644 plugins/plugin-vue/test/__snapshots__/script-compilers.test.js.snap create mode 100644 plugins/plugin-vue/test/plugin-tsx-jsx.test.js create mode 100644 plugins/plugin-vue/test/plugin-vue-ts-tsx-jsx.test.js create mode 100644 plugins/plugin-vue/test/plugin.test.js create mode 100644 plugins/plugin-vue/test/script-compilers.test.js create mode 100644 plugins/plugin-vue/test/stubs/JsxContent.jsx create mode 100644 plugins/plugin-vue/test/stubs/TsContent.ts create mode 100644 plugins/plugin-vue/test/stubs/TsxContent.tsx create mode 100644 plugins/plugin-vue/test/stubs/VueContent.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentErrorStyle.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentErrorTpl.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentJsx.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentOnlyTpl.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentStyleScoped.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentTs.vue create mode 100644 plugins/plugin-vue/test/stubs/VueContentTsx.vue create mode 100644 plugins/plugin-vue/test/stubs/tsconfig.json diff --git a/plugins/plugin-vue/.gitignore b/plugins/plugin-vue/.gitignore new file mode 100644 index 0000000000..ed9f9cc128 --- /dev/null +++ b/plugins/plugin-vue/.gitignore @@ -0,0 +1 @@ +coverage \ No newline at end of file diff --git a/plugins/plugin-vue/package.json b/plugins/plugin-vue/package.json index ab1220645a..12a6488291 100644 --- a/plugins/plugin-vue/package.json +++ b/plugins/plugin-vue/package.json @@ -6,12 +6,23 @@ "publishConfig": { "access": "public" }, + "scripts": { + "test": "jest" + }, "dependencies": { "@vue/compiler-sfc": "^3.0.0-0", "hash-sum": "^2.0.0" }, "devDependencies": { + "jest": "^26.4.2", "vue": "*" }, - "gitHead": "a01616bb0787d56cd782f94cecf2daa12c7594e4" + "gitHead": "a01616bb0787d56cd782f94cecf2daa12c7594e4", + "jest": { + "collectCoverageFrom": [ + "plugin.js", + "script-compilers.js", + "!**/node_modules/**" + ] + } } diff --git a/plugins/plugin-vue/plugin-tsx-jsx.js b/plugins/plugin-vue/plugin-tsx-jsx.js new file mode 100644 index 0000000000..f5e3cad582 --- /dev/null +++ b/plugins/plugin-vue/plugin-tsx-jsx.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const scriptCompilers = require('./src/script-compilers'); + +module.exports = function plugin(snowpackConfig, pluginOptions) { + const curPluginOptions = pluginOptions || {}; + const {sourceMaps} = snowpackConfig.buildOptions; + const tsconfigFilePath = curPluginOptions.tsconfig; + + return { + name: '@snowpack/plugin-vue-tsx-jsx', + resolve: { + input: ['.tsx', '.jsx'], + output: ['.js'], + }, + async load({filePath, fileExt}) { + const content = fs.readFileSync(filePath, 'utf-8'); + const lang = fileExt.slice(fileExt.lastIndexOf('.') + 1); + const result = scriptCompilers.esbuildCompile(content, lang, tsconfigFilePath); + return result; + }, + }; +}; diff --git a/plugins/plugin-vue/plugin.js b/plugins/plugin-vue/plugin.js index 6c8dc95236..ec42e2abaa 100644 --- a/plugins/plugin-vue/plugin.js +++ b/plugins/plugin-vue/plugin.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const hashsum = require('hash-sum'); const compiler = require('@vue/compiler-sfc'); +const scriptCompilers = require('./src/script-compilers'); /** Friendly error display */ function displayError({contents, filePath, error}) { @@ -31,7 +32,10 @@ function displayError({contents, filePath, error}) { return output.join('\n'); } -module.exports = function plugin(snowpackConfig) { +module.exports = function plugin(snowpackConfig, pluginOptions) { + const curPluginOptions = pluginOptions || {}; + const tsconfigFilePath = curPluginOptions.tsconfig; + return { name: '@snowpack/plugin-vue', resolve: { @@ -56,14 +60,22 @@ module.exports = function plugin(snowpackConfig) { }; if (descriptor.script) { - output['.js'].code += descriptor.script.content.replace( - `export default`, - 'const defaultExport =', - ); + const scriptLang = descriptor.script.lang; + let scriptContent = descriptor.script.content; + if (['jsx', 'ts', 'tsx'].includes(scriptLang)) { + scriptContent = scriptCompilers.esbuildCompile( + scriptContent, + scriptLang, + tsconfigFilePath, + ); + } + if (['js', 'ts'].includes(scriptLang) || !scriptLang) { + scriptContent = scriptContent.replace(`export default`, 'const defaultExport ='); + } + output['.js'].code += scriptContent; } else { output['.js'].code += `const defaultExport = {};`; } - await Promise.all( descriptor.styles.map((stylePart) => { const css = compiler.compileStyle({ diff --git a/plugins/plugin-vue/src/script-compilers.js b/plugins/plugin-vue/src/script-compilers.js new file mode 100644 index 0000000000..43c21491c3 --- /dev/null +++ b/plugins/plugin-vue/src/script-compilers.js @@ -0,0 +1,39 @@ +const esbuild = require('esbuild'); + +const codeSnippetH = `import { Fragment } from '/web_modules/vue.js';`; + +// https://github.com/vitejs/vite/blob/master/src/client/vueJsxCompat.ts +const codeSnippetVueJsxCompat = +`import {createVNode, isVNode} from '/web_modules/vue.js'; +const slice = Array.prototype.slice; +export function jsx(tag, props = null, children = null) { + if (arguments.length > 3 || isVNode(children)) { + children = slice.call(arguments, 2); + } + return createVNode(tag, props, children); +}`; + +/** + * @param {string} content + * @param {'jsx' | 'ts' | 'tsx'} lang + * @param {string} [tsconfig] + */ +const esbuildCompile = (content, lang, tsconfig) => { + let result = ''; + if (['jsx', 'tsx'].includes(lang)) { + result += `${codeSnippetH}\n`; + result += `${codeSnippetVueJsxCompat}\n`; + } + const {js} = esbuild.transformSync(content, { + loader: lang, + tsconfig, + jsxFactory: tsconfig ? undefined : 'jsx', + jsxFragment: tsconfig ? undefined : 'Fragment', + }); + result += `\n${js.trim()}\n`; + return result.trim(); +}; + +module.exports = { + esbuildCompile, +}; diff --git a/plugins/plugin-vue/test/__snapshots__/plugin-tsx-jsx.test.js.snap b/plugins/plugin-vue/test/__snapshots__/plugin-tsx-jsx.test.js.snap new file mode 100644 index 0000000000..ae61e32409 --- /dev/null +++ b/plugins/plugin-vue/test/__snapshots__/plugin-tsx-jsx.test.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugin with jsx 1`] = ` +"import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"JsxContent\\", + setup() { + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"JsxContent\\")); + } +});" +`; + +exports[`plugin with tsx 1`] = ` +"import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"TsxContent\\", + setup() { + const name = \\"TsxContent\\"; + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"TsxContent\\")); + } +});" +`; diff --git a/plugins/plugin-vue/test/__snapshots__/plugin-vue-ts-tsx-jsx.test.js.snap b/plugins/plugin-vue/test/__snapshots__/plugin-vue-ts-tsx-jsx.test.js.snap new file mode 100644 index 0000000000..7615ec184c --- /dev/null +++ b/plugins/plugin-vue/test/__snapshots__/plugin-vue-ts-tsx-jsx.test.js.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugin vue with jsx 1`] = ` +Object { + ".css": Object { + "code": "", + "map": "", + }, + ".js": Object { + "code": "import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"VueContentJsx\\", + setup() { + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"VueContentJsx\\")); + } +});", + "map": "", + }, +} +`; + +exports[`plugin vue with ts 1`] = ` +Object { + ".css": Object { + "code": "", + "map": "", + }, + ".js": Object { + "code": "import {defineComponent} from \\"vue\\"; +const defaultExport = defineComponent({ + name: \\"VueContentTs\\", + setup() { + const name = \\"VueContentTs\\"; + } +}); +import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"/web_modules/vue.js\\" + +export function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"h1\\", null, \\"Vue Content Ts\\")) +} + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; + +exports[`plugin vue with tsx 1`] = ` +Object { + ".css": Object { + "code": "", + "map": "", + }, + ".js": Object { + "code": "import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"VueContentTsx\\", + setup() { + const name = \\"VueContentTsx\\"; + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"VueContentTsx\\")); + } +});", + "map": "", + }, +} +`; diff --git a/plugins/plugin-vue/test/__snapshots__/plugin.test.js.snap b/plugins/plugin-vue/test/__snapshots__/plugin.test.js.snap new file mode 100644 index 0000000000..546d07b838 --- /dev/null +++ b/plugins/plugin-vue/test/__snapshots__/plugin.test.js.snap @@ -0,0 +1,253 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugin base 1`] = ` +Object { + ".css": Object { + "code": " +.App { + text-align: center; +} +.App-header { + background-color: #f9f6f6; + color: #32485f; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} +.App-link { + color: #00c185; +} +.App-logo { + height: 40vmin; + pointer-events: none; + margin-bottom: 1rem; + animation: App-logo-spin infinite 1.6s ease-in-out alternate; +} +@keyframes App-logo-spin { +from { + transform: scale(1); +} +to { + transform: scale(1.06); +} +} +", + "map": "", + }, + ".js": Object { + "code": " +const defaultExport = { + data() { + return { + message: \\"Learn Vue\\" + }; + } +}; + +import { createVNode as _createVNode, createTextVNode as _createTextVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from \\"/web_modules/vue.js\\" + +const _hoisted_1 = { class: \\"App\\" } +const _hoisted_2 = { class: \\"App-header\\" } +const _hoisted_3 = /*#__PURE__*/_createVNode(\\"img\\", { + src: \\"/logo.svg\\", + class: \\"App-logo\\", + alt: \\"logo\\" +}, null, -1 /* HOISTED */) +const _hoisted_4 = /*#__PURE__*/_createVNode(\\"p\\", null, [ + /*#__PURE__*/_createTextVNode(\\" Edit \\"), + /*#__PURE__*/_createVNode(\\"code\\", null, \\"src/App.vue\\"), + /*#__PURE__*/_createTextVNode(\\" and save to reload. \\") +], -1 /* HOISTED */) +const _hoisted_5 = { + class: \\"App-link\\", + href: \\"https://vuejs.org\\", + target: \\"_blank\\", + rel: \\"noopener noreferrer\\" +} + +export function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [ + _createVNode(\\"header\\", _hoisted_2, [ + _hoisted_3, + _hoisted_4, + _createVNode(\\"a\\", _hoisted_5, _toDisplayString(_ctx.message), 1 /* TEXT */) + ]) + ])) +} + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; + +exports[`plugin base only tpl 1`] = ` +Object { + ".css": Object { + "code": "", + "map": "", + }, + ".js": Object { + "code": "const defaultExport = {}; +import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"/web_modules/vue.js\\" + +export function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"h1\\", null, \\"Vue Content Only Tpl\\")) +} + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; + +exports[`plugin base style scoped 1`] = ` +Object { + ".css": Object { + "code": " +h1[data-v-46843ce0] { + color: red; +} +", + "map": "", + }, + ".js": Object { + "code": " +const defaultExport = {}; + +import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"/web_modules/vue.js\\" +const _withId = /*#__PURE__*/_withScopeId(\\"data-v-46843ce0\\") + +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"h1\\", null, \\"Vue Content Style Scoped\\")) +}) + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; + +exports[`plugin base with error style 1`] = ` +Object { + ".css": Object { + "code": "", + "map": "", + }, + ".js": Object { + "code": " +const defaultExport = {}; + +import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"/web_modules/vue.js\\" + +export function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"h1\\", null, \\"Vue Content Error Style\\")) +} + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; + +exports[`plugin base with error tpl 1`] = ` +[Error: SyntaxError: Element is missing end tag. +[/Users/myouaki/Desktop/Develop/atome-fe/snowpack/plugins/plugin-vue/test/stubs/VueContentErrorTpl.vue] Line 2, Column 28 + + 1 | ] +`; + +exports[`plugin base with sourceMap 1`] = ` +Object { + ".css": Object { + "code": " +.App { + text-align: center; +} +.App-header { + background-color: #f9f6f6; + color: #32485f; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} +.App-link { + color: #00c185; +} +.App-logo { + height: 40vmin; + pointer-events: none; + margin-bottom: 1rem; + animation: App-logo-spin infinite 1.6s ease-in-out alternate; +} +@keyframes App-logo-spin { +from { + transform: scale(1); +} +to { + transform: scale(1.06); +} +} +", + "map": "", + }, + ".js": Object { + "code": " +const defaultExport = { + data() { + return { + message: \\"Learn Vue\\" + }; + } +}; + +import { createVNode as _createVNode, createTextVNode as _createTextVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from \\"/web_modules/vue.js\\" + +const _hoisted_1 = { class: \\"App\\" } +const _hoisted_2 = { class: \\"App-header\\" } +const _hoisted_3 = /*#__PURE__*/_createVNode(\\"img\\", { + src: \\"/logo.svg\\", + class: \\"App-logo\\", + alt: \\"logo\\" +}, null, -1 /* HOISTED */) +const _hoisted_4 = /*#__PURE__*/_createVNode(\\"p\\", null, [ + /*#__PURE__*/_createTextVNode(\\" Edit \\"), + /*#__PURE__*/_createVNode(\\"code\\", null, \\"src/App.vue\\"), + /*#__PURE__*/_createTextVNode(\\" and save to reload. \\") +], -1 /* HOISTED */) +const _hoisted_5 = { + class: \\"App-link\\", + href: \\"https://vuejs.org\\", + target: \\"_blank\\", + rel: \\"noopener noreferrer\\" +} + +export function render(_ctx, _cache) { + return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [ + _createVNode(\\"header\\", _hoisted_2, [ + _hoisted_3, + _hoisted_4, + _createVNode(\\"a\\", _hoisted_5, _toDisplayString(_ctx.message), 1 /* TEXT */) + ]) + ])) +} + +defaultExport.render = render +export default defaultExport", + "map": "", + }, +} +`; diff --git a/plugins/plugin-vue/test/__snapshots__/script-compilers.test.js.snap b/plugins/plugin-vue/test/__snapshots__/script-compilers.test.js.snap new file mode 100644 index 0000000000..6ac66b5624 --- /dev/null +++ b/plugins/plugin-vue/test/__snapshots__/script-compilers.test.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`esbuildCompile jsx 1`] = ` +"import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"JsxContent\\", + setup() { + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"JsxContent\\")); + } +});" +`; + +exports[`esbuildCompile ts 1`] = ` +"import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"TsContent\\", + setup() { + const name = \\"TsContent\\"; + } +});" +`; + +exports[`esbuildCompile tsx 1`] = ` +"import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"TsxContent\\", + setup() { + const name = \\"TsxContent\\"; + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"TsxContent\\")); + } +});" +`; + +exports[`esbuildCompile tsx with tsconfig 1`] = ` +"import { h, Fragment } from '/web_modules/vue.js'; +import {defineComponent} from \\"vue\\"; +export default defineComponent({ + name: \\"TsxContent\\", + setup() { + const name = \\"TsxContent\\"; + return () => /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(\\"h1\\", null, \\"TsxContent\\")); + } +});" +`; diff --git a/plugins/plugin-vue/test/plugin-tsx-jsx.test.js b/plugins/plugin-vue/test/plugin-tsx-jsx.test.js new file mode 100644 index 0000000000..2048a0930e --- /dev/null +++ b/plugins/plugin-vue/test/plugin-tsx-jsx.test.js @@ -0,0 +1,32 @@ +const path = require('path'); +const pluginTsxJsx = require('../plugin-tsx-jsx.js'); + +test('plugin with tsx', async () => { + const pluginInstance = pluginTsxJsx({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/TsxContent.tsx'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + fileExt: '.tsx', + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin with jsx', async () => { + const pluginInstance = pluginTsxJsx({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/JsxContent.jsx'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + fileExt: '.jsx', + }); + expect(resultContent).toMatchSnapshot(); +}); \ No newline at end of file diff --git a/plugins/plugin-vue/test/plugin-vue-ts-tsx-jsx.test.js b/plugins/plugin-vue/test/plugin-vue-ts-tsx-jsx.test.js new file mode 100644 index 0000000000..ac20928d1b --- /dev/null +++ b/plugins/plugin-vue/test/plugin-vue-ts-tsx-jsx.test.js @@ -0,0 +1,44 @@ +const path = require('path'); +const plugin = require('../plugin'); + +test('plugin vue with ts', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentTs.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin vue with tsx', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentTsx.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin vue with jsx', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentJsx.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); \ No newline at end of file diff --git a/plugins/plugin-vue/test/plugin.test.js b/plugins/plugin-vue/test/plugin.test.js new file mode 100644 index 0000000000..1b5243594a --- /dev/null +++ b/plugins/plugin-vue/test/plugin.test.js @@ -0,0 +1,94 @@ +const path = require('path'); +const plugin = require('../plugin'); + +test('plugin base', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: false, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContent.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin base with sourceMap', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContent.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin base only tpl', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentOnlyTpl.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin base style scoped', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentStyleScoped.vue'); + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); +}); + +test('plugin base with error tpl', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentErrorTpl.vue'); + try { + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); + } catch (error) { + expect(error).toMatchSnapshot(); + } +}); + +test('plugin base with error style', async () => { + const pluginInstance = plugin({ + buildOptions: { + sourceMap: true, + }, + }); + const pluginLoad = pluginInstance.load; + const codeFilePath = path.resolve(__dirname, './stubs/VueContentErrorStyle.vue'); + try { + const resultContent = await pluginLoad({ + filePath: codeFilePath, + }); + expect(resultContent).toMatchSnapshot(); + } catch (error) { + expect(error).toMatchSnapshot(); + } +}); \ No newline at end of file diff --git a/plugins/plugin-vue/test/script-compilers.test.js b/plugins/plugin-vue/test/script-compilers.test.js new file mode 100644 index 0000000000..5030770f79 --- /dev/null +++ b/plugins/plugin-vue/test/script-compilers.test.js @@ -0,0 +1,33 @@ +const fs = require('fs'); +const path = require('path'); +const scriptCompilers = require('../src/script-compilers'); + +test('esbuildCompile ts', () => { + const { esbuildCompile } = scriptCompilers; + const codeContent = fs.readFileSync(path.resolve(__dirname, './stubs/TsContent.ts')).toString(); + const resultContent = esbuildCompile(codeContent, 'ts'); + expect(resultContent).toMatchSnapshot(); +}); + +test('esbuildCompile tsx', () => { + const { esbuildCompile } = scriptCompilers; + const codeContent = fs.readFileSync(path.resolve(__dirname, './stubs/TsxContent.tsx')).toString(); + const resultContent = esbuildCompile(codeContent, 'tsx'); + expect(resultContent).toMatchSnapshot(); +}); + +test('esbuildCompile jsx', () => { + const { esbuildCompile } = scriptCompilers; + const codeContent = fs.readFileSync(path.resolve(__dirname, './stubs/JsxContent.jsx')).toString(); + const resultContent = esbuildCompile(codeContent, 'jsx'); + expect(resultContent).toMatchSnapshot(); +}); + +// BUG: https://github.com/evanw/esbuild/issues/366 +test.skip('esbuildCompile tsx with tsconfig', () => { + const { esbuildCompile } = scriptCompilers; + const tsconfigFilePath = path.join(__dirname, './stubs/tsconfig.json'); + const codeContent = fs.readFileSync(path.resolve(__dirname, './stubs/TsxContent.tsx')).toString(); + const resultContent = esbuildCompile(codeContent, 'tsx', tsconfigFilePath); + expect(resultContent).toMatchSnapshot(); +}); diff --git a/plugins/plugin-vue/test/stubs/JsxContent.jsx b/plugins/plugin-vue/test/stubs/JsxContent.jsx new file mode 100644 index 0000000000..c35780d78c --- /dev/null +++ b/plugins/plugin-vue/test/stubs/JsxContent.jsx @@ -0,0 +1,12 @@ +import {defineComponent} from 'vue'; + +export default defineComponent({ + name: 'JsxContent', + setup() { + return () => ( + <> +

JsxContent

+ + ); + }, +}); diff --git a/plugins/plugin-vue/test/stubs/TsContent.ts b/plugins/plugin-vue/test/stubs/TsContent.ts new file mode 100644 index 0000000000..21162e7e77 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/TsContent.ts @@ -0,0 +1,8 @@ +import {defineComponent} from 'vue'; + +export default defineComponent({ + name: 'TsContent', + setup() { + const name: string = 'TsContent'; + }, +}); diff --git a/plugins/plugin-vue/test/stubs/TsxContent.tsx b/plugins/plugin-vue/test/stubs/TsxContent.tsx new file mode 100644 index 0000000000..5fd1826a2d --- /dev/null +++ b/plugins/plugin-vue/test/stubs/TsxContent.tsx @@ -0,0 +1,13 @@ +import {defineComponent} from 'vue'; + +export default defineComponent({ + name: 'TsxContent', + setup() { + const name: string = 'TsxContent'; + return () => ( + <> +

TsxContent

+ + ); + }, +}); diff --git a/plugins/plugin-vue/test/stubs/VueContent.vue b/plugins/plugin-vue/test/stubs/VueContent.vue new file mode 100644 index 0000000000..5134670b00 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContent.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/plugins/plugin-vue/test/stubs/VueContentErrorStyle.vue b/plugins/plugin-vue/test/stubs/VueContentErrorStyle.vue new file mode 100644 index 0000000000..6883302f5d --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentErrorStyle.vue @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentErrorTpl.vue b/plugins/plugin-vue/test/stubs/VueContentErrorTpl.vue new file mode 100644 index 0000000000..30cd6164ea --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentErrorTpl.vue @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentJsx.vue b/plugins/plugin-vue/test/stubs/VueContentJsx.vue new file mode 100644 index 0000000000..375a38f291 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentJsx.vue @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentOnlyTpl.vue b/plugins/plugin-vue/test/stubs/VueContentOnlyTpl.vue new file mode 100644 index 0000000000..a443d83225 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentOnlyTpl.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentStyleScoped.vue b/plugins/plugin-vue/test/stubs/VueContentStyleScoped.vue new file mode 100644 index 0000000000..802a70c90d --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentStyleScoped.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentTs.vue b/plugins/plugin-vue/test/stubs/VueContentTs.vue new file mode 100644 index 0000000000..0c46f9a3b1 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentTs.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/VueContentTsx.vue b/plugins/plugin-vue/test/stubs/VueContentTsx.vue new file mode 100644 index 0000000000..8f2c03fdfa --- /dev/null +++ b/plugins/plugin-vue/test/stubs/VueContentTsx.vue @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/plugins/plugin-vue/test/stubs/tsconfig.json b/plugins/plugin-vue/test/stubs/tsconfig.json new file mode 100644 index 0000000000..2613afea43 --- /dev/null +++ b/plugins/plugin-vue/test/stubs/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment" + } +}