UNPKG

@kui-shell/core

Version:

An Electron-based shell for cloud-native development

227 lines (226 loc) • 9.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compile = void 0; exports.compileUserInstalled = compileUserInstalled; exports.default = void 0; var _debug = _interopRequireDefault(require("debug")); var _path = require("path"); var _fsExtra = require("fs-extra"); var plugins = _interopRequireWildcard(require("./plugins")); var _scanner = require("./scanner"); var _events = _interopRequireDefault(require("../core/events")); var _tree = require("../commands/tree"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 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/assembler'); /** * Return the location of the serialized `PrescanModel` that * represents the model that ships with a client (as opposed to the * model that represents user-installed plugins) * */ const prescanned = () => require.resolve('@kui-shell/prescan'); /** * Serialize a `PrescanModel` to disk * */ const writePrescanned = (modules, destFile = prescanned()) => __awaiter(void 0, void 0, void 0, function* () { debug('writePrescanned', process.cwd(), destFile, modules); const str = JSON.stringify(modules); yield (0, _fsExtra.ensureFile)(destFile); yield (0, _fsExtra.writeFile)(destFile, str); }); /** * Make a tree out of a flat map. * e.g. take "/wsk" and "/wsk/actions" and make a tree out of that flat * structure based on the "/path/hierarchy" * */ const makeTree = (map, docs) => { const keys = Object.keys(map); if (keys.length === 0) { debug('interesting, not a single command registered a usage model'); // this isn't the end of the world, but probably a sign of // incomplete plugin design; so let's warn the developer (note // that this command is executed as part of plugin precompilation // so the user in this case is the plugin developer) console.error('Warning: none of your commands registered a usage model'); return {}; } // sort the keys lexicographically keys.sort(); /** create new node */ const newLeaf = route => ({ route }); const newNode = route => Object.assign(newLeaf(route), { children: {} }); /** get or create a subtree */ const getOrCreate = (tree, pathPrefix) => { if (!tree.children) { tree.children = {}; } const entry = tree.children[pathPrefix]; if (!entry) { tree.children[pathPrefix] = newNode(pathPrefix); return tree.children[pathPrefix]; } else { return entry; } }; const tree = keys.reduce((tree, route) => { const split = route.split(/\//); let subtree = tree; for (let idx = 0; idx < split.length; idx++) { const pathPrefix = split.slice(0, idx).join('/'); subtree = getOrCreate(subtree, pathPrefix); } if (!subtree.children) subtree.children = {}; const leaf = subtree.children[route] = newLeaf(route); leaf.usage = map[route]; leaf.docs = leaf.usage && leaf.usage.header || docs[route]; return tree; }, newNode('/')); return tree.children[''].children[''].children; }; /** * Scan the registered commands for usage docs, so that we can stash * them away in the compiled plugin registry. This will allow us to * present docs in a general way, not only in response to evaluation * of commands. * */ const amendWithUsageModels = modules => { modules.docs = {}; modules.usage = {}; (0, _tree.getModel)().forEachNode(({ route, options, synonyms }) => { if (options && options.usage) { modules.usage[route] = Object.assign({ route }, options.usage); if (options.needsUI) modules.usage[route].needsUI = true; if (options.requiresLocal) modules.usage[route].requiresLocal = true; // if (options.noAuthOk) modules.usage[route].noAuthOk = true if (options.synonymFor) modules.usage[route].synonymFor = options.synonymFor.route; if (synonyms) modules.usage[route].synonyms = Object.keys(synonyms).map(route => synonyms[route].key); if (options.usage.docs) { modules.docs[route] = options.usage.docs; } } if (options && options.docs) { modules.docs[route] = options.docs; } }); // modules.usage right not is flat, i.e. it may contain entries // for "/wsk" and "/wsk/actions"; make a tree out of that flat // structure based on the "/path/hierarchy" modules.usage = makeTree(modules.usage, modules.docs); return modules; }; /** * Assemble the plugin `PrescanModel`, and serialize it to disk. This * code is called both for assembling the model that is shipped with a * client, and (via `compileUserInstalled`, below) for assembling, on * the fly, the registry for user-installed plugins. * */ const compile = (pluginRoot = process.env.PLUGIN_ROOT || (0, _path.join)(__dirname, plugins.pluginRoot), externalOnly = false) => __awaiter(void 0, void 0, void 0, function* () { debug('pluginRoot is %s', pluginRoot); debug('externalOnly is %s', externalOnly); (0, _tree.initIfNeeded)(); const modules = yield (0, _scanner.assemble)(plugins.registrar, { externalOnly, pluginRoot }); debug('modules', modules); /** make the paths relative to the root directory */ const fixupOnePath = filepath => { // NOTE ON relativization: this is important so that webpack can // be instructed to pull in the plugins into the build see the // corresponding NOTE in ./plugins.ts and ./preloader.ts const pattern = new RegExp(`^(.*\\${_path.sep})(client.*|plugin-.*)$`); const fixed = externalOnly ? filepath : (0, _path.relative)(pluginRoot, filepath).replace(pattern, '$2'); return fixed; }; const fixupPaths = pluginList => pluginList.map(plugin => Object.assign(plugin, { path: fixupOnePath(plugin.path) })); const model = Object.assign(modules, { preloads: fixupPaths(modules.preloads), flat: fixupPaths(modules.flat) }); const modelWithUsage = amendWithUsageModels(model); const destFile = process.env.KUI_PRESCAN || (externalOnly ? (0, _path.join)(pluginRoot, '@kui-shell/prescan.json') : prescanned()); yield Promise.all([writePrescanned(modelWithUsage, destFile)]); if (externalOnly) { // then this is a live install, we need to smash it into the live model plugins._useUpdatedUserPrescan(modelWithUsage); } }); /** * The entry point for recompiling the user-installed plugin registry * */ exports.compile = compile; function compileUserInstalled(pluginToBeRemoved) { return __awaiter(this, void 0, void 0, function* () { debug('compileUserInstalled', pluginToBeRemoved); const home = yield plugins.userInstalledHome(); const pluginRoot = (0, _path.join)(home, 'node_modules'); yield compile(pluginRoot, true); _events.default.emit('/plugin/compile/done'); }); } var _default = compile; exports.default = _default; debug('loading done');