@chaoswise/intl 3.0.0 → 3.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.
@@ -3,29 +3,37 @@ const runCollect = require('./scripts/collect');
3
3
  const runUpdate = require('./scripts/update');
4
4
  const runInitConfig = require('./scripts/initConfig');
5
5
  const runAddLocale = require('./scripts/addLocale');
6
+ const runNoZhCn = require('./scripts/nozhcn');
6
7
 
7
- // 可执行的指令
8
- const SCRIPTS = ['intl', 'collect', 'update'];
8
+ const SCRIPTS = ['intl', 'collect', 'update', 'nozhcn', 'addLocale', 'init'];
9
9
 
10
10
  const args = process.argv.slice(2);
11
11
 
12
- const scriptIndex = args.findIndex(arg => {
13
- const script = SCRIPTS.filter(script => arg === script)[0];
14
- return arg === script;
15
- });
16
-
12
+ const scriptIndex = args.findIndex(arg => SCRIPTS.includes(arg));
17
13
  const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
18
14
 
19
- if(script === 'init') {
15
+ if (script === 'init') {
20
16
  runInitConfig();
21
17
  }
22
- if(script === 'collect') {
18
+ if (script === 'collect') {
23
19
  runCollect();
24
20
  }
25
- if(script === 'update') {
21
+ if (script === 'update') {
26
22
  runUpdate();
27
23
  }
28
- // 收集全部语种
29
- if(script === 'addLocale') {
24
+ if (script === 'addLocale') {
30
25
  runAddLocale();
31
26
  }
27
+ // nozhcn: detect (and optionally fix) Chinese characters in source files
28
+ // Usage:
29
+ // chaoswise-intl nozhcn → check mode (CI gate, exit 1 on findings)
30
+ // chaoswise-intl nozhcn check → same as above
31
+ // chaoswise-intl nozhcn fix → auto-fix comments, report remaining issues
32
+ // chaoswise-intl nozhcn report → non-blocking scan, output JSON report
33
+ if (script === 'nozhcn') {
34
+ // The sub-mode is the argument after 'nozhcn', default to 'check'
35
+ const modeArg = args[scriptIndex + 1];
36
+ const validModes = ['check', 'fix', 'report'];
37
+ const mode = validModes.includes(modeArg) ? modeArg : 'check';
38
+ runNoZhCn(mode);
39
+ }
@@ -94,7 +94,55 @@ module.exports = function (excludes = []) {
94
94
 
95
95
  // 国际化平台地址
96
96
  baseURL: 'http://10.0.1.133:18000',
97
- };
97
+
98
+ // ─────────────────────────────────────────────────────────────────
99
+ // nozhcn (No Chinese) 专属配置
100
+ // ─────────────────────────────────────────────────────────────────
101
+
102
+ // 需要检测的中文类型,可按需删减:
103
+ // 'comment' - 注释(可自动修复)
104
+ // 'string' - 字符串字面量
105
+ // 'template' - 模板字符串
106
+ // 'jsx' - JSX 文本内容
107
+ // 'identifier' - 标识符名称(极少出现,需手动修复)
108
+ noZhCnCheckTypes: ['comment', 'string', 'template', 'jsx'],
109
+
110
+ // 注释中文修复策略(fix 模式生效):
111
+ // 'clean' - 仅删除中文字符,保留非中文内容;若注释全为中文则整条删除
112
+ // 'remove' - 含中文的注释整条全部删除
113
+ noZhCnCommentStrategy: 'clean',
114
+
115
+ // 报告文件输出路径(null 表示不输出报告文件,report 模式默认使用此路径)
116
+ noZhCnReportFile: null,
117
+
118
+ // nozhcn 专属文件排除规则(叠加到主 exclude 之上)
119
+ noZhCnExclude: [],
120
+
121
+ // 是否忽略 intl.get('key').d('中文') 中 .d() 内的默认中文值
122
+ // 说明:collect 脚本会将中文字符串替换为 intl.get('key').d('原始中文')
123
+ // .d() 中的中文是有意保留的回退值,开启此选项可跳过这类误报
124
+ noZhCnIgnoreI18nDefault: true,
125
+
126
+ // fix 模式下,将 .d('中文') 替换为指定语言的翻译文本。
127
+ // 值为语言包文件名(不含 .json),对应 localeOutput/locales/{lang}.json。
128
+ // 例如:'en_US' → 读取 src/public/locales/en_US.json 中对应 key 的翻译
129
+ // null 表示不执行此替换(保持现有 .d() 内容不变)
130
+ noZhCnDefaultLang: null,
131
+ // 是否扫描项目中的独立 .svg 文件(默认关闭,按需开启)
132
+ // SVG 文件无法用 Babel AST 解析,采用正则扫描
133
+ noZhCnIncludeSvg: false,
134
+
135
+ // SVG 元数据元素(<title>、<desc>、<metadata>)的修复策略(fix 模式生效):
136
+ // 'remove' - 删除整个元素
137
+ // 'empty' - 保留元素但清空内容:<title>搜索</title> → <title></title>
138
+ noZhCnSvgMetadataStrategy: 'remove',
139
+
140
+ // SVG 属性中文修复策略(fix 模式生效):
141
+ // 'clean' - 删除属性值中的中文字符(及 CJK 标点),清理后为空则移除整个属性;
142
+ // 若修改了 id 属性,同步更新 SVG 内部引用(url(#…), href)
143
+ // 'remove' - 移除包含中文的属性
144
+ // false - 不自动修复属性中的中文
145
+ noZhCnSvgAttrStrategy: 'clean', };
98
146
 
99
147
  excludes.forEach((key) => delete config[key]);
100
148
 
@@ -0,0 +1,440 @@
1
+ /**
2
+ * nozhcn.js ─ No Chinese Characters
3
+ *
4
+ * Orchestrates scanning and (optionally) fixing Chinese characters in
5
+ * all source files covered by the intl configuration.
6
+ *
7
+ * Modes
8
+ * ─────
9
+ * check (default) Scan every file. Print findings. Exit with code 1 if
10
+ * any Chinese is found — suitable for CI/CD gate use.
11
+ *
12
+ * fix Scan → auto-fix comments + .d() defaults + SVG →
13
+ * single re-scan → print remaining findings.
14
+ * Exit with code 1 if unfixable findings remain.
15
+ *
16
+ * report Like check but writes a structured JSON report file
17
+ * even when findings exist. Does NOT exit with code 1
18
+ * (non-blocking, good for dashboards / tracking).
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const glob = require('glob');
24
+ const chalk = require('chalk');
25
+ const getConf = require('./conf');
26
+ const { targetEntryFiles } = require('./util/getTargetFiles');
27
+ const findZhCnInFile = require('./util/findZhCnInFile');
28
+ const fixZhCnInFile = require('./util/fixZhCnInFile');
29
+ const fixI18nDefaultInFile = require('./util/fixI18nDefaultInFile');
30
+ const findZhCnInSvgFile = require('./util/findZhCnInSvgFile');
31
+ const fixZhCnInSvgFile = require('./util/fixZhCnInSvgFile');
32
+ const log = require('./util/log');
33
+ const file = require('./util/file');
34
+
35
+ // ─── Display labels ──────────────────────────────────────────────────────────
36
+
37
+ const TYPE_LABEL = {
38
+ comment: 'Comment ',
39
+ string: 'String ',
40
+ template: 'Template ',
41
+ jsx: 'JSX Text ',
42
+ 'jsx-svg': 'JSX SVG ',
43
+ identifier: 'Identifier',
44
+ 'svg-comment': 'SVG Comment ',
45
+ 'svg-metadata': 'SVG Metadata',
46
+ 'svg-text': 'SVG Text ',
47
+ 'svg-attr': 'SVG Attr ',
48
+ };
49
+
50
+ const TYPE_COLOR = {
51
+ comment: chalk.gray,
52
+ string: chalk.yellow,
53
+ template: chalk.yellow,
54
+ jsx: chalk.cyan,
55
+ 'jsx-svg': chalk.magenta,
56
+ identifier: chalk.red,
57
+ 'svg-comment': chalk.gray,
58
+ 'svg-metadata': chalk.gray,
59
+ 'svg-text': chalk.magenta,
60
+ 'svg-attr': chalk.magenta,
61
+ };
62
+
63
+ // Types that can be auto-fixed by nozhcn fix
64
+ // Note: 'svg-attr' is conditionally fixable when noZhCnSvgAttrStrategy is set
65
+ const AUTO_FIXABLE_TYPES = new Set(['comment', 'svg-comment', 'svg-metadata', 'svg-attr']);
66
+
67
+ // Types that `collect` script can handle
68
+ const COLLECT_TYPES = new Set(['string', 'template', 'jsx']);
69
+
70
+ // Types that need SVG-specific manual handling
71
+ const SVG_MANUAL_TYPES = new Set(['jsx-svg', 'svg-text']);
72
+
73
+ // ─── Output helpers ──────────────────────────────────────────────────────────
74
+
75
+ function printHeader(msg) {
76
+ console.log(chalk.bold.blue(`\n[nozhcn] ${msg}`));
77
+ }
78
+
79
+ function printDivider() {
80
+ console.log(chalk.dim('─'.repeat(72)));
81
+ }
82
+
83
+ /**
84
+ * Print all findings grouped by file path.
85
+ * @param {Finding[]} findings
86
+ */
87
+ function printFindings(findings) {
88
+ if (!findings.length) {
89
+ log.success('[nozhcn] No Chinese characters found.');
90
+ return;
91
+ }
92
+
93
+ // Group by filePath
94
+ const byFile = new Map();
95
+ findings.forEach((f) => {
96
+ if (!byFile.has(f.filePath)) byFile.set(f.filePath, []);
97
+ byFile.get(f.filePath).push(f);
98
+ });
99
+
100
+ printDivider();
101
+ byFile.forEach((items, fp) => {
102
+ // Show relative path from cwd for readability
103
+ const rel = path.relative(process.cwd(), fp);
104
+ console.log(chalk.underline(`\n ${rel}`));
105
+
106
+ items.forEach((item) => {
107
+ const lineStr = String(item.line || '?').padStart(6);
108
+ const typeLabel = (TYPE_COLOR[item.type] || chalk.white)(
109
+ `[${TYPE_LABEL[item.type] || item.type}]`
110
+ );
111
+ // Truncate long content to 80 chars for terminal readability
112
+ const content = (item.content || '').replace(/\n/g, '\\n').slice(0, 80);
113
+ console.log(` Line ${lineStr} ${typeLabel} ${content}`);
114
+ });
115
+ });
116
+ printDivider();
117
+ }
118
+
119
+ /**
120
+ * Print a summary of findings by type.
121
+ * @param {Finding[]} findings
122
+ * @param {'check'|'fix'|'report'} mode
123
+ * @param {number} autoFixedCount - number of auto-fixed comment findings
124
+ * @param {number} i18nDefaultFixed - number of auto-fixed .d() default values
125
+ */
126
+ function printSummary(findings, mode, autoFixedCount, i18nDefaultFixed) {
127
+ if (!findings.length && autoFixedCount === 0 && i18nDefaultFixed === 0) {
128
+ log.success('[nozhcn] ✓ All clean — no Chinese characters detected.');
129
+ return;
130
+ }
131
+
132
+ // Count by type
133
+ const counts = {};
134
+ findings.forEach((f) => {
135
+ counts[f.type] = (counts[f.type] || 0) + 1;
136
+ });
137
+
138
+ const total = findings.length;
139
+
140
+ if (mode === 'fix' && autoFixedCount > 0) {
141
+ log.success(`[nozhcn] Auto-fixed ${autoFixedCount} comment(s).`);
142
+ }
143
+ if (mode === 'fix' && i18nDefaultFixed > 0) {
144
+ log.success(`[nozhcn] Replaced ${i18nDefaultFixed} .d() default value(s) with translations.`);
145
+ }
146
+
147
+ if (total > 0) {
148
+ const countStr = Object.entries(counts)
149
+ .map(([t, n]) => `${n} ${t}`)
150
+ .join(', ');
151
+ log.warnToLog(`[nozhcn] ${total} Chinese occurrence(s) remaining: ${countStr}`);
152
+
153
+ const collectNeeded = findings.some((f) => COLLECT_TYPES.has(f.type));
154
+ if (collectNeeded) {
155
+ console.log(
156
+ chalk.dim(
157
+ " Tip: Run 'npm run intl-collect' (or: chaoswise-intl collect) to internationalize string/template/jsx occurrences."
158
+ )
159
+ );
160
+ }
161
+
162
+ const svgManualNeeded = findings.some((f) => SVG_MANUAL_TYPES.has(f.type));
163
+ if (svgManualNeeded) {
164
+ console.log(
165
+ chalk.dim(
166
+ ' Tip: SVG text/attribute occurrences require manual handling.\n' +
167
+ ' For standalone .svg files, run fix to clean comments/metadata;\n' +
168
+ ' for <text> content, consider replacing with a JS-driven label.'
169
+ )
170
+ );
171
+ }
172
+
173
+ const identifierFound = findings.some((f) => f.type === 'identifier');
174
+ if (identifierFound) {
175
+ console.log(
176
+ chalk.dim(' Tip: Identifier occurrences require manual renaming.')
177
+ );
178
+ }
179
+ } else if (mode === 'fix') {
180
+ log.success('[nozhcn] ✓ All auto-fixable Chinese occurrences have been fixed.');
181
+ }
182
+ }
183
+
184
+ // ─── Main entry ──────────────────────────────────────────────────────────────
185
+
186
+ /**
187
+ * @param {'check'|'fix'|'report'} mode
188
+ */
189
+ module.exports = function noZhCn(mode) {
190
+ mode = mode || 'check';
191
+
192
+ printHeader(`Scanning for Chinese characters... (mode: ${mode})`);
193
+
194
+ const conf = getConf();
195
+ const {
196
+ noZhCnCheckTypes = ['comment', 'string', 'template', 'jsx'],
197
+ noZhCnCommentStrategy = 'clean',
198
+ noZhCnReportFile = null,
199
+ noZhCnExclude = [],
200
+ noZhCnIgnoreI18nDefault = true,
201
+ noZhCnDefaultLang = null,
202
+ noZhCnIncludeSvg = false,
203
+ noZhCnSvgMetadataStrategy = 'remove',
204
+ noZhCnSvgAttrStrategy = 'clean',
205
+ localeOutput = 'src/public',
206
+ primaryRegx,
207
+ babelPresets,
208
+ babelPlugins,
209
+ i18nObject,
210
+ i18nMethod,
211
+ i18nDefaultFunctionKey,
212
+ ignoreComponents = ['svg', 'style'],
213
+ } = conf;
214
+
215
+ // Merge nozhcn-specific excludes into the main exclude list
216
+ const mergedConf = {
217
+ ...conf,
218
+ exclude: [...(conf.exclude || []), ...(noZhCnExclude || [])],
219
+ };
220
+
221
+ const files = targetEntryFiles(mergedConf);
222
+
223
+ if (!files.length) {
224
+ log.warnToLog('[nozhcn] No files found to scan. Please check your `entry` config.');
225
+ return;
226
+ }
227
+
228
+ log.info(`[nozhcn] ${files.length} file(s) to scan (types: ${noZhCnCheckTypes.join(', ')})`);
229
+
230
+ // Shared scan options
231
+ const scanOpts = {
232
+ checkTypes: noZhCnCheckTypes,
233
+ babelPresets,
234
+ babelPlugins,
235
+ primaryRegx,
236
+ i18nObject,
237
+ i18nMethod,
238
+ i18nDefaultFunctionKey,
239
+ ignoreI18nDefault: noZhCnIgnoreI18nDefault,
240
+ ignoreComponents,
241
+ };
242
+
243
+ // ── Phase 1: Initial scan ─────────────────────────────────────────────────
244
+
245
+ let allFindings = [];
246
+ const errorFiles = [];
247
+
248
+ files.forEach(({ filePath }) => {
249
+ const result = findZhCnInFile(filePath, scanOpts);
250
+ if (result.error) {
251
+ errorFiles.push({ filePath, error: result.error });
252
+ }
253
+ allFindings = allFindings.concat(result.findings);
254
+ });
255
+
256
+ if (errorFiles.length) {
257
+ log.warnToLog(`[nozhcn] ${errorFiles.length} file(s) failed to parse:`);
258
+ errorFiles.forEach(({ filePath: fp, error }) => {
259
+ const rel = path.relative(process.cwd(), fp);
260
+ console.log(chalk.dim(` ${rel}: ${error}`));
261
+ });
262
+ }
263
+
264
+ // ── Phase 1b: SVG file scan (opt-in) ────────────────────────────────────
265
+
266
+ let svgFiles = [];
267
+ if (noZhCnIncludeSvg) {
268
+ const entryDirs = [].concat(mergedConf.entry || ['src']);
269
+ const excludePatterns = mergedConf.exclude || [];
270
+ entryDirs.forEach((dir) => {
271
+ const matched = glob.sync(`${dir}/**/*.svg`, { ignore: excludePatterns });
272
+ svgFiles = svgFiles.concat(matched);
273
+ });
274
+
275
+ log.info(`[nozhcn] ${svgFiles.length} SVG file(s) to scan`);
276
+
277
+ svgFiles.forEach((fp) => {
278
+ const result = findZhCnInSvgFile(fp);
279
+ if (result.error) {
280
+ errorFiles.push({ filePath: fp, error: result.error });
281
+ }
282
+ allFindings = allFindings.concat(result.findings);
283
+ });
284
+ }
285
+
286
+ // ── Phase 2: Auto-fix (fix mode only) ────────────────────────────────────
287
+ //
288
+ // All fix sub-phases (2 / 2b / 2c) run first WITHOUT intermediate re-scans.
289
+ // A single final re-scan at the end produces accurate remaining findings.
290
+ // This avoids up to 3 redundant full-project AST parses on large codebases.
291
+
292
+ let autoFixedCount = 0;
293
+ let i18nDefaultFixed = 0;
294
+ const i18nDefaultMissing = []; // { key, chinese } entries with no translation found
295
+ let svgFixedCount = 0;
296
+
297
+ if (mode === 'fix') {
298
+ let anyFixApplied = false;
299
+ // ── Phase 2a: Fix comments in JS/TS files ─────────────────────────────
300
+ const commentByFile = new Map();
301
+ allFindings.forEach((f) => {
302
+ if (f.type !== 'comment') return;
303
+ if (!commentByFile.has(f.filePath)) commentByFile.set(f.filePath, []);
304
+ commentByFile.get(f.filePath).push(f);
305
+ });
306
+
307
+ commentByFile.forEach((findings, fp) => {
308
+ autoFixedCount += fixZhCnInFile(fp, findings, noZhCnCommentStrategy);
309
+ });
310
+ if (autoFixedCount > 0) anyFixApplied = true;
311
+
312
+ // ── Phase 2b: Replace .d('Chinese') with translations ─────────────────
313
+ if (noZhCnDefaultLang) {
314
+ const localePath = path.resolve(
315
+ process.cwd(),
316
+ localeOutput,
317
+ 'locales',
318
+ `${noZhCnDefaultLang}.json`
319
+ );
320
+
321
+ if (!fs.existsSync(localePath)) {
322
+ log.warnToLog(
323
+ `[nozhcn] noZhCnDefaultLang='${noZhCnDefaultLang}' is set but locale file does not exist: ${localePath}\n` +
324
+ ` Run 'npm run intl-update' (or: chaoswise-intl update) first to generate locale files.`
325
+ );
326
+ } else {
327
+ let localeMap;
328
+ try {
329
+ localeMap = JSON.parse(fs.readFileSync(localePath, 'utf8'));
330
+ } catch (err) {
331
+ log.error(`[nozhcn] Failed to parse locale file: ${localePath} — ${err.message}`);
332
+ }
333
+
334
+ if (localeMap) {
335
+ const fixOpts = { i18nObject, i18nMethod, i18nDefaultFunctionKey, babelPresets, babelPlugins };
336
+ // Collect JS/TS file paths from initial findings (exclude svg-* types)
337
+ const filesToFix = new Set(
338
+ allFindings.filter((f) => !f.type.startsWith('svg-')).map((f) => f.filePath)
339
+ );
340
+ filesToFix.forEach((fp) => {
341
+ const result = fixI18nDefaultInFile(fp, localeMap, fixOpts);
342
+ i18nDefaultFixed += result.fixed;
343
+ i18nDefaultMissing.push(...result.missing.map((m) => ({ ...m, filePath: fp })));
344
+ });
345
+ if (i18nDefaultFixed > 0) anyFixApplied = true;
346
+
347
+ if (i18nDefaultMissing.length) {
348
+ log.warnToLog(
349
+ `[nozhcn] ${i18nDefaultMissing.length} .d() default value(s) have no '${noZhCnDefaultLang}' translation yet:`
350
+ );
351
+ i18nDefaultMissing.slice(0, 10).forEach(({ filePath: fp, key, chinese }) => {
352
+ const rel = path.relative(process.cwd(), fp);
353
+ console.log(chalk.dim(` ${rel} key=${key} zh='${chinese}'`));
354
+ });
355
+ if (i18nDefaultMissing.length > 10) {
356
+ console.log(chalk.dim(` ... and ${i18nDefaultMissing.length - 10} more`));
357
+ }
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ // ── Phase 2c: Fix SVG files ───────────────────────────────────────────
364
+ if (noZhCnIncludeSvg && svgFiles.length) {
365
+ const svgByFile = new Map();
366
+ allFindings.forEach((f) => {
367
+ if (!f.type.startsWith('svg-')) return;
368
+ if (!svgByFile.has(f.filePath)) svgByFile.set(f.filePath, []);
369
+ svgByFile.get(f.filePath).push(f);
370
+ });
371
+
372
+ svgByFile.forEach((findings, fp) => {
373
+ const result = fixZhCnInSvgFile(fp, findings, {
374
+ commentStrategy: noZhCnCommentStrategy,
375
+ metadataStrategy: noZhCnSvgMetadataStrategy,
376
+ attrStrategy: noZhCnSvgAttrStrategy,
377
+ });
378
+ svgFixedCount += result.fixed;
379
+ });
380
+ if (svgFixedCount > 0) anyFixApplied = true;
381
+ }
382
+
383
+ // ── Single final re-scan after all fixes ──────────────────────────────
384
+ if (anyFixApplied) {
385
+ allFindings = [];
386
+ files.forEach(({ filePath }) => {
387
+ const result = findZhCnInFile(filePath, scanOpts);
388
+ allFindings = allFindings.concat(result.findings);
389
+ });
390
+ if (noZhCnIncludeSvg && svgFiles.length) {
391
+ svgFiles.forEach((fp) => {
392
+ const result = findZhCnInSvgFile(fp);
393
+ allFindings = allFindings.concat(result.findings);
394
+ });
395
+ }
396
+ }
397
+ }
398
+
399
+ // ── Phase 3: Output ───────────────────────────────────────────────────────
400
+
401
+ printFindings(allFindings);
402
+ printSummary(allFindings, mode, autoFixedCount + svgFixedCount, i18nDefaultFixed);
403
+
404
+ // Write JSON report if configured
405
+ const reportPath = noZhCnReportFile || (mode === 'report' ? 'nozhcn-report.json' : null);
406
+ if (reportPath) {
407
+ // Build summary counts
408
+ const summary = {};
409
+ allFindings.forEach((f) => {
410
+ summary[f.type] = (summary[f.type] || 0) + 1;
411
+ });
412
+
413
+ const report = {
414
+ timestamp: new Date().toISOString(),
415
+ mode,
416
+ total: allFindings.length,
417
+ autoFixed: autoFixedCount,
418
+ i18nDefaultFixed,
419
+ i18nDefaultMissingCount: i18nDefaultMissing.length,
420
+ summary,
421
+ findings: allFindings.map((f) => ({
422
+ filePath: path.relative(process.cwd(), f.filePath),
423
+ type: f.type,
424
+ line: f.line,
425
+ col: f.col,
426
+ content: f.content,
427
+ })),
428
+ };
429
+
430
+ file.write(reportPath, JSON.stringify(report, null, 2));
431
+ log.info(`[nozhcn] Report written to: ${reportPath}`);
432
+ }
433
+
434
+ // ── Phase 4: Exit code ────────────────────────────────────────────────────
435
+
436
+ // 'report' mode is non-blocking (used for dashboards / tracking)
437
+ if (mode !== 'report' && allFindings.length > 0) {
438
+ process.exit(1);
439
+ }
440
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * babelOptions.js
3
+ *
4
+ * Shared Babel transform options used by all AST-based scan/fix utilities.
5
+ * Matches the superset-of-JS/TS/JSX/TSX syntax configuration from transformAst.js.
6
+ */
7
+
8
+ const pluginSyntaxJSX = require('@babel/plugin-syntax-jsx');
9
+ const pluginSyntaxProposalOptionalChaining = require('@babel/plugin-proposal-optional-chaining');
10
+ const pluginSyntaxClassProperties = require('@babel/plugin-syntax-class-properties');
11
+ const pluginSyntaxDecorators = require('@babel/plugin-syntax-decorators');
12
+ const pluginSyntaxObjectRestSpread = require('@babel/plugin-syntax-object-rest-spread');
13
+ const pluginSyntaxAsyncGenerators = require('@babel/plugin-syntax-async-generators');
14
+ const pluginSyntaxDoExpressions = require('@babel/plugin-syntax-do-expressions');
15
+ const pluginSyntaxDynamicImport = require('@babel/plugin-syntax-dynamic-import');
16
+ const pluginSyntaxFunctionBind = require('@babel/plugin-syntax-function-bind');
17
+ const pluginExportDefaultFrom = require('@babel/plugin-proposal-export-default-from');
18
+ const presetTypescript = require('@babel/preset-typescript').default;
19
+
20
+ /**
21
+ * @param {Array} [babelPresets]
22
+ * @param {Array} [babelPlugins]
23
+ * @returns {import('@babel/core').TransformOptions}
24
+ */
25
+ module.exports = function buildTransformOptions(babelPresets, babelPlugins) {
26
+ return {
27
+ sourceType: 'module',
28
+ ast: true,
29
+ configFile: false,
30
+ parserOpts: { tokens: true },
31
+ presets: [
32
+ ...(babelPresets || []),
33
+ [presetTypescript, { isTSX: true, allExtensions: true }],
34
+ ],
35
+ plugins: [
36
+ pluginSyntaxJSX,
37
+ pluginSyntaxProposalOptionalChaining,
38
+ pluginSyntaxClassProperties,
39
+ [pluginSyntaxDecorators, { decoratorsBeforeExport: true }],
40
+ pluginSyntaxObjectRestSpread,
41
+ pluginSyntaxAsyncGenerators,
42
+ pluginSyntaxDoExpressions,
43
+ pluginSyntaxDynamicImport,
44
+ pluginSyntaxFunctionBind,
45
+ pluginExportDefaultFrom,
46
+ ...(babelPlugins || []),
47
+ ],
48
+ };
49
+ };