Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing copy/paste #57262

Merged
merged 53 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
36e67a4
Testing basic changes
navya9singh Jan 18, 2024
5d931f8
Basix structure working
navya9singh Jan 26, 2024
17f9897
Allows different text in each cursor location
navya9singh Feb 1, 2024
64da77e
working changes
navya9singh Feb 1, 2024
89ef072
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 1, 2024
4fe48f6
removing extra code
navya9singh Feb 1, 2024
17ff23a
Fixing protocol changes and tests
navya9singh Feb 9, 2024
1b89d2e
Removing deleted files
navya9singh Feb 9, 2024
e6515ea
Removing deleted files
navya9singh Feb 9, 2024
bfe79a9
fixing eslint errors
navya9singh Feb 9, 2024
ab542b5
fiixng formatting and baseline changes
navya9singh Feb 12, 2024
9ef3d85
Minor fixes
navya9singh Feb 12, 2024
281e2f1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 12, 2024
7f41f30
Fixing baselines
navya9singh Feb 12, 2024
d3816d3
changes based on reviews 1
navya9singh Feb 20, 2024
3914539
changes based on review 2
navya9singh Feb 20, 2024
83a4328
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 20, 2024
c6b6f23
fixing formatting
navya9singh Feb 20, 2024
c8c3e29
baseline changes
navya9singh Feb 20, 2024
5192ff2
accepting baselines
navya9singh Feb 20, 2024
215e778
Removing updateGraph for reverting the file
navya9singh Feb 22, 2024
ccd35b1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 4, 2024
1fe2754
changing protocol name
navya9singh Mar 4, 2024
c67137b
Removing extra baselines
navya9singh Mar 4, 2024
e83eae1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 4, 2024
392dd75
adressing pr comments
navya9singh Mar 5, 2024
d7cbcd0
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 8, 2024
778e2b4
protocol changes and fixed tests
navya9singh Mar 19, 2024
f302b65
missed change for fourslash tests
navya9singh Mar 19, 2024
7027ea3
changes to avoid duplicated imports for symbols that are added to exi…
navya9singh Mar 19, 2024
a9f7127
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 19, 2024
8e486d6
fixing tests
navya9singh Mar 20, 2024
835acda
small fixes
navya9singh Mar 20, 2024
3b79cb1
new fixes for non exported symbols in import adder
navya9singh Mar 28, 2024
d7f7b15
Merging with main
navya9singh Mar 28, 2024
82fc3bf
adressing pr comments
navya9singh Mar 28, 2024
5e7e39b
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 25, 2024
0ef7161
fixing merge conflicts
navya9singh Apr 25, 2024
4980cc5
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 25, 2024
7c6a146
resolving merge conflicts
navya9singh Apr 26, 2024
eaa1aaf
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 26, 2024
4ec4ccc
fixing tests
navya9singh Apr 29, 2024
1a36951
small fixes
navya9singh Apr 29, 2024
4d37a06
Adressing pr comments
navya9singh Apr 30, 2024
66ca5be
adressing pr comments
navya9singh Apr 30, 2024
e8189c3
adding `getPasteEdits` to session.ts and services.ts
navya9singh Apr 30, 2024
213587e
adding changes to handle pasted text
navya9singh May 1, 2024
68ce405
Removing baselines for deleted tests
navya9singh May 1, 2024
5e1ff78
adressing pr comments
navya9singh May 2, 2024
a3fbd69
adding changes tests and protocol
navya9singh May 2, 2024
8c23899
fixing test formatting
navya9singh May 7, 2024
1958d1a
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh May 7, 2024
3fe8f48
fixing error in pasteEdit.ts
navya9singh May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/harness/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import {
notImplemented,
OrganizeImportsArgs,
OutliningSpan,
PasteEdits,
PasteEditsArgs,
PatternMatchKind,
Program,
QuickInfo,
Expand Down Expand Up @@ -1006,6 +1008,25 @@ export class SessionClient implements LanguageService {
return getSupportedCodeFixes();
}

getPasteEdits(
sheetalkamat marked this conversation as resolved.
Show resolved Hide resolved
{ targetFile, pastedText, pasteLocations, copiedFrom }: PasteEditsArgs,
_formatOptions: FormatCodeSettings,
): PasteEdits {
const args: protocol.GetPasteEditsRequestArgs = {
file: targetFile,
pastedText,
pasteLocations: pasteLocations.map(range => ({ start: this.positionToOneBasedLineOffset(targetFile, range.pos), end: this.positionToOneBasedLineOffset(targetFile, range.end) })),
copiedFrom: copiedFrom ? { file: copiedFrom.file, range: copiedFrom.range.map(range => ({ start: this.positionToOneBasedLineOffset(copiedFrom.file, range.pos), end: this.positionToOneBasedLineOffset(copiedFrom.file, range.end) })) } : undefined,
};
const request = this.processRequest<protocol.GetPasteEditsRequest>(protocol.CommandTypes.GetPasteEdits, args);
const response = this.processResponse<protocol.GetPasteEditsResponse>(request);
if (!response.body) {
return { edits: [] };
}
const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits);
return { edits, fixId: response.body.fixId };
}

getProgram(): Program {
throw new Error("Program objects are not serializable through the server protocol.");
}
Expand Down
5 changes: 5 additions & 0 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3560,6 +3560,11 @@ export class TestState {
assert.deepEqual(actualModuleSpecifiers, moduleSpecifiers);
}

public verifyPasteEdits(options: FourSlashInterface.PasteEditsOptions): void {
const editInfo = this.languageService.getPasteEdits({ targetFile: this.activeFile.fileName, pastedText: options.args.pastedText, pasteLocations: options.args.pasteLocations, copiedFrom: options.args.copiedFrom, preferences: options.args.preferences }, this.formatCodeSettings);
(options.fixId === editInfo.fixId) ? this.verifyNewContent({ newFileContent: options.newFileContents }, editInfo.edits) : this.raiseError(`Expected to find a fix ${editInfo.fixId}, but none exists`);
}

public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined, options?: ts.DocCommentTemplateOptions) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition, options || { generateReturnInDocTemplate: true }, this.formatCodeSettings)!;
Expand Down
10 changes: 10 additions & 0 deletions src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ export class Verify extends VerifyNegatable {
public organizeImports(newContent: string, mode?: ts.OrganizeImportsMode, preferences?: ts.UserPreferences): void {
this.state.verifyOrganizeImports(newContent, mode, preferences);
}

public pasteEdits(options: PasteEditsOptions): void {
this.state.verifyPasteEdits(options);
}
}

export class Edit {
Expand Down Expand Up @@ -1923,6 +1927,12 @@ export interface MoveToFileOptions {
readonly preferences?: ts.UserPreferences;
}

export interface PasteEditsOptions {
readonly newFileContents: { readonly [fileName: string]: string; };
args: ts.PasteEditsArgs;
readonly fixId: string;
}

export type RenameLocationsOptions = readonly RenameLocationOptions[] | {
readonly findInStrings?: boolean;
readonly findInComments?: boolean;
Expand Down
12 changes: 12 additions & 0 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,18 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
return this.noDtsResolutionProject;
}

/** @internal */
runWithTemporaryFileUpdate(rootFile: string, updatedText: string, cb: (updatedProgram: Program, originalProgram: Program | undefined, updatedFile: SourceFile) => void) {
const originalProgram = this.program;
const originalText = this.program?.getSourceFile(rootFile)?.getText();
Debug.assert(this.program && this.program.getSourceFile(rootFile) && originalText);

this.getScriptInfo(rootFile)?.editContent(0, this.program.getSourceFile(rootFile)!.getText().length, updatedText);
this.updateGraph();
sheetalkamat marked this conversation as resolved.
Show resolved Hide resolved
cb(this.program, originalProgram, (this.program?.getSourceFile(rootFile))!);
this.getScriptInfo(rootFile)?.editContent(0, this.program.getSourceFile(rootFile)!.getText().length, originalText);
}

/** @internal */
private getCompilerOptionsForNoDtsResolutionProject() {
return {
Expand Down
25 changes: 25 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export const enum CommandTypes {
GetApplicableRefactors = "getApplicableRefactors",
GetEditsForRefactor = "getEditsForRefactor",
GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions",
GetPasteEdits = "getPasteEdits",
/** @internal */
GetEditsForRefactorFull = "getEditsForRefactor-full",

Expand Down Expand Up @@ -625,6 +626,30 @@ export interface GetMoveToRefactoringFileSuggestions extends Response {
};
}

/**
* Request refactorings at a given position post pasting text from some other location.
*/

export interface GetPasteEditsRequest extends Request {
command: CommandTypes.GetPasteEdits;
arguments: GetPasteEditsRequestArgs;
}

export interface GetPasteEditsRequestArgs extends FileRequestArgs {
pastedText: string[];
pasteLocations: TextSpan[];
copiedFrom?: { file: string; range: TextSpan[]; };
andrewbranch marked this conversation as resolved.
Show resolved Hide resolved
navya9singh marked this conversation as resolved.
Show resolved Hide resolved
}

export interface GetPasteEditsResponse extends Response {
body: PasteEditsAction;
}

export interface PasteEditsAction {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the future we will likely want a name and/or id too so users can tell what type of edit is being made. We can leave that out until there are multiple providers, but means that we'll have to add a flag so older clients don't unexpectedly start using the wrong paste edit

navya9singh marked this conversation as resolved.
Show resolved Hide resolved
edits: FileCodeEdits[];
fixId?: {};
}

export interface GetEditsForRefactorRequest extends Request {
command: CommandTypes.GetEditsForRefactor;
arguments: GetEditsForRefactorRequestArgs;
Expand Down
27 changes: 27 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import {
OperationCanceledException,
OrganizeImportsMode,
OutliningSpan,
PasteEdits,
Path,
perfLogger,
PerformanceEvent,
Expand Down Expand Up @@ -910,6 +911,7 @@ const invalidPartialSemanticModeCommands: readonly protocol.CommandTypes[] = [
protocol.CommandTypes.PrepareCallHierarchy,
protocol.CommandTypes.ProvideCallHierarchyIncomingCalls,
protocol.CommandTypes.ProvideCallHierarchyOutgoingCalls,
protocol.CommandTypes.GetPasteEdits,
];

const invalidSyntacticModeCommands: readonly protocol.CommandTypes[] = [
Expand Down Expand Up @@ -2796,6 +2798,24 @@ export class Session<TMessage = string> implements EventSender {
return project.getLanguageService().getMoveToRefactoringFileSuggestions(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file));
}

private getPasteEdits(args: protocol.GetPasteEditsRequestArgs): protocol.PasteEditsAction | undefined {
const { file, project } = this.getFileAndProject(args);
const copiedFrom = args.copiedFrom
? { file: args.copiedFrom.file, range: args.copiedFrom.range.map(copies => this.getRange({ file: args.copiedFrom!.file, startLine: copies.start.line, startOffset: copies.start.offset, endLine: copies.end.line, endOffset: copies.end.offset }, project.getScriptInfoForNormalizedPath(toNormalizedPath(args.copiedFrom!.file))!)) }
: undefined;
const result = project.getLanguageService().getPasteEdits(
{
targetFile: file,
pastedText: args.pastedText,
pasteLocations: args.pasteLocations.map(paste => this.getRange({ file, startLine: paste.start.line, startOffset: paste.start.offset, endLine: paste.end.line, endOffset: paste.end.offset }, project.getScriptInfoForNormalizedPath(file)!)),
copiedFrom,
preferences: this.getPreferences(file),
},
this.getFormatOptions(file),
);
return result && this.mapPasteEditsAction(result);
}

private organizeImports(args: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): readonly protocol.FileCodeEdits[] | readonly FileTextChanges[] {
Debug.assert(args.scope.type === "file");
const { file, project } = this.getFileAndProject(args.scope.args);
Expand Down Expand Up @@ -2928,6 +2948,10 @@ export class Session<TMessage = string> implements EventSender {
return { fixName, description, changes: this.mapTextChangesToCodeEdits(changes), commands, fixId, fixAllDescription };
}

private mapPasteEditsAction({ edits, fixId }: PasteEdits): protocol.PasteEditsAction {
return { edits: this.mapTextChangesToCodeEdits(edits), fixId };
}

private mapTextChangesToCodeEdits(textChanges: readonly FileTextChanges[]): protocol.FileCodeEdits[] {
return textChanges.map(change => this.mapTextChangeToCodeEdit(change));
}
Expand Down Expand Up @@ -3521,6 +3545,9 @@ export class Session<TMessage = string> implements EventSender {
[protocol.CommandTypes.GetMoveToRefactoringFileSuggestions]: (request: protocol.GetMoveToRefactoringFileSuggestionsRequest) => {
return this.requiredResponse(this.getMoveToRefactoringFileSuggestions(request.arguments));
},
[protocol.CommandTypes.GetPasteEdits]: (request: protocol.GetPasteEditsRequest) => {
return this.requiredResponse(this.getPasteEdits(request.arguments));
},
[protocol.CommandTypes.GetEditsForRefactorFull]: (request: protocol.GetEditsForRefactorRequest) => {
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ false));
},
Expand Down
1 change: 1 addition & 0 deletions src/services/_namespaces/ts.PasteEdits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "../pasteEdits";
2 changes: 2 additions & 0 deletions src/services/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ import * as textChanges from "./ts.textChanges";
export { textChanges };
import * as formatting from "./ts.formatting";
export { formatting };
import * as pasteEdits from "./ts.PasteEdits";
export { pasteEdits };
8 changes: 2 additions & 6 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,10 +893,6 @@ function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, module
}
}

function isFutureSymbolExportInfoArray(info: readonly SymbolExportInfo[] | readonly FutureSymbolExportInfo[]): info is readonly FutureSymbolExportInfo[] {
return info[0].symbol === undefined;
}

function getImportFixes(
exportInfos: readonly SymbolExportInfo[] | readonly FutureSymbolExportInfo[],
usagePosition: number | undefined,
Expand All @@ -910,7 +906,7 @@ function getImportFixes(
fromCacheOnly?: boolean,
): { computedWithoutCacheCount: number; fixes: readonly ImportFixWithModuleSpecifier[]; } {
const checker = program.getTypeChecker();
const existingImports = importMap && !isFutureSymbolExportInfoArray(exportInfos) ? flatMap(exportInfos, importMap.getImportsForExportInfo) : emptyArray;
const existingImports = importMap ? flatMap(exportInfos, importMap.getImportsForExportInfo) : emptyArray;
const useNamespace = usagePosition !== undefined && tryUseExistingNamespaceImport(existingImports, usagePosition);
const addToExisting = tryAddToExistingImport(existingImports, isValidTypeOnlyUseSite, checker, program.getCompilerOptions());
if (addToExisting) {
Expand Down Expand Up @@ -1092,7 +1088,7 @@ function createExistingImportMap(importingFile: SourceFile, program: Program) {
}

return {
getImportsForExportInfo: ({ moduleSymbol, exportKind, targetFlags, symbol }: SymbolExportInfo): readonly FixAddToExistingImportInfo[] => {
getImportsForExportInfo: ({ moduleSymbol, exportKind, targetFlags, symbol }: SymbolExportInfo | FutureSymbolExportInfo): readonly FixAddToExistingImportInfo[] => {
const matchingDeclarations = importMap?.get(getSymbolId(moduleSymbol));
if (!matchingDeclarations) return emptyArray;

Expand Down
115 changes: 115 additions & 0 deletions src/services/pasteEdits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { addRange } from "../compiler/core";
import {
CancellationToken,
Program,
SourceFile,
Statement,
SymbolFlags,
TextRange,
UserPreferences,
} from "../compiler/types";
import { getLineOfLocalPosition } from "../compiler/utilities";
import {
codefix,
Debug,
fileShouldUseJavaScriptRequire,
forEachChild,
formatting,
getQuotePreference,
isIdentifier,
textChanges,
} from "./_namespaces/ts";
import { addTargetFileImports } from "./refactors/helpers";
import {
addExportsInOldFile,
getExistingLocals,
getUsageInfo,
} from "./refactors/moveToFile";
import {
CodeFixContextBase,
FileTextChanges,
LanguageServiceHost,
PasteEdits,
} from "./types";

const fixId = "providePostPasteEdits";
/** @internal */
export function pasteEditsProvider(
targetFile: SourceFile,
pastedText: string[],
pasteLocations: TextRange[],
copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined,
host: LanguageServiceHost,
preferences: UserPreferences,
formatContext: formatting.FormatContext,
cancellationToken: CancellationToken,
): PasteEdits {
const changes: FileTextChanges[] = textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => pasteEdits(targetFile, pastedText, pasteLocations, copiedFrom, host, preferences, formatContext, cancellationToken, changeTracker));
return { edits: changes, fixId };
}

function pasteEdits(
targetFile: SourceFile,
pastedText: string[],
pasteLocations: TextRange[],
copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined,
host: LanguageServiceHost,
preferences: UserPreferences,
formatContext: formatting.FormatContext,
cancellationToken: CancellationToken,
changes: textChanges.ChangeTracker,
) {
let actualPastedText: string[] | undefined;
if (pastedText.length !== pasteLocations.length) {
actualPastedText = pastedText.length === 1 ? pastedText : [pastedText.join("\n")];
}
pasteLocations.forEach((paste, i) => {
changes.replaceRangeWithText(
targetFile,
{ pos: paste.pos, end: paste.end },
actualPastedText ?
actualPastedText[0] : pastedText[i],
);
});

const statements: Statement[] = [];

let newText = targetFile.text;
for (let i = pasteLocations.length - 1; i >= 0; i--) {
const { pos, end } = pasteLocations[i];
newText = actualPastedText ? newText.slice(0, pos) + actualPastedText[0] + newText.slice(end) : newText.slice(0, pos) + pastedText[i] + newText.slice(end);
}

Debug.checkDefined(host.runWithTemporaryFileUpdate).call(host, targetFile.fileName, newText, (updatedProgram: Program, originalProgram: Program | undefined, updatedFile: SourceFile) => {
const importAdder = codefix.createImportAdder(updatedFile, updatedProgram, preferences, host);
if (copiedFrom?.range) {
Debug.assert(copiedFrom.range.length === pastedText.length);
copiedFrom.range.forEach(copy => {
addRange(statements, copiedFrom.file.statements, getLineOfLocalPosition(copiedFrom.file, copy.pos), getLineOfLocalPosition(copiedFrom.file, copy.end) + 1);
});
const usage = getUsageInfo(copiedFrom.file, statements, originalProgram!.getTypeChecker(), getExistingLocals(updatedFile, statements, originalProgram!.getTypeChecker()));
Debug.assertIsDefined(originalProgram);
const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(targetFile.fileName, originalProgram, host, !!copiedFrom.file.commonJsModuleIndicator);
addExportsInOldFile(copiedFrom.file, usage.targetFileImportsFromOldFile, changes, useEsModuleSyntax);
addTargetFileImports(copiedFrom.file, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, originalProgram.getTypeChecker(), updatedProgram, importAdder);
}
else {
const context: CodeFixContextBase = {
sourceFile: updatedFile,
program: originalProgram!,
cancellationToken,
host,
preferences,
formatContext,
};
forEachChild(updatedFile, function cb(node) {
if (isIdentifier(node) && !originalProgram?.getTypeChecker().resolveName(node.text, node, SymbolFlags.All, /*excludeGlobals*/ false)) {
// generate imports
importAdder.addImportForUnresolvedIdentifier(context, node, /*useAutoImportProvider*/ true);
}
node.forEachChild(cb);
});
}
importAdder.writeFixes(changes, getQuotePreference(copiedFrom ? copiedFrom.file : targetFile, preferences));
});
}
6 changes: 4 additions & 2 deletions src/services/refactors/moveToFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ export function deleteUnusedOldImports(oldFile: SourceFile, toMove: readonly Sta
}
}

function addExportsInOldFile(oldFile: SourceFile, targetFileImportsFromOldFile: Map<Symbol, boolean>, changes: textChanges.ChangeTracker, useEsModuleSyntax: boolean) {
/** @internal */
export function addExportsInOldFile(oldFile: SourceFile, targetFileImportsFromOldFile: Map<Symbol, boolean>, changes: textChanges.ChangeTracker, useEsModuleSyntax: boolean) {
const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`.
targetFileImportsFromOldFile.forEach((_, symbol) => {
if (!symbol.declarations) {
Expand Down Expand Up @@ -1118,7 +1119,8 @@ function getOverloadRangeToMove(sourceFile: SourceFile, statement: Statement) {
return undefined;
}

function getExistingLocals(sourceFile: SourceFile, statements: readonly Statement[], checker: TypeChecker) {
/** @internal */
export function getExistingLocals(sourceFile: SourceFile, statements: readonly Statement[], checker: TypeChecker) {
const existingLocals = new Set<Symbol>();
for (const moduleSpecifier of sourceFile.imports) {
const declaration = importFromModuleSpecifier(moduleSpecifier);
Expand Down
Loading