@anysphere/scip-typescript
Version:
SCIP indexer for TypeScript and JavaScript
194 lines (193 loc) • 8.29 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectIndexer = void 0;
const path = __importStar(require("path"));
const pretty_ms_1 = __importDefault(require("pretty-ms"));
const progress_1 = __importDefault(require("progress"));
const ts = __importStar(require("typescript"));
const FileIndexer_1 = require("./FileIndexer");
const Input_1 = require("./Input");
const Packages_1 = require("./Packages");
const scip = __importStar(require("./scip"));
function createCompilerHost(cache, compilerOptions, projectOptions) {
const host = ts.createCompilerHost(compilerOptions);
if (!projectOptions.globalCaches) {
return host;
}
const hostCopy = { ...host };
host.getParsedCommandLine = (fileName) => {
if (!hostCopy.getParsedCommandLine) {
return undefined;
}
const fromCache = cache.parsedCommandLines.get(fileName);
if (fromCache !== undefined) {
return fromCache;
}
const result = hostCopy.getParsedCommandLine(fileName);
if (result !== undefined) {
// Don't cache undefined results even if they could be cached
// theoretically. The big performance gains from this cache come from
// caching non-undefined results.
cache.parsedCommandLines.set(fileName, result);
}
return result;
};
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
const fromCache = cache.sources.get(fileName);
if (fromCache !== undefined) {
const [sourceFile, cachedLanguageVersion] = fromCache;
if (isSameLanguageVersion(languageVersion, cachedLanguageVersion)) {
return sourceFile;
}
}
const result = hostCopy.getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
if (result !== undefined) {
// Don't cache undefined results even if they could be cached
// theoretically. The big performance gains from this cache come from
// caching non-undefined results.
cache.sources.set(fileName, [result, languageVersion]);
}
return result;
};
return host;
}
class ProjectIndexer {
constructor(config, options, ignoeMap, cache) {
this.config = config;
this.options = options;
this.ignoeMap = ignoeMap;
this.symbolCache = new Map();
this.hasConstructor = new Map();
const host = createCompilerHost(cache, config.options, options);
this.program = ts.createProgram(config.fileNames, config.options, host);
this.checker = this.program.getTypeChecker();
this.packages = new Packages_1.Packages(options.projectRoot);
}
index() {
const startTimestamp = Date.now();
const sourceFiles = this.program.getSourceFiles();
const filesToIndex = [];
console.log(`+ cwd ${this.options.cwd}`);
// Visit every sourceFile in the program
for (const sourceFile of sourceFiles) {
const includes = this.config.fileNames.includes(sourceFile.fileName);
if (!includes) {
continue;
}
filesToIndex.push(sourceFile);
}
if (filesToIndex.length === 0) {
throw new Error(`no indexable files in project '${this.options.projectDisplayName}'`);
}
const jobs = this.options.progressBar
? new progress_1.default(` ${this.options.projectDisplayName} [:bar] :current/:total :title`, {
total: filesToIndex.length,
renderThrottle: 100,
incomplete: '_',
complete: '#',
width: 20,
clear: true,
stream: process.stderr,
})
: undefined;
let lastWrite = startTimestamp;
for (const [index, sourceFile] of filesToIndex.entries()) {
const title = path.relative(this.options.cwd, sourceFile.fileName);
// check gitignore
if (this.options.gitignore) {
let pIgnores = [];
for (const [key, value] of this.ignoeMap.entries()) {
if (sourceFile.fileName.startsWith(key)) {
pIgnores.push(value);
}
}
let doIgnore = false;
for (const ignore of pIgnores) {
if (ignore.ignores(title)) {
doIgnore = true;
break;
}
}
if (doIgnore) {
continue;
}
}
jobs === null || jobs === void 0 ? void 0 : jobs.tick({ title });
if (!this.options.progressBar) {
const now = Date.now();
const elapsed = now - lastWrite;
if (elapsed > 1000 && index > 2) {
lastWrite = now;
process.stdout.write('.');
}
}
const document = new scip.scip.Document({
relative_path: path.relative(this.options.cwd, sourceFile.fileName),
occurrences: [],
});
const input = new Input_1.Input(sourceFile.fileName, sourceFile.getText());
const visitor = new FileIndexer_1.FileIndexer(this.checker, this.options, input, document, this.symbolCache, this.hasConstructor, this.packages, sourceFile);
try {
visitor.index();
}
catch (error) {
console.error(`unexpected error indexing project root '${this.options.cwd}'`, error);
}
if (visitor.document.occurrences.length > 0) {
this.options.writeIndex(new scip.scip.Index({
documents: [visitor.document],
}));
}
}
jobs === null || jobs === void 0 ? void 0 : jobs.terminate();
const elapsed = Date.now() - startTimestamp;
if (!this.options.progressBar && lastWrite > startTimestamp) {
process.stdout.write('\n');
}
console.log(`+ ${this.options.projectDisplayName} (${(0, pretty_ms_1.default)(elapsed)})`);
}
}
exports.ProjectIndexer = ProjectIndexer;
function isSameLanguageVersion(a, b) {
if (typeof a === 'number' && typeof b === 'number') {
return a === b;
}
if (typeof a === 'number' || typeof b === 'number') {
// Different shape: one is ts.ScriptTarget, the other is
// ts.CreateSourceFileOptions
return false;
}
return (a.languageVersion === b.languageVersion &&
a.impliedNodeFormat === b.impliedNodeFormat
// Ignore setExternalModuleIndicator even if that increases the risk of a
// false positive. A local experiment revealed that we never get a cache hit
// if we compare setExternalModuleIndicator since it's function with a
// unique reference on every `CompilerHost.getSourceFile` callback.
);
}