diff --git a/.eslintrc.js b/.eslintrc.js index fd6e0c605cc2b785fe8c8a85387b88072ed34af7..c2639598e11596273c44b3ed168384c4678ad6c1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -39,7 +39,15 @@ module.exports = { 'no-else-return': 'off', // allow dangling underscores for 'fields' - 'no-underscore-dangle': ['error', {'allowAfterThis': true}], + 'no-underscore-dangle': 'off', + + // Allow marking variables unused using a underscore at the start + 'no-unused-vars': ["error", { + "varsIgnorePattern": "^_.*$", + "argsIgnorePattern": "^_.*$", + "caughtErrorsIgnorePattern": "^_.*$" + }], + // enforce license header (todo: improve plugin to support patterns for multi-lines) 'header/header': [2, 'block', ['', diff --git a/cli.js b/cli.js index 6e0e776c8d3427a7607f8eb260f1dc6d95371907..60d5a0d638fc7159cb4aa91df31df2b40374bde8 100755 --- a/cli.js +++ b/cli.js @@ -11,6 +11,7 @@ * governing permissions and limitations under the License. */ +const Optimist = require('optimist'); const Promise = require('bluebird'); const path = require('path'); const _ = require('lodash'); @@ -18,12 +19,13 @@ const fs = Promise.promisifyAll(require('fs')); const readdirp = require('readdirp'); const Ajv = require('ajv'); const logger = require('winston'); +const i18n = require('i18n'); const Schema = require('./lib/schema'); const readSchemaFile = require('./lib/readSchemaFile'); // parse/process command line arguments -const { argv } = require('optimist') +const { argv } = Optimist .usage('Generate Markdown documentation from JSON Schema.\n\nUsage: $0') .demand('d') .alias('d', 'input') @@ -48,10 +50,10 @@ const { argv } = require('optimist') .describe('link-*', 'Add this file as a link the explain the * attribute, e.g. --link-abstract=abstract.md') .check((args) => { if (!fs.existsSync(args.input)) { - throw `Input file "${args.input}" does not exist!`; + throw new Error(`Input file "${args.input}" does not exist!`); } if (args.s && !fs.existsSync(args.s)) { - throw `Meta schema file "${args.s}" does not exist!`; + throw new Error(`Meta schema file "${args.s}" does not exist!`); } }) .alias('i', 'i18n') @@ -61,8 +63,11 @@ const { argv } = require('optimist') .default('h', true) .argv; -const docs = _.fromPairs(_.toPairs(argv).filter(([key, value]) => key.startsWith('link-')).map(([key, value]) => [key.substr(5), value])); -const i18n = require('i18n'); +const docs = _.fromPairs( + _.toPairs(argv) + .filter(([key, _value]) => key.startsWith('link-')) + .map(([key, value]) => [key.substr(5), value]), +); logger.configure({ level: 'info', @@ -78,37 +83,41 @@ logger.configure({ const ajv = new Ajv({ allErrors: true, messages: true, schemaId: 'auto', logger, }); -console.log(argv.v); +logger.info(argv.v); if (argv.v === '06' || argv.v === 6) { - console.log('enabling draft-06 support'); + logger.info('enabling draft-06 support'); + // eslint-disable-next-line global-require ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); } else if (argv.v === '04' || argv.v === 4) { + // eslint-disable-next-line global-require ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); } const schemaPathMap = {}; const metaElements = {}; const schemaPath = path.resolve(argv.d); const outDir = path.resolve(argv.o); -const schemaDir = argv.x === '-' ? '' : argv.x ? path.resolve(argv.x) : outDir; +// eslint-disable-next-line no-nested-ternary +const schemaDir = argv.x === '-' ? '' : (argv.x ? path.resolve(argv.x) : outDir); const target = fs.statSync(schemaPath); const readme = argv.n !== true; const schemaExtension = argv.e || 'schema.json'; if (argv.s) { + // eslint-disable-next-line import/no-dynamic-require, global-require ajv.addMetaSchema(require(path.resolve(argv.s))); } if (argv.m) { if (_.isArray(argv.m)) { _.each(argv.m, (item) => { - const meta = item.split('='); - if (meta.length === 2) { - metaElements[meta[0]] = meta[1]; + const [key, val] = item.split('='); + if (val !== undefined) { + metaElements[key] = val; } }); } else { - const meta = (argv.m).split('='); - if (meta.length === 2) { - metaElements[meta[0]] = meta[1]; + const [key, val] = (argv.m).split('='); + if (val !== undefined) { + metaElements[key] = val; } } } @@ -134,6 +143,7 @@ if (target.isDirectory()) { .on('data', (entry) => { files.push(entry.fullPath); try { + // eslint-disable-next-line import/no-dynamic-require, global-require ajv.addSchema(require(entry.fullPath), entry.fullPath); } catch (e) { logger.error('Ajv processing error for schema at path %s', entry.fullPath); @@ -146,8 +156,14 @@ if (target.isDirectory()) { Schema.setSchemaPathMap(schemaPathMap); return Promise.reduce(files, readSchemaFile, schemaPathMap) .then((schemaMap) => { - logger.info('finished reading all *.%s files in %s, beginning processing….', schemaExtension, schemaPath); - return Schema.process(schemaMap, schemaPath, outDir, schemaDir, metaElements, readme, docs, argv); + logger.info( + 'finished reading all *.%s files in %s, beginning processing….', + schemaExtension, schemaPath, + ); + return Schema.process( + schemaMap, schemaPath, outDir, schemaDir, metaElements, + readme, docs, argv, + ); }) .then(() => { logger.info('Processing complete.'); @@ -164,11 +180,13 @@ if (target.isDirectory()) { } else { readSchemaFile(schemaPathMap, schemaPath) .then((schemaMap) => { + // eslint-disable-next-line import/no-dynamic-require, global-require ajv.addSchema(require(schemaPath), schemaPath); Schema.setAjv(ajv); Schema.setSchemaPathMap(schemaPathMap); logger.info('finished reading %s, beginning processing....', schemaPath); - return Schema.process(schemaMap, schemaPath, outDir, schemaDir, metaElements, false, docs, argv); + return Schema.process(schemaMap, schemaPath, outDir, schemaDir, + metaElements, false, docs, argv); }) .then(() => { logger.info('Processing complete.'); diff --git a/lib/header.js b/lib/header.js index 693faf79ce663c4b61559a864b13f23d5a750511..dc5e255a0664bfeac095b86db927a2cbb55e9e71 100644 --- a/lib/header.js +++ b/lib/header.js @@ -16,7 +16,7 @@ const path = require('path'); function custom(schema) { if (schema.allOf) { - for (let i = 0; i < schema.allOf.length; i++) { + for (let i = 0; i < schema.allOf.length; i += 1) { if (schema.allOf[i].$ref && schema.allOf[i].$ref === 'https://ns.adobe.com/xdm/common/extensible.schema.json#/definitions/@context') { return true; } @@ -49,10 +49,10 @@ class Header { this.renderValue = this.render(this.value, this.links, this.base); } - render(text, link, base) { - return function () { - if (link) { - return `[${text}](${base}${link})`; + render(text, ref, base) { + return () => { + if (ref) { + return `[${text}](${base}${ref})`; } else { return text; } @@ -67,14 +67,12 @@ function header(name, docs, value, links) { this.links = links; - this.render = function (text, link) { - return function () { - if (link) { - return `[${text}](${link})`; - } else { - return text; - } - }; + this.render = (text, ref) => () => { + if (ref) { + return `[${text}](${ref})`; + } else { + return text; + } }; this.renderHeader = this.render(this.name, this.docs); this.renderValue = this.render(this.value, this.links); @@ -114,7 +112,7 @@ function headers(schema, indir, filename, docs) { this.myheaders.push(new Header(i18n.__('header.tabel.additionalProperties'), link(indir, filename, this.doclinks.additional), props.additionalProperties)); this.myheaders.push(new Header(i18n.__('header.tabel.definedIn'), undefined, props.original, path.basename(props.original))); - this.render = function () { + this.render = () => { let buf = ''; // header diff --git a/lib/main.js b/lib/main.js index e0559f21c0c401777a8c7c0fa606a8291dfba3ae..0e872309e8fdc32f64c4b7727f46352d729c0b2c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ +/* eslint-disable global-require */ + module.exports = { writer: require('./writeFiles'), reader: require('./readSchemaFile'), diff --git a/lib/markdownWriter.js b/lib/markdownWriter.js index 71f5c5b1902d97c71528ee0dee77c6b36c029102..87e0bbac71f2b7b3ba32cd4d5e0aafc4e72b075c 100644 --- a/lib/markdownWriter.js +++ b/lib/markdownWriter.js @@ -9,6 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ + +/* eslint-disable no-param-reassign */ + const i18n = require('i18n'); const Promise = require('bluebird'); const path = require('path'); @@ -16,7 +19,6 @@ const _ = require('lodash'); const ejs = require('ejs'); const pejs = Promise.promisifyAll(ejs); -const validUrl = require('valid-url'); const GithubSlugger = require('github-slugger'); const { headers } = require('./header'); const prettyMarkdown = require('./prettyMarkdown'); @@ -25,7 +27,7 @@ const writeFile = require('./writeFiles'); function createGithubSlugs(names) { const slugger = new GithubSlugger(); slugger.reset(); - names = names.sort(); + names = Array.from(names).sort(); return names.reduce((result, item) => { result[item] = slugger.slug(item); return result; @@ -41,7 +43,7 @@ function build(total, fragment) { } function assoc(obj, key, value) { - if (obj == null) { + if (obj === null) { return assoc({}, key, value); } obj[key] = value; @@ -65,7 +67,6 @@ function stringifyExamples(examples) { if (typeof examples === 'string') { examples = [examples]; } - // console.log(examples); return examples.map(example => JSON.stringify(example, null, 2)); } else { return false; @@ -82,6 +83,7 @@ function simpletype(prop) { if (prop.$linkVal !== undefined) { prop.simpletype = prop.$linkVal; } else { + // eslint-disable-next-line no-console console.log(`unresolved reference: ${prop.$ref}`); prop.simpletype = 'reference'; } @@ -90,7 +92,7 @@ function simpletype(prop) { if (prop['meta:enum'] === undefined) { prop['meta:enum'] = {}; } - for (let i = 0; i < prop.enum.length; i++) { + for (let i = 0; i < prop.enum.length; i += 1) { if (prop['meta:enum'][prop.enum[i]] === undefined) { // setting an empty description for each unknown enum prop['meta:enum'][prop.enum[i]] = ''; @@ -116,22 +118,19 @@ function simpletype(prop) { if (innertype.simpletype === 'complex') { prop.simpletype = '`array`'; } else { - // console.log(prop.title); prop.simpletype = innertype.simpletype.replace(/(`)$/, '[]$1'); } } else { prop.simpletype = '`array`'; } } else if (Array.isArray(type)) { - function nullfilter(str) { - return str !== 'null'; - } + const nullfilter = str => str !== 'null'; const filtered = type.filter(nullfilter); if (type.length - 1 === filtered.length) { prop.nullable = true; } if (filtered.length === 1) { - prop.type = filtered[0]; + ([prop.type] = filtered); prop.simpletype = `\`${filtered[0]}\``; } else { prop.type = filtered; @@ -151,9 +150,9 @@ function simpletype(prop) { */ function requiredProperties(properties, required) { if (required) { - for (let i = 0; i < required.length; i++) { - if (properties[required[i]]) { - properties[required[i]].isrequired = true; + for (const prop of required) { + if (properties[prop]) { + properties[prop].isrequired = true; } } } @@ -166,32 +165,35 @@ function ejsRender(template, ctx) { // return JSON.stringify(obj, null, 2); } -const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, docs, consoleArgs) => { +const generateMarkdown = (filename, schema, schemaPath, outDir, + dependencyMap, docs, consoleArgs) => { outDir = outDir || path.resolve(path.join('.', 'out')); // this structure allows us to have separate templates for each element. Instead of having // one huge template, each block can be built individually - outDir = outDir ? outDir : path.resolve(path.join('.', 'out')); + outDir = outDir || path.resolve(path.join('.', 'out')); let multi = []; - multi.push([ 'frontmatter.ejs', { meta: schema.metaElements } ]); - if (!consoleArgs||consoleArgs.header){ - multi.push([ 'header.ejs', { - i18n: i18n, - schema: schema, + multi.push(['frontmatter.ejs', { meta: schema.metaElements }]); + if (!consoleArgs || consoleArgs.header) { + multi.push(['header.ejs', { + i18n, + schema, dependencies: flatten(dependencyMap), - table: headers(schema, schemaPath, filename, docs).render() } ]); + table: headers(schema, schemaPath, filename, docs).render(), + }]); } - multi.push([ 'examples.ejs', { examples: stringifyExamples(schema.examples), title: schema.title, i18n: i18n } ]); - const required = []; //to store required of whole schema, even those in definitions + multi.push(['examples.ejs', { examples: stringifyExamples(schema.examples), title: schema.title, i18n }]); + const required = []; // to store required of whole schema, even those in definitions - // Processing schema.definitions before schema.properties to get any required properties present in definitions + // Processing schema.definitions before schema.properties to get any + // required properties present in definitions if (_.keys(schema.definitions).length > 0) { const abstract = {}; - for (let i = 0; i < _.keys(schema.definitions).length; i++) { + for (let i = 0; i < _.keys(schema.definitions).length; i += 1) { if (schema.definitions[_.keys(schema.definitions)[i]].properties !== undefined) { const definition = schema.definitions[_.keys(schema.definitions)[i]].properties; const tempRequired = schema.definitions[_.keys(schema.definitions)[i]].required; const hasRequiredProperties = (tempRequired !== undefined); - for (let j = 0; j < _.keys(definition).length; j++) { + for (let j = 0; j < _.keys(definition).length; j += 1) { const name = _.keys(definition)[j]; const property = definition[_.keys(definition)[j]]; if (hasRequiredProperties && tempRequired.includes(name)) { @@ -215,7 +217,7 @@ const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, d propertiesSlugs, i18n, }]); - for (let i = 0; i < _.keys(abstract).length; i++) { + for (let i = 0; i < _.keys(abstract).length; i += 1) { const name = _.keys(abstract).sort()[i]; multi.push(['property.ejs', { name, @@ -231,7 +233,9 @@ const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, d } const propertiesSlugs = createGithubSlugs(_.keys(schema.properties)); if (_.keys(schema.properties).length > 0) { - if (schema.required === undefined) { schema.required = []; } + if (schema.required === undefined) { + schema.required = []; + } schema.required = _.union(schema.required, required); // table of contents multi.push(['properties.ejs', { @@ -243,7 +247,7 @@ const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, d i18n, }]); // regular properties - for (let i = 0; i < _.keys(schema.properties).length; i++) { + for (let i = 0; i < _.keys(schema.properties).length; i += 1) { const name = _.keys(schema.properties).sort()[i]; multi.push(['property.ejs', { name, @@ -259,7 +263,7 @@ const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, d if (_.keys(schema.patternProperties).length > 0) { // patterns properties - for (let i = 0; i < _.keys(schema.patternProperties).length; i++) { + for (let i = 0; i < _.keys(schema.patternProperties).length; i += 1) { const name = _.keys(schema.patternProperties)[i]; multi.push(['pattern-property.ejs', { name, @@ -293,10 +297,12 @@ const generateMarkdown = (filename, schema, schemaPath, outDir, dependencyMap, d return Promise.reduce(Promise.map(multi, render), build, '').then((str) => { const mdfile = `${path.basename(filename).slice(0, -5)}.md`; - return writeFile(path.join(path.join(outDir), path.dirname(filename.substr(schemaPath.length))), mdfile, prettyMarkdown(str)); - }).then(out => - // console.log('markdown written (promise)', out); - out); + return writeFile( + path.join(outDir, path.dirname(filename.substr(schemaPath.length))), + mdfile, + prettyMarkdown(str), + ); + }); }; module.exports = generateMarkdown; diff --git a/lib/prettyMarkdown.js b/lib/prettyMarkdown.js index f47b710fb60ef4628bc1bfc75564ebfe28a4bdd3..89a4df5b3ac8160821805f8c09439c0b4fcf4217 100644 --- a/lib/prettyMarkdown.js +++ b/lib/prettyMarkdown.js @@ -12,8 +12,6 @@ const prettier = require('prettier'); -const prettyMarkdown = function (str) { - return prettier.format(str, { parser: 'markdown', proseWrap: 'always', printWidth: 119 }); -}; +const prettyMarkdown = str => prettier.format(str, { parser: 'markdown', proseWrap: 'always', printWidth: 119 }); module.exports = prettyMarkdown; diff --git a/lib/readSchemaFile.js b/lib/readSchemaFile.js index 3a8002ef393d4d69c7843496bfb35a1a07247546..294ffc7d277478a341d2379784cb2f055327927e 100644 --- a/lib/readSchemaFile.js +++ b/lib/readSchemaFile.js @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ +/* eslint-disable no-param-reassign */ + const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); @@ -22,26 +24,24 @@ const fs = Promise.promisifyAll(require('fs')); * @param {map} schemaPathMap - the map of schema paths and JSON schemas * @param {*} fullPath - the full path to the schema */ -module.exports = function readSchemaFile(schemaPathMap, fullPath) { - if (!schemaPathMap) { - schemaPathMap = {}; - } - return fs.readFileAsync(fullPath) - .then((data) => { - const schema = JSON.parse(data); - const obj = {}; - obj.filePath = fullPath; - obj.jsonSchema = schema; - if (schema.$id && schema.$id.length > 0) { - if (!schemaPathMap[schema.$id]) { - schemaPathMap[schema.$id] = obj; - } - // TODO err - // TODO check Missing Specific properties to throw warning // function for warning - } else { - console.warn(`schema ${fullPath} has no $id`); - schemaPathMap[fullPath] = obj; +const readSchemaFile = (schemaPathMap, fullPath) => { + schemaPathMap = schemaPathMap || {}; + return fs.readFileAsync(fullPath).then((data) => { + const schema = JSON.parse(data); + const obj = {}; + obj.filePath = fullPath; + obj.jsonSchema = schema; + if (schema.$id && schema.$id.length > 0) { + if (!schemaPathMap[schema.$id]) { + schemaPathMap[schema.$id] = obj; } - return schemaPathMap; - }); + } else { + // eslint-disable-next-line no-console + console.warn(`schema ${fullPath} has no $id`); + schemaPathMap[fullPath] = obj; + } + return schemaPathMap; + }); }; + +module.exports = readSchemaFile; diff --git a/lib/readmeWriter.js b/lib/readmeWriter.js index 98062032ae784802d4e28b1e88bfbdc7ba4e6cb3..eb3be7f3d698e16adcba35fd5bd825e221a28003 100644 --- a/lib/readmeWriter.js +++ b/lib/readmeWriter.js @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ +/* eslint-disable no-param-reassign */ + const Promise = require('bluebird'); const _ = require('lodash'); const ejs = require('ejs'); @@ -21,7 +23,7 @@ const i18n = require('i18n'); const prettyMarkdown = require('./prettyMarkdown'); const writeFile = require('./writeFiles'); -function relativePath(full, base) { +const relativePath = (full, base) => { full = full.replace(/\\/g, '/'); base = base.replace(/\\/g, '/'); if (full.indexOf(base) === 0) { @@ -29,11 +31,9 @@ function relativePath(full, base) { } else { return full; } -} +}; -function directory(full, base) { - return relativePath(full, base).replace(/[^\/]+$/, ''); -} +const directory = (full, base) => relativePath(full, base).replace(/[^/]+$/, ''); /** * Generates a README.md file from the `schemas` passed in at directory `out` @@ -42,7 +42,7 @@ function directory(full, base) { * @param {string} out - output directory * @param {string} base - schema base directory */ -const generateReadme = function (paths, schemas, out, base) { +const generateReadme = (paths, schemas, out, base) => { const coreinfo = _.values(schemas).map(schema => ({ id: schema.jsonSchema.$id, title: schema.jsonSchema.title, @@ -61,12 +61,7 @@ const generateReadme = function (paths, schemas, out, base) { core: coreinfo, groups: _.groupBy(coreinfo, key => key.dir), }; - return pejs.renderFileAsync(path.join(__dirname, '../templates/md/readme.ejs'), ctx, { debug: false }, i18n).then((str) => { - console.log('Writing README'); - return writeFile(out, 'README.md', prettyMarkdown(str)); - }).then(out => - // console.log('markdown written (promise)', out); - out); + return pejs.renderFileAsync(path.join(__dirname, '../templates/md/readme.ejs'), ctx, { debug: false }, i18n).then(str => writeFile(out, 'README.md', prettyMarkdown(str))); }; module.exports = generateReadme; diff --git a/lib/schema.js b/lib/schema.js index e8c7ec42d51425b2e19766814dd7e31415b86ef8..88113ef3a3ee9d98ae283613391c3793f2beeb9a 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -10,40 +10,42 @@ * governing permissions and limitations under the License. */ +/* eslint-disable no-param-reassign, no-console, no-use-before-define */ + const path = require('path'); const _ = require('lodash'); const logger = require('winston'); const readdirp = require('readdirp'); const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); +const pointer = require('json-pointer'); const markdownWriter = require('./markdownWriter'); const schemaWriter = require('./schemaWriter'); const readmeWriter = require('./readmeWriter'); const deff = '#/definitions/'; const absUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i'); -const pointer = require('json-pointer'); let smap; // TODO remove global let sPath; const wmap = {}; function get$refType(refValue) { - let startpart = ''; let endpart = ''; var - refType = ''; + let startpart = ''; + let endpart = ''; + let refType = ''; const arr = refValue.split('#'); - if (arr.length > 1) { endpart = arr[1]; } + if (arr.length > 1) { + // eslint-disable-next-line prefer-destructuring + endpart = arr[1]; + } + // eslint-disable-next-line prefer-destructuring startpart = arr[0]; - // TODO yRelNoDef - // relative-- yRelWithDef, yRelNoDef, - // absolute-- yAbsWithDef, yAbsFSchema, yAbsWithFragment - var refType = ''; - const deff = '/definitions/'; // if( absUrlRegex.test(refVal) ){ if (startpart.length > 1) { if (startpart in smap) { - if (endpart.startsWith(deff)) { + if (endpart.startsWith('/definitions/')) { refType = 'yAbsWithDef'; } else if (endpart.length === 0) { refType = 'yAbsFSchema'; @@ -51,7 +53,7 @@ function get$refType(refValue) { refType = 'yAbsWithFragment'; } } - } else if (endpart.startsWith(deff)) { + } else if (endpart.startsWith('/definitions/')) { refType = 'yRelWithDef'; } // } @@ -60,20 +62,24 @@ function get$refType(refValue) { function normaliseLinks(obj, refArr) { const basepath = refArr.startpart; - let $linkVal = ''; let - $linkPath = ''; + let $linkVal = ''; + let $linkPath = ''; if (basepath in smap) { const newpath = path.relative(path.dirname(sPath), smap[basepath].filePath).replace(/\\/g, '/'); // to cater windows paths const temp = newpath.slice(0, -5).split('/'); $linkVal = obj.title ? obj.title : path.basename(newpath).slice(0, -5); $linkPath = `${temp.join('/')}.md`; return { $linkVal, $linkPath }; + } else { + return undefined; } } const resolve$ref = Promise.method((val, base$id) => { - let obj; let - link; - if (!(base$id in wmap)) { wmap[base$id] = {}; } + let obj; + let link; + if (!(base$id in wmap)) { + wmap[base$id] = {}; + } const refArr = get$refType(val.$ref); if (refArr.refType === 'yRelWithDef') { refArr.startpart = base$id; @@ -103,8 +109,9 @@ const resolve$ref = Promise.method((val, base$id) => { return processISchema(ischema, refArr.startpart); } } + return undefined; }); -var processFurther = Promise.method((val, key, $id) => { +const processFurther = Promise.method((val, key, $id) => { const base$id = $id; if (val.$ref) { return resolve$ref(val, base$id); @@ -135,6 +142,7 @@ var processFurther = Promise.method((val, key, $id) => { // go recursively down to check for a $ref return processFurther(propertyValue, propertyKey, base$id); } + return undefined; }); return val; @@ -143,9 +151,10 @@ var processFurther = Promise.method((val, key, $id) => { return val; } }); -function processISchema() {} // define w/ function so it gets hoisted and we avoid eslint errors about what is defined first: processISchema or resolve$ref. Both rely on each other! -processISchema = Promise.method((schema, base$id) => { - if (!(base$id in wmap)) { wmap[base$id] = {}; } +const processISchema = Promise.method((schema, base$id) => { + if (!(base$id in wmap)) { + wmap[base$id] = {}; + } if (schema.anyOf || schema.oneOf) { // var $definitions=[] schema.type = schema.anyOf ? 'anyOf' : 'oneOf'; @@ -169,16 +178,13 @@ processISchema = Promise.method((schema, base$id) => { if (schema.items) { const val = schema.items; - if (!schema.type) { schema.type = 'array'; } - if (_.isArray(val)) { - // TODO - - } else if (val.$ref) { + if (!schema.type) { + schema.type = 'array'; + } + if (val.$ref && !_.isArray(val)) { resolve$ref(val, base$id).then((piSchema) => { // check // not sending correct id schema.items = piSchema; }); - } else { - // TODO if such a scenario } } // schema.$definitions = $definitions @@ -186,15 +192,19 @@ processISchema = Promise.method((schema, base$id) => { }); function processSchema(schema) { return new Promise((resolve, reject) => { - if (!schema.properties) { schema.properties = {}; } + if (!schema.properties) { + schema.properties = {}; + } const $id = schema.$id || schema.id; const base$id = $id; - if (!(base$id in wmap)) { wmap[base$id] = {}; } + if (!(base$id in wmap)) { + wmap[base$id] = {}; + } if (schema.allOf) { _.each(schema.allOf, (value) => { if (value.$ref) { - let obj; let - link; + let obj; + let link; const refArr = get$refType(value.$ref); if (refArr.refType === 'yRelWithDef') { refArr.startpart = base$id; @@ -242,10 +252,12 @@ function processSchema(schema) { schema.properties[key].$oSchema.$linkPath = link.$linkPath; } if (ischema.required) { - if (key in ischema.required) { schema.required.push(key); } + if (key in ischema.required) { + schema.required.push(key); + } } } else { - reject('No further schema found'); + reject(new Error('No further schema found')); } }); }); @@ -287,12 +299,12 @@ function processSchema(schema) { } -const Schema = function (ajv, schemaMap) { +const Schema = (ajv, schemaMap) => { this._ajv = ajv; this._schemaPathMap = schemaMap; }; -Schema.resolveRef = function (key, obj, currpath) { +Schema.resolveRef = (key, obj, currpath) => { if (key === '$ref') { const refVal = obj[key]; let temp; @@ -320,27 +332,14 @@ Schema.resolveRef = function (key, obj, currpath) { obj.$linkPath = `${temp.join('/')}.md`; } } - if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') { obj.$type = key; } -}; - -/* The following function does not seem to be used anymore! -var traverseSchema = function(object,schemaFilePath){ - return new Promise((resolve,reject) => { - var recurse=function(curr,key,prev){ - if (key){ - if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') {prev.$type=key;} - } - var result; - if (Array.isArray(curr)) {curr.map((item,index) => recurse(item,index,curr));} else { - (typeof curr === 'object') ? Object.keys(curr).map(key => recurse(curr[key],key,curr)):Schema.resolveRef(key,prev,schemaFilePath); - } - return object; - }; - resolve(recurse(object)); - }); + if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') { + obj.$type = key; + } }; -*/ +// TODO: For some exceedingly strange reason this generates test failures +// when an arrow function is used… +// eslint-disable-next-line func-names Schema.getExamples = function (filePath, schema) { const exampleFileNames = []; let examples = []; @@ -352,13 +351,17 @@ Schema.getExamples = function (filePath, schema) { .on('data', entry => exampleFileNames.push(entry.fullPath)) .on('end', () => resolve(exampleFileNames)) .on('error', err => reject(err)); - }).then((exampleFileNames) => { + }).then(() => { if (exampleFileNames.length > 0) { const validate = this._ajv.compile(schema); return Promise.map(exampleFileNames, entry => fs.readFileAsync(entry).then((example) => { const data = JSON.parse(example.toString()); const valid = validate(data); - if (valid) { examples.push({ filename: entry, data }); } else { logger.error(`${entry} is an invalid Example`); } + if (valid) { + examples.push({ filename: entry, data }); + } else { + logger.error(`${entry} is an invalid Example`); + } })).then(() => { // Sort according to filenames in order not to have random prints examples.sort((a, b) => (a.filename > b.filename ? 1 : -1)); @@ -367,11 +370,13 @@ Schema.getExamples = function (filePath, schema) { schema.examples = examples; return schema; }); - } else { return schema; } + } else { + return schema; + } }); }; -Schema.getDescription = function (filePath, schema) { +Schema.getDescription = (filePath, schema) => { let temp = path.basename(filePath, path.extname(filePath)); // TODO should err be thrown here? temp = `${temp.split('.')[0]}.description.md`; @@ -383,10 +388,16 @@ Schema.getDescription = function (filePath, schema) { .catch(() => schema); }; +// TODO: For some exceedingly strange reason this generates test failures +// when an arrow function is used… +// eslint-disable-next-line func-names Schema.setAjv = function (ajv) { this._ajv = ajv; }; +// TODO: For some exceedingly strange reason this generates test failures +// when an arrow function is used… +// eslint-disable-next-line func-names Schema.setSchemaPathMap = function (schemaMap) { this._schemaPathMap = schemaMap; }; @@ -395,17 +406,20 @@ Schema.setSchemaPathMap = function (schemaMap) { * @param {*} schemaMap * @param {*} schemaPath * @param {string} docDir - where documentation will be generated - * @param {string} schemaDir - where schemas will be generated, if not set, no schema's will be output - * @param {map} metaElements - a map of additional YAML frontmatter to be added to the generated Markdown + * @param {string} schemaDir - where schemas will be generated, if not set, no schema's + * will be output + * @param {map} metaElements - a map of additional YAML frontmatter to be added to + * the generated Markdown * @param {boolean} readme - generate a README.md directory listing * @param {map} docs - a map of documentation links for headers */ -Schema.process = function (schemaMap, schemaPath, docDir, schemaDir, metaElements, readme, docs, consoleArgs) { +Schema.process = (schemaMap, schemaPath, docDir, schemaDir, + metaElements, readme, docs, consoleArgs) => { smap = schemaMap; const keys = Object.keys(schemaMap); return Promise.mapSeries(keys, (schemaKey) => { const props = Object.keys(wmap); - for (let i = 0; i < props.length; i++) { + for (let i = 0; i < props.length; i += 1) { delete wmap[props[i]]; } @@ -425,9 +439,26 @@ Schema.process = function (schemaMap, schemaPath, docDir, schemaDir, metaElement return { mSchema, wSchema: allSchema, dep: wmap }; }); }).then((object) => { - const outputTasks = [markdownWriter(schemaMap[schemaKey].filePath, object.mSchema, schemaPath, docDir, object.dep, docs, consoleArgs)]; + const outputTasks = [ + markdownWriter( + schemaMap[schemaKey].filePath, + object.mSchema, + schemaPath, + docDir, + object.dep, + docs, + consoleArgs, + ), + ]; if (schemaDir !== '') { - outputTasks.push(schemaWriter(schemaMap[schemaKey].filePath, object.wSchema, schemaPath, schemaDir)); + outputTasks.push( + schemaWriter( + schemaMap[schemaKey].filePath, + object.wSchema, + schemaPath, + schemaDir, + ), + ); } return Promise.all(outputTasks); }) @@ -442,7 +473,7 @@ Schema.process = function (schemaMap, schemaPath, docDir, schemaDir, metaElement const markdowns = result.map(r => r[0]); return readmeWriter(markdowns, schemaMap, docDir, schemaPath); } else { - console.log('Output processed.'); + return undefined; } }); }; diff --git a/lib/schemaWriter.js b/lib/schemaWriter.js index c06d24055ec3d48ddccd4155ff891d772b960a9e..5b8e7d6ed0e029949a644783fe0106a37f8a0b81 100644 --- a/lib/schemaWriter.js +++ b/lib/schemaWriter.js @@ -13,8 +13,10 @@ const path = require('path'); const writeFile = require('./writeFiles'); -const generateNewSchemaFiles = function (filename, schema, schemaPath, outDir) { - return writeFile(path.join(path.join(outDir), path.dirname(filename.substr(schemaPath.length))), path.basename(filename), JSON.stringify(schema, null, 4)); -}; +const generateNewSchemaFiles = (filename, schema, schemaPath, outDir) => writeFile( + path.join(outDir, path.dirname(filename.substr(schemaPath.length))), + path.basename(filename), + JSON.stringify(schema, null, 4), +); module.exports = generateNewSchemaFiles; diff --git a/lib/writeFiles.js b/lib/writeFiles.js index b4cf524bce7da347622833070065d960e0bb7965..848080dd65d5163242543b69491be516f16ea314 100644 --- a/lib/writeFiles.js +++ b/lib/writeFiles.js @@ -15,12 +15,13 @@ const fs = Promise.promisifyAll(require('fs')); const path = require('path'); const mkdirp = Promise.promisify(require('mkdirp')); -const writeFile = function (outputDir, fileName, data) { +const writeFile = (outputDir, fileName, data) => Promise.resolve().then(() => { if (!fs.existsSync(outputDir)) { - return mkdirp(outputDir).then(() => fs.writeFileAsync(path.join(outputDir, fileName), data).then(() => path.join(outputDir, fileName))); + return mkdirp(outputDir); } else { - return fs.writeFileAsync(path.join(outputDir, fileName), data).then(() => path.join(outputDir, fileName)); + return Promise.resolve(); } -}; +}).then(() => fs.writeFileAsync(path.join(outputDir, fileName), data)) + .then(() => path.join(outputDir, fileName)); module.exports = writeFile; diff --git a/spec/lib/header.spec.js b/spec/lib/header.spec.js index a7f38ce991391dd44a20a688f53f97234fb7793c..6663fbfd40a55a41da93a70d83f2f007ded77c5a 100644 --- a/spec/lib/header.spec.js +++ b/spec/lib/header.spec.js @@ -14,10 +14,11 @@ const i18n = require('i18n'); const path = require('path'); +const diff = require('jasmine-diff'); const { Header, headers } = require('../../lib/header'); beforeEach(() => { - jasmine.addMatchers(require('jasmine-diff')(jasmine, { + jasmine.addMatchers(diff(jasmine, { colors: true, inline: true, })); diff --git a/spec/lib/integrationTest.spec.js b/spec/lib/integrationTest.spec.js index 9b07850f4c912bf3208ca262f8a9c9550bc86a23..9be1e277f7bf06d93afbbba6a23f27b1ed529330 100644 --- a/spec/lib/integrationTest.spec.js +++ b/spec/lib/integrationTest.spec.js @@ -15,11 +15,12 @@ const { spawn } = require('child_process'); const path = require('path'); const { readFileSync, readdirSync, statSync } = require('fs'); +const diff = require('jasmine-diff'); beforeEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; jasmine.addMatchers( - require('jasmine-diff')(jasmine, { + diff(jasmine, { colors: true, inline: true, }), @@ -68,22 +69,22 @@ describe('Compare results', () => { ]); ls.on('close', (code) => { expect(code).toEqual(0); - const files = readdirSync('./spec/examples').filter(item => !(/(^|\/)\.[^\/\.]/g).test(item)); + const files = readdirSync('./spec/examples').filter(item => !(/(^|\/)\.[^/.]/g).test(item)); expect(files.length).toEqual(23); files.forEach((file) => { if (statSync(`./spec/examples/${file}`).isFile()) { const expectedstr = readFileSync(path.resolve('./spec/examples/', file)).toString(); let actualstr = readFileSync(path.resolve('./examples/docs/', file)).toString(); - actualstr=actualstr.replace(/\r\n/g, '\n'); - expect(actualstr).toEqual(expectedstr, file + ' does not match'); + actualstr = actualstr.replace(/\r\n/g, '\n'); + expect(actualstr).toEqual(expectedstr, `${file} does not match`); } }); done(); }); }); - it('Run jsonschema2md for a file and do not generate a header', done => { + it('Run jsonschema2md for a file and do not generate a header', (done) => { const ls = spawn('node', [ 'cli.js', '-d', @@ -94,17 +95,17 @@ describe('Compare results', () => { 'examples/generated-schemas', '--no-header', '-v', - '06' + '06', ]); - ls.on('close', code => { + ls.on('close', (code) => { expect(code).toEqual(0); - const files = readdirSync('./spec/examples/withoutHeader').filter(item => !(/(^|\/)\.[^\/\.]/g).test(item)); - files.forEach(file => { - if (statSync('./spec/examples/withoutHeader/' + file).isFile()) { + const files = readdirSync('./spec/examples/withoutHeader').filter(item => !(/(^|\/)\.[^/.]/g).test(item)); + files.forEach((file) => { + if (statSync(`./spec/examples/withoutHeader/${file}`).isFile()) { const expectedstr = readFileSync(path.resolve('./spec/examples/withoutHeader/', file)).toString(); - let actualstr = readFileSync(path.resolve('./examples/docsWithoutHeader/', file)).toString(); - expect(actualstr).toEqual(expectedstr, file + ' does not match'); + const actualstr = readFileSync(path.resolve('./examples/docsWithoutHeader/', file)).toString(); + expect(actualstr).toEqual(expectedstr, `${file} does not match`); } }); done();