UNPKG

@kui-shell/core

Version:

An Electron-based shell for cloud-native development

184 lines (183 loc) • 7.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeResolver = void 0; var _debug = _interopRequireDefault(require("debug")); var _path = require("./path"); var _commandTree = require("../core/command-tree"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* * Copyright 2017 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 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) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const debug = (0, _debug.default)('core/plugins/resolver'); /** export the prequire function */ const prequire = (route, prescan, registrar, options) => __awaiter(void 0, void 0, void 0, function* () { try { if (!Object.prototype.hasOwnProperty.call(registrar, route)) { // note how we stash a promise in the registrar immediately, to // avoid race conditions with multiple threads trying to prequire // the same plugin // eslint-disable-next-line no-async-promise-executor registrar[route] = new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () { const module = prescan.flat.find(_ => _.route === route); if (module) { try { // NOTE ON @kui-shell relativization: this is important so that // webpack can be instructed to pull in the plugins into the build // see the corresponding NOTE in ./assembler.ts and ./preloader.ts const registrationRef = module.path.charAt(0) === '/' ? yield Promise.resolve().then(() => require(`${/* webpackIgnore: true */module.path}`)) : module.route === 'client' ? yield Promise.resolve().then(() => require(`${'@kui-shell/clien' + (0, _path.webpackPath)(module.route).slice(5) + '/mdist/plugin'}`)) : yield Promise.resolve().then(() => require(`${'@kui-shell/plugin-' + (0, _path.webpackPath)(module.route) + '/mdist/plugin'}`)); const registration = registrationRef.default || registrationRef; const combinedOptions = Object.assign({ usage: prescan.usage, docs: prescan.docs }, options); resolve(registration((0, _commandTree.proxy)(route), combinedOptions)); } catch (err) { console.error(`prequire error ${route}`, err); reject(err); } } else { reject(new Error(`Internal error: plugin ${route} not found`)); } })); } } catch (err) { debug('prequire failure', route); console.error(err); } return registrar[route]; }); /** * Make a plugin resolver from a given prescan model * */ const makeResolver = (prescan, registrar) => { /** memoize resolved plugins */ const isResolved = {}; /** resolve one given plugin */ const resolveOne = plugin => __awaiter(void 0, void 0, void 0, function* () { try { if (!plugin) { return; } else if (!isResolved[plugin]) { // eslint-disable-next-line no-async-promise-executor isResolved[plugin] = new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () { try { const prereqs = prescan.topological[plugin]; if (prereqs) { yield Promise.all(prereqs.map(route => prequire(route, prescan, registrar))); } const loadedPlugin = prequire(plugin, prescan, registrar); resolve(loadedPlugin); } catch (err) { console.error(`Error resolving plugin ${plugin}`, err); reject(err); } })); } // NOTE: even if isResolved[plugin] already has an entry, we may // need to wait for it to complete, if there is a race between // two threads trying to load the plugin return isResolved[plugin]; } finally { // debug('resolveOne done', plugin) } }); /* resolveOne */ /** a plugin resolver impl */ const resolver = { isOverridden: route => prescan.overrides[route] !== undefined, isAlpha: (route, plugin) => prescan.overrides[route] === undefined || prescan.overrides[route] === plugin, resolveOne, disambiguate: command => { return prescan.disambiguator[command]; }, /** given a partial command, do we have a disambiguation of it? e.g. "gr" => "grid" */ disambiguatePartial: partial => { const matches = []; if (prescan.disambiguator) { for (const command in prescan.disambiguator) { if (command.indexOf(partial) === 0) { // const { route, plugin } = prescan.disambiguator[command] matches.push(command); } } } return matches; }, /** load any plugins required by the given command */ resolve: (command, { subtree = false, tryCatchalls = true } = {}) => __awaiter(void 0, void 0, void 0, function* () { // subpath if we are looking for plugins for a subtree, e.g. for cd /auth let plugin; let matchLen; for (const route in prescan.commandToPlugin) { if (route === command) { plugin = prescan.commandToPlugin[route]; break; } else if (subtree ? route.indexOf(command) === 0 : command.indexOf(`${route}/`) === 0) { if (!matchLen || route.length > matchLen) { plugin = prescan.commandToPlugin[route]; matchLen = route.length; } } } if (plugin) { yield resolveOne(plugin); } else if (prescan.catchalls.length > 0 && tryCatchalls) { // see if we have catchall yield Promise.all(prescan.catchalls.map(_ => resolveOne(_.plugin))).catch(err => { console.error('There seems to be an inconsistency in the prescan model versus the current state of the filesystem: the prescan model refers to a catchall that cannot currently be found', err); // Note: we hope for the best, and intentionally don't // rethrow the error. the upstream, in plugin.ts where it // calls resolver.resolve(), will handle the catastrophe // (which, at worst, will be a "command not found"), if it // ensues }); } }) }; /* resolver */ return resolver; }; exports.makeResolver = makeResolver;