├── demos
├── src
│ ├── Template.vue
│ ├── Nest
│ │ ├── Home
│ │ │ ├── meta.yml
│ │ │ ├── Infor
│ │ │ │ └── Index.vue
│ │ │ ├── Test
│ │ │ │ └── Index.vue
│ │ │ ├── Account
│ │ │ │ ├── meta.yml
│ │ │ │ ├── Index.vue
│ │ │ │ └── _Id
│ │ │ │ │ └── Index.vue
│ │ │ └── Home.vue
│ │ ├── index.scss
│ │ ├── test
│ │ │ └── index.vue
│ │ ├── meta.yml
│ │ └── Index.vue
│ ├── Complex
│ │ ├── Login
│ │ │ ├── Images
│ │ │ │ └── test.png
│ │ │ └── Index.vue
│ │ ├── Home
│ │ │ ├── Details
│ │ │ │ ├── Intro
│ │ │ │ │ └── Index.vue
│ │ │ │ ├── meta.yml
│ │ │ │ ├── Infor
│ │ │ │ │ └── Index.vue
│ │ │ │ └── Details.vue
│ │ │ ├── Account
│ │ │ │ ├── _Dynamic
│ │ │ │ │ └── Index.vue
│ │ │ │ ├── Chunk
│ │ │ │ │ └── Index.vue
│ │ │ │ ├── Inner
│ │ │ │ │ └── Index.vue
│ │ │ │ └── Account.vue
│ │ │ └── Home.vue
│ │ └── Index.vue
│ ├── Dynamic
│ │ ├── _UserForm
│ │ │ ├── meta.yml
│ │ │ └── Index.vue
│ │ └── Index.vue
│ ├── Index.vue
│ ├── NotFound.vue
│ └── Single
│ │ ├── Index.vue
│ │ └── User-Name
│ │ ├── Index.vue
│ │ └── meta.yml
├── apis
│ └── index.js
├── App.vue
├── index.js
├── index.html
├── webpack.config.js
└── .invoke
│ └── router.js
├── .stylelintignore
├── tests
├── ignore
│ ├── Login
│ │ └── Index.vue
│ ├── Components
│ │ └── Index.vue
│ └── Images
│ │ └── Index.vue
├── single
│ ├── User
│ │ └── Index.vue
│ └── Login
│ │ └── Index.vue
├── utils
│ └── index.js
├── dynamic.spec.js
├── nest.spec.js
├── single.spec.js
└── option.spec.js
├── .eslintignore
├── .yarnrc
├── prettier.config.js
├── commitlint.config.js
├── docs
├── images
│ ├── name.png
│ ├── notice.png
│ ├── index_cn.png
│ └── index_en.png
└── zh_CN
│ └── README.md
├── .gitignore
├── .npmrc
├── stylelint.config.js
├── .editorconfig
├── types
├── index.d.ts
└── options.d.ts
├── .babelrc
├── jest.config.js
├── .circleci
└── config.yml
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── core
├── index.js
├── invoke.js
├── utils.js
├── files.js
└── ast.js
├── scripts
├── publish.sh
└── publish.js
├── LICENSE
├── .eslintrc
├── CHANGELOG.md
├── package.json
└── README.md
/demos/src/Template.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | demos/dist
--------------------------------------------------------------------------------
/demos/src/Nest/Home/meta.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demos/src/Nest/index.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/ignore/Login/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/single/User/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demos/src/Nest/test/index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/ignore/Components/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/ignore/Images/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/single/Login/Index.vue:
--------------------------------------------------------------------------------
1 | 456
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Infor/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Test/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demos/src/Complex/Login/Images/test.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Details/Intro/Index.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | demos/dist
2 | tests/single
3 | coverage
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | registry "https://registry.npm.taobao.org/"
--------------------------------------------------------------------------------
/demos/src/Dynamic/_UserForm/meta.yml:
--------------------------------------------------------------------------------
1 | meta:
2 | - name: user
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Details/meta.yml:
--------------------------------------------------------------------------------
1 | meta:
2 | - name: details
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Account/meta.yml:
--------------------------------------------------------------------------------
1 | meta:
2 | - name: account
3 |
--------------------------------------------------------------------------------
/demos/src/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | home
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Nest/meta.yml:
--------------------------------------------------------------------------------
1 | meta:
2 | - name: nest
3 | - bool: true
4 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true
3 | };
4 |
--------------------------------------------------------------------------------
/demos/src/Nest/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | nest
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Complex/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | complex
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Dynamic/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | dynamic
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 | 404NotFound
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Single/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | single
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Complex/Login/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | complex-login
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Single/User-Name/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | single-user
3 |
4 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional']
3 | };
4 |
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Account/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | nest-home-account
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Single/User-Name/meta.yml:
--------------------------------------------------------------------------------
1 | meta:
2 | - name: user
3 | redirect:
4 | - path: '/another'
5 |
--------------------------------------------------------------------------------
/demos/src/Dynamic/_UserForm/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | dynamic-{{ $route.params }}
3 |
4 |
--------------------------------------------------------------------------------
/docs/images/name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qymh/vue-router-invoke-webpack-plugin/HEAD/docs/images/name.png
--------------------------------------------------------------------------------
/docs/images/notice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qymh/vue-router-invoke-webpack-plugin/HEAD/docs/images/notice.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 |
4 | demos/dist
5 | .DS_Store
6 | coverage
7 |
8 | tests/.invoke
9 | .vscode
--------------------------------------------------------------------------------
/demos/apis/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | async getForbiddenRoute() {
3 | return ['/single/user'];
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/docs/images/index_cn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qymh/vue-router-invoke-webpack-plugin/HEAD/docs/images/index_cn.png
--------------------------------------------------------------------------------
/docs/images/index_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qymh/vue-router-invoke-webpack-plugin/HEAD/docs/images/index_en.png
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Account/_Id/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | nest-home-account-{{ $route.params }}
3 |
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .gitignore
4 | .DS_Store
5 | .invoke
6 | .vscode
7 | registry=https://registry.npmjs.org/
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Account/_Dynamic/Index.vue:
--------------------------------------------------------------------------------
1 |
2 | complex-home-account-{{ $route.params }}
3 |
4 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Details/Infor/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home-details-infor
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Account/Chunk/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home-account-chunk
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Account/Inner/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home-account-inner
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demos/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/demos/src/Nest/Home/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | nest-home
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Account/Account.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home-account
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/demos/src/Complex/Home/Details/Details.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | complex-home-details
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['stylelint-config-standard'],
3 | rules: {
4 | 'rule-empty-line-before': 'never-multi-line'
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/demos/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | import router from './.invoke/router.js';
4 |
5 | export default new Vue({
6 | el: '#app',
7 | router,
8 | render: h => h(App)
9 | });
10 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { VueRouterInvokeWebpackPluginOptions } from './options';
2 |
3 | declare class VueRouterInvokeWebpackPlugin {
4 | constructor(options: VueRouterInvokeWebpackPluginOptions);
5 | }
6 |
7 | export = VueRouterInvokeWebpackPlugin;
8 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: ['js'],
3 | transform: {
4 | '^.+\\.jsx?$': 'babel-jest'
5 | },
6 | transformIgnorePatterns: ['/node_modules/'],
7 | testMatch: ['**/tests/**/*.spec.js'],
8 | collectCoverageFrom: ['core/**/*.js'],
9 | coverageDirectory: 'coverage',
10 | testEnvironment: 'node'
11 | };
12 |
--------------------------------------------------------------------------------
/demos/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/tests/utils/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fse = require('fs-extra');
3 | const fs = require('fs');
4 | const path = require('path');
5 | const tests = path.resolve(process.cwd(), 'tests');
6 |
7 | exports.makeFile = str => {
8 | str = str.split('/');
9 | if (!str.length) {
10 | return;
11 | }
12 | const file = str.pop();
13 | const dir = str.join('/');
14 | fse.ensureDirSync(path.resolve(tests, dir));
15 | fs.writeFileSync(path.resolve(tests, dir, file), '');
16 | };
17 |
18 | exports.writeFile = (file, str) => {
19 | fs.writeFileSync(path.resolve(tests, file), str);
20 | };
21 |
22 | exports.removeFile = str => {
23 | fse.removeSync(path.resolve(tests, str));
24 | };
25 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scripts/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # 带时间的日志输出
5 | log() {
6 | echo -e "\033[31m [$(date '+%Y-%m-%d %H:%M:%S')] $1 \033[0m"
7 | }
8 |
9 | version=$1
10 |
11 | log "cur version: $version"
12 |
13 | log "code review"
14 | yarn lint
15 |
16 | git add .
17 |
18 | if [[ `git status -s | grep -o -E ".*"` ]]
19 | then
20 | git commit -m "chore: 🤖 $version code"
21 | fi
22 |
23 | log "write version"
24 | npm version $version --message "$version"
25 |
26 | git tag -d "v${version}"
27 |
28 | log "changelog"
29 | yarn changelog
30 | git add .
31 | git commit -m "chore: 🤖 $version changelog"
32 |
33 | git tag "v${version}"
34 |
35 | log "publishing"
36 | npm publish
37 | git push
38 | git push origin v$version
--------------------------------------------------------------------------------
/types/options.d.ts:
--------------------------------------------------------------------------------
1 | import { Route, NavigationGuard } from 'vue-router';
2 | import Vue from 'vue';
3 |
4 | type mode = 'hash' | 'history';
5 | type language = 'javascript' | 'typescript';
6 | type Position = { x: number; y: number };
7 | type PositionResult = Position | { selector: string; offset?: Position } | void;
8 |
9 | export interface VueRouterInvokeWebpackPluginOptions {
10 | dir: string;
11 | alias: string;
12 | notFound?: string;
13 | mode?: mode;
14 | meta?: string;
15 | routerDir?: string;
16 | language?: language;
17 | ignore?: string[];
18 | redirect?: string[];
19 | modules?: string[];
20 | scrollBehavior?: (
21 | to: Route,
22 | from: Route,
23 | savedPosition: Position | void
24 | ) => PositionResult | Promise;
25 | beforeEach?: (guard: NavigationGuard) => void;
26 | beforeResolve?: (guard: NavigationGuard) => void;
27 | afterEach?: (to: Route, from: Route) => void;
28 | }
29 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/scripts/publish.js:
--------------------------------------------------------------------------------
1 | const execa = require('execa');
2 | const semver = require('semver');
3 | const inquirer = require('inquirer');
4 |
5 | const curVersion = require('../package').version;
6 | const bumps = ['patch', 'minor', 'major', 'prerelease', 'premajor'];
7 |
8 | const redTips = msg =>
9 | // eslint-disable-next-line
10 | console.log('\x1b[1;31m' + msg + '\x1b[0m');
11 |
12 | const versions = bumps.reduce((acc, val) => {
13 | acc[val] = semver.inc(curVersion, val);
14 | return acc;
15 | }, {});
16 |
17 | const bumpChoices = bumps.reduce((acc, val) => {
18 | acc.push({
19 | name: `${val} (${versions[val]})`,
20 | value: `${val}`
21 | });
22 | return acc;
23 | }, []);
24 |
25 | async function release() {
26 | const { bump, customVersion, npmTag } = await inquirer.prompt([
27 | {
28 | name: 'bump',
29 | message: 'Please choose the release type:',
30 | type: 'list',
31 | choices: [...bumpChoices, { name: 'custom', value: 'custom' }]
32 | },
33 | {
34 | name: 'customVersion',
35 | message: 'Please input the version:',
36 | type: 'input',
37 | when: answers => answers.bump === 'custom'
38 | }
39 | ]);
40 |
41 | const curVersion = customVersion || versions[bump];
42 |
43 | const { confirm } = await inquirer.prompt({
44 | name: 'confirm',
45 | message: `Please confirm the version ${curVersion}`,
46 | type: 'list',
47 | choices: ['Y', 'N']
48 | });
49 |
50 | if (confirm === 'N') {
51 | redTips('exit publish');
52 | return;
53 | }
54 |
55 | const promise = execa('bash', ['scripts/publish.sh', curVersion, npmTag]);
56 | promise.stdout.pipe(process.stdout);
57 | promise.stderr.pipe(process.stderr);
58 | // eslint-disable-next-line
59 | (async () => {
60 | try {
61 | const { stdout, stderr } = await promise;
62 | stdout && redTips(stdout);
63 | stderr && redTips(stderr);
64 | } catch (error) {
65 | redTips(error.stderr);
66 | }
67 | })();
68 | }
69 |
70 | release();
71 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/dynamic.spec.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const VueRouterInvokeWebpackPlugin = require('../core');
5 | const { makeFile, removeFile } = require('./utils');
6 | function testPlugin(options, expectVal, notExpectVal) {
7 | webpack({
8 | resolve: {
9 | alias: {
10 | '@': path.resolve(process.cwd(), 'tests')
11 | }
12 | },
13 | plugins: [
14 | new VueRouterInvokeWebpackPlugin(
15 | Object.assign({ routerDir: 'tests/dynamicT' }, options)
16 | )
17 | ]
18 | });
19 | if (expectVal || notExpectVal) {
20 | const isTs = options.language === 'typescript';
21 | let file = fs.readFileSync(
22 | `tests/dynamicT/.invoke/router.${isTs ? 'ts' : 'js'}`,
23 | 'utf-8'
24 | );
25 | file = file.replace(/\s/g, '');
26 | if (expectVal) {
27 | expect(new RegExp(expectVal, 'i').test(file)).toBeTruthy();
28 | } else {
29 | expect(new RegExp(notExpectVal, 'i').test(file)).toBeFalsy();
30 | }
31 | }
32 | }
33 |
34 | describe('dynamicRoute', () => {
35 | it('hump name', () => {
36 | makeFile('dynamicT/_Dynamic/Index.vue');
37 | testPlugin(
38 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
39 | `name\\:\\'dynamic\\',path\\:\\'\\/\\:dynamic\\'`
40 | );
41 | removeFile('dynamicT');
42 | });
43 |
44 | it('yakitori name', () => {
45 | makeFile('dynamicT/_Dynamic-Name/Index.vue');
46 | testPlugin(
47 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
48 | `name\\:\\'dynamicName\\',path\\:\\'\\/\\:dynamicName\\'`
49 | );
50 | removeFile('dynamicT');
51 | });
52 |
53 | it('underlinename', () => {
54 | makeFile('dynamicT/_Dynamic_Name/Index.vue');
55 | testPlugin(
56 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
57 | `name\\:\\'dynamicName\\',path\\:\\'\\/\\:dynamicName\\'`
58 | );
59 | removeFile('dynamicT');
60 | });
61 |
62 | it('lowercase name', () => {
63 | makeFile('dynamicT/_dynamic_name/index.vue');
64 | testPlugin(
65 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
66 | `name\\:\\'dynamicName\\',path\\:\\'\\/\\:dynamicName\\'`
67 | );
68 | removeFile('dynamicT');
69 | });
70 |
71 | it('uppercase name', () => {
72 | makeFile('dynamicT/_DYNAMIC_NAME/INDEX.vue');
73 | testPlugin(
74 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
75 | `name\\:\\'dynamicName\\',path\\:\\'\\/\\:dynamicName\\'`
76 | );
77 | removeFile('dynamicT');
78 | });
79 |
80 | it('multiple', () => {
81 | makeFile('dynamicT/_dynamic_name/index.vue');
82 | makeFile('dynamicT/_dynamic_name/_dynamic_inner/index.vue');
83 | testPlugin(
84 | { dir: 'tests/dynamicT', alias: '@/dynamicT' },
85 | `(name\\:\\'dynamicName\\',path\\:\\'\\/\\:dynamicName\\'|name\\:\\'dynamicName-dynamicInner\\',path\\:\\'\\/\\:dynamicName\\/\\:dynamicInner\\')`
86 | );
87 | removeFile('dynamicT');
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/tests/nest.spec.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const VueRouterInvokeWebpackPlugin = require('../core');
5 | const { makeFile, removeFile } = require('./utils');
6 | function testPlugin(options, expectVal, notExpectVal) {
7 | webpack({
8 | resolve: {
9 | alias: {
10 | '@': path.resolve(process.cwd(), 'tests')
11 | }
12 | },
13 | plugins: [
14 | new VueRouterInvokeWebpackPlugin(
15 | Object.assign({ routerDir: 'tests/nestT' }, options)
16 | )
17 | ]
18 | });
19 | if (expectVal || notExpectVal) {
20 | const isTs = options.language === 'typescript';
21 | let file = fs.readFileSync(
22 | `tests/nestT/.invoke/router.${isTs ? 'ts' : 'js'}`,
23 | 'utf-8'
24 | );
25 | file = file.replace(/\s/g, '');
26 | if (expectVal) {
27 | expect(new RegExp(expectVal, 'i').test(file)).toBeTruthy();
28 | } else {
29 | expect(new RegExp(notExpectVal, 'i').test(file)).toBeFalsy();
30 | }
31 | }
32 | }
33 |
34 | describe('nestRoute', () => {
35 | it('hump name', () => {
36 | makeFile('nestT/Parent/Parent.vue');
37 | testPlugin(
38 | { dir: 'tests/nestT', alias: '@/nestT' },
39 | `name\\:\\'parent\\',path\\:\\'\\/parent\\',children\\:\\[\\]`
40 | );
41 | removeFile('nestT');
42 | });
43 |
44 | it('yakitori name', () => {
45 | makeFile('nestT/_Parent/_Parent.vue');
46 | testPlugin(
47 | { dir: 'tests/nestT', alias: '@/nestT' },
48 | `name\\:\\'parent\\',path\\:\\'\\/\\:parent\\',children\\:\\[\\]`
49 | );
50 | removeFile('nestT');
51 | });
52 |
53 | it('underlinename', () => {
54 | makeFile('nestT/_Parent_Name/_Parent_Name.vue');
55 | testPlugin(
56 | { dir: 'tests/nestT', alias: '@/nestT' },
57 | `name\\:\\'parentName\\',path\\:\\'\\/\\:parentName\\',children\\:\\[\\]`
58 | );
59 | removeFile('nestT');
60 | });
61 |
62 | it('lowercase name', () => {
63 | makeFile('nestT/_parent_name/_parent_name.vue');
64 | testPlugin(
65 | { dir: 'tests/nestT', alias: '@/nestT' },
66 | `name\\:\\'parentName\\',path\\:\\'\\/\\:parentName\\',children\\:\\[\\]`
67 | );
68 | removeFile('nestT');
69 | });
70 |
71 | it('uppercase name', () => {
72 | makeFile('nestT/_PARENT_NAME/_PARENT_NAME.vue');
73 | testPlugin(
74 | { dir: 'tests/nestT', alias: '@/nestT' },
75 | `name\\:\\'parentName\\',path\\:\\'\\/\\:parentName\\',children\\:\\[\\]`
76 | );
77 | removeFile('nestT');
78 | });
79 |
80 | it('multiple', () => {
81 | makeFile('nestT/_parent_name/_parent_name.vue');
82 | makeFile('nestT/_parent_name/Child/Index.vue');
83 | testPlugin(
84 | { dir: 'tests/nestT', alias: '@/nestT' },
85 | `(name\\:\\'parentName\\',path\\:\\'\\/\\:parentName\\',children\\:\\[\\]|name\\:\\'parentName-child\\',path\\:\\'child\\')`
86 | );
87 | removeFile('nestT');
88 | });
89 |
90 | it('mixed single and nested route', () => {
91 | makeFile('nestT/test/index.vue');
92 | makeFile('nestT/test/test.vue');
93 | expect(() => {
94 | testPlugin({ dir: 'tests/nestT', alias: '@/nestT' });
95 | }).toThrow();
96 | removeFile('nestT');
97 | removeFile('nestT');
98 | });
99 | });
100 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-router-invoke-webpack-plugin",
3 | "version": "0.4.4",
4 | "description": "automatic generate your vue-router path and stronger normalize your file directory",
5 | "main": "core/index.js",
6 | "author": {
7 | "name": "Qymh",
8 | "email": "bowei.zhang@ankerbox.com"
9 | },
10 | "keywords": [
11 | "vue",
12 | "vue-router",
13 | "webpack"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/Qymh/vue-router-invoke-webpack-plugin.git"
18 | },
19 | "engines": {
20 | "node": ">=8.15.1"
21 | },
22 | "typings": "types/index.d.ts",
23 | "license": "MIT",
24 | "scripts": {
25 | "check": "yarn upgrade-interactive --latest",
26 | "lint:corejs": "eslint \"core/**/*.js\"",
27 | "lint:demosjs": "eslint \"demos/**/*.{js,vue} \"",
28 | "lint:js": "npm run lint:corejs && npm run lint:demosjs",
29 | "lint:style": "stylelint \"demos/**/*.{js,vue}\"",
30 | "lint": "npm run lint:js && npm run lint:style",
31 | "dev:demos": "cross-env NODE_ENV=development webpack-dev-server --config demos/webpack.config.js",
32 | "build:demos": "cross-env NODE_ENV=production webpack --config demos/webpack.config.js",
33 | "test": "jest --coverage --watchAll",
34 | "test:single": "jest --coverage",
35 | "test:ci": "jest && codecov",
36 | "pub": "node scripts/publish",
37 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
38 | "commit": "git cz"
39 | },
40 | "dependencies": {
41 | "js-beautify": "^1.10.2",
42 | "js-yaml": "^3.13.1",
43 | "yamljs": "^0.3.0"
44 | },
45 | "devDependencies": {
46 | "@commitlint/cli": "^8.2.0",
47 | "@commitlint/config-conventional": "^8.2.0",
48 | "@types/jest": "^24.0.23",
49 | "@vue/babel-preset-app": "^4.1.1",
50 | "babel-jest": "^24.9.0",
51 | "babel-loader": "^8.0.6",
52 | "chokidar": "^3.3.0",
53 | "codecov": "^3.6.1",
54 | "conventional-changelog-cli": "^2.0.31",
55 | "core-js": "^3.6.2",
56 | "cross-env": "^6.0.3",
57 | "eslint": "^6.7.2",
58 | "eslint-config-prettier": "^6.7.0",
59 | "eslint-config-standard": "^14.1.0",
60 | "eslint-plugin-import": "^2.18.2",
61 | "eslint-plugin-node": "^10.0.0",
62 | "eslint-plugin-prettier": "^3.1.1",
63 | "eslint-plugin-promise": "^4.2.1",
64 | "eslint-plugin-standard": "^4.0.1",
65 | "eslint-plugin-vue": "^6.0.1",
66 | "execa": "^3.4.0",
67 | "fs-extra": "^8.1.0",
68 | "git-cz": "^3.3.0",
69 | "html-webpack-plugin": "^3.2.0",
70 | "husky": "^3.1.0",
71 | "inquirer": "^7.0.1",
72 | "jest": "^24.9.0",
73 | "lint-staged": "^9.5.0",
74 | "progress-bar-webpack-plugin": "^1.12.1",
75 | "rimraf": "^3.0.0",
76 | "semver": "^7.0.0",
77 | "stylelint": "^12.0.0",
78 | "stylelint-config-standard": "^19.0.0",
79 | "vue": "^2.6.10",
80 | "vue-loader": "^15.7.2",
81 | "vue-router": "^3.1.3",
82 | "vue-template-compiler": "^2.6.10",
83 | "webpack": "^4.41.2",
84 | "webpack-cli": "^3.3.10",
85 | "webpack-dev-server": "^3.9.0"
86 | },
87 | "husky": {
88 | "hooks": {
89 | "pre-commit": "lint-staged"
90 | }
91 | },
92 | "lint-staged": {
93 | "*.css": [
94 | "stylelint --fix",
95 | "git add"
96 | ],
97 | "*.js": [
98 | "eslint --fix",
99 | "git add"
100 | ],
101 | "*.vue": [
102 | "stylelint --fix",
103 | "eslint --fix",
104 | "git add"
105 | ]
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/demos/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const VueLoaderPlugin = require('vue-loader/lib/plugin');
4 | const Progress = require('progress-bar-webpack-plugin');
5 | const HtmlWebpackPlugin = require('html-webpack-plugin');
6 | const VueRouterInvokePlugin = require('../core/index');
7 | const isDev = process.env.NODE_ENV === 'development';
8 |
9 | const base = {
10 | mode: process.env.NODE_ENV,
11 | entry: {
12 | app: path.resolve(__dirname, './index.js')
13 | },
14 | output: {
15 | filename: '[name].js',
16 | path: path.resolve(__dirname, './dist'),
17 | publicPath: '/'
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js$/,
23 | loader: 'babel-loader',
24 | exclude: /node_modules/
25 | },
26 | {
27 | test: /\.vue$/,
28 | loader: 'vue-loader'
29 | }
30 | ]
31 | },
32 | resolve: {
33 | extensions: ['.js', '.vue'],
34 | alias: {
35 | '@': path.resolve(process.cwd(), 'demos')
36 | }
37 | },
38 | plugins: [
39 | new VueRouterInvokePlugin({
40 | dir: 'demos/src',
41 | alias: '@/src',
42 | language: 'javascript',
43 | routerDir: 'demos',
44 | ignore: [
45 | 'images',
46 | 'template.vue',
47 | 'components',
48 | 'notfound.vue',
49 | /\.scss$/
50 | ],
51 | notFound: '@/src/NotFound.vue',
52 | modules: [
53 | {
54 | name: 'apis',
55 | package: '@/apis'
56 | }
57 | ],
58 | scrollBehavior: (to, from, savedPosition) => {
59 | if (savedPosition) {
60 | return savedPosition;
61 | } else {
62 | return { x: 0, y: 0 };
63 | }
64 | },
65 | /* eslint-disable */
66 | beforeEach: async (to, from, next) => {
67 | if (!Vue._cachedForbiddenRoute) {
68 | Vue._cachedForbiddenRoute = [];
69 | await apis.getForbiddenRoute().then(res => {
70 | Vue._cachedForbiddenRoute = res;
71 | });
72 | }
73 | if (Vue._cachedForbiddenRoute.includes(to.path)) {
74 | next({
75 | name: 'notFound'
76 | });
77 | } else {
78 | next();
79 | }
80 | },
81 | /* eslint-enable */
82 | beforeResolve: (to, from, next) => {
83 | next();
84 | },
85 | afterEach: (to, from) => {}
86 | }),
87 | new VueLoaderPlugin(),
88 | new HtmlWebpackPlugin({
89 | filename: 'index.html',
90 | template: path.resolve(__dirname, './index.html')
91 | }),
92 | new webpack.NoEmitOnErrorsPlugin()
93 | ],
94 | stats: {
95 | colors: true,
96 | modules: false,
97 | children: false,
98 | chunks: false,
99 | chunkModules: false
100 | }
101 | };
102 |
103 | if (isDev) {
104 | base.devServer = {
105 | port: '8089',
106 | publicPath: '/',
107 | inline: true,
108 | quiet: true,
109 | open: false,
110 | clientLogLevel: 'warning',
111 | historyApiFallback: true
112 | };
113 |
114 | base.plugins.push(
115 | new Progress(),
116 | new webpack.DefinePlugin({
117 | process: {
118 | env: {
119 | NODE_ENV: '"development"'
120 | }
121 | }
122 | })
123 | );
124 | }
125 |
126 | if (!isDev) {
127 | base.plugins.push(
128 | new webpack.DefinePlugin({
129 | process: {
130 | env: {
131 | NODE_ENV: '"production"'
132 | }
133 | }
134 | })
135 | );
136 | }
137 |
138 | module.exports = base;
139 |
--------------------------------------------------------------------------------
/tests/single.spec.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const VueRouterInvokeWebpackPlugin = require('../core');
5 | const { makeFile, removeFile } = require('./utils');
6 | function testPlugin(options, expectVal, notExpectVal) {
7 | webpack({
8 | resolve: {
9 | alias: {
10 | '@': path.resolve(process.cwd(), 'tests')
11 | }
12 | },
13 | plugins: [
14 | new VueRouterInvokeWebpackPlugin(
15 | Object.assign({ routerDir: 'tests/singleT' }, options)
16 | )
17 | ]
18 | });
19 | if (expectVal || notExpectVal) {
20 | const isTs = options.language === 'typescript';
21 | let file = fs.readFileSync(
22 | `tests/singleT/.invoke/router.${isTs ? 'ts' : 'js'}`,
23 | 'utf-8'
24 | );
25 | file = file.replace(/\s/g, '');
26 | if (expectVal) {
27 | expect(new RegExp(expectVal, 'i').test(file)).toBeTruthy();
28 | } else {
29 | expect(new RegExp(notExpectVal, 'i').test(file)).toBeFalsy();
30 | }
31 | }
32 | }
33 |
34 | describe('singleRoute', () => {
35 | it('root wrong name', () => {
36 | makeFile('singleT/Login.vue');
37 | expect(() => {
38 | testPlugin({ dir: 'tests/singleT', alias: '@/singleT' });
39 | }).toThrow();
40 | removeFile('singleT/Login.vue');
41 | });
42 |
43 | it('child wrong name', () => {
44 | makeFile('singleT/Child/Login.vue');
45 | expect(() => {
46 | testPlugin({ dir: 'tests/singleT', alias: '@/singleT' });
47 | }).toThrow();
48 | removeFile('singleT/Child/Login.vue');
49 | });
50 |
51 | it('hump name', () => {
52 | makeFile('singleT/Login/Index.vue');
53 | testPlugin(
54 | { dir: 'tests/singleT', alias: '@/singleT' },
55 | `name\\:\\'login\\',path\\:\\'\\/login\\'`
56 | );
57 | removeFile('singleT');
58 | });
59 |
60 | it('homepage', () => {
61 | makeFile('singleT/Login/Index.vue');
62 | makeFile('singleT/Index.vue');
63 | testPlugin(
64 | { dir: 'tests/singleT', alias: '@/singleT' },
65 | `name\\:\\'index\\',path\\:\\'\\/\\'`
66 | );
67 | removeFile('singleT');
68 | });
69 |
70 | it('yakitori name', () => {
71 | makeFile('singleT/Login-Name/Index.vue');
72 | testPlugin(
73 | { dir: 'tests/singleT', alias: '@/singleT' },
74 | `name\\:\\'loginName\\',path\\:\\'\\/loginName\\'`
75 | );
76 | removeFile('singleT');
77 | });
78 |
79 | it('underlinename', () => {
80 | makeFile('singleT/Login_Name/Index.vue');
81 | testPlugin(
82 | { dir: 'tests/singleT', alias: '@/singleT' },
83 | `name\\:\\'loginName\\',path\\:\\'\\/loginName\\'`
84 | );
85 | removeFile('singleT');
86 | });
87 |
88 | it('lowercase name', () => {
89 | makeFile('singleT/login_name/index.vue');
90 | testPlugin(
91 | { dir: 'tests/singleT', alias: '@/singleT' },
92 | `name\\:\\'loginName\\',path\\:\\'\\/loginName\\'`
93 | );
94 | removeFile('singleT');
95 | });
96 |
97 | it('uppercase name', () => {
98 | makeFile('singleT/LOGIN_NAME/INDEX.vue');
99 | testPlugin(
100 | { dir: 'tests/singleT', alias: '@/singleT' },
101 | `name\\:\\'loginName\\',path\\:\\'\\/loginName\\'`
102 | );
103 | removeFile('singleT');
104 | });
105 |
106 | it('multiple', () => {
107 | makeFile('singleT/login_name/index.vue');
108 | makeFile('singleT/login_name/login_inner/index.vue');
109 | testPlugin(
110 | { dir: 'tests/singleT', alias: '@/singleT' },
111 | `(name\\:\\'loginName\\',path\\:\\'\\/loginName\\'|name\\:\\'loginName-loginInner\\',path\\:\\'\\/loginName\\/loginInner\\')`
112 | );
113 | removeFile('singleT');
114 | });
115 | });
116 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/tests/option.spec.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const rimraf = require('rimraf');
5 | const VueRouterInvokeWebpackPlugin = require('../core');
6 | const { makeFile, removeFile, writeFile } = require('./utils');
7 | function testPlugin(options, expectVal, notExpectVal) {
8 | webpack({
9 | resolve: {
10 | alias: {
11 | '@': path.resolve(process.cwd(), 'tests')
12 | }
13 | },
14 | plugins: [
15 | new VueRouterInvokeWebpackPlugin(
16 | Object.assign({ routerDir: 'tests' }, options)
17 | )
18 | ]
19 | });
20 | if (expectVal || notExpectVal) {
21 | const isTs = options.language === 'typescript';
22 | let file = fs.readFileSync(
23 | `tests/.invoke/router.${isTs ? 'ts' : 'js'}`,
24 | 'utf-8'
25 | );
26 | file = file.replace(/\s/g, '');
27 | if (expectVal) {
28 | expect(new RegExp(expectVal, 'i').test(file)).toBeTruthy();
29 | } else {
30 | expect(new RegExp(notExpectVal, 'i').test(file)).toBeFalsy();
31 | }
32 | }
33 | }
34 |
35 | describe('option', () => {
36 | it('create', () => {
37 | testPlugin({
38 | dir: 'tests/single',
39 | alias: '@/single'
40 | });
41 | });
42 |
43 | it('must have dir', () => {
44 | expect(() => {
45 | testPlugin();
46 | }).toThrow();
47 | });
48 |
49 | it('wrong empty dir', () => {
50 | expect(() => {
51 | testPlugin({ dir: 'tests/empty', alias: '@/empty' });
52 | }).toThrow();
53 | });
54 |
55 | it('must have alias', () => {
56 | expect(() => {
57 | testPlugin({ dir: 'tests/single' });
58 | }).toThrow();
59 | });
60 |
61 | it('mode should be hash or history', () => {
62 | expect(() => {
63 | testPlugin({
64 | dir: 'tests/single',
65 | alias: '@/single',
66 | mode: 'test'
67 | });
68 | }).toThrow();
69 | });
70 |
71 | it('default routerDir', () => {
72 | rimraf.sync(path.resolve(process.cwd(), '.invoke'));
73 | webpack({
74 | resolve: {
75 | alias: {
76 | '@': path.resolve(process.cwd(), 'demos')
77 | }
78 | },
79 | plugins: [
80 | new VueRouterInvokeWebpackPlugin({
81 | dir: 'demos/src',
82 | alias: '@/src',
83 | ignore: 'notfound'
84 | })
85 | ]
86 | });
87 | rimraf.sync(path.resolve(process.cwd(), '.invoke'));
88 | });
89 |
90 | it('language should be javascript or typescript', () => {
91 | expect(() => {
92 | testPlugin({
93 | dir: 'tests/single',
94 | alias: '@/single',
95 | language: 'test'
96 | });
97 | }).toThrow();
98 | });
99 |
100 | it('javascript', () => {
101 | expect(() => {
102 | testPlugin({
103 | dir: 'tests/single',
104 | alias: '@/single',
105 | language: 'javascript'
106 | });
107 | }).not.toThrow();
108 | });
109 |
110 | it('typescript', () => {
111 | testPlugin(
112 | {
113 | dir: 'tests/single',
114 | alias: '@/single',
115 | language: 'typescript'
116 | },
117 | '{RouteConfig}'
118 | );
119 | });
120 |
121 | it('meta', () => {
122 | removeFile('metaTest');
123 | makeFile('metaTest/login/Index.vue');
124 | makeFile('metaTest/login/meta.yml');
125 | writeFile(
126 | 'metaTest/login/meta.yml',
127 | `
128 | meta:
129 | - name: metaTest
130 | `
131 | );
132 | testPlugin(
133 | { dir: 'tests/metaTest', alias: '@/metaTest' },
134 | `meta\\:\\{name\\:\\'metaTest\\'`
135 | );
136 | removeFile('metaTest');
137 | });
138 |
139 | it('boolean smeta', () => {
140 | removeFile('metaTest');
141 | makeFile('metaTest/login/Index.vue');
142 | makeFile('metaTest/login/meta.yml');
143 | writeFile(
144 | 'metaTest/login/meta.yml',
145 | `
146 | meta:
147 | - name: true
148 | `
149 | );
150 | testPlugin(
151 | { dir: 'tests/metaTest', alias: '@/metaTest' },
152 | `meta\\:\\{name\\:true`
153 | );
154 | removeFile('metaTest');
155 | });
156 |
157 | it('redirect', () => {
158 | removeFile('metaTest');
159 | makeFile('metaTest/login/Index.vue');
160 | makeFile('metaTest/login/meta.yml');
161 | writeFile(
162 | 'metaTest/login/meta.yml',
163 | `
164 | redirect:
165 | - path: /test
166 | `
167 | );
168 | testPlugin(
169 | { dir: 'tests/metaTest', alias: '@/metaTest' },
170 | `redirect\\:\\{path\\:\\'/test\\'`
171 | );
172 | removeFile('metaTest');
173 | });
174 |
175 | it('empty meta', () => {
176 | removeFile('metaTest');
177 | makeFile('metaTest/home/home.vue');
178 | makeFile('metaTest/home/login/index.vue');
179 | makeFile('metaTest/home/login/meta.yml');
180 | testPlugin(
181 | { dir: 'tests/metaTest', alias: '@/metaTest' },
182 | '',
183 | `meta\\:\\{name\\:\\'metaTest\\'`
184 | );
185 | removeFile('metaTest');
186 | });
187 |
188 | it('multiple meta', () => {
189 | removeFile('metaTest');
190 | makeFile('metaTest/home/home.vue');
191 | makeFile('metaTest/home/inner/inner.vue');
192 | makeFile('metaTest/home/inner/test/index.vue');
193 | makeFile('metaTest/home/inner/testt/index.vue');
194 | makeFile('metaTest/home/inner/meta.yml');
195 | testPlugin(
196 | { dir: 'tests/metaTest', alias: '@/metaTest' },
197 | '',
198 | `meta\\:\\{name\\:\\'metaTest\\'`
199 | );
200 | removeFile('metaTest');
201 | });
202 |
203 | it('ignore', () => {
204 | testPlugin(
205 | {
206 | dir: 'tests/ignore',
207 | alias: '@/ignore',
208 | ignore: ['images', 'components']
209 | },
210 | '',
211 | 'images|components'
212 | );
213 | });
214 |
215 | it('redirect', () => {
216 | testPlugin(
217 | {
218 | dir: 'tests/single',
219 | alias: '@/single',
220 | redirect: [
221 | {
222 | path: '/',
223 | redirect: '/home'
224 | }
225 | ]
226 | },
227 | `redirect\\:\\'\\/home\\'`
228 | );
229 | });
230 |
231 | it('notFound', () => {
232 | testPlugin(
233 | {
234 | dir: 'tests/single',
235 | alias: '@/single',
236 | redirect: [
237 | {
238 | path: '/',
239 | redirect: '/home'
240 | }
241 | ],
242 | ignore: ['NotFound.vue'],
243 | notFound: '@/single/NotFound.vue'
244 | },
245 | `name\\:\\'notFound\\',path\\:\\'\\*\\'`
246 | );
247 | });
248 |
249 | it('modules', () => {
250 | testPlugin(
251 | {
252 | dir: 'tests/single',
253 | alias: '@/single',
254 | modules: [
255 | {
256 | name: 'some',
257 | package: 'Some'
258 | }
259 | ]
260 | },
261 | `importsomefrom\\'Some\\'`
262 | );
263 | });
264 |
265 | it('scrollBehavior', () => {
266 | testPlugin(
267 | {
268 | dir: 'tests/single',
269 | alias: '@/single',
270 | scrollBehavior: (to, from, savedPosition) => {
271 | if (savedPosition) {
272 | return savedPosition;
273 | } else {
274 | return { x: 0, y: 0 };
275 | }
276 | }
277 | },
278 | 'scrollBehavior:\\(to,from,savedPosition\\)'
279 | );
280 | });
281 |
282 | it('beforeEach', () => {
283 | testPlugin(
284 | {
285 | dir: 'tests/single',
286 | alias: '@/single',
287 | beforeEach: (to, from, next) => {
288 | next();
289 | }
290 | },
291 | 'beforeEach\\(\\(to,from,next\\)'
292 | );
293 | });
294 |
295 | it('beforeResolve', () => {
296 | testPlugin(
297 | {
298 | dir: 'tests/single',
299 | alias: '@/single',
300 | beforeResolve: (to, from, next) => {
301 | next();
302 | }
303 | },
304 | 'beforeResolve\\(\\(to,from,next\\)'
305 | );
306 | });
307 |
308 | it('afterEach', () => {
309 | testPlugin(
310 | {
311 | dir: 'tests/single',
312 | alias: '@/single',
313 | afterEach: (to, from) => {}
314 | },
315 | 'afterEach\\(\\(to,from\\)'
316 | );
317 | });
318 |
319 | it('watchFiles', done => {
320 | process.env.NODE_ENV = 'development';
321 | testPlugin({
322 | dir: 'tests/single',
323 | alias: '@/single'
324 | });
325 | process.env.NODE_ENV = 'test';
326 | removeFile('single/watch');
327 | setTimeout(() => {
328 | makeFile('single/watch/Index.vue');
329 | setTimeout(() => {
330 | let file = fs.readFileSync('tests/.invoke/router.js', 'utf-8');
331 | file = file.replace(/\s/g, '');
332 | expect(
333 | new RegExp(`name\\:\\'watch\\',path\\:\\'\\/watch\\'`).test(file)
334 | );
335 | removeFile('single/watch');
336 | done();
337 | }, 1000);
338 | }, 100);
339 | });
340 | });
341 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/docs/zh_CN/README.md:
--------------------------------------------------------------------------------
1 | # vue-router-invoke-webpack-plugin
2 |
3 | 
4 | 
5 | 
6 | 
7 |
8 | > `typescript`重写且同时支持 vue2.x 和 vue3.x 的新版本 [vue-router-invoke-next-webpack-plugin](https://github.com/Qymh/vue-router-invoke-next-webpack-plugin)
9 |
10 | 根据文件格式自动生成`vue-router`的路由
11 |
12 | ## 下载
13 |
14 | ### npm
15 |
16 | ```javascript
17 | npm install vue-router-invoke-webpack-plugin -D
18 | ```
19 |
20 | ### cnpm
21 |
22 | ```javascript
23 | cnpm install vue-router-invoke-webpack-plugin -D
24 | ```
25 |
26 | ### yarn
27 |
28 | ```javascript
29 | yarn add vue-router-invoke-webpack-plugin -D
30 | ```
31 |
32 | ## 什么是路由自动注入
33 |
34 | 路由自动注入是指根据文件目录的格式自动生成对应的`router.js`, 而不需要每次创建模块都去手动引用
35 |
36 | ## 用法
37 |
38 | ### Webpack
39 |
40 | - 我们需要确定当前环境处于开发(`development`)还是生产(`production`)环境,所以你需要设置`process.env.NODE_ENV`在开发环境下为`development`,在生产环境下为`production`.不考虑跨平台的话环境变量是可以直接设置的,但也有很多插件实现了跨平台设置,我们推荐[cross-env](https://github.com/kentcdodds/cross-env)
41 | - 考虑到多人开发,所以引用路由的时候不能通过你的本机绝对路由引入,所以你需要给观察的目录`dir`设置一个别名[alias](https://webpack.js.org/configuration/resolve/#resolvealias)
42 | - 自动构建的路由是懒加载的,所以你需要引用一个 babel 插件[@babel/plugin-syntax-dynamic-import](https://babeljs.io/docs/en/next/babel-plugin-syntax-dynamic-import.html)
43 |
44 | ```javascript
45 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
46 | const path = require('path')
47 |
48 | // 省略掉其他配置...
49 |
50 | resolve: {
51 | alias: {
52 | '@': path.resolve(process.cwd(), 'demos')
53 | }
54 | }
55 |
56 | plugins: [
57 | new VueRouterInvokeWebpackPlugin(
58 | dir: 'demos/src',
59 | alias: '@/src'
60 | )
61 | ];
62 | ```
63 |
64 | ### VueCli3
65 |
66 | vuecli3 会比 webpack 配置容易点
67 |
68 | `vue.config.js`
69 |
70 | ```javascript
71 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
72 |
73 | module.exports = {
74 | // 省略掉其他配置...
75 | configureWebpack(config) {
76 | config.plugins.push(
77 | new VueRouterInvokeWebpackPlugin({
78 | dir: 'src/views',
79 | // 必须设置dir配置的别名
80 | alias: '@/views'
81 | })
82 | );
83 | }
84 | };
85 |
86 | // 或者采用另外一个方法
87 |
88 | module.exports = {
89 | // 省略掉其他配置...
90 | configureWebpack: {
91 | plugins: [
92 | new VueRouterInvokeWebpackPlugin({
93 | dir: 'src/views',
94 | // 必须设置dir配置的别名
95 | alias: '@/views'
96 | })
97 | ]
98 | }
99 | };
100 | ```
101 |
102 | ### Start
103 |
104 | 在配置好后,你可以通过`npm run serve`或者你定义的其他命令在开发模式下激活插件,当第一次运行或`dir`观察的目录发生变化时,`router.js`会被自动构建
105 |
106 | 同样的在生产环境下,你可以通过`npm run build`或者你定义的其他命令激活插件,`router.js`会被自动构建和打包
107 |
108 | ### 配置
109 |
110 | | Prop | Type | Required | Default | Description |
111 | | -------------- | :------: | :------: | :----------: | -------------------------: |
112 | | dir | String | true | '' | 观察的 vue 文件目录 |
113 | | alias | String | true | '' | 观察的 vue 文件目录的别名 |
114 | | notFound | String | false | '' | 404 路由 |
115 | | mode | String | false | history | hash 或者 history |
116 | | meta | String | false | meta | 定义 meta 的 yml 文件名 |
117 | | routerDir | String | false | ROOT | 构建后的 router.js 的位置 |
118 | | language | String | false | javascript | javascript 或者 typescript |
119 | | ignore | Array | false | ['.dsstore'] | 忽略的文件或者文件夹 |
120 | | redirect | Array | false | [] | 重定向路由 |
121 | | modules | Array | false | [] | 导入的其他模块 |
122 | | scrollBehavior | Function | false | '' | 同 scrollBehavior |
123 | | beforeEach | Function | false | '' | router.beforeEach |
124 | | beforeResolve | Function | false | '' | router.beforeResolve |
125 | | afterEach | Function | false | '' | router.afterEach |
126 |
127 | ## 如何使用自动注入
128 |
129 | 下面将会使用 vuecli3 作为列子
130 |
131 | `vue.config.js`
132 |
133 | ```javascript
134 | const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin');
135 |
136 | module.exports = {
137 | // 省略其他配置
138 | configureWebpack(config) {
139 | config.plugins.push(
140 | new VueRouterInvokeWebpackPlugin({
141 | dir: 'src/views',
142 | alias: '@/views'
143 | })
144 | );
145 | }
146 | };
147 | ```
148 |
149 | 然后在入口文件`src/main.js`中引用构建好的`router.js`
150 |
151 | 默认生成的`router.js`的位置在项目根路由的`.invoke`文件夹中,你可以通过`routerDir`这个配置更改默认位置
152 |
153 | 要注意的是`routerDir`是相对于根路由的地址,而不是绝对地址哦
154 |
155 | 我们建议把生成的`router.js`放在`.gitignore`和`.eslintignore`中,它没有必要被版本控制或者 eslint 校验,因为它是自动生成的
156 |
157 | ```javascript
158 | import Vue from 'vue';
159 | import App from './App.vue';
160 | import router from '../.invoke/router';
161 |
162 | export default new Vue({
163 | el: '#app',
164 | router,
165 | render: h => h(App)
166 | });
167 | ```
168 |
169 | ### 单路由
170 |
171 | 请注意,文件格式是有一层文件夹放在了外面,vue 命名为`Index.vue`而不是直接命名 vue 文件,这一点可能和平常的认知不太一样
172 |
173 | 同样的,也不要去命名文件夹名字为`Index`这会与嵌套路由的判断产生歧义
174 |
175 | > 0.2.7 的版本我们引入了暴力提醒 第一次打包的时候如果命名规则和期望值不同,插件会直接报错,在开发环境下插件不会中断程序运行,但会高亮报错信息,比如这样
176 |
177 | 
178 |
179 | 如果你发现此类的提醒,可以检查下文件格式是否符合规则
180 |
181 | 如果你的文件是这样的
182 |
183 | ```
184 | src
185 | ├── views
186 | │ ├── Login
187 | │ │ └── Index.vue
188 | │ └── User
189 | │ ├── Account
190 | │ │ └── Index.vue
191 | │ ├── Home
192 | │ │ └── Index.vue
193 | │ └── Index.vue
194 | ```
195 |
196 | 那么自动生成的路由会是这样
197 |
198 | ```javascript
199 | {
200 | component: () =>
201 | import('@/views/Login/Index.vue'),
202 | name: 'login',
203 | path: '/login'
204 | },
205 | {
206 | component: () =>
207 | import('@/views/User/Index.vue'),
208 | name: 'user',
209 | path: '/user'
210 | },
211 | {
212 | component: () =>
213 | import('@/views/User/Account/Index.vue'),
214 | name: 'user-account',
215 | path: '/user/account'
216 | },
217 | {
218 | component: () =>
219 | import('@/views/User/Home/Index.vue'),
220 | name: 'user-home',
221 | path: '/user/home'
222 | }
223 | ```
224 |
225 | ### 首页
226 |
227 | 我们对首页做了特殊处理,首页直接用`Index.vue`表示
228 |
229 | 如果你的文件是这样的
230 |
231 | ```
232 | src
233 | ├── views
234 | │ ├── Login
235 | │ │ └── Index.vue
236 | │ └── Index.vue
237 | ```
238 |
239 | 那么自动生成的路由会是这样
240 |
241 | ```javascript
242 | {
243 | component: () =>
244 | import('@/views/Index.vue'),
245 | name: 'index',
246 | path: '/'
247 | },
248 | {
249 | component: () =>
250 | import('@/views/Login/Index.vue'),
251 | name: 'login',
252 | path: '/login'
253 | }
254 | ```
255 |
256 | ### 动态路由
257 |
258 | 如果你的文件是这样的
259 |
260 | ```
261 | src
262 | ├── views
263 | │ ├── Login
264 | │ │ └── Index.vue
265 | │ └── User
266 | │ ├── _Home
267 | │ │ └── Index.vue
268 | │ └── Index.vue
269 | ```
270 |
271 | 那么自动生成的路由会是这样
272 |
273 | ```javascript
274 | {
275 | component: () =>
276 | import('@/views/Login/Index.vue'),
277 | name: 'login',
278 | path: '/login'
279 | },
280 | {
281 | component: () =>
282 | import('@/views/User/Index.vue'),
283 | name: 'user',
284 | path: '/user'
285 | },
286 | {
287 | component: () =>
288 | import('@/views/User/_Home/Index.vue'),
289 | name: 'user-home',
290 | path: '/user/:home'
291 | }
292 | ```
293 |
294 | ### 嵌套路由
295 |
296 | 如果你的文件是这样的
297 |
298 | ```
299 | src
300 | ├── views
301 | │ ├── Login
302 | │ │ └── Index.vue
303 | │ └── User
304 | │ ├── Chart
305 | │ │ └── Index.vue
306 | │ ├── Home
307 | │ │ └── Index.vue
308 | │ └── User.vue
309 | ```
310 |
311 | 那么自动生成的路由会是这样
312 |
313 | ```javascript
314 | {
315 | component: () =>
316 | import('@/views/Login/Index.vue'),
317 | name: 'login',
318 | path: '/login'
319 | },
320 | {
321 | component: () =>
322 | import('@/views/User/User.vue'),
323 | name: 'user',
324 | path: '/user',
325 | children: [
326 | {
327 | component: () =>
328 | import('@/views/User/Chart/Index.vue'),
329 | name: 'user-chart',
330 | path: 'chart'
331 | },
332 | {
333 | component: () =>
334 | import('@/views/User/Home/Index.vue'),
335 | name: 'user-home',
336 | path: 'home'
337 | }
338 | ]
339 | }
340 | ```
341 |
342 | ### 动态嵌套路由
343 |
344 | 如果你的文件长这样
345 |
346 | ```
347 | src
348 | ├── views
349 | │ ├── Login
350 | │ │ └── Index.vue
351 | │ └── User
352 | │ ├── _Category
353 | │ │ ├── _Category.vue
354 | │ │ └── Infor
355 | │ │ └── Index.vue
356 | │ └── Index.vue
357 | ```
358 |
359 | 那么自动生成的路由会是这样
360 |
361 | ```javascript
362 | {
363 | component: () =>
364 | import('@/views/Login/Index.vue'),
365 | name: 'login',
366 | path: '/login'
367 | },
368 | {
369 | component: () =>
370 | import('@/views/User/Index.vue'),
371 | name: 'user',
372 | path: '/user'
373 | },
374 | {
375 | component: () =>
376 | import('@/views/User/_Category/_Category.vue'),
377 | name: 'user-category',
378 | path: '/user/:category',
379 | children: [
380 | {
381 | component: () =>
382 | import('@/views/User/_Category/Infor/Index.vue'),
383 | name: 'user-category-infor',
384 | path: 'infor'
385 | }
386 | ]
387 | }
388 | ```
389 |
390 | ## 命名矫正
391 |
392 | 在为什么使用`vue-router-invoke-webpack-plugin`中提到过命名不统一的问题,我们会将所有不同的命名转化为驼峰命名,这只是一个小的命名矫正,根本的方法应该是统一命名规则
393 |
394 | 举个列子
395 |
396 | ```
397 | src
398 | ├── views
399 | │ ├── LoginPage
400 | │ │ └── index.vue
401 | │ └── User-home
402 | │ ├── account
403 | │ │ └── Index.vue
404 | │ ├── Home-details
405 | │ │ └── Index.vue
406 | │ └── Index.vue
407 | ```
408 |
409 | 矫正命名后的路由会是这样
410 |
411 | ```javascript
412 | {
413 | component: () => import('@/views/LoginPage/index.vue'),
414 | name: 'loginPage',
415 | path: '/loginPage'
416 | },
417 | {
418 | component: () => import('@/views/User-home/Index.vue'),
419 | name: 'userHome',
420 | path: '/userHome'
421 | },
422 | {
423 | component: () => import('@/views/User-home/Home-details/Index.vue'),
424 | name: 'userHome-homeDetails',
425 | path: '/userHome/homeDetails'
426 | },
427 | {
428 | component: () => import('@/views/User-home/account/Index.vue'),
429 | name: 'userHome-account',
430 | path: '/userHome/account'
431 | },
432 | ```
433 |
434 | ## meta 替代品
435 |
436 | `meta`属性可以解决很多问题,比如定义当前页面标题或者给一个字段判断当前页面是否需要登录.
437 |
438 | 定义标题之类的可以通过[vue-meta](https://github.com/nuxt/vue-meta)解决
439 |
440 | 但如果你需要定义`meta`属性的话,需要写一个`yml`文件
441 |
442 | 举个列子
443 |
444 | ```javascript
445 | src/views
446 | ├── Single
447 | │ ├── Index.vue
448 | │ └── User
449 | │ ├── Index.vue
450 | │ └── meta.yml
451 | ```
452 |
453 | `meta.yml`
454 |
455 | ```yml
456 | meta:
457 | - name: user
458 | ```
459 |
460 | 自动构建后的路由会是这样
461 |
462 | ```javascript
463 | {
464 | component: () => import('@/views/Single/Index.vue'),
465 | name: 'single',
466 | path: 'single'
467 | },
468 | {
469 | component: () => import('@/views/Single/User/Index.vue'),
470 | name: 'single-user',
471 | meta: { name: user },
472 | path: 'single/user'
473 | }
474 | ```
475 |
476 | > 在版本 0.4.1 后,meta 的类型支持了布尔、字符串、数组、原生对象,但是类型并不支持`JSON.stringify`无法转移的值比如`Symbol`、函数、`undefined`、循环引用的对象
477 |
478 | ## 特殊的配置
479 |
480 | ### 404 路由
481 |
482 | 举个列子
483 |
484 | ```javascript
485 | plugins: [
486 | new VueRouterInvokeWebpackPlugin({
487 | dir: 'src/views',
488 | alias: '@/views',
489 | // muse set ignore for notFound chunk
490 | ignore: ['NotFound.vue'],
491 | notFound: '@/views/NotFound.vue'
492 | })
493 | ];
494 | ```
495 |
496 | ```
497 | src
498 | ├── views
499 | │ ├── Login
500 | │ │ └── Index.vue
501 | │ └── Index.vue
502 | │ └── NotFound.vue
503 |
504 | ```
505 |
506 | 自动构建后的路由会是这样
507 |
508 | ```javascript
509 | {
510 | component: () =>
511 | import('@/views/Index.vue'),
512 | name: 'index',
513 | path: '/'
514 | },
515 | {
516 | component: () =>
517 | import('@/views/NotFound.vue'),
518 | name: 'notFound',
519 | path: '*'
520 | },
521 | {
522 | component: () =>
523 | import('@/views/Login/Index.vue'),
524 | name: 'login',
525 | path: '/login'
526 | }
527 | ```
528 |
529 | ### 忽略文件和文件夹
530 |
531 | ```javascript
532 | plugins: [
533 | new VueRouterInvokeWebpackPlugin({
534 | dir: 'src/views',
535 | alias: '@/views',
536 | language: 'javascript',
537 | ignore: ['images', 'components', 'template.vue']
538 | })
539 | ];
540 | ```
541 |
542 | 自动构建的路由将会不区分大小写的忽略掉`images` `components` `template.vue`
543 |
544 | 在版本`0.4.3` 之上 你也可以在ignore中定义正则来匹配文件
545 |
546 | ```javascript
547 | plugins: [
548 | new VueRouterInvokeWebpackPlugin({
549 | dir: 'src/views',
550 | alias: '@/views',
551 | language: 'javascript',
552 | ignore: ['images', 'components', 'template.vue', /\.scss$/]
553 | })
554 | ];
555 | ```
556 |
557 | the directory
558 |
559 | ```
560 | src
561 | ├── views
562 | │ ├── Login
563 | │ │ └── Index.vue
564 | │ │ └── Index.scss
565 | │ ├── Template.vue
566 | │ └── User
567 | │ ├── Components
568 | │ ├── Images
569 | │ └── Index.vue
570 | ```
571 |
572 | ```javascript
573 | {
574 | component: () =>
575 | import('@/views/Login/Index.vue'),
576 | name: 'login',
577 | path: '/login'
578 | },
579 | {
580 | component: () =>
581 | import('@/views/User/Index.vue'),
582 | name: 'user',
583 | path: '/user'
584 | }
585 | ```
586 |
587 | ### 重定向
588 |
589 | 假设你的配置如下
590 |
591 | ```javascript
592 | plugins: [
593 | new VueRouterInvokeWebpackPlugin({
594 | dir: 'src/views',
595 | alias: '@/views',
596 | language: 'javascript',
597 | redirect: [
598 | {
599 | redirect: '/',
600 | path: '/home'
601 | },
602 | {
603 | redirect: '/test',
604 | path: '/demo'
605 | }
606 | ]
607 | })
608 | ];
609 | ```
610 |
611 | 自动构建的路由会长这样
612 |
613 | ```javascript
614 | {
615 | path: '/home',
616 | redirect: '/'
617 | },
618 | {
619 | path: '/demo',
620 | redirect: '/test'
621 | }
622 | ```
623 |
624 | #### 重定向 yml 配置
625 |
626 | > '0.4.0'版本之后 你可以在 yml 中配置重定向
627 |
628 | 举个列子
629 |
630 | ```javascript
631 | src/views
632 | ├── Single
633 | │ ├── Index.vue
634 | │ └── User
635 | │ ├── Index.vue
636 | │ └── meta.yml
637 | ```
638 |
639 | `meta.yml`
640 |
641 | ```yml
642 | redirect:
643 | - path: /test
644 | ```
645 |
646 | 自动生成的路由会变成这样
647 |
648 | ```javascript
649 | {
650 | component: () => import('@/views/Single/Index.vue'),
651 | name: 'single',
652 | path: 'single'
653 | },
654 | {
655 | component: () => import('@/views/Single/User/Index.vue'),
656 | name: 'single-user',
657 | path: 'single/user',
658 | redirect: {
659 | path: '/test'
660 | },
661 | }
662 | ```
663 |
664 | ### 模块
665 |
666 | 自动生成的`router.js`有两个模块 `vue` `vue-router`
667 |
668 | ```javascript
669 | import Vue from 'vue';
670 | import Router from 'vue-router';
671 | ```
672 |
673 | 如果你需要其他模块用在`beforeEach`之类的方法中,你需要手动加上,举个列子
674 |
675 | ```javascript
676 | new VueRouterInvokeWebpackPlugin({
677 | dir: 'src/views',
678 | alias: '@/views',
679 | modules: [
680 | {
681 | name: 'diyName',
682 | package: 'some-packages'
683 | }
684 | ]
685 | });
686 | ```
687 |
688 | 自动生成的路由会长这样
689 |
690 | ```javascript
691 | // 省略其它配置
692 | import diyName from 'some-packages';
693 | ```
694 |
695 | ### vue 路由全局守卫
696 |
697 | 如果你的配置如下
698 |
699 | ```javascript
700 | new VueRouterInvokeWebpackPlugin({
701 | dir: 'src/views',
702 | alias: '@/views',
703 | language: 'javascript',
704 | beforeEach: (to, from, next) => {
705 | next();
706 | },
707 | beforeResolve: (to, from, next) => {
708 | next();
709 | },
710 | afterEach: (to, from) => {}
711 | });
712 | ```
713 |
714 | 自动生成的路由会是这样
715 |
716 | ```javascript
717 | // 省略其它配置
718 | const router = new Router({ mode: 'history', routes });
719 | router.beforeEach((to, from, next) => {
720 | next();
721 | });
722 |
723 | router.beforeResolve((to, from, next) => {
724 | next();
725 | });
726 |
727 | router.afterEach((to, from) => {});
728 | export default router;
729 | ```
730 |
731 | ### ScrollBehavior
732 |
733 | 如果你的配置如下
734 |
735 | ```javascript
736 | new VueRouterInvokeWebpackPlugin({
737 | dir: 'src/views',
738 | alias: '@/views',
739 | language: 'javascript',
740 | scrollBehavior: (to, from, savedPosition) => {
741 | if (savedPosition) {
742 | return savedPosition;
743 | } else {
744 | return { x: 0, y: 0 };
745 | }
746 | }
747 | });
748 | ```
749 |
750 | 自动生成的路由会是这样
751 |
752 | ```javascript
753 | // 省略其他配置
754 | const router = new Router({
755 | mode: 'history',
756 | routes,
757 | scrollBehavior: (to, from, savedPosition) => {
758 | if (savedPosition) {
759 | return savedPosition;
760 | } else {
761 | return { x: 0, y: 0 };
762 | }
763 | }
764 | });
765 | ```
766 |
767 | ## Demos
768 |
769 | 详细的演示你可以`git clone`克隆我们的项目然后执行`npm run build:demos`,`demo`没有什么内容它的着重点在于`router.js`的构建,你可以看到不同文件目录对应的不同格式
770 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------