@citestyle/registry 0.0.2 → 1.1.0
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 +100 -55
- package/package.json +20 -6
- package/src/format.js +70 -0
- package/src/index.js +4 -0
- package/src/registry.js +85 -7
- package/types.d.ts +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @citestyle/registry
|
|
2
2
|
|
|
3
|
-
Citation registry
|
|
3
|
+
Citation registry for managing cross-reference state across a document. When you have multiple citations and need them to interact — numbered references, year-suffix disambiguation (Smith 2024a, 2024b), cite collapsing ([1-3, 5]), sorted bibliographies — the registry handles it.
|
|
4
|
+
|
|
5
|
+
For one-off formatting where citations don't interact, this package also exports a simpler `format()` API that needs no setup.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -8,16 +10,14 @@ Citation registry that manages cross-citation state for a compiled CSL style. Ha
|
|
|
8
10
|
npm install @citestyle/registry
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Quick start
|
|
12
14
|
|
|
13
|
-
```
|
|
15
|
+
```javascript
|
|
14
16
|
import { createRegistry } from '@citestyle/registry'
|
|
15
17
|
import * as apa from '@citestyle/styles/apa'
|
|
16
18
|
|
|
17
|
-
// Create a registry bound to a compiled style
|
|
18
19
|
const registry = createRegistry(apa)
|
|
19
20
|
|
|
20
|
-
// Add items (assigns citation numbers, triggers disambiguation)
|
|
21
21
|
registry.addItems([
|
|
22
22
|
{
|
|
23
23
|
id: 'smith2024',
|
|
@@ -40,84 +40,129 @@ registry.addItems([
|
|
|
40
40
|
},
|
|
41
41
|
])
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.log(citation.html) // <span class="csl-citation">(Smith, 2024)</span>
|
|
43
|
+
// Inline citation
|
|
44
|
+
registry.cite([{ id: 'smith2024' }])
|
|
45
|
+
// → { text: '(Smith, 2024)', html: '<span class="csl-citation">...</span>' }
|
|
47
46
|
|
|
48
|
-
// Citation with locator
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
])
|
|
52
|
-
console.log(withPage.text) // (Smith, 2024, p. 105)
|
|
47
|
+
// Citation with page locator
|
|
48
|
+
registry.cite([{ id: 'smith2024', locator: '105', label: 'page' }])
|
|
49
|
+
// → { text: '(Smith, 2024, p. 105)', html: '...' }
|
|
53
50
|
|
|
54
|
-
// Multi-cite
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{ id: 'doe2023' },
|
|
58
|
-
])
|
|
59
|
-
console.log(multi.text) // (Doe, 2023; Smith, 2024)
|
|
51
|
+
// Multi-cite (sorted per style rules)
|
|
52
|
+
registry.cite([{ id: 'smith2024' }, { id: 'doe2023' }])
|
|
53
|
+
// → { text: '(Doe, 2023; Smith, 2024)', html: '...' }
|
|
60
54
|
|
|
61
|
-
//
|
|
55
|
+
// Full bibliography — sorted, numbered, disambiguated
|
|
62
56
|
const entries = registry.getBibliography()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
entries[0].text // Plain text
|
|
58
|
+
entries[0].html // Semantic HTML with CSS classes and linked DOIs
|
|
59
|
+
entries[0].parts // Decomposed fields for custom layouts
|
|
60
|
+
entries[0].links // Extracted DOI, URL links
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Simple API (no registry)
|
|
64
|
+
|
|
65
|
+
When you just need to format one or a few items and don't need cross-reference features:
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
```javascript
|
|
68
|
+
import { format, formatAll, formatCitation } from '@citestyle/registry'
|
|
69
|
+
import * as apa from '@citestyle/styles/apa'
|
|
70
|
+
|
|
71
|
+
// Single bibliography entry
|
|
72
|
+
const entry = format(apa, item)
|
|
73
|
+
// → { html, text, parts, links }
|
|
74
|
+
|
|
75
|
+
// Multiple entries (sorted per style rules, auto-numbered)
|
|
76
|
+
const entries = formatAll(apa, [item1, item2, item3])
|
|
77
|
+
|
|
78
|
+
// Single inline citation
|
|
79
|
+
const cite = formatCitation(apa, [{ item, locator: '42', label: 'page' }])
|
|
80
|
+
// → { html, text }
|
|
70
81
|
```
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
Use `createRegistry()` instead when you need any of the cross-reference features below.
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
## When to use the registry vs. `format()`
|
|
75
86
|
|
|
76
|
-
|
|
87
|
+
| Feature | `format()` | `createRegistry()` |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| Format a bibliography entry | Yes | Yes |
|
|
90
|
+
| Format a citation | Yes | Yes |
|
|
91
|
+
| Sort bibliography | Yes | Yes |
|
|
92
|
+
| Citation numbering (IEEE, Vancouver) | No | Yes |
|
|
93
|
+
| Year-suffix disambiguation (2024a, 2024b) | No | Yes |
|
|
94
|
+
| Name disambiguation (expand initials/names) | No | Yes |
|
|
95
|
+
| Cite collapsing ([1-3] or "Smith 2020, 2021") | No | Yes |
|
|
96
|
+
| Subsequent-author-substitute (em-dash) | No | Yes |
|
|
77
97
|
|
|
78
|
-
|
|
79
|
-
- `options.subsequentAuthorSubstitute` (string) -- Override the style's substitute string (e.g., `"---"`)
|
|
98
|
+
**Rule of thumb**: use `format()` for publication lists, portfolio pages, and anywhere each entry stands alone. Use `createRegistry()` for documents — papers, articles, blog posts — where citations reference and interact with each other.
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
### `createRegistry(style, options?)`
|
|
103
|
+
|
|
104
|
+
Create a new registry bound to a compiled style.
|
|
82
105
|
|
|
83
|
-
|
|
106
|
+
- `style` — A compiled style module (from `@citestyle/styles/*` or your own compiled `.js`)
|
|
107
|
+
- `options.subsequentAuthorSubstitute` (string) — Override the style's substitute string (e.g., `"---"`)
|
|
84
108
|
|
|
85
|
-
|
|
109
|
+
### `registry.addItems(items)`
|
|
86
110
|
|
|
87
|
-
Add CSL-JSON items
|
|
111
|
+
Add CSL-JSON items. Items are stored by `id` and assigned citation numbers in insertion order. Adding an item with a duplicate ID updates the existing entry.
|
|
88
112
|
|
|
89
|
-
|
|
113
|
+
### `registry.getItem(id)`
|
|
90
114
|
|
|
91
115
|
Look up a single item by ID. Returns `undefined` if not found.
|
|
92
116
|
|
|
93
|
-
|
|
117
|
+
### `registry.cite(cites, ctx?)`
|
|
94
118
|
|
|
95
|
-
Format a citation cluster. Each cite
|
|
119
|
+
Format a citation cluster. Each cite can include:
|
|
96
120
|
|
|
97
121
|
| Property | Type | Description |
|
|
98
122
|
|---|---|---|
|
|
99
|
-
| `id` | string | Item ID (
|
|
123
|
+
| `id` | string | Item ID (looked up from registry) |
|
|
100
124
|
| `item` | CslItem | Pre-resolved item (bypasses lookup) |
|
|
101
|
-
| `locator` | string | Locator value (
|
|
102
|
-
| `label` | string | Locator label
|
|
103
|
-
| `prefix` | string | Text before the citation |
|
|
104
|
-
| `suffix` | string | Text after the citation |
|
|
125
|
+
| `locator` | string | Locator value (page number, chapter, etc.) |
|
|
126
|
+
| `label` | string | Locator label: `'page'`, `'chapter'`, `'section'`, etc. |
|
|
127
|
+
| `prefix` | string | Text inserted before the citation |
|
|
128
|
+
| `suffix` | string | Text appended after the citation |
|
|
105
129
|
|
|
106
130
|
Returns `{ text: string, html: string }`.
|
|
107
131
|
|
|
108
|
-
|
|
132
|
+
### `registry.getBibliography(ctx?)`
|
|
133
|
+
|
|
134
|
+
Get the full formatted bibliography. Items are sorted per the style's sort rules. Returns an array of `FormattedEntry` objects:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
{
|
|
138
|
+
text: string, // Plain text
|
|
139
|
+
html: string, // Semantic HTML with CSS classes and auto-linked DOIs
|
|
140
|
+
parts: object, // Decomposed fields (authors, year, title, ...)
|
|
141
|
+
links: object, // Extracted links (doi, url)
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `registry.size`
|
|
146
|
+
|
|
147
|
+
Read-only property: number of items in the registry.
|
|
148
|
+
|
|
149
|
+
### `format(style, item, ctx?)`
|
|
150
|
+
|
|
151
|
+
Format a single bibliography entry without a registry. Returns `{ text, html, parts, links }`.
|
|
152
|
+
|
|
153
|
+
### `formatAll(style, items, ctx?)`
|
|
109
154
|
|
|
110
|
-
|
|
155
|
+
Format multiple bibliography entries. Applies the style's sort order and assigns citation numbers. Returns an array of `{ text, html, parts, links }`.
|
|
111
156
|
|
|
112
|
-
|
|
157
|
+
### `formatCitation(style, cites, ctx?)`
|
|
113
158
|
|
|
114
|
-
|
|
159
|
+
Format a single citation cluster without a registry. Each cite must include an `item` property (not an `id`, since there's no registry to look it up from). Returns `{ text, html }`.
|
|
115
160
|
|
|
116
|
-
##
|
|
161
|
+
## What the registry handles automatically
|
|
117
162
|
|
|
118
|
-
- **Citation numbering
|
|
119
|
-
- **Bibliography sorting
|
|
120
|
-
- **Year-suffix
|
|
121
|
-
- **Name disambiguation
|
|
122
|
-
- **Cite collapsing
|
|
123
|
-
- **Subsequent-author-substitute
|
|
163
|
+
- **Citation numbering** — Numeric styles (IEEE, Vancouver) get auto-assigned `[1]`, `[2]`, etc. in insertion order
|
|
164
|
+
- **Bibliography sorting** — Uses the style's compiled sort comparator (alphabetical for APA, insertion-order for IEEE, etc.)
|
|
165
|
+
- **Year-suffix disambiguation** — When two items share the same author(s) and year, the registry assigns suffixes: Smith (2024a), Smith (2024b)
|
|
166
|
+
- **Name disambiguation** — Expands initials or adds names to distinguish ambiguous citations, following the style's `disambiguate-add-givenname` rules (5 rule variants supported)
|
|
167
|
+
- **Cite collapsing** — Numeric ranges: `[1, 2, 3, 5]` → `[1-3, 5]`. Author-date grouping: `(Smith 2020; Smith 2021)` → `(Smith 2020, 2021)`
|
|
168
|
+
- **Subsequent-author-substitute** — Replaces repeated authors in the bibliography with a dash or other substitute string, per the style's rules
|
package/package.json
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@citestyle/registry",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Citation registry: year-suffixes, numbering, sorting (~5-8KB)",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Citation registry: year-suffixes, numbering, sorting, disambiguation (~5-8KB)",
|
|
5
|
+
"author": "Uniweb",
|
|
6
|
+
"homepage": "https://github.com/uniweb/csl/tree/main/packages/registry",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/uniweb/csl.git",
|
|
10
|
+
"directory": "packages/registry"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"csl",
|
|
14
|
+
"citation",
|
|
15
|
+
"registry",
|
|
16
|
+
"bibliography",
|
|
17
|
+
"disambiguation",
|
|
18
|
+
"year-suffix"
|
|
19
|
+
],
|
|
5
20
|
"type": "module",
|
|
6
21
|
"exports": {
|
|
7
22
|
".": {
|
|
@@ -15,8 +30,8 @@
|
|
|
15
30
|
"README.md"
|
|
16
31
|
],
|
|
17
32
|
"dependencies": {
|
|
18
|
-
"@citestyle/core": "0.0
|
|
19
|
-
"@citestyle/types": "
|
|
33
|
+
"@citestyle/core": "1.0.0",
|
|
34
|
+
"@citestyle/types": "1.0.1"
|
|
20
35
|
},
|
|
21
36
|
"devDependencies": {
|
|
22
37
|
"vitest": "^3.1.1"
|
|
@@ -26,7 +41,6 @@
|
|
|
26
41
|
},
|
|
27
42
|
"license": "MIT",
|
|
28
43
|
"scripts": {
|
|
29
|
-
"test": "vitest run --passWithNoTests"
|
|
30
|
-
"lint": "echo 'TODO: configure linter'"
|
|
44
|
+
"test": "vitest run --passWithNoTests"
|
|
31
45
|
}
|
|
32
46
|
}
|
package/src/format.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple formatting API — format items without a full registry.
|
|
3
|
+
*
|
|
4
|
+
* Use these for one-off formatting where you don't need cross-reference
|
|
5
|
+
* features (year-suffix, citation numbering, disambiguation, collapsing).
|
|
6
|
+
* For documents with multiple citations, use createRegistry() instead.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format a single bibliography entry.
|
|
11
|
+
*
|
|
12
|
+
* @param {import('@citestyle/types').CompiledStyle} style - A compiled CSL style module
|
|
13
|
+
* @param {import('@citestyle/types').CslItem} item - CSL-JSON item
|
|
14
|
+
* @param {import('@citestyle/types').FormatContext} [ctx] - Optional format context
|
|
15
|
+
* @returns {import('@citestyle/types').FormattedEntry}
|
|
16
|
+
*/
|
|
17
|
+
export function format(style, item, ctx = {}) {
|
|
18
|
+
if (!style?.bibliography) {
|
|
19
|
+
throw new Error('format() requires a compiled style with a bibliography() function')
|
|
20
|
+
}
|
|
21
|
+
return style.bibliography(item, ctx)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Format multiple bibliography entries with basic sorting.
|
|
26
|
+
*
|
|
27
|
+
* Items are sorted using the style's sort comparator (if any) and assigned
|
|
28
|
+
* citation numbers in order. Does NOT perform year-suffix disambiguation,
|
|
29
|
+
* name disambiguation, or subsequent-author-substitute — use createRegistry()
|
|
30
|
+
* for those features.
|
|
31
|
+
*
|
|
32
|
+
* @param {import('@citestyle/types').CompiledStyle} style - A compiled CSL style module
|
|
33
|
+
* @param {import('@citestyle/types').CslItem[]} items - CSL-JSON items
|
|
34
|
+
* @param {import('@citestyle/types').FormatContext} [ctx] - Optional format context
|
|
35
|
+
* @returns {import('@citestyle/types').FormattedEntry[]}
|
|
36
|
+
*/
|
|
37
|
+
export function formatAll(style, items, ctx = {}) {
|
|
38
|
+
if (!style?.bibliography) {
|
|
39
|
+
throw new Error('formatAll() requires a compiled style with a bibliography() function')
|
|
40
|
+
}
|
|
41
|
+
let sorted = [...items]
|
|
42
|
+
if (style.bibliographySort) {
|
|
43
|
+
sorted.sort(style.bibliographySort)
|
|
44
|
+
}
|
|
45
|
+
// Assign citation numbers (shallow-clone to avoid mutating user's items)
|
|
46
|
+
sorted = sorted.map((item, i) => ({ ...item, 'citation-number': i + 1 }))
|
|
47
|
+
return sorted.map(item => style.bibliography(item, ctx))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Format a single inline citation.
|
|
52
|
+
*
|
|
53
|
+
* @param {import('@citestyle/types').CompiledStyle} style - A compiled CSL style module
|
|
54
|
+
* @param {import('@citestyle/types').CiteRef[]} cites - Citation references with items
|
|
55
|
+
* @param {import('@citestyle/types').FormatContext} [ctx] - Optional format context
|
|
56
|
+
* @returns {import('@citestyle/types').FormattedCitation}
|
|
57
|
+
*/
|
|
58
|
+
export function formatCitation(style, cites, ctx = {}) {
|
|
59
|
+
if (!style?.citation) {
|
|
60
|
+
throw new Error('formatCitation() requires a compiled style with a citation() function')
|
|
61
|
+
}
|
|
62
|
+
// Assign citation numbers if not already set (shallow-clone to avoid mutating user's items)
|
|
63
|
+
const safeCites = cites.map((cite, i) => {
|
|
64
|
+
if (cite.item && cite.item['citation-number'] == null) {
|
|
65
|
+
return { ...cite, item: { ...cite.item, 'citation-number': i + 1 } }
|
|
66
|
+
}
|
|
67
|
+
return cite
|
|
68
|
+
})
|
|
69
|
+
return style.citation(safeCites, ctx)
|
|
70
|
+
}
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* - Citation number tracking (numeric styles)
|
|
9
9
|
* - Bibliography sorting (using compiled comparator)
|
|
10
10
|
* - Subsequent-author-substitute
|
|
11
|
+
*
|
|
12
|
+
* Also provides simple format/formatAll/formatCitation helpers for
|
|
13
|
+
* one-off formatting without a full registry.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
export { createRegistry } from './registry.js'
|
|
17
|
+
export { format, formatAll, formatCitation } from './format.js'
|
package/src/registry.js
CHANGED
|
@@ -87,8 +87,22 @@ export function createRegistry(style, options = {}) {
|
|
|
87
87
|
* Format a citation cluster.
|
|
88
88
|
* Resolves item IDs to items and delegates to the compiled style's citation().
|
|
89
89
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
90
|
+
* **Author suppression** (CSL `suppress-author` mode) is implemented as a
|
|
91
|
+
* post-render strip: render normally, then remove the
|
|
92
|
+
* `<span class="csl-author">…</span>` wrappers and any orphaned
|
|
93
|
+
* surrounding punctuation (leading "Smith, " or trailing space). The
|
|
94
|
+
* compiler doesn't emit a per-mode citation branch yet — the standard
|
|
95
|
+
* CSL way — so this layer fills the gap. Two ways to trigger it:
|
|
96
|
+
*
|
|
97
|
+
* - `ctx.mode === 'suppress-author'` (whole cluster, CSL-canonical)
|
|
98
|
+
* - `cite.suppressAuthor === true` on every cite in a single-cite cluster
|
|
99
|
+
*
|
|
100
|
+
* Mixed-suppression clusters (some cites suppressed, others not) aren't
|
|
101
|
+
* supported here — the cluster renderer would need per-cite re-rendering,
|
|
102
|
+
* which is more rework than the current consumer demands.
|
|
103
|
+
*
|
|
104
|
+
* @param {Array<{id: string, item?: object, locator?: string, label?: string, prefix?: string, suffix?: string, suppressAuthor?: boolean}>} cites
|
|
105
|
+
* @param {object} [ctx] - Format context. `mode: 'suppress-author'` strips the author from the rendered cluster.
|
|
92
106
|
* @returns {import('@citestyle/types').FormattedCitation}
|
|
93
107
|
*/
|
|
94
108
|
function cite(cites, ctx = {}) {
|
|
@@ -114,6 +128,7 @@ export function createRegistry(style, options = {}) {
|
|
|
114
128
|
if (c.label != null) citeObj.label = c.label
|
|
115
129
|
if (c.prefix != null) citeObj.prefix = c.prefix
|
|
116
130
|
if (c.suffix != null) citeObj.suffix = c.suffix
|
|
131
|
+
if (c.suppressAuthor) citeObj.suppressAuthor = true
|
|
117
132
|
|
|
118
133
|
// Attach per-item disambiguation overrides
|
|
119
134
|
const disambig = disambigState.get(String(item.id))
|
|
@@ -122,16 +137,26 @@ export function createRegistry(style, options = {}) {
|
|
|
122
137
|
return citeObj
|
|
123
138
|
})
|
|
124
139
|
|
|
140
|
+
// Decide whether to suppress the author across the whole cluster.
|
|
141
|
+
const allCitesSuppress = resolvedCites.length > 0 &&
|
|
142
|
+
resolvedCites.every(c => c.suppressAuthor)
|
|
143
|
+
const suppressAuthor = ctx.mode === 'suppress-author' || allCitesSuppress
|
|
144
|
+
|
|
125
145
|
// Apply cite collapsing
|
|
126
146
|
const collapse = style.meta?.collapse
|
|
147
|
+
let result
|
|
127
148
|
if (collapse === 'citation-number' && resolvedCites.length > 1) {
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
149
|
+
result = collapseNumericCitation(resolvedCites, ctx)
|
|
150
|
+
} else if ((collapse === 'year' || collapse === 'year-suffix' || collapse === 'year-suffix-ranged') && resolvedCites.length > 1) {
|
|
151
|
+
result = collapseAuthorDateCitation(resolvedCites, ctx, collapse)
|
|
152
|
+
} else {
|
|
153
|
+
result = style.citation(resolvedCites, ctx)
|
|
132
154
|
}
|
|
133
155
|
|
|
134
|
-
|
|
156
|
+
if (suppressAuthor) {
|
|
157
|
+
result = stripAuthor(result)
|
|
158
|
+
}
|
|
159
|
+
return result
|
|
135
160
|
}
|
|
136
161
|
|
|
137
162
|
// ── Numeric cite collapsing ──────────────────────────────────────────────
|
|
@@ -565,6 +590,59 @@ export function createRegistry(style, options = {}) {
|
|
|
565
590
|
|
|
566
591
|
// ── Helper functions ──────────────────────────────────────────────────────
|
|
567
592
|
|
|
593
|
+
/**
|
|
594
|
+
* Strip the author from a formatted citation result.
|
|
595
|
+
*
|
|
596
|
+
* Citation styles that opt into `<span class="csl-author">` wrappers
|
|
597
|
+
* (every shipped style does today) can have their author portion
|
|
598
|
+
* removed by string-substitution: drop the wrapper and clean up
|
|
599
|
+
* orphaned surrounding punctuation. The result is the
|
|
600
|
+
* "(2024, p. 42)" / "(2024)" form authors reach for when the author's
|
|
601
|
+
* name already appears in the running prose.
|
|
602
|
+
*
|
|
603
|
+
* Cleanup rules:
|
|
604
|
+
* - "Author, " (author span followed by comma + space) -> ""
|
|
605
|
+
* - "Author " (author span followed by space) -> ""
|
|
606
|
+
* - bare "Author" with no trailing punctuation -> ""
|
|
607
|
+
*
|
|
608
|
+
* The text form is rebuilt from the stripped HTML rather than parallel-
|
|
609
|
+
* stripped — keeps the two views in sync without duplicating the
|
|
610
|
+
* regex set.
|
|
611
|
+
*/
|
|
612
|
+
function stripAuthor(result) {
|
|
613
|
+
if (!result || typeof result.html !== 'string') return result
|
|
614
|
+
|
|
615
|
+
let html = result.html
|
|
616
|
+
// Remove every csl-author span plus a trailing ", " or " " if present.
|
|
617
|
+
// Multi-cite clusters can have more than one — flag g.
|
|
618
|
+
html = html.replace(
|
|
619
|
+
/<span class="csl-author"[^>]*>[\s\S]*?<\/span>(,\s*|\s+)?/g,
|
|
620
|
+
'',
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
// Tidy any leftover doubled spaces or stray opening punctuation.
|
|
624
|
+
html = html
|
|
625
|
+
.replace(/\(\s+/g, '(')
|
|
626
|
+
.replace(/\s{2,}/g, ' ')
|
|
627
|
+
.replace(/\s+,/g, ',')
|
|
628
|
+
.replace(/\(,\s*/g, '(')
|
|
629
|
+
|
|
630
|
+
const text = htmlToPlainText(html)
|
|
631
|
+
return { text, html }
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** Strip HTML tags and decode the entity subset citestyle emits. */
|
|
635
|
+
function htmlToPlainText(html) {
|
|
636
|
+
if (!html) return ''
|
|
637
|
+
return String(html)
|
|
638
|
+
.replace(/<[^>]+>/g, '')
|
|
639
|
+
.replace(/&/g, '&')
|
|
640
|
+
.replace(/</g, '<')
|
|
641
|
+
.replace(/>/g, '>')
|
|
642
|
+
.replace(/"/g, '"')
|
|
643
|
+
.replace(/'/g, "'")
|
|
644
|
+
}
|
|
645
|
+
|
|
568
646
|
/**
|
|
569
647
|
* Apply subsequent-author-substitute to formatted bibliography entries.
|
|
570
648
|
*/
|
package/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createRegistry, Registry, RegistryOptions } from '@citestyle/types'
|
|
1
|
+
export { createRegistry, format, formatAll, formatCitation, Registry, RegistryOptions } from '@citestyle/types'
|