Skip to content

Commit

Permalink
feat(hmr): change hmr API path to vite/hmr and provide typing
Browse files Browse the repository at this point in the history
Based on #92
  • Loading branch information
yyx990803 committed May 8, 2020
1 parent 3af44fc commit eab49a4
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 18 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ The above will throw an error by default. Vite detects such bare module imports

```js
import { foo } from './foo.js'
import { hot } from '@hmr'
import { hot } from 'vite/hmr'

foo()

Expand All @@ -95,7 +95,7 @@ The above will throw an error by default. Vite detects such bare module imports
Modules can also be self-accepting:

```js
import { hot } from '@hmr'
import { hot } from 'vite/hmr'

export const count = 1

Expand All @@ -110,6 +110,8 @@ The above will throw an error by default. Vite detects such bare module imports
A self-accepting module, or a module that expects to be accepted by others can use `hot.dispose` to cleanup any persistent side effects created by its updated copy:

```js
import { hot } from 'vite/hmr'

function setupSideEffect() {}
function cleanupSideEffect() {}

Expand Down
12 changes: 12 additions & 0 deletions hmr.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export declare const hot: {
// single dep
accept(dep: string, cb?: (newModule: any) => void): void
// multiple deps
accept(deps: string[], cb?: (newModules: any[]) => void): void
// self-accepting
accept(cb: (newModule: any) => void): void
// dispose
dispose(cb: () => void): void
// custom events
on(event: string, cb: () => void): void
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"types": "dist/index.d.ts",
"files": [
"bin",
"dist"
"dist",
"hmr.d.ts"
],
"engines": {
"node": ">=10.0.0"
Expand All @@ -29,7 +30,7 @@
"dev-node": "tsc -w --p src/node",
"build": "rm -rf dist && tsc -p src/client && tsc -p src/node",
"lint": "prettier --write --parser typescript \"src/**/*.ts\"",
"test": "jest --runInBand",
"test": "jest --clearCache && jest --runInBand",
"windows-ci": "yarn build && jest --runInBand --forceExit",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"prepublishOnly": "yarn build && yarn changelog",
Expand Down
2 changes: 1 addition & 1 deletion playground/testHmrManual.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hot } from '@hmr'
import { hot } from 'vite/hmr'

export const foo = 1

Expand Down
2 changes: 1 addition & 1 deletion src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ export interface ServerConfig {
}

const internalPlugins: Plugin[] = [
hmrPlugin,
moduleRewritePlugin,
moduleResolvePlugin,
vuePlugin,
esbuildPlugin,
jsonPlugin,
cssPlugin,
assetPathPlugin,
hmrPlugin,
serveStaticPlugin
]

Expand Down
6 changes: 3 additions & 3 deletions src/node/server/serverPluginHmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// 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
// that it accepts hot updates (by importing from the `/@hmr` special module)
// that it accepts hot updates (by importing from the `/vite/hmr` special module)
// 5. If any parent chain exhausts without ever running into an HMR boundary,
// it's considered a "dead end". This causes a full page reload.
// 6. If a `.vue` boundary is encountered, we add it to the `vueImports` Set.
Expand All @@ -21,7 +21,7 @@
// client to update all `jsImporters` and `vueImporters`.

// How do we get a js HMR boundary's accepted list on the server
// 1. During the import rewriting, if `/@hmr` import is present in a js file,
// 1. During the import rewriting, if `/vite/hmr` import is present in a js file,
// we will do a fullblown parse of the file to find the `hot.accept` call,
// and records the file and its accepted dependencies in a `hmrBoundariesMap`
// 2. We also inject the boundary file's full path into the `hot.accept` call
Expand Down Expand Up @@ -60,7 +60,7 @@ export const importeeMap: HMRStateMap = new Map()

// client and node files are placed flat in the dist folder
export const hmrClientFilePath = path.resolve(__dirname, '../client.js')
export const hmrClientId = '@hmr'
export const hmrClientId = 'vite/hmr'
export const hmrClientPublicPath = `/${hmrClientId}`

interface HMRPayload {
Expand Down
18 changes: 9 additions & 9 deletions src/node/server/serverPluginModuleRewrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
importeeMap,
ensureMapEntry,
rewriteFileWithHMR,
hmrClientPublicPath
hmrClientPublicPath,
hmrClientId
} from './serverPluginHmr'
import {
readBody,
Expand Down Expand Up @@ -100,7 +101,7 @@ export const moduleRewritePlugin: Plugin = ({ app, watcher, resolver }) => {
ctx.response.is('js') &&
!ctx.url.endsWith('.map') &&
// skip internal client
!ctx.path.startsWith(`/@hmr`) &&
!ctx.path.startsWith(hmrClientPublicPath) &&
// only need to rewrite for <script> part in vue files
!((ctx.path.endsWith('.vue') || ctx.vue) && ctx.query.type != null)
) {
Expand Down Expand Up @@ -154,22 +155,21 @@ function rewriteImports(
if (dynamicIndex === -1 || hasLiteralDynamicId) {
// do not rewrite external imports
if (isExternalUrl(id)) {
break
continue
}

let resolved
if (/^[^\/\.]/.test(id)) {
resolved = resolver.idToRequest(id) || `/@modules/${id}`
if (
resolved === hmrClientPublicPath &&
!/.vue$|.vue\?type=/.test(importer)
) {
if (id === hmrClientId) {
resolved = hmrClientPublicPath
if (!/.vue$|.vue\?type=/.test(importer)) {
// the user explicit imports the HMR API in a js file
// making the module hot.
rewriteFileWithHMR(source, importer, s)
// we rewrite the hot.accept call
hasReplaced = true
}
} else if (/^[^\/\.]/.test(id)) {
resolved = resolver.idToRequest(id) || `/@modules/${id}`
} else {
let { pathname, query } = resolveRelativeRequest(importer, id)
// append .js or .ts for extension-less imports
Expand Down

0 comments on commit eab49a4

Please sign in to comment.