Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): improve nodejs api #323

Merged
merged 20 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7a756e9
chore: change schema loader internal api
eduardomourar Jul 8, 2021
92426f9
feat(api): create typescript declaration for api
eduardomourar Jul 9, 2021
db855cf
chore(lint): change license to jsdoc comment
eduardomourar Jul 12, 2021
44302e2
chore(typescript): refactor public api declaration
eduardomourar Jul 12, 2021
378e729
chore(npm): remove unnecessary files from package
eduardomourar Jul 12, 2021
797090f
feat(api): change shape of input and output data
eduardomourar Jul 12, 2021
904b249
chore(api): update unit tests due to code change
eduardomourar Jul 12, 2021
10b9256
chore(typescript): emit types only during release
eduardomourar Jul 12, 2021
50ec153
chore(typescript): emit types for each file
eduardomourar Jul 13, 2021
2f3ff24
chore(api): load from path if no content
eduardomourar Jul 15, 2021
9839b78
chore: revert license date changes
eduardomourar Jul 20, 2021
6f8fb8d
chore: revert converting to camel case
eduardomourar Jul 20, 2021
6799526
chore: increase test coverage
eduardomourar Jul 20, 2021
d3a2a4d
chore: check additional branch during api test
eduardomourar Jul 29, 2021
664941f
Merge 'main' into feat/improve-node-api
eduardomourar Jul 29, 2021
2f51be2
chore: make public api backward compatible
eduardomourar Jul 29, 2021
9447bab
Merge branch 'main' into feat/improve-node-api
eduardomourar Sep 21, 2021
af0bcb3
Merge branch 'main' into feat/improve-node-api
eduardomourar Sep 21, 2021
5fe2cd3
chore: put back license headers as expected
eduardomourar Sep 21, 2021
d52325f
chore: emit updated types
eduardomourar Sep 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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])}*`,
eduardomourar marked this conversation as resolved.
Show resolved Hide resolved
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