@formfeed/devkit 0.3.11 → 0.3.13
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 +173 -15
- package/index.js +174 -7
- package/package.json +3 -3
- package/types/index.d.cts +9 -5
- package/types/index.d.ts +9 -5
- package/types/lib/accessibility.d.cts +21 -0
- package/types/lib/accessibility.d.ts +21 -0
- package/types/lib/einvoice.d.cts +37 -0
- package/types/lib/einvoice.d.ts +37 -0
package/index.cjs
CHANGED
|
@@ -26,10 +26,13 @@ __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
|
+
diagnoseAccessibility: () => diagnoseAccessibility,
|
|
35
|
+
diagnoseEinvoice: () => diagnoseEinvoice,
|
|
33
36
|
findProject: () => findProject,
|
|
34
37
|
generateTypes: () => generateTypes,
|
|
35
38
|
htmlSnapshot: () => htmlSnapshot,
|
|
@@ -37,6 +40,7 @@ __export(index_exports, {
|
|
|
37
40
|
listLocalFiles: () => listLocalFiles,
|
|
38
41
|
listTemplateSlugs: () => listTemplateSlugs,
|
|
39
42
|
loadProject: () => loadProject,
|
|
43
|
+
localEinvoiceXml: () => localEinvoiceXml,
|
|
40
44
|
localFilePath: () => localFilePath,
|
|
41
45
|
localTypeSource: () => localTypeSource,
|
|
42
46
|
officeSnapshot: () => officeSnapshot,
|
|
@@ -806,13 +810,163 @@ function previewDocument(tpl, rendered, mode, pagedScriptUrl) {
|
|
|
806
810
|
settings: rendered.settings,
|
|
807
811
|
kind
|
|
808
812
|
};
|
|
809
|
-
|
|
813
|
+
const audit = kind === "pdf" && (0, import_engine4.resolveUa)(rendered.settings.pdf?.ua) !== null;
|
|
814
|
+
return mode === "paged" && kind === "pdf" ? (0, import_engine4.pagedDocument)(draft, { pagedScriptUrl, audit }) : (0, import_engine4.flowDocument)(draft, { audit });
|
|
810
815
|
}
|
|
811
816
|
|
|
812
|
-
// packages/devkit/src/lib/
|
|
813
|
-
var import_node_crypto2 = require("node:crypto");
|
|
817
|
+
// packages/devkit/src/lib/einvoice.ts
|
|
814
818
|
var import_node_fs4 = require("node:fs");
|
|
815
819
|
var import_node_path4 = require("node:path");
|
|
820
|
+
var import_engine5 = require("@formfeed/engine");
|
|
821
|
+
function declaredEinvoice(tpl, override) {
|
|
822
|
+
const resolved = (0, import_engine5.resolveEinvoiceOptions)(tpl.settings.einvoice, override);
|
|
823
|
+
if (!resolved.ok)
|
|
824
|
+
throw new DevkitError(
|
|
825
|
+
`${tpl.slug}: e-invoice ${resolved.field} "${resolved.value}": ${resolved.reason}`,
|
|
826
|
+
"validation"
|
|
827
|
+
);
|
|
828
|
+
return resolved.options;
|
|
829
|
+
}
|
|
830
|
+
var nowhere2 = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
831
|
+
function diagnoseEinvoice(tpl) {
|
|
832
|
+
let options;
|
|
833
|
+
try {
|
|
834
|
+
options = declaredEinvoice(tpl);
|
|
835
|
+
} catch (e) {
|
|
836
|
+
return [
|
|
837
|
+
{
|
|
838
|
+
dataSet: "",
|
|
839
|
+
file: "settings.json",
|
|
840
|
+
diagnostics: [
|
|
841
|
+
{
|
|
842
|
+
severity: "error",
|
|
843
|
+
code: "einvoice-profile",
|
|
844
|
+
message: e instanceof Error ? e.message : String(e),
|
|
845
|
+
range: nowhere2
|
|
846
|
+
}
|
|
847
|
+
]
|
|
848
|
+
}
|
|
849
|
+
];
|
|
850
|
+
}
|
|
851
|
+
if (!options) return [];
|
|
852
|
+
if (tpl.meta.kind === "image")
|
|
853
|
+
return [
|
|
854
|
+
{
|
|
855
|
+
dataSet: "",
|
|
856
|
+
file: "settings.json",
|
|
857
|
+
diagnostics: [
|
|
858
|
+
{
|
|
859
|
+
severity: "warning",
|
|
860
|
+
code: "einvoice-image",
|
|
861
|
+
message: "settings.json declares an e-invoice, which an image template never produces: an e-invoice is a PDF",
|
|
862
|
+
range: nowhere2
|
|
863
|
+
}
|
|
864
|
+
]
|
|
865
|
+
}
|
|
866
|
+
];
|
|
867
|
+
const out = [];
|
|
868
|
+
for (const [name, data] of Object.entries(tpl.dataSets)) {
|
|
869
|
+
const result = (0, import_engine5.einvoiceXml)(data, options);
|
|
870
|
+
if (result.ok) continue;
|
|
871
|
+
const file = `data/${name}.json`;
|
|
872
|
+
const path = (0, import_node_path4.join)(tpl.dir, "data", `${name}.json`);
|
|
873
|
+
const text2 = (0, import_node_fs4.existsSync)(path) ? (0, import_node_fs4.readFileSync)(path, "utf8") : null;
|
|
874
|
+
out.push({
|
|
875
|
+
dataSet: name,
|
|
876
|
+
file,
|
|
877
|
+
diagnostics: result.problems.map((problem) => {
|
|
878
|
+
const at = text2 ? (0, import_engine5.locateJsonPath)(text2, [
|
|
879
|
+
import_engine5.INVOICE_DATA_KEY,
|
|
880
|
+
...(0, import_engine5.invoicePathSegments)(problem.path)
|
|
881
|
+
]) : null;
|
|
882
|
+
const start = at ? { line: at.line, column: at.column } : nowhere2.start;
|
|
883
|
+
return {
|
|
884
|
+
severity: "error",
|
|
885
|
+
code: `einvoice-${problem.code.replace(/_/g, "-")}`,
|
|
886
|
+
message: problem.message,
|
|
887
|
+
range: { start, end: start }
|
|
888
|
+
};
|
|
889
|
+
})
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
return out;
|
|
893
|
+
}
|
|
894
|
+
function localEinvoiceXml(tpl, data, override) {
|
|
895
|
+
const options = declaredEinvoice(tpl, override ?? {});
|
|
896
|
+
if (!options)
|
|
897
|
+
throw new DevkitError(`${tpl.slug} declares no e-invoice`, "validation");
|
|
898
|
+
const result = (0, import_engine5.einvoiceXml)(data, options);
|
|
899
|
+
if (!result.ok)
|
|
900
|
+
throw new DevkitError(
|
|
901
|
+
[
|
|
902
|
+
`${tpl.slug}: the _invoice block does not make an e-invoice`,
|
|
903
|
+
...result.problems.map((problem) => ` ${problem.message}`)
|
|
904
|
+
].join("\n"),
|
|
905
|
+
"validation"
|
|
906
|
+
);
|
|
907
|
+
return { xml: result.xml, invoice: result.invoice, options };
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// packages/devkit/src/lib/accessibility.ts
|
|
911
|
+
var import_engine6 = require("@formfeed/engine");
|
|
912
|
+
var nowhere3 = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
913
|
+
var about = (severity, code, message) => ({
|
|
914
|
+
file: "settings.json",
|
|
915
|
+
diagnostics: [{ severity, code, message, range: nowhere3 }]
|
|
916
|
+
});
|
|
917
|
+
function diagnoseAccessibility(tpl) {
|
|
918
|
+
const declared = tpl.settings.pdf?.ua;
|
|
919
|
+
if (declared !== void 0 && !(0, import_engine6.isUaSetting)(declared))
|
|
920
|
+
return [
|
|
921
|
+
about(
|
|
922
|
+
"error",
|
|
923
|
+
"accessibility-setting",
|
|
924
|
+
'pdf.ua is true, false or { "check": "strict" | "report" }'
|
|
925
|
+
)
|
|
926
|
+
];
|
|
927
|
+
const ua = (0, import_engine6.resolveUa)(declared);
|
|
928
|
+
if (!ua) return [];
|
|
929
|
+
if (tpl.meta.kind === "image")
|
|
930
|
+
return [
|
|
931
|
+
about(
|
|
932
|
+
"warning",
|
|
933
|
+
"accessibility-image",
|
|
934
|
+
"settings.json declares pdf.ua, which an image template never produces: PDF/UA is a format of PDF"
|
|
935
|
+
)
|
|
936
|
+
];
|
|
937
|
+
if ((0, import_engine6.isOfficeKind)(tpl.meta.kind))
|
|
938
|
+
return [about("error", "accessibility-office", import_engine6.UA_REFUSALS.office)];
|
|
939
|
+
const out = [];
|
|
940
|
+
const body = [];
|
|
941
|
+
for (const found of (0, import_engine6.staticAuditDiagnostics)(
|
|
942
|
+
{
|
|
943
|
+
engine: tpl.meta.engine,
|
|
944
|
+
html: tpl.html,
|
|
945
|
+
head: tpl.head,
|
|
946
|
+
title: tpl.settings.pdf?.metadata?.title
|
|
947
|
+
},
|
|
948
|
+
ua
|
|
949
|
+
)) {
|
|
950
|
+
if (!found.location) {
|
|
951
|
+
out.push(about(found.severity, found.code, found.message));
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
body.push({
|
|
955
|
+
severity: found.severity,
|
|
956
|
+
code: found.code,
|
|
957
|
+
message: found.message,
|
|
958
|
+
range: { start: found.location, end: found.location }
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
if (body.length)
|
|
962
|
+
out.push({ file: templateFileName(tpl.meta.kind), diagnostics: body });
|
|
963
|
+
return out;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// packages/devkit/src/lib/files.ts
|
|
967
|
+
var import_node_crypto2 = require("node:crypto");
|
|
968
|
+
var import_node_fs5 = require("node:fs");
|
|
969
|
+
var import_node_path5 = require("node:path");
|
|
816
970
|
|
|
817
971
|
// packages/api-types/src/lib/assets.ts
|
|
818
972
|
var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._-]*";
|
|
@@ -844,25 +998,25 @@ function sha256File(bytes) {
|
|
|
844
998
|
}
|
|
845
999
|
function listLocalFiles(project) {
|
|
846
1000
|
const listing = { files: [], skipped: [] };
|
|
847
|
-
if (!(0,
|
|
1001
|
+
if (!(0, import_node_fs5.existsSync)(project.filesDir)) return listing;
|
|
848
1002
|
const walk2 = (dir) => {
|
|
849
|
-
for (const entry of (0,
|
|
1003
|
+
for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
|
|
850
1004
|
if (entry.name.startsWith(".")) continue;
|
|
851
|
-
const path = (0,
|
|
1005
|
+
const path = (0, import_node_path5.join)(dir, entry.name);
|
|
852
1006
|
if (entry.isDirectory()) {
|
|
853
1007
|
walk2(path);
|
|
854
1008
|
continue;
|
|
855
1009
|
}
|
|
856
1010
|
if (!entry.isFile()) continue;
|
|
857
|
-
const name = (0,
|
|
858
|
-
const size = (0,
|
|
1011
|
+
const name = (0, import_node_path5.relative)(project.filesDir, path).split(import_node_path5.sep).join("/");
|
|
1012
|
+
const size = (0, import_node_fs5.statSync)(path).size;
|
|
859
1013
|
const contentType = contentTypeFor(name);
|
|
860
1014
|
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;
|
|
861
1015
|
if (reason) {
|
|
862
1016
|
listing.skipped.push({ path, reason });
|
|
863
1017
|
continue;
|
|
864
1018
|
}
|
|
865
|
-
const bytes = (0,
|
|
1019
|
+
const bytes = (0, import_node_fs5.readFileSync)(path);
|
|
866
1020
|
listing.files.push({ name, path, bytes: size, sha256: sha256File(bytes), contentType });
|
|
867
1021
|
}
|
|
868
1022
|
};
|
|
@@ -872,14 +1026,14 @@ function listLocalFiles(project) {
|
|
|
872
1026
|
}
|
|
873
1027
|
function localFilePath(project, name) {
|
|
874
1028
|
if (!isAssetName(name)) throw new DevkitError(`"${name}" is not a library file name`);
|
|
875
|
-
const path = (0,
|
|
876
|
-
if (!path.startsWith((0,
|
|
1029
|
+
const path = (0, import_node_path5.resolve)(project.filesDir, ...name.split("/"));
|
|
1030
|
+
if (!path.startsWith((0, import_node_path5.resolve)(project.filesDir) + import_node_path5.sep)) throw new DevkitError(`"${name}" is not inside the files folder`);
|
|
877
1031
|
return path;
|
|
878
1032
|
}
|
|
879
1033
|
function writeLocalFile(project, name, bytes) {
|
|
880
1034
|
const path = localFilePath(project, name);
|
|
881
|
-
(0,
|
|
882
|
-
(0,
|
|
1035
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(path), { recursive: true });
|
|
1036
|
+
(0, import_node_fs5.writeFileSync)(path, bytes);
|
|
883
1037
|
return path;
|
|
884
1038
|
}
|
|
885
1039
|
|
|
@@ -947,7 +1101,7 @@ function isRecord(value) {
|
|
|
947
1101
|
}
|
|
948
1102
|
|
|
949
1103
|
// packages/devkit/src/lib/types/index.ts
|
|
950
|
-
var
|
|
1104
|
+
var import_engine7 = require("@formfeed/engine");
|
|
951
1105
|
|
|
952
1106
|
// packages/devkit/src/lib/types/model.ts
|
|
953
1107
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1435,7 +1589,7 @@ function localTypeSource(tpl) {
|
|
|
1435
1589
|
const sets = Object.values(tpl.dataSets);
|
|
1436
1590
|
return {
|
|
1437
1591
|
slug: tpl.slug,
|
|
1438
|
-
schema: (0,
|
|
1592
|
+
schema: (0, import_engine7.inferSchemaFromDataSets)(sets.length ? sets : [{}]),
|
|
1439
1593
|
origin: "inferred"
|
|
1440
1594
|
};
|
|
1441
1595
|
}
|
|
@@ -1447,10 +1601,13 @@ function localTypeSource(tpl) {
|
|
|
1447
1601
|
collectPartials,
|
|
1448
1602
|
contentHash,
|
|
1449
1603
|
contentTypeFor,
|
|
1604
|
+
declaredEinvoice,
|
|
1450
1605
|
defaultData,
|
|
1451
1606
|
defaultProjectConfig,
|
|
1452
1607
|
defaultTypesFile,
|
|
1453
1608
|
diagnose,
|
|
1609
|
+
diagnoseAccessibility,
|
|
1610
|
+
diagnoseEinvoice,
|
|
1454
1611
|
findProject,
|
|
1455
1612
|
generateTypes,
|
|
1456
1613
|
htmlSnapshot,
|
|
@@ -1458,6 +1615,7 @@ function localTypeSource(tpl) {
|
|
|
1458
1615
|
listLocalFiles,
|
|
1459
1616
|
listTemplateSlugs,
|
|
1460
1617
|
loadProject,
|
|
1618
|
+
localEinvoiceXml,
|
|
1461
1619
|
localFilePath,
|
|
1462
1620
|
localTypeSource,
|
|
1463
1621
|
officeSnapshot,
|
package/index.js
CHANGED
|
@@ -543,7 +543,8 @@ import {
|
|
|
543
543
|
pagedDocument,
|
|
544
544
|
parseDesign,
|
|
545
545
|
renderOffice,
|
|
546
|
-
renderVersion
|
|
546
|
+
renderVersion,
|
|
547
|
+
resolveUa
|
|
547
548
|
} from "@formfeed/engine";
|
|
548
549
|
function diagnose(tpl, sampleData) {
|
|
549
550
|
if (tpl.file) return diagnoseOffice(tpl, tpl.file.bytes, sampleData);
|
|
@@ -757,13 +758,175 @@ function previewDocument(tpl, rendered, mode, pagedScriptUrl) {
|
|
|
757
758
|
settings: rendered.settings,
|
|
758
759
|
kind
|
|
759
760
|
};
|
|
760
|
-
|
|
761
|
+
const audit = kind === "pdf" && resolveUa(rendered.settings.pdf?.ua) !== null;
|
|
762
|
+
return mode === "paged" && kind === "pdf" ? pagedDocument(draft, { pagedScriptUrl, audit }) : flowDocument(draft, { audit });
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// packages/devkit/src/lib/einvoice.ts
|
|
766
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
767
|
+
import { join as join4 } from "node:path";
|
|
768
|
+
import {
|
|
769
|
+
INVOICE_DATA_KEY,
|
|
770
|
+
einvoiceXml,
|
|
771
|
+
invoicePathSegments,
|
|
772
|
+
locateJsonPath,
|
|
773
|
+
resolveEinvoiceOptions
|
|
774
|
+
} from "@formfeed/engine";
|
|
775
|
+
function declaredEinvoice(tpl, override) {
|
|
776
|
+
const resolved = resolveEinvoiceOptions(tpl.settings.einvoice, override);
|
|
777
|
+
if (!resolved.ok)
|
|
778
|
+
throw new DevkitError(
|
|
779
|
+
`${tpl.slug}: e-invoice ${resolved.field} "${resolved.value}": ${resolved.reason}`,
|
|
780
|
+
"validation"
|
|
781
|
+
);
|
|
782
|
+
return resolved.options;
|
|
783
|
+
}
|
|
784
|
+
var nowhere2 = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
785
|
+
function diagnoseEinvoice(tpl) {
|
|
786
|
+
let options;
|
|
787
|
+
try {
|
|
788
|
+
options = declaredEinvoice(tpl);
|
|
789
|
+
} catch (e) {
|
|
790
|
+
return [
|
|
791
|
+
{
|
|
792
|
+
dataSet: "",
|
|
793
|
+
file: "settings.json",
|
|
794
|
+
diagnostics: [
|
|
795
|
+
{
|
|
796
|
+
severity: "error",
|
|
797
|
+
code: "einvoice-profile",
|
|
798
|
+
message: e instanceof Error ? e.message : String(e),
|
|
799
|
+
range: nowhere2
|
|
800
|
+
}
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
];
|
|
804
|
+
}
|
|
805
|
+
if (!options) return [];
|
|
806
|
+
if (tpl.meta.kind === "image")
|
|
807
|
+
return [
|
|
808
|
+
{
|
|
809
|
+
dataSet: "",
|
|
810
|
+
file: "settings.json",
|
|
811
|
+
diagnostics: [
|
|
812
|
+
{
|
|
813
|
+
severity: "warning",
|
|
814
|
+
code: "einvoice-image",
|
|
815
|
+
message: "settings.json declares an e-invoice, which an image template never produces: an e-invoice is a PDF",
|
|
816
|
+
range: nowhere2
|
|
817
|
+
}
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
];
|
|
821
|
+
const out = [];
|
|
822
|
+
for (const [name, data] of Object.entries(tpl.dataSets)) {
|
|
823
|
+
const result = einvoiceXml(data, options);
|
|
824
|
+
if (result.ok) continue;
|
|
825
|
+
const file = `data/${name}.json`;
|
|
826
|
+
const path = join4(tpl.dir, "data", `${name}.json`);
|
|
827
|
+
const text2 = existsSync4(path) ? readFileSync4(path, "utf8") : null;
|
|
828
|
+
out.push({
|
|
829
|
+
dataSet: name,
|
|
830
|
+
file,
|
|
831
|
+
diagnostics: result.problems.map((problem) => {
|
|
832
|
+
const at = text2 ? locateJsonPath(text2, [
|
|
833
|
+
INVOICE_DATA_KEY,
|
|
834
|
+
...invoicePathSegments(problem.path)
|
|
835
|
+
]) : null;
|
|
836
|
+
const start = at ? { line: at.line, column: at.column } : nowhere2.start;
|
|
837
|
+
return {
|
|
838
|
+
severity: "error",
|
|
839
|
+
code: `einvoice-${problem.code.replace(/_/g, "-")}`,
|
|
840
|
+
message: problem.message,
|
|
841
|
+
range: { start, end: start }
|
|
842
|
+
};
|
|
843
|
+
})
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
return out;
|
|
847
|
+
}
|
|
848
|
+
function localEinvoiceXml(tpl, data, override) {
|
|
849
|
+
const options = declaredEinvoice(tpl, override ?? {});
|
|
850
|
+
if (!options)
|
|
851
|
+
throw new DevkitError(`${tpl.slug} declares no e-invoice`, "validation");
|
|
852
|
+
const result = einvoiceXml(data, options);
|
|
853
|
+
if (!result.ok)
|
|
854
|
+
throw new DevkitError(
|
|
855
|
+
[
|
|
856
|
+
`${tpl.slug}: the _invoice block does not make an e-invoice`,
|
|
857
|
+
...result.problems.map((problem) => ` ${problem.message}`)
|
|
858
|
+
].join("\n"),
|
|
859
|
+
"validation"
|
|
860
|
+
);
|
|
861
|
+
return { xml: result.xml, invoice: result.invoice, options };
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// packages/devkit/src/lib/accessibility.ts
|
|
865
|
+
import {
|
|
866
|
+
UA_REFUSALS,
|
|
867
|
+
isOfficeKind as isOfficeKind3,
|
|
868
|
+
isUaSetting,
|
|
869
|
+
resolveUa as resolveUa2,
|
|
870
|
+
staticAuditDiagnostics
|
|
871
|
+
} from "@formfeed/engine";
|
|
872
|
+
var nowhere3 = { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } };
|
|
873
|
+
var about = (severity, code, message) => ({
|
|
874
|
+
file: "settings.json",
|
|
875
|
+
diagnostics: [{ severity, code, message, range: nowhere3 }]
|
|
876
|
+
});
|
|
877
|
+
function diagnoseAccessibility(tpl) {
|
|
878
|
+
const declared = tpl.settings.pdf?.ua;
|
|
879
|
+
if (declared !== void 0 && !isUaSetting(declared))
|
|
880
|
+
return [
|
|
881
|
+
about(
|
|
882
|
+
"error",
|
|
883
|
+
"accessibility-setting",
|
|
884
|
+
'pdf.ua is true, false or { "check": "strict" | "report" }'
|
|
885
|
+
)
|
|
886
|
+
];
|
|
887
|
+
const ua = resolveUa2(declared);
|
|
888
|
+
if (!ua) return [];
|
|
889
|
+
if (tpl.meta.kind === "image")
|
|
890
|
+
return [
|
|
891
|
+
about(
|
|
892
|
+
"warning",
|
|
893
|
+
"accessibility-image",
|
|
894
|
+
"settings.json declares pdf.ua, which an image template never produces: PDF/UA is a format of PDF"
|
|
895
|
+
)
|
|
896
|
+
];
|
|
897
|
+
if (isOfficeKind3(tpl.meta.kind))
|
|
898
|
+
return [about("error", "accessibility-office", UA_REFUSALS.office)];
|
|
899
|
+
const out = [];
|
|
900
|
+
const body = [];
|
|
901
|
+
for (const found of staticAuditDiagnostics(
|
|
902
|
+
{
|
|
903
|
+
engine: tpl.meta.engine,
|
|
904
|
+
html: tpl.html,
|
|
905
|
+
head: tpl.head,
|
|
906
|
+
title: tpl.settings.pdf?.metadata?.title
|
|
907
|
+
},
|
|
908
|
+
ua
|
|
909
|
+
)) {
|
|
910
|
+
if (!found.location) {
|
|
911
|
+
out.push(about(found.severity, found.code, found.message));
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
body.push({
|
|
915
|
+
severity: found.severity,
|
|
916
|
+
code: found.code,
|
|
917
|
+
message: found.message,
|
|
918
|
+
range: { start: found.location, end: found.location }
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
if (body.length)
|
|
922
|
+
out.push({ file: templateFileName(tpl.meta.kind), diagnostics: body });
|
|
923
|
+
return out;
|
|
761
924
|
}
|
|
762
925
|
|
|
763
926
|
// packages/devkit/src/lib/files.ts
|
|
764
927
|
import { createHash as createHash2 } from "node:crypto";
|
|
765
|
-
import { existsSync as
|
|
766
|
-
import { dirname as dirname3, join as
|
|
928
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
929
|
+
import { dirname as dirname3, join as join5, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
767
930
|
|
|
768
931
|
// packages/api-types/src/lib/assets.ts
|
|
769
932
|
var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._-]*";
|
|
@@ -795,11 +958,11 @@ function sha256File(bytes) {
|
|
|
795
958
|
}
|
|
796
959
|
function listLocalFiles(project) {
|
|
797
960
|
const listing = { files: [], skipped: [] };
|
|
798
|
-
if (!
|
|
961
|
+
if (!existsSync5(project.filesDir)) return listing;
|
|
799
962
|
const walk2 = (dir) => {
|
|
800
963
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
801
964
|
if (entry.name.startsWith(".")) continue;
|
|
802
|
-
const path =
|
|
965
|
+
const path = join5(dir, entry.name);
|
|
803
966
|
if (entry.isDirectory()) {
|
|
804
967
|
walk2(path);
|
|
805
968
|
continue;
|
|
@@ -813,7 +976,7 @@ function listLocalFiles(project) {
|
|
|
813
976
|
listing.skipped.push({ path, reason });
|
|
814
977
|
continue;
|
|
815
978
|
}
|
|
816
|
-
const bytes =
|
|
979
|
+
const bytes = readFileSync5(path);
|
|
817
980
|
listing.files.push({ name, path, bytes: size, sha256: sha256File(bytes), contentType });
|
|
818
981
|
}
|
|
819
982
|
};
|
|
@@ -1397,10 +1560,13 @@ export {
|
|
|
1397
1560
|
collectPartials,
|
|
1398
1561
|
contentHash,
|
|
1399
1562
|
contentTypeFor,
|
|
1563
|
+
declaredEinvoice,
|
|
1400
1564
|
defaultData,
|
|
1401
1565
|
defaultProjectConfig,
|
|
1402
1566
|
defaultTypesFile,
|
|
1403
1567
|
diagnose,
|
|
1568
|
+
diagnoseAccessibility,
|
|
1569
|
+
diagnoseEinvoice,
|
|
1404
1570
|
findProject,
|
|
1405
1571
|
generateTypes,
|
|
1406
1572
|
htmlSnapshot,
|
|
@@ -1408,6 +1574,7 @@ export {
|
|
|
1408
1574
|
listLocalFiles,
|
|
1409
1575
|
listTemplateSlugs,
|
|
1410
1576
|
loadProject,
|
|
1577
|
+
localEinvoiceXml,
|
|
1411
1578
|
localFilePath,
|
|
1412
1579
|
localTypeSource,
|
|
1413
1580
|
officeSnapshot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formfeed/devkit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"description": "Formfeed template folders, project config and local rendering, shared by the CLI and @formfeed/testing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
],
|
|
31
31
|
"sideEffects": false,
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@formfeed/engine": "0.3.
|
|
34
|
-
"@formfeed/sdk": "0.3.
|
|
33
|
+
"@formfeed/engine": "0.3.13",
|
|
34
|
+
"@formfeed/sdk": "0.3.13"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
37
37
|
"formfeed",
|
package/types/index.d.cts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
export { DevkitError } from './lib/errors.cjs';
|
|
2
|
-
export { defaultProjectConfig, findProject, isIgnored, loadProject, projectAround, writeProjectConfig } from './lib/project-config.cjs';
|
|
2
|
+
export { defaultProjectConfig, findProject, isIgnored, loadProject, projectAround, writeProjectConfig, } from './lib/project-config.cjs';
|
|
3
3
|
export type { Project, ProjectConfig } from './lib/project-config.cjs';
|
|
4
4
|
export { contentHash, defaultData, listTemplateSlugs, officeVersionPayload, partialContentHash, partialResolver, readState, readTemplate, recordSharedPartial, recordSync, sharedPartialNames, sharedPartialPath, templateDir, templateFileName, titleFromSlug, versionPayload, writePartials, writeState, writeTemplate, } from './lib/project.cjs';
|
|
5
|
-
export type { LocalOfficeFile, LocalTemplate, ProjectState, SharedPartialState, TemplateMeta, TemplateState } from './lib/project.cjs';
|
|
5
|
+
export type { LocalOfficeFile, LocalTemplate, ProjectState, SharedPartialState, TemplateMeta, TemplateState, } from './lib/project.cjs';
|
|
6
6
|
export { brandFromJson, brandPath, readBrand, writeBrand } from './lib/brand.cjs';
|
|
7
7
|
export { collectPartials } from './lib/partials.cjs';
|
|
8
8
|
export type { CollectedPartials } from './lib/partials.cjs';
|
|
9
|
-
export { diagnose, htmlSnapshot, officeSnapshot, prettyXml, previewDocument, renderContext, renderLocal, renderOfficeLocal, renderedSettings } from './lib/local-render.cjs';
|
|
9
|
+
export { diagnose, htmlSnapshot, officeSnapshot, prettyXml, previewDocument, renderContext, renderLocal, renderOfficeLocal, renderedSettings, } from './lib/local-render.cjs';
|
|
10
10
|
export type { LocalDiagnostic, OfficeLocalOptions } from './lib/local-render.cjs';
|
|
11
|
-
export {
|
|
11
|
+
export { declaredEinvoice, diagnoseEinvoice, localEinvoiceXml, } from './lib/einvoice.cjs';
|
|
12
|
+
export type { DataSetDiagnostics } from './lib/einvoice.cjs';
|
|
13
|
+
export { diagnoseAccessibility } from './lib/accessibility.cjs';
|
|
14
|
+
export type { FileDiagnostics } from './lib/accessibility.cjs';
|
|
15
|
+
export { contentTypeFor, listLocalFiles, localFilePath, sha256File, writeLocalFile, } from './lib/files.cjs';
|
|
12
16
|
export type { LocalFile, LocalFileListing } from './lib/files.cjs';
|
|
13
17
|
export type { LocalRenderOptions } from './lib/local-render.cjs';
|
|
14
18
|
export { parseRedactPath, redactData, redactString } from './lib/redact.cjs';
|
|
15
19
|
export { defaultTypesFile, generateTypes, localTypeSource } from './lib/types/index.cjs';
|
|
16
|
-
export type { SchemaOrigin, TypeScriptOptions, TypeSource, TypesLanguage } from './lib/types/index.cjs';
|
|
20
|
+
export type { SchemaOrigin, TypeScriptOptions, TypeSource, TypesLanguage, } from './lib/types/index.cjs';
|
package/types/index.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
export { DevkitError } from './lib/errors.js';
|
|
2
|
-
export { defaultProjectConfig, findProject, isIgnored, loadProject, projectAround, writeProjectConfig } from './lib/project-config.js';
|
|
2
|
+
export { defaultProjectConfig, findProject, isIgnored, loadProject, projectAround, writeProjectConfig, } from './lib/project-config.js';
|
|
3
3
|
export type { Project, ProjectConfig } from './lib/project-config.js';
|
|
4
4
|
export { contentHash, defaultData, listTemplateSlugs, officeVersionPayload, partialContentHash, partialResolver, readState, readTemplate, recordSharedPartial, recordSync, sharedPartialNames, sharedPartialPath, templateDir, templateFileName, titleFromSlug, versionPayload, writePartials, writeState, writeTemplate, } from './lib/project.js';
|
|
5
|
-
export type { LocalOfficeFile, LocalTemplate, ProjectState, SharedPartialState, TemplateMeta, TemplateState } from './lib/project.js';
|
|
5
|
+
export type { LocalOfficeFile, LocalTemplate, ProjectState, SharedPartialState, TemplateMeta, TemplateState, } from './lib/project.js';
|
|
6
6
|
export { brandFromJson, brandPath, readBrand, writeBrand } from './lib/brand.js';
|
|
7
7
|
export { collectPartials } from './lib/partials.js';
|
|
8
8
|
export type { CollectedPartials } from './lib/partials.js';
|
|
9
|
-
export { diagnose, htmlSnapshot, officeSnapshot, prettyXml, previewDocument, renderContext, renderLocal, renderOfficeLocal, renderedSettings } from './lib/local-render.js';
|
|
9
|
+
export { diagnose, htmlSnapshot, officeSnapshot, prettyXml, previewDocument, renderContext, renderLocal, renderOfficeLocal, renderedSettings, } from './lib/local-render.js';
|
|
10
10
|
export type { LocalDiagnostic, OfficeLocalOptions } from './lib/local-render.js';
|
|
11
|
-
export {
|
|
11
|
+
export { declaredEinvoice, diagnoseEinvoice, localEinvoiceXml, } from './lib/einvoice.js';
|
|
12
|
+
export type { DataSetDiagnostics } from './lib/einvoice.js';
|
|
13
|
+
export { diagnoseAccessibility } from './lib/accessibility.js';
|
|
14
|
+
export type { FileDiagnostics } from './lib/accessibility.js';
|
|
15
|
+
export { contentTypeFor, listLocalFiles, localFilePath, sha256File, writeLocalFile, } from './lib/files.js';
|
|
12
16
|
export type { LocalFile, LocalFileListing } from './lib/files.js';
|
|
13
17
|
export type { LocalRenderOptions } from './lib/local-render.js';
|
|
14
18
|
export { parseRedactPath, redactData, redactString } from './lib/redact.js';
|
|
15
19
|
export { defaultTypesFile, generateTypes, localTypeSource } from './lib/types/index.js';
|
|
16
|
-
export type { SchemaOrigin, TypeScriptOptions, TypeSource, TypesLanguage } from './lib/types/index.js';
|
|
20
|
+
export type { SchemaOrigin, TypeScriptOptions, TypeSource, TypesLanguage, } from './lib/types/index.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LocalDiagnostic } from './local-render.cjs';
|
|
2
|
+
import { type LocalTemplate } from './project.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* `pdf.ua` of a template folder (plan 21 §6): what can be said about a template that declares
|
|
5
|
+
* PDF/UA-1 without rendering it. `formfeed validate` and the MCP server's validation report it. It
|
|
6
|
+
* is the static half of the template check: a document without a title, an image written without
|
|
7
|
+
* `alt`, and a declaration the API will refuse. What depends on the data (heading levels, table
|
|
8
|
+
* rows, a chart's name) is found in the rendered page, by the editor's preview and by the render.
|
|
9
|
+
*/
|
|
10
|
+
/** The findings of one file of a template folder. */
|
|
11
|
+
export interface FileDiagnostics {
|
|
12
|
+
/** Relative to the template folder: `template.html`, `settings.json`. */
|
|
13
|
+
file: string;
|
|
14
|
+
diagnostics: LocalDiagnostic[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Empty for a template that does not declare `pdf.ua`. A fault the validator fails the file for is
|
|
18
|
+
* an error where the template is strict, since every render of it then fails, and a warning where
|
|
19
|
+
* it only reports; what a reader stumbles over without a rule failing is a warning either way.
|
|
20
|
+
*/
|
|
21
|
+
export declare function diagnoseAccessibility(tpl: LocalTemplate): FileDiagnostics[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LocalDiagnostic } from './local-render.js';
|
|
2
|
+
import { type LocalTemplate } from './project.js';
|
|
3
|
+
/**
|
|
4
|
+
* `pdf.ua` of a template folder (plan 21 §6): what can be said about a template that declares
|
|
5
|
+
* PDF/UA-1 without rendering it. `formfeed validate` and the MCP server's validation report it. It
|
|
6
|
+
* is the static half of the template check: a document without a title, an image written without
|
|
7
|
+
* `alt`, and a declaration the API will refuse. What depends on the data (heading levels, table
|
|
8
|
+
* rows, a chart's name) is found in the rendered page, by the editor's preview and by the render.
|
|
9
|
+
*/
|
|
10
|
+
/** The findings of one file of a template folder. */
|
|
11
|
+
export interface FileDiagnostics {
|
|
12
|
+
/** Relative to the template folder: `template.html`, `settings.json`. */
|
|
13
|
+
file: string;
|
|
14
|
+
diagnostics: LocalDiagnostic[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Empty for a template that does not declare `pdf.ua`. A fault the validator fails the file for is
|
|
18
|
+
* an error where the template is strict, since every render of it then fails, and a warning where
|
|
19
|
+
* it only reports; what a reader stumbles over without a rule failing is a warning either way.
|
|
20
|
+
*/
|
|
21
|
+
export declare function diagnoseAccessibility(tpl: LocalTemplate): FileDiagnostics[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type EinvoiceOptions, type EinvoiceOptionsInput, type Invoice } from '@formfeed/engine';
|
|
2
|
+
import type { LocalDiagnostic } from './local-render.cjs';
|
|
3
|
+
import type { LocalTemplate } from './project.cjs';
|
|
4
|
+
/**
|
|
5
|
+
* The e-invoice of a template folder (spec 17): what `settings.json` declares, the check of every
|
|
6
|
+
* data set against it, and the XML itself. All of it runs offline, with the code the API runs, so
|
|
7
|
+
* what `formfeed validate` accepts the API accepts too.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The e-invoice a template declares in `settings.json`, with every default filled in; `null` when
|
|
11
|
+
* it declares none. `override` is merged over it like a request's `post.einvoice`; `{}` asks for an
|
|
12
|
+
* e-invoice with the defaults from a template that declares nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function declaredEinvoice(tpl: LocalTemplate, override?: EinvoiceOptionsInput | false): EinvoiceOptions | null;
|
|
15
|
+
/** The findings of one data set, with the file they are in. */
|
|
16
|
+
export interface DataSetDiagnostics {
|
|
17
|
+
dataSet: string;
|
|
18
|
+
/** Relative to the template folder: `data/default.json`. */
|
|
19
|
+
file: string;
|
|
20
|
+
diagnostics: LocalDiagnostic[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Every data set of an invoice template against the `_invoice` schema and the rules of EN 16931
|
|
24
|
+
* (spec 17 §8). Each is an error: a render with that data fails with `einvoice_data_invalid`, and a
|
|
25
|
+
* template that declares an e-invoice renders nothing else. Empty for a template that declares
|
|
26
|
+
* none; one warning for an image template, where the declaration has no effect.
|
|
27
|
+
*/
|
|
28
|
+
export declare function diagnoseEinvoice(tpl: LocalTemplate): DataSetDiagnostics[];
|
|
29
|
+
/**
|
|
30
|
+
* The XML of one data set, as a render of the template would embed it. Throws a validation error
|
|
31
|
+
* that lists what is wrong with the block when it does not pass.
|
|
32
|
+
*/
|
|
33
|
+
export declare function localEinvoiceXml(tpl: LocalTemplate, data: unknown, override?: EinvoiceOptionsInput): {
|
|
34
|
+
xml: string;
|
|
35
|
+
invoice: Invoice;
|
|
36
|
+
options: EinvoiceOptions;
|
|
37
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type EinvoiceOptions, type EinvoiceOptionsInput, type Invoice } from '@formfeed/engine';
|
|
2
|
+
import type { LocalDiagnostic } from './local-render.js';
|
|
3
|
+
import type { LocalTemplate } from './project.js';
|
|
4
|
+
/**
|
|
5
|
+
* The e-invoice of a template folder (spec 17): what `settings.json` declares, the check of every
|
|
6
|
+
* data set against it, and the XML itself. All of it runs offline, with the code the API runs, so
|
|
7
|
+
* what `formfeed validate` accepts the API accepts too.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The e-invoice a template declares in `settings.json`, with every default filled in; `null` when
|
|
11
|
+
* it declares none. `override` is merged over it like a request's `post.einvoice`; `{}` asks for an
|
|
12
|
+
* e-invoice with the defaults from a template that declares nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function declaredEinvoice(tpl: LocalTemplate, override?: EinvoiceOptionsInput | false): EinvoiceOptions | null;
|
|
15
|
+
/** The findings of one data set, with the file they are in. */
|
|
16
|
+
export interface DataSetDiagnostics {
|
|
17
|
+
dataSet: string;
|
|
18
|
+
/** Relative to the template folder: `data/default.json`. */
|
|
19
|
+
file: string;
|
|
20
|
+
diagnostics: LocalDiagnostic[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Every data set of an invoice template against the `_invoice` schema and the rules of EN 16931
|
|
24
|
+
* (spec 17 §8). Each is an error: a render with that data fails with `einvoice_data_invalid`, and a
|
|
25
|
+
* template that declares an e-invoice renders nothing else. Empty for a template that declares
|
|
26
|
+
* none; one warning for an image template, where the declaration has no effect.
|
|
27
|
+
*/
|
|
28
|
+
export declare function diagnoseEinvoice(tpl: LocalTemplate): DataSetDiagnostics[];
|
|
29
|
+
/**
|
|
30
|
+
* The XML of one data set, as a render of the template would embed it. Throws a validation error
|
|
31
|
+
* that lists what is wrong with the block when it does not pass.
|
|
32
|
+
*/
|
|
33
|
+
export declare function localEinvoiceXml(tpl: LocalTemplate, data: unknown, override?: EinvoiceOptionsInput): {
|
|
34
|
+
xml: string;
|
|
35
|
+
invoice: Invoice;
|
|
36
|
+
options: EinvoiceOptions;
|
|
37
|
+
};
|