spec-up-t 1.0.71 → 1.0.73
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/get-xrefs-data.js +143 -77
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spec-up-t",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.73",
|
|
4
4
|
"description": "Technical specification drafting tool that generates rich specification documents from markdown. Forked from https://github.com/decentralized-identity/spec-up by Daniel Buchner (https://github.com/csuwildcat)",
|
|
5
5
|
"main": "./index",
|
|
6
6
|
"repository": {
|
package/src/get-xrefs-data.js
CHANGED
|
@@ -29,62 +29,84 @@ const outputPathJSON = 'output/xrefs-data.json';
|
|
|
29
29
|
const outputPathJS = 'output/xrefs-data.js';
|
|
30
30
|
const outputPathJSTimeStamped = 'output/xrefs-history/xrefs-data-' + Date.now() + '.js';
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
function addAllXrefs(GITHUB_API_TOKEN) {
|
|
34
|
-
// Set headers for GitHub API requests. Include an authorization token if provided.
|
|
32
|
+
function setupFetchHeaders(GITHUB_API_TOKEN) {
|
|
35
33
|
const fetchHeaders = {
|
|
36
34
|
'Accept': 'application/vnd.github.v3+json'
|
|
37
35
|
};
|
|
38
36
|
|
|
39
|
-
// If GITHUB_API_TOKEN is present, it adds an Authorization header to the fetchHeaders object with the value set to token ${GITHUB_API_TOKEN}. This header is used to authenticate requests to the GitHub API, allowing the user to make authenticated requests which have higher rate limits compared to unauthenticated requests.
|
|
40
37
|
if (GITHUB_API_TOKEN) {
|
|
41
38
|
fetchHeaders['Authorization'] = `token ${GITHUB_API_TOKEN}`;
|
|
42
39
|
} else {
|
|
43
40
|
console.log('\n SPEC-UP-T: There is no GitHub token set up. Therefore, you are more likely to be at your limit of GitHub API requests. If you run into the limit, create a token and search the documentation on this topic.\n');
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
return fetchHeaders;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Function to check the rate limit of the GitHub API
|
|
47
|
+
function checkRateLimit(response) {
|
|
48
|
+
if (response.status === 403 && response.headers.get('X-RateLimit-Remaining') === '0') {
|
|
49
|
+
const resetTime = new Date(response.headers.get('X-RateLimit-Reset') * 1000);
|
|
50
|
+
console.error(`\n SPEC-UP-T: Github API rate limit exceeded. Try again after ${resetTime}. See https://blockchainbird.github.io/spec-up-t-website/docs/github-token/ for more info.` + "\n");
|
|
51
|
+
return true;
|
|
52
|
+
} else {
|
|
53
|
+
console.log(`\n SPEC-UP-T: Github API rate limit: ${response.headers.get('X-RateLimit-Remaining')} requests remaining. See https://blockchainbird.github.io/spec-up-t-website/docs/github-token/ for more info.` + "\n");
|
|
56
54
|
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
// Function to fetch term information from GitHub, including commit hash and content.
|
|
59
|
+
async function fetchTermInfoFromGithub(GITHUB_API_TOKEN, xref) {
|
|
60
|
+
try {
|
|
61
|
+
// prerequisite: filename should be the term in the match object with spaces replaced by dashes and all lowercase
|
|
62
|
+
//TODO: Loop through all markdown files to find the term and get the filename, instead of assuming that the filename is the term with spaces replaced by dashes and all lowercase
|
|
63
|
+
const url = `https://api.github.com/repos/${xref.owner}/${xref.repo}/commits?path=${xref.terms_dir}/${xref.term.replace(/ /g, '-').toLowerCase()}.md&per_page=1`;
|
|
64
|
+
const response = await fetch(url, { headers: setupFetchHeaders(GITHUB_API_TOKEN) });
|
|
65
|
+
|
|
66
|
+
// Check for rate limit before proceeding
|
|
67
|
+
if (checkRateLimit(response)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (response.ok) {
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
if (data.length > 0) {
|
|
74
|
+
const commitHash = data[0].sha;
|
|
75
|
+
const content = await fetchFileContentFromCommit(GITHUB_API_TOKEN, xref.owner, xref.repo, commitHash, `${xref.terms_dir}/${xref.term.replace(/ /g, '-').toLowerCase()}.md`);
|
|
76
|
+
return { commitHash, content };
|
|
69
77
|
}
|
|
78
|
+
} else {
|
|
79
|
+
console.error(`\n SPEC-UP-T: Failed to fetch commit hash for ${xref.term}: ${response.statusText}\n`);
|
|
80
|
+
return { commitHash: null, content: null };
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`\n SPEC-UP-T: Error fetching data for term ${xref.term}: ${error.message}\n`);
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
// Function to fetch the content of a file from a specific commit in a GitHub repository.
|
|
89
|
+
async function fetchFileContentFromCommit(GITHUB_API_TOKEN, owner, repo, commitHash, filePath) {
|
|
90
|
+
try {
|
|
91
|
+
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${commitHash}?recursive=1`;
|
|
92
|
+
const treeResponse = await fetch(treeUrl, { headers: setupFetchHeaders(GITHUB_API_TOKEN) });
|
|
93
|
+
|
|
94
|
+
if (treeResponse.ok) {
|
|
95
|
+
const treeData = await treeResponse.json();
|
|
96
|
+
const file = treeData.tree.find(item => item.path === filePath);
|
|
97
|
+
if (file) {
|
|
98
|
+
const fileContentResponse = await fetch(file.url);
|
|
99
|
+
const fileContentData = await fileContentResponse.json();
|
|
100
|
+
return Buffer.from(fileContentData.content, 'base64').toString('utf-8');
|
|
81
101
|
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error(`\n SPEC-UP-T: Error fetching data for term ${xref.term}: ${error.message}\n`);
|
|
84
102
|
}
|
|
85
|
-
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(`\n SPEC-UP-T: Error fetching file content: ${error.message}\n`);
|
|
86
105
|
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
87
108
|
|
|
109
|
+
function updateXrefs(GITHUB_API_TOKEN) {
|
|
88
110
|
// Function to extend xref objects with additional information, such as repository URL and directory information.
|
|
89
111
|
function extendXrefs(config, xrefs) {
|
|
90
112
|
if (config.specs[0].external_specs_repos) {
|
|
@@ -125,56 +147,53 @@ function addAllXrefs(GITHUB_API_TOKEN) {
|
|
|
125
147
|
});
|
|
126
148
|
}
|
|
127
149
|
|
|
128
|
-
//
|
|
129
|
-
async function fetchFileContentFromCommit(owner, repo, commitHash, filePath) {
|
|
130
|
-
try {
|
|
131
|
-
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${commitHash}?recursive=1`;
|
|
132
|
-
const treeResponse = await fetch(treeUrl, { headers: fetchHeaders });
|
|
133
|
-
|
|
134
|
-
if (treeResponse.ok) {
|
|
135
|
-
const treeData = await treeResponse.json();
|
|
136
|
-
const file = treeData.tree.find(item => item.path === filePath);
|
|
137
|
-
if (file) {
|
|
138
|
-
const fileContentResponse = await fetch(file.url);
|
|
139
|
-
const fileContentData = await fileContentResponse.json();
|
|
140
|
-
return Buffer.from(fileContentData.content, 'base64').toString('utf-8');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error(`\n SPEC-UP-T: Error fetching file content: ${error.message}\n`);
|
|
145
|
-
}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Initialize an object to store all xrefs. If the output JSON file exists, load its data.
|
|
150
|
+
// Initialize an object to store all xrefs.
|
|
150
151
|
let allXrefs = { xrefs: [] };
|
|
151
152
|
|
|
153
|
+
// If the output JSON file exists, load its data.
|
|
152
154
|
if (fs.existsSync(outputPathJSON)) {
|
|
153
155
|
const existingXrefs = fs.readJsonSync(outputPathJSON);
|
|
154
156
|
allXrefs = existingXrefs && existingXrefs.xrefs ? existingXrefs : { xrefs: [] };
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
//
|
|
159
|
+
// Function to check if an xref is in the markdown content
|
|
160
|
+
function isXrefInMarkdown(xref, markdownContent) {
|
|
161
|
+
const regex = new RegExp(`\\[\\[xref:${xref.term}\\]\\]`, 'g');
|
|
162
|
+
const result = regex.test(markdownContent);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Collect all markdown content
|
|
167
|
+
let allMarkdownContent = '';
|
|
168
|
+
|
|
158
169
|
specTermsDirectories.forEach(specDirectory => {
|
|
159
170
|
fs.readdirSync(specDirectory).forEach(file => {
|
|
160
171
|
if (file.endsWith('.md')) {
|
|
161
172
|
const markdown = fs.readFileSync(`${specDirectory}/${file}`, 'utf8');
|
|
162
|
-
|
|
163
|
-
if (regex.test(markdown)) {
|
|
164
|
-
const xrefs = markdown.match(regex);
|
|
165
|
-
xrefs.forEach(xref => {
|
|
166
|
-
const newXrefObj = processXref(xref);
|
|
167
|
-
if (!allXrefs.xrefs.some(existingXref =>
|
|
168
|
-
existingXref.term === newXrefObj.term && existingXref.externalSpec === newXrefObj.externalSpec)) {
|
|
169
|
-
allXrefs.xrefs.push(newXrefObj);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
+
allMarkdownContent += markdown;
|
|
173
174
|
}
|
|
174
175
|
});
|
|
175
176
|
});
|
|
176
177
|
|
|
177
|
-
//
|
|
178
|
+
// Remove existing entries if not in the combined markdown content
|
|
179
|
+
allXrefs.xrefs = allXrefs.xrefs.filter(existingXref => {
|
|
180
|
+
return isXrefInMarkdown(existingXref, allMarkdownContent);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Add new entries if they are in the markdown
|
|
184
|
+
const regex = /\[\[xref:.*?\]\]/g;
|
|
185
|
+
if (regex.test(allMarkdownContent)) {
|
|
186
|
+
const xrefs = allMarkdownContent.match(regex);
|
|
187
|
+
xrefs.forEach(xref => {
|
|
188
|
+
const newXrefObj = processXref(xref);
|
|
189
|
+
if (!allXrefs.xrefs.some(existingXref =>
|
|
190
|
+
existingXref.term === newXrefObj.term && existingXref.externalSpec === newXrefObj.externalSpec)) {
|
|
191
|
+
allXrefs.xrefs.push(newXrefObj);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Function to process and clean up xref strings found in the markdown file, returning an object with `externalSpec` and `term` properties.
|
|
178
197
|
function processXref(xref) {
|
|
179
198
|
let [externalSpec, term] = xref.replace(/\[\[xref:/, '').replace(/\]\]/, '').trim().split(/,/, 2);
|
|
180
199
|
return {
|
|
@@ -186,10 +205,16 @@ function addAllXrefs(GITHUB_API_TOKEN) {
|
|
|
186
205
|
// Extend each xref with additional data and fetch commit information from GitHub.
|
|
187
206
|
extendXrefs(config, allXrefs.xrefs);
|
|
188
207
|
|
|
208
|
+
|
|
209
|
+
/*
|
|
210
|
+
Function to fetch all term information from GitHub. The function will not fetch the commit hash again if an entry already contains a commit hash.
|
|
211
|
+
|
|
212
|
+
It checks if the xref object already has a commitHash and content.If both are present, it skips fetching the term information from GitHub. This ensures that existing commit hashes are not overwritten.
|
|
213
|
+
*/
|
|
189
214
|
async function fetchAllTermsInfoFromGithub() {
|
|
190
215
|
for (let xref of allXrefs.xrefs) {
|
|
191
216
|
if (!xref.commitHash || !xref.content) {
|
|
192
|
-
const fetchedData = await fetchTermInfoFromGithub(xref);
|
|
217
|
+
const fetchedData = await fetchTermInfoFromGithub(GITHUB_API_TOKEN, xref);
|
|
193
218
|
if (fetchedData) {
|
|
194
219
|
xref.commitHash = fetchedData.commitHash;
|
|
195
220
|
xref.content = fetchedData.content;
|
|
@@ -260,8 +285,49 @@ function removeXref(term, externalSpec) {
|
|
|
260
285
|
return messages;
|
|
261
286
|
}
|
|
262
287
|
|
|
263
|
-
|
|
288
|
+
function addXref(term, externalSpec) {
|
|
289
|
+
let messages = [];
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
// Read the JSON file
|
|
293
|
+
let currentXrefs = fs.readJsonSync(outputPathJSON);
|
|
294
|
+
|
|
295
|
+
// Check if the term and externalSpec exist
|
|
296
|
+
const entryExists = currentXrefs.xrefs.some(xref => xref.term === term && xref.externalSpec === externalSpec);
|
|
297
|
+
|
|
298
|
+
if (entryExists) {
|
|
299
|
+
messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" already exists.\n`);
|
|
300
|
+
return messages;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Add the entry to the JSON file
|
|
304
|
+
currentXrefs.xrefs.push({ term, externalSpec });
|
|
305
|
+
|
|
306
|
+
// Convert the JSON object back to a JSON string
|
|
307
|
+
const currentXrefsStr = JSON.stringify(currentXrefs, null, 2);
|
|
308
|
+
|
|
309
|
+
// Write the JSON code to a .json file
|
|
310
|
+
fs.writeFileSync(outputPathJSON, currentXrefsStr, 'utf8');
|
|
311
|
+
|
|
312
|
+
// Create the JS code for the assignment
|
|
313
|
+
const stringReadyForFileWrite = `const allXrefs = ${currentXrefsStr};`;
|
|
314
|
+
|
|
315
|
+
// Write the JS code to a .js file
|
|
316
|
+
fs.writeFileSync(outputPathJS, stringReadyForFileWrite, 'utf8');
|
|
317
|
+
|
|
318
|
+
messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" added.\n`);
|
|
319
|
+
|
|
320
|
+
// Run the render function to update the HTML file
|
|
321
|
+
require('../index.js')({ nowatch: true });
|
|
322
|
+
|
|
323
|
+
} catch (error) {
|
|
324
|
+
messages.push(`\n SPEC-UP-T: An error occurred - ${error.message}\n`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Export the updateXrefs and removeXref functions for use in other modules.
|
|
264
329
|
module.exports = {
|
|
265
|
-
|
|
266
|
-
removeXref
|
|
330
|
+
updateXrefs,
|
|
331
|
+
removeXref,
|
|
332
|
+
addXref
|
|
267
333
|
}
|