@u1f992/vivliostyle-index 0.2.4 → 0.3.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.
Files changed (102) hide show
  1. package/README.md +230 -24
  2. package/dist/document-renderer.d.ts +7 -0
  3. package/dist/document-renderer.d.ts.map +1 -0
  4. package/dist/document-renderer.js +86 -0
  5. package/dist/document-renderer.js.map +1 -0
  6. package/dist/file-system.d.ts +11 -0
  7. package/dist/file-system.d.ts.map +1 -0
  8. package/dist/file-system.js +93 -0
  9. package/dist/file-system.js.map +1 -0
  10. package/dist/html.d.ts +4 -0
  11. package/dist/html.d.ts.map +1 -0
  12. package/dist/html.js +9 -0
  13. package/dist/html.js.map +1 -0
  14. package/dist/id.d.ts +5 -0
  15. package/dist/id.d.ts.map +1 -0
  16. package/dist/id.js +30 -0
  17. package/dist/id.js.map +1 -0
  18. package/dist/index-builder.d.ts +15 -0
  19. package/dist/index-builder.d.ts.map +1 -0
  20. package/dist/index-builder.js +138 -0
  21. package/dist/index-builder.js.map +1 -0
  22. package/dist/index-state.d.ts +29 -0
  23. package/dist/index-state.d.ts.map +1 -0
  24. package/dist/index-state.js +95 -0
  25. package/dist/index-state.js.map +1 -0
  26. package/dist/index.d.ts +45 -15
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +53 -122
  29. package/dist/index.js.map +1 -1
  30. package/dist/instruction.d.ts +37 -0
  31. package/dist/instruction.d.ts.map +1 -0
  32. package/dist/instruction.js +267 -0
  33. package/dist/instruction.js.map +1 -0
  34. package/dist/location.d.ts +2 -0
  35. package/dist/location.d.ts.map +1 -0
  36. package/dist/location.js +17 -0
  37. package/dist/location.js.map +1 -0
  38. package/dist/messages.d.ts +24 -0
  39. package/dist/messages.d.ts.map +1 -0
  40. package/dist/messages.js +114 -0
  41. package/dist/messages.js.map +1 -0
  42. package/dist/model.d.ts +77 -36
  43. package/dist/model.d.ts.map +1 -1
  44. package/dist/model.js +112 -148
  45. package/dist/model.js.map +1 -1
  46. package/dist/platform.d.ts +4 -0
  47. package/dist/platform.d.ts.map +1 -0
  48. package/dist/platform.js +13 -0
  49. package/dist/platform.js.map +1 -0
  50. package/dist/profile.d.ts +11 -0
  51. package/dist/profile.d.ts.map +1 -0
  52. package/dist/profile.js +17 -0
  53. package/dist/profile.js.map +1 -0
  54. package/dist/render.d.ts +175 -0
  55. package/dist/render.d.ts.map +1 -0
  56. package/dist/render.js +285 -0
  57. package/dist/render.js.map +1 -0
  58. package/dist/sort.d.ts +22 -16
  59. package/dist/sort.d.ts.map +1 -1
  60. package/dist/sort.js +91 -53
  61. package/dist/sort.js.map +1 -1
  62. package/dist/source-snapshot.d.ts +21 -0
  63. package/dist/source-snapshot.d.ts.map +1 -0
  64. package/dist/source-snapshot.js +76 -0
  65. package/dist/source-snapshot.js.map +1 -0
  66. package/dist/target.d.ts +9 -0
  67. package/dist/target.d.ts.map +1 -0
  68. package/dist/target.js +17 -0
  69. package/dist/target.js.map +1 -0
  70. package/dist/template.d.ts +4 -0
  71. package/dist/template.d.ts.map +1 -0
  72. package/dist/template.js +15 -0
  73. package/dist/template.js.map +1 -0
  74. package/package.json +25 -17
  75. package/dist/command/expand.d.ts +0 -5
  76. package/dist/command/expand.d.ts.map +0 -1
  77. package/dist/command/expand.js +0 -193
  78. package/dist/command/expand.js.map +0 -1
  79. package/dist/command/insert-page.d.ts +0 -5
  80. package/dist/command/insert-page.d.ts.map +0 -1
  81. package/dist/command/insert-page.js +0 -32
  82. package/dist/command/insert-page.js.map +0 -1
  83. package/dist/command/insert-range.d.ts +0 -21
  84. package/dist/command/insert-range.d.ts.map +0 -1
  85. package/dist/command/insert-range.js +0 -61
  86. package/dist/command/insert-range.js.map +0 -1
  87. package/dist/command/insert-reference.d.ts +0 -4
  88. package/dist/command/insert-reference.d.ts.map +0 -1
  89. package/dist/command/insert-reference.js +0 -58
  90. package/dist/command/insert-reference.js.map +0 -1
  91. package/dist/command.d.ts +0 -25
  92. package/dist/command.d.ts.map +0 -1
  93. package/dist/command.js +0 -159
  94. package/dist/command.js.map +0 -1
  95. package/dist/resolve.d.ts +0 -3
  96. package/dist/resolve.d.ts.map +0 -1
  97. package/dist/resolve.js +0 -95
  98. package/dist/resolve.js.map +0 -1
  99. package/dist/util.d.ts +0 -10
  100. package/dist/util.d.ts.map +0 -1
  101. package/dist/util.js +0 -27
  102. package/dist/util.js.map +0 -1
package/README.md CHANGED
@@ -1,24 +1,230 @@
1
- # @u1f992/vivliostyle-index
2
-
3
- [unified](https://github.com/unifiedjs/unified) plugin that integrates automatic index generation into [Vivliostyle CLI](https://github.com/vivliostyle/vivliostyle-cli) workflows.
4
-
5
- This project seeks to align with the requirements presented in 藤田節子's『[本の索引の作り方](http://www.chijinshokan.co.jp/Books/ISBN978-4-8052-0932-5.htm)(地人書館) and [SIST 13 索引作成](https://jipsti.jst.go.jp/sist/handbook/sist13/sist13_m.htm).
6
-
7
- ## Usage
8
-
9
- The basic concept is to embed index manipulation commands in the `data-index` attribute using the format `func,args...`.
10
-
11
- The command body is a YAML list with top-level brackets omitted. Token and escaping rules follow YAML conventions, plus HTML attribute character encoding. For most words as headings, simply use `foobar`, but for example, to make `null` a heading word, write `"null"`, which becomes `"null"`.
12
-
13
- | Format | Description |
14
- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `(page,)<id>,<heading>` | Adds a locator (page). Creates the index and heading specified by `<id>` and `<heading>` if they don't exist (`(page,)` must always be omitted). |
16
- | `page!,<id>,<heading>` | Adds a high-importance locator (page). Creates the index and heading specified by `<id>` and `<heading>` if they don't exist. |
17
- | `range,<id>,<heading>,<rangeId>` | Starts a locator (range). Not added to the index until properly closed. |
18
- | `range!,<id>,<heading>,<rangeId>` | Starts a high-importance locator (range). Not added to the index until properly closed. |
19
- | `/range,<rangeId>` | Closes the corresponding locator (range) and adds it to the index. Creates the index and heading specified by `<id>` and `<heading>` from `range`/`range!` if they don't exist. |
20
- | `see,<id>,<heading>,<target>` | Adds a "see" reference. Creates the index and heading specified by `<id>` and `<heading>` if they don't exist, but `<target>` is not automatically created. |
21
- | `seeAlso,<id>,<heading>,<target>` | Adds a "see also" reference. Creates the index and heading specified by `<id>` and `<heading>` if they don't exist, but `<target>` is not automatically created. |
22
- | `expand,<id>` | Expands the index specified by `<id>` as a child of this element. |
23
-
24
- Furthermore, `<heading>` and `<target>` follow the format `[Key, Key] | [Key, Key, Key]` where `type Key = null | string | [null, string] | [string, string]`. The basic form of `Key` is a tuple consisting of the heading's innerHTML (JSON representation of `hast.Element.children: hast.ElementContent[]`) and reading for sorting. When the heading is `null`, the innerHTML of the element with the `data-index` attribute is used. `null | string` should be considered as a tuple with the reading omitted. In this case, after collecting all index items, the reading is supplemented from existing items with matching [innerText](https://github.com/syntax-tree/hast-util-to-text), or if none exist, the innerText of `key[0]` is used as the reading. Taking two `Key`s corresponds to group heading and main heading, while three `Key`s correspond to group heading, main heading, and subheading.
1
+ # @u1f992/vivliostyle-index
2
+
3
+ A [unified](https://github.com/unifiedjs/unified) plugin for generating indexes in [Vivliostyle CLI](https://github.com/vivliostyle/vivliostyle-cli) builds.
4
+
5
+ The indexing rules are based on the Japan Science and Technology Agency's SIST 13 (n.d.); Fujita (2019) provides an abridged commentary on it.
6
+
7
+ The default output follows and extends the index structure specified by the DAISY Consortium (n.d.). The plugin's `data-index-role` vocabulary similarly draws on and extends the index terms defined by the EPUB 3 Structural Semantics Vocabulary 1.1 (Herman and Garrish 2026). Index targets carry the DPUB-ARIA `doc-index` role (Garrish and Siegman 2025).
8
+
9
+ ## Concept
10
+
11
+ This plugin encodes index-building instructions in URLs. A path-and-fragment pair uniquely identifies an index target, while the `q` query parameter contains the instruction. The instruction uses a DSL inspired by MakeIndex and upmendex; the following sections define its grammar.
12
+
13
+ See [the basic example configuration](examples/basic/vivliostyle.config.ts) for the required plugin setup. After configuring the plugin, add the following reference to `chapter.md`:
14
+
15
+ ```html
16
+ <span data-index="index.md?q=a!Apple#index">Apple</span>
17
+ ```
18
+
19
+ In `index.md` in the same directory, provide the target navigation element:
20
+
21
+ ```html
22
+ <nav id="index" role="doc-index"></nav>
23
+ ```
24
+
25
+ The `data-index` value is a relative URL resolved against the document that contains it. Its path and fragment identify the `<nav>` in `index.md` as the index target; that element must carry the `doc-index` role. The instruction `a!Apple` registers the entry `Apple` in the group `a`, using the position of the `<span>` as its locator. The plugin fills the `<nav>` with the built index, and the generated locator links back to the `<span>`. If the `<span>` has no `id`, the plugin assigns one for this link. If a reference omits the path, its target is resolved within the referring document, allowing a document to host its own index.
26
+
27
+ As a consequence of this design, the `q` value is interpreted at two syntactic layers: first as part of a URL, then as an `application/x-www-form-urlencoded` query parameter. For a uniform reversible encoding, percent-encode `#`, `&`, `+`, and `%` in a raw instruction before placing it in `q`.
28
+
29
+ At the URL layer, `#` ends the query and starts the fragment ([query state](https://url.spec.whatwg.org/#query-state)). During query parsing, `&` separates parameters, `+` denotes a space, and a `%` followed by two ASCII hexadecimal digits introduces a percent-encoded byte ([`application/x-www-form-urlencoded` parsing](https://url.spec.whatwg.org/#urlencoded-parsing)) (WHATWG n.d.). Encoding every `%` prevents a sequence in the raw instruction from being decoded accidentally. For example, the instruction `a@A!ampersand@&|seealso{p@P!plus@+}` is written as:
30
+
31
+ ```
32
+ index.md?q=a@A!ampersand@%26|seealso{p@P!plus@%2B}#index
33
+ ```
34
+
35
+ The following `sed` command percent-encodes those four characters in a raw instruction:
36
+
37
+ ```shellsession
38
+ $ printf '%s\n' 'a@A!ampersand@&|seealso{p@P!plus@+}' | sed 's/%/%25/g; s/#/%23/g; s/&/%26/g; s/+/%2B/g'
39
+ a@A!ampersand@%26|seealso{p@P!plus@%2B}
40
+ ```
41
+
42
+ ## Index modeling
43
+
44
+ A document contains zero or more indexes. Each index contains zero or more groups, and each group contains zero or more entries (Japanese: 「主見出し」). An entry contains zero or more locators (Japanese: 「所在指示」), preferred cross-references (`see`; Japanese: 「を見よ参照」), related cross-references (`see also`; Japanese: 「をも見よ参照」), and subentries (Japanese: 「副見出し」). A subentry has the same locator and cross-reference collections as an entry, but cannot contain further subentries.
45
+
46
+ Locators distinguish single pages from page ranges. Modeling every locator as a range would require the renderer to collapse equal endpoints into a single-page form.[^locator-css] A cross-reference targets either an entry or a subentry. Each locator and cross-reference also has an HTML template, primarily used to wrap its rendered form in decorative markup.
47
+
48
+ [^locator-css]: CSS cross-reference functions such as [`target-counter()`](https://drafts.csswg.org/css-content-3/#target-counter) and [`target-text()`](https://drafts.csswg.org/css-content-3/#target-text) retrieve information from one target at a time. They provide no conditional mechanism for comparing two range endpoints and collapsing the output when both resolve to the same page.
49
+
50
+ The following class diagram summarizes these relationships.
51
+
52
+ ```mermaid
53
+ classDiagram
54
+ direction LR
55
+
56
+ Document "1" *-- "0..*" Index
57
+ Index "1" *-- "0..*" Group
58
+ Group "1" *-- "0..*" Entry
59
+ Entry "1" *-- "0..*" Locator
60
+ Entry "1" *-- "0..*" PreferredXref
61
+ Entry "1" *-- "0..*" RelatedXref
62
+ Entry "1" *-- "0..*" Subentry
63
+ Subentry "1" *-- "0..*" Locator
64
+ Subentry "1" *-- "0..*" PreferredXref
65
+ Subentry "1" *-- "0..*" RelatedXref
66
+ Locator <|-- PageLocator
67
+ Locator <|-- RangeLocator
68
+ Xref <|-- PreferredXref
69
+ Xref <|-- RelatedXref
70
+ XrefTarget <|-- Entry
71
+ XrefTarget <|-- Subentry
72
+ Xref "0..*" --> "1" XrefTarget
73
+ Locator "1" *-- "1" Template
74
+ Xref "1" *-- "1" Template
75
+ ```
76
+
77
+ The implementation keys each index by the path-and-fragment pair of its output target; together they identify a DOM element. Each group, entry, and subentry key is a pair of `reading` and `html` values. In the DSL, this pair is written as `reading@display`; the display value becomes the key's inner HTML. A cross-reference identifies its target by the group and entry keys, plus the subentry key when present.
78
+
79
+ ## Instruction syntax
80
+
81
+ An indexing reference carries its instruction in the `q` parameter of its `data-index` attribute. The instruction is the decoded value returned for `q` after parsing the URL query, including `+`-to-space conversion and percent-decoding. The lexer scans the decoded instruction from left to right. A backslash starts an escape; otherwise, the lexer selects the longest recognized token at each position and treats unmatched characters as literal text.
82
+
83
+ | Token | Role |
84
+ | --- | --- |
85
+ | `!` | Separates the group, entry, and optional subentry of an address |
86
+ | `@` | Separates a reading from its display value within a group, entry, or subentry |
87
+ | `\|` | Introduces a template |
88
+ | `\|(` | Marks a range start |
89
+ | `\|)` | Marks a range end |
90
+ | `\|see{` | Opens the target of a preferred cross-reference |
91
+ | `\|seealso{` | Opens the target of a related cross-reference |
92
+ | `}` | Closes a cross-reference target |
93
+
94
+ ### Escaping
95
+
96
+ A backslash makes the next character literal text. Only `\`, `@`, `!`, `|`, `(`, `)`, `{`, and `}` can be escaped; a backslash before any other character is a syntax error.
97
+
98
+ An escaped character never participates in token matching. Escaping the leading `|` makes an entire `|`-prefixed sequence literal: `\|see{` spells the text `|see{`. Escaping a later character instead can cause the leading `|` to match the standalone template introducer; for example, `|see\{` introduces a template containing `see{`.
99
+
100
+ `(`, `)`, and `{` are not tokens on their own, but escaping them can prevent `|(`, `|)`, `|see{`, or `|seealso{` from matching. Escape `}` when it should be text rather than a cross-reference closer.
101
+
102
+ ### Accepted instructions
103
+
104
+ Common instruction forms are shown below.
105
+
106
+ | Form | Example |
107
+ | --- | --- |
108
+ | Page | `a!Apple` |
109
+ | Range start | `a!Apple|(` |
110
+ | Range end | `a!Apple|)` |
111
+ | Preferred cross-reference | `a!Apple|see{b!Banana}` |
112
+ | Related cross-reference | `a!Apple|seealso{b!Banana}` |
113
+ | Template | `a!Apple|<strong><slot></slot></strong>` |
114
+
115
+ The diagram below shows every token sequence that forms an accepted instruction. Token-consuming transitions are labeled with tokens. Here, `text` means either a maximal run of literal characters or one escaped character. An unlabeled transition to `[*]` indicates that the instruction may end in the source state; an instruction is accepted only if all input has been consumed in such a state. A path through `range start` or `range end` represents a range boundary; one through `cross-reference target` represents a cross-reference; any other accepted path represents a page reference.
116
+
117
+ ```mermaid
118
+ stateDiagram-v2
119
+ direction TB
120
+
121
+ state "address" as address {
122
+ direction TB
123
+ state "group" as a_g
124
+ state "group reading" as a_gr
125
+ state "group @" as a_ga
126
+ state "group display" as a_gd
127
+ state "entry" as a_e
128
+ state "entry reading" as a_er
129
+ state "entry @" as a_ea
130
+ state "entry display" as a_ed
131
+ state "subentry" as a_s
132
+ state "subentry reading" as a_sr
133
+ state "subentry @" as a_sa
134
+ state "subentry display" as a_sd
135
+ [*] --> a_g
136
+ a_g --> a_gr : text
137
+ a_gr --> a_gr : text
138
+ a_gr --> a_ga : @
139
+ a_ga --> a_gd : text
140
+ a_gd --> a_gd : text
141
+ a_gr --> a_e : !
142
+ a_gd --> a_e : !
143
+ a_e --> a_er : text
144
+ a_er --> a_er : text
145
+ a_er --> a_ea : @
146
+ a_ea --> a_ed : text
147
+ a_ed --> a_ed : text
148
+ a_er --> a_s : !
149
+ a_ed --> a_s : !
150
+ a_s --> a_sr : text
151
+ a_sr --> a_sr : text
152
+ a_sr --> a_sa : @
153
+ a_sa --> a_sd : text
154
+ a_sd --> a_sd : text
155
+ a_er --> [*]
156
+ a_ed --> [*]
157
+ a_sr --> [*]
158
+ a_sd --> [*]
159
+ }
160
+
161
+ state "cross-reference target" as target {
162
+ direction TB
163
+ state "group" as t_g
164
+ state "group reading" as t_gr
165
+ state "group @" as t_ga
166
+ state "group display" as t_gd
167
+ state "entry" as t_e
168
+ state "entry reading" as t_er
169
+ state "entry @" as t_ea
170
+ state "entry display" as t_ed
171
+ state "subentry" as t_s
172
+ state "subentry reading" as t_sr
173
+ state "subentry @" as t_sa
174
+ state "subentry display" as t_sd
175
+ [*] --> t_g
176
+ t_g --> t_gr : text
177
+ t_gr --> t_gr : text
178
+ t_gr --> t_ga : @
179
+ t_ga --> t_gd : text
180
+ t_gd --> t_gd : text
181
+ t_gr --> t_e : !
182
+ t_gd --> t_e : !
183
+ t_e --> t_er : text
184
+ t_er --> t_er : text
185
+ t_er --> t_ea : @
186
+ t_ea --> t_ed : text
187
+ t_ed --> t_ed : text
188
+ t_er --> t_s : !
189
+ t_ed --> t_s : !
190
+ t_s --> t_sr : text
191
+ t_sr --> t_sr : text
192
+ t_sr --> t_sa : @
193
+ t_sa --> t_sd : text
194
+ t_sd --> t_sd : text
195
+ t_er --> [*]
196
+ t_ed --> [*]
197
+ t_sr --> [*]
198
+ t_sd --> [*]
199
+ }
200
+
201
+ state "range start" as range_start
202
+ state "range end" as range_end
203
+ state "target closed" as xref_closed
204
+ state "template" as template
205
+
206
+ [*] --> address
207
+ address --> [*]
208
+ address --> template : |
209
+ address --> range_start : |(
210
+ address --> range_end : |)
211
+ address --> target : |see{
212
+ address --> target : |seealso{
213
+ range_start --> [*]
214
+ range_start --> template : |
215
+ range_end --> [*]
216
+ target --> xref_closed : }
217
+ xref_closed --> [*]
218
+ xref_closed --> template : |
219
+ template --> template : text
220
+ template --> [*]
221
+ ```
222
+
223
+ ## References
224
+
225
+ - DAISY Consortium. n.d. “[Indexes](https://kb.daisy.org/publishing/docs/html/indexes.html).” *Accessible Publishing Knowledge Base*. Accessed August 18, 2026. Revision history: [publishing/docs/html/indexes.html](https://github.com/DAISY/kb/commits/main/publishing/docs/html/indexes.html).
226
+ - Fujita, Setsuko. 2019. *[本の索引の作り方](http://www.chijinshokan.co.jp/Books/ISBN978-4-8052-0932-5.htm)*. 地人書館.
227
+ - Garrish, Matt, and Tzviya Siegman, eds. 2025. “[`doc-index`](https://www.w3.org/TR/dpub-aria-1.1/#doc-index).” *Digital Publishing WAI-ARIA Module 1.1*. W3C Recommendation, June 12, 2025. World Wide Web Consortium. Revision history: [dpub-aria/index.html](https://github.com/w3c/aria/commits/main/dpub-aria/index.html).
228
+ - Herman, Ivan, and Matt Garrish, eds. 2026. “[Indexes](https://www.w3.org/TR/epub-ssv/#h_indexes).” *EPUB 3 Structural Semantics Vocabulary 1.1*. W3C Group Note, May 28, 2026. World Wide Web Consortium. Revision history: [wg-notes/ssv/index.html](https://github.com/w3c/epub-specs/commits/main/wg-notes/ssv/index.html).
229
+ - Japan Science and Technology Agency. n.d. “[SIST 13 索引作成](https://warp.ndl.go.jp/20220119/20220113214526id_/jipsti.jst.go.jp/sist/handbook/sist13/sist13_m.htm) [Indexes and Indexing].” Archived January 13, 2022, by the National Diet Library Web Archiving Project. Accessed August 18, 2026.
230
+ - WHATWG. n.d. “[URL](https://url.spec.whatwg.org/).” *WHATWG Living Standard*. Accessed August 19, 2026. Revision history: [url.bs](https://github.com/whatwg/url/commits/main/url.bs).
@@ -0,0 +1,7 @@
1
+ import type * as hast from "hast";
2
+ import type { VFile } from "vfile";
3
+ import type { BuiltIndex } from "./index-builder.ts";
4
+ import { type ResolvedIndexProfile } from "./profile.ts";
5
+ import type { TargetKey } from "./target.ts";
6
+ export declare function renderDocumentIndexes(root: hast.Root, documentPath: string, indexes: ReadonlyMap<TargetKey, BuiltIndex>, profiles: ReadonlyMap<string, ResolvedIndexProfile>, file: VFile): void;
7
+ //# sourceMappingURL=document-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-renderer.d.ts","sourceRoot":"","sources":["../src/document-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,MAAM,CAAC;AAIlC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAyE7C,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,EAC3C,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,EACnD,IAAI,EAAE,KAAK,GACV,IAAI,CAqBN"}
@@ -0,0 +1,86 @@
1
+ import { getAttribute } from "hast-util-get-attribute";
2
+ import { h } from "hastscript";
3
+ import { EXIT, visitParents } from "unist-util-visit-parents";
4
+ import { messages } from "./messages.js";
5
+ import { defaultProfile } from "./profile.js";
6
+ import { renderIndex } from "./render.js";
7
+ import { sort } from "./sort.js";
8
+ function collatableLanguage(language) {
9
+ try {
10
+ return Intl.Collator.supportedLocalesOf(language).length !== 0;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ function resolveLocales(language, file) {
17
+ if (language === undefined || language === "") {
18
+ return "en";
19
+ }
20
+ if (collatableLanguage(language)) {
21
+ return language;
22
+ }
23
+ file.message(...messages.unsupportedLanguage(language));
24
+ return "en";
25
+ }
26
+ function findTargetElement(root, id) {
27
+ // Valid HTML IDs are not necessarily valid unescaped CSS ID selectors.
28
+ let found;
29
+ visitParents(root, "element", (element, ancestors) => {
30
+ if (getAttribute(element, "id") !== id) {
31
+ return;
32
+ }
33
+ const language = [...ancestors, element]
34
+ .reverse()
35
+ .flatMap((ancestor) => {
36
+ const lang = ancestor.type === "element" ? getAttribute(ancestor, "lang") : null;
37
+ return lang === null ? [] : [lang];
38
+ })
39
+ .at(0);
40
+ found = { element, language };
41
+ return EXIT;
42
+ });
43
+ return found;
44
+ }
45
+ // The role attribute holds a token list (https://www.w3.org/TR/wai-aria-1.2/#host_general_role)
46
+ // split on the host language's ASCII whitespace, which is narrower than \s.
47
+ const roleSeparator = /[\t\n\f\r ]+/;
48
+ function carriesDocIndexRole(element) {
49
+ const role = getAttribute(element, "role");
50
+ return role !== null && role.split(roleSeparator).includes("doc-index");
51
+ }
52
+ function resolveProfile(element, target, profiles, file) {
53
+ const profileName = getAttribute(element, "data-index-profile");
54
+ if (profileName === null) {
55
+ return defaultProfile;
56
+ }
57
+ const profile = profiles.get(profileName);
58
+ if (profile === undefined) {
59
+ file.message(...messages.unknownIndexProfile(target, profileName));
60
+ return defaultProfile;
61
+ }
62
+ return profile;
63
+ }
64
+ export function renderDocumentIndexes(root, documentPath, indexes, profiles, file) {
65
+ for (const { target, index } of indexes.values()) {
66
+ if (target.path !== documentPath) {
67
+ continue;
68
+ }
69
+ const found = findTargetElement(root, target.fragment);
70
+ if (!found) {
71
+ file.message(...messages.missingIndexTarget(target));
72
+ continue;
73
+ }
74
+ const { element, language } = found;
75
+ if (!carriesDocIndexRole(element)) {
76
+ file.message(...messages.missingIndexRole(target));
77
+ continue;
78
+ }
79
+ const profile = resolveProfile(element, target, profiles, file);
80
+ const comparator = profile.comparator(resolveLocales(language, file));
81
+ const sorted = sort(index, comparator);
82
+ const renderer = profile.renderer({ h, index: sorted });
83
+ renderIndex(sorted, element, target.fragment, renderer);
84
+ }
85
+ }
86
+ //# sourceMappingURL=document-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-renderer.js","sourceRoot":"","sources":["../src/document-renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import fs from "node:fs";
2
+ export type FileSystem = {
3
+ readFileSync: (path: string) => string;
4
+ touchSync: (path: string) => void;
5
+ };
6
+ type BaseFileSystem = Pick<typeof fs, "readFileSync" | "utimesSync" | "statSync" | "openSync" | "writeSync" | "ftruncateSync" | "closeSync">;
7
+ type ExecuteFileSync = (file: string, args: readonly string[]) => void;
8
+ export declare function createFileSystem(base: BaseFileSystem, execute?: ExecuteFileSync): FileSystem;
9
+ export declare const nodeFileSystem: FileSystem;
10
+ export {};
11
+ //# sourceMappingURL=file-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,cAAc,GACd,YAAY,GACZ,UAAU,GACV,UAAU,GACV,WAAW,GACX,eAAe,GACf,WAAW,CACd,CAAC;AAEF,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;AAiGvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,CAK5F;AAED,eAAO,MAAM,cAAc,EAAE,UAAkD,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ const systemTouchExecutables = ["/usr/bin/touch", "/bin/touch"];
4
+ const executeFileSync = (file, args) => {
5
+ execFileSync(file, args);
6
+ };
7
+ function updateTimestamp(base, fileName) {
8
+ const time = new Date();
9
+ base.utimesSync(fileName, time, time);
10
+ }
11
+ function appendAndRestoreSize(base, fileName) {
12
+ // Opening and closing a writable file does not update mtime. A real write does,
13
+ // so the last-resort path appends one byte and truncates it immediately. The
14
+ // original bytes and size survive, while the filesystem still records the write.
15
+ const originalSize = base.statSync(fileName).size;
16
+ const fd = base.openSync(fileName, "a");
17
+ try {
18
+ base.writeSync(fd, " ");
19
+ base.ftruncateSync(fd, originalSize);
20
+ }
21
+ finally {
22
+ base.closeSync(fd);
23
+ }
24
+ }
25
+ function hasErrorCode(error, code) {
26
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
27
+ }
28
+ // execFileSync copies spawnSync fields onto both spawn errors and child exit errors.
29
+ // A spawn error still has status and signal properties, but both values are null;
30
+ // a started child has either a numeric exit status or a terminating signal.
31
+ function processStarted(error) {
32
+ return (typeof error === "object" &&
33
+ error !== null &&
34
+ (("status" in error && error.status !== null && error.status !== undefined) ||
35
+ ("signal" in error && error.signal !== null && error.signal !== undefined)));
36
+ }
37
+ function executeSystemTouch(base, execute, fileName) {
38
+ // POSIX distinguishes explicit timestamps from "set both fields to now".
39
+ // Node's utimesSync supplies explicit values, which normally requires the
40
+ // file owner or CAP_FOWNER on Linux. The system touch command can request
41
+ // the kernel's current-time operation, which also permits a writable file
42
+ // owned by another UID.
43
+ //
44
+ // npm prepends node_modules/.bin to PATH. Absolute system locations prevent
45
+ // a package-provided executable from taking the place of the OS utility.
46
+ for (const executable of systemTouchExecutables) {
47
+ try {
48
+ // The option terminator keeps names such as "--help" from being parsed as
49
+ // command options. execFileSync also avoids shell interpretation entirely.
50
+ execute(executable, ["-c", "--", fileName]);
51
+ }
52
+ catch (error) {
53
+ // Spawn failures have no child status or signal and can arise from a missing
54
+ // executable, noexec mounts, or Node's process permissions. Once a child has
55
+ // started, its failure describes the target filesystem and must be preserved.
56
+ if (!processStarted(error)) {
57
+ continue;
58
+ }
59
+ throw error;
60
+ }
61
+ // touch -c deliberately reports success when the target is absent. Restore
62
+ // the FileSystem contract by checking that the requested file still exists.
63
+ base.statSync(fileName);
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+ function touchSync(base, execute, fileName) {
69
+ try {
70
+ // The Node API avoids spawning a process and preserves its precise filesystem
71
+ // errors. EPERM is exceptional because the OS current-time operation has a
72
+ // less restrictive permission model than Node's explicit timestamps.
73
+ updateTimestamp(base, fileName);
74
+ return;
75
+ }
76
+ catch (error) {
77
+ if (!hasErrorCode(error, "EPERM")) {
78
+ throw error;
79
+ }
80
+ }
81
+ if (execute && executeSystemTouch(base, execute, fileName)) {
82
+ return;
83
+ }
84
+ appendAndRestoreSize(base, fileName);
85
+ }
86
+ export function createFileSystem(base, execute) {
87
+ return {
88
+ readFileSync: (path) => base.readFileSync(path, { encoding: "utf-8" }),
89
+ touchSync: (path) => touchSync(base, execute, path),
90
+ };
91
+ }
92
+ export const nodeFileSystem = createFileSystem(fs, executeFileSync);
93
+ //# sourceMappingURL=file-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-system.js","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"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"}
package/dist/html.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type * as hast from "hast";
2
+ export declare function parseFragment(html: string): hast.ElementContent[];
3
+ export declare function fragmentToText(html: string): string;
4
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,MAAM,CAAC;AAIlC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAIjE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,UAE1C"}
package/dist/html.js ADDED
@@ -0,0 +1,9 @@
1
+ import { fromHtml } from "hast-util-from-html";
2
+ import { toText } from "hast-util-to-text";
3
+ export function parseFragment(html) {
4
+ return fromHtml(html, { fragment: true }).children.filter((child) => child.type !== "doctype");
5
+ }
6
+ export function fragmentToText(html) {
7
+ return toText(fromHtml(html, { fragment: true }));
8
+ }
9
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CACvD,CAAC,KAAK,EAAgC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAClE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC"}
package/dist/id.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { Key } from "./model.ts";
2
+ export declare const createSourceId: (xpath: string) => string;
3
+ export declare const createEntryId: (indexId: string, group: Key, entry: Key) => string;
4
+ export declare const createSubentryId: (indexId: string, group: Key, entry: Key, subentry: Key) => string;
5
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAwCtC,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAAqC,CAAC;AAErF,eAAO,MAAM,aAAa,YAAa,MAAM,SAAS,GAAG,SAAS,GAAG,KAAG,MACV,CAAC;AAE/D,eAAO,MAAM,gBAAgB,YAAa,MAAM,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,KAAG,MACf,CAAC"}
package/dist/id.js ADDED
@@ -0,0 +1,30 @@
1
+ // TextEncoder is not injective: WHATWG Encoding replaces lone surrogates with U+FFFD.
2
+ const wtf8Bytes = (value) => {
3
+ const bytes = [];
4
+ for (const character of value) {
5
+ const codePoint = character.codePointAt(0);
6
+ if (codePoint <= 0x7f) {
7
+ bytes.push(codePoint);
8
+ }
9
+ else if (codePoint <= 0x7ff) {
10
+ bytes.push(0xc0 | (codePoint >> 6), 0x80 | (codePoint & 0x3f));
11
+ }
12
+ else if (codePoint <= 0xffff) {
13
+ bytes.push(0xe0 | (codePoint >> 12), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
14
+ }
15
+ else {
16
+ bytes.push(0xf0 | (codePoint >> 18), 0x80 | ((codePoint >> 12) & 0x3f), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
17
+ }
18
+ }
19
+ return bytes;
20
+ };
21
+ const encodeIdSegment = (value) => {
22
+ const binary = wtf8Bytes(value).reduce((binary, byte) => binary + String.fromCharCode(byte), "");
23
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
24
+ };
25
+ const keySegments = (keys) => keys.flatMap(({ reading, html }) => [reading, html]);
26
+ const createId = (type, segments) => ["index", type, ...segments.map(encodeIdSegment)].join(".");
27
+ export const createSourceId = (xpath) => createId("source", [xpath]);
28
+ export const createEntryId = (indexId, group, entry) => createId("entry", [indexId, ...keySegments([group, entry])]);
29
+ export const createSubentryId = (indexId, group, entry, subentry) => createId("subentry", [indexId, ...keySegments([group, entry, subentry])]);
30
+ //# sourceMappingURL=id.js.map
package/dist/id.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type MessageArguments } from "./messages.ts";
2
+ import { type Index } from "./model.ts";
3
+ import type { SourceSnapshot } from "./source-snapshot.ts";
4
+ import type { Target, TargetKey } from "./target.ts";
5
+ export type BuiltIndex = Readonly<{
6
+ target: Target;
7
+ index: Index;
8
+ sourcePaths: readonly string[];
9
+ }>;
10
+ export type BuiltIndexes = Readonly<{
11
+ indexes: ReadonlyMap<TargetKey, BuiltIndex>;
12
+ messages: ReadonlyMap<string, readonly MessageArguments[]>;
13
+ }>;
14
+ export declare function buildIndexes(entryPaths: readonly string[], sources: ReadonlyMap<string, SourceSnapshot>): BuiltIndexes;
15
+ //# sourceMappingURL=index-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-builder.d.ts","sourceRoot":"","sources":["../src/index-builder.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAML,KAAK,KAAK,EAEX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGrD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC5C,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC,CAAC;CAC5D,CAAC,CAAC;AAuJH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,GAC3C,YAAY,CAmDd"}