ts-simple-ast
Version:
TypeScript compiler wrapper for static analysis and code manipulation.
803 lines (802 loc) • 37.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var errors = require("../../../errors");
var manipulation_1 = require("../../../manipulation");
var textSeek_1 = require("../../../manipulation/textSeek");
var typescript_1 = require("../../../typescript");
var utils_1 = require("../../../utils");
var helpers_1 = require("../base/helpers");
var base_1 = require("../base");
var callBaseSet_1 = require("../callBaseSet");
var common_1 = require("../common");
var statement_1 = require("../statement");
var FileSystemRefreshResult_1 = require("./FileSystemRefreshResult");
var callBaseGetStructure_1 = require("../callBaseGetStructure");
// todo: not sure why I need to explicitly type this in order to get TS to not complain... (TS 2.4.1)
exports.SourceFileBase = base_1.ModuledNode(base_1.TextInsertableNode(statement_1.StatementedNode(common_1.Node)));
var SourceFile = /** @class */ (function (_super) {
tslib_1.__extends(SourceFile, _super);
/**
* Initializes a new instance.
* @private
* @param context - Project context.
* @param node - Underlying node.
*/
function SourceFile(context, node) {
var _this =
// start hack :(
_super.call(this, context, node, undefined) || this;
/** @internal */
_this._isSaved = false;
/** @internal */
_this._modifiedEventContainer = new utils_1.EventContainer();
/** @internal */
_this._preModifiedEventContainer = new utils_1.EventContainer();
/** @internal */
_this._referenceContainer = new utils_1.SourceFileReferenceContainer(_this);
_this._sourceFile = _this;
// end hack
// store this before a modification happens to the file
var onPreModified = function () {
_this.isFromExternalLibrary(); // memoize
_this._preModifiedEventContainer.unsubscribe(onPreModified);
};
_this._preModifiedEventContainer.subscribe(onPreModified);
return _this;
}
/**
* @internal
*
* WARNING: This should only be called by the compiler factory!
*/
SourceFile.prototype._replaceCompilerNodeFromFactory = function (compilerNode) {
_super.prototype._replaceCompilerNodeFromFactory.call(this, compilerNode);
this._context.resetProgram(); // make sure the program has the latest source file
this._isSaved = false;
this._modifiedEventContainer.fire(this);
};
/**
* Gets the file path.
*/
SourceFile.prototype.getFilePath = function () {
return this.compilerNode.fileName;
};
/**
* Gets the file path's base name.
*/
SourceFile.prototype.getBaseName = function () {
return utils_1.FileUtils.getBaseName(this.getFilePath());
};
/**
* Gets the file path's base name without the extension.
*/
SourceFile.prototype.getBaseNameWithoutExtension = function () {
var baseName = this.getBaseName();
var extension = this.getExtension();
return baseName.substring(0, baseName.length - extension.length);
};
/**
* Gets the file path's extension.
*/
SourceFile.prototype.getExtension = function () {
return utils_1.FileUtils.getExtension(this.getFilePath());
};
/**
* Gets the directory that the source file is contained in.
*/
SourceFile.prototype.getDirectory = function () {
return this._context.compilerFactory.getDirectoryFromCache(this.getDirectoryPath());
};
/**
* Gets the directory path that the source file is contained in.
*/
SourceFile.prototype.getDirectoryPath = function () {
return utils_1.FileUtils.getDirPath(this.compilerNode.fileName);
};
/**
* Gets the full text with leading trivia.
*/
SourceFile.prototype.getFullText = function () {
// return the string instead of letting Node.getFullText() do a substring to prevent an extra allocation
return this.compilerNode.text;
};
/**
* Gets the line number at the provided position.
* @param pos - Position
*/
SourceFile.prototype.getLineNumberAtPos = function (pos) {
return utils_1.StringUtils.getLineNumberAtPos(this.getFullText(), pos);
};
/**
* Gets the character count from the start of the line to the provided position.
* @param pos - Position.
*/
SourceFile.prototype.getLengthFromLineStartAtPos = function (pos) {
return utils_1.StringUtils.getLengthFromLineStartAtPos(this.getFullText(), pos);
};
/**
* Copies this source file to the specified directory.
*
* This will modify the module specifiers in the new file, if necessary.
* @param dirPathOrDirectory Directory path or directory object to copy the file to.
* @param options Options for copying.
* @returns The source file the copy was made to.
*/
SourceFile.prototype.copyToDirectory = function (dirPathOrDirectory, options) {
var dirPath = typeof dirPathOrDirectory === "string" ? dirPathOrDirectory : dirPathOrDirectory.getPath();
return this.copy(utils_1.FileUtils.pathJoin(dirPath, this.getBaseName()), options);
};
/**
* Copy this source file to a new file.
*
* This will modify the module specifiers in the new file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for copying.
*/
SourceFile.prototype.copy = function (filePath, options) {
if (options === void 0) { options = {}; }
var result = this._copyInternal(filePath, options);
if (result === false)
return this;
var copiedSourceFile = result;
if (copiedSourceFile.getDirectoryPath() !== this.getDirectoryPath())
copiedSourceFile._updateReferencesForCopyInternal(this._getReferencesForCopyInternal());
return copiedSourceFile;
};
/** @internal */
SourceFile.prototype._copyInternal = function (filePath, options) {
if (options === void 0) { options = {}; }
var _a = options.overwrite, overwrite = _a === void 0 ? false : _a;
var _b = this._context, compilerFactory = _b.compilerFactory, fileSystemWrapper = _b.fileSystemWrapper;
filePath = fileSystemWrapper.getStandardizedAbsolutePath(filePath, this.getDirectoryPath());
if (filePath === this.getFilePath())
return false;
return getCopiedSourceFile(this);
function getCopiedSourceFile(currentFile) {
try {
return compilerFactory.createSourceFileFromText(filePath, currentFile.getFullText(), { overwrite: overwrite, markInProject: getShouldBeInProject() });
}
catch (err) {
if (err instanceof errors.InvalidOperationError)
throw new errors.InvalidOperationError("Did you mean to provide the overwrite option? " + err.message);
else
throw err;
}
function getShouldBeInProject() {
if (currentFile._isInProject())
return true;
var destinationFile = compilerFactory.getSourceFileFromCacheFromFilePath(filePath);
return destinationFile != null && destinationFile._isInProject();
}
}
};
/** @internal */
SourceFile.prototype._getReferencesForCopyInternal = function () {
return utils_1.ArrayUtils.from(this._referenceContainer.getLiteralsReferencingOtherSourceFilesEntries());
};
/** @internal */
SourceFile.prototype._updateReferencesForCopyInternal = function (literalReferences) {
var e_1, _a;
try {
// update the nodes in this list to point to the nodes in this copied source file
for (var literalReferences_1 = tslib_1.__values(literalReferences), literalReferences_1_1 = literalReferences_1.next(); !literalReferences_1_1.done; literalReferences_1_1 = literalReferences_1.next()) {
var reference = literalReferences_1_1.value;
reference[0] = this.getChildSyntaxListOrThrow().getDescendantAtStartWithWidth(reference[0].getStart(), reference[0].getWidth());
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (literalReferences_1_1 && !literalReferences_1_1.done && (_a = literalReferences_1.return)) _a.call(literalReferences_1);
}
finally { if (e_1) throw e_1.error; }
}
// update the string literals in the copied file
updateStringLiteralReferences(literalReferences);
};
/**
* Copy this source file to a new file and immediately saves it to the file system asynchronously.
*
* This will modify the module specifiers in the new file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for copying.
*/
SourceFile.prototype.copyImmediately = function (filePath, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var newSourceFile;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
newSourceFile = this.copy(filePath, options);
return [4 /*yield*/, newSourceFile.save()];
case 1:
_a.sent();
return [2 /*return*/, newSourceFile];
}
});
});
};
/**
* Copy this source file to a new file and immediately saves it to the file system synchronously.
*
* This will modify the module specifiers in the new file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for copying.
*/
SourceFile.prototype.copyImmediatelySync = function (filePath, options) {
var newSourceFile = this.copy(filePath, options);
newSourceFile.saveSync();
return newSourceFile;
};
/**
* Moves this source file to the specified directory.
*
* This will modify the module specifiers in other files that specify this file and the module specifiers in the current file, if necessary.
* @param dirPathOrDirectory Directory path or directory object to move the file to.
* @param options Options for moving.
*/
SourceFile.prototype.moveToDirectory = function (dirPathOrDirectory, options) {
var dirPath = typeof dirPathOrDirectory === "string" ? dirPathOrDirectory : dirPathOrDirectory.getPath();
return this.move(utils_1.FileUtils.pathJoin(dirPath, this.getBaseName()), options);
};
/**
* Moves this source file to a new file.
*
* This will modify the module specifiers in other files that specify this file and the module specifiers in the current file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for moving.
*/
SourceFile.prototype.move = function (filePath, options) {
if (options === void 0) { options = {}; }
var oldDirPath = this.getDirectoryPath();
var sourceFileReferences = this._getReferencesForMoveInternal();
var oldFilePath = this.getFilePath();
if (!this._moveInternal(filePath, options))
return this;
this._context.fileSystemWrapper.queueFileDelete(oldFilePath);
this._updateReferencesForMoveInternal(sourceFileReferences, oldDirPath);
// ignore any modifications in other source files
this._context.lazyReferenceCoordinator.clearDirtySourceFiles();
// need to add the current source file as being dirty because it was removed and added to the cache in the move
this._context.lazyReferenceCoordinator.addDirtySourceFile(this);
return this;
};
/** @internal */
SourceFile.prototype._moveInternal = function (filePath, options) {
if (options === void 0) { options = {}; }
var _a = options.overwrite, overwrite = _a === void 0 ? false : _a;
filePath = this._context.fileSystemWrapper.getStandardizedAbsolutePath(filePath, this.getDirectoryPath());
if (filePath === this.getFilePath())
return false;
var markAsInProject = false;
if (overwrite) {
// remove the past file if it exists
var existingSourceFile = this._context.compilerFactory.getSourceFileFromCacheFromFilePath(filePath);
if (existingSourceFile != null) {
markAsInProject = existingSourceFile._isInProject();
existingSourceFile.forget();
}
}
else
this._context.compilerFactory.throwIfFileExists(filePath, "Did you mean to provide the overwrite option?");
manipulation_1.replaceSourceFileForFilePathMove({
newFilePath: filePath,
sourceFile: this
});
if (markAsInProject)
this._markAsInProject();
if (this._isInProject())
this.getDirectory()._markAsInProject();
return true;
};
/** @internal */
SourceFile.prototype._getReferencesForMoveInternal = function () {
return {
literalReferences: utils_1.ArrayUtils.from(this._referenceContainer.getLiteralsReferencingOtherSourceFilesEntries()),
referencingLiterals: utils_1.ArrayUtils.from(this._referenceContainer.getReferencingLiteralsInOtherSourceFiles())
};
};
/** @internal */
SourceFile.prototype._updateReferencesForMoveInternal = function (sourceFileReferences, oldDirPath) {
var _this = this;
var literalReferences = sourceFileReferences.literalReferences, referencingLiterals = sourceFileReferences.referencingLiterals;
// update the literals in this file if the directory has changed
if (oldDirPath !== this.getDirectoryPath())
updateStringLiteralReferences(literalReferences);
// update the string literals in other files
updateStringLiteralReferences(referencingLiterals.map(function (node) { return ([node, _this]); }));
};
/**
* Moves this source file to a new file and asynchronously updates the file system immediately.
*
* This will modify the module specifiers in other files that specify this file and the module specifiers in the current file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for moving.
*/
SourceFile.prototype.moveImmediately = function (filePath, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var oldFilePath, newFilePath;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
oldFilePath = this.getFilePath();
newFilePath = this._context.fileSystemWrapper.getStandardizedAbsolutePath(filePath, this.getDirectoryPath());
this.move(filePath, options);
if (!(oldFilePath !== newFilePath)) return [3 /*break*/, 2];
return [4 /*yield*/, this._context.fileSystemWrapper.moveFileImmediately(oldFilePath, newFilePath, this.getFullText())];
case 1:
_a.sent();
this._isSaved = true;
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, this.save()];
case 3:
_a.sent();
_a.label = 4;
case 4: return [2 /*return*/, this];
}
});
});
};
/**
* Moves this source file to a new file and synchronously updates the file system immediately.
*
* This will modify the module specifiers in other files that specify this file and the module specifiers in the current file, if necessary.
* @param filePath - New file path. Can be relative to the original file or an absolute path.
* @param options - Options for moving.
*/
SourceFile.prototype.moveImmediatelySync = function (filePath, options) {
var oldFilePath = this.getFilePath();
var newFilePath = this._context.fileSystemWrapper.getStandardizedAbsolutePath(filePath, this.getDirectoryPath());
this.move(filePath, options);
if (oldFilePath !== newFilePath) {
this._context.fileSystemWrapper.moveFileImmediatelySync(oldFilePath, newFilePath, this.getFullText());
this._isSaved = true;
}
else
this.saveSync();
return this;
};
/**
* Queues a deletion of the file to the file system.
*
* The file will be deleted when you call ast.save(). If you wish to immediately delete the file, then use deleteImmediately().
*/
SourceFile.prototype.delete = function () {
var filePath = this.getFilePath();
this.forget();
this._context.fileSystemWrapper.queueFileDelete(filePath);
};
/**
* Asynchronously deletes the file from the file system.
*/
SourceFile.prototype.deleteImmediately = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var filePath;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
filePath = this.getFilePath();
this.forget();
return [4 /*yield*/, this._context.fileSystemWrapper.deleteFileImmediately(filePath)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* Synchronously deletes the file from the file system.
*/
SourceFile.prototype.deleteImmediatelySync = function () {
var filePath = this.getFilePath();
this.forget();
this._context.fileSystemWrapper.deleteFileImmediatelySync(filePath);
};
/**
* Asynchronously saves this file with any changes.
*/
SourceFile.prototype.save = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._context.fileSystemWrapper.writeFile(this.getFilePath(), this._getTextForSave())];
case 1:
_a.sent();
this._isSaved = true;
return [2 /*return*/];
}
});
});
};
/**
* Synchronously saves this file with any changes.
*/
SourceFile.prototype.saveSync = function () {
this._context.fileSystemWrapper.writeFileSync(this.getFilePath(), this._getTextForSave());
this._isSaved = true;
};
/** @internal */
SourceFile.prototype._getTextForSave = function () {
var text = this.getFullText();
return this._hasBom ? "\uFEFF" + text : text;
};
/**
* Gets any source files referenced via `/// <reference path="..." />` comments.
*/
SourceFile.prototype.getReferencedFiles = function () {
var _this = this;
var dirPath = this.getDirectoryPath();
return (this.compilerNode.referencedFiles || [])
.map(function (f) { return _this._context.compilerFactory.addOrGetSourceFileFromFilePath(utils_1.FileUtils.pathJoin(dirPath, f.fileName), { markInProject: false }); })
.filter(function (f) { return f != null; });
};
/**
* Gets any source files referenced via `/// <reference types="..." />` comments.
*/
SourceFile.prototype.getTypeReferenceDirectives = function () {
var _this = this;
var dirPath = this.getDirectoryPath();
return (this.compilerNode.typeReferenceDirectives || [])
.map(function (f) { return _this._context.compilerFactory.addOrGetSourceFileFromFilePath(utils_1.FileUtils.pathJoin(dirPath, f.fileName), { markInProject: false }); })
.filter(function (f) { return f != null; });
};
/**
* Get any source files that reference this source file.
*/
SourceFile.prototype.getReferencingSourceFiles = function () {
return utils_1.ArrayUtils.from(this._referenceContainer.getDependentSourceFiles());
};
/**
* Gets the import and exports in other source files that reference this source file.
*/
SourceFile.prototype.getReferencingNodesInOtherSourceFiles = function () {
return utils_1.ArrayUtils.from(this._referenceContainer.getReferencingNodesInOtherSourceFiles());
};
/**
* Gets the string literals in other source files that reference this source file.
*/
SourceFile.prototype.getReferencingLiteralsInOtherSourceFiles = function () {
return utils_1.ArrayUtils.from(this._referenceContainer.getReferencingLiteralsInOtherSourceFiles());
};
/**
* Gets all the descendant string literals that reference a source file.
*/
SourceFile.prototype.getImportStringLiterals = function () {
var _this = this;
this._ensureBound();
var literals = (this.compilerNode.imports || []);
return literals.filter(function (l) { return (l.flags & typescript_1.ts.NodeFlags.Synthesized) === 0; }).map(function (l) { return _this._getNodeFromCompilerNode(l); });
};
/**
* Gets the script target of the source file.
*/
SourceFile.prototype.getLanguageVersion = function () {
return this.compilerNode.languageVersion;
};
/**
* Gets the language variant of the source file.
*/
SourceFile.prototype.getLanguageVariant = function () {
return this.compilerNode.languageVariant;
};
/**
* Gets if this is a declaration file.
*/
SourceFile.prototype.isDeclarationFile = function () {
return this.compilerNode.isDeclarationFile;
};
/**
* Gets if the source file was discovered while loading an external library.
*/
SourceFile.prototype.isFromExternalLibrary = function () {
// This needs to be memoized and stored before modification because the TypeScript
// compiler does the following code:
//
// function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
// return !!sourceFilesFoundSearchingNodeModules.get(file.path);
// }
//
// So the compiler node will become out of date after a manipulation occurs and
// this will return false.
// do not create the program if not created before... if the program is
// not created then we know this source file wasn't discovered by the program
if (!this._context.program._isCompilerProgramCreated())
return false;
var compilerProgram = this._context.program.compilerObject;
return compilerProgram.isSourceFileFromExternalLibrary(this.compilerNode);
};
/**
* Gets if the source file is a descendant of a node_modules directory.
*/
SourceFile.prototype.isInNodeModules = function () {
return this.getFilePath().indexOf("/node_modules/") >= 0;
};
/**
* Gets if this source file has been saved or if the latest changes have been saved.
*/
SourceFile.prototype.isSaved = function () {
return this._isSaved;
};
/**
* Sets if this source file has been saved.
* @internal
*/
SourceFile.prototype._setIsSaved = function (value) {
this._isSaved = value;
};
/**
* Gets the pre-emit diagnostics of the specified source file.
*/
SourceFile.prototype.getPreEmitDiagnostics = function () {
return this._context.getPreEmitDiagnostics(this);
};
SourceFile.prototype.unindent = function (positionRangeOrPos, times) {
if (times === void 0) { times = 1; }
return this.indent(positionRangeOrPos, times * -1);
};
SourceFile.prototype.indent = function (positionRangeOrPos, times) {
var _this = this;
if (times === void 0) { times = 1; }
if (times === 0)
return this;
var sourceFileText = this.getFullText();
var positionRange = typeof positionRangeOrPos === "number" ? [positionRangeOrPos, positionRangeOrPos] : positionRangeOrPos;
errors.throwIfRangeOutOfRange(positionRange, [0, sourceFileText.length], "positionRange");
var startLinePos = textSeek_1.getPreviousMatchingPos(sourceFileText, positionRange[0], function (char) { return char === "\n"; });
var endLinePos = textSeek_1.getNextMatchingPos(sourceFileText, positionRange[1], function (char) { return char === "\r" || char === "\n"; });
var indentText = this._context.manipulationSettings.getIndentationText();
var correctedText = utils_1.StringUtils.indent(sourceFileText.substring(startLinePos, endLinePos), times, indentText, function (pos) { return _this.isInStringAtPos(pos + startLinePos); });
manipulation_1.replaceSourceFileTextForFormatting({
sourceFile: this,
newText: sourceFileText.substring(0, startLinePos) + correctedText + sourceFileText.substring(endLinePos)
});
return this;
};
/**
* Emits the source file.
*/
SourceFile.prototype.emit = function (options) {
return this._context.program.emit(tslib_1.__assign({ targetSourceFile: this }, options));
};
/**
* Gets the emit output of this source file.
* @param options - Emit options.
*/
SourceFile.prototype.getEmitOutput = function (options) {
if (options === void 0) { options = {}; }
return this._context.languageService.getEmitOutput(this, options.emitOnlyDtsFiles || false);
};
/**
* Formats the source file text using the internal TypeScript formatting API.
* @param settings - Format code settings.
*/
SourceFile.prototype.formatText = function (settings) {
if (settings === void 0) { settings = {}; }
manipulation_1.replaceSourceFileTextForFormatting({
sourceFile: this,
newText: this._context.languageService.getFormattedDocumentText(this.getFilePath(), settings)
});
};
/**
* Refresh the source file from the file system.
*
* WARNING: When updating from the file system, this will "forget" any previously navigated nodes.
* @returns What action ended up taking place.
*/
SourceFile.prototype.refreshFromFileSystem = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var fileReadResult;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._context.fileSystemWrapper.readFileOrNotExists(this.getFilePath(), this._context.getEncoding())];
case 1:
fileReadResult = _a.sent();
return [2 /*return*/, this._refreshFromFileSystemInternal(fileReadResult)];
}
});
});
};
/**
* Synchronously refreshes the source file from the file system.
*
* WARNING: When updating from the file system, this will "forget" any previously navigated nodes.
* @returns What action ended up taking place.
*/
SourceFile.prototype.refreshFromFileSystemSync = function () {
var fileReadResult = this._context.fileSystemWrapper.readFileOrNotExistsSync(this.getFilePath(), this._context.getEncoding());
return this._refreshFromFileSystemInternal(fileReadResult);
};
SourceFile.prototype.getRelativePathTo = function (sourceFileOrDir) {
return this.getDirectory().getRelativePathTo(sourceFileOrDir);
};
SourceFile.prototype.getRelativePathAsModuleSpecifierTo = function (sourceFileOrDir) {
return this.getDirectory().getRelativePathAsModuleSpecifierTo(sourceFileOrDir);
};
/**
* Subscribe to when the source file is modified.
* @param subscription - Subscription.
* @param subscribe - Optional and defaults to true. Use an explicit false to unsubscribe.
*/
SourceFile.prototype.onModified = function (subscription, subscribe) {
if (subscribe === void 0) { subscribe = true; }
if (subscribe)
this._modifiedEventContainer.subscribe(subscription);
else
this._modifiedEventContainer.unsubscribe(subscription);
return this;
};
/**
* Do an action the next time the source file is modified.
* @param action - Action to run.
* @internal
*/
SourceFile.prototype._doActionPreNextModification = function (action) {
var _this = this;
var wrappedSubscription = function () {
action();
_this._preModifiedEventContainer.unsubscribe(wrappedSubscription);
};
this._preModifiedEventContainer.subscribe(wrappedSubscription);
return this;
};
/** @internal */
SourceFile.prototype._firePreModified = function () {
this._preModifiedEventContainer.fire(this);
};
/**
* Organizes the imports in the file.
*
* WARNING! This will forget all the nodes in the file! It's best to do this after you're all done with the file.
* @param formatSettings - Format code settings.
* @param userPreferences - User preferences for refactoring.
*/
SourceFile.prototype.organizeImports = function (formatSettings, userPreferences) {
if (formatSettings === void 0) { formatSettings = {}; }
if (userPreferences === void 0) { userPreferences = {}; }
this.applyTextChanges(utils_1.ArrayUtils.flatten(this._context.languageService.organizeImports(this, formatSettings, userPreferences).map(function (r) { return r.getTextChanges(); })));
return this;
};
/**
* Code fix to add import declarations for identifiers that are referenced, but not imported in the source file.
* @param formatSettings - Format code settings.
* @param userPreferences - User preferences for refactoring.
*/
SourceFile.prototype.fixMissingImports = function (formatSettings, userPreferences) {
if (formatSettings === void 0) { formatSettings = {}; }
if (userPreferences === void 0) { userPreferences = {}; }
var e_2, _a;
var combinedCodeFix = this._context.languageService.getCombinedCodeFix(this, "fixMissingImport", formatSettings, userPreferences);
var sourceFile = this;
try {
for (var _b = tslib_1.__values(combinedCodeFix.getChanges()), _c = _b.next(); !_c.done; _c = _b.next()) {
var fileTextChanges = _c.value;
var changes = fileTextChanges.getTextChanges();
removeUnnecessaryDoubleBlankLines(changes);
applyTextChanges(changes);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
return this;
function removeUnnecessaryDoubleBlankLines(changes) {
changes.sort(function (a, b) { return a.getSpan().getStart() - b.getSpan().getStart(); });
// when a file has no imports, it will add a double newline to every change
// so remove them except for the last change
for (var i = 0; i < changes.length - 1; i++) { // skip last change
var compilerObject = changes[i].compilerObject;
compilerObject.newText = compilerObject.newText.replace(/(\r?)\n\r?\n$/, "$1\n");
}
}
function applyTextChanges(changes) {
var e_3, _a;
// group all the changes by their start position and insert them into the file
var groups = utils_1.ArrayUtils.groupBy(changes, function (change) { return change.getSpan().getStart(); });
var addedLength = 0;
try {
for (var groups_1 = tslib_1.__values(groups), groups_1_1 = groups_1.next(); !groups_1_1.done; groups_1_1 = groups_1.next()) {
var group = groups_1_1.value;
// these should all be import declarations so it should be safe
var insertPos = group[0].getSpan().getStart() + addedLength;
var newText = group.map(function (item) { return item.getNewText(); }).join("");
manipulation_1.insertIntoTextRange({
sourceFile: sourceFile,
insertPos: insertPos,
newText: newText
});
addedLength += newText.length;
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (groups_1_1 && !groups_1_1.done && (_a = groups_1.return)) _a.call(groups_1);
}
finally { if (e_3) throw e_3.error; }
}
}
};
/**
* Applies the text changes to the source file.
*
* WARNING! This will forget all the nodes in the file! It's best to do this after you're all done with the file.
* @param textChanges - Text changes.
*/
SourceFile.prototype.applyTextChanges = function (textChanges) {
// do nothing if no changes
if (textChanges.length === 0)
return this;
this.forgetDescendants();
manipulation_1.replaceNodeText({
sourceFile: this._sourceFile,
start: 0,
replacingLength: this.getFullWidth(),
newText: manipulation_1.getTextFromFormattingEdits(this, textChanges)
});
return this;
};
/**
* Sets the node from a structure.
* @param structure - Structure to set the node with.
*/
SourceFile.prototype.set = function (structure) {
callBaseSet_1.callBaseSet(exports.SourceFileBase.prototype, this, structure);
return this;
};
/**
* Gets the structure equivalent to this node.
*/
SourceFile.prototype.getStructure = function () {
return callBaseGetStructure_1.callBaseGetStructure(exports.SourceFileBase.prototype, this, {
bodyText: helpers_1.getBodyTextWithoutLeadingIndentation(this)
});
};
SourceFile.prototype._refreshFromFileSystemInternal = function (fileReadResult) {
if (fileReadResult === false) {
this.forget();
return FileSystemRefreshResult_1.FileSystemRefreshResult.Deleted;
}
var fileText = fileReadResult;
if (fileText === this.getFullText())
return FileSystemRefreshResult_1.FileSystemRefreshResult.NoChange;
this.replaceText([0, this.getEnd()], fileText);
this._setIsSaved(true); // saved when loaded from file system
return FileSystemRefreshResult_1.FileSystemRefreshResult.Updated;
};
/** @internal */
SourceFile.prototype._isInProject = function () {
return this._context.inProjectCoordinator.isSourceFileInProject(this);
};
/** @internal */
SourceFile.prototype._markAsInProject = function () {
this._context.inProjectCoordinator.markSourceFileAsInProject(this);
};
tslib_1.__decorate([
utils_1.Memoize
], SourceFile.prototype, "isFromExternalLibrary", null);
return SourceFile;
}(exports.SourceFileBase));
exports.SourceFile = SourceFile;
function updateStringLiteralReferences(nodeReferences) {
var e_4, _a;
try {
for (var nodeReferences_1 = tslib_1.__values(nodeReferences), nodeReferences_1_1 = nodeReferences_1.next(); !nodeReferences_1_1.done; nodeReferences_1_1 = nodeReferences_1.next()) {
var _b = tslib_1.__read(nodeReferences_1_1.value, 2), stringLiteral = _b[0], sourceFile = _b[1];
if (utils_1.ModuleUtils.isModuleSpecifierRelative(stringLiteral.getLiteralText()))
stringLiteral.setLiteralValue(stringLiteral._sourceFile.getRelativePathAsModuleSpecifierTo(sourceFile));
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (nodeReferences_1_1 && !nodeReferences_1_1.done && (_a = nodeReferences_1.return)) _a.call(nodeReferences_1);
}
finally { if (e_4) throw e_4.error; }
}
}