glossarist 0.3.8 → 0.4.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.
- package/package.json +1 -1
- package/src/concept-collection.js +3 -11
- package/src/concept-parser.js +7 -1
- package/src/dataset-asset.js +3 -0
- package/src/entity-directory.js +43 -0
- package/src/gcr-reader.js +53 -5
- package/src/gcr-writer.js +3 -1
- package/src/index.d.ts +40 -7
- package/src/index.js +19 -2
- package/src/managed-concept-collection.js +11 -2
- package/src/models/bibliography-data.js +43 -0
- package/src/models/bibliography-entry.js +26 -0
- package/src/models/concept.js +44 -0
- package/src/models/designation.js +2 -14
- package/src/models/detailed-definition.js +30 -6
- package/src/models/figure.js +71 -0
- package/src/models/formula.js +21 -0
- package/src/models/index.d.ts +5 -0
- package/src/models/index.js +12 -1
- package/src/models/localized-concept.js +21 -0
- package/src/models/localized-string.js +19 -0
- package/src/models/non-verb-rep.js +15 -13
- package/src/models/non-verbal-entity.js +39 -0
- package/src/models/non-verbal-reference.js +35 -0
- package/src/models/non-verbal-references.js +16 -0
- package/src/models/registrable.js +25 -0
- package/src/models/shared-non-verbal-entity.js +28 -0
- package/src/models/table.js +21 -0
- package/src/reference-mention.js +19 -3
- package/src/reference-resolver.js +77 -31
- package/src/render-classification.js +10 -1
- package/src/validators/asset-index.js +66 -0
- package/src/validators/index.js +5 -1
- package/src/validators/v3-rules.js +132 -24
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ValidationRule } from './validation-rule.js';
|
|
2
|
+
import { parseMention } from '../reference-mention.js';
|
|
3
|
+
import { GraphicalSymbol } from '../models/designation.js';
|
|
2
4
|
|
|
3
5
|
const _eachLocalization = (concept, fn) => {
|
|
4
6
|
for (const lang of concept.languages) {
|
|
@@ -160,36 +162,14 @@ const CITE_MENTION_RE = /\{\{\s*cite:([^,}\s]+)[^}]*?\}\}/g;
|
|
|
160
162
|
|
|
161
163
|
function _findCiteMentions(concept) {
|
|
162
164
|
const mentions = [];
|
|
163
|
-
const
|
|
164
|
-
if (typeof text !== 'string' || text.length === 0)
|
|
165
|
+
for (const { text, source } of concept.walkTexts()) {
|
|
166
|
+
if (typeof text !== 'string' || text.length === 0) continue;
|
|
165
167
|
CITE_MENTION_RE.lastIndex = 0;
|
|
166
168
|
let m;
|
|
167
169
|
while ((m = CITE_MENTION_RE.exec(text)) !== null) {
|
|
168
170
|
mentions.push({ key: m[1].trim(), source });
|
|
169
171
|
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
for (const lang of concept.languages) {
|
|
173
|
-
const lc = concept.localization(lang);
|
|
174
|
-
if (!lc) continue;
|
|
175
|
-
|
|
176
|
-
for (let i = 0; lc.definitions[i]; i++) {
|
|
177
|
-
walkText(lc.definitions[i]?.content, `localizations.${lang}.definitions[${i}].content`);
|
|
178
|
-
}
|
|
179
|
-
for (let i = 0; lc.notes[i]; i++) {
|
|
180
|
-
const content = typeof lc.notes[i] === 'object'
|
|
181
|
-
? (lc.notes[i]?.content ?? '')
|
|
182
|
-
: String(lc.notes[i] ?? '');
|
|
183
|
-
walkText(content, `localizations.${lang}.notes[${i}].content`);
|
|
184
|
-
}
|
|
185
|
-
for (let i = 0; lc.examples[i]; i++) {
|
|
186
|
-
walkText(lc.examples[i]?.content, `localizations.${lang}.examples[${i}].content`);
|
|
187
|
-
}
|
|
188
|
-
for (let i = 0; lc.annotations[i]; i++) {
|
|
189
|
-
walkText(lc.annotations[i]?.content, `localizations.${lang}.annotations[${i}].content`);
|
|
190
|
-
}
|
|
191
172
|
}
|
|
192
|
-
|
|
193
173
|
return mentions;
|
|
194
174
|
}
|
|
195
175
|
|
|
@@ -267,3 +247,131 @@ export class CiteRefIntegrityRule extends ValidationRule {
|
|
|
267
247
|
}
|
|
268
248
|
}
|
|
269
249
|
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
// ── NonVerbalRefIntegrityRule ────────────────────────────────────────
|
|
253
|
+
// Uses parseMention for classification (no regex duplication).
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
const NVR_ARRAYS = Object.freeze([
|
|
257
|
+
{ name: 'figures', entityType: 'figure' },
|
|
258
|
+
{ name: 'tables', entityType: 'table' },
|
|
259
|
+
{ name: 'formulas', entityType: 'formula' },
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
function _findNvrMentions(concept) {
|
|
263
|
+
const mentions = [];
|
|
264
|
+
const walkText = (text, source) => {
|
|
265
|
+
if (typeof text !== 'string' || text.length === 0) return;
|
|
266
|
+
const re = /\{\{([^{}]*?)\}\}/g;
|
|
267
|
+
let m;
|
|
268
|
+
while ((m = re.exec(text)) !== null) {
|
|
269
|
+
const parsed = parseMention(m[1]);
|
|
270
|
+
if (parsed.kind === 'fig-ref' ||
|
|
271
|
+
parsed.kind === 'table-ref' ||
|
|
272
|
+
parsed.kind === 'formula-ref') {
|
|
273
|
+
mentions.push({ key: parsed.key, source });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
for (const { text, source } of concept.walkTexts()) {
|
|
279
|
+
walkText(text, source);
|
|
280
|
+
}
|
|
281
|
+
return mentions;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export class NonVerbalRefIntegrityRule extends ValidationRule {
|
|
285
|
+
constructor() {
|
|
286
|
+
super('nvr-integrity', 'warning');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
validate(concept, path, result) {
|
|
290
|
+
for (const { name } of NVR_ARRAYS) {
|
|
291
|
+
const counts = new Map();
|
|
292
|
+
for (const ref of concept[name]) {
|
|
293
|
+
if (ref.entityId == null) continue;
|
|
294
|
+
counts.set(ref.entityId, (counts.get(ref.entityId) ?? 0) + 1);
|
|
295
|
+
}
|
|
296
|
+
for (const [id, count] of counts) {
|
|
297
|
+
if (count > 1) {
|
|
298
|
+
this.addIssue(result, `${path}${name}`,
|
|
299
|
+
`duplicate ${name} reference id "${id}" appears ${count} times`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const mentions = _findNvrMentions(concept);
|
|
305
|
+
if (mentions.length === 0) return;
|
|
306
|
+
|
|
307
|
+
const knownIds = new Set();
|
|
308
|
+
for (const { name } of NVR_ARRAYS) {
|
|
309
|
+
for (const ref of concept[name]) {
|
|
310
|
+
if (ref.entityId != null) knownIds.add(ref.entityId);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
for (const { key, source } of mentions) {
|
|
315
|
+
if (!knownIds.has(key)) {
|
|
316
|
+
this.addIssue(result, source,
|
|
317
|
+
`inline NVR mention "${key}" does not resolve to any figures/tables/formulas entry`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ── OrphanedImagesRule ───────────────────────────────────────────────
|
|
324
|
+
// Collection-scope rule: needs AssetIndex + all concepts. Called
|
|
325
|
+
// directly by GcrValidator (not in concept validator chain).
|
|
326
|
+
|
|
327
|
+
export class OrphanedImagesRule {
|
|
328
|
+
constructor() {
|
|
329
|
+
this.name = 'orphaned-images';
|
|
330
|
+
this.severity = 'warning';
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
check(context) {
|
|
334
|
+
const { assetIndex, concepts, resolver } = context;
|
|
335
|
+
if (!assetIndex || assetIndex.size === 0) return [];
|
|
336
|
+
|
|
337
|
+
const referenced = new Set();
|
|
338
|
+
|
|
339
|
+
for (const concept of concepts) {
|
|
340
|
+
if (resolver) {
|
|
341
|
+
for (const ref of resolver.extractReferences(concept)) {
|
|
342
|
+
if (ref.target && ref.target.includes('images/')) {
|
|
343
|
+
referenced.add(ref.target.replace(/^\//, ''));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (const lang of concept.languages) {
|
|
349
|
+
const lc = concept.localization(lang);
|
|
350
|
+
if (!lc) continue;
|
|
351
|
+
|
|
352
|
+
for (const nvr of lc.nonVerbalRep) {
|
|
353
|
+
for (const img of nvr.images) {
|
|
354
|
+
if (img.src) referenced.add(img.src.replace(/^\//, ''));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
for (const term of lc.terms) {
|
|
358
|
+
if (term instanceof GraphicalSymbol && term.image) {
|
|
359
|
+
referenced.add(term.image.replace(/^\//, ''));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const issues = [];
|
|
366
|
+
for (const imgPath of assetIndex.paths) {
|
|
367
|
+
if (!referenced.has(imgPath)) {
|
|
368
|
+
issues.push({
|
|
369
|
+
path: imgPath,
|
|
370
|
+
severity: 'warning',
|
|
371
|
+
message: `orphaned image: ${imgPath} (not referenced by any concept)`,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return issues;
|
|
376
|
+
}
|
|
377
|
+
}
|