spec-up-t 1.2.3 → 1.2.4
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/.sonarlint/connectedMode.json +5 -0
- package/assets/compiled/body.js +31 -31
- package/assets/compiled/head.css +5 -5
- package/assets/compiled/head.js +3 -3
- package/assets/css/adjust-font-size.css +6 -11
- package/assets/css/backToTop.css +0 -1
- package/assets/css/index.css +1 -2
- package/assets/css/pdf-styles.css +23 -27
- package/assets/css/repo-issues.css +0 -6
- package/assets/css/search.css +0 -1
- package/assets/css/sidebar-toc.css +13 -12
- package/assets/css/terms-and-definitions.css +43 -37
- package/assets/js/add-href-to-snapshot-link.js +2 -1
- package/assets/js/addAnchorsToTerms.js +0 -1
- package/assets/js/adjust-font-size.js +0 -9
- package/assets/js/create-alphabet-index.js +1 -1
- package/assets/js/custom-elements.js +13 -18
- package/assets/js/declare-markdown-it.js +1 -1
- package/assets/js/highlightMenuItems.js +3 -3
- package/assets/js/index.js +1 -5
- package/assets/js/insert-trefs.js +2 -2
- package/assets/js/modal.js +3 -3
- package/assets/js/search.js +3 -3
- package/assets/js/utils.js +2 -3
- package/index.js +5 -15
- package/package.json +2 -2
- package/src/add-remove-xref-source.js +0 -2
- package/src/collect-external-references.js +187 -179
- package/src/collectExternalReferences/fetchTermsFromIndex.js +2 -1
- package/src/create-external-specs-list.js +1 -1
- package/src/fix-markdown-files.js +152 -90
- package/src/health-check/external-specs-checker.js +173 -94
- package/src/health-check/output-gitignore-checker.js +327 -191
- package/src/health-check/specs-configuration-checker.js +288 -210
- package/src/health-check/term-references-checker.js +200 -123
- package/src/health-check/tref-term-checker.js +264 -179
- package/src/health-check.js +51 -35
- package/src/init.js +0 -3
- package/src/install-from-boilerplate/boilerplate/gitignore +2 -1
- package/src/install-from-boilerplate/config-system-files.js +9 -1
- package/src/install-from-boilerplate/copy-system-files.js +1 -1
- package/src/markdown-it-extensions.js +199 -106
- package/src/references.js +1 -2
- package/src/utils/doesUrlExist.js +7 -5
- package/templates/template.html +1 -2
- package/assets/js/insert-xrefs.js +0 -370
- package/src/create-term-relations.js +0 -131
- package/src/prepare-tref.js +0 -174
|
@@ -1,14 +1,282 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Field descriptions for specs.json keys
|
|
6
|
+
*/
|
|
7
|
+
const fieldDescriptions = {
|
|
8
|
+
'title': 'Specification title',
|
|
9
|
+
'description': 'Specification description',
|
|
10
|
+
'author': 'Specification author',
|
|
11
|
+
'source': 'Source repository information',
|
|
12
|
+
'spec_directory': 'Directory containing specification content',
|
|
13
|
+
'spec_terms_directory': 'Directory containing term definitions',
|
|
14
|
+
'output_path': 'Output directory for generated files',
|
|
15
|
+
'markdown_paths': 'List of markdown files to include in the specification',
|
|
16
|
+
'logo': 'Logo URL',
|
|
17
|
+
'logo_link': 'Link to the logo',
|
|
18
|
+
'favicon': 'Favicon URL',
|
|
19
|
+
'external_specs': 'External specifications',
|
|
20
|
+
'katex': 'KaTeX math rendering configuration'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Fields that can remain at their default values without being flagged
|
|
25
|
+
*/
|
|
26
|
+
const allowDefaultValueFields = [
|
|
27
|
+
'spec_directory',
|
|
28
|
+
'spec_terms_directory',
|
|
29
|
+
'output_path',
|
|
30
|
+
'katex',
|
|
31
|
+
'logo',
|
|
32
|
+
'logo_link',
|
|
33
|
+
'favicon'
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Fields that should fail if they're not modified from default values
|
|
38
|
+
*/
|
|
39
|
+
const mustChangeFields = [
|
|
40
|
+
'title',
|
|
41
|
+
'description',
|
|
42
|
+
'author',
|
|
43
|
+
'source'
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Known optional fields
|
|
48
|
+
*/
|
|
49
|
+
const knownOptionalFields = [
|
|
50
|
+
'logo',
|
|
51
|
+
'external_specs',
|
|
52
|
+
'logo_link',
|
|
53
|
+
'favicon',
|
|
54
|
+
'katex'
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if the files needed for configuration check exist
|
|
59
|
+
* @param {string} projectSpecsPath - Path to project specs.json
|
|
60
|
+
* @param {string} defaultSpecsPath - Path to default specs.json
|
|
61
|
+
* @returns {Array|null} - Check results or null if files exist
|
|
62
|
+
*/
|
|
63
|
+
function checkFilesExist(projectSpecsPath, defaultSpecsPath) {
|
|
64
|
+
if (!fs.existsSync(projectSpecsPath)) {
|
|
65
|
+
return [{
|
|
66
|
+
name: 'Find specs.json file',
|
|
67
|
+
success: false,
|
|
68
|
+
details: 'specs.json file not found in project root'
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(defaultSpecsPath)) {
|
|
73
|
+
return [{
|
|
74
|
+
name: 'Find default specs.json template',
|
|
75
|
+
success: false,
|
|
76
|
+
details: 'Default specs.json template not found'
|
|
77
|
+
}];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Categorize fields into required and optional
|
|
85
|
+
* @param {Array} defaultSpecKeys - Keys from default specs
|
|
86
|
+
* @returns {Object} - Object containing required and optional fields
|
|
87
|
+
*/
|
|
88
|
+
function categorizeFields(defaultSpecKeys) {
|
|
89
|
+
const requiredFields = defaultSpecKeys
|
|
90
|
+
.filter(key => !knownOptionalFields.includes(key))
|
|
91
|
+
.map(key => ({
|
|
92
|
+
key,
|
|
93
|
+
description: fieldDescriptions[key] || `${key.replace(/_/g, ' ')} field`,
|
|
94
|
+
allowDefaultValue: allowDefaultValueFields.includes(key),
|
|
95
|
+
mustChange: mustChangeFields.includes(key)
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const optionalFields = defaultSpecKeys
|
|
99
|
+
.filter(key => knownOptionalFields.includes(key))
|
|
100
|
+
.map(key => ({
|
|
101
|
+
key,
|
|
102
|
+
description: fieldDescriptions[key] || `${key.replace(/_/g, ' ')} field`,
|
|
103
|
+
allowDefaultValue: allowDefaultValueFields.includes(key)
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
return { requiredFields, optionalFields };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if a field value has been configured
|
|
111
|
+
* @param {any} projectValue - Value from project specs
|
|
112
|
+
* @param {any} defaultValue - Value from default specs
|
|
113
|
+
* @returns {boolean} - True if configured
|
|
114
|
+
*/
|
|
115
|
+
function isFieldConfigured(projectValue, defaultValue) {
|
|
116
|
+
if (typeof projectValue === 'object') {
|
|
117
|
+
return JSON.stringify(projectValue) !== JSON.stringify(defaultValue);
|
|
118
|
+
}
|
|
119
|
+
return projectValue !== defaultValue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Evaluate a required field and generate result
|
|
124
|
+
* @param {Object} field - Field definition
|
|
125
|
+
* @param {Object} projectSpecs - Project specs object
|
|
126
|
+
* @param {Object} defaultSpecs - Default specs object
|
|
127
|
+
* @returns {Object} - Check result
|
|
128
|
+
*/
|
|
129
|
+
function evaluateRequiredField(field, projectSpecs, defaultSpecs) {
|
|
130
|
+
const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
|
|
131
|
+
|
|
132
|
+
if (!hasField) {
|
|
133
|
+
return {
|
|
134
|
+
name: `${field.description} configuration`,
|
|
135
|
+
success: false,
|
|
136
|
+
details: `Required "${field.key}" key is missing in specs.json`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const projectValue = projectSpecs.specs[0][field.key];
|
|
141
|
+
const defaultValue = defaultSpecs.specs?.[0]?.[field.key];
|
|
142
|
+
let configured = isFieldConfigured(projectValue, defaultValue);
|
|
143
|
+
|
|
144
|
+
// For fields that can keep their default values, mark as configured
|
|
145
|
+
if (field.allowDefaultValue) {
|
|
146
|
+
configured = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let status;
|
|
150
|
+
let success = true;
|
|
151
|
+
|
|
152
|
+
if (!configured) {
|
|
153
|
+
if (field.mustChange) {
|
|
154
|
+
status = undefined; // No status means it shows as failure
|
|
155
|
+
success = false;
|
|
156
|
+
} else {
|
|
157
|
+
status = 'warning';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let details = '';
|
|
162
|
+
if (configured) {
|
|
163
|
+
details = (projectValue === defaultValue && field.allowDefaultValue)
|
|
164
|
+
? `Default value for ${field.description} is acceptable`
|
|
165
|
+
: `${field.description} has been changed from default`;
|
|
166
|
+
} else {
|
|
167
|
+
details = `${field.description} is still set to default value${['title', 'author'].includes(field.key) ? `: \"${defaultValue}\"` : ''}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
name: `${field.description} configuration`,
|
|
172
|
+
status,
|
|
173
|
+
success,
|
|
174
|
+
details
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Evaluate an optional field and generate result
|
|
180
|
+
* @param {Object} field - Field definition
|
|
181
|
+
* @param {Object} projectSpecs - Project specs object
|
|
182
|
+
* @param {Object} defaultSpecs - Default specs object
|
|
183
|
+
* @returns {Object} - Check result
|
|
184
|
+
*/
|
|
185
|
+
function evaluateOptionalField(field, projectSpecs, defaultSpecs) {
|
|
186
|
+
const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
|
|
187
|
+
|
|
188
|
+
if (!hasField) {
|
|
189
|
+
return {
|
|
190
|
+
name: `${field.description} configuration`,
|
|
191
|
+
success: true,
|
|
192
|
+
details: `Optional "${field.key}" key is not present (this is not required)`
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const projectValue = projectSpecs.specs[0][field.key];
|
|
197
|
+
const defaultValue = defaultSpecs.specs?.[0]?.[field.key];
|
|
198
|
+
let configured = isFieldConfigured(projectValue, defaultValue);
|
|
199
|
+
|
|
200
|
+
if (field.allowDefaultValue) {
|
|
201
|
+
configured = true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let details = '';
|
|
205
|
+
if (configured) {
|
|
206
|
+
details = (projectValue === defaultValue && field.allowDefaultValue)
|
|
207
|
+
? `Default value for ${field.description} is acceptable`
|
|
208
|
+
: `${field.description} has been changed from default`;
|
|
209
|
+
} else {
|
|
210
|
+
details = `${field.description} is still set to default value`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
name: `${field.description} configuration`,
|
|
215
|
+
status: configured ? undefined : 'warning',
|
|
216
|
+
success: true, // Always true for optional fields
|
|
217
|
+
details
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Generate summary results for the configuration check
|
|
223
|
+
* @param {Array} results - Existing check results
|
|
224
|
+
* @param {Array} missingRequiredKeys - List of missing required keys
|
|
225
|
+
* @param {Array} unexpectedKeys - List of unexpected keys
|
|
226
|
+
* @returns {Array} - Additional summary results
|
|
227
|
+
*/
|
|
228
|
+
function generateSummaryResults(results, missingRequiredKeys, unexpectedKeys) {
|
|
229
|
+
const summaryResults = [];
|
|
230
|
+
|
|
231
|
+
// Add a summary of missing required fields
|
|
232
|
+
if (missingRequiredKeys.length > 0) {
|
|
233
|
+
summaryResults.push({
|
|
234
|
+
name: 'Required fields check',
|
|
235
|
+
success: false,
|
|
236
|
+
details: `Missing required fields: ${missingRequiredKeys.join(', ')}`
|
|
237
|
+
});
|
|
238
|
+
} else {
|
|
239
|
+
summaryResults.push({
|
|
240
|
+
name: 'Required fields check',
|
|
241
|
+
success: true,
|
|
242
|
+
details: 'All required fields are present'
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check for unexpected fields
|
|
247
|
+
if (unexpectedKeys.length > 0) {
|
|
248
|
+
summaryResults.push({
|
|
249
|
+
name: 'Unexpected fields check',
|
|
250
|
+
success: false,
|
|
251
|
+
details: `Found unexpected fields that might be typos: ${unexpectedKeys.join(', ')}`
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Overall configuration status
|
|
256
|
+
const fieldResults = results.filter(r =>
|
|
257
|
+
r.name.includes('configuration') &&
|
|
258
|
+
!r.name.includes('Overall')
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
const configuredItemsCount = fieldResults.filter(r => r.success).length;
|
|
262
|
+
const totalItems = fieldResults.length;
|
|
263
|
+
const configurationPercentage = Math.round((configuredItemsCount / totalItems) * 100);
|
|
264
|
+
|
|
265
|
+
summaryResults.push({
|
|
266
|
+
name: 'Overall configuration status',
|
|
267
|
+
success: configurationPercentage > 50 && missingRequiredKeys.length === 0,
|
|
268
|
+
details: `${configurationPercentage}% of specs.json has been configured (${configuredItemsCount}/${totalItems} items)`
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return summaryResults;
|
|
272
|
+
}
|
|
273
|
+
|
|
4
274
|
/**
|
|
5
275
|
* Check if specs.json has been configured from default
|
|
6
276
|
* @param {string} projectRoot - Root directory of the project
|
|
7
277
|
* @returns {Promise<Array>} - Array of check results
|
|
8
278
|
*/
|
|
9
279
|
async function checkSpecsJsonConfiguration(projectRoot) {
|
|
10
|
-
const results = [];
|
|
11
|
-
|
|
12
280
|
try {
|
|
13
281
|
// Path to the project's specs.json
|
|
14
282
|
const projectSpecsPath = path.join(projectRoot, 'specs.json');
|
|
@@ -22,243 +290,53 @@ async function checkSpecsJsonConfiguration(projectRoot) {
|
|
|
22
290
|
'specs.json'
|
|
23
291
|
);
|
|
24
292
|
|
|
25
|
-
// Check if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
}];
|
|
293
|
+
// Check if required files exist
|
|
294
|
+
const fileCheckResults = checkFilesExist(projectSpecsPath, defaultSpecsPath);
|
|
295
|
+
if (fileCheckResults) {
|
|
296
|
+
return fileCheckResults;
|
|
41
297
|
}
|
|
42
298
|
|
|
43
299
|
// Read both files
|
|
44
300
|
const projectSpecs = JSON.parse(fs.readFileSync(projectSpecsPath, 'utf8'));
|
|
45
301
|
const defaultSpecs = JSON.parse(fs.readFileSync(defaultSpecsPath, 'utf8'));
|
|
46
302
|
|
|
47
|
-
|
|
48
|
-
results.push({
|
|
303
|
+
const results = [{
|
|
49
304
|
name: 'specs.json exists',
|
|
50
305
|
success: true,
|
|
51
306
|
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
|
-
};
|
|
307
|
+
}];
|
|
71
308
|
|
|
72
309
|
// Define required and optional fields based on the default specs.json
|
|
73
310
|
const defaultSpecKeys = Object.keys(defaultSpecs.specs?.[0] || {});
|
|
311
|
+
const { requiredFields, optionalFields } = categorizeFields(defaultSpecKeys);
|
|
74
312
|
|
|
75
|
-
//
|
|
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
|
|
313
|
+
// Check each required field
|
|
118
314
|
const missingRequiredKeys = [];
|
|
119
|
-
|
|
315
|
+
|
|
120
316
|
for (const field of requiredFields) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
if (!hasField) {
|
|
317
|
+
const result = evaluateRequiredField(field, projectSpecs, defaultSpecs);
|
|
318
|
+
if (!result.success && result.details.includes('missing')) {
|
|
124
319
|
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
320
|
}
|
|
321
|
+
results.push(result);
|
|
175
322
|
}
|
|
176
323
|
|
|
177
324
|
// Check optional fields
|
|
178
325
|
for (const field of optionalFields) {
|
|
179
|
-
|
|
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
|
-
});
|
|
326
|
+
results.push(evaluateOptionalField(field, projectSpecs, defaultSpecs));
|
|
229
327
|
}
|
|
230
328
|
|
|
231
|
-
// Check
|
|
329
|
+
// Check for unexpected fields
|
|
232
330
|
const allStandardKeys = [...requiredFields, ...optionalFields].map(f => f.key);
|
|
233
331
|
const unexpectedKeys = Object.keys(projectSpecs.specs?.[0] || {})
|
|
234
332
|
.filter(key => !allStandardKeys.includes(key));
|
|
235
333
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
});
|
|
334
|
+
// Add summary results
|
|
335
|
+
const summaryResults = generateSummaryResults(results, missingRequiredKeys, unexpectedKeys);
|
|
336
|
+
results.push(...summaryResults);
|
|
260
337
|
|
|
261
338
|
return results;
|
|
339
|
+
|
|
262
340
|
} catch (error) {
|
|
263
341
|
console.error('Error checking specs.json configuration:', error);
|
|
264
342
|
return [{
|