@foxpage/foxpage-node-sdk
Version:
foxpage node sdk
340 lines (339 loc) • 12.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComponentLoaderImpl = void 0;
const lodash_1 = __importDefault(require("lodash"));
const foxpage_manager_1 = require("@foxpage/foxpage-manager");
const foxpage_shared_1 = require("@foxpage/foxpage-shared");
const logger_1 = require("../logger");
/**
* component loader
*
* @export
* @class ComponentLoaderImpl
* @implements {ComponentLoader}
*/
class ComponentLoaderImpl {
constructor(appId, opt) {
/**
* the loaded components map
*
* @private`
*/
this.loadedMap = new Map();
/**
* miss load componentMap
*
* @private
*/
this.missLoadMap = new Map();
/**
* id -> version map
*
* @private
*/
this.resolvedVersionMap = new Map();
/**
* all dependencies
* "name":"version" list
* @private
* @type {string[]}
*/
this.dependencies = [];
/**
* dependencies loaded map
*
* @private
*/
this.loadedDependencyMap = new Map();
/**
* dependencies miss load
* key: "name":"version", value: {name, version}
* @private
* @type {FPPackageDependency[]}
*/
this.missLoadDependencyMap = new Map();
/**
* for record the missed package version that had fetched from server
* "name":"version" | "name":"" -> "name":"version",
* @private
*/
this.missedVersionMap = new Map();
this.splitKey = (key) => {
return foxpage_shared_1.packager.splitKey(key);
};
this.app = (0, foxpage_manager_1.getApplication)(appId);
this.opt = Object.assign({ autoDownloadComponent: true, useStructureVersion: false }, opt);
this.logger = (0, logger_1.loggerCreate)('ComponentLoader');
}
/**
* load components
* @param {StructureNode[]} schemas
*/
async load(schemas) {
await this.resolveComponents(schemas);
await this.resolveDependencies();
// auto download the missed packages
if (this.opt.autoDownloadComponent) {
const loadedMap = await this.downloadMisses();
await this.resolveMisses(loadedMap);
}
}
/**
* get loaded components
*
* @return {*} {Map<string, FoxpageComponent>}
*/
getLoadedComponents() {
return this.loadedMap;
}
/**
* get loaded dependencies
*
* @return {*} {Map<string, FoxpageComponent>}
*/
getLoadedDependencies() {
return this.loadedDependencyMap;
}
/**
* destroy: clear all source
*
*/
async destroy() {
this.loadedMap.clear();
this.missLoadMap.clear();
this.resolvedVersionMap.clear();
this.dependencies = [];
this.loadedDependencyMap.clear();
this.missLoadDependencyMap.clear();
this.missedVersionMap.clear();
}
/**
* download the missed packages
*
* @private
* @return {*}
*/
async downloadMisses() {
var _a, _b;
const loadedMap = new Map();
// get all messed packages name & versions
const namedVersions = this.getMissedPackages();
if (namedVersions.length > 0) {
this.logger.info('fetch missed packages from server');
this.logger.debug('fetch missed packages from server:', namedVersions);
// fetch from server
const fetches = await ((_a = this.app) === null || _a === void 0 ? void 0 : _a.packageManager.fetchPackagesByNamedVersions(namedVersions, {
isCanary: this.opt.isCanary,
isSemver: this.opt.isSemver,
}));
// get need install packages
const packages = [];
fetches === null || fetches === void 0 ? void 0 : fetches.forEach(item => {
const key = this.generateKey(item.name, item.version);
this.missedVersionMap.set(key, this.generateKey(item.package.name, item.package.version));
packages.push(item.package);
});
// install
// preview or canary will not cache the installed
const cacheState = !this.opt.isPreviewMode && !this.opt.isCanary;
const installed = await ((_b = this.app) === null || _b === void 0 ? void 0 : _b.packageManager.install(packages, {
cache: cacheState,
ignoreLocal: this.opt.isCanary,
reInstall: this.opt.reLoadComponent,
}));
installed === null || installed === void 0 ? void 0 : installed.forEach(item => {
loadedMap.set(this.generateKey(item.name, item.version), item);
});
}
return loadedMap;
}
/**
* resolve the missed packages
*
* @private
* @param {(Map<string, Package | null>)} loadedMap
*/
async resolveMisses(loadedMap) {
const resolver = (list, cb) => {
list.forEach(item => {
const messages = new foxpage_shared_1.Messages();
const { name, version = '' } = item;
const key = this.generateKey(name, version);
const missKey = this.missedVersionMap.get(key);
const pkg = missKey ? loadedMap.get(missKey) : null;
let msg = '';
if (pkg) {
msg = `package ${key} load succeed with version: ${pkg.version}`;
// logger.info(msg);
}
else {
msg = `package ${key} load failed`;
// logger.warn(msg);
}
messages.push(msg);
if (typeof cb === 'function') {
cb(item, pkg, messages);
}
});
};
// resolve missed component
resolver(Array.from(this.missLoadMap.values()), (item, pkg, messages) => {
this.loadedMap.set(item.id, this.componentFormatter(item, pkg, messages));
});
// resolve missed dependencies
resolver(Array.from(this.missLoadDependencyMap.values()), (item, pkg, messages) => {
const key = this.generateKey(item.name, item.version);
this.loadedDependencyMap.set(key, this.componentFormatter(this.generateStructureNode(key, item.name, item.version || ''), pkg, messages));
});
}
/**
* resolve the component
* for divide the available or invalid
* @private
* @param {StructureNode[]} schemas
*/
async resolveComponents(schemas) {
var _a, _b, _c;
for (const item of schemas) {
const { id, name, version = '', children } = item; // schema structure node
const key = this.generateKey(name, version);
if (this.isComponent(item) && !this.loadedMap.has(key)) {
const messages = new foxpage_shared_1.Messages();
// in canary
// will get all packages canary version from server
// no canary will return last live version
// reLoadComponent: need to load component all
if (this.opt.isCanary || this.opt.reLoadComponent) {
this.missLoadMap.set(id, item);
this.setResolveVersion(item, item.version || '');
}
else {
// get component from local
const pkg = await ((_a = this.app) === null || _a === void 0 ? void 0 : _a.packageManager.getLocalPackage(name, ((_b = this.opt) === null || _b === void 0 ? void 0 : _b.useStructureVersion) ? version : ''));
if (pkg && pkg.available) {
messages.push(`component@${item.id} -> package ${pkg.name} local version: ${pkg.version}`);
// collect the dependencies
(_c = pkg.dependencies) === null || _c === void 0 ? void 0 : _c.forEach(dep => {
this.dependencies.push(this.generateKey(dep.name, dep.version));
});
this.loadedMap.set(id, this.componentFormatter(item, pkg, messages));
this.setResolveVersion(item, pkg.version);
}
else {
this.logger.warn(`component@${item.id} -> package ${key} local not exist available version, try to download`);
this.missLoadMap.set(id, item);
this.setResolveVersion(item, item.version || '');
}
}
}
if (children && children.length > 0) {
await this.resolveComponents(children);
}
}
}
/**
* resolve the dependencies
*
* @private
*/
async resolveDependencies() {
var _a;
for (const key of this.dependencies) {
const [name, version = ''] = this.splitKey(key);
const messages = new foxpage_shared_1.Messages();
const pkg = await ((_a = this.app) === null || _a === void 0 ? void 0 : _a.packageManager.getLocalPackage(name, version));
if (pkg && pkg.available) {
messages.push(`dependency ${key} loaded`);
this.loadedDependencyMap.set(key, this.componentFormatter(this.generateStructureNode(key, name, version), pkg, messages));
}
else {
this.logger.warn(`dependency ${key} loaded failed, try to auto download.`);
this.missLoadDependencyMap.set(key, { name, version });
}
}
}
/**
* get the missed packages
*
* @private
* @return {PackageNamedVersion} namedVersions
*/
getMissedPackages() {
const missComponents = Array.from(this.missLoadMap.values());
let missList = [];
missComponents.forEach(item => {
const versions = this.resolvedVersionMap.get(item.id) || [];
versions.forEach(version => {
missList.push({
name: item.name,
version,
});
});
});
missList = missList.concat(Array.from(this.missLoadDependencyMap.values()));
const namedVersions = lodash_1.default.uniqWith(missList, lodash_1.default.isEqual);
return namedVersions;
}
componentFormatter(node, pkg, messages) {
const newMsg = messages || new foxpage_shared_1.Messages();
if (pkg) {
const { type, version, source, supportNode, deps, url, downloadUrl, debugUrl, cssUrl, componentFactory, messages: pkgMessages, } = pkg;
pkgMessages.forEach(msg => {
newMsg.push(msg);
});
let meta = pkg.meta;
// will remove
if (!meta) {
meta = {};
}
if (cssUrl) {
meta.assets = [{ url: cssUrl, type: 'css' }];
}
return {
type,
name: node.name,
version: node.version || version,
isLive: pkg.isLive,
browserURL: url,
debugURL: debugUrl || url,
nodeURL: downloadUrl,
cssURL: cssUrl,
supportSSR: supportNode,
factory: componentFactory,
dependencies: deps,
source,
meta,
messages,
};
}
return {
name: node.name,
version: node.version,
messages: newMsg.hasError ? newMsg : undefined,
};
}
generateStructureNode(key, name, version) {
return { id: `builtin_${key}`, name, version, props: {}, type: 'react.component' };
}
setResolveVersion(structureNode, version) {
const nodeId = structureNode.id;
if (!this.resolvedVersionMap.has(nodeId)) {
this.resolvedVersionMap.set(nodeId, []);
}
const list = this.resolvedVersionMap.get(nodeId) || [];
if (list.indexOf(version) === -1) {
list.push(version);
}
this.resolvedVersionMap.set(nodeId, list);
}
isComponent(structureNode) {
return !!structureNode.name;
}
generateKey(name, version) {
return foxpage_shared_1.packager.generateKey(name, version);
}
}
exports.ComponentLoaderImpl = ComponentLoaderImpl;