zoro-cli
Version:
https://github.com/vuejs/vue-cli
217 lines (203 loc) • 6.6 kB
JavaScript
const axios = require('axios')
const internalIp = require('internal-ip')
const chalk = require('chalk')
const { info, warn, error } = require('zoro-cli-util/logger')
const path = require('path')
const url = require('url')
const { ajax: ajaxMap } = require('./data')
const pkg = require('../package.json')
const { readPortSync } = require('../script/port')
const options = require('../project.config')
// 代理项目的 .js/.css 的文件到本地服务器
const regFile = new RegExp(
`(fonts|img|dist|www|(${pkg.name}\\/\\d+\\.\\d+\\.\\d+))`
)
const regJsCss = /\.(css|js)(\?.*)?$/
const regImg = /\.(png|jpe?g|gif|webp)(\?.*)?$/
const regMedia = /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/
const regFont = /\.(woff2?|eot|ttf|otf)(\?.*)?$/i
const ip = internalIp.v4.sync()
// port
// port = pkg.scripts.dev.match(/\d{4,}/)
// port = port ? port[0] : 8000
const port = readPortSync()
info(`local server at port ${port}`)
info()
module.exports = {
summary: 'a rule to hack response',
*beforeSendRequest(requestDetail) {
// see http://anyproxy.io/cn/#beforesendrequest
const { requestOptions } = requestDetail
const urlInfo = url.parse(requestOptions.path)
const pathname = urlInfo.pathname
// referer info
const { Referer } = requestOptions.headers
let refererInfo
let refererHostname
if (Referer) {
refererInfo = url.parse(Referer)
refererHostname = refererInfo.hostname
}
let body
let header = {}
// mock
const mockData = ajaxMap[pathname]
if (mockData) {
const { filepath, mockResponse } = mockData
// 请求代理
try {
if (typeof mockResponse === 'function') {
body = mockResponse(requestOptions)
} else {
body = mockResponse
}
body = JSON.stringify(body)
info(`delegate ${pathname} to ${filepath}`)
info()
} catch (err) {
error(chalk.yellow(`failed to delegate ${pathname} to ${filepath}`))
}
}
if (
refererHostname !== ip &&
refererHostname !== 'localhost' &&
refererHostname !== '127.0.0.1'
) {
// console.log(refererInfo)
// hmr
const hmrMatch = urlInfo.path.match(
/\/(info|xhr_streaming|eventsource|xhr)(\?t=\d*?)?$/
)
if (hmrMatch) {
// hmr 代理
urlInfo.protocol = 'http'
urlInfo.hostname = ip
urlInfo.port = port
urlInfo.pathname = '/sockjs-node/info'
const newUrl = url.format(urlInfo)
yield axios
.get(newUrl)
.then(obj => {
info(`delegate ${pathname} to ${newUrl}`)
info()
// just let the request fail, do not support hmr now
body = obj.data
// body = JSON.stringify(obj.data)
header = {
...header,
'Access-Control-Allow-Credentials': true,
'Content-Type': 'application/json; charset=UTF-8',
}
if (refererInfo) {
header['Access-Control-Allow-Origin'] = `${
refererInfo.protocol
}//${refererInfo.host}`
}
})
.catch(err => {
warn(`failed to delegate ${pathname} to ${newUrl}`)
error(err.toString())
})
} else if (
regFile.test(pathname) &&
(regJsCss.test(pathname) ||
regImg.test(pathname) ||
regMedia.test(pathname) ||
regFont.test(pathname))
) {
// 文件代理
const pathInfo = path.parse(pathname)
const ext = pathInfo.ext
let filename = pathInfo.base
// console.log(filename)
// 处理 render 路径
if (pathname.indexOf('www') !== -1) {
const reg = /([\w-]+?)-(\w+?)\./
filename = filename.replace(reg, '$1.')
}
// 处理资源文件夹
let folder = ''
if (regImg.test(pathname)) {
folder = options.outputImgDir || 'img'
} else if (regMedia.test(pathname)) {
folder = options.outputMediaDir || 'media'
} else if (regFont.test(pathname)) {
folder = options.outputFontsDir || 'fonts'
}
// 拼接本地文件地址
let newUrl = ''
if (folder) {
newUrl = `http://${ip}:${port}/${folder}/${filename}`
} else {
newUrl = `http://${ip}:${port}/${filename}`
}
yield axios
.get(newUrl, {
// files are received as binary data
responseType: 'arraybuffer',
})
.then(obj => {
info(`delegate ${pathname} to ${newUrl}`)
info()
body = obj.data
// CORS
if (regFont.test(pathname)) {
header = {
...header,
// ...obj.headers,
'Transfer-Encoding': 'chunked',
'Access-Control-Allow-Origin': '*',
'Content-Type': `font/${ext.slice(1)}`,
}
}
})
.catch(err => {
warn(`failed to delegate ${pathname} to ${newUrl}`)
error(err.toString())
// dev 环境不 extract js, 所以将 js 文件映射为空文件
if (ext === '.js') {
body = 'var anyproxy_js_empty = true;'
header = {
'Content-Type': 'application/javascript; charset=utf-8',
...header,
}
}
// dev 环境不 extract css, 所以将 css 文件映射为空文件
if (ext === '.css') {
body = '.anyproxy_css_empty{--empty:true;}'
header = {
'Content-Type': 'text/css',
...header,
}
}
})
}
}
if (body) {
requestDetail.response = {
statusCode: 200,
header,
body,
}
}
return requestDetail
},
*beforeSendResponse(requestDetail, responseDetail) {
// see http://anyproxy.io/cn/#beforesendresponse
if (requestDetail.url === 'http://httpbin.org/user-agent') {
const newResponse = responseDetail.response
newResponse.body += '- AnyProxy Hacked!'
yield new Promise(resolve => {
setTimeout(() => {
// delay
resolve({ response: newResponse })
}, 5000)
})
}
},
/* eslint-disable-next-line no-unused-vars */
beforeDealHttpsRequest(requestDetail) {
// see http://anyproxy.io/cn/#beforedealhttpsrequest
return Promise.resolve(true)
},
}