@foxpage/foxpage-manager
Version:
foxpage resource manager
403 lines (402 loc) • 13.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CSRPluginManagerImpl = void 0;
const foxpage_shared_1 = require("@foxpage/foxpage-shared");
const common_1 = require("../common");
const data_service_1 = require("../data-service");
const plugin_1 = require("./plugin");
/**
* plugin manager for csr
*
* @export
* @class CSRPluginManagerImpl
* @implements {CSRPluginManager}
*/
class CSRPluginManagerImpl extends common_1.ManagerBaseImpl {
constructor(app) {
super(app, { type: 'plugin', diskCache: { enable: true }, lruCache: { cloned: false } });
/**
* cache plugin name & versions
* key: plugin name, value: version list
* @private
*/
this.pluginVersionsMap = new Map();
/**
* plugin id map
* key: plugin id, value: plugin name
*
* @private
*/
this.pluginIdMap = new Map();
this.generateKey = (name, version) => {
return foxpage_shared_1.packager.generateKey(name, version);
};
this.splitKey = (key) => {
return foxpage_shared_1.packager.splitKey(key);
};
this.logger = (0, foxpage_shared_1.createLogger)('pluginManager');
this.resourceConfig = app.resourceMap;
this.config = app.configs.package || {};
}
/**
* add plugin to manager
*
* @param {PrePackage} pkg
*/
addPlugin(pkg) {
const { id, name, version } = pkg;
this.logger.info(`add plugin ${name}@${version}`);
if (!version) {
this.logger.warn('plugin version is invalid');
return null;
}
const newPkg = this.newPlugin(pkg);
this.addOne(newPkg.key, pkg, newPkg);
this.updatePluginVersionsMap(pkg);
this.updatePluginLiveVersion(pkg);
this.pluginIdMap.set(id, name);
return newPkg;
}
/**
* remove plugin from manager via plugin names
*
* @param {string[]} names plugin names
*/
removePlugins(names) {
const versionInfo = this.getPluginVersionsByNames(names);
Object.keys(versionInfo).forEach(name => {
var _a;
const versions = versionInfo[name];
if (versions) {
this.remove(versions.map(item => this.generateKey(name, item)));
}
const pluginId = (_a = this.pluginVersionsMap.get(name)) === null || _a === void 0 ? void 0 : _a.id;
if (pluginId) {
this.pluginIdMap.delete(pluginId);
}
this.pluginVersionsMap.delete(name);
});
}
/**
* get plugin by plugin name
* will return the live versions
* @param {string} type
* @return {*} {PrePackage | undefined}
*/
async getPlugin(name) {
return (await this.getPlugins([name]))[0];
}
/**
* get all libraries by plugin names
* will return the live versions
* @param {string[]} names
* @return {*} {Promise<PrePackage[]>}
*/
async getPlugins(names) {
const keys = names.map(name => {
const liveVersion = this.getPluginLiveVersion(name);
return this.generateKey(name, liveVersion || '');
});
return await this.find(keys);
}
async getPluginsById(ids) {
const keys = ids
.map(id => {
const name = this.pluginIdMap.get(id);
if (name) {
const liveVersion = this.getPluginLiveVersion(name);
return this.generateKey(name, liveVersion || '');
}
})
.filter(foxpage_shared_1.isNotNill);
return await this.find(keys);
}
/**
* select one version
*
* @param {string} name plugin name
* @return {*}
*/
getPluginLiveVersion(name) {
var _a;
return (_a = this.pluginVersionsMap.get(name)) === null || _a === void 0 ? void 0 : _a.liveVersion;
}
/**
* update plugin live version
*
* @param {string} pkg plugin content
*/
updatePluginLiveVersion(pkg) {
if (pkg.isLive) {
const pkgInfo = this.pluginVersionsMap.get(pkg.name);
if (pkgInfo) {
pkgInfo.liveVersion = pkg.version;
}
}
}
/**
* get plugin sync from hot
* @param name plugin name
* @param version plugin version
* @returns pkg
*/
getPluginSync(name, version) {
const versions = this.getPluginVersionsByNames([name])[name];
if (!versions) {
return null;
}
if (version && versions.indexOf(version) === -1) {
this.logger.warn(`get local plugin: ${name}@${version} is empty.`);
return null;
}
// !version use live version, or use appoint version
let curVersion = this.getPluginLiveVersion(name);
if (version && version !== curVersion) {
this.logger.info(`plugin ${name} local live version is: ${curVersion}, appoint version is: ${version}`);
curVersion = version;
}
if (!curVersion) {
this.logger.warn(`plugin ${name} version is invalid`);
return null;
}
const key = this.generateKey(name, curVersion);
const pkg = this.findOne(key);
if (!pkg || pkg.version !== curVersion) {
this.logger.warn(`get local plugin: ${key} is empty.`);
return null;
}
// this.logger.info(`get local plugin: ${key} succeed.`);
return pkg;
}
/**
* get local plugin
*
* @param {string} name plugin name
* @param {string} version plugin version
* @return {*} {(PrePackage | null)}
*/
async getLocalPlugin(name, version) {
const versions = this.getPluginVersionsByNames([name])[name];
if (!versions) {
return null;
}
if (version && versions.indexOf(version) === -1) {
this.logger.warn(`get local plugin: ${name}@${version} is empty.`);
return null;
}
// !version use live version, or use appoint version
let curVersion = this.getPluginLiveVersion(name);
if (version && version !== curVersion) {
this.logger.info(`plugin ${name} local live version is: ${curVersion}, appoint version is: ${version}`);
curVersion = version;
}
if (!curVersion) {
this.logger.warn(`plugin ${name} version is invalid`);
return null;
}
const key = this.generateKey(name, curVersion);
const pkg = await this.findOneFromLocal(key);
if (!pkg || pkg.version !== curVersion) {
this.logger.warn(`get local plugin: ${key} is empty.`);
return null;
}
// this.logger.info(`get local plugin: ${key} succeed.`);
return pkg;
}
/**
* fetch libraries, instance libraries and install libraries then add to local
* note: return contains plugin dependencies contents
* @param {PackageFreshOption} [params]
* @return {*}
*/
async freshPlugins(params) {
const libraries = (params === null || params === void 0 ? void 0 : params.namedVersions)
? this.resolvePlugin(await this.fetchPluginsByNamedVersions(params.namedVersions))
: await this.fetchPlugins(params === null || params === void 0 ? void 0 : params.packageIds, { strategy: params === null || params === void 0 ? void 0 : params.strategy });
return await this.install(libraries, { cache: true });
}
/**
* install libraries
*
* @param {FPPackage[]} libraries
* @param {{ cache: boolean; ignoreLocal:boolean }} [opt] {cache: will add libraries instance to manager}
* @return {*}
*/
async install(libraries, opt) {
const installs = await this.initInstalls(libraries, opt);
return installs;
}
/**
* fetch libraries from server
*
* @param {string[]} [pluginIds]
* @return {*}
*/
async fetchPlugins(packageIds, opt = {}) {
var _a;
return await data_service_1.foxpageDataService.fetchPlugins(this.appId, Object.assign(Object.assign({ packageIds }, opt), { semver: (_a = this.config) === null || _a === void 0 ? void 0 : _a.version }));
}
/**
* fetch libraries with name and version from server
*
* @param {PackageNamedVersion[]} nameVersions
* @param {PackageFreshOption} opt {isCanary,...}
* @return {Promise<FPPackageResponse[]>}
*/
async fetchPluginsByNamedVersions(nameVersions, opt) {
var _a;
return await data_service_1.foxpageDataService.fetchPluginsByNamedVersions(this.appId, {
nameVersions,
isCanary: opt === null || opt === void 0 ? void 0 : opt.isCanary,
semver: (_a = this.config) === null || _a === void 0 ? void 0 : _a.version,
});
}
/**
* listen the "ON_PULL" event
* updates & removes is the plugin content id list
* @protected
* @param {ResourceUpdateInfo} data
* @return {*} {Promise<void>}
*/
async onPull(data) {
const { updates, removes } = data.plugin || {};
if (updates && updates.length > 0) {
// this.markNeedUpdates(updates);
await this.freshPlugins({ packageIds: updates });
}
if (removes && removes.length > 0) {
this.removePlugins(removes.map(item => this.pluginIdMap.get(item)).filter(foxpage_shared_1.isNotNill));
}
}
/**
* on stash
* @param data
*/
onStash(data) {
var _a;
(_a = data.plugins) === null || _a === void 0 ? void 0 : _a.map(item => {
this.addPlugin(item);
});
}
/**
* on fetch
* list: the manager keys
* @protected
* @param {string[]} list
* @return {*} {Promise<PackageImpl[]>}
*/
async onFetch(list) {
return await this.freshPlugins({
namedVersions: list.map(item => {
const [name, version] = this.splitKey(item);
return { name, version };
}),
});
}
async createInstance(data) {
const instance = this.newPlugin(data);
return instance;
}
newPlugin(data) {
return new plugin_1.PluginInstance(data, {
appId: this.appId,
appSlug: this.appSlug,
resource: this.resourceConfig,
logger: this.logger,
});
}
resolvePlugin(pluginInfos) {
return pluginInfos.map(item => item.package);
}
/**
* init need install libraries instances
*
* @private
* @param {FPPackage[]} libraries
* @param {{ cache: boolean, ignoreLocal:boolean }} [opt]
* @return {*}
*/
async initInstalls(libraries, opt) {
var _a;
const installMap = new Map();
const { cache = false, ignoreLocal = false } = opt || {};
const create = async (list, { initDependencies } = { initDependencies: false }) => {
for (const pkg of list) {
const { name, version, components } = pkg;
const key = this.generateKey(name, version);
const localPkg = !ignoreLocal ? await this.getLocalPlugin(name, version) : null;
// will install it
if (!localPkg && !installMap.has(key)) {
let newPkg;
if (cache) {
newPkg = await this.addPlugin(pkg);
}
else {
newPkg = this.newPlugin(pkg);
}
if (newPkg) {
installMap.set(key, newPkg);
}
}
else {
if (cache) {
this.updatePluginLiveVersion(pkg);
}
}
// init components
// components: the dependencies contents
if (initDependencies && (components === null || components === void 0 ? void 0 : components.length)) {
await create(components);
}
}
};
try {
await create(libraries, { initDependencies: true });
}
catch (e) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`install libraries failed: `, e);
}
return Array.from(installMap.values());
}
/**
* get plugin versions
*
* @private
* @param {string[]} names plugin names
* @return {*} {[key]: plugin name, [value]: versions}
*/
getPluginVersionsByNames(names) {
const versionInfo = {};
names.forEach(name => {
const { versions } = this.pluginVersionsMap.get(name) || {};
if (!versions) {
// this.logger.warn(`not exist the plugin@${name} in pluginMap`);
versionInfo[name] = null;
}
else {
versionInfo[name] = versions;
}
});
return versionInfo;
}
updatePluginVersionsMap(pkg) {
const { id, name, version } = pkg;
const { versions } = this.pluginVersionsMap.get(name) || {};
if (versions) {
versions.push(version);
}
else {
this.pluginVersionsMap.set(name, { id, liveVersion: '', versions: [version] });
this.updatePluginLiveVersion(pkg);
}
}
/**
* destroy
*/
destroy() {
super.destroy();
this.pluginIdMap.clear();
this.pluginVersionsMap.clear();
}
}
exports.CSRPluginManagerImpl = CSRPluginManagerImpl;