diff --git a/.changeset/real-cars-chew.md b/.changeset/real-cars-chew.md
new file mode 100644
index 000000000000..8cefb52452e0
--- /dev/null
+++ b/.changeset/real-cars-chew.md
@@ -0,0 +1,7 @@
+---
+'@modern-js/builder-plugin-vue2': patch
+---
+
+feat(builder): add new builder-plugin-vue2
+
+feat(builder): 新增 builder-plugin-vue2 插件
diff --git a/packages/builder/plugin-vue2/.eslintrc.js b/packages/builder/plugin-vue2/.eslintrc.js
new file mode 100644
index 000000000000..f9da2e208b89
--- /dev/null
+++ b/packages/builder/plugin-vue2/.eslintrc.js
@@ -0,0 +1,11 @@
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ extends: ['@modern-js'],
+ ignorePatterns: ['vitest.config.ts'],
+ parserOptions: {
+ project: require.resolve('./tsconfig.json'),
+ },
+ rules: {
+ 'import/order': 0,
+ },
+};
diff --git a/packages/builder/plugin-vue2/.npmignore b/packages/builder/plugin-vue2/.npmignore
new file mode 100644
index 000000000000..5faf67231f9d
--- /dev/null
+++ b/packages/builder/plugin-vue2/.npmignore
@@ -0,0 +1,23 @@
+.DS_Store
+
+*.log*
+*.pid
+*.pid.*
+*.report
+
+node_modules/
+.nyc_output
+*.tsbuildinfo
+.eslintcache
+.sonarlint
+
+coverage/
+tests/
+
+src/
+
+modern.config.js
+modern.config.ts
+vitest.config.ts
+.eslintrc.js
+tsconfig.json
diff --git a/packages/builder/plugin-vue2/CHANGELOG.md b/packages/builder/plugin-vue2/CHANGELOG.md
new file mode 100644
index 000000000000..262b999e9377
--- /dev/null
+++ b/packages/builder/plugin-vue2/CHANGELOG.md
@@ -0,0 +1 @@
+# @modern-js/builder-plugin-vue2
diff --git a/packages/builder/plugin-vue2/LICENSE b/packages/builder/plugin-vue2/LICENSE
new file mode 100644
index 000000000000..a33f52674491
--- /dev/null
+++ b/packages/builder/plugin-vue2/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Modern.js
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/builder/plugin-vue2/README.md b/packages/builder/plugin-vue2/README.md
new file mode 100644
index 000000000000..0c1cc1d4e71f
--- /dev/null
+++ b/packages/builder/plugin-vue2/README.md
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Modern.js Builder
+
+
+ modernjs.dev/builder
+
+
+
+ A build engine for web development.
+
+
+# @modern-js/builder-plugin-vue2
+
+This package is the Vue 2 plugin of Modern.js Builder.
+
+## Getting Started
+
+- [Documentation](https://modernjs.dev/builder/en/plugins/plugin-vue2.html)
+
+## Contributing
+
+- [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md)
diff --git a/packages/builder/plugin-vue2/modern.config.js b/packages/builder/plugin-vue2/modern.config.js
new file mode 100644
index 000000000000..c2bf3e85b393
--- /dev/null
+++ b/packages/builder/plugin-vue2/modern.config.js
@@ -0,0 +1,5 @@
+const { tscLikeBuildConfig } = require('@scripts/build');
+
+module.exports = {
+ buildConfig: tscLikeBuildConfig,
+};
diff --git a/packages/builder/plugin-vue2/package.json b/packages/builder/plugin-vue2/package.json
new file mode 100644
index 000000000000..ef102fbb0efc
--- /dev/null
+++ b/packages/builder/plugin-vue2/package.json
@@ -0,0 +1,75 @@
+{
+ "name": "@modern-js/builder-plugin-vue2",
+ "description": "Vue 2 plugin of Modern.js Builder",
+ "homepage": "https://modernjs.dev/builder",
+ "bugs": "https://github.com/web-infra-dev/modern.js/issues",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/web-infra-dev/modern.js",
+ "directory": "packages/builder/plugin-vue"
+ },
+ "license": "MIT",
+ "keywords": [
+ "react",
+ "framework",
+ "modern",
+ "modern.js"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "version": "2.26.0",
+ "jsnext:source": "./src/index.ts",
+ "types": "./src/index.ts",
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "exports": {
+ ".": {
+ "jsnext:source": "./src/index.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "scripts": {
+ "prepublishOnly": "only-allow-pnpm",
+ "new": "modern new",
+ "build": "modern-lib build",
+ "dev": "modern-lib build --watch",
+ "test": "vitest run",
+ "test:watch": "vitest dev --no-coverage"
+ },
+ "dependencies": {
+ "@modern-js/builder-shared": "workspace:*",
+ "@swc/helpers": "0.5.1",
+ "@vue/babel-preset-jsx": "^1.4.0",
+ "vue-loader": "^15.10.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.21.8",
+ "@modern-js/builder": "workspace:*",
+ "@modern-js/builder-webpack-provider": "workspace:*",
+ "@modern-js/builder-rspack-provider": "workspace:*",
+ "@modern-js/utils": "workspace:*",
+ "@scripts/build": "workspace:*",
+ "@scripts/vitest-config": "workspace:*",
+ "typescript": "^5",
+ "webpack": "^5.88.1"
+ },
+ "peerDependencies": {
+ "@modern-js/builder-webpack-provider": "workspace:^2.26.0",
+ "@modern-js/builder-rspack-provider": "workspace:^2.26.0"
+ },
+ "peerDependenciesMeta": {
+ "@modern-js/builder-webpack-provider": {
+ "optional": true
+ },
+ "@modern-js/builder-rspack-provider": {
+ "optional": true
+ }
+ },
+ "sideEffects": false,
+ "publishConfig": {
+ "registry": "https://registry.npmjs.org/",
+ "access": "public",
+ "types": "./dist/index.d.ts"
+ }
+}
diff --git a/packages/builder/plugin-vue2/src/index.ts b/packages/builder/plugin-vue2/src/index.ts
new file mode 100644
index 000000000000..f6b4b958fd23
--- /dev/null
+++ b/packages/builder/plugin-vue2/src/index.ts
@@ -0,0 +1,79 @@
+import { merge as deepMerge } from '@modern-js/utils/lodash';
+import { VueLoaderPlugin } from 'vue-loader';
+import type { BuilderPlugin } from '@modern-js/builder';
+import type { BuilderPluginAPI } from '@modern-js/builder-webpack-provider';
+import type { VueLoaderOptions } from 'vue-loader';
+
+type VueJSXPresetOptions = {
+ compositionAPI?: boolean | string;
+ functional?: boolean;
+ injectH?: boolean;
+ vModel?: boolean;
+ vOn?: boolean;
+};
+
+export type PluginVueOptions = {
+ vueJsxOptions?: VueJSXPresetOptions;
+ vueLoaderOptions?: VueLoaderOptions;
+};
+
+export function builderPluginVue2(
+ options: PluginVueOptions = {},
+): BuilderPlugin {
+ return {
+ name: 'builder-plugin-vue2',
+
+ // Remove built-in react plugins.
+ // These plugins should be moved to a separate package in the next major version.
+ remove: [
+ 'builder-plugin-react',
+ 'builder-plugin-antd',
+ 'builder-plugin-arco',
+ ],
+
+ async setup(api) {
+ api.modifyBuilderConfig((config, { mergeBuilderConfig }) => {
+ return mergeBuilderConfig(config, {
+ output: {
+ disableSvgr: true,
+ },
+ tools: {
+ babel(_, { addPresets }) {
+ addPresets([
+ [
+ require.resolve('@vue/babel-preset-jsx'),
+ {
+ injectH: true,
+ ...options.vueJsxOptions,
+ },
+ ],
+ ]);
+ },
+ },
+ });
+ });
+
+ api.modifyBundlerChain(async (chain, { CHAIN_ID }) => {
+ chain.resolve.extensions.add('.vue');
+
+ const vueLoaderOptions = deepMerge(
+ {
+ compilerOptions: {
+ preserveWhitespace: false,
+ },
+ },
+ options.vueLoaderOptions,
+ );
+
+ chain.module
+ .rule(CHAIN_ID.RULE.VUE)
+ .test(/\.vue$/)
+ .use(CHAIN_ID.USE.VUE)
+ .loader(require.resolve('vue-loader'))
+ .options(vueLoaderOptions);
+
+ chain.plugin(CHAIN_ID.PLUGIN.VUE_LOADER_PLUGIN).use(VueLoaderPlugin);
+ });
+ },
+ };
+}
diff --git a/packages/builder/plugin-vue2/tests/.eslintrc.js b/packages/builder/plugin-vue2/tests/.eslintrc.js
new file mode 100644
index 000000000000..8ad708bcff41
--- /dev/null
+++ b/packages/builder/plugin-vue2/tests/.eslintrc.js
@@ -0,0 +1,6 @@
+module.exports = {
+ extends: ['@modern-js'],
+ parserOptions: {
+ project: require.resolve('./tsconfig.json'),
+ },
+};
diff --git a/packages/builder/plugin-vue2/tests/__snapshots__/index.test.ts.snap b/packages/builder/plugin-vue2/tests/__snapshots__/index.test.ts.snap
new file mode 100644
index 000000000000..c444d19689fb
--- /dev/null
+++ b/packages/builder/plugin-vue2/tests/__snapshots__/index.test.ts.snap
@@ -0,0 +1,556 @@
+// Vitest Snapshot v1
+
+exports[`plugins/vue > should add vue-loader and VueLoaderPlugin correctly 1`] = `
+{
+ "module": {
+ "rules": [
+ {
+ "test": /\\\\\\.vue\\$/,
+ "use": [
+ {
+ "loader": "/node_modules//vue-loader/lib/index.js",
+ "options": {
+ "compilerOptions": {
+ "preserveWhitespace": false,
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ "plugins": [
+ VueLoaderPlugin {},
+ ],
+ "resolve": {
+ "extensions": [
+ ".vue",
+ ],
+ },
+}
+`;
+
+exports[`plugins/vue > should allow to configure jsx babel plugin options 1`] = `
+{
+ "module": {
+ "rules": [
+ {
+ "test": /\\\\\\.vue\\$/,
+ "use": [
+ {
+ "loader": "/node_modules//vue-loader/lib/index.js",
+ "options": {
+ "compilerOptions": {
+ "preserveWhitespace": false,
+ },
+ },
+ },
+ ],
+ },
+ {
+ "include": [
+ {
+ "and": [
+ "",
+ {
+ "not": /node_modules/,
+ },
+ ],
+ },
+ ],
+ "test": /\\\\\\.\\(js\\|mjs\\|cjs\\|jsx\\)\\$\\|\\\\\\.\\(ts\\|mts\\|cts\\|tsx\\)\\$/,
+ "use": [
+ {
+ "loader": "/packages/builder/builder-shared/compiled/babel-loader",
+ "options": {
+ "babelrc": false,
+ "compact": false,
+ "configFile": false,
+ "plugins": [
+ [
+ "/packages/cli/babel-preset-app/src/babelPluginLockCorejsVersion",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-dynamic-import-node/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-lodash/index.js",
+ {},
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-decorators/index.js",
+ {
+ "legacy": true,
+ },
+ ],
+ [
+ "/node_modules//@babel/plugin-transform-runtime/lib/index.js",
+ {
+ "helpers": false,
+ "regenerator": true,
+ "useESModules": true,
+ "version": "7.21.5",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-export-default-from/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-pipeline-operator/index.js",
+ {
+ "proposal": "minimal",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-partial-application/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-styled-components/index.js",
+ {
+ "displayName": true,
+ "pure": false,
+ "ssr": false,
+ "transpileTemplateLiterals": true,
+ },
+ "styled-components",
+ ],
+ ],
+ "presets": [
+ [
+ "/node_modules//@babel/preset-env/lib/index.js",
+ {
+ "bugfixes": false,
+ "corejs": {
+ "proposals": true,
+ "version": "3.30",
+ },
+ "exclude": [
+ "transform-typeof-symbol",
+ ],
+ "modules": false,
+ "shippedProposals": false,
+ "targets": [
+ "> 0.01%",
+ "not dead",
+ "not op_mini all",
+ ],
+ "useBuiltIns": "entry",
+ },
+ ],
+ [
+ "/node_modules//@babel/preset-typescript/lib/index.js",
+ {
+ "allExtensions": true,
+ "allowDeclareFields": true,
+ "allowNamespaces": true,
+ "isTSX": true,
+ "optimizeConstEnums": true,
+ },
+ ],
+ [
+ "/node_modules//@vue/babel-preset-jsx/dist/plugin.cjs.js",
+ {
+ "injectH": false,
+ },
+ ],
+ ],
+ },
+ },
+ ],
+ },
+ {
+ "mimetype": {
+ "or": [
+ "text/javascript",
+ "application/javascript",
+ ],
+ },
+ "use": [
+ {
+ "loader": "/packages/builder/builder-shared/compiled/babel-loader",
+ "options": {
+ "babelrc": false,
+ "compact": false,
+ "configFile": false,
+ "plugins": [
+ [
+ "/packages/cli/babel-preset-app/src/babelPluginLockCorejsVersion",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-dynamic-import-node/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-lodash/index.js",
+ {},
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-decorators/index.js",
+ {
+ "legacy": true,
+ },
+ ],
+ [
+ "/node_modules//@babel/plugin-transform-runtime/lib/index.js",
+ {
+ "helpers": false,
+ "regenerator": true,
+ "useESModules": true,
+ "version": "7.21.5",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-export-default-from/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-pipeline-operator/index.js",
+ {
+ "proposal": "minimal",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-partial-application/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-styled-components/index.js",
+ {
+ "displayName": true,
+ "pure": false,
+ "ssr": false,
+ "transpileTemplateLiterals": true,
+ },
+ "styled-components",
+ ],
+ ],
+ "presets": [
+ [
+ "/node_modules//@babel/preset-env/lib/index.js",
+ {
+ "bugfixes": false,
+ "corejs": {
+ "proposals": true,
+ "version": "3.30",
+ },
+ "exclude": [
+ "transform-typeof-symbol",
+ ],
+ "modules": false,
+ "shippedProposals": false,
+ "targets": [
+ "> 0.01%",
+ "not dead",
+ "not op_mini all",
+ ],
+ "useBuiltIns": "entry",
+ },
+ ],
+ [
+ "/node_modules//@babel/preset-typescript/lib/index.js",
+ {
+ "allExtensions": true,
+ "allowDeclareFields": true,
+ "allowNamespaces": true,
+ "isTSX": true,
+ "optimizeConstEnums": true,
+ },
+ ],
+ [
+ "/node_modules//@vue/babel-preset-jsx/dist/plugin.cjs.js",
+ {
+ "injectH": false,
+ },
+ ],
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ "plugins": [
+ VueLoaderPlugin {},
+ ],
+ "resolve": {
+ "extensions": [
+ ".vue",
+ ],
+ },
+}
+`;
+
+exports[`plugins/vue > should allow to configure vueLoader options 1`] = `
+{
+ "module": {
+ "rules": [
+ {
+ "test": /\\\\\\.vue\\$/,
+ "use": [
+ {
+ "loader": "/node_modules//vue-loader/lib/index.js",
+ "options": {
+ "compilerOptions": {
+ "preserveWhitespace": false,
+ },
+ "hotReload": false,
+ },
+ },
+ ],
+ },
+ ],
+ },
+ "plugins": [
+ VueLoaderPlugin {},
+ ],
+ "resolve": {
+ "extensions": [
+ ".vue",
+ ],
+ },
+}
+`;
+
+exports[`plugins/vue > should apply jsx babel plugin correctly 1`] = `
+{
+ "module": {
+ "rules": [
+ {
+ "test": /\\\\\\.vue\\$/,
+ "use": [
+ {
+ "loader": "/node_modules//vue-loader/lib/index.js",
+ "options": {
+ "compilerOptions": {
+ "preserveWhitespace": false,
+ },
+ },
+ },
+ ],
+ },
+ {
+ "include": [
+ {
+ "and": [
+ "",
+ {
+ "not": /node_modules/,
+ },
+ ],
+ },
+ ],
+ "test": /\\\\\\.\\(js\\|mjs\\|cjs\\|jsx\\)\\$\\|\\\\\\.\\(ts\\|mts\\|cts\\|tsx\\)\\$/,
+ "use": [
+ {
+ "loader": "/packages/builder/builder-shared/compiled/babel-loader",
+ "options": {
+ "babelrc": false,
+ "compact": false,
+ "configFile": false,
+ "plugins": [
+ [
+ "/packages/cli/babel-preset-app/src/babelPluginLockCorejsVersion",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-dynamic-import-node/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-lodash/index.js",
+ {},
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-decorators/index.js",
+ {
+ "legacy": true,
+ },
+ ],
+ [
+ "/node_modules//@babel/plugin-transform-runtime/lib/index.js",
+ {
+ "helpers": false,
+ "regenerator": true,
+ "useESModules": true,
+ "version": "7.21.5",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-export-default-from/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-pipeline-operator/index.js",
+ {
+ "proposal": "minimal",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-partial-application/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-styled-components/index.js",
+ {
+ "displayName": true,
+ "pure": false,
+ "ssr": false,
+ "transpileTemplateLiterals": true,
+ },
+ "styled-components",
+ ],
+ ],
+ "presets": [
+ [
+ "/node_modules//@babel/preset-env/lib/index.js",
+ {
+ "bugfixes": false,
+ "corejs": {
+ "proposals": true,
+ "version": "3.30",
+ },
+ "exclude": [
+ "transform-typeof-symbol",
+ ],
+ "modules": false,
+ "shippedProposals": false,
+ "targets": [
+ "> 0.01%",
+ "not dead",
+ "not op_mini all",
+ ],
+ "useBuiltIns": "entry",
+ },
+ ],
+ [
+ "/node_modules//@babel/preset-typescript/lib/index.js",
+ {
+ "allExtensions": true,
+ "allowDeclareFields": true,
+ "allowNamespaces": true,
+ "isTSX": true,
+ "optimizeConstEnums": true,
+ },
+ ],
+ [
+ "/node_modules//@vue/babel-preset-jsx/dist/plugin.cjs.js",
+ {
+ "injectH": true,
+ },
+ ],
+ ],
+ },
+ },
+ ],
+ },
+ {
+ "mimetype": {
+ "or": [
+ "text/javascript",
+ "application/javascript",
+ ],
+ },
+ "use": [
+ {
+ "loader": "/packages/builder/builder-shared/compiled/babel-loader",
+ "options": {
+ "babelrc": false,
+ "compact": false,
+ "configFile": false,
+ "plugins": [
+ [
+ "/packages/cli/babel-preset-app/src/babelPluginLockCorejsVersion",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-dynamic-import-node/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-lodash/index.js",
+ {},
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-decorators/index.js",
+ {
+ "legacy": true,
+ },
+ ],
+ [
+ "/node_modules//@babel/plugin-transform-runtime/lib/index.js",
+ {
+ "helpers": false,
+ "regenerator": true,
+ "useESModules": true,
+ "version": "7.21.5",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-export-default-from/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-pipeline-operator/index.js",
+ {
+ "proposal": "minimal",
+ },
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/@babel/plugin-proposal-partial-application/index.js",
+ ],
+ [
+ "/packages/cli/babel-preset-base/compiled/babel-plugin-styled-components/index.js",
+ {
+ "displayName": true,
+ "pure": false,
+ "ssr": false,
+ "transpileTemplateLiterals": true,
+ },
+ "styled-components",
+ ],
+ ],
+ "presets": [
+ [
+ "/node_modules//@babel/preset-env/lib/index.js",
+ {
+ "bugfixes": false,
+ "corejs": {
+ "proposals": true,
+ "version": "3.30",
+ },
+ "exclude": [
+ "transform-typeof-symbol",
+ ],
+ "modules": false,
+ "shippedProposals": false,
+ "targets": [
+ "> 0.01%",
+ "not dead",
+ "not op_mini all",
+ ],
+ "useBuiltIns": "entry",
+ },
+ ],
+ [
+ "/node_modules//@babel/preset-typescript/lib/index.js",
+ {
+ "allExtensions": true,
+ "allowDeclareFields": true,
+ "allowNamespaces": true,
+ "isTSX": true,
+ "optimizeConstEnums": true,
+ },
+ ],
+ [
+ "/node_modules//@vue/babel-preset-jsx/dist/plugin.cjs.js",
+ {
+ "injectH": true,
+ },
+ ],
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ "plugins": [
+ VueLoaderPlugin {},
+ ],
+ "resolve": {
+ "extensions": [
+ ".vue",
+ ],
+ },
+}
+`;
diff --git a/packages/builder/plugin-vue2/tests/index.test.ts b/packages/builder/plugin-vue2/tests/index.test.ts
new file mode 100644
index 000000000000..c26a51e54d08
--- /dev/null
+++ b/packages/builder/plugin-vue2/tests/index.test.ts
@@ -0,0 +1,55 @@
+import { expect, describe, it } from 'vitest';
+import { createStubBuilder } from '@modern-js/builder-webpack-provider/stub';
+import { builderPluginBabel } from '@modern-js/builder-webpack-provider/plugins/babel';
+import { builderPluginVue2 } from '../src';
+
+describe('plugins/vue', () => {
+ it('should add vue-loader and VueLoaderPlugin correctly', async () => {
+ const builder = await createStubBuilder({
+ plugins: [builderPluginVue2()],
+ });
+ const config = await builder.unwrapWebpackConfig();
+
+ expect(config).toMatchSnapshot();
+ });
+
+ it('should allow to configure vueLoader options', async () => {
+ const builder = await createStubBuilder({
+ plugins: [
+ builderPluginVue2({
+ vueLoaderOptions: {
+ hotReload: false,
+ },
+ }),
+ ],
+ });
+ const config = await builder.unwrapWebpackConfig();
+
+ expect(config).toMatchSnapshot();
+ });
+
+ it('should apply jsx babel plugin correctly', async () => {
+ const builder = await createStubBuilder({
+ plugins: [builderPluginVue2(), builderPluginBabel()],
+ });
+ const config = await builder.unwrapWebpackConfig();
+
+ expect(config).toMatchSnapshot();
+ });
+
+ it('should allow to configure jsx babel plugin options', async () => {
+ const builder = await createStubBuilder({
+ plugins: [
+ builderPluginVue2({
+ vueJsxOptions: {
+ injectH: false,
+ },
+ }),
+ builderPluginBabel(),
+ ],
+ });
+ const config = await builder.unwrapWebpackConfig();
+
+ expect(config).toMatchSnapshot();
+ });
+});
diff --git a/packages/builder/plugin-vue2/tests/setup.ts b/packages/builder/plugin-vue2/tests/setup.ts
new file mode 100644
index 000000000000..f1f04eaf82fe
--- /dev/null
+++ b/packages/builder/plugin-vue2/tests/setup.ts
@@ -0,0 +1,4 @@
+import { expect } from 'vitest';
+import { createSnapshotSerializer } from '@scripts/vitest-config';
+
+expect.addSnapshotSerializer(createSnapshotSerializer());
diff --git a/packages/builder/plugin-vue2/tests/tsconfig.json b/packages/builder/plugin-vue2/tests/tsconfig.json
new file mode 100644
index 000000000000..787a882891b3
--- /dev/null
+++ b/packages/builder/plugin-vue2/tests/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "@modern-js/tsconfig/base",
+ "compilerOptions": {
+ "noEmit": true,
+ "declaration": false,
+ "jsx": "preserve",
+ "baseUrl": "./",
+ "isolatedModules": true,
+ "paths": {
+ "@rspack-builder/tests/*": ["../../builder-rspack-provider/tests/*"]
+ }
+ }
+}
diff --git a/packages/builder/plugin-vue2/tsconfig.json b/packages/builder/plugin-vue2/tsconfig.json
new file mode 100644
index 000000000000..104083062e4b
--- /dev/null
+++ b/packages/builder/plugin-vue2/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "@modern-js/tsconfig/base",
+ "compilerOptions": {
+ "target": "ES2019",
+ "declaration": true,
+ "outDir": "./dist",
+ "jsx": "preserve",
+ "baseUrl": "./",
+ "isolatedModules": true,
+ "paths": {},
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/builder/plugin-vue2/vitest.config.ts b/packages/builder/plugin-vue2/vitest.config.ts
new file mode 100644
index 000000000000..62b26b26b8e0
--- /dev/null
+++ b/packages/builder/plugin-vue2/vitest.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vitest/config';
+import { withTestPreset } from '@scripts/vitest-config';
+
+const config = defineConfig({
+ test: {
+ root: __dirname,
+ environment: 'node',
+ setupFiles: ['./tests/setup.ts'],
+ },
+});
+
+export default withTestPreset(config);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ae1d452d3ad8..cedac5f1427d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -709,6 +709,49 @@ importers:
specifier: ^5.88.1
version: 5.88.1(esbuild@0.17.19)(webpack-cli@5.1.4)
+ packages/builder/plugin-vue2:
+ dependencies:
+ '@modern-js/builder-shared':
+ specifier: workspace:*
+ version: link:../builder-shared
+ '@swc/helpers':
+ specifier: 0.5.1
+ version: 0.5.1
+ '@vue/babel-preset-jsx':
+ specifier: ^1.4.0
+ version: 1.4.0(@babel/core@7.21.8)
+ vue-loader:
+ specifier: ^15.10.1
+ version: 15.10.1(css-loader@6.7.1)(webpack@5.88.1)
+ devDependencies:
+ '@babel/core':
+ specifier: ^7.21.8
+ version: 7.21.8
+ '@modern-js/builder':
+ specifier: workspace:*
+ version: link:../builder
+ '@modern-js/builder-rspack-provider':
+ specifier: workspace:*
+ version: link:../builder-rspack-provider
+ '@modern-js/builder-webpack-provider':
+ specifier: workspace:*
+ version: link:../builder-webpack-provider
+ '@modern-js/utils':
+ specifier: workspace:*
+ version: link:../../toolkit/utils
+ '@scripts/build':
+ specifier: workspace:*
+ version: link:../../../scripts/build
+ '@scripts/vitest-config':
+ specifier: workspace:*
+ version: link:../../../scripts/vitest-config
+ typescript:
+ specifier: ^5
+ version: 5.0.4
+ webpack:
+ specifier: ^5.88.1
+ version: 5.88.1(esbuild@0.17.19)(webpack-cli@5.1.4)
+
packages/cli/babel-preset-app:
dependencies:
'@babel/core':
@@ -6046,6 +6089,9 @@ importers:
'@modern-js/builder-plugin-vue':
specifier: workspace:*
version: link:../../../packages/builder/plugin-vue
+ '@modern-js/builder-plugin-vue2':
+ specifier: workspace:*
+ version: link:../../../packages/builder/plugin-vue2
'@modern-js/builder-rspack-provider':
specifier: workspace:*
version: link:../../../packages/builder/builder-rspack-provider
@@ -6109,6 +6155,9 @@ importers:
typescript:
specifier: ^5
version: 5.0.4
+ vue:
+ specifier: ^3.3.4
+ version: 3.3.4
tests/e2e/builder/cases/builder-cli/builder-cli-webpack:
devDependencies:
@@ -6167,6 +6216,18 @@ importers:
specifier: ^2
version: 2.29.3
+ tests/e2e/builder/cases/vue:
+ dependencies:
+ vue:
+ specifier: ^3.3.4
+ version: 3.3.4
+
+ tests/e2e/builder/cases/vue2:
+ dependencies:
+ vue:
+ specifier: ^2.7.14
+ version: 2.7.14
+
tests/e2e/garfish:
dependencies:
react:
@@ -15754,6 +15815,10 @@ packages:
sirv: 2.0.2
dev: true
+ /@vue/babel-helper-vue-jsx-merge-props@1.4.0:
+ resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==}
+ dev: false
+
/@vue/babel-helper-vue-transform-on@1.0.2:
resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==}
dev: false
@@ -15775,6 +15840,101 @@ packages:
- supports-color
dev: false
+ /@vue/babel-plugin-transform-vue-jsx@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/helper-module-imports': 7.21.4
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+ html-tags: 2.0.0
+ lodash.kebabcase: 4.1.1
+ svg-tags: 1.0.0
+ dev: false
+
+ /@vue/babel-preset-jsx@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ vue: '*'
+ peerDependenciesMeta:
+ vue:
+ optional: true
+ dependencies:
+ '@babel/core': 7.21.8
+ '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+ '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-composition-api-inject-h': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-composition-api-render-instance': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-functional-vue': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-inject-h': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-v-model': 1.4.0(@babel/core@7.21.8)
+ '@vue/babel-sugar-v-on': 1.4.0(@babel/core@7.21.8)
+ dev: false
+
+ /@vue/babel-sugar-composition-api-inject-h@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ dev: false
+
+ /@vue/babel-sugar-composition-api-render-instance@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ dev: false
+
+ /@vue/babel-sugar-functional-vue@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ dev: false
+
+ /@vue/babel-sugar-inject-h@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ dev: false
+
+ /@vue/babel-sugar-v-model@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+ '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.21.8)
+ camelcase: 5.3.1
+ html-tags: 2.0.0
+ svg-tags: 1.0.0
+ dev: false
+
+ /@vue/babel-sugar-v-on@1.4.0(@babel/core@7.21.8):
+ resolution: {integrity: sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.21.8
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8)
+ '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.21.8)
+ camelcase: 5.3.1
+ dev: false
+
/@vue/compiler-core@3.3.4:
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
dependencies:
@@ -15782,13 +15942,19 @@ packages:
'@vue/shared': 3.3.4
estree-walker: 2.0.2
source-map-js: 1.0.2
- dev: false
/@vue/compiler-dom@3.3.4:
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
dependencies:
'@vue/compiler-core': 3.3.4
'@vue/shared': 3.3.4
+
+ /@vue/compiler-sfc@2.7.14:
+ resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
+ dependencies:
+ '@babel/parser': 7.21.8
+ postcss: 8.4.21
+ source-map: 0.6.1
dev: false
/@vue/compiler-sfc@3.3.4:
@@ -15805,13 +15971,80 @@ packages:
magic-string: 0.30.0
postcss: 8.4.21
source-map-js: 1.0.2
- dev: false
/@vue/compiler-ssr@3.3.4:
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
dependencies:
'@vue/compiler-dom': 3.3.4
'@vue/shared': 3.3.4
+
+ /@vue/component-compiler-utils@3.3.0:
+ resolution: {integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==}
+ dependencies:
+ consolidate: 0.15.1
+ hash-sum: 1.0.2
+ lru-cache: 4.1.5
+ merge-source-map: 1.1.0
+ postcss: 7.0.39
+ postcss-selector-parser: 6.0.11
+ source-map: 0.6.1
+ vue-template-es2015-compiler: 1.9.1
+ optionalDependencies:
+ prettier: 2.8.7
+ transitivePeerDependencies:
+ - arc-templates
+ - atpl
+ - babel-core
+ - bracket-template
+ - coffee-script
+ - dot
+ - dust
+ - dustjs-helpers
+ - dustjs-linkedin
+ - eco
+ - ect
+ - ejs
+ - haml-coffee
+ - hamlet
+ - hamljs
+ - handlebars
+ - hogan.js
+ - htmling
+ - jade
+ - jazz
+ - jqtpl
+ - just
+ - liquid-node
+ - liquor
+ - lodash
+ - marko
+ - mote
+ - mustache
+ - nunjucks
+ - plates
+ - pug
+ - qejs
+ - ractive
+ - razor-tmpl
+ - react
+ - react-dom
+ - slm
+ - squirrelly
+ - swig
+ - swig-templates
+ - teacup
+ - templayed
+ - then-jade
+ - then-pug
+ - tinyliquid
+ - toffee
+ - twig
+ - twing
+ - underscore
+ - vash
+ - velocityjs
+ - walrus
+ - whiskers
dev: false
/@vue/reactivity-transform@3.3.4:
@@ -15822,20 +16055,17 @@ packages:
'@vue/shared': 3.3.4
estree-walker: 2.0.2
magic-string: 0.30.0
- dev: false
/@vue/reactivity@3.3.4:
resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==}
dependencies:
'@vue/shared': 3.3.4
- dev: false
/@vue/runtime-core@3.3.4:
resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==}
dependencies:
'@vue/reactivity': 3.3.4
'@vue/shared': 3.3.4
- dev: false
/@vue/runtime-dom@3.3.4:
resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==}
@@ -15843,7 +16073,6 @@ packages:
'@vue/runtime-core': 3.3.4
'@vue/shared': 3.3.4
csstype: 3.1.2
- dev: false
/@vue/server-renderer@3.3.4(vue@3.3.4):
resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==}
@@ -15853,11 +16082,9 @@ packages:
'@vue/compiler-ssr': 3.3.4
'@vue/shared': 3.3.4
vue: 3.3.4
- dev: false
/@vue/shared@3.3.4:
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
- dev: false
/@web3-storage/multipart-parser@1.0.0:
resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==}
@@ -18331,6 +18558,175 @@ packages:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
dev: false
+ /consolidate@0.15.1:
+ resolution: {integrity: sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==}
+ engines: {node: '>= 0.10.0'}
+ deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog
+ peerDependencies:
+ arc-templates: ^0.5.3
+ atpl: '>=0.7.6'
+ babel-core: ^6.26.3
+ bracket-template: ^1.1.5
+ coffee-script: ^1.12.7
+ dot: ^1.1.3
+ dust: ^0.3.0
+ dustjs-helpers: ^1.7.4
+ dustjs-linkedin: ^2.7.5
+ eco: ^1.1.0-rc-3
+ ect: ^0.5.9
+ ejs: ^3.1.5
+ haml-coffee: ^1.14.1
+ hamlet: ^0.3.3
+ hamljs: ^0.6.2
+ handlebars: ^4.7.6
+ hogan.js: ^3.0.2
+ htmling: ^0.0.8
+ jade: ^1.11.0
+ jazz: ^0.0.18
+ jqtpl: ~1.1.0
+ just: ^0.1.8
+ liquid-node: ^3.0.1
+ liquor: ^0.0.5
+ lodash: ^4.17.20
+ marko: ^3.14.4
+ mote: ^0.2.0
+ mustache: ^3.0.0
+ nunjucks: ^3.2.2
+ plates: ~0.4.11
+ pug: ^3.0.0
+ qejs: ^3.0.5
+ ractive: ^1.3.12
+ razor-tmpl: ^1.3.1
+ react: ^16.13.1
+ react-dom: ^16.13.1
+ slm: ^2.0.0
+ squirrelly: ^5.1.0
+ swig: ^1.4.2
+ swig-templates: ^2.0.3
+ teacup: ^2.0.0
+ templayed: '>=0.2.3'
+ then-jade: '*'
+ then-pug: '*'
+ tinyliquid: ^0.2.34
+ toffee: ^0.3.6
+ twig: ^1.15.2
+ twing: ^5.0.2
+ underscore: ^1.11.0
+ vash: ^0.13.0
+ velocityjs: ^2.0.1
+ walrus: ^0.10.1
+ whiskers: ^0.4.0
+ peerDependenciesMeta:
+ arc-templates:
+ optional: true
+ atpl:
+ optional: true
+ babel-core:
+ optional: true
+ bracket-template:
+ optional: true
+ coffee-script:
+ optional: true
+ dot:
+ optional: true
+ dust:
+ optional: true
+ dustjs-helpers:
+ optional: true
+ dustjs-linkedin:
+ optional: true
+ eco:
+ optional: true
+ ect:
+ optional: true
+ ejs:
+ optional: true
+ haml-coffee:
+ optional: true
+ hamlet:
+ optional: true
+ hamljs:
+ optional: true
+ handlebars:
+ optional: true
+ hogan.js:
+ optional: true
+ htmling:
+ optional: true
+ jade:
+ optional: true
+ jazz:
+ optional: true
+ jqtpl:
+ optional: true
+ just:
+ optional: true
+ liquid-node:
+ optional: true
+ liquor:
+ optional: true
+ lodash:
+ optional: true
+ marko:
+ optional: true
+ mote:
+ optional: true
+ mustache:
+ optional: true
+ nunjucks:
+ optional: true
+ plates:
+ optional: true
+ pug:
+ optional: true
+ qejs:
+ optional: true
+ ractive:
+ optional: true
+ razor-tmpl:
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ slm:
+ optional: true
+ squirrelly:
+ optional: true
+ swig:
+ optional: true
+ swig-templates:
+ optional: true
+ teacup:
+ optional: true
+ templayed:
+ optional: true
+ then-jade:
+ optional: true
+ then-pug:
+ optional: true
+ tinyliquid:
+ optional: true
+ toffee:
+ optional: true
+ twig:
+ optional: true
+ twing:
+ optional: true
+ underscore:
+ optional: true
+ vash:
+ optional: true
+ velocityjs:
+ optional: true
+ walrus:
+ optional: true
+ whiskers:
+ optional: true
+ dependencies:
+ bluebird: 3.7.2
+ dev: false
+
/constantinople@4.0.1:
resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
dependencies:
@@ -18743,7 +19139,6 @@ packages:
postcss-value-parser: 4.2.0
semver: 7.3.7
webpack: 5.88.1(esbuild@0.17.19)(webpack-cli@5.1.4)
- dev: true
/css-minimizer-webpack-plugin@5.0.0(esbuild@0.17.19)(webpack@5.88.1):
resolution: {integrity: sha512-1wZ/PYvg+ZKwi5FX6YrvbB31jMAdurS+CmRQLwWCVSlfzJC85l/a6RVICqUHFa+jXyhilfnCyjafzJGbmz5tcA==}
@@ -22143,6 +22538,10 @@ packages:
safe-buffer: 5.2.1
dev: false
+ /hash-sum@1.0.2:
+ resolution: {integrity: sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==}
+ dev: false
+
/hash-sum@2.0.0:
resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
dev: false
@@ -22474,6 +22873,11 @@ packages:
terser: 5.17.3
dev: false
+ /html-tags@2.0.0:
+ resolution: {integrity: sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==}
+ engines: {node: '>=4'}
+ dev: false
+
/html-tags@3.2.0:
resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
engines: {node: '>=8'}
@@ -25346,6 +25750,12 @@ packages:
/merge-descriptors@1.0.1:
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
+ /merge-source-map@1.1.0:
+ resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==}
+ dependencies:
+ source-map: 0.6.1
+ dev: false
+
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
@@ -33018,6 +33428,89 @@ packages:
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
dev: false
+ /vue-hot-reload-api@2.3.4:
+ resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==}
+ dev: false
+
+ /vue-loader@15.10.1(css-loader@6.7.1)(webpack@5.88.1):
+ resolution: {integrity: sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==}
+ peerDependencies:
+ '@vue/compiler-sfc': ^3.0.8
+ cache-loader: '*'
+ css-loader: '*'
+ vue-template-compiler: '*'
+ webpack: ^3.0.0 || ^4.1.0 || ^5.0.0-0
+ peerDependenciesMeta:
+ '@vue/compiler-sfc':
+ optional: true
+ cache-loader:
+ optional: true
+ vue-template-compiler:
+ optional: true
+ dependencies:
+ '@vue/component-compiler-utils': 3.3.0
+ css-loader: 6.7.1(webpack@5.88.1)
+ hash-sum: 1.0.2
+ loader-utils: 1.4.0
+ vue-hot-reload-api: 2.3.4
+ vue-style-loader: 4.1.3
+ webpack: 5.88.1(esbuild@0.17.19)(webpack-cli@5.1.4)
+ transitivePeerDependencies:
+ - arc-templates
+ - atpl
+ - babel-core
+ - bracket-template
+ - coffee-script
+ - dot
+ - dust
+ - dustjs-helpers
+ - dustjs-linkedin
+ - eco
+ - ect
+ - ejs
+ - haml-coffee
+ - hamlet
+ - hamljs
+ - handlebars
+ - hogan.js
+ - htmling
+ - jade
+ - jazz
+ - jqtpl
+ - just
+ - liquid-node
+ - liquor
+ - lodash
+ - marko
+ - mote
+ - mustache
+ - nunjucks
+ - plates
+ - pug
+ - qejs
+ - ractive
+ - razor-tmpl
+ - react
+ - react-dom
+ - slm
+ - squirrelly
+ - swig
+ - swig-templates
+ - teacup
+ - templayed
+ - then-jade
+ - then-pug
+ - tinyliquid
+ - toffee
+ - twig
+ - twing
+ - underscore
+ - vash
+ - velocityjs
+ - walrus
+ - whiskers
+ dev: false
+
/vue-loader@17.2.2(webpack@5.88.1):
resolution: {integrity: sha512-aqNvKJvnz2A/6VWeJZodAo8XLoAlVwBv+2Z6dama+LHsAF+P/xijQ+OfWrxIs0wcGSJduvdzvTuATzXbNKkpiw==}
peerDependencies:
@@ -33036,6 +33529,13 @@ packages:
webpack: 5.88.1(esbuild@0.17.19)(webpack-cli@5.1.4)
dev: false
+ /vue-style-loader@4.1.3:
+ resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==}
+ dependencies:
+ hash-sum: 1.0.2
+ loader-utils: 1.4.0
+ dev: false
+
/vue-template-compiler@2.7.14:
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
requiresBuild: true
@@ -33045,6 +33545,17 @@ packages:
dev: false
optional: true
+ /vue-template-es2015-compiler@1.9.1:
+ resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
+ dev: false
+
+ /vue@2.7.14:
+ resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
+ dependencies:
+ '@vue/compiler-sfc': 2.7.14
+ csstype: 3.1.2
+ dev: false
+
/vue@3.3.4:
resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
dependencies:
@@ -33053,7 +33564,6 @@ packages:
'@vue/runtime-dom': 3.3.4
'@vue/server-renderer': 3.3.4(vue@3.3.4)
'@vue/shared': 3.3.4
- dev: false
/w3c-xmlserializer@4.0.0:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
diff --git a/tests/e2e/builder/cases/builder-cli/builder-cli-vue/package.json b/tests/e2e/builder/cases/builder-cli/builder-cli-vue/package.json
index 8577ebb8bb08..6d2654288f25 100644
--- a/tests/e2e/builder/cases/builder-cli/builder-cli-vue/package.json
+++ b/tests/e2e/builder/cases/builder-cli/builder-cli-vue/package.json
@@ -17,7 +17,8 @@
"devDependencies": {
"@modern-js/builder-cli": "workspace:*",
"@modern-js/builder-rspack-provider": "workspace:*",
+ "@types/node": "^14",
"typescript": "^5",
- "@types/node": "^14"
+ "vue": "^3.3.4"
}
}
diff --git a/tests/e2e/builder/cases/vue/package.json b/tests/e2e/builder/cases/vue/package.json
new file mode 100644
index 000000000000..26b75112dab2
--- /dev/null
+++ b/tests/e2e/builder/cases/vue/package.json
@@ -0,0 +1,8 @@
+{
+ "private": true,
+ "name": "@e2e/builder-vue3",
+ "version": "1.0.0",
+ "dependencies": {
+ "vue": "^3.3.4"
+ }
+}
diff --git a/tests/e2e/builder/cases/vue2/index.test.ts b/tests/e2e/builder/cases/vue2/index.test.ts
new file mode 100644
index 000000000000..15d6139ade39
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/index.test.ts
@@ -0,0 +1,119 @@
+import { join } from 'path';
+import { expect, test } from '@modern-js/e2e/playwright';
+import { build, getHrefByEntryName } from '@scripts/shared';
+import { builderPluginVue2 } from '@modern-js/builder-plugin-vue2';
+
+test('should build basic Vue sfc correctly', async ({ page }) => {
+ const root = join(__dirname, 'sfc-basic');
+
+ const builder = await build({
+ cwd: root,
+ entry: {
+ main: join(root, 'src/index.js'),
+ },
+ runServer: true,
+ plugins: [builderPluginVue2()],
+ });
+
+ await page.goto(getHrefByEntryName('main', builder.port));
+ await expect(
+ page.evaluate(`document.querySelector('#button1').innerHTML`),
+ ).resolves.toBe('A: 0');
+ await expect(
+ page.evaluate(`document.querySelector('#button2').innerHTML`),
+ ).resolves.toBe('B: 0');
+
+ builder.close();
+});
+
+test('should build basic Vue jsx correctly', async ({ page }) => {
+ const root = join(__dirname, 'jsx-basic');
+
+ const builder = await build({
+ cwd: root,
+ entry: {
+ main: join(root, 'src/index.js'),
+ },
+ runServer: true,
+ plugins: [builderPluginVue2()],
+ });
+
+ await page.goto(getHrefByEntryName('main', builder.port));
+
+ await expect(
+ page.evaluate(`document.querySelector('#button1').innerHTML`),
+ ).resolves.toBe('A: 0');
+
+ builder.close();
+});
+
+test('should build Vue sfc with lang="ts" correctly', async ({ page }) => {
+ const root = join(__dirname, 'sfc-lang-ts');
+
+ const builder = await build({
+ cwd: root,
+ entry: {
+ main: join(root, 'src/index.js'),
+ },
+ runServer: true,
+ plugins: [builderPluginVue2()],
+ });
+
+ await page.goto(getHrefByEntryName('main', builder.port));
+
+ await expect(
+ page.evaluate(`document.querySelector('#button').innerHTML`),
+ ).resolves.toContain('count: 0 foo: bar');
+
+ builder.close();
+});
+
+test('should build Vue sfc with lang="jsx" correctly', async ({ page }) => {
+ const root = join(__dirname, 'sfc-lang-jsx');
+
+ const builder = await build({
+ cwd: root,
+ entry: {
+ main: join(root, 'src/index.js'),
+ },
+ runServer: true,
+ plugins: [builderPluginVue2()],
+ });
+
+ await page.goto(getHrefByEntryName('main', builder.port));
+
+ await expect(
+ page.evaluate(`document.querySelector('#button').innerHTML`),
+ ).resolves.toBe('0');
+
+ await expect(
+ page.evaluate(`document.querySelector('#foo').innerHTML`),
+ ).resolves.toBe('Foo');
+
+ builder.close();
+});
+
+test('should build Vue sfc with lang="tsx" correctly', async ({ page }) => {
+ const root = join(__dirname, 'sfc-lang-tsx');
+
+ const builder = await build({
+ cwd: root,
+ entry: {
+ main: join(root, 'src/index.js'),
+ },
+ runServer: true,
+ plugins: [builderPluginVue2()],
+ });
+
+ await page.goto(getHrefByEntryName('main', builder.port));
+
+ await expect(
+ page.evaluate(`document.querySelector('#button').innerHTML`),
+ ).resolves.toBe('0');
+
+ await expect(
+ page.evaluate(`document.querySelector('#foo').innerHTML`),
+ ).resolves.toBe('Foo');
+
+ builder.close();
+});
diff --git a/tests/e2e/builder/cases/vue2/jsx-basic/src/A.jsx b/tests/e2e/builder/cases/vue2/jsx-basic/src/A.jsx
new file mode 100644
index 000000000000..a43c0f589be9
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/jsx-basic/src/A.jsx
@@ -0,0 +1,19 @@
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+ name: 'Test',
+
+ data() {
+ return {
+ count: 0,
+ };
+ },
+
+ render() {
+ return (
+ this.count++}>
+ A: {this.count}
+
+ );
+ },
+});
diff --git a/tests/e2e/builder/cases/vue2/jsx-basic/src/index.js b/tests/e2e/builder/cases/vue2/jsx-basic/src/index.js
new file mode 100644
index 000000000000..3e33bf75f523
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/jsx-basic/src/index.js
@@ -0,0 +1,8 @@
+import Vue from 'vue';
+import A from './A';
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: '#root',
+ render: h => h(A),
+});
diff --git a/tests/e2e/builder/cases/vue2/package.json b/tests/e2e/builder/cases/vue2/package.json
new file mode 100644
index 000000000000..414a22950795
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/package.json
@@ -0,0 +1,8 @@
+{
+ "private": true,
+ "name": "@e2e/builder-vue2",
+ "version": "1.0.0",
+ "dependencies": {
+ "vue": "^2.7.14"
+ }
+}
diff --git a/tests/e2e/builder/cases/vue2/sfc-basic/src/A.vue b/tests/e2e/builder/cases/vue2/sfc-basic/src/A.vue
new file mode 100644
index 000000000000..54f53ba7aa53
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-basic/src/A.vue
@@ -0,0 +1,25 @@
+
+
+ A: {{ count }}
+
+
+
+
+
diff --git a/tests/e2e/builder/cases/vue2/sfc-basic/src/B.vue b/tests/e2e/builder/cases/vue2/sfc-basic/src/B.vue
new file mode 100644
index 000000000000..37396202aad9
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-basic/src/B.vue
@@ -0,0 +1,14 @@
+
+ B: {{ count }}
+
+
+
diff --git a/tests/e2e/builder/cases/vue2/sfc-basic/src/index.js b/tests/e2e/builder/cases/vue2/sfc-basic/src/index.js
new file mode 100644
index 000000000000..f6536e93cab6
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-basic/src/index.js
@@ -0,0 +1,8 @@
+import Vue from 'vue';
+import A from './A.vue';
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: '#root',
+ render: h => h(A),
+});
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/App.vue b/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/App.vue
new file mode 100644
index 000000000000..7f9fc425b877
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/App.vue
@@ -0,0 +1,27 @@
+
+
+ {{ count }}
+
+
+
+
+
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/index.js b/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/index.js
new file mode 100644
index 000000000000..625f1c8c3b95
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-jsx/src/index.js
@@ -0,0 +1,8 @@
+import Vue from 'vue';
+import App from './App.vue';
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: '#root',
+ render: h => h(App),
+});
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/App.vue b/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/App.vue
new file mode 100644
index 000000000000..06e673427a0b
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/App.vue
@@ -0,0 +1,22 @@
+
+
+
+ count: {{ count }} foo: {{ foo }}
+
+
+
+
+
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/index.js b/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/index.js
new file mode 100644
index 000000000000..579a575168c8
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-ts/src/index.js
@@ -0,0 +1,8 @@
+import Vue from 'vue';
+import App from './App.vue';
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: '#root',
+ render: h => h(App, { props: { foo: 'bar' } }),
+});
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/App.vue b/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/App.vue
new file mode 100644
index 000000000000..a983a1402f34
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/App.vue
@@ -0,0 +1,27 @@
+
+
+ {{ count }}
+
+
+
+
+
diff --git a/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/index.js b/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/index.js
new file mode 100644
index 000000000000..625f1c8c3b95
--- /dev/null
+++ b/tests/e2e/builder/cases/vue2/sfc-lang-tsx/src/index.js
@@ -0,0 +1,8 @@
+import Vue from 'vue';
+import App from './App.vue';
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: '#root',
+ render: h => h(App),
+});
diff --git a/tests/e2e/builder/package.json b/tests/e2e/builder/package.json
index de83c3c12205..be226102809c 100644
--- a/tests/e2e/builder/package.json
+++ b/tests/e2e/builder/package.json
@@ -20,6 +20,7 @@
"@modern-js/builder-plugin-stylus": "workspace:*",
"@modern-js/builder-plugin-swc": "workspace:*",
"@modern-js/builder-plugin-vue": "workspace:*",
+ "@modern-js/builder-plugin-vue2": "workspace:*",
"@modern-js/builder-rspack-provider": "workspace:*",
"@modern-js/builder-shared": "workspace:*",
"@modern-js/builder-webpack-provider": "workspace:*",