@diplodoc/transform 4.70.0 → 4.70.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.
@@ -122,6 +122,120 @@ function termInlineRule(state: StateInline, silent: boolean): boolean {
122
122
  return true;
123
123
  }
124
124
 
125
+ const RAW_CODE_TOKEN_TYPES = new Set(['fence', 'code_block', 'yfm_page-constructor']);
126
+
127
+ function collectRawTermMatches(content: string, reg: RegExp, out: Set<string>) {
128
+ reg.lastIndex = 0;
129
+ let match: RegExpExecArray | null;
130
+
131
+ while ((match = reg.exec(content))) {
132
+ out.add(':' + match[3]);
133
+ }
134
+ }
135
+
136
+ function collectTermsFromRawContent(tokens: Token[], reg: RegExp, out: Set<string>) {
137
+ for (const token of tokens) {
138
+ if (RAW_CODE_TOKEN_TYPES.has(token.type) && token.content) {
139
+ collectRawTermMatches(token.content, reg, out);
140
+ }
141
+
142
+ if (token.type === 'inline' && token.children) {
143
+ for (const child of token.children) {
144
+ if (child.type === 'code_inline' && child.content) {
145
+ collectRawTermMatches(child.content, reg, out);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ function collectTermKeysFromInlineChildren(children: Token[], out: Set<string>) {
153
+ for (const child of children) {
154
+ if (child.type === 'term_open') {
155
+ const key = child.attrGet('term-key');
156
+
157
+ if (key) {
158
+ out.add(key);
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ function collectTermsFromContent(tokens: Token[], out: Set<string>) {
165
+ let inDfn = false;
166
+
167
+ for (const token of tokens) {
168
+ if (token.type === 'dfn_open') {
169
+ inDfn = true;
170
+ continue;
171
+ }
172
+
173
+ if (token.type === 'dfn_close') {
174
+ inDfn = false;
175
+ continue;
176
+ }
177
+
178
+ if (!inDfn && token.type === 'inline' && token.children) {
179
+ collectTermKeysFromInlineChildren(token.children, out);
180
+ }
181
+ }
182
+ }
183
+
184
+ function collectTermsFromDfns(tokens: Token[], out: Set<string>, referencedDfns: Set<string>) {
185
+ let inReferencedDfn = false;
186
+
187
+ for (const token of tokens) {
188
+ if (token.type === 'dfn_open') {
189
+ const key = (token.attrGet('id') || '').replace(/_element$/, '');
190
+
191
+ inReferencedDfn = Boolean(key) && referencedDfns.has(key);
192
+ continue;
193
+ }
194
+
195
+ if (token.type === 'dfn_close') {
196
+ inReferencedDfn = false;
197
+ continue;
198
+ }
199
+
200
+ if (inReferencedDfn && token.type === 'inline' && token.children) {
201
+ collectTermKeysFromInlineChildren(token.children, out);
202
+ }
203
+ }
204
+ }
205
+
206
+ function findDfnCloseIdx(tokens: Token[], from: number) {
207
+ let endIdx = from;
208
+
209
+ while (endIdx < tokens.length && tokens[endIdx].type !== 'dfn_close') {
210
+ endIdx++;
211
+ }
212
+
213
+ return endIdx;
214
+ }
215
+
216
+ function removeUnreferencedDefinitions(tokens: Token[], referencedTerms: Set<string>) {
217
+ let idx = 0;
218
+
219
+ while (idx < tokens.length) {
220
+ const tok = tokens[idx];
221
+
222
+ if (tok.type === 'dfn_open') {
223
+ const termKey = (tok.attrGet('id') || '').replace(/_element$/, '');
224
+
225
+ if (termKey && !referencedTerms.has(termKey)) {
226
+ const endIdx = findDfnCloseIdx(tokens, idx + 1);
227
+
228
+ if (endIdx < tokens.length) {
229
+ tokens.splice(idx, endIdx - idx + 1);
230
+ continue;
231
+ }
232
+ }
233
+ }
234
+
235
+ idx++;
236
+ }
237
+ }
238
+
125
239
  const term: MarkdownItPluginCb = (md, options) => {
126
240
  const escapeRE = md.utils.escapeRE;
127
241
  const arrayReplaceAt = md.utils.arrayReplaceAt;
@@ -150,13 +264,27 @@ const term: MarkdownItPluginCb = (md, options) => {
150
264
  return;
151
265
  }
152
266
 
153
- const regTerms = Object.keys(state.env.terms)
154
- .map((el) => el.substr(1))
267
+ const termKeys = Object.keys(state.env.terms);
268
+
269
+ if (!termKeys.length) {
270
+ return;
271
+ }
272
+
273
+ const referencedTerms = new Set<string>();
274
+
275
+ const regTerms = termKeys
276
+ .map((el) => el.slice(1))
155
277
  .map(escapeRE)
156
278
  .join('|');
157
279
  const regText = '\\[([^\\[]+)\\](\\(\\*(' + regTerms + ')\\))';
158
280
  const reg = new RegExp(regText, 'g');
159
281
 
282
+ collectTermsFromRawContent(blockTokens, reg, referencedTerms);
283
+ collectTermsFromContent(blockTokens, referencedTerms);
284
+
285
+ // Collect terms transitively referenced inside definitions of referenced terms.
286
+ collectTermsFromDfns(blockTokens, referencedTerms, referencedTerms);
287
+
160
288
  for (j = 0, l = blockTokens.length; j < l; j++) {
161
289
  if (blockTokens[j].type === 'heading_open') {
162
290
  while (blockTokens[j].type !== 'heading_close') {
@@ -210,6 +338,8 @@ const term: MarkdownItPluginCb = (md, options) => {
210
338
  const termTitle = termMatch[1];
211
339
  const termKey = termMatch[3];
212
340
 
341
+ referencedTerms.add(':' + termKey);
342
+
213
343
  if (termMatch.index > 0 || termMatch[1].length > 0) {
214
344
  token = new state.Token('text', '', 0);
215
345
  token.content = text.slice(pos, termMatch.index);
@@ -244,6 +374,12 @@ const term: MarkdownItPluginCb = (md, options) => {
244
374
  blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes);
245
375
  }
246
376
  }
377
+
378
+ // Remove definitions without any reference on the page
379
+ // Skip during linting to allow lint rules to check term definitions
380
+ if (!isLintRun) {
381
+ removeUnreferencedDefinitions(state.tokens, referencedTerms);
382
+ }
247
383
  }
248
384
 
249
385
  md.block.ruler.before('reference', 'termDefinitions', termDefinitions(md, options), {