spec-up-t 1.6.3-beta.1 → 1.6.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.
Files changed (35) hide show
  1. package/.github/copilot-instructions.md +2 -0
  2. package/assets/compiled/body.js +9 -7
  3. package/assets/compiled/head.css +6 -6
  4. package/assets/css/adjust-font-size.css +0 -4
  5. package/assets/css/download-pdf-docx.css +19 -20
  6. package/assets/css/header-navbar.css +0 -4
  7. package/assets/css/index.css +36 -0
  8. package/assets/css/terms-and-definitions.css +0 -1
  9. package/assets/css/validate-external-refs.css +198 -3
  10. package/assets/js/add-href-to-snapshot-link.js +11 -5
  11. package/assets/js/download-pdf-docx.js +20 -9
  12. package/assets/js/edit-term-buttons.js +71 -68
  13. package/assets/js/github-issues.js +27 -27
  14. package/assets/js/insert-irefs.js +1 -1
  15. package/assets/js/validate-external-refs.js +356 -7
  16. package/package.json +3 -3
  17. package/src/add-remove-xref-source.js +0 -5
  18. package/src/collect-external-references.test.js +98 -0
  19. package/src/install-from-boilerplate/boilerplate/gitignore +2 -2
  20. package/src/install-from-boilerplate/boilerplate/menu-wrapper.sh +19 -0
  21. package/src/install-from-boilerplate/boilerplate/specs.json +3 -6
  22. package/src/install-from-boilerplate/config-scripts-keys.js +1 -1
  23. package/src/install-from-boilerplate/config-system-files.js +1 -0
  24. package/src/install-from-boilerplate/custom-update.js +6 -3
  25. package/src/install-from-boilerplate/update-dependencies.js +105 -0
  26. package/src/pipeline/references/collect-external-references.js +12 -0
  27. package/src/pipeline/references/external-references-service.js +1 -1
  28. package/src/pipeline/rendering/render-spec-document.js +5 -1
  29. package/templates/template.html +43 -10
  30. package/src/health-check/destination-gitignore-checker.js +0 -414
  31. package/src/health-check/external-specs-checker.js +0 -287
  32. package/src/health-check/specs-configuration-checker.js +0 -387
  33. package/src/health-check/term-references-checker.js +0 -270
  34. package/src/health-check/terms-intro-checker.js +0 -82
  35. package/src/health-check/tref-term-checker.js +0 -549
@@ -1,287 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const https = require('https');
4
- const url = require('url');
5
- const Logger = require('../utils/logger');
6
-
7
- /**
8
- * Check if a given URL has correct GitHub Pages structure
9
- * @param {string} urlStr - URL to check
10
- * @returns {boolean} - Whether the URL has correct GitHub Pages structure
11
- */
12
- function isValidGitHubPagesUrl(urlStr) {
13
- try {
14
- const parsedUrl = new URL(urlStr);
15
- // GitHub Pages URLs are either username.github.io or github.io/repo-name
16
- return (
17
- parsedUrl.hostname === 'github.io' ||
18
- parsedUrl.hostname.endsWith('.github.io')
19
- );
20
- } catch (error) {
21
- Logger.error(`Error validating GitHub Pages URL: ${error.message}`);
22
- return false;
23
- }
24
- }
25
-
26
- /**
27
- * Check if a given URL has correct GitHub repo structure
28
- * @param {string} urlStr - URL to check
29
- * @returns {boolean} - Whether the URL has correct GitHub repo structure
30
- */
31
- function isValidGitHubRepoUrl(urlStr) {
32
- try {
33
- const parsedUrl = new URL(urlStr);
34
- return (
35
- parsedUrl.hostname === 'github.com' &&
36
- parsedUrl.pathname.split('/').filter(Boolean).length >= 2
37
- );
38
- } catch (error) {
39
- Logger.error(`Error validating GitHub repo URL: ${error.message}`);
40
- return false;
41
- }
42
- }
43
-
44
- /**
45
- * Check if URL exists (returns a valid response)
46
- * @param {string} urlStr - URL to check
47
- * @returns {Promise<boolean>} - Whether the URL exists
48
- */
49
- function urlExists(urlStr) {
50
- return new Promise((resolve) => {
51
- try {
52
- const parsedUrl = new URL(urlStr);
53
- const options = {
54
- method: 'HEAD',
55
- host: parsedUrl.hostname,
56
- path: parsedUrl.pathname + parsedUrl.search,
57
- timeout: 5000
58
- };
59
-
60
- const req = https.request(options, (res) => {
61
- resolve(res.statusCode >= 200 && res.statusCode < 400);
62
- });
63
-
64
- req.on('error', () => {
65
- resolve(false);
66
- });
67
-
68
- req.on('timeout', () => {
69
- req.destroy();
70
- resolve(false);
71
- });
72
-
73
- req.end();
74
- } catch (error) {
75
- Logger.error(`URL Format Error: Invalid URL format for ${urlStr} - ${error.message}`);
76
- resolve(false);
77
- }
78
- });
79
- }
80
-
81
- /**
82
- * Check if specs.json file exists and read it
83
- * @param {string} projectRoot - Root directory of the project
84
- * @returns {Object} - Object containing results and specs data if found
85
- */
86
- function checkAndReadSpecsFile(projectRoot) {
87
- const specsPath = path.join(projectRoot, 'specs.json');
88
-
89
- if (!fs.existsSync(specsPath)) {
90
- return {
91
- results: [{
92
- name: 'Find specs.json file',
93
- success: false,
94
- details: 'specs.json file not found in project root'
95
- }],
96
- specs: null
97
- };
98
- }
99
-
100
- try {
101
- const specsContent = fs.readFileSync(specsPath, 'utf8');
102
- const specs = JSON.parse(specsContent);
103
-
104
- return {
105
- results: [{
106
- name: 'Find specs.json file',
107
- success: true,
108
- details: 'specs.json file found'
109
- }],
110
- specs
111
- };
112
- } catch (error) {
113
- return {
114
- results: [{
115
- name: 'Parse specs.json file',
116
- success: false,
117
- details: `❌ Error parsing specs.json: ${error.message}`
118
- }],
119
- specs: null
120
- };
121
- }
122
- }
123
-
124
- /**
125
- * Extract external specs from specs data
126
- * @param {Object} specs - Specs data
127
- * @returns {Object} - Object containing results and external specs if found
128
- */
129
- function extractExternalSpecs(specs) {
130
- if (!specs.specs || !Array.isArray(specs.specs) || !specs.specs.some(spec => spec.external_specs)) {
131
- return {
132
- results: [{
133
- name: 'Find external_specs in specs.json',
134
- success: false,
135
- details: 'external_specs key not found in specs.json'
136
- }],
137
- externalSpecs: []
138
- };
139
- }
140
-
141
- const externalSpecs = [];
142
-
143
- specs.specs.forEach(spec => {
144
- if (spec.external_specs && Array.isArray(spec.external_specs)) {
145
- externalSpecs.push(...spec.external_specs);
146
- }
147
- });
148
-
149
- return {
150
- results: [{
151
- name: 'Find external_specs in specs.json',
152
- success: true,
153
- details: 'external_specs key found in specs.json'
154
- }],
155
- externalSpecs
156
- };
157
- }
158
-
159
- /**
160
- * Check GitHub Pages URL for an external spec
161
- * @param {Object} spec - External spec object
162
- * @returns {Promise<Array>} - Array of check results
163
- */
164
- async function checkGitHubPagesUrl(spec) {
165
- const results = [];
166
-
167
- if (!spec.gh_page) {
168
- results.push({
169
- name: `Check "${spec.external_spec}" gh_page URL`,
170
- success: false,
171
- details: 'gh_page URL is missing'
172
- });
173
- return results;
174
- }
175
-
176
- const isValidGhPage = isValidGitHubPagesUrl(spec.gh_page);
177
- results.push({
178
- name: `Check "${spec.external_spec}" gh_page URL structure`,
179
- success: isValidGhPage,
180
- details: isValidGhPage
181
- ? 'Valid GitHub Pages URL structure'
182
- : `Invalid GitHub Pages URL structure: ${spec.gh_page}`
183
- });
184
-
185
- if (isValidGhPage) {
186
- const ghPageExists = await urlExists(spec.gh_page);
187
- results.push({
188
- name: `Check "${spec.external_spec}" gh_page URL exists`,
189
- success: ghPageExists,
190
- details: ghPageExists
191
- ? 'GitHub Pages URL is accessible'
192
- : `GitHub Pages URL is not accessible: ${spec.gh_page}`
193
- });
194
- }
195
-
196
- return results;
197
- }
198
-
199
- /**
200
- * Check repository URL for an external spec
201
- * @param {Object} spec - External spec object
202
- * @returns {Promise<Array>} - Array of check results
203
- */
204
- async function checkRepositoryUrl(spec) {
205
- const results = [];
206
-
207
- if (!spec.url) {
208
- results.push({
209
- name: `Check "${spec.external_spec}" repo URL`,
210
- success: false,
211
- details: 'Repository URL is missing'
212
- });
213
- return results;
214
- }
215
-
216
- const isValidRepoUrl = isValidGitHubRepoUrl(spec.url);
217
- results.push({
218
- name: `Check "${spec.external_spec}" repo URL structure`,
219
- success: isValidRepoUrl,
220
- details: isValidRepoUrl
221
- ? 'Valid GitHub repository URL structure'
222
- : `Invalid GitHub repository URL structure: ${spec.url}`
223
- });
224
-
225
- if (isValidRepoUrl) {
226
- const repoUrlExists = await urlExists(spec.url);
227
- results.push({
228
- name: `Check "${spec.external_spec}" repo URL exists`,
229
- success: repoUrlExists,
230
- details: repoUrlExists
231
- ? 'GitHub repository URL is accessible'
232
- : `GitHub repository URL is not accessible: ${spec.url}`
233
- });
234
- }
235
-
236
- return results;
237
- }
238
-
239
- /**
240
- * Check external specs in a specs.json file
241
- * @param {string} projectRoot - Root directory of the project
242
- * @returns {Promise<Array>} - Array of check results
243
- */
244
- async function checkExternalSpecs(projectRoot) {
245
- try {
246
- // Check for and read specs.json file
247
- const { results, specs } = checkAndReadSpecsFile(projectRoot);
248
-
249
- if (!specs) {
250
- return results;
251
- }
252
-
253
- // Extract external specs from specs data
254
- const { results: extractResults, externalSpecs } = extractExternalSpecs(specs);
255
-
256
- // Combine results
257
- const allResults = [...results, ...extractResults];
258
-
259
- if (externalSpecs.length === 0) {
260
- return allResults;
261
- }
262
-
263
- // Check each external spec
264
- for (const spec of externalSpecs) {
265
- // Check GitHub Pages URL
266
- const ghPageResults = await checkGitHubPagesUrl(spec);
267
- allResults.push(...ghPageResults);
268
-
269
- // Check repository URL
270
- const repoUrlResults = await checkRepositoryUrl(spec);
271
- allResults.push(...repoUrlResults);
272
- }
273
-
274
- return allResults;
275
- } catch (error) {
276
- Logger.error('Error checking external specs:', error);
277
- return [{
278
- name: 'External specs check',
279
- success: false,
280
- details: `Error: ${error.message}`
281
- }];
282
- }
283
- }
284
-
285
- module.exports = {
286
- checkExternalSpecs
287
- };
@@ -1,387 +0,0 @@
1
- /**
2
- * @fileoverview Spec-Up-T specs.json Configuration Validator
3
- *
4
- * Validates specs.json configuration files by comparing project configurations
5
- * against default templates to ensure proper setup and catch common issues.
6
- *
7
- * **Validation Flow:**
8
- * 1. File existence check (project specs.json + default template)
9
- * 2. Field categorization (required vs optional fields)
10
- * 3. Required field validation (presence + configuration status)
11
- * 4. Optional field validation (configuration warnings)
12
- * 5. Unexpected field detection (typo prevention)
13
- * 6. Summary report generation
14
- *
15
- * **Field Categories:**
16
- * - **Required fields**: Must be present (e.g., title, author, source)
17
- * - **Optional fields**: Can be omitted (e.g., logo, external_specs)
18
- * - **Must-change fields**: Cannot use default values (title, author, etc.)
19
- * - **Allow-default fields**: Can keep default values (spec_directory, etc.)
20
- * - **Deprecated fields**: Legacy fields ignored during validation
21
- *
22
- * **Output:**
23
- * Returns structured validation results with pass/fail/warning status,
24
- * detailed messages, and actionable feedback for configuration improvements.
25
- *
26
- * @author Spec-Up-T Team
27
- * @since 2025-06-06
28
- */
29
-
30
- const fs = require('fs');
31
- const path = require('path');
32
- const Logger = require('../utils/logger');
33
-
34
- /**
35
- * Field descriptions for specs.json keys
36
- */
37
- const fieldDescriptions = {
38
- 'title': 'Specification title',
39
- 'description': 'Specification description',
40
- 'author': 'Specification author',
41
- 'source': 'Source repository information',
42
- 'spec_directory': 'Directory containing specification content',
43
- 'spec_terms_directory': 'Directory containing term definitions',
44
- 'output_path': 'Output directory for generated files',
45
- 'markdown_paths': 'List of markdown files to include in the specification',
46
- 'logo': 'Logo URL',
47
- 'logo_link': 'Link to the logo',
48
- 'favicon': 'Favicon URL',
49
- 'external_specs': 'External specifications',
50
- 'katex': 'KaTeX math rendering configuration'
51
- };
52
-
53
- /**
54
- * Fields that can remain at their default values without being flagged
55
- */
56
- const allowDefaultValueFields = [
57
- 'spec_directory',
58
- 'spec_terms_directory',
59
- 'output_path',
60
- 'katex',
61
- 'logo',
62
- 'logo_link',
63
- 'favicon'
64
- ];
65
-
66
- /**
67
- * Fields that should fail if they're not modified from default values
68
- */
69
- const mustChangeFields = [
70
- 'title',
71
- 'description',
72
- 'author',
73
- 'source'
74
- ];
75
-
76
- /**
77
- * Known optional fields
78
- */
79
- const knownOptionalFields = [
80
- 'logo',
81
- 'external_specs',
82
- 'logo_link',
83
- 'favicon',
84
- 'katex',
85
- 'spec_directory',
86
- 'spec_terms_directory',
87
- 'output_path',
88
- 'markdown_paths'
89
- ];
90
-
91
- /**
92
- * Deprecated fields that should not be flagged as unexpected
93
- */
94
- const deprecatedFields = [];
95
-
96
- /**
97
- * Check if the files needed for configuration check exist
98
- * @param {string} projectSpecsPath - Path to project specs.json
99
- * @param {string} defaultSpecsPath - Path to default specs.json
100
- * @returns {Array|null} - Check results or null if files exist
101
- */
102
- function checkFilesExist(projectSpecsPath, defaultSpecsPath) {
103
- if (!fs.existsSync(projectSpecsPath)) {
104
- return [{
105
- name: 'Find specs.json file',
106
- success: false,
107
- details: 'specs.json file not found in project root'
108
- }];
109
- }
110
-
111
- if (!fs.existsSync(defaultSpecsPath)) {
112
- return [{
113
- name: 'Find default specs.json template',
114
- success: false,
115
- details: 'Default specs.json template not found'
116
- }];
117
- }
118
-
119
- return null;
120
- }
121
-
122
- /**
123
- * Get all valid field names (required + optional + deprecated). Creates a comprehensive list of all field names that should not be flagged as "unexpected"
124
- * @param {Array} defaultSpecKeys - Keys from default specs
125
- * @returns {Array} - All valid field names
126
- */
127
- function getAllValidFields(defaultSpecKeys) {
128
- // Get all field names from descriptions (these are the canonical field names)
129
- const canonicalFields = Object.keys(fieldDescriptions);
130
-
131
- // Combine with known optional fields and deprecated fields
132
- const allValidFields = [
133
- ...canonicalFields,
134
- ...knownOptionalFields,
135
- ...deprecatedFields,
136
- ...defaultSpecKeys
137
- ];
138
-
139
- // Remove duplicates
140
- return [...new Set(allValidFields)];
141
- }
142
-
143
- /**
144
- * Categorize fields into required and optional
145
- * @param {Array} defaultSpecKeys - Keys from default specs
146
- * @returns {Object} - Object containing required and optional fields
147
- */
148
- function categorizeFields(defaultSpecKeys) {
149
- const createFieldObject = key => ({
150
- key,
151
- description: fieldDescriptions[key] || `${key.replace(/_/g, ' ')} field`,
152
- allowDefaultValue: allowDefaultValueFields.includes(key),
153
- mustChange: mustChangeFields.includes(key)
154
- });
155
-
156
- const requiredFields = defaultSpecKeys
157
- .filter(key => !knownOptionalFields.includes(key))
158
- .map(createFieldObject);
159
-
160
- const optionalFields = defaultSpecKeys
161
- .filter(key => knownOptionalFields.includes(key))
162
- .map(createFieldObject);
163
-
164
- return { requiredFields, optionalFields };
165
- }
166
-
167
- /**
168
- * Process field validation results. Orchestrates the validation of all fields in the specs.json
169
- * @param {Object} projectSpecs - Project specs object
170
- * @param {Object} defaultSpecs - Default specs object
171
- * @param {Array} defaultSpecKeys - Keys from default specs
172
- * @returns {Object} - Object with results and missingRequiredKeys
173
- */
174
- function processFieldValidation(projectSpecs, defaultSpecs, defaultSpecKeys) {
175
- const { requiredFields, optionalFields } = categorizeFields(defaultSpecKeys);
176
-
177
- const requiredResults = requiredFields.map(field => evaluateRequiredField(field, projectSpecs, defaultSpecs));
178
- const optionalResults = optionalFields.map(field => evaluateOptionalField(field, projectSpecs, defaultSpecs));
179
-
180
- const missingRequiredKeys = requiredResults
181
- .filter(result => !result.success && result.details.includes('missing'))
182
- .map((_, index) => requiredFields[index].key);
183
-
184
- return {
185
- results: [...requiredResults, ...optionalResults],
186
- missingRequiredKeys
187
- };
188
- }
189
-
190
- /**
191
- * Check for unexpected fields in project specs
192
- * @param {Object} projectSpecs - Project specs object
193
- * @param {Array} defaultSpecKeys - Keys from default specs
194
- * @returns {Array} - Array of unexpected field names
195
- */
196
- function findUnexpectedFields(projectSpecs, defaultSpecKeys) {
197
- const projectKeys = Object.keys(projectSpecs.specs?.[0] || {});
198
- const allValidFields = getAllValidFields(defaultSpecKeys);
199
-
200
- return projectKeys.filter(key => !allValidFields.includes(key));
201
- }
202
-
203
- /**
204
- * Check if a field value has been configured
205
- * @param {any} projectValue - Value from project specs
206
- * @param {any} defaultValue - Value from default specs
207
- * @returns {boolean} - True if configured
208
- */
209
- function isFieldConfigured(projectValue, defaultValue) {
210
- if (typeof projectValue === 'object') {
211
- return JSON.stringify(projectValue) !== JSON.stringify(defaultValue);
212
- }
213
- return projectValue !== defaultValue;
214
- }
215
-
216
- /**
217
- * Evaluate a field and generate result (unified for required/optional)
218
- * @param {Object} field - Field definition
219
- * @param {Object} projectSpecs - Project specs object
220
- * @param {Object} defaultSpecs - Default specs object
221
- * @param {boolean} isRequired - Whether field is required
222
- * @returns {Object} - Check result
223
- */
224
- function evaluateField(field, projectSpecs, defaultSpecs, isRequired) {
225
- const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
226
-
227
- if (!hasField) {
228
- return {
229
- name: `${field.description} configuration`,
230
- success: !isRequired,
231
- details: isRequired
232
- ? `Required "${field.key}" key is missing in specs.json`
233
- : `Optional "${field.key}" key is not present (this is not required)`
234
- };
235
- }
236
-
237
- const projectValue = projectSpecs.specs[0][field.key];
238
- const defaultValue = defaultSpecs.specs?.[0]?.[field.key];
239
- const isConfigured = field.allowDefaultValue || isFieldConfigured(projectValue, defaultValue);
240
-
241
- // Show warning when fields haven't been configured from their default values
242
- const status = isConfigured ? undefined : 'warning';
243
-
244
- const details = isConfigured
245
- ? (projectValue === defaultValue && field.allowDefaultValue)
246
- ? `Default value for ${field.description} is acceptable`
247
- : `${field.description} has been changed from default`
248
- : `${field.description} is still set to default value${mustChangeFields.includes(field.key) ? `: \"${defaultValue}\"` : ''}`;
249
-
250
- return {
251
- name: `${field.description} configuration`,
252
- status,
253
- success: true,
254
- details
255
- };
256
- }
257
-
258
- /**
259
- * Evaluate a required field and generate result
260
- * @param {Object} field - Field definition
261
- * @param {Object} projectSpecs - Project specs object
262
- * @param {Object} defaultSpecs - Default specs object
263
- * @returns {Object} - Check result
264
- */
265
- function evaluateRequiredField(field, projectSpecs, defaultSpecs) {
266
- return evaluateField(field, projectSpecs, defaultSpecs, true);
267
- }
268
-
269
- /**
270
- * Evaluate an optional field and generate result
271
- * @param {Object} field - Field definition
272
- * @param {Object} projectSpecs - Project specs object
273
- * @param {Object} defaultSpecs - Default specs object
274
- * @returns {Object} - Check result
275
- */
276
- function evaluateOptionalField(field, projectSpecs, defaultSpecs) {
277
- return evaluateField(field, projectSpecs, defaultSpecs, false);
278
- }
279
-
280
- /**
281
- * Generate summary results for the configuration check
282
- * @param {Array} results - Existing check results
283
- * @param {Array} missingRequiredKeys - List of missing required keys
284
- * @param {Array} unexpectedKeys - List of unexpected keys
285
- * @returns {Array} - Additional summary results
286
- */
287
- function generateSummaryResults(results, missingRequiredKeys, unexpectedKeys) {
288
- const summaryResults = [];
289
-
290
- // Required fields summary
291
- summaryResults.push({
292
- name: 'Required fields check',
293
- success: missingRequiredKeys.length === 0,
294
- details: missingRequiredKeys.length > 0
295
- ? `Missing required fields: ${missingRequiredKeys.join(', ')}`
296
- : 'All required fields are present'
297
- });
298
-
299
- // Unexpected fields check
300
- if (unexpectedKeys.length > 0) {
301
- summaryResults.push({
302
- name: 'Unexpected fields check',
303
- success: false,
304
- details: `Found unexpected fields that might be typos: ${unexpectedKeys.join(', ')}`
305
- });
306
- }
307
-
308
- // Overall configuration status
309
- const fieldResults = results.filter(r =>
310
- r.name.includes('configuration') && !r.name.includes('Overall')
311
- );
312
-
313
- const configuredItemsCount = fieldResults.filter(r => r.success).length;
314
- const configurationPercentage = Math.round((configuredItemsCount / fieldResults.length) * 100);
315
-
316
- summaryResults.push({
317
- name: 'Overall configuration status',
318
- success: configurationPercentage > 50 && missingRequiredKeys.length === 0,
319
- details: `${configurationPercentage}% of specs.json has been configured (${configuredItemsCount}/${fieldResults.length} items)`
320
- });
321
-
322
- return summaryResults;
323
- }
324
-
325
- /**
326
- * Load and parse configuration files
327
- * @param {string} projectRoot - Root directory of the project
328
- * @returns {Object} - Object containing parsed specs and file paths
329
- */
330
- function loadConfigurationFiles(projectRoot) {
331
- const projectSpecsPath = path.join(projectRoot, 'specs.json');
332
- const defaultSpecsPath = path.join(
333
- __dirname, '..', 'install-from-boilerplate', 'boilerplate', 'specs.json'
334
- );
335
-
336
- const fileCheckResults = checkFilesExist(projectSpecsPath, defaultSpecsPath);
337
- if (fileCheckResults) {
338
- return { error: fileCheckResults };
339
- }
340
-
341
- const projectSpecs = JSON.parse(fs.readFileSync(projectSpecsPath, 'utf8'));
342
- const defaultSpecs = JSON.parse(fs.readFileSync(defaultSpecsPath, 'utf8'));
343
-
344
- return { projectSpecs, defaultSpecs };
345
- }
346
-
347
- /**
348
- * Check if specs.json has been configured from default
349
- * @param {string} projectRoot - Root directory of the project
350
- * @returns {Promise<Array>} - Array of check results
351
- */
352
- async function checkSpecsJsonConfiguration(projectRoot) {
353
- try {
354
- const { error, projectSpecs, defaultSpecs } = loadConfigurationFiles(projectRoot);
355
- if (error) return error;
356
-
357
- const results = [{
358
- name: 'specs.json exists',
359
- success: true,
360
- details: 'Project specs.json file found'
361
- }];
362
-
363
- const defaultSpecKeys = Object.keys(defaultSpecs.specs?.[0] || {});
364
- const { results: fieldResults, missingRequiredKeys } = processFieldValidation(
365
- projectSpecs, defaultSpecs, defaultSpecKeys
366
- );
367
-
368
- results.push(...fieldResults);
369
-
370
- const unexpectedKeys = findUnexpectedFields(projectSpecs, defaultSpecKeys);
371
- const summaryResults = generateSummaryResults(results, missingRequiredKeys, unexpectedKeys);
372
-
373
- return [...results, ...summaryResults];
374
-
375
- } catch (error) {
376
- Logger.error('Error checking specs.json configuration:', error);
377
- return [{
378
- name: 'specs.json configuration check',
379
- success: false,
380
- details: `Error: ${error.message}`
381
- }];
382
- }
383
- }
384
-
385
- module.exports = {
386
- checkSpecsJsonConfiguration
387
- };