diff --git a/index.js b/index.js
index 460e793..6df802b 100644
--- a/index.js
+++ b/index.js
@@ -5,40 +5,53 @@ const XHTMLEntities = require('./xhtml');
const hexNumber = /^[\da-fA-F]+$/;
const decimalNumber = /^\d+$/;
-const acorn = require("acorn");
-const tt = acorn.tokTypes;
-const TokContext = acorn.TokContext;
-const tokContexts = acorn.tokContexts;
-const TokenType = acorn.TokenType;
-const isNewLine = acorn.isNewLine;
-const isIdentifierStart = acorn.isIdentifierStart;
-const isIdentifierChar = acorn.isIdentifierChar;
-
-const tc_oTag = new TokContext('...', true, true);
-
-const tok = {
- jsxName: new TokenType('jsxName'),
- jsxText: new TokenType('jsxText', {beforeExpr: true}),
- jsxTagStart: new TokenType('jsxTagStart'),
- jsxTagEnd: new TokenType('jsxTagEnd')
-}
+// The map to `acorn-jsx` tokens from `acorn` namespace objects.
+const acornJsxMap = new WeakMap();
+
+// Get the original tokens for the given `acorn` namespace object.
+function getJsxTokens(acorn) {
+ acorn = acorn.Parser.acorn || acorn;
+ let acornJsx = acornJsxMap.get(acorn);
+ if (!acornJsx) {
+ const tt = acorn.tokTypes;
+ const TokContext = acorn.TokContext;
+ const TokenType = acorn.TokenType;
+ const tc_oTag = new TokContext('...', true, true);
+ const tokContexts = {
+ tc_oTag: tc_oTag,
+ tc_cTag: tc_cTag,
+ tc_expr: tc_expr
+ };
+ const tokTypes = {
+ jsxName: new TokenType('jsxName'),
+ jsxText: new TokenType('jsxText', {beforeExpr: true}),
+ jsxTagStart: new TokenType('jsxTagStart'),
+ jsxTagEnd: new TokenType('jsxTagEnd')
+ };
+
+ tokTypes.jsxTagStart.updateContext = function() {
+ this.context.push(tc_expr); // treat as beginning of JSX expression
+ this.context.push(tc_oTag); // start opening tag context
+ this.exprAllowed = false;
+ };
+ tokTypes.jsxTagEnd.updateContext = function(prevType) {
+ let out = this.context.pop();
+ if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
+ this.context.pop();
+ this.exprAllowed = this.curContext() === tc_expr;
+ } else {
+ this.exprAllowed = true;
+ }
+ };
-tok.jsxTagStart.updateContext = function() {
- this.context.push(tc_expr); // treat as beginning of JSX expression
- this.context.push(tc_oTag); // start opening tag context
- this.exprAllowed = false;
-};
-tok.jsxTagEnd.updateContext = function(prevType) {
- let out = this.context.pop();
- if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
- this.context.pop();
- this.exprAllowed = this.curContext() === tc_expr;
- } else {
- this.exprAllowed = true;
+ acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes };
+ acornJsxMap.set(acorn, acornJsx);
}
-};
+
+ return acornJsx;
+}
// Transforms JSX element name to string.
@@ -64,12 +77,38 @@ module.exports = function(options) {
allowNamespaces: options.allowNamespaces !== false,
allowNamespacedObjects: !!options.allowNamespacedObjects
}, Parser);
- }
+ };
};
-module.exports.tokTypes = tok;
+
+// This is `tokTypes` of the peer dep.
+// This can be different instances from the actual `tokTypes` this plugin uses.
+Object.defineProperty(module.exports, "tokTypes", {
+ get: function get_tokTypes() {
+ return getJsxTokens(require("acorn")).tokTypes;
+ },
+ configurable: true,
+ enumerable: true
+});
function plugin(options, Parser) {
+ const acorn = Parser.acorn || require("acorn");
+ const acornJsx = getJsxTokens(acorn);
+ const tt = acorn.tokTypes;
+ const tok = acornJsx.tokTypes;
+ const tokContexts = acorn.tokContexts;
+ const tc_oTag = acornJsx.tokContexts.tc_oTag;
+ const tc_cTag = acornJsx.tokContexts.tc_cTag;
+ const tc_expr = acornJsx.tokContexts.tc_expr;
+ const isNewLine = acorn.isNewLine;
+ const isIdentifierStart = acorn.isIdentifierStart;
+ const isIdentifierChar = acorn.isIdentifierChar;
+
return class extends Parser {
+ // Expose actual `tokTypes` and `tokContexts` to other plugins.
+ static get acornJsx() {
+ return acornJsx;
+ }
+
// Reads inline JSX contents token.
jsx_readToken() {
let out = '', chunkStart = this.pos;
@@ -419,7 +458,7 @@ function plugin(options, Parser) {
++this.pos;
return this.finishToken(tok.jsxTagStart);
}
- return super.readToken(code)
+ return super.readToken(code);
}
updateContext(prevType) {
@@ -427,7 +466,7 @@ function plugin(options, Parser) {
var curContext = this.curContext();
if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);
else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);
- else super.updateContext(prevType)
+ else super.updateContext(prevType);
this.exprAllowed = true;
} else if (this.type === tt.slash && prevType === tok.jsxTagStart) {
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
diff --git a/test/run.js b/test/run.js
index e398b1b..b9d98bf 100644
--- a/test/run.js
+++ b/test/run.js
@@ -1,5 +1,6 @@
var driver = require("./driver.js");
require("./tests-jsx.js");
+require("./tests-misc.js");
function group(name) {
if (typeof console === "object" && console.group) {
diff --git a/test/tests-misc.js b/test/tests-misc.js
new file mode 100644
index 0000000..82a03dd
--- /dev/null
+++ b/test/tests-misc.js
@@ -0,0 +1,50 @@
+"use strict";
+
+if (typeof exports !== "undefined") {
+ var assert = require("assert");
+ var acorn = require("acorn");
+ var jsx = require("..");
+ var testAssert = require("./driver.js").testAssert;
+}
+
+testAssert("// the enhanced Parser instance should have a static property 'acornJsx'.", function() {
+ const JsxParser = acorn.Parser.extend(jsx());
+ assert(JsxParser.acornJsx);
+});
+
+testAssert("// 'acornJsx' should be the same instance for the same acorn.", function() {
+ const JsxParser1 = acorn.Parser.extend(jsx());
+ const JsxParser2 = acorn.Parser.extend(jsx());
+ assert.strictEqual(JsxParser1.acornJsx, JsxParser2.acornJsx);
+});
+
+testAssert("// the static property 'acornJsx' should have two properties.", function() {
+ const JsxParser = acorn.Parser.extend(jsx());
+ assert(JsxParser.acornJsx.tokTypes, "should have 'tokTypes'.");
+ assert(JsxParser.acornJsx.tokContexts, "should have 'tokContexts'.");
+});
+
+testAssert("// 'acornJsx.tokTypes' should be used.", function() {
+ const JsxParser = acorn.Parser.extend(jsx());
+ const code = '{/* foo */}';
+ const expectedTokTypes = [
+ JsxParser.acornJsx.tokTypes.jsxTagStart,
+ JsxParser.acornJsx.tokTypes.jsxName,
+ JsxParser.acornJsx.tokTypes.jsxTagEnd,
+ acorn.tokTypes.braceL,
+ acorn.tokTypes.braceR,
+ JsxParser.acornJsx.tokTypes.jsxTagStart,
+ acorn.tokTypes.slash,
+ JsxParser.acornJsx.tokTypes.jsxName,
+ JsxParser.acornJsx.tokTypes.jsxTagEnd,
+ acorn.tokTypes.eof
+ ]
+ const actualTokens = []
+
+ JsxParser.parse(code, {onToken: actualTokens})
+
+ assert.strictEqual(actualTokens.length, expectedTokTypes.length);
+ for (let i = 0; i < actualTokens.length; ++i) {
+ assert.strictEqual(actualTokens[i].type, expectedTokTypes[i]);
+ }
+});