diff --git a/src/render.js b/src/render.js index 65a62e3..ed86628 100644 --- a/src/render.js +++ b/src/render.js @@ -1,4 +1,4 @@ -import { dirname } from "path"; +import path from "path"; import { fileURLToPath } from "url"; import nunjucks from "nunjucks"; @@ -7,13 +7,15 @@ import showdown from "showdown"; import { removeExtraEmptyLines } from "./text.js"; // load converters & template engines -const srcDir = dirname(fileURLToPath(import.meta.url)); +const srcDir = path.dirname(fileURLToPath(import.meta.url)); +const templateDir = path.join(srcDir, "templates"); const sd = new showdown.Converter(); -const env = new nunjucks.Environment(new nunjucks.FileSystemLoader(srcDir)); +const loader = new nunjucks.FileSystemLoader(templateDir); +const env = new nunjucks.Environment(loader); env.addFilter("parseJson", JSON.parse); /// basic convertion functions -const toMarkDown = (schema) => env.render("template.md", schema); +const toMarkDown = (schema) => env.render("base.md", schema); const toHtml = (schema) => sd.makeHtml(toMarkDown(schema)); // helpers for the engine function below diff --git a/src/template.md b/src/template.md deleted file mode 100644 index 0a2c82f..0000000 --- a/src/template.md +++ /dev/null @@ -1,31 +0,0 @@ -# {{ title }} - -**(`{{ type }}`)** - -{{ description|safe }} - -{% for _, property in properties %} - -## {{ property.title }} - -{% if property.type %}**(`{{ property.type }}`)** {% endif %}{% if property.default %}Defaults to _{{ property.default }}_.{% endif %} - -{% if property.description %} -{{ property.description }} -{% endif %} - -{% if property.context %} -{{ property.context|safe }} -{% endif %} - -{% if property.examples %} - -### Example{% if property.examples.length > 1 %}s{% endif %} - -{% for example in property.examples %} - -- `{{ example|parseJson|dump|safe }}` - {% endfor %} - {% endif %} - -{% endfor %} diff --git a/src/templates/base.md b/src/templates/base.md new file mode 100644 index 0000000..fb1b4ee --- /dev/null +++ b/src/templates/base.md @@ -0,0 +1,13 @@ +{% import "macros.md" as macros %} + +# {{ title }} + +**(`{{ type }}`)** + +{{ description|safe }} + +{% for _, property in properties %} + +{{ macros.instance(property, title=2) }} + +{% endfor %} diff --git a/src/templates/macros.md b/src/templates/macros.md new file mode 100644 index 0000000..c58bf5f --- /dev/null +++ b/src/templates/macros.md @@ -0,0 +1,66 @@ +{% macro instance(property, level=2) %} + +{{ "#".repeat(level) }} {{ property.title }} + +{% if property.type %}**(`{{ property.type }}`)**{% endif %}{% if property.default %} Defaults to _{{ property.default }}_.{% endif %} + +{% if property.description %} +{{ property.description }} +{% endif %} + +{% if property.context %} +{{ property.context|safe }} +{% endif %} + +{% if property.oneOf or property.anyOf or property.allOf or property.not %} +{{ "#".repeat(level + 1) }} Validation + +{% if property.oneOf %} +{{ "#".repeat(level + 2) }} It must satisfy one of these conditions + +{% for item in property.oneOf %} +{{ instance(item, level + 3) }} +{% endfor %} +{% endif %} + +{% if property.anyOf %} +{{ "#".repeat(level + 2) }} It must satisfy at least one of these conditions + +{% for item in property.anyOf %} +{{ instance(item, level + 3) }} +{% endfor %} +{% endif %} + +{% if property.allOf %} +{{ "#".repeat(level + 2) }} It must satisfy all one of these conditions + +{% for item in property.allOf %} +{{ instance(item, level + 3) }} +{% endfor %} +{% endif %} + +{% if property.not %} +{{ "#".repeat(level + 2) }} It cannot satisfy any of these conditions + +{% for item in property.not %} +{{ instance(item, level + 3) }} +{% endfor %} +{% endif %} + +{% endif %} + +{% if property.examples %} +{% if property.examples.length > 1 %} +{% set example_section_title = "Examples" %} +{% else %} +{% set example_section_title = "Example" %} +{% endif %} +{{ "#".repeat(level + 1) }} {{ example_section_title }} + +{% for example in property.examples %} + +- `{{ example|parseJson|dump|safe }}` + {% endfor %} + +{% endif %} +{% endmacro %} diff --git a/src/text.js b/src/text.js index cf70a0b..a009180 100644 --- a/src/text.js +++ b/src/text.js @@ -1,8 +1,18 @@ function removeExtraEmptyLines(text) { - let cleaned = text.trim(); - cleaned = cleaned.replace(/\n{3,}/gm, "\n\n"); // remove extra empty lines - cleaned += "\n"; // add new line at end of string - return cleaned; + const lines = text.split("\n"); + const cleaned = []; + + for (let idx = 0; idx < lines.length; idx++) { + let line = lines[idx].trim(); + let previous = cleaned[cleaned.length - 1] || ""; + + if (line == "" && previous == "") { + continue; // skip duplicate empty lines + } + cleaned.push(line); + } + + return cleaned.join("\n").trim() + "\n"; } export { removeExtraEmptyLines }; diff --git a/test/fixtures/data-resource.html b/test/fixtures/data-resource.html index e2b3b86..f6523dc 100644 --- a/test/fixtures/data-resource.html +++ b/test/fixtures/data-resource.html @@ -11,7 +11,7 @@
{"profile":"http://example.com/my-profiles-json-schema.json"}
(string)
(string)
An identifier string. Lower case characters with ., _, - and / are allowed.
This is ideally a url-usable and human-readable name. Name SHOULD be invariant, meaning it SHOULD NOT change when its parent descriptor is updated.
A reference to the data for this resource, as either a path as a string, or an array of paths as strings. of valid URIs.
The dereferenced value of each referenced data source in path MUST be commensurate with a native, dereferenced representation of the data the resource describes. For example, in a Tabular Data Resource, this means that the dereferenced value of path MUST be an array.
(string)
A fully qualified URL, or a POSIX file path..
+Implementations need to negotiate the type of path provided, and dereference the data accordingly.
+{"path":"file.csv"}
{"path":"http://example.com/file.csv"}
(array)
["file.csv"]
["http://example.com/file.csv"]
{"path":["file.csv","file2.csv"]}
{"path":["http://example.com/file.csv","http://example.com/file2.csv"]}
Inline data for this resource.
(object)
(object)
A schema for this resource.
(string)
(string)
A human-readable title.
{"title":"My Package Title"}(string)
(string)
A text description. Markdown is encouraged.
{"description":"# My Package description\nAll about my package."}(string)
(string)
The home on the web that is related to this data package.
{"homepage":"http://example.com/"}(array)
(array)
The raw sources for this resource.
{"sources":[{"title":"World Bank and OECD","path":"http://data.worldbank.org/indicator/NY.GDP.MKTP.CD"}]}(array)
(array)
The license(s) under which the resource is published.
This property is not legally binding and does not guarantee that the package is licensed under the terms defined herein.
{"licenses":[{"name":"odc-pddl-1.0","path":"http://opendatacommons.org/licenses/pddl/","title":"Open Data Commons Public Domain Dedication and License v1.0"}]}(string)
(string)
The file format of this resource.
csv, xls, json are examples of common formats.
{"format":"xls"}(string)
(string)
The media type of this resource. Can be any valid media type listed with IANA.
{"encoding":"utf-8"}(integer)
(integer)
The size of this resource in bytes.
{"bytes":2082}(string)
(string)
The MD5 hash of this resource. Indicate other hashing algorithms with the {algorithm}:{hash} format.
-{"hash":"d25c9c77f588f5dc32059d2da1136c02"}
{"hash":"SHA256:5262f12512590031bbcc9a430452bfd75c2791ad6771320bb4b5728bfb78c4d0"}