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..f3c57116c96c7bc46fa1a0b5374412f4fdf40f8e 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,21 +50,23 @@ 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') .describe('i', 'path to a locales folder with an en.json file in it. This file will be used for all text parts in all templates') .alias('h', 'header') .describe('h', 'if the value is false the header will be skipped') - .default('h', true) - .argv; + .default('h', true); -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 +82,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 +142,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 +155,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 +179,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();