openyida 2026.7.23 → 2026.7.25

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.
@@ -25,10 +25,14 @@
25
25
  * 这些 UMD 依赖由画布运行时依据 importedModules 白名单按需注入。
26
26
  *
27
27
  * 因此本地编译 = Babel 把 JSX/TS 转成 ES5 → 把 import 改写成 window 别名引用、
28
- * 把 export default 改写成 `var YidaComp = ...` → 正则抽出依赖包名。
28
+ * 把 export default 改写成画布入口 `YidaComp` → 正则抽出依赖包名。
29
29
  */
30
30
 
31
31
  const Babel = require('@babel/standalone');
32
+ const {
33
+ assertNoEmojiInArtifactName,
34
+ assertNoEmojiInText,
35
+ } = require('../core/no-emoji-guard');
32
36
 
33
37
  /**
34
38
  * 依赖 → window 别名白名单。
@@ -148,6 +152,15 @@ function esmToWindowPlugin({ types: t }, options = {}) {
148
152
  );
149
153
  }
150
154
 
155
+ function hasProgramBinding(path, name) {
156
+ const program = path.findParent((parentPath) => parentPath.isProgram());
157
+ return Boolean(program && program.scope.hasBinding(name));
158
+ }
159
+
160
+ function isExistingYidaCompDefault(path, decl) {
161
+ return t.isIdentifier(decl, { name: 'YidaComp' }) && hasProgramBinding(path, 'YidaComp');
162
+ }
163
+
151
164
  return {
152
165
  name: 'yida-esm-to-window',
153
166
  visitor: {
@@ -213,6 +226,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
213
226
  const decl = path.node.declaration;
214
227
  if (t.isFunctionDeclaration(decl) || t.isClassDeclaration(decl)) {
215
228
  if (decl.id) {
229
+ if (decl.id.name === 'YidaComp') {
230
+ path.replaceWith(decl);
231
+ return;
232
+ }
216
233
  // 具名声明:保留声明本体,再补 `var YidaComp = <name>;`
217
234
  const idName = decl.id.name;
218
235
  path.replaceWithMultiple([
@@ -231,6 +248,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
231
248
  );
232
249
  return;
233
250
  }
251
+ if (isExistingYidaCompDefault(path, decl)) {
252
+ path.remove();
253
+ return;
254
+ }
234
255
  // export default <表达式>
235
256
  path.replaceWith(
236
257
  t.variableDeclaration('var', [t.variableDeclarator(t.identifier('YidaComp'), decl)])
@@ -254,12 +275,48 @@ function esmToWindowPlugin({ types: t }, options = {}) {
254
275
  };
255
276
  }
256
277
 
278
+ function buildCanvasRuntimeWrapper(runtimeCode) {
279
+ return (
280
+ 'return function(iframeWindow, parentWindow){ const window = iframeWindow; '
281
+ + runtimeCode
282
+ + ' return YidaComp; }'
283
+ );
284
+ }
285
+
286
+ function assertCanvasRuntimeParseable(runtimeCode, options = {}) {
287
+ try {
288
+ // 只编译 wrapper,不执行 factory 或用户组件,避免触发源码里的运行期副作用。
289
+ // eslint-disable-next-line no-new-func
290
+ new Function(buildCanvasRuntimeWrapper(runtimeCode));
291
+ } catch (error) {
292
+ const detail = error && error.message ? error.message : String(error);
293
+ const sourceLabel = options.sourcePath ? ` (${options.sourcePath})` : '';
294
+ const compileError = new Error(
295
+ `Code Canvas runtimeCode 无法通过画布运行时装配校验${sourceLabel}: ${detail}`
296
+ + '。请检查默认导出、YidaComp 入口或 window 等运行时保留变量是否重复声明。'
297
+ );
298
+ compileError.code = 'OPENYIDA_CANVAS_RUNTIME_PARSE_FAILED';
299
+ compileError.cause = error;
300
+ throw compileError;
301
+ }
302
+ }
303
+
257
304
  /**
258
305
  * 本地编译 Code Canvas 源码。
259
306
  * @param {string} source 原始 React/JSX/TSX 源码
260
307
  * @returns {{ runtimeCode: string, importedModules: string }}
261
308
  */
262
309
  function compileCanvasLocal(source, options = {}) {
310
+ if (options.sourcePath) {
311
+ assertNoEmojiInArtifactName(options.sourcePath, {
312
+ code: 'OPENYIDA_PAGE_FILENAME_EMOJI_FORBIDDEN',
313
+ });
314
+ }
315
+ assertNoEmojiInText(source, {
316
+ artifact: options.sourcePath || 'Code Canvas source',
317
+ code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
318
+ });
319
+
263
320
  const importedModules = extractImportedModules(source);
264
321
 
265
322
  // 第一步:剥类型 + 转 JSX(classic runtime,产出 React.createElement,
@@ -304,6 +361,11 @@ function compileCanvasLocal(source, options = {}) {
304
361
  });
305
362
 
306
363
  const runtimeCode = stage2.code || '';
364
+ assertNoEmojiInText(runtimeCode, {
365
+ artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
366
+ code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
367
+ });
368
+ assertCanvasRuntimeParseable(runtimeCode, options);
307
369
  return {
308
370
  runtimeCode,
309
371
  importedModules: JSON.stringify(importedModules),
@@ -338,5 +400,6 @@ module.exports = {
338
400
  extractImportedModules,
339
401
  resolveWindowAlias,
340
402
  shouldAllowUnsupportedBareImports,
403
+ assertCanvasRuntimeParseable,
341
404
  MODULE_ALIAS_MAP,
342
405
  };