123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const tslib_1 = require("tslib");
- const fs = require("fs");
- const path = require("path");
- const chalk_1 = require("chalk");
- const guards_1 = require("../guards");
- const errors_1 = require("./errors");
- const fs_1 = require("@ionic/cli-framework/utils/fs");
- const http_1 = require("./utils/http");
- const npm_1 = require("./utils/npm");
- exports.ERROR_PLUGIN_NOT_INSTALLED = 'PLUGIN_NOT_INSTALLED';
- exports.ERROR_PLUGIN_NOT_FOUND = 'PLUGIN_NOT_FOUND';
- exports.ERROR_PLUGIN_INVALID = 'PLUGIN_INVALID';
- exports.KNOWN_PLUGINS = ['proxy'];
- const ORG_PREFIX = '@ionic';
- const PLUGIN_PREFIX = 'cli-plugin-';
- function formatFullPluginName(name) {
- return `${ORG_PREFIX}/${PLUGIN_PREFIX}${name}`;
- }
- exports.formatFullPluginName = formatFullPluginName;
- function promptToInstallPlugin(env, pluginName, { message, global = false, reinstall = false }) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- if (!global && !env.project.directory) {
- return;
- }
- try {
- return yield loadPlugin(env, pluginName, {
- askToInstall: true,
- global,
- reinstall,
- message,
- });
- }
- catch (e) {
- if (e !== exports.ERROR_PLUGIN_NOT_INSTALLED) {
- throw e;
- }
- }
- });
- }
- exports.promptToInstallPlugin = promptToInstallPlugin;
- function registerPlugin(env, plugin) {
- if (plugin.registerHooks) {
- plugin.registerHooks(env.hooks);
- }
- env.plugins[plugin.meta.name] = plugin;
- }
- exports.registerPlugin = registerPlugin;
- function unregisterPlugin(env, plugin) {
- env.hooks.deleteSource(plugin.meta.name);
- delete env.plugins[plugin.meta.name];
- }
- exports.unregisterPlugin = unregisterPlugin;
- function loadPlugins(env) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const global = !env.meta.local;
- const modulesDir = path.resolve(global ? path.dirname(path.dirname(path.dirname(env.meta.libPath))) : path.join(env.project.directory, 'node_modules'));
- const pluginPkgs = yield Promise.all(exports.KNOWN_PLUGINS
- .map(formatFullPluginName)
- .map((pkgName) => tslib_1.__awaiter(this, void 0, void 0, function* () {
- const pluginPath = path.resolve(modulesDir, path.normalize(pkgName));
- const exists = yield fs_1.pathExists(pluginPath);
- return [pkgName, exists];
- })));
- const [, proxyVar] = http_1.getGlobalProxy();
- if (proxyVar) {
- const proxyPluginPkg = formatFullPluginName('proxy');
- env.log.debug(() => `Detected ${chalk_1.default.green(proxyVar)} in environment`);
- if (!pluginPkgs.find(v => v[0] === proxyPluginPkg && v[1])) {
- const canInstall = yield fs_1.pathAccessible(env.plugins.ionic.meta.filePath, fs.constants.W_OK);
- const proxyInstallArgs = yield npm_1.pkgManagerArgs(env, { pkg: proxyPluginPkg, global });
- const installMsg = `Detected ${chalk_1.default.green(proxyVar)} in environment, but to proxy CLI requests, you'll need ${chalk_1.default.cyan(proxyPluginPkg)} installed.`;
- if (canInstall) {
- yield promptToInstallPlugin(env, proxyPluginPkg, {
- message: `${installMsg} Install now?`,
- reinstall: true,
- global,
- });
- }
- else {
- env.log.warn(`${installMsg}\nYou can install it manually:\n\n${chalk_1.default.green(proxyInstallArgs.join(' '))}\n`);
- }
- }
- }
- const pluginPromises = pluginPkgs.map((pkg) => tslib_1.__awaiter(this, void 0, void 0, function* () {
- const [pkgName, exists] = pkg;
- if (exists) {
- try {
- return yield loadPlugin(env, pkgName, { askToInstall: false, global });
- }
- catch (e) {
- if (e !== exports.ERROR_PLUGIN_INVALID) {
- throw e;
- }
- }
- }
- }));
- for (let p of pluginPromises) {
- const plugin = yield p;
- if (plugin) {
- registerPlugin(env, plugin);
- }
- }
- });
- }
- exports.loadPlugins = loadPlugins;
- function loadPlugin(env, pluginName, { message, askToInstall = true, reinstall = false, global = false }) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const config = yield env.config.load();
- const modulesDir = path.resolve(global ? path.dirname(path.dirname(path.dirname(env.meta.libPath))) : path.join(env.project.directory, 'node_modules'));
- let mResolvedPath;
- let m;
- if (!message) {
- message = `The plugin ${chalk_1.default.cyan(pluginName)} is not installed. Would you like to install it and continue?`;
- }
- env.log.debug(() => `Loading ${global ? 'global' : 'local'} plugin ${chalk_1.default.bold(pluginName)}`);
- try {
- mResolvedPath = require.resolve(path.resolve(modulesDir, pluginName));
- delete require.cache[mResolvedPath];
- m = require(mResolvedPath);
- }
- catch (e) {
- if (e.code !== 'MODULE_NOT_FOUND') {
- throw e;
- }
- if (!askToInstall) {
- env.log.debug(() => `${chalk_1.default.red(exports.ERROR_PLUGIN_NOT_INSTALLED)}: ${global ? 'global' : 'local'} ${chalk_1.default.bold(pluginName)}`);
- throw exports.ERROR_PLUGIN_NOT_INSTALLED;
- }
- }
- if (!m || reinstall) {
- const confirm = yield env.prompt({
- type: 'confirm',
- name: 'confirm',
- message,
- });
- if (confirm) {
- const [installer, ...installerArgs] = yield pkgInstallPluginArgs(env, pluginName, { global });
- yield env.shell.run(installer, installerArgs, {});
- m = yield loadPlugin(env, pluginName, { askToInstall: false, global });
- mResolvedPath = require.resolve(path.resolve(modulesDir, pluginName));
- }
- else {
- config.state.lastNoResponseToUpdate = new Date().toISOString();
- throw exports.ERROR_PLUGIN_NOT_INSTALLED;
- }
- }
- if (m.version || !guards_1.isPlugin(m) || !mResolvedPath) {
- env.log.debug(() => `${chalk_1.default.red(exports.ERROR_PLUGIN_INVALID)}: ${global ? 'global' : 'local'} ${chalk_1.default.bold(pluginName)}`);
- throw exports.ERROR_PLUGIN_INVALID;
- }
- const meta = yield getPluginMeta(mResolvedPath);
- m.meta = meta;
- if (config.daemon.updates) {
- const latestVersion = yield getLatestPluginVersion(env, meta.name, meta.version);
- env.log.debug(() => `Latest plugin version of ${chalk_1.default.bold(meta.name + (meta.distTag === 'latest' ? '' : '@' + meta.distTag))} is ${chalk_1.default.bold(latestVersion || 'unknown')}, according to daemon file.`);
- m.meta.latestVersion = latestVersion;
- m.meta.updateAvailable = latestVersion ? yield versionNeedsUpdating(meta.version, latestVersion) : undefined;
- }
- return m;
- });
- }
- exports.loadPlugin = loadPlugin;
- function getPluginMeta(p) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const packageJson = yield npm_1.readPackageJsonFileOfResolvedModule(p);
- const name = packageJson.name;
- const version = packageJson.version || '';
- const distTag = determineDistTag(version);
- return {
- distTag,
- filePath: p,
- name,
- version,
- };
- });
- }
- exports.getPluginMeta = getPluginMeta;
- function versionNeedsUpdating(version, latestVersion) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const semver = yield Promise.resolve().then(() => require('semver'));
- const distTag = determineDistTag(version);
- return semver.gt(latestVersion, version) || (['canary', 'testing'].includes(distTag) && latestVersion !== version);
- });
- }
- exports.versionNeedsUpdating = versionNeedsUpdating;
- function facilitateIonicUpdate(env, ionicPlugin, latestVersion) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const config = yield env.config.load();
- const global = !env.meta.local;
- const ionicInstallArgs = yield pkgInstallPluginArgs(env, 'ionic', { global });
- const updateMsg = `The Ionic CLI ${global ? '' : '(local version) '}has an update available (${chalk_1.default.cyan(ionicPlugin.meta.version)} => ${chalk_1.default.cyan(latestVersion)})!`;
- const canInstall = global ? yield fs_1.pathAccessible(ionicPlugin.meta.filePath, fs.constants.W_OK) : true;
- if (canInstall) {
- const confirm = yield env.prompt({
- name: 'confirm',
- type: 'confirm',
- message: `${updateMsg} Would you like to install it?`,
- });
- if (confirm) {
- const [installer, ...installerArgs] = ionicInstallArgs;
- yield env.shell.run(installer, installerArgs, {});
- const revertArgs = yield npm_1.pkgManagerArgs(env, { pkg: `ionic@${ionicPlugin.meta.version}`, global });
- env.log.nl();
- env.log.ok(`Updated Ionic CLI to ${chalk_1.default.bold(latestVersion)}! 🎉`);
- env.log.nl();
- env.log.msg(chalk_1.default.bold('Please re-run your command.'));
- env.log.nl();
- throw new errors_1.FatalException(`${chalk_1.default.bold('Note')}: You can downgrade to your old version by running: ${chalk_1.default.green(revertArgs.join(' '))}`, 0);
- }
- else {
- config.state.lastNoResponseToUpdate = new Date().toISOString();
- env.log.info(`Not automatically updating your CLI.`);
- }
- }
- else {
- env.log.info(updateMsg);
- env.log.nl();
- env.log.warn(`No write permissions for ${global ? 'global' : 'local'} ${chalk_1.default.bold('node_modules')}--automatic CLI updates are disabled.\n` +
- `To fix, see ${chalk_1.default.bold('https://docs.npmjs.com/getting-started/fixing-npm-permissions')}\n\n` +
- `Or, install the CLI update manually:\n\n${chalk_1.default.green(ionicInstallArgs.join(' '))}\n`);
- }
- });
- }
- function facilitatePluginUpdate(env, ionicPlugin, plugin, latestVersion) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const global = !env.meta.local;
- const startMsg = `${global ? 'Global' : 'Local'} plugin ${chalk_1.default.cyan(plugin.meta.name)}`;
- const updateMsg = `${startMsg} has an update available (${chalk_1.default.cyan(plugin.meta.version)} => ${chalk_1.default.cyan(latestVersion)})!`;
- const canInstall = global ? yield fs_1.pathAccessible(plugin.meta.filePath, fs.constants.W_OK) : true;
- if (canInstall) {
- const message = ionicPlugin.meta.distTag === plugin.meta.distTag ?
- `${updateMsg} Would you like to install it?` :
- `${startMsg} has a different dist-tag (${chalk_1.default.cyan('@' + plugin.meta.distTag)}) than the Ionic CLI (${chalk_1.default.cyan('@' + ionicPlugin.meta.distTag)}). Would you like to install the appropriate plugin version?`;
- const okmessage = ionicPlugin.meta.distTag === plugin.meta.distTag ?
- `Updated ${chalk_1.default.bold(plugin.meta.name)} to ${chalk_1.default.bold(latestVersion)}! 🎉` :
- `Installed ${chalk_1.default.bold(plugin.meta.name + '@' + ionicPlugin.meta.distTag)}`;
- const p = yield promptToInstallPlugin(env, plugin.meta.name, {
- message,
- reinstall: true,
- global,
- });
- if (p) {
- unregisterPlugin(env, plugin);
- registerPlugin(env, p);
- env.log.ok(okmessage);
- return true;
- }
- env.log.info(`Not automatically updating ${chalk_1.default.bold(plugin.meta.name)}.`);
- }
- else {
- env.log.info(updateMsg);
- env.log.nl();
- env.log.warn(`No write permissions for ${global ? 'global' : 'local'}${chalk_1.default.bold('node_modules')}--automatic plugin updates are disabled.\n` +
- `To fix, see ${chalk_1.default.bold('https://docs.npmjs.com/getting-started/fixing-npm-permissions')}\n`);
- }
- return false;
- });
- }
- function checkForUpdates(env) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const [config,] = yield Promise.all([env.config.load(), env.daemon.load()]);
- if (!config.daemon.updates) {
- return [];
- }
- yield env.daemon.save();
- if (env.plugins.ionic.meta.updateAvailable && env.plugins.ionic.meta.latestVersion) {
- yield facilitateIonicUpdate(env, env.plugins.ionic, env.plugins.ionic.meta.latestVersion);
- }
- const values = yield Promise.resolve().then(() => require('lodash/values'));
- const plugins = values(env.plugins).filter(p => p !== env.plugins.ionic);
- const updates = [];
- for (let plugin of plugins) {
- // TODO: differing dist-tags?
- if ((yield env.config.isUpdatingEnabled()) && plugin.meta.updateAvailable && plugin.meta.latestVersion) {
- const installed = yield facilitatePluginUpdate(env, env.plugins.ionic, plugin, plugin.meta.latestVersion);
- if (installed) {
- updates.push(plugin.meta.name);
- }
- }
- }
- if (updates.length > 0) {
- const [installer, ...dedupeArgs] = yield npm_1.pkgManagerArgs(env, { command: 'dedupe' });
- if (dedupeArgs.length > 0) {
- try {
- yield env.shell.run(installer, dedupeArgs, { fatalOnError: false });
- }
- catch (e) {
- env.log.warn('Error while deduping npm dependencies. Attempting to continue...');
- }
- }
- }
- return updates;
- });
- }
- exports.checkForUpdates = checkForUpdates;
- function getLatestPluginVersion(env, name, version) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const daemon = yield env.daemon.load();
- const distTag = determineDistTag(version);
- if (typeof daemon.latestVersions[distTag] === 'object') {
- if (daemon.latestVersions[distTag][name]) {
- const version = daemon.latestVersions[distTag][name];
- return version;
- }
- }
- else {
- env.daemon.populateDistTag(distTag);
- }
- });
- }
- exports.getLatestPluginVersion = getLatestPluginVersion;
- function pkgInstallPluginArgs(env, name, options = {}) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const releaseChannelName = determineDistTag(env.plugins.ionic.meta.version);
- let pluginInstallVersion = `${name}@${releaseChannelName}`;
- options.pkg = pluginInstallVersion;
- options.saveDev = true;
- return npm_1.pkgManagerArgs(env, options);
- });
- }
- exports.pkgInstallPluginArgs = pkgInstallPluginArgs;
- function determineDistTag(version) {
- if (version.includes('-alpha')) {
- return 'canary';
- }
- if (version.includes('-testing')) {
- return 'testing';
- }
- return 'latest';
- }
- exports.determineDistTag = determineDistTag;
- function detectAndWarnAboutDeprecatedPlugin(env, plugin) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const packageJson = yield env.project.loadPackageJson();
- if (packageJson.devDependencies && packageJson.devDependencies[plugin]) {
- const { pkgManagerArgs } = yield Promise.resolve().then(() => require('../lib/utils/npm'));
- const args = yield pkgManagerArgs(env, { pkg: plugin, command: 'uninstall', saveDev: true });
- env.log.warn(`Detected ${chalk_1.default.bold(plugin)} in your ${chalk_1.default.bold('package.json')}.\n` +
- `As of CLI 3.8, it is no longer needed. You can uninstall it:\n\n${chalk_1.default.green(args.join(' '))}\n`);
- }
- });
- }
- exports.detectAndWarnAboutDeprecatedPlugin = detectAndWarnAboutDeprecatedPlugin;
|