diff --git a/packages/vite-plugin-simple-hmr/README.md b/packages/vite-plugin-simple-hmr/README.md
new file mode 100644
index 000000000..56196bf31
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/README.md
@@ -0,0 +1,22 @@
+# vite-plugin-simple-hmr
+
+Simple HMR to reassign all exports, which can be useful for [SSR HMR](https://github.com/vitejs/vite/pull/12165) to accompany with React plugin's client HMR.
+
+## example
+
+See [./examples/react](./examples/react).
+
+```tsx
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import { vitePluginSimpleHmr } from "@hiogawa/vite-plugin-simple-hmr";
+
+export default defineConfig({
+ plugins: [
+ react(),
+ vitePluginSimpleHmr({
+ include: ["**/*.tsx"],
+ }),
+ ],
+});
+```
diff --git a/packages/vite-plugin-simple-hmr/examples/react/README.md b/packages/vite-plugin-simple-hmr/examples/react/README.md
new file mode 100644
index 000000000..2a578ecec
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/README.md
@@ -0,0 +1,3 @@
+```sh
+pnpm -C packages/vite-plugin-hmr/examples/react dev
+```
diff --git a/packages/vite-plugin-simple-hmr/examples/react/index.html b/packages/vite-plugin-simple-hmr/examples/react/index.html
new file mode 100644
index 000000000..b11e3bffd
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ vite-plugin-hmr-ssr-react
+
+
+
+
+
+
diff --git a/packages/vite-plugin-simple-hmr/examples/react/package.json b/packages/vite-plugin-simple-hmr/examples/react/package.json
new file mode 100644
index 000000000..f324b5914
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@hiogawa/vite-plugin-hmr-example-react",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev"
+ },
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@hiogawa/vite-plugin-simple-hmr": "workspace:*",
+ "@hiogawa/vite-plugin-ssr-middleware": "workspace:*",
+ "@types/react": "18.2.12",
+ "@types/react-dom": "18.2.5"
+ }
+}
diff --git a/packages/vite-plugin-simple-hmr/examples/react/src/App.tsx b/packages/vite-plugin-simple-hmr/examples/react/src/App.tsx
new file mode 100644
index 000000000..bc4a4f104
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/src/App.tsx
@@ -0,0 +1,23 @@
+import { useState } from "react";
+import { AppDep1, AppDep2 } from "./AppDep";
+import { AppDepDep } from "./AppDepDep";
+
+export function App() {
+ const [count, setCount] = useState(0);
+
+ return (
+ <>
+ ssr + hmr + react
+
+
+
+
+ >
+ );
+}
diff --git a/packages/vite-plugin-simple-hmr/examples/react/src/AppDep.tsx b/packages/vite-plugin-simple-hmr/examples/react/src/AppDep.tsx
new file mode 100644
index 000000000..4886cfbc4
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/src/AppDep.tsx
@@ -0,0 +1,13 @@
+import { AppDepDep } from "./AppDepDep";
+
+export function AppDep1() {
+ return AppDep1
;
+}
+
+export const AppDep2 = () => {
+ return (
+
+ );
+};
diff --git a/packages/vite-plugin-simple-hmr/examples/react/src/AppDepDep.tsx b/packages/vite-plugin-simple-hmr/examples/react/src/AppDepDep.tsx
new file mode 100644
index 000000000..0a8e60172
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/src/AppDepDep.tsx
@@ -0,0 +1,3 @@
+export function AppDepDep() {
+ return AppDepDep;
+}
diff --git a/packages/vite-plugin-simple-hmr/examples/react/src/entry-client.tsx b/packages/vite-plugin-simple-hmr/examples/react/src/entry-client.tsx
new file mode 100644
index 000000000..e7f5c997d
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/src/entry-client.tsx
@@ -0,0 +1,8 @@
+import { hydrateRoot } from "react-dom/client";
+import { App } from "./App";
+
+function main() {
+ hydrateRoot(document.getElementById("root")!, );
+}
+
+main();
diff --git a/packages/vite-plugin-simple-hmr/examples/react/src/entry-server.tsx b/packages/vite-plugin-simple-hmr/examples/react/src/entry-server.tsx
new file mode 100644
index 000000000..dc8124e39
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/src/entry-server.tsx
@@ -0,0 +1,18 @@
+import fs from "node:fs";
+import type http from "node:http";
+import { renderToString } from "react-dom/server";
+import type { ViteDevServer } from "vite";
+import { App } from "./App";
+
+export default async function handler(
+ req: http.IncomingMessage & { viteDevServer: ViteDevServer },
+ res: http.ServerResponse
+) {
+ let html = await fs.promises.readFile("./index.html", "utf-8");
+ html = await req.viteDevServer.transformIndexHtml("/", html);
+
+ const ssrHtml = renderToString();
+ html = html.replace("", ssrHtml);
+
+ res.setHeader("content-type", "text/html").end(html);
+}
diff --git a/packages/vite-plugin-simple-hmr/examples/react/tsconfig.json b/packages/vite-plugin-simple-hmr/examples/react/tsconfig.json
new file mode 100644
index 000000000..389c44df1
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../../../tsconfig.base.json",
+ "compilerOptions": {
+ "types": ["vite/client"],
+ "jsx": "react-jsx"
+ }
+}
diff --git a/packages/vite-plugin-simple-hmr/examples/react/vite.config.ts b/packages/vite-plugin-simple-hmr/examples/react/vite.config.ts
new file mode 100644
index 000000000..24e58e177
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/examples/react/vite.config.ts
@@ -0,0 +1,18 @@
+import { vitePluginSimpleHmr } from "@hiogawa/vite-plugin-simple-hmr";
+import { vitePluginSsrMiddleware } from "@hiogawa/vite-plugin-ssr-middleware";
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ clearScreen: false,
+ plugins: [
+ react(),
+ vitePluginSimpleHmr({
+ include: ["**/*.tsx"],
+ }),
+ vitePluginSsrMiddleware({
+ entry: "/src/entry-server.tsx",
+ mode: "ViteRuntime",
+ }),
+ ],
+});
diff --git a/packages/vite-plugin-simple-hmr/package.json b/packages/vite-plugin-simple-hmr/package.json
new file mode 100644
index 000000000..c4ee97a63
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "@hiogawa/vite-plugin-simple-hmr",
+ "version": "0.0.1-pre.0",
+ "homepage": "https://github.com/hi-ogawa/vite-plugins/tree/main/packages/vite-plugin-simple-hmr",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/hi-ogawa/vite-plugins",
+ "directory": "packages/vite-plugin-simple-hmr"
+ },
+ "license": "MIT",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./runtime": {
+ "types": "./dist/runtime.d.ts",
+ "default": "./dist/runtime.js"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "tsup",
+ "test": "vitest",
+ "release": "pnpm publish --no-git-checks --access public"
+ },
+ "dependencies": {
+ "magic-string": "^0.30.6"
+ },
+ "devDependencies": {
+ "@types/estree": "^1.0.5",
+ "vite": "^5.1.0"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ }
+}
diff --git a/packages/vite-plugin-simple-hmr/src/__snapshots__/index.test.ts.snap b/packages/vite-plugin-simple-hmr/src/__snapshots__/index.test.ts.snap
new file mode 100644
index 000000000..44cef6fbe
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/__snapshots__/index.test.ts.snap
@@ -0,0 +1,183 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`hmrTransform > 0 - basic > analyzeExports 1`] = `
+{
+ "errors": [],
+ "exportIds": [
+ "Test1",
+ "Test2",
+ "Test3",
+ "Test4",
+ ],
+}
+`;
+
+exports[`hmrTransform > 0 - basic > hmrTransform 1`] = `
+{
+ "code": "
+export function Test1() {}
+
+export let Test2 = () => {}
+
+export /*some*/ let /*comment*/ Test3 = () => {}
+
+export default function Test4() {}
+
+
+if (import.meta.env.SSR && import.meta.hot) {
+ const $$hmr = await import("@hiogawa/vite-plugin-simple-hmr/runtime");
+ const $$registry = $$hmr.createRegistry();
+
+
+ $$registry.exports["Test1"] = {
+ value: Test1,
+ update: ($$next) => {
+ Test1 = $$next;
+ }
+ };
+
+
+ $$registry.exports["Test2"] = {
+ value: Test2,
+ update: ($$next) => {
+ Test2 = $$next;
+ }
+ };
+
+
+ $$registry.exports["Test3"] = {
+ value: Test3,
+ update: ($$next) => {
+ Test3 = $$next;
+ }
+ };
+
+
+ $$registry.exports["Test4"] = {
+ value: Test4,
+ update: ($$next) => {
+ Test4 = $$next;
+ }
+ };
+
+
+ $$hmr.setupHot(import.meta.hot, $$registry);
+ import.meta.hot.accept;
+}
+",
+ "map": SourceMap {
+ "file": undefined,
+ "mappings": "AAAA;AACA;AACA;AACA;AACA;AACA,mBAAqB;AACrB;AACA;",
+ "names": [],
+ "sources": [
+ "",
+ ],
+ "sourcesContent": undefined,
+ "version": 3,
+ },
+}
+`;
+
+exports[`hmrTransform > 1 - separate named export (unsupported) > analyzeExports 1`] = `
+{
+ "errors": [
+ "export { SomeNamed };",
+ ],
+ "exportIds": [],
+}
+`;
+
+exports[`hmrTransform > 1 - separate named export (unsupported) > hmrTransform 1`] = `
+{
+ "code": "
+const SomeNamed = () => {};
+export { SomeNamed };
+
+
+if (import.meta.env.SSR && import.meta.hot) {
+ import.meta.hot.accept(() => {
+ import.meta.hot.invalidate("unsupported usage: export { SomeNamed };")
+ });
+}
+",
+ "map": SourceMap {
+ "file": undefined,
+ "mappings": "AAAA;AACA;AACA;",
+ "names": [],
+ "sources": [
+ "",
+ ],
+ "sourcesContent": undefined,
+ "version": 3,
+ },
+}
+`;
+
+exports[`hmrTransform > 2 - separate default export (unsupported) > analyzeExports 1`] = `
+{
+ "errors": [
+ "export default SomeDefault;",
+ ],
+ "exportIds": [],
+}
+`;
+
+exports[`hmrTransform > 2 - separate default export (unsupported) > hmrTransform 1`] = `
+{
+ "code": "
+const SomeDeafult = () => {};
+export default SomeDefault;
+
+
+if (import.meta.env.SSR && import.meta.hot) {
+ import.meta.hot.accept(() => {
+ import.meta.hot.invalidate("unsupported usage: export default SomeDefault;")
+ });
+}
+",
+ "map": SourceMap {
+ "file": undefined,
+ "mappings": "AAAA;AACA;AACA;",
+ "names": [],
+ "sources": [
+ "",
+ ],
+ "sourcesContent": undefined,
+ "version": 3,
+ },
+}
+`;
+
+exports[`hmrTransform > 3 - anonymous default export (unsupported) > analyzeExports 1`] = `
+{
+ "errors": [
+ "export default () => {};",
+ ],
+ "exportIds": [],
+}
+`;
+
+exports[`hmrTransform > 3 - anonymous default export (unsupported) > hmrTransform 1`] = `
+{
+ "code": "
+export default () => {};
+
+
+if (import.meta.env.SSR && import.meta.hot) {
+ import.meta.hot.accept(() => {
+ import.meta.hot.invalidate("unsupported usage: export default () => {};")
+ });
+}
+",
+ "map": SourceMap {
+ "file": undefined,
+ "mappings": "AAAA;AACA;",
+ "names": [],
+ "sources": [
+ "",
+ ],
+ "sourcesContent": undefined,
+ "version": 3,
+ },
+}
+`;
diff --git a/packages/vite-plugin-simple-hmr/src/index.test.ts b/packages/vite-plugin-simple-hmr/src/index.test.ts
new file mode 100644
index 000000000..b7b84ae0d
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/index.test.ts
@@ -0,0 +1,61 @@
+import MagicString from "magic-string";
+import { parseAstAsync } from "vite";
+import { describe, expect, it } from "vitest";
+import { analyzeExports, hmrTransform } from "./transform";
+
+describe(hmrTransform, () => {
+ const examples = [
+ [
+ "basic",
+ `
+export function Test1() {}
+
+export let Test2 = () => {}
+
+export /*some*/ const /*comment*/ Test3 = () => {}
+
+export default function Test4() {}
+`,
+ ],
+ [
+ "separate named export (unsupported)",
+ `
+const SomeNamed = () => {};
+export { SomeNamed };
+`,
+ ],
+ [
+ "separate default export (unsupported)",
+ `
+const SomeDeafult = () => {};
+export default SomeDefault;
+`,
+ ],
+ [
+ "anonymous default export (unsupported)",
+ `
+export default () => {};
+`,
+ ],
+ ] as const;
+
+ examples.forEach(([title, code], i) => {
+ describe(`${i} - ${title}`, () => {
+ it(analyzeExports, async () => {
+ const ast = await parseAstAsync(code);
+ const { exportIds, errors } = analyzeExports(
+ new MagicString(code),
+ ast as any
+ );
+ expect({
+ exportIds,
+ errors: errors.map((e) => code.slice(e.node.start, e.node.end)),
+ }).toMatchSnapshot();
+ });
+
+ it(hmrTransform, async () => {
+ expect(await hmrTransform(code)).toMatchSnapshot();
+ });
+ });
+ });
+});
diff --git a/packages/vite-plugin-simple-hmr/src/index.ts b/packages/vite-plugin-simple-hmr/src/index.ts
new file mode 100644
index 000000000..4c151932c
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/index.ts
@@ -0,0 +1 @@
+export { vitePluginSimpleHmr } from "./plugin";
diff --git a/packages/vite-plugin-simple-hmr/src/plugin.ts b/packages/vite-plugin-simple-hmr/src/plugin.ts
new file mode 100644
index 000000000..97cae966b
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/plugin.ts
@@ -0,0 +1,24 @@
+import { type FilterPattern, type Plugin, createFilter } from "vite";
+import { name as packageName } from "../package.json";
+import { hmrTransform } from "./transform";
+
+export function vitePluginSimpleHmr(pluginOpts: {
+ include?: FilterPattern;
+ exclude?: FilterPattern;
+}): Plugin {
+ const filter = createFilter(
+ pluginOpts.include,
+ pluginOpts.exclude ?? ["**/node_modules/**"]
+ );
+
+ return {
+ name: packageName,
+ apply: "serve",
+ transform(code, id, options) {
+ if (options?.ssr && filter(id)) {
+ return hmrTransform(code);
+ }
+ return;
+ },
+ };
+}
diff --git a/packages/vite-plugin-simple-hmr/src/runtime.ts b/packages/vite-plugin-simple-hmr/src/runtime.ts
new file mode 100644
index 000000000..cc5839eb6
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/runtime.ts
@@ -0,0 +1,66 @@
+import { name as packageName } from "../package.json";
+
+const REGISTRY_KEY = Symbol.for(`${packageName}:registry`);
+
+export interface ViteHot {
+ data: {
+ [REGISTRY_KEY]?: Registry;
+ };
+ accept: (cb: (exports?: unknown) => void) => void;
+ invalidate: (message?: string) => void;
+}
+
+interface Export {
+ value: unknown;
+ update: (next: unknown) => void;
+}
+
+interface Registry {
+ exports: Record;
+ // keep track of all exports of hot update history
+ // since currently "writer" is responsible to keep old module up-to-date
+ // where each old export could be used by other modules at any point in time
+ // but this approach obviously leaks memory indefinitely
+ // (alternative is to let "reader" be responsible for looking up latest module using proxy)
+ history: Record[];
+}
+
+export function createRegistry(): Registry {
+ const exports = {};
+ return { exports, history: [exports] };
+}
+
+function patchRegistry(current: Registry, next: Registry): boolean {
+ // replace all exports in history or full reload
+ const keys = [
+ ...new Set([...Object.keys(current.exports), ...Object.keys(next.exports)]),
+ ];
+ const mismatches = keys.filter(
+ (key) => !(key in current.exports && key in next.exports)
+ );
+ if (mismatches.length > 0) {
+ console.log("[simple-hmr] mismatch: ", mismatches.join(", "));
+ return false;
+ }
+ for (const key of keys) {
+ console.log("[simple-hmr]", key);
+ for (const e of current.history) {
+ e[key]!.update(next.exports[key]!.value);
+ }
+ }
+ next.history = current.history;
+ next.history.push(next.exports);
+ return true;
+}
+
+export function setupHot(hot: ViteHot, registry: Registry) {
+ hot.data[REGISTRY_KEY] = registry;
+
+ hot.accept((newExports) => {
+ const next = hot.data[REGISTRY_KEY];
+ const ok = newExports && next && patchRegistry(registry, next);
+ if (!ok) {
+ hot.invalidate();
+ }
+ });
+}
diff --git a/packages/vite-plugin-simple-hmr/src/transform.ts b/packages/vite-plugin-simple-hmr/src/transform.ts
new file mode 100644
index 000000000..b53bafd64
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/src/transform.ts
@@ -0,0 +1,150 @@
+import type { Node, Program } from "estree";
+import MagicString from "magic-string";
+import { parseAstAsync } from "vite";
+import { name as packageName } from "../package.json";
+
+export async function hmrTransform(code: string) {
+ const magic = new MagicString(code);
+
+ const ast = await parseAstAsync(code);
+ const result = analyzeExports(magic, ast as any);
+
+ if (result.errors.length > 0) {
+ const node = result.errors[0]!.node;
+ const message = "unsupported usage: " + code.slice(node.start, node.end);
+ magic.append("\n" + generateFooterUnsupported(message));
+ } else {
+ magic.append("\n" + generateFooter(result.exportIds));
+ }
+
+ return {
+ code: magic.toString(),
+ map: magic.generateMap(),
+ };
+}
+
+function generateFooter(names: string[]) {
+ const parts = names.map(
+ (name) => `
+ $$registry.exports["${name}"] = {
+ value: ${name},
+ update: ($$next) => {
+ ${name} = $$next;
+ }
+ };
+`
+ );
+
+ // requires dummy "hot.accept" for vite to detect
+ return `
+if (import.meta.env.SSR && import.meta.hot) {
+ const $$hmr = await import("${packageName}/runtime");
+ const $$registry = $$hmr.createRegistry();
+
+${parts.join("\n")}
+
+ $$hmr.setupHot(import.meta.hot, $$registry);
+ import.meta.hot.accept;
+}
+`;
+}
+
+// always invalidate on unsupported usage
+function generateFooterUnsupported(message: string) {
+ return `
+if (import.meta.env.SSR && import.meta.hot) {
+ import.meta.hot.accept(() => {
+ import.meta.hot.invalidate(${JSON.stringify(message)})
+ });
+}
+`;
+}
+
+// traverse export declaration statements based on
+// https://github.com/vitejs/vite/blob/fc2bceb09fb65cc6dc843462f51506586251a703/packages/vite/src/node/ssr/ssrTransform.ts#L172
+
+declare module "estree" {
+ interface BaseNode {
+ start: number;
+ end: number;
+ }
+}
+
+export function analyzeExports(code: MagicString, ast: Program) {
+ // extract exported top-level identifiers
+ const exportIds: string[] = [];
+
+ // unsupported export usage
+ const errors: { node: Node }[] = [];
+
+ for (const node of ast.body) {
+ // named exports
+ if (node.type === "ExportNamedDeclaration") {
+ if (node.declaration) {
+ if (
+ node.declaration.type === "FunctionDeclaration" ||
+ node.declaration.type === "ClassDeclaration"
+ ) {
+ /**
+ * export function foo() {}
+ */
+ exportIds.push(node.declaration.id.name);
+ } else if (node.declaration.type === "VariableDeclaration") {
+ /**
+ * export const foo = 1, bar = 2
+ */
+ if (node.declaration.kind === "const") {
+ // rewrite from "const" to "let"
+ code.remove(node.declaration.start, node.declaration.start + 5);
+ code.appendLeft(node.declaration.start, "let");
+ }
+ for (const decl of node.declaration.declarations) {
+ if (decl.id.type === "Identifier") {
+ exportIds.push(decl.id.name);
+ } else {
+ errors.push({ node: decl });
+ }
+ }
+ }
+ } else {
+ if (node.source) {
+ /**
+ * export { foo, bar } from './foo'
+ */
+ } else {
+ /**
+ * export { foo, bar }
+ */
+ // TODO: support by analyzing scope? or just rewrite all top level `const` into `let`?
+ errors.push({ node });
+ }
+ }
+ }
+
+ // default export
+ if (node.type === "ExportDefaultDeclaration") {
+ if (
+ (node.declaration.type === "FunctionDeclaration" ||
+ node.declaration.type === "ClassExpression") &&
+ node.declaration.id
+ ) {
+ /**
+ * export default function foo() {}
+ * export default class A {}
+ */
+ exportIds.push(node.declaration.id.name);
+ } else {
+ // anonymous default exports
+ errors.push({ node });
+ }
+ }
+
+ /**
+ * export * from './foo'
+ */
+ if (node.type === "ExportAllDeclaration") {
+ }
+ }
+
+ return { exportIds, errors };
+}
diff --git a/packages/vite-plugin-simple-hmr/tsconfig.json b/packages/vite-plugin-simple-hmr/tsconfig.json
new file mode 100644
index 000000000..b42d82b9a
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": ["src"],
+ "compilerOptions": {
+ "jsx": "react-jsx"
+ }
+}
diff --git a/packages/vite-plugin-simple-hmr/tsup.config.ts b/packages/vite-plugin-simple-hmr/tsup.config.ts
new file mode 100644
index 000000000..65c51f39b
--- /dev/null
+++ b/packages/vite-plugin-simple-hmr/tsup.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+ entry: ["src/index.ts", "src/runtime.ts"],
+ format: ["esm"],
+ dts: true,
+ splitting: false,
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 46e20d38b..d8ef7079e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -423,6 +423,41 @@ importers:
specifier: ^5.1.0
version: 5.1.0(@types/node@18.16.18)
+ packages/vite-plugin-simple-hmr:
+ dependencies:
+ magic-string:
+ specifier: ^0.30.6
+ version: 0.30.7
+ devDependencies:
+ '@types/estree':
+ specifier: ^1.0.5
+ version: 1.0.5
+ vite:
+ specifier: ^5.1.0
+ version: 5.1.0(@types/node@18.16.18)
+
+ packages/vite-plugin-simple-hmr/examples/react:
+ dependencies:
+ react:
+ specifier: ^18.2.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^18.2.0
+ version: 18.2.0(react@18.2.0)
+ devDependencies:
+ '@hiogawa/vite-plugin-simple-hmr':
+ specifier: workspace:*
+ version: link:../..
+ '@hiogawa/vite-plugin-ssr-middleware':
+ specifier: workspace:*
+ version: link:../../../vite-plugin-ssr-middleware
+ '@types/react':
+ specifier: 18.2.12
+ version: 18.2.12
+ '@types/react-dom':
+ specifier: 18.2.5
+ version: 18.2.5
+
packages/vite-plugin-ssr-middleware:
devDependencies:
vite:
@@ -2576,7 +2611,7 @@ packages:
rollup:
optional: true
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
estree-walker: 2.0.2
picomatch: 2.3.1
dev: true
@@ -2711,7 +2746,7 @@ packages:
/@types/acorn@4.0.6:
resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
dev: true
/@types/babel__core@7.20.5:
@@ -2768,11 +2803,7 @@ packages:
/@types/estree-jsx@1.0.3:
resolution: {integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==}
dependencies:
- '@types/estree': 1.0.1
- dev: true
-
- /@types/estree@1.0.1:
- resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
+ '@types/estree': 1.0.5
dev: true
/@types/estree@1.0.5:
@@ -2937,7 +2968,7 @@ packages:
colorette: 2.0.20
consola: 3.2.3
fast-glob: 3.3.2
- magic-string: 0.30.5
+ magic-string: 0.30.7
pathe: 1.1.1
perfect-debounce: 1.0.0
transitivePeerDependencies:
@@ -2992,7 +3023,7 @@ packages:
'@unocss/rule-utils': 0.57.7
css-tree: 2.3.1
fast-glob: 3.3.2
- magic-string: 0.30.5
+ magic-string: 0.30.7
postcss: 8.4.35
dev: true
@@ -3095,7 +3126,7 @@ packages:
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.57.7
- magic-string: 0.30.5
+ magic-string: 0.30.7
dev: true
/@unocss/rule-utils@0.58.0:
@@ -3103,7 +3134,7 @@ packages:
engines: {node: '>=14'}
dependencies:
'@unocss/core': 0.58.0
- magic-string: 0.30.5
+ magic-string: 0.30.7
dev: true
/@unocss/scope@0.57.7:
@@ -3161,7 +3192,7 @@ packages:
'@unocss/transformer-directives': 0.57.7
chokidar: 3.5.3
fast-glob: 3.3.2
- magic-string: 0.30.5
+ magic-string: 0.30.7
vite: 5.1.0(@types/node@18.16.18)
transitivePeerDependencies:
- rollup
@@ -3256,7 +3287,7 @@ packages:
/@vitest/snapshot@1.0.4:
resolution: {integrity: sha512-vkfXUrNyNRA/Gzsp2lpyJxh94vU2OHT1amoD6WuvUAA12n32xeVZQ0KjjQIf8F6u7bcq2A2k969fMVxEsxeKYA==}
dependencies:
- magic-string: 0.30.5
+ magic-string: 0.30.7
pathe: 1.1.1
pretty-format: 29.7.0
dev: true
@@ -4323,7 +4354,7 @@ packages:
/estree-util-attach-comments@2.1.1:
resolution: {integrity: sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
dev: true
/estree-util-build-jsx@2.2.2:
@@ -4371,7 +4402,7 @@ packages:
/estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
dev: true
/etag@1.8.1:
@@ -4866,7 +4897,7 @@ packages:
/hast-util-to-estree@2.3.3:
resolution: {integrity: sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
'@types/estree-jsx': 1.0.3
'@types/hast': 2.3.8
'@types/unist': 2.0.10
@@ -5152,7 +5183,7 @@ packages:
/is-reference@3.0.2:
resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
dev: true
/is-regex@1.1.4:
@@ -5474,6 +5505,12 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
+ /magic-string@0.30.7:
+ resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.15
+
/map-obj@1.0.1:
resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
engines: {node: '>=0.10.0'}
@@ -5701,7 +5738,7 @@ packages:
/micromark-extension-mdx-expression@1.0.8:
resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
micromark-factory-mdx-expression: 1.0.9
micromark-factory-space: 1.1.0
micromark-util-character: 1.2.0
@@ -5715,7 +5752,7 @@ packages:
resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==}
dependencies:
'@types/acorn': 4.0.6
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
estree-util-is-identifier-name: 2.1.0
micromark-factory-mdx-expression: 1.0.9
micromark-factory-space: 1.1.0
@@ -5735,7 +5772,7 @@ packages:
/micromark-extension-mdxjs-esm@1.0.5:
resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
micromark-core-commonmark: 1.1.0
micromark-util-character: 1.2.0
micromark-util-events-to-acorn: 1.2.3
@@ -5779,7 +5816,7 @@ packages:
/micromark-factory-mdx-expression@1.0.9:
resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
micromark-util-character: 1.2.0
micromark-util-events-to-acorn: 1.2.3
micromark-util-symbol: 1.1.0
@@ -5865,7 +5902,7 @@ packages:
resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==}
dependencies:
'@types/acorn': 4.0.6
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
'@types/unist': 2.0.10
estree-util-visit: 1.2.1
micromark-util-symbol: 1.1.0
@@ -6469,7 +6506,7 @@ packages:
/periscopic@3.1.0:
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
dependencies:
- '@types/estree': 1.0.1
+ '@types/estree': 1.0.5
estree-walker: 3.0.3
is-reference: 3.0.2
dev: true