Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 14 additions & 39 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,9 @@ function warnFailedFetch(err: Error, path: string | string[]) {

// Listen for messages
socket.addEventListener('message', async ({ data }) => {
const {
type,
path,
changeSrcPath,
id,
index,
timestamp,
customData
} = JSON.parse(data)
const { type, path, changeSrcPath, id, timestamp, customData } = JSON.parse(
data
)

if (changeSrcPath) {
await bustSwCache(changeSrcPath)
Expand All @@ -84,35 +78,10 @@ socket.addEventListener('message', async ({ data }) => {
case 'connected':
console.log(`[vite] connected.`)
break
case 'vue-reload':
import(`${path}?t=${timestamp}`)
.then((m) => {
__VUE_HMR_RUNTIME__.reload(path, m.default)
console.log(`[vite] ${path} reloaded.`)
})
.catch((err) => warnFailedFetch(err, path))
break
case 'vue-rerender':
const templatePath = `${path}?type=template`
await bustSwCache(templatePath)
import(`${templatePath}&t=${timestamp}`).then((m) => {
__VUE_HMR_RUNTIME__.rerender(path, m.render)
console.log(`[vite] ${path} template updated.`)
})
break
case 'vue-style-update':
const stylePath = `${path}?type=style&index=${index}`
await bustSwCache(stylePath)
const content = await import(stylePath + `&t=${timestamp}`)
updateStyle(id, content.default)
console.log(
`[vite] ${path} style${index > 0 ? `#${index}` : ``} updated.`
)
break
case 'style-update':
await bustSwCache(`${path}?import`)
const style = await import(`${path}?t=${timestamp}`)
updateStyle(id, style.default)
const hasQuery = path.includes('?') ? '&' : '?'
await bustSwCache(`${path}${hasQuery}import`)
await import(`${path}${hasQuery}t=${timestamp}`)
console.log(`[vite] ${path} updated.`)
break
case 'style-remove':
Expand Down Expand Up @@ -220,8 +189,14 @@ async function updateModule(
Array.from(modulesToUpdate).map(async (dep) => {
const disposer = jsDisposeMap.get(dep)
if (disposer) await disposer()
const newMod = await import(dep + `?t=${timestamp}`)
moduleMap.set(dep, newMod)
try {
const newMod = await import(
dep + (dep.includes('?') ? '&' : '?') + `t=${timestamp}`
)
moduleMap.set(dep, newMod)
} catch (e) {
warnFailedFetch(e, dep)
}
})
)

Expand Down
30 changes: 7 additions & 23 deletions src/node/server/serverPluginCss.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ServerPlugin } from '.'
import { hmrClientId } from './serverPluginHmr'
import hash_sum from 'hash-sum'
import { Context } from 'koa'
import { cleanUrl, isImportRequest, readBody } from '../utils'
import { srcImportMap, vueCache } from './serverPluginVue'
import {
codegenCss,
compileCss,
cssPreprocessLangRE,
rewriteCssUrls
Expand Down Expand Up @@ -34,33 +34,21 @@ export const cssPlugin: ServerPlugin = ({
// note ctx.body could be null if upstream set status to 304
ctx.body
) {
const id = JSON.stringify(hash_sum(ctx.path))
if (isImportRequest(ctx)) {
await processCss(root, ctx)
// we rewrite css with `?import` to a js module that inserts a style
// tag linking to the actual raw url
ctx.type = 'js'
const id = JSON.stringify(hash_sum(ctx.path))
let code =
`import { updateStyle } from "${hmrClientId}"\n` +
`const css = ${JSON.stringify(processedCSS.get(ctx.path)!.css)}\n` +
`updateStyle(${id}, css)\n`
if (ctx.path.endsWith('.module.css')) {
code += `export default ${JSON.stringify(
processedCSS.get(ctx.path)!.modules
)}`
} else {
code += `export default css`
}
ctx.body = code.trim()
const { css, modules } = processedCSS.get(ctx.path)!
ctx.body = codegenCss(id, css, modules)
} else {
// raw request, return compiled css
if (!processedCSS.has(ctx.path)) {
await processCss(root, ctx)
}
ctx.type = 'js'
ctx.body = `export default ${JSON.stringify(
processedCSS.get(ctx.path)!.css
)}`
ctx.body = codegenCss(id, processedCSS.get(ctx.path)!.css)
}
}
})
Expand All @@ -78,10 +66,8 @@ export const cssPlugin: ServerPlugin = ({
chalk.green(`[vite:hmr] `) + `${publicPath} updated. (style)`
)
watcher.send({
type: 'vue-style-update',
path: publicPath,
index: Number(index),
id: `${hash_sum(publicPath)}-${index}`,
type: 'style-update',
path: `${publicPath}?type=style&index=${index}`,
timestamp: Date.now()
})
return
Expand All @@ -94,14 +80,12 @@ export const cssPlugin: ServerPlugin = ({
}

const publicPath = resolver.fileToRequest(file)
const id = hash_sum(publicPath)

// bust process cache
processedCSS.delete(publicPath)

watcher.send({
type: 'style-update',
id,
path: publicPath,
timestamp: Date.now()
})
Expand Down
76 changes: 38 additions & 38 deletions src/node/server/serverPluginHmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ export const hmrClientPublicPath = `/${hmrClientId}`

interface HMRPayload {
type:
| 'vue-rerender'
| 'vue-reload'
| 'vue-style-update'
| 'js-update'
| 'style-update'
| 'style-remove'
Expand Down Expand Up @@ -178,12 +175,24 @@ export const hmrPlugin: ServerPlugin = ({
}

// check which part of the file changed
let needReload = false
let needCssModuleReload = false
let needRerender = false

const vueReload = () => {
send({
type: 'js-update',
path: publicPath,
changeSrcPath: publicPath,
timestamp
})
console.log(
chalk.green(`[vite:hmr] `) +
`${path.relative(root, file)} updated. (reload)`
)
}

if (!isEqual(descriptor.script, prevDescriptor.script)) {
needReload = true
vueReload()
return
}

if (!isEqual(descriptor.template, prevDescriptor.template)) {
Expand All @@ -194,12 +203,6 @@ export const hmrPlugin: ServerPlugin = ({
const styleId = hash_sum(publicPath)
const prevStyles = prevDescriptor.styles || []
const nextStyles = descriptor.styles || []
if (
!needReload &&
prevStyles.some((s) => s.scoped) !== nextStyles.some((s) => s.scoped)
) {
needReload = true
}

// css modules update causes a reload because the $style object is changed
// and it may be used in JS. It also needs to trigger a vue-style-update
Expand All @@ -208,25 +211,26 @@ export const hmrPlugin: ServerPlugin = ({
prevStyles.some((s) => s.module != null) ||
nextStyles.some((s) => s.module != null)
) {
needCssModuleReload = true
vueReload()
return
}

if (prevStyles.some((s) => s.scoped) !== nextStyles.some((s) => s.scoped)) {
needRerender = true
}

// only need to update styles if not reloading, since reload forces
// style updates as well.
if (!needReload) {
nextStyles.forEach((_, i) => {
if (!prevStyles[i] || !isEqual(prevStyles[i], nextStyles[i])) {
didUpdateStyle = true
send({
type: 'vue-style-update',
path: publicPath,
index: i,
id: `${styleId}-${i}`,
timestamp
})
}
})
}
nextStyles.forEach((_, i) => {
if (!prevStyles[i] || !isEqual(prevStyles[i], nextStyles[i])) {
didUpdateStyle = true
send({
type: 'style-update',
path: `${publicPath}?type=style&index=${i}`,
timestamp
})
}
})

// stale styles always need to be removed
prevStyles.slice(nextStyles.length).forEach((_, i) => {
Expand All @@ -239,22 +243,17 @@ export const hmrPlugin: ServerPlugin = ({
})
})

if (needReload || needCssModuleReload) {
send({
type: 'vue-reload',
path: publicPath,
timestamp
})
} else if (needRerender) {
if (needRerender) {
send({
type: 'vue-rerender',
type: 'js-update',
path: publicPath,
changeSrcPath: `${publicPath}?type=template`,
timestamp
})
}

if (needReload || needRerender || didUpdateStyle) {
let updateType = needReload ? `reload` : needRerender ? `template` : ``
if (needRerender || didUpdateStyle) {
let updateType = needRerender ? `template` : ``
if (didUpdateStyle) {
updateType += ` & style`
}
Expand Down Expand Up @@ -308,7 +307,7 @@ export const hmrPlugin: ServerPlugin = ({
`${vueBoundary} reloaded due to change in ${relativeFile}.`
)
send({
type: 'vue-reload',
type: 'js-update',
path: vueBoundary,
changeSrcPath: publicPath,
timestamp
Expand Down Expand Up @@ -385,6 +384,7 @@ function isHmrAccepted(importer: string, dep: string): boolean {
function isEqual(a: SFCBlock | null, b: SFCBlock | null) {
if (!a && !b) return true
if (!a || !b) return false
if (a.content.length !== b.content.length) return false
if (a.content !== b.content) return false
const keysA = Object.keys(a.attrs)
const keysB = Object.keys(b.attrs)
Expand Down
5 changes: 1 addition & 4 deletions src/node/server/serverPluginModuleRewrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,7 @@ export function rewriteImports(
let resolved
if (id === hmrClientId) {
resolved = hmrClientPublicPath
if (
/\bhot\b/.test(source.substring(ss, se)) &&
!/.vue$|.vue\?type=/.test(importer)
) {
if (/\bhot\b/.test(source.substring(ss, se))) {
// the user explicit imports the HMR API in a js file
// making the module hot.
rewriteFileWithHMR(root, source, importer, resolver, s)
Expand Down
37 changes: 22 additions & 15 deletions src/node/server/serverPluginVue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Context } from 'koa'
import { transform } from '../esbuildService'
import { InternalResolver } from '../resolver'
import { seenUrls } from './serverPluginServeStatic'
import { compileCss, rewriteCssUrls } from '../utils/cssUtils'
import { codegenCss, compileCss, rewriteCssUrls } from '../utils/cssUtils'

const debug = require('debug')('vite:sfc')
const getEtag = require('etag')
Expand Down Expand Up @@ -114,20 +114,16 @@ export const vuePlugin: ServerPlugin = ({
if (styleBlock.src) {
filename = await resolveSrcImport(styleBlock, ctx, resolver)
}
const id = hash_sum(publicPath)
const result = await compileSFCStyle(
root,
styleBlock,
index,
filename,
publicPath
)
if (query.module != null) {
ctx.type = 'js'
ctx.body = `export default ${JSON.stringify(result.modules)}`
} else {
ctx.type = 'js'
ctx.body = `export default ${JSON.stringify(result.code)}`
}
ctx.type = 'js'
ctx.body = codegenCss(`${id}-${index}`, result.code, result.modules)
return etagCacheCheck(ctx)
}

Expand Down Expand Up @@ -219,7 +215,8 @@ async function compileSFCMain(
return cached.script
}

let code = ''
const id = hash_sum(publicPath)
let code = `\nimport { updateStyle, hot } from "${hmrClientId}"\n`
if (descriptor.script) {
let content = descriptor.script.content
if (descriptor.script.lang === 'ts') {
Expand All @@ -231,11 +228,15 @@ async function compileSFCMain(
code += `const __script = {}`
}

const id = hash_sum(publicPath)
code += `\n if (__DEV__) {
hot.accept((m) => {
__VUE_HMR_RUNTIME__.reload("${id}", m.default)
})
}`

let hasScoped = false
let hasCSSModules = false
if (descriptor.styles) {
code += `\nimport { updateStyle } from "${hmrClientId}"\n`
descriptor.styles.forEach((s, i) => {
const styleRequest = publicPath + `?type=style&index=${i}`
if (s.scoped) hasScoped = true
Expand All @@ -250,22 +251,28 @@ async function compileSFCMain(
styleRequest + '&module'
)}`
code += `\n__cssModules[${JSON.stringify(moduleName)}] = ${styleVar}`
} else {
code += `\nimport ${JSON.stringify(styleRequest)}`
}
code += `\nimport css_${i} from ${JSON.stringify(styleRequest)}`
code += `\nupdateStyle("${id}-${i}", css_${i})`
})
if (hasScoped) {
code += `\n__script.__scopeId = "data-v-${id}"`
}
}

if (descriptor.template) {
const templateRequest = publicPath + `?type=template`
code += `\nimport { render as __render } from ${JSON.stringify(
publicPath + `?type=template`
templateRequest
)}`
code += `\n__script.render = __render`
code += `\n if (__DEV__) {
hot.accept(${JSON.stringify(templateRequest)}, (m) => {
__VUE_HMR_RUNTIME__.rerender("${id}", m.render)
})
}`
}
code += `\n__script.__hmrId = ${JSON.stringify(publicPath)}`
code += `\n__script.__hmrId = ${JSON.stringify(id)}`
code += `\n__script.__file = ${JSON.stringify(filePath)}`
code += `\nexport default __script`

Expand Down
Loading