@kui-shell/core
Version:
An Electron-based shell for cloud-native development
184 lines (183 loc) • 7.46 kB
JavaScript
;
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;