spec-up-t 1.5.0 → 1.6.0-beta.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/assets/compiled/body.js +1 -0
- package/assets/compiled/head.css +3 -3
- package/assets/css/index.css +5 -4
- package/assets/css/refs.css +30 -0
- package/assets/css/terms-and-definitions.css +89 -1
- package/assets/js/insert-irefs.js +214 -0
- package/config/asset-map.json +2 -1
- package/package.json +1 -1
- package/src/create-docx.js +13 -6
- package/src/create-pdf.js +22 -18
- package/src/init.js +7 -3
- package/src/parsers/template-tag-parser.js +23 -1
- package/src/pipeline/references/collect-external-references.js +41 -14
- package/src/pipeline/references/external-references-service.js +60 -24
- package/src/pipeline/references/process-xtrefs-data.js +43 -19
- package/src/utils/logger.js +116 -9
- package/src/utils/regex-patterns.js +3 -1
- package/test/logger.test.js +290 -0
- package/assets/css/insert-trefs.css +0 -1
- package/src/utils/LOGGER.md +0 -81
package/src/init.js
CHANGED
|
@@ -8,14 +8,14 @@ async function initialize() {
|
|
|
8
8
|
try {
|
|
9
9
|
// Ensure the .cache directory exists
|
|
10
10
|
await fs.ensureDir(outputDir);
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
// Check if the init script has already run
|
|
13
13
|
if (await fs.pathExists(initFlagPath)) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// Place the init script here
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
// End of the init script
|
|
20
20
|
|
|
21
21
|
// Create the init flag file
|
|
@@ -23,7 +23,11 @@ async function initialize() {
|
|
|
23
23
|
|
|
24
24
|
Logger.success('Initialization complete.');
|
|
25
25
|
} catch (error) {
|
|
26
|
-
Logger.error(
|
|
26
|
+
Logger.error('Initialization failed', {
|
|
27
|
+
context: 'Failed to set up spec-up-t boilerplate files',
|
|
28
|
+
hint: 'Ensure you have write permissions in the current directory. Try running with appropriate permissions or in a different directory',
|
|
29
|
+
details: error.message
|
|
30
|
+
});
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
|
|
@@ -33,7 +33,7 @@ function extractCurrentFile(token, globalState) {
|
|
|
33
33
|
* @param {Object} config - Configuration object containing specs and settings
|
|
34
34
|
* @param {Object} globalState - Global state object containing definitions, references, etc.
|
|
35
35
|
* @param {Object} token - The markdown-it token being processed
|
|
36
|
-
* @param {string} type - The type of construct (def, ref, xref, tref)
|
|
36
|
+
* @param {string} type - The type of construct (def, ref, iref, xref, tref)
|
|
37
37
|
* @param {string} primary - The primary content/term
|
|
38
38
|
* @returns {string} The rendered HTML for the construct
|
|
39
39
|
*/
|
|
@@ -45,6 +45,8 @@ function parseTemplateTag(config, globalState, token, type, primary) {
|
|
|
45
45
|
switch (type) {
|
|
46
46
|
case 'def':
|
|
47
47
|
return parseDef(globalState, token, primary, currentFile);
|
|
48
|
+
case 'iref':
|
|
49
|
+
return parseIref(globalState, primary);
|
|
48
50
|
case 'xref':
|
|
49
51
|
return parseXref(config, token);
|
|
50
52
|
case 'tref':
|
|
@@ -130,6 +132,25 @@ function parseRef(globalState, primary) {
|
|
|
130
132
|
return `<a class="term-reference" href="#term:${termId}">${primary}</a>`;
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Processes [[iref: term]] constructs
|
|
137
|
+
* Creates a placeholder that will be replaced client-side with a copy of the term definition
|
|
138
|
+
* This allows inline copying of existing term definitions from the terms-and-definitions-list
|
|
139
|
+
* @param {Object} globalState - Global state to track inline references
|
|
140
|
+
* @param {string} primary - The term to inline copy
|
|
141
|
+
* @returns {string} HTML placeholder element that will be replaced by client-side script
|
|
142
|
+
*/
|
|
143
|
+
function parseIref(globalState, primary) {
|
|
144
|
+
// Track this inline reference for validation purposes
|
|
145
|
+
globalState.references.push(primary);
|
|
146
|
+
|
|
147
|
+
// Create a placeholder span with data attribute containing the term to copy
|
|
148
|
+
// The client-side script (insert-irefs.js) will find this and replace it with
|
|
149
|
+
// a copy of the actual <dt> and <dd> elements from the terms-and-definitions-list
|
|
150
|
+
const termId = primary.replace(whitespace.oneOrMore, '-').toLowerCase();
|
|
151
|
+
return `<span class="iref-placeholder" data-iref-term="${termId}" data-iref-original="${primary}"></span>`;
|
|
152
|
+
}
|
|
153
|
+
|
|
133
154
|
/**
|
|
134
155
|
* Processes [[xref: spec, term, alias, ...]] constructs
|
|
135
156
|
* Creates links to external specification terms with tooltips
|
|
@@ -298,6 +319,7 @@ module.exports = {
|
|
|
298
319
|
createTemplateTagParser,
|
|
299
320
|
// Export individual functions for testing purposes
|
|
300
321
|
parseDef,
|
|
322
|
+
parseIref,
|
|
301
323
|
parseXref,
|
|
302
324
|
parseTref,
|
|
303
325
|
parseRef,
|
|
@@ -46,7 +46,11 @@ function normalizeSpecConfiguration(config, { noSpecsMessage }) {
|
|
|
46
46
|
const specs = Array.isArray(config?.specs) ? config.specs : [];
|
|
47
47
|
|
|
48
48
|
if (specs.length === 0) {
|
|
49
|
-
Logger.error(noSpecsMessage
|
|
49
|
+
Logger.error(noSpecsMessage, {
|
|
50
|
+
context: 'specs.json is missing or has no specs array',
|
|
51
|
+
hint: 'Create a valid specs.json file in your project root. Run "npm run init" or copy from: https://github.com/trustoverip/spec-up-t-starter-pack',
|
|
52
|
+
details: 'The specs array is required to configure your specification'
|
|
53
|
+
});
|
|
50
54
|
return null;
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -65,7 +69,11 @@ function normalizeSpecConfiguration(config, { noSpecsMessage }) {
|
|
|
65
69
|
*/
|
|
66
70
|
function extendXTrefs(config, xtrefs) {
|
|
67
71
|
if (config.specs[0].external_specs_repos) {
|
|
68
|
-
Logger.warn('Your specs.json file uses an outdated structure
|
|
72
|
+
Logger.warn('Your specs.json file uses an outdated structure', {
|
|
73
|
+
context: 'The "external_specs_repos" field is deprecated',
|
|
74
|
+
hint: 'Update to the new structure using "external_specs" instead. See: https://github.com/trustoverip/spec-up-t/blob/master/src/install-from-boilerplate/boilerplate/specs.json',
|
|
75
|
+
details: 'External references may not work correctly with the old structure'
|
|
76
|
+
});
|
|
69
77
|
return;
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -118,7 +126,11 @@ function extendXTrefs(config, xtrefs) {
|
|
|
118
126
|
try {
|
|
119
127
|
xtref.branch = getCurrentBranch();
|
|
120
128
|
} catch (error) {
|
|
121
|
-
Logger.warn(`Could not get current branch for xtref ${xtref.externalSpec}:${xtref.term}
|
|
129
|
+
Logger.warn(`Could not get current branch for xtref ${xtref.externalSpec}:${xtref.term}`, {
|
|
130
|
+
context: 'Git branch detection failed for external reference',
|
|
131
|
+
hint: 'Ensure you\'re in a git repository, or specify github_repo_branch in specs.json external_specs configuration',
|
|
132
|
+
details: `${error.message}. Using default: main`
|
|
133
|
+
});
|
|
122
134
|
xtref.branch = 'main';
|
|
123
135
|
}
|
|
124
136
|
});
|
|
@@ -153,7 +165,7 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
|
|
|
153
165
|
}
|
|
154
166
|
|
|
155
167
|
const userInput = readlineSync.question(
|
|
156
|
-
`❌ This external reference is not a valid URL:
|
|
168
|
+
`❌ This external reference is not a valid URL:
|
|
157
169
|
|
|
158
170
|
Repository: ${repo.url},
|
|
159
171
|
|
|
@@ -195,14 +207,22 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
|
|
|
195
207
|
const termsDir = spec?.spec_terms_directory;
|
|
196
208
|
|
|
197
209
|
if (!specDir || !termsDir) {
|
|
198
|
-
Logger.warn(`Spec entry is missing spec_directory or spec_terms_directory
|
|
210
|
+
Logger.warn(`Spec entry is missing spec_directory or spec_terms_directory`, {
|
|
211
|
+
context: 'Invalid specs.json configuration',
|
|
212
|
+
hint: 'Ensure each spec in specs.json has both "spec_directory" and "spec_terms_directory" fields',
|
|
213
|
+
details: `Incomplete spec entry: ${JSON.stringify(spec)}`
|
|
214
|
+
});
|
|
199
215
|
return directories;
|
|
200
216
|
}
|
|
201
217
|
|
|
202
218
|
const resolvedDir = path.join(specDir, termsDir);
|
|
203
219
|
|
|
204
220
|
if (!fs.existsSync(resolvedDir)) {
|
|
205
|
-
Logger.warn(`Spec terms directory does not exist: ${resolvedDir}
|
|
221
|
+
Logger.warn(`Spec terms directory does not exist: ${resolvedDir}`, {
|
|
222
|
+
context: 'Directory specified in specs.json not found',
|
|
223
|
+
hint: 'Create the directory or update the path in specs.json. Typically this should be "spec/term-definitions" or similar',
|
|
224
|
+
details: `Expected path: ${resolvedDir}`
|
|
225
|
+
});
|
|
206
226
|
return directories;
|
|
207
227
|
}
|
|
208
228
|
|
|
@@ -211,7 +231,11 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
|
|
|
211
231
|
}, []);
|
|
212
232
|
|
|
213
233
|
if (specTermsDirectories.length === 0) {
|
|
214
|
-
Logger.warn('No spec terms directories found. Skipping external reference collection
|
|
234
|
+
Logger.warn('No spec terms directories found. Skipping external reference collection', {
|
|
235
|
+
context: 'Cannot collect external references without valid terminology directories',
|
|
236
|
+
hint: 'Check specs.json configuration. Ensure spec_directory and spec_terms_directory point to existing directories',
|
|
237
|
+
details: 'External trefs and xrefs will not be processed'
|
|
238
|
+
});
|
|
215
239
|
return;
|
|
216
240
|
}
|
|
217
241
|
|
|
@@ -249,7 +273,7 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
|
|
|
249
273
|
function collectExternalReferences(options = {}) {
|
|
250
274
|
// Start collecting messages if requested
|
|
251
275
|
const shouldCollectMessages = options.collectMessages !== false; // Collect by default
|
|
252
|
-
|
|
276
|
+
|
|
253
277
|
if (shouldCollectMessages) {
|
|
254
278
|
messageCollector.clearMessages();
|
|
255
279
|
messageCollector.startCollecting('collectExternalReferences');
|
|
@@ -273,8 +297,11 @@ function collectExternalReferences(options = {}) {
|
|
|
273
297
|
const GITHUB_API_TOKEN = options.pat || process.env.GITHUB_API_TOKEN;
|
|
274
298
|
|
|
275
299
|
if (!GITHUB_API_TOKEN) {
|
|
276
|
-
Logger.warn('No GitHub Personal Access Token (PAT) found. Running without authentication
|
|
277
|
-
|
|
300
|
+
Logger.warn('No GitHub Personal Access Token (PAT) found. Running without authentication', {
|
|
301
|
+
context: 'GitHub API requests will use unauthenticated access',
|
|
302
|
+
hint: 'Set GITHUB_PAT environment variable to increase rate limit from 60 to 5000 requests/hour. See: https://trustoverip.github.io/spec-up-t-website/docs/getting-started/github-token',
|
|
303
|
+
details: 'You may hit rate limits when fetching many external references'
|
|
304
|
+
});
|
|
278
305
|
}
|
|
279
306
|
|
|
280
307
|
// Communicate that the expected external_specs array is missing entirely.
|
|
@@ -282,7 +309,7 @@ function collectExternalReferences(options = {}) {
|
|
|
282
309
|
Logger.info(
|
|
283
310
|
'No external_specs array found on the first spec entry in specs.json. External reference collection is skipped.'
|
|
284
311
|
);
|
|
285
|
-
|
|
312
|
+
|
|
286
313
|
if (shouldCollectMessages) {
|
|
287
314
|
messageCollector.stopCollecting();
|
|
288
315
|
messageCollector.saveMessages().then(path => {
|
|
@@ -300,7 +327,7 @@ function collectExternalReferences(options = {}) {
|
|
|
300
327
|
Logger.info(
|
|
301
328
|
'The external_specs array in specs.json is empty. Add external repositories to collect external references.'
|
|
302
329
|
);
|
|
303
|
-
|
|
330
|
+
|
|
304
331
|
if (shouldCollectMessages) {
|
|
305
332
|
messageCollector.stopCollecting();
|
|
306
333
|
messageCollector.saveMessages().then(path => {
|
|
@@ -333,14 +360,14 @@ function collectExternalReferences(options = {}) {
|
|
|
333
360
|
})
|
|
334
361
|
.catch(error => {
|
|
335
362
|
Logger.error('Rendering failed after collecting external references.', error);
|
|
336
|
-
|
|
363
|
+
|
|
337
364
|
if (shouldCollectMessages) {
|
|
338
365
|
messageCollector.stopCollecting();
|
|
339
366
|
messageCollector.saveMessages().catch(() => {
|
|
340
367
|
// Silent fail on save error
|
|
341
368
|
});
|
|
342
369
|
}
|
|
343
|
-
|
|
370
|
+
|
|
344
371
|
throw error;
|
|
345
372
|
});
|
|
346
373
|
} else {
|
|
@@ -17,7 +17,11 @@ function validateReferences(references, definitions, render) {
|
|
|
17
17
|
}
|
|
18
18
|
);
|
|
19
19
|
if (unresolvedRefs.length > 0) {
|
|
20
|
-
Logger.warn(`Unresolved References: ${unresolvedRefs.join(',')}
|
|
20
|
+
Logger.warn(`Unresolved References: ${unresolvedRefs.join(',')}`, {
|
|
21
|
+
context: 'These terms are referenced in your spec but not defined',
|
|
22
|
+
hint: 'Add [[def: term]] definitions for these terms in your terminology files, or check for typos in [[ref: term]] references',
|
|
23
|
+
details: `Count: ${unresolvedRefs.length} unresolved term(s)`
|
|
24
|
+
});
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
const danglingDefs = [];
|
|
@@ -38,7 +42,11 @@ function validateReferences(references, definitions, render) {
|
|
|
38
42
|
}
|
|
39
43
|
})
|
|
40
44
|
if (danglingDefs.length > 0) {
|
|
41
|
-
Logger.warn(`Dangling Definitions: ${danglingDefs.join(',')}
|
|
45
|
+
Logger.warn(`Dangling Definitions: ${danglingDefs.join(',')}`, {
|
|
46
|
+
context: 'These terms are defined but never referenced in your spec',
|
|
47
|
+
hint: 'Add [[ref: term]] references where needed.',
|
|
48
|
+
details: `Count: ${danglingDefs.length} unused definition(s)`
|
|
49
|
+
});
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -71,7 +79,11 @@ async function fetchExternalSpecs(spec) {
|
|
|
71
79
|
const msg = f.error.response
|
|
72
80
|
? `HTTP ${f.error.response.status} for ${f.url}`
|
|
73
81
|
: `Network error for ${f.url}: ${f.error.message}`;
|
|
74
|
-
Logger.error("External spec fetch failed
|
|
82
|
+
Logger.error("External spec fetch failed", {
|
|
83
|
+
context: `Attempting to fetch external terminology from: ${f.url}`,
|
|
84
|
+
hint: 'Verify the URL is correct in specs.json under external_specs[].gh_page. Ensure the external spec has published their GitHub Pages site',
|
|
85
|
+
details: msg
|
|
86
|
+
});
|
|
75
87
|
});
|
|
76
88
|
}
|
|
77
89
|
|
|
@@ -93,7 +105,11 @@ async function fetchExternalSpecs(spec) {
|
|
|
93
105
|
|
|
94
106
|
return extractedTerms;
|
|
95
107
|
} catch (e) {
|
|
96
|
-
Logger.error("Unexpected error in fetchExternalSpecs
|
|
108
|
+
Logger.error("Unexpected error in fetchExternalSpecs", {
|
|
109
|
+
context: 'Failed while fetching terms from external specifications',
|
|
110
|
+
hint: 'Check your internet connection and verify all external_specs URLs in specs.json are valid. Run with GITHUB_PAT set if accessing private repos',
|
|
111
|
+
details: e.message
|
|
112
|
+
});
|
|
97
113
|
return [];
|
|
98
114
|
}
|
|
99
115
|
}
|
|
@@ -132,7 +148,7 @@ async function mergeXrefTermsIntoAllXTrefs(xrefTerms, outputPathJSON, outputPath
|
|
|
132
148
|
if (existingIndex >= 0) {
|
|
133
149
|
// Get the existing entry to check if it's a tref
|
|
134
150
|
const existingXtref = allXTrefs.xtrefs[existingIndex];
|
|
135
|
-
|
|
151
|
+
|
|
136
152
|
// Update existing entry - preserve the existing metadata but add/update content
|
|
137
153
|
allXTrefs.xtrefs[existingIndex] = {
|
|
138
154
|
...existingXtref,
|
|
@@ -151,26 +167,34 @@ async function mergeXrefTermsIntoAllXTrefs(xrefTerms, outputPathJSON, outputPath
|
|
|
151
167
|
|
|
152
168
|
if (isExternalTref && isTref) {
|
|
153
169
|
// Build a readable list of source files for the error message
|
|
154
|
-
const sourceFilesList = existingXtref.sourceFile
|
|
155
|
-
? existingXtref.sourceFile
|
|
170
|
+
const sourceFilesList = existingXtref.sourceFile
|
|
171
|
+
? existingXtref.sourceFile
|
|
156
172
|
: (existingXtref.sourceFiles || []).map(sf => sf.file).join(', ');
|
|
157
|
-
|
|
173
|
+
|
|
158
174
|
// Construct the external repository URL
|
|
159
175
|
const externalRepoUrl = existingXtref.ghPageUrl || existingXtref.repoUrl || `https://github.com/${existingXtref.owner}/${existingXtref.repo}`;
|
|
160
|
-
|
|
161
|
-
Logger.error(`
|
|
176
|
+
|
|
177
|
+
Logger.error(`NESTED TREF DETECTED: Term "${existingXtref.term}" in ${existingXtref.externalSpec}`, {
|
|
178
|
+
context: `Origin: ${sourceFilesList} - This term is itself transcluded from another spec`,
|
|
179
|
+
hint: 'Avoid chaining external references (tref → tref). Reference the original source spec directly, or define the term locally in your spec',
|
|
180
|
+
details: `This creates a complex dependency chain. Repository: ${externalRepoUrl}`
|
|
181
|
+
});
|
|
162
182
|
}
|
|
163
183
|
|
|
164
184
|
if (isExternalTref && isXref) {
|
|
165
185
|
// Build a readable list of source files for the warning message
|
|
166
|
-
const sourceFilesList = existingXtref.sourceFile
|
|
167
|
-
? existingXtref.sourceFile
|
|
186
|
+
const sourceFilesList = existingXtref.sourceFile
|
|
187
|
+
? existingXtref.sourceFile
|
|
168
188
|
: (existingXtref.sourceFiles || []).map(sf => sf.file).join(', ');
|
|
169
|
-
|
|
189
|
+
|
|
170
190
|
// Construct the external repository URL
|
|
171
191
|
const externalRepoUrl = existingXtref.ghPageUrl || existingXtref.repoUrl || `https://github.com/${existingXtref.owner}/${existingXtref.repo}`;
|
|
172
|
-
|
|
173
|
-
Logger.error(`
|
|
192
|
+
|
|
193
|
+
Logger.error(`NESTED XREF DETECTED: Term "${existingXtref.term}" in ${existingXtref.externalSpec}`, {
|
|
194
|
+
context: `Origin: ${sourceFilesList} - This xref points to a term that is already transcluded from elsewhere`,
|
|
195
|
+
hint: 'Use [[xref]] only for terms directly defined in the external spec. For nested references, either reference the original source or define the term locally',
|
|
196
|
+
details: `This creates a chain of external references. Repository: ${externalRepoUrl}`
|
|
197
|
+
});
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
matchedCount++;
|
|
@@ -191,7 +215,11 @@ async function mergeXrefTermsIntoAllXTrefs(xrefTerms, outputPathJSON, outputPath
|
|
|
191
215
|
Logger.success(`Merged xref terms: ${matchedCount} matched, ${skippedCount} skipped (not referenced). Total entries: ${allXTrefs.xtrefs.length}`);
|
|
192
216
|
|
|
193
217
|
} catch (error) {
|
|
194
|
-
Logger.error('Error merging xref terms into allXTrefs
|
|
218
|
+
Logger.error('Error merging xref terms into allXTrefs', {
|
|
219
|
+
context: 'Failed while merging external reference terms into the xtrefs cache',
|
|
220
|
+
hint: 'This may indicate corrupted cache files in .cache/xtrefs/. Try deleting the .cache directory and running the build again',
|
|
221
|
+
details: error.message
|
|
222
|
+
});
|
|
195
223
|
}
|
|
196
224
|
}
|
|
197
225
|
|
|
@@ -219,18 +247,18 @@ function extractTermsFromHtml(externalSpec, html) {
|
|
|
219
247
|
batch.each((index, termElement) => {
|
|
220
248
|
try {
|
|
221
249
|
const $termElement = $(termElement);
|
|
222
|
-
|
|
250
|
+
|
|
223
251
|
// The id can be on the dt element itself OR on span(s) inside it
|
|
224
252
|
// Some terms have multiple nested spans with different IDs (e.g., aliases)
|
|
225
253
|
// We need to extract ALL term IDs from the dt element
|
|
226
254
|
const termIds = [];
|
|
227
|
-
|
|
255
|
+
|
|
228
256
|
// First check if dt itself has an id
|
|
229
257
|
const dtId = $termElement.attr('id');
|
|
230
258
|
if (dtId && dtId.includes('term:')) {
|
|
231
259
|
termIds.push(dtId.replace('term:', ''));
|
|
232
260
|
}
|
|
233
|
-
|
|
261
|
+
|
|
234
262
|
// Then find all spans with ids that contain 'term:'
|
|
235
263
|
$termElement.find('span[id*="term:"]').each((i, span) => {
|
|
236
264
|
const spanId = $(span).attr('id');
|
|
@@ -241,7 +269,7 @@ function extractTermsFromHtml(externalSpec, html) {
|
|
|
241
269
|
}
|
|
242
270
|
}
|
|
243
271
|
});
|
|
244
|
-
|
|
272
|
+
|
|
245
273
|
// Skip if no valid term IDs found
|
|
246
274
|
if (termIds.length === 0) {
|
|
247
275
|
return;
|
|
@@ -252,12 +280,12 @@ function extractTermsFromHtml(externalSpec, html) {
|
|
|
252
280
|
const dtClasses = $termElement.attr('class');
|
|
253
281
|
const classArray = dtClasses ? dtClasses.split(/\s+/).filter(Boolean) : [];
|
|
254
282
|
const termClasses = classArray.filter(cls => cls === 'term-local' || cls === 'term-external');
|
|
255
|
-
|
|
283
|
+
|
|
256
284
|
const dd = $termElement.next('dd');
|
|
257
285
|
|
|
258
286
|
if (dd.length > 0) {
|
|
259
287
|
const ddContent = $.html(dd); // Store the complete DD content once
|
|
260
|
-
|
|
288
|
+
|
|
261
289
|
// Create a term object for each ID found in this dt element
|
|
262
290
|
// This handles cases where one term definition has multiple aliases
|
|
263
291
|
termIds.forEach(termName => {
|
|
@@ -275,7 +303,11 @@ function extractTermsFromHtml(externalSpec, html) {
|
|
|
275
303
|
});
|
|
276
304
|
}
|
|
277
305
|
} catch (termError) {
|
|
278
|
-
Logger.warn(`Error processing term in ${externalSpec}
|
|
306
|
+
Logger.warn(`Error processing term in ${externalSpec}`, {
|
|
307
|
+
context: 'Failed to extract a specific term from external spec HTML',
|
|
308
|
+
hint: 'The external spec may have malformed HTML or non-standard term definitions. Contact the spec maintainer if this persists',
|
|
309
|
+
details: termError.message
|
|
310
|
+
});
|
|
279
311
|
}
|
|
280
312
|
});
|
|
281
313
|
|
|
@@ -289,7 +321,11 @@ function extractTermsFromHtml(externalSpec, html) {
|
|
|
289
321
|
return terms;
|
|
290
322
|
|
|
291
323
|
} catch (error) {
|
|
292
|
-
Logger.error(`Error extracting terms from external spec '${externalSpec}'
|
|
324
|
+
Logger.error(`Error extracting terms from external spec '${externalSpec}'`, {
|
|
325
|
+
context: 'Failed while parsing HTML from external spec to extract term definitions',
|
|
326
|
+
hint: 'Verify that the external spec is a valid spec-up-t generated document with a proper terms-and-definitions section',
|
|
327
|
+
details: error.message
|
|
328
|
+
});
|
|
293
329
|
return [];
|
|
294
330
|
}
|
|
295
331
|
}
|
|
@@ -14,7 +14,11 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
14
14
|
|
|
15
15
|
allXTrefs.xtrefs = allXTrefs.xtrefs.filter(xtref => {
|
|
16
16
|
if (!xtref.owner || !xtref.repo || !xtref.repoUrl) {
|
|
17
|
-
Logger.error(`Removing incomplete reference: ${xtref.externalSpec}, ${xtref.term}
|
|
17
|
+
Logger.error(`Removing incomplete reference: ${xtref.externalSpec}, ${xtref.term}`, {
|
|
18
|
+
context: 'External reference is missing required repository information',
|
|
19
|
+
hint: 'Verify external_specs configuration in specs.json includes github_repo_url for each entry',
|
|
20
|
+
details: `Missing: ${!xtref.owner ? 'owner' : ''} ${!xtref.repo ? 'repo' : ''} ${!xtref.repoUrl ? 'repoUrl' : ''}`
|
|
21
|
+
});
|
|
18
22
|
return false;
|
|
19
23
|
}
|
|
20
24
|
return true;
|
|
@@ -51,7 +55,11 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
51
55
|
);
|
|
52
56
|
|
|
53
57
|
if (!allTermsData) {
|
|
54
|
-
Logger.error(`Could not fetch terms from repository ${repoKey}
|
|
58
|
+
Logger.error(`Could not fetch terms from repository ${repoKey}`, {
|
|
59
|
+
context: `Failed to retrieve terminology from ${repoUrl}`,
|
|
60
|
+
hint: 'Ensure the repository exists, is accessible, and has published its spec. If it\'s private, set GITHUB_PAT environment variable',
|
|
61
|
+
details: `Repository: ${repoUrl}. Check if GitHub Pages is enabled or if the repo has a valid specs.json`
|
|
62
|
+
});
|
|
55
63
|
repoGroup.xtrefs.forEach(xtref => {
|
|
56
64
|
xtref.commitHash = 'not found';
|
|
57
65
|
xtref.content = 'This term was not found in the external repository.';
|
|
@@ -81,26 +89,34 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
81
89
|
|
|
82
90
|
if (isExternalTref && isTref) {
|
|
83
91
|
// Build a readable list of source files for the error message
|
|
84
|
-
const sourceFilesList = xtref.sourceFile
|
|
85
|
-
? xtref.sourceFile
|
|
92
|
+
const sourceFilesList = xtref.sourceFile
|
|
93
|
+
? xtref.sourceFile
|
|
86
94
|
: (xtref.sourceFiles || []).map(sf => sf.file).join(', ');
|
|
87
|
-
|
|
95
|
+
|
|
88
96
|
// Construct the external repository URL
|
|
89
97
|
const externalRepoUrl = xtref.ghPageUrl || xtref.repoUrl || `https://github.com/${xtref.owner}/${xtref.repo}`;
|
|
90
|
-
|
|
91
|
-
Logger.error(`
|
|
98
|
+
|
|
99
|
+
Logger.error(`NESTED TREF DETECTED: Term "${xtref.term}" in ${xtref.externalSpec}`, {
|
|
100
|
+
context: `Origin: ${sourceFilesList} - This term is itself a tref transcluded from another spec`,
|
|
101
|
+
hint: 'Avoid chaining trefs (tref → tref). Either reference the original source spec directly, or define the term locally',
|
|
102
|
+
details: `Repository: ${externalRepoUrl}. Nested trefs create complex dependency chains`
|
|
103
|
+
});
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
if (isExternalTref && isXref) {
|
|
95
107
|
// Build a readable list of source files for the warning message
|
|
96
|
-
const sourceFilesList = xtref.sourceFile
|
|
97
|
-
? xtref.sourceFile
|
|
108
|
+
const sourceFilesList = xtref.sourceFile
|
|
109
|
+
? xtref.sourceFile
|
|
98
110
|
: (xtref.sourceFiles || []).map(sf => sf.file).join(', ');
|
|
99
|
-
|
|
111
|
+
|
|
100
112
|
// Construct the external repository URL
|
|
101
113
|
const externalRepoUrl = xtref.ghPageUrl || xtref.repoUrl || `https://github.com/${xtref.owner}/${xtref.repo}`;
|
|
102
|
-
|
|
103
|
-
Logger.error(`
|
|
114
|
+
|
|
115
|
+
Logger.error(`NESTED XREF DETECTED: Term "${xtref.term}" in ${xtref.externalSpec}`, {
|
|
116
|
+
context: `Origin: ${sourceFilesList} - This xref points to a term that is already transcluded elsewhere`,
|
|
117
|
+
hint: 'Use [[xref]] only for terms directly defined in the external spec. For nested refs, reference the original source',
|
|
118
|
+
details: `Repository: ${externalRepoUrl}. This creates a chain of external references`
|
|
119
|
+
});
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
Logger.success(`Match found for term: ${xtref.term} in ${xtref.externalSpec}`);
|
|
@@ -108,7 +124,7 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
108
124
|
xtref.commitHash = 'not found';
|
|
109
125
|
xtref.content = 'This term was not found in the external repository.';
|
|
110
126
|
xtref.avatarUrl = null;
|
|
111
|
-
|
|
127
|
+
|
|
112
128
|
// Build a readable list of source files for the error message.
|
|
113
129
|
// Two possible data structures exist:
|
|
114
130
|
// 1. xtref.sourceFile is a STRING like "primitive.md"
|
|
@@ -119,15 +135,19 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
119
135
|
// - Otherwise → extract file names from the sourceFiles array:
|
|
120
136
|
// - .map(sf => sf.file) extracts just the filename from each object
|
|
121
137
|
// - .join(', ') combines them into a comma-separated string
|
|
122
|
-
const sourceFilesList = xtref.sourceFile
|
|
123
|
-
? xtref.sourceFile
|
|
138
|
+
const sourceFilesList = xtref.sourceFile
|
|
139
|
+
? xtref.sourceFile
|
|
124
140
|
: (xtref.sourceFiles || []).map(sf => sf.file).join(', ');
|
|
125
141
|
|
|
126
142
|
// Prefer an explicit repo URL if provided on the xtref, otherwise
|
|
127
143
|
// build a standard GitHub URL from the owner/repo.
|
|
128
144
|
const githubUrl = xtref.repoUrl || `https://github.com/${repoKey}`;
|
|
129
145
|
|
|
130
|
-
Logger.error(`
|
|
146
|
+
Logger.error(`No match found for term: ${xtref.term} in ${xtref.externalSpec}`, {
|
|
147
|
+
context: `Origin: ${sourceFilesList} - Term not found in external repository`,
|
|
148
|
+
hint: 'Check if the term exists in the external spec. Verify spelling, ensure the external spec has published, and confirm the term is in their terminology section',
|
|
149
|
+
details: `Repository: ${githubUrl}. The term may have been renamed or removed`
|
|
150
|
+
});
|
|
131
151
|
}
|
|
132
152
|
}
|
|
133
153
|
|
|
@@ -138,10 +158,14 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
|
|
|
138
158
|
const allXTrefsStr = JSON.stringify(allXTrefs, null, 2);
|
|
139
159
|
fs.writeFileSync(outputPathJSON, allXTrefsStr, 'utf8');
|
|
140
160
|
const jsPayload = `const allXTrefs = ${allXTrefsStr};`;
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
fs.writeFileSync(outputPathJS, jsPayload, 'utf8');
|
|
162
|
+
fs.writeFileSync(outputPathJSTimeStamped, jsPayload, 'utf8');
|
|
143
163
|
} catch (error) {
|
|
144
|
-
Logger.error('An error occurred
|
|
164
|
+
Logger.error('An error occurred during xtrefs processing', {
|
|
165
|
+
context: 'Failed while processing external references and fetching terms',
|
|
166
|
+
hint: 'Check your internet connection, verify GITHUB_PAT is set if needed, and ensure specs.json external_specs configuration is correct',
|
|
167
|
+
details: error.message
|
|
168
|
+
});
|
|
145
169
|
}
|
|
146
170
|
}
|
|
147
171
|
|