Skip to content

Commit

Permalink
Merge pull request #323 from eduardomourar/feat/improve-node-api
Browse files Browse the repository at this point in the history
feat(api): improve nodejs api
  • Loading branch information
trieloff authored Sep 22, 2021
2 parents 48335c3 + d52325f commit 2a0f6f8
Show file tree
Hide file tree
Showing 37 changed files with 1,047 additions and 294 deletions.
11 changes: 7 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
out/
junit
tmp/
*.tgz
*.zip

# ide files
.project
.settings
.idea/
/.vscode
*.iml

# node specific files
Expand All @@ -28,8 +31,8 @@ npm-debug.log
.tool-versions

# instanbul code coverage…
coverage
.nyc_output/
coverage/
/examples/tmp-docs
.nyc_output
junit/
test/fixtures/cyclic-out/
/.vscode
23 changes: 23 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
out/
tmp/
*.tgz
*.zip

# ide files
.project
.settings
.idea/
.vscode/
*.iml

# node specific files
Expand All @@ -21,3 +27,20 @@ npm-debug.log

# mac files
.DS_Store

.tool-versions

# instanbul code coverage…
.nyc_output/
coverage/
/examples/
junit/
test/
tsconfig.json

# CI/CD
.circleci/
.github/
.renovaterc.json
.tidelift.yml
crowdin.yml
1 change: 1 addition & 0 deletions examples/generated-schemas/subdir.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"meta:license":["Copyright 2017 Adobe Systems Incorporated. All rights reserved.","This file is licensed to you under the Apache License, Version 2.0 (the 'License');","you may not use this file except in compliance with the License. You may obtain a copy","of the License at http://www.apache.org/licenses/LICENSE-2.0"],"$schema":"http://json-schema.org/draft-06/schema#","$id":"https://example.com/schemas/subdir/subdir","title":"Subdir","type":"object","description":"A schema in a sub directory","definitions":{"content":{"properties":{"id":{"type":"string","format":"uri","description":"A unique identifier given to every addressable thing.","version":"1.0.0","testProperty":"test"}}}},"allOf":[{"$ref":"#/definitions/content"}]}
16 changes: 8 additions & 8 deletions lib/formatInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,24 @@ function iscustom(schema) {
function getdefined(schema) {
if (schema[s.parent]) {
return {
text: `${path.basename(schema[s.filename])}*`,
link: schema[s.filename],
text: `${schema[s.filename]}*`,
link: schema[s.fullpath],
};
}
return {
text: path.basename(schema[s.filename]),
link: schema[s.filename],
text: schema[s.filename],
link: schema[s.fullpath],
};
}

function plaindescription(schema) {
try {
if (schema[s.filename] && !schema[s.parent]) {
const filename = path.resolve(
path.dirname(schema[s.filename]),
if (schema[s.fullpath] && !schema[s.parent]) {
const fullPath = path.resolve(
path.dirname(schema[s.fullpath]),
schema[s.filename].replace(/\..*$/, '.description.md'),
);
const longdesc = fs.readFileSync(filename);
const longdesc = fs.readFileSync(fullPath);
return longdesc.toString();
}
} catch {
Expand Down
204 changes: 147 additions & 57 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/* @ts-check */
const yargs = require('yargs');
const nodepath = require('path');

const fs = require('fs');
const readdirp = require('readdirp');
const logger = require('@adobe/helix-log');
const {
iter, pipe, filter, map, obj,
} = require('ferrum');
const npath = require('path');
const nodepath = require('path');
const { i18nConfig } = require('es2015-i18n-tag');
const traverse = require('./traverseSchema');
const build = require('./markdownBuilder');
Expand All @@ -30,46 +28,134 @@ const { writeSchema } = require('./writeSchema');

const { info, error, debug } = logger;

function jsonschema2md(schema, {
schemaPath,
out,
meta,
schemaOut,
includeReadme,
links,
i18n,
language,
exampleFormat,
includeProperties,
header,
skipProperties,
}) {
const outOrDefault = out || nodepath.resolve(nodepath.join('.', 'out'));
/**
* @typedef {import("../types/api").JsonSchema} JsonSchema
*/
/**
* @typedef {import("../types/api").SchemaList} SchemaList
*/
/**
* @typedef {import("../types/api").SchemaContent} SchemaContent
*/
/**
* @typedef {import("../types/api").SchemaFiles} SchemaFiles
*/
/**
* @typedef {import("../types/api").GeneratedOutput} GeneratedOutput
*/

/**
* Public API for jsonschema2md that can be used to turn JSON Schema files
* into readable Markdown documentation.
* @param {JsonSchema | SchemaFiles} schema JSON Schema input to get data from
* @param {Object} options Additional options for generation
* @param {string} [options.schemaPath] - (optional) Path to directory containing all JSON Schemas
* or a single JSON Schema file. This will be considered as the baseURL.
* @param {string} [options.outDir] - (optional) Path to output directory. Generating files will
* be skipped if directory is not specified.
* @param {{ [key:string]: string }} [options.metadata] - (optional) Add metadata elements to
* .md files.
* @param {string} [options.schemaOut] - (optional) Output JSON Schema files including
* description and validated examples in the specified folder.
* @param {boolean} [options.includeReadme=true] - (optional) Generate a README.md file in the
* output directory.
* @param {{ [key:string]: string }[]} [options.links] - (optional) Add this file as a link
* explaining the specified attribute.
* @param {string} [options.i18n="locales/"] - (optional) Path to a locales folder with
* JSON files used for internationalization.
* @param {"en_US" | "de"} [options.language="en_US"] - (optional) Selected language.
* @param {"json" | "yaml"} [options.exampleFormat="json"] - (optional) How to format examples.
* @param {string[]} [options.includeProperties=[]] - (optional) Name of custom properties
* which should be also in the description of an element.
* @param {boolean} [options.header=true] - (optional) Whether or not to include the header
* in markdown.
* @param {string[]} [options.skipProperties=[]] - (optional) Name of a default property to
* skip in markdown.
* @returns {GeneratedOutput} List of raw markdown that were generated from input schema.
*/
function jsonschema2md(schema, options) {
const {
schemaPath,
outDir,
metadata,
schemaOut,
includeReadme,
links,
i18n,
language,
exampleFormat,
includeProperties,
header,
skipProperties,
} = options;
if (!schema || typeof schema !== 'object') {
throw Error('Input is not valid. Provide JSON schema either as Object or Array.');
}
const locales = i18n || nodepath.resolve(__dirname, 'locales');
// eslint-disable-next-line import/no-dynamic-require, global-require
i18nConfig(require(nodepath.resolve(locales, `${language || 'en_US'}.json`)));
let out = outDir;
if (options.out) {
// eslint-disable-next-line no-console
console.warn("Options 'out' has been deprecated. Please, use 'outDir' instead");
out = options.out;
}
let meta = metadata;
if (options.meta) {
// eslint-disable-next-line no-console
console.warn("Options 'meta' has been deprecated. Please, use 'metadata' instead");
meta = options.meta;
}

const schemas = [].concat(schema);
if (schemaOut) {
console.log('writing schemas');
writeSchema({
schemadir: schemaOut,
origindir: schemaPath,
})(schemas);
/** @type {SchemaFiles} */
let normalized;
if (Array.isArray(schema)) {
normalized = schema;
} else {
normalized = [{
fileName: 'definition.schema.json',
content: schema,
}];
}

const schemaLoader = loader();

// collect data about the schemas and turn everything into a big object
const schemas = pipe(
normalized,
// Checking if data contains the file path or its contents (JSON schema)
map(({ fileName, fullPath, content }) => {
if (!content && fullPath) {
// eslint-disable-next-line import/no-dynamic-require, global-require
return schemaLoader(fullPath, require(fullPath));
}
return schemaLoader(fileName, content);
}),
traverse,
);

/**
* @type {GeneratedOutput}
*/
const output = {};

console.log('preparing schemas...');
output.schema = writeSchema({
schemadir: schemaOut,
origindir: schemaPath,
})(schemas);

if (includeReadme) {
console.log('writing README');
pipe(
console.log('preparing README...');
output.readme = pipe(
schemas,
// build readme
readme({
readme: includeReadme,
readme: true,
}),

writereadme({
readme: includeReadme,
out: outOrDefault,
out,
info,
error,
debug,
Expand All @@ -78,8 +164,8 @@ function jsonschema2md(schema, {
);
}

console.log('writing documentation');
const markdown = pipe(
console.log('preparing documentation...');
output.markdown = pipe(
schemas,
// generate Markdown ASTs
build({
Expand All @@ -89,31 +175,39 @@ function jsonschema2md(schema, {
exampleFormat,
skipProperties,
rewritelinks: (origin) => {
const mddir = outOrDefault;
const mddir = out;
if (!mddir) {
return origin;
}
const srcdir = schemaPath;
const schemadir = schemaOut || schemaPath;

const target = npath.relative(
const target = nodepath.relative(
mddir,
npath.resolve(schemadir, npath.relative(srcdir, origin)),
).split(npath.sep).join(npath.posix.sep);
nodepath.resolve(schemadir, nodepath.relative(srcdir, origin)),
).split(nodepath.sep).join(nodepath.posix.sep);
return target;
},
}),

// write to files
writemarkdown({
out: outOrDefault,
out,
info,
error,
debug,
meta,
}),
);

return markdown;
return output;
}

/**
* Main function used in the CLI.
* @param {{ [key:string]: unknown }} args CLI arguments from user input
* @returns The generated Markdown files to the specified output directory
*/
async function main(args) {
// parse/process command line arguments
const { argv } = yargs(args)
Expand Down Expand Up @@ -198,8 +292,8 @@ async function main(args) {
);

const schemaPath = argv.d;
const out = argv.o;
const meta = argv.m;
const outDir = argv.o;
const metadata = argv.m;
const schemaOut = argv.x !== '-' ? argv.x : null;
const includeReadme = !argv.n;
const i18n = argv.i;
Expand All @@ -211,27 +305,23 @@ async function main(args) {

const schemaExtension = argv.e;

const schemaloader = loader();

// list all schema files in the specified directory
const schemafiles = await readdirp.promise(schemaPath, { root: schemaPath, fileFilter: `*.${schemaExtension}` });
const schemaFiles = await readdirp.promise(schemaPath, { root: schemaPath, fileFilter: `*.${schemaExtension}` });

console.log(`loading ${schemafiles.length} schemas`);
console.log(`loading ${schemaFiles.length} schemas`);

// then collect data about the schemas and turn everything into a big object
const loadedschemas = pipe(
schemafiles,
map((schema) => schema.fullPath),
// eslint-disable-next-line import/no-dynamic-require, global-require
map((path) => schemaloader(require(path), path)),
// find contained schemas
traverse,
);
/**
* @type {SchemaList[]}
* */
const schemas = schemaFiles.map((schema) => ({
fileName: schema.basename,
fullPath: schema.fullPath,
}));

jsonschema2md(loadedschemas, {
jsonschema2md(schemas, {
schemaPath,
out,
meta,
outDir,
metadata,
schemaOut,
includeReadme,
links,
Expand Down
Loading

0 comments on commit 2a0f6f8

Please sign in to comment.