@blocklet/pages-kit-block-studio 0.0.16 → 0.0.18
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.
- package/lib/cjs/constants/index.js +9 -0
- package/lib/cjs/middlewares/init-block-studio-router.js +61 -13
- package/lib/cjs/middlewares/init-resource-router.js +49 -2
- package/lib/cjs/middlewares/init-uploader-router.js +7 -6
- package/lib/cjs/plugins/vite-plugin-block-studio.js +4 -0
- package/lib/cjs/plugins/vite-plugin-html-transform.js +5 -0
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/utils/build-lib.js +1 -1
- package/lib/cjs/utils/generate-wrapper-code.js +145 -7
- package/lib/cjs/utils/helper.js +172 -6
- package/lib/esm/constants/index.js +6 -0
- package/lib/esm/middlewares/init-block-studio-router.js +62 -14
- package/lib/esm/middlewares/init-resource-router.js +50 -3
- package/lib/esm/middlewares/init-uploader-router.js +7 -6
- package/lib/esm/plugins/vite-plugin-block-studio.js +5 -1
- package/lib/esm/plugins/vite-plugin-html-transform.js +5 -2
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/utils/build-lib.js +1 -1
- package/lib/esm/utils/generate-wrapper-code.js +145 -7
- package/lib/esm/utils/helper.js +131 -4
- package/lib/types/constants/index.d.ts +6 -0
- package/lib/types/plugins/vite-plugin-html-transform.d.ts +14 -0
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/utils/helper.d.ts +18 -3
- package/package.json +3 -2
|
@@ -29,7 +29,7 @@ function buildLib(options) {
|
|
|
29
29
|
const filterModules = process.argv.includes('--filter')
|
|
30
30
|
? (_a = process.argv[process.argv.indexOf('--filter') + 1]) === null || _a === void 0 ? void 0 : _a.split(',')
|
|
31
31
|
: ((_b = process.env.BLOCK_FILTER) === null || _b === void 0 ? void 0 : _b.split(',')) || null;
|
|
32
|
-
const ignoreViteLog =
|
|
32
|
+
const ignoreViteLog = process.argv.includes('--log') ? false : process.env.BLOCK_LOG !== 'true';
|
|
33
33
|
const multiMode = process.argv.includes('--multi') || process.env.BLOCK_MULTI === 'true';
|
|
34
34
|
const blocks = allBlocks.filter((name) => !filterModules || filterModules.includes(name || ''));
|
|
35
35
|
if (!blocks.length) {
|
|
@@ -81,11 +81,13 @@ function generateWrapperCode(_a) {
|
|
|
81
81
|
},
|
|
82
82
|
}, null, 2);
|
|
83
83
|
const client = `\
|
|
84
|
-
import
|
|
84
|
+
import PageRenderComponent, { RuntimeProps, RuntimeType } from '@blocklet/pages-kit-runtime/client';
|
|
85
85
|
|
|
86
|
+
export type { RuntimeProps, RuntimeType };
|
|
86
87
|
|
|
88
|
+
${generatePageDataTypes(state)}
|
|
87
89
|
|
|
88
|
-
export default
|
|
90
|
+
export default PageRenderComponent;
|
|
89
91
|
`;
|
|
90
92
|
const index = client;
|
|
91
93
|
const middleware = `
|
|
@@ -205,10 +207,14 @@ export default router;
|
|
|
205
207
|
compilerOptions: Object.assign(Object.assign({}, compilerOptions), { module: ts.ModuleKind.ESNext }),
|
|
206
208
|
});
|
|
207
209
|
// Generate proper type declarations
|
|
208
|
-
const dts = ts.transpileModule(content, {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
// const dts = ts.transpileModule(content, {
|
|
211
|
+
// fileName,
|
|
212
|
+
// compilerOptions: {
|
|
213
|
+
// ...compilerOptions,
|
|
214
|
+
// declaration: true,
|
|
215
|
+
// emitDeclarationOnly: true,
|
|
216
|
+
// },
|
|
217
|
+
// });
|
|
212
218
|
return [
|
|
213
219
|
{
|
|
214
220
|
fileName: fileName.replace(/\.ts$/, '.cjs'),
|
|
@@ -220,11 +226,143 @@ export default router;
|
|
|
220
226
|
},
|
|
221
227
|
{
|
|
222
228
|
fileName: fileName.replace(/\.ts$/, '.d.ts'),
|
|
223
|
-
content
|
|
229
|
+
content, // Fallback to original content if declaration fails
|
|
224
230
|
},
|
|
225
231
|
];
|
|
226
232
|
})))).flat();
|
|
227
233
|
return [...result, { fileName: 'package.json', content: packageJson }];
|
|
228
234
|
});
|
|
229
235
|
}
|
|
236
|
+
const basicComponentSectionTypes = {
|
|
237
|
+
iframe: `
|
|
238
|
+
src: string;
|
|
239
|
+
title?: string;
|
|
240
|
+
description?: string;
|
|
241
|
+
`,
|
|
242
|
+
section: `
|
|
243
|
+
title?: string;
|
|
244
|
+
description?: string;
|
|
245
|
+
image?: string;
|
|
246
|
+
imageMeta?: { naturalWidth?: number; naturalHeight?: number; filename: string };
|
|
247
|
+
`,
|
|
248
|
+
'section-card-list': `
|
|
249
|
+
title?: string;
|
|
250
|
+
description?: string;
|
|
251
|
+
list: {
|
|
252
|
+
id: string;
|
|
253
|
+
title: string;
|
|
254
|
+
description: string;
|
|
255
|
+
image: string;
|
|
256
|
+
}[];
|
|
257
|
+
`,
|
|
258
|
+
toc: `
|
|
259
|
+
title?: string;
|
|
260
|
+
description?: string;
|
|
261
|
+
`,
|
|
262
|
+
};
|
|
263
|
+
function getCustomComponentPropertyType(type) {
|
|
264
|
+
if (!type)
|
|
265
|
+
return 'string';
|
|
266
|
+
if (type === 'string' || type === 'multiline')
|
|
267
|
+
return 'string';
|
|
268
|
+
if (type === 'url')
|
|
269
|
+
return '{ url: string; mediaKitUrl?: string; width?: number; height?: number }';
|
|
270
|
+
if (type === 'json')
|
|
271
|
+
return 'any';
|
|
272
|
+
if (type === 'yaml')
|
|
273
|
+
return 'any';
|
|
274
|
+
return 'any';
|
|
275
|
+
}
|
|
276
|
+
const ALLOWED_PROPERTY_TYPES = ['string', 'multiline', 'url', 'json', 'yaml'];
|
|
277
|
+
function generatePageDataTypes(state) {
|
|
278
|
+
const pageTypes = Object.values(state.pages)
|
|
279
|
+
.map((page) => {
|
|
280
|
+
// check if the page is a template page
|
|
281
|
+
if (!page.isTemplate)
|
|
282
|
+
return null;
|
|
283
|
+
// 将 slug 转换为有效的 TypeScript 类型名
|
|
284
|
+
const typeName = `Page${page.slug
|
|
285
|
+
.replace(/^\//, '')
|
|
286
|
+
.split('/')
|
|
287
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
288
|
+
.join('')
|
|
289
|
+
.replace(/[^a-zA-Z0-9]/g, '')}Data`;
|
|
290
|
+
// 收集所有 section 的配置信息
|
|
291
|
+
const sectionTypes = page.sectionIds
|
|
292
|
+
.map((sectionId) => {
|
|
293
|
+
var _a, _b, _c, _d;
|
|
294
|
+
const section = page.sections[sectionId];
|
|
295
|
+
if (!section)
|
|
296
|
+
return null;
|
|
297
|
+
// check if the section is a template section
|
|
298
|
+
if (!section.isTemplateSection)
|
|
299
|
+
return null;
|
|
300
|
+
if (section.component === 'custom-component') {
|
|
301
|
+
const componentId = (_a = section.config) === null || _a === void 0 ? void 0 : _a.componentId;
|
|
302
|
+
if (!componentId)
|
|
303
|
+
return null;
|
|
304
|
+
const component = ((_b = state.components[componentId]) === null || _b === void 0 ? void 0 : _b.data) || ((_d = (_c = state.resources.components) === null || _c === void 0 ? void 0 : _c[componentId]) === null || _d === void 0 ? void 0 : _d.component);
|
|
305
|
+
if (!component)
|
|
306
|
+
return null;
|
|
307
|
+
// 为自定义组件生成属性类型
|
|
308
|
+
const properties = Object.entries(component.properties || {})
|
|
309
|
+
.map(([, prop]) => {
|
|
310
|
+
var _a, _b, _c, _d, _e;
|
|
311
|
+
// check if the property type is allowed
|
|
312
|
+
if (((_a = prop.data) === null || _a === void 0 ? void 0 : _a.type) && !ALLOWED_PROPERTY_TYPES.includes((_b = prop.data) === null || _b === void 0 ? void 0 : _b.type))
|
|
313
|
+
return null;
|
|
314
|
+
// if key is undefined, use id
|
|
315
|
+
const key = ((_c = prop.data) === null || _c === void 0 ? void 0 : _c.key) || ((_d = prop.data) === null || _d === void 0 ? void 0 : _d.id);
|
|
316
|
+
if (!key)
|
|
317
|
+
return null;
|
|
318
|
+
return ` "${key}"?: ${getCustomComponentPropertyType((_e = prop.data) === null || _e === void 0 ? void 0 : _e.type)};`;
|
|
319
|
+
})
|
|
320
|
+
.filter(Boolean)
|
|
321
|
+
.join('\n');
|
|
322
|
+
return ` "${section.name || section.id}"?: {
|
|
323
|
+
${properties}
|
|
324
|
+
};`;
|
|
325
|
+
}
|
|
326
|
+
// basic component section types
|
|
327
|
+
if (basicComponentSectionTypes[section.component]) {
|
|
328
|
+
return ` "${section.name || section.id}"?: { ${basicComponentSectionTypes[section.component]} };`;
|
|
329
|
+
}
|
|
330
|
+
return ` "${section.name || section.id}"?: any;`;
|
|
331
|
+
})
|
|
332
|
+
.filter(Boolean)
|
|
333
|
+
.join('\n');
|
|
334
|
+
return `
|
|
335
|
+
export interface ${typeName} {
|
|
336
|
+
title?: string;
|
|
337
|
+
image?: string;
|
|
338
|
+
description?: string;
|
|
339
|
+
sectionsData: {
|
|
340
|
+
${sectionTypes}
|
|
341
|
+
};
|
|
342
|
+
}`;
|
|
343
|
+
})
|
|
344
|
+
.filter(Boolean)
|
|
345
|
+
.join('\n');
|
|
346
|
+
// 生成 PageData 类型的联合类型
|
|
347
|
+
const pageUnionTypes = Object.values(state.pages)
|
|
348
|
+
.map((page) => {
|
|
349
|
+
if (!page.isTemplate)
|
|
350
|
+
return null;
|
|
351
|
+
const typeName = `Page${page.slug
|
|
352
|
+
.replace(/^\//, '')
|
|
353
|
+
.split('/')
|
|
354
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
355
|
+
.join('')
|
|
356
|
+
.replace(/[^a-zA-Z0-9]/g, '')}Data`;
|
|
357
|
+
return typeName;
|
|
358
|
+
})
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.join(' | ');
|
|
361
|
+
return `
|
|
362
|
+
// 页面数据类型定义
|
|
363
|
+
${pageTypes}
|
|
364
|
+
|
|
365
|
+
// 所有页面数据类型的联合类型
|
|
366
|
+
export type PageDataUnion = ${pageUnionTypes || 'never'};`;
|
|
367
|
+
}
|
|
230
368
|
exports.default = generateWrapperCode;
|
package/lib/cjs/utils/helper.js
CHANGED
|
@@ -1,16 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
2
44
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
45
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
46
|
};
|
|
5
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isDev = exports.isPathSafe = exports.libDir = exports.logger = void 0;
|
|
48
|
+
exports.getBlockCode = exports.isPagesKitBlockStudio = exports.getPreviewImageRelativePath = exports.downloadAsset = exports.isMetadataFile = exports.isDev = exports.isPathSafe = exports.NANOID_LENGTH = exports.PREVIEW_IMAGE_DIR = exports.METADATA_FILE_NAME = exports.libDir = exports.logger = void 0;
|
|
7
49
|
exports.setBlockEntryFilesPattern = setBlockEntryFilesPattern;
|
|
8
50
|
exports.getBlockEntryFilesPattern = getBlockEntryFilesPattern;
|
|
9
51
|
exports.findComponentFiles = findComponentFiles;
|
|
10
52
|
exports.getBlockName = getBlockName;
|
|
11
|
-
exports.
|
|
53
|
+
exports.initializeMetadata = initializeMetadata;
|
|
54
|
+
exports.generateYaml = generateYaml;
|
|
55
|
+
exports.getBlockStudioInfo = getBlockStudioInfo;
|
|
56
|
+
const component_1 = require("@blocklet/sdk/lib/component");
|
|
57
|
+
const config_1 = __importDefault(require("@blocklet/sdk/lib/config"));
|
|
58
|
+
const fs_1 = __importDefault(require("fs"));
|
|
59
|
+
const promises_1 = require("fs/promises");
|
|
12
60
|
const glob_1 = require("glob");
|
|
13
|
-
const
|
|
61
|
+
const nanoid_1 = require("nanoid");
|
|
62
|
+
const path_1 = __importStar(require("path"));
|
|
63
|
+
const promises_2 = require("stream/promises");
|
|
64
|
+
const ufo_1 = require("ufo");
|
|
65
|
+
const yaml = __importStar(require("yaml"));
|
|
66
|
+
const constants_1 = require("../constants");
|
|
67
|
+
exports.logger = console;
|
|
68
|
+
exports.libDir = 'lib';
|
|
69
|
+
exports.METADATA_FILE_NAME = '@metadata.json';
|
|
70
|
+
exports.PREVIEW_IMAGE_DIR = '@preview-images';
|
|
71
|
+
exports.NANOID_LENGTH = 16;
|
|
14
72
|
const DEFAULT_BLOCK_ENTRY_FILES_PATTERN = 'src/**/index.{ts,tsx,html}';
|
|
15
73
|
if (!process.env.BLOCK_ENTRY_FILES_PATTERN) {
|
|
16
74
|
process.env.BLOCK_ENTRY_FILES_PATTERN = DEFAULT_BLOCK_ENTRY_FILES_PATTERN;
|
|
@@ -32,11 +90,13 @@ function findComponentFiles(options = {}) {
|
|
|
32
90
|
const blockName = getBlockName(file);
|
|
33
91
|
const fullPath = path_1.default.resolve(cwd, file);
|
|
34
92
|
const isHtml = file.endsWith('.html');
|
|
93
|
+
const metadata = initializeMetadata(fullPath);
|
|
35
94
|
return {
|
|
36
95
|
file,
|
|
37
96
|
blockName,
|
|
38
97
|
fullPath,
|
|
39
98
|
isHtml,
|
|
99
|
+
metadata,
|
|
40
100
|
};
|
|
41
101
|
})
|
|
42
102
|
.filter(({ blockName }) => !(filter === null || filter === void 0 ? void 0 : filter.length) || filter.includes(blockName || ''));
|
|
@@ -48,12 +108,14 @@ function getBlockName(entry) {
|
|
|
48
108
|
const [, pathWithoutIndex] = indexMatch;
|
|
49
109
|
return path_1.default.basename(pathWithoutIndex || 'unknown');
|
|
50
110
|
}
|
|
111
|
+
const matadataMatch = entry.match(/(.*)\/@metadata\.json$/);
|
|
112
|
+
if (matadataMatch) {
|
|
113
|
+
const [, pathWithoutMetadata] = matadataMatch;
|
|
114
|
+
return path_1.default.basename(pathWithoutMetadata || 'unknown');
|
|
115
|
+
}
|
|
51
116
|
// If not an index file, return the filename without extension
|
|
52
117
|
return path_1.default.basename(entry, path_1.default.extname(entry));
|
|
53
118
|
}
|
|
54
|
-
exports.logger = console;
|
|
55
|
-
function generateBlockYml() { }
|
|
56
|
-
exports.libDir = 'lib';
|
|
57
119
|
const isPathSafe = (filePath) => {
|
|
58
120
|
const normalizedPath = path_1.default.normalize(filePath);
|
|
59
121
|
const pwd = process.env.PWD || process.cwd();
|
|
@@ -61,3 +123,107 @@ const isPathSafe = (filePath) => {
|
|
|
61
123
|
};
|
|
62
124
|
exports.isPathSafe = isPathSafe;
|
|
63
125
|
exports.isDev = process.env.BLOCKLET_MODE === 'development';
|
|
126
|
+
const isMetadataFile = (filePath) => {
|
|
127
|
+
return filePath.endsWith(exports.METADATA_FILE_NAME);
|
|
128
|
+
};
|
|
129
|
+
exports.isMetadataFile = isMetadataFile;
|
|
130
|
+
const downloadAsset = (_a) => __awaiter(void 0, [_a], void 0, function* ({ asset, savePath, componentDid, }) {
|
|
131
|
+
if (!componentDid || !exports.isDev || !asset || !savePath) {
|
|
132
|
+
throw new Error('Invalid params');
|
|
133
|
+
}
|
|
134
|
+
// Check path safety
|
|
135
|
+
if (!(0, exports.isPathSafe)(savePath)) {
|
|
136
|
+
throw new Error('Invalid save path: path traversal detected');
|
|
137
|
+
}
|
|
138
|
+
const fileName = (0, path_1.basename)(asset);
|
|
139
|
+
// Ensure target directory exists
|
|
140
|
+
yield (0, promises_1.mkdir)(path_1.default.dirname(savePath), { recursive: true });
|
|
141
|
+
// download asset from pages-kit's /uploads
|
|
142
|
+
const res = yield (0, component_1.call)({
|
|
143
|
+
name: process.env.BLOCKLET_COMPONENT_DID,
|
|
144
|
+
path: (0, ufo_1.joinURL)('/uploads', fileName),
|
|
145
|
+
responseType: 'stream',
|
|
146
|
+
method: 'GET',
|
|
147
|
+
});
|
|
148
|
+
if (res.status >= 200 && res.status < 400) {
|
|
149
|
+
const file = fs_1.default.createWriteStream(savePath);
|
|
150
|
+
yield (0, promises_2.pipeline)(res.data, file);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
throw new Error(`download asset failed ${res.status}`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
exports.downloadAsset = downloadAsset;
|
|
157
|
+
const getPreviewImageRelativePath = (name) => {
|
|
158
|
+
return path_1.default.join(exports.PREVIEW_IMAGE_DIR, name);
|
|
159
|
+
};
|
|
160
|
+
exports.getPreviewImageRelativePath = getPreviewImageRelativePath;
|
|
161
|
+
function initializeMetadata(tempFilePath) {
|
|
162
|
+
let content = '';
|
|
163
|
+
if (!tempFilePath) {
|
|
164
|
+
throw new Error('File path is required');
|
|
165
|
+
}
|
|
166
|
+
// file path is metadata file
|
|
167
|
+
const filePath = (0, exports.isMetadataFile)(tempFilePath) ? tempFilePath : path_1.default.join((0, path_1.dirname)(tempFilePath), exports.METADATA_FILE_NAME);
|
|
168
|
+
if (!filePath) {
|
|
169
|
+
throw new Error('File path is required');
|
|
170
|
+
}
|
|
171
|
+
if (exports.isPagesKitBlockStudio && fs_1.default.existsSync(filePath)) {
|
|
172
|
+
content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
173
|
+
}
|
|
174
|
+
let metadata = {};
|
|
175
|
+
if (content) {
|
|
176
|
+
try {
|
|
177
|
+
metadata = JSON.parse(content);
|
|
178
|
+
}
|
|
179
|
+
catch (_a) {
|
|
180
|
+
// If parsing fails, use empty object
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Add id if not exists
|
|
184
|
+
if (!metadata.id) {
|
|
185
|
+
metadata.id = (0, nanoid_1.nanoid)(exports.NANOID_LENGTH);
|
|
186
|
+
}
|
|
187
|
+
// Add createdAt if not exists
|
|
188
|
+
if (!metadata.createdAt) {
|
|
189
|
+
metadata.createdAt = new Date().toISOString();
|
|
190
|
+
}
|
|
191
|
+
// Add updatedAt if not exists
|
|
192
|
+
if (!metadata.updatedAt) {
|
|
193
|
+
metadata.updatedAt = new Date().toISOString();
|
|
194
|
+
}
|
|
195
|
+
// Add name if not exists
|
|
196
|
+
if (!metadata.name) {
|
|
197
|
+
metadata.name = getBlockName(filePath);
|
|
198
|
+
}
|
|
199
|
+
if (exports.isPagesKitBlockStudio && !fs_1.default.existsSync(filePath)) {
|
|
200
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(metadata, null, 2));
|
|
201
|
+
}
|
|
202
|
+
return metadata;
|
|
203
|
+
}
|
|
204
|
+
function generateYaml(metadata) {
|
|
205
|
+
return yaml.stringify(metadata);
|
|
206
|
+
}
|
|
207
|
+
function getBlockStudioInfo() {
|
|
208
|
+
return config_1.default.components.find((c) => c.did === constants_1.PAGES_KIT_BLOCK_STUDIO_DID);
|
|
209
|
+
}
|
|
210
|
+
exports.isPagesKitBlockStudio = process.env.BLOCKLET_COMPONENT_DID === constants_1.PAGES_KIT_BLOCK_STUDIO_DID;
|
|
211
|
+
const getBlockCode = (filePath) => {
|
|
212
|
+
var _a;
|
|
213
|
+
// Check path safety first
|
|
214
|
+
if (!(0, exports.isPathSafe)(filePath)) {
|
|
215
|
+
throw new Error('Invalid file path: path traversal detected');
|
|
216
|
+
}
|
|
217
|
+
const blockName = getBlockName(filePath);
|
|
218
|
+
// Find the actual code file using the block entry pattern
|
|
219
|
+
const files = findComponentFiles({ filter: [blockName] });
|
|
220
|
+
if (!files.length) {
|
|
221
|
+
throw new Error(`No code file found for block: ${blockName}`);
|
|
222
|
+
}
|
|
223
|
+
const codeFile = (_a = files[0]) === null || _a === void 0 ? void 0 : _a.fullPath;
|
|
224
|
+
if (!codeFile) {
|
|
225
|
+
throw new Error(`No code file found for block: ${blockName}`);
|
|
226
|
+
}
|
|
227
|
+
return fs_1.default.readFileSync(codeFile, 'utf8');
|
|
228
|
+
};
|
|
229
|
+
exports.getBlockCode = getBlockCode;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const PAGES_KIT_DID = 'z8iZiDFg3vkkrPwsiba1TLXy3H9XHzFERsP8o';
|
|
2
|
+
export const PAGES_KIT_RESOURCE_TYPE = 'page';
|
|
3
|
+
export const PAGES_KIT_BLOCK_STUDIO_DID = 'z2qa7rr3eUyVnWp2PCxEVARuUfLFh6cE5V2xV';
|
|
4
|
+
export const PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE = 'page';
|
|
5
|
+
export const MEDIA_KIT_DID = 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9';
|
|
6
|
+
export const MEDIA_KIT_RESOURCE_TYPE = 'imgpack';
|
|
@@ -9,8 +9,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { Router } from 'express';
|
|
11
11
|
import fs from 'fs';
|
|
12
|
+
import { isEqual, keyBy, set } from 'lodash';
|
|
12
13
|
import path from 'path';
|
|
13
|
-
import { isPathSafe, isDev } from '../utils/helper';
|
|
14
|
+
import { isPathSafe, isDev, downloadAsset, isMetadataFile, getPreviewImageRelativePath, initializeMetadata, findComponentFiles, getBlockStudioInfo, getBlockCode, } from '../utils/helper';
|
|
14
15
|
export const initBlockStudioRouter = Router();
|
|
15
16
|
const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.svg'];
|
|
16
17
|
initBlockStudioRouter.get('/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -25,25 +26,23 @@ initBlockStudioRouter.get('/', (req, res) => __awaiter(void 0, void 0, void 0, f
|
|
|
25
26
|
return res.status(403).json({ error: 'Invalid path' });
|
|
26
27
|
}
|
|
27
28
|
try {
|
|
28
|
-
if (!fs.existsSync(filePath)) {
|
|
29
|
-
return res.json(null);
|
|
30
|
-
}
|
|
31
29
|
const ext = path.extname(filePath).toLowerCase();
|
|
32
30
|
if (BINARY_EXTENSIONS.includes(ext)) {
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
return res.json(null);
|
|
33
|
+
}
|
|
33
34
|
// For images, stream the file directly
|
|
34
35
|
const mimeType = `image/${ext.slice(1)}`;
|
|
35
36
|
res.setHeader('Content-Type', mimeType);
|
|
36
37
|
return fs.createReadStream(filePath).pipe(res);
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
catch (_a) {
|
|
44
|
-
// If JSON parsing fails, return as plain text
|
|
45
|
-
return res.json(content);
|
|
39
|
+
const metadata = initializeMetadata(filePath);
|
|
40
|
+
const code = getBlockCode(filePath);
|
|
41
|
+
if (code) {
|
|
42
|
+
set(metadata, 'renderer.script', code);
|
|
43
|
+
set(metadata, 'renderer.type', 'react-component');
|
|
46
44
|
}
|
|
45
|
+
return res.json(metadata);
|
|
47
46
|
}
|
|
48
47
|
catch (error) {
|
|
49
48
|
return res.status(500).json({ error: 'Failed to read file' });
|
|
@@ -65,11 +64,60 @@ initBlockStudioRouter.post('/', (req, res) => __awaiter(void 0, void 0, void 0,
|
|
|
65
64
|
if (!fs.existsSync(dir)) {
|
|
66
65
|
fs.mkdirSync(dir, { recursive: true });
|
|
67
66
|
}
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const currentMetadata = initializeMetadata(filePath);
|
|
68
|
+
const mergedContent = Object.assign(Object.assign({}, currentMetadata), content);
|
|
69
|
+
if (isEqual(currentMetadata, mergedContent)) {
|
|
70
|
+
return res.json({ success: true, content: mergedContent, message: 'No changes' });
|
|
71
|
+
}
|
|
72
|
+
mergedContent.updatedAt = new Date().toISOString();
|
|
73
|
+
// Check if this is a metadata file and has previewImage update
|
|
74
|
+
if (isMetadataFile(filePath) && content.previewImage && currentMetadata.previewImage !== content.previewImage) {
|
|
75
|
+
const previewImagePath = path.join(dir, getPreviewImageRelativePath(content.previewImage));
|
|
76
|
+
if (!fs.existsSync(previewImagePath)) {
|
|
77
|
+
yield downloadAsset({
|
|
78
|
+
asset: content.previewImage,
|
|
79
|
+
savePath: previewImagePath,
|
|
80
|
+
componentDid: process.env.BLOCKLET_COMPONENT_DID || '',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
res.json({ success: true, content: mergedContent, message: 'Updated' });
|
|
85
|
+
// save metadata without renderer after response
|
|
86
|
+
delete mergedContent.renderer;
|
|
87
|
+
fs.writeFileSync(filePath, JSON.stringify(mergedContent, null, 2));
|
|
88
|
+
return null;
|
|
70
89
|
}
|
|
71
90
|
catch (error) {
|
|
72
91
|
return res.status(500).json({ error: 'Failed to write file' });
|
|
73
92
|
}
|
|
74
93
|
}));
|
|
94
|
+
initBlockStudioRouter.get('/all', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
+
const { withBlockletData = true } = req.query;
|
|
96
|
+
const allBlocks = yield findComponentFiles();
|
|
97
|
+
// get code to metadata
|
|
98
|
+
const allBlocksWithCode = allBlocks.map((block) => {
|
|
99
|
+
const code = getBlockCode(block.fullPath);
|
|
100
|
+
if (code) {
|
|
101
|
+
set(block.metadata, 'renderer.script', code);
|
|
102
|
+
set(block.metadata, 'renderer.type', 'react-component');
|
|
103
|
+
}
|
|
104
|
+
return block;
|
|
105
|
+
});
|
|
106
|
+
if (withBlockletData) {
|
|
107
|
+
const allBlocksWithBlockletData = allBlocksWithCode.map((block) => {
|
|
108
|
+
const blockletInfo = getBlockStudioInfo() || {
|
|
109
|
+
title: '',
|
|
110
|
+
did: '',
|
|
111
|
+
};
|
|
112
|
+
const item = {
|
|
113
|
+
blockletTitle: blockletInfo.title,
|
|
114
|
+
blockletId: blockletInfo.did,
|
|
115
|
+
data: block.metadata,
|
|
116
|
+
};
|
|
117
|
+
return item;
|
|
118
|
+
});
|
|
119
|
+
return res.json(keyBy(allBlocksWithBlockletData, 'data.id'));
|
|
120
|
+
}
|
|
121
|
+
return res.json(keyBy(allBlocksWithCode, 'id'));
|
|
122
|
+
}));
|
|
75
123
|
export default initBlockStudioRouter;
|
|
@@ -11,8 +11,9 @@ import { getResourceExportDir } from '@blocklet/sdk/lib/component';
|
|
|
11
11
|
import { spawn } from 'child_process';
|
|
12
12
|
import { Router } from 'express';
|
|
13
13
|
import fs from 'fs';
|
|
14
|
+
import set from 'lodash/set';
|
|
14
15
|
import path, { join } from 'path';
|
|
15
|
-
import { findComponentFiles, libDir } from '../utils/helper';
|
|
16
|
+
import { findComponentFiles, libDir, getPreviewImageRelativePath, generateYaml, logger } from '../utils/helper';
|
|
16
17
|
const DID = 'z2qa7rr3eUyVnWp2PCxEVARuUfLFh6cE5V2xV';
|
|
17
18
|
const RESOURCE_TYPE = 'page';
|
|
18
19
|
const allTag = '@ALL_COMPONENTS';
|
|
@@ -111,9 +112,55 @@ initResourceRouter.post('/', (req, res) => __awaiter(void 0, void 0, void 0, fun
|
|
|
111
112
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
112
113
|
fs.mkdirSync(dir, { recursive: true });
|
|
113
114
|
const rootDir = process.cwd();
|
|
114
|
-
const
|
|
115
|
+
const distDir = join(rootDir, libDir);
|
|
116
|
+
const tmpPackage = join(distDir, 'resource-blocklet');
|
|
117
|
+
fs.mkdirSync(tmpPackage, { recursive: true });
|
|
118
|
+
const pagesDir = join(tmpPackage, 'pages');
|
|
119
|
+
fs.mkdirSync(pagesDir, { recursive: true });
|
|
120
|
+
const componentsDir = join(tmpPackage, 'components');
|
|
121
|
+
fs.mkdirSync(componentsDir, { recursive: true });
|
|
122
|
+
// get @metadata.json by glob
|
|
123
|
+
const canUseComponents = findComponentFiles({ cwd: rootDir, filter: componentIds });
|
|
124
|
+
// Filter and process metadata files
|
|
125
|
+
const metadataList = canUseComponents.map(({ fullPath, blockName, metadata: _metadata }) => {
|
|
126
|
+
// get metadata
|
|
127
|
+
const metadata = _metadata;
|
|
128
|
+
// get code to metadata
|
|
129
|
+
const code = fs.readFileSync(join(distDir, 'es', `${blockName}.js`), 'utf8');
|
|
130
|
+
if (code) {
|
|
131
|
+
set(metadata, 'renderer.script', code);
|
|
132
|
+
set(metadata, 'renderer.type', 'react-component');
|
|
133
|
+
}
|
|
134
|
+
// write metadata to metadataPath
|
|
135
|
+
const metadataYmlPath = path.join(componentsDir, `${metadata.name || 'unnamed'}.${metadata.id}.yml`);
|
|
136
|
+
fs.writeFileSync(metadataYmlPath, generateYaml(metadata));
|
|
137
|
+
// Handle preview image if exists
|
|
138
|
+
if (metadata.previewImage) {
|
|
139
|
+
const imagePath = path.join(path.dirname(fullPath), getPreviewImageRelativePath(metadata.previewImage));
|
|
140
|
+
const imageDestPath = path.join(componentsDir, metadata.previewImage);
|
|
141
|
+
if (fs.existsSync(imagePath)) {
|
|
142
|
+
fs.copyFileSync(imagePath, imageDestPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return metadata;
|
|
146
|
+
});
|
|
147
|
+
// write pages.config.yml
|
|
148
|
+
const pagesConfigPath = path.join(tmpPackage, '.blocklet/pages/pages.config.yml');
|
|
149
|
+
fs.mkdirSync(path.dirname(pagesConfigPath), { recursive: true });
|
|
150
|
+
const pagesConfig = {
|
|
151
|
+
pages: [],
|
|
152
|
+
components: metadataList.map((metadata) => ({
|
|
153
|
+
id: metadata.id,
|
|
154
|
+
name: metadata.name,
|
|
155
|
+
})),
|
|
156
|
+
supportedLocales: [],
|
|
157
|
+
config: {},
|
|
158
|
+
};
|
|
159
|
+
fs.writeFileSync(pagesConfigPath, generateYaml(pagesConfig));
|
|
160
|
+
logger.info('generate resource blocklet block count:', metadataList.length);
|
|
115
161
|
yield copyRecursive(tmpPackage, dir);
|
|
116
|
-
|
|
162
|
+
// remove tmpPackage
|
|
163
|
+
// fs.rmSync(tmpPackage, { recursive: true, force: true });
|
|
117
164
|
res.json({ success: true });
|
|
118
165
|
}
|
|
119
166
|
catch (error) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-ignore
|
|
2
2
|
import { initProxyToMediaKitUploadsMiddleware, initStaticResourceMiddleware } from '@blocklet/uploader-server';
|
|
3
3
|
import express, { Router } from 'express';
|
|
4
|
+
import { PAGES_KIT_DID, PAGES_KIT_RESOURCE_TYPE, MEDIA_KIT_DID, MEDIA_KIT_RESOURCE_TYPE, PAGES_KIT_BLOCK_STUDIO_DID, PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE, } from '../constants';
|
|
4
5
|
// init uploader router
|
|
5
6
|
export const initUploaderRouter = Router();
|
|
6
7
|
initUploaderRouter.use('/', initProxyToMediaKitUploadsMiddleware({
|
|
@@ -10,20 +11,20 @@ initUploaderRouter.use('/', initProxyToMediaKitUploadsMiddleware({
|
|
|
10
11
|
resourceTypes: [
|
|
11
12
|
// image bin resource
|
|
12
13
|
{
|
|
13
|
-
type:
|
|
14
|
-
did:
|
|
14
|
+
type: MEDIA_KIT_RESOURCE_TYPE,
|
|
15
|
+
did: MEDIA_KIT_DID,
|
|
15
16
|
},
|
|
16
17
|
// pages kit resource (pages and components folder)
|
|
17
18
|
{
|
|
18
|
-
type:
|
|
19
|
-
did:
|
|
19
|
+
type: PAGES_KIT_RESOURCE_TYPE,
|
|
20
|
+
did: PAGES_KIT_DID,
|
|
20
21
|
folder: ['pages', 'components'],
|
|
21
22
|
blacklist: ['.yml'],
|
|
22
23
|
},
|
|
23
24
|
// pages kit block studio resource
|
|
24
25
|
{
|
|
25
|
-
type:
|
|
26
|
-
did:
|
|
26
|
+
type: PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE,
|
|
27
|
+
did: PAGES_KIT_BLOCK_STUDIO_DID,
|
|
27
28
|
folder: ['pages', 'components'],
|
|
28
29
|
blacklist: ['.yml'],
|
|
29
30
|
},
|
|
@@ -13,7 +13,7 @@ import { readFileSync, existsSync } from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import pages, { DefaultPageStrategy } from 'vite-plugin-react-pages';
|
|
15
15
|
import { findComponentFiles, setBlockEntryFilesPattern, getBlockEntryFilesPattern, getBlockName, logger, } from '../utils/helper';
|
|
16
|
-
import { initHtmlPreviewTransformPlugin, VIRTUAL_MODULE_ID } from './vite-plugin-html-transform';
|
|
16
|
+
import { initHtmlPreviewTransformPlugin, VIRTUAL_MODULE_ID, readHtmlFiles, generateComponent, } from './vite-plugin-html-transform';
|
|
17
17
|
// import initRemoteScriptLocalizerPlugin from './vite-plugin-remote-script-localizer';
|
|
18
18
|
export function initBlockStudioPlugins(options) {
|
|
19
19
|
const workingDir = (options === null || options === void 0 ? void 0 : options.cwd) || process.cwd();
|
|
@@ -85,6 +85,9 @@ export function initBlockStudioPlugins(options) {
|
|
|
85
85
|
'@arcblock/did-connect',
|
|
86
86
|
],
|
|
87
87
|
output: {
|
|
88
|
+
chunkFileNames: () => {
|
|
89
|
+
return '[format]/_chunks/[name]-[hash].js';
|
|
90
|
+
},
|
|
88
91
|
// 为所有外部依赖提供全局变量
|
|
89
92
|
globals: {
|
|
90
93
|
react: 'React',
|
|
@@ -130,6 +133,7 @@ export function initBlockStudioPlugins(options) {
|
|
|
130
133
|
? {
|
|
131
134
|
isHtmlPreview: true,
|
|
132
135
|
dataPath: filePath,
|
|
136
|
+
code: generateComponent(readHtmlFiles(dirPath.split('?dir=')[1] || '')),
|
|
133
137
|
blockName,
|
|
134
138
|
dirPath,
|
|
135
139
|
metadataPath,
|