diff --git a/index.cjs b/index.cjs new file mode 100644 index 0000000..e1bc0ab --- /dev/null +++ b/index.cjs @@ -0,0 +1,767 @@ +// DO NOT edit this file, it's auto-generated from 'rollup.config.js'; any changes will be overwritten. + +'use strict'; + +var acorn = require('acorn'); + +function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n["default"] = e; + return Object.freeze(n); +} + +var acorn__namespace = /*#__PURE__*/_interopNamespace(acorn); + +var XHTMLEntities = { + quot: '\u0022', + amp: '&', + apos: '\u0027', + lt: '<', + gt: '>', + nbsp: '\u00A0', + iexcl: '\u00A1', + cent: '\u00A2', + pound: '\u00A3', + curren: '\u00A4', + yen: '\u00A5', + brvbar: '\u00A6', + sect: '\u00A7', + uml: '\u00A8', + copy: '\u00A9', + ordf: '\u00AA', + laquo: '\u00AB', + not: '\u00AC', + shy: '\u00AD', + reg: '\u00AE', + macr: '\u00AF', + deg: '\u00B0', + plusmn: '\u00B1', + sup2: '\u00B2', + sup3: '\u00B3', + acute: '\u00B4', + micro: '\u00B5', + para: '\u00B6', + middot: '\u00B7', + cedil: '\u00B8', + sup1: '\u00B9', + ordm: '\u00BA', + raquo: '\u00BB', + frac14: '\u00BC', + frac12: '\u00BD', + frac34: '\u00BE', + iquest: '\u00BF', + Agrave: '\u00C0', + Aacute: '\u00C1', + Acirc: '\u00C2', + Atilde: '\u00C3', + Auml: '\u00C4', + Aring: '\u00C5', + AElig: '\u00C6', + Ccedil: '\u00C7', + Egrave: '\u00C8', + Eacute: '\u00C9', + Ecirc: '\u00CA', + Euml: '\u00CB', + Igrave: '\u00CC', + Iacute: '\u00CD', + Icirc: '\u00CE', + Iuml: '\u00CF', + ETH: '\u00D0', + Ntilde: '\u00D1', + Ograve: '\u00D2', + Oacute: '\u00D3', + Ocirc: '\u00D4', + Otilde: '\u00D5', + Ouml: '\u00D6', + times: '\u00D7', + Oslash: '\u00D8', + Ugrave: '\u00D9', + Uacute: '\u00DA', + Ucirc: '\u00DB', + Uuml: '\u00DC', + Yacute: '\u00DD', + THORN: '\u00DE', + szlig: '\u00DF', + agrave: '\u00E0', + aacute: '\u00E1', + acirc: '\u00E2', + atilde: '\u00E3', + auml: '\u00E4', + aring: '\u00E5', + aelig: '\u00E6', + ccedil: '\u00E7', + egrave: '\u00E8', + eacute: '\u00E9', + ecirc: '\u00EA', + euml: '\u00EB', + igrave: '\u00EC', + iacute: '\u00ED', + icirc: '\u00EE', + iuml: '\u00EF', + eth: '\u00F0', + ntilde: '\u00F1', + ograve: '\u00F2', + oacute: '\u00F3', + ocirc: '\u00F4', + otilde: '\u00F5', + ouml: '\u00F6', + divide: '\u00F7', + oslash: '\u00F8', + ugrave: '\u00F9', + uacute: '\u00FA', + ucirc: '\u00FB', + uuml: '\u00FC', + yacute: '\u00FD', + thorn: '\u00FE', + yuml: '\u00FF', + OElig: '\u0152', + oelig: '\u0153', + Scaron: '\u0160', + scaron: '\u0161', + Yuml: '\u0178', + fnof: '\u0192', + circ: '\u02C6', + tilde: '\u02DC', + Alpha: '\u0391', + Beta: '\u0392', + Gamma: '\u0393', + Delta: '\u0394', + Epsilon: '\u0395', + Zeta: '\u0396', + Eta: '\u0397', + Theta: '\u0398', + Iota: '\u0399', + Kappa: '\u039A', + Lambda: '\u039B', + Mu: '\u039C', + Nu: '\u039D', + Xi: '\u039E', + Omicron: '\u039F', + Pi: '\u03A0', + Rho: '\u03A1', + Sigma: '\u03A3', + Tau: '\u03A4', + Upsilon: '\u03A5', + Phi: '\u03A6', + Chi: '\u03A7', + Psi: '\u03A8', + Omega: '\u03A9', + alpha: '\u03B1', + beta: '\u03B2', + gamma: '\u03B3', + delta: '\u03B4', + epsilon: '\u03B5', + zeta: '\u03B6', + eta: '\u03B7', + theta: '\u03B8', + iota: '\u03B9', + kappa: '\u03BA', + lambda: '\u03BB', + mu: '\u03BC', + nu: '\u03BD', + xi: '\u03BE', + omicron: '\u03BF', + pi: '\u03C0', + rho: '\u03C1', + sigmaf: '\u03C2', + sigma: '\u03C3', + tau: '\u03C4', + upsilon: '\u03C5', + phi: '\u03C6', + chi: '\u03C7', + psi: '\u03C8', + omega: '\u03C9', + thetasym: '\u03D1', + upsih: '\u03D2', + piv: '\u03D6', + ensp: '\u2002', + emsp: '\u2003', + thinsp: '\u2009', + zwnj: '\u200C', + zwj: '\u200D', + lrm: '\u200E', + rlm: '\u200F', + ndash: '\u2013', + mdash: '\u2014', + lsquo: '\u2018', + rsquo: '\u2019', + sbquo: '\u201A', + ldquo: '\u201C', + rdquo: '\u201D', + bdquo: '\u201E', + dagger: '\u2020', + Dagger: '\u2021', + bull: '\u2022', + hellip: '\u2026', + permil: '\u2030', + prime: '\u2032', + Prime: '\u2033', + lsaquo: '\u2039', + rsaquo: '\u203A', + oline: '\u203E', + frasl: '\u2044', + euro: '\u20AC', + image: '\u2111', + weierp: '\u2118', + real: '\u211C', + trade: '\u2122', + alefsym: '\u2135', + larr: '\u2190', + uarr: '\u2191', + rarr: '\u2192', + darr: '\u2193', + harr: '\u2194', + crarr: '\u21B5', + lArr: '\u21D0', + uArr: '\u21D1', + rArr: '\u21D2', + dArr: '\u21D3', + hArr: '\u21D4', + forall: '\u2200', + part: '\u2202', + exist: '\u2203', + empty: '\u2205', + nabla: '\u2207', + isin: '\u2208', + notin: '\u2209', + ni: '\u220B', + prod: '\u220F', + sum: '\u2211', + minus: '\u2212', + lowast: '\u2217', + radic: '\u221A', + prop: '\u221D', + infin: '\u221E', + ang: '\u2220', + and: '\u2227', + or: '\u2228', + cap: '\u2229', + cup: '\u222A', + 'int': '\u222B', + there4: '\u2234', + sim: '\u223C', + cong: '\u2245', + asymp: '\u2248', + ne: '\u2260', + equiv: '\u2261', + le: '\u2264', + ge: '\u2265', + sub: '\u2282', + sup: '\u2283', + nsub: '\u2284', + sube: '\u2286', + supe: '\u2287', + oplus: '\u2295', + otimes: '\u2297', + perp: '\u22A5', + sdot: '\u22C5', + lceil: '\u2308', + rceil: '\u2309', + lfloor: '\u230A', + rfloor: '\u230B', + lang: '\u2329', + rang: '\u232A', + loz: '\u25CA', + spades: '\u2660', + clubs: '\u2663', + hearts: '\u2665', + diams: '\u2666' +}; + +const hexNumber = /^[\da-fA-F]+$/; +const decimalNumber = /^\d+$/; + +// 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', {startsExpr: true}), + 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; + } + }; + + acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes }; + acornJsxMap.set(acorn, acornJsx); + } + + return acornJsx; +} + +// Transforms JSX element name to string. + +function getQualifiedJSXName(object) { + if (!object) + return object; + + if (object.type === 'JSXIdentifier') + return object.name; + + if (object.type === 'JSXNamespacedName') + return object.namespace.name + ':' + object.name.name; + + if (object.type === 'JSXMemberExpression') + return getQualifiedJSXName(object.object) + '.' + + getQualifiedJSXName(object.property); +} + +function acornJsx(options) { + options = options || {}; + return function(Parser) { + return plugin({ + allowNamespaces: options.allowNamespaces !== false, + allowNamespacedObjects: !!options.allowNamespacedObjects + }, Parser); + }; +} +// This is `tokTypes` of the peer dep. +// This can be different instances from the actual `tokTypes` this plugin uses. +Object.defineProperty(acornJsx, "tokTypes", { + get: function get_tokTypes() { + return getJsxTokens(acorn__namespace).tokTypes; + }, + configurable: true, + enumerable: true +}); + +function plugin(options, Parser) { + const acrn = Parser.acorn || acorn__namespace; + const acornJsx = getJsxTokens(acorn__namespace); + const tt = acrn.tokTypes; + const tok = acornJsx.tokTypes; + const tokContexts = acrn.tokContexts; + const tc_oTag = acornJsx.tokContexts.tc_oTag; + const tc_cTag = acornJsx.tokContexts.tc_cTag; + const tc_expr = acornJsx.tokContexts.tc_expr; + const isNewLine = acrn.isNewLine; + const isIdentifierStart = acrn.isIdentifierStart; + const isIdentifierChar = acrn.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; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, 'Unterminated JSX contents'); + let 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(tok.jsxTagStart); + } + return this.getTokenFromCode(ch); + } + out += this.input.slice(chunkStart, this.pos); + return this.finishToken(tok.jsxText, out); + + case 38: // '&' + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readEntity(); + chunkStart = this.pos; + break; + + case 62: // '>' + case 125: // '}' + this.raise( + this.pos, + "Unexpected token `" + this.input[this.pos] + "`. Did you mean `" + + (ch === 62 ? ">" : "}") + "` or " + "`{\"" + this.input[this.pos] + "\"}" + "`?" + ); + + default: + if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readNewLine(true); + chunkStart = this.pos; + } else { + ++this.pos; + } + } + } + } + + jsx_readNewLine(normalizeCRLF) { + let ch = this.input.charCodeAt(this.pos); + let out; + ++this.pos; + if (ch === 13 && this.input.charCodeAt(this.pos) === 10) { + ++this.pos; + out = normalizeCRLF ? '\n' : '\r\n'; + } else { + out = String.fromCharCode(ch); + } + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + + return out; + } + + jsx_readString(quote) { + let out = '', chunkStart = ++this.pos; + for (;;) { + if (this.pos >= this.input.length) + this.raise(this.start, 'Unterminated string constant'); + let ch = this.input.charCodeAt(this.pos); + if (ch === quote) break; + if (ch === 38) { // '&' + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readEntity(); + chunkStart = this.pos; + } else if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos); + out += this.jsx_readNewLine(false); + chunkStart = this.pos; + } else { + ++this.pos; + } + } + out += this.input.slice(chunkStart, this.pos++); + return this.finishToken(tt.string, out); + } + + jsx_readEntity() { + let str = '', count = 0, entity; + let ch = this.input[this.pos]; + if (ch !== '&') + this.raise(this.pos, 'Entity must start with an ampersand'); + let startPos = ++this.pos; + while (this.pos < this.input.length && count++ < 10) { + ch = this.input[this.pos++]; + if (ch === ';') { + if (str[0] === '#') { + if (str[1] === 'x') { + str = str.substr(2); + if (hexNumber.test(str)) + entity = String.fromCharCode(parseInt(str, 16)); + } else { + str = str.substr(1); + if (decimalNumber.test(str)) + entity = String.fromCharCode(parseInt(str, 10)); + } + } else { + entity = XHTMLEntities[str]; + } + break; + } + str += ch; + } + if (!entity) { + this.pos = startPos; + return '&'; + } + return entity; + } + + // Read a JSX identifier (valid tag or attribute name). + // + // Optimized version since JSX identifiers can't contain + // escape characters and so can be read as single slice. + // Also assumes that first character was already checked + // by isIdentifierStart in readToken. + + jsx_readWord() { + let ch, start = this.pos; + do { + ch = this.input.charCodeAt(++this.pos); + } while (isIdentifierChar(ch) || ch === 45); // '-' + return this.finishToken(tok.jsxName, this.input.slice(start, this.pos)); + } + + // Parse next token as JSX identifier + + jsx_parseIdentifier() { + let node = this.startNode(); + if (this.type === tok.jsxName) + node.name = this.value; + else if (this.type.keyword) + node.name = this.type.keyword; + else + this.unexpected(); + this.next(); + return this.finishNode(node, 'JSXIdentifier'); + } + + // Parse namespaced identifier. + + jsx_parseNamespacedName() { + let startPos = this.start, startLoc = this.startLoc; + let name = this.jsx_parseIdentifier(); + if (!options.allowNamespaces || !this.eat(tt.colon)) return name; + var node = this.startNodeAt(startPos, startLoc); + node.namespace = name; + node.name = this.jsx_parseIdentifier(); + return this.finishNode(node, 'JSXNamespacedName'); + } + + // Parses element name in any form - namespaced, member + // or single identifier. + + jsx_parseElementName() { + if (this.type === tok.jsxTagEnd) return ''; + let startPos = this.start, startLoc = this.startLoc; + let node = this.jsx_parseNamespacedName(); + if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !options.allowNamespacedObjects) { + this.unexpected(); + } + while (this.eat(tt.dot)) { + let newNode = this.startNodeAt(startPos, startLoc); + newNode.object = node; + newNode.property = this.jsx_parseIdentifier(); + node = this.finishNode(newNode, 'JSXMemberExpression'); + } + return node; + } + + // Parses any type of JSX attribute value. + + jsx_parseAttributeValue() { + switch (this.type) { + case tt.braceL: + let node = this.jsx_parseExpressionContainer(); + if (node.expression.type === 'JSXEmptyExpression') + this.raise(node.start, 'JSX attributes must only be assigned a non-empty expression'); + return node; + + case tok.jsxTagStart: + case tt.string: + return this.parseExprAtom(); + + default: + this.raise(this.start, 'JSX value should be either an expression or a quoted JSX text'); + } + } + + // JSXEmptyExpression is unique type since it doesn't actually parse anything, + // and so it should start at the end of last read token (left brace) and finish + // at the beginning of the next one (right brace). + + jsx_parseEmptyExpression() { + let node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc); + return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc); + } + + // Parses JSX expression enclosed into curly brackets. + + jsx_parseExpressionContainer() { + let node = this.startNode(); + this.next(); + node.expression = this.type === tt.braceR + ? this.jsx_parseEmptyExpression() + : this.parseExpression(); + this.expect(tt.braceR); + return this.finishNode(node, 'JSXExpressionContainer'); + } + + // Parses following JSX attribute name-value pair. + + jsx_parseAttribute() { + let node = this.startNode(); + if (this.eat(tt.braceL)) { + this.expect(tt.ellipsis); + node.argument = this.parseMaybeAssign(); + this.expect(tt.braceR); + return this.finishNode(node, 'JSXSpreadAttribute'); + } + node.name = this.jsx_parseNamespacedName(); + node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null; + return this.finishNode(node, 'JSXAttribute'); + } + + // Parses JSX opening tag starting after '<'. + + jsx_parseOpeningElementAt(startPos, startLoc) { + let node = this.startNodeAt(startPos, startLoc); + node.attributes = []; + let nodeName = this.jsx_parseElementName(); + if (nodeName) node.name = nodeName; + while (this.type !== tt.slash && this.type !== tok.jsxTagEnd) + node.attributes.push(this.jsx_parseAttribute()); + node.selfClosing = this.eat(tt.slash); + this.expect(tok.jsxTagEnd); + return this.finishNode(node, nodeName ? 'JSXOpeningElement' : 'JSXOpeningFragment'); + } + + // Parses JSX closing tag starting after ''); + } + } + let fragmentOrElement = openingElement.name ? 'Element' : 'Fragment'; + + node['opening' + fragmentOrElement] = openingElement; + node['closing' + fragmentOrElement] = closingElement; + node.children = children; + if (this.type === tt.relational && this.value === "<") { + this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag"); + } + return this.finishNode(node, 'JSX' + fragmentOrElement); + } + + // Parse JSX text + + jsx_parseText() { + let node = this.parseLiteral(this.value); + node.type = "JSXText"; + return node; + } + + // Parses entire JSX element from current position. + + jsx_parseElement() { + let startPos = this.start, startLoc = this.startLoc; + this.next(); + return this.jsx_parseElementAt(startPos, startLoc); + } + + parseExprAtom(refShortHandDefaultPos) { + if (this.type === tok.jsxText) + return this.jsx_parseText(); + else if (this.type === tok.jsxTagStart) + return this.jsx_parseElement(); + else + return super.parseExprAtom(refShortHandDefaultPos); + } + + readToken(code) { + let context = this.curContext(); + + if (context === tc_expr) return this.jsx_readToken(); + + if (context === tc_oTag || context === tc_cTag) { + if (isIdentifierStart(code)) return this.jsx_readWord(); + + if (code == 62) { + ++this.pos; + return this.finishToken(tok.jsxTagEnd); + } + + if ((code === 34 || code === 39) && context == tc_oTag) + return this.jsx_readString(code); + } + + if (code === 60 && this.exprAllowed && this.input.charCodeAt(this.pos + 1) !== 33) { + ++this.pos; + return this.finishToken(tok.jsxTagStart); + } + return super.readToken(code); + } + + updateContext(prevType) { + if (this.type == tt.braceL) { + 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); + 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 + this.context.push(tc_cTag); // reconsider as closing tag context + this.exprAllowed = false; + } else { + return super.updateContext(prevType); + } + } + }; +} + +module.exports = acornJsx; diff --git a/index.d.cts b/index.d.cts new file mode 100644 index 0000000..0a2803d --- /dev/null +++ b/index.d.cts @@ -0,0 +1,3 @@ +import * as jsx from './index.js'; + +export = jsx; diff --git a/index.d.ts b/index.d.ts index 66ef62f..5a0f90d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,47 +1,49 @@ import * as acorn from 'acorn'; -interface JsxTokTypes extends AcornTokTypes { +export declare class TokContext { + constructor( + token: string, + isExpr: boolean, + preserveSpace: boolean, + override?: (parser: any) => void + ) +} + +export interface JsxTokTypes extends AcornTokTypes { jsxName: acorn.TokenType, jsxText: acorn.TokenType, jsxTagEnd: acorn.TokenType, jsxTagStart: acorn.TokenType } -declare const jsx: { - tokTypes: JsxTokTypes; - (options?: jsx.Options): (BaseParser: typeof acorn.Parser) => jsx.AcornJsxParserCtor -} - -type AcornTokTypes = typeof acorn.tokTypes; - -declare namespace jsx { +export type AcornTokTypes = typeof acorn.tokTypes; - type TokTypes = JsxTokTypes +export type TokTypes = JsxTokTypes - interface Options { +export interface Options { allowNamespacedObjects?: boolean; allowNamespaces?: boolean; - } +} - interface TokContexts { - tc_oTag: acorn.TokContext, - tc_cTag: acorn.TokContext, - tc_expr: acorn.TokContext - } +export interface TokContexts { + tc_oTag: TokContext, + tc_cTag: TokContext, + tc_expr: TokContext +} - // We pick (statics) from acorn rather than plain extending to avoid complaint - // about base constructors needing the same return type (i.e., we return - // `AcornJsxParser` here) - interface AcornJsxParserCtor extends Pick { +// We pick (statics) from acorn rather than plain extending to avoid complaint +// about base constructors needing the same return type (i.e., we return +// `AcornJsxParser` here) +export interface AcornJsxParserCtor extends Pick { readonly acornJsx: { - tokTypes: TokTypes; - tokContexts: TokContexts + tokTypes: TokTypes; + tokContexts: TokContexts }; new (options: acorn.Options, input: string, startPos?: number): AcornJsxParser; - } +} - interface AcornJsxParser extends acorn.Parser { +export interface AcornJsxParser extends acorn.Parser { jsx_readToken(): string; jsx_readNewLine(normalizeCRLF: boolean): void; jsx_readString(quote: number): void; @@ -59,7 +61,16 @@ declare namespace jsx { jsx_parseElementAt(startPos: number, startLoc?: acorn.SourceLocation): acorn.Node; jsx_parseText(): acorn.Node; jsx_parseElement(): acorn.Node; - } } -export = jsx; +export interface JsxFunctionProperties { + tokTypes: JsxTokTypes; +} + +export type JsxFunctionSignature = (options?: Options) => (BaseParser: typeof acorn.Parser) => AcornJsxParserCtor + +export type JsxFunction = JsxFunctionSignature & JsxFunctionProperties; + +declare const jsx: JsxFunction; + +export default jsx; diff --git a/index.js b/index.js index 004e080..b4d3212 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,5 @@ -'use strict'; - -const XHTMLEntities = require('./xhtml'); +import * as acorn from "acorn"; +import XHTMLEntities from './xhtml.js'; const hexNumber = /^[\da-fA-F]+$/; const decimalNumber = /^\d+$/; @@ -70,7 +69,7 @@ function getQualifiedJSXName(object) { getQualifiedJSXName(object.property); } -module.exports = function(options) { +function acornJsx(options) { options = options || {}; return function(Parser) { return plugin({ @@ -82,26 +81,29 @@ module.exports = function(options) { // This is `tokTypes` of the peer dep. // This can be different instances from the actual `tokTypes` this plugin uses. -Object.defineProperty(module.exports, "tokTypes", { +Object.defineProperty(acornJsx, "tokTypes", { get: function get_tokTypes() { - return getJsxTokens(require("acorn")).tokTypes; + return getJsxTokens(acorn).tokTypes; }, configurable: true, enumerable: true }); + +export default acornJsx; + function plugin(options, Parser) { - const acorn = Parser.acorn || require("acorn"); + const acrn = Parser.acorn || acorn; const acornJsx = getJsxTokens(acorn); - const tt = acorn.tokTypes; + const tt = acrn.tokTypes; const tok = acornJsx.tokTypes; - const tokContexts = acorn.tokContexts; + const tokContexts = acrn.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; + const isNewLine = acrn.isNewLine; + const isIdentifierStart = acrn.isIdentifierStart; + const isIdentifierChar = acrn.isIdentifierChar; return class extends Parser { // Expose actual `tokTypes` and `tokContexts` to other plugins. diff --git a/package.json b/package.json index 6debde9..c899924 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,16 @@ "description": "Modern, fast React.js JSX parser", "homepage": "https://github.com/acornjs/acorn-jsx", "version": "5.3.2", + "type": "module", + "main": "index.cjs", + "exports": { + "types": { + "import": "./index.d.ts", + "require": "./index.d.cts" + }, + "import": "./index.js", + "require": "./index.cjs" + }, "maintainers": [ { "name": "Ingvar Stepanyan", @@ -16,12 +26,18 @@ }, "license": "MIT", "scripts": { - "test": "node test/run.js" + "attw": "attw --pack", + "test": "node test/run.js", + "build": "rollup -c", + "prepublishOnly": "npm run build" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "devDependencies": { - "acorn": "^8.0.1" + "@arethetypeswrong/cli": "^0.18.2", + "@rollup/plugin-commonjs": "^12.0.0", + "acorn": "^8.15.0", + "rollup": "^2.12.0" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..c1be435 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,10 @@ +export default { + input: "index.js", + external: ["acorn"], + output: { + file: "index.cjs", + exports: 'default', + format: "cjs", + banner: "// DO NOT edit this file, it's auto-generated from 'rollup.config.js'; any changes will be overwritten. \n" + } +}; diff --git a/test/driver.js b/test/driver.js index 3e152c1..a0b33f1 100644 --- a/test/driver.js +++ b/test/driver.js @@ -1,16 +1,16 @@ var tests = []; -exports.test = function(code, ast, options, pluginOptions) { +export function test(code, ast, options, pluginOptions) { tests.push({code, ast, options, pluginOptions}); }; -exports.testFail = function(code, message, options, pluginOptions) { +export function testFail(code, message, options, pluginOptions) { tests.push({code, error: message, options, pluginOptions}); }; -exports.testAssert = function(code, assert, options) { +export function testAssert(code, assert, options) { tests.push({code, assert, options}); }; -exports.runTests = function(config, callback) { +export function runTests(config, callback) { var parse = config.parse; for (var i = 0; i < tests.length; ++i) { @@ -72,7 +72,7 @@ function addPath(str, pt) { return str + " (" + pt + ")"; } -var misMatch = exports.misMatch = function(exp, act) { +var misMatch = function(exp, act) { if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) { if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act); } else if (exp instanceof RegExp || act instanceof RegExp) { diff --git a/test/run.js b/test/run.js index b9d98bf..e462920 100644 --- a/test/run.js +++ b/test/run.js @@ -1,6 +1,8 @@ -var driver = require("./driver.js"); -require("./tests-jsx.js"); -require("./tests-misc.js"); +import * as driver from "./driver.js"; +import "./tests-jsx.js"; +import "./tests-misc.js"; +import * as acorn from "acorn"; +import jsx from "../index.js"; function group(name) { if (typeof console === "object" && console.group) { @@ -18,7 +20,6 @@ function log(title, message) { if (typeof console === "object") console.log(title, message); } -const acorn = require("acorn"), jsx = require("..") const Parser = acorn.Parser.extend(jsx()) var stats, modes = { diff --git a/test/tests-jsx.js b/test/tests-jsx.js index b53549d..072b44d 100644 --- a/test/tests-jsx.js +++ b/test/tests-jsx.js @@ -20,6 +20,13 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import { test } from "./driver.js"; +import { testFail } from "./driver.js"; +import acornJsx from "../index.js"; +import { tokTypes as acornTokens } from "acorn"; + +let jsxTokens = acornJsx.tokTypes; + var fbTestFixture = { // Taken and adapted from esprima-fb/fbtest.js. 'JSX': { @@ -3725,13 +3732,6 @@ var fbTestFixture = { } }; -if (typeof exports !== "undefined") { - var test = require("./driver.js").test; - var testFail = require("./driver.js").testFail; - var jsxTokens = require("..").tokTypes; - var acornTokens = require("acorn").tokTypes; -} - testFail("var x =
one
two
;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)"); testFail("", "Unexpected token (1:4)"); diff --git a/test/tests-misc.js b/test/tests-misc.js index 9ff0fa3..500d3df 100644 --- a/test/tests-misc.js +++ b/test/tests-misc.js @@ -1,11 +1,7 @@ -"use strict"; - -if (typeof exports !== "undefined") { - var assert = require("assert"); - var acorn = require("acorn"); - var jsx = require(".."); - var testAssert = require("./driver.js").testAssert; -} +import assert from "assert"; +import * as acorn from "acorn"; +import jsx from "../index.js"; +import { testAssert } from "./driver.js"; testAssert("// the enhanced Parser instance should have a static property 'acornJsx'.", function() { const JsxParser = acorn.Parser.extend(jsx()); diff --git a/xhtml.js b/xhtml.js index c152009..8f5aa79 100644 --- a/xhtml.js +++ b/xhtml.js @@ -1,4 +1,4 @@ -module.exports = { +export default { quot: '\u0022', amp: '&', apos: '\u0027',