Skip to content

Commit 744fe8b

Browse files
committed
feat: using vite hmr runtime
1 parent 05a2be6 commit 744fe8b

File tree

7 files changed

+174
-12
lines changed

7 files changed

+174
-12
lines changed

Diff for: packages/vite/src/client/client.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
normalizeModuleRunnerTransport,
88
} from '../shared/moduleRunnerTransport'
99
import { ErrorOverlay, overlayId } from './overlay'
10-
import '@vite/env'
10+
import './hmrModuleRunner'
11+
// import '@vite/env'
1112

1213
// injected by the hmr plugin when served
1314
declare const __BASE__: string
@@ -140,19 +141,16 @@ const hmrClient = new HMRClient(
140141
},
141142
transport,
142143
async function importUpdatedModule({
144+
url,
143145
acceptedPath,
144-
timestamp,
145-
explicitImportRequired,
146+
// timestamp,
147+
// explicitImportRequired,
146148
isWithinCircularImport,
147149
}) {
148-
const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`)
150+
// const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`)
149151
const importPromise = import(
150152
/* @vite-ignore */
151-
base +
152-
acceptedPathWithoutQuery.slice(1) +
153-
`?${explicitImportRequired ? 'import&' : ''}t=${timestamp}${
154-
query ? `&${query}` : ''
155-
}`
153+
base + url
156154
)
157155
if (isWithinCircularImport) {
158156
importPromise.catch(() => {
@@ -163,7 +161,10 @@ const hmrClient = new HMRClient(
163161
pageReload()
164162
})
165163
}
166-
return await importPromise
164+
// @ts-expect-error globalThis.__rolldown_runtime__
165+
return await importPromise.then(() =>
166+
globalThis.__rolldown_runtime__.loadExports(acceptedPath),
167+
)
167168
},
168169
)
169170
transport.connect!(handleMessage)

Diff for: packages/vite/src/client/hmrModuleRunner.ts

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { createHotContext } from './client'
2+
3+
class DevRuntime {
4+
modules: Record<string, { exports: any }> = {}
5+
6+
static getInstance() {
7+
// @ts-expect-error __rolldown_runtime__
8+
let instance = globalThis.__rolldown_runtime__;
9+
if (!instance) {
10+
instance = new DevRuntime();
11+
// @ts-expect-error __rolldown_runtime__
12+
globalThis.__rolldown_runtime__ = instance;
13+
}
14+
return instance
15+
}
16+
17+
createModuleHotContext(moduleId: string) {
18+
return createHotContext(moduleId);
19+
}
20+
21+
applyUpdates(_boundaries: string[]) {
22+
// trigger callbacks of accept() correctly
23+
// for (const moduleId of boundaries) {
24+
// const hotContext = this.moduleHotContexts.get(moduleId);
25+
// if (hotContext) {
26+
// const acceptCallbacks = hotContext.acceptCallbacks;
27+
// acceptCallbacks.filter((cb) => {
28+
// cb.fn(this.modules[moduleId].exports);
29+
// })
30+
// }
31+
// }
32+
// this.moduleHotContextsToBeUpdated.forEach((hotContext, moduleId) => {
33+
// this.moduleHotContexts[moduleId] = hotContext;
34+
// })
35+
// this.moduleHotContextsToBeUpdated.clear()
36+
// swap new contexts
37+
}
38+
39+
registerModule(id: string, exportGetters: Record<string, () => any>) {
40+
const exports = {};
41+
Object.keys(exportGetters).forEach((key) => {
42+
if (Object.prototype.hasOwnProperty.call(exportGetters, key)) {
43+
Object.defineProperty(exports, key, {
44+
enumerable: true,
45+
get: exportGetters[key],
46+
});
47+
}
48+
})
49+
if (this.modules[id]) {
50+
this.modules[id] = {
51+
exports,
52+
}
53+
} else {
54+
// If the module is not in the cache, we need to register it.
55+
this.modules[id] = {
56+
exports,
57+
};
58+
}
59+
}
60+
61+
loadExports(id: string) {
62+
const module = this.modules[id];
63+
if (module) {
64+
return module.exports;
65+
} else {
66+
console.warn(`Module ${id} not found`);
67+
return {};
68+
}
69+
}
70+
71+
// __esmMin
72+
// createEsmInitializer = (fn, res) => () => (fn && (res = fn(fn = 0)), res)
73+
// __commonJSMin
74+
// createCjsInitializer = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports)
75+
}
76+
77+
DevRuntime.getInstance();
78+
79+
// export function loadScript(url: string): void {
80+
// const script = document.createElement('script');
81+
// script.src = url;
82+
// script.type = 'module';
83+
// script.onerror = function () {
84+
// console.error('Failed to load script: ' + url);
85+
// }
86+
// document.body.appendChild(script);
87+
// }
88+

Diff for: packages/vite/src/node/build.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import type { MinimalPluginContext, Plugin, PluginContext } from './plugin'
8787
import type { RollupPluginHooks } from './typeUtils'
8888
import { buildOxcPlugin } from './plugins/oxc'
8989
import type { ViteDevServer } from './server'
90+
import { getHmrImplement } from './plugins/clientInjections'
9091

9192
export interface BuildEnvironmentOptions {
9293
/**
@@ -655,7 +656,12 @@ async function buildEnvironment(
655656
'.css': 'js',
656657
},
657658
experimental: {
658-
hmr: true,
659+
hmr: server
660+
? {
661+
implement: await getHmrImplement(environment.config),
662+
}
663+
: false,
664+
// hmr: true,
659665
// hmr: server ? {
660666
// host: server._currentServerHost!,
661667
// port: server._currentServerPort!,

Diff for: packages/vite/src/node/plugins/clientInjections.ts

+63
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import path from 'node:path'
2+
import fs from 'node:fs'
23
import type { Plugin } from '../plugin'
34
import type { ResolvedConfig } from '../config'
45
import { CLIENT_ENTRY, ENV_ENTRY } from '../constants'
@@ -121,3 +122,65 @@ function escapeReplacement(value: string | number | boolean | null) {
121122
const jsonValue = JSON.stringify(value)
122123
return () => jsonValue
123124
}
125+
126+
export async function getHmrImplement(config: ResolvedConfig): Promise<string> {
127+
const content = fs.readFileSync(normalizedClientEntry, 'utf-8')
128+
const resolvedServerHostname = (await resolveHostname(config.server.host))
129+
.name
130+
const resolvedServerPort = config.server.port!
131+
const devBase = config.base
132+
133+
const serverHost = `${resolvedServerHostname}:${resolvedServerPort}${devBase}`
134+
135+
let hmrConfig = config.server.hmr
136+
hmrConfig = isObject(hmrConfig) ? hmrConfig : undefined
137+
const host = hmrConfig?.host || null
138+
const protocol = hmrConfig?.protocol || null
139+
const timeout = hmrConfig?.timeout || 30000
140+
const overlay = hmrConfig?.overlay !== false
141+
const isHmrServerSpecified = !!hmrConfig?.server
142+
const hmrConfigName = path.basename(config.configFile || 'vite.config.js')
143+
144+
// hmr.clientPort -> hmr.port
145+
// -> (24678 if middleware mode and HMR server is not specified) -> new URL(import.meta.url).port
146+
let port = hmrConfig?.clientPort || hmrConfig?.port || null
147+
if (config.server.middlewareMode && !isHmrServerSpecified) {
148+
port ||= 24678
149+
}
150+
151+
let directTarget = hmrConfig?.host || resolvedServerHostname
152+
directTarget += `:${hmrConfig?.port || resolvedServerPort}`
153+
directTarget += devBase
154+
155+
let hmrBase = devBase
156+
if (hmrConfig?.path) {
157+
hmrBase = path.posix.join(hmrBase, hmrConfig.path)
158+
}
159+
160+
const modeReplacement = escapeReplacement(config.mode)
161+
const baseReplacement = escapeReplacement(devBase)
162+
const serverHostReplacement = escapeReplacement(serverHost)
163+
const hmrProtocolReplacement = escapeReplacement(protocol)
164+
const hmrHostnameReplacement = escapeReplacement(host)
165+
const hmrPortReplacement = escapeReplacement(port)
166+
const hmrDirectTargetReplacement = escapeReplacement(directTarget)
167+
const hmrBaseReplacement = escapeReplacement(hmrBase)
168+
const hmrTimeoutReplacement = escapeReplacement(timeout)
169+
const hmrEnableOverlayReplacement = escapeReplacement(overlay)
170+
const hmrConfigNameReplacement = escapeReplacement(hmrConfigName)
171+
const wsTokenReplacement = escapeReplacement(config.webSocketToken)
172+
173+
return content
174+
.replace(`__MODE__`, modeReplacement)
175+
.replace(/__BASE__/g, baseReplacement)
176+
.replace(`__SERVER_HOST__`, serverHostReplacement)
177+
.replace(`__HMR_PROTOCOL__`, hmrProtocolReplacement)
178+
.replace(`__HMR_HOSTNAME__`, hmrHostnameReplacement)
179+
.replace(`__HMR_PORT__`, hmrPortReplacement)
180+
.replace(`__HMR_DIRECT_TARGET__`, hmrDirectTargetReplacement)
181+
.replace(`__HMR_BASE__`, hmrBaseReplacement)
182+
.replace(`__HMR_TIMEOUT__`, hmrTimeoutReplacement)
183+
.replace(`__HMR_ENABLE_OVERLAY__`, hmrEnableOverlayReplacement)
184+
.replace(`__HMR_CONFIG_NAME__`, hmrConfigNameReplacement)
185+
.replace(`__WS_TOKEN__`, wsTokenReplacement)
186+
}

Diff for: packages/vite/src/node/plugins/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export async function resolvePlugins(
131131
: oxcPlugin(config)
132132
: null,
133133
enableNativePlugin === true
134-
? nativeJsonPlugin({ ...config.json, isBuild })
134+
? nativeJsonPlugin({ ...config.json, minify: isBuild })
135135
: jsonPlugin(config.json, isBuild),
136136
enableNativePlugin === true ? nativeWasmHelperPlugin() : wasmHelperPlugin(),
137137
webWorkerPlugin(config),

Diff for: packages/vite/types/hmrPayload.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface UpdatePayload {
2424

2525
export interface Update {
2626
type: 'js-update' | 'css-update'
27+
url?: string // the hmr chunk url
2728
path: string
2829
acceptedPath: string
2930
timestamp: number

Diff for: vitest.config.e2e.ts

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export default defineConfig({
1414
test: {
1515
include: ['./playground/**/*.spec.[tj]s'],
1616
exclude: [
17+
'./playground/hmr/**/*.spec.[tj]s',
18+
'./playground/ssr/**/*.spec.[tj]s',
19+
'./playground/ssr*/**/*.spec.[tj]s',
1720
'./playground/legacy/**/*.spec.[tj]s', // system format
1821
...(isBuild
1922
? [

0 commit comments

Comments
 (0)