spec-up-t 1.4.1 → 1.6.0-beta.1

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 (39) hide show
  1. package/assets/compiled/body.js +3 -2
  2. package/assets/compiled/head.css +5 -5
  3. package/assets/css/embedded-libraries/bootstrap.min.css +1 -1
  4. package/assets/css/header-navbar.css +4 -4
  5. package/assets/css/index.css +5 -4
  6. package/assets/css/refs.css +30 -0
  7. package/assets/css/terms-and-definitions.css +89 -1
  8. package/assets/js/github-issues.js +3 -3
  9. package/assets/js/insert-irefs.js +214 -0
  10. package/config/asset-map.json +2 -1
  11. package/examples/read-console-messages.js +102 -0
  12. package/gulpfile.js +42 -1
  13. package/index.js +49 -1
  14. package/package.json +2 -1
  15. package/src/create-docx.js +13 -6
  16. package/src/create-pdf.js +22 -18
  17. package/src/health-check.js +47 -629
  18. package/src/init.js +7 -3
  19. package/src/install-from-boilerplate/config-scripts-keys.js +1 -1
  20. package/src/markdown-it/README.md +2 -14
  21. package/src/markdown-it/index.js +1 -7
  22. package/src/parsers/template-tag-parser.js +42 -4
  23. package/src/pipeline/postprocessing/definition-list-postprocessor.js +4 -2
  24. package/src/pipeline/references/collect-external-references.js +101 -17
  25. package/src/pipeline/references/external-references-service.js +102 -21
  26. package/src/pipeline/references/fetch-terms-from-index.js +62 -1
  27. package/src/pipeline/references/process-xtrefs-data.js +67 -9
  28. package/src/pipeline/references/xtref-utils.js +22 -3
  29. package/src/pipeline/rendering/render-spec-document.js +0 -1
  30. package/src/run-healthcheck.js +177 -0
  31. package/src/utils/logger.js +129 -8
  32. package/src/utils/message-collector.js +144 -0
  33. package/src/utils/regex-patterns.js +3 -1
  34. package/templates/template.html +6 -6
  35. package/test/logger.test.js +290 -0
  36. package/test/message-collector.test.js +286 -0
  37. package/assets/css/insert-trefs.css +0 -1
  38. package/src/markdown-it/link-enhancement.js +0 -98
  39. package/src/utils/LOGGER.md +0 -81
package/src/init.js CHANGED
@@ -8,14 +8,14 @@ async function initialize() {
8
8
  try {
9
9
  // Ensure the .cache directory exists
10
10
  await fs.ensureDir(outputDir);
11
-
11
+
12
12
  // Check if the init script has already run
13
13
  if (await fs.pathExists(initFlagPath)) {
14
14
  return;
15
15
  }
16
16
 
17
17
  // Place the init script here
18
-
18
+
19
19
  // End of the init script
20
20
 
21
21
  // Create the init flag file
@@ -23,7 +23,11 @@ async function initialize() {
23
23
 
24
24
  Logger.success('Initialization complete.');
25
25
  } catch (error) {
26
- Logger.error(`Initialization failed: ${error.message}`);
26
+ Logger.error('Initialization failed', {
27
+ context: 'Failed to set up spec-up-t boilerplate files',
28
+ hint: 'Ensure you have write permissions in the current directory. Try running with appropriate permissions or in a different directory',
29
+ details: error.message
30
+ });
27
31
  }
28
32
  }
29
33
 
@@ -11,7 +11,7 @@ const configScriptsKeys = {
11
11
  "menu": "bash ./node_modules/spec-up-t/src/install-from-boilerplate/menu.sh",
12
12
  "addremovexrefsource": "node --no-warnings -e \"require('spec-up-t/src/add-remove-xref-source.js')\"",
13
13
  "configure": "node --no-warnings -e \"require('spec-up-t/src/configure.js')\"",
14
- "healthCheck": "node --no-warnings -e \"require('spec-up-t/src/health-check.js')\"",
14
+ "healthCheck": "node --no-warnings ./node_modules/spec-up-t/src/health-check.js",
15
15
  "custom-update": "npm update && node -e \"require('spec-up-t/src/install-from-boilerplate/custom-update.js')\""
16
16
  };
17
17
 
@@ -46,19 +46,7 @@ Our extensions override default renderer rules and add custom inline parsing rul
46
46
  - Creates template tokens with parsed information
47
47
  - Provides a renderer rule to convert template tokens to HTML
48
48
 
49
- ### 3. `link-enhancement.js`
50
-
51
- **Purpose**: Adds path-based attributes to links for CSS styling and JavaScript targeting.
52
-
53
- **Features**:
54
-
55
- - Extracts domain and path segments from URLs
56
- - Adds `path-0`, `path-1`, etc. attributes to anchor tags
57
- - Special handling for auto-detected links (linkify)
58
-
59
- **How it works**: Overrides `link_open` and `link_close` renderer rules.
60
-
61
- ### 4. `definition-lists.js`
49
+ ### 3. `definition-lists.js`
62
50
 
63
51
  **Purpose**: Advanced processing of definition lists for terminology and reference management.
64
52
 
@@ -75,7 +63,7 @@ Our extensions override default renderer rules and add custom inline parsing rul
75
63
  - Uses helper functions to analyze token structure and content
76
64
  - Applies CSS classes based on term types and context
77
65
 
78
- ### 5. `index.js`
66
+ ### 4. `index.js`
79
67
 
80
68
  **Purpose**: Main orchestrator that applies all enhancements in the correct order.
81
69
 
@@ -12,7 +12,6 @@
12
12
  *
13
13
  * - TABLE ENHANCEMENT: Bootstrap styling and responsive wrappers
14
14
  * - TEMPLATE-TAG SYNTAX: Custom [[template-tag:args]] syntax processing
15
- * - LINK ENHANCEMENT: Path-based attributes for links
16
15
  * - DEFINITION LISTS: Advanced terminology and reference list handling
17
16
  *
18
17
  * This modular approach makes the code more maintainable and easier to
@@ -22,7 +21,6 @@
22
21
  // Import all the specialized enhancement modules
23
22
  const applyTableEnhancements = require('./table-enhancement');
24
23
  const applyTemplateTagSyntax = require('./template-tag-syntax');
25
- const applyLinkEnhancements = require('./link-enhancement');
26
24
  const applyDefinitionListEnhancements = require('./definition-lists');
27
25
 
28
26
  /**
@@ -64,10 +62,7 @@ function applyMarkdownItExtensions(md, templates = []) {
64
62
  // 2. Template-tag syntax - should be applied early as other modules may depend on it
65
63
  applyTemplateTagSyntax(md, templates);
66
64
 
67
- // 3. Link enhancements - independent, can be applied anytime
68
- applyLinkEnhancements(md);
69
-
70
- // 4. Definition lists - depends on template-tag syntax for term type detection
65
+ // 3. Definition lists - depends on template-tag syntax for term type detection
71
66
  applyDefinitionListEnhancements(md);
72
67
 
73
68
  // The markdown-it instance is now fully enhanced and ready for use
@@ -79,5 +74,4 @@ module.exports = applyMarkdownItExtensions;
79
74
  // Also export individual modules for fine-grained control if needed
80
75
  module.exports.tableEnhancements = applyTableEnhancements;
81
76
  module.exports.templateTagSyntax = applyTemplateTagSyntax;
82
- module.exports.linkEnhancements = applyLinkEnhancements;
83
77
  module.exports.definitionLists = applyDefinitionListEnhancements;
@@ -14,6 +14,7 @@
14
14
  const { findExternalSpecByKey } = require('../pipeline/references/external-references-service.js');
15
15
  const { lookupXrefTerm } = require('../pipeline/rendering/render-utils.js');
16
16
  const { whitespace, htmlComments, contentCleaning, externalReferences } = require('../utils/regex-patterns');
17
+ const Logger = require('../utils/logger.js');
17
18
 
18
19
  /**
19
20
  * Extracts the current file from token content for source tracking
@@ -32,7 +33,7 @@ function extractCurrentFile(token, globalState) {
32
33
  * @param {Object} config - Configuration object containing specs and settings
33
34
  * @param {Object} globalState - Global state object containing definitions, references, etc.
34
35
  * @param {Object} token - The markdown-it token being processed
35
- * @param {string} type - The type of construct (def, ref, xref, tref)
36
+ * @param {string} type - The type of construct (def, ref, iref, xref, tref)
36
37
  * @param {string} primary - The primary content/term
37
38
  * @returns {string} The rendered HTML for the construct
38
39
  */
@@ -44,6 +45,8 @@ function parseTemplateTag(config, globalState, token, type, primary) {
44
45
  switch (type) {
45
46
  case 'def':
46
47
  return parseDef(globalState, token, primary, currentFile);
48
+ case 'iref':
49
+ return parseIref(globalState, primary);
47
50
  case 'xref':
48
51
  return parseXref(config, token);
49
52
  case 'tref':
@@ -129,17 +132,41 @@ function parseRef(globalState, primary) {
129
132
  return `<a class="term-reference" href="#term:${termId}">${primary}</a>`;
130
133
  }
131
134
 
135
+ /**
136
+ * Processes [[iref: term]] constructs
137
+ * Creates a placeholder that will be replaced client-side with a copy of the term definition
138
+ * This allows inline copying of existing term definitions from the terms-and-definitions-list
139
+ * @param {Object} globalState - Global state to track inline references
140
+ * @param {string} primary - The term to inline copy
141
+ * @returns {string} HTML placeholder element that will be replaced by client-side script
142
+ */
143
+ function parseIref(globalState, primary) {
144
+ // Track this inline reference for validation purposes
145
+ globalState.references.push(primary);
146
+
147
+ // Create a placeholder span with data attribute containing the term to copy
148
+ // The client-side script (insert-irefs.js) will find this and replace it with
149
+ // a copy of the actual <dt> and <dd> elements from the terms-and-definitions-list
150
+ const termId = primary.replace(whitespace.oneOrMore, '-').toLowerCase();
151
+ return `<span class="iref-placeholder" data-iref-term="${termId}" data-iref-original="${primary}"></span>`;
152
+ }
153
+
132
154
  /**
133
155
  * Processes [[xref: spec, term, alias, ...]] constructs
134
156
  * Creates links to external specification terms with tooltips
135
157
  * Uses primaryDisplayTerm concept: shows first alias if available, otherwise shows the term itself
136
158
  * @param {Object} config - Configuration containing external specs
137
159
  * @param {Object} token - The markdown-it token
138
- * @returns {string} HTML anchor element linking to external term
160
+ * @returns {string} HTML anchor element linking to external term or error span if unresolved
139
161
  */
140
162
  function parseXref(config, token) {
141
163
  const externalSpec = findExternalSpecByKey(config, token.info.args[0]);
142
- const url = externalSpec?.gh_page || '#';
164
+
165
+ // If external spec cannot be found, return error indicator
166
+ if (!externalSpec) {
167
+ return `<span class="no-xref-found-message" title="External spec '${token.info.args[0]}' not found in configuration">xref cannot be resolved</span>`;
168
+ }
169
+
143
170
  const termName = token.info.args[1];
144
171
  const aliases = token.info.args.slice(2).filter(Boolean); // Get all aliases after the term
145
172
  const term = termName.replace(whitespace.oneOrMore, '-').toLowerCase();
@@ -148,8 +175,11 @@ function parseXref(config, token) {
148
175
  // Determine the primary display term (first alias if available, otherwise original term)
149
176
  const primaryDisplayTerm = aliases.length > 0 ? aliases[0] : termName;
150
177
 
178
+ // Build the href attribute using the external spec's gh_page
179
+ const href = `${externalSpec.gh_page}#term:${term}`;
180
+
151
181
  // Build link attributes with both local and external href capabilities
152
- let linkAttributes = `class="x-term-reference term-reference" data-local-href="#term:${token.info.args[0]}:${term}" href="${url}#term:${term}"`;
182
+ let linkAttributes = `class="x-term-reference term-reference" data-local-href="#term:${token.info.args[0]}:${term}" href="${href}"`;
153
183
 
154
184
  // Add tooltip content if term definition is available
155
185
  if (xrefTerm && xrefTerm.content) {
@@ -262,6 +292,13 @@ function processXTrefObject(xtref) {
262
292
  if (allAliases.length > 0) {
263
293
  xtrefObject.firstXrefAlias = allAliases[0];
264
294
  }
295
+
296
+ // Log error if xref has more than one alias
297
+ // xref should only have 0 or 1 alias, unlike tref which supports multiple aliases
298
+ if (allAliases.length > 1) {
299
+ const extraAliases = allAliases.slice(1).join(', ');
300
+ Logger.error(`Invalid xref syntax: [[xref: ${xtrefObject.externalSpec}, ${xtrefObject.term}, ${allAliases.join(', ')}]] has ${allAliases.length} aliases. Only the first alias "${allAliases[0]}" will be used. Extra aliases ignored: ${extraAliases}.`);
301
+ }
265
302
  }
266
303
 
267
304
  return xtrefObject;
@@ -282,6 +319,7 @@ module.exports = {
282
319
  createTemplateTagParser,
283
320
  // Export individual functions for testing purposes
284
321
  parseDef,
322
+ parseIref,
285
323
  parseXref,
286
324
  parseTref,
287
325
  parseRef,
@@ -65,7 +65,8 @@ function sortDefinitionTermsInHtml(html) {
65
65
  });
66
66
 
67
67
  // Return the modified HTML
68
- return dom.serialize();
68
+ // Extract only the body's innerHTML to avoid wrapping in <html><head></head><body> tags
69
+ return dom.window.document.body.innerHTML;
69
70
  }
70
71
 
71
72
  /**
@@ -340,7 +341,8 @@ function fixDefinitionListStructure(html) {
340
341
  }
341
342
 
342
343
  // Return the fixed HTML
343
- return dom.serialize();
344
+ // Extract only the body's innerHTML to avoid wrapping in <html><head></head><body> tags
345
+ return dom.window.document.body.innerHTML;
344
346
  }
345
347
 
346
348
  module.exports = {
@@ -13,6 +13,7 @@ const fs = require('fs-extra');
13
13
  const readlineSync = require('readline-sync');
14
14
 
15
15
  const Logger = require('../../utils/logger');
16
+ const messageCollector = require('../../utils/message-collector');
16
17
  const { shouldProcessFile } = require('../../utils/file-filter');
17
18
  const { getCurrentBranch } = require('../../utils/git-info');
18
19
  const { addNewXTrefsFromMarkdown, isXTrefInAnyFile } = require('./xtref-utils');
@@ -24,7 +25,8 @@ const { processXTrefObject } = require('../../parsers/template-tag-parser');
24
25
  * automated callers of `collectExternalReferences` continue to receive a fully rendered spec.
25
26
  */
26
27
  function renderSpecification() {
27
- require('../../../index.js')({ nowatch: true });
28
+ // Pass skipClear to preserve messages from collectExternalReferences
29
+ require('../../../index.js')({ nowatch: true, skipClear: true });
28
30
  }
29
31
 
30
32
  /**
@@ -44,7 +46,11 @@ function normalizeSpecConfiguration(config, { noSpecsMessage }) {
44
46
  const specs = Array.isArray(config?.specs) ? config.specs : [];
45
47
 
46
48
  if (specs.length === 0) {
47
- Logger.error(noSpecsMessage);
49
+ Logger.error(noSpecsMessage, {
50
+ context: 'specs.json is missing or has no specs array',
51
+ hint: 'Create a valid specs.json file in your project root. Run "npm run init" or copy from: https://github.com/trustoverip/spec-up-t-starter-pack',
52
+ details: 'The specs array is required to configure your specification'
53
+ });
48
54
  return null;
49
55
  }
50
56
 
@@ -63,7 +69,11 @@ function normalizeSpecConfiguration(config, { noSpecsMessage }) {
63
69
  */
64
70
  function extendXTrefs(config, xtrefs) {
65
71
  if (config.specs[0].external_specs_repos) {
66
- Logger.warn('Your specs.json file uses an outdated structure. Update it using: https://github.com/trustoverip/spec-up-t/blob/master/src/install-from-boilerplate/boilerplate/specs.json');
72
+ Logger.warn('Your specs.json file uses an outdated structure', {
73
+ context: 'The "external_specs_repos" field is deprecated',
74
+ hint: 'Update to the new structure using "external_specs" instead. See: https://github.com/trustoverip/spec-up-t/blob/master/src/install-from-boilerplate/boilerplate/specs.json',
75
+ details: 'External references may not work correctly with the old structure'
76
+ });
67
77
  return;
68
78
  }
69
79
 
@@ -116,7 +126,11 @@ function extendXTrefs(config, xtrefs) {
116
126
  try {
117
127
  xtref.branch = getCurrentBranch();
118
128
  } catch (error) {
119
- Logger.warn(`Could not get current branch for xtref ${xtref.externalSpec}:${xtref.term}: ${error.message}`);
129
+ Logger.warn(`Could not get current branch for xtref ${xtref.externalSpec}:${xtref.term}`, {
130
+ context: 'Git branch detection failed for external reference',
131
+ hint: 'Ensure you\'re in a git repository, or specify github_repo_branch in specs.json external_specs configuration',
132
+ details: `${error.message}. Using default: main`
133
+ });
120
134
  xtref.branch = 'main';
121
135
  }
122
136
  });
@@ -151,7 +165,7 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
151
165
  }
152
166
 
153
167
  const userInput = readlineSync.question(
154
- `❌ This external reference is not a valid URL:
168
+ `❌ This external reference is not a valid URL:
155
169
 
156
170
  Repository: ${repo.url},
157
171
 
@@ -193,14 +207,22 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
193
207
  const termsDir = spec?.spec_terms_directory;
194
208
 
195
209
  if (!specDir || !termsDir) {
196
- Logger.warn(`Spec entry is missing spec_directory or spec_terms_directory: ${JSON.stringify(spec)}`);
210
+ Logger.warn(`Spec entry is missing spec_directory or spec_terms_directory`, {
211
+ context: 'Invalid specs.json configuration',
212
+ hint: 'Ensure each spec in specs.json has both "spec_directory" and "spec_terms_directory" fields',
213
+ details: `Incomplete spec entry: ${JSON.stringify(spec)}`
214
+ });
197
215
  return directories;
198
216
  }
199
217
 
200
218
  const resolvedDir = path.join(specDir, termsDir);
201
219
 
202
220
  if (!fs.existsSync(resolvedDir)) {
203
- Logger.warn(`Spec terms directory does not exist: ${resolvedDir}`);
221
+ Logger.warn(`Spec terms directory does not exist: ${resolvedDir}`, {
222
+ context: 'Directory specified in specs.json not found',
223
+ hint: 'Create the directory or update the path in specs.json. Typically this should be "spec/term-definitions" or similar',
224
+ details: `Expected path: ${resolvedDir}`
225
+ });
204
226
  return directories;
205
227
  }
206
228
 
@@ -209,7 +231,11 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
209
231
  }, []);
210
232
 
211
233
  if (specTermsDirectories.length === 0) {
212
- Logger.warn('No spec terms directories found. Skipping external reference collection.');
234
+ Logger.warn('No spec terms directories found. Skipping external reference collection', {
235
+ context: 'Cannot collect external references without valid terminology directories',
236
+ hint: 'Check specs.json configuration. Ensure spec_directory and spec_terms_directory point to existing directories',
237
+ details: 'External trefs and xrefs will not be processed'
238
+ });
213
239
  return;
214
240
  }
215
241
 
@@ -232,7 +258,7 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
232
258
  );
233
259
 
234
260
  fileContents.forEach((content, filename) => {
235
- addNewXTrefsFromMarkdown(content, allXTrefs, filename, processXTrefObject);
261
+ addNewXTrefsFromMarkdown(content, allXTrefs, filename, processXTrefObject, externalSpecsRepos);
236
262
  });
237
263
 
238
264
  extendXTrefs(config, allXTrefs.xtrefs);
@@ -242,9 +268,17 @@ function processExternalReferences(config, GITHUB_API_TOKEN) {
242
268
  /**
243
269
  * Public entry point for the external reference collection stage.
244
270
  *
245
- * @param {{ pat?: string }} options - Optional overrides (GitHub PAT).
271
+ * @param {{ pat?: string, collectMessages?: boolean }} options - Optional overrides (GitHub PAT, message collection).
246
272
  */
247
273
  function collectExternalReferences(options = {}) {
274
+ // Start collecting messages if requested
275
+ const shouldCollectMessages = options.collectMessages !== false; // Collect by default
276
+
277
+ if (shouldCollectMessages) {
278
+ messageCollector.clearMessages();
279
+ messageCollector.startCollecting('collectExternalReferences');
280
+ }
281
+
248
282
  const config = fs.readJsonSync('specs.json');
249
283
  const normalizedConfig = normalizeSpecConfiguration(config, {
250
284
  noSpecsMessage: 'No specs defined in specs.json. Nothing to collect.'
@@ -252,6 +286,10 @@ function collectExternalReferences(options = {}) {
252
286
 
253
287
  // Bail out immediately if the specs.json file lacks the required specs collection.
254
288
  if (!normalizedConfig) {
289
+ if (shouldCollectMessages) {
290
+ messageCollector.stopCollecting();
291
+ messageCollector.saveMessages();
292
+ }
255
293
  return;
256
294
  }
257
295
 
@@ -259,8 +297,11 @@ function collectExternalReferences(options = {}) {
259
297
  const GITHUB_API_TOKEN = options.pat || process.env.GITHUB_API_TOKEN;
260
298
 
261
299
  if (!GITHUB_API_TOKEN) {
262
- Logger.warn('No GitHub Personal Access Token (PAT) found. Running without authentication (may hit rate limits).');
263
- Logger.info('For better performance, set up a PAT: https://trustoverip.github.io/spec-up-t-website/docs/getting-started/github-token\n');
300
+ Logger.warn('No GitHub Personal Access Token (PAT) found. Running without authentication', {
301
+ context: 'GitHub API requests will use unauthenticated access',
302
+ hint: 'Set GITHUB_PAT environment variable to increase rate limit from 60 to 5000 requests/hour. See: https://trustoverip.github.io/spec-up-t-website/docs/getting-started/github-token',
303
+ details: 'You may hit rate limits when fetching many external references'
304
+ });
264
305
  }
265
306
 
266
307
  // Communicate that the expected external_specs array is missing entirely.
@@ -268,7 +309,16 @@ function collectExternalReferences(options = {}) {
268
309
  Logger.info(
269
310
  'No external_specs array found on the first spec entry in specs.json. External reference collection is skipped.'
270
311
  );
271
- renderSpecification();
312
+
313
+ if (shouldCollectMessages) {
314
+ messageCollector.stopCollecting();
315
+ messageCollector.saveMessages().then(path => {
316
+ Logger.success(`Console messages saved to: ${path}`);
317
+ renderSpecification();
318
+ });
319
+ } else {
320
+ renderSpecification();
321
+ }
272
322
  return;
273
323
  }
274
324
 
@@ -277,7 +327,16 @@ function collectExternalReferences(options = {}) {
277
327
  Logger.info(
278
328
  'The external_specs array in specs.json is empty. Add external repositories to collect external references.'
279
329
  );
280
- renderSpecification();
330
+
331
+ if (shouldCollectMessages) {
332
+ messageCollector.stopCollecting();
333
+ messageCollector.saveMessages().then(path => {
334
+ Logger.success(`Console messages saved to: ${path}`);
335
+ renderSpecification();
336
+ });
337
+ } else {
338
+ renderSpecification();
339
+ }
281
340
  return;
282
341
  }
283
342
 
@@ -287,15 +346,40 @@ function collectExternalReferences(options = {}) {
287
346
  if (pipeline && typeof pipeline.then === 'function') {
288
347
  return pipeline
289
348
  .then(result => {
290
- renderSpecification();
291
- return result;
349
+ if (shouldCollectMessages) {
350
+ messageCollector.stopCollecting();
351
+ return messageCollector.saveMessages().then(path => {
352
+ Logger.success(`Console messages saved to: ${path}`);
353
+ renderSpecification();
354
+ return result;
355
+ });
356
+ } else {
357
+ renderSpecification();
358
+ return result;
359
+ }
292
360
  })
293
361
  .catch(error => {
294
362
  Logger.error('Rendering failed after collecting external references.', error);
363
+
364
+ if (shouldCollectMessages) {
365
+ messageCollector.stopCollecting();
366
+ messageCollector.saveMessages().catch(() => {
367
+ // Silent fail on save error
368
+ });
369
+ }
370
+
295
371
  throw error;
296
372
  });
297
373
  } else {
298
- renderSpecification();
374
+ if (shouldCollectMessages) {
375
+ messageCollector.stopCollecting();
376
+ messageCollector.saveMessages().then(path => {
377
+ Logger.success(`Console messages saved to: ${path}`);
378
+ renderSpecification();
379
+ });
380
+ } else {
381
+ renderSpecification();
382
+ }
299
383
  return pipeline;
300
384
  }
301
385
  }