From b00667a14d1dc3f4698a4b63376a1f8947d584b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BC=96=E7=A0=81=E7=8C=BF?= Date: Fri, 27 Sep 2024 01:00:10 +0800 Subject: [PATCH] first commit --- .gitignore | 23 +++ .pydio | 1 + README.md | 326 ++++++++++++++++++++++++++++++++++++++++ generator/.pydio | 1 + generator/index.js | 11 ++ index.js | 17 +++ package-lock.json | 159 ++++++++++++++++++++ package.json | 25 +++ service/.pydio | 1 + service/regist-route.js | 309 +++++++++++++++++++++++++++++++++++++ utils/.pydio | 1 + utils/welcome.js | 12 ++ 12 files changed, 886 insertions(+) create mode 100644 .gitignore create mode 100644 .pydio create mode 100644 README.md create mode 100644 generator/.pydio create mode 100644 generator/index.js create mode 100644 index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 service/.pydio create mode 100644 service/regist-route.js create mode 100644 utils/.pydio create mode 100644 utils/welcome.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..403adbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +/dist + + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.pydio b/.pydio new file mode 100644 index 0000000..89fcf38 --- /dev/null +++ b/.pydio @@ -0,0 +1 @@ +6e388d74-c773-45ae-bc65-b69a46cca116 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5623d62 --- /dev/null +++ b/README.md @@ -0,0 +1,326 @@ +## vue-cli-plugin-autorouter + +借鉴 [Nuxt.js](https://zh.nuxtjs.org/) 的路由源码实现。可以根据存放Vue-Router页面的文件夹结构自动生成Vue-Router配置文件。 + +**相信我,你会中毒的,妈妈再也不用担心我手动低效的去`Ctrl + C`,`Ctrl + V`了,一切交给插件自动生成路由配置文件,我们要做的就是按照规则维护好文件夹结构,世界突然美妙了。** + +> 注意:这个插件目前为止还没有改成通用的插件包,有这个考虑。不过现阶段主要用于 [Vue3template](https://github.com/helpcode/Vue3template) 这个脚手架项目。后面还会考虑再开发一个脚手架项目的`cli`命令行工具,实现类似`Vue-cli`通过命令进行项目的初始化等功能。 + +## 1:安装(Vue3template默认已安装配置好,可直接使用) + +注意这是一个基于[Vue-cli 3.0](https://cli.vuejs.org/zh/)也就是 vue-cli-service 开发的插件,不支持老版本Vue 2.0的脚手架哦,请注意了! + +```bash +vue add vue-cli-plugin-autorouter +``` + +`add`安装的时候会自动向你项目的`package.json`的`devDependencies`注入下面依赖: + +```txt +"glob": "^7.1.3", +"pify": "^4.0.1" +``` + +这是 `vue-cli-service`插件开发`Generator`自动注入的,也是本插件需要使用到的依赖。请勿随意删除。 + +## 2:配置 + +在`vue.config.js`中你可以增加`pluginOptions`配置节点,实现对插件的自定义化需求定制: + +```js +module.exports = { + // ... + pluginOptions: { + route: { + // 默认:page。存放Vue路由页面的文件夹名称 + TemplateFolderName: 'page', + // 默认:./src/application。从src/开始到TemplateFolderName文件夹父一级文件夹相对路径 + RootFolderName: './src/application', + // 默认 src/core/config/route.config.ts。生成的配置文件保存路径,相对插件的位置来。 + SaveConfigPath: '../../../src/core/config/route.config.ts' + } + }, + // ... +} +``` + +**然后你就可以很爽的直接使用命令,注意这个命令是用来自动生成配置文件的:** +```bash +vue-cli-service route +``` + +> 注意:每次项目新增页面后,请重新执行此命令用以生成新的路由配置文件!! + +不过,推荐在项目的`package.json`中做如下配置: + +```txt +"scripts": { + "serve": "npm run route && vue-cli-service serve", + "route": "vue-cli-service route", + "build": "vue-cli-service build", + }, +``` + +注意:一定要在 `vue-cli-service serve`命令运行前,先跑`vue-cli-service route`来扫描`src/application/page`的文件结构进而自动生成`Vue-Router`的配置文件。默认配置文件保存在:`src/core/config/route.config.ts`中。 + +## 3:卸载 + +东西再好,总有些开发者拒绝学习,或者拒绝尝试新的东西,喜欢固守自己已熟知的技术栈,那么下面提供卸载`Vue3template`自带的` vue-cli-plugin-autorouter`教程。 + +### 3.1: 删除依赖(两种方式) + +- 1: 使用npm命令卸载依赖: + +```bash +npm uninstall --save glob pify vue-cli-plugin-autorouter +``` + +- 2: 从`package.json`中手动删除下面依赖信息 + +```text + "glob": "^7.1.3", + "pify": "^4.0.1", + "vue-cli-plugin-autorouter": "^1.1.8", +``` + +然后删除项目`node_modules`,在重新`npm i`。 + +### 3.2: vue.config.js + +从`vue.config.js`中你删除`pluginOptions`配置节点。 + + +### 3.3: 修改源码 + +1: 从`src/core/config`中删除文件`route.config.ts`。 + +2:然后修改:`index.config.ts`,去除:`import { AutoRoutesConfig } from './route.config';` + +3:之后修改:`RouterConfigUrl`中的`routes`对象,`routes`对象的内容需要你自己手动 + +> 注意:`RouterConfigUrl` 中的 `routes` 需要你自己手动配置了,这里提供一份配置参考: + +```typescript + public static RouterConfigUrl: RouterOptions = { + mode: 'hash', + routes: [ + { + name: "user", + path: "/user", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/index.vue'), + meta: {} + }, + { + name: "user-one", + path: "/user/one", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/one.vue'), + meta: {} + }, + { + name: "index", + path: "/", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/index.vue'), + meta: {} + } + ] + } +``` + +## 【新增功能】4:路由meta配置 + +在`page/`文件夹的组件中使用可以直接使用自定义标签``来实现对路由`meta`的直接配置,插件会提取``中的代码直接注入到生成的路由配置文件中。下面请看使用案例: + +```vue + + { + "isLogin": false, + "title": "首页" + } + + +``` + +**注意这里`route-meta`标签中填的必须是一个`JSON`对象,注意书写格式,不能有误!!** + +那么自动生成的路由配置如下: + +```js +{ + name: "index", + path: "/", + component: () => import(/* webpackChunkName: 'index' */ '@/page/index.vue'), + meta: { + isLogin: false, + title: "首页" + } +} +``` + +## 5:使用 + +这里使用请注意文件夹结构,多看几次下面的例子加强理解,以便能够更好的组织自己的路由结构!! + + +### 5.1: 基础路由 + +基础路由很简单,直接在`page/`文件下新建`vue`组件即可,插件会根据文件结构自动生成路由配置文件!! + +假设 page 的目录结构如下: + +```txt +pages/ +--| user/ +-----| index.vue +-----| one.vue +--| index.vue +``` + +那么自动生成的路由配置如下: + +```js +[ + { + name: "user", + path: "/user", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/index.vue'), + meta: {} + }, + { + name: "user-one", + path: "/user/one", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/one.vue'), + meta: {} + }, + { + name: "index", + path: "/", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/index.vue'), + meta: {} + } +] +``` + +### 5.2: 动态路由(带参数) + +如果路由需要参数,那么可以按照命名的规则:`_参数名.vue`,在`user/`文件夹创建`Vue`组件。例如这里的:`_id.vue`,其实这里的 `_id.vue`就相当于我们平时开发时用的`user_info.vue`组件。 + +假设 page 的目录结构如下: + +```txt +page/ +--| user/ +-----| _id.vue +--| index.vue +``` + +那么自动生成的路由配置如下: + +```js +[ + { + name: "user-id", + path: "/user/:id?", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/_id.vue'), + meta: {} + }, + { + name: "index", + path: "/", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/index.vue'), + meta: {} + } +] +``` + +--- + +这里我们有了`user-id`组件(相当于 `user_info.vue`),那么`user`首页组件如何添加?其实很简单! + +基于上面的文件结构在`user/`里面在创建一个`index.vue`组件即可,默认文件夹下面的`index.vue`作为路由的首页 + +改变后的文件结构如下: + +```txt +page/ +--| user/ +-----| _id.vue # 这个相当于平时开发的 user_info.vue,个人中心的详情页面 +-----| index.vue # user首页,类似个人中心 +--| index.vue +``` + +那么自动生成的路由配置如下: + +```js +[ + { + name: "user", + path: "/user", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/index.vue'), + meta: {} + }, + { + name: "user-id", + path: "/user/:id", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/_id.vue'), + meta: {} + }, + { + name: "index", + path: "/", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/index.vue'), + meta: {} + } +] +```` + +### 5.3: 嵌套路由 + +创建内嵌子路由,上面我们已经有了`user/`文件夹,那么我们可以在`user/`文件夹同级目录创建一个名字一样的`Vue`组件,这里添加:`user.vue`! + +假设 page 的目录结构如下: + +```txt +page/ +--| user/ +-----| _id.vue +-----| index.vue +--| user.vue +--| index.vue +``` + +那么自动生成的路由配置如下: + +```js +[ + { + path: "/user", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user.vue'), + meta: {}, + children: [ + { + name: "user", + path: "", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/index.vue'), + meta: {} + }, + { + name: "user-id", + path: ":id", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/user/_id.vue'), + meta: {} + } + ] + }, + { + name: "index", + path: "/", + component: () => import(/* webpackChunkName: '[request]' */ '@/page/index.vue'), + meta: {} + } +] +``` + +> 记住:这是嵌套路由所以别忘记在`user.vue`组件中添加:`router-view` 来展示子路由的页面。 \ No newline at end of file diff --git a/generator/.pydio b/generator/.pydio new file mode 100644 index 0000000..3535e3c --- /dev/null +++ b/generator/.pydio @@ -0,0 +1 @@ +7d22662f-47a9-47f7-9945-1c842bb893aa \ No newline at end of file diff --git a/generator/index.js b/generator/index.js new file mode 100644 index 0000000..8f327fd --- /dev/null +++ b/generator/index.js @@ -0,0 +1,11 @@ +module.exports = (api, options, rootOptions) => { + api.extendPackage({ + devDependencies: { + "@babel/generator": "^7.11.4", + "@babel/types": "^7.11.0", + "@babel/parser": "^7.11.4", + "glob": "^7.1.3", + "pify": "^4.0.1" + }, + }); +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..ac4ea6b --- /dev/null +++ b/index.js @@ -0,0 +1,17 @@ +const creatRouter = require('./service/regist-route') +require('./utils/welcome'); + +module.exports = (api, projectOptions) => { + api.registerCommand('route', { + description: '根据page文件结构自动生成路由配置文件', + usage: 'vue-cli-service route', + options: {} + }, (args) => { + let routeParams = projectOptions.pluginOptions.route; + creatRouter.creatRouter(false, routeParams); + }) +}; + +module.exports.defaultModes = { + build: 'development' +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a777dfa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,159 @@ +{ + "name": "vue-cli-plugin-autorouter", + "version": "1.3.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/generator": { + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", + "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "requires": { + "@babel/types": "^7.11.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/parser": { + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", + "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==" + }, + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "vue-template-compiler": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz", + "integrity": "sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==", + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9cc6851 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "vue-cli-plugin-tsx-autorouter", + "version": "1.3.8", + "description": "vue cli plugin to auto add router tsx", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "bmy", + "license": "ISC", + "dependencies": { + "@babel/generator": "^7.11.4", + "@babel/types": "^7.11.0", + "@babel/parser": "^7.11.4", + "glob": "^7.1.3", + "pify": "^4.0.1" + }, + "keywords": [ + "vue", + "vue-cli", + "vue-router", + "autorouter" + ], + "devDependencies": {} +} \ No newline at end of file diff --git a/service/.pydio b/service/.pydio new file mode 100644 index 0000000..da60162 --- /dev/null +++ b/service/.pydio @@ -0,0 +1 @@ +876a8496-8207-43a5-ab7b-f46a75d7118a \ No newline at end of file diff --git a/service/regist-route.js b/service/regist-route.js new file mode 100644 index 0000000..07596e7 --- /dev/null +++ b/service/regist-route.js @@ -0,0 +1,309 @@ +const Glob = require('glob'); +const pify = require('pify'); +const path = require('path'); +const fs = require('fs'); +const { parse } = require('@babel/parser'); +const t = require('@babel/types'); +const generate = require('@babel/generator'); + +const glob = pify(Glob); +let globPath = ""; +let OptionSetting = {}; + +// 获取文件路径,构造路由参数数组 +generateRoutesAndFiles = async (PagePath) => { + const files = {}; + + (await glob(`${PagePath}/**/*.{vue,js,tsx,jsx}`, { + cwd: path.resolve(process.cwd(), OptionSetting.RootFolderName), + ignore: ['**/*.test.*', '**/*.spec.*', '**/-*.*'] + })).forEach((f) => { + const key = f.replace(/\.(js|vue)$/, '') + if (/\.vue$/.test(f) || !files[key]) { + files[key] = f.replace(/('|")/g, '\\$1') + } + }); + + return createRoutes( + Object.values(files), + path.resolve(process.cwd(), OptionSetting.RootFolderName), + PagePath + ) +}; + +// 填充路由配置 +// `Utils.AsyncComponentHook('${camelCase(keys.join('-').replace('_', ''))}')` +const createRoutes = function createRoutes(files, srcDir, pagesDir) { + const routes = []; + files.forEach((file) => { + const keys = file + .replace(RegExp(`^${pagesDir}`), '') + .replace(/\.(vue|js|tsx)$/, '') + .replace(/\/{2,}/g, '/') + .split('/') + .slice(1); + const route = { + name: '', + path: '', + component: `_() => import(/* webpackChunkName: '${keys.toString().replace(/,/g, "")}' */ '@${pagesDir}/${keys.join('/')}')`, + meta: routeMeta(file) + }; + let parent = routes; + keys.forEach((key, i) => { + const sanitizedKey = key.startsWith('_') ? key.substr(1) : key; + + route.name = route.name + ? route.name + '-' + sanitizedKey + : sanitizedKey; + + route.name += key === '_' ? 'all' : ''; + const child = parent.find(parentRoute => parentRoute.name === route.name); + + if (child) { + child.children = child.children || []; + parent = child.children; + route.path = ''; + } else if (key === 'index' && i + 1 === keys.length) { + route.path += i > 0 ? '' : '/'; + } else { + route.path += '/' + getRoutePathExtension(key); + + if (key.startsWith('_') && key.length > 1) { + route.path += '?'; + } + } + }); + parent.push(route); + }); + + sortRoutes(routes); + return cleanChildrenRoutes(routes) +}; + +const startsWithAlias = aliasArray => str => aliasArray.some(c => str.startsWith(c)); +const reqSep = /\//g; +const sysSep = escapeRegExp(path.sep); +const normalize = string => string.replace(reqSep, sysSep); +const startsWithSrcAlias = startsWithAlias(['@', '~']); + +function routeMeta(f) { + let meta = {}; + const VuePath = path.join(__dirname,`../../.${OptionSetting.RootFolderName}/${f}`); + let VueSource = fs.readFileSync(VuePath).toString(); + // 将tsx字符串代码转换成ast语法树 + const ast = parse(VueSource, { + sourceType: 'module', + plugins: ['typescript', 'jsx', 'classProperties', + ['decorators', { decoratorsBeforeExport: true }] + ] + }); + // 从语法树中找到类私有属性 private RouterMeta + output = "" + ast.program.body.forEach((v, i) => { + if (t.isExportDefaultDeclaration(v)) { + v.declaration.body.body.forEach((b)=>{ + if (t.isClassProperty(b)) { + if (t.isIdentifier(b.key) && b.key.name == 'RouterMeta') { + // 将 私有属性 RouterMeta 的值从语法树转换为js代码 + output = generate.default(b.value, { + compact: true, + }).code; + } + } + }) + } + }) + + if(output.length != 0) { + return eval(JSON.stringify(output)); + } else { + return JSON.stringify({ + test: `请在组件配置:route-meta 标签,参考文档:https://www.npmjs.com/package/vue-cli-plugin-autorouter` + }) + } +} + +const r = function r(...args) { + const lastArg = args[args.length - 1]; + if (startsWithSrcAlias(lastArg)) { + return wp(lastArg) + } + return wp(path.resolve(...args.map(normalize))) +}; + +function baseToString(value) { + if (typeof value == 'string') { + return value; + } + if (isArray_1(value)) { + return _arrayMap(value, baseToString) + ''; + } + if (isSymbol_1(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; +} + +function toString(value) { + return value == null ? '' : baseToString(value); +} + +function escapeRegExp(string) { + const reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + string = toString(string); + return (string && reHasRegExpChar.test(string)) ? + string.replace(reRegExpChar, '\\$&') : + string; +} +const isWindows = /^win/.test(process.platform); + +const wp = function wp(p = '') { + if (isWindows) { + return p.replace(/\\/g, '\\\\') + } + return p +}; + +const getRoutePathExtension = (key) => { + if (key === '_') { + return '*' + } + + if (key.startsWith('_')) { + return `:${key.substr(1)}` + } + + return key +}; + +const DYNAMIC_ROUTE_REGEX = /^\/(:|\*)/; + +/** + * 排序路由 + * @param {*} routes + */ +function sortRoutes(routes) { + routes.sort((a, b) => { + if (!a.path.length) { + return -1 + } + if (!b.path.length) { + return 1 + } + if (a.path === '/') { + return DYNAMIC_ROUTE_REGEX.test(b.path) ? -1 : 1 + } + if (b.path === '/') { + return DYNAMIC_ROUTE_REGEX.test(a.path) ? 1 : -1 + } + + let i + let res = 0 + let y = 0 + let z = 0 + const _a = a.path.split('/') + const _b = b.path.split('/') + for (i = 0; i < _a.length; i++) { + if (res !== 0) { + break + } + y = _a[i] === '*' ? 2 : _a[i].includes(':') ? 1 : 0 + z = _b[i] === '*' ? 2 : _b[i].includes(':') ? 1 : 0 + res = y - z + // If a.length >= b.length + if (i === _b.length - 1 && res === 0) { + // unless * found sort by level, then alphabetically + res = _a[i] === '*' ? -1 : ( + _a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length) + ) + } + } + + if (res === 0) { + res = _a[i - 1] === '*' && _b[i] ? 1 : ( + _a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length) + ) + } + return res + }) + + routes.forEach((route) => { + if (route.children) { + sortRoutes(route.children) + } + }) + + return routes +} + +function cleanChildrenRoutes(routes, isChild = false) { + let start = -1 + const routesIndex = [] + routes.forEach((route) => { + if (/-index$/.test(route.name) || route.name === 'index') { + const res = route.name.split('-') + const s = res.indexOf('index') + start = start === -1 || s < start ? s : start + routesIndex.push(res) + } + }) + routes.forEach((route) => { + route.path = isChild ? route.path.replace('/', '') : route.path + if (route.path.includes('?')) { + const names = route.name.split('-') + const paths = route.path.split('/') + if (!isChild) { + paths.shift() + } + routesIndex.forEach((r) => { + const i = r.indexOf('index') - start + if (i < paths.length) { + for (let a = 0; a <= i; a++) { + if (a === i) { + paths[a] = paths[a].replace('?', '') + } + if (a < i && names[a] !== r[a]) { + break + } + } + } + }) + route.path = (isChild ? '' : '/') + paths.join('/') + } + route.name = route.name.replace(/-index$/, '') + if (route.children) { + if (route.children.find(child => child.path === '')) { + delete route.name + } + route.children = cleanChildrenRoutes(route.children, true) + } + }) + return routes +} + +function camelCase(string) { + return string.replace(/-([a-z])/g, function (all, letter) { + return letter.toUpperCase(); + }); +} + +module.exports.creatRouter = (flag = false, root) => { + if (flag) return; + OptionSetting = root; + generateRoutesAndFiles(OptionSetting.TemplateFolderName).then(res => { + let string = `// 根据page目录结构,自动生成的路由配置文件\n// 参考Nuxt.js:https://zh.nuxtjs.org/guide/routing\n// @ts-ignore\n\n`; + + string += `export const AutoRoutesConfig = ${JSON.stringify(res, null, 2)};` + .replace(/"component": "(\w+?)"/g, `"component": $1`) + .replace(/"(\w+?)":/g, '$1:') + .replace(/"_()/g, "") + .replace(/"{/g, "{") + .replace(/}"/g, "}") + .replace(/\'\)\"/g, "')") + + let buildPath = path.resolve(process.cwd(), path.join(__dirname, OptionSetting.SaveConfigPath)); + fs.writeFileSync(buildPath, string); + }) +}; \ No newline at end of file diff --git a/utils/.pydio b/utils/.pydio new file mode 100644 index 0000000..9a1a733 --- /dev/null +++ b/utils/.pydio @@ -0,0 +1 @@ +57690bf2-4361-4277-ae8c-5aab54aeb228 \ No newline at end of file diff --git a/utils/welcome.js b/utils/welcome.js new file mode 100644 index 0000000..66a6545 --- /dev/null +++ b/utils/welcome.js @@ -0,0 +1,12 @@ +const { readFileSync, writeFileSync } = require("fs"); +const { join } = require("path"); +const package = require("../package.json"); +const Path = '../../@vue/cli-service/lib/commands/serve.js'; +let serve = (readFileSync(join(__dirname, Path))).toString(); +serve = serve.replace('Starting development server...',"正在启动开发服务器..."); +serve = serve.replace('App running at:',`🎉 欢迎使用 ${package.name}\n 🎉 该插件基于本人另一个项目[vue-cli-plugin-autorouter]适配自己的VueClassApi + Tsx 的脚手架而来 \n\n 📦 项目地址:https://www.npmjs.com/package/vue-cli-plugin-autorouter\n 👨‍💻 作者:bmy\n 📞 Github:https://github.com/helpcode\n 应用运行在:`); +serve = serve.replace('- Local:'," - 本地:"); +serve = serve.replace('- Network:'," - 网络:"); +serve = serve.replace('Note that the development build is not optimized.',"注意开发阶段项目没有做优化。"); +serve = serve.replace('To create a production build, run',"如果要创建生产代码,请运行命令: "); +writeFileSync(join(__dirname, Path),serve); \ No newline at end of file