spec-up-t 1.1.54 → 1.2.0
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 +59 -8
- package/assets/compiled/head.css +13 -12
- package/assets/css/adjust-font-size.css +11 -0
- package/assets/css/collapse-meta-info.css +27 -8
- package/assets/css/create-term-filter.css +11 -0
- package/assets/css/index.css +15 -0
- package/assets/css/prism.css +176 -153
- package/assets/css/prism.dark.css +157 -0
- package/assets/css/prism.default.css +180 -0
- package/assets/css/terms-and-definitions.1.css +223 -0
- package/assets/css/terms-and-definitions.css +214 -100
- package/assets/js/addAnchorsToTerms.js +1 -1
- package/assets/js/collapse-definitions.js +2 -1
- package/assets/js/collapse-meta-info.js +25 -11
- package/assets/js/create-term-filter.js +61 -0
- package/assets/js/horizontal-scroll-hint.js +159 -0
- package/assets/js/index.1.js +137 -0
- package/assets/js/index.js +2 -1
- package/assets/js/insert-trefs.js +122 -116
- package/assets/js/insert-xrefs.1.js +372 -0
- package/assets/js/{show-commit-hashes.js → insert-xrefs.js} +67 -7
- package/assets/js/prism.dark.js +24 -0
- package/assets/js/prism.default.js +23 -0
- package/assets/js/prism.js +4 -5
- package/assets/js/search.js +1 -1
- package/assets/js/toggle-dense-info.js +40 -0
- package/branches.md +4 -29
- package/index.js +397 -189
- package/index.new.js +662 -0
- package/package.json +1 -1
- package/src/asset-map.json +9 -5
- package/src/collect-external-references.js +16 -9
- package/src/collectExternalReferences/fetchTermsFromIndex.js +328 -0
- package/src/collectExternalReferences/processXTrefsData.js +73 -18
- package/src/create-pdf.js +385 -89
- package/src/health-check/external-specs-checker.js +207 -0
- package/src/health-check/output-gitignore-checker.js +261 -0
- package/src/health-check/specs-configuration-checker.js +274 -0
- package/src/health-check/term-references-checker.js +191 -0
- package/src/health-check/terms-intro-checker.js +81 -0
- package/src/health-check/tref-term-checker.js +463 -0
- package/src/health-check.js +445 -0
- package/src/install-from-boilerplate/config-scripts-keys.js +2 -0
- package/src/install-from-boilerplate/menu.sh +6 -3
- package/src/markdown-it-extensions.js +134 -103
- package/src/prepare-tref.js +61 -24
- package/src/utils/fetch.js +100 -0
- package/templates/template.html +12 -7
- package/assets/js/css-helper.js +0 -30
- package/src/collectExternalReferences/fetchTermsFromGitHubRepository.js +0 -232
- package/src/collectExternalReferences/fetchTermsFromGitHubRepository.test.js +0 -385
- package/src/collectExternalReferences/octokitClient.js +0 -96
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if specs.json has been configured from default
|
|
6
|
+
* @param {string} projectRoot - Root directory of the project
|
|
7
|
+
* @returns {Promise<Array>} - Array of check results
|
|
8
|
+
*/
|
|
9
|
+
async function checkSpecsJsonConfiguration(projectRoot) {
|
|
10
|
+
const results = [];
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
// Path to the project's specs.json
|
|
14
|
+
const projectSpecsPath = path.join(projectRoot, 'specs.json');
|
|
15
|
+
|
|
16
|
+
// Path to the default boilerplate specs.json
|
|
17
|
+
const defaultSpecsPath = path.join(
|
|
18
|
+
__dirname,
|
|
19
|
+
'..',
|
|
20
|
+
'install-from-boilerplate',
|
|
21
|
+
'boilerplate',
|
|
22
|
+
'specs.json'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Check if project specs.json exists
|
|
26
|
+
if (!fs.existsSync(projectSpecsPath)) {
|
|
27
|
+
return [{
|
|
28
|
+
name: 'Find specs.json file',
|
|
29
|
+
success: false,
|
|
30
|
+
details: 'specs.json file not found in project root'
|
|
31
|
+
}];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if default specs.json exists
|
|
35
|
+
if (!fs.existsSync(defaultSpecsPath)) {
|
|
36
|
+
return [{
|
|
37
|
+
name: 'Find default specs.json template',
|
|
38
|
+
success: false,
|
|
39
|
+
details: 'Default specs.json template not found'
|
|
40
|
+
}];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Read both files
|
|
44
|
+
const projectSpecs = JSON.parse(fs.readFileSync(projectSpecsPath, 'utf8'));
|
|
45
|
+
const defaultSpecs = JSON.parse(fs.readFileSync(defaultSpecsPath, 'utf8'));
|
|
46
|
+
|
|
47
|
+
// Compare key parts to see if the file has been configured
|
|
48
|
+
results.push({
|
|
49
|
+
name: 'specs.json exists',
|
|
50
|
+
success: true,
|
|
51
|
+
details: 'Project specs.json file found'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Dynamically extract field definitions from the default specs.json
|
|
55
|
+
const fieldDescriptions = {
|
|
56
|
+
'title': 'Specification title',
|
|
57
|
+
'description': 'Specification description',
|
|
58
|
+
'author': 'Specification author',
|
|
59
|
+
'source': 'Source repository information',
|
|
60
|
+
'spec_directory': 'Directory containing specification content',
|
|
61
|
+
'spec_terms_directory': 'Directory containing term definitions',
|
|
62
|
+
'output_path': 'Output directory for generated files',
|
|
63
|
+
'markdown_paths': 'List of markdown files to include in the specification',
|
|
64
|
+
'logo': 'Logo URL',
|
|
65
|
+
'logo_link': 'Link to the logo',
|
|
66
|
+
'favicon': 'Favicon URL',
|
|
67
|
+
'external_specs': 'External specifications',
|
|
68
|
+
'katex': 'KaTeX math rendering configuration'
|
|
69
|
+
// Add more descriptions as needed, or create a more sophisticated lookup
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Define required and optional fields based on the default specs.json
|
|
73
|
+
const defaultSpecKeys = Object.keys(defaultSpecs.specs?.[0] || {});
|
|
74
|
+
|
|
75
|
+
// Known optional fields - this could be pulled from documentation if available
|
|
76
|
+
const knownOptionalFields = ['logo', 'external_specs', 'logo_link', 'favicon', 'katex'];
|
|
77
|
+
|
|
78
|
+
// Fields that can remain at their default values without being flagged
|
|
79
|
+
const allowDefaultValueFields = [
|
|
80
|
+
'spec_directory',
|
|
81
|
+
'spec_terms_directory',
|
|
82
|
+
'output_path',
|
|
83
|
+
'katex',
|
|
84
|
+
'logo',
|
|
85
|
+
'logo_link',
|
|
86
|
+
'favicon'
|
|
87
|
+
// Add any other fields that should be allowed to have default values
|
|
88
|
+
// Add any new field here that should be allowed to have default values
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
// Fields that should fail if they're not modified from default values
|
|
92
|
+
const mustChangeFields = [
|
|
93
|
+
'title',
|
|
94
|
+
'description',
|
|
95
|
+
'author',
|
|
96
|
+
'source'
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// Consider all keys in the template as required unless explicitly marked as optional
|
|
100
|
+
const requiredFields = defaultSpecKeys
|
|
101
|
+
.filter(key => !knownOptionalFields.includes(key))
|
|
102
|
+
.map(key => ({
|
|
103
|
+
key,
|
|
104
|
+
description: fieldDescriptions[key] || `${key.replace(/_/g, ' ')} field`,
|
|
105
|
+
allowDefaultValue: allowDefaultValueFields.includes(key),
|
|
106
|
+
mustChange: mustChangeFields.includes(key)
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
const optionalFields = defaultSpecKeys
|
|
110
|
+
.filter(key => knownOptionalFields.includes(key))
|
|
111
|
+
.map(key => ({
|
|
112
|
+
key,
|
|
113
|
+
description: fieldDescriptions[key] || `${key.replace(/_/g, ' ')} field`,
|
|
114
|
+
allowDefaultValue: allowDefaultValueFields.includes(key)
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
// Check each required field exists
|
|
118
|
+
const missingRequiredKeys = [];
|
|
119
|
+
|
|
120
|
+
for (const field of requiredFields) {
|
|
121
|
+
const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
|
|
122
|
+
|
|
123
|
+
if (!hasField) {
|
|
124
|
+
missingRequiredKeys.push(field.key);
|
|
125
|
+
|
|
126
|
+
results.push({
|
|
127
|
+
name: `${field.description} configuration`,
|
|
128
|
+
success: false,
|
|
129
|
+
details: `Required "${field.key}" key is missing in specs.json`
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
// Field exists, check if it's configured
|
|
133
|
+
const projectValue = projectSpecs.specs[0][field.key];
|
|
134
|
+
const defaultValue = defaultSpecs.specs?.[0]?.[field.key];
|
|
135
|
+
let isConfigured = false;
|
|
136
|
+
|
|
137
|
+
if (typeof projectValue === 'object') {
|
|
138
|
+
isConfigured = JSON.stringify(projectValue) !== JSON.stringify(defaultValue);
|
|
139
|
+
} else {
|
|
140
|
+
isConfigured = projectValue !== defaultValue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// For fields that can keep their default values, we'll mark them as configured
|
|
144
|
+
if (field.allowDefaultValue) {
|
|
145
|
+
isConfigured = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Determine if we should show warning or fail for unconfigured fields
|
|
149
|
+
let status = undefined;
|
|
150
|
+
let success = true;
|
|
151
|
+
|
|
152
|
+
if (!isConfigured) {
|
|
153
|
+
if (field.mustChange) {
|
|
154
|
+
// Must-change fields should fail if not configured
|
|
155
|
+
status = undefined; // No status means it shows as failure
|
|
156
|
+
success = false;
|
|
157
|
+
} else {
|
|
158
|
+
// Other fields should show a warning
|
|
159
|
+
status = 'warning';
|
|
160
|
+
success = true; // Still technically passes with a warning
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
results.push({
|
|
165
|
+
name: `${field.description} configuration`,
|
|
166
|
+
status: status,
|
|
167
|
+
success: success,
|
|
168
|
+
details: isConfigured
|
|
169
|
+
? (projectValue === defaultValue && field.allowDefaultValue
|
|
170
|
+
? `Default value for ${field.description} is acceptable`
|
|
171
|
+
: `${field.description} has been changed from default`)
|
|
172
|
+
: `${field.description} is still set to default value${field.key === 'title' || field.key === 'author' ? `: "${defaultValue}"` : ''}`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check optional fields
|
|
178
|
+
for (const field of optionalFields) {
|
|
179
|
+
const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
|
|
180
|
+
|
|
181
|
+
if (hasField) {
|
|
182
|
+
const projectValue = projectSpecs.specs[0][field.key];
|
|
183
|
+
const defaultValue = defaultSpecs.specs?.[0]?.[field.key];
|
|
184
|
+
let isConfigured = false;
|
|
185
|
+
|
|
186
|
+
if (typeof projectValue === 'object') {
|
|
187
|
+
isConfigured = JSON.stringify(projectValue) !== JSON.stringify(defaultValue);
|
|
188
|
+
} else {
|
|
189
|
+
isConfigured = projectValue !== defaultValue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// For optional fields that can keep their default values, we'll mark them as configured
|
|
193
|
+
if (field.allowDefaultValue) {
|
|
194
|
+
isConfigured = true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
results.push({
|
|
198
|
+
name: `${field.description} configuration`,
|
|
199
|
+
status: isConfigured ? undefined : 'warning',
|
|
200
|
+
success: isConfigured || !isConfigured, // Always true for backward compatibility when using warning
|
|
201
|
+
details: isConfigured
|
|
202
|
+
? (projectValue === defaultValue && field.allowDefaultValue
|
|
203
|
+
? `Default value for ${field.description} is acceptable`
|
|
204
|
+
: `${field.description} has been changed from default`)
|
|
205
|
+
: `${field.description} is still set to default value`
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
results.push({
|
|
209
|
+
name: `${field.description} configuration`,
|
|
210
|
+
success: true,
|
|
211
|
+
details: `Optional "${field.key}" key is not present (this is not required)`
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Add a summary of missing required fields
|
|
217
|
+
if (missingRequiredKeys.length > 0) {
|
|
218
|
+
results.push({
|
|
219
|
+
name: 'Required fields check',
|
|
220
|
+
success: false,
|
|
221
|
+
details: `Missing required fields: ${missingRequiredKeys.join(', ')}`
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
results.push({
|
|
225
|
+
name: 'Required fields check',
|
|
226
|
+
success: true,
|
|
227
|
+
details: 'All required fields are present'
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check if any fields exist that aren't in the standard template (could be typos)
|
|
232
|
+
const allStandardKeys = [...requiredFields, ...optionalFields].map(f => f.key);
|
|
233
|
+
const unexpectedKeys = Object.keys(projectSpecs.specs?.[0] || {})
|
|
234
|
+
.filter(key => !allStandardKeys.includes(key));
|
|
235
|
+
|
|
236
|
+
if (unexpectedKeys.length > 0) {
|
|
237
|
+
results.push({
|
|
238
|
+
name: 'Unexpected fields check',
|
|
239
|
+
success: false,
|
|
240
|
+
details: `Found unexpected fields that might be typos: ${unexpectedKeys.join(', ')}`
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Overall configuration status
|
|
245
|
+
// Count all fields that are present and configured
|
|
246
|
+
const fieldResults = results.filter(r =>
|
|
247
|
+
r.name.includes('configuration') &&
|
|
248
|
+
!r.name.includes('Overall')
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const configuredItemsCount = fieldResults.filter(r => r.success).length;
|
|
252
|
+
const totalItems = fieldResults.length;
|
|
253
|
+
const configurationPercentage = Math.round((configuredItemsCount / totalItems) * 100);
|
|
254
|
+
|
|
255
|
+
results.push({
|
|
256
|
+
name: 'Overall configuration status',
|
|
257
|
+
success: configurationPercentage > 50 && missingRequiredKeys.length === 0,
|
|
258
|
+
details: `${configurationPercentage}% of specs.json has been configured (${configuredItemsCount}/${totalItems} items)`
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return results;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error('Error checking specs.json configuration:', error);
|
|
264
|
+
return [{
|
|
265
|
+
name: 'specs.json configuration check',
|
|
266
|
+
success: false,
|
|
267
|
+
details: `Error: ${error.message}`
|
|
268
|
+
}];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
checkSpecsJsonConfiguration
|
|
274
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the spec name from a tref tag at the beginning of a markdown file
|
|
6
|
+
* @param {string} firstLine - The first line of a markdown file
|
|
7
|
+
* @returns {string|null} - The extracted spec name or null if not found
|
|
8
|
+
*/
|
|
9
|
+
function extractSpecNameFromTref(firstLine) {
|
|
10
|
+
if (!firstLine.includes('[[tref:')) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Extract content between [[tref: and the next comma
|
|
16
|
+
const match = firstLine.match(/\[\[tref:([^,]+)/);
|
|
17
|
+
if (match && match[1]) {
|
|
18
|
+
// Trim whitespace
|
|
19
|
+
return match[1].trim();
|
|
20
|
+
}
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error('Error extracting spec name from tref:', error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if all markdown files in spec terms directories have valid tref references
|
|
30
|
+
* @param {string} projectRoot - Root directory of the project
|
|
31
|
+
* @returns {Promise<Array>} - Array of check results
|
|
32
|
+
*/
|
|
33
|
+
async function checkTermReferences(projectRoot) {
|
|
34
|
+
const results = [];
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const specsPath = path.join(projectRoot, 'specs.json');
|
|
38
|
+
|
|
39
|
+
// Check if specs.json exists
|
|
40
|
+
if (!fs.existsSync(specsPath)) {
|
|
41
|
+
return [{
|
|
42
|
+
name: 'Find specs.json file',
|
|
43
|
+
success: false,
|
|
44
|
+
details: 'specs.json file not found in project root'
|
|
45
|
+
}];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Read specs.json
|
|
49
|
+
const specsContent = fs.readFileSync(specsPath, 'utf8');
|
|
50
|
+
const specs = JSON.parse(specsContent);
|
|
51
|
+
|
|
52
|
+
// Find all external specs
|
|
53
|
+
const externalSpecs = [];
|
|
54
|
+
let allSpecDirectories = [];
|
|
55
|
+
|
|
56
|
+
if (specs.specs && Array.isArray(specs.specs)) {
|
|
57
|
+
// Collect all external specs
|
|
58
|
+
specs.specs.forEach(spec => {
|
|
59
|
+
if (spec.external_specs && Array.isArray(spec.external_specs)) {
|
|
60
|
+
spec.external_specs.forEach(extSpec => {
|
|
61
|
+
if (extSpec.external_spec) {
|
|
62
|
+
externalSpecs.push(extSpec.external_spec);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Collect term directories
|
|
68
|
+
if (spec.spec_directory && spec.spec_terms_directory) {
|
|
69
|
+
const termsDir = path.join(
|
|
70
|
+
projectRoot,
|
|
71
|
+
spec.spec_directory,
|
|
72
|
+
spec.spec_terms_directory
|
|
73
|
+
);
|
|
74
|
+
allSpecDirectories.push(termsDir);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (externalSpecs.length === 0) {
|
|
80
|
+
results.push({
|
|
81
|
+
name: 'Find external specs',
|
|
82
|
+
success: false,
|
|
83
|
+
details: 'No external_spec entries found in specs.json'
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
results.push({
|
|
87
|
+
name: 'Find external specs',
|
|
88
|
+
success: true,
|
|
89
|
+
details: `Found ${externalSpecs.length} external specs: ${externalSpecs.join(', ')}`
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (allSpecDirectories.length === 0) {
|
|
94
|
+
results.push({
|
|
95
|
+
name: 'Find spec terms directories',
|
|
96
|
+
success: false,
|
|
97
|
+
details: 'No spec_directory/spec_terms_directory entries found in specs.json'
|
|
98
|
+
});
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
results.push({
|
|
103
|
+
name: 'Find spec terms directories',
|
|
104
|
+
success: true,
|
|
105
|
+
details: `Found ${allSpecDirectories.length} spec terms directories`
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Process all markdown files in all terms directories
|
|
109
|
+
for (const termsDir of allSpecDirectories) {
|
|
110
|
+
if (!fs.existsSync(termsDir)) {
|
|
111
|
+
results.push({
|
|
112
|
+
name: `Check terms directory: ${termsDir}`,
|
|
113
|
+
success: false,
|
|
114
|
+
details: `Terms directory does not exist: ${termsDir}`
|
|
115
|
+
});
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Find all markdown files in the terms directory
|
|
120
|
+
const markdownFiles = fs.readdirSync(termsDir)
|
|
121
|
+
.filter(file => path.extname(file) === '.md')
|
|
122
|
+
.map(file => path.join(termsDir, file));
|
|
123
|
+
|
|
124
|
+
if (markdownFiles.length === 0) {
|
|
125
|
+
results.push({
|
|
126
|
+
name: `Find markdown files in <code>${termsDir}</code>`,
|
|
127
|
+
success: false,
|
|
128
|
+
details: `No markdown files found in terms directory: ${termsDir}`
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
results.push({
|
|
134
|
+
name: `Find markdown files in <code>${termsDir}</code>`,
|
|
135
|
+
success: true,
|
|
136
|
+
details: `Found ${markdownFiles.length} markdown files`
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Check each markdown file
|
|
140
|
+
for (const mdFile of markdownFiles) {
|
|
141
|
+
try {
|
|
142
|
+
const content = fs.readFileSync(mdFile, 'utf8');
|
|
143
|
+
const firstLine = content.split('\n')[0];
|
|
144
|
+
|
|
145
|
+
if (!firstLine.includes('[[tref:')) {
|
|
146
|
+
// Skip this file as it doesn't contain a tref tag in the first line
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const specName = extractSpecNameFromTref(firstLine);
|
|
151
|
+
if (!specName) {
|
|
152
|
+
results.push({
|
|
153
|
+
name: `Check tref in ${path.basename(mdFile)}`,
|
|
154
|
+
success: false,
|
|
155
|
+
details: `Could not extract spec name from tref tag in first line: "${firstLine}"`
|
|
156
|
+
});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const isValid = externalSpecs.includes(specName);
|
|
161
|
+
results.push({
|
|
162
|
+
name: `Check tref spec "${specName}" in <code>${path.basename(mdFile)}</code>`,
|
|
163
|
+
success: isValid,
|
|
164
|
+
details: isValid
|
|
165
|
+
? `Valid external spec reference: ${specName}`
|
|
166
|
+
: `Invalid external spec reference: "${specName}" is not defined in external_specs`
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
results.push({
|
|
170
|
+
name: `Check file ${path.basename(mdFile)}`,
|
|
171
|
+
success: false,
|
|
172
|
+
details: `Error reading or processing file: ${error.message}`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return results;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('Error checking term references:', error);
|
|
181
|
+
return [{
|
|
182
|
+
name: 'Term references check',
|
|
183
|
+
success: false,
|
|
184
|
+
details: `Error: ${error.message}`
|
|
185
|
+
}];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
checkTermReferences
|
|
191
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if the terms-and-definitions-intro.md file exists in the spec directory
|
|
6
|
+
* @param {string} projectRoot - Root directory of the project
|
|
7
|
+
* @returns {Promise<Array>} - Array of check results
|
|
8
|
+
*/
|
|
9
|
+
async function checkTermsIntroFile(projectRoot) {
|
|
10
|
+
const results = [];
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
// Path to the project's specs.json
|
|
14
|
+
const specsPath = path.join(projectRoot, 'specs.json');
|
|
15
|
+
|
|
16
|
+
// Check if specs.json exists
|
|
17
|
+
if (!fs.existsSync(specsPath)) {
|
|
18
|
+
return [{
|
|
19
|
+
name: 'Find specs.json file',
|
|
20
|
+
success: false,
|
|
21
|
+
details: 'specs.json file not found in project root'
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
results.push({
|
|
26
|
+
name: 'Find specs.json file',
|
|
27
|
+
success: true,
|
|
28
|
+
details: 'specs.json file found'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Read specs.json to get the spec directory
|
|
32
|
+
const specsContent = fs.readFileSync(specsPath, 'utf8');
|
|
33
|
+
const specs = JSON.parse(specsContent);
|
|
34
|
+
|
|
35
|
+
// Get the spec_directory value
|
|
36
|
+
const specDir = specs.specs?.[0]?.spec_directory;
|
|
37
|
+
|
|
38
|
+
if (!specDir) {
|
|
39
|
+
results.push({
|
|
40
|
+
name: 'Find spec_directory field',
|
|
41
|
+
success: false,
|
|
42
|
+
details: 'spec_directory field not found in specs.json'
|
|
43
|
+
});
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
results.push({
|
|
48
|
+
name: 'Find spec_directory field',
|
|
49
|
+
success: true,
|
|
50
|
+
details: `spec_directory field found: "${specDir}"`
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Build the path to the terms-and-definitions-intro.md file
|
|
54
|
+
const specDirPath = path.resolve(projectRoot, specDir);
|
|
55
|
+
const termsIntroPath = path.join(specDirPath, 'terms-and-definitions-intro.md');
|
|
56
|
+
|
|
57
|
+
// Check if the terms-and-definitions-intro.md file exists
|
|
58
|
+
const termsIntroExists = fs.existsSync(termsIntroPath);
|
|
59
|
+
|
|
60
|
+
results.push({
|
|
61
|
+
name: 'Find terms-and-definitions-intro.md file',
|
|
62
|
+
success: termsIntroExists,
|
|
63
|
+
details: termsIntroExists
|
|
64
|
+
? 'terms-and-definitions-intro.md file found in spec directory'
|
|
65
|
+
: `terms-and-definitions-intro.md file not found in ${specDirPath}`
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return results;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Error checking terms-and-definitions-intro.md file:', error);
|
|
71
|
+
return [{
|
|
72
|
+
name: 'Terms intro file check',
|
|
73
|
+
success: false,
|
|
74
|
+
details: `Error: ${error.message}`
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
checkTermsIntroFile
|
|
81
|
+
};
|