@blocklet/meta 1.8.39 → 1.8.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ declare const checkLink: (value: any) => boolean;
2
+ /**
3
+ *
4
+ * @param {object} tree 需要深度遍历的数状结构
5
+ * @param {function} cb 每一个节点的回调函数,回调的参数:当前节点数据,父亲节点数据,{index:当前节点所属的数组中的序号,level: 当前节点所处的深度}
6
+ * @param {object} param 深度遍历的参数
7
+ * @param {string} param.key 孩子数组的 key 值
8
+ * @param {number} param.index 当前节点所属的数组中的序号
9
+ * @param {object} param.parent 当前节点的父节点
10
+ * @param {number} param.level 当前节点的深度
11
+ */
12
+ declare function deepWalk(tree: any, cb?: () => void, { key, order }?: {
13
+ key?: string;
14
+ order?: string;
15
+ }): void;
16
+ /**
17
+ * 判断一个传入值是否属于一个 section
18
+ * @param {string | array} sections 需要判断的对象
19
+ * @param {string} section 目标 section
20
+ */
21
+ declare function isMatchSection(sections: any, section: any): boolean;
22
+ declare function joinLink(navigation: any, components: any): any;
23
+ /**
24
+ * 将树状结构的导航列表进行扁平化处理
25
+ * @param {array} navigationList 树状结构的导航列表
26
+ * @param {object} params 配置参数
27
+ * @param {number} params.depth 扁平化的层级,默认为 1(全拍平)
28
+ * @param {function} params.transform 当发生拍平处理时
29
+ * @returns 扁平化后的导航列表
30
+ */
31
+ declare function flatternNavigation(list?: any[], { depth, transform }?: {
32
+ depth?: number;
33
+ transform?: (v: any) => any;
34
+ }): any[];
35
+ /**
36
+ * 根据导航中每一个子菜单所属的 section,将原由的导航数据分离为多个导航数据(此时每一个导航 item 只会包含一个 section)
37
+ * @param {array} navigation 导航列表数据(树状结构,目前只适用于两层的树状结构)
38
+ * @returns
39
+ */
40
+ declare function splitNavigationBySection(navigation: any): any[];
41
+ /**
42
+ * 将导航数据进行层叠处理
43
+ * @param {array} list 扁平化的导航数据
44
+ * @returns 处理后的导航
45
+ */
46
+ declare function nestNavigationList(list?: any[]): any[];
47
+ declare function filterNavigation(navigationList: any, components?: any[]): any[];
48
+ declare function parseNavigation(blocklet?: {}, options?: {}): {
49
+ navigationList: any[];
50
+ components: any[];
51
+ builtinList: any[];
52
+ };
53
+ export { parseNavigation, deepWalk, isMatchSection, nestNavigationList, filterNavigation, joinLink, checkLink, flatternNavigation, splitNavigationBySection, };
@@ -0,0 +1,523 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.splitNavigationBySection = exports.flatternNavigation = exports.checkLink = exports.joinLink = exports.filterNavigation = exports.nestNavigationList = exports.isMatchSection = exports.deepWalk = exports.parseNavigation = void 0;
8
+ const unionWith_1 = __importDefault(require("lodash/unionWith"));
9
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
10
+ const pick_1 = __importDefault(require("lodash/pick"));
11
+ const isNil_1 = __importDefault(require("lodash/isNil"));
12
+ const omit_1 = __importDefault(require("lodash/omit"));
13
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
14
+ const url_join_1 = __importDefault(require("url-join"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const is_absolute_url_1 = __importDefault(require("is-absolute-url"));
17
+ const lodash_1 = require("lodash");
18
+ const DEFAULT_SECTION = 'header';
19
+ const BASE_PATH = '/';
20
+ const DEFAULT_LINK = '/';
21
+ const ID_SEPARATE = '/';
22
+ /**
23
+ * 判断一个 url 是否为合法的 url 拼接路径
24
+ * /abc, /abc/bcd valid
25
+ * /abc, /abc//bcd invalid
26
+ * @param value 需要检查的 url path
27
+ * @returns boolean
28
+ */
29
+ const checkUrlPath = (value) => {
30
+ return /^\/(?:[^/]+\/)*$/.test(value) || /^\/(?:[^/]+\/)*[^/]+$/.test(value);
31
+ };
32
+ const checkLink = (value) => {
33
+ if ((0, is_absolute_url_1.default)(value) || checkUrlPath(value)) {
34
+ return true;
35
+ }
36
+ return false;
37
+ };
38
+ exports.checkLink = checkLink;
39
+ /**
40
+ *
41
+ * @param {object} tree 需要深度遍历的数状结构
42
+ * @param {function} cb 每一个节点的回调函数,回调的参数:当前节点数据,父亲节点数据,{index:当前节点所属的数组中的序号,level: 当前节点所处的深度}
43
+ * @param {object} param 深度遍历的参数
44
+ * @param {string} param.key 孩子数组的 key 值
45
+ * @param {number} param.index 当前节点所属的数组中的序号
46
+ * @param {object} param.parent 当前节点的父节点
47
+ * @param {number} param.level 当前节点的深度
48
+ */
49
+ function deepWalk(tree, cb = () => { }, { key = 'children', order = 'first' } = {}) {
50
+ function walk(current, { index = 0, parent = null, level = 0 } = {}) {
51
+ if (Array.isArray(current)) {
52
+ current.forEach((item, i) => {
53
+ walk(item, { index: i, parent, level: level + 1 });
54
+ });
55
+ }
56
+ else if (current) {
57
+ if (order === 'first') {
58
+ cb(current, parent, { index, level });
59
+ }
60
+ walk(current[key], { parent: current, level });
61
+ if (order === 'last') {
62
+ cb(current, parent, { index, level });
63
+ }
64
+ }
65
+ }
66
+ walk(tree);
67
+ }
68
+ exports.deepWalk = deepWalk;
69
+ /**
70
+ * 判断一个传入值是否属于一个 section
71
+ * @param {string | array} sections 需要判断的对象
72
+ * @param {string} section 目标 section
73
+ */
74
+ function isMatchSection(sections, section) {
75
+ if (section === DEFAULT_SECTION && (0, isNil_1.default)(sections)) {
76
+ return true;
77
+ }
78
+ if (Array.isArray(sections)) {
79
+ return sections.includes(section);
80
+ }
81
+ return sections === section;
82
+ }
83
+ exports.isMatchSection = isMatchSection;
84
+ function tryParseItem(item) {
85
+ try {
86
+ return JSON.parse(item);
87
+ }
88
+ catch (_a) {
89
+ return item;
90
+ }
91
+ }
92
+ function normalizeNavigationList(navigationList) {
93
+ return navigationList.map((item) => {
94
+ const tempData = Object.assign({}, item);
95
+ if (tempData.role) {
96
+ tempData.role = tryParseItem(tempData.role);
97
+ }
98
+ if (tempData.section) {
99
+ tempData.section = tryParseItem(tempData.section);
100
+ }
101
+ if (tempData.title) {
102
+ tempData.title = tryParseItem(tempData.title);
103
+ }
104
+ if (tempData.link) {
105
+ tempData.link = tryParseItem(tempData.link);
106
+ }
107
+ return tempData;
108
+ });
109
+ }
110
+ function optionalJoin(prefix = '/', url = '') {
111
+ if ((0, is_absolute_url_1.default)(url || '')) {
112
+ return url;
113
+ }
114
+ // remove last slash
115
+ const resultUrl = path_1.default.join(prefix, url || DEFAULT_LINK);
116
+ if (resultUrl.length > 1 && resultUrl.endsWith('/')) {
117
+ return resultUrl.slice(0, resultUrl.length - 1);
118
+ }
119
+ return resultUrl;
120
+ }
121
+ function smartJoinLink(_parentLink, _childLink, { strict = true } = {}) {
122
+ let parentLink = _parentLink;
123
+ let childLink = _childLink;
124
+ if (!strict) {
125
+ parentLink = parentLink || '/';
126
+ childLink = childLink || '/';
127
+ }
128
+ if ((0, lodash_1.isObject)(parentLink) && (0, lodash_1.isString)(childLink) && checkLink(childLink)) {
129
+ return Object.keys(parentLink).reduce((res, key) => {
130
+ res[key] = optionalJoin(parentLink[key], childLink);
131
+ return res;
132
+ }, {});
133
+ }
134
+ if ((0, lodash_1.isString)(parentLink) && checkLink(parentLink) && (0, lodash_1.isObject)(childLink)) {
135
+ return Object.keys(childLink).reduce((res, key) => {
136
+ res[key] = optionalJoin(parentLink, childLink[key]);
137
+ return res;
138
+ }, {});
139
+ }
140
+ if ((0, lodash_1.isString)(parentLink) && (0, lodash_1.isString)(childLink)) {
141
+ if (checkLink(parentLink) || checkLink(childLink)) {
142
+ return optionalJoin(parentLink, childLink);
143
+ }
144
+ return childLink;
145
+ }
146
+ if ((0, lodash_1.isObject)(parentLink) && (0, lodash_1.isObject)(childLink)) {
147
+ const keys = [...new Set([...Object.keys(parentLink), ...Object.keys(childLink)])];
148
+ return keys.reduce((res, key) => {
149
+ res[key] = optionalJoin(parentLink[key], childLink[key]);
150
+ return res;
151
+ }, {});
152
+ }
153
+ return childLink;
154
+ }
155
+ function joinLink(navigation, components) {
156
+ const copyNavigation = (0, cloneDeep_1.default)(navigation);
157
+ deepWalk(copyNavigation, (item, parent) => {
158
+ const component = item.component || item.child;
159
+ if (component) {
160
+ const findComponent = components.find((v) => v.name === component);
161
+ if (findComponent) {
162
+ item.link = smartJoinLink(findComponent.link, item.link, { strict: false });
163
+ }
164
+ }
165
+ else if (parent) {
166
+ item.link = smartJoinLink(parent.link, item.link);
167
+ }
168
+ }, { key: 'items' });
169
+ return copyNavigation;
170
+ }
171
+ exports.joinLink = joinLink;
172
+ /**
173
+ * 将树状结构的导航列表进行扁平化处理
174
+ * @param {array} navigationList 树状结构的导航列表
175
+ * @param {object} params 配置参数
176
+ * @param {number} params.depth 扁平化的层级,默认为 1(全拍平)
177
+ * @param {function} params.transform 当发生拍平处理时
178
+ * @returns 扁平化后的导航列表
179
+ */
180
+ function flatternNavigation(list = [], { depth = 1, transform = (v) => v } = {}) {
181
+ const copyList = (0, cloneDeep_1.default)(list);
182
+ const finalList = [];
183
+ deepWalk(copyList, (item, parent, { level }) => {
184
+ if (level >= depth) {
185
+ const { items = [] } = item;
186
+ if (items && Array.isArray(items) && items.length > 0) {
187
+ delete item.items;
188
+ const transformedItems = items.map((v) => transform(v, item));
189
+ if (parent) {
190
+ parent.items.push(...transformedItems);
191
+ }
192
+ else {
193
+ const tmpItem = transform((0, omit_1.default)(item, ['items']), parent);
194
+ finalList.push(tmpItem, ...transformedItems);
195
+ }
196
+ }
197
+ else if (level === 1) {
198
+ const tmpItem = transform((0, omit_1.default)(item, ['items']), parent);
199
+ finalList.push(tmpItem);
200
+ }
201
+ }
202
+ else if (level === 1) {
203
+ finalList.push(item);
204
+ }
205
+ }, { key: 'items', order: 'last' });
206
+ return finalList;
207
+ }
208
+ exports.flatternNavigation = flatternNavigation;
209
+ /**
210
+ * 将 blocklet 中的数据进行处理,获得当前应用的导航数据及组件数据
211
+ * @param {object} blocklet blocklet 应用实例对象
212
+ * @returns 导航数据及组件数据
213
+ */
214
+ function parseBlockletNavigationList(blocklet = {}) {
215
+ const components = [];
216
+ /**
217
+ *
218
+ * @param {object} current 当前 blocklet 的数据
219
+ * @param {object} parent 当前 blocklet 的父组件数据
220
+ * @returns
221
+ */
222
+ function genNavigationListByBlocklet(current, parent = {}) {
223
+ var _a, _b, _c, _d, _e, _f, _g;
224
+ const targetList = [];
225
+ const { children = [], meta = {} } = current;
226
+ const navigation = (0, cloneDeep_1.default)((meta === null || meta === void 0 ? void 0 : meta.navigation) || []);
227
+ if (((_b = (_a = current.meta) === null || _a === void 0 ? void 0 : _a.capabilities) === null || _b === void 0 ? void 0 : _b.navigation) !== false) {
228
+ targetList.push(...navigation);
229
+ }
230
+ const parentName = parent.name || '';
231
+ const parentBase = parent.mountPoint || BASE_PATH;
232
+ const currentName = current === blocklet ? '' : meta.name || '';
233
+ const currentBase = current === blocklet ? '' : current.mountPoint || BASE_PATH;
234
+ for (const child of children) {
235
+ const childName = child.meta.name;
236
+ const childBase = child.mountPoint;
237
+ const mergeName = [parentName, currentName, childName].filter(Boolean).join('.');
238
+ const childNavigation = child.meta.navigation || [];
239
+ const mergeBase = (0, url_join_1.default)(parentBase, currentBase, childBase);
240
+ if (((_d = (_c = child.meta) === null || _c === void 0 ? void 0 : _c.capabilities) === null || _d === void 0 ? void 0 : _d.navigation) !== false) {
241
+ components.push({
242
+ did: child.meta.did,
243
+ name: mergeName,
244
+ link: mergeBase,
245
+ title: child.meta.title || '',
246
+ navigation: childNavigation.map((item) => (Object.assign({
247
+ // 给每个 navigation 赋予一个 setion,用于在 autocomplete 提供依据 section 筛选的基础
248
+ section: DEFAULT_SECTION }, item))),
249
+ });
250
+ }
251
+ // 在现有的 navigation 中判断是否存在 children
252
+ const matchNavigation = navigation.find((item) => {
253
+ if (item.component) {
254
+ return item.component === childName;
255
+ }
256
+ return false;
257
+ });
258
+ // 如果存在,并且当前 navigation 未配置任何 link,则将 child mountpoint 给它
259
+ if (matchNavigation) {
260
+ if (!matchNavigation.link) {
261
+ if (child.meta.navigation && child.meta.navigation.length > 0) {
262
+ const items = genNavigationListByBlocklet(child, { mountPoint: currentBase, name: currentName });
263
+ if (items.length > 0) {
264
+ matchNavigation.items = (_e = matchNavigation.items) !== null && _e !== void 0 ? _e : [];
265
+ matchNavigation.items.push(...items);
266
+ }
267
+ }
268
+ else {
269
+ matchNavigation.link = DEFAULT_LINK;
270
+ }
271
+ }
272
+ }
273
+ else if (((_g = (_f = child.meta) === null || _f === void 0 ? void 0 : _f.capabilities) === null || _g === void 0 ? void 0 : _g.navigation) !== false) {
274
+ const childItems = genNavigationListByBlocklet(child, { mountPoint: currentBase, name: currentName });
275
+ // 否则动态注入一个 navigation
276
+ const tmpData = {
277
+ title: child.meta.title,
278
+ component: childName,
279
+ // 动态注入的 navigation 需要一个默认的 id,blocklet.meta.id 是唯一的,可以用上这个值
280
+ id: child.meta.did,
281
+ };
282
+ if (childItems.length > 0) {
283
+ tmpData.items = childItems;
284
+ tmpData.link = undefined;
285
+ }
286
+ else {
287
+ tmpData.link = DEFAULT_LINK;
288
+ }
289
+ targetList.push(tmpData);
290
+ }
291
+ }
292
+ return targetList;
293
+ }
294
+ const navigationList = genNavigationListByBlocklet(blocklet);
295
+ return {
296
+ navigationList,
297
+ components,
298
+ };
299
+ }
300
+ function patchBuiltinNavigation(navigation) {
301
+ const copyNavigation = (0, cloneDeep_1.default)(navigation).filter((item) => item.id);
302
+ deepWalk(copyNavigation, (item, parent) => {
303
+ var _a;
304
+ // item.id = item.component || JSON.stringify(item.title) || nanoid();
305
+ if (item.items && item.items.length) {
306
+ for (let i = item.items.length - 1; i >= 0; i--) {
307
+ if (!item.items[i].id) {
308
+ item.items.splice(i, 1);
309
+ }
310
+ }
311
+ // 如果 items 全都不符合规范,保留 item 本身,为其增加一个默认的链接
312
+ if (item.items.length === 0) {
313
+ if (item.component) {
314
+ item.link = item.link || DEFAULT_LINK;
315
+ }
316
+ }
317
+ }
318
+ if (parent) {
319
+ item.parent = parent.id;
320
+ // 由于默认设置的 id(在 blocklet.yml 中手动赋予的存在重复的可能性比较大,所以通过 `/` 拼接父节点的 id,可以大大降低重复的概率)
321
+ item.id = [parent.id, item.id].join(ID_SEPARATE);
322
+ }
323
+ item.from = item.from || 'yaml';
324
+ item.visible = (_a = item.visible) !== null && _a !== void 0 ? _a : true;
325
+ }, { key: 'items' });
326
+ return copyNavigation;
327
+ }
328
+ /**
329
+ * 将多层结构的导航列表压缩至指定最大深度的树状结构
330
+ * @param {array} navigation 树状结构的导航列表数据
331
+ * @param {number} depth 压缩的层级
332
+ * @returns 压缩后的树状结构导航列表数据
333
+ */
334
+ function compactNavigation(navigation, depth = 2) {
335
+ const copyNavigation = (0, cloneDeep_1.default)(navigation);
336
+ const resData = flatternNavigation(copyNavigation, {
337
+ depth,
338
+ transform: (item, parent) => {
339
+ var _a;
340
+ if (parent) {
341
+ if (!item._parent) {
342
+ item._parent = parent.id;
343
+ if (!parent.component) {
344
+ item.link = smartJoinLink(parent.link, item.link);
345
+ }
346
+ }
347
+ item.section = item.section || parent.section || [DEFAULT_SECTION];
348
+ item.visible = (_a = item.visible) !== null && _a !== void 0 ? _a : parent.visible;
349
+ }
350
+ item.component = [parent.component, item.component].filter(Boolean).join('.');
351
+ return item;
352
+ },
353
+ });
354
+ deepWalk(resData, (item) => {
355
+ if (item.items && item.items.length > 0) {
356
+ item.items.reduceRight((all, cur) => {
357
+ if (cur._parent) {
358
+ const index = item.items.findIndex((v) => v.id === cur._parent);
359
+ if (index >= 0) {
360
+ item.items.splice(index, 1);
361
+ }
362
+ delete cur._parent;
363
+ }
364
+ return null;
365
+ }, null);
366
+ }
367
+ }, { key: 'items' });
368
+ return resData;
369
+ }
370
+ /**
371
+ * 返回指定的导航中的子菜单(属于指定 section)
372
+ * @param {object} navigationItem 指定的某一个导航数据
373
+ * @param {string} section 指定的 section 区域
374
+ * @returns
375
+ */
376
+ function getNavigationListBySection(navigationItem, section) {
377
+ if (section && Array.isArray(navigationItem.items)) {
378
+ return navigationItem.items
379
+ .filter((item) => {
380
+ // 如果当前子菜单没有 section,它的 section 应该跟随父菜单的 section
381
+ if ((0, isNil_1.default)(item.section)) {
382
+ return isMatchSection(navigationItem.section, section);
383
+ }
384
+ if (isMatchSection(item.section, section)) {
385
+ return true;
386
+ }
387
+ return false;
388
+ })
389
+ .map((item) => (Object.assign(Object.assign({}, item), { section })));
390
+ }
391
+ return [];
392
+ }
393
+ /**
394
+ * 根据导航中每一个子菜单所属的 section,将原由的导航数据分离为多个导航数据(此时每一个导航 item 只会包含一个 section)
395
+ * @param {array} navigation 导航列表数据(树状结构,目前只适用于两层的树状结构)
396
+ * @returns
397
+ */
398
+ function splitNavigationBySection(navigation) {
399
+ const copyNavigation = (0, cloneDeep_1.default)(navigation);
400
+ const allNavigationList = [];
401
+ for (const navigationItem of copyNavigation) {
402
+ const baseNavigation = (0, cloneDeep_1.default)((0, omit_1.default)(navigationItem, ['items']));
403
+ const itemNavigationList = [];
404
+ // eslint-disable-next-line no-inner-declarations
405
+ function patchNavigationItem(item, section) {
406
+ const sectionNavigationList = getNavigationListBySection(item, section);
407
+ itemNavigationList.push(Object.assign(Object.assign({}, baseNavigation), { section, items: sectionNavigationList }));
408
+ }
409
+ if (Array.isArray(navigationItem.section)) {
410
+ for (const section of navigationItem.section) {
411
+ patchNavigationItem(navigationItem, section);
412
+ }
413
+ }
414
+ else if (navigationItem.section) {
415
+ const { section } = navigationItem;
416
+ patchNavigationItem(navigationItem, section);
417
+ }
418
+ else if (navigationItem.items && navigationItem.items.length > 0) {
419
+ const allSectionList = navigationItem.items.reduce((list, currentValue) => {
420
+ const { section = [DEFAULT_SECTION] } = currentValue || {};
421
+ list.push(...section);
422
+ return list;
423
+ }, []);
424
+ const sectionList = [...new Set(allSectionList)];
425
+ for (const section of sectionList) {
426
+ patchNavigationItem(navigationItem, section);
427
+ }
428
+ }
429
+ else {
430
+ itemNavigationList.push(Object.assign(Object.assign({}, navigationItem), { section: DEFAULT_SECTION }));
431
+ }
432
+ allNavigationList.push(...itemNavigationList);
433
+ }
434
+ return allNavigationList;
435
+ }
436
+ exports.splitNavigationBySection = splitNavigationBySection;
437
+ /**
438
+ * 将导航数据进行层叠处理
439
+ * @param {array} list 扁平化的导航数据
440
+ * @returns 处理后的导航
441
+ */
442
+ function nestNavigationList(list = []) {
443
+ const cloneList = (0, cloneDeep_1.default)(list);
444
+ cloneList.reduceRight((res, item, index) => {
445
+ if (item.parent) {
446
+ const parent = cloneList.find((i) => {
447
+ if (item.section && i.section) {
448
+ return i.section === item.section && i.id === item.parent;
449
+ }
450
+ return i.id === item.parent;
451
+ });
452
+ if (parent) {
453
+ if (!parent.items)
454
+ parent.items = [];
455
+ // 由于 reduceRight 是从后向前遍历,所以后遍历到的其实顺序在前面
456
+ parent.items = [item, ...parent.items];
457
+ }
458
+ cloneList.splice(index, 1);
459
+ }
460
+ return null;
461
+ }, null);
462
+ return cloneList;
463
+ }
464
+ exports.nestNavigationList = nestNavigationList;
465
+ function filterNavigation(navigationList, components = []) {
466
+ const fullNavigation = nestNavigationList(navigationList);
467
+ deepWalk(fullNavigation, (item) => {
468
+ if (item === null || item === void 0 ? void 0 : item.component) {
469
+ if (!components.some((x) => x.name === item.component)) {
470
+ item.visible = false;
471
+ }
472
+ }
473
+ if (item.items && item.items.length) {
474
+ for (let i = item.items.length - 1; i >= 0; i--) {
475
+ if (item.items[i].visible === false) {
476
+ item.items.splice(i, 1);
477
+ }
478
+ }
479
+ }
480
+ }, { key: 'items' });
481
+ return fullNavigation.filter((item) => item.visible !== false);
482
+ }
483
+ exports.filterNavigation = filterNavigation;
484
+ function parseNavigation(blocklet = {}, options = {}) {
485
+ var _a;
486
+ const { beforeProcess = (v) => v } = options;
487
+ const { navigationList: builtinNavigation, components } = parseBlockletNavigationList(blocklet);
488
+ const customNavigationList = ((_a = blocklet === null || blocklet === void 0 ? void 0 : blocklet.settings) === null || _a === void 0 ? void 0 : _a.navigations) || [];
489
+ const compactedNavigation = compactNavigation(beforeProcess(builtinNavigation));
490
+ const patchedNavigation = patchBuiltinNavigation(compactedNavigation);
491
+ const splitNavigation = splitNavigationBySection(patchedNavigation);
492
+ // 将 footer-social, footer-bottom, sessionManager 的二级菜单提升为一级菜单
493
+ const levelUpNavigation = splitNavigation.reduce((all, cur) => {
494
+ if (['bottom', 'social', 'sessionManager'].includes(cur.section)) {
495
+ if (cur.items && cur.items.length > 0) {
496
+ all.push(...cur.items.map((x) => {
497
+ const { section } = cur;
498
+ const link = smartJoinLink(cur.link, x.link);
499
+ const component = [cur.component, x.component].filter(Boolean).join('.');
500
+ return Object.assign(Object.assign({}, x), { section, link, component, parent: '' });
501
+ }));
502
+ return all;
503
+ }
504
+ }
505
+ all.push(cur);
506
+ return all;
507
+ }, []);
508
+ const flatNavigation = flatternNavigation(levelUpNavigation, {
509
+ transform(item, parent) {
510
+ let { component } = item;
511
+ if (parent === null || parent === void 0 ? void 0 : parent.component) {
512
+ component = [parent.component, item.component].filter(Boolean).join('.');
513
+ }
514
+ return Object.assign(Object.assign({}, item), { component });
515
+ },
516
+ });
517
+ const rawNavigation = (0, unionWith_1.default)(normalizeNavigationList(customNavigationList), flatNavigation, (prev, next) => {
518
+ const keys = ['id', 'section'];
519
+ return (0, isEqual_1.default)((0, pick_1.default)(prev, keys), (0, pick_1.default)(next, keys));
520
+ });
521
+ return { navigationList: rawNavigation, components, builtinList: flatNavigation };
522
+ }
523
+ exports.parseNavigation = parseNavigation;
@@ -46,6 +46,7 @@ const doParseNavigation = (navigation, blocklet, prefix = '/', _level = 1) => {
46
46
  }
47
47
  const item = {
48
48
  title: nav.title,
49
+ id: undefined,
49
50
  };
50
51
  if (nav.section) {
51
52
  item.section = nav.section;
@@ -82,6 +83,7 @@ const doParseNavigation = (navigation, blocklet, prefix = '/', _level = 1) => {
82
83
  const childTitle = child.meta.title || child.meta.name;
83
84
  const itemProto = {
84
85
  title: nav.title || childTitle,
86
+ id: undefined,
85
87
  };
86
88
  if (nav.section) {
87
89
  itemProto.section = nav.section;
package/lib/schema.js CHANGED
@@ -21,10 +21,12 @@ const joi_1 = __importDefault(require("joi"));
21
21
  const cjk_length_1 = __importDefault(require("cjk-length"));
22
22
  const is_glob_1 = __importDefault(require("is-glob"));
23
23
  const joi_extension_semver_1 = require("joi-extension-semver");
24
+ const is_var_name_1 = __importDefault(require("is-var-name"));
24
25
  const did_1 = __importDefault(require("./did"));
25
26
  const extension_1 = require("./extension");
26
27
  const name_1 = require("./name");
27
28
  const constants_1 = __importDefault(require("./constants"));
29
+ const parse_navigation_from_blocklet_1 = require("./parse-navigation-from-blocklet");
28
30
  const cjkLength = cjk_length_1.default.default;
29
31
  const { BLOCKLET_GROUPS, BLOCKLET_PLATFORMS, BLOCKLET_ARCHITECTURES, BLOCKLET_INTERFACE_TYPES, BLOCKLET_INTERFACE_PROTOCOLS, BLOCKLET_ENTRY_FILE, BLOCKLET_BUNDLE_FILE, BLOCKLET_DEFAULT_PORT_NAME, BLOCKLET_DYNAMIC_PATH_PREFIX, BlockletGroup, BLOCKLET_LATEST_REQUIREMENT_SERVER, BLOCKLET_INTERFACE_TYPE_WEB, BLOCKLET_INTERFACE_TYPE_WELLKNOWN, BLOCKLET_APP_SPACE_ENDPOINTS, BLOCKLET_CONFIGURABLE_KEY, } = constants_1.default;
30
32
  const WELLKNOWN_PATH_PREFIX = '/.well-known';
@@ -34,6 +36,20 @@ const Joi = joi_1.default.extend(joi_extension_semver_1.semver)
34
36
  .extend(joi_extension_semver_1.semverRange)
35
37
  .extend(extension_1.fileExtension)
36
38
  .extend(extension_1.didExtension);
39
+ const checkLinkHelper = (value, helper) => {
40
+ if ((0, parse_navigation_from_blocklet_1.checkLink)(value)) {
41
+ return value;
42
+ }
43
+ // @ts-expect-error
44
+ return helper.message('Invalid navigation link');
45
+ };
46
+ const checkId = (value, helper) => {
47
+ if (!value || (0, is_var_name_1.default)(value)) {
48
+ return value;
49
+ }
50
+ // @ts-expect-error
51
+ return helper.message('Invalid navigation id');
52
+ };
37
53
  const titleSchema = Joi.string()
38
54
  .trim()
39
55
  .min(1)
@@ -297,21 +313,22 @@ const signatureSchema = Joi.object({
297
313
  unknownType: 'any',
298
314
  });
299
315
  exports.signatureSchema = signatureSchema;
316
+ const localeList = ['en', 'zh', 'fr', 'ru', 'ar', 'es', 'de', 'pt', 'ja', 'hi'];
300
317
  const navigationItemProps = {
318
+ id: Joi.string().custom(checkId),
301
319
  title: Joi.alternatives()
302
- .try(Joi.string(), Joi.object({
303
- zh: Joi.string(),
304
- en: Joi.string(),
305
- }).min(1))
320
+ .try(Joi.string().min(1).max(MAX_TITLE_LENGTH), Joi.object()
321
+ .min(1)
322
+ .pattern(Joi.string().valid(...localeList), Joi.string().min(1).max(MAX_TITLE_LENGTH)))
306
323
  .required(),
307
- link: Joi.alternatives().try(Joi.string(), Joi.object({
308
- zh: Joi.string(),
309
- en: Joi.string(),
310
- }).min(1)),
324
+ link: Joi.alternatives().try(Joi.string().custom(checkLinkHelper), Joi.object()
325
+ .min(1)
326
+ .pattern(Joi.string().valid(...localeList), Joi.string().custom(checkLinkHelper))),
311
327
  component: Joi.string().min(1),
312
328
  section: Joi.array().items(Joi.string().min(1)).single(),
313
329
  role: Joi.array().items(Joi.string().min(1)).single(),
314
330
  icon: Joi.string().min(1),
331
+ visible: Joi.boolean(),
315
332
  };
316
333
  const navigationItemSchema = Joi.object(Object.assign(Object.assign({}, navigationItemProps), { items: Joi.array().items(Joi.object(Object.assign({}, navigationItemProps)).rename('child', 'component')) }))
317
334
  .rename('child', 'component')
@@ -320,7 +337,15 @@ const navigationItemSchema = Joi.object(Object.assign(Object.assign({}, navigati
320
337
  unknownType: 'any',
321
338
  });
322
339
  exports.navigationItemSchema = navigationItemSchema;
323
- const navigationSchema = Joi.array().items(navigationItemSchema).meta({
340
+ const navigationSchema = Joi.array()
341
+ .items(navigationItemSchema)
342
+ .unique((a, b) => {
343
+ if (a.id && b.id) {
344
+ return a.id === b.id;
345
+ }
346
+ return false;
347
+ })
348
+ .meta({
324
349
  className: 'TNavigation',
325
350
  unknownType: 'any',
326
351
  });
@@ -516,6 +541,7 @@ const blockletMetaProps = {
516
541
  didSpace: Joi.string()
517
542
  .valid(...Object.values(BLOCKLET_APP_SPACE_ENDPOINTS))
518
543
  .optional(),
544
+ navigation: Joi.boolean().default(true), // Should blocklet join navigation auto-merge process
519
545
  }).default({
520
546
  clusterMode: false,
521
547
  component: true,
@@ -574,9 +600,10 @@ const createBlockletSchema = (baseDir, _a = {}) => {
574
600
  .rename('children', 'components')
575
601
  .custom((data, helper) => {
576
602
  const { did, name } = data;
577
- if ((0, did_1.default)(name) !== did) {
603
+ const expectDid = (0, did_1.default)(name);
604
+ if (expectDid !== did) {
578
605
  // @ts-expect-error
579
- return helper.message(`name "${name}" does not match with did "${did}"`);
606
+ return helper.message(`the did of name "${name}" should be "${expectDid}"`);
580
607
  }
581
608
  return data;
582
609
  });
@@ -14,6 +14,7 @@ export interface TBlockletMeta {
14
14
  clusterMode?: boolean;
15
15
  component?: boolean;
16
16
  didSpace?: 'required' | 'optional';
17
+ navigation?: boolean;
17
18
  };
18
19
  community?: string;
19
20
  components?: TComponent[];
@@ -173,30 +174,42 @@ export declare type TNavigation = TNavigationItem[];
173
174
  export interface TNavigationItem {
174
175
  component?: string;
175
176
  icon?: string;
177
+ id?: string;
176
178
  items?: {
177
179
  component?: string;
178
180
  icon?: string;
181
+ id?: string;
179
182
  link?: string | {
180
- en?: string;
181
- zh?: string;
183
+ /**
184
+ * Unknown Property
185
+ */
186
+ [x: string]: string;
182
187
  };
183
188
  role?: string[];
184
189
  section?: string[];
185
190
  title: string | {
186
- en?: string;
187
- zh?: string;
191
+ /**
192
+ * Unknown Property
193
+ */
194
+ [x: string]: string;
188
195
  };
196
+ visible?: boolean;
189
197
  }[];
190
198
  link?: string | {
191
- en?: string;
192
- zh?: string;
199
+ /**
200
+ * Unknown Property
201
+ */
202
+ [x: string]: string;
193
203
  };
194
204
  role?: string[];
195
205
  section?: string[];
196
206
  title: string | {
197
- en?: string;
198
- zh?: string;
207
+ /**
208
+ * Unknown Property
209
+ */
210
+ [x: string]: string;
199
211
  };
212
+ visible?: boolean;
200
213
  }
201
214
  export interface TPerson {
202
215
  email?: string;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.39",
6
+ "version": "1.8.40",
7
7
  "description": "Library to parse/validate/fix blocklet meta",
8
8
  "main": "./lib/index.js",
9
9
  "typings": "./lib/index.d.ts",
@@ -24,26 +24,28 @@
24
24
  "author": "wangshijun <wangshijun2020@gmail.com> (http://github.com/wangshijun)",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@abtnode/client": "1.8.39",
28
- "@abtnode/constant": "1.8.39",
29
- "@abtnode/util": "1.8.39",
30
- "@arcblock/did": "1.18.18",
31
- "@arcblock/did-ext": "1.18.18",
32
- "@arcblock/did-util": "1.18.18",
33
- "@arcblock/jwt": "1.18.18",
34
- "@blocklet/constant": "1.8.39",
35
- "@ocap/asset": "1.18.18",
36
- "@ocap/mcrypto": "1.18.18",
37
- "@ocap/types": "1.18.18",
38
- "@ocap/util": "1.18.18",
39
- "@ocap/wallet": "1.18.18",
27
+ "@abtnode/client": "1.8.40",
28
+ "@abtnode/constant": "1.8.40",
29
+ "@abtnode/util": "1.8.40",
30
+ "@arcblock/did": "1.18.26",
31
+ "@arcblock/did-ext": "1.18.26",
32
+ "@arcblock/did-util": "1.18.26",
33
+ "@arcblock/jwt": "1.18.26",
34
+ "@blocklet/constant": "1.8.40",
35
+ "@ocap/asset": "1.18.26",
36
+ "@ocap/mcrypto": "1.18.26",
37
+ "@ocap/types": "1.18.26",
38
+ "@ocap/util": "1.18.26",
39
+ "@ocap/wallet": "1.18.26",
40
40
  "ajv": "^8.11.0",
41
41
  "axios": "^0.27.2",
42
42
  "cjk-length": "^1.0.0",
43
43
  "debug": "^4.3.4",
44
44
  "fs-extra": "^10.1.0",
45
45
  "hosted-git-info": "3.0.8",
46
+ "is-absolute-url": "^3.0.3",
46
47
  "is-glob": "^4.0.3",
48
+ "is-var-name": "^2.0.0",
47
49
  "joi": "17.6.3",
48
50
  "joi-extension-semver": "^5.0.0",
49
51
  "js-yaml": "^4.1.0",
@@ -56,7 +58,7 @@
56
58
  "validate-npm-package-name": "^3.0.0"
57
59
  },
58
60
  "devDependencies": {
59
- "@abtnode/client": "^1.8.37",
61
+ "@abtnode/client": "^1.8.39",
60
62
  "@arcblock/eslint-config-ts": "^0.2.3",
61
63
  "@types/express": "^4.17.14",
62
64
  "@types/jest": "^29.2.2",
@@ -77,5 +79,5 @@
77
79
  "ts-node": "^10.9.1",
78
80
  "typescript": "^4.8.4"
79
81
  },
80
- "gitHead": "cf2223c4d9a999993b2be1c943dd75b4221593c3"
82
+ "gitHead": "336dbe0ad2cf512a322358899e56aab3668b1715"
81
83
  }