ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
291 lines (290 loc) • 14.6 kB
JavaScript
"use strict";
var __values = (this && this.__values)/* istanbul ignore next */ || function (o) {
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
if (m) return m.call(o);
return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
};
var __read = (this && this.__read)/* istanbul ignore next */ || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("typescript");
var manipulation_1 = require("./../../manipulation");
var errors = require("./../../errors");
var fileSystem_1 = require("./../../fileSystem");
var utils_1 = require("./../../utils");
var Program_1 = require("./Program");
var results_1 = require("./results");
var LanguageService = /** @class */ (function () {
/** @internal */
function LanguageService(global) {
var _this = this;
this.global = global;
// I don't know what I'm doing for some of this...
var version = 0;
var fileExistsSync = function (path) { return _this.global.compilerFactory.containsSourceFileAtPath(path) || global.fileSystem.fileExistsSync(path); };
var languageServiceHost = {
getCompilationSettings: function () { return global.compilerOptions; },
getNewLine: function () { return global.manipulationSettings.getNewLineKind(); },
getScriptFileNames: function () { return _this.global.compilerFactory.getSourceFilePaths(); },
getScriptVersion: function (fileName) {
return (version++).toString();
},
getScriptSnapshot: function (fileName) {
if (!fileExistsSync(fileName))
return undefined;
return ts.ScriptSnapshot.fromString(_this.global.compilerFactory.getSourceFileFromFilePath(fileName).getFullText());
},
getCurrentDirectory: function () { return global.fileSystem.getCurrentDirectory(); },
getDefaultLibFileName: function (options) {
if (_this.global.fileSystem instanceof fileSystem_1.DefaultFileSystemHost)
return ts.getDefaultLibFilePath(global.compilerOptions);
else
return utils_1.FileUtils.pathJoin(global.fileSystem.getCurrentDirectory(), "node_modules/typescript/lib/" + ts.getDefaultLibFileName(global.compilerOptions));
},
useCaseSensitiveFileNames: function () { return true; },
readFile: function (path, encoding) {
if (_this.global.compilerFactory.containsSourceFileAtPath(path))
return _this.global.compilerFactory.getSourceFileFromFilePath(path).getFullText();
return _this.global.fileSystem.readFileSync(path, encoding);
},
fileExists: fileExistsSync,
directoryExists: function (dirName) { return _this.global.compilerFactory.containsDirectoryAtPath(dirName) || _this.global.fileSystem.directoryExistsSync(dirName); }
};
this.compilerHost = {
getSourceFile: function (fileName, languageVersion, onError) {
var sourceFile = _this.global.compilerFactory.getSourceFileFromFilePath(fileName);
return sourceFile == null ? undefined : sourceFile.compilerNode;
},
// getSourceFileByPath: (...) => {}, // not providing these will force it to use the file name as the file path
// getDefaultLibLocation: (...) => {},
getDefaultLibFileName: function (options) { return languageServiceHost.getDefaultLibFileName(options); },
writeFile: function (filePath, data, writeByteOrderMark, onError, sourceFiles) {
_this.global.fileSystem.writeFileSync(filePath, data);
},
getCurrentDirectory: function () { return languageServiceHost.getCurrentDirectory(); },
getDirectories: function (path) {
// todo: not sure where this is used...
return [];
},
fileExists: function (fileName) { return languageServiceHost.fileExists(fileName); },
readFile: function (fileName) { return languageServiceHost.readFile(fileName); },
getCanonicalFileName: function (fileName) { return utils_1.FileUtils.getStandardizedAbsolutePath(_this.global.fileSystem, fileName); },
useCaseSensitiveFileNames: function () { return languageServiceHost.useCaseSensitiveFileNames(); },
getNewLine: function () { return languageServiceHost.getNewLine(); },
getEnvironmentVariable: function (name) { return process.env[name]; }
};
this._compilerObject = ts.createLanguageService(languageServiceHost);
this.program = new Program_1.Program(this.global, this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);
this.global.compilerFactory.onSourceFileAdded(function () { return _this.resetProgram(); });
this.global.compilerFactory.onSourceFileRemoved(function () { return _this.resetProgram(); });
}
Object.defineProperty(LanguageService.prototype, "compilerObject", {
/**
* Gets the compiler language service.
*/
get: function () {
return this._compilerObject;
},
enumerable: true,
configurable: true
});
/**
* Resets the program. This should be done whenever any modifications happen.
* @internal
*/
LanguageService.prototype.resetProgram = function () {
this.program.reset(this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);
};
/**
* Gets the language service's program.
*/
LanguageService.prototype.getProgram = function () {
return this.program;
};
/**
* Rename the specified node.
* @param node - Node to rename.
* @param newName - New name for the node.
*/
LanguageService.prototype.renameNode = function (node, newName) {
errors.throwIfNotStringOrWhitespace(newName, "newName");
if (node.getText() === newName)
return;
this.renameLocations(this.findRenameLocations(node), newName);
};
/**
* Rename the provided rename locations.
* @param renameLocations - Rename locations.
* @param newName - New name for the node.
*/
LanguageService.prototype.renameLocations = function (renameLocations, newName) {
var renameLocationsBySourceFile = new utils_1.KeyValueCache();
try {
for (var renameLocations_1 = __values(renameLocations), renameLocations_1_1 = renameLocations_1.next(); !renameLocations_1_1.done; renameLocations_1_1 = renameLocations_1.next()) {
var renameLocation = renameLocations_1_1.value;
var locations = renameLocationsBySourceFile.getOrCreate(renameLocation.getSourceFile(), function () { return []; });
locations.push(renameLocation);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (renameLocations_1_1 && !renameLocations_1_1.done && (_a = renameLocations_1.return)) _a.call(renameLocations_1);
}
finally { if (e_1) throw e_1.error; }
}
try {
for (var _b = __values(renameLocationsBySourceFile.getEntries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), sourceFile = _d[0], locations = _d[1];
manipulation_1.replaceSourceFileTextForRename({
sourceFile: sourceFile,
renameLocations: locations,
newName: newName
});
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
var e_1, _a, e_2, _e;
};
/**
* Gets the definitions for the specified node.
* @param node - Node.
*/
LanguageService.prototype.getDefinitions = function (node) {
return this.getDefinitionsAtPosition(node.sourceFile, node.getStart());
};
/**
* Gets the definitions at the specified position.
* @param sourceFile - Source file.
* @param pos - Position.
*/
LanguageService.prototype.getDefinitionsAtPosition = function (sourceFile, pos) {
var _this = this;
var results = this.compilerObject.getDefinitionAtPosition(sourceFile.getFilePath(), pos) || [];
return results.map(function (info) { return new results_1.DefinitionInfo(_this.global, info); });
};
/**
* Gets the implementations for the specified node.
* @param node - Node.
*/
LanguageService.prototype.getImplementations = function (node) {
return this.getImplementationsAtPosition(node.sourceFile, node.getStart());
};
/**
* Gets the implementations at the specified position.
* @param sourceFile - Source file.
* @param pos - Position.
*/
LanguageService.prototype.getImplementationsAtPosition = function (sourceFile, pos) {
var _this = this;
var results = this.compilerObject.getImplementationAtPosition(sourceFile.getFilePath(), pos) || [];
return results.map(function (location) { return new results_1.ImplementationLocation(_this.global, location); });
};
/**
* Finds references based on the specified node.
* @param node - Node to find references for.
*/
LanguageService.prototype.findReferences = function (node) {
return this.findReferencesAtPosition(node.sourceFile, node.getStart());
};
/**
* Finds references based on the specified position.
* @param sourceFile - Source file.
* @param pos - Position to find the reference at.
*/
LanguageService.prototype.findReferencesAtPosition = function (sourceFile, pos) {
var _this = this;
var results = this.compilerObject.findReferences(sourceFile.getFilePath(), pos) || [];
return results.map(function (s) { return new results_1.ReferencedSymbol(_this.global, s); });
};
/**
* Find the rename locations for the specified node.
* @param node - Node to get the rename locations for.
*/
LanguageService.prototype.findRenameLocations = function (node) {
var _this = this;
var sourceFile = node.getSourceFile();
var renameLocations = this.compilerObject.findRenameLocations(sourceFile.getFilePath(), node.getStart(), false, false) || [];
return renameLocations.map(function (l) { return new results_1.RenameLocation(_this.global, l); });
};
/**
* Gets the formatting edits for a document.
* @param filePath - File path of the source file.
* @param settings - Format code settings.
*/
LanguageService.prototype.getFormattingEditsForDocument = function (filePath, settings) {
utils_1.fillDefaultFormatCodeSettings(settings, this.global.manipulationSettings);
var formattingEdits = this.compilerObject.getFormattingEditsForDocument(filePath, settings) || [];
return formattingEdits.map(function (e) { return new results_1.TextChange(e); });
};
/**
* Gets the formatted text for a document.
* @param filePath - File path of the source file.
* @param settings - Format code settings.
*/
LanguageService.prototype.getFormattedDocumentText = function (filePath, settings) {
var sourceFile = this.global.compilerFactory.getSourceFileFromFilePath(filePath);
if (sourceFile == null)
throw new errors.FileNotFoundError(filePath);
var newText = getCompilerFormattedText(this);
if (settings.ensureNewLineAtEndOfFile && !utils_1.StringUtils.endsWith(newText, settings.newLineCharacter))
newText += settings.newLineCharacter;
return newText.replace(/\r?\n/g, settings.newLineCharacter);
function getCompilerFormattedText(languageService) {
var formattingEditsInReverseOrder = languageService.getFormattingEditsForDocument(filePath, settings)
.sort(function (a, b) { return b.getSpan().getStart() - a.getSpan().getStart(); });
var fullText = sourceFile.getFullText();
try {
for (var formattingEditsInReverseOrder_1 = __values(formattingEditsInReverseOrder), formattingEditsInReverseOrder_1_1 = formattingEditsInReverseOrder_1.next(); !formattingEditsInReverseOrder_1_1.done; formattingEditsInReverseOrder_1_1 = formattingEditsInReverseOrder_1.next()) {
var textChange = formattingEditsInReverseOrder_1_1.value;
var span = textChange.getSpan();
fullText = fullText.slice(0, span.getStart()) + textChange.getNewText() + fullText.slice(span.getEnd());
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (formattingEditsInReverseOrder_1_1 && !formattingEditsInReverseOrder_1_1.done && (_a = formattingEditsInReverseOrder_1.return)) _a.call(formattingEditsInReverseOrder_1);
}
finally { if (e_3) throw e_3.error; }
}
return fullText;
var e_3, _a;
}
};
LanguageService.prototype.getEmitOutput = function (filePathOrSourceFile, emitOnlyDtsFiles) {
var filePath = typeof filePathOrSourceFile === "string"
? utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, filePathOrSourceFile)
: filePathOrSourceFile.getFilePath();
if (!this.global.compilerFactory.containsSourceFileAtPath(filePath))
throw new errors.FileNotFoundError(filePath);
return new results_1.EmitOutput(this.global, filePath, this.compilerObject.getEmitOutput(filePath, emitOnlyDtsFiles));
};
return LanguageService;
}());
exports.LanguageService = LanguageService;