rapier-html 1.2.30 → 1.2.32

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.
@@ -0,0 +1,153 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import {splitOpeningFrontmatter} from './frontmatter.mjs';
3
+
4
+ export function markdownSourcePositions(source, parser) {
5
+ if (typeof source !== 'string' || !parser) throw new TypeError('Source positions require text and its Markdown parser');
6
+ const {body, bodyOffset} = splitOpeningFrontmatter(source), lines = [bodyOffset];
7
+ for (const row of body.matchAll(/\r\n?|\n/g)) lines.push(bodyOffset + row.index + row[0].length);
8
+ const lineAt = line => lines[line] ?? source.length;
9
+ const literal = [], comments = new Map(), observed = new WeakMap(), typography = [], openedStyles = new Map();
10
+ const add = (start, end) => { if (end > start) literal.push({start, end}); };
11
+ if (bodyOffset) add(0, bodyOffset);
12
+ const owner = Object.create(parser), inline = Object.create(parser.inline), ruler = Object.create(parser.inline.ruler);
13
+ owner.inline = inline; inline.ruler = ruler;
14
+ ruler.getRules = chain => parser.inline.ruler.getRules(chain).map(rule => (state, silent) => {
15
+ const start = state.pos, first = state.tokens.length;
16
+ const matched = rule(state, silent), token = state.tokens.at(-1);
17
+ if (matched && !silent && state.tokens.length > first && !observed.has(token) &&
18
+ ['code_inline', 'html_inline', 'image', 'footnote_ref'].includes(token?.type)) {
19
+ observed.set(token, {start, end: state.pos, source: state.src,
20
+ comment: token.type === 'html_inline' && token.content.startsWith('<!--'),
21
+ footnote: token.type === 'footnote_ref' && state.src.startsWith('^[', start)
22
+ ? state.env.footnotes?.list?.[token.meta.id] : null});
23
+ }
24
+ return matched;
25
+ });
26
+ const read = new WeakMap();
27
+ inline.parse = function (text, md, env, children) { read.set(children, text); return parser.inline.parse.call(this, text, md, env, children); };
28
+ const {tokens, cells} = markdownTableSources(body, owner, {}, lines);
29
+ const readInline = (children, content, rawOffset) => {
30
+ for (const child of children || []) {
31
+ const span = observed.get(child);
32
+ if (!span || span.source !== content) continue;
33
+ if (span.footnote?.tokens) {
34
+ readInline(span.footnote.tokens, span.footnote.content, pos => rawOffset(span.start + 2 + pos));
35
+ } else if (child.type !== 'footnote_ref') {
36
+ const start = rawOffset(span.start), end = rawOffset(span.end);
37
+ const style = child.meta?.typography;
38
+ if (style && child.nesting === 1) openedStyles.set(style, {start, innerStart: end, style: style.style});
39
+ else if (style && child.nesting === -1 && openedStyles.has(style)) {
40
+ typography.push({...openedStyles.get(style), innerEnd: start, end});
41
+ openedStyles.delete(style);
42
+ }
43
+ if (span.comment) comments.set(start, end);
44
+ else add(start, end);
45
+ }
46
+ }
47
+ };
48
+ for (const token of tokens) {
49
+ if (token.map && ['fence', 'code_block', 'html_block', 'reference_definition'].includes(token.type)) {
50
+ const start = lineAt(token.map[0]), end = lineAt(token.map[1]);
51
+ add(start, end);
52
+ if (token.type === 'html_block' && /^\s*<!--/.test(token.content)) {
53
+ let at = source.indexOf('<!--', start);
54
+ while (at >= start && at < end) {
55
+ const close = source.indexOf('-->', at + 4);
56
+ const stop = close >= 0 && close + 3 <= end ? close + 3 : end;
57
+ comments.set(at, stop);
58
+ at = source.indexOf('<!--', stop);
59
+ }
60
+ }
61
+ continue;
62
+ }
63
+ const content = read.get(token.children) ?? token.content;
64
+ if (token.type !== 'inline' || !content) continue;
65
+ let rawOffset;
66
+ if (token.map) {
67
+ const offsets = [], starts = [], spaces = [], rows = content.split('\n');
68
+ let local = 0;
69
+ for (let i = 0; i < rows.length; i++) {
70
+ const start = lineAt(token.map[0] + i), end = lineAt(token.map[0] + i + 1), line = source.slice(start, end).replace(/\0/g, '\ufffd');
71
+ let at = line.indexOf(rows[i]), lead = 0;
72
+ if (at < 0) { const rest = rows[i].replace(/^ +/, ''); lead = rows[i].length - rest.length; at = line.indexOf(rest); if (!lead || line[at - 1] !== '\t') at = -1; }
73
+ if (at < 0) throw new Error('Markdown inline source mapping failed');
74
+ starts.push(local); spaces.push(lead); offsets.push(start + at - lead); local += rows[i].length + 1;
75
+ }
76
+ rawOffset = pos => {
77
+ let row = starts.length - 1;
78
+ while (row > 0 && starts[row] > pos) row--;
79
+ return offsets[row] + Math.max(pos - starts[row], spaces[row] - 1);
80
+ };
81
+ } else if (cells.has(token)) {
82
+ rawOffset = cells.get(token);
83
+ } else continue;
84
+ readInline(token.children, content, rawOffset);
85
+ }
86
+ literal.sort((a, b) => a.start - b.start || b.end - a.end);
87
+ const merged = [];
88
+ for (const range of literal) {
89
+ const last = merged.at(-1);
90
+ if (last && range.start <= last.end) last.end = Math.max(last.end, range.end);
91
+ else merged.push({...range});
92
+ }
93
+ const isText = position => {
94
+ if (comments.has(position)) return false;
95
+ let low = 0, high = merged.length;
96
+ while (low < high) { const mid = (low + high) >> 1; if (merged[mid].end <= position) low = mid + 1; else high = mid; }
97
+ return low < merged.length && merged[low].start <= position;
98
+ };
99
+ return {isText, isComment: (start, end) => comments.get(start) === end, typography: typography.sort((a, b) => a.start - b.start)};
100
+ }
101
+
102
+ export function markdownTableSources(source, parser, environment, lineStarts = null, parse = null) {
103
+ const cells = new WeakMap();
104
+ const read = owner => {
105
+ if (parse) return parse(owner, source, environment).then(tokens => ({tokens, cells}));
106
+ return {tokens: owner.parse(source, environment), cells};
107
+ };
108
+ if (!source.includes('|')) return read(parser);
109
+ const lines = lineStarts || [0];
110
+ if (!lineStarts) for (const row of source.matchAll(/\r\n?|\n/g)) lines.push(row.index + row[0].length);
111
+ const owner = Object.create(parser), block = Object.create(parser.block), ruler = Object.create(parser.block.ruler);
112
+ owner.block = block; block.ruler = ruler;
113
+ ruler.getRules = chain => parser.block.ruler.getRules(chain).map(rule => (state, start, end, silent) => {
114
+ const first = state.tokens.length, matched = rule(state, start, end, silent);
115
+ if (matched && !silent && state.tokens[first]?.type === 'table_open') {
116
+ let cell;
117
+ for (let at = first; at < state.tokens.length; at++) {
118
+ const token = state.tokens[at];
119
+ if (token.type === 'tr_open') {
120
+ const row = token.map[0], from = state.bMarks[row] + state.tShift[row];
121
+ const line = state.src.lastIndexOf('\n', from - 1) + 1;
122
+ cell = tableSource(state.src.slice(from, state.eMarks[row]), lines[row + (state.rapierFirstLine || 0)] + from - line);
123
+ } else if (token.type === 'inline' && cell) cells.set(token, cell(token.content));
124
+ else if (token.type === 'tr_close') cell = null;
125
+ }
126
+ }
127
+ return matched;
128
+ });
129
+ return read(owner);
130
+ }
131
+
132
+ function tableSource(row, base) {
133
+ const trim = row.trimStart();
134
+ base += row.length - trim.length; row = trim.trimEnd();
135
+ const offsets = [], stops = [];
136
+ let text = '', cursor = 0;
137
+ for (let at = 0; at < row.length; at++) {
138
+ if (row[at] === '\\' && row[at + 1] === '|') at++;
139
+ else if (row[at] === '|') stops.push(text.length);
140
+ offsets.push(base + at); text += row[at];
141
+ }
142
+ offsets.push(base + row.length);
143
+ let next = stops[0] === 0 ? 1 : 0;
144
+ if (next) cursor = 1;
145
+ if (stops.at(-1) !== text.length - 1) stops.push(text.length);
146
+ return content => {
147
+ const stop = stops[next++] ?? text.length, raw = text.slice(cursor, stop);
148
+ const start = Math.min(text.length, cursor + raw.length - raw.trimStart().length);
149
+ if (raw.trim() !== content) throw new Error('Markdown table source mapping failed');
150
+ cursor = stop + 1;
151
+ return pos => offsets[start + pos];
152
+ };
153
+ }
@@ -1,6 +1,4 @@
1
1
  // SPDX-License-Identifier: MIT
2
- export function isTemplatePage(source) {
3
- const match = /^(?:)?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(String(source).slice(0, 65536));
4
- return !!match && /^template:[ \t]*page[ \t]*(?:#.*)?$/m.test(match[1]);
5
- }
2
+ import {readDocumentSettings} from './document-settings.mjs';
3
+ export const isTemplatePage = source => readDocumentSettings(source).template === 'page';
6
4
  export const isPageBorderLayout = layout => layout?.wrap === 'behind' && layout.lock === 'on';