juice-email-cli 2.3.15 → 2.4.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/juice.js CHANGED
@@ -1,34 +1,36 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, resolve } from 'path';
6
+ import chalk from 'chalk';
7
+ import { run } from '../src/index.js';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
3
11
 
4
- const { program } = require('commander');
5
- const path = require('path');
6
- const pkg = require('../package.json');
7
- const { run } = require('../src/index');
8
12
  let ExitPromptError;
9
13
  try {
10
- ({ ExitPromptError } = require('@inquirer/core'));
14
+ ({ ExitPromptError } = await import('@inquirer/core'));
11
15
  } catch (_) {}
12
16
 
13
- /**
14
- * Wrap an async action handler to catch Ctrl+C and other errors gracefully.
15
- */
16
17
  function safeAction(fn) {
17
18
  return async (...args) => {
18
19
  try {
19
20
  await fn(...args);
20
21
  } catch (err) {
21
22
  if (ExitPromptError && err instanceof ExitPromptError) {
22
- // Ctrl+C — exit silently
23
23
  process.exit(0);
24
24
  }
25
- console.error('\n' + require('chalk').red(` ✘ ${err.message}`));
25
+ console.error('\n' + chalk.red(` ✘ ${err.message}`));
26
26
  if (process.env.DEBUG) console.error(err);
27
27
  process.exit(1);
28
28
  }
29
29
  };
30
30
  }
31
31
 
32
+ const program = new Command();
33
+
32
34
  program
33
35
  .name('juice')
34
36
  .description('生成符合各大邮件平台的内联 CSS HTML 邮件(同时输出标准版 + 压缩版)')
@@ -68,6 +70,24 @@ program
68
70
  交互模式(逐步选择品牌、模板、片段、配置):
69
71
  juice
70
72
 
73
+ ════════════════════════════════════════════════════════════════
74
+ 资源浏览
75
+ ════════════════════════════════════════════════════════════════
76
+
77
+ 查看资源树:
78
+ juice view 查看所有品牌、模板、系列、片段
79
+ juice view elabscience 查看指定品牌的模板和系列
80
+ juice view --templates 列出所有模板
81
+ juice view --series 列出所有系列
82
+ juice view --snippets 列出所有片段
83
+
84
+ 拷贝到当前目录:
85
+ juice init 交互式选择并拷贝模板/片段/配置
86
+ juice init --template <file> 仅拷贝指定模板 HTML
87
+ juice init --snippet <file> 仅拷贝指定片段 HTML
88
+ juice init --config <file> 仅拷贝指定配置 YAML
89
+ juice init --all [target] 拷贝整个 EDM 资源库
90
+
71
91
  ════════════════════════════════════════════════════════════════
72
92
  输出文件说明
73
93
  ════════════════════════════════════════════════════════════════
@@ -111,7 +131,7 @@ program
111
131
  .option('--series', '列出所有系列')
112
132
  .option('--snippets', '列出所有片段')
113
133
  .action(safeAction(async (viewPath, options) => {
114
- const { runViewMode } = require('../src/view');
134
+ const { runViewMode } = await import('../src/view.js');
115
135
  await runViewMode({
116
136
  viewPath: viewPath || null,
117
137
  interactive: !!options.interactive,
@@ -130,7 +150,7 @@ program
130
150
  .option('-c, --config <file-path>', '仅拷贝配置 YAML 文件')
131
151
  .option('--all [target]', '拷贝整个 EDM 资源目录到当前或指定目录')
132
152
  .action(safeAction(async (initPath, options) => {
133
- const { runInitMode } = require('../src/init');
153
+ const { runInitMode } = await import('../src/init.js');
134
154
  await runInitMode({
135
155
  initPath: initPath || null,
136
156
  template: options.template || null,
@@ -143,21 +163,19 @@ program
143
163
  // ─── Default action (backward compatible) ───────────────────────────────
144
164
  program
145
165
  .action(safeAction(async (options) => {
146
- // 注册/卸载右键菜单
147
166
  if (options.install) {
148
- const { registerContextMenu } = require('../src/context-menu');
167
+ const { registerContextMenu } = await import('../src/context-menu.js');
149
168
  await registerContextMenu();
150
169
  return;
151
170
  }
152
171
  if (options.uninstall) {
153
- const { unregisterContextMenu } = require('../src/context-menu');
172
+ const { unregisterContextMenu } = await import('../src/context-menu.js');
154
173
  await unregisterContextMenu();
155
174
  return;
156
175
  }
157
176
 
158
- // --snippet / -s 模式:片段组装
159
177
  if (options.snippet) {
160
- const { runSnippetMode } = require('../src/snippet');
178
+ const { runSnippetMode } = await import('../src/snippet.js');
161
179
  await runSnippetMode({
162
180
  snippet: options.snippet,
163
181
  template: options.file,
@@ -167,7 +185,6 @@ program
167
185
  return;
168
186
  }
169
187
 
170
- // 普通模式:-f 指定文件
171
188
  let inputFile = options.file;
172
189
  if (!inputFile && program.args.length > 0) {
173
190
  inputFile = program.args[0];
@@ -181,10 +198,8 @@ program
181
198
  return;
182
199
  }
183
200
 
184
- // 全交互模式:无 --snippet,无 -f
185
- const { runInteractiveMode } = require('../src/snippet');
201
+ const { runInteractiveMode } = await import('../src/snippet.js');
186
202
  await runInteractiveMode({ config: options.config });
187
203
  }));
188
204
 
189
- program.allowUnknownOption(false);
190
205
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "juice-email-cli",
3
- "version": "2.3.15",
3
+ "version": "2.4.1",
4
+ "type": "module",
4
5
  "description": "CLI tool to generate email-client-compatible HTML with juice (CSS inlining) + Mustache templating + minification",
5
6
  "main": "src/index.js",
6
7
  "bin": {
@@ -36,17 +37,17 @@
36
37
  "url": "https://github.com/GuoSirius/juice-cli/issues"
37
38
  },
38
39
  "dependencies": {
39
- "@inquirer/prompts": "^5.0.2",
40
- "chalk": "^4.1.2",
41
- "commander": "^11.1.0",
40
+ "@inquirer/prompts": "^8.4.3",
41
+ "chalk": "^5.6.2",
42
+ "commander": "^14.0.3",
42
43
  "html-minifier-terser": "^7.2.0",
43
- "js-yaml": "^4.1.0",
44
- "juice": "^10.0.0",
44
+ "js-yaml": "^4.1.1",
45
+ "juice": "^11.1.1",
45
46
  "mustache": "^4.2.0",
46
- "ora": "^5.4.1"
47
+ "ora": "^9.4.0"
47
48
  },
48
49
  "engines": {
49
- "node": ">=16.0.0"
50
+ "node": ">=24.0.0"
50
51
  },
51
52
  "files": [
52
53
  "bin/",
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Windows 右键菜单注册 / 取消注册
5
3
  *
@@ -32,10 +30,13 @@
32
30
  * └── 📂 在此打开终端 (可选)
33
31
  */
34
32
 
35
- const { spawnSync } = require('child_process');
36
- const fs = require('fs');
37
- const path = require('path');
38
- const chalk = require('chalk');
33
+ import { spawnSync } from 'child_process';
34
+ import fs from 'fs';
35
+ import path from 'path';
36
+ import { fileURLToPath } from 'url';
37
+ import chalk from 'chalk';
38
+
39
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
40
 
40
41
  // ─── 路径工具 ─────────────────────────────────────────────────────────────────
41
42
 
@@ -459,4 +460,4 @@ async function unregisterContextMenu() {
459
460
  }
460
461
  }
461
462
 
462
- module.exports = { registerContextMenu, unregisterContextMenu };
463
+ export { registerContextMenu, unregisterContextMenu };
package/src/index.js CHANGED
@@ -1,25 +1,25 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const yaml = require('js-yaml');
7
- const juice = require('juice');
8
- const Mustache = require('mustache');
9
- const chalk = require('chalk');
10
- const ora = require('ora');
11
- const { minify: htmlMinify } = require('html-minifier-terser');
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { fileURLToPath } from 'url';
5
+ import yaml from 'js-yaml';
6
+ import juice from 'juice';
7
+ import Mustache from 'mustache';
8
+ import chalk from 'chalk';
9
+ import ora from 'ora';
10
+ import { minify as htmlMinify } from 'html-minifier-terser';
11
+
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
13
 
13
14
  // ─── 从 defaults/juice.yaml 加载默认配置 ─────────────────────────────────────
14
15
 
15
- const DEFAULT_CONFIG_PATH = path.resolve(__dirname, '..', 'defaults', 'juice.yaml');
16
+ export const DEFAULT_CONFIG_PATH = path.resolve(__dirname, '..', 'defaults', 'juice.yaml');
16
17
 
17
18
  function loadDefaultConfig() {
18
19
  try {
19
20
  const content = fs.readFileSync(DEFAULT_CONFIG_PATH, 'utf8');
20
21
  return yaml.load(content) || {};
21
22
  } catch (e) {
22
- // 如果 defaults/juice.yaml 不存在,使用内置最小配置
23
23
  console.warn(chalk.yellow(` ⚠ 无法加载默认配置 ${DEFAULT_CONFIG_PATH},使用内置默认值\n`));
24
24
  return {};
25
25
  }
@@ -27,34 +27,20 @@ function loadDefaultConfig() {
27
27
 
28
28
  const CODE_DEFAULTS = loadDefaultConfig();
29
29
 
30
- // 用户主目录候选(支持 yaml/yml)
31
30
  const HOME_CANDIDATES = [
32
31
  path.join(os.homedir(), 'juice.yaml'),
33
32
  path.join(os.homedir(), 'juice.yml'),
34
33
  ];
35
34
 
36
- function resolveHomeConfig() {
35
+ export function resolveHomeConfig() {
37
36
  return HOME_CANDIDATES.find((c) => fs.existsSync(c)) || null;
38
37
  }
39
38
 
40
39
  // ─── 配置文件查找 ─────────────────────────────────────────────────────────────
41
40
 
42
- /**
43
- * 查找用户配置文件
44
- *
45
- * 配置加载顺序(优先级从低到高):
46
- * 1. CLI 内置默认值(defaults/juice.yaml)
47
- * 2. 用户主目录 ~/juice.yaml(如果存在)
48
- * 3. 优先配置文件(-c 指定 或 输入文件同级目录,二者互斥)
49
- *
50
- * 返回:
51
- * - highPriorityPath: 高优先级配置路径(-c 指定 或 输入文件同级目录,二者互斥)
52
- * - homePath: 用户主目录配置路径(可能不存在)
53
- */
54
- function findConfigs(configPath, inputFile) {
41
+ export function findConfigs(configPath, inputFile) {
55
42
  let highPriorityPath = null;
56
43
 
57
- // 1. 优先使用 -c 指定的配置文件
58
44
  if (configPath) {
59
45
  const resolved = path.resolve(configPath);
60
46
  if (!fs.existsSync(resolved)) {
@@ -62,7 +48,6 @@ function findConfigs(configPath, inputFile) {
62
48
  }
63
49
  highPriorityPath = resolved;
64
50
  }
65
- // 2. 否则使用输入文件同级目录下的配置(互斥)
66
51
  else if (inputFile) {
67
52
  const inputDir = path.dirname(path.resolve(inputFile));
68
53
  const fileCandidates = [
@@ -82,7 +67,7 @@ function findConfigs(configPath, inputFile) {
82
67
 
83
68
  // ─── 配置文件加载 ─────────────────────────────────────────────────────────────
84
69
 
85
- function loadYaml(filePath) {
70
+ export function loadYaml(filePath) {
86
71
  if (!filePath || !fs.existsSync(filePath)) return {};
87
72
  try {
88
73
  return yaml.load(fs.readFileSync(filePath, 'utf8')) || {};
@@ -91,10 +76,9 @@ function loadYaml(filePath) {
91
76
  }
92
77
  }
93
78
 
94
- /**
95
- * 深度合并(后面的覆盖前面的),仅对普通对象递归,数组/基本类型直接覆盖
96
- */
97
- function deepMerge(base, ...overrides) {
79
+ // ─── 深度合并 ─────────────────────────────────────────────────────────────────
80
+
81
+ export function deepMerge(base, ...overrides) {
98
82
  let result = Object.assign({}, base);
99
83
  for (const src of overrides) {
100
84
  if (!src || typeof src !== 'object' || Array.isArray(src)) continue;
@@ -118,24 +102,17 @@ function deepMerge(base, ...overrides) {
118
102
  return result;
119
103
  }
120
104
 
121
- /**
122
- * 配置合并,返回最终生效配置
123
- *
124
- * 合并顺序(优先级从低到高):
125
- * CLI 内置默认值 < 用户目录 ~/juice.yaml < 优先配置(-c/输入目录)
126
- */
127
- function buildConfig(highPriorityPath, homePath) {
105
+ // ─── 配置合并 ─────────────────────────────────────────────────────────────────
106
+
107
+ export function buildConfig(highPriorityPath, homePath) {
128
108
  const layers = [];
129
109
 
130
- // 1. CLI 内置默认值(最低优先级)
131
110
  layers.push({ label: 'CLI 内置默认值', data: CODE_DEFAULTS });
132
111
 
133
- // 2. 用户目录配置(如果有)
134
112
  if (homePath) {
135
113
  layers.push({ label: `用户目录配置 (${homePath})`, data: loadYaml(homePath) });
136
114
  }
137
115
 
138
- // 3. 优先配置(最高优先级,-c 指定 或 输入文件同级目录,互斥)
139
116
  if (highPriorityPath) {
140
117
  layers.push({ label: `优先配置 (${highPriorityPath})`, data: loadYaml(highPriorityPath) });
141
118
  }
@@ -146,10 +123,9 @@ function buildConfig(highPriorityPath, homePath) {
146
123
 
147
124
  // ─── HTML 模板处理 ────────────────────────────────────────────────────────────
148
125
 
149
- function processTemplate(inputFile, config) {
126
+ export function processTemplate(inputFile, config) {
150
127
  const htmlRaw = fs.readFileSync(inputFile, 'utf8');
151
128
 
152
- // 1. Mustache 变量替换(根据 rawHtml 配置决定是否转义 HTML)
153
129
  const originalEscape = Mustache.escape;
154
130
  let htmlWithVars;
155
131
  try {
@@ -161,18 +137,16 @@ function processTemplate(inputFile, config) {
161
137
  Mustache.escape = originalEscape;
162
138
  }
163
139
 
164
- // 2. 收集 extraCssFiles
165
140
  const basePath = path.dirname(path.resolve(inputFile));
166
141
  const extraCss = collectExtraCss(basePath, config);
167
142
 
168
- // 3. juice CSS 内联
169
143
  const juiceOpts = Object.assign({}, config.juice || {});
170
144
  delete juiceOpts.extraCssFiles;
171
145
 
172
146
  return juice(htmlWithVars, { ...juiceOpts, extraCss });
173
147
  }
174
148
 
175
- function collectExtraCss(basePath, config) {
149
+ export function collectExtraCss(basePath, config) {
176
150
  const extraFiles = (config.juice && config.juice.extraCssFiles) || [];
177
151
  if (!extraFiles.length) return '';
178
152
  return extraFiles
@@ -189,7 +163,7 @@ function collectExtraCss(basePath, config) {
189
163
 
190
164
  // ─── 压缩 ─────────────────────────────────────────────────────────────────────
191
165
 
192
- async function minifyHtml(html, minifyConfig) {
166
+ export async function minifyHtml(html, minifyConfig) {
193
167
  return htmlMinify(html, {
194
168
  removeConditionalComments: false,
195
169
  ...(minifyConfig || {}),
@@ -208,39 +182,46 @@ function resolveOutputPaths(inputFile, config) {
208
182
  };
209
183
  }
210
184
 
185
+ // ─── 格式化 ───────────────────────────────────────────────────────────────────
186
+
187
+ export function fmtSize(str) {
188
+ const b = Buffer.byteLength(str, 'utf8');
189
+ return b < 1024 ? `${b} B` : `${(b / 1024).toFixed(1)} KB`;
190
+ }
191
+
192
+ export function savings(original, minified) {
193
+ const orig = Buffer.byteLength(original, 'utf8');
194
+ const mini = Buffer.byteLength(minified, 'utf8');
195
+ return (((orig - mini) / orig) * 100).toFixed(1) + '%';
196
+ }
197
+
211
198
  // ─── 主入口 ───────────────────────────────────────────────────────────────────
212
199
 
213
- async function run({ file, config: configPath }) {
200
+ export async function run({ file, config: configPath }) {
214
201
  const spinner = ora({ text: '正在处理...', color: 'cyan' }).start();
215
202
 
216
203
  try {
217
- // 1. 输入文件
218
204
  const inputFile = path.resolve(file);
219
205
  if (!fs.existsSync(inputFile)) {
220
206
  spinner.fail(chalk.red(`输入文件不存在:${inputFile}`));
221
207
  process.exit(1);
222
208
  }
223
209
 
224
- // 2. 查找配置(-c 和输入文件同级目录互斥)+ 合并
225
210
  const { highPriorityPath, homePath } = findConfigs(configPath, inputFile);
226
211
  const { config, layers } = buildConfig(highPriorityPath, homePath);
227
212
  const encoding = (config.output && config.output.encoding) || 'utf8';
228
213
 
229
- // 3. CSS 内联
230
214
  spinner.text = `CSS 内联处理:${path.basename(inputFile)}`;
231
215
  const resultHtml = processTemplate(inputFile, config);
232
216
 
233
- // 4. 输出路径
234
217
  const outPaths = resolveOutputPaths(inputFile, config);
235
218
 
236
- // 5. 写出标准版
237
219
  spinner.text = '写出 .output.html ...';
238
220
  if (fs.existsSync(outPaths.normal)) {
239
221
  spinner.warn(chalk.yellow(`目标文件已存在,将覆盖:${outPaths.normal}`));
240
222
  }
241
223
  fs.writeFileSync(outPaths.normal, resultHtml, encoding);
242
224
 
243
- // 6. 压缩并写出
244
225
  spinner.text = '写出 .minified.html ...';
245
226
  if (fs.existsSync(outPaths.minified)) {
246
227
  spinner.warn(chalk.yellow(`目标文件已存在,将覆盖:${outPaths.minified}`));
@@ -248,7 +229,6 @@ async function run({ file, config: configPath }) {
248
229
  const minified = await minifyHtml(resultHtml, config.minify);
249
230
  fs.writeFileSync(outPaths.minified, minified, encoding);
250
231
 
251
- // 7. 汇报
252
232
  const layerLines = layers
253
233
  .map((l) => ` ${chalk.gray('·')} ${l.label}`)
254
234
  .join('\n');
@@ -267,16 +247,3 @@ async function run({ file, config: configPath }) {
267
247
  process.exit(1);
268
248
  }
269
249
  }
270
-
271
- function fmtSize(str) {
272
- const b = Buffer.byteLength(str, 'utf8');
273
- return b < 1024 ? `${b} B` : `${(b / 1024).toFixed(1)} KB`;
274
- }
275
-
276
- function savings(original, minified) {
277
- const orig = Buffer.byteLength(original, 'utf8');
278
- const mini = Buffer.byteLength(minified, 'utf8');
279
- return (((orig - mini) / orig) * 100).toFixed(1) + '%';
280
- }
281
-
282
- module.exports = { run, findConfigs, buildConfig, processTemplate, minifyHtml, deepMerge, collectExtraCss, loadYaml, fmtSize, savings, DEFAULT_CONFIG_PATH, resolveHomeConfig };
package/src/init.js CHANGED
@@ -1,13 +1,14 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const chalk = require('chalk');
6
- const {
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import chalk from 'chalk';
5
+ import {
7
6
  resolveEdmDir, loadMeta, findBrands, findTemplateVersions,
8
7
  findSeriesDirs, filterSeries, findSnippetVariants, findConfigs,
9
8
  promptOutputName,
10
- } = require('./snippet');
9
+ } from './snippet.js';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
12
 
12
13
  function fmtBytes(b) {
13
14
  return b < 1024 ? `${b} B` : `${(b / 1024).toFixed(1)} KB`;
@@ -433,7 +434,7 @@ async function runInitMode({ initPath, template, snippet, config, all }) {
433
434
  }
434
435
 
435
436
  if (initPath) {
436
- const { parseViewPath } = require('./view');
437
+ const { parseViewPath } = await import('./view.js');
437
438
  let edmDir;
438
439
  try {
439
440
  edmDir = resolveEdmDir();
@@ -526,4 +527,4 @@ async function runInitMode({ initPath, template, snippet, config, all }) {
526
527
  await interactiveInit(edmDir, cwd);
527
528
  }
528
529
 
529
- module.exports = { runInitMode };
530
+ export { runInitMode };
package/src/snippet.js CHANGED
@@ -1,12 +1,11 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const juice = require('juice');
7
- const Mustache = require('mustache');
8
- const chalk = require('chalk');
9
- const {
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { fileURLToPath } from 'url';
5
+ import juice from 'juice';
6
+ import Mustache from 'mustache';
7
+ import chalk from 'chalk';
8
+ import {
10
9
  loadYaml,
11
10
  deepMerge,
12
11
  collectExtraCss,
@@ -14,7 +13,9 @@ const {
14
13
  fmtSize,
15
14
  savings,
16
15
  DEFAULT_CONFIG_PATH,
17
- } = require('./index');
16
+ } from './index.js';
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
19
 
19
20
  // ─── EDM 目录解析 ──────────────────────────────────────────────────────────────
20
21
 
@@ -948,7 +949,7 @@ async function runInteractiveMode({ config: cliConfigPath }) {
948
949
  });
949
950
  }
950
951
 
951
- module.exports = {
952
+ export {
952
953
  runSnippetMode,
953
954
  runInteractiveMode,
954
955
  // EDM resolution & scanning
package/src/view.js CHANGED
@@ -1,12 +1,10 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const chalk = require('chalk');
6
- const {
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import {
7
5
  resolveEdmDir, loadMeta, findBrands, findTemplateVersions,
8
6
  findSeriesDirs, findSnippetVariants, findConfigs,
9
- } = require('./snippet');
7
+ } from './snippet.js';
10
8
 
11
9
  function fmtBytes(b) {
12
10
  return b < 1024 ? `${b} B` : `${(b / 1024).toFixed(1)} KB`;
@@ -349,7 +347,7 @@ async function showCheckbox(title, choices) {
349
347
  */
350
348
  async function copyResource(type, resourcePath, cwd) {
351
349
  try {
352
- const { runInitMode } = require('./init');
350
+ const { runInitMode } = await import('./init.js');
353
351
  if (type === 'template') {
354
352
  await runInitMode({ template: resourcePath });
355
353
  } else if (type === 'snippet') {
@@ -616,7 +614,7 @@ async function interactiveBrowse(edmDir, startParsed) {
616
614
  // Include template version in default name for clarity
617
615
  const versionName = versions.length > 0 ? versions[0].name : 'template';
618
616
  const defaultBaseName = `${current.brand}-${versionName}-${current.series}-${current.variant}`;
619
- const { promptOutputName: pn } = require('./snippet');
617
+ const { promptOutputName: pn } = await import('./snippet.js');
620
618
  const outputBaseName = await pn(defaultBaseName, process.cwd());
621
619
  const cwd = process.cwd();
622
620
  const cwdRel = (p) => './' + path.relative(cwd, p);
@@ -714,7 +712,7 @@ async function runViewMode({ viewPath, interactive, scope }) {
714
712
  });
715
713
  if (action === 'copy') {
716
714
  console.log();
717
- const { runInitMode } = require('./init');
715
+ const { runInitMode } = await import('./init.js');
718
716
  await runInitMode({ template: picked.version.templatePath });
719
717
  console.log();
720
718
  }
@@ -792,7 +790,7 @@ async function runViewMode({ viewPath, interactive, scope }) {
792
790
  actions
793
791
  );
794
792
  if (action === 'exit') return;
795
- const { runInitMode } = require('./init');
793
+ const { runInitMode } = await import('./init.js');
796
794
  console.log();
797
795
  if (action === 'snippet') {
798
796
  await runInitMode({ snippet: snipPath });
@@ -872,7 +870,7 @@ async function runViewMode({ viewPath, interactive, scope }) {
872
870
  variantData: picked.variant,
873
871
  };
874
872
  } else {
875
- const { runInitMode } = require('./init');
873
+ const { runInitMode } = await import('./init.js');
876
874
  console.log();
877
875
  if (action === 'copy-snippet') {
878
876
  await runInitMode({ snippet: picked.snippetPath });
@@ -938,4 +936,4 @@ async function runViewMode({ viewPath, interactive, scope }) {
938
936
  printFullTree(edmDir);
939
937
  }
940
938
 
941
- module.exports = { runViewMode, parseViewPath };
939
+ export { runViewMode, parseViewPath };