spec-up-t 1.2.3 → 1.2.5

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 (61) hide show
  1. package/.sonarlint/connectedMode.json +5 -0
  2. package/assets/compiled/body.js +35 -32
  3. package/assets/compiled/head.css +7 -5
  4. package/assets/compiled/head.js +3 -3
  5. package/assets/css/add-bootstrap-classes-to-images.css +34 -0
  6. package/assets/css/adjust-font-size.css +6 -11
  7. package/assets/css/backToTop.css +0 -1
  8. package/assets/css/image-full-size.css +44 -0
  9. package/assets/css/index.css +1 -2
  10. package/assets/css/pdf-styles.css +23 -27
  11. package/assets/css/repo-issues.css +0 -6
  12. package/assets/css/search.css +0 -1
  13. package/assets/css/sidebar-toc.css +13 -12
  14. package/assets/css/terms-and-definitions.css +43 -37
  15. package/assets/js/add-bootstrap-classes-to-images.js +98 -0
  16. package/assets/js/add-href-to-snapshot-link.js +2 -1
  17. package/assets/js/addAnchorsToTerms.js +0 -1
  18. package/assets/js/adjust-font-size.js +0 -9
  19. package/assets/js/create-alphabet-index.js +12 -3
  20. package/assets/js/create-term-filter.js +12 -0
  21. package/assets/js/custom-elements.js +13 -18
  22. package/assets/js/declare-markdown-it.js +1 -1
  23. package/assets/js/hide-show-utility-container.js +17 -0
  24. package/assets/js/highlightMenuItems.js +3 -3
  25. package/assets/js/image-full-size.js +76 -0
  26. package/assets/js/index.js +1 -5
  27. package/assets/js/insert-trefs.js +2 -2
  28. package/assets/js/modal.js +3 -3
  29. package/assets/js/search.js +15 -3
  30. package/assets/js/utils.js +2 -3
  31. package/index.js +7 -17
  32. package/package.json +2 -2
  33. package/src/README.md +3 -3
  34. package/src/add-remove-xref-source.js +0 -2
  35. package/src/asset-map.json +5 -0
  36. package/src/collect-external-references.js +187 -179
  37. package/src/collectExternalReferences/fetchTermsFromIndex.js +2 -1
  38. package/src/config/paths.js +2 -2
  39. package/src/create-external-specs-list.js +1 -1
  40. package/src/create-term-index.js +126 -22
  41. package/src/fix-markdown-files.js +152 -90
  42. package/src/health-check/external-specs-checker.js +173 -94
  43. package/src/health-check/output-gitignore-checker.js +327 -191
  44. package/src/health-check/specs-configuration-checker.js +288 -210
  45. package/src/health-check/term-references-checker.js +200 -123
  46. package/src/health-check/tref-term-checker.js +264 -179
  47. package/src/health-check.js +52 -36
  48. package/src/init.js +1 -4
  49. package/src/insert-term-index.js +5 -5
  50. package/src/install-from-boilerplate/add-scripts-keys.js +3 -1
  51. package/src/install-from-boilerplate/boilerplate/gitignore +2 -1
  52. package/src/install-from-boilerplate/config-system-files.js +9 -1
  53. package/src/install-from-boilerplate/copy-system-files.js +1 -1
  54. package/src/markdown-it-extensions.js +199 -106
  55. package/src/references.js +1 -2
  56. package/src/utils/doesUrlExist.js +7 -5
  57. package/src/utils/fetch.js +14 -14
  58. package/templates/template.html +1 -2
  59. package/assets/js/insert-xrefs.js +0 -370
  60. package/src/create-term-relations.js +0 -131
  61. 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 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
- }];
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
- // Compare key parts to see if the file has been configured
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
- // 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
313
+ // Check each required field
118
314
  const missingRequiredKeys = [];
119
-
315
+
120
316
  for (const field of requiredFields) {
121
- const hasField = projectSpecs.specs?.[0]?.hasOwnProperty(field.key);
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
- 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
- });
326
+ results.push(evaluateOptionalField(field, projectSpecs, defaultSpecs));
229
327
  }
230
328
 
231
- // Check if any fields exist that aren't in the standard template (could be typos)
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
- 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
- });
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 [{