more architectural changes

This commit is contained in:
Sebastian McKenzie 2015-07-11 20:56:26 +01:00
parent afe5eb118c
commit a226641631
42 changed files with 393 additions and 463 deletions

View File

@ -19,7 +19,8 @@
"no-loop-func": 0, "no-loop-func": 0,
"no-unreachable": 0, "no-unreachable": 0,
"no-labels": 0, "no-labels": 0,
"no-process-exit": 0 "no-process-exit": 0,
"camelcase": 0
}, },
"env": { "env": {
"node": true "node": true

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store .DS_Store
node_modules node_modules
test/core/tmp test/tmp
*.log *.log
*.cache *.cache
/templates.json /templates.json

View File

@ -6,7 +6,6 @@ cache:
node_js: node_js:
- iojs - iojs
- "0.8"
- "0.10" - "0.10"
- "0.12" - "0.12"

View File

@ -10,6 +10,4 @@ commander.option("-t, --output-type [type]", "Type of output (global|umd|var)",
commander.usage("[options]"); commander.usage("[options]");
commander.parse(process.argv); commander.parse(process.argv);
util.ensureTemplates().then(function () {
console.log(runtime(commander.whitelist, commander.outputType)); console.log(runtime(commander.whitelist, commander.outputType));
});

View File

@ -35,7 +35,7 @@ exports.transform = function (filename, code, opts) {
opts.ignore = null; opts.ignore = null;
opts.only = null; opts.only = null;
var result = babel.__plsDontUseThis(code, opts); var result = babel.transform(code, opts);
result.filename = filename; result.filename = filename;
result.actual = code; result.actual = code;
return result; return result;

View File

@ -0,0 +1,2 @@
var bar = () => console.log("bar");
bar();

View File

@ -0,0 +1,5 @@
import "./bar2";
import "./not_node_modules";
var foo = () => console.log("foo");
foo();

View File

@ -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 {
<Some jsx="element" />;
}
catch (e) {}

View File

@ -4,11 +4,6 @@ import * as babylon from "babylon";
import * as util from "../util"; import * as util from "../util";
import fs from "fs"; import fs from "fs";
var deasync;
try {
deasync = require("deasync");
} catch (err) {}
export { util, babylon as acorn, transform }; export { util, babylon as acorn, transform };
export { pipeline } from "../transformation"; export { pipeline } from "../transformation";
export { canCompile } from "../util"; export { canCompile } from "../util";
@ -42,32 +37,24 @@ export function transformFile(filename: string, opts?: Object, callback: Functio
opts.filename = filename; 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);
} }
export function __plsDontUseThis(code, opts) { callback(null, result);
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 = {}) { export function parse(code, opts = {}) {

View File

@ -57,7 +57,7 @@ var compile = function (filename, opts = {}) {
optsManager.mergeOptions(transformOpts); optsManager.mergeOptions(transformOpts);
opts = optsManager.init(opts); 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; var env = process.env.BABEL_ENV || process.env.NODE_ENV;
if (env) cacheKey += `:${env}`; if (env) cacheKey += `:${env}`;
@ -70,8 +70,7 @@ var compile = function (filename, opts = {}) {
} }
if (!result) { if (!result) {
var file = fs.readFileSync(filename, "utf8"); result = babel.transformFileSync(filename, extend(opts, {
result = babel.__plsDontUseThis(file, extend(opts, {
sourceMap: "both", sourceMap: "both",
ast: false ast: false
})); }));

View File

@ -45,9 +45,8 @@ export default function (code, opts = {}) {
parseOpts.plugins.flow = true; parseOpts.plugins.flow = true;
} }
return babylon.parse(code, parseOpts).then(function (ast) { var ast = babylon.parse(code, parseOpts);
estraverse.attachComments(ast, comments, tokens); estraverse.attachComments(ast, comments, tokens);
ast = normalizeAst(ast, comments, commentsAndTokens); ast = normalizeAst(ast, comments, commentsAndTokens);
return ast; return ast;
});
} }

View File

@ -12,7 +12,6 @@ import defaults from "lodash/object/defaults";
import includes from "lodash/collection/includes"; import includes from "lodash/collection/includes";
import traverse from "../../traversal"; import traverse from "../../traversal";
import Logger from "./logger"; import Logger from "./logger";
import { Promise } from "bluebird";
import Plugin from "../plugin"; import Plugin from "../plugin";
import parse from "../../helpers/parse"; import parse from "../../helpers/parse";
import Hub from "../../traversal/hub"; import Hub from "../../traversal/hub";
@ -429,10 +428,9 @@ export default class File {
parseOpts.sourceType = "module"; parseOpts.sourceType = "module";
this.log.debug("Parse start"); this.log.debug("Parse start");
return parse(code, parseOpts).then((ast) => { var ast = parse(code, parseOpts);
this.log.debug("Parse stop"); this.log.debug("Parse stop");
return ast; return ast;
});
} }
_addAst(ast) { _addAst(ast) {
@ -467,11 +465,7 @@ export default class File {
} }
this.call("post"); this.call("post");
return new Promise((resolve, reject) => { return this.generate();
util.ensureTemplates().then(() => {
resolve(this.generate());
}, reject);
});
} }
wrap(code, callback) { wrap(code, callback) {
@ -479,7 +473,7 @@ export default class File {
try { try {
if (this.shouldIgnore()) { if (this.shouldIgnore()) {
return Promise.resolve(this.makeResult({ code, ignored: true })); return this.makeResult({ code, ignored: true });
} else { } else {
return callback(); return callback();
} }
@ -519,9 +513,8 @@ export default class File {
parseCode() { parseCode() {
this.parseShebang(); this.parseShebang();
return this.parse(this.code).then((ast) => { var ast = this.parse(this.code);
this.addAst(ast); this.addAst(ast);
});
} }
shouldIgnore() { shouldIgnore() {
@ -567,6 +560,10 @@ export default class File {
map: map map: map
}; };
result.then = function (callback) {
callback(result);
};
if (this.opts.code) { if (this.opts.code) {
result.code = code; result.code = code;
} }

View File

@ -1,6 +1,5 @@
import PluginManager from "./file/plugin-manager"; import PluginManager from "./file/plugin-manager";
import normalizeAst from "../helpers/normalize-ast"; import normalizeAst from "../helpers/normalize-ast";
import { Promise } from "bluebird";
import Plugin from "./plugin"; import Plugin from "./plugin";
import assign from "lodash/object/assign"; import assign from "lodash/object/assign";
import object from "../helpers/object"; import object from "../helpers/object";
@ -91,20 +90,19 @@ export default class Pipeline {
var file = new File(opts, this); var file = new File(opts, this);
return file.wrap(code, function () { return file.wrap(code, function () {
file.addCode(code); file.addCode(code);
return file.parseCode(code).then(() => file.transform()); file.parseCode(code);
return file.transform();
}); });
} }
transformFromAst(ast, code, opts) { transformFromAst(ast, code, opts) {
return new Promise((resolve) => {
ast = normalizeAst(ast); ast = normalizeAst(ast);
var file = new File(opts, this); var file = new File(opts, this);
resolve(file.wrap(code, function () { return file.wrap(code, function () {
file.addCode(code); file.addCode(code);
file.addAst(ast); file.addAst(ast);
return file.transform(); return file.transform();
}));
}); });
} }

View File

@ -5,7 +5,6 @@ export default {
//- builtin-pre //- builtin-pre
strict: require("./other/strict"), strict: require("./other/strict"),
eval: require("babel-plugin-eval"), eval: require("babel-plugin-eval"),
_explode: require("./internal/explode"),
_validation: require("./internal/validation"), _validation: require("./internal/validation"),
_hoistDirectives: require("./internal/hoist-directives"), _hoistDirectives: require("./internal/hoist-directives"),
"minification.removeDebugger": require("babel-plugin-remove-debugger"), "minification.removeDebugger": require("babel-plugin-remove-debugger"),

View File

@ -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")
};

View File

@ -10,7 +10,6 @@ import contains from "lodash/collection/contains";
import traverse from "./traversal"; import traverse from "./traversal";
import isString from "lodash/lang/isString"; import isString from "lodash/lang/isString";
import isRegExp from "lodash/lang/isRegExp"; import isRegExp from "lodash/lang/isRegExp";
import { Promise } from "bluebird";
import Module from "module"; import Module from "module";
import isEmpty from "lodash/lang/isEmpty"; import isEmpty from "lodash/lang/isEmpty";
import parse from "./helpers/parse"; import parse from "./helpers/parse";
@ -157,8 +156,6 @@ var templateVisitor = {
// //
export function template(name: string, nodes?: Array<Object>, keepExpression?: boolean): Object { export function template(name: string, nodes?: Array<Object>, keepExpression?: boolean): Object {
if (!exports.templates) throw new ReferenceError(`haven't initialised templates yet`);
var ast = exports.templates[name]; var ast = exports.templates[name];
if (!ast) throw new ReferenceError(`unknown template ${name}`); if (!ast) throw new ReferenceError(`unknown template ${name}`);
@ -183,11 +180,10 @@ export function template(name: string, nodes?: Array<Object>, keepExpression?: b
return node; return node;
} }
} }
export function parseTemplate(loc: string, code: string): Object { export function parseTemplate(loc: string, code: string): Object {
return parse(code, { filename: loc, looseModules: true }).then(function (ast) { var ast = parse(code, { filename: loc, looseModules: true }).program;
return traverse.removeProperties(ast.program); ast = traverse.removeProperties(ast);
}); return ast;
} }
function loadTemplates() { function loadTemplates() {
@ -198,37 +194,22 @@ function loadTemplates() {
throw new ReferenceError(messages.get("missingTemplatesDirectory")); 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)) { var key = path.basename(name, path.extname(name));
if (name[0] === ".") continue; var loc = path.join(templatesLoc, name);
var code = fs.readFileSync(loc, "utf8");
let key = path.basename(name, path.extname(name)); templates[key] = parseTemplate(loc, code);
let loc = path.join(templatesLoc, name);
let code = fs.readFileSync(loc, "utf8");
promises.push(parseTemplate(loc, code).then(function (template) {
templates[key] = template;
}));
} }
return Promise.all(promises).then(function () { return templates;
return exports.templates = templates;
});
}
export function ensureTemplates() {
return new Promise((resolve) => {
if (exports.templates) {
resolve(exports.templates);
} else {
resolve(loadTemplates());
}
});
} }
try { try {
exports.templates = require("../templates.json"); exports.templates = require("../../templates.json");
} catch (err) { } catch (err) {
if (err.code !== "MODULE_NOT_FOUND") throw err; if (err.code !== "MODULE_NOT_FOUND") throw err;
exports.templates = loadTemplates();
} }

View File

@ -12,12 +12,7 @@ var _ = require("lodash");
require("../lib/polyfill"); require("../lib/polyfill");
var doneBeforeEval = false;
function beforeEval() {
if (doneBeforeEval) return;
doneBeforeEval = true;
eval(buildExernalHelpers()); eval(buildExernalHelpers());
}
global.assertNoOwnProperties = function (obj) { global.assertNoOwnProperties = function (obj) {
assert.equal(Object.getOwnPropertyNames(obj).length, 0); assert.equal(Object.getOwnPropertyNames(obj).length, 0);
@ -49,7 +44,6 @@ chai.assert.throw = function (fn, msg) {
return chai.assert._throw(fn, msg); return chai.assert._throw(fn, msg);
}; };
var run = function (task, done) { var run = function (task, done) {
var actual = task.actual; var actual = task.actual;
var expect = task.expect; var expect = task.expect;
@ -74,10 +68,8 @@ var run = function (task, done) {
helper.esvalid(result.ast.program, result.code, opts.loc); helper.esvalid(result.ast.program, result.code, opts.loc);
}; };
var promises = [];
if (execCode) { if (execCode) {
promises.push(transform(execCode, getOpts(exec)).then(function (result) { result = transform(execCode, getOpts(exec));
execCode = result.code; execCode = result.code;
try { try {
@ -91,9 +83,8 @@ var run = function (task, done) {
} }
}; };
var fn = new Function("require", "exports", execCode); var fn = new Function("require", "done", "exports", execCode);
beforeEval(); fn.call(global, fakeRequire, chai.assert, {}, done);
fn.call(global, fakeRequire, {});
} catch (err) { } catch (err) {
err.message = exec.loc + ": " + err.message; err.message = exec.loc + ": " + err.message;
err.message += codeFrame(execCode); err.message += codeFrame(execCode);
@ -101,13 +92,12 @@ var run = function (task, done) {
} }
checkAst(result, exec); checkAst(result, exec);
}));
} }
var actualCode = actual.code; var actualCode = actual.code;
var expectCode = expect.code; var expectCode = expect.code;
if (!execCode || actualCode) { if (!execCode || actualCode) {
promises.push(transform(actualCode, getOpts(actual)).then(function (result) { result = transform(actualCode, getOpts(actual));
actualCode = result.code.trim(); actualCode = result.code.trim();
try { try {
@ -118,6 +108,7 @@ var run = function (task, done) {
} }
checkAst(result, actual); checkAst(result, actual);
}
if (task.sourceMap) { if (task.sourceMap) {
chai.expect(result.map).to.deep.equal(task.sourceMap); chai.expect(result.map).to.deep.equal(task.sourceMap);
@ -133,12 +124,6 @@ var run = function (task, done) {
chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual);
}); });
} }
}));
}
Promise.all(promises).then(function () {
done()
}, done);
}; };
module.exports = function (suiteOpts, taskOpts, dynamicOpts) { module.exports = function (suiteOpts, taskOpts, dynamicOpts) {
@ -155,7 +140,11 @@ module.exports = function (suiteOpts, taskOpts, dynamicOpts) {
_.each(testSuite.tests, function (task) { _.each(testSuite.tests, function (task) {
if (_.contains(suiteOpts.ignoreTasks, task.title) || _.contains(suiteOpts.ignoreTasks, testSuite.title + "/" + task.title)) return; 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); _.extend(task.options, taskOpts);
if (dynamicOpts) dynamicOpts(task.options, task); if (dynamicOpts) dynamicOpts(task.options, task);
@ -165,26 +154,24 @@ module.exports = function (suiteOpts, taskOpts, dynamicOpts) {
// the options object with useless options // the options object with useless options
delete task.options.throws; delete task.options.throws;
return run(task, function (err) { assert.throws(runTask, function (err) {
if (err) { return throwMsg === true || err.message.indexOf(throwMsg) >= 0;
// we just wanted any error message });
if (throwMsg === true) return done(); } else {
runTask();
}
};
if (err.message.indexOf(throwMsg) >= 0) { var callback;
// success! if (task.options.asyncExec) {
done(); callback = runTest;
} else { } else {
// didn't include it :( callback = function () {
done(new Error("Expected an error message of " + JSON.stringify(throwMsg) + " but got " + JSON.stringify(err.message))); return runTest();
};
} }
} else {
done(new Error("Expected an error message but got none")); test(task.title, !task.disabled && callback);
}
});
} else {
return run(task, done);
}
});
}); });
}); });
}); });

View File

@ -2,5 +2,5 @@
var A = "a"; var A = "a";
var o = { var o = {
A: A // comment A // comment
}; : A };

View File

@ -0,0 +1,3 @@
{
"noCheckAst": true
}

View File

@ -3,9 +3,7 @@
Object.defineProperty(exports, "__esModule", { Object.defineProperty(exports, "__esModule", {
value: true value: true
}); });
function f(a) {
exports.f = function f(a) {
return !a; return !a;
} };
exports.f = f;

View File

@ -34,9 +34,7 @@ function* x() {
} }
} }
function y() { exports.y = function y() {
return [].concat(babelHelpers.toConsumableArray(x())); return [].concat(babelHelpers.toConsumableArray(x()));
} };
exports.y = y;

View File

@ -22,11 +22,11 @@ suite("generation", function () {
_.each(helper.get("generation"), function (testSuite) { _.each(helper.get("generation"), function (testSuite) {
suite("generation/" + testSuite.title, function () { suite("generation/" + testSuite.title, function () {
_.each(testSuite.tests, function (task) { _.each(testSuite.tests, function (task) {
test(task.title, !task.disabled && function (done) { test(task.title, !task.disabled && function () {
var expect = task.expect; var expect = task.expect;
var actual = task.actual; var actual = task.actual;
parse(actual.code, { var actualAst = parse(actual.code, {
filename: actual.loc, filename: actual.loc,
nonStandard: true, nonStandard: true,
strictMode: false, strictMode: false,
@ -38,11 +38,10 @@ _.each(helper.get("generation"), function (testSuite) {
"es7.exportExtensions": true, "es7.exportExtensions": true,
"es7.functionBind": true "es7.functionBind": true
} }
}).then(function (actualAst) { });
var actualCode = generate(actualAst, task.options, actual.code).code; var actualCode = generate(actualAst, task.options, actual.code).code;
chai.expect(actualCode).to.equal(expect.code, actual.loc + " !== " + expect.loc); chai.expect(actualCode).to.equal(expect.code, actual.loc + " !== " + expect.loc);
done();
}, done);
}); });
}); });
}); });

View File

@ -12,16 +12,10 @@ suite("util", function () {
test("templates do not recurse", function () { test("templates do not recurse", function () {
var key = __filename; 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;");
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 }); var result = util.template(key, { KEY: KEY, VALUE: VALUE });
delete util.templates[key]; delete util.templates[key];
@ -31,9 +25,6 @@ suite("util", function () {
"template should not recurse into replaced nodes, replacing KEY inside VALUE" "template should not recurse into replaced nodes, replacing KEY inside VALUE"
); );
}); });
});
});
});
test("canCompile", function () { test("canCompile", function () {
assert.ok(util.canCompile("test.js")); assert.ok(util.canCompile("test.js"));

View File

@ -3,5 +3,13 @@
</p> </p>
<p align="center"> <p align="center">
Babylon is a streaming parser for <a href="https://github.com/babel/babel">Babel</a>. Babylon is a JavaScript parser used in <a href="https://github.com/babel/babel">Babel</a>.
</p> </p>
----
## 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.

View File

@ -5,8 +5,5 @@
"homepage": "https://babeljs.io/", "homepage": "https://babeljs.io/",
"license": "MIT", "license": "MIT",
"repository": "babel/babel", "repository": "babel/babel",
"main": "lib/index.js", "main": "lib/index.js"
"dependencies": {
"bluebird": "^2.9.33"
}
} }

View File

@ -19,7 +19,6 @@
import { types as tt } from "./tokentype"; import { types as tt } from "./tokentype";
import { Parser } from "./state"; import { Parser } from "./state";
import { reservedWords } from "./identifier"; import { reservedWords } from "./identifier";
import {has} from "./util";
const pp = Parser.prototype; const pp = Parser.prototype;
@ -29,14 +28,15 @@ const pp = Parser.prototype;
// strict mode, init properties are also not allowed to be repeated. // strict mode, init properties are also not allowed to be repeated.
pp.checkPropClash = function (prop, propHash) { pp.checkPropClash = function (prop, propHash) {
if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
return;
let key = prop.key, name; let key = prop.key, name;
switch (key.type) { switch (key.type) {
case "Identifier": name = key.name; break; case "Identifier": name = key.name; break;
case "Literal": name = String(key.value); break; case "Literal": name = String(key.value); break;
default: return; default: return;
} }
let kind = prop.kind; let kind = prop.kind;
if (this.options.ecmaVersion >= 6) { if (this.options.ecmaVersion >= 6) {
if (name === "__proto__" && kind === "init") { if (name === "__proto__" && kind === "init") {
@ -45,6 +45,7 @@ pp.checkPropClash = function (prop, propHash) {
} }
return; return;
} }
let other; let other;
if (propHash[name]) { if (propHash[name]) {
other = propHash[name]; other = propHash[name];
@ -196,10 +197,11 @@ pp.parseMaybeUnary = function (refShorthandDefaultPos) {
this.next(); this.next();
node.argument = this.parseMaybeUnary(); node.argument = this.parseMaybeUnary();
if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
if (update) this.checkLVal(node.argument); if (update) {
else if (this.strict && node.operator === "delete" && this.checkLVal(node.argument);
node.argument.type === "Identifier") } else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") {
this.raise(node.start, "Deleting local variable in strict mode"); this.raise(node.start, "Deleting local variable in strict mode");
}
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
} }
let startPos = this.start, startLoc = this.startLoc; 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.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||
(!this.options.allowReserved && this.isReservedWord(prop.key.name))) (!this.options.allowReserved && this.isReservedWord(prop.key.name)))
this.raise(prop.key.start, "Binding " + 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) { } else if (this.type === tt.eq && refShorthandDefaultPos) {
if (!refShorthandDefaultPos.start) if (!refShorthandDefaultPos.start)
refShorthandDefaultPos.start = this.start; refShorthandDefaultPos.start = this.start;
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
} else { } else {
prop.value = prop.key; prop.value = prop.key.__clone();
} }
prop.shorthand = true; prop.shorthand = true;
} else { } else {

View File

@ -1,24 +1,3 @@
// 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 { Parser, plugins } from "./state";
import { getOptions } from "./options"; import { getOptions } from "./options";
import "./parseutil"; import "./parseutil";
@ -45,13 +24,6 @@ import jsxPlugin from "./plugins/jsx";
plugins.flow = flowPlugin; plugins.flow = flowPlugin;
plugins.jsx = jsxPlugin; 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) { export function parse(input, options) {
return new Parser(getOptions(options), input).parse(); return new Parser(getOptions(options), input).parse();
} }

View File

@ -1,7 +1,6 @@
import { types as tt } from "./tokentype"; import { types as tt } from "./tokentype";
import { Parser } from "./state"; import { Parser } from "./state";
import { reservedWords } from "./identifier"; import { reservedWords } from "./identifier";
import {has} from "./util";
const pp = Parser.prototype; const pp = Parser.prototype;

View File

@ -10,14 +10,28 @@ export class Node {
this.type = ""; this.type = "";
this.start = pos; this.start = pos;
this.end = 0; this.end = 0;
if (parser.options.locations)
if (parser) {
if (parser.options.locations) {
this.loc = new SourceLocation(parser, loc); this.loc = new SourceLocation(parser, loc);
if (parser.options.directSourceFile) }
if (parser.options.directSourceFile) {
this.sourceFile = parser.options.directSourceFile; this.sourceFile = parser.options.directSourceFile;
if (parser.options.ranges) }
if (parser.options.ranges) {
this.range = [pos, 0]; this.range = [pos, 0];
} }
} }
}
__clone() {
var node2 = new Node;
for (var key in this) node2[key] = this[key];
return node2;
}
}
pp.startNode = function () { pp.startNode = function () {
return new Node(this, this.start, this.startLoc); return new Node(this, this.start, this.startLoc);

View File

@ -820,4 +820,4 @@ export default function (instance) {
} }
}; };
}); });
}; }

View File

@ -3,7 +3,7 @@ import { TokenType, types as tt } from "../../tokentype";
import { TokContext, types as tc } from "../../tokencontext"; import { TokContext, types as tc } from "../../tokencontext";
import { Parser } from "../../state"; import { Parser } from "../../state";
import { isIdentifierChar, isIdentifierStart } from "../../identifier"; import { isIdentifierChar, isIdentifierStart } from "../../identifier";
import { isNewLine, lineBreak, lineBreakG } from "../../whitespace"; import { isNewLine } from "../../whitespace";
const HEX_NUMBER = /^[\da-fA-F]+$/; const HEX_NUMBER = /^[\da-fA-F]+$/;
const DECIMAL_NUMBER = /^\d+$/; const DECIMAL_NUMBER = /^\d+$/;
@ -40,8 +40,10 @@ var pp = Parser.prototype;
pp.jsxReadToken = function() { pp.jsxReadToken = function() {
var out = "", chunkStart = this.pos; var out = "", chunkStart = this.pos;
for (;;) { for (;;) {
if (this.pos >= this.input.length) if (this.pos >= this.input.length) {
this.raise(this.start, "Unterminated JSX contents"); this.raise(this.start, "Unterminated JSX contents");
}
var ch = this.input.charCodeAt(this.pos); var ch = this.input.charCodeAt(this.pos);
switch (ch) { switch (ch) {
@ -96,8 +98,10 @@ pp.jsxReadNewLine = function(normalizeCRLF) {
pp.jsxReadString = function(quote) { pp.jsxReadString = function(quote) {
var out = "", chunkStart = ++this.pos; var out = "", chunkStart = ++this.pos;
for (;;) { for (;;) {
if (this.pos >= this.input.length) if (this.pos >= this.input.length) {
this.raise(this.start, "Unterminated string constant"); this.raise(this.start, "Unterminated string constant");
}
var ch = this.input.charCodeAt(this.pos); var ch = this.input.charCodeAt(this.pos);
if (ch === quote) break; if (ch === quote) break;
if (ch === 38) { // "&" if (ch === 38) { // "&"
@ -119,8 +123,8 @@ pp.jsxReadString = function(quote) {
pp.jsxReadEntity = function() { pp.jsxReadEntity = function() {
var str = "", count = 0, entity; var str = "", count = 0, entity;
var ch = this.input[this.pos]; var ch = this.input[this.pos];
if (ch !== "&") if (ch !== "&") this.raise(this.pos, "Entity must start with an ampersand");
this.raise(this.pos, "Entity must start with an ampersand");
var startPos = ++this.pos; var startPos = ++this.pos;
while (this.pos < this.input.length && count++ < 10) { while (this.pos < this.input.length && count++ < 10) {
ch = this.input[this.pos++]; ch = this.input[this.pos++];
@ -168,27 +172,30 @@ pp.jsxReadWord = function() {
// Transforms JSX element name to string. // Transforms JSX element name to string.
function getQualifiedJSXName(object) { function getQualifiedJSXName(object) {
if (object.type === "JSXIdentifier") if (object.type === "JSXIdentifier") {
return object.name; return object.name;
}
if (object.type === "JSXNamespacedName") if (object.type === "JSXNamespacedName") {
return object.namespace.name + ":" + object.name.name; return object.namespace.name + ":" + object.name.name;
}
if (object.type === "JSXMemberExpression") if (object.type === "JSXMemberExpression") {
return getQualifiedJSXName(object.object) + "." + return getQualifiedJSXName(object.object) + "." + getQualifiedJSXName(object.property);
getQualifiedJSXName(object.property); }
} }
// Parse next token as JSX identifier // Parse next token as JSX identifier
pp.jsxParseIdentifier = function() { pp.jsxParseIdentifier = function() {
var node = this.startNode(); var node = this.startNode();
if (this.type === tt.jsxName) if (this.type === tt.jsxName) {
node.name = this.value; node.name = this.value;
else if (this.type.keyword) } else if (this.type.keyword) {
node.name = this.type.keyword; node.name = this.type.keyword;
else } else {
this.unexpected(); this.unexpected();
}
this.next(); this.next();
return this.finishNode(node, "JSXIdentifier"); return this.finishNode(node, "JSXIdentifier");
}; };
@ -199,6 +206,7 @@ pp.jsxParseNamespacedName = function() {
var startPos = this.start, startLoc = this.startLoc; var startPos = this.start, startLoc = this.startLoc;
var name = this.jsxParseIdentifier(); var name = this.jsxParseIdentifier();
if (!this.eat(tt.colon)) return name; if (!this.eat(tt.colon)) return name;
var node = this.startNodeAt(startPos, startLoc); var node = this.startNodeAt(startPos, startLoc);
node.namespace = name; node.namespace = name;
node.name = this.jsxParseIdentifier(); node.name = this.jsxParseIdentifier();
@ -226,9 +234,11 @@ pp.jsxParseAttributeValue = function() {
switch (this.type) { switch (this.type) {
case tt.braceL: case tt.braceL:
var node = this.jsxParseExpressionContainer(); var node = this.jsxParseExpressionContainer();
if (node.expression.type === "JSXEmptyExpression") if (node.expression.type === "JSXEmptyExpression") {
this.raise(node.start, "JSX attributes must only be assigned a non-empty expression"); this.raise(node.start, "JSX attributes must only be assigned a non-empty expression");
} else {
return node; return node;
}
case tt.jsxTagStart: case tt.jsxTagStart:
case tt.string: case tt.string:
@ -261,9 +271,11 @@ pp.jsxParseEmptyExpression = function() {
pp.jsxParseExpressionContainer = function() { pp.jsxParseExpressionContainer = function() {
var node = this.startNode(); var node = this.startNode();
this.next(); this.next();
node.expression = this.type === tt.braceR if (this.type === tt.braceR) {
? this.jsxParseEmptyExpression() node.expression = this.jsxParseEmptyExpression();
: this.parseExpression(); } else {
node.expression = this.parseExpression();
}
this.expect(tt.braceR); this.expect(tt.braceR);
return this.finishNode(node, "JSXExpressionContainer"); return this.finishNode(node, "JSXExpressionContainer");
}; };
@ -289,8 +301,9 @@ pp.jsxParseOpeningElementAt = function(startPos, startLoc) {
var node = this.startNodeAt(startPos, startLoc); var node = this.startNodeAt(startPos, startLoc);
node.attributes = []; node.attributes = [];
node.name = this.jsxParseElementName(); 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.attributes.push(this.jsxParseAttribute());
}
node.selfClosing = this.eat(tt.slash); node.selfClosing = this.eat(tt.slash);
this.expect(tt.jsxTagEnd); this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningElement"); return this.finishNode(node, "JSXOpeningElement");
@ -342,7 +355,8 @@ pp.jsxParseElementAt = function(startPos, startLoc) {
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) { if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
this.raise( this.raise(
closingElement.start, 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) {
} }
}; };
}); });
}; }

View File

@ -80,9 +80,7 @@ Parser.prototype.loadPlugins = function (plugins) {
}; };
Parser.prototype.parse = function () { Parser.prototype.parse = function () {
return new Promise((resolve) => {
let node = this.options.program || this.startNode(); let node = this.options.program || this.startNode();
this.nextToken(); this.nextToken();
resolve(this.parseTopLevel(node)); return this.parseTopLevel(node);
});
}; };

View File

@ -734,7 +734,7 @@ pp.parseExportSpecifiers = function () {
let node = this.startNode(); let node = this.startNode();
node.local = this.parseIdent(this.type === tt._default); 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")); nodes.push(this.finishNode(node, "ExportSpecifier"));
} }
@ -792,7 +792,7 @@ pp.parseImportSpecifiers = function (node) {
let specifier = this.startNode(); let specifier = this.startNode();
specifier.imported = this.parseIdent(true); 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); this.checkLVal(specifier.local, true);
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
} }

View File

@ -128,9 +128,10 @@ pp.skipLineComment = function (startSkip) {
++this.pos; ++this.pos;
ch = this.input.charCodeAt(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, this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
startLoc, this.curPosition()); startLoc, this.curPosition());
}
}; };
// Called at the start of the parse and after every token. Skips // Called at the start of the parse and after every token. Skips
@ -143,10 +144,12 @@ pp.skipSpace = function() {
case 32: case 160: // ' ' case 32: case 160: // ' '
++this.pos; ++this.pos;
break; break;
case 13: case 13:
if (this.input.charCodeAt(this.pos + 1) === 10) { if (this.input.charCodeAt(this.pos + 1) === 10) {
++this.pos; ++this.pos;
} }
case 10: case 8232: case 8233: case 10: case 8232: case 8233:
++this.pos; ++this.pos;
if (this.options.locations) { if (this.options.locations) {
@ -154,18 +157,22 @@ pp.skipSpace = function() {
this.lineStart = this.pos; this.lineStart = this.pos;
} }
break; break;
case 47: // '/' case 47: // '/'
switch (this.input.charCodeAt(this.pos + 1)) { switch (this.input.charCodeAt(this.pos + 1)) {
case 42: // '*' case 42: // '*'
this.skipBlockComment(); this.skipBlockComment();
break; break;
case 47: case 47:
this.skipLineComment(2); this.skipLineComment(2);
break; break;
default: default:
break loop; break loop;
} }
break; break;
default: default:
if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
++this.pos; ++this.pos;
@ -402,28 +409,35 @@ pp.finishOp = function (type, size) {
return this.finishToken(type, str); 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, // Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression. // since a '/' inside a '[]' set does not end the expression.
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() { pp.readRegexp = function() {
let escaped, inClass, start = this.pos; let escaped, inClass, start = this.pos;
for (;;) { for (;;) {
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression"); if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
let ch = this.input.charAt(this.pos); let ch = this.input.charAt(this.pos);
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression"); if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
if (!escaped) { if (escaped) {
escaped = false;
} else {
if (ch === "[") inClass = true; if (ch === "[") inClass = true;
else if (ch === "]" && inClass) inClass = false; else if (ch === "]" && inClass) inClass = false;
else if (ch === "/" && !inClass) break; else if (ch === "/" && !inClass) break;
escaped = ch === "\\"; escaped = ch === "\\";
} else {
escaped = false;
} }
++this.pos; ++this.pos;
} }
@ -456,23 +470,14 @@ pp.readRegexp = function () {
} }
// Detect invalid regular expressions. // Detect invalid regular expressions.
let value = null; let value = null;
// Rhino's regular expression parser is flaky and throws uncatchable exceptions, // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
// so don't do detection if we are running under Rhino // so don't do detection if we are running under Rhino
if (!isRhino) { if (!isRhino) {
try { tryCreateRegexp(tmp, undefined, start);
new RegExp(tmp);
} catch (e) {
if (e instanceof SyntaxError) this.raise(start, `Error parsing regular expression: ${e.message}`);
this.raise(e);
}
// Get a regular expression object for this pattern-flag pair, or `null` in // Get a regular expression object for this pattern-flag pair, or `null` in
// case the current environment doesn't support the flags it uses. // case the current environment doesn't support the flags it uses.
try { value = tryCreateRegexp(content, mods);
value = new RegExp(content, mods);
} catch (err) {}
} }
return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value}); return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value});
}; };

View File

@ -27,7 +27,20 @@ function runTest(test) {
if (expected.onToken = testOpts.onToken) if (expected.onToken = testOpts.onToken)
testOpts.onToken = []; testOpts.onToken = [];
return parse(test.code, testOpts).then(function (ast) { try {
var ast = parse(test.code, testOpts);
} catch (err) {
if (test.error) {
if (err.message === test.error) {
return;
} else {
throw new Error("Expected error message: " + test.error + ". Got error message: " + err.message);
}
}
throw err;
}
if (test.error) { if (test.error) {
throw new Error("Expected error message: " + test.error + ". But parsing succeeded."); throw new Error("Expected error message: " + test.error + ". But parsing succeeded.");
} else if (test.assert) { } else if (test.assert) {
@ -44,17 +57,6 @@ function runTest(test) {
} }
if (mis) throw new Error(mis); if (mis) throw new Error(mis);
} }
}, function (err) {
if (test.error) {
if (err.message === test.error) {
return;
} else {
throw new Error("Expected error message: " + test.error + ". Got error message: " + err.message);
}
}
throw err;
});
}; };
function ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); } function ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); }

View File

@ -3636,7 +3636,9 @@ if (typeof exports !== "undefined") {
var testFail = require("./driver.js").testFail; var testFail = require("./driver.js").testFail;
} }
testFail("var x = <div>one</div><div>two</div>;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)"); testFail("var x = <div>one</div><div>two</div>;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)", {
plugins: { jsx: true }
});
for (var ns in fbTestFixture) { for (var ns in fbTestFixture) {
ns = fbTestFixture[ns]; ns = fbTestFixture[ns];