vite-plugin-mirrorstate 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +95 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ const logger = debug("mirrorstate:vite-plugin");
8
8
  export function mirrorStatePlugin(options = {}) {
9
9
  const opts = {
10
10
  path: "/mirrorstate",
11
- filePattern: "**/*.mirror.json",
11
+ filePattern: ["*.mirror.json", "**/*.mirror.json"],
12
12
  prettyPrint: true,
13
13
  ...options,
14
14
  };
@@ -16,6 +16,7 @@ export function mirrorStatePlugin(options = {}) {
16
16
  let watcher;
17
17
  let recentWrites = new Set(); // Track recent writes to prevent echo
18
18
  let lastMessageHash = new Map(); // Track last message hash per client to prevent duplicates
19
+ let watcherReady = false; // Track if watcher has finished initial scan
19
20
  return {
20
21
  name: "vite-plugin-mirrorstate",
21
22
  configureServer(server) {
@@ -28,22 +29,91 @@ export function mirrorStatePlugin(options = {}) {
28
29
  });
29
30
  }
30
31
  });
31
- watcher = chokidar.watch(opts.filePattern, {
32
+ const baseDir = server.config.root || process.cwd();
33
+ const pattern = Array.isArray(opts.filePattern)
34
+ ? opts.filePattern.map((p) => path.join(baseDir, p))
35
+ : [path.join(baseDir, opts.filePattern)];
36
+ // Find all existing files matching the pattern
37
+ const existingFiles = pattern.flatMap((p) => glob.sync(p, {
38
+ ignore: "node_modules/**",
39
+ }));
40
+ logger(`Setting up file watcher for ${existingFiles.length} files: ${JSON.stringify(existingFiles)}`);
41
+ // Watch both existing files AND the directory for new files
42
+ const watchTargets = [...existingFiles, baseDir];
43
+ watcher = chokidar.watch(watchTargets, {
32
44
  ignored: /node_modules/,
33
45
  persistent: true,
34
46
  ...opts.watchOptions,
35
47
  });
48
+ watcher.on("add", (filePath) => {
49
+ // Only process .mirror.json files added after initial scan
50
+ if (!watcherReady || !filePath.endsWith(".mirror.json")) {
51
+ return;
52
+ }
53
+ try {
54
+ const relativePath = path.relative(baseDir, filePath);
55
+ const content = fs.readFileSync(filePath, "utf8");
56
+ const data = JSON.parse(content);
57
+ const name = relativePath.replace(/\.mirror\.json$/, "");
58
+ // Send new state to all connected clients
59
+ wss.clients.forEach((client) => {
60
+ if (client.readyState === client.OPEN) {
61
+ client.send(JSON.stringify({
62
+ type: "initialState",
63
+ name,
64
+ state: data,
65
+ }));
66
+ }
67
+ });
68
+ // Invalidate the virtual module for HMR
69
+ const mod = server.moduleGraph.getModuleById("\0virtual:mirrorstate/initial-states");
70
+ if (mod) {
71
+ server.moduleGraph.invalidateModule(mod);
72
+ }
73
+ logger(`New mirror file added: ${name}`);
74
+ }
75
+ catch (error) {
76
+ console.error(`Error reading new mirror file ${filePath}:`, error);
77
+ }
78
+ });
79
+ watcher.on("unlink", (filePath) => {
80
+ // Only process .mirror.json files
81
+ if (!filePath.endsWith(".mirror.json")) {
82
+ return;
83
+ }
84
+ try {
85
+ const relativePath = path.relative(baseDir, filePath);
86
+ const name = relativePath.replace(/\.mirror\.json$/, "");
87
+ // Invalidate the virtual module for HMR
88
+ const mod = server.moduleGraph.getModuleById("\0virtual:mirrorstate/initial-states");
89
+ if (mod) {
90
+ server.moduleGraph.invalidateModule(mod);
91
+ }
92
+ logger(`Mirror file deleted: ${name}`);
93
+ }
94
+ catch (error) {
95
+ console.error(`Error handling mirror file deletion ${filePath}:`, error);
96
+ }
97
+ });
98
+ watcher.on("ready", () => {
99
+ watcherReady = true;
100
+ logger("File watcher is ready");
101
+ });
36
102
  logger(`MirrorState WebSocket listening on ws://localhost:${server.config.server.port || 5173}${wsPath}`);
37
103
  watcher.on("change", (filePath) => {
104
+ // Only watch .mirror.json files
105
+ if (!filePath.endsWith(".mirror.json")) {
106
+ return;
107
+ }
38
108
  try {
109
+ const relativePath = path.relative(baseDir, filePath);
39
110
  // Skip if this was a recent write from WebSocket to prevent echo
40
- if (recentWrites.has(filePath)) {
41
- recentWrites.delete(filePath);
111
+ if (recentWrites.has(relativePath)) {
112
+ recentWrites.delete(relativePath);
42
113
  return;
43
114
  }
44
115
  const content = fs.readFileSync(filePath, "utf8");
45
116
  const data = JSON.parse(content);
46
- const relativePath = path.relative(server.config.root || process.cwd(), filePath);
47
117
  const name = relativePath.replace(/\.mirror\.json$/, "");
48
118
  // This is an external file change (from editor, etc.)
49
119
  wss.clients.forEach((client) => {
@@ -57,7 +127,7 @@ export function mirrorStatePlugin(options = {}) {
57
127
  }
58
128
  });
59
129
  // Invalidate the virtual module for HMR
60
- const mod = server.moduleGraph.getModuleById("virtual:mirrorstate/initial-states");
130
+ const mod = server.moduleGraph.getModuleById("\0virtual:mirrorstate/initial-states");
61
131
  if (mod) {
62
132
  server.moduleGraph.invalidateModule(mod);
63
133
  }
@@ -73,9 +143,11 @@ export function mirrorStatePlugin(options = {}) {
73
143
  ws.clientId = clientId;
74
144
  logger(`Client connected to MirrorState (${clientId})`);
75
145
  const pattern = Array.isArray(opts.filePattern)
76
- ? opts.filePattern
77
- : [opts.filePattern];
78
- const mirrorFiles = pattern.flatMap((p) => glob.sync(p, { ignore: "node_modules/**" }));
146
+ ? opts.filePattern.map((p) => path.join(baseDir, p))
147
+ : [path.join(baseDir, opts.filePattern)];
148
+ const mirrorFiles = pattern.flatMap((p) => glob.sync(p, {
149
+ ignore: "node_modules/**",
150
+ }));
79
151
  mirrorFiles.forEach((filePath) => {
80
152
  try {
81
153
  const content = fs.readFileSync(filePath, "utf8");
@@ -107,27 +179,28 @@ export function mirrorStatePlugin(options = {}) {
107
179
  }
108
180
  // Update last message hash for this client
109
181
  lastMessageHash.set(clientId, messageHash);
110
- const filePath = `${name}.mirror.json`;
182
+ const baseDir = server.config.root || process.cwd();
183
+ const relativeFilePath = `${name}.mirror.json`;
184
+ const filePath = path.join(baseDir, relativeFilePath);
111
185
  const jsonContent = opts.prettyPrint
112
186
  ? JSON.stringify(state, null, 2)
113
187
  : JSON.stringify(state);
114
- // Mark this as a recent write to prevent file watcher echo
115
- recentWrites.add(filePath);
116
188
  // Write state to file
117
189
  fs.writeFileSync(filePath, jsonContent);
190
+ // Mark this as a recent write to prevent file watcher echo
191
+ // (only after successful write)
192
+ recentWrites.add(relativeFilePath);
118
193
  // Invalidate the virtual module for HMR
119
- const mod = server.moduleGraph.getModuleById("virtual:mirrorstate/initial-states");
194
+ const mod = server.moduleGraph.getModuleById("\0virtual:mirrorstate/initial-states");
120
195
  if (mod) {
121
196
  server.moduleGraph.invalidateModule(mod);
122
197
  }
123
198
  // Broadcast to other clients (exclude sender to prevent echo)
124
199
  wss.clients.forEach((client) => {
125
200
  if (client !== ws && client.readyState === client.OPEN) {
126
- const relativePath = path.relative(server.config.root || process.cwd(), filePath);
127
- const fileName = relativePath.replace(/\.mirror\.json$/, "");
128
201
  client.send(JSON.stringify({
129
202
  type: "fileChange",
130
- name: fileName,
203
+ name,
131
204
  state: state,
132
205
  source: clientId,
133
206
  }));
@@ -160,10 +233,13 @@ export function mirrorStatePlugin(options = {}) {
160
233
  }
161
234
  if (id === "\0virtual:mirrorstate/initial-states") {
162
235
  // During build, read all mirror files and inline them
236
+ const baseDir = process.cwd();
163
237
  const pattern = Array.isArray(opts.filePattern)
164
- ? opts.filePattern
165
- : [opts.filePattern];
166
- const mirrorFiles = pattern.flatMap((p) => glob.sync(p, { ignore: "node_modules/**" }));
238
+ ? opts.filePattern.map((p) => path.join(baseDir, p))
239
+ : [path.join(baseDir, opts.filePattern)];
240
+ const mirrorFiles = pattern.flatMap((p) => glob.sync(p, {
241
+ ignore: "node_modules/**",
242
+ }));
167
243
  const states = {};
168
244
  mirrorFiles.forEach((filePath) => {
169
245
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-mirrorstate",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Vite plugin for bidirectional state synchronization through *.mirror.json files",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",