@jasy/pdf 1.0.0-beta.6 → 1.0.0-beta.7
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/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/assets/standard-fonts.d.ts +4 -0
- package/dist/assets/standard-fonts.js +2365 -0
- package/dist/edit/fill.js +2 -0
- package/dist/edit/flatten.js +2 -0
- package/dist/renderer/pdf-document-class.js +3 -0
- package/dist/renderer/pdf-renderer.js +3 -0
- package/dist/utils/afm-parser.d.ts +30 -16
- package/dist/utils/afm-parser.js +83 -113
- package/dist/utils/pdf-object-manager.js +3 -5
- package/package.json +2 -4
- package/dist/assets/Courier-Bold.afm +0 -342
- package/dist/assets/Courier-BoldOblique.afm +0 -342
- package/dist/assets/Courier-Oblique.afm +0 -342
- package/dist/assets/Courier.afm +0 -342
- package/dist/assets/Helvetica-Bold.afm +0 -2827
- package/dist/assets/Helvetica-BoldOblique.afm +0 -2827
- package/dist/assets/Helvetica-Oblique.afm +0 -3051
- package/dist/assets/Helvetica.afm +0 -3051
- package/dist/assets/Symbol.afm +0 -213
- package/dist/assets/Times-Bold.afm +0 -2588
- package/dist/assets/Times-BoldItalic.afm +0 -2384
- package/dist/assets/Times-Italic.afm +0 -2667
- package/dist/assets/Times-Roman.afm +0 -2419
- package/dist/assets/ZapfDingbats.afm +0 -225
- package/dist/assets/agl.txt +0 -695
- package/dist/assets/font-data.d.ts +0 -2
- package/dist/assets/font-data.js +0 -21
- package/dist/assets/font-data.ts +0 -37
package/dist/edit/fill.js
CHANGED
|
@@ -6,6 +6,7 @@ import { flattenInto } from "./flatten.js";
|
|
|
6
6
|
import { getArrayBuffer } from "../utils/utf8-to-windows1252-encoder.js";
|
|
7
7
|
import { get, isDict, isRef } from "./objects.js";
|
|
8
8
|
import { encodePdfText } from "../text/pdf-text-string.js";
|
|
9
|
+
import { preloadStandardFonts } from "../utils/afm-parser.js";
|
|
9
10
|
/** Field flags we have to honour when filling (see `forms/acroform.ts` for the writer's side). */
|
|
10
11
|
const FF_READ_ONLY = 1 << 0;
|
|
11
12
|
const FF_MULTI_SELECT = 1 << 21;
|
|
@@ -227,6 +228,7 @@ function plan(field, value, enciphered) {
|
|
|
227
228
|
* @param values field name -> value. Names are the fully qualified ones `readAcroForm` reports.
|
|
228
229
|
*/
|
|
229
230
|
export async function fillForm(bytes, values, options = {}) {
|
|
231
|
+
await preloadStandardFonts(); // a field's new face is measured in Helvetica
|
|
230
232
|
// Two option combinations describe a document nobody wants. Refused before the file is opened, since
|
|
231
233
|
// the answer does not depend on it.
|
|
232
234
|
if (options.fieldAppearances === false && options.needAppearances === false) {
|
package/dist/edit/flatten.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { preloadStandardFonts } from "../utils/afm-parser.js";
|
|
1
2
|
import { PdfDocument } from "./document.js";
|
|
2
3
|
import { readAcroForm } from "./acroform-reader.js";
|
|
3
4
|
import { bakeAppearance, bakeButtonStates, readLook } from "./appearance.js";
|
|
@@ -430,6 +431,7 @@ function pruneKids(doc, list, gone, out = [], depth = 0) {
|
|
|
430
431
|
}
|
|
431
432
|
/** Flatten an existing form: its values become page content and stop being editable. */
|
|
432
433
|
export async function flattenForm(bytes, options = {}) {
|
|
434
|
+
await preloadStandardFonts(); // a widget without a face gets one, measured in Helvetica
|
|
433
435
|
const doc = await PdfDocument.open(bytes, {
|
|
434
436
|
password: options.password,
|
|
435
437
|
maxStreamSize: options.maxStreamSize,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { preloadStandardFonts } from "../utils/afm-parser.js";
|
|
1
2
|
import { FontStyle, PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
2
3
|
import { PDFRenderer } from "./pdf-renderer.js";
|
|
3
4
|
// Re-exported so existing `../renderer` consumers keep importing these from here.
|
|
@@ -97,6 +98,8 @@ export class PDFDocument {
|
|
|
97
98
|
}
|
|
98
99
|
beforeRenderer() { }
|
|
99
100
|
static async render() {
|
|
101
|
+
// Before build(): a subclass may measure text there, through `objectManager`.
|
|
102
|
+
await preloadStandardFonts();
|
|
100
103
|
const instance = new this();
|
|
101
104
|
instance.child = instance.build();
|
|
102
105
|
return await PDFRenderer.render(instance.child, instance._objectManager);
|
|
@@ -52,9 +52,12 @@ import { RotatedElement } from "../elements/layout/rotated-element.js";
|
|
|
52
52
|
import { RotatedBoxElement } from "../elements/layout/rotated-box-element.js";
|
|
53
53
|
import { RotatedRenderer } from "./rotated-renderer.js";
|
|
54
54
|
import { collectImageElements } from "../layout/collect-images.js";
|
|
55
|
+
import { preloadStandardFonts } from "../utils/afm-parser.js";
|
|
55
56
|
import { DEFAULT_TEXT_STYLE, mergeTextStyle } from "../text/text-style.js";
|
|
56
57
|
export class PDFRenderer {
|
|
57
58
|
static async render(document, objectManager) {
|
|
59
|
+
// Layout measures synchronously, so the standard font metrics are loaded before it starts.
|
|
60
|
+
await preloadStandardFonts();
|
|
58
61
|
// Register all Renderer
|
|
59
62
|
RendererRegistry.register(TextElement, TextRenderer.render);
|
|
60
63
|
RendererRegistry.register(ContainerElement, ContainerRenderer.render);
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import type { FontVerticals } from "../text/line-metrics.js";
|
|
2
2
|
import type { FontDecoration } from "../text/text-decoration.js";
|
|
3
|
-
|
|
3
|
+
/** One standard font's metrics, pre-parsed from its AFM file by scripts/gen-font-data.mjs. */
|
|
4
|
+
export interface StandardFontData {
|
|
5
|
+
/** FontBBox, in glyph units (1000 / em). */
|
|
6
|
+
bbox: number[];
|
|
7
|
+
/** UnderlinePosition (negative: below the baseline), UnderlineThickness. */
|
|
8
|
+
underline: number[];
|
|
9
|
+
capHeight: number;
|
|
10
|
+
xHeight: number;
|
|
11
|
+
/** The advance of every glyph by index; 0 where the font has none. */
|
|
12
|
+
widths: number[];
|
|
13
|
+
/** [first, count, second - previous second, value, ...] */
|
|
14
|
+
kerning: number[];
|
|
15
|
+
}
|
|
16
|
+
/** The 14 names, known without their data: registering a font must not wait for it. */
|
|
17
|
+
export declare const STANDARD_FONT_NAMES: ReadonlySet<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Loads the metrics of the 14 standard fonts. Every render does this itself; call it ahead of time to
|
|
20
|
+
* have the first PDF without the wait, or to measure outside a render. Once per process.
|
|
21
|
+
*/
|
|
22
|
+
export declare function preloadStandardFonts(): Promise<void>;
|
|
23
|
+
export declare function standardParser(fullName: string): AFMParser;
|
|
24
|
+
/** A standard font's metrics, read on first use - by then a render has loaded them. */
|
|
4
25
|
export declare class AFMParser {
|
|
5
|
-
private
|
|
6
|
-
private
|
|
7
|
-
private glyphMap;
|
|
8
|
-
private bbox;
|
|
26
|
+
private readonly fullName;
|
|
27
|
+
private font?;
|
|
9
28
|
private cachedVerticals?;
|
|
10
|
-
private underlinePosition;
|
|
11
|
-
private underlineThickness;
|
|
12
|
-
private capHeight;
|
|
13
|
-
private xHeight;
|
|
14
29
|
private cachedDecoration?;
|
|
15
|
-
constructor(
|
|
16
|
-
private
|
|
17
|
-
private parseAFMData;
|
|
18
|
-
private parseCharMetrics;
|
|
30
|
+
constructor(fullName: string);
|
|
31
|
+
private get data();
|
|
19
32
|
/**
|
|
20
33
|
* The font's LINE metrics, as fractions of the em: how far a line box must reach above and below
|
|
21
34
|
* the baseline. Taken from the `FontBBox`.
|
|
@@ -41,8 +54,9 @@ export declare class AFMParser {
|
|
|
41
54
|
* strikethrough crosses at half of that. Still read from the font, not guessed.
|
|
42
55
|
*/
|
|
43
56
|
decoration(): FontDecoration;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
/** One character's advance. A string of several characters is not a glyph name here: it measures 0. */
|
|
58
|
+
getAdvanceWidth(char: string): number;
|
|
59
|
+
/** The pair's kerning. The AFM keys its pairs by GLYPH, like its widths - so the characters are
|
|
60
|
+
* mapped to glyphs first, or only letter-letter pairs (where the two coincide) would ever hit. */
|
|
47
61
|
getKerning(firstChar: string, secondChar: string): number;
|
|
48
62
|
}
|
package/dist/utils/afm-parser.js
CHANGED
|
@@ -1,118 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
/** The 14 names, known without their data: registering a font must not wait for it. */
|
|
2
|
+
export const STANDARD_FONT_NAMES = new Set([
|
|
3
|
+
"Courier",
|
|
4
|
+
"Courier-Bold",
|
|
5
|
+
"Courier-BoldOblique",
|
|
6
|
+
"Courier-Oblique",
|
|
7
|
+
"Helvetica",
|
|
8
|
+
"Helvetica-Bold",
|
|
9
|
+
"Helvetica-BoldOblique",
|
|
10
|
+
"Helvetica-Oblique",
|
|
11
|
+
"Symbol",
|
|
12
|
+
"Times-Bold",
|
|
13
|
+
"Times-BoldItalic",
|
|
14
|
+
"Times-Italic",
|
|
15
|
+
"Times-Roman",
|
|
16
|
+
"ZapfDingbats",
|
|
17
|
+
]);
|
|
18
|
+
// The metrics are 155 KB of numbers that only a render needs, so they are loaded then, and an app
|
|
19
|
+
// that ships jasy does not carry them in its start bundle. Layout is synchronous: everything that
|
|
20
|
+
// measures awaits this first (PDFRenderer.render, fillForm, flattenForm).
|
|
21
|
+
let metrics;
|
|
22
|
+
let loading;
|
|
23
|
+
let glyphOf;
|
|
24
|
+
/**
|
|
25
|
+
* Loads the metrics of the 14 standard fonts. Every render does this itself; call it ahead of time to
|
|
26
|
+
* have the first PDF without the wait, or to measure outside a render. Once per process.
|
|
27
|
+
*/
|
|
28
|
+
export function preloadStandardFonts() {
|
|
29
|
+
// ".js", not ".ts": tsc rewrites the extension of a static import, not of a dynamic one.
|
|
30
|
+
return (loading ?? (loading = import("../assets/standard-fonts.js").then((m) => {
|
|
31
|
+
metrics = m;
|
|
32
|
+
glyphOf = new Map();
|
|
33
|
+
for (let i = 0; i < m.CHARS.length; i += 2) {
|
|
34
|
+
glyphOf.set(String.fromCharCode(m.CHARS[i]), m.CHARS[i + 1]);
|
|
35
|
+
}
|
|
36
|
+
}, (e) => {
|
|
37
|
+
loading = undefined; // a chunk lost to a deploy: the next render tries again
|
|
38
|
+
throw e;
|
|
39
|
+
})));
|
|
22
40
|
}
|
|
23
|
-
/** One parser per standard-14 face for the whole process: the
|
|
41
|
+
/** One parser per standard-14 face for the whole process: the metrics are constants and a parsed
|
|
24
42
|
* face is read-only, so every document can share one. */
|
|
25
43
|
const STANDARD_PARSERS = new Map();
|
|
26
|
-
export function standardParser(fullName
|
|
44
|
+
export function standardParser(fullName) {
|
|
27
45
|
let parser = STANDARD_PARSERS.get(fullName);
|
|
28
46
|
if (!parser)
|
|
29
|
-
STANDARD_PARSERS.set(fullName, (parser = new AFMParser(
|
|
47
|
+
STANDARD_PARSERS.set(fullName, (parser = new AFMParser(fullName)));
|
|
30
48
|
return parser;
|
|
31
49
|
}
|
|
50
|
+
/** A standard font's metrics, read on first use - by then a render has loaded them. */
|
|
32
51
|
export class AFMParser {
|
|
33
|
-
constructor(
|
|
34
|
-
this.
|
|
35
|
-
this.kerningPairs = {};
|
|
36
|
-
this.glyphMap = glyphList();
|
|
37
|
-
// The font's bounding box in glyph space (1000 units / em), from the AFM header. This - not the
|
|
38
|
-
// `Ascender` line - is what a line box is built from; see `verticals()`.
|
|
39
|
-
this.bbox = [0, 0, 0, 0];
|
|
40
|
-
// Glyph metrics from the AFM header, for underline / strikethrough. All 14 standard fonts carry
|
|
41
|
-
// the underline pair; Symbol and ZapfDingbats have no CapHeight/XHeight (they have no lowercase).
|
|
42
|
-
this.underlinePosition = 0; // negative, as the AFM writes it: below the baseline
|
|
43
|
-
this.underlineThickness = 0;
|
|
44
|
-
this.capHeight = 0;
|
|
45
|
-
this.xHeight = 0;
|
|
46
|
-
this.parseAFMData(afmData);
|
|
52
|
+
constructor(fullName) {
|
|
53
|
+
this.fullName = fullName;
|
|
47
54
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (line.startsWith("StartCharMetrics")) {
|
|
58
|
-
inCharMetrics = true;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (line.startsWith("EndCharMetrics")) {
|
|
62
|
-
inCharMetrics = false;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (inCharMetrics) {
|
|
66
|
-
const charData = this.parseCharMetrics(line);
|
|
67
|
-
if (charData) {
|
|
68
|
-
const { charName, wx } = charData;
|
|
69
|
-
this.advanceWidths[charName] = wx;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (line.startsWith("UnderlinePosition ")) {
|
|
73
|
-
this.underlinePosition = parseFloat(line.slice(18));
|
|
74
|
-
}
|
|
75
|
-
if (line.startsWith("UnderlineThickness ")) {
|
|
76
|
-
this.underlineThickness = parseFloat(line.slice(19));
|
|
77
|
-
}
|
|
78
|
-
if (line.startsWith("CapHeight ")) {
|
|
79
|
-
this.capHeight = parseFloat(line.slice(10));
|
|
80
|
-
}
|
|
81
|
-
if (line.startsWith("XHeight ")) {
|
|
82
|
-
this.xHeight = parseFloat(line.slice(8));
|
|
83
|
-
}
|
|
84
|
-
if (line.startsWith("FontBBox ")) {
|
|
85
|
-
const [x0, y0, x1, y1] = line.slice(9).trim().split(/\s+/).map(parseFloat);
|
|
86
|
-
this.bbox = [x0, y0, x1, y1];
|
|
87
|
-
}
|
|
88
|
-
if (line.startsWith("KPX")) {
|
|
89
|
-
const parts = line.split(/\s+/);
|
|
90
|
-
const firstChar = parts[1];
|
|
91
|
-
const secondChar = parts[2];
|
|
92
|
-
const kerning = parseFloat(parts[3]);
|
|
93
|
-
// Nested by first glyph, then second. A flat `${a}-${b}` key would have to be BUILT on every
|
|
94
|
-
// lookup, and getKerning runs once per adjacent character pair - that string allocation was a
|
|
95
|
-
// quarter of a standard-font render.
|
|
96
|
-
((_a = this.kerningPairs)[firstChar] ?? (_a[firstChar] = {}))[secondChar] = kerning;
|
|
97
|
-
}
|
|
55
|
+
get data() {
|
|
56
|
+
if (this.font)
|
|
57
|
+
return this.font;
|
|
58
|
+
const raw = metrics?.FONTS[this.fullName];
|
|
59
|
+
if (!raw) {
|
|
60
|
+
throw new Error(metrics
|
|
61
|
+
? `@jasy/pdf: "${this.fullName}" is not one of the 14 standard fonts.`
|
|
62
|
+
: "@jasy/pdf: the standard font metrics are not loaded. A render loads them itself; to " +
|
|
63
|
+
"measure text outside one, await preloadStandardFonts() first.");
|
|
98
64
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
if (part.startsWith("WX ")) {
|
|
109
|
-
wx = parseFloat(part.split(" ")[1]);
|
|
65
|
+
const kerning = new Map();
|
|
66
|
+
for (let i = 0; i < raw.kerning.length;) {
|
|
67
|
+
const first = raw.kerning[i++];
|
|
68
|
+
const pairs = new Map();
|
|
69
|
+
let second = 0;
|
|
70
|
+
for (let n = raw.kerning[i++]; n > 0; n--) {
|
|
71
|
+
second += raw.kerning[i++];
|
|
72
|
+
pairs.set(second, raw.kerning[i++]);
|
|
110
73
|
}
|
|
74
|
+
kerning.set(first, pairs);
|
|
111
75
|
}
|
|
112
|
-
|
|
113
|
-
return { charName, wx };
|
|
114
|
-
}
|
|
115
|
-
return null;
|
|
76
|
+
return (this.font = { ...raw, kerning });
|
|
116
77
|
}
|
|
117
78
|
/**
|
|
118
79
|
* The font's LINE metrics, as fractions of the em: how far a line box must reach above and below
|
|
@@ -133,7 +94,7 @@ export class AFMParser {
|
|
|
133
94
|
verticals() {
|
|
134
95
|
if (this.cachedVerticals)
|
|
135
96
|
return this.cachedVerticals;
|
|
136
|
-
const [, yMin, , yMax] = this.bbox;
|
|
97
|
+
const [, yMin, , yMax] = this.data.bbox;
|
|
137
98
|
this.cachedVerticals = { ascent: yMax / 1000, descent: -yMin / 1000, lineGap: 0 };
|
|
138
99
|
return this.cachedVerticals;
|
|
139
100
|
}
|
|
@@ -147,21 +108,30 @@ export class AFMParser {
|
|
|
147
108
|
decoration() {
|
|
148
109
|
if (this.cachedDecoration)
|
|
149
110
|
return this.cachedDecoration;
|
|
150
|
-
const
|
|
111
|
+
const { bbox, underline, xHeight } = this.data;
|
|
112
|
+
const capHeight = this.data.capHeight || bbox[3];
|
|
151
113
|
this.cachedDecoration = {
|
|
152
|
-
underlinePosition: -
|
|
153
|
-
underlineThickness:
|
|
114
|
+
underlinePosition: -underline[0] / 1000, // AFM writes it negative (below the baseline)
|
|
115
|
+
underlineThickness: underline[1] / 1000,
|
|
154
116
|
capHeight: capHeight / 1000,
|
|
155
|
-
xHeight: (
|
|
117
|
+
xHeight: (xHeight || capHeight / 2) / 1000,
|
|
156
118
|
};
|
|
157
119
|
return this.cachedDecoration;
|
|
158
120
|
}
|
|
159
|
-
|
|
160
|
-
|
|
121
|
+
/** One character's advance. A string of several characters is not a glyph name here: it measures 0. */
|
|
122
|
+
getAdvanceWidth(char) {
|
|
123
|
+
const { widths } = this.data; // first: it throws when the metrics are not loaded
|
|
124
|
+
const glyph = glyphOf.get(char);
|
|
125
|
+
return glyph === undefined ? 0 : widths[glyph];
|
|
161
126
|
}
|
|
162
|
-
/** The pair's kerning. The AFM keys its pairs by GLYPH
|
|
163
|
-
*
|
|
127
|
+
/** The pair's kerning. The AFM keys its pairs by GLYPH, like its widths - so the characters are
|
|
128
|
+
* mapped to glyphs first, or only letter-letter pairs (where the two coincide) would ever hit. */
|
|
164
129
|
getKerning(firstChar, secondChar) {
|
|
165
|
-
|
|
130
|
+
const { kerning } = this.data;
|
|
131
|
+
const first = glyphOf.get(firstChar);
|
|
132
|
+
const second = glyphOf.get(secondChar);
|
|
133
|
+
if (first === undefined || second === undefined)
|
|
134
|
+
return 0;
|
|
135
|
+
return kerning.get(first)?.get(second) ?? 0;
|
|
166
136
|
}
|
|
167
137
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { pageFormats, PageSize } from "../constants/page-sizes.js";
|
|
2
|
-
import { STANDARD_AFM } from "../assets/font-data.js";
|
|
3
2
|
import { md5, md5Hex } from "./md5.js";
|
|
4
3
|
import { zlibSync } from "fflate";
|
|
5
4
|
import { bytesFromLatin1, latin1FromBytes } from "./bytes.js";
|
|
6
|
-
import { standardParser } from "./afm-parser.js";
|
|
5
|
+
import { STANDARD_FONT_NAMES, standardParser } from "./afm-parser.js";
|
|
7
6
|
import { mergeSpans, TTFParser } from "./ttf-parser.js";
|
|
8
7
|
import { WoffError, isWoff, woffToSfnt } from "./woff.js";
|
|
9
8
|
import { isWoff2 } from "./woff2.js";
|
|
@@ -612,13 +611,12 @@ endstream`;
|
|
|
612
611
|
this.fontEpoch++;
|
|
613
612
|
this.verticalsCache.delete(fontName);
|
|
614
613
|
this.decorationCache.delete(fontName);
|
|
615
|
-
|
|
616
|
-
if (data !== undefined) {
|
|
614
|
+
if (STANDARD_FONT_NAMES.has(fullName)) {
|
|
617
615
|
this.afmParsers.push({
|
|
618
616
|
fontName,
|
|
619
617
|
fontStyle,
|
|
620
618
|
fullFontName: fullName,
|
|
621
|
-
parser: standardParser(fullName
|
|
619
|
+
parser: standardParser(fullName),
|
|
622
620
|
});
|
|
623
621
|
}
|
|
624
622
|
const resourceNumber = this.objects.length + 1; // New resource number
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jasy/pdf",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "Create PDFs in TypeScript: declarative, component-based layout with real pagination, in Node or the browser. Its own engine - no headless browser, no Java. Powers ZUGFeRD / XRechnung e-invoices.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -56,14 +56,12 @@
|
|
|
56
56
|
"@types/node": "^25.9.3",
|
|
57
57
|
"@vitest/coverage-v8": "^2.1.1",
|
|
58
58
|
"hyphen": "^1.14.1",
|
|
59
|
-
"ncp": "^2.0.0",
|
|
60
59
|
"oxfmt": "^0.55.0",
|
|
61
60
|
"oxlint": "^1.70.0",
|
|
62
61
|
"typescript": "^5.5.4",
|
|
63
62
|
"vitest": "^2.1.1"
|
|
64
63
|
},
|
|
65
64
|
"scripts": {
|
|
66
|
-
"copy-assets": "ncp ./src/lib/assets ./dist/assets",
|
|
67
65
|
"cli": "pnpm --filter @jasy/cli dev",
|
|
68
66
|
"cli:watch": "pnpm --filter @jasy/cli watch",
|
|
69
67
|
"play": "pnpm --filter @jasy/playground play",
|
|
@@ -71,7 +69,7 @@
|
|
|
71
69
|
"play:react": "pnpm --filter @jasy/react play",
|
|
72
70
|
"test": "vitest",
|
|
73
71
|
"test:coverage": "vitest --coverage",
|
|
74
|
-
"build": "tsc && node scripts/fix-dts-ext.mjs
|
|
72
|
+
"build": "tsc && node scripts/fix-dts-ext.mjs",
|
|
75
73
|
"lint": "oxlint",
|
|
76
74
|
"lint:fix": "oxlint --fix",
|
|
77
75
|
"fmt": "oxfmt",
|