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 @@

Examples

  • {"profile":"http://example.com/my-profiles-json-schema.json"}

  • Name

    -

    (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.

    Example

    @@ -21,7 +21,25 @@

    Example

    Path

    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.

    -

    Examples

    +

    Validation

    +

    It must satisfy one of these conditions

    +
    Path
    +

    (string)

    +

    A fully qualified URL, or a POSIX file path..

    +

    Implementations need to negotiate the type of path provided, and dereference the data accordingly.

    +
    Examples
    + +
    +

    (array)

    +
    Examples
    + +

    Examples

    Format

    -

    (string)

    +

    (string)

    The file format of this resource.

    csv, xls, json are examples of common formats.

    Example

    @@ -77,7 +95,7 @@

    Example

  • {"format":"xls"}
  • Media Type

    -

    (string)

    +

    (string)

    The media type of this resource. Can be any valid media type listed with IANA.

    Example

    Bytes

    -

    (integer)

    +

    (integer)

    The size of this resource in bytes.

    Example

    Hash

    -

    (string)

    +

    (string)

    The MD5 hash of this resource. Indicate other hashing algorithms with the {algorithm}:{hash} format.

    -

    Examples

    +

    Examples