├── .conventional-changelog-lintrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .jsonlintignore
├── .jsonlintrc
├── .npmrc
├── changelog.md
├── contributing.md
├── index.js
├── jsonlint-cli-banner.svg
├── jsonlint-cli.svg
├── library
├── cli.js
├── fetch-schema.js
├── filter.js
├── format.js
├── get-configuration.js
├── get-input.js
├── help.js
├── lint.js
├── list-files.js
├── load-schema.js
├── print.js
├── resolve-keys.js
├── schema-error.js
├── sort.js
└── unknown.js
├── license.md
├── package.json
├── pattern
├── .jsonlintrc
├── pattern-schema.json
└── pattern.json
└── readme.md
/.conventional-changelog-lintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "scope-enum": [
4 | 2,
5 | "always",
6 | [
7 | "release",
8 | "system",
9 | "validation",
10 | "parsing",
11 | "configuration",
12 | "cli"
13 | ]
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = lf
3 | insert_final_newline = true
4 | trim_trailing_whitespace = true
5 | indent_style = tab
6 |
7 | [{.*rc,*.yml,*.md,package.json,*.svg}]
8 | indent_style = space
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["xo"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 | # jsonlint-cli cache
36 | .tmp
37 |
--------------------------------------------------------------------------------
/.jsonlintignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 |
--------------------------------------------------------------------------------
/.jsonlintrc:
--------------------------------------------------------------------------------
1 | {
2 | "validate": "http://json.schemastore.org/package"
3 | }
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | spin = false
2 | progress = false
3 | save-exact = true
4 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 |
2 | ## [1.0.1](https://github.com/marionebl/jsonlint-cli/compare/v1.0.0...v1.0.1) (2016-03-17)
3 |
4 |
5 | ### Bug Fixes
6 |
7 | * **configuration:** employ correct precedence for configuration files ([742df22](https://github.com/marionebl/jsonlint-cli/commit/742df22))
8 | * **configuration:** filter ignored files before reading ([17c32f4](https://github.com/marionebl/jsonlint-cli/commit/17c32f4)), closes [#2](https://github.com/marionebl/jsonlint-cli/issues/2)
9 |
10 |
11 |
12 |
13 | # [1.0.0](https://github.com/marionebl/jsonlint-cli/compare/v0.2.8...v1.0.0) (2016-03-13)
14 |
15 |
16 | ### Code Refactoring
17 |
18 | * split program into logical bits ([a7fe762](https://github.com/marionebl/jsonlint-cli/commit/a7fe762)), closes [#1](https://github.com/marionebl/jsonlint-cli/issues/1)
19 |
20 |
21 | ### BREAKING CHANGES
22 |
23 | * * removed the --quiet flag
24 | * errors are always printed
25 |
26 |
27 |
28 |
29 | ## [0.2.8](https://github.com/marionebl/jsonlint-cli/compare/v0.2.7...v0.2.8) (2016-03-01)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * add missing minimatch dependency ([1f72a5a](https://github.com/marionebl/jsonlint-cli/commit/1f72a5a))
35 |
36 |
37 |
38 |
39 | ## [0.2.7](https://github.com/marionebl/jsonlint-cli/compare/v0.2.6...v0.2.7) (2016-02-11)
40 |
41 |
42 | ### Bug Fixes
43 |
44 | * actually expose jsonlint-cli ([63510de](https://github.com/marionebl/jsonlint-cli/commit/63510de))
45 | * resolve local schemas properly ([fb345be](https://github.com/marionebl/jsonlint-cli/commit/fb345be))
46 | * use schema for validation messages again ([89bcbf9](https://github.com/marionebl/jsonlint-cli/commit/89bcbf9))
47 |
48 |
49 |
50 |
51 | ## [0.2.6](https://github.com/marionebl/jsonlint-cli/compare/v0.2.5...v0.2.6) (2016-02-11)
52 |
53 |
54 |
55 |
56 |
57 | ## [0.2.5](https://github.com/marionebl/jsonlint-cli/compare/v0.2.4...v0.2.5) (2016-02-11)
58 |
59 |
60 |
61 |
62 |
63 | ## [0.2.4](https://github.com/marionebl/jsonlint-cli/compare/v0.2.3...v0.2.4) (2016-02-11)
64 |
65 |
66 |
67 |
68 |
69 | ## 0.2.3 (2016-02-11)
70 |
71 |
72 | ### Features
73 |
74 | * add missing help command ([448e5ec](https://github.com/marionebl/jsonlint-cli/commit/448e5ec))
75 | * enhance jsonlint cli with globbing, remote schemas ([f69d8c3](https://github.com/marionebl/jsonlint-cli/commit/f69d8c3))
76 |
77 |
78 |
79 |
80 | ## 0.2.2 (2016-02-11)
81 |
82 |
83 | ### Features
84 |
85 | * add missing help command ([448e5ec](https://github.com/marionebl/jsonlint-cli/commit/448e5ec))
86 | * enhance jsonlint cli with globbing, remote schemas ([f69d8c3](https://github.com/marionebl/jsonlint-cli/commit/f69d8c3))
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | > cli wrapper for jsonlint
2 |
3 |
4 |
jsonlint-cli
5 |
6 |
7 | Yeay! You want to contribute to jsonlint-cli. That's amazing!
8 | To smoothen everyone's experience involved with the project please take note of the following guidelines and rules.
9 |
10 | ## Found an Issue?
11 | Thank you for reporting any issues you find. We do our best to test and make jsonlint-cli as solid as possible, but any reported issue is a real help.
12 |
13 | > jsonlint-cli issues
14 |
15 | Please follow these guidelines when reporting issues:
16 | * Provide a title in the format of ` when `
17 | * Tag your issue with the tag `bug`
18 | * Provide a short summary of what you are trying to do
19 | * Provide the log of the encountered error if applicable
20 | * Provide the exact version of jsonlint-cli. Check `npm ls jsonlint-cli` when in doubt
21 | * Be awesome and consider contributing a [pull request](#want-to-contribute)
22 |
23 | ## Want to contribute?
24 | You consider contributing changes to jsonlint-cli – we dig that!
25 | Please consider these guidelines when filing a pull request:
26 |
27 | > jsonlint-cli pull requests
28 |
29 | * Follow the [Coding Rules](#coding-rules)
30 | * Follow the [Commit Rules](#commit-rules)
31 | * Make sure you rebased the current master branch when filing the pull request
32 | * Squash your commits when filing the pull request
33 | * Provide a short title with a maximum of 100 characters
34 | * Provide a more detailed description containing
35 | * What you want to achieve
36 | * What you changed
37 | * What you added
38 | * What you removed
39 |
40 | ## Coding Rules
41 | To keep the code base of jsonlint-cli neat and tidy the following rules apply to every change
42 |
43 | > Coding standards
44 |
45 | * [Happiness](/sindresorhus/xo) enforced via eslint
46 | * Favor micro library over swiss army knives (rimraf, ncp vs. fs-extra)
47 | * Coverage never drops below 90%
48 | * No change may lower coverage by more than 5%
49 | * Be awesome
50 |
51 | ## Commit Rules
52 | To help everyone with understanding the commit history of jsonlint-cli the following commit rules are enforced.
53 | To make your life easier jsonlint-cli is commitizen-friendly and provides the npm run-script `commit`.
54 |
55 | > Commit standards
56 |
57 | * [conventional-changelog](/commitizen/cz-conventional-changelog)
58 | * husky commit message hook available
59 | * present tense
60 | * maximum of 100 characters
61 | * message format of `$type($scope): $message`
62 |
63 |
64 | ---
65 | Copyright 2016 by [Mario Nebl](https://github.com/marionebl) and [contributors](./graphs/contributors). Released under the [MIT license]('./license.md').
66 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const merge = require('lodash').merge;
3 |
4 | const cli = require('./library/cli');
5 | const listFiles = require('./library/list-files');
6 | const fetchSchema = require('./library/fetch-schema');
7 | const getConfiguration = require('./library/get-configuration');
8 | const getInput = require('./library/get-input');
9 | const resolveKeys = require('./library/resolve-keys');
10 | const lint = require('./library/lint');
11 | const format = require('./library/format');
12 | const print = require('./library/print');
13 | const filter = require('./library/filter');
14 | const pkg = require('./package');
15 |
16 | // Main program
17 | function main(options) {
18 | return listFiles(options.input)
19 | // Load file configurations
20 | .then(getConfiguration)
21 | // Filter files according to ignore config
22 | .then(filter)
23 | // Load file contents
24 | .then(getInput)
25 | // Fetch json schemas
26 | .then(fetchSchema)
27 | // Wait for resolution of async tasks
28 | .then(resolveKeys)
29 | .then(inputs => {
30 | // Merge cli options on file configuration
31 | return inputs.map(input => {
32 | input.configuration = merge({}, input.configuration, options.flags);
33 | return input;
34 | });
35 | })
36 | .then(inputs => {
37 | // Lint and validate files
38 | return inputs.map(input => {
39 | input.content.path = input.path;
40 | input.data = lint(input.content, input.configuration, input.schema);
41 | return input;
42 | });
43 | })
44 | .then(inputs => {
45 | // Format results
46 | return inputs.map(input => {
47 | input.formatted = format(input.data, input.configuration);
48 | return input;
49 | });
50 | })
51 | .then(inputs => {
52 | // Print results
53 | inputs.forEach(print);
54 | return inputs;
55 | });
56 | }
57 |
58 | // Start the engines
59 | main(cli)
60 | .catch(error =>
61 | setTimeout(() => {
62 | if (error.type === pkg.name) {
63 | console.error(error.message);
64 | process.exit(1);
65 | }
66 | throw error;
67 | })
68 | );
69 |
70 | // handle unhandled rejections
71 | process.on('unhandledRejection', (reason, promise) => {
72 | if (reason.type === pkg.name) {
73 | process.exit(1);
74 | }
75 | console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
76 | throw reason;
77 | });
78 |
79 | /* const fs = require('fs');
80 | const path = require('path');
81 | const url = require('url');
82 | const crypto = require('crypto');
83 |
84 | const minimist = require('minimist');
85 | const rcNodeBack = require('cli-rc');
86 | const globby = require('globby');
87 | const parser = require('jsonlint').parser;
88 | const denodeify = require('denodeify');
89 | const jjv = require('jjv');
90 | const request = require('sync-request');
91 | const mkdirp = require('mkdirp');
92 | var memoize = require('lodash.memoize');
93 | var minimatch = require('minimatch');
94 |
95 | const read = denodeify(fs.readFile);
96 | const rc = denodeify(rcNodeBack);
97 |
98 | const pkg = require('./package.json');
99 |
100 | const defaults = {
101 | ignore: ['node_modules'],
102 | validate: null,
103 | indent: ' ',
104 | env: 'json-schema-draft-04',
105 | quiet: false,
106 | pretty: false
107 | };
108 |
109 | const aliases = {
110 | ignore: 'i',
111 | validate: 's',
112 | indent: 'w',
113 | env: 'e',
114 | quiet: 'q',
115 | pretty: 'p'
116 | };
117 |
118 | const descriptions = {
119 | ignore: 'glob pattern to exclude from linting',
120 | validate: 'uri to schema to use for validation',
121 | indent: 'whitespace to use for pretty printing',
122 | env: 'json schema env to use for validation',
123 | quiet: 'surpress all output',
124 | pretty: 'pretty-print the input'
125 | };
126 |
127 | function repeat(s, count) {
128 | return new Array(count + 1).join(s);
129 | }
130 | function formatJson(source, indent) {
131 | var i = 0; // eslint-disable-line no-var
132 | var il = 0; // eslint-disable-line no-var
133 | var tab = (typeof indent === 'undefined') ? ' ' : indent; // eslint-disable-line no-var
134 | var newJson = ''; // eslint-disable-line no-var
135 | var indentLevel = 0; // eslint-disable-line no-var
136 | var inString = false; // eslint-disable-line no-var
137 | var currentChar = null; // eslint-disable-line no-var
138 |
139 | for (i = 0, il = source.length; i < il; i += 1) {
140 | currentChar = source.charAt(i);
141 |
142 | switch (currentChar) {
143 | case '{':
144 | case '[':
145 | if (inString) {
146 | newJson += currentChar;
147 | } else {
148 | newJson += currentChar + '\n' + repeat(tab, indentLevel + 1); // eslint-disable-line prefer-template
149 | indentLevel += 1;
150 | }
151 | break;
152 | case '}':
153 | case ']':
154 | if (inString) {
155 | newJson += currentChar;
156 | } else {
157 | indentLevel -= 1;
158 | newJson += '\n' + repeat(tab, indentLevel) + currentChar; // eslint-disable-line prefer-template
159 | }
160 | break;
161 | case ',':
162 | if (inString) {
163 | newJson += currentChar;
164 | } else {
165 | newJson += ',\n' + repeat(tab, indentLevel); // eslint-disable-line prefer-template
166 | }
167 | break;
168 | case ':':
169 | if (inString) {
170 | newJson += currentChar;
171 | } else {
172 | newJson += ': ';
173 | }
174 | break;
175 | case ' ':
176 | case '\n':
177 | case '\t':
178 | if (inString) {
179 | newJson += currentChar;
180 | }
181 | break;
182 | case '"':
183 | if (i > 0 && source.charAt(i - 1) !== '\\') {
184 | inString = !inString;
185 | }
186 | newJson += currentChar;
187 | break;
188 | default:
189 | newJson += currentChar;
190 | break;
191 | }
192 | }
193 | return newJson;
194 | }
195 |
196 | function sort(o) {
197 | if (Array.isArray(o)) {
198 | return o.map(sort);
199 | } else if (Object.prototype.toString.call(o) !== '[object Object]') {
200 | return o;
201 | }
202 |
203 | const sorted = {};
204 | const a = [];
205 | var key; // eslint-disable-line no-var
206 |
207 | for (key in o) {
208 | if (o.hasOwnProperty(key)) {
209 | a.push(key);
210 | }
211 | }
212 |
213 | a.sort();
214 |
215 | for (key = 0; key < a.length; key++) {
216 | sorted[a[key]] = sort(o[a[key]]);
217 | }
218 |
219 | return sorted;
220 | }
221 |
222 | const lex = {
223 | type(key, value) {
224 | return `"${key}" must be of type "${value}"`;
225 | },
226 | minLength(key, value, ruleName, ruleValue) {
227 | return `"${key}" must be at least "${ruleValue}" characters`;
228 | },
229 | maxLength(key, value, ruleName, ruleValue) {
230 | return `"${key}" may be at most "${ruleValue}" characters`;
231 | },
232 | minProperties(key, value, ruleName, ruleValue) {
233 | return `"${key}" must hold at least "${ruleValue}" properties`;
234 | },
235 | maxProperties(key, value, ruleName, ruleValue) {
236 | return `"${key}" may hold at most "${ruleValue}" properties`;
237 | },
238 | patternProperties(key, value, ruleName, ruleValue) {
239 | return `"${key}" must hold "${ruleValue}" properties`;
240 | },
241 | minItems(key, value, ruleName, ruleValue) {
242 | return `"${key}" must have at leat "${ruleValue}" items`;
243 | },
244 | maxItems(key, value, ruleName, ruleValue) {
245 | return `"${key}" may have at most "${ruleValue}" items`;
246 | },
247 | required(key, _, name) {
248 | return `"${key}" is ${name} but unset`;
249 | },
250 | additional(key, value, name) {
251 | return `"${key}" is not allowed as ${name} key `;
252 | },
253 | fallback(key, value, ruleName, ruleValue, prop) {
254 | const ruleValueString = typeof ruleValue === 'string' ? JSON.stringify(ruleValue) : ruleValue;
255 | return `"${key}" does not meet rule "${ruleName}=${ruleValueString}" - ${prop.description}`;
256 | }
257 | };
258 |
259 | function schemaError(error, schema) {
260 | return Object.keys(error.validation)
261 | .reduce((messages, key) => {
262 | const validation = error.validation[key];
263 | const names = Object.keys(validation);
264 |
265 | return messages.concat(
266 | names
267 | .map(name => lex[name] || lex.fallback)
268 | .map((formatter, index) => {
269 | const name = names[index];
270 | const props = schema.properties || {};
271 | const prop = props[key] || {};
272 | return formatter(
273 | key,
274 | validation[name],
275 | name,
276 | prop[name] || '',
277 | prop
278 | );
279 | })
280 | );
281 | }, []);
282 | }
283 |
284 | function getSchemaCacheId(uri) {
285 | const sum = crypto.createHash('md5');
286 | sum.update(uri);
287 | return sum.digest('hex');
288 | }
289 |
290 | function readSchemaCache(uri) {
291 | const id = getSchemaCacheId(uri);
292 | const tmp = path.resolve(__dirname, '.tmp', `${id}.json`);
293 |
294 | try {
295 | return fs.readFileSync(tmp);
296 | } catch (error) {
297 | return null;
298 | }
299 | }
300 |
301 | function writeSchemaCache(uri, schema) {
302 | const id = getSchemaCacheId(uri);
303 | const tmp = path.resolve(__dirname, '.tmp', `${id}.json`);
304 |
305 | try {
306 | mkdirp.sync(path.dirname(tmp));
307 | return fs.writeFileSync(tmp, schema);
308 | } catch (error) {
309 | return null;
310 | }
311 | }
312 |
313 | function getSchema(uri) {
314 | const parsed = url.parse(uri);
315 |
316 | if (parsed.protocol && parsed.host) {
317 | const buffer = readSchemaCache(uri);
318 | const response = buffer ? buffer.toString('utf-8') : request('GET', uri).getBody();
319 | const data = JSON.parse(response);
320 | writeSchemaCache(uri, response);
321 | return data;
322 | }
323 |
324 | return require(uri);
325 | }
326 |
327 | const obtainSchema = memoize(getSchema);
328 |
329 | function lint(source, sourcePath, settings) {
330 | return new Promise((resolve, reject) => {
331 | const absSourcePath = sourcePath ? path.resolve(sourcePath) : null;
332 |
333 | try {
334 | const parsed = settings.source ?
335 | sort(parser.parse(source)) :
336 | parser.parse(source);
337 |
338 | if (settings.pretty && !settings.quiet) {
339 | console.log(formatJson(source, settings.indent));
340 | }
341 |
342 | if (settings.validate) {
343 | const environment = jjv(settings.env);
344 | const schema = obtainSchema(settings.validate);
345 | environment.addSchema('default', schema);
346 | const errors = environment.validate('default', parsed);
347 | if (errors) {
348 | const jsonLintError = new Error([`"${absSourcePath}" fails against schema "${settings.validate}"`]
349 | .concat(schemaError(errors, schema)
350 | .filter(Boolean)
351 | .map(message => ` ${message}`)
352 | ).join('\n'));
353 |
354 | jsonLintError.type = 'jsonlint';
355 | throw jsonLintError;
356 | }
357 | }
358 | } catch (error) {
359 | error.message = settings.quiet ? null : `${absSourcePath} ${error.message}`;
360 | error.file = absSourcePath;
361 | error.type = 'jsonlint';
362 | reject(error);
363 | }
364 | });
365 | }
366 |
367 | function readStdin() {
368 | return new Promise(resolve => {
369 | const source = [];
370 | const stdin = process.openStdin();
371 | stdin.setEncoding('utf8');
372 | stdin.on('data', chunk => {
373 | source.push(chunk.toString('utf8'));
374 | });
375 | stdin.on('end', () => {
376 | resolve(source.join(''));
377 | });
378 | });
379 | }
380 |
381 | function getSettings(options, file) {
382 | const filePath = path.extname(file) ?
383 | path.dirname(file) :
384 | file;
385 |
386 | const loaders = [
387 | {
388 | name: '.jsonlintrc',
389 | path: [filePath],
390 | prepend: [filePath]
391 | },
392 | {
393 | name: '.jsonlintignore',
394 | path: [filePath],
395 | type: 'ini',
396 | prepend: [filePath]
397 | }
398 | ].map(loader => {
399 | return rc(loader)
400 | .then(config => Object.assign(config))
401 | .catch(error => {
402 | setTimeout(() => {
403 | throw error;
404 | });
405 | });
406 | });
407 |
408 | return Promise.all(loaders)
409 | .then(results => {
410 | const configuration = results[0];
411 | configuration._file = file;
412 |
413 | const ignore = (options.ignore || [])
414 | .concat(results[0].ignore || [])
415 | .concat(Object.keys(results[1]));
416 |
417 | if (configuration.validate) {
418 | const parsed = url.parse(configuration.validate);
419 | configuration.validate = parsed.protocol && parsed.host ?
420 | configuration.validate :
421 | path.resolve(file, configuration.validate);
422 | }
423 |
424 | return Object.assign({}, defaults, options, configuration, {
425 | ignore
426 | });
427 | });
428 | }
429 |
430 | function execute(settings) {
431 | const files = settings._ || [];
432 | const ignored = settings.ignore.map(rule => `!${rule}`);
433 | const glob = files.concat(ignored);
434 |
435 | // read from stdin if no files are given
436 | if (files.length === 0) {
437 | return readStdin()
438 | .then(content => {
439 | return lint(content, null, settings);
440 | });
441 | }
442 |
443 | // read the glob if files are given
444 | return globby(glob).then(paths => {
445 | Promise
446 | .all(paths.map(file => {
447 | return getSettings(settings, file);
448 | }))
449 | .then(configurations => {
450 | return Promise.all(
451 | configurations.map(configuration => {
452 | // ignore could be changed by config files
453 | const ignored = configuration.ignore
454 | .filter(pattern => {
455 | return minimatch(configuration._file, pattern) ||
456 | path.basename(configuration._file) === pattern;
457 | }).length > 0;
458 |
459 | if (ignored) {
460 | return null;
461 | }
462 | return read(configuration._file)
463 | .then(content => {
464 | return {
465 | content: content.toString('utf-8'),
466 | path: configuration._file,
467 | configuration: configuration
468 | };
469 | });
470 | })
471 | );
472 | })
473 | .then(payloads => {
474 | return Promise.all(
475 | payloads.map(payload => {
476 | return lint(
477 | payload.content,
478 | payload.path,
479 | payload.configuration
480 | );
481 | })
482 | );
483 | });
484 | });
485 | }
486 |
487 | function printFlags() {
488 | const flags = Object.keys(defaults)
489 | .map(key => {
490 | return [`--${aliases[key]}, --${key}`, `${descriptions[key]}, defaults to: "${defaults[key]}"`];
491 | });
492 |
493 | const lines = [
494 | [`--h, --help`, `show this help`],
495 | [`--v, --version`, `show jsonlint-cli version`]
496 | ].concat(flags);
497 |
498 | const longestKeyLine = lines.sort((a, b) => b[0].length - a[0].length)[0];
499 | const longest = longestKeyLine[0].length;
500 |
501 | return lines
502 | .map(line => `${line[0]}${' '.repeat(4 + longest - line[0].length)}${line[1]}`)
503 | .join('\n');
504 | }
505 |
506 | function help() {
507 | console.log(`
508 | ${pkg.name} [options] [file] - ${pkg.description}
509 |
510 | ${printFlags()}
511 | `);
512 | }
513 |
514 | function main(options) {
515 | if (options.help) {
516 | help();
517 | return Promise.resolve();
518 | }
519 |
520 | if (options.version) {
521 | console.log(pkg.version);
522 | return Promise.resolve();
523 | }
524 |
525 | return new Promise((resolve, reject) => {
526 | getSettings(options, process.cwd())
527 | .then(execute)
528 | .then(resolve)
529 | .catch(reject);
530 | });
531 | }
532 |
533 | // parse cli flags
534 | const args = minimist(process.argv.slice(2));
535 |
536 | // start the main function
537 | main(args)
538 | .catch(error => {
539 | if (error.type === 'jsonlint') {
540 | if (error.message !== null) {
541 | console.log(error.message);
542 | }
543 | process.exit(1);
544 | } else {
545 | setTimeout(() => {
546 | throw error;
547 | });
548 | }
549 | });
550 |
551 | // Catch unhandled rejections globally
552 | process.on('unhandledRejection', (reason, promise) => {
553 | console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
554 | throw reason;
555 | });
556 | */
557 |
--------------------------------------------------------------------------------
/jsonlint-cli-banner.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/jsonlint-cli.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/library/cli.js:
--------------------------------------------------------------------------------
1 | const meow = require('meow');
2 | const pkg = require('../package');
3 |
4 | const help = require('./help');
5 | const unknown = require('./unknown');
6 |
7 | const configuration = {
8 | // flags of string type
9 | string: ['ignore', 'validate', 'indent', 'env'],
10 | // flags of bool type
11 | boolean: ['pretty', 'sort', 'help', 'version'],
12 | // flag aliases
13 | alias: {
14 | i: 'ignore',
15 | s: 'validate',
16 | t: 'sort',
17 | w: 'indent',
18 | e: 'env',
19 | p: 'pretty',
20 | h: 'help',
21 | v: 'version'
22 | },
23 | description: {
24 | ignore: 'glob pattern to exclude from linting',
25 | validate: 'uri to schema to use for validation',
26 | indent: 'whitespace to use for pretty printing',
27 | env: 'json schema env to use for validation',
28 | pretty: 'pretty-print the input',
29 | sort: 'sort json keys alphabetically',
30 | version: 'print the version',
31 | help: 'show this help'
32 | },
33 | // flag defaults
34 | default: {
35 | ignore: ['node_modules'],
36 | validate: '',
37 | indent: '" "',
38 | env: 'json-schema-draft-04',
39 | quiet: true,
40 | pretty: false,
41 | sort: false
42 | },
43 | unknown: unknown
44 | };
45 |
46 | module.exports = meow({
47 | help: `[input] reads from stdin if [files] are omitted\n${help(configuration)}`,
48 | description: `${pkg.name}@${pkg.version} - ${pkg.description}`
49 | }, configuration);
50 |
--------------------------------------------------------------------------------
/library/fetch-schema.js:
--------------------------------------------------------------------------------
1 | const loadSchema = require('./load-schema');
2 |
3 | module.exports = items => {
4 | return Promise.all(items.map(item => {
5 | return item.configuration
6 | .then(config => {
7 | item.configuration = config;
8 | if (config.validate) {
9 | item.schema = loadSchema(config.validate);
10 | }
11 | return item;
12 | });
13 | }));
14 | };
15 |
--------------------------------------------------------------------------------
/library/filter.js:
--------------------------------------------------------------------------------
1 | const debuglog = require('util').debuglog;
2 | const relative = require('path').relative;
3 | const basename = require('path').basename;
4 | const minimatch = require('minimatch');
5 | const merge = require('lodash').merge;
6 |
7 | const log = debuglog('jsonlint-cli');
8 |
9 | module.exports = files => {
10 | const configuringFiles = Promise.all(
11 | files.map(file => {
12 | return file.configuration
13 | .then(configuration => {
14 | return merge({}, file, {
15 | configuration: configuration
16 | });
17 | });
18 | })
19 | );
20 |
21 | return configuringFiles
22 | .then(configuredFiles => {
23 | log('found files:', configuredFiles.length);
24 | return configuredFiles
25 | .filter(configuredFile => {
26 | const ignore = configuredFile.configuration.ignore || [];
27 | const config = Array.isArray(ignore) ?
28 | ignore : ignore.split(',');
29 | return !config.some(pattern => {
30 | return minimatch(configuredFile.path, pattern) ||
31 | minimatch(relative(process.cwd(), configuredFile.path), pattern) ||
32 | basename(configuredFile.path) === pattern;
33 | });
34 | });
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/library/format.js:
--------------------------------------------------------------------------------
1 | const sort = require('./sort');
2 |
3 | function repeat(s, count) {
4 | return new Array(count + 1).join(s);
5 | }
6 |
7 | function formatJSON(source, indent) {
8 | var i = 0; // eslint-disable-line no-var
9 | var il = 0; // eslint-disable-line no-var
10 | var tab = (typeof indent === 'undefined') ? ' ' : indent; // eslint-disable-line no-var
11 | var newJson = ''; // eslint-disable-line no-var
12 | var indentLevel = 0; // eslint-disable-line no-var
13 | var inString = false; // eslint-disable-line no-var
14 | var currentChar = null; // eslint-disable-line no-var
15 |
16 | for (i = 0, il = source.length; i < il; i += 1) {
17 | currentChar = source.charAt(i);
18 |
19 | switch (currentChar) {
20 | case '{':
21 | case '[':
22 | if (inString) {
23 | newJson += currentChar;
24 | } else {
25 | newJson += currentChar + '\n' + repeat(tab, indentLevel + 1); // eslint-disable-line prefer-template
26 | indentLevel += 1;
27 | }
28 | break;
29 | case '}':
30 | case ']':
31 | if (inString) {
32 | newJson += currentChar;
33 | } else {
34 | indentLevel -= 1;
35 | newJson += '\n' + repeat(tab, indentLevel) + currentChar; // eslint-disable-line prefer-template
36 | }
37 | break;
38 | case ',':
39 | if (inString) {
40 | newJson += currentChar;
41 | } else {
42 | newJson += ',\n' + repeat(tab, indentLevel); // eslint-disable-line prefer-template
43 | }
44 | break;
45 | case ':':
46 | if (inString) {
47 | newJson += currentChar;
48 | } else {
49 | newJson += ': ';
50 | }
51 | break;
52 | case ' ':
53 | case '\n':
54 | case '\t':
55 | if (inString) {
56 | newJson += currentChar;
57 | }
58 | break;
59 | case '"':
60 | if (i > 0 && source.charAt(i - 1) !== '\\') {
61 | inString = !inString;
62 | }
63 | newJson += currentChar;
64 | break;
65 | default:
66 | newJson += currentChar;
67 | break;
68 | }
69 | }
70 | return newJson;
71 | }
72 |
73 | module.exports = (data, settings) => {
74 | const sorted = settings.sort ?
75 | sort(data) :
76 | data;
77 | return formatJSON(JSON.stringify(sorted), settings.indent.replace(/["']/g, ''));
78 | };
79 |
--------------------------------------------------------------------------------
/library/get-configuration.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const rcNodeBack = require('cli-rc');
3 | const denodeify = require('denodeify');
4 | const entries = require('core-js/fn/object/entries');
5 | const merge = require('lodash').merge;
6 |
7 | const rc = denodeify(rcNodeBack);
8 |
9 | const loaders = [
10 | {
11 | name: '.jsonlintrc',
12 | process: config => config
13 | },
14 | {
15 | name: '.jsonlintignore',
16 | type: 'ini',
17 | process: config => {
18 | return {
19 | ignore: entries(config)
20 | .filter(item => item[1])
21 | .map(item => item[0])
22 | };
23 | }
24 | }
25 | ];
26 |
27 | function getPaths(directory) {
28 | return directory
29 | .split(path.sep)
30 | .map((_, index) => {
31 | const args = [directory].concat(Array(index).fill('..'));
32 | return path.resolve.apply(null, args);
33 | });
34 | }
35 |
36 | function load(directory) {
37 | return Promise.all(
38 | loaders
39 | .map(load => {
40 | const paths = getPaths(directory);
41 | const loader = merge({}, load, {
42 | path: [directory],
43 | prepend: paths
44 | });
45 | return rc(loader)
46 | .then(result => {
47 | return result;
48 | })
49 | .then(loader.process);
50 | })
51 | ).then(configurations => {
52 | return configurations
53 | .reverse()
54 | .reduce((registry, config) => {
55 | return merge({}, registry, config);
56 | }, {});
57 | });
58 | }
59 |
60 | module.exports = inputs => {
61 | return inputs
62 | .map(input => {
63 | input.configuration = load(input.directory);
64 | return input;
65 | });
66 | };
67 |
--------------------------------------------------------------------------------
/library/get-input.js:
--------------------------------------------------------------------------------
1 | const debuglog = require('util').debuglog;
2 | const readFileNodeback = require('fs').readFile;
3 | const denodeify = require('denodeify');
4 |
5 | const readFile = denodeify(readFileNodeback);
6 | const log = debuglog('jsonlint-cli');
7 |
8 | module.exports = files => {
9 | log('reading files:', files.length);
10 | return Promise.all(
11 | files.map(file => {
12 | return file.configuration
13 | .then(configuration => {
14 | if (file.content && file.piped) {
15 | return file;
16 | }
17 | return readFile(file.path)
18 | .then(content => {
19 | file.content = content;
20 | return file;
21 | })
22 | });
23 | })
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/library/help.js:
--------------------------------------------------------------------------------
1 | const entries = require('core-js/fn/object/entries');
2 |
3 | module.exports = configuration => {
4 | const lines = entries(configuration.description)
5 | .map(entry => {
6 | const name = entry[0];
7 | const desc = entry[1];
8 | const alias = Object.entries(configuration.alias)
9 | .find(entry => entry[1] === name)
10 | .map(entry => entry[0])[0];
11 | const defaults = configuration.default[name];
12 | return [[name, alias].filter(Boolean), desc, defaults]
13 | .filter(item => item !== null && typeof item !== 'undefined');
14 | });
15 |
16 | const longest = lines
17 | .map(line => {
18 | const flags = line[0];
19 | return flags.reduce((sum, flag) => sum + flag.length, 0);
20 | })
21 | .sort(Number)[0];
22 |
23 | return lines
24 | .map(line => {
25 | const flags = line[0];
26 | const desc = line[1];
27 | const defaults = line[2];
28 | const fs = flags.map(flag => flag.length > 1 ? `--${flag}` : `-${flag}`);
29 | const ds = typeof defaults === 'undefined' ? '' : `, defaults to: ${defaults}`;
30 | const length = flags.reduce((sum, flag) => sum + flag.length, 0);
31 | return `${fs.join(',')}${' '.repeat(7 + longest - length)}${desc}${ds}`;
32 | })
33 | .join('\n');
34 | };
35 |
--------------------------------------------------------------------------------
/library/lint.js:
--------------------------------------------------------------------------------
1 | const parser = require('jsonlint').parser;
2 | const jjv = require('jjv');
3 | const schemaError = require('./schema-error');
4 | const pkg = require('../package');
5 |
6 | function parse(source) {
7 | try {
8 | return parser.parse(source);
9 | } catch (error) {
10 | error.type = pkg.name;
11 | error.step = 'parse';
12 | throw error;
13 | }
14 | }
15 |
16 | function validate(data, settings, schema) {
17 | if (!schema) {
18 | return data;
19 | }
20 |
21 | const environment = jjv(settings.env);
22 | environment.addSchema('default', schema);
23 | const errors = environment.validate('default', data);
24 |
25 | if (errors) {
26 | const message = schemaError(errors, schema)
27 | .filter(Boolean).join('\n');
28 | const error = new Error(message);
29 | error.type = pkg.name;
30 | error.step = 'validation';
31 | throw error;
32 | }
33 |
34 | return data;
35 | }
36 |
37 | module.exports = (source, settings, schema) => {
38 | try {
39 | const json = source.toString();
40 | const parsed = parse(json, settings);
41 | return validate(parsed, settings, schema);
42 | } catch (error) {
43 | const intro = `${source.path} ${error.step}`;
44 | error.file = error.file || source.path;
45 | error.message = `${intro}:\n${error.message}`;
46 | throw error;
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/library/list-files.js:
--------------------------------------------------------------------------------
1 | const dirname = require('path').dirname;
2 | const resolve = require('path').resolve;
3 | const globby = require('globby');
4 | const getStdin = require('get-stdin');
5 |
6 | module.exports = input => {
7 | if (input.length === 0) {
8 | return Promise.resolve(
9 | [{
10 | content: getStdin.buffer(),
11 | directory: process.cwd(),
12 | path: process.cwd(),
13 | piped: true
14 | }]
15 | );
16 | }
17 | return globby(input)
18 | .then(paths => {
19 | return paths.map(path => {
20 | return {
21 | content: null,
22 | directory: dirname(resolve(process.cwd(), path)),
23 | path: resolve(process.cwd(), path),
24 | piped: false
25 | };
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/library/load-schema.js:
--------------------------------------------------------------------------------
1 | const createHash = require('crypto').createHash;
2 | const denodeify = require('denodeify');
3 | const readFileNodeback = require('fs').readFile;
4 | const writeFileNodeback = require('fs').writeFile;
5 | const parse = require('url').parse;
6 | const resolve = require('path').resolve;
7 | const dirname = require('path').dirname;
8 | const mkdirpNodeback = require('mkdirp');
9 | const fetch = require('omni-fetch');
10 |
11 | const readFile = denodeify(readFileNodeback);
12 | const writeFile = denodeify(writeFileNodeback);
13 | const mkdirp = denodeify(mkdirpNodeback);
14 |
15 | function getSchemaCacheId(uri) {
16 | const sum = createHash('md5');
17 | sum.update(uri);
18 | return sum.digest('hex');
19 | }
20 |
21 | function readSchemaCache(uri) {
22 | const id = getSchemaCacheId(uri);
23 | const tmp = resolve(__dirname, '.tmp', `${id}.json`);
24 | return readFile(tmp)
25 | .catch(() => {});
26 | }
27 |
28 | function writeSchemaCache(uri, schema) {
29 | const id = getSchemaCacheId(uri);
30 | const tmp = resolve(__dirname, '..', '.tmp', `${id}.json`);
31 |
32 | mkdirp(dirname(tmp))
33 | .then(() => {
34 | return writeFile(tmp, schema);
35 | })
36 | .catch(err => {
37 | console.log(err);
38 | });
39 | }
40 |
41 | module.exports = schema => {
42 | const parsed = parse(schema);
43 |
44 | if (parsed.protocol && parsed.host) {
45 | return readSchemaCache(schema)
46 | .then(buffer => {
47 | return buffer ?
48 | buffer.toString('utf-8') :
49 | fetch(schema)
50 | .then(resp => resp.text())
51 | .then(data => {
52 | setTimeout(() => {
53 | writeSchemaCache(schema, data);
54 | }, 0);
55 | return data;
56 | });
57 | })
58 | .then(data => JSON.parse(data));
59 | }
60 |
61 | return require(schema);
62 | };
63 |
--------------------------------------------------------------------------------
/library/print.js:
--------------------------------------------------------------------------------
1 | module.exports = input => {
2 | if (input.configuration.pretty) {
3 | console.log(input.formatted);
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/library/resolve-keys.js:
--------------------------------------------------------------------------------
1 | const entries = require('core-js/fn/object/entries');
2 | const merge = require('lodash').merge;
3 |
4 | module.exports = items => {
5 | return Promise.all(
6 | items
7 | .map(item => {
8 | return Promise
9 | .all(entries(item).map(entry => {
10 | return Promise
11 | .resolve(entry[1])
12 | .then(result => {
13 | const resolved = {};
14 | resolved[entry[0]] = result;
15 | return resolved;
16 | });
17 | }))
18 | .then(resolved => {
19 | return resolved.reduce((registry, entry) => {
20 | return merge(registry, entry);
21 | }, {});
22 | });
23 | })
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/library/schema-error.js:
--------------------------------------------------------------------------------
1 | const lex = {
2 | type(key, value) {
3 | return `"${key}" must be of type "${value}"`;
4 | },
5 | minLength(key, value, ruleName, ruleValue) {
6 | return `"${key}" must be at least "${ruleValue}" characters`;
7 | },
8 | maxLength(key, value, ruleName, ruleValue) {
9 | return `"${key}" may be at most "${ruleValue}" characters`;
10 | },
11 | minProperties(key, value, ruleName, ruleValue) {
12 | return `"${key}" must hold at least "${ruleValue}" properties`;
13 | },
14 | maxProperties(key, value, ruleName, ruleValue) {
15 | return `"${key}" may hold at most "${ruleValue}" properties`;
16 | },
17 | patternProperties(key, value, ruleName, ruleValue) {
18 | return `"${key}" must hold "${ruleValue}" properties`;
19 | },
20 | minItems(key, value, ruleName, ruleValue) {
21 | return `"${key}" must have at leat "${ruleValue}" items`;
22 | },
23 | maxItems(key, value, ruleName, ruleValue) {
24 | return `"${key}" may have at most "${ruleValue}" items`;
25 | },
26 | required(key, _, name) {
27 | return `"${key}" is ${name} but unset`;
28 | },
29 | additional(key, value, name) {
30 | return `"${key}" is not allowed as ${name} key `;
31 | },
32 | fallback(key, value, ruleName, ruleValue, prop) {
33 | const ruleValueString = typeof ruleValue === 'string' ? JSON.stringify(ruleValue) : ruleValue;
34 | return `"${key}" does not meet rule "${ruleName}=${ruleValueString}" - ${prop.description}`;
35 | }
36 | };
37 |
38 | module.exports = function schemaError(error, schema) {
39 | return Object.keys(error.validation)
40 | .reduce((messages, key) => {
41 | const validation = error.validation[key];
42 | const names = Object.keys(validation);
43 |
44 | return messages.concat(
45 | names
46 | .map(name => lex[name] || lex.fallback)
47 | .map((formatter, index) => {
48 | const name = names[index];
49 | const props = schema.properties || {};
50 | const prop = props[key] || {};
51 | return formatter(
52 | key,
53 | validation[name],
54 | name,
55 | prop[name] || '',
56 | prop
57 | );
58 | })
59 | );
60 | }, []);
61 | };
62 |
--------------------------------------------------------------------------------
/library/sort.js:
--------------------------------------------------------------------------------
1 | const entries = require('core-js/fn/object/entries');
2 |
3 | function entrySorter(a, b) {
4 | const aKey = a[0];
5 | const bKey = b[0];
6 | if (aKey < bKey) {
7 | return -1;
8 | } else if (aKey > bKey) {
9 | return 1;
10 | }
11 | return 0;
12 | }
13 |
14 | module.exports = function sort(data) {
15 | if (Array.isArray(data)) {
16 | return data.map(sort);
17 | } else if (Object.prototype.toString.call(data) !== '[object Object]') {
18 | return data;
19 | }
20 |
21 | return entries(data)
22 | .sort(entrySorter)
23 | .reduce((sorted, item) => {
24 | sorted[item[0]] = item[1];
25 | return sorted;
26 | }, {});
27 | };
28 |
--------------------------------------------------------------------------------
/library/unknown.js:
--------------------------------------------------------------------------------
1 | const pkg = require('../package');
2 |
3 | function isFlag(arg) {
4 | return arg[0] === '-' ||
5 | arg.slice(0, 2) === '--';
6 | }
7 |
8 | module.exports = arg => {
9 | if (isFlag(arg) === false) {
10 | return;
11 | }
12 | const error = new RangeError(`unknown flags: ${arg}`);
13 | error.type = pkg.name;
14 | throw error;
15 | };
16 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mario Nebl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ---
24 | Copyright 2016 by [Mario Nebl](https://github.com/marionebl) and [contributors](./graphs/contributors). Released under the [MIT license]('./license.md').
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsonlint-cli",
3 | "version": "1.0.1",
4 | "description": "cli wrapper for jsonlint",
5 | "main": "index.js",
6 | "scripts": {
7 | "commit": "git-cz",
8 | "commitmsg": "conventional-changelog-lint -e",
9 | "changelog": "conventional-changelog --preset angular --infile changelog.md --same-file --output-unreleased",
10 | "push": "git push && git push --tags && hub release create \"v$(cat .git/RELEASE_VERSION.tmp)\" --message=\"v$(cat .git/RELEASE_VERSION.tmp)\n$(cat .git/COMMITMSG.tmp)\" && npm publish && rm .git/RELEASE_VERSION.tmp && rm .git/COMMITMSG.tmp",
11 | "release": "npm version $(conventional-recommended-bump -p angular)",
12 | "test": "parallelshell \"eslint index.js\" \"node index.js **/*.json *.json\" ",
13 | "preversion": "npm test",
14 | "version": "npm run changelog && git add . && echo \"$(conventional-changelog -p angular)\" > .git/COMMITMSG.tmp",
15 | "postversion": "echo $(git log -1 --pretty=%B HEAD^..HEAD) > .git/RELEASE_VERSION.tmp && git tag -d v$(cat .git/RELEASE_VERSION.tmp) && git commit --amend -m \"chore(release): $(cat .git/RELEASE_VERSION.tmp)\n$(cat .git/COMMITMSG.tmp)\" && git tag -a v$(cat .git/RELEASE_VERSION.tmp) -m \"$(cat .git/COMMITMSG.tmp)\""
16 | },
17 | "config": {
18 | "commitizen": {
19 | "path": "cz-conventional-changelog-lint"
20 | }
21 | },
22 | "bin": {
23 | "jsonlint-cli": "./index.js"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/marionebl/jsonlint-cli.git"
28 | },
29 | "keywords": [
30 | "jsonlint",
31 | "cli",
32 | "jsonschema"
33 | ],
34 | "author": {
35 | "name": "Mario Nebl",
36 | "email": "hello@herebecode.com"
37 | },
38 | "license": "MIT",
39 | "bugs": {
40 | "url": "https://github.com/marionebl/jsonlint-cli/issues"
41 | },
42 | "homepage": "https://github.com/marionebl/jsonlint-cli#readme",
43 | "devDependencies": {
44 | "commitizen": "2.7.2",
45 | "conventional-changelog-cli": "1.0.0",
46 | "conventional-changelog-lint": "0.3.2",
47 | "conventional-recommended-bump": "0.1.0",
48 | "cz-conventional-changelog-lint": "0.1.3",
49 | "eslint": "1.10.3",
50 | "eslint-config-xo": "0.9.2",
51 | "husky": "0.11.3",
52 | "parallelshell": "2.0.0"
53 | },
54 | "dependencies": {
55 | "cli-rc": "1.0.12",
56 | "core-js": "2.1.5",
57 | "denodeify": "1.2.1",
58 | "get-stdin": "5.0.1",
59 | "globby": "4.0.0",
60 | "isomorphic-fetch": "2.2.1",
61 | "jjv": "1.0.2",
62 | "jsonlint": "1.6.2",
63 | "lodash": "4.6.1",
64 | "meow": "3.7.0",
65 | "minimatch": "3.0.4",
66 | "mkdirp": "0.5.1",
67 | "omni-fetch": "0.1.0",
68 | "path-exists": "2.1.0"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/pattern/.jsonlintrc:
--------------------------------------------------------------------------------
1 | {
2 | "validate": "./pattern-schema.json",
3 | "ignore": ["pattern-schema.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/pattern/pattern-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "title": "pattern manifest",
4 | "type": "object",
5 | "additionalProperties": false,
6 | "required": [
7 | "name",
8 | "version"
9 | ],
10 | "properties": {
11 | "id": {
12 | "description": "Unique id of this pattern",
13 | "type": "string",
14 | "minLength": 1
15 | },
16 | "name": {
17 | "description": "Machine readable name of the pattern",
18 | "type": "string",
19 | "minLength": 1,
20 | "pattern": "^[[a-z]*[-]?[a-z]*]*$"
21 | },
22 | "displayName": {
23 | "description": "Human readable name of the pattern",
24 | "type": "string",
25 | "minLength": 1
26 | },
27 | "version": {
28 | "description": "Semantic version of the pattern",
29 | "type": "string",
30 | "pattern": "^\\d\\.\\d\\.\\d(-[a-z]*){0,1}$"
31 | },
32 | "versions": {
33 | "description": "Available semantic versions of the pattern",
34 | "type": "array",
35 | "minItems": 1,
36 | "items": {
37 | "description": "Semantic version of the pattern",
38 | "type": "string",
39 | "pattern": "^\\d\\.\\d\\.\\d(-[a-z]*){0,1}$"
40 | }
41 | },
42 | "flag": {
43 | "description": "Stability flag of the pattern",
44 | "type": "string",
45 | "pattern": "^alpha|beta|rc|stable$"
46 | },
47 | "tags": {
48 | "description": "Array of tags describing the pattern",
49 | "type": "array",
50 | "minItems": 1,
51 | "items": {
52 | "description": "Tag describing the pattern",
53 | "type": "string",
54 | "minLength": 1
55 | },
56 | "uniqueItems": true
57 | },
58 | "data": {
59 | "description": "Custom data object supplied by user",
60 | "type": "object",
61 | "minProperties": 1
62 | },
63 | "meta": {
64 | "description": "Custom meta data object supplied by user",
65 | "type": "object",
66 | "minProperties": 1
67 | },
68 | "options": {
69 | "description": "Custom options object supplied by user",
70 | "type": "object",
71 | "minProperties": 1
72 | },
73 | "patterns": {
74 | "description": "Dependencies of the pattern",
75 | "type": "object",
76 | "minProperties": 1,
77 | "patternProperties": {
78 | "^.+$": {
79 | "type": "string",
80 | "pattern": "^(/)?([^/\u0000]+(/)?)+$"
81 | }
82 | }
83 | },
84 | "demoPatterns": {
85 | "description": "Dependencies of the pattern used for demo purposes",
86 | "minProperties": 1,
87 | "patternProperties": {
88 | "^.+$": {
89 | "type": "string",
90 | "pattern": "^(/)?([^/\u0000]+(/)?)+$"
91 | }
92 | }
93 | },
94 | "overrides": {
95 | "description": "Options for overriding of core pattern behaviour",
96 | "type": "object",
97 | "minProperties": 1,
98 | "properties": {
99 | "files": {
100 | "description": "Custom mapping between patternplate files and paths to use in exchange for this pattern",
101 | "type": "object",
102 | "minProperties": 1,
103 | "patternProperties": {
104 | "^.+$": {
105 | "type": "string",
106 | "pattern": "^(/)?([^/\u0000]+(/)?)+$"
107 | }
108 | }
109 | },
110 | "demo": {
111 | "description": "Custom url to use as demo for this pattern",
112 | "type": "string"
113 | }
114 | }
115 | },
116 | "_patternplate": {
117 | "description": "Technical values saved by patternplate core",
118 | "type": "object"
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/pattern/pattern.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pattern",
3 | "version": "0.1.0"
4 | }
5 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # jsonlint-cli
2 |
3 | 
4 |
5 | > jsonlint-cli - cli wrapper for jsonlint
6 |
7 | Thin wrapper around [jsonlint](https://github.com/zaach/jsonlint)
8 | improving on its cli.
9 | It introduces glob expansion and advanced schema validation.
10 | Borrows heavily from `jsonlint` in every regard.
11 |
12 | ## Feature comparison
13 |
14 | `jsonlint-cli` introduces valuable improvements
15 | and additions to the cli shipping with [jsonlint](https://github.com/zaach/jsonlint).
16 |
17 | |Feature |jsonlint |jsonlint-cli |Description |
18 | |------------------------|:----------------:|:----------------:|:-----------------------------------------------|
19 | |json validity checking |:heavy_check_mark:|:heavy_check_mark:|jsonlint-cli uses jsonlint to parse and validate|
20 | |local schema validation |:heavy_check_mark:|:heavy_check_mark:|specify local schemas to validate input against |
21 | |read from stdin |:heavy_check_mark:|:heavy_check_mark:|stream json in via stdin |
22 | |read from fs |:heavy_check_mark:|:heavy_check_mark:|specify file's path to lint |
23 | |glob expansion |:x: |:heavy_check_mark:|specify globs of files to lint, e.g. `**/*.json`|
24 | |remote schema validation|:x: |:heavy_check_mark:|specify [remote schemas][1] to validate against |
25 | |v4 schema validation |:x: |:heavy_check_mark:|use v4 jsonschema |
26 | |config files |:x: |:heavy_check_mark:|support for `eslint` style config files |
27 |
28 | ## Installation
29 |
30 | ```shell
31 | # Install it from npm
32 | npm install -g jsonlint-cli
33 | ```
34 |
35 | ### Usage
36 |
37 | `jsonlint-cli` exposes a command line interface
38 |
39 | ```shell
40 | ❯ jsonlint-cli --help
41 | jsonlint-cli@1.0.0 - cli wrapper for jsonlint
42 |
43 | [input] reads from stdin if [files] are omitted
44 | --ignore,-i glob pattern to exclude from linting, defaults to: node_modules
45 | --validate,-s uri to schema to use for validation, defaults to:
46 | --indent,-w whitespace to use for pretty printing, defaults to: " "
47 | --env,-e json schema env to use for validation, defaults to: json-schema-draft-04
48 | --pretty,-p pretty-print the input, defaults to: false
49 | --sort,-t sort json keys alphabetically, defaults to: false
50 | --version,-v print the version
51 | --help,-h show this help
52 | ```
53 |
54 | ## Configuration
55 |
56 | `jsonlint-cli` picks up configuration files,
57 | searching upwards from `process.cwd()` or the file path if specified.
58 |
59 | ### .jsonlintrc
60 |
61 | ```js
62 | {
63 | "validate": "", // schema uri to validate against
64 | "ignore": ["node_modules/**/*"], // glob patterns to ignore
65 | "indent": "", // indent to use for pretty-printed output
66 | "env": "json-schema-draft-04", // json schema env version to use
67 | "pretty": true // pretty-print formatted json if quiet is false
68 | }
69 | ```
70 |
71 | ### .jsonlintignore
72 |
73 | ```ini
74 | node_modules/ # ignored by default
75 | distribution/
76 | ```
77 |
78 | ---
79 |
80 | Copyright 2016 by [Mario Nebl](https://github.com/marionebl)
81 | and [contributors](./graphs/contributors).
82 | Released under the [MIT license]('./license.md').
83 |
84 | [1]: http://schemastore.org/json/
85 |
--------------------------------------------------------------------------------