diff --git a/.eslintrc b/.eslintrc index 2782382c7d..ea7b1eed61 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,7 +19,8 @@ "no-loop-func": 0, "no-unreachable": 0, "no-labels": 0, - "no-process-exit": 0 + "no-process-exit": 0, + "camelcase": 0 }, "env": { "node": true diff --git a/.gitignore b/.gitignore index 7256bc9d3c..f636502c0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store node_modules -test/core/tmp +test/tmp *.log *.cache /templates.json diff --git a/.travis.yml b/.travis.yml index 85b05c77da..d071282eb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ cache: node_js: - iojs - - "0.8" - "0.10" - "0.12" diff --git a/packages/babel-cli/src/babel-external-helpers b/packages/babel-cli/src/babel-external-helpers index 8d2d907306..8c85f0352c 100755 --- a/packages/babel-cli/src/babel-external-helpers +++ b/packages/babel-cli/src/babel-external-helpers @@ -10,6 +10,4 @@ commander.option("-t, --output-type [type]", "Type of output (global|umd|var)", commander.usage("[options]"); commander.parse(process.argv); -util.ensureTemplates().then(function () { - console.log(runtime(commander.whitelist, commander.outputType)); -}); +console.log(runtime(commander.whitelist, commander.outputType)); diff --git a/packages/babel-cli/src/babel/util.js b/packages/babel-cli/src/babel/util.js index e83f5faaac..8ade28d1d6 100644 --- a/packages/babel-cli/src/babel/util.js +++ b/packages/babel-cli/src/babel/util.js @@ -35,7 +35,7 @@ exports.transform = function (filename, code, opts) { opts.ignore = null; opts.only = null; - var result = babel.__plsDontUseThis(code, opts); + var result = babel.transform(code, opts); result.filename = filename; result.actual = code; return result; diff --git a/packages/babel-cli/test/tmp/bar2.js b/packages/babel-cli/test/tmp/bar2.js new file mode 100644 index 0000000000..26c2d21a78 --- /dev/null +++ b/packages/babel-cli/test/tmp/bar2.js @@ -0,0 +1,2 @@ +var bar = () => console.log("bar"); +bar(); \ No newline at end of file diff --git a/packages/babel-cli/test/tmp/foo2.js b/packages/babel-cli/test/tmp/foo2.js new file mode 100644 index 0000000000..4c4e8d7d6d --- /dev/null +++ b/packages/babel-cli/test/tmp/foo2.js @@ -0,0 +1,5 @@ +import "./bar2"; +import "./not_node_modules"; + +var foo = () => console.log("foo"); +foo(); \ No newline at end of file diff --git a/packages/babel-cli/test/tmp/not_node_modules.jsx b/packages/babel-cli/test/tmp/not_node_modules.jsx new file mode 100644 index 0000000000..d46284bf2d --- /dev/null +++ b/packages/babel-cli/test/tmp/not_node_modules.jsx @@ -0,0 +1,10 @@ +/* +The purpose of this file is to test that the node_modules check in the require +hook doesn't mistakenly exclude something like "not_node_modules". To pass, this +file merely needs to be transpiled. The transpiled code won't, and doesn't need +to, execute without error. It won't execute because React will be undefined. +*/ +try { + ; +} +catch (e) {} \ No newline at end of file diff --git a/packages/babel/src/api/node.js b/packages/babel/src/api/node.js index 1c3904486c..d53ea55ac6 100644 --- a/packages/babel/src/api/node.js +++ b/packages/babel/src/api/node.js @@ -4,11 +4,6 @@ import * as babylon from "babylon"; import * as util from "../util"; import fs from "fs"; -var deasync; -try { - deasync = require("deasync"); -} catch (err) {} - export { util, babylon as acorn, transform }; export { pipeline } from "../transformation"; export { canCompile } from "../util"; @@ -42,32 +37,24 @@ export function transformFile(filename: string, opts?: Object, callback: Functio opts.filename = filename; - return transform(fs.createReadStream(filename), opts); + fs.readFile(filename, function (err, code) { + if (err) return callback(err); + + var result; + + try { + result = transform(code, opts); + } catch (err) { + return callback(err); + } + + callback(null, result); + }); } -export function __plsDontUseThis(code, opts) { - if (!deasync) { - throw new Error("Sorry, this API isn't available in the current environment."); - } - - var done = false; - var result, err; - - transform(code, opts).then(function (_result) { - result = _result; - done = true; - }, function (_err) { - err = _err; - done = true; - }); - - deasync.loopWhile(() => !done); - - if (err) { - throw err; - } else { - return result; - } +export function transformFileSync(filename: string, opts?: Object = {}) { + opts.filename = filename; + return transform(fs.readFileSync(filename, "utf8"), opts); } export function parse(code, opts = {}) { diff --git a/packages/babel/src/api/register/node.js b/packages/babel/src/api/register/node.js index 126561b906..70673bb9e5 100644 --- a/packages/babel/src/api/register/node.js +++ b/packages/babel/src/api/register/node.js @@ -57,7 +57,7 @@ var compile = function (filename, opts = {}) { optsManager.mergeOptions(transformOpts); opts = optsManager.init(opts); - var cacheKey = `${filename}:${JSON.stringify(opts)}:${babel.version}`; + var cacheKey = `${JSON.stringify(opts)}:${babel.version}`; var env = process.env.BABEL_ENV || process.env.NODE_ENV; if (env) cacheKey += `:${env}`; @@ -70,8 +70,7 @@ var compile = function (filename, opts = {}) { } if (!result) { - var file = fs.readFileSync(filename, "utf8"); - result = babel.__plsDontUseThis(file, extend(opts, { + result = babel.transformFileSync(filename, extend(opts, { sourceMap: "both", ast: false })); diff --git a/packages/babel/src/helpers/parse.js b/packages/babel/src/helpers/parse.js index fdb73f6426..4a799fe6f4 100644 --- a/packages/babel/src/helpers/parse.js +++ b/packages/babel/src/helpers/parse.js @@ -45,9 +45,8 @@ export default function (code, opts = {}) { parseOpts.plugins.flow = true; } - return babylon.parse(code, parseOpts).then(function (ast) { - estraverse.attachComments(ast, comments, tokens); - ast = normalizeAst(ast, comments, commentsAndTokens); - return ast; - }); + var ast = babylon.parse(code, parseOpts); + estraverse.attachComments(ast, comments, tokens); + ast = normalizeAst(ast, comments, commentsAndTokens); + return ast; } diff --git a/packages/babel/src/transformation/file/index.js b/packages/babel/src/transformation/file/index.js index 7efb2a968e..454a781fd9 100644 --- a/packages/babel/src/transformation/file/index.js +++ b/packages/babel/src/transformation/file/index.js @@ -12,7 +12,6 @@ import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; import traverse from "../../traversal"; import Logger from "./logger"; -import { Promise } from "bluebird"; import Plugin from "../plugin"; import parse from "../../helpers/parse"; import Hub from "../../traversal/hub"; @@ -429,10 +428,9 @@ export default class File { parseOpts.sourceType = "module"; this.log.debug("Parse start"); - return parse(code, parseOpts).then((ast) => { - this.log.debug("Parse stop"); - return ast; - }); + var ast = parse(code, parseOpts); + this.log.debug("Parse stop"); + return ast; } _addAst(ast) { @@ -467,11 +465,7 @@ export default class File { } this.call("post"); - return new Promise((resolve, reject) => { - util.ensureTemplates().then(() => { - resolve(this.generate()); - }, reject); - }); + return this.generate(); } wrap(code, callback) { @@ -479,7 +473,7 @@ export default class File { try { if (this.shouldIgnore()) { - return Promise.resolve(this.makeResult({ code, ignored: true })); + return this.makeResult({ code, ignored: true }); } else { return callback(); } @@ -519,9 +513,8 @@ export default class File { parseCode() { this.parseShebang(); - return this.parse(this.code).then((ast) => { - this.addAst(ast); - }); + var ast = this.parse(this.code); + this.addAst(ast); } shouldIgnore() { @@ -567,6 +560,10 @@ export default class File { map: map }; + result.then = function (callback) { + callback(result); + }; + if (this.opts.code) { result.code = code; } diff --git a/packages/babel/src/transformation/file/options/option-manager.js b/packages/babel/src/transformation/file/options/option-manager.js index 8e4511167c..bc4612a669 100644 --- a/packages/babel/src/transformation/file/options/option-manager.js +++ b/packages/babel/src/transformation/file/options/option-manager.js @@ -97,7 +97,7 @@ export default class OptionManager { */ addIgnoreConfig(loc) { - var file = fs.readFileSync(loc, "utf8"); + var file = fs.readFileSync(loc, "utf8"); var lines = file.split("\n"); lines = lines.map(function (line) { diff --git a/packages/babel/src/transformation/pipeline.js b/packages/babel/src/transformation/pipeline.js index 104b1a679d..ae74d9d0bc 100644 --- a/packages/babel/src/transformation/pipeline.js +++ b/packages/babel/src/transformation/pipeline.js @@ -1,6 +1,5 @@ import PluginManager from "./file/plugin-manager"; import normalizeAst from "../helpers/normalize-ast"; -import { Promise } from "bluebird"; import Plugin from "./plugin"; import assign from "lodash/object/assign"; import object from "../helpers/object"; @@ -91,20 +90,19 @@ export default class Pipeline { var file = new File(opts, this); return file.wrap(code, function () { file.addCode(code); - return file.parseCode(code).then(() => file.transform()); + file.parseCode(code); + return file.transform(); }); } transformFromAst(ast, code, opts) { - return new Promise((resolve) => { - ast = normalizeAst(ast); + ast = normalizeAst(ast); - var file = new File(opts, this); - resolve(file.wrap(code, function () { - file.addCode(code); - file.addAst(ast); - return file.transform(); - })); + var file = new File(opts, this); + return file.wrap(code, function () { + file.addCode(code); + file.addAst(ast); + return file.transform(); }); } diff --git a/packages/babel/src/transformation/transformers/index.js b/packages/babel/src/transformation/transformers/index.js index 9dacb52d1e..20d66e9c4a 100644 --- a/packages/babel/src/transformation/transformers/index.js +++ b/packages/babel/src/transformation/transformers/index.js @@ -5,7 +5,6 @@ export default { //- builtin-pre strict: require("./other/strict"), eval: require("babel-plugin-eval"), - _explode: require("./internal/explode"), _validation: require("./internal/validation"), _hoistDirectives: require("./internal/hoist-directives"), "minification.removeDebugger": require("babel-plugin-remove-debugger"), diff --git a/packages/babel/src/transformation/transformers/internal/explode.js b/packages/babel/src/transformation/transformers/internal/explode.js deleted file mode 100644 index 4bb6040a5d..0000000000 --- a/packages/babel/src/transformation/transformers/internal/explode.js +++ /dev/null @@ -1,34 +0,0 @@ -import clone from "lodash/lang/clone"; -import * as t from "../../../types"; - -export var metadata = { - group: "builtin-pre" -}; - -function buildClone(bindingKey, refKey, check?) { - return function (node) { - if (node[bindingKey] === node[refKey] || (check && check(node))) { - node[refKey] = t.removeComments(clone(node[refKey])); - } - }; -} - -function buildListClone(listKey, bindingKey, refKey) { - var clone = buildClone(bindingKey, refKey); - - return function (node) { - if (!node[listKey]) return; - - for (var subNode of (node[listKey]: Array)) { - clone(subNode); - } - }; -} - -export var visitor = { - Property: buildClone("value", "key", function (node) { - return t.isAssignmentPattern(node.value) && node.value.left === node.key; - }), - ExportDeclaration: buildListClone("specifiers", "local", "exported"), - ImportDeclaration: buildListClone("specifiers", "local", "imported") -}; diff --git a/packages/babel/src/util.js b/packages/babel/src/util.js index 1398470651..916ffe2da3 100644 --- a/packages/babel/src/util.js +++ b/packages/babel/src/util.js @@ -10,7 +10,6 @@ import contains from "lodash/collection/contains"; import traverse from "./traversal"; import isString from "lodash/lang/isString"; import isRegExp from "lodash/lang/isRegExp"; -import { Promise } from "bluebird"; import Module from "module"; import isEmpty from "lodash/lang/isEmpty"; import parse from "./helpers/parse"; @@ -157,8 +156,6 @@ var templateVisitor = { // export function template(name: string, nodes?: Array, keepExpression?: boolean): Object { - if (!exports.templates) throw new ReferenceError(`haven't initialised templates yet`); - var ast = exports.templates[name]; if (!ast) throw new ReferenceError(`unknown template ${name}`); @@ -183,11 +180,10 @@ export function template(name: string, nodes?: Array, keepExpression?: b return node; } } - export function parseTemplate(loc: string, code: string): Object { - return parse(code, { filename: loc, looseModules: true }).then(function (ast) { - return traverse.removeProperties(ast.program); - }); + var ast = parse(code, { filename: loc, looseModules: true }).program; + ast = traverse.removeProperties(ast); + return ast; } function loadTemplates() { @@ -198,37 +194,22 @@ function loadTemplates() { throw new ReferenceError(messages.get("missingTemplatesDirectory")); } - var promises = []; + for (var name of (fs.readdirSync(templatesLoc): Array)) { + if (name[0] === ".") return; - for (let name of (fs.readdirSync(templatesLoc): Array)) { - if (name[0] === ".") continue; + var key = path.basename(name, path.extname(name)); + var loc = path.join(templatesLoc, name); + var code = fs.readFileSync(loc, "utf8"); - let key = path.basename(name, path.extname(name)); - let loc = path.join(templatesLoc, name); - let code = fs.readFileSync(loc, "utf8"); - - promises.push(parseTemplate(loc, code).then(function (template) { - templates[key] = template; - })); + templates[key] = parseTemplate(loc, code); } - return Promise.all(promises).then(function () { - return exports.templates = templates; - }); -} - -export function ensureTemplates() { - return new Promise((resolve) => { - if (exports.templates) { - resolve(exports.templates); - } else { - resolve(loadTemplates()); - } - }); + return templates; } try { - exports.templates = require("../templates.json"); + exports.templates = require("../../templates.json"); } catch (err) { if (err.code !== "MODULE_NOT_FOUND") throw err; + exports.templates = loadTemplates(); } diff --git a/packages/babel/test/_transformation-helper.js b/packages/babel/test/_transformation-helper.js index 71a422326a..f115800613 100644 --- a/packages/babel/test/_transformation-helper.js +++ b/packages/babel/test/_transformation-helper.js @@ -12,12 +12,7 @@ var _ = require("lodash"); require("../lib/polyfill"); -var doneBeforeEval = false; -function beforeEval() { - if (doneBeforeEval) return; - doneBeforeEval = true; - eval(buildExernalHelpers()); -} +eval(buildExernalHelpers()); global.assertNoOwnProperties = function (obj) { assert.equal(Object.getOwnPropertyNames(obj).length, 0); @@ -49,7 +44,6 @@ chai.assert.throw = function (fn, msg) { return chai.assert._throw(fn, msg); }; - var run = function (task, done) { var actual = task.actual; var expect = task.expect; @@ -74,71 +68,62 @@ var run = function (task, done) { helper.esvalid(result.ast.program, result.code, opts.loc); }; - var promises = []; - if (execCode) { - promises.push(transform(execCode, getOpts(exec)).then(function (result) { - execCode = result.code; + result = transform(execCode, getOpts(exec)); + execCode = result.code; - try { - var fakeRequire = function (loc) { - if (loc === "../../../src/runtime/polyfills/Number.js") { - return Number; - } else if (loc === "../../../src/runtime/polyfills/Math.js") { - return Math; - } else { - return require(path.resolve(exec.loc, "..", loc)); - } - }; + try { + var fakeRequire = function (loc) { + if (loc === "../../../src/runtime/polyfills/Number.js") { + return Number; + } else if (loc === "../../../src/runtime/polyfills/Math.js") { + return Math; + } else { + return require(path.resolve(exec.loc, "..", loc)); + } + }; - var fn = new Function("require", "exports", execCode); - beforeEval(); - fn.call(global, fakeRequire, {}); - } catch (err) { - err.message = exec.loc + ": " + err.message; - err.message += codeFrame(execCode); - throw err; - } + var fn = new Function("require", "done", "exports", execCode); + fn.call(global, fakeRequire, chai.assert, {}, done); + } catch (err) { + err.message = exec.loc + ": " + err.message; + err.message += codeFrame(execCode); + throw err; + } - checkAst(result, exec); - })); + checkAst(result, exec); } var actualCode = actual.code; var expectCode = expect.code; if (!execCode || actualCode) { - promises.push(transform(actualCode, getOpts(actual)).then(function (result) { - actualCode = result.code.trim(); + result = transform(actualCode, getOpts(actual)); + actualCode = result.code.trim(); - try { - chai.expect(actualCode).to.be.equal(expectCode, actual.loc + " !== " + expect.loc); - } catch (err) { - //require("fs").writeFileSync(expect.loc, actualCode); - throw err; - } + try { + chai.expect(actualCode).to.be.equal(expectCode, actual.loc + " !== " + expect.loc); + } catch (err) { + //require("fs").writeFileSync(expect.loc, actualCode); + throw err; + } - checkAst(result, actual); - - if (task.sourceMap) { - chai.expect(result.map).to.deep.equal(task.sourceMap); - } - - if (task.sourceMappings) { - var consumer = new sourceMap.SourceMapConsumer(result.map); - - _.each(task.sourceMappings, function (mapping, i) { - var actual = mapping.original; - - var expect = consumer.originalPositionFor(mapping.generated); - chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); - }); - } - })); + checkAst(result, actual); } - Promise.all(promises).then(function () { - done() - }, done); + if (task.sourceMap) { + chai.expect(result.map).to.deep.equal(task.sourceMap); + } + + if (task.sourceMappings) { + var consumer = new sourceMap.SourceMapConsumer(result.map); + + _.each(task.sourceMappings, function (mapping, i) { + var actual = mapping.original; + + var expect = consumer.originalPositionFor(mapping.generated); + chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); + }); + } }; module.exports = function (suiteOpts, taskOpts, dynamicOpts) { @@ -155,7 +140,11 @@ module.exports = function (suiteOpts, taskOpts, dynamicOpts) { _.each(testSuite.tests, function (task) { if (_.contains(suiteOpts.ignoreTasks, task.title) || _.contains(suiteOpts.ignoreTasks, testSuite.title + "/" + task.title)) return; - test(task.title, !task.disabled && function (done) { + var runTest = function (done) { + var runTask = function () { + run(task, done); + }; + _.extend(task.options, taskOpts); if (dynamicOpts) dynamicOpts(task.options, task); @@ -165,26 +154,24 @@ module.exports = function (suiteOpts, taskOpts, dynamicOpts) { // the options object with useless options delete task.options.throws; - return run(task, function (err) { - if (err) { - // we just wanted any error message - if (throwMsg === true) return done(); - - if (err.message.indexOf(throwMsg) >= 0) { - // success! - done(); - } else { - // didn't include it :( - done(new Error("Expected an error message of " + JSON.stringify(throwMsg) + " but got " + JSON.stringify(err.message))); - } - } else { - done(new Error("Expected an error message but got none")); - } + assert.throws(runTask, function (err) { + return throwMsg === true || err.message.indexOf(throwMsg) >= 0; }); } else { - return run(task, done); + runTask(); } - }); + }; + + var callback; + if (task.options.asyncExec) { + callback = runTest; + } else { + callback = function () { + return runTest(); + }; + } + + test(task.title, !task.disabled && callback); }); }); }); diff --git a/packages/babel/test/fixtures/transformation/es6.properties.shorthand/shorthand-comments/expected.js b/packages/babel/test/fixtures/transformation/es6.properties.shorthand/shorthand-comments/expected.js index bb002dfed6..7ad721f79c 100644 --- a/packages/babel/test/fixtures/transformation/es6.properties.shorthand/shorthand-comments/expected.js +++ b/packages/babel/test/fixtures/transformation/es6.properties.shorthand/shorthand-comments/expected.js @@ -2,5 +2,5 @@ var A = "a"; var o = { - A: A // comment -}; + A // comment + : A }; diff --git a/packages/babel/test/fixtures/transformation/es6.regex.unicode/options.json b/packages/babel/test/fixtures/transformation/es6.regex.unicode/options.json new file mode 100644 index 0000000000..df1ae3d305 --- /dev/null +++ b/packages/babel/test/fixtures/transformation/es6.regex.unicode/options.json @@ -0,0 +1,3 @@ +{ + "noCheckAst": true +} diff --git a/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1523/expected.js b/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1523/expected.js index d9867f1116..80eccc460c 100644 --- a/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1523/expected.js +++ b/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1523/expected.js @@ -3,9 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -function f(a) { + +exports.f = function f(a) { return !a; -} - -exports.f = f; - +}; diff --git a/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1524/expected.js b/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1524/expected.js index 9f22ed1fa1..a656c4c4af 100644 --- a/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1524/expected.js +++ b/packages/babel/test/fixtures/transformation/minification.dead-code-elimination/issue-1524/expected.js @@ -34,9 +34,7 @@ function* x() { } } -function y() { +exports.y = function y() { return [].concat(babelHelpers.toConsumableArray(x())); -} - -exports.y = y; +}; diff --git a/packages/babel/test/generation.js b/packages/babel/test/generation.js index 49f1960271..834da9c663 100644 --- a/packages/babel/test/generation.js +++ b/packages/babel/test/generation.js @@ -22,11 +22,11 @@ suite("generation", function () { _.each(helper.get("generation"), function (testSuite) { suite("generation/" + testSuite.title, function () { _.each(testSuite.tests, function (task) { - test(task.title, !task.disabled && function (done) { + test(task.title, !task.disabled && function () { var expect = task.expect; var actual = task.actual; - parse(actual.code, { + var actualAst = parse(actual.code, { filename: actual.loc, nonStandard: true, strictMode: false, @@ -38,11 +38,10 @@ _.each(helper.get("generation"), function (testSuite) { "es7.exportExtensions": true, "es7.functionBind": true } - }).then(function (actualAst) { - var actualCode = generate(actualAst, task.options, actual.code).code; - chai.expect(actualCode).to.equal(expect.code, actual.loc + " !== " + expect.loc); - done(); - }, done); + }); + + var actualCode = generate(actualAst, task.options, actual.code).code; + chai.expect(actualCode).to.equal(expect.code, actual.loc + " !== " + expect.loc); }); }); }); diff --git a/packages/babel/test/util.js b/packages/babel/test/util.js index 1fccf8ec77..28a6cd26fa 100644 --- a/packages/babel/test/util.js +++ b/packages/babel/test/util.js @@ -12,27 +12,18 @@ suite("util", function () { test("templates do not recurse", function () { var key = __filename; + var KEY = parse("replacedKey").program.body[0].expression; + var VALUE = parse("+KEY").program.body[0].expression; + util.templates[key] = util.parseTemplate(key, "KEY = VALUE;"); + var result = util.template(key, { KEY: KEY, VALUE: VALUE }); + delete util.templates[key]; - return parse("replacedKey").then(function (result) { - var KEY = result.program.body[0].expression; - - return parse("+KEY").then(function (result) { - var VALUE = result.program.body[0].expression; - - return util.parseTemplate(key, "KEY = VALUE;").then(function (template) { - util.templates[key] = template; - var result = util.template(key, { KEY: KEY, VALUE: VALUE }); - delete util.templates[key]; - - assert.strictEqual( - result.right.argument.name, - "KEY", - "template should not recurse into replaced nodes, replacing KEY inside VALUE" - ); - }); - }); - }); + assert.strictEqual( + result.right.argument.name, + "KEY", + "template should not recurse into replaced nodes, replacing KEY inside VALUE" + ); }); test("canCompile", function () { diff --git a/packages/babylon/README.md b/packages/babylon/README.md index d8da1c161c..c308be37a1 100644 --- a/packages/babylon/README.md +++ b/packages/babylon/README.md @@ -3,5 +3,13 @@

- Babylon is a streaming parser for Babel. + Babylon is a JavaScript parser used in Babel.

+ +---- + +## Credits + +Heavily based on [acorn](https://github.com/marijnh/acorn) and [acorn-jsx](https://github.com/RReverser/acorn-jsx). +Significant diversions expected to occur in the future such as streaming, EBNF definitions, sweet.js integration, +interspacial parsing, comment attachment etc. diff --git a/packages/babylon/package.json b/packages/babylon/package.json index 767cde522a..b5eb00a005 100644 --- a/packages/babylon/package.json +++ b/packages/babylon/package.json @@ -5,8 +5,5 @@ "homepage": "https://babeljs.io/", "license": "MIT", "repository": "babel/babel", - "main": "lib/index.js", - "dependencies": { - "bluebird": "^2.9.33" - } + "main": "lib/index.js" } diff --git a/packages/babylon/src/expression.js b/packages/babylon/src/expression.js index 2126431a17..2fc04783a9 100755 --- a/packages/babylon/src/expression.js +++ b/packages/babylon/src/expression.js @@ -16,10 +16,9 @@ // // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser -import {types as tt} from "./tokentype"; -import {Parser} from "./state"; -import {reservedWords} from "./identifier"; -import {has} from "./util"; +import { types as tt } from "./tokentype"; +import { Parser } from "./state"; +import { reservedWords } from "./identifier"; const pp = Parser.prototype; @@ -29,14 +28,15 @@ const pp = Parser.prototype; // strict mode, init properties are also not allowed to be repeated. pp.checkPropClash = function (prop, propHash) { - if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) - return; + if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return; + let key = prop.key, name; switch (key.type) { - case "Identifier": name = key.name; break; - case "Literal": name = String(key.value); break; - default: return; + case "Identifier": name = key.name; break; + case "Literal": name = String(key.value); break; + default: return; } + let kind = prop.kind; if (this.options.ecmaVersion >= 6) { if (name === "__proto__" && kind === "init") { @@ -45,6 +45,7 @@ pp.checkPropClash = function (prop, propHash) { } return; } + let other; if (propHash[name]) { other = propHash[name]; @@ -196,10 +197,11 @@ pp.parseMaybeUnary = function (refShorthandDefaultPos) { this.next(); node.argument = this.parseMaybeUnary(); if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); - if (update) this.checkLVal(node.argument); - else if (this.strict && node.operator === "delete" && - node.argument.type === "Identifier") + if (update) { + this.checkLVal(node.argument); + } else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") { this.raise(node.start, "Deleting local variable in strict mode"); + } return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); } let startPos = this.start, startLoc = this.startLoc; @@ -666,13 +668,13 @@ pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync, (this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) || (!this.options.allowReserved && this.isReservedWord(prop.key.name))) this.raise(prop.key.start, "Binding " + prop.key.name); - prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone()); } else if (this.type === tt.eq && refShorthandDefaultPos) { if (!refShorthandDefaultPos.start) refShorthandDefaultPos.start = this.start; - prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone()); } else { - prop.value = prop.key; + prop.value = prop.key.__clone(); } prop.shorthand = true; } else { diff --git a/packages/babylon/src/index.js b/packages/babylon/src/index.js index 6b0541ea41..c9cf42ffe5 100755 --- a/packages/babylon/src/index.js +++ b/packages/babylon/src/index.js @@ -1,26 +1,5 @@ -// Acorn is a tiny, fast JavaScript parser written in JavaScript. -// -// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and -// various contributors and released under an MIT license. -// -// Git repositories for Acorn are available at -// -// http://marijnhaverbeke.nl/git/acorn -// https://github.com/marijnh/acorn.git -// -// Please use the [github bug tracker][ghbt] to report issues. -// -// [ghbt]: https://github.com/marijnh/acorn/issues -// -// This file defines the main parser interface. The library also comes -// with a [error-tolerant parser][dammit] and an -// [abstract syntax tree walker][walk], defined in other files. -// -// [dammit]: acorn_loose.js -// [walk]: util/walk.js - -import {Parser, plugins} from "./state"; -import {getOptions} from "./options"; +import { Parser, plugins } from "./state"; +import { getOptions } from "./options"; import "./parseutil"; import "./statement"; import "./lval"; @@ -29,29 +8,22 @@ import "./lookahead"; import "./tokentype"; import "./tokencontext"; -export {Parser, plugins} from "./state"; -export {defaultOptions} from "./options"; -export {SourceLocation} from "./location"; -export {getLineInfo} from "./location"; -export {Node} from "./node"; -export {TokenType, types as tokTypes} from "./tokentype"; -export {TokContext, types as tokContexts} from "./tokencontext"; -export {isIdentifierChar, isIdentifierStart} from "./identifier"; -export {Token} from "./tokenize"; -export {isNewLine, lineBreak, lineBreakG} from "./whitespace"; +export { Parser, plugins } from "./state"; +export { defaultOptions } from "./options"; +export { SourceLocation } from "./location"; +export { getLineInfo } from "./location"; +export { Node } from "./node"; +export { TokenType, types as tokTypes } from "./tokentype"; +export { TokContext, types as tokContexts } from "./tokencontext"; +export { isIdentifierChar, isIdentifierStart } from "./identifier"; +export { Token } from "./tokenize"; +export { isNewLine, lineBreak, lineBreakG } from "./whitespace"; import flowPlugin from "./plugins/flow"; import jsxPlugin from "./plugins/jsx"; plugins.flow = flowPlugin; plugins.jsx = jsxPlugin; -// The main exported interface (under `self.acorn` when in the -// browser) is a `parse` function that takes a code string and -// returns an abstract syntax tree as specified by [Mozilla parser -// API][api]. -// -// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - export function parse(input, options) { return new Parser(getOptions(options), input).parse(); } diff --git a/packages/babylon/src/location.js b/packages/babylon/src/location.js index 5ab0028460..c6832b04da 100755 --- a/packages/babylon/src/location.js +++ b/packages/babylon/src/location.js @@ -1,5 +1,5 @@ -import {Parser} from "./state"; -import {lineBreakG} from "./whitespace"; +import { Parser } from "./state"; +import { lineBreakG } from "./whitespace"; // These are used when `options.locations` is on, for the // `startLoc` and `endLoc` properties. diff --git a/packages/babylon/src/lookahead.js b/packages/babylon/src/lookahead.js index f6f456cfa3..5c1718e45e 100644 --- a/packages/babylon/src/lookahead.js +++ b/packages/babylon/src/lookahead.js @@ -1,4 +1,4 @@ -import {Parser} from "./state"; +import { Parser } from "./state"; const pp = Parser.prototype; diff --git a/packages/babylon/src/lval.js b/packages/babylon/src/lval.js index 6a6dc98b7f..7b083a0f9c 100755 --- a/packages/babylon/src/lval.js +++ b/packages/babylon/src/lval.js @@ -1,7 +1,6 @@ -import {types as tt} from "./tokentype"; -import {Parser} from "./state"; -import {reservedWords} from "./identifier"; -import {has} from "./util"; +import { types as tt } from "./tokentype"; +import { Parser } from "./state"; +import { reservedWords } from "./identifier"; const pp = Parser.prototype; diff --git a/packages/babylon/src/node.js b/packages/babylon/src/node.js index e58a28dc55..0d40e97cc7 100755 --- a/packages/babylon/src/node.js +++ b/packages/babylon/src/node.js @@ -1,5 +1,5 @@ -import {Parser} from "./state"; -import {SourceLocation} from "./location"; +import { Parser } from "./state"; +import { SourceLocation } from "./location"; // Start an AST node, attaching a start offset. @@ -10,12 +10,26 @@ export class Node { this.type = ""; this.start = pos; this.end = 0; - if (parser.options.locations) - this.loc = new SourceLocation(parser, loc); - if (parser.options.directSourceFile) - this.sourceFile = parser.options.directSourceFile; - if (parser.options.ranges) - this.range = [pos, 0]; + + if (parser) { + if (parser.options.locations) { + this.loc = new SourceLocation(parser, loc); + } + + if (parser.options.directSourceFile) { + this.sourceFile = parser.options.directSourceFile; + } + + if (parser.options.ranges) { + this.range = [pos, 0]; + } + } + } + + __clone() { + var node2 = new Node; + for (var key in this) node2[key] = this[key]; + return node2; } } diff --git a/packages/babylon/src/options.js b/packages/babylon/src/options.js index 88ea8fce47..bdbab22040 100755 --- a/packages/babylon/src/options.js +++ b/packages/babylon/src/options.js @@ -1,5 +1,5 @@ -import {has} from "./util"; -import {SourceLocation} from "./location"; +import { has } from "./util"; +import { SourceLocation } from "./location"; // A second optional argument can be given to further configure // the parser process. These options are recognized: diff --git a/packages/babylon/src/parseutil.js b/packages/babylon/src/parseutil.js index 2fae5e6729..1834724bdc 100755 --- a/packages/babylon/src/parseutil.js +++ b/packages/babylon/src/parseutil.js @@ -1,6 +1,6 @@ -import {types as tt} from "./tokentype"; -import {Parser} from "./state"; -import {lineBreak} from "./whitespace"; +import { types as tt } from "./tokentype"; +import { Parser } from "./state"; +import { lineBreak } from "./whitespace"; const pp = Parser.prototype; diff --git a/packages/babylon/src/plugins/flow.js b/packages/babylon/src/plugins/flow.js index a219d6b37d..6efb79b961 100644 --- a/packages/babylon/src/plugins/flow.js +++ b/packages/babylon/src/plugins/flow.js @@ -820,4 +820,4 @@ export default function (instance) { } }; }); -}; +} diff --git a/packages/babylon/src/plugins/jsx/index.js b/packages/babylon/src/plugins/jsx/index.js index 19ff2e3697..3e3368f476 100644 --- a/packages/babylon/src/plugins/jsx/index.js +++ b/packages/babylon/src/plugins/jsx/index.js @@ -3,7 +3,7 @@ import { TokenType, types as tt } from "../../tokentype"; import { TokContext, types as tc } from "../../tokencontext"; import { Parser } from "../../state"; import { isIdentifierChar, isIdentifierStart } from "../../identifier"; -import { isNewLine, lineBreak, lineBreakG } from "../../whitespace"; +import { isNewLine } from "../../whitespace"; const HEX_NUMBER = /^[\da-fA-F]+$/; const DECIMAL_NUMBER = /^\d+$/; @@ -40,37 +40,39 @@ var pp = Parser.prototype; pp.jsxReadToken = function() { var out = "", chunkStart = this.pos; for (;;) { - if (this.pos >= this.input.length) + if (this.pos >= this.input.length) { this.raise(this.start, "Unterminated JSX contents"); + } + var ch = this.input.charCodeAt(this.pos); switch (ch) { - case 60: // "<" - case 123: // "{" - if (this.pos === this.start) { - if (ch === 60 && this.exprAllowed) { - ++this.pos; - return this.finishToken(tt.jsxTagStart); + case 60: // "<" + case 123: // "{" + if (this.pos === this.start) { + if (ch === 60 && this.exprAllowed) { + ++this.pos; + return this.finishToken(tt.jsxTagStart); + } + return this.getTokenFromCode(ch); } - return this.getTokenFromCode(ch); - } - out += this.input.slice(chunkStart, this.pos); - return this.finishToken(tt.jsxText, out); - - case 38: // "&" - out += this.input.slice(chunkStart, this.pos); - out += this.jsxReadEntity(); - chunkStart = this.pos; - break; - - default: - if (isNewLine(ch)) { out += this.input.slice(chunkStart, this.pos); - out += this.jsxReadNewLine(true); + return this.finishToken(tt.jsxText, out); + + case 38: // "&" + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadEntity(); chunkStart = this.pos; - } else { - ++this.pos; - } + break; + + default: + if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + out += this.jsxReadNewLine(true); + chunkStart = this.pos; + } else { + ++this.pos; + } } } }; @@ -96,8 +98,10 @@ pp.jsxReadNewLine = function(normalizeCRLF) { pp.jsxReadString = function(quote) { var out = "", chunkStart = ++this.pos; for (;;) { - if (this.pos >= this.input.length) + if (this.pos >= this.input.length) { this.raise(this.start, "Unterminated string constant"); + } + var ch = this.input.charCodeAt(this.pos); if (ch === quote) break; if (ch === 38) { // "&" @@ -119,8 +123,8 @@ pp.jsxReadString = function(quote) { pp.jsxReadEntity = function() { var str = "", count = 0, entity; var ch = this.input[this.pos]; - if (ch !== "&") - this.raise(this.pos, "Entity must start with an ampersand"); + if (ch !== "&") this.raise(this.pos, "Entity must start with an ampersand"); + var startPos = ++this.pos; while (this.pos < this.input.length && count++ < 10) { ch = this.input[this.pos++]; @@ -168,27 +172,30 @@ pp.jsxReadWord = function() { // Transforms JSX element name to string. function getQualifiedJSXName(object) { - if (object.type === "JSXIdentifier") + if (object.type === "JSXIdentifier") { return object.name; + } - if (object.type === "JSXNamespacedName") + if (object.type === "JSXNamespacedName") { return object.namespace.name + ":" + object.name.name; + } - if (object.type === "JSXMemberExpression") - return getQualifiedJSXName(object.object) + "." + - getQualifiedJSXName(object.property); + if (object.type === "JSXMemberExpression") { + return getQualifiedJSXName(object.object) + "." + getQualifiedJSXName(object.property); + } } // Parse next token as JSX identifier pp.jsxParseIdentifier = function() { var node = this.startNode(); - if (this.type === tt.jsxName) + if (this.type === tt.jsxName) { node.name = this.value; - else if (this.type.keyword) + } else if (this.type.keyword) { node.name = this.type.keyword; - else + } else { this.unexpected(); + } this.next(); return this.finishNode(node, "JSXIdentifier"); }; @@ -199,6 +206,7 @@ pp.jsxParseNamespacedName = function() { var startPos = this.start, startLoc = this.startLoc; var name = this.jsxParseIdentifier(); if (!this.eat(tt.colon)) return name; + var node = this.startNodeAt(startPos, startLoc); node.namespace = name; node.name = this.jsxParseIdentifier(); @@ -224,18 +232,20 @@ pp.jsxParseElementName = function() { pp.jsxParseAttributeValue = function() { switch (this.type) { - case tt.braceL: - var node = this.jsxParseExpressionContainer(); - if (node.expression.type === "JSXEmptyExpression") - this.raise(node.start, "JSX attributes must only be assigned a non-empty expression"); - return node; + case tt.braceL: + var node = this.jsxParseExpressionContainer(); + if (node.expression.type === "JSXEmptyExpression") { + this.raise(node.start, "JSX attributes must only be assigned a non-empty expression"); + } else { + return node; + } - case tt.jsxTagStart: - case tt.string: - return this.parseExprAtom(); + case tt.jsxTagStart: + case tt.string: + return this.parseExprAtom(); - default: - this.raise(this.start, "JSX value should be either an expression or a quoted JSX text"); + default: + this.raise(this.start, "JSX value should be either an expression or a quoted JSX text"); } }; @@ -261,9 +271,11 @@ pp.jsxParseEmptyExpression = function() { pp.jsxParseExpressionContainer = function() { var node = this.startNode(); this.next(); - node.expression = this.type === tt.braceR - ? this.jsxParseEmptyExpression() - : this.parseExpression(); + if (this.type === tt.braceR) { + node.expression = this.jsxParseEmptyExpression(); + } else { + node.expression = this.parseExpression(); + } this.expect(tt.braceR); return this.finishNode(node, "JSXExpressionContainer"); }; @@ -289,8 +301,9 @@ pp.jsxParseOpeningElementAt = function(startPos, startLoc) { var node = this.startNodeAt(startPos, startLoc); node.attributes = []; node.name = this.jsxParseElementName(); - while (this.type !== tt.slash && this.type !== tt.jsxTagEnd) + while (this.type !== tt.slash && this.type !== tt.jsxTagEnd) { node.attributes.push(this.jsxParseAttribute()); + } node.selfClosing = this.eat(tt.slash); this.expect(tt.jsxTagEnd); return this.finishNode(node, "JSXOpeningElement"); @@ -317,32 +330,33 @@ pp.jsxParseElementAt = function(startPos, startLoc) { if (!openingElement.selfClosing) { contents: for (;;) { switch (this.type) { - case tt.jsxTagStart: - startPos = this.start; startLoc = this.startLoc; - this.next(); - if (this.eat(tt.slash)) { - closingElement = this.jsxParseClosingElementAt(startPos, startLoc); - break contents; - } - children.push(this.jsxParseElementAt(startPos, startLoc)); - break; + case tt.jsxTagStart: + startPos = this.start; startLoc = this.startLoc; + this.next(); + if (this.eat(tt.slash)) { + closingElement = this.jsxParseClosingElementAt(startPos, startLoc); + break contents; + } + children.push(this.jsxParseElementAt(startPos, startLoc)); + break; - case tt.jsxText: - children.push(this.parseExprAtom()); - break; + case tt.jsxText: + children.push(this.parseExprAtom()); + break; - case tt.braceL: - children.push(this.jsxParseExpressionContainer()); - break; + case tt.braceL: + children.push(this.jsxParseExpressionContainer()); + break; - default: - this.unexpected(); + default: + this.unexpected(); } } if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) { this.raise( closingElement.start, - "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">"); + "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">" + ); } } @@ -418,4 +432,4 @@ export default function(instance) { } }; }); -}; +} diff --git a/packages/babylon/src/state.js b/packages/babylon/src/state.js index 651e485a7e..dd48c064e7 100755 --- a/packages/babylon/src/state.js +++ b/packages/babylon/src/state.js @@ -1,6 +1,6 @@ -import {reservedWords, keywords} from "./identifier"; -import {types as tt} from "./tokentype"; -import {lineBreak} from "./whitespace"; +import { reservedWords, keywords } from "./identifier"; +import { types as tt } from "./tokentype"; +import { lineBreak } from "./whitespace"; export function Parser(options, input, startPos) { this.options = options; @@ -80,9 +80,7 @@ Parser.prototype.loadPlugins = function (plugins) { }; Parser.prototype.parse = function () { - return new Promise((resolve) => { - let node = this.options.program || this.startNode(); - this.nextToken(); - resolve(this.parseTopLevel(node)); - }); + let node = this.options.program || this.startNode(); + this.nextToken(); + return this.parseTopLevel(node); }; diff --git a/packages/babylon/src/statement.js b/packages/babylon/src/statement.js index e62a54c2d4..bc441511d0 100755 --- a/packages/babylon/src/statement.js +++ b/packages/babylon/src/statement.js @@ -1,6 +1,6 @@ -import {types as tt} from "./tokentype"; -import {Parser} from "./state"; -import {lineBreak} from "./whitespace"; +import { types as tt } from "./tokentype"; +import { Parser } from "./state"; +import { lineBreak } from "./whitespace"; const pp = Parser.prototype; @@ -734,7 +734,7 @@ pp.parseExportSpecifiers = function () { let node = this.startNode(); node.local = this.parseIdent(this.type === tt._default); - node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; + node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local.__clone(); nodes.push(this.finishNode(node, "ExportSpecifier")); } @@ -792,7 +792,7 @@ pp.parseImportSpecifiers = function (node) { let specifier = this.startNode(); specifier.imported = this.parseIdent(true); - specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported; + specifier.local = this.eatContextual("as") ? this.parseIdent() : specifier.imported.__clone(); this.checkLVal(specifier.local, true); node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); } diff --git a/packages/babylon/src/tokencontext.js b/packages/babylon/src/tokencontext.js index 8395160c8d..16853534ad 100755 --- a/packages/babylon/src/tokencontext.js +++ b/packages/babylon/src/tokencontext.js @@ -2,9 +2,9 @@ // given point in the program is loosely based on sweet.js' approach. // See https://github.com/mozilla/sweet.js/wiki/design -import {Parser} from "./state"; -import {types as tt} from "./tokentype"; -import {lineBreak} from "./whitespace"; +import { Parser } from "./state"; +import { types as tt } from "./tokentype"; +import { lineBreak } from "./whitespace"; export class TokContext { constructor(token, isExpr, preserveSpace, override) { diff --git a/packages/babylon/src/tokenize.js b/packages/babylon/src/tokenize.js index 8cade26638..fbe25a01f2 100755 --- a/packages/babylon/src/tokenize.js +++ b/packages/babylon/src/tokenize.js @@ -1,8 +1,8 @@ -import {isIdentifierStart, isIdentifierChar} from "./identifier"; -import {types as tt, keywords as keywordTypes} from "./tokentype"; -import {Parser} from "./state"; -import {SourceLocation} from "./location"; -import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace"; +import { isIdentifierStart, isIdentifierChar } from "./identifier"; +import { types as tt, keywords as keywordTypes } from "./tokentype"; +import { Parser } from "./state"; +import { SourceLocation } from "./location"; +import { lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace } from "./whitespace"; // Object type used to represent tokens. Note that normally, tokens // simply exist as properties on the parser object. This is only @@ -123,14 +123,15 @@ pp.skipBlockComment = function () { pp.skipLineComment = function (startSkip) { let start = this.pos; let startLoc = this.options.onComment && this.curPosition(); - let ch = this.input.charCodeAt(this.pos+=startSkip); + let ch = this.input.charCodeAt(this.pos += startSkip); while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { ++this.pos; ch = this.input.charCodeAt(this.pos); } - if (this.options.onComment) + if (this.options.onComment) { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.curPosition()); + } }; // Called at the start of the parse and after every token. Skips @@ -143,10 +144,12 @@ pp.skipSpace = function() { case 32: case 160: // ' ' ++this.pos; break; + case 13: if (this.input.charCodeAt(this.pos + 1) === 10) { ++this.pos; } + case 10: case 8232: case 8233: ++this.pos; if (this.options.locations) { @@ -154,18 +157,22 @@ pp.skipSpace = function() { this.lineStart = this.pos; } break; + case 47: // '/' switch (this.input.charCodeAt(this.pos + 1)) { case 42: // '*' this.skipBlockComment(); break; + case 47: this.skipLineComment(2); break; + default: break loop; } break; + default: if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { ++this.pos; @@ -402,28 +409,35 @@ pp.finishOp = function (type, size) { return this.finishToken(type, str); }; -var regexpUnicodeSupport = false; -try { - new RegExp("\uffff", "u"); - regexpUnicodeSupport = true; -} catch(e) {} - // Parse a regular expression. Some context-awareness is necessary, // since a '/' inside a '[]' set does not end the expression. -pp.readRegexp = function () { +function tryCreateRegexp(src, flags, throwErrorStart) { + try { + return new RegExp(src, flags); + } catch (e) { + if (throwErrorStart !== undefined) { + if (e instanceof SyntaxError) this.raise(throwErrorStart, "Error parsing regular expression: " + e.message); + this.raise(e); + } + } +} + +var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u"); + +pp.readRegexp = function() { let escaped, inClass, start = this.pos; for (;;) { if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression"); let ch = this.input.charAt(this.pos); if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression"); - if (!escaped) { + if (escaped) { + escaped = false; + } else { if (ch === "[") inClass = true; else if (ch === "]" && inClass) inClass = false; else if (ch === "/" && !inClass) break; escaped = ch === "\\"; - } else { - escaped = false; } ++this.pos; } @@ -456,23 +470,14 @@ pp.readRegexp = function () { } // Detect invalid regular expressions. let value = null; - // Rhino's regular expression parser is flaky and throws uncatchable exceptions, // so don't do detection if we are running under Rhino if (!isRhino) { - try { - new RegExp(tmp); - } catch (e) { - if (e instanceof SyntaxError) this.raise(start, `Error parsing regular expression: ${e.message}`); - this.raise(e); - } + tryCreateRegexp(tmp, undefined, start); // Get a regular expression object for this pattern-flag pair, or `null` in // case the current environment doesn't support the flags it uses. - try { - value = new RegExp(content, mods); - } catch (err) {} + value = tryCreateRegexp(content, mods); } - return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value}); }; diff --git a/packages/babylon/test/driver.js b/packages/babylon/test/driver.js index 3a02fcf9e2..527fda18df 100755 --- a/packages/babylon/test/driver.js +++ b/packages/babylon/test/driver.js @@ -27,24 +27,9 @@ function runTest(test) { if (expected.onToken = testOpts.onToken) testOpts.onToken = []; - return parse(test.code, testOpts).then(function (ast) { - if (test.error) { - throw new Error("Expected error message: " + test.error + ". But parsing succeeded."); - } else if (test.assert) { - var error = test.assert(ast); - if (error) throw new Error("Assertion failed: " + error); - } else { - var mis = misMatch(test.ast, ast); - for (var name in expected) { - if (mis) break; - if (expected[name]) { - mis = misMatch(expected[name], testOpts[name]); - testOpts[name] = expected[name]; - } - } - if (mis) throw new Error(mis); - } - }, function (err) { + try { + var ast = parse(test.code, testOpts); + } catch (err) { if (test.error) { if (err.message === test.error) { return; @@ -54,7 +39,24 @@ function runTest(test) { } throw err; - }); + } + + if (test.error) { + throw new Error("Expected error message: " + test.error + ". But parsing succeeded."); + } else if (test.assert) { + var error = test.assert(ast); + if (error) throw new Error("Assertion failed: " + error); + } else { + var mis = misMatch(test.ast, ast); + for (var name in expected) { + if (mis) break; + if (expected[name]) { + mis = misMatch(expected[name], testOpts[name]); + testOpts[name] = expected[name]; + } + } + if (mis) throw new Error(mis); + } }; function ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); } diff --git a/packages/babylon/test/tests-jsx.js b/packages/babylon/test/tests-jsx.js index e31c41a1d4..02f86a1ab3 100644 --- a/packages/babylon/test/tests-jsx.js +++ b/packages/babylon/test/tests-jsx.js @@ -3636,7 +3636,9 @@ if (typeof exports !== "undefined") { var testFail = require("./driver.js").testFail; } -testFail("var x =
one
two
;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)"); +testFail("var x =
one
two
;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)", { + plugins: { jsx: true } +}); for (var ns in fbTestFixture) { ns = fbTestFixture[ns];