Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vuejs/vue-cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.0.0-beta.11
Choose a base ref
...
head repository: vuejs/vue-cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.0.0-beta.12
Choose a head ref

Commits on Mar 3, 2018

  1. feat(ui): Initial app

    Guillaume Chau committed Mar 3, 2018
    Copy the full SHA
    8947a45 View commit details

Commits on Mar 4, 2018

  1. Copy the full SHA
    1aee235 View commit details
  2. feat(ui): Initial schema and folder API

    Guillaume Chau committed Mar 4, 2018
    Copy the full SHA
    1751ca1 View commit details
  3. feat(ui): FolderExplorer

    Guillaume Chau committed Mar 4, 2018
    Copy the full SHA
    3333c94 View commit details

Commits on Mar 5, 2018

  1. Copy the full SHA
    08514eb View commit details
  2. feat(ui): FolderExplorer list scrolling

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    ae0d895 View commit details
  3. feat(ui): FolderExplorer favorites + Project select page

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    376e4bb View commit details
  4. fix(ui): Project select page class

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    0a527d7 View commit details
  5. fix(ui): FolderExplorer favorites dropdown placement

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    1a71164 View commit details
  6. feat(ui): Project Create details form

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    8399838 View commit details
  7. chore: merge dev

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    ac50b82 View commit details
  8. feat(ui): Preset tab

    Guillaume Chau committed Mar 5, 2018
    Copy the full SHA
    45e3c82 View commit details

Commits on Mar 6, 2018

  1. refactor(ui): StepWizard next/previous now on scoped slot

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    7addd68 View commit details
  2. refactor(ui): ProjectSelect now uses StepWizard

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    48a23aa View commit details
  3. feat(ui): ProjectCreate saves formData

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    d59b35e View commit details
  4. feat(ui): Project select hide tabs when creating project

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    db67f1e View commit details
  5. feat(ui): ProjectCreate path preview

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    d0703b0 View commit details
  6. feat(ui): ProjectCreate features tab

    Guillaume Chau committed Mar 6, 2018
    Copy the full SHA
    ee59f54 View commit details

Commits on Mar 7, 2018

  1. feat(ui): ProjectCreate prompts tab

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    239c4d4 View commit details
  2. feat(ui): ProjectCreate save preset

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    bea5df9 View commit details
  3. fix(ui): pormpts remove result in answers when disabled

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    a29a3b4 View commit details
  4. fix(ui): prompts deep objects

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    fd3188d View commit details
  5. fix(ui): ListItemInfo vertical align

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    c7e4ca1 View commit details
  6. chore: merge dev

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    971404e View commit details
  7. chore(ui): bump version

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    49d2b4d View commit details
  8. feat(ui): Project creation working!

    Guillaume Chau committed Mar 7, 2018
    Copy the full SHA
    61655b1 View commit details

Commits on Mar 9, 2018

  1. feat(ui): Progress and Logs systems

    Guillaume Chau committed Mar 9, 2018
    Copy the full SHA
    9f0eece View commit details
  2. fix(ui): LoggerView scrollToBottom

    Guillaume Chau committed Mar 9, 2018
    Copy the full SHA
    6c2e99a View commit details
  3. feat(ui): StatusBar/LoggerView improvements

    Guillaume Chau committed Mar 9, 2018
    Copy the full SHA
    e1dc6e7 View commit details
  4. feat(ui): StatusBar 'No logs yet'

    Guillaume Chau committed Mar 9, 2018
    Copy the full SHA
    e20e21d View commit details

Commits on Mar 10, 2018

  1. feat(ui): New StepWizard frame style

    Guillaume Chau committed Mar 10, 2018
    Copy the full SHA
    921e99f View commit details
  2. feat(ui): Reset CWD to project path

    Guillaume Chau committed Mar 10, 2018
    Copy the full SHA
    601fb1f View commit details

Commits on Mar 11, 2018

  1. feat(ui): wip plugins list

    Guillaume Chau committed Mar 11, 2018
    Copy the full SHA
    b9a714c View commit details
  2. feat(ui): plugin logo

    Guillaume Chau committed Mar 11, 2018
    Copy the full SHA
    088d316 View commit details

Commits on Mar 12, 2018

  1. feat(ui): Plugin add search (wip)

    Guillaume Chau committed Mar 12, 2018
    Copy the full SHA
    83939c9 View commit details
  2. feat(ui): ItemLogo special vuejs styling

    Guillaume Chau committed Mar 12, 2018
    Copy the full SHA
    da0d37e View commit details
  3. feat(ui): install/uninstall plugin

    Guillaume Chau committed Mar 12, 2018
    Copy the full SHA
    63ccde8 View commit details

Commits on Mar 14, 2018

  1. feat(ui): plugin add prompts

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    ce4cf9a View commit details
  2. fix(ui): Prompt validation

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    009b880 View commit details
  3. feat(ui): prompt error ui

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    798445f View commit details
  4. feat(ui): plugin invoke

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    1a48c9f View commit details
  5. fix: move request deps to shared-utils

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    982c494 View commit details
  6. Copy the full SHA
    ba12ca5 View commit details
  7. feat(ui): PluginAdd config cta-text

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    faac5e5 View commit details
  8. chore(ui): update to latest @vue/ui

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    0248ee2 View commit details
  9. fix(ui): PluginAdd current plugin display

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    33b1e20 View commit details
  10. fix(ui): PluginAdd tab check

    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    ca01d95 View commit details
  11. fix(ui): progress handler should not throw error (casuing process to …

    …exit)
    Guillaume Chau committed Mar 14, 2018
    Copy the full SHA
    3d4d8f0 View commit details

Commits on Mar 15, 2018

  1. refactor(ui): plugins getMetadata significantly faster

    Guillaume Chau committed Mar 15, 2018
    Copy the full SHA
    0a38556 View commit details
  2. fix(ui): StatusBar and scrolling fixes

    Guillaume Chau committed Mar 15, 2018
    Copy the full SHA
    7440d0f View commit details
Showing 386 changed files with 19,503 additions and 986 deletions.
14 changes: 14 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -56,6 +56,17 @@ jobs:
- run: yarn test -p unit-mocha,unit-jest,e2e-nightwatch,e2e-cypress
- run: yarn test tsPluginE2e

cli-ui:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: cd packages/@vue/cli-ui && yarn test
- store_artifacts:
path: packages/@vue/cli-ui/tests/e2e/videos
- store_artifacts:
path: packages/@vue/cli-ui/tests/e2e/screenshots

workflows:
version: 2
test:
@@ -73,3 +84,6 @@ workflows:
- group-4:
requires:
- install
- cli-ui:
requires:
- install
5 changes: 5 additions & 0 deletions .postcssrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": {
"autoprefixer": {}
}
}
41 changes: 40 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,42 @@
<a name="3.0.0-beta.11"></a>
# [3.0.0-beta.11](https://github.com/vuejs/vue-cli/compare/v3.0.0-beta.10...v3.0.0-beta.11) (2018-05-21)


### Bug Fixes

* css sourceMap in production ([#1270](https://github.com/vuejs/vue-cli/issues/1270)) ([2d09a4c](https://github.com/vuejs/vue-cli/commit/2d09a4c))
* **css:** css-loader importLoaders should account for vue-loader's injected ([853662c](https://github.com/vuejs/vue-cli/commit/853662c)), closes [#1267](https://github.com/vuejs/vue-cli/issues/1267)
* fix babel.config.js compat in vue-jest ([48d7e00](https://github.com/vuejs/vue-cli/commit/48d7e00))
* fix transpileDependencies by always using babel.config.js ([1279b3e](https://github.com/vuejs/vue-cli/commit/1279b3e))
* **eslint:** ensure all config values are contained in config file ([83f5f4f](https://github.com/vuejs/vue-cli/commit/83f5f4f)), closes [#1006](https://github.com/vuejs/vue-cli/issues/1006) [#1313](https://github.com/vuejs/vue-cli/issues/1313)
* fix ts/tsx rule separation ([41a56f1](https://github.com/vuejs/vue-cli/commit/41a56f1)), closes [#1315](https://github.com/vuejs/vue-cli/issues/1315)
* handle failed git commit ([a1ccde8](https://github.com/vuejs/vue-cli/commit/a1ccde8)), closes [#1306](https://github.com/vuejs/vue-cli/issues/1306)
* rename test-utils `shallow` to `shallowMount` ([#1269](https://github.com/vuejs/vue-cli/issues/1269)) ([5c54df7](https://github.com/vuejs/vue-cli/commit/5c54df7))
* stringifyJS should be used in all call sites ([07ac887](https://github.com/vuejs/vue-cli/commit/07ac887))
* typo in vue-loader compilerOptions ([#1263](https://github.com/vuejs/vue-cli/issues/1263)) ([b2b277a](https://github.com/vuejs/vue-cli/commit/b2b277a))


### Features

* **build:** add 'watch' option ([#1332](https://github.com/vuejs/vue-cli/issues/1332)) ([6ea17c9](https://github.com/vuejs/vue-cli/commit/6ea17c9))
* **cli-service:** add assetsDir option to specify assets root directory ([#1322](https://github.com/vuejs/vue-cli/issues/1322)) ([9638d90](https://github.com/vuejs/vue-cli/commit/9638d90)), closes [#1311](https://github.com/vuejs/vue-cli/issues/1311)
* **eslint:** add --max-warnings and --max-errors for cli-plugin-eslint ([#1289](https://github.com/vuejs/vue-cli/issues/1289)) ([ab877a2](https://github.com/vuejs/vue-cli/commit/ab877a2)), closes [#1268](https://github.com/vuejs/vue-cli/issues/1268)
* **eslint:** enable caching ([ff0f97b](https://github.com/vuejs/vue-cli/commit/ff0f97b))
* **eslint:** pass cli arguments to linter ([#1258](https://github.com/vuejs/vue-cli/issues/1258)) ([9ac2642](https://github.com/vuejs/vue-cli/commit/9ac2642)), closes [#1255](https://github.com/vuejs/vue-cli/issues/1255)
* **inspect:** add --rule and --plugin options for inspect command ([82349ba](https://github.com/vuejs/vue-cli/commit/82349ba))
* **inspect:** add --rules and --plugins options for inspect command ([fd1c0d5](https://github.com/vuejs/vue-cli/commit/fd1c0d5))
* support `<style lang="postcss">` ([#1259](https://github.com/vuejs/vue-cli/issues/1259)) ([1037b9c](https://github.com/vuejs/vue-cli/commit/1037b9c))
* **inspect:** improve `vue inspect` output with webpack-chain hints ([f6bfb63](https://github.com/vuejs/vue-cli/commit/f6bfb63)), closes [#881](https://github.com/vuejs/vue-cli/issues/881)
* allow disabling serve progress via devServer.progress ([da38747](https://github.com/vuejs/vue-cli/commit/da38747)), closes [#1284](https://github.com/vuejs/vue-cli/issues/1284)
* allow router/vuex to be late added via `vue add` ([2a195f0](https://github.com/vuejs/vue-cli/commit/2a195f0)), closes [#1202](https://github.com/vuejs/vue-cli/issues/1202) [#1204](https://github.com/vuejs/vue-cli/issues/1204)
* ask for whether to use taobao registry when getting versions ([#1273](https://github.com/vuejs/vue-cli/issues/1273)) ([8fbbd35](https://github.com/vuejs/vue-cli/commit/8fbbd35))
* GeneratorAPI: addImports & addRootOptions ([8b32f4a](https://github.com/vuejs/vue-cli/commit/8b32f4a))
* make it possible to opt-out of Babel ([d75ea99](https://github.com/vuejs/vue-cli/commit/d75ea99)), closes [#1199](https://github.com/vuejs/vue-cli/issues/1199)
* support webp ([763cf7a](https://github.com/vuejs/vue-cli/commit/763cf7a)), closes [#1321](https://github.com/vuejs/vue-cli/issues/1321)
* temporarily fix source map by patching babel ([453597a](https://github.com/vuejs/vue-cli/commit/453597a))



<a name="3.0.0-beta.10"></a>
# [3.0.0-beta.10](https://github.com/vuejs/vue-cli/compare/v3.0.0-beta.9...v3.0.0-beta.10) (2018-05-11)

@@ -54,7 +93,7 @@ modifications must be webpcak 4 compatible. Drop support
for webpack plugins that do not work with v4 or above.
* dll option has been removed.
* the "vueLoader" option has been removed. To modify vue-loader
options, use chainWebpack then `config.module.rule('vue').use('vue-loader').tap()`.
options, use chainWebpack then `config.module.rule(vue).use(vue-loader).tap()`.
vue-loader has been upgraded to v15 and expects different options from v14.
* To include a dependency for Babel transpilation, tapping
babel-loader and adding .include() will no longer work. Use the new
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ test_script:
- node --version
- yarn --version
- yarn test
- cd "packages/@vue/cli-ui" && yarn test

cache:
- node_modules -> yarn.lock
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -162,3 +162,4 @@ See [@vue/cli-plugin-typescript](https://github.com/vuejs/vue-cli/tree/dev/packa

- [Contributing Guide](https://github.com/vuejs/vue-cli/blob/dev/.github/CONTRIBUTING.md)
- [Plugin Development Guide](https://github.com/vuejs/vue-cli/blob/dev/docs/plugin-dev.md)
- [Plugin UI Development Guide](https://github.com/vuejs/vue-cli/blob/dev/docs/plugin-dev-ui.md)
18 changes: 18 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

- [Installation](#installation)
- [Usage](#usage)
- [Launch the GUI](#launch-the-gui)
- [Creating a New Project](#creating-a-new-project)
- [Presets](#presets)
- [Zero-config Prototyping](#zero-config-prototyping)
@@ -28,11 +29,28 @@ Commands:
inspect [options] [paths...] inspect the webpack config in a project with vue-cli-service
serve [options] [entry] serve a .js or .vue file in development mode with zero config
build [options] [entry] build a .js or .vue file in production mode with zero config
ui [options] start and open the vue-cli ui
init <template> <app-name> generate a project from a remote template (legacy API, requires @vue/cli-init)
```

For each command, you can also use `vue <command> --help` to see more detailed usage.

### Launch the GUI

```
Usage: ui [options]
start and open the vue-cli ui
Options:
-p, --port <port> Port used for the UI server (by default search for awailable port)
-h, --help output usage information
```

![Vue-cli UI preview](./vue-cli-ui-preview.gif)

### Creating a New Project

```
Binary file added docs/config-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
@@ -16,6 +16,9 @@ module.exports = {
// where to output built files
outputDir: 'dist',

// where to put static assets (js/css/img/font/...)
assetsDir: '',

// whether to use eslint-loader for lint on save.
// valid values: true | false | 'error'
// when set to 'error', lint errors will cause compilation to fail.
@@ -66,6 +69,7 @@ module.exports = {
// configure webpack-dev-server behavior
devServer: {
open: process.platform === 'darwin',
disableHostCheck: false,
host: '0.0.0.0',
port: 8080,
https: false,
Binary file added docs/custom-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/env.md
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ In addition to `VUE_APP_*` variables, there are also two special variables that
All resolved env variables will be available inside `public/index.html` via [lodash template interpolation](https://lodash.com/docs/4.17.5#template):

- `<%= VAR %>` for unescaped interpolation;
- `<%- VAR %>` for HTML-escaped interpolationl;
- `<%- VAR %>` for HTML-escaped interpolation;
- `<% expression %>` for JavaScript control flows.

For example, to reference static assets copied from the root of `public`, you will need to use the `BASE_URL` variable:
809 changes: 809 additions & 0 deletions docs/plugin-dev-ui.md

Large diffs are not rendered by default.

Binary file added docs/plugin-search-item.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/plugins.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/task-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/tasks-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/vue-cli-ui-preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/vue-cli-ui-schema.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -2,5 +2,5 @@
"lerna": "2.5.1",
"npmClient": "yarn",
"useWorkspaces": true,
"version": "3.0.0-beta.11"
"version": "3.0.0-beta.12"
}
2 changes: 1 addition & 1 deletion packages/@vue/babel-preset-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/babel-preset-app",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "babel-preset-app for vue-cli",
"main": "index.js",
"publishConfig": {
4 changes: 2 additions & 2 deletions packages/@vue/cli-init/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-init",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "init addon for vue-cli",
"main": "index.js",
"publishConfig": {
@@ -21,7 +21,7 @@
},
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-init#readme",
"dependencies": {
"execa": "^0.9.0",
"execa": "^0.10.0",
"vue-cli": "^2.9.2"
}
}
2 changes: 1 addition & 1 deletion packages/@vue/cli-overlay/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-overlay",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "error overlay & dev server middleware for vue-cli",
"main": "dist/client.js",
"files": [
3 changes: 1 addition & 2 deletions packages/@vue/cli-plugin-babel/README.md
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ module.exports = {

## Caching

[cache-loader](https://github.com/webpack-contrib/cache-loader) is enabled by default and cache is stored in `<projectRoot>/node_modules/.cache/cache-loader`.
[cache-loader](https://github.com/webpack-contrib/cache-loader) is enabled by default and cache is stored in `<projectRoot>/node_modules/.cache/babel-loader`.

## Parallelization

@@ -37,4 +37,3 @@ vue add @vue/babel
- `config.rule('js')`
- `config.rule('js').use('babel-loader')`
- `config.rule('js').use('cache-loader')`
- `config.rule('js').use('thread-loader')`
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ beforeAll(async () => {
test('dep from node_modules should not been transpiled', async () => {
const { stdout } = await project.run('vue-cli-service build')

let $vendorjs = stdout.match(/(js\/vendors~app\.[^.]+\.js)/)[1]
let $vendorjs = stdout.match(/(js\/chunk-vendors\.[^.]+\.js)/)[1]

$vendorjs = `dist/${$vendorjs}`
$vendorjs = await project.read($vendorjs)
@@ -58,7 +58,7 @@ test('dep from node_modules should been transpiled', async () => {

const { stdout } = await project.run('vue-cli-service build')

let $vendorjs = stdout.match(/(js\/vendors~app\.[^.]+\.js)/)[1]
let $vendorjs = stdout.match(/(js\/chunk-vendors\.[^.]+\.js)/)[1]

$vendorjs = `dist/${$vendorjs}`
$vendorjs = await project.read($vendorjs)
13 changes: 5 additions & 8 deletions packages/@vue/cli-plugin-babel/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
module.exports = (api, {
parallel,
transpileDependencies
}) => {
const useThreads = process.env.NODE_ENV === 'production' && parallel
const cacheDirectory = api.resolve('node_modules/.cache/cache-loader')
module.exports = (api, options) => {
const { genCacheConfig } = require('@vue/cli-shared-utils')
const useThreads = process.env.NODE_ENV === 'production' && options.parallel
const cliServicePath = require('path').dirname(require.resolve('@vue/cli-service'))

api.chainWebpack(webpackConfig => {
@@ -21,7 +18,7 @@ module.exports = (api, {
return true
}
// check if this is something the user explicitly wants to transpile
if (transpileDependencies.some(dep => filepath.match(dep))) {
if (options.transpileDependencies.some(dep => filepath.match(dep))) {
return false
}
// Don't transpile node_modules
@@ -30,7 +27,7 @@ module.exports = (api, {
.end()
.use('cache-loader')
.loader('cache-loader')
.options({ cacheDirectory })
.options(genCacheConfig(api, options, 'babel-loader', 'babel.config.js'))
.end()

if (useThreads) {
Binary file added packages/@vue/cli-plugin-babel/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions packages/@vue/cli-plugin-babel/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-babel",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "babel plugin for vue-cli",
"main": "index.js",
"repository": {
@@ -20,7 +20,7 @@
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-plugin-babel#readme",
"dependencies": {
"@babel/core": "7.0.0-beta.47",
"@vue/babel-preset-app": "^3.0.0-beta.11",
"@vue/babel-preset-app": "^3.0.0-beta.12",
"babel-loader": "^8.0.0-0"
},
"publishConfig": {
Binary file added packages/@vue/cli-plugin-e2e-cypress/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-e2e-cypress/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-e2e-cypress",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "e2e-cypress plugin for vue-cli",
"main": "index.js",
"repository": {
46 changes: 46 additions & 0 deletions packages/@vue/cli-plugin-e2e-cypress/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module.exports = api => {
api.describeTask({
match: /vue-cli-service test:e2e/,
description: 'cypress.tasks.test.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-cypress#injected-commands',
prompts: [
{
name: 'headless',
type: 'confirm',
default: false,
description: 'cypress.tasks.test.headless'
},
{
name: 'mode',
type: 'list',
default: 'development',
choices: [
{
name: 'development',
value: 'development'
},
{
name: 'production',
value: 'production'
},
{
name: 'test',
value: 'test'
}
],
description: 'cypress.tasks.test.mode'
},
{
name: 'url',
type: 'input',
default: '',
description: 'cypress.tasks.test.url'
}
],
onBeforeRun: ({ answers, args }) => {
if (answers.headless) args.push('--headless')
if (answers.mode) args.push('--mode', answers.mode)
if (answers.url) args.push('--url=' + answers.url)
}
})
}
Binary file added packages/@vue/cli-plugin-e2e-nightwatch/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-e2e-nightwatch/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-e2e-nightwatch",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "e2e-nightwatch plugin for vue-cli",
"main": "index.js",
"repository": {
18 changes: 18 additions & 0 deletions packages/@vue/cli-plugin-e2e-nightwatch/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = api => {
api.describeTask({
match: /vue-cli-service test:e2e/,
description: 'nightwatch.tasks.test.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-nightwatch#injected-commands',
prompts: [
{
name: 'url',
type: 'input',
default: '',
description: 'nightwatch.tasks.test.url'
}
],
onBeforeRun: ({ answers, args }) => {
if (answers.url) args.push('--url', answers.url)
}
})
}
6 changes: 4 additions & 2 deletions packages/@vue/cli-plugin-eslint/README.md
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@
--format [formatter] specify formatter (default: codeframe)
--no-fix do not fix errors
--max-errors specify number of errors to make build failed (default: 0)
--max-warnings specify number of warnings to make build failed (default: Infinity)
```

Lints and fixes files. If no specific files are given, it lints all files in `src` and `test`.
@@ -39,5 +41,5 @@ vue add @vue/eslint

## Injected webpack-chain Rules

- `config.rule('eslint')`
- `config.rule('eslint').use('eslint-loader')`
- `config.module.rule('eslint')`
- `config.module.rule('eslint').use('eslint-loader')`
8 changes: 4 additions & 4 deletions packages/@vue/cli-plugin-eslint/generator.js
Original file line number Diff line number Diff line change
@@ -16,17 +16,17 @@ module.exports = (api, { config, lintOn = [] }) => {
if (config === 'airbnb') {
eslintConfig.extends.push('@vue/airbnb')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-airbnb': '^3.0.0-beta.11'
'@vue/eslint-config-airbnb': '^3.0.0-beta.12'
})
} else if (config === 'standard') {
eslintConfig.extends.push('@vue/standard')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-standard': '^3.0.0-beta.11'
'@vue/eslint-config-standard': '^3.0.0-beta.12'
})
} else if (config === 'prettier') {
eslintConfig.extends.push('@vue/prettier')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-prettier': '^3.0.0-beta.11'
'@vue/eslint-config-prettier': '^3.0.0-beta.12'
})
} else {
// default
@@ -37,7 +37,7 @@ module.exports = (api, { config, lintOn = [] }) => {
if (api.hasPlugin('typescript')) {
eslintConfig.extends.push('@vue/typescript')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-typescript': '^3.0.0-beta.11'
'@vue/eslint-config-typescript': '^3.0.0-beta.12'
})
}

58 changes: 24 additions & 34 deletions packages/@vue/cli-plugin-eslint/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
module.exports = (api, { lintOnSave }) => {
if (lintOnSave) {
module.exports = (api, options) => {
if (options.lintOnSave) {
const extensions = require('./eslintOptions').extensions(api)
const cacheIdentifier = genCacheIdentifier(api.resolve('.'))

// eslint-loader doesn't bust cache when eslint config changes
// so we have to manually generate a cache identifier that takes the config
// into account.
const { genCacheConfig } = require('@vue/cli-shared-utils')
const { cacheIdentifier } = genCacheConfig(
api,
options,
[
'eslint-loader',
'eslint'
],
[
'.eslintrc.js',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
'.eslintrc',
'package.json'
]
)

api.chainWebpack(webpackConfig => {
webpackConfig.module
@@ -18,7 +38,7 @@ module.exports = (api, { lintOnSave }) => {
extensions,
cache: true,
cacheIdentifier,
emitWarning: lintOnSave !== 'error',
emitWarning: options.lintOnSave !== 'error',
formatter: require('eslint/lib/formatters/codeframe')
})
})
@@ -38,33 +58,3 @@ module.exports = (api, { lintOnSave }) => {
require('./lint')(args, api)
})
}

// eslint-loader doesn't bust cache when eslint config changes
// so we have to manually generate a cache identifier that takes the config
// into account.
function genCacheIdentifier (context) {
const fs = require('fs')
const path = require('path')
const files = [
'.eslintrc.js',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
'.eslintrc',
'package.json'
]

const configTimeStamp = (() => {
for (const file of files) {
if (fs.existsSync(path.join(context, file))) {
return fs.statSync(file).mtimeMs
}
}
})()

return JSON.stringify({
'eslint-loader': require('eslint-loader/package.json').version,
'eslint': require('eslint/package.json').version,
'config': configTimeStamp
})
}
4 changes: 2 additions & 2 deletions packages/@vue/cli-plugin-eslint/lint.js
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ module.exports = function lint (args = {}, api) {
const chalk = require('chalk')
const cwd = api.resolve('.')
const { CLIEngine } = require('eslint')
const { log, done } = require('@vue/cli-shared-utils')
const { log, done, exit } = require('@vue/cli-shared-utils')

const files = args._ && args._.length ? args._ : ['src', 'tests', '*.js']
const extensions = require('./eslintOptions').extensions(api)
@@ -70,7 +70,7 @@ module.exports = function lint (args = {}, api) {
if (isWarningsExceeded) {
log(`Eslint found too many warnings (maximum: ${argsConfig.maxWarnings}).`)
}
process.exit(1)
exit(1)
}
}

Binary file added packages/@vue/cli-plugin-eslint/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-eslint/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-eslint",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "eslint plugin for vue-cli",
"main": "index.js",
"repository": {
227 changes: 227 additions & 0 deletions packages/@vue/cli-plugin-eslint/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
module.exports = api => {
// Config file
api.describeConfig({
id: 'eslintrc',
name: 'ESLint configuration',
description: 'eslint.config.eslint.description',
link: 'https://github.com/vuejs/eslint-plugin-vue',
icon: '.eslintrc.json',
files: {
json: ['.eslintrc', '.eslintrc.json'],
js: ['.eslintrc.js'],
package: 'eslintConfig'
},
onRead: ({ data }) => ({
prompts: [
{
name: 'vue/attribute-hyphenation',
type: 'list',
message: 'Attribute hyphenation',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'Enforce attribute naming style in template (`my-prop` or `myProp`)',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',
default: JSON.stringify('off'),
choices: [
{
name: 'Off',
value: JSON.stringify('off')
},
{
name: 'Never',
value: JSON.stringify(['error', 'never'])
},
{
name: 'Always',
value: JSON.stringify(['error', 'always'])
}
],
value: data.rules && JSON.stringify(data.rules['vue/attribute-hyphenation'])
},
{
name: 'vue/html-end-tags',
type: 'confirm',
message: 'Template end tags style',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'End tag on Void elements, end tags and self-closing opening tags',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-end-tags.md',
default: false,
value: data.rules && data.rules['vue/html-end-tags'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
},
{
name: 'vue/html-indent',
type: 'list',
message: 'Template indentation',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'Enforce indentation in template',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-indent.md',
default: JSON.stringify('off'),
choices: [
{
name: 'Off',
value: JSON.stringify('off')
},
{
name: 'Tabs',
value: JSON.stringify(['error', 'tabs'])
},
{
name: '2 spaces',
value: JSON.stringify(['error', 2])
},
{
name: '4 spaces',
value: JSON.stringify(['error', 4])
},
{
name: '8 spaces',
value: JSON.stringify(['error', 8])
}
],
value: data.rules && JSON.stringify(data.rules['vue/html-indent'])
},
{
name: 'vue/html-self-closing',
type: 'confirm',
message: 'Template tag self-closing style',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'Self-close any component or non-Void element tags',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-self-closing.md',
default: false,
value: data.rules && data.rules['vue/html-self-closing'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
},
{
name: 'vue/require-default-prop',
type: 'confirm',
message: 'Require default in required props',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'This rule requires default value to be set for each props that are not marked as `required`',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-default-prop.md',
default: false,
value: data.rules && data.rules['vue/require-default-prop'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
},
{
name: 'vue/require-prop-types',
type: 'confirm',
message: 'Require types for props',
group: 'eslint.config.eslint.groups.strongly-recommended',
description: 'In committed code, prop definitions should always be as detailed as possible, specifying at least type(s)',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/require-prop-types.md',
default: false,
value: data.rules && data.rules['vue/require-prop-types'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
},
{
name: 'vue/attributes-order',
type: 'confirm',
message: 'Attribute order',
group: 'eslint.config.eslint.groups.recommended',
description: 'This rule aims to enforce ordering of component attributes (the default order is specified in the Vue style guide)',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attributes-order.md',
default: false,
value: data.rules && data.rules['vue/attributes-order'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
},
{
name: 'vue/html-quotes',
type: 'list',
message: 'Attribute quote style',
group: 'eslint.config.eslint.groups.recommended',
description: 'Enforce style of the attribute quotes in templates',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/html-quotes.md',
default: JSON.stringify('off'),
choices: [
{
name: 'Off',
value: JSON.stringify('off')
},
{
name: 'Double quotes',
value: JSON.stringify(['error', 'double'])
},
{
name: 'Single quotes',
value: JSON.stringify(['error', 'single'])
}
],
value: data.rules && JSON.stringify(data.rules['vue/html-quotes'])
},
{
name: 'vue/order-in-components',
type: 'confirm',
message: 'Component options order',
group: 'eslint.config.eslint.groups.recommended',
description: 'This rule aims to enforce ordering of component options (the default order is specified in the Vue style guide)',
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/order-in-components.md',
default: false,
value: data.rules && data.rules['vue/order-in-components'] === 'error',
filter: input => JSON.stringify(input ? 'error' : 'off'),
transformer: input => input === JSON.stringify('error')
}
]
}),
onWrite: async ({ api, prompts }) => {
const result = {}
for (const prompt of prompts) {
result[`rules.${prompt.id}`] = await api.getAnswer(prompt.id, JSON.parse)
}
api.setData(result)
}
})

api.describeConfig({
id: 'eslintrc-config',
name: 'ESLint extra',
description: 'eslint.config.eslint-extra.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint#configuration',
icon: '.eslintrc.json',
files: {
js: ['vue.config.js']
},
onRead: ({ data }) => ({
prompts: [
{
name: 'lintOnSave',
type: 'confirm',
message: 'eslint.config.eslint-extra.lintOnSave.message',
description: 'eslint.config.eslint-extra.lintOnSave.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint#configuration',
default: true,
value: data.lintOnSave
}
]
}),
onWrite: async ({ api, prompts }) => {
const result = {}
for (const prompt of prompts) {
result[prompt.id] = await api.getAnswer(prompt.id)
}
api.setData(result)
}
})

// Tasks
api.describeTask({
match: /vue-cli-service lint/,
description: 'eslint.tasks.lint.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint#injected-commands',
prompts: [
{
name: 'noFix',
type: 'confirm',
default: false,
description: 'eslint.tasks.lint.noFix'
}
],
onBeforeRun: ({ answers, args }) => {
if (answers.noFix) args.push('--no-fix')
}
})
}
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-pwa/__tests__/pwaPlugin.spec.js
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ test('pwa', async () => {

// should split and preload app.js & vendor.js
expect(index).toMatch(/<link [^>]+js\/app[^>]+\.js rel=preload>/)
expect(index).toMatch(/<link [^>]+js\/vendors~app[^>]+\.js rel=preload>/)
expect(index).toMatch(/<link [^>]+js\/chunk-vendors[^>]+\.js rel=preload>/)
// should preload css
expect(index).toMatch(/<link [^>]+app[^>]+\.css rel=preload>/)

7 changes: 6 additions & 1 deletion packages/@vue/cli-plugin-pwa/generator/index.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,11 @@ module.exports = api => {
'register-service-worker': '^1.0.0'
}
})
api.injectImports(`src/main.js`, `import './registerServiceWorker'`)
api.injectImports(api.entryFile, `import './registerServiceWorker'`)
api.render('./template')

if (api.invoking && api.hasPlugin('typescript')) {
const convertFiles = require('@vue/cli-plugin-typescript/generator/convert')
convertFiles(api)
}
}
Binary file added packages/@vue/cli-plugin-pwa/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions packages/@vue/cli-plugin-pwa/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-pwa/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-pwa",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "pwa plugin for vue-cli",
"main": "index.js",
"repository": {
137 changes: 137 additions & 0 deletions packages/@vue/cli-plugin-pwa/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const path = require('path')
const fs = require('fs')

function readAppManifest (cwd) {
const manifestPath = path.join(cwd, 'public/manifest.json')
if (fs.existsSync(manifestPath)) {
try {
return JSON.parse(fs.readFileSync(manifestPath, { encoding: 'utf8' }))
} catch (e) {
console.log(`Can't read JSON in ${manifestPath}`)
}
}
}

function updateAppManifest (cwd, handler) {
const manifestPath = path.join(cwd, 'public/manifest.json')
if (fs.existsSync(manifestPath)) {
try {
const manifest = JSON.parse(fs.readFileSync(manifestPath, { encoding: 'utf8' }))
handler(manifest)
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), { encoding: 'utf8' })
} catch (e) {
console.log(`Can't update JSON in ${manifestPath}`)
}
}
}

module.exports = api => {
// Config file
api.describeConfig({
id: 'pwa',
name: 'PWA',
description: 'pwa.config.pwa.description',
link: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa#configuration',
icon: 'mobile.xm',
files: {
js: ['vue.config.js']
},
onRead: ({ data, cwd }) => {
const manifest = readAppManifest(cwd)
return {
prompts: [
{
name: 'workboxPluginMode',
type: 'list',
message: 'pwa.config.pwa.workboxPluginMode.message',
description: 'pwa.config.pwa.workboxPluginMode.description',
link: 'https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#which_plugin_to_use',
default: 'GenerateSW',
value: data.pwa && data.pwa.workboxPluginMode,
choices: [
{
name: 'GenerateSW',
value: 'GenerateSW'
},
{
name: 'InjectManifest',
value: 'InjectManifest'
}
]
},
{
name: 'name',
type: 'input',
message: 'pwa.config.pwa.name.message',
description: 'pwa.config.pwa.name.description',
value: data.pwa && data.pwa.name
},
{
name: 'themeColor',
type: 'color',
message: 'pwa.config.pwa.themeColor.message',
description: 'pwa.config.pwa.themeColor.description',
default: '#4DBA87',
value: data.pwa && data.pwa.themeColor
},
{
name: 'backgroundColor',
type: 'color',
message: 'pwa.config.pwa.backgroundColor.message',
description: 'pwa.config.pwa.backgroundColor.description',
default: '#000000',
value: manifest && manifest.background_color,
skipSave: true
},
{
name: 'msTileColor',
type: 'color',
message: 'pwa.config.pwa.msTileColor.message',
description: 'pwa.config.pwa.msTileColor.description',
default: '#000000',
value: data.pwa && data.pwa.msTileColor
},
{
name: 'appleMobileWebAppStatusBarStyle',
type: 'input',
message: 'pwa.config.pwa.appleMobileWebAppStatusBarStyle.message',
description: 'pwa.config.pwa.appleMobileWebAppStatusBarStyle.description',
default: 'default',
value: data.pwa && data.pwa.appleMobileWebAppStatusBarStyle
}
]
}
},
onWrite: async ({ api, prompts, cwd }) => {
const result = {}
for (const prompt of prompts.filter(p => !p.raw.skipSave)) {
result[`pwa.${prompt.id}`] = await api.getAnswer(prompt.id)
}
api.setData(result)

// Update app manifest

const name = result['pwa.name']
if (name) {
updateAppManifest(cwd, manifest => {
manifest.name = name
manifest.short_name = name
})
}

const themeColor = result['pwa.themeColor']
if (themeColor) {
updateAppManifest(cwd, manifest => {
manifest.theme_color = themeColor
})
}

const backgroundColor = await api.getAnswer('backgroundColor')
if (backgroundColor) {
updateAppManifest(cwd, manifest => {
manifest.background_color = backgroundColor
})
}
}
})
}
3 changes: 1 addition & 2 deletions packages/@vue/cli-plugin-typescript/README.md
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ If opted to use [TSLint](https://palantir.github.io/tslint/) during project crea

## Caching

[cache-loader](https://github.com/webpack-contrib/cache-loader) is enabled by default and cache is stored in `<projectRoot>/node_modules/.cache/cache-loader`.
[cache-loader](https://github.com/webpack-contrib/cache-loader) is enabled by default and cache is stored in `<projectRoot>/node_modules/.cache/ts-loader`.

## Parallelization

@@ -34,5 +34,4 @@ vue add @vue/typescript
- `config.rule('ts').use('ts-loader')`
- `config.rule('ts').use('babel-loader')` (when used alongside `@vue/cli-plugin-babel`)
- `config.rule('ts').use('cache-loader')`
- `config.rule('ts').use('thread-loader')`
- `config.plugin('fork-ts-checker')`
22 changes: 22 additions & 0 deletions packages/@vue/cli-plugin-typescript/generator/convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = (api, { tsLint = false } = {}) => {
// delete all js files that have a ts file of the same name
// and simply rename other js files to ts
const jsRE = /\.js$/
const excludeRE = /^tests\/e2e\/|(\.config|rc)\.js$/
const convertLintFlags = require('../lib/convertLintFlags')
api.postProcessFiles(files => {
for (const file in files) {
if (jsRE.test(file) && !excludeRE.test(file)) {
const tsFile = file.replace(jsRE, '.ts')
if (!files[tsFile]) {
let content = files[file]
if (tsLint) {
content = convertLintFlags(content)
}
files[tsFile] = content
}
delete files[file]
}
}
})
}
21 changes: 1 addition & 20 deletions packages/@vue/cli-plugin-typescript/generator/index.js
Original file line number Diff line number Diff line change
@@ -79,24 +79,5 @@ module.exports = (api, {
hasJest
})

// delete all js files that have a ts file of the same name
// and simply rename other js files to ts
const jsRE = /\.js$/
const excludeRE = /^tests\/e2e\/|(\.config|rc)\.js$/
const convertLintFlags = require('../lib/convertLintFlags')
api.postProcessFiles(files => {
for (const file in files) {
if (jsRE.test(file) && !excludeRE.test(file)) {
const tsFile = file.replace(jsRE, '.ts')
if (!files[tsFile]) {
let content = files[file]
if (tsLint) {
content = convertLintFlags(content)
}
files[tsFile] = content
}
delete files[file]
}
}
})
require('./convert')(api, { tsLint })
}
Original file line number Diff line number Diff line change
@@ -24,7 +24,13 @@
"@/*": [
"src/*"
]
}
},
"lib": [
"es2015",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
18 changes: 8 additions & 10 deletions packages/@vue/cli-plugin-typescript/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
module.exports = (api, {
parallel,
lintOnSave
}) => {
module.exports = (api, options) => {
const fs = require('fs')
const useThreads = process.env.NODE_ENV === 'production' && parallel
const cacheDirectory = api.resolve('node_modules/.cache/cache-loader')
const { genCacheConfig } = require('@vue/cli-shared-utils')
const useThreads = process.env.NODE_ENV === 'production' && options.parallel

api.chainWebpack(config => {
config.entry('app')
@@ -26,8 +23,9 @@ module.exports = (api, {

addLoader({
loader: 'cache-loader',
options: { cacheDirectory }
options: genCacheConfig(api, options, 'ts-loader', 'tsconfig.json')
})

if (useThreads) {
addLoader({
loader: 'thread-loader'
@@ -43,7 +41,7 @@ module.exports = (api, {
loader: 'ts-loader',
options: {
transpileOnly: true,
appendTsSuffixTo: [/\.vue$/],
appendTsSuffixTo: ['\\.vue$'],
// https://github.com/TypeStrong/ts-loader#happypackmode-boolean-defaultfalse
happyPackMode: useThreads
}
@@ -52,15 +50,15 @@ module.exports = (api, {
tsxRule.use('ts-loader').loader('ts-loader').tap(options => {
options = Object.assign({}, options)
delete options.appendTsSuffixTo
options.appendTsxSuffixTo = [/\.vue$/]
options.appendTsxSuffixTo = ['\\.vue$']
return options
})

config
.plugin('fork-ts-checker')
.use(require('fork-ts-checker-webpack-plugin'), [{
vue: true,
tslint: lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')),
tslint: options.lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')),
formatter: 'codeframe',
// https://github.com/TypeStrong/ts-loader#happypackmode-boolean-defaultfalse
checkSyntacticErrors: useThreads
Binary file added packages/@vue/cli-plugin-typescript/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-typescript",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "typescript plugin for vue-cli",
"main": "index.js",
"repository": {
Binary file added packages/@vue/cli-plugin-unit-jest/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-unit-jest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-unit-jest",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "unit-jest plugin for vue-cli",
"main": "index.js",
"repository": {
Binary file added packages/@vue/cli-plugin-unit-mocha/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-unit-mocha/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-plugin-unit-mocha",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "mocha unit testing plugin for vue-cli",
"main": "index.js",
"repository": {
10 changes: 5 additions & 5 deletions packages/@vue/cli-service-global/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-service-global",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "vue-cli-service global addon for vue-cli",
"main": "index.js",
"publishConfig": {
@@ -21,10 +21,10 @@
},
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-build#readme",
"dependencies": {
"@vue/babel-preset-app": "^3.0.0-beta.11",
"@vue/cli-plugin-babel": "^3.0.0-beta.11",
"@vue/cli-plugin-eslint": "^3.0.0-beta.11",
"@vue/cli-service": "^3.0.0-beta.11",
"@vue/babel-preset-app": "^3.0.0-beta.12",
"@vue/cli-plugin-babel": "^3.0.0-beta.12",
"@vue/cli-plugin-eslint": "^3.0.0-beta.12",
"@vue/cli-service": "^3.0.0-beta.12",
"chalk": "^2.4.1",
"eslint-plugin-vue": "^4.5.0",
"resolve": "^1.7.1",
2 changes: 1 addition & 1 deletion packages/@vue/cli-service/__tests__/Service.spec.js
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ test('loading plugins from package.json', () => {
mockPkg({
devDependencies: {
'bar': '^1.0.0',
'@vue/cli-plugin-babel': '^3.0.0-beta.11',
'@vue/cli-plugin-babel': '^3.0.0-beta.12',
'vue-cli-plugin-foo': '^1.0.0'
}
})
2 changes: 1 addition & 1 deletion packages/@vue/cli-service/__tests__/build.spec.js
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ test('build', async () => {
const index = await project.read('dist/index.html')
// should split and preload app.js & vendor.js
expect(index).toMatch(/<link [^>]+js\/app[^>]+\.js rel=preload>/)
expect(index).toMatch(/<link [^>]+js\/vendors~app[^>]+\.js rel=preload>/)
expect(index).toMatch(/<link [^>]+js\/chunk-vendors[^>]+\.js rel=preload>/)
// should preload css
expect(index).toMatch(/<link [^>]+app[^>]+\.css rel=preload>/)

25 changes: 18 additions & 7 deletions packages/@vue/cli-service/__tests__/css.spec.js
Original file line number Diff line number Diff line change
@@ -22,12 +22,15 @@ const genConfig = (pkg = {}, env) => {
return config
}

const findRule = (config, lang, index = 2) => {
const findRule = (config, lang, index = 3) => {
const baseRule = config.module.rules.find(rule => {
return rule.test.test(`.${lang}`)
})
// all CSS rules have oneOf with two child rules, one for <style lang="module">
// and one for normal imports
// all CSS rules have 4 oneOf rules:
// - <style lang="module"> in Vue files
// - <style> in Vue files
// - *.modules.css imports from JS
// - *.css imports from JS
return baseRule.oneOf[index]
}

@@ -76,7 +79,13 @@ test('production defaults', () => {
})

test('CSS Modules rules', () => {
const config = genConfig()
const config = genConfig({
vue: {
css: {
modules: true
}
}
})
LANGS.forEach(lang => {
const expected = {
importLoaders: lang === 'css' ? 1 : 2, // no postcss-loader
@@ -85,10 +94,12 @@ test('CSS Modules rules', () => {
sourceMap: false,
modules: true
}
// module-query rules
// vue-modules rules
expect(findOptions(config, lang, 'css', 0)).toEqual(expected)
// module-ext rules
expect(findOptions(config, lang, 'css', 1)).toEqual(expected)
// normal-modules rules
expect(findOptions(config, lang, 'css', 2)).toEqual(expected)
// normal rules
expect(findOptions(config, lang, 'css', 3)).toEqual(expected)
})
})

170 changes: 170 additions & 0 deletions packages/@vue/cli-service/__tests__/multiPage.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
jest.setTimeout(30000)

const path = require('path')
const portfinder = require('portfinder')
const { defaultPreset } = require('@vue/cli/lib/options')
const { createServer } = require('http-server')
const create = require('@vue/cli-test-utils/createTestProject')
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')

async function makeProjectMultiPage (project) {
await project.write('vue.config.js', `
module.exports = {
pages: {
index: { entry: 'src/main.js' },
foo: { entry: 'src/foo.js' },
bar: { entry: 'src/bar.js' }
},
chainWebpack: config => {
const splitOptions = config.optimization.get('splitChunks')
config.optimization.splitChunks(Object.assign({}, splitOptions, {
minSize: 10000
}))
}
}
`)
await project.write('src/foo.js', `
import Vue from 'vue'
new Vue({
el: '#app',
render: h => h('h1', 'Foo')
})
`)
await project.write('src/bar.js', `
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
})
`)
const app = await project.read('src/App.vue')
await project.write('src/App.vue', app.replace(
`import HelloWorld from './components/HelloWorld.vue'`,
`const HelloWorld = () => import('./components/HelloWorld.vue')`
))
}

test('serve w/ multi page', async () => {
const project = await create('e2e-multi-page-serve', defaultPreset)

await makeProjectMultiPage(project)

await serve(
() => project.run('vue-cli-service serve'),
async ({ page, url, helpers }) => {
expect(await helpers.getText('h1')).toMatch(`Welcome to Your Vue.js App`)

await page.goto(`${url}/foo.html`)
expect(await helpers.getText('h1')).toMatch(`Foo`)

await page.goto(`${url}/bar.html`)
expect(await helpers.getText('h1')).toMatch(`Welcome to Your Vue.js App`)
}
)
})

let server, browser, page
test('build w/ multi page', async () => {
const project = await create('e2e-multi-page-build', defaultPreset)

await makeProjectMultiPage(project)

const { stdout } = await project.run('vue-cli-service build')
expect(stdout).toMatch('Build complete.')

// should generate the HTML pages
expect(project.has('dist/index.html')).toBe(true)
expect(project.has('dist/foo.html')).toBe(true)
expect(project.has('dist/bar.html')).toBe(true)

const assertSharedAssets = file => {
// should split and preload vendor chunk
expect(file).toMatch(/<link [^>]+js\/chunk-vendors[^>]+\.js rel=preload>/)
// should split and preload common js and css
expect(file).toMatch(/<link [^>]+js\/chunk-common[^>]+\.js rel=preload>/)
expect(file).toMatch(/<link [^>]+chunk-common[^>]+\.css rel=preload>/)
// should load common css
expect(file).toMatch(/<link href=\/css\/chunk-common\.\w+\.css rel=stylesheet>/)
// should load common js
expect(file).toMatch(/<script [^>]+src=\/js\/chunk-vendors\.\w+\.js>/)
expect(file).toMatch(/<script [^>]+src=\/js\/chunk-common\.\w+\.js>/)
}

const index = await project.read('dist/index.html')
assertSharedAssets(index)
// should preload correct page file
expect(index).toMatch(/<link [^>]+js\/index[^>]+\.js rel=preload>/)
expect(index).not.toMatch(/<link [^>]+js\/foo[^>]+\.js rel=preload>/)
expect(index).not.toMatch(/<link [^>]+js\/bar[^>]+\.js rel=preload>/)
// should prefetch async chunk js and css
expect(index).toMatch(/<link [^>]+css\/0\.\w+\.css rel=prefetch>/)
expect(index).toMatch(/<link [^>]+js\/0\.\w+\.js rel=prefetch>/)
// should load correct page js
expect(index).toMatch(/<script [^>]+src=\/js\/index\.\w+\.js>/)
expect(index).not.toMatch(/<script [^>]+src=\/js\/foo\.\w+\.js>/)
expect(index).not.toMatch(/<script [^>]+src=\/js\/bar\.\w+\.js>/)

const foo = await project.read('dist/foo.html')
assertSharedAssets(foo)
// should preload correct page file
expect(foo).not.toMatch(/<link [^>]+js\/index[^>]+\.js rel=preload>/)
expect(foo).toMatch(/<link [^>]+js\/foo[^>]+\.js rel=preload>/)
expect(foo).not.toMatch(/<link [^>]+js\/bar[^>]+\.js rel=preload>/)
// should not prefetch async chunk js and css because it's not used by
// this entry
expect(foo).not.toMatch(/<link [^>]+css\/0\.\w+\.css rel=prefetch>/)
expect(foo).not.toMatch(/<link [^>]+js\/0\.\w+\.js rel=prefetch>/)
// should load correct page js
expect(foo).not.toMatch(/<script [^>]+src=\/js\/index\.\w+\.js>/)
expect(foo).toMatch(/<script [^>]+src=\/js\/foo\.\w+\.js>/)
expect(foo).not.toMatch(/<script [^>]+src=\/js\/bar\.\w+\.js>/)

const bar = await project.read('dist/bar.html')
assertSharedAssets(bar)
// should preload correct page file
expect(bar).not.toMatch(/<link [^>]+js\/index[^>]+\.js rel=preload>/)
expect(bar).not.toMatch(/<link [^>]+js\/foo[^>]+\.js rel=preload>/)
expect(bar).toMatch(/<link [^>]+js\/bar[^>]+\.js rel=preload>/)
// should prefetch async chunk js and css
expect(bar).toMatch(/<link [^>]+css\/0\.\w+\.css rel=prefetch>/)
expect(bar).toMatch(/<link [^>]+js\/0\.\w+\.js rel=prefetch>/)
// should load correct page js
expect(bar).not.toMatch(/<script [^>]+src=\/js\/index\.\w+\.js>/)
expect(bar).not.toMatch(/<script [^>]+src=\/js\/foo\.\w+\.js>/)
expect(bar).toMatch(/<script [^>]+src=\/js\/bar\.\w+\.js>/)

// assert pages work
const port = await portfinder.getPortPromise()
server = createServer({ root: path.join(project.dir, 'dist') })

await new Promise((resolve, reject) => {
server.listen(port, err => {
if (err) return reject(err)
resolve()
})
})

const url = `http://localhost:${port}/`
const launched = await launchPuppeteer(url)
browser = launched.browser
page = launched.page

const getH1Text = async () => page.evaluate(() => {
return document.querySelector('h1').textContent
})

expect(await getH1Text()).toMatch('Welcome to Your Vue.js App')

await page.goto(`${url}foo.html`)
expect(await getH1Text()).toMatch('Foo')

await page.goto(`${url}bar.html`)
expect(await getH1Text()).toMatch('Welcome to Your Vue.js App')
})

afterAll(async () => {
await browser.close()
server.close()
})
27 changes: 24 additions & 3 deletions packages/@vue/cli-service/__tests__/serve.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
jest.setTimeout(45000)
jest.setTimeout(60000)

const path = require('path')
const fs = require('fs-extra')
const { defaultPreset } = require('@vue/cli/lib/options')
const create = require('@vue/cli-test-utils/createTestProject')
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
@@ -50,8 +52,6 @@ test('serve with router', async () => {
test('serve with inline entry', async () => {
const project = await create('e2e-serve-inline-entry', defaultPreset)

const path = require('path')
const fs = require('fs-extra')
await fs.move(
path.resolve(project.dir, 'src/main.js'),
path.resolve(project.dir, 'src/index.js')
@@ -72,3 +72,24 @@ test('serve with inline entry', async () => {
}
)
})

test('serve with no public dir', async () => {
const project = await create('e2e-serve-no-public', defaultPreset)

await fs.remove(path.resolve(project.dir, 'public'))

await serve(
() => project.run('vue-cli-service serve'),
async ({ nextUpdate, helpers }) => {
const msg = `Welcome to Your Vue.js App`
expect(await helpers.getText('h1')).toMatch(msg)

// test hot reload
const file = await project.read(`src/App.vue`)
project.write(`src/App.vue`, file.replace(msg, `Updated`))
await nextUpdate() // wait for child stdout update signal
await sleep(1000) // give the client time to update
expect(await helpers.getText('h1')).toMatch(`Updated`)
}
)
})
8 changes: 1 addition & 7 deletions packages/@vue/cli-service/generator/index.js
Original file line number Diff line number Diff line change
@@ -3,13 +3,7 @@ module.exports = (api, options) => {

api.extendPackage({
scripts: {
'serve': 'vue-cli-service serve' + (
// only auto open browser on MacOS where applescript
// can avoid dupilcate window opens
process.platform === 'darwin'
? ' --open'
: ''
),
'serve': 'vue-cli-service serve',
'build': 'vue-cli-service build'
},
dependencies: {
11 changes: 8 additions & 3 deletions packages/@vue/cli-service/generator/router/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
module.exports = (api, options) => {
api.injectImports(`src/main.js`, `import router from './router'`)
api.injectRootOptions(`src/main.js`, `router`)
api.injectImports(api.entryFile, `import router from './router'`)
api.injectRootOptions(api.entryFile, `router`)
api.extendPackage({
dependencies: {
'vue-router': '^3.0.1'
}
})
api.render('./template')

if (options.invoking) {
if (api.invoking) {
api.postProcessFiles(files => {
const appFile = files[`src/App.vue`]
if (appFile) {
@@ -26,5 +26,10 @@ module.exports = (api, options) => {
console.log(files[`src/App.vue`])
}
})

if (api.hasPlugin('typescript')) {
const convertFiles = require('@vue/cli-plugin-typescript/generator/convert')
convertFiles(api)
}
}
}
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org/en/essentials/getting-started.html" target="_blank">vue-router</a></li>
<li><a href="https://vuex.vuejs.org/en/intro.html" target="_blank">vuex</a></li>
<li><a href="https://router.vuejs.org" target="_blank">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
9 changes: 7 additions & 2 deletions packages/@vue/cli-service/generator/vuex/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
module.exports = (api, options) => {
api.injectImports(`src/main.js`, `import store from './store'`)
api.injectRootOptions(`src/main.js`, `store`)
api.injectImports(api.entryFile, `import store from './store'`)
api.injectRootOptions(api.entryFile, `store`)
api.extendPackage({
dependencies: {
vuex: '^3.0.1'
}
})
api.render('./template')

if (api.invoking && api.hasPlugin('typescript')) {
const convertFiles = require('@vue/cli-plugin-typescript/generator/convert')
convertFiles(api)
}
}
10 changes: 9 additions & 1 deletion packages/@vue/cli-service/lib/commands/build/index.js
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ module.exports = (api, options) => {
// respect inline build destination in copy plugin
if (args.dest) {
api.chainWebpack(config => {
if (args.target === 'app') {
if (config.plugins.has('copy')) {
config.plugin('copy').tap(args => {
args[0][0].to = targetDir
return args
@@ -127,6 +127,14 @@ module.exports = (api, options) => {

await fs.remove(targetDir)

// Expose advanced stats
if (args.dashboard) {
const DashboardPlugin = require('../../webpack/DashboardPlugin')
;(webpackConfig.plugins = webpackConfig.plugins || []).push(new DashboardPlugin({
type: 'build'
}))
}

return new Promise((resolve, reject) => {
webpack(webpackConfig, (err, stats) => {
stopSpinner(false)
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ module.exports = (api, { entry, name }, options) => {
const config = api.resolveChainableWebpackConfig()

// adjust css output name so they write to the same file
if (options.css.extract !== false) {
if (config.plugins.has('extract-css')) {
config
.plugin('extract-css')
.tap(args => {
@@ -89,7 +89,12 @@ module.exports = (api, { entry, name }, options) => {
// use dynamic publicPath so this can be deployed anywhere
// the actual path will be determined at runtime by checking
// document.currentScript.src.
publicPath: ''
publicPath: '',
// preserve UDM header from webpack 3 until webpack provides either
// libraryTarget: 'esm' or target: 'universal'
// https://github.com/webpack/webpack/issues/6522
// https://github.com/webpack/webpack/issues/6525
globalObject: `typeof self !== 'undefined' ? self : this`
})

return rawConfig
23 changes: 22 additions & 1 deletion packages/@vue/cli-service/lib/commands/serve.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const {
info,
hasYarn,
openBrowser
openBrowser,
IpcMessenger
} = require('@vue/cli-shared-utils')

const defaults = {
@@ -42,6 +43,15 @@ module.exports = (api, options) => {
// resolve webpack config
const webpackConfig = api.resolveWebpackConfig()

// expose advanced stats
if (args.dashboard) {
const DashboardPlugin = require('../webpack/DashboardPlugin')
;(webpackConfig.plugins = webpackConfig.plugins || []).push(new DashboardPlugin({
type: 'serve'
}))
}

// entry arg
const entry = args._[0]
if (entry) {
webpackConfig.entry = {
@@ -174,6 +184,17 @@ module.exports = (api, options) => {
openBrowser(urls.localUrlForBrowser)
}

// Send final app URL
if (args.dashboard) {
const ipc = new IpcMessenger()
ipc.connect()
ipc.send({
vueServe: {
url: urls.localUrlForBrowser
}
})
}

// resolve returned Promise
// so other commands can do api.service.run('serve').then(...)
resolve({
174 changes: 126 additions & 48 deletions packages/@vue/cli-service/lib/config/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// config that are specific to --target app
const fs = require('fs')
const path = require('path')

module.exports = (api, options) => {
api.chainWebpack(webpackConfig => {
@@ -7,8 +9,11 @@ module.exports = (api, options) => {
return
}

const isProd = process.env.NODE_ENV === 'production'

// HTML plugin
const resolveClientEnv = require('../util/resolveClientEnv')

const htmlOptions = {
templateParameters: (compilation, assets, pluginOptions) => {
// enhance html-webpack-plugin's built in template params
@@ -27,62 +32,135 @@ module.exports = (api, options) => {
}, resolveClientEnv(options.baseUrl, true /* raw */))
}
}
// only set template path if index.html exists
const htmlPath = api.resolve('public/index.html')
if (require('fs').existsSync(htmlPath)) {
htmlOptions.template = htmlPath

if (isProd) {
Object.assign(htmlOptions, {
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
})
}

webpackConfig
.plugin('html')
.use(require('html-webpack-plugin'), [htmlOptions])

// inject preload/prefetch to HTML
const PreloadPlugin = require('preload-webpack-plugin')
webpackConfig
.plugin('preload')
.use(PreloadPlugin, [{
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/]
}])

webpackConfig
.plugin('prefetch')
.use(PreloadPlugin, [{
rel: 'prefetch',
include: 'asyncChunks'
}])
// resolve HTML file(s)
const HTMLPlugin = require('html-webpack-plugin')
const PreloadPlugin = require('@vue/preload-webpack-plugin')
const multiPageConfig = options.pages
const htmlPath = api.resolve('public/index.html')
const defaultHtmlPath = path.resolve(__dirname, 'index-default.html')

if (!multiPageConfig) {
// default, single page setup.
htmlOptions.template = fs.existsSync(htmlPath)
? htmlPath
: defaultHtmlPath

// copy static assets in public/
webpackConfig
.plugin('copy')
.use(require('copy-webpack-plugin'), [[{
from: api.resolve('public'),
to: api.resolve(options.outputDir),
ignore: ['index.html', '.DS_Store']
}]])

if (process.env.NODE_ENV === 'production') {
// minify HTML
webpackConfig
.plugin('html')
.tap(([options]) => [Object.assign(options, {
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
})])
.use(HTMLPlugin, [htmlOptions])

// inject preload/prefetch to HTML
webpackConfig
.plugin('preload')
.use(PreloadPlugin, [{
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/]
}])

webpackConfig
.plugin('prefetch')
.use(PreloadPlugin, [{
rel: 'prefetch',
include: 'asyncChunks'
}])
} else {
// multi-page setup
webpackConfig.entryPoints.clear()

const pages = Object.keys(multiPageConfig)

// code splitting
pages.forEach(name => {
const {
entry,
template = `public/${name}.html`,
filename = `${name}.html`
} = multiPageConfig[name]
// inject entry
webpackConfig.entry(name).add(api.resolve(entry))

// inject html plugin for the page
const pageHtmlOptions = Object.assign({}, htmlOptions, {
chunks: ['chunk-vendors', 'chunk-common', name],
template: fs.existsSync(template) ? template : defaultHtmlPath,
filename
})

webpackConfig
.plugin(`html-${name}`)
.use(HTMLPlugin, [pageHtmlOptions])
})

pages.forEach(name => {
const { filename = `${name}.html` } = multiPageConfig[name]
webpackConfig
.plugin(`preload-${name}`)
.use(PreloadPlugin, [{
rel: 'preload',
includeHtmlNames: [filename],
include: {
type: 'initial',
entries: [name]
},
fileBlacklist: [/\.map$/, /hot-update\.js$/]
}])

webpackConfig
.plugin(`prefetch-${name}`)
.use(PreloadPlugin, [{
rel: 'prefetch',
includeHtmlNames: [filename],
include: {
type: 'asyncChunks',
entries: [name]
}
}])
})
}

// copy static assets in public/
if (fs.existsSync(api.resolve('public'))) {
webpackConfig
.plugin('copy')
.use(require('copy-webpack-plugin'), [[{
from: api.resolve('public'),
to: api.resolve(options.outputDir),
ignore: ['index.html', '.DS_Store']
}]])
}

// code splitting
if (isProd) {
webpackConfig
.optimization.splitChunks({
chunks: 'all'
chunks: 'all',
name: (m, chunks, cacheGroup) => `chunk-${cacheGroup}`,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
common: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
})
}
})
18 changes: 15 additions & 3 deletions packages/@vue/cli-service/lib/config/base.js
Original file line number Diff line number Diff line change
@@ -26,7 +26,12 @@ module.exports = (api, options) => {
.end()
.alias
.set('@', api.resolve('src'))
.set('vue$', options.compiler ? 'vue/dist/vue.esm.js' : 'vue/dist/vue.runtime.esm.js')
.set(
'vue$',
options.runtimeCompiler
? 'vue/dist/vue.esm.js'
: 'vue/dist/vue.runtime.esm.js'
)

webpackConfig.resolveLoader
.set('symlinks', true)
@@ -42,16 +47,23 @@ module.exports = (api, options) => {

// vue-loader --------------------------------------------------------------

const { genCacheConfig } = require('@vue/cli-shared-utils')
const vueLoaderCacheConfig = genCacheConfig(api, options, 'vue-loader')

webpackConfig.module
.rule('vue')
.test(/\.vue$/)
.use('cache-loader')
.loader('cache-loader')
.options(vueLoaderCacheConfig)
.end()
.use('vue-loader')
.loader('vue-loader')
.options({
.options(Object.assign({
compilerOptions: {
preserveWhitespace: false
}
})
}, vueLoaderCacheConfig))

webpackConfig
.plugin('vue-loader')
18 changes: 12 additions & 6 deletions packages/@vue/cli-service/lib/config/css.js
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ module.exports = (api, options) => {
const getAssetPath = require('../util/getAssetPath')

const {
modules = false,
extract = true,
sourceMap = false,
localIdentName = '[name]_[local]_[hash:base64:5]',
@@ -25,7 +26,7 @@ module.exports = (api, options) => {
const shouldExtract = isProd && extract !== false && !shadowMode
const extractOptions = Object.assign({
filename: getAssetPath(options, `css/[name].[contenthash:8].css`),
chunkFilename: getAssetPath(options, 'css/[name].[id].[contenthash:8].css')
chunkFilename: getAssetPath(options, 'css/[name].[contenthash:8].css')
}, extract && typeof extract === 'object' ? extract : {})

// check if the project has a valid postcss config
@@ -43,15 +44,20 @@ module.exports = (api, options) => {
const baseRule = webpackConfig.module.rule(lang).test(test)

// rules for <style lang="module">
const modulesRule = baseRule.oneOf('modules-query').resourceQuery(/module/)
applyLoaders(modulesRule, true)
const vueModulesRule = baseRule.oneOf('vue-modules').resourceQuery(/module/)
applyLoaders(vueModulesRule, true)

// rules for <style>
const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
applyLoaders(vueNormalRule, false)

// rules for *.module.* files
const modulesExtRule = baseRule.oneOf('modules-ext').test(/\.module\.\w+$/)
applyLoaders(modulesExtRule, true)
const extModulesRule = baseRule.oneOf('normal-modules').test(/\.module\.\w+$/)
applyLoaders(extModulesRule, true)

// rules for normal CSS imports
const normalRule = baseRule.oneOf('normal')
applyLoaders(normalRule, false)
applyLoaders(normalRule, modules)

function applyLoaders (rule, modules) {
if (shouldExtract) {
2 changes: 1 addition & 1 deletion packages/@vue/cli-service/lib/config/dev.js
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ module.exports = (api, options) => {
webpackConfig
.devtool('cheap-module-eval-source-map')
.output
.publicPath('/')
.publicPath(options.devBaseUrl || '/')

webpackConfig
.plugin('hmr')
12 changes: 12 additions & 0 deletions packages/@vue/cli-service/lib/config/index-default.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Vue App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
9 changes: 8 additions & 1 deletion packages/@vue/cli-service/lib/options.js
Original file line number Diff line number Diff line change
@@ -2,16 +2,19 @@ const { createSchema, validate } = require('@vue/cli-shared-utils')

const schema = createSchema(joi => joi.object({
baseUrl: joi.string(),
devBaseUrl: joi.string(),
outputDir: joi.string(),
assetsDir: joi.string(),
compiler: joi.boolean(),
runtimeCompiler: joi.boolean(),
transpileDependencies: joi.array(),
productionSourceMap: joi.boolean(),
parallel: joi.boolean(),
devServer: joi.object(),
pages: joi.object(),

// css
css: joi.object({
modules: joi.boolean(),
localIdentName: joi.string(),
extract: joi.alternatives().try(joi.boolean(), joi.object()),
sourceMap: joi.boolean(),
@@ -64,8 +67,12 @@ exports.defaults = () => ({
// enabled by default if the machine has more than 1 cores
parallel: require('os').cpus().length > 1,

// multi-page config
pages: undefined,

css: {
// extract: true,
// modules: false,
// localIdentName: '[name]_[local]_[hash:base64:5]',
// sourceMap: false,
// loaderOptions: {}
189 changes: 189 additions & 0 deletions packages/@vue/cli-service/lib/webpack/DashboardPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// From https://github.com/FormidableLabs/webpack-dashboard/blob/7f99b31c5f00a7818d8129cb8a8fc6eb1b71799c/plugin/index.js
// Modified by Guillaume Chau (Akryum)

/* eslint-disable max-params, max-statements */
'use strict'

const path = require('path')
const webpack = require('webpack')
const { IpcMessenger } = require('@vue/cli-shared-utils')
const { analyzeBundle } = require('./analyzeBundle')

const ID = 'DashboardPlugin'
const ONE_SECOND = 1000
const FILENAME_QUERY_REGEXP = /\?.*$/

const ipc = new IpcMessenger()

function getTimeMessage (timer) {
let time = Date.now() - timer

if (time >= ONE_SECOND) {
time /= ONE_SECOND
time = Math.round(time)
time += 's'
} else {
time += 'ms'
}

return ` (${time})`
}

class DashboardPlugin {
constructor (options) {
this.type = options.type
this.watching = false
}

cleanup () {
this.sendData = null
ipc.disconnect()
}

apply (compiler) {
let sendData = this.sendData
let timer

let assetSources = new Map()

if (!sendData) {
ipc.connect()
sendData = data => ipc.send({
webpackDashboardData: {
type: this.type,
value: data
}
})
}

// Progress status
let progressTime = Date.now()
const progressPlugin = new webpack.ProgressPlugin((percent, msg) => {
// Debouncing
const time = Date.now()
if (time - progressTime > 100) {
progressTime = time
sendData([
{
type: 'status',
value: 'Compiling'
},
{
type: 'progress',
value: percent
},
{
type: 'operations',
value: msg + getTimeMessage(timer)
}
])
}
})
progressPlugin.apply(compiler)

compiler.hooks.watchRun.tap(ID, c => {
this.watching = true
})

compiler.hooks.run.tap(ID, c => {
this.watching = false
})

compiler.hooks.compile.tap(ID, () => {
timer = Date.now()

sendData([
{
type: 'status',
value: 'Compiling'
}
])
})

compiler.hooks.invalid.tap(ID, () => {
sendData([
{
type: 'status',
value: 'Invalidated'
},
{
type: 'progress',
value: 0
},
{
type: 'operations',
value: 'idle'
}
])
})

compiler.hooks.failed.tap(ID, () => {
sendData([
{
type: 'status',
value: 'Failed'
},
{
type: 'operations',
value: `idle${getTimeMessage(timer)}`
}
])
})

compiler.hooks.afterEmit.tap(ID, compilation => {
assetSources = new Map()
for (const name in compilation.assets) {
const asset = compilation.assets[name]
assetSources.set(name.replace(FILENAME_QUERY_REGEXP, ''), asset.source())
}
})

compiler.hooks.done.tap(ID, stats => {
let statsData = stats.toJson()
// Sometimes all the information is located in `children` array
if ((!statsData.assets || !statsData.assets.length) && statsData.children && statsData.children.length) {
statsData = statsData.children[0]
}

const outputPath = compiler.options.output.path
statsData.assets.forEach(asset => {
// Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it)
asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, '')
asset.fullPath = path.join(outputPath, asset.name)
})
// Analyze the assets and update sizes on assets and modules
analyzeBundle(statsData, assetSources)

const hasErrors = stats.hasErrors()

sendData([
{
type: 'status',
value: hasErrors ? 'Failed' : 'Success'
},
{
type: 'progress',
value: 0
},
{
type: 'operations',
value: `idle${getTimeMessage(timer)}`
},
{
type: 'stats',
value: {
errors: hasErrors,
warnings: stats.hasWarnings(),
data: statsData
}
}
])

if (!this.watching) {
this.cleanup()
}
})
}
}

module.exports = DashboardPlugin
328 changes: 328 additions & 0 deletions packages/@vue/cli-service/lib/webpack/analyzeBundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
// From https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/ba3dbd71cec7becec0fbf529833204425f66efde/src/parseUtils.js
// Modified by Guillaume Chau (Akryum)

const acorn = require('acorn')
const walk = require('acorn/dist/walk')
const mapValues = require('lodash.mapvalues')
const transform = require('lodash.transform')
const zlib = require('zlib')
const { warn } = require('@vue/cli-shared-utils')

exports.analyzeBundle = function analyzeBundle (bundleStats, assetSources) {
// Picking only `*.js` assets from bundle that has non-empty `chunks` array
const jsAssets = []
const otherAssets = []

// Separate JS assets
bundleStats.assets.forEach(asset => {
if (asset.name.endsWith('.js') && asset.chunks && asset.chunks.length) {
jsAssets.push(asset)
} else {
otherAssets.push(asset)
}
})

// Trying to parse bundle assets and get real module sizes
let bundlesSources = null
let parsedModules = null

bundlesSources = {}
parsedModules = {}

for (const asset of jsAssets) {
const source = assetSources.get(asset.name)
let bundleInfo

try {
bundleInfo = parseBundle(source)
} catch (err) {
bundleInfo = null
}

if (!bundleInfo) {
warn(
`\nCouldn't parse bundle asset "${asset.fullPath}".\n` +
'Analyzer will use module sizes from stats file.\n'
)
parsedModules = null
bundlesSources = null
break
}

bundlesSources[asset.name] = bundleInfo.src
Object.assign(parsedModules, bundleInfo.modules)
}

// Update sizes

bundleStats.modules.forEach(module => {
const parsedSrc = parsedModules && parsedModules[module.id]
module.size = {
stats: module.size
}
if (parsedSrc) {
module.size.parsed = parsedSrc.length
module.size.gzip = getGzipSize(parsedSrc)
} else {
module.size.parsed = module.size.stats
module.size.gzip = 0
}
})

jsAssets.forEach(asset => {
const src = bundlesSources && bundlesSources[asset.name]
asset.size = {
stats: asset.size
}
if (src) {
asset.size.parsed = src.length
asset.size.gzip = getGzipSize(src)
} else {
asset.size.parsed = asset.size.stats
asset.size.gzip = 0
}
}, {})

otherAssets.forEach(asset => {
const src = assetSources.get(asset.name)
asset.size = {
stats: asset.size,
parsed: asset.size
}
if (src) {
asset.size.gzip = getGzipSize(src)
} else {
asset.size.gzip = 0
}
})
}

function parseBundle (bundleContent) {
const ast = acorn.parse(bundleContent, {
sourceType: 'script',
// I believe in a bright future of ECMAScript!
// Actually, it's set to `2050` to support the latest ECMAScript version that currently exists.
// Seems like `acorn` supports such weird option value.
ecmaVersion: 2050
})

const walkState = {
locations: null
}

walk.recursive(
ast,
walkState,
{
CallExpression (node, state, c) {
if (state.sizes) return

const args = node.arguments

// Additional bundle without webpack loader.
// Modules are stored in second argument, after chunk ids:
// webpackJsonp([<chunks>], <modules>, ...)
// As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
if (
node.callee.type === 'Identifier' &&
args.length >= 2 &&
isArgumentContainsChunkIds(args[0]) &&
isArgumentContainsModulesList(args[1])
) {
state.locations = getModulesLocationFromFunctionArgument(args[1])
return
}

// Additional bundle without webpack loader, with module IDs optimized.
// Modules are stored in second arguments Array(n).concat() call
// webpackJsonp([<chunks>], Array([minimum ID]).concat([<module>, <module>, ...]))
// As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
if (
node.callee.type === 'Identifier' &&
(args.length === 2 || args.length === 3) &&
isArgumentContainsChunkIds(args[0]) &&
isArgumentArrayConcatContainingChunks(args[1])
) {
state.locations = getModulesLocationFromArrayConcat(args[1])
return
}

// Main bundle with webpack loader
// Modules are stored in first argument:
// (function (...) {...})(<modules>)
if (
node.callee.type === 'FunctionExpression' &&
!node.callee.id &&
args.length === 1 &&
isArgumentContainsModulesList(args[0])
) {
state.locations = getModulesLocationFromFunctionArgument(args[0])
return
}

// Additional bundles with webpack 4 are loaded with:
// (window.webpackJsonp=window.webpackJsonp||[]).push([[chunkId], [<module>, <module>], [[optional_entries]]]);
if (
isWindowPropertyPushExpression(node) &&
args.length === 1 &&
isArgumentContainingChunkIdsAndModulesList(args[0])
) {
state.locations = getModulesLocationFromFunctionArgument(args[0].elements[1])
return
}

// Walking into arguments because some of plugins (e.g. `DedupePlugin`) or some Webpack
// features (e.g. `umd` library output) can wrap modules list into additional IIFE.
args.forEach(arg => c(arg, state))
}
}
)

if (!walkState.locations) {
return null
}

return {
src: bundleContent,
modules: mapValues(walkState.locations,
loc => bundleContent.slice(loc.start, loc.end)
)
}
}

function getGzipSize (buffer) {
return zlib.gzipSync(buffer).length
}

function isArgumentContainsChunkIds (arg) {
// Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
return (arg.type === 'ArrayExpression' && arg.elements.every(isModuleId))
}

function isArgumentContainsModulesList (arg) {
if (arg.type === 'ObjectExpression') {
return arg.properties
.map(arg => arg.value)
.every(isModuleWrapper)
}

if (arg.type === 'ArrayExpression') {
// Modules are contained in array.
// Array indexes are module ids
return arg.elements.every(elem =>
// Some of array items may be skipped because there is no module with such id
!elem ||
isModuleWrapper(elem)
)
}

return false
}

function isArgumentContainingChunkIdsAndModulesList (arg) {
if (
arg.type === 'ArrayExpression' &&
arg.elements.length >= 2 &&
isArgumentContainsChunkIds(arg.elements[0]) &&
isArgumentContainsModulesList(arg.elements[1])
) {
return true
}
return false
}

function isArgumentArrayConcatContainingChunks (arg) {
if (
arg.type === 'CallExpression' &&
arg.callee.type === 'MemberExpression' &&
// Make sure the object called is `Array(<some number>)`
arg.callee.object.type === 'CallExpression' &&
arg.callee.object.callee.type === 'Identifier' &&
arg.callee.object.callee.name === 'Array' &&
arg.callee.object.arguments.length === 1 &&
isNumericId(arg.callee.object.arguments[0]) &&
// Make sure the property X called for `Array(<some number>).X` is `concat`
arg.callee.property.type === 'Identifier' &&
arg.callee.property.name === 'concat' &&
// Make sure exactly one array is passed in to `concat`
arg.arguments.length === 1 &&
arg.arguments[0].type === 'ArrayExpression'
) {
// Modules are contained in `Array(<minimum ID>).concat(` array:
// https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
// The `<minimum ID>` + array indexes are module ids
return true
}

return false
}

function isWindowPropertyPushExpression (node) {
return node.callee.type === 'MemberExpression' &&
node.callee.property.name === 'push' &&
node.callee.object.type === 'AssignmentExpression' &&
node.callee.object.left.object.name === 'window'
}

function isModuleWrapper (node) {
return (
// It's an anonymous function expression that wraps module
((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.id) ||
// If `DedupePlugin` is used it can be an ID of duplicated module...
isModuleId(node) ||
// or an array of shape [<module_id>, ...args]
(node.type === 'ArrayExpression' && node.elements.length > 1 && isModuleId(node.elements[0]))
)
}

function isModuleId (node) {
return (node.type === 'Literal' && (isNumericId(node) || typeof node.value === 'string'))
}

function isNumericId (node) {
return (node.type === 'Literal' && Number.isInteger(node.value) && node.value >= 0)
}

function getModulesLocationFromFunctionArgument (arg) {
if (arg.type === 'ObjectExpression') {
const modulesNodes = arg.properties

return transform(modulesNodes, (result, moduleNode) => {
const moduleId = moduleNode.key.name || moduleNode.key.value

result[moduleId] = getModuleLocation(moduleNode.value)
}, {})
}

if (arg.type === 'ArrayExpression') {
const modulesNodes = arg.elements

return transform(modulesNodes, (result, moduleNode, i) => {
if (!moduleNode) return

result[i] = getModuleLocation(moduleNode)
}, {})
}

return {}
}

function getModulesLocationFromArrayConcat (arg) {
// arg(CallExpression) =
// Array([minId]).concat([<minId module>, <minId+1 module>, ...])
//
// Get the [minId] value from the Array() call first argument literal value
const minId = arg.callee.object.arguments[0].value
// The modules reside in the `concat()` function call arguments
const modulesNodes = arg.arguments[0].elements

return transform(modulesNodes, (result, moduleNode, i) => {
if (!moduleNode) return

result[i + minId] = getModuleLocation(moduleNode)
}, {})
}

function getModuleLocation (node) {
return { start: node.start, end: node.end }
}
13 changes: 8 additions & 5 deletions packages/@vue/cli-service/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-service",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "local service for vue-cli projects",
"main": "lib/Service.js",
"bin": {
@@ -21,9 +21,11 @@
},
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-service#readme",
"dependencies": {
"@vue/cli-overlay": "^3.0.0-beta.11",
"@vue/cli-shared-utils": "^3.0.0-beta.11",
"@vue/cli-overlay": "^3.0.0-beta.12",
"@vue/cli-shared-utils": "^3.0.0-beta.12",
"@vue/preload-webpack-plugin": "^1.0.0",
"@vue/web-component-wrapper": "^1.2.0",
"acorn": "^5.5.3",
"address": "^1.0.3",
"autoprefixer": "^8.4.1",
"cache-loader": "^1.2.2",
@@ -41,13 +43,14 @@
"html-webpack-plugin": "^3.2.0",
"launch-editor-middleware": "^2.2.1",
"lodash.defaultsdeep": "^4.6.0",
"lodash.mapvalues": "^4.6.0",
"lodash.transform": "^4.6.0",
"mini-css-extract-plugin": "^0.4.0",
"minimist": "^1.2.0",
"optimize-css-assets-webpack-plugin": "^4.0.1",
"ora": "^2.1.0",
"portfinder": "^1.0.13",
"postcss-loader": "^2.1.5",
"preload-webpack-plugin": "^3.0.0-alpha.1",
"read-pkg": "^3.0.0",
"semver": "^5.5.0",
"slash": "^2.0.0",
@@ -56,7 +59,7 @@
"thread-loader": "^1.1.5",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "^1.0.1",
"vue-loader": "^15.1.0",
"vue-loader": "^15.2.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.8.2",
"webpack-chain": "^4.8.0",
265 changes: 265 additions & 0 deletions packages/@vue/cli-service/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
const { openBrowser } = require('@vue/cli-shared-utils')

module.exports = api => {
const { setSharedData, getSharedData, removeSharedData, onAction } = api.namespace('webpack-dashboard-')

function resetSharedData (key) {
setSharedData(`${key}-status`, null)
setSharedData(`${key}-progress`, 0)
setSharedData(`${key}-operations`, null)
setSharedData(`${key}-stats`, null)
setSharedData(`${key}-sizes`, null)
setSharedData(`${key}-problems`, null)
}

function onWebpackMessage ({ data: message }) {
if (message.webpackDashboardData) {
const type = message.webpackDashboardData.type
for (const data of message.webpackDashboardData.value) {
setSharedData(`${type}-${data.type}`, data.value)
}
}
}

// Init data
api.onProjectOpen(() => {
for (const key of ['serve', 'build']) {
resetSharedData(key)
}
})

// Tasks
const views = {
views: [
{
id: 'vue-webpack-dashboard',
label: 'vue-webpack.dashboard.title',
icon: 'dashboard',
component: 'vue-webpack-dashboard'
},
{
id: 'vue-webpack-analyzer',
label: 'vue-webpack.analyzer.title',
icon: 'donut_large',
component: 'vue-webpack-analyzer'
}
],
defaultView: 'vue-webpack-dashboard'
}
api.describeTask({
match: /vue-cli-service serve/,
description: 'vue-webpack.tasks.serve.description',
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve',
prompts: [
{
name: 'open',
type: 'confirm',
default: false,
description: 'vue-webpack.tasks.serve.open'
},
{
name: 'mode',
type: 'list',
default: 'development',
choices: [
{
name: 'development',
value: 'development'
},
{
name: 'production',
value: 'production'
},
{
name: 'test',
value: 'test'
}
],
description: 'vue-webpack.tasks.serve.mode'
},
{
name: 'host',
type: 'input',
default: '0.0.0.0',
description: 'vue-webpack.tasks.serve.host'
},
{
name: 'port',
type: 'input',
default: 8080,
description: 'vue-webpack.tasks.serve.port'
},
{
name: 'https',
type: 'confirm',
default: false,
description: 'vue-webpack.tasks.serve.https'
}
],
onBeforeRun: ({ answers, args }) => {
// Args
if (answers.open) args.push('--open')
if (answers.mode) args.push('--mode', answers.mode)
if (answers.host) args.push('--host', answers.host)
if (answers.port) args.push('--port', answers.port)
if (answers.https) args.push('--https')
args.push('--dashboard')

// Data
resetSharedData('serve')
removeSharedData('serve-url')
},
onRun: () => {
api.ipcOn(onWebpackMessage)
},
onExit: () => {
api.ipcOff(onWebpackMessage)
removeSharedData('serve-url')
},
...views
})
api.describeTask({
match: /vue-cli-service build/,
description: 'vue-webpack.tasks.build.description',
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#build',
prompts: [
{
name: 'mode',
type: 'list',
default: 'production',
choices: [
{
name: 'development',
value: 'development'
},
{
name: 'production',
value: 'production'
},
{
name: 'test',
value: 'test'
}
],
description: 'vue-webpack.tasks.build.mode'
},
{
name: 'dest',
type: 'input',
default: 'dist',
description: 'vue-webpack.tasks.build.dest'
},
{
name: 'target',
type: 'list',
default: 'app',
choices: [
{
name: 'vue-webpack.tasks.build.target.app',
value: 'app'
},
{
name: 'vue-webpack.tasks.build.target.lib',
value: 'lib'
},
{
name: 'vue-webpack.tasks.build.target.wc',
value: 'wc'
},
{
name: 'vue-webpack.tasks.build.wc-async',
value: 'wc-async'
}
],
description: 'vue-webpack.tasks.build.description'
},
{
name: 'name',
type: 'input',
default: '',
description: 'vue-webpack.tasks.build.name'
},
{
name: 'watch',
type: 'confirm',
default: false,
description: 'vue-webpack.tasks.build.watch'
}
],
onBeforeRun: ({ answers, args }) => {
// Args
if (answers.mode) args.push('--mode', answers.mode)
if (answers.dest) args.push('--dest', answers.dest)
if (answers.target) args.push('--target', answers.target)
if (answers.name) args.push('--port', answers.name)
if (answers.watch) args.push('--watch')
args.push('--dashboard')

// Data
resetSharedData('build')
},
onRun: () => {
api.ipcOn(onWebpackMessage)
},
onExit: () => {
api.ipcOff(onWebpackMessage)
},
...views
})
// vue inspect
api.addTask({
name: 'inspect',
command: 'vue-cli-service inspect',
description: 'vue-webpack.tasks.inspect.description',
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md#inspecting-the-projects-webpack-config',
prompts: [
{
name: 'mode',
type: 'list',
default: 'production',
choices: [
{
name: 'development',
value: 'development'
},
{
name: 'production',
value: 'production'
},
{
name: 'test',
value: 'test'
}
],
description: 'vue-webpack.tasks.inspect.mode'
},
{
name: 'verbose',
type: 'confirm',
default: false,
description: 'vue-webpack.tasks.inspect.verbose'
}
],
onBeforeRun: ({ answers, args }) => {
if (answers.mode) args.push('--mode', answers.mode)
if (answers.verbose) args.push('--verbose')
}
})

// Webpack dashboard
api.addClientAddon({
id: 'vue-webpack',
path: '@vue/cli-ui-addon-webpack/dist'
})

// Open app button
api.ipcOn(({ data }) => {
if (data.vueServe) {
setSharedData('serve-url', data.vueServe.url)
}
})
onAction('open-app', () => {
const url = getSharedData('serve-url')
url && openBrowser(url.value)
})
}
10 changes: 7 additions & 3 deletions packages/@vue/cli-shared-utils/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
[
'env',
'exit',
'ipc',
'cache',
'logger',
'spinner',
'validate',
'openBrowser',
'pluginResolution'
'pluginResolution',
'request',
'spinner',
'validate'
].forEach(m => {
Object.assign(exports, require(`./lib/${m}`))
})
44 changes: 44 additions & 0 deletions packages/@vue/cli-shared-utils/lib/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const fs = require('fs')
const hash = require('hash-sum')

exports.genCacheConfig = (api, options, deps, configFiles) => {
if (!Array.isArray(deps)) {
deps = [deps]
}
const id = deps[0]
const cacheDirectory = api.resolve(`node_modules/.cache/${id}`)

const variables = {
'cache-loader': require('cache-loader/package.json').version,
env: process.env.NODE_ENV,
test: !!process.env.VUE_CLI_TEST,
config: [options.chainWebpack, options.configureWebpack]
}

for (const dep of deps) {
variables[dep] = require(`${dep}/package.json`).version
}

const readConfig = file => {
const absolutePath = api.resolve(file)
if (fs.existsSync(absolutePath)) {
return fs.readFileSync(absolutePath, 'utf-8')
}
}

if (configFiles) {
if (!Array.isArray(configFiles)) {
configFiles = [configFiles]
}
for (const file of configFiles) {
const content = readConfig(file)
if (content) {
variables.configFiles = content
break
}
}
}

const cacheIdentifier = hash(variables)
return { cacheDirectory, cacheIdentifier }
}
9 changes: 9 additions & 0 deletions packages/@vue/cli-shared-utils/lib/exit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
exports.exitProcess = true

exports.exit = function (code) {
if (exports.exitProcess) {
process.exit(code)
} else if (code > 0) {
throw new Error(`Process exited with code ${code}`)
}
}
92 changes: 92 additions & 0 deletions packages/@vue/cli-shared-utils/lib/ipc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const ipc = require('node-ipc')

exports.IpcMessenger = class IpcMessenger {
constructor (id = 'vue-cli') {
ipc.config.id = this.id = id
ipc.config.retry = 1500
ipc.config.silent = true

this.connected = false
this.connecting = false
this.disconnecting = false
this.queue = null

this.listeners = []

this.disconnectTimeout = 15000

// Prevent forced process exit
// (or else ipc messages may not be sent before kill)
process.exit = code => {
process.exitCode = code
}

this._reset()
}

send (data, type = 'message') {
if (this.connected) {
ipc.of[this.id].emit(type, data)
} else {
this.queue.push(data)
}
}

connect () {
if (this.connected || this.connecting) return
this.connecting = true
this.disconnecting = false
ipc.connectTo(this.id, () => {
this.connected = true
this.connecting = false
this.queue && this.queue.forEach(data => this.send(data))
this.queue = null

ipc.of[this.id].on('message', this._onMessage)
})
}

disconnect () {
if (!this.connected || this.disconnecting) return
this.disconnecting = true
this.connecting = false

const ipcTimer = setTimeout(() => {
this._disconnect()
}, this.disconnectTimeout)

this.send({ done: true }, 'ack')

ipc.of[this.id].on('ack', data => {
if (data.ok) {
clearTimeout(ipcTimer)
this._disconnect()
}
})
}

on (listener) {
this.listeners.push(listener)
}

off (listener) {
const index = this.listeners.indexOf(listener)
if (index !== -1) this.listeners.splice(index, 1)
}

_reset () {
this.queue = []
this.connected = false
}

_disconnect () {
this.connected = false
this.disconnecting = false
ipc.disconnect(this.id)
this._reset()
}

_onMessage (data) {
this.listeners.forEach(fn => fn(data))
}
}
23 changes: 22 additions & 1 deletion packages/@vue/cli-shared-utils/lib/logger.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
const chalk = require('chalk')
const readline = require('readline')
const padStart = require('string.prototype.padstart')
const EventEmitter = require('events')

exports.events = new EventEmitter()

function _log (type, tag, message) {
if (process.env.VUE_CLI_API_MODE && message) {
exports.events.emit('log', {
message,
type,
tag
})
}
}

const format = (label, msg) => {
return msg.split('\n').map((line, i) => {
@@ -12,24 +25,32 @@ const format = (label, msg) => {

const chalkTag = msg => chalk.bgBlackBright.white.dim(` ${msg} `)

exports.log = (msg = '', tag = null) => tag ? console.log(format(chalkTag(tag), msg)) : console.log(msg)
exports.log = (msg = '', tag = null) => {
tag ? console.log(format(chalkTag(tag), msg)) : console.log(msg)
_log('log', tag, msg)
}

exports.info = (msg, tag = null) => {
console.log(format(chalk.bgBlue.black(' INFO ') + (tag ? chalkTag(tag) : ''), msg))
_log('info', tag, msg)
}

exports.done = (msg, tag = null) => {
console.log(format(chalk.bgGreen.black(' DONE ') + (tag ? chalkTag(tag) : ''), msg))
_log('done', tag, msg)
}

exports.warn = (msg, tag = null) => {
console.warn(format(chalk.bgYellow.black(' WARN ') + (tag ? chalkTag(tag) : ''), chalk.yellow(msg)))
_log('warn', tag, msg)
}

exports.error = (msg, tag = null) => {
console.error(format(chalk.bgRed(' ERROR ') + (tag ? chalkTag(tag) : ''), chalk.red(msg)))
_log('error', tag, msg)
if (msg instanceof Error) {
console.error(msg.stack)
_log('error', tag, msg.stack)
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const request = require('request-promise-native')

module.exports = {
async get (uri) {
exports.request = {
get (uri) {
const reqOpts = {
method: 'GET',
resolveWithFullResponse: true,
10 changes: 9 additions & 1 deletion packages/@vue/cli-shared-utils/lib/validate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const joi = require('joi')
const { exit } = require('./exit')

// proxy to joi for option validation
exports.createSchema = fn => fn(joi)
@@ -10,8 +11,15 @@ exports.validate = (obj, schema, cb) => {
if (process.env.VUE_CLI_TEST) {
throw err
} else {
process.exit(1)
exit(1)
}
}
})
}

exports.validateSync = (obj, schema) => {
const result = joi.validate(obj, schema)
if (result.error) {
throw result.error
}
}
8 changes: 6 additions & 2 deletions packages/@vue/cli-shared-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-shared-utils",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "shared utilities for vue-cli packages",
"main": "index.js",
"repository": {
@@ -21,10 +21,14 @@
"dependencies": {
"chalk": "^2.3.0",
"cmd-shim": "^2.0.2",
"execa": "^0.9.0",
"execa": "^0.10.0",
"hash-sum": "^1.0.2",
"joi": "^12.0.0",
"node-ipc": "^9.1.1",
"opn": "^5.2.0",
"ora": "^1.3.0",
"request": "^2.83.0",
"request-promise-native": "^1.0.5",
"string.prototype.padstart": "^3.0.0"
},
"publishConfig": {
2 changes: 1 addition & 1 deletion packages/@vue/cli-test-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/cli-test-utils",
"version": "3.0.0-beta.11",
"version": "3.0.0-beta.12",
"description": "test utilities for vue-cli packages",
"repository": {
"type": "git",
5 changes: 5 additions & 0 deletions packages/@vue/cli-ui-addon-webpack/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
"@vue/app"
]
}
12 changes: 12 additions & 0 deletions packages/@vue/cli-ui-addon-webpack/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
root: true,
extends: [
'plugin:vue/essential',
'@vue/standard'
],
globals: {
ClientAddonApi: false,
mapSharedData: false,
Vue: false
}
}
20 changes: 20 additions & 0 deletions packages/@vue/cli-ui-addon-webpack/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
5 changes: 5 additions & 0 deletions packages/@vue/cli-ui-addon-webpack/.postcssrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": {
"autoprefixer": {}
}
}
8 changes: 8 additions & 0 deletions packages/@vue/cli-ui-addon-webpack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Webpack dashboard

> Dashboard & analyzer components for @vue/cli-ui

```
yarn serve
yarn build
```
Loading