@hanology/cham-browser 0.2.3 → 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.
@@ -1,14 +1,2 @@
1
- import type { BookConfig, BookMeta, BookData, LibraryIndex, LibraryScale, CrossRef, OutputPiece } from '@hanology/cham/types';
2
- export interface AuthorRecord {
3
- name: string;
4
- dynasty: string;
5
- bio?: string;
6
- }
7
- export declare function buildPieceFromCham(chamSource: string, bookConfig: BookConfig, authors: Record<string, AuthorRecord>, bookId: string, proseFiles: Map<string, string>, layerFiles: Map<string, string>): OutputPiece | null;
8
- export declare function buildBookMeta(config: BookConfig, pieceCount: number): BookMeta;
9
- export declare function buildBookData(config: BookConfig, pieces: OutputPiece[]): BookData;
10
- export declare function detectScale(bookCount: number, singleBookPieceCount?: number): LibraryScale;
11
- export declare function buildCrossRefs(allPieces: OutputPiece[]): CrossRef[];
12
- export declare function buildLibraryIndex(bookMetas: BookMeta[], allPieces: OutputPiece[]): LibraryIndex;
13
- export declare function buildAuthorsJson(authors: Record<string, AuthorRecord>, allPieces: OutputPiece[]): object[];
14
- export declare function buildDynastiesJson(allPieces: OutputPiece[]): Record<string, object>;
1
+ export { buildPieceFromCham, buildBookMeta, buildBookData, buildLibraryIndex, buildCrossRefs, detectScale, buildAuthorsJson, buildDynastiesJson, } from '@hanology/cham/pipeline';
2
+ export type { AuthorRecord } from '@hanology/cham/types';
package/dist/pipeline.js CHANGED
@@ -1,377 +1,2 @@
1
- import { parse } from '@hanology/cham/parser';
2
- // ─── Markdown Helpers ─────────────────────────────────────────
3
- function cleanHardWraps(text) {
4
- return text
5
- .split('\n\n')
6
- .map(seg => seg.replace(/\n/g, ''))
7
- .join('\n\n');
8
- }
9
- function splitMdFrontmatter(content) {
10
- const trimmed = content.replace(/^/, '');
11
- if (!trimmed.startsWith('---'))
12
- return { frontmatter: null, body: trimmed };
13
- const end = trimmed.indexOf('\n---', 3);
14
- if (end === -1)
15
- return { frontmatter: null, body: trimmed };
16
- try {
17
- // Inline minimal YAML-like parsing for frontmatter
18
- const fmText = trimmed.slice(3, end);
19
- const fm = {};
20
- for (const line of fmText.split('\n')) {
21
- const m = line.match(/^(\w[\w-]*)\s*:\s*(.+)$/);
22
- if (m)
23
- fm[m[1]] = m[2].trim().replace(/^['"]|['"]$/g, '');
24
- }
25
- const body = trimmed.slice(end + 4);
26
- return { frontmatter: fm, body: body.startsWith('\n') ? body.slice(1) : body };
27
- }
28
- catch {
29
- return { frontmatter: null, body: trimmed.slice(end + 4) };
30
- }
31
- }
32
- // ─── Kind Mapping ─────────────────────────────────────────────
33
- function mapKind(kind) {
34
- if (kind === 'pron')
35
- return 'pronunciation';
36
- if (kind === 'meaning')
37
- return 'semantic';
38
- return kind;
39
- }
40
- // ─── Annotation Helpers ───────────────────────────────────────
41
- function entryToRange(entry, doc) {
42
- switch (entry.target.type) {
43
- case 'title':
44
- return { type: 'range', scope: 'title', start: 0, end: 1 };
45
- case 'full':
46
- return { type: 'range', scope: 'title', start: 0, end: 0 };
47
- case 'marker': {
48
- const marker = doc.markers.get(entry.target.markerId);
49
- if (!marker)
50
- return { type: 'range', scope: 'title', start: 0, end: 1 };
51
- return {
52
- type: 'range',
53
- scope: 'verse',
54
- verseIndex: marker.blockIndex,
55
- start: marker.offset,
56
- end: marker.offset + marker.length,
57
- };
58
- }
59
- case 'verse':
60
- return {
61
- type: 'range',
62
- scope: 'verse',
63
- verseIndex: entry.target.line,
64
- start: entry.target.char,
65
- end: entry.target.char,
66
- };
67
- }
68
- }
69
- function buildAnnotations(doc, pieceId) {
70
- const annotations = [];
71
- let annId = 1;
72
- for (const section of doc.sections) {
73
- for (const entry of section.entries) {
74
- const range = entryToRange(entry, doc);
75
- if (!range)
76
- continue;
77
- annotations.push({
78
- id: `${pieceId}-${annId++}`,
79
- range,
80
- kind: mapKind(entry.kind),
81
- lang: entry.params.lang,
82
- text: entry.value.trim(),
83
- source: 'cham',
84
- });
85
- }
86
- }
87
- return annotations;
88
- }
89
- function buildAnnotationsFromLayer(layerDoc, primaryDoc, layerId) {
90
- const annotations = [];
91
- let annId = 1;
92
- for (const section of layerDoc.sections) {
93
- for (const entry of section.entries) {
94
- const range = entryToRange(entry, primaryDoc);
95
- if (!range)
96
- continue;
97
- annotations.push({
98
- id: `${layerId}-${annId++}`,
99
- range,
100
- kind: mapKind(entry.kind),
101
- lang: entry.params.lang,
102
- text: entry.value,
103
- source: 'cham',
104
- });
105
- }
106
- }
107
- return annotations;
108
- }
109
- function getHeadword(doc, ann) {
110
- if (ann.range.scope === 'title') {
111
- return doc.meta.title.slice(ann.range.start, ann.range.end);
112
- }
113
- if (ann.range.scope === 'verse' && ann.range.verseIndex !== undefined) {
114
- const block = doc.textBlocks[ann.range.verseIndex];
115
- if (block)
116
- return block.text.slice(ann.range.start, ann.range.end);
117
- }
118
- return '';
119
- }
120
- function buildAnnotationsText(doc, annotations) {
121
- if (!annotations.length)
122
- return '';
123
- const groups = new Map();
124
- for (const ann of annotations) {
125
- const key = `${ann.range.scope}:${ann.range.verseIndex ?? ''}:${ann.range.start}:${ann.range.end}`;
126
- if (!groups.has(key)) {
127
- groups.set(key, { headword: getHeadword(doc, ann), pron: [], meaning: [] });
128
- }
129
- const g = groups.get(key);
130
- if (ann.kind === 'pronunciation')
131
- g.pron.push(ann);
132
- else
133
- g.meaning.push(ann);
134
- }
135
- const lines = [];
136
- let num = 1;
137
- for (const [, g] of groups) {
138
- const parts = [];
139
- if (g.pron.length) {
140
- const pronParts = g.pron.map(a => {
141
- const lang = a.lang === 'yue' ? '粵' : '普';
142
- return `○${lang}${a.text}`;
143
- });
144
- parts.push(pronParts.join(';'));
145
- }
146
- for (const m of g.meaning) {
147
- parts.push(m.text);
148
- }
149
- lines.push(`${num}.${g.headword}:${parts.join('。')}`);
150
- num++;
151
- }
152
- return lines.join('\n');
153
- }
154
- // ─── Prose Sections ───────────────────────────────────────────
155
- function parseProseSections(files) {
156
- const sections = {};
157
- const structured = [];
158
- const BUILTIN = {
159
- 'author-brief.md': { key: 'author_bio', title: '作者簡介', order: 1 },
160
- 'background.md': { key: 'background', title: '背景資料', order: 2 },
161
- 'analysis.md': { key: 'analysis', title: '賞析', order: 3 },
162
- 'follow-up.md': { key: 'follow_up', title: '延伸活動', order: 4 },
163
- 'think-questions.md': { key: 'think_questions', title: '思考問題', order: 5 },
164
- 'preparation.md': { key: 'preparation', title: '教學準備', order: 6 },
165
- };
166
- for (const [filename, content] of files) {
167
- if (!filename.endsWith('.md') || filename.endsWith('.cham.md'))
168
- continue;
169
- if (filename.startsWith('_'))
170
- continue;
171
- const { frontmatter, body } = splitMdFrontmatter(content);
172
- const builtin = BUILTIN[filename];
173
- let key, title, order;
174
- if (builtin) {
175
- key = builtin.key;
176
- title = frontmatter?.title || builtin.title;
177
- order = frontmatter?.order ?? builtin.order;
178
- }
179
- else if (filename.startsWith('custom-')) {
180
- const stem = filename.slice(7, -3);
181
- key = `custom_${stem}`;
182
- title = frontmatter?.title || stem;
183
- order = frontmatter?.order ?? 99;
184
- }
185
- else {
186
- continue;
187
- }
188
- const cleanedBody = cleanHardWraps(body.trim());
189
- sections[key] = cleanedBody;
190
- structured.push({ key, title, filename, body: cleanedBody, order });
191
- }
192
- structured.sort((a, b) => a.order - b.order);
193
- return { sections, structuredSections: structured };
194
- }
195
- // ─── Commentary Layers ────────────────────────────────────────
196
- function parseCommentaryLayers(files, primaryDoc) {
197
- const layers = {};
198
- for (const [filename, content] of files) {
199
- if (!filename.endsWith('.cham.md') || filename === 'text.cham.md')
200
- continue;
201
- const layerDoc = parse(content);
202
- if (layerDoc.meta.type !== 'secondary')
203
- continue;
204
- const layerId = filename.replace('.cham.md', '');
205
- layers[layerId] = buildAnnotationsFromLayer(layerDoc, primaryDoc, layerId);
206
- }
207
- return layers;
208
- }
209
- // ─── Annotation Layers ────────────────────────────────────────
210
- function buildAnnotationLayers(layerAnnotations, bookConfig) {
211
- const bookLayers = bookConfig.layers || [];
212
- if (bookLayers.length === 0 && Object.keys(layerAnnotations).length === 0)
213
- return [];
214
- const result = [];
215
- result.push({
216
- id: 'default',
217
- label: bookConfig.annotation?.defaultLabel || '原文',
218
- shortLabel: bookConfig.annotation?.defaultShortLabel || '文',
219
- contributor: bookConfig.contributors?.[0]?.ref || '',
220
- role: 'author',
221
- nature: 'annotation',
222
- displayOrder: 0,
223
- enabled: true,
224
- annotations: [],
225
- });
226
- for (const bookLayer of bookLayers) {
227
- const annotations = layerAnnotations[bookLayer.id] || [];
228
- result.push({
229
- id: bookLayer.id,
230
- label: bookLayer.label,
231
- shortLabel: bookLayer.shortLabel || bookLayer.label.charAt(0),
232
- contributor: bookLayer.contributor,
233
- role: bookLayer.role || 'commentator',
234
- nature: bookLayer.nature || 'commentary',
235
- displayOrder: bookLayer.displayOrder ?? result.length,
236
- enabled: bookLayer.enabled !== false,
237
- annotations,
238
- });
239
- }
240
- return result;
241
- }
242
- // ─── Public API ───────────────────────────────────────────────
243
- export function buildPieceFromCham(chamSource, bookConfig, authors, bookId, proseFiles, layerFiles) {
244
- const doc = parse(chamSource);
245
- if (doc.meta.type !== 'primary')
246
- return null;
247
- const pmeta = doc.meta;
248
- const verses = doc.textBlocks.map(b => ({ text: b.text }));
249
- const annotations = buildAnnotations(doc, pmeta.id);
250
- const { sections, structuredSections } = parseProseSections(proseFiles);
251
- const annText = buildAnnotationsText(doc, annotations);
252
- if (annText)
253
- sections['annotations'] = annText;
254
- const rawContributors = pmeta.contributors?.length
255
- ? pmeta.contributors
256
- : bookConfig.contributors || [];
257
- const contributors = rawContributors.map(c => ({
258
- id: c.ref,
259
- name: authors[c.ref]?.name || c.ref,
260
- role: c.role,
261
- ...(c.title ? { title: c.title } : {}),
262
- }));
263
- const authorId = contributors[0]?.id || '';
264
- const authorName = contributors[0]?.name || '';
265
- const dynastyName = authors[authorId]?.dynasty
266
- || pmeta.date?.dynasty
267
- || bookConfig.date?.dynasty
268
- || '';
269
- const layers = parseCommentaryLayers(layerFiles, doc);
270
- const annotationLayers = buildAnnotationLayers(layers, bookConfig);
271
- return {
272
- bookId,
273
- num: pmeta.id,
274
- title: pmeta.title,
275
- author: authorName,
276
- authorId,
277
- ...(contributors.length > 1 ? { contributors } : {}),
278
- dynasty: dynastyName,
279
- genre: pmeta.genre || bookConfig.genre || 'poetry',
280
- verses,
281
- sections,
282
- annotations,
283
- ...(Object.keys(layers).length > 0 ? { layers } : {}),
284
- ...(annotationLayers.length > 0 ? { annotationLayers } : {}),
285
- ...(pmeta.source ? { source: pmeta.source } : {}),
286
- ...(structuredSections.length > 0 ? { structuredSections } : {}),
287
- };
288
- }
289
- export function buildBookMeta(config, pieceCount) {
290
- return {
291
- id: config.id,
292
- title: config.title,
293
- subtitle: config.subtitle,
294
- titleEn: config.titleEn,
295
- publisher: config.publisher,
296
- genre: config.genre || 'poetry',
297
- count: pieceCount,
298
- hero: config.hero,
299
- layers: config.layers,
300
- annotation: config.annotation,
301
- };
302
- }
303
- export function buildBookData(config, pieces) {
304
- return { meta: buildBookMeta(config, pieces.length), pieces };
305
- }
306
- export function detectScale(bookCount, singleBookPieceCount) {
307
- if (bookCount === 0)
308
- return 'single-piece';
309
- if (bookCount === 1)
310
- return (singleBookPieceCount ?? 0) <= 1 ? 'single-piece' : 'single-book';
311
- return 'library';
312
- }
313
- export function buildCrossRefs(allPieces) {
314
- const refs = [];
315
- for (const piece of allPieces) {
316
- const src = piece.source;
317
- if (!src || src.relation === 'standalone')
318
- continue;
319
- if (!src.textRef)
320
- continue;
321
- refs.push({
322
- focusedBookId: piece.bookId,
323
- focusedNum: piece.num,
324
- fullBookId: src.textRef,
325
- fullNum: src.pieceRef,
326
- relation: src.relation,
327
- });
328
- }
329
- return refs;
330
- }
331
- export function buildLibraryIndex(bookMetas, allPieces) {
332
- const crossRefs = buildCrossRefs(allPieces);
333
- return {
334
- scale: detectScale(bookMetas.length),
335
- books: bookMetas,
336
- crossRefs,
337
- };
338
- }
339
- export function buildAuthorsJson(authors, allPieces) {
340
- const pieceCounts = new Map();
341
- for (const p of allPieces) {
342
- pieceCounts.set(p.authorId, (pieceCounts.get(p.authorId) || 0) + 1);
343
- }
344
- return Object.entries(authors).map(([id, data]) => ({
345
- '@id': `author:${encodeURIComponent(data.name)}`,
346
- '@type': 'Person',
347
- name: data.name,
348
- dynasty: data.dynasty || '',
349
- bio: data.bio || '',
350
- poemCount: pieceCounts.get(id) || 0,
351
- }));
352
- }
353
- export function buildDynastiesJson(allPieces) {
354
- const map = new Map();
355
- for (const piece of allPieces) {
356
- const d = piece.dynasty;
357
- if (!d)
358
- continue;
359
- if (!map.has(d))
360
- map.set(d, { authors: new Set(), count: 0 });
361
- const entry = map.get(d);
362
- entry.authors.add(piece.author);
363
- entry.count++;
364
- }
365
- const result = {};
366
- for (const [name, data] of map) {
367
- result[name] = {
368
- '@id': `dynasty:${encodeURIComponent(name)}`,
369
- '@type': 'HistoricalPeriod',
370
- name,
371
- authors: [...data.authors],
372
- poemCount: data.count,
373
- };
374
- }
375
- return result;
376
- }
1
+ export { buildPieceFromCham, buildBookMeta, buildBookData, buildLibraryIndex, buildCrossRefs, detectScale, buildAuthorsJson, buildDynastiesJson, } from '@hanology/cham/pipeline';
377
2
  //# sourceMappingURL=pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EACnE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,kBAAkB,GAClE,MAAM,yBAAyB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanology/cham-browser",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "CHAM — browser-compatible parser, serializer, and site generator for Classical Han Annotated Markdown",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,9 @@
32
32
  "yaml": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "typescript": "^6.0.0"
35
+ "happy-dom": "^20.9.0",
36
+ "typescript": "^6.0.0",
37
+ "vitest": "^3.2.4"
36
38
  },
37
39
  "license": "MIT",
38
40
  "repository": {
@@ -0,0 +1,140 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import {
3
+ buildVerseAnnotations,
4
+ renderAnnotatedText,
5
+ renderVerseGutter,
6
+ esc,
7
+ } from '../src/composables/useAnnotationRenderer'
8
+ import type { Annotation } from '../src/types'
9
+
10
+ function makeAnn(overrides: Partial<Annotation> & { id: string; kind: Annotation['kind'] }): Annotation {
11
+ return {
12
+ text: 'test annotation',
13
+ range: { scope: 'verse', verseIndex: 0, start: 0, end: 1 },
14
+ ...overrides,
15
+ }
16
+ }
17
+
18
+ describe('esc', () => {
19
+ it('escapes HTML special characters', () => {
20
+ expect(esc('<b>test</b>')).toBe('&lt;b&gt;test&lt;/b&gt;')
21
+ expect(esc('a & b')).toBe('a &amp; b')
22
+ })
23
+ })
24
+
25
+ describe('buildVerseAnnotations', () => {
26
+ const anns: Annotation[] = [
27
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
28
+ makeAnn({ id: 'p1', kind: 'pronunciation', range: { scope: 'verse', verseIndex: 0, start: 3, end: 5 } }),
29
+ makeAnn({ id: 'a2', kind: 'semantic', range: { scope: 'verse', verseIndex: 1, start: 0, end: 1 } }),
30
+ ]
31
+
32
+ it('returns spans only for the requested verse', () => {
33
+ const spans = buildVerseAnnotations(anns, 0)
34
+ expect(spans).toHaveLength(2)
35
+ expect(spans[0].start).toBe(0)
36
+ expect(spans[0].end).toBe(2)
37
+ expect(spans[1].start).toBe(3)
38
+ expect(spans[1].end).toBe(5)
39
+ })
40
+
41
+ it('groups annotations at the same position', () => {
42
+ const overlapping: Annotation[] = [
43
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
44
+ makeAnn({ id: 'p1', kind: 'pronunciation', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
45
+ ]
46
+ const spans = buildVerseAnnotations(overlapping, 0)
47
+ expect(spans).toHaveLength(1)
48
+ expect(spans[0].annotations).toHaveLength(2)
49
+ })
50
+
51
+ it('returns empty array for verse with no annotations', () => {
52
+ expect(buildVerseAnnotations(anns, 5)).toHaveLength(0)
53
+ })
54
+
55
+ it('sorts spans by start position', () => {
56
+ const unsorted: Annotation[] = [
57
+ makeAnn({ id: 'b', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 5, end: 7 } }),
58
+ makeAnn({ id: 'a', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
59
+ ]
60
+ const spans = buildVerseAnnotations(unsorted, 0)
61
+ expect(spans[0].start).toBe(0)
62
+ expect(spans[1].start).toBe(5)
63
+ })
64
+ })
65
+
66
+ describe('renderAnnotatedText', () => {
67
+ const anns: Annotation[] = [
68
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
69
+ ]
70
+ const spans = buildVerseAnnotations(anns, 0)
71
+
72
+ it('escapes plain text without annotations', () => {
73
+ expect(renderAnnotatedText('hello', [])).toBe('hello')
74
+ expect(renderAnnotatedText('<script>', [])).toBe('&lt;script&gt;')
75
+ })
76
+
77
+ it('wraps annotated text in span with ann-target class', () => {
78
+ const html = renderAnnotatedText('明月光', spans, false, 0)
79
+ expect(html).toContain('class="ann-target semantic"')
80
+ expect(html).toContain('data-ann-ids="a1"')
81
+ expect(html).toContain('明月')
82
+ })
83
+
84
+ it('wraps annotated text in ruby when useRuby is true', () => {
85
+ const html = renderAnnotatedText('明月光', spans, true, 0)
86
+ expect(html).toContain('<ruby')
87
+ expect(html).toContain('ann-num')
88
+ expect(html).toContain('</ruby>')
89
+ })
90
+
91
+ it('outputs Chinese number for annotation markers', () => {
92
+ const html = renderAnnotatedText('明月光', spans, false, 0)
93
+ expect(html).toContain('一')
94
+ })
95
+
96
+ it('handles multiple annotations', () => {
97
+ const multi: Annotation[] = [
98
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
99
+ makeAnn({ id: 'a2', kind: 'pronunciation', range: { scope: 'verse', verseIndex: 0, start: 3, end: 4 } }),
100
+ ]
101
+ const multiSpans = buildVerseAnnotations(multi, 0)
102
+ const html = renderAnnotatedText('明月光的', multiSpans, false, 0)
103
+ expect(html).toContain('一')
104
+ expect(html).toContain('二')
105
+ })
106
+
107
+ it('preserves text after last annotation', () => {
108
+ const html = renderAnnotatedText('明月光', spans, false, 0)
109
+ expect(html).toContain('光')
110
+ })
111
+
112
+ it('preserves text before first annotation', () => {
113
+ const offset: Annotation[] = [
114
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 2, end: 4 } }),
115
+ ]
116
+ const offsetSpans = buildVerseAnnotations(offset, 0)
117
+ const html = renderAnnotatedText('床前明月', offsetSpans, false, 0)
118
+ expect(html).toContain('床前')
119
+ expect(html).toContain('明月')
120
+ })
121
+ })
122
+
123
+ describe('renderVerseGutter', () => {
124
+ it('returns plain text when no annotations', () => {
125
+ const result = renderVerseGutter('床前明月光', [])
126
+ expect(result.textHtml).toBe('床前明月光')
127
+ expect(result.gutterHtml).toBe('')
128
+ })
129
+
130
+ it('generates gutter numbers for annotated ranges', () => {
131
+ const anns: Annotation[] = [
132
+ makeAnn({ id: 'a1', kind: 'semantic', range: { scope: 'verse', verseIndex: 0, start: 0, end: 2 } }),
133
+ ]
134
+ const spans = buildVerseAnnotations(anns, 0)
135
+ const result = renderVerseGutter('明月光', spans, 0)
136
+ expect(result.textHtml).toContain('ann-target')
137
+ expect(result.gutterHtml).toContain('ann-gutter-num')
138
+ expect(result.gutterHtml).toContain('一')
139
+ })
140
+ })