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.
- package/.github/copilot-instructions.md +2 -0
- package/assets/compiled/body.js +9 -7
- package/assets/compiled/head.css +6 -6
- package/assets/css/adjust-font-size.css +0 -4
- package/assets/css/download-pdf-docx.css +19 -20
- package/assets/css/header-navbar.css +0 -4
- package/assets/css/index.css +36 -0
- package/assets/css/terms-and-definitions.css +0 -1
- package/assets/css/validate-external-refs.css +198 -3
- package/assets/js/add-href-to-snapshot-link.js +11 -5
- package/assets/js/download-pdf-docx.js +20 -9
- package/assets/js/edit-term-buttons.js +71 -68
- package/assets/js/github-issues.js +27 -27
- package/assets/js/insert-irefs.js +1 -1
- package/assets/js/validate-external-refs.js +356 -7
- package/package.json +3 -3
- package/src/add-remove-xref-source.js +0 -5
- package/src/collect-external-references.test.js +98 -0
- package/src/install-from-boilerplate/boilerplate/gitignore +2 -2
- package/src/install-from-boilerplate/boilerplate/menu-wrapper.sh +19 -0
- package/src/install-from-boilerplate/boilerplate/specs.json +3 -6
- package/src/install-from-boilerplate/config-scripts-keys.js +1 -1
- package/src/install-from-boilerplate/config-system-files.js +1 -0
- package/src/install-from-boilerplate/custom-update.js +6 -3
- package/src/install-from-boilerplate/update-dependencies.js +105 -0
- package/src/pipeline/references/collect-external-references.js +12 -0
- package/src/pipeline/references/external-references-service.js +1 -1
- package/src/pipeline/rendering/render-spec-document.js +5 -1
- package/templates/template.html +43 -10
- package/src/health-check/destination-gitignore-checker.js +0 -414
- package/src/health-check/external-specs-checker.js +0 -287
- package/src/health-check/specs-configuration-checker.js +0 -387
- package/src/health-check/term-references-checker.js +0 -270
- package/src/health-check/terms-intro-checker.js +0 -82
- 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
|
|
package/templates/template.html
CHANGED
|
@@ -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
|
-
|
|
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
|
-
};
|