@foxpage/foxpage-manager
Version:
foxpage resource manager
193 lines (192 loc) • 8.26 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.PackageInstance = void 0;
const fs_extra_1 = require("fs-extra");
const lodash_1 = __importDefault(require("lodash"));
const foxpage_shared_1 = require("@foxpage/foxpage-shared");
const cache_1 = require("../cache");
const reporter_1 = require("../reporter");
const fetcher_1 = require("./fetcher");
const resolver_1 = require("./resolver");
const utils_1 = require("./utils");
const wrapper_1 = require("./wrapper");
/**
* package
*
* @export
* @interface Package
*/
class PackageInstance extends foxpage_shared_1.PrePackageInstance {
constructor(info, opt) {
super(info, opt);
this._loaded = false;
this.status = 'preInstall';
this.available = false;
this.filePath = this.name ? (0, resolver_1.resolvePackageJSPath)(this.appId, this.name, this.version) : '';
this.reporter = (0, reporter_1.getReporter)();
}
get exported() {
var _a, _b, _c, _d, _e;
if (!this.supportNode || !this.filePath) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info(`package ${this.name}@${this.version} not support node or not exist the file path`);
return undefined;
}
// load file
// ssrSupport === false, not need load file
if (!this._loaded && (((_b = this.meta) === null || _b === void 0 ? void 0 : _b.ssrSupport) === undefined || ((_c = this.meta) === null || _c === void 0 ? void 0 : _c.ssrSupport) === true)) {
try {
const res = (0, utils_1.loadFile)(this.filePath);
if (!(res.default && (typeof res.default === 'function' || !lodash_1.default.isEmpty(res.default)))) {
(_d = this.logger) === null || _d === void 0 ? void 0 : _d.error(`package ${this.name}@${this.version} load file is empty or invalid: not a function`);
this.available = false;
this.status = 'loadFailed';
return;
}
this._exported = res;
this._loaded = true;
}
catch (error) {
this.messages.push(error);
(_e = this.logger) === null || _e === void 0 ? void 0 : _e.error(`package ${this.name}@${this.version} get exported failed:`, error);
this.available = false;
this.status = 'loadFailed';
return;
}
}
return this._exported;
}
get componentFactory() {
return (0, foxpage_shared_1.getESModuleExport)(this.exported).default;
}
/**
* install package
*
* @param {PackageInstallOption} opt
*/
async install(opt = {}) {
var _a, _b, _c, _d, _e;
this.status = 'installing';
// ssrSupport === false or other values, not need fetch code
if (!(((_a = this.meta) === null || _a === void 0 ? void 0 : _a.ssrSupport) === undefined || ((_b = this.meta) === null || _b === void 0 ? void 0 : _b.ssrSupport) === true)) {
this.available = true;
this.status = 'installed';
}
else {
try {
const cost = (_c = this.reporter) === null || _c === void 0 ? void 0 : _c.cost(this.name);
await this.fetchCode(opt);
this.available = true;
this.status = 'installed';
const loadCost = cost === null || cost === void 0 ? void 0 : cost();
(_d = this.reporter) === null || _d === void 0 ? void 0 : _d.componentReporter(this.name, { version: this.version, loadCost }, { appId: this.appId });
}
catch (error) {
this.available = false;
this.status = 'fail';
this.messages.push(error);
(_e = this.logger) === null || _e === void 0 ? void 0 : _e.error(`install package ${this.name}@${this.version}@${this.downloadUrl} failed:`, error);
return;
}
}
}
async fetchCode({ inspect = false, wrap = true }) {
var _a, _b;
if (this.available && this.filePath && (await (0, fs_extra_1.pathExists)(this.filePath))) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info(`package ${this.name}@${this.version} path ${this.filePath} exist.`);
return;
}
if (!this.source || !this.source.node) {
return foxpage_shared_1.optional.fail('miss source');
}
let fetchResult = await this.fetch(this.downloadUrl, this.downloadProxy);
if (fetchResult.fail) {
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.error(`fetch package ${this.name}@${this.version} failed: ${this.downloadUrl}`);
// with retry downloads
if (this.retryDownloads && this.retryDownloads.length > 0) {
fetchResult = await this.retry(this.retryDownloads);
if (fetchResult.fail) {
throw fetchResult.error;
}
}
else {
throw fetchResult.error;
}
}
const code = wrap && this.deps.length > 0 ? (0, wrapper_1.wrapCode)(fetchResult.data.content, this) : fetchResult.data.content;
if (inspect) {
const inspectResult = await this.inspectPackage(code);
if (inspectResult.fail) {
const msg = `${this.name}@${this.version} inspect fail: ${inspectResult.error.message}`;
this.supportNode = false;
throw new Error(msg);
}
}
await this.processJSCode(code);
}
async inspectPackage(code) {
try {
(0, utils_1.runInNodeContext)(code);
return foxpage_shared_1.optional.ok(true);
}
catch (error) {
return foxpage_shared_1.optional.fail(error);
}
}
async fetch(downloadUrl, proxy) {
if (!downloadUrl) {
return foxpage_shared_1.optional.fail('miss downloadUrl');
}
const _opt = {};
if (proxy && proxy.host && proxy.protocol) {
_opt.proxy = proxy;
}
const fetcher = new fetcher_1.PackageFetcher(downloadUrl, {
downloadTimeout: 3000,
requestOpt: _opt,
});
const result = await fetcher.fetch();
return result;
}
async retry(list) {
var _a, _b;
let result = {};
for (const item of list) {
result = await this.fetch(item.url, item.proxy);
if (result.ok) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info(`retry fetch package ${this.name}@${this.version} succeed: ${item.url}`);
break;
}
else {
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.error(`retry fetch package ${this.name}@${this.version} failed: ${item.url}`);
}
}
return result;
}
async processJSCode(jsContent) {
try {
const targetJsPath = (0, resolver_1.resolvePackageJSPath)(this.appId, this.name, this.version);
const lockedStr = targetJsPath.replace('.js', '-locked');
const exist = await (0, fs_extra_1.pathExists)(targetJsPath);
if (!(exist && this.available)) {
await cache_1.locker.withLock(lockedStr, async () => {
var _a;
await (0, fs_extra_1.outputFile)(targetJsPath, jsContent, {
encoding: 'utf-8',
flag: 'w',
});
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info('install package %s@%j succeed', this.name, this.version);
});
}
}
catch (err) {
const isExistError = err.code === 'EEXIST' || String(err).includes('exists');
if (!isExistError) {
throw err;
}
}
}
}
exports.PackageInstance = PackageInstance;