├── .npmrc ├── .travis.yml ├── LICENSE ├── example ├── check.js └── src.js ├── index.js ├── package.json ├── readme.markdown └── test ├── check.js ├── esm.js ├── html.js ├── ok.js ├── run.js ├── run2.js ├── shebang.js ├── sources ├── check.js ├── esm.js ├── ok.js ├── run.js ├── run2.js ├── shebang.js ├── spread.js └── yield.js ├── spread.js └── yield.js /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - 12 5 | - 10 6 | - 8 7 | - 6 8 | - 4 9 | - "iojs" 10 | - "0.12" 11 | - "0.10" 12 | - "0.8" 13 | before_install: 14 | - nvm install-latest-npm 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is released under the MIT license: 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /example/check.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var check = require('../'); 3 | 4 | var file = __dirname + '/src.js'; 5 | var src = fs.readFileSync(file); 6 | 7 | var err = check(src, file); 8 | if (err) { 9 | console.error('ERROR DETECTED' + Array(62).join('!')); 10 | console.error(err); 11 | console.error(Array(76).join('-')); 12 | } 13 | -------------------------------------------------------------------------------- /example/src.js: -------------------------------------------------------------------------------- 1 | module.exports = function (xs, fn) { 2 | var res = []; 3 | for (var i = 0; i < xs.length; i++) { 4 | var x = fn(xs[i], i); 5 | if (Array.isArray(x) res.push.apply(res, x); 6 | else res.push(x); 7 | } 8 | return res; 9 | }; 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var aparse = require('acorn-node').parse; 2 | function parse (src, opts) { 3 | if (!opts) opts = {} 4 | return aparse(src, opts); 5 | } 6 | 7 | module.exports = function (src, file,opts) { 8 | if (typeof src !== 'string') src = String(src); 9 | 10 | try { 11 | eval('throw "STOP"; (function () { ' + src + '\n})()'); 12 | return; 13 | } 14 | catch (err) { 15 | if (err === 'STOP') return undefined; 16 | if (err.constructor.name !== 'SyntaxError') return err; 17 | return errorInfo(src, file, opts); 18 | } 19 | }; 20 | 21 | function errorInfo (src, file, opts) { 22 | try { parse(src,opts) } 23 | catch (err) { 24 | return new ParseError(err, src, file); 25 | } 26 | return undefined; 27 | } 28 | 29 | function ParseError (err, src, file) { 30 | SyntaxError.call(this); 31 | 32 | this.message = err.message.replace(/\s+\(\d+:\d+\)$/, ''); 33 | 34 | this.line = err.loc.line; 35 | this.column = err.loc.column + 1; 36 | 37 | this.annotated = '\n' 38 | + (file || '(anonymous file)') 39 | + ':' + this.line 40 | + '\n' 41 | + src.split('\n')[this.line - 1] 42 | + '\n' 43 | + Array(this.column).join(' ') + '^' 44 | + '\n' 45 | + 'ParseError: ' + this.message 46 | ; 47 | } 48 | 49 | ParseError.prototype = Object.create(SyntaxError.prototype); 50 | 51 | ParseError.prototype.toString = function () { 52 | return this.annotated; 53 | }; 54 | 55 | ParseError.prototype.inspect = function () { 56 | return this.annotated; 57 | }; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "syntax-error", 3 | "version": "1.4.0", 4 | "description": "detect and report syntax errors in source code strings", 5 | "main": "index.js", 6 | "dependencies": { 7 | "acorn-node": "^1.2.0" 8 | }, 9 | "devDependencies": { 10 | "tap": "^10.7.3" 11 | }, 12 | "scripts": { 13 | "test": "tap test/*.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/browserify/syntax-error.git" 18 | }, 19 | "homepage": "https://github.com/browserify/syntax-error", 20 | "keywords": [ 21 | "syntax", 22 | "error", 23 | "esprima", 24 | "stack", 25 | "line", 26 | "column" 27 | ], 28 | "author": { 29 | "name": "James Halliday", 30 | "email": "mail@substack.net", 31 | "url": "http://substack.net" 32 | }, 33 | "license": "MIT", 34 | "engine": { 35 | "node": ">=0.6" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | # syntax-error 2 | 3 | Detect and report syntax errors in source code strings. 4 | 5 | [![build status](https://secure.travis-ci.org/browserify/syntax-error.png)](http://travis-ci.org/browserify/syntax-error) 6 | 7 | When you type `node src.js` you get a friendly error report about exactly where 8 | the syntax error is. This module lets you check for syntax errors and report 9 | them in a similarly friendly format that wrapping a try/catch around 10 | `Function()` or `vm.runInNewContext()` doesn't get you. 11 | 12 | # example 13 | 14 | ``` js 15 | var fs = require('fs'); 16 | var check = require('syntax-error'); 17 | 18 | var file = __dirname + '/src.js'; 19 | var src = fs.readFileSync(file); 20 | 21 | var err = check(src, file); 22 | if (err) { 23 | console.error('ERROR DETECTED' + Array(62).join('!')); 24 | console.error(err); 25 | console.error(Array(76).join('-')); 26 | } 27 | ``` 28 | 29 | --- 30 | 31 | ``` 32 | $ node check.js 33 | ERROR DETECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 34 | 35 | /home/substack/projects/node-syntax-error/example/src.js:5 36 | if (Array.isArray(x) res.push.apply(res, x); 37 | ^ 38 | ParseError: Unexpected identifier 39 | --------------------------------------------------------------------------- 40 | ``` 41 | 42 | # methods 43 | 44 | ``` js 45 | var check = require('syntax-error') 46 | ``` 47 | 48 | ## var err = check(src, file, opts={}) 49 | 50 | Check the source code string `src` for syntax errors. 51 | Optionally you can specify a filename `file` that will show up in the output. 52 | 53 | If `src` has a syntax error, return an error object `err` that can be printed or 54 | stringified. 55 | 56 | If there are no syntax errors in `src`, return `undefined`. 57 | 58 | Options will be passed through to [acorn-node](https://github.com/browserify/acorn-node). 59 | acorn-node defaults to options that match the most recent Node versions. 60 | 61 | ## err.toString() 62 | 63 | Return the long string description with a source snippet and a `^` under 64 | pointing exactly where the error was detected. 65 | 66 | # attributes 67 | 68 | ## err.message 69 | 70 | short string description of the error type 71 | 72 | ## err.line 73 | 74 | line number of the error in the original source (indexing starts at 1) 75 | 76 | ## err.column 77 | 78 | column number of the error in the original source (indexing starts at 1) 79 | 80 | # install 81 | 82 | With [npm](http://npmjs.org) do: 83 | 84 | ``` 85 | npm install syntax-error 86 | ``` 87 | 88 | # license 89 | 90 | MIT 91 | -------------------------------------------------------------------------------- /test/check.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/check.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('check', function (t) { 10 | var err = check(src, file); 11 | t.ok(err); 12 | t.equal(err.line, 5); 13 | t.equal(err.column, 30); 14 | t.equal(err.message, 'Unexpected token'); 15 | t.ok(String(err).indexOf(file + ':5')); 16 | t.end(); 17 | }); 18 | -------------------------------------------------------------------------------- /test/esm.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/esm.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('esm with sourceType script', function (t) { 10 | var err = check(src, file); 11 | t.ok(err); 12 | t.equal(err.line, 1); 13 | t.equal(err.column, 1); 14 | t.equal(err.message, "'import' and 'export' may appear only with 'sourceType: module'"); 15 | t.ok(String(err).indexOf(file + ':1')); 16 | t.end(); 17 | }); 18 | 19 | test('esm with sourceType module', function (t) { 20 | var err = check(src, file, { sourceType: 'module' }); 21 | t.notOk(err); 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /test/html.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var src = ''; 7 | 8 | test('html', function (t) { 9 | var err = check(src, 'foo.js'); 10 | t.ok(err); 11 | t.equal(err.line, 1); 12 | t.equal(err.column, 1); 13 | t.equal(err.message, 'Unexpected token'); 14 | t.ok(/foo.js:1/.test(err), 'foo.js:1'); 15 | t.end(); 16 | }); 17 | -------------------------------------------------------------------------------- /test/ok.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/ok.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('ok', function (t) { 10 | var err = check(src, file); 11 | t.notOk(err); 12 | t.end(); 13 | }); 14 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | var check = require('../'); 3 | 4 | var fs = require('fs'); 5 | var file = __dirname + '/sources/run.js'; 6 | var src = fs.readFileSync(file); 7 | 8 | test('do not run sources', function (t) { 9 | t.plan(1); 10 | var err = check(src, file); 11 | t.notOk(err); 12 | }); 13 | -------------------------------------------------------------------------------- /test/run2.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | var check = require('../'); 3 | 4 | var fs = require('fs'); 5 | var file = __dirname + '/sources/run2.js'; 6 | var src = fs.readFileSync(file); 7 | 8 | test('do not run sources (2)', function (t) { 9 | t.plan(1); 10 | var err = check(src, file); 11 | t.notOk(err); 12 | }); 13 | -------------------------------------------------------------------------------- /test/shebang.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/shebang.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('shebang', function (t) { 10 | var err = check(src, file); 11 | t.notOk(err); 12 | t.end(); 13 | }); 14 | -------------------------------------------------------------------------------- /test/sources/check.js: -------------------------------------------------------------------------------- 1 | module.exports = function (xs, fn) { 2 | var res = []; 3 | for (var i = 0; i < xs.length; i++) { 4 | var x = fn(xs[i], i); 5 | if (Array.isArray(x) res.push.apply(res, x); 6 | else res.push(x); 7 | } 8 | return res; 9 | }; 10 | -------------------------------------------------------------------------------- /test/sources/esm.js: -------------------------------------------------------------------------------- 1 | import x from 'y'; 2 | export default z; 3 | -------------------------------------------------------------------------------- /test/sources/ok.js: -------------------------------------------------------------------------------- 1 | function f () {} 2 | -------------------------------------------------------------------------------- /test/sources/run.js: -------------------------------------------------------------------------------- 1 | process.exit(1); 2 | -------------------------------------------------------------------------------- /test/sources/run2.js: -------------------------------------------------------------------------------- 1 | })(); 2 | process.exit(1); 3 | (function () { 4 | -------------------------------------------------------------------------------- /test/sources/shebang.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | console.log('foo'); 3 | -------------------------------------------------------------------------------- /test/sources/spread.js: -------------------------------------------------------------------------------- 1 | var a = { ...b } 2 | ;({ d, ...e } = a) 3 | -------------------------------------------------------------------------------- /test/sources/yield.js: -------------------------------------------------------------------------------- 1 | function *foo () { 2 | yield 5 3 | } 4 | 5 | (function *() { 6 | console.log(foo().next().value) 7 | })().next(); 8 | 9 | (function *() { })(); 10 | 11 | (function * () { 12 | yield yield 3 13 | })(); 14 | -------------------------------------------------------------------------------- /test/spread.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/spread.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('spread', function (t) { 10 | var err = check(src, file); 11 | t.notOk(err); 12 | t.end(); 13 | }); 14 | -------------------------------------------------------------------------------- /test/yield.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | var fs = require('fs'); 4 | var check = require('../'); 5 | 6 | var file = __dirname + '/sources/yield.js'; 7 | var src = fs.readFileSync(file); 8 | 9 | test('yield', function (t) { 10 | var err = check(src, file); 11 | t.notOk(err); 12 | t.end(); 13 | }); 14 | --------------------------------------------------------------------------------