@blocklet/pages-kit-runtime 0.1.1
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/LICENSE +13 -0
- package/lib/cjs/block-studio/build-lib.js +95 -0
- package/lib/cjs/block-studio/generate-wrapper-code.js +123 -0
- package/lib/cjs/block-studio/init-resource-router.js +166 -0
- package/lib/cjs/block-studio/plugins/_theme.js +7 -0
- package/lib/cjs/block-studio/plugins/index.js +19 -0
- package/lib/cjs/block-studio/plugins/vite-plugin-block-studio.js +176 -0
- package/lib/cjs/block-studio/plugins/vite-plugin-html-transform.js +245 -0
- package/lib/cjs/block-studio/plugins/vite-plugin-remote-script-localizer.js +211 -0
- package/lib/cjs/block-studio/utils.js +53 -0
- package/lib/cjs/client.js +24 -0
- package/lib/cjs/components/create-resource.js +32 -0
- package/lib/cjs/components/index.js +17 -0
- package/lib/cjs/index.js +2 -0
- package/lib/cjs/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/types/index.js +5 -0
- package/lib/cjs/utils/index.js +1 -0
- package/lib/esm/block-studio/build-lib.js +89 -0
- package/lib/esm/block-studio/generate-wrapper-code.js +87 -0
- package/lib/esm/block-studio/init-resource-router.js +124 -0
- package/lib/esm/block-studio/plugins/_theme.js +4 -0
- package/lib/esm/block-studio/plugins/index.js +3 -0
- package/lib/esm/block-studio/plugins/vite-plugin-block-studio.js +140 -0
- package/lib/esm/block-studio/plugins/vite-plugin-html-transform.js +241 -0
- package/lib/esm/block-studio/plugins/vite-plugin-remote-script-localizer.js +175 -0
- package/lib/esm/block-studio/utils.js +43 -0
- package/lib/esm/client.js +17 -0
- package/lib/esm/components/create-resource.js +29 -0
- package/lib/esm/components/index.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/tsconfig.tsbuildinfo +1 -0
- package/lib/esm/types/index.js +2 -0
- package/lib/esm/utils/index.js +1 -0
- package/lib/types/block-studio/build-lib.d.ts +3 -0
- package/lib/types/block-studio/generate-wrapper-code.d.ts +5 -0
- package/lib/types/block-studio/init-resource-router.d.ts +5 -0
- package/lib/types/block-studio/plugins/_theme.d.ts +1 -0
- package/lib/types/block-studio/plugins/index.d.ts +3 -0
- package/lib/types/block-studio/plugins/vite-plugin-block-studio.d.ts +6 -0
- package/lib/types/block-studio/plugins/vite-plugin-html-transform.d.ts +5 -0
- package/lib/types/block-studio/plugins/vite-plugin-remote-script-localizer.d.ts +8 -0
- package/lib/types/block-studio/utils.d.ts +14 -0
- package/lib/types/client.d.ts +11 -0
- package/lib/types/components/create-resource.d.ts +7 -0
- package/lib/types/components/index.d.ts +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/tsconfig.tsbuildinfo +1 -0
- package/lib/types/types/index.d.ts +39 -0
- package/lib/types/utils/index.d.ts +0 -0
- package/package.json +169 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RESOLVED_VIRTUAL_MODULE_ID = exports.VIRTUAL_MODULE_ID = void 0;
|
|
13
|
+
exports.initHtmlPreviewTransformPlugin = initHtmlPreviewTransformPlugin;
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
exports.VIRTUAL_MODULE_ID = 'virtual:html-preview';
|
|
17
|
+
exports.RESOLVED_VIRTUAL_MODULE_ID = `\0${exports.VIRTUAL_MODULE_ID}`;
|
|
18
|
+
// Helper function to check if a path is relative
|
|
19
|
+
const isRelativePath = (path) => {
|
|
20
|
+
return path.startsWith('./') || path.startsWith('../') || (!path.startsWith('http') && !path.startsWith('//'));
|
|
21
|
+
};
|
|
22
|
+
function extractExternalResources(html, _dirPath) {
|
|
23
|
+
const external = {
|
|
24
|
+
js: [],
|
|
25
|
+
css: [],
|
|
26
|
+
};
|
|
27
|
+
// 提取 script src
|
|
28
|
+
const scriptMatches = html.matchAll(/<script.*?src=["'](.*?)["']/g);
|
|
29
|
+
for (const match of scriptMatches) {
|
|
30
|
+
const src = match[1] || '';
|
|
31
|
+
if (!isRelativePath(src)) {
|
|
32
|
+
external.js.push(src);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 提取 link href
|
|
36
|
+
const linkMatches = html.matchAll(/<link.*?href=["'](.*?)["'].*?>/g);
|
|
37
|
+
for (const match of linkMatches) {
|
|
38
|
+
if (match[0].includes('stylesheet')) {
|
|
39
|
+
const href = match[1] || '';
|
|
40
|
+
if (!isRelativePath(href)) {
|
|
41
|
+
external.css.push(href);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 只移除相对路径引用,保留 HTML 结构
|
|
46
|
+
const htmlWithoutRelativeImport = html
|
|
47
|
+
.replace(/<script.*?src=["']((?:\.|\.\.)\/.+?)["'].*?><\/script>/g, '') // 移除相对路径的 script 标签
|
|
48
|
+
.replace(/<link.*?href=["']((?:\.|\.\.)\/.+?)["'].*?>/g, '') // 移除相对路径的 link 标签
|
|
49
|
+
.trim();
|
|
50
|
+
return { external, htmlWithoutRelativeImport };
|
|
51
|
+
}
|
|
52
|
+
function readHtmlFiles(dirPath) {
|
|
53
|
+
const htmlPath = (0, path_1.join)(dirPath, 'index.html');
|
|
54
|
+
if (!(0, fs_1.existsSync)(htmlPath)) {
|
|
55
|
+
throw new Error(`No index.html found in ${dirPath}`);
|
|
56
|
+
}
|
|
57
|
+
const html = (0, fs_1.readFileSync)(htmlPath, 'utf-8');
|
|
58
|
+
const cssFiles = [];
|
|
59
|
+
const jsFiles = [];
|
|
60
|
+
const cssContents = [];
|
|
61
|
+
const jsContents = [];
|
|
62
|
+
// Process imports in JS/CSS files
|
|
63
|
+
function processFile(filePath, type, processedFiles = new Set()) {
|
|
64
|
+
if (processedFiles.has(filePath))
|
|
65
|
+
return '';
|
|
66
|
+
processedFiles.add(filePath);
|
|
67
|
+
let content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
68
|
+
if (type === 'js') {
|
|
69
|
+
const importMatches = content.matchAll(/import\s+['"](.+?)['"]/g);
|
|
70
|
+
for (const match of importMatches) {
|
|
71
|
+
const importPath = match[1] || '';
|
|
72
|
+
if (isRelativePath(importPath)) {
|
|
73
|
+
const importFilePath = (0, path_1.join)((0, path_1.dirname)(filePath), importPath);
|
|
74
|
+
const resolvedPath = importFilePath.endsWith('.js') ? importFilePath : `${importFilePath}.js`;
|
|
75
|
+
if ((0, fs_1.existsSync)(resolvedPath)) {
|
|
76
|
+
const importedContent = processFile(resolvedPath, 'js', processedFiles);
|
|
77
|
+
content = content.replace(match[0], '');
|
|
78
|
+
content = `/* START: imported from ${importPath} */
|
|
79
|
+
${importedContent.trim()}
|
|
80
|
+
/* END: imported from ${importPath} */
|
|
81
|
+
${content.trim()}`;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// 标记不存在的导入
|
|
85
|
+
content = content.replace(match[0], `/* SKIP: file not found - ${importPath} */`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const importMatches = content.matchAll(/@import\s+['"](.+?)['"];?/g);
|
|
92
|
+
for (const match of importMatches) {
|
|
93
|
+
const importPath = match[1] || '';
|
|
94
|
+
if (isRelativePath(importPath)) {
|
|
95
|
+
const importFilePath = (0, path_1.join)((0, path_1.dirname)(filePath), importPath);
|
|
96
|
+
const resolvedPath = importFilePath.endsWith('.css') ? importFilePath : `${importFilePath}.css`;
|
|
97
|
+
if ((0, fs_1.existsSync)(resolvedPath)) {
|
|
98
|
+
const importedContent = processFile(resolvedPath, 'css', processedFiles);
|
|
99
|
+
content = content.replace(match[0], '');
|
|
100
|
+
content = `/* START: imported from ${importPath} */
|
|
101
|
+
${importedContent.trim()}
|
|
102
|
+
/* END: imported from ${importPath} */
|
|
103
|
+
${content.trim()}`;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// 标记不存在的导入
|
|
107
|
+
content = content.replace(match[0], `/* SKIP: file not found - ${importPath} */`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return content;
|
|
113
|
+
}
|
|
114
|
+
// Extract relative paths from HTML
|
|
115
|
+
const scriptMatches = html.matchAll(/<script.*?src=["'](.*?)["']/g);
|
|
116
|
+
const linkMatches = html.matchAll(/<link.*?href=["'](.*?)["'].*?>/g);
|
|
117
|
+
// Process JS files
|
|
118
|
+
for (const match of scriptMatches) {
|
|
119
|
+
const src = match[1] || '';
|
|
120
|
+
if (isRelativePath(src)) {
|
|
121
|
+
const filePath = (0, path_1.join)(dirPath, src);
|
|
122
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
123
|
+
jsFiles.push(src);
|
|
124
|
+
jsContents.push(processFile(filePath, 'js', new Set()));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Process CSS files
|
|
129
|
+
for (const match of linkMatches) {
|
|
130
|
+
if (match[0].includes('stylesheet')) {
|
|
131
|
+
const href = match[1] || '';
|
|
132
|
+
if (isRelativePath(href)) {
|
|
133
|
+
const filePath = (0, path_1.join)(dirPath, href);
|
|
134
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
135
|
+
cssFiles.push(href);
|
|
136
|
+
cssContents.push(processFile(filePath, 'css', new Set()));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const { external, htmlWithoutRelativeImport } = extractExternalResources(html, dirPath);
|
|
142
|
+
const commonCdnDomains = [
|
|
143
|
+
'https://*.jsdelivr.net',
|
|
144
|
+
'https://*.unpkg.com',
|
|
145
|
+
'https://*.cloudflare.com',
|
|
146
|
+
'https://*.bootstrapcdn.com',
|
|
147
|
+
'https://*.googleapis.com',
|
|
148
|
+
'https://cdn.skypack.dev',
|
|
149
|
+
'https://*.cdnjs.com',
|
|
150
|
+
];
|
|
151
|
+
const cspDirectives = [
|
|
152
|
+
"default-src 'none'",
|
|
153
|
+
`script-src 'unsafe-inline' 'unsafe-eval' ${commonCdnDomains.join(' ')} ${external.js.join(' ')}`,
|
|
154
|
+
`style-src 'unsafe-inline' ${commonCdnDomains.join(' ')} ${external.css.join(' ')}`,
|
|
155
|
+
"img-src 'self' data: https:",
|
|
156
|
+
`connect-src 'self' ${commonCdnDomains.join(' ')} https:`,
|
|
157
|
+
"frame-src 'none'",
|
|
158
|
+
"object-src 'none'",
|
|
159
|
+
"base-uri 'none'",
|
|
160
|
+
"form-action 'none'",
|
|
161
|
+
"font-src 'none'",
|
|
162
|
+
"media-src 'self' https:",
|
|
163
|
+
"manifest-src 'none'",
|
|
164
|
+
"worker-src 'none'",
|
|
165
|
+
].join('; ');
|
|
166
|
+
const securityHeaders = `
|
|
167
|
+
<meta http-equiv="Content-Security-Policy" content="${cspDirectives}">
|
|
168
|
+
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
|
169
|
+
<meta http-equiv="Referrer-Policy" content="no-referrer">
|
|
170
|
+
<meta http-equiv="Permissions-Policy" content="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()">`;
|
|
171
|
+
// 将处理后的相对路径引入的 CSS 和 JS 注入到 HTML 中
|
|
172
|
+
const htmlContent = htmlWithoutRelativeImport
|
|
173
|
+
.replace('<head>', `<head>${securityHeaders}`)
|
|
174
|
+
.replace('</head>', `${cssContents.map((css) => `<style>${css.trim()}</style>`).join('\n')}
|
|
175
|
+
</head>`)
|
|
176
|
+
.replace('</body>', `
|
|
177
|
+
${jsContents.map((js) => `<script>${js}</script>`).join('\n')}
|
|
178
|
+
</body>
|
|
179
|
+
`)
|
|
180
|
+
.trim();
|
|
181
|
+
return {
|
|
182
|
+
html: JSON.stringify(htmlContent).replace(/`/g, '\\`'),
|
|
183
|
+
css: cssContents,
|
|
184
|
+
js: jsContents,
|
|
185
|
+
cssFiles,
|
|
186
|
+
jsFiles,
|
|
187
|
+
external,
|
|
188
|
+
name: (0, path_1.basename)(dirPath),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function generateComponent(content, _isDev = true) {
|
|
192
|
+
const htmlContent = content.html;
|
|
193
|
+
const { name } = content;
|
|
194
|
+
return `import { createElement } from 'react';
|
|
195
|
+
|
|
196
|
+
export default function HtmlPreview() {
|
|
197
|
+
return createElement('iframe', {
|
|
198
|
+
style: { border: 'none', width: '100%', height: '100%' },
|
|
199
|
+
sandbox: 'allow-scripts',
|
|
200
|
+
title: 'Preview ${name}',
|
|
201
|
+
srcDoc: ${htmlContent}
|
|
202
|
+
});
|
|
203
|
+
}`;
|
|
204
|
+
}
|
|
205
|
+
function initHtmlPreviewTransformPlugin() {
|
|
206
|
+
const htmlCache = new Map();
|
|
207
|
+
let isBuild = false;
|
|
208
|
+
return {
|
|
209
|
+
name: 'vite-plugin-html-preview',
|
|
210
|
+
configResolved(config) {
|
|
211
|
+
isBuild = config.command === 'build';
|
|
212
|
+
},
|
|
213
|
+
resolveId(id) {
|
|
214
|
+
// 识别 virtual:html-preview 的 id,build 的时候会返回一个带文件路径的 id,所以这里需要使用 includes 而不是 startsWith
|
|
215
|
+
if (id.includes(exports.VIRTUAL_MODULE_ID)) {
|
|
216
|
+
const dirPath = id.split('?dir=')[1];
|
|
217
|
+
return `${exports.RESOLVED_VIRTUAL_MODULE_ID}?dir=${dirPath}`;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
},
|
|
221
|
+
load(id) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
const useCache = false;
|
|
224
|
+
if (id.startsWith(exports.RESOLVED_VIRTUAL_MODULE_ID)) {
|
|
225
|
+
const dirPath = id.split('?dir=')[1] || '';
|
|
226
|
+
// 使用缓存避免重复读取
|
|
227
|
+
let content = useCache ? htmlCache.get(dirPath) : null;
|
|
228
|
+
if (!content) {
|
|
229
|
+
content = readHtmlFiles(dirPath);
|
|
230
|
+
if (useCache) {
|
|
231
|
+
htmlCache.set(dirPath, content);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return generateComponent(content, !isBuild);
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
// 清理缓存
|
|
240
|
+
buildEnd() {
|
|
241
|
+
htmlCache.clear();
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
exports.default = initHtmlPreviewTransformPlugin;
|
|
@@ -0,0 +1,211 @@
|
|
|
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
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.initRemoteScriptLocalizerPlugin = initRemoteScriptLocalizerPlugin;
|
|
46
|
+
/* eslint-disable no-console */
|
|
47
|
+
const crypto_1 = require("crypto");
|
|
48
|
+
const promises_1 = require("fs/promises");
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
function initRemoteScriptLocalizerPlugin(options = {}) {
|
|
51
|
+
const { tempDir = 'temp/remote-scripts', maxConcurrent = 5, timeout = 30000 } = options;
|
|
52
|
+
let initialized = false;
|
|
53
|
+
const downloadQueue = [];
|
|
54
|
+
const initTempDir = () => __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
try {
|
|
56
|
+
yield (0, promises_1.rm)(tempDir, { recursive: true, force: true });
|
|
57
|
+
yield (0, promises_1.mkdir)(tempDir, { recursive: true });
|
|
58
|
+
initialized = true;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error('Failed to initialize temp directory:', error);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const downloadWithTimeout = (url, ms) => __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
const timeoutId = setTimeout(() => controller.abort(), ms);
|
|
68
|
+
try {
|
|
69
|
+
const response = yield fetch(url, { signal: controller.signal });
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
return response;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const downloadScript = (url) => __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
var _a;
|
|
80
|
+
try {
|
|
81
|
+
const response = yield downloadWithTimeout(url, timeout);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
const content = yield response.text();
|
|
86
|
+
const hash = (0, crypto_1.createHash)('md5').update(url).digest('hex').slice(0, 8);
|
|
87
|
+
const filename = `${hash}-${((_a = url
|
|
88
|
+
.split('/')
|
|
89
|
+
.pop()) === null || _a === void 0 ? void 0 : _a.replace(/[^a-zA-Z0-9.-]/g, '_')) || 'script.js'}`;
|
|
90
|
+
const localPath = path.join(tempDir, filename);
|
|
91
|
+
yield (0, promises_1.writeFile)(localPath, content, 'utf-8');
|
|
92
|
+
return localPath;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error(`Error downloading script from ${url}:`, error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const processDownloads = (urls) => __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
const results = new Map();
|
|
101
|
+
// Process downloads in chunks to limit concurrency
|
|
102
|
+
for (let i = 0; i < urls.length; i += maxConcurrent) {
|
|
103
|
+
const chunk = urls.slice(i, i + maxConcurrent);
|
|
104
|
+
const promises = chunk.map((url) => __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
try {
|
|
106
|
+
const localPath = yield downloadScript(url);
|
|
107
|
+
results.set(url, localPath);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.warn(`Failed to download ${url}:`, error);
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
113
|
+
downloadQueue.push(...promises);
|
|
114
|
+
yield Promise.all(promises);
|
|
115
|
+
}
|
|
116
|
+
return results;
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
name: 'remote-script-localizer',
|
|
120
|
+
enforce: 'pre',
|
|
121
|
+
// 添加 resolveId 钩子来处理虚拟模块
|
|
122
|
+
resolveId(id, importer) {
|
|
123
|
+
console.log('[remote-script-localizer] resolveId:', { id, importer });
|
|
124
|
+
return null; // 让其他插件继续处理
|
|
125
|
+
},
|
|
126
|
+
// 添加 load 钩子来处理虚拟模块的加载
|
|
127
|
+
load(id) {
|
|
128
|
+
console.log('[remote-script-localizer] load:', id);
|
|
129
|
+
return null; // 让其他插件继续处理
|
|
130
|
+
},
|
|
131
|
+
transform(code, id) {
|
|
132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
+
// 添加调试日志
|
|
134
|
+
console.log('[remote-script-localizer] transform:', {
|
|
135
|
+
id,
|
|
136
|
+
isVirtual: id.includes('\0'),
|
|
137
|
+
codeLength: code.length,
|
|
138
|
+
});
|
|
139
|
+
// 检查文件类型
|
|
140
|
+
const isJS = /\.[jt]sx?$/.test(id);
|
|
141
|
+
const isHTML = /\.html$/.test(id);
|
|
142
|
+
if (!isJS && !isHTML)
|
|
143
|
+
return null;
|
|
144
|
+
// 匹配多种远程脚本模式,包括模板字符串中的内容
|
|
145
|
+
const patterns = [
|
|
146
|
+
// 动态导入
|
|
147
|
+
/import\s*\(\s*['"]https?:\/\/[^'"]+['"]\s*\)/g,
|
|
148
|
+
// HTML script 标签 (包括转义的版本)
|
|
149
|
+
/<script[^>]*src=["'](https?:\/\/[^"']+)["'][^>]*>(?:<\\\/script>)?/g,
|
|
150
|
+
// 模板字符串中的 script 标签
|
|
151
|
+
/`[^`]*<script[^>]*src=["'](https?:\/\/[^"']+)["'][^>]*>(?:<\\\/script>)?[^`]*`/g,
|
|
152
|
+
// HTML link 标签
|
|
153
|
+
/<link[^>]*href=["'](https?:\/\/[^"']+)["'][^>]*>/g,
|
|
154
|
+
];
|
|
155
|
+
let hasRemoteUrls = false;
|
|
156
|
+
let newCode = code;
|
|
157
|
+
const urls = new Set();
|
|
158
|
+
// 收集所有远程 URL
|
|
159
|
+
patterns.forEach((pattern) => {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
const matches = code.matchAll(pattern);
|
|
162
|
+
for (const match of matches) {
|
|
163
|
+
hasRemoteUrls = true;
|
|
164
|
+
// 提取 URL(处理不同的匹配组)
|
|
165
|
+
const url = match[1] || ((_b = (_a = match[0].match(/['"]https?:\/\/[^'"]+['"]/)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1));
|
|
166
|
+
if (url)
|
|
167
|
+
urls.add(url);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
if (hasRemoteUrls) {
|
|
171
|
+
console.log(`[remote-script-localizer] Found remote URLs in ${id}:`, [...urls]);
|
|
172
|
+
const downloadResults = yield processDownloads([...urls]);
|
|
173
|
+
downloadResults.forEach((localPath, url) => {
|
|
174
|
+
// 替换模板字符串中的 script 标签
|
|
175
|
+
newCode = newCode.replace(new RegExp(`<script([^>]*)src=["']${url}["']([^>]*)>(?:<\\\\/script>)?`, 'g'), `<script$1src="/${localPath}"$2></script>`);
|
|
176
|
+
// 替换其他情况
|
|
177
|
+
newCode = newCode.replace(new RegExp(`(['"\`])${url}\\1`, 'g'), `$1/${localPath}$1`);
|
|
178
|
+
console.log(`[remote-script-localizer] Localized: ${url} -> ${localPath}`);
|
|
179
|
+
});
|
|
180
|
+
return {
|
|
181
|
+
code: newCode,
|
|
182
|
+
map: null,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
buildStart() {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
if (!initialized) {
|
|
191
|
+
yield initTempDir();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
buildEnd() {
|
|
196
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
197
|
+
// Wait for any remaining downloads to complete
|
|
198
|
+
yield Promise.all(downloadQueue);
|
|
199
|
+
// Clean up temp directory
|
|
200
|
+
try {
|
|
201
|
+
// await rm(tempDir, { recursive: true, force: true });
|
|
202
|
+
// console.log('Cleaned up temporary remote scripts directory');
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error('Failed to clean up temp directory:', error);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
exports.default = initRemoteScriptLocalizerPlugin;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = void 0;
|
|
7
|
+
exports.setBlockEntryFilesPattern = setBlockEntryFilesPattern;
|
|
8
|
+
exports.getBlockEntryFilesPattern = getBlockEntryFilesPattern;
|
|
9
|
+
exports.findComponentFiles = findComponentFiles;
|
|
10
|
+
exports.getBlockName = getBlockName;
|
|
11
|
+
const glob_1 = require("glob");
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const DEFAULT_BLOCK_ENTRY_FILES_PATTERN = 'src/**/index.{ts,tsx,html}';
|
|
14
|
+
if (!process.env.BLOCK_ENTRY_FILES_PATTERN) {
|
|
15
|
+
process.env.BLOCK_ENTRY_FILES_PATTERN = DEFAULT_BLOCK_ENTRY_FILES_PATTERN;
|
|
16
|
+
}
|
|
17
|
+
function normalizePattern(pattern) {
|
|
18
|
+
return pattern.replace(/^[/\\]+/, ''); // Remove leading slashes
|
|
19
|
+
}
|
|
20
|
+
function setBlockEntryFilesPattern(pattern) {
|
|
21
|
+
process.env.BLOCK_ENTRY_FILES_PATTERN = normalizePattern(pattern);
|
|
22
|
+
}
|
|
23
|
+
function getBlockEntryFilesPattern() {
|
|
24
|
+
return process.env.BLOCK_ENTRY_FILES_PATTERN || DEFAULT_BLOCK_ENTRY_FILES_PATTERN;
|
|
25
|
+
}
|
|
26
|
+
function findComponentFiles(options = {}) {
|
|
27
|
+
const { cwd = process.cwd(), filter } = options;
|
|
28
|
+
const files = (0, glob_1.globSync)(getBlockEntryFilesPattern(), { cwd });
|
|
29
|
+
return files
|
|
30
|
+
.map((file) => {
|
|
31
|
+
const blockName = getBlockName(file);
|
|
32
|
+
const fullPath = path_1.default.resolve(cwd, file);
|
|
33
|
+
const isHtml = file.endsWith('.html');
|
|
34
|
+
return {
|
|
35
|
+
file,
|
|
36
|
+
blockName,
|
|
37
|
+
fullPath,
|
|
38
|
+
isHtml,
|
|
39
|
+
};
|
|
40
|
+
})
|
|
41
|
+
.filter(({ blockName }) => !(filter === null || filter === void 0 ? void 0 : filter.length) || filter.includes(blockName || ''));
|
|
42
|
+
}
|
|
43
|
+
function getBlockName(entry) {
|
|
44
|
+
// First try to match index.{ts,tsx,html} pattern
|
|
45
|
+
const indexMatch = entry.match(/(.*)\/index\.(ts|tsx|html)$/);
|
|
46
|
+
if (indexMatch) {
|
|
47
|
+
const [, pathWithoutIndex] = indexMatch;
|
|
48
|
+
return path_1.default.basename(pathWithoutIndex || 'unknown');
|
|
49
|
+
}
|
|
50
|
+
// If not an index file, return the filename without extension
|
|
51
|
+
return path_1.default.basename(entry, path_1.default.extname(entry));
|
|
52
|
+
}
|
|
53
|
+
exports.logger = console;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProjectRuntime = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const home_1 = __importDefault(require("@blocklet/pages-kit-inner-components/home"));
|
|
10
|
+
const react_1 = __importDefault(require("react"));
|
|
11
|
+
const client_1 = require("react-dom/client");
|
|
12
|
+
class ProjectRuntime {
|
|
13
|
+
constructor({ state, projectId }) {
|
|
14
|
+
this.state = state;
|
|
15
|
+
this.projectId = projectId;
|
|
16
|
+
// eslint-disable-next-line no-console
|
|
17
|
+
console.log('ProjectRuntime', this.state, this.projectId);
|
|
18
|
+
}
|
|
19
|
+
render(container) {
|
|
20
|
+
const root = (0, client_1.createRoot)(container);
|
|
21
|
+
root.render((0, jsx_runtime_1.jsx)(react_1.default.StrictMode, { children: (0, jsx_runtime_1.jsx)(home_1.default, {}) }));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.ProjectRuntime = ProjectRuntime;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateResource = CreateResource;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ui_react_1 = require("@blocklet/ui-react");
|
|
6
|
+
const material_1 = require("@mui/material");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
function CreateResource({ open, onClose, blockletDid, mode, }) {
|
|
9
|
+
if (!open) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const tenantScope = 'pages-kit-block-studio';
|
|
13
|
+
const isPage = mode === 'page';
|
|
14
|
+
const boxProps = isPage
|
|
15
|
+
? {
|
|
16
|
+
sx: {
|
|
17
|
+
iframe: {
|
|
18
|
+
width: 'calc(100% - 16px) !important',
|
|
19
|
+
height: 'calc(100% - 16px) !important',
|
|
20
|
+
padding: '16px !important',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
: {};
|
|
25
|
+
return ((0, jsx_runtime_1.jsx)(material_1.Box, Object.assign({}, boxProps, { children: (0, jsx_runtime_1.jsx)(react_1.Suspense, { children: (0, jsx_runtime_1.jsx)(ui_react_1.BlockletStudio, { mode: mode, tenantScope: tenantScope, title: "Pages Kit Blocks", description: "", note: "", introduction: "", logo: "", componentDid: blockletDid,
|
|
26
|
+
// 透传到 get blocklet resource 的参数
|
|
27
|
+
resourcesParams: {}, dependentComponentsMode: "readonly", open: true, setOpen: () => onClose(), onConnected: () => { }, onUploaded: () => { }, onReleased: () => { },
|
|
28
|
+
// onOpened={() => onOpened?.()}
|
|
29
|
+
// 默认选中的资源
|
|
30
|
+
resources: {} }) }) })));
|
|
31
|
+
}
|
|
32
|
+
exports.default = CreateResource;
|
|
@@ -0,0 +1,17 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./create-resource"), exports);
|
package/lib/cjs/index.js
ADDED