From b3f73a0fae67c189ffa480af3bd01987e8cc5d7c Mon Sep 17 00:00:00 2001 From: yutent Date: Mon, 17 Apr 2023 11:33:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0HMR=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 39 ++++++++--------- .vscodeignore | 5 +-- README.md | 8 ++++ index.js | 104 ++++++++++++++++++++++++++++++++++++++++---- package.json | 7 +-- test/.httpserver | 3 ++ test/index.html | 16 +++++++ 7 files changed, 144 insertions(+), 38 deletions(-) create mode 100644 test/.httpserver create mode 100644 test/index.html diff --git a/.vscode/launch.json b/.vscode/launch.json index 6899e23..c5f594e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,26 +3,21 @@ // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { - "version": "0.2.0", - "configurations": [ - { - "name": "Run Extension", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ] - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/test/suite/index" - ] - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"] + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionTestsPath=${workspaceFolder}"] + } + ] } diff --git a/.vscodeignore b/.vscodeignore index 1bd2951..cef9f8c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,10 +1,7 @@ .vscode/** -.vscode-test/** node_modules/** -test/** +test/* package-lock.json -.travis.yml .gitignore index.js -test.js diff --git a/README.md b/README.md index 9da64c1..7153955 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,14 @@ > 这插件只是因为`Live Server插件`有bug,应一个基友需求临时写的。如果你有其他的功能需求, 请在插件商店中`搜索其他插件`。 +## 插件特色 +- 支持指定子目录为根目录 +- 支持前端`history`路由 +- 自动指定端口(端口不可用时自动寻找其他可用端口) +- 支持注入`headers`,满足某些测试场景下的测试需求 +- 支持简单的热更新 + + ## 插件使用 > 在项目根目录中放置 `.httpserver`文件, 并配置好之后, 重启或重新加载窗口, 以启动服务。 diff --git a/index.js b/index.js index cb63908..5cfa439 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ const { join } = require('path') const { parse } = require('url') const http = require('http') const fs = require('iofs') +const { WebSocketServer } = require('ws') const std = vsc.window.createOutputChannel('http.server') const decode = decodeURIComponent @@ -56,9 +57,60 @@ let root let enabled = false let port = 23333 let baseUrl = 'http://127.0.0.1:' + port +let ws = null std.out = function (...args) { std.appendLine('[simple.http]: ' + args.join(' ')) + // console.log('[simple.http]: ' + args.join(' ')) +} + +const HMR_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() { @@ -68,7 +120,7 @@ function createServer() { return } std.out(`尝试使用${port}端口...`) - http + const server = http .createServer(function (req, res) { let pathname = parse(req.url).pathname.slice(1) let paths = pathname.split('/') @@ -82,6 +134,7 @@ function createServer() { let file = join(root, pathname) let stat = fs.stat(file) let ext = pathname.split('.').pop() + let code for (let k in COMMON_HEADERS) { res.setHeader(k, COMMON_HEADERS[k]) @@ -90,18 +143,40 @@ function createServer() { if (stat.isFile()) { res.setHeader('accept-ranges', 'bytes') 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('', HMR_SCRIPT + '') + + res.setHeader('content-length', code.length) + } else { + res.setHeader('content-length', stat.size) + } + res.writeHead(200, 'OK') - fs.origin.createReadStream(file).pipe(res) + + if (ext === 'html') { + res.end(code) + } else { + fs.origin.createReadStream(file).pipe(res) + } } else { if (paths.length > 1 && paths[0].endsWith('.html')) { let rootFile = join(root, paths[0]) if (fs.isfile(rootFile)) { + code = fs + .cat(rootFile) + .toString() + .replace('', HMR_SCRIPT + '') + res.setHeader('accept-ranges', 'bytes') 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') - fs.origin.createReadStream(rootFile).pipe(res) + + res.end(code) return } } @@ -117,10 +192,12 @@ function createServer() { port++ createServer() }) - .on('listening', _ => { - baseUrl = 'http://127.0.0.1:' + port - std.out('启动成功, 请访问', baseUrl) - }) + + server.on('listening', _ => { + baseUrl = 'http://127.0.0.1:' + port + ws = new WebSocket(server) + std.out('启动成功, 请访问', baseUrl) + }) } function __init__() { @@ -166,6 +243,15 @@ function deactivate() {} exports.activate = function (ctx) { __init__() + vsc.workspace.onDidSaveTextDocument(doc => { + if (enabled) { + let file = doc.fileName + if (file.startsWith(root)) { + ws.send() + } + } + }) + let cmd = vsc.commands.registerCommand('HttpServer.open', _ => { if (enabled) { let editor = vsc.window.activeTextEditor diff --git a/package.json b/package.json index 4e9e8c3..4b65422 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "simple-http", "displayName": "simple http", "description": "🔥 简单的http服务器, 方便临时调试html", - "version": "1.4.0", + "version": "1.5.0", "publisher": "yutent", "author": "Yutent [@yutent]", "icon": "logo.png", @@ -44,8 +44,9 @@ "yutent" ], "scripts": { - "start": "esbuild index.js --bundle --outfile=out.js --external:vscode --format=cjs --platform=node", - "pack": "npm start -- --minify" + "bundle": "esbuild index.js --bundle --outfile=out.js --external:vscode --format=cjs --platform=node", + "start": "npm run bundle -- --watch", + "pack": "npm run bundle -- --minify" }, "license": "MIT", "dependencies": {}, diff --git a/test/.httpserver b/test/.httpserver new file mode 100644 index 0000000..37c8cef --- /dev/null +++ b/test/.httpserver @@ -0,0 +1,3 @@ +{ + "enabled": true +} \ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..cbe627d --- /dev/null +++ b/test/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + sdsdsdsdsssdsdsd + dsdsd + + \ No newline at end of file