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
@@ -3,17 +3,20 @@ const { configOverwriteScriptsKeys } = require('./config-scripts-keys');
3
3
  const addScriptsKeys = require('./add-scripts-keys');
4
4
  const copySystemFiles = require('./copy-system-files');
5
5
  const { gitIgnoreEntries } = require('./config-gitignore-entries');
6
- const {updateGitignore} = require('./add-gitignore-entries');
6
+ const { updateGitignore } = require('./add-gitignore-entries');
7
+ const updateDependencies = require('./update-dependencies');
8
+ const Logger = require('../utils/logger');
7
9
 
8
10
 
9
11
  addScriptsKeys(configScriptsKeys, configOverwriteScriptsKeys);
10
12
  copySystemFiles();
11
13
  updateGitignore(gitIgnoreEntries.gitignorePath, gitIgnoreEntries.filesToAdd);
12
14
 
13
- const Logger = require('../utils/logger');
14
-
15
15
  // We can use this file to do any custom updates during post-install.
16
16
  const customUpdate = () => {
17
+ // Update dependencies based on package.spec-up-t.json
18
+ updateDependencies();
19
+
17
20
  // Custom logic here
18
21
  // ...
19
22
  }
@@ -0,0 +1,105 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const https = require('https');
4
+ const Logger = require('../utils/logger');
5
+
6
+ /**
7
+ * URL to the canonical package.spec-up-t.json in the starter-pack repository.
8
+ * This defines the recommended dependency versions for consuming repos.
9
+ */
10
+ const STARTER_PACK_PACKAGE_URL = 'https://raw.githubusercontent.com/trustoverip/spec-up-t-starter-pack/main/package.spec-up-t.json';
11
+
12
+ /**
13
+ * Fetches package.spec-up-t.json from the remote starter-pack repository.
14
+ *
15
+ * @returns {Promise<Object>} The parsed package.spec-up-t.json content
16
+ * @throws {Error} If the fetch fails or the response is invalid
17
+ */
18
+ function fetchStarterPackageConfig() {
19
+ return new Promise((resolve, reject) => {
20
+ https.get(STARTER_PACK_PACKAGE_URL, (response) => {
21
+ if (response.statusCode !== 200) {
22
+ reject(new Error(`Failed to fetch package.spec-up-t.json: HTTP ${response.statusCode}`));
23
+ return;
24
+ }
25
+
26
+ let data = '';
27
+ response.on('data', (chunk) => {
28
+ data += chunk;
29
+ });
30
+
31
+ response.on('end', () => {
32
+ try {
33
+ const config = JSON.parse(data);
34
+ resolve(config);
35
+ } catch (error) {
36
+ reject(new Error(`Failed to parse package.spec-up-t.json: ${error.message}`));
37
+ }
38
+ });
39
+ }).on('error', (error) => {
40
+ reject(new Error(`Network error fetching package.spec-up-t.json: ${error.message}`));
41
+ });
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Updates dependencies in the consuming repo's package.json based on the
47
+ * canonical package.spec-up-t.json from the starter-pack repository.
48
+ * This ensures that the consuming repo always has the correct dependency versions
49
+ * as specified by the spec-up-t-starter-pack.
50
+ */
51
+ async function updateDependencies() {
52
+ // Path to the consuming repo's package.json
53
+ const consumerPackagePath = path.resolve(process.cwd(), 'package.json');
54
+
55
+ try {
56
+ // Fetch the canonical package.spec-up-t.json from the starter-pack repo
57
+ Logger.info('Fetching latest dependency configuration...');
58
+ const specUpPackage = await fetchStarterPackageConfig();
59
+
60
+ // Read consuming repo's package.json
61
+ if (!fs.existsSync(consumerPackagePath)) {
62
+ Logger.error('package.json not found at:', consumerPackagePath);
63
+ return;
64
+ }
65
+
66
+ const consumerPackageData = fs.readFileSync(consumerPackagePath, 'utf8');
67
+ const consumerPackage = JSON.parse(consumerPackageData);
68
+
69
+ // Initialize dependencies section if it doesn't exist
70
+ if (!consumerPackage.dependencies) {
71
+ consumerPackage.dependencies = {};
72
+ }
73
+
74
+ // Check if there are dependencies to update
75
+ if (!specUpPackage.dependencies) {
76
+ Logger.info('No dependencies found in package.spec-up-t.json');
77
+ return;
78
+ }
79
+
80
+ let updatedCount = 0;
81
+
82
+ // Update each dependency from package.spec-up-t.json
83
+ for (const [packageName, version] of Object.entries(specUpPackage.dependencies)) {
84
+ const currentVersion = consumerPackage.dependencies[packageName];
85
+
86
+ if (currentVersion !== version) {
87
+ consumerPackage.dependencies[packageName] = version;
88
+ updatedCount++;
89
+ Logger.info(`Updated ${packageName}: ${currentVersion || 'not installed'} -> ${version}`);
90
+ }
91
+ }
92
+
93
+ if (updatedCount > 0) {
94
+ // Write the updated package.json back to disk
95
+ fs.writeFileSync(consumerPackagePath, JSON.stringify(consumerPackage, null, 2) + '\n', 'utf8');
96
+ Logger.success(`Successfully updated ${updatedCount} dependenc${updatedCount === 1 ? 'y' : 'ies'} in package.json`);
97
+ } else {
98
+ Logger.info('All dependencies are already up to date');
99
+ }
100
+ } catch (error) {
101
+ Logger.error('Error updating dependencies:', error.message);
102
+ }
103
+ }
104
+
105
+ module.exports = updateDependencies;
@@ -253,10 +253,22 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
253
253
  });
254
254
  });
255
255
 
256
+ // Filter out xtrefs that don't exist in any current file
256
257
  allXTrefs.xtrefs = allXTrefs.xtrefs.filter(existingXTref =>
257
258
  isXTrefInAnyFile(existingXTref, fileContents)
258
259
  );
259
260
 
261
+ // Clean up sourceFiles arrays to remove deleted files
262
+ // This ensures that if a file is deleted, its entry is removed from all sourceFiles arrays
263
+ allXTrefs.xtrefs.forEach(xtref => {
264
+ if (xtref.sourceFiles && Array.isArray(xtref.sourceFiles)) {
265
+ const currentFiles = Array.from(fileContents.keys());
266
+ xtref.sourceFiles = xtref.sourceFiles.filter(sf =>
267
+ currentFiles.includes(sf.file)
268
+ );
269
+ }
270
+ });
271
+
260
272
  fileContents.forEach((content, filename) => {
261
273
  addNewXTrefsFromMarkdown(content, allXTrefs, filename, processXTrefObject, externalSpecsRepos);
262
274
  });
@@ -19,7 +19,7 @@ function validateReferences(references, definitions, render) {
19
19
  if (unresolvedRefs.length > 0) {
20
20
  Logger.warn(`Unresolved References: ${unresolvedRefs.join(',')}`, {
21
21
  context: 'These terms are referenced in your spec but not defined',
22
- hint: 'Add [[def: term]] definitions or [[tref: repo, term]] transclusion for these terms in your terminology files, or check for typos in [[ref: term]] references',
22
+ hint: 'Add [[def: term]] definitions or [[tref: repo, term]] transclusion for these terms or alter existing definitions, optionally by adding aliases, in your terminology files, or check for typos in [[ref: term]] references',
23
23
  details: `Count: ${unresolvedRefs.length} unresolved term(s)`
24
24
  });
25
25
  }
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const { fetchExternalSpecs, validateReferences, mergeXrefTermsIntoAllXTrefs } = require('../references/external-references-service.js');
11
11
  const { processEscapedTags, restoreEscapedTags } = require('../preprocessing/escape-placeholder-utils.js');
12
12
  const { sortDefinitionTermsInHtml, fixDefinitionListStructure } = require('../postprocessing/definition-list-postprocessor.js');
13
- const { getGithubRepoInfo } = require('../../utils/git-info.js');
13
+ const { getGithubRepoInfo, getCurrentBranch } = require('../../utils/git-info.js');
14
14
  const { templateTags } = require('../../utils/regex-patterns.js');
15
15
 
16
16
  const { createScriptElementWithXTrefDataForEmbeddingInHtml, applyReplacers } = require('./render-utils.js');
@@ -37,6 +37,9 @@ async function render(spec, assets, sharedVars, config, template, assetsGlobal,
37
37
  // Add universal timestamp in ISO 8601 format for template injection
38
38
  const universalTimestamp = date.toISOString();
39
39
 
40
+ // Get the branch name from which the index.html was generated
41
+ const buildBranch = getCurrentBranch();
42
+
40
43
  // Read all markdown files into an array
41
44
  const docs = await Promise.all(
42
45
  (spec.markdown_paths || ['spec.md']).map(_path =>
@@ -162,6 +165,7 @@ async function render(spec, assets, sharedVars, config, template, assetsGlobal,
162
165
  externalSpecsList: externalSpecsList,
163
166
  currentDate: currentDate,
164
167
  universalTimestamp: universalTimestamp,
168
+ buildBranch: buildBranch,
165
169
  githubRepoInfo: getGithubRepoInfo(spec)
166
170
  });
167
171
 
@@ -137,15 +137,6 @@
137
137
  <!-- Right-aligned elements -->
138
138
  <div class="d-flex align-items-center service-menu d-print-none">
139
139
 
140
- <!-- Settings side menu -->
141
- <button id="repo_settings" class="btn btn-sm btn-outline-secondary ms-2" type="button"
142
- data-bs-toggle="offcanvas" data-bs-target="#offcanvasSettings" aria-controls="offcanvasSettings">
143
- <svg class="bi" width="1em" height="1em" aria-hidden="true">
144
- <use href="#gear-wide-connected"></use>
145
- </svg>
146
- <span class="visually-hidden">Settings</span>
147
- </button>
148
-
149
140
  <!-- Dark / Light theme selector -->
150
141
  <div class="dropdown bd-mode-toggle">
151
142
  <button
@@ -247,6 +238,16 @@
247
238
  </div>
248
239
  </div>
249
240
 
241
+ <!-- Menu toggle button - hamburger icon fixed at right edge for slide-in panel -->
242
+ <button id="repo_settings" class="btn btn-sm btn-outline-secondary menu-toggle-btn d-print-none" type="button"
243
+ data-bs-toggle="offcanvas" data-bs-target="#offcanvasSettings" aria-controls="offcanvasSettings"
244
+ title="Open menu">
245
+ <svg class="bi" width="1.25em" height="1.25em" aria-hidden="true">
246
+ <use href="#list"></use>
247
+ </svg>
248
+ <span class="visually-hidden">Open menu</span>
249
+ </button>
250
+
250
251
  <div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasSettings" aria-labelledby="offcanvasSettingsLabel">
251
252
  <div class="offcanvas-header">
252
253
  <h5 class="offcanvas-title mt-0" id="offcanvasSettingsLabel">Settings</h5>
@@ -293,7 +294,39 @@
293
294
  ${externalSpecsList}
294
295
  </div>
295
296
  <hr>
296
- <p class="ps-3 pe-3" id="w3c-state">W3C Recommendation, ${currentDate}</p>
297
+ <!-- External Reference Changes Report Section -->
298
+ <div class="ps-3 pe-3" id="external-ref-changes-section" style="display: none;">
299
+ <div class="d-flex justify-content-between align-items-center mb-2">
300
+ <p class="mb-0 fw-bold">External Reference Changes:</p>
301
+ <span id="external-ref-changes-badge" class="badge bg-warning text-dark">0</span>
302
+ </div>
303
+ <p class="small text-muted mb-2">Terms whose definitions have changed since the last build:</p>
304
+ <div id="external-ref-changes-container" class="external-ref-changes-report">
305
+ <!-- Report table will be populated by JavaScript -->
306
+ </div>
307
+ <div id="external-ref-validation-status" class="small text-muted mt-2">
308
+ <i class="bi bi-hourglass-split me-1"></i>
309
+ Validating external references...
310
+ </div>
311
+ </div>
312
+ <hr id="external-ref-changes-divider" style="display: none;">
313
+ <p class="d-none ps-3 pe-3" id="w3c-state">W3C Recommendation, ${currentDate}</p>
314
+ <!-- Build Information Section -->
315
+ <div class="ps-3 pe-3">
316
+ <p class="mb-2 fw-bold">Build Information:</p>
317
+ <div id="build-info" class="small text-muted">
318
+ <div class="d-flex align-items-center mb-1">
319
+ <i class="bi bi-calendar-event me-2"></i>
320
+ <time class="timestamp" datetime="${universalTimestamp}">
321
+ Generated: ${universalTimestamp}
322
+ </time>
323
+ </div>
324
+ <div class="d-flex align-items-center mb-1">
325
+ <i class="bi bi-signpost-split me-2"></i>
326
+ <span>Source branch: <strong>${buildBranch}</strong></span>
327
+ </div>
328
+ </div>
329
+ </div>
297
330
  </div>
298
331
  </div>
299
332
 
@@ -1,414 +0,0 @@
1
- /**
2
- * @file destination-gitignore-checker.js
3
- * @description Checks if the final destination directory (from output_path in specs.json)
4
- * is being ignored by Git. This is the directory where index.html is generated,
5
- * NOT the temporary .cache directory (formerly called "output").
6
- *
7
- * Important: This file deals with concept #1 (output_path from specs.json),
8
- * not concept #2 (the temporary .cache directory for build artifacts).
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const { spawnSync } = require('child_process');
14
- const fileOpener = require('../utils/file-opener');
15
- const Logger = require('../utils/logger');
16
- const { utils } = require('../utils/regex-patterns');
17
-
18
- /**
19
- * Checks if a path is gitignored
20
- * @param {string} projectRoot - Root directory of the project
21
- * @param {string} targetPath - Path to check (relative to project root)
22
- * @returns {boolean} - Whether the path is gitignored
23
- */
24
- function isPathGitIgnored(projectRoot, targetPath) {
25
- try {
26
- // Use git check-ignore to determine if the path is ignored
27
- // If command exits with status 0, path is ignored
28
- // If command exits with status 1, path is not ignored
29
- const gitPath = fileOpener.getCommandPath('git');
30
- const result = spawnSync(gitPath, ['-C', projectRoot, 'check-ignore', '-q', targetPath], {
31
- stdio: 'ignore'
32
- });
33
- return result.status === 0; // Path is ignored (command exited with status 0)
34
- } catch (error) {
35
- Logger.info(`Error checking if path is gitignored: ${error.message}`);
36
- return false; // Path is not ignored (command exited with non-zero status)
37
- }
38
- }
39
-
40
- /**
41
- * Check if specs.json exists and return relevant result
42
- * @param {string} projectRoot - Root directory of the project
43
- * @returns {Object} - Result object with specs file check result
44
- */
45
- function checkSpecsFileExists(projectRoot) {
46
- const specsPath = path.join(projectRoot, 'specs.json');
47
-
48
- if (!fs.existsSync(specsPath)) {
49
- return {
50
- result: [{
51
- name: 'Find specs.json file',
52
- success: false,
53
- details: 'specs.json file not found in project root'
54
- }],
55
- specsPath: null
56
- };
57
- }
58
-
59
- return {
60
- result: [{
61
- name: 'Find specs.json file',
62
- success: true,
63
- details: 'specs.json file found'
64
- }],
65
- specsPath
66
- };
67
- }
68
-
69
- /**
70
- * Extract output path from specs.json
71
- * @param {string} specsPath - Path to specs.json file
72
- * @returns {Object} - Result object with output path check result
73
- */
74
- function extractOutputPath(specsPath) {
75
- const results = [];
76
-
77
- // Read specs.json to get the output path
78
- const specsContent = fs.readFileSync(specsPath, 'utf8');
79
- const specs = JSON.parse(specsContent);
80
-
81
- // Get the output_path value
82
- const outputPath = specs.specs?.[0]?.output_path;
83
-
84
- if (!outputPath) {
85
- results.push({
86
- name: 'Find output_path field',
87
- success: false,
88
- details: 'output_path field not found in specs.json'
89
- });
90
- return { results, outputPath: null };
91
- }
92
-
93
- results.push({
94
- name: 'Find output_path field',
95
- success: true,
96
- details: `output_path field found: "${outputPath}"`
97
- });
98
-
99
- // Normalize the path to handle different formats (./, /, etc.)
100
- const normalizedPath = outputPath.replace(/^\.\/|^\//, '');
101
-
102
- return { results, outputPath, normalizedPath };
103
- }
104
-
105
- /**
106
- * Check if the final destination directory (from output_path) exists
107
- * @param {string} projectRoot - Root directory of the project
108
- * @param {string} outputPath - Output path from specs.json
109
- * @param {string} normalizedPath - Normalized output path
110
- * @returns {Object} - Result with final destination directory check
111
- */
112
- function checkOutputDirExists(projectRoot, outputPath, normalizedPath) {
113
- // Check if the path exists
114
- const fullPath = path.join(projectRoot, normalizedPath);
115
- const outputPathExists = fs.existsSync(fullPath);
116
-
117
- if (!outputPathExists) {
118
- return {
119
- name: 'Final destination directory existence',
120
- status: 'warning',
121
- success: true, // Still considered a "success" for backward compatibility
122
- details: `Final destination directory "${outputPath}" does not exist yet. This is OK if you haven't rendered the specs yet.`
123
- };
124
- }
125
-
126
- return {
127
- name: 'Final destination directory existence',
128
- success: true,
129
- details: `Final destination directory "${outputPath}" exists`
130
- };
131
- }
132
-
133
- /**
134
- * Check if .gitignore file exists
135
- * @param {string} projectRoot - Root directory of the project
136
- * @param {string} normalizedPath - Normalized output path
137
- * @returns {Object} - Result with gitignore check details
138
- */
139
- function checkGitignoreExists(projectRoot, normalizedPath) {
140
- const results = [];
141
-
142
- const gitignorePath = path.join(projectRoot, '.gitignore');
143
- if (!fs.existsSync(gitignorePath)) {
144
- results.push({
145
- name: 'Find .gitignore file',
146
- status: 'warning',
147
- success: true, // Still considered a "success" for backward compatibility
148
- details: '.gitignore file not found in project root. Consider adding one for better version control.'
149
- });
150
-
151
- // If no .gitignore, we can assume the output path is not ignored
152
- results.push({
153
- name: 'Check if output directory is gitignored',
154
- success: true,
155
- details: 'No .gitignore file found, so output directory is not being ignored'
156
- });
157
-
158
- return { results, gitignoreExists: false };
159
- }
160
-
161
- results.push({
162
- name: 'Find .gitignore file',
163
- success: true,
164
- details: '.gitignore file found'
165
- });
166
-
167
- return { results, gitignoreExists: true, gitignorePath };
168
- }
169
-
170
- /**
171
- * Filter non-comment lines from gitignore content
172
- * @param {string} content - Content of .gitignore file
173
- * @returns {Array} - Array of non-comment, non-empty lines
174
- */
175
- function getRelevantGitignoreLines(content) {
176
- const allLines = content.split('\n');
177
-
178
- return allLines.filter(line => {
179
- const trimmedLine = line.trim();
180
- return trimmedLine !== '' && !trimmedLine.startsWith('#');
181
- }).map(line => line.trim());
182
- }
183
-
184
- /**
185
- * Check for patterns that might ignore output directory
186
- * @param {Array} lines - Array of gitignore lines
187
- * @param {string} normalizedPath - Normalized output path
188
- * @param {string} dirName - Directory name from path
189
- * @returns {Array} - Array of ignoring patterns found
190
- */
191
- function findOutputDirIgnorePatterns(lines, normalizedPath, dirName) {
192
- const dirIgnorePatterns = [];
193
-
194
- for (const trimmedLine of lines) {
195
- // Directly check for common patterns that would ignore the output directory
196
- if (
197
- trimmedLine === normalizedPath ||
198
- trimmedLine === `/${normalizedPath}` ||
199
- trimmedLine === `./${normalizedPath}` ||
200
- trimmedLine === `${normalizedPath}/` ||
201
- trimmedLine === `/${normalizedPath}/` ||
202
- trimmedLine === `./${normalizedPath}/` ||
203
- // Check for just the directory name (e.g., "docs")
204
- trimmedLine === dirName ||
205
- trimmedLine === `/${dirName}` ||
206
- trimmedLine === `./${dirName}` ||
207
- trimmedLine === `${dirName}/` ||
208
- trimmedLine === `/${dirName}/` ||
209
- trimmedLine === `./${dirName}/` ||
210
- // Check for wildcards covering all directories
211
- trimmedLine === '*/' ||
212
- // Check for wildcards that might match our path using regex
213
- (trimmedLine.includes('*') && utils.createGitignoreRegex(trimmedLine).test(normalizedPath))
214
- ) {
215
- dirIgnorePatterns.push(trimmedLine);
216
- }
217
- }
218
-
219
- return dirIgnorePatterns;
220
- }
221
-
222
- /**
223
- * Check for patterns that might ignore HTML files
224
- * @param {Array} lines - Array of gitignore lines
225
- * @returns {Array} - Array of HTML ignoring patterns found
226
- */
227
- function findHtmlIgnorePatterns(lines) {
228
- const htmlIgnorePatterns = [];
229
-
230
- for (const trimmedLine of lines) {
231
- if (
232
- trimmedLine === 'index.html' ||
233
- trimmedLine === '*.html' ||
234
- trimmedLine === '/index.html' ||
235
- trimmedLine === '**/index.html' ||
236
- trimmedLine === '**/*.html'
237
- ) {
238
- htmlIgnorePatterns.push(trimmedLine);
239
- }
240
- }
241
-
242
- return htmlIgnorePatterns;
243
- }
244
-
245
- /**
246
- * Find complex patterns that might be affecting HTML files
247
- * @param {Array} lines - Array of gitignore lines
248
- * @returns {Array} - Array of complex patterns found
249
- */
250
- function findComplexHtmlPatterns(lines) {
251
- const patterns = [];
252
-
253
- for (const trimmedLine of lines) {
254
- // Check for wildcards and patterns that might match index.html
255
- if (trimmedLine.includes('*') || trimmedLine.includes('.html')) {
256
- patterns.push(trimmedLine);
257
- }
258
- }
259
-
260
- return patterns;
261
- }
262
-
263
- /**
264
- * Check if HTML files in the final destination directory are being ignored by Git
265
- * @param {string} projectRoot - Root directory of the project
266
- * @param {string} normalizedPath - Normalized output path
267
- * @param {string} outputPath - Original output path
268
- * @param {Array} relevantLines - Relevant lines from .gitignore
269
- * @returns {Array} - Results for HTML files gitignore check
270
- */
271
- function checkHtmlFilesGitignore(projectRoot, normalizedPath, outputPath, relevantLines) {
272
- const results = [];
273
- const htmlIgnorePatterns = findHtmlIgnorePatterns(relevantLines);
274
-
275
- if (htmlIgnorePatterns.length > 0) {
276
- results.push({
277
- name: 'Check if index.html files are gitignored',
278
- success: false,
279
- details: `Found patterns in .gitignore that would ignore HTML files: ${htmlIgnorePatterns.join(', ')}. This is problematic as they're crucial output files.`
280
- });
281
- return results;
282
- }
283
-
284
- // Check if index.html would be ignored
285
- const indexHtmlPath = path.join(normalizedPath, 'index.html');
286
- const isIndexHtmlIgnored = isPathGitIgnored(projectRoot, indexHtmlPath);
287
-
288
- results.push({
289
- name: 'Check if index.html files are gitignored',
290
- success: !isIndexHtmlIgnored,
291
- details: isIndexHtmlIgnored
292
- ? `index.html files in the final destination directory would be ignored by Git. This is problematic as they're crucial output files.`
293
- : `index.html files in the final destination directory are properly tracked by Git.`
294
- });
295
-
296
- // If index.html is ignored but we couldn't find an explicit pattern, look for more complex patterns
297
- if (isIndexHtmlIgnored && htmlIgnorePatterns.length === 0) {
298
- const complexPatterns = findComplexHtmlPatterns(relevantLines);
299
-
300
- if (complexPatterns.length > 0) {
301
- results.push({
302
- name: 'Found complex .gitignore entries potentially affecting HTML files',
303
- status: 'warning',
304
- success: true, // Still considered a "success" for backward compatibility
305
- details: `The following entries in .gitignore might cause HTML files to be ignored: ${complexPatterns.join(', ')}. Consider reviewing these patterns.`
306
- });
307
- }
308
- }
309
-
310
- return results;
311
- }
312
-
313
- /**
314
- * Check if final destination directory (from output_path) is being ignored by Git
315
- * @param {string} projectRoot - Root directory of the project
316
- * @param {string} normalizedPath - Normalized output path
317
- * @param {string} outputPath - Original output path
318
- * @param {string} dirName - Directory name from path
319
- * @param {Array} relevantLines - Relevant lines from .gitignore
320
- * @returns {Array} - Results for final destination directory gitignore check
321
- */
322
- function checkOutputDirIgnorePatterns(projectRoot, normalizedPath, outputPath, dirName, relevantLines) {
323
- const dirIgnorePatterns = findOutputDirIgnorePatterns(relevantLines, normalizedPath, dirName);
324
-
325
- if (dirIgnorePatterns.length > 0) {
326
- return [{
327
- name: 'Check if final destination directory is gitignored',
328
- success: false,
329
- details: `Found patterns in .gitignore that would ignore the final destination directory: ${dirIgnorePatterns.join(', ')}. Remove these entries to ensure generated content is tracked.`
330
- }];
331
- }
332
-
333
- // Fall back to using git check-ignore for verification
334
- const isIgnored = isPathGitIgnored(projectRoot, normalizedPath);
335
-
336
- return [{
337
- name: 'Check if final destination directory is gitignored',
338
- success: !isIgnored,
339
- details: isIgnored
340
- ? `Final destination directory "${outputPath}" is being ignored by Git. This could be due to a complex pattern in .gitignore. Remove any entries that might affect this directory.`
341
- : `Final destination directory "${outputPath}" is not being ignored by Git, which is good.`
342
- }];
343
- }
344
-
345
- /**
346
- * Check if the final destination directory (from output_path in specs.json) is being ignored by Git
347
- * This checks the directory where index.html is generated, NOT the temporary .cache directory
348
- * @param {string} projectRoot - Root directory of the project
349
- * @returns {Promise<Array>} - Array of check results
350
- */
351
- async function checkDestinationGitIgnore(projectRoot) {
352
- const results = [];
353
-
354
- try {
355
- // Check if specs.json exists
356
- const specsCheck = checkSpecsFileExists(projectRoot);
357
- if (!specsCheck.specsPath) {
358
- return specsCheck.result;
359
- }
360
-
361
- results.push(...specsCheck.result);
362
-
363
- // Extract and validate output path
364
- const outputPathCheck = extractOutputPath(specsCheck.specsPath);
365
- if (!outputPathCheck.outputPath) {
366
- return [...results, ...outputPathCheck.results];
367
- }
368
-
369
- results.push(...outputPathCheck.results);
370
- const { outputPath, normalizedPath } = outputPathCheck;
371
-
372
- // Check if output directory exists
373
- const outputDirResult = checkOutputDirExists(projectRoot, outputPath, normalizedPath);
374
- results.push(outputDirResult);
375
-
376
- // Check if .gitignore exists
377
- const gitignoreCheck = checkGitignoreExists(projectRoot, normalizedPath);
378
- results.push(...gitignoreCheck.results);
379
-
380
- if (!gitignoreCheck.gitignoreExists) {
381
- return results;
382
- }
383
-
384
- // Read .gitignore content and process it
385
- const gitignoreContent = fs.readFileSync(gitignoreCheck.gitignorePath, 'utf8');
386
- const relevantLines = getRelevantGitignoreLines(gitignoreContent);
387
- const dirName = path.basename(normalizedPath);
388
-
389
- // Check final destination directory ignore patterns
390
- const dirResults = checkOutputDirIgnorePatterns(
391
- projectRoot, normalizedPath, outputPath, dirName, relevantLines
392
- );
393
- results.push(...dirResults);
394
-
395
- // Check HTML files ignore patterns
396
- const htmlResults = checkHtmlFilesGitignore(
397
- projectRoot, normalizedPath, outputPath, relevantLines
398
- );
399
- results.push(...htmlResults);
400
-
401
- return results;
402
- } catch (error) {
403
- Logger.error('Error checking final destination directory gitignore status:', error);
404
- return [{
405
- name: 'Final destination directory gitignore check',
406
- success: false,
407
- details: `Error: ${error.message}`
408
- }];
409
- }
410
- }
411
-
412
- module.exports = {
413
- checkDestinationGitIgnore
414
- };