@keymanapp/kmc-package 17.0.300-beta → 17.0.302-beta
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/build/src/compiler/cp1252.js +289 -288
- package/build/src/compiler/cp1252.js.map +1 -1
- package/build/src/compiler/kmp-compiler.js +494 -493
- package/build/src/compiler/kmp-compiler.js.map +1 -1
- package/build/src/compiler/kmp-inf-writer.js +145 -144
- package/build/src/compiler/kmp-inf-writer.js.map +1 -1
- package/build/src/compiler/kmx-keyboard-metadata.js +19 -18
- package/build/src/compiler/kmx-keyboard-metadata.js.map +1 -1
- package/build/src/compiler/markdown.js +49 -48
- package/build/src/compiler/markdown.js.map +1 -1
- package/build/src/compiler/package-compiler-messages.js +79 -78
- package/build/src/compiler/package-compiler-messages.js.map +1 -1
- package/build/src/compiler/package-keyboard-target-validator.js +45 -44
- package/build/src/compiler/package-keyboard-target-validator.js.map +1 -1
- package/build/src/compiler/package-metadata-collector.js +78 -77
- package/build/src/compiler/package-metadata-collector.js.map +1 -1
- package/build/src/compiler/package-metadata-updater.js +13 -12
- package/build/src/compiler/package-metadata-updater.js.map +1 -1
- package/build/src/compiler/package-validation.js +180 -179
- package/build/src/compiler/package-validation.js.map +1 -1
- package/build/src/compiler/package-version-validator.js +92 -91
- package/build/src/compiler/package-version-validator.js.map +1 -1
- package/build/src/compiler/redist-files.js +59 -58
- package/build/src/compiler/redist-files.js.map +1 -1
- package/build/src/compiler/web-keyboard-metadata.js +37 -36
- package/build/src/compiler/web-keyboard-metadata.js.map +1 -1
- package/build/src/compiler/windows-package-installer-compiler.js +180 -179
- package/build/src/compiler/windows-package-installer-compiler.js.map +1 -1
- package/build/src/main.js +7 -6
- package/build/src/main.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,494 +1,495 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
;
|
|
22
|
-
;
|
|
23
|
-
;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
let
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
kmp.options.
|
|
182
|
-
kmp.options.
|
|
183
|
-
kmp.options.
|
|
184
|
-
kmp.options.
|
|
185
|
-
kmp.options.
|
|
186
|
-
kmp.options.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
kmp.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
targetValidator.
|
|
293
|
-
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
updater
|
|
298
|
-
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
['
|
|
338
|
-
['
|
|
339
|
-
['
|
|
340
|
-
['
|
|
341
|
-
['
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* @param
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
data[
|
|
488
|
-
data[
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b6fb6e95-5ebd-510f-bb92-192fc2ad2371")}catch(e){}}();
|
|
3
|
+
import * as xml2js from 'xml2js';
|
|
4
|
+
import JSZip from 'jszip';
|
|
5
|
+
import KEYMAN_VERSION from "@keymanapp/keyman-version";
|
|
6
|
+
import { SchemaValidators, KeymanFileTypes, KvkFile } from '@keymanapp/common-types';
|
|
7
|
+
import { CompilerMessages } from './package-compiler-messages.js';
|
|
8
|
+
import { PackageMetadataCollector } from './package-metadata-collector.js';
|
|
9
|
+
import { KmpInfWriter } from './kmp-inf-writer.js';
|
|
10
|
+
import { transcodeToCP1252 } from './cp1252.js';
|
|
11
|
+
import { MIN_LM_FILEVERSION_KMP_JSON, PackageVersionValidator } from './package-version-validator.js';
|
|
12
|
+
import { PackageKeyboardTargetValidator } from './package-keyboard-target-validator.js';
|
|
13
|
+
import { PackageMetadataUpdater } from './package-metadata-updater.js';
|
|
14
|
+
import { markdownToHTML } from './markdown.js';
|
|
15
|
+
import { PackageValidation } from './package-validation.js';
|
|
16
|
+
const KMP_JSON_FILENAME = 'kmp.json';
|
|
17
|
+
const KMP_INF_FILENAME = 'kmp.inf';
|
|
18
|
+
// welcome.htm: this is a legacy filename, as of 17.0 the welcome
|
|
19
|
+
// (documentation) filename can be any file, but we will fallback to detecting
|
|
20
|
+
// this filename for existing keyboard packages.
|
|
21
|
+
const WELCOME_HTM_FILENAME = 'welcome.htm';
|
|
22
|
+
;
|
|
23
|
+
;
|
|
24
|
+
;
|
|
25
|
+
/**
|
|
26
|
+
* @public
|
|
27
|
+
* Compiles a .kps file to a .kmp archive. The compiler does not read or write
|
|
28
|
+
* from filesystem or network directly, but relies on callbacks for all external
|
|
29
|
+
* IO.
|
|
30
|
+
*/
|
|
31
|
+
export class KmpCompiler {
|
|
32
|
+
callbacks;
|
|
33
|
+
options;
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the compiler.
|
|
36
|
+
* Copies options.
|
|
37
|
+
* @param callbacks - Callbacks for external interfaces, including message
|
|
38
|
+
* reporting and file io
|
|
39
|
+
* @param options - Compiler options
|
|
40
|
+
* @returns false if initialization fails
|
|
41
|
+
*/
|
|
42
|
+
async init(callbacks, options) {
|
|
43
|
+
this.callbacks = callbacks;
|
|
44
|
+
this.options = options ? { ...options } : {};
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compiles a .kps file to .kmp file. Returns an object containing binary
|
|
49
|
+
* artifacts on success. The files are passed in by name, and the compiler
|
|
50
|
+
* will use callbacks as passed to the {@link KmpCompiler.init} function
|
|
51
|
+
* to read any input files by disk.
|
|
52
|
+
* @param infile - Path to source file. Path will be parsed to find relative
|
|
53
|
+
* references in the .kmn file, such as icon or On Screen
|
|
54
|
+
* Keyboard file
|
|
55
|
+
* @param outfile - Path to output file. The file will not be written to, but
|
|
56
|
+
* will be included in the result for use by
|
|
57
|
+
* {@link KmpCompiler.write}.
|
|
58
|
+
* @returns Binary artifacts on success, null on failure.
|
|
59
|
+
*/
|
|
60
|
+
async run(inputFilename, outputFilename) {
|
|
61
|
+
const kmpJsonData = this.transformKpsToKmpObject(inputFilename);
|
|
62
|
+
if (!kmpJsonData) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
//
|
|
66
|
+
// Validate the package file
|
|
67
|
+
//
|
|
68
|
+
const validation = new PackageValidation(this.callbacks, this.options);
|
|
69
|
+
if (!validation.validate(inputFilename, kmpJsonData)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
//
|
|
73
|
+
// Build the .kmp package file
|
|
74
|
+
//
|
|
75
|
+
const data = await this.buildKmpFile(inputFilename, kmpJsonData);
|
|
76
|
+
if (!data) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const result = {
|
|
80
|
+
artifacts: {
|
|
81
|
+
kmp: {
|
|
82
|
+
data,
|
|
83
|
+
filename: outputFilename ?? inputFilename.replace(/\.kps$/, '.kmp')
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Write artifacts from a successful compile to disk, via callbacks methods.
|
|
91
|
+
* The artifacts written may include:
|
|
92
|
+
*
|
|
93
|
+
* - .kmp file - binary keyboard package used by Keyman on desktop and touch
|
|
94
|
+
* platforms
|
|
95
|
+
*
|
|
96
|
+
* @param artifacts - object containing artifact binary data to write out
|
|
97
|
+
* @returns true on success
|
|
98
|
+
*/
|
|
99
|
+
async write(artifacts) {
|
|
100
|
+
this.callbacks.fs.writeFileSync(artifacts.kmp.filename, artifacts.kmp.data);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
transformKpsToKmpObject(kpsFilename) {
|
|
107
|
+
const kps = this.loadKpsFile(kpsFilename);
|
|
108
|
+
if (!kps) {
|
|
109
|
+
// errors will already have been reported by loadKpsFile
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const kmp = this.transformKpsFileToKmpObject(kpsFilename, kps);
|
|
113
|
+
if (!kmp) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
// Verify that the generated kmp.json validates with the kmp.json schema
|
|
117
|
+
if (!SchemaValidators.default.kmp(kmp)) {
|
|
118
|
+
// This is an internal error, so throwing an exception is appropriate
|
|
119
|
+
throw new Error(JSON.stringify(SchemaValidators.default.kmp.errors));
|
|
120
|
+
}
|
|
121
|
+
return kmp;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
loadKpsFile(kpsFilename) {
|
|
127
|
+
// Load the KPS data from XML as JS structured data.
|
|
128
|
+
const buffer = this.callbacks.loadFile(kpsFilename);
|
|
129
|
+
if (!buffer) {
|
|
130
|
+
this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({ filename: kpsFilename }));
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const data = new TextDecoder().decode(buffer);
|
|
134
|
+
const kpsPackage = (() => {
|
|
135
|
+
let a;
|
|
136
|
+
let parser = new xml2js.Parser({
|
|
137
|
+
explicitArray: false
|
|
138
|
+
});
|
|
139
|
+
try {
|
|
140
|
+
parser.parseString(data, (e, r) => { if (e)
|
|
141
|
+
throw e; a = r; });
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
this.callbacks.reportMessage(CompilerMessages.Error_InvalidPackageFile({ e }));
|
|
145
|
+
}
|
|
146
|
+
return a;
|
|
147
|
+
})();
|
|
148
|
+
if (!kpsPackage) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const kps = kpsPackage.Package;
|
|
152
|
+
return kps;
|
|
153
|
+
}
|
|
154
|
+
normalizePath = (path) => path || path === '' ? path.trim().replaceAll('\\', '/') : undefined;
|
|
155
|
+
/**
|
|
156
|
+
* @internal
|
|
157
|
+
*/
|
|
158
|
+
transformKpsFileToKmpObject(kpsFilename, kps) {
|
|
159
|
+
//
|
|
160
|
+
// To convert to kmp.json, we need to:
|
|
161
|
+
//
|
|
162
|
+
// 1. Unwrap arrays (and convert to array where single object)
|
|
163
|
+
// 2. Fix casing on `iD`
|
|
164
|
+
// 3. Rewrap info, keyboard.languages, lexicalModel.languages, startMenu.items elements
|
|
165
|
+
// 4. Remove options.followKeyboardVersion, file.fileType
|
|
166
|
+
// 5. Convert file.copyLocation to a Number
|
|
167
|
+
// 6. Filenames need to be basenames (but this comes after processing)
|
|
168
|
+
//
|
|
169
|
+
// Start to construct the kmp.json file from the .kps file
|
|
170
|
+
let kmp = {
|
|
171
|
+
system: {
|
|
172
|
+
fileVersion: null,
|
|
173
|
+
keymanDeveloperVersion: KEYMAN_VERSION.VERSION
|
|
174
|
+
},
|
|
175
|
+
options: {}
|
|
176
|
+
};
|
|
177
|
+
//
|
|
178
|
+
// Fill in additional fields
|
|
179
|
+
//
|
|
180
|
+
if (kps.Options) {
|
|
181
|
+
kmp.options.executeProgram = this.normalizePath(kps.Options.ExecuteProgram || undefined);
|
|
182
|
+
kmp.options.graphicFile = this.normalizePath(kps.Options.GraphicFile || undefined);
|
|
183
|
+
kmp.options.msiFilename = this.normalizePath(kps.Options.MSIFileName || undefined);
|
|
184
|
+
kmp.options.msiOptions = kps.Options.MSIOptions || undefined;
|
|
185
|
+
kmp.options.readmeFile = this.normalizePath(kps.Options.ReadMeFile || undefined);
|
|
186
|
+
kmp.options.licenseFile = this.normalizePath(kps.Options.LicenseFile || undefined);
|
|
187
|
+
kmp.options.welcomeFile = this.normalizePath(kps.Options.WelcomeFile || undefined);
|
|
188
|
+
}
|
|
189
|
+
//
|
|
190
|
+
// Add basic metadata
|
|
191
|
+
//
|
|
192
|
+
if (kps.Info) {
|
|
193
|
+
kmp.info = this.kpsInfoToKmpInfo(kps.Info);
|
|
194
|
+
}
|
|
195
|
+
//
|
|
196
|
+
// Add related package metadata
|
|
197
|
+
//
|
|
198
|
+
if (kps.RelatedPackages) {
|
|
199
|
+
// Note: 'relationship' field is required for kmp.json but optional for .kps, only
|
|
200
|
+
// two values are supported -- deprecates or related.
|
|
201
|
+
kmp.relatedPackages = this.arrayWrap(kps.RelatedPackages.RelatedPackage).map(p => ({ id: p.$.ID, relationship: p.$.Relationship == 'deprecates' ? 'deprecates' : 'related' }));
|
|
202
|
+
}
|
|
203
|
+
//
|
|
204
|
+
// Add file metadata
|
|
205
|
+
//
|
|
206
|
+
if (kps.Files && kps.Files.File) {
|
|
207
|
+
kmp.files = this.arrayWrap(kps.Files.File).map((file) => {
|
|
208
|
+
return {
|
|
209
|
+
name: this.normalizePath(file.Name),
|
|
210
|
+
description: file.Description.trim(),
|
|
211
|
+
copyLocation: parseInt(file.CopyLocation, 10) || undefined
|
|
212
|
+
// note: we don't emit fileType as that is not permitted in kmp.json
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
kmp.files = kmp.files ?? [];
|
|
217
|
+
// Keyboard packages also include a legacy kmp.inf file (this will be removed,
|
|
218
|
+
// one day)
|
|
219
|
+
if (kps.Keyboards && kps.Keyboards.Keyboard) {
|
|
220
|
+
kmp.files.push({
|
|
221
|
+
name: KMP_INF_FILENAME,
|
|
222
|
+
description: "Package information"
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
// Add the standard kmp.json self-referential to match existing implementations
|
|
226
|
+
kmp.files.push({
|
|
227
|
+
name: KMP_JSON_FILENAME,
|
|
228
|
+
description: "Package information (JSON)"
|
|
229
|
+
});
|
|
230
|
+
//
|
|
231
|
+
// Add keyboard metadata
|
|
232
|
+
//
|
|
233
|
+
if (kps.Keyboards && kps.Keyboards.Keyboard) {
|
|
234
|
+
kmp.keyboards = this.arrayWrap(kps.Keyboards.Keyboard).map((keyboard) => ({
|
|
235
|
+
displayFont: keyboard.DisplayFont ? this.callbacks.path.basename(this.normalizePath(keyboard.DisplayFont)) : undefined,
|
|
236
|
+
oskFont: keyboard.OSKFont ? this.callbacks.path.basename(this.normalizePath(keyboard.OSKFont)) : undefined,
|
|
237
|
+
name: keyboard.Name.trim(),
|
|
238
|
+
id: keyboard.ID.trim(),
|
|
239
|
+
version: keyboard.Version.trim(),
|
|
240
|
+
rtl: keyboard.RTL == 'True' ? true : undefined,
|
|
241
|
+
languages: keyboard.Languages ?
|
|
242
|
+
this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.Languages.Language)) :
|
|
243
|
+
[],
|
|
244
|
+
examples: keyboard.Examples ?
|
|
245
|
+
this.arrayWrap(keyboard.Examples.Example).map(e => ({ id: e.$.ID, keys: e.$.Keys, text: e.$.Text, note: e.$.Note })) :
|
|
246
|
+
undefined,
|
|
247
|
+
webDisplayFonts: keyboard.WebDisplayFonts ?
|
|
248
|
+
this.arrayWrap(keyboard.WebDisplayFonts.Font).map(e => (this.callbacks.path.basename(this.normalizePath(e.$.Filename)))) :
|
|
249
|
+
undefined,
|
|
250
|
+
webOskFonts: keyboard.WebOSKFonts ?
|
|
251
|
+
this.arrayWrap(keyboard.WebOSKFonts.Font).map(e => (this.callbacks.path.basename(this.normalizePath(e.$.Filename)))) :
|
|
252
|
+
undefined,
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
//
|
|
256
|
+
// Add lexical-model metadata
|
|
257
|
+
//
|
|
258
|
+
if (kps.LexicalModels && kps.LexicalModels.LexicalModel) {
|
|
259
|
+
kmp.lexicalModels = this.arrayWrap(kps.LexicalModels.LexicalModel).map((model) => ({
|
|
260
|
+
name: model.Name.trim(),
|
|
261
|
+
id: model.ID.trim(),
|
|
262
|
+
languages: model.Languages ?
|
|
263
|
+
this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.Languages.Language)) : []
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
//
|
|
267
|
+
// Collect metadata from keyboards (and later models) in order to update
|
|
268
|
+
// the kmp.json metadata for use downstream in apps. This will also be
|
|
269
|
+
// used later to fill in .keyboard_info file data.
|
|
270
|
+
//
|
|
271
|
+
const collector = new PackageMetadataCollector(this.callbacks);
|
|
272
|
+
const metadata = collector.collectKeyboardMetadata(kpsFilename, kmp);
|
|
273
|
+
if (metadata == null) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
//
|
|
277
|
+
// Verify keyboard versions and update version metadata where appropriate
|
|
278
|
+
//
|
|
279
|
+
const versionValidator = new PackageVersionValidator(this.callbacks);
|
|
280
|
+
if (!versionValidator.validateAndUpdateVersions(kps, kmp, metadata)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
if (kps.Keyboards && kps.Keyboards.Keyboard) {
|
|
284
|
+
kmp.system.fileVersion = versionValidator.getMinKeymanVersion(metadata);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
kmp.system.fileVersion = MIN_LM_FILEVERSION_KMP_JSON;
|
|
288
|
+
}
|
|
289
|
+
//
|
|
290
|
+
// Verify that packages that target mobile devices include a .js file
|
|
291
|
+
//
|
|
292
|
+
const targetValidator = new PackageKeyboardTargetValidator(this.callbacks);
|
|
293
|
+
targetValidator.verifyAllTargets(kmp, metadata);
|
|
294
|
+
//
|
|
295
|
+
// Update assorted keyboard metadata from the keyboards in the package
|
|
296
|
+
//
|
|
297
|
+
const updater = new PackageMetadataUpdater();
|
|
298
|
+
updater.updatePackage(metadata);
|
|
299
|
+
//
|
|
300
|
+
// Add Windows Start Menu metadata
|
|
301
|
+
//
|
|
302
|
+
if (kps.StartMenu && (kps.StartMenu.Folder || kps.StartMenu.Items)) {
|
|
303
|
+
kmp.startMenu = {};
|
|
304
|
+
if (kps.StartMenu.AddUninstallEntry === '')
|
|
305
|
+
kmp.startMenu.addUninstallEntry = true;
|
|
306
|
+
if (kps.StartMenu.Folder)
|
|
307
|
+
kmp.startMenu.folder = kps.StartMenu.Folder;
|
|
308
|
+
if (kps.StartMenu.Items && kps.StartMenu.Items.Item) {
|
|
309
|
+
kmp.startMenu.items = this.arrayWrap(kps.StartMenu.Items.Item).map((item) => ({
|
|
310
|
+
filename: item.FileName,
|
|
311
|
+
name: item.Name,
|
|
312
|
+
arguments: item.Arguments,
|
|
313
|
+
icon: item.Icon,
|
|
314
|
+
location: item.Location
|
|
315
|
+
}));
|
|
316
|
+
// Remove default values
|
|
317
|
+
for (let item of kmp.startMenu.items) {
|
|
318
|
+
if (item.icon == '')
|
|
319
|
+
delete item.icon;
|
|
320
|
+
if (item.location == 'psmelStartMenu')
|
|
321
|
+
delete item.location;
|
|
322
|
+
if (item.arguments == '')
|
|
323
|
+
delete item.arguments;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
kmp.startMenu.items = [];
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
kmp = this.stripUndefined(kmp);
|
|
331
|
+
return kmp;
|
|
332
|
+
}
|
|
333
|
+
// Helper functions
|
|
334
|
+
kpsInfoToKmpInfo(kpsInfo) {
|
|
335
|
+
let kmpInfo = {};
|
|
336
|
+
const keys = [
|
|
337
|
+
['Author', 'author', false],
|
|
338
|
+
['Copyright', 'copyright', false],
|
|
339
|
+
['Name', 'name', false],
|
|
340
|
+
['Version', 'version', false],
|
|
341
|
+
['WebSite', 'website', false],
|
|
342
|
+
['Description', 'description', true],
|
|
343
|
+
];
|
|
344
|
+
for (let [src, dst, isMarkdown] of keys) {
|
|
345
|
+
if (kpsInfo[src]) {
|
|
346
|
+
kmpInfo[dst] = {
|
|
347
|
+
description: (kpsInfo[src]._ ?? (typeof kpsInfo[src] == 'string' ? kpsInfo[src].toString() : '')).trim()
|
|
348
|
+
};
|
|
349
|
+
if (isMarkdown) {
|
|
350
|
+
kmpInfo[dst].description = markdownToHTML(kmpInfo[dst].description, false).trim();
|
|
351
|
+
}
|
|
352
|
+
if (kpsInfo[src].$?.URL) {
|
|
353
|
+
kmpInfo[dst].url = kpsInfo[src].$.URL.trim();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return kmpInfo;
|
|
358
|
+
}
|
|
359
|
+
;
|
|
360
|
+
arrayWrap(a) {
|
|
361
|
+
if (Array.isArray(a)) {
|
|
362
|
+
return a;
|
|
363
|
+
}
|
|
364
|
+
return [a];
|
|
365
|
+
}
|
|
366
|
+
;
|
|
367
|
+
kpsLanguagesToKmpLanguages(language) {
|
|
368
|
+
if (language.length == 0 || language[0] == undefined) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
return language.map((element) => { return { name: element._, id: element.$.ID }; });
|
|
372
|
+
}
|
|
373
|
+
;
|
|
374
|
+
stripUndefined(o) {
|
|
375
|
+
for (const key in o) {
|
|
376
|
+
if (o[key] === undefined) {
|
|
377
|
+
delete o[key];
|
|
378
|
+
}
|
|
379
|
+
else if (typeof o[key] == 'object') {
|
|
380
|
+
o[key] = this.stripUndefined(o[key]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return o;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* @internal
|
|
387
|
+
* Returns a Promise to the serialized data which can then be written to a .kmp file.
|
|
388
|
+
*
|
|
389
|
+
* @param kpsFilename - Filename of the kps, not read, used only for calculating relative paths
|
|
390
|
+
* @param kmpJsonData - The kmp.json Object
|
|
391
|
+
*/
|
|
392
|
+
buildKmpFile(kpsFilename, kmpJsonData) {
|
|
393
|
+
const zip = JSZip();
|
|
394
|
+
// Make a copy of kmpJsonData, as we mutate paths for writing
|
|
395
|
+
const data = JSON.parse(JSON.stringify(kmpJsonData));
|
|
396
|
+
if (!data.files) {
|
|
397
|
+
data.files = [];
|
|
398
|
+
}
|
|
399
|
+
const hasKmpInf = !!data.files.find(file => file.name == KMP_INF_FILENAME);
|
|
400
|
+
let failed = false;
|
|
401
|
+
data.files.forEach((value) => {
|
|
402
|
+
// Get the path of the file
|
|
403
|
+
let filename = value.name;
|
|
404
|
+
// We add this separately after zipping all other files
|
|
405
|
+
if (filename == KMP_JSON_FILENAME || filename == KMP_INF_FILENAME) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (this.callbacks.path.isAbsolute(filename)) {
|
|
409
|
+
// absolute paths are not portable to other computers
|
|
410
|
+
this.callbacks.reportMessage(CompilerMessages.Warn_AbsolutePath({ filename: filename }));
|
|
411
|
+
}
|
|
412
|
+
filename = this.callbacks.resolveFilename(kpsFilename, filename);
|
|
413
|
+
const basename = this.callbacks.path.basename(filename);
|
|
414
|
+
if (!this.callbacks.fs.existsSync(filename)) {
|
|
415
|
+
this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({ filename: filename }));
|
|
416
|
+
failed = true;
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
let memberFileData;
|
|
420
|
+
try {
|
|
421
|
+
memberFileData = this.callbacks.loadFile(filename);
|
|
422
|
+
}
|
|
423
|
+
catch (e) {
|
|
424
|
+
this.callbacks.reportMessage(CompilerMessages.Error_FileCouldNotBeRead({ filename: filename, e: e }));
|
|
425
|
+
failed = true;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.warnIfKvkFileIsNotBinary(filename, memberFileData);
|
|
429
|
+
zip.file(basename, memberFileData);
|
|
430
|
+
// Remove path data from files before JSON save
|
|
431
|
+
value.name = basename;
|
|
432
|
+
});
|
|
433
|
+
if (failed) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
// TODO #9477: transform .md to .htm
|
|
437
|
+
// Remove path data from file references in options
|
|
438
|
+
if (data.options.graphicFile) {
|
|
439
|
+
data.options.graphicFile = this.callbacks.path.basename(data.options.graphicFile);
|
|
440
|
+
}
|
|
441
|
+
if (data.options.readmeFile) {
|
|
442
|
+
data.options.readmeFile = this.callbacks.path.basename(data.options.readmeFile);
|
|
443
|
+
}
|
|
444
|
+
if (data.options.licenseFile) {
|
|
445
|
+
data.options.licenseFile = this.callbacks.path.basename(data.options.licenseFile);
|
|
446
|
+
}
|
|
447
|
+
if (data.options.welcomeFile) {
|
|
448
|
+
data.options.welcomeFile = this.callbacks.path.basename(data.options.welcomeFile);
|
|
449
|
+
}
|
|
450
|
+
else if (data.files.find(file => file.name == WELCOME_HTM_FILENAME)) {
|
|
451
|
+
// We will, for improved backward-compatibility with existing packages, add a
|
|
452
|
+
// reference to the file welcome.htm is it is present in the package. This allows
|
|
453
|
+
// newer tools to avoid knowing about welcome.htm, if we assume that they work with
|
|
454
|
+
// packages compiled with kmc-package (17.0+) and not kmcomp (5.x-16.x).
|
|
455
|
+
data.options.welcomeFile = WELCOME_HTM_FILENAME;
|
|
456
|
+
}
|
|
457
|
+
if (data.options.msiFilename) {
|
|
458
|
+
data.options.msiFilename = this.callbacks.path.basename(data.options.msiFilename);
|
|
459
|
+
}
|
|
460
|
+
// Write kmp.json and kmp.inf
|
|
461
|
+
zip.file(KMP_JSON_FILENAME, JSON.stringify(data, null, 2));
|
|
462
|
+
if (hasKmpInf) {
|
|
463
|
+
zip.file(KMP_INF_FILENAME, this.buildKmpInf(data));
|
|
464
|
+
}
|
|
465
|
+
// Generate kmp file
|
|
466
|
+
return zip.generateAsync({ type: 'uint8array', compression: 'DEFLATE' });
|
|
467
|
+
}
|
|
468
|
+
buildKmpInf(data) {
|
|
469
|
+
const writer = new KmpInfWriter(data);
|
|
470
|
+
const s = writer.write();
|
|
471
|
+
return transcodeToCP1252(s);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Legacy .kmp compiler would transform xml-format .kvk files into a binary .kvk file; now
|
|
475
|
+
* we want that to remain the responsibility of the keyboard compiler, so we'll warn the
|
|
476
|
+
* few users who are still doing this
|
|
477
|
+
*/
|
|
478
|
+
warnIfKvkFileIsNotBinary(filename, data) {
|
|
479
|
+
if (!KeymanFileTypes.filenameIs(filename, ".kvk" /* KeymanFileTypes.Binary.VisualKeyboard */)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (data.byteLength < 4) {
|
|
483
|
+
// TODO: Not a valid .kvk file; should we be reporting this?
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (data[0] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[0] ||
|
|
487
|
+
data[1] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[1] ||
|
|
488
|
+
data[2] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[2] ||
|
|
489
|
+
data[3] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[3]) {
|
|
490
|
+
this.callbacks.reportMessage(CompilerMessages.Warn_FileIsNotABinaryKvkFile({ filename: filename }));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
494
|
//# sourceMappingURL=kmp-compiler.js.map
|
|
495
|
+
//# debugId=b6fb6e95-5ebd-510f-bb92-192fc2ad2371
|