@blocklet/pages-kit-block-studio 0.0.18 → 0.1.0

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.
Files changed (28) hide show
  1. package/lib/cjs/constants/index.js +12 -1
  2. package/lib/cjs/constants/new-block-template/@metadata.json +59 -0
  3. package/lib/cjs/constants/new-block-template/index.js +55 -0
  4. package/lib/cjs/constants/new-block-template/index.tsx +89 -0
  5. package/lib/cjs/middlewares/init-block-studio-router.js +43 -9
  6. package/lib/cjs/middlewares/init-resource-router.js +5 -1
  7. package/lib/cjs/plugins/vite-plugin-block-studio.js +29 -31
  8. package/lib/cjs/plugins/vite-plugin-html-transform.js +40 -5
  9. package/lib/cjs/tsconfig.tsbuildinfo +1 -1
  10. package/lib/cjs/utils/generate-wrapper-code.js +21 -25
  11. package/lib/cjs/utils/helper.js +20 -12
  12. package/lib/esm/constants/index.js +8 -0
  13. package/lib/esm/constants/new-block-template/@metadata.json +59 -0
  14. package/lib/esm/constants/new-block-template/index.js +50 -0
  15. package/lib/esm/constants/new-block-template/index.tsx +89 -0
  16. package/lib/esm/middlewares/init-block-studio-router.js +44 -10
  17. package/lib/esm/middlewares/init-resource-router.js +5 -1
  18. package/lib/esm/plugins/vite-plugin-block-studio.js +29 -31
  19. package/lib/esm/plugins/vite-plugin-html-transform.js +40 -5
  20. package/lib/esm/tsconfig.tsbuildinfo +1 -1
  21. package/lib/esm/utils/generate-wrapper-code.js +21 -25
  22. package/lib/esm/utils/helper.js +10 -6
  23. package/lib/types/constants/index.d.ts +7 -0
  24. package/lib/types/constants/new-block-template/index.d.ts +13 -0
  25. package/lib/types/tsconfig.tsbuildinfo +1 -1
  26. package/lib/types/utils/helper.d.ts +2 -4
  27. package/package.json +17 -5
  28. package/tsconfig.json +3 -1
@@ -1,9 +1,20 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MEDIA_KIT_RESOURCE_TYPE = exports.MEDIA_KIT_DID = exports.PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE = exports.PAGES_KIT_BLOCK_STUDIO_DID = exports.PAGES_KIT_RESOURCE_TYPE = exports.PAGES_KIT_DID = void 0;
6
+ exports.DEFAULT_BLOCK_ENTRY_FILES_PATTERN = exports.NANOID_LENGTH = exports.PREVIEW_IMAGE_DIR = exports.METADATA_FILE_NAME = exports.libDir = exports.NEW_BLOCK_TEMPLATE_METADATA_PATH = exports.NEW_BLOCK_TEMPLATE_PATH = exports.MEDIA_KIT_RESOURCE_TYPE = exports.MEDIA_KIT_DID = exports.PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE = exports.PAGES_KIT_BLOCK_STUDIO_DID = exports.PAGES_KIT_RESOURCE_TYPE = exports.PAGES_KIT_DID = void 0;
7
+ const path_1 = __importDefault(require("path"));
4
8
  exports.PAGES_KIT_DID = 'z8iZiDFg3vkkrPwsiba1TLXy3H9XHzFERsP8o';
5
9
  exports.PAGES_KIT_RESOURCE_TYPE = 'page';
6
10
  exports.PAGES_KIT_BLOCK_STUDIO_DID = 'z2qa7rr3eUyVnWp2PCxEVARuUfLFh6cE5V2xV';
7
11
  exports.PAGES_KIT_BLOCK_STUDIO_RESOURCE_TYPE = 'page';
8
12
  exports.MEDIA_KIT_DID = 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9';
9
13
  exports.MEDIA_KIT_RESOURCE_TYPE = 'imgpack';
14
+ exports.NEW_BLOCK_TEMPLATE_PATH = path_1.default.join(__dirname, 'new-block-template', 'index.tsx');
15
+ exports.NEW_BLOCK_TEMPLATE_METADATA_PATH = path_1.default.join(__dirname, 'new-block-template', '@metadata.json');
16
+ exports.libDir = 'lib';
17
+ exports.METADATA_FILE_NAME = '@metadata.json';
18
+ exports.PREVIEW_IMAGE_DIR = '@preview-images';
19
+ exports.NANOID_LENGTH = 16;
20
+ exports.DEFAULT_BLOCK_ENTRY_FILES_PATTERN = 'src/**/index.{ts,tsx,html}';
@@ -0,0 +1,59 @@
1
+ {
2
+ "properties": {
3
+ "gs1rn5jmxfvpxptx": {
4
+ "index": 0,
5
+ "data": {
6
+ "id": "gs1rn5jmxfvpxptx",
7
+ "key": "title",
8
+ "locales": {
9
+ "zh": {
10
+ "name": "Title"
11
+ }
12
+ }
13
+ }
14
+ },
15
+ "9ajrz12ik7esfk1z": {
16
+ "index": 1,
17
+ "data": {
18
+ "id": "9ajrz12ik7esfk1z",
19
+ "key": "description",
20
+ "locales": {
21
+ "zh": {
22
+ "name": "Description",
23
+ "defaultValue": "Welcome to Pages Kit Block Studio"
24
+ }
25
+ }
26
+ }
27
+ },
28
+ "3ckcfvf6b7zyskk8": {
29
+ "index": 2,
30
+ "data": {
31
+ "id": "3ckcfvf6b7zyskk8",
32
+ "key": "logo",
33
+ "type": "url",
34
+ "locales": {
35
+ "zh": {
36
+ "defaultValue": {
37
+ "url": "/.well-known/service/blocklet/logo?imageFilter=convert&f=png&h=80",
38
+ "mediaKitUrl": "/.well-known/service/blocklet/logo?imageFilter=convert&f=png&h=80"
39
+ },
40
+ "name": "Logo"
41
+ }
42
+ }
43
+ }
44
+ },
45
+ "x3lqht8ikble1itx": {
46
+ "index": 3,
47
+ "data": {
48
+ "id": "x3lqht8ikble1itx",
49
+ "key": "copyright",
50
+ "locales": {
51
+ "zh": {
52
+ "defaultValue": "Powered by Pages Kit Block Studio"
53
+ }
54
+ },
55
+ "visible": false
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.EditComponent = void 0;
15
+ exports.default = HelloWorld;
16
+ const jsx_runtime_1 = require("react/jsx-runtime");
17
+ // default export
18
+ function HelloWorld({ title = 'Hello World', logo, description, copyright }) {
19
+ return ((0, jsx_runtime_1.jsxs)("div", { style: {
20
+ display: 'flex',
21
+ flexDirection: 'column',
22
+ alignItems: 'center',
23
+ padding: '16px 0',
24
+ }, children: [title && (0, jsx_runtime_1.jsx)("h1", { children: title }), logo && ((0, jsx_runtime_1.jsx)("img", { src: typeof logo === 'object' ? logo.url : logo, alt: "logo", style: {
25
+ margin: '16px 0',
26
+ maxWidth: '200px',
27
+ } })), description && ((0, jsx_runtime_1.jsx)("div", { style: {
28
+ color: '#666',
29
+ marginTop: '8px',
30
+ }, children: description })), copyright && ((0, jsx_runtime_1.jsx)("div", { style: {
31
+ color: '#999',
32
+ fontSize: '12px',
33
+ marginTop: '16px',
34
+ }, children: copyright }))] }));
35
+ }
36
+ // export edit component
37
+ const EditComponent = (_a) => {
38
+ var { onChange } = _a, props = __rest(_a, ["onChange"]);
39
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', flexDirection: 'column', gap: '16px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
40
+ fontSize: '14px',
41
+ fontWeight: 500,
42
+ color: '#333',
43
+ padding: '8px 0',
44
+ borderBottom: '1px solid #eee',
45
+ }, children: "Footer Parameters" }), (0, jsx_runtime_1.jsx)("input", { id: "copyright-input", type: "text", style: {
46
+ width: '100%',
47
+ padding: '8px 12px',
48
+ border: '1px solid #ddd',
49
+ borderRadius: '4px',
50
+ fontSize: '14px',
51
+ transition: 'border-color 0.3s',
52
+ outline: 'none',
53
+ }, value: props.copyright || '', onChange: (e) => onChange === null || onChange === void 0 ? void 0 : onChange({ copyright: e.target.value }), placeholder: "Please Input Copyright" })] }));
54
+ };
55
+ exports.EditComponent = EditComponent;
@@ -0,0 +1,89 @@
1
+ import React from 'react';
2
+
3
+ export interface HelloWorldProps {
4
+ title?: string;
5
+ logo?: string | { url: string };
6
+ description?: string;
7
+ copyright?: string;
8
+ }
9
+
10
+ // default export
11
+ export default function HelloWorld({ title = 'Hello World', logo, description, copyright }: HelloWorldProps) {
12
+ return (
13
+ <div
14
+ style={{
15
+ display: 'flex',
16
+ flexDirection: 'column',
17
+ alignItems: 'center',
18
+ padding: '16px 0',
19
+ }}>
20
+ {title && <h1>{title}</h1>}
21
+ {logo && (
22
+ <img
23
+ src={typeof logo === 'object' ? logo.url : logo}
24
+ alt="logo"
25
+ style={{
26
+ margin: '16px 0',
27
+ maxWidth: '200px',
28
+ }}
29
+ />
30
+ )}
31
+ {description && (
32
+ <div
33
+ style={{
34
+ color: '#666',
35
+ marginTop: '8px',
36
+ }}>
37
+ {description}
38
+ </div>
39
+ )}
40
+ {copyright && (
41
+ <div
42
+ style={{
43
+ color: '#999',
44
+ fontSize: '12px',
45
+ marginTop: '16px',
46
+ }}>
47
+ {copyright}
48
+ </div>
49
+ )}
50
+ </div>
51
+ );
52
+ }
53
+
54
+ // export edit component
55
+ export const EditComponent: React.FC<HelloWorldProps & { onChange?: (value: HelloWorldProps) => void }> = ({
56
+ onChange,
57
+ ...props
58
+ }) => {
59
+ return (
60
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
61
+ <div
62
+ style={{
63
+ fontSize: '14px',
64
+ fontWeight: 500,
65
+ color: '#333',
66
+ padding: '8px 0',
67
+ borderBottom: '1px solid #eee',
68
+ }}>
69
+ Footer Parameters
70
+ </div>
71
+ <input
72
+ id="copyright-input"
73
+ type="text"
74
+ style={{
75
+ width: '100%',
76
+ padding: '8px 12px',
77
+ border: '1px solid #ddd',
78
+ borderRadius: '4px',
79
+ fontSize: '14px',
80
+ transition: 'border-color 0.3s',
81
+ outline: 'none',
82
+ }}
83
+ value={props.copyright || ''}
84
+ onChange={(e) => onChange?.({ copyright: e.target.value })}
85
+ placeholder="Please Input Copyright"
86
+ />
87
+ </div>
88
+ );
89
+ };
@@ -17,6 +17,7 @@ const express_1 = require("express");
17
17
  const fs_1 = __importDefault(require("fs"));
18
18
  const lodash_1 = require("lodash");
19
19
  const path_1 = __importDefault(require("path"));
20
+ const constants_1 = require("../constants");
20
21
  const helper_1 = require("../utils/helper");
21
22
  exports.initBlockStudioRouter = (0, express_1.Router)();
22
23
  const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.svg'];
@@ -43,11 +44,6 @@ exports.initBlockStudioRouter.get('/', (req, res) => __awaiter(void 0, void 0, v
43
44
  return fs_1.default.createReadStream(filePath).pipe(res);
44
45
  }
45
46
  const metadata = (0, helper_1.initializeMetadata)(filePath);
46
- const code = (0, helper_1.getBlockCode)(filePath);
47
- if (code) {
48
- (0, lodash_1.set)(metadata, 'renderer.script', code);
49
- (0, lodash_1.set)(metadata, 'renderer.type', 'react-component');
50
- }
51
47
  return res.json(metadata);
52
48
  }
53
49
  catch (error) {
@@ -87,16 +83,54 @@ exports.initBlockStudioRouter.post('/', (req, res) => __awaiter(void 0, void 0,
87
83
  });
88
84
  }
89
85
  }
90
- res.json({ success: true, content: mergedContent, message: 'Updated' });
91
- // save metadata without renderer after response
92
- delete mergedContent.renderer;
93
86
  fs_1.default.writeFileSync(filePath, JSON.stringify(mergedContent, null, 2));
94
- return null;
87
+ return res.json({ success: true, content: mergedContent, message: 'Updated' });
95
88
  }
96
89
  catch (error) {
97
90
  return res.status(500).json({ error: 'Failed to write file' });
98
91
  }
99
92
  }));
93
+ exports.initBlockStudioRouter.post('/create', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
94
+ if (!helper_1.isDev) {
95
+ return res.status(403).json({ error: 'Only available in development mode' });
96
+ }
97
+ const { name, description } = req.body;
98
+ if (!name) {
99
+ return res.status(400).json({ error: 'Name is required' });
100
+ }
101
+ try {
102
+ const pattern = (0, helper_1.getBlockEntryFilesPattern)();
103
+ const baseDir = path_1.default.dirname(pattern.replace(/\*\*?/g, ''));
104
+ const blockDir = path_1.default.join(baseDir, name);
105
+ const metadataPath = path_1.default.join(blockDir, constants_1.METADATA_FILE_NAME);
106
+ const indexPath = path_1.default.join(blockDir, 'index.tsx');
107
+ // Check if block already exists
108
+ if (fs_1.default.existsSync(blockDir)) {
109
+ return res.status(409).json({ error: 'Block already exists' });
110
+ }
111
+ // Create block directory
112
+ fs_1.default.mkdirSync(blockDir, { recursive: true });
113
+ // Initialize metadata
114
+ let metadata = (0, helper_1.initializeMetadata)(metadataPath);
115
+ metadata.name = name;
116
+ metadata.description = description || '';
117
+ metadata.createdAt = new Date().toISOString();
118
+ metadata.updatedAt = new Date().toISOString();
119
+ // get template metadata from @metadata.json
120
+ const metadataContent = fs_1.default.readFileSync(constants_1.NEW_BLOCK_TEMPLATE_METADATA_PATH, 'utf-8');
121
+ metadata = Object.assign(Object.assign({}, metadata), (0, helper_1.safeParse)(metadataContent));
122
+ // Write metadata file
123
+ fs_1.default.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
124
+ // Copy template file to index.tsx
125
+ const templateContent = fs_1.default.readFileSync(constants_1.NEW_BLOCK_TEMPLATE_PATH, 'utf-8');
126
+ fs_1.default.writeFileSync(indexPath, templateContent);
127
+ return res.json({ success: true, metadata });
128
+ }
129
+ catch (error) {
130
+ console.error('Failed to create block:', error);
131
+ return res.status(500).json({ error: 'Failed to create block' });
132
+ }
133
+ }));
100
134
  exports.initBlockStudioRouter.get('/all', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
101
135
  const { withBlockletData = true } = req.query;
102
136
  const allBlocks = yield (0, helper_1.findComponentFiles)();
@@ -140,7 +140,7 @@ exports.initResourceRouter.post('/', (req, res) => __awaiter(void 0, void 0, voi
140
140
  const buildProcess = (0, child_process_1.spawn)('pnpm', ['run', 'build-lib'], {
141
141
  stdio: 'inherit',
142
142
  shell: true,
143
- env: Object.assign(Object.assign({}, process.env), { FORCE_COLOR: '1', BLOCK_FILTER: componentIds.join(',') }),
143
+ env: Object.assign(Object.assign({}, process.env), { FORCE_COLOR: '1', BLOCK_FILTER: componentIds.join(','), NODE_OPTIONS: '--max_old_space_size=16384' }),
144
144
  });
145
145
  yield new Promise((resolve, reject) => {
146
146
  buildProcess.on('close', (code) => {
@@ -149,6 +149,10 @@ exports.initResourceRouter.post('/', (req, res) => __awaiter(void 0, void 0, voi
149
149
  else
150
150
  reject(new Error(`Build process exited with code ${code}`));
151
151
  });
152
+ buildProcess.on('error', (error) => {
153
+ console.error('Build process error:', error);
154
+ reject(error);
155
+ });
152
156
  });
153
157
  const dir = getExportDir(projectId, releaseId);
154
158
  fs_1.default.rmSync(dir, { recursive: true, force: true });
@@ -47,6 +47,7 @@ exports.initBlockStudioPlugins = initBlockStudioPlugins;
47
47
  // import typescript from '@rollup/plugin-typescript';
48
48
  const fs_1 = require("fs");
49
49
  const path = __importStar(require("path"));
50
+ const ufo_1 = require("ufo");
50
51
  const vite_plugin_react_pages_1 = __importStar(require("vite-plugin-react-pages"));
51
52
  const helper_1 = require("../utils/helper");
52
53
  const vite_plugin_html_transform_1 = require("./vite-plugin-html-transform");
@@ -108,18 +109,13 @@ function initBlockStudioPlugins(options) {
108
109
  formats: ['es', !multiMode ? 'umd' : 'cjs'],
109
110
  fileName: (format, entryName) => `${format}/${entryName}.js`,
110
111
  }, rollupOptions: {
111
- external: [
112
- 'react',
113
- 'react-router-dom',
114
- 'react-dom',
115
- 'react-is',
116
- 'react/jsx-runtime',
117
- 'crypto',
118
- // '@emotion/react',
119
- // '@emotion/styled',
120
- '@arcblock/ux',
121
- '@arcblock/did-connect',
122
- ],
112
+ external: (id) => {
113
+ const skip = ['react', 'crypto'];
114
+ if (skip.some((s) => id === s)) {
115
+ return true;
116
+ }
117
+ return false;
118
+ },
123
119
  output: {
124
120
  chunkFileNames: () => {
125
121
  return '[format]/_chunks/[name]-[hash].js';
@@ -127,22 +123,13 @@ function initBlockStudioPlugins(options) {
127
123
  // 为所有外部依赖提供全局变量
128
124
  globals: {
129
125
  react: 'React',
130
- 'react-dom': 'ReactDOM',
131
- 'react-is': 'ReactIs',
132
- 'react-router-dom': 'ReactRouterDOM',
133
- 'react/jsx-runtime': 'ReactJsxRuntime',
134
- // '@emotion/react': 'emotionReact',
135
- // '@emotion/styled': 'emotionStyled',
136
- '@mui/material': 'MUI',
137
- '@arcblock/ux': 'ArcBlockUX',
138
- '@arcblock/did-connect': 'ArcBlockDidConnect',
139
126
  },
140
127
  paths: {
141
- // Redirect 'react' imports to '@blocklet/pages-kit/builtin/react'
142
- // react: '@blocklet/pages-kit/builtin/react',
128
+ // Redirect 'react' imports to '@blocklet/pages-kit/builtin/react'
129
+ react: '@blocklet/pages-kit/builtin/react',
143
130
  },
144
131
  // 确保正确处理命名导出和默认导出
145
- // interop: 'auto',
132
+ interop: 'auto',
146
133
  },
147
134
  } }, _config === null || _config === void 0 ? void 0 : _config.build),
148
135
  };
@@ -162,21 +149,23 @@ function initBlockStudioPlugins(options) {
162
149
  const pageId = `/${blockName}`;
163
150
  const dirPath = path.dirname(filePath);
164
151
  const metadataPath = path.join(dirPath, '@metadata.json');
152
+ const dataPath = isHtml ? `${vite_plugin_html_transform_1.VIRTUAL_MODULE_ID}?dir=${dirPath}` : filePath;
165
153
  api.addPageData({
166
154
  pageId,
167
- dataPath: isHtml ? `${vite_plugin_html_transform_1.VIRTUAL_MODULE_ID}?dir=${dirPath}` : filePath,
155
+ dataPath,
168
156
  staticData: isHtml
169
157
  ? {
170
- isHtmlPreview: true,
171
- dataPath: filePath,
172
- code: (0, vite_plugin_html_transform_1.generateComponent)((0, vite_plugin_html_transform_1.readHtmlFiles)(dirPath.split('?dir=')[1] || '')),
158
+ isHtml: true,
159
+ dataPath,
160
+ code: (0, vite_plugin_html_transform_1.generateComponent)((0, vite_plugin_html_transform_1.readHtmlFiles)(dirPath)),
173
161
  blockName,
174
162
  dirPath,
163
+ importPath: (0, ufo_1.joinURL)('@id', dataPath),
175
164
  metadataPath,
176
165
  }
177
- : Object.assign(Object.assign({}, (yield helpers.extractStaticData(file))), { code: (0, fs_1.readFileSync)(file.path, 'utf-8'), dataPath: filePath, blockName,
178
- dirPath,
179
- metadataPath }),
166
+ : Object.assign(Object.assign({}, (yield helpers.extractStaticData(file))), { code: (0, fs_1.readFileSync)(file.path, 'utf-8'), dataPath,
167
+ blockName,
168
+ dirPath, importPath: (0, ufo_1.joinURL)('@fs', dataPath), metadataPath }),
180
169
  });
181
170
  });
182
171
  });
@@ -197,6 +186,15 @@ function initBlockStudioPlugins(options) {
197
186
  // maxConcurrent: 5, // 可选,默认值
198
187
  // timeout: 30 * 1000, // 可选,默认值 30 秒
199
188
  // }),
189
+ {
190
+ name: 'build-force-exit',
191
+ apply: 'build',
192
+ enforce: 'post',
193
+ closeBundle() {
194
+ // ensure vite build exit
195
+ process.exit(0);
196
+ },
197
+ },
200
198
  ];
201
199
  }
202
200
  exports.default = initBlockStudioPlugins;
@@ -21,6 +21,7 @@ exports.RESOLVED_VIRTUAL_MODULE_ID = `\0${exports.VIRTUAL_MODULE_ID}`;
21
21
  const isRelativePath = (path) => {
22
22
  return path.startsWith('./') || path.startsWith('../') || (!path.startsWith('http') && !path.startsWith('//'));
23
23
  };
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
25
  function extractExternalResources(html, _dirPath) {
25
26
  const external = {
26
27
  js: [],
@@ -170,10 +171,24 @@ ${content.trim()}`;
170
171
  <meta http-equiv="X-Content-Type-Options" content="nosniff">
171
172
  <meta http-equiv="Referrer-Policy" content="no-referrer">
172
173
  <meta http-equiv="Permissions-Policy" content="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()">`;
174
+ const autoHeightScript = `<script>
175
+ (function() {
176
+ if ('ResizeObserver' in window) {
177
+ const resizeObserver = new ResizeObserver(() => {
178
+ const height = document.documentElement.scrollHeight;
179
+ window.parent.postMessage({ height }, '*');
180
+ });
181
+
182
+ resizeObserver.observe(document.documentElement);
183
+ }
184
+ })();
185
+ </script>`;
173
186
  // 将处理后的相对路径引入的 CSS 和 JS 注入到 HTML 中
174
187
  const htmlContent = htmlWithoutRelativeImport
175
188
  .replace('<head>', `<head>${securityHeaders}`)
176
189
  .replace('</head>', `${cssContents.map((css) => `<style>${css.trim()}</style>`).join('\n')}
190
+
191
+ ${autoHeightScript}
177
192
  </head>`)
178
193
  .replace('</body>', `
179
194
  ${jsContents.map((js) => `<script>${js}</script>`).join('\n')}
@@ -190,21 +205,41 @@ ${content.trim()}`;
190
205
  name: (0, path_1.basename)(dirPath),
191
206
  };
192
207
  }
208
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
193
209
  function generateComponent(content, _isDev = true) {
194
210
  const htmlContent = content.html;
195
211
  const { name } = content;
196
- if (_isDev) {
197
- // do something
198
- }
199
- return `import { createElement } from 'react';
212
+ return `import { createElement, useEffect, useRef } from 'react';
213
+
200
214
 
201
215
  export default function HtmlPreview() {
216
+ const iframeRef = useRef(null);
217
+
218
+ useEffect(() => {
219
+ const iframe = iframeRef.current;
220
+ if (!iframe) return;
221
+
222
+ const handleMessage = (event) => {
223
+ if (event.source === iframe.contentWindow) {
224
+ const height = event.data.height;
225
+ if (height) {
226
+ iframe.style.height = height + 'px';
227
+ }
228
+ }
229
+ };
230
+
231
+ window.addEventListener('message', handleMessage);
232
+ return () => window.removeEventListener('message', handleMessage);
233
+ }, []);
234
+
202
235
  return createElement('iframe', {
203
- style: { border: 'none', width: '100%', height: '100%' },
236
+ ref: iframeRef,
237
+ style: { border: 'none', width: '100%', height: '100%', maxHeight: '100vh' },
204
238
  sandbox: 'allow-scripts',
205
239
  title: 'Preview ${name}',
206
240
  srcDoc: ${htmlContent}
207
241
  });
242
+
208
243
  }`;
209
244
  }
210
245
  function initHtmlPreviewTransformPlugin() {