完善hmr

pull/1/head
yutent 2023-02-01 10:51:33 +08:00
parent 100e000609
commit 98e7fb6864
5 changed files with 85 additions and 32 deletions

View File

@ -7,8 +7,15 @@
import fs from 'iofs' import fs from 'iofs'
import scss from '@bytedo/sass' import scss from '@bytedo/sass'
import { createHash } from 'crypto' import { createHash } from 'crypto'
import Es from 'esbuild'
import { JS_EXP, STYLE_EXP, HTML_EXP, CSS_SHEET_EXP } from './constants.js' import {
JS_EXP,
STYLE_EXP,
HTML_EXP,
CSS_SHEET_EXP,
HMR_SCRIPT
} from './constants.js'
const OPTIONS = { const OPTIONS = {
indentType: 'space', indentType: 'space',
@ -227,7 +234,11 @@ export function parseHtml(html, { page, imports, entry }, isBuild = false) {
` <script>window.process = {env: {NODE_ENV: '${ ` <script>window.process = {env: {NODE_ENV: '${
isBuild ? 'production' : 'development' isBuild ? 'production' : 'development'
}'}}</script>\n${ }'}}</script>\n${
isBuild ? '' : ' <script src="/assets/ws.js"></script>' isBuild
? ''
: ` <script>${
Es.transformSync(HMR_SCRIPT, { minify: true }).code
}</script>`
}</head>` }</head>`
) )
.replace('{{title}}', page.title || '') .replace('{{title}}', page.title || '')

View File

@ -14,3 +14,38 @@ export const CSS_SHEET_EXP =
export const COMMON_HEADERS = { export const COMMON_HEADERS = {
'Cache-Control': 'no-store' 'Cache-Control': 'no-store'
} }
export const HMR_SCRIPT = `
!(function(){
var ws = new WebSocket(\`ws://\${location.host}/ws-vue-live\`)
ws.addEventListener('open', function (r) {
console.log('vue-live hmr ready...')
})
ws.addEventListener('message', function (ev) {
var { action, data } = JSON.parse(ev.data)
switch (action) {
case 'reload':
location.reload()
break
case 'render':
{
let tmp = [...document.adoptedStyleSheets]
for (let i = -1, it; (it = tmp[++i]); ) {
if (it.path === data.path) {
let stylesheet = new CSSStyleSheet()
stylesheet.path = data.path
stylesheet.replaceSync(data.content)
document.adoptedStyleSheets[i] = stylesheet
break
}
}
}
break
}
})
})()
`

View File

@ -5,6 +5,7 @@ import { join, resolve, dirname } from 'path'
import { parse } from 'url' import { parse } from 'url'
import socket from './ws.js' import socket from './ws.js'
import chokidar from 'chokidar' import chokidar from 'chokidar'
import { red, cyan, blue } from 'kolorist'
import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js'
@ -134,10 +135,16 @@ export default async function createServer(root = '', conf = {}) {
file = join(root, rpath) file = join(root, rpath)
} }
if (!fs.isfile(file)) { if (!fs.isfile(file)) {
file = file.replace(/\.vue$/, '/index.vue') console.clear()
console.log(cyan(rpath), red(`not found!!!`))
console.log(
red(
'请正确填写引入的文件路径, vue-live 不再自动补全【%s】\n'
),
blue('/index.vue')
)
} }
// console.log('>>>>', file)
code = compileVue(file, conf.imports, { code = compileVue(file, conf.imports, {
IS_MPA, IS_MPA,
currentPage, currentPage,
@ -176,8 +183,14 @@ export default async function createServer(root = '', conf = {}) {
if (fs.isfile(file)) { if (fs.isfile(file)) {
code = fs.cat(file) code = fs.cat(file)
} else { } else {
file = file.replace(/\.js$/, '/index.js') console.clear()
code = fs.cat(file) console.log(cyan(pathname), red(`not found!!!`))
console.log(
red(
'请正确填写引入的文件路径, vue-live 不再自动补全【%s】\n'
),
blue('/index.js')
)
} }
code = parseJs(code + '', conf.imports, { IS_MPA, currentPage }) code = parseJs(code + '', conf.imports, { IS_MPA, currentPage })
res.setHeader('content-type', MIME_TYPES.js) res.setHeader('content-type', MIME_TYPES.js)
@ -215,7 +228,10 @@ export default async function createServer(root = '', conf = {}) {
}) })
.on('error', err => { .on('error', err => {
console.log(`${PORT}端口被占用~~~`) console.log(
red(`${PORT} 端口被占用!!! 尝试使用 ${PORT + 1} 端口...`),
'\n'
)
conf.devServer.port = PORT + 1 conf.devServer.port = PORT + 1
createServer(root, conf) createServer(root, conf)
}) })
@ -255,7 +271,6 @@ export default async function createServer(root = '', conf = {}) {
case 'vue': case 'vue':
{ {
// console.log('>>>>', file)
let content = compileVue(filePath, conf.imports, { let content = compileVue(filePath, conf.imports, {
IS_MPA, IS_MPA,
currentPage, currentPage,
@ -264,9 +279,7 @@ export default async function createServer(root = '', conf = {}) {
CACHE CACHE
}) })
let tmp = CACHE[filePath] let tmp = CACHE[filePath]
// console.log(tmp);
if (tmp.changed) { if (tmp.changed) {
console.log('需要刷新了')
ws.send({ action: 'reload' }) ws.send({ action: 'reload' })
} else { } else {
ws.send({ ws.send({

View File

@ -1,25 +1,17 @@
/** /**
* 一个简单到极致WebSocket * 一个简单到发指WebSocket
* @author yutent<yutent.io@gmail.com> * @author yutent<yutent.io@gmail.com>
* @date 2023/01/17 17:32:51 * @date 2023/01/17 17:32:51
*/ */
import { WebSocketServer } from 'ws' import { WebSocketServer } from 'ws'
function defer() {
var o = {}
o.promise = new Promise((resolve, reject) => {
o.resolve = resolve
o.reject = reject
})
return o
}
class WebSocket { class WebSocket {
#ws = null // ws实例 #ws = null // ws实例
#queue = [] // 消息队列 #queue = [] // 消息队列
constructor(server) { constructor(server) {
var conn = new WebSocketServer({ server, path: '/ws-vue-live' }) if (server.listening) {
let conn = new WebSocketServer({ server, path: '/ws-vue-live' })
conn.on('connection', ws => { conn.on('connection', ws => {
this.#ws = ws this.#ws = ws
// ws.on('message', data => { // ws.on('message', data => {
@ -31,6 +23,7 @@ class WebSocket {
} }
}) })
} }
}
send(msg = {}) { send(msg = {}) {
if (this.#ws) { if (this.#ws) {

View File

@ -1,7 +1,7 @@
{ {
"name": "@bytedo/vue-live", "name": "@bytedo/vue-live",
"type": "module", "type": "module",
"version": "0.1.0", "version": "0.1.1",
"bin": { "bin": {
"vue-live": "index.js" "vue-live": "index.js"
}, },
@ -10,6 +10,7 @@
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"esbuild": "^0.15.13", "esbuild": "^0.15.13",
"iofs": "^1.5.2", "iofs": "^1.5.2",
"kolorist": "^1.6.0",
"ws": "^8.12.0" "ws": "^8.12.0"
} }
} }