openyida 2026.7.21 → 2026.7.23-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/bin/yida.js CHANGED
@@ -14,7 +14,7 @@ const { version: currentVersion } = require('../package.json');
14
14
  const { t } = require('../lib/core/i18n');
15
15
  const { warn } = require('../lib/core/chalk');
16
16
  const { CliError, isCliError, toErrorPayload } = require('../lib/core/cli-error');
17
- const { COMMAND_GROUPS, buildCommandManifest } = require('../lib/core/command-manifest');
17
+ const { COMMAND_GROUPS, buildCommandManifest, findCommandSuggestion } = require('../lib/core/command-manifest');
18
18
 
19
19
  const command = process.argv[2];
20
20
  const args = process.argv.slice(3);
@@ -477,6 +477,33 @@ function throwCliUsage(...lines) {
477
477
  });
478
478
  }
479
479
 
480
+ function formatSuggestionMessage(suggestion) {
481
+ if (!suggestion) {
482
+ return '';
483
+ }
484
+ if (suggestion.message_key) {
485
+ return t(suggestion.message_key, ...(suggestion.message_args || []));
486
+ }
487
+ return '';
488
+ }
489
+
490
+ function throwUnknownCommand(commandName, commandArgs = []) {
491
+ const suggestion = findCommandSuggestion([commandName, ...commandArgs]);
492
+ const lines = [t('cli.unknown_command', commandName)];
493
+ if (suggestion) {
494
+ lines.push(t('cli.command_suggestion', suggestion.suggested_usage));
495
+ const suggestionMessage = formatSuggestionMessage(suggestion);
496
+ if (suggestionMessage) {
497
+ lines.push(suggestionMessage);
498
+ }
499
+ }
500
+ lines.push(t('cli.run_help'));
501
+ throw new CliError(lines.filter(Boolean).join('\n'), {
502
+ code: 'INVALID_ARGUMENTS',
503
+ details: suggestion ? { suggestion } : undefined,
504
+ });
505
+ }
506
+
480
507
  function hasHelpFlag(cliArgs = []) {
481
508
  return cliArgs.includes('--help') || cliArgs.includes('-h');
482
509
  }
@@ -1147,7 +1174,7 @@ async function main() {
1147
1174
  }
1148
1175
 
1149
1176
  default: {
1150
- throwCliUsage(t('cli.unknown_command', command), t('cli.run_help'));
1177
+ throwUnknownCommand(command, args);
1151
1178
  }
1152
1179
  }
1153
1180
  }
@@ -35,8 +35,9 @@ const Babel = require('@babel/standalone');
35
35
  * 逐条镜像自 @ali/vc-deep-yida 的
36
36
  * src/components/yida-code-canvas/dependencies.ts → getModuleAliasMap()
37
37
  * 只保留运行时真正用到的 windowAlias(资源 URL 由画布运行时按别名注入,
38
- * 本地编译不关心 CDN 地址)。若此处与物料白名单漂移,编译产物仍可运行,
39
- * 只是未收录的包会在浏览器端 `${name} is not found in dependencies map` 告警。
38
+ * 本地编译不关心 CDN 地址)。默认拒绝未收录依赖;如果宜搭物料依赖表已经
39
+ * 先于 CLI 升级,可临时设置 OPENYIDA_CANVAS_ALLOW_UNSUPPORTED_IMPORTS=1
40
+ * 退回旧的 window["pkg"] 映射,避免白名单漂移阻断发布。
40
41
  * @type {Record<string, string>}
41
42
  */
42
43
  const MODULE_ALIAS_MAP = {
@@ -59,6 +60,19 @@ const IMPORT_SIDE_EFFECT_PATTERN = /import\s+['"]([^'"]+)['"]/g;
59
60
  const REQUIRE_PATTERN = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
60
61
  const DYNAMIC_IMPORT_PATTERN = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
61
62
 
63
+ function isTruthy(value) {
64
+ return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
65
+ }
66
+
67
+ function shouldAllowUnsupportedBareImports(options = {}, env = process.env) {
68
+ return options.allowUnsupportedBareImports === true ||
69
+ isTruthy(env.OPENYIDA_CANVAS_ALLOW_UNSUPPORTED_IMPORTS);
70
+ }
71
+
72
+ function packageTempName(pkg) {
73
+ return String(pkg || 'module').replace(/[^A-Za-z0-9_$]+/g, '_') || 'module';
74
+ }
75
+
62
76
  function stripJsComments(code) {
63
77
  return String(code || '')
64
78
  .replace(/\/\*[\s\S]*?\*\//g, '')
@@ -113,16 +127,27 @@ function resolveWindowAlias(pkg) {
113
127
  * export const x = ... → const x = ...(去掉 export 关键字)
114
128
  * @param {{ types: import('@babel/types') }} babel
115
129
  */
116
- function esmToWindowPlugin({ types: t }) {
117
- function moduleExpr(pkg) {
118
- const alias = resolveWindowAlias(pkg);
130
+ function esmToWindowPlugin({ types: t }, options = {}) {
131
+ const allowUnsupportedBareImports = options.allowUnsupportedBareImports === true;
132
+
133
+ function moduleExpr(pkg, alias) {
119
134
  if (alias) {
120
135
  return t.memberExpression(t.identifier('window'), t.identifier(alias));
121
136
  }
122
- // 未收录包:退化为 window["pkg"],运行时若未注入会自然报错(与线上一致)。
123
137
  return t.memberExpression(t.identifier('window'), t.stringLiteral(pkg), true);
124
138
  }
125
139
 
140
+ function buildUnsupportedBareImportError(path, pkg) {
141
+ return path.buildCodeFrameError(
142
+ `Code Canvas 不支持从裸包 "${pkg}" 导入绑定。`
143
+ + '只允许 MODULE_ALIAS_MAP 白名单依赖;'
144
+ + '宜搭平台运行态全局对象请显式使用 window.* 访问'
145
+ + '(例如 window.Deep、window.DeepYida、window.YidaNativeComponents),不要从包中 import。'
146
+ + '若已确认宜搭物料运行态已注入该包且 CLI 白名单滞后,可临时设置 '
147
+ + 'OPENYIDA_CANVAS_ALLOW_UNSUPPORTED_IMPORTS=1 退回 legacy window["pkg"] 映射。'
148
+ );
149
+ }
150
+
126
151
  return {
127
152
  name: 'yida-esm-to-window',
128
153
  visitor: {
@@ -140,10 +165,14 @@ function esmToWindowPlugin({ types: t }) {
140
165
  path.remove();
141
166
  return;
142
167
  }
168
+ const alias = resolveWindowAlias(pkg);
169
+ if (!alias && !allowUnsupportedBareImports) {
170
+ throw buildUnsupportedBareImportError(path, pkg);
171
+ }
143
172
 
144
173
  const decls = [];
145
- const tmp = path.scope.generateUidIdentifier(resolveWindowAlias(pkg) || 'mod');
146
- decls.push(t.variableDeclarator(t.cloneNode(tmp), moduleExpr(pkg)));
174
+ const tmp = path.scope.generateUidIdentifier(alias || packageTempName(pkg));
175
+ decls.push(t.variableDeclarator(t.cloneNode(tmp), moduleExpr(pkg, alias)));
147
176
 
148
177
  const namedProps = [];
149
178
  for (const spec of specifiers) {
@@ -230,7 +259,7 @@ function esmToWindowPlugin({ types: t }) {
230
259
  * @param {string} source 原始 React/JSX/TSX 源码
231
260
  * @returns {{ runtimeCode: string, importedModules: string }}
232
261
  */
233
- function compileCanvasLocal(source) {
262
+ function compileCanvasLocal(source, options = {}) {
234
263
  const importedModules = extractImportedModules(source);
235
264
 
236
265
  // 第一步:剥类型 + 转 JSX(classic runtime,产出 React.createElement,
@@ -265,7 +294,9 @@ function compileCanvasLocal(source) {
265
294
  // 第二步:把 import/export 改写成 window 别名 + YidaComp。
266
295
  const stage2 = Babel.transform(intermediate, {
267
296
  filename: 'canvas.js',
268
- plugins: [esmToWindowPlugin],
297
+ plugins: [[esmToWindowPlugin, {
298
+ allowUnsupportedBareImports: shouldAllowUnsupportedBareImports(options),
299
+ }]],
269
300
  sourceType: 'module',
270
301
  compact: false,
271
302
  babelrc: false,
@@ -286,14 +317,14 @@ function compileCanvasLocal(source) {
286
317
  * @param {object} [options] 兼容占位,未使用
287
318
  * @returns {Promise<{ runtimeCode: string, importedModules: string }>}
288
319
  */
289
- function compileCanvas(source, options = {}) { // eslint-disable-line no-unused-vars
320
+ function compileCanvas(source, options = {}) {
290
321
  return new Promise((resolve, reject) => {
291
322
  if (typeof source !== 'string' || source.trim() === '') {
292
323
  reject(new Error('canvas 编译源码为空'));
293
324
  return;
294
325
  }
295
326
  try {
296
- resolve(compileCanvasLocal(source));
327
+ resolve(compileCanvasLocal(source, options));
297
328
  } catch (compileError) {
298
329
  const detail = compileError && compileError.message ? compileError.message : String(compileError);
299
330
  reject(new Error(`Code Canvas 本地编译失败: ${detail}`));
@@ -306,5 +337,6 @@ module.exports = {
306
337
  compileCanvasLocal,
307
338
  extractImportedModules,
308
339
  resolveWindowAlias,
340
+ shouldAllowUnsupportedBareImports,
309
341
  MODULE_ALIAS_MAP,
310
342
  };
@@ -17,6 +17,26 @@ function readJsonInput(value, options) {
17
17
  return fs.readFileSync(resolvedPath, 'utf-8');
18
18
  }
19
19
 
20
+ function normalizeCreateFields(fields) {
21
+ if (
22
+ !Array.isArray(fields)
23
+ || fields.length === 0
24
+ || !fields.every((item) => (
25
+ item
26
+ && typeof item === 'object'
27
+ && !Array.isArray(item)
28
+ && String(item.action || '').toLowerCase() === 'add'
29
+ && item.field
30
+ && typeof item.field === 'object'
31
+ && !Array.isArray(item.field)
32
+ ))
33
+ ) {
34
+ return fields;
35
+ }
36
+
37
+ return fields.map((item) => item.field);
38
+ }
39
+
20
40
  function createDefinitionReaders(dependencies) {
21
41
  const {
22
42
  fs,
@@ -43,9 +63,9 @@ function createDefinitionReaders(dependencies) {
43
63
  let columns = 1;
44
64
 
45
65
  if (Array.isArray(parsed)) {
46
- fields = parsed;
66
+ fields = normalizeCreateFields(parsed);
47
67
  } else if (typeof parsed === 'object' && parsed !== null) {
48
- fields = parsed.fields || [];
68
+ fields = normalizeCreateFields(parsed.fields || []);
49
69
  columns = parsed.columns !== undefined ? parsed.columns : 1;
50
70
  if (Array.isArray(parsed.validations)) {
51
71
  validations = parsed.validations;