@asyncapi/generator 2.5.0 → 2.7.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/CHANGELOG.md +146 -0
- package/Dockerfile +7 -8
- package/cli.js +7 -0
- package/docs/api.md +12 -3
- package/docs/asyncapi-document.md +2 -1
- package/docs/configuration-file.md +112 -33
- package/docs/file-templates.md +2 -0
- package/docs/generator-template-java.md +560 -0
- package/docs/generator-template.md +62 -29
- package/docs/hooks.md +3 -3
- package/docs/installation-guide.md +5 -51
- package/docs/migration-cli.md +70 -0
- package/docs/migration-nunjucks-react.md +144 -0
- package/docs/nunjucks-render-engine.md +6 -2
- package/docs/template.md +2 -2
- package/docs/usage.md +14 -32
- package/docs/versioning.md +3 -1
- package/lib/conditionalGeneration.js +162 -0
- package/lib/filtersRegistry.js +5 -1
- package/lib/generator.js +96 -43
- package/lib/hooksRegistry.js +8 -1
- package/lib/logMessages.js +6 -1
- package/lib/parser.js +10 -0
- package/lib/renderer/nunjucks.js +2 -2
- package/lib/templateConfigValidator.js +30 -1
- package/package.json +4 -6
- package/test/generator.test.js +1 -1
- package/test/hooksRegistry.test.js +173 -0
- package/test/integration.test.js +51 -4
- package/test/parser.test.js +100 -2
- package/test/templateConfigValidator.test.js +18 -1
- package/test/test-project/README.md +5 -1
- package/test/test-project/docker-compose.yml +7 -15
- package/test/test-project/package.json +0 -1
- package/test/test-project/test-project.test.js +6 -2
- package/test/test-project/test-registry.test.js +7 -2
- package/test/test-project/test.sh +6 -1
- package/test/test-project/verdaccio/config.yaml +6 -0
- package/test/test-templates/nunjucks-template/package-lock.json +110 -183
- package/test/test-templates/react-template/.ageneratorrc +33 -0
- package/test/test-templates/react-template/package.json +5 -20
- package/test/test-templates/react-template/template/conditionalFile.txt +0 -0
- package/test/test-templates/react-template/template/conditionalFolder/conditionalFile.txt +0 -0
- package/test/test-templates/react-template/template/conditionalFolder2/input.txt +0 -0
- package/test/test-templates/react-template/template/models.js +6 -0
- package/test/utils.test.js +53 -0
- package/test/test-project/test-entrypoint.sh +0 -12
- package/test/test-templates/react-template/__transpiled/test-file.md.js +0 -24
- package/test/test-templates/react-template/__transpiled/test-file.md.js.map +0 -1
- package/test/test-templates/react-template/package-lock.json +0 -4135
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const log = require('loglevel');
|
|
2
|
+
const logMessage = require('./logMessages');
|
|
3
|
+
const jmespath = require('jmespath');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Determines whether the generation of a file or folder should be skipped
|
|
7
|
+
* based on conditions defined in the template configuration.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} templateConfig - The template configuration containing conditional logic.
|
|
10
|
+
* @param {string} matchedConditionPath - The matched path used to find applicable conditions.
|
|
11
|
+
* @param {Object} templateParams - Parameters passed to the template.
|
|
12
|
+
* @param {AsyncAPIDocument} asyncapiDocument - The AsyncAPI document used for evaluating conditions.
|
|
13
|
+
* @returns {Promise<boolean>} A promise that resolves to `true` if the condition is met, allowing the file or folder to render; otherwise, resolves to `false`.
|
|
14
|
+
*/
|
|
15
|
+
async function isGenerationConditionMet (
|
|
16
|
+
templateConfig,
|
|
17
|
+
matchedConditionPath,
|
|
18
|
+
templateParams,
|
|
19
|
+
asyncapiDocument
|
|
20
|
+
) {
|
|
21
|
+
const conditionFilesGeneration = templateConfig?.conditionalFiles?.[matchedConditionPath] || {};
|
|
22
|
+
const conditionalGeneration = templateConfig?.conditionalGeneration?.[matchedConditionPath] || {};
|
|
23
|
+
|
|
24
|
+
const config = Object.keys(conditionFilesGeneration).length > 0
|
|
25
|
+
? conditionFilesGeneration
|
|
26
|
+
: conditionalGeneration;
|
|
27
|
+
|
|
28
|
+
const subject = config?.subject;
|
|
29
|
+
|
|
30
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
31
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
32
|
+
if (Object.keys(conditionFilesGeneration).length > 0 && subject) {
|
|
33
|
+
return conditionalFilesGenerationDeprecatedVersion(
|
|
34
|
+
asyncapiDocument,
|
|
35
|
+
templateConfig,
|
|
36
|
+
matchedConditionPath,
|
|
37
|
+
templateParams
|
|
38
|
+
);
|
|
39
|
+
} else if (Object.keys(conditionalGeneration).length > 0) {
|
|
40
|
+
// Case when the subject is present in conditionalGeneration
|
|
41
|
+
if (subject) {
|
|
42
|
+
return conditionalSubjectGeneration(
|
|
43
|
+
asyncapiDocument,
|
|
44
|
+
templateConfig,
|
|
45
|
+
matchedConditionPath
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return conditionalParameterGeneration(templateConfig,matchedConditionPath,templateParams);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Evaluates whether a template path should be conditionally generated
|
|
54
|
+
* based on a parameter defined in the template configuration.
|
|
55
|
+
* @private
|
|
56
|
+
* @async
|
|
57
|
+
* @function conditionalParameterGeneration
|
|
58
|
+
* @param {Object} templateConfig - The full template configuration object.
|
|
59
|
+
* @param {string} matchedConditionPath - The path of the file/folder being conditionally generated.
|
|
60
|
+
* @param {Object} templateParams - The parameters passed to the generator, usually user input or default values.
|
|
61
|
+
* @returns {Promise<boolean>} - Resolves to `true` if the parameter passes validation, `false` otherwise.
|
|
62
|
+
*/
|
|
63
|
+
async function conditionalParameterGeneration(templateConfig, matchedConditionPath, templateParams) {
|
|
64
|
+
const conditionalGenerationConfig = templateConfig.conditionalGeneration?.[matchedConditionPath];
|
|
65
|
+
const parameterName = conditionalGenerationConfig.parameter;
|
|
66
|
+
const parameterValue = templateParams[parameterName];
|
|
67
|
+
return validateStatus(parameterValue, matchedConditionPath, templateConfig);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Determines whether a file should be conditionally included based on the provided subject expression
|
|
72
|
+
* and optional validation logic defined in the template configuration.
|
|
73
|
+
* @private
|
|
74
|
+
* @param {Object} asyncapiDocument - The parsed AsyncAPI document instance used for context evaluation.
|
|
75
|
+
* @param {Object} templateConfig - The configuration object that contains `conditionalFiles` rules.
|
|
76
|
+
* @param {string} matchedConditionPath - The path of the file/folder being conditionally generated.
|
|
77
|
+
* @param {Object} templateParams - The parameters passed to the generator, usually user input or default values.
|
|
78
|
+
* @returns {Boolean} - Returns `true` if the file should be included; `false` if it should be skipped.
|
|
79
|
+
*/
|
|
80
|
+
async function conditionalFilesGenerationDeprecatedVersion (
|
|
81
|
+
asyncapiDocument,
|
|
82
|
+
templateConfig,
|
|
83
|
+
matchedConditionPath,
|
|
84
|
+
templateParams
|
|
85
|
+
) {
|
|
86
|
+
return conditionalSubjectGeneration(asyncapiDocument, templateConfig, matchedConditionPath, templateParams);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Determines whether a file should be conditionally included based on the provided subject expression
|
|
91
|
+
* and optional validation logic defined in the template configuration.
|
|
92
|
+
* @private
|
|
93
|
+
* @param {Object} asyncapiDocument - The parsed AsyncAPI document instance used for context evaluation.
|
|
94
|
+
* @param {Object} templateConfig - The configuration object that contains `conditionalFiles` rules.
|
|
95
|
+
* @param {String} matchedConditionPath - The relative path to the directory of the source file.
|
|
96
|
+
* @param {Object} templateParams - Parameters passed to the template.
|
|
97
|
+
* @returns {Boolean} - Returns `true` if the file should be included; `false` if it should be skipped.
|
|
98
|
+
*/
|
|
99
|
+
async function conditionalSubjectGeneration (
|
|
100
|
+
asyncapiDocument,
|
|
101
|
+
templateConfig,
|
|
102
|
+
matchedConditionPath,
|
|
103
|
+
templateParams
|
|
104
|
+
|
|
105
|
+
) {
|
|
106
|
+
const fileCondition = templateConfig.conditionalGeneration?.[matchedConditionPath] || templateConfig.conditionalFiles?.[matchedConditionPath];
|
|
107
|
+
if (!fileCondition || !fileCondition.subject) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const { subject } = fileCondition;
|
|
111
|
+
const server = templateParams.server && asyncapiDocument.servers().get(templateParams.server);
|
|
112
|
+
const source = jmespath.search({
|
|
113
|
+
...asyncapiDocument.json(),
|
|
114
|
+
...{
|
|
115
|
+
server: server ? server.json() : undefined,
|
|
116
|
+
},
|
|
117
|
+
}, subject);
|
|
118
|
+
|
|
119
|
+
if (!source) {
|
|
120
|
+
log.debug(logMessage.relativeSourceFileNotGenerated(matchedConditionPath, subject));
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return validateStatus(source, matchedConditionPath, templateConfig);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validates the argument value based on the provided validation schema.
|
|
128
|
+
*
|
|
129
|
+
* @param {any} argument The value to validate.
|
|
130
|
+
* @param {String} matchedConditionPath The matched condition path.
|
|
131
|
+
* @param {Object} templateConfig - The template configuration containing conditional logic.
|
|
132
|
+
* @return {Promise<Boolean>} A promise that resolves to false if the generation should be skipped, true otherwise.
|
|
133
|
+
*/
|
|
134
|
+
async function validateStatus(
|
|
135
|
+
argument,
|
|
136
|
+
matchedConditionPath,
|
|
137
|
+
templateConfig
|
|
138
|
+
) {
|
|
139
|
+
const validation = templateConfig.conditionalGeneration?.[matchedConditionPath]?.validate || templateConfig.conditionalFiles?.[matchedConditionPath]?.validate;
|
|
140
|
+
if (!validation) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const isValid = validation(argument);
|
|
145
|
+
|
|
146
|
+
if (!isValid) {
|
|
147
|
+
if (templateConfig.conditionalGeneration?.[matchedConditionPath]) {
|
|
148
|
+
log.debug(logMessage.conditionalGenerationMatched(matchedConditionPath));
|
|
149
|
+
} else {
|
|
150
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
151
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
152
|
+
log.debug(logMessage.conditionalFilesMatched(matchedConditionPath));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
isGenerationConditionMet
|
|
162
|
+
};
|
package/lib/filtersRegistry.js
CHANGED
|
@@ -6,6 +6,7 @@ const nunjucksFilters = require('@asyncapi/nunjucks-filters');
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Registers all template filters.
|
|
9
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
9
10
|
* @param {Object} nunjucks Nunjucks environment.
|
|
10
11
|
* @param {Object} templateConfig Template configuration.
|
|
11
12
|
* @param {String} templateDir Directory where template is located.
|
|
@@ -15,12 +16,13 @@ module.exports.registerFilters = async (nunjucks, templateConfig, templateDir, f
|
|
|
15
16
|
await registerLocalFilters(nunjucks, templateDir, filtersDir);
|
|
16
17
|
registerConfigFilters(nunjucks, templateDir, templateConfig);
|
|
17
18
|
|
|
18
|
-
// Register Nunjucks filters from the 'nunjucks-filters' module without needing to list them
|
|
19
|
+
// Register Nunjucks filters from the 'nunjucks-filters' module without needing to list them in package.json or .ageneratorrc file.
|
|
19
20
|
addFilters(nunjucks, nunjucksFilters);
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Registers the local template filters.
|
|
25
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
24
26
|
* @private
|
|
25
27
|
* @param {Object} nunjucks Nunjucks environment.
|
|
26
28
|
* @param {String} templateDir Directory where template is located.
|
|
@@ -68,6 +70,7 @@ function registerLocalFilters(nunjucks, templateDir, filtersDir) {
|
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* Registers the additionally configured filters.
|
|
73
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
71
74
|
* @private
|
|
72
75
|
* @param {Object} nunjucks Nunjucks environment.
|
|
73
76
|
* @param {String} templateDir Directory where template is located.
|
|
@@ -112,6 +115,7 @@ async function registerConfigFilters(nunjucks, templateDir, templateConfig) {
|
|
|
112
115
|
|
|
113
116
|
/**
|
|
114
117
|
* Add filter functions to Nunjucks environment. Only owned functions from the module are added.
|
|
118
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
115
119
|
* @private
|
|
116
120
|
* @param {Object} nunjucks Nunjucks environment.
|
|
117
121
|
* @param {Object} filters Module with functions.
|
package/lib/generator.js
CHANGED
|
@@ -2,7 +2,6 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const xfs = require('fs.extra');
|
|
4
4
|
const minimatch = require('minimatch');
|
|
5
|
-
const jmespath = require('jmespath');
|
|
6
5
|
const filenamify = require('filenamify');
|
|
7
6
|
const git = require('simple-git');
|
|
8
7
|
const log = require('loglevel');
|
|
@@ -10,12 +9,12 @@ const Arborist = require('@npmcli/arborist');
|
|
|
10
9
|
const Config = require('@npmcli/config');
|
|
11
10
|
const requireg = require('requireg');
|
|
12
11
|
const npmPath = requireg.resolve('npm').replace('index.js','');
|
|
13
|
-
|
|
14
12
|
const { isAsyncAPIDocument } = require('@asyncapi/parser/cjs/document');
|
|
15
13
|
|
|
16
14
|
const { configureReact, renderReact, saveRenderedReactContent } = require('./renderer/react');
|
|
17
15
|
const { configureNunjucks, renderNunjucks } = require('./renderer/nunjucks');
|
|
18
16
|
const { validateTemplateConfig } = require('./templateConfigValidator');
|
|
17
|
+
const { isGenerationConditionMet } = require('./conditionalGeneration');
|
|
19
18
|
const {
|
|
20
19
|
convertMapToObject,
|
|
21
20
|
isFileSystemPath,
|
|
@@ -135,7 +134,7 @@ class Generator {
|
|
|
135
134
|
enumerable: true,
|
|
136
135
|
get() {
|
|
137
136
|
if (!self.templateConfig.parameters?.[key]) {
|
|
138
|
-
throw new Error(`Template parameter "${key}" has not been defined in the
|
|
137
|
+
throw new Error(`Template parameter "${key}" has not been defined in the Generator Configuration. Please make sure it's listed there before you use it in your template.`);
|
|
139
138
|
}
|
|
140
139
|
return templateParams[key];
|
|
141
140
|
}
|
|
@@ -436,7 +435,7 @@ class Generator {
|
|
|
436
435
|
* @param {String} asyncapiString AsyncAPI string to use as source.
|
|
437
436
|
* @param {Object} [parseOptions={}] AsyncAPI Parser parse options. Check out {@link https://www.github.com/asyncapi/parser-js|@asyncapi/parser} for more information.
|
|
438
437
|
* @deprecated Use the `generate` function instead. Just change the function name and it works out of the box.
|
|
439
|
-
* @return {Promise}
|
|
438
|
+
* @return {Promise<TemplateRenderResult|undefined>}
|
|
440
439
|
*/
|
|
441
440
|
async generateFromString(asyncapiString, parseOptions = {}) {
|
|
442
441
|
const isParsableCompatible = asyncapiString && typeof asyncapiString === 'string';
|
|
@@ -466,7 +465,7 @@ class Generator {
|
|
|
466
465
|
* }
|
|
467
466
|
*
|
|
468
467
|
* @param {String} asyncapiURL Link to AsyncAPI file
|
|
469
|
-
* @return {Promise}
|
|
468
|
+
* @return {Promise<TemplateRenderResult|undefined>}
|
|
470
469
|
*/
|
|
471
470
|
async generateFromURL(asyncapiURL) {
|
|
472
471
|
const doc = await fetchSpec(asyncapiURL);
|
|
@@ -493,7 +492,7 @@ class Generator {
|
|
|
493
492
|
* }
|
|
494
493
|
*
|
|
495
494
|
* @param {String} asyncapiFile AsyncAPI file to use as source.
|
|
496
|
-
* @return {Promise}
|
|
495
|
+
* @return {Promise<TemplateRenderResult|undefined>}
|
|
497
496
|
*/
|
|
498
497
|
async generateFromFile(asyncapiFile) {
|
|
499
498
|
const doc = await readFile(asyncapiFile, { encoding: 'utf8' });
|
|
@@ -620,7 +619,7 @@ class Generator {
|
|
|
620
619
|
path: packagePath,
|
|
621
620
|
};
|
|
622
621
|
} catch (err) {
|
|
623
|
-
throw new Error(
|
|
622
|
+
throw new Error(`Installation failed: ${ err.message }`);
|
|
624
623
|
}
|
|
625
624
|
}
|
|
626
625
|
|
|
@@ -687,7 +686,7 @@ class Generator {
|
|
|
687
686
|
|
|
688
687
|
walker.on('directory', async (root, stats, next) => {
|
|
689
688
|
try {
|
|
690
|
-
this.ignoredDirHandler(root, stats, next);
|
|
689
|
+
await this.ignoredDirHandler(root, stats, next);
|
|
691
690
|
} catch (e) {
|
|
692
691
|
reject(e);
|
|
693
692
|
}
|
|
@@ -711,10 +710,23 @@ class Generator {
|
|
|
711
710
|
* @param {String} stats Information about the file.
|
|
712
711
|
* @param {Function} next Callback function
|
|
713
712
|
*/
|
|
714
|
-
ignoredDirHandler(root, stats, next) {
|
|
713
|
+
async ignoredDirHandler(root, stats, next) {
|
|
715
714
|
const relativeDir = path.relative(this.templateContentDir, path.resolve(root, stats.name));
|
|
716
715
|
const dirPath = path.resolve(this.targetDir, relativeDir);
|
|
717
|
-
|
|
716
|
+
const conditionalEntry = this.templateConfig?.conditionalGeneration?.[relativeDir];
|
|
717
|
+
let shouldGenerate = true;
|
|
718
|
+
if (conditionalEntry) {
|
|
719
|
+
shouldGenerate = await isGenerationConditionMet(
|
|
720
|
+
this.templateConfig,
|
|
721
|
+
relativeDir,
|
|
722
|
+
this.templateParams,
|
|
723
|
+
this.asyncapiDocument
|
|
724
|
+
);
|
|
725
|
+
if (!shouldGenerate) {
|
|
726
|
+
log.debug(logMessage.conditionalGenerationMatched(relativeDir));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (!shouldIgnoreDir(relativeDir) && shouldGenerate) {
|
|
718
730
|
xfs.mkdirpSync(dirPath);
|
|
719
731
|
}
|
|
720
732
|
next();
|
|
@@ -854,7 +866,7 @@ class Generator {
|
|
|
854
866
|
await writeFile(outputpath, renderContent);
|
|
855
867
|
}
|
|
856
868
|
}
|
|
857
|
-
|
|
869
|
+
|
|
858
870
|
/**
|
|
859
871
|
* Generates a file.
|
|
860
872
|
*
|
|
@@ -867,34 +879,61 @@ class Generator {
|
|
|
867
879
|
async generateFile(asyncapiDocument, fileName, baseDir) {
|
|
868
880
|
const sourceFile = path.resolve(baseDir, fileName);
|
|
869
881
|
const relativeSourceFile = path.relative(this.templateContentDir, sourceFile);
|
|
882
|
+
const relativeSourceDirectory = relativeSourceFile.split(path.sep)[0] || '.';
|
|
883
|
+
|
|
870
884
|
const targetFile = path.resolve(this.targetDir, this.maybeRenameSourceFile(relativeSourceFile));
|
|
871
885
|
const relativeTargetFile = path.relative(this.targetDir, targetFile);
|
|
872
|
-
|
|
886
|
+
let shouldGenerate = true;
|
|
887
|
+
|
|
873
888
|
if (shouldIgnoreFile(relativeSourceFile)) return;
|
|
889
|
+
|
|
890
|
+
if (!(await this.shouldOverwriteFile(relativeTargetFile))) return;
|
|
891
|
+
|
|
892
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
893
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
894
|
+
let conditionalPath = '';
|
|
895
|
+
if (
|
|
896
|
+
this.templateConfig.conditionalFiles &&
|
|
897
|
+
this.templateConfig.conditionalGeneration
|
|
898
|
+
) {
|
|
899
|
+
log.debug(
|
|
900
|
+
'Both \'conditionalFiles\' and \'conditionalGeneration\' are defined. Ignoring \'conditionalFiles\' and using \'conditionalGeneration\' only.'
|
|
901
|
+
);
|
|
902
|
+
}
|
|
874
903
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
904
|
+
if (this.templateConfig.conditionalGeneration?.[relativeSourceDirectory]) {
|
|
905
|
+
conditionalPath = relativeSourceDirectory;
|
|
906
|
+
} else if (this.templateConfig.conditionalGeneration?.[relativeSourceFile]) {
|
|
907
|
+
conditionalPath = relativeSourceFile;
|
|
908
|
+
} else
|
|
909
|
+
if (this.templateConfig.conditionalFiles?.[relativeSourceFile]) {
|
|
910
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
911
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
912
|
+
conditionalPath = relativeSourceDirectory;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (conditionalPath) {
|
|
916
|
+
shouldGenerate = await isGenerationConditionMet(
|
|
917
|
+
this.templateConfig,
|
|
918
|
+
conditionalPath,
|
|
919
|
+
this.templateParams,
|
|
920
|
+
asyncapiDocument
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (!shouldGenerate) {
|
|
925
|
+
if (this.templateConfig.conditionalFiles?.[relativeSourceFile]) {
|
|
926
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
927
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
928
|
+
return log.debug(logMessage.conditionalFilesMatched(relativeSourceFile));
|
|
893
929
|
}
|
|
930
|
+
|
|
931
|
+
return log.debug(logMessage.conditionalGenerationMatched(conditionalPath));
|
|
894
932
|
}
|
|
895
|
-
|
|
933
|
+
|
|
896
934
|
if (this.isNonRenderableFile(relativeSourceFile)) return await copyFile(sourceFile, targetFile);
|
|
897
935
|
await this.renderAndWriteToFile(asyncapiDocument, sourceFile, targetFile);
|
|
936
|
+
log.debug(`Successfully rendered template and wrote file ${relativeSourceFile} to location: ${targetFile}`);
|
|
898
937
|
}
|
|
899
938
|
|
|
900
939
|
/**
|
|
@@ -942,10 +981,9 @@ class Generator {
|
|
|
942
981
|
*/
|
|
943
982
|
isNonRenderableFile(fileName) {
|
|
944
983
|
const nonRenderableFiles = this.templateConfig.nonRenderableFiles || [];
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return false;
|
|
984
|
+
return Array.isArray(nonRenderableFiles) &&
|
|
985
|
+
(nonRenderableFiles.some(globExp => minimatch(fileName, globExp)) ||
|
|
986
|
+
(isReactTemplate(this.templateConfig) && !isJsFile(fileName)));
|
|
949
987
|
}
|
|
950
988
|
|
|
951
989
|
/**
|
|
@@ -968,19 +1006,34 @@ class Generator {
|
|
|
968
1006
|
* @private
|
|
969
1007
|
*/
|
|
970
1008
|
async loadTemplateConfig() {
|
|
1009
|
+
this.templateConfig = {};
|
|
1010
|
+
|
|
1011
|
+
// Try to load config from .ageneratorrc
|
|
1012
|
+
try {
|
|
1013
|
+
const rcConfigPath = path.resolve(this.templateDir, '.ageneratorrc');
|
|
1014
|
+
const yaml = await readFile(rcConfigPath, { encoding: 'utf8' });
|
|
1015
|
+
const yamlConfig = require('js-yaml').load(yaml);
|
|
1016
|
+
this.templateConfig = yamlConfig || {};
|
|
1017
|
+
|
|
1018
|
+
await this.loadDefaultValues();
|
|
1019
|
+
return;
|
|
1020
|
+
} catch (rcError) {
|
|
1021
|
+
// console.error('Could not load .ageneratorrc file:', rcError);
|
|
1022
|
+
log.debug('Could not load .ageneratorrc file:', rcError);
|
|
1023
|
+
// Continue to try package.json if .ageneratorrc fails
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// Try to load config from package.json
|
|
971
1027
|
try {
|
|
972
1028
|
const configPath = path.resolve(this.templateDir, CONFIG_FILENAME);
|
|
973
|
-
if (!fs.existsSync(configPath)) {
|
|
974
|
-
this.templateConfig = {};
|
|
975
|
-
return;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
1029
|
const json = await readFile(configPath, { encoding: 'utf8' });
|
|
979
1030
|
const generatorProp = JSON.parse(json).generator;
|
|
980
1031
|
this.templateConfig = generatorProp || {};
|
|
981
|
-
} catch (
|
|
982
|
-
|
|
1032
|
+
} catch (packageError) {
|
|
1033
|
+
// console.error('Could not load generator config from package.json:', packageError);
|
|
1034
|
+
log.debug('Could not load generator config from package.json:', packageError);
|
|
983
1035
|
}
|
|
1036
|
+
|
|
984
1037
|
await this.loadDefaultValues();
|
|
985
1038
|
}
|
|
986
1039
|
|
|
@@ -1085,4 +1138,4 @@ class Generator {
|
|
|
1085
1138
|
Generator.DEFAULT_TEMPLATES_DIR = DEFAULT_TEMPLATES_DIR;
|
|
1086
1139
|
Generator.TRANSPILED_TEMPLATE_LOCATION = TRANSPILED_TEMPLATE_LOCATION;
|
|
1087
1140
|
|
|
1088
|
-
module.exports = Generator;
|
|
1141
|
+
module.exports = Generator;
|
package/lib/hooksRegistry.js
CHANGED
|
@@ -9,7 +9,7 @@ const { exists, registerTypeScript } = require('./utils');
|
|
|
9
9
|
* @param {String} templateDir Directory where template is located.
|
|
10
10
|
* @param {String} hooksDir Directory where local hooks are located.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
async function registerHooks (hooks, templateConfig, templateDir, hooksDir) {
|
|
13
13
|
await registerLocalHooks(hooks, templateDir, hooksDir);
|
|
14
14
|
|
|
15
15
|
if (templateConfig && Object.keys(templateConfig).length > 0) await registerConfigHooks(hooks, templateDir, templateConfig);
|
|
@@ -122,3 +122,10 @@ function addHook(hooks, mod, config) {
|
|
|
122
122
|
});
|
|
123
123
|
return hooks;
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
registerHooks,
|
|
128
|
+
registerLocalHooks,
|
|
129
|
+
registerConfigHooks,
|
|
130
|
+
addHook
|
|
131
|
+
};
|
package/lib/logMessages.js
CHANGED
|
@@ -22,7 +22,6 @@ function packageNotAvailable(packageDetails) {
|
|
|
22
22
|
if (packageDetails?.pkgPath) {
|
|
23
23
|
return `Unable to resolve template location at ${packageDetails.pkgPath}. Package is not available locally.`;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
25
|
return `Template is not available locally and expected location is undefined. Known details are: ${JSON.stringify(packageDetails, null, 2)}`;
|
|
27
26
|
}
|
|
28
27
|
|
|
@@ -42,6 +41,11 @@ function skipOverwrite(testFilePath) {
|
|
|
42
41
|
return `Skipping overwrite for: ${testFilePath}`;
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
function conditionalGenerationMatched(conditionalPath) {
|
|
45
|
+
return `${conditionalPath} was not generated because condition specified for this location in template configuration in conditionalGeneration matched.`;
|
|
46
|
+
}
|
|
47
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
48
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
45
49
|
function conditionalFilesMatched(relativeSourceFile) {
|
|
46
50
|
return `${relativeSourceFile} was not generated because condition specified for this file in template configuration in conditionalFiles matched.`;
|
|
47
51
|
}
|
|
@@ -62,6 +66,7 @@ module.exports = {
|
|
|
62
66
|
installationDebugMessage,
|
|
63
67
|
templateSuccessfullyInstalled,
|
|
64
68
|
relativeSourceFileNotGenerated,
|
|
69
|
+
conditionalGenerationMatched,
|
|
65
70
|
conditionalFilesMatched,
|
|
66
71
|
compileEnabled,
|
|
67
72
|
skipOverwrite
|
package/lib/parser.js
CHANGED
|
@@ -50,6 +50,14 @@ parser.getProperApiDocument = (asyncapiDocument, templateConfig = {}) => {
|
|
|
50
50
|
return ConvertDocumentParserAPIVersion(asyncapiDocument, apiVersion);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Converts old parser options to the new format.
|
|
55
|
+
*
|
|
56
|
+
* @private - This function should not be used outside this module.
|
|
57
|
+
* @param {object} oldOptions - The old options to convert.
|
|
58
|
+
* @param {object} generator - The generator instance.
|
|
59
|
+
* @returns {object} The converted options.
|
|
60
|
+
*/
|
|
53
61
|
// The new options for the v2 Parser are different from those for the v1 version, but in order not to release Generator v2, we are converting the old options of Parser to the new ones.
|
|
54
62
|
function convertOldOptionsToNew(oldOptions, generator) {
|
|
55
63
|
if (!oldOptions) return;
|
|
@@ -143,3 +151,5 @@ function canReadFn(uri, canRead) {
|
|
|
143
151
|
}
|
|
144
152
|
return false;
|
|
145
153
|
}
|
|
154
|
+
|
|
155
|
+
module.exports.convertOldOptionsToNew = convertOldOptionsToNew;
|
package/lib/renderer/nunjucks.js
CHANGED
|
@@ -3,7 +3,7 @@ const nunjucksExport = module.exports;
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Configures Nunjucks templating system
|
|
6
|
-
*
|
|
6
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
7
7
|
* @private
|
|
8
8
|
* @param {boolean} debug flag
|
|
9
9
|
* @param {string} templateDir path
|
|
@@ -17,7 +17,7 @@ nunjucksExport.configureNunjucks = (debug, templateDir) => {
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Renders the template with nunjucks and returns a string.
|
|
20
|
-
*
|
|
20
|
+
* @deprecated This method is deprecated. For more details, see the release notes: https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.6.0
|
|
21
21
|
* @param {import('@asyncapi/parser').AsyncAPIDocument} asyncapiDocument
|
|
22
22
|
* @param {string} templateString template filecontent to be rendered with nunjucks
|
|
23
23
|
* @param {string} filePath path to the template file
|
|
@@ -24,9 +24,14 @@ const supportedParserAPIMajorVersions = [
|
|
|
24
24
|
* @return {Boolean}
|
|
25
25
|
*/
|
|
26
26
|
module.exports.validateTemplateConfig = (templateConfig, templateParams, asyncapiDocument) => {
|
|
27
|
-
|
|
27
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
28
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
29
|
+
const { parameters, supportedProtocols, conditionalFiles, conditionalGeneration, generator, apiVersion } = templateConfig;
|
|
28
30
|
|
|
31
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
32
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
29
33
|
validateConditionalFiles(conditionalFiles);
|
|
34
|
+
validateConditionalGeneration(conditionalGeneration);
|
|
30
35
|
isTemplateCompatible(generator, apiVersion);
|
|
31
36
|
isRequiredParamProvided(parameters, templateParams);
|
|
32
37
|
isProvidedTemplateRendererSupported(templateConfig);
|
|
@@ -148,6 +153,8 @@ function isServerProvidedInDocument(server, paramsServerName) {
|
|
|
148
153
|
if (typeof paramsServerName === 'string' && !server) throw new Error(`Couldn't find server with name ${paramsServerName}.`);
|
|
149
154
|
}
|
|
150
155
|
|
|
156
|
+
// conditionalFiles becomes deprecated with this PR, and soon will be removed.
|
|
157
|
+
// TODO: https://github.com/asyncapi/generator/issues/1553
|
|
151
158
|
/**
|
|
152
159
|
* Checks if conditional files are specified properly in the template
|
|
153
160
|
* @private
|
|
@@ -165,3 +172,25 @@ function validateConditionalFiles(conditionalFiles) {
|
|
|
165
172
|
});
|
|
166
173
|
}
|
|
167
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Validates conditionalGeneration settings in the template config.
|
|
178
|
+
* @private
|
|
179
|
+
* @param {Object} conditionalGeneration - The conditions specified in the template config.
|
|
180
|
+
*/
|
|
181
|
+
function validateConditionalGeneration(conditionalGeneration) {
|
|
182
|
+
if (!conditionalGeneration || typeof conditionalGeneration !== 'object') return;
|
|
183
|
+
|
|
184
|
+
for (const [fileName, def] of Object.entries(conditionalGeneration)) {
|
|
185
|
+
const { subject, parameter, validation } = def;
|
|
186
|
+
if (subject && typeof subject !== 'string')
|
|
187
|
+
throw new Error(`Invalid 'subject' for ${fileName}: ${subject}`);
|
|
188
|
+
if (parameter && typeof parameter !== 'string')
|
|
189
|
+
throw new Error(`Invalid 'parameter' for ${fileName}: ${parameter}`);
|
|
190
|
+
if (subject && parameter)
|
|
191
|
+
throw new Error(`Both 'subject' and 'parameter' cannot be defined for ${fileName}`);
|
|
192
|
+
if (typeof validation !== 'object')
|
|
193
|
+
throw new Error(`Invalid 'validation' object for ${fileName}: ${validation}`);
|
|
194
|
+
def.validate = ajv.compile(validation);
|
|
195
|
+
}
|
|
196
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asyncapi/generator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "The AsyncAPI generator. It can generate documentation, code, anything!",
|
|
5
5
|
"main": "./lib/generator.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,17 +12,15 @@
|
|
|
12
12
|
"npm": ">=8.19.0"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "npm run test:unit && npm run test:integration
|
|
15
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
16
16
|
"test:unit": "jest --coverage --testPathIgnorePatterns=integration --testPathIgnorePatterns=test-project",
|
|
17
17
|
"test:dev": "npm run test:unit -- --watchAll",
|
|
18
18
|
"test:integration": "npm run test:cleanup && jest --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
|
|
19
19
|
"test:integration:update": "jest --updateSnapshot --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
|
|
20
|
-
"test:cli": "node cli.js ./test/docs/dummy.yml ./test/test-templates/react-template -o test/output --force-write --debug && test -e test/output/test-file.md",
|
|
21
20
|
"test:cleanup": "rimraf \"test/temp\"",
|
|
22
21
|
"docs": "jsdoc2md --partial docs/jsdoc2md-handlebars/custom-sig-name.hbs docs/jsdoc2md-handlebars/main.hbs docs/jsdoc2md-handlebars/docs.hbs docs/jsdoc2md-handlebars/header.hbs docs/jsdoc2md-handlebars/defaultvalue.hbs docs/jsdoc2md-handlebars/link.hbs docs/jsdoc2md-handlebars/params-table.hbs --files lib/generator.js > docs/api.md",
|
|
23
22
|
"docker:build": "docker build -t asyncapi/generator:latest .",
|
|
24
23
|
"lint": "eslint --max-warnings 0 --config ../../.eslintrc --ignore-path ../../.eslintignore .",
|
|
25
|
-
"lint:tpl:validator": "eslint --fix --config ../../.eslintrc ../../.github/templates-list-validator",
|
|
26
24
|
"generate:readme:toc": "markdown-toc -i README.md",
|
|
27
25
|
"generate:assets": "npm run docs && npm run generate:readme:toc",
|
|
28
26
|
"bump:version": "npm --no-git-tag-version --allow-same-version version $VERSION"
|
|
@@ -49,10 +47,10 @@
|
|
|
49
47
|
"license": "Apache-2.0",
|
|
50
48
|
"homepage": "https://github.com/asyncapi/generator",
|
|
51
49
|
"dependencies": {
|
|
50
|
+
"@asyncapi/generator-hooks": "*",
|
|
52
51
|
"@asyncapi/generator-react-sdk": "^1.1.2",
|
|
53
52
|
"@asyncapi/multi-parser": "^2.1.1",
|
|
54
53
|
"@asyncapi/nunjucks-filters": "*",
|
|
55
|
-
"@asyncapi/generator-hooks": "*",
|
|
56
54
|
"@asyncapi/parser": "^3.0.14",
|
|
57
55
|
"@npmcli/arborist": "5.6.3",
|
|
58
56
|
"@npmcli/config": "^8.0.2",
|
|
@@ -82,7 +80,7 @@
|
|
|
82
80
|
"eslint-plugin-jest": "^23.8.2",
|
|
83
81
|
"eslint-plugin-react": "^7.34.1",
|
|
84
82
|
"eslint-plugin-sonarjs": "^0.5.0",
|
|
85
|
-
"fs-extra": "
|
|
83
|
+
"fs-extra": "11.2.0",
|
|
86
84
|
"jest": "^27.3.1",
|
|
87
85
|
"jsdoc-to-markdown": "^7.1.1",
|
|
88
86
|
"markdown-toc": "^1.2.0",
|
package/test/generator.test.js
CHANGED
|
@@ -51,7 +51,7 @@ describe('Generator', () => {
|
|
|
51
51
|
expect(gen.forceWrite).toStrictEqual(true);
|
|
52
52
|
expect(gen.install).toStrictEqual(true);
|
|
53
53
|
expect(gen.compile).toStrictEqual(false);
|
|
54
|
-
expect(() => gen.templateParams.test).toThrow('Template parameter "test" has not been defined in the
|
|
54
|
+
expect(() => gen.templateParams.test).toThrow('Template parameter "test" has not been defined in the Generator Configuration. Please make sure it\'s listed there before you use it in your template.');
|
|
55
55
|
|
|
56
56
|
// Mock params on templateConfig so it doesn't fail.
|
|
57
57
|
gen.templateConfig.parameters = { test: {} };
|