pptx2js 0.4.0 → 0.4.3
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.html +45 -37
- package/README.md +54 -34
- package/lib/{utils/bounds.js → bounds.js} +27 -4
- package/lib/chart.js +104 -30
- package/lib/codegen.js +66 -7
- package/lib/color.js +275 -0
- package/lib/convert.js +12 -8
- package/lib/extractor.js +200 -153
- package/lib/mapper.js +4 -0
- package/lib/packager.js +65 -8
- package/lib/placeholder.js +161 -29
- package/lib/presentation.js +5 -5
- package/lib/rels.js +19 -6
- package/lib/run-utils.js +181 -0
- package/lib/smartart.js +7 -15
- package/lib/table.js +33 -28
- package/lib/text-utils.js +235 -0
- package/lib/xml-parser.js +191 -15
- package/lib/xml-utils.js +82 -36
- package/package.json +4 -4
- package/lib/utils/color.js +0 -128
- package/lib/utils/emu.js +0 -20
package/lib/codegen.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ⑤ 代码生成器 — IR → PptxGenJS 脚本
|
|
3
3
|
*/
|
|
4
|
+
const { compressRunOptions } = require('./run-utils');
|
|
4
5
|
|
|
5
6
|
function generateScript(ir, options = {}) {
|
|
6
7
|
const lines = [];
|
|
@@ -80,7 +81,10 @@ function emitElement(el, slideVar, indent, options) {
|
|
|
80
81
|
if (el.type === 'chart') {
|
|
81
82
|
const dataLit = JSON.stringify(el.data);
|
|
82
83
|
const opts = [`${pos}`];
|
|
83
|
-
if (el.title)
|
|
84
|
+
if (el.title) {
|
|
85
|
+
opts.push('showTitle: true');
|
|
86
|
+
opts.push(`title: '${escapeJs(el.title)}'`);
|
|
87
|
+
}
|
|
84
88
|
return [
|
|
85
89
|
`${indent(1)}${slideVar}.addChart(pptx.charts.${el.chartType}, ${dataLit}, { ${opts.join(', ')} });`,
|
|
86
90
|
];
|
|
@@ -90,7 +94,13 @@ function emitElement(el, slideVar, indent, options) {
|
|
|
90
94
|
const shapeRef = `pptx.shapes.${el.shape}`;
|
|
91
95
|
const opts = [pos];
|
|
92
96
|
if (el.fill) opts.push(`fill: { color: '${escapeJs(el.fill)}' }`);
|
|
93
|
-
if (el.line)
|
|
97
|
+
if (el.line) {
|
|
98
|
+
const linePt = el.lineWidth ?? 1;
|
|
99
|
+
const dash = el.lineDash ? `, dashType: '${escapeJs(el.lineDash)}'` : '';
|
|
100
|
+
opts.push(`line: { color: '${escapeJs(el.line)}', width: ${linePt}${dash} }`);
|
|
101
|
+
}
|
|
102
|
+
if (el.flipH) opts.push('flipH: true');
|
|
103
|
+
if (el.flipV) opts.push('flipV: true');
|
|
94
104
|
return [`${indent(1)}${slideVar}.addShape(${shapeRef}, { ${opts.join(', ')} });`];
|
|
95
105
|
}
|
|
96
106
|
|
|
@@ -100,10 +110,13 @@ function emitElement(el, slideVar, indent, options) {
|
|
|
100
110
|
function formatTableRows(rows) {
|
|
101
111
|
const mapped = rows.map((row) =>
|
|
102
112
|
row.map((cell) => {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
const textLit = Array.isArray(cell.text)
|
|
114
|
+
? formatTextRuns(cell.text)
|
|
115
|
+
: `'${escapeJs(cell.text ?? '')}'`;
|
|
116
|
+
const parts = [`text: ${textLit}`];
|
|
117
|
+
const opts = compressTableCellOptions(cell.options ?? {});
|
|
118
|
+
if (Object.keys(opts).length) {
|
|
119
|
+
parts.push(`options: ${formatOptionsObject(opts)}`);
|
|
107
120
|
}
|
|
108
121
|
return `{ ${parts.join(', ')} }`;
|
|
109
122
|
})
|
|
@@ -111,6 +124,47 @@ function formatTableRows(rows) {
|
|
|
111
124
|
return `[\n ${mapped.map((r) => `[${r.join(', ')}]`).join(',\n ')}\n ]`;
|
|
112
125
|
}
|
|
113
126
|
|
|
127
|
+
/**
|
|
128
|
+
* @param {object} opts
|
|
129
|
+
*/
|
|
130
|
+
function compressTableCellOptions(opts) {
|
|
131
|
+
const copy = compressRunOptions({ ...opts });
|
|
132
|
+
return copy;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 生成可读的 options 字面量(避免 JSON 双引号风格)
|
|
137
|
+
* @param {object} opts
|
|
138
|
+
*/
|
|
139
|
+
function formatBorderObject(border) {
|
|
140
|
+
if (border.type === 'solid' && border.color) {
|
|
141
|
+
const parts = [`type: 'solid'`, `color: '${escapeJs(border.color)}'`];
|
|
142
|
+
if (border.pt != null) parts.push(`pt: ${border.pt}`);
|
|
143
|
+
return `{ ${parts.join(', ')} }`;
|
|
144
|
+
}
|
|
145
|
+
return JSON.stringify(border);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function formatOptionsObject(opts) {
|
|
149
|
+
const entries = [];
|
|
150
|
+
for (const [key, val] of Object.entries(opts)) {
|
|
151
|
+
if (key === 'border' && val && typeof val === 'object') {
|
|
152
|
+
entries.push(`border: ${formatBorderObject(val)}`);
|
|
153
|
+
} else if (val === true) {
|
|
154
|
+
entries.push(`${key}: true`);
|
|
155
|
+
} else if (val === false) {
|
|
156
|
+
entries.push(`${key}: false`);
|
|
157
|
+
} else if (typeof val === 'number') {
|
|
158
|
+
entries.push(`${key}: ${val}`);
|
|
159
|
+
} else if (typeof val === 'string') {
|
|
160
|
+
entries.push(`${key}: '${escapeJs(val)}'`);
|
|
161
|
+
} else {
|
|
162
|
+
entries.push(`${key}: ${JSON.stringify(val)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return `{ ${entries.join(', ')} }`;
|
|
166
|
+
}
|
|
167
|
+
|
|
114
168
|
function formatTextRuns(runs) {
|
|
115
169
|
if (!runs?.length) return "''";
|
|
116
170
|
|
|
@@ -131,7 +185,7 @@ function formatTextRuns(runs) {
|
|
|
131
185
|
}
|
|
132
186
|
|
|
133
187
|
function formatRunOptions(options) {
|
|
134
|
-
const copy = { ...options };
|
|
188
|
+
const copy = compressRunOptions({ ...options });
|
|
135
189
|
delete copy._degraded;
|
|
136
190
|
const entries = [];
|
|
137
191
|
if (copy.fontSize != null) entries.push(`fontSize: ${copy.fontSize}`);
|
|
@@ -146,6 +200,11 @@ function formatRunOptions(options) {
|
|
|
146
200
|
if (copy.hyperlink?.url) {
|
|
147
201
|
entries.push(`hyperlink: { url: '${escapeJs(copy.hyperlink.url)}' }`);
|
|
148
202
|
}
|
|
203
|
+
if (copy.underline === true) {
|
|
204
|
+
entries.push('underline: true');
|
|
205
|
+
} else if (copy.underline && typeof copy.underline === 'object') {
|
|
206
|
+
entries.push(`underline: { style: '${escapeJs(copy.underline.style ?? 'sng')}' }`);
|
|
207
|
+
}
|
|
149
208
|
if (copy.align) entries.push(`align: '${escapeJs(String(copy.align))}'`);
|
|
150
209
|
if (copy.indentLevel != null && copy.indentLevel > 0) {
|
|
151
210
|
entries.push(`indentLevel: ${copy.indentLevel}`);
|
package/lib/color.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 颜色规范化(srgbClr、schemeClr、渐变首色标)
|
|
3
|
+
*/
|
|
4
|
+
const { attr, child, children, documentRoot } = require('./xml-utils');
|
|
5
|
+
|
|
6
|
+
/** OOXML a:prstClr val → 6 位 RGB(源 PPT 主要使用 black / white) */
|
|
7
|
+
const PRST_COLOR_MAP = {
|
|
8
|
+
black: '000000',
|
|
9
|
+
white: 'FFFFFF',
|
|
10
|
+
red: 'FF0000',
|
|
11
|
+
green: '00FF00',
|
|
12
|
+
blue: '0000FF',
|
|
13
|
+
yellow: 'FFFF00',
|
|
14
|
+
gray: '808080',
|
|
15
|
+
grey: '808080',
|
|
16
|
+
silver: 'C0C0C0',
|
|
17
|
+
navy: '000080',
|
|
18
|
+
teal: '008080',
|
|
19
|
+
lime: '00FF00',
|
|
20
|
+
olive: '808000',
|
|
21
|
+
maroon: '800000',
|
|
22
|
+
aqua: '00FFFF',
|
|
23
|
+
fuchsia: 'FF00FF',
|
|
24
|
+
orange: 'FFA500',
|
|
25
|
+
purple: '800080',
|
|
26
|
+
brown: 'A52A2A',
|
|
27
|
+
pink: 'FFC0CB',
|
|
28
|
+
dkGreen: '006400',
|
|
29
|
+
dkBlue: '00008B',
|
|
30
|
+
dkRed: '8B0000',
|
|
31
|
+
dkGray: 'A9A9A9',
|
|
32
|
+
dkGrey: 'A9A9A9',
|
|
33
|
+
ltGray: 'D3D3D3',
|
|
34
|
+
ltGrey: 'D3D3D3',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** @type {Record<string, string>} */
|
|
38
|
+
const DEFAULT_SCHEME = {
|
|
39
|
+
dk1: '000000',
|
|
40
|
+
lt1: 'FFFFFF',
|
|
41
|
+
dk2: '44546A',
|
|
42
|
+
lt2: 'E7E6E6',
|
|
43
|
+
accent1: '4472C4',
|
|
44
|
+
accent2: 'ED7D31',
|
|
45
|
+
accent3: 'A5A5A5',
|
|
46
|
+
accent4: 'FFC000',
|
|
47
|
+
accent5: '5B9BD5',
|
|
48
|
+
accent6: '70AD47',
|
|
49
|
+
hlink: '0563C1',
|
|
50
|
+
folHlink: '954F72',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {Record<string, object>|null} parsed
|
|
55
|
+
* @param {string|null} themePath
|
|
56
|
+
* @returns {Record<string, string>}
|
|
57
|
+
*/
|
|
58
|
+
function loadColorScheme(parsed, themePath) {
|
|
59
|
+
if (!themePath || !parsed[themePath]) return { ...DEFAULT_SCHEME };
|
|
60
|
+
|
|
61
|
+
const theme = documentRoot(parsed[themePath], 'a:theme');
|
|
62
|
+
const clrScheme = child(child(theme, 'a:themeElements'), 'a:clrScheme');
|
|
63
|
+
if (!clrScheme) return { ...DEFAULT_SCHEME };
|
|
64
|
+
|
|
65
|
+
const scheme = { ...DEFAULT_SCHEME };
|
|
66
|
+
for (const name of Object.keys(DEFAULT_SCHEME)) {
|
|
67
|
+
const slot = child(clrScheme, `a:${name}`);
|
|
68
|
+
const rgb = extractRgbFromColorNode(slot);
|
|
69
|
+
if (rgb) scheme[name] = rgb;
|
|
70
|
+
}
|
|
71
|
+
return scheme;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {object|null|undefined} colorNode a:srgbClr / a:schemeClr 的父级
|
|
76
|
+
* @returns {string|null}
|
|
77
|
+
*/
|
|
78
|
+
function extractRgbFromColorNode(colorNode) {
|
|
79
|
+
if (!colorNode) return null;
|
|
80
|
+
|
|
81
|
+
const srgb = child(colorNode, 'a:srgbClr');
|
|
82
|
+
if (srgb) {
|
|
83
|
+
const val = attr(srgb, 'val');
|
|
84
|
+
return val ? val.toUpperCase() : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sys = child(colorNode, 'a:sysClr');
|
|
88
|
+
if (sys) {
|
|
89
|
+
const last = attr(sys, 'lastClr');
|
|
90
|
+
return last ? applyColorModifiers(last.toUpperCase(), sys) : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {object|null|undefined} fillNode a:solidFill | a:gradFill 等
|
|
98
|
+
* @param {Record<string, string>} scheme
|
|
99
|
+
* @returns {{ color: string|null, degraded: boolean }}
|
|
100
|
+
*/
|
|
101
|
+
function resolveFillColor(fillNode, scheme) {
|
|
102
|
+
if (!fillNode) return { color: null, degraded: false };
|
|
103
|
+
|
|
104
|
+
const solid =
|
|
105
|
+
fillNode.tag === 'a:solidFill' ? fillNode : child(fillNode, 'a:solidFill');
|
|
106
|
+
if (solid) {
|
|
107
|
+
return { color: resolveColorFromContainer(solid, scheme), degraded: false };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const grad =
|
|
111
|
+
fillNode.tag === 'a:gradFill' ? fillNode : child(fillNode, 'a:gradFill');
|
|
112
|
+
if (grad) {
|
|
113
|
+
const gs = firstGradientStop(grad);
|
|
114
|
+
return {
|
|
115
|
+
color: gs ? resolveColorFromContainer(gs, scheme) : null,
|
|
116
|
+
degraded: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const ptn =
|
|
121
|
+
fillNode.tag === 'a:ptnFill' ? fillNode : child(fillNode, 'a:ptnFill');
|
|
122
|
+
if (ptn) {
|
|
123
|
+
const fgClr = child(ptn, 'a:fgClr');
|
|
124
|
+
return {
|
|
125
|
+
color: fgClr ? resolveColorFromContainer(fgClr, scheme) : null,
|
|
126
|
+
degraded: true,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { color: null, degraded: false };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 渐变色标按 pos 升序,取最小 pos 对应色标(OOXML 0–100000)
|
|
135
|
+
* @param {object} grad a:gradFill
|
|
136
|
+
* @returns {object|null}
|
|
137
|
+
*/
|
|
138
|
+
function firstGradientStop(grad) {
|
|
139
|
+
const gsLst = child(grad, 'a:gsLst');
|
|
140
|
+
if (!gsLst) return null;
|
|
141
|
+
const stops = children(gsLst, 'a:gs');
|
|
142
|
+
if (!stops.length) return null;
|
|
143
|
+
return [...stops].sort(
|
|
144
|
+
(a, b) => parseInt(attr(a, 'pos') ?? '0', 10) - parseInt(attr(b, 'pos') ?? '0', 10)
|
|
145
|
+
)[0];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {object|null|undefined} container 含 a:srgbClr / a:schemeClr
|
|
150
|
+
* @param {Record<string, string>} scheme
|
|
151
|
+
* @returns {string|null}
|
|
152
|
+
*/
|
|
153
|
+
function resolveColorFromContainer(container, scheme) {
|
|
154
|
+
if (!container) return null;
|
|
155
|
+
|
|
156
|
+
const srgb = child(container, 'a:srgbClr');
|
|
157
|
+
if (srgb) {
|
|
158
|
+
const val = attr(srgb, 'val');
|
|
159
|
+
if (!val) return null;
|
|
160
|
+
return applyColorModifiers(val.toUpperCase(), srgb);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const schemeClr = child(container, 'a:schemeClr');
|
|
164
|
+
if (schemeClr) {
|
|
165
|
+
const name = attr(schemeClr, 'val');
|
|
166
|
+
const base = name ? scheme[name] ?? DEFAULT_SCHEME[name] ?? null : null;
|
|
167
|
+
return base ? applyColorModifiers(base, schemeClr) : null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const prstClr = child(container, 'a:prstClr');
|
|
171
|
+
if (prstClr) {
|
|
172
|
+
const val = attr(prstClr, 'val');
|
|
173
|
+
const base = val ? PRST_COLOR_MAP[val] ?? PRST_COLOR_MAP[val.toLowerCase()] ?? '000000' : null;
|
|
174
|
+
return base ? applyColorModifiers(base, prstClr) : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const sysClr = child(container, 'a:sysClr');
|
|
178
|
+
if (sysClr) {
|
|
179
|
+
const last = attr(sysClr, 'lastClr');
|
|
180
|
+
return last ? applyColorModifiers(last.toUpperCase(), sysClr) : null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return extractRgbFromColorNode(container);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} hex 6 位 RGB
|
|
188
|
+
*/
|
|
189
|
+
function hexToRgb(hex) {
|
|
190
|
+
const h = String(hex).replace(/^#/, '');
|
|
191
|
+
if (h.length !== 6) return { r: 0, g: 0, b: 0 };
|
|
192
|
+
return {
|
|
193
|
+
r: parseInt(h.slice(0, 2), 16),
|
|
194
|
+
g: parseInt(h.slice(2, 4), 16),
|
|
195
|
+
b: parseInt(h.slice(4, 6), 16),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @param {{ r: number, g: number, b: number }} rgb
|
|
201
|
+
*/
|
|
202
|
+
function rgbToHex(rgb) {
|
|
203
|
+
const clamp = (n) => Math.max(0, Math.min(255, Math.round(n)));
|
|
204
|
+
return [rgb.r, rgb.g, rgb.b]
|
|
205
|
+
.map((c) => clamp(c).toString(16).padStart(2, '0'))
|
|
206
|
+
.join('')
|
|
207
|
+
.toUpperCase();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* srgbClr / schemeClr 上的 lumMod / shade / tint 等修饰(OOXML 比例 1/100000)
|
|
212
|
+
* @param {string} hex
|
|
213
|
+
* @param {object} clrNode
|
|
214
|
+
*/
|
|
215
|
+
function applyColorModifiers(hex, clrNode) {
|
|
216
|
+
let { r, g, b } = hexToRgb(hex);
|
|
217
|
+
|
|
218
|
+
const lumMod = child(clrNode, 'a:lumMod');
|
|
219
|
+
if (lumMod) {
|
|
220
|
+
const pct = parseInt(attr(lumMod, 'val') ?? '100000', 10) / 100000;
|
|
221
|
+
r *= pct;
|
|
222
|
+
g *= pct;
|
|
223
|
+
b *= pct;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const lumOff = child(clrNode, 'a:lumOff');
|
|
227
|
+
if (lumOff) {
|
|
228
|
+
const pct = parseInt(attr(lumOff, 'val') ?? '0', 10) / 100000;
|
|
229
|
+
const delta = 255 * pct;
|
|
230
|
+
r += delta;
|
|
231
|
+
g += delta;
|
|
232
|
+
b += delta;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const shade = child(clrNode, 'a:shade');
|
|
236
|
+
if (shade) {
|
|
237
|
+
const pct = parseInt(attr(shade, 'val') ?? '0', 10) / 100000;
|
|
238
|
+
r *= pct;
|
|
239
|
+
g *= pct;
|
|
240
|
+
b *= pct;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const tint = child(clrNode, 'a:tint');
|
|
244
|
+
if (tint) {
|
|
245
|
+
const pct = parseInt(attr(tint, 'val') ?? '0', 10) / 100000;
|
|
246
|
+
r += (255 - r) * pct;
|
|
247
|
+
g += (255 - g) * pct;
|
|
248
|
+
b += (255 - b) * pct;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return rgbToHex({ r, g, b });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** @deprecated 使用 applyColorModifiers */
|
|
255
|
+
const applySchemeClrModifiers = applyColorModifiers;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @param {Record<string, string>} scheme
|
|
259
|
+
* @param {object|null|undefined} clrNode
|
|
260
|
+
* @returns {string|null}
|
|
261
|
+
*/
|
|
262
|
+
function resolveColor(scheme, clrNode) {
|
|
263
|
+
return resolveColorFromContainer(clrNode, scheme);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
DEFAULT_SCHEME,
|
|
268
|
+
loadColorScheme,
|
|
269
|
+
resolveFillColor,
|
|
270
|
+
resolveColor,
|
|
271
|
+
resolveColorFromContainer,
|
|
272
|
+
firstGradientStop,
|
|
273
|
+
applyColorModifiers,
|
|
274
|
+
applySchemeClrModifiers,
|
|
275
|
+
};
|
package/lib/convert.js
CHANGED
|
@@ -46,8 +46,11 @@ async function convert(filePath, options = {}) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const stat = fs.statSync(resolvedPath);
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const maxSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
50
|
+
if (stat.size > maxSize) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`源文件超过大小限制 (${stat.size} > ${maxSize} 字节)。请拆分 PPT 或使用 --max-file-size 提高上限。`
|
|
53
|
+
);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
const outputDir = path.resolve(options.outputDir ?? DEFAULT_OUTPUT);
|
|
@@ -59,8 +62,8 @@ async function convert(filePath, options = {}) {
|
|
|
59
62
|
const parsed = {};
|
|
60
63
|
for (const [entryPath, content] of archive.files) {
|
|
61
64
|
if (content === '__binary__') continue;
|
|
62
|
-
if (
|
|
63
|
-
parsed[entryPath] =
|
|
65
|
+
if (/\.(xml|rels)$/i.test(entryPath)) {
|
|
66
|
+
parsed[entryPath] = parseXml(content);
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -68,15 +71,16 @@ async function convert(filePath, options = {}) {
|
|
|
68
71
|
const ctx = { relIndex, parsed, sourcePath: resolvedPath };
|
|
69
72
|
const entities = extractEntities(ctx);
|
|
70
73
|
const ir = mapToIR(entities, ctx);
|
|
71
|
-
const script = generateScript(ir, options);
|
|
72
|
-
|
|
73
|
-
const scriptPath = path.join(outputDir, 'output.js');
|
|
74
|
-
fs.writeFileSync(scriptPath, script, 'utf8');
|
|
75
74
|
|
|
75
|
+
// packageMedia 会就地更新 ir 中的 mediaPath,generateScript 必须在其之后调用
|
|
76
76
|
await packageMedia({ archive, ir }, outputDir, {
|
|
77
77
|
noMedia: options.noMedia,
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
const script = generateScript(ir, options);
|
|
81
|
+
const scriptPath = path.join(outputDir, 'output.js');
|
|
82
|
+
fs.writeFileSync(scriptPath, script, 'utf8');
|
|
83
|
+
|
|
80
84
|
const log = buildConversionLog(resolvedPath, stat, entities, ir);
|
|
81
85
|
writeConversionLog(outputDir, log);
|
|
82
86
|
writeOutputReadme(outputDir, {
|