mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'main' into release-5.1
This commit is contained in:
@@ -43,10 +43,8 @@ Please keep and fill in the line that best applies:
|
||||
### ⏯ Playground Link
|
||||
|
||||
<!--
|
||||
A link to a TypeScript Playground "Share" link which shows this behavior
|
||||
|
||||
The TypeScript Workbench can be used for more complex setups, try
|
||||
https://www.typescriptlang.org/dev/bug-workbench/
|
||||
A link to a TypeScript Playground "Share" link which shows this behavior.
|
||||
This should have the same code as the code snippet below, and use whichever settings are relevant to your report.
|
||||
|
||||
As a last resort, you can link to a repo, but these will be slower for us to investigate.
|
||||
-->
|
||||
@@ -54,7 +52,7 @@ Please keep and fill in the line that best applies:
|
||||
|
||||
### 💻 Code
|
||||
|
||||
<!-- Please post the relevant code sample here as well-->
|
||||
<!-- Please post the relevant code sample here as well. This code and the Playground code should be the same, do not use separate examples -->
|
||||
```ts
|
||||
// We can quickly address your report if:
|
||||
// - The code sample is short. Nearly all TypeScript bugs can be demonstrated in 20-30 lines of code!
|
||||
|
||||
@@ -12,3 +12,4 @@ gabritto
|
||||
jakebailey
|
||||
DanielRosenwasser
|
||||
navya9singh
|
||||
iisaduan
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- "19"
|
||||
- "20"
|
||||
- "18"
|
||||
- "16"
|
||||
- "14"
|
||||
@@ -202,3 +202,30 @@ jobs:
|
||||
|
||||
- name: Self build
|
||||
run: npx hereby build-src --built
|
||||
|
||||
unused-baselines:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "*"
|
||||
check-latest: true
|
||||
- run: npm ci
|
||||
|
||||
- name: Remove all baselines
|
||||
run: rm -rf tests/baselines/reference
|
||||
|
||||
- name: Run tests
|
||||
run: npm test &> /dev/null || exit 0
|
||||
|
||||
- name: Accept baselines
|
||||
run: npx hereby baseline-accept
|
||||
|
||||
- name: Check for unused baselines
|
||||
run: |
|
||||
if ! git diff --exit-code --quiet; then
|
||||
echo "Unused baselines:"
|
||||
git diff --exit-code --name-only
|
||||
fi
|
||||
|
||||
@@ -616,7 +616,6 @@ export const runTestsAndWatch = task({
|
||||
});
|
||||
|
||||
process.on("SIGINT", endWatchMode);
|
||||
process.on("SIGKILL", endWatchMode);
|
||||
process.on("beforeExit", endWatchMode);
|
||||
watchTestsEmitter.on("rebuild", onRebuild);
|
||||
testCaseWatcher.on("all", onChange);
|
||||
|
||||
Generated
+416
-410
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -108,9 +108,9 @@
|
||||
"source-map-support": false,
|
||||
"inspector": false
|
||||
},
|
||||
"packageManager": "npm@8.19.3",
|
||||
"packageManager": "npm@8.19.4",
|
||||
"volta": {
|
||||
"node": "14.21.1",
|
||||
"npm": "8.19.3"
|
||||
"node": "20.1.0",
|
||||
"npm": "8.19.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ import {
|
||||
getEnclosingBlockScopeContainer,
|
||||
getErrorSpanForNode,
|
||||
getEscapedTextOfIdentifierOrLiteral,
|
||||
getEscapedTextOfJsxAttributeName,
|
||||
getEscapedTextOfJsxNamespacedName,
|
||||
getExpandoInitializer,
|
||||
getHostSignatureFromJSDoc,
|
||||
getImmediatelyInvokedFunctionExpression,
|
||||
@@ -682,7 +682,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
|
||||
return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText);
|
||||
}
|
||||
if (isJsxNamespacedName(name)) {
|
||||
return getEscapedTextOfJsxAttributeName(name);
|
||||
return getEscapedTextOfJsxNamespacedName(name);
|
||||
}
|
||||
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
|
||||
}
|
||||
|
||||
+91
-4
@@ -22,6 +22,7 @@ import {
|
||||
convertToOptionsWithAbsolutePaths,
|
||||
createBuildInfo,
|
||||
createGetCanonicalFileName,
|
||||
createModuleNotFoundChain,
|
||||
createProgram,
|
||||
CustomTransformers,
|
||||
Debug,
|
||||
@@ -68,8 +69,11 @@ import {
|
||||
ProjectReference,
|
||||
ReadBuildProgramHost,
|
||||
ReadonlyCollection,
|
||||
RepopulateDiagnosticChainInfo,
|
||||
RepopulateModuleNotFoundDiagnosticChain,
|
||||
returnFalse,
|
||||
returnUndefined,
|
||||
sameMap,
|
||||
SemanticDiagnosticsBuilderProgram,
|
||||
skipTypeChecking,
|
||||
some,
|
||||
@@ -103,7 +107,18 @@ export interface ReusableDiagnosticRelatedInformation {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
|
||||
export interface ReusableRepopulateModuleNotFoundChain {
|
||||
info: RepopulateModuleNotFoundDiagnosticChain;
|
||||
next?: ReusableDiagnosticMessageChain[];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type SerializedDiagnosticMessageChain = Omit<DiagnosticMessageChain, "next" | "repopulateInfo"> & {
|
||||
next?: ReusableDiagnosticMessageChain[];
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
export type ReusableDiagnosticMessageChain = SerializedDiagnosticMessageChain | ReusableRepopulateModuleNotFoundChain;
|
||||
|
||||
/**
|
||||
* Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string
|
||||
@@ -364,7 +379,12 @@ function createBuilderProgramState(newProgram: Program, oldState: Readonly<Reusa
|
||||
// Unchanged file copy diagnostics
|
||||
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
|
||||
if (diagnostics) {
|
||||
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) : diagnostics as readonly Diagnostic[]);
|
||||
state.semanticDiagnosticsPerFile!.set(
|
||||
sourceFilePath,
|
||||
oldState!.hasReusableDiagnostic ?
|
||||
convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) :
|
||||
repopulateDiagnostics(diagnostics as readonly Diagnostic[], newProgram)
|
||||
);
|
||||
if (!state.semanticDiagnosticsFromOldState) {
|
||||
state.semanticDiagnosticsFromOldState = new Set();
|
||||
}
|
||||
@@ -448,6 +468,43 @@ function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions:
|
||||
isString(oldEmitSignature) ? [oldEmitSignature] : oldEmitSignature[0];
|
||||
}
|
||||
|
||||
function repopulateDiagnostics(diagnostics: readonly Diagnostic[], newProgram: Program): readonly Diagnostic[] {
|
||||
if (!diagnostics.length) return diagnostics;
|
||||
return sameMap(diagnostics, diag => {
|
||||
if (isString(diag.messageText)) return diag;
|
||||
const repopulatedChain = convertOrRepopulateDiagnosticMessageChain(diag.messageText, diag.file, newProgram, chain => chain.repopulateInfo?.());
|
||||
return repopulatedChain === diag.messageText ?
|
||||
diag :
|
||||
{ ...diag, messageText: repopulatedChain };
|
||||
});
|
||||
}
|
||||
|
||||
function convertOrRepopulateDiagnosticMessageChain<T extends DiagnosticMessageChain | ReusableDiagnosticMessageChain>(
|
||||
chain: T,
|
||||
sourceFile: SourceFile | undefined,
|
||||
newProgram: Program,
|
||||
repopulateInfo: (chain: T) => RepopulateDiagnosticChainInfo | undefined,
|
||||
): DiagnosticMessageChain {
|
||||
const info = repopulateInfo(chain);
|
||||
if (info) {
|
||||
return {
|
||||
...createModuleNotFoundChain(sourceFile!, newProgram, info.moduleReference, info.mode, info.packageName || info.moduleReference),
|
||||
next: convertOrRepopulateDiagnosticMessageChainArray(chain.next as T[], sourceFile, newProgram, repopulateInfo),
|
||||
};
|
||||
}
|
||||
const next = convertOrRepopulateDiagnosticMessageChainArray(chain.next as T[], sourceFile, newProgram, repopulateInfo);
|
||||
return next === chain.next ? chain as DiagnosticMessageChain : { ...chain as DiagnosticMessageChain, next };
|
||||
}
|
||||
|
||||
function convertOrRepopulateDiagnosticMessageChainArray<T extends DiagnosticMessageChain | ReusableDiagnosticMessageChain>(
|
||||
array: T[] | undefined,
|
||||
sourceFile: SourceFile | undefined,
|
||||
newProgram: Program,
|
||||
repopulateInfo: (chain: T) => RepopulateDiagnosticChainInfo | undefined,
|
||||
): DiagnosticMessageChain[] | undefined {
|
||||
return sameMap(array, chain => convertOrRepopulateDiagnosticMessageChain(chain, sourceFile, newProgram, repopulateInfo));
|
||||
}
|
||||
|
||||
function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program): readonly Diagnostic[] {
|
||||
if (!diagnostics.length) return emptyArray;
|
||||
let buildInfoDirectory: string | undefined;
|
||||
@@ -474,9 +531,13 @@ function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newPro
|
||||
|
||||
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
|
||||
const { file } = diagnostic;
|
||||
const sourceFile = file ? newProgram.getSourceFileByPath(toPath(file)) : undefined;
|
||||
return {
|
||||
...diagnostic,
|
||||
file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
|
||||
file: sourceFile,
|
||||
messageText: isString(diagnostic.messageText) ?
|
||||
diagnostic.messageText :
|
||||
convertOrRepopulateDiagnosticMessageChain(diagnostic.messageText, sourceFile, newProgram, chain => (chain as ReusableRepopulateModuleNotFoundChain).info),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1232,10 +1293,36 @@ function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRel
|
||||
const { file } = diagnostic;
|
||||
return {
|
||||
...diagnostic,
|
||||
file: file ? relativeToBuildInfo(file.resolvedPath) : undefined
|
||||
file: file ? relativeToBuildInfo(file.resolvedPath) : undefined,
|
||||
messageText: isString(diagnostic.messageText) ? diagnostic.messageText : convertToReusableDiagnosticMessageChain(diagnostic.messageText),
|
||||
};
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticMessageChain(chain: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
|
||||
if (chain.repopulateInfo) {
|
||||
return {
|
||||
info: chain.repopulateInfo(),
|
||||
next: convertToReusableDiagnosticMessageChainArray(chain.next),
|
||||
};
|
||||
}
|
||||
const next = convertToReusableDiagnosticMessageChainArray(chain.next);
|
||||
return next === chain.next ? chain : { ...chain, next };
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticMessageChainArray(array: DiagnosticMessageChain[] | undefined): ReusableDiagnosticMessageChain[] | undefined {
|
||||
if (!array) return array;
|
||||
return forEach(array, (chain, index) => {
|
||||
const reusable = convertToReusableDiagnosticMessageChain(chain);
|
||||
if (chain === reusable) return undefined;
|
||||
const result: ReusableDiagnosticMessageChain[] = index > 0 ? array.slice(0, index - 1) : [];
|
||||
result.push(reusable);
|
||||
for (let i = index + 1; i < array.length; i++) {
|
||||
result.push(convertToReusableDiagnosticMessageChain(array[i]));
|
||||
}
|
||||
return result;
|
||||
}) || array;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export enum BuilderProgramKind {
|
||||
SemanticDiagnosticsBuilderProgram,
|
||||
|
||||
+225
-135
@@ -110,6 +110,7 @@ import {
|
||||
createGetCanonicalFileName,
|
||||
createGetSymbolWalker,
|
||||
createModeAwareCacheKey,
|
||||
createModuleNotFoundChain,
|
||||
createMultiMap,
|
||||
createPrinterWithDefaults,
|
||||
createPrinterWithRemoveComments,
|
||||
@@ -359,7 +360,6 @@ import {
|
||||
getThisParameter,
|
||||
getTrailingSemicolonDeferringWriter,
|
||||
getTypeParameterFromJsDoc,
|
||||
getTypesPackageName,
|
||||
getUseDefineForClassFields,
|
||||
group,
|
||||
hasAbstractModifier,
|
||||
@@ -776,6 +776,7 @@ import {
|
||||
JSDocVariadicType,
|
||||
JsxAttribute,
|
||||
JsxAttributeLike,
|
||||
JsxAttributeName,
|
||||
JsxAttributes,
|
||||
JsxChild,
|
||||
JsxClosingElement,
|
||||
@@ -805,7 +806,6 @@ import {
|
||||
LiteralExpression,
|
||||
LiteralType,
|
||||
LiteralTypeNode,
|
||||
mangleScopedPackageName,
|
||||
map,
|
||||
mapDefined,
|
||||
MappedSymbol,
|
||||
@@ -814,7 +814,6 @@ import {
|
||||
MatchingKeys,
|
||||
maybeBind,
|
||||
MemberOverrideStatus,
|
||||
memoize,
|
||||
MetaProperty,
|
||||
MethodDeclaration,
|
||||
MethodSignature,
|
||||
@@ -852,7 +851,6 @@ import {
|
||||
nodeIsPresent,
|
||||
nodeIsSynthesized,
|
||||
NodeLinks,
|
||||
nodeModulesPathPart,
|
||||
nodeStartsNewLexicalEnvironment,
|
||||
NodeWithTypeArguments,
|
||||
NonNullChain,
|
||||
@@ -1391,22 +1389,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// Why var? It avoids TDZ checks in the runtime which can be costly.
|
||||
// See: https://github.com/microsoft/TypeScript/issues/52924
|
||||
/* eslint-disable no-var */
|
||||
var getPackagesMap = memoize(() => {
|
||||
// A package name maps to true when we detect it has .d.ts files.
|
||||
// This is useful as an approximation of whether a package bundles its own types.
|
||||
// Note: we only look at files already found by module resolution,
|
||||
// so there may be files we did not consider.
|
||||
var map = new Map<string, boolean>();
|
||||
host.getSourceFiles().forEach(sf => {
|
||||
if (!sf.resolvedModules) return;
|
||||
|
||||
sf.resolvedModules.forEach(({ resolvedModule }) => {
|
||||
if (resolvedModule?.packageId) map.set(resolvedModule.packageId.name, resolvedModule.extension === Extension.Dts || !!map.get(resolvedModule.packageId.name));
|
||||
});
|
||||
});
|
||||
return map;
|
||||
});
|
||||
|
||||
var deferredDiagnosticsCallbacks: (() => void)[] = [];
|
||||
|
||||
var addLazyDiagnostic = (arg: () => void) => {
|
||||
@@ -2036,7 +2018,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
getGlobalIterableType: getGlobalAsyncIterableType,
|
||||
getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType,
|
||||
getGlobalGeneratorType: getGlobalAsyncGeneratorType,
|
||||
resolveIterationType: getAwaitedType,
|
||||
resolveIterationType: (type, errorNode) => getAwaitedType(type, errorNode, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member),
|
||||
mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method,
|
||||
mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method,
|
||||
mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property,
|
||||
@@ -2392,13 +2374,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function isDeprecatedSymbol(symbol: Symbol) {
|
||||
if (length(symbol.declarations) > 1) {
|
||||
const parentSymbol = getParentOfSymbol(symbol);
|
||||
if (parentSymbol && parentSymbol.flags & SymbolFlags.Interface) {
|
||||
return some(symbol.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated));
|
||||
}
|
||||
const parentSymbol = getParentOfSymbol(symbol);
|
||||
if (parentSymbol && length(symbol.declarations) > 1) {
|
||||
return parentSymbol.flags & SymbolFlags.Interface ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration);
|
||||
}
|
||||
return !!(getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Deprecated);
|
||||
return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration)
|
||||
|| length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration);
|
||||
}
|
||||
|
||||
function isDeprecatedDeclaration(declaration: Declaration) {
|
||||
return !!(getCombinedNodeFlags(declaration) & NodeFlags.Deprecated);
|
||||
}
|
||||
|
||||
function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) {
|
||||
@@ -2811,7 +2796,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return true;
|
||||
}
|
||||
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
|
||||
if (getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
|
||||
if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 && useDefineForClassFields
|
||||
&& getContainingClass(declaration)
|
||||
&& (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent))) {
|
||||
return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true);
|
||||
@@ -3037,6 +3022,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// (it refers to the constant type of the expression instead)
|
||||
return undefined;
|
||||
}
|
||||
if (isModuleDeclaration(location) && lastLocation && location.name === lastLocation) {
|
||||
// If this is the name of a namespace, skip the parent since it will have is own locals that could
|
||||
// conflict.
|
||||
lastLocation = location;
|
||||
location = location.parent;
|
||||
}
|
||||
// Locals of a source file are not in scope (because they get merged into the global symbol table)
|
||||
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
|
||||
if (result = lookup(location.locals, name, meaning)) {
|
||||
@@ -3331,6 +3322,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
// External module export bindings shouldn't be resolved to local symbols.
|
||||
if (lastLocation &&
|
||||
lastLocation === (location as ExportSpecifier).propertyName &&
|
||||
(location as ExportSpecifier).parent.parent.moduleSpecifier) {
|
||||
location = location.parent.parent.parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isSelfReferenceLocation(location)) {
|
||||
lastSelfReferenceLocation = location;
|
||||
@@ -3390,6 +3389,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (nameNotFoundMessage) {
|
||||
addLazyDiagnostic(() => {
|
||||
if (!errorLocation ||
|
||||
errorLocation.parent.kind !== SyntaxKind.JSDocLink &&
|
||||
!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217
|
||||
!checkAndReportErrorForInvalidInitializer() &&
|
||||
!checkAndReportErrorForExtendingInterface(errorLocation) &&
|
||||
@@ -5049,31 +5049,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function errorOnImplicitAnyModule(isError: boolean, errorNode: Node, sourceFile: SourceFile, mode: ResolutionMode, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): void {
|
||||
let errorInfo;
|
||||
let errorInfo: DiagnosticMessageChain | undefined;
|
||||
if (!isExternalModuleNameRelative(moduleReference) && packageId) {
|
||||
const node10Result = sourceFile.resolvedModules?.get(moduleReference, mode)?.node10Result;
|
||||
errorInfo = node10Result
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings,
|
||||
node10Result,
|
||||
node10Result.indexOf(nodeModulesPathPart + "@types/") > -1 ? `@types/${mangleScopedPackageName(packageId.name)}` : packageId.name)
|
||||
: typesPackageExists(packageId.name)
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1,
|
||||
packageId.name, mangleScopedPackageName(packageId.name))
|
||||
: packageBundlesTypes(packageId.name)
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1,
|
||||
packageId.name,
|
||||
moduleReference)
|
||||
: chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
|
||||
moduleReference,
|
||||
mangleScopedPackageName(packageId.name));
|
||||
errorInfo = createModuleNotFoundChain(sourceFile, host, moduleReference, mode, packageId.name);
|
||||
}
|
||||
errorOrSuggestion(isError, errorNode, chainDiagnosticMessages(
|
||||
errorInfo,
|
||||
@@ -5081,12 +5059,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
moduleReference,
|
||||
resolvedFileName));
|
||||
}
|
||||
function typesPackageExists(packageName: string): boolean {
|
||||
return getPackagesMap().has(getTypesPackageName(packageName));
|
||||
}
|
||||
function packageBundlesTypes(packageName: string): boolean {
|
||||
return !!getPackagesMap().get(packageName);
|
||||
}
|
||||
|
||||
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol;
|
||||
function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined;
|
||||
@@ -8670,7 +8642,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
// Need to skip over export= symbols below - json source files get a single `Property` flagged
|
||||
// symbol of name `export=` which needs to be handled like an alias. It's not great, but it is what it is.
|
||||
if (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property)
|
||||
if (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property | SymbolFlags.Accessor)
|
||||
&& symbol.escapedName !== InternalSymbolName.ExportEquals
|
||||
&& !(symbol.flags & SymbolFlags.Prototype)
|
||||
&& !(symbol.flags & SymbolFlags.Class)
|
||||
@@ -9106,7 +9078,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
context.enclosingDeclaration = originalDecl || oldEnclosing;
|
||||
const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
|
||||
const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context));
|
||||
const classType = getDeclaredTypeOfClassOrInterface(symbol);
|
||||
const classType = getTypeWithThisArgument(getDeclaredTypeOfClassOrInterface(symbol)) as InterfaceType;
|
||||
const baseTypes = getBaseTypes(classType);
|
||||
const originalImplements = originalDecl && getEffectiveImplementsTypeNodes(originalDecl);
|
||||
const implementsExpressions = originalImplements && sanitizeJSDocImplements(originalImplements)
|
||||
@@ -10392,7 +10364,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// If the parent is a tuple type, the rest element has a tuple type of the
|
||||
// remaining tuple element types. Otherwise, the rest element has an array type with same
|
||||
// element type as the parent type.
|
||||
const baseConstraint = getBaseConstraintOrType(parentType);
|
||||
const baseConstraint = mapType(parentType, t => t.flags & TypeFlags.InstantiableNonPrimitive ? getBaseConstraintOrType(t) : t);
|
||||
type = everyType(baseConstraint, isTupleType) ?
|
||||
mapType(baseConstraint, t => sliceTupleType(t as TupleTypeReference, index)) :
|
||||
createArrayType(elementType);
|
||||
@@ -12539,6 +12511,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return needApparentType ? getApparentType(type) : type;
|
||||
}
|
||||
|
||||
function getThisArgument(type: Type) {
|
||||
return getObjectFlags(type) & ObjectFlags.Reference && length(getTypeArguments(type as TypeReference)) > getTypeReferenceArity(type as TypeReference) ? last(getTypeArguments(type as TypeReference)) : type;
|
||||
}
|
||||
|
||||
function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: readonly TypeParameter[], typeArguments: readonly Type[]) {
|
||||
let mapper: TypeMapper | undefined;
|
||||
let members: SymbolTable;
|
||||
@@ -12668,7 +12644,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return [sig.parameters];
|
||||
|
||||
function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number) {
|
||||
const elementTypes = getTypeArguments(restType);
|
||||
const elementTypes = getElementTypes(restType);
|
||||
const associatedNames = getUniqAssociatedNamesFromTupleType(restType);
|
||||
const restParams = map(elementTypes, (t, i) => {
|
||||
// Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name
|
||||
@@ -13566,7 +13542,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType) ||
|
||||
type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType)) ||
|
||||
type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType) ||
|
||||
isGenericTupleType(type) && findIndex(getTypeArguments(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t)) >= 0));
|
||||
isGenericTupleType(type) && findIndex(getElementTypes(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t)) >= 0));
|
||||
}
|
||||
|
||||
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
|
||||
@@ -13691,7 +13667,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getBaseConstraintOfType(type: Type): Type | undefined {
|
||||
if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) {
|
||||
if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || isGenericTupleType(type)) {
|
||||
const constraint = getResolvedBaseConstraint(type as InstantiableType | UnionOrIntersectionType);
|
||||
return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined;
|
||||
}
|
||||
@@ -13720,7 +13696,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return type.resolvedBaseConstraint;
|
||||
}
|
||||
const stack: object[] = [];
|
||||
return type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type);
|
||||
return type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), getThisArgument(type));
|
||||
|
||||
function getImmediateBaseConstraint(t: Type): Type {
|
||||
if (!t.immediateBaseConstraint) {
|
||||
@@ -13822,6 +13798,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (t.flags & TypeFlags.Substitution) {
|
||||
return getBaseConstraint(getSubstitutionIntersection(t as SubstitutionType));
|
||||
}
|
||||
if (isGenericTupleType(t)) {
|
||||
// We substitute constraints for variadic elements only when the constraints are array types or
|
||||
// non-variadic tuple types as we want to avoid further (possibly unbounded) recursion.
|
||||
const newElements = map(getElementTypes(t), (v, i) => {
|
||||
const constraint = t.target.elementFlags[i] & ElementFlags.Variadic && getBaseConstraint(v) || v;
|
||||
return constraint && everyType(constraint, c => isArrayOrTupleType(c) && !isGenericTupleType(c)) ? constraint : v;
|
||||
});
|
||||
return createTupleType(newElements, t.target.elementFlags, t.target.readonly, t.target.labeledElementDeclarations);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -14543,8 +14528,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
const classType = declaration.kind === SyntaxKind.Constructor ?
|
||||
getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent as ClassDeclaration).symbol))
|
||||
const hostDeclaration = isJSDocSignature(declaration) ? getEffectiveJSDocHost(declaration) : declaration;
|
||||
const classType = hostDeclaration && isConstructorDeclaration(hostDeclaration) ?
|
||||
getDeclaredTypeOfClassOrInterface(getMergedSymbol((hostDeclaration.parent as ClassDeclaration).symbol))
|
||||
: undefined;
|
||||
const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
|
||||
if (hasRestParameter(declaration) || isInJSFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters)) {
|
||||
@@ -14803,16 +14789,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (declaration.kind === SyntaxKind.Constructor) {
|
||||
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent as ClassDeclaration).symbol));
|
||||
}
|
||||
const typeNode = getEffectiveReturnTypeNode(declaration);
|
||||
if (isJSDocSignature(declaration)) {
|
||||
const root = getJSDocRoot(declaration);
|
||||
if (root && isConstructorDeclaration(root.parent)) {
|
||||
if (root && isConstructorDeclaration(root.parent) && !typeNode) {
|
||||
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((root.parent.parent as ClassDeclaration).symbol));
|
||||
}
|
||||
}
|
||||
if (isJSDocConstructSignature(declaration)) {
|
||||
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
|
||||
}
|
||||
const typeNode = getEffectiveReturnTypeNode(declaration);
|
||||
if (typeNode) {
|
||||
return getTypeFromTypeNode(typeNode);
|
||||
}
|
||||
@@ -16129,7 +16115,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
addElement(type, ElementFlags.Variadic, target.labeledElementDeclarations?.[i]);
|
||||
}
|
||||
else if (isTupleType(type)) {
|
||||
const elements = getTypeArguments(type);
|
||||
const elements = getElementTypes(type);
|
||||
if (elements.length + expandedTypes.length >= 10_000) {
|
||||
error(currentNode, isPartOfTypeNode(currentNode!)
|
||||
? Diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent
|
||||
@@ -16211,6 +16197,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return type.elementFlags.length - findLastIndex(type.elementFlags, f => !(f & flags)) - 1;
|
||||
}
|
||||
|
||||
function getElementTypes(type: TupleTypeReference): readonly Type[] {
|
||||
const typeArguments = getTypeArguments(type);
|
||||
const arity = getTypeReferenceArity(type);
|
||||
return typeArguments.length === arity ? typeArguments : typeArguments.slice(0, arity);
|
||||
}
|
||||
|
||||
function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type {
|
||||
return addOptionality(getTypeFromTypeNode(node.type), /*isProperty*/ true);
|
||||
}
|
||||
@@ -16988,19 +16980,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
function getLiteralTypeFromPropertyName(name: PropertyName) {
|
||||
function getLiteralTypeFromPropertyName(name: PropertyName | JsxAttributeName) {
|
||||
if (isPrivateIdentifier(name)) {
|
||||
return neverType;
|
||||
}
|
||||
return isIdentifier(name) ? getStringLiteralType(unescapeLeadingUnderscores(name.escapedText)) :
|
||||
getRegularTypeOfLiteralType(isComputedPropertyName(name) ? checkComputedPropertyName(name) : checkExpression(name));
|
||||
if (isNumericLiteral(name)) {
|
||||
return getRegularTypeOfLiteralType(checkExpression(name));
|
||||
}
|
||||
if (isComputedPropertyName(name)) {
|
||||
return getRegularTypeOfLiteralType(checkComputedPropertyName(name));
|
||||
}
|
||||
const propertyName = getPropertyNameForPropertyNameNode(name);
|
||||
if (propertyName !== undefined) {
|
||||
return getStringLiteralType(unescapeLeadingUnderscores(propertyName));
|
||||
}
|
||||
if (isExpression(name)) {
|
||||
return getRegularTypeOfLiteralType(checkExpression(name));
|
||||
}
|
||||
return neverType;
|
||||
}
|
||||
|
||||
function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags, includeNonPublic?: boolean) {
|
||||
if (includeNonPublic || !(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) {
|
||||
let type = getSymbolLinks(getLateBoundSymbol(prop)).nameType;
|
||||
if (!type) {
|
||||
const name = getNameOfDeclaration(prop.valueDeclaration) as PropertyName;
|
||||
const name = getNameOfDeclaration(prop.valueDeclaration) as PropertyName | JsxAttributeName;
|
||||
type = prop.escapedName === InternalSymbolName.Default ? getStringLiteralType("default") :
|
||||
name && getLiteralTypeFromPropertyName(name) || (!isKnownSymbol(prop) ? getStringLiteralType(symbolName(prop)) : undefined);
|
||||
}
|
||||
@@ -17761,7 +17765,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function isDeferredType(type: Type, checkTuples: boolean) {
|
||||
return isGenericType(type) || checkTuples && isTupleType(type) && some(getTypeArguments(type), isGenericType);
|
||||
return isGenericType(type) || checkTuples && isTupleType(type) && some(getElementTypes(type), isGenericType);
|
||||
}
|
||||
|
||||
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
|
||||
@@ -18902,7 +18906,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// M<[A, B?, ...T, ...C[]] into [...M<[A]>, ...M<[B?]>, ...M<T>, ...M<C[]>] and then rely on tuple type
|
||||
// normalization to resolve the non-generic parts of the resulting tuple.
|
||||
const elementFlags = tupleType.target.elementFlags;
|
||||
const elementTypes = map(getTypeArguments(tupleType), (t, i) => {
|
||||
const elementTypes = map(getElementTypes(tupleType), (t, i) => {
|
||||
const singleton = elementFlags[i] & ElementFlags.Variadic ? t :
|
||||
elementFlags[i] & ElementFlags.Rest ? createArrayType(t) :
|
||||
createTupleType([t], [elementFlags[i]]);
|
||||
@@ -18921,7 +18925,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
|
||||
const elementFlags = tupleType.target.elementFlags;
|
||||
const elementTypes = map(getTypeArguments(tupleType), (_, i) =>
|
||||
const elementTypes = map(getElementTypes(tupleType), (_, i) =>
|
||||
instantiateMappedTypeTemplate(mappedType, getStringLiteralType("" + i), !!(elementFlags[i] & ElementFlags.Optional), mapper));
|
||||
const modifiers = getMappedTypeModifiers(mappedType);
|
||||
const newTupleModifiers = modifiers & MappedTypeModifiers.IncludeOptional ? map(elementFlags, f => f & ElementFlags.Required ? ElementFlags.Optional : f) :
|
||||
@@ -20226,7 +20230,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getNormalizedTupleType(type: TupleTypeReference, writing: boolean): Type {
|
||||
const elements = getTypeArguments(type);
|
||||
const elements = getElementTypes(type);
|
||||
const normalizedElements = sameMap(elements, t => t.flags & TypeFlags.Simplifiable ? getSimplifiedType(t, writing) : t);
|
||||
return elements !== normalizedElements ? createNormalizedTupleType(type.target, normalizedElements) : type;
|
||||
}
|
||||
@@ -20594,6 +20598,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
* * Ternary.False if they are not related.
|
||||
*/
|
||||
function isRelatedTo(originalSource: Type, originalTarget: Type, recursionFlags: RecursionFlags = RecursionFlags.Both, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
|
||||
if (originalSource === originalTarget) return Ternary.True;
|
||||
|
||||
// Before normalization: if `source` is type an object type, and `target` is primitive,
|
||||
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
|
||||
if (originalSource.flags & TypeFlags.Object && originalTarget.flags & TypeFlags.Primitive) {
|
||||
@@ -21782,6 +21788,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
else if (isGenericTupleType(source) && isTupleType(target) && !isGenericTupleType(target)) {
|
||||
const constraint = getBaseConstraintOrType(source);
|
||||
if (constraint !== source) {
|
||||
return isRelatedTo(constraint, target, RecursionFlags.Source, reportErrors);
|
||||
}
|
||||
}
|
||||
// A fresh empty object type is never a subtype of a non-empty object type. This ensures fresh({}) <: { [x: string]: xxx }
|
||||
// but not vice-versa. Without this rule, those types would be mutual subtypes.
|
||||
else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
|
||||
@@ -24063,7 +24075,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function isPartiallyInferableType(type: Type): boolean {
|
||||
return !(getObjectFlags(type) & ObjectFlags.NonInferrableType) ||
|
||||
isObjectLiteralType(type) && some(getPropertiesOfType(type), prop => isPartiallyInferableType(getTypeOfSymbol(prop))) ||
|
||||
isTupleType(type) && some(getTypeArguments(type), isPartiallyInferableType);
|
||||
isTupleType(type) && some(getElementTypes(type), isPartiallyInferableType);
|
||||
}
|
||||
|
||||
function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) {
|
||||
@@ -24078,7 +24090,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return createArrayType(inferReverseMappedType(getTypeArguments(source)[0], target, constraint), isReadonlyArrayType(source));
|
||||
}
|
||||
if (isTupleType(source)) {
|
||||
const elementTypes = map(getTypeArguments(source), t => inferReverseMappedType(t, target, constraint));
|
||||
const elementTypes = map(getElementTypes(source), t => inferReverseMappedType(t, target, constraint));
|
||||
const elementFlags = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
|
||||
sameMap(source.target.elementFlags, f => f & ElementFlags.Optional ? ElementFlags.Required : f) :
|
||||
source.target.elementFlags;
|
||||
@@ -25139,22 +25151,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const inference = context.inferences[index];
|
||||
if (!inference.inferredType) {
|
||||
let inferredType: Type | undefined;
|
||||
const signature = context.signature;
|
||||
if (signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
|
||||
if (inference.contraCandidates) {
|
||||
// If we have both co- and contra-variant inferences, we use the co-variant inference if it is not 'never',
|
||||
// it is a subtype of some contra-variant inference, and no other type parameter is constrained to this type
|
||||
// parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference.
|
||||
const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
let fallbackType: Type | undefined;
|
||||
if (context.signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, context.signature) : undefined;
|
||||
const inferredContravariantType = inference.contraCandidates ? getContravariantInference(inference) : undefined;
|
||||
if (inferredCovariantType || inferredContravariantType) {
|
||||
// If we have both co- and contra-variant inferences, we prefer the co-variant inference if it is not 'never',
|
||||
// all co-variant inferences are subtypes of it (i.e. it isn't one of a conflicting set of candidates), it is
|
||||
// a subtype of some contra-variant inference, and no other type parameter is constrained to this type parameter
|
||||
// and has inferences that would conflict. Otherwise, we prefer the contra-variant inference.
|
||||
const preferCovariantType = inferredCovariantType && (!inferredContravariantType ||
|
||||
!(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) &&
|
||||
every(context.inferences, other =>
|
||||
other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter ||
|
||||
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType)));
|
||||
inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference);
|
||||
}
|
||||
else if (inferredCovariantType) {
|
||||
inferredType = inferredCovariantType;
|
||||
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType))));
|
||||
inferredType = preferCovariantType ? inferredCovariantType : inferredContravariantType;
|
||||
fallbackType = preferCovariantType ? inferredContravariantType : inferredCovariantType;
|
||||
}
|
||||
else if (context.flags & InferenceFlags.NoDefault) {
|
||||
// We use silentNeverType as the wildcard that signals no inferences.
|
||||
@@ -25184,7 +25197,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (constraint) {
|
||||
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
|
||||
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
||||
inference.inferredType = inferredType = instantiatedConstraint;
|
||||
// If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
|
||||
inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27871,7 +27885,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
|
||||
const targetSymbol = checkDeprecatedAliasedSymbol(localOrExportSymbol, node);
|
||||
const targetSymbol = resolveAliasWithDeprecationCheck(localOrExportSymbol, node);
|
||||
if (isDeprecatedSymbol(targetSymbol) && isUncalledFunctionReference(node, targetSymbol) && targetSymbol.declarations) {
|
||||
addDeprecatedSuggestion(node, targetSymbol.declarations, node.escapedText as string);
|
||||
}
|
||||
@@ -29236,7 +29250,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild, contextFlags: ContextFlags | undefined) {
|
||||
const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName, contextFlags);
|
||||
const attributesType = getApparentTypeOfContextualType(node.openingElement.attributes, contextFlags);
|
||||
// JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty)
|
||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
|
||||
if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) {
|
||||
@@ -29321,6 +29335,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) {
|
||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
|
||||
return discriminateTypeByDiscriminableItems(contextualType,
|
||||
concatenate(
|
||||
map(
|
||||
@@ -29328,7 +29343,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
|
||||
),
|
||||
map(
|
||||
filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)),
|
||||
filter(getPropertiesOfType(contextualType), s => {
|
||||
if (!(s.flags & SymbolFlags.Optional) || !node?.symbol?.members) {
|
||||
return false;
|
||||
}
|
||||
const element = node.parent.parent;
|
||||
if (s.escapedName === jsxChildrenPropertyName && isJsxElement(element) && getSemanticJsxChildren(element.children).length) {
|
||||
return false;
|
||||
}
|
||||
return !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName);
|
||||
}),
|
||||
s => [() => undefinedType, s.escapedName] as [() => Type, __String]
|
||||
)
|
||||
),
|
||||
@@ -29620,18 +29644,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) {
|
||||
const managedSym = getJsxLibraryManagedAttributes(ns);
|
||||
if (managedSym) {
|
||||
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
|
||||
const ctorType = getStaticTypeOfReferencedJsxConstructor(context);
|
||||
if (managedSym.flags & SymbolFlags.TypeAlias) {
|
||||
const params = getSymbolLinks(managedSym).typeParameters;
|
||||
if (length(params) >= 2) {
|
||||
const args = fillMissingTypeArguments([ctorType, attributesType], params, 2, isInJSFile(context));
|
||||
return getTypeAliasInstantiation(managedSym, args);
|
||||
}
|
||||
}
|
||||
if (length((declaredManagedType as GenericType).typeParameters) >= 2) {
|
||||
const args = fillMissingTypeArguments([ctorType, attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
|
||||
return createTypeReference((declaredManagedType as GenericType), args);
|
||||
const result = instantiateAliasOrInterfaceWithDefaults(managedSym, isInJSFile(context), ctorType, attributesType);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return attributesType;
|
||||
@@ -30057,7 +30073,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return links.immediateTarget;
|
||||
}
|
||||
|
||||
function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type {
|
||||
function checkObjectLiteral(node: ObjectLiteralExpression, checkMode: CheckMode = CheckMode.Normal): Type {
|
||||
const inDestructuringPattern = isAssignmentTarget(node);
|
||||
// Grammar checking
|
||||
checkGrammarObjectLiteralExpression(node, inDestructuringPattern);
|
||||
@@ -30159,7 +30175,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
member = prop;
|
||||
allPropertiesTable?.set(prop.escapedName, prop);
|
||||
|
||||
if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
|
||||
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
|
||||
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
|
||||
const inferenceContext = getInferenceContext(node);
|
||||
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
|
||||
@@ -30179,7 +30195,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
hasComputedNumberProperty = false;
|
||||
hasComputedSymbolProperty = false;
|
||||
}
|
||||
const type = getReducedType(checkExpression(memberDecl.expression));
|
||||
const type = getReducedType(checkExpression(memberDecl.expression, checkMode & CheckMode.Inferential));
|
||||
if (isValidSpreadType(type)) {
|
||||
const mergedType = tryMergeUnionOfObjectTypeAndEmptyObject(type, inConstContext);
|
||||
if (allPropertiesTable) {
|
||||
@@ -30379,7 +30395,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
* @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral,
|
||||
* which also calls getSpreadType.
|
||||
*/
|
||||
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode | undefined) {
|
||||
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode = CheckMode.Normal) {
|
||||
const attributes = openingLikeElement.attributes;
|
||||
const contextualType = getContextualType(attributes, ContextFlags.None);
|
||||
const allAttributesTable = strictNullChecks ? createSymbolTable() : undefined;
|
||||
@@ -30416,7 +30432,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
|
||||
}
|
||||
}
|
||||
if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
|
||||
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
|
||||
const inferenceContext = getInferenceContext(attributes);
|
||||
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
|
||||
const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;
|
||||
@@ -30429,7 +30445,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, objectFlags, /*readonly*/ false);
|
||||
attributesTable = createSymbolTable();
|
||||
}
|
||||
const exprType = getReducedType(checkExpressionCached(attributeDecl.expression, checkMode));
|
||||
const exprType = getReducedType(checkExpression(attributeDecl.expression, checkMode & CheckMode.Inferential));
|
||||
if (isTypeAny(exprType)) {
|
||||
hasSpreadAnyType = true;
|
||||
}
|
||||
@@ -30690,6 +30706,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.LibraryManagedAttributes, SymbolFlags.Type);
|
||||
}
|
||||
|
||||
function getJsxElementTypeSymbol(jsxNamespace: Symbol) {
|
||||
// JSX.ElementType [symbol]
|
||||
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.ElementType, SymbolFlags.Type);
|
||||
}
|
||||
|
||||
/// e.g. "props" for React.d.ts,
|
||||
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
|
||||
/// non-intrinsic elements' attributes type is 'any'),
|
||||
@@ -30826,11 +30847,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getJsxElementTypeTypeAt(location: Node): Type | undefined {
|
||||
const type = getJsxType(JsxNames.ElementType, location);
|
||||
if (isErrorType(type)) return undefined;
|
||||
const ns = getJsxNamespaceAt(location);
|
||||
if (!ns) return undefined;
|
||||
const sym = getJsxElementTypeSymbol(ns);
|
||||
if (!sym) return undefined;
|
||||
const type = instantiateAliasOrInterfaceWithDefaults(sym, isInJSFile(location));
|
||||
if (!type || isErrorType(type)) return undefined;
|
||||
return type;
|
||||
}
|
||||
|
||||
function instantiateAliasOrInterfaceWithDefaults(managedSym: Symbol, inJs: boolean, ...typeArguments: Type[]) {
|
||||
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
|
||||
if (managedSym.flags & SymbolFlags.TypeAlias) {
|
||||
const params = getSymbolLinks(managedSym).typeParameters;
|
||||
if (length(params) >= typeArguments.length) {
|
||||
const args = fillMissingTypeArguments(typeArguments, params, typeArguments.length, inJs);
|
||||
return length(args) === 0 ? declaredManagedType : getTypeAliasInstantiation(managedSym, args);
|
||||
}
|
||||
}
|
||||
if (length((declaredManagedType as GenericType).typeParameters) >= typeArguments.length) {
|
||||
const args = fillMissingTypeArguments(typeArguments, (declaredManagedType as GenericType).typeParameters, typeArguments.length, inJs);
|
||||
return createTypeReference((declaredManagedType as GenericType), args);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the properties of the Jsx.IntrinsicElements interface
|
||||
*/
|
||||
@@ -31508,8 +31549,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isDeprecatedSymbol(prop) && isUncalledFunctionReference(node, prop) && prop.declarations) {
|
||||
addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string);
|
||||
const targetPropSymbol = resolveAliasWithDeprecationCheck(prop, right);
|
||||
if (isDeprecatedSymbol(targetPropSymbol) && isUncalledFunctionReference(node, targetPropSymbol) && targetPropSymbol.declarations) {
|
||||
addDeprecatedSuggestion(right, targetPropSymbol.declarations, right.escapedText as string);
|
||||
}
|
||||
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
|
||||
markPropertyAsReferenced(prop, node, isSelfTypeAccess(left, parentSymbol));
|
||||
@@ -32441,7 +32483,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function getMutableArrayOrTupleType(type: Type) {
|
||||
return type.flags & TypeFlags.Union ? mapType(type, getMutableArrayOrTupleType) :
|
||||
type.flags & TypeFlags.Any || isMutableArrayOrTuple(getBaseConstraintOfType(type) || type) ? type :
|
||||
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.elementFlags, /*readonly*/ false, type.target.labeledElementDeclarations) :
|
||||
isTupleType(type) ? createTupleType(getElementTypes(type), type.target.elementFlags, /*readonly*/ false, type.target.labeledElementDeclarations) :
|
||||
createTupleType([type], [ElementFlags.Variadic]);
|
||||
}
|
||||
|
||||
@@ -32571,7 +32613,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (getJsxNamespaceContainerForImplicitImport(node)) {
|
||||
return true; // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet)
|
||||
}
|
||||
const tagType = isJsxOpeningElement(node) || isJsxSelfClosingElement(node) && !isJsxIntrinsicTagName(node.tagName) ? checkExpression(node.tagName) : undefined;
|
||||
const tagType = (isJsxOpeningElement(node) || isJsxSelfClosingElement(node)) && !(isJsxIntrinsicTagName(node.tagName) || isJsxNamespacedName(node.tagName)) ? checkExpression(node.tagName) : undefined;
|
||||
if (!tagType) {
|
||||
return true;
|
||||
}
|
||||
@@ -32775,7 +32817,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// We can call checkExpressionCached because spread expressions never have a contextual type.
|
||||
const spreadType = arg.kind === SyntaxKind.SpreadElement && (flowLoopCount ? checkExpression((arg as SpreadElement).expression) : checkExpressionCached((arg as SpreadElement).expression));
|
||||
if (spreadType && isTupleType(spreadType)) {
|
||||
forEach(getTypeArguments(spreadType), (t, i) => {
|
||||
forEach(getElementTypes(spreadType), (t, i) => {
|
||||
const flags = spreadType.target.elementFlags[i];
|
||||
const syntheticArg = createSyntheticExpression(arg, flags & ElementFlags.Rest ? createArrayType(t) : t,
|
||||
!!(flags & ElementFlags.Variable), spreadType.target.labeledElementDeclarations?.[i]);
|
||||
@@ -34481,6 +34523,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
return getRegularTypeOfLiteralType(exprType);
|
||||
}
|
||||
const links = getNodeLinks(node);
|
||||
links.assertionExpressionType = exprType;
|
||||
checkSourceElement(type);
|
||||
checkNodeDeferred(node);
|
||||
return getTypeFromTypeNode(type);
|
||||
@@ -34505,9 +34549,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function checkAssertionDeferred(node: JSDocTypeAssertion | AssertionExpression) {
|
||||
const { type, expression } = getAssertionTypeAndExpression(node);
|
||||
const { type } = getAssertionTypeAndExpression(node);
|
||||
const errNode = isParenthesizedExpression(node) ? type : node;
|
||||
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression)));
|
||||
const links = getNodeLinks(node);
|
||||
Debug.assertIsDefined(links.assertionExpressionType);
|
||||
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(links.assertionExpressionType));
|
||||
const targetType = getTypeFromTypeNode(type);
|
||||
if (!isErrorType(targetType)) {
|
||||
addLazyDiagnostic(() => {
|
||||
@@ -35673,6 +35719,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
forEachReturnStatement(func.body as Block, returnStatement => {
|
||||
const expr = returnStatement.expression;
|
||||
if (expr) {
|
||||
// Bare calls to this same function don't contribute to inference
|
||||
if (expr.kind === SyntaxKind.CallExpression &&
|
||||
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
|
||||
checkExpressionCached((expr as CallExpression).expression).symbol === func.symbol) {
|
||||
hasReturnOfTypeNever = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions);
|
||||
if (functionFlags & FunctionFlags.Async) {
|
||||
// From within an async function you can return either a non-promise value or a promise. Any
|
||||
@@ -37386,7 +37440,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function padTupleType(type: TupleTypeReference, pattern: ArrayBindingPattern) {
|
||||
const patternElements = pattern.elements;
|
||||
const elementTypes = getTypeArguments(type).slice();
|
||||
const elementTypes = getElementTypes(type).slice();
|
||||
const elementFlags = type.target.elementFlags.slice();
|
||||
for (let i = getTypeReferenceArity(type); i < patternElements.length; i++) {
|
||||
const e = patternElements[i];
|
||||
@@ -44122,20 +44176,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
if (isImportSpecifier(node)) {
|
||||
const targetSymbol = checkDeprecatedAliasedSymbol(symbol, node);
|
||||
if (isDeprecatedAliasedSymbol(targetSymbol) && targetSymbol.declarations) {
|
||||
const targetSymbol = resolveAliasWithDeprecationCheck(symbol, node);
|
||||
if (isDeprecatedSymbol(targetSymbol) && targetSymbol.declarations) {
|
||||
addDeprecatedSuggestion(node, targetSymbol.declarations, targetSymbol.escapedName as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isDeprecatedAliasedSymbol(symbol: Symbol) {
|
||||
return !!symbol.declarations && every(symbol.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated));
|
||||
}
|
||||
|
||||
function checkDeprecatedAliasedSymbol(symbol: Symbol, location: Node) {
|
||||
if (!(symbol.flags & SymbolFlags.Alias)) return symbol;
|
||||
function resolveAliasWithDeprecationCheck(symbol: Symbol, location: Node) {
|
||||
if (!(symbol.flags & SymbolFlags.Alias) || isDeprecatedSymbol(symbol) || !getDeclarationOfAliasSymbol(symbol)) {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
const targetSymbol = resolveAlias(symbol);
|
||||
if (targetSymbol === unknownSymbol) return targetSymbol;
|
||||
@@ -44145,7 +44197,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (target) {
|
||||
if (target === targetSymbol) break;
|
||||
if (target.declarations && length(target.declarations)) {
|
||||
if (isDeprecatedAliasedSymbol(target)) {
|
||||
if (isDeprecatedSymbol(target)) {
|
||||
addDeprecatedSuggestion(location, target.declarations, target.escapedName as string);
|
||||
break;
|
||||
}
|
||||
@@ -45445,11 +45497,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement);
|
||||
return symbol === unknownSymbol ? undefined : symbol;
|
||||
}
|
||||
const result = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
|
||||
const result = resolveEntityName(name, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
|
||||
if (!result && isJSDoc) {
|
||||
const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration));
|
||||
if (container) {
|
||||
return resolveJSDocMemberName(name, /*ignoreErrors*/ false, getSymbolOfDeclaration(container));
|
||||
return resolveJSDocMemberName(name, /*ignoreErrors*/ true, getSymbolOfDeclaration(container));
|
||||
}
|
||||
}
|
||||
if (result && isJSDoc) {
|
||||
@@ -46473,6 +46525,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getReferencedValueDeclarations(referenceIn: Identifier): Declaration[] | undefined {
|
||||
if (!isGeneratedIdentifier(referenceIn)) {
|
||||
const reference = getParseTreeNode(referenceIn, isIdentifier);
|
||||
if (reference) {
|
||||
const symbol = getReferencedValueSymbol(reference);
|
||||
if (symbol) {
|
||||
return filter(getExportSymbolOfValueSymbolIfExported(symbol).declarations, declaration => {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.BindingElement:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean {
|
||||
if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node)) {
|
||||
return isFreshLiteralType(getTypeOfSymbol(getSymbolOfDeclaration(node)));
|
||||
@@ -46580,6 +46669,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
},
|
||||
collectLinkedAliases,
|
||||
getReferencedValueDeclaration,
|
||||
getReferencedValueDeclarations,
|
||||
getTypeReferenceSerializationKind,
|
||||
isOptionalParameter,
|
||||
moduleExportsSomeValue,
|
||||
|
||||
@@ -364,21 +364,21 @@ export function *mapIterator<T, U>(iter: Iterable<T>, mapFn: (x: T) => U) {
|
||||
* Maps from T to T and avoids allocation if all elements map to themselves
|
||||
*
|
||||
* @internal */
|
||||
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[];
|
||||
export function sameMap<T, U = T>(array: T[], f: (x: T, i: number) => U): U[];
|
||||
/** @internal */
|
||||
export function sameMap<T>(array: readonly T[], f: (x: T, i: number) => T): readonly T[];
|
||||
export function sameMap<T, U = T>(array: readonly T[], f: (x: T, i: number) => U): readonly U[];
|
||||
/** @internal */
|
||||
export function sameMap<T>(array: T[] | undefined, f: (x: T, i: number) => T): T[] | undefined;
|
||||
export function sameMap<T, U = T>(array: T[] | undefined, f: (x: T, i: number) => U): U[] | undefined;
|
||||
/** @internal */
|
||||
export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined;
|
||||
export function sameMap<T, U = T>(array: readonly T[] | undefined, f: (x: T, i: number) => U): readonly U[] | undefined;
|
||||
/** @internal */
|
||||
export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined {
|
||||
export function sameMap<T, U = T>(array: readonly T[] | undefined, f: (x: T, i: number) => U): readonly U[] | undefined {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
const mapped = f(item, i);
|
||||
if (item !== mapped) {
|
||||
const result = array.slice(0, i);
|
||||
if (item as unknown !== mapped) {
|
||||
const result: U[] = array.slice(0, i) as unknown[] as U[];
|
||||
result.push(mapped);
|
||||
for (i++; i < array.length; i++) {
|
||||
result.push(f(array[i], i));
|
||||
@@ -387,7 +387,7 @@ export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number)
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
return array as unknown[] as U[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4917,6 +4917,10 @@
|
||||
"category": "Message",
|
||||
"code": 6163
|
||||
},
|
||||
"Skipping module '{0}' that looks like an absolute URI, target file types: {1}.": {
|
||||
"category": "Message",
|
||||
"code": 6164
|
||||
},
|
||||
"Do not truncate error messages.": {
|
||||
"category": "Message",
|
||||
"code": 6165
|
||||
@@ -6592,6 +6596,10 @@
|
||||
"category": "Error",
|
||||
"code": 8038
|
||||
},
|
||||
"A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag": {
|
||||
"category": "Error",
|
||||
"code": 8039
|
||||
},
|
||||
|
||||
"Declaration emit for this file requires using private name '{0}'. An explicit type annotation may unblock declaration emit.": {
|
||||
"category": "Error",
|
||||
@@ -7600,6 +7608,10 @@
|
||||
"category": "Message",
|
||||
"code": 95177
|
||||
},
|
||||
"Move to file": {
|
||||
"category": "Message",
|
||||
"code": 95178
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
+16
-10
@@ -179,6 +179,7 @@ import {
|
||||
getSyntheticLeadingComments,
|
||||
getSyntheticTrailingComments,
|
||||
getTextOfJSDocComment,
|
||||
getTextOfJsxNamespacedName,
|
||||
getTrailingCommentRanges,
|
||||
getTrailingSemicolonDeferringWriter,
|
||||
getTransformers,
|
||||
@@ -228,6 +229,7 @@ import {
|
||||
isJSDocLikeText,
|
||||
isJsonSourceFile,
|
||||
isJsxClosingElement,
|
||||
isJsxNamespacedName,
|
||||
isJsxOpeningElement,
|
||||
isKeyword,
|
||||
isLet,
|
||||
@@ -1155,6 +1157,7 @@ export const notImplementedResolver: EmitResolver = {
|
||||
// Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
|
||||
getConstantValue: notImplemented,
|
||||
getReferencedValueDeclaration: notImplemented,
|
||||
getReferencedValueDeclarations: notImplemented,
|
||||
getTypeReferenceSerializationKind: notImplemented,
|
||||
isOptionalParameter: notImplemented,
|
||||
moduleExportsSomeValue: notImplemented,
|
||||
@@ -2065,6 +2068,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return emitJsxSpreadAttribute(node as JsxSpreadAttribute);
|
||||
case SyntaxKind.JsxExpression:
|
||||
return emitJsxExpression(node as JsxExpression);
|
||||
case SyntaxKind.JsxNamespacedName:
|
||||
return emitJsxNamespacedName(node as JsxNamespacedName);
|
||||
|
||||
// Clauses
|
||||
case SyntaxKind.CaseClause:
|
||||
@@ -2178,8 +2183,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
|
||||
// Transformation nodes
|
||||
case SyntaxKind.NotEmittedStatement:
|
||||
case SyntaxKind.EndOfDeclarationMarker:
|
||||
case SyntaxKind.MergeDeclarationMarker:
|
||||
return;
|
||||
}
|
||||
if (isExpression(node)) {
|
||||
@@ -2284,8 +2287,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return emitJsxSelfClosingElement(node as JsxSelfClosingElement);
|
||||
case SyntaxKind.JsxFragment:
|
||||
return emitJsxFragment(node as JsxFragment);
|
||||
case SyntaxKind.JsxNamespacedName:
|
||||
return emitJsxNamespacedName(node as JsxNamespacedName);
|
||||
|
||||
// Synthesized list
|
||||
case SyntaxKind.SyntaxList:
|
||||
@@ -2298,9 +2299,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return emitPartiallyEmittedExpression(node as PartiallyEmittedExpression);
|
||||
case SyntaxKind.CommaListExpression:
|
||||
return emitCommaList(node as CommaListExpression);
|
||||
case SyntaxKind.MergeDeclarationMarker:
|
||||
case SyntaxKind.EndOfDeclarationMarker:
|
||||
return;
|
||||
case SyntaxKind.SyntheticReferenceExpression:
|
||||
return Debug.fail("SyntheticReferenceExpression should not be printed");
|
||||
}
|
||||
@@ -4875,7 +4873,10 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
}
|
||||
|
||||
function emitEmbeddedStatement(parent: Node, node: Statement) {
|
||||
if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) {
|
||||
if (isBlock(node) ||
|
||||
getEmitFlags(parent) & EmitFlags.SingleLine ||
|
||||
preserveSourceNewlines && !getLeadingLineTerminatorCount(parent, node, ListFormat.None)
|
||||
) {
|
||||
writeSpace();
|
||||
emit(node);
|
||||
}
|
||||
@@ -5529,7 +5530,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return node;
|
||||
}
|
||||
|
||||
function getTextOfNode(node: Identifier | PrivateIdentifier | LiteralExpression, includeTrivia?: boolean): string {
|
||||
function getTextOfNode(node: Identifier | PrivateIdentifier | LiteralExpression | JsxNamespacedName, includeTrivia?: boolean): string {
|
||||
if (isGeneratedIdentifier(node) || isGeneratedPrivateIdentifier(node)) {
|
||||
return generateName(node);
|
||||
}
|
||||
@@ -5543,6 +5544,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return idText(node);
|
||||
}
|
||||
}
|
||||
else if (isJsxNamespacedName(node)) {
|
||||
if (!canUseSourceFile || getSourceFileOfNode(node) !== getOriginalNode(sourceFile)) {
|
||||
return getTextOfJsxNamespacedName(node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assertNode(node, isLiteralExpression); // not strictly necessary
|
||||
if (!canUseSourceFile) {
|
||||
@@ -5555,7 +5561,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined, jsxAttributeEscape: boolean): string {
|
||||
if (node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).textSourceNode) {
|
||||
const textSourceNode = (node as StringLiteral).textSourceNode!;
|
||||
if (isIdentifier(textSourceNode) || isPrivateIdentifier(textSourceNode) || isNumericLiteral(textSourceNode)) {
|
||||
if (isIdentifier(textSourceNode) || isPrivateIdentifier(textSourceNode) || isNumericLiteral(textSourceNode) || isJsxNamespacedName(textSourceNode)) {
|
||||
const text = isNumericLiteral(textSourceNode) ? textSourceNode.text : getTextOfNode(textSourceNode);
|
||||
return jsxAttributeEscape ? `"${escapeJsxAttributeString(text)}"` :
|
||||
neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? `"${escapeString(text)}"` :
|
||||
|
||||
@@ -79,7 +79,6 @@ import {
|
||||
EmitNode,
|
||||
emptyArray,
|
||||
EmptyStatement,
|
||||
EndOfDeclarationMarker,
|
||||
EndOfFileToken,
|
||||
EntityName,
|
||||
EnumDeclaration,
|
||||
@@ -121,6 +120,7 @@ import {
|
||||
getLineAndCharacterOfPosition,
|
||||
getNameOfDeclaration,
|
||||
getNodeId,
|
||||
getNonAssignedNameOfDeclaration,
|
||||
getSourceMapRange,
|
||||
getSyntheticLeadingComments,
|
||||
getSyntheticTrailingComments,
|
||||
@@ -298,7 +298,6 @@ import {
|
||||
MemberName,
|
||||
memoize,
|
||||
memoizeOne,
|
||||
MergeDeclarationMarker,
|
||||
MetaProperty,
|
||||
MethodDeclaration,
|
||||
MethodSignature,
|
||||
@@ -946,8 +945,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
updatePartiallyEmittedExpression,
|
||||
createCommaListExpression,
|
||||
updateCommaListExpression,
|
||||
createEndOfDeclarationMarker,
|
||||
createMergeDeclarationMarker,
|
||||
createSyntheticReferenceExpression,
|
||||
updateSyntheticReferenceExpression,
|
||||
cloneNode,
|
||||
@@ -6210,30 +6207,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
: node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a synthetic element to act as a placeholder for the end of an emitted declaration in
|
||||
* order to properly emit exports.
|
||||
*/
|
||||
// @api
|
||||
function createEndOfDeclarationMarker(original: Node) {
|
||||
const node = createBaseNode<EndOfDeclarationMarker>(SyntaxKind.EndOfDeclarationMarker);
|
||||
node.emitNode = {} as EmitNode;
|
||||
node.original = original;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a synthetic element to act as a placeholder for the beginning of a merged declaration in
|
||||
* order to properly emit exports.
|
||||
*/
|
||||
// @api
|
||||
function createMergeDeclarationMarker(original: Node) {
|
||||
const node = createBaseNode<MergeDeclarationMarker>(SyntaxKind.MergeDeclarationMarker);
|
||||
node.emitNode = {} as EmitNode;
|
||||
node.original = original;
|
||||
return node;
|
||||
}
|
||||
|
||||
// @api
|
||||
function createSyntheticReferenceExpression(expression: Expression, thisArg: Expression) {
|
||||
const node = createBaseNode<SyntheticReferenceExpression>(SyntaxKind.SyntheticReferenceExpression);
|
||||
@@ -6672,8 +6645,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
: reduceLeft(expressions, factory.createComma)!;
|
||||
}
|
||||
|
||||
function getName(node: Declaration | undefined, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags: EmitFlags = 0) {
|
||||
const nodeName = getNameOfDeclaration(node);
|
||||
function getName(node: Declaration | undefined, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags: EmitFlags = 0, ignoreAssignedName?: boolean) {
|
||||
const nodeName = ignoreAssignedName ? node && getNonAssignedNameOfDeclaration(node) : getNameOfDeclaration(node);
|
||||
if (nodeName && isIdentifier(nodeName) && !isGeneratedIdentifier(nodeName)) {
|
||||
// TODO(rbuckton): Does this need to be parented?
|
||||
const name = setParent(setTextRange(cloneNode(nodeName), nodeName), nodeName.parent);
|
||||
@@ -6710,9 +6683,10 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
* @param node The declaration.
|
||||
* @param allowComments A value indicating whether comments may be emitted for the name.
|
||||
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
|
||||
* @param ignoreAssignedName Indicates that the assigned name of a declaration shouldn't be considered.
|
||||
*/
|
||||
function getLocalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean) {
|
||||
return getName(node, allowComments, allowSourceMaps, EmitFlags.LocalName);
|
||||
function getLocalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean, ignoreAssignedName?: boolean) {
|
||||
return getName(node, allowComments, allowSourceMaps, EmitFlags.LocalName, ignoreAssignedName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,6 @@ import {
|
||||
DotDotDotToken,
|
||||
ElementAccessExpression,
|
||||
EmptyStatement,
|
||||
EndOfDeclarationMarker,
|
||||
EnumDeclaration,
|
||||
EnumMember,
|
||||
EqualsGreaterThanToken,
|
||||
@@ -137,7 +136,6 @@ import {
|
||||
LabeledStatement,
|
||||
LiteralTypeNode,
|
||||
MappedTypeNode,
|
||||
MergeDeclarationMarker,
|
||||
MetaProperty,
|
||||
MethodDeclaration,
|
||||
MethodSignature,
|
||||
@@ -902,16 +900,6 @@ export function isSyntheticReference(node: Node): node is SyntheticReferenceExpr
|
||||
return node.kind === SyntaxKind.SyntheticReferenceExpression;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isMergeDeclarationMarker(node: Node): node is MergeDeclarationMarker {
|
||||
return node.kind === SyntaxKind.MergeDeclarationMarker;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isEndOfDeclarationMarker(node: Node): node is EndOfDeclarationMarker {
|
||||
return node.kind === SyntaxKind.EndOfDeclarationMarker;
|
||||
}
|
||||
|
||||
// Module References
|
||||
|
||||
export function isExternalModuleReference(node: Node): node is ExternalModuleReference {
|
||||
|
||||
@@ -1249,13 +1249,14 @@ function createModuleOrTypeReferenceResolutionCache<T>(
|
||||
export function createModuleResolutionCache(
|
||||
currentDirectory: string,
|
||||
getCanonicalFileName: (s: string) => string,
|
||||
options?: CompilerOptions
|
||||
options?: CompilerOptions,
|
||||
packageJsonInfoCache?: PackageJsonInfoCache,
|
||||
): ModuleResolutionCache {
|
||||
const result = createModuleOrTypeReferenceResolutionCache(
|
||||
currentDirectory,
|
||||
getCanonicalFileName,
|
||||
options,
|
||||
/*packageJsonInfoCache*/ undefined,
|
||||
packageJsonInfoCache,
|
||||
getOriginalOrResolvedModuleFileName,
|
||||
) as ModuleResolutionCache;
|
||||
result.getOrCreateCacheForModuleName = (nonRelativeName, mode, redirectedReference) => result.getOrCreateCacheForNonRelativeName(nonRelativeName, mode, redirectedReference);
|
||||
@@ -1277,6 +1278,16 @@ export function createTypeReferenceDirectiveResolutionCache(
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getOptionsForLibraryResolution(options: CompilerOptions) {
|
||||
return { moduleResolution: ModuleResolutionKind.Node10, traceResolution: options.traceResolution };
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function resolveLibrary(libraryName: string, resolveFrom: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
|
||||
return resolveModuleName(libraryName, resolveFrom, getOptionsForLibraryResolution(compilerOptions), host, cache);
|
||||
}
|
||||
|
||||
export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache, mode?: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined {
|
||||
const containingDirectory = getDirectoryPath(containingFile);
|
||||
return cache.getFromDirectoryCache(moduleName, mode, containingDirectory, /*redirectedReference*/ undefined);
|
||||
@@ -1747,8 +1758,6 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
|
||||
const diagnosticState = {
|
||||
...state,
|
||||
features: state.features & ~NodeResolutionFeatures.Exports,
|
||||
failedLookupLocations: [],
|
||||
affectingLocations: [],
|
||||
reportDiagnostic: noop,
|
||||
};
|
||||
const diagnosticResult = tryResolve(extensions & (Extensions.TypeScript | Extensions.Declaration), diagnosticState);
|
||||
@@ -1784,6 +1793,12 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
|
||||
resolved = loadModuleFromSelfNameReference(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
|
||||
}
|
||||
if (!resolved) {
|
||||
if (moduleName.indexOf(":") > -1) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Skipping_module_0_that_looks_like_an_absolute_URI_target_file_types_Colon_1, moduleName, formatExtensions(extensions));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_types_Colon_1, moduleName, formatExtensions(extensions));
|
||||
}
|
||||
@@ -1889,7 +1904,7 @@ export function pathContainsNodeModules(path: string): boolean {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function parseNodeModuleFromPath(resolved: string): string | undefined {
|
||||
export function parseNodeModuleFromPath(resolved: string, isFolder?: boolean): string | undefined {
|
||||
const path = normalizePath(resolved);
|
||||
const idx = path.lastIndexOf(nodeModulesPathPart);
|
||||
if (idx === -1) {
|
||||
@@ -1897,16 +1912,16 @@ export function parseNodeModuleFromPath(resolved: string): string | undefined {
|
||||
}
|
||||
|
||||
const indexAfterNodeModules = idx + nodeModulesPathPart.length;
|
||||
let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules);
|
||||
let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules, isFolder);
|
||||
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
|
||||
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
|
||||
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName, isFolder);
|
||||
}
|
||||
return path.slice(0, indexAfterPackageName);
|
||||
}
|
||||
|
||||
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
|
||||
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number, isFolder: boolean | undefined): number {
|
||||
const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1);
|
||||
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
|
||||
return nextSeparatorIndex === -1 ? isFolder ? path.length : prevSeparatorIndex : nextSeparatorIndex;
|
||||
}
|
||||
|
||||
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
|
||||
@@ -2885,7 +2900,7 @@ function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, modu
|
||||
|
||||
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
|
||||
let pathAndExtension =
|
||||
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
|
||||
(rest || !(state.features & NodeResolutionFeatures.EsmMode)) && loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
|
||||
loadNodeModuleFromDirectoryWorker(
|
||||
extensions,
|
||||
candidate,
|
||||
@@ -2926,7 +2941,6 @@ function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, modu
|
||||
return fromPaths.value;
|
||||
}
|
||||
}
|
||||
|
||||
return loader(extensions, candidate, !nodeModulesDirectoryExists, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
ExportAssignment,
|
||||
Extension,
|
||||
extensionFromPath,
|
||||
extensionsNotSupportingExtensionlessResolution,
|
||||
fileExtensionIsOneOf,
|
||||
FileIncludeKind,
|
||||
firstDefined,
|
||||
@@ -91,6 +92,7 @@ import {
|
||||
removeExtension,
|
||||
removeFileExtension,
|
||||
removeSuffix,
|
||||
removeTrailingDirectorySeparator,
|
||||
ResolutionMode,
|
||||
resolvePath,
|
||||
ScriptKind,
|
||||
@@ -151,11 +153,18 @@ function getPreferences(
|
||||
? [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Index]
|
||||
: [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.JsExtension];
|
||||
}
|
||||
const allowImportingTsExtension = shouldAllowImportingTsExtension(compilerOptions, importingSourceFile.fileName);
|
||||
switch (preferredEnding) {
|
||||
case ModuleSpecifierEnding.JsExtension: return [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index];
|
||||
case ModuleSpecifierEnding.JsExtension: return allowImportingTsExtension
|
||||
? [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.TsExtension, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index]
|
||||
: [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index];
|
||||
case ModuleSpecifierEnding.TsExtension: return [ModuleSpecifierEnding.TsExtension, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Index];
|
||||
case ModuleSpecifierEnding.Index: return [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.JsExtension];
|
||||
case ModuleSpecifierEnding.Minimal: return [ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index, ModuleSpecifierEnding.JsExtension];
|
||||
case ModuleSpecifierEnding.Index: return allowImportingTsExtension
|
||||
? [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.TsExtension, ModuleSpecifierEnding.JsExtension]
|
||||
: [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.JsExtension];
|
||||
case ModuleSpecifierEnding.Minimal: return allowImportingTsExtension
|
||||
? [ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index, ModuleSpecifierEnding.TsExtension, ModuleSpecifierEnding.JsExtension]
|
||||
: [ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index, ModuleSpecifierEnding.JsExtension];
|
||||
default: Debug.assertNever(preferredEnding);
|
||||
}
|
||||
},
|
||||
@@ -452,7 +461,7 @@ function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOpt
|
||||
}
|
||||
|
||||
const baseDirectory = getNormalizedAbsolutePath(getPathsBasePath(compilerOptions, host) || baseUrl!, host.getCurrentDirectory());
|
||||
const relativeToBaseUrl = getRelativePathIfInDirectory(moduleFileName, baseDirectory, getCanonicalFileName);
|
||||
const relativeToBaseUrl = getRelativePathIfInSameVolume(moduleFileName, baseDirectory, getCanonicalFileName);
|
||||
if (!relativeToBaseUrl) {
|
||||
return pathsOnly ? undefined : relativePath;
|
||||
}
|
||||
@@ -766,7 +775,7 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike<rea
|
||||
validateEnding({ ending, value })
|
||||
) {
|
||||
const matchedStar = value.substring(prefix.length, value.length - suffix.length);
|
||||
return key.replace("*", matchedStar);
|
||||
return pathIsRelative(matchedStar) ? undefined : key.replace("*", matchedStar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1000,10 +1009,24 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan
|
||||
// happens very easily in fourslash tests though, since every test file listed gets included. See
|
||||
// importNameCodeFix_typesVersions.ts for an example.)
|
||||
const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName);
|
||||
if (removeFileExtension(mainExportFile) === removeFileExtension(getCanonicalFileName(moduleFileToTry))) {
|
||||
const canonicalModuleFileToTry = getCanonicalFileName(moduleFileToTry);
|
||||
if (removeFileExtension(mainExportFile) === removeFileExtension(canonicalModuleFileToTry)) {
|
||||
// ^ An arbitrary removal of file extension for this comparison is almost certainly wrong
|
||||
return { packageRootPath, moduleFileToTry };
|
||||
}
|
||||
else if (
|
||||
packageJsonContent.type !== "module" &&
|
||||
!fileExtensionIsOneOf(canonicalModuleFileToTry, extensionsNotSupportingExtensionlessResolution) &&
|
||||
startsWith(canonicalModuleFileToTry, mainExportFile) &&
|
||||
getDirectoryPath(canonicalModuleFileToTry) === removeTrailingDirectorySeparator(mainExportFile) &&
|
||||
removeFileExtension(getBaseFileName(canonicalModuleFileToTry)) === "index"
|
||||
) {
|
||||
// if mainExportFile is a directory, which contains moduleFileToTry, we just try index file
|
||||
// example mainExportFile: `pkg/lib` and moduleFileToTry: `pkg/lib/index`, we can use packageRootPath
|
||||
// but this behavior is deprecated for packages with "type": "module", so we only do this for packages without "type": "module"
|
||||
// and make sure that the extension on index.{???} is something that supports omitting the extension
|
||||
return { packageRootPath, moduleFileToTry };
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1031,7 +1054,7 @@ function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string
|
||||
|
||||
function getPathsRelativeToRootDirs(path: string, rootDirs: readonly string[], getCanonicalFileName: GetCanonicalFileName): string[] | undefined {
|
||||
return mapDefined(rootDirs, rootDir => {
|
||||
const relativePath = getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName);
|
||||
const relativePath = getRelativePathIfInSameVolume(path, rootDir, getCanonicalFileName);
|
||||
return relativePath !== undefined && isPathRelativeToParent(relativePath) ? undefined : relativePath;
|
||||
});
|
||||
}
|
||||
@@ -1046,7 +1069,12 @@ function processEnding(fileName: string, allowedEndings: readonly ModuleSpecifie
|
||||
return fileName;
|
||||
}
|
||||
|
||||
if (fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Dcts, Extension.Cts])) {
|
||||
const jsPriority = allowedEndings.indexOf(ModuleSpecifierEnding.JsExtension);
|
||||
const tsPriority = allowedEndings.indexOf(ModuleSpecifierEnding.TsExtension);
|
||||
if (fileExtensionIsOneOf(fileName, [Extension.Mts, Extension.Cts]) && tsPriority !== -1 && tsPriority < jsPriority) {
|
||||
return fileName;
|
||||
}
|
||||
else if (fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Dcts, Extension.Cts])) {
|
||||
return noExtension + getJSExtensionForFile(fileName, options);
|
||||
}
|
||||
else if (!fileExtensionIsOneOf(fileName, [Extension.Dts]) && fileExtensionIsOneOf(fileName, [Extension.Ts]) && stringContains(fileName, ".d.")) {
|
||||
@@ -1072,7 +1100,6 @@ function processEnding(fileName: string, allowedEndings: readonly ModuleSpecifie
|
||||
// know if a .d.ts extension is valid, so use no extension or a .js extension
|
||||
if (isDeclarationFileName(fileName)) {
|
||||
const extensionlessPriority = allowedEndings.findIndex(e => e === ModuleSpecifierEnding.Minimal || e === ModuleSpecifierEnding.Index);
|
||||
const jsPriority = allowedEndings.indexOf(ModuleSpecifierEnding.JsExtension);
|
||||
return extensionlessPriority !== -1 && extensionlessPriority < jsPriority
|
||||
? noExtension
|
||||
: noExtension + getJSExtensionForFile(fileName, options);
|
||||
@@ -1122,7 +1149,7 @@ export function tryGetJSExtensionForFile(fileName: string, options: CompilerOpti
|
||||
}
|
||||
}
|
||||
|
||||
function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
|
||||
function getRelativePathIfInSameVolume(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return isRootedDiskPath(relativePath) ? undefined : relativePath;
|
||||
}
|
||||
|
||||
+58
-21
@@ -147,6 +147,7 @@ import {
|
||||
isJSDocNullableType,
|
||||
isJSDocReturnTag,
|
||||
isJSDocTypeTag,
|
||||
isJsxNamespacedName,
|
||||
isJsxOpeningElement,
|
||||
isJsxOpeningFragment,
|
||||
isKeyword,
|
||||
@@ -228,7 +229,6 @@ import {
|
||||
JsxSelfClosingElement,
|
||||
JsxSpreadAttribute,
|
||||
JsxTagNameExpression,
|
||||
JsxTagNamePropertyAccess,
|
||||
JsxText,
|
||||
JsxTokenSyntaxKind,
|
||||
LabeledStatement,
|
||||
@@ -1491,6 +1491,8 @@ namespace Parser {
|
||||
var identifiers: Map<string, string>;
|
||||
var identifierCount: number;
|
||||
|
||||
// TODO(jakebailey): This type is a lie; this value actually contains the result
|
||||
// of ORing a bunch of `1 << ParsingContext.XYZ`.
|
||||
var parsingContext: ParsingContext;
|
||||
|
||||
var notParenthesizedArrow: Set<number> | undefined;
|
||||
@@ -2872,9 +2874,13 @@ namespace Parser {
|
||||
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken;
|
||||
case ParsingContext.JsxChildren:
|
||||
return true;
|
||||
case ParsingContext.JSDocComment:
|
||||
return true;
|
||||
case ParsingContext.Count:
|
||||
return Debug.fail("ParsingContext.Count used as a context"); // Not a real context, only a marker.
|
||||
default:
|
||||
Debug.assertNever(parsingContext, "Non-exhaustive case in 'isListElement'.");
|
||||
}
|
||||
|
||||
return Debug.fail("Non-exhaustive case in 'isListElement'.");
|
||||
}
|
||||
|
||||
function isValidHeritageClauseObjectLiteral() {
|
||||
@@ -3010,6 +3016,9 @@ namespace Parser {
|
||||
|
||||
// True if positioned at element or terminator of the current list or any enclosing list
|
||||
function isInSomeParsingContext(): boolean {
|
||||
// We should be in at least one parsing context, be it SourceElements while parsing
|
||||
// a SourceFile, or JSDocComment when lazily parsing JSDoc.
|
||||
Debug.assert(parsingContext, "Missing parsing context");
|
||||
for (let kind = 0; kind < ParsingContext.Count; kind++) {
|
||||
if (parsingContext & (1 << kind)) {
|
||||
if (isListElement(kind, /*inErrorRecovery*/ true) || isListTerminator(kind)) {
|
||||
@@ -3385,6 +3394,7 @@ namespace Parser {
|
||||
case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
|
||||
case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
|
||||
case ParsingContext.AssertEntries: return parseErrorAtCurrentToken(Diagnostics.Identifier_or_string_literal_expected); // AssertionKey.
|
||||
case ParsingContext.JSDocComment: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
|
||||
case ParsingContext.Count: return Debug.fail("ParsingContext.Count used as a context"); // Not a real context, only a marker.
|
||||
default: Debug.assertNever(context);
|
||||
}
|
||||
@@ -6112,11 +6122,15 @@ namespace Parser {
|
||||
// primaryExpression in the form of an identifier and "this" keyword
|
||||
// We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword
|
||||
// We only want to consider "this" as a primaryExpression
|
||||
let expression: JsxTagNameExpression = parseJsxTagName();
|
||||
while (parseOptional(SyntaxKind.DotToken)) {
|
||||
expression = finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos) as JsxTagNamePropertyAccess;
|
||||
const initialExpression = parseJsxTagName();
|
||||
if (isJsxNamespacedName(initialExpression)) {
|
||||
return initialExpression; // `a:b.c` is invalid syntax, don't even look for the `.` if we parse `a:b`, and let `parseAttribute` report "unexpected :" instead.
|
||||
}
|
||||
return expression;
|
||||
let expression: PropertyAccessExpression | Identifier | ThisExpression = initialExpression;
|
||||
while (parseOptional(SyntaxKind.DotToken)) {
|
||||
expression = finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos);
|
||||
}
|
||||
return expression as JsxTagNameExpression;
|
||||
}
|
||||
|
||||
function parseJsxTagName(): Identifier | JsxNamespacedName | ThisExpression {
|
||||
@@ -7289,6 +7303,8 @@ namespace Parser {
|
||||
|
||||
function tryReuseAmbientDeclaration(pos: number): Statement | undefined {
|
||||
return doInsideOfContext(NodeFlags.Ambient, () => {
|
||||
// TODO(jakebailey): this is totally wrong; `parsingContext` is the result of ORing a bunch of `1 << ParsingContext.XYZ`.
|
||||
// The enum should really be a bunch of flags.
|
||||
const node = currentNode(parsingContext, pos);
|
||||
if (node) {
|
||||
return consumeNode(node) as Statement;
|
||||
@@ -7835,12 +7851,12 @@ namespace Parser {
|
||||
|
||||
function parseClassElement(): ClassElement {
|
||||
const pos = getNodePos();
|
||||
const hasJSDoc = hasPrecedingJSDocComment();
|
||||
if (token() === SyntaxKind.SemicolonToken) {
|
||||
nextToken();
|
||||
return finishNode(factory.createSemicolonClassElement(), pos);
|
||||
return withJSDoc(finishNode(factory.createSemicolonClassElement(), pos), hasJSDoc);
|
||||
}
|
||||
|
||||
const hasJSDoc = hasPrecedingJSDocComment();
|
||||
const modifiers = parseModifiers(/*allowDecorators*/ true, /*permitConstAsModifier*/ true, /*stopOnStartOfClassStaticBlock*/ true);
|
||||
if (token() === SyntaxKind.StaticKeyword && lookAhead(nextTokenIsOpenBrace)) {
|
||||
return parseClassStaticBlockDeclaration(pos, hasJSDoc, modifiers);
|
||||
@@ -8492,7 +8508,8 @@ namespace Parser {
|
||||
TupleElementTypes, // Element types in tuple element type list
|
||||
HeritageClauses, // Heritage clauses for a class or interface declaration.
|
||||
ImportOrExportSpecifiers, // Named import clause's import specifier list,
|
||||
AssertEntries, // Import entries list.
|
||||
AssertEntries, // Import entries list.
|
||||
JSDocComment, // Parsing via JSDocParser
|
||||
Count // Number of parsing contexts
|
||||
}
|
||||
|
||||
@@ -8598,6 +8615,9 @@ namespace Parser {
|
||||
}
|
||||
|
||||
function parseJSDocCommentWorker(start = 0, length: number | undefined): JSDoc | undefined {
|
||||
const saveParsingContext = parsingContext;
|
||||
parsingContext |= 1 << ParsingContext.JSDocComment;
|
||||
|
||||
const content = sourceText;
|
||||
const end = length === undefined ? content.length : start + length;
|
||||
length = end - start;
|
||||
@@ -8620,7 +8640,11 @@ namespace Parser {
|
||||
const parts: JSDocComment[] = [];
|
||||
|
||||
// + 3 for leading /**, - 5 in total for /** */
|
||||
return scanner.scanRange(start + 3, length - 5, () => {
|
||||
const result = scanner.scanRange(start + 3, length - 5, doJSDocScan);
|
||||
parsingContext = saveParsingContext;
|
||||
return result;
|
||||
|
||||
function doJSDocScan() {
|
||||
// Initially we can parse out a tag. We also have seen a starting asterisk.
|
||||
// This is so that /** * @type */ doesn't parse.
|
||||
let state = JSDocState.SawAsterisk;
|
||||
@@ -8726,7 +8750,7 @@ namespace Parser {
|
||||
if (parts.length && tags) Debug.assertIsDefined(commentsPos, "having parsed tags implies that the end of the comment span should be set");
|
||||
const tagsArray = tags && createNodeArray(tags, tagsPos, tagsEnd);
|
||||
return finishNode(factory.createJSDocComment(parts.length ? createNodeArray(parts, start, commentsPos) : trimmedComments.length ? trimmedComments : undefined, tagsArray), start, end);
|
||||
});
|
||||
}
|
||||
|
||||
function removeLeadingNewlines(comments: string[]) {
|
||||
while (comments.length && (comments[0] === "\n" || comments[0] === "\r")) {
|
||||
@@ -9140,12 +9164,15 @@ namespace Parser {
|
||||
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) {
|
||||
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
const pos = getNodePos();
|
||||
let child: JSDocPropertyLikeTag | JSDocTypeTag | false;
|
||||
let child: JSDocPropertyLikeTag | JSDocTypeTag | JSDocTemplateTag | false;
|
||||
let children: JSDocPropertyLikeTag[] | undefined;
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) {
|
||||
if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) {
|
||||
children = append(children, child);
|
||||
}
|
||||
else if (child.kind === SyntaxKind.JSDocTemplateTag) {
|
||||
parseErrorAtRange(child.tagName, Diagnostics.A_JSDoc_template_tag_may_not_follow_a_typedef_callback_or_overload_tag);
|
||||
}
|
||||
}
|
||||
if (children) {
|
||||
const literal = finishNode(factory.createJSDocTypeLiteral(children, typeExpression.type.kind === SyntaxKind.ArrayType), pos);
|
||||
@@ -9244,7 +9271,9 @@ namespace Parser {
|
||||
const usedBrace = parseOptional(SyntaxKind.OpenBraceToken);
|
||||
const pos = getNodePos();
|
||||
const expression = parsePropertyAccessEntityNameExpression();
|
||||
scanner.setInJSDocType(true);
|
||||
const typeArguments = tryParseTypeArguments();
|
||||
scanner.setInJSDocType(false);
|
||||
const node = factory.createExpressionWithTypeArguments(expression, typeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression };
|
||||
const res = finishNode(node, pos);
|
||||
if (usedBrace) {
|
||||
@@ -9289,11 +9318,14 @@ namespace Parser {
|
||||
|
||||
let end: number | undefined;
|
||||
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
let child: JSDocTypeTag | JSDocPropertyTag | false;
|
||||
let child: JSDocTypeTag | JSDocPropertyTag | JSDocTemplateTag | false;
|
||||
let childTypeTag: JSDocTypeTag | undefined;
|
||||
let jsDocPropertyTags: JSDocPropertyTag[] | undefined;
|
||||
let hasChildren = false;
|
||||
while (child = tryParse(() => parseChildPropertyTag(indent))) {
|
||||
if (child.kind === SyntaxKind.JSDocTemplateTag) {
|
||||
break;
|
||||
}
|
||||
hasChildren = true;
|
||||
if (child.kind === SyntaxKind.JSDocTypeTag) {
|
||||
if (childTypeTag) {
|
||||
@@ -9357,12 +9389,15 @@ namespace Parser {
|
||||
return typeNameOrNamespaceName;
|
||||
}
|
||||
|
||||
|
||||
function parseCallbackTagParameters(indent: number) {
|
||||
const pos = getNodePos();
|
||||
let child: JSDocParameterTag | false;
|
||||
let child: JSDocParameterTag | JSDocTemplateTag | false;
|
||||
let parameters;
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag)) {
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag | JSDocTemplateTag)) {
|
||||
if (child.kind === SyntaxKind.JSDocTemplateTag) {
|
||||
parseErrorAtRange(child.tagName, Diagnostics.A_JSDoc_template_tag_may_not_follow_a_typedef_callback_or_overload_tag);
|
||||
break;
|
||||
}
|
||||
parameters = append(parameters, child);
|
||||
}
|
||||
return createNodeArray(parameters || [], pos);
|
||||
@@ -9418,10 +9453,10 @@ namespace Parser {
|
||||
}
|
||||
|
||||
function parseChildPropertyTag(indent: number) {
|
||||
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | false;
|
||||
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | JSDocTemplateTag | false;
|
||||
}
|
||||
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false {
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = false;
|
||||
while (true) {
|
||||
@@ -9457,13 +9492,13 @@ namespace Parser {
|
||||
}
|
||||
}
|
||||
|
||||
function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
|
||||
function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false {
|
||||
Debug.assert(token() === SyntaxKind.AtToken);
|
||||
const start = scanner.getTokenFullStart();
|
||||
nextTokenJSDoc();
|
||||
|
||||
const tagName = parseJSDocIdentifierName();
|
||||
skipWhitespace();
|
||||
const indentText = skipWhitespaceOrAsterisk();
|
||||
let t: PropertyLikeParse;
|
||||
switch (tagName.escapedText) {
|
||||
case "type":
|
||||
@@ -9477,6 +9512,8 @@ namespace Parser {
|
||||
case "param":
|
||||
t = PropertyLikeParse.Parameter | PropertyLikeParse.CallbackParameter;
|
||||
break;
|
||||
case "template":
|
||||
return parseTemplateTag(start, tagName, indent, indentText);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
+143
-25
@@ -153,9 +153,11 @@ import {
|
||||
getTsBuildInfoEmitOutputFilePath,
|
||||
getTsConfigObjectLiteralExpression,
|
||||
getTsConfigPropArrayElementValue,
|
||||
getTypesPackageName,
|
||||
HasChangedAutomaticTypeDirectiveNames,
|
||||
hasChangesInResolutions,
|
||||
hasExtension,
|
||||
HasInvalidatedLibResolutions,
|
||||
HasInvalidatedResolutions,
|
||||
hasJSDocNodes,
|
||||
hasJSFileExtension,
|
||||
@@ -211,6 +213,7 @@ import {
|
||||
JsxEmit,
|
||||
length,
|
||||
libMap,
|
||||
LibResolution,
|
||||
libs,
|
||||
mapDefined,
|
||||
mapDefinedIterator,
|
||||
@@ -276,6 +279,7 @@ import {
|
||||
ResolvedModuleWithFailedLookupLocations,
|
||||
ResolvedProjectReference,
|
||||
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
|
||||
resolveLibrary,
|
||||
resolveModuleName,
|
||||
resolveTypeReferenceDirective,
|
||||
returnFalse,
|
||||
@@ -1097,6 +1101,32 @@ function forEachProjectReference<T>(
|
||||
/** @internal */
|
||||
export const inferredTypesContainingFile = "__inferred type names__.ts";
|
||||
|
||||
/** @internal */
|
||||
export function getInferredLibraryNameResolveFrom(options: CompilerOptions, currentDirectory: string, libFileName: string) {
|
||||
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
|
||||
return combinePaths(containingDirectory, `__lib_node_modules_lookup_${libFileName}__.ts`);
|
||||
}
|
||||
|
||||
function getLibraryNameFromLibFileName(libFileName: string) {
|
||||
// Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
|
||||
// lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
|
||||
// lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
|
||||
const components = libFileName.split(".");
|
||||
let path = components[1];
|
||||
let i = 2;
|
||||
while (components[i] && components[i] !== "d") {
|
||||
path += (i === 2 ? "/" : "-") + components[i];
|
||||
i++;
|
||||
}
|
||||
return "@typescript/lib-" + path;
|
||||
}
|
||||
|
||||
function getLibFileNameFromLibReference(libReference: FileReference) {
|
||||
const libName = toFileNameLowerCase(libReference.fileName);
|
||||
const libFileName = libMap.get(libName);
|
||||
return { libName, libFileName };
|
||||
}
|
||||
|
||||
interface DiagnosticCache<T extends Diagnostic> {
|
||||
perFile?: Map<Path, readonly T[]>;
|
||||
allDiagnostics?: readonly T[];
|
||||
@@ -1176,6 +1206,7 @@ export function isProgramUptoDate(
|
||||
getSourceVersion: (path: Path, fileName: string) => string | undefined,
|
||||
fileExists: (fileName: string) => boolean,
|
||||
hasInvalidatedResolutions: HasInvalidatedResolutions,
|
||||
hasInvalidatedLibResolutions: HasInvalidatedLibResolutions,
|
||||
hasChangedAutomaticTypeDirectiveNames: HasChangedAutomaticTypeDirectiveNames | undefined,
|
||||
getParsedCommandLine: (fileName: string) => ParsedCommandLine | undefined,
|
||||
projectReferences: readonly ProjectReference[] | undefined
|
||||
@@ -1201,6 +1232,9 @@ export function isProgramUptoDate(
|
||||
// If the compilation settings do no match, then the program is not up-to-date
|
||||
if (!compareDataObjects(currentOptions, newOptions)) return false;
|
||||
|
||||
// If library resolution is invalidated, then the program is not up-to-date
|
||||
if (program.resolvedLibReferences && forEachEntry(program.resolvedLibReferences, (_value, libFileName) => hasInvalidatedLibResolutions(libFileName))) return false;
|
||||
|
||||
// If everything matches but the text of config file is changed,
|
||||
// error locations can change for program options, so update the program
|
||||
if (currentOptions.configFile && newOptions.configFile) return currentOptions.configFile.text === newOptions.configFile.text;
|
||||
@@ -1469,6 +1503,12 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
let automaticTypeDirectiveNames: string[] | undefined;
|
||||
let automaticTypeDirectiveResolutions: ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
|
||||
|
||||
let resolvedLibReferences: Map<string, LibResolution> | undefined;
|
||||
let resolvedLibProcessing: Map<string, LibResolution> | undefined;
|
||||
|
||||
let packageMap: Map<string, boolean> | undefined;
|
||||
|
||||
|
||||
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
|
||||
// This works as imported modules are discovered recursively in a depth first manner, specifically:
|
||||
// - For each root file, findSourceFile is called.
|
||||
@@ -1592,6 +1632,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
);
|
||||
}
|
||||
|
||||
const hasInvalidatedLibResolutions = host.hasInvalidatedLibResolutions || returnFalse;
|
||||
let actualResolveLibrary: (libraryName: string, resolveFrom: string, options: CompilerOptions, libFileName: string) => ResolvedModuleWithFailedLookupLocations;
|
||||
if (host.resolveLibrary) {
|
||||
actualResolveLibrary = host.resolveLibrary.bind(host);
|
||||
}
|
||||
else {
|
||||
const libraryResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options, moduleResolutionCache?.getPackageJsonInfoCache());
|
||||
actualResolveLibrary = (libraryName, resolveFrom, options) =>
|
||||
resolveLibrary(libraryName, resolveFrom, options, host, libraryResolutionCache);
|
||||
}
|
||||
|
||||
// Map from a stringified PackageId to the source file with that id.
|
||||
// Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
|
||||
// `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
|
||||
@@ -1688,7 +1739,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
if (automaticTypeDirectiveNames.length) {
|
||||
tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: automaticTypeDirectiveNames.length });
|
||||
// This containingFilename needs to match with the one used in managed-side
|
||||
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
|
||||
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
|
||||
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(automaticTypeDirectiveNames, containingFilename);
|
||||
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
|
||||
@@ -1772,6 +1823,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
|
||||
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
|
||||
oldProgram = undefined;
|
||||
resolvedLibProcessing = undefined;
|
||||
|
||||
const program: Program = {
|
||||
getRootFileNames: () => rootNames,
|
||||
@@ -1813,6 +1865,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
sourceFileToPackageName,
|
||||
redirectTargetsMap,
|
||||
usesUriStyleNodeCoreModules,
|
||||
resolvedLibReferences,
|
||||
getCurrentPackagesMap: () => packageMap,
|
||||
typesPackageExists,
|
||||
packageBundlesTypes,
|
||||
isEmittedFile,
|
||||
getConfigFileParsingDiagnostics,
|
||||
getProjectReferences,
|
||||
@@ -1859,6 +1915,30 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
|
||||
return program;
|
||||
|
||||
function getPackagesMap() {
|
||||
if (packageMap) return packageMap;
|
||||
packageMap = new Map();
|
||||
// A package name maps to true when we detect it has .d.ts files.
|
||||
// This is useful as an approximation of whether a package bundles its own types.
|
||||
// Note: we only look at files already found by module resolution,
|
||||
// so there may be files we did not consider.
|
||||
files.forEach(sf => {
|
||||
if (!sf.resolvedModules) return;
|
||||
|
||||
sf.resolvedModules.forEach(({ resolvedModule }) => {
|
||||
if (resolvedModule?.packageId) packageMap!.set(resolvedModule.packageId.name, resolvedModule.extension === Extension.Dts || !!packageMap!.get(resolvedModule.packageId.name));
|
||||
});
|
||||
});
|
||||
return packageMap;
|
||||
}
|
||||
|
||||
function typesPackageExists(packageName: string): boolean {
|
||||
return getPackagesMap().has(getTypesPackageName(packageName));
|
||||
}
|
||||
function packageBundlesTypes(packageName: string): boolean {
|
||||
return !!getPackagesMap().get(packageName);
|
||||
}
|
||||
|
||||
function addResolutionDiagnostics(resolution: ResolvedModuleWithFailedLookupLocations | ResolvedTypeReferenceDirectiveWithFailedLookupLocations) {
|
||||
if (!resolution.resolutionDiagnostics?.length) return;
|
||||
(fileProcessingDiagnostics ??= []).push({
|
||||
@@ -2441,6 +2521,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
return StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
if (oldProgram.resolvedLibReferences &&
|
||||
forEachEntry(oldProgram.resolvedLibReferences, (resolution, libFileName) => pathForLibFileWorker(libFileName).actual !== resolution.actual)) {
|
||||
return StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
if (host.hasChangedAutomaticTypeDirectiveNames) {
|
||||
if (host.hasChangedAutomaticTypeDirectiveNames()) return StructureIsReused.SafeModules;
|
||||
}
|
||||
@@ -2481,6 +2566,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
|
||||
redirectTargetsMap = oldProgram.redirectTargetsMap;
|
||||
usesUriStyleNodeCoreModules = oldProgram.usesUriStyleNodeCoreModules;
|
||||
resolvedLibReferences = oldProgram.resolvedLibReferences;
|
||||
packageMap = oldProgram.getCurrentPackagesMap();
|
||||
|
||||
return StructureIsReused.Completely;
|
||||
}
|
||||
@@ -2596,7 +2683,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
return equalityComparer(file.fileName, getDefaultLibraryFileName());
|
||||
}
|
||||
else {
|
||||
return some(options.lib, libFileName => equalityComparer(file.fileName, pathForLibFile(libFileName)));
|
||||
return some(options.lib, libFileName => equalityComparer(file.fileName, resolvedLibReferences!.get(libFileName)!.actual));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3310,11 +3397,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
}
|
||||
|
||||
function getLibFileFromReference(ref: FileReference) {
|
||||
const libName = toFileNameLowerCase(ref.fileName);
|
||||
const libFileName = libMap.get(libName);
|
||||
if (libFileName) {
|
||||
return getSourceFile(pathForLibFile(libFileName));
|
||||
}
|
||||
const { libFileName } = getLibFileNameFromLibReference(ref);
|
||||
const actualFileName = libFileName && resolvedLibReferences?.get(libFileName)?.actual;
|
||||
return actualFileName !== undefined ? getSourceFile(actualFileName) : undefined;
|
||||
}
|
||||
|
||||
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
|
||||
@@ -3810,28 +3895,61 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
}
|
||||
|
||||
function pathForLibFile(libFileName: string): string {
|
||||
// Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
|
||||
// lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
|
||||
// lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
|
||||
const components = libFileName.split(".");
|
||||
let path = components[1];
|
||||
let i = 2;
|
||||
while (components[i] && components[i] !== "d") {
|
||||
path += (i === 2 ? "/" : "-") + components[i];
|
||||
i++;
|
||||
const existing = resolvedLibReferences?.get(libFileName);
|
||||
if (existing) return existing.actual;
|
||||
const result = pathForLibFileWorker(libFileName);
|
||||
(resolvedLibReferences ??= new Map()).set(libFileName, result);
|
||||
return result.actual;
|
||||
}
|
||||
|
||||
function pathForLibFileWorker(libFileName: string): LibResolution {
|
||||
const existing = resolvedLibProcessing?.get(libFileName);
|
||||
if (existing) return existing;
|
||||
|
||||
if (structureIsReused !== StructureIsReused.Not && oldProgram && !hasInvalidatedLibResolutions(libFileName)) {
|
||||
const oldResolution = oldProgram.resolvedLibReferences?.get(libFileName);
|
||||
if (oldResolution) {
|
||||
if (oldResolution.resolution && isTraceEnabled(options, host)) {
|
||||
const libraryName = getLibraryNameFromLibFileName(libFileName);
|
||||
const resolveFrom = getInferredLibraryNameResolveFrom(options, currentDirectory, libFileName);
|
||||
trace(host,
|
||||
oldResolution.resolution.resolvedModule ?
|
||||
oldResolution.resolution.resolvedModule.packageId ?
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2 :
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_not_resolved,
|
||||
libraryName,
|
||||
getNormalizedAbsolutePath(resolveFrom, currentDirectory),
|
||||
oldResolution.resolution.resolvedModule?.resolvedFileName,
|
||||
oldResolution.resolution.resolvedModule?.packageId && packageIdToString(oldResolution.resolution.resolvedModule.packageId)
|
||||
);
|
||||
}
|
||||
(resolvedLibProcessing ??= new Map()).set(libFileName, oldResolution);
|
||||
return oldResolution;
|
||||
}
|
||||
}
|
||||
const resolveFrom = combinePaths(currentDirectory, `__lib_node_modules_lookup_${libFileName}__.ts`);
|
||||
const localOverrideModuleResult = resolveModuleName("@typescript/lib-" + path, resolveFrom, { moduleResolution: ModuleResolutionKind.Node10 }, host, moduleResolutionCache);
|
||||
if (localOverrideModuleResult?.resolvedModule) {
|
||||
return localOverrideModuleResult.resolvedModule.resolvedFileName;
|
||||
}
|
||||
return combinePaths(defaultLibraryPath, libFileName);
|
||||
|
||||
const libraryName = getLibraryNameFromLibFileName(libFileName);
|
||||
const resolveFrom = getInferredLibraryNameResolveFrom(options, currentDirectory, libFileName);
|
||||
tracing?.push(tracing.Phase.Program, "resolveLibrary", { resolveFrom });
|
||||
performance.mark("beforeResolveLibrary");
|
||||
const resolution = actualResolveLibrary(libraryName, resolveFrom, options, libFileName);
|
||||
performance.mark("afterResolveLibrary");
|
||||
performance.measure("ResolveLibrary", "beforeResolveLibrary", "afterResolveLibrary");
|
||||
tracing?.pop();
|
||||
const result: LibResolution = {
|
||||
resolution,
|
||||
actual: resolution.resolvedModule ?
|
||||
resolution.resolvedModule.resolvedFileName :
|
||||
combinePaths(defaultLibraryPath, libFileName)
|
||||
};
|
||||
(resolvedLibProcessing ??= new Map()).set(libFileName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function processLibReferenceDirectives(file: SourceFile) {
|
||||
forEach(file.libReferenceDirectives, (libReference, index) => {
|
||||
const libName = toFileNameLowerCase(libReference.fileName);
|
||||
const libFileName = libMap.get(libName);
|
||||
const { libName, libFileName } = getLibFileNameFromLibReference(libReference);
|
||||
if (libFileName) {
|
||||
// we ignore any 'no-default-lib' reference set on this file.
|
||||
processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
|
||||
@@ -3971,7 +4089,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
}
|
||||
else {
|
||||
// An absolute path pointing to the containing directory of the config file
|
||||
const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
|
||||
const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), currentDirectory);
|
||||
sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
|
||||
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
if (sourceFile === undefined) {
|
||||
|
||||
+206
-71
@@ -26,9 +26,12 @@ import {
|
||||
GetCanonicalFileName,
|
||||
getDirectoryPath,
|
||||
getEffectiveTypeRoots,
|
||||
getInferredLibraryNameResolveFrom,
|
||||
getNormalizedAbsolutePath,
|
||||
getOptionsForLibraryResolution,
|
||||
getPathComponents,
|
||||
getPathFromPathComponents,
|
||||
HasInvalidatedLibResolutions,
|
||||
HasInvalidatedResolutions,
|
||||
hasTrailingDirectorySeparator,
|
||||
ignoredPaths,
|
||||
@@ -63,6 +66,7 @@ import {
|
||||
ResolvedModuleWithFailedLookupLocations,
|
||||
ResolvedProjectReference,
|
||||
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
|
||||
resolveLibrary as ts_resolveLibrary,
|
||||
resolveModuleName as ts_resolveModuleName,
|
||||
returnTrue,
|
||||
some,
|
||||
@@ -75,6 +79,11 @@ import {
|
||||
WatchDirectoryFlags,
|
||||
} from "./_namespaces/ts";
|
||||
|
||||
/** @internal */
|
||||
export interface HasInvalidatedFromResolutionCache {
|
||||
hasInvalidatedResolutions: HasInvalidatedResolutions;
|
||||
hasInvalidatedLibResolutions: HasInvalidatedLibResolutions;
|
||||
}
|
||||
/**
|
||||
* This is the cache of module/typedirectives resolution that can be retained across program
|
||||
*
|
||||
@@ -100,7 +109,12 @@ export interface ResolutionCache {
|
||||
containingSourceFile: SourceFile | undefined,
|
||||
reusedNames: readonly T[] | undefined
|
||||
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
|
||||
|
||||
resolveLibrary(
|
||||
libraryName: string,
|
||||
resolveFrom: string,
|
||||
options: CompilerOptions,
|
||||
libFileName: string,
|
||||
): ResolvedModuleWithFailedLookupLocations;
|
||||
resolveSingleModuleNameWithoutWatching(
|
||||
moduleName: string,
|
||||
containingFile: string,
|
||||
@@ -111,7 +125,10 @@ export interface ResolutionCache {
|
||||
removeResolutionsOfFile(filePath: Path): void;
|
||||
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
|
||||
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map<Path, readonly string[]>): void;
|
||||
createHasInvalidatedResolutions(customHasInvalidatedResolutions: HasInvalidatedResolutions): HasInvalidatedResolutions;
|
||||
createHasInvalidatedResolutions(
|
||||
customHasInvalidatedResolutions: HasInvalidatedResolutions,
|
||||
customHasInvalidatedLibResolutions: HasInvalidatedLibResolutions,
|
||||
): HasInvalidatedFromResolutionCache;
|
||||
hasChangedAutomaticTypeDirectiveNames(): boolean;
|
||||
isFileWithInvalidatedNonRelativeUnresolvedImports(path: Path): boolean;
|
||||
|
||||
@@ -125,6 +142,7 @@ export interface ResolutionCache {
|
||||
getModuleResolutionCache(): ModuleResolutionCache;
|
||||
|
||||
clear(): void;
|
||||
onChangesAffectModuleResolution(): void;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -135,6 +153,7 @@ export interface ResolutionWithFailedLookupLocations {
|
||||
refCount?: number;
|
||||
// Files that have this resolution using
|
||||
files?: Set<Path>;
|
||||
node10Result?: string;
|
||||
}
|
||||
|
||||
interface ResolutionWithResolvedFileName {
|
||||
@@ -412,6 +431,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
let failedLookupChecks: Set<Path> | undefined;
|
||||
let startsWithPathChecks: Set<Path> | undefined;
|
||||
let isInDirectoryChecks: Set<Path> | undefined;
|
||||
let allModuleAndTypeResolutionsAreInvalidated = false;
|
||||
|
||||
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!()); // TODO: GH#18217
|
||||
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
|
||||
@@ -434,6 +454,14 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
moduleResolutionCache.getPackageJsonInfoCache(),
|
||||
);
|
||||
|
||||
const resolvedLibraries = new Map<string, CachedResolvedModuleWithFailedLookupLocations>();
|
||||
const libraryResolutionCache = createModuleResolutionCache(
|
||||
getCurrentDirectory(),
|
||||
resolutionHost.getCanonicalFileName,
|
||||
getOptionsForLibraryResolution(resolutionHost.getCompilationSettings()),
|
||||
moduleResolutionCache.getPackageJsonInfoCache(),
|
||||
);
|
||||
|
||||
const directoryWatchesOfFailedLookups = new Map<string, DirectoryWatchesOfFailedLookup>();
|
||||
const fileWatchesOfAffectingLocations = new Map<string, FileWatcherOfAffectingLocation>();
|
||||
const rootDir = getRootDirectoryOfResolutionCache(rootDirForResolution, getCurrentDirectory);
|
||||
@@ -453,6 +481,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
finishCachingPerDirectoryResolution,
|
||||
resolveModuleNameLiterals,
|
||||
resolveTypeReferenceDirectiveReferences,
|
||||
resolveLibrary,
|
||||
resolveSingleModuleNameWithoutWatching,
|
||||
removeResolutionsFromProjectReferenceRedirects,
|
||||
removeResolutionsOfFile,
|
||||
@@ -464,7 +493,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
isFileWithInvalidatedNonRelativeUnresolvedImports,
|
||||
updateTypeRootsWatch,
|
||||
closeTypeRootsWatch,
|
||||
clear
|
||||
clear,
|
||||
onChangesAffectModuleResolution,
|
||||
};
|
||||
|
||||
function getResolvedModule(resolution: CachedResolvedModuleWithFailedLookupLocations) {
|
||||
@@ -490,14 +520,25 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
isInDirectoryChecks = undefined;
|
||||
affectingPathChecks = undefined;
|
||||
affectingPathChecksForFile = undefined;
|
||||
allModuleAndTypeResolutionsAreInvalidated = false;
|
||||
moduleResolutionCache.clear();
|
||||
typeReferenceDirectiveResolutionCache.clear();
|
||||
moduleResolutionCache.update(resolutionHost.getCompilationSettings());
|
||||
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
|
||||
libraryResolutionCache.clear();
|
||||
impliedFormatPackageJsons.clear();
|
||||
resolvedLibraries.clear();
|
||||
hasChangedAutomaticTypeDirectiveNames = false;
|
||||
}
|
||||
|
||||
function onChangesAffectModuleResolution() {
|
||||
allModuleAndTypeResolutionsAreInvalidated = true;
|
||||
moduleResolutionCache.clearAllExceptPackageJsonInfoCache();
|
||||
typeReferenceDirectiveResolutionCache.clearAllExceptPackageJsonInfoCache();
|
||||
moduleResolutionCache.update(resolutionHost.getCompilationSettings());
|
||||
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
|
||||
}
|
||||
|
||||
function startRecordingFilesWithChangedResolutions() {
|
||||
filesWithChangedSetOfUnresolvedImports = [];
|
||||
}
|
||||
@@ -518,31 +559,55 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
return !!value && !!value.length;
|
||||
}
|
||||
|
||||
function createHasInvalidatedResolutions(customHasInvalidatedResolutions: HasInvalidatedResolutions): HasInvalidatedResolutions {
|
||||
function createHasInvalidatedResolutions(
|
||||
customHasInvalidatedResolutions: HasInvalidatedResolutions,
|
||||
customHasInvalidatedLibResolutions: HasInvalidatedLibResolutions,
|
||||
): HasInvalidatedFromResolutionCache {
|
||||
// Ensure pending resolutions are applied
|
||||
invalidateResolutionsOfFailedLookupLocations();
|
||||
const collected = filesWithInvalidatedResolutions;
|
||||
filesWithInvalidatedResolutions = undefined;
|
||||
return path => customHasInvalidatedResolutions(path) ||
|
||||
!!collected?.has(path) ||
|
||||
isFileWithInvalidatedNonRelativeUnresolvedImports(path);
|
||||
return {
|
||||
hasInvalidatedResolutions: path => customHasInvalidatedResolutions(path) ||
|
||||
allModuleAndTypeResolutionsAreInvalidated ||
|
||||
!!collected?.has(path) ||
|
||||
isFileWithInvalidatedNonRelativeUnresolvedImports(path),
|
||||
hasInvalidatedLibResolutions: libFileName => customHasInvalidatedLibResolutions(libFileName) ||
|
||||
!!resolvedLibraries?.get(libFileName)?.isInvalidated,
|
||||
};
|
||||
}
|
||||
|
||||
function startCachingPerDirectoryResolution() {
|
||||
moduleResolutionCache.clearAllExceptPackageJsonInfoCache();
|
||||
typeReferenceDirectiveResolutionCache.clearAllExceptPackageJsonInfoCache();
|
||||
libraryResolutionCache.clearAllExceptPackageJsonInfoCache();
|
||||
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
|
||||
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
|
||||
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
|
||||
nonRelativeExternalModuleResolutions.clear();
|
||||
}
|
||||
|
||||
function cleanupLibResolutionWatching(newProgram: Program | undefined) {
|
||||
resolvedLibraries.forEach((resolution, libFileName) => {
|
||||
if (!newProgram?.resolvedLibReferences?.has(libFileName)) {
|
||||
stopWatchFailedLookupLocationOfResolution(
|
||||
resolution,
|
||||
resolutionHost.toPath(getInferredLibraryNameResolveFrom(newProgram!.getCompilerOptions(), getCurrentDirectory(), libFileName)),
|
||||
getResolvedModule,
|
||||
);
|
||||
resolvedLibraries.delete(libFileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function finishCachingPerDirectoryResolution(newProgram: Program | undefined, oldProgram: Program | undefined) {
|
||||
filesWithInvalidatedNonRelativeUnresolvedImports = undefined;
|
||||
allModuleAndTypeResolutionsAreInvalidated = false;
|
||||
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
|
||||
nonRelativeExternalModuleResolutions.clear();
|
||||
// Update file watches
|
||||
if (newProgram !== oldProgram) {
|
||||
cleanupLibResolutionWatching(newProgram);
|
||||
newProgram?.getSourceFiles().forEach(newFile => {
|
||||
const expected = isExternalOrCommonJsModule(newFile) ? newFile.packageJsonLocations?.length ?? 0 : 0;
|
||||
const existing = impliedFormatPackageJsons.get(newFile.path) ?? emptyArray;
|
||||
@@ -644,11 +709,12 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
|
||||
shouldRetryResolution: (t: T) => boolean;
|
||||
logChanges?: boolean;
|
||||
deferWatchingNonRelativeResolution: boolean;
|
||||
}
|
||||
function resolveNamesWithLocalCache<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
|
||||
entries, containingFile, containingSourceFile, redirectedReference, options,
|
||||
perFileCache, reusedNames,
|
||||
loader, getResolutionWithResolvedFileName,
|
||||
loader, getResolutionWithResolvedFileName, deferWatchingNonRelativeResolution,
|
||||
shouldRetryResolution, logChanges,
|
||||
}: ResolveNamesWithLocalCacheInput<Entry, SourceFile, T, R>): readonly T[] {
|
||||
const path = resolutionHost.toPath(containingFile);
|
||||
@@ -670,16 +736,16 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
let resolution = resolutionsInFile.get(name, mode);
|
||||
// Resolution is valid if it is present and not invalidated
|
||||
if (!seenNamesInFile.has(name, mode) &&
|
||||
unmatchedRedirects || !resolution || resolution.isInvalidated ||
|
||||
// If the name is unresolved import that was invalidated, recalculate
|
||||
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
|
||||
(allModuleAndTypeResolutionsAreInvalidated || unmatchedRedirects || !resolution || resolution.isInvalidated ||
|
||||
// If the name is unresolved import that was invalidated, recalculate
|
||||
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution)))) {
|
||||
const existingResolution = resolution;
|
||||
resolution = loader.resolve(name, mode);
|
||||
if (resolutionHost.onDiscoveredSymlink && resolutionIsSymlink(resolution)) {
|
||||
resolutionHost.onDiscoveredSymlink();
|
||||
}
|
||||
resolutionsInFile.set(name, mode, resolution);
|
||||
watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, path, getResolutionWithResolvedFileName);
|
||||
watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, path, getResolutionWithResolvedFileName, deferWatchingNonRelativeResolution);
|
||||
if (existingResolution) {
|
||||
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolutionWithResolvedFileName);
|
||||
}
|
||||
@@ -693,7 +759,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
else {
|
||||
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
|
||||
if (isTraceEnabled(options, host) && !seenNamesInFile.has(name, mode)) {
|
||||
const resolved = getResolutionWithResolvedFileName(resolution);
|
||||
const resolved = getResolutionWithResolvedFileName(resolution!);
|
||||
trace(
|
||||
host,
|
||||
perFileCache === resolvedModuleNames as unknown ?
|
||||
@@ -778,6 +844,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
),
|
||||
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective,
|
||||
shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined,
|
||||
deferWatchingNonRelativeResolution: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -805,9 +872,48 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
getResolutionWithResolvedFileName: getResolvedModule,
|
||||
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
|
||||
logChanges: logChangesWhenResolvingModule,
|
||||
deferWatchingNonRelativeResolution: true, // Defer non relative resolution watch because we could be using ambient modules
|
||||
});
|
||||
}
|
||||
|
||||
function resolveLibrary(
|
||||
libraryName: string,
|
||||
resolveFrom: string,
|
||||
options: CompilerOptions,
|
||||
libFileName: string,
|
||||
) {
|
||||
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
|
||||
let resolution = resolvedLibraries?.get(libFileName);
|
||||
if (!resolution || resolution.isInvalidated) {
|
||||
const existingResolution = resolution;
|
||||
resolution = ts_resolveLibrary(libraryName, resolveFrom, options, host, libraryResolutionCache);
|
||||
const path = resolutionHost.toPath(resolveFrom);
|
||||
watchFailedLookupLocationsOfExternalModuleResolutions(libraryName, resolution, path, getResolvedModule, /*deferWatchingNonRelativeResolution*/ false);
|
||||
resolvedLibraries.set(libFileName, resolution);
|
||||
if (existingResolution) {
|
||||
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolvedModule);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isTraceEnabled(options, host)) {
|
||||
const resolved = getResolvedModule(resolution);
|
||||
trace(
|
||||
host,
|
||||
resolved?.resolvedFileName ?
|
||||
resolved.packageId ?
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2 :
|
||||
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_not_resolved,
|
||||
libraryName,
|
||||
resolveFrom,
|
||||
resolved?.resolvedFileName,
|
||||
resolved?.packageId && packageIdToString(resolved.packageId)
|
||||
);
|
||||
}
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
function resolveSingleModuleNameWithoutWatching(moduleName: string, containingFile: string) {
|
||||
const path = resolutionHost.toPath(containingFile);
|
||||
const resolutionsInFile = resolvedModuleNames.get(path);
|
||||
@@ -825,6 +931,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
resolution: T,
|
||||
filePath: Path,
|
||||
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
|
||||
deferWatchingNonRelativeResolution: boolean,
|
||||
) {
|
||||
if (resolution.refCount) {
|
||||
resolution.refCount++;
|
||||
@@ -833,7 +940,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
else {
|
||||
resolution.refCount = 1;
|
||||
Debug.assert(!resolution.files?.size); // This resolution shouldnt be referenced by any file yet
|
||||
if (isExternalModuleNameRelative(name)) {
|
||||
if (!deferWatchingNonRelativeResolution || isExternalModuleNameRelative(name)) {
|
||||
watchFailedLookupLocationOfResolution(resolution);
|
||||
}
|
||||
else {
|
||||
@@ -850,43 +957,48 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
(resolution.files ??= new Set()).add(filePath);
|
||||
}
|
||||
|
||||
function watchFailedLookupLocation(failedLookupLocation: string, setAtRoot: boolean) {
|
||||
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
|
||||
const toWatch = getDirectoryToWatchFailedLookupLocation(
|
||||
failedLookupLocation,
|
||||
failedLookupLocationPath,
|
||||
rootDir,
|
||||
rootPath,
|
||||
rootPathComponents,
|
||||
getCurrentDirectory,
|
||||
);
|
||||
if (toWatch) {
|
||||
const { dir, dirPath, nonRecursive } = toWatch;
|
||||
if (dirPath === rootPath) {
|
||||
Debug.assert(nonRecursive);
|
||||
setAtRoot = true;
|
||||
}
|
||||
else {
|
||||
setDirectoryWatcher(dir, dirPath, nonRecursive);
|
||||
}
|
||||
}
|
||||
return setAtRoot;
|
||||
}
|
||||
|
||||
function watchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
|
||||
Debug.assert(!!resolution.refCount);
|
||||
|
||||
const { failedLookupLocations, affectingLocations } = resolution;
|
||||
if (!failedLookupLocations?.length && !affectingLocations?.length) return;
|
||||
if (failedLookupLocations?.length) resolutionsWithFailedLookups.add(resolution);
|
||||
const { failedLookupLocations, affectingLocations, node10Result } = resolution;
|
||||
if (!failedLookupLocations?.length && !affectingLocations?.length && !node10Result) return;
|
||||
if (failedLookupLocations?.length || node10Result) resolutionsWithFailedLookups.add(resolution);
|
||||
|
||||
let setAtRoot = false;
|
||||
if (failedLookupLocations) {
|
||||
for (const failedLookupLocation of failedLookupLocations) {
|
||||
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
|
||||
const toWatch = getDirectoryToWatchFailedLookupLocation(
|
||||
failedLookupLocation,
|
||||
failedLookupLocationPath,
|
||||
rootDir,
|
||||
rootPath,
|
||||
rootPathComponents,
|
||||
getCurrentDirectory,
|
||||
);
|
||||
if (toWatch) {
|
||||
const { dir, dirPath, nonRecursive } = toWatch;
|
||||
if (dirPath === rootPath) {
|
||||
Debug.assert(nonRecursive);
|
||||
setAtRoot = true;
|
||||
}
|
||||
else {
|
||||
setDirectoryWatcher(dir, dirPath, nonRecursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setAtRoot) {
|
||||
// This is always non recursive
|
||||
setDirectoryWatcher(rootDir, rootPath, /*nonRecursive*/ true); // TODO: GH#18217
|
||||
setAtRoot = watchFailedLookupLocation(failedLookupLocation, setAtRoot);
|
||||
}
|
||||
}
|
||||
watchAffectingLocationsOfResolution(resolution, !failedLookupLocations?.length);
|
||||
if (node10Result) setAtRoot = watchFailedLookupLocation(node10Result, setAtRoot);
|
||||
if (setAtRoot) {
|
||||
// This is always non recursive
|
||||
setDirectoryWatcher(rootDir, rootPath, /*nonRecursive*/ true);
|
||||
}
|
||||
watchAffectingLocationsOfResolution(resolution, !failedLookupLocations?.length && !node10Result);
|
||||
}
|
||||
|
||||
function watchAffectingLocationsOfResolution(resolution: ResolutionWithFailedLookupLocations, addToResolutionsWithOnlyAffectingLocations: boolean) {
|
||||
@@ -974,6 +1086,28 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
}
|
||||
}
|
||||
|
||||
function stopWatchFailedLookupLocation(failedLookupLocation: string, removeAtRoot: boolean) {
|
||||
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
|
||||
const toWatch = getDirectoryToWatchFailedLookupLocation(
|
||||
failedLookupLocation,
|
||||
failedLookupLocationPath,
|
||||
rootDir,
|
||||
rootPath,
|
||||
rootPathComponents,
|
||||
getCurrentDirectory,
|
||||
);
|
||||
if (toWatch) {
|
||||
const { dirPath } = toWatch;
|
||||
if (dirPath === rootPath) {
|
||||
removeAtRoot = true;
|
||||
}
|
||||
else {
|
||||
removeDirectoryWatcher(dirPath);
|
||||
}
|
||||
}
|
||||
return removeAtRoot;
|
||||
}
|
||||
|
||||
function stopWatchFailedLookupLocationOfResolution<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
|
||||
resolution: T,
|
||||
filePath: Path,
|
||||
@@ -991,32 +1125,16 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
if (resolutions?.delete(resolution) && !resolutions.size) resolvedFileToResolution.delete(key);
|
||||
}
|
||||
|
||||
const { failedLookupLocations, affectingLocations } = resolution;
|
||||
const { failedLookupLocations, affectingLocations, node10Result } = resolution;
|
||||
if (resolutionsWithFailedLookups.delete(resolution)) {
|
||||
let removeAtRoot = false;
|
||||
for (const failedLookupLocation of failedLookupLocations!) {
|
||||
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
|
||||
const toWatch = getDirectoryToWatchFailedLookupLocation(
|
||||
failedLookupLocation,
|
||||
failedLookupLocationPath,
|
||||
rootDir,
|
||||
rootPath,
|
||||
rootPathComponents,
|
||||
getCurrentDirectory,
|
||||
);
|
||||
if (toWatch) {
|
||||
const { dirPath } = toWatch;
|
||||
if (dirPath === rootPath) {
|
||||
removeAtRoot = true;
|
||||
}
|
||||
else {
|
||||
removeDirectoryWatcher(dirPath);
|
||||
}
|
||||
if (failedLookupLocations) {
|
||||
for (const failedLookupLocation of failedLookupLocations) {
|
||||
removeAtRoot = stopWatchFailedLookupLocation(failedLookupLocation, removeAtRoot);
|
||||
}
|
||||
}
|
||||
if (removeAtRoot) {
|
||||
removeDirectoryWatcher(rootPath);
|
||||
}
|
||||
if (node10Result) removeAtRoot = stopWatchFailedLookupLocation(node10Result, removeAtRoot);
|
||||
if (removeAtRoot) removeDirectoryWatcher(rootPath);
|
||||
}
|
||||
else if (affectingLocations?.length) {
|
||||
resolutionsWithOnlyAffectingLocations.delete(resolution);
|
||||
@@ -1080,7 +1198,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, getResolvedTypeReferenceDirective);
|
||||
}
|
||||
|
||||
function invalidateResolutions(resolutions: Set<ResolutionWithFailedLookupLocations> | undefined, canInvalidate: (resolution: ResolutionWithFailedLookupLocations) => boolean | undefined) {
|
||||
function invalidateResolutions(resolutions: Set<ResolutionWithFailedLookupLocations> | Map<string, ResolutionWithFailedLookupLocations> | undefined, canInvalidate: (resolution: ResolutionWithFailedLookupLocations) => boolean | undefined) {
|
||||
if (!resolutions) return false;
|
||||
let invalidated = false;
|
||||
resolutions.forEach(resolution => {
|
||||
@@ -1152,14 +1270,33 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
// If the invalidated file is from a node_modules package, invalidate everything else
|
||||
// in the package since we might not get notifications for other files in the package.
|
||||
// This hardens our logic against unreliable file watchers.
|
||||
const packagePath = parseNodeModuleFromPath(fileOrDirectoryPath);
|
||||
const packagePath = parseNodeModuleFromPath(fileOrDirectoryPath, /*isFolder*/ true);
|
||||
if (packagePath) (startsWithPathChecks ||= new Set()).add(packagePath as Path);
|
||||
}
|
||||
}
|
||||
resolutionHost.scheduleInvalidateResolutionsOfFailedLookupLocations();
|
||||
}
|
||||
|
||||
function invalidatePackageJsonMap() {
|
||||
const packageJsonMap = moduleResolutionCache.getPackageJsonInfoCache().getInternalMap();
|
||||
if (packageJsonMap && (failedLookupChecks || startsWithPathChecks || isInDirectoryChecks)) {
|
||||
packageJsonMap.forEach((_value, path) => isInvalidatedFailedLookup(path) ? packageJsonMap.delete(path) : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function invalidateResolutionsOfFailedLookupLocations() {
|
||||
if (allModuleAndTypeResolutionsAreInvalidated) {
|
||||
affectingPathChecksForFile = undefined;
|
||||
invalidatePackageJsonMap();
|
||||
if (failedLookupChecks || startsWithPathChecks || isInDirectoryChecks || affectingPathChecks) {
|
||||
invalidateResolutions(resolvedLibraries, canInvalidateFailedLookupResolution);
|
||||
}
|
||||
failedLookupChecks = undefined;
|
||||
startsWithPathChecks = undefined;
|
||||
isInDirectoryChecks = undefined;
|
||||
affectingPathChecks = undefined;
|
||||
return true;
|
||||
}
|
||||
let invalidated = false;
|
||||
if (affectingPathChecksForFile) {
|
||||
resolutionHost.getCurrentProgram()?.getSourceFiles().forEach(f => {
|
||||
@@ -1176,10 +1313,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
}
|
||||
|
||||
invalidated = invalidateResolutions(resolutionsWithFailedLookups, canInvalidateFailedLookupResolution) || invalidated;
|
||||
const packageJsonMap = moduleResolutionCache.getPackageJsonInfoCache().getInternalMap();
|
||||
if (packageJsonMap && (failedLookupChecks || startsWithPathChecks || isInDirectoryChecks)) {
|
||||
packageJsonMap.forEach((_value, path) => isInvalidatedFailedLookup(path) ? packageJsonMap.delete(path) : undefined);
|
||||
}
|
||||
invalidatePackageJsonMap();
|
||||
failedLookupChecks = undefined;
|
||||
startsWithPathChecks = undefined;
|
||||
isInDirectoryChecks = undefined;
|
||||
@@ -1191,7 +1325,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
|
||||
function canInvalidateFailedLookupResolution(resolution: ResolutionWithFailedLookupLocations) {
|
||||
if (canInvalidatedFailedLookupResolutionWithAffectingLocation(resolution)) return true;
|
||||
if (!failedLookupChecks && !startsWithPathChecks && !isInDirectoryChecks) return false;
|
||||
return resolution.failedLookupLocations?.some(location => isInvalidatedFailedLookup(resolutionHost.toPath(location)));
|
||||
return resolution.failedLookupLocations?.some(location => isInvalidatedFailedLookup(resolutionHost.toPath(location))) ||
|
||||
(!!resolution.node10Result && isInvalidatedFailedLookup(resolutionHost.toPath(resolution.node10Result)));
|
||||
}
|
||||
|
||||
function isInvalidatedFailedLookup(locationPath: Path) {
|
||||
|
||||
+2
-2
@@ -28,6 +28,7 @@ import {
|
||||
matchesExclude,
|
||||
matchFiles,
|
||||
memoize,
|
||||
ModuleImportResult,
|
||||
noop,
|
||||
normalizePath,
|
||||
normalizeSlashes,
|
||||
@@ -35,7 +36,6 @@ import {
|
||||
Path,
|
||||
perfLogger,
|
||||
PollingWatchKind,
|
||||
RequireResult,
|
||||
resolveJSModule,
|
||||
some,
|
||||
startsWith,
|
||||
@@ -1428,7 +1428,7 @@ export interface System {
|
||||
base64decode?(input: string): string;
|
||||
base64encode?(input: string): string;
|
||||
/** @internal */ bufferFrom?(input: string, encoding?: string): Buffer;
|
||||
/** @internal */ require?(baseDir: string, moduleName: string): RequireResult;
|
||||
/** @internal */ require?(baseDir: string, moduleName: string): ModuleImportResult;
|
||||
|
||||
// For testing
|
||||
/** @internal */ now?(): Date;
|
||||
|
||||
@@ -93,6 +93,7 @@ import {
|
||||
isConstructorDeclaration,
|
||||
isDestructuringAssignment,
|
||||
isElementAccessExpression,
|
||||
isExportOrDefaultModifier,
|
||||
isExpression,
|
||||
isExpressionStatement,
|
||||
isForInitializer,
|
||||
@@ -1843,25 +1844,13 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
}
|
||||
|
||||
const modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier);
|
||||
const isExport = hasSyntacticModifier(node, ModifierFlags.Export);
|
||||
const isDefault = hasSyntacticModifier(node, ModifierFlags.Default);
|
||||
let modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier);
|
||||
const heritageClauses = visitNodes(node.heritageClauses, heritageClauseVisitor, isHeritageClause);
|
||||
const { members, prologue } = transformClassMembers(node);
|
||||
const classDecl = factory.updateClassDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
node.name,
|
||||
/*typeParameters*/ undefined,
|
||||
heritageClauses,
|
||||
members
|
||||
);
|
||||
|
||||
const statements: Statement[] = [];
|
||||
if (prologue) {
|
||||
statements.push(factory.createExpressionStatement(prologue));
|
||||
}
|
||||
|
||||
statements.push(classDecl);
|
||||
|
||||
if (pendingClassReferenceAssignment) {
|
||||
getPendingExpressions().unshift(pendingClassReferenceAssignment);
|
||||
}
|
||||
@@ -1884,6 +1873,29 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
}
|
||||
|
||||
if (statements.length > 0 && isExport && isDefault) {
|
||||
modifiers = visitNodes(modifiers, node => isExportOrDefaultModifier(node) ? undefined : node, isModifier);
|
||||
statements.push(factory.createExportAssignment(
|
||||
/*modifiers*/ undefined,
|
||||
/*isExportEquals*/ false,
|
||||
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)
|
||||
));
|
||||
}
|
||||
|
||||
const classDecl = factory.updateClassDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
node.name,
|
||||
/*typeParameters*/ undefined,
|
||||
heritageClauses,
|
||||
members
|
||||
);
|
||||
statements.unshift(classDecl);
|
||||
|
||||
if (prologue) {
|
||||
statements.unshift(factory.createExpressionStatement(prologue));
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
|
||||
@@ -966,13 +966,6 @@ export function transformES2015(context: TransformationContext): (x: SourceFile
|
||||
statements.push(exportStatement);
|
||||
}
|
||||
|
||||
const emitFlags = getEmitFlags(node);
|
||||
if ((emitFlags & EmitFlags.HasEndOfDeclarationMarker) === 0) {
|
||||
// Add a DeclarationMarker as a marker for the end of the declaration
|
||||
statements.push(factory.createEndOfDeclarationMarker(node));
|
||||
setEmitFlags(statement, emitFlags | EmitFlags.HasEndOfDeclarationMarker);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
|
||||
@@ -767,11 +767,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
|
||||
const exitNonUserCodeStatement = factory.createExpressionStatement(exitNonUserCodeExpression);
|
||||
setSourceMapRange(exitNonUserCodeStatement, node.expression);
|
||||
|
||||
const enterNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createTrue());
|
||||
const enterNonUserCodeStatement = factory.createExpressionStatement(enterNonUserCodeExpression);
|
||||
setSourceMapRange(exitNonUserCodeStatement, node.expression);
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const statements: Statement[] = [iteratorValueStatement, exitNonUserCodeStatement];
|
||||
const binding = createForOfBindingStatement(factory, node.initializer, value);
|
||||
statements.push(visitNode(binding, visitor, isStatement));
|
||||
|
||||
@@ -787,28 +783,13 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
const body = setEmitFlags(
|
||||
setTextRange(
|
||||
factory.createBlock(
|
||||
setTextRange(factory.createNodeArray(statements), statementsLocation),
|
||||
/*multiLine*/ true
|
||||
),
|
||||
bodyLocation
|
||||
return setTextRange(
|
||||
factory.createBlock(
|
||||
setTextRange(factory.createNodeArray(statements), statementsLocation),
|
||||
/*multiLine*/ true
|
||||
),
|
||||
EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps
|
||||
bodyLocation
|
||||
);
|
||||
|
||||
return factory.createBlock([
|
||||
iteratorValueStatement,
|
||||
exitNonUserCodeStatement,
|
||||
factory.createTryStatement(
|
||||
body,
|
||||
/*catchClause*/ undefined,
|
||||
factory.createBlock([
|
||||
enterNonUserCodeStatement
|
||||
])
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
function createDownlevelAwait(expression: Expression) {
|
||||
@@ -859,7 +840,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile
|
||||
factory.createAssignment(done, getDone),
|
||||
factory.createLogicalNot(done)
|
||||
]),
|
||||
/*incrementor*/ undefined,
|
||||
/*incrementor*/ factory.createAssignment(nonUserCode, factory.createTrue()),
|
||||
/*statement*/ convertForOfStatementHead(node, getValue, nonUserCode)
|
||||
),
|
||||
/*location*/ node
|
||||
|
||||
@@ -640,7 +640,7 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
|
||||
function transformClassLike(node: ClassLikeDeclaration, className: Expression) {
|
||||
startLexicalEnvironment();
|
||||
|
||||
const classReference = node.name ?? factory.getGeneratedNameForNode(node);
|
||||
const classReference = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ false, /*ignoreAssignedName*/ true);
|
||||
const classInfo = createClassInfo(node);
|
||||
const classDefinitionStatements: Statement[] = [];
|
||||
let leadingBlockStatements: Statement[] | undefined;
|
||||
@@ -1013,6 +1013,8 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
|
||||
factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) :
|
||||
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
const varDecl = factory.createVariableDeclaration(declName, /*exclamationToken*/ undefined, /*type*/ undefined, iife);
|
||||
setOriginalNode(varDecl, node);
|
||||
|
||||
const varDecls = factory.createVariableDeclarationList([varDecl], NodeFlags.Let);
|
||||
const statement = factory.createVariableStatement(modifiers, varDecls);
|
||||
setOriginalNode(statement, node);
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
GetAccessorDeclaration,
|
||||
getAllDecoratorsOfClass,
|
||||
getAllDecoratorsOfClassElement,
|
||||
getEmitFlags,
|
||||
getEmitScriptTarget,
|
||||
getOriginalNodeId,
|
||||
groupBy,
|
||||
@@ -38,13 +37,16 @@ import {
|
||||
isBlock,
|
||||
isCallToHelper,
|
||||
isClassElement,
|
||||
isClassStaticBlockDeclaration,
|
||||
isComputedPropertyName,
|
||||
isDecorator,
|
||||
isExportOrDefaultModifier,
|
||||
isExpression,
|
||||
isGeneratedIdentifier,
|
||||
isHeritageClause,
|
||||
isIdentifier,
|
||||
isModifier,
|
||||
isModifierLike,
|
||||
isParameter,
|
||||
isPrivateIdentifier,
|
||||
isPropertyDeclaration,
|
||||
@@ -53,6 +55,7 @@ import {
|
||||
isStatic,
|
||||
map,
|
||||
MethodDeclaration,
|
||||
Modifier,
|
||||
ModifierFlags,
|
||||
moveRangePastModifiers,
|
||||
Node,
|
||||
@@ -157,12 +160,6 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
transformClassDeclarationWithClassDecorators(node, node.name) :
|
||||
transformClassDeclarationWithoutClassDecorators(node, node.name);
|
||||
|
||||
if (statements.length > 1) {
|
||||
// Add a DeclarationMarker as a marker for the end of the declaration
|
||||
statements.push(factory.createEndOfDeclarationMarker(node));
|
||||
setEmitFlags(statements[0], getEmitFlags(statements[0]) | EmitFlags.HasEndOfDeclarationMarker);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -321,12 +318,16 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
|
||||
const isExport = hasSyntacticModifier(node, ModifierFlags.Export);
|
||||
const isDefault = hasSyntacticModifier(node, ModifierFlags.Default);
|
||||
const modifiers = visitNodes(node.modifiers, node => isExportOrDefaultModifier(node) || isDecorator(node) ? undefined : node, isModifierLike);
|
||||
|
||||
const location = moveRangePastModifiers(node);
|
||||
const classAlias = getClassAliasIfNeeded(node);
|
||||
|
||||
// When we transform to ES5/3 this will be moved inside an IIFE and should reference the name
|
||||
// without any block-scoped variable collision handling
|
||||
const declName = languageVersion <= ScriptTarget.ES2015 ?
|
||||
const declName = languageVersion < ScriptTarget.ES2015 ?
|
||||
factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) :
|
||||
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
|
||||
@@ -339,8 +340,29 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
let decorationStatements: Statement[] | undefined = [];
|
||||
({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members));
|
||||
|
||||
// If we're emitting to ES2022 or later then we need to reassign the class alias before
|
||||
// static initializers are evaluated.
|
||||
const assignClassAliasInStaticBlock =
|
||||
languageVersion >= ScriptTarget.ES2022 &&
|
||||
!!classAlias &&
|
||||
some(members, member =>
|
||||
isPropertyDeclaration(member) && hasSyntacticModifier(member, ModifierFlags.Static) ||
|
||||
isClassStaticBlockDeclaration(member));
|
||||
if (assignClassAliasInStaticBlock) {
|
||||
members = setTextRange(factory.createNodeArray([
|
||||
factory.createClassStaticBlockDeclaration(
|
||||
factory.createBlock([
|
||||
factory.createExpressionStatement(
|
||||
factory.createAssignment(classAlias, factory.createThis())
|
||||
)
|
||||
])
|
||||
),
|
||||
...members
|
||||
]), members);
|
||||
}
|
||||
|
||||
const classExpression = factory.createClassExpression(
|
||||
/*modifiers*/ undefined,
|
||||
modifiers,
|
||||
name && isGeneratedIdentifier(name) ? undefined : name,
|
||||
/*typeParameters*/ undefined,
|
||||
heritageClauses,
|
||||
@@ -351,15 +373,23 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
|
||||
// let ${name} = ${classExpression} where name is either declaredName if the class doesn't contain self-reference
|
||||
// or decoratedClassAlias if the class contain self-reference.
|
||||
const varDecl = factory.createVariableDeclaration(
|
||||
declName,
|
||||
/*exclamationToken*/ undefined,
|
||||
/*type*/ undefined,
|
||||
classAlias && !assignClassAliasInStaticBlock ? factory.createAssignment(classAlias, classExpression) : classExpression
|
||||
);
|
||||
setOriginalNode(varDecl, node);
|
||||
|
||||
let varModifiers: Modifier[] | undefined;
|
||||
if (isExport && !isDefault) {
|
||||
varModifiers = factory.createModifiersFromModifierFlags(ModifierFlags.Export);
|
||||
}
|
||||
|
||||
const statement = factory.createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
varModifiers,
|
||||
factory.createVariableDeclarationList([
|
||||
factory.createVariableDeclaration(
|
||||
declName,
|
||||
/*exclamationToken*/ undefined,
|
||||
/*type*/ undefined,
|
||||
classAlias ? factory.createAssignment(classAlias, classExpression) : classExpression
|
||||
)
|
||||
varDecl
|
||||
], NodeFlags.Let)
|
||||
);
|
||||
setOriginalNode(statement, node);
|
||||
@@ -369,6 +399,15 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
const statements: Statement[] = [statement];
|
||||
addRange(statements, decorationStatements);
|
||||
addConstructorDecorationStatement(statements, node);
|
||||
|
||||
if (isExport && isDefault) {
|
||||
statements.push(factory.createExportAssignment(
|
||||
/*modifiers*/ undefined,
|
||||
/*isExportEquals*/ false,
|
||||
declName
|
||||
));
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
@@ -645,9 +684,9 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S
|
||||
|
||||
// When we transform to ES5/3 this will be moved inside an IIFE and should reference the name
|
||||
// without any block-scoped variable collision handling
|
||||
const localName = languageVersion <= ScriptTarget.ES2015 ?
|
||||
const localName = languageVersion < ScriptTarget.ES2015 ?
|
||||
factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) :
|
||||
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
factory.getDeclarationName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
const decorate = emitHelpers().createDecorateHelper(decoratorExpressions, localName);
|
||||
const expression = factory.createAssignment(localName, classAlias ? factory.createAssignment(classAlias, decorate) : decorate);
|
||||
setEmitFlags(expression, EmitFlags.NoComments);
|
||||
|
||||
@@ -4,22 +4,28 @@ import {
|
||||
addInternalEmitFlags,
|
||||
addRange,
|
||||
append,
|
||||
arrayFrom,
|
||||
ArrowFunction,
|
||||
BinaryExpression,
|
||||
BindingElement,
|
||||
Block,
|
||||
Bundle,
|
||||
CallExpression,
|
||||
CaseBlock,
|
||||
CaseClause,
|
||||
CatchClause,
|
||||
chainBundle,
|
||||
ClassDeclaration,
|
||||
collectExternalModuleInfo,
|
||||
Debug,
|
||||
Declaration,
|
||||
DefaultClause,
|
||||
DestructuringAssignment,
|
||||
DoStatement,
|
||||
EmitFlags,
|
||||
EmitHelper,
|
||||
EmitHint,
|
||||
emptyArray,
|
||||
EndOfDeclarationMarker,
|
||||
ExportAssignment,
|
||||
ExportDeclaration,
|
||||
Expression,
|
||||
@@ -28,6 +34,8 @@ import {
|
||||
firstOrUndefined,
|
||||
flattenDestructuringAssignment,
|
||||
FlattenLevel,
|
||||
ForInStatement,
|
||||
ForOfStatement,
|
||||
ForStatement,
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
@@ -52,6 +60,7 @@ import {
|
||||
hasSyntacticModifier,
|
||||
Identifier,
|
||||
idText,
|
||||
IfStatement,
|
||||
ImportCall,
|
||||
ImportDeclaration,
|
||||
ImportEqualsDeclaration,
|
||||
@@ -62,6 +71,9 @@ import {
|
||||
isArrowFunction,
|
||||
isAssignmentOperator,
|
||||
isBindingPattern,
|
||||
isBlock,
|
||||
isCaseBlock,
|
||||
isCaseOrDefaultClause,
|
||||
isClassElement,
|
||||
isClassExpression,
|
||||
isDeclarationNameOfEnumOrNamespace,
|
||||
@@ -99,9 +111,11 @@ import {
|
||||
isSpreadElement,
|
||||
isStatement,
|
||||
isStringLiteral,
|
||||
isVariableDeclaration,
|
||||
isVariableDeclarationList,
|
||||
LabeledStatement,
|
||||
length,
|
||||
mapDefined,
|
||||
MergeDeclarationMarker,
|
||||
Modifier,
|
||||
ModifierFlags,
|
||||
ModuleKind,
|
||||
@@ -123,22 +137,28 @@ import {
|
||||
setTextRange,
|
||||
ShorthandPropertyAssignment,
|
||||
singleOrMany,
|
||||
some,
|
||||
SourceFile,
|
||||
startOnNewLine,
|
||||
Statement,
|
||||
SwitchStatement,
|
||||
SyntaxKind,
|
||||
TaggedTemplateExpression,
|
||||
TextRange,
|
||||
TransformationContext,
|
||||
TransformFlags,
|
||||
tryGetModuleNameFromFile,
|
||||
TryStatement,
|
||||
VariableDeclaration,
|
||||
VariableDeclarationList,
|
||||
VariableStatement,
|
||||
visitEachChild,
|
||||
visitIterationBody,
|
||||
visitNode,
|
||||
visitNodes,
|
||||
VisitResult,
|
||||
WhileStatement,
|
||||
WithStatement,
|
||||
} from "../../_namespaces/ts";
|
||||
|
||||
/** @internal */
|
||||
@@ -182,7 +202,6 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file.
|
||||
const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
|
||||
let currentSourceFile: SourceFile; // The current file.
|
||||
let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file.
|
||||
@@ -660,6 +679,24 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return visitExportAssignment(node as ExportAssignment);
|
||||
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
return visitFunctionDeclaration(node as FunctionDeclaration);
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return visitClassDeclaration(node as ClassDeclaration);
|
||||
|
||||
default:
|
||||
return topLevelNestedVisitor(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit nested elements at the top-level of a module.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function topLevelNestedVisitor(node: Node): VisitResult<Node | undefined> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
return visitVariableStatement(node as VariableStatement);
|
||||
|
||||
@@ -669,11 +706,50 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return visitClassDeclaration(node as ClassDeclaration);
|
||||
|
||||
case SyntaxKind.MergeDeclarationMarker:
|
||||
return visitMergeDeclarationMarker(node as MergeDeclarationMarker);
|
||||
case SyntaxKind.ForStatement:
|
||||
return visitForStatement(node as ForStatement, /*isTopLevel*/ true);
|
||||
|
||||
case SyntaxKind.EndOfDeclarationMarker:
|
||||
return visitEndOfDeclarationMarker(node as EndOfDeclarationMarker);
|
||||
case SyntaxKind.ForInStatement:
|
||||
return visitForInStatement(node as ForInStatement);
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return visitForOfStatement(node as ForOfStatement);
|
||||
|
||||
case SyntaxKind.DoStatement:
|
||||
return visitDoStatement(node as DoStatement);
|
||||
|
||||
case SyntaxKind.WhileStatement:
|
||||
return visitWhileStatement(node as WhileStatement);
|
||||
|
||||
case SyntaxKind.LabeledStatement:
|
||||
return visitLabeledStatement(node as LabeledStatement);
|
||||
|
||||
case SyntaxKind.WithStatement:
|
||||
return visitWithStatement(node as WithStatement);
|
||||
|
||||
case SyntaxKind.IfStatement:
|
||||
return visitIfStatement(node as IfStatement);
|
||||
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return visitSwitchStatement(node as SwitchStatement);
|
||||
|
||||
case SyntaxKind.CaseBlock:
|
||||
return visitCaseBlock(node as CaseBlock);
|
||||
|
||||
case SyntaxKind.CaseClause:
|
||||
return visitCaseClause(node as CaseClause);
|
||||
|
||||
case SyntaxKind.DefaultClause:
|
||||
return visitDefaultClause(node as DefaultClause);
|
||||
|
||||
case SyntaxKind.TryStatement:
|
||||
return visitTryStatement(node as TryStatement);
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
return visitCatchClause(node as CatchClause);
|
||||
|
||||
case SyntaxKind.Block:
|
||||
return visitBlock(node as Block);
|
||||
|
||||
default:
|
||||
return visitor(node);
|
||||
@@ -689,7 +765,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
return visitForStatement(node as ForStatement);
|
||||
return visitForStatement(node as ForStatement, /*isTopLevel*/ false);
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return visitExpressionStatement(node as ExpressionStatement);
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
@@ -774,16 +850,232 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitForStatement(node: ForStatement) {
|
||||
function visitForStatement(node: ForStatement, isTopLevel: boolean) {
|
||||
if (isTopLevel && node.initializer &&
|
||||
isVariableDeclarationList(node.initializer) &&
|
||||
!(node.initializer.flags & NodeFlags.BlockScoped)) {
|
||||
const exportStatements = appendExportsOfVariableDeclarationList(/*statements*/ undefined, node.initializer, /*isForInOrOfInitializer*/ false);
|
||||
if (exportStatements) {
|
||||
const statements: Statement[] = [];
|
||||
const varDeclList = visitNode(node.initializer, discardedValueVisitor, isVariableDeclarationList);
|
||||
const varStatement = factory.createVariableStatement(/*modifiers*/ undefined, varDeclList);
|
||||
statements.push(varStatement);
|
||||
addRange(statements, exportStatements);
|
||||
|
||||
const condition = visitNode(node.condition, visitor, isExpression);
|
||||
const incrementor = visitNode(node.incrementor, discardedValueVisitor, isExpression);
|
||||
const body = visitIterationBody(node.statement, isTopLevel ? topLevelNestedVisitor : visitor, context);
|
||||
statements.push(factory.updateForStatement(node, /*initializer*/ undefined, condition, incrementor, body));
|
||||
return statements;
|
||||
}
|
||||
}
|
||||
return factory.updateForStatement(
|
||||
node,
|
||||
visitNode(node.initializer, discardedValueVisitor, isForInitializer),
|
||||
visitNode(node.condition, visitor, isExpression),
|
||||
visitNode(node.incrementor, discardedValueVisitor, isExpression),
|
||||
visitIterationBody(node.statement, visitor, context)
|
||||
visitIterationBody(node.statement, isTopLevel ? topLevelNestedVisitor : visitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a ForInStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitForInStatement(node: ForInStatement): VisitResult<Statement> {
|
||||
if (isVariableDeclarationList(node.initializer) && !(node.initializer.flags & NodeFlags.BlockScoped)) {
|
||||
const exportStatements = appendExportsOfVariableDeclarationList(/*statements*/ undefined, node.initializer, /*isForInOrOfInitializer*/ true);
|
||||
if (some(exportStatements)) {
|
||||
const initializer = visitNode(node.initializer, discardedValueVisitor, isForInitializer);
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const body = visitIterationBody(node.statement, topLevelNestedVisitor, context);
|
||||
const mergedBody = isBlock(body) ?
|
||||
factory.updateBlock(body, [...exportStatements, ...body.statements]) :
|
||||
factory.createBlock([...exportStatements, body], /*multiLine*/ true);
|
||||
return factory.updateForInStatement(node, initializer, expression, mergedBody);
|
||||
}
|
||||
}
|
||||
return factory.updateForInStatement(
|
||||
node,
|
||||
visitNode(node.initializer, discardedValueVisitor, isForInitializer),
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a ForOfStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitForOfStatement(node: ForOfStatement): VisitResult<Statement> {
|
||||
if (isVariableDeclarationList(node.initializer) && !(node.initializer.flags & NodeFlags.BlockScoped)) {
|
||||
const exportStatements = appendExportsOfVariableDeclarationList(/*statements*/ undefined, node.initializer, /*isForInOrOfInitializer*/ true);
|
||||
const initializer = visitNode(node.initializer, discardedValueVisitor, isForInitializer);
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
let body = visitIterationBody(node.statement, topLevelNestedVisitor, context);
|
||||
if (some(exportStatements)) {
|
||||
body = isBlock(body) ?
|
||||
factory.updateBlock(body, [...exportStatements, ...body.statements]) :
|
||||
factory.createBlock([...exportStatements, body], /*multiLine*/ true);
|
||||
}
|
||||
return factory.updateForOfStatement(node, node.awaitModifier, initializer, expression, body);
|
||||
}
|
||||
return factory.updateForOfStatement(
|
||||
node,
|
||||
node.awaitModifier,
|
||||
visitNode(node.initializer, discardedValueVisitor, isForInitializer),
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a DoStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitDoStatement(node: DoStatement): DoStatement {
|
||||
return factory.updateDoStatement(
|
||||
node,
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context),
|
||||
visitNode(node.expression, visitor, isExpression)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a WhileStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitWhileStatement(node: WhileStatement): WhileStatement {
|
||||
return factory.updateWhileStatement(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a LabeledStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitLabeledStatement(node: LabeledStatement): LabeledStatement {
|
||||
return factory.updateLabeledStatement(
|
||||
node,
|
||||
node.label,
|
||||
Debug.checkDefined(visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a WithStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitWithStatement(node: WithStatement): WithStatement {
|
||||
return factory.updateWithStatement(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
Debug.checkDefined(visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a IfStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitIfStatement(node: IfStatement): IfStatement {
|
||||
return factory.updateIfStatement(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
Debug.checkDefined(visitNode(node.thenStatement, topLevelNestedVisitor, isStatement, factory.liftToBlock)),
|
||||
visitNode(node.elseStatement, topLevelNestedVisitor, isStatement, factory.liftToBlock)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a SwitchStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitSwitchStatement(node: SwitchStatement): SwitchStatement {
|
||||
return factory.updateSwitchStatement(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
Debug.checkDefined(visitNode(node.caseBlock, topLevelNestedVisitor, isCaseBlock))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a CaseBlock to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitCaseBlock(node: CaseBlock): CaseBlock {
|
||||
return factory.updateCaseBlock(
|
||||
node,
|
||||
visitNodes(node.clauses, topLevelNestedVisitor, isCaseOrDefaultClause)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a CaseClause to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitCaseClause(node: CaseClause): CaseClause {
|
||||
return factory.updateCaseClause(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitNodes(node.statements, topLevelNestedVisitor, isStatement)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a DefaultClause to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitDefaultClause(node: DefaultClause): DefaultClause {
|
||||
return visitEachChild(node, topLevelNestedVisitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a TryStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitTryStatement(node: TryStatement): TryStatement {
|
||||
return visitEachChild(node, topLevelNestedVisitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a CatchClause to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitCatchClause(node: CatchClause): CatchClause {
|
||||
return factory.updateCatchClause(
|
||||
node,
|
||||
node.variableDeclaration,
|
||||
Debug.checkDefined(visitNode(node.block, topLevelNestedVisitor, isBlock))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a Block to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitBlock(node: Block): Block {
|
||||
node = visitEachChild(node, topLevelNestedVisitor, context);
|
||||
return node;
|
||||
}
|
||||
|
||||
function visitExpressionStatement(node: ExpressionStatement) {
|
||||
return factory.updateExpressionStatement(
|
||||
node,
|
||||
@@ -1154,15 +1446,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
);
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfImportDeclaration(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfImportDeclaration(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1245,15 +1529,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfImportEqualsDeclaration(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfImportEqualsDeclaration(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1377,18 +1653,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let statements: Statement[] | undefined;
|
||||
const original = node.original;
|
||||
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, visitor, isExpression), /*location*/ node, /*allowComments*/ true);
|
||||
}
|
||||
else {
|
||||
statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, visitor, isExpression), /*location*/ node, /*allowComments*/ true);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
return createExportStatement(factory.createIdentifier("default"), visitNode(node.expression, visitor, isExpression), /*location*/ node, /*allowComments*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1421,15 +1686,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1461,15 +1718,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1560,15 +1809,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfVariableStatement(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfVariableStatement(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1618,57 +1859,6 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a MergeDeclarationMarker used as a placeholder for the beginning of a merged
|
||||
* and transformed declaration.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitMergeDeclarationMarker(node: MergeDeclarationMarker): VisitResult<Statement> {
|
||||
// For an EnumDeclaration or ModuleDeclaration that merges with a preceeding
|
||||
// declaration we do not emit a leading variable declaration. To preserve the
|
||||
// begin/end semantics of the declararation and to properly handle exports
|
||||
// we wrapped the leading variable declaration in a `MergeDeclarationMarker`.
|
||||
//
|
||||
// To balance the declaration, add the exports of the elided variable
|
||||
// statement.
|
||||
if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) {
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a node has an associated EndOfDeclarationMarker.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function hasAssociatedEndOfDeclarationMarker(node: Node) {
|
||||
return (getEmitFlags(node) & EmitFlags.HasEndOfDeclarationMarker) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a DeclarationMarker used as a placeholder for the end of a transformed
|
||||
* declaration.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitEndOfDeclarationMarker(node: EndOfDeclarationMarker): VisitResult<Statement> {
|
||||
// For some transformations we emit an `EndOfDeclarationMarker` to mark the actual
|
||||
// end of the transformed declaration. We use this marker to emit any deferred exports
|
||||
// of the declaration.
|
||||
const id = getOriginalNodeId(node);
|
||||
const statements = deferredExports[id];
|
||||
if (statements) {
|
||||
delete deferredExports[id];
|
||||
return append(statements, node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the exports of an ImportDeclaration to a statement list, returning the
|
||||
* statement list.
|
||||
@@ -1738,12 +1928,25 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
* @param node The VariableStatement whose exports are to be recorded.
|
||||
*/
|
||||
function appendExportsOfVariableStatement(statements: Statement[] | undefined, node: VariableStatement): Statement[] | undefined {
|
||||
return appendExportsOfVariableDeclarationList(statements, node.declarationList, /*isForInOrOfInitializer*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the exports of a VariableDeclarationList to a statement list, returning the statement
|
||||
* list.
|
||||
*
|
||||
* @param statements A statement list to which the down-level export statements are to be
|
||||
* appended. If `statements` is `undefined`, a new array is allocated if statements are
|
||||
* appended.
|
||||
* @param node The VariableDeclarationList whose exports are to be recorded.
|
||||
*/
|
||||
function appendExportsOfVariableDeclarationList(statements: Statement[] | undefined, node: VariableDeclarationList, isForInOrOfInitializer: boolean): Statement[] | undefined {
|
||||
if (currentModuleInfo.exportEquals) {
|
||||
return statements;
|
||||
}
|
||||
|
||||
for (const decl of node.declarationList.declarations) {
|
||||
statements = appendExportsOfBindingElement(statements, decl);
|
||||
for (const decl of node.declarations) {
|
||||
statements = appendExportsOfBindingElement(statements, decl, isForInOrOfInitializer);
|
||||
}
|
||||
|
||||
return statements;
|
||||
@@ -1758,7 +1961,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
* appended.
|
||||
* @param decl The declaration whose exports are to be recorded.
|
||||
*/
|
||||
function appendExportsOfBindingElement(statements: Statement[] | undefined, decl: VariableDeclaration | BindingElement): Statement[] | undefined {
|
||||
function appendExportsOfBindingElement(statements: Statement[] | undefined, decl: VariableDeclaration | BindingElement, isForInOrOfInitializer: boolean): Statement[] | undefined {
|
||||
if (currentModuleInfo.exportEquals) {
|
||||
return statements;
|
||||
}
|
||||
@@ -1766,11 +1969,11 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
if (isBindingPattern(decl.name)) {
|
||||
for (const element of decl.name.elements) {
|
||||
if (!isOmittedExpression(element)) {
|
||||
statements = appendExportsOfBindingElement(statements, element);
|
||||
statements = appendExportsOfBindingElement(statements, element, isForInOrOfInitializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!isGeneratedIdentifier(decl.name)) {
|
||||
else if (!isGeneratedIdentifier(decl.name) && (!isVariableDeclaration(decl) || decl.initializer || isForInOrOfInitializer)) {
|
||||
statements = appendExportsOfDeclaration(statements, decl);
|
||||
}
|
||||
|
||||
@@ -2140,14 +2343,11 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
//
|
||||
// - We do not substitute generated identifiers for any reason.
|
||||
// - We do not substitute identifiers tagged with the LocalName flag.
|
||||
// - We do not substitute identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only substitute identifiers that are exported at the top level.
|
||||
if (isAssignmentOperator(node.operatorToken.kind)
|
||||
&& isIdentifier(node.left)
|
||||
&& !isGeneratedIdentifier(node.left)
|
||||
&& !isLocalName(node.left)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.left)) {
|
||||
&& !isLocalName(node.left)) {
|
||||
const exportedNames = getExports(node.left);
|
||||
if (exportedNames) {
|
||||
// For each additional export of the declaration, apply an export assignment.
|
||||
@@ -2172,11 +2372,27 @@ export function transformModule(context: TransformationContext): (x: SourceFile
|
||||
*/
|
||||
function getExports(name: Identifier): Identifier[] | undefined {
|
||||
if (!isGeneratedIdentifier(name)) {
|
||||
const valueDeclaration = resolver.getReferencedImportDeclaration(name)
|
||||
|| resolver.getReferencedValueDeclaration(name);
|
||||
if (valueDeclaration) {
|
||||
return currentModuleInfo
|
||||
&& currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)];
|
||||
const importDeclaration = resolver.getReferencedImportDeclaration(name);
|
||||
if (importDeclaration) {
|
||||
return currentModuleInfo?.exportedBindings[getOriginalNodeId(importDeclaration)];
|
||||
}
|
||||
|
||||
// An exported namespace or enum may merge with an ambient declaration, which won't show up in .js emit, so
|
||||
// we analyze all value exports of a symbol.
|
||||
const bindingsSet = new Set<Identifier>();
|
||||
const declarations = resolver.getReferencedValueDeclarations(name);
|
||||
if (declarations) {
|
||||
for (const declaration of declarations) {
|
||||
const bindings = currentModuleInfo?.exportedBindings[getOriginalNodeId(declaration)];
|
||||
if (bindings) {
|
||||
for (const binding of bindings) {
|
||||
bindingsSet.add(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bindingsSet.size) {
|
||||
return arrayFrom(bindingsSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
DoStatement,
|
||||
EmitFlags,
|
||||
EmitHint,
|
||||
EndOfDeclarationMarker,
|
||||
ExportAssignment,
|
||||
ExportDeclaration,
|
||||
Expression,
|
||||
@@ -46,6 +45,7 @@ import {
|
||||
hasSyntacticModifier,
|
||||
Identifier,
|
||||
idText,
|
||||
IfStatement,
|
||||
ImportCall,
|
||||
ImportDeclaration,
|
||||
ImportEqualsDeclaration,
|
||||
@@ -74,7 +74,6 @@ import {
|
||||
isImportSpecifier,
|
||||
isLocalName,
|
||||
isModifierLike,
|
||||
isModuleOrEnumDeclaration,
|
||||
isNamedExports,
|
||||
isObjectLiteralExpression,
|
||||
isOmittedExpression,
|
||||
@@ -88,7 +87,6 @@ import {
|
||||
isVariableDeclarationList,
|
||||
LabeledStatement,
|
||||
map,
|
||||
MergeDeclarationMarker,
|
||||
MetaProperty,
|
||||
ModifierFlags,
|
||||
moveEmitHelpers,
|
||||
@@ -158,7 +156,6 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file.
|
||||
const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file.
|
||||
const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file.
|
||||
const contextObjectMap: Identifier[] = []; // The context object associated with a source file.
|
||||
@@ -737,17 +734,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
if (node.importClause) {
|
||||
hoistVariableDeclaration(getLocalNameForExternalImport(factory, node, currentSourceFile)!); // TODO: GH#18217
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfImportDeclaration(statements, node);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
return singleOrMany(appendExportsOfImportDeclaration(statements, node));
|
||||
}
|
||||
|
||||
function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement | undefined> {
|
||||
@@ -765,17 +752,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
|
||||
let statements: Statement[] | undefined;
|
||||
hoistVariableDeclaration(getLocalNameForExternalImport(factory, node, currentSourceFile)!); // TODO: GH#18217
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfImportEqualsDeclaration(statements, node);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
return singleOrMany(appendExportsOfImportEqualsDeclaration(statements, node));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -790,15 +767,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
}
|
||||
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const original = node.original;
|
||||
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), expression, /*allowComments*/ true);
|
||||
}
|
||||
else {
|
||||
return createExportStatement(factory.createIdentifier("default"), expression, /*allowComments*/ true);
|
||||
}
|
||||
return createExportStatement(factory.createIdentifier("default"), expression, /*allowComments*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -823,15 +792,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
hoistedStatements = append(hoistedStatements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node);
|
||||
}
|
||||
|
||||
hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -869,15 +830,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
)
|
||||
);
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
}
|
||||
|
||||
statements = appendExportsOfHoistedDeclaration(statements, node);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -894,10 +847,9 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
|
||||
let expressions: Expression[] | undefined;
|
||||
const isExportedDeclaration = hasSyntacticModifier(node, ModifierFlags.Export);
|
||||
const isMarkedDeclaration = hasAssociatedEndOfDeclarationMarker(node);
|
||||
for (const variable of node.declarationList.declarations) {
|
||||
if (variable.initializer) {
|
||||
expressions = append(expressions, transformInitializedVariable(variable, isExportedDeclaration && !isMarkedDeclaration));
|
||||
expressions = append(expressions, transformInitializedVariable(variable, isExportedDeclaration));
|
||||
}
|
||||
else {
|
||||
hoistBindingElement(variable);
|
||||
@@ -909,15 +861,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
statements = append(statements, setTextRange(factory.createExpressionStatement(factory.inlineExpressions(expressions)), node));
|
||||
}
|
||||
|
||||
if (isMarkedDeclaration) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration);
|
||||
}
|
||||
else {
|
||||
statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false);
|
||||
}
|
||||
|
||||
statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false);
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
@@ -1008,64 +952,6 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
: preventSubstitution(setTextRange(factory.createAssignment(name, value), location));
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a MergeDeclarationMarker used as a placeholder for the beginning of a merged
|
||||
* and transformed declaration.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitMergeDeclarationMarker(node: MergeDeclarationMarker): VisitResult<Statement> {
|
||||
// For an EnumDeclaration or ModuleDeclaration that merges with a preceeding
|
||||
// declaration we do not emit a leading variable declaration. To preserve the
|
||||
// begin/end semantics of the declararation and to properly handle exports
|
||||
// we wrapped the leading variable declaration in a `MergeDeclarationMarker`.
|
||||
//
|
||||
// To balance the declaration, we defer the exports of the elided variable
|
||||
// statement until we visit this declaration's `EndOfDeclarationMarker`.
|
||||
if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) {
|
||||
const id = getOriginalNodeId(node);
|
||||
const isExportedDeclaration = hasSyntacticModifier(node.original!, ModifierFlags.Export);
|
||||
deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement, isExportedDeclaration);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a node has an associated EndOfDeclarationMarker.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function hasAssociatedEndOfDeclarationMarker(node: Node) {
|
||||
return (getEmitFlags(node) & EmitFlags.HasEndOfDeclarationMarker) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a DeclarationMarker used as a placeholder for the end of a transformed
|
||||
* declaration.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitEndOfDeclarationMarker(node: EndOfDeclarationMarker): VisitResult<Statement> {
|
||||
// For some transformations we emit an `EndOfDeclarationMarker` to mark the actual
|
||||
// end of the transformed declaration. We use this marker to emit any deferred exports
|
||||
// of the declaration.
|
||||
const id = getOriginalNodeId(node);
|
||||
const statements = deferredExports[id];
|
||||
if (statements) {
|
||||
delete deferredExports[id];
|
||||
return append(statements, node);
|
||||
}
|
||||
else {
|
||||
const original = getOriginalNode(node);
|
||||
if (isModuleOrEnumDeclaration(original)) {
|
||||
return append(appendExportsOfDeclaration(statements, original), node);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the exports of an ImportDeclaration to a statement list, returning the
|
||||
* statement list.
|
||||
@@ -1325,6 +1211,9 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
case SyntaxKind.WithStatement:
|
||||
return visitWithStatement(node as WithStatement);
|
||||
|
||||
case SyntaxKind.IfStatement:
|
||||
return visitIfStatement(node as IfStatement);
|
||||
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return visitSwitchStatement(node as SwitchStatement);
|
||||
|
||||
@@ -1346,12 +1235,6 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
case SyntaxKind.Block:
|
||||
return visitBlock(node as Block);
|
||||
|
||||
case SyntaxKind.MergeDeclarationMarker:
|
||||
return visitMergeDeclarationMarker(node as MergeDeclarationMarker);
|
||||
|
||||
case SyntaxKind.EndOfDeclarationMarker:
|
||||
return visitEndOfDeclarationMarker(node as EndOfDeclarationMarker);
|
||||
|
||||
default:
|
||||
return visitor(node);
|
||||
}
|
||||
@@ -1504,6 +1387,20 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a IfStatement to hoist declarations.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitIfStatement(node: IfStatement): VisitResult<Statement> {
|
||||
return factory.updateIfStatement(
|
||||
node,
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
Debug.checkDefined(visitNode(node.thenStatement, topLevelNestedVisitor, isStatement, factory.liftToBlock)),
|
||||
visitNode(node.elseStatement, topLevelNestedVisitor, isStatement, factory.liftToBlock)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the body of a SwitchStatement to hoist declarations.
|
||||
*
|
||||
@@ -1999,14 +1896,11 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
//
|
||||
// - We do not substitute generated identifiers for any reason.
|
||||
// - We do not substitute identifiers tagged with the LocalName flag.
|
||||
// - We do not substitute identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only substitute identifiers that are exported at the top level.
|
||||
if (isAssignmentOperator(node.operatorToken.kind)
|
||||
&& isIdentifier(node.left)
|
||||
&& !isGeneratedIdentifier(node.left)
|
||||
&& !isLocalName(node.left)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.left)) {
|
||||
&& !isLocalName(node.left)) {
|
||||
const exportedNames = getExports(node.left);
|
||||
if (exportedNames) {
|
||||
// For each additional export of the declaration, apply an export assignment.
|
||||
@@ -2036,23 +1930,39 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
|
||||
*/
|
||||
function getExports(name: Identifier) {
|
||||
let exportedNames: Identifier[] | undefined;
|
||||
if (!isGeneratedIdentifier(name)) {
|
||||
const valueDeclaration = resolver.getReferencedImportDeclaration(name)
|
||||
|| resolver.getReferencedValueDeclaration(name);
|
||||
|
||||
if (valueDeclaration) {
|
||||
const exportContainer = resolver.getReferencedExportContainer(name, /*prefixLocals*/ false);
|
||||
if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) {
|
||||
exportedNames = append(exportedNames, factory.getDeclarationName(valueDeclaration));
|
||||
}
|
||||
|
||||
exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]);
|
||||
const valueDeclaration = getReferencedDeclaration(name);
|
||||
if (valueDeclaration) {
|
||||
const exportContainer = resolver.getReferencedExportContainer(name, /*prefixLocals*/ false);
|
||||
if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) {
|
||||
exportedNames = append(exportedNames, factory.getDeclarationName(valueDeclaration));
|
||||
}
|
||||
}
|
||||
|
||||
exportedNames = addRange(exportedNames, moduleInfo?.exportedBindings[getOriginalNodeId(valueDeclaration)]);
|
||||
}
|
||||
return exportedNames;
|
||||
}
|
||||
|
||||
function getReferencedDeclaration(name: Identifier) {
|
||||
if (!isGeneratedIdentifier(name)) {
|
||||
const importDeclaration = resolver.getReferencedImportDeclaration(name);
|
||||
if (importDeclaration) return importDeclaration;
|
||||
|
||||
const valueDeclaration = resolver.getReferencedValueDeclaration(name);
|
||||
if (valueDeclaration && moduleInfo?.exportedBindings[getOriginalNodeId(valueDeclaration)]) return valueDeclaration;
|
||||
|
||||
// An exported namespace or enum may merge with an ambient declaration, which won't show up in
|
||||
// .js emit. When that happens, try to find bindings associated with a non-ambient declaration.
|
||||
const declarations = resolver.getReferencedValueDeclarations(name);
|
||||
if (declarations) {
|
||||
for (const declaration of declarations) {
|
||||
if (declaration !== valueDeclaration && moduleInfo?.exportedBindings[getOriginalNodeId(declaration)]) return declaration;
|
||||
}
|
||||
}
|
||||
|
||||
return valueDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent substitution of a node for this transformer.
|
||||
*
|
||||
|
||||
@@ -846,9 +846,7 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
|
||||
const moveModifiers =
|
||||
promoteToIIFE ||
|
||||
facts & ClassFacts.IsExportOfNamespace ||
|
||||
facts & ClassFacts.HasClassOrConstructorParameterDecorators && legacyDecorators ||
|
||||
facts & ClassFacts.HasStaticInitializedProperties;
|
||||
facts & ClassFacts.IsExportOfNamespace;
|
||||
|
||||
// elide modifiers on the declaration if we are emitting an IIFE or the class is
|
||||
// a namespace export
|
||||
@@ -954,34 +952,28 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
|
||||
if (moveModifiers) {
|
||||
if (facts & ClassFacts.IsExportOfNamespace) {
|
||||
return demarcateMultiStatementExport(
|
||||
return [
|
||||
statement,
|
||||
createExportMemberAssignmentStatement(node));
|
||||
createExportMemberAssignmentStatement(node)
|
||||
];
|
||||
}
|
||||
if (facts & ClassFacts.IsDefaultExternalExport) {
|
||||
return demarcateMultiStatementExport(
|
||||
return [
|
||||
statement,
|
||||
factory.createExportDefault(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)));
|
||||
factory.createExportDefault(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))
|
||||
];
|
||||
}
|
||||
if (facts & ClassFacts.IsNamedExternalExport && !promoteToIIFE) {
|
||||
return demarcateMultiStatementExport(
|
||||
return [
|
||||
statement,
|
||||
factory.createExternalModuleExport(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)));
|
||||
factory.createExternalModuleExport(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
function demarcateMultiStatementExport(declarationStatement: Statement, exportStatement: Statement) {
|
||||
addEmitFlags(declarationStatement, EmitFlags.HasEndOfDeclarationMarker);
|
||||
return [
|
||||
declarationStatement,
|
||||
exportStatement,
|
||||
factory.createEndOfDeclarationMarker(declarationStatement)
|
||||
];
|
||||
}
|
||||
|
||||
function visitClassExpression(node: ClassExpression): Expression {
|
||||
let modifiers = visitNodes(node.modifiers, modifierElidingVisitor, isModifierLike);
|
||||
if (classOrConstructorParameterIsDecorated(legacyDecorators, node)) {
|
||||
@@ -1762,9 +1754,9 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
const containerName = getNamespaceContainerName(node);
|
||||
|
||||
// `exportName` is the expression used within this node's container for any exported references.
|
||||
const exportName = hasSyntacticModifier(node, ModifierFlags.Export)
|
||||
const exportName = isExportOfNamespace(node)
|
||||
? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true)
|
||||
: factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
: factory.getDeclarationName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
|
||||
// x || (x = {})
|
||||
// exports.x || (exports.x = {})
|
||||
@@ -1777,7 +1769,7 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
)
|
||||
);
|
||||
|
||||
if (hasNamespaceQualifiedExportName(node)) {
|
||||
if (isExportOfNamespace(node)) {
|
||||
// `localName` is the expression used within this node's containing scope for any local references.
|
||||
const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
|
||||
@@ -1815,9 +1807,6 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
addEmitFlags(enumStatement, emitFlags);
|
||||
statements.push(enumStatement);
|
||||
|
||||
// Add a DeclarationMarker for the enum to preserve trailing comments and mark
|
||||
// the end of the declaration.
|
||||
statements.push(factory.createEndOfDeclarationMarker(node));
|
||||
return statements;
|
||||
}
|
||||
|
||||
@@ -1916,20 +1905,6 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
return isInstantiatedModule(node, shouldPreserveConstEnums(compilerOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an exported declaration will have a qualified export name (e.g. `f.x`
|
||||
* or `exports.x`).
|
||||
*/
|
||||
function hasNamespaceQualifiedExportName(node: Node) {
|
||||
return isExportOfNamespace(node)
|
||||
|| (isExternalModuleExport(node)
|
||||
&& moduleKind !== ModuleKind.ES2015
|
||||
&& moduleKind !== ModuleKind.ES2020
|
||||
&& moduleKind !== ModuleKind.ES2022
|
||||
&& moduleKind !== ModuleKind.ESNext
|
||||
&& moduleKind !== ModuleKind.System);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records that a declaration was emitted in the current scope, if it was the first
|
||||
* declaration for the provided symbol.
|
||||
@@ -1969,15 +1944,17 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
// Emit a variable statement for the module. We emit top-level enums as a `var`
|
||||
// declaration to avoid static errors in global scripts scripts due to redeclaration.
|
||||
// enums in any other scope are emitted as a `let` declaration.
|
||||
const varDecl = factory.createVariableDeclaration(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true));
|
||||
const varFlags = currentLexicalScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let;
|
||||
const statement = factory.createVariableStatement(
|
||||
visitNodes(node.modifiers, modifierVisitor, isModifier),
|
||||
factory.createVariableDeclarationList([
|
||||
factory.createVariableDeclaration(
|
||||
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)
|
||||
)
|
||||
], currentLexicalScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let)
|
||||
factory.createVariableDeclarationList([varDecl], varFlags)
|
||||
);
|
||||
|
||||
setOriginalNode(varDecl, node);
|
||||
setSyntheticLeadingComments(varDecl, undefined);
|
||||
setSyntheticTrailingComments(varDecl, undefined);
|
||||
|
||||
setOriginalNode(statement, node);
|
||||
|
||||
recordEmittedDeclarationInScope(node);
|
||||
@@ -2009,20 +1986,14 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
// })(m1 || (m1 = {})); // trailing comment module
|
||||
//
|
||||
setCommentRange(statement, node);
|
||||
addEmitFlags(statement, EmitFlags.NoTrailingComments | EmitFlags.HasEndOfDeclarationMarker);
|
||||
addEmitFlags(statement, EmitFlags.NoTrailingComments);
|
||||
statements.push(statement);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// For an EnumDeclaration or ModuleDeclaration that merges with a preceeding
|
||||
// declaration we do not emit a leading variable declaration. To preserve the
|
||||
// begin/end semantics of the declararation and to properly handle exports
|
||||
// we wrap the leading variable declaration in a `MergeDeclarationMarker`.
|
||||
const mergeMarker = factory.createMergeDeclarationMarker(statement);
|
||||
setEmitFlags(mergeMarker, EmitFlags.NoComments | EmitFlags.HasEndOfDeclarationMarker);
|
||||
statements.push(mergeMarker);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For an EnumDeclaration or ModuleDeclaration that merges with a preceeding
|
||||
// declaration we do not emit a leading variable declaration.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2064,9 +2035,9 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
const containerName = getNamespaceContainerName(node);
|
||||
|
||||
// `exportName` is the expression used within this node's container for any exported references.
|
||||
const exportName = hasSyntacticModifier(node, ModifierFlags.Export)
|
||||
const exportName = isExportOfNamespace(node)
|
||||
? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true)
|
||||
: factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
: factory.getDeclarationName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
|
||||
// x || (x = {})
|
||||
// exports.x || (exports.x = {})
|
||||
@@ -2079,7 +2050,7 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
)
|
||||
);
|
||||
|
||||
if (hasNamespaceQualifiedExportName(node)) {
|
||||
if (isExportOfNamespace(node)) {
|
||||
// `localName` is the expression used within this node's containing scope for any local references.
|
||||
const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
|
||||
|
||||
@@ -2116,9 +2087,6 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
addEmitFlags(moduleStatement, emitFlags);
|
||||
statements.push(moduleStatement);
|
||||
|
||||
// Add a DeclarationMarker for the namespace to preserve trailing comments and mark
|
||||
// the end of the declaration.
|
||||
statements.push(factory.createEndOfDeclarationMarker(node));
|
||||
return statements;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
isGeneratedPrivateIdentifier,
|
||||
isIdentifier,
|
||||
isKeyword,
|
||||
isLocalName,
|
||||
isMethodOrAccessor,
|
||||
isNamedExports,
|
||||
isNamedImports,
|
||||
@@ -236,7 +237,7 @@ export function collectExternalModuleInfo(context: TransformationContext, source
|
||||
case SyntaxKind.VariableStatement:
|
||||
if (hasSyntacticModifier(node, ModifierFlags.Export)) {
|
||||
for (const decl of (node as VariableStatement).declarationList.declarations) {
|
||||
exportedNames = collectExportedVariableInfo(decl, uniqueExports, exportedNames);
|
||||
exportedNames = collectExportedVariableInfo(decl, uniqueExports, exportedNames, exportedBindings);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -314,11 +315,11 @@ export function collectExternalModuleInfo(context: TransformationContext, source
|
||||
}
|
||||
}
|
||||
|
||||
function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map<string, boolean>, exportedNames: Identifier[] | undefined) {
|
||||
function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map<string, boolean>, exportedNames: Identifier[] | undefined, exportedBindings: Identifier[][]) {
|
||||
if (isBindingPattern(decl.name)) {
|
||||
for (const element of decl.name.elements) {
|
||||
if (!isOmittedExpression(element)) {
|
||||
exportedNames = collectExportedVariableInfo(element, uniqueExports, exportedNames);
|
||||
exportedNames = collectExportedVariableInfo(element, uniqueExports, exportedNames, exportedBindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,6 +328,9 @@ function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement,
|
||||
if (!uniqueExports.get(text)) {
|
||||
uniqueExports.set(text, true);
|
||||
exportedNames = append(exportedNames, decl.name);
|
||||
if (isLocalName(decl.name)) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), decl.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return exportedNames;
|
||||
|
||||
@@ -99,6 +99,7 @@ import {
|
||||
ReadBuildProgramHost,
|
||||
resolveConfigFileProjectName,
|
||||
ResolvedConfigFileName,
|
||||
resolveLibrary,
|
||||
resolvePath,
|
||||
resolveProjectReferencePath,
|
||||
returnUndefined,
|
||||
@@ -394,6 +395,7 @@ interface SolutionBuilderState<T extends BuilderProgram> extends WatchFactory<Wa
|
||||
readonly compilerHost: CompilerHost & ReadBuildProgramHost;
|
||||
readonly moduleResolutionCache: ModuleResolutionCache | undefined;
|
||||
readonly typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
|
||||
readonly libraryResolutionCache: ModuleResolutionCache | undefined;
|
||||
|
||||
// Mutable state
|
||||
buildOrder: AnyBuildOrder | undefined;
|
||||
@@ -432,6 +434,7 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
|
||||
compilerHost.getParsedCommandLine = fileName => parseConfigFile(state, fileName as ResolvedConfigFileName, toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName));
|
||||
compilerHost.resolveModuleNameLiterals = maybeBind(host, host.resolveModuleNameLiterals);
|
||||
compilerHost.resolveTypeReferenceDirectiveReferences = maybeBind(host, host.resolveTypeReferenceDirectiveReferences);
|
||||
compilerHost.resolveLibrary = maybeBind(host, host.resolveLibrary);
|
||||
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
|
||||
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
|
||||
compilerHost.getModuleResolutionCache = maybeBind(host, host.getModuleResolutionCache);
|
||||
@@ -465,6 +468,17 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
|
||||
createTypeReferenceResolutionLoader,
|
||||
);
|
||||
}
|
||||
let libraryResolutionCache: ModuleResolutionCache | undefined;
|
||||
if (!compilerHost.resolveLibrary) {
|
||||
libraryResolutionCache = createModuleResolutionCache(compilerHost.getCurrentDirectory(), compilerHost.getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
|
||||
compilerHost.resolveLibrary = (libraryName, resolveFrom, options) => resolveLibrary(
|
||||
libraryName,
|
||||
resolveFrom,
|
||||
options,
|
||||
host,
|
||||
libraryResolutionCache,
|
||||
);
|
||||
}
|
||||
compilerHost.getBuildInfo = (fileName, configFilePath) => getBuildInfo(state, fileName, toResolvedConfigFilePath(state, configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined);
|
||||
|
||||
const { watchFile, watchDirectory, writeLog } = createWatchFactory<ResolvedConfigFileName>(hostWithWatch, options);
|
||||
@@ -496,6 +510,7 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
|
||||
compilerHost,
|
||||
moduleResolutionCache,
|
||||
typeReferenceDirectiveResolutionCache,
|
||||
libraryResolutionCache,
|
||||
|
||||
// Mutable state
|
||||
buildOrder: undefined,
|
||||
@@ -747,7 +762,7 @@ function enableCache<T extends BuilderProgram>(state: SolutionBuilderState<T>) {
|
||||
function disableCache<T extends BuilderProgram>(state: SolutionBuilderState<T>) {
|
||||
if (!state.cache) return;
|
||||
|
||||
const { cache, host, compilerHost, extendedConfigCache, moduleResolutionCache, typeReferenceDirectiveResolutionCache } = state;
|
||||
const { cache, host, compilerHost, extendedConfigCache, moduleResolutionCache, typeReferenceDirectiveResolutionCache, libraryResolutionCache } = state;
|
||||
|
||||
host.readFile = cache.originalReadFile;
|
||||
host.fileExists = cache.originalFileExists;
|
||||
@@ -759,6 +774,7 @@ function disableCache<T extends BuilderProgram>(state: SolutionBuilderState<T>)
|
||||
extendedConfigCache.clear();
|
||||
moduleResolutionCache?.clear();
|
||||
typeReferenceDirectiveResolutionCache?.clear();
|
||||
libraryResolutionCache?.clear();
|
||||
state.cache = undefined;
|
||||
}
|
||||
|
||||
|
||||
+56
-36
@@ -451,8 +451,6 @@ export const enum SyntaxKind {
|
||||
NotEmittedStatement,
|
||||
PartiallyEmittedExpression,
|
||||
CommaListExpression,
|
||||
MergeDeclarationMarker,
|
||||
EndOfDeclarationMarker,
|
||||
SyntheticReferenceExpression,
|
||||
|
||||
// Enum value count
|
||||
@@ -1221,6 +1219,7 @@ export type HasJSDoc =
|
||||
| PropertyDeclaration
|
||||
| PropertySignature
|
||||
| ReturnStatement
|
||||
| SemicolonClassElement
|
||||
| ShorthandPropertyAssignment
|
||||
| SpreadAssignment
|
||||
| SwitchStatement
|
||||
@@ -1723,11 +1722,9 @@ export type PropertyName = Identifier | StringLiteral | NumericLiteral | Compute
|
||||
export type MemberName = Identifier | PrivateIdentifier;
|
||||
|
||||
export type DeclarationName =
|
||||
| Identifier
|
||||
| PrivateIdentifier
|
||||
| PropertyName
|
||||
| JsxAttributeName
|
||||
| StringLiteralLike
|
||||
| NumericLiteral
|
||||
| ComputedPropertyName
|
||||
| ElementAccessExpression
|
||||
| BindingPattern
|
||||
| EntityNameExpression;
|
||||
@@ -2103,7 +2100,7 @@ export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, Cla
|
||||
}
|
||||
|
||||
/** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements. */
|
||||
export interface SemicolonClassElement extends ClassElement {
|
||||
export interface SemicolonClassElement extends ClassElement, JSDocContainer {
|
||||
readonly kind: SyntaxKind.SemicolonClassElement;
|
||||
readonly parent: ClassLikeDeclaration;
|
||||
}
|
||||
@@ -2333,7 +2330,7 @@ export interface LiteralTypeNode extends TypeNode {
|
||||
|
||||
export interface StringLiteral extends LiteralExpression, Declaration {
|
||||
readonly kind: SyntaxKind.StringLiteral;
|
||||
/** @internal */ readonly textSourceNode?: Identifier | StringLiteralLike | NumericLiteral | PrivateIdentifier; // Allows a StringLiteral to get its text from another node (used by transforms).
|
||||
/** @internal */ readonly textSourceNode?: Identifier | StringLiteralLike | NumericLiteral | PrivateIdentifier | JsxNamespacedName; // Allows a StringLiteral to get its text from another node (used by transforms).
|
||||
/**
|
||||
* Note: this is only set when synthesizing a node, not during parsing.
|
||||
*
|
||||
@@ -2343,7 +2340,7 @@ export interface StringLiteral extends LiteralExpression, Declaration {
|
||||
}
|
||||
|
||||
export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral;
|
||||
export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral;
|
||||
export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral | JsxNamespacedName;
|
||||
|
||||
export interface TemplateLiteralTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.TemplateLiteralType,
|
||||
@@ -3192,7 +3189,7 @@ export type JsxTagNameExpression =
|
||||
;
|
||||
|
||||
export interface JsxTagNamePropertyAccess extends PropertyAccessExpression {
|
||||
readonly expression: JsxTagNameExpression;
|
||||
readonly expression: Identifier | ThisExpression | JsxTagNamePropertyAccess;
|
||||
}
|
||||
|
||||
export interface JsxAttributes extends PrimaryExpression, Declaration {
|
||||
@@ -3201,7 +3198,7 @@ export interface JsxAttributes extends PrimaryExpression, Declaration {
|
||||
readonly parent: JsxOpeningLikeElement;
|
||||
}
|
||||
|
||||
export interface JsxNamespacedName extends PrimaryExpression {
|
||||
export interface JsxNamespacedName extends Node {
|
||||
readonly kind: SyntaxKind.JsxNamespacedName;
|
||||
readonly name: Identifier;
|
||||
readonly namespace: Identifier;
|
||||
@@ -3303,15 +3300,6 @@ export interface NotEmittedStatement extends Statement {
|
||||
readonly kind: SyntaxKind.NotEmittedStatement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the end of transformed declaration to properly emit exports.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface EndOfDeclarationMarker extends Statement {
|
||||
readonly kind: SyntaxKind.EndOfDeclarationMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of comma-separated expressions. This node is only created by transformations.
|
||||
*/
|
||||
@@ -3320,14 +3308,6 @@ export interface CommaListExpression extends Expression {
|
||||
readonly elements: NodeArray<Expression>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the beginning of a merged transformed declaration.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface MergeDeclarationMarker extends Statement {
|
||||
readonly kind: SyntaxKind.MergeDeclarationMarker;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface SyntheticReferenceExpression extends LeftHandSideExpression {
|
||||
@@ -4239,7 +4219,6 @@ export interface SourceFileLike {
|
||||
getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
export interface RedirectInfo {
|
||||
/** Source file this redirects to. */
|
||||
@@ -4725,6 +4704,12 @@ export const enum EmitOnly{
|
||||
Js,
|
||||
Dts,
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface LibResolution<T extends ResolvedModuleWithFailedLookupLocations = ResolvedModuleWithFailedLookupLocations> {
|
||||
resolution: T;
|
||||
actual: string;
|
||||
}
|
||||
export interface Program extends ScriptReferenceHost {
|
||||
getCurrentDirectory(): string;
|
||||
/**
|
||||
@@ -4825,6 +4810,12 @@ export interface Program extends ScriptReferenceHost {
|
||||
* @internal
|
||||
*/
|
||||
readonly usesUriStyleNodeCoreModules: boolean;
|
||||
/**
|
||||
* Map from libFileName to actual resolved location of the lib
|
||||
* @internal
|
||||
*/
|
||||
resolvedLibReferences: Map<string, LibResolution> | undefined;
|
||||
/** @internal */ getCurrentPackagesMap(): Map<string, boolean> | undefined;
|
||||
/**
|
||||
* Is the file emitted file
|
||||
*
|
||||
@@ -4960,6 +4951,9 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
|
||||
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||
|
||||
readonly redirectTargetsMap: RedirectTargetsMap;
|
||||
|
||||
typesPackageExists(packageName: string): boolean;
|
||||
packageBundlesTypes(packageName: string): boolean;
|
||||
}
|
||||
|
||||
export interface TypeChecker {
|
||||
@@ -5702,6 +5696,7 @@ export interface EmitResolver {
|
||||
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
|
||||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
|
||||
getReferencedValueDeclaration(reference: Identifier): Declaration | undefined;
|
||||
getReferencedValueDeclarations(reference: Identifier): Declaration[] | undefined;
|
||||
getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind;
|
||||
isOptionalParameter(node: ParameterDeclaration): boolean;
|
||||
moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean;
|
||||
@@ -6065,6 +6060,7 @@ export interface NodeLinks {
|
||||
spreadIndices?: { first: number | undefined, last: number | undefined }; // Indices of first and last spread elements in array literal
|
||||
parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined".
|
||||
fakeScopeForSignatureDeclaration?: boolean; // True if this is a fake scope injected into an enclosing declaration chain.
|
||||
assertionExpressionType?: Type; // Cached type of the expression of a type assertion
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -6932,6 +6928,16 @@ export interface DiagnosticMessage {
|
||||
elidedInCompatabilityPyramid?: boolean;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface RepopulateModuleNotFoundDiagnosticChain {
|
||||
moduleReference: string;
|
||||
mode: ResolutionMode;
|
||||
packageName: string | undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type RepopulateDiagnosticChainInfo = RepopulateModuleNotFoundDiagnosticChain;
|
||||
|
||||
/**
|
||||
* A linked list of formatted diagnostic messages to be used as part of a multiline message.
|
||||
* It is built from the bottom up, leaving the head to be the "main" diagnostic.
|
||||
@@ -6943,6 +6949,8 @@ export interface DiagnosticMessageChain {
|
||||
category: DiagnosticCategory;
|
||||
code: number;
|
||||
next?: DiagnosticMessageChain[];
|
||||
/** @internal */
|
||||
repopulateInfo?: () => RepopulateDiagnosticChainInfo;
|
||||
}
|
||||
|
||||
export interface Diagnostic extends DiagnosticRelatedInformation {
|
||||
@@ -7360,7 +7368,7 @@ export interface ConfigFileSpecs {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type RequireResult<T = {}> =
|
||||
export type ModuleImportResult<T = {}> =
|
||||
| { module: T, modulePath?: string, error: undefined }
|
||||
| { module: undefined, modulePath?: undefined, error: { stack?: string, message?: string } };
|
||||
|
||||
@@ -7741,6 +7749,8 @@ export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
|
||||
/** @internal */
|
||||
export type HasInvalidatedResolutions = (sourceFile: Path) => boolean;
|
||||
/** @internal */
|
||||
export type HasInvalidatedLibResolutions = (libFileName: string) => boolean;
|
||||
/** @internal */
|
||||
export type HasChangedAutomaticTypeDirectiveNames = () => boolean;
|
||||
|
||||
export interface CompilerHost extends ModuleResolutionHost {
|
||||
@@ -7791,6 +7801,18 @@ export interface CompilerHost extends ModuleResolutionHost {
|
||||
containingSourceFile: SourceFile | undefined,
|
||||
reusedNames: readonly T[] | undefined
|
||||
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
|
||||
/** @internal */
|
||||
resolveLibrary?(
|
||||
libraryName: string,
|
||||
resolveFrom: string,
|
||||
options: CompilerOptions,
|
||||
libFileName: string,
|
||||
): ResolvedModuleWithFailedLookupLocations;
|
||||
/**
|
||||
* If provided along with custom resolveLibrary, used to determine if we should redo library resolutions
|
||||
* @internal
|
||||
*/
|
||||
hasInvalidatedLibResolutions?(libFileName: string): boolean;
|
||||
getEnvironmentVariable?(name: string): string | undefined;
|
||||
/** @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
|
||||
/** @internal */ onReleaseParsedCommandLine?(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, optionOptions: CompilerOptions): void;
|
||||
@@ -7995,9 +8017,8 @@ export const enum EmitFlags {
|
||||
ReuseTempVariableScope = 1 << 20, // Reuse the existing temp variable scope during emit.
|
||||
CustomPrologue = 1 << 21, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
|
||||
NoHoisting = 1 << 22, // Do not hoist this declaration in --module system
|
||||
HasEndOfDeclarationMarker = 1 << 23, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
|
||||
Iterator = 1 << 24, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
|
||||
NoAsciiEscaping = 1 << 25, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions.
|
||||
Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
|
||||
NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions.
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -8799,8 +8820,6 @@ export interface NodeFactory {
|
||||
//
|
||||
|
||||
createNotEmittedStatement(original: Node): NotEmittedStatement;
|
||||
/** @internal */ createEndOfDeclarationMarker(original: Node): EndOfDeclarationMarker;
|
||||
/** @internal */ createMergeDeclarationMarker(original: Node): MergeDeclarationMarker;
|
||||
createPartiallyEmittedExpression(expression: Expression, original?: Node): PartiallyEmittedExpression;
|
||||
updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression;
|
||||
/** @internal */ createSyntheticReferenceExpression(expression: Expression, thisArg: Expression): SyntheticReferenceExpression;
|
||||
@@ -8920,10 +8939,11 @@ export interface NodeFactory {
|
||||
* @param node The declaration.
|
||||
* @param allowComments A value indicating whether comments may be emitted for the name.
|
||||
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
|
||||
* @param ignoreAssignedName Indicates that the assigned name of a declaration shouldn't be considered.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
getLocalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean): Identifier;
|
||||
getLocalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean, ignoreAssignedName?: boolean): Identifier;
|
||||
/**
|
||||
* Gets the export name of a declaration. This is primarily used for declarations that can be
|
||||
* referred to by name in the declaration's immediate scope (classes, enums, namespaces). An
|
||||
|
||||
+74
-11
@@ -293,6 +293,7 @@ import {
|
||||
isJSDocTypeTag,
|
||||
isJsxChild,
|
||||
isJsxFragment,
|
||||
isJsxNamespacedName,
|
||||
isJsxOpeningLikeElement,
|
||||
isJsxText,
|
||||
isLeftHandSideExpression,
|
||||
@@ -378,6 +379,7 @@ import {
|
||||
LiteralLikeNode,
|
||||
LogicalOperator,
|
||||
LogicalOrCoalescingAssignmentOperator,
|
||||
mangleScopedPackageName,
|
||||
map,
|
||||
mapDefined,
|
||||
MapLike,
|
||||
@@ -525,6 +527,7 @@ import {
|
||||
TypeAliasDeclaration,
|
||||
TypeAssertion,
|
||||
TypeChecker,
|
||||
TypeCheckerHost,
|
||||
TypeElement,
|
||||
TypeFlags,
|
||||
TypeLiteralNode,
|
||||
@@ -780,7 +783,37 @@ export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleWithFaile
|
||||
oldResolution.resolvedModule.extension === newResolution.resolvedModule.extension &&
|
||||
oldResolution.resolvedModule.resolvedFileName === newResolution.resolvedModule.resolvedFileName &&
|
||||
oldResolution.resolvedModule.originalPath === newResolution.resolvedModule.originalPath &&
|
||||
packageIdIsEqual(oldResolution.resolvedModule.packageId, newResolution.resolvedModule.packageId);
|
||||
packageIdIsEqual(oldResolution.resolvedModule.packageId, newResolution.resolvedModule.packageId) &&
|
||||
oldResolution.node10Result === newResolution.node10Result;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createModuleNotFoundChain(sourceFile: SourceFile, host: TypeCheckerHost, moduleReference: string, mode: ResolutionMode, packageName: string) {
|
||||
const node10Result = sourceFile.resolvedModules?.get(moduleReference, mode)?.node10Result;
|
||||
const result = node10Result
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings,
|
||||
node10Result,
|
||||
node10Result.indexOf(nodeModulesPathPart + "@types/") > -1 ? `@types/${mangleScopedPackageName(packageName)}` : packageName)
|
||||
: host.typesPackageExists(packageName)
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1,
|
||||
packageName, mangleScopedPackageName(packageName))
|
||||
: host.packageBundlesTypes(packageName)
|
||||
? chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1,
|
||||
packageName,
|
||||
moduleReference)
|
||||
: chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
|
||||
moduleReference,
|
||||
mangleScopedPackageName(packageName));
|
||||
if (result) result.repopulateInfo = () => ({ moduleReference, mode, packageName: packageName === moduleReference ? undefined : packageName });
|
||||
return result;
|
||||
}
|
||||
|
||||
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
|
||||
@@ -1824,7 +1857,7 @@ function isCommonJSContainingModuleKind(kind: ModuleKind) {
|
||||
|
||||
/** @internal */
|
||||
export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) {
|
||||
return isExternalModule(node) || getIsolatedModules(compilerOptions) || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator);
|
||||
return isExternalModule(node) || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2024,7 +2057,7 @@ export function isComputedNonLiteralName(name: PropertyName): boolean {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String | undefined {
|
||||
export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral | JsxAttributeName): __String | undefined {
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.PrivateIdentifier:
|
||||
@@ -2036,13 +2069,15 @@ export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemp
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text);
|
||||
return undefined;
|
||||
case SyntaxKind.JsxNamespacedName:
|
||||
return getEscapedTextOfJsxNamespacedName(name);
|
||||
default:
|
||||
return Debug.assertNever(name);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String {
|
||||
export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral | JsxAttributeName): __String {
|
||||
return Debug.checkDefined(tryGetTextOfPropertyName(name));
|
||||
}
|
||||
|
||||
@@ -3074,7 +3109,7 @@ export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNam
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getInvokedExpression(node: CallLikeExpression): Expression {
|
||||
export function getInvokedExpression(node: CallLikeExpression): Expression | JsxTagNameExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return node.tag;
|
||||
@@ -4180,6 +4215,7 @@ export function canHaveJSDoc(node: Node): node is HasJSDoc {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.SemicolonClassElement:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
@@ -4198,7 +4234,29 @@ export function canHaveJSDoc(node: Node): node is HasJSDoc {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/**
|
||||
* This function checks multiple locations for JSDoc comments that apply to a host node.
|
||||
* At each location, the whole comment may apply to the node, or only a specific tag in
|
||||
* the comment. In the first case, location adds the entire {@link JSDoc} object. In the
|
||||
* second case, it adds the applicable {@link JSDocTag}.
|
||||
*
|
||||
* For example, a JSDoc comment before a parameter adds the entire {@link JSDoc}. But a
|
||||
* `@param` tag on the parent function only adds the {@link JSDocTag} for the `@param`.
|
||||
*
|
||||
* ```ts
|
||||
* /** JSDoc will be returned for `a` *\/
|
||||
* const a = 0
|
||||
* /**
|
||||
* * Entire JSDoc will be returned for `b`
|
||||
* * @param c JSDocTag will be returned for `c`
|
||||
* *\/
|
||||
* function b(/** JSDoc will be returned for `c` *\/ c) {}
|
||||
* ```
|
||||
*/
|
||||
export function getJSDocCommentsAndTags(hostNode: Node): readonly (JSDoc | JSDocTag)[];
|
||||
/** @internal separate signature so that stripInternal can remove noCache from the public API */
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): readonly (JSDoc | JSDocTag)[];
|
||||
export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): readonly (JSDoc | JSDocTag)[] {
|
||||
let result: (JSDoc | JSDocTag)[] | undefined;
|
||||
// Pull parameter comments from declaring function as well
|
||||
@@ -4931,7 +4989,7 @@ export function isDynamicName(name: DeclarationName): boolean {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getPropertyNameForPropertyNameNode(name: PropertyName): __String | undefined {
|
||||
export function getPropertyNameForPropertyNameNode(name: PropertyName | JsxAttributeName): __String | undefined {
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.PrivateIdentifier:
|
||||
@@ -4951,6 +5009,8 @@ export function getPropertyNameForPropertyNameNode(name: PropertyName): __String
|
||||
return nameExpression.operand.text as __String;
|
||||
}
|
||||
return undefined;
|
||||
case SyntaxKind.JsxNamespacedName:
|
||||
return getEscapedTextOfJsxNamespacedName(name);
|
||||
default:
|
||||
return Debug.assertNever(name);
|
||||
}
|
||||
@@ -4970,12 +5030,12 @@ export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral {
|
||||
}
|
||||
/** @internal */
|
||||
export function getTextOfIdentifierOrLiteral(node: PropertyNameLiteral | PrivateIdentifier): string {
|
||||
return isMemberName(node) ? idText(node) : node.text;
|
||||
return isMemberName(node) ? idText(node) : isJsxNamespacedName(node) ? getTextOfJsxNamespacedName(node) : node.text;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getEscapedTextOfIdentifierOrLiteral(node: PropertyNameLiteral): __String {
|
||||
return isMemberName(node) ? node.escapedText : escapeLeadingUnderscores(node.text);
|
||||
return isMemberName(node) ? node.escapedText : isJsxNamespacedName(node) ? getEscapedTextOfJsxNamespacedName(node) : escapeLeadingUnderscores(node.text);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -7002,7 +7062,7 @@ export function isPropertyAccessEntityNameExpression(node: Node): node is Proper
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined {
|
||||
export function tryGetPropertyAccessOrIdentifierToString(expr: Expression | JsxTagNameExpression): string | undefined {
|
||||
if (isPropertyAccessExpression(expr)) {
|
||||
const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression);
|
||||
if (baseStr !== undefined) {
|
||||
@@ -7018,6 +7078,9 @@ export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): stri
|
||||
else if (isIdentifier(expr)) {
|
||||
return unescapeLeadingUnderscores(expr.escapedText);
|
||||
}
|
||||
else if (isJsxNamespacedName(expr)) {
|
||||
return getTextOfJsxNamespacedName(expr);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -9100,7 +9163,7 @@ export const supportedTSExtensions: readonly Extension[][] = [[Extension.Ts, Ext
|
||||
export const supportedTSExtensionsFlat: readonly Extension[] = flatten(supportedTSExtensions);
|
||||
const supportedTSExtensionsWithJson: readonly Extension[][] = [...supportedTSExtensions, [Extension.Json]];
|
||||
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
|
||||
const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx, Extension.Cts, Extension.Mts];
|
||||
const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx];
|
||||
/** @internal */
|
||||
export const supportedJSExtensions: readonly Extension[][] = [[Extension.Js, Extension.Jsx], [Extension.Mjs], [Extension.Cjs]];
|
||||
/** @internal */
|
||||
|
||||
@@ -1941,7 +1941,6 @@ function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean {
|
||||
case SyntaxKind.JsxElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxFragment:
|
||||
case SyntaxKind.JsxNamespacedName:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
@@ -2334,9 +2333,7 @@ function isStatementKindButNotDeclarationKind(kind: SyntaxKind) {
|
||||
|| kind === SyntaxKind.VariableStatement
|
||||
|| kind === SyntaxKind.WhileStatement
|
||||
|| kind === SyntaxKind.WithStatement
|
||||
|| kind === SyntaxKind.NotEmittedStatement
|
||||
|| kind === SyntaxKind.EndOfDeclarationMarker
|
||||
|| kind === SyntaxKind.MergeDeclarationMarker;
|
||||
|| kind === SyntaxKind.NotEmittedStatement;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -692,6 +692,8 @@ export const WatchType: WatchTypeRegistry = {
|
||||
NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root",
|
||||
MissingGeneratedFile: "Missing generated file",
|
||||
NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation",
|
||||
TypingInstallerLocationFile: "File location for typing installer",
|
||||
TypingInstallerLocationDirectory: "Directory location for typing installer",
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
@@ -717,6 +719,8 @@ export interface WatchTypeRegistry {
|
||||
NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root",
|
||||
MissingGeneratedFile: "Missing generated file",
|
||||
NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation",
|
||||
TypingInstallerLocationFile: "File location for typing installer",
|
||||
TypingInstallerLocationDirectory: "Directory location for typing installer",
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
getParsedCommandLineOfConfigFile,
|
||||
getSourceFileVersionAsHashFromText,
|
||||
getTsBuildInfoEmitOutputFilePath,
|
||||
HasInvalidatedLibResolutions,
|
||||
HasInvalidatedResolutions,
|
||||
isArray,
|
||||
isIgnoredFileFromWildCardWatching,
|
||||
@@ -229,6 +230,19 @@ export interface ProgramHost<T extends BuilderProgram> {
|
||||
containingSourceFile: SourceFile | undefined,
|
||||
reusedNames: readonly T[] | undefined
|
||||
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
|
||||
/** @internal */
|
||||
resolveLibrary?(
|
||||
libraryName: string,
|
||||
resolveFrom: string,
|
||||
options: CompilerOptions,
|
||||
libFileName: string,
|
||||
): ResolvedModuleWithFailedLookupLocations;
|
||||
/**
|
||||
* If provided along with custom resolveLibrary, used to determine if we should redo library resolutions
|
||||
* @internal
|
||||
*/
|
||||
hasInvalidatedLibResolutions?(libFileName: string): boolean;
|
||||
|
||||
/** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */
|
||||
hasInvalidatedResolutions?(filePath: Path): boolean;
|
||||
/**
|
||||
@@ -503,6 +517,9 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
if (!compilerHost.resolveTypeReferenceDirectiveReferences && !compilerHost.resolveTypeReferenceDirectives) {
|
||||
compilerHost.resolveTypeReferenceDirectiveReferences = resolutionCache.resolveTypeReferenceDirectiveReferences.bind(resolutionCache);
|
||||
}
|
||||
compilerHost.resolveLibrary = !host.resolveLibrary ?
|
||||
resolutionCache.resolveLibrary.bind(resolutionCache) :
|
||||
host.resolveLibrary.bind(host);
|
||||
compilerHost.getModuleResolutionCache = host.resolveModuleNameLiterals || host.resolveModuleNames ?
|
||||
maybeBind(host, host.getModuleResolutionCache) :
|
||||
(() => resolutionCache.getModuleResolutionCache());
|
||||
@@ -512,6 +529,9 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
const customHasInvalidatedResolutions = userProvidedResolution ?
|
||||
maybeBind(host, host.hasInvalidatedResolutions) || returnTrue :
|
||||
returnFalse;
|
||||
const customHasInvalidLibResolutions = host.resolveLibrary ?
|
||||
maybeBind(host, host.hasInvalidatedLibResolutions) || returnTrue :
|
||||
returnFalse;
|
||||
|
||||
builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T;
|
||||
synchronizeProgram();
|
||||
@@ -584,16 +604,17 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
if (hasChangedCompilerOptions) {
|
||||
newLine = updateNewLine();
|
||||
if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) {
|
||||
resolutionCache.clear();
|
||||
debugger;
|
||||
resolutionCache.onChangesAffectModuleResolution();
|
||||
}
|
||||
}
|
||||
|
||||
const hasInvalidatedResolutions = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions);
|
||||
const { hasInvalidatedResolutions, hasInvalidatedLibResolutions } = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions, customHasInvalidLibResolutions);
|
||||
const {
|
||||
originalReadFile, originalFileExists, originalDirectoryExists,
|
||||
originalCreateDirectory, originalWriteFile, readFileWithCache
|
||||
} = changeCompilerHostLikeToUseCache(compilerHost, toPath);
|
||||
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
|
||||
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasInvalidatedLibResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
|
||||
if (hasChangedConfigFileParsingErrors) {
|
||||
if (reportFileChangeDetectedOnCreateProgram) {
|
||||
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation);
|
||||
@@ -606,7 +627,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
if (reportFileChangeDetectedOnCreateProgram) {
|
||||
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation);
|
||||
}
|
||||
createNewProgram(hasInvalidatedResolutions);
|
||||
createNewProgram(hasInvalidatedResolutions, hasInvalidatedLibResolutions);
|
||||
}
|
||||
|
||||
reportFileChangeDetectedOnCreateProgram = false;
|
||||
@@ -623,7 +644,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
return builderProgram;
|
||||
}
|
||||
|
||||
function createNewProgram(hasInvalidatedResolutions: HasInvalidatedResolutions) {
|
||||
function createNewProgram(hasInvalidatedResolutions: HasInvalidatedResolutions, hasInvalidatedLibResolutions: HasInvalidatedLibResolutions) {
|
||||
// Compile the program
|
||||
writeLog("CreatingProgramWith::");
|
||||
writeLog(` roots: ${JSON.stringify(rootFileNames)}`);
|
||||
@@ -635,6 +656,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
|
||||
hasChangedConfigFileParsingErrors = false;
|
||||
resolutionCache.startCachingPerDirectoryResolution();
|
||||
compilerHost.hasInvalidatedResolutions = hasInvalidatedResolutions;
|
||||
compilerHost.hasInvalidatedLibResolutions = hasInvalidatedLibResolutions;
|
||||
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
|
||||
const oldProgram = getCurrentProgram();
|
||||
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences);
|
||||
|
||||
+14
-4
@@ -568,15 +568,17 @@ export class SessionClient implements LanguageService {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] {
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences | boolean | undefined): RenameLocation[] {
|
||||
if (!this.lastRenameEntry ||
|
||||
this.lastRenameEntry.inputs.fileName !== fileName ||
|
||||
this.lastRenameEntry.inputs.position !== position ||
|
||||
this.lastRenameEntry.inputs.findInStrings !== findInStrings ||
|
||||
this.lastRenameEntry.inputs.findInComments !== findInComments) {
|
||||
if (providePrefixAndSuffixTextForRename !== undefined) {
|
||||
const providePrefixAndSuffixTextForRename = typeof preferences === "boolean" ? preferences : preferences?.providePrefixAndSuffixTextForRename;
|
||||
const quotePreference = typeof preferences === "boolean" ? undefined : preferences?.quotePreference;
|
||||
if (providePrefixAndSuffixTextForRename !== undefined || quotePreference !== undefined) {
|
||||
// User preferences have to be set through the `Configure` command
|
||||
this.configure({ providePrefixAndSuffixTextForRename });
|
||||
this.configure({ providePrefixAndSuffixTextForRename, quotePreference });
|
||||
// Options argument is not used, so don't pass in options
|
||||
this.getRenameInfo(fileName, position, /*preferences*/{}, findInStrings, findInComments);
|
||||
// Restore previous user preferences
|
||||
@@ -793,6 +795,14 @@ export class SessionClient implements LanguageService {
|
||||
return response.body!; // TODO: GH#18217
|
||||
}
|
||||
|
||||
getMoveToRefactoringFileSuggestions(fileName: string, positionOrRange: number | TextRange): { newFileName: string; files: string[]; } {
|
||||
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName);
|
||||
|
||||
const request = this.processRequest<protocol.GetMoveToRefactoringFileSuggestionsRequest>(protocol.CommandTypes.GetMoveToRefactoringFileSuggestions, args);
|
||||
const response = this.processResponse<protocol.GetMoveToRefactoringFileSuggestions>(request);
|
||||
return { newFileName: response.body?.newFileName, files:response.body?.files }!;
|
||||
}
|
||||
|
||||
getEditsForRefactor(
|
||||
fileName: string,
|
||||
_formatOptions: FormatCodeSettings,
|
||||
@@ -816,7 +826,7 @@ export class SessionClient implements LanguageService {
|
||||
const renameFilename: string | undefined = response.body.renameFilename;
|
||||
let renameLocation: number | undefined;
|
||||
if (renameFilename !== undefined) {
|
||||
renameLocation = this.lineOffsetToPosition(renameFilename, response.body.renameLocation!); // TODO: GH#18217
|
||||
renameLocation = this.lineOffsetToPosition(renameFilename, response.body.renameLocation!);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1802,13 +1802,18 @@ export class TestState {
|
||||
isMarker(markerOrRange) ?
|
||||
markerOrRange :
|
||||
{ fileName: markerOrRange.fileName, position: markerOrRange.pos };
|
||||
const { findInStrings = false, findInComments = false, providePrefixAndSuffixTextForRename = true } = options || {};
|
||||
const {
|
||||
findInStrings = false,
|
||||
findInComments = false,
|
||||
providePrefixAndSuffixTextForRename = true,
|
||||
quotePreference = "double"
|
||||
} = options || {};
|
||||
const locations = this.languageService.findRenameLocations(
|
||||
fileName,
|
||||
position,
|
||||
findInStrings,
|
||||
findInComments,
|
||||
providePrefixAndSuffixTextForRename,
|
||||
{ providePrefixAndSuffixTextForRename, quotePreference },
|
||||
);
|
||||
|
||||
if (!locations) {
|
||||
@@ -1818,7 +1823,8 @@ export class TestState {
|
||||
const renameOptions = options ?
|
||||
(options.findInStrings !== undefined ? `// @findInStrings: ${findInStrings}\n` : "") +
|
||||
(options.findInComments !== undefined ? `// @findInComments: ${findInComments}\n` : "") +
|
||||
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") :
|
||||
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") +
|
||||
(options.quotePreference !== undefined ? `// @quotePreference: ${quotePreference}\n` : "") :
|
||||
"";
|
||||
|
||||
return renameOptions + (renameOptions ? "\n" : "") + this.getBaselineForDocumentSpansWithFileContents(
|
||||
@@ -3293,7 +3299,6 @@ export class TestState {
|
||||
ts.Debug.fail(`Did not expect a change in ${change.fileName}`);
|
||||
}
|
||||
const oldText = this.tryGetFileContent(change.fileName);
|
||||
ts.Debug.assert(!!change.isNewFile === (oldText === undefined));
|
||||
const newContent = change.isNewFile ? ts.first(change.textChanges).newText : ts.textChanges.applyChanges(oldText!, change.textChanges);
|
||||
this.verifyTextMatches(newContent, /*includeWhitespace*/ true, expectedNewContent);
|
||||
}
|
||||
@@ -3906,6 +3911,18 @@ export class TestState {
|
||||
this.verifyNewContent({ newFileContent: options.newFileContents }, editInfo.edits);
|
||||
}
|
||||
|
||||
public moveToFile(options: FourSlashInterface.MoveToFileOptions): void {
|
||||
assert(this.getRanges().length === 1, "Must have exactly one fourslash range (source enclosed between '[|' and '|]' delimiters) in the source file");
|
||||
const range = this.getRanges()[0];
|
||||
const refactor = ts.find(this.getApplicableRefactors(range, { allowTextChangesInNewFiles: true }, /*triggerReason*/ undefined, /*kind*/ undefined, /*includeInteractiveActions*/ true), r => r.name === "Move to file")!;
|
||||
assert(refactor.actions.length === 1);
|
||||
const action = ts.first(refactor.actions);
|
||||
assert(action.name === "Move to file" && action.description === "Move to file");
|
||||
|
||||
const editInfo = this.languageService.getEditsForRefactor(range.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.emptyOptions, options.interactiveRefactorArguments)!;
|
||||
this.verifyNewContent({ newFileContent: options.newFileContents }, editInfo.edits);
|
||||
}
|
||||
|
||||
private testNewFileContents(edits: readonly ts.FileTextChanges[], newFileContents: { [fileName: string]: string }, description: string): void {
|
||||
for (const { fileName, textChanges } of edits) {
|
||||
const newContent = newFileContents[fileName];
|
||||
@@ -4211,11 +4228,11 @@ export class TestState {
|
||||
private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string, preferences = ts.emptyOptions) {
|
||||
return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, kind);
|
||||
}
|
||||
private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, kind); // eslint-disable-line local/no-in-operator
|
||||
private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string, includeInteractiveActions?: boolean): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, kind, includeInteractiveActions); // eslint-disable-line local/no-in-operator
|
||||
}
|
||||
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, kind?: string): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind) || ts.emptyArray;
|
||||
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, kind?: string, includeInteractiveActions?: boolean): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions) || ts.emptyArray;
|
||||
}
|
||||
|
||||
public configurePlugin(pluginName: string, configuration: any): void {
|
||||
|
||||
@@ -610,6 +610,10 @@ export class Verify extends VerifyNegatable {
|
||||
this.state.moveToNewFile(options);
|
||||
}
|
||||
|
||||
public moveToFile(options: MoveToFileOptions): void {
|
||||
this.state.moveToFile(options);
|
||||
}
|
||||
|
||||
public noMoveToNewFile(): void {
|
||||
this.state.noMoveToNewFile();
|
||||
}
|
||||
@@ -1896,6 +1900,12 @@ export interface MoveToNewFileOptions {
|
||||
readonly preferences?: ts.UserPreferences;
|
||||
}
|
||||
|
||||
export interface MoveToFileOptions {
|
||||
readonly newFileContents: { readonly [fileName: string]: string };
|
||||
readonly interactiveRefactorArguments: ts.InteractiveRefactorArguments;
|
||||
readonly preferences?: ts.UserPreferences;
|
||||
}
|
||||
|
||||
export type RenameLocationsOptions = readonly RenameLocationOptions[] | {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
@@ -1910,6 +1920,7 @@ export interface RenameOptions {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
readonly quotePreference?: "auto" | "double" | "single";
|
||||
}
|
||||
export type BaselineCommandWithMarkerOrRange = {
|
||||
type: "findAllReferences" | "goToDefinition" | "getDefinitionAtPosition" | "goToSourceDefinition" | "goToType" | "goToImplementation";
|
||||
|
||||
@@ -527,8 +527,8 @@ class LanguageServiceShimProxy implements ts.LanguageService {
|
||||
getSmartSelectionRange(fileName: string, position: number): ts.SelectionRange {
|
||||
return unwrapJSONCallResult(this.shim.getSmartSelectionRange(fileName, position));
|
||||
}
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: ts.UserPreferences | boolean): ts.RenameLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, preferences));
|
||||
}
|
||||
getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
|
||||
return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));
|
||||
@@ -616,6 +616,9 @@ class LanguageServiceShimProxy implements ts.LanguageService {
|
||||
getApplicableRefactors(): ts.ApplicableRefactorInfo[] {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
getMoveToRefactoringFileSuggestions(): { newFileName: string, files: string[] } {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
organizeImports(_args: ts.OrganizeImportsArgs, _formatOptions: ts.FormatCodeSettings): readonly ts.FileTextChanges[] {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
@@ -885,7 +888,7 @@ class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
|
||||
return mockHash(s);
|
||||
}
|
||||
|
||||
require(_initialDir: string, _moduleName: string): ts.RequireResult {
|
||||
require(_initialDir: string, _moduleName: string): ts.ModuleImportResult {
|
||||
switch (_moduleName) {
|
||||
// Adds to the Quick Info a fixed string and a string from the config file
|
||||
// and replaces the first display part
|
||||
|
||||
@@ -10,6 +10,7 @@ export type EventTypesRegistry = "event::typesRegistry";
|
||||
export type EventBeginInstallTypes = "event::beginInstallTypes";
|
||||
export type EventEndInstallTypes = "event::endInstallTypes";
|
||||
export type EventInitializationFailed = "event::initializationFailed";
|
||||
export type ActionWatchTypingLocations = "action::watchTypingLocations";
|
||||
/** @internal */
|
||||
export const ActionSet: ActionSet = "action::set";
|
||||
/** @internal */
|
||||
@@ -24,6 +25,8 @@ export const EventBeginInstallTypes: EventBeginInstallTypes = "event::beginInsta
|
||||
export const EventEndInstallTypes: EventEndInstallTypes = "event::endInstallTypes";
|
||||
/** @internal */
|
||||
export const EventInitializationFailed: EventInitializationFailed = "event::initializationFailed";
|
||||
/** @internal */
|
||||
export const ActionWatchTypingLocations: ActionWatchTypingLocations = "action::watchTypingLocations";
|
||||
|
||||
/** @internal */
|
||||
export namespace Arguments {
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import {
|
||||
CompilerOptions,
|
||||
DirectoryWatcherCallback,
|
||||
FileWatcher,
|
||||
FileWatcherCallback,
|
||||
JsTyping,
|
||||
MapLike,
|
||||
Path,
|
||||
SortedReadonlyArray,
|
||||
TypeAcquisition,
|
||||
WatchOptions,
|
||||
} from "./_namespaces/ts";
|
||||
import {
|
||||
ActionInvalidate,
|
||||
ActionPackageInstalled,
|
||||
ActionSet,
|
||||
ActionWatchTypingLocations,
|
||||
EventBeginInstallTypes,
|
||||
EventEndInstallTypes,
|
||||
EventInitializationFailed,
|
||||
@@ -21,7 +18,7 @@ import {
|
||||
} from "./_namespaces/ts.server";
|
||||
|
||||
export interface TypingInstallerResponse {
|
||||
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
|
||||
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed | ActionWatchTypingLocations;
|
||||
}
|
||||
|
||||
export interface TypingInstallerRequestWithProjectName {
|
||||
@@ -35,7 +32,6 @@ export interface DiscoverTypings extends TypingInstallerRequestWithProjectName {
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: Path;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly watchOptions?: WatchOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly cachePath?: string;
|
||||
@@ -104,8 +100,6 @@ export interface InstallTypingHost extends JsTyping.TypingResolutionHost {
|
||||
writeFile(path: string, content: string): void;
|
||||
createDirectory(path: string): void;
|
||||
getCurrentDirectory?(): string;
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
|
||||
}
|
||||
|
||||
export interface SetTypings extends ProjectResponse {
|
||||
@@ -116,5 +110,11 @@ export interface SetTypings extends ProjectResponse {
|
||||
readonly kind: ActionSet;
|
||||
}
|
||||
|
||||
export interface WatchTypingLocations extends ProjectResponse {
|
||||
/** if files is undefined, retain same set of watchers */
|
||||
readonly files: readonly string[] | undefined;
|
||||
readonly kind: ActionWatchTypingLocations;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type TypingInstallerResponseUnion = SetTypings | InvalidateCachedTypings | TypesRegistryResponse | PackageInstalledResponse | InstallTypes | InitializationFailedResponse;
|
||||
export type TypingInstallerResponseUnion = SetTypings | InvalidateCachedTypings | TypesRegistryResponse | PackageInstalledResponse | InstallTypes | InitializationFailedResponse | WatchTypingLocations;
|
||||
|
||||
Vendored
+14
-4
@@ -4311,12 +4311,21 @@ interface Float64Array {
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Float64Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
* at begin, inclusive, up to end, exclusive.
|
||||
* @param begin The index of the beginning of the array.
|
||||
* @param end The index of the end of the array.
|
||||
*/
|
||||
subarray(begin?: number, end?: number): Float64Array;
|
||||
|
||||
/**
|
||||
* Converts a number to a string by using the current locale.
|
||||
*/
|
||||
toLocaleString(): string;
|
||||
|
||||
/**
|
||||
* Returns a string representation of an array.
|
||||
*/
|
||||
toString(): string;
|
||||
|
||||
/** Returns the primitive value of the specified object. */
|
||||
@@ -4365,11 +4374,12 @@ declare var Float64Array: Float64ArrayConstructor;
|
||||
|
||||
declare namespace Intl {
|
||||
interface CollatorOptions {
|
||||
usage?: string | undefined;
|
||||
localeMatcher?: string | undefined;
|
||||
usage?: "sort" | "search" | undefined;
|
||||
localeMatcher?: "lookup" | "best fit" | undefined;
|
||||
numeric?: boolean | undefined;
|
||||
caseFirst?: string | undefined;
|
||||
sensitivity?: string | undefined;
|
||||
caseFirst?: "upper" | "lower" | "false" | undefined;
|
||||
sensitivity?: "base" | "accent" | "case" | "variant" | undefined;
|
||||
collation?: "big5han" | "compat" | "dict" | "direct" | "ducet" | "emoji" | "eor" | "gb2312" | "phonebk" | "phonetic" | "pinyin" | "reformed" | "searchjl" | "stroke" | "trad" | "unihan" | "zhuyin" | undefined;
|
||||
ignorePunctuation?: boolean | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ import {
|
||||
ThrottledOperations,
|
||||
toNormalizedPath,
|
||||
TypingsCache,
|
||||
WatchTypingLocations,
|
||||
} from "./_namespaces/ts.server";
|
||||
import * as protocol from "./protocol";
|
||||
|
||||
@@ -579,7 +580,7 @@ export interface ProjectServiceOptions {
|
||||
cancellationToken: HostCancellationToken;
|
||||
useSingleInferredProject: boolean;
|
||||
useInferredProjectPerProjectRoot: boolean;
|
||||
typingsInstaller: ITypingsInstaller;
|
||||
typingsInstaller?: ITypingsInstaller;
|
||||
eventHandler?: ProjectServiceEventHandler;
|
||||
suppressDiagnosticEvents?: boolean;
|
||||
throttleWaitMilliseconds?: number;
|
||||
@@ -962,7 +963,7 @@ export class ProjectService {
|
||||
public readonly globalPlugins: readonly string[];
|
||||
public readonly pluginProbeLocations: readonly string[];
|
||||
public readonly allowLocalPluginLoads: boolean;
|
||||
private currentPluginConfigOverrides: Map<string, any> | undefined;
|
||||
/** @internal */ currentPluginConfigOverrides: Map<string, any> | undefined;
|
||||
|
||||
public readonly typesMapLocation: string | undefined;
|
||||
|
||||
@@ -1153,6 +1154,11 @@ export class ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
watchTypingLocations(response: WatchTypingLocations) {
|
||||
this.findProject(response.projectName)?.watchTypingLocations(response.files);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
delayEnsureProjectForOpenFiles() {
|
||||
if (!this.openFiles.size) return;
|
||||
@@ -2189,7 +2195,6 @@ export class ProjectService {
|
||||
/*lastFileExceededProgramSize*/ this.getFilenameForExceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
|
||||
options.compileOnSave === undefined ? true : options.compileOnSave,
|
||||
/*projectFilePath*/ undefined,
|
||||
this.currentPluginConfigOverrides,
|
||||
watchOptionsAndErrors?.watchOptions
|
||||
);
|
||||
project.setProjectErrors(watchOptionsAndErrors?.errors);
|
||||
@@ -2354,7 +2359,7 @@ export class ProjectService {
|
||||
project.enableLanguageService();
|
||||
this.watchWildcards(configFilename, configFileExistenceInfo, project);
|
||||
}
|
||||
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
|
||||
project.enablePluginsWithOptions(compilerOptions);
|
||||
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());
|
||||
this.updateRootAndOptionsOfNonInferredProject(project, filesToAdd, fileNamePropertyReader, compilerOptions, parsedCommandLine.typeAcquisition!, parsedCommandLine.compileOnSave, parsedCommandLine.watchOptions);
|
||||
tracing?.pop();
|
||||
@@ -2737,7 +2742,7 @@ export class ProjectService {
|
||||
typeAcquisition = this.typeAcquisitionForInferredProjects;
|
||||
}
|
||||
watchOptionsAndErrors = watchOptionsAndErrors || undefined;
|
||||
const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptionsAndErrors?.watchOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides, typeAcquisition);
|
||||
const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptionsAndErrors?.watchOptions, projectRootPath, currentDirectory, typeAcquisition);
|
||||
project.setProjectErrors(watchOptionsAndErrors?.errors);
|
||||
if (isSingleInferredProject) {
|
||||
this.inferredProjects.unshift(project);
|
||||
@@ -4242,8 +4247,11 @@ export class ProjectService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
requestEnablePlugin(project: Project, pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<string, any> | undefined) {
|
||||
/**
|
||||
* Performs the initial steps of enabling a plugin by finding and instantiating the module for a plugin either asynchronously or synchronously
|
||||
* @internal
|
||||
*/
|
||||
requestEnablePlugin(project: Project, pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
if (!this.host.importPlugin && !this.host.require) {
|
||||
this.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
return;
|
||||
@@ -4257,7 +4265,12 @@ export class ProjectService {
|
||||
|
||||
// If the host supports dynamic import, begin enabling the plugin asynchronously.
|
||||
if (this.host.importPlugin) {
|
||||
const importPromise = project.beginEnablePluginAsync(pluginConfigEntry, searchPaths, pluginConfigOverrides);
|
||||
const importPromise = Project.importServicePluginAsync(
|
||||
pluginConfigEntry,
|
||||
searchPaths,
|
||||
this.host,
|
||||
s => this.logger.info(s),
|
||||
) as Promise<BeginEnablePluginResult>;
|
||||
this.pendingPluginEnablements ??= new Map();
|
||||
let promises = this.pendingPluginEnablements.get(project);
|
||||
if (!promises) this.pendingPluginEnablements.set(project, promises = []);
|
||||
@@ -4266,7 +4279,33 @@ export class ProjectService {
|
||||
}
|
||||
|
||||
// Otherwise, load the plugin using `require`
|
||||
project.endEnablePlugin(project.beginEnablePluginSync(pluginConfigEntry, searchPaths, pluginConfigOverrides));
|
||||
this.endEnablePlugin(project, Project.importServicePluginSync(
|
||||
pluginConfigEntry,
|
||||
searchPaths,
|
||||
this.host,
|
||||
s => this.logger.info(s),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the remaining steps of enabling a plugin after its module has been instantiated.
|
||||
* @internal
|
||||
*/
|
||||
private endEnablePlugin(project: Project, { pluginConfigEntry, resolvedModule, errorLogs }: BeginEnablePluginResult) {
|
||||
if (resolvedModule) {
|
||||
const configurationOverride = this.currentPluginConfigOverrides?.get(pluginConfigEntry.name);
|
||||
if (configurationOverride) {
|
||||
// Preserve the name property since it's immutable
|
||||
const pluginName = pluginConfigEntry.name;
|
||||
pluginConfigEntry = configurationOverride;
|
||||
pluginConfigEntry.name = pluginName;
|
||||
}
|
||||
project.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
}
|
||||
else {
|
||||
forEach(errorLogs, message => this.logger.info(message));
|
||||
this.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -4345,7 +4384,7 @@ export class ProjectService {
|
||||
}
|
||||
|
||||
for (const result of results) {
|
||||
project.endEnablePlugin(result);
|
||||
this.endEnablePlugin(project, result);
|
||||
}
|
||||
|
||||
// Plugins may have modified external files, so mark the project as dirty.
|
||||
|
||||
+205
-109
@@ -13,16 +13,19 @@ import {
|
||||
closeFileWatcher,
|
||||
closeFileWatcherOf,
|
||||
combinePaths,
|
||||
comparePaths,
|
||||
CompilerHost,
|
||||
CompilerOptions,
|
||||
concatenate,
|
||||
ConfigFileProgramReloadLevel,
|
||||
containsPath,
|
||||
createCacheableExportInfoMap,
|
||||
createLanguageService,
|
||||
createResolutionCache,
|
||||
createSymlinkCache,
|
||||
Debug,
|
||||
Diagnostic,
|
||||
directorySeparator,
|
||||
DirectoryStructureHost,
|
||||
DirectoryWatcherCallback,
|
||||
DocumentPositionMapper,
|
||||
@@ -38,7 +41,6 @@ import {
|
||||
FileWatcherCallback,
|
||||
FileWatcherEventKind,
|
||||
filter,
|
||||
firstDefined,
|
||||
flatMap,
|
||||
forEach,
|
||||
forEachEntry,
|
||||
@@ -46,6 +48,7 @@ import {
|
||||
generateDjb2Hash,
|
||||
getAllowJSCompilerOption,
|
||||
getAutomaticTypeDirectiveNames,
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getDeclarationEmitOutputFilePathWorker,
|
||||
getDefaultCompilerOptions,
|
||||
@@ -58,6 +61,7 @@ import {
|
||||
getNormalizedAbsolutePath,
|
||||
getOrUpdate,
|
||||
getStringComparer,
|
||||
HasInvalidatedLibResolutions,
|
||||
HasInvalidatedResolutions,
|
||||
HostCancellationToken,
|
||||
inferredTypesContainingFile,
|
||||
@@ -126,6 +130,7 @@ import {
|
||||
WatchType,
|
||||
} from "./_namespaces/ts";
|
||||
import {
|
||||
ActionInvalidate,
|
||||
asNormalizedPath,
|
||||
createModuleSpecifierCache,
|
||||
emptyArray,
|
||||
@@ -252,13 +257,15 @@ export interface PluginModuleWithName {
|
||||
export type PluginModuleFactory = (mod: { typescript: typeof ts }) => PluginModule;
|
||||
|
||||
/** @internal */
|
||||
export interface BeginEnablePluginResult {
|
||||
export interface PluginImportResult<T> {
|
||||
pluginConfigEntry: PluginImport;
|
||||
pluginConfigOverrides: Map<string, any> | undefined;
|
||||
resolvedModule: PluginModuleFactory | undefined;
|
||||
resolvedModule: T | undefined;
|
||||
errorLogs: string[] | undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type BeginEnablePluginResult = PluginImportResult<PluginModuleFactory>;
|
||||
|
||||
/**
|
||||
* The project root can be script info - if root is present,
|
||||
* or it could be just normalized path if root wasn't present on the host(only for non inferred project)
|
||||
@@ -285,6 +292,14 @@ export interface EmitResult {
|
||||
diagnostics: readonly Diagnostic[];
|
||||
}
|
||||
|
||||
const enum TypingWatcherType {
|
||||
FileWatcher = "FileWatcher",
|
||||
DirectoryWatcher = "DirectoryWatcher"
|
||||
}
|
||||
|
||||
type TypingWatchers = Map<Path, FileWatcher> & { isInvoked?: boolean; };
|
||||
|
||||
|
||||
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap = new Map<string, ProjectRootFile>();
|
||||
@@ -326,6 +341,9 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
/** @internal */
|
||||
hasInvalidatedResolutions: HasInvalidatedResolutions | undefined;
|
||||
|
||||
/** @internal */
|
||||
hasInvalidatedLibResolutions: HasInvalidatedLibResolutions | undefined;
|
||||
|
||||
/** @internal */
|
||||
resolutionCache: ResolutionCache;
|
||||
|
||||
@@ -365,6 +383,9 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
/** @internal */
|
||||
typingFiles: SortedReadonlyArray<string> = emptyArray;
|
||||
|
||||
/** @internal */
|
||||
private typingWatchers: TypingWatchers | undefined;
|
||||
|
||||
/** @internal */
|
||||
originalConfiguredProjects: Set<NormalizedPath> | undefined;
|
||||
|
||||
@@ -394,36 +415,62 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
return hasOneOrMoreJsAndNoTsFiles(this);
|
||||
}
|
||||
|
||||
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined {
|
||||
const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules")));
|
||||
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
|
||||
const result = host.require!(resolvedPath, moduleName); // TODO: GH#18217
|
||||
if (result.error) {
|
||||
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
|
||||
(logErrors || log)(`Failed to load module '${moduleName}' from ${resolvedPath}: ${err}`);
|
||||
return undefined;
|
||||
}
|
||||
return result.module;
|
||||
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} | undefined {
|
||||
return Project.importServicePluginSync({ name: moduleName }, [initialDir], host, log).resolvedModule;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public static async importServicePluginAsync(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void, logErrors?: (message: string) => void): Promise<{} | undefined> {
|
||||
Debug.assertIsDefined(host.importPlugin);
|
||||
const resolvedPath = combinePaths(initialDir, "node_modules");
|
||||
log(`Dynamically importing ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
|
||||
let result: ModuleImportResult;
|
||||
try {
|
||||
result = await host.importPlugin(resolvedPath, moduleName);
|
||||
}
|
||||
catch (e) {
|
||||
result = { module: undefined, error: e };
|
||||
}
|
||||
if (result.error) {
|
||||
public static importServicePluginSync<T = {}>(
|
||||
pluginConfigEntry: PluginImport,
|
||||
searchPaths: string[],
|
||||
host: ServerHost,
|
||||
log: (message: string) => void,
|
||||
): PluginImportResult<T> {
|
||||
Debug.assertIsDefined(host.require);
|
||||
let errorLogs: string[] | undefined;
|
||||
let resolvedModule: T | undefined;
|
||||
for (const initialDir of searchPaths) {
|
||||
const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules")));
|
||||
log(`Loading ${pluginConfigEntry.name} from ${initialDir} (resolved to ${resolvedPath})`);
|
||||
const result = host.require(resolvedPath, pluginConfigEntry.name); // TODO: GH#18217
|
||||
if (!result.error) {
|
||||
resolvedModule = result.module as T;
|
||||
break;
|
||||
}
|
||||
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
|
||||
(logErrors || log)(`Failed to dynamically import module '${moduleName}' from ${resolvedPath}: ${err}`);
|
||||
return undefined;
|
||||
(errorLogs ??= []).push(`Failed to load module '${pluginConfigEntry.name}' from ${resolvedPath}: ${err}`);
|
||||
}
|
||||
return result.module;
|
||||
return { pluginConfigEntry, resolvedModule, errorLogs };
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public static async importServicePluginAsync<T = {}>(
|
||||
pluginConfigEntry: PluginImport,
|
||||
searchPaths: string[],
|
||||
host: ServerHost,
|
||||
log: (message: string) => void,
|
||||
): Promise<PluginImportResult<T>> {
|
||||
Debug.assertIsDefined(host.importPlugin);
|
||||
let errorLogs: string[] | undefined;
|
||||
let resolvedModule: T | undefined;
|
||||
for (const initialDir of searchPaths) {
|
||||
const resolvedPath = combinePaths(initialDir, "node_modules");
|
||||
log(`Dynamically importing ${pluginConfigEntry.name} from ${initialDir} (resolved to ${resolvedPath})`);
|
||||
let result: ModuleImportResult;
|
||||
try {
|
||||
result = await host.importPlugin(resolvedPath, pluginConfigEntry.name);
|
||||
}
|
||||
catch (e) {
|
||||
result = { module: undefined, error: e };
|
||||
}
|
||||
if (!result.error) {
|
||||
resolvedModule = result.module as T;
|
||||
break;
|
||||
}
|
||||
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
|
||||
(errorLogs ??= []).push(`Failed to dynamically import module '${pluginConfigEntry.name}' from ${resolvedPath}: ${err}`);
|
||||
}
|
||||
return { pluginConfigEntry, resolvedModule, errorLogs };
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -684,6 +731,11 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
resolveLibrary(libraryName: string, resolveFrom: string, options: CompilerOptions, libFileName: string): ResolvedModuleWithFailedLookupLocations {
|
||||
return this.resolutionCache.resolveLibrary(libraryName, resolveFrom, options, libFileName);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
return this.directoryStructureHost.directoryExists!(path); // TODO: GH#18217
|
||||
}
|
||||
@@ -976,6 +1028,8 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
}
|
||||
|
||||
close() {
|
||||
this.projectService.typingsCache.onProjectClosed(this);
|
||||
this.closeWatchingTypingLocations();
|
||||
if (this.program) {
|
||||
// if we have a program - release all files that are enlisted in program but arent root
|
||||
// The releasing of the roots happens later
|
||||
@@ -1324,6 +1378,108 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private closeWatchingTypingLocations() {
|
||||
if (this.typingWatchers) clearMap(this.typingWatchers, closeFileWatcher);
|
||||
this.typingWatchers = undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private onTypingInstallerWatchInvoke() {
|
||||
this.typingWatchers!.isInvoked = true;
|
||||
this.projectService.updateTypingsForProject({ projectName: this.getProjectName(), kind: ActionInvalidate });
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
watchTypingLocations(files: readonly string[] | undefined) {
|
||||
if (!files) {
|
||||
this.typingWatchers!.isInvoked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
// shut down existing watchers
|
||||
this.closeWatchingTypingLocations();
|
||||
return;
|
||||
}
|
||||
|
||||
const toRemove = new Map(this.typingWatchers);
|
||||
if (!this.typingWatchers) this.typingWatchers = new Map();
|
||||
|
||||
// handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings
|
||||
this.typingWatchers.isInvoked = false;
|
||||
const createProjectWatcher = (path: string, typingsWatcherType: TypingWatcherType) => {
|
||||
const canonicalPath = this.toPath(path);
|
||||
toRemove.delete(canonicalPath);
|
||||
if (!this.typingWatchers!.has(canonicalPath)) {
|
||||
this.typingWatchers!.set(canonicalPath, typingsWatcherType === TypingWatcherType.FileWatcher ?
|
||||
this.projectService.watchFactory.watchFile(
|
||||
path,
|
||||
() => !this.typingWatchers!.isInvoked ?
|
||||
this.onTypingInstallerWatchInvoke() :
|
||||
this.writeLog(`TypingWatchers already invoked`),
|
||||
PollingInterval.High,
|
||||
this.projectService.getWatchOptions(this),
|
||||
WatchType.TypingInstallerLocationFile,
|
||||
this,
|
||||
) :
|
||||
this.projectService.watchFactory.watchDirectory(
|
||||
path,
|
||||
f => {
|
||||
if (this.typingWatchers!.isInvoked) return this.writeLog(`TypingWatchers already invoked`);
|
||||
if (!fileExtensionIs(f, Extension.Json)) return this.writeLog(`Ignoring files that are not *.json`);
|
||||
if (comparePaths(f, combinePaths(this.projectService.typingsInstaller.globalTypingsCacheLocation!, "package.json"), !this.useCaseSensitiveFileNames())) return this.writeLog(`Ignoring package.json change at global typings location`);
|
||||
this.onTypingInstallerWatchInvoke();
|
||||
},
|
||||
WatchDirectoryFlags.Recursive,
|
||||
this.projectService.getWatchOptions(this),
|
||||
WatchType.TypingInstallerLocationDirectory,
|
||||
this,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Create watches from list of files
|
||||
for (const file of files) {
|
||||
const basename = getBaseFileName(file);
|
||||
if (basename === "package.json" || basename === "bower.json") {
|
||||
// package.json or bower.json exists, watch the file to detect changes and update typings
|
||||
createProjectWatcher(file, TypingWatcherType.FileWatcher);
|
||||
continue;
|
||||
}
|
||||
|
||||
// path in projectRoot, watch project root
|
||||
if (containsPath(this.currentDirectory, file, this.currentDirectory, !this.useCaseSensitiveFileNames())) {
|
||||
const subDirectory = file.indexOf(directorySeparator, this.currentDirectory.length + 1);
|
||||
if (subDirectory !== -1) {
|
||||
// Watch subDirectory
|
||||
createProjectWatcher(file.substr(0, subDirectory), TypingWatcherType.DirectoryWatcher);
|
||||
}
|
||||
else {
|
||||
// Watch the directory itself
|
||||
createProjectWatcher(file, TypingWatcherType.DirectoryWatcher);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// path in global cache, watch global cache
|
||||
if (containsPath(this.projectService.typingsInstaller.globalTypingsCacheLocation!, file, this.currentDirectory, !this.useCaseSensitiveFileNames())) {
|
||||
createProjectWatcher(this.projectService.typingsInstaller.globalTypingsCacheLocation!, TypingWatcherType.DirectoryWatcher);
|
||||
continue;
|
||||
}
|
||||
|
||||
// watch node_modules or bower_components
|
||||
createProjectWatcher(file, TypingWatcherType.DirectoryWatcher);
|
||||
}
|
||||
|
||||
// Remove unused watches
|
||||
toRemove.forEach((watch, path) => {
|
||||
watch.close();
|
||||
this.typingWatchers!.delete(path);
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
getCurrentProgram(): Program | undefined {
|
||||
return this.program;
|
||||
@@ -1339,7 +1495,9 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
Debug.assert(!this.isClosed(), "Called update graph worker of closed project");
|
||||
this.writeLog(`Starting updateGraphWorker: Project: ${this.getProjectName()}`);
|
||||
const start = timestamp();
|
||||
this.hasInvalidatedResolutions = this.resolutionCache.createHasInvalidatedResolutions(returnFalse);
|
||||
const { hasInvalidatedResolutions, hasInvalidatedLibResolutions } = this.resolutionCache.createHasInvalidatedResolutions(returnFalse, returnFalse);
|
||||
this.hasInvalidatedResolutions = hasInvalidatedResolutions;
|
||||
this.hasInvalidatedLibResolutions = hasInvalidatedLibResolutions;
|
||||
this.resolutionCache.startCachingPerDirectoryResolution();
|
||||
this.program = this.languageService.getProgram(); // TODO: GH#18217
|
||||
this.dirty = false;
|
||||
@@ -1643,7 +1801,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
// reset cached unresolved imports if changes in compiler options affected module resolution
|
||||
this.cachedUnresolvedImportsPerFile.clear();
|
||||
this.lastCachedUnresolvedImportsList = undefined;
|
||||
this.resolutionCache.clear();
|
||||
this.resolutionCache.onChangesAffectModuleResolution();
|
||||
this.moduleSpecifierCache.clear();
|
||||
}
|
||||
this.markAsDirty();
|
||||
@@ -1796,7 +1954,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
];
|
||||
}
|
||||
|
||||
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<string, any> | undefined): void {
|
||||
protected enableGlobalPlugins(options: CompilerOptions): void {
|
||||
if (!this.projectService.globalPlugins.length) return;
|
||||
const host = this.projectService.host;
|
||||
|
||||
@@ -1817,80 +1975,16 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths, pluginConfigOverrides);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the initial steps of enabling a plugin by finding and instantiating the module for a plugin synchronously using 'require'.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
beginEnablePluginSync(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<string, any> | undefined): BeginEnablePluginResult {
|
||||
Debug.assertIsDefined(this.projectService.host.require);
|
||||
|
||||
let errorLogs: string[] | undefined;
|
||||
const log = (message: string) => this.projectService.logger.info(message);
|
||||
const logError = (message: string) => {
|
||||
(errorLogs ??= []).push(message);
|
||||
};
|
||||
const resolvedModule = firstDefined(searchPaths, searchPath =>
|
||||
Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError) as PluginModuleFactory | undefined);
|
||||
return { pluginConfigEntry, pluginConfigOverrides, resolvedModule, errorLogs };
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void {
|
||||
this.projectService.requestEnablePlugin(this, pluginConfigEntry, searchPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the initial steps of enabling a plugin by finding and instantiating the module for a plugin asynchronously using dynamic `import`.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
async beginEnablePluginAsync(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<string, any> | undefined): Promise<BeginEnablePluginResult> {
|
||||
Debug.assertIsDefined(this.projectService.host.importPlugin);
|
||||
|
||||
let errorLogs: string[] | undefined;
|
||||
const log = (message: string) => this.projectService.logger.info(message);
|
||||
const logError = (message: string) => {
|
||||
(errorLogs ??= []).push(message);
|
||||
};
|
||||
|
||||
let resolvedModule: PluginModuleFactory | undefined;
|
||||
for (const searchPath of searchPaths) {
|
||||
resolvedModule = await Project.importServicePluginAsync(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError) as PluginModuleFactory | undefined;
|
||||
if (resolvedModule !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { pluginConfigEntry, pluginConfigOverrides, resolvedModule, errorLogs };
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the remaining steps of enabling a plugin after its module has been instantiated.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
endEnablePlugin({ pluginConfigEntry, pluginConfigOverrides, resolvedModule, errorLogs }: BeginEnablePluginResult) {
|
||||
if (resolvedModule) {
|
||||
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
|
||||
if (configurationOverride) {
|
||||
// Preserve the name property since it's immutable
|
||||
const pluginName = pluginConfigEntry.name;
|
||||
pluginConfigEntry = configurationOverride;
|
||||
pluginConfigEntry.name = pluginName;
|
||||
}
|
||||
|
||||
this.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
}
|
||||
else {
|
||||
forEach(errorLogs, message => this.projectService.logger.info(message));
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<string, any> | undefined): void {
|
||||
this.projectService.requestEnablePlugin(this, pluginConfigEntry, searchPaths, pluginConfigOverrides);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
/** @internal */
|
||||
enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
try {
|
||||
if (typeof pluginModuleFactory !== "function") {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did not expose a proper factory function`);
|
||||
@@ -2177,7 +2271,6 @@ export class InferredProject extends Project {
|
||||
watchOptions: WatchOptions | undefined,
|
||||
projectRootPath: NormalizedPath | undefined,
|
||||
currentDirectory: string,
|
||||
pluginConfigOverrides: Map<string, any> | undefined,
|
||||
typeAcquisition: TypeAcquisition | undefined) {
|
||||
super(projectService.newInferredProjectName(),
|
||||
ProjectKind.Inferred,
|
||||
@@ -2196,7 +2289,7 @@ export class InferredProject extends Project {
|
||||
if (!projectRootPath && !projectService.useSingleInferredProject) {
|
||||
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
|
||||
}
|
||||
this.enableGlobalPlugins(this.getCompilerOptions(), pluginConfigOverrides);
|
||||
this.enableGlobalPlugins(this.getCompilerOptions());
|
||||
}
|
||||
|
||||
override addRoot(info: ScriptInfo) {
|
||||
@@ -2205,13 +2298,17 @@ export class InferredProject extends Project {
|
||||
if (!this._isJsInferredProject && info.isJavaScript()) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ true);
|
||||
}
|
||||
else if (this.isOrphan() && this._isJsInferredProject && !info.isJavaScript()) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ false);
|
||||
}
|
||||
super.addRoot(info);
|
||||
}
|
||||
|
||||
override removeRoot(info: ScriptInfo) {
|
||||
this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info);
|
||||
super.removeRoot(info);
|
||||
if (this._isJsInferredProject && info.isJavaScript()) {
|
||||
// Delay toggling to isJsInferredProject = false till we actually need it again
|
||||
if (!this.isOrphan() && this._isJsInferredProject && info.isJavaScript()) {
|
||||
if (every(this.getRootScriptInfos(), rootInfo => !rootInfo.isJavaScript())) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ false);
|
||||
}
|
||||
@@ -2714,7 +2811,7 @@ export class ConfiguredProject extends Project {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<string, any> | undefined): void {
|
||||
enablePluginsWithOptions(options: CompilerOptions): void {
|
||||
this.plugins.length = 0;
|
||||
if (!options.plugins?.length && !this.projectService.globalPlugins.length) return;
|
||||
const host = this.projectService.host;
|
||||
@@ -2733,11 +2830,11 @@ export class ConfiguredProject extends Project {
|
||||
// Enable tsconfig-specified plugins
|
||||
if (options.plugins) {
|
||||
for (const pluginConfigEntry of options.plugins) {
|
||||
this.enablePlugin(pluginConfigEntry, searchPaths, pluginConfigOverrides);
|
||||
this.enablePlugin(pluginConfigEntry, searchPaths);
|
||||
}
|
||||
}
|
||||
|
||||
return this.enableGlobalPlugins(options, pluginConfigOverrides);
|
||||
return this.enableGlobalPlugins(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2869,7 +2966,6 @@ export class ExternalProject extends Project {
|
||||
lastFileExceededProgramSize: string | undefined,
|
||||
public override compileOnSaveEnabled: boolean,
|
||||
projectFilePath?: string,
|
||||
pluginConfigOverrides?: Map<string, any>,
|
||||
watchOptions?: WatchOptions) {
|
||||
super(externalProjectName,
|
||||
ProjectKind.External,
|
||||
@@ -2882,7 +2978,7 @@ export class ExternalProject extends Project {
|
||||
watchOptions,
|
||||
projectService.host,
|
||||
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
|
||||
this.enableGlobalPlugins(this.getCompilerOptions(), pluginConfigOverrides);
|
||||
this.enableGlobalPlugins(this.getCompilerOptions());
|
||||
}
|
||||
|
||||
override updateGraph() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
EndOfLineState,
|
||||
FileExtensionInfo,
|
||||
HighlightSpanKind,
|
||||
InteractiveRefactorArguments,
|
||||
MapLike,
|
||||
OutliningSpanKind,
|
||||
OutputFile,
|
||||
@@ -142,6 +143,7 @@ export const enum CommandTypes {
|
||||
|
||||
GetApplicableRefactors = "getApplicableRefactors",
|
||||
GetEditsForRefactor = "getEditsForRefactor",
|
||||
GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions",
|
||||
/** @internal */
|
||||
GetEditsForRefactorFull = "getEditsForRefactor-full",
|
||||
|
||||
@@ -586,6 +588,14 @@ export interface GetApplicableRefactorsRequest extends Request {
|
||||
export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
triggerReason?: RefactorTriggerReason;
|
||||
kind?: string;
|
||||
/**
|
||||
* Include refactor actions that require additional arguments to be passed when
|
||||
* calling 'GetEditsForRefactor'. When true, clients should inspect the
|
||||
* `isInteractive` property of each returned `RefactorActionInfo`
|
||||
* and ensure they are able to collect the appropriate arguments for any
|
||||
* interactive refactor before offering it.
|
||||
*/
|
||||
includeInteractiveActions?: boolean;
|
||||
};
|
||||
|
||||
export type RefactorTriggerReason = "implicit" | "invoked";
|
||||
@@ -598,6 +608,27 @@ export interface GetApplicableRefactorsResponse extends Response {
|
||||
body?: ApplicableRefactorInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Request refactorings at a given position or selection area to move to an existing file.
|
||||
*/
|
||||
export interface GetMoveToRefactoringFileSuggestionsRequest extends Request {
|
||||
command: CommandTypes.GetMoveToRefactoringFileSuggestions;
|
||||
arguments: GetMoveToRefactoringFileSuggestionsRequestArgs;
|
||||
}
|
||||
export type GetMoveToRefactoringFileSuggestionsRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
kind?: string;
|
||||
};
|
||||
/**
|
||||
* Response is a list of available files.
|
||||
* Each refactoring exposes one or more "Actions"; a user selects one action to invoke a refactoring
|
||||
*/
|
||||
export interface GetMoveToRefactoringFileSuggestions extends Response {
|
||||
body: {
|
||||
newFileName: string;
|
||||
files: string[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of one or more available refactoring actions, grouped under a parent refactoring.
|
||||
*/
|
||||
@@ -650,6 +681,12 @@ export interface RefactorActionInfo {
|
||||
* The hierarchical dotted name of the refactor action.
|
||||
*/
|
||||
kind?: string;
|
||||
|
||||
/**
|
||||
* Indicates that the action requires additional arguments to be passed
|
||||
* when calling 'GetEditsForRefactor'.
|
||||
*/
|
||||
isInteractive?: boolean;
|
||||
}
|
||||
|
||||
export interface GetEditsForRefactorRequest extends Request {
|
||||
@@ -666,6 +703,8 @@ export type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
refactor: string;
|
||||
/* The 'name' property from the refactoring action */
|
||||
action: string;
|
||||
/* Arguments for interactive action */
|
||||
interactiveRefactorArguments?: InteractiveRefactorArguments;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+24
-11
@@ -136,7 +136,6 @@ import {
|
||||
toFileNameLowerCase,
|
||||
tracing,
|
||||
unmangleScopedPackageName,
|
||||
UserPreferences,
|
||||
version,
|
||||
WithMetadata,
|
||||
} from "./_namespaces/ts";
|
||||
@@ -161,6 +160,7 @@ import {
|
||||
LogLevel,
|
||||
Msg,
|
||||
NormalizedPath,
|
||||
nullTypingsInstaller,
|
||||
Project,
|
||||
ProjectInfoTelemetryEvent,
|
||||
ProjectKind,
|
||||
@@ -319,7 +319,7 @@ export function formatMessage<T extends protocol.Message>(msg: T, logger: Logger
|
||||
|
||||
const json = JSON.stringify(msg);
|
||||
if (verboseLogging) {
|
||||
logger.info(`${msg.type}:${indent(json)}`);
|
||||
logger.info(`${msg.type}:${indent(JSON.stringify(msg, undefined, " "))}`);
|
||||
}
|
||||
|
||||
const len = byteLength(json, "utf8");
|
||||
@@ -496,14 +496,14 @@ function getRenameLocationsWorker(
|
||||
initialLocation: DocumentPosition,
|
||||
findInStrings: boolean,
|
||||
findInComments: boolean,
|
||||
{ providePrefixAndSuffixTextForRename }: UserPreferences
|
||||
preferences: protocol.UserPreferences
|
||||
): readonly RenameLocation[] {
|
||||
const perProjectResults = getPerProjectReferences(
|
||||
projects,
|
||||
defaultProject,
|
||||
initialLocation,
|
||||
/*isForRename*/ true,
|
||||
(project, position) => project.getLanguageService().findRenameLocations(position.fileName, position.pos, findInStrings, findInComments, providePrefixAndSuffixTextForRename),
|
||||
(project, position) => project.getLanguageService().findRenameLocations(position.fileName, position.pos, findInStrings, findInComments, preferences),
|
||||
(renameLocation, cb) => cb(documentSpanLocation(renameLocation)),
|
||||
);
|
||||
|
||||
@@ -881,6 +881,7 @@ const invalidPartialSemanticModeCommands: readonly protocol.CommandTypes[] = [
|
||||
protocol.CommandTypes.ApplyCodeActionCommand,
|
||||
protocol.CommandTypes.GetSupportedCodeFixes,
|
||||
protocol.CommandTypes.GetApplicableRefactors,
|
||||
protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
|
||||
protocol.CommandTypes.GetEditsForRefactor,
|
||||
protocol.CommandTypes.GetEditsForRefactorFull,
|
||||
protocol.CommandTypes.OrganizeImports,
|
||||
@@ -926,7 +927,7 @@ export interface SessionOptions {
|
||||
cancellationToken: ServerCancellationToken;
|
||||
useSingleInferredProject: boolean;
|
||||
useInferredProjectPerProjectRoot: boolean;
|
||||
typingsInstaller: ITypingsInstaller;
|
||||
typingsInstaller?: ITypingsInstaller;
|
||||
byteLength: (buf: string, encoding?: BufferEncoding) => number;
|
||||
hrtime: (start?: [number, number]) => [number, number];
|
||||
logger: Logger;
|
||||
@@ -972,7 +973,7 @@ export class Session<TMessage = string> implements EventSender {
|
||||
constructor(opts: SessionOptions) {
|
||||
this.host = opts.host;
|
||||
this.cancellationToken = opts.cancellationToken;
|
||||
this.typingsInstaller = opts.typingsInstaller;
|
||||
this.typingsInstaller = opts.typingsInstaller || nullTypingsInstaller;
|
||||
this.byteLength = opts.byteLength;
|
||||
this.hrtime = opts.hrtime;
|
||||
this.logger = opts.logger;
|
||||
@@ -2674,7 +2675,7 @@ export class Session<TMessage = string> implements EventSender {
|
||||
private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file)!;
|
||||
return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind);
|
||||
return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind, args.includeInteractiveActions);
|
||||
}
|
||||
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
@@ -2687,6 +2688,7 @@ export class Session<TMessage = string> implements EventSender {
|
||||
args.refactor,
|
||||
args.action,
|
||||
this.getPreferences(file),
|
||||
args.interactiveRefactorArguments
|
||||
);
|
||||
|
||||
if (result === undefined) {
|
||||
@@ -2702,11 +2704,19 @@ export class Session<TMessage = string> implements EventSender {
|
||||
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(renameFilename))!;
|
||||
mappedRenameLocation = getLocationInNewDocument(getSnapshotText(renameScriptInfo.getSnapshot()), renameFilename, renameLocation, edits);
|
||||
}
|
||||
return { renameLocation: mappedRenameLocation, renameFilename, edits: this.mapTextChangesToCodeEdits(edits) };
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
return {
|
||||
renameLocation: mappedRenameLocation,
|
||||
renameFilename,
|
||||
edits: this.mapTextChangesToCodeEdits(edits)
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getMoveToRefactoringFileSuggestions(args: protocol.GetMoveToRefactoringFileSuggestionsRequestArgs): { newFileName: string, files: string[] }{
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file)!;
|
||||
return project.getLanguageService().getMoveToRefactoringFileSuggestions(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file));
|
||||
}
|
||||
|
||||
private organizeImports(args: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): readonly protocol.FileCodeEdits[] | readonly FileTextChanges[] {
|
||||
@@ -3429,6 +3439,9 @@ export class Session<TMessage = string> implements EventSender {
|
||||
[protocol.CommandTypes.GetEditsForRefactor]: (request: protocol.GetEditsForRefactorRequest) => {
|
||||
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[protocol.CommandTypes.GetMoveToRefactoringFileSuggestions]: (request: protocol.GetMoveToRefactoringFileSuggestionsRequest) => {
|
||||
return this.requiredResponse(this.getMoveToRefactoringFileSuggestions(request.arguments));
|
||||
},
|
||||
[protocol.CommandTypes.GetEditsForRefactorFull]: (request: protocol.GetEditsForRefactorRequest) => {
|
||||
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
|
||||
@@ -46,7 +46,6 @@ export function createInstallTypingsRequest(project: Project, typeAcquisition: T
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true).concat(project.getExcludedFiles() as NormalizedPath[]),
|
||||
compilerOptions: project.getCompilationSettings(),
|
||||
watchOptions: project.projectService.getWatchOptions(project),
|
||||
typeAcquisition,
|
||||
unresolvedImports,
|
||||
projectRootPath: project.getCurrentDirectory() as Path,
|
||||
|
||||
@@ -6,6 +6,7 @@ export * from "../refactors/convertImport";
|
||||
export * from "../refactors/extractType";
|
||||
export * from "../refactors/helpers";
|
||||
export * from "../refactors/moveToNewFile";
|
||||
export * from "../refactors/moveToFile";
|
||||
import * as addOrRemoveBracesToArrowFunction from "./ts.refactor.addOrRemoveBracesToArrowFunction";
|
||||
export { addOrRemoveBracesToArrowFunction };
|
||||
import * as convertArrowFunctionOrFunctionExpression from "./ts.refactor.convertArrowFunctionOrFunctionExpression";
|
||||
|
||||
@@ -2938,7 +2938,7 @@ function getContextualType(previousToken: Node, position: number, sourceFile: So
|
||||
isEqualityOperatorKind(previousToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind) ?
|
||||
// completion at `x ===/**/` should be for the right side
|
||||
checker.getTypeAtLocation(parent.left) :
|
||||
checker.getContextualType(previousToken as Expression);
|
||||
checker.getContextualType(previousToken as Expression, ContextFlags.Completions) || checker.getContextualType(previousToken as Expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4607,9 +4607,9 @@ function getCompletionData(
|
||||
return isDeclarationName(contextToken)
|
||||
&& !isShorthandPropertyAssignment(contextToken.parent)
|
||||
&& !isJsxAttribute(contextToken.parent)
|
||||
// Don't block completions if we're in `class C /**/` or `interface I /**/`, because we're *past* the end of the identifier and might want to complete `extends`.
|
||||
// If `contextToken !== previousToken`, this is `class C ex/**/ or `interface I ex/**/``.
|
||||
&& !((isClassLike(contextToken.parent) || isInterfaceDeclaration(contextToken.parent)) && (contextToken !== previousToken || position > previousToken.end));
|
||||
// Don't block completions if we're in `class C /**/`, `interface I /**/` or `<T /**/>` , because we're *past* the end of the identifier and might want to complete `extends`.
|
||||
// If `contextToken !== previousToken`, this is `class C ex/**/`, `interface I ex/**/` or `<T ex/**/>`.
|
||||
&& !((isClassLike(contextToken.parent) || isInterfaceDeclaration(contextToken.parent) || isTypeParameterDeclaration(contextToken.parent)) && (contextToken !== previousToken || position > previousToken.end));
|
||||
}
|
||||
|
||||
function isPreviousPropertyDeclarationTerminated(contextToken: Node, position: number) {
|
||||
|
||||
@@ -72,6 +72,7 @@ import {
|
||||
getNodeKind,
|
||||
getPropertySymbolFromBindingElement,
|
||||
getPropertySymbolsFromContextualType,
|
||||
getQuoteFromPreference,
|
||||
getReferencedFileLocation,
|
||||
getSuperContainer,
|
||||
getSymbolId,
|
||||
@@ -151,6 +152,7 @@ import {
|
||||
isNamespaceExportDeclaration,
|
||||
isNewExpressionTarget,
|
||||
isNoSubstitutionTemplateLiteral,
|
||||
isNumericLiteral,
|
||||
isObjectBindingElementWithoutPropertyName,
|
||||
isObjectLiteralExpression,
|
||||
isObjectLiteralMethod,
|
||||
@@ -205,6 +207,7 @@ import {
|
||||
PropertyAssignment,
|
||||
PropertyDeclaration,
|
||||
punctuationPart,
|
||||
QuotePreference,
|
||||
rangeIsOnSingleLine,
|
||||
ReferencedSymbol,
|
||||
ReferencedSymbolDefinitionInfo,
|
||||
@@ -673,8 +676,8 @@ function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker,
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean): RenameLocation {
|
||||
return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker)) };
|
||||
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean, quotePreference: QuotePreference): RenameLocation {
|
||||
return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker, quotePreference)) };
|
||||
}
|
||||
|
||||
function toReferencedSymbolEntry(entry: Entry, symbol: Symbol | undefined): ReferencedSymbolEntry {
|
||||
@@ -716,7 +719,7 @@ function entryToDocumentSpan(entry: Entry): DocumentSpan {
|
||||
}
|
||||
|
||||
interface PrefixAndSuffix { readonly prefixText?: string; readonly suffixText?: string; }
|
||||
function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker): PrefixAndSuffix {
|
||||
function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker, quotePreference: QuotePreference): PrefixAndSuffix {
|
||||
if (entry.kind !== EntryKind.Span && isIdentifier(originalNode)) {
|
||||
const { node, kind } = entry;
|
||||
const parent = node.parent;
|
||||
@@ -760,6 +763,12 @@ function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeC
|
||||
}
|
||||
}
|
||||
|
||||
// If the node is a numerical indexing literal, then add quotes around the property access.
|
||||
if (entry.kind !== EntryKind.Span && isNumericLiteral(entry.node) && isAccessExpression(entry.node.parent)) {
|
||||
const quote = getQuoteFromPreference(quotePreference);
|
||||
return { prefixText: quote, suffixText: quote };
|
||||
}
|
||||
|
||||
return emptyOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
ApplicableRefactorInfo,
|
||||
arrayFrom,
|
||||
flatMapIterator,
|
||||
InteractiveRefactorArguments,
|
||||
Refactor,
|
||||
RefactorContext,
|
||||
RefactorEditInfo,
|
||||
@@ -22,15 +23,15 @@ export function registerRefactor(name: string, refactor: Refactor) {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] {
|
||||
export function getApplicableRefactors(context: RefactorContext, includeInteractiveActions?: boolean): ApplicableRefactorInfo[] {
|
||||
return arrayFrom(flatMapIterator(refactors.values(), refactor =>
|
||||
context.cancellationToken && context.cancellationToken.isCancellationRequested() ||
|
||||
!refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) ? undefined :
|
||||
refactor.getAvailableActions(context)));
|
||||
refactor.getAvailableActions(context, includeInteractiveActions)));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined {
|
||||
export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string, interactiveRefactorArguments?: InteractiveRefactorArguments): RefactorEditInfo | undefined {
|
||||
const refactor = refactors.get(refactorName);
|
||||
return refactor && refactor.getEditsForAction(context, actionName);
|
||||
return refactor && refactor.getEditsForAction(context, actionName, interactiveRefactorArguments);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +1,56 @@
|
||||
import { getModuleSpecifier } from "../../compiler/moduleSpecifiers";
|
||||
import {
|
||||
AnyImportOrRequireStatement,
|
||||
append,
|
||||
ApplicableRefactorInfo,
|
||||
AssignmentDeclarationKind,
|
||||
BinaryExpression,
|
||||
BindingElement,
|
||||
BindingName,
|
||||
CallExpression,
|
||||
canHaveDecorators,
|
||||
canHaveModifiers,
|
||||
canHaveSymbol, cast,
|
||||
ClassDeclaration,
|
||||
codefix,
|
||||
combinePaths,
|
||||
concatenate,
|
||||
contains,
|
||||
copyEntries,
|
||||
createModuleSpecifierResolutionHost,
|
||||
createTextRangeFromSpan,
|
||||
Debug,
|
||||
Declaration,
|
||||
DeclarationStatement,
|
||||
Diagnostics,
|
||||
emptyArray,
|
||||
EnumDeclaration,
|
||||
escapeLeadingUnderscores,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
extensionFromPath,
|
||||
ExternalModuleReference,
|
||||
factory,
|
||||
find,
|
||||
FindAllReferences,
|
||||
findIndex,
|
||||
firstDefined,
|
||||
flatMap,
|
||||
forEachEntry,
|
||||
FunctionDeclaration,
|
||||
getAssignmentDeclarationKind,
|
||||
fileShouldUseJavaScriptRequire,
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getDecorators,
|
||||
getDirectoryPath,
|
||||
getLocaleSpecificMessage,
|
||||
getModifiers,
|
||||
getPropertySymbolFromBindingElement,
|
||||
getQuotePreference,
|
||||
getRangesWhere,
|
||||
getRefactorContextSpan,
|
||||
getRelativePathFromFile,
|
||||
getSymbolId,
|
||||
getUniqueName,
|
||||
hasSyntacticModifier,
|
||||
hostGetCanonicalFileName,
|
||||
Identifier,
|
||||
ImportDeclaration,
|
||||
ImportEqualsDeclaration,
|
||||
insertImports,
|
||||
InterfaceDeclaration,
|
||||
InternalSymbolName,
|
||||
isArrayLiteralExpression,
|
||||
isBinaryExpression,
|
||||
isBindingElement,
|
||||
isDeclarationName,
|
||||
isExpressionStatement,
|
||||
isExternalModuleReference,
|
||||
isIdentifier,
|
||||
isImportDeclaration,
|
||||
isImportEqualsDeclaration,
|
||||
isNamedDeclaration,
|
||||
isObjectLiteralExpression,
|
||||
isOmittedExpression,
|
||||
isPrologueDirective,
|
||||
isPropertyAccessExpression,
|
||||
isPropertyAssignment,
|
||||
isRequireCall,
|
||||
isSourceFile,
|
||||
isStringLiteral,
|
||||
isStringLiteralLike,
|
||||
isVariableDeclaration,
|
||||
isVariableDeclarationList,
|
||||
isVariableStatement,
|
||||
LanguageServiceHost,
|
||||
last,
|
||||
length,
|
||||
makeImportIfNecessary,
|
||||
makeStringLiteral,
|
||||
mapDefined,
|
||||
ModifierFlags,
|
||||
ModifierLike,
|
||||
ModuleDeclaration,
|
||||
NamedImportBindings,
|
||||
Node,
|
||||
NodeFlags,
|
||||
nodeSeenTracker,
|
||||
normalizePath,
|
||||
ObjectBindingElementWithoutPropertyName,
|
||||
Program,
|
||||
PropertyAccessExpression,
|
||||
PropertyAssignment,
|
||||
QuotePreference,
|
||||
rangeContainsRange,
|
||||
RefactorContext,
|
||||
RefactorEditInfo,
|
||||
RequireOrImportCall,
|
||||
RequireVariableStatement,
|
||||
resolvePath,
|
||||
ScriptTarget,
|
||||
skipAlias,
|
||||
some,
|
||||
SourceFile,
|
||||
Statement,
|
||||
StringLiteralLike,
|
||||
Symbol,
|
||||
SymbolFlags,
|
||||
symbolNameNoDefault,
|
||||
SyntaxKind,
|
||||
takeWhile,
|
||||
textChanges,
|
||||
TransformFlags,
|
||||
tryCast,
|
||||
TypeAliasDeclaration,
|
||||
TypeChecker,
|
||||
TypeNode,
|
||||
UserPreferences,
|
||||
VariableDeclaration,
|
||||
VariableDeclarationList,
|
||||
VariableStatement,
|
||||
} from "../_namespaces/ts";
|
||||
import { registerRefactor } from "../_namespaces/ts.refactor";
|
||||
import {
|
||||
addExports,
|
||||
addExportToChanges,
|
||||
addNewFileToTsconfig,
|
||||
createNewFileName,
|
||||
createOldFileImportsFromTargetFile,
|
||||
deleteMovedStatements,
|
||||
deleteUnusedOldImports,
|
||||
filterImport,
|
||||
forEachImportInStatement,
|
||||
getStatementsToMove,
|
||||
getTopLevelDeclarationStatement,
|
||||
getUsageInfo,
|
||||
isTopLevelDeclaration,
|
||||
makeImportOrRequire,
|
||||
moduleSpecifierFromImport,
|
||||
nameOfTopLevelDeclaration,
|
||||
registerRefactor,
|
||||
SupportedImportStatement,
|
||||
ToMove,
|
||||
updateImportsInOtherFiles,
|
||||
UsageInfo
|
||||
} from "../_namespaces/ts.refactor";
|
||||
|
||||
const refactorName = "Move to a new file";
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file);
|
||||
@@ -156,55 +77,16 @@ registerRefactor(refactorName, {
|
||||
getEditsForAction: function getRefactorEditsToMoveToNewFile(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === refactorName, "Wrong refactor invoked");
|
||||
const statements = Debug.checkDefined(getStatementsToMove(context));
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context.preferences));
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context.preferences, context));
|
||||
return { edits, renameFilename: undefined, renameLocation: undefined };
|
||||
}
|
||||
});
|
||||
|
||||
interface RangeToMove { readonly toMove: readonly Statement[]; readonly afterLast: Statement | undefined; }
|
||||
function getRangeToMove(context: RefactorContext): RangeToMove | undefined {
|
||||
const { file } = context;
|
||||
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
|
||||
const { statements } = file;
|
||||
|
||||
const startNodeIndex = findIndex(statements, s => s.end > range.pos);
|
||||
if (startNodeIndex === -1) return undefined;
|
||||
|
||||
const startStatement = statements[startNodeIndex];
|
||||
if (isNamedDeclaration(startStatement) && startStatement.name && rangeContainsRange(startStatement.name, range)) {
|
||||
return { toMove: [statements[startNodeIndex]], afterLast: statements[startNodeIndex + 1] };
|
||||
}
|
||||
|
||||
// Can't only partially include the start node or be partially into the next node
|
||||
if (range.pos > startStatement.getStart(file)) return undefined;
|
||||
const afterEndNodeIndex = findIndex(statements, s => s.end > range.end, startNodeIndex);
|
||||
// Can't be partially into the next node
|
||||
if (afterEndNodeIndex !== -1 && (afterEndNodeIndex === 0 || statements[afterEndNodeIndex].getStart(file) < range.end)) return undefined;
|
||||
|
||||
return {
|
||||
toMove: statements.slice(startNodeIndex, afterEndNodeIndex === -1 ? statements.length : afterEndNodeIndex),
|
||||
afterLast: afterEndNodeIndex === -1 ? undefined : statements[afterEndNodeIndex],
|
||||
};
|
||||
}
|
||||
|
||||
function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences): void {
|
||||
function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences, context: RefactorContext): void {
|
||||
const checker = program.getTypeChecker();
|
||||
const usage = getUsageInfo(oldFile, toMove.all, checker);
|
||||
|
||||
const currentDirectory = getDirectoryPath(oldFile.fileName);
|
||||
const extension = extensionFromPath(oldFile.fileName);
|
||||
const newFilename = combinePaths(
|
||||
// new file is always placed in the same directory as the old file
|
||||
currentDirectory,
|
||||
// ensures the filename computed below isn't already taken
|
||||
makeUniqueFilename(
|
||||
// infers a name for the new file from the symbols being moved
|
||||
inferNewFilename(usage.oldFileImportsFromNewFile, usage.movedSymbols),
|
||||
extension,
|
||||
currentDirectory,
|
||||
host))
|
||||
// new file has same extension as old file
|
||||
+ extension;
|
||||
const newFilename = createNewFileName(oldFile, program, context, host);
|
||||
|
||||
// If previous file was global, this is easy.
|
||||
changes.createNewFile(oldFile, newFilename, getNewStatementsAndRemoveFromOldFile(oldFile, usage, changes, toMove, program, host, newFilename, preferences));
|
||||
@@ -212,76 +94,19 @@ function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes
|
||||
addNewFileToTsconfig(program, changes, oldFile.fileName, newFilename, hostGetCanonicalFileName(host));
|
||||
}
|
||||
|
||||
interface StatementRange {
|
||||
readonly first: Statement;
|
||||
readonly afterLast: Statement | undefined;
|
||||
}
|
||||
interface ToMove {
|
||||
readonly all: readonly Statement[];
|
||||
readonly ranges: readonly StatementRange[];
|
||||
}
|
||||
|
||||
function getStatementsToMove(context: RefactorContext): ToMove | undefined {
|
||||
const rangeToMove = getRangeToMove(context);
|
||||
if (rangeToMove === undefined) return undefined;
|
||||
const all: Statement[] = [];
|
||||
const ranges: StatementRange[] = [];
|
||||
const { toMove, afterLast } = rangeToMove;
|
||||
getRangesWhere(toMove, isAllowedStatementToMove, (start, afterEndIndex) => {
|
||||
for (let i = start; i < afterEndIndex; i++) all.push(toMove[i]);
|
||||
ranges.push({ first: toMove[start], afterLast });
|
||||
});
|
||||
return all.length === 0 ? undefined : { all, ranges };
|
||||
}
|
||||
|
||||
function isAllowedStatementToMove(statement: Statement): boolean {
|
||||
// Filters imports and prologue directives out of the range of statements to move.
|
||||
// Imports will be copied to the new file anyway, and may still be needed in the old file.
|
||||
// Prologue directives will be copied to the new file and should be left in the old file.
|
||||
return !isPureImport(statement) && !isPrologueDirective(statement);
|
||||
}
|
||||
|
||||
function isPureImport(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return !hasSyntacticModifier(node, ModifierFlags.Export);
|
||||
case SyntaxKind.VariableStatement:
|
||||
return (node as VariableStatement).declarationList.declarations.every(d => !!d.initializer && isRequireCall(d.initializer, /*requireStringLiteralLikeArgument*/ true));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function addNewFileToTsconfig(program: Program, changes: textChanges.ChangeTracker, oldFileName: string, newFileNameWithExtension: string, getCanonicalFileName: GetCanonicalFileName): void {
|
||||
const cfg = program.getCompilerOptions().configFile;
|
||||
if (!cfg) return;
|
||||
|
||||
const newFileAbsolutePath = normalizePath(combinePaths(oldFileName, "..", newFileNameWithExtension));
|
||||
const newFilePath = getRelativePathFromFile(cfg.fileName, newFileAbsolutePath, getCanonicalFileName);
|
||||
|
||||
const cfgObject = cfg.statements[0] && tryCast(cfg.statements[0].expression, isObjectLiteralExpression);
|
||||
const filesProp = cfgObject && find(cfgObject.properties, (prop): prop is PropertyAssignment =>
|
||||
isPropertyAssignment(prop) && isStringLiteral(prop.name) && prop.name.text === "files");
|
||||
if (filesProp && isArrayLiteralExpression(filesProp.initializer)) {
|
||||
changes.insertNodeInListAfter(cfg, last(filesProp.initializer.elements), factory.createStringLiteral(newFilePath), filesProp.initializer.elements);
|
||||
}
|
||||
}
|
||||
|
||||
function getNewStatementsAndRemoveFromOldFile(
|
||||
oldFile: SourceFile, usage: UsageInfo, changes: textChanges.ChangeTracker, toMove: ToMove, program: Program, host: LanguageServiceHost, newFilename: string, preferences: UserPreferences,
|
||||
) {
|
||||
const checker = program.getTypeChecker();
|
||||
const prologueDirectives = takeWhile(oldFile.statements, isPrologueDirective);
|
||||
if (oldFile.externalModuleIndicator === undefined && oldFile.commonJsModuleIndicator === undefined && usage.oldImportsNeededByNewFile.size() === 0) {
|
||||
if (oldFile.externalModuleIndicator === undefined && oldFile.commonJsModuleIndicator === undefined && usage.oldImportsNeededByTargetFile.size === 0) {
|
||||
deleteMovedStatements(oldFile, toMove.ranges, changes);
|
||||
return [...prologueDirectives, ...toMove.all];
|
||||
}
|
||||
|
||||
const useEsModuleSyntax = !!oldFile.externalModuleIndicator;
|
||||
const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(newFilename, program, host, !!oldFile.commonJsModuleIndicator);
|
||||
const quotePreference = getQuotePreference(oldFile, preferences);
|
||||
const importsFromNewFile = createOldFileImportsFromNewFile(oldFile, usage.oldFileImportsFromNewFile, newFilename, program, host, useEsModuleSyntax, quotePreference);
|
||||
const importsFromNewFile = createOldFileImportsFromTargetFile(oldFile, usage.oldFileImportsFromTargetFile, newFilename, program, host, useEsModuleSyntax, quotePreference);
|
||||
if (importsFromNewFile) {
|
||||
insertImports(changes, oldFile, importsFromNewFile, /*blankLineBetween*/ true, preferences);
|
||||
}
|
||||
@@ -290,8 +115,8 @@ function getNewStatementsAndRemoveFromOldFile(
|
||||
deleteMovedStatements(oldFile, toMove.ranges, changes);
|
||||
updateImportsInOtherFiles(changes, program, host, oldFile, usage.movedSymbols, newFilename, quotePreference);
|
||||
|
||||
const imports = getNewFileImportsAndAddExportInOldFile(oldFile, usage.oldImportsNeededByNewFile, usage.newFileImportsFromOldFile, changes, checker, program, host, useEsModuleSyntax, quotePreference);
|
||||
const body = addExports(oldFile, toMove.all, usage.oldFileImportsFromNewFile, useEsModuleSyntax);
|
||||
const imports = getNewFileImportsAndAddExportInOldFile(oldFile, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, changes, checker, program, host, useEsModuleSyntax, quotePreference);
|
||||
const body = addExports(oldFile, toMove.all, usage.oldFileImportsFromTargetFile, useEsModuleSyntax);
|
||||
if (imports.length && body.length) {
|
||||
return [
|
||||
...prologueDirectives,
|
||||
@@ -308,295 +133,10 @@ function getNewStatementsAndRemoveFromOldFile(
|
||||
];
|
||||
}
|
||||
|
||||
function deleteMovedStatements(sourceFile: SourceFile, moved: readonly StatementRange[], changes: textChanges.ChangeTracker) {
|
||||
for (const { first, afterLast } of moved) {
|
||||
changes.deleteNodeRangeExcludingEnd(sourceFile, first, afterLast);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteUnusedOldImports(oldFile: SourceFile, toMove: readonly Statement[], changes: textChanges.ChangeTracker, toDelete: ReadonlySymbolSet, checker: TypeChecker) {
|
||||
for (const statement of oldFile.statements) {
|
||||
if (contains(toMove, statement)) continue;
|
||||
forEachImportInStatement(statement, i => deleteUnusedImports(oldFile, i, changes, name => toDelete.has(checker.getSymbolAtLocation(name)!)));
|
||||
}
|
||||
}
|
||||
|
||||
function updateImportsInOtherFiles(
|
||||
changes: textChanges.ChangeTracker, program: Program, host: LanguageServiceHost, oldFile: SourceFile, movedSymbols: ReadonlySymbolSet, newFilename: string, quotePreference: QuotePreference
|
||||
): void {
|
||||
const checker = program.getTypeChecker();
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
if (sourceFile === oldFile) continue;
|
||||
for (const statement of sourceFile.statements) {
|
||||
forEachImportInStatement(statement, importNode => {
|
||||
if (checker.getSymbolAtLocation(moduleSpecifierFromImport(importNode)) !== oldFile.symbol) return;
|
||||
|
||||
const shouldMove = (name: Identifier): boolean => {
|
||||
const symbol = isBindingElement(name.parent)
|
||||
? getPropertySymbolFromBindingElement(checker, name.parent as ObjectBindingElementWithoutPropertyName)
|
||||
: skipAlias(checker.getSymbolAtLocation(name)!, checker); // TODO: GH#18217
|
||||
return !!symbol && movedSymbols.has(symbol);
|
||||
};
|
||||
deleteUnusedImports(sourceFile, importNode, changes, shouldMove); // These will be changed to imports from the new file
|
||||
|
||||
const pathToNewFileWithExtension = resolvePath(getDirectoryPath(oldFile.path), newFilename);
|
||||
const newModuleSpecifier = getModuleSpecifier(program.getCompilerOptions(), sourceFile, sourceFile.path, pathToNewFileWithExtension, createModuleSpecifierResolutionHost(program, host));
|
||||
const newImportDeclaration = filterImport(importNode, makeStringLiteral(newModuleSpecifier, quotePreference), shouldMove);
|
||||
if (newImportDeclaration) changes.insertNodeAfter(sourceFile, statement, newImportDeclaration);
|
||||
|
||||
const ns = getNamespaceLikeImport(importNode);
|
||||
if (ns) updateNamespaceLikeImport(changes, sourceFile, checker, movedSymbols, newModuleSpecifier, ns, importNode, quotePreference);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNamespaceLikeImport(node: SupportedImport): Identifier | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return node.importClause && node.importClause.namedBindings && node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport ?
|
||||
node.importClause.namedBindings.name : undefined;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return node.name;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return tryCast(node.name, isIdentifier);
|
||||
default:
|
||||
return Debug.assertNever(node, `Unexpected node kind ${(node as SupportedImport).kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
function updateNamespaceLikeImport(
|
||||
changes: textChanges.ChangeTracker,
|
||||
sourceFile: SourceFile,
|
||||
checker: TypeChecker,
|
||||
movedSymbols: ReadonlySymbolSet,
|
||||
newModuleSpecifier: string,
|
||||
oldImportId: Identifier,
|
||||
oldImportNode: SupportedImport,
|
||||
quotePreference: QuotePreference
|
||||
): void {
|
||||
const preferredNewNamespaceName = codefix.moduleSpecifierToValidIdentifier(newModuleSpecifier, ScriptTarget.ESNext);
|
||||
let needUniqueName = false;
|
||||
const toChange: Identifier[] = [];
|
||||
FindAllReferences.Core.eachSymbolReferenceInFile(oldImportId, checker, sourceFile, ref => {
|
||||
if (!isPropertyAccessExpression(ref.parent)) return;
|
||||
needUniqueName = needUniqueName || !!checker.resolveName(preferredNewNamespaceName, ref, SymbolFlags.All, /*excludeGlobals*/ true);
|
||||
if (movedSymbols.has(checker.getSymbolAtLocation(ref.parent.name)!)) {
|
||||
toChange.push(ref);
|
||||
}
|
||||
});
|
||||
|
||||
if (toChange.length) {
|
||||
const newNamespaceName = needUniqueName ? getUniqueName(preferredNewNamespaceName, sourceFile) : preferredNewNamespaceName;
|
||||
for (const ref of toChange) {
|
||||
changes.replaceNode(sourceFile, ref, factory.createIdentifier(newNamespaceName));
|
||||
}
|
||||
changes.insertNodeAfter(sourceFile, oldImportNode, updateNamespaceLikeImportNode(oldImportNode, preferredNewNamespaceName, newModuleSpecifier, quotePreference));
|
||||
}
|
||||
}
|
||||
|
||||
function updateNamespaceLikeImportNode(node: SupportedImport, newNamespaceName: string, newModuleSpecifier: string, quotePreference: QuotePreference): Node {
|
||||
const newNamespaceId = factory.createIdentifier(newNamespaceName);
|
||||
const newModuleString = makeStringLiteral(newModuleSpecifier, quotePreference);
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return factory.createImportDeclaration(
|
||||
/*modifiers*/ undefined,
|
||||
factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamespaceImport(newNamespaceId)),
|
||||
newModuleString,
|
||||
/*assertClause*/ undefined);
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return factory.createImportEqualsDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, newNamespaceId, factory.createExternalModuleReference(newModuleString));
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return factory.createVariableDeclaration(newNamespaceId, /*exclamationToken*/ undefined, /*type*/ undefined, createRequireCall(newModuleString));
|
||||
default:
|
||||
return Debug.assertNever(node, `Unexpected node kind ${(node as SupportedImport).kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
function moduleSpecifierFromImport(i: SupportedImport): StringLiteralLike {
|
||||
return (i.kind === SyntaxKind.ImportDeclaration ? i.moduleSpecifier
|
||||
: i.kind === SyntaxKind.ImportEqualsDeclaration ? i.moduleReference.expression
|
||||
: i.initializer.arguments[0]);
|
||||
}
|
||||
|
||||
function forEachImportInStatement(statement: Statement, cb: (importNode: SupportedImport) => void): void {
|
||||
if (isImportDeclaration(statement)) {
|
||||
if (isStringLiteral(statement.moduleSpecifier)) cb(statement as SupportedImport);
|
||||
}
|
||||
else if (isImportEqualsDeclaration(statement)) {
|
||||
if (isExternalModuleReference(statement.moduleReference) && isStringLiteralLike(statement.moduleReference.expression)) {
|
||||
cb(statement as SupportedImport);
|
||||
}
|
||||
}
|
||||
else if (isVariableStatement(statement)) {
|
||||
for (const decl of statement.declarationList.declarations) {
|
||||
if (decl.initializer && isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true)) {
|
||||
cb(decl as SupportedImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SupportedImport =
|
||||
| ImportDeclaration & { moduleSpecifier: StringLiteralLike }
|
||||
| ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteralLike } }
|
||||
| VariableDeclaration & { initializer: RequireOrImportCall };
|
||||
type SupportedImportStatement =
|
||||
| ImportDeclaration
|
||||
| ImportEqualsDeclaration
|
||||
| VariableStatement;
|
||||
|
||||
function createOldFileImportsFromNewFile(
|
||||
sourceFile: SourceFile,
|
||||
newFileNeedExport: ReadonlySymbolSet,
|
||||
newFileNameWithExtension: string,
|
||||
program: Program,
|
||||
host: LanguageServiceHost,
|
||||
useEs6Imports: boolean,
|
||||
quotePreference: QuotePreference
|
||||
): AnyImportOrRequireStatement | undefined {
|
||||
let defaultImport: Identifier | undefined;
|
||||
const imports: string[] = [];
|
||||
newFileNeedExport.forEach(symbol => {
|
||||
if (symbol.escapedName === InternalSymbolName.Default) {
|
||||
defaultImport = factory.createIdentifier(symbolNameNoDefault(symbol)!); // TODO: GH#18217
|
||||
}
|
||||
else {
|
||||
imports.push(symbol.name);
|
||||
}
|
||||
});
|
||||
return makeImportOrRequire(sourceFile, defaultImport, imports, newFileNameWithExtension, program, host, useEs6Imports, quotePreference);
|
||||
}
|
||||
|
||||
function makeImportOrRequire(
|
||||
sourceFile: SourceFile,
|
||||
defaultImport: Identifier | undefined,
|
||||
imports: readonly string[],
|
||||
newFileNameWithExtension: string,
|
||||
program: Program,
|
||||
host: LanguageServiceHost,
|
||||
useEs6Imports: boolean,
|
||||
quotePreference: QuotePreference
|
||||
): AnyImportOrRequireStatement | undefined {
|
||||
const pathToNewFile = resolvePath(getDirectoryPath(sourceFile.path), newFileNameWithExtension);
|
||||
const pathToNewFileWithCorrectExtension = getModuleSpecifier(program.getCompilerOptions(), sourceFile, sourceFile.path, pathToNewFile, createModuleSpecifierResolutionHost(program, host));
|
||||
|
||||
if (useEs6Imports) {
|
||||
const specifiers = imports.map(i => factory.createImportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, factory.createIdentifier(i)));
|
||||
return makeImportIfNecessary(defaultImport, specifiers, pathToNewFileWithCorrectExtension, quotePreference);
|
||||
}
|
||||
else {
|
||||
Debug.assert(!defaultImport, "No default import should exist"); // If there's a default export, it should have been an es6 module.
|
||||
const bindingElements = imports.map(i => factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, i));
|
||||
return bindingElements.length
|
||||
? makeVariableStatement(factory.createObjectBindingPattern(bindingElements), /*type*/ undefined, createRequireCall(makeStringLiteral(pathToNewFileWithCorrectExtension, quotePreference))) as RequireVariableStatement
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function makeVariableStatement(name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined, flags: NodeFlags = NodeFlags.Const) {
|
||||
return factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(name, /*exclamationToken*/ undefined, type, initializer)], flags));
|
||||
}
|
||||
|
||||
function createRequireCall(moduleSpecifier: StringLiteralLike): CallExpression {
|
||||
return factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [moduleSpecifier]);
|
||||
}
|
||||
|
||||
function addExports(sourceFile: SourceFile, toMove: readonly Statement[], needExport: ReadonlySymbolSet, useEs6Exports: boolean): readonly Statement[] {
|
||||
return flatMap(toMove, statement => {
|
||||
if (isTopLevelDeclarationStatement(statement) &&
|
||||
!isExported(sourceFile, statement, useEs6Exports) &&
|
||||
forEachTopLevelDeclaration(statement, d => needExport.has(Debug.checkDefined(tryCast(d, canHaveSymbol)?.symbol)))) {
|
||||
const exports = addExport(statement, useEs6Exports);
|
||||
if (exports) return exports;
|
||||
}
|
||||
return statement;
|
||||
});
|
||||
}
|
||||
|
||||
function deleteUnusedImports(sourceFile: SourceFile, importDecl: SupportedImport, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean): void {
|
||||
switch (importDecl.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
deleteUnusedImportsInDeclaration(sourceFile, importDecl, changes, isUnused);
|
||||
break;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
if (isUnused(importDecl.name)) {
|
||||
changes.delete(sourceFile, importDecl);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
deleteUnusedImportsInVariableDeclaration(sourceFile, importDecl, changes, isUnused);
|
||||
break;
|
||||
default:
|
||||
Debug.assertNever(importDecl, `Unexpected import decl kind ${(importDecl as SupportedImport).kind}`);
|
||||
}
|
||||
}
|
||||
function deleteUnusedImportsInDeclaration(sourceFile: SourceFile, importDecl: ImportDeclaration, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean): void {
|
||||
if (!importDecl.importClause) return;
|
||||
const { name, namedBindings } = importDecl.importClause;
|
||||
const defaultUnused = !name || isUnused(name);
|
||||
const namedBindingsUnused = !namedBindings ||
|
||||
(namedBindings.kind === SyntaxKind.NamespaceImport ? isUnused(namedBindings.name) : namedBindings.elements.length !== 0 && namedBindings.elements.every(e => isUnused(e.name)));
|
||||
if (defaultUnused && namedBindingsUnused) {
|
||||
changes.delete(sourceFile, importDecl);
|
||||
}
|
||||
else {
|
||||
if (name && defaultUnused) {
|
||||
changes.delete(sourceFile, name);
|
||||
}
|
||||
if (namedBindings) {
|
||||
if (namedBindingsUnused) {
|
||||
changes.replaceNode(
|
||||
sourceFile,
|
||||
importDecl.importClause,
|
||||
factory.updateImportClause(importDecl.importClause, importDecl.importClause.isTypeOnly, name, /*namedBindings*/ undefined)
|
||||
);
|
||||
}
|
||||
else if (namedBindings.kind === SyntaxKind.NamedImports) {
|
||||
for (const element of namedBindings.elements) {
|
||||
if (isUnused(element.name)) changes.delete(sourceFile, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function deleteUnusedImportsInVariableDeclaration(sourceFile: SourceFile, varDecl: VariableDeclaration, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean) {
|
||||
const { name } = varDecl;
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
if (isUnused(name)) {
|
||||
if (varDecl.initializer && isRequireCall(varDecl.initializer, /*requireStringLiteralLikeArgument*/ true)) {
|
||||
changes.delete(sourceFile,
|
||||
isVariableDeclarationList(varDecl.parent) && length(varDecl.parent.declarations) === 1 ? varDecl.parent.parent : varDecl);
|
||||
}
|
||||
else {
|
||||
changes.delete(sourceFile, name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
break;
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
if (name.elements.every(e => isIdentifier(e.name) && isUnused(e.name))) {
|
||||
changes.delete(sourceFile,
|
||||
isVariableDeclarationList(varDecl.parent) && varDecl.parent.declarations.length === 1 ? varDecl.parent.parent : varDecl);
|
||||
}
|
||||
else {
|
||||
for (const element of name.elements) {
|
||||
if (isIdentifier(element.name) && isUnused(element.name)) {
|
||||
changes.delete(sourceFile, element.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getNewFileImportsAndAddExportInOldFile(
|
||||
oldFile: SourceFile,
|
||||
importsToCopy: ReadonlySymbolSet,
|
||||
newFileImportsFromOldFile: ReadonlySymbolSet,
|
||||
importsToCopy: Map<Symbol, boolean>,
|
||||
newFileImportsFromOldFile: Set<Symbol>,
|
||||
changes: textChanges.ChangeTracker,
|
||||
checker: TypeChecker,
|
||||
program: Program,
|
||||
@@ -640,379 +180,3 @@ function getNewFileImportsAndAddExportInOldFile(
|
||||
append(copiedOldImports, makeImportOrRequire(oldFile, oldFileDefault, oldFileNamedImports, getBaseFileName(oldFile.fileName), program, host, useEsModuleSyntax, quotePreference));
|
||||
return copiedOldImports;
|
||||
}
|
||||
|
||||
function makeUniqueFilename(proposedFilename: string, extension: string, inDirectory: string, host: LanguageServiceHost): string {
|
||||
let newFilename = proposedFilename;
|
||||
for (let i = 1; ; i++) {
|
||||
const name = combinePaths(inDirectory, newFilename + extension);
|
||||
if (!host.fileExists(name)) return newFilename;
|
||||
newFilename = `${proposedFilename}.${i}`;
|
||||
}
|
||||
}
|
||||
|
||||
function inferNewFilename(importsFromNewFile: ReadonlySymbolSet, movedSymbols: ReadonlySymbolSet): string {
|
||||
return importsFromNewFile.forEachEntry(symbolNameNoDefault) || movedSymbols.forEachEntry(symbolNameNoDefault) || "newFile";
|
||||
}
|
||||
|
||||
interface UsageInfo {
|
||||
// Symbols whose declarations are moved from the old file to the new file.
|
||||
readonly movedSymbols: ReadonlySymbolSet;
|
||||
|
||||
// Symbols declared in the old file that must be imported by the new file. (May not already be exported.)
|
||||
readonly newFileImportsFromOldFile: ReadonlySymbolSet;
|
||||
// Subset of movedSymbols that are still used elsewhere in the old file and must be imported back.
|
||||
readonly oldFileImportsFromNewFile: ReadonlySymbolSet;
|
||||
|
||||
readonly oldImportsNeededByNewFile: ReadonlySymbolSet;
|
||||
// Subset of oldImportsNeededByNewFile that are will no longer be used in the old file.
|
||||
readonly unusedImportsFromOldFile: ReadonlySymbolSet;
|
||||
}
|
||||
function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[], checker: TypeChecker): UsageInfo {
|
||||
const movedSymbols = new SymbolSet();
|
||||
const oldImportsNeededByNewFile = new SymbolSet();
|
||||
const newFileImportsFromOldFile = new SymbolSet();
|
||||
|
||||
const containsJsx = find(toMove, statement => !!(statement.transformFlags & TransformFlags.ContainsJsx));
|
||||
const jsxNamespaceSymbol = getJsxNamespaceSymbol(containsJsx);
|
||||
if (jsxNamespaceSymbol) { // Might not exist (e.g. in non-compiling code)
|
||||
oldImportsNeededByNewFile.add(jsxNamespaceSymbol);
|
||||
}
|
||||
|
||||
for (const statement of toMove) {
|
||||
forEachTopLevelDeclaration(statement, decl => {
|
||||
movedSymbols.add(Debug.checkDefined(isExpressionStatement(decl) ? checker.getSymbolAtLocation(decl.expression.left) : decl.symbol, "Need a symbol here"));
|
||||
});
|
||||
}
|
||||
for (const statement of toMove) {
|
||||
forEachReference(statement, checker, symbol => {
|
||||
if (!symbol.declarations) return;
|
||||
for (const decl of symbol.declarations) {
|
||||
if (isInImport(decl)) {
|
||||
oldImportsNeededByNewFile.add(symbol);
|
||||
}
|
||||
else if (isTopLevelDeclaration(decl) && sourceFileOfTopLevelDeclaration(decl) === oldFile && !movedSymbols.has(symbol)) {
|
||||
newFileImportsFromOldFile.add(symbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const unusedImportsFromOldFile = oldImportsNeededByNewFile.clone();
|
||||
|
||||
const oldFileImportsFromNewFile = new SymbolSet();
|
||||
for (const statement of oldFile.statements) {
|
||||
if (contains(toMove, statement)) continue;
|
||||
|
||||
// jsxNamespaceSymbol will only be set iff it is in oldImportsNeededByNewFile.
|
||||
if (jsxNamespaceSymbol && !!(statement.transformFlags & TransformFlags.ContainsJsx)) {
|
||||
unusedImportsFromOldFile.delete(jsxNamespaceSymbol);
|
||||
}
|
||||
|
||||
forEachReference(statement, checker, symbol => {
|
||||
if (movedSymbols.has(symbol)) oldFileImportsFromNewFile.add(symbol);
|
||||
unusedImportsFromOldFile.delete(symbol);
|
||||
});
|
||||
}
|
||||
|
||||
return { movedSymbols, newFileImportsFromOldFile, oldFileImportsFromNewFile, oldImportsNeededByNewFile, unusedImportsFromOldFile };
|
||||
|
||||
function getJsxNamespaceSymbol(containsJsx: Node | undefined) {
|
||||
if (containsJsx === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const jsxNamespace = checker.getJsxNamespace(containsJsx);
|
||||
|
||||
// Strictly speaking, this could resolve to a symbol other than the JSX namespace.
|
||||
// This will produce erroneous output (probably, an incorrectly copied import) but
|
||||
// is expected to be very rare and easily reversible.
|
||||
const jsxNamespaceSymbol = checker.resolveName(jsxNamespace, containsJsx, SymbolFlags.Namespace, /*excludeGlobals*/ true);
|
||||
|
||||
return !!jsxNamespaceSymbol && some(jsxNamespaceSymbol.declarations, isInImport)
|
||||
? jsxNamespaceSymbol
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Below should all be utilities
|
||||
|
||||
function isInImport(decl: Declaration) {
|
||||
switch (decl.kind) {
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportClause:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return true;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return isVariableDeclarationInImport(decl as VariableDeclaration);
|
||||
case SyntaxKind.BindingElement:
|
||||
return isVariableDeclaration(decl.parent.parent) && isVariableDeclarationInImport(decl.parent.parent);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function isVariableDeclarationInImport(decl: VariableDeclaration) {
|
||||
return isSourceFile(decl.parent.parent.parent) &&
|
||||
!!decl.initializer && isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true);
|
||||
}
|
||||
|
||||
function filterImport(i: SupportedImport, moduleSpecifier: StringLiteralLike, keep: (name: Identifier) => boolean): SupportedImportStatement | undefined {
|
||||
switch (i.kind) {
|
||||
case SyntaxKind.ImportDeclaration: {
|
||||
const clause = i.importClause;
|
||||
if (!clause) return undefined;
|
||||
const defaultImport = clause.name && keep(clause.name) ? clause.name : undefined;
|
||||
const namedBindings = clause.namedBindings && filterNamedBindings(clause.namedBindings, keep);
|
||||
return defaultImport || namedBindings
|
||||
? factory.createImportDeclaration(/*modifiers*/ undefined, factory.createImportClause(clause.isTypeOnly, defaultImport, namedBindings), moduleSpecifier, /*assertClause*/ undefined)
|
||||
: undefined;
|
||||
}
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return keep(i.name) ? i : undefined;
|
||||
case SyntaxKind.VariableDeclaration: {
|
||||
const name = filterBindingName(i.name, keep);
|
||||
return name ? makeVariableStatement(name, i.type, createRequireCall(moduleSpecifier), i.parent.flags) : undefined;
|
||||
}
|
||||
default:
|
||||
return Debug.assertNever(i, `Unexpected import kind ${(i as SupportedImport).kind}`);
|
||||
}
|
||||
}
|
||||
function filterNamedBindings(namedBindings: NamedImportBindings, keep: (name: Identifier) => boolean): NamedImportBindings | undefined {
|
||||
if (namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||
return keep(namedBindings.name) ? namedBindings : undefined;
|
||||
}
|
||||
else {
|
||||
const newElements = namedBindings.elements.filter(e => keep(e.name));
|
||||
return newElements.length ? factory.createNamedImports(newElements) : undefined;
|
||||
}
|
||||
}
|
||||
function filterBindingName(name: BindingName, keep: (name: Identifier) => boolean): BindingName | undefined {
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return keep(name) ? name : undefined;
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return name;
|
||||
case SyntaxKind.ObjectBindingPattern: {
|
||||
// We can't handle nested destructurings or property names well here, so just copy them all.
|
||||
const newElements = name.elements.filter(prop => prop.propertyName || !isIdentifier(prop.name) || keep(prop.name));
|
||||
return newElements.length ? factory.createObjectBindingPattern(newElements) : undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function forEachReference(node: Node, checker: TypeChecker, onReference: (s: Symbol) => void) {
|
||||
node.forEachChild(function cb(node) {
|
||||
if (isIdentifier(node) && !isDeclarationName(node)) {
|
||||
const sym = checker.getSymbolAtLocation(node);
|
||||
if (sym) onReference(sym);
|
||||
}
|
||||
else {
|
||||
node.forEachChild(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface ReadonlySymbolSet {
|
||||
size(): number;
|
||||
has(symbol: Symbol): boolean;
|
||||
forEach(cb: (symbol: Symbol) => void): void;
|
||||
forEachEntry<T>(cb: (symbol: Symbol) => T | undefined): T | undefined;
|
||||
}
|
||||
|
||||
class SymbolSet implements ReadonlySymbolSet {
|
||||
private map = new Map<string, Symbol>();
|
||||
add(symbol: Symbol): void {
|
||||
this.map.set(String(getSymbolId(symbol)), symbol);
|
||||
}
|
||||
has(symbol: Symbol): boolean {
|
||||
return this.map.has(String(getSymbolId(symbol)));
|
||||
}
|
||||
delete(symbol: Symbol): void {
|
||||
this.map.delete(String(getSymbolId(symbol)));
|
||||
}
|
||||
forEach(cb: (symbol: Symbol) => void): void {
|
||||
this.map.forEach(cb);
|
||||
}
|
||||
forEachEntry<T>(cb: (symbol: Symbol) => T | undefined): T | undefined {
|
||||
return forEachEntry(this.map, cb);
|
||||
}
|
||||
clone(): SymbolSet {
|
||||
const clone = new SymbolSet();
|
||||
copyEntries(this.map, clone.map);
|
||||
return clone;
|
||||
}
|
||||
size() {
|
||||
return this.map.size;
|
||||
}
|
||||
}
|
||||
|
||||
type TopLevelExpressionStatement = ExpressionStatement & { expression: BinaryExpression & { left: PropertyAccessExpression } }; // 'exports.x = ...'
|
||||
type NonVariableTopLevelDeclaration =
|
||||
| FunctionDeclaration
|
||||
| ClassDeclaration
|
||||
| EnumDeclaration
|
||||
| TypeAliasDeclaration
|
||||
| InterfaceDeclaration
|
||||
| ModuleDeclaration
|
||||
| TopLevelExpressionStatement
|
||||
| ImportEqualsDeclaration;
|
||||
type TopLevelDeclarationStatement = NonVariableTopLevelDeclaration | VariableStatement;
|
||||
interface TopLevelVariableDeclaration extends VariableDeclaration { parent: VariableDeclarationList & { parent: VariableStatement; }; }
|
||||
type TopLevelDeclaration = NonVariableTopLevelDeclaration | TopLevelVariableDeclaration | BindingElement;
|
||||
function isTopLevelDeclaration(node: Node): node is TopLevelDeclaration {
|
||||
return isNonVariableTopLevelDeclaration(node) && isSourceFile(node.parent) || isVariableDeclaration(node) && isSourceFile(node.parent.parent.parent);
|
||||
}
|
||||
|
||||
function sourceFileOfTopLevelDeclaration(node: TopLevelDeclaration): Node {
|
||||
return isVariableDeclaration(node) ? node.parent.parent.parent : node.parent;
|
||||
}
|
||||
|
||||
function isTopLevelDeclarationStatement(node: Node): node is TopLevelDeclarationStatement {
|
||||
Debug.assert(isSourceFile(node.parent), "Node parent should be a SourceFile");
|
||||
return isNonVariableTopLevelDeclaration(node) || isVariableStatement(node);
|
||||
}
|
||||
|
||||
function isNonVariableTopLevelDeclaration(node: Node): node is NonVariableTopLevelDeclaration {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function forEachTopLevelDeclaration<T>(statement: Statement, cb: (node: TopLevelDeclaration) => T): T | undefined {
|
||||
switch (statement.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return cb(statement as FunctionDeclaration | ClassDeclaration | EnumDeclaration | ModuleDeclaration | TypeAliasDeclaration | InterfaceDeclaration | ImportEqualsDeclaration);
|
||||
|
||||
case SyntaxKind.VariableStatement:
|
||||
return firstDefined((statement as VariableStatement).declarationList.declarations, decl => forEachTopLevelDeclarationInBindingName(decl.name, cb));
|
||||
|
||||
case SyntaxKind.ExpressionStatement: {
|
||||
const { expression } = statement as ExpressionStatement;
|
||||
return isBinaryExpression(expression) && getAssignmentDeclarationKind(expression) === AssignmentDeclarationKind.ExportsProperty
|
||||
? cb(statement as TopLevelExpressionStatement)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
function forEachTopLevelDeclarationInBindingName<T>(name: BindingName, cb: (node: TopLevelDeclaration) => T): T | undefined {
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return cb(cast(name.parent, (x): x is TopLevelVariableDeclaration | BindingElement => isVariableDeclaration(x) || isBindingElement(x)));
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
return firstDefined(name.elements, em => isOmittedExpression(em) ? undefined : forEachTopLevelDeclarationInBindingName(em.name, cb));
|
||||
default:
|
||||
return Debug.assertNever(name, `Unexpected name kind ${(name as BindingName).kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
function nameOfTopLevelDeclaration(d: TopLevelDeclaration): Identifier | undefined {
|
||||
return isExpressionStatement(d) ? tryCast(d.expression.left.name, isIdentifier) : tryCast(d.name, isIdentifier);
|
||||
}
|
||||
|
||||
function getTopLevelDeclarationStatement(d: TopLevelDeclaration): TopLevelDeclarationStatement {
|
||||
switch (d.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return d.parent.parent;
|
||||
case SyntaxKind.BindingElement:
|
||||
return getTopLevelDeclarationStatement(
|
||||
cast(d.parent.parent, (p): p is TopLevelVariableDeclaration | BindingElement => isVariableDeclaration(p) || isBindingElement(p)));
|
||||
default:
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
function addExportToChanges(sourceFile: SourceFile, decl: TopLevelDeclarationStatement, name: Identifier, changes: textChanges.ChangeTracker, useEs6Exports: boolean): void {
|
||||
if (isExported(sourceFile, decl, useEs6Exports, name)) return;
|
||||
if (useEs6Exports) {
|
||||
if (!isExpressionStatement(decl)) changes.insertExportModifier(sourceFile, decl);
|
||||
}
|
||||
else {
|
||||
const names = getNamesToExportInCommonJS(decl);
|
||||
if (names.length !== 0) changes.insertNodesAfter(sourceFile, decl, names.map(createExportAssignment));
|
||||
}
|
||||
}
|
||||
|
||||
function isExported(sourceFile: SourceFile, decl: TopLevelDeclarationStatement, useEs6Exports: boolean, name?: Identifier): boolean {
|
||||
if (useEs6Exports) {
|
||||
return !isExpressionStatement(decl) && hasSyntacticModifier(decl, ModifierFlags.Export) || !!(name && sourceFile.symbol.exports?.has(name.escapedText));
|
||||
}
|
||||
return !!sourceFile.symbol && !!sourceFile.symbol.exports &&
|
||||
getNamesToExportInCommonJS(decl).some(name => sourceFile.symbol.exports!.has(escapeLeadingUnderscores(name)));
|
||||
}
|
||||
|
||||
function addExport(decl: TopLevelDeclarationStatement, useEs6Exports: boolean): readonly Statement[] | undefined {
|
||||
return useEs6Exports ? [addEs6Export(decl)] : addCommonjsExport(decl);
|
||||
}
|
||||
function addEs6Export(d: TopLevelDeclarationStatement): TopLevelDeclarationStatement {
|
||||
const modifiers = canHaveModifiers(d) ? concatenate([factory.createModifier(SyntaxKind.ExportKeyword)], getModifiers(d)) : undefined;
|
||||
switch (d.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
return factory.updateFunctionDeclaration(d, modifiers, d.asteriskToken, d.name, d.typeParameters, d.parameters, d.type, d.body);
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
const decorators = canHaveDecorators(d) ? getDecorators(d) : undefined;
|
||||
return factory.updateClassDeclaration(d, concatenate<ModifierLike>(decorators, modifiers), d.name, d.typeParameters, d.heritageClauses, d.members);
|
||||
case SyntaxKind.VariableStatement:
|
||||
return factory.updateVariableStatement(d, modifiers, d.declarationList);
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return factory.updateModuleDeclaration(d, modifiers, d.name, d.body);
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return factory.updateEnumDeclaration(d, modifiers, d.name, d.members);
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return factory.updateTypeAliasDeclaration(d, modifiers, d.name, d.typeParameters, d.type);
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return factory.updateInterfaceDeclaration(d, modifiers, d.name, d.typeParameters, d.heritageClauses, d.members);
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return factory.updateImportEqualsDeclaration(d, modifiers, d.isTypeOnly, d.name, d.moduleReference);
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return Debug.fail(); // Shouldn't try to add 'export' keyword to `exports.x = ...`
|
||||
default:
|
||||
return Debug.assertNever(d, `Unexpected declaration kind ${(d as DeclarationStatement).kind}`);
|
||||
}
|
||||
}
|
||||
function addCommonjsExport(decl: TopLevelDeclarationStatement): readonly Statement[] | undefined {
|
||||
return [decl, ...getNamesToExportInCommonJS(decl).map(createExportAssignment)];
|
||||
}
|
||||
function getNamesToExportInCommonJS(decl: TopLevelDeclarationStatement): readonly string[] {
|
||||
switch (decl.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return [decl.name!.text]; // TODO: GH#18217
|
||||
case SyntaxKind.VariableStatement:
|
||||
return mapDefined(decl.declarationList.declarations, d => isIdentifier(d.name) ? d.name.text : undefined);
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return emptyArray;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return Debug.fail("Can't export an ExpressionStatement"); // Shouldn't try to add 'export' keyword to `exports.x = ...`
|
||||
default:
|
||||
return Debug.assertNever(decl, `Unexpected decl kind ${(decl as TopLevelDeclarationStatement).kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates `exports.x = x;` */
|
||||
function createExportAssignment(name: string): Statement {
|
||||
return factory.createExpressionStatement(
|
||||
factory.createBinaryExpression(
|
||||
factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(name)),
|
||||
SyntaxKind.EqualsToken,
|
||||
factory.createIdentifier(name)));
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ import {
|
||||
EntityName,
|
||||
equateValues,
|
||||
ExportDeclaration,
|
||||
Extension,
|
||||
extensionFromPath,
|
||||
FileReference,
|
||||
FileTextChanges,
|
||||
filter,
|
||||
@@ -86,6 +88,7 @@ import {
|
||||
getAdjustedRenameLocation,
|
||||
getAllSuperTypeNodes,
|
||||
getAssignmentDeclarationKind,
|
||||
getBaseFileName,
|
||||
GetCompletionsAtPositionOptions,
|
||||
getContainerNode,
|
||||
getDefaultLibFileName,
|
||||
@@ -105,6 +108,7 @@ import {
|
||||
getNonAssignedNameOfDeclaration,
|
||||
getNormalizedAbsolutePath,
|
||||
getObjectFlags,
|
||||
getQuotePreference,
|
||||
getScriptKind,
|
||||
getSetExternalModuleIndicator,
|
||||
getSnapshotText,
|
||||
@@ -134,6 +138,7 @@ import {
|
||||
InlayHints,
|
||||
InlayHintsContext,
|
||||
insertSorted,
|
||||
InteractiveRefactorArguments,
|
||||
InterfaceType,
|
||||
IntersectionType,
|
||||
isArray,
|
||||
@@ -276,6 +281,7 @@ import {
|
||||
SourceFile,
|
||||
SourceFileLike,
|
||||
SourceMapSource,
|
||||
startsWith,
|
||||
Statement,
|
||||
stringContains,
|
||||
StringLiteral,
|
||||
@@ -319,6 +325,7 @@ import {
|
||||
} from "./_namespaces/ts";
|
||||
import * as NavigateTo from "./_namespaces/ts.NavigateTo";
|
||||
import * as NavigationBar from "./_namespaces/ts.NavigationBar";
|
||||
import { createNewFileName } from "./_namespaces/ts.refactor";
|
||||
import * as classifier from "./classifier";
|
||||
import * as classifier2020 from "./classifier2020";
|
||||
|
||||
@@ -1634,6 +1641,7 @@ export function createLanguageService(
|
||||
// Get a fresh cache of the host information
|
||||
const newSettings = host.getCompilationSettings() || getDefaultCompilerOptions();
|
||||
const hasInvalidatedResolutions: HasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse;
|
||||
const hasInvalidatedLibResolutions = maybeBind(host, host.hasInvalidatedLibResolutions) || returnFalse;
|
||||
const hasChangedAutomaticTypeDirectiveNames = maybeBind(host, host.hasChangedAutomaticTypeDirectiveNames);
|
||||
const projectReferences = host.getProjectReferences?.();
|
||||
let parsedCommandLines: Map<Path, ParsedCommandLine | false> | undefined;
|
||||
@@ -1666,6 +1674,7 @@ export function createLanguageService(
|
||||
onReleaseOldSourceFile,
|
||||
onReleaseParsedCommandLine,
|
||||
hasInvalidatedResolutions,
|
||||
hasInvalidatedLibResolutions,
|
||||
hasChangedAutomaticTypeDirectiveNames,
|
||||
trace: maybeBind(host, host.trace),
|
||||
resolveModuleNames: maybeBind(host, host.resolveModuleNames),
|
||||
@@ -1674,6 +1683,7 @@ export function createLanguageService(
|
||||
resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives),
|
||||
resolveModuleNameLiterals: maybeBind(host, host.resolveModuleNameLiterals),
|
||||
resolveTypeReferenceDirectiveReferences: maybeBind(host, host.resolveTypeReferenceDirectiveReferences),
|
||||
resolveLibrary: maybeBind(host, host.resolveLibrary),
|
||||
useSourceOfProjectReferenceRedirect: maybeBind(host, host.useSourceOfProjectReferenceRedirect),
|
||||
getParsedCommandLine,
|
||||
};
|
||||
@@ -1705,7 +1715,7 @@ export function createLanguageService(
|
||||
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
|
||||
|
||||
// If the program is already up-to-date, we can reuse it
|
||||
if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), fileName => compilerHost!.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
|
||||
if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), fileName => compilerHost!.fileExists(fileName), hasInvalidatedResolutions, hasInvalidatedLibResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2125,7 +2135,7 @@ export function createLanguageService(
|
||||
return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
|
||||
}
|
||||
|
||||
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined {
|
||||
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: UserPreferences | boolean): RenameLocation[] | undefined {
|
||||
synchronizeHostData();
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position));
|
||||
@@ -2142,8 +2152,10 @@ export function createLanguageService(
|
||||
});
|
||||
}
|
||||
else {
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences ?? emptyOptions);
|
||||
const providePrefixAndSuffixTextForRename = typeof preferences === "boolean" ? preferences : preferences?.providePrefixAndSuffixTextForRename;
|
||||
return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, use: FindAllReferences.FindReferencesUse.Rename },
|
||||
(entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false));
|
||||
(entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false, quotePreference));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2492,6 +2504,9 @@ export function createLanguageService(
|
||||
const token = findPrecedingToken(position, sourceFile);
|
||||
if (!token || token.parent.kind === SyntaxKind.SourceFile) return undefined;
|
||||
|
||||
// matches more than valid tag names to allow linked editing when typing is in progress or tag name is incomplete
|
||||
const jsxTagWordPattern = "[a-zA-Z0-9:\\-\\._$]*";
|
||||
|
||||
if (isJsxFragment(token.parent.parent)) {
|
||||
const openFragment = token.parent.parent.openingFragment;
|
||||
const closeFragment = token.parent.parent.closingFragment;
|
||||
@@ -2503,7 +2518,10 @@ export function createLanguageService(
|
||||
// only allows linked editing right after opening bracket: <| ></| >
|
||||
if ((position !== openPos) && (position !== closePos)) return undefined;
|
||||
|
||||
return { ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }] };
|
||||
return {
|
||||
ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }],
|
||||
wordPattern: jsxTagWordPattern,
|
||||
};
|
||||
}
|
||||
else {
|
||||
// determines if the cursor is in an element tag
|
||||
@@ -2534,6 +2552,7 @@ export function createLanguageService(
|
||||
|
||||
return {
|
||||
ranges: [{ start: openTagStart, length: openTagEnd - openTagStart }, { start: closeTagStart, length: closeTagEnd - closeTagStart }],
|
||||
wordPattern: jsxTagWordPattern,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2971,10 +2990,23 @@ export function createLanguageService(
|
||||
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
|
||||
}
|
||||
|
||||
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] {
|
||||
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[] {
|
||||
synchronizeHostData();
|
||||
const file = getValidSourceFile(fileName);
|
||||
return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind));
|
||||
return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind), includeInteractiveActions);
|
||||
}
|
||||
|
||||
function getMoveToRefactoringFileSuggestions(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): { newFileName: string, files: string[] } {
|
||||
synchronizeHostData();
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const allFiles = Debug.checkDefined(program.getSourceFiles());
|
||||
const extension = extensionFromPath(fileName);
|
||||
const files = mapDefined(allFiles, file => !program?.isSourceFileFromExternalLibrary(sourceFile) &&
|
||||
!(sourceFile === getValidSourceFile(file.fileName) || extension === Extension.Ts && extensionFromPath(file.fileName) === Extension.Dts || extension === Extension.Dts && startsWith(getBaseFileName(file.fileName), "lib.") && extensionFromPath(file.fileName) === Extension.Dts)
|
||||
&& extension === extensionFromPath(file.fileName) ? file.fileName : undefined);
|
||||
|
||||
const newFileName = createNewFileName(sourceFile, program, getRefactorContext(sourceFile, positionOrRange, preferences, emptyOptions), host);
|
||||
return { newFileName, files };
|
||||
}
|
||||
|
||||
function getEditsForRefactor(
|
||||
@@ -2984,10 +3016,11 @@ export function createLanguageService(
|
||||
refactorName: string,
|
||||
actionName: string,
|
||||
preferences: UserPreferences = emptyOptions,
|
||||
interactiveRefactorArguments?: InteractiveRefactorArguments,
|
||||
): RefactorEditInfo | undefined {
|
||||
synchronizeHostData();
|
||||
const file = getValidSourceFile(fileName);
|
||||
return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName);
|
||||
return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName, interactiveRefactorArguments);
|
||||
}
|
||||
|
||||
function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
|
||||
@@ -3084,6 +3117,7 @@ export function createLanguageService(
|
||||
updateIsDefinitionOfReferencedSymbols,
|
||||
getApplicableRefactors,
|
||||
getEditsForRefactor,
|
||||
getMoveToRefactoringFileSuggestions,
|
||||
toLineColumnOffset,
|
||||
getSourceMapper: () => sourceMapper,
|
||||
clearSourceMapperCache: () => sourceMapper.clearCache(),
|
||||
|
||||
@@ -256,7 +256,7 @@ export interface LanguageServiceShim extends Shim {
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { fileName: string, textSpan: { start: number, length: number } }[]
|
||||
*/
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string;
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: UserPreferences | boolean): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
@@ -952,10 +952,10 @@ class LanguageServiceShimObject extends ShimBase implements LanguageServiceShim
|
||||
);
|
||||
}
|
||||
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): string {
|
||||
return this.forwardJSONCall(
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
|
||||
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments})`,
|
||||
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, preferences)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
isTemplateSpan,
|
||||
isTemplateTail,
|
||||
isTransientSymbol,
|
||||
JsxTagNameExpression,
|
||||
last,
|
||||
lastOrUndefined,
|
||||
ListFormat,
|
||||
@@ -599,7 +600,7 @@ function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node,
|
||||
return children[indexOfOpenerToken + 1];
|
||||
}
|
||||
|
||||
function getExpressionFromInvocation(invocation: CallInvocation | TypeArgsInvocation): Expression {
|
||||
function getExpressionFromInvocation(invocation: CallInvocation | TypeArgsInvocation): Expression | JsxTagNameExpression {
|
||||
return invocation.kind === InvocationKind.Call ? getInvokedExpression(invocation.node) : invocation.called;
|
||||
}
|
||||
|
||||
|
||||
@@ -452,7 +452,7 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
displayParts.push(spacePart());
|
||||
addRange(displayParts, typeToDisplayParts(typeChecker, isConstTypeReference(location.parent) ? typeChecker.getTypeAtLocation(location.parent) : typeChecker.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration, TypeFormatFlags.InTypeAlias));
|
||||
addRange(displayParts, typeToDisplayParts(typeChecker, location.parent && isConstTypeReference(location.parent) ? typeChecker.getTypeAtLocation(location.parent) : typeChecker.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration, TypeFormatFlags.InTypeAlias));
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Enum) {
|
||||
prefixNextMeaning();
|
||||
@@ -535,12 +535,12 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ
|
||||
// don't use symbolFlags since getAliasedSymbol requires the flag on the symbol itself
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
prefixNextMeaning();
|
||||
if (!hasAddedSymbolInfo) {
|
||||
if (!hasAddedSymbolInfo || documentation.length === 0 && tags.length === 0) {
|
||||
const resolvedSymbol = typeChecker.getAliasedSymbol(symbol);
|
||||
if (resolvedSymbol !== symbol && resolvedSymbol.declarations && resolvedSymbol.declarations.length > 0) {
|
||||
const resolvedNode = resolvedSymbol.declarations[0];
|
||||
const declarationName = getNameOfDeclaration(resolvedNode);
|
||||
if (declarationName) {
|
||||
if (declarationName && !hasAddedSymbolInfo) {
|
||||
const isExternalModuleDeclaration =
|
||||
isModuleWithStringLiteralName(resolvedNode) &&
|
||||
hasSyntacticModifier(resolvedNode, ModifierFlags.Ambient);
|
||||
@@ -835,7 +835,6 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ
|
||||
documentation = allSignatures[0].getDocumentationComment(typeChecker);
|
||||
tags = allSignatures[0].getJsDocTags().filter(tag => tag.name !== "deprecated"); // should only include @deprecated JSDoc tag on the first overload (#49368)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined) {
|
||||
|
||||
+39
-24
@@ -10,6 +10,7 @@ import {
|
||||
concatenate,
|
||||
ConstructorDeclaration,
|
||||
contains,
|
||||
createMultiMap,
|
||||
createNodeFactory,
|
||||
createPrinter,
|
||||
createRange,
|
||||
@@ -51,9 +52,11 @@ import {
|
||||
getNewLineKind,
|
||||
getNewLineOrDefaultFromHost,
|
||||
getNodeId,
|
||||
getOriginalNode,
|
||||
getPrecedingNonSpaceCharacterPosition,
|
||||
getScriptKindFromFileName,
|
||||
getShebang,
|
||||
getSourceFileOfNode,
|
||||
getStartPositionOfLine,
|
||||
getTokenAtPosition,
|
||||
getTouchingToken,
|
||||
@@ -116,6 +119,7 @@ import {
|
||||
mapDefined,
|
||||
MethodSignature,
|
||||
Modifier,
|
||||
MultiMap,
|
||||
NamedImportBindings,
|
||||
NamedImports,
|
||||
NamespaceImport,
|
||||
@@ -337,6 +341,11 @@ interface ChangeText extends BaseChange {
|
||||
readonly text: string;
|
||||
}
|
||||
|
||||
interface NewFileInsertion {
|
||||
readonly oldFile?: SourceFile;
|
||||
readonly statements: readonly (Statement | SyntaxKind.NewLineTrivia)[];
|
||||
}
|
||||
|
||||
function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange {
|
||||
return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
||||
}
|
||||
@@ -480,7 +489,7 @@ export function isThisTypeAnnotatable(containingFunction: SignatureDeclaration):
|
||||
/** @internal */
|
||||
export class ChangeTracker {
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: readonly (Statement | SyntaxKind.NewLineTrivia)[] }[] = [];
|
||||
private newFileChanges?: MultiMap<string, NewFileInsertion> ;
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, readonly sourceFile: SourceFile }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
|
||||
|
||||
@@ -622,6 +631,13 @@ export class ChangeTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private insertStatementsInNewFile(fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], oldFile?: SourceFile): void {
|
||||
if (!this.newFileChanges) {
|
||||
this.newFileChanges = createMultiMap<string, NewFileInsertion>();
|
||||
}
|
||||
this.newFileChanges.add(fileName, { oldFile, statements });
|
||||
}
|
||||
|
||||
public insertFirstParameter(sourceFile: SourceFile, parameters: NodeArray<ParameterDeclaration>, newParam: ParameterDeclaration): void {
|
||||
const p0 = firstOrUndefined(parameters);
|
||||
if (p0) {
|
||||
@@ -1128,14 +1144,16 @@ export class ChangeTracker {
|
||||
this.finishDeleteDeclarations();
|
||||
this.finishClassesWithNodesInsertedAtStart();
|
||||
const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, validate);
|
||||
for (const { oldFile, fileName, statements } of this.newFiles) {
|
||||
changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext));
|
||||
if (this.newFileChanges) {
|
||||
this.newFileChanges.forEach((insertions, fileName) => {
|
||||
changes.push(changesToText.newFileChanges(fileName, insertions, this.newLineCharacter, this.formatContext));
|
||||
});
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
public createNewFile(oldFile: SourceFile | undefined, fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[]): void {
|
||||
this.newFiles.push({ oldFile, fileName, statements });
|
||||
this.insertStatementsInNewFile(fileName, statements, oldFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,11 +1225,6 @@ function getMembersOrProperties(node: ClassLikeDeclaration | InterfaceDeclaratio
|
||||
/** @internal */
|
||||
export type ValidateNonFormattedText = (node: Node, text: string) => void;
|
||||
|
||||
/** @internal */
|
||||
export function getNewFileText(statements: readonly Statement[], scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext): string {
|
||||
return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext);
|
||||
}
|
||||
|
||||
namespace changesToText {
|
||||
export function getTextChangesFromChanges(changes: readonly Change[], newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] {
|
||||
return mapDefined(group(changes, c => c.sourceFile.path), changesInFile => {
|
||||
@@ -1227,10 +1240,12 @@ namespace changesToText {
|
||||
|
||||
const textChanges = mapDefined(normalized, c => {
|
||||
const span = createTextSpanFromRange(c.range);
|
||||
const newText = computeNewText(c, sourceFile, newLineCharacter, formatContext, validate);
|
||||
|
||||
const targetSourceFile = c.kind === ChangeKind.ReplaceWithSingleNode ? getSourceFileOfNode(getOriginalNode(c.node)) ?? c.sourceFile :
|
||||
c.kind === ChangeKind.ReplaceWithMultipleNodes ? getSourceFileOfNode(getOriginalNode(c.nodes[0])) ?? c.sourceFile :
|
||||
c.sourceFile;
|
||||
const newText = computeNewText(c, targetSourceFile, sourceFile, newLineCharacter, formatContext, validate);
|
||||
// Filter out redundant changes.
|
||||
if (span.length === newText.length && stringContainsAt(sourceFile.text, newText, span.start)) {
|
||||
if (span.length === newText.length && stringContainsAt(targetSourceFile.text, newText, span.start)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1241,20 +1256,20 @@ namespace changesToText {
|
||||
});
|
||||
}
|
||||
|
||||
export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges {
|
||||
const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext);
|
||||
export function newFileChanges(fileName: string, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges {
|
||||
const text = newFileChangesWorker(getScriptKindFromFileName(fileName), insertions, newLineCharacter, formatContext);
|
||||
return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true };
|
||||
}
|
||||
|
||||
export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], newLineCharacter: string, formatContext: formatting.FormatContext): string {
|
||||
export function newFileChangesWorker(scriptKind: ScriptKind, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: formatting.FormatContext): string {
|
||||
// TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this
|
||||
const nonFormattedText = statements.map(s => s === SyntaxKind.NewLineTrivia ? "" : getNonformattedText(s, oldFile, newLineCharacter).text).join(newLineCharacter);
|
||||
const nonFormattedText = flatMap(insertions, insertion => insertion.statements.map(s => s === SyntaxKind.NewLineTrivia ? "" : getNonformattedText(s, insertion.oldFile, newLineCharacter).text)).join(newLineCharacter);
|
||||
const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind);
|
||||
const changes = formatting.formatDocument(sourceFile, formatContext);
|
||||
return applyChanges(nonFormattedText, changes) + newLineCharacter;
|
||||
}
|
||||
|
||||
function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {
|
||||
function computeNewText(change: Change, targetSourceFile: SourceFile, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {
|
||||
if (change.kind === ChangeKind.Remove) {
|
||||
return "";
|
||||
}
|
||||
@@ -1263,26 +1278,26 @@ namespace changesToText {
|
||||
}
|
||||
|
||||
const { options = {}, range: { pos } } = change;
|
||||
const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, validate);
|
||||
const format = (n: Node) => getFormattedTextOfNode(n, targetSourceFile, sourceFile, pos, options, newLineCharacter, formatContext, validate);
|
||||
const text = change.kind === ChangeKind.ReplaceWithMultipleNodes
|
||||
? change.nodes.map(n => removeSuffix(format(n), newLineCharacter)).join(change.options?.joiner || newLineCharacter)
|
||||
: format(change.node);
|
||||
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
|
||||
const noIndent = (options.indentation !== undefined || getLineStartPositionForPosition(pos, sourceFile) === pos) ? text : text.replace(/^\s+/, "");
|
||||
const noIndent = (options.indentation !== undefined || getLineStartPositionForPosition(pos, targetSourceFile) === pos) ? text : text.replace(/^\s+/, "");
|
||||
return (options.prefix || "") + noIndent
|
||||
+ ((!options.suffix || endsWith(noIndent, options.suffix))
|
||||
? "" : options.suffix);
|
||||
}
|
||||
|
||||
/** Note: this may mutate `nodeIn`. */
|
||||
function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {
|
||||
const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter);
|
||||
function getFormattedTextOfNode(nodeIn: Node, targetSourceFile: SourceFile, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {
|
||||
const { node, text } = getNonformattedText(nodeIn, targetSourceFile, newLineCharacter);
|
||||
if (validate) validate(node, text);
|
||||
const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile);
|
||||
const formatOptions = getFormatCodeSettingsForWriting(formatContext, targetSourceFile);
|
||||
const initialIndentation =
|
||||
indentation !== undefined
|
||||
? indentation
|
||||
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
|
||||
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, targetSourceFile) === pos);
|
||||
if (delta === undefined) {
|
||||
delta = formatting.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0;
|
||||
}
|
||||
@@ -1293,7 +1308,7 @@ namespace changesToText {
|
||||
return getLineAndCharacterOfPosition(this, pos);
|
||||
}
|
||||
};
|
||||
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions });
|
||||
const changes = formatting.formatNodeGivenIndentation(node, file, targetSourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions });
|
||||
return applyChanges(text, changes);
|
||||
}
|
||||
|
||||
|
||||
+36
-4
@@ -378,6 +378,19 @@ export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalR
|
||||
containingSourceFile: SourceFile | undefined,
|
||||
reusedNames: readonly T[] | undefined
|
||||
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
|
||||
/** @internal */
|
||||
resolveLibrary?(
|
||||
libraryName: string,
|
||||
resolveFrom: string,
|
||||
options: CompilerOptions,
|
||||
libFileName: string,
|
||||
): ResolvedModuleWithFailedLookupLocations;
|
||||
/**
|
||||
* If provided along with custom resolveLibrary, used to determine if we should redo library resolutions
|
||||
* @internal
|
||||
*/
|
||||
hasInvalidatedLibResolutions?(libFileName: string): boolean;
|
||||
|
||||
/** @internal */ hasInvalidatedResolutions?: HasInvalidatedResolutions;
|
||||
/** @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames;
|
||||
/** @internal */ getGlobalTypingsCacheLocation?(): string | undefined;
|
||||
@@ -562,6 +575,8 @@ export interface LanguageService {
|
||||
/** @deprecated Use the signature with `UserPreferences` instead. */
|
||||
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
|
||||
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): readonly RenameLocation[] | undefined;
|
||||
/** @deprecated Pass `providePrefixAndSuffixTextForRename` as part of a `UserPreferences` parameter. */
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): readonly RenameLocation[] | undefined;
|
||||
|
||||
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
|
||||
@@ -630,8 +645,15 @@ export interface LanguageService {
|
||||
/** @deprecated `fileName` will be ignored */
|
||||
applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
|
||||
|
||||
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[];
|
||||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined;
|
||||
/**
|
||||
* @param includeInteractiveActions Include refactor actions that require additional arguments to be
|
||||
* passed when calling `getEditsForRefactor`. When true, clients should inspect the `isInteractive`
|
||||
* property of each returned `RefactorActionInfo` and ensure they are able to collect the appropriate
|
||||
* arguments for any interactive action before offering it.
|
||||
*/
|
||||
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[];
|
||||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined, includeInteractiveActions?: InteractiveRefactorArguments): RefactorEditInfo | undefined;
|
||||
getMoveToRefactoringFileSuggestions(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): { newFileName: string, files: string[] };
|
||||
organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
|
||||
@@ -963,6 +985,12 @@ export interface RefactorActionInfo {
|
||||
* The hierarchical dotted name of the refactor action.
|
||||
*/
|
||||
kind?: string;
|
||||
|
||||
/**
|
||||
* Indicates that the action requires additional arguments to be passed
|
||||
* when calling `getEditsForRefactor`.
|
||||
*/
|
||||
isInteractive?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1266,6 +1294,10 @@ export interface DocCommentTemplateOptions {
|
||||
readonly generateReturnInDocTemplate?: boolean;
|
||||
}
|
||||
|
||||
export interface InteractiveRefactorArguments {
|
||||
targetFile: string;
|
||||
}
|
||||
|
||||
export interface SignatureHelpParameter {
|
||||
name: string;
|
||||
documentation: SymbolDisplayPart[];
|
||||
@@ -1758,10 +1790,10 @@ export interface Refactor {
|
||||
kinds?: string[];
|
||||
|
||||
/** Compute the associated code actions */
|
||||
getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined;
|
||||
getEditsForAction(context: RefactorContext, actionName: string, interactiveRefactorArguments?: InteractiveRefactorArguments): RefactorEditInfo | undefined;
|
||||
|
||||
/** Compute (quickly) which actions are available here */
|
||||
getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[];
|
||||
getAvailableActions(context: RefactorContext, includeInteractive?: boolean, interactiveRefactorArguments?: InteractiveRefactorArguments): readonly ApplicableRefactorInfo[];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -54,6 +54,7 @@ import {
|
||||
ElementAccessExpression,
|
||||
EmitFlags,
|
||||
EmitHint,
|
||||
emitModuleKindIsNonNodeESM,
|
||||
emptyArray,
|
||||
EndOfFileToken,
|
||||
endsWith,
|
||||
@@ -88,8 +89,10 @@ import {
|
||||
getAssignmentDeclarationKind,
|
||||
getCombinedNodeFlagsAlwaysIncludeJSDoc,
|
||||
getDirectoryPath,
|
||||
getEmitModuleKind,
|
||||
getEmitScriptTarget,
|
||||
getExternalModuleImportEqualsDeclarationExpression,
|
||||
getImpliedNodeFormatForFile,
|
||||
getIndentString,
|
||||
getJSDocEnumTag,
|
||||
getLastChild,
|
||||
@@ -108,8 +111,10 @@ import {
|
||||
getTextOfIdentifierOrLiteral,
|
||||
getTextOfNode,
|
||||
getTypesPackageName,
|
||||
hasJSFileExtension,
|
||||
hasSyntacticModifier,
|
||||
HeritageClause,
|
||||
hostGetCanonicalFileName,
|
||||
Identifier,
|
||||
identifierIsThisKeyword,
|
||||
identity,
|
||||
@@ -256,6 +261,7 @@ import {
|
||||
JsTyping,
|
||||
JsxEmit,
|
||||
JsxOpeningLikeElement,
|
||||
JsxTagNameExpression,
|
||||
LabeledStatement,
|
||||
LanguageServiceHost,
|
||||
last,
|
||||
@@ -267,6 +273,7 @@ import {
|
||||
ModifierFlags,
|
||||
ModuleDeclaration,
|
||||
ModuleInstanceState,
|
||||
ModuleKind,
|
||||
ModuleResolutionKind,
|
||||
ModuleSpecifierResolutionHost,
|
||||
moduleSpecifiers,
|
||||
@@ -350,6 +357,7 @@ import {
|
||||
textSpanEnd,
|
||||
Token,
|
||||
tokenToString,
|
||||
toPath,
|
||||
tryCast,
|
||||
Type,
|
||||
TypeChecker,
|
||||
@@ -610,7 +618,7 @@ function selectTagNameOfJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
|
||||
return node.tagName;
|
||||
}
|
||||
|
||||
function isCalleeWorker<T extends CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement>(node: Node, pred: (node: Node) => node is T, calleeSelector: (node: T) => Expression, includeElementAccess: boolean, skipPastOuterExpressions: boolean) {
|
||||
function isCalleeWorker<T extends CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement>(node: Node, pred: (node: Node) => node is T, calleeSelector: (node: T) => Expression | JsxTagNameExpression, includeElementAccess: boolean, skipPastOuterExpressions: boolean) {
|
||||
let target = includeElementAccess ? climbPastPropertyOrElementAccess(node) : climbPastPropertyAccess(node);
|
||||
if (skipPastOuterExpressions) {
|
||||
target = skipOuterExpressions(target);
|
||||
@@ -3137,7 +3145,12 @@ export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T>, in
|
||||
export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia?: boolean): NodeArray<T> | undefined;
|
||||
/** @internal */
|
||||
export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia = true): NodeArray<T> | undefined {
|
||||
return nodes && factory.createNodeArray(nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), nodes.hasTrailingComma);
|
||||
if (nodes) {
|
||||
const cloned = factory.createNodeArray(nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), nodes.hasTrailingComma);
|
||||
setTextRange(cloned, nodes);
|
||||
return cloned;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -4165,3 +4178,45 @@ export function newCaseClauseTracker(checker: TypeChecker, clauses: readonly (Ca
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function fileShouldUseJavaScriptRequire(file: SourceFile | string, program: Program, host: LanguageServiceHost, preferRequire?: boolean) {
|
||||
const fileName = typeof file === "string" ? file : file.fileName;
|
||||
if (!hasJSFileExtension(fileName)) {
|
||||
return false;
|
||||
}
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
const moduleKind = getEmitModuleKind(compilerOptions);
|
||||
const impliedNodeFormat = typeof file === "string"
|
||||
? getImpliedNodeFormatForFile(toPath(file, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), program.getPackageJsonInfoCache?.(), host, compilerOptions)
|
||||
: file.impliedNodeFormat;
|
||||
|
||||
if (impliedNodeFormat === ModuleKind.ESNext) {
|
||||
return false;
|
||||
}
|
||||
if (impliedNodeFormat === ModuleKind.CommonJS) {
|
||||
// Since we're in a JS file, assume the user is writing the JS that will run
|
||||
// (i.e., assume `noEmit`), so a CJS-format file should just have require
|
||||
// syntax, rather than imports that will be downleveled to `require`.
|
||||
return true;
|
||||
}
|
||||
if (compilerOptions.verbatimModuleSyntax && moduleKind === ModuleKind.CommonJS) {
|
||||
// Using ESM syntax under these options would result in an error.
|
||||
return true;
|
||||
}
|
||||
if (compilerOptions.verbatimModuleSyntax && emitModuleKindIsNonNodeESM(moduleKind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// impliedNodeFormat is undefined and `verbatimModuleSyntax` is off (or in an invalid combo)
|
||||
// Use heuristics from existing code
|
||||
if (typeof file === "object") {
|
||||
if (file.commonJsModuleIndicator) {
|
||||
return true;
|
||||
}
|
||||
if (file.externalModuleIndicator) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return preferRequire;
|
||||
}
|
||||
+10
-1
@@ -53,7 +53,6 @@ import "./unittests/services/extract/constants";
|
||||
import "./unittests/services/extract/functions";
|
||||
import "./unittests/services/extract/symbolWalker";
|
||||
import "./unittests/services/extract/ranges";
|
||||
import "./unittests/services/findAllReferences";
|
||||
import "./unittests/services/hostNewLineSupport";
|
||||
import "./unittests/services/languageService";
|
||||
import "./unittests/services/organizeImports";
|
||||
@@ -78,6 +77,7 @@ import "./unittests/tsbuild/graphOrdering";
|
||||
import "./unittests/tsbuild/inferredTypeFromTransitiveModule";
|
||||
import "./unittests/tsbuild/javascriptProjectEmit";
|
||||
import "./unittests/tsbuild/lateBoundSymbol";
|
||||
import "./unittests/tsbuild/libraryResolution";
|
||||
import "./unittests/tsbuild/moduleResolution";
|
||||
import "./unittests/tsbuild/moduleSpecifiers";
|
||||
import "./unittests/tsbuild/noEmit";
|
||||
@@ -92,6 +92,7 @@ import "./unittests/tsbuild/sample";
|
||||
import "./unittests/tsbuild/transitiveReferences";
|
||||
import "./unittests/tsbuildWatch/configFileErrors";
|
||||
import "./unittests/tsbuildWatch/demo";
|
||||
import "./unittests/tsbuildWatch/libraryResolution";
|
||||
import "./unittests/tsbuildWatch/moduleResolution";
|
||||
import "./unittests/tsbuildWatch/noEmit";
|
||||
import "./unittests/tsbuildWatch/noEmitOnError";
|
||||
@@ -105,7 +106,9 @@ import "./unittests/tsc/composite";
|
||||
import "./unittests/tsc/declarationEmit";
|
||||
import "./unittests/tsc/forceConsistentCasingInFileNames";
|
||||
import "./unittests/tsc/incremental";
|
||||
import "./unittests/tsc/libraryResolution";
|
||||
import "./unittests/tsc/listFilesOnly";
|
||||
import "./unittests/tsc/moduleResolution";
|
||||
import "./unittests/tsc/projectReferences";
|
||||
import "./unittests/tsc/projectReferencesConfig";
|
||||
import "./unittests/tsc/redirect";
|
||||
@@ -116,6 +119,7 @@ import "./unittests/tscWatch/nodeNextWatch";
|
||||
import "./unittests/tscWatch/emitAndErrorUpdates";
|
||||
import "./unittests/tscWatch/forceConsistentCasingInFileNames";
|
||||
import "./unittests/tscWatch/incremental";
|
||||
import "./unittests/tscWatch/libraryResolution";
|
||||
import "./unittests/tscWatch/moduleResolution";
|
||||
import "./unittests/tscWatch/programUpdates";
|
||||
import "./unittests/tscWatch/projectsWithReferences";
|
||||
@@ -144,17 +148,20 @@ import "./unittests/tsserver/events/projectLoading";
|
||||
import "./unittests/tsserver/events/projectUpdatedInBackground";
|
||||
import "./unittests/tsserver/exportMapCache";
|
||||
import "./unittests/tsserver/externalProjects";
|
||||
import "./unittests/tsserver/findAllReferences";
|
||||
import "./unittests/tsserver/forceConsistentCasingInFileNames";
|
||||
import "./unittests/tsserver/formatSettings";
|
||||
import "./unittests/tsserver/getApplicableRefactors";
|
||||
import "./unittests/tsserver/getEditsForFileRename";
|
||||
import "./unittests/tsserver/getExportReferences";
|
||||
import "./unittests/tsserver/getFileReferences";
|
||||
import "./unittests/tsserver/goToDefinition";
|
||||
import "./unittests/tsserver/importHelpers";
|
||||
import "./unittests/tsserver/inlayHints";
|
||||
import "./unittests/tsserver/inferredProjects";
|
||||
import "./unittests/tsserver/jsdocTag";
|
||||
import "./unittests/tsserver/languageService";
|
||||
import "./unittests/tsserver/libraryResolution";
|
||||
import "./unittests/tsserver/maxNodeModuleJsDepth";
|
||||
import "./unittests/tsserver/metadataInResponse";
|
||||
import "./unittests/tsserver/moduleResolution";
|
||||
@@ -165,6 +172,7 @@ import "./unittests/tsserver/openFile";
|
||||
import "./unittests/tsserver/packageJsonInfo";
|
||||
import "./unittests/tsserver/partialSemanticServer";
|
||||
import "./unittests/tsserver/plugins";
|
||||
import "./unittests/tsserver/pluginsAsync";
|
||||
import "./unittests/tsserver/projectErrors";
|
||||
import "./unittests/tsserver/projectReferenceCompileOnSave";
|
||||
import "./unittests/tsserver/projectReferenceErrors";
|
||||
@@ -194,3 +202,4 @@ import "./unittests/tsserver/versionCache";
|
||||
import "./unittests/tsserver/watchEnvironment";
|
||||
import "./unittests/debugDeprecation";
|
||||
import "./unittests/tsserver/inconsistentErrorInEditor";
|
||||
import "./unittests/tsserver/getMoveToRefactoringFileSuggestions";
|
||||
|
||||
@@ -106,6 +106,81 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => {
|
||||
assert.instanceOf(result.output[2], Promise);
|
||||
});
|
||||
|
||||
it("call return when user code return (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
return { value: undefined, done: false };
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
for await (const item of iterator) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isTrue(await result.main());
|
||||
});
|
||||
|
||||
it("call return when user code break (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
return { value: undefined, done: false };
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
for await (const item of iterator) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isTrue(await result.main());
|
||||
});
|
||||
|
||||
it("call return when user code throws (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
return { value: undefined, done: false };
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
for await (const item of iterator) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isTrue(await result.main());
|
||||
});
|
||||
|
||||
it("don't call return when non-user code throws (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
@@ -132,4 +207,96 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => {
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isFalse(await result.main());
|
||||
});
|
||||
|
||||
it("don't call return when user code continue (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
let i = 0;
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
i++;
|
||||
if (i < 2) return { value: undefined, done: false };
|
||||
throw new Error();
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
for await (const item of iterator) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isFalse(await result.main());
|
||||
});
|
||||
|
||||
it("don't call return when user code continue to local label (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
let i = 0;
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
i++;
|
||||
if (i < 2) return { value: undefined, done: false };
|
||||
throw new Error();
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
outerLoop:
|
||||
for (const outerItem of [1, 2, 3]) {
|
||||
innerLoop:
|
||||
for await (const item of iterator) {
|
||||
continue innerLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isFalse(await result.main());
|
||||
});
|
||||
|
||||
it("call return when user code continue to non-local label (es2015)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
let returnCalled = false;
|
||||
async function f() {
|
||||
let i = 0;
|
||||
const iterator = {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
|
||||
async next() {
|
||||
i++;
|
||||
if (i < 2) return { value: undefined, done: false };
|
||||
return { value: undefined, done: true };
|
||||
},
|
||||
async return() {
|
||||
returnCalled = true;
|
||||
}
|
||||
};
|
||||
outerLoop:
|
||||
for (const outerItem of [1, 2, 3]) {
|
||||
innerLoop:
|
||||
for await (const item of iterator) {
|
||||
continue outerLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function main() {
|
||||
try { await f(); } catch { }
|
||||
return returnCalled;
|
||||
}
|
||||
`, { target: ts.ScriptTarget.ES2015 });
|
||||
assert.isTrue(await result.main());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,370 @@
|
||||
import * as fakes from "../../_namespaces/fakes";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { TscCompileSystem } from "./tsc";
|
||||
import { TestServerHost } from "./virtualFileSystemWithWatch";
|
||||
|
||||
export type CommandLineProgram = [ts.Program, ts.BuilderProgram?];
|
||||
export interface CommandLineCallbacks {
|
||||
cb: ts.ExecuteCommandLineCallbacks;
|
||||
getPrograms: () => readonly CommandLineProgram[];
|
||||
}
|
||||
|
||||
function isAnyProgram(program: ts.Program | ts.BuilderProgram | ts.ParsedCommandLine): program is ts.Program | ts.BuilderProgram {
|
||||
return !!(program as ts.Program | ts.BuilderProgram).getCompilerOptions;
|
||||
}
|
||||
export function commandLineCallbacks(
|
||||
sys: TscCompileSystem | TestServerHost,
|
||||
originalReadCall?: ts.System["readFile"],
|
||||
): CommandLineCallbacks {
|
||||
let programs: CommandLineProgram[] | undefined;
|
||||
|
||||
return {
|
||||
cb: program => {
|
||||
if (isAnyProgram(program)) {
|
||||
baselineBuildInfo(program.getCompilerOptions(), sys, originalReadCall);
|
||||
(programs || (programs = [])).push(ts.isBuilderProgram(program) ?
|
||||
[program.getProgram(), program] :
|
||||
[program]
|
||||
);
|
||||
}
|
||||
else {
|
||||
baselineBuildInfo(program.options, sys, originalReadCall);
|
||||
}
|
||||
},
|
||||
getPrograms: () => {
|
||||
const result = programs || ts.emptyArray;
|
||||
programs = undefined;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function baselinePrograms(baseline: string[], getPrograms: () => readonly CommandLineProgram[], oldPrograms: readonly (CommandLineProgram | undefined)[], baselineDependencies: boolean | undefined) {
|
||||
const programs = getPrograms();
|
||||
for (let i = 0; i < programs.length; i++) {
|
||||
baselineProgram(baseline, programs[i], oldPrograms[i], baselineDependencies);
|
||||
}
|
||||
return programs;
|
||||
}
|
||||
|
||||
function baselineProgram(baseline: string[], [program, builderProgram]: CommandLineProgram, oldProgram: CommandLineProgram | undefined, baselineDependencies: boolean | undefined) {
|
||||
if (program !== oldProgram?.[0]) {
|
||||
const options = program.getCompilerOptions();
|
||||
baseline.push(`Program root files: ${JSON.stringify(program.getRootFileNames())}`);
|
||||
baseline.push(`Program options: ${JSON.stringify(options)}`);
|
||||
baseline.push(`Program structureReused: ${(ts as any).StructureIsReused[program.structureIsReused]}`);
|
||||
baseline.push("Program files::");
|
||||
for (const file of program.getSourceFiles()) {
|
||||
baseline.push(file.fileName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseline.push(`Program: Same as old program`);
|
||||
}
|
||||
baseline.push("");
|
||||
|
||||
if (!builderProgram) return;
|
||||
if (builderProgram !== oldProgram?.[1]) {
|
||||
const state = builderProgram.getState();
|
||||
const internalState = state as unknown as ts.BuilderProgramState;
|
||||
if (state.semanticDiagnosticsPerFile?.size) {
|
||||
baseline.push("Semantic diagnostics in builder refreshed for::");
|
||||
for (const file of program.getSourceFiles()) {
|
||||
if (!internalState.semanticDiagnosticsFromOldState || !internalState.semanticDiagnosticsFromOldState.has(file.resolvedPath)) {
|
||||
baseline.push(file.fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseline.push("No cached semantic diagnostics in the builder::");
|
||||
}
|
||||
if (internalState) {
|
||||
baseline.push("");
|
||||
if (internalState.hasCalledUpdateShapeSignature?.size) {
|
||||
baseline.push("Shape signatures in builder refreshed for::");
|
||||
internalState.hasCalledUpdateShapeSignature.forEach((path: ts.Path) => {
|
||||
const info = state.fileInfos.get(path);
|
||||
if (info?.version === info?.signature || !info?.signature) {
|
||||
baseline.push(path + " (used version)");
|
||||
}
|
||||
else if (internalState.filesChangingSignature?.has(path)) {
|
||||
baseline.push(path + " (computed .d.ts during emit)");
|
||||
}
|
||||
else {
|
||||
baseline.push(path + " (computed .d.ts)");
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
baseline.push("No shapes updated in the builder::");
|
||||
}
|
||||
}
|
||||
baseline.push("");
|
||||
if (!baselineDependencies) return;
|
||||
baseline.push("Dependencies for::");
|
||||
for (const file of builderProgram.getSourceFiles()) {
|
||||
baseline.push(`${file.fileName}:`);
|
||||
for (const depenedency of builderProgram.getAllDependencies(file)) {
|
||||
baseline.push(` ${depenedency}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseline.push(`BuilderProgram: Same as old builder program`);
|
||||
}
|
||||
baseline.push("");
|
||||
}
|
||||
|
||||
export function generateSourceMapBaselineFiles(sys: ts.System & { writtenFiles: ts.ReadonlyCollection<ts.Path>; }) {
|
||||
const mapFileNames = ts.mapDefinedIterator(sys.writtenFiles.keys(), f => f.endsWith(".map") ? f : undefined);
|
||||
for (const mapFile of mapFileNames) {
|
||||
const text = Harness.SourceMapRecorder.getSourceMapRecordWithSystem(sys, mapFile);
|
||||
sys.writeFile(`${mapFile}.baseline.txt`, text);
|
||||
}
|
||||
}
|
||||
|
||||
function generateBundleFileSectionInfo(sys: ts.System, originalReadCall: ts.System["readFile"], baselineRecorder: Harness.Compiler.WriterAggregator, bundleFileInfo: ts.BundleFileInfo | undefined, outFile: string | undefined) {
|
||||
if (!ts.length(bundleFileInfo && bundleFileInfo.sections) && !outFile) return; // Nothing to baseline
|
||||
|
||||
const content = outFile && sys.fileExists(outFile) ? originalReadCall.call(sys, outFile, "utf8")! : "";
|
||||
baselineRecorder.WriteLine("======================================================================");
|
||||
baselineRecorder.WriteLine(`File:: ${outFile}`);
|
||||
for (const section of bundleFileInfo ? bundleFileInfo.sections : ts.emptyArray) {
|
||||
baselineRecorder.WriteLine("----------------------------------------------------------------------");
|
||||
writeSectionHeader(section);
|
||||
if (section.kind !== ts.BundleFileSectionKind.Prepend) {
|
||||
writeTextOfSection(section.pos, section.end);
|
||||
}
|
||||
else if (section.texts.length > 0) {
|
||||
ts.Debug.assert(section.pos === ts.first(section.texts).pos);
|
||||
ts.Debug.assert(section.end === ts.last(section.texts).end);
|
||||
for (const text of section.texts) {
|
||||
baselineRecorder.WriteLine(">>--------------------------------------------------------------------");
|
||||
writeSectionHeader(text);
|
||||
writeTextOfSection(text.pos, text.end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ts.Debug.assert(section.pos === section.end);
|
||||
}
|
||||
}
|
||||
baselineRecorder.WriteLine("======================================================================");
|
||||
|
||||
function writeTextOfSection(pos: number, end: number) {
|
||||
const textLines = content.substring(pos, end).split(/\r?\n/);
|
||||
for (const line of textLines) {
|
||||
baselineRecorder.WriteLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
function writeSectionHeader(section: ts.BundleFileSection) {
|
||||
baselineRecorder.WriteLine(`${section.kind}: (${section.pos}-${section.end})${section.data ? ":: " + section.data : ""}${section.kind === ts.BundleFileSectionKind.Prepend ? " texts:: " + section.texts.length : ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
export type ReadableProgramBuildInfoDiagnostic = string | [string, readonly ts.ReusableDiagnostic[]];
|
||||
export type ReadableBuilderFileEmit = string & { __readableBuilderFileEmit: any; };
|
||||
export type ReadableProgramBuilderInfoFilePendingEmit = [original: string | [string], emitKind: ReadableBuilderFileEmit];
|
||||
export type ReadableProgramBuildInfoEmitSignature = string | [string, ts.EmitSignature | []];
|
||||
export type ReadableProgramBuildInfoFileInfo<T> = Omit<ts.BuilderState.FileInfo, "impliedFormat"> & {
|
||||
impliedFormat: string | undefined;
|
||||
original: T | undefined;
|
||||
};
|
||||
export type ReadableProgramBuildInfoRoot =
|
||||
[original: ts.ProgramBuildInfoFileId, readable: string] |
|
||||
[orginal: ts.ProgramBuildInfoRootStartEnd, readable: readonly string[]];
|
||||
export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo,
|
||||
"fileIdsList" | "fileInfos" | "root" |
|
||||
"referencedMap" | "exportedModulesMap" | "semanticDiagnosticsPerFile" |
|
||||
"affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"
|
||||
> & {
|
||||
fileNamesList: readonly (readonly string[])[] | undefined;
|
||||
fileInfos: ts.MapLike<ReadableProgramBuildInfoFileInfo<ts.ProgramMultiFileEmitBuildInfoFileInfo>>;
|
||||
root: readonly ReadableProgramBuildInfoRoot[];
|
||||
referencedMap: ts.MapLike<string[]> | undefined;
|
||||
exportedModulesMap: ts.MapLike<string[]> | undefined;
|
||||
semanticDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
|
||||
affectedFilesPendingEmit: readonly ReadableProgramBuilderInfoFilePendingEmit[] | undefined;
|
||||
changeFileSet: readonly string[] | undefined;
|
||||
emitSignatures: readonly ReadableProgramBuildInfoEmitSignature[] | undefined;
|
||||
};
|
||||
export type ReadableProgramBuildInfoBundlePendingEmit = [emitKind: ReadableBuilderFileEmit, original: ts.ProgramBuildInfoBundlePendingEmit];
|
||||
export type ReadableProgramBundleEmitBuildInfo = Omit<ts.ProgramBundleEmitBuildInfo, "fileInfos" | "root" | "pendingEmit"> & {
|
||||
fileInfos: ts.MapLike<string | ReadableProgramBuildInfoFileInfo<ts.BuilderState.FileInfo>>;
|
||||
root: readonly ReadableProgramBuildInfoRoot[];
|
||||
pendingEmit: ReadableProgramBuildInfoBundlePendingEmit | undefined;
|
||||
};
|
||||
|
||||
export type ReadableProgramBuildInfo = ReadableProgramMultiFileEmitBuildInfo | ReadableProgramBundleEmitBuildInfo;
|
||||
|
||||
export function isReadableProgramBundleEmitBuildInfo(info: ReadableProgramBuildInfo | undefined): info is ReadableProgramBundleEmitBuildInfo {
|
||||
return !!info && !!ts.outFile(info.options || {});
|
||||
}
|
||||
export type ReadableBuildInfo = Omit<ts.BuildInfo, "program"> & { program: ReadableProgramBuildInfo | undefined; size: number; };
|
||||
function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string, buildInfo: ts.BuildInfo) {
|
||||
let program: ReadableProgramBuildInfo | undefined;
|
||||
let fileNamesList: string[][] | undefined;
|
||||
if (buildInfo.program && ts.isProgramBundleEmitBuildInfo(buildInfo.program)) {
|
||||
const fileInfos: ReadableProgramBundleEmitBuildInfo["fileInfos"] = {};
|
||||
buildInfo.program?.fileInfos?.forEach((fileInfo, index) =>
|
||||
fileInfos[toFileName(index + 1 as ts.ProgramBuildInfoFileId)] = ts.isString(fileInfo) ?
|
||||
fileInfo :
|
||||
toReadableFileInfo(fileInfo, ts.identity)
|
||||
);
|
||||
const pendingEmit = buildInfo.program.pendingEmit;
|
||||
program = {
|
||||
...buildInfo.program,
|
||||
fileInfos,
|
||||
root: buildInfo.program.root.map(toReadableProgramBuildInfoRoot),
|
||||
pendingEmit: pendingEmit === undefined ?
|
||||
undefined :
|
||||
[
|
||||
toReadableBuilderFileEmit(ts.toProgramEmitPending(pendingEmit, buildInfo.program.options)),
|
||||
pendingEmit
|
||||
],
|
||||
};
|
||||
}
|
||||
else if (buildInfo.program) {
|
||||
const fileInfos: ReadableProgramMultiFileEmitBuildInfo["fileInfos"] = {};
|
||||
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ts.ProgramBuildInfoFileId)] = toReadableFileInfo(fileInfo, ts.toBuilderStateFileInfoForMultiEmit));
|
||||
fileNamesList = buildInfo.program.fileIdsList?.map(fileIdsListId => fileIdsListId.map(toFileName));
|
||||
const fullEmitForOptions = buildInfo.program.affectedFilesPendingEmit ? ts.getBuilderFileEmit(buildInfo.program.options || {}) : undefined;
|
||||
program = buildInfo.program && {
|
||||
fileNames: buildInfo.program.fileNames,
|
||||
fileNamesList,
|
||||
fileInfos: buildInfo.program.fileInfos ? fileInfos : undefined!,
|
||||
root: buildInfo.program.root.map(toReadableProgramBuildInfoRoot),
|
||||
options: buildInfo.program.options,
|
||||
referencedMap: toMapOfReferencedSet(buildInfo.program.referencedMap),
|
||||
exportedModulesMap: toMapOfReferencedSet(buildInfo.program.exportedModulesMap),
|
||||
semanticDiagnosticsPerFile: buildInfo.program.semanticDiagnosticsPerFile?.map(d =>
|
||||
ts.isNumber(d) ?
|
||||
toFileName(d) :
|
||||
[toFileName(d[0]), d[1]]
|
||||
),
|
||||
affectedFilesPendingEmit: buildInfo.program.affectedFilesPendingEmit?.map(value => toReadableProgramBuilderInfoFilePendingEmit(value, fullEmitForOptions!)),
|
||||
changeFileSet: buildInfo.program.changeFileSet?.map(toFileName),
|
||||
emitSignatures: buildInfo.program.emitSignatures?.map(s =>
|
||||
ts.isNumber(s) ?
|
||||
toFileName(s) :
|
||||
[toFileName(s[0]), s[1]]
|
||||
),
|
||||
latestChangedDtsFile: buildInfo.program.latestChangedDtsFile,
|
||||
};
|
||||
}
|
||||
const version = buildInfo.version === ts.version ? fakes.version : buildInfo.version;
|
||||
const result: ReadableBuildInfo = {
|
||||
// Baseline fixed order for bundle
|
||||
bundle: buildInfo.bundle && {
|
||||
...buildInfo.bundle,
|
||||
js: buildInfo.bundle.js && {
|
||||
sections: buildInfo.bundle.js.sections,
|
||||
hash: buildInfo.bundle.js.hash,
|
||||
mapHash: buildInfo.bundle.js.mapHash,
|
||||
sources: buildInfo.bundle.js.sources,
|
||||
},
|
||||
dts: buildInfo.bundle.dts && {
|
||||
sections: buildInfo.bundle.dts.sections,
|
||||
hash: buildInfo.bundle.dts.hash,
|
||||
mapHash: buildInfo.bundle.dts.mapHash,
|
||||
sources: buildInfo.bundle.dts.sources,
|
||||
},
|
||||
},
|
||||
program,
|
||||
version,
|
||||
size: ts.getBuildInfoText({ ...buildInfo, version }).length,
|
||||
};
|
||||
// For now its just JSON.stringify
|
||||
sys.writeFile(`${buildInfoPath}.readable.baseline.txt`, JSON.stringify(result, /*replacer*/ undefined, 2));
|
||||
|
||||
function toFileName(fileId: ts.ProgramBuildInfoFileId) {
|
||||
return buildInfo.program!.fileNames[fileId - 1];
|
||||
}
|
||||
|
||||
function toFileNames(fileIdsListId: ts.ProgramBuildInfoFileIdListId) {
|
||||
return fileNamesList![fileIdsListId - 1];
|
||||
}
|
||||
|
||||
function toReadableFileInfo<T>(original: T, toFileInfo: (fileInfo: T) => ts.BuilderState.FileInfo): ReadableProgramBuildInfoFileInfo<T> {
|
||||
const info = toFileInfo(original);
|
||||
return {
|
||||
original: ts.isString(original) ? undefined : original,
|
||||
...info,
|
||||
impliedFormat: info.impliedFormat && ts.getNameOfCompilerOptionValue(info.impliedFormat, ts.moduleOptionDeclaration.type),
|
||||
};
|
||||
}
|
||||
|
||||
function toReadableProgramBuildInfoRoot(original: ts.ProgramBuildInfoRoot): ReadableProgramBuildInfoRoot {
|
||||
if (!ts.isArray(original)) return [original, toFileName(original)];
|
||||
const readable: string[] = [];
|
||||
for (let index = original[0]; index <= original[1]; index++) readable.push(toFileName(index));
|
||||
return [original, readable];
|
||||
}
|
||||
|
||||
function toMapOfReferencedSet(referenceMap: ts.ProgramBuildInfoReferencedMap | undefined): ts.MapLike<string[]> | undefined {
|
||||
if (!referenceMap) return undefined;
|
||||
const result: ts.MapLike<string[]> = {};
|
||||
for (const [fileNamesKey, fileNamesListKey] of referenceMap) {
|
||||
result[toFileName(fileNamesKey)] = toFileNames(fileNamesListKey);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function toReadableProgramBuilderInfoFilePendingEmit(value: ts.ProgramBuilderInfoFilePendingEmit, fullEmitForOptions: ts.BuilderFileEmit): ReadableProgramBuilderInfoFilePendingEmit {
|
||||
return [
|
||||
ts.isNumber(value) ? toFileName(value) : [toFileName(value[0])],
|
||||
toReadableBuilderFileEmit(ts.toBuilderFileEmit(value, fullEmitForOptions)),
|
||||
];
|
||||
}
|
||||
|
||||
function toReadableBuilderFileEmit(emit: ts.BuilderFileEmit | undefined): ReadableBuilderFileEmit {
|
||||
let result = "";
|
||||
if (emit) {
|
||||
if (emit & ts.BuilderFileEmit.Js) addFlags("Js");
|
||||
if (emit & ts.BuilderFileEmit.JsMap) addFlags("JsMap");
|
||||
if (emit & ts.BuilderFileEmit.JsInlineMap) addFlags("JsInlineMap");
|
||||
if (emit & ts.BuilderFileEmit.Dts) addFlags("Dts");
|
||||
if (emit & ts.BuilderFileEmit.DtsMap) addFlags("DtsMap");
|
||||
}
|
||||
return (result || "None") as ReadableBuilderFileEmit;
|
||||
function addFlags(flag: string) {
|
||||
result = result ? `${result} | ${flag}` : flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function toPathWithSystem(sys: ts.System, fileName: string): ts.Path {
|
||||
return ts.toPath(fileName, sys.getCurrentDirectory(), ts.createGetCanonicalFileName(sys.useCaseSensitiveFileNames));
|
||||
}
|
||||
|
||||
export function baselineBuildInfo(
|
||||
options: ts.CompilerOptions,
|
||||
sys: TscCompileSystem | TestServerHost,
|
||||
originalReadCall?: ts.System["readFile"],
|
||||
) {
|
||||
const buildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(options);
|
||||
if (!buildInfoPath || !sys.writtenFiles!.has(toPathWithSystem(sys, buildInfoPath))) return;
|
||||
if (!sys.fileExists(buildInfoPath)) return;
|
||||
|
||||
const buildInfo = ts.getBuildInfo(buildInfoPath, (originalReadCall || sys.readFile).call(sys, buildInfoPath, "utf8")!);
|
||||
if (!buildInfo) return sys.writeFile(`${buildInfoPath}.baseline.txt`, "Error reading valid buildinfo file");
|
||||
generateBuildInfoProgramBaseline(sys, buildInfoPath, buildInfo);
|
||||
|
||||
if (!ts.outFile(options)) return;
|
||||
const { jsFilePath, declarationFilePath } = ts.getOutputPathsForBundle(options, /*forceDtsPaths*/ false);
|
||||
const bundle = buildInfo.bundle;
|
||||
if (!bundle || (!ts.length(bundle.js && bundle.js.sections) && !ts.length(bundle.dts && bundle.dts.sections))) return;
|
||||
|
||||
// Write the baselines:
|
||||
const baselineRecorder = new Harness.Compiler.WriterAggregator();
|
||||
generateBundleFileSectionInfo(sys, originalReadCall || sys.readFile, baselineRecorder, bundle.js, jsFilePath);
|
||||
generateBundleFileSectionInfo(sys, originalReadCall || sys.readFile, baselineRecorder, bundle.dts, declarationFilePath);
|
||||
baselineRecorder.Close();
|
||||
const text = baselineRecorder.lines.join("\r\n");
|
||||
sys.writeFile(`${buildInfoPath}.baseline.txt`, text);
|
||||
}
|
||||
|
||||
export function tscBaselineName(scenario: string, subScenario: string, commandLineArgs: readonly string[], isWatch?: boolean, suffix?: string) {
|
||||
return `${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { libFile } from "./virtualFileSystemWithWatch";
|
||||
|
||||
export function compilerOptionsToConfigJson(options: ts.CompilerOptions) {
|
||||
return ts.optionMapToObject(ts.serializeCompilerOptions(options));
|
||||
}
|
||||
|
||||
export const libContent = `${libFile.content}
|
||||
interface ReadonlyArray<T> {}
|
||||
declare const console: { log(msg: any): void; };`;
|
||||
|
||||
export const symbolLibContent = `
|
||||
interface SymbolConstructor {
|
||||
readonly species: symbol;
|
||||
readonly toStringTag: symbol;
|
||||
}
|
||||
declare var Symbol: SymbolConstructor;
|
||||
interface Symbol {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
}
|
||||
`;
|
||||
|
||||
export interface FsContents {
|
||||
[path: string]: string;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { dedent } from "../../_namespaces/Utils";
|
||||
import { FsContents, libContent } from "./contents";
|
||||
import { loadProjectFromFiles } from "./vfs";
|
||||
import { createServerHost, createWatchedSystem } from "./virtualFileSystemWithWatch";
|
||||
|
||||
function getFsContentsForLibResolution(libRedirection?: boolean): FsContents {
|
||||
return {
|
||||
"/home/src/projects/project1/utils.d.ts": `export const y = 10;`,
|
||||
"/home/src/projects/project1/file.ts": `export const file = 10;`,
|
||||
"/home/src/projects/project1/core.d.ts": `export const core = 10;`,
|
||||
"/home/src/projects/project1/index.ts": `export const x = "type1";`,
|
||||
"/home/src/projects/project1/file2.ts": dedent`
|
||||
/// <reference lib="webworker"/>
|
||||
/// <reference lib="scripthost"/>
|
||||
/// <reference lib="es5"/>
|
||||
`,
|
||||
"/home/src/projects/project1/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: { composite: true, typeRoots: ["./typeroot1"], lib: ["es5", "dom"], traceResolution: true },
|
||||
}),
|
||||
"/home/src/projects/project1/typeroot1/sometype/index.d.ts": `export type TheNum = "type1";`,
|
||||
"/home/src/projects/project2/utils.d.ts": `export const y = 10;`,
|
||||
"/home/src/projects/project2/index.ts": `export const y = 10`,
|
||||
"/home/src/projects/project2/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: { composite: true, lib: ["es5", "dom"], traceResolution: true },
|
||||
}),
|
||||
"/home/src/projects/project3/utils.d.ts": `export const y = 10;`,
|
||||
"/home/src/projects/project3/index.ts": `export const z = 10`,
|
||||
"/home/src/projects/project3/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: { composite: true, lib: ["es5", "dom"], traceResolution: true },
|
||||
}),
|
||||
"/home/src/projects/project4/utils.d.ts": `export const y = 10;`,
|
||||
"/home/src/projects/project4/index.ts": `export const z = 10`,
|
||||
"/home/src/projects/project4/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: { composite: true, lib: ["esnext", "dom", "webworker"], traceResolution: true },
|
||||
}),
|
||||
"/home/src/lib/lib.es5.d.ts": libContent,
|
||||
"/home/src/lib/lib.esnext.d.ts": libContent,
|
||||
"/home/src/lib/lib.dom.d.ts": "interface DOMInterface { }",
|
||||
"/home/src/lib/lib.webworker.d.ts": "interface WebWorkerInterface { }",
|
||||
"/home/src/lib/lib.scripthost.d.ts": "interface ScriptHostInterface { }",
|
||||
"/home/src/projects/node_modules/@typescript/unlreated/index.d.ts": "export const unrelated = 10;",
|
||||
...libRedirection ? {
|
||||
"/home/src/projects/node_modules/@typescript/lib-es5/index.d.ts": libContent,
|
||||
"/home/src/projects/node_modules/@typescript/lib-esnext/index.d.ts": libContent,
|
||||
"/home/src/projects/node_modules/@typescript/lib-dom/index.d.ts": "interface DOMInterface { }",
|
||||
"/home/src/projects/node_modules/@typescript/lib-webworker/index.d.ts": "interface WebworkerInterface { }",
|
||||
"/home/src/projects/node_modules/@typescript/lib-scripthost/index.d.ts": "interface ScriptHostInterface { }",
|
||||
} : undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function getFsForLibResolution(libRedirection: true | undefined) {
|
||||
return loadProjectFromFiles(
|
||||
getFsContentsForLibResolution(libRedirection),
|
||||
{
|
||||
cwd: "/home/src/projects",
|
||||
executingFilePath: "/home/src/lib/tsc.js",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getSysForLibResolution(libRedirection?: true) {
|
||||
return createWatchedSystem(
|
||||
getFsContentsForLibResolution(libRedirection),
|
||||
{
|
||||
currentDirectory: "/home/src/projects",
|
||||
executingFilePath: "/home/src/lib/tsc.js",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getServerHosForLibResolution(libRedirection?: true) {
|
||||
return createServerHost(
|
||||
getFsContentsForLibResolution(libRedirection),
|
||||
{
|
||||
currentDirectory: "/home/src/projects",
|
||||
executingFilePath: "/home/src/lib/tsc.js",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getCommandLineArgsForLibResolution(withoutConfig: true | undefined) {
|
||||
return withoutConfig ?
|
||||
["project1/core.d.ts", "project1/utils.d.ts", "project1/file.ts", "project1/index.ts", "project1/file2.ts", "--lib", "es5,dom", "--traceResolution", "--explainFiles"] :
|
||||
["-p", "project1", "--explainFiles"];
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { dedent } from "../../_namespaces/Utils";
|
||||
import { FsContents } from "./contents";
|
||||
import { libFile } from "./virtualFileSystemWithWatch";
|
||||
|
||||
export function getFsConentsForNode10ResultAtTypesPackageJson(packageName: string, addTypesCondition: boolean) {
|
||||
return JSON.stringify({
|
||||
name: `@types/${packageName}`,
|
||||
version: "1.0.0",
|
||||
types: "index.d.ts",
|
||||
exports: {
|
||||
".": {
|
||||
...(addTypesCondition ? { types: "./index.d.ts" } : {}),
|
||||
require: "./index.d.ts"
|
||||
}
|
||||
}
|
||||
}, undefined, " ");
|
||||
}
|
||||
|
||||
export function getFsContentsForNode10ResultPackageJson(packageName: string, addTypes: boolean, addTypesCondition: boolean) {
|
||||
return JSON.stringify({
|
||||
name: packageName,
|
||||
version: "1.0.0",
|
||||
main: "index.js",
|
||||
...(addTypes ? { types: "index.d.ts" } : {}),
|
||||
exports: {
|
||||
".": {
|
||||
...(addTypesCondition ? { types: "./index.d.ts" } : {}),
|
||||
import: "./index.mjs",
|
||||
require: "./index.js"
|
||||
}
|
||||
}
|
||||
}, undefined, " ");
|
||||
}
|
||||
|
||||
export function getFsContentsForNode10ResultDts(packageName: string) {
|
||||
return `export declare const ${packageName}: number;`;
|
||||
}
|
||||
|
||||
function js(packageName: string) {
|
||||
return `module.exports = { ${packageName}: 1 };`;
|
||||
}
|
||||
|
||||
function mjs(packageName: string) {
|
||||
return `export const ${packageName} = 1;`;
|
||||
}
|
||||
|
||||
export function getFsContentsForNode10Result(): FsContents {
|
||||
return {
|
||||
"/home/src/projects/project/node_modules/@types/bar/package.json": getFsConentsForNode10ResultAtTypesPackageJson("bar", /*addTypesCondition*/ false),
|
||||
"/home/src/projects/project/node_modules/@types/bar/index.d.ts": getFsContentsForNode10ResultDts("bar"),
|
||||
"/home/src/projects/project/node_modules/bar/package.json": getFsContentsForNode10ResultPackageJson("bar", /*addTypes*/ false, /*addTypesCondition*/ false),
|
||||
"/home/src/projects/project/node_modules/bar/index.js": js("bar"),
|
||||
"/home/src/projects/project/node_modules/bar/index.mjs": mjs("bar"),
|
||||
"/home/src/projects/project/node_modules/foo/package.json": getFsContentsForNode10ResultPackageJson("foo", /*addTypes*/ true, /*addTypesCondition*/ false),
|
||||
"/home/src/projects/project/node_modules/foo/index.js": js("foo"),
|
||||
"/home/src/projects/project/node_modules/foo/index.mjs": mjs("foo"),
|
||||
"/home/src/projects/project/node_modules/foo/index.d.ts": getFsContentsForNode10ResultDts("foo"),
|
||||
"/home/src/projects/project/node_modules/@types/bar2/package.json": getFsConentsForNode10ResultAtTypesPackageJson("bar2", /*addTypesCondition*/ true),
|
||||
"/home/src/projects/project/node_modules/@types/bar2/index.d.ts": getFsContentsForNode10ResultDts("bar2"),
|
||||
"/home/src/projects/project/node_modules/bar2/package.json": getFsContentsForNode10ResultPackageJson("bar2", /*addTypes*/ false, /*addTypesCondition*/ false),
|
||||
"/home/src/projects/project/node_modules/bar2/index.js": js("bar2"),
|
||||
"/home/src/projects/project/node_modules/bar2/index.mjs": mjs("bar2"),
|
||||
"/home/src/projects/project/node_modules/foo2/package.json": getFsContentsForNode10ResultPackageJson("foo2", /*addTypes*/ true, /*addTypesCondition*/ true),
|
||||
"/home/src/projects/project/node_modules/foo2/index.js": js("foo2"),
|
||||
"/home/src/projects/project/node_modules/foo2/index.mjs": mjs("foo2"),
|
||||
"/home/src/projects/project/node_modules/foo2/index.d.ts": getFsContentsForNode10ResultDts("foo2"),
|
||||
"/home/src/projects/project/index.mts": dedent`
|
||||
import { foo } from "foo";
|
||||
import { bar } from "bar";
|
||||
import { foo2 } from "foo2";
|
||||
import { bar2 } from "bar2";
|
||||
`,
|
||||
"/home/src/projects/project/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
moduleResolution: "node16",
|
||||
traceResolution: true,
|
||||
incremental: true,
|
||||
strict: true,
|
||||
types: [],
|
||||
},
|
||||
files: ["index.mts"]
|
||||
}),
|
||||
[libFile.path]: libFile.content,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import * as fakes from "../../_namespaces/fakes";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { commandLineCallbacks } from "./baseline";
|
||||
import {
|
||||
makeSystemReadyForBaseline,
|
||||
TscCompileSystem,
|
||||
} from "./tsc";
|
||||
import {
|
||||
changeToHostTrackingWrittenFiles,
|
||||
createWatchedSystem,
|
||||
FileOrFolderOrSymLink,
|
||||
FileOrFolderOrSymLinkMap,
|
||||
TestServerHost,
|
||||
TestServerHostCreationParameters,
|
||||
} from "./virtualFileSystemWithWatch";
|
||||
|
||||
export function createSolutionBuilderHostForBaseline(
|
||||
sys: TscCompileSystem | TestServerHost,
|
||||
versionToWrite?: string,
|
||||
originalRead?: (TscCompileSystem | TestServerHost)["readFile"]
|
||||
) {
|
||||
if (sys instanceof fakes.System) makeSystemReadyForBaseline(sys, versionToWrite);
|
||||
const { cb } = commandLineCallbacks(sys, originalRead);
|
||||
const host = ts.createSolutionBuilderHost(sys,
|
||||
/*createProgram*/ undefined,
|
||||
ts.createDiagnosticReporter(sys, /*pretty*/ true),
|
||||
ts.createBuilderStatusReporter(sys, /*pretty*/ true)
|
||||
);
|
||||
host.afterProgramEmitAndDiagnostics = cb;
|
||||
host.afterEmitBundle = cb;
|
||||
return host;
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: TestServerHost, rootNames: readonly string[], originalRead?: TestServerHost["readFile"]) {
|
||||
const host = createSolutionBuilderHostForBaseline(system, /*versionToWrite*/ undefined, originalRead);
|
||||
return ts.createSolutionBuilder(host, rootNames, {});
|
||||
}
|
||||
|
||||
export function ensureErrorFreeBuild(host: TestServerHost, rootNames: readonly string[]) {
|
||||
// ts build should succeed
|
||||
solutionBuildWithBaseline(host, rootNames);
|
||||
assert.equal(host.getOutput().length, 0, JSON.stringify(host.getOutput(), /*replacer*/ undefined, " "));
|
||||
}
|
||||
|
||||
export function solutionBuildWithBaseline(sys: TestServerHost, solutionRoots: readonly string[], originalRead?: TestServerHost["readFile"]) {
|
||||
const originalReadFile = sys.readFile;
|
||||
const originalWrite = sys.write;
|
||||
const originalWriteFile = sys.writeFile;
|
||||
ts.Debug.assert(sys.writtenFiles === undefined);
|
||||
const solutionBuilder = createSolutionBuilder(changeToHostTrackingWrittenFiles(
|
||||
fakes.patchHostForBuildInfoReadWrite(sys)
|
||||
), solutionRoots, originalRead);
|
||||
solutionBuilder.build();
|
||||
sys.readFile = originalReadFile;
|
||||
sys.write = originalWrite;
|
||||
sys.writeFile = originalWriteFile;
|
||||
sys.writtenFiles = undefined;
|
||||
return sys;
|
||||
}
|
||||
|
||||
export function createSystemWithSolutionBuild(solutionRoots: readonly string[], files: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters) {
|
||||
return solutionBuildWithBaseline(createWatchedSystem(files, params), solutionRoots);
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
import * as fakes from "../../_namespaces/fakes";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
baselinePrograms,
|
||||
CommandLineCallbacks,
|
||||
commandLineCallbacks,
|
||||
CommandLineProgram,
|
||||
generateSourceMapBaselineFiles,
|
||||
isReadableProgramBundleEmitBuildInfo,
|
||||
ReadableBuildInfo,
|
||||
ReadableProgramBuildInfoFileInfo,
|
||||
ReadableProgramMultiFileEmitBuildInfo,
|
||||
toPathWithSystem,
|
||||
tscBaselineName,
|
||||
} from "./baseline";
|
||||
|
||||
export interface DtsSignatureData {
|
||||
signature: string | undefined;
|
||||
exportedModules: string[] | undefined;
|
||||
}
|
||||
|
||||
export type TscCompileSystem = fakes.System & {
|
||||
writtenFiles: Set<ts.Path>;
|
||||
baseLine(): { file: string; text: string; };
|
||||
dtsSignaures?: Map<ts.Path, Map<string, DtsSignatureData>>;
|
||||
storeFilesChangingSignatureDuringEmit?: boolean;
|
||||
};
|
||||
|
||||
export const noChangeRun: TestTscEdit = {
|
||||
caption: "no-change-run",
|
||||
edit: ts.noop
|
||||
};
|
||||
export const noChangeOnlyRuns = [noChangeRun];
|
||||
|
||||
export interface TestTscCompile extends TestTscCompileLikeBase {
|
||||
baselineSourceMap?: boolean;
|
||||
baselineReadFileCalls?: boolean;
|
||||
baselinePrograms?: boolean;
|
||||
baselineDependencies?: boolean;
|
||||
}
|
||||
|
||||
export interface TestTscCompileLikeBase extends VerifyTscCompileLike {
|
||||
diffWithInitial?: boolean;
|
||||
modifyFs?: (fs: vfs.FileSystem) => void;
|
||||
computeDtsSignatures?: boolean;
|
||||
environmentVariables?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface TestTscCompileLike extends TestTscCompileLikeBase {
|
||||
compile: (sys: TscCompileSystem) => void;
|
||||
additionalBaseline?: (sys: TscCompileSystem) => void;
|
||||
}
|
||||
/**
|
||||
* Initialize FS, run compile function and save baseline
|
||||
*/
|
||||
export function testTscCompileLike(input: TestTscCompileLike) {
|
||||
const initialFs = input.fs();
|
||||
const inputFs = initialFs.shadow();
|
||||
const {
|
||||
scenario, subScenario, diffWithInitial,
|
||||
commandLineArgs, modifyFs,
|
||||
environmentVariables,
|
||||
compile: worker, additionalBaseline,
|
||||
} = input;
|
||||
if (modifyFs) modifyFs(inputFs);
|
||||
inputFs.makeReadonly();
|
||||
const fs = inputFs.shadow();
|
||||
|
||||
// Create system
|
||||
const sys = new fakes.System(fs, { executingFilePath: `${fs.meta.get("defaultLibLocation")}/tsc`, env: environmentVariables }) as TscCompileSystem;
|
||||
sys.storeFilesChangingSignatureDuringEmit = true;
|
||||
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
|
||||
sys.exit = exitCode => sys.exitCode = exitCode;
|
||||
worker(sys);
|
||||
sys.write(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}\n`);
|
||||
additionalBaseline?.(sys);
|
||||
fs.makeReadonly();
|
||||
sys.baseLine = () => {
|
||||
const baseFsPatch = diffWithInitial ?
|
||||
inputFs.diff(initialFs, { includeChangedFileWithSameContent: true }) :
|
||||
inputFs.diff(/*base*/ undefined, { baseIsNotShadowRoot: true });
|
||||
const patch = fs.diff(inputFs, { includeChangedFileWithSameContent: true });
|
||||
return {
|
||||
file: tscBaselineName(scenario, subScenario, commandLineArgs),
|
||||
text: `Input::
|
||||
${baseFsPatch ? vfs.formatPatch(baseFsPatch) : ""}
|
||||
|
||||
Output::
|
||||
${sys.output.join("")}
|
||||
|
||||
${patch ? vfs.formatPatch(patch) : ""}`
|
||||
};
|
||||
};
|
||||
return sys;
|
||||
}
|
||||
|
||||
export function makeSystemReadyForBaseline(sys: TscCompileSystem, versionToWrite?: string) {
|
||||
if (versionToWrite) {
|
||||
fakes.patchHostForBuildInfoWrite(sys, versionToWrite);
|
||||
}
|
||||
else {
|
||||
fakes.patchHostForBuildInfoReadWrite(sys);
|
||||
}
|
||||
const writtenFiles = sys.writtenFiles = new Set();
|
||||
const originalWriteFile = sys.writeFile;
|
||||
sys.writeFile = (fileName, content, writeByteOrderMark) => {
|
||||
const path = toPathWithSystem(sys, fileName);
|
||||
// When buildinfo is same for two projects,
|
||||
// it gives error and doesnt write buildinfo but because buildInfo is written for one project,
|
||||
// readable baseline will be written two times for those two projects with same contents and is ok
|
||||
ts.Debug.assert(!writtenFiles.has(path) || ts.endsWith(path, "baseline.txt"));
|
||||
writtenFiles.add(path);
|
||||
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Fs, execute command line and save baseline
|
||||
*/
|
||||
export function testTscCompile(input: TestTscCompile) {
|
||||
let actualReadFileMap: ts.MapLike<number> | undefined;
|
||||
let getPrograms: CommandLineCallbacks["getPrograms"] | undefined;
|
||||
return testTscCompileLike({
|
||||
...input,
|
||||
compile: commandLineCompile,
|
||||
additionalBaseline
|
||||
});
|
||||
|
||||
function commandLineCompile(sys: TscCompileSystem) {
|
||||
makeSystemReadyForBaseline(sys);
|
||||
actualReadFileMap = {};
|
||||
const originalReadFile = sys.readFile;
|
||||
sys.readFile = path => {
|
||||
// Dont record libs
|
||||
if (path.startsWith("/src/")) {
|
||||
actualReadFileMap![path] = (ts.getProperty(actualReadFileMap!, path) || 0) + 1;
|
||||
}
|
||||
return originalReadFile.call(sys, path);
|
||||
};
|
||||
|
||||
const result = commandLineCallbacks(sys, originalReadFile);
|
||||
ts.executeCommandLine(
|
||||
sys,
|
||||
result.cb,
|
||||
input.commandLineArgs,
|
||||
);
|
||||
sys.readFile = originalReadFile;
|
||||
getPrograms = result.getPrograms;
|
||||
}
|
||||
|
||||
function additionalBaseline(sys: TscCompileSystem) {
|
||||
const { baselineSourceMap, baselineReadFileCalls, baselinePrograms: shouldBaselinePrograms, baselineDependencies } = input;
|
||||
if (input.computeDtsSignatures) storeDtsSignatures(sys, getPrograms!());
|
||||
if (shouldBaselinePrograms) {
|
||||
const baseline: string[] = [];
|
||||
baselinePrograms(baseline, getPrograms!, ts.emptyArray, baselineDependencies);
|
||||
sys.write(baseline.join("\n"));
|
||||
}
|
||||
if (baselineReadFileCalls) {
|
||||
sys.write(`readFiles:: ${JSON.stringify(actualReadFileMap, /*replacer*/ undefined, " ")} `);
|
||||
}
|
||||
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
|
||||
actualReadFileMap = undefined;
|
||||
getPrograms = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function storeDtsSignatures(sys: TscCompileSystem, programs: readonly CommandLineProgram[]) {
|
||||
for (const [program, builderProgram] of programs) {
|
||||
if (!builderProgram) continue;
|
||||
const buildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(program.getCompilerOptions());
|
||||
if (!buildInfoPath) continue;
|
||||
sys.dtsSignaures ??= new Map();
|
||||
const dtsSignatureData = new Map<string, DtsSignatureData>();
|
||||
sys.dtsSignaures.set(`${toPathWithSystem(sys, buildInfoPath)}.readable.baseline.txt` as ts.Path, dtsSignatureData);
|
||||
const state = builderProgram.getState();
|
||||
state.hasCalledUpdateShapeSignature?.forEach(resolvedPath => {
|
||||
const file = program.getSourceFileByPath(resolvedPath);
|
||||
if (!file || file.isDeclarationFile) return;
|
||||
// Compute dts and exported map and store it
|
||||
ts.BuilderState.computeDtsSignature(
|
||||
program,
|
||||
file,
|
||||
/*cancellationToken*/ undefined,
|
||||
sys,
|
||||
(signature, sourceFiles) => {
|
||||
const exportedModules = ts.BuilderState.getExportedModules(state.exportedModulesMap && sourceFiles[0].exportedModulesFromDeclarationEmit);
|
||||
dtsSignatureData.set(relativeToBuildInfo(resolvedPath), { signature, exportedModules: exportedModules && ts.arrayFrom(exportedModules.keys(), relativeToBuildInfo) });
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
function relativeToBuildInfo(path: string) {
|
||||
const currentDirectory = program.getCurrentDirectory();
|
||||
const getCanonicalFileName = ts.createGetCanonicalFileName(program.useCaseSensitiveFileNames());
|
||||
const buildInfoDirectory = ts.getDirectoryPath(ts.getNormalizedAbsolutePath(buildInfoPath!, currentDirectory));
|
||||
return ts.ensurePathIsNonModuleName(ts.getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyTscBaseline(sys: () => { baseLine: TscCompileSystem["baseLine"]; }) {
|
||||
it(`Generates files matching the baseline`, () => {
|
||||
const { file, text } = sys().baseLine();
|
||||
Harness.Baseline.runBaseline(file, text);
|
||||
});
|
||||
}
|
||||
export interface VerifyTscCompileLike {
|
||||
scenario: string;
|
||||
subScenario: string;
|
||||
commandLineArgs: readonly string[];
|
||||
fs: () => vfs.FileSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify by baselining after initializing FS and custom compile
|
||||
*/
|
||||
export function verifyTscCompileLike<T extends VerifyTscCompileLike>(verifier: (input: T) => { baseLine: TscCompileSystem["baseLine"]; }, input: T) {
|
||||
describe(`tsc ${input.commandLineArgs.join(" ")} ${input.scenario}:: ${input.subScenario}`, () => {
|
||||
describe(input.scenario, () => {
|
||||
describe(input.subScenario, () => {
|
||||
verifyTscBaseline(() => verifier({
|
||||
...input,
|
||||
fs: () => input.fs().makeReadonly()
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
interface VerifyTscEditDiscrepanciesInput {
|
||||
index: number;
|
||||
edits: readonly TestTscEdit[];
|
||||
scenario: TestTscCompile["scenario"];
|
||||
baselines: string[] | undefined;
|
||||
commandLineArgs: TestTscCompile["commandLineArgs"];
|
||||
modifyFs: TestTscCompile["modifyFs"];
|
||||
baseFs: vfs.FileSystem;
|
||||
newSys: TscCompileSystem;
|
||||
environmentVariables: TestTscCompile["environmentVariables"];
|
||||
}
|
||||
function verifyTscEditDiscrepancies({
|
||||
index, edits, scenario, commandLineArgs, environmentVariables,
|
||||
baselines,
|
||||
modifyFs, baseFs, newSys
|
||||
}: VerifyTscEditDiscrepanciesInput): string[] | undefined {
|
||||
const { caption, discrepancyExplanation } = edits[index];
|
||||
const sys = testTscCompile({
|
||||
scenario,
|
||||
subScenario: caption,
|
||||
fs: () => baseFs.makeReadonly(),
|
||||
commandLineArgs: edits[index].commandLineArgs || commandLineArgs,
|
||||
modifyFs: fs => {
|
||||
if (modifyFs) modifyFs(fs);
|
||||
for (let i = 0; i <= index; i++) {
|
||||
edits[i].edit(fs);
|
||||
}
|
||||
},
|
||||
environmentVariables,
|
||||
computeDtsSignatures: true,
|
||||
});
|
||||
let headerAdded = false;
|
||||
for (const outputFile of sys.writtenFiles.keys()) {
|
||||
const cleanBuildText = sys.readFile(outputFile);
|
||||
const incrementalBuildText = newSys.readFile(outputFile);
|
||||
if (ts.isBuildInfoFile(outputFile)) {
|
||||
// Check only presence and absence and not text as we will do that for readable baseline
|
||||
if (!sys.fileExists(`${outputFile}.readable.baseline.txt`)) addBaseline(`Readable baseline not present in clean build:: File:: ${outputFile}`);
|
||||
if (!newSys.fileExists(`${outputFile}.readable.baseline.txt`)) addBaseline(`Readable baseline not present in incremental build:: File:: ${outputFile}`);
|
||||
verifyPresenceAbsence(incrementalBuildText, cleanBuildText, `Incremental and clean tsbuildinfo file presence differs:: File:: ${outputFile}`);
|
||||
}
|
||||
else if (!ts.fileExtensionIs(outputFile, ".tsbuildinfo.readable.baseline.txt")) {
|
||||
verifyTextEqual(incrementalBuildText, cleanBuildText, `File: ${outputFile}`);
|
||||
}
|
||||
else if (incrementalBuildText !== cleanBuildText) {
|
||||
// Verify build info without affectedFilesPendingEmit
|
||||
const { buildInfo: incrementalBuildInfo, readableBuildInfo: incrementalReadableBuildInfo } = getBuildInfoForIncrementalCorrectnessCheck(incrementalBuildText);
|
||||
const { buildInfo: cleanBuildInfo, readableBuildInfo: cleanReadableBuildInfo } = getBuildInfoForIncrementalCorrectnessCheck(cleanBuildText);
|
||||
const dtsSignaures = sys.dtsSignaures?.get(outputFile);
|
||||
verifyTextEqual(incrementalBuildInfo, cleanBuildInfo, `TsBuild info text without affectedFilesPendingEmit:: ${outputFile}::`);
|
||||
// Verify file info sigantures
|
||||
verifyMapLike(
|
||||
incrementalReadableBuildInfo?.program?.fileInfos as ReadableProgramMultiFileEmitBuildInfo["fileInfos"],
|
||||
cleanReadableBuildInfo?.program?.fileInfos as ReadableProgramMultiFileEmitBuildInfo["fileInfos"],
|
||||
(key, incrementalFileInfo, cleanFileInfo) => {
|
||||
const dtsForKey = dtsSignaures?.get(key);
|
||||
if (!incrementalFileInfo || !cleanFileInfo || incrementalFileInfo.signature !== cleanFileInfo.signature && (!dtsForKey || incrementalFileInfo.signature !== dtsForKey.signature)) {
|
||||
return [
|
||||
`Incremental signature is neither dts signature nor file version for File:: ${key}`,
|
||||
`Incremental:: ${JSON.stringify(incrementalFileInfo, /*replacer*/ undefined, 2)}`,
|
||||
`Clean:: ${JSON.stringify(cleanFileInfo, /*replacer*/ undefined, 2)}`,
|
||||
`Dts Signature:: $${JSON.stringify(dtsForKey?.signature)}`
|
||||
];
|
||||
}
|
||||
},
|
||||
`FileInfos:: File:: ${outputFile}`
|
||||
);
|
||||
if (!isReadableProgramBundleEmitBuildInfo(incrementalReadableBuildInfo?.program)) {
|
||||
ts.Debug.assert(!isReadableProgramBundleEmitBuildInfo(cleanReadableBuildInfo?.program));
|
||||
// Verify exportedModulesMap
|
||||
verifyMapLike(
|
||||
incrementalReadableBuildInfo?.program?.exportedModulesMap,
|
||||
cleanReadableBuildInfo?.program?.exportedModulesMap,
|
||||
(key, incrementalReferenceSet, cleanReferenceSet) => {
|
||||
const dtsForKey = dtsSignaures?.get(key);
|
||||
if (!ts.arrayIsEqualTo(incrementalReferenceSet, cleanReferenceSet) &&
|
||||
(!dtsForKey || !ts.arrayIsEqualTo(incrementalReferenceSet, dtsForKey.exportedModules))) {
|
||||
return [
|
||||
`Incremental Reference set is neither from dts nor files reference map for File:: ${key}::`,
|
||||
`Incremental:: ${JSON.stringify(incrementalReferenceSet, /*replacer*/ undefined, 2)}`,
|
||||
`Clean:: ${JSON.stringify(cleanReferenceSet, /*replacer*/ undefined, 2)}`,
|
||||
`DtsExportsMap:: ${JSON.stringify(dtsForKey?.exportedModules, /*replacer*/ undefined, 2)}`
|
||||
];
|
||||
}
|
||||
},
|
||||
`exportedModulesMap:: File:: ${outputFile}`
|
||||
);
|
||||
// Verify that incrementally pending affected file emit are in clean build since clean build can contain more files compared to incremental depending of noEmitOnError option
|
||||
if (incrementalReadableBuildInfo?.program?.affectedFilesPendingEmit) {
|
||||
if (cleanReadableBuildInfo?.program?.affectedFilesPendingEmit === undefined) {
|
||||
addBaseline(
|
||||
`Incremental build contains affectedFilesPendingEmit, clean build does not have it: ${outputFile}::`,
|
||||
`Incremental buildInfoText:: ${incrementalBuildText}`,
|
||||
`Clean buildInfoText:: ${cleanBuildText}`
|
||||
);
|
||||
}
|
||||
let expectedIndex = 0;
|
||||
incrementalReadableBuildInfo.program.affectedFilesPendingEmit.forEach(([actualFileOrArray]) => {
|
||||
const actualFile = ts.isString(actualFileOrArray) ? actualFileOrArray : actualFileOrArray[0];
|
||||
expectedIndex = ts.findIndex(
|
||||
(cleanReadableBuildInfo!.program! as ReadableProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit,
|
||||
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
|
||||
expectedIndex
|
||||
);
|
||||
if (expectedIndex === -1) {
|
||||
addBaseline(
|
||||
`Incremental build contains ${actualFile} file as pending emit, clean build does not have it: ${outputFile}::`,
|
||||
`Incremental buildInfoText:: ${incrementalBuildText}`,
|
||||
`Clean buildInfoText:: ${cleanBuildText}`
|
||||
);
|
||||
}
|
||||
expectedIndex++;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!headerAdded && discrepancyExplanation) addBaseline("*** Supplied discrepancy explanation but didnt file any difference");
|
||||
return baselines;
|
||||
|
||||
function verifyTextEqual(incrementalText: string | undefined, cleanText: string | undefined, message: string) {
|
||||
if (incrementalText !== cleanText) writeNotEqual(incrementalText, cleanText, message);
|
||||
}
|
||||
|
||||
function verifyMapLike<T>(
|
||||
incremental: ts.MapLike<T> | undefined,
|
||||
clean: ts.MapLike<T> | undefined,
|
||||
verifyValue: (key: string, incrementalValue: T | undefined, cleanValue: T | undefined) => string[] | undefined,
|
||||
message: string,
|
||||
) {
|
||||
verifyPresenceAbsence(incremental, clean, `Incremental and clean do not match:: ${message}`);
|
||||
if (!incremental || !clean) return;
|
||||
const incrementalMap = new Map(Object.entries(incremental));
|
||||
const cleanMap = new Map(Object.entries(clean));
|
||||
cleanMap.forEach((cleanValue, key) => {
|
||||
const result = verifyValue(key, incrementalMap.get(key), cleanValue);
|
||||
if (result) addBaseline(...result);
|
||||
});
|
||||
incrementalMap.forEach((incremetnalValue, key) => {
|
||||
if (cleanMap.has(key)) return;
|
||||
// This is value only in incremental Map
|
||||
const result = verifyValue(key, incremetnalValue, /*cleanValue*/ undefined);
|
||||
if (result) addBaseline(...result);
|
||||
});
|
||||
}
|
||||
|
||||
function verifyPresenceAbsence<T>(actual: T | undefined, expected: T | undefined, message: string) {
|
||||
if (expected === undefined) {
|
||||
if (actual === undefined) return;
|
||||
}
|
||||
else {
|
||||
if (actual !== undefined) return;
|
||||
}
|
||||
writeNotEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
function writeNotEqual<T>(actual: T | undefined, expected: T | undefined, message: string) {
|
||||
addBaseline(
|
||||
message,
|
||||
"CleanBuild:",
|
||||
ts.isString(expected) ? expected : JSON.stringify(expected),
|
||||
"IncrementalBuild:",
|
||||
ts.isString(actual) ? actual : JSON.stringify(actual),
|
||||
);
|
||||
}
|
||||
|
||||
function addBaseline(...text: string[]) {
|
||||
if (!baselines || !headerAdded) {
|
||||
(baselines ||= []).push(`${index}:: ${caption}`, ...(discrepancyExplanation?.()|| ["*** Needs explanation"]));
|
||||
headerAdded = true;
|
||||
}
|
||||
baselines.push(...text);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildInfoForIncrementalCorrectnessCheck(text: string | undefined): {
|
||||
buildInfo: string | undefined;
|
||||
readableBuildInfo?: ReadableBuildInfo;
|
||||
} {
|
||||
if (!text) return { buildInfo: text };
|
||||
const readableBuildInfo = JSON.parse(text) as ReadableBuildInfo;
|
||||
let sanitizedFileInfos: ts.MapLike<string | Omit<ReadableProgramBuildInfoFileInfo<ts.ProgramMultiFileEmitBuildInfoFileInfo> | ReadableProgramBuildInfoFileInfo<ts.BuilderState.FileInfo>, "signature" | "original"> & { signature: undefined; original: undefined; }> | undefined;
|
||||
if (readableBuildInfo.program?.fileInfos) {
|
||||
sanitizedFileInfos = {};
|
||||
for (const id in readableBuildInfo.program.fileInfos) {
|
||||
if (ts.hasProperty(readableBuildInfo.program.fileInfos, id)) {
|
||||
const info = readableBuildInfo.program.fileInfos[id];
|
||||
sanitizedFileInfos[id] = ts.isString(info) ? info : { ...info, signature: undefined, original: undefined };
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
buildInfo: JSON.stringify({
|
||||
...readableBuildInfo,
|
||||
program: readableBuildInfo.program && {
|
||||
...readableBuildInfo.program,
|
||||
fileNames: undefined,
|
||||
fileNamesList: undefined,
|
||||
fileInfos: sanitizedFileInfos,
|
||||
// Ignore noEmit since that shouldnt be reason to emit the tsbuild info and presence of it in the buildinfo file does not matter
|
||||
options: { ...readableBuildInfo.program.options, noEmit: undefined },
|
||||
exportedModulesMap: undefined,
|
||||
affectedFilesPendingEmit: undefined,
|
||||
latestChangedDtsFile: readableBuildInfo.program.latestChangedDtsFile ? "FakeFileName" : undefined,
|
||||
},
|
||||
size: undefined, // Size doesnt need to be equal
|
||||
}, /*replacer*/ undefined, 2),
|
||||
readableBuildInfo,
|
||||
};
|
||||
}
|
||||
|
||||
export interface TestTscEdit {
|
||||
edit: (fs: vfs.FileSystem) => void;
|
||||
caption: string;
|
||||
commandLineArgs?: readonly string[];
|
||||
/** An array of lines to be printed in order when a discrepancy is detected */
|
||||
discrepancyExplanation?: () => readonly string[];
|
||||
}
|
||||
|
||||
export interface VerifyTscWithEditsInput extends TestTscCompile {
|
||||
edits?: readonly TestTscEdit[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify non watch tsc invokcation after each edit
|
||||
*/
|
||||
export function verifyTsc({
|
||||
subScenario, fs, scenario, commandLineArgs, environmentVariables,
|
||||
baselineSourceMap, modifyFs, baselineReadFileCalls, baselinePrograms,
|
||||
edits
|
||||
}: VerifyTscWithEditsInput) {
|
||||
describe(`tsc ${commandLineArgs.join(" ")} ${scenario}:: ${subScenario}`, () => {
|
||||
let sys: TscCompileSystem;
|
||||
let baseFs: vfs.FileSystem;
|
||||
let editsSys: TscCompileSystem[] | undefined;
|
||||
before(() => {
|
||||
baseFs = fs().makeReadonly();
|
||||
sys = testTscCompile({
|
||||
scenario,
|
||||
subScenario,
|
||||
fs: () => baseFs,
|
||||
commandLineArgs,
|
||||
modifyFs,
|
||||
baselineSourceMap,
|
||||
baselineReadFileCalls,
|
||||
baselinePrograms,
|
||||
environmentVariables,
|
||||
});
|
||||
edits?.forEach((
|
||||
{ edit, caption, commandLineArgs: editCommandLineArgs },
|
||||
index
|
||||
) => {
|
||||
(editsSys || (editsSys = [])).push(testTscCompile({
|
||||
scenario,
|
||||
subScenario: caption,
|
||||
diffWithInitial: true,
|
||||
fs: () => index === 0 ? sys.vfs : editsSys![index - 1].vfs,
|
||||
commandLineArgs: editCommandLineArgs || commandLineArgs,
|
||||
modifyFs: edit,
|
||||
baselineSourceMap,
|
||||
baselineReadFileCalls,
|
||||
baselinePrograms,
|
||||
environmentVariables,
|
||||
}));
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
baseFs = undefined!;
|
||||
sys = undefined!;
|
||||
editsSys = undefined!;
|
||||
});
|
||||
verifyTscBaseline(() => ({
|
||||
baseLine: () => {
|
||||
const { file, text } = sys.baseLine();
|
||||
const texts: string[] = [text];
|
||||
editsSys?.forEach((sys, index) => {
|
||||
const incrementalScenario = edits![index];
|
||||
texts.push("");
|
||||
texts.push(`Change:: ${incrementalScenario.caption}`);
|
||||
texts.push(sys.baseLine().text);
|
||||
});
|
||||
return {
|
||||
file,
|
||||
text: `currentDirectory:: ${sys.getCurrentDirectory()} useCaseSensitiveFileNames: ${sys.useCaseSensitiveFileNames}\r\n` +
|
||||
texts.join("\r\n"),
|
||||
};
|
||||
}
|
||||
}));
|
||||
if (edits?.length) {
|
||||
it("tsc invocation after edit and clean build correctness", () => {
|
||||
let baselines: string[] | undefined;
|
||||
for (let index = 0; index < edits.length; index++) {
|
||||
baselines = verifyTscEditDiscrepancies({
|
||||
index,
|
||||
edits,
|
||||
scenario,
|
||||
baselines,
|
||||
baseFs,
|
||||
newSys: editsSys![index],
|
||||
commandLineArgs,
|
||||
modifyFs,
|
||||
environmentVariables,
|
||||
});
|
||||
}
|
||||
Harness.Baseline.runBaseline(
|
||||
tscBaselineName(scenario, subScenario, commandLineArgs, /*isWatch*/ undefined, "-discrepancies"),
|
||||
baselines ? baselines.join("\r\n") : null // eslint-disable-line no-null/no-null
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+5
-40
@@ -6,19 +6,15 @@ import {
|
||||
CommandLineCallbacks,
|
||||
commandLineCallbacks,
|
||||
CommandLineProgram,
|
||||
createSolutionBuilderHostForBaseline,
|
||||
generateSourceMapBaselineFiles,
|
||||
} from "../tsc/helpers";
|
||||
tscBaselineName,
|
||||
} from "./baseline";
|
||||
import {
|
||||
changeToHostTrackingWrittenFiles,
|
||||
createWatchedSystem,
|
||||
File,
|
||||
FileOrFolderOrSymLink,
|
||||
FileOrFolderOrSymLinkMap,
|
||||
TestServerHost,
|
||||
TestServerHostCreationParameters,
|
||||
TestServerHostTrackingWrittenFiles,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "./virtualFileSystemWithWatch";
|
||||
|
||||
export const commonFile1: File = {
|
||||
path: "/a/b/commonFile1.ts",
|
||||
@@ -249,10 +245,10 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
|
||||
});
|
||||
}
|
||||
}
|
||||
Baseline.runBaseline(`${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}.js`, baseline.join("\r\n"));
|
||||
Baseline.runBaseline(tscBaselineName(scenario, subScenario, commandLineArgs, isWatch(commandLineArgs)), baseline.join("\r\n"));
|
||||
}
|
||||
|
||||
function isWatch(commandLineArgs: readonly string[]) {
|
||||
export function isWatch(commandLineArgs: readonly string[]) {
|
||||
return ts.forEach(commandLineArgs, arg => {
|
||||
if (arg.charCodeAt(0) !== ts.CharacterCodes.minus) return false;
|
||||
const option = arg.slice(arg.charCodeAt(1) === ts.CharacterCodes.minus ? 2 : 1).toLowerCase();
|
||||
@@ -296,34 +292,3 @@ export function verifyTscWatch(input: VerifyTscWatch) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: TestServerHost, rootNames: readonly string[], originalRead?: TestServerHost["readFile"]) {
|
||||
const host = createSolutionBuilderHostForBaseline(system, /*versionToWrite*/ undefined, originalRead);
|
||||
return ts.createSolutionBuilder(host, rootNames, {});
|
||||
}
|
||||
|
||||
export function ensureErrorFreeBuild(host: TestServerHost, rootNames: readonly string[]) {
|
||||
// ts build should succeed
|
||||
solutionBuildWithBaseline(host, rootNames);
|
||||
assert.equal(host.getOutput().length, 0, JSON.stringify(host.getOutput(), /*replacer*/ undefined, " "));
|
||||
}
|
||||
|
||||
export function solutionBuildWithBaseline(sys: TestServerHost, solutionRoots: readonly string[], originalRead?: TestServerHost["readFile"]) {
|
||||
const originalReadFile = sys.readFile;
|
||||
const originalWrite = sys.write;
|
||||
const originalWriteFile = sys.writeFile;
|
||||
ts.Debug.assert(sys.writtenFiles === undefined);
|
||||
const solutionBuilder = createSolutionBuilder(changeToHostTrackingWrittenFiles(
|
||||
patchHostForBuildInfoReadWrite(sys)
|
||||
), solutionRoots, originalRead);
|
||||
solutionBuilder.build();
|
||||
sys.readFile = originalReadFile;
|
||||
sys.write = originalWrite;
|
||||
sys.writeFile = originalWriteFile;
|
||||
sys.writtenFiles = undefined;
|
||||
return sys;
|
||||
}
|
||||
|
||||
export function createSystemWithSolutionBuild(solutionRoots: readonly string[], files: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters) {
|
||||
return solutionBuildWithBaseline(createWatchedSystem(files, params), solutionRoots);
|
||||
}
|
||||
+13
-9
@@ -1,6 +1,7 @@
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { ensureErrorFreeBuild } from "../tscWatch/helpers";
|
||||
import { ActionWatchTypingLocations } from "../../_namespaces/ts.server";
|
||||
import { ensureErrorFreeBuild } from "./solutionBuilder";
|
||||
import {
|
||||
changeToHostTrackingWrittenFiles,
|
||||
createServerHost,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
libFile,
|
||||
TestServerHost,
|
||||
TestServerHostTrackingWrittenFiles,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "./virtualFileSystemWithWatch";
|
||||
|
||||
export const customTypesMap = {
|
||||
path: "/typesMap.json" as ts.Path,
|
||||
@@ -116,9 +117,9 @@ export function createLoggerWritingToConsole(host: TestServerHost): Logger {
|
||||
|
||||
function sanitizeLog(s: string) {
|
||||
return s.replace(/Elapsed::?\s*\d+(?:\.\d+)?ms/g, "Elapsed:: *ms")
|
||||
.replace(/\"updateGraphDurationMs\"\:\d+(?:\.\d+)?/g, `"updateGraphDurationMs":*`)
|
||||
.replace(/\"createAutoImportProviderProgramDurationMs\"\:\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs":*`)
|
||||
.replace(`"version":"${ts.version}"`, `"version":"FakeVersion"`)
|
||||
.replace(/\"updateGraphDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"updateGraphDurationMs": *`)
|
||||
.replace(/\"createAutoImportProviderProgramDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs": *`)
|
||||
.replace(versionRegExp, `FakeVersion`)
|
||||
.replace(/getCompletionData: Get current token: \d+(?:\.\d+)?/g, `getCompletionData: Get current token: *`)
|
||||
.replace(/getCompletionData: Is inside comment: \d+(?:\.\d+)?/g, `getCompletionData: Is inside comment: *`)
|
||||
.replace(/getCompletionData: Get previous token: \d+(?:\.\d+)?/g, `getCompletionData: Get previous token: *`)
|
||||
@@ -280,11 +281,12 @@ export class TestTypingsInstallerWorker extends ts.server.typingsInstaller.Typin
|
||||
this.addPostExecAction("success", requestId, packageNames, cb);
|
||||
}
|
||||
|
||||
sendResponse(response: ts.server.SetTypings | ts.server.InvalidateCachedTypings) {
|
||||
sendResponse(response: ts.server.SetTypings | ts.server.InvalidateCachedTypings | ts.server.WatchTypingLocations) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Sending response:\n ${JSON.stringify(response)}`);
|
||||
}
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
if (response.kind !== ActionWatchTypingLocations) this.projectService.updateTypingsForProject(response);
|
||||
else this.projectService.watchTypingLocations(response);
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest(project: ts.server.Project, typeAcquisition: ts.TypeAcquisition, unresolvedImports: ts.SortedReadonlyArray<string>) {
|
||||
@@ -324,7 +326,9 @@ export class TestTypingsInstaller<T extends TestTypingsInstallerWorker = TestTyp
|
||||
this.projectService = projectService;
|
||||
}
|
||||
|
||||
onProjectClosed = ts.noop;
|
||||
onProjectClosed(p: ts.server.Project) {
|
||||
this.installer?.closeProject({ projectName: p.getProjectName(), kind: "closeProject" });
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest(project: ts.server.Project, typeAcquisition: ts.TypeAcquisition, unresolvedImports: ts.SortedReadonlyArray<string>) {
|
||||
if (!this.installer) {
|
||||
@@ -868,4 +872,4 @@ export function logInferredProjectsOrphanStatus(projectService: ts.server.Projec
|
||||
|
||||
export function logConfiguredProjectsHasOpenRefStatus(projectService: ts.server.ProjectService) {
|
||||
projectService.configuredProjects.forEach(configuredProject => (projectService.logger as Logger).log(`Configured project: ${configuredProject.projectName} hasOpenRef:: ${configuredProject.hasOpenRef()} isClosed: ${configuredProject.isClosed()}`));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import { getDirectoryPath } from "../../_namespaces/ts";
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import * as vpath from "../../_namespaces/vpath";
|
||||
import { libContent } from "./contents";
|
||||
|
||||
export interface FsOptions {
|
||||
libContentToAppend?: string;
|
||||
cwd?: string;
|
||||
executingFilePath?: string;
|
||||
}
|
||||
export type FsOptionsOrLibContentsToAppend = FsOptions | string;
|
||||
|
||||
function valueOfFsOptions(options: FsOptionsOrLibContentsToAppend | undefined, key: keyof FsOptions) {
|
||||
return typeof options === "string" ?
|
||||
key === "libContentToAppend" ? options : undefined :
|
||||
options?.[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load project from disk into /src folder
|
||||
*/
|
||||
export function loadProjectFromDisk(
|
||||
root: string,
|
||||
options?: FsOptionsOrLibContentsToAppend
|
||||
): vfs.FileSystem {
|
||||
const resolver = vfs.createResolver(Harness.IO);
|
||||
return loadProjectFromFiles({
|
||||
["/src"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), root), resolver)
|
||||
}, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* All the files must be in /src
|
||||
*/
|
||||
export function loadProjectFromFiles(
|
||||
files: vfs.FileSet,
|
||||
options?: FsOptionsOrLibContentsToAppend,
|
||||
): vfs.FileSystem {
|
||||
const executingFilePath = valueOfFsOptions(options, "executingFilePath");
|
||||
const defaultLibLocation = executingFilePath ? getDirectoryPath(executingFilePath) : "/lib";
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
|
||||
files,
|
||||
cwd: valueOfFsOptions(options, "cwd") || "/",
|
||||
meta: { defaultLibLocation },
|
||||
});
|
||||
const libContentToAppend = valueOfFsOptions(options, "libContentToAppend");
|
||||
fs.mkdirpSync(defaultLibLocation);
|
||||
fs.writeFileSync(`${defaultLibLocation}/lib.d.ts`, libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
|
||||
fs.makeReadonly();
|
||||
return fs;
|
||||
}
|
||||
|
||||
export function replaceText(fs: vfs.FileSystem, path: string, oldText: string, newText: string) {
|
||||
if (!fs.statSync(path).isFile()) {
|
||||
throw new Error(`File ${path} does not exist`);
|
||||
}
|
||||
const old = fs.readFileSync(path, "utf-8");
|
||||
if (old.indexOf(oldText) < 0) {
|
||||
throw new Error(`Text "${oldText}" does not exist in file ${path}`);
|
||||
}
|
||||
const newContent = old.replace(oldText, newText);
|
||||
fs.writeFileSync(path, newContent, "utf-8");
|
||||
}
|
||||
|
||||
export function prependText(fs: vfs.FileSystem, path: string, additionalContent: string) {
|
||||
if (!fs.statSync(path).isFile()) {
|
||||
throw new Error(`File ${path} does not exist`);
|
||||
}
|
||||
const old = fs.readFileSync(path, "utf-8");
|
||||
fs.writeFileSync(path, `${additionalContent}${old}`, "utf-8");
|
||||
}
|
||||
|
||||
export function appendText(fs: vfs.FileSystem, path: string, additionalContent: string) {
|
||||
if (!fs.statSync(path).isFile()) {
|
||||
throw new Error(`File ${path} does not exist`);
|
||||
}
|
||||
const old = fs.readFileSync(path, "utf-8");
|
||||
fs.writeFileSync(path, `${old}${additionalContent}`);
|
||||
}
|
||||
|
||||
export function enableStrict(fs: vfs.FileSystem, path: string) {
|
||||
replaceText(fs, path, `"strict": false`, `"strict": true`);
|
||||
}
|
||||
|
||||
export function addTestPrologue(fs: vfs.FileSystem, path: string, prologue: string) {
|
||||
prependText(fs, path, `${prologue}
|
||||
`);
|
||||
}
|
||||
|
||||
export function addShebang(fs: vfs.FileSystem, project: string, file: string) {
|
||||
prependText(fs, `src/${project}/${file}.ts`, `#!someshebang ${project} ${file}
|
||||
`);
|
||||
}
|
||||
|
||||
export function restContent(project: string, file: string) {
|
||||
return `function for${project}${file}Rest() {
|
||||
const { b, ...rest } = { a: 10, b: 30, yy: 30 };
|
||||
}`;
|
||||
}
|
||||
function nonrestContent(project: string, file: string) {
|
||||
return `function for${project}${file}Rest() { }`;
|
||||
}
|
||||
|
||||
export function addRest(fs: vfs.FileSystem, project: string, file: string) {
|
||||
appendText(fs, `src/${project}/${file}.ts`, restContent(project, file));
|
||||
}
|
||||
|
||||
export function removeRest(fs: vfs.FileSystem, project: string, file: string) {
|
||||
replaceText(fs, `src/${project}/${file}.ts`, restContent(project, file), nonrestContent(project, file));
|
||||
}
|
||||
|
||||
export function addStubFoo(fs: vfs.FileSystem, project: string, file: string) {
|
||||
appendText(fs, `src/${project}/${file}.ts`, nonrestContent(project, file));
|
||||
}
|
||||
|
||||
export function changeStubToRest(fs: vfs.FileSystem, project: string, file: string) {
|
||||
replaceText(fs, `src/${project}/${file}.ts`, nonrestContent(project, file), restContent(project, file));
|
||||
}
|
||||
|
||||
export function addSpread(fs: vfs.FileSystem, project: string, file: string) {
|
||||
const path = `src/${project}/${file}.ts`;
|
||||
const content = fs.readFileSync(path, "utf8");
|
||||
fs.writeFileSync(path, `${content}
|
||||
function ${project}${file}Spread(...b: number[]) { }
|
||||
const ${project}${file}_ar = [20, 30];
|
||||
${project}${file}Spread(10, ...${project}${file}_ar);`);
|
||||
|
||||
replaceText(fs, `src/${project}/tsconfig.json`, `"strict": false,`, `"strict": false,
|
||||
"downlevelIteration": true,`);
|
||||
}
|
||||
|
||||
export function getTripleSlashRef(project: string) {
|
||||
return `/src/${project}/tripleRef.d.ts`;
|
||||
}
|
||||
|
||||
export function addTripleSlashRef(fs: vfs.FileSystem, project: string, file: string) {
|
||||
fs.writeFileSync(getTripleSlashRef(project), `declare class ${project}${file} { }`);
|
||||
prependText(fs, `src/${project}/${file}.ts`, `///<reference path="./tripleRef.d.ts"/>
|
||||
const ${file}Const = new ${project}${file}();
|
||||
`);
|
||||
}
|
||||
+6
-5
@@ -1,4 +1,4 @@
|
||||
import * as Harness from "../_namespaces/Harness";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import {
|
||||
clear,
|
||||
clone,
|
||||
@@ -31,19 +31,19 @@ import {
|
||||
isString,
|
||||
mapDefined,
|
||||
matchFiles,
|
||||
ModuleImportResult,
|
||||
ModuleResolutionHost,
|
||||
MultiMap,
|
||||
noop,
|
||||
patchWriteFileEnsuringDirectory,
|
||||
Path,
|
||||
PollingInterval,
|
||||
RequireResult,
|
||||
server,
|
||||
SortedArray,
|
||||
sys,
|
||||
toPath,
|
||||
} from "../_namespaces/ts";
|
||||
import { timeIncrements } from "../_namespaces/vfs";
|
||||
} from "../../_namespaces/ts";
|
||||
import { timeIncrements } from "../../_namespaces/vfs";
|
||||
|
||||
export const libFile: File = {
|
||||
path: "/a/lib/lib.d.ts",
|
||||
@@ -292,7 +292,8 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
private readonly environmentVariables?: Map<string, string>;
|
||||
private readonly executingFilePath: string;
|
||||
private readonly currentDirectory: string;
|
||||
public require: ((initialPath: string, moduleName: string) => RequireResult) | undefined;
|
||||
require?: (initialPath: string, moduleName: string) => ModuleImportResult;
|
||||
importPlugin?: (root: string, moduleName: string) => Promise<ModuleImportResult>;
|
||||
public storeFilesChangingSignatureDuringEmit = true;
|
||||
watchFile: HostWatchFile;
|
||||
private inodeWatching: boolean | undefined;
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
createWatchedSystem,
|
||||
File,
|
||||
libFile,
|
||||
} from "./virtualFileSystemWithWatch";
|
||||
} from "./helpers/virtualFileSystemWithWatch";
|
||||
|
||||
describe("unittests:: Reuse program structure:: General", () => {
|
||||
function baselineCache<T>(baselines: string[], cacheType: string, cache: ts.ModeAwareCache<T> | undefined) {
|
||||
@@ -549,6 +549,7 @@ describe("unittests:: Reuse program structure:: isProgramUptoDate", () => {
|
||||
program, newRootFileNames, newOptions,
|
||||
path => program.getSourceFileByPath(path)!.version, /*fileExists*/ ts.returnFalse,
|
||||
/*hasInvalidatedResolutions*/ ts.returnFalse,
|
||||
/*hasInvalidatedLibResolutions*/ ts.returnFalse,
|
||||
/*hasChangedAutomaticTypeDirectiveNames*/ undefined,
|
||||
/*getParsedCommandLine*/ ts.returnUndefined,
|
||||
/*projectReferences*/ undefined
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { createProjectService } from "../tsserver/helpers";
|
||||
import { createProjectService } from "../helpers/tsserver";
|
||||
import {
|
||||
createServerHost,
|
||||
File,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "../helpers/virtualFileSystemWithWatch";
|
||||
import {
|
||||
extractTest,
|
||||
newLineCharacter,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Harness from "../../../_namespaces/Harness";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import { createProjectService } from "../../tsserver/helpers";
|
||||
import { createProjectService } from "../../helpers/tsserver";
|
||||
import {
|
||||
createServerHost,
|
||||
libFile,
|
||||
} from "../../virtualFileSystemWithWatch";
|
||||
} from "../../helpers/virtualFileSystemWithWatch";
|
||||
|
||||
interface Range {
|
||||
pos: number;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createServerHost,
|
||||
File,
|
||||
libFile,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "../helpers/virtualFileSystemWithWatch";
|
||||
|
||||
describe("unittests:: services:: languageService", () => {
|
||||
const files: {[index: string]: string} = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { createProjectService } from "../tsserver/helpers";
|
||||
import { createProjectService } from "../helpers/tsserver";
|
||||
import {
|
||||
createServerHost,
|
||||
File,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "../helpers/virtualFileSystemWithWatch";
|
||||
import { newLineCharacter } from "./extract/helpers";
|
||||
|
||||
describe("unittests:: services:: organizeImports", () => {
|
||||
|
||||
@@ -130,7 +130,7 @@ var x = 0;`, {
|
||||
testVerbatimModuleSyntax: true
|
||||
});
|
||||
|
||||
transpilesCorrectly("Generates module output", `var x = 0;`, {
|
||||
transpilesCorrectly("Generates module output", `var x = 0; export {};`, {
|
||||
options: { compilerOptions: { module: ts.ModuleKind.AMD } }
|
||||
});
|
||||
|
||||
@@ -139,7 +139,7 @@ var x = 0;`, {
|
||||
testVerbatimModuleSyntax: true
|
||||
});
|
||||
|
||||
transpilesCorrectly("Sets module name", "var x = 1;", {
|
||||
transpilesCorrectly("Sets module name", "var x = 1; export {};", {
|
||||
options: { compilerOptions: { module: ts.ModuleKind.System, newLine: ts.NewLineKind.LineFeed }, moduleName: "NamedModule" }
|
||||
});
|
||||
|
||||
|
||||
@@ -304,6 +304,7 @@ describe("unittests:: TransformAPI", () => {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
module: ts.ModuleKind.System,
|
||||
newLine: ts.NewLineKind.CarriageReturnLineFeed,
|
||||
moduleDetection: ts.ModuleDetectionKind.Force,
|
||||
}
|
||||
}).outputText;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
addRest,
|
||||
addShebang,
|
||||
@@ -10,9 +13,8 @@ import {
|
||||
enableStrict,
|
||||
loadProjectFromDisk,
|
||||
removeRest,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: outFile:: on amd modules with --out", () => {
|
||||
let outFileFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild - clean", () => {
|
||||
verifyTsc({
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import { compilerOptionsToConfigJson } from "../helpers/contents";
|
||||
import {
|
||||
appendText,
|
||||
compilerOptionsToConfigJson,
|
||||
loadProjectFromFiles,
|
||||
noChangeRun,
|
||||
replaceText,
|
||||
TestTscEdit,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
appendText,
|
||||
loadProjectFromFiles, replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: commandLine::", () => {
|
||||
describe("different options::", () => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { dedent } from "../../_namespaces/Utils";
|
||||
import {
|
||||
noChangeRun,
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
appendText,
|
||||
loadProjectFromDisk,
|
||||
loadProjectFromFiles,
|
||||
noChangeRun,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
loadProjectFromFiles, replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: configFileErrors:: when tsconfig extends the missing file", () => {
|
||||
verifyTsc({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: configFileExtends:: when tsconfig extends another config", () => {
|
||||
function getConfigExtendsWithIncludeFs() {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import {
|
||||
noChangeOnlyRuns,
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
loadProjectFromFiles,
|
||||
noChangeOnlyRuns,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
loadProjectFromFiles, replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: when containerOnly project is referenced", () => {
|
||||
verifyTsc({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: declarationEmit", () => {
|
||||
function getFiles(): vfs.FileSet {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
prependText,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: on demo project", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromDisk } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild - empty files option in tsconfig", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/33849
|
||||
describe("unittests:: tsbuild:: exitCodeOnBogusFile:: test exit code", () => {
|
||||
|
||||
@@ -2,11 +2,11 @@ import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent
|
||||
} from "../../_namespaces/Utils";
|
||||
import { compilerOptionsToConfigJson } from "../helpers/contents";
|
||||
import {
|
||||
compilerOptionsToConfigJson,
|
||||
loadProjectFromFiles,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: fileDelete::", () => {
|
||||
function fs(childOptions: ts.CompilerOptions, mainOptions?: ts.CompilerOptions) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
appendText,
|
||||
loadProjectFromDisk,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import { symbolLibContent } from "../helpers/contents";
|
||||
import {
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
replaceText,
|
||||
symbolLibContent,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: javascriptProjectEmit::", () => {
|
||||
verifyTsc({
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
appendText,
|
||||
loadProjectFromDisk,
|
||||
replaceText,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: lateBoundSymbol:: interface is merged and contains late bound member", () => {
|
||||
verifyTsc({
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getFsForLibResolution } from "../helpers/libraryResolution";
|
||||
import { verifyTsc } from "../helpers/tsc";
|
||||
|
||||
describe("unittests:: tsbuild:: libraryResolution:: library file resolution", () => {
|
||||
function verify(libRedirection?: true) {
|
||||
verifyTsc({
|
||||
scenario: "libraryResolution",
|
||||
subScenario: `with config${libRedirection ? " with redirection" : ""}`,
|
||||
fs: () => getFsForLibResolution(libRedirection),
|
||||
commandLineArgs: ["-b", "project1", "project2", "project3", "project4", "--verbose", "--explainFiles"],
|
||||
baselinePrograms: true,
|
||||
});
|
||||
}
|
||||
verify();
|
||||
verify(/*libRedirection*/ true);
|
||||
});
|
||||
@@ -1,15 +1,15 @@
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
noChangeOnlyRuns,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
import { verifyTscWatch } from "../tscWatch/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { verifyTscWatch } from "../helpers/tscWatch";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
import {
|
||||
createWatchedSystem,
|
||||
libFile,
|
||||
} from "../virtualFileSystemWithWatch";
|
||||
} from "../helpers/virtualFileSystemWithWatch";
|
||||
|
||||
describe("unittests:: tsbuild:: moduleResolution:: handles the modules and options from referenced project correctly", () => {
|
||||
function sys(optionsToExtend?: ts.CompilerOptions) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import { symbolLibContent } from "../helpers/contents";
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
symbolLibContent,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
import { libFile } from "../virtualFileSystemWithWatch";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
import { libFile } from "../helpers/virtualFileSystemWithWatch";
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/31696
|
||||
describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
loadProjectFromFiles,
|
||||
noChangeRun,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromFiles } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: noEmit", () => {
|
||||
function verifyNoEmitWorker(subScenario: string, aTsContent: string, commandLineArgs: readonly string[]) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import {
|
||||
loadProjectFromDisk,
|
||||
noChangeRun,
|
||||
verifyTsc,
|
||||
} from "../tsc/helpers";
|
||||
} from "../helpers/tsc";
|
||||
import { loadProjectFromDisk } from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild - with noEmitOnError", () => {
|
||||
let projFs: vfs.FileSystem;
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import * as fakes from "../../_namespaces/fakes";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import * as vfs from "../../_namespaces/vfs";
|
||||
import { createSolutionBuilderHostForBaseline } from "../helpers/solutionBuilder";
|
||||
import {
|
||||
noChangeOnlyRuns,
|
||||
testTscCompileLike,
|
||||
TestTscEdit,
|
||||
TscCompileSystem,
|
||||
verifyTsc,
|
||||
verifyTscCompileLike,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
addRest,
|
||||
addShebang,
|
||||
@@ -9,20 +18,11 @@ import {
|
||||
addTestPrologue,
|
||||
addTripleSlashRef,
|
||||
appendText,
|
||||
changeStubToRest,
|
||||
createSolutionBuilderHostForBaseline,
|
||||
enableStrict,
|
||||
loadProjectFromDisk,
|
||||
noChangeOnlyRuns,
|
||||
prependText,
|
||||
changeStubToRest, enableStrict,
|
||||
loadProjectFromDisk, prependText,
|
||||
removeRest,
|
||||
replaceText,
|
||||
testTscCompileLike,
|
||||
TestTscEdit,
|
||||
TscCompileSystem,
|
||||
verifyTsc,
|
||||
verifyTscCompileLike,
|
||||
} from "../tsc/helpers";
|
||||
replaceText
|
||||
} from "../helpers/vfs";
|
||||
|
||||
describe("unittests:: tsbuild:: outFile::", () => {
|
||||
let outFileFs: vfs.FileSystem;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user