spec-up-t 1.1.55 → 1.2.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.
Files changed (45) hide show
  1. package/assets/compiled/body.js +59 -8
  2. package/assets/compiled/head.css +13 -12
  3. package/assets/css/adjust-font-size.css +11 -0
  4. package/assets/css/collapse-meta-info.css +27 -8
  5. package/assets/css/create-term-filter.css +11 -0
  6. package/assets/css/index.css +15 -0
  7. package/assets/css/prism.css +176 -153
  8. package/assets/css/prism.dark.css +157 -0
  9. package/assets/css/prism.default.css +180 -0
  10. package/assets/css/terms-and-definitions.1.css +223 -0
  11. package/assets/css/terms-and-definitions.css +214 -100
  12. package/assets/js/addAnchorsToTerms.js +1 -1
  13. package/assets/js/collapse-definitions.js +2 -1
  14. package/assets/js/collapse-meta-info.js +25 -11
  15. package/assets/js/create-term-filter.js +61 -0
  16. package/assets/js/horizontal-scroll-hint.js +159 -0
  17. package/assets/js/index.1.js +137 -0
  18. package/assets/js/index.js +2 -1
  19. package/assets/js/insert-trefs.js +122 -116
  20. package/assets/js/insert-xrefs.1.js +372 -0
  21. package/assets/js/{show-commit-hashes.js → insert-xrefs.js} +67 -7
  22. package/assets/js/prism.dark.js +24 -0
  23. package/assets/js/prism.default.js +23 -0
  24. package/assets/js/prism.js +4 -5
  25. package/assets/js/search.js +1 -1
  26. package/assets/js/toggle-dense-info.js +40 -0
  27. package/branches.md +4 -24
  28. package/index.js +429 -190
  29. package/index.new.js +662 -0
  30. package/package.json +1 -2
  31. package/src/asset-map.json +9 -5
  32. package/src/collect-external-references.js +16 -9
  33. package/src/collectExternalReferences/fetchTermsFromIndex.js +328 -0
  34. package/src/collectExternalReferences/processXTrefsData.js +73 -18
  35. package/src/create-pdf.js +385 -89
  36. package/src/health-check/term-references-checker.js +3 -2
  37. package/src/health-check/tref-term-checker.js +18 -17
  38. package/src/markdown-it-extensions.js +134 -103
  39. package/src/prepare-tref.js +61 -24
  40. package/src/utils/fetch.js +100 -0
  41. package/templates/template.html +12 -7
  42. package/assets/js/css-helper.js +0 -30
  43. package/src/collectExternalReferences/fetchTermsFromGitHubRepository.js +0 -232
  44. package/src/collectExternalReferences/fetchTermsFromGitHubRepository.test.js +0 -385
  45. package/src/collectExternalReferences/octokitClient.js +0 -96
@@ -2,23 +2,24 @@
2
2
  "head": {
3
3
  "css": [
4
4
  "assets/css/theme-vars.css",
5
+ "assets/css/bootstrap.min.css",
5
6
  "assets/css/custom-elements.css",
6
7
  "assets/css/prism.css",
7
8
  "assets/css/chart.css",
9
+ "assets/css/header-navbar.css",
10
+ "assets/css/sidebar-toc.css",
11
+ "assets/css/terms-and-definitions.css",
8
12
  "assets/css/edit-term-buttons.css",
9
13
  "assets/css/search.css",
10
14
  "assets/css/highlightMenuItems.css",
11
15
  "assets/css/backToTop.css",
12
16
  "assets/css/notyf.css",
13
17
  "assets/css/collapse-definitions.css",
18
+ "assets/css/create-term-filter.css",
14
19
  "assets/css/collapse-meta-info.css",
15
20
  "assets/css/modal.css",
16
21
  "assets/css/create-alphabet-index.css",
17
22
  "assets/css/pdf-download.css",
18
- "assets/css/terms-and-definitions.css",
19
- "assets/css/bootstrap.min.css",
20
- "assets/css/header-navbar.css",
21
- "assets/css/sidebar-toc.css",
22
23
  "assets/css/loader.css",
23
24
  "assets/css/external-links.css",
24
25
  "assets/css/repo-issues.css",
@@ -56,12 +57,15 @@
56
57
  "assets/js/pdf-download.js",
57
58
  "assets/js/insert-trefs.js",
58
59
  "assets/js/collapse-definitions.js",
60
+ "assets/js/create-term-filter.js",
59
61
  "assets/js/collapse-meta-info.js",
60
62
  "assets/js/add-href-to-snapshot-link.js",
61
63
  "assets/js/adjust-font-size.js",
64
+ "assets/js/toggle-dense-info.js",
62
65
  "assets/js/close-off-canvas-menu.js",
66
+
63
67
  "assets/js/index.js",
64
- "assets/js/css-helper.js",
68
+ "assets/js/horizontal-scroll-hint.js",
65
69
  "assets/js/bootstrap.bundle.min.js"
66
70
  ]
67
71
  }
@@ -42,6 +42,8 @@
42
42
 
43
43
 
44
44
  const { shouldProcessFile } = require('./utils/file-filter');
45
+ const path = require('path');
46
+
45
47
  function isXTrefInMarkdown(xtref, markdownContent) {
46
48
  const regex = new RegExp(`\\[\\[(?:x|t)ref:${xtref.externalSpec},\\s*${xtref.term}\\]\\]`, 'g');
47
49
  return regex.test(markdownContent);
@@ -192,22 +194,26 @@ function collectExternalReferences(options = {}) {
192
194
  // Collect all directories that contain files with a term and definition
193
195
  // This maps over the specs in the config file and constructs paths to directories
194
196
  // where the term definition files are located.
195
- const specTermsDirectories = config.specs.map(spec => spec.spec_directory + '/' + spec.spec_terms_directory);
197
+ const specTermsDirectories = config.specs.map(spec =>
198
+ path.join(spec.spec_directory, spec.spec_terms_directory)
199
+ );
196
200
 
197
201
  // Ensure that the 'output' directory exists, creating it if necessary.
198
- if (!fs.existsSync('output')) {
199
- fs.mkdirSync('output');
202
+ const outputDir = 'output';
203
+ if (!fs.existsSync(outputDir)) {
204
+ fs.mkdirSync(outputDir);
200
205
  }
201
206
 
202
207
  // Ensure that the 'output/xtrefs-history' directory exists, creating it if necessary.
203
- if (!fs.existsSync('output/xtrefs-history')) {
204
- fs.mkdirSync('output/xtrefs-history');
208
+ const xtrefsHistoryDir = path.join(outputDir, 'xtrefs-history');
209
+ if (!fs.existsSync(xtrefsHistoryDir)) {
210
+ fs.mkdirSync(xtrefsHistoryDir);
205
211
  }
206
212
 
207
213
  // Define paths for various output files, including JSON and JS files.
208
- const outputPathJSON = 'output/xtrefs-data.json';
209
- const outputPathJS = 'output/xtrefs-data.js';
210
- const outputPathJSTimeStamped = 'output/xtrefs-history/xtrefs-data-' + Date.now() + '.js';
214
+ const outputPathJSON = path.join(outputDir, 'xtrefs-data.json');
215
+ const outputPathJS = path.join(outputDir, 'xtrefs-data.js');
216
+ const outputPathJSTimeStamped = path.join(xtrefsHistoryDir, `xtrefs-data-${Date.now()}.js`);
211
217
 
212
218
  // Function to extend xtref objects with additional information, such as repository URL and directory information.
213
219
  function extendXTrefs(config, xtrefs) {
@@ -280,7 +286,8 @@ function collectExternalReferences(options = {}) {
280
286
  specTermsDirectories.forEach(specDirectory => {
281
287
  fs.readdirSync(specDirectory).forEach(file => {
282
288
  if (shouldProcessFile(file)) {
283
- const markdown = fs.readFileSync(`${specDirectory}/${file}`, 'utf8');
289
+ const filePath = path.join(specDirectory, file);
290
+ const markdown = fs.readFileSync(filePath, 'utf8');
284
291
  allMarkdownContent += markdown;
285
292
  }
286
293
  });
@@ -0,0 +1,328 @@
1
+ /**
2
+ * @file fetchTermsFromIndex.js
3
+ * @description Fetches terms and definitions from external repository's index.html
4
+ * @author Generated with assistance from GitHub Copilot
5
+ * @version 1.0.0
6
+ * @since 2025-04-15
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { JSDOM } = require('jsdom');
12
+ const axios = require('axios');
13
+ const { addPath, getPath, getAllPaths } = require('../config/paths');
14
+ const crypto = require('crypto');
15
+
16
+ // Directory to store cached files
17
+ const CACHE_DIR = getPath('githubcache');
18
+
19
+ /**
20
+ * Generates a cache key based on repository information
21
+ * @param {string} owner - Repository owner
22
+ * @param {string} repo - Repository name
23
+ * @returns {string} - Cache key
24
+ */
25
+ function generateCacheKey(owner, repo) {
26
+ const input = `${owner}-${repo}-index`;
27
+ return crypto.createHash('md5').update(input).digest('hex');
28
+ }
29
+
30
+ /**
31
+ * Checks if a cached version exists and is valid
32
+ * @param {string} cacheKey - Cache key
33
+ * @param {object} options - Options object
34
+ * @param {number} options.cacheTTL - Time-to-live for cache in milliseconds (default: 24 hours)
35
+ * @returns {object|null} - Cached data or null if not found or expired
36
+ */
37
+ function getFromCache(cacheKey, options = {}) {
38
+ const cachePath = path.join(CACHE_DIR, `${cacheKey}.json`);
39
+ // const cacheTTL = options.cacheTTL || 24 * 60 * 60 * 1000; // Default: 24 hours
40
+ const cacheTTL = 0;
41
+
42
+ if (!fs.existsSync(cachePath)) {
43
+ return null;
44
+ }
45
+
46
+ const cacheData = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
47
+ const cacheTime = new Date(cacheData.timestamp).getTime();
48
+ const currentTime = new Date().getTime();
49
+
50
+ // Check if cache is expired
51
+ if (currentTime - cacheTime > cacheTTL) {
52
+ console.log(`Cache expired for key: ${cacheKey}`);
53
+ return null;
54
+ }
55
+
56
+ console.log(`Using cached data for key: ${cacheKey}`);
57
+ return cacheData;
58
+ }
59
+
60
+ /**
61
+ * Saves data to cache
62
+ * @param {string} cacheKey - Cache key
63
+ * @param {object} data - Data to cache
64
+ */
65
+ function saveToCache(cacheKey, data) {
66
+ const cachePath = path.join(CACHE_DIR, `${cacheKey}.json`);
67
+ const cacheData = {
68
+ timestamp: new Date().toISOString(),
69
+ ...data
70
+ };
71
+ fs.writeFileSync(cachePath, JSON.stringify(cacheData, null, 2));
72
+ console.log(`Saved to cache: ${cacheKey}`);
73
+ }
74
+
75
+ /**
76
+ * Fetches the latest commit hash for a specific file in a repository
77
+ * @param {string} token - GitHub API Token
78
+ * @param {string} owner - Repository owner
79
+ * @param {string} repo - Repository name
80
+ * @param {string} filePath - Path to the file in the repository
81
+ * @param {object} headers - Request headers
82
+ * @returns {string|null} - Latest commit hash or null if error
83
+ */
84
+ async function getFileCommitHash(token, owner, repo, filePath, headers) {
85
+ try {
86
+ // Normalize the file path to ensure it doesn't have leading slash
87
+ const normalizedPath = filePath.replace(/^\//, '');
88
+
89
+ // Construct API URL to get commits for the specific file
90
+ const commitsUrl = `https://api.github.com/repos/${owner}/${repo}/commits?path=${normalizedPath}&per_page=1`;
91
+ console.log(`Fetching latest commit for file: ${commitsUrl}`);
92
+
93
+ const response = await axios.get(commitsUrl, { headers });
94
+
95
+ if (response.status !== 200 || !response.data || response.data.length === 0) {
96
+ console.log(`❌ Could not find commit information for ${filePath}`);
97
+ return null;
98
+ }
99
+
100
+ // Return the SHA of the latest commit
101
+ return response.data[0].sha;
102
+ } catch (error) {
103
+ console.error(`❌ Error fetching commit hash: ${error.message}`);
104
+ return null;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Fetches all terms and definitions from a repository's index.html
110
+ * @param {string} token - GitHub API Token
111
+ * @param {string} owner - Repository owner
112
+ * @param {string} repo - Repository name
113
+ * @param {object} options - Additional options
114
+ * @returns {object|null} - Object containing all terms or null if error
115
+ */
116
+ async function fetchAllTermsFromIndex(token, owner, repo, options = {}) {
117
+ try {
118
+ // Generate cache key based on repo information
119
+ const cacheKey = generateCacheKey(owner, repo);
120
+ let cachedData = null;
121
+
122
+ // Check cache first if caching is enabled
123
+ if (options.cache !== false) {
124
+ cachedData = getFromCache(cacheKey, options);
125
+ if (cachedData) {
126
+ return cachedData;
127
+ }
128
+ }
129
+
130
+ // Configure headers for GitHub API
131
+ const headers = {};
132
+ if (token) {
133
+ headers['Authorization'] = `token ${token}`;
134
+ }
135
+
136
+ // Get the specs.json content from the repository to find the output_path
137
+ const specsJsonUrl = `https://api.github.com/repos/${owner}/${repo}/contents/specs.json`;
138
+ console.log(`Fetching specs.json from: ${specsJsonUrl}`);
139
+
140
+ // Fetch specs.json content
141
+ const specsJsonResponse = await axios.get(specsJsonUrl, { headers });
142
+ if (specsJsonResponse.status !== 200) {
143
+ console.log(`❌ Could not find specs.json in repository ${owner}/${repo}`);
144
+ return null;
145
+ }
146
+
147
+ // Decode specs.json content from base64
148
+ const specsJsonContent = Buffer.from(specsJsonResponse.data.content, 'base64').toString('utf8');
149
+ const specsJson = JSON.parse(specsJsonContent);
150
+
151
+ // Get the output_path from specs.json
152
+ const outputPath = specsJson.specs[0].output_path;
153
+ if (!outputPath) {
154
+ console.log(`❌ No output_path found in specs.json for repository ${owner}/${repo}`);
155
+ return null;
156
+ }
157
+
158
+ // Fix: Properly normalize the output path to ensure it doesn't have leading "./" or trailing "/"
159
+ const normalizedOutputPath = outputPath.replace(/^\.\//, '').replace(/\/$/, '');
160
+
161
+ // Create the path to the index.html file
162
+ const indexHtmlPath = `${normalizedOutputPath}/index.html`;
163
+
164
+ // Fetch the index.html content with properly constructed URL
165
+ const indexHtmlUrl = `https://raw.githubusercontent.com/${owner}/${repo}/main/${indexHtmlPath}`;
166
+ console.log(`Fetching index.html from: ${indexHtmlUrl}`);
167
+
168
+ const indexHtmlResponse = await axios.get(indexHtmlUrl, { headers });
169
+ if (indexHtmlResponse.status !== 200) {
170
+ console.log(`❌ Could not find index.html at ${indexHtmlUrl}`);
171
+ return null;
172
+ }
173
+
174
+ // Get the commit hash for the index.html file
175
+ const commitHash = await getFileCommitHash(token, owner, repo, indexHtmlPath, headers);
176
+ if (!commitHash) {
177
+ console.log(`⚠️ Could not get commit hash for index.html, continuing without it`);
178
+ }
179
+
180
+ const htmlContent = indexHtmlResponse.data;
181
+
182
+ // Parse HTML using JSDOM
183
+ const dom = new JSDOM(htmlContent);
184
+ const document = dom.window.document;
185
+
186
+ // Find all term definition lists with class "terms-and-definitions-list"
187
+ const termDlList = document.querySelector('dl.terms-and-definitions-list');
188
+ if (!termDlList) {
189
+ console.log(`❌ No terms-and-definitions-list found in ${indexHtmlUrl}`);
190
+ return null;
191
+ }
192
+
193
+ // Extract all terms and definitions
194
+ const terms = [];
195
+ let dtElements = termDlList.querySelectorAll('dt');
196
+
197
+ dtElements.forEach(dt => {
198
+ // Find the term span that starts with id="term:
199
+ const termSpan = dt.querySelector('span[id^="term:"]');
200
+ if (!termSpan) return;
201
+
202
+ // Get the term text (all text content, excluding nested spans)
203
+ let termText = '';
204
+ for (let node of termSpan.childNodes) {
205
+ if (node.nodeType === dom.window.Node.TEXT_NODE) {
206
+ termText += node.textContent.trim();
207
+ }
208
+ }
209
+
210
+ // If no text found, try to get the full text content
211
+ if (!termText) {
212
+ termText = termSpan.textContent.trim();
213
+ }
214
+
215
+ // Skip empty terms
216
+ if (!termText) return;
217
+
218
+ // Find all corresponding definition elements
219
+ let dds = [];
220
+ let nextElement = dt.nextElementSibling;
221
+
222
+ // Collect all consecutive <dd> elements until we reach another <dt>
223
+ while (nextElement && nextElement.tagName.toLowerCase() === 'dd') {
224
+ dds.push(nextElement.outerHTML);
225
+ nextElement = nextElement.nextElementSibling;
226
+ }
227
+
228
+ terms.push({
229
+ term: termText,
230
+ definition: dds.join('\n')
231
+ });
232
+ });
233
+
234
+ // Store all terms in a JSON file with timestamp
235
+ const timestamp = Date.now();
236
+ const outputDir = path.join(CACHE_DIR);
237
+ if (!fs.existsSync(outputDir)) {
238
+ fs.mkdirSync(outputDir, { recursive: true });
239
+ }
240
+
241
+ // Create output filename with timestamp
242
+ const outputFileName = `${timestamp}-${owner}-${repo}-terms.json`;
243
+ const outputFilePath = path.join(outputDir, outputFileName);
244
+
245
+ // Create the result object
246
+ const result = {
247
+ timestamp,
248
+ repository: `${owner}/${repo}`,
249
+ terms,
250
+ sha: commitHash, // Use the commit hash of the index.html file
251
+ avatarUrl: null,
252
+ outputFileName
253
+ };
254
+
255
+ // Save all terms to file
256
+ fs.writeFileSync(outputFilePath, JSON.stringify(result, null, 2));
257
+ console.log(`✅ Saved ${terms.length} terms to ${outputFilePath}`);
258
+
259
+ // Save to cache if enabled
260
+ if (options.cache !== false) {
261
+ saveToCache(cacheKey, result);
262
+ }
263
+
264
+ return result;
265
+
266
+ } catch (error) {
267
+ if (error.response) {
268
+ if (error.response.status === 404) {
269
+ console.log(`❌ Resource not found: ${error.config.url}`);
270
+ } else if (error.response.status === 403 && error.response.headers['x-ratelimit-remaining'] === '0') {
271
+ const resetTime = new Date(parseInt(error.response.headers['x-ratelimit-reset']) * 1000);
272
+ console.error(`❌ GitHub API rate limit exceeded. Try again after ${resetTime.toLocaleString()}`);
273
+ } else {
274
+ console.error(`❌ Error fetching data: ${error.response.status} ${error.response.statusText}`);
275
+ }
276
+ } else {
277
+ console.error(`❌ Error fetching term: ${error.message}`);
278
+ }
279
+ return null;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Fetches a specific term from repository's index.html
285
+ * This is a wrapper that uses fetchAllTermsFromIndex for efficiency
286
+ * @param {string} token - GitHub API Token
287
+ * @param {string} term - The specific term to look for
288
+ * @param {string} owner - Repository owner
289
+ * @param {string} repo - Repository name
290
+ * @param {string} termsDir - Directory containing term definitions (not used in this implementation)
291
+ * @param {object} options - Additional options
292
+ * @returns {object|null} - Found term data or null if not found
293
+ */
294
+ async function fetchTermsFromIndex(token, term, owner, repo, termsDir, options = {}) {
295
+ // First get all terms from the repository (which is cached)
296
+ const allTermsData = await fetchAllTermsFromIndex(token, owner, repo, options);
297
+
298
+ if (!allTermsData || !allTermsData.terms) {
299
+ return null;
300
+ }
301
+
302
+ // Find the specific term
303
+ const foundTerm = allTermsData.terms.find(t => t.term.toLowerCase() === term.toLowerCase());
304
+
305
+ if (foundTerm) {
306
+ console.log(`Found term '${term}' in repository ${owner}/${repo}`);
307
+ return {
308
+ term: foundTerm.term,
309
+ content: foundTerm.definition,
310
+ sha: allTermsData.sha,
311
+ repository: {
312
+ owner: {
313
+ login: owner,
314
+ avatar_url: allTermsData.avatarUrl
315
+ },
316
+ name: repo
317
+ }
318
+ };
319
+ } else {
320
+ console.log(`❌ Term "${term}" not found in repository ${owner}/${repo}`);
321
+ return null;
322
+ }
323
+ }
324
+
325
+ module.exports = {
326
+ fetchTermsFromIndex,
327
+ fetchAllTermsFromIndex // Export the function to fetch all terms as well
328
+ };
@@ -1,14 +1,14 @@
1
1
  const fs = require('fs');
2
- const { fetchTermsFromGitHubRepository } = require('./fetchTermsFromGitHubRepository.js');
2
+ const { fetchTermsFromIndex, fetchAllTermsFromIndex } = require('./fetchTermsFromIndex.js');
3
3
  const { matchTerm } = require('./matchTerm.js');
4
4
  const { addPath, getPath, getAllPaths } = require('../config/paths');
5
+ const path = require('path');
5
6
 
6
7
  // Directory to store cached files
7
8
  const CACHE_DIR = getPath('githubcache');
8
9
 
9
10
  async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, outputPathJS, outputPathJSTimeStamped, options) {
10
11
  try {
11
-
12
12
  // Clear the cache (remove the cache directory) if the cache option is set to false
13
13
  if (options.cache === false) {
14
14
  if (fs.existsSync(CACHE_DIR)) {
@@ -20,24 +20,78 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
20
20
  if (!fs.existsSync(CACHE_DIR)) {
21
21
  fs.mkdirSync(CACHE_DIR, { recursive: true });
22
22
  }
23
+
24
+ // Filter out incomplete xtrefs that don't have proper repository information
25
+ allXTrefs.xtrefs = allXTrefs.xtrefs.filter(xtref => {
26
+ if (!xtref.owner || !xtref.repo || !xtref.repoUrl) {
27
+ console.log(`⚠️ Removing incomplete reference: ${xtref.externalSpec}, ${xtref.term}`);
28
+ return false;
29
+ }
30
+ return true;
31
+ });
32
+
33
+ // Group xtrefs by repository to avoid multiple downloads of the same index.html
34
+ const xrefsByRepo = allXTrefs.xtrefs.reduce((groups, xtref) => {
35
+ const repoKey = `${xtref.owner}/${xtref.repo}`;
36
+ if (!groups[repoKey]) {
37
+ groups[repoKey] = {
38
+ owner: xtref.owner,
39
+ repo: xtref.repo,
40
+ xtrefs: []
41
+ };
42
+ }
43
+ groups[repoKey].xtrefs.push(xtref);
44
+ return groups;
45
+ }, {});
23
46
 
24
- for (let xtref of allXTrefs.xtrefs) {
25
- // Go and look if the term is in the external repository and if so, get the commit hash, and other meta info plus the content of the file
26
- const item = await fetchTermsFromGitHubRepository(GITHUB_API_TOKEN, xtref.term, xtref.owner, xtref.repo, xtref.terms_dir, options);
27
-
28
- // // Check if fetchedData.data is defined
29
- if (item !== null && matchTerm(item.content, xtref.term)) {
30
- xtref.commitHash = item.sha;
31
- xtref.content = item.content;
32
- xtref.avatarUrl = item.repository.owner.avatar_url;
33
- console.log(`✅ Match found for term: ${xtref.term} in ${xtref.externalSpec};`);
34
- console.log("============================================\n\n");
35
- } else {
36
- xtref.commitHash = "not found";
37
- xtref.content = "This term was not found in the external repository.";
38
- console.log(`ℹ️ No match found for term: ${xtref.term} in ${xtref.externalSpec};`);
39
- console.log("============================================\n\n");
47
+ console.log(`✅ Grouped ${allXTrefs.xtrefs.length} terms into ${Object.keys(xrefsByRepo).length} repositories`);
48
+
49
+ // Process each repository once
50
+ for (const repoKey of Object.keys(xrefsByRepo)) {
51
+ const repoGroup = xrefsByRepo[repoKey];
52
+ console.log(`Processing repository: ${repoKey} (${repoGroup.xtrefs.length} terms)`);
53
+
54
+ // First, fetch all terms from this repository
55
+ const allTermsData = await fetchAllTermsFromIndex(
56
+ GITHUB_API_TOKEN,
57
+ repoGroup.owner,
58
+ repoGroup.repo,
59
+ options
60
+ );
61
+
62
+ if (!allTermsData) {
63
+ console.log(`❌ Could not fetch terms from repository ${repoKey}`);
64
+ // Mark all terms from this repo as not found
65
+ repoGroup.xtrefs.forEach(xtref => {
66
+ xtref.commitHash = "not found";
67
+ xtref.content = "This term was not found in the external repository.";
68
+ xtref.avatarUrl = null;
69
+ });
70
+ continue; // Skip to next repository
71
+ }
72
+
73
+ // Now process each term in this repository
74
+ for (const xtref of repoGroup.xtrefs) {
75
+ // Find the term in the pre-fetched data
76
+ const foundTerm = allTermsData.terms.find(
77
+ t => t.term.toLowerCase() === xtref.term.toLowerCase()
78
+ );
79
+
80
+ if (foundTerm) {
81
+ xtref.commitHash = allTermsData.sha;
82
+ xtref.content = foundTerm.definition;
83
+ xtref.avatarUrl = allTermsData.avatarUrl;
84
+ console.log(`✅ Match found for term: ${xtref.term} in ${xtref.externalSpec}`);
85
+ } else {
86
+ xtref.commitHash = "not found";
87
+ xtref.content = "This term was not found in the external repository.";
88
+ xtref.avatarUrl = null;
89
+ console.log(`ℹ️ No match found for term: ${xtref.term} in ${xtref.externalSpec}`);
90
+ }
40
91
  }
92
+
93
+ console.log(`✅ Finished processing repository: ${repoKey}`);
94
+ console.log("============================================\n\n");
41
95
  }
42
96
 
43
97
  const allXTrefsStr = JSON.stringify(allXTrefs, null, 2);
@@ -46,6 +100,7 @@ async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, ou
46
100
  fs.writeFileSync(outputPathJS, stringReadyForFileWrite, 'utf8');
47
101
  fs.writeFileSync(outputPathJSTimeStamped, stringReadyForFileWrite, 'utf8');
48
102
 
103
+ // This will run index.js
49
104
  require('../../index.js')({ nowatch: true });
50
105
  } catch (error) {
51
106
  console.error("An error occurred:", error);