From 0f8ae2af61af69c15158e9facbf786e9f8f3f445 Mon Sep 17 00:00:00 2001 From: Carlos Rivera Date: Fri, 5 Mar 2021 23:42:39 -0600 Subject: [PATCH] handlebars partials and helpers support --- example/templates/custom/helpers/.gitkeep | 0 example/templates/custom/helpers/loud.js | 3 ++ example/templates/custom/partials/quote.hbs | 1 + .../custom/partials/sub-folder/bold.hbs | 1 + example/templates/custom/path.hdb | 11 ++++ example/templates/mdx/partials/.gitkeep | 0 package.json | 2 +- readme.md | 40 ++++++++++++++- src/index.ts | 4 ++ src/loaders.ts | 51 +++++++++++++++++++ tests/index.test.ts | 6 +++ 11 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 example/templates/custom/helpers/.gitkeep create mode 100644 example/templates/custom/helpers/loud.js create mode 100644 example/templates/custom/partials/quote.hbs create mode 100644 example/templates/custom/partials/sub-folder/bold.hbs create mode 100644 example/templates/custom/path.hdb create mode 100644 example/templates/mdx/partials/.gitkeep create mode 100644 src/loaders.ts diff --git a/example/templates/custom/helpers/.gitkeep b/example/templates/custom/helpers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/example/templates/custom/helpers/loud.js b/example/templates/custom/helpers/loud.js new file mode 100644 index 0000000..2d6e32f --- /dev/null +++ b/example/templates/custom/helpers/loud.js @@ -0,0 +1,3 @@ +exports.default = function (text) { + return text.toUpperCase() +} diff --git a/example/templates/custom/partials/quote.hbs b/example/templates/custom/partials/quote.hbs new file mode 100644 index 0000000..91704a9 --- /dev/null +++ b/example/templates/custom/partials/quote.hbs @@ -0,0 +1 @@ +>{{text}} diff --git a/example/templates/custom/partials/sub-folder/bold.hbs b/example/templates/custom/partials/sub-folder/bold.hbs new file mode 100644 index 0000000..f5141eb --- /dev/null +++ b/example/templates/custom/partials/sub-folder/bold.hbs @@ -0,0 +1 @@ +**{{text}}** diff --git a/example/templates/custom/path.hdb b/example/templates/custom/path.hdb new file mode 100644 index 0000000..35e46de --- /dev/null +++ b/example/templates/custom/path.hdb @@ -0,0 +1,11 @@ +--- +id: {{method.operationId}} +title: {{method.summary}} +--- + +## This will call a partial template to create the quote +{{>quote text="Hello, World!"}} + +Part of this text will be {{>bold text="bold"}} + +{{loud "This text will uppercased using a helper"}} diff --git a/example/templates/mdx/partials/.gitkeep b/example/templates/mdx/partials/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index a97b84e..c2f7efc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@synx-ai/oas3-mdx", - "version": "0.3.12", + "version": "0.4.1", "description": "Convert OpenAPI spec to Markdown files.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/readme.md b/readme.md index f4f3a6a..36cf8e4 100644 --- a/readme.md +++ b/readme.md @@ -109,7 +109,7 @@ For the parser, while `mdx` or `markdown` are suggested, you can use anything su For every [**operation**](https://swagger.io/docs/specification/paths-and-operations/) in `paths`, object with all references resolved will be passed to `templates/path.hdb`, please refer to default template for an example in how to use it. -Please note that before saving, prettify will be executed to format the output, you can disable it using the `` tag, exmaple: +Please note that before saving, prettify will be executed to format the output, you can disable it using the `` tag, example: ```html @@ -122,6 +122,44 @@ Please note that before saving, prettify will be executed to format the output, ``` + +### Using partials + +In your `templatesPath` create a `dir` called `partials` every single file with `.hdb` extension within its subdirs will be loaded as partial, using the filename as partial name. Example: + +A file named `partials/quote.hdb` with the following code, will create a `quote` partial. + +``` +>{{text}} +``` + +This partial can be used in your templates as follows: + +```markdown +{{>quote "This text will be quoted."}} +``` + +### Using helpers + +In your `templatesPath` create a `dir` called `helpers` every single file with `.js` extension within its subdirs will be loaded as a helper, using the filename as the helper name. Example: + +A file named `partials/loud.js` with the following code, will create a `load` helper. + +```javascript +// the script should export an anonymous function in order to execute +// you can use many parameters as needed +exports.default = function (text) { + return text.toUpperCase() +} +``` + +This helper can be used in your templates as follows: + +```markdown +{{loud "This text will be uppercased."}} +``` + + ## Troubleshooting - Most common errors happens due a malformed file, to validate and lint your spec for possible errors check [Speccy](https://github.com/wework/speccy). - If your specification has multiple paths which map to the same OpenAPI path, you can should set `"x-hasEquivalentPaths": true,` on the root object, example: diff --git a/src/index.ts b/src/index.ts index cc692f0..72b2878 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import * as OpenAPISnippet from "openapi-snippet"; import * as prettier from "prettier"; import bundler from "./bundler"; +import loaders from "./loaders"; /** * Helper function to render a block of code @@ -98,6 +99,9 @@ const convert = (specFile: string, options: Optional = {}): Promise => { // ToDo: delete existing path } + loaders.loadHelpers(path.resolve(process.cwd(), path.join(templatesPath, "helpers"))); + loaders.loadPartials(path.resolve(process.cwd(), path.join(templatesPath, "partials"))); + Handlebars.registerHelper("codeSnippet", (content: string, lang: string, title: string) => { // render code block return codeBlock(content, lang, title); diff --git a/src/loaders.ts b/src/loaders.ts new file mode 100644 index 0000000..21ffd99 --- /dev/null +++ b/src/loaders.ts @@ -0,0 +1,51 @@ +"use strict"; + +import * as fs from "fs"; +import * as path from "path"; +import * as Handlebars from "handlebars"; + +const walkSync = (partialsPath: string, filelist: string[] = []): string[] => { + fs.readdirSync(partialsPath).forEach((file) => { + filelist = fs.statSync(path.join(partialsPath, file)).isDirectory() + ? walkSync(path.join(partialsPath, file), filelist) + : filelist.concat(path.join(partialsPath, file)); + }); + return filelist; +}; + +const loaders = { + loadPartials: (partialsPath: string) => { + if (fs.existsSync(partialsPath)) { + const filelist = walkSync(partialsPath); + if (filelist.length > 0) { + Object.values(filelist).forEach((filename) => { + const matches = /^([^.]+).hbs$/.exec(path.basename(filename)); + if (!matches) { + return; + } + const name = matches[1]; + const template = fs.readFileSync(filename, "utf8"); + Handlebars.registerPartial(name, template); + }); + } + } + }, + loadHelpers: (helpersPath: string) => { + if (fs.existsSync(helpersPath)) { + const filelist = walkSync(helpersPath); + if (filelist.length > 0) { + Object.values(filelist).forEach((filename) => { + const matches = /^([^.]+).js$/.exec(path.basename(filename)); + if (!matches) { + return; + } + const name = matches[1]; + const helper = require(filename).default; + Handlebars.registerHelper(name, helper); + }); + } + } + }, +}; + +export default loaders; diff --git a/tests/index.test.ts b/tests/index.test.ts index 7cc6cb6..53eeaf9 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -7,6 +7,7 @@ const fileThatExistsInAnotherFormat = "./readme.md"; const templatesThatExists = "./example/templates/mdx"; const templatesThatNotExists = "./example/templates/jsx"; +const templatesThatExistsCustomized = "./example/templates/custom"; const urlThatExists = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml"; const urlThatNotExists = "https://github.com/synx-ai/oas3-mdx/blob/master/404"; @@ -23,6 +24,11 @@ describe("convert()", () => { return expect(convert(fileThatExists, { outPath: "./build", templatesPath: templatesThatExists, prettierParser: "markdown" })).resolves.toBeUndefined(); }); + // this will test a custom template options, also will test another parser and the use of handlebar partials + it("should execute from file", () => { + return expect(convert(fileThatExists, { outPath: "./build/custom", templatesPath: templatesThatExistsCustomized })).resolves.toBeUndefined(); + }); + // this will test a failed attempt to execute it("should reject from wrong template dir", () => { return expect(convert(fileThatExists, { outPath: "./build", templatesPath: templatesThatNotExists })).rejects.toBe("Can not find templates path");