Skip to content

Commit

Permalink
feat: allow plugins to send custom hmr events
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 29, 2020
1 parent ea97e3b commit a22472d
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 23 deletions.
15 changes: 14 additions & 1 deletion src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const socket = new WebSocket(`ws://${location.host}`)

// Listen for messages
socket.addEventListener('message', ({ data }) => {
const { type, path, id, index, timestamp } = JSON.parse(data)
const { type, path, id, index, timestamp, customData } = JSON.parse(data)
switch (type) {
case 'connected':
console.log(`[vite] connected.`)
Expand Down Expand Up @@ -49,6 +49,12 @@ socket.addEventListener('message', ({ data }) => {
)
}
break
case 'custom':
const cbs = customUpdateMap.get(id)
if (cbs) {
cbs.forEach((cb) => cb(customData))
}
break
case 'full-reload':
location.reload()
}
Expand Down Expand Up @@ -78,6 +84,7 @@ export function updateStyle(id: string, url: string) {
}

const jsUpdateMap = new Map<string, (timestamp: number) => void>()
const customUpdateMap = new Map<string, ((customData: any) => void)[]>()

export const hot = {
accept(
Expand All @@ -94,5 +101,11 @@ export const hot = {
import(deps + `?t=${timestamp}`).then(callback)
}
})
},

on(event: string, cb: () => void) {
const exisitng = customUpdateMap.get(event) || []
exisitng.push(cb)
customUpdateMap.set(event, exisitng)
}
}
13 changes: 4 additions & 9 deletions src/node/server.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
import http, { Server } from 'http'
import Koa from 'koa'
import chokidar, { FSWatcher } from 'chokidar'
import chokidar from 'chokidar'
import { Resolver, createResolver, InternalResolver } from './resolver'
import { modulesPlugin } from './serverPluginModules'
import { vuePlugin } from './serverPluginVue'
import { hmrPlugin } from './serverPluginHmr'
import { hmrPlugin, HMRWatcher } from './serverPluginHmr'
import { servePlugin } from './serverPluginServe'

export { Resolver }

export type Plugin = (ctx: PluginContext) => void

export type ViteWatcher = FSWatcher & {
handleVueReload: (file: string, timestamp?: number, content?: string) => void
handleJSReload: (file: string, timestamp?: number) => void
}

export interface PluginContext {
root: string
app: Koa
server: Server
watcher: ViteWatcher
watcher: HMRWatcher
resolver: InternalResolver
}

Expand All @@ -43,7 +38,7 @@ export function createServer(config: ServerConfig = {}): Server {
const server = http.createServer(app.callback())
const watcher = chokidar.watch(root, {
ignored: [/node_modules/]
}) as ViteWatcher
}) as HMRWatcher
const resolver = createResolver(root, resolvers)

;[...plugins, ...internalPlugins].forEach((m) =>
Expand Down
35 changes: 22 additions & 13 deletions src/node/serverPluginHmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// we also record the importer/importee relationships which can be used for
// HMR analysis (we do both at the same time to avoid double parse costs)
// 3. When a `.vue` file changes, we directly read, parse it again and
// notify the client because Vue components are self-accepting by nature
// send the client because Vue components are self-accepting by nature
// 4. When a js file changes, it triggers an HMR graph analysis, where we try to
// walk its importer chains and see if we reach a "HMR boundary". An HMR
// boundary is either a `.vue` file or a `.js` file that explicitly indicated
Expand All @@ -17,7 +17,7 @@
// child importer is in the accepted list of the boundary (see additional
// explanation below). If yes, record current child importer in the
// `jsImporters` Set.
// 8. If the graph walk finished without running into dead ends, notify the
// 8. If the graph walk finished without running into dead ends, send the
// client to update all `jsImporters` and `vueImporters`.

// How do we get a js HMR boundary's accepted list on the server
Expand All @@ -37,6 +37,13 @@ import { parseSFC, vueCache } from './serverPluginVue'
import { cachedRead } from './utils'
import { importerMap, hmrBoundariesMap } from './serverPluginModules'
import chalk from 'chalk'
import { FSWatcher } from 'chokidar'

export type HMRWatcher = FSWatcher & {
handleVueReload: (file: string, timestamp?: number, content?: string) => void
handleJSReload: (file: string, timestamp?: number) => void
send: (payload: HMRPayload) => void
}

export const debugHmr = require('debug')('vite:hmr')

Expand All @@ -51,6 +58,7 @@ interface HMRPayload {
path?: string
id?: string
index?: number
customData?: any
}

export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
Expand Down Expand Up @@ -83,12 +91,16 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
}
})

const notify = (payload: HMRPayload) => {
const send = (payload: HMRPayload) => {
const stringified = JSON.stringify(payload, null, 2)
debugHmr(`update: ${stringified}`)
sockets.forEach((s) => s.send(stringified))
}

watcher.handleVueReload = handleVueReload
watcher.handleJSReload = handleJSReload
watcher.send = send

watcher.on('change', async (file) => {
const timestamp = Date.now()
if (file.endsWith('.vue')) {
Expand All @@ -98,9 +110,6 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
}
})

watcher.handleVueReload = handleVueReload
watcher.handleJSReload = handleJSReload

async function handleVueReload(
file: string,
timestamp: number = Date.now(),
Expand Down Expand Up @@ -157,7 +166,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
if (!needReload) {
nextStyles.forEach((_, i) => {
if (!prevStyles[i] || !isEqual(prevStyles[i], nextStyles[i])) {
notify({
send({
type: 'vue-style-update',
path: publicPath,
index: i,
Expand All @@ -170,7 +179,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {

// stale styles always need to be removed
prevStyles.slice(nextStyles.length).forEach((_, i) => {
notify({
send({
type: 'vue-style-remove',
path: publicPath,
id: `${styleId}-${i + nextStyles.length}`,
Expand All @@ -179,13 +188,13 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
})

if (needReload) {
notify({
send({
type: 'vue-reload',
path: publicPath,
timestamp
})
} else if (needRerender) {
notify({
send({
type: 'vue-rerender',
path: publicPath,
timestamp
Expand All @@ -208,20 +217,20 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
)

if (hasDeadEnd) {
notify({
send({
type: 'full-reload',
timestamp
})
} else {
vueImporters.forEach((vueImporter) => {
notify({
send({
type: 'vue-reload',
path: vueImporter,
timestamp
})
})
jsHotImporters.forEach((jsImporter) => {
notify({
send({
type: 'js-update',
path: jsImporter,
timestamp
Expand Down

0 comments on commit a22472d

Please sign in to comment.