ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
472 lines (471 loc) • 21.8 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter)/* istanbul ignore next */ || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator)/* istanbul ignore next */ || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
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 };
}
};
};
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("typescript");
var compiler = require("./../compiler");
var errors = require("./../errors");
var utils_1 = require("./../utils");
var createTempSourceFile_1 = require("./createTempSourceFile");
var nodeToWrapperMappings_1 = require("./nodeToWrapperMappings");
var ForgetfulNodeCache_1 = require("./ForgetfulNodeCache");
var DirectoryCache_1 = require("./DirectoryCache");
/**
* Factory for creating compiler wrappers.
* @internal
*/
var CompilerFactory = /** @class */ (function () {
/**
* Initializes a new instance of CompilerFactory.
* @param global - Global container.
*/
function CompilerFactory(global) {
this.global = global;
this.sourceFileCacheByFilePath = new utils_1.KeyValueCache();
this.nodeCache = new ForgetfulNodeCache_1.ForgetfulNodeCache();
this.sourceFileAddedEventContainer = new utils_1.EventContainer();
this.sourceFileRemovedEventContainer = new utils_1.EventContainer();
this.directoryCache = new DirectoryCache_1.DirectoryCache(global);
}
/**
* Gets all the source files sorted by their directory depth.
*/
CompilerFactory.prototype.getSourceFilesByDirectoryDepth = function () {
var _a, _b, dir, e_1_1, e_1, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_d.trys.push([0, 5, 6, 7]);
_a = __values(this.getDirectoriesByDepth()), _b = _a.next();
_d.label = 1;
case 1:
if (!!_b.done) return [3 /*break*/, 4];
dir = _b.value;
return [5 /*yield**/, __values(dir.getSourceFiles())];
case 2:
_d.sent();
_d.label = 3;
case 3:
_b = _a.next();
return [3 /*break*/, 1];
case 4: return [3 /*break*/, 7];
case 5:
e_1_1 = _d.sent();
e_1 = { error: e_1_1 };
return [3 /*break*/, 7];
case 6:
try {
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
}
finally { if (e_1) throw e_1.error; }
return [7 /*endfinally*/];
case 7: return [2 /*return*/];
}
});
};
/**
* Gets the source file paths from the internal cache.
*/
CompilerFactory.prototype.getSourceFilePaths = function () {
return utils_1.ArrayUtils.from(this.sourceFileCacheByFilePath.getKeys());
};
/**
* Occurs when a source file is added to the cache.
* @param subscription - Subscripton.
*/
CompilerFactory.prototype.onSourceFileAdded = function (subscription) {
this.sourceFileAddedEventContainer.subscribe(subscription);
};
/**
* Occurs when a source file is removed from the cache.
* @param subscription - Subscripton.
*/
CompilerFactory.prototype.onSourceFileRemoved = function (subscription) {
this.sourceFileRemovedEventContainer.subscribe(subscription);
};
/**
* Adds a source file by structure or text.
* @param filePath - File path.
* @param structureOrText - Structure or text.
*/
CompilerFactory.prototype.createSourceFile = function (filePath, structureOrText) {
if (structureOrText == null || typeof structureOrText === "string")
return this.createSourceFileFromText(filePath, structureOrText || "");
var sourceFile = this.createSourceFileFromText(filePath, "");
sourceFile.fill(structureOrText);
return sourceFile;
};
/**
* Creates a source file from a file path and text.
* Adds it to the cache.
* @param filePath - File path for the source file.
* @param sourceText - Text to create the source file with.
* @throws InvalidOperationError if the file exists.
*/
CompilerFactory.prototype.createSourceFileFromText = function (filePath, sourceText) {
filePath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, filePath);
if (this.containsSourceFileAtPath(filePath) || this.global.fileSystem.fileExistsSync(filePath))
throw new errors.InvalidOperationError("A source file already exists at the provided file path: " + filePath);
return this.getSourceFileFromText(filePath, sourceText);
};
/**
* Creates or overwrites a source file and text.
* Adds it to the cache.
* @param filePath - File path for the source file.
* @param sourceText - Text to create the source file with.
*/
CompilerFactory.prototype.createOrOverwriteSourceFileFromText = function (filePath, sourceText) {
filePath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, filePath);
var existingSourceFile = this.getSourceFileFromFilePath(filePath);
if (existingSourceFile != null) {
existingSourceFile.replaceWithText(sourceText);
return existingSourceFile;
}
return this.getSourceFileFromText(filePath, sourceText);
};
/**
* Creates a temporary source file that won't be added to the language service.
* @param sourceText - Text to create the source file with.
* @param filePath - File path to use.
* @returns Wrapped source file.
*/
CompilerFactory.prototype.createTempSourceFileFromText = function (sourceText, opts) {
if (opts === void 0) { opts = {}; }
var _a = opts.filePath, filePath = _a === void 0 ? "tsSimpleAstTempFile.ts" : _a, _b = opts.createLanguageService, createLanguageService = _b === void 0 ? false : _b;
return createTempSourceFile_1.createTempSourceFile(filePath, sourceText, { createLanguageService: createLanguageService, compilerOptions: this.global.compilerOptions });
};
/**
* Gets a source file from a file path. Will use the file path cache if the file exists.
* @param filePath - File path to get the file from.
*/
CompilerFactory.prototype.getSourceFileFromFilePath = function (filePath) {
filePath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, filePath);
var sourceFile = this.sourceFileCacheByFilePath.get(filePath);
if (sourceFile == null) {
if (this.global.fileSystem.fileExistsSync(filePath)) {
utils_1.Logger.log("Loading file: " + filePath);
sourceFile = this.getSourceFileFromText(filePath, this.global.fileSystem.readFileSync(filePath));
sourceFile.setIsSaved(true); // source files loaded from the disk are saved to start with
}
if (sourceFile != null) {
// ensure these are added to the ast
sourceFile.getReferencedFiles();
sourceFile.getTypeReferenceDirectives();
}
}
return sourceFile;
};
/**
* Gets if the internal cache contains a source file at a specific file path.
* @param filePath - File path to check.
*/
CompilerFactory.prototype.containsSourceFileAtPath = function (filePath) {
var absoluteFilePath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, filePath);
return this.sourceFileCacheByFilePath.has(absoluteFilePath);
};
/**
* Gets if the internal cache contains a source file with the specified directory path.
* @param dirPath - Directory path to check.
*/
CompilerFactory.prototype.containsDirectoryAtPath = function (dirPath) {
var normalizedDirPath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, dirPath);
return this.directoryCache.has(normalizedDirPath);
};
/**
* Gets the source file for a node.
* @param compilerNode - Compiler node to get the source file of.
*/
CompilerFactory.prototype.getSourceFileForNode = function (compilerNode) {
var currentNode = compilerNode;
while (currentNode.kind !== ts.SyntaxKind.SourceFile) {
if (currentNode.parent == null)
throw new errors.NotImplementedError("Could not find node source file.");
currentNode = currentNode.parent;
}
return this.getSourceFile(currentNode);
};
/**
* Gets if the factory contains the compiler node in its internal cache.
* @param compilerNode - Compiler node.
*/
CompilerFactory.prototype.hasCompilerNode = function (compilerNode) {
return this.nodeCache.has(compilerNode);
};
/**
* Gets an existing node from the cache.
* @param compilerNode - Compiler node.
*/
CompilerFactory.prototype.getExistingCompilerNode = function (compilerNode) {
return this.nodeCache.get(compilerNode);
};
/**
* Gets a wrapped compiler type based on the node's kind.
* @param node - Node to get the wrapped object from.
*/
CompilerFactory.prototype.getNodeFromCompilerNode = function (compilerNode, sourceFile) {
var _this = this;
if (compilerNode.kind === ts.SyntaxKind.SourceFile)
return this.getSourceFile(compilerNode);
var createNode = function (ctor) {
// ensure the parent is created
if (compilerNode.parent != null && !_this.nodeCache.has(compilerNode.parent))
_this.getNodeFromCompilerNode(compilerNode.parent, sourceFile);
return new ctor(_this.global, compilerNode, sourceFile);
};
if (nodeToWrapperMappings_1.nodeToWrapperMappings[compilerNode.kind] != null)
return this.nodeCache.getOrCreate(compilerNode, function () { return createNode(nodeToWrapperMappings_1.nodeToWrapperMappings[compilerNode.kind]); });
else
return this.nodeCache.getOrCreate(compilerNode, function () { return createNode(compiler.Node); });
};
CompilerFactory.prototype.getSourceFileFromText = function (filePath, sourceText) {
var compilerSourceFile = ts.createSourceFile(filePath, sourceText, this.global.manipulationSettings.getScriptTarget(), true);
return this.getSourceFile(compilerSourceFile);
};
/**
* Gets a wrapped source file from a compiler source file.
* @param sourceFile - Compiler source file.
*/
CompilerFactory.prototype.getSourceFile = function (compilerSourceFile) {
var _this = this;
return this.nodeCache.getOrCreate(compilerSourceFile, function () {
var sourceFile = new compiler.SourceFile(_this.global, compilerSourceFile);
_this.sourceFileCacheByFilePath.set(sourceFile.getFilePath(), sourceFile);
// add to list of directories
var dirPath = sourceFile.getDirectoryPath();
_this.directoryCache.createOrAddIfNotExists(dirPath);
_this.directoryCache.get(dirPath)._addSourceFile(sourceFile);
// fire the event
_this.sourceFileAddedEventContainer.fire(undefined);
return sourceFile;
});
};
/**
* Gets a directory from a path.
* @param dirPath - Directory path.
*/
CompilerFactory.prototype.getDirectoryFromPath = function (dirPath) {
dirPath = utils_1.FileUtils.getStandardizedAbsolutePath(this.global.fileSystem, dirPath);
var directory = this.directoryCache.get(dirPath);
if (directory == null && this.global.fileSystem.directoryExistsSync(dirPath))
directory = this.directoryCache.createOrAddIfNotExists(dirPath);
return directory;
};
/**
* Creates or adds a directory if it doesn't exist.
* @param dirPath - Directory path.
*/
CompilerFactory.prototype.createOrAddDirectoryIfNotExists = function (dirPath) {
return this.directoryCache.createOrAddIfNotExists(dirPath);
};
/**
* Creates a directory.
* @param dirPath - Directory path.
*/
CompilerFactory.prototype.createDirectory = function (dirPath) {
if (this.containsDirectoryAtPath(dirPath) || this.global.fileSystem.directoryExistsSync(dirPath))
throw new errors.InvalidOperationError("A directory already exists at the provided path: " + dirPath);
return this.directoryCache.createOrAddIfNotExists(dirPath);
};
/**
* Gets a directory.
* @param dirPath - Directory path.
*/
CompilerFactory.prototype.getDirectory = function (dirPath) {
return this.directoryCache.get(dirPath);
};
/**
* Gets all the directories iterated by depth.
*/
CompilerFactory.prototype.getDirectoriesByDepth = function () {
return this.directoryCache.getAllByDepth();
};
/**
* Gets the directories without a parent.
*/
CompilerFactory.prototype.getOrphanDirectories = function () {
return this.directoryCache.getOrphans();
};
/**
* Gets a warpped symbol display part form a compiler symbol display part.
* @param compilerObject - Compiler symbol display part.
*/
CompilerFactory.prototype.getSymbolDisplayPart = function (compilerObject) {
return new compiler.SymbolDisplayPart(compilerObject);
};
/**
* Gets a wrapped type from a compiler type.
* @param type - Compiler type.
*/
CompilerFactory.prototype.getType = function (type) {
return new compiler.Type(this.global, type);
};
/**
* Gets a warpped type parameter from a compiler type parameter.
* @param typeParameter - Compiler type parameter
*/
CompilerFactory.prototype.getTypeParameter = function (typeParameter) {
return new compiler.TypeParameter(this.global, typeParameter);
};
/**
* Gets a wrapped signature from a compiler signature.
* @param signature - Compiler signature.
*/
CompilerFactory.prototype.getSignature = function (signature) {
return new compiler.Signature(this.global, signature);
};
/**
* Gets a wrapped symbol from a compiler symbol.
* @param symbol - Compiler symbol.
*/
CompilerFactory.prototype.getSymbol = function (symbol) {
return new compiler.Symbol(this.global, symbol);
};
/**
* Gets a wrapped diagnostic from a compiler diagnostic.
* @param diagnostic - Compiler diagnostic.
*/
CompilerFactory.prototype.getDiagnostic = function (diagnostic) {
return new compiler.Diagnostic(this.global, diagnostic);
};
/**
* Gets a wrapped diagnostic message chain from a compiler diagnostic message chain.
* @param diagnostic - Compiler diagnostic message chain.
*/
CompilerFactory.prototype.getDiagnosticMessageChain = function (diagnosticMessageChain) {
return new compiler.DiagnosticMessageChain(this.global, diagnosticMessageChain);
};
/**
* Gets a warpped JS doc tag info from a compiler object.
* @param jsDocTagInfo - Compiler object.
*/
CompilerFactory.prototype.getJSDocTagInfo = function (jsDocTagInfo) {
return new compiler.JSDocTagInfo(jsDocTagInfo);
};
/**
* Replaces a compiler node in the cache.
* @param oldNode - Old node to remove.
* @param newNode - New node to use.
*/
CompilerFactory.prototype.replaceCompilerNode = function (oldNode, newNode) {
var nodeToReplace = oldNode instanceof compiler.Node ? oldNode.compilerNode : oldNode;
var node = oldNode instanceof compiler.Node ? oldNode : this.nodeCache.get(oldNode);
this.nodeCache.replaceKey(nodeToReplace, newNode);
if (node != null)
node.replaceCompilerNodeFromFactory(newNode);
};
/**
* Removes a node from the cache.
* @param node - Node to remove.
*/
CompilerFactory.prototype.removeNodeFromCache = function (node) {
var compilerNode = node.compilerNode;
this.nodeCache.removeByKey(compilerNode);
if (compilerNode.kind === ts.SyntaxKind.SourceFile) {
var sourceFile = compilerNode;
this.directoryCache.get(utils_1.FileUtils.getDirPath(sourceFile.fileName))._removeSourceFile(sourceFile.fileName);
this.sourceFileCacheByFilePath.removeByKey(sourceFile.fileName);
this.sourceFileRemovedEventContainer.fire(undefined);
}
};
/**
* Removes the directory from the cache.
* @param directory - Directory.
*/
CompilerFactory.prototype.removeDirectoryFromCache = function (directory) {
this.directoryCache.remove(directory.getPath());
};
/**
* Forgets the nodes created in the block.
* @param block - Block of code to run.
*/
CompilerFactory.prototype.forgetNodesCreatedInBlock = function (block) {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
var result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.nodeCache.setForgetPoint();
_a.label = 1;
case 1:
_a.trys.push([1, , 4, 5]);
result = block(function () {
var nodes = [];
for (var _i = 0; _i < arguments.length; _i++) {
nodes[_i] = arguments[_i];
}
try {
for (var nodes_1 = __values(nodes), nodes_1_1 = nodes_1.next(); !nodes_1_1.done; nodes_1_1 = nodes_1.next()) {
var node = nodes_1_1.value;
_this.nodeCache.rememberNode(node);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (nodes_1_1 && !nodes_1_1.done && (_a = nodes_1.return)) _a.call(nodes_1);
}
finally { if (e_2) throw e_2.error; }
}
var e_2, _a;
});
if (!(result != null && typeof result.then === "function")) return [3 /*break*/, 3];
return [4 /*yield*/, result];
case 2:
_a.sent();
_a.label = 3;
case 3: return [3 /*break*/, 5];
case 4:
this.nodeCache.forgetLastPoint();
return [7 /*endfinally*/];
case 5: return [2 /*return*/];
}
});
});
};
return CompilerFactory;
}());
exports.CompilerFactory = CompilerFactory;