coc.nvim
Version:
LSP based intellisense engine for neovim & vim8.
159 lines • 7.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const child_process_1 = require("child_process");
const fs_1 = tslib_1.__importDefault(require("fs"));
const mkdirp_1 = tslib_1.__importDefault(require("mkdirp"));
const path_1 = tslib_1.__importDefault(require("path"));
const rimraf_1 = tslib_1.__importDefault(require("rimraf"));
const mv_1 = tslib_1.__importDefault(require("mv"));
const semver_1 = tslib_1.__importDefault(require("semver"));
const util_1 = require("util");
const workspace_1 = tslib_1.__importDefault(require("../workspace"));
const download_1 = tslib_1.__importDefault(require("./download"));
const fetch_1 = tslib_1.__importDefault(require("./fetch"));
const rc_1 = tslib_1.__importDefault(require("rc"));
const logger = require('../util/logger')('model-extension');
function registryUrl(scope = '') {
const result = rc_1.default('npm', { registry: 'https://registry.npmjs.org/' });
const url = result[`${scope}:registry`] || result.config_registry || result.registry;
return url.slice(-1) === '/' ? url : `${url}/`;
}
class ExtensionManager {
constructor(root) {
this.root = root;
this.checked = false;
}
checkFolder() {
if (this.checked)
return;
this.checked = true;
let { root } = this;
mkdirp_1.default.sync(root);
mkdirp_1.default.sync(path_1.default.join(root, 'node_modules/.cache'));
}
async getInfo(npm, name) {
if (name.startsWith('https:'))
return await this.getInfoFromUri(name);
let url = `${registryUrl()}/${name}`;
let res = await fetch_1.default(url);
let latest = res['versions'][res['dist-tags']['latest']];
return {
'dist.tarball': latest['dist']['tarball'],
'engines.coc': latest['engines'] && latest['engines']['coc'],
version: latest['version'],
name: res.name
};
}
async removeFolder(folder) {
if (fs_1.default.existsSync(folder)) {
let stat = await util_1.promisify(fs_1.default.lstat)(folder);
if (stat.isSymbolicLink()) {
await util_1.promisify(fs_1.default.unlink)(folder);
}
else {
await util_1.promisify(rimraf_1.default)(folder, { glob: false });
}
}
}
async _install(npm, def, info, onMessage) {
let tmpFolder = await util_1.promisify(fs_1.default.mkdtemp)(path_1.default.join(this.root, 'node_modules/.cache', `${info.name}-`));
let url = info['dist.tarball'];
onMessage(`Downloading from ${url}`);
await download_1.default(url, { dest: tmpFolder });
let content = await util_1.promisify(fs_1.default.readFile)(path_1.default.join(tmpFolder, 'package.json'), 'utf8');
let { dependencies } = JSON.parse(content);
if (dependencies && Object.keys(dependencies).length) {
onMessage(`Installing dependencies.`);
let p = new Promise((resolve, reject) => {
let args = ['install', '--ignore-scripts', '--no-lockfile', '--no-bin-links', '--production'];
const child = child_process_1.spawn(npm, args, { cwd: tmpFolder });
child.on('error', reject);
child.on('exit', resolve);
});
await p;
}
let jsonFile = path_1.default.join(this.root, 'package.json');
let obj = JSON.parse(fs_1.default.readFileSync(jsonFile, 'utf8'));
obj.dependencies = obj.dependencies || {};
if (/^https?:/.test(def)) {
obj.dependencies[info.name] = def;
}
else {
obj.dependencies[info.name] = '>=' + info.version;
}
fs_1.default.writeFileSync(jsonFile, JSON.stringify(obj, null, 2), { encoding: 'utf8' });
onMessage(`Moving to new folder.`);
let folder = path_1.default.join(this.root, 'node_modules', info.name);
await this.removeFolder(folder);
await util_1.promisify(mv_1.default)(tmpFolder, folder, { mkdirp: true });
}
async install(npm, def) {
this.checkFolder();
logger.info(`Using npm from: ${npm}`);
logger.info(`Loading info of ${def}.`);
let info = await this.getInfo(npm, def);
if (info.error) {
let { code, summary } = info.error;
let msg = code == 'E404' ? `module ${def} not exists!` : summary;
throw new Error(msg);
}
let { name } = info;
let required = info['engines.coc'] ? info['engines.coc'].replace(/^\^/, '>=') : '';
if (required && !semver_1.default.satisfies(workspace_1.default.version, required)) {
throw new Error(`${name} ${info.version} requires coc.nvim >= ${required}, please update coc.nvim.`);
}
await this._install(npm, def, info, msg => {
logger.info(msg);
});
workspace_1.default.showMessage(`Installed extension: ${name}`, 'more');
logger.info(`Installed extension: ${name}`);
return name;
}
async update(npm, name, uri) {
this.checkFolder();
let folder = path_1.default.join(this.root, 'node_modules', name);
let stat = await util_1.promisify(fs_1.default.lstat)(folder);
if (stat.isSymbolicLink()) {
logger.info(`skipped update of ${name}`);
return false;
}
let version;
if (fs_1.default.existsSync(path_1.default.join(folder, 'package.json'))) {
let content = await util_1.promisify(fs_1.default.readFile)(path_1.default.join(folder, 'package.json'), 'utf8');
version = JSON.parse(content).version;
}
logger.info(`Loading info of ${name}.`);
let info = await this.getInfo(npm, uri ? uri : name);
if (info.error)
return;
if (version && info.version && semver_1.default.gte(version, info.version)) {
logger.info(`Extension ${name} is up to date.`);
return false;
}
let required = info['engines.coc'] ? info['engines.coc'].replace(/^\^/, '>=') : '';
if (required && !semver_1.default.satisfies(workspace_1.default.version, required)) {
throw new Error(`${name} ${info.version} requires coc.nvim >= ${required}, please update coc.nvim.`);
}
await this._install(npm, uri ? uri : name, info, msg => { logger.info(msg); });
workspace_1.default.showMessage(`Updated extension: ${name} to ${info.version}`, 'more');
logger.info(`Update extension: ${name}`);
return true;
}
async getInfoFromUri(uri) {
if (uri.indexOf('github.com') == -1)
return;
uri = uri.replace(/\/$/, '');
let fileUrl = uri.replace('github.com', 'raw.githubusercontent.com') + '/master/package.json';
let content = await fetch_1.default(fileUrl);
let obj = typeof content == 'string' ? JSON.parse(content) : content;
return {
'dist.tarball': `${uri}/archive/master.tar.gz`,
'engines.coc': obj['engines'] ? obj['engines']['coc'] : undefined,
name: obj.name,
version: obj.version
};
}
}
exports.default = ExtensionManager;
//# sourceMappingURL=extension.js.map