UNPKG

6.45 kBJavaScriptView Raw
1/*
2 * Copyright 2019 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13const yargs = require('yargs');
14const nodepath = require('path');
15
16const fs = require('fs');
17const readdirp = require('readdirp');
18const logger = require('@adobe/helix-log');
19const {
20 iter, pipe, filter, map, obj,
21} = require('ferrum');
22const npath = require('path');
23const { i18nConfig } = require('es2015-i18n-tag');
24const traverse = require('./traverseSchema');
25const build = require('./markdownBuilder');
26const { writereadme, writemarkdown } = require('./writeMarkdown');
27const readme = require('./readmeBuilder');
28const { loader } = require('./schemaProxy');
29const { writeSchema } = require('./writeSchema');
30
31const { info, error, debug } = logger;
32
33async function main(args) {
34// parse/process command line arguments
35 const { argv } = yargs(args)
36 .usage('Generate Markdown documentation from JSON Schema.\n\nUsage: $0')
37
38 .demand('d')
39 .alias('d', 'input')
40 .describe('d', 'path to directory containing all JSON Schemas or a single JSON Schema file. This will be considered as the baseURL. By default only files ending in .schema.json will be processed, unless the schema-extension is set with the -e flag.')
41 .coerce('d', (d) => {
42 const resolved = nodepath.resolve(d);
43 if (fs.existsSync(resolved) && fs.lstatSync(d).isDirectory()) {
44 return resolved;
45 }
46 throw new Error(`Input file "${d}" is not a directory!`);
47 })
48
49 .alias('o', 'out')
50 .describe('o', 'path to output directory')
51 .default('o', nodepath.resolve(nodepath.join('.', 'out')))
52 .coerce('o', (o) => nodepath.resolve(o))
53
54 .option('m', {
55 type: 'array',
56 })
57 .alias('m', 'meta')
58 .describe('m', 'add metadata elements to .md files Eg -m template=reference. Multiple values can be added by repeating the flag Eg: -m template=reference -m hide-nav=true')
59 .coerce('m', (m) => pipe(
60 // turn this into an object of key value pairs
61 iter(m),
62 map((i) => i.split('=')),
63 obj,
64 ))
65
66 .alias('x', 'schema-out')
67 .describe('x', 'output JSON Schema files including description and validated examples in the specified folder, or suppress with -')
68 .default('x', nodepath.resolve(nodepath.join('.', 'out')))
69 .coerce('x', (x) => (x === '-' ? '-' : nodepath.resolve(x)))
70
71 .alias('e', 'schema-extension')
72 .describe('e', 'JSON Schema file extension eg. schema.json or json')
73 .default('e', 'schema.json')
74
75 .alias('n', 'no-readme')
76 .describe('n', 'Do not generate a README.md file in the output directory')
77
78 .describe('link-*', 'Add this file as a link the explain the * attribute, e.g. --link-abstract=abstract.md')
79
80 .alias('i', 'i18n')
81 .describe('i', 'path to a locales folder with JSON files')
82 .default('i', nodepath.resolve(__dirname, 'locales'))
83 .coerce('i', (i) => nodepath.resolve(i))
84
85 .alias('l', 'language')
86 .describe('l', 'the selected language')
87 .choices('l', ['en_US', 'de'])
88 .default('l', 'en_US')
89
90 .alias('f', 'example-format')
91 .describe('f', 'how to format examples')
92 .choices('f', ['json', 'yaml'])
93 .default('f', 'json')
94
95 .alias('p', 'properties')
96 .array('p')
97 .describe('p', 'name of a custom property which should be also in the description of an element (may be used multiple times)')
98 .default('p', [])
99 .alias('h', 'header')
100 .boolean('h')
101 .describe('h', 'if the value is false the header will be skipped')
102 .default('h', true)
103
104 .alias('s', 'skip')
105 .array('s')
106 .describe('s', 'name of a default property to skip in markdown (may be used multiple times), e.g. -s typefact -s proptable')
107 .default('s', []);
108
109 const docs = pipe(
110 iter(argv),
111 filter(([key, _value]) => key.startsWith('link-')),
112 map(([key, value]) => [key.substr(5), value]),
113 obj,
114 );
115
116 const schemaPath = argv.d;
117 const schemaExtension = argv.e;
118
119 // eslint-disable-next-line import/no-dynamic-require, global-require
120 i18nConfig(require(nodepath.resolve(argv.i, `${argv.l}.json`)));
121
122 const schemaloader = loader();
123
124 // list all schema files in the specified directory
125 const schemafiles = await readdirp.promise(schemaPath, { root: schemaPath, fileFilter: `*.${schemaExtension}` });
126
127 console.log(`loading ${schemafiles.length} schemas`);
128
129 // then collect data about the schemas and turn everything into a big object
130 const loadedschemas = pipe(
131 schemafiles,
132 map((schema) => schema.fullPath),
133 // eslint-disable-next-line import/no-dynamic-require, global-require
134 map((path) => schemaloader(require(path), path)),
135 // find contained schemas
136 traverse,
137 );
138
139 if (argv.x !== '-') {
140 console.log('writing schemas');
141 writeSchema({
142 schemadir: argv.x,
143 origindir: argv.d,
144 })(loadedschemas);
145 }
146
147 if (!argv.n) {
148 console.log('writing README');
149 pipe(
150 loadedschemas,
151 // build readme
152 readme({
153 readme: !argv.n,
154 }),
155
156 writereadme({
157 readme: !argv.n,
158 out: argv.o,
159 info,
160 error,
161 debug,
162 meta: argv.m,
163 }),
164 );
165 }
166
167 console.log('writing documentation');
168 pipe(
169 loadedschemas,
170 // generate Markdown ASTs
171 build({
172 header: argv.h,
173 links: docs,
174 includeproperties: argv.p,
175 exampleformat: argv.f,
176 skipproperties: argv.s,
177 rewritelinks: (origin) => {
178 const mddir = argv.o;
179 const srcdir = argv.d;
180 const schemadir = argv.x !== '-' ? argv.x : argv.d;
181
182 const target = npath.relative(
183 mddir,
184 npath.resolve(schemadir, npath.relative(srcdir, origin)),
185 ).split(npath.sep).join(npath.posix.sep);
186 return target;
187 },
188 }),
189
190 // write to files
191
192 writemarkdown({
193 out: argv.o,
194 info,
195 error,
196 debug,
197 meta: argv.m,
198 }),
199 );
200
201 return 1;
202}
203
204module.exports = main;