Skip to content

Commit cee1344

Browse files
mysticateamarijnh
authored andcommitted
update to use Parser.acorn
1 parent 1f01445 commit cee1344

File tree

3 files changed

+126
-36
lines changed

3 files changed

+126
-36
lines changed

index.js

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,53 @@ const XHTMLEntities = require('./xhtml');
55
const hexNumber = /^[\da-fA-F]+$/;
66
const decimalNumber = /^\d+$/;
77

8-
const acorn = require("acorn");
9-
const tt = acorn.tokTypes;
10-
const TokContext = acorn.TokContext;
11-
const tokContexts = acorn.tokContexts;
12-
const TokenType = acorn.TokenType;
13-
const isNewLine = acorn.isNewLine;
14-
const isIdentifierStart = acorn.isIdentifierStart;
15-
const isIdentifierChar = acorn.isIdentifierChar;
16-
17-
const tc_oTag = new TokContext('<tag', false);
18-
const tc_cTag = new TokContext('</tag', false);
19-
const tc_expr = new TokContext('<tag>...</tag>', true, true);
20-
21-
const tok = {
22-
jsxName: new TokenType('jsxName'),
23-
jsxText: new TokenType('jsxText', {beforeExpr: true}),
24-
jsxTagStart: new TokenType('jsxTagStart'),
25-
jsxTagEnd: new TokenType('jsxTagEnd')
26-
}
8+
// The map to `acorn-jsx` tokens from `acorn` namespace objects.
9+
const acornJsxMap = new WeakMap();
10+
11+
// Get the original tokens for the given `acorn` namespace object.
12+
function getJsxTokens(acorn) {
13+
acorn = acorn.Parser.acorn || acorn;
14+
let acornJsx = acornJsxMap.get(acorn);
15+
if (!acornJsx) {
16+
const tt = acorn.tokTypes;
17+
const TokContext = acorn.TokContext;
18+
const TokenType = acorn.TokenType;
19+
const tc_oTag = new TokContext('<tag', false);
20+
const tc_cTag = new TokContext('</tag', false);
21+
const tc_expr = new TokContext('<tag>...</tag>', true, true);
22+
const tokContexts = {
23+
tc_oTag: tc_oTag,
24+
tc_cTag: tc_cTag,
25+
tc_expr: tc_expr
26+
};
27+
const tokTypes = {
28+
jsxName: new TokenType('jsxName'),
29+
jsxText: new TokenType('jsxText', {beforeExpr: true}),
30+
jsxTagStart: new TokenType('jsxTagStart'),
31+
jsxTagEnd: new TokenType('jsxTagEnd')
32+
};
33+
34+
tokTypes.jsxTagStart.updateContext = function() {
35+
this.context.push(tc_expr); // treat as beginning of JSX expression
36+
this.context.push(tc_oTag); // start opening tag context
37+
this.exprAllowed = false;
38+
};
39+
tokTypes.jsxTagEnd.updateContext = function(prevType) {
40+
let out = this.context.pop();
41+
if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
42+
this.context.pop();
43+
this.exprAllowed = this.curContext() === tc_expr;
44+
} else {
45+
this.exprAllowed = true;
46+
}
47+
};
2748

28-
tok.jsxTagStart.updateContext = function() {
29-
this.context.push(tc_expr); // treat as beginning of JSX expression
30-
this.context.push(tc_oTag); // start opening tag context
31-
this.exprAllowed = false;
32-
};
33-
tok.jsxTagEnd.updateContext = function(prevType) {
34-
let out = this.context.pop();
35-
if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
36-
this.context.pop();
37-
this.exprAllowed = this.curContext() === tc_expr;
38-
} else {
39-
this.exprAllowed = true;
49+
acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes };
50+
acornJsxMap.set(acorn, acornJsx);
4051
}
41-
};
52+
53+
return acornJsx;
54+
}
4255

4356
// Transforms JSX element name to string.
4457

@@ -64,12 +77,38 @@ module.exports = function(options) {
6477
allowNamespaces: options.allowNamespaces !== false,
6578
allowNamespacedObjects: !!options.allowNamespacedObjects
6679
}, Parser);
67-
}
80+
};
6881
};
69-
module.exports.tokTypes = tok;
82+
83+
// This is `tokTypes` of the peer dep.
84+
// This can be different instances from the actual `tokTypes` this plugin uses.
85+
Object.defineProperty(module.exports, "tokTypes", {
86+
get: function get_tokTypes() {
87+
return getJsxTokens(require("acorn")).tokTypes;
88+
},
89+
configurable: true,
90+
enumerable: true
91+
});
7092

7193
function plugin(options, Parser) {
94+
const acorn = Parser.acorn || require("acorn");
95+
const acornJsx = getJsxTokens(acorn);
96+
const tt = acorn.tokTypes;
97+
const tok = acornJsx.tokTypes;
98+
const tokContexts = acorn.tokContexts;
99+
const tc_oTag = acornJsx.tokContexts.tc_oTag;
100+
const tc_cTag = acornJsx.tokContexts.tc_cTag;
101+
const tc_expr = acornJsx.tokContexts.tc_expr;
102+
const isNewLine = acorn.isNewLine;
103+
const isIdentifierStart = acorn.isIdentifierStart;
104+
const isIdentifierChar = acorn.isIdentifierChar;
105+
72106
return class extends Parser {
107+
// Expose actual `tokTypes` and `tokContexts` to other plugins.
108+
static get acornJsx() {
109+
return acornJsx;
110+
}
111+
73112
// Reads inline JSX contents token.
74113
jsx_readToken() {
75114
let out = '', chunkStart = this.pos;
@@ -419,15 +458,15 @@ function plugin(options, Parser) {
419458
++this.pos;
420459
return this.finishToken(tok.jsxTagStart);
421460
}
422-
return super.readToken(code)
461+
return super.readToken(code);
423462
}
424463

425464
updateContext(prevType) {
426465
if (this.type == tt.braceL) {
427466
var curContext = this.curContext();
428467
if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);
429468
else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);
430-
else super.updateContext(prevType)
469+
else super.updateContext(prevType);
431470
this.exprAllowed = true;
432471
} else if (this.type === tt.slash && prevType === tok.jsxTagStart) {
433472
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore

test/run.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var driver = require("./driver.js");
22
require("./tests-jsx.js");
3+
require("./tests-misc.js");
34

45
function group(name) {
56
if (typeof console === "object" && console.group) {

test/tests-misc.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"use strict";
2+
3+
if (typeof exports !== "undefined") {
4+
var assert = require("assert");
5+
var acorn = require("acorn");
6+
var jsx = require("..");
7+
var testAssert = require("./driver.js").testAssert;
8+
}
9+
10+
testAssert("// the enhanced Parser instance should have a static property 'acornJsx'.", function() {
11+
const JsxParser = acorn.Parser.extend(jsx());
12+
assert(JsxParser.acornJsx);
13+
});
14+
15+
testAssert("// 'acornJsx' should be the same instance for the same acorn.", function() {
16+
const JsxParser1 = acorn.Parser.extend(jsx());
17+
const JsxParser2 = acorn.Parser.extend(jsx());
18+
assert.strictEqual(JsxParser1.acornJsx, JsxParser2.acornJsx);
19+
});
20+
21+
testAssert("// the static property 'acornJsx' should have two properties.", function() {
22+
const JsxParser = acorn.Parser.extend(jsx());
23+
assert(JsxParser.acornJsx.tokTypes, "should have 'tokTypes'.");
24+
assert(JsxParser.acornJsx.tokContexts, "should have 'tokContexts'.");
25+
});
26+
27+
testAssert("// 'acornJsx.tokTypes' should be used.", function() {
28+
const JsxParser = acorn.Parser.extend(jsx());
29+
const code = '<a>{/* foo */}</a>';
30+
const expectedTokTypes = [
31+
JsxParser.acornJsx.tokTypes.jsxTagStart,
32+
JsxParser.acornJsx.tokTypes.jsxName,
33+
JsxParser.acornJsx.tokTypes.jsxTagEnd,
34+
acorn.tokTypes.braceL,
35+
acorn.tokTypes.braceR,
36+
JsxParser.acornJsx.tokTypes.jsxTagStart,
37+
acorn.tokTypes.slash,
38+
JsxParser.acornJsx.tokTypes.jsxName,
39+
JsxParser.acornJsx.tokTypes.jsxTagEnd,
40+
acorn.tokTypes.eof
41+
]
42+
const actualTokens = []
43+
44+
JsxParser.parse(code, {onToken: actualTokens})
45+
46+
assert.strictEqual(actualTokens.length, expectedTokTypes.length);
47+
for (let i = 0; i < actualTokens.length; ++i) {
48+
assert.strictEqual(actualTokens[i].type, expectedTokTypes[i]);
49+
}
50+
});

0 commit comments

Comments
 (0)