From 3c59d6f4f40c7d8e42238c32ca9eb5cbbaaa7f0f Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Mon, 9 Apr 2018 01:53:41 +0200 Subject: [PATCH] feat(ui): client addons, ipc, shared data, plugin actions --- .postcssrc | 5 + packages/@vue/cli-plugin-eslint/ui.js | 2 +- .../cli-service/lib/commands/build/index.js | 8 + .../@vue/cli-service/lib/commands/serve.js | 10 + .../lib/webpack/DashboardPlugin.js | 372 ++++++++++++++++++ packages/@vue/cli-service/package.json | 4 + packages/@vue/cli-service/ui.js | 201 ++++++++++ packages/@vue/cli-ui-addon-build/.babelrc | 5 + .../@vue/cli-ui-addon-build/.eslintrc.json | 11 + packages/@vue/cli-ui-addon-build/.gitignore | 20 + packages/@vue/cli-ui-addon-build/.postcssrc | 5 + packages/@vue/cli-ui-addon-build/package.json | 31 ++ .../cli-ui-addon-build/public/favicon.ico | Bin 0 -> 1150 bytes .../@vue/cli-ui-addon-build/public/index.html | 17 + .../cli-ui-addon-build/src/assets/logo.png | Bin 0 -> 6849 bytes .../cli-ui-addon-build/src/assets/speeds.json | 14 + .../src/components/AssetList.vue | 65 +++ .../src/components/AssetListItem.vue | 77 ++++ .../src/components/BuildProgress.vue | 80 ++++ .../src/components/BuildStatus.vue | 89 +++++ .../src/components/ModuleList.vue | 39 ++ .../src/components/ModuleListItem.vue | 51 +++ .../src/components/SpeedStats.vue | 55 +++ .../src/components/SpeedStatsItem.vue | 54 +++ .../src/components/WebpackDashboard.vue | 117 ++++++ .../@vue/cli-ui-addon-build/src/filters.js | 25 ++ packages/@vue/cli-ui-addon-build/src/main.js | 8 + .../cli-ui-addon-build/src/store/index.js | 53 +++ .../cli-ui-addon-build/src/util/assets.js | 63 +++ .../cli-ui-addon-build/src/util/modules.js | 38 ++ .../@vue/cli-ui-addon-build/vue.config.js | 12 + packages/@vue/cli-ui/.eslintrc | 7 +- packages/@vue/cli-ui/package.json | 1 + packages/@vue/cli-ui/src/App.vue | 11 +- .../src/components/ClientAddonComponent.vue | 49 +++ .../src/components/ClientAddonLoader.vue | 43 ++ .../@vue/cli-ui/src/components/TaskItem.vue | 1 + .../cli-ui/src/components/TerminalView.vue | 23 +- .../cli-ui/src/graphql-api/api/PluginApi.js | 57 ++- .../@vue/cli-ui/src/graphql-api/channels.js | 6 +- .../graphql-api/connectors/client-addons.js | 36 ++ .../src/graphql-api/connectors/plugins.js | 43 +- .../src/graphql-api/connectors/prompts.js | 2 +- .../src/graphql-api/connectors/shared-data.js | 28 ++ .../src/graphql-api/connectors/tasks.js | 35 +- .../@vue/cli-ui/src/graphql-api/resolvers.js | 33 +- .../@vue/cli-ui/src/graphql-api/type-defs.js | 40 ++ .../@vue/cli-ui/src/graphql-api/utils/ipc.js | 42 ++ .../cli-ui/src/graphql/clientAddonAdded.gql | 7 + .../src/graphql/clientAddonFragment.gql | 4 + .../@vue/cli-ui/src/graphql/clientAddons.gql | 7 + .../cli-ui/src/graphql/pluginActionCall.gql | 7 + .../src/graphql/pluginActionCallFragment.gql | 4 + .../cli-ui/src/graphql/pluginActionCalled.gql | 7 + .../src/graphql/pluginActionResolved.gql | 7 + .../graphql/pluginActionResultFragment.gql | 6 + .../@vue/cli-ui/src/graphql/sharedData.gql | 7 + .../cli-ui/src/graphql/sharedDataFragment.gql | 4 + .../cli-ui/src/graphql/sharedDataUpdate.gql | 7 + .../cli-ui/src/graphql/sharedDataUpdated.gql | 7 + packages/@vue/cli-ui/src/graphql/task.gql | 9 +- packages/@vue/cli-ui/src/main.js | 26 ++ packages/@vue/cli-ui/src/style/main.styl | 26 ++ .../@vue/cli-ui/src/util/ClientAddonApi.js | 49 +++ packages/@vue/cli-ui/src/util/PluginAction.js | 35 ++ packages/@vue/cli-ui/src/util/SharedData.js | 111 ++++++ packages/@vue/cli-ui/src/util/responsive.js | 28 ++ .../cli-ui/src/views/ProjectTaskDetails.vue | 97 ++++- yarn.lock | 121 +++++- 69 files changed, 2506 insertions(+), 58 deletions(-) create mode 100644 .postcssrc create mode 100644 packages/@vue/cli-service/lib/webpack/DashboardPlugin.js create mode 100644 packages/@vue/cli-service/ui.js create mode 100644 packages/@vue/cli-ui-addon-build/.babelrc create mode 100644 packages/@vue/cli-ui-addon-build/.eslintrc.json create mode 100644 packages/@vue/cli-ui-addon-build/.gitignore create mode 100644 packages/@vue/cli-ui-addon-build/.postcssrc create mode 100644 packages/@vue/cli-ui-addon-build/package.json create mode 100644 packages/@vue/cli-ui-addon-build/public/favicon.ico create mode 100644 packages/@vue/cli-ui-addon-build/public/index.html create mode 100644 packages/@vue/cli-ui-addon-build/src/assets/logo.png create mode 100644 packages/@vue/cli-ui-addon-build/src/assets/speeds.json create mode 100644 packages/@vue/cli-ui-addon-build/src/components/AssetList.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/AssetListItem.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/BuildProgress.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/BuildStatus.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/ModuleList.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/ModuleListItem.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/SpeedStats.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/SpeedStatsItem.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/components/WebpackDashboard.vue create mode 100644 packages/@vue/cli-ui-addon-build/src/filters.js create mode 100644 packages/@vue/cli-ui-addon-build/src/main.js create mode 100644 packages/@vue/cli-ui-addon-build/src/store/index.js create mode 100644 packages/@vue/cli-ui-addon-build/src/util/assets.js create mode 100644 packages/@vue/cli-ui-addon-build/src/util/modules.js create mode 100644 packages/@vue/cli-ui-addon-build/vue.config.js create mode 100644 packages/@vue/cli-ui/src/components/ClientAddonComponent.vue create mode 100644 packages/@vue/cli-ui/src/components/ClientAddonLoader.vue create mode 100644 packages/@vue/cli-ui/src/graphql-api/connectors/client-addons.js create mode 100644 packages/@vue/cli-ui/src/graphql-api/connectors/shared-data.js create mode 100644 packages/@vue/cli-ui/src/graphql-api/utils/ipc.js create mode 100644 packages/@vue/cli-ui/src/graphql/clientAddonAdded.gql create mode 100644 packages/@vue/cli-ui/src/graphql/clientAddonFragment.gql create mode 100644 packages/@vue/cli-ui/src/graphql/clientAddons.gql create mode 100644 packages/@vue/cli-ui/src/graphql/pluginActionCall.gql create mode 100644 packages/@vue/cli-ui/src/graphql/pluginActionCallFragment.gql create mode 100644 packages/@vue/cli-ui/src/graphql/pluginActionCalled.gql create mode 100644 packages/@vue/cli-ui/src/graphql/pluginActionResolved.gql create mode 100644 packages/@vue/cli-ui/src/graphql/pluginActionResultFragment.gql create mode 100644 packages/@vue/cli-ui/src/graphql/sharedData.gql create mode 100644 packages/@vue/cli-ui/src/graphql/sharedDataFragment.gql create mode 100644 packages/@vue/cli-ui/src/graphql/sharedDataUpdate.gql create mode 100644 packages/@vue/cli-ui/src/graphql/sharedDataUpdated.gql create mode 100644 packages/@vue/cli-ui/src/util/ClientAddonApi.js create mode 100644 packages/@vue/cli-ui/src/util/PluginAction.js create mode 100644 packages/@vue/cli-ui/src/util/SharedData.js create mode 100644 packages/@vue/cli-ui/src/util/responsive.js diff --git a/.postcssrc b/.postcssrc new file mode 100644 index 0000000000..ed0149bf8b --- /dev/null +++ b/.postcssrc @@ -0,0 +1,5 @@ +{ + "plugins": { + "autoprefixer": {} + } +} \ No newline at end of file diff --git a/packages/@vue/cli-plugin-eslint/ui.js b/packages/@vue/cli-plugin-eslint/ui.js index bc7cfa30d8..022e5a53ad 100644 --- a/packages/@vue/cli-plugin-eslint/ui.js +++ b/packages/@vue/cli-plugin-eslint/ui.js @@ -68,7 +68,7 @@ module.exports = api => { description: 'Do not fix errors' } ], - onRun: ({ answers, args }) => { + onBeforeRun: ({ answers, args }) => { if (answers.noFix) { args.push('--no-fix') } diff --git a/packages/@vue/cli-service/lib/commands/build/index.js b/packages/@vue/cli-service/lib/commands/build/index.js index 07849ac293..30bab45c8b 100644 --- a/packages/@vue/cli-service/lib/commands/build/index.js +++ b/packages/@vue/cli-service/lib/commands/build/index.js @@ -97,6 +97,14 @@ module.exports = (api, options) => { 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) => { rimraf(targetDir, err => { if (err) { diff --git a/packages/@vue/cli-service/lib/commands/serve.js b/packages/@vue/cli-service/lib/commands/serve.js index 7a7b5aa787..2ec5f8a4fa 100644 --- a/packages/@vue/cli-service/lib/commands/serve.js +++ b/packages/@vue/cli-service/lib/commands/serve.js @@ -49,6 +49,16 @@ module.exports = (api, options) => { return portPromise.then(port => new Promise((resolve, reject) => { const webpackConfig = api.resolveWebpackConfig() + // Expose advanced stats + if (args.dashboard) { + const DashboardPlugin = require('../webpack/DashboardPlugin') + ;(webpackConfig.plugins = webpackConfig.plugins || []).push(new DashboardPlugin({ + type: 'serve', + gzip: false, + minified: false + })) + } + const urls = prepareURLs( useHttps ? 'https' : 'http', host, diff --git a/packages/@vue/cli-service/lib/webpack/DashboardPlugin.js b/packages/@vue/cli-service/lib/webpack/DashboardPlugin.js new file mode 100644 index 0000000000..58f8d996ec --- /dev/null +++ b/packages/@vue/cli-service/lib/webpack/DashboardPlugin.js @@ -0,0 +1,372 @@ +// From https://github.com/FormidableLabs/webpack-dashboard/blob/master/plugin/index.js +// Modified by Guillaume Chau (Akryum) +/* eslint-disable max-params, max-statements */ +'use strict' + +const os = require('os') +const path = require('path') +const most = require('most') +const webpack = require('webpack') +const InspectpackDaemon = require('inspectpack').daemon +const flatten = require('lodash.flatten') +const zlib = require('zlib') +const ipc = require('node-ipc') + +const ONE_SECOND = 1000 +const INSPECTPACK_PROBLEM_ACTIONS = ['versions', 'duplicates'] +const INSPECTPACK_PROBLEM_TYPE = 'problems' + +ipc.config.id = 'vue-cli' +ipc.config.retry = 1500 +ipc.config.silent = true + +let sendMessage + +ipc.connectTo('vue-cli', () => { + sendMessage = data => ipc.of['vue-cli'].emit('message', data) +}) + +const cacheFilename = path.resolve(os.homedir(), '.webpack-dashboard-cache.db') + +const serializeError = err => ({ + code: err.code, + message: err.message, + stack: err.stack +}) + +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})` +} + +function getGzipSize (buffer) { + return zlib.gzipSync(buffer).length +} + +class DashboardPlugin { + constructor (options) { + if (typeof options === 'function') { + this.handler = options + } else { + options = options || {} + this.root = options.root + this.gzip = !(options.gzip === false) + // `gzip = true` implies `minified = true`. + this.minified = this.gzip || !(options.minified === false) + this.handler = options.handler || null + this.type = options.type + } + + this.cleanup = this.cleanup.bind(this) + + this.watching = false + } + + cleanup () { + if (!this.watching) { + this.handler = null + if (this.inspectpack) { + this.inspectpack.terminate() + } + } + + setTimeout(() => { + ipc.disconnect('vue-cli') + }, 4000) + } + + apply (compiler) { + // Lazily created so plugin can be configured without starting the daemon + this.inspectpack = InspectpackDaemon.create({ cacheFilename }) + + let handler = this.handler + let timer + + let assetSources + + // Enable pathinfo for inspectpack support + compiler.options.output.pathinfo = true + + const nodeEnv = process.env.NODE_ENV + + if (!handler) { + handler = data => sendMessage && sendMessage({ + webpackDashboardData: { + type: this.type, + value: data + } + }) + } + + compiler.apply( + new webpack.ProgressPlugin((percent, msg) => { + handler([ + { + type: 'status', + value: 'Compiling' + }, + { + type: 'progress', + value: percent + }, + { + type: 'operations', + value: msg + getTimeMessage(timer) + } + ]) + }) + ) + + compiler.plugin('watch-run', (c, done) => { + this.watching = true + done() + }) + + compiler.plugin('run', (c, done) => { + this.watching = false + done() + }) + + compiler.plugin('compile', () => { + timer = Date.now() + handler([ + { + type: 'status', + value: 'Compiling' + } + ]) + }) + + compiler.plugin('invalid', () => { + handler([ + { + type: 'status', + value: 'Invalidated' + }, + { + type: 'progress', + value: 0 + }, + { + type: 'operations', + value: 'idle' + } + ]) + }) + + compiler.plugin('failed', () => { + handler([ + { + type: 'status', + value: 'Failed' + }, + { + type: 'operations', + value: `idle${getTimeMessage(timer)}` + } + ]) + }) + + compiler.plugin('after-emit', (compilation, done) => { + assetSources = new Map() + for (const name in compilation.assets) { + const asset = compilation.assets[name] + assetSources.set(name, asset.source()) + } + done() + }) + + compiler.plugin('done', stats => { + const statsData = stats.toJson() + const outputPath = compiler.options.output.path + statsData.assets.forEach(asset => { + asset.fullPath = path.join(outputPath, asset.name) + asset.gzipSize = getGzipSize(assetSources.get(asset.name)) + }) + + handler([ + { + type: 'status', + value: 'Success' + }, + { + type: 'progress', + value: 0 + }, + { + type: 'operations', + value: `idle${getTimeMessage(timer)}` + }, + { + type: 'stats', + value: { + errors: stats.hasErrors(), + warnings: stats.hasWarnings(), + data: statsData + } + } + ]) + + if (!this.minimal && nodeEnv !== 'production') { + this.observeBundleMetrics(stats, this.inspectpack).subscribe({ + next: message => handler([message]), + error: err => { + console.log('Error from inspectpack:', err) // eslint-disable-line no-console + this.cleanup() + }, + complete: this.cleanup + }) + } else { + this.cleanup() + } + }) + } + + /** + * Infer the root of the project, w/ package.json + node_modules. + * + * Inspectpack's `version` option needs to know where to start resolving + * packages from to translate `~/lodash/index.js` to + * `/ACTUAL/PATH/node_modules/index.js`. + * + * In common practice, this is _usually_ `bundle.context`, but sometimes folks + * will set that to a _different_ directory of assets directly copied in or + * something. + * + * To handle varying scenarios, we resolve the project's root as: + * 1. Plugin `root` option, if set + * 2. `bundle.context`, if `package.json` exists + * 3. `process.cwd()`, if `package.json` exists + * 4. `null` if nothing else matches + * + * @param {Object} bundle Bundle + * @returns {String|null} Project root path or null + */ + getProjectRoot (bundle) { + /*eslint-disable global-require*/ + // Start with plugin option (and don't check it). + // We **will** allow a bad project root to blow up webpack-dashboard. + if (this.root) { + return this.root + } + + // Try bundle context. + try { + if (bundle.context && require(path.join(bundle.context, 'package.json'))) { + return bundle.context + } + } catch (err) { /* passthrough */ } + + // Try CWD. + try { + if (require(path.resolve('package.json'))) { + return process.cwd() + } + } catch (err) { /* passthrough */ } + + // A null will be filtered out, disabling `versions` action. + return null + } + + observeBundleMetrics (stats, inspectpack) { + const bundlesToObserve = Object.keys(stats.compilation.assets) + .filter( + bundlePath => + // Don't include hot reload assets, they break everything + // and the updates are already included in the new assets + bundlePath.indexOf('.hot-update.') === -1 && + // Don't parse sourcemaps! + path.extname(bundlePath) === '.js' + ) + .map(bundlePath => ({ + path: bundlePath, + context: stats.compilation.options.context, + source: stats.compilation.assets[bundlePath].source() + })) + + const getSizes = bundles => + Promise.all( + bundles.map(bundle => + inspectpack + .sizes({ + code: bundle.source, + format: 'object', + minified: this.minified, + gzip: this.gzip + }) + .then(metrics => ({ + path: bundle.path, + metrics + })) + ) + ) + .then(bundle => ({ + type: 'sizes', + value: bundle + })) + .catch(err => ({ + type: 'sizes', + error: true, + value: serializeError(err) + })) + + const getProblems = bundles => + Promise.all( + INSPECTPACK_PROBLEM_ACTIONS.map(action => + Promise.all( + bundles + .map(bundle => { + // Root is only needed for versions and we hit disk to check it. + // So, only check on actual actions and bail out if not found. + let root + if (action === 'versions') { + root = this.getProjectRoot(bundle) + if (!root) { + return null + } + } + + return inspectpack[action]({ + code: bundle.source, + root, + duplicates: true, + format: 'object', + minified: this.minified, + gzip: this.gzip + }) + .then(metrics => ({ + path: bundle.path, + [action]: metrics + })) + }) + .filter(Boolean) // Filter out incorrect actions. + ) + ) + ) + .then(bundle => ({ + type: INSPECTPACK_PROBLEM_TYPE, + value: flatten(bundle) + })) + .catch(err => ({ + type: INSPECTPACK_PROBLEM_TYPE, + error: true, + value: serializeError(err) + })) + + const sizesStream = most.of(bundlesToObserve).map(getSizes) + const problemsStream = most.of(bundlesToObserve).map(getProblems) + + return most.mergeArray([sizesStream, problemsStream]).chain(most.fromPromise) + } +} + +module.exports = DashboardPlugin diff --git a/packages/@vue/cli-service/package.json b/packages/@vue/cli-service/package.json index 68a4f85daa..720891e562 100644 --- a/packages/@vue/cli-service/package.json +++ b/packages/@vue/cli-service/package.json @@ -40,10 +40,14 @@ "get-value": "^3.0.0", "globby": "^8.0.1", "html-webpack-plugin": "^3.0.6", + "inspectpack": "^2.2.4", "javascript-stringify": "^1.6.0", "launch-editor-middleware": "^2.2.1", "lodash.defaultsdeep": "^4.6.0", + "lodash.flatten": "^4.4.0", "minimist": "^1.2.0", + "most": "^1.7.3", + "node-ipc": "^9.1.1", "optimize-css-assets-webpack-plugin": "^3.2.0", "ora": "^2.0.0", "portfinder": "^1.0.13", diff --git a/packages/@vue/cli-service/ui.js b/packages/@vue/cli-service/ui.js new file mode 100644 index 0000000000..41eb8664af --- /dev/null +++ b/packages/@vue/cli-service/ui.js @@ -0,0 +1,201 @@ +module.exports = api => { + const { setSharedData } = 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 + for (const key of ['serve', 'build']) { + resetSharedData(key) + } + + // Tasks + api.describeTask({ + match: /vue-cli-service serve/, + description: 'Compiles and hot-reloads for development', + link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve', + prompts: [ + { + name: 'open', + type: 'confirm', + default: false, + description: 'Open browser on server start' + }, + { + name: 'mode', + type: 'list', + default: 'development', + choices: [ + { + name: 'development', + value: 'development' + }, + { + name: 'production', + value: 'production' + }, + { + name: 'test', + value: 'test' + } + ], + description: 'Specify env mode' + }, + { + name: 'host', + type: 'input', + default: '0.0.0.0', + description: 'Specify host' + }, + { + name: 'port', + type: 'input', + default: 8080, + description: 'Specify port' + }, + { + name: 'https', + type: 'confirm', + default: false, + description: 'Use 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') + }, + onRun: () => { + api.ipcOn(onWebpackMessage) + }, + onExit: () => { + api.ipcOff(onWebpackMessage) + }, + views: [ + { + id: 'vue-webpack-dashboard', + label: 'Dashboard', + icon: 'dashboard', + component: 'vue-webpack-dashboard' + } + ], + defaultView: 'vue-webpack-dashboard' + }) + api.describeTask({ + match: /vue-cli-service build/, + description: 'Compiles and minifies for production', + 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: 'Specify env mode' + }, + { + name: 'dest', + type: 'input', + default: 'dist', + description: 'Output directory' + }, + { + name: 'target', + type: 'list', + default: 'app', + choices: [ + { + name: 'Web app', + value: 'app' + }, + { + name: 'Library', + value: 'lib' + }, + { + name: 'Web component', + value: 'wc' + }, + { + name: 'Async web component', + value: 'wc-async' + } + ], + description: 'Build target' + }, + { + name: 'name', + type: 'input', + default: '', + description: 'Name for library or web-component mode (default: "name" in package.json or entry filename)' + } + ], + 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) + args.push('--dashboard') + + // Data + resetSharedData('build') + }, + onRun: () => { + api.ipcOn(onWebpackMessage) + }, + onExit: () => { + api.ipcOff(onWebpackMessage) + }, + views: [ + { + id: 'vue-webpack-dashboard', + label: 'Dashboard', + icon: 'dashboard', + component: 'vue-webpack-dashboard' + } + ], + defaultView: 'vue-webpack-dashboard' + }) + + // Testing client addon + api.addClientAddon({ + id: 'vue-webpack', + url: 'http://localhost:8081/app.js' + }) +} diff --git a/packages/@vue/cli-ui-addon-build/.babelrc b/packages/@vue/cli-ui-addon-build/.babelrc new file mode 100644 index 0000000000..2a818842cc --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "@vue/app" + ] +} diff --git a/packages/@vue/cli-ui-addon-build/.eslintrc.json b/packages/@vue/cli-ui-addon-build/.eslintrc.json new file mode 100644 index 0000000000..b12f10152c --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "plugin:vue/essential", + "@vue/standard" + ], + "globals": { + "ClientAddonApi": false, + "Vue": false + } +} diff --git a/packages/@vue/cli-ui-addon-build/.gitignore b/packages/@vue/cli-ui-addon-build/.gitignore new file mode 100644 index 0000000000..d7efd2016c --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/.gitignore @@ -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 diff --git a/packages/@vue/cli-ui-addon-build/.postcssrc b/packages/@vue/cli-ui-addon-build/.postcssrc new file mode 100644 index 0000000000..ed0149bf8b --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/.postcssrc @@ -0,0 +1,5 @@ +{ + "plugins": { + "autoprefixer": {} + } +} \ No newline at end of file diff --git a/packages/@vue/cli-ui-addon-build/package.json b/packages/@vue/cli-ui-addon-build/package.json new file mode 100644 index 0000000000..abb162f82a --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/package.json @@ -0,0 +1,31 @@ +{ + "name": "cli-ui-addon-build", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "vue-progress-path": "^0.0.2", + "vuex": "^3.0.1" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "^3.0.0-beta.6", + "@vue/cli-plugin-eslint": "^3.0.0-beta.6", + "@vue/cli-service": "^3.0.0-beta.6", + "@vue/eslint-config-standard": "^3.0.0-beta.3", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.1", + "vue-template-compiler": "^2.5.13" + }, + "peerDependencies": { + "vue": "^2.5.13" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/packages/@vue/cli-ui-addon-build/public/favicon.ico b/packages/@vue/cli-ui-addon-build/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c7b9a43c8cd16d0b434adaf513fcacb340809a11 GIT binary patch literal 1150 zcmchVOGsN$5QZm2NTI$erQpKHrdQX(jn+pVxKN`Ng)RzW5+8_2Xb@Y)Dkd6tq9V8u z3WAh^C@KZ1kA;tohzs}b3NC_*QmUXr$oP*rH(2mdT{z*(KX=aj=bX$9kqMvFRKj;Q zwI&d~A);J>5-PDega~WT5us%#Dc(Y}C4WpP?+fS;FaZ*z_CFzgiW=w{I02=q_TUz( z?=^H2uwoIK1n%|Ay21~QgjV1emYtWttJdz^L#=DjJ@Ex*9UPc*7<=rZo*_NAh4PxA zqkso~Ioa1y$e+3kIkXi29YNLi&lW}vY6C}ut4{8ou(7w=$_=$v{yJ$h?y!&bJfq*( zL_NQRF37$6e>%9erGV?p^lRFD?|5J_eupXaS;QluyrOmBT>PJhirMYb*i?(4Tf=j~?VvnUlY_ zDCVuuk3E&T9aP~Cr-0i-MaKUjf_|U!=R&t}_CfD=d${p~HH`BPaqb9aXT}UI$iGRg z>0^GlZ`vM4?;$*LhfI(RG|XK4GF+@-W*W}YJT5&2N_ZyZuaM_Ry=%PWx>r0P(Rc?> jRc4}SfGA>*agjwN{7E7DEm(*)%rSx{B0<6wBoglxJAy|R literal 0 HcmV?d00001 diff --git a/packages/@vue/cli-ui-addon-build/public/index.html b/packages/@vue/cli-ui-addon-build/public/index.html new file mode 100644 index 0000000000..ffaf0a08a6 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + cli-ui-addon-build + + + +
+ + + diff --git a/packages/@vue/cli-ui-addon-build/src/assets/logo.png b/packages/@vue/cli-ui-addon-build/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d2503fc2a44b5053b0837ebea6e87a2d339a43 GIT binary patch literal 6849 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 +
+
+
Assets
+ + + + +
+
+ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/AssetListItem.vue b/packages/@vue/cli-ui-addon-build/src/components/AssetListItem.vue new file mode 100644 index 0000000000..a3e6b9e749 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/AssetListItem.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/BuildProgress.vue b/packages/@vue/cli-ui-addon-build/src/components/BuildProgress.vue new file mode 100644 index 0000000000..0fc63dbf28 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/BuildProgress.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/BuildStatus.vue b/packages/@vue/cli-ui-addon-build/src/components/BuildStatus.vue new file mode 100644 index 0000000000..02ff1ee2bf --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/BuildStatus.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/ModuleList.vue b/packages/@vue/cli-ui-addon-build/src/components/ModuleList.vue new file mode 100644 index 0000000000..8a70c58ab6 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/ModuleList.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/ModuleListItem.vue b/packages/@vue/cli-ui-addon-build/src/components/ModuleListItem.vue new file mode 100644 index 0000000000..a99b5fecb4 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/ModuleListItem.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/SpeedStats.vue b/packages/@vue/cli-ui-addon-build/src/components/SpeedStats.vue new file mode 100644 index 0000000000..aa9db31ff1 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/SpeedStats.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/SpeedStatsItem.vue b/packages/@vue/cli-ui-addon-build/src/components/SpeedStatsItem.vue new file mode 100644 index 0000000000..9f0315eb65 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/SpeedStatsItem.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/components/WebpackDashboard.vue b/packages/@vue/cli-ui-addon-build/src/components/WebpackDashboard.vue new file mode 100644 index 0000000000..575184f5eb --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/components/WebpackDashboard.vue @@ -0,0 +1,117 @@ + + + + + + + diff --git a/packages/@vue/cli-ui-addon-build/src/filters.js b/packages/@vue/cli-ui-addon-build/src/filters.js new file mode 100644 index 0000000000..e37492dd2b --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/filters.js @@ -0,0 +1,25 @@ +export function size (size, unit = '', precision = 1) { + const kb = { + label: 'k', + value: 1024 + } + const mb = { + label: 'M', + value: 1024 * 1024 + } + let denominator + + if (size >= mb.value) { + denominator = mb + } else { + denominator = kb + if (size < kb.value * 0.92 && precision === 0) { + precision = 1 + } + } + return (size / denominator.value).toFixed(precision) + denominator.label + unit +} + +export function round (value, precision) { + return Math.round(value * precision) / precision +} diff --git a/packages/@vue/cli-ui-addon-build/src/main.js b/packages/@vue/cli-ui-addon-build/src/main.js new file mode 100644 index 0000000000..d910e179df --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/main.js @@ -0,0 +1,8 @@ +import VueProgress from 'vue-progress-path' +import WebpackDashboard from './components/WebpackDashboard.vue' + +Vue.use(VueProgress, { + defaultShape: 'circle' +}) + +ClientAddonApi.component('vue-webpack-dashboard', WebpackDashboard) diff --git a/packages/@vue/cli-ui-addon-build/src/store/index.js b/packages/@vue/cli-ui-addon-build/src/store/index.js new file mode 100644 index 0000000000..78d479681a --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/store/index.js @@ -0,0 +1,53 @@ +import Vuex from 'vuex' + +import { buildSortedAssets } from '../util/assets' +import { buildDepModules } from '../util/modules' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + state () { + return { + useGzip: true, + mode: 'serve', + serve: { + stats: null + }, + build: { + stats: null + } + } + }, + + getters: { + useGzip: state => state.useGzip, + mode: state => state.mode, + stats: state => state[state.mode].stats, + errors: (state, getters) => (getters.stats && getters.stats.data.errors) || [], + warnings: (state, getters) => (getters.stats && getters.stats.data.warnings) || [], + assets: (state, getters) => (getters.stats && getters.stats.data.assets) || [], + assetsSorted: (state, getters) => buildSortedAssets(getters.assets, getters.useGzip), + assetsTotalSize: (state, getters) => getters.assetsSorted.filter(a => !a.secondary).reduce((total, asset) => total + asset.size, 0), + modules: (state, getters) => (getters.stats && getters.stats.data.modules) || [], + modulesTotalSize: (state, getters) => getters.modules.reduce((total, module) => total + module.size, 0), + depModules: (state, getters) => buildDepModules(getters.modules), + depModulesTotalSize: (state, getters) => getters.depModules.reduce((total, module) => total + module.size, 0), + chunks: (state, getters) => (getters.stats && getters.stats.data.chunks) || [] + }, + + mutations: { + useGzip (state, value) { + state.useGzip = value + }, + + mode (state, value) { + state.mode = value + }, + + stats (state, { mode, value }) { + state[mode].stats = value + } + } +}) + +export default store diff --git a/packages/@vue/cli-ui-addon-build/src/util/assets.js b/packages/@vue/cli-ui-addon-build/src/util/assets.js new file mode 100644 index 0000000000..9ac8544d43 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/util/assets.js @@ -0,0 +1,63 @@ +import speedsData from '../assets/speeds.json' + +const DOWNLOAD_TIME_THRESHOLD_SECONDS = 5 + +export function getSpeedData (datapoint, size) { + const assetsSizeInMB = size / 1024 / 1024 + const bandwidthInMbps = datapoint.mbps + const bandwidthInMBps = bandwidthInMbps / 8 + const rttInSeconds = datapoint.rtt / 1000 + + const totalDownloadTime = assetsSizeInMB / bandwidthInMBps + rttInSeconds + + const isDownloadTimeOverThreshold = + totalDownloadTime > DOWNLOAD_TIME_THRESHOLD_SECONDS + const timeDifferenceToThreshold = + (isDownloadTimeOverThreshold ? '+' : '-') + + Math.abs(totalDownloadTime - DOWNLOAD_TIME_THRESHOLD_SECONDS).toFixed(2) + +'s' + + return { + totalDownloadTime, + isDownloadTimeOverThreshold, + timeDifferenceToThreshold + } +} + +export function getSpeeds (size) { + return Object.keys(speedsData).reduce((obj, key) => { + obj[key] = { + ...getSpeedData(speedsData[key], size), + ...speedsData[key] + } + return obj + }, {}) +} + +export function buildSortedAssets (assets, userGzip) { + let list = assets.slice() + if (list.length) { + const max = list[0].size + list = list.map(asset => { + const size = userGzip ? asset.gzipSize : asset.size + return { + name: asset.name, + size, + big: size > 250000, + ratio: size / max, + secondary: /\.map$/.test(asset.name), + speeds: getSpeeds(size) + } + }) + list = list.sort((a, b) => { + if (a.secondary === b.secondary) { + return b.size - a.size + } else if (a.secondary && !b.secondary) { + return 1 + } else { + return -1 + } + }) + } + return list +} diff --git a/packages/@vue/cli-ui-addon-build/src/util/modules.js b/packages/@vue/cli-ui-addon-build/src/util/modules.js new file mode 100644 index 0000000000..06db4a7b21 --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/src/util/modules.js @@ -0,0 +1,38 @@ +const getModulePath = function (identifier) { + return identifier.replace(/.*!/, '').replace(/\\/g, '/') +} + +export function buildDepModules (modules) { + const deps = new Map() + for (const module of modules) { + const path = getModulePath(module.identifier) + const pathParts = path.split('/node_modules/') + if (pathParts.length === 2) { + let name = pathParts[1] + if (name.charAt(0) === '@') { + // Scoped package + name = name.substr(0, name.indexOf('/', name.indexOf('/') + 1)) + } else { + name = name.substr(0, name.indexOf('/')) + } + let dep = deps.get(name) + if (!dep) { + dep = { + name, + size: 0 + } + deps.set(name, dep) + } + dep.size += module.size + } + } + let list = Array.from(deps.values()) + list = list.sort((a, b) => b.size - a.size) + if (list.length) { + const max = list[0].size + for (const dep of list) { + dep.ratio = dep.size / max + } + } + return list +} diff --git a/packages/@vue/cli-ui-addon-build/vue.config.js b/packages/@vue/cli-ui-addon-build/vue.config.js new file mode 100644 index 0000000000..ef1bb3e32d --- /dev/null +++ b/packages/@vue/cli-ui-addon-build/vue.config.js @@ -0,0 +1,12 @@ +module.exports = { + configureWebpack: { + output: { + publicPath: 'http://localhost:8081/' + } + }, + devServer: { + headers: { + 'Access-Control-Allow-Origin': '*' + } + } +} diff --git a/packages/@vue/cli-ui/.eslintrc b/packages/@vue/cli-ui/.eslintrc index dfc723fafa..71cf2c9e79 100644 --- a/packages/@vue/cli-ui/.eslintrc +++ b/packages/@vue/cli-ui/.eslintrc @@ -3,5 +3,8 @@ "extends": [ "plugin:vue/essential", "@vue/standard" - ] -} \ No newline at end of file + ], + "globals": { + "ClientAddonApi": false + } +} diff --git a/packages/@vue/cli-ui/package.json b/packages/@vue/cli-ui/package.json index 039340f98e..efd81baa97 100644 --- a/packages/@vue/cli-ui/package.json +++ b/packages/@vue/cli-ui/package.json @@ -24,6 +24,7 @@ "clone": "^1.0.4", "file-icons-js": "^1.0.3", "graphql": "^0.13.0", + "graphql-type-json": "^0.2.0", "js-yaml": "^3.11.0", "lowdb": "^1.0.0", "lru-cache": "^4.1.2", diff --git a/packages/@vue/cli-ui/src/App.vue b/packages/@vue/cli-ui/src/App.vue index 6a543dc2ba..d503819e7e 100644 --- a/packages/@vue/cli-ui/src/App.vue +++ b/packages/@vue/cli-ui/src/App.vue @@ -5,19 +5,10 @@ + - - diff --git a/packages/@vue/cli-ui/src/components/ClientAddonLoader.vue b/packages/@vue/cli-ui/src/components/ClientAddonLoader.vue new file mode 100644 index 0000000000..29fcd46d48 --- /dev/null +++ b/packages/@vue/cli-ui/src/components/ClientAddonLoader.vue @@ -0,0 +1,43 @@ + diff --git a/packages/@vue/cli-ui/src/components/TaskItem.vue b/packages/@vue/cli-ui/src/components/TaskItem.vue index c171bb8695..b90f7b9418 100644 --- a/packages/@vue/cli-ui/src/components/TaskItem.vue +++ b/packages/@vue/cli-ui/src/components/TaskItem.vue @@ -71,6 +71,7 @@ export default { .list-item-info flex 100% 1 1 width 0 + overflow hidden >>> .description white-space nowrap diff --git a/packages/@vue/cli-ui/src/components/TerminalView.vue b/packages/@vue/cli-ui/src/components/TerminalView.vue index d0b5541c65..7f066765ba 100644 --- a/packages/@vue/cli-ui/src/components/TerminalView.vue +++ b/packages/@vue/cli-ui/src/components/TerminalView.vue @@ -1,6 +1,6 @@