@snack-kit/scripts 0.4.0 → 0.6.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.
package/README.md CHANGED
@@ -10,27 +10,32 @@ npm install @snack-kit/scripts --save-dev
10
10
 
11
11
  ## 命令
12
12
 
13
- | 命令 | 说明 |
14
- |------|------|
15
- | `snack-scripts init [dir]` | 初始化新 snack 工程 |
16
- | `snack-scripts create` | 在当前工程创建新模块模版 |
17
- | `snack-scripts start` | 启动开发调试服务 |
18
- | `snack-scripts build` | 生产模式打包 |
19
- | `snack-scripts entry` | 打包独立入口页面 |
13
+ | 命令 | 说明 |
14
+ |---------------------------|-------------------------------|
15
+ | `snack-cli init [dir]` | 初始化新 snack 工程 |
16
+ | `snack-cli create` | 在当前工程创建新模块模版 |
17
+ | `snack-cli start` | 启动开发调试服务 |
18
+ | `snack-cli build` | 生产模式打包 |
19
+ | `snack-cli entry` | 打包独立入口页面 |
20
+
21
+ > `snack-scripts` 作为历史兼容别名保留,与 `snack-cli` 完全等价。
20
22
 
21
23
  ### init — 初始化工程
22
24
 
23
25
  ```bash
24
26
  # 在当前目录初始化
25
- snack-scripts init
27
+ snack-cli init
26
28
 
27
29
  # 在指定目录初始化
28
- snack-scripts init my-project
30
+ snack-cli init my-project
29
31
  ```
30
32
 
31
- 交互式提问:项目名、描述、作者、调试服务地址、入口页面 id/type
33
+ 交互式提问:项目名、描述、作者、调试服务地址、入口页面 id/type、React 版本(17 / 18 / 19)。
34
+
35
+ 提示语言跟随系统语言自动切换(中文 / English)。
32
36
 
33
37
  生成文件:
38
+
34
39
  ```
35
40
  my-project/
36
41
  ├── package.json
@@ -46,12 +51,13 @@ my-project/
46
51
  在 snack 工程根目录执行:
47
52
 
48
53
  ```bash
49
- snack-scripts create
54
+ snack-cli create
50
55
  ```
51
56
 
52
- 交互式提问:模块中文名、目录名(PascalCase)、描述、作者、是否生成 setting 模块。
57
+ 交互式提问:模块名称、目录名(PascalCase)、描述、作者、是否生成 setting 模块。
53
58
 
54
59
  生成文件(以 `UserManager` 为例):
60
+
55
61
  ```
56
62
  src/package/UserManager/
57
63
  ├── snack.json
@@ -64,63 +70,85 @@ src/package/UserManager/
64
70
  └── setting.scss
65
71
  ```
66
72
 
73
+ 初始化生成的 `package.json` scripts:
74
+
67
75
  ```json
68
76
  {
69
77
  "scripts": {
70
- "start": "snack-scripts start",
71
- "build": "snack-scripts build",
72
- "entry": "snack-scripts entry"
78
+ "start": "snack-cli start",
79
+ "build": "snack-cli build",
80
+ "entry": "snack-cli entry"
73
81
  }
74
82
  }
75
83
  ```
76
84
 
77
85
  ## package.json 配置项
78
86
 
79
- ```js
87
+ ```json
80
88
  {
81
- "input": "./src/package", // snack 模块目录,默认 "src/package"
82
- "output": "./dist", // 生产输出目录,默认 "dist"
83
- "snack": {
84
- "externals": {}, // 同 webpack externals,过滤不打包的依赖
85
- "buildIgnore": [], // 生产打包忽略的模块(模块目录名)
86
- "devPackage": [], // 开发模式仅启动指定模块(模块目录名),空数组表示全部启动
87
- "entry": { // 独立入口页面配置(snack-scripts entry 命令使用)
88
- "name": "entry", // 输出目录名,默认 "entry"
89
- "id": "my_page", // 所加载的页面 id
90
- "type": "portal", // 所加载的页面分类(可选)
91
- "title": "My App", // HTML 页面 title(可选)
92
- "favicon": "./favicon.ico", // 浏览器角标路径,相对项目根路径(可选)
93
- "service": "", // snackbar 服务端地址,默认使用当前地址栏(可选)
94
- "devServer": "", // 调试服务网关(可选)
95
- "mobile": { // 移动端页面配置,竖屏或宽度 < 1024px 时加载(可选)
96
- "id": "my_page_mobile",
97
- "type": "portal"
98
- },
99
- "files": [], // 需复制到发布目录的文件或目录路径(可选)
100
- "js": "./plugin.ts", // 外挂打包的 JS/TS 文件路径(可选)
101
- "css": "./plugin.scss", // 外挂打包的 CSS/SCSS 文件路径(可选)
102
- "template": { // 自定义 HTML 模板路径,相对项目根路径(可选)
103
- "index": "./public/index.html",
104
- "dev": "./public/dev.html",
105
- "entry": "./public/entry.html"
106
- }
107
- },
108
- "plugin": { // snack 模块独立 index.html 外挂文件(可选)
109
- "js": "./plugin.ts",
110
- "css": "./plugin.scss"
89
+ "input": "./src/package",
90
+ "output": "./dist",
91
+ "snack": {
92
+ "externals": {},
93
+ "buildIgnore": [],
94
+ "devPackage": [],
95
+ "entry": {
96
+ "name": "entry",
97
+ "id": "my_page",
98
+ "type": "portal",
99
+ "title": "My App",
100
+ "favicon": "./favicon.ico",
101
+ "service": "",
102
+ "devServer": "",
103
+ "mobile": {
104
+ "id": "my_page_mobile",
105
+ "type": "portal"
106
+ },
107
+ "files": [],
108
+ "js": "./plugin.ts",
109
+ "css": "./plugin.scss",
110
+ "template": {
111
+ "index": "./public/index.html",
112
+ "dev": "./public/dev.html",
113
+ "entry": "./public/entry.html"
114
+ }
115
+ },
116
+ "plugin": {
117
+ "js": "./plugin.ts",
118
+ "css": "./plugin.scss"
119
+ }
111
120
  }
112
- }
113
121
  }
114
122
  ```
115
123
 
124
+ | 字段 | 说明 | 默认值 |
125
+ |------|------|--------|
126
+ | `input` | snack 模块目录 | `"src/package"` |
127
+ | `output` | 生产输出目录 | `"dist"` |
128
+ | `snack.externals` | 同 webpack externals,过滤不打包的依赖 | `{}` |
129
+ | `snack.buildIgnore` | 生产打包忽略的模块(模块目录名) | `[]` |
130
+ | `snack.devPackage` | 开发模式仅启动指定模块,空数组表示全部启动 | `[]` |
131
+ | `snack.entry.name` | 入口页输出目录名 | `"entry"` |
132
+ | `snack.entry.id` | 所加载的页面 id | — |
133
+ | `snack.entry.type` | 所加载的页面分类 | — |
134
+ | `snack.entry.title` | HTML 页面 title | — |
135
+ | `snack.entry.favicon` | 浏览器角标路径,相对项目根路径 | — |
136
+ | `snack.entry.service` | snackbar 服务端地址,默认使用当前地址栏 | — |
137
+ | `snack.entry.devServer` | 调试服务网关 | — |
138
+ | `snack.entry.mobile` | 移动端页面配置,竖屏或宽度 < 1024px 时加载 | — |
139
+ | `snack.entry.files` | 需复制到发布目录的文件或目录路径 | `[]` |
140
+ | `snack.entry.js` | 外挂打包的 JS/TS 文件路径 | — |
141
+ | `snack.entry.css` | 外挂打包的 CSS/SCSS 文件路径 | — |
142
+ | `snack.entry.template` | 自定义 HTML 模板路径,相对项目根路径 | — |
143
+
116
144
  ## config-overrides.js
117
145
 
118
146
  在项目根目录创建 `config-overrides.js` 可自定义 webpack 配置:
119
147
 
120
148
  ```js
121
149
  module.exports = (config) => {
122
- // 修改 webpack 配置
123
- return config;
150
+ // 修改 webpack 配置
151
+ return config;
124
152
  };
125
153
  ```
126
154
 
@@ -131,9 +159,9 @@ module.exports = (config) => {
131
159
  ```json
132
160
  {
133
161
  "scripts": {
134
- "start": "snack-scripts start --templatePath=template",
135
- "build": "snack-scripts build --templatePath=template",
136
- "entry": "snack-scripts entry --templatePath=template"
162
+ "start": "snack-cli start --templatePath=template",
163
+ "build": "snack-cli build --templatePath=template",
164
+ "entry": "snack-cli entry --templatePath=template"
137
165
  }
138
166
  }
139
167
  ```
@@ -142,6 +170,19 @@ module.exports = (config) => {
142
170
 
143
171
  ## Changelog
144
172
 
173
+ ### 0.6.0
174
+
175
+ - 新增:`snack-cli` 命令别名,与 `snack-scripts` 完全等价,`snack-scripts` 保留作为历史兼容
176
+ - 新增:`init` 命令支持交互式选择 React 版本(17 / 18 / 19),生成对应依赖版本
177
+ - 新增:CLI 提示语言跟随系统语言自动切换(`LANG` 环境变量,中文 / English)
178
+ - 修复:`init` 生成的 `@snack-kit/core` 版本从 `^0.1.0` 修正为 `^0.3.0`
179
+ - 修复:`init` 生成的 `scripts` 命令从 `snack-scripts` 更新为 `snack-cli`
180
+ - 优化:升级 `sass-loader` 至 v13,启用 Modern API(`api: 'modern'`),消除 Dart Sass Legacy JS API deprecation 警告
181
+
182
+ ### 0.5.0
183
+
184
+ - 修复:默认渲染高度的问题
185
+
145
186
  ### 0.4.0
146
187
 
147
188
  - 修复:调试窗口 topbar 工具区样式细节优化
package/bin/main.js CHANGED
@@ -29,6 +29,58 @@ const platform = process.platform;
29
29
  const webpackFile = platform === 'win32' ? 'webpack.cmd' : 'webpack';
30
30
  const isNpm = fs.existsSync(path.resolve(process.cwd(), './node_modules/.bin/', webpackFile));
31
31
 
32
+ // ─── 语言检测 ─────────────────────────────────────────────────────────────────
33
+
34
+ function detectLang() {
35
+ const env = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || process.env.LC_MESSAGES || '';
36
+ return env.toLowerCase().startsWith('zh') ? 'zh' : 'en';
37
+ }
38
+
39
+ const lang = detectLang();
40
+ const t = {
41
+ // init
42
+ initTitle: { zh: ' Snack 项目初始化', en: ' Snack Project Init' },
43
+ projectName: { zh: '项目名称', en: 'Project name' },
44
+ description: { zh: '项目描述', en: 'Description' },
45
+ author: { zh: '作者', en: 'Author' },
46
+ debugServers: { zh: '调试服务器(多个用逗号分隔)', en: 'Debug servers (comma separated)' },
47
+ entryPageId: { zh: '入口页面 ID', en: 'Entry page id' },
48
+ entryPageType: { zh: '入口页面类型', en: 'Entry page type' },
49
+ reactVersion: { zh: 'React 版本', en: 'React version' },
50
+ projectCreated: { zh: ' 项目已创建:', en: ' Project created at:' },
51
+ nextSteps: { zh: ' 后续步骤:', en: ' Next steps:' },
52
+ // create
53
+ createTitle: { zh: ' Snack 模块创建', en: ' Snack Module Create' },
54
+ moduleName: { zh: '模块名称', en: 'Module name' },
55
+ moduleDirName: { zh: '模块目录名(PascalCase)', en: 'Module directory name (PascalCase)' },
56
+ withSetting: { zh: '生成配置模块?(y/n)', en: 'Generate setting module? (y/n)' },
57
+ moduleCreated: { zh: ' 模块已创建:', en: ' Module created:' },
58
+ filesGenerated: { zh: ' 已生成文件:', en: ' Files generated:' },
59
+ // errors
60
+ errNoPkg: { zh: '错误:当前目录没有 package.json,请在 snack 工程根目录下执行该命令', en: 'Error: No package.json found. Run this command in a snack project root.' },
61
+ errDirRequired: { zh: '错误:模块目录名不能为空', en: 'Error: Module directory name is required' },
62
+ errDirExists: { zh: '错误:模块目录已存在:', en: 'Error: Module directory already exists:' },
63
+ errNameConflict: { zh: '请修改 snack-cli package.json "name" 的默认名称,该名称将在部署时作为分类目录', en: 'Please change the default "name" in package.json. It will be used as a category directory during deployment.' },
64
+ errNoEntry: { zh: 'package.json 未找到 snack.entry 配置项', en: 'snack.entry config not found in package.json' },
65
+ errTempDir: { zh: '临时目录创建失败', en: 'Failed to create temp directory' },
66
+ errIntegrity: { zh: '完整性校验失败', en: 'Integrity check failed' },
67
+ // build logs
68
+ clearOutputDir: { zh: '清理输出目录', en: 'clear output dir' },
69
+ clearOutputDirEnd: { zh: '清理完成', en: 'clear output dir end' },
70
+ devServerRunning: { zh: 'Snack 开发服务器运行中...', en: 'Snack DevServer Runing...' },
71
+ createLoaderAndHTML: { zh: '生成 loader.js 和入口 HTML(并行)...', en: 'create loader.js & entry HTML (parallel) ...' },
72
+ createLoaderAndHTMLEnd: { zh: '生成完成', en: 'create loader.js & entry HTML end' },
73
+ createSnack: { zh: '生成 snack 模块...', en: 'create snack ...' },
74
+ createSnackEnd: { zh: '生成完成', en: 'create snack end' },
75
+ createEntry: { zh: '生成入口页...', en: 'create entry ...' },
76
+ integrityPass: { zh: '完整性校验通过', en: 'Integrity check passed' },
77
+ integrityFileMissing: { zh: '完整性校验:文件缺失', en: 'snack check: file is missing' },
78
+ };
79
+
80
+ function i(key) {
81
+ return t[key][lang];
82
+ }
83
+
32
84
  // ─── 交互式提问工具 ───────────────────────────────────────────────────────────
33
85
 
34
86
  function prompt(rl, question, defaultValue) {
@@ -41,36 +93,59 @@ function prompt(rl, question, defaultValue) {
41
93
  });
42
94
  }
43
95
 
96
+ function select(rl, question, options, defaultValue) {
97
+ return new Promise(resolve => {
98
+ const hint = options.map((o, i) => `${i + 1}) ${o}`).join(' ');
99
+ const defaultHint = options.includes(defaultValue) ? defaultValue : options[0];
100
+ rl.question(`${question} [${hint}] (${defaultHint}): `, answer => {
101
+ const val = answer.trim();
102
+ if (val === '') return resolve(defaultHint);
103
+ const num = parseInt(val, 10);
104
+ if (!isNaN(num) && num >= 1 && num <= options.length) return resolve(options[num - 1]);
105
+ if (options.includes(val)) return resolve(val);
106
+ resolve(defaultHint);
107
+ });
108
+ });
109
+ }
110
+
44
111
  // ─── init 命令:初始化新 snack 工程 ──────────────────────────────────────────
45
112
 
46
113
  async function runInit() {
47
114
  const targetDir = argv._[1] ? path.resolve(process.cwd(), argv._[1]) : process.cwd();
48
115
  const dirName = path.basename(targetDir);
49
116
 
50
- console.log('\n Snack Project Init\n');
117
+ console.log(`\n${i('initTitle')}\n`);
51
118
 
52
119
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
53
120
 
54
- const name = await prompt(rl, 'Project name', dirName);
55
- const description = await prompt(rl, 'Description', '');
56
- const author = await prompt(rl, 'Author', '');
57
- const debugInput = await prompt(rl, 'Debug servers (comma separated)', 'http://127.0.0.1:3000');
58
- const entryId = await prompt(rl, 'Entry page id', name);
59
- const entryType = await prompt(rl, 'Entry page type', 'portal');
121
+ const name = await prompt(rl, i('projectName'), dirName);
122
+ const description = await prompt(rl, i('description'), '');
123
+ const author = await prompt(rl, i('author'), '');
124
+ const debugInput = await prompt(rl, i('debugServers'), 'http://127.0.0.1:3000');
125
+ const entryId = await prompt(rl, i('entryPageId'), name);
126
+ const entryType = await prompt(rl, i('entryPageType'), 'portal');
127
+ const reactVersion = await select(rl, i('reactVersion'), ['17', '18', '19'], '19');
60
128
 
61
129
  rl.close();
62
130
 
63
131
  const debugServers = debugInput.split(',').map(s => s.trim()).filter(Boolean);
64
132
 
133
+ const reactVersionMap = {
134
+ '17': { react: '^17.0.0', reactDom: '^17.0.0', typesReact: '^17.0.0', typesReactDom: '^17.0.0' },
135
+ '18': { react: '^18.0.0', reactDom: '^18.0.0', typesReact: '^18.0.0', typesReactDom: '^18.0.0' },
136
+ '19': { react: '^19.0.0', reactDom: '^19.0.0', typesReact: '^19.0.0', typesReactDom: '^19.0.0' },
137
+ };
138
+ const reactDeps = reactVersionMap[reactVersion];
139
+
65
140
  // ── package.json ─────────────────────────────────────────────────────────
66
141
  const pkg = {
67
142
  name,
68
143
  version: '1.0.0',
69
144
  description,
70
145
  scripts: {
71
- start: 'snack-scripts start',
72
- build: 'snack-scripts build',
73
- entry: 'snack-scripts entry'
146
+ start: 'snack-cli start',
147
+ build: 'snack-cli build',
148
+ entry: 'snack-cli entry'
74
149
  },
75
150
  input: './src/package',
76
151
  output: './dist',
@@ -90,13 +165,13 @@ async function runInit() {
90
165
  author,
91
166
  license: 'ISC',
92
167
  dependencies: {
93
- '@snack-kit/core': '^0.1.0',
94
- 'react': '^19.0.0',
95
- 'react-dom': '^19.0.0'
168
+ '@snack-kit/core': '^0.3.0',
169
+ 'react': reactDeps.react,
170
+ 'react-dom': reactDeps.reactDom
96
171
  },
97
172
  devDependencies: {
98
- '@types/react': '^19.0.0',
99
- '@types/react-dom': '^19.0.0'
173
+ '@types/react': reactDeps.typesReact,
174
+ '@types/react-dom': reactDeps.typesReactDom
100
175
  },
101
176
  dev: { debug: debugServers }
102
177
  };
@@ -150,9 +225,9 @@ declare module '*.bmp'
150
225
  await fs.outputFile(path.join(targetDir, 'src/env.d.ts'), envDts);
151
226
 
152
227
  console.log(`
153
- Project created at: ${targetDir}
228
+ ${i('projectCreated')} ${targetDir}
154
229
 
155
- Next steps:
230
+ ${i('nextSteps')}
156
231
  ${targetDir !== process.cwd() ? `cd ${argv._[1]}\n ` : ''}npm install
157
232
  npm run start
158
233
  `);
@@ -161,45 +236,43 @@ declare module '*.bmp'
161
236
  // ─── create 命令:在当前工程创建新模块模版 ────────────────────────────────────
162
237
 
163
238
  async function runCreate() {
164
- // 确认在一个 snack 工程目录下
165
239
  const pkgPath = path.resolve(projectPath, 'package.json');
166
240
  if (!fs.existsSync(pkgPath)) {
167
- console.error('Error: 当前目录没有 package.json,请在 snack 工程根目录下执行该命令');
241
+ console.error(i('errNoPkg'));
168
242
  process.exit(1);
169
243
  }
170
244
  const projectPkg = fs.readJSONSync(pkgPath);
171
245
  const packageDir = path.resolve(projectPath, projectPkg.input || 'src/package');
172
246
 
173
- console.log('\n Snack Module Create\n');
247
+ console.log(`\n${i('createTitle')}\n`);
174
248
 
175
249
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
176
250
 
177
- const chineseName = await prompt(rl, 'Module name (Chinese)', '新模块');
178
- let dirName = await prompt(rl, 'Module directory name (PascalCase)', '');
179
- const description = await prompt(rl, 'Description', '');
180
- const author = await prompt(rl, 'Author', projectPkg.author || '');
181
- const withSetting = (await prompt(rl, 'Generate setting module? (y/n)', 'y')).toLowerCase() === 'y';
251
+ const moduleName = await prompt(rl, i('moduleName'), lang === 'zh' ? '新模块' : 'New Module');
252
+ let dirName = await prompt(rl, i('moduleDirName'), '');
253
+ const description = await prompt(rl, i('description'), '');
254
+ const author = await prompt(rl, i('author'), projectPkg.author || '');
255
+ const withSetting = (await prompt(rl, i('withSetting'), 'y')).toLowerCase() === 'y';
182
256
 
183
257
  rl.close();
184
258
 
185
259
  if (!dirName) {
186
- console.error('Error: Module directory name is required');
260
+ console.error(i('errDirRequired'));
187
261
  process.exit(1);
188
262
  }
189
263
 
190
- // 首字母大写
191
264
  dirName = dirName.charAt(0).toUpperCase() + dirName.slice(1);
192
265
  const moduleDir = path.join(packageDir, dirName);
193
266
  const className = dirName;
194
267
 
195
268
  if (fs.existsSync(moduleDir)) {
196
- console.error(`Error: 模块目录已存在: ${moduleDir}`);
269
+ console.error(`${i('errDirExists')} ${moduleDir}`);
197
270
  process.exit(1);
198
271
  }
199
272
 
200
273
  // ── snack.json ────────────────────────────────────────────────────────────
201
274
  const snackJson = {
202
- name: chineseName,
275
+ name: moduleName,
203
276
  version: '0.0.0.1',
204
277
  author,
205
278
  description
@@ -268,9 +341,9 @@ export class ${className}Setting extends SnackSetting {
268
341
  }
269
342
 
270
343
  console.log(`
271
- Module created: ${moduleDir}
344
+ ${i('moduleCreated')} ${moduleDir}
272
345
 
273
- Files generated:
346
+ ${i('filesGenerated')}
274
347
  ${dirName}/snack.json
275
348
  ${dirName}/index.ts
276
349
  ${dirName}/src/index.tsx
@@ -305,7 +378,7 @@ async function check(output) {
305
378
  if (!await fs.exists(snackjson)) continue;
306
379
  const index = path.join(output, dirname, 'index.js');
307
380
  if (!await fs.exists(index)) {
308
- console.error('snack check: ', index, ' file is missing');
381
+ console.error(`${i('integrityFileMissing')}: ${index}`);
309
382
  return false;
310
383
  }
311
384
  }
@@ -313,14 +386,14 @@ async function check(output) {
313
386
  console.error('snack check: ', err);
314
387
  return false;
315
388
  }
316
- console.log('完整性校验通过');
389
+ console.log(i('integrityPass'));
317
390
  return true;
318
391
  }
319
392
 
320
393
  async function run(script) {
321
394
  const projectPackageJSON = fs.readJSONSync(path.resolve(projectPath, 'package.json'));
322
395
  if (projectPackageJSON.name === 'snack-cli') {
323
- throw '请修改 snack-cli package.json "name" 的默认名称,该名称将在部署时作为分类目录';
396
+ throw i('errNameConflict');
324
397
  }
325
398
 
326
399
  process.env.SNACK_CLI_VERSION = version;
@@ -337,17 +410,17 @@ async function run(script) {
337
410
  await fs.remove(process.env.PROJECT_TEMPLATE_PATH);
338
411
  const err = await fs.copy(templatePath, process.env.PROJECT_TEMPLATE_PATH);
339
412
  if (err) {
340
- console.error('临时目录创建失败');
413
+ console.error(i('errTempDir'));
341
414
  throw err;
342
415
  }
343
416
 
344
417
  process.env.PROJECT_SNACK_CONFIG = JSON.stringify(projectPackageJSON.snack || {});
345
418
 
346
419
  if (script === 'entry') {
347
- if (!projectPackageJSON.snack?.entry) throw 'package.json 未找到snack.entry配置项';
420
+ if (!projectPackageJSON.snack?.entry) throw i('errNoEntry');
348
421
  process.env.RUN_MODE = 'production';
349
422
  process.env.PROJECT_OUT_PATH = path.resolve(projectPath, projectPackageJSON.snack.entry.name || 'entry');
350
- console.log('create entry ...');
423
+ console.log(i('createEntry'));
351
424
  await execWebpack(['--mode=production', `--config=${entryConfigPath}`]);
352
425
  } else {
353
426
  if (script === 'start') {
@@ -360,28 +433,28 @@ async function run(script) {
360
433
 
361
434
  process.env.PROJECT_PACKAGE_LIST = JSON.stringify(GetPackageList(process.env.PROJECT_PACKAGE_PATH));
362
435
 
363
- console.log(`clear output dir "${process.env.PROJECT_OUT_PATH}" ...`);
436
+ console.log(`${i('clearOutputDir')} "${process.env.PROJECT_OUT_PATH}" ...`);
364
437
  clearOutput(process.env.PROJECT_OUT_PATH);
365
- console.log('clear output dir end');
438
+ console.log(i('clearOutputDirEnd'));
366
439
 
367
440
  if (script === 'start') {
368
- console.log('Snack DevServer Runing...');
441
+ console.log(i('devServerRunning'));
369
442
  await execWebpack(['serve', '--mode=development', `--config=${devConfigPath}`]);
370
443
  } else {
371
- console.log('create loader.js & entry HTML (parallel) ...');
444
+ console.log(i('createLoaderAndHTML'));
372
445
  await Promise.all([
373
446
  execWebpack(['--mode=production', `--config=${loaderConfigPath}`]),
374
447
  execWebpack(['--mode=production', `--config=${indexHTMLPath}`])
375
448
  ]);
376
- console.log('create loader.js & entry HTML end');
377
- console.log('create snack ...');
449
+ console.log(i('createLoaderAndHTMLEnd'));
450
+ console.log(i('createSnack'));
378
451
  await execWebpack(['--mode=production', `--config=${snackPath}`]);
379
- console.log('create snack end');
452
+ console.log(i('createSnackEnd'));
380
453
  }
381
454
 
382
455
  if (process.env.RUN_MODE !== 'development' && !await check(process.env.PROJECT_OUT_PATH)) {
383
456
  fs.removeSync(process.env.PROJECT_OUT_PATH);
384
- throw '完整性校验失败';
457
+ throw i('errIntegrity');
385
458
  }
386
459
  }
387
460
 
@@ -151,7 +151,7 @@ function createSwcRule(withRefresh = false) {
151
151
  */
152
152
  const styleRule = {
153
153
  test: /\.(css|sass|scss)$/,
154
- use: ['style-loader', 'css-loader', 'sass-loader']
154
+ use: ['style-loader', 'css-loader', { loader: 'sass-loader', options: { api: 'modern' } }]
155
155
  };
156
156
 
157
157
  /**
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@snack-kit/scripts",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "snack-cli package scripts Powered by Para FED",
5
5
  "bin": {
6
- "snack-scripts": "./bin/main.js"
6
+ "snack-scripts": "./bin/main.js",
7
+ "snack-cli": "./bin/main.js"
7
8
  },
8
9
  "files": [
9
10
  "bin",
@@ -33,8 +34,6 @@
33
34
  "@babel/preset-react": "^7.24.7",
34
35
  "@babel/preset-typescript": "^7.24.7",
35
36
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
36
- "react": "^19.0.0",
37
- "react-dom": "^19.0.0",
38
37
  "@snack-kit/core": "^0.3.0",
39
38
  "@snack-kit/lib": "^0.6.0",
40
39
  "@swc/core": "^1.2.100",
@@ -45,10 +44,12 @@
45
44
  "fs-extra": "^8.1.0",
46
45
  "html-webpack-plugin": "^5.3.2",
47
46
  "minimist": "^1.2.8",
47
+ "react": "^19.0.0",
48
+ "react-dom": "^19.0.0",
48
49
  "react-refresh": "^0.11.0",
49
50
  "regenerator-runtime": "^0.13.9",
50
51
  "sass": "^1.77.2",
51
- "sass-loader": "^12.1.0",
52
+ "sass-loader": "^13.3.3",
52
53
  "style-loader": "^3.2.1",
53
54
  "swc-loader": "^0.1.15",
54
55
  "typescript": "^4.4.2",
@@ -411,22 +411,17 @@ const RefreshBtn = ({ onRefresh }: any) => (
411
411
  // ─── Pane Content Wrapper ─────────────────────────────────────────────────────
412
412
 
413
413
  const PaneContent = ({ width, scale, bg, children }: any) => {
414
- const style: React.CSSProperties = {};
414
+ const innerStyle: React.CSSProperties = {};
415
415
  if (width) {
416
- style.width = width;
417
- style.margin = '0 auto';
418
- style.flexShrink = 0;
416
+ innerStyle.width = width;
417
+ innerStyle.margin = '0 auto';
419
418
  }
420
419
  if (scale !== 100) {
421
- style.transform = `scale(${scale / 100})`;
422
- style.transformOrigin = 'top left';
423
- if (width) {
424
- style.width = width;
425
- }
420
+ innerStyle.zoom = scale / 100;
426
421
  }
427
422
  return (
428
423
  <div className={`sd-pane__content sd-pane__content--bg-${bg}`}>
429
- <div style={style}>
424
+ <div style={innerStyle}>
430
425
  {children}
431
426
  </div>
432
427
  </div>
@@ -743,6 +743,13 @@ button {
743
743
  overflow: auto;
744
744
  padding: 16px;
745
745
  transition: background .2s;
746
+ // 让内部子元素可以用 min-height: 100% 撑满
747
+ display: flex;
748
+ flex-direction: column;
749
+
750
+ & > div {
751
+ flex: 1;
752
+ }
746
753
 
747
754
  // 背景模式
748
755
  &--bg-default { background: var(--bg); }