@ts-morph/bootstrap
Version:
API for getting quickly set up with the TypeScript Compiler API.
460 lines (450 loc) • 23.3 kB
JavaScript
'use strict';
var common = require('@ts-morph/common');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
}
function __runInitializers(thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
class SourceFileCache {
#sourceFilesByFilePath = new Map();
#projectVersion = 0;
#fileSystemWrapper;
#compilerOptions;
documentRegistry;
constructor(fileSystemWrapper, compilerOptions) {
this.documentRegistry = new common.DocumentRegistry(fileSystemWrapper);
this.#fileSystemWrapper = fileSystemWrapper;
this.#compilerOptions = compilerOptions;
}
containsSourceFileAtPath(filePath) {
return this.#sourceFilesByFilePath.has(filePath);
}
getSourceFilePaths() {
return this.#sourceFilesByFilePath.keys();
}
getSourceFiles() {
return this.#sourceFilesByFilePath.values();
}
getProjectVersion() {
return this.#projectVersion;
}
getSourceFileVersion(sourceFile) {
return this.documentRegistry.getSourceFileVersion(sourceFile);
}
getSourceFileFromCacheFromFilePath(filePath) {
return this.#sourceFilesByFilePath.get(filePath);
}
async addOrGetSourceFileFromFilePath(filePath, options) {
let sourceFile = this.#sourceFilesByFilePath.get(filePath);
if (sourceFile == null) {
const fileText = await this.#fileSystemWrapper.readFileIfExists(filePath, this.#compilerOptions.getEncoding());
if (fileText != null) {
sourceFile = this.createSourceFileFromText(filePath, fileText, options);
}
}
return sourceFile;
}
addOrGetSourceFileFromFilePathSync(filePath, options) {
let sourceFile = this.#sourceFilesByFilePath.get(filePath);
if (sourceFile == null) {
const fileText = this.#fileSystemWrapper.readFileIfExistsSync(filePath, this.#compilerOptions.getEncoding());
if (fileText != null) {
sourceFile = this.createSourceFileFromText(filePath, fileText, options);
}
}
return sourceFile;
}
createSourceFileFromText(filePath, text, options) {
filePath = this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath);
const hasBom = common.StringUtils.hasBom(text);
if (hasBom)
text = common.StringUtils.stripBom(text);
const sourceFile = this.documentRegistry.createOrUpdateSourceFile(filePath, this.#compilerOptions.get(), common.ts.ScriptSnapshot.fromString(text), options.scriptKind);
this.setSourceFile(sourceFile);
return sourceFile;
}
setSourceFile(sourceFile) {
const standardizedFilePath = this.#fileSystemWrapper.getStandardizedAbsolutePath(sourceFile.fileName);
sourceFile.fileName = standardizedFilePath;
this.documentRegistry.updateDocument(standardizedFilePath, this.#compilerOptions.get(), common.ts.ScriptSnapshot.fromString(sourceFile.text), this.getSourceFileVersion(sourceFile), sourceFile["scriptKind"]);
const dirPath = common.FileUtils.getDirPath(standardizedFilePath);
if (!this.#fileSystemWrapper.directoryExistsSync(dirPath))
this.#fileSystemWrapper.queueMkdir(dirPath);
this.#sourceFilesByFilePath.set(standardizedFilePath, sourceFile);
this.#projectVersion++;
}
removeSourceFile(filePath) {
this.#sourceFilesByFilePath.delete(filePath);
}
containsDirectoryAtPath(dirPath) {
return this.#fileSystemWrapper.directoryExistsSync(dirPath);
}
getChildDirectoriesOfDirectory(dirPath) {
return this.#fileSystemWrapper.getDirectories(dirPath);
}
}
async function createProject(options = {}) {
const { project, tsConfigResolver } = createProjectCommon(options);
if (tsConfigResolver != null && options.skipAddingFilesFromTsConfig !== true) {
await addSourceFilesForTsConfigResolver(project, tsConfigResolver, project.compilerOptions.get());
if (!options.skipFileDependencyResolution)
project.resolveSourceFileDependencies();
}
return project;
}
function createProjectSync(options = {}) {
const { project, tsConfigResolver } = createProjectCommon(options);
if (tsConfigResolver != null && options.skipAddingFilesFromTsConfig !== true) {
addSourceFilesForTsConfigResolverSync(project, tsConfigResolver, project.compilerOptions.get());
if (!options.skipFileDependencyResolution)
project.resolveSourceFileDependencies();
}
return project;
}
function createProjectCommon(options) {
verifyOptions();
const fileSystem = getFileSystem();
const fileSystemWrapper = new common.TransactionalFileSystem({
fileSystem,
libFolderPath: options.libFolderPath,
skipLoadingLibFiles: options.skipLoadingLibFiles,
});
const tsConfigResolver = options.tsConfigFilePath == null
? undefined
: new common.TsConfigResolver(fileSystemWrapper, fileSystemWrapper.getStandardizedAbsolutePath(options.tsConfigFilePath), getEncodingFromProvidedOptions());
const project = new Project({
fileSystem,
fileSystemWrapper,
tsConfigResolver,
}, options);
return { project, tsConfigResolver };
function verifyOptions() {
if (options.fileSystem != null && options.useInMemoryFileSystem)
throw new common.errors.InvalidOperationError("Cannot provide a file system when specifying to use an in-memory file system.");
}
function getFileSystem() {
if (options.useInMemoryFileSystem)
return new common.InMemoryFileSystemHost();
return options.fileSystem ?? new common.RealFileSystemHost();
}
function getEncodingFromProvidedOptions() {
const defaultEncoding = "utf-8";
if (options.compilerOptions != null)
return options.compilerOptions.charset || defaultEncoding;
return defaultEncoding;
}
}
let Project = (() => {
let _instanceExtraInitializers = [];
let _getLanguageService_decorators;
let _getModuleResolutionHost_decorators;
return class Project {
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_getLanguageService_decorators = [common.Memoize];
_getModuleResolutionHost_decorators = [common.Memoize];
__esDecorate(this, null, _getLanguageService_decorators, { kind: "method", name: "getLanguageService", static: false, private: false, access: { has: obj => "getLanguageService" in obj, get: obj => obj.getLanguageService }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(this, null, _getModuleResolutionHost_decorators, { kind: "method", name: "getModuleResolutionHost", static: false, private: false, access: { has: obj => "getModuleResolutionHost" in obj, get: obj => obj.getModuleResolutionHost }, metadata: _metadata }, null, _instanceExtraInitializers);
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
#sourceFileCache = __runInitializers(this, _instanceExtraInitializers);
#fileSystemWrapper;
#languageServiceHost;
#compilerHost;
#configFileParsingDiagnostics;
constructor(objs, options) {
const { tsConfigResolver } = objs;
this.fileSystem = objs.fileSystem;
this.#fileSystemWrapper = objs.fileSystemWrapper;
const tsCompilerOptions = getCompilerOptions();
this.compilerOptions = new common.CompilerOptionsContainer();
this.compilerOptions.set(tsCompilerOptions);
this.#sourceFileCache = new SourceFileCache(this.#fileSystemWrapper, this.compilerOptions);
const resolutionHost = !options.resolutionHost
? undefined
: options.resolutionHost(this.getModuleResolutionHost(), () => this.compilerOptions.get());
const newLineKind = "\n";
const { languageServiceHost, compilerHost } = common.createHosts({
transactionalFileSystem: this.#fileSystemWrapper,
sourceFileContainer: this.#sourceFileCache,
compilerOptions: this.compilerOptions,
getNewLine: () => newLineKind,
resolutionHost: resolutionHost || {},
getProjectVersion: () => this.#sourceFileCache.getProjectVersion().toString(),
isKnownTypesPackageName: options.isKnownTypesPackageName,
libFolderPath: options.libFolderPath,
skipLoadingLibFiles: options.skipLoadingLibFiles,
});
this.#languageServiceHost = languageServiceHost;
this.#compilerHost = compilerHost;
this.#configFileParsingDiagnostics = tsConfigResolver?.getErrors() ?? [];
function getCompilerOptions() {
return {
...getTsConfigCompilerOptions(),
...(options.compilerOptions || {}),
};
}
function getTsConfigCompilerOptions() {
if (tsConfigResolver == null)
return {};
return tsConfigResolver.getCompilerOptions();
}
}
compilerOptions;
fileSystem;
async addSourceFileAtPath(filePath, options) {
const sourceFile = await this.addSourceFileAtPathIfExists(filePath, options);
if (sourceFile == null)
throw new common.errors.FileNotFoundError(this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath));
return sourceFile;
}
addSourceFileAtPathSync(filePath, options) {
const sourceFile = this.addSourceFileAtPathIfExistsSync(filePath, options);
if (sourceFile == null)
throw new common.errors.FileNotFoundError(this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath));
return sourceFile;
}
addSourceFileAtPathIfExists(filePath, options) {
return this.#sourceFileCache.addOrGetSourceFileFromFilePath(this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath), {
scriptKind: options && options.scriptKind,
});
}
addSourceFileAtPathIfExistsSync(filePath, options) {
return this.#sourceFileCache.addOrGetSourceFileFromFilePathSync(this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath), {
scriptKind: options && options.scriptKind,
});
}
async addSourceFilesByPaths(fileGlobs) {
if (typeof fileGlobs === "string")
fileGlobs = [fileGlobs];
const sourceFilePromises = [];
const sourceFiles = [];
for (const filePath of await this.#fileSystemWrapper.glob(fileGlobs)) {
sourceFilePromises.push(this.addSourceFileAtPathIfExists(filePath).then(sourceFile => {
if (sourceFile != null)
sourceFiles.push(sourceFile);
}));
}
await Promise.all(sourceFilePromises);
return sourceFiles;
}
addSourceFilesByPathsSync(fileGlobs) {
if (typeof fileGlobs === "string")
fileGlobs = [fileGlobs];
const sourceFiles = [];
for (const filePath of this.#fileSystemWrapper.globSync(fileGlobs)) {
const sourceFile = this.addSourceFileAtPathIfExistsSync(filePath);
if (sourceFile != null)
sourceFiles.push(sourceFile);
}
return sourceFiles;
}
addSourceFilesFromTsConfig(tsConfigFilePath) {
const resolver = this.#getTsConfigResolver(tsConfigFilePath);
return addSourceFilesForTsConfigResolver(this, resolver, resolver.getCompilerOptions());
}
addSourceFilesFromTsConfigSync(tsConfigFilePath) {
const resolver = this.#getTsConfigResolver(tsConfigFilePath);
return addSourceFilesForTsConfigResolverSync(this, resolver, resolver.getCompilerOptions());
}
#getTsConfigResolver(tsConfigFilePath) {
const standardizedFilePath = this.#fileSystemWrapper.getStandardizedAbsolutePath(tsConfigFilePath);
return new common.TsConfigResolver(this.#fileSystemWrapper, standardizedFilePath, this.compilerOptions.getEncoding());
}
createSourceFile(filePath, sourceFileText, options) {
return this.#sourceFileCache.createSourceFileFromText(this.#fileSystemWrapper.getStandardizedAbsolutePath(filePath), sourceFileText || "", { scriptKind: options && options.scriptKind });
}
updateSourceFile(filePathOrSourceFile, sourceFileText, options) {
if (typeof filePathOrSourceFile === "string")
return this.createSourceFile(filePathOrSourceFile, sourceFileText, options);
incrementVersion(filePathOrSourceFile);
ensureScriptSnapshot(filePathOrSourceFile);
return this.#sourceFileCache.setSourceFile(filePathOrSourceFile);
function incrementVersion(sourceFile) {
let version = sourceFile.version || "-1";
const parsedVersion = parseInt(version, 10);
if (isNaN(parsedVersion))
version = "0";
else
version = (parsedVersion + 1).toString();
sourceFile.version = version;
}
function ensureScriptSnapshot(sourceFile) {
if (sourceFile.scriptSnapshot == null)
sourceFile.scriptSnapshot = common.ts.ScriptSnapshot.fromString(sourceFile.text);
}
}
removeSourceFile(filePathOrSourceFile) {
this.#sourceFileCache.removeSourceFile(this.#fileSystemWrapper.getStandardizedAbsolutePath(typeof filePathOrSourceFile === "string" ? filePathOrSourceFile : filePathOrSourceFile.fileName));
}
resolveSourceFileDependencies() {
this.createProgram();
}
#oldProgram;
createProgram(options) {
const oldProgram = this.#oldProgram;
const program = common.ts.createProgram({
rootNames: Array.from(this.#sourceFileCache.getSourceFilePaths()),
options: this.compilerOptions.get(),
host: this.#compilerHost,
oldProgram,
configFileParsingDiagnostics: this.#configFileParsingDiagnostics,
...options,
});
this.#oldProgram = program;
return program;
}
getLanguageService() {
return common.ts.createLanguageService(this.#languageServiceHost, this.#sourceFileCache.documentRegistry);
}
getSourceFileOrThrow(fileNameOrSearchFunction) {
const sourceFile = this.#getSourceFileInternal(fileNameOrSearchFunction);
if (sourceFile != null)
return sourceFile;
if (typeof fileNameOrSearchFunction === "string") {
const fileNameOrPath = common.FileUtils.standardizeSlashes(fileNameOrSearchFunction);
if (common.FileUtils.pathIsAbsolute(fileNameOrPath) || fileNameOrPath.indexOf("/") >= 0) {
const errorFileNameOrPath = this.#fileSystemWrapper.getStandardizedAbsolutePath(fileNameOrPath);
throw new common.errors.InvalidOperationError(`Could not find source file in project at the provided path: ${errorFileNameOrPath}`);
}
else {
throw new common.errors.InvalidOperationError(`Could not find source file in project with the provided file name: ${fileNameOrSearchFunction}`);
}
}
else {
throw new common.errors.InvalidOperationError(`Could not find source file in project based on the provided condition.`);
}
}
getSourceFile(fileNameOrSearchFunction) {
return this.#getSourceFileInternal(fileNameOrSearchFunction);
}
#getSourceFileInternal(fileNameOrSearchFunction) {
const filePathOrSearchFunction = getFilePathOrSearchFunction(this.#fileSystemWrapper);
if (isStandardizedFilePath(filePathOrSearchFunction)) {
return this.#sourceFileCache.getSourceFileFromCacheFromFilePath(filePathOrSearchFunction);
}
const allSourceFilesIterable = this.getSourceFiles();
return selectSmallestDirPathResult(function* () {
for (const sourceFile of allSourceFilesIterable) {
if (filePathOrSearchFunction(sourceFile))
yield sourceFile;
}
}());
function getFilePathOrSearchFunction(fileSystemWrapper) {
if (fileNameOrSearchFunction instanceof Function)
return fileNameOrSearchFunction;
const fileNameOrPath = common.FileUtils.standardizeSlashes(fileNameOrSearchFunction);
if (common.FileUtils.pathIsAbsolute(fileNameOrPath) || fileNameOrPath.indexOf("/") >= 0)
return fileSystemWrapper.getStandardizedAbsolutePath(fileNameOrPath);
else
return def => common.FileUtils.pathEndsWith(def.fileName, fileNameOrPath);
}
function selectSmallestDirPathResult(results) {
let result;
for (const sourceFile of results) {
if (result == null || common.FileUtils.getDirPath(sourceFile.fileName).length < common.FileUtils.getDirPath(result.fileName).length)
result = sourceFile;
}
return result;
}
function isStandardizedFilePath(obj) {
return typeof obj === "string";
}
}
getSourceFiles() {
return Array.from(this.#sourceFileCache.getSourceFiles());
}
formatDiagnosticsWithColorAndContext(diagnostics, opts = {}) {
return common.ts.formatDiagnosticsWithColorAndContext(diagnostics, {
getCurrentDirectory: () => this.#fileSystemWrapper.getCurrentDirectory(),
getCanonicalFileName: fileName => fileName,
getNewLine: () => opts.newLineChar || common.runtime.getEndOfLine(),
});
}
getModuleResolutionHost() {
return common.createModuleResolutionHost({
transactionalFileSystem: this.#fileSystemWrapper,
getEncoding: () => this.compilerOptions.getEncoding(),
sourceFileContainer: this.#sourceFileCache,
});
}
};
})();
async function addSourceFilesForTsConfigResolver(project, tsConfigResolver, compilerOptions) {
const sourceFiles = [];
await Promise.all(tsConfigResolver.getPaths(compilerOptions).filePaths
.map(p => project.addSourceFileAtPath(p).then(s => sourceFiles.push(s))));
return sourceFiles;
}
function addSourceFilesForTsConfigResolverSync(project, tsConfigResolver, compilerOptions) {
return tsConfigResolver.getPaths(compilerOptions).filePaths.map(p => project.addSourceFileAtPathSync(p));
}
Object.defineProperty(exports, "CompilerOptionsContainer", {
enumerable: true,
get: function () { return common.CompilerOptionsContainer; }
});
Object.defineProperty(exports, "InMemoryFileSystemHost", {
enumerable: true,
get: function () { return common.InMemoryFileSystemHost; }
});
Object.defineProperty(exports, "ResolutionHosts", {
enumerable: true,
get: function () { return common.ResolutionHosts; }
});
Object.defineProperty(exports, "SettingsContainer", {
enumerable: true,
get: function () { return common.SettingsContainer; }
});
Object.defineProperty(exports, "ts", {
enumerable: true,
get: function () { return common.ts; }
});
exports.Project = Project;
exports.createProject = createProject;
exports.createProjectSync = createProjectSync;