Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert templates in packages #219

Merged
merged 17 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
* [new Generator(templateName, targetDir, options)](#new_Generator_new)
* _instance_
* [.generate(asyncapiDocument)](#Generator+generate) ⇒ <code>Promise</code>
* [.generateFromString(asyncapiString)](#Generator+generateFromString) ⇒ <code>Promise</code>
* [.generateFromString(asyncapiString, [asyncApiFileLocation])](#Generator+generateFromString) ⇒ <code>Promise</code>
* [.generateFromFile(asyncapiFile)](#Generator+generateFromFile) ⇒ <code>Promise</code>
* [.getAllParameters(asyncapiDocument)](#Generator+getAllParameters)
* _static_
* [.getTemplateFile(templateName, filePath, options)](#Generator.getTemplateFile) ⇒ <code>Promise</code>

Expand Down Expand Up @@ -40,8 +41,7 @@ const generator = new Generator('html', path.resolve(__dirname, 'example'));
const path = require('path');
const generator = new Generator('html', path.resolve(__dirname, 'example'), {
templateParams: {
sidebarOrganization: 'byTags',
baseHref: '/async-docs/'
sidebarOrganization: 'byTags'
}
});
```
Expand Down Expand Up @@ -83,14 +83,15 @@ try {
```
<a name="Generator+generateFromString"></a>

### generator.generateFromString(asyncapiString) ⇒ <code>Promise</code>
### generator.generateFromString(asyncapiString, [asyncApiFileLocation]) ⇒ <code>Promise</code>
Generates files from a given template and AsyncAPI string.

**Kind**: instance method of [<code>Generator</code>](#Generator)

| Param | Type | Description |
| --- | --- | --- |
| asyncapiString | <code>String</code> | AsyncAPI string to use as source. |
| [asyncApiFileLocation] | <code>String</code> | AsyncAPI file location, used by the asyncapi-parser for references. |

**Example**
```js
Expand Down Expand Up @@ -154,6 +155,15 @@ try {
console.error(e);
}
```
<a name="Generator+getAllParameters"></a>

### generator.getAllParameters(asyncapiDocument)
**Kind**: instance method of [<code>Generator</code>](#Generator)

| Param | Type | Description |
| --- | --- | --- |
| asyncapiDocument | <code>AsyncAPIDocument</code> | AsyncAPI document to use as the source. |

<a name="Generator.getTemplateFile"></a>

### Generator.getTemplateFile(templateName, filePath, options) ⇒ <code>Promise</code>
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ asyncapi/generator -o ./output asyncapi.yml markdown
Options:

-V, --version output the version number
-w, --watch watches the template directory and the AsyncAPI document for change, and re-generate the files when they occur.
-o, --output <outputDir> directory where to put the generated files (default: the current directory)
-w, --watch watches the templates directory and the AsyncAPI document for changes, and re-generate the files when they occur
-o, --output <outputDir> directory where to put the generated files (defaults to current directory)
-d, --disable-hook <hookName> disable a specific hook
-n, --no-overwrite <glob> glob or path of the file(s) to skip when regenerating
-p, --param <name=value> additional param to pass to templates
-t, --templates <templateDir> directory where templates are located (default: Internal template folder)
-t, --templates <templateDir> directory where templates are located (defaults to internal templates directory)
--force-install forces the installation of the template dependencies
fmvilas marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added those suggestions to explain the flag as at first it was weird for me that there is a --force-install flag but I did not pass it and dependencies were installed anyway (at first run). Maybe the name is confusing, maybe reinstall or something 🤔

btw, why no -f flag?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The -f flag will be a generic --force flag. It would "force" everything. Including, for instance, the generation when the repo has unstaged changes. It's useful but it's probably another flag and another PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeap, since last release we have --force-write so --force-install now nicely adds to the list and then as you wrote we can have -f god to apply all force flags

-h, --help output usage information
```

Expand Down
41 changes: 38 additions & 3 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const path = require('path');
const os = require('os');
const fs = require('fs');
const npmi = require('npmi');
const program = require('commander');
const packageInfo = require('./package.json');
const mkdirp = require('mkdirp');
Expand All @@ -15,6 +17,7 @@ const green = text => `\x1b[32m${text}\x1b[0m`;

let asyncapiFile;
let template;
let templatesDir = Generator.DEFAULT_TEMPLATES_DIR;
const params = {};
const noOverwriteGlobs = [];
const disabledHooks = [];
Expand All @@ -30,6 +33,10 @@ const paramParser = v => {
return v;
};

const parseTemplatesDir = v => {
templatesDir = v;
};

const noOverwriteParser = v => noOverwriteGlobs.push(v);
const disableHooksParser = v => disabledHooks.push(v);

Expand All @@ -52,12 +59,13 @@ program
asyncapiFile = path.resolve(asyncAPIPath);
template = tmpl;
})
.option('-w, --watch', 'Watch the templates directory (--templates) for changes and re-generate when they occur')
.option('-w, --watch', 'watches the templates directory and the AsyncAPI document for changes, and re-generate the files when they occur')
.option('-o, --output <outputDir>', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd())
.option('-d, --disable-hook <hookName>', 'disable a specific hook', disableHooksParser)
.option('-n, --no-overwrite <glob>', 'glob or path of the file(s) to skip when regenerating', noOverwriteParser)
.option('-p, --param <name=value>', 'additional param to pass to templates', paramParser)
.option('-t, --templates <templateDir>', 'directory where templates are located (defaults to internal templates directory)', Generator.DEFAULT_TEMPLATES_DIR, path.resolve(__dirname, 'templates'))
.option('-t, --templates <templateDir>', 'directory where templates are located (defaults to internal templates directory)', parseTemplatesDir, Generator.DEFAULT_TEMPLATES_DIR)
.option('--force-install', 'forces the installation of the template dependencies')
fmvilas marked this conversation as resolved.
Show resolved Hide resolved
.parse(process.argv);

if (!asyncapiFile) {
Expand All @@ -68,6 +76,7 @@ if (!asyncapiFile) {
mkdirp(program.output, async err => {
if (err) return showErrorAndExit(err);
try {
await installTemplate(program.forceInstall);
await generate(program.output);
} catch (e) {
return showErrorAndExit(e);
Expand Down Expand Up @@ -126,7 +135,7 @@ function generate(targetDir) {
});

await generator.generateFromFile(asyncapiFile);
console.log(green('Done! ✨'));
console.log(green('\n\nDone! ✨'));
console.log(`${yellow('Check out your shiny new generated files at ') + magenta(program.output) + yellow('.')}\n`);
resolve();
} catch (e) {
Expand All @@ -135,4 +144,30 @@ function generate(targetDir) {
});
}

/**
* Installs template dependencies.
*
* @param {Boolean} [force=false] Whether to force installation or not.
fmvilas marked this conversation as resolved.
Show resolved Hide resolved
*/
function installTemplate(force = false) {
return new Promise((resolve, reject) => {
const nodeModulesDir = path.resolve(templatesDir, template, 'node_modules');
if (!force && fs.existsSync(nodeModulesDir)) return resolve();

console.log(magenta('Installing template dependencies...'));

npmi({
path: path.resolve(templatesDir, template),
}, err => {
if (err) {
console.error(err.message);
return reject(err);
}

console.log(magenta('Finished installing template dependencies.'));
resolve();
});
});
}

process.on('unhandledRejection', showErrorAndExit);
39 changes: 23 additions & 16 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ const fs = require('fs');
const util = require('util');
const xfs = require('fs.extra');
const walkSync = require('klaw-sync');
const _ = require('lodash');
const Markdown = require('markdown-it');
const OpenAPISampler = require('openapi-sampler');
const minimatch = require('minimatch');
const { parse, AsyncAPIDocument } = require('asyncapi-parser');
const Nunjucks = require('nunjucks');
Expand All @@ -23,14 +20,30 @@ const exists = util.promisify(fs.exists);
const FILTERS_DIRNAME = '.filters';
const PARTIALS_DIRNAME = '.partials';
const HOOKS_DIRNAME = '.hooks';
const NODE_MODULES_DIRNAME = 'node_modules';
const CONFIG_FILENAME = '.tp-config.json';
const PACKAGE_JSON_FILENAME = 'package.json';
const PACKAGE_LOCK_FILENAME = 'package-lock.json';
const DEFAULT_TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');

const shouldIgnoreFile = filePath =>
filePath.startsWith(`${PARTIALS_DIRNAME}${path.sep}`)
|| filePath.startsWith(`${FILTERS_DIRNAME}${path.sep}`)
|| filePath.startsWith(`${HOOKS_DIRNAME}${path.sep}`)
|| path.basename(filePath) === CONFIG_FILENAME;
|| path.basename(filePath) === CONFIG_FILENAME
|| path.basename(filePath) === PACKAGE_JSON_FILENAME
|| path.basename(filePath) === PACKAGE_LOCK_FILENAME
|| filePath.startsWith(`${NODE_MODULES_DIRNAME}${path.sep}`);

const shouldIgnoreDir = dirPath =>
fmvilas marked this conversation as resolved.
Show resolved Hide resolved
dirPath === PARTIALS_DIRNAME
|| dirPath.startsWith(`${PARTIALS_DIRNAME}${path.sep}`)
|| dirPath === FILTERS_DIRNAME
|| dirPath.startsWith(`${FILTERS_DIRNAME}${path.sep}`)
|| dirPath === HOOKS_DIRNAME
|| dirPath.startsWith(`${HOOKS_DIRNAME}${path.sep}`)
|| dirPath === NODE_MODULES_DIRNAME
|| dirPath.startsWith(`${NODE_MODULES_DIRNAME}${path.sep}`);

class Generator {
/**
Expand Down Expand Up @@ -277,12 +290,7 @@ class Generator {
// If it's a module constructor, inject dependencies to ensure consistent usage in remote templates in other projects or plain directories.
const mod = require(filePath);
if (typeof mod === 'function') {
mod({
Nunjucks: this.nunjucks,
_,
Markdown,
OpenAPISampler,
});
mod({ Nunjucks: this.nunjucks });
}
next();
} catch (e) {
Expand Down Expand Up @@ -389,12 +397,9 @@ class Generator {

walker.on('directory', async (root, stats, next) => {
try {
const dirPath = path.resolve(this.targetDir, path.relative(this.templateDir, path.resolve(root, stats.name)));
if (
stats.name !== PARTIALS_DIRNAME
&& stats.name !== FILTERS_DIRNAME
&& stats.name !== HOOKS_DIRNAME
) {
const relativeDir = path.relative(this.templateDir, path.resolve(root, stats.name));
const dirPath = path.resolve(this.targetDir, relativeDir);
if (!shouldIgnoreDir(relativeDir)) {
xfs.mkdirpSync(dirPath);
}
next();
Expand Down Expand Up @@ -485,6 +490,8 @@ class Generator {
const targetFile = path.resolve(this.targetDir, relativeSourceFile);
const relativeTargetFile = path.relative(this.targetDir, targetFile);

if (shouldIgnoreFile(relativeSourceFile)) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add code comment here what we are protecting from here as I couldn't figure out why,
I ran all templates and was never able to get true for any file. I see proper protection is here https://github.com/asyncapi/generator/blob/master/lib/generator.js#L379

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the only place where generateFile is called, that's why. I should probably remove it from the line you mentioned.


if (this.isNonRenderableFile(relativeSourceFile)) {
return await copyFile(sourceFile, targetFile);
}
Expand Down
Loading