@formfeed/devkit 0.3.10 → 0.3.12
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/index.cjs +334 -58
- package/index.js +339 -50
- package/package.json +3 -3
- package/types/index.d.cts +7 -5
- package/types/index.d.ts +7 -5
- package/types/lib/einvoice.d.cts +37 -0
- package/types/lib/einvoice.d.ts +37 -0
- package/types/lib/project.d.cts +2 -1
- package/types/lib/project.d.ts +2 -1
package/index.cjs
CHANGED
|
@@ -26,10 +26,12 @@ __export(index_exports, {
|
|
|
26
26
|
collectPartials: () => collectPartials,
|
|
27
27
|
contentHash: () => contentHash,
|
|
28
28
|
contentTypeFor: () => contentTypeFor,
|
|
29
|
+
declaredEinvoice: () => declaredEinvoice,
|
|
29
30
|
defaultData: () => defaultData,
|
|
30
31
|
defaultProjectConfig: () => defaultProjectConfig,
|
|
31
32
|
defaultTypesFile: () => defaultTypesFile,
|
|
32
33
|
diagnose: () => diagnose,
|
|
34
|
+
diagnoseEinvoice: () => diagnoseEinvoice,
|
|
33
35
|
findProject: () => findProject,
|
|
34
36
|
generateTypes: () => generateTypes,
|
|
35
37
|
htmlSnapshot: () => htmlSnapshot,
|
|
@@ -37,6 +39,7 @@ __export(index_exports, {
|
|
|
37
39
|
listLocalFiles: () => listLocalFiles,
|
|
38
40
|
listTemplateSlugs: () => listTemplateSlugs,
|
|
39
41
|
loadProject: () => loadProject,
|
|
42
|
+
localEinvoiceXml: () => localEinvoiceXml,
|
|
40
43
|
localFilePath: () => localFilePath,
|
|
41
44
|
localTypeSource: () => localTypeSource,
|
|
42
45
|
officeSnapshot: () => officeSnapshot,
|
|
@@ -221,16 +224,23 @@ function readJson(path, what) {
|
|
|
221
224
|
try {
|
|
222
225
|
return JSON.parse(text2);
|
|
223
226
|
} catch (e) {
|
|
224
|
-
throw new DevkitError(
|
|
227
|
+
throw new DevkitError(
|
|
228
|
+
`${what} is not valid JSON: ${path} (${e instanceof Error ? e.message : e})`,
|
|
229
|
+
"validation"
|
|
230
|
+
);
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
var templateFileName = (kind) => (0, import_engine2.isOfficeKind)(kind) ? `template.${kind}` : "template.html";
|
|
228
234
|
function sourceFilesIn(dir) {
|
|
229
|
-
return ["template.html", ...import_engine2.officeKinds.map((k) => `template.${k}`)].filter(
|
|
235
|
+
return ["template.html", ...import_engine2.officeKinds.map((k) => `template.${k}`)].filter(
|
|
236
|
+
(name) => (0, import_node_fs2.existsSync)((0, import_node_path2.join)(dir, name))
|
|
237
|
+
);
|
|
230
238
|
}
|
|
231
239
|
function listTemplateSlugs(project) {
|
|
232
240
|
if (!(0, import_node_fs2.existsSync)(project.templatesDir)) return [];
|
|
233
|
-
return (0, import_node_fs2.readdirSync)(project.templatesDir).filter(
|
|
241
|
+
return (0, import_node_fs2.readdirSync)(project.templatesDir).filter(
|
|
242
|
+
(name) => sourceFilesIn((0, import_node_path2.join)(project.templatesDir, name)).length > 0
|
|
243
|
+
).sort();
|
|
234
244
|
}
|
|
235
245
|
function templateDir(project, slug) {
|
|
236
246
|
return (0, import_node_path2.join)(project.templatesDir, slug);
|
|
@@ -239,22 +249,41 @@ var sha256Of = (bytes) => (0, import_node_crypto.createHash)("sha256").update(by
|
|
|
239
249
|
function readOfficeFile(dir, name) {
|
|
240
250
|
const path = (0, import_node_path2.join)(dir, name);
|
|
241
251
|
const bytes = new Uint8Array((0, import_node_fs2.readFileSync)(path));
|
|
242
|
-
return {
|
|
252
|
+
return {
|
|
253
|
+
format: (0, import_node_path2.extname)(name).slice(1),
|
|
254
|
+
path,
|
|
255
|
+
bytes,
|
|
256
|
+
sha256: sha256Of(bytes)
|
|
257
|
+
};
|
|
243
258
|
}
|
|
244
259
|
function readTemplate(project, slug) {
|
|
245
260
|
const dir = templateDir(project, slug);
|
|
246
261
|
const sources = sourceFilesIn(dir);
|
|
247
262
|
if (sources.length === 0)
|
|
248
|
-
throw new DevkitError(
|
|
263
|
+
throw new DevkitError(
|
|
264
|
+
`No template "${slug}" in ${project.templatesDir} (template.html, template.docx or template.pptx missing)`
|
|
265
|
+
);
|
|
249
266
|
if (sources.length > 1)
|
|
250
|
-
throw new DevkitError(
|
|
267
|
+
throw new DevkitError(
|
|
268
|
+
`${dir} holds ${sources.join(" and ")}; a template folder has one of them`,
|
|
269
|
+
"validation"
|
|
270
|
+
);
|
|
251
271
|
const file = sources[0] === "template.html" ? null : readOfficeFile(dir, sources[0]);
|
|
252
272
|
const html = file ? "" : readText((0, import_node_path2.join)(dir, "template.html")) ?? "";
|
|
253
|
-
const metaFile = readJson(
|
|
273
|
+
const metaFile = readJson(
|
|
274
|
+
(0, import_node_path2.join)(dir, "template.json"),
|
|
275
|
+
"template.json"
|
|
276
|
+
) ?? {};
|
|
254
277
|
if (file && metaFile.kind && metaFile.kind !== file.format)
|
|
255
|
-
throw new DevkitError(
|
|
278
|
+
throw new DevkitError(
|
|
279
|
+
`${slug}: template.json says kind "${metaFile.kind}", but the folder holds ${(0, import_node_path2.basename)(file.path)}`,
|
|
280
|
+
"validation"
|
|
281
|
+
);
|
|
256
282
|
if (!file && (0, import_engine2.isOfficeKind)(metaFile.kind))
|
|
257
|
-
throw new DevkitError(
|
|
283
|
+
throw new DevkitError(
|
|
284
|
+
`${slug}: template.json says kind "${metaFile.kind}", but the folder holds template.html instead of template.${metaFile.kind}`,
|
|
285
|
+
"validation"
|
|
286
|
+
);
|
|
258
287
|
const meta = {
|
|
259
288
|
name: metaFile.name ?? titleFromSlug(slug),
|
|
260
289
|
kind: file?.format ?? metaFile.kind ?? "pdf",
|
|
@@ -262,19 +291,38 @@ function readTemplate(project, slug) {
|
|
|
262
291
|
description: metaFile.description ?? null,
|
|
263
292
|
tags: metaFile.tags ?? []
|
|
264
293
|
};
|
|
265
|
-
const settings = {
|
|
294
|
+
const settings = {
|
|
295
|
+
...readJson(
|
|
296
|
+
(0, import_node_path2.join)(dir, "settings.json"),
|
|
297
|
+
"settings.json"
|
|
298
|
+
) ?? {}
|
|
299
|
+
};
|
|
266
300
|
const header = file ? null : readText((0, import_node_path2.join)(dir, "header.html"));
|
|
267
301
|
const footer = file ? null : readText((0, import_node_path2.join)(dir, "footer.html"));
|
|
268
|
-
if (header !== null && header.trim())
|
|
269
|
-
|
|
302
|
+
if (header !== null && header.trim())
|
|
303
|
+
settings.header = { ...settings.header ?? {}, html: header };
|
|
304
|
+
if (footer !== null && footer.trim())
|
|
305
|
+
settings.footer = { ...settings.footer ?? {}, html: footer };
|
|
306
|
+
const design = readJson(
|
|
307
|
+
(0, import_node_path2.join)(dir, "design.json"),
|
|
308
|
+
"design.json"
|
|
309
|
+
);
|
|
310
|
+
if (design) settings.design = design;
|
|
270
311
|
const dataSets = {};
|
|
271
312
|
const dataDir = (0, import_node_path2.join)(dir, "data");
|
|
272
313
|
if ((0, import_node_fs2.existsSync)(dataDir)) {
|
|
273
314
|
for (const file2 of (0, import_node_fs2.readdirSync)(dataDir).filter((f) => f.endsWith(".json")).sort()) {
|
|
274
|
-
dataSets[(0, import_node_path2.basename)(file2, ".json")] = readJson(
|
|
315
|
+
dataSets[(0, import_node_path2.basename)(file2, ".json")] = readJson(
|
|
316
|
+
(0, import_node_path2.join)(dataDir, file2),
|
|
317
|
+
`data/${file2}`
|
|
318
|
+
);
|
|
275
319
|
}
|
|
276
320
|
}
|
|
277
|
-
const { partials, missing, shared } = file ? { partials: {}, missing: [], shared: [] } : collectPartials(project, meta.engine, [
|
|
321
|
+
const { partials, missing, shared } = file ? { partials: {}, missing: [], shared: [] } : collectPartials(project, meta.engine, [
|
|
322
|
+
html,
|
|
323
|
+
settings.header?.html,
|
|
324
|
+
settings.footer?.html
|
|
325
|
+
]);
|
|
278
326
|
return {
|
|
279
327
|
slug,
|
|
280
328
|
dir,
|
|
@@ -285,8 +333,14 @@ function readTemplate(project, slug) {
|
|
|
285
333
|
head: file ? "" : readText((0, import_node_path2.join)(dir, "head.html")) ?? "",
|
|
286
334
|
settings,
|
|
287
335
|
dataSets,
|
|
288
|
-
dataSchema: readJson(
|
|
289
|
-
|
|
336
|
+
dataSchema: readJson(
|
|
337
|
+
(0, import_node_path2.join)(dir, "schema.json"),
|
|
338
|
+
"schema.json"
|
|
339
|
+
),
|
|
340
|
+
i18n: readJson(
|
|
341
|
+
(0, import_node_path2.join)(dir, "i18n.json"),
|
|
342
|
+
"i18n.json"
|
|
343
|
+
),
|
|
290
344
|
partials,
|
|
291
345
|
missingPartials: missing,
|
|
292
346
|
sharedPartials: shared
|
|
@@ -296,35 +350,68 @@ function defaultData(tpl, name) {
|
|
|
296
350
|
const names = Object.keys(tpl.dataSets);
|
|
297
351
|
const chosen = name ?? (names.includes("default") ? "default" : names[0]);
|
|
298
352
|
if (name && !(name in tpl.dataSets))
|
|
299
|
-
throw new DevkitError(
|
|
353
|
+
throw new DevkitError(
|
|
354
|
+
`Data set "${name}" not found; available: ${names.join(", ") || "none"}`
|
|
355
|
+
);
|
|
300
356
|
return chosen ? { name: chosen, data: tpl.dataSets[chosen] } : { name: "empty", data: {} };
|
|
301
357
|
}
|
|
302
358
|
function writeTemplate(project, slug, meta, version, file) {
|
|
303
359
|
const dir = templateDir(project, slug);
|
|
304
360
|
const office = (0, import_engine2.isOfficeKind)(meta.kind);
|
|
305
|
-
if (office && !file)
|
|
361
|
+
if (office && !file)
|
|
362
|
+
throw new DevkitError(
|
|
363
|
+
`${slug} is a ${meta.kind} template; its document is needed to write the folder`
|
|
364
|
+
);
|
|
306
365
|
(0, import_node_fs2.mkdirSync)((0, import_node_path2.join)(dir, "data"), { recursive: true });
|
|
307
366
|
const settings = { ...version.settings ?? {} };
|
|
308
367
|
const header = office ? "" : settings.header?.html ?? "";
|
|
309
368
|
const footer = office ? "" : settings.footer?.html ?? "";
|
|
310
369
|
if (settings.header) settings.header = stripHtml(settings.header);
|
|
311
370
|
if (settings.footer) settings.footer = stripHtml(settings.footer);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
371
|
+
const design = settings.design;
|
|
372
|
+
delete settings.design;
|
|
373
|
+
(0, import_node_fs2.writeFileSync)(
|
|
374
|
+
(0, import_node_path2.join)(dir, "template.json"),
|
|
375
|
+
JSON.stringify(meta, null, 2) + "\n"
|
|
376
|
+
);
|
|
377
|
+
for (const name of [
|
|
378
|
+
"template.html",
|
|
379
|
+
...import_engine2.officeKinds.map((k) => `template.${k}`)
|
|
380
|
+
])
|
|
381
|
+
if (name !== templateFileName(meta.kind) && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(dir, name)))
|
|
382
|
+
(0, import_node_fs2.rmSync)((0, import_node_path2.join)(dir, name));
|
|
315
383
|
if (office) (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, templateFileName(meta.kind)), file);
|
|
316
384
|
else (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dir, "template.html"), version.html ?? "");
|
|
317
385
|
writeOrRemove((0, import_node_path2.join)(dir, "style.css"), office ? "" : version.css ?? "");
|
|
318
386
|
writeOrRemove((0, import_node_path2.join)(dir, "head.html"), office ? "" : version.head ?? "");
|
|
319
387
|
writeOrRemove((0, import_node_path2.join)(dir, "header.html"), header);
|
|
320
388
|
writeOrRemove((0, import_node_path2.join)(dir, "footer.html"), footer);
|
|
321
|
-
(0, import_node_fs2.writeFileSync)(
|
|
322
|
-
|
|
389
|
+
(0, import_node_fs2.writeFileSync)(
|
|
390
|
+
(0, import_node_path2.join)(dir, "settings.json"),
|
|
391
|
+
JSON.stringify(settings, null, 2) + "\n"
|
|
392
|
+
);
|
|
393
|
+
writeOrRemove(
|
|
394
|
+
(0, import_node_path2.join)(dir, "design.json"),
|
|
395
|
+
design && typeof design === "object" ? JSON.stringify(design, null, 2) + "\n" : ""
|
|
396
|
+
);
|
|
397
|
+
(0, import_node_fs2.writeFileSync)(
|
|
398
|
+
(0, import_node_path2.join)(dir, "data", "default.json"),
|
|
399
|
+
JSON.stringify(version.sample_data ?? {}, null, 2) + "\n"
|
|
400
|
+
);
|
|
323
401
|
for (const [name, data] of Object.entries(version.data_sets ?? {}))
|
|
324
402
|
if (name !== "default" && /^[\w.-]+$/.test(name))
|
|
325
|
-
(0, import_node_fs2.writeFileSync)(
|
|
326
|
-
|
|
327
|
-
|
|
403
|
+
(0, import_node_fs2.writeFileSync)(
|
|
404
|
+
(0, import_node_path2.join)(dir, "data", `${name}.json`),
|
|
405
|
+
JSON.stringify(data ?? {}, null, 2) + "\n"
|
|
406
|
+
);
|
|
407
|
+
writeOrRemove(
|
|
408
|
+
(0, import_node_path2.join)(dir, "schema.json"),
|
|
409
|
+
version.data_schema ? JSON.stringify(version.data_schema, null, 2) + "\n" : ""
|
|
410
|
+
);
|
|
411
|
+
writeOrRemove(
|
|
412
|
+
(0, import_node_path2.join)(dir, "i18n.json"),
|
|
413
|
+
version.i18n ? JSON.stringify(version.i18n, null, 2) + "\n" : ""
|
|
414
|
+
);
|
|
328
415
|
writePartials(project, version.partials ?? null);
|
|
329
416
|
return dir;
|
|
330
417
|
}
|
|
@@ -333,7 +420,10 @@ function writePartials(project, partials) {
|
|
|
333
420
|
const changed = [];
|
|
334
421
|
for (const [name, source] of Object.entries(partials ?? {})) {
|
|
335
422
|
if (!partialName.test(name)) continue;
|
|
336
|
-
const path = (0, import_node_path2.join)(
|
|
423
|
+
const path = (0, import_node_path2.join)(
|
|
424
|
+
project.partialsDir,
|
|
425
|
+
name.includes(".") ? name : `${name}.html`
|
|
426
|
+
);
|
|
337
427
|
if (readText(path) === source) continue;
|
|
338
428
|
(0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(path), { recursive: true });
|
|
339
429
|
(0, import_node_fs2.writeFileSync)(path, source);
|
|
@@ -379,17 +469,36 @@ function versionPayload(tpl) {
|
|
|
379
469
|
function contentHash(tpl) {
|
|
380
470
|
if (tpl.file) {
|
|
381
471
|
const payload2 = officeVersionPayload(tpl);
|
|
382
|
-
const parts2 = [
|
|
472
|
+
const parts2 = [
|
|
473
|
+
tpl.file.format,
|
|
474
|
+
tpl.file.sha256,
|
|
475
|
+
payload2.settings,
|
|
476
|
+
payload2.sample_data,
|
|
477
|
+
payload2.data_sets,
|
|
478
|
+
payload2.data_schema,
|
|
479
|
+
payload2.i18n
|
|
480
|
+
];
|
|
383
481
|
return (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(parts2)).digest("hex");
|
|
384
482
|
}
|
|
385
483
|
const payload = versionPayload(tpl);
|
|
386
|
-
const parts = [
|
|
484
|
+
const parts = [
|
|
485
|
+
payload.html,
|
|
486
|
+
payload.css,
|
|
487
|
+
payload.head,
|
|
488
|
+
payload.settings,
|
|
489
|
+
payload.sample_data,
|
|
490
|
+
payload.data_sets,
|
|
491
|
+
payload.data_schema,
|
|
492
|
+
payload.i18n
|
|
493
|
+
];
|
|
387
494
|
if (payload.partials) parts.push(payload.partials);
|
|
388
495
|
return (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
389
496
|
}
|
|
390
497
|
var statePath = (project) => (0, import_node_path2.join)(project.root, ".formfeed", "state.json");
|
|
391
498
|
function readState(project) {
|
|
392
|
-
return readJson(statePath(project), ".formfeed/state.json") ?? {
|
|
499
|
+
return readJson(statePath(project), ".formfeed/state.json") ?? {
|
|
500
|
+
templates: {}
|
|
501
|
+
};
|
|
393
502
|
}
|
|
394
503
|
function writeState(project, state) {
|
|
395
504
|
(0, import_node_fs2.mkdirSync)((0, import_node_path2.join)(project.root, ".formfeed"), { recursive: true });
|
|
@@ -415,7 +524,8 @@ function partialResolver(project) {
|
|
|
415
524
|
return (name) => {
|
|
416
525
|
for (const candidate of [name, `${name}.html`]) {
|
|
417
526
|
const path = (0, import_node_path2.join)(project.partialsDir, candidate);
|
|
418
|
-
if ((0, import_node_fs2.existsSync)(path) && (0, import_node_fs2.statSync)(path).isFile())
|
|
527
|
+
if ((0, import_node_fs2.existsSync)(path) && (0, import_node_fs2.statSync)(path).isFile())
|
|
528
|
+
return (0, import_node_fs2.readFileSync)(path, "utf8");
|
|
419
529
|
}
|
|
420
530
|
shared ??= new Set(Object.keys(readState(project).sharedPartials ?? {}));
|
|
421
531
|
const normalised = (0, import_engine2.normalisePartialName)(name);
|
|
@@ -436,7 +546,12 @@ function recordSharedPartial(project, name, remote, source) {
|
|
|
436
546
|
const state = readState(project);
|
|
437
547
|
state.sharedPartials = {
|
|
438
548
|
...state.sharedPartials ?? {},
|
|
439
|
-
[name]: {
|
|
549
|
+
[name]: {
|
|
550
|
+
version: remote.version,
|
|
551
|
+
engine: remote.engine,
|
|
552
|
+
contentHash: partialContentHash(source),
|
|
553
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
554
|
+
}
|
|
440
555
|
};
|
|
441
556
|
writeState(project, state);
|
|
442
557
|
}
|
|
@@ -494,7 +609,10 @@ function diagnose(tpl, sampleData) {
|
|
|
494
609
|
severity: "error",
|
|
495
610
|
code: "syntax-error",
|
|
496
611
|
message: e.message,
|
|
497
|
-
range: {
|
|
612
|
+
range: {
|
|
613
|
+
start: { line: e.line ?? 1, column: e.column ?? 1 },
|
|
614
|
+
end: { line: e.line ?? 1, column: (e.column ?? 1) + 1 }
|
|
615
|
+
}
|
|
498
616
|
});
|
|
499
617
|
} else throw e;
|
|
500
618
|
}
|
|
@@ -504,7 +622,8 @@ function diagnose(tpl, sampleData) {
|
|
|
504
622
|
["footer", tpl.settings.footer?.html]
|
|
505
623
|
]) {
|
|
506
624
|
if (!source) continue;
|
|
507
|
-
for (const d of engine.analyze(source, { sampleData }).diagnostics)
|
|
625
|
+
for (const d of engine.analyze(source, { sampleData }).diagnostics)
|
|
626
|
+
diagnostics.push({ ...d, message: `${part}: ${d.message}` });
|
|
508
627
|
}
|
|
509
628
|
for (const name of tpl.missingPartials)
|
|
510
629
|
diagnostics.push({
|
|
@@ -514,8 +633,28 @@ function diagnose(tpl, sampleData) {
|
|
|
514
633
|
message: `No partial "${name}" in the project's partials folder; if it is shared by the organisation, formfeed partials pull brings it`,
|
|
515
634
|
range: { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }
|
|
516
635
|
});
|
|
636
|
+
const design = designDiagnostic(tpl);
|
|
637
|
+
if (design) diagnostics.push(design);
|
|
517
638
|
return diagnostics;
|
|
518
639
|
}
|
|
640
|
+
function designDiagnostic(tpl) {
|
|
641
|
+
if (tpl.settings.design === void 0) return null;
|
|
642
|
+
const parsed = (0, import_engine4.parseDesign)(tpl.settings.design);
|
|
643
|
+
if (!parsed.design)
|
|
644
|
+
return {
|
|
645
|
+
severity: "warning",
|
|
646
|
+
code: "design-invalid",
|
|
647
|
+
message: `design.json is not a design the app can open (${parsed.problems.slice(0, 3).join("; ")}); the template opens as code`,
|
|
648
|
+
range: nowhere
|
|
649
|
+
};
|
|
650
|
+
if (parsed.design.output === (0, import_engine4.designOutputHash)(tpl.html, tpl.css)) return null;
|
|
651
|
+
return {
|
|
652
|
+
severity: "warning",
|
|
653
|
+
code: "design-stale",
|
|
654
|
+
message: "template.html or style.css was edited after design.json was written; the app opens the template as code until you choose to keep the code or apply the design again",
|
|
655
|
+
range: nowhere
|
|
656
|
+
};
|
|
657
|
+
}
|
|
519
658
|
var nowhere = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
520
659
|
function diagnoseOffice(tpl, file, sampleData) {
|
|
521
660
|
let analysis;
|
|
@@ -523,7 +662,14 @@ function diagnoseOffice(tpl, file, sampleData) {
|
|
|
523
662
|
analysis = (0, import_engine4.analyzeOffice)(file, { engine: tpl.meta.engine, sampleData });
|
|
524
663
|
} catch (e) {
|
|
525
664
|
const code = e.code;
|
|
526
|
-
return [
|
|
665
|
+
return [
|
|
666
|
+
{
|
|
667
|
+
severity: "error",
|
|
668
|
+
code: typeof code === "string" ? code : "office-document-invalid",
|
|
669
|
+
message: e instanceof Error ? e.message : String(e),
|
|
670
|
+
range: nowhere
|
|
671
|
+
}
|
|
672
|
+
];
|
|
527
673
|
}
|
|
528
674
|
return analysis.diagnostics.map((d) => ({
|
|
529
675
|
severity: d.severity,
|
|
@@ -535,17 +681,34 @@ function diagnoseOffice(tpl, file, sampleData) {
|
|
|
535
681
|
}));
|
|
536
682
|
}
|
|
537
683
|
async function renderOfficeLocal(project, tpl, data, options = {}) {
|
|
538
|
-
if (!tpl.file)
|
|
539
|
-
|
|
684
|
+
if (!tpl.file)
|
|
685
|
+
throw new DevkitError(
|
|
686
|
+
`${tpl.slug} is not a Word or PowerPoint template`,
|
|
687
|
+
"validation"
|
|
688
|
+
);
|
|
689
|
+
const context = renderContext(
|
|
690
|
+
project,
|
|
691
|
+
tpl,
|
|
692
|
+
options.assetBaseUrl,
|
|
693
|
+
options.brand
|
|
694
|
+
);
|
|
540
695
|
if (options.locale) context.locale = options.locale;
|
|
541
|
-
return (0, import_engine4.renderOffice)(tpl.file.bytes, {
|
|
696
|
+
return (0, import_engine4.renderOffice)(tpl.file.bytes, {
|
|
697
|
+
engine: tpl.meta.engine,
|
|
698
|
+
data,
|
|
699
|
+
context,
|
|
700
|
+
images: options.images,
|
|
701
|
+
random: options.random
|
|
702
|
+
});
|
|
542
703
|
}
|
|
543
704
|
function htmlSnapshot(rendered) {
|
|
544
705
|
let snapshot = rendered.document;
|
|
545
|
-
if (rendered.headerHtml)
|
|
706
|
+
if (rendered.headerHtml)
|
|
707
|
+
snapshot += `
|
|
546
708
|
<!-- formfeed:header -->
|
|
547
709
|
${rendered.headerHtml}`;
|
|
548
|
-
if (rendered.footerHtml)
|
|
710
|
+
if (rendered.footerHtml)
|
|
711
|
+
snapshot += `
|
|
549
712
|
<!-- formfeed:footer -->
|
|
550
713
|
${rendered.footerHtml}`;
|
|
551
714
|
return snapshot;
|
|
@@ -598,7 +761,14 @@ async function renderLocal(project, tpl, data, options) {
|
|
|
598
761
|
const ctx = renderContext(project, tpl, options.assetBaseUrl, options.brand);
|
|
599
762
|
if (options.locale) ctx.locale = options.locale;
|
|
600
763
|
return (0, import_engine4.renderVersion)(
|
|
601
|
-
{
|
|
764
|
+
{
|
|
765
|
+
engine: tpl.meta.engine,
|
|
766
|
+
html: tpl.html,
|
|
767
|
+
css: tpl.css,
|
|
768
|
+
head: tpl.head,
|
|
769
|
+
settings: (0, import_engine4.mergeSettings)(tpl.settings),
|
|
770
|
+
kind
|
|
771
|
+
},
|
|
602
772
|
data,
|
|
603
773
|
ctx,
|
|
604
774
|
options.mode,
|
|
@@ -608,16 +778,26 @@ async function renderLocal(project, tpl, data, options) {
|
|
|
608
778
|
function renderedSettings(rendered) {
|
|
609
779
|
const style = rendered.inlineCss ? `<style>${rendered.inlineCss}</style>` : "";
|
|
610
780
|
const part = (settings2, html) => settings2?.html ? { ...settings2, html: `${style}${html ?? ""}` } : settings2;
|
|
611
|
-
const { settings } = rendered;
|
|
781
|
+
const { design: _design, ...settings } = rendered.settings;
|
|
782
|
+
void _design;
|
|
612
783
|
return {
|
|
613
784
|
...settings,
|
|
614
785
|
...settings.header ? { header: part(settings.header, rendered.headerHtml) } : {},
|
|
615
786
|
...settings.footer ? { footer: part(settings.footer, rendered.footerHtml) } : {},
|
|
616
|
-
...settings.pdf?.metadata?.title !== void 0 ? {
|
|
787
|
+
...settings.pdf?.metadata?.title !== void 0 ? {
|
|
788
|
+
pdf: {
|
|
789
|
+
...settings.pdf,
|
|
790
|
+
metadata: { ...settings.pdf.metadata, title: rendered.title ?? "" }
|
|
791
|
+
}
|
|
792
|
+
} : {}
|
|
617
793
|
};
|
|
618
794
|
}
|
|
619
795
|
function htmlKind(tpl) {
|
|
620
|
-
if ((0, import_engine4.isOfficeKind)(tpl.meta.kind))
|
|
796
|
+
if ((0, import_engine4.isOfficeKind)(tpl.meta.kind))
|
|
797
|
+
throw new DevkitError(
|
|
798
|
+
`${tpl.slug} is a ${tpl.meta.kind} template; fill it with renderOfficeLocal`,
|
|
799
|
+
"validation"
|
|
800
|
+
);
|
|
621
801
|
return tpl.meta.kind;
|
|
622
802
|
}
|
|
623
803
|
function previewDocument(tpl, rendered, mode, pagedScriptUrl) {
|
|
@@ -632,10 +812,103 @@ function previewDocument(tpl, rendered, mode, pagedScriptUrl) {
|
|
|
632
812
|
return mode === "paged" && kind === "pdf" ? (0, import_engine4.pagedDocument)(draft, { pagedScriptUrl }) : (0, import_engine4.flowDocument)(draft);
|
|
633
813
|
}
|
|
634
814
|
|
|
635
|
-
// packages/devkit/src/lib/
|
|
636
|
-
var import_node_crypto2 = require("node:crypto");
|
|
815
|
+
// packages/devkit/src/lib/einvoice.ts
|
|
637
816
|
var import_node_fs4 = require("node:fs");
|
|
638
817
|
var import_node_path4 = require("node:path");
|
|
818
|
+
var import_engine5 = require("@formfeed/engine");
|
|
819
|
+
function declaredEinvoice(tpl, override) {
|
|
820
|
+
const resolved = (0, import_engine5.resolveEinvoiceOptions)(tpl.settings.einvoice, override);
|
|
821
|
+
if (!resolved.ok)
|
|
822
|
+
throw new DevkitError(
|
|
823
|
+
`${tpl.slug}: e-invoice ${resolved.field} "${resolved.value}": ${resolved.reason}`,
|
|
824
|
+
"validation"
|
|
825
|
+
);
|
|
826
|
+
return resolved.options;
|
|
827
|
+
}
|
|
828
|
+
var nowhere2 = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
829
|
+
function diagnoseEinvoice(tpl) {
|
|
830
|
+
let options;
|
|
831
|
+
try {
|
|
832
|
+
options = declaredEinvoice(tpl);
|
|
833
|
+
} catch (e) {
|
|
834
|
+
return [
|
|
835
|
+
{
|
|
836
|
+
dataSet: "",
|
|
837
|
+
file: "settings.json",
|
|
838
|
+
diagnostics: [
|
|
839
|
+
{
|
|
840
|
+
severity: "error",
|
|
841
|
+
code: "einvoice-profile",
|
|
842
|
+
message: e instanceof Error ? e.message : String(e),
|
|
843
|
+
range: nowhere2
|
|
844
|
+
}
|
|
845
|
+
]
|
|
846
|
+
}
|
|
847
|
+
];
|
|
848
|
+
}
|
|
849
|
+
if (!options) return [];
|
|
850
|
+
if (tpl.meta.kind === "image")
|
|
851
|
+
return [
|
|
852
|
+
{
|
|
853
|
+
dataSet: "",
|
|
854
|
+
file: "settings.json",
|
|
855
|
+
diagnostics: [
|
|
856
|
+
{
|
|
857
|
+
severity: "warning",
|
|
858
|
+
code: "einvoice-image",
|
|
859
|
+
message: "settings.json declares an e-invoice, which an image template never produces: an e-invoice is a PDF",
|
|
860
|
+
range: nowhere2
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
}
|
|
864
|
+
];
|
|
865
|
+
const out = [];
|
|
866
|
+
for (const [name, data] of Object.entries(tpl.dataSets)) {
|
|
867
|
+
const result = (0, import_engine5.einvoiceXml)(data, options);
|
|
868
|
+
if (result.ok) continue;
|
|
869
|
+
const file = `data/${name}.json`;
|
|
870
|
+
const path = (0, import_node_path4.join)(tpl.dir, "data", `${name}.json`);
|
|
871
|
+
const text2 = (0, import_node_fs4.existsSync)(path) ? (0, import_node_fs4.readFileSync)(path, "utf8") : null;
|
|
872
|
+
out.push({
|
|
873
|
+
dataSet: name,
|
|
874
|
+
file,
|
|
875
|
+
diagnostics: result.problems.map((problem) => {
|
|
876
|
+
const at = text2 ? (0, import_engine5.locateJsonPath)(text2, [
|
|
877
|
+
import_engine5.INVOICE_DATA_KEY,
|
|
878
|
+
...(0, import_engine5.invoicePathSegments)(problem.path)
|
|
879
|
+
]) : null;
|
|
880
|
+
const start = at ? { line: at.line, column: at.column } : nowhere2.start;
|
|
881
|
+
return {
|
|
882
|
+
severity: "error",
|
|
883
|
+
code: `einvoice-${problem.code.replace(/_/g, "-")}`,
|
|
884
|
+
message: problem.message,
|
|
885
|
+
range: { start, end: start }
|
|
886
|
+
};
|
|
887
|
+
})
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
return out;
|
|
891
|
+
}
|
|
892
|
+
function localEinvoiceXml(tpl, data, override) {
|
|
893
|
+
const options = declaredEinvoice(tpl, override ?? {});
|
|
894
|
+
if (!options)
|
|
895
|
+
throw new DevkitError(`${tpl.slug} declares no e-invoice`, "validation");
|
|
896
|
+
const result = (0, import_engine5.einvoiceXml)(data, options);
|
|
897
|
+
if (!result.ok)
|
|
898
|
+
throw new DevkitError(
|
|
899
|
+
[
|
|
900
|
+
`${tpl.slug}: the _invoice block does not make an e-invoice`,
|
|
901
|
+
...result.problems.map((problem) => ` ${problem.message}`)
|
|
902
|
+
].join("\n"),
|
|
903
|
+
"validation"
|
|
904
|
+
);
|
|
905
|
+
return { xml: result.xml, invoice: result.invoice, options };
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// packages/devkit/src/lib/files.ts
|
|
909
|
+
var import_node_crypto2 = require("node:crypto");
|
|
910
|
+
var import_node_fs5 = require("node:fs");
|
|
911
|
+
var import_node_path5 = require("node:path");
|
|
639
912
|
|
|
640
913
|
// packages/api-types/src/lib/assets.ts
|
|
641
914
|
var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._-]*";
|
|
@@ -667,25 +940,25 @@ function sha256File(bytes) {
|
|
|
667
940
|
}
|
|
668
941
|
function listLocalFiles(project) {
|
|
669
942
|
const listing = { files: [], skipped: [] };
|
|
670
|
-
if (!(0,
|
|
943
|
+
if (!(0, import_node_fs5.existsSync)(project.filesDir)) return listing;
|
|
671
944
|
const walk2 = (dir) => {
|
|
672
|
-
for (const entry of (0,
|
|
945
|
+
for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
|
|
673
946
|
if (entry.name.startsWith(".")) continue;
|
|
674
|
-
const path = (0,
|
|
947
|
+
const path = (0, import_node_path5.join)(dir, entry.name);
|
|
675
948
|
if (entry.isDirectory()) {
|
|
676
949
|
walk2(path);
|
|
677
950
|
continue;
|
|
678
951
|
}
|
|
679
952
|
if (!entry.isFile()) continue;
|
|
680
|
-
const name = (0,
|
|
681
|
-
const size = (0,
|
|
953
|
+
const name = (0, import_node_path5.relative)(project.filesDir, path).split(import_node_path5.sep).join("/");
|
|
954
|
+
const size = (0, import_node_fs5.statSync)(path).size;
|
|
682
955
|
const contentType = contentTypeFor(name);
|
|
683
956
|
const reason = !isAssetName(name) ? "the name has characters a library name cannot have (letters, digits, dot, dash, underscore)" : !contentType ? "not an image or a PDF" : size === 0 ? "empty" : size > ASSET_MAX_BYTES ? `larger than ${ASSET_MAX_BYTES / 1024 / 1024} MB` : null;
|
|
684
957
|
if (reason) {
|
|
685
958
|
listing.skipped.push({ path, reason });
|
|
686
959
|
continue;
|
|
687
960
|
}
|
|
688
|
-
const bytes = (0,
|
|
961
|
+
const bytes = (0, import_node_fs5.readFileSync)(path);
|
|
689
962
|
listing.files.push({ name, path, bytes: size, sha256: sha256File(bytes), contentType });
|
|
690
963
|
}
|
|
691
964
|
};
|
|
@@ -695,14 +968,14 @@ function listLocalFiles(project) {
|
|
|
695
968
|
}
|
|
696
969
|
function localFilePath(project, name) {
|
|
697
970
|
if (!isAssetName(name)) throw new DevkitError(`"${name}" is not a library file name`);
|
|
698
|
-
const path = (0,
|
|
699
|
-
if (!path.startsWith((0,
|
|
971
|
+
const path = (0, import_node_path5.resolve)(project.filesDir, ...name.split("/"));
|
|
972
|
+
if (!path.startsWith((0, import_node_path5.resolve)(project.filesDir) + import_node_path5.sep)) throw new DevkitError(`"${name}" is not inside the files folder`);
|
|
700
973
|
return path;
|
|
701
974
|
}
|
|
702
975
|
function writeLocalFile(project, name, bytes) {
|
|
703
976
|
const path = localFilePath(project, name);
|
|
704
|
-
(0,
|
|
705
|
-
(0,
|
|
977
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(path), { recursive: true });
|
|
978
|
+
(0, import_node_fs5.writeFileSync)(path, bytes);
|
|
706
979
|
return path;
|
|
707
980
|
}
|
|
708
981
|
|
|
@@ -770,7 +1043,7 @@ function isRecord(value) {
|
|
|
770
1043
|
}
|
|
771
1044
|
|
|
772
1045
|
// packages/devkit/src/lib/types/index.ts
|
|
773
|
-
var
|
|
1046
|
+
var import_engine6 = require("@formfeed/engine");
|
|
774
1047
|
|
|
775
1048
|
// packages/devkit/src/lib/types/model.ts
|
|
776
1049
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1258,7 +1531,7 @@ function localTypeSource(tpl) {
|
|
|
1258
1531
|
const sets = Object.values(tpl.dataSets);
|
|
1259
1532
|
return {
|
|
1260
1533
|
slug: tpl.slug,
|
|
1261
|
-
schema: (0,
|
|
1534
|
+
schema: (0, import_engine6.inferSchemaFromDataSets)(sets.length ? sets : [{}]),
|
|
1262
1535
|
origin: "inferred"
|
|
1263
1536
|
};
|
|
1264
1537
|
}
|
|
@@ -1270,10 +1543,12 @@ function localTypeSource(tpl) {
|
|
|
1270
1543
|
collectPartials,
|
|
1271
1544
|
contentHash,
|
|
1272
1545
|
contentTypeFor,
|
|
1546
|
+
declaredEinvoice,
|
|
1273
1547
|
defaultData,
|
|
1274
1548
|
defaultProjectConfig,
|
|
1275
1549
|
defaultTypesFile,
|
|
1276
1550
|
diagnose,
|
|
1551
|
+
diagnoseEinvoice,
|
|
1277
1552
|
findProject,
|
|
1278
1553
|
generateTypes,
|
|
1279
1554
|
htmlSnapshot,
|
|
@@ -1281,6 +1556,7 @@ function localTypeSource(tpl) {
|
|
|
1281
1556
|
listLocalFiles,
|
|
1282
1557
|
listTemplateSlugs,
|
|
1283
1558
|
loadProject,
|
|
1559
|
+
localEinvoiceXml,
|
|
1284
1560
|
localFilePath,
|
|
1285
1561
|
localTypeSource,
|
|
1286
1562
|
officeSnapshot,
|