electron-forge-maker-innosetup 0.2.9 → 0.3.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 +79 -11
- package/dist/MakerInnosetup.js +51 -3
- package/dist/generator.d.ts +5 -0
- package/dist/generator.js +73 -6
- package/dist/parser.d.ts +15 -1
- package/dist/parser.js +119 -4
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
- package/vendor/innosetup/ISPP.dll +0 -0
- package/vendor/innosetup/Setup.e32 +0 -0
- package/vendor/innosetup/SetupLdr.e32 +0 -0
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# electron-forge-maker-innosetup
|
|
2
2
|
|
|
3
3
|
一个用于 [Electron Forge](https://www.electronforge.io/) 的 Innosetup Maker,支持使用 Innosetup 为 Windows 平台创建安装程序。继承自 `@electron-forge/maker-base`。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install --save-dev
|
|
8
|
+
npm install --save-dev electron-forge-maker-innosetup
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## 前置要求
|
|
@@ -33,16 +33,16 @@ vendor/
|
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
35
|
// 命名导入(推荐)
|
|
36
|
-
import { MakerInnosetup } from "
|
|
36
|
+
import { MakerInnosetup } from "electron-forge-maker-innosetup";
|
|
37
37
|
|
|
38
38
|
// 默认导入
|
|
39
|
-
import MakerInnosetup from "
|
|
39
|
+
import MakerInnosetup from "electron-forge-maker-innosetup";
|
|
40
40
|
|
|
41
41
|
// 导入解析器
|
|
42
42
|
import {
|
|
43
43
|
MakerInnosetup,
|
|
44
44
|
InnoScriptParser,
|
|
45
|
-
} from "
|
|
45
|
+
} from "electron-forge-maker-innosetup";
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
### 方式一:在配置文件中使用(推荐)
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
|
|
52
52
|
```typescript
|
|
53
53
|
import type { ForgeConfig } from "@electron-forge/shared-types";
|
|
54
|
-
import MakerInnosetup from "
|
|
54
|
+
import MakerInnosetup from "electron-forge-maker-innosetup";
|
|
55
55
|
|
|
56
56
|
const config: ForgeConfig = {
|
|
57
57
|
makers: [
|
|
@@ -78,7 +78,7 @@ export default config;
|
|
|
78
78
|
module.exports = {
|
|
79
79
|
makers: [
|
|
80
80
|
{
|
|
81
|
-
name: "
|
|
81
|
+
name: "electron-forge-maker-innosetup",
|
|
82
82
|
config: {
|
|
83
83
|
appName: "MyApp",
|
|
84
84
|
appPublisher: "My Company",
|
|
@@ -92,7 +92,7 @@ module.exports = {
|
|
|
92
92
|
### 完整配置示例
|
|
93
93
|
|
|
94
94
|
```typescript
|
|
95
|
-
import type { MakerInnosetupConfig } from "
|
|
95
|
+
import type { MakerInnosetupConfig } from "electron-forge-maker-innosetup";
|
|
96
96
|
|
|
97
97
|
const config: MakerInnosetupConfig = {
|
|
98
98
|
// 应用信息
|
|
@@ -185,7 +185,7 @@ const config: MakerInnosetupConfig = {
|
|
|
185
185
|
|
|
186
186
|
```javascript
|
|
187
187
|
{
|
|
188
|
-
name: '
|
|
188
|
+
name: 'electron-forge-maker-innosetup',
|
|
189
189
|
config: {
|
|
190
190
|
scriptPath: './installer.iss'
|
|
191
191
|
}
|
|
@@ -196,7 +196,7 @@ const config: MakerInnosetupConfig = {
|
|
|
196
196
|
#### 方法 2: 解析 ISS 文件为配置
|
|
197
197
|
|
|
198
198
|
```typescript
|
|
199
|
-
import { MakerInnosetup } from "
|
|
199
|
+
import { MakerInnosetup } from "electron-forge-maker-innosetup";
|
|
200
200
|
|
|
201
201
|
// 从 ISS 文件解析配置
|
|
202
202
|
const config = MakerInnosetup.fromIssFile("./installer.iss");
|
|
@@ -209,7 +209,7 @@ const config2 = MakerInnosetup.fromIssContent(issContent);
|
|
|
209
209
|
const forgeConfig: ForgeConfig = {
|
|
210
210
|
makers: [
|
|
211
211
|
{
|
|
212
|
-
name: "
|
|
212
|
+
name: "electron-forge-maker-innosetup",
|
|
213
213
|
config: config, // 使用解析后的配置
|
|
214
214
|
platforms: ["win32"],
|
|
215
215
|
},
|
|
@@ -268,6 +268,74 @@ const forgeConfig: ForgeConfig = {
|
|
|
268
268
|
|
|
269
269
|
## 高级用法
|
|
270
270
|
|
|
271
|
+
### 使用预处理器常量 (#define)
|
|
272
|
+
|
|
273
|
+
使用 `Defines` 字段可以定义预处理器常量,在配置中使用 `{#ConstantName}` 引用:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
config: {
|
|
277
|
+
config: {
|
|
278
|
+
// 定义预处理器常量
|
|
279
|
+
Defines: {
|
|
280
|
+
MyAppName: "Police Self Report",
|
|
281
|
+
MyAppVersion: "1.0.0",
|
|
282
|
+
MyAppPublisher: "合肥视尔信息科技有限公司",
|
|
283
|
+
MyAppExeName: "Police Self Report.exe",
|
|
284
|
+
MyAppAssocName: "Police Self Report File",
|
|
285
|
+
MyAppAssocExt: ".myp",
|
|
286
|
+
MyAppShortcutName: "公安自助接报案系统",
|
|
287
|
+
},
|
|
288
|
+
Setup: {
|
|
289
|
+
// 使用 {#ConstantName} 引用预处理器常量
|
|
290
|
+
AppName: "{#MyAppName}",
|
|
291
|
+
AppVersion: "{#MyAppVersion}",
|
|
292
|
+
AppPublisher: "{#MyAppPublisher}",
|
|
293
|
+
DefaultDirName: "{autopf}\\{#MyAppName}",
|
|
294
|
+
OutputBaseFilename: "{#MyAppName}_{#MyAppVersion}",
|
|
295
|
+
ChangesAssociations: true,
|
|
296
|
+
},
|
|
297
|
+
Icons: [
|
|
298
|
+
{
|
|
299
|
+
Name: "{group}\\{#MyAppShortcutName}",
|
|
300
|
+
Filename: "{app}\\{#MyAppExeName}",
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
Registry: [
|
|
304
|
+
{
|
|
305
|
+
Root: "HKCR",
|
|
306
|
+
Subkey: "{#MyAppAssocExt}",
|
|
307
|
+
ValueType: "string",
|
|
308
|
+
ValueName: "",
|
|
309
|
+
ValueData: "{#MyAppAssocName}",
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
生成的 ISS 脚本将包含:
|
|
317
|
+
|
|
318
|
+
```iss
|
|
319
|
+
; Script generated by the Inno Setup Script Wizard.
|
|
320
|
+
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
|
321
|
+
|
|
322
|
+
#define MyAppName "Police Self Report"
|
|
323
|
+
#define MyAppVersion "1.0.0"
|
|
324
|
+
#define MyAppPublisher "合肥视尔信息科技有限公司"
|
|
325
|
+
#define MyAppExeName "Police Self Report.exe"
|
|
326
|
+
|
|
327
|
+
[Setup]
|
|
328
|
+
AppName={#MyAppName}
|
|
329
|
+
AppVersion={#MyAppVersion}
|
|
330
|
+
AppPublisher={#MyAppPublisher}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**优势:**
|
|
334
|
+
|
|
335
|
+
- 🔄 更易维护:集中管理常量,修改一处即可
|
|
336
|
+
- 📖 更清晰:生成的 ISS 脚本更具可读性
|
|
337
|
+
- 🔧 更灵活:支持 Inno Setup 原生的预处理器功能
|
|
338
|
+
|
|
271
339
|
### 添加自定义 Pascal 代码
|
|
272
340
|
|
|
273
341
|
```typescript
|
package/dist/MakerInnosetup.js
CHANGED
|
@@ -174,7 +174,7 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
174
174
|
AppName: this.config.appName || appName,
|
|
175
175
|
AppVersion: this.config.appVersion || appVersion,
|
|
176
176
|
AppPublisher: this.config.appPublisher || "",
|
|
177
|
-
AppId: this.config.appId || `{{${appName}}`,
|
|
177
|
+
AppId: this.config.appId || `{{${appName}}}`,
|
|
178
178
|
DefaultDirName: `{autopf}\\${appName}`,
|
|
179
179
|
DefaultGroupName: appName,
|
|
180
180
|
OutputDir: outputDir,
|
|
@@ -234,6 +234,7 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
234
234
|
return defaultConfig;
|
|
235
235
|
}
|
|
236
236
|
return {
|
|
237
|
+
Defines: userConfig.Defines || defaultConfig.Defines,
|
|
237
238
|
Setup: { ...defaultConfig.Setup, ...userConfig.Setup },
|
|
238
239
|
Languages: userConfig.Languages || defaultConfig.Languages,
|
|
239
240
|
Tasks: userConfig.Tasks || defaultConfig.Tasks,
|
|
@@ -260,7 +261,12 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
260
261
|
if (!config.Tasks) {
|
|
261
262
|
config.Tasks = [];
|
|
262
263
|
}
|
|
263
|
-
|
|
264
|
+
// 检查用户是否已经自定义了桌面图标任务
|
|
265
|
+
const hasDesktopTask = config.Tasks.some((task) => task.Name === "desktopicon");
|
|
266
|
+
const hasDesktopIcon = config.Icons?.some((icon) => icon.Name?.includes("{autodesktop}") && icon.Tasks === "desktopicon");
|
|
267
|
+
if (this.config.createDesktopIcon !== false &&
|
|
268
|
+
!hasDesktopTask &&
|
|
269
|
+
!hasDesktopIcon) {
|
|
264
270
|
config.Tasks.push({
|
|
265
271
|
Name: "desktopicon",
|
|
266
272
|
Description: "Create a &desktop icon",
|
|
@@ -275,7 +281,12 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
275
281
|
});
|
|
276
282
|
}
|
|
277
283
|
}
|
|
278
|
-
|
|
284
|
+
// 检查用户是否已经自定义了快速启动任务
|
|
285
|
+
const hasQuickLaunchTask = config.Tasks.some((task) => task.Name === "quicklaunchicon");
|
|
286
|
+
const hasQuickLaunchIcon = config.Icons?.some((icon) => icon.Name?.includes("Quick Launch") && icon.Tasks === "quicklaunchicon");
|
|
287
|
+
if (this.config.createQuickLaunchIcon &&
|
|
288
|
+
!hasQuickLaunchTask &&
|
|
289
|
+
!hasQuickLaunchIcon) {
|
|
279
290
|
config.Tasks.push({
|
|
280
291
|
Name: "quicklaunchicon",
|
|
281
292
|
Description: "Create a &Quick Launch icon",
|
|
@@ -306,6 +317,30 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
306
317
|
});
|
|
307
318
|
let output = "";
|
|
308
319
|
let errorOutput = "";
|
|
320
|
+
let isResolved = false;
|
|
321
|
+
const timeout = this.config.compileTimeout || 300000;
|
|
322
|
+
const timeoutHandle = setTimeout(() => {
|
|
323
|
+
if (!isResolved) {
|
|
324
|
+
isResolved = false;
|
|
325
|
+
console.error("Innosetup compilation timeout, killing process...");
|
|
326
|
+
try {
|
|
327
|
+
iscc.kill("SIGKILL");
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
console.error("Failed to kill process", e);
|
|
331
|
+
}
|
|
332
|
+
reject(new Error(`Innosetup compilation timeout after ${timeout} ms`));
|
|
333
|
+
}
|
|
334
|
+
}, timeout);
|
|
335
|
+
const cleanup = () => {
|
|
336
|
+
clearTimeout(timeoutHandle);
|
|
337
|
+
try {
|
|
338
|
+
if (iscc && !iscc.killed) {
|
|
339
|
+
iscc.kill("SIGKILL");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (e) { }
|
|
343
|
+
};
|
|
309
344
|
iscc.stdout.on("data", (data) => {
|
|
310
345
|
const text = data.toString();
|
|
311
346
|
output += text;
|
|
@@ -317,16 +352,29 @@ class MakerInnosetup extends maker_base_1.default {
|
|
|
317
352
|
console.error(text);
|
|
318
353
|
});
|
|
319
354
|
iscc.on("close", (code) => {
|
|
355
|
+
if (isResolved)
|
|
356
|
+
return;
|
|
357
|
+
isResolved = true;
|
|
358
|
+
clearTimeout(timeoutHandle);
|
|
320
359
|
if (code === 0) {
|
|
321
360
|
resolve(output);
|
|
322
361
|
}
|
|
323
362
|
else {
|
|
363
|
+
cleanup();
|
|
324
364
|
reject(new Error(`Innosetup compilation failed with code ${code}\n${errorOutput}`));
|
|
325
365
|
}
|
|
326
366
|
});
|
|
327
367
|
iscc.on("error", (err) => {
|
|
368
|
+
if (isResolved)
|
|
369
|
+
return;
|
|
370
|
+
isResolved = true;
|
|
371
|
+
cleanup();
|
|
328
372
|
reject(new Error(`Failed to start Innosetup compiler: ${err.message}`));
|
|
329
373
|
});
|
|
374
|
+
// 处理进程意外退出
|
|
375
|
+
process.on("exit", cleanup);
|
|
376
|
+
process.on("SIGINT", cleanup);
|
|
377
|
+
process.on("SIGTERM", cleanup);
|
|
330
378
|
});
|
|
331
379
|
}
|
|
332
380
|
/**
|
package/dist/generator.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export declare class InnoScriptGenerator {
|
|
|
7
7
|
* 生成脚本内容
|
|
8
8
|
*/
|
|
9
9
|
generate(config: InnoSetupConfig): string;
|
|
10
|
+
/**
|
|
11
|
+
* 生成 #define 指令部分
|
|
12
|
+
*/
|
|
13
|
+
private generateDefinesSection;
|
|
10
14
|
/**
|
|
11
15
|
* 生成 Setup 部分
|
|
12
16
|
*/
|
|
@@ -77,6 +81,7 @@ export declare class InnoScriptGenerator {
|
|
|
77
81
|
private generateCodeSection;
|
|
78
82
|
/**
|
|
79
83
|
* 将脚本保存到文件
|
|
84
|
+
* 注意:使用 UTF-8 编码保存,并在文件开头添加 BOM 标记
|
|
80
85
|
*/
|
|
81
86
|
saveToFile(scriptContent: string, filePath: string): void;
|
|
82
87
|
}
|
package/dist/generator.js
CHANGED
|
@@ -45,6 +45,10 @@ class InnoScriptGenerator {
|
|
|
45
45
|
*/
|
|
46
46
|
generate(config) {
|
|
47
47
|
const sections = [];
|
|
48
|
+
// 生成 #define 指令
|
|
49
|
+
if (config.Defines && Object.keys(config.Defines).length > 0) {
|
|
50
|
+
sections.push(this.generateDefinesSection(config.Defines));
|
|
51
|
+
}
|
|
48
52
|
// 生成 [Setup] 部分
|
|
49
53
|
if (config.Setup) {
|
|
50
54
|
sections.push(this.generateSetupSection(config.Setup));
|
|
@@ -114,7 +118,40 @@ class InnoScriptGenerator {
|
|
|
114
118
|
if (config.Code) {
|
|
115
119
|
sections.push(this.generateCodeSection(config.Code));
|
|
116
120
|
}
|
|
117
|
-
|
|
121
|
+
// 添加 UTF-8 BOM 标记,确保 Inno Setup 正确识别中文
|
|
122
|
+
return "\uFEFF" + sections.join("\n\n");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 生成 #define 指令部分
|
|
126
|
+
*/
|
|
127
|
+
generateDefinesSection(defines) {
|
|
128
|
+
const lines = [
|
|
129
|
+
"; Script generated by the Inno Setup Script Wizard.",
|
|
130
|
+
"; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!",
|
|
131
|
+
"",
|
|
132
|
+
];
|
|
133
|
+
for (const [key, value] of Object.entries(defines)) {
|
|
134
|
+
if (value !== undefined && value !== null) {
|
|
135
|
+
const strValue = String(value);
|
|
136
|
+
// 检查是否是表达式(包含函数调用、引用或拼接)
|
|
137
|
+
const isExpression = strValue.includes("(") ||
|
|
138
|
+
strValue.includes("+") ||
|
|
139
|
+
(strValue.match(/^\w+$/) && !strValue.match(/^\d+$/));
|
|
140
|
+
if (typeof value === "string" && !isExpression) {
|
|
141
|
+
// 普通字符串,添加引号
|
|
142
|
+
lines.push(`#define ${key} "${value}"`);
|
|
143
|
+
}
|
|
144
|
+
else if (typeof value === "string" && isExpression) {
|
|
145
|
+
// 表达式,不添加引号
|
|
146
|
+
lines.push(`#define ${key} ${value}`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// 数字等其他类型
|
|
150
|
+
lines.push(`#define ${key} ${value}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return lines.join("\n");
|
|
118
155
|
}
|
|
119
156
|
/**
|
|
120
157
|
* 生成 Setup 部分
|
|
@@ -130,7 +167,15 @@ class InnoScriptGenerator {
|
|
|
130
167
|
lines.push(`${key}=${value}`);
|
|
131
168
|
}
|
|
132
169
|
else {
|
|
133
|
-
|
|
170
|
+
// 检查值是否包含 {#...} 引用
|
|
171
|
+
const stringValue = String(value);
|
|
172
|
+
if (stringValue.includes("{#")) {
|
|
173
|
+
// 如果包含常量引用,不添加额外引号
|
|
174
|
+
lines.push(`${key}=${stringValue}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
lines.push(`${key}=${stringValue}`);
|
|
178
|
+
}
|
|
134
179
|
}
|
|
135
180
|
}
|
|
136
181
|
}
|
|
@@ -366,14 +411,34 @@ class InnoScriptGenerator {
|
|
|
366
411
|
const lines = ["[Registry]"];
|
|
367
412
|
for (const item of items) {
|
|
368
413
|
const parts = [`Root: ${item.Root}`];
|
|
369
|
-
|
|
414
|
+
// Subkey: 检查是否包含 {#...} 引用或函数调用
|
|
415
|
+
const subkey = String(item.Subkey);
|
|
416
|
+
if (subkey.includes("{#") || subkey.includes("StringChange(")) {
|
|
417
|
+
// 如果包含常量引用或函数,不添加引号
|
|
418
|
+
parts.push(`Subkey: "${subkey}"`);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
parts.push(`Subkey: "${subkey}"`);
|
|
422
|
+
}
|
|
370
423
|
if (item.ValueType)
|
|
371
424
|
parts.push(`ValueType: ${item.ValueType}`);
|
|
372
|
-
|
|
373
|
-
|
|
425
|
+
// ValueName: 检查是否包含引用或函数调用
|
|
426
|
+
if (item.ValueName !== undefined) {
|
|
427
|
+
const valueName = String(item.ValueName);
|
|
428
|
+
if (valueName.includes("{#") || valueName.includes("StringChange(")) {
|
|
429
|
+
// 如果包含常量引用或函数,不添加引号
|
|
430
|
+
parts.push(`ValueName: "${valueName}"`);
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
parts.push(`ValueName: "${valueName}"`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
374
436
|
if (item.ValueData !== undefined) {
|
|
375
437
|
if (typeof item.ValueData === "string") {
|
|
376
|
-
|
|
438
|
+
const valueData = item.ValueData;
|
|
439
|
+
// 直接使用原始值,不需要额外转义
|
|
440
|
+
// Inno Setup 中的引号转义应该由用户自己处理
|
|
441
|
+
parts.push(`ValueData: "${valueData}"`);
|
|
377
442
|
}
|
|
378
443
|
else {
|
|
379
444
|
parts.push(`ValueData: ${item.ValueData}`);
|
|
@@ -483,12 +548,14 @@ class InnoScriptGenerator {
|
|
|
483
548
|
}
|
|
484
549
|
/**
|
|
485
550
|
* 将脚本保存到文件
|
|
551
|
+
* 注意:使用 UTF-8 编码保存,并在文件开头添加 BOM 标记
|
|
486
552
|
*/
|
|
487
553
|
saveToFile(scriptContent, filePath) {
|
|
488
554
|
const dir = path.dirname(filePath);
|
|
489
555
|
if (!fs.existsSync(dir)) {
|
|
490
556
|
fs.mkdirSync(dir, { recursive: true });
|
|
491
557
|
}
|
|
558
|
+
// 使用 UTF-8 编码保存,scriptContent 已经包含 BOM
|
|
492
559
|
fs.writeFileSync(filePath, scriptContent, "utf-8");
|
|
493
560
|
}
|
|
494
561
|
}
|
package/dist/parser.d.ts
CHANGED
|
@@ -10,8 +10,22 @@ export declare class InnoScriptParser {
|
|
|
10
10
|
static parseFile(issFilePath: string): InnoSetupConfig;
|
|
11
11
|
/**
|
|
12
12
|
* 解析 ISS 脚本内容
|
|
13
|
+
* @param content ISS 脚本内容
|
|
14
|
+
* @param preserveDefineReferences 是否保留 {#...} 引用(不替换为实际值)
|
|
13
15
|
*/
|
|
14
|
-
static parse(content: string): InnoSetupConfig;
|
|
16
|
+
static parse(content: string, preserveDefineReferences?: boolean): InnoSetupConfig;
|
|
17
|
+
/**
|
|
18
|
+
* 保留 #define 的原始表达式
|
|
19
|
+
*/
|
|
20
|
+
private static preserveDefineExpression;
|
|
21
|
+
/**
|
|
22
|
+
* 解析 #define 的值,支持字符串拼接和函数调用
|
|
23
|
+
*/
|
|
24
|
+
private static parseDefineValue;
|
|
25
|
+
/**
|
|
26
|
+
* 替换字符串中的常量引用 {#ConstantName}
|
|
27
|
+
*/
|
|
28
|
+
private static replaceDefines;
|
|
15
29
|
/**
|
|
16
30
|
* 解析 Setup 段落的一行
|
|
17
31
|
*/
|
package/dist/parser.js
CHANGED
|
@@ -49,19 +49,51 @@ class InnoScriptParser {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* 解析 ISS 脚本内容
|
|
52
|
+
* @param content ISS 脚本内容
|
|
53
|
+
* @param preserveDefineReferences 是否保留 {#...} 引用(不替换为实际值)
|
|
52
54
|
*/
|
|
53
|
-
static parse(content) {
|
|
55
|
+
static parse(content, preserveDefineReferences = true) {
|
|
54
56
|
const config = {};
|
|
57
|
+
const defines = {};
|
|
55
58
|
const lines = content.split(/\r?\n/);
|
|
56
59
|
let currentSection = null;
|
|
57
60
|
let codeSection = "";
|
|
58
61
|
let inCodeSection = false;
|
|
62
|
+
// 第一次扫描:提取所有 #define 定义
|
|
59
63
|
for (let i = 0; i < lines.length; i++) {
|
|
60
64
|
let line = lines[i].trim();
|
|
61
65
|
// 跳过空行和注释
|
|
62
66
|
if (!line || line.startsWith(";") || line.startsWith("//")) {
|
|
63
67
|
continue;
|
|
64
68
|
}
|
|
69
|
+
// 解析 #define 指令
|
|
70
|
+
const defineMatch = line.match(/^#define\s+(\w+)\s+(.+)$/);
|
|
71
|
+
if (defineMatch) {
|
|
72
|
+
const [, varName, varValue] = defineMatch;
|
|
73
|
+
// 如果保留引用,则保存原始表达式;否则计算值
|
|
74
|
+
defines[varName] = preserveDefineReferences
|
|
75
|
+
? this.preserveDefineExpression(varValue)
|
|
76
|
+
: this.parseDefineValue(varValue, defines);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// 如果有 defines,添加到配置中
|
|
80
|
+
if (Object.keys(defines).length > 0) {
|
|
81
|
+
config.Defines = defines;
|
|
82
|
+
}
|
|
83
|
+
// 第二次扫描:解析配置段落
|
|
84
|
+
for (let i = 0; i < lines.length; i++) {
|
|
85
|
+
let line = lines[i].trim();
|
|
86
|
+
// 跳过空行、注释和 #define 指令
|
|
87
|
+
if (!line ||
|
|
88
|
+
line.startsWith(";") ||
|
|
89
|
+
line.startsWith("//") ||
|
|
90
|
+
line.startsWith("#define")) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// 如果不保留引用,则替换常量引用 {#ConstantName}
|
|
94
|
+
if (!preserveDefineReferences) {
|
|
95
|
+
line = this.replaceDefines(line, defines);
|
|
96
|
+
}
|
|
65
97
|
// 检测段落
|
|
66
98
|
const sectionMatch = line.match(/^\[(\w+)\]$/);
|
|
67
99
|
if (sectionMatch) {
|
|
@@ -78,7 +110,11 @@ class InnoScriptParser {
|
|
|
78
110
|
}
|
|
79
111
|
// 如果在 Code 段落中,收集所有代码
|
|
80
112
|
if (inCodeSection) {
|
|
81
|
-
|
|
113
|
+
// 对于 Code 段落,使用原始行(带缩进)
|
|
114
|
+
const processedLine = preserveDefineReferences
|
|
115
|
+
? lines[i]
|
|
116
|
+
: this.replaceDefines(lines[i], defines);
|
|
117
|
+
codeSection += processedLine + "\n";
|
|
82
118
|
continue;
|
|
83
119
|
}
|
|
84
120
|
// 解析不同段落
|
|
@@ -137,6 +173,83 @@ class InnoScriptParser {
|
|
|
137
173
|
}
|
|
138
174
|
return config;
|
|
139
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* 保留 #define 的原始表达式
|
|
178
|
+
*/
|
|
179
|
+
static preserveDefineExpression(value) {
|
|
180
|
+
value = value.trim();
|
|
181
|
+
// 移除外层引号(如果有)
|
|
182
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
183
|
+
return value.slice(1, -1);
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 解析 #define 的值,支持字符串拼接和函数调用
|
|
189
|
+
*/
|
|
190
|
+
static parseDefineValue(value, defines) {
|
|
191
|
+
value = value.trim();
|
|
192
|
+
// 处理字符串拼接 (e.g., MyAppName + " File")
|
|
193
|
+
if (value.includes("+")) {
|
|
194
|
+
const parts = value.split("+").map((p) => p.trim());
|
|
195
|
+
let result = "";
|
|
196
|
+
for (const part of parts) {
|
|
197
|
+
if (part.startsWith('"') && part.endsWith('"')) {
|
|
198
|
+
// 直接的字符串字面量
|
|
199
|
+
result += part.slice(1, -1);
|
|
200
|
+
}
|
|
201
|
+
else if (defines[part]) {
|
|
202
|
+
// 引用已定义的常量
|
|
203
|
+
result += defines[part];
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// 未知引用,保持原样
|
|
207
|
+
result += part;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
// 处理 StringChange 函数 (e.g., StringChange(MyAppAssocName, " ", ""))
|
|
213
|
+
const stringChangeMatch = value.match(/StringChange\(([^,]+),\s*"([^"]*)",\s*"([^"]*)"\)/);
|
|
214
|
+
if (stringChangeMatch) {
|
|
215
|
+
const [, varRef, searchStr, replaceStr] = stringChangeMatch;
|
|
216
|
+
const varName = varRef.trim();
|
|
217
|
+
const sourceValue = defines[varName] ? String(defines[varName]) : varName;
|
|
218
|
+
let result = sourceValue.replace(new RegExp(searchStr, "g"), replaceStr);
|
|
219
|
+
// 处理后续的拼接,例如 StringChange(...) + ".myp"
|
|
220
|
+
const fullMatch = value.match(/StringChange\([^)]+\)(.*)$/);
|
|
221
|
+
if (fullMatch && fullMatch[1]) {
|
|
222
|
+
const suffix = fullMatch[1].trim();
|
|
223
|
+
if (suffix.startsWith("+")) {
|
|
224
|
+
const suffixPart = suffix.substring(1).trim();
|
|
225
|
+
if (suffixPart.startsWith('"') && suffixPart.endsWith('"')) {
|
|
226
|
+
result += suffixPart.slice(1, -1);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
result += suffixPart;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
// 处理常量引用 (e.g., MyAppName)
|
|
236
|
+
if (defines[value]) {
|
|
237
|
+
return String(defines[value]);
|
|
238
|
+
}
|
|
239
|
+
// 移除外层引号
|
|
240
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
241
|
+
return value.slice(1, -1);
|
|
242
|
+
}
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 替换字符串中的常量引用 {#ConstantName}
|
|
247
|
+
*/
|
|
248
|
+
static replaceDefines(text, defines) {
|
|
249
|
+
return text.replace(/\{#(\w+)\}/g, (match, varName) => {
|
|
250
|
+
return defines[varName] !== undefined ? String(defines[varName]) : match;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
140
253
|
/**
|
|
141
254
|
* 解析 Setup 段落的一行
|
|
142
255
|
*/
|
|
@@ -171,7 +284,7 @@ class InnoScriptParser {
|
|
|
171
284
|
*/
|
|
172
285
|
static parseParams(line) {
|
|
173
286
|
const params = {};
|
|
174
|
-
const parts = line.split(";").map(p => p.trim());
|
|
287
|
+
const parts = line.split(";").map((p) => p.trim());
|
|
175
288
|
for (const part of parts) {
|
|
176
289
|
const match = part.match(/^(\w+):\s*(.+)$/);
|
|
177
290
|
if (match) {
|
|
@@ -253,7 +366,9 @@ class InnoScriptParser {
|
|
|
253
366
|
Description: params.Description,
|
|
254
367
|
Types: params.Types,
|
|
255
368
|
Flags: params.Flags,
|
|
256
|
-
ExtraDiskSpaceRequired: params.ExtraDiskSpaceRequired
|
|
369
|
+
ExtraDiskSpaceRequired: params.ExtraDiskSpaceRequired
|
|
370
|
+
? parseInt(params.ExtraDiskSpaceRequired)
|
|
371
|
+
: undefined,
|
|
257
372
|
});
|
|
258
373
|
}
|
|
259
374
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Innosetup 完整的 TypeScript 类型定义
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* Defines 部分配置 - 预处理器常量定义
|
|
6
|
+
*/
|
|
7
|
+
export interface InnoSetupDefines {
|
|
8
|
+
[key: string]: string | number;
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
11
|
* Setup 部分配置
|
|
6
12
|
*/
|
|
7
13
|
export interface InnoSetupConfig {
|
|
14
|
+
/** Defines 部分配置 - 预处理器常量定义 */
|
|
15
|
+
Defines?: InnoSetupDefines;
|
|
8
16
|
/** Setup 部分配置 */
|
|
9
17
|
Setup?: {
|
|
10
18
|
/** 应用程序名称 */
|
|
@@ -604,4 +612,6 @@ export interface MakerInnosetupConfig {
|
|
|
604
612
|
createDesktopIcon?: boolean;
|
|
605
613
|
/** 是否创建快速启动图标 */
|
|
606
614
|
createQuickLaunchIcon?: boolean;
|
|
615
|
+
/** 编译超时时间(毫秒),默认 5 分钟 */
|
|
616
|
+
compileTimeout?: number;
|
|
607
617
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|