@lcap/nasl
Version:
NetEase Application Specific Language
706 lines • 28 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.replaceAssetsPrefix = exports.genReleaseBodyForParallel = exports.genReleaseBody = exports.getFrontendBundleFiles = exports.getNaslAnnotatedJSONFile = exports.addEntityToDefaultDS = exports.__getNaslAnnotatedJSON = exports.replaceAssets = exports.replaceFrontendTypesAssets = void 0;
const concepts_1 = require("../../concepts");
const genBundleFiles_1 = require("../genBundleFiles");
const is_view_logic_1 = require("../../service/instruct-ruler/rules/is-view-logic");
const utils_1 = require("./utils");
const permission_1 = require("../permission");
const validation_1 = require("./validation");
const memory_optimization_1 = require("./memory-optimization");
const utils = __importStar(require("../../utils"));
const config_1 = require("../../config");
const share_content_1 = require("./share-content");
/**
* 替换端类型中的资源
* @param frontendTypes
* @param fn
*/
function replaceFrontendTypesAssets(frontendTypes, fn) {
frontendTypes.forEach((frontendType) => {
(0, concepts_1.fastTraverseNaslObject)(frontendType, (node) => {
const meta = (0, concepts_1.getConceptMeta)(node.concept);
for (const key of meta.normalProperties) {
const attr = node[key];
if (typeof attr === 'string') {
fn(node, key, attr);
}
}
});
});
}
exports.replaceFrontendTypesAssets = replaceFrontendTypesAssets;
function replaceAssets(app, json, opt) {
// 非增量发布的发布状态排除 views,导出源码不排除
if (!opt?.isIncremental && opt?.realRelease) {
json.frontendTypes.forEach((frontendType) => {
frontendType.frontends.forEach((frontend) => {
delete frontend.views;
});
});
}
const assets = opt?.assets ?? [];
if (opt?.isExport) {
// 导出前端源码+后端源码
if (opt?.ignoreFiles && Array.isArray(assets) && assets.length) {
const assetsMap = opt.assetsMap;
// 正则
const regex = utils.MediaURLOrPathRegex;
/**
* 注意⚠️
* 如果要调整目录结构,请下面函数的逻辑
* replaceAssetsPrefix
*/
replaceFrontendTypesAssets(json.frontendTypes, (node, key, url) => {
if (['staticStyle'].includes(key)) {
node[key] = url.replace(regex, (url) => {
const asset = utils.getAssetFromAssetsMap(assetsMap, url);
if (asset) {
const assetName = asset.fileKey || asset.name;
const path = `/assets/${assetName}`;
config_1.config.assets.push({
path,
isDir: false,
url,
});
const assetNames = (assetName?.split('/') || []).map((name) => encodeURIComponent(name));
const encodeAssetName = assetNames.join('/');
const encodePath = `/assets/${encodeAssetName}`;
return encodePath;
}
return url;
});
}
else {
const asset = utils.getAssetFromAssetsMap(assetsMap, url);
if (asset) {
// 这里是用户上传的图片链接 在导出的时候变为 离线下载的
// 上边也是同样的目的 只是处理的css样式里的图片链接
const assetName = asset.fileKey || asset.name;
const path = `/assets/${assetName}`;
config_1.config.assets.push({
path,
isDir: false,
url,
});
const assetNames = (assetName?.split('/') || []).map((name) => encodeURIComponent(name));
const encodeAssetName = assetNames.join('/');
const encodePath = `/assets/${encodeAssetName}`;
node[key] = encodePath;
}
}
});
}
}
}
exports.replaceAssets = replaceAssets;
/** 获取全量标注数据 */
async function __getNaslAnnotatedJSON(app, opt) {
let json;
try {
json = await app.naslServer.fastGetNaslAnnotatedJSON(app);
await opt?.logPublishFunc?.('语言', '获取全量类型标注成功');
// Memory optimization is now integrated into the annotation process
// No need for separate compaction step - saves memory and time
await opt?.logPublishFunc?.('优化', '内存优化已集成到标注过程');
// 打印传给服务端的 nasl,为了服务端排查问题方便
if (utils.isDebugMode) {
console.dir(json, '标注后的nasl');
}
}
catch (err) {
console.error('代码标注失败', err.stack ?? err.message ?? err.toString());
throw err;
}
app.curDeployEnv = opt?.env;
replaceAssets(app, json, opt);
await opt?.logPublishFunc?.('资源', '前端静态资源替换完成');
return json;
}
exports.__getNaslAnnotatedJSON = __getNaslAnnotatedJSON;
// 暂时发布的时候加一个实体
// 为了服务端发布,使用文件系统,标注的nasl 强制增加一个
// 服务端承诺 2024年10月份会挪走, @钟春燕 @杨剑飞
function addEntityToDefaultDS(app, defaultDS) {
const tableName = 'lcap_async_task_' + (app.id || '').slice(0, 6);
defaultDS.entities.push({
"concept": "Entity",
"name": "LCAPAsyncTask",
"uuid": "fe2cef1efd9c4189951b1f0bb6eacf11",
"tableName": tableName,
"description": null,
"origin": "ide",
"composedBy": null,
"properties": [
{
"concept": "EntityProperty",
"name": "id",
"uuid": "af657c05c1e545769887cfd2104257bb",
"columnName": "id",
"label": "主键",
"description": "主键",
"required": true,
"primaryKey": true,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": false,
"inFilter": false,
"inForm": false,
"inDetail": false
},
"rules": [],
"generationRule": "auto",
"sequence": "Entity11299220825",
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "Long"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "task_id",
"uuid": "1ee6f95909b34416bc091775766e9733",
"columnName": "task_id",
"label": "任务id",
"description": null,
"required": true,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "task_status",
"uuid": "4b11c717968d43e7a5dccb329d444cc1",
"columnName": "task_status",
"label": "任务状态",
"description": null,
"required": true,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "task_name",
"uuid": "e069c6541835457483a37df8c0cdafc0",
"columnName": "task_name",
"label": "任务名称",
"description": null,
"required": true,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "result",
"uuid": "6a6a3b70e5d04831a1da89e6d72b445c",
"columnName": "result",
"label": "任务结果",
"description": null,
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [
"maxLength(16777215)"
],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "error_msg",
"uuid": "7c1c143003784277882b044aa88224d9",
"columnName": "error_msg",
"label": "任务异常信息",
"description": null,
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [
"maxLength(16777215)"
],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "job_parameters",
"uuid": "82f83a95684642568701562d9d91571c",
"columnName": "job_parameters",
"label": "任务参数",
"description": null,
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [
"maxLength(16777215)"
],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "file_plugin_code",
"uuid": "f05f15779bd84edb8f358ef8483acc23",
"columnName": "file_plugin_code",
"label": "文件插件唯一标识",
"description": "文件插件唯一标识",
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "file_type",
"uuid": "79355d76950a48609b123417e08f85bc",
"columnName": "file_type",
"label": "需要生成的文件类型",
"description": null,
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": true,
"inForm": true,
"inDetail": true
},
"rules": [],
"generationRule": "manual",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "String"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "createdTime",
"uuid": "273fa6230d8b4c068b57a76f7b08bdfa",
"columnName": "created_time",
"label": "创建时间",
"description": "创建时间",
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": false,
"inForm": false,
"inDetail": false
},
"rules": [],
"generationRule": "auto",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "DateTime"
},
"databaseTypeAnnotation": null,
"defaultValue": null
},
{
"concept": "EntityProperty",
"name": "updatedTime",
"uuid": "7d2ec9616309424fbd37830b1e8f1a9d",
"columnName": "updated_time",
"label": "更新时间",
"description": "更新时间",
"required": false,
"primaryKey": false,
"relationNamespace": null,
"relationEntity": null,
"relationProperty": null,
"deleteRule": null,
"display": {
"inTable": true,
"inFilter": false,
"inForm": false,
"inDetail": false
},
"rules": [],
"generationRule": "auto",
"sequence": null,
"composedBy": null,
"typeAnnotation": {
"concept": "TypeAnnotation",
"typeKind": "primitive",
"typeNamespace": "nasl.core",
"typeName": "DateTime"
},
"databaseTypeAnnotation": null,
"defaultValue": null
}
],
"indexes": [
{
"concept": "EntityIndex",
"name": "uni_task_id",
"indexName": null,
"propertyNames": [
"task_id"
],
"unique": true,
"description": null
}
],
"applyAnnotations": null
});
}
exports.addEntityToDefaultDS = addEntityToDefaultDS;
/**
* 获取全量标注文件
* @description 数据将会被 stringify
*/
async function getNaslAnnotatedJSONFile(app, opt) {
return {
name: 'nasl-annotated.json',
content: JSON.stringify(await __getNaslAnnotatedJSON(app, opt)),
};
}
exports.getNaslAnnotatedJSONFile = getNaslAnnotatedJSONFile;
async function getFrontendBundleFiles(app, opt) {
let files = [];
const config = {
...opt.appInfo,
...opt,
appid: app.id,
devDnsAddr: app.devDnsAddr,
miniEnable: app.miniEnable,
};
if (opt.frontends && opt.frontends.length) {
await opt.logPublishFunc?.('页面', '生成页面代码:开始');
app.naslServer.logger.time('生成前端页面');
files = await (0, genBundleFiles_1.genFrontendBundleFiles)(app, opt.frontends, config);
if (files && files.length === 0) {
throw new Error('前端代码生成失败');
}
if (opt.staticUrl) {
files = (0, utils_1.replaceAssetUrl)(files, opt.appInfo.USER_STATIC_URL, opt.staticUrl);
}
app.naslServer.logger.timeEnd('生成前端页面');
await opt.logPublishFunc?.('页面', '生成页面代码:完成');
}
return files;
}
exports.getFrontendBundleFiles = getFrontendBundleFiles;
async function mergeBodyData(app, opt) {
const authReport = (0, utils_1.getAuthReport)(app, opt.frontends);
await opt.logPublishFunc?.('权限', '分析权限数据成功!');
const logicPageResourceDtoList = (0, permission_1.genPermissionData)(app);
// 缓存应用权限数据
await (0, is_view_logic_1.cachePermission)(logicPageResourceDtoList);
// 与老板逻辑权限数据对比
// await testWithOldPermissionResult(app, logicPageResourceDtoList);
const allFrontends = (0, utils_1.getFrontendByTypes)(app?.frontendTypes);
const optFrontendIds = opt.frontends.map((frontend) => frontend.id);
const releaseFrontends = allFrontends.map((frontend) => ({
name: frontend.name,
type: frontend.type,
path: frontend.path,
selected: optFrontendIds.includes(frontend.id),
title: frontend.title,
}));
await utils.delay(100);
const callbackLogicsName = app.getExtensionsCallbackLogics();
const authFlags = (0, permission_1.genLogicAuthFlag)(app);
const body = {
...authReport, // 如果发布需要上报权限,导出源码不需要
logicPageResourceDtoList,
processFormDefinitions: (0, utils_1.getProcessFormDefinitions)(app, opt),
logicAuthFlag: authFlags,
reportIdList: (0, utils_1.findAllReportIdList)(app, opt.frontends),
appId: app.id,
env: opt.env,
branchId: opt.branchId,
replicas: opt.replicas,
pipelineVersion: opt.isIncremental ? '2.0' : '2.1', // 2.1 版本用于前后端并行发布的版本
appSpecification: opt.appSpecification,
callbackLogicsName,
frontends: releaseFrontends,
callLogicValidations: {},
downloadFileTasks: {},
generatorConfig: '',
files: [],
};
if (logicPageResourceDtoList) {
Object.assign(body, { logicPageResourceDtoList });
}
return body;
}
async function genReleaseBody(_app, opt) {
// @ts-expect-error
const app = _app.__v_raw ?? _app;
console.time('--backend--');
console.time('mergeBodyData');
const body = await mergeBodyData(app, opt);
await utils.delay(100);
console.timeEnd('mergeBodyData');
let callLogicValidations;
if (!opt?.isBackendValidation) {
callLogicValidations = await (0, validation_1.getCallLogicData)(app, opt.validations?.validationRules);
}
console.timeEnd('--backend--');
console.time('getFrontendBundleFiles');
const files = (opt.ignoreFiles || opt.isIncremental) ? [] : await getFrontendBundleFiles(app, opt);
await utils.delay(100);
console.timeEnd('getFrontendBundleFiles');
// 增量发布需要组合生成器上下文数据
if (opt.isIncremental) {
body.generatorConfig = (0, utils_1.encodeBundleFileGeneratorConfig)({
...opt.appInfo,
...opt,
appid: app.id,
devDnsAddr: app.devDnsAddr,
miniEnable: app.miniEnable,
});
}
// nodejs 环境下的极速开发模式,在某些情况下字符会发生错误,需要转译
if (utils.isNode) {
files.forEach((file) => {
file.name = encodeURIComponent(file.name);
});
}
if (!opt?.isBackendValidation) {
body.callLogicValidations = callLogicValidations;
}
// Add memory-aware delay to allow GC
await (0, memory_optimization_1.smartDelay)(50);
const exportShareContent = (0, share_content_1.genShareContent)(app);
Object.assign(body, {
exportShareContent: Object.keys(exportShareContent.dependOn).length ? exportShareContent : null,
});
return body;
}
exports.genReleaseBody = genReleaseBody;
// 相对 genReleaseBody 函数,genReleaseBodyForParallel 需要额外返回调用参数 app/opt,以供并行发布中的前端编译使用
async function genReleaseBodyForParallel(app, opt) {
const body = await genReleaseBody(app, opt);
return {
releaseBody: body,
app,
opt
};
}
exports.genReleaseBodyForParallel = genReleaseBodyForParallel;
/**
* 替换端和业务组件的资源前缀, 提供给generator-fe调用
* 放在这里是为
* 1、版本控制
* 2、避免与replaceFrontendTypesAssets分散在两个地方
*/
function replaceAssetsPrefix(frontends, prefix) {
const callback = (node, key, val, prefix) => {
const sysPath = prefix !== '/' ? prefix : '';
if (!sysPath)
return;
// 增加处理两种情况 1 css 自带图片; 2 src 属性带的图片 3 用户上传的icon name 属性
if (['staticStyle'].includes(key) && /url\((['"])?\/assets\//.test(val)) {
// "background: url('/assets/login-bg-1.jpg') no-repeat; background-size: cover; height: 100vh;"
node[key] = val.replace(/url\((['"])?\/assets\//, `url($1${sysPath}/assets/`);
}
else if (/^\/assets\/(.*?)$/.test(val)) {
/**
* 这里会有一些极端场景,比如:用户在文本内容属性 设置了 /assets/xxx
* 这种情况下,直接替换会导致路径错误,但概率很小,先不考虑吧
* 最好就是这块逻辑也有replaceAssets的匹配逻辑
*/
node[key] = `${sysPath}${val}`;
}
};
frontends?.forEach((item) => {
item.traverseStrictChildren((node) => {
const meta = (0, concepts_1.getConceptMeta)(node.concept);
for (const key of meta.normalProperties) {
const val = node[key];
if (typeof val === 'string') {
callback(node, key, val, prefix);
}
}
});
});
}
exports.replaceAssetsPrefix = replaceAssetsPrefix;
//# sourceMappingURL=body.js.map