@blocklet/pages-kit-block-studio 0.6.14 → 0.6.15

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.
@@ -1,24 +1,25 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  /* eslint-disable no-console */
3
+ import { getMediaKitFileStream } from '@blocklet/uploader-server';
3
4
  import chalk from 'chalk';
4
5
  import { execSync, spawn } from 'child_process';
5
- import { findComponentFiles, libDir } from './helper';
6
- export async function buildLib(options) {
7
- const workingDir = options?.cwd || process.cwd();
6
+ import fs from 'fs';
7
+ import get from 'lodash/get';
8
+ import set from 'lodash/set';
9
+ import path, { join, basename } from 'path';
10
+ import ts from 'typescript';
11
+ import { findComponentFiles, libDir, getPreviewImageRelativePath, generateYaml, logger, getComponentScriptName, getEditComponentBlockName, getAigneOutputValueSchemaBlockName, getPropertiesSchemaBlockName, getGetServerSidePropsBlockName, copyRecursive, } from './helper';
12
+ async function performBuild(workingDir, componentIds) {
8
13
  // Scan for all index files
9
14
  const allBlocks = findComponentFiles({ cwd: workingDir }).map((entry) => {
10
15
  return entry.blockName;
11
16
  });
12
- const filterModules = process.argv.includes('--filter')
13
- ? process.argv[process.argv.indexOf('--filter') + 1]?.split(',')
14
- : process.env.BLOCK_FILTER?.split(',') || null;
15
17
  const ignoreViteLog = process.argv.includes('--log') ? false : process.env.BLOCK_LOG !== 'true';
16
18
  const multiMode = process.argv.includes('--multi') || process.env.BLOCK_MULTI === 'true';
17
- const blocks = allBlocks.filter((name) => !filterModules || filterModules.includes(name || ''));
19
+ const blocks = allBlocks.filter((name) => !componentIds || componentIds.includes(name || ''));
18
20
  if (!blocks.length) {
19
- console.log(chalk.yellow('No components founds, please check has any component been selected'));
20
- throw new Error('No components founds, please check has any component been selected');
21
- return;
21
+ console.log(chalk.yellow('No components found, please check has any component been selected'));
22
+ throw new Error('No components found, please check has any component been selected');
22
23
  }
23
24
  // Clean up the lib directory
24
25
  await execSync(`rm -rf ${libDir}`, { cwd: workingDir });
@@ -26,51 +27,266 @@ export async function buildLib(options) {
26
27
  // Start to build
27
28
  console.log(chalk.cyan(`Start to build ${blocks.length} blocks in parallel\n`));
28
29
  const canBuildBlocks = multiMode ? blocks : [blocks.join(',')];
29
- const configFile = 'vite-lib.config';
30
- const hasConfig = execSync(`ls ${configFile}.* 2>/dev/null || true`, {
30
+ const configFile = 'vite-lib.config.ts';
31
+ const hasConfig = execSync(`ls ${configFile} 2>/dev/null || true`, {
31
32
  cwd: workingDir,
32
33
  encoding: 'utf-8',
33
34
  }).trim() !== '';
35
+ await Promise.all(canBuildBlocks.map((blockName) => {
36
+ console.log(chalk.blue(`Building ${blockName} Block...`));
37
+ return new Promise((resolve, reject) => {
38
+ const build = spawn('vite', [
39
+ 'build',
40
+ '--filter',
41
+ `"${blockName}"`,
42
+ '--outDir',
43
+ libDir,
44
+ '--emptyOutDir=false',
45
+ hasConfig ? `--config ${configFile}.*` : '',
46
+ ], {
47
+ stdio: ignoreViteLog ? ['inherit', 'ignore', 'ignore'] : 'inherit',
48
+ shell: true,
49
+ cwd: workingDir,
50
+ });
51
+ build.on('close', (code) => {
52
+ if (code === 0) {
53
+ console.log(chalk.green(`Build ${blockName} successfully`));
54
+ resolve();
55
+ }
56
+ else {
57
+ reject(new Error(`Build failed with code ${code}`));
58
+ }
59
+ });
60
+ });
61
+ }));
62
+ console.log(chalk.green(`\nBuild ${blocks.length} blocks successfully`));
63
+ console.log(chalk.gray(`Generate the ${libDir} directory successfully`));
64
+ }
65
+ async function performPackaging({ exportDir, componentIds, workingDir }) {
66
+ if (exportDir) {
67
+ // Clear and create export directory
68
+ fs.rmSync(exportDir, { recursive: true, force: true });
69
+ fs.mkdirSync(exportDir, { recursive: true });
70
+ }
71
+ const distDir = join(workingDir, libDir);
72
+ const codeDir = join(distDir, 'es');
73
+ const tmpPackage = join(distDir, 'resource-blocklet');
74
+ fs.mkdirSync(tmpPackage, { recursive: true });
75
+ const pagesDir = join(tmpPackage, 'pages');
76
+ fs.mkdirSync(pagesDir, { recursive: true });
77
+ const componentsDir = join(tmpPackage, 'components');
78
+ fs.mkdirSync(componentsDir, { recursive: true });
79
+ const chunksDir = join(tmpPackage, 'chunks');
80
+ fs.mkdirSync(chunksDir, { recursive: true });
81
+ const chunksMapPath = join(codeDir, 'chunks-map.json');
82
+ const chunksMap = JSON.parse(fs.readFileSync(chunksMapPath, 'utf8'));
83
+ // Get components with metadata
84
+ const canUseComponents = findComponentFiles({
85
+ cwd: workingDir,
86
+ filter: componentIds,
87
+ strictExistMetadata: true,
88
+ });
89
+ // Process metadata files
90
+ const metadataList = await Promise.all(canUseComponents.map(async ({ fullPath, blockName, metadata: _metadata }) => {
91
+ const metadata = _metadata;
92
+ const jsName = `${blockName}.js`;
93
+ // Set version to 2
94
+ set(metadata, 'version', 2);
95
+ // Get main component code
96
+ const code = fs.readFileSync(join(codeDir, `${getComponentScriptName(blockName, 'esm')}.js`), 'utf8');
97
+ if (code) {
98
+ set(metadata, 'renderer.script', code);
99
+ set(metadata, 'renderer.type', 'react-component');
100
+ set(metadata, 'renderer.chunks', chunksMap[jsName] || []);
101
+ }
102
+ // Transpile to CJS
103
+ try {
104
+ const cjsCode = ts.transpileModule(code, {
105
+ compilerOptions: {
106
+ jsx: ts.JsxEmit.React,
107
+ target: ts.ScriptTarget.ES2016,
108
+ module: ts.ModuleKind.CommonJS,
109
+ moduleResolution: ts.ModuleResolutionKind.Node16,
110
+ },
111
+ }).outputText;
112
+ if (cjsCode) {
113
+ set(metadata, 'renderer.cjsScript', cjsCode);
114
+ }
115
+ }
116
+ catch (error) {
117
+ // ignore error
118
+ }
119
+ // Handle edit component code
120
+ try {
121
+ const editComponentJsName = `${getEditComponentBlockName(blockName)}.js`;
122
+ const editComponentCode = fs.readFileSync(join(codeDir, editComponentJsName), 'utf8');
123
+ if (editComponentCode) {
124
+ set(metadata, 'renderer.editComponent', editComponentCode);
125
+ set(metadata, 'renderer.chunks', Array.from(new Set([...get(metadata, 'renderer.chunks', []), ...(chunksMap[editComponentJsName] || [])])));
126
+ }
127
+ }
128
+ catch (error) {
129
+ // ignore error
130
+ }
131
+ // Handle aigne output value schema
132
+ try {
133
+ const aigneOutputValueSchemaJsName = `${getAigneOutputValueSchemaBlockName(blockName)}.js`;
134
+ const aigneOutputValueSchemaCode = fs.readFileSync(join(codeDir, aigneOutputValueSchemaJsName), 'utf8');
135
+ if (aigneOutputValueSchemaCode) {
136
+ set(metadata, 'renderer.aigneOutputValueSchema', aigneOutputValueSchemaCode);
137
+ }
138
+ }
139
+ catch (error) {
140
+ // ignore error
141
+ }
142
+ // Handle properties schema
143
+ try {
144
+ const propertiesSchemaJsName = `${getPropertiesSchemaBlockName(blockName)}.js`;
145
+ const propertiesSchemaCode = fs.readFileSync(join(codeDir, propertiesSchemaJsName), 'utf8');
146
+ if (propertiesSchemaCode) {
147
+ set(metadata, 'renderer.PROPERTIES_SCHEMA', propertiesSchemaCode);
148
+ }
149
+ }
150
+ catch (error) {
151
+ // ignore error
152
+ }
153
+ // Handle getServerSideProps
154
+ try {
155
+ const getServerSidePropsJsName = `${getGetServerSidePropsBlockName(blockName)}.js`;
156
+ const getServerSidePropsCode = fs.readFileSync(join(codeDir, getServerSidePropsJsName), 'utf8');
157
+ if (getServerSidePropsCode) {
158
+ set(metadata, 'renderer.getServerSideProps', getServerSidePropsCode);
159
+ }
160
+ }
161
+ catch (error) {
162
+ // ignore error
163
+ }
164
+ // Handle preview image
165
+ if (metadata.previewImage) {
166
+ const imagePath = path.join(path.dirname(fullPath), getPreviewImageRelativePath(metadata.previewImage));
167
+ const imageDestPath = path.join(componentsDir, metadata.previewImage);
168
+ if (fs.existsSync(imagePath)) {
169
+ fs.copyFileSync(imagePath, imageDestPath);
170
+ }
171
+ }
172
+ // Handle type:url properties
173
+ if (metadata.properties) {
174
+ const downloadPromises = [];
175
+ Object.keys(metadata.properties).forEach((key) => {
176
+ const property = metadata.properties[key].data;
177
+ if (property.type === 'url') {
178
+ Object.keys(property.locales).forEach((locale) => {
179
+ if (property.locales[locale].defaultValue?.mediaKitUrl?.startsWith('mediakit://')) {
180
+ const downloadPromise = (async () => {
181
+ try {
182
+ const fileName = basename(property.locales[locale].defaultValue.mediaKitUrl);
183
+ const targetPath = path.join(componentsDir, fileName);
184
+ if (fs.existsSync(targetPath)) {
185
+ return;
186
+ }
187
+ // try to get image from preview image
188
+ const imagePathInPreview = path.join(path.dirname(fullPath), getPreviewImageRelativePath(fileName));
189
+ if (fs.existsSync(imagePathInPreview)) {
190
+ fs.copyFileSync(imagePathInPreview, targetPath);
191
+ return;
192
+ }
193
+ const resWithStream = await getMediaKitFileStream(property.locales[locale].defaultValue.mediaKitUrl);
194
+ if (!resWithStream.data) {
195
+ return;
196
+ }
197
+ const writeStream = fs.createWriteStream(targetPath);
198
+ resWithStream.data.pipe(writeStream);
199
+ await new Promise((resolve, reject) => {
200
+ writeStream.on('finish', () => {
201
+ property.locales[locale].defaultValue.url = property.locales[locale].defaultValue.mediaKitUrl;
202
+ resolve();
203
+ });
204
+ writeStream.on('error', reject);
205
+ });
206
+ }
207
+ catch (error) {
208
+ console.error('Download media kit file failed:', error.message);
209
+ }
210
+ })();
211
+ downloadPromises.push(downloadPromise);
212
+ }
213
+ });
214
+ }
215
+ });
216
+ if (downloadPromises.length > 0) {
217
+ await Promise.allSettled(downloadPromises);
218
+ }
219
+ }
220
+ // Write metadata file
221
+ const metadataYmlPath = path.join(componentsDir, `${metadata.name || 'unnamed'}.${metadata.id}.yml`);
222
+ fs.writeFileSync(metadataYmlPath, generateYaml(metadata));
223
+ return metadata;
224
+ }));
225
+ // Copy chunks directory
226
+ await copyRecursive(join(codeDir, 'chunks'), chunksDir);
227
+ // Generate pages.config.yml
228
+ const pagesConfigPath = path.join(tmpPackage, '.blocklet/pages/pages.config.yml');
229
+ fs.mkdirSync(path.dirname(pagesConfigPath), { recursive: true });
230
+ const pagesConfig = {
231
+ version: 2,
232
+ pages: [],
233
+ components: metadataList.map((metadata) => ({
234
+ id: metadata.id,
235
+ name: metadata.name,
236
+ })),
237
+ supportedLocales: [],
238
+ config: {},
239
+ };
240
+ fs.writeFileSync(pagesConfigPath, generateYaml(pagesConfig));
241
+ logger.info('generate resource blocklet block count:', metadataList.length);
242
+ if (exportDir) {
243
+ // Copy to target directory
244
+ await copyRecursive(tmpPackage, exportDir);
245
+ console.log(chalk.green(`Package exported to: ${exportDir}`));
246
+ }
247
+ }
248
+ export async function buildLib(options = {}) {
249
+ const workingDir = options?.cwd || process.cwd();
250
+ const filterModules = process.argv.includes('--filter')
251
+ ? process.argv[process.argv.indexOf('--filter') + 1]?.split(',')
252
+ : process.env.BLOCK_FILTER?.split(',');
253
+ // Get parameters from environment variables
254
+ const componentIds = filterModules ?? null;
255
+ const exportDir = process.argv.includes('--export-dir')
256
+ ? process.argv[process.argv.indexOf('--export-dir') + 1]
257
+ : process.env.EXPORT_DIR;
34
258
  try {
35
- await Promise.all(canBuildBlocks.map((blockName) => {
36
- console.log(chalk.blue(`Building ${blockName} Block...`));
37
- return new Promise((resolve, reject) => {
38
- const build = spawn('vite', [
39
- 'build',
40
- '--filter',
41
- `"${blockName}"`,
42
- '--outDir',
43
- libDir,
44
- '--emptyOutDir=false',
45
- hasConfig ? `--config ${configFile}.*` : '',
46
- ], {
47
- stdio: ignoreViteLog ? ['inherit', 'ignore', 'ignore'] : 'inherit',
48
- shell: true,
49
- cwd: workingDir,
50
- });
51
- build.on('close', (code) => {
52
- if (code === 0) {
53
- console.log(chalk.green(`Build ${blockName} successfully`));
54
- resolve();
55
- }
56
- else {
57
- reject(new Error(`Build failed with code ${code}`));
58
- }
59
- });
259
+ // Phase 1: Build components
260
+ await performBuild(workingDir, componentIds);
261
+ console.log(chalk.green('Build completed successfully'));
262
+ // Phase 2: Package resources (if export directory is specified)
263
+ if (exportDir) {
264
+ await performPackaging({
265
+ exportDir,
266
+ componentIds,
267
+ workingDir,
60
268
  });
61
- }));
62
- console.log(chalk.green(`\nBuild ${blocks.length} blocks successfully`));
63
- console.log(chalk.gray(`Generate the ${libDir} directory successfully`));
64
- process.exit(0);
269
+ console.log(chalk.green('Package completed successfully'));
270
+ }
65
271
  }
66
272
  catch (err) {
67
273
  console.error(chalk.red('Build failed:'), err);
68
- process.exit(1);
274
+ if (process.env.NODE_ENV !== 'test') {
275
+ process.exit(1);
276
+ }
277
+ throw err;
278
+ }
279
+ finally {
280
+ process.exit(0);
69
281
  }
70
282
  }
71
283
  const cwdArg = process.argv.indexOf('--cwd');
72
284
  const cwd = cwdArg !== -1 ? process.argv[cwdArg + 1] : undefined;
73
- buildLib({ cwd }).catch((err) => {
285
+ buildLib({ cwd })
286
+ .catch((err) => {
74
287
  console.error(chalk.red('Build failed:'), err);
75
288
  process.exit(1);
289
+ })
290
+ .finally(() => {
291
+ process.exit(0);
76
292
  });
@@ -202,3 +202,38 @@ export const GET_SERVER_SIDE_PROPS_NAME = 'getServerSideProps';
202
202
  export const getGetServerSidePropsBlockName = (blockName) => {
203
203
  return `${blockName}(${GET_SERVER_SIDE_PROPS_NAME})`;
204
204
  };
205
+ export function copyFile(src, dest) {
206
+ return new Promise((resolve, reject) => {
207
+ const readStream = fs.createReadStream(src);
208
+ const writeStream = fs.createWriteStream(dest);
209
+ readStream.on('error', reject);
210
+ writeStream.on('error', reject);
211
+ writeStream.on('finish', resolve);
212
+ readStream.pipe(writeStream);
213
+ });
214
+ }
215
+ export async function copyDirectory(src, dest) {
216
+ await fs.promises.mkdir(dest, { recursive: true });
217
+ const entries = await fs.promises.readdir(src, { withFileTypes: true });
218
+ for (const entry of entries) {
219
+ const srcPath = path.join(src, entry.name);
220
+ const destPath = path.join(dest, entry.name);
221
+ if (entry.isDirectory()) {
222
+ // eslint-disable-next-line no-await-in-loop
223
+ await copyDirectory(srcPath, destPath);
224
+ }
225
+ else {
226
+ // eslint-disable-next-line no-await-in-loop
227
+ await copyFile(srcPath, destPath);
228
+ }
229
+ }
230
+ }
231
+ export async function copyRecursive(src, dest) {
232
+ const srcStats = await fs.promises.stat(src);
233
+ if (srcStats.isDirectory()) {
234
+ await copyDirectory(src, dest);
235
+ }
236
+ else {
237
+ await copyFile(src, dest);
238
+ }
239
+ }
@@ -1,5 +1,2 @@
1
- export declare function copyFile(src: string, dest: string): Promise<void>;
2
- export declare function copyDirectory(src: string, dest: string): Promise<void>;
3
- export declare function copyRecursive(src: string, dest: string): Promise<void>;
4
1
  export declare const initResourceRouter: import("express-ws").Router;
5
2
  export default initResourceRouter;