spec-up-t 1.3.0 → 1.4.0
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 +13 -0
- package/assets/compiled/body.js +18 -12
- package/assets/compiled/head.css +8 -6
- package/assets/css/collapse-definitions.css +0 -1
- package/assets/css/counter.css +10 -22
- package/assets/css/create-pdf.css +4 -2
- package/assets/css/create-term-filter.css +4 -4
- package/assets/css/definition-buttons-container.css +60 -0
- package/assets/css/{pdf-download.css → download-pdf-docx.css} +9 -5
- package/assets/css/insert-trefs.css +7 -0
- package/assets/css/sidebar-toc.css +2 -1
- package/assets/css/terms-and-definitions.css +73 -22
- package/assets/js/add-href-to-snapshot-link.js +16 -9
- package/assets/js/addAnchorsToTerms.js +2 -2
- package/assets/js/charts.js +10 -0
- package/assets/js/collapse-definitions.js +13 -2
- package/assets/js/collapse-meta-info.js +11 -9
- package/assets/js/definition-button-container-utils.js +82 -0
- package/assets/js/download-pdf-docx.js +68 -0
- package/assets/js/edit-term-buttons.js +77 -20
- package/assets/js/github-issues.js +35 -0
- package/assets/js/github-repo-info.js +144 -0
- package/assets/js/highlight-heading-plus-sibling-nodes.test.js +18 -0
- package/assets/js/insert-trefs.js +62 -13
- package/assets/js/mermaid-diagrams.js +11 -0
- package/assets/js/terminology-section-utility-container/README.md +107 -0
- package/assets/js/terminology-section-utility-container/create-alphabet-index.js +17 -0
- package/assets/js/{create-term-filter.js → terminology-section-utility-container/create-term-filter.js} +11 -44
- package/assets/js/terminology-section-utility-container/hide-show-utility-container.js +21 -0
- package/assets/js/terminology-section-utility-container/search.js +203 -0
- package/assets/js/terminology-section-utility-container.js +203 -0
- package/assets/js/tooltips.js +283 -0
- package/config/asset-map.json +26 -18
- package/index.js +57 -390
- package/package.json +5 -2
- package/src/add-remove-xref-source.js +20 -21
- package/src/collect-external-references.js +8 -337
- package/src/collect-external-references.test.js +440 -33
- package/src/configure.js +8 -109
- package/src/create-docx.js +7 -6
- package/src/create-pdf.js +15 -14
- package/src/freeze-spec-data.js +46 -0
- package/src/git-info.test.js +76 -0
- package/src/health-check/destination-gitignore-checker.js +5 -3
- package/src/health-check/external-specs-checker.js +5 -4
- package/src/health-check/specs-configuration-checker.js +2 -1
- package/src/health-check/term-references-checker.js +5 -3
- package/src/health-check/terms-intro-checker.js +2 -1
- package/src/health-check/tref-term-checker.js +8 -7
- package/src/health-check.js +8 -7
- package/src/init.js +3 -2
- package/src/install-from-boilerplate/add-gitignore-entries.js +3 -2
- package/src/install-from-boilerplate/add-scripts-keys.js +5 -4
- package/src/install-from-boilerplate/boilerplate/.github/workflows/menu.yml +74 -97
- package/src/install-from-boilerplate/boilerplate/README.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/spec-head.md +2 -2
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/composability.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/compost.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/fertilizer.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/mulch.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/pruning.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/seedling.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/soil.md +11 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/watering.md +3 -0
- package/src/install-from-boilerplate/boilerplate/specs.json +24 -10
- package/src/install-from-boilerplate/config-scripts-keys.js +3 -3
- package/src/install-from-boilerplate/config-system-files.js +0 -1
- package/src/install-from-boilerplate/copy-boilerplate.js +2 -1
- package/src/install-from-boilerplate/copy-system-files.js +4 -3
- package/src/install-from-boilerplate/custom-update.js +12 -1
- package/src/install-from-boilerplate/help.txt +1 -1
- package/src/install-from-boilerplate/menu.sh +6 -6
- package/src/json-key-validator.js +17 -11
- package/src/markdown-it/README.md +207 -0
- package/src/markdown-it/definition-lists.js +397 -0
- package/src/markdown-it/index.js +83 -0
- package/src/markdown-it/link-enhancement.js +98 -0
- package/src/markdown-it/plugins.js +118 -0
- package/src/markdown-it/table-enhancement.js +97 -0
- package/src/markdown-it/template-tag-syntax.js +152 -0
- package/src/parsers/index.js +16 -0
- package/src/parsers/spec-parser.js +152 -0
- package/src/parsers/spec-parser.test.js +109 -0
- package/src/parsers/template-tag-parser.js +277 -0
- package/src/parsers/template-tag-parser.test.js +107 -0
- package/src/pipeline/configuration/configure-starterpack.js +200 -0
- package/src/{create-external-specs-list.js → pipeline/configuration/create-external-specs-list.js} +13 -12
- package/src/{create-term-index.js → pipeline/configuration/create-term-index.js} +19 -18
- package/src/{create-versions-index.js → pipeline/configuration/create-versions-index.js} +4 -3
- package/src/{insert-term-index.js → pipeline/configuration/insert-term-index.js} +2 -2
- package/src/pipeline/configuration/prepare-spec-configuration.js +70 -0
- package/src/pipeline/parsing/apply-markdown-it-extensions.js +35 -0
- package/src/pipeline/parsing/create-markdown-parser.js +94 -0
- package/src/pipeline/parsing/create-markdown-parser.test.js +49 -0
- package/src/{html-dom-processor.js → pipeline/postprocessing/definition-list-postprocessor.js} +69 -10
- package/src/{escape-handler.js → pipeline/preprocessing/escape-processor.js} +3 -1
- package/src/{fix-markdown-files.js → pipeline/preprocessing/normalize-terminology-markdown.js} +41 -31
- package/src/pipeline/references/collect-external-references.js +307 -0
- package/src/pipeline/references/external-references-service.js +231 -0
- package/src/pipeline/references/fetch-terms-from-index.js +198 -0
- package/src/pipeline/references/match-term.js +34 -0
- package/src/{collectExternalReferences/matchTerm.test.js → pipeline/references/match-term.test.js} +8 -2
- package/src/pipeline/references/process-xtrefs-data.js +94 -0
- package/src/pipeline/references/xtref-utils.js +166 -0
- package/src/pipeline/rendering/render-spec-document.js +146 -0
- package/src/pipeline/rendering/render-utils.js +154 -0
- package/src/utils/LOGGER.md +81 -0
- package/src/utils/{doesUrlExist.js → does-url-exist.js} +4 -3
- package/src/utils/fetch.js +5 -4
- package/src/utils/file-opener.js +3 -2
- package/src/utils/git-info.js +77 -0
- package/src/utils/logger.js +74 -0
- package/src/utils/regex-patterns.js +471 -0
- package/src/utils/regex-patterns.test.js +281 -0
- package/templates/template.html +56 -21
- package/assets/js/create-alphabet-index.js +0 -60
- package/assets/js/hide-show-utility-container.js +0 -16
- package/assets/js/index.js +0 -87
- package/assets/js/pdf-download.js +0 -46
- package/assets/js/search.js +0 -365
- package/src/collectExternalReferences/fetchTermsFromIndex.js +0 -284
- package/src/collectExternalReferences/matchTerm.js +0 -32
- package/src/collectExternalReferences/processXTrefsData.js +0 -108
- package/src/freeze.js +0 -90
- package/src/install-from-boilerplate/boilerplate/.github/workflows/fetch-and-push-xrefs.yml.old +0 -42
- package/src/install-from-boilerplate/boilerplate/.github/workflows/render-specs.yml +0 -47
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-1.md +0 -13
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-2.md +0 -3
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-3.md +0 -3
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-4.md +0 -3
- package/src/markdown-it-extensions.js +0 -395
- package/src/references.js +0 -114
- /package/assets/css/{bootstrap.min.css → embedded-libraries/bootstrap.min.css} +0 -0
- /package/assets/css/{prism.css → embedded-libraries/prism.css} +0 -0
- /package/assets/css/{prism.dark.css → embedded-libraries/prism.dark.css} +0 -0
- /package/assets/css/{prism.default.css → embedded-libraries/prism.default.css} +0 -0
- /package/assets/js/{bootstrap.bundle.min.js → embedded-libraries/bootstrap.bundle.min.js} +0 -0
- /package/assets/js/{chart.js → embedded-libraries/chart.js} +0 -0
- /package/assets/js/{diff.min.js → embedded-libraries/diff.min.js} +0 -0
- /package/assets/js/{font-awesome.js → embedded-libraries/font-awesome.js} +0 -0
- /package/assets/js/{mermaid.js → embedded-libraries/mermaid.js} +0 -0
- /package/assets/js/{notyf.js → embedded-libraries/notyf.js} +0 -0
- /package/assets/js/{popper.js → embedded-libraries/popper.js} +0 -0
- /package/assets/js/{prism.dark.js → embedded-libraries/prism.dark.js} +0 -0
- /package/assets/js/{prism.default.js → embedded-libraries/prism.default.js} +0 -0
- /package/assets/js/{prism.js → embedded-libraries/prism.js} +0 -0
- /package/assets/js/{tippy.js → embedded-libraries/tippy.js} +0 -0
- /package/src/{escape-mechanism.js → pipeline/preprocessing/escape-placeholder-utils.js} +0 -0
- /package/src/utils/{isLineWithDefinition.js → is-line-with-definition.js} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Parser Tests - Functional Style
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that the specification parsing functions work correctly
|
|
5
|
+
* and maintain backward compatibility while providing better testability.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
createSpecParser
|
|
10
|
+
} = require('../pipeline/parsing/create-markdown-parser.js');
|
|
11
|
+
|
|
12
|
+
// Import functions directly from their modules since they're not exported through index
|
|
13
|
+
const {
|
|
14
|
+
parseSpecReference,
|
|
15
|
+
renderIndividualSpec,
|
|
16
|
+
hasSpec
|
|
17
|
+
} = require('./spec-parser');
|
|
18
|
+
|
|
19
|
+
// Tests for verifying the specification parsing functions work correctly
|
|
20
|
+
describe('Specification Parsing Functions', () => {
|
|
21
|
+
let mockGlobal, mockSpecCorpus;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockGlobal = {
|
|
25
|
+
definitions: [],
|
|
26
|
+
references: [],
|
|
27
|
+
specGroups: {},
|
|
28
|
+
noticeTitles: {},
|
|
29
|
+
currentFile: 'test.md'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
mockSpecCorpus = {
|
|
33
|
+
'RFC-2119': {
|
|
34
|
+
title: 'Key words for use in RFCs',
|
|
35
|
+
href: 'https://tools.ietf.org/html/rfc2119'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Set up global state
|
|
40
|
+
global.definitions = mockGlobal.definitions;
|
|
41
|
+
global.references = mockGlobal.references;
|
|
42
|
+
global.specGroups = mockGlobal.specGroups;
|
|
43
|
+
global.noticeTitles = mockGlobal.noticeTitles;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Test: Can the system parse specification references properly?
|
|
47
|
+
test('should parse spec reference correctly', () => {
|
|
48
|
+
const mockToken = { info: {} };
|
|
49
|
+
|
|
50
|
+
parseSpecReference(mockSpecCorpus, mockGlobal, mockToken, 'spec', 'RFC-2119');
|
|
51
|
+
|
|
52
|
+
expect(mockGlobal.specGroups.spec).toBeDefined();
|
|
53
|
+
expect(mockGlobal.specGroups.spec['RFC-2119']).toBeDefined();
|
|
54
|
+
expect(mockToken.info.spec).toBeDefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Test: Can the system render individual spec references as HTML?
|
|
58
|
+
test('should render individual spec correctly', () => {
|
|
59
|
+
const mockToken = {
|
|
60
|
+
info: {
|
|
61
|
+
spec: {
|
|
62
|
+
_name: 'RFC-2119',
|
|
63
|
+
title: 'Key words for use in RFCs'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = renderIndividualSpec(mockToken);
|
|
69
|
+
|
|
70
|
+
expect(result).toContain('class="spec-reference"');
|
|
71
|
+
expect(result).toContain('href="#ref:RFC-2119"');
|
|
72
|
+
expect(result).toContain('RFC-2119');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Test: Can the system determine if a specification exists?
|
|
76
|
+
test('should check if spec exists', () => {
|
|
77
|
+
expect(hasSpec(mockSpecCorpus, 'RFC-2119')).toBe(true);
|
|
78
|
+
expect(hasSpec(mockSpecCorpus, 'nonexistent-spec')).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Test: Does the spec parser factory create functional parsers?
|
|
82
|
+
test('createSpecParser should return a working parser object', () => {
|
|
83
|
+
const specParser = createSpecParser(mockSpecCorpus, mockGlobal);
|
|
84
|
+
|
|
85
|
+
expect(typeof specParser.parseSpecReference).toBe('function');
|
|
86
|
+
expect(typeof specParser.renderSpecReference).toBe('function');
|
|
87
|
+
expect(typeof specParser.hasSpec).toBe('function');
|
|
88
|
+
|
|
89
|
+
expect(specParser.hasSpec('RFC-2119')).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Test: Can the functions be combined and used together effectively?
|
|
93
|
+
test('functions should compose well', () => {
|
|
94
|
+
// Test that functions can be used in different combinations
|
|
95
|
+
const specParser = createSpecParser(mockSpecCorpus, mockGlobal);
|
|
96
|
+
|
|
97
|
+
// Should be independent functions
|
|
98
|
+
expect(typeof specParser).toBe('object');
|
|
99
|
+
expect(typeof specParser.parseSpecReference).toBe('function');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Test: Can individual functions be imported and used separately?
|
|
103
|
+
test('individual functions should be importable', () => {
|
|
104
|
+
// Test that individual functions can be imported and used
|
|
105
|
+
expect(typeof hasSpec).toBe('function');
|
|
106
|
+
expect(typeof parseSpecReference).toBe('function');
|
|
107
|
+
expect(typeof renderIndividualSpec).toBe('function');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Tag Parser - Functional Style
|
|
3
|
+
*
|
|
4
|
+
* This module provides pure functions for processing template-tag markdown extensions:
|
|
5
|
+
* - [[def: term, alias]] - Term definitions with optional aliases
|
|
6
|
+
* - [[ref: term]] - Internal term references
|
|
7
|
+
* - [[xref: spec, term]] - External specification term references
|
|
8
|
+
* - [[tref: spec, term, alias1, alias2, ...]] - External term references with multiple aliases
|
|
9
|
+
*
|
|
10
|
+
* The functional approach reduces cognitive complexity and makes functions easier to test
|
|
11
|
+
* as they are pure functions with clear inputs and outputs.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { findExternalSpecByKey } = require('../pipeline/references/external-references-service.js');
|
|
15
|
+
const { lookupXrefTerm } = require('../pipeline/rendering/render-utils.js');
|
|
16
|
+
const { whitespace, htmlComments, contentCleaning, externalReferences } = require('../utils/regex-patterns');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extracts the current file from token content for source tracking
|
|
20
|
+
* @param {Object} token - The markdown-it token
|
|
21
|
+
* @param {Object} globalState - Global state containing fallback currentFile
|
|
22
|
+
* @returns {string} The source file name
|
|
23
|
+
*/
|
|
24
|
+
function extractCurrentFile(token, globalState) {
|
|
25
|
+
const content = token.map ? token.map[0] : '';
|
|
26
|
+
const fileMatch = content && content.match && content.match(htmlComments.fileTracker);
|
|
27
|
+
return fileMatch ? fileMatch[1] : globalState.currentFile || 'unknown';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Main parsing entry point for template-tag constructs
|
|
32
|
+
* @param {Object} config - Configuration object containing specs and settings
|
|
33
|
+
* @param {Object} globalState - Global state object containing definitions, references, etc.
|
|
34
|
+
* @param {Object} token - The markdown-it token being processed
|
|
35
|
+
* @param {string} type - The type of construct (def, ref, xref, tref)
|
|
36
|
+
* @param {string} primary - The primary content/term
|
|
37
|
+
* @returns {string} The rendered HTML for the construct
|
|
38
|
+
*/
|
|
39
|
+
function parseTemplateTag(config, globalState, token, type, primary) {
|
|
40
|
+
if (!primary) return;
|
|
41
|
+
|
|
42
|
+
const currentFile = extractCurrentFile(token, globalState);
|
|
43
|
+
|
|
44
|
+
switch (type) {
|
|
45
|
+
case 'def':
|
|
46
|
+
return parseDef(globalState, token, primary, currentFile);
|
|
47
|
+
case 'xref':
|
|
48
|
+
return parseXref(config, token);
|
|
49
|
+
case 'tref':
|
|
50
|
+
return parseTref(token);
|
|
51
|
+
default:
|
|
52
|
+
return parseRef(globalState, primary);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Processes [[def: term, alias1, alias2, ...]] constructs
|
|
58
|
+
* Creates definition entries and generates HTML spans with proper IDs
|
|
59
|
+
* Format: "Primary Alias (alias2, alias3, original-term)" similar to tref
|
|
60
|
+
* @param {Object} globalState - Global state to store definitions
|
|
61
|
+
* @param {Object} token - The markdown-it token
|
|
62
|
+
* @param {string} primary - The primary term content
|
|
63
|
+
* @param {string} currentFile - The source file containing this definition
|
|
64
|
+
* @returns {string} HTML span elements with term IDs
|
|
65
|
+
*/
|
|
66
|
+
function parseDef(globalState, token, primary, currentFile) {
|
|
67
|
+
const termName = token.info.args[0];
|
|
68
|
+
const aliases = token.info.args.slice(1).filter(Boolean); // Get all aliases after the term
|
|
69
|
+
|
|
70
|
+
// Store definition in global state for validation and cross-referencing
|
|
71
|
+
globalState.definitions.push({
|
|
72
|
+
term: termName,
|
|
73
|
+
alias: aliases[0] || null, // First alias, or null if no aliases
|
|
74
|
+
source: currentFile
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Determine the primary display term (first alias if available, otherwise original term)
|
|
78
|
+
const primaryDisplayTerm = aliases.length > 0 ? aliases[0] : termName;
|
|
79
|
+
|
|
80
|
+
// Build the display text format: "Primary (alias1, alias2, original-term)"
|
|
81
|
+
let displayText = primaryDisplayTerm;
|
|
82
|
+
|
|
83
|
+
if (aliases.length > 0) {
|
|
84
|
+
// Collect all additional terms to show in parentheses
|
|
85
|
+
const parentheticalContent = [];
|
|
86
|
+
|
|
87
|
+
// Add remaining aliases (after the first one used as primary)
|
|
88
|
+
if (aliases.length > 1) {
|
|
89
|
+
parentheticalContent.push(...aliases.slice(1));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Add original term if it's different from the primary display term
|
|
93
|
+
if (termName !== primaryDisplayTerm) {
|
|
94
|
+
parentheticalContent.push(`<span class='term-local-original-term' title='original term'>${termName}</span>`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Append parenthetical terms if any exist
|
|
98
|
+
if (parentheticalContent.length > 0) {
|
|
99
|
+
displayText += ` <span class='term-local-parenthetical-terms'>(${parentheticalContent.join(', ')})</span>`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Generate HTML spans for each term/alias combination
|
|
104
|
+
// This creates anchor points that can be referenced by links
|
|
105
|
+
// IDs stay intact - we create an ID for the original term and each alias
|
|
106
|
+
return token.info.args.reduce((acc, syn) => {
|
|
107
|
+
// Generate a unique term ID by normalizing the synonym: replace whitespace with hyphens and convert to lowercase. The ID is used for fragment identifier (hash) in the URL, which in turn can be used for an anchor in a web page.
|
|
108
|
+
const termId = `term:${syn.replace(whitespace.oneOrMore, '-').toLowerCase()}`;
|
|
109
|
+
return `<span id="${termId}">${acc}</span>`;
|
|
110
|
+
}, displayText);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Processes [[ref: term]] constructs
|
|
115
|
+
* Creates internal links to locally defined terms
|
|
116
|
+
* @param {Object} globalState - Global state to track references
|
|
117
|
+
* @param {string} primary - The term to reference
|
|
118
|
+
* @returns {string} HTML anchor element linking to local term
|
|
119
|
+
*/
|
|
120
|
+
function parseRef(globalState, primary) {
|
|
121
|
+
// Track this reference for validation purposes
|
|
122
|
+
globalState.references.push(primary);
|
|
123
|
+
|
|
124
|
+
// Create internal link to the term definition
|
|
125
|
+
const termId = primary.replace(whitespace.oneOrMore, '-').toLowerCase();
|
|
126
|
+
return `<a class="term-reference" href="#term:${termId}">${primary}</a>`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Processes [[xref: spec, term, alias, ...]] constructs
|
|
131
|
+
* Creates links to external specification terms with tooltips
|
|
132
|
+
* Uses primaryDisplayTerm concept: shows first alias if available, otherwise shows the term itself
|
|
133
|
+
* @param {Object} config - Configuration containing external specs
|
|
134
|
+
* @param {Object} token - The markdown-it token
|
|
135
|
+
* @returns {string} HTML anchor element linking to external term
|
|
136
|
+
*/
|
|
137
|
+
function parseXref(config, token) {
|
|
138
|
+
const externalSpec = findExternalSpecByKey(config, token.info.args[0]);
|
|
139
|
+
const url = externalSpec?.gh_page || '#';
|
|
140
|
+
const termName = token.info.args[1];
|
|
141
|
+
const aliases = token.info.args.slice(2).filter(Boolean); // Get all aliases after the term
|
|
142
|
+
const term = termName.replace(whitespace.oneOrMore, '-').toLowerCase();
|
|
143
|
+
const xrefTerm = lookupXrefTerm(token.info.args[0], term);
|
|
144
|
+
|
|
145
|
+
// Determine the primary display term (first alias if available, otherwise original term)
|
|
146
|
+
const primaryDisplayTerm = aliases.length > 0 ? aliases[0] : termName;
|
|
147
|
+
|
|
148
|
+
// Build link attributes with both local and external href capabilities
|
|
149
|
+
let linkAttributes = `class="x-term-reference term-reference" data-local-href="#term:${token.info.args[0]}:${term}" href="${url}#term:${term}"`;
|
|
150
|
+
|
|
151
|
+
// Add tooltip content if term definition is available
|
|
152
|
+
if (xrefTerm && xrefTerm.content) {
|
|
153
|
+
const cleanContent = xrefTerm.content.replace(contentCleaning.quotes, '"').replace(contentCleaning.newlines, ' ');
|
|
154
|
+
linkAttributes += ` title="External term definition" data-term-content="${cleanContent}"`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return `<a ${linkAttributes}>${primaryDisplayTerm}</a>`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Processes [[tref: spec, term, alias, ...]] constructs
|
|
162
|
+
* Creates external term references with multiple aliases displayed in a readable format
|
|
163
|
+
* Format: "Primary Alias (alias1, alias2, original-term)"
|
|
164
|
+
* @param {Object} token - The markdown-it token
|
|
165
|
+
* @returns {string} HTML span element for external term reference
|
|
166
|
+
*/
|
|
167
|
+
function parseTref(token) {
|
|
168
|
+
const termName = token.info.args[1];
|
|
169
|
+
const aliases = token.info.args.slice(2).filter(Boolean); // Get all aliases after the term
|
|
170
|
+
|
|
171
|
+
// Determine the primary display term (first alias if available, otherwise original term)
|
|
172
|
+
const primaryDisplayTerm = aliases.length > 0 ? aliases[0] : termName;
|
|
173
|
+
|
|
174
|
+
// Build the display text format: "Primary (alias1, alias2, original-term)"
|
|
175
|
+
let displayText = primaryDisplayTerm;
|
|
176
|
+
|
|
177
|
+
if (aliases.length > 0) {
|
|
178
|
+
// Collect all additional terms to show in parentheses
|
|
179
|
+
const parentheticalContent = [];
|
|
180
|
+
|
|
181
|
+
// Add remaining aliases (after the first one used as primary)
|
|
182
|
+
if (aliases.length > 1) {
|
|
183
|
+
parentheticalContent.push(...aliases.slice(1));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Add original term if it's different from the primary display term
|
|
187
|
+
if (termName !== primaryDisplayTerm) {
|
|
188
|
+
parentheticalContent.push(`<span class='term-external-original-term' title='original term'>${termName}</span>`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Append parenthetical terms if any exist
|
|
192
|
+
if (parentheticalContent.length > 0) {
|
|
193
|
+
displayText += ` <span class='term-external-parenthetical-terms'>(${parentheticalContent.join(', ')})</span>`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const termId = `term:${termName.replace(whitespace.oneOrMore, '-').toLowerCase()}`;
|
|
198
|
+
const primaryAliasId = aliases.length > 0 ? `term:${aliases[0].replace(whitespace.oneOrMore, '-').toLowerCase()}` : '';
|
|
199
|
+
|
|
200
|
+
// Handle cases where we have aliases
|
|
201
|
+
if (aliases.length > 0 && aliases[0] !== termName) {
|
|
202
|
+
return `<span data-original-term="${termName}" class="term-external" id="${termId}"><span title="Externally defined as ${termName}" id="${primaryAliasId}">${displayText}</span></span>`;
|
|
203
|
+
} else {
|
|
204
|
+
return `<span title="Externally also defined as ${termName}" data-original-term="${termName}" class="term-external" id="${termId}">${displayText}</span>`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Parses an `[[xref:...]]` or `[[tref:...]]` string into a structured object.
|
|
210
|
+
* This function was moved from xtref-utils.js to consolidate parsing logic
|
|
211
|
+
* and prevent cross-module object mutation.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} xtref - Raw reference markup including brackets and prefix.
|
|
214
|
+
* @returns {{ externalSpec: string, term: string, referenceType: string, firstAlias?: string, aliases: string[] }}
|
|
215
|
+
*/
|
|
216
|
+
function processXTrefObject(xtref) {
|
|
217
|
+
const referenceTypeMatch = xtref.match(externalReferences.referenceType);
|
|
218
|
+
const referenceType = referenceTypeMatch ? referenceTypeMatch[1] : 'unknown';
|
|
219
|
+
|
|
220
|
+
const parts = xtref
|
|
221
|
+
.replace(externalReferences.openingTag, '')
|
|
222
|
+
.replace(externalReferences.closingTag, '')
|
|
223
|
+
.trim()
|
|
224
|
+
.split(externalReferences.argsSeparator);
|
|
225
|
+
|
|
226
|
+
const xtrefObject = {
|
|
227
|
+
externalSpec: parts[0].trim(),
|
|
228
|
+
term: parts[1].trim(),
|
|
229
|
+
referenceType
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Collect all aliases from parts after the term (index 1), trim and filter empties
|
|
233
|
+
const allAliases = parts.slice(2).map(p => p.trim()).filter(Boolean);
|
|
234
|
+
|
|
235
|
+
// Initialize both tref and xref alias arrays
|
|
236
|
+
xtrefObject.trefAliases = [];
|
|
237
|
+
xtrefObject.xrefAliases = [];
|
|
238
|
+
|
|
239
|
+
// Store aliases in the appropriate array based on reference type
|
|
240
|
+
if (referenceType === 'tref') {
|
|
241
|
+
xtrefObject.trefAliases = allAliases;
|
|
242
|
+
// Store the first tref alias separately as it has special meaning
|
|
243
|
+
if (allAliases.length > 0) {
|
|
244
|
+
xtrefObject.firstTrefAlias = allAliases[0];
|
|
245
|
+
}
|
|
246
|
+
} else if (referenceType === 'xref') {
|
|
247
|
+
xtrefObject.xrefAliases = allAliases;
|
|
248
|
+
// Store the first xref alias separately as it has special meaning
|
|
249
|
+
if (allAliases.length > 0) {
|
|
250
|
+
xtrefObject.firstXrefAlias = allAliases[0];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return xtrefObject;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Creates a template-tag parser function with bound configuration and global state.
|
|
259
|
+
* This provides a clean interface similar to the class-based approach but with functional benefits.
|
|
260
|
+
* @param {Object} config - Configuration object
|
|
261
|
+
* @param {Object} globalState - Global state object
|
|
262
|
+
* @returns {Function} A parser function that can be called with (token, type, primary)
|
|
263
|
+
*/
|
|
264
|
+
function createTemplateTagParser(config, globalState) {
|
|
265
|
+
return (token, type, primary) => parseTemplateTag(config, globalState, token, type, primary);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = {
|
|
269
|
+
createTemplateTagParser,
|
|
270
|
+
// Export individual functions for testing purposes
|
|
271
|
+
parseDef,
|
|
272
|
+
parseXref,
|
|
273
|
+
parseTref,
|
|
274
|
+
parseRef,
|
|
275
|
+
parseTemplateTag,
|
|
276
|
+
processXTrefObject
|
|
277
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Tag Parser Tests - Functional Style
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that the template tag parsing functions work correctly
|
|
5
|
+
* and maintain backward compatibility while providing better testability.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
createTemplateTagParser
|
|
10
|
+
} = require('../pipeline/parsing/create-markdown-parser.js');
|
|
11
|
+
|
|
12
|
+
// Import functions directly from their modules since they're not exported through index
|
|
13
|
+
const {
|
|
14
|
+
parseDef,
|
|
15
|
+
parseRef
|
|
16
|
+
} = require('./template-tag-parser');
|
|
17
|
+
|
|
18
|
+
// Tests for verifying the template tag parsing functions work correctly
|
|
19
|
+
describe('Template Tag Parsing Functions', () => {
|
|
20
|
+
let mockConfig, mockGlobal;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockConfig = {
|
|
24
|
+
specs: [{
|
|
25
|
+
external_specs: [{
|
|
26
|
+
external_spec: 'test-spec',
|
|
27
|
+
gh_page: 'https://example.com/spec'
|
|
28
|
+
}]
|
|
29
|
+
}]
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
mockGlobal = {
|
|
33
|
+
definitions: [],
|
|
34
|
+
references: [],
|
|
35
|
+
specGroups: {},
|
|
36
|
+
noticeTitles: {},
|
|
37
|
+
currentFile: 'test.md'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Set up global state
|
|
41
|
+
global.definitions = mockGlobal.definitions;
|
|
42
|
+
global.references = mockGlobal.references;
|
|
43
|
+
global.specGroups = mockGlobal.specGroups;
|
|
44
|
+
global.noticeTitles = mockGlobal.noticeTitles;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Test: Can the system parse definition markup into proper HTML?
|
|
48
|
+
test('should parse definition correctly', () => {
|
|
49
|
+
const mockToken = {
|
|
50
|
+
info: { args: ['test-term', 'alias'] }
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const result = parseDef(mockGlobal, mockToken, 'Test Term', 'test.md');
|
|
54
|
+
|
|
55
|
+
expect(result).toContain('id="term:test-term"');
|
|
56
|
+
expect(result).toContain('id="term:alias"');
|
|
57
|
+
expect(mockGlobal.definitions).toHaveLength(1);
|
|
58
|
+
expect(mockGlobal.definitions[0]).toEqual({
|
|
59
|
+
term: 'test-term',
|
|
60
|
+
alias: 'alias',
|
|
61
|
+
source: 'test.md'
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Test: Can the system parse reference markup into proper links?
|
|
66
|
+
test('should parse reference correctly', () => {
|
|
67
|
+
const result = parseRef(mockGlobal, 'test-term');
|
|
68
|
+
|
|
69
|
+
expect(result).toContain('href="#term:test-term"');
|
|
70
|
+
expect(result).toContain('class="term-reference"');
|
|
71
|
+
expect(mockGlobal.references).toContain('test-term');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Test: Does the parser factory create functional parsers?
|
|
75
|
+
test('createTemplateTagParser should return a working function', () => {
|
|
76
|
+
const templateTagParser = createTemplateTagParser(mockConfig, mockGlobal);
|
|
77
|
+
|
|
78
|
+
expect(typeof templateTagParser).toBe('function');
|
|
79
|
+
|
|
80
|
+
const mockToken = {
|
|
81
|
+
info: { args: ['test-term'] }
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const result = templateTagParser(mockToken, 'def', 'Test Term');
|
|
85
|
+
expect(result).toContain('id="term:test-term"');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Test: Are the functions pure and independently testable?
|
|
89
|
+
test('functions should be pure and testable', () => {
|
|
90
|
+
// Test that pure functions work independently
|
|
91
|
+
const testGlobal = { definitions: [], references: [] };
|
|
92
|
+
const testToken = { info: { args: ['pure-test'] } };
|
|
93
|
+
|
|
94
|
+
const result = parseDef(testGlobal, testToken, 'Pure Test', 'test.md');
|
|
95
|
+
|
|
96
|
+
expect(result).toContain('id="term:pure-test"');
|
|
97
|
+
expect(testGlobal.definitions).toHaveLength(1);
|
|
98
|
+
expect(mockGlobal.definitions).toHaveLength(0); // Original should be unchanged
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Test: Can individual functions be imported and used separately?
|
|
102
|
+
test('individual functions should be importable', () => {
|
|
103
|
+
// Test that individual functions can be imported and used
|
|
104
|
+
expect(typeof parseDef).toBe('function');
|
|
105
|
+
expect(typeof parseRef).toBe('function');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive configurator for the Spec-Up-T starter pack.
|
|
3
|
+
*
|
|
4
|
+
* The module collects starter metadata from the user, updates the first
|
|
5
|
+
* spec entry in `specs.json`, and persists the changes. It can be required
|
|
6
|
+
* programmatically or executed directly from the command line.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
|
|
13
|
+
const Logger = require('../../utils/logger');
|
|
14
|
+
|
|
15
|
+
const SPECS_KEY = 'specs';
|
|
16
|
+
const JSON_FILE_PATH = path.resolve(process.cwd(), 'specs.json');
|
|
17
|
+
|
|
18
|
+
const defaultQuestions = [
|
|
19
|
+
{ field: 'title', prompt: 'Enter title', defaultValue: 'Spec-Up-T Starterpack' },
|
|
20
|
+
{ field: 'description', prompt: 'Enter description', defaultValue: 'Create technical specifications in markdown. Based on the original Spec-Up, extended with Terminology tooling' },
|
|
21
|
+
{ field: 'author', prompt: 'Enter author', defaultValue: 'Trust over IP Foundation' },
|
|
22
|
+
{ field: 'account', prompt: 'Enter account', defaultValue: 'trustoverip' },
|
|
23
|
+
{ field: 'repo', prompt: 'Enter repo', defaultValue: 'spec-up-t-starter-pack' }
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Verifies that the specs.json file exists before prompting the user.
|
|
28
|
+
*/
|
|
29
|
+
function assertSpecsFileExists() {
|
|
30
|
+
if (fs.existsSync(JSON_FILE_PATH)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Logger.error(`Error: ${JSON_FILE_PATH} does not exist.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Reads and parses the specs.json file, providing helpful feedback on failure.
|
|
40
|
+
*
|
|
41
|
+
* @returns {object} Parsed JSON contents.
|
|
42
|
+
*/
|
|
43
|
+
function loadSpecsConfig() {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(fs.readFileSync(JSON_FILE_PATH, 'utf8'));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
Logger.error(`Error: Could not parse ${JSON_FILE_PATH}.`, error.message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Ensures the first spec entry exists and returns it for updates. Exits when the
|
|
54
|
+
* structure is malformed because continuing would corrupt the file.
|
|
55
|
+
*
|
|
56
|
+
* @param {object} config - Parsed configuration object from specs.json.
|
|
57
|
+
* @returns {{ config: object, primarySpec: object }} The config and its first spec entry.
|
|
58
|
+
*/
|
|
59
|
+
function resolvePrimarySpec(config) {
|
|
60
|
+
const specs = config[SPECS_KEY];
|
|
61
|
+
|
|
62
|
+
if (!Array.isArray(specs) || specs.length === 0 || !specs[0]) {
|
|
63
|
+
Logger.error(`Error: Invalid JSON structure. "${SPECS_KEY}[0]" is missing.`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { config, primarySpec: specs[0] };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Builds the prompt text with the default value appended for clarity.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} label - Question label displayed to the user.
|
|
74
|
+
* @param {string} defaultValue - Value shown as default.
|
|
75
|
+
* @returns {string} Prompt string formatted for readline.
|
|
76
|
+
*/
|
|
77
|
+
function formatPrompt(label, defaultValue) {
|
|
78
|
+
return `${label} (${defaultValue}): `;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Collects answers for all configuration questions from stdin/stdout.
|
|
83
|
+
*
|
|
84
|
+
* @param {Array<{ field: string, prompt: string, defaultValue: string }>} questions - Questions to ask.
|
|
85
|
+
* @param {NodeJS.ReadableStream} input - Input stream for readline.
|
|
86
|
+
* @param {NodeJS.WritableStream} output - Output stream for readline.
|
|
87
|
+
* @returns {Promise<object>} Map of field names to user answers.
|
|
88
|
+
*/
|
|
89
|
+
function promptForAnswers(questions, input, output) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const rl = readline.createInterface({ input, output });
|
|
92
|
+
const answers = {};
|
|
93
|
+
let index = 0;
|
|
94
|
+
|
|
95
|
+
const askNext = () => {
|
|
96
|
+
if (index >= questions.length) {
|
|
97
|
+
rl.close();
|
|
98
|
+
resolve(answers);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { field, prompt, defaultValue } = questions[index];
|
|
103
|
+
rl.question(formatPrompt(prompt, defaultValue), answer => {
|
|
104
|
+
answers[field] = answer ? answer.trim() : defaultValue;
|
|
105
|
+
index += 1;
|
|
106
|
+
askNext();
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
rl.on('error', error => {
|
|
111
|
+
rl.close();
|
|
112
|
+
reject(error);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
askNext();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Applies the collected answers to the spec configuration, keeping nested
|
|
121
|
+
* `source` fields isolated from top-level metadata.
|
|
122
|
+
*
|
|
123
|
+
* @param {object} primarySpec - The spec entry to mutate.
|
|
124
|
+
* @param {object} answers - User responses keyed by field.
|
|
125
|
+
*/
|
|
126
|
+
function applyAnswersToSpec(primarySpec, answers) {
|
|
127
|
+
if (!primarySpec.source) {
|
|
128
|
+
primarySpec.source = {};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Object.entries(answers).forEach(([field, value]) => {
|
|
132
|
+
if (['account', 'repo'].includes(field)) {
|
|
133
|
+
primarySpec.source[field] = value;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
primarySpec[field] = value;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Persists the updated configuration back to specs.json.
|
|
143
|
+
*
|
|
144
|
+
* @param {object} config - Configuration object with modifications applied.
|
|
145
|
+
*/
|
|
146
|
+
function persistUpdatedConfig(config) {
|
|
147
|
+
try {
|
|
148
|
+
fs.writeFileSync(JSON_FILE_PATH, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
|
|
149
|
+
Logger.success(`Successfully updated ${JSON_FILE_PATH}.`);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
Logger.error(`Error: Could not update ${JSON_FILE_PATH}.`, error.message);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Runs the interactive configuration workflow.
|
|
158
|
+
*
|
|
159
|
+
* @param {object} [options]
|
|
160
|
+
* @param {Array} [options.questions] - Override the default question set.
|
|
161
|
+
* @param {NodeJS.ReadableStream} [options.input] - Custom input stream for testing.
|
|
162
|
+
* @param {NodeJS.WritableStream} [options.output] - Custom output stream for testing.
|
|
163
|
+
* @returns {Promise<void>}
|
|
164
|
+
*/
|
|
165
|
+
async function runStarterpackConfigurator({
|
|
166
|
+
questions = defaultQuestions,
|
|
167
|
+
input = process.stdin,
|
|
168
|
+
output = process.stdout
|
|
169
|
+
} = {}) {
|
|
170
|
+
assertSpecsFileExists();
|
|
171
|
+
|
|
172
|
+
Logger.info(`\nWelcome to the Spec-Up-T Starterpack configuration tool!\n` +
|
|
173
|
+
'\nYou will be asked a series of questions to customize your project.\n' +
|
|
174
|
+
"Here’s what each field means:\n" +
|
|
175
|
+
'- "Title": The name of your project.\n' +
|
|
176
|
+
'- "Description": A brief explanation of your project.\n' +
|
|
177
|
+
'- "Author": The name of the person or organization creating the project.\n' +
|
|
178
|
+
'- "Account": The GitHub account or organization where the repository will be hosted.\n' +
|
|
179
|
+
'- "Repo": The name of the GitHub repository.\n\n' +
|
|
180
|
+
'Press Enter to accept the default value shown in parentheses.\n'
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const answers = await promptForAnswers(questions, input, output);
|
|
185
|
+
const { config, primarySpec } = resolvePrimarySpec(loadSpecsConfig());
|
|
186
|
+
applyAnswersToSpec(primarySpec, answers);
|
|
187
|
+
persistUpdatedConfig(config);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
Logger.error('Configuration aborted due to an unexpected error.', error.message);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (process.env.SPEC_UP_T_CONFIGURATOR_AUTORUN !== 'false') {
|
|
195
|
+
runStarterpackConfigurator();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = {
|
|
199
|
+
runStarterpackConfigurator
|
|
200
|
+
};
|