@lark-project/cli
Version:
飞书项目插件开发工具
259 lines (258 loc) • 12.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.localAPIServer = void 0;
const morgan_1 = __importDefault(require("morgan"));
const get_plugin_profile_for_v2_1 = require("../../../get-plugin-profile-for-v2");
const types_1 = require("../../../types");
const constants_1 = require("../../../constants");
const logger_1 = require("../../../utils/logger");
function localAPIServer(devServer, serverOrigin) {
if (!(devServer === null || devServer === void 0 ? void 0 : devServer.app)) {
throw new Error('webpack-dev-server is not defined');
}
devServer.app.use((0, morgan_1.default)('combined'));
devServer.app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', '*');
next();
});
// 查询调试插件的功能构成
// @query platform 过滤适配端 web | mobile,默认 web
// @query point_type 过滤点位类型,button | control | dashboard | board | view | intercept | config,不传为全部
// @query project_id 空间 id
// @query type_key 过滤工作项类型,不传为全部
// @query scene 按场景过滤,不传为全部
devServer.app.get('/api/v2/plugin/point_info', async function (request, response) {
try {
const platform = (request.query.platform || types_1.EFeaturePlatform.web);
const pointType = request.query.point_type;
const typeKey = request.query.type_key;
const scene = request.query.scene
? Number.parseInt(request.query.scene, 10)
: undefined;
const pluginProfile = await (0, get_plugin_profile_for_v2_1.getPluginProfileForV2)();
const matchedFeatures = pluginProfile.features
.filter(feature => !pointType || feature.type === pointType)
.filter(feature => {
if (
/**
* typeKey
* field_template 没有typeKey
*/
!typeKey ||
!feature.workItemTypeKeys || // 构成没有指定工作项类型 scope 则视为全部
feature.workItemTypeKeys.includes(types_1.EFeatureWorkItemTypeScope.all) ||
feature.workItemTypeKeys.some(typeScope => typeKey === typeScope) ||
feature.workItemTypeKeys.includes(types_1.EFeatureWorkItemTypeScope.others)) {
return true;
}
return false;
})
.filter(feature => {
var _a, _b;
return !!((_b = (_a = feature[platform === types_1.EFeaturePlatform.web ? types_1.EFeaturePlatform.web : types_1.EFeaturePlatform.mobile]) === null || _a === void 0 ? void 0 : _a.resourceId) === null || _b === void 0 ? void 0 : _b.resource);
})
.filter(feature => {
/**
* scene
* field_template 不需要scene
*/
if (pointType === types_1.EFeatureType.customField ||
pointType === types_1.EFeatureType.liteAppComponent) {
return true;
}
return !scene || !feature[platform].scenes || feature[platform].scenes.includes(scene);
});
response.json({
data: {
plugin_info: {
key: pluginProfile.id,
name: pluginProfile.name,
icon: pluginProfile.icon,
short: pluginProfile.short,
},
matched_points: matchedFeatures.map(feature => {
var _a;
const extension = (_a = feature.extension) === null || _a === void 0 ? void 0 : _a.reduce((obj, ext) => {
if ((ext === null || ext === void 0 ? void 0 : ext.ext_config) && (ext === null || ext === void 0 ? void 0 : ext.ext_subType)) {
obj[ext.ext_subType] = ext.ext_config;
}
return obj;
}, {});
return {
key: feature.key,
name: feature.name,
type: feature.type,
icon: feature.icon,
work_item_type_keys: feature.workItemTypeKeys,
custom_work_item_type_keys: feature.customWorkItemTypeKeys,
scenes: feature[platform].scenes,
component_type: feature.component_type,
control_display_style: extension === null || extension === void 0 ? void 0 : extension.mob_control_block_style,
field_template_display_style: extension === null || extension === void 0 ? void 0 : extension.mob_field_template_block_style,
};
}),
},
statusCode: 0,
});
}
catch (e) {
response.status(500).send(e.message);
}
});
/**
* 查询点位对应的资源
* @query platform 过滤适配端 web | mobile,默认 web
* @query point_key 点位标识
*/
devServer.app.get('/api/v2/plugin/point/resource', async function (request, response) {
var _a, _b;
try {
const platform = (request.query.platform || types_1.EFeaturePlatform.web);
const pointKey = request.query.point_key;
if (!pointKey) {
response.status(400).send('Specify point_key in the querystring');
return;
}
const pluginProfile = await (0, get_plugin_profile_for_v2_1.getPluginProfileForV2)();
const feature = pluginProfile.features.find(feature => feature.key === pointKey);
const resourceKey = (_b = (_a = feature === null || feature === void 0 ? void 0 : feature[platform]) === null || _a === void 0 ? void 0 : _a.resourceId) === null || _b === void 0 ? void 0 : _b.resource;
if (!resourceKey) {
response.status(500).send(`Can't find the resource for the point_key ${pointKey}`);
return;
}
const matchedPoints = {
[pointKey]: {
point_info: {
key: feature.key,
name: feature.name,
type: feature.type,
component_type: feature.component_type,
icon: feature.icon,
resource_id: {
resource: resourceKey,
},
},
url_info: {
'resource_id.resource': `${serverOrigin}/app/page/${resourceKey}?platform=${platform}`,
},
extension: feature.extension || [],
},
};
response.json({
data: {
app_version: 'dev',
plugin_info: {
key: pluginProfile.id,
name: pluginProfile.name,
icon: pluginProfile.icon,
short: pluginProfile.short,
},
point_info_map: matchedPoints,
},
statusCode: 0,
});
}
catch (e) {
response.status(500).send(e.message);
}
});
/**
* v2 仅用于 Web 调试
*/
devServer.app.get('/api/v1/plugin/info', async function (req, res) {
try {
const pluginProfile = await (0, get_plugin_profile_for_v2_1.getPluginProfileForV2)();
const pluginId = pluginProfile.id;
const frameworkVersion = pluginProfile.frameworkVersion;
console.log('===== pluginProfile', pluginProfile.features);
res.json({
data: {
version: 'dev',
pluginId,
enable: true,
name: pluginProfile.name,
icon: pluginProfile.icon,
integrate_points: pluginProfile.features
.filter(feature => { var _a, _b; return (_b = (_a = feature[types_1.EFeaturePlatform.web]) === null || _a === void 0 ? void 0 : _a.resourceId) === null || _b === void 0 ? void 0 : _b.resource; }) // 过滤掉没有 Web 资源的点位配置
.map(feature => ({
key: feature.key,
name: feature.name,
type: feature.type.toUpperCase(), // Web 代码逻辑点位枚举都是大写,这里返回统一处理下
icon: feature.icon,
work_item_type: feature.workItemTypeKeys,
custom_work_item_type: feature.customWorkItemTypeKeys,
scene: feature[types_1.EFeaturePlatform.web].scenes,
component_type: feature.component_type,
i18n_info: feature.i18n_info,
description: feature.description,
})) || [],
runtime_version: frameworkVersion,
},
statusCode: 0,
});
}
catch (e) {
logger_1.logger.debug('get plugin info failed:', e);
res.json({ data: null, statusCode: 1001 });
}
});
/**
* 请求插件静态资源
* @param resourceId 资源 id
* @query instanceId 插件点位实例 id
* @query platform 当前请求的适配端
*/
devServer.app.get('/app/page/:resourceId', async function (req, res) {
const { resourceId } = req.params;
const { instanceId, platform } = req.query;
const multiCompiler = devServer.compiler;
const compiler = multiCompiler.compilers.find(c => c.name === resourceId);
if (!compiler) {
res.status(404).send(`Can't find the compiler for ${resourceId}`);
return;
}
function _readFileAndResponse() {
const mfs = compiler.outputFileSystem;
mfs.readFile(`dist/${resourceId}/index.html`, (error, file) => {
if (error) {
logger_1.logger.warn(error);
res.status(404).send(`Can't find the resources for ${resourceId}`);
return;
}
res.setHeader('Content-Type', 'text/html');
res.setHeader('Cache-Control', 'no-store');
// Web 端的 HTML 请求进行处理,将 instanceId 替换进去
if (instanceId && platform === types_1.EFeaturePlatform.web) {
const finalHTML = file
.toString()
.replace(constants_1.HTML_TPL_INSTANCE_PLACEHOLDER, req.query.instanceId);
res.send(finalHTML);
return;
}
res.send(file);
});
}
// 空闲则说明打包完成,直接访问并返回
if (compiler.idle) {
_readFileAndResponse();
}
else {
logger_1.logger.info('wait until bundle finished:', resourceId);
let flag = false;
compiler.hooks.done.tap('cli-static', stats => {
logger_1.logger.info('bundle finished:', resourceId);
// 代码编辑后重编译会触发 cli-static,有概率 response setHeader 多次导致 crash
if (!flag) {
_readFileAndResponse();
}
flag = true;
});
}
});
}
exports.localAPIServer = localAPIServer;