|
1 | 1 | import { PluginOption } from "vite"; |
2 | | -import {fileToTypes, refreshProject, transform, typeToFile} from "./ts-transformer.js"; |
| 2 | +import {fileToTypes, invalidateOneFile, transform, typeToFile} from "./ts-transformer.js"; |
3 | 3 |
|
4 | 4 | // noinspection JSUnusedGlobalSymbols |
5 | 5 | /** |
@@ -28,40 +28,61 @@ export default function TsRuntimePickerVitePlugin(): PluginOption { |
28 | 28 | return null; |
29 | 29 | }, |
30 | 30 |
|
31 | | - handleHotUpdate: ({server, file}) => { |
32 | | - const affectedFiles = []; |
| 31 | + handleHotUpdate: ({server, file, timestamp}) => { |
| 32 | + const isKnownTypeDefinitionFile = [...typeToFile.values()].includes(file); |
33 | 33 |
|
34 | | - // Loop over all files that used types |
| 34 | + if (!isKnownTypeDefinitionFile) { |
| 35 | + // This file change doesn't affect any of our transformations, so we can ignore it. |
| 36 | + return; |
| 37 | + } |
| 38 | + |
| 39 | + console.log(`[ts-runtime-picker] Change detected in a tracked type definition: ${file}`); |
| 40 | + |
| 41 | + |
| 42 | + // Surgically update the ts-morph project |
| 43 | + if (!invalidateOneFile(file)) return; |
| 44 | + |
| 45 | + const dependentModulesToUpdate = []; |
| 46 | + |
| 47 | + // Find all files (`userFile`) that import a type from the file that just changed (`file`). |
35 | 48 | for (const [userFile, usedTypes] of fileToTypes.entries()) { |
36 | 49 | for (const typeName of usedTypes) { |
37 | 50 | if (typeToFile.get(typeName) === file) { |
38 | | - affectedFiles.push(userFile); |
39 | | - break; |
| 51 | + // This `userFile` depends on the changed type. We need to update it. |
| 52 | + const moduleNode = server.moduleGraph.getModuleById(userFile); |
| 53 | + if (moduleNode) { |
| 54 | + dependentModulesToUpdate.push(moduleNode); |
| 55 | + // Once we know the file is affected, we don't need to check its other types. |
| 56 | + break; |
| 57 | + } |
40 | 58 | } |
41 | 59 | } |
42 | 60 | } |
43 | 61 |
|
44 | | - if (affectedFiles.length) { |
45 | | - refreshProject(); |
| 62 | + if (dependentModulesToUpdate.length > 0) { |
| 63 | + console.log(`[ts-runtime-picker] Found ${dependentModulesToUpdate.length} module(s) to update.`); |
46 | 64 |
|
47 | | - for (const file of affectedFiles) { |
48 | | - const mod = server.moduleGraph.getModuleById(file); |
49 | | - if (mod) { |
50 | | - server.moduleGraph.invalidateModule(mod); |
51 | | - server.ws.send({ |
52 | | - type: 'update', |
53 | | - updates: [ |
54 | | - { |
55 | | - type: 'js-update', |
56 | | - path: mod.url, // relative to root |
57 | | - acceptedPath: mod.url, |
58 | | - timestamp: Date.now() |
59 | | - } |
60 | | - ] |
61 | | - }); |
62 | | - } |
| 65 | + //Invalidate modules and notify the client |
| 66 | + for (const mod of dependentModulesToUpdate) { |
| 67 | + server.moduleGraph.invalidateModule(mod); |
63 | 68 | } |
| 69 | + |
| 70 | + // This sends the HMR update to the client (browser), telling it which |
| 71 | + // modules have changed so it can re-request them. |
| 72 | + server.ws.send({ |
| 73 | + type: 'update', |
| 74 | + updates: dependentModulesToUpdate.map((mod) => ({ |
| 75 | + type: 'js-update', |
| 76 | + path: mod.url, |
| 77 | + acceptedPath: mod.url, |
| 78 | + timestamp: timestamp |
| 79 | + })) |
| 80 | + }); |
64 | 81 | } |
| 82 | + |
| 83 | + // We return the list of modules we've handled so Vite's core HMR logic |
| 84 | + // doesn't try to process them again. |
| 85 | + return dependentModulesToUpdate; |
65 | 86 | } |
66 | 87 | } as PluginOption; |
67 | 88 | } |
|
0 commit comments