mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-05-01 17:29:53 -05:00
proxy + wip overlay
This commit is contained in:
@@ -3,3 +3,4 @@ node_modules
|
||||
design
|
||||
*.log
|
||||
packages/test
|
||||
dist
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
const launchEditor = require('./launchEditor')
|
||||
|
||||
module.exports = () => {
|
||||
return function launchEditorMiddleware (req, res, next) {
|
||||
if (req.url.startsWith('/open-in-editor')) {
|
||||
launchEditor(req.query.fileName, req.query.lineNumber)
|
||||
res.end()
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file at
|
||||
* https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const shellQuote = require('shell-quote')
|
||||
const child_process = require('child_process') // eslint-disable-line
|
||||
|
||||
function isTerminalEditor (editor) {
|
||||
switch (editor) {
|
||||
case 'vim':
|
||||
case 'emacs':
|
||||
case 'nano':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Map from full process name to binary that starts the process
|
||||
// We can't just re-use full process name, because it will spawn a new instance
|
||||
// of the app every time
|
||||
const COMMON_EDITORS_OSX = {
|
||||
'/Applications/Atom.app/Contents/MacOS/Atom': 'atom',
|
||||
'/Applications/Atom Beta.app/Contents/MacOS/Atom Beta':
|
||||
'/Applications/Atom Beta.app/Contents/MacOS/Atom Beta',
|
||||
'/Applications/Brackets.app/Contents/MacOS/Brackets': 'brackets',
|
||||
'/Applications/Sublime Text.app/Contents/MacOS/Sublime Text':
|
||||
'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl',
|
||||
'/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2':
|
||||
'/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl',
|
||||
'/Applications/Visual Studio Code.app/Contents/MacOS/Electron': 'code',
|
||||
'/Applications/AppCode.app/Contents/MacOS/appcode':
|
||||
'/Applications/AppCode.app/Contents/MacOS/appcode',
|
||||
'/Applications/CLion.app/Contents/MacOS/clion':
|
||||
'/Applications/CLion.app/Contents/MacOS/clion',
|
||||
'/Applications/IntelliJ IDEA.app/Contents/MacOS/idea':
|
||||
'/Applications/IntelliJ IDEA.app/Contents/MacOS/idea',
|
||||
'/Applications/PhpStorm.app/Contents/MacOS/phpstorm':
|
||||
'/Applications/PhpStorm.app/Contents/MacOS/phpstorm',
|
||||
'/Applications/PyCharm.app/Contents/MacOS/pycharm':
|
||||
'/Applications/PyCharm.app/Contents/MacOS/pycharm',
|
||||
'/Applications/PyCharm CE.app/Contents/MacOS/pycharm':
|
||||
'/Applications/PyCharm CE.app/Contents/MacOS/pycharm',
|
||||
'/Applications/RubyMine.app/Contents/MacOS/rubymine':
|
||||
'/Applications/RubyMine.app/Contents/MacOS/rubymine',
|
||||
'/Applications/WebStorm.app/Contents/MacOS/webstorm':
|
||||
'/Applications/WebStorm.app/Contents/MacOS/webstorm'
|
||||
}
|
||||
|
||||
const COMMON_EDITORS_LINUX = {
|
||||
atom: 'atom',
|
||||
Brackets: 'brackets',
|
||||
code: 'code',
|
||||
emacs: 'emacs',
|
||||
'idea.sh': 'idea',
|
||||
'phpstorm.sh': 'phpstorm',
|
||||
'pycharm.sh': 'pycharm',
|
||||
'rubymine.sh': 'rubymine',
|
||||
sublime_text: 'sublime_text',
|
||||
vim: 'vim',
|
||||
'webstorm.sh': 'webstorm'
|
||||
}
|
||||
|
||||
const COMMON_EDITORS_WIN = [
|
||||
'Brackets.exe',
|
||||
'Code.exe',
|
||||
'atom.exe',
|
||||
'sublime_text.exe',
|
||||
'notepad++.exe',
|
||||
'clion.exe',
|
||||
'clion64.exe',
|
||||
'idea.exe',
|
||||
'idea64.exe',
|
||||
'phpstorm.exe',
|
||||
'phpstorm64.exe',
|
||||
'pycharm.exe',
|
||||
'pycharm64.exe',
|
||||
'rubymine.exe',
|
||||
'rubymine64.exe',
|
||||
'webstorm.exe',
|
||||
'webstorm64.exe'
|
||||
]
|
||||
|
||||
function addWorkspaceToArgumentsIfExists (args, workspace) {
|
||||
if (workspace) {
|
||||
args.unshift(workspace)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
function getArgumentsForLineNumber (editor, fileName, lineNumber, workspace) {
|
||||
const editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, '')
|
||||
switch (editorBasename) {
|
||||
case 'atom':
|
||||
case 'Atom':
|
||||
case 'Atom Beta':
|
||||
case 'subl':
|
||||
case 'sublime':
|
||||
case 'sublime_text':
|
||||
case 'wstorm':
|
||||
case 'charm':
|
||||
return [fileName + ':' + lineNumber]
|
||||
case 'notepad++':
|
||||
return ['-n' + lineNumber, fileName]
|
||||
case 'vim':
|
||||
case 'mvim':
|
||||
case 'joe':
|
||||
case 'emacs':
|
||||
case 'emacsclient':
|
||||
return ['+' + lineNumber, fileName]
|
||||
case 'rmate':
|
||||
case 'mate':
|
||||
case 'mine':
|
||||
return ['--line', lineNumber, fileName]
|
||||
case 'code':
|
||||
case 'Code':
|
||||
return addWorkspaceToArgumentsIfExists(
|
||||
['-g', fileName + ':' + lineNumber],
|
||||
workspace
|
||||
)
|
||||
case 'appcode':
|
||||
case 'clion':
|
||||
case 'clion64':
|
||||
case 'idea':
|
||||
case 'idea64':
|
||||
case 'phpstorm':
|
||||
case 'phpstorm64':
|
||||
case 'pycharm':
|
||||
case 'pycharm64':
|
||||
case 'rubymine':
|
||||
case 'rubymine64':
|
||||
case 'webstorm':
|
||||
case 'webstorm64':
|
||||
return addWorkspaceToArgumentsIfExists(
|
||||
['--line', lineNumber, fileName],
|
||||
workspace
|
||||
)
|
||||
}
|
||||
|
||||
// For all others, drop the lineNumber until we have
|
||||
// a mapping above, since providing the lineNumber incorrectly
|
||||
// can result in errors or confusing behavior.
|
||||
return [fileName]
|
||||
}
|
||||
|
||||
function guessEditor () {
|
||||
// Explicit config always wins
|
||||
if (process.env.REACT_EDITOR) {
|
||||
return shellQuote.parse(process.env.REACT_EDITOR)
|
||||
}
|
||||
|
||||
// We can find out which editor is currently running by:
|
||||
// `ps x` on macOS and Linux
|
||||
// `Get-Process` on Windows
|
||||
try {
|
||||
if (process.platform === 'darwin') {
|
||||
const output = child_process.execSync('ps x').toString()
|
||||
const processNames = Object.keys(COMMON_EDITORS_OSX)
|
||||
for (let i = 0; i < processNames.length; i++) {
|
||||
const processName = processNames[i]
|
||||
if (output.indexOf(processName) !== -1) {
|
||||
return [COMMON_EDITORS_OSX[processName]]
|
||||
}
|
||||
}
|
||||
} else if (process.platform === 'win32') {
|
||||
const output = child_process
|
||||
.execSync('powershell -Command "Get-Process | Select-Object Path"', {
|
||||
stdio: ['pipe', 'pipe', 'ignore']
|
||||
})
|
||||
.toString()
|
||||
const runningProcesses = output.split('\r\n')
|
||||
for (let i = 0; i < runningProcesses.length; i++) {
|
||||
// `Get-Process` sometimes returns empty lines
|
||||
if (!runningProcesses[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
const fullProcessPath = runningProcesses[i].trim()
|
||||
const shortProcessName = path.basename(fullProcessPath)
|
||||
|
||||
if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
|
||||
return [fullProcessPath]
|
||||
}
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
// --no-heading No header line
|
||||
// x List all processes owned by you
|
||||
// -o comm Need only names column
|
||||
const output = child_process
|
||||
.execSync('ps x --no-heading -o comm --sort=comm')
|
||||
.toString()
|
||||
const processNames = Object.keys(COMMON_EDITORS_LINUX)
|
||||
for (let i = 0; i < processNames.length; i++) {
|
||||
const processName = processNames[i]
|
||||
if (output.indexOf(processName) !== -1) {
|
||||
return [COMMON_EDITORS_LINUX[processName]]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore...
|
||||
}
|
||||
|
||||
// Last resort, use old skool env vars
|
||||
if (process.env.VISUAL) {
|
||||
return [process.env.VISUAL]
|
||||
} else if (process.env.EDITOR) {
|
||||
return [process.env.EDITOR]
|
||||
}
|
||||
|
||||
return [null]
|
||||
}
|
||||
|
||||
function printInstructions (fileName, errorMessage) {
|
||||
console.log()
|
||||
console.log(
|
||||
chalk.red('Could not open ' + path.basename(fileName) + ' in the editor.')
|
||||
)
|
||||
if (errorMessage) {
|
||||
if (errorMessage[errorMessage.length - 1] !== '.') {
|
||||
errorMessage += '.'
|
||||
}
|
||||
console.log(
|
||||
chalk.red('The editor process exited with an error: ' + errorMessage)
|
||||
)
|
||||
}
|
||||
console.log()
|
||||
console.log(
|
||||
'To set up the editor integration, add something like ' +
|
||||
chalk.cyan('REACT_EDITOR=atom') +
|
||||
' to the ' +
|
||||
chalk.green('.env.local') +
|
||||
' file in your project folder ' +
|
||||
'and restart the development server. Learn more: ' +
|
||||
chalk.green('https://goo.gl/MMTaZt')
|
||||
)
|
||||
console.log()
|
||||
}
|
||||
|
||||
let _childProcess = null
|
||||
function launchEditor (fileName, lineNumber) {
|
||||
if (!fs.existsSync(fileName)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sanitize lineNumber to prevent malicious use on win32
|
||||
// via: https://github.com/nodejs/node/blob/c3bb4b1aa5e907d489619fb43d233c3336bfc03d/lib/child_process.js#L333
|
||||
if (lineNumber && isNaN(lineNumber)) {
|
||||
return
|
||||
}
|
||||
|
||||
let [editor, ...args] = guessEditor() // eslint-disable-line
|
||||
if (!editor) {
|
||||
printInstructions(fileName, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
process.platform === 'linux' &&
|
||||
fileName.startsWith('/mnt/') &&
|
||||
/Microsoft/i.test(os.release())
|
||||
) {
|
||||
// Assume WSL / "Bash on Ubuntu on Windows" is being used, and
|
||||
// that the file exists on the Windows file system.
|
||||
// `os.release()` is "4.4.0-43-Microsoft" in the current release
|
||||
// build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364
|
||||
// When a Windows editor is specified, interop functionality can
|
||||
// handle the path translation, but only if a relative path is used.
|
||||
fileName = path.relative('', fileName)
|
||||
}
|
||||
|
||||
const workspace = null
|
||||
if (lineNumber) {
|
||||
args = args.concat(
|
||||
getArgumentsForLineNumber(editor, fileName, lineNumber, workspace)
|
||||
)
|
||||
} else {
|
||||
args.push(fileName)
|
||||
}
|
||||
|
||||
if (_childProcess && isTerminalEditor(editor)) {
|
||||
// There's an existing editor process already and it's attached
|
||||
// to the terminal, so go kill it. Otherwise two separate editor
|
||||
// instances attach to the stdin/stdout which gets confusing.
|
||||
_childProcess.kill('SIGKILL')
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// On Windows, launch the editor in a shell because spawn can only
|
||||
// launch .exe files.
|
||||
_childProcess = child_process.spawn(
|
||||
'cmd.exe',
|
||||
['/C', editor].concat(args),
|
||||
{ stdio: 'inherit' }
|
||||
)
|
||||
} else {
|
||||
_childProcess = child_process.spawn(editor, args, { stdio: 'inherit' })
|
||||
}
|
||||
_childProcess.on('exit', function (errorCode) {
|
||||
_childProcess = null
|
||||
|
||||
if (errorCode) {
|
||||
printInstructions(fileName, '(code ' + errorCode + ')')
|
||||
}
|
||||
})
|
||||
|
||||
_childProcess.on('error', function (error) {
|
||||
printInstructions(fileName, error.message)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = launchEditor
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@vue/cli-overlay",
|
||||
"version": "1.0.0",
|
||||
"description": "error overlay & dev server middleware for vue-cli",
|
||||
"main": "dist/client.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"middleware"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/vue-cli.git"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"cli",
|
||||
"cli-overlay"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/vue-cli/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-overlay#readme",
|
||||
"dependencies": {
|
||||
"chalk": "^2.3.0",
|
||||
"shell-quote": "^1.6.1"
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,8 @@ module.exports = class PluginAPI {
|
||||
resolveWebpackConfig () {
|
||||
return this.service.resolveWebpackConfig()
|
||||
}
|
||||
|
||||
configureDevServer (fn) {
|
||||
this.service.devServerConfigFns.push(fn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ module.exports = class Service {
|
||||
this.webpackConfig = new Config()
|
||||
this.webpackChainFns = []
|
||||
this.webpackRawConfigFns = []
|
||||
this.devServerConfigFns = []
|
||||
this.commands = {}
|
||||
|
||||
const pkg = getPkg.sync()
|
||||
@@ -67,10 +68,7 @@ module.exports = class Service {
|
||||
return fn(args)
|
||||
}
|
||||
|
||||
resolveWebpackConfig (env) {
|
||||
if (env) {
|
||||
process.env.NODE_ENV = env
|
||||
}
|
||||
resolveWebpackConfig () {
|
||||
// apply chains
|
||||
this.webpackChainFns.forEach(fn => fn(this.webpackConfig))
|
||||
// to raw config
|
||||
|
||||
@@ -20,6 +20,8 @@ module.exports = (api, options) => {
|
||||
const portfinder = require('portfinder')
|
||||
const openBrowser = require('../util/openBrowser')
|
||||
const prepareURLs = require('../util/prepareURLs')
|
||||
const prepareProxy = require('../util/prepareProxy')
|
||||
const overlayMiddleware = require('@vue/cli-overlay/middleware')
|
||||
|
||||
const projectDevServerOptions = options.devServer || {}
|
||||
const useHttps = args.https || projectDevServerOptions.https
|
||||
@@ -41,10 +43,14 @@ module.exports = (api, options) => {
|
||||
|
||||
// inject dev/hot client
|
||||
addDevClientToEntry(webpackConfig, [
|
||||
// dev server client
|
||||
`webpack-dev-server/client/?${urls.localUrlForBrowser}`,
|
||||
// hmr client
|
||||
projectDevServerOptions.hotOnly
|
||||
? 'webpack/hot/dev-server'
|
||||
: 'webpack/hot/only-dev-server'
|
||||
: 'webpack/hot/only-dev-server',
|
||||
// custom overlay client
|
||||
`@vue/cli-overlay/dist/client`
|
||||
])
|
||||
|
||||
const compiler = webpack(webpackConfig)
|
||||
@@ -77,6 +83,11 @@ module.exports = (api, options) => {
|
||||
}
|
||||
})
|
||||
|
||||
const proxySettings = prepareProxy(
|
||||
projectDevServerOptions.proxy,
|
||||
api.resolve('public')
|
||||
)
|
||||
|
||||
const server = new WebpackDevServer(compiler, Object.assign({
|
||||
clientLogLevel: 'none',
|
||||
historyApiFallback: {
|
||||
@@ -84,16 +95,22 @@ module.exports = (api, options) => {
|
||||
},
|
||||
contentBase: api.resolve('public'),
|
||||
watchContentBase: true,
|
||||
https: useHttps,
|
||||
hot: true,
|
||||
quiet: true,
|
||||
compress: true,
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
// TODO use custom overlay w/ open-in-editor
|
||||
overlay: { warnings: false, errors: true },
|
||||
// TODO handle proxy
|
||||
proxy: {}
|
||||
}, projectDevServerOptions))
|
||||
publicPath: webpackConfig.output.publicPath
|
||||
}, projectDevServerOptions, {
|
||||
https: useHttps,
|
||||
proxy: proxySettings,
|
||||
before (app) {
|
||||
// overlay
|
||||
app.use(overlayMiddleware())
|
||||
// allow other plugins to register middlewares, e.g. PWA
|
||||
api.service.devServerConfigFns.forEach(fn => fn(app))
|
||||
// apply in project middlewares
|
||||
projectDevServerOptions.before && projectDevServerOptions.before(app)
|
||||
}
|
||||
}))
|
||||
|
||||
server.listen(port, host, err => {
|
||||
if (err) {
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file at
|
||||
* https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const url = require('url')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const address = require('address')
|
||||
|
||||
module.exports = function prepareProxy (proxy, appPublicFolder) {
|
||||
// `proxy` lets you specify alternate servers for specific requests.
|
||||
// It can either be a string or an object conforming to the Webpack dev server proxy configuration
|
||||
// https://webpack.github.io/docs/webpack-dev-server.html
|
||||
if (!proxy) {
|
||||
return undefined
|
||||
}
|
||||
if (typeof proxy !== 'object' && typeof proxy !== 'string') {
|
||||
console.log(
|
||||
chalk.red(
|
||||
'When specified, "proxy" in package.json must be a string or an object.'
|
||||
)
|
||||
)
|
||||
console.log(
|
||||
chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
|
||||
)
|
||||
console.log(
|
||||
chalk.red(
|
||||
'Either remove "proxy" from package.json, or make it an object.'
|
||||
)
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Otherwise, if proxy is specified, we will let it handle any request except for files in the public folder.
|
||||
function mayProxy (pathname) {
|
||||
const maybePublicPath = path.resolve(appPublicFolder, pathname.slice(1))
|
||||
return !fs.existsSync(maybePublicPath)
|
||||
}
|
||||
|
||||
// Support proxy as a string for those who are using the simple proxy option
|
||||
if (typeof proxy === 'string') {
|
||||
if (!/^http(s)?:\/\//.test(proxy)) {
|
||||
console.log(
|
||||
chalk.red(
|
||||
'When "proxy" is specified in package.json it must start with either http:// or https://'
|
||||
)
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let target
|
||||
if (process.platform === 'win32') {
|
||||
target = resolveLoopback(proxy)
|
||||
} else {
|
||||
target = proxy
|
||||
}
|
||||
return [
|
||||
{
|
||||
target,
|
||||
logLevel: 'silent',
|
||||
// For single page apps, we generally want to fallback to /index.html.
|
||||
// However we also want to respect `proxy` for API calls.
|
||||
// So if `proxy` is specified as a string, we need to decide which fallback to use.
|
||||
// We use a heuristic: if request `accept`s text/html, we pick /index.html.
|
||||
// Modern browsers include text/html into `accept` header when navigating.
|
||||
// However API calls like `fetch()` won’t generally accept text/html.
|
||||
// If this heuristic doesn’t work well for you, use a custom `proxy` object.
|
||||
context: function (pathname, req) {
|
||||
return (
|
||||
mayProxy(pathname) &&
|
||||
req.headers.accept &&
|
||||
req.headers.accept.indexOf('text/html') === -1
|
||||
)
|
||||
},
|
||||
onProxyReq: proxyReq => {
|
||||
// Browers may send Origin headers even with same-origin
|
||||
// requests. To prevent CORS issues, we have to change
|
||||
// the Origin to match the target URL.
|
||||
if (proxyReq.getHeader('origin')) {
|
||||
proxyReq.setHeader('origin', target)
|
||||
}
|
||||
},
|
||||
onError: onProxyError(target),
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
xfwd: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Otherwise, proxy is an object so create an array of proxies to pass to webpackDevServer
|
||||
return Object.keys(proxy).map(function (context) {
|
||||
if (!proxy[context].hasOwnProperty('target')) {
|
||||
console.log(
|
||||
chalk.red(
|
||||
'When `proxy` in package.json is as an object, each `context` object must have a ' +
|
||||
'`target` property specified as a url string'
|
||||
)
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
let target
|
||||
if (process.platform === 'win32') {
|
||||
target = resolveLoopback(proxy[context].target)
|
||||
} else {
|
||||
target = proxy[context].target
|
||||
}
|
||||
return Object.assign({}, proxy[context], {
|
||||
context: function (pathname) {
|
||||
return mayProxy(pathname) && pathname.match(context)
|
||||
},
|
||||
onProxyReq: proxyReq => {
|
||||
// Browers may send Origin headers even with same-origin
|
||||
// requests. To prevent CORS issues, we have to change
|
||||
// the Origin to match the target URL.
|
||||
if (proxyReq.getHeader('origin')) {
|
||||
proxyReq.setHeader('origin', target)
|
||||
}
|
||||
},
|
||||
target,
|
||||
onError: onProxyError(target)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function resolveLoopback (proxy) {
|
||||
const o = url.parse(proxy)
|
||||
o.host = undefined
|
||||
if (o.hostname !== 'localhost') {
|
||||
return proxy
|
||||
}
|
||||
// Unfortunately, many languages (unlike node) do not yet support IPv6.
|
||||
// This means even though localhost resolves to ::1, the application
|
||||
// must fall back to IPv4 (on 127.0.0.1).
|
||||
// We can re-enable this in a few years.
|
||||
/* try {
|
||||
o.hostname = address.ipv6() ? '::1' : '127.0.0.1';
|
||||
} catch (_ignored) {
|
||||
o.hostname = '127.0.0.1';
|
||||
}*/
|
||||
|
||||
try {
|
||||
// Check if we're on a network; if we are, chances are we can resolve
|
||||
// localhost. Otherwise, we can just be safe and assume localhost is
|
||||
// IPv4 for maximum compatibility.
|
||||
if (!address.ip()) {
|
||||
o.hostname = '127.0.0.1'
|
||||
}
|
||||
} catch (_ignored) {
|
||||
o.hostname = '127.0.0.1'
|
||||
}
|
||||
return url.format(o)
|
||||
}
|
||||
|
||||
// We need to provide a custom onError function for httpProxyMiddleware.
|
||||
// It allows us to log custom error messages on the console.
|
||||
function onProxyError (proxy) {
|
||||
return (err, req, res) => {
|
||||
const host = req.headers && req.headers.host
|
||||
console.log(
|
||||
chalk.red('Proxy error:') +
|
||||
' Could not proxy request ' +
|
||||
chalk.cyan(req.url) +
|
||||
' from ' +
|
||||
chalk.cyan(host) +
|
||||
' to ' +
|
||||
chalk.cyan(proxy) +
|
||||
'.'
|
||||
)
|
||||
console.log(
|
||||
'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
|
||||
chalk.cyan(err.code) +
|
||||
').'
|
||||
)
|
||||
console.log()
|
||||
|
||||
// And immediately send the proper error response to the client.
|
||||
// Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
|
||||
if (res.writeHead && !res.headersSent) {
|
||||
res.writeHead(500)
|
||||
}
|
||||
res.end(
|
||||
'Proxy error: Could not proxy request ' +
|
||||
req.url +
|
||||
' from ' +
|
||||
host +
|
||||
' to ' +
|
||||
proxy +
|
||||
' (' +
|
||||
err.code +
|
||||
').'
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-service#readme",
|
||||
"dependencies": {
|
||||
"@vue/cli-overlay": "^1.0.0",
|
||||
"address": "^1.0.3",
|
||||
"chalk": "^2.3.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
|
||||
@@ -192,6 +192,10 @@ array-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
||||
|
||||
array-filter@~0.0.0:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
|
||||
|
||||
array-find-index@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
|
||||
@@ -215,6 +219,14 @@ array-includes@^3.0.3:
|
||||
define-properties "^1.1.2"
|
||||
es-abstract "^1.7.0"
|
||||
|
||||
array-map@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
|
||||
|
||||
array-reduce@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
|
||||
|
||||
array-union@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
|
||||
@@ -6220,6 +6232,15 @@ shebang-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
|
||||
shell-quote@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
|
||||
dependencies:
|
||||
array-filter "~0.0.0"
|
||||
array-map "~0.0.0"
|
||||
array-reduce "~0.0.0"
|
||||
jsonify "~0.0.0"
|
||||
|
||||
shellwords@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
@@ -7032,7 +7053,7 @@ vue-template-es2015-compiler@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||
|
||||
vue-test-utils@^1.0.0:
|
||||
vue-test-utils@^1.0.0-beta.9:
|
||||
version "1.0.0-beta.9"
|
||||
resolved "https://registry.yarnpkg.com/vue-test-utils/-/vue-test-utils-1.0.0-beta.9.tgz#bb67c01e2386f85c3ffbceae460b6e785eb7f81a"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user