mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-20 22:51:17 -05:00
Introduce the concept of a Dynamic File
Dynamic files are generally created by the debugger when while debugging it can't find a matching file on disk. Since these files don't exist on disk, we shouldn't check if the file exists on disk, and allow the content to be controlled by the host.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
namespace ts.server {
|
||||
|
||||
export function shouldEmitFile(scriptInfo: ScriptInfo) {
|
||||
return !scriptInfo.hasMixedContent;
|
||||
return !scriptInfo.hasMixedContent && !scriptInfo.isDynamic;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +188,7 @@ namespace ts.server {
|
||||
*/
|
||||
getFilesAffectedBy(scriptInfo: ScriptInfo): string[] {
|
||||
const info = this.getOrCreateFileInfo(scriptInfo.path);
|
||||
const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName];
|
||||
const singleFileResult = scriptInfo.hasMixedContent || scriptInfo.isDynamic ? [] : [scriptInfo.fileName];
|
||||
if (info.updateShapeSignature()) {
|
||||
const options = this.project.getCompilerOptions();
|
||||
// If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
|
||||
@@ -303,7 +303,7 @@ namespace ts.server {
|
||||
getFilesAffectedBy(scriptInfo: ScriptInfo): string[] {
|
||||
this.ensureProjectDependencyGraphUpToDate();
|
||||
|
||||
const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName];
|
||||
const singleFileResult = scriptInfo.hasMixedContent || scriptInfo.isDynamic ? [] : [scriptInfo.fileName];
|
||||
const fileInfo = this.getFileInfo(scriptInfo.path);
|
||||
if (!fileInfo || !fileInfo.updateShapeSignature()) {
|
||||
return singleFileResult;
|
||||
|
||||
@@ -234,18 +234,21 @@ namespace ts.server {
|
||||
getFileName(f: T): string;
|
||||
getScriptKind(f: T): ScriptKind;
|
||||
hasMixedContent(f: T, extraFileExtensions: JsFileExtensionInfo[]): boolean;
|
||||
isDynamicFile(f: T): boolean;
|
||||
}
|
||||
|
||||
const fileNamePropertyReader: FilePropertyReader<string> = {
|
||||
getFileName: x => x,
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)),
|
||||
isDynamicFile: x => x[0] == '^',
|
||||
};
|
||||
|
||||
const externalFilePropertyReader: FilePropertyReader<protocol.ExternalFile> = {
|
||||
getFileName: x => x.fileName,
|
||||
getScriptKind: x => tryConvertScriptKindName(x.scriptKind),
|
||||
hasMixedContent: x => x.hasMixedContent
|
||||
hasMixedContent: x => x.hasMixedContent,
|
||||
isDynamicFile: x => x.fileName[0] == '^',
|
||||
};
|
||||
|
||||
function findProjectByName<T extends Project>(projectName: string, projects: T[]): T {
|
||||
@@ -1177,15 +1180,16 @@ namespace ts.server {
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): void {
|
||||
let errors: Diagnostic[];
|
||||
for (const f of files) {
|
||||
const rootFilename = propertyReader.getFileName(f);
|
||||
const rootFileName = propertyReader.getFileName(f);
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
if (this.host.fileExists(rootFilename)) {
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName === rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
const isDynamicFile = propertyReader.isDynamicFile(f);
|
||||
if (isDynamicFile || this.host.fileExists(rootFileName)) {
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFileName), /*openedByClient*/ clientFileName === rootFileName, /*fileContent*/ undefined, scriptKind, hasMixedContent, isDynamicFile);
|
||||
project.addRoot(info);
|
||||
}
|
||||
else {
|
||||
(errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFilename));
|
||||
(errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFileName));
|
||||
}
|
||||
}
|
||||
project.setProjectErrors(concatenate(configFileErrors, errors));
|
||||
@@ -1215,7 +1219,8 @@ namespace ts.server {
|
||||
let rootFilesChanged = false;
|
||||
for (const f of newUncheckedFiles) {
|
||||
const newRootFile = propertyReader.getFileName(f);
|
||||
if (!this.host.fileExists(newRootFile)) {
|
||||
const isDynamic = propertyReader.isDynamicFile(f);
|
||||
if (!isDynamic && !this.host.fileExists(newRootFile)) {
|
||||
(projectErrors || (projectErrors = [])).push(createFileNotFoundDiagnostic(newRootFile));
|
||||
continue;
|
||||
}
|
||||
@@ -1226,7 +1231,7 @@ namespace ts.server {
|
||||
if (!scriptInfo) {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, isDynamic);
|
||||
}
|
||||
}
|
||||
newRootScriptInfos.push(scriptInfo);
|
||||
@@ -1410,17 +1415,17 @@ namespace ts.server {
|
||||
|
||||
watchClosedScriptInfo(info: ScriptInfo) {
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (!info.hasMixedContent) {
|
||||
if (!info.hasMixedContent && !info.isDynamic) {
|
||||
const { fileName } = info;
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
}
|
||||
}
|
||||
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) {
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, isDynamic?: boolean) {
|
||||
let info = this.getScriptInfoForNormalizedPath(fileName);
|
||||
if (!info) {
|
||||
if (openedByClient || this.host.fileExists(fileName)) {
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent);
|
||||
if (openedByClient || isDynamic || this.host.fileExists(fileName)) {
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, isDynamic);
|
||||
|
||||
this.filenameToScriptInfo.set(info.path, info);
|
||||
|
||||
@@ -1430,6 +1435,7 @@ namespace ts.server {
|
||||
fileContent = this.host.readFile(fileName) || "";
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
this.watchClosedScriptInfo(info);
|
||||
}
|
||||
|
||||
@@ -156,11 +156,12 @@ namespace ts.server {
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
readonly scriptKind: ScriptKind,
|
||||
public hasMixedContent = false) {
|
||||
public hasMixedContent = false,
|
||||
public isDynamic = false) {
|
||||
|
||||
this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames));
|
||||
this.textStorage = new TextStorage(host, fileName);
|
||||
if (hasMixedContent) {
|
||||
if (hasMixedContent || isDynamic) {
|
||||
this.textStorage.reload("");
|
||||
}
|
||||
this.scriptKind = scriptKind
|
||||
@@ -180,7 +181,7 @@ namespace ts.server {
|
||||
|
||||
public close() {
|
||||
this.isOpen = false;
|
||||
this.textStorage.useText(this.hasMixedContent ? "" : undefined);
|
||||
this.textStorage.useText(this.hasMixedContent || this.isDynamic ? "" : undefined);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
@@ -307,7 +308,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
reloadFromFile(tempFileName?: NormalizedPath) {
|
||||
if (this.hasMixedContent) {
|
||||
if (this.hasMixedContent || this.isDynamic) {
|
||||
this.reload("");
|
||||
}
|
||||
else {
|
||||
@@ -354,4 +355,4 @@ namespace ts.server {
|
||||
return this.scriptKind === ScriptKind.JS || this.scriptKind === ScriptKind.JSX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeMapLikes(target: MapLike<any>, source: MapLike <any>): void {
|
||||
export function mergeMapLikes(target: MapLike<any>, source: MapLike<any>): void {
|
||||
for (const key in source) {
|
||||
if (hasProperty(source, key)) {
|
||||
target[key] = source[key];
|
||||
@@ -115,9 +115,9 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
/* tslint:disable:no-null-keyword */
|
||||
const map = createMap<T>();
|
||||
/* tslint:enable:no-null-keyword */
|
||||
/* tslint:enable:no-null-keyword */
|
||||
return {
|
||||
get(path) {
|
||||
return map.get(path);
|
||||
@@ -290,4 +290,4 @@ namespace ts.server {
|
||||
deleted(oldItems[oldIndex++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user