Skip to content

Commit 8b5fc9a

Browse files
committed
feat(vue-component-meta): add updateFile, deleteFile apis for HMR supports
close #1889
1 parent ed43ee9 commit 8b5fc9a

File tree

1 file changed

+67
-29
lines changed
  • vue-language-tools/vue-component-meta/src

1 file changed

+67
-29
lines changed

vue-language-tools/vue-component-meta/src/index.ts

+67-29
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const extraFileExtensions: ts.FileExtensionInfo[] = [{
3131
scriptKind: ts.ScriptKind.Deferred,
3232
}];
3333

34-
export type ComponentMetaChecker = ReturnType<typeof createComponentMetaCheckerBase>;
34+
export type ComponentMetaChecker = ReturnType<typeof baseCreate>;
3535

3636
export function createComponentMetaCheckerByJsonConfig(root: string, json: any, checkerOptions: MetaCheckerOptions = {}) {
3737

@@ -41,7 +41,7 @@ export function createComponentMetaCheckerByJsonConfig(root: string, json: any,
4141
console.error(error);
4242
}
4343

44-
return createComponentMetaCheckerBase(root + '/jsconfig.json', parsedCommandLine, checkerOptions);
44+
return baseCreate(parsedCommandLine, checkerOptions);
4545
}
4646

4747
export function createComponentMetaChecker(tsconfigPath: string, checkerOptions: MetaCheckerOptions = {}) {
@@ -52,49 +52,77 @@ export function createComponentMetaChecker(tsconfigPath: string, checkerOptions:
5252
console.error(error);
5353
}
5454

55-
return createComponentMetaCheckerBase(tsconfigPath, parsedCommandLine, checkerOptions);
55+
return baseCreate(parsedCommandLine, checkerOptions);
5656
}
5757

58-
function createComponentMetaCheckerBase(tsconfigPath: string, parsedCommandLine: vue.ParsedCommandLine, checkerOptions: MetaCheckerOptions) {
58+
function baseCreate(parsedCommandLine: vue.ParsedCommandLine, checkerOptions: MetaCheckerOptions) {
5959

60-
const scriptSnapshot: Record<string, ts.IScriptSnapshot> = {};
61-
const globalComponentName = tsconfigPath.replace(/\\/g, '/') + '.global.vue';
62-
const host: vue.LanguageServiceHost = {
60+
/**
61+
* Original Host
62+
*/
63+
const scriptSnapshots = new Map<string, ts.IScriptSnapshot>();
64+
const scriptVersions = new Map<string, number>();
65+
let projectVersion = 0;
66+
const _host: vue.LanguageServiceHost = {
6367
...ts.sys,
68+
getProjectVersion: () => projectVersion.toString(),
6469
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options), // should use ts.getDefaultLibFilePath not ts.getDefaultLibFileName
6570
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
6671
getCompilationSettings: () => parsedCommandLine.options,
67-
getScriptFileNames: () => {
68-
return [
69-
...parsedCommandLine.fileNames,
70-
...parsedCommandLine.fileNames.map(getMetaFileName),
71-
globalComponentName,
72-
getMetaFileName(globalComponentName),
73-
];
74-
},
72+
getScriptFileNames: () => parsedCommandLine.fileNames,
7573
getProjectReferences: () => parsedCommandLine.projectReferences,
76-
getScriptVersion: (fileName) => '0',
74+
getScriptVersion: (fileName) => scriptVersions.get(fileName)?.toString() ?? '',
7775
getScriptSnapshot: (fileName) => {
78-
if (!scriptSnapshot[fileName]) {
79-
let fileText: string | undefined;
80-
if (isMetaFileName(fileName)) {
81-
fileText = getMetaScriptContent(fileName);
82-
}
83-
else if (fileName === globalComponentName) {
84-
fileText = `<script setup lang="ts"></script>`;
85-
}
86-
else {
87-
fileText = ts.sys.readFile(fileName);
88-
}
76+
if (!scriptSnapshots.has(fileName)) {
77+
const fileText = ts.sys.readFile(fileName);
8978
if (fileText !== undefined) {
90-
scriptSnapshot[fileName] = ts.ScriptSnapshot.fromString(fileText);
79+
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(fileText));
9180
}
9281
}
93-
return scriptSnapshot[fileName];
82+
return scriptSnapshots.get(fileName);
9483
},
9584
getTypeScriptModule: () => ts,
9685
getVueCompilationSettings: () => parsedCommandLine.vueOptions,
9786
};
87+
88+
/**
89+
* Meta
90+
*/
91+
const globalComponentName = _host.getCurrentDirectory().replace(/\\/g, '/') + '/__global.vue';
92+
const globalComponentSnapshot = ts.ScriptSnapshot.fromString('<script setup lang="ts"></script>');
93+
const metaSnapshots: Record<string, ts.IScriptSnapshot> = {};
94+
const host = new Proxy<Partial<vue.LanguageServiceHost>>({
95+
getScriptFileNames: () => {
96+
const names = _host.getScriptFileNames();
97+
return [
98+
...names,
99+
...names.map(getMetaFileName),
100+
globalComponentName,
101+
getMetaFileName(globalComponentName),
102+
];
103+
},
104+
getScriptSnapshot: fileName => {
105+
if (isMetaFileName(fileName)) {
106+
if (!metaSnapshots[fileName]) {
107+
metaSnapshots[fileName] = ts.ScriptSnapshot.fromString(getMetaScriptContent(fileName));
108+
}
109+
return metaSnapshots[fileName];
110+
}
111+
else if (fileName === globalComponentName) {
112+
return globalComponentSnapshot;
113+
}
114+
else {
115+
return _host.getScriptSnapshot(fileName);
116+
}
117+
},
118+
}, {
119+
get(target, prop) {
120+
if (prop in target) {
121+
return target[prop as keyof typeof target];
122+
}
123+
return _host[prop as keyof typeof _host];
124+
},
125+
}) as vue.LanguageServiceHost;
98126
const vueLanguageModule = vue.createEmbeddedLanguageModule(
99127
host.getTypeScriptModule(),
100128
host.getCurrentDirectory(),
@@ -131,7 +159,17 @@ function createComponentMetaCheckerBase(tsconfigPath: string, parsedCommandLine:
131159
return {
132160
getExportNames,
133161
getComponentMeta,
162+
updateFile(fileName: string, text: string) {
163+
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(text));
164+
scriptVersions.set(fileName, scriptVersions.has(fileName) ? scriptVersions.get(fileName)! + 1 : 1);
165+
projectVersion++;
166+
},
167+
deleteFile(fileName: string) {
168+
parsedCommandLine.fileNames = parsedCommandLine.fileNames.filter(f => f !== fileName);
169+
projectVersion++;
170+
},
134171
__internal__: {
172+
parsedCommandLine,
135173
program,
136174
tsLs,
137175
typeChecker,

0 commit comments

Comments
 (0)