Skip to content

Commit 4c61bbd

Browse files
Merge pull request #96 from HichemTab-tech/fix-vite-dev-mode
Omptimise/fix parsing in vite dev mode
2 parents 416c203 + 12acd19 commit 4c61bbd

3 files changed

Lines changed: 100 additions & 6 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-runtime-picker",
3-
"version": "2.1.3",
3+
"version": "2.2.0",
44
"main": "dist/index.js",
55
"exports": {
66
".": {

src/ts-transformer.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
import { Project, SyntaxKind } from "ts-morph";
22

3-
const project = new Project({
4-
tsConfigFilePath: "tsconfig.json",
5-
});
3+
export const fileToTypes = new Map<string, Set<string>>();
4+
5+
export const typeToFile = new Map<string, string>();
6+
7+
let project = null as unknown as Project;
8+
9+
export const refreshProject = () => {
10+
project = new Project({
11+
tsConfigFilePath: "tsconfig.json",
12+
});
13+
}
14+
15+
export const invalidateOneFile = (file: string) => {
16+
17+
const sourceFileToRefresh = project.getSourceFile(file);
18+
if (sourceFileToRefresh) {
19+
sourceFileToRefresh.refreshFromFileSystemSync();
20+
return true;
21+
} else {
22+
return false;
23+
}
24+
}
625

726
export function transform(code: string, filePath: string): string {
827

28+
const usedTypes = new Set<string>();
29+
930
let sourceFile = project.getSourceFile(filePath);
1031

1132
if (!sourceFile) {
@@ -16,6 +37,20 @@ export function transform(code: string, filePath: string): string {
1637
sourceFile.replaceWithText(code);
1738
}
1839

40+
sourceFile.getDescendantsOfKind(SyntaxKind.TypeReference).forEach(ref => {
41+
const typeName = ref.getTypeName().getText();
42+
usedTypes.add(typeName);
43+
44+
// BONUS: resolve where this type is defined
45+
const decl = ref.getType().getSymbol()?.getDeclarations()?.[0];
46+
const source = decl?.getSourceFile()?.getFilePath();
47+
if (source) {
48+
typeToFile.set(typeName, source);
49+
}
50+
});
51+
52+
fileToTypes.set(filePath, usedTypes);
53+
1954
// Figure out what the “createPicker” import is called (alias or direct)
2055
let createPickerAlias: string | null = null;
2156
for (const importDecl of sourceFile.getImportDeclarations()) {
@@ -63,4 +98,6 @@ export function transform(code: string, filePath: string): string {
6398
}
6499

65100
return sourceFile.getFullText();
66-
}
101+
}
102+
103+
refreshProject();

src/vite-plugin.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PluginOption } from "vite";
2-
import { transform } from "./ts-transformer.js";
2+
import {fileToTypes, invalidateOneFile, transform, typeToFile} from "./ts-transformer.js";
33

44
// noinspection JSUnusedGlobalSymbols
55
/**
@@ -27,6 +27,63 @@ export default function TsRuntimePickerVitePlugin(): PluginOption {
2727

2828
return null;
2929
},
30+
31+
handleHotUpdate: ({server, file, timestamp}) => {
32+
const isKnownTypeDefinitionFile = [...typeToFile.values()].includes(file);
33+
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`).
48+
for (const [userFile, usedTypes] of fileToTypes.entries()) {
49+
for (const typeName of usedTypes) {
50+
if (typeToFile.get(typeName) === file) {
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+
}
58+
}
59+
}
60+
}
61+
62+
if (dependentModulesToUpdate.length > 0) {
63+
console.log(`[ts-runtime-picker] Found ${dependentModulesToUpdate.length} module(s) to update.`);
64+
65+
//Invalidate modules and notify the client
66+
for (const mod of dependentModulesToUpdate) {
67+
server.moduleGraph.invalidateModule(mod);
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+
});
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;
86+
}
3087
} as PluginOption;
3188
}
3289

0 commit comments

Comments
 (0)