Skip to content

Commit

Permalink
feat: Add support for remote templates (#261)
Browse files Browse the repository at this point in the history
* Add support for remote templates 🎉

* Move some utility functions to utils package

* Update docs

* Remove templates from this repo to avoid confusion

* Ignore .git directory on local templates

* Make loadTemplateConfig async

* Fix this.templateDir assignment

* Rename forceInstall with install

* Add docs and address comments
  • Loading branch information
fmvilas authored Mar 27, 2020
1 parent 3482cb8 commit 543f9d5
Show file tree
Hide file tree
Showing 110 changed files with 247 additions and 4,095 deletions.
71 changes: 49 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,70 @@ asyncapi/generator -o ./output asyncapi.yml markdown
### From the command-line interface (CLI)

```bash
Usage: ag [options] <asyncapi> <template>


Options:

-V, --version output the version number
-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 (defaults to internal templates directory)
--force-install forces the installation of the template dependencies. By default, dependencies are installed and this flag is taken into account only if `node_modules` is not in place.
-h, --help output usage information
--force-write force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)
Usage: cli [options] <asyncapi> <template>

Options:
-V, --version output the version number
-d, --disable-hook <hookName> disable a specific hook
-i, --install installs the template and its dependencies (defaults to false)
-n, --no-overwrite <glob> glob or path of the file(s) to skip when regenerating
-o, --output <outputDir> directory where to put the generated files (defaults to current directory)
-p, --param <name=value> additional param to pass to templates
--force-write force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)
--watch-template watches the template directory and the AsyncAPI document, and re-generate the files when changes occur
-h, --help output usage information
```
Please check out the **templates** directory to get a list of the supported languages/formats.
<details>
<summary>Click here to read more about supported values for the <code>&lt;template&gt;</code> parameter.</summary>
<br>
Templates are installable npm packages. Therefore, the value of <code>&lt;template&gt;</code> can be anything supported by <code>npm install</code>. Here's a summary of the possibilities:
<br><br>
<pre><code>
npm install [&lt;@scope&gt;/]&lt;name&gt;
npm install [&lt;@scope&gt;/]&lt;name&gt;@&lt;tag&gt;
npm install [&lt;@scope&gt;/]&lt;name&gt;@&lt;version&gt;
npm install [&lt;@scope&gt;/]&lt;name&gt;@&lt;version range&gt;
npm install &lt;git-host&gt;:&lt;git-user&gt;/&lt;repo-name&gt;
npm install &lt;git repo url&gt;
npm install &lt;tarball file&gt;
npm install &lt;tarball url&gt;
npm install &lt;folder&gt;</code></pre>
</details>
<br>
:mag: Do you want to find a template? **[Click here!](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate)**
#### Examples
The shortest possible syntax:
**The shortest possible syntax:**
```bash
ag asyncapi.yaml markdown
ag asyncapi.yaml @asyncapi/html-template
```
Specify where to put the result:
**Specify where to put the result:**
```bash
ag -o ./docs asyncapi.yaml markdown
ag asyncapi.yaml @asyncapi/html-template -o ./docs
```
Passing parameters to templates:
**Passing parameters to templates:**
```bash
ag -o ./docs --param title='Hello from param' asyncapi.yaml markdown
ag asyncapi.yaml @asyncapi/html-template -o ./docs -p title='Hello from param'
```
In the template you can use it like this: ` {{ params.title }}`
**Installing the template from a folder:**
```bash
ag asyncapi.yaml ~/my-template
```
It creates a symbolic link to the target directory (`~/my-template` in this case).
**Installing the template from a git URL:**
```bash
ag asyncapi.yaml https://github.com/asyncapi/html-template.git
```
### As a module
See [API documentation](docs/api.md).
Expand Down
23 changes: 13 additions & 10 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const packageInfo = require('./package.json');
const mkdirp = require('mkdirp');
const Generator = require('./lib/generator');
const Watcher = require('./lib/watcher');
const { isLocalTemplate } = require('./lib/utils');

const red = text => `\x1b[31m${text}\x1b[0m`;
const magenta = text => `\x1b[35m${text}\x1b[0m`;
Expand Down Expand Up @@ -52,14 +53,13 @@ program
asyncapiFile = path.resolve(asyncAPIPath);
template = tmpl;
})
.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('-i, --install', 'installs the template and its dependencies (defaults to false)')
.option('-n, --no-overwrite <glob>', 'glob or path of the file(s) to skip when regenerating', noOverwriteParser)
.option('-o, --output <outputDir>', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd())
.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('--force-install', 'forces the installation of the template dependencies. By default, dependencies are installed and this flag is taken into account only if `node_modules` is not in place.')
.option('--force-write', 'force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)')
.option('--watch-template', 'watches the template directory and the AsyncAPI document, and re-generate the files when changes occur')
.parse(process.argv);

if (!asyncapiFile) {
Expand All @@ -76,15 +76,19 @@ mkdirp(program.output, async err => {
}

// If we want to watch for changes do that
if (program.watch) {
const watchDir = path.resolve(program.templates, template);
console.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)} and in the async api file ${magenta(asyncapiFile)}`);
if (program.watchTemplate) {
const watchDir = path.resolve(Generator.DEFAULT_TEMPLATES_DIR, template);
console.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)} and in the AsyncAPI file ${magenta(asyncapiFile)}`);

if (!(await isLocalTemplate(watchDir))) {
console.warn(`WARNING: ${template} is a remote template. Changes may be lost on subsequent installations.`);
}

const watcher = new Watcher([asyncapiFile, watchDir]);
watcher.watch(async (changedFiles) => {
console.clear();
console.log('[WATCHER] Change detected');
for (const [key, value] of Object.entries(changedFiles)) {
for (const [, value] of Object.entries(changedFiles)) {
let eventText;
switch (value.eventType) {
case 'changed':
Expand Down Expand Up @@ -121,12 +125,11 @@ function generate(targetDir) {
return new Promise(async (resolve, reject) => {
try {
const generator = new Generator(template, targetDir || path.resolve(os.tmpdir(), 'asyncapi-generator'), {
templatesDir: program.templates,
templateParams: params,
noOverwriteGlobs,
disabledHooks,
forceWrite: program.forceWrite,
forceInstall: program.forceInstall,
forceInstall: program.install,
});

await generator.generateFromFile(asyncapiFile);
Expand Down
33 changes: 3 additions & 30 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* [.generate(asyncapiDocument)](#Generator+generate) ⇒ <code>Promise</code>
* [.generateFromString(asyncapiString, [asyncApiFileLocation])](#Generator+generateFromString) ⇒ <code>Promise</code>
* [.generateFromFile(asyncapiFile)](#Generator+generateFromFile) ⇒ <code>Promise</code>
* [.getAllParameters(asyncapiDocument)](#Generator+getAllParameters)
* [.installTemplate([force])](#Generator+installTemplate)
* _static_
* [.getTemplateFile(templateName, filePath, options)](#Generator.getTemplateFile) ⇒ <code>Promise</code>
Expand All @@ -25,14 +24,13 @@ Instantiates a new Generator object.
| templateName | <code>String</code> | | Name of the template to generate. |
| targetDir | <code>String</code> | | Path to the directory where the files will be generated. |
| options | <code>Object</code> | | |
| [options.templatesDir] | <code>String</code> | | Path to the directory where to find the given template. Defaults to internal `templates` directory. |
| [options.templateParams] | <code>String</code> | | Optional parameters to pass to the template. Each template define their own params. |
| [options.entrypoint] | <code>String</code> | | Name of the file to use as the entry point for the rendering process. Use in case you want to use only a specific template file. Note: this potentially avoids rendering every file in the template. |
| [options.noOverwriteGlobs] | <code>Array.&lt;String&gt;</code> | | List of globs to skip when regenerating the template. |
| [options.disabledHooks] | <code>Array.&lt;String&gt;</code> | | List of hooks to disable. |
| [options.output] | <code>String</code> | <code>&#x27;fs&#x27;</code> | Type of output. Can be either 'fs' (default) or 'string'. Only available when entrypoint is set. |
| [options.forceWrite] | <code>Boolean</code> | <code>false</code> | Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir. Default is set to false. |
| [options.forceInstall] | <code>Boolean</code> | <code>false</code> | Force the installation of the template dependencies. By default, dependencies are installed and this flag is taken into account only if `node_modules` is not in place. |
| [options.forceInstall] | <code>Boolean</code> | <code>false</code> | Force the installation of the template and its dependencies. |

**Example**
```js
Expand All @@ -48,13 +46,6 @@ const generator = new Generator('html', path.resolve(__dirname, 'example'), {
}
});
```
**Example** *(Specifying a custom directory for templates)*
```js
const path = require('path');
const generator = new Generator('myTemplate', path.resolve(__dirname, 'example'), {
templatesDir: path.resolve(__dirname, 'my-templates')
});
```
<a name="Generator+generate"></a>

### generator.generate(asyncapiDocument) ⇒ <code>Promise</code>
Expand Down Expand Up @@ -158,25 +149,16 @@ 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+installTemplate"></a>

### generator.installTemplate([force])
Installs template dependencies.
Downloads and installs a template and its dependencies.

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

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [force] | <code>Boolean</code> | <code>false</code> | Whether to force installation or not. |
| [force] | <code>Boolean</code> | <code>false</code> | Whether to force installation (and skip cache) or not. |

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

Expand All @@ -190,18 +172,9 @@ Returns the content of a given template file.
| templateName | <code>String</code> | Name of the template to generate. |
| filePath | <code>String</code> | Path to the file to render. Relative to the template directory. |
| options | <code>Object</code> | |
| [options.templatesDir] | <code>String</code> | Path to the directory where to find the given template. Defaults to internal `templates` directory. |

**Example**
```js
const Generator = require('asyncapi-generator');
const content = await Generator.getTemplateFile('html', '.partials/content.html');
```
**Example** *(Obtaining the content of a file from a custom template)*
```js
const path = require('path');
const Generator = require('asyncapi-generator');
const content = await Generator.getTemplateFile('myTemplate', 'a/file.js', {
templatesDir: path.resolve(__dirname, 'my-templates')
});
```
Loading

0 comments on commit 543f9d5

Please sign in to comment.