Commit 5c302ae0 authored by Ashur Cabrera's avatar Ashur Cabrera
Browse files

Initial commit

parents
module.exports = {
"env": {
"commonjs": true,
"es6": true,
"nova/nova": true,
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly",
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
},
"plugins": [
"nova",
],
};
.DS_Store
main.dist.js
node_modules
{
"script" : "if [ $NOVA_TASK_NAME == \"build\" ]; then\n\tnpm run build\nfi\n\nif [ $NOVA_TASK_NAME == \"run\" ]; then\n\tnpm run watch\nfi\n",
"actions" : [
"build",
"run"
],
"identifier" : "2261895D-89D1-4AD2-9B77-2E42E2F2A339",
"openLogOnRun" : "start"
}
\ No newline at end of file
{
"script" : "if [ $NOVA_TASK_NAME == \"build\" ]; then\n\tnpm run build:dist\nfi\n",
"actions" : [
"build"
],
"identifier" : "8527A415-B85C-4DB3-B10E-46B09E4FC1D0",
"openLogOnRun" : "start"
}
\ No newline at end of file
## Version 1.0
Initial release
## Troubleshooting
If you see the following error message:
> JSONLint requires NPM and Node.js. Please download and install the latest version of Node.js, or verify that NPM can be found on $PATH.
the JSONLint extension can't find NPM on your system. There are a few reasons this might be happening.
### Verify NPM Is Installed
Open a **Local Terminal** tab in Nova (or macOS's **Terminal.app**) and run the following command:
```
npm --version
```
If the result is a version number:
```
$ npm --version
6.13.6
```
NPM is installed and ready to go! If you see an error message:
```
$ npm --version
npm: command not found
```
you might need to download and install the [latest version of Node.js and NPM][node] before continuing.
### NPM Is Installed, but Can’t Be Found
If you've ensured that NPM is installed but you're still seeing the error:
1. Open Nova Preferences
1. Switch to the **Tools** pane
1. Enable the _Automatically request environment from login shell_ option
This ensures that JSONLint and other extensions can access information about where tools like NPM are installed on your system.
#### Advanced
Nova's option to request environment variables from your login shell does not include variables set in an interactive shell (i.e., a standard terminal session).
If NPM's location is added to your `$PATH` by a shell command or script (ex., [NVM][nvm]), you can copy or move the relevant invocation to a startup file sourced by login shells:
- `~/.bash_profile`, `~/.bash_login`, or `~/.profile` for **Bash**
- `~/.zprofile` or `~/.zlogin` for **ZSH**
> 💡 Startup files like `.bashrc` and `.zshrc` are sourced in interactive shells; modifications to `$PATH` made in those files won't be reflected in Nova.
[node]: https://nodejs.org
[nvm]: https://github.com/nvm-sh/nvm
Check JSON files for syntax errors
## Requirements
- [NPM][node] 5.2.0* or newer
> \*Bundled with Node.js 8.2.0+
[node]: https://nodejs.org
{
"identifier": "ashur.JSONLint",
"name": "JSONLint",
"vendor": "Ashur Cabrera",
"description": "Check JSON files for syntax errors",
"version": "1.0",
"categories": ["formatters"],
"homepage": "https://dev.panic.com/ashur/nova-jsonlint",
"repository": "https://dev.panic.com/ashur/nova-jsonlint",
"bugs": "https://dev.panic.com/ashur/nova-jsonlint/issues",
"main": "main.dist.js",
"entitlements": {
"filesystem": "readonly",
"process": true
}
}
{
"name": "jsonlint.novaextension",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"JSV": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
"integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
},
"ansi-styles": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
"integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg="
},
"chalk": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
"integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
"requires": {
"ansi-styles": "~1.0.0",
"has-color": "~0.1.0",
"strip-ansi": "~0.1.0"
}
},
"has-color": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
"integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
},
"jsonlint": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
"integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==",
"requires": {
"JSV": "^4.0.x",
"nomnom": "^1.5.x"
}
},
"nomnom": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
"integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
"requires": {
"chalk": "~0.4.0",
"underscore": "~1.6.0"
}
},
"strip-ansi": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
"integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
},
"underscore": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
}
}
}
{
"name": "jsonlint.novaextension",
"version": "0.1.0",
"description": "A JSONLint extension for Nova",
"main": "index.js",
"scripts": {},
"author": "Ashur Cabrera",
"license": "ISC",
"dependencies": {
"jsonlint": "^1.6.3"
}
}
# nova-jsonlint
A JSONLint extension for [Nova][nova].
[nova]: https://panic.com/nova
This diff is collapsed.
{
"name": "nova-jsonlint",
"version": "1.0.0",
"description": "A JSONLint extension for Nova",
"repository": "https://dev.panic.com/ashur/nova-jsonlint",
"scripts": {
"build": "npx rollup -c",
"build:dist": "npm run build && rm -r *.novaextension/node_modules",
"test:lint": "eslint *.novaextension/scripts/*.js",
"watch": "onchange -i \"src/**/*.js\" \"*.novaextension/extension.json\" -- npm run build"
},
"keywords": [
"nova",
"jsonlint"
],
"author": "Ashur Cabrera",
"license": "ISC",
"devDependencies": {
"eslint": "^6.8.0",
"eslint-plugin-nova": "^1.0.0-beta.3",
"onchange": "^6.1.0"
},
"dependencies": {
"@rollup/plugin-commonjs": "^11.0.1",
"@rollup/plugin-node-resolve": "^7.0.0",
"nova-npm-executable": "^0.1.0",
"rollup": "^1.29.1"
}
}
const commonjs = require( "@rollup/plugin-commonjs" );
const resolve = require( "@rollup/plugin-node-resolve" );
module.exports =
{
input: "src/scripts/main.js",
output: {
file: "JSONLint.novaextension/scripts/main.dist.js",
format: "cjs",
exports: "named",
},
plugins: [
commonjs(),
resolve(),
],
};
const {NPMExecutable} = require( "nova-npm-executable" );
/** Class that provides linting support via external JSONLint executable */
module.exports.JSONLint = class JSONLint
{
constructor()
{
this.jsonlint = new NPMExecutable( "jsonlint" );
if( !this.jsonlint.isInstalled )
{
this.jsonlint.install()
.catch( error =>
{
console.error( error );
});
}
this._isNpxInstalled = null;
this.didNotify = false;
this.issueCollection = new IssueCollection();
}
/**
* Returns whether `npx` can be found on $PATH
*
* @return {Promise<boolean>}
*/
get isNpxInstalled()
{
if( this._isNpxInstalled )
{
return Promise.resolve( true );
}
return new Promise( resolve =>
{
let env = new Process( "/usr/bin/env", { args: ["which", "npx"], shell: true } );
env.onDidExit( status =>
{
this._isNpxInstalled = status === 0;
resolve( this._isNpxInstalled );
});
env.start();
});
}
/**
* Lint a document's contents
*
* @return {Promise}
*/
async lintDocument( textDocument )
{
let isNpxInstalled = await this.isNpxInstalled;
if( !isNpxInstalled && !this.didNotify )
{
this.didNotify = true;
let request = new NotificationRequest( "ashur.JSONLint.npxNotFound" );
request.title = "NPM Not Found";
request.body = "JSONLint requires NPM and Node.js. Please download and install the latest version of Node.js, or verify that NPM can be found on $PATH.";
request.actions = ["OK", "Help"];
try
{
let response = await nova.notifications.add( request );
if( response.actionIdx === 1 )
{
nova.openConfig();
}
}
catch( error )
{
console.error( error );
}
}
if( textDocument.syntax !== "json" )
{
return;
}
let range = new Range( 0, textDocument.length );
let contents = textDocument.getTextInRange( range );
return this.lintString( contents, textDocument.uri );
}
/**
* Write a string to the JSONLint process's STDIN, then parse output and
* pass results to reporter.
*
* @param {String} string
* @param {String} fileURI
* @see parseOutput
* @see report
* @return {Promise}
*/
async lintString( string, fileURI )
{
try
{
// Equivalent to running `echo <string> | npx jsonlint -c`
let process = await this.jsonlint.process( { args: ["-c"] } );
let output = "";
process.onStdout( line => output += line.trimRight() );
process.onStderr( line => output += line.trimRight() );
process.onDidExit( status =>
{
let results = status === 0 ? [] : this.parseOutput( output );
// We need to report all documents, regardless of whether JSONLint
// reported any problems. Otherwise, fixing a problem in the
// document doesn't clear the Issue.
this.report( results, fileURI );
});
process.start();
let writer = process.stdin.getWriter();
writer.write( string );
writer.close();
}
catch( error )
{
console.error( error )
}
}
/**
* Parse a string for JSONLint error output
*
* @return {[Object]} Array of objects describing results
*/
parseOutput( string )
{
let results = [];
// JSONLint only reports one issue at a time, so there's no need to
// perform a global match.
let pattern = /line (\d+), col (\d+), found: '([^']+)' - expected: '([^']+)'/;
let matches = string.match( pattern );
if( matches )
{
results.push({
line: matches[1],
column: matches[2],
found: matches[3],
expected: matches[4],
});
}
return results;
}
/**
* @param {String} fileURI
*/
removeIssues( fileURI )
{
this.issueCollection.remove( fileURI );
}
/**
* Convert parsed JSONLint output into Nova issues
*
* @param {[Object]} results
* @param {String} fileURI
*/
report( results, fileURI )
{
let issues = results.map( result =>
{
let issue = new Issue();
issue.message = `Found ${JSON.stringify( result.found )}; Expected ${result.expected}`;
issue.severity = IssueSeverity.Error;
issue.line = result.line;
issue.column = parseInt( result.column ) + 1;
return issue;
});
this.issueCollection.set( fileURI, issues );
}
}
const {JSONLint} = require( "./jsonlint" );
module.exports.activate = async () =>
{
try
{
let jsonlint = new JSONLint();
/**
* Lint all documents as soon as they are opened, and add listeners for
* several important lifecycle events.
*/
nova.workspace.onDidAddTextEditor( textEditor =>
{
jsonlint.lintDocument( textEditor.document );
textEditor.onDidStopChanging( textEditor =>
{
jsonlint.lintDocument( textEditor.document );
});
textEditor.document.onDidChangeSyntax( textDocument =>
{
jsonlint.lintDocument( textDocument );
});
textEditor.onDidDestroy( destroyedTextEditor =>
{
// If the backing documents of any other TextEditor instances share
// the same URI, don't remove the Issues.
let anotherEditor = nova.workspace.textEditors.find( textEditor =>
{
return textEditor.document.uri === destroyedTextEditor.document.uri;
});
if( !anotherEditor )
{
jsonlint.removeIssues( destroyedTextEditor.document.uri );
}
});
});
}
catch( error )
{
console.error( error );
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment