@behackl/citation-js-extras 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -1
- package/dist/index.d.ts +21 -2
- package/dist/index.js +54 -11
- package/dist/types.d.ts +20 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,7 +74,9 @@ const rendered = bib.formatHtml(bib.entries, {
|
|
|
74
74
|
`myMathRenderer` is an application-supplied function returning **trusted HTML**
|
|
75
75
|
(e.g. MathJax SVG or KaTeX output). No math renderer is bundled. Configure it for
|
|
76
76
|
untrusted TeX as appropriate; the callback output is inserted verbatim, not
|
|
77
|
-
sanitized.
|
|
77
|
+
sanitized. Treat renderer output as untrusted HTML unless you control both the
|
|
78
|
+
renderer configuration and the TeX: substitute it only after the surrounding
|
|
79
|
+
HTML has been sanitized (see below). Exceptions propagate to the caller. `renderMath` also works with
|
|
78
80
|
`formatEntry`; it has no effect unless `preserveMath` was enabled.
|
|
79
81
|
|
|
80
82
|
Supported delimiters are `$…$`, `$$…$$`, `\(…\)`, and `\[…\]`.
|
|
@@ -100,6 +102,46 @@ Restoration runs after title linking, badges, and URL linkification. Neither
|
|
|
100
102
|
renderer output nor restored TeX is fed back through these HTML helpers. Disabling
|
|
101
103
|
preservation (the default) retains the previous behavior.
|
|
102
104
|
|
|
105
|
+
If `renderMath` throws, the error names the entry it came from
|
|
106
|
+
(`entry doe:2024: Undefined control sequence: \\kk`), so a broken formula can be
|
|
107
|
+
traced to a line in the `.bib` file.
|
|
108
|
+
|
|
109
|
+
### Sanitizing formatted output
|
|
110
|
+
|
|
111
|
+
Rendered mathematics does **not** survive an HTML sanitizer with default
|
|
112
|
+
settings: KaTeX and MathJax output relies on `class`, `style` and MathML
|
|
113
|
+
elements that `rehype-sanitize` strips, leaving empty boxes. Sanitize the
|
|
114
|
+
citation HTML *first*, then insert the trusted renderer output:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
const math = [];
|
|
118
|
+
const html = bib.formatHtml(entries, {
|
|
119
|
+
// Stand in for each formula while the HTML is sanitized ...
|
|
120
|
+
renderMath: (tex, { display }) => {
|
|
121
|
+
math.push(renderWithKatex(tex, display));
|
|
122
|
+
return `mathplaceholder${math.length - 1}end`;
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
const safe = sanitize(html);
|
|
126
|
+
// ... then substitute the rendered markup back in.
|
|
127
|
+
const final = safe.replace(/mathplaceholder(\d+)end/g, (_, i) => math[Number(i)]);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Two related gotchas when using `rehype-sanitize`:
|
|
131
|
+
|
|
132
|
+
- its `defaultSchema` allows `className` only with an explicit value list *per
|
|
133
|
+
tag*, and that per-tag rule overrides anything added under `'*'`; to keep
|
|
134
|
+
badge or list classes, merge your class names into the existing entry for
|
|
135
|
+
that tag
|
|
136
|
+
- pick a placeholder that cannot occur in your bibliography (append characters
|
|
137
|
+
until it is absent from the source)
|
|
138
|
+
|
|
139
|
+
This recipe deliberately reinserts renderer output *after* sanitizing, so it is
|
|
140
|
+
only as safe as the renderer: a renderer configured to emit arbitrary markup
|
|
141
|
+
(for instance KaTeX with `trust: true`) can reintroduce `<script>`. Restrict the
|
|
142
|
+
renderer configuration, or sanitize its output separately with a schema that
|
|
143
|
+
keeps the elements and attributes mathematics needs.
|
|
144
|
+
|
|
103
145
|
## Development checks
|
|
104
146
|
|
|
105
147
|
```sh
|
|
@@ -125,6 +167,13 @@ Use Node 24 LTS for these development checks, matching CI and publishing.
|
|
|
125
167
|
| `cslStyle` | `string?` | CSL style — a registered template name, raw XML, or a file path. Defaults to `'apa'`. |
|
|
126
168
|
| `customFields` | `string[]?` | BibTeX field names to preserve. These appear on each entry under `.custom`. |
|
|
127
169
|
| `preserveMath` | `boolean?` | Preserve math in display-text fields through CSL formatting. Defaults to `false`. |
|
|
170
|
+
| `titleLink` | `string[]?` | Default fields used for the title link, checked in order. Defaults to `['url', 'doi', 'arxiv']`. |
|
|
171
|
+
| `badges` | `BadgeConfig[]?` | Default badge configuration, used by every `formatHtml`/`formatEntry` call. No badges are rendered unless set. |
|
|
172
|
+
| `linkifyUrls` | `boolean?` | Default for URL linkification. Defaults to `true`. |
|
|
173
|
+
|
|
174
|
+
`titleLink`, `badges` and `linkifyUrls` are usually the same for every call, so
|
|
175
|
+
they can be set once here; a value passed to `formatHtml`/`formatEntry` overrides
|
|
176
|
+
the default for that call.
|
|
128
177
|
|
|
129
178
|
### `bib.entries`
|
|
130
179
|
|
|
@@ -174,10 +223,14 @@ bib.formatHtml(entries, {
|
|
|
174
223
|
|
|
175
224
|
Entries are formatted in one citeproc pass, so style-dependent state (for example numeric labels in Vancouver) remains correct.
|
|
176
225
|
|
|
226
|
+
An empty entry list returns an empty string, not an empty wrapper element.
|
|
227
|
+
|
|
177
228
|
### `bib.formatEntry(entry, options?)`
|
|
178
229
|
|
|
179
230
|
Render a single entry as an HTML string (no list wrapper). The title link targets the actual CSL title text, regardless of italics.
|
|
180
231
|
|
|
232
|
+
Accepts the same title linking, badge and `linkifyUrls` options as `formatHtml` and applies them identically (`list`/`listAttributes` are ignored — there is no wrapper). Bare URLs are linkified unless `linkifyUrls: false` is set, either per call or on the constructor.
|
|
233
|
+
|
|
181
234
|
For citation styles that depend on multi-entry context (numbered labels, ibid behavior, etc.), prefer `formatHtml(...)`.
|
|
182
235
|
|
|
183
236
|
Title links are only created for safe URL schemes (`http`, `https`, `mailto`) or normalized DOI/arXiv links.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BibEntry, BibliographyOptions, FormatOptions } from "./types.js";
|
|
2
|
-
export type { BadgeConfig, BibEntry, BibliographyOptions, FormatOptions, MathRenderer } from "./types.js";
|
|
2
|
+
export type { BadgeConfig, BibEntry, BibliographyOptions, FormatDefaults, FormatOptions, MathRenderer, } from "./types.js";
|
|
3
3
|
export declare class Bibliography {
|
|
4
4
|
/** The CSL template name to use for formatting. */
|
|
5
5
|
readonly templateName: string;
|
|
@@ -7,6 +7,8 @@ export declare class Bibliography {
|
|
|
7
7
|
readonly entries: BibEntry[];
|
|
8
8
|
private readonly customFieldNames;
|
|
9
9
|
private readonly math?;
|
|
10
|
+
/** Formatting defaults from the constructor; each call may override them. */
|
|
11
|
+
private readonly formatDefaults;
|
|
10
12
|
constructor(options: BibliographyOptions);
|
|
11
13
|
/**
|
|
12
14
|
* Return entries whose custom fields match **all** given key/value pairs.
|
|
@@ -28,9 +30,26 @@ export declare class Bibliography {
|
|
|
28
30
|
}): BibEntry[];
|
|
29
31
|
/**
|
|
30
32
|
* Format a single entry as an HTML string (no wrapper element).
|
|
31
|
-
* Applies title linking and
|
|
33
|
+
* Applies title linking, badge injection and URL linkification.
|
|
32
34
|
*/
|
|
33
35
|
formatEntry(entry: BibEntry, options?: FormatOptions): string;
|
|
36
|
+
/**
|
|
37
|
+
* Decorate, linkify and restore math for one rendered entry. Shared by
|
|
38
|
+
* `formatEntry` and `formatHtml` so both honour the same options.
|
|
39
|
+
*
|
|
40
|
+
* Linkification runs before math restoration: rendered MathML carries an
|
|
41
|
+
* xmlns URL that must not be turned into a link. Math is restored per entry
|
|
42
|
+
* so a failing formula can be reported with its citation key.
|
|
43
|
+
*/
|
|
44
|
+
private buildEntryHtml;
|
|
45
|
+
/** Per-call options win over the defaults given to the constructor. */
|
|
46
|
+
private mergeOptions;
|
|
47
|
+
/**
|
|
48
|
+
* Restore protected math, naming the entry when a renderer rejects a formula.
|
|
49
|
+
* Without the citation key, `renderMath` failures are near-impossible to
|
|
50
|
+
* trace back to a line in the .bib file.
|
|
51
|
+
*/
|
|
52
|
+
private restoreMath;
|
|
34
53
|
/**
|
|
35
54
|
* Format a list of entries as a complete HTML bibliography.
|
|
36
55
|
*/
|
package/dist/index.js
CHANGED
|
@@ -11,9 +11,16 @@ export class Bibliography {
|
|
|
11
11
|
entries;
|
|
12
12
|
customFieldNames;
|
|
13
13
|
math;
|
|
14
|
+
/** Formatting defaults from the constructor; each call may override them. */
|
|
15
|
+
formatDefaults;
|
|
14
16
|
constructor(options) {
|
|
15
17
|
const bibData = maybeReadFile(options.data);
|
|
16
18
|
this.customFieldNames = options.customFields ?? [];
|
|
19
|
+
this.formatDefaults = {
|
|
20
|
+
titleLink: options.titleLink,
|
|
21
|
+
badges: options.badges,
|
|
22
|
+
linkifyUrls: options.linkifyUrls,
|
|
23
|
+
};
|
|
17
24
|
// Register CSL style
|
|
18
25
|
this.templateName = this.registerStyle(options.cslStyle);
|
|
19
26
|
// Two-pass parse: raw (preserves all fields) + CSL (for formatting)
|
|
@@ -81,14 +88,53 @@ export class Bibliography {
|
|
|
81
88
|
// -------------------------------------------------------------------------
|
|
82
89
|
/**
|
|
83
90
|
* Format a single entry as an HTML string (no wrapper element).
|
|
84
|
-
* Applies title linking and
|
|
91
|
+
* Applies title linking, badge injection and URL linkification.
|
|
85
92
|
*/
|
|
86
93
|
formatEntry(entry, options = {}) {
|
|
94
|
+
const merged = this.mergeOptions(options);
|
|
87
95
|
const rendered = this.renderCslEntries([entry]);
|
|
88
96
|
const raw = rendered[0]?.[1] ?? "";
|
|
89
97
|
const innerHtml = unwrapCslEntry(raw) ?? raw.trim();
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
return this.buildEntryHtml(entry, innerHtml, merged);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Decorate, linkify and restore math for one rendered entry. Shared by
|
|
102
|
+
* `formatEntry` and `formatHtml` so both honour the same options.
|
|
103
|
+
*
|
|
104
|
+
* Linkification runs before math restoration: rendered MathML carries an
|
|
105
|
+
* xmlns URL that must not be turned into a link. Math is restored per entry
|
|
106
|
+
* so a failing formula can be reported with its citation key.
|
|
107
|
+
*/
|
|
108
|
+
buildEntryHtml(entry, innerRaw, merged) {
|
|
109
|
+
let inner = this.decorateEntryHtml(entry, innerRaw, merged);
|
|
110
|
+
if (merged.linkifyUrls !== false)
|
|
111
|
+
inner = linkifyBareUrls(inner);
|
|
112
|
+
return this.restoreMath(inner, merged, entry);
|
|
113
|
+
}
|
|
114
|
+
/** Per-call options win over the defaults given to the constructor. */
|
|
115
|
+
mergeOptions(options) {
|
|
116
|
+
return {
|
|
117
|
+
...options,
|
|
118
|
+
titleLink: options.titleLink ?? this.formatDefaults.titleLink,
|
|
119
|
+
badges: options.badges ?? this.formatDefaults.badges,
|
|
120
|
+
linkifyUrls: options.linkifyUrls ?? this.formatDefaults.linkifyUrls,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Restore protected math, naming the entry when a renderer rejects a formula.
|
|
125
|
+
* Without the citation key, `renderMath` failures are near-impossible to
|
|
126
|
+
* trace back to a line in the .bib file.
|
|
127
|
+
*/
|
|
128
|
+
restoreMath(html, options, entry) {
|
|
129
|
+
if (!this.math)
|
|
130
|
+
return html;
|
|
131
|
+
try {
|
|
132
|
+
return this.math.restore(html, options.renderMath);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
136
|
+
throw new Error(entry ? `entry ${entry.key}: ${message}` : message, { cause: error });
|
|
137
|
+
}
|
|
92
138
|
}
|
|
93
139
|
/**
|
|
94
140
|
* Format a list of entries as a complete HTML bibliography.
|
|
@@ -96,8 +142,9 @@ export class Bibliography {
|
|
|
96
142
|
formatHtml(entries, options = {}) {
|
|
97
143
|
if (entries.length === 0)
|
|
98
144
|
return "";
|
|
99
|
-
const
|
|
100
|
-
const
|
|
145
|
+
const merged = this.mergeOptions(options);
|
|
146
|
+
const tag = merged.list ?? "ol";
|
|
147
|
+
const attrs = merged.listAttributes ?? (tag === "ol" ? { reversed: true } : {});
|
|
101
148
|
const attrStr = renderAttributes(attrs);
|
|
102
149
|
// Render all entries in one citeproc run so style-dependent numbering/state
|
|
103
150
|
// (e.g. vancouver left-margin labels) remains correct.
|
|
@@ -111,14 +158,10 @@ export class Bibliography {
|
|
|
111
158
|
?? rendered[index]?.[1]
|
|
112
159
|
?? "";
|
|
113
160
|
const innerRaw = unwrapCslEntry(raw) ?? raw.trim();
|
|
114
|
-
const inner = this.
|
|
161
|
+
const inner = this.buildEntryHtml(entry, innerRaw, merged);
|
|
115
162
|
return `<${itemTag} data-csl-entry-id="${escapeAttr(entry.key)}" class="csl-entry">${inner}</${itemTag}>`;
|
|
116
163
|
});
|
|
117
|
-
|
|
118
|
-
if (options.linkifyUrls !== false) {
|
|
119
|
-
html = linkifyBareUrls(html);
|
|
120
|
-
}
|
|
121
|
-
return this.math ? this.math.restore(html, options.renderMath) : html;
|
|
164
|
+
return `<${tag}${attrStr} class="csl-bib-body">\n${items.join("\n")}\n</${tag}>`;
|
|
122
165
|
}
|
|
123
166
|
// -------------------------------------------------------------------------
|
|
124
167
|
// Private helpers
|
package/dist/types.d.ts
CHANGED
|
@@ -45,14 +45,20 @@ export interface BadgeConfig {
|
|
|
45
45
|
export type MathRenderer = (tex: string, context: {
|
|
46
46
|
display: boolean;
|
|
47
47
|
}) => string;
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Options passed to {@link Bibliography.formatHtml} or
|
|
50
|
+
* {@link Bibliography.formatEntry}. `list` and `listAttributes` only apply to
|
|
51
|
+
* `formatHtml`, which is the only method emitting a wrapper element.
|
|
52
|
+
*/
|
|
49
53
|
export interface FormatOptions {
|
|
50
54
|
/** Render protected math as trusted HTML; requires preserveMath at construction.
|
|
51
55
|
* Without this callback, escaped original TeX delimiters are restored.
|
|
56
|
+
* Not settable on the constructor.
|
|
52
57
|
*/
|
|
53
58
|
renderMath?: MathRenderer;
|
|
54
59
|
/**
|
|
55
60
|
* Fields to use for linking the title, checked in order.
|
|
61
|
+
* Overrides the constructor default, if any.
|
|
56
62
|
* A `doi` value is expanded to `https://doi.org/<value>`, an `arxiv`
|
|
57
63
|
* value to `https://arxiv.org/abs/<value>`, and direct values are only
|
|
58
64
|
* accepted when they use `http`, `https`, or `mailto`.
|
|
@@ -60,7 +66,12 @@ export interface FormatOptions {
|
|
|
60
66
|
* @default ['url', 'doi', 'arxiv']
|
|
61
67
|
*/
|
|
62
68
|
titleLink?: string[];
|
|
63
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Badge configurations to append to each entry.
|
|
71
|
+
* Overrides the constructor default, if any.
|
|
72
|
+
*
|
|
73
|
+
* @default [] (no badges)
|
|
74
|
+
*/
|
|
64
75
|
badges?: BadgeConfig[];
|
|
65
76
|
/**
|
|
66
77
|
* Wrapper list element.
|
|
@@ -77,6 +88,7 @@ export interface FormatOptions {
|
|
|
77
88
|
/**
|
|
78
89
|
* Auto-linkify bare `http(s)://` URLs in the rendered output that aren't
|
|
79
90
|
* already inside `<a>`, `<script>`, or `<style>` tags.
|
|
91
|
+
* Overrides the constructor default, if any.
|
|
80
92
|
*
|
|
81
93
|
* @default true
|
|
82
94
|
*/
|
|
@@ -98,8 +110,13 @@ export interface BibEntry {
|
|
|
98
110
|
/** The full raw BibTeX properties (unfiltered). */
|
|
99
111
|
raw: Record<string, any>;
|
|
100
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Formatting options that can be set once on the constructor and overridden
|
|
115
|
+
* by the options of an individual `formatHtml`/`formatEntry` call.
|
|
116
|
+
*/
|
|
117
|
+
export type FormatDefaults = Pick<FormatOptions, "titleLink" | "badges" | "linkifyUrls">;
|
|
101
118
|
/** Options for constructing a {@link Bibliography}. */
|
|
102
|
-
export interface BibliographyOptions {
|
|
119
|
+
export interface BibliographyOptions extends FormatDefaults {
|
|
103
120
|
/** Preserve math in supported display-text fields before CSL conversion.
|
|
104
121
|
* Opt-in; entries' CSL text contains internal placeholders until HTML formatting.
|
|
105
122
|
* Raw/custom fields remain unchanged. Unclosed or empty math throws.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@behackl/citation-js-extras",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Preserve custom BibTeX fields through citation-js and render academic bibliographies with linked titles, badges, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|