├── .babelrc
├── .circleci
└── config.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── .npmrc
├── .stylelintignore
├── .yarnrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── commitlint.config.js
├── core
├── ast.js
├── files.js
├── index.js
├── invoke.js
└── utils.js
├── demos
├── .invoke
│ └── router.js
├── App.vue
├── apis
│ └── index.js
├── index.html
├── index.js
├── src
│ ├── Complex
│ │ ├── Home
│ │ │ ├── Account
│ │ │ │ ├── Account.vue
│ │ │ │ ├── Chunk
│ │ │ │ │ └── Index.vue
│ │ │ │ ├── Inner
│ │ │ │ │ └── Index.vue
│ │ │ │ └── _Dynamic
│ │ │ │ │ └── Index.vue
│ │ │ ├── Details
│ │ │ │ ├── Details.vue
│ │ │ │ ├── Infor
│ │ │ │ │ └── Index.vue
│ │ │ │ ├── Intro
│ │ │ │ │ └── Index.vue
│ │ │ │ └── meta.yml
│ │ │ └── Home.vue
│ │ ├── Index.vue
│ │ └── Login
│ │ │ ├── Images
│ │ │ └── test.png
│ │ │ └── Index.vue
│ ├── Dynamic
│ │ ├── Index.vue
│ │ └── _UserForm
│ │ │ ├── Index.vue
│ │ │ └── meta.yml
│ ├── Index.vue
│ ├── Nest
│ │ ├── Home
│ │ │ ├── Account
│ │ │ │ ├── Index.vue
│ │ │ │ ├── _Id
│ │ │ │ │ └── Index.vue
│ │ │ │ └── meta.yml
│ │ │ ├── Home.vue
│ │ │ ├── Infor
│ │ │ │ └── Index.vue
│ │ │ ├── Test
│ │ │ │ └── Index.vue
│ │ │ └── meta.yml
│ │ ├── Index.vue
│ │ ├── index.scss
│ │ ├── meta.yml
│ │ └── test
│ │ │ └── index.vue
│ ├── NotFound.vue
│ ├── Single
│ │ ├── Index.vue
│ │ └── User-Name
│ │ │ ├── Index.vue
│ │ │ └── meta.yml
│ └── Template.vue
└── webpack.config.js
├── docs
├── images
│ ├── index_cn.png
│ ├── index_en.png
│ ├── name.png
│ └── notice.png
└── zh_CN
│ └── README.md
├── jest.config.js
├── package.json
├── prettier.config.js
├── scripts
├── publish.js
└── publish.sh
├── stylelint.config.js
├── tests
├── dynamic.spec.js
├── ignore
│ ├── Components
│ │ └── Index.vue
│ ├── Images
│ │ └── Index.vue
│ └── Login
│ │ └── Index.vue
├── nest.spec.js
├── option.spec.js
├── single.spec.js
├── single
│ ├── Login
│ │ └── Index.vue
│ └── User
│ │ └── Index.vue
└── utils
│ └── index.js
├── types
├── index.d.ts
└── options.d.ts
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@vue/app"],
3 | "env": {
4 | "test": {
5 | "presets": [
6 | [
7 | "@vue/app",
8 | { "modules": "commonjs", "targets": { "node": "current" } }
9 | ]
10 | ]
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node:8.15.1
6 | working_directory: ~/app
7 | steps:
8 | - checkout
9 | - restore_cache:
10 | key: vue-router-invoke-webpack-plugin-{{ .Branch }}-{{ checksum "yarn.lock" }}
11 | - run: yarn
12 | - save_cache:
13 | paths:
14 | - node_modules
15 | key: vue-router-invoke-webpack-plugin-{{ .Branch }}-{{ checksum "yarn.lock" }}
16 | - run: npm run lint
17 | - run: npm run test:single
18 | - run: npm run test:ci
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | demos/dist
2 | tests/single
3 | coverage
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true,
4 | "node": true
5 | },
6 | "extends": [
7 | "standard",
8 | "plugin:prettier/recommended",
9 | "plugin:vue/essential",
10 | "plugin:vue/strongly-recommended",
11 | "plugin:vue/recommended"
12 | ],
13 | "rules": {
14 | "prettier/prettier": "error",
15 | "quotes": ["error", "single", { "allowTemplateLiterals": true }],
16 | "space-before-function-paren": ["error", "never"],
17 | "prefer-promise-reject-errors": "off",
18 | "no-new": "off",
19 | "no-console": "error",
20 | "no-debugger": "error",
21 | "brace-style": [0],
22 | "vue/html-self-closing": [
23 | "error",
24 | {
25 | "html": {
26 | "void": "any"
27 | }
28 | }
29 | ],
30 | "vue/html-closing-bracket-spacing": [0],
31 | "vue/max-attributes-per-line": [0],
32 | "vue/attribute-hyphenation": ["error", "never"],
33 | "vue/order-in-components": [
34 | "error",
35 | {
36 | "order": [
37 | "el",
38 | "name",
39 | "parent",
40 | "functional",
41 | ["delimiters", "comments"],
42 | ["components", "directives", "filters"],
43 | "extends",
44 | "mixins",
45 | "inheritAttrs",
46 | "model",
47 | "data",
48 | ["props", "propsData"],
49 | "computed",
50 | "watch",
51 | "LIFECYCLE_HOOKS",
52 | "methods",
53 | ["template", "render"],
54 | "renderError"
55 | ]
56 | }
57 | ],
58 | "vue/no-v-html": [0]
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: Qymh
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Version**
14 | `vue-router-invoke-webpack-plugin`:
15 | `webpack`:
16 | `node`:
17 | `system`: [windows or mac]
18 |
19 | ** The tree of files or Screenshots**
20 |
21 | [example of tree]
22 |
23 | ```
24 | src
25 | ├── views
26 | │ ├── Login
27 | │ │ └── Index.vue
28 | │ └── User
29 | │ ├── Account
30 | │ │ └── Index.vue
31 | │ ├── Home
32 | │ │ └── Index.vue
33 | │ └── Index.vue
34 | ```
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 |
4 | demos/dist
5 | .DS_Store
6 | coverage
7 |
8 | tests/.invoke
9 | .vscode
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .gitignore
4 | .DS_Store
5 | .invoke
6 | .vscode
7 | registry=https://registry.npmjs.org/
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | demos/dist
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | registry "https://registry.npm.taobao.org/"
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.4.4](https://github.com/Qymh/vue-router-invoke-webpack-plugin/compare/v0.4.3...v0.4.4) (2020-02-28)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * 🐛 wrong generation when multistage nested routes exist ([#27](https://github.com/Qymh/vue-router-invoke-webpack-plugin/issues/27)) ([6977b0f](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/6977b0fb31f013b0225a1d750676f1d408a09401))
7 |
8 |
9 |
10 | ## [0.4.3](https://github.com/Qymh/vue-router-invoke-webpack-plugin/compare/v0.4.2...v0.4.3) (2020-01-10)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * 🐛 wrong test in circleci ([4dc5542](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/4dc5542ff45377e159c14468a3cf850296df26ac))
16 |
17 |
18 | ### Features
19 |
20 | * 🎸 add codecov ([675c5e5](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/675c5e5c06030fe09b1658fe60fa8454d397db41))
21 | * 🎸 add regexp to set ignore options ([#25](https://github.com/Qymh/vue-router-invoke-webpack-plugin/issues/25)) ([189a5a4](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/189a5a46d4e3f7bf5143426ffb388c5e37760056))
22 |
23 |
24 |
25 | ## [0.4.2](https://github.com/Qymh/vue-router-invoke-webpack-plugin/compare/v0.4.1...v0.4.2) (2020-01-09)
26 |
27 |
28 | ### Bug Fixes
29 |
30 | * 🐛 wrong dependency for js-beautify ([#24](https://github.com/Qymh/vue-router-invoke-webpack-plugin/issues/24)) ([d9ed248](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/d9ed248d26f001c3cbc592d88f1affd12bf71d72))
31 |
32 |
33 |
34 | ## [0.4.1](https://github.com/Qymh/vue-router-invoke-webpack-plugin/compare/v0.4.0...v0.4.1) (2019-12-16)
35 |
36 |
37 | ### Features
38 |
39 | * 🎸 improve frame work ([228d5bf](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/228d5bfab55c8b26c4cde91918f18574816b3729))
40 | * 🎸 meta supports boolean and plain object ([#23](https://github.com/Qymh/vue-router-invoke-webpack-plugin/issues/23)) ([82a86d7](https://github.com/Qymh/vue-router-invoke-webpack-plugin/commit/82a86d7481eaf65eabac0074fcefbf4c90921bce))
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Qymh
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-router-invoke-webpack-plugin
2 |
3 | 
4 | 
5 | 
6 | 
7 |
8 | > a new version has been rewritten by `typescript` [vue-router-invoke-next-webpack-plugin](https://github.com/Qymh/vue-router-invoke-next-webpack-plugin) both supported vue2.x and vue3.x
9 |
10 | [CHANGELOG](https://github.com/Qymh/vue-router-invoke-webpack-plugin/blob/dev/CHANGELOG.md)
11 |
12 | [中文版本](https://github.com/Qymh/vue-router-invoke-webpack-plugin/blob/dev/docs/zh_CN/README.md)
13 |
14 | Automatic generate the routes of `vue-router` based on the file directory.
15 |
16 | ## Install
17 |
18 | ### npm
19 |
20 | ```javascript
21 | npm install vue-router-invoke-webpack-plugin -D
22 | ```
23 |
24 | ### cnpm
25 |
26 | ```javascript
27 | cnpm install vue-router-invoke-webpack-plugin -D
28 | ```
29 |
30 | ### yarn
31 |
32 | ```javascript
33 | yarn add vue-router-invoke-webpack-plugin -D
34 | ```
35 |
36 | ## What is Automatic Generate Routes
37 |
38 | Routing automatic injection refers to according to the format of the file directory to automatically generate the corresponding `router.js`, every time without the need to create a module to reference manual
39 |
40 | ## Usage
41 |
42 | ### Webpack
43 |
44 | - We need know whether the environment is `development` or `production`.So you should set `process.env.NODE_ENV` which is equal to `development` in the development environment and is equal to `production` in the production environment.There are many plugins can do that. We recommend [cross-env](https://github.com/kentcdodds/cross-env)
45 | - If there are many people working together,we can't import route by the absolute address,so you should set a [alias](https://webpack.js.org/configuration/resolve/#resolvealias) for the watching `dir`.
46 | - the generated route will be lazyload. So make sure you have add [@babel/plugin-syntax-dynamic-import](https://babeljs.io/docs/en/next/babel-plugin-syntax-dynamic-import.html)
47 |
48 | ```javascript
49 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
50 | const path = require('path')
51 |
52 | // omit some other option...
53 |
54 | resolve: {
55 | alias: {
56 | '@': path.resolve(process.cwd(), 'demos')
57 | }
58 | }
59 |
60 | plugins: [
61 | new VueRouterInvokeWebpackPlugin(
62 | dir: 'demos/src',
63 | alias: '@/src'
64 | )
65 | ];
66 | ```
67 |
68 | ### VueCli3
69 |
70 | vueCli3 will be easier than webpack
71 |
72 | `vue.config.js`
73 |
74 | ```javascript
75 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
76 |
77 | module.exports = {
78 | // omit other options...
79 | configureWebpack(config) {
80 | config.plugins.push(
81 | new VueRouterInvokeWebpackPlugin({
82 | dir: 'src/views',
83 | // must set the alias for the dir option which you have set
84 | alias: '@/views'
85 | })
86 | );
87 | }
88 | };
89 |
90 | // or another way..
91 |
92 | module.exports = {
93 | // omit other options...
94 | configureWebpack: {
95 | plugins: [
96 | new VueRouterInvokeWebpackPlugin({
97 | dir: 'src/views',
98 | // must set the alias for the dir option which you have set
99 | alias: '@/views'
100 | })
101 | ]
102 | }
103 | };
104 | ```
105 |
106 | ### Start
107 |
108 | After configure the options you can use `npm run serve` or some other scripts that you defined to activate the plugin in the development environment. When first generated or the file which in the `dir` option's direction changes.`router.js` will be automatic generated.
109 |
110 | And you can use `npm run build` or some other scripts that you defined to activate the plugin in the production environment. `router.js` will be automatic generated.
111 |
112 | ## Options
113 |
114 | | Prop | Type | Required | Default | Description |
115 | | -------------- | :------: | :------: | :----------: | ---------------------------------------: |
116 | | dir | String | true | '' | vue file directory |
117 | | alias | String | true | '' | the option `dir`'s alias |
118 | | notFound | String | false | '' | the alias address of notFound chunk |
119 | | mode | String | false | history | hash or history |
120 | | meta | String | false | meta | the yml file's name |
121 | | routerDir | String | false | ROOT | generated router.js file |
122 | | language | String | false | javascript | javascript or typescript |
123 | | ignore | Array | false | ['.dsstore'] | files or directions will not be resolved |
124 | | redirect | Array | false | [] | redirect route |
125 | | modules | Array | false | [] | the import modules |
126 | | scrollBehavior | Function | false | '' | same as scrollBehavior |
127 | | beforeEach | Function | false | '' | router.beforeEach |
128 | | beforeResolve | Function | false | '' | router.beforeResolve |
129 | | afterEach | Function | false | '' | router.afterEach |
130 |
131 | ## How To Automatical Invoke
132 |
133 | The following example depends on VueCli3. I believe that if you know how to use in VueCli3,the using of webpack is easy for you.
134 |
135 | `vue.config.js`
136 |
137 | ```javascript
138 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
139 |
140 | module.exports = {
141 | // omit other options...
142 | configureWebpack(config) {
143 | config.plugins.push(
144 | new VueRouterInvokeWebpackPlugin({
145 | dir: 'src/views',
146 | alias: '@/views'
147 | })
148 | );
149 | }
150 | };
151 | ```
152 |
153 | And import `router.js` in your entry file `src/main.js`
154 |
155 | The default location of `router.js` is under the invoke folder in the root directory,You can change the location anywhere by setting the `routerDir` option
156 |
157 | The address of `routerDir` is relative to `ROOT`, Pay attention to that it is not a absolute address
158 |
159 | And I recommoned that `router.js` may put into `.gitignore` or `.eslintignore`. Everyone's branch can be independent because `router.js` will be automatic generated
160 |
161 | ```javascript
162 | import Vue from 'vue';
163 | import App from './App.vue';
164 | import router from '../.invoke/router';
165 |
166 | export default new Vue({
167 | el: '#app',
168 | router,
169 | render: h => h(App)
170 | });
171 | ```
172 |
173 | ### SingleRoute
174 |
175 | Please pay attention to that there is a direcotry which wrapping the `Index.vue`,Do not name `vue` directly.It maybe not quite in the usual way
176 |
177 | The same, do not name the directory with `Index`, it may have diffrent sense on `Nested Route`
178 |
179 | > version 0.2.7, The plugin will throw an error when the wrong naming of the directory in production environment and will show you a danger notice in development environment
180 |
181 | So if you see that
182 |
183 | 
184 |
185 | The rule of naming about your directory maybe wrong
186 |
187 | If your directory just like this
188 |
189 | ```
190 | src
191 | ├── views
192 | │ ├── Login
193 | │ │ └── Index.vue
194 | │ └── User
195 | │ ├── Account
196 | │ │ └── Index.vue
197 | │ ├── Home
198 | │ │ └── Index.vue
199 | │ └── Index.vue
200 | ```
201 |
202 | automatical generated route will be this
203 |
204 | ```javascript
205 | {
206 | component: () =>
207 | import('@/views/Login/Index.vue'),
208 | name: 'login',
209 | path: '/login'
210 | },
211 | {
212 | component: () =>
213 | import('@/views/User/Index.vue'),
214 | name: 'user',
215 | path: '/user'
216 | },
217 | {
218 | component: () =>
219 | import('@/views/User/Account/Index.vue'),
220 | name: 'user-account',
221 | path: '/user/account'
222 | },
223 | {
224 | component: () =>
225 | import('@/views/User/Home/Index.vue'),
226 | name: 'user-home',
227 | path: '/user/home'
228 | }
229 | ```
230 |
231 | ### HomePage
232 |
233 | We make a special treatment for HomePage which route is `/`
234 |
235 | HomePage we named `Index.vue` and is a unique route
236 |
237 | If your directory just like this
238 |
239 | ```
240 | src
241 | ├── views
242 | │ ├── Login
243 | │ │ └── Index.vue
244 | │ └── Index.vue
245 | ```
246 |
247 | automatical generated route will be this
248 |
249 | ```javascript
250 | {
251 | component: () =>
252 | import('@/views/Index.vue'),
253 | name: 'index',
254 | path: '/'
255 | },
256 | {
257 | component: () =>
258 | import('@/views/Login/Index.vue'),
259 | name: 'login',
260 | path: '/login'
261 | }
262 | ```
263 |
264 | ### Dynamic Route
265 |
266 | If your directory just like this
267 |
268 | ```
269 | src
270 | ├── views
271 | │ ├── Login
272 | │ │ └── Index.vue
273 | │ └── User
274 | │ ├── _Home
275 | │ │ └── Index.vue
276 | │ └── Index.vue
277 | ```
278 |
279 | automatical generated route will be this
280 |
281 | ```javascript
282 | {
283 | component: () =>
284 | import('@/views/Login/Index.vue'),
285 | name: 'login',
286 | path: '/login'
287 | },
288 | {
289 | component: () =>
290 | import('@/views/User/Index.vue'),
291 | name: 'user',
292 | path: '/user'
293 | },
294 | {
295 | component: () =>
296 | import('@/views/User/_Home/Index.vue'),
297 | name: 'user-home',
298 | path: '/user/:home'
299 | }
300 | ```
301 |
302 | ### Nested Route
303 |
304 | If your directory just like this
305 |
306 | ```
307 | src
308 | ├── views
309 | │ ├── Login
310 | │ │ └── Index.vue
311 | │ └── User
312 | │ ├── Chart
313 | │ │ └── Index.vue
314 | │ ├── Home
315 | │ │ └── Index.vue
316 | │ └── User.vue
317 | ```
318 |
319 | automatical generated route will be this
320 |
321 | ```javascript
322 | {
323 | component: () =>
324 | import('@/views/Login/Index.vue'),
325 | name: 'login',
326 | path: '/login'
327 | },
328 | {
329 | component: () =>
330 | import('@/views/User/User.vue'),
331 | name: 'user',
332 | path: '/user',
333 | children: [
334 | {
335 | component: () =>
336 | import('@/views/User/Chart/Index.vue'),
337 | name: 'user-chart',
338 | path: 'chart'
339 | },
340 | {
341 | component: () =>
342 | import('@/views/User/Home/Index.vue'),
343 | name: 'user-home',
344 | path: 'home'
345 | }
346 | ]
347 | }
348 | ```
349 |
350 | ### Dymaic and Nested Route
351 |
352 | If your directory just like this
353 |
354 | ```
355 | src
356 | ├── views
357 | │ ├── Login
358 | │ │ └── Index.vue
359 | │ └── User
360 | │ ├── _Category
361 | │ │ ├── _Category.vue
362 | │ │ └── Infor
363 | │ │ └── Index.vue
364 | │ └── Index.vue
365 | ```
366 |
367 | automatical generated route will be this
368 |
369 | ```javascript
370 | {
371 | component: () =>
372 | import('@/views/Login/Index.vue'),
373 | name: 'login',
374 | path: '/login'
375 | },
376 | {
377 | component: () =>
378 | import('@/views/User/Index.vue'),
379 | name: 'user',
380 | path: '/user'
381 | },
382 | {
383 | component: () =>
384 | import('@/views/User/_Category/_Category.vue'),
385 | name: 'user-category',
386 | path: '/user/:category',
387 | children: [
388 | {
389 | component: () =>
390 | import('@/views/User/_Category/Infor/Index.vue'),
391 | name: 'user-category-infor',
392 | path: 'infor'
393 | }
394 | ]
395 | }
396 | ```
397 |
398 | ## Correct the name
399 |
400 | We will transform diffetent rule of naming into `upperCamelCase` naming
401 |
402 | For Example
403 |
404 | ```
405 | src
406 | ├── views
407 | │ ├── LoginPage
408 | │ │ └── index.vue
409 | │ └── User-home
410 | │ ├── account
411 | │ │ └── Index.vue
412 | │ ├── Home-details
413 | │ │ └── Index.vue
414 | │ └── Index.vue
415 | ```
416 |
417 | automatical generated route will be this
418 |
419 | ```javascript
420 | {
421 | component: () => import('@/views/LoginPage/index.vue'),
422 | name: 'loginPage',
423 | path: '/loginPage'
424 | },
425 | {
426 | component: () => import('@/views/User-home/Index.vue'),
427 | name: 'userHome',
428 | path: '/userHome'
429 | },
430 | {
431 | component: () => import('@/views/User-home/Home-details/Index.vue'),
432 | name: 'userHome-homeDetails',
433 | path: '/userHome/homeDetails'
434 | },
435 | {
436 | component: () => import('@/views/User-home/account/Index.vue'),
437 | name: 'userHome-account',
438 | path: '/userHome/account'
439 | },
440 | ```
441 |
442 | ## Meta Succedaneum
443 |
444 | The `meta` option in `vue-router` can resolve many questions.Just like define the title of a page or define a page is necessary to login or not.
445 |
446 | Some of the questions just like define the page title can be resolved by [vue-meta](https://github.com/nuxt/vue-meta).That is a fantastic repository.
447 |
448 | But if you really need define the plain `meta` option of `vue-router` .You should make a `yml` file.
449 |
450 | For example
451 |
452 | ```javascript
453 | src/views
454 | ├── Single
455 | │ ├── Index.vue
456 | │ └── User
457 | │ ├── Index.vue
458 | │ └── meta.yml
459 | ```
460 |
461 | `meta.yml`
462 |
463 | ```yml
464 | meta:
465 | - name: user
466 | ```
467 |
468 | automatical generated route will be this
469 |
470 | ```javascript
471 | {
472 | component: () => import('@/views/Single/Index.vue'),
473 | name: 'single',
474 | path: 'single'
475 | },
476 | {
477 | component: () => import('@/views/Single/User/Index.vue'),
478 | name: 'single-user',
479 | meta: { name: user },
480 | path: 'single/user'
481 | }
482 | ```
483 |
484 | > Version greater than 0.4.1, meta's type supports `boolean` `string` `array` `plain object`, but it doesn't support `Symbol` `function` `undefined` `circled object` that can't be translated by `JSON.stringify`
485 |
486 | ## Special Options
487 |
488 | ### NotFound
489 |
490 | If your set options like this
491 |
492 | ```javascript
493 | plugins: [
494 | new VueRouterInvokeWebpackPlugin({
495 | dir: 'src/views',
496 | alias: '@/views',
497 | // muse set ignore for notFound chunk
498 | ignore: ['NotFound.vue'],
499 | notFound: '@/views/NotFound.vue'
500 | })
501 | ];
502 | ```
503 |
504 | the directory
505 |
506 | ```
507 | src
508 | ├── views
509 | │ ├── Login
510 | │ │ └── Index.vue
511 | │ └── Index.vue
512 | │ └── NotFound.vue
513 |
514 | ```
515 |
516 | automatical generated route will be this
517 |
518 | ```javascript
519 | {
520 | component: () =>
521 | import('@/views/Index.vue'),
522 | name: 'index',
523 | path: '/'
524 | },
525 | {
526 | component: () =>
527 | import('@/views/NotFound.vue'),
528 | name: 'notFound',
529 | path: '*'
530 | },
531 | {
532 | component: () =>
533 | import('@/views/Login/Index.vue'),
534 | name: 'login',
535 | path: '/login'
536 | }
537 | ```
538 |
539 | ### Ignore
540 |
541 | If your set options like this
542 |
543 | `images` `components` `template.vue` will not be resolved by the plugin
544 |
545 | Above Version `0.4.3` you can use `RegExp` to ignore files
546 |
547 | And the value ignore case
548 |
549 | ```javascript
550 | plugins: [
551 | new VueRouterInvokeWebpackPlugin({
552 | dir: 'src/views',
553 | alias: '@/views',
554 | language: 'javascript',
555 | ignore: ['images', 'components', 'template.vue', /\.scss$/]
556 | })
557 | ];
558 | ```
559 |
560 | the directory
561 |
562 | ```
563 | src
564 | ├── views
565 | │ ├── Login
566 | │ │ └── Index.vue
567 | │ │ └── Index.scss
568 | │ ├── Template.vue
569 | │ └── User
570 | │ ├── Components
571 | │ ├── Images
572 | │ └── Index.vue
573 | ```
574 |
575 | automatical generated route will be this
576 |
577 | ```javascript
578 | {
579 | component: () =>
580 | import('@/views/Login/Index.vue'),
581 | name: 'login',
582 | path: '/login'
583 | },
584 | {
585 | component: () =>
586 | import('@/views/User/Index.vue'),
587 | name: 'user',
588 | path: '/user'
589 | }
590 | ```
591 |
592 | Obviously The plugin ignores the files
593 |
594 | ### Redirect
595 |
596 | If your set options like this
597 |
598 | ```javascript
599 | plugins: [
600 | new VueRouterInvokeWebpackPlugin({
601 | dir: 'src/views',
602 | alias: '@/views',
603 | language: 'javascript',
604 | redirect: [
605 | {
606 | redirect: '/',
607 | path: '/home'
608 | },
609 | {
610 | redirect: '/test',
611 | path: '/demo'
612 | }
613 | ]
614 | })
615 | ];
616 | ```
617 |
618 | automatical generated route will be this
619 |
620 | ```javascript
621 | {
622 | path: '/home',
623 | redirect: '/'
624 | },
625 | {
626 | path: '/demo',
627 | redirect: '/test'
628 | }
629 | ```
630 |
631 | #### Redirect In yml
632 |
633 | > Feature In `0.4.0`
634 |
635 | you can add redirect path by using `yml`
636 |
637 | For example
638 |
639 | ```javascript
640 | src/views
641 | ├── Single
642 | │ ├── Index.vue
643 | │ └── User
644 | │ ├── Index.vue
645 | │ └── meta.yml
646 | ```
647 |
648 | `meta.yml`
649 |
650 | ```yml
651 | redirect:
652 | - path: /test
653 | ```
654 |
655 | automatical generated route will be this
656 |
657 | ```javascript
658 | {
659 | component: () => import('@/views/Single/Index.vue'),
660 | name: 'single',
661 | path: 'single'
662 | },
663 | {
664 | component: () => import('@/views/Single/User/Index.vue'),
665 | name: 'single-user',
666 | path: 'single/user',
667 | redirect: {
668 | path: '/test'
669 | },
670 | }
671 | ```
672 |
673 | ### Modules
674 |
675 | The generated `router.js` has Two modules
676 |
677 | ```javascript
678 | import Vue from 'vue';
679 | import Router from 'vue-router';
680 | ```
681 |
682 | If you need some other module which would use in `beforeEach` or some other place you can define it by using `modules`. For example
683 |
684 | ```javascript
685 | new VueRouterInvokeWebpackPlugin({
686 | dir: 'src/views',
687 | alias: '@/views',
688 | modules: [
689 | {
690 | name: 'diyName',
691 | package: 'some-packages'
692 | }
693 | ]
694 | });
695 | ```
696 |
697 | automatical generated route will be this
698 |
699 | ```javascript
700 | // omit other options
701 | import diyName from 'some-packages';
702 | ```
703 |
704 | ### VueRouter Guards
705 |
706 | we have supported VueRouter's Guards `beforeEach` `beforeResolve` `afterEach`
707 |
708 | If your set options like this
709 |
710 | ```javascript
711 | new VueRouterInvokeWebpackPlugin({
712 | dir: 'src/views',
713 | alias: '@/views',
714 | language: 'javascript',
715 | beforeEach: (to, from, next) => {
716 | next();
717 | },
718 | beforeResolve: (to, from, next) => {
719 | next();
720 | },
721 | afterEach: (to, from) => {}
722 | });
723 | ```
724 |
725 | automatical generated route will be this
726 |
727 | ```javascript
728 | // omit others ...
729 | const router = new Router({ mode: 'history', routes });
730 | router.beforeEach((to, from, next) => {
731 | next();
732 | });
733 |
734 | router.beforeResolve((to, from, next) => {
735 | next();
736 | });
737 |
738 | router.afterEach((to, from) => {});
739 | export default router;
740 | ```
741 |
742 | ### ScrollBehavior
743 |
744 | If your set options like this
745 |
746 | ```javascript
747 | new VueRouterInvokeWebpackPlugin({
748 | dir: 'src/views',
749 | alias: '@/views',
750 | language: 'javascript',
751 | scrollBehavior: (to, from, savedPosition) => {
752 | if (savedPosition) {
753 | return savedPosition;
754 | } else {
755 | return { x: 0, y: 0 };
756 | }
757 | }
758 | });
759 | ```
760 |
761 | automatical generated route will be this
762 |
763 | ```javascript
764 | // omit others...
765 | const router = new Router({
766 | mode: 'history',
767 | routes,
768 | scrollBehavior: (to, from, savedPosition) => {
769 | if (savedPosition) {
770 | return savedPosition;
771 | } else {
772 | return { x: 0, y: 0 };
773 | }
774 | }
775 | });
776 | ```
777 |
778 | ## Demos
779 |
780 | The detailed usage you can `git clone` our project and run `npm run build:demos` or you can just watch our [demos](https://github.com/Qymh/vue-router-invoke-webpack-plugin/tree/master/demos) directly.The demos dont't have substantial content,the more we focus is on the generation of directory,you can get how `router.js` generated in the demos.
781 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional']
3 | };
4 |
--------------------------------------------------------------------------------
/core/ast.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const yamljs = require('js-yaml');
5 | const {
6 | warn,
7 | tips,
8 | camelize,
9 | replaceVue,
10 | firstLowerCase,
11 | replaceAlias,
12 | replaceArtificialDynamic,
13 | makeMap,
14 | diff
15 | } = require('./utils');
16 | const {
17 | isFile,
18 | root,
19 | getRouterDir,
20 | getWatchDir,
21 | generateIgnoreFiles,
22 | generateModules
23 | } = require('./files');
24 | const routeStringPreJs = modules =>
25 | `import Vue from 'vue';import Router from 'vue-router';${modules};Vue.use(Router);export const routes = [`;
26 | const routeStringPreTs = modules =>
27 | `import Vue from 'vue';import Router, { RouteConfig } from 'vue-router';${modules};Vue.use(Router);export const routes: RouteConfig[] = [`;
28 | const routeStringPostFn = (mode, behavior) =>
29 | `];const router = new Router({mode: '${mode}',routes,${behavior &&
30 | 'scrollBehavior:' + behavior}});`;
31 | const routeStringExport = 'export default router;';
32 |
33 | const modeMap = makeMap('hash,history');
34 | const languageMap = makeMap('javascript,typescript');
35 |
36 | function sortByIsFile(arr) {
37 | return arr.sort((a, b) => Number(b.isFile) - Number(a.isFile));
38 | }
39 |
40 | function generateYmlReg(meta) {
41 | this.metaYmlReg = new RegExp(`^${meta}\\.yml$`, 'i');
42 | }
43 |
44 | let nestCollections = {};
45 |
46 | /**
47 | * @param {Object} options
48 | */
49 | function init(options) {
50 | const mode = options.mode || 'history';
51 | const language = options.language || 'javascript';
52 | const meta = options.meta || 'meta';
53 | if (!modeMap(mode)) {
54 | warn(
55 | `the mode can only be hash or history, make sure you have set the value correctly`
56 | );
57 | }
58 | if (!languageMap(language)) {
59 | warn(
60 | `the language can only be javascript or typescript, make sure you have set the value correctly`
61 | );
62 | }
63 | if (!options.dir) {
64 | warn(`the dir option is required please set the main files of vue`);
65 | }
66 | if (!options.alias) {
67 | warn(
68 | `the alias option is required, make sure you have set the alias of the dir option: ${options.dir} `
69 | );
70 | }
71 | let behavior = '';
72 | if (options.scrollBehavior) {
73 | behavior = options.scrollBehavior.toString();
74 | }
75 | const modules = generateModules(options);
76 | this.isFirst = this.isFirst !== false;
77 | this.metaYmlReg = '';
78 | this.routerDir = '';
79 | this.watchDir = '';
80 | this.routeString = '';
81 | this.ignoreRegExp = '';
82 | this.nestArr = [];
83 | this.routeStringPre =
84 | language === 'javascript'
85 | ? routeStringPreJs(modules)
86 | : routeStringPreTs(modules);
87 | this.routeStringPost = routeStringPostFn(mode, behavior);
88 | this.routeStringExport = routeStringExport;
89 | this.alias = options.alias;
90 | this.dir = options.dir;
91 | generateYmlReg.call(this, meta);
92 | getRouterDir.call(this, options);
93 | generateIgnoreFiles.call(this, options);
94 | getWatchDir.call(this, options);
95 | this.routeString += this.routeStringPre;
96 | this.filesAst = [];
97 | }
98 |
99 | exports.init = init;
100 |
101 | /**
102 | *
103 | * @param {String} dir
104 | * @param {Array} filesAst
105 | * @param {Object} parent
106 | */
107 | function generateFilesAst(dir, filesAst, parent) {
108 | const files = fs.readdirSync(dir);
109 | if (!files.length && !parent) {
110 | warn(
111 | `the directory ${dir} is empty, make sure you have set the directory correctly`
112 | );
113 | }
114 | for (const file of files) {
115 | const curAst = {};
116 | const fileLowerCase = firstLowerCase(file);
117 | const curDir = `${root}/${dir}/${file}`;
118 | if (this.metaYmlReg.test(file)) {
119 | const ymlStr = fs.readFileSync(curDir, 'utf8');
120 | let ymlObj;
121 | try {
122 | ymlObj = yamljs.load(ymlStr);
123 | } catch (error) {
124 | tips(error.message);
125 | ymlObj = undefined;
126 | }
127 | parent.children.map(v => {
128 | if (!this.metaYmlReg.test(v.file) && v.isFile) {
129 | v.meta = ymlObj && ymlObj.meta;
130 | v.redirect = ymlObj && ymlObj.redirect;
131 | }
132 | });
133 | }
134 | curAst.dir = curDir;
135 | curAst.alias = `${this.alias}${replaceAlias(dir, this.dir)}/${file}`;
136 | curAst.isVue = /\.vue$/.test(fileLowerCase);
137 | curAst.file = camelize(replaceVue(fileLowerCase));
138 | curAst.isFile = isFile(curDir);
139 | if (parent) {
140 | curAst.isNest = curAst.file.trim() === camelize(parent.file).trim();
141 | curAst.parentName = parent.parentName.concat(parent.file);
142 | } else {
143 | curAst.parentName = [];
144 | }
145 | filesAst.push(curAst);
146 |
147 | if (this.ignoreRegExp.test(curAst.alias)) {
148 | curAst.ignore = true;
149 | }
150 |
151 | let multipleError;
152 |
153 | // fix empty vue
154 | if (
155 | (curAst.isFile &&
156 | !(
157 | curAst.file === parent.file ||
158 | (curAst.file && curAst.file.toLowerCase() === 'index') ||
159 | (this.metaYmlReg && this.metaYmlReg.test(curAst.file))
160 | )) ||
161 | (multipleError =
162 | parent.children && parent.children.filter(v => v.isVue).length === 2)
163 | ) {
164 | if (!this.ignoreRegExp.test(curAst.alias)) {
165 | curAst.ignore = true;
166 | tips(
167 | `\n'${curAst.alias}' ${
168 | multipleError
169 | ? 'is mixed with nested and single route'
170 | : 'is not in accordance with the rules \n you can not name it directly without a file wraps it '
171 | }\n you may check the correct use in documentation https://github.com/Qymh/vue-router-invoke-webpack-plugin#singleroute\n or you should make sure you have set it in the ignore option`
172 | );
173 | if (this.isFirst) {
174 | warn(
175 | `\n'${curAst.alias}' ${
176 | multipleError
177 | ? 'is mixed by nested and single route'
178 | : 'is not in accordance with the rules \n you can not name it directly without a file wraps it '
179 | }\n you may check the correct use in documentation https://github.com/Qymh/vue-router-invoke-webpack-plugin#singleroute\n or you should make sure you have set it in the ignore option`
180 | );
181 | }
182 | }
183 | }
184 |
185 | if (!curAst.isFile) {
186 | curAst.children = [];
187 | generateFilesAst.call(this, `${dir}/${file}`, curAst.children, curAst);
188 | }
189 | }
190 | }
191 |
192 | /**
193 | * isFile:true will in front of isFile:false
194 | * @param {Array} filesAst
195 | */
196 | function sortFilesAst(filesAst) {
197 | sortByIsFile(filesAst);
198 | for (const item of filesAst) {
199 | if (item.children) {
200 | sortFilesAst.call(this, item.children);
201 | }
202 | }
203 | }
204 |
205 | /**
206 | * keep original value
207 | * @param {string} key
208 | * @param {any} value
209 | */
210 | function handleKeyValueType(key, value) {
211 | const type = typeof value;
212 | switch (type) {
213 | case 'object':
214 | return `${key}: ${JSON.stringify(value)},`;
215 | case 'string':
216 | return `${key}: '${value}',`;
217 | default:
218 | return `${key}: ${value},`;
219 | }
220 | }
221 |
222 | /**
223 | *
224 | * @param {Array} filesAst
225 | * @param {Object} pre
226 | */
227 | function generateRouteString(filesAst, pre) {
228 | if (!pre) {
229 | nestCollections = {};
230 | }
231 | for (const item of filesAst) {
232 | // fix when non-compliance file
233 | if (item.ignore) {
234 | } else {
235 | if (!item.isFile) {
236 | generateRouteString.call(this, item.children, item);
237 | } else {
238 | if (this.metaYmlReg.test(item.file)) {
239 | if (nestCollections[item.parentName.join('-')]) {
240 | nestCollections[item.parentName.join('-')]--;
241 | }
242 | } else {
243 | this.routeString += `
244 | {
245 | component: () => import('${item.alias}'),
246 | name:'${
247 | item.parentName.length
248 | ? item.parentName
249 | .map(v => replaceArtificialDynamic(v))
250 | .join('-')
251 | : 'index'
252 | }',
253 | `;
254 | if (item.meta) {
255 | this.routeString += `meta:{`;
256 | for (const meta of item.meta) {
257 | for (const key in meta) {
258 | this.routeString += handleKeyValueType(key, meta[key]);
259 | }
260 | }
261 | this.routeString += `},`;
262 | }
263 | if (item.redirect) {
264 | this.routeString += `redirect:{`;
265 | for (const redirect of item.redirect) {
266 | for (const key in redirect) {
267 | this.routeString += `${key}:'${redirect[key]}',`;
268 | }
269 | }
270 | this.routeString += `},`;
271 | }
272 | if (Object.keys(nestCollections).length) {
273 | const curNest = this.nestArr[this.nestArr.length - 1].split('-');
274 | const res = diff(curNest, item.parentName);
275 | this.routeString += `path:'${res.join('/')}',`;
276 | } else {
277 | this.routeString += `path:'/${item.parentName.join('/')}',`;
278 | }
279 | if (item.isNest) {
280 | this.nestArr.push(item.parentName.join('-'));
281 | // fix when directory is empty and non-compliance file
282 | pre.children = pre.children.filter(v => {
283 | return (v.children && v.children.length) !== 0 && !v.ignore;
284 | });
285 | nestCollections[item.parentName.join('-')] =
286 | pre.children.length - 1;
287 | this.routeString += `children:[`;
288 | if (pre.children.length - 1 === 0) {
289 | this.routeString += '],},';
290 | nestCollections[item.parentName.join('-')]--;
291 | }
292 | } else {
293 | this.routeString += '},';
294 | }
295 | const isNestChild = this.nestArr.some(v =>
296 | pre.parentName.join('-').includes(v)
297 | );
298 | if (isNestChild) {
299 | this.nestArr.forEach(v => {
300 | if (pre.parentName.join('-').includes(v)) {
301 | nestCollections[v]--;
302 | }
303 | });
304 | // fix when meta.yml is empty
305 | if (item.meta !== undefined) {
306 | nestCollections[pre.parentName.join('-')] -= 1;
307 | }
308 | // fix when nested route which has more than two childish routes
309 | if (pre.children.length >= 2) {
310 | nestCollections[pre.parentName.join('-')] +=
311 | pre.children.length - 1;
312 | }
313 | for (const key in nestCollections) {
314 | const val = nestCollections[key];
315 | if (val === 0) {
316 | delete nestCollections[key];
317 | this.routeString += '],},';
318 | }
319 | }
320 | }
321 | }
322 | }
323 | }
324 | }
325 | }
326 |
327 | exports.generateFilesAst = generateFilesAst;
328 | exports.sortFilesAst = sortFilesAst;
329 | exports.generateRouteString = generateRouteString;
330 |
--------------------------------------------------------------------------------
/core/files.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const fse = require('fs-extra');
5 | const chokidar = require('chokidar');
6 | const beautify = require('js-beautify').js;
7 | const { toPlain } = require('./utils');
8 | const isFile = dir => fs.statSync(dir).isFile();
9 |
10 | let isRunning = false;
11 |
12 | exports.isFile = isFile;
13 |
14 | const root = process.cwd();
15 | exports.root = root;
16 |
17 | function writeFile(options) {
18 | if (!fs.existsSync(this.routerDir)) {
19 | if (options.routerDir) {
20 | fse.ensureDirSync(`${root}/${options.routerDir}/.invoke`);
21 | } else {
22 | fse.ensureDirSync(`${root}/.invoke`);
23 | }
24 | fs.writeFileSync(
25 | this.routerDir,
26 | beautify(this.routeString, { indent_size: 2, space_in_empty_paren: true })
27 | );
28 | isRunning = false;
29 | this.isFirst = false;
30 | } else {
31 | fs.writeFileSync(
32 | this.routerDir,
33 | beautify(this.routeString, { indent_size: 2, space_in_empty_paren: true })
34 | );
35 | isRunning = false;
36 | this.isFirst = false;
37 | }
38 | }
39 |
40 | function watchFile(options, start) {
41 | writeFile.call(this, options);
42 | const watcher = chokidar.watch(this.watchDir, { persistent: true });
43 | watcher.on('raw', (event, path) => {
44 | if ((event === 'modified' && !/\.yml$/.test(path)) || isRunning) {
45 | return;
46 | }
47 | isRunning = true;
48 | start.call(this, options);
49 | writeFile.call(this, options);
50 | });
51 | }
52 |
53 | exports.writeOrWatchFile = function(options, start) {
54 | const isDev = process.env.NODE_ENV === 'development';
55 | isDev ? watchFile.call(this, options, start) : writeFile.call(this, options);
56 | };
57 |
58 | exports.getRouterDir = function(options) {
59 | const routerDir = options.routerDir;
60 | const ext = options.language
61 | ? options.language === 'javascript'
62 | ? '.js'
63 | : '.ts'
64 | : '.js';
65 | if (routerDir) {
66 | this.routerDir = `${root}/${routerDir}/.invoke/router${ext}`;
67 | } else {
68 | this.routerDir = `${root}/.invoke/router${ext}`;
69 | }
70 | };
71 |
72 | exports.getWatchDir = function(options) {
73 | this.watchDir = `${root}/${options.dir}`;
74 | };
75 |
76 | exports.generateIgnoreFiles = function(options) {
77 | options.ignore = options.ignore
78 | ? [...options.ignore, '.dsstore']
79 | : ['.dsstore'];
80 | options.ignore = options.ignore.map(v => {
81 | if (toPlain(v) === 'RegExp') {
82 | return v.toString().replace(/(\/)\B/g, '');
83 | } else if (v) {
84 | return v;
85 | }
86 | });
87 | const reg = new RegExp(`(${options.ignore.join('|')})`, 'i');
88 | this.ignoreRegExp = reg;
89 | };
90 |
91 | exports.generateRedirectRoute = function(options) {
92 | const { redirect } = options;
93 | if (!redirect) {
94 | return;
95 | }
96 | for (const item of redirect) {
97 | this.routeString += `
98 | {
99 | path:'${item.path}',
100 | redirect:'${item.redirect}'
101 | },
102 | `;
103 | }
104 | };
105 |
106 | exports.generateGuards = function(options) {
107 | if (options.beforeEach) {
108 | const str = options.beforeEach.toString();
109 | this.routeString += `
110 | router.beforeEach(${str});
111 | `;
112 | }
113 | if (options.beforeResolve) {
114 | const str = options.beforeResolve.toString();
115 | this.routeString += `
116 | router.beforeResolve(${str});
117 | `;
118 | }
119 | if (options.afterEach) {
120 | const str = options.afterEach.toString();
121 | this.routeString += `
122 | router.afterEach(${str});
123 | `;
124 | }
125 | };
126 |
127 | exports.generateModules = function(options) {
128 | let str = '';
129 | if (options.modules) {
130 | for (const module of options.modules) {
131 | str += `import ${module.name} from '${module.package}';`;
132 | }
133 | }
134 | return str;
135 | };
136 |
137 | exports.generateNotFound = function(options) {
138 | if (options.notFound) {
139 | this.routeString += `
140 | {
141 | name:'notFound',
142 | path:'*',
143 | component: () => import('${options.notFound}')
144 | },
145 | `;
146 | }
147 | };
148 |
--------------------------------------------------------------------------------
/core/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { invoke } = require('./invoke');
4 | class VueRouterInvokeWebpackPlugin {
5 | constructor(options) {
6 | this.$options = options;
7 | this.routerDir = '';
8 | }
9 |
10 | apply(compiler) {
11 | // webpack4
12 | if (compiler && compiler.hooks && compiler.hooks.entryOption) {
13 | compiler.hooks.entryOption.tap('invoke', () => {
14 | invoke.call(this, this.$options);
15 | });
16 | }
17 | // webpack3
18 | else {
19 | compiler.plugin('entry-option', () => {
20 | invoke.call(this, this.$options);
21 | });
22 | }
23 | }
24 | }
25 |
26 | module.exports = VueRouterInvokeWebpackPlugin;
27 |
--------------------------------------------------------------------------------
/core/invoke.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const {
4 | init,
5 | generateRouteString,
6 | sortFilesAst,
7 | generateFilesAst
8 | } = require('./ast');
9 | const {
10 | generateRedirectRoute,
11 | generateGuards,
12 | writeOrWatchFile,
13 | generateNotFound
14 | } = require('./files');
15 |
16 | function start(options) {
17 | init.call(this, options);
18 | generateFilesAst.call(this, options.dir, this.filesAst, '');
19 | sortFilesAst.call(this, this.filesAst);
20 | // console.dir(this.filesAst, { depth: null });
21 | generateRouteString.call(this, this.filesAst);
22 | generateRedirectRoute.call(this, options);
23 | generateNotFound.call(this, options);
24 | this.routeString += this.routeStringPost;
25 | generateGuards.call(this, options);
26 | this.routeString += this.routeStringExport;
27 | }
28 |
29 | exports.start = start;
30 |
31 | exports.invoke = function(options) {
32 | start.call(this, options);
33 | writeOrWatchFile.call(this, options, start);
34 | };
35 |
--------------------------------------------------------------------------------
/core/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const assert = require('assert');
4 |
5 | exports.warn = msg => {
6 | assert.fail(`\n\n\x1B[31mvue-router-invoke-webpack-plugin:${msg} \x1b[39m\n`);
7 | };
8 |
9 | exports.tips = msg => {
10 | // eslint-disable-next-line
11 | console.log(`\n\n\x1B[31mvue-router-invoke-webpack-plugin:${msg} \x1b[39m\n`);
12 | };
13 |
14 | exports.firstLowerCase = ([first, ...rest]) => {
15 | if (first === '_') {
16 | return first + rest.shift().toLowerCase() + rest.join('');
17 | } else {
18 | return first.toLowerCase() + rest.join('');
19 | }
20 | };
21 |
22 | exports.replaceAlias = (str, dir) => {
23 | return str.replace(new RegExp(dir, 'i'), '');
24 | };
25 |
26 | exports.replaceVue = str => str.replace(/\.vue/g, '');
27 |
28 | exports.camelize = str =>
29 | str.replace(/[-_](\w)/g, (_, c, i) => {
30 | return i === 0 ? `:${c}` : c.toUpperCase();
31 | });
32 |
33 | exports.makeMap = str => {
34 | const map = Object.create(null);
35 | const list = str.split(',');
36 | for (let i = 0; i < list.length; i++) {
37 | map[list[i]] = true;
38 | }
39 | return val => map[val];
40 | };
41 |
42 | exports.replaceArtificialDynamic = str => str.replace(/:/g, '');
43 |
44 | exports.diff = (a, b) => {
45 | const aSet = new Set(a);
46 | return b.filter(v => !aSet.has(v));
47 | };
48 |
49 | exports.toPlain = value => Object.prototype.toString.call(value).slice(8, -1);
50 |
--------------------------------------------------------------------------------
/demos/.invoke/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 | import apis from '@/apis';;
4 | Vue.use(Router);
5 | export const routes = [{
6 | component: () => import('@/src/Index.vue'),
7 | name: 'index',
8 | path: '/',
9 | },
10 | {
11 | component: () => import('@/src/Complex/Index.vue'),
12 | name: 'complex',
13 | path: '/complex',
14 | },
15 | {
16 | component: () => import('@/src/Complex/Home/Home.vue'),
17 | name: 'complex-home',
18 | path: '/complex/home',
19 | children: [{
20 | component: () => import('@/src/Complex/Home/Account/Account.vue'),
21 | name: 'complex-home-account',
22 | path: 'account',
23 | children: [{
24 | component: () => import('@/src/Complex/Home/Account/Chunk/Index.vue'),
25 | name: 'complex-home-account-chunk',
26 | path: 'chunk',
27 | },
28 | {
29 | component: () => import('@/src/Complex/Home/Account/Inner/Index.vue'),
30 | name: 'complex-home-account-inner',
31 | path: 'inner',
32 | },
33 | {
34 | component: () => import('@/src/Complex/Home/Account/_Dynamic/Index.vue'),
35 | name: 'complex-home-account-dynamic',
36 | path: ':dynamic',
37 | },
38 | ],
39 | },
40 | {
41 | component: () => import('@/src/Complex/Home/Details/Details.vue'),
42 | name: 'complex-home-details',
43 | meta: {
44 | name: 'details',
45 | },
46 | path: 'details',
47 | children: [{
48 | component: () => import('@/src/Complex/Home/Details/Infor/Index.vue'),
49 | name: 'complex-home-details-infor',
50 | path: 'infor',
51 | },
52 | {
53 | component: () => import('@/src/Complex/Home/Details/Intro/Index.vue'),
54 | name: 'complex-home-details-intro',
55 | path: 'intro',
56 | },
57 | ],
58 | },
59 | ],
60 | },
61 | {
62 | component: () => import('@/src/Complex/Login/Index.vue'),
63 | name: 'complex-login',
64 | path: '/complex/login',
65 | },
66 | {
67 | component: () => import('@/src/Dynamic/Index.vue'),
68 | name: 'dynamic',
69 | path: '/dynamic',
70 | },
71 | {
72 | component: () => import('@/src/Dynamic/_UserForm/Index.vue'),
73 | name: 'dynamic-userForm',
74 | meta: {
75 | name: 'user',
76 | },
77 | path: '/dynamic/:userForm',
78 | },
79 | {
80 | component: () => import('@/src/Nest/Index.vue'),
81 | name: 'nest',
82 | meta: {
83 | name: 'nest',
84 | bool: true,
85 | },
86 | path: '/nest',
87 | },
88 | {
89 | component: () => import('@/src/Nest/Home/Home.vue'),
90 | name: 'nest-home',
91 | path: '/nest/home',
92 | children: [{
93 | component: () => import('@/src/Nest/Home/Account/Index.vue'),
94 | name: 'nest-home-account',
95 | meta: {
96 | name: 'account',
97 | },
98 | path: 'account',
99 | },
100 | {
101 | component: () => import('@/src/Nest/Home/Account/_Id/Index.vue'),
102 | name: 'nest-home-account-id',
103 | path: 'account/:id',
104 | },
105 | {
106 | component: () => import('@/src/Nest/Home/Infor/Index.vue'),
107 | name: 'nest-home-infor',
108 | path: 'infor',
109 | },
110 | {
111 | component: () => import('@/src/Nest/Home/Test/Index.vue'),
112 | name: 'nest-home-test',
113 | path: 'test',
114 | },
115 | ],
116 | },
117 | {
118 | component: () => import('@/src/Nest/test/index.vue'),
119 | name: 'nest-test',
120 | path: '/nest/test',
121 | },
122 | {
123 | component: () => import('@/src/Single/Index.vue'),
124 | name: 'single',
125 | path: '/single',
126 | },
127 | {
128 | component: () => import('@/src/Single/User-Name/Index.vue'),
129 | name: 'single-userName',
130 | meta: {
131 | name: 'user',
132 | },
133 | redirect: {
134 | path: '/another',
135 | },
136 | path: '/single/userName',
137 | },
138 | {
139 | name: 'notFound',
140 | path: '*',
141 | component: () => import('@/src/NotFound.vue')
142 | },
143 | ];
144 | const router = new Router({
145 | mode: 'history',
146 | routes,
147 | scrollBehavior: (to, from, savedPosition) => {
148 | if (savedPosition) {
149 | return savedPosition;
150 | } else {
151 | return {
152 | x: 0,
153 | y: 0
154 | };
155 | }
156 | }
157 | });
158 | router.beforeEach(async (to, from, next) => {
159 | if (!Vue._cachedForbiddenRoute) {
160 | Vue._cachedForbiddenRoute = [];
161 | await apis.getForbiddenRoute().then(res => {
162 | Vue._cachedForbiddenRoute = res;
163 | });
164 | }
165 | if (Vue._cachedForbiddenRoute.includes(to.path)) {
166 | next({
167 | name: 'notFound'
168 | });
169 | } else {
170 | next();
171 | }
172 | });
173 |
174 | router.beforeResolve((to, from, next) => {
175 | next();
176 | });
177 |
178 | router.afterEach((to, from) => {});
179 | export default router;
--------------------------------------------------------------------------------
/demos/App.vue:
--------------------------------------------------------------------------------
1 |
2 |