'Move to file' refactor (#53542)

This commit is contained in:
navya9singh
2023-04-21 11:48:53 -07:00
committed by GitHub
parent 09b1c55f83
commit d3bbef390d
48 changed files with 3437 additions and 910 deletions
+4
View File
@@ -7600,6 +7600,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 -5
View File
@@ -151,11 +151,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);
}
},
@@ -1046,7 +1053,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 +1084,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);
-1
View File
@@ -4239,7 +4239,6 @@ export interface SourceFileLike {
getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number;
}
/** @internal */
export interface RedirectInfo {
/** Source file this redirects to. */
+9 -1
View File
@@ -795,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,
@@ -818,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 {
+16 -5
View File
@@ -3299,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);
}
@@ -3912,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];
@@ -4217,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 {
+10
View File
@@ -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;
+3
View File
@@ -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.");
}
+25
View File
@@ -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",
@@ -606,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.
*/
@@ -680,6 +703,8 @@ export type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
refactor: string;
/* The 'name' property from the refactoring action */
action: string;
/* Arguments for interactive action */
interactiveRefactorArguments?: InteractiveRefactorArguments;
};
+17 -4
View File
@@ -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,
@@ -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));
},
+1
View File
@@ -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";
+3 -2
View File
@@ -2,6 +2,7 @@ import {
ApplicableRefactorInfo,
arrayFrom,
flatMapIterator,
InteractiveRefactorArguments,
Refactor,
RefactorContext,
RefactorEditInfo,
@@ -30,7 +31,7 @@ export function getApplicableRefactors(context: RefactorContext, includeInteract
}
/** @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
+34 -870
View File
@@ -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)));
}
+22 -1
View File
@@ -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,
@@ -135,6 +138,7 @@ import {
InlayHints,
InlayHintsContext,
insertSorted,
InteractiveRefactorArguments,
InterfaceType,
IntersectionType,
isArray,
@@ -277,6 +281,7 @@ import {
SourceFile,
SourceFileLike,
SourceMapSource,
startsWith,
Statement,
stringContains,
StringLiteral,
@@ -320,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";
@@ -2990,6 +2996,19 @@ export function createLanguageService(
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(
fileName: string,
formatOptions: FormatCodeSettings,
@@ -2997,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 {
@@ -3097,6 +3117,7 @@ export function createLanguageService(
updateIsDefinitionOfReferencedSymbols,
getApplicableRefactors,
getEditsForRefactor,
getMoveToRefactoringFileSuggestions,
toLineColumnOffset,
getSourceMapper: () => sourceMapper,
clearSourceMapperCache: () => sourceMapper.clearCache(),
+24 -14
View File
@@ -10,6 +10,7 @@ import {
concatenate,
ConstructorDeclaration,
contains,
createMultiMap,
createNodeFactory,
createPrinter,
createRange,
@@ -116,6 +117,7 @@ import {
mapDefined,
MethodSignature,
Modifier,
MultiMap,
NamedImportBindings,
NamedImports,
NamespaceImport,
@@ -337,6 +339,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 +487,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 +629,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 +1142,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 +1223,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 => {
@@ -1228,7 +1239,6 @@ namespace changesToText {
const textChanges = mapDefined(normalized, c => {
const span = createTextSpanFromRange(c.range);
const newText = computeNewText(c, sourceFile, newLineCharacter, formatContext, validate);
// Filter out redundant changes.
if (span.length === newText.length && stringContainsAt(sourceFile.text, newText, span.start)) {
return undefined;
@@ -1241,14 +1251,14 @@ 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;
+8 -3
View File
@@ -652,7 +652,8 @@ export interface LanguageService {
* 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): RefactorEditInfo | undefined;
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[];
@@ -1293,6 +1294,10 @@ export interface DocCommentTemplateOptions {
readonly generateReturnInDocTemplate?: boolean;
}
export interface InteractiveRefactorArguments {
targetFile: string;
}
export interface SignatureHelpParameter {
name: string;
documentation: SymbolDisplayPart[];
@@ -1785,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, includeInteractive?: boolean): readonly ApplicableRefactorInfo[];
getAvailableActions(context: RefactorContext, includeInteractive?: boolean, interactiveRefactorArguments?: InteractiveRefactorArguments): readonly ApplicableRefactorInfo[];
}
/** @internal */
+55 -1
View File
@@ -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,
@@ -267,6 +272,7 @@ import {
ModifierFlags,
ModuleDeclaration,
ModuleInstanceState,
ModuleKind,
ModuleResolutionKind,
ModuleSpecifierResolutionHost,
moduleSpecifiers,
@@ -350,6 +356,7 @@ import {
textSpanEnd,
Token,
tokenToString,
toPath,
tryCast,
Type,
TypeChecker,
@@ -3137,7 +3144,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 +4177,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;
}
+1
View File
@@ -199,3 +199,4 @@ import "./unittests/tsserver/versionCache";
import "./unittests/tsserver/watchEnvironment";
import "./unittests/debugDeprecation";
import "./unittests/tsserver/inconsistentErrorInEditor";
import "./unittests/tsserver/getMoveToRefactoringFileSuggestions";
@@ -0,0 +1,117 @@
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createLoggerWithInMemoryLogs,
createSession,
openFilesForSession
} from "../helpers/tsserver";
import {
createServerHost,
File
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsserver:: getMoveToRefactoringFileSuggestions", () => {
it("works for suggesting a list of files, excluding node_modules within a project", () => {
const file1: File = {
path: "/project/a/file1.ts",
content: `interface ka {
name: string;
}
`
};
const file2: File = { path: "/project/b/file2.ts", content: "" };
const file3: File = { path: "/project/d/e/file3.ts", content: "" };
const file4: File = {
path: "/project/a/file4.ts",
content: `import { value } from "../node_modules/@types/node/someFile.d.ts";
import { value1 } from "../node_modules/.cache/someFile.d.ts";`
};
const nodeModulesFile1: File = {
path: "project/node_modules/@types/node/someFile.d.ts",
content: `export const value = 0;`
};
const nodeModulesFile2: File = {
path: "project/node_modules/.cache/someFile.d.ts",
content: `export const value1 = 0;`
};
const tsconfig: File = {
path: "/project/tsconfig.json",
content: "{}",
};
const host = createServerHost([file1, file2, file3, file3, file4, nodeModulesFile1, nodeModulesFile2, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
command: ts.server.protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
arguments: { file: file1.path, line: 1, offset: 11 }
});
baselineTsserverLogs("getMoveToRefactoringFileSuggestions", "works for suggesting a list of files, excluding node_modules within a project", session);
});
it("suggests only .ts file for a .ts filepath", () => {
const file1: File = {
path: "/file1.ts",
content: `interface ka {
name: string;
}
`
};
const file2: File = { path: "/file2.tsx", content: "" };
const file3: File = { path: "/file3.mts", content: "" };
const file4: File = { path: "/file4.cts", content: "" };
const file5: File = { path: "/file5.js", content: "" };
const file6: File = { path: "/file6.d.ts", content: "" };
const file7: File = { path: "/file7.ts", content: "" };
const tsconfig: File = { path: "/tsconfig.json", content: JSON.stringify({ files: ["./file1.ts", "./file2.tsx", "./file3.mts", "./file4.cts", "./file5.js", "./file6.d.ts", "./file7.ts"] }) };
const host = createServerHost([file1, file2, file3, file4, file5, file6, file7, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
command: ts.server.protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
arguments: { file: file1.path, line: 1, offset: 11 }
});
baselineTsserverLogs("getMoveToRefactoringFileSuggestions", "suggests only .ts file for a .ts filepath", session);
});
it("suggests only .js file for a .js filepath", () => {
const file1: File = {
path: "/file1.js",
content: `class C {}`
};
const file2: File = { path: "/file2.js", content: "" };
const file3: File = { path: "/file3.mts", content: "" };
const file4: File = { path: "/file4.ts", content: "" };
const file5: File = { path: "/file5.js", content: "" };
const tsconfig: File = { path: "/tsconfig.json", content: JSON.stringify({ files: ["./file1.js", "./file2.js", "./file3.mts", "./file4.ts", "./file5.js"] }) };
const host = createServerHost([file1, file2, file3, file4, file5, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
command: ts.server.protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
arguments: { file: file1.path, line: 1, offset: 7 }
});
baselineTsserverLogs("getMoveToRefactoringFileSuggestions", "suggests only .js file for a .js filepath", session);
});
it("skips lib.d.ts files", () => {
const file1: File = {
path: "/file1.d.ts",
content: `class C {}`
};
const file2: File = { path: "/a/lib.d.ts", content: "" };
const file3: File = { path: "/a/file3.d.ts", content: "" };
const file4: File = { path: "/a/lib.es6.d.ts", content: "" };
const tsconfig: File = { path: "/tsconfig.json", content: JSON.stringify({ files: ["./file1.d.ts", "./a/lib.d.ts", "./a/file3.d.ts", "/a/lib.es6.d.ts"] }) };
const host = createServerHost([file1, file2, file3, file4, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
command: ts.server.protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
arguments: { file: file1.path, line: 1, offset: 7 }
});
baselineTsserverLogs("getMoveToRefactoringFileSuggestions", "skips lib.d.ts files", session);
});
});
@@ -94,4 +94,30 @@ describe("unittests:: tsserver:: refactors", () => {
});
baselineTsserverLogs("refactors", "handles canonicalization of tsconfig path", session);
});
it("handles moving statement to an existing file", () => {
const aTs: File = { path: "/Foo/a.ts", content: "const x = 0;" };
const bTs: File = {
path: "/Foo/b.ts", content: `import {} from "./bar";
const a = 1;`};
const tsconfig: File = { path: "/Foo/tsconfig.json", content: `{ "files": ["./a.ts", "./b.ts"] }` };
const host = createServerHost([aTs, bTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
command: ts.server.protocol.CommandTypes.GetEditsForRefactor,
arguments: {
file: aTs.path,
startLine: 1,
startOffset: 1,
endLine: 2,
endOffset: aTs.content.length,
refactor: "Move to file",
action: "Move to file",
interactiveRefactorArguments: { targetFile: "/Foo/b.ts" },
}
});
baselineTsserverLogs("refactors", "handles moving statement to an existing file", session);
});
});
+31 -1
View File
@@ -155,6 +155,7 @@ declare namespace ts {
GetSupportedCodeFixes = "getSupportedCodeFixes",
GetApplicableRefactors = "getApplicableRefactors",
GetEditsForRefactor = "getEditsForRefactor",
GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions",
OrganizeImports = "organizeImports",
GetEditsForFileRename = "getEditsForFileRename",
ConfigurePlugin = "configurePlugin",
@@ -518,6 +519,26 @@ declare namespace ts {
interface GetApplicableRefactorsResponse extends Response {
body?: ApplicableRefactorInfo[];
}
/**
* Request refactorings at a given position or selection area to move to an existing file.
*/
interface GetMoveToRefactoringFileSuggestionsRequest extends Request {
command: CommandTypes.GetMoveToRefactoringFileSuggestions;
arguments: GetMoveToRefactoringFileSuggestionsRequestArgs;
}
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
*/
interface GetMoveToRefactoringFileSuggestions extends Response {
body: {
newFileName: string;
files: string[];
};
}
/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
@@ -582,6 +603,7 @@ declare namespace ts {
type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
refactor: string;
action: string;
interactiveRefactorArguments?: InteractiveRefactorArguments;
};
interface GetEditsForRefactorResponse extends Response {
body?: RefactorEditInfo;
@@ -3987,6 +4009,7 @@ declare namespace ts {
private getRange;
private getApplicableRefactors;
private getEditsForRefactor;
private getMoveToRefactoringFileSuggestions;
private organizeImports;
private getEditsForFileRename;
private getCodeFixes;
@@ -10149,7 +10172,11 @@ declare namespace ts {
* 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): RefactorEditInfo | undefined;
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[];
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput;
@@ -10657,6 +10684,9 @@ declare namespace ts {
interface DocCommentTemplateOptions {
readonly generateReturnInDocTemplate?: boolean;
}
interface InteractiveRefactorArguments {
targetFile: string;
}
interface SignatureHelpParameter {
name: string;
documentation: SymbolDisplayPart[];
+8 -1
View File
@@ -6207,7 +6207,11 @@ declare namespace ts {
* 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): RefactorEditInfo | undefined;
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[];
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput;
@@ -6715,6 +6719,9 @@ declare namespace ts {
interface DocCommentTemplateOptions {
readonly generateReturnInDocTemplate?: boolean;
}
interface InteractiveRefactorArguments {
targetFile: string;
}
interface SignatureHelpParameter {
name: string;
documentation: SymbolDisplayPart[];
@@ -0,0 +1,118 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.d.ts]
class C {}
//// [/a/lib.d.ts]
//// [/a/file3.d.ts]
//// [/a/lib.es6.d.ts]
//// [/tsconfig.json]
{"files":["./file1.d.ts","./a/lib.d.ts","./a/file3.d.ts","/a/lib.es6.d.ts"]}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/file1.d.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /file1.d.ts :: Config file name: /tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /tsconfig.json : {
"rootNames": [
"/file1.d.ts",
"/a/lib.d.ts",
"/a/file3.d.ts",
"/a/lib.es6.d.ts"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/file3.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib.es6.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (4)
/file1.d.ts SVC-1-0 "class C {}"
/a/lib.d.ts Text-1 ""
/a/file3.d.ts Text-1 ""
/a/lib.es6.d.ts Text-1 ""
file1.d.ts
Part of 'files' list in tsconfig.json
a/lib.d.ts
Part of 'files' list in tsconfig.json
a/file3.d.ts
Part of 'files' list in tsconfig.json
a/lib.es6.d.ts
Part of 'files' list in tsconfig.json
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (4)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /file1.d.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/a/lib.d.ts: *new*
{}
/a/file3.d.ts: *new*
{}
/a/lib.es6.d.ts: *new*
{}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file1.d.ts",
"line": 1,
"offset": 7
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"newFileName": "/C.d.ts",
"files": [
"/a/file3.d.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,129 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.js]
class C {}
//// [/file2.js]
//// [/file3.mts]
//// [/file4.ts]
//// [/file5.js]
//// [/tsconfig.json]
{"files":["./file1.js","./file2.js","./file3.mts","./file4.ts","./file5.js"]}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/file1.js"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /file1.js :: Config file name: /tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /tsconfig.json : {
"rootNames": [
"/file1.js",
"/file2.js",
"/file3.mts",
"/file4.ts",
"/file5.js"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file2.js 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file3.mts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file4.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file5.js 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
/file1.js SVC-1-0 "class C {}"
/file2.js Text-1 ""
/file3.mts Text-1 ""
/file4.ts Text-1 ""
/file5.js Text-1 ""
file1.js
Part of 'files' list in tsconfig.json
file2.js
Part of 'files' list in tsconfig.json
file3.mts
Part of 'files' list in tsconfig.json
file4.ts
Part of 'files' list in tsconfig.json
file5.js
Part of 'files' list in tsconfig.json
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /file1.js ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/file2.js: *new*
{}
/file3.mts: *new*
{}
/file4.ts: *new*
{}
/file5.js: *new*
{}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file1.js",
"line": 1,
"offset": 7
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"newFileName": "/C.js",
"files": [
"/file2.js",
"/file5.js"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,151 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.ts]
interface ka {
name: string;
}
//// [/file2.tsx]
//// [/file3.mts]
//// [/file4.cts]
//// [/file5.js]
//// [/file6.d.ts]
//// [/file7.ts]
//// [/tsconfig.json]
{"files":["./file1.ts","./file2.tsx","./file3.mts","./file4.cts","./file5.js","./file6.d.ts","./file7.ts"]}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/file1.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /file1.ts :: Config file name: /tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /tsconfig.json : {
"rootNames": [
"/file1.ts",
"/file2.tsx",
"/file3.mts",
"/file4.cts",
"/file5.js",
"/file6.d.ts",
"/file7.ts"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file2.tsx 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file3.mts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file4.cts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file5.js 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file6.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file7.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (7)
/file1.ts SVC-1-0 "interface ka {\n name: string;\n }\n "
/file2.tsx Text-1 ""
/file3.mts Text-1 ""
/file4.cts Text-1 ""
/file5.js Text-1 ""
/file6.d.ts Text-1 ""
/file7.ts Text-1 ""
file1.ts
Part of 'files' list in tsconfig.json
file2.tsx
Part of 'files' list in tsconfig.json
file3.mts
Part of 'files' list in tsconfig.json
file4.cts
Part of 'files' list in tsconfig.json
file5.js
Part of 'files' list in tsconfig.json
file6.d.ts
Part of 'files' list in tsconfig.json
file7.ts
Part of 'files' list in tsconfig.json
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (7)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /file1.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/file2.tsx: *new*
{}
/file3.mts: *new*
{}
/file4.cts: *new*
{}
/file5.js: *new*
{}
/file6.d.ts: *new*
{}
/file7.ts: *new*
{}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file1.ts",
"line": 1,
"offset": 11
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"newFileName": "/ka.ts",
"files": [
"/file7.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,146 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/project/a/file1.ts]
interface ka {
name: string;
}
//// [/project/b/file2.ts]
//// [/project/d/e/file3.ts]
//// [/project/a/file4.ts]
import { value } from "../node_modules/@types/node/someFile.d.ts";
import { value1 } from "../node_modules/.cache/someFile.d.ts";
//// [/project/node_modules/@types/node/someFile.d.ts]
export const value = 0;
//// [/project/node_modules/.cache/someFile.d.ts]
export const value1 = 0;
//// [/project/tsconfig.json]
{}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/project/a/file1.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /project/a
Info seq [hh:mm:ss:mss] For info: /project/a/file1.ts :: Config file name: /project/tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /project/tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/tsconfig.json 2000 undefined Project: /project/tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /project/tsconfig.json : {
"rootNames": [
"/project/a/file1.ts",
"/project/a/file4.ts",
"/project/b/file2.ts",
"/project/d/e/file3.ts"
],
"options": {
"configFilePath": "/project/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /project 1 undefined Config: /project/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /project 1 undefined Config: /project/tsconfig.json WatchType: Wild card directory
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/a/file4.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/b/file2.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/d/e/file3.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /project/tsconfig.json
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /project/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /project/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /project/tsconfig.json WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /project/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (6)
/project/a/file1.ts SVC-1-0 "interface ka {\n name: string;\n }\n "
/project/node_modules/@types/node/someFile.d.ts Text-1 "export const value = 0;"
/project/node_modules/.cache/someFile.d.ts Text-1 "export const value1 = 0;"
/project/a/file4.ts Text-1 "import { value } from \"../node_modules/@types/node/someFile.d.ts\";\nimport { value1 } from \"../node_modules/.cache/someFile.d.ts\";"
/project/b/file2.ts Text-1 ""
/project/d/e/file3.ts Text-1 ""
a/file1.ts
Matched by default include pattern '**/*'
node_modules/@types/node/someFile.d.ts
Imported via "../node_modules/@types/node/someFile.d.ts" from file 'a/file4.ts'
node_modules/.cache/someFile.d.ts
Imported via "../node_modules/.cache/someFile.d.ts" from file 'a/file4.ts'
a/file4.ts
Matched by default include pattern '**/*'
b/file2.ts
Matched by default include pattern '**/*'
d/e/file3.ts
Matched by default include pattern '**/*'
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (6)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /project/a/file1.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /project/tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/project/tsconfig.json: *new*
{}
/project/a/file4.ts: *new*
{}
/project/b/file2.ts: *new*
{}
/project/d/e/file3.ts: *new*
{}
FsWatchesRecursive::
/project: *new*
{}
/project/node_modules: *new*
{}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/project/a/file1.ts",
"line": 1,
"offset": 11
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"newFileName": "/project/a/ka.ts",
"files": [
"/project/a/file4.ts",
"/project/b/file2.ts",
"/project/d/e/file3.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,112 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info 0 [00:00:19.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/blah/file1.ts]
class CC { }
//// [/blah/file2.ts]
//// [/blah-tests/file3.ts]
import { value1 } from "../blah/file1.ts";
//// [/blah-tests/file4.ts]
//// [/blah/tsconfig.json]
{ "files": ["./file1.ts", "./file2.ts"] }
//// [/blah-tests/tsconfig.json]
{ "files": ["./file3.ts", "./file4.ts"] }
Info 1 [00:00:20.000] request:
{
"command": "open",
"arguments": {
"file": "/blah/file1.ts"
},
"seq": 1,
"type": "request"
}
Info 2 [00:00:21.000] Search path: /blah
Info 3 [00:00:22.000] For info: /blah/file1.ts :: Config file name: /blah/tsconfig.json
Info 4 [00:00:23.000] Creating configuration project /blah/tsconfig.json
Info 5 [00:00:24.000] FileWatcher:: Added:: WatchInfo: /blah/tsconfig.json 2000 undefined Project: /blah/tsconfig.json WatchType: Config file
Info 6 [00:00:25.000] Config: /blah/tsconfig.json : {
"rootNames": [
"/blah/file1.ts",
"/blah/file2.ts"
],
"options": {
"configFilePath": "/blah/tsconfig.json"
}
}
Info 7 [00:00:26.000] FileWatcher:: Added:: WatchInfo: /blah/file2.ts 500 undefined WatchType: Closed Script info
Info 8 [00:00:27.000] Starting updateGraphWorker: Project: /blah/tsconfig.json
Info 9 [00:00:28.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /blah/tsconfig.json WatchType: Missing file
Info 10 [00:00:29.000] DirectoryWatcher:: Added:: WatchInfo: /blah/node_modules/@types 1 undefined Project: /blah/tsconfig.json WatchType: Type roots
Info 11 [00:00:30.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /blah/node_modules/@types 1 undefined Project: /blah/tsconfig.json WatchType: Type roots
Info 12 [00:00:31.000] Finishing updateGraphWorker: Project: /blah/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 13 [00:00:32.000] Project '/blah/tsconfig.json' (Configured)
Info 14 [00:00:33.000] Files (2)
/blah/file1.ts SVC-1-0 "class CC { }"
/blah/file2.ts Text-1 ""
file1.ts
Part of 'files' list in tsconfig.json
file2.ts
Part of 'files' list in tsconfig.json
Info 15 [00:00:34.000] -----------------------------------------------
Info 16 [00:00:35.000] Project '/blah/tsconfig.json' (Configured)
Info 16 [00:00:36.000] Files (2)
Info 16 [00:00:37.000] -----------------------------------------------
Info 16 [00:00:38.000] Open files:
Info 16 [00:00:39.000] FileName: /blah/file1.ts ProjectRootPath: undefined
Info 16 [00:00:40.000] Projects: /blah/tsconfig.json
Info 16 [00:00:41.000] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
/blah/node_modules/@types: *new*
{"pollingInterval":500}
FsWatches::
/blah/tsconfig.json: *new*
{}
/blah/file2.ts: *new*
{}
Before request
Info 17 [00:00:42.000] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/blah/file1.ts",
"line": 1,
"offset": 7
},
"seq": 2,
"type": "request"
}
Info 18 [00:00:43.000] response:
{
"response": {
"newFilename": "/blah/CC.ts",
"files": [
"/blah/file1.ts",
"/blah/file2.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,201 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info 0 [00:00:09.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.js]
import {} from "./file.js";
//// [/file2.js]
class C {}
//// [/jsconfig.json]
{"files":["./file1.js","./file.js"]}
Info 1 [00:00:10.000] request:
{
"command": "open",
"arguments": {
"file": "/file2.js"
},
"seq": 1,
"type": "request"
}
Info 2 [00:00:11.000] Search path: /
Info 3 [00:00:12.000] For info: /file2.js :: Config file name: /jsconfig.json
Info 4 [00:00:13.000] Creating configuration project /jsconfig.json
Info 5 [00:00:14.000] FileWatcher:: Added:: WatchInfo: /jsconfig.json 2000 undefined Project: /jsconfig.json WatchType: Config file
Info 6 [00:00:15.000] Config: /jsconfig.json : {
"rootNames": [
"/file1.js",
"/file.js"
],
"options": {
"allowJs": true,
"maxNodeModuleJsDepth": 2,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"noEmit": true,
"configFilePath": "/jsconfig.json"
}
}
Info 7 [00:00:16.000] FileWatcher:: Added:: WatchInfo: /file1.js 500 undefined WatchType: Closed Script info
Info 8 [00:00:17.000] Starting updateGraphWorker: Project: /jsconfig.json
Info 9 [00:00:18.000] DirectoryWatcher:: Added:: WatchInfo: /file.js 1 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
Info 10 [00:00:19.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /file.js 1 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
Info 11 [00:00:20.000] DirectoryWatcher:: Added:: WatchInfo: 0 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
Info 12 [00:00:21.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: 0 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
Info 13 [00:00:22.000] FileWatcher:: Added:: WatchInfo: /file.js 500 undefined Project: /jsconfig.json WatchType: Missing file
Info 14 [00:00:23.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /jsconfig.json WatchType: Missing file
Info 15 [00:00:24.000] Finishing updateGraphWorker: Project: /jsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 16 [00:00:25.000] Project '/jsconfig.json' (Configured)
Info 17 [00:00:26.000] Files (1)
/file1.js Text-1 "import {} from \"./file.js\";"
file1.js
Part of 'files' list in tsconfig.json
Info 18 [00:00:27.000] -----------------------------------------------
TI:: Creating typing installer
PolledWatches::
/file.js: *new*
{"pollingInterval":500}
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/jsconfig.json: *new*
{}
/file1.js: *new*
{}
/: *new*
{}
TI:: [00:00:28.000] Global cache location '/a/data/', safe file path '/safeList.json', types map path /typesMap.json
TI:: [00:00:29.000] Processing cache location '/a/data/'
TI:: [00:00:30.000] Trying to find '/a/data/package.json'...
TI:: [00:00:31.000] Finished processing cache location '/a/data/'
TI:: [00:00:32.000] Npm config file: /a/data/package.json
TI:: [00:00:33.000] Npm config file: '/a/data/package.json' is missing, creating new one...
Info 19 [00:00:36.000] DirectoryWatcher:: Triggered with a :: WatchInfo: 0 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
Info 20 [00:00:37.000] Elapsed:: *ms DirectoryWatcher:: Triggered with a :: WatchInfo: 0 undefined Project: /jsconfig.json WatchType: Failed Lookup Locations
TI:: [00:00:42.000] Updating types-registry npm package...
TI:: [00:00:43.000] npm install --ignore-scripts types-registry@latest
TI:: [00:00:50.000] TI:: Updated types-registry npm package
TI:: typing installer creation complete
//// [/a/data/package.json]
{ "private": true }
//// [/a/data/node_modules/types-registry/index.json]
{
"entries": {}
}
TI:: [00:00:51.000] Got install request {"projectName":"/jsconfig.json","fileNames":["/file1.js"],"compilerOptions":{"allowJs":true,"maxNodeModuleJsDepth":2,"allowSyntheticDefaultImports":true,"skipLibCheck":true,"noEmit":true,"configFilePath":"/jsconfig.json","allowNonTsExtensions":true},"typeAcquisition":{"enable":true,"include":[],"exclude":[]},"unresolvedImports":[],"projectRootPath":"/","cachePath":"/a/data/","kind":"discover"}
TI:: [00:00:52.000] Request specifies cache path '/a/data/', loading cached information...
TI:: [00:00:53.000] Processing cache location '/a/data/'
TI:: [00:00:54.000] Cache location was already processed...
TI:: [00:00:55.000] Failed to load safelist from types map file '/typesMap.json'
TI:: [00:00:56.000] Explicitly included types: []
TI:: [00:00:57.000] Inferred typings from unresolved imports: []
TI:: [00:00:58.000] Result: {"cachedTypingPaths":[],"newTypingNames":[],"filesToWatch":["/bower_components","/node_modules"]}
TI:: [00:00:59.000] Finished typings discovery: {"cachedTypingPaths":[],"newTypingNames":[],"filesToWatch":["/bower_components","/node_modules"]}
TI:: [00:01:00.000] DirectoryWatcher:: Added:: WatchInfo: /bower_components
TI:: [00:01:01.000] DirectoryWatcher:: Added:: WatchInfo: /bower_components 1 undefined Project: /jsconfig.json watcher already invoked: false
TI:: [00:01:02.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /bower_components 1 undefined Project: /jsconfig.json watcher already invoked: false
TI:: [00:01:03.000] DirectoryWatcher:: Added:: WatchInfo: /node_modules
TI:: [00:01:04.000] DirectoryWatcher:: Added:: WatchInfo: /node_modules 1 undefined Project: /jsconfig.json watcher already invoked: false
TI:: [00:01:05.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /node_modules 1 undefined Project: /jsconfig.json watcher already invoked: false
TI:: [00:01:06.000] Sending response:
{"projectName":"/jsconfig.json","typeAcquisition":{"enable":true,"include":[],"exclude":[]},"compilerOptions":{"allowJs":true,"maxNodeModuleJsDepth":2,"allowSyntheticDefaultImports":true,"skipLibCheck":true,"noEmit":true,"configFilePath":"/jsconfig.json","allowNonTsExtensions":true},"typings":[],"unresolvedImports":[],"kind":"action::set"}
TI:: [00:01:07.000] No new typings were requested as a result of typings discovery
Info 21 [00:01:08.000] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info 22 [00:01:09.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info 23 [00:01:10.000] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 24 [00:01:11.000] Project '/dev/null/inferredProject1*' (Inferred)
Info 25 [00:01:12.000] Files (1)
/file2.js SVC-1-0 "class C {}"
file2.js
Root file specified for compilation
Info 26 [00:01:13.000] -----------------------------------------------
TI:: [00:01:14.000] Got install request {"projectName":"/dev/null/inferredProject1*","fileNames":["/file2.js"],"compilerOptions":{"target":1,"jsx":1,"allowNonTsExtensions":true,"allowJs":true,"noEmitForJsFiles":true,"maxNodeModuleJsDepth":2},"typeAcquisition":{"enable":true,"include":[],"exclude":[]},"unresolvedImports":[],"projectRootPath":"/","cachePath":"/a/data/","kind":"discover"}
TI:: [00:01:15.000] Request specifies cache path '/a/data/', loading cached information...
TI:: [00:01:16.000] Processing cache location '/a/data/'
TI:: [00:01:17.000] Cache location was already processed...
TI:: [00:01:18.000] Explicitly included types: []
TI:: [00:01:19.000] Inferred typings from unresolved imports: []
TI:: [00:01:20.000] Result: {"cachedTypingPaths":[],"newTypingNames":[],"filesToWatch":["/bower_components","/node_modules"]}
TI:: [00:01:21.000] Finished typings discovery: {"cachedTypingPaths":[],"newTypingNames":[],"filesToWatch":["/bower_components","/node_modules"]}
TI:: [00:01:22.000] DirectoryWatcher:: Added:: WatchInfo: /bower_components
TI:: [00:01:23.000] DirectoryWatcher:: Added:: WatchInfo: /bower_components 1 undefined Project: /dev/null/inferredProject1* watcher already invoked: false
TI:: [00:01:24.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /bower_components 1 undefined Project: /dev/null/inferredProject1* watcher already invoked: false
TI:: [00:01:25.000] DirectoryWatcher:: Added:: WatchInfo: /node_modules
TI:: [00:01:26.000] DirectoryWatcher:: Added:: WatchInfo: /node_modules 1 undefined Project: /dev/null/inferredProject1* watcher already invoked: false
TI:: [00:01:27.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /node_modules 1 undefined Project: /dev/null/inferredProject1* watcher already invoked: false
TI:: [00:01:28.000] Sending response:
{"projectName":"/dev/null/inferredProject1*","typeAcquisition":{"enable":true,"include":[],"exclude":[]},"compilerOptions":{"target":1,"jsx":1,"allowNonTsExtensions":true,"allowJs":true,"noEmitForJsFiles":true,"maxNodeModuleJsDepth":2},"typings":[],"unresolvedImports":[],"kind":"action::set"}
TI:: [00:01:29.000] No new typings were requested as a result of typings discovery
Info 27 [00:01:30.000] Project '/jsconfig.json' (Configured)
Info 27 [00:01:31.000] Files (1)
Info 27 [00:01:32.000] -----------------------------------------------
Info 27 [00:01:33.000] Project '/dev/null/inferredProject1*' (Inferred)
Info 27 [00:01:34.000] Files (1)
Info 27 [00:01:35.000] -----------------------------------------------
Info 27 [00:01:36.000] Open files:
Info 27 [00:01:37.000] FileName: /file2.js ProjectRootPath: undefined
Info 27 [00:01:38.000] Projects: /dev/null/inferredProject1*
Info 27 [00:01:39.000] response:
{
"responseRequired": false
}
After request
PolledWatches::
/file.js:
{"pollingInterval":500}
/a/lib/lib.d.ts:
{"pollingInterval":500}
/bower_components: *new*
{"pollingInterval":500}
/node_modules: *new*
{"pollingInterval":500}
FsWatches::
/jsconfig.json:
{}
/file1.js:
{}
/:
{}
Before request
Info 28 [00:01:40.000] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file2.js",
"line": 1,
"offset": 7
},
"seq": 2,
"type": "request"
}
Info 29 [00:01:41.000] response:
{
"response": {
"newFilename": "/C.js",
"files": [
"/file2.js"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,123 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info 0 [00:00:09.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.ts]
import {} from "./file.ts";
//// [/file2.ts]
interface ka {
name: string;
}
//// [/tsconfig.json]
{"files":["./file1.ts","./file.ts"]}
Info 1 [00:00:10.000] request:
{
"command": "open",
"arguments": {
"file": "/file2.ts"
},
"seq": 1,
"type": "request"
}
Info 2 [00:00:11.000] Search path: /
Info 3 [00:00:12.000] For info: /file2.ts :: Config file name: /tsconfig.json
Info 4 [00:00:13.000] Creating configuration project /tsconfig.json
Info 5 [00:00:14.000] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info 6 [00:00:15.000] Config: /tsconfig.json : {
"rootNames": [
"/file1.ts",
"/file.ts"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info 7 [00:00:16.000] FileWatcher:: Added:: WatchInfo: /file1.ts 500 undefined WatchType: Closed Script info
Info 8 [00:00:17.000] Starting updateGraphWorker: Project: /tsconfig.json
Info 9 [00:00:18.000] DirectoryWatcher:: Added:: WatchInfo: /file.ts 1 undefined Project: /tsconfig.json WatchType: Failed Lookup Locations
Info 10 [00:00:19.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /file.ts 1 undefined Project: /tsconfig.json WatchType: Failed Lookup Locations
Info 11 [00:00:20.000] DirectoryWatcher:: Added:: WatchInfo: 0 undefined Project: /tsconfig.json WatchType: Failed Lookup Locations
Info 12 [00:00:21.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: 0 undefined Project: /tsconfig.json WatchType: Failed Lookup Locations
Info 13 [00:00:22.000] FileWatcher:: Added:: WatchInfo: /file.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info 14 [00:00:23.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info 15 [00:00:24.000] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 16 [00:00:25.000] Project '/tsconfig.json' (Configured)
Info 17 [00:00:26.000] Files (1)
/file1.ts Text-1 "import {} from \"./file.ts\";"
file1.ts
Part of 'files' list in tsconfig.json
Info 18 [00:00:27.000] -----------------------------------------------
Info 19 [00:00:28.000] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info 20 [00:00:29.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info 21 [00:00:30.000] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 22 [00:00:31.000] Project '/dev/null/inferredProject1*' (Inferred)
Info 23 [00:00:32.000] Files (1)
/file2.ts SVC-1-0 "interface ka {\n name: string;\n }\n "
file2.ts
Root file specified for compilation
Info 24 [00:00:33.000] -----------------------------------------------
Info 25 [00:00:34.000] Project '/tsconfig.json' (Configured)
Info 25 [00:00:35.000] Files (1)
Info 25 [00:00:36.000] -----------------------------------------------
Info 25 [00:00:37.000] Project '/dev/null/inferredProject1*' (Inferred)
Info 25 [00:00:38.000] Files (1)
Info 25 [00:00:39.000] -----------------------------------------------
Info 25 [00:00:40.000] Open files:
Info 25 [00:00:41.000] FileName: /file2.ts ProjectRootPath: undefined
Info 25 [00:00:42.000] Projects: /dev/null/inferredProject1*
Info 25 [00:00:43.000] response:
{
"responseRequired": false
}
After request
PolledWatches::
/file.ts: *new*
{"pollingInterval":500}
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/file1.ts: *new*
{}
/: *new*
{}
Before request
Info 26 [00:00:44.000] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file2.ts",
"line": 1,
"offset": 11
},
"seq": 2,
"type": "request"
}
Info 27 [00:00:45.000] response:
{
"response": {
"newFilename": "/ka.ts",
"files": [
"/file2.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,145 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info 0 [00:00:17.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.ts]
interface ka {
name: string;
}
//// [/file2.tsx]
//// [/file3.mts]
//// [/file4.cts]
//// [/file5.js]
//// [/file6.d.ts]
//// [/tsconfig.json]
{"files":["./file1.ts","./file2.tsx","./file3.mts","./file4.cts","./file5.js","./file6.d.ts"]}
Info 1 [00:00:18.000] request:
{
"command": "open",
"arguments": {
"file": "/file1.ts"
},
"seq": 1,
"type": "request"
}
Info 2 [00:00:19.000] Search path: /
Info 3 [00:00:20.000] For info: /file1.ts :: Config file name: /tsconfig.json
Info 4 [00:00:21.000] Creating configuration project /tsconfig.json
Info 5 [00:00:22.000] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info 6 [00:00:23.000] Config: /tsconfig.json : {
"rootNames": [
"/file1.ts",
"/file2.tsx",
"/file3.mts",
"/file4.cts",
"/file5.js",
"/file6.d.ts"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info 7 [00:00:24.000] FileWatcher:: Added:: WatchInfo: /file2.tsx 500 undefined WatchType: Closed Script info
Info 8 [00:00:25.000] FileWatcher:: Added:: WatchInfo: /file3.mts 500 undefined WatchType: Closed Script info
Info 9 [00:00:26.000] FileWatcher:: Added:: WatchInfo: /file4.cts 500 undefined WatchType: Closed Script info
Info 10 [00:00:27.000] FileWatcher:: Added:: WatchInfo: /file5.js 500 undefined WatchType: Closed Script info
Info 11 [00:00:28.000] FileWatcher:: Added:: WatchInfo: /file6.d.ts 500 undefined WatchType: Closed Script info
Info 12 [00:00:29.000] Starting updateGraphWorker: Project: /tsconfig.json
Info 13 [00:00:30.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info 14 [00:00:31.000] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 15 [00:00:32.000] Project '/tsconfig.json' (Configured)
Info 16 [00:00:33.000] Files (6)
/file1.ts SVC-1-0 "interface ka {\n name: string;\n }\n "
/file2.tsx Text-1 ""
/file3.mts Text-1 ""
/file4.cts Text-1 ""
/file5.js Text-1 ""
/file6.d.ts Text-1 ""
file1.ts
Part of 'files' list in tsconfig.json
file2.tsx
Part of 'files' list in tsconfig.json
file3.mts
Part of 'files' list in tsconfig.json
file4.cts
Part of 'files' list in tsconfig.json
file5.js
Part of 'files' list in tsconfig.json
file6.d.ts
Part of 'files' list in tsconfig.json
Info 17 [00:00:34.000] -----------------------------------------------
Info 18 [00:00:35.000] Project '/tsconfig.json' (Configured)
Info 18 [00:00:36.000] Files (6)
Info 18 [00:00:37.000] -----------------------------------------------
Info 18 [00:00:38.000] Open files:
Info 18 [00:00:39.000] FileName: /file1.ts ProjectRootPath: undefined
Info 18 [00:00:40.000] Projects: /tsconfig.json
Info 18 [00:00:41.000] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/file2.tsx: *new*
{}
/file3.mts: *new*
{}
/file4.cts: *new*
{}
/file5.js: *new*
{}
/file6.d.ts: *new*
{}
Before request
Info 19 [00:00:42.000] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file1.ts",
"line": 1,
"offset": 11
},
"seq": 2,
"type": "request"
}
Info 20 [00:00:43.000] response:
{
"response": {
"newFilename": "/ka.ts",
"files": [
"/file1.ts",
"/file2.tsx",
"/file3.mts",
"/file4.cts",
"/file6.d.ts"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,130 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info 0 [00:00:15.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/file1.js]
class C {}
//// [/file2.js]
//// [/file3.mts]
//// [/file4.ts]
//// [/file5.js]
//// [/tsconfig.json]
{"files":["./file1.js","./file2.js","./file3.mts","./file4.ts","./file5.js"]}
Info 1 [00:00:16.000] request:
{
"command": "open",
"arguments": {
"file": "/file1.js"
},
"seq": 1,
"type": "request"
}
Info 2 [00:00:17.000] Search path: /
Info 3 [00:00:18.000] For info: /file1.js :: Config file name: /tsconfig.json
Info 4 [00:00:19.000] Creating configuration project /tsconfig.json
Info 5 [00:00:20.000] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info 6 [00:00:21.000] Config: /tsconfig.json : {
"rootNames": [
"/file1.js",
"/file2.js",
"/file3.mts",
"/file4.ts",
"/file5.js"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info 7 [00:00:22.000] FileWatcher:: Added:: WatchInfo: /file2.js 500 undefined WatchType: Closed Script info
Info 8 [00:00:23.000] FileWatcher:: Added:: WatchInfo: /file3.mts 500 undefined WatchType: Closed Script info
Info 9 [00:00:24.000] FileWatcher:: Added:: WatchInfo: /file4.ts 500 undefined WatchType: Closed Script info
Info 10 [00:00:25.000] FileWatcher:: Added:: WatchInfo: /file5.js 500 undefined WatchType: Closed Script info
Info 11 [00:00:26.000] Starting updateGraphWorker: Project: /tsconfig.json
Info 12 [00:00:27.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /tsconfig.json WatchType: Missing file
Info 13 [00:00:28.000] Finishing updateGraphWorker: Project: /tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info 14 [00:00:29.000] Project '/tsconfig.json' (Configured)
Info 15 [00:00:30.000] Files (5)
/file1.js SVC-1-0 "class C {}"
/file2.js Text-1 ""
/file3.mts Text-1 ""
/file4.ts Text-1 ""
/file5.js Text-1 ""
file1.js
Part of 'files' list in tsconfig.json
file2.js
Part of 'files' list in tsconfig.json
file3.mts
Part of 'files' list in tsconfig.json
file4.ts
Part of 'files' list in tsconfig.json
file5.js
Part of 'files' list in tsconfig.json
Info 16 [00:00:31.000] -----------------------------------------------
Info 17 [00:00:32.000] Project '/tsconfig.json' (Configured)
Info 17 [00:00:33.000] Files (5)
Info 17 [00:00:34.000] -----------------------------------------------
Info 17 [00:00:35.000] Open files:
Info 17 [00:00:36.000] FileName: /file1.js ProjectRootPath: undefined
Info 17 [00:00:37.000] Projects: /tsconfig.json
Info 17 [00:00:38.000] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/tsconfig.json: *new*
{}
/file2.js: *new*
{}
/file3.mts: *new*
{}
/file4.ts: *new*
{}
/file5.js: *new*
{}
Before request
Info 18 [00:00:39.000] request:
{
"command": "getMoveToRefactoringFileSuggestions",
"arguments": {
"file": "/file1.js",
"line": 1,
"offset": 7
},
"seq": 2,
"type": "request"
}
Info 19 [00:00:40.000] response:
{
"response": {
"newFilename": "/C.js",
"files": [
"/file1.js",
"/file2.js",
"/file5.js"
]
},
"responseRequired": true
}
After request
@@ -0,0 +1,136 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request
//// [/Foo/a.ts]
const x = 0;
//// [/Foo/b.ts]
import {} from "./bar";
const a = 1;
//// [/Foo/tsconfig.json]
{ "files": ["./a.ts", "./b.ts"] }
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/Foo/a.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /Foo
Info seq [hh:mm:ss:mss] For info: /Foo/a.ts :: Config file name: /Foo/tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /Foo/tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /Foo/tsconfig.json 2000 undefined Project: /Foo/tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /Foo/tsconfig.json : {
"rootNames": [
"/Foo/a.ts",
"/Foo/b.ts"
],
"options": {
"configFilePath": "/Foo/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /Foo/b.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /Foo/tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /Foo/tsconfig.json WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /Foo/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/Foo/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (2)
/Foo/a.ts SVC-1-0 "const x = 0;"
/Foo/b.ts Text-1 "import {} from \"./bar\";\nconst a = 1;"
a.ts
Part of 'files' list in tsconfig.json
b.ts
Part of 'files' list in tsconfig.json
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/Foo/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (2)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /Foo/a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /Foo/tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
FsWatches::
/foo/tsconfig.json: *new*
{}
/foo/b.ts: *new*
{}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getEditsForRefactor",
"arguments": {
"file": "/Foo/a.ts",
"startLine": 1,
"startOffset": 1,
"endLine": 2,
"endOffset": 12,
"refactor": "Move to file",
"action": "Move to file",
"interactiveRefactorArguments": {
"targetFile": "/Foo/b.ts"
}
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"edits": [
{
"fileName": "/Foo/a.ts",
"textChanges": [
{
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 13
},
"newText": ""
}
]
},
{
"fileName": "/Foo/b.ts",
"textChanges": [
{
"start": {
"line": 2,
"offset": 13
},
"end": {
"line": 2,
"offset": 13
},
"newText": "\nconst x = 0;\n"
}
]
}
]
},
"responseRequired": true
}
After request
+8 -1
View File
@@ -198,6 +198,9 @@ declare namespace FourSlashInterface {
readonly semicolons?: ts.SemicolonPreference;
readonly indentSwitchCase?: boolean;
}
interface InteractiveRefactorArguments {
targetFile: string;
}
interface Range {
fileName: string;
pos: number;
@@ -431,7 +434,11 @@ declare namespace FourSlashInterface {
readonly preferences?: UserPreferences;
}): void;
noMoveToNewFile(): void;
moveToFile(options: {
readonly newFileContents: { readonly [fileName: string]: string };
readonly interactiveRefactorArguments: InteractiveRefactorArguments;
readonly preferences?: UserPreferences;
}): void;
generateTypes(...options: GenerateTypesOptions[]): void;
organizeImports(newContent: string, mode?: ts.OrganizeImportsMode, preferences?: UserPreferences): void;
@@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
//@Filename: /bar.ts
////import './blah';
////
////const a = 2;
// @Filename: /a.ts
////import { b } from './other';
////
////[|const y = b + 10;|]
////y;
// @Filename: /other.ts
////export const b = 1;
verify.moveToFile({
newFileContents: {
"/a.ts":
`import { y } from './bar';
y;`,
"/bar.ts":
`import './blah';
import { b } from './other';
const a = 2;
export const y = b + 10;
`,
},
interactiveRefactorArguments: {targetFile: "/bar.ts"},
});
@@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// @Filename: /bar.ts
//////
// @Filename: /a.ts
////import { b } from './other';
////const p = 0;
////[|const y: Date = p + b;|]
// @Filename: /other.ts
////export const b = 1;
verify.moveToFile({
newFileContents: {
"/a.ts":
`export const p = 0;
`,
"/bar.ts":
`import { b } from './other';
import { p } from './a';
const y: Date = p + b;
`,
},
interactiveRefactorArguments: {targetFile: "/bar.ts"},
});
@@ -0,0 +1,36 @@
/// <reference path='fourslash.ts' />
// @module: nodenext
// @allowImportingTsExtensions: true
// @noEmit: true
//@Filename: /src/dir2/bar.mts
////import './blah.ts';
////const a = 2;
// @Filename: /src/dir1/a.cts
////import { b } from './other.cts';
////const p = 0;
////[|const y = p + b;|]
////y;
// @Filename: /src/dir1/other.cts
////export const b = 1;
verify.moveToFile({
newFileContents: {
"/src/dir1/a.cts":
`import { y } from '../dir2/bar.mts';
export const p = 0;
y;`,
"/src/dir2/bar.mts":
`import { p } from '../dir1/a.cts';
import { b } from '../dir1/other.cts';
import './blah.ts';
const a = 2;
export const y = p + b;
`,
},
interactiveRefactorArguments: { targetFile: "/src/dir2/bar.mts" },
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts' />
//@moduleResolution: bundler
//@module: esnext
// @Filename: /src/dir1/a.ts
////import { b } from './other';
////[|const y = b + 10;|]
////y;
// @Filename: /src/dir1/other.ts
////export const b = 1;
//@Filename: /src/dir2/bar.ts
////import './blah';
////const a = 2;
verify.moveToFile({
newFileContents: {
"/src/dir1/a.ts":
`import { y } from '../dir2/bar';
y;`,
"/src/dir2/bar.ts":
`import { b } from '../dir1/other';
import './blah';
const a = 2;
export const y = b + 10;
`,
},
interactiveRefactorArguments: { targetFile: "/src/dir2/bar.ts" },
});
@@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
//@moduleResolution: bundler
//@module: esnext
// @Filename: /src/dir1/a.ts
////import { b } from './other';
////const a = 10;
////[|const y = b + a;|]
////y;
// @Filename: /src/dir1/other.ts
////export const b = 1;
//@Filename: /src/dir2/bar.ts
////
verify.moveToFile({
newFileContents: {
"/src/dir1/a.ts":
`import { y } from '../dir2/bar';
export const a = 10;
y;`,
"/src/dir2/bar.ts":
`import { b } from '../dir1/other';
import { a } from '../dir1/a';
export const y = b + a;
`,
},
interactiveRefactorArguments: { targetFile: "/src/dir2/bar.ts" }
});
@@ -0,0 +1,37 @@
/// <reference path='fourslash.ts' />
// @module: nodenext
// @Filename: /bar.cts
////const a = 2;
// @Filename: /node_modules/esm-only/package.json
//// {
//// "name": "esm-only",
//// "version": "1.0.0",
//// "type": "module",
//// "exports": {
//// ".": {
//// "import": "./index.js"
//// }
//// }
//// }
// @Filename: /node_modules/esm-only/index.d.ts
//// export declare const esm: any;
// @Filename: /main.mts
//// import { esm } from "esm-only";
//// [|esm.ohno;|]
verify.moveToFile({
newFileContents: {
"/main.mts":``,
"/bar.cts":
`const a = 2;
esm.ohno;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.cts" }
});
@@ -0,0 +1,36 @@
/// <reference path='fourslash.ts' />
//@Filename: /bar.ts
////import { } from './somefile';
////const a = 12;
// @Filename: /a.ts
////[|const q = 0;
////const t = 2;
////function f() {}
////class C {}
////enum E {}
////namespace N { export const h = 0; }
////type T = number;
////interface I {}|]
verify.moveToFile({
newFileContents: {
"/a.ts":
``,
"/bar.ts":
`import { } from './somefile';
const a = 12;
const q = 0;
const t = 2;
function f() { }
class C { }
enum E { }
namespace N { export const h = 0; }
type T = number;
interface I { }
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});
@@ -0,0 +1,45 @@
/// <reference path='fourslash.ts' />
// @Filename: /bar.ts
////const q = 0;
// @Filename: /a.ts
////// header comment
////
////import { } from './other';
////import type { } from './other';
////
////export const p = 0;
////export const b = 1;
////[|const y = p + b;|]
// @Filename: /other.ts
////export const t = 2;
verify.moveToFile({
newFileContents: {
"/a.ts":
`// header comment
import { } from './other';
import type { } from './other';
export const p = 0;
export const b = 1;
`,
"/bar.ts":
`import { p, b } from './a';
const q = 0;
const y = p + b;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" },
preferences: {
quotePreference: "single",
}
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.ts
////[|interface ka {
//// name: string;
////}|]
verify.moveToFile({
newFileContents: {
"/a.ts":
``,
"/bar.ts":
`interface ka {
name: string;
}
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});
@@ -0,0 +1,27 @@
/// <reference path='fourslash.ts' />
//@Filename: /bar.ts
////const q = "test";
// @Filename: /a.ts
////const z = 1;
////[|const y = z + 2;|]
////const t = y + 1;
verify.moveToFile({
newFileContents: {
"/a.ts":
`import { y } from "./bar";
export const z = 1;
const t = y + 1;`,
"/bar.ts":
`import { z } from "./a";
const q = "test";
export const y = z + 2;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts' />
//@Filename: /bar.ts
////import './someFile';
////
////const q = 20;
// @Filename: /a.ts
////import { b } from './other';
////const p = 0;
////[|const y: Date = p + b;|]
// @Filename: /other.ts
////export const b = 2;
verify.moveToFile({
newFileContents: {
"/a.ts":
`export const p = 0;
`,
"/bar.ts":
`import { p } from './a';
import { b } from './other';
import './someFile';
const q = 20;
const y: Date = p + b;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});
@@ -0,0 +1,44 @@
/// <reference path='fourslash.ts' />
// @module: commonjs
// @allowJs: true
//@Filename: /bar.js
////const x = 0;
//@Filename: /a.js
////const { a, b } = require("./other");
////const p = 0;
////[|const y = p;
////const z = 0;
////exports.z = 0;|]
////a; y; z;
//@Filename: /other.js
////const a = 1;
////exports.a = a;
//@Filename: /user.ts
////const { x, y } = require("./a");
verify.moveToFile({
newFileContents: {
"/a.js":
`const { y, z } = require("./bar");
const { a, b } = require("./other");
const p = 0;
exports.p = p;
a; y; z;`,
"/bar.js":
`const { p } = require("./a");
const x = 0;
const y = p;
exports.y = y;
const z = 0;
exports.z = 0;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.js" }
});
@@ -0,0 +1,41 @@
/// <reference path='fourslash.ts' />
// @verbatimModuleSyntax: true
// @module: commonjs
// @allowJs: true
//@Filename: /a.js
////const { a, b } = require("./other");
////const p = 0;
////[|const y = p;
////const z = 0;
////exports.z = 0;|]
////a; y; z;
//@Filename: /other.js
////const a = 1;
////exports.a = a;
//@Filename: /user.ts
////const { x, y } = require("./a");
verify.moveToFile({
newFileContents: {
"/a.js":
`const { y, z } = require("./bar");
const { a, b } = require("./other");
const p = 0;
exports.p = p;
a; y; z;`,
"/bar.js":
`const { p } = require("./a");
const y = p;
exports.y = y;
const z = 0;
exports.z = 0;
`,
},
interactiveRefactorArguments: { targetFile: "/bar.js" }
});
@@ -0,0 +1,35 @@
/// <reference path='fourslash.ts' />
// @verbatimModuleSyntax: true
//@module: esnext
//moduleResolution: bundler
// @Filename: /bar.ts
////import {} from "./somefile";
// @Filename: /a.ts
////import { type A } from "./other";
////import type { B } from "./other";
////[|function f(a: B) {}|]
// @Filename: /other.ts
////export type B = {};
////export interface A {
//// x: number;
////}
verify.moveToFile({
newFileContents: {
"/a.ts":
`import { type A } from "./other";
`,
"/bar.ts":
`import type { B } from "./other";
import {} from "./somefile";
function f(a: B) { }
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />
// @Filename: /bar.ts
////const a = 1;
// @Filename: /a.ts
////import { a } from 'doesnt-exist';
////[|a();|]
verify.moveToFile({
newFileContents: {
"/a.ts":
``,
"/bar.ts":
`import { a } from "doesnt-exist";
const a = 1;
a();
`,
},
interactiveRefactorArguments: { targetFile: "/bar.ts" }
});