Merge branch 'master' into lazyConfiguredProjectsFromExternalProject

This commit is contained in:
Sheetal Nandi 2018-08-28 14:49:00 -07:00
commit 52fef42baa
58 changed files with 2304 additions and 900 deletions

View file

@ -370,7 +370,9 @@ namespace ts {
finally {
cancellationToken = undefined;
}
}
},
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
};
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, isForSignatureHelp: boolean): Signature | undefined {
@ -1208,17 +1210,23 @@ namespace ts {
// local types not visible outside the function body
: false;
}
if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult =
if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.Variable) {
// parameter initializer will lookup as normal variable scope when targeting es2015+
if (compilerOptions.target && compilerOptions.target >= ScriptTarget.ES2015 && isParameter(lastLocation) && result.valueDeclaration !== lastLocation) {
useResult = false;
}
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult =
lastLocation.kind === SyntaxKind.Parameter ||
(
lastLocation === (<FunctionLikeDeclaration>location).type &&
!!findAncestor(result.valueDeclaration, isParameter)
);
}
}
}
else if (location.kind === SyntaxKind.ConditionalType) {
@ -2280,8 +2288,6 @@ namespace ts {
return getPackagesSet().has(getTypesPackageName(packageName));
}
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol;
function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined;
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
@ -3920,13 +3926,22 @@ namespace ts {
const links = getSymbolLinks(symbol);
let specifier = links.specifierCache && links.specifierCache.get(contextFile.path);
if (!specifier) {
specifier = moduleSpecifiers.getModuleSpecifierForDeclarationFile(
const isBundle = (compilerOptions.out || compilerOptions.outFile);
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
// just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this
// using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative
// specifier preference
const { moduleResolverHost } = context.tracker;
const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions;
specifier = first(first(moduleSpecifiers.getModuleSpecifiers(
symbol,
compilerOptions,
specifierCompilerOptions,
contextFile,
context.tracker.moduleResolverHost,
moduleResolverHost,
host.getSourceFiles(),
{ importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" },
host.redirectTargetsMap,
);
)));
links.specifierCache = links.specifierCache || createMap();
links.specifierCache.set(contextFile.path, specifier);
}

View file

@ -815,10 +815,11 @@ namespace ts {
}
function getOptionNameMap(): OptionNameMap {
if (optionNameMapCache) {
return optionNameMapCache;
}
return optionNameMapCache || (optionNameMapCache = createOptionNameMap(optionDeclarations));
}
/*@internal*/
export function createOptionNameMap(optionDeclarations: ReadonlyArray<CommandLineOption>): OptionNameMap {
const optionNameMap = createMap<CommandLineOption>();
const shortOptionNames = createMap<string>();
forEach(optionDeclarations, option => {
@ -828,8 +829,7 @@ namespace ts {
}
});
optionNameMapCache = { optionNameMap, shortOptionNames };
return optionNameMapCache;
return { optionNameMap, shortOptionNames };
}
/* @internal */
@ -979,7 +979,12 @@ namespace ts {
}
/** @internal */
export function getOptionFromName(optionName: string, allowShort = false): CommandLineOption | undefined {
export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined {
return getOptionDeclarationFromName(getOptionNameMap, optionName, allowShort);
}
/*@internal*/
export function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
optionName = optionName.toLowerCase();
const { optionNameMap, shortOptionNames } = getOptionNameMap();
// Try to translate short option names to their full equivalents.

View file

@ -2896,6 +2896,11 @@
"category": "Error",
"code": 5071
},
"Unknown build option '{0}'.": {
"category": "Error",
"code": 5072
},
"Generates a sourcemap for each corresponding '.d.ts' file.": {
"category": "Message",

View file

@ -1,8 +1,52 @@
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
/* @internal */
namespace ts.moduleSpecifiers {
export interface ModuleSpecifierPreferences {
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
const enum RelativePreference { Relative, NonRelative, Auto }
// See UserPreferences#importPathEnding
const enum Ending { Minimal, Index, JsExtension }
// Processed preferences
interface Preferences {
readonly relativePreference: RelativePreference;
readonly ending: Ending;
}
function getPreferences({ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences, compilerOptions: CompilerOptions, importingSourceFile: SourceFile): Preferences {
return {
relativePreference: importModuleSpecifierPreference === "relative" ? RelativePreference.Relative : importModuleSpecifierPreference === "non-relative" ? RelativePreference.NonRelative : RelativePreference.Auto,
ending: getEnding(),
};
function getEnding(): Ending {
switch (importModuleSpecifierEnding) {
case "minimal": return Ending.Minimal;
case "index": return Ending.Index;
case "js": return Ending.JsExtension;
default: return usesJsExtensionOnImports(importingSourceFile) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs ? Ending.Index : Ending.Minimal;
}
}
}
function getPreferencesForUpdate(compilerOptions: CompilerOptions, oldImportSpecifier: string): Preferences {
return {
relativePreference: isExternalModuleNameRelative(oldImportSpecifier) ? RelativePreference.Relative : RelativePreference.NonRelative,
ending: hasJavaScriptOrJsonFileExtension(oldImportSpecifier) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs || endsWith(oldImportSpecifier, "index") ? Ending.Index : Ending.Minimal,
};
}
export function updateModuleSpecifier(
compilerOptions: CompilerOptions,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
redirectTargetsMap: RedirectTargetsMap,
oldImportSpecifier: string,
): string | undefined {
const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, files, redirectTargetsMap, getPreferencesForUpdate(compilerOptions, oldImportSpecifier));
if (res === oldImportSpecifier) return undefined;
return res;
}
// Note: importingSourceFile is just for usesJsExtensionOnImports
@ -13,35 +57,25 @@ namespace ts.moduleSpecifiers {
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
preferences: ModuleSpecifierPreferences = {},
preferences: UserPreferences = {},
redirectTargetsMap: RedirectTargetsMap,
): string {
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFileName, host);
const modulePaths = getAllModulePaths(files, importingSourceFileName, toFileName, info.getCanonicalFileName, host, redirectTargetsMap);
return firstDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)) ||
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, files, redirectTargetsMap, getPreferences(preferences, compilerOptions, importingSourceFile));
}
export function getModuleSpecifierForDeclarationFile(
moduleSymbol: Symbol,
function getModuleSpecifierWorker(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
redirectTargetsMap: RedirectTargetsMap,
preferences: Preferences
): string {
const isBundle = (compilerOptions.out || compilerOptions.outFile);
if (isBundle && host.getCommonSourceDirectory) {
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
// just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this
// using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative
// specifier preference
compilerOptions = {
...compilerOptions,
baseUrl: host.getCommonSourceDirectory(),
};
}
const preferences: ModuleSpecifierPreferences = { importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" };
return first(first(getModuleSpecifiers(moduleSymbol, compilerOptions, importingSourceFile, host, host.getSourceFiles ? host.getSourceFiles() : [importingSourceFile], preferences, redirectTargetsMap)));
const info = getInfo(importingSourceFileName, host);
const modulePaths = getAllModulePaths(files, importingSourceFileName, toFileName, info.getCanonicalFileName, host, redirectTargetsMap);
return firstDefined(modulePaths, moduleFileName => tryGetModuleNameAsNodeModule(moduleFileName, info, host, compilerOptions)) ||
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
}
// For each symlink/original for a module, returns a list of ways to import that file.
@ -51,60 +85,39 @@ namespace ts.moduleSpecifiers {
importingSourceFile: SourceFile,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
preferences: ModuleSpecifierPreferences,
userPreferences: UserPreferences,
redirectTargetsMap: RedirectTargetsMap,
): ReadonlyArray<ReadonlyArray<string>> {
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
if (ambient) return [[ambient]];
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.path, host);
if (!files) {
return Debug.fail("Files list must be present to resolve symlinks in specifier resolution");
}
const info = getInfo(importingSourceFile.path, host);
const moduleSourceFile = getSourceFileOfNode(moduleSymbol.valueDeclaration || getNonAugmentationDeclaration(moduleSymbol));
const modulePaths = getAllModulePaths(files, importingSourceFile.path, moduleSourceFile.fileName, info.getCanonicalFileName, host, redirectTargetsMap);
const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions));
const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
const global = mapDefined(modulePaths, moduleFileName => tryGetModuleNameAsNodeModule(moduleFileName, info, host, compilerOptions));
return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName =>
getLocalModuleSpecifiers(moduleFileName, info, compilerOptions, preferences));
}
interface Info {
readonly moduleResolutionKind: ModuleResolutionKind;
readonly addJsExtension: boolean;
readonly getCanonicalFileName: GetCanonicalFileName;
readonly sourceDirectory: Path;
}
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Info {
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
const addJsExtension = usesJsExtensionOnImports(importingSourceFile);
function getInfo(importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Info {
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true);
const sourceDirectory = getDirectoryPath(importingSourceFileName);
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory };
return { getCanonicalFileName, sourceDirectory };
}
function getGlobalModuleSpecifier(
moduleFileName: string,
{ addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
host: ModuleSpecifierResolutionHost,
compilerOptions: CompilerOptions,
) {
return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension)
|| tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory);
}
function getLocalModuleSpecifiers(
moduleFileName: string,
{ moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
compilerOptions: CompilerOptions,
preferences: ModuleSpecifierPreferences,
): ReadonlyArray<string> {
function getLocalModuleSpecifiers(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, compilerOptions: CompilerOptions, { ending, relativePreference }: Preferences): ReadonlyArray<string> {
const { baseUrl, paths, rootDirs } = compilerOptions;
const relativePath = rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName) ||
removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension);
if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") {
removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), ending, compilerOptions);
if (!baseUrl || relativePreference === RelativePreference.Relative) {
return [relativePath];
}
@ -113,7 +126,7 @@ namespace ts.moduleSpecifiers {
return [relativePath];
}
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addJsExtension);
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, ending, compilerOptions);
if (paths) {
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
if (fromPaths) {
@ -121,11 +134,11 @@ namespace ts.moduleSpecifiers {
}
}
if (preferences.importModuleSpecifierPreference === "non-relative") {
if (relativePreference === RelativePreference.NonRelative) {
return [importRelativeToBaseUrl];
}
if (preferences.importModuleSpecifierPreference !== undefined) Debug.assertNever(preferences.importModuleSpecifierPreference);
if (relativePreference !== RelativePreference.Auto) Debug.assertNever(relativePreference);
if (isPathRelativeToParent(relativeToBaseUrl)) {
return [relativePath];
@ -164,7 +177,7 @@ namespace ts.moduleSpecifiers {
}
function usesJsExtensionOnImports({ imports }: SourceFile): boolean {
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? hasJavaScriptOrJsonFileExtension(text) : undefined) || false;
}
function stringsEqual(a: string, b: string, getCanonicalFileName: GetCanonicalFileName): boolean {
@ -283,37 +296,8 @@ namespace ts.moduleSpecifiers {
return removeFileExtension(relativePath);
}
function tryGetModuleNameFromTypeRoots(
options: CompilerOptions,
host: GetEffectiveTypeRootsHost,
getCanonicalFileName: (file: string) => string,
moduleFileName: string,
addJsExtension: boolean,
): string | undefined {
const roots = getEffectiveTypeRoots(options, host);
return firstDefined(roots, unNormalizedTypeRoot => {
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
if (startsWith(moduleFileName, typeRoot)) {
// For a type definition, we can strip `/index` even with classic resolution.
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addJsExtension);
}
});
}
function tryGetModuleNameAsNodeModule(
options: CompilerOptions,
moduleFileName: string,
host: ModuleSpecifierResolutionHost,
getCanonicalFileName: (file: string) => string,
sourceDirectory: Path,
): string | undefined {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
function tryGetModuleNameAsNodeModule(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions): string | undefined {
const parts: NodeModulePathParts = getNodeModulePathParts(moduleFileName)!;
if (!parts) {
return undefined;
}
@ -325,8 +309,12 @@ namespace ts.moduleSpecifiers {
// Get a path that's relative to node_modules or the importing file's path
// if node_modules folder is in this folder or any of its parent folders, no need to keep it.
if (!startsWith(sourceDirectory, getCanonicalFileName(moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex)))) return undefined;
// If the module was found in @types, get the actual Node package name
return getPackageNameFromAtTypesDirectory(moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1));
const nodeModulesDirectoryName = moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1);
const packageName = getPackageNameFromAtTypesDirectory(nodeModulesDirectoryName);
// For classic resolution, only allow importing from node_modules/@types, not other node_modules
return getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs && packageName === nodeModulesDirectoryName ? undefined : packageName;
function getDirectoryOrExtensionlessFileName(path: string): string {
// If the file is the main module, it can be imported by the package name
@ -440,13 +428,35 @@ namespace ts.moduleSpecifiers {
});
}
function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addJsExtension: boolean): string {
function removeExtensionAndIndexPostFix(fileName: string, ending: Ending, options: CompilerOptions): string {
const noExtension = removeFileExtension(fileName);
return addJsExtension
? noExtension + ".js"
: moduleResolutionKind === ModuleResolutionKind.NodeJs
? removeSuffix(noExtension, "/index")
: noExtension;
switch (ending) {
case Ending.Minimal:
return removeSuffix(noExtension, "/index");
case Ending.Index:
return noExtension;
case Ending.JsExtension:
return noExtension + getJavaScriptExtensionForFile(fileName, options);
default:
return Debug.assertNever(ending);
}
}
function getJavaScriptExtensionForFile(fileName: string, options: CompilerOptions): Extension {
const ext = extensionFromPath(fileName);
switch (ext) {
case Extension.Ts:
case Extension.Dts:
return Extension.Js;
case Extension.Tsx:
return options.jsx === JsxEmit.Preserve ? Extension.Jsx : Extension.Js;
case Extension.Js:
case Extension.Jsx:
case Extension.Json:
return ext;
default:
return Debug.assertNever(ext);
}
}
function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {

View file

@ -67,19 +67,24 @@ namespace ts {
}
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
return createCompilerHostWorker(options, setParentNodes);
}
/*@internal*/
// TODO(shkamat): update this after reworking ts build API
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
const existingDirectories = createMap<boolean>();
function getCanonicalFileName(fileName: string): string {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return system.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
}
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = sys.readFile(fileName, options.charset);
text = system.readFile(fileName, options.charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
@ -97,7 +102,7 @@ namespace ts {
if (existingDirectories.has(directoryPath)) {
return true;
}
if (sys.directoryExists(directoryPath)) {
if (system.directoryExists(directoryPath)) {
existingDirectories.set(directoryPath, true);
return true;
}
@ -108,7 +113,7 @@ namespace ts {
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
sys.createDirectory(directoryPath);
system.createDirectory(directoryPath);
}
}
@ -119,8 +124,8 @@ namespace ts {
outputFingerprints = createMap<OutputFingerprint>();
}
const hash = sys.createHash!(data); // TODO: GH#18217
const mtimeBefore = sys.getModifiedTime!(fileName); // TODO: GH#18217
const hash = system.createHash!(data); // TODO: GH#18217
const mtimeBefore = system.getModifiedTime!(fileName); // TODO: GH#18217
if (mtimeBefore) {
const fingerprint = outputFingerprints.get(fileName);
@ -133,9 +138,9 @@ namespace ts {
}
}
sys.writeFile(fileName, data, writeByteOrderMark);
system.writeFile(fileName, data, writeByteOrderMark);
const mtimeAfter = sys.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217
const mtimeAfter = system.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217
outputFingerprints.set(fileName, {
hash,
@ -149,11 +154,11 @@ namespace ts {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
if (isWatchSet(options) && system.createHash && system.getModifiedTime) {
writeFileIfUpdated(fileName, data, writeByteOrderMark);
}
else {
sys.writeFile(fileName, data, writeByteOrderMark);
system.writeFile(fileName, data, writeByteOrderMark);
}
performance.mark("afterIOWrite");
@ -167,32 +172,29 @@ namespace ts {
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
}
const newLine = getNewLineCharacter(options);
const realpath = sys.realpath && ((path: string) => sys.realpath!(path));
const newLine = getNewLineCharacter(options, () => system.newLine);
const realpath = system.realpath && ((path: string) => system.realpath!(path));
return {
getSourceFile,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile,
getCurrentDirectory: memoize(() => sys.getCurrentDirectory()),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => sys.getDirectories(path),
fileExists: fileName => system.fileExists(fileName),
readFile: fileName => system.readFile(fileName),
trace: (s: string) => system.write(s + newLine),
directoryExists: directoryName => system.directoryExists(directoryName),
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => system.getDirectories(path),
realpath,
readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth),
getModifiedTime: sys.getModifiedTime && (path => sys.getModifiedTime!(path)),
setModifiedTime: sys.setModifiedTime && ((path, date) => sys.setModifiedTime!(path, date)),
deleteFile: sys.deleteFile && (path => sys.deleteFile!(path))
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth)
};
}

File diff suppressed because it is too large Load diff

View file

@ -3097,6 +3097,10 @@ namespace ts {
*/
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
/* @internal */ getTypePredicateOfSignature(signature: Signature): TypePredicate;
/**
* An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
* and an external module with no 'export =' declaration resolves to the module itself.
*/
/* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol;
/** @param node A location where we might consider accessing `this`. Not necessarily a ThisExpression. */
/* @internal */ tryGetThisTypeAt(node: Node): Type | undefined;
@ -3114,6 +3118,8 @@ namespace ts {
* and the operation is cancelled, then it should be discarded, otherwise it is safe to keep.
*/
runWithCancellationToken<T>(token: CancellationToken, cb: (checker: TypeChecker) => T): T;
/* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): ReadonlyArray<TypeParameter> | undefined;
}
/* @internal */
@ -4717,16 +4723,6 @@ namespace ts {
verticalTab = 0x0B, // \v
}
/*@internal*/
export interface UpToDateHost {
fileExists(fileName: string): boolean;
getModifiedTime(fileName: string): Date | undefined;
getUnchangedTime?(fileName: string): Date | undefined;
getLastStatus?(fileName: string): UpToDateStatus | undefined;
setLastStatus?(fileName: string, status: UpToDateStatus): void;
parseConfigFile?(configFilePath: ResolvedConfigFileName): ParsedCommandLine | undefined;
}
export interface ModuleResolutionHost {
// TODO: GH#18217 Optional methods frequently used as non-optional
@ -4857,10 +4853,6 @@ namespace ts {
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
/* @internal */
@ -5345,8 +5337,6 @@ namespace ts {
useCaseSensitiveFileNames?(): boolean;
fileExists?(path: string): boolean;
readFile?(path: string): string | undefined;
getSourceFiles?(): ReadonlyArray<SourceFile>; // Used for cached resolutions to find symlinks without traversing the fs (again)
getCommonSourceDirectory?(): string;
}
// Note: this used to be deprecated in our public API, but is still used internally
@ -5359,7 +5349,7 @@ namespace ts {
reportInaccessibleThisError?(): void;
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
reportInaccessibleUniqueSymbolError?(): void;
moduleResolverHost?: ModuleSpecifierResolutionHost;
moduleResolverHost?: EmitHost;
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void;
}
@ -5609,4 +5599,15 @@ namespace ts {
get<TKey extends keyof PragmaPsuedoMap>(key: TKey): PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][];
forEach(action: <TKey extends keyof PragmaPsuedoMap>(value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][], key: TKey) => void): void;
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}

View file

@ -2331,6 +2331,13 @@ namespace ts {
return node;
}
function skipParenthesesUp(node: Node): Node {
while (node.kind === SyntaxKind.ParenthesizedExpression) {
node = node.parent;
}
return node;
}
// a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped
export function isDeleteTarget(node: Node): boolean {
if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
@ -4206,6 +4213,8 @@ namespace ts {
if (!parent) return AccessKind.Read;
switch (parent.kind) {
case SyntaxKind.ParenthesizedExpression:
return accessKind(parent);
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
@ -4217,13 +4226,35 @@ namespace ts {
: AccessKind.Read;
case SyntaxKind.PropertyAccessExpression:
return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
case SyntaxKind.PropertyAssignment: {
const parentAccess = accessKind(parent.parent);
// In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write.
return node === (parent as PropertyAssignment).name ? reverseAccessKind(parentAccess) : parentAccess;
}
case SyntaxKind.ShorthandPropertyAssignment:
// Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals.
return node === (parent as ShorthandPropertyAssignment).objectAssignmentInitializer ? AccessKind.Read : accessKind(parent.parent);
case SyntaxKind.ArrayLiteralExpression:
return accessKind(parent);
default:
return AccessKind.Read;
}
function writeOrReadWrite(): AccessKind {
// If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
return parent.parent && skipParenthesesUp(parent.parent).kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
}
}
function reverseAccessKind(a: AccessKind): AccessKind {
switch (a) {
case AccessKind.Read:
return AccessKind.Write;
case AccessKind.Write:
return AccessKind.Read;
case AccessKind.ReadWrite:
return AccessKind.ReadWrite;
default:
return Debug.assertNever(a);
}
}
@ -7905,6 +7936,7 @@ namespace ts {
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
export const supportedJavaScriptAndJsonExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx, Extension.Json];
const allSupportedExtensions: ReadonlyArray<Extension> = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<FileExtensionInfo>): ReadonlyArray<string> {
@ -7930,6 +7962,10 @@ namespace ts {
return some(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function hasJavaScriptOrJsonFileExtension(fileName: string): boolean {
return supportedJavaScriptAndJsonExtensions.some(ext => fileExtensionIs(fileName, ext));
}
export function hasTypeScriptFileExtension(fileName: string): boolean {
return some(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}

View file

@ -27,12 +27,6 @@ namespace ts {
};
}
/** @internal */
export const nonClearingMessageCodes: number[] = [
Diagnostics.Found_1_error_Watching_for_file_changes.code,
Diagnostics.Found_0_errors_Watching_for_file_changes.code
];
/**
* @returns Whether the screen was cleared.
*/
@ -41,7 +35,7 @@ namespace ts {
!options.preserveWatchOutput &&
!options.extendedDiagnostics &&
!options.diagnostics &&
!contains(nonClearingMessageCodes, diagnostic.code)) {
contains(screenStartingMessageCodes, diagnostic.code)) {
system.clearScreen();
return true;
}
@ -174,6 +168,17 @@ namespace ts {
const noopFileWatcher: FileWatcher = { close: noop };
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
return {
onWatchStatusChange,
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop
};
}
/**
* Creates the watch compiler host that can be extended with config file or root file names and options host
*/
@ -186,7 +191,7 @@ namespace ts {
host; // tslint:disable-line no-unused-expression (TODO: `host` is unused!)
const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames;
const writeFileName = (s: string) => system.write(s + system.newLine);
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
const { onWatchStatusChange, watchFile, watchDirectory, setTimeout, clearTimeout } = createWatchHost(system, reportWatchStatus);
return {
useCaseSensitiveFileNames,
getNewLine: () => system.newLine,
@ -200,10 +205,10 @@ namespace ts {
readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth),
realpath: system.realpath && (path => system.realpath!(path)),
getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)),
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop,
watchFile,
watchDirectory,
setTimeout,
clearTimeout,
trace: s => system.write(s),
onWatchStatusChange,
createDirectory: path => system.createDirectory(path),
@ -224,10 +229,10 @@ namespace ts {
const reportSummary = (errorCount: number) => {
if (errorCount === 1) {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
}
else {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
}
};
@ -270,7 +275,21 @@ namespace ts {
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
export interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
export interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
export interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
// TODO: GH#18217 Optional methods are frequently asserted
/**
@ -279,8 +298,6 @@ namespace ts {
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
// Only for testing
/*@internal*/
@ -323,15 +340,6 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/** Internal interface used to wire emit through same host */

View file

@ -374,5 +374,41 @@ namespace fakes {
return parsed;
}
}
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost {
diagnostics: ts.Diagnostic[] = [];
reportDiagnostic(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
}
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
}
clearDiagnostics() {
this.diagnostics.length = 0;
}
assertDiagnosticMessages(...expected: ts.DiagnosticMessage[]) {
const actual = this.diagnostics.slice();
if (actual.length !== expected.length) {
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
}
for (let i = 0; i < actual.length; i++) {
if (actual[i].code !== expected[i].code) {
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
}
}
}
printDiagnostics(header = "== Diagnostics ==") {
const out = ts.createDiagnosticReporter(ts.sys);
ts.sys.write(header + "\r\n");
for (const d of this.diagnostics) {
out(d);
}
}
}
}

View file

@ -908,8 +908,8 @@ namespace FourSlash {
}
private verifyCompletionEntry(actual: ts.CompletionEntry, expected: FourSlashInterface.ExpectedCompletionEntry) {
const { insertText, replacementSpan, hasAction, isRecommended, kind, text, documentation, sourceDisplay } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, text: undefined, documentation: undefined, sourceDisplay: undefined }
const { insertText, replacementSpan, hasAction, isRecommended, kind, text, documentation, source, sourceDisplay } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, text: undefined, documentation: undefined, source: undefined, sourceDisplay: undefined }
: expected;
if (actual.insertText !== insertText) {
@ -927,6 +927,7 @@ namespace FourSlash {
assert.equal(actual.hasAction, hasAction);
assert.equal(actual.isRecommended, isRecommended);
assert.equal(actual.source, source);
if (text) {
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
@ -4789,6 +4790,7 @@ namespace FourSlashInterface {
export type ExpectedCompletionEntry = string | {
readonly name: string,
readonly source?: string,
readonly insertText?: string,
readonly replacementSpan?: FourSlash.Range,
readonly hasAction?: boolean, // If not specified, will assert that this is false.

View file

@ -620,14 +620,14 @@ interface Array<T> {}`
}
}
removeFile(filePath: string) {
deleteFile(filePath: string) {
const path = this.toFullPath(filePath);
const currentEntry = this.fs.get(path) as FsFile;
Debug.assert(isFsFile(currentEntry));
this.removeFileOrFolder(currentEntry, returnFalse);
}
removeFolder(folderPath: string, recursive?: boolean) {
deleteFolder(folderPath: string, recursive?: boolean) {
const path = this.toFullPath(folderPath);
const currentEntry = this.fs.get(path) as FsFolder;
Debug.assert(isFsFolder(currentEntry));
@ -635,7 +635,7 @@ interface Array<T> {}`
const subEntries = currentEntry.entries.slice();
subEntries.forEach(fsEntry => {
if (isFsFolder(fsEntry)) {
this.removeFolder(fsEntry.fullPath, recursive);
this.deleteFolder(fsEntry.fullPath, recursive);
}
else {
this.removeFileOrFolder(fsEntry, returnFalse);
@ -766,6 +766,14 @@ interface Array<T> {}`
return (fsEntry && fsEntry.modifiedTime)!; // TODO: GH#18217
}
setModifiedTime(s: string, date: Date) {
const path = this.toFullPath(s);
const fsEntry = this.fs.get(path);
if (fsEntry) {
fsEntry.modifiedTime = date;
}
}
readFile(s: string): string | undefined {
const fsEntry = this.getRealFile(this.toFullPath(s));
return fsEntry ? fsEntry.content : undefined;

View file

@ -1,3 +1,9 @@
namespace ts {
export interface UserPreferences {
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
}
namespace ts.server {
export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024;
/*@internal*/

View file

@ -163,7 +163,7 @@ namespace ts.codefix {
position: number,
preferences: UserPreferences,
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
const exportInfos = getAllReExportingModules(exportedSymbol, moduleSymbol, symbolName, sourceFile, program.getTypeChecker(), program.getSourceFiles());
const exportInfos = getAllReExportingModules(exportedSymbol, moduleSymbol, symbolName, sourceFile, program.getCompilerOptions(), program.getTypeChecker(), program.getSourceFiles());
Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol));
// We sort the best codefixes first, so taking `first` is best for completions.
const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, exportInfos, host, preferences)).moduleSpecifier;
@ -175,7 +175,7 @@ namespace ts.codefix {
return { description, changes, commands };
}
function getAllReExportingModules(exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, sourceFile: SourceFile, checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>): ReadonlyArray<SymbolExportInfo> {
function getAllReExportingModules(exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, sourceFile: SourceFile, compilerOptions: CompilerOptions, checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>): ReadonlyArray<SymbolExportInfo> {
const result: SymbolExportInfo[] = [];
forEachExternalModule(checker, allSourceFiles, (moduleSymbol, moduleFile) => {
// Don't import from a re-export when looking "up" like to `./index` or `../index`.
@ -183,10 +183,14 @@ namespace ts.codefix {
return;
}
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
if (defaultInfo && defaultInfo.name === symbolName && skipAlias(defaultInfo.symbol, checker) === exportedSymbol) {
result.push({ moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(defaultInfo.symbol) });
}
for (const exported of checker.getExportsOfModule(moduleSymbol)) {
if ((exported.escapedName === InternalSymbolName.Default || exported.name === symbolName) && skipAlias(exported, checker) === exportedSymbol) {
const isDefaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol) === exported;
result.push({ moduleSymbol, importKind: isDefaultExport ? ImportKind.Default : ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported) });
if (exported.name === symbolName && skipAlias(exported, checker) === exportedSymbol) {
result.push({ moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported) });
}
}
});
@ -400,13 +404,9 @@ namespace ts.codefix {
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
cancellationToken.throwIfCancellationRequested();
// check the default export
const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
if (defaultExport) {
const info = getDefaultExportInfo(defaultExport, moduleSymbol, program);
if (info && info.name === symbolName && symbolHasMeaning(info.symbolForMeaning, currentTokenMeaning)) {
addSymbol(moduleSymbol, defaultExport, ImportKind.Default);
}
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, program.getCompilerOptions());
if (defaultInfo && defaultInfo.name === symbolName && symbolHasMeaning(defaultInfo.symbolForMeaning, currentTokenMeaning)) {
addSymbol(moduleSymbol, defaultInfo.symbol, defaultInfo.kind);
}
// check exports with the same name
@ -418,7 +418,24 @@ namespace ts.codefix {
return originalSymbolToExportInfos;
}
function getDefaultExportInfo(defaultExport: Symbol, moduleSymbol: Symbol, program: Program): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
function getDefaultLikeExportInfo(
moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions,
): { readonly symbol: Symbol, readonly symbolForMeaning: Symbol, readonly name: string, readonly kind: ImportKind.Default | ImportKind.Equals } | undefined {
const exported = getDefaultLikeExportWorker(moduleSymbol, checker);
if (!exported) return undefined;
const { symbol, kind } = exported;
const info = getDefaultExportInfoWorker(symbol, moduleSymbol, checker, compilerOptions);
return info && { symbol, symbolForMeaning: info.symbolForMeaning, name: info.name, kind };
}
function getDefaultLikeExportWorker(moduleSymbol: Symbol, checker: TypeChecker): { readonly symbol: Symbol, readonly kind: ImportKind.Default | ImportKind.Equals } | undefined {
const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
if (defaultExport) return { symbol: defaultExport, kind: ImportKind.Default };
const exportEquals = checker.resolveExternalModuleSymbol(moduleSymbol);
return exportEquals === moduleSymbol ? undefined : { symbol: exportEquals, kind: ImportKind.Equals };
}
function getDefaultExportInfoWorker(defaultExport: Symbol, moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
@ -426,11 +443,11 @@ namespace ts.codefix {
if (name !== undefined) return { symbolForMeaning: defaultExport, name };
if (defaultExport.flags & SymbolFlags.Alias) {
const aliased = program.getTypeChecker().getImmediateAliasedSymbol(defaultExport);
return aliased && getDefaultExportInfo(aliased, Debug.assertDefined(aliased.parent), program);
const aliased = checker.getImmediateAliasedSymbol(defaultExport);
return aliased && getDefaultExportInfoWorker(aliased, Debug.assertDefined(aliased.parent), checker, compilerOptions);
}
else {
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, program.getCompilerOptions().target!) };
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target!) };
}
}

View file

@ -1378,6 +1378,14 @@ namespace ts.Completions {
return;
}
if (resolvedModuleSymbol !== moduleSymbol &&
// Don't add another completion for `export =` of a symbol that's already global.
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
resolvedModuleSymbol.declarations.some(d => !!d.getSourceFile().externalModuleIndicator)) {
symbols.push(resolvedModuleSymbol);
symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false };
}
for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
// Don't add a completion for a re-export, only for the original.
// The actual import fix might end up coming from a re-export -- we don't compute that until getting completion details.

View file

@ -6,7 +6,7 @@ namespace ts {
newFileOrDirPath: string,
host: LanguageServiceHost,
formatContext: formatting.FormatContext,
preferences: UserPreferences,
_preferences: UserPreferences,
sourceMapper: SourceMapper,
): ReadonlyArray<FileTextChanges> {
const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
@ -15,7 +15,7 @@ namespace ts {
const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper);
return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => {
updateTsconfigFiles(program, changeTracker, oldToNew, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames);
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName, preferences);
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName);
});
}
@ -122,7 +122,6 @@ namespace ts {
newToOld: PathUpdater,
host: LanguageServiceHost,
getCanonicalFileName: GetCanonicalFileName,
preferences: UserPreferences,
): void {
const allFiles = program.getSourceFiles();
for (const sourceFile of allFiles) {
@ -156,7 +155,7 @@ namespace ts {
// Need an update if the imported file moved, or the importing file moved and was using a relative path.
return toImport !== undefined && (toImport.updated || (importingSourceFileMoved && pathIsRelative(importLiteral.text)))
? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, allFiles, preferences, program.redirectTargetsMap)
? moduleSpecifiers.updateModuleSpecifier(program.getCompilerOptions(), newImportFromPath, toImport.newFileName, host, allFiles, program.redirectTargetsMap, importLiteral.text)
: undefined;
});
}
@ -210,7 +209,7 @@ namespace ts {
}
function updateImportsWorker(sourceFile: SourceFile, changeTracker: textChanges.ChangeTracker, updateRef: (refText: string) => string | undefined, updateImport: (importLiteral: StringLiteralLike) => string | undefined) {
for (const ref of sourceFile.referencedFiles) {
for (const ref of sourceFile.referencedFiles || emptyArray) { // TODO: GH#26162
const updated = updateRef(ref.fileName);
if (updated !== undefined && updated !== sourceFile.text.slice(ref.pos, ref.end)) changeTracker.replaceRangeWithText(sourceFile, ref, updated);
}

View file

@ -29,7 +29,7 @@ namespace ts.GoToDefinition {
const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
// Don't go to the component constructor definition for a JSX element, just go to the component definition.
if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorDeclaration(calledDeclaration))) {
if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorLike(calledDeclaration))) {
const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration);
// For a function, if this is the original function definition, return just sigInfo.
// If this is the original constructor definition, parent is the class.
@ -319,4 +319,15 @@ namespace ts.GoToDefinition {
// Don't go to a function type, go to the value having that type.
return tryCast(signature && signature.declaration, (d): d is SignatureDeclaration => isFunctionLike(d) && !isFunctionTypeNode(d));
}
function isConstructorLike(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
return true;
default:
return false;
}
}
}

View file

@ -2,7 +2,7 @@
namespace ts.SignatureHelp {
const enum InvocationKind { Call, TypeArgs, Contextual }
interface CallInvocation { readonly kind: InvocationKind.Call; readonly node: CallLikeExpression; }
interface TypeArgsInvocation { readonly kind: InvocationKind.TypeArgs; readonly called: Expression; }
interface TypeArgsInvocation { readonly kind: InvocationKind.TypeArgs; readonly called: Identifier; }
interface ContextualInvocation {
readonly kind: InvocationKind.Contextual;
readonly signature: Signature;
@ -44,7 +44,7 @@ namespace ts.SignatureHelp {
cancellationToken.throwIfCancellationRequested();
// Extra syntactic and semantic filtering of signature help
const candidateInfo = getCandidateInfo(argumentInfo, typeChecker, sourceFile, startingToken, onlyUseSyntacticOwners);
const candidateInfo = getCandidateOrTypeInfo(argumentInfo, typeChecker, sourceFile, startingToken, onlyUseSyntacticOwners);
cancellationToken.throwIfCancellationRequested();
if (!candidateInfo) {
@ -53,11 +53,24 @@ namespace ts.SignatureHelp {
return isSourceFileJavaScript(sourceFile) ? createJavaScriptSignatureHelpItems(argumentInfo, program, cancellationToken) : undefined;
}
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker));
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
candidateInfo.kind === CandidateOrTypeKind.Candidate
? createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker)
: createTypeHelpItems(candidateInfo.symbol, argumentInfo, sourceFile, typeChecker));
}
interface CandidateInfo { readonly candidates: ReadonlyArray<Signature>; readonly resolvedSignature: Signature; }
function getCandidateInfo({ invocation, argumentCount }: ArgumentListInfo, checker: TypeChecker, sourceFile: SourceFile, startingToken: Node, onlyUseSyntacticOwners: boolean): CandidateInfo | undefined {
const enum CandidateOrTypeKind { Candidate, Type }
interface CandidateInfo {
readonly kind: CandidateOrTypeKind.Candidate;
readonly candidates: ReadonlyArray<Signature>;
readonly resolvedSignature: Signature;
}
interface TypeInfo {
readonly kind: CandidateOrTypeKind.Type;
readonly symbol: Symbol;
}
function getCandidateOrTypeInfo({ invocation, argumentCount }: ArgumentListInfo, checker: TypeChecker, sourceFile: SourceFile, startingToken: Node, onlyUseSyntacticOwners: boolean): CandidateInfo | TypeInfo | undefined {
switch (invocation.kind) {
case InvocationKind.Call: {
if (onlyUseSyntacticOwners && !isSyntacticOwner(startingToken, invocation.node, sourceFile)) {
@ -65,17 +78,21 @@ namespace ts.SignatureHelp {
}
const candidates: Signature[] = [];
const resolvedSignature = checker.getResolvedSignatureForSignatureHelp(invocation.node, candidates, argumentCount)!; // TODO: GH#18217
return candidates.length === 0 ? undefined : { candidates, resolvedSignature };
return candidates.length === 0 ? undefined : { kind: CandidateOrTypeKind.Candidate, candidates, resolvedSignature };
}
case InvocationKind.TypeArgs: {
if (onlyUseSyntacticOwners && !lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.called)) {
const { called } = invocation;
if (onlyUseSyntacticOwners && !containsPrecedingToken(startingToken, sourceFile, isIdentifier(called) ? called.parent : called)) {
return undefined;
}
const candidates = getPossibleGenericSignatures(invocation.called, argumentCount, checker);
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
const candidates = getPossibleGenericSignatures(called, argumentCount, checker);
if (candidates.length !== 0) return { kind: CandidateOrTypeKind.Candidate, candidates, resolvedSignature: first(candidates) };
const symbol = checker.getSymbolAtLocation(called);
return symbol && { kind: CandidateOrTypeKind.Type, symbol };
}
case InvocationKind.Contextual:
return { candidates: [invocation.signature], resolvedSignature: invocation.signature };
return { kind: CandidateOrTypeKind.Candidate, candidates: [invocation.signature], resolvedSignature: invocation.signature };
default:
return Debug.assertNever(invocation);
}
@ -92,7 +109,7 @@ namespace ts.SignatureHelp {
return !!containingList && contains(invocationChildren, containingList);
}
case SyntaxKind.LessThanToken:
return lessThanFollowsCalledExpression(startingToken, sourceFile, node.expression);
return containsPrecedingToken(startingToken, sourceFile, node.expression);
default:
return false;
}
@ -114,12 +131,12 @@ namespace ts.SignatureHelp {
}));
}
function lessThanFollowsCalledExpression(startingToken: Node, sourceFile: SourceFile, calledExpression: Expression) {
function containsPrecedingToken(startingToken: Node, sourceFile: SourceFile, container: Node) {
const precedingToken = Debug.assertDefined(
findPrecedingToken(startingToken.getFullStart(), sourceFile, startingToken.parent, /*excludeJsdoc*/ true)
);
return rangeContainsRange(calledExpression, precedingToken);
return rangeContainsRange(container, precedingToken);
}
export interface ArgumentInfoForCompletions {
@ -457,6 +474,10 @@ namespace ts.SignatureHelp {
return invocation.kind === InvocationKind.Call ? getInvokedExpression(invocation.node) : invocation.called;
}
function getEnclosingDeclarationFromInvocation(invocation: Invocation): Node {
return invocation.kind === InvocationKind.Call ? invocation.node : invocation.kind === InvocationKind.TypeArgs ? invocation.called : invocation.node;
}
const signatureHelpNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
function createSignatureHelpItems(
candidates: ReadonlyArray<Signature>,
@ -465,7 +486,7 @@ namespace ts.SignatureHelp {
sourceFile: SourceFile,
typeChecker: TypeChecker,
): SignatureHelpItems {
const enclosingDeclaration = invocation.kind === InvocationKind.Call ? invocation.node : invocation.kind === InvocationKind.TypeArgs ? invocation.called : invocation.node;
const enclosingDeclaration = getEnclosingDeclarationFromInvocation(invocation);
const callTargetSymbol = invocation.kind === InvocationKind.Contextual ? invocation.symbol : typeChecker.getSymbolAtLocation(getExpressionFromInvocation(invocation));
const callTargetDisplayParts = callTargetSymbol ? symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : emptyArray;
const items = candidates.map(candidateSignature => getSignatureHelpItem(candidateSignature, callTargetDisplayParts, isTypeParameterList, typeChecker, enclosingDeclaration, sourceFile));
@ -480,11 +501,36 @@ namespace ts.SignatureHelp {
return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
}
function createTypeHelpItems(
symbol: Symbol,
{ argumentCount, argumentsSpan: applicableSpan, invocation, argumentIndex }: ArgumentListInfo,
sourceFile: SourceFile,
checker: TypeChecker
): SignatureHelpItems | undefined {
const typeParameters = checker.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (!typeParameters) return undefined;
const items = [getTypeHelpItem(symbol, typeParameters, checker, getEnclosingDeclarationFromInvocation(invocation), sourceFile)];
return { items, applicableSpan, selectedItemIndex: 0, argumentIndex, argumentCount };
}
function getTypeHelpItem(symbol: Symbol, typeParameters: ReadonlyArray<TypeParameter>, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem {
const typeSymbolDisplay = symbolToDisplayParts(checker, symbol);
const printer = createPrinter({ removeComments: true });
const parameters = typeParameters.map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer));
const documentation = symbol.getDocumentationComment(checker);
const tags = symbol.getJsDocTags();
const prefixDisplayParts = [...typeSymbolDisplay, punctuationPart(SyntaxKind.LessThanToken)];
return { isVariadic: false, prefixDisplayParts, suffixDisplayParts: [punctuationPart(SyntaxKind.GreaterThanToken)], separatorDisplayParts, parameters, documentation, tags };
}
const separatorDisplayParts: SymbolDisplayPart[] = [punctuationPart(SyntaxKind.CommaToken), spacePart()];
function getSignatureHelpItem(candidateSignature: Signature, callTargetDisplayParts: ReadonlyArray<SymbolDisplayPart>, isTypeParameterList: boolean, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem {
const { isVariadic, parameters, prefix, suffix } = (isTypeParameterList ? itemInfoForTypeParameters : itemInfoForParameters)(candidateSignature, checker, enclosingDeclaration, sourceFile);
const prefixDisplayParts = [...callTargetDisplayParts, ...prefix];
const suffixDisplayParts = [...suffix, ...returnTypeToDisplayParts(candidateSignature, enclosingDeclaration, checker)];
const separatorDisplayParts = [punctuationPart(SyntaxKind.CommaToken), spacePart()];
const documentation = candidateSignature.getDocumentationComment(checker);
const tags = candidateSignature.getJsDocTags();
return { isVariadic, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts, parameters, documentation, tags };

View file

@ -233,15 +233,6 @@ namespace ts {
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
/* @internal */
export const emptyOptions = {};

View file

@ -80,6 +80,7 @@
"unittests/transform.ts",
"unittests/transpile.ts",
"unittests/tsbuild.ts",
"unittests/tsbuildWatchMode.ts",
"unittests/tsconfigParsing.ts",
"unittests/tscWatchMode.ts",
"unittests/versionCache.ts",

View file

@ -1,15 +1,5 @@
namespace ts {
let currentTime = 100;
let lastDiagnostics: Diagnostic[] = [];
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiagnostic(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
error: report,
verbose: report,
message: report,
errorDiagnostic: d => reportDiagnostic(d)
};
export namespace Sample1 {
tick();
const projFs = loadProjectFromDisk("tests/projects/sample1");
@ -21,12 +11,12 @@ namespace ts {
describe("tsbuild - sanity check of clean build of 'sample1' project", () => {
it("can build the sample project 'sample1' without error", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
clearDiagnostics();
host.clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*empty*/);
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
@ -37,12 +27,11 @@ namespace ts {
describe("tsbuild - dry builds", () => {
it("doesn't write any files in a dry build", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
// Check for outputs to not be written. Not an exhaustive list
for (const output of allExpectedOutputs) {
@ -51,28 +40,26 @@ namespace ts {
});
it("indicates that it would skip builds during a dry build", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
tick();
clearDiagnostics();
builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
host.clearDiagnostics();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
host.assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
});
});
describe("tsbuild - clean builds", () => {
it("removes all files it built", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
// Verify they exist
for (const output of allExpectedOutputs) {
@ -91,9 +78,9 @@ namespace ts {
describe("tsbuild - force builds", () => {
it("always builds under --force", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: true, verbose: false });
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.buildAllProjects();
let currentTime = time();
checkOutputTimestamps(currentTime);
@ -116,14 +103,14 @@ namespace ts {
describe("tsbuild - can detect when and what to rebuild", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
it("Builds the project", () => {
clearDiagnostics();
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
Diagnostics.Building_project_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
@ -135,10 +122,10 @@ namespace ts {
// All three projects are up to date
it("Detects that all projects are up to date", () => {
clearDiagnostics();
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
@ -147,12 +134,12 @@ namespace ts {
// Update a file in the leaf node (tests), only it should rebuild the last one
it("Only builds the leaf node project", () => {
clearDiagnostics();
host.clearDiagnostics();
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
@ -162,12 +149,12 @@ namespace ts {
// Update a file in the parent (without affecting types), should get fast downstream builds
it("Detects type-only changes in upstream projects", () => {
clearDiagnostics();
host.clearDiagnostics();
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
Diagnostics.Building_project_0,
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
@ -180,15 +167,13 @@ namespace ts {
describe("tsbuild - downstream-blocked compilations", () => {
it("won't build downstream projects if upstream projects have errors", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
// Induce an error in the middle project
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
builder.buildAllProjects();
assertDiagnosticMessages(
host.assertDiagnosticMessages(
Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
Diagnostics.Building_project_0,
@ -204,12 +189,11 @@ namespace ts {
describe("tsbuild - project invalidation", () => {
it("invalidates projects correctly", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*empty*/);
host.assertDiagnosticMessages(/*empty*/);
// Update a timestamp in the middle project
tick();
@ -221,14 +205,14 @@ namespace ts {
// Rebuild this project
tick();
builder.invalidateProject("/src/logic");
builder.buildInvalidatedProjects();
builder.buildInvalidatedProject();
// The file should be updated
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
// Build downstream projects should update 'tests', but not 'core'
tick();
builder.buildDependentInvalidatedProjects();
builder.buildInvalidatedProject();
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
});
@ -240,11 +224,10 @@ namespace ts {
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, [configFile], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(...expectedDiagnosticMessages);
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
if (!expectedDiagnosticMessages.length) {
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
@ -274,11 +257,11 @@ namespace ts {
let fs: vfs.FileSystem | undefined;
before(() => {
fs = outFileFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
});
after(() => {
fs = undefined;
@ -293,25 +276,24 @@ namespace ts {
describe("tsbuild - downstream prepend projects always get rebuilt", () => {
it("", () => {
const fs = outFileFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct");
tick();
replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola");
tick();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct");
});
});
}
describe("tsbuild - graph-ordering", () => {
let host: fakes.CompilerHost | undefined;
let host: fakes.SolutionBuilderHost | undefined;
const deps: [string, string][] = [
["A", "B"],
["B", "C"],
@ -324,7 +306,7 @@ namespace ts {
before(() => {
const fs = new vfs.FileSystem(false);
host = new fakes.CompilerHost(fs);
host = new fakes.SolutionBuilderHost(fs);
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps);
});
@ -349,7 +331,7 @@ namespace ts {
});
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
const builder = createSolutionBuilder(host!, buildHost, rootNames, { dry: true, force: false, verbose: false });
const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false });
const projFileNames = rootNames.map(getProjectFileName);
const graph = builder.getBuildGraph(projFileNames);
@ -404,30 +386,6 @@ namespace ts {
fs.writeFileSync(path, newContent, "utf-8");
}
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
const actual = lastDiagnostics.slice();
if (actual.length !== expected.length) {
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
}
for (let i = 0; i < actual.length; i++) {
if (actual[i].code !== expected[i].code) {
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
}
}
}
function clearDiagnostics() {
lastDiagnostics = [];
}
export function printDiagnostics(header = "== Diagnostics ==") {
const out = createDiagnosticReporter(sys);
sys.write(header + "\r\n");
for (const d of lastDiagnostics) {
out(d);
}
}
function tick() {
currentTime += 60_000;
}

View file

@ -0,0 +1,133 @@
namespace ts.tscWatch {
export import libFile = TestFSWithWatch.libFile;
function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const host = createSolutionBuilderWithWatchHost(system);
return ts.createSolutionBuilder(host, rootNames, defaultOptions || { dry: false, force: false, verbose: false, watch: true });
}
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const solutionBuilder = createSolutionBuilder(host, rootNames, defaultOptions);
solutionBuilder.buildAllProjects();
solutionBuilder.startWatching();
return solutionBuilder;
}
describe("tsbuild-watch program updates", () => {
const projectsLocation = "/user/username/projects";
const project = "sample1";
const enum SubProject {
core = "core",
logic = "logic",
tests = "tests",
ui = "ui"
}
type ReadonlyFile = Readonly<File>;
/** [tsconfig, index] | [tsconfig, index, anotherModule, someDecl] */
type SubProjectFiles = [ReadonlyFile, ReadonlyFile] | [ReadonlyFile, ReadonlyFile, ReadonlyFile, ReadonlyFile];
const root = Harness.IO.getWorkspaceRoot();
function projectFilePath(subProject: SubProject, baseFileName: string) {
return `${projectsLocation}/${project}/${subProject}/${baseFileName.toLowerCase()}`;
}
function projectFile(subProject: SubProject, baseFileName: string): File {
return {
path: projectFilePath(subProject, baseFileName),
content: Harness.IO.readFile(`${root}/tests/projects/${project}/${subProject}/${baseFileName}`)!
};
}
function subProjectFiles(subProject: SubProject, anotherModuleAndSomeDecl?: true): SubProjectFiles {
const tsconfig = projectFile(subProject, "tsconfig.json");
const index = projectFile(subProject, "index.ts");
if (!anotherModuleAndSomeDecl) {
return [tsconfig, index];
}
const anotherModule = projectFile(SubProject.core, "anotherModule.ts");
const someDecl = projectFile(SubProject.core, "some_decl.ts");
return [tsconfig, index, anotherModule, someDecl];
}
function getOutputFileNames(subProject: SubProject, baseFileNameWithoutExtension: string) {
const file = projectFilePath(subProject, baseFileNameWithoutExtension);
return [`${file}.js`, `${file}.d.ts`];
}
type OutputFileStamp = [string, Date | undefined];
function getOutputStamps(host: WatchedSystem, subProject: SubProject, baseFileNameWithoutExtension: string): OutputFileStamp[] {
return getOutputFileNames(subProject, baseFileNameWithoutExtension).map(f => [f, host.getModifiedTime(f)] as OutputFileStamp);
}
function getOutputFileStamps(host: WatchedSystem): OutputFileStamp[] {
return [
...getOutputStamps(host, SubProject.core, "anotherModule"),
...getOutputStamps(host, SubProject.core, "index"),
...getOutputStamps(host, SubProject.logic, "index"),
...getOutputStamps(host, SubProject.tests, "index"),
];
}
function verifyChangedFiles(actualStamps: OutputFileStamp[], oldTimeStamps: OutputFileStamp[], changedFiles: string[]) {
for (let i = 0; i < oldTimeStamps.length; i++) {
const actual = actualStamps[i];
const old = oldTimeStamps[i];
if (contains(changedFiles, actual[0])) {
assert.isTrue((actual[1] || 0) > (old[1] || 0), `${actual[0]} expected to written`);
}
else {
assert.equal(actual[1], old[1], `${actual[0]} expected to not change`);
}
}
}
const core = subProjectFiles(SubProject.core, /*anotherModuleAndSomeDecl*/ true);
const logic = subProjectFiles(SubProject.logic);
const tests = subProjectFiles(SubProject.tests);
const ui = subProjectFiles(SubProject.ui);
const allFiles: ReadonlyArray<File> = [libFile, ...core, ...logic, ...tests, ...ui];
const testProjectExpectedWatchedFiles = [core[0], core[1], core[2], ...logic, ...tests].map(f => f.path);
function createSolutionInWatchMode() {
const host = createWatchedSystem(allFiles, { currentDirectory: projectsLocation });
createSolutionBuilderWithWatch(host, [`${project}/${SubProject.tests}`]);
checkWatchedFiles(host, testProjectExpectedWatchedFiles);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true); // TODO: #26524
checkOutputErrorsInitial(host, emptyArray);
const outputFileStamps = getOutputFileStamps(host);
for (const stamp of outputFileStamps) {
assert.isDefined(stamp[1], `${stamp[0]} expected to be present`);
}
return { host, outputFileStamps };
}
it("creates solution in watch mode", () => {
createSolutionInWatchMode();
});
it("change builds changes and reports found errors message", () => {
const { host, outputFileStamps } = createSolutionInWatchMode();
host.writeFile(core[1].path, `${core[1].content}
export class someClass { }`);
host.checkTimeoutQueueLengthAndRun(1); // Builds core
const changedCore = getOutputFileStamps(host);
verifyChangedFiles(changedCore, outputFileStamps, [
...getOutputFileNames(SubProject.core, "anotherModule"), // This should not be written really
...getOutputFileNames(SubProject.core, "index")
]);
host.checkTimeoutQueueLengthAndRun(1); // Builds tests
const changedTests = getOutputFileStamps(host);
verifyChangedFiles(changedTests, changedCore, [
...getOutputFileNames(SubProject.tests, "index") // Again these need not be written
]);
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
const changedLogic = getOutputFileStamps(host);
verifyChangedFiles(changedLogic, changedTests, [
...getOutputFileNames(SubProject.logic, "index") // Again these need not be written
]);
host.checkTimeoutQueueLength(0);
checkOutputErrorsIncremental(host, emptyArray);
});
// TODO: write tests reporting errors but that will have more involved work since file
});
}

View file

@ -1,17 +1,16 @@
namespace ts.tscWatch {
import WatchedSystem = TestFSWithWatch.TestServerHost;
type File = TestFSWithWatch.File;
type SymLink = TestFSWithWatch.SymLink;
import createWatchedSystem = TestFSWithWatch.createWatchedSystem;
import checkArray = TestFSWithWatch.checkArray;
import libFile = TestFSWithWatch.libFile;
import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles;
import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed;
import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
import checkOutputContains = TestFSWithWatch.checkOutputContains;
import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain;
import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
export import WatchedSystem = TestFSWithWatch.TestServerHost;
export type File = TestFSWithWatch.File;
export type SymLink = TestFSWithWatch.SymLink;
export import createWatchedSystem = TestFSWithWatch.createWatchedSystem;
export import checkArray = TestFSWithWatch.checkArray;
export import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles;
export import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed;
export import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
export import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
export import checkOutputContains = TestFSWithWatch.checkOutputContains;
export import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain;
export import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
export function checkProgramActualFiles(program: Program, expectedFiles: string[]) {
checkArray(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles);
@ -111,7 +110,7 @@ namespace ts.tscWatch {
function assertWatchDiagnostic(diagnostic: Diagnostic) {
const expected = getWatchDiagnosticWithoutDate(diagnostic);
if (!disableConsoleClears && !contains(nonClearingMessageCodes, diagnostic.code)) {
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
screenClears++;
}
@ -137,7 +136,7 @@ namespace ts.tscWatch {
: createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length);
}
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
/*logsBeforeWatchDiagnostic*/ undefined,
@ -148,7 +147,7 @@ namespace ts.tscWatch {
createErrorsFoundCompilerDiagnostic(errors));
}
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
logsBeforeWatchDiagnostic,

View file

@ -8365,7 +8365,7 @@ new C();`
verifyProjectWithResolvedModule(session);
host.removeFolder(recognizersTextDist, /*recursive*/ true);
host.deleteFolder(recognizersTextDist, /*recursive*/ true);
host.runQueuedTimeoutCallbacks();
verifyProjectWithUnresolvedModule(session);
@ -9281,7 +9281,7 @@ describe("Test Suite 1", () => {
checkProjectActualFiles(project, expectedFilesWithUnitTest1);
const navBarResultUnitTest1 = navBarFull(session, unitTest1);
host.removeFile(unitTest1.path);
host.deleteFile(unitTest1.path);
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(project, expectedFilesWithoutUnitTest1);
@ -9527,7 +9527,7 @@ export function Test2() {
checkDeclarationFiles(bTs, session, [bDtsMap, bDts]);
// Testing what happens if we delete the original sources.
host.removeFile(bTs.path);
host.deleteFile(bTs.path);
openFilesForSession([userTs], session);
const service = session.getProjectService();

View file

@ -13,7 +13,7 @@ namespace ts {
}
let reportDiagnostic = createDiagnosticReporter(sys);
function updateReportDiagnostic(options: CompilerOptions) {
function updateReportDiagnostic(options?: CompilerOptions) {
if (shouldBePretty(options)) {
reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true);
}
@ -23,8 +23,8 @@ namespace ts {
return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY();
}
function shouldBePretty(options: CompilerOptions) {
if (typeof options.pretty === "undefined") {
function shouldBePretty(options?: CompilerOptions) {
if (!options || typeof options.pretty === "undefined") {
return defaultIsPretty();
}
return options.pretty;
@ -54,15 +54,7 @@ namespace ts {
export function executeCommandLine(args: string[]): void {
if (args.length > 0 && ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b"))) {
const reportDiag = createDiagnosticReporter(sys, defaultIsPretty());
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
error: report,
verbose: report,
message: report,
errorDiagnostic: d => reportDiag(d)
};
const result = performBuild(args.slice(1), createCompilerHost({}), buildHost, sys);
const result = performBuild(args.slice(1));
// undefined = in watch mode, do not exit
if (result !== undefined) {
return sys.exit(result);
@ -172,6 +164,146 @@ namespace ts {
}
}
function performBuild(args: string[]): number | undefined {
const buildOpts: CommandLineOption[] = [
{
name: "help",
shortName: "h",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Print_this_message,
},
{
name: "help",
shortName: "?",
type: "boolean"
},
{
name: "verbose",
shortName: "v",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Enable_verbose_logging,
type: "boolean"
},
{
name: "dry",
shortName: "d",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean,
type: "boolean"
},
{
name: "force",
shortName: "f",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Build_all_projects_including_those_that_appear_to_be_up_to_date,
type: "boolean"
},
{
name: "clean",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Delete_the_outputs_of_all_projects,
type: "boolean"
},
{
name: "watch",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Watch_input_files,
type: "boolean"
},
{
name: "preserveWatchOutput",
type: "boolean",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Whether_to_keep_outdated_console_output_in_watch_mode_instead_of_clearing_the_screen,
},
];
let buildOptionNameMap: OptionNameMap | undefined;
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
const buildOptions: BuildOptions = {};
const projects: string[] = [];
for (const arg of args) {
if (arg.charCodeAt(0) === CharacterCodes.minus) {
const opt = getOptionDeclarationFromName(returnBuildOptionNameMap, arg.slice(arg.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
if (opt) {
buildOptions[opt.name as keyof BuildOptions] = true;
}
else {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Unknown_build_option_0, arg));
}
}
else {
// Not a flag, parse as filename
addProject(arg);
}
}
if (buildOptions.help) {
printVersion();
printHelp(buildOpts, "--build ");
return ExitStatus.Success;
}
// Update to pretty if host supports it
updateReportDiagnostic();
if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.watch) {
reportWatchModeWithoutSysSupport();
}
// Nonsensical combinations
if (buildOptions.clean && buildOptions.force) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.clean && buildOptions.verbose) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.clean && buildOptions.watch) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.watch && buildOptions.dry) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (projects.length === 0) {
// tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ."
addProject(".");
}
// TODO: change this to host if watch => watchHost otherwiue without wathc
const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()), projects, buildOptions);
if (buildOptions.clean) {
return builder.cleanAllProjects();
}
if (buildOptions.watch) {
builder.buildAllProjects();
builder.startWatching();
return undefined;
}
return builder.buildAllProjects();
function addProject(projectSpecification: string) {
const fileName = resolvePath(sys.getCurrentDirectory(), projectSpecification);
const refPath = resolveProjectReferencePath(sys, { path: fileName });
if (!sys.fileExists(refPath)) {
return reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
}
projects.push(refPath);
}
}
function performCompilation(rootNames: string[], projectReferences: ReadonlyArray<ProjectReference> | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
const host = createCompilerHost(options);
enableStatistics(options);
@ -205,7 +337,7 @@ namespace ts {
};
}
function createWatchStatusReporter(options: CompilerOptions) {
function createWatchStatusReporter(options?: CompilerOptions) {
return ts.createWatchStatusReporter(sys, shouldBePretty(options));
}

View file

@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;
@ -2993,6 +2990,16 @@ declare namespace ts {
Parameters = 1296,
IndexSignatureParameters = 4432
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
declare function clearTimeout(handle: any): void;
@ -4312,15 +4319,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@ -4353,14 +4371,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options
@ -4636,14 +4646,6 @@ declare namespace ts {
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
}
interface LanguageService {
cleanupSemanticCache(): void;
getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[];
@ -7795,6 +7797,7 @@ declare namespace ts.server.protocol {
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
interface CompilerOptions {
allowJs?: boolean;
@ -8198,6 +8201,11 @@ declare namespace ts.server {
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void;
}
}
declare namespace ts {
interface UserPreferences {
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
}
declare namespace ts.server {
const maxProgramSizeForNonTsFiles: number;
const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
@ -8325,7 +8333,6 @@ declare namespace ts.server {
cancellationToken: HostCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
lazyConfiguredProjectsFromExternalProject?: boolean;
typingsInstaller: ITypingsInstaller;
eventHandler?: ProjectServiceEventHandler;
suppressDiagnosticEvents?: boolean;
@ -8398,7 +8405,6 @@ declare namespace ts.server {
readonly cancellationToken: HostCancellationToken;
readonly useSingleInferredProject: boolean;
readonly useInferredProjectPerProjectRoot: boolean;
private readonly lazyConfiguredProjectsFromExternalProject?;
readonly typingsInstaller: ITypingsInstaller;
private readonly globalCacheLocationDirectoryPath;
readonly throttleWaitMilliseconds?: number;
@ -8600,7 +8606,6 @@ declare namespace ts.server {
cancellationToken: ServerCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
lazyConfiguredProjectsFromExternalProject?: boolean;
typingsInstaller: ITypingsInstaller;
byteLength: (buf: string, encoding?: string) => number;
hrtime: (start?: number[]) => number[];

View file

@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;
@ -2993,6 +2990,16 @@ declare namespace ts {
Parameters = 1296,
IndexSignatureParameters = 4432
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
declare function clearTimeout(handle: any): void;
@ -4312,15 +4319,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@ -4353,14 +4371,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options
@ -4636,14 +4646,6 @@ declare namespace ts {
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
}
interface LanguageService {
cleanupSemanticCache(): void;
getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[];

View file

@ -1,14 +1,22 @@
tests/cases/compiler/noUnusedLocals_writeOnly.ts(1,12): error TS6133: 'x' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_writeOnly.ts(10,9): error TS6133: 'z' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_writeOnly.ts(18,9): error TS6133: 'z' is declared but its value is never read.
==== tests/cases/compiler/noUnusedLocals_writeOnly.ts (2 errors) ====
function f(x = 0) {
function f(x = 0, b = false) {
~
!!! error TS6133: 'x' is declared but its value is never read.
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
([x] = [1]);
({ x } = { x: 1 });
({ x: x } = { x: 1 });
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
({ x = 2 } = { x: b ? 1 : undefined });
let used = 1;
({ x = used } = { x: b ? 1 : undefined });
let y = 0;
// This is a write access to y, but not a write-*only* access.
@ -19,4 +27,5 @@ tests/cases/compiler/noUnusedLocals_writeOnly.ts(10,9): error TS6133: 'z' is dec
!!! error TS6133: 'z' is declared but its value is never read.
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_: ReadonlyArray<number>): void {}

View file

@ -1,8 +1,16 @@
//// [noUnusedLocals_writeOnly.ts]
function f(x = 0) {
function f(x = 0, b = false) {
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
([x] = [1]);
({ x } = { x: 1 });
({ x: x } = { x: 1 });
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
({ x = 2 } = { x: b ? 1 : undefined });
let used = 1;
({ x = used } = { x: b ? 1 : undefined });
let y = 0;
// This is a write access to y, but not a write-*only* access.
@ -11,17 +19,30 @@ function f(x = 0) {
let z = 0;
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_: ReadonlyArray<number>): void {}
//// [noUnusedLocals_writeOnly.js]
function f(x) {
"use strict";
function f(x, b) {
if (x === void 0) { x = 0; }
if (b === void 0) { b = false; }
var _a, _b;
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
(x = [1][0]);
(x = { x: 1 }.x);
(x = { x: 1 }.x);
(x = { a: [{ b: 1 }] }.a[0].b);
(_a = { x: b ? 1 : undefined }.x, x = _a === void 0 ? 2 : _a);
var used = 1;
(_b = { x: b ? 1 : undefined }.x, x = _b === void 0 ? used : _b);
var y = 0;
// This is a write access to y, but not a write-*only* access.
f(y++);
var z = 0;
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_) { }

View file

@ -1,8 +1,10 @@
=== tests/cases/compiler/noUnusedLocals_writeOnly.ts ===
function f(x = 0) {
function f(x = 0, b = false) {
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
// None of these statements read from 'x', so it will be marked unused.
x = 1;
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
@ -12,19 +14,58 @@ function f(x = 0) {
x /= 2;
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
([x] = [1]);
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
({ x } = { x: 1 });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 6, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 6, 14))
({ x: x } = { x: 1 });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 7, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 7, 17))
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
>a : Symbol(a, Decl(noUnusedLocals_writeOnly.ts, 8, 6))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 8, 12))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>a : Symbol(a, Decl(noUnusedLocals_writeOnly.ts, 8, 26))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 8, 32))
({ x = 2 } = { x: b ? 1 : undefined });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 9, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 9, 18))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
>undefined : Symbol(undefined)
let used = 1;
>used : Symbol(used, Decl(noUnusedLocals_writeOnly.ts, 10, 7))
({ x = used } = { x: b ? 1 : undefined });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 11, 6))
>used : Symbol(used, Decl(noUnusedLocals_writeOnly.ts, 10, 7))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 11, 21))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
>undefined : Symbol(undefined)
let y = 0;
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 5, 7))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 13, 7))
// This is a write access to y, but not a write-*only* access.
f(y++);
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 5, 7))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 13, 7))
let z = 0;
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 9, 7))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 17, 7))
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 9, 7))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 17, 7))
}
function f2(_: ReadonlyArray<number>): void {}
>f2 : Symbol(f2, Decl(noUnusedLocals_writeOnly.ts, 19, 1))
>_ : Symbol(_, Decl(noUnusedLocals_writeOnly.ts, 20, 12))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))

View file

@ -1,9 +1,12 @@
=== tests/cases/compiler/noUnusedLocals_writeOnly.ts ===
function f(x = 0) {
>f : (x?: number) => void
function f(x = 0, b = false) {
>f : (x?: number, b?: boolean) => void
>x : number
>0 : 0
>b : boolean
>false : false
// None of these statements read from 'x', so it will be marked unused.
x = 1;
>x = 1 : 1
>x : number
@ -18,6 +21,79 @@ function f(x = 0) {
>x : number
>2 : 2
([x] = [1]);
>([x] = [1]) : [number]
>[x] = [1] : [number]
>[x] : [number]
>x : number
>[1] : [number]
>1 : 1
({ x } = { x: 1 });
>({ x } = { x: 1 }) : { x: number; }
>{ x } = { x: 1 } : { x: number; }
>{ x } : { x: number; }
>x : number
>{ x: 1 } : { x: number; }
>x : number
>1 : 1
({ x: x } = { x: 1 });
>({ x: x } = { x: 1 }) : { x: number; }
>{ x: x } = { x: 1 } : { x: number; }
>{ x: x } : { x: number; }
>x : number
>x : number
>{ x: 1 } : { x: number; }
>x : number
>1 : 1
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
>({ a: [{ b: x }] } = { a: [{ b: 1 }] }) : { a: [{ b: number; }]; }
>{ a: [{ b: x }] } = { a: [{ b: 1 }] } : { a: [{ b: number; }]; }
>{ a: [{ b: x }] } : { a: [{ b: number; }]; }
>a : [{ b: number; }]
>[{ b: x }] : [{ b: number; }]
>{ b: x } : { b: number; }
>b : number
>x : number
>{ a: [{ b: 1 }] } : { a: [{ b: number; }]; }
>a : [{ b: number; }]
>[{ b: 1 }] : [{ b: number; }]
>{ b: 1 } : { b: number; }
>b : number
>1 : 1
({ x = 2 } = { x: b ? 1 : undefined });
>({ x = 2 } = { x: b ? 1 : undefined }) : { x?: number | undefined; }
>{ x = 2 } = { x: b ? 1 : undefined } : { x?: number | undefined; }
>{ x = 2 } : { x?: number; }
>x : number
>2 : 2
>{ x: b ? 1 : undefined } : { x?: number | undefined; }
>x : number | undefined
>b ? 1 : undefined : 1 | undefined
>b : boolean
>1 : 1
>undefined : undefined
let used = 1;
>used : number
>1 : 1
({ x = used } = { x: b ? 1 : undefined });
>({ x = used } = { x: b ? 1 : undefined }) : { x?: number | undefined; }
>{ x = used } = { x: b ? 1 : undefined } : { x?: number | undefined; }
>{ x = used } : { x?: number; }
>x : number
>used : number
>{ x: b ? 1 : undefined } : { x?: number | undefined; }
>x : number | undefined
>b ? 1 : undefined : 1 | undefined
>b : boolean
>1 : 1
>undefined : undefined
let y = 0;
>y : number
>0 : 0
@ -25,7 +101,7 @@ function f(x = 0) {
// This is a write access to y, but not a write-*only* access.
f(y++);
>f(y++) : void
>f : (x?: number) => void
>f : (x?: number, b?: boolean) => void
>y++ : number
>y : number
@ -35,9 +111,12 @@ function f(x = 0) {
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
>f(z = 1) : void
>f : (x?: number) => void
>f : (x?: number, b?: boolean) => void
>z = 1 : 1
>z : number
>1 : 1
}
function f2(_: ReadonlyArray<number>): void {}
>f2 : (_: ReadonlyArray<number>) => void
>_ : ReadonlyArray<number>

View file

@ -0,0 +1,54 @@
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(3,20): error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(8,27): error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(13,20): error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(21,18): error TS2372: Parameter 'a' cannot be referenced in its initializer.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(25,22): error TS2372: Parameter 'async' cannot be referenced in its initializer.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts(29,15): error TS2448: Block-scoped variable 'foo' used before its declaration.
==== tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts (6 errors) ====
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
~~~
!!! error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
~~~
!!! error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
~~~
!!! error TS2373: Initializer of parameter 'bar' cannot reference identifier 'foo' declared after it.
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
~
!!! error TS2372: Parameter 'a' cannot be referenced in its initializer.
return a
}
function f6 (async = async) {
~~~~~
!!! error TS2372: Parameter 'async' cannot be referenced in its initializer.
return async
}
function f7({[foo]: bar}: any[]) {
~~~
!!! error TS2448: Block-scoped variable 'foo' used before its declaration.
!!! related TS2728 tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts:30:9: 'foo' is declared here.
let foo: number = 2;
}

View file

@ -0,0 +1,70 @@
//// [parameterInitializersForwardReferencing1.ts]
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
return a
}
function f6 (async = async) {
return async
}
function f7({[foo]: bar}: any[]) {
let foo: number = 2;
}
//// [parameterInitializersForwardReferencing1.js]
var foo = "";
function f1(bar) {
if (bar === void 0) { bar = foo; }
var foo = 2;
return bar; // returns 1
}
function f2(bar) {
if (bar === void 0) { bar = function (baz) {
if (baz === void 0) { baz = foo; }
return baz;
}; }
var foo = 2;
return bar(); // returns 1
}
function f3(bar, foo) {
if (bar === void 0) { bar = foo; }
if (foo === void 0) { foo = 2; }
return bar;
}
function f4(foo, bar) {
if (bar === void 0) { bar = foo; }
return bar;
}
function f5(a) {
if (a === void 0) { a = a; }
return a;
}
function f6(async) {
if (async === void 0) { async = async; }
return async;
}
function f7(_a) {
var _b = foo, bar = _a[_b];
var foo = 2;
}

View file

@ -0,0 +1,77 @@
=== tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts ===
let foo: string = "";
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 0, 3))
function f1 (bar = foo) { // unexpected compiler error; works at runtime
>f1 : Symbol(f1, Decl(parameterInitializersForwardReferencing1.ts, 0, 21))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 2, 13))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 3, 7))
var foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 3, 7))
return bar; // returns 1
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 2, 13))
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
>f2 : Symbol(f2, Decl(parameterInitializersForwardReferencing1.ts, 5, 1))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 7, 13))
>baz : Symbol(baz, Decl(parameterInitializersForwardReferencing1.ts, 7, 20))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 8, 7))
>baz : Symbol(baz, Decl(parameterInitializersForwardReferencing1.ts, 7, 20))
var foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 8, 7))
return bar(); // returns 1
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 7, 13))
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
>f3 : Symbol(f3, Decl(parameterInitializersForwardReferencing1.ts, 10, 1))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 12, 13))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 12, 23))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 12, 23))
return bar;
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 12, 13))
}
function f4 (foo, bar = foo) {
>f4 : Symbol(f4, Decl(parameterInitializersForwardReferencing1.ts, 14, 1))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 16, 13))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 16, 17))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 16, 13))
return bar
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 16, 17))
}
function f5 (a = a) {
>f5 : Symbol(f5, Decl(parameterInitializersForwardReferencing1.ts, 18, 1))
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1.ts, 20, 13))
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1.ts, 20, 13))
return a
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1.ts, 20, 13))
}
function f6 (async = async) {
>f6 : Symbol(f6, Decl(parameterInitializersForwardReferencing1.ts, 22, 1))
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1.ts, 24, 13))
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1.ts, 24, 13))
return async
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1.ts, 24, 13))
}
function f7({[foo]: bar}: any[]) {
>f7 : Symbol(f7, Decl(parameterInitializersForwardReferencing1.ts, 26, 1))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 29, 7))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1.ts, 28, 13))
let foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1.ts, 29, 7))
}

View file

@ -0,0 +1,84 @@
=== tests/cases/conformance/functions/parameterInitializersForwardReferencing1.ts ===
let foo: string = "";
>foo : string
>"" : ""
function f1 (bar = foo) { // unexpected compiler error; works at runtime
>f1 : (bar?: number) => number
>bar : number
>foo : number
var foo: number = 2;
>foo : number
>2 : 2
return bar; // returns 1
>bar : number
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
>f2 : (bar?: (baz?: number) => number) => number
>bar : (baz?: number) => number
>(baz = foo) => baz : (baz?: number) => number
>baz : number
>foo : number
>baz : number
var foo: number = 2;
>foo : number
>2 : 2
return bar(); // returns 1
>bar() : number
>bar : (baz?: number) => number
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
>f3 : (bar?: number, foo?: number) => number
>bar : number
>foo : number
>foo : number
>2 : 2
return bar;
>bar : number
}
function f4 (foo, bar = foo) {
>f4 : (foo: any, bar?: any) => any
>foo : any
>bar : any
>foo : any
return bar
>bar : any
}
function f5 (a = a) {
>f5 : (a?: any) => any
>a : any
>a : any
return a
>a : any
}
function f6 (async = async) {
>f6 : (async?: any) => any
>async : any
>async : any
return async
>async : any
}
function f7({[foo]: bar}: any[]) {
>f7 : ({ [foo]: bar }: any[]) => void
>foo : number
>bar : any
let foo: number = 2;
>foo : number
>2 : 2
}

View file

@ -0,0 +1,41 @@
tests/cases/conformance/functions/parameterInitializersForwardReferencing1_es6.ts(21,18): error TS2372: Parameter 'a' cannot be referenced in its initializer.
tests/cases/conformance/functions/parameterInitializersForwardReferencing1_es6.ts(25,22): error TS2372: Parameter 'async' cannot be referenced in its initializer.
==== tests/cases/conformance/functions/parameterInitializersForwardReferencing1_es6.ts (2 errors) ====
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
~
!!! error TS2372: Parameter 'a' cannot be referenced in its initializer.
return a
}
function f6 (async = async) {
~~~~~
!!! error TS2372: Parameter 'async' cannot be referenced in its initializer.
return async
}
function f7({[foo]: bar}: any[]) {
let foo: number = 2;
}

View file

@ -0,0 +1,59 @@
//// [parameterInitializersForwardReferencing1_es6.ts]
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
return a
}
function f6 (async = async) {
return async
}
function f7({[foo]: bar}: any[]) {
let foo: number = 2;
}
//// [parameterInitializersForwardReferencing1_es6.js]
let foo = "";
function f1(bar = foo) {
var foo = 2;
return bar; // returns 1
}
function f2(bar = (baz = foo) => baz) {
var foo = 2;
return bar(); // returns 1
}
function f3(bar = foo, foo = 2) {
return bar;
}
function f4(foo, bar = foo) {
return bar;
}
function f5(a = a) {
return a;
}
function f6(async = async) {
return async;
}
function f7({ [foo]: bar }) {
let foo = 2;
}

View file

@ -0,0 +1,77 @@
=== tests/cases/conformance/functions/parameterInitializersForwardReferencing1_es6.ts ===
let foo: string = "";
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
function f1 (bar = foo) { // unexpected compiler error; works at runtime
>f1 : Symbol(f1, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 21))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 2, 13))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
var foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 3, 7))
return bar; // returns 1
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 2, 13))
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
>f2 : Symbol(f2, Decl(parameterInitializersForwardReferencing1_es6.ts, 5, 1))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 7, 13))
>baz : Symbol(baz, Decl(parameterInitializersForwardReferencing1_es6.ts, 7, 20))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
>baz : Symbol(baz, Decl(parameterInitializersForwardReferencing1_es6.ts, 7, 20))
var foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 8, 7))
return bar(); // returns 1
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 7, 13))
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
>f3 : Symbol(f3, Decl(parameterInitializersForwardReferencing1_es6.ts, 10, 1))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 12, 13))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 12, 23))
return bar;
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 12, 13))
}
function f4 (foo, bar = foo) {
>f4 : Symbol(f4, Decl(parameterInitializersForwardReferencing1_es6.ts, 14, 1))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 16, 13))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 16, 17))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
return bar
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 16, 17))
}
function f5 (a = a) {
>f5 : Symbol(f5, Decl(parameterInitializersForwardReferencing1_es6.ts, 18, 1))
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1_es6.ts, 20, 13))
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1_es6.ts, 20, 13))
return a
>a : Symbol(a, Decl(parameterInitializersForwardReferencing1_es6.ts, 20, 13))
}
function f6 (async = async) {
>f6 : Symbol(f6, Decl(parameterInitializersForwardReferencing1_es6.ts, 22, 1))
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1_es6.ts, 24, 13))
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1_es6.ts, 24, 13))
return async
>async : Symbol(async, Decl(parameterInitializersForwardReferencing1_es6.ts, 24, 13))
}
function f7({[foo]: bar}: any[]) {
>f7 : Symbol(f7, Decl(parameterInitializersForwardReferencing1_es6.ts, 26, 1))
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 0, 3))
>bar : Symbol(bar, Decl(parameterInitializersForwardReferencing1_es6.ts, 28, 13))
let foo: number = 2;
>foo : Symbol(foo, Decl(parameterInitializersForwardReferencing1_es6.ts, 29, 7))
}

View file

@ -0,0 +1,84 @@
=== tests/cases/conformance/functions/parameterInitializersForwardReferencing1_es6.ts ===
let foo: string = "";
>foo : string
>"" : ""
function f1 (bar = foo) { // unexpected compiler error; works at runtime
>f1 : (bar?: string) => string
>bar : string
>foo : string
var foo: number = 2;
>foo : number
>2 : 2
return bar; // returns 1
>bar : string
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
>f2 : (bar?: (baz?: string) => string) => string
>bar : (baz?: string) => string
>(baz = foo) => baz : (baz?: string) => string
>baz : string
>foo : string
>baz : string
var foo: number = 2;
>foo : number
>2 : 2
return bar(); // returns 1
>bar() : string
>bar : (baz?: string) => string
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
>f3 : (bar?: string, foo?: number) => string
>bar : string
>foo : string
>foo : number
>2 : 2
return bar;
>bar : string
}
function f4 (foo, bar = foo) {
>f4 : (foo: any, bar?: string) => string
>foo : any
>bar : string
>foo : string
return bar
>bar : string
}
function f5 (a = a) {
>f5 : (a?: any) => any
>a : any
>a : any
return a
>a : any
}
function f6 (async = async) {
>f6 : (async?: any) => any
>async : any
>async : any
return async
>async : any
}
function f7({[foo]: bar}: any[]) {
>f7 : ({ [foo]: bar }: any[]) => void
>foo : string
>bar : any
let foo: number = 2;
>foo : number
>2 : 2
}

View file

@ -1,10 +1,19 @@
// @strict: true
// @noUnusedLocals: true
// @noUnusedParameters: true
function f(x = 0) {
function f(x = 0, b = false) {
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
([x] = [1]);
({ x } = { x: 1 });
({ x: x } = { x: 1 });
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
({ x = 2 } = { x: b ? 1 : undefined });
let used = 1;
({ x = used } = { x: b ? 1 : undefined });
let y = 0;
// This is a write access to y, but not a write-*only* access.
@ -13,3 +22,4 @@ function f(x = 0) {
let z = 0;
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_: ReadonlyArray<number>): void {}

View file

@ -0,0 +1,31 @@
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
return a
}
function f6 (async = async) {
return async
}
function f7({[foo]: bar}: any[]) {
let foo: number = 2;
}

View file

@ -0,0 +1,33 @@
// @target: es2015
let foo: string = "";
function f1 (bar = foo) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar; // returns 1
}
function f2 (bar = (baz = foo) => baz) { // unexpected compiler error; works at runtime
var foo: number = 2;
return bar(); // returns 1
}
function f3 (bar = foo, foo = 2) { // correct compiler error, error at runtime
return bar;
}
function f4 (foo, bar = foo) {
return bar
}
function f5 (a = a) {
return a
}
function f6 (async = async) {
return async
}
function f7({[foo]: bar}: any[]) {
let foo: number = 2;
}

View file

@ -11,7 +11,7 @@
////fooB/*1*/
goTo.marker("0");
const preferences = { includeCompletionsForModuleExports: true };
const preferences: FourSlashInterface.UserPreferences = { includeCompletionsForModuleExports: true };
verify.completions(
{ marker: "0", excludes: { name: "default", source: "/src/foo-bar" }, preferences },
{ marker: "1", includes: { name: "fooBar", source: "/src/foo-bar", sourceDisplay: "./foo-bar", text: "(property) default: 0", kind: "property", hasAction: true }, preferences }

View file

@ -0,0 +1,53 @@
/// <reference path="fourslash.ts" />
// @module: commonjs
// @Filename: /a.d.ts
////declare function a(): void;
////declare namespace a {
//// export interface b {}
////}
////export = a;
// @Filename: /b.ts
////a/*0*/;
////let x: b/*1*/;
const preferences: FourSlashInterface.UserPreferences = { includeCompletionsForModuleExports: true };
verify.completions(
{
marker: "0",
includes: { name: "a", source: "/a", hasAction: true, },
preferences,
},
{
marker: "1",
includes: { name: "b", source: "/a", hasAction: true },
preferences,
}
);
// Import { b } first, or it will just add a qualified name from 'a' (which isn't what we're trying to test)
verify.applyCodeActionFromCompletion("1", {
name: "b",
source: "/a",
description: `Import 'b' from module "./a"`,
newFileContent:
`import { b } from "./a";
a;
let x: b;`,
});
verify.applyCodeActionFromCompletion("0", {
name: "a",
source: "/a",
description: `Import 'a' from module "./a"`,
newFileContent:
`import { b } from "./a";
import a = require("./a");
a;
let x: b;`,
});

View file

@ -18,7 +18,7 @@
verify.completions({
marker: "",
// Tester will assert that it is only included once
includes: [{ name: "foo", hasAction: true }],
includes: [{ name: "foo", source: "a", hasAction: true }],
preferences: {
includeCompletionsForModuleExports: true,
}

View file

@ -12,7 +12,7 @@
verify.completions({
marker: "",
includes: { name: "Foo", source: "/a.tsx", hasAction: true },
includes: { name: "Foo", source: "/a", hasAction: true },
excludes: "Bar",
preferences: {
includeCompletionsForModuleExports: true,

View file

@ -24,7 +24,7 @@ verify.completions({
marker: "",
exact: [
"n",
{ name: "publicSym", insertText: "[publicSym]", replacementSpan: test.ranges()[0], hasAction: true },
{ name: "publicSym", source: "/a", insertText: "[publicSym]", replacementSpan: test.ranges()[0], hasAction: true },
],
preferences: {
includeInsertTextCompletions: true,

View file

@ -528,10 +528,11 @@ declare namespace FourSlashInterface {
filesToSearch?: ReadonlyArray<string>;
}
interface UserPreferences {
quotePreference?: "double" | "single";
includeCompletionsForModuleExports?: boolean;
includeInsertTextCompletions?: boolean;
importModuleSpecifierPreference?: "relative" | "non-relative";
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeInsertTextCompletions?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
}
interface CompletionsAtOptions extends UserPreferences {
triggerCharacter?: string;

View file

@ -11,21 +11,37 @@
////var x : testClass</*type2*/
////class Bar<T> extends testClass</*type3*/
////var x : testClass<,, /*type4*/any>;
////
////interface I<T> {}
////let i: I</*interface*/>;
////
////type Ty<T> = T;
////let t: Ty</*typeAlias*/>;
// TODO: GH#26699
if (false) {
verify.signatureHelp(
{
marker: ["type1", "type2", "type3"],
text: "testClass<T extends IFoo, U, M extends IFoo>",
parameterName: "T",
parameterSpan: "T extends IFoo",
},
{
marker: "type4",
parameterName: "M",
parameterSpan: "M extends IFoo",
}
);
}
verify.signatureHelp(
{
marker: ["type1", "type2", "type3"],
text: "testClass<T extends IFoo, U, M extends IFoo>",
parameterName: "T",
parameterSpan: "T extends IFoo",
triggerReason: { kind: "characterTyped", triggerCharacter: "<" },
},
{
marker: "type4",
parameterName: "M",
parameterSpan: "M extends IFoo",
triggerReason: { kind: "characterTyped", triggerCharacter: "," },
},
{
marker: "interface",
text: "I<T>",
parameterName: "T",
parameterSpan: "T",
},
{
marker: "typeAlias",
text: "Ty<T>",
parameterName: "T",
parameterSpan: "T",
},
);

View file

@ -0,0 +1,44 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @checkJs: true
// @strict: true
// @jsx: preserve
// @resolveJsonModule: true
// @Filename: /index.js
////export const x = 0;
// @Filename: /jsx.jsx
////export const y = 0;
// @Filename: /j.jonah.json
////{ "j": 0 }
// @Filename: /a.js
////import { x as x0 } from ".";
////import { x as x1 } from "./index";
////import { x as x2 } from "./index.js";
////import { y } from "./jsx.jsx";
////import { j } from "./j.jonah.json";
verify.noErrors();
verify.getEditsForFileRename({
oldPath: "/a.js",
newPath: "/b.js",
newFileContents: {}, // No change
});
verify.getEditsForFileRename({
oldPath: "/b.js",
newPath: "/src/b.js",
newFileContents: {
"/b.js":
`import { x as x0 } from "..";
import { x as x1 } from "../index";
import { x as x2 } from "../index.js";
import { y } from "../jsx.jsx";
import { j } from "../j.jonah.json";`,
},
});

View file

@ -21,9 +21,20 @@
////o.[|/*useM*/m|]();
////class Component { /*componentCtr*/constructor(props: {}) {} }
////type ComponentClass = /*ComponentClass*/new () => Component;
////interface ComponentClass2 { /*ComponentClass2*/new(): Component; }
////
////class /*MyComponent*/MyComponent extends Component {}
////<[|/*jsxMyComponent*/MyComponent|] />
////<[|/*jsxMyComponent*/MyComponent|] />;
////new [|/*newMyComponent*/MyComponent|]({});
////
////declare const /*MyComponent2*/MyComponent2: ComponentClass;
////<[|/*jsxMyComponent2*/MyComponent2|] />;
////new [|/*newMyComponent2*/MyComponent2|]();
////
////declare const /*MyComponent3*/MyComponent3: ComponentClass2;
////<[|/*jsxMyComponent3*/MyComponent3|] />;
////new [|/*newMyComponent3*/MyComponent3|]();
verify.noErrors();
@ -38,4 +49,10 @@ verify.goToDefinition({
jsxMyComponent: "MyComponent",
newMyComponent: ["MyComponent", "componentCtr"],
jsxMyComponent2: "MyComponent2",
newMyComponent2: ["MyComponent2", "ComponentClass"],
jsxMyComponent3: "MyComponent3",
newMyComponent3: ["MyComponent3", "ComponentClass2"],
});

View file

@ -1,22 +1,23 @@
/// <reference path="fourslash.ts" />
// @Filename: a/f1.ts
//// [|foo/*0*/();|]
// @Filename: types/random/index.ts
//// export function foo() {};
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "typeRoots": [
//// "./types"
//// ]
//// }
//// }
verify.importFixAtPosition([
`import { foo } from "random";
foo();`
]);
/// <reference path="fourslash.ts" />
// @Filename: a/f1.ts
//// [|foo/*0*/();|]
// @Filename: types/random/index.ts
//// export function foo() {};
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "typeRoots": [
//// "./types"
//// ]
//// }
//// }
// "typeRoots" does not affect module resolution. Importing from "random" would be a compile error.
verify.importFixAtPosition([
`import { foo } from "../types/random";
foo();`
]);

View file

@ -1,23 +1,27 @@
/// <reference path="fourslash.ts" />
// @Filename: a/f1.ts
//// [|foo/*0*/();|]
// @Filename: types/random/index.ts
//// export function foo() {};
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": ".",
//// "typeRoots": [
//// "./types"
//// ]
//// }
//// }
verify.importFixAtPosition([
`import { foo } from "random";
foo();`
]);
/// <reference path="fourslash.ts" />
// @Filename: a/f1.ts
//// [|foo/*0*/();|]
// @Filename: types/random/index.ts
//// export function foo() {};
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": ".",
//// "typeRoots": [
//// "./types"
//// ]
//// }
//// }
// "typeRoots" does not affect module resolution. Importing from "random" would be a compile error.
verify.importFixAtPosition([
`import { foo } from "types/random";
foo();`,
`import { foo } from "../types/random";
foo();`
]);

View file

@ -37,11 +37,11 @@ verify.codeFixAll({
fixId: "fixMissingImport",
fixAllDescription: "Add all missing imports",
newFileContent:
// TODO: GH#25135 (should import 'e')
`import bd, * as b from "./b";
import cd, { c0 } from "./c";
import dd, { d0, d1 } from "./d";
import ad, { a0 } from "./a";
import e = require("./e");
ad; ad; a0; a0;
bd; bd; b.b0; b.b0;

View file

@ -0,0 +1,26 @@
/// <reference path="fourslash.ts" />
// @moduleResolution: node
// @Filename: /foo/index.ts
////export const foo = 0;
// @Filename: /a.ts
////foo;
// @Filename: /b.ts
////foo;
// @Filename: /c.ts
////foo;
const tests: ReadonlyArray<[string, FourSlashInterface.UserPreferences["importModuleSpecifierEnding"], string]> = [
["/a.ts", "js", "./foo/index.js"],
["/b.ts", "index", "./foo/index"],
["/c.ts", "minimal", "./foo"],
];
for (const [fileName, importModuleSpecifierEnding, specifier] of tests) {
goTo.file(fileName);
verify.importFixAtPosition([`import { foo } from "${specifier}";\n\nfoo;`,], /*errorCode*/ undefined, { importModuleSpecifierEnding });
}

View file

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.d.ts
////declare function a(): void;
////declare namespace a {
//// export interface b {}
////}
////export = a;
// @Filename: /b.ts
////a;
////let x: b;
goTo.file("/b.ts");
verify.codeFixAll({
fixId: "fixMissingImport",
fixAllDescription: "Add all missing imports",
newFileContent:
`import { b } from "./a";
import a = require("./a");
a;
let x: b;`,
});

View file

@ -2,6 +2,7 @@
// @moduleResolution: node
// @noLib: true
// @jsx: preserve
// @Filename: /a.ts
////export function a() {}
@ -9,17 +10,24 @@
// @Filename: /b.ts
////export function b() {}
// @Filename: /c.tsx
////export function c() {}
// @Filename: /c.ts
////import * as g from "global"; // Global imports skipped
////import { a } from "./a.js";
////import { a as a2 } from "./a"; // Ignored, only the first relative import is considered
////b;
////b; c;
goTo.file("/c.ts");
verify.importFixAtPosition([
verify.codeFixAll({
fixId: "fixMissingImport",
fixAllDescription: "Add all missing imports",
newFileContent:
`import * as g from "global"; // Global imports skipped
import { a } from "./a.js";
import { a as a2 } from "./a"; // Ignored, only the first relative import is considered
import { b } from "./b.js";
b;`,
]);
import { c } from "./c.jsx";
b; c;`,
});

View file

@ -5,12 +5,22 @@
// @Filename: /node_modules/@types/foo/index.d.ts
////export const xyz: number;
// @Filename: /node_modules/bar/index.d.ts
////export const qrs: number;
// @Filename: /a.ts
////[|xyz|]
////xyz;
////qrs;
goTo.file("/a.ts");
verify.importFixAtPosition([
verify.codeFixAll({
fixId: "fixMissingImport",
fixAllDescription: "Add all missing imports",
newFileContent:
`import { xyz } from "foo";
xyz`
]);
import { qrs } from "./node_modules/bar/index";
xyz;
qrs;`,
});