parent
9c2b9020e7
commit
b3f73a0fae
|
@ -3,26 +3,21 @@
|
||||||
// Hover to view descriptions of existing attributes.
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
{
|
{
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Run Extension",
|
"name": "Run Extension",
|
||||||
"type": "extensionHost",
|
"type": "extensionHost",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": [
|
"args": ["--extensionDevelopmentPath=${workspaceFolder}"]
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
},
|
||||||
]
|
{
|
||||||
},
|
"name": "Extension Tests",
|
||||||
{
|
"type": "extensionHost",
|
||||||
"name": "Extension Tests",
|
"request": "launch",
|
||||||
"type": "extensionHost",
|
"runtimeExecutable": "${execPath}",
|
||||||
"request": "launch",
|
"args": ["--extensionTestsPath=${workspaceFolder}"]
|
||||||
"runtimeExecutable": "${execPath}",
|
}
|
||||||
"args": [
|
]
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
|
||||||
"--extensionTestsPath=${workspaceFolder}/test/suite/index"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
.vscode/**
|
.vscode/**
|
||||||
.vscode-test/**
|
|
||||||
node_modules/**
|
node_modules/**
|
||||||
test/**
|
test/*
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.travis.yml
|
|
||||||
.gitignore
|
.gitignore
|
||||||
index.js
|
index.js
|
||||||
test.js
|
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,14 @@
|
||||||
> 这插件只是因为`Live Server插件`有bug,应一个基友需求临时写的。如果你有其他的功能需求, 请在插件商店中`搜索其他插件`。
|
> 这插件只是因为`Live Server插件`有bug,应一个基友需求临时写的。如果你有其他的功能需求, 请在插件商店中`搜索其他插件`。
|
||||||
|
|
||||||
|
|
||||||
|
## 插件特色
|
||||||
|
- 支持指定子目录为根目录
|
||||||
|
- 支持前端`history`路由
|
||||||
|
- 自动指定端口(端口不可用时自动寻找其他可用端口)
|
||||||
|
- 支持注入`headers`,满足某些测试场景下的测试需求
|
||||||
|
- 支持简单的热更新
|
||||||
|
|
||||||
|
|
||||||
## 插件使用
|
## 插件使用
|
||||||
> 在项目根目录中放置 `.httpserver`文件, 并配置好之后, 重启或重新加载窗口, 以启动服务。
|
> 在项目根目录中放置 `.httpserver`文件, 并配置好之后, 重启或重新加载窗口, 以启动服务。
|
||||||
|
|
||||||
|
|
104
index.js
104
index.js
|
@ -9,6 +9,7 @@ const { join } = require('path')
|
||||||
const { parse } = require('url')
|
const { parse } = require('url')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const fs = require('iofs')
|
const fs = require('iofs')
|
||||||
|
const { WebSocketServer } = require('ws')
|
||||||
|
|
||||||
const std = vsc.window.createOutputChannel('http.server')
|
const std = vsc.window.createOutputChannel('http.server')
|
||||||
const decode = decodeURIComponent
|
const decode = decodeURIComponent
|
||||||
|
@ -56,9 +57,60 @@ let root
|
||||||
let enabled = false
|
let enabled = false
|
||||||
let port = 23333
|
let port = 23333
|
||||||
let baseUrl = 'http://127.0.0.1:' + port
|
let baseUrl = 'http://127.0.0.1:' + port
|
||||||
|
let ws = null
|
||||||
|
|
||||||
std.out = function (...args) {
|
std.out = function (...args) {
|
||||||
std.appendLine('[simple.http]: ' + args.join(' '))
|
std.appendLine('[simple.http]: ' + args.join(' '))
|
||||||
|
// console.log('[simple.http]: ' + args.join(' '))
|
||||||
|
}
|
||||||
|
|
||||||
|
const HMR_SCRIPT = `
|
||||||
|
<script>
|
||||||
|
!(function http_hmr(){
|
||||||
|
var ws = new WebSocket(\`ws://\${location.host}/ws-http-hmr\`)
|
||||||
|
ws.addEventListener('open', function (r) {
|
||||||
|
if(http_hmr.closed){
|
||||||
|
delete http_hmr.closed
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
console.log('http.server hmr ready...')
|
||||||
|
})
|
||||||
|
ws.addEventListener('close', function(){
|
||||||
|
http_hmr.closed = true
|
||||||
|
setTimeout(http_hmr, 2000)
|
||||||
|
})
|
||||||
|
|
||||||
|
ws.addEventListener('message', function (ev) {
|
||||||
|
location.reload()
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
|
`
|
||||||
|
|
||||||
|
class WebSocket {
|
||||||
|
#ws = null // ws实例
|
||||||
|
#queue = [] // 消息队列
|
||||||
|
|
||||||
|
constructor(server) {
|
||||||
|
if (server.listening) {
|
||||||
|
let conn = new WebSocketServer({ server, path: '/ws-http-hmr' })
|
||||||
|
conn.on('connection', ws => {
|
||||||
|
this.#ws = ws
|
||||||
|
if (this.#queue.length) {
|
||||||
|
this.#queue = []
|
||||||
|
this.send()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send(msg = {}) {
|
||||||
|
if (this.#ws) {
|
||||||
|
this.#ws.send(JSON.stringify(msg))
|
||||||
|
} else {
|
||||||
|
this.#queue.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServer() {
|
function createServer() {
|
||||||
|
@ -68,7 +120,7 @@ function createServer() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
std.out(`尝试使用${port}端口...`)
|
std.out(`尝试使用${port}端口...`)
|
||||||
http
|
const server = http
|
||||||
.createServer(function (req, res) {
|
.createServer(function (req, res) {
|
||||||
let pathname = parse(req.url).pathname.slice(1)
|
let pathname = parse(req.url).pathname.slice(1)
|
||||||
let paths = pathname.split('/')
|
let paths = pathname.split('/')
|
||||||
|
@ -82,6 +134,7 @@ function createServer() {
|
||||||
let file = join(root, pathname)
|
let file = join(root, pathname)
|
||||||
let stat = fs.stat(file)
|
let stat = fs.stat(file)
|
||||||
let ext = pathname.split('.').pop()
|
let ext = pathname.split('.').pop()
|
||||||
|
let code
|
||||||
|
|
||||||
for (let k in COMMON_HEADERS) {
|
for (let k in COMMON_HEADERS) {
|
||||||
res.setHeader(k, COMMON_HEADERS[k])
|
res.setHeader(k, COMMON_HEADERS[k])
|
||||||
|
@ -90,18 +143,40 @@ function createServer() {
|
||||||
if (stat.isFile()) {
|
if (stat.isFile()) {
|
||||||
res.setHeader('accept-ranges', 'bytes')
|
res.setHeader('accept-ranges', 'bytes')
|
||||||
res.setHeader('content-type', MIME_TYPES[ext] || MIME_TYPES.other)
|
res.setHeader('content-type', MIME_TYPES[ext] || MIME_TYPES.other)
|
||||||
res.setHeader('content-length', stat.size)
|
|
||||||
|
if (ext === 'html') {
|
||||||
|
code = fs
|
||||||
|
.cat(file)
|
||||||
|
.toString()
|
||||||
|
.replace('</head>', HMR_SCRIPT + '</head>')
|
||||||
|
|
||||||
|
res.setHeader('content-length', code.length)
|
||||||
|
} else {
|
||||||
|
res.setHeader('content-length', stat.size)
|
||||||
|
}
|
||||||
|
|
||||||
res.writeHead(200, 'OK')
|
res.writeHead(200, 'OK')
|
||||||
fs.origin.createReadStream(file).pipe(res)
|
|
||||||
|
if (ext === 'html') {
|
||||||
|
res.end(code)
|
||||||
|
} else {
|
||||||
|
fs.origin.createReadStream(file).pipe(res)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (paths.length > 1 && paths[0].endsWith('.html')) {
|
if (paths.length > 1 && paths[0].endsWith('.html')) {
|
||||||
let rootFile = join(root, paths[0])
|
let rootFile = join(root, paths[0])
|
||||||
if (fs.isfile(rootFile)) {
|
if (fs.isfile(rootFile)) {
|
||||||
|
code = fs
|
||||||
|
.cat(rootFile)
|
||||||
|
.toString()
|
||||||
|
.replace('</head>', HMR_SCRIPT + '</head>')
|
||||||
|
|
||||||
res.setHeader('accept-ranges', 'bytes')
|
res.setHeader('accept-ranges', 'bytes')
|
||||||
res.setHeader('content-type', MIME_TYPES.html)
|
res.setHeader('content-type', MIME_TYPES.html)
|
||||||
res.setHeader('content-length', fs.stat(rootFile).size)
|
res.setHeader('content-length', code.length)
|
||||||
res.writeHead(200, 'OK')
|
res.writeHead(200, 'OK')
|
||||||
fs.origin.createReadStream(rootFile).pipe(res)
|
|
||||||
|
res.end(code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,10 +192,12 @@ function createServer() {
|
||||||
port++
|
port++
|
||||||
createServer()
|
createServer()
|
||||||
})
|
})
|
||||||
.on('listening', _ => {
|
|
||||||
baseUrl = 'http://127.0.0.1:' + port
|
server.on('listening', _ => {
|
||||||
std.out('启动成功, 请访问', baseUrl)
|
baseUrl = 'http://127.0.0.1:' + port
|
||||||
})
|
ws = new WebSocket(server)
|
||||||
|
std.out('启动成功, 请访问', baseUrl)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function __init__() {
|
function __init__() {
|
||||||
|
@ -166,6 +243,15 @@ function deactivate() {}
|
||||||
exports.activate = function (ctx) {
|
exports.activate = function (ctx) {
|
||||||
__init__()
|
__init__()
|
||||||
|
|
||||||
|
vsc.workspace.onDidSaveTextDocument(doc => {
|
||||||
|
if (enabled) {
|
||||||
|
let file = doc.fileName
|
||||||
|
if (file.startsWith(root)) {
|
||||||
|
ws.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
let cmd = vsc.commands.registerCommand('HttpServer.open', _ => {
|
let cmd = vsc.commands.registerCommand('HttpServer.open', _ => {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
let editor = vsc.window.activeTextEditor
|
let editor = vsc.window.activeTextEditor
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "simple-http",
|
"name": "simple-http",
|
||||||
"displayName": "simple http",
|
"displayName": "simple http",
|
||||||
"description": "🔥 简单的http服务器, 方便临时调试html",
|
"description": "🔥 简单的http服务器, 方便临时调试html",
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"publisher": "yutent",
|
"publisher": "yutent",
|
||||||
"author": "Yutent [@yutent]",
|
"author": "Yutent [@yutent]",
|
||||||
"icon": "logo.png",
|
"icon": "logo.png",
|
||||||
|
@ -44,8 +44,9 @@
|
||||||
"yutent"
|
"yutent"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "esbuild index.js --bundle --outfile=out.js --external:vscode --format=cjs --platform=node",
|
"bundle": "esbuild index.js --bundle --outfile=out.js --external:vscode --format=cjs --platform=node",
|
||||||
"pack": "npm start -- --minify"
|
"start": "npm run bundle -- --watch",
|
||||||
|
"pack": "npm run bundle -- --minify"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"enabled": true
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||||
|
<title></title>
|
||||||
|
<meta name="keywords" content="">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<link href="" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
sdsdsdsdsssdsdsd
|
||||||
|
dsdsd
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue