You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

8941 lines
292 KiB

4 years ago
  1. /* Riot Compiler WIP, @license MIT */
  2. 'use strict';
  3. Object.defineProperty(exports, '__esModule', { value: true });
  4. function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  5. var recast = require('recast');
  6. var util = require('recast/lib/util');
  7. var sourceMap = require('source-map');
  8. var cssEscape = _interopDefault(require('cssesc'));
  9. var globalScope = _interopDefault(require('globals'));
  10. const TAG_LOGIC_PROPERTY = 'exports';
  11. const TAG_CSS_PROPERTY = 'css';
  12. const TAG_TEMPLATE_PROPERTY = 'template';
  13. const TAG_NAME_PROPERTY = 'name';
  14. const types = recast.types;
  15. const builders = recast.types.builders;
  16. const namedTypes = recast.types.namedTypes;
  17. function nullNode() {
  18. return builders.literal(null)
  19. }
  20. function simplePropertyNode(key, value) {
  21. return builders.property('init', builders.literal(key), value, false)
  22. }
  23. /**
  24. * Return a source map as JSON, it it has not the toJSON method it means it can
  25. * be used right the way
  26. * @param { SourceMapGenerator|Object } map - a sourcemap generator or simply an json object
  27. * @returns { Object } the source map as JSON
  28. */
  29. function sourcemapAsJSON(map) {
  30. if (map && map.toJSON) return map.toJSON()
  31. return map
  32. }
  33. /**
  34. * Quick type checking
  35. * @param {*} element - anything
  36. * @param {string} type - type definition
  37. * @returns {boolean} true if the type corresponds
  38. */
  39. /**
  40. * Detect node js environements
  41. * @returns {boolean} true if the runtime is node
  42. */
  43. function isNode() {
  44. return typeof process !== 'undefined'
  45. }
  46. /**
  47. * Compose two sourcemaps
  48. * @param { SourceMapGenerator } formerMap - original sourcemap
  49. * @param { SourceMapGenerator } latterMap - target sourcemap
  50. * @returns { Object } sourcemap json
  51. */
  52. function composeSourcemaps(formerMap, latterMap) {
  53. if (
  54. isNode() &&
  55. formerMap && latterMap && latterMap.mappings
  56. ) {
  57. return util.composeSourceMaps(sourcemapAsJSON(formerMap), sourcemapAsJSON(latterMap))
  58. } else if (isNode() && formerMap) {
  59. return sourcemapAsJSON(formerMap)
  60. }
  61. return {}
  62. }
  63. /**
  64. * Create a new sourcemap generator
  65. * @param { Object } options - sourcemap options
  66. * @returns { SourceMapGenerator } SourceMapGenerator instance
  67. */
  68. function createSourcemap(options) {
  69. return new sourceMap.SourceMapGenerator(options)
  70. }
  71. const Output = Object.freeze({
  72. code: '',
  73. ast: [],
  74. meta: {},
  75. map: null
  76. });
  77. /**
  78. * Create the right output data result of a parsing
  79. * @param { Object } data - output data
  80. * @param { string } data.code - code generated
  81. * @param { AST } data.ast - ast representing the code
  82. * @param { SourceMapGenerator } data.map - source map generated along with the code
  83. * @param { Object } meta - compilation meta infomration
  84. * @returns { Output } output container object
  85. */
  86. function createOutput(data, meta) {
  87. const output = {
  88. ...Output,
  89. ...data,
  90. meta
  91. };
  92. if (!output.map && meta && meta.options && meta.options.file)
  93. return {
  94. ...output,
  95. map: createSourcemap({ file: meta.options.file })
  96. }
  97. return output
  98. }
  99. /**
  100. * Transform the source code received via a compiler function
  101. * @param { Function } compiler - function needed to generate the output code
  102. * @param { Object } meta - compilation meta information
  103. * @param { string } source - source code
  104. * @returns { Output } output - the result of the compiler
  105. */
  106. function transform(compiler, meta, source) {
  107. const result = (compiler ? compiler(source, meta) : { code: source });
  108. return createOutput(result, meta)
  109. }
  110. /**
  111. * Throw an error with a descriptive message
  112. * @param { string } message - error message
  113. * @returns { undefined } hoppla.. at this point the program should stop working
  114. */
  115. function panic(message) {
  116. throw new Error(message)
  117. }
  118. const postprocessors = new Set();
  119. /**
  120. * Register a postprocessor that will be used after the parsing and compilation of the riot tags
  121. * @param { Function } postprocessor - transformer that will receive the output code ans sourcemap
  122. * @returns { Set } the postprocessors collection
  123. */
  124. function register(postprocessor) {
  125. if (postprocessors.has(postprocessor)) {
  126. panic(`This postprocessor "${postprocessor.name || postprocessor.toString()}" was already registered`);
  127. }
  128. postprocessors.add(postprocessor);
  129. return postprocessors
  130. }
  131. /**
  132. * Exec all the postprocessors in sequence combining the sourcemaps generated
  133. * @param { Output } compilerOutput - output generated by the compiler
  134. * @param { Object } meta - compiling meta information
  135. * @returns { Output } object containing output code and source map
  136. */
  137. function execute(compilerOutput, meta) {
  138. return Array.from(postprocessors).reduce(function(acc, postprocessor) {
  139. const { code, map } = acc;
  140. const output = postprocessor(code, meta);
  141. return {
  142. code: output.code,
  143. map: composeSourcemaps(map, output.map)
  144. }
  145. }, createOutput(compilerOutput, meta))
  146. }
  147. /**
  148. * Parsers that can be registered by users to preparse components fragments
  149. * @type { Object }
  150. */
  151. const preprocessors = Object.freeze({
  152. javascript: new Map(),
  153. css: new Map(),
  154. template: new Map().set('default', code => ({ code }))
  155. });
  156. // throw a processor type error
  157. function preprocessorTypeError(type) {
  158. panic(`No preprocessor of type "${type}" was found, please make sure to use one of these: 'javascript', 'css' or 'template'`);
  159. }
  160. // throw an error if the preprocessor was not registered
  161. function preprocessorNameNotFoundError(name) {
  162. panic(`No preprocessor named "${name}" was found, are you sure you have registered it?'`);
  163. }
  164. /**
  165. * Register a custom preprocessor
  166. * @param { string } type - preprocessor type either 'js', 'css' or 'template'
  167. * @param { string } name - unique preprocessor id
  168. * @param { Function } preprocessor - preprocessor function
  169. * @returns { Map } - the preprocessors map
  170. */
  171. function register$1(type, name, preprocessor) {
  172. if (!type) panic('Please define the type of preprocessor you want to register \'javascript\', \'css\' or \'template\'');
  173. if (!name) panic('Please define a name for your preprocessor');
  174. if (!preprocessor) panic('Please provide a preprocessor function');
  175. if (!preprocessors[type]) preprocessorTypeError(type);
  176. if (preprocessors[type].has(name)) panic(`The preprocessor ${name} was already registered before`);
  177. preprocessors[type].set(name, preprocessor);
  178. return preprocessors
  179. }
  180. /**
  181. * Exec the compilation of a preprocessor
  182. * @param { string } type - preprocessor type either 'js', 'css' or 'template'
  183. * @param { string } name - unique preprocessor id
  184. * @param { Object } meta - preprocessor meta information
  185. * @param { string } source - source code
  186. * @returns { Output } object containing a sourcemap and a code string
  187. */
  188. function execute$1(type, name, meta, source) {
  189. if (!preprocessors[type]) preprocessorTypeError(type);
  190. if (!preprocessors[type].has(name)) preprocessorNameNotFoundError(name);
  191. return transform(preprocessors[type].get(name), meta, source)
  192. }
  193. /**
  194. * Similar to compose but performs from left-to-right function composition.<br/>
  195. * {@link https://30secondsofcode.org/function#composeright see also}
  196. * @param {...[function]} fns) - list of unary function
  197. * @returns {*} result of the computation
  198. */
  199. /**
  200. * Performs right-to-left function composition.<br/>
  201. * Use Array.prototype.reduce() to perform right-to-left function composition.<br/>
  202. * The last (rightmost) function can accept one or more arguments; the remaining functions must be unary.<br/>
  203. * {@link https://30secondsofcode.org/function#compose original source code}
  204. * @param {...[function]} fns) - list of unary function
  205. * @returns {*} result of the computation
  206. */
  207. function compose(...fns) {
  208. return fns.reduce((f, g) => (...args) => f(g(...args)))
  209. }
  210. const ATTRIBUTE_TYPE_NAME = 'type';
  211. /**
  212. * Get the type attribute from a node generated by the riot parser
  213. * @param { Object} sourceNode - riot parser node
  214. * @returns { string|null } a valid type to identify the preprocessor to use or nothing
  215. */
  216. function getPreprocessorTypeByAttribute(sourceNode) {
  217. const typeAttribute = sourceNode.attributes ?
  218. sourceNode.attributes.find(attribute => attribute.name === ATTRIBUTE_TYPE_NAME) :
  219. null;
  220. return typeAttribute ? normalize(typeAttribute.value) : null
  221. }
  222. /**
  223. * Remove the noise in case a user has defined the preprocessor type='text/scss'
  224. * @param { string } value - input string
  225. * @returns { string } normalized string
  226. */
  227. function normalize(value) {
  228. return value.replace('text/', '')
  229. }
  230. /**
  231. * Preprocess a riot parser node
  232. * @param { string } preprocessorType - either css, js
  233. * @param { string } preprocessorName - preprocessor id
  234. * @param { Object } meta - compilation meta information
  235. * @param { RiotParser.nodeTypes } node - css node detected by the parser
  236. * @returns { Output } code and sourcemap generated by the preprocessor
  237. */
  238. function preprocess(preprocessorType, preprocessorName, meta, node) {
  239. const code = node.text;
  240. return (preprocessorName ?
  241. execute$1(preprocessorType, preprocessorName, meta, code) :
  242. { code }
  243. )
  244. }
  245. /**
  246. * Matches valid, multiline JavaScript comments in almost all its forms.
  247. * @const {RegExp}
  248. * @static
  249. */
  250. const R_MLCOMMS = /\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//g;
  251. /**
  252. * Source for creating regexes matching valid quoted, single-line JavaScript strings.
  253. * It recognizes escape characters, including nested quotes and line continuation.
  254. * @const {string}
  255. */
  256. const S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source;
  257. /**
  258. * Matches CSS selectors, excluding those beginning with '@' and quoted strings.
  259. * @const {RegExp}
  260. */
  261. const CSS_SELECTOR = RegExp(`([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|${S_LINESTR}`, 'g');
  262. /**
  263. * Parses styles enclosed in a "scoped" tag
  264. * The "css" string is received without comments or surrounding spaces.
  265. *
  266. * @param {string} tag - Tag name of the root element
  267. * @param {string} css - The CSS code
  268. * @returns {string} CSS with the styles scoped to the root element
  269. */
  270. function scopedCSS(tag, css) {
  271. const host = ':host';
  272. const selectorsBlacklist = ['from', 'to'];
  273. return css.replace(CSS_SELECTOR, function(m, p1, p2) {
  274. // skip quoted strings
  275. if (!p2) return m
  276. // we have a selector list, parse each individually
  277. p2 = p2.replace(/[^,]+/g, function(sel) {
  278. const s = sel.trim();
  279. // skip selectors already using the tag name
  280. if (s.indexOf(tag) === 0) {
  281. return sel
  282. }
  283. // skips the keywords and percents of css animations
  284. if (!s || selectorsBlacklist.indexOf(s) > -1 || s.slice(-1) === '%') {
  285. return sel
  286. }
  287. // replace the `:host` pseudo-selector, where it is, with the root tag name;
  288. // if `:host` was not included, add the tag name as prefix, and mirror all
  289. // `[data-is]`
  290. if (s.indexOf(host) < 0) {
  291. return `${tag} ${s},[is="${tag}"] ${s}`
  292. } else {
  293. return `${s.replace(host, tag)},${
  294. s.replace(host, `[is="${tag}"]`)}`
  295. }
  296. });
  297. // add the danling bracket char and return the processed selector list
  298. return p1 ? `${p1} ${p2}` : p2
  299. })
  300. }
  301. /**
  302. * Remove comments, compact and trim whitespace
  303. * @param { string } code - compiled css code
  304. * @returns { string } css code normalized
  305. */
  306. function compactCss(code) {
  307. return code.replace(R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
  308. }
  309. const escapeBackslashes = s => s.replace(/\\/g, '\\\\');
  310. const escapeIdentifier = identifier => escapeBackslashes(cssEscape(identifier, {
  311. isIdentifier: true
  312. }));
  313. /**
  314. * Generate the component css
  315. * @param { Object } sourceNode - node generated by the riot compiler
  316. * @param { string } source - original component source code
  317. * @param { Object } meta - compilation meta information
  318. * @param { AST } ast - current AST output
  319. * @returns { AST } the AST generated
  320. */
  321. function css(sourceNode, source, meta, ast) {
  322. const preprocessorName = getPreprocessorTypeByAttribute(sourceNode);
  323. const { options } = meta;
  324. const preprocessorOutput = preprocess('css', preprocessorName, meta, sourceNode.text);
  325. const normalizedCssCode = compactCss(preprocessorOutput.code);
  326. const escapedCssIdentifier = escapeIdentifier(meta.tagName);
  327. const cssCode = (options.scopedCss ?
  328. scopedCSS(escapedCssIdentifier, escapeBackslashes(normalizedCssCode)) :
  329. escapeBackslashes(normalizedCssCode)
  330. ).trim();
  331. types.visit(ast, {
  332. visitProperty(path) {
  333. if (path.value.key.value === TAG_CSS_PROPERTY) {
  334. path.value.value = builders.templateLiteral(
  335. [builders.templateElement({ raw: cssCode, cooked: '' }, false)],
  336. []
  337. );
  338. return false
  339. }
  340. this.traverse(path);
  341. }
  342. });
  343. return ast
  344. }
  345. /**
  346. * Function to curry any javascript method
  347. * @param {Function} fn - the target function we want to curry
  348. * @param {...[args]} acc - initial arguments
  349. * @returns {Function|*} it will return a function until the target function
  350. * will receive all of its arguments
  351. */
  352. function curry(fn, ...acc) {
  353. return (...args) => {
  354. args = [...acc, ...args];
  355. return args.length < fn.length ?
  356. curry(fn, ...args) :
  357. fn(...args)
  358. }
  359. }
  360. /**
  361. * Generate the javascript from an ast source
  362. * @param {AST} ast - ast object
  363. * @param {Object} options - printer options
  364. * @returns {Object} code + map
  365. */
  366. function generateJavascript(ast, options) {
  367. return recast.print(ast, {
  368. ...options,
  369. tabWidth: 2,
  370. quote: 'single'
  371. })
  372. }
  373. /**
  374. * True if the sourcemap has no mappings, it is empty
  375. * @param {Object} map - sourcemap json
  376. * @returns {boolean} true if empty
  377. */
  378. function isEmptySourcemap(map) {
  379. return !map || !map.mappings || !map.mappings.length
  380. }
  381. const LINES_RE = /\r\n?|\n/g;
  382. /**
  383. * Split a string into a rows array generated from its EOL matches
  384. * @param { string } string [description]
  385. * @returns { Array } array containing all the string rows
  386. */
  387. function splitStringByEOL(string) {
  388. return string.split(LINES_RE)
  389. }
  390. /**
  391. * Get the line and the column of a source text based on its position in the string
  392. * @param { string } string - target string
  393. * @param { number } position - target position
  394. * @returns { Object } object containing the source text line and column
  395. */
  396. function getLineAndColumnByPosition(string, position) {
  397. const lines = splitStringByEOL(string.slice(0, position));
  398. return {
  399. line: lines.length,
  400. column: lines[lines.length - 1].length
  401. }
  402. }
  403. /**
  404. * Add the offset to the code that must be parsed in order to generate properly the sourcemaps
  405. * @param {string} input - input string
  406. * @param {string} source - original source code
  407. * @param {RiotParser.Node} node - node that we are going to transform
  408. * @return {string} the input string with the offset properly set
  409. */
  410. function addLineOffset(input, source, node) {
  411. const {column, line} = getLineAndColumnByPosition(source, node.start);
  412. return `${'\n'.repeat(line - 1)}${' '.repeat(column + 1)}${input}`
  413. }
  414. // Reserved word lists for various dialects of the language
  415. var reservedWords = {
  416. 3: "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile",
  417. 5: "class enum extends super const export import",
  418. 6: "enum",
  419. strict: "implements interface let package private protected public static yield",
  420. strictBind: "eval arguments"
  421. };
  422. // And the keywords
  423. var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
  424. var keywords = {
  425. 5: ecma5AndLessKeywords,
  426. "5module": ecma5AndLessKeywords + " export import",
  427. 6: ecma5AndLessKeywords + " const class extends export import super"
  428. };
  429. var keywordRelationalOperator = /^in(stanceof)?$/;
  430. // ## Character categories
  431. // Big ugly regular expressions that match characters in the
  432. // whitespace, identifier, and identifier-start categories. These
  433. // are only applied when a character is found to actually have a
  434. // code point above 128.
  435. // Generated by `bin/generate-identifier-regex.js`.
  436. var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-
  437. var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
  438. var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
  439. var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
  440. nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
  441. // These are a run-length and offset encoded representation of the
  442. // >0xffff code points that are a valid part of identifiers. The
  443. // offset starts at 0x10000, and each pair of numbers represents an
  444. // offset to the next range, and then a size of the range. They were
  445. // generated by bin/generate-identifier-regex.js
  446. // eslint-disable-next-line comma-spacing
  447. var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,477,28,11,0,9,21,155,22,13,52,76,44,33,24,27,35,30,0,12,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,0,33,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,230,43,117,63,32,0,161,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,35,56,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,270,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,754,9486,286,50,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,2357,44,11,6,17,0,370,43,1301,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,15,7472,3104,541];
  448. // eslint-disable-next-line comma-spacing
  449. var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,525,10,176,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,4,9,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,232,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,135,4,60,6,26,9,1014,0,2,54,8,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,419,13,1495,6,110,6,6,9,792487,239];
  450. // This has a complexity linear to the value of the code. The
  451. // assumption is that looking up astral identifier characters is
  452. // rare.
  453. function isInAstralSet(code, set) {
  454. var pos = 0x10000;
  455. for (var i = 0; i < set.length; i += 2) {
  456. pos += set[i];
  457. if (pos > code) { return false }
  458. pos += set[i + 1];
  459. if (pos >= code) { return true }
  460. }
  461. }
  462. // Test whether a given character code starts an identifier.
  463. function isIdentifierStart(code, astral) {
  464. if (code < 65) { return code === 36 }
  465. if (code < 91) { return true }
  466. if (code < 97) { return code === 95 }
  467. if (code < 123) { return true }
  468. if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }
  469. if (astral === false) { return false }
  470. return isInAstralSet(code, astralIdentifierStartCodes)
  471. }
  472. // Test whether a given character is part of an identifier.
  473. function isIdentifierChar(code, astral) {
  474. if (code < 48) { return code === 36 }
  475. if (code < 58) { return true }
  476. if (code < 65) { return false }
  477. if (code < 91) { return true }
  478. if (code < 97) { return code === 95 }
  479. if (code < 123) { return true }
  480. if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }
  481. if (astral === false) { return false }
  482. return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
  483. }
  484. // ## Token types
  485. // The assignment of fine-grained, information-carrying type objects
  486. // allows the tokenizer to store the information it has about a
  487. // token in a way that is very cheap for the parser to look up.
  488. // All token type variables start with an underscore, to make them
  489. // easy to recognize.
  490. // The `beforeExpr` property is used to disambiguate between regular
  491. // expressions and divisions. It is set on all token types that can
  492. // be followed by an expression (thus, a slash after them would be a
  493. // regular expression).
  494. //
  495. // The `startsExpr` property is used to check if the token ends a
  496. // `yield` expression. It is set on all token types that either can
  497. // directly start an expression (like a quotation mark) or can
  498. // continue an expression (like the body of a string).
  499. //
  500. // `isLoop` marks a keyword as starting a loop, which is important
  501. // to know when parsing a label, in order to allow or disallow
  502. // continue jumps to that label.
  503. var TokenType = function TokenType(label, conf) {
  504. if ( conf === void 0 ) conf = {};
  505. this.label = label;
  506. this.keyword = conf.keyword;
  507. this.beforeExpr = !!conf.beforeExpr;
  508. this.startsExpr = !!conf.startsExpr;
  509. this.isLoop = !!conf.isLoop;
  510. this.isAssign = !!conf.isAssign;
  511. this.prefix = !!conf.prefix;
  512. this.postfix = !!conf.postfix;
  513. this.binop = conf.binop || null;
  514. this.updateContext = null;
  515. };
  516. function binop(name, prec) {
  517. return new TokenType(name, {beforeExpr: true, binop: prec})
  518. }
  519. var beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};
  520. // Map keyword names to token types.
  521. var keywords$1 = {};
  522. // Succinct definitions of keyword token types
  523. function kw(name, options) {
  524. if ( options === void 0 ) options = {};
  525. options.keyword = name;
  526. return keywords$1[name] = new TokenType(name, options)
  527. }
  528. var types$1 = {
  529. num: new TokenType("num", startsExpr),
  530. regexp: new TokenType("regexp", startsExpr),
  531. string: new TokenType("string", startsExpr),
  532. name: new TokenType("name", startsExpr),
  533. eof: new TokenType("eof"),
  534. // Punctuation token types.
  535. bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
  536. bracketR: new TokenType("]"),
  537. braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
  538. braceR: new TokenType("}"),
  539. parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
  540. parenR: new TokenType(")"),
  541. comma: new TokenType(",", beforeExpr),
  542. semi: new TokenType(";", beforeExpr),
  543. colon: new TokenType(":", beforeExpr),
  544. dot: new TokenType("."),
  545. question: new TokenType("?", beforeExpr),
  546. arrow: new TokenType("=>", beforeExpr),
  547. template: new TokenType("template"),
  548. invalidTemplate: new TokenType("invalidTemplate"),
  549. ellipsis: new TokenType("...", beforeExpr),
  550. backQuote: new TokenType("`", startsExpr),
  551. dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),
  552. // Operators. These carry several kinds of properties to help the
  553. // parser use them properly (the presence of these properties is
  554. // what categorizes them as operators).
  555. //
  556. // `binop`, when present, specifies that this operator is a binary
  557. // operator, and will refer to its precedence.
  558. //
  559. // `prefix` and `postfix` mark the operator as a prefix or postfix
  560. // unary operator.
  561. //
  562. // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  563. // binary operators with a very low precedence, that should result
  564. // in AssignmentExpression nodes.
  565. eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
  566. assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
  567. incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
  568. prefix: new TokenType("!/~", {beforeExpr: true, prefix: true, startsExpr: true}),
  569. logicalOR: binop("||", 1),
  570. logicalAND: binop("&&", 2),
  571. bitwiseOR: binop("|", 3),
  572. bitwiseXOR: binop("^", 4),
  573. bitwiseAND: binop("&", 5),
  574. equality: binop("==/!=/===/!==", 6),
  575. relational: binop("</>/<=/>=", 7),
  576. bitShift: binop("<</>>/>>>", 8),
  577. plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
  578. modulo: binop("%", 10),
  579. star: binop("*", 10),
  580. slash: binop("/", 10),
  581. starstar: new TokenType("**", {beforeExpr: true}),
  582. // Keyword token types.
  583. _break: kw("break"),
  584. _case: kw("case", beforeExpr),
  585. _catch: kw("catch"),
  586. _continue: kw("continue"),
  587. _debugger: kw("debugger"),
  588. _default: kw("default", beforeExpr),
  589. _do: kw("do", {isLoop: true, beforeExpr: true}),
  590. _else: kw("else", beforeExpr),
  591. _finally: kw("finally"),
  592. _for: kw("for", {isLoop: true}),
  593. _function: kw("function", startsExpr),
  594. _if: kw("if"),
  595. _return: kw("return", beforeExpr),
  596. _switch: kw("switch"),
  597. _throw: kw("throw", beforeExpr),
  598. _try: kw("try"),
  599. _var: kw("var"),
  600. _const: kw("const"),
  601. _while: kw("while", {isLoop: true}),
  602. _with: kw("with"),
  603. _new: kw("new", {beforeExpr: true, startsExpr: true}),
  604. _this: kw("this", startsExpr),
  605. _super: kw("super", startsExpr),
  606. _class: kw("class", startsExpr),
  607. _extends: kw("extends", beforeExpr),
  608. _export: kw("export"),
  609. _import: kw("import", startsExpr),
  610. _null: kw("null", startsExpr),
  611. _true: kw("true", startsExpr),
  612. _false: kw("false", startsExpr),
  613. _in: kw("in", {beforeExpr: true, binop: 7}),
  614. _instanceof: kw("instanceof", {beforeExpr: true, binop: 7}),
  615. _typeof: kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true}),
  616. _void: kw("void", {beforeExpr: true, prefix: true, startsExpr: true}),
  617. _delete: kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
  618. };
  619. // Matches a whole line break (where CRLF is considered a single
  620. // line break). Used to count lines.
  621. var lineBreak = /\r\n?|\n|\u2028|\u2029/;
  622. var lineBreakG = new RegExp(lineBreak.source, "g");
  623. function isNewLine(code, ecma2019String) {
  624. return code === 10 || code === 13 || (!ecma2019String && (code === 0x2028 || code === 0x2029))
  625. }
  626. var nonASCIIwhitespace = /[\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
  627. var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
  628. var ref = Object.prototype;
  629. var hasOwnProperty = ref.hasOwnProperty;
  630. var toString = ref.toString;
  631. // Checks if an object has a property.
  632. function has(obj, propName) {
  633. return hasOwnProperty.call(obj, propName)
  634. }
  635. var isArray = Array.isArray || (function (obj) { return (
  636. toString.call(obj) === "[object Array]"
  637. ); });
  638. function wordsRegexp(words) {
  639. return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$")
  640. }
  641. // These are used when `options.locations` is on, for the
  642. // `startLoc` and `endLoc` properties.
  643. var Position = function Position(line, col) {
  644. this.line = line;
  645. this.column = col;
  646. };
  647. Position.prototype.offset = function offset (n) {
  648. return new Position(this.line, this.column + n)
  649. };
  650. var SourceLocation = function SourceLocation(p, start, end) {
  651. this.start = start;
  652. this.end = end;
  653. if (p.sourceFile !== null) { this.source = p.sourceFile; }
  654. };
  655. // The `getLineInfo` function is mostly useful when the
  656. // `locations` option is off (for performance reasons) and you
  657. // want to find the line/column position for a given character
  658. // offset. `input` should be the code string that the offset refers
  659. // into.
  660. function getLineInfo(input, offset) {
  661. for (var line = 1, cur = 0;;) {
  662. lineBreakG.lastIndex = cur;
  663. var match = lineBreakG.exec(input);
  664. if (match && match.index < offset) {
  665. ++line;
  666. cur = match.index + match[0].length;
  667. } else {
  668. return new Position(line, offset - cur)
  669. }
  670. }
  671. }
  672. // A second optional argument can be given to further configure
  673. // the parser process. These options are recognized:
  674. var defaultOptions = {
  675. // `ecmaVersion` indicates the ECMAScript version to parse. Must be
  676. // either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018), or 10
  677. // (2019). This influences support for strict mode, the set of
  678. // reserved words, and support for new syntax features. The default
  679. // is 10.
  680. ecmaVersion: 10,
  681. // `sourceType` indicates the mode the code should be parsed in.
  682. // Can be either `"script"` or `"module"`. This influences global
  683. // strict mode and parsing of `import` and `export` declarations.
  684. sourceType: "script",
  685. // `onInsertedSemicolon` can be a callback that will be called
  686. // when a semicolon is automatically inserted. It will be passed
  687. // the position of the comma as an offset, and if `locations` is
  688. // enabled, it is given the location as a `{line, column}` object
  689. // as second argument.
  690. onInsertedSemicolon: null,
  691. // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
  692. // trailing commas.
  693. onTrailingComma: null,
  694. // By default, reserved words are only enforced if ecmaVersion >= 5.
  695. // Set `allowReserved` to a boolean value to explicitly turn this on
  696. // an off. When this option has the value "never", reserved words
  697. // and keywords can also not be used as property names.
  698. allowReserved: null,
  699. // When enabled, a return at the top level is not considered an
  700. // error.
  701. allowReturnOutsideFunction: false,
  702. // When enabled, import/export statements are not constrained to
  703. // appearing at the top of the program.
  704. allowImportExportEverywhere: false,
  705. // When enabled, await identifiers are allowed to appear at the top-level scope,
  706. // but they are still not allowed in non-async functions.
  707. allowAwaitOutsideFunction: false,
  708. // When enabled, hashbang directive in the beginning of file
  709. // is allowed and treated as a line comment.
  710. allowHashBang: false,
  711. // When `locations` is on, `loc` properties holding objects with
  712. // `start` and `end` properties in `{line, column}` form (with
  713. // line being 1-based and column 0-based) will be attached to the
  714. // nodes.
  715. locations: false,
  716. // A function can be passed as `onToken` option, which will
  717. // cause Acorn to call that function with object in the same
  718. // format as tokens returned from `tokenizer().getToken()`. Note
  719. // that you are not allowed to call the parser from the
  720. // callback—that will corrupt its internal state.
  721. onToken: null,
  722. // A function can be passed as `onComment` option, which will
  723. // cause Acorn to call that function with `(block, text, start,
  724. // end)` parameters whenever a comment is skipped. `block` is a
  725. // boolean indicating whether this is a block (`/* */`) comment,
  726. // `text` is the content of the comment, and `start` and `end` are
  727. // character offsets that denote the start and end of the comment.
  728. // When the `locations` option is on, two more parameters are
  729. // passed, the full `{line, column}` locations of the start and
  730. // end of the comments. Note that you are not allowed to call the
  731. // parser from the callback—that will corrupt its internal state.
  732. onComment: null,
  733. // Nodes have their start and end characters offsets recorded in
  734. // `start` and `end` properties (directly on the node, rather than
  735. // the `loc` object, which holds line/column data. To also add a
  736. // [semi-standardized][range] `range` property holding a `[start,
  737. // end]` array with the same numbers, set the `ranges` option to
  738. // `true`.
  739. //
  740. // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
  741. ranges: false,
  742. // It is possible to parse multiple files into a single AST by
  743. // passing the tree produced by parsing the first file as
  744. // `program` option in subsequent parses. This will add the
  745. // toplevel forms of the parsed file to the `Program` (top) node
  746. // of an existing parse tree.
  747. program: null,
  748. // When `locations` is on, you can pass this to record the source
  749. // file in every node's `loc` object.
  750. sourceFile: null,
  751. // This value, if given, is stored in every node, whether
  752. // `locations` is on or off.
  753. directSourceFile: null,
  754. // When enabled, parenthesized expressions are represented by
  755. // (non-standard) ParenthesizedExpression nodes
  756. preserveParens: false
  757. };
  758. // Interpret and default an options object
  759. function getOptions(opts) {
  760. var options = {};
  761. for (var opt in defaultOptions)
  762. { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; }
  763. if (options.ecmaVersion >= 2015)
  764. { options.ecmaVersion -= 2009; }
  765. if (options.allowReserved == null)
  766. { options.allowReserved = options.ecmaVersion < 5; }
  767. if (isArray(options.onToken)) {
  768. var tokens = options.onToken;
  769. options.onToken = function (token) { return tokens.push(token); };
  770. }
  771. if (isArray(options.onComment))
  772. { options.onComment = pushComment(options, options.onComment); }
  773. return options
  774. }
  775. function pushComment(options, array) {
  776. return function(block, text, start, end, startLoc, endLoc) {
  777. var comment = {
  778. type: block ? "Block" : "Line",
  779. value: text,
  780. start: start,
  781. end: end
  782. };
  783. if (options.locations)
  784. { comment.loc = new SourceLocation(this, startLoc, endLoc); }
  785. if (options.ranges)
  786. { comment.range = [start, end]; }
  787. array.push(comment);
  788. }
  789. }
  790. // Each scope gets a bitset that may contain these flags
  791. var
  792. SCOPE_TOP = 1,
  793. SCOPE_FUNCTION = 2,
  794. SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION,
  795. SCOPE_ASYNC = 4,
  796. SCOPE_GENERATOR = 8,
  797. SCOPE_ARROW = 16,
  798. SCOPE_SIMPLE_CATCH = 32,
  799. SCOPE_SUPER = 64,
  800. SCOPE_DIRECT_SUPER = 128;
  801. function functionFlags(async, generator) {
  802. return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)
  803. }
  804. // Used in checkLVal and declareName to determine the type of a binding
  805. var
  806. BIND_NONE = 0, // Not a binding
  807. BIND_VAR = 1, // Var-style binding
  808. BIND_LEXICAL = 2, // Let- or const-style binding
  809. BIND_FUNCTION = 3, // Function declaration
  810. BIND_SIMPLE_CATCH = 4, // Simple (identifier pattern) catch binding
  811. BIND_OUTSIDE = 5; // Special case for function names as bound inside the function
  812. var Parser = function Parser(options, input, startPos) {
  813. this.options = options = getOptions(options);
  814. this.sourceFile = options.sourceFile;
  815. this.keywords = wordsRegexp(keywords[options.ecmaVersion >= 6 ? 6 : options.sourceType === "module" ? "5module" : 5]);
  816. var reserved = "";
  817. if (options.allowReserved !== true) {
  818. for (var v = options.ecmaVersion;; v--)
  819. { if (reserved = reservedWords[v]) { break } }
  820. if (options.sourceType === "module") { reserved += " await"; }
  821. }
  822. this.reservedWords = wordsRegexp(reserved);
  823. var reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict;
  824. this.reservedWordsStrict = wordsRegexp(reservedStrict);
  825. this.reservedWordsStrictBind = wordsRegexp(reservedStrict + " " + reservedWords.strictBind);
  826. this.input = String(input);
  827. // Used to signal to callers of `readWord1` whether the word
  828. // contained any escape sequences. This is needed because words with
  829. // escape sequences must not be interpreted as keywords.
  830. this.containsEsc = false;
  831. // Set up token state
  832. // The current position of the tokenizer in the input.
  833. if (startPos) {
  834. this.pos = startPos;
  835. this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1;
  836. this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
  837. } else {
  838. this.pos = this.lineStart = 0;
  839. this.curLine = 1;
  840. }
  841. // Properties of the current token:
  842. // Its type
  843. this.type = types$1.eof;
  844. // For tokens that include more information than their type, the value
  845. this.value = null;
  846. // Its start and end offset
  847. this.start = this.end = this.pos;
  848. // And, if locations are used, the {line, column} object
  849. // corresponding to those offsets
  850. this.startLoc = this.endLoc = this.curPosition();
  851. // Position information for the previous token
  852. this.lastTokEndLoc = this.lastTokStartLoc = null;
  853. this.lastTokStart = this.lastTokEnd = this.pos;
  854. // The context stack is used to superficially track syntactic
  855. // context to predict whether a regular expression is allowed in a
  856. // given position.
  857. this.context = this.initialContext();
  858. this.exprAllowed = true;
  859. // Figure out if it's a module code.
  860. this.inModule = options.sourceType === "module";
  861. this.strict = this.inModule || this.strictDirective(this.pos);
  862. // Used to signify the start of a potential arrow function
  863. this.potentialArrowAt = -1;
  864. // Positions to delayed-check that yield/await does not exist in default parameters.
  865. this.yieldPos = this.awaitPos = this.awaitIdentPos = 0;
  866. // Labels in scope.
  867. this.labels = [];
  868. // Thus-far undefined exports.
  869. this.undefinedExports = {};
  870. // If enabled, skip leading hashbang line.
  871. if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!")
  872. { this.skipLineComment(2); }
  873. // Scope tracking for duplicate variable names (see scope.js)
  874. this.scopeStack = [];
  875. this.enterScope(SCOPE_TOP);
  876. // For RegExp validation
  877. this.regexpState = null;
  878. };
  879. var prototypeAccessors = { inFunction: { configurable: true },inGenerator: { configurable: true },inAsync: { configurable: true },allowSuper: { configurable: true },allowDirectSuper: { configurable: true },treatFunctionsAsVar: { configurable: true } };
  880. Parser.prototype.parse = function parse () {
  881. var node = this.options.program || this.startNode();
  882. this.nextToken();
  883. return this.parseTopLevel(node)
  884. };
  885. prototypeAccessors.inFunction.get = function () { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 };
  886. prototypeAccessors.inGenerator.get = function () { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 };
  887. prototypeAccessors.inAsync.get = function () { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 };
  888. prototypeAccessors.allowSuper.get = function () { return (this.currentThisScope().flags & SCOPE_SUPER) > 0 };
  889. prototypeAccessors.allowDirectSuper.get = function () { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 };
  890. prototypeAccessors.treatFunctionsAsVar.get = function () { return this.treatFunctionsAsVarInScope(this.currentScope()) };
  891. // Switch to a getter for 7.0.0.
  892. Parser.prototype.inNonArrowFunction = function inNonArrowFunction () { return (this.currentThisScope().flags & SCOPE_FUNCTION) > 0 };
  893. Parser.extend = function extend () {
  894. var plugins = [], len = arguments.length;
  895. while ( len-- ) plugins[ len ] = arguments[ len ];
  896. var cls = this;
  897. for (var i = 0; i < plugins.length; i++) { cls = plugins[i](cls); }
  898. return cls
  899. };
  900. Parser.parse = function parse (input, options) {
  901. return new this(options, input).parse()
  902. };
  903. Parser.parseExpressionAt = function parseExpressionAt (input, pos, options) {
  904. var parser = new this(options, input, pos);
  905. parser.nextToken();
  906. return parser.parseExpression()
  907. };
  908. Parser.tokenizer = function tokenizer (input, options) {
  909. return new this(options, input)
  910. };
  911. Object.defineProperties( Parser.prototype, prototypeAccessors );
  912. var pp = Parser.prototype;
  913. // ## Parser utilities
  914. var literal = /^(?:'((?:\\.|[^'])*?)'|"((?:\\.|[^"])*?)")/;
  915. pp.strictDirective = function(start) {
  916. for (;;) {
  917. // Try to find string literal.
  918. skipWhiteSpace.lastIndex = start;
  919. start += skipWhiteSpace.exec(this.input)[0].length;
  920. var match = literal.exec(this.input.slice(start));
  921. if (!match) { return false }
  922. if ((match[1] || match[2]) === "use strict") { return true }
  923. start += match[0].length;
  924. // Skip semicolon, if any.
  925. skipWhiteSpace.lastIndex = start;
  926. start += skipWhiteSpace.exec(this.input)[0].length;
  927. if (this.input[start] === ";")
  928. { start++; }
  929. }
  930. };
  931. // Predicate that tests whether the next token is of the given
  932. // type, and if yes, consumes it as a side effect.
  933. pp.eat = function(type) {
  934. if (this.type === type) {
  935. this.next();
  936. return true
  937. } else {
  938. return false
  939. }
  940. };
  941. // Tests whether parsed token is a contextual keyword.
  942. pp.isContextual = function(name) {
  943. return this.type === types$1.name && this.value === name && !this.containsEsc
  944. };
  945. // Consumes contextual keyword if possible.
  946. pp.eatContextual = function(name) {
  947. if (!this.isContextual(name)) { return false }
  948. this.next();
  949. return true
  950. };
  951. // Asserts that following token is given contextual keyword.
  952. pp.expectContextual = function(name) {
  953. if (!this.eatContextual(name)) { this.unexpected(); }
  954. };
  955. // Test whether a semicolon can be inserted at the current position.
  956. pp.canInsertSemicolon = function() {
  957. return this.type === types$1.eof ||
  958. this.type === types$1.braceR ||
  959. lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
  960. };
  961. pp.insertSemicolon = function() {
  962. if (this.canInsertSemicolon()) {
  963. if (this.options.onInsertedSemicolon)
  964. { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }
  965. return true
  966. }
  967. };
  968. // Consume a semicolon, or, failing that, see if we are allowed to
  969. // pretend that there is a semicolon at this position.
  970. pp.semicolon = function() {
  971. if (!this.eat(types$1.semi) && !this.insertSemicolon()) { this.unexpected(); }
  972. };
  973. pp.afterTrailingComma = function(tokType, notNext) {
  974. if (this.type === tokType) {
  975. if (this.options.onTrailingComma)
  976. { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }
  977. if (!notNext)
  978. { this.next(); }
  979. return true
  980. }
  981. };
  982. // Expect a token of a given type. If found, consume it, otherwise,
  983. // raise an unexpected token error.
  984. pp.expect = function(type) {
  985. this.eat(type) || this.unexpected();
  986. };
  987. // Raise an unexpected token error.
  988. pp.unexpected = function(pos) {
  989. this.raise(pos != null ? pos : this.start, "Unexpected token");
  990. };
  991. function DestructuringErrors() {
  992. this.shorthandAssign =
  993. this.trailingComma =
  994. this.parenthesizedAssign =
  995. this.parenthesizedBind =
  996. this.doubleProto =
  997. -1;
  998. }
  999. pp.checkPatternErrors = function(refDestructuringErrors, isAssign) {
  1000. if (!refDestructuringErrors) { return }
  1001. if (refDestructuringErrors.trailingComma > -1)
  1002. { this.raiseRecoverable(refDestructuringErrors.trailingComma, "Comma is not permitted after the rest element"); }
  1003. var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;
  1004. if (parens > -1) { this.raiseRecoverable(parens, "Parenthesized pattern"); }
  1005. };
  1006. pp.checkExpressionErrors = function(refDestructuringErrors, andThrow) {
  1007. if (!refDestructuringErrors) { return false }
  1008. var shorthandAssign = refDestructuringErrors.shorthandAssign;
  1009. var doubleProto = refDestructuringErrors.doubleProto;
  1010. if (!andThrow) { return shorthandAssign >= 0 || doubleProto >= 0 }
  1011. if (shorthandAssign >= 0)
  1012. { this.raise(shorthandAssign, "Shorthand property assignments are valid only in destructuring patterns"); }
  1013. if (doubleProto >= 0)
  1014. { this.raiseRecoverable(doubleProto, "Redefinition of __proto__ property"); }
  1015. };
  1016. pp.checkYieldAwaitInDefaultParams = function() {
  1017. if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))
  1018. { this.raise(this.yieldPos, "Yield expression cannot be a default value"); }
  1019. if (this.awaitPos)
  1020. { this.raise(this.awaitPos, "Await expression cannot be a default value"); }
  1021. };
  1022. pp.isSimpleAssignTarget = function(expr) {
  1023. if (expr.type === "ParenthesizedExpression")
  1024. { return this.isSimpleAssignTarget(expr.expression) }
  1025. return expr.type === "Identifier" || expr.type === "MemberExpression"
  1026. };
  1027. var pp$1 = Parser.prototype;
  1028. // ### Statement parsing
  1029. // Parse a program. Initializes the parser, reads any number of
  1030. // statements, and wraps them in a Program node. Optionally takes a
  1031. // `program` argument. If present, the statements will be appended
  1032. // to its body instead of creating a new node.
  1033. pp$1.parseTopLevel = function(node) {
  1034. var exports = {};
  1035. if (!node.body) { node.body = []; }
  1036. while (this.type !== types$1.eof) {
  1037. var stmt = this.parseStatement(null, true, exports);
  1038. node.body.push(stmt);
  1039. }
  1040. if (this.inModule)
  1041. { for (var i = 0, list = Object.keys(this.undefinedExports); i < list.length; i += 1)
  1042. {
  1043. var name = list[i];
  1044. this.raiseRecoverable(this.undefinedExports[name].start, ("Export '" + name + "' is not defined"));
  1045. } }
  1046. this.adaptDirectivePrologue(node.body);
  1047. this.next();
  1048. node.sourceType = this.options.sourceType;
  1049. return this.finishNode(node, "Program")
  1050. };
  1051. var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};
  1052. pp$1.isLet = function(context) {
  1053. if (this.options.ecmaVersion < 6 || !this.isContextual("let")) { return false }
  1054. skipWhiteSpace.lastIndex = this.pos;
  1055. var skip = skipWhiteSpace.exec(this.input);
  1056. var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);
  1057. // For ambiguous cases, determine if a LexicalDeclaration (or only a
  1058. // Statement) is allowed here. If context is not empty then only a Statement
  1059. // is allowed. However, `let [` is an explicit negative lookahead for
  1060. // ExpressionStatement, so special-case it first.
  1061. if (nextCh === 91) { return true } // '['
  1062. if (context) { return false }
  1063. if (nextCh === 123) { return true } // '{'
  1064. if (isIdentifierStart(nextCh, true)) {
  1065. var pos = next + 1;
  1066. while (isIdentifierChar(this.input.charCodeAt(pos), true)) { ++pos; }
  1067. var ident = this.input.slice(next, pos);
  1068. if (!keywordRelationalOperator.test(ident)) { return true }
  1069. }
  1070. return false
  1071. };
  1072. // check 'async [no LineTerminator here] function'
  1073. // - 'async /*foo*/ function' is OK.
  1074. // - 'async /*\n*/ function' is invalid.
  1075. pp$1.isAsyncFunction = function() {
  1076. if (this.options.ecmaVersion < 8 || !this.isContextual("async"))
  1077. { return false }
  1078. skipWhiteSpace.lastIndex = this.pos;
  1079. var skip = skipWhiteSpace.exec(this.input);
  1080. var next = this.pos + skip[0].length;
  1081. return !lineBreak.test(this.input.slice(this.pos, next)) &&
  1082. this.input.slice(next, next + 8) === "function" &&
  1083. (next + 8 === this.input.length || !isIdentifierChar(this.input.charAt(next + 8)))
  1084. };
  1085. // Parse a single statement.
  1086. //
  1087. // If expecting a statement and finding a slash operator, parse a
  1088. // regular expression literal. This is to handle cases like
  1089. // `if (foo) /blah/.exec(foo)`, where looking at the previous token
  1090. // does not help.
  1091. pp$1.parseStatement = function(context, topLevel, exports) {
  1092. var starttype = this.type, node = this.startNode(), kind;
  1093. if (this.isLet(context)) {
  1094. starttype = types$1._var;
  1095. kind = "let";
  1096. }
  1097. // Most types of statements are recognized by the keyword they
  1098. // start with. Many are trivial to parse, some require a bit of
  1099. // complexity.
  1100. switch (starttype) {
  1101. case types$1._break: case types$1._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
  1102. case types$1._debugger: return this.parseDebuggerStatement(node)
  1103. case types$1._do: return this.parseDoStatement(node)
  1104. case types$1._for: return this.parseForStatement(node)
  1105. case types$1._function:
  1106. // Function as sole body of either an if statement or a labeled statement
  1107. // works, but not when it is part of a labeled statement that is the sole
  1108. // body of an if statement.
  1109. if ((context && (this.strict || context !== "if" && context !== "label")) && this.options.ecmaVersion >= 6) { this.unexpected(); }
  1110. return this.parseFunctionStatement(node, false, !context)
  1111. case types$1._class:
  1112. if (context) { this.unexpected(); }
  1113. return this.parseClass(node, true)
  1114. case types$1._if: return this.parseIfStatement(node)
  1115. case types$1._return: return this.parseReturnStatement(node)
  1116. case types$1._switch: return this.parseSwitchStatement(node)
  1117. case types$1._throw: return this.parseThrowStatement(node)
  1118. case types$1._try: return this.parseTryStatement(node)
  1119. case types$1._const: case types$1._var:
  1120. kind = kind || this.value;
  1121. if (context && kind !== "var") { this.unexpected(); }
  1122. return this.parseVarStatement(node, kind)
  1123. case types$1._while: return this.parseWhileStatement(node)
  1124. case types$1._with: return this.parseWithStatement(node)
  1125. case types$1.braceL: return this.parseBlock(true, node)
  1126. case types$1.semi: return this.parseEmptyStatement(node)
  1127. case types$1._export:
  1128. case types$1._import:
  1129. if (this.options.ecmaVersion > 10 && starttype === types$1._import) {
  1130. skipWhiteSpace.lastIndex = this.pos;
  1131. var skip = skipWhiteSpace.exec(this.input);
  1132. var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);
  1133. if (nextCh === 40) // '('
  1134. { return this.parseExpressionStatement(node, this.parseExpression()) }
  1135. }
  1136. if (!this.options.allowImportExportEverywhere) {
  1137. if (!topLevel)
  1138. { this.raise(this.start, "'import' and 'export' may only appear at the top level"); }
  1139. if (!this.inModule)
  1140. { this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); }
  1141. }
  1142. return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)
  1143. // If the statement does not start with a statement keyword or a
  1144. // brace, it's an ExpressionStatement or LabeledStatement. We
  1145. // simply start parsing an expression, and afterwards, if the
  1146. // next token is a colon and the expression was a simple
  1147. // Identifier node, we switch to interpreting it as a label.
  1148. default:
  1149. if (this.isAsyncFunction()) {
  1150. if (context) { this.unexpected(); }
  1151. this.next();
  1152. return this.parseFunctionStatement(node, true, !context)
  1153. }
  1154. var maybeName = this.value, expr = this.parseExpression();
  1155. if (starttype === types$1.name && expr.type === "Identifier" && this.eat(types$1.colon))
  1156. { return this.parseLabeledStatement(node, maybeName, expr, context) }
  1157. else { return this.parseExpressionStatement(node, expr) }
  1158. }
  1159. };
  1160. pp$1.parseBreakContinueStatement = function(node, keyword) {
  1161. var isBreak = keyword === "break";
  1162. this.next();
  1163. if (this.eat(types$1.semi) || this.insertSemicolon()) { node.label = null; }
  1164. else if (this.type !== types$1.name) { this.unexpected(); }
  1165. else {
  1166. node.label = this.parseIdent();
  1167. this.semicolon();
  1168. }
  1169. // Verify that there is an actual destination to break or
  1170. // continue to.
  1171. var i = 0;
  1172. for (; i < this.labels.length; ++i) {
  1173. var lab = this.labels[i];
  1174. if (node.label == null || lab.name === node.label.name) {
  1175. if (lab.kind != null && (isBreak || lab.kind === "loop")) { break }
  1176. if (node.label && isBreak) { break }
  1177. }
  1178. }
  1179. if (i === this.labels.length) { this.raise(node.start, "Unsyntactic " + keyword); }
  1180. return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
  1181. };
  1182. pp$1.parseDebuggerStatement = function(node) {
  1183. this.next();
  1184. this.semicolon();
  1185. return this.finishNode(node, "DebuggerStatement")
  1186. };
  1187. pp$1.parseDoStatement = function(node) {
  1188. this.next();
  1189. this.labels.push(loopLabel);
  1190. node.body = this.parseStatement("do");
  1191. this.labels.pop();
  1192. this.expect(types$1._while);
  1193. node.test = this.parseParenExpression();
  1194. if (this.options.ecmaVersion >= 6)
  1195. { this.eat(types$1.semi); }
  1196. else
  1197. { this.semicolon(); }
  1198. return this.finishNode(node, "DoWhileStatement")
  1199. };
  1200. // Disambiguating between a `for` and a `for`/`in` or `for`/`of`
  1201. // loop is non-trivial. Basically, we have to parse the init `var`
  1202. // statement or expression, disallowing the `in` operator (see
  1203. // the second parameter to `parseExpression`), and then check
  1204. // whether the next token is `in` or `of`. When there is no init
  1205. // part (semicolon immediately after the opening parenthesis), it
  1206. // is a regular `for` loop.
  1207. pp$1.parseForStatement = function(node) {
  1208. this.next();
  1209. var awaitAt = (this.options.ecmaVersion >= 9 && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction)) && this.eatContextual("await")) ? this.lastTokStart : -1;
  1210. this.labels.push(loopLabel);
  1211. this.enterScope(0);
  1212. this.expect(types$1.parenL);
  1213. if (this.type === types$1.semi) {
  1214. if (awaitAt > -1) { this.unexpected(awaitAt); }
  1215. return this.parseFor(node, null)
  1216. }
  1217. var isLet = this.isLet();
  1218. if (this.type === types$1._var || this.type === types$1._const || isLet) {
  1219. var init$1 = this.startNode(), kind = isLet ? "let" : this.value;
  1220. this.next();
  1221. this.parseVar(init$1, true, kind);
  1222. this.finishNode(init$1, "VariableDeclaration");
  1223. if ((this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init$1.declarations.length === 1) {
  1224. if (this.options.ecmaVersion >= 9) {
  1225. if (this.type === types$1._in) {
  1226. if (awaitAt > -1) { this.unexpected(awaitAt); }
  1227. } else { node.await = awaitAt > -1; }
  1228. }
  1229. return this.parseForIn(node, init$1)
  1230. }
  1231. if (awaitAt > -1) { this.unexpected(awaitAt); }
  1232. return this.parseFor(node, init$1)
  1233. }
  1234. var refDestructuringErrors = new DestructuringErrors;
  1235. var init = this.parseExpression(true, refDestructuringErrors);
  1236. if (this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
  1237. if (this.options.ecmaVersion >= 9) {
  1238. if (this.type === types$1._in) {
  1239. if (awaitAt > -1) { this.unexpected(awaitAt); }
  1240. } else { node.await = awaitAt > -1; }
  1241. }
  1242. this.toAssignable(init, false, refDestructuringErrors);
  1243. this.checkLVal(init);
  1244. return this.parseForIn(node, init)
  1245. } else {
  1246. this.checkExpressionErrors(refDestructuringErrors, true);
  1247. }
  1248. if (awaitAt > -1) { this.unexpected(awaitAt); }
  1249. return this.parseFor(node, init)
  1250. };
  1251. pp$1.parseFunctionStatement = function(node, isAsync, declarationPosition) {
  1252. this.next();
  1253. return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)
  1254. };
  1255. pp$1.parseIfStatement = function(node) {
  1256. this.next();
  1257. node.test = this.parseParenExpression();
  1258. // allow function declarations in branches, but only in non-strict mode
  1259. node.consequent = this.parseStatement("if");
  1260. node.alternate = this.eat(types$1._else) ? this.parseStatement("if") : null;
  1261. return this.finishNode(node, "IfStatement")
  1262. };
  1263. pp$1.parseReturnStatement = function(node) {
  1264. if (!this.inFunction && !this.options.allowReturnOutsideFunction)
  1265. { this.raise(this.start, "'return' outside of function"); }
  1266. this.next();
  1267. // In `return` (and `break`/`continue`), the keywords with
  1268. // optional arguments, we eagerly look for a semicolon or the
  1269. // possibility to insert one.
  1270. if (this.eat(types$1.semi) || this.insertSemicolon()) { node.argument = null; }
  1271. else { node.argument = this.parseExpression(); this.semicolon(); }
  1272. return this.finishNode(node, "ReturnStatement")
  1273. };
  1274. pp$1.parseSwitchStatement = function(node) {
  1275. this.next();
  1276. node.discriminant = this.parseParenExpression();
  1277. node.cases = [];
  1278. this.expect(types$1.braceL);
  1279. this.labels.push(switchLabel);
  1280. this.enterScope(0);
  1281. // Statements under must be grouped (by label) in SwitchCase
  1282. // nodes. `cur` is used to keep the node that we are currently
  1283. // adding statements to.
  1284. var cur;
  1285. for (var sawDefault = false; this.type !== types$1.braceR;) {
  1286. if (this.type === types$1._case || this.type === types$1._default) {
  1287. var isCase = this.type === types$1._case;
  1288. if (cur) { this.finishNode(cur, "SwitchCase"); }
  1289. node.cases.push(cur = this.startNode());
  1290. cur.consequent = [];
  1291. this.next();
  1292. if (isCase) {
  1293. cur.test = this.parseExpression();
  1294. } else {
  1295. if (sawDefault) { this.raiseRecoverable(this.lastTokStart, "Multiple default clauses"); }
  1296. sawDefault = true;
  1297. cur.test = null;
  1298. }
  1299. this.expect(types$1.colon);
  1300. } else {
  1301. if (!cur) { this.unexpected(); }
  1302. cur.consequent.push(this.parseStatement(null));
  1303. }
  1304. }
  1305. this.exitScope();
  1306. if (cur) { this.finishNode(cur, "SwitchCase"); }
  1307. this.next(); // Closing brace
  1308. this.labels.pop();
  1309. return this.finishNode(node, "SwitchStatement")
  1310. };
  1311. pp$1.parseThrowStatement = function(node) {
  1312. this.next();
  1313. if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
  1314. { this.raise(this.lastTokEnd, "Illegal newline after throw"); }
  1315. node.argument = this.parseExpression();
  1316. this.semicolon();
  1317. return this.finishNode(node, "ThrowStatement")
  1318. };
  1319. // Reused empty array added for node fields that are always empty.
  1320. var empty = [];
  1321. pp$1.parseTryStatement = function(node) {
  1322. this.next();
  1323. node.block = this.parseBlock();
  1324. node.handler = null;
  1325. if (this.type === types$1._catch) {
  1326. var clause = this.startNode();
  1327. this.next();
  1328. if (this.eat(types$1.parenL)) {
  1329. clause.param = this.parseBindingAtom();
  1330. var simple = clause.param.type === "Identifier";
  1331. this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);
  1332. this.checkLVal(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);
  1333. this.expect(types$1.parenR);
  1334. } else {
  1335. if (this.options.ecmaVersion < 10) { this.unexpected(); }
  1336. clause.param = null;
  1337. this.enterScope(0);
  1338. }
  1339. clause.body = this.parseBlock(false);
  1340. this.exitScope();
  1341. node.handler = this.finishNode(clause, "CatchClause");
  1342. }
  1343. node.finalizer = this.eat(types$1._finally) ? this.parseBlock() : null;
  1344. if (!node.handler && !node.finalizer)
  1345. { this.raise(node.start, "Missing catch or finally clause"); }
  1346. return this.finishNode(node, "TryStatement")
  1347. };
  1348. pp$1.parseVarStatement = function(node, kind) {
  1349. this.next();
  1350. this.parseVar(node, false, kind);
  1351. this.semicolon();
  1352. return this.finishNode(node, "VariableDeclaration")
  1353. };
  1354. pp$1.parseWhileStatement = function(node) {
  1355. this.next();
  1356. node.test = this.parseParenExpression();
  1357. this.labels.push(loopLabel);
  1358. node.body = this.parseStatement("while");
  1359. this.labels.pop();
  1360. return this.finishNode(node, "WhileStatement")
  1361. };
  1362. pp$1.parseWithStatement = function(node) {
  1363. if (this.strict) { this.raise(this.start, "'with' in strict mode"); }
  1364. this.next();
  1365. node.object = this.parseParenExpression();
  1366. node.body = this.parseStatement("with");
  1367. return this.finishNode(node, "WithStatement")
  1368. };
  1369. pp$1.parseEmptyStatement = function(node) {
  1370. this.next();
  1371. return this.finishNode(node, "EmptyStatement")
  1372. };
  1373. pp$1.parseLabeledStatement = function(node, maybeName, expr, context) {
  1374. for (var i$1 = 0, list = this.labels; i$1 < list.length; i$1 += 1)
  1375. {
  1376. var label = list[i$1];
  1377. if (label.name === maybeName)
  1378. { this.raise(expr.start, "Label '" + maybeName + "' is already declared");
  1379. } }
  1380. var kind = this.type.isLoop ? "loop" : this.type === types$1._switch ? "switch" : null;
  1381. for (var i = this.labels.length - 1; i >= 0; i--) {
  1382. var label$1 = this.labels[i];
  1383. if (label$1.statementStart === node.start) {
  1384. // Update information about previous labels on this node
  1385. label$1.statementStart = this.start;
  1386. label$1.kind = kind;
  1387. } else { break }
  1388. }
  1389. this.labels.push({name: maybeName, kind: kind, statementStart: this.start});
  1390. node.body = this.parseStatement(context ? context.indexOf("label") === -1 ? context + "label" : context : "label");
  1391. this.labels.pop();
  1392. node.label = expr;
  1393. return this.finishNode(node, "LabeledStatement")
  1394. };
  1395. pp$1.parseExpressionStatement = function(node, expr) {
  1396. node.expression = expr;
  1397. this.semicolon();
  1398. return this.finishNode(node, "ExpressionStatement")
  1399. };
  1400. // Parse a semicolon-enclosed block of statements, handling `"use
  1401. // strict"` declarations when `allowStrict` is true (used for
  1402. // function bodies).
  1403. pp$1.parseBlock = function(createNewLexicalScope, node) {
  1404. if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;
  1405. if ( node === void 0 ) node = this.startNode();
  1406. node.body = [];
  1407. this.expect(types$1.braceL);
  1408. if (createNewLexicalScope) { this.enterScope(0); }
  1409. while (!this.eat(types$1.braceR)) {
  1410. var stmt = this.parseStatement(null);
  1411. node.body.push(stmt);
  1412. }
  1413. if (createNewLexicalScope) { this.exitScope(); }
  1414. return this.finishNode(node, "BlockStatement")
  1415. };
  1416. // Parse a regular `for` loop. The disambiguation code in
  1417. // `parseStatement` will already have parsed the init statement or
  1418. // expression.
  1419. pp$1.parseFor = function(node, init) {
  1420. node.init = init;
  1421. this.expect(types$1.semi);
  1422. node.test = this.type === types$1.semi ? null : this.parseExpression();
  1423. this.expect(types$1.semi);
  1424. node.update = this.type === types$1.parenR ? null : this.parseExpression();
  1425. this.expect(types$1.parenR);
  1426. node.body = this.parseStatement("for");
  1427. this.exitScope();
  1428. this.labels.pop();
  1429. return this.finishNode(node, "ForStatement")
  1430. };
  1431. // Parse a `for`/`in` and `for`/`of` loop, which are almost
  1432. // same from parser's perspective.
  1433. pp$1.parseForIn = function(node, init) {
  1434. var isForIn = this.type === types$1._in;
  1435. this.next();
  1436. if (
  1437. init.type === "VariableDeclaration" &&
  1438. init.declarations[0].init != null &&
  1439. (
  1440. !isForIn ||
  1441. this.options.ecmaVersion < 8 ||
  1442. this.strict ||
  1443. init.kind !== "var" ||
  1444. init.declarations[0].id.type !== "Identifier"
  1445. )
  1446. ) {
  1447. this.raise(
  1448. init.start,
  1449. ((isForIn ? "for-in" : "for-of") + " loop variable declaration may not have an initializer")
  1450. );
  1451. } else if (init.type === "AssignmentPattern") {
  1452. this.raise(init.start, "Invalid left-hand side in for-loop");
  1453. }
  1454. node.left = init;
  1455. node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();
  1456. this.expect(types$1.parenR);
  1457. node.body = this.parseStatement("for");
  1458. this.exitScope();
  1459. this.labels.pop();
  1460. return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement")
  1461. };
  1462. // Parse a list of variable declarations.
  1463. pp$1.parseVar = function(node, isFor, kind) {
  1464. node.declarations = [];
  1465. node.kind = kind;
  1466. for (;;) {
  1467. var decl = this.startNode();
  1468. this.parseVarId(decl, kind);
  1469. if (this.eat(types$1.eq)) {
  1470. decl.init = this.parseMaybeAssign(isFor);
  1471. } else if (kind === "const" && !(this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
  1472. this.unexpected();
  1473. } else if (decl.id.type !== "Identifier" && !(isFor && (this.type === types$1._in || this.isContextual("of")))) {
  1474. this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value");
  1475. } else {
  1476. decl.init = null;
  1477. }
  1478. node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
  1479. if (!this.eat(types$1.comma)) { break }
  1480. }
  1481. return node
  1482. };
  1483. pp$1.parseVarId = function(decl, kind) {
  1484. decl.id = this.parseBindingAtom();
  1485. this.checkLVal(decl.id, kind === "var" ? BIND_VAR : BIND_LEXICAL, false);
  1486. };
  1487. var FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4;
  1488. // Parse a function declaration or literal (depending on the
  1489. // `statement & FUNC_STATEMENT`).
  1490. // Remove `allowExpressionBody` for 7.0.0, as it is only called with false
  1491. pp$1.parseFunction = function(node, statement, allowExpressionBody, isAsync) {
  1492. this.initFunction(node);
  1493. if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {
  1494. if (this.type === types$1.star && (statement & FUNC_HANGING_STATEMENT))
  1495. { this.unexpected(); }
  1496. node.generator = this.eat(types$1.star);
  1497. }
  1498. if (this.options.ecmaVersion >= 8)
  1499. { node.async = !!isAsync; }
  1500. if (statement & FUNC_STATEMENT) {
  1501. node.id = (statement & FUNC_NULLABLE_ID) && this.type !== types$1.name ? null : this.parseIdent();
  1502. if (node.id && !(statement & FUNC_HANGING_STATEMENT))
  1503. // If it is a regular function declaration in sloppy mode, then it is
  1504. // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding
  1505. // mode depends on properties of the current scope (see
  1506. // treatFunctionsAsVar).
  1507. { this.checkLVal(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION); }
  1508. }
  1509. var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;
  1510. this.yieldPos = 0;
  1511. this.awaitPos = 0;
  1512. this.awaitIdentPos = 0;
  1513. this.enterScope(functionFlags(node.async, node.generator));
  1514. if (!(statement & FUNC_STATEMENT))
  1515. { node.id = this.type === types$1.name ? this.parseIdent() : null; }
  1516. this.parseFunctionParams(node);
  1517. this.parseFunctionBody(node, allowExpressionBody, false);
  1518. this.yieldPos = oldYieldPos;
  1519. this.awaitPos = oldAwaitPos;
  1520. this.awaitIdentPos = oldAwaitIdentPos;
  1521. return this.finishNode(node, (statement & FUNC_STATEMENT) ? "FunctionDeclaration" : "FunctionExpression")
  1522. };
  1523. pp$1.parseFunctionParams = function(node) {
  1524. this.expect(types$1.parenL);
  1525. node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);
  1526. this.checkYieldAwaitInDefaultParams();
  1527. };
  1528. // Parse a class declaration or literal (depending on the
  1529. // `isStatement` parameter).
  1530. pp$1.parseClass = function(node, isStatement) {
  1531. this.next();
  1532. // ecma-262 14.6 Class Definitions
  1533. // A class definition is always strict mode code.
  1534. var oldStrict = this.strict;
  1535. this.strict = true;
  1536. this.parseClassId(node, isStatement);
  1537. this.parseClassSuper(node);
  1538. var classBody = this.startNode();
  1539. var hadConstructor = false;
  1540. classBody.body = [];
  1541. this.expect(types$1.braceL);
  1542. while (!this.eat(types$1.braceR)) {
  1543. var element = this.parseClassElement(node.superClass !== null);
  1544. if (element) {
  1545. classBody.body.push(element);
  1546. if (element.type === "MethodDefinition" && element.kind === "constructor") {
  1547. if (hadConstructor) { this.raise(element.start, "Duplicate constructor in the same class"); }
  1548. hadConstructor = true;
  1549. }
  1550. }
  1551. }
  1552. node.body = this.finishNode(classBody, "ClassBody");
  1553. this.strict = oldStrict;
  1554. return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
  1555. };
  1556. pp$1.parseClassElement = function(constructorAllowsSuper) {
  1557. var this$1 = this;
  1558. if (this.eat(types$1.semi)) { return null }
  1559. var method = this.startNode();
  1560. var tryContextual = function (k, noLineBreak) {
  1561. if ( noLineBreak === void 0 ) noLineBreak = false;
  1562. var start = this$1.start, startLoc = this$1.startLoc;
  1563. if (!this$1.eatContextual(k)) { return false }
  1564. if (this$1.type !== types$1.parenL && (!noLineBreak || !this$1.canInsertSemicolon())) { return true }
  1565. if (method.key) { this$1.unexpected(); }
  1566. method.computed = false;
  1567. method.key = this$1.startNodeAt(start, startLoc);
  1568. method.key.name = k;
  1569. this$1.finishNode(method.key, "Identifier");
  1570. return false
  1571. };
  1572. method.kind = "method";
  1573. method.static = tryContextual("static");
  1574. var isGenerator = this.eat(types$1.star);
  1575. var isAsync = false;
  1576. if (!isGenerator) {
  1577. if (this.options.ecmaVersion >= 8 && tryContextual("async", true)) {
  1578. isAsync = true;
  1579. isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);
  1580. } else if (tryContextual("get")) {
  1581. method.kind = "get";
  1582. } else if (tryContextual("set")) {
  1583. method.kind = "set";
  1584. }
  1585. }
  1586. if (!method.key) { this.parsePropertyName(method); }
  1587. var key = method.key;
  1588. var allowsDirectSuper = false;
  1589. if (!method.computed && !method.static && (key.type === "Identifier" && key.name === "constructor" ||
  1590. key.type === "Literal" && key.value === "constructor")) {
  1591. if (method.kind !== "method") { this.raise(key.start, "Constructor can't have get/set modifier"); }
  1592. if (isGenerator) { this.raise(key.start, "Constructor can't be a generator"); }
  1593. if (isAsync) { this.raise(key.start, "Constructor can't be an async method"); }
  1594. method.kind = "constructor";
  1595. allowsDirectSuper = constructorAllowsSuper;
  1596. } else if (method.static && key.type === "Identifier" && key.name === "prototype") {
  1597. this.raise(key.start, "Classes may not have a static property named prototype");
  1598. }
  1599. this.parseClassMethod(method, isGenerator, isAsync, allowsDirectSuper);
  1600. if (method.kind === "get" && method.value.params.length !== 0)
  1601. { this.raiseRecoverable(method.value.start, "getter should have no params"); }
  1602. if (method.kind === "set" && method.value.params.length !== 1)
  1603. { this.raiseRecoverable(method.value.start, "setter should have exactly one param"); }
  1604. if (method.kind === "set" && method.value.params[0].type === "RestElement")
  1605. { this.raiseRecoverable(method.value.params[0].start, "Setter cannot use rest params"); }
  1606. return method
  1607. };
  1608. pp$1.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {
  1609. method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper);
  1610. return this.finishNode(method, "MethodDefinition")
  1611. };
  1612. pp$1.parseClassId = function(node, isStatement) {
  1613. if (this.type === types$1.name) {
  1614. node.id = this.parseIdent();
  1615. if (isStatement)
  1616. { this.checkLVal(node.id, BIND_LEXICAL, false); }
  1617. } else {
  1618. if (isStatement === true)
  1619. { this.unexpected(); }
  1620. node.id = null;
  1621. }
  1622. };
  1623. pp$1.parseClassSuper = function(node) {
  1624. node.superClass = this.eat(types$1._extends) ? this.parseExprSubscripts() : null;
  1625. };
  1626. // Parses module export declaration.
  1627. pp$1.parseExport = function(node, exports) {
  1628. this.next();
  1629. // export * from '...'
  1630. if (this.eat(types$1.star)) {
  1631. this.expectContextual("from");
  1632. if (this.type !== types$1.string) { this.unexpected(); }
  1633. node.source = this.parseExprAtom();
  1634. this.semicolon();
  1635. return this.finishNode(node, "ExportAllDeclaration")
  1636. }
  1637. if (this.eat(types$1._default)) { // export default ...
  1638. this.checkExport(exports, "default", this.lastTokStart);
  1639. var isAsync;
  1640. if (this.type === types$1._function || (isAsync = this.isAsyncFunction())) {
  1641. var fNode = this.startNode();
  1642. this.next();
  1643. if (isAsync) { this.next(); }
  1644. node.declaration = this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync);
  1645. } else if (this.type === types$1._class) {
  1646. var cNode = this.startNode();
  1647. node.declaration = this.parseClass(cNode, "nullableID");
  1648. } else {
  1649. node.declaration = this.parseMaybeAssign();
  1650. this.semicolon();
  1651. }
  1652. return this.finishNode(node, "ExportDefaultDeclaration")
  1653. }
  1654. // export var|const|let|function|class ...
  1655. if (this.shouldParseExportStatement()) {
  1656. node.declaration = this.parseStatement(null);
  1657. if (node.declaration.type === "VariableDeclaration")
  1658. { this.checkVariableExport(exports, node.declaration.declarations); }
  1659. else
  1660. { this.checkExport(exports, node.declaration.id.name, node.declaration.id.start); }
  1661. node.specifiers = [];
  1662. node.source = null;
  1663. } else { // export { x, y as z } [from '...']
  1664. node.declaration = null;
  1665. node.specifiers = this.parseExportSpecifiers(exports);
  1666. if (this.eatContextual("from")) {
  1667. if (this.type !== types$1.string) { this.unexpected(); }
  1668. node.source = this.parseExprAtom();
  1669. } else {
  1670. for (var i = 0, list = node.specifiers; i < list.length; i += 1) {
  1671. // check for keywords used as local names
  1672. var spec = list[i];
  1673. this.checkUnreserved(spec.local);
  1674. // check if export is defined
  1675. this.checkLocalExport(spec.local);
  1676. }
  1677. node.source = null;
  1678. }
  1679. this.semicolon();
  1680. }
  1681. return this.finishNode(node, "ExportNamedDeclaration")
  1682. };
  1683. pp$1.checkExport = function(exports, name, pos) {
  1684. if (!exports) { return }
  1685. if (has(exports, name))
  1686. { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); }
  1687. exports[name] = true;
  1688. };
  1689. pp$1.checkPatternExport = function(exports, pat) {
  1690. var type = pat.type;
  1691. if (type === "Identifier")
  1692. { this.checkExport(exports, pat.name, pat.start); }
  1693. else if (type === "ObjectPattern")
  1694. { for (var i = 0, list = pat.properties; i < list.length; i += 1)
  1695. {
  1696. var prop = list[i];
  1697. this.checkPatternExport(exports, prop);
  1698. } }
  1699. else if (type === "ArrayPattern")
  1700. { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {
  1701. var elt = list$1[i$1];
  1702. if (elt) { this.checkPatternExport(exports, elt); }
  1703. } }
  1704. else if (type === "Property")
  1705. { this.checkPatternExport(exports, pat.value); }
  1706. else if (type === "AssignmentPattern")
  1707. { this.checkPatternExport(exports, pat.left); }
  1708. else if (type === "RestElement")
  1709. { this.checkPatternExport(exports, pat.argument); }
  1710. else if (type === "ParenthesizedExpression")
  1711. { this.checkPatternExport(exports, pat.expression); }
  1712. };
  1713. pp$1.checkVariableExport = function(exports, decls) {
  1714. if (!exports) { return }
  1715. for (var i = 0, list = decls; i < list.length; i += 1)
  1716. {
  1717. var decl = list[i];
  1718. this.checkPatternExport(exports, decl.id);
  1719. }
  1720. };
  1721. pp$1.shouldParseExportStatement = function() {
  1722. return this.type.keyword === "var" ||
  1723. this.type.keyword === "const" ||
  1724. this.type.keyword === "class" ||
  1725. this.type.keyword === "function" ||
  1726. this.isLet() ||
  1727. this.isAsyncFunction()
  1728. };
  1729. // Parses a comma-separated list of module exports.
  1730. pp$1.parseExportSpecifiers = function(exports) {
  1731. var nodes = [], first = true;
  1732. // export { x, y as z } [from '...']
  1733. this.expect(types$1.braceL);
  1734. while (!this.eat(types$1.braceR)) {
  1735. if (!first) {
  1736. this.expect(types$1.comma);
  1737. if (this.afterTrailingComma(types$1.braceR)) { break }
  1738. } else { first = false; }
  1739. var node = this.startNode();
  1740. node.local = this.parseIdent(true);
  1741. node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
  1742. this.checkExport(exports, node.exported.name, node.exported.start);
  1743. nodes.push(this.finishNode(node, "ExportSpecifier"));
  1744. }
  1745. return nodes
  1746. };
  1747. // Parses import declaration.
  1748. pp$1.parseImport = function(node) {
  1749. this.next();
  1750. // import '...'
  1751. if (this.type === types$1.string) {
  1752. node.specifiers = empty;
  1753. node.source = this.parseExprAtom();
  1754. } else {
  1755. node.specifiers = this.parseImportSpecifiers();
  1756. this.expectContextual("from");
  1757. node.source = this.type === types$1.string ? this.parseExprAtom() : this.unexpected();
  1758. }
  1759. this.semicolon();
  1760. return this.finishNode(node, "ImportDeclaration")
  1761. };
  1762. // Parses a comma-separated list of module imports.
  1763. pp$1.parseImportSpecifiers = function() {
  1764. var nodes = [], first = true;
  1765. if (this.type === types$1.name) {
  1766. // import defaultObj, { x, y as z } from '...'
  1767. var node = this.startNode();
  1768. node.local = this.parseIdent();
  1769. this.checkLVal(node.local, BIND_LEXICAL);
  1770. nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
  1771. if (!this.eat(types$1.comma)) { return nodes }
  1772. }
  1773. if (this.type === types$1.star) {
  1774. var node$1 = this.startNode();
  1775. this.next();
  1776. this.expectContextual("as");
  1777. node$1.local = this.parseIdent();
  1778. this.checkLVal(node$1.local, BIND_LEXICAL);
  1779. nodes.push(this.finishNode(node$1, "ImportNamespaceSpecifier"));
  1780. return nodes
  1781. }
  1782. this.expect(types$1.braceL);
  1783. while (!this.eat(types$1.braceR)) {
  1784. if (!first) {
  1785. this.expect(types$1.comma);
  1786. if (this.afterTrailingComma(types$1.braceR)) { break }
  1787. } else { first = false; }
  1788. var node$2 = this.startNode();
  1789. node$2.imported = this.parseIdent(true);
  1790. if (this.eatContextual("as")) {
  1791. node$2.local = this.parseIdent();
  1792. } else {
  1793. this.checkUnreserved(node$2.imported);
  1794. node$2.local = node$2.imported;
  1795. }
  1796. this.checkLVal(node$2.local, BIND_LEXICAL);
  1797. nodes.push(this.finishNode(node$2, "ImportSpecifier"));
  1798. }
  1799. return nodes
  1800. };
  1801. // Set `ExpressionStatement#directive` property for directive prologues.
  1802. pp$1.adaptDirectivePrologue = function(statements) {
  1803. for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {
  1804. statements[i].directive = statements[i].expression.raw.slice(1, -1);
  1805. }
  1806. };
  1807. pp$1.isDirectiveCandidate = function(statement) {
  1808. return (
  1809. statement.type === "ExpressionStatement" &&
  1810. statement.expression.type === "Literal" &&
  1811. typeof statement.expression.value === "string" &&
  1812. // Reject parenthesized strings.
  1813. (this.input[statement.start] === "\"" || this.input[statement.start] === "'")
  1814. )
  1815. };
  1816. var pp$2 = Parser.prototype;
  1817. // Convert existing expression atom to assignable pattern
  1818. // if possible.
  1819. pp$2.toAssignable = function(node, isBinding, refDestructuringErrors) {
  1820. if (this.options.ecmaVersion >= 6 && node) {
  1821. switch (node.type) {
  1822. case "Identifier":
  1823. if (this.inAsync && node.name === "await")
  1824. { this.raise(node.start, "Cannot use 'await' as identifier inside an async function"); }
  1825. break
  1826. case "ObjectPattern":
  1827. case "ArrayPattern":
  1828. case "RestElement":
  1829. break
  1830. case "ObjectExpression":
  1831. node.type = "ObjectPattern";
  1832. if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }
  1833. for (var i = 0, list = node.properties; i < list.length; i += 1) {
  1834. var prop = list[i];
  1835. this.toAssignable(prop, isBinding);
  1836. // Early error:
  1837. // AssignmentRestProperty[Yield, Await] :
  1838. // `...` DestructuringAssignmentTarget[Yield, Await]
  1839. //
  1840. // It is a Syntax Error if |DestructuringAssignmentTarget| is an |ArrayLiteral| or an |ObjectLiteral|.
  1841. if (
  1842. prop.type === "RestElement" &&
  1843. (prop.argument.type === "ArrayPattern" || prop.argument.type === "ObjectPattern")
  1844. ) {
  1845. this.raise(prop.argument.start, "Unexpected token");
  1846. }
  1847. }
  1848. break
  1849. case "Property":
  1850. // AssignmentProperty has type === "Property"
  1851. if (node.kind !== "init") { this.raise(node.key.start, "Object pattern can't contain getter or setter"); }
  1852. this.toAssignable(node.value, isBinding);
  1853. break
  1854. case "ArrayExpression":
  1855. node.type = "ArrayPattern";
  1856. if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }
  1857. this.toAssignableList(node.elements, isBinding);
  1858. break
  1859. case "SpreadElement":
  1860. node.type = "RestElement";
  1861. this.toAssignable(node.argument, isBinding);
  1862. if (node.argument.type === "AssignmentPattern")
  1863. { this.raise(node.argument.start, "Rest elements cannot have a default value"); }
  1864. break
  1865. case "AssignmentExpression":
  1866. if (node.operator !== "=") { this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); }
  1867. node.type = "AssignmentPattern";
  1868. delete node.operator;
  1869. this.toAssignable(node.left, isBinding);
  1870. // falls through to AssignmentPattern
  1871. case "AssignmentPattern":
  1872. break
  1873. case "ParenthesizedExpression":
  1874. this.toAssignable(node.expression, isBinding, refDestructuringErrors);
  1875. break
  1876. case "MemberExpression":
  1877. if (!isBinding) { break }
  1878. default:
  1879. this.raise(node.start, "Assigning to rvalue");
  1880. }
  1881. } else if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }
  1882. return node
  1883. };
  1884. // Convert list of expression atoms to binding list.
  1885. pp$2.toAssignableList = function(exprList, isBinding) {
  1886. var end = exprList.length;
  1887. for (var i = 0; i < end; i++) {
  1888. var elt = exprList[i];
  1889. if (elt) { this.toAssignable(elt, isBinding); }
  1890. }
  1891. if (end) {
  1892. var last = exprList[end - 1];
  1893. if (this.options.ecmaVersion === 6 && isBinding && last && last.type === "RestElement" && last.argument.type !== "Identifier")
  1894. { this.unexpected(last.argument.start); }
  1895. }
  1896. return exprList
  1897. };
  1898. // Parses spread element.
  1899. pp$2.parseSpread = function(refDestructuringErrors) {
  1900. var node = this.startNode();
  1901. this.next();
  1902. node.argument = this.parseMaybeAssign(false, refDestructuringErrors);
  1903. return this.finishNode(node, "SpreadElement")
  1904. };
  1905. pp$2.parseRestBinding = function() {
  1906. var node = this.startNode();
  1907. this.next();
  1908. // RestElement inside of a function parameter must be an identifier
  1909. if (this.options.ecmaVersion === 6 && this.type !== types$1.name)
  1910. { this.unexpected(); }
  1911. node.argument = this.parseBindingAtom();
  1912. return this.finishNode(node, "RestElement")
  1913. };
  1914. // Parses lvalue (assignable) atom.
  1915. pp$2.parseBindingAtom = function() {
  1916. if (this.options.ecmaVersion >= 6) {
  1917. switch (this.type) {
  1918. case types$1.bracketL:
  1919. var node = this.startNode();
  1920. this.next();
  1921. node.elements = this.parseBindingList(types$1.bracketR, true, true);
  1922. return this.finishNode(node, "ArrayPattern")
  1923. case types$1.braceL:
  1924. return this.parseObj(true)
  1925. }
  1926. }
  1927. return this.parseIdent()
  1928. };
  1929. pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
  1930. var elts = [], first = true;
  1931. while (!this.eat(close)) {
  1932. if (first) { first = false; }
  1933. else { this.expect(types$1.comma); }
  1934. if (allowEmpty && this.type === types$1.comma) {
  1935. elts.push(null);
  1936. } else if (allowTrailingComma && this.afterTrailingComma(close)) {
  1937. break
  1938. } else if (this.type === types$1.ellipsis) {
  1939. var rest = this.parseRestBinding();
  1940. this.parseBindingListItem(rest);
  1941. elts.push(rest);
  1942. if (this.type === types$1.comma) { this.raise(this.start, "Comma is not permitted after the rest element"); }
  1943. this.expect(close);
  1944. break
  1945. } else {
  1946. var elem = this.parseMaybeDefault(this.start, this.startLoc);
  1947. this.parseBindingListItem(elem);
  1948. elts.push(elem);
  1949. }
  1950. }
  1951. return elts
  1952. };
  1953. pp$2.parseBindingListItem = function(param) {
  1954. return param
  1955. };
  1956. // Parses assignment pattern around given atom if possible.
  1957. pp$2.parseMaybeDefault = function(startPos, startLoc, left) {
  1958. left = left || this.parseBindingAtom();
  1959. if (this.options.ecmaVersion < 6 || !this.eat(types$1.eq)) { return left }
  1960. var node = this.startNodeAt(startPos, startLoc);
  1961. node.left = left;
  1962. node.right = this.parseMaybeAssign();
  1963. return this.finishNode(node, "AssignmentPattern")
  1964. };
  1965. // Verify that a node is an lval — something that can be assigned
  1966. // to.
  1967. // bindingType can be either:
  1968. // 'var' indicating that the lval creates a 'var' binding
  1969. // 'let' indicating that the lval creates a lexical ('let' or 'const') binding
  1970. // 'none' indicating that the binding should be checked for illegal identifiers, but not for duplicate references
  1971. pp$2.checkLVal = function(expr, bindingType, checkClashes) {
  1972. if ( bindingType === void 0 ) bindingType = BIND_NONE;
  1973. switch (expr.type) {
  1974. case "Identifier":
  1975. if (bindingType === BIND_LEXICAL && expr.name === "let")
  1976. { this.raiseRecoverable(expr.start, "let is disallowed as a lexically bound name"); }
  1977. if (this.strict && this.reservedWordsStrictBind.test(expr.name))
  1978. { this.raiseRecoverable(expr.start, (bindingType ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); }
  1979. if (checkClashes) {
  1980. if (has(checkClashes, expr.name))
  1981. { this.raiseRecoverable(expr.start, "Argument name clash"); }
  1982. checkClashes[expr.name] = true;
  1983. }
  1984. if (bindingType !== BIND_NONE && bindingType !== BIND_OUTSIDE) { this.declareName(expr.name, bindingType, expr.start); }
  1985. break
  1986. case "MemberExpression":
  1987. if (bindingType) { this.raiseRecoverable(expr.start, "Binding member expression"); }
  1988. break
  1989. case "ObjectPattern":
  1990. for (var i = 0, list = expr.properties; i < list.length; i += 1)
  1991. {
  1992. var prop = list[i];
  1993. this.checkLVal(prop, bindingType, checkClashes);
  1994. }
  1995. break
  1996. case "Property":
  1997. // AssignmentProperty has type === "Property"
  1998. this.checkLVal(expr.value, bindingType, checkClashes);
  1999. break
  2000. case "ArrayPattern":
  2001. for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {
  2002. var elem = list$1[i$1];
  2003. if (elem) { this.checkLVal(elem, bindingType, checkClashes); }
  2004. }
  2005. break
  2006. case "AssignmentPattern":
  2007. this.checkLVal(expr.left, bindingType, checkClashes);
  2008. break
  2009. case "RestElement":
  2010. this.checkLVal(expr.argument, bindingType, checkClashes);
  2011. break
  2012. case "ParenthesizedExpression":
  2013. this.checkLVal(expr.expression, bindingType, checkClashes);
  2014. break
  2015. default:
  2016. this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue");
  2017. }
  2018. };
  2019. // A recursive descent parser operates by defining functions for all
  2020. var pp$3 = Parser.prototype;
  2021. // Check if property name clashes with already added.
  2022. // Object/class getters and setters are not allowed to clash —
  2023. // either with each other or with an init property — and in
  2024. // strict mode, init properties are also not allowed to be repeated.
  2025. pp$3.checkPropClash = function(prop, propHash, refDestructuringErrors) {
  2026. if (this.options.ecmaVersion >= 9 && prop.type === "SpreadElement")
  2027. { return }
  2028. if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))
  2029. { return }
  2030. var key = prop.key;
  2031. var name;
  2032. switch (key.type) {
  2033. case "Identifier": name = key.name; break
  2034. case "Literal": name = String(key.value); break
  2035. default: return
  2036. }
  2037. var kind = prop.kind;
  2038. if (this.options.ecmaVersion >= 6) {
  2039. if (name === "__proto__" && kind === "init") {
  2040. if (propHash.proto) {
  2041. if (refDestructuringErrors && refDestructuringErrors.doubleProto < 0) { refDestructuringErrors.doubleProto = key.start; }
  2042. // Backwards-compat kludge. Can be removed in version 6.0
  2043. else { this.raiseRecoverable(key.start, "Redefinition of __proto__ property"); }
  2044. }
  2045. propHash.proto = true;
  2046. }
  2047. return
  2048. }
  2049. name = "$" + name;
  2050. var other = propHash[name];
  2051. if (other) {
  2052. var redefinition;
  2053. if (kind === "init") {
  2054. redefinition = this.strict && other.init || other.get || other.set;
  2055. } else {
  2056. redefinition = other.init || other[kind];
  2057. }
  2058. if (redefinition)
  2059. { this.raiseRecoverable(key.start, "Redefinition of property"); }
  2060. } else {
  2061. other = propHash[name] = {
  2062. init: false,
  2063. get: false,
  2064. set: false
  2065. };
  2066. }
  2067. other[kind] = true;
  2068. };
  2069. // ### Expression parsing
  2070. // These nest, from the most general expression type at the top to
  2071. // 'atomic', nondivisible expression types at the bottom. Most of
  2072. // the functions will simply let the function(s) below them parse,
  2073. // and, *if* the syntactic construct they handle is present, wrap
  2074. // the AST node that the inner parser gave them in another node.
  2075. // Parse a full expression. The optional arguments are used to
  2076. // forbid the `in` operator (in for loops initalization expressions)
  2077. // and provide reference for storing '=' operator inside shorthand
  2078. // property assignment in contexts where both object expression
  2079. // and object pattern might appear (so it's possible to raise
  2080. // delayed syntax error at correct position).
  2081. pp$3.parseExpression = function(noIn, refDestructuringErrors) {
  2082. var startPos = this.start, startLoc = this.startLoc;
  2083. var expr = this.parseMaybeAssign(noIn, refDestructuringErrors);
  2084. if (this.type === types$1.comma) {
  2085. var node = this.startNodeAt(startPos, startLoc);
  2086. node.expressions = [expr];
  2087. while (this.eat(types$1.comma)) { node.expressions.push(this.parseMaybeAssign(noIn, refDestructuringErrors)); }
  2088. return this.finishNode(node, "SequenceExpression")
  2089. }
  2090. return expr
  2091. };
  2092. // Parse an assignment expression. This includes applications of
  2093. // operators like `+=`.
  2094. pp$3.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
  2095. if (this.isContextual("yield")) {
  2096. if (this.inGenerator) { return this.parseYield(noIn) }
  2097. // The tokenizer will assume an expression is allowed after
  2098. // `yield`, but this isn't that kind of yield
  2099. else { this.exprAllowed = false; }
  2100. }
  2101. var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldShorthandAssign = -1;
  2102. if (refDestructuringErrors) {
  2103. oldParenAssign = refDestructuringErrors.parenthesizedAssign;
  2104. oldTrailingComma = refDestructuringErrors.trailingComma;
  2105. oldShorthandAssign = refDestructuringErrors.shorthandAssign;
  2106. refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.shorthandAssign = -1;
  2107. } else {
  2108. refDestructuringErrors = new DestructuringErrors;
  2109. ownDestructuringErrors = true;
  2110. }
  2111. var startPos = this.start, startLoc = this.startLoc;
  2112. if (this.type === types$1.parenL || this.type === types$1.name)
  2113. { this.potentialArrowAt = this.start; }
  2114. var left = this.parseMaybeConditional(noIn, refDestructuringErrors);
  2115. if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }
  2116. if (this.type.isAssign) {
  2117. var node = this.startNodeAt(startPos, startLoc);
  2118. node.operator = this.value;
  2119. node.left = this.type === types$1.eq ? this.toAssignable(left, false, refDestructuringErrors) : left;
  2120. if (!ownDestructuringErrors) { DestructuringErrors.call(refDestructuringErrors); }
  2121. refDestructuringErrors.shorthandAssign = -1; // reset because shorthand default was used correctly
  2122. this.checkLVal(left);
  2123. this.next();
  2124. node.right = this.parseMaybeAssign(noIn);
  2125. return this.finishNode(node, "AssignmentExpression")
  2126. } else {
  2127. if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }
  2128. }
  2129. if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }
  2130. if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }
  2131. if (oldShorthandAssign > -1) { refDestructuringErrors.shorthandAssign = oldShorthandAssign; }
  2132. return left
  2133. };
  2134. // Parse a ternary conditional (`?:`) operator.
  2135. pp$3.parseMaybeConditional = function(noIn, refDestructuringErrors) {
  2136. var startPos = this.start, startLoc = this.startLoc;
  2137. var expr = this.parseExprOps(noIn, refDestructuringErrors);
  2138. if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  2139. if (this.eat(types$1.question)) {
  2140. var node = this.startNodeAt(startPos, startLoc);
  2141. node.test = expr;
  2142. node.consequent = this.parseMaybeAssign();
  2143. this.expect(types$1.colon);
  2144. node.alternate = this.parseMaybeAssign(noIn);
  2145. return this.finishNode(node, "ConditionalExpression")
  2146. }
  2147. return expr
  2148. };
  2149. // Start the precedence parser.
  2150. pp$3.parseExprOps = function(noIn, refDestructuringErrors) {
  2151. var startPos = this.start, startLoc = this.startLoc;
  2152. var expr = this.parseMaybeUnary(refDestructuringErrors, false);
  2153. if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  2154. return expr.start === startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn)
  2155. };
  2156. // Parse binary operators with the operator precedence parsing
  2157. // algorithm. `left` is the left-hand side of the operator.
  2158. // `minPrec` provides context that allows the function to stop and
  2159. // defer further parser to one of its callers when it encounters an
  2160. // operator that has a lower precedence than the set it is parsing.
  2161. pp$3.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
  2162. var prec = this.type.binop;
  2163. if (prec != null && (!noIn || this.type !== types$1._in)) {
  2164. if (prec > minPrec) {
  2165. var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;
  2166. var op = this.value;
  2167. this.next();
  2168. var startPos = this.start, startLoc = this.startLoc;
  2169. var right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn);
  2170. var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical);
  2171. return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
  2172. }
  2173. }
  2174. return left
  2175. };
  2176. pp$3.buildBinary = function(startPos, startLoc, left, right, op, logical) {
  2177. var node = this.startNodeAt(startPos, startLoc);
  2178. node.left = left;
  2179. node.operator = op;
  2180. node.right = right;
  2181. return this.finishNode(node, logical ? "LogicalExpression" : "BinaryExpression")
  2182. };
  2183. // Parse unary operators, both prefix and postfix.
  2184. pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
  2185. var startPos = this.start, startLoc = this.startLoc, expr;
  2186. if (this.isContextual("await") && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))) {
  2187. expr = this.parseAwait();
  2188. sawUnary = true;
  2189. } else if (this.type.prefix) {
  2190. var node = this.startNode(), update = this.type === types$1.incDec;
  2191. node.operator = this.value;
  2192. node.prefix = true;
  2193. this.next();
  2194. node.argument = this.parseMaybeUnary(null, true);
  2195. this.checkExpressionErrors(refDestructuringErrors, true);
  2196. if (update) { this.checkLVal(node.argument); }
  2197. else if (this.strict && node.operator === "delete" &&
  2198. node.argument.type === "Identifier")
  2199. { this.raiseRecoverable(node.start, "Deleting local variable in strict mode"); }
  2200. else { sawUnary = true; }
  2201. expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  2202. } else {
  2203. expr = this.parseExprSubscripts(refDestructuringErrors);
  2204. if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  2205. while (this.type.postfix && !this.canInsertSemicolon()) {
  2206. var node$1 = this.startNodeAt(startPos, startLoc);
  2207. node$1.operator = this.value;
  2208. node$1.prefix = false;
  2209. node$1.argument = expr;
  2210. this.checkLVal(expr);
  2211. this.next();
  2212. expr = this.finishNode(node$1, "UpdateExpression");
  2213. }
  2214. }
  2215. if (!sawUnary && this.eat(types$1.starstar))
  2216. { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false) }
  2217. else
  2218. { return expr }
  2219. };
  2220. // Parse call, dot, and `[]`-subscript expressions.
  2221. pp$3.parseExprSubscripts = function(refDestructuringErrors) {
  2222. var startPos = this.start, startLoc = this.startLoc;
  2223. var expr = this.parseExprAtom(refDestructuringErrors);
  2224. var skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")";
  2225. if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) { return expr }
  2226. var result = this.parseSubscripts(expr, startPos, startLoc);
  2227. if (refDestructuringErrors && result.type === "MemberExpression") {
  2228. if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }
  2229. if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }
  2230. }
  2231. return result
  2232. };
  2233. pp$3.parseSubscripts = function(base, startPos, startLoc, noCalls) {
  2234. var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" &&
  2235. this.lastTokEnd === base.end && !this.canInsertSemicolon() && this.input.slice(base.start, base.end) === "async";
  2236. while (true) {
  2237. var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow);
  2238. if (element === base || element.type === "ArrowFunctionExpression") { return element }
  2239. base = element;
  2240. }
  2241. };
  2242. pp$3.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow) {
  2243. var computed = this.eat(types$1.bracketL);
  2244. if (computed || this.eat(types$1.dot)) {
  2245. var node = this.startNodeAt(startPos, startLoc);
  2246. node.object = base;
  2247. node.property = computed ? this.parseExpression() : this.parseIdent(this.options.allowReserved !== "never");
  2248. node.computed = !!computed;
  2249. if (computed) { this.expect(types$1.bracketR); }
  2250. base = this.finishNode(node, "MemberExpression");
  2251. } else if (!noCalls && this.eat(types$1.parenL)) {
  2252. var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;
  2253. this.yieldPos = 0;
  2254. this.awaitPos = 0;
  2255. this.awaitIdentPos = 0;
  2256. var exprList = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);
  2257. if (maybeAsyncArrow && !this.canInsertSemicolon() && this.eat(types$1.arrow)) {
  2258. this.checkPatternErrors(refDestructuringErrors, false);
  2259. this.checkYieldAwaitInDefaultParams();
  2260. if (this.awaitIdentPos > 0)
  2261. { this.raise(this.awaitIdentPos, "Cannot use 'await' as identifier inside an async function"); }
  2262. this.yieldPos = oldYieldPos;
  2263. this.awaitPos = oldAwaitPos;
  2264. this.awaitIdentPos = oldAwaitIdentPos;
  2265. return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true)
  2266. }
  2267. this.checkExpressionErrors(refDestructuringErrors, true);
  2268. this.yieldPos = oldYieldPos || this.yieldPos;
  2269. this.awaitPos = oldAwaitPos || this.awaitPos;
  2270. this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;
  2271. var node$1 = this.startNodeAt(startPos, startLoc);
  2272. node$1.callee = base;
  2273. node$1.arguments = exprList;
  2274. base = this.finishNode(node$1, "CallExpression");
  2275. } else if (this.type === types$1.backQuote) {
  2276. var node$2 = this.startNodeAt(startPos, startLoc);
  2277. node$2.tag = base;
  2278. node$2.quasi = this.parseTemplate({isTagged: true});
  2279. base = this.finishNode(node$2, "TaggedTemplateExpression");
  2280. }
  2281. return base
  2282. };
  2283. // Parse an atomic expression — either a single token that is an
  2284. // expression, an expression started by a keyword like `function` or
  2285. // `new`, or an expression wrapped in punctuation like `()`, `[]`,
  2286. // or `{}`.
  2287. pp$3.parseExprAtom = function(refDestructuringErrors) {
  2288. // If a division operator appears in an expression position, the
  2289. // tokenizer got confused, and we force it to read a regexp instead.
  2290. if (this.type === types$1.slash) { this.readRegexp(); }
  2291. var node, canBeArrow = this.potentialArrowAt === this.start;
  2292. switch (this.type) {
  2293. case types$1._super:
  2294. if (!this.allowSuper)
  2295. { this.raise(this.start, "'super' keyword outside a method"); }
  2296. node = this.startNode();
  2297. this.next();
  2298. if (this.type === types$1.parenL && !this.allowDirectSuper)
  2299. { this.raise(node.start, "super() call outside constructor of a subclass"); }
  2300. // The `super` keyword can appear at below:
  2301. // SuperProperty:
  2302. // super [ Expression ]
  2303. // super . IdentifierName
  2304. // SuperCall:
  2305. // super ( Arguments )
  2306. if (this.type !== types$1.dot && this.type !== types$1.bracketL && this.type !== types$1.parenL)
  2307. { this.unexpected(); }
  2308. return this.finishNode(node, "Super")
  2309. case types$1._this:
  2310. node = this.startNode();
  2311. this.next();
  2312. return this.finishNode(node, "ThisExpression")
  2313. case types$1.name:
  2314. var startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc;
  2315. var id = this.parseIdent(false);
  2316. if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === "async" && !this.canInsertSemicolon() && this.eat(types$1._function))
  2317. { return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true) }
  2318. if (canBeArrow && !this.canInsertSemicolon()) {
  2319. if (this.eat(types$1.arrow))
  2320. { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false) }
  2321. if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === types$1.name && !containsEsc) {
  2322. id = this.parseIdent(false);
  2323. if (this.canInsertSemicolon() || !this.eat(types$1.arrow))
  2324. { this.unexpected(); }
  2325. return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true)
  2326. }
  2327. }
  2328. return id
  2329. case types$1.regexp:
  2330. var value = this.value;
  2331. node = this.parseLiteral(value.value);
  2332. node.regex = {pattern: value.pattern, flags: value.flags};
  2333. return node
  2334. case types$1.num: case types$1.string:
  2335. return this.parseLiteral(this.value)
  2336. case types$1._null: case types$1._true: case types$1._false:
  2337. node = this.startNode();
  2338. node.value = this.type === types$1._null ? null : this.type === types$1._true;
  2339. node.raw = this.type.keyword;
  2340. this.next();
  2341. return this.finishNode(node, "Literal")
  2342. case types$1.parenL:
  2343. var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow);
  2344. if (refDestructuringErrors) {
  2345. if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))
  2346. { refDestructuringErrors.parenthesizedAssign = start; }
  2347. if (refDestructuringErrors.parenthesizedBind < 0)
  2348. { refDestructuringErrors.parenthesizedBind = start; }
  2349. }
  2350. return expr
  2351. case types$1.bracketL:
  2352. node = this.startNode();
  2353. this.next();
  2354. node.elements = this.parseExprList(types$1.bracketR, true, true, refDestructuringErrors);
  2355. return this.finishNode(node, "ArrayExpression")
  2356. case types$1.braceL:
  2357. return this.parseObj(false, refDestructuringErrors)
  2358. case types$1._function:
  2359. node = this.startNode();
  2360. this.next();
  2361. return this.parseFunction(node, 0)
  2362. case types$1._class:
  2363. return this.parseClass(this.startNode(), false)
  2364. case types$1._new:
  2365. return this.parseNew()
  2366. case types$1.backQuote:
  2367. return this.parseTemplate()
  2368. case types$1._import:
  2369. if (this.options.ecmaVersion >= 11) {
  2370. return this.parseExprImport()
  2371. } else {
  2372. return this.unexpected()
  2373. }
  2374. default:
  2375. this.unexpected();
  2376. }
  2377. };
  2378. pp$3.parseExprImport = function() {
  2379. var node = this.startNode();
  2380. this.next(); // skip `import`
  2381. switch (this.type) {
  2382. case types$1.parenL:
  2383. return this.parseDynamicImport(node)
  2384. default:
  2385. this.unexpected();
  2386. }
  2387. };
  2388. pp$3.parseDynamicImport = function(node) {
  2389. this.next(); // skip `(`
  2390. // Parse node.source.
  2391. node.source = this.parseMaybeAssign();
  2392. // Verify ending.
  2393. if (!this.eat(types$1.parenR)) {
  2394. var errorPos = this.start;
  2395. if (this.eat(types$1.comma) && this.eat(types$1.parenR)) {
  2396. this.raiseRecoverable(errorPos, "Trailing comma is not allowed in import()");
  2397. } else {
  2398. this.unexpected(errorPos);
  2399. }
  2400. }
  2401. return this.finishNode(node, "ImportExpression")
  2402. };
  2403. pp$3.parseLiteral = function(value) {
  2404. var node = this.startNode();
  2405. node.value = value;
  2406. node.raw = this.input.slice(this.start, this.end);
  2407. if (node.raw.charCodeAt(node.raw.length - 1) === 110) { node.bigint = node.raw.slice(0, -1); }
  2408. this.next();
  2409. return this.finishNode(node, "Literal")
  2410. };
  2411. pp$3.parseParenExpression = function() {
  2412. this.expect(types$1.parenL);
  2413. var val = this.parseExpression();
  2414. this.expect(types$1.parenR);
  2415. return val
  2416. };
  2417. pp$3.parseParenAndDistinguishExpression = function(canBeArrow) {
  2418. var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;
  2419. if (this.options.ecmaVersion >= 6) {
  2420. this.next();
  2421. var innerStartPos = this.start, innerStartLoc = this.startLoc;
  2422. var exprList = [], first = true, lastIsComma = false;
  2423. var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart;
  2424. this.yieldPos = 0;
  2425. this.awaitPos = 0;
  2426. // Do not save awaitIdentPos to allow checking awaits nested in parameters
  2427. while (this.type !== types$1.parenR) {
  2428. first ? first = false : this.expect(types$1.comma);
  2429. if (allowTrailingComma && this.afterTrailingComma(types$1.parenR, true)) {
  2430. lastIsComma = true;
  2431. break
  2432. } else if (this.type === types$1.ellipsis) {
  2433. spreadStart = this.start;
  2434. exprList.push(this.parseParenItem(this.parseRestBinding()));
  2435. if (this.type === types$1.comma) { this.raise(this.start, "Comma is not permitted after the rest element"); }
  2436. break
  2437. } else {
  2438. exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));
  2439. }
  2440. }
  2441. var innerEndPos = this.start, innerEndLoc = this.startLoc;
  2442. this.expect(types$1.parenR);
  2443. if (canBeArrow && !this.canInsertSemicolon() && this.eat(types$1.arrow)) {
  2444. this.checkPatternErrors(refDestructuringErrors, false);
  2445. this.checkYieldAwaitInDefaultParams();
  2446. this.yieldPos = oldYieldPos;
  2447. this.awaitPos = oldAwaitPos;
  2448. return this.parseParenArrowList(startPos, startLoc, exprList)
  2449. }
  2450. if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }
  2451. if (spreadStart) { this.unexpected(spreadStart); }
  2452. this.checkExpressionErrors(refDestructuringErrors, true);
  2453. this.yieldPos = oldYieldPos || this.yieldPos;
  2454. this.awaitPos = oldAwaitPos || this.awaitPos;
  2455. if (exprList.length > 1) {
  2456. val = this.startNodeAt(innerStartPos, innerStartLoc);
  2457. val.expressions = exprList;
  2458. this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
  2459. } else {
  2460. val = exprList[0];
  2461. }
  2462. } else {
  2463. val = this.parseParenExpression();
  2464. }
  2465. if (this.options.preserveParens) {
  2466. var par = this.startNodeAt(startPos, startLoc);
  2467. par.expression = val;
  2468. return this.finishNode(par, "ParenthesizedExpression")
  2469. } else {
  2470. return val
  2471. }
  2472. };
  2473. pp$3.parseParenItem = function(item) {
  2474. return item
  2475. };
  2476. pp$3.parseParenArrowList = function(startPos, startLoc, exprList) {
  2477. return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
  2478. };
  2479. // New's precedence is slightly tricky. It must allow its argument to
  2480. // be a `[]` or dot subscript expression, but not a call — at least,
  2481. // not without wrapping it in parentheses. Thus, it uses the noCalls
  2482. // argument to parseSubscripts to prevent it from consuming the
  2483. // argument list.
  2484. var empty$1 = [];
  2485. pp$3.parseNew = function() {
  2486. var node = this.startNode();
  2487. var meta = this.parseIdent(true);
  2488. if (this.options.ecmaVersion >= 6 && this.eat(types$1.dot)) {
  2489. node.meta = meta;
  2490. var containsEsc = this.containsEsc;
  2491. node.property = this.parseIdent(true);
  2492. if (node.property.name !== "target" || containsEsc)
  2493. { this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target"); }
  2494. if (!this.inNonArrowFunction())
  2495. { this.raiseRecoverable(node.start, "new.target can only be used in functions"); }
  2496. return this.finishNode(node, "MetaProperty")
  2497. }
  2498. var startPos = this.start, startLoc = this.startLoc, isImport = this.type === types$1._import;
  2499. node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
  2500. if (isImport && node.callee.type === "ImportExpression") {
  2501. this.raise(startPos, "Cannot use new with import()");
  2502. }
  2503. if (this.eat(types$1.parenL)) { node.arguments = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false); }
  2504. else { node.arguments = empty$1; }
  2505. return this.finishNode(node, "NewExpression")
  2506. };
  2507. // Parse template expression.
  2508. pp$3.parseTemplateElement = function(ref) {
  2509. var isTagged = ref.isTagged;
  2510. var elem = this.startNode();
  2511. if (this.type === types$1.invalidTemplate) {
  2512. if (!isTagged) {
  2513. this.raiseRecoverable(this.start, "Bad escape sequence in untagged template literal");
  2514. }
  2515. elem.value = {
  2516. raw: this.value,
  2517. cooked: null
  2518. };
  2519. } else {
  2520. elem.value = {
  2521. raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
  2522. cooked: this.value
  2523. };
  2524. }
  2525. this.next();
  2526. elem.tail = this.type === types$1.backQuote;
  2527. return this.finishNode(elem, "TemplateElement")
  2528. };
  2529. pp$3.parseTemplate = function(ref) {
  2530. if ( ref === void 0 ) ref = {};
  2531. var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;
  2532. var node = this.startNode();
  2533. this.next();
  2534. node.expressions = [];
  2535. var curElt = this.parseTemplateElement({isTagged: isTagged});
  2536. node.quasis = [curElt];
  2537. while (!curElt.tail) {
  2538. if (this.type === types$1.eof) { this.raise(this.pos, "Unterminated template literal"); }
  2539. this.expect(types$1.dollarBraceL);
  2540. node.expressions.push(this.parseExpression());
  2541. this.expect(types$1.braceR);
  2542. node.quasis.push(curElt = this.parseTemplateElement({isTagged: isTagged}));
  2543. }
  2544. this.next();
  2545. return this.finishNode(node, "TemplateLiteral")
  2546. };
  2547. pp$3.isAsyncProp = function(prop) {
  2548. return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" &&
  2549. (this.type === types$1.name || this.type === types$1.num || this.type === types$1.string || this.type === types$1.bracketL || this.type.keyword || (this.options.ecmaVersion >= 9 && this.type === types$1.star)) &&
  2550. !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
  2551. };
  2552. // Parse an object literal or binding pattern.
  2553. pp$3.parseObj = function(isPattern, refDestructuringErrors) {
  2554. var node = this.startNode(), first = true, propHash = {};
  2555. node.properties = [];
  2556. this.next();
  2557. while (!this.eat(types$1.braceR)) {
  2558. if (!first) {
  2559. this.expect(types$1.comma);
  2560. if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types$1.braceR)) { break }
  2561. } else { first = false; }
  2562. var prop = this.parseProperty(isPattern, refDestructuringErrors);
  2563. if (!isPattern) { this.checkPropClash(prop, propHash, refDestructuringErrors); }
  2564. node.properties.push(prop);
  2565. }
  2566. return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
  2567. };
  2568. pp$3.parseProperty = function(isPattern, refDestructuringErrors) {
  2569. var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;
  2570. if (this.options.ecmaVersion >= 9 && this.eat(types$1.ellipsis)) {
  2571. if (isPattern) {
  2572. prop.argument = this.parseIdent(false);
  2573. if (this.type === types$1.comma) {
  2574. this.raise(this.start, "Comma is not permitted after the rest element");
  2575. }
  2576. return this.finishNode(prop, "RestElement")
  2577. }
  2578. // To disallow parenthesized identifier via `this.toAssignable()`.
  2579. if (this.type === types$1.parenL && refDestructuringErrors) {
  2580. if (refDestructuringErrors.parenthesizedAssign < 0) {
  2581. refDestructuringErrors.parenthesizedAssign = this.start;
  2582. }
  2583. if (refDestructuringErrors.parenthesizedBind < 0) {
  2584. refDestructuringErrors.parenthesizedBind = this.start;
  2585. }
  2586. }
  2587. // Parse argument.
  2588. prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);
  2589. // To disallow trailing comma via `this.toAssignable()`.
  2590. if (this.type === types$1.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {
  2591. refDestructuringErrors.trailingComma = this.start;
  2592. }
  2593. // Finish
  2594. return this.finishNode(prop, "SpreadElement")
  2595. }
  2596. if (this.options.ecmaVersion >= 6) {
  2597. prop.method = false;
  2598. prop.shorthand = false;
  2599. if (isPattern || refDestructuringErrors) {
  2600. startPos = this.start;
  2601. startLoc = this.startLoc;
  2602. }
  2603. if (!isPattern)
  2604. { isGenerator = this.eat(types$1.star); }
  2605. }
  2606. var containsEsc = this.containsEsc;
  2607. this.parsePropertyName(prop);
  2608. if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {
  2609. isAsync = true;
  2610. isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);
  2611. this.parsePropertyName(prop, refDestructuringErrors);
  2612. } else {
  2613. isAsync = false;
  2614. }
  2615. this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);
  2616. return this.finishNode(prop, "Property")
  2617. };
  2618. pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {
  2619. if ((isGenerator || isAsync) && this.type === types$1.colon)
  2620. { this.unexpected(); }
  2621. if (this.eat(types$1.colon)) {
  2622. prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);
  2623. prop.kind = "init";
  2624. } else if (this.options.ecmaVersion >= 6 && this.type === types$1.parenL) {
  2625. if (isPattern) { this.unexpected(); }
  2626. prop.kind = "init";
  2627. prop.method = true;
  2628. prop.value = this.parseMethod(isGenerator, isAsync);
  2629. } else if (!isPattern && !containsEsc &&
  2630. this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
  2631. (prop.key.name === "get" || prop.key.name === "set") &&
  2632. (this.type !== types$1.comma && this.type !== types$1.braceR)) {
  2633. if (isGenerator || isAsync) { this.unexpected(); }
  2634. prop.kind = prop.key.name;
  2635. this.parsePropertyName(prop);
  2636. prop.value = this.parseMethod(false);
  2637. var paramCount = prop.kind === "get" ? 0 : 1;
  2638. if (prop.value.params.length !== paramCount) {
  2639. var start = prop.value.start;
  2640. if (prop.kind === "get")
  2641. { this.raiseRecoverable(start, "getter should have no params"); }
  2642. else
  2643. { this.raiseRecoverable(start, "setter should have exactly one param"); }
  2644. } else {
  2645. if (prop.kind === "set" && prop.value.params[0].type === "RestElement")
  2646. { this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params"); }
  2647. }
  2648. } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
  2649. if (isGenerator || isAsync) { this.unexpected(); }
  2650. this.checkUnreserved(prop.key);
  2651. if (prop.key.name === "await" && !this.awaitIdentPos)
  2652. { this.awaitIdentPos = startPos; }
  2653. prop.kind = "init";
  2654. if (isPattern) {
  2655. prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
  2656. } else if (this.type === types$1.eq && refDestructuringErrors) {
  2657. if (refDestructuringErrors.shorthandAssign < 0)
  2658. { refDestructuringErrors.shorthandAssign = this.start; }
  2659. prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
  2660. } else {
  2661. prop.value = prop.key;
  2662. }
  2663. prop.shorthand = true;
  2664. } else { this.unexpected(); }
  2665. };
  2666. pp$3.parsePropertyName = function(prop) {
  2667. if (this.options.ecmaVersion >= 6) {
  2668. if (this.eat(types$1.bracketL)) {
  2669. prop.computed = true;
  2670. prop.key = this.parseMaybeAssign();
  2671. this.expect(types$1.bracketR);
  2672. return prop.key
  2673. } else {
  2674. prop.computed = false;
  2675. }
  2676. }
  2677. return prop.key = this.type === types$1.num || this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== "never")
  2678. };
  2679. // Initialize empty function node.
  2680. pp$3.initFunction = function(node) {
  2681. node.id = null;
  2682. if (this.options.ecmaVersion >= 6) { node.generator = node.expression = false; }
  2683. if (this.options.ecmaVersion >= 8) { node.async = false; }
  2684. };
  2685. // Parse object or class method.
  2686. pp$3.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {
  2687. var node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;
  2688. this.initFunction(node);
  2689. if (this.options.ecmaVersion >= 6)
  2690. { node.generator = isGenerator; }
  2691. if (this.options.ecmaVersion >= 8)
  2692. { node.async = !!isAsync; }
  2693. this.yieldPos = 0;
  2694. this.awaitPos = 0;
  2695. this.awaitIdentPos = 0;
  2696. this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0));
  2697. this.expect(types$1.parenL);
  2698. node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);
  2699. this.checkYieldAwaitInDefaultParams();
  2700. this.parseFunctionBody(node, false, true);
  2701. this.yieldPos = oldYieldPos;
  2702. this.awaitPos = oldAwaitPos;
  2703. this.awaitIdentPos = oldAwaitIdentPos;
  2704. return this.finishNode(node, "FunctionExpression")
  2705. };
  2706. // Parse arrow function expression with given parameters.
  2707. pp$3.parseArrowExpression = function(node, params, isAsync) {
  2708. var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;
  2709. this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW);
  2710. this.initFunction(node);
  2711. if (this.options.ecmaVersion >= 8) { node.async = !!isAsync; }
  2712. this.yieldPos = 0;
  2713. this.awaitPos = 0;
  2714. this.awaitIdentPos = 0;
  2715. node.params = this.toAssignableList(params, true);
  2716. this.parseFunctionBody(node, true, false);
  2717. this.yieldPos = oldYieldPos;
  2718. this.awaitPos = oldAwaitPos;
  2719. this.awaitIdentPos = oldAwaitIdentPos;
  2720. return this.finishNode(node, "ArrowFunctionExpression")
  2721. };
  2722. // Parse function body and check parameters.
  2723. pp$3.parseFunctionBody = function(node, isArrowFunction, isMethod) {
  2724. var isExpression = isArrowFunction && this.type !== types$1.braceL;
  2725. var oldStrict = this.strict, useStrict = false;
  2726. if (isExpression) {
  2727. node.body = this.parseMaybeAssign();
  2728. node.expression = true;
  2729. this.checkParams(node, false);
  2730. } else {
  2731. var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);
  2732. if (!oldStrict || nonSimple) {
  2733. useStrict = this.strictDirective(this.end);
  2734. // If this is a strict mode function, verify that argument names
  2735. // are not repeated, and it does not try to bind the words `eval`
  2736. // or `arguments`.
  2737. if (useStrict && nonSimple)
  2738. { this.raiseRecoverable(node.start, "Illegal 'use strict' directive in function with non-simple parameter list"); }
  2739. }
  2740. // Start a new scope with regard to labels and the `inFunction`
  2741. // flag (restore them to their old value afterwards).
  2742. var oldLabels = this.labels;
  2743. this.labels = [];
  2744. if (useStrict) { this.strict = true; }
  2745. // Add the params to varDeclaredNames to ensure that an error is thrown
  2746. // if a let/const declaration in the function clashes with one of the params.
  2747. this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));
  2748. node.body = this.parseBlock(false);
  2749. node.expression = false;
  2750. this.adaptDirectivePrologue(node.body.body);
  2751. this.labels = oldLabels;
  2752. }
  2753. this.exitScope();
  2754. // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
  2755. if (this.strict && node.id) { this.checkLVal(node.id, BIND_OUTSIDE); }
  2756. this.strict = oldStrict;
  2757. };
  2758. pp$3.isSimpleParamList = function(params) {
  2759. for (var i = 0, list = params; i < list.length; i += 1)
  2760. {
  2761. var param = list[i];
  2762. if (param.type !== "Identifier") { return false
  2763. } }
  2764. return true
  2765. };
  2766. // Checks function params for various disallowed patterns such as using "eval"
  2767. // or "arguments" and duplicate parameters.
  2768. pp$3.checkParams = function(node, allowDuplicates) {
  2769. var nameHash = {};
  2770. for (var i = 0, list = node.params; i < list.length; i += 1)
  2771. {
  2772. var param = list[i];
  2773. this.checkLVal(param, BIND_VAR, allowDuplicates ? null : nameHash);
  2774. }
  2775. };
  2776. // Parses a comma-separated list of expressions, and returns them as
  2777. // an array. `close` is the token type that ends the list, and
  2778. // `allowEmpty` can be turned on to allow subsequent commas with
  2779. // nothing in between them to be parsed as `null` (which is needed
  2780. // for array literals).
  2781. pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
  2782. var elts = [], first = true;
  2783. while (!this.eat(close)) {
  2784. if (!first) {
  2785. this.expect(types$1.comma);
  2786. if (allowTrailingComma && this.afterTrailingComma(close)) { break }
  2787. } else { first = false; }
  2788. var elt = (void 0);
  2789. if (allowEmpty && this.type === types$1.comma)
  2790. { elt = null; }
  2791. else if (this.type === types$1.ellipsis) {
  2792. elt = this.parseSpread(refDestructuringErrors);
  2793. if (refDestructuringErrors && this.type === types$1.comma && refDestructuringErrors.trailingComma < 0)
  2794. { refDestructuringErrors.trailingComma = this.start; }
  2795. } else {
  2796. elt = this.parseMaybeAssign(false, refDestructuringErrors);
  2797. }
  2798. elts.push(elt);
  2799. }
  2800. return elts
  2801. };
  2802. pp$3.checkUnreserved = function(ref) {
  2803. var start = ref.start;
  2804. var end = ref.end;
  2805. var name = ref.name;
  2806. if (this.inGenerator && name === "yield")
  2807. { this.raiseRecoverable(start, "Cannot use 'yield' as identifier inside a generator"); }
  2808. if (this.inAsync && name === "await")
  2809. { this.raiseRecoverable(start, "Cannot use 'await' as identifier inside an async function"); }
  2810. if (this.keywords.test(name))
  2811. { this.raise(start, ("Unexpected keyword '" + name + "'")); }
  2812. if (this.options.ecmaVersion < 6 &&
  2813. this.input.slice(start, end).indexOf("\\") !== -1) { return }
  2814. var re = this.strict ? this.reservedWordsStrict : this.reservedWords;
  2815. if (re.test(name)) {
  2816. if (!this.inAsync && name === "await")
  2817. { this.raiseRecoverable(start, "Cannot use keyword 'await' outside an async function"); }
  2818. this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved"));
  2819. }
  2820. };
  2821. // Parse the next token as an identifier. If `liberal` is true (used
  2822. // when parsing properties), it will also convert keywords into
  2823. // identifiers.
  2824. pp$3.parseIdent = function(liberal, isBinding) {
  2825. var node = this.startNode();
  2826. if (this.type === types$1.name) {
  2827. node.name = this.value;
  2828. } else if (this.type.keyword) {
  2829. node.name = this.type.keyword;
  2830. // To fix https://github.com/acornjs/acorn/issues/575
  2831. // `class` and `function` keywords push new context into this.context.
  2832. // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
  2833. // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
  2834. if ((node.name === "class" || node.name === "function") &&
  2835. (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {
  2836. this.context.pop();
  2837. }
  2838. } else {
  2839. this.unexpected();
  2840. }
  2841. this.next();
  2842. this.finishNode(node, "Identifier");
  2843. if (!liberal) {
  2844. this.checkUnreserved(node);
  2845. if (node.name === "await" && !this.awaitIdentPos)
  2846. { this.awaitIdentPos = node.start; }
  2847. }
  2848. return node
  2849. };
  2850. // Parses yield expression inside generator.
  2851. pp$3.parseYield = function(noIn) {
  2852. if (!this.yieldPos) { this.yieldPos = this.start; }
  2853. var node = this.startNode();
  2854. this.next();
  2855. if (this.type === types$1.semi || this.canInsertSemicolon() || (this.type !== types$1.star && !this.type.startsExpr)) {
  2856. node.delegate = false;
  2857. node.argument = null;
  2858. } else {
  2859. node.delegate = this.eat(types$1.star);
  2860. node.argument = this.parseMaybeAssign(noIn);
  2861. }
  2862. return this.finishNode(node, "YieldExpression")
  2863. };
  2864. pp$3.parseAwait = function() {
  2865. if (!this.awaitPos) { this.awaitPos = this.start; }
  2866. var node = this.startNode();
  2867. this.next();
  2868. node.argument = this.parseMaybeUnary(null, true);
  2869. return this.finishNode(node, "AwaitExpression")
  2870. };
  2871. var pp$4 = Parser.prototype;
  2872. // This function is used to raise exceptions on parse errors. It
  2873. // takes an offset integer (into the current `input`) to indicate
  2874. // the location of the error, attaches the position to the end
  2875. // of the error message, and then raises a `SyntaxError` with that
  2876. // message.
  2877. pp$4.raise = function(pos, message) {
  2878. var loc = getLineInfo(this.input, pos);
  2879. message += " (" + loc.line + ":" + loc.column + ")";
  2880. var err = new SyntaxError(message);
  2881. err.pos = pos; err.loc = loc; err.raisedAt = this.pos;
  2882. throw err
  2883. };
  2884. pp$4.raiseRecoverable = pp$4.raise;
  2885. pp$4.curPosition = function() {
  2886. if (this.options.locations) {
  2887. return new Position(this.curLine, this.pos - this.lineStart)
  2888. }
  2889. };
  2890. var pp$5 = Parser.prototype;
  2891. var Scope = function Scope(flags) {
  2892. this.flags = flags;
  2893. // A list of var-declared names in the current lexical scope
  2894. this.var = [];
  2895. // A list of lexically-declared names in the current lexical scope
  2896. this.lexical = [];
  2897. // A list of lexically-declared FunctionDeclaration names in the current lexical scope
  2898. this.functions = [];
  2899. };
  2900. // The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.
  2901. pp$5.enterScope = function(flags) {
  2902. this.scopeStack.push(new Scope(flags));
  2903. };
  2904. pp$5.exitScope = function() {
  2905. this.scopeStack.pop();
  2906. };
  2907. // The spec says:
  2908. // > At the top level of a function, or script, function declarations are
  2909. // > treated like var declarations rather than like lexical declarations.
  2910. pp$5.treatFunctionsAsVarInScope = function(scope) {
  2911. return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)
  2912. };
  2913. pp$5.declareName = function(name, bindingType, pos) {
  2914. var redeclared = false;
  2915. if (bindingType === BIND_LEXICAL) {
  2916. var scope = this.currentScope();
  2917. redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1;
  2918. scope.lexical.push(name);
  2919. if (this.inModule && (scope.flags & SCOPE_TOP))
  2920. { delete this.undefinedExports[name]; }
  2921. } else if (bindingType === BIND_SIMPLE_CATCH) {
  2922. var scope$1 = this.currentScope();
  2923. scope$1.lexical.push(name);
  2924. } else if (bindingType === BIND_FUNCTION) {
  2925. var scope$2 = this.currentScope();
  2926. if (this.treatFunctionsAsVar)
  2927. { redeclared = scope$2.lexical.indexOf(name) > -1; }
  2928. else
  2929. { redeclared = scope$2.lexical.indexOf(name) > -1 || scope$2.var.indexOf(name) > -1; }
  2930. scope$2.functions.push(name);
  2931. } else {
  2932. for (var i = this.scopeStack.length - 1; i >= 0; --i) {
  2933. var scope$3 = this.scopeStack[i];
  2934. if (scope$3.lexical.indexOf(name) > -1 && !((scope$3.flags & SCOPE_SIMPLE_CATCH) && scope$3.lexical[0] === name) ||
  2935. !this.treatFunctionsAsVarInScope(scope$3) && scope$3.functions.indexOf(name) > -1) {
  2936. redeclared = true;
  2937. break
  2938. }
  2939. scope$3.var.push(name);
  2940. if (this.inModule && (scope$3.flags & SCOPE_TOP))
  2941. { delete this.undefinedExports[name]; }
  2942. if (scope$3.flags & SCOPE_VAR) { break }
  2943. }
  2944. }
  2945. if (redeclared) { this.raiseRecoverable(pos, ("Identifier '" + name + "' has already been declared")); }
  2946. };
  2947. pp$5.checkLocalExport = function(id) {
  2948. // scope.functions must be empty as Module code is always strict.
  2949. if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&
  2950. this.scopeStack[0].var.indexOf(id.name) === -1) {
  2951. this.undefinedExports[id.name] = id;
  2952. }
  2953. };
  2954. pp$5.currentScope = function() {
  2955. return this.scopeStack[this.scopeStack.length - 1]
  2956. };
  2957. pp$5.currentVarScope = function() {
  2958. for (var i = this.scopeStack.length - 1;; i--) {
  2959. var scope = this.scopeStack[i];
  2960. if (scope.flags & SCOPE_VAR) { return scope }
  2961. }
  2962. };
  2963. // Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.
  2964. pp$5.currentThisScope = function() {
  2965. for (var i = this.scopeStack.length - 1;; i--) {
  2966. var scope = this.scopeStack[i];
  2967. if (scope.flags & SCOPE_VAR && !(scope.flags & SCOPE_ARROW)) { return scope }
  2968. }
  2969. };
  2970. var Node = function Node(parser, pos, loc) {
  2971. this.type = "";
  2972. this.start = pos;
  2973. this.end = 0;
  2974. if (parser.options.locations)
  2975. { this.loc = new SourceLocation(parser, loc); }
  2976. if (parser.options.directSourceFile)
  2977. { this.sourceFile = parser.options.directSourceFile; }
  2978. if (parser.options.ranges)
  2979. { this.range = [pos, 0]; }
  2980. };
  2981. // Start an AST node, attaching a start offset.
  2982. var pp$6 = Parser.prototype;
  2983. pp$6.startNode = function() {
  2984. return new Node(this, this.start, this.startLoc)
  2985. };
  2986. pp$6.startNodeAt = function(pos, loc) {
  2987. return new Node(this, pos, loc)
  2988. };
  2989. // Finish an AST node, adding `type` and `end` properties.
  2990. function finishNodeAt(node, type, pos, loc) {
  2991. node.type = type;
  2992. node.end = pos;
  2993. if (this.options.locations)
  2994. { node.loc.end = loc; }
  2995. if (this.options.ranges)
  2996. { node.range[1] = pos; }
  2997. return node
  2998. }
  2999. pp$6.finishNode = function(node, type) {
  3000. return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)
  3001. };
  3002. // Finish node at given position
  3003. pp$6.finishNodeAt = function(node, type, pos, loc) {
  3004. return finishNodeAt.call(this, node, type, pos, loc)
  3005. };
  3006. // The algorithm used to determine whether a regexp can appear at a
  3007. var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {
  3008. this.token = token;
  3009. this.isExpr = !!isExpr;
  3010. this.preserveSpace = !!preserveSpace;
  3011. this.override = override;
  3012. this.generator = !!generator;
  3013. };
  3014. var types$1$1 = {
  3015. b_stat: new TokContext("{", false),
  3016. b_expr: new TokContext("{", true),
  3017. b_tmpl: new TokContext("${", false),
  3018. p_stat: new TokContext("(", false),
  3019. p_expr: new TokContext("(", true),
  3020. q_tmpl: new TokContext("`", true, true, function (p) { return p.tryReadTemplateToken(); }),
  3021. f_stat: new TokContext("function", false),
  3022. f_expr: new TokContext("function", true),
  3023. f_expr_gen: new TokContext("function", true, false, null, true),
  3024. f_gen: new TokContext("function", false, false, null, true)
  3025. };
  3026. var pp$7 = Parser.prototype;
  3027. pp$7.initialContext = function() {
  3028. return [types$1$1.b_stat]
  3029. };
  3030. pp$7.braceIsBlock = function(prevType) {
  3031. var parent = this.curContext();
  3032. if (parent === types$1$1.f_expr || parent === types$1$1.f_stat)
  3033. { return true }
  3034. if (prevType === types$1.colon && (parent === types$1$1.b_stat || parent === types$1$1.b_expr))
  3035. { return !parent.isExpr }
  3036. // The check for `tt.name && exprAllowed` detects whether we are
  3037. // after a `yield` or `of` construct. See the `updateContext` for
  3038. // `tt.name`.
  3039. if (prevType === types$1._return || prevType === types$1.name && this.exprAllowed)
  3040. { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }
  3041. if (prevType === types$1._else || prevType === types$1.semi || prevType === types$1.eof || prevType === types$1.parenR || prevType === types$1.arrow)
  3042. { return true }
  3043. if (prevType === types$1.braceL)
  3044. { return parent === types$1$1.b_stat }
  3045. if (prevType === types$1._var || prevType === types$1._const || prevType === types$1.name)
  3046. { return false }
  3047. return !this.exprAllowed
  3048. };
  3049. pp$7.inGeneratorContext = function() {
  3050. for (var i = this.context.length - 1; i >= 1; i--) {
  3051. var context = this.context[i];
  3052. if (context.token === "function")
  3053. { return context.generator }
  3054. }
  3055. return false
  3056. };
  3057. pp$7.updateContext = function(prevType) {
  3058. var update, type = this.type;
  3059. if (type.keyword && prevType === types$1.dot)
  3060. { this.exprAllowed = false; }
  3061. else if (update = type.updateContext)
  3062. { update.call(this, prevType); }
  3063. else
  3064. { this.exprAllowed = type.beforeExpr; }
  3065. };
  3066. // Token-specific context update code
  3067. types$1.parenR.updateContext = types$1.braceR.updateContext = function() {
  3068. if (this.context.length === 1) {
  3069. this.exprAllowed = true;
  3070. return
  3071. }
  3072. var out = this.context.pop();
  3073. if (out === types$1$1.b_stat && this.curContext().token === "function") {
  3074. out = this.context.pop();
  3075. }
  3076. this.exprAllowed = !out.isExpr;
  3077. };
  3078. types$1.braceL.updateContext = function(prevType) {
  3079. this.context.push(this.braceIsBlock(prevType) ? types$1$1.b_stat : types$1$1.b_expr);
  3080. this.exprAllowed = true;
  3081. };
  3082. types$1.dollarBraceL.updateContext = function() {
  3083. this.context.push(types$1$1.b_tmpl);
  3084. this.exprAllowed = true;
  3085. };
  3086. types$1.parenL.updateContext = function(prevType) {
  3087. var statementParens = prevType === types$1._if || prevType === types$1._for || prevType === types$1._with || prevType === types$1._while;
  3088. this.context.push(statementParens ? types$1$1.p_stat : types$1$1.p_expr);
  3089. this.exprAllowed = true;
  3090. };
  3091. types$1.incDec.updateContext = function() {
  3092. // tokExprAllowed stays unchanged
  3093. };
  3094. types$1._function.updateContext = types$1._class.updateContext = function(prevType) {
  3095. if (prevType.beforeExpr && prevType !== types$1.semi && prevType !== types$1._else &&
  3096. !(prevType === types$1._return && lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) &&
  3097. !((prevType === types$1.colon || prevType === types$1.braceL) && this.curContext() === types$1$1.b_stat))
  3098. { this.context.push(types$1$1.f_expr); }
  3099. else
  3100. { this.context.push(types$1$1.f_stat); }
  3101. this.exprAllowed = false;
  3102. };
  3103. types$1.backQuote.updateContext = function() {
  3104. if (this.curContext() === types$1$1.q_tmpl)
  3105. { this.context.pop(); }
  3106. else
  3107. { this.context.push(types$1$1.q_tmpl); }
  3108. this.exprAllowed = false;
  3109. };
  3110. types$1.star.updateContext = function(prevType) {
  3111. if (prevType === types$1._function) {
  3112. var index = this.context.length - 1;
  3113. if (this.context[index] === types$1$1.f_expr)
  3114. { this.context[index] = types$1$1.f_expr_gen; }
  3115. else
  3116. { this.context[index] = types$1$1.f_gen; }
  3117. }
  3118. this.exprAllowed = true;
  3119. };
  3120. types$1.name.updateContext = function(prevType) {
  3121. var allowed = false;
  3122. if (this.options.ecmaVersion >= 6 && prevType !== types$1.dot) {
  3123. if (this.value === "of" && !this.exprAllowed ||
  3124. this.value === "yield" && this.inGeneratorContext())
  3125. { allowed = true; }
  3126. }
  3127. this.exprAllowed = allowed;
  3128. };
  3129. // This file contains Unicode properties extracted from the ECMAScript
  3130. // specification. The lists are extracted like so:
  3131. // $$('#table-binary-unicode-properties > figure > table > tbody > tr > td:nth-child(1) code').map(el => el.innerText)
  3132. // #table-binary-unicode-properties
  3133. var ecma9BinaryProperties = "ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS";
  3134. var ecma10BinaryProperties = ecma9BinaryProperties + " Extended_Pictographic";
  3135. var ecma11BinaryProperties = ecma10BinaryProperties;
  3136. var unicodeBinaryProperties = {
  3137. 9: ecma9BinaryProperties,
  3138. 10: ecma10BinaryProperties,
  3139. 11: ecma11BinaryProperties
  3140. };
  3141. // #table-unicode-general-category-values
  3142. var unicodeGeneralCategoryValues = "Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu";
  3143. // #table-unicode-script-values
  3144. var ecma9ScriptValues = "Adlam Adlm Ahom Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb";
  3145. var ecma10ScriptValues = ecma9ScriptValues + " Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd";
  3146. var ecma11ScriptValues = ecma10ScriptValues + " Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho";
  3147. var unicodeScriptValues = {
  3148. 9: ecma9ScriptValues,
  3149. 10: ecma10ScriptValues,
  3150. 11: ecma11ScriptValues
  3151. };
  3152. var data = {};
  3153. function buildUnicodeData(ecmaVersion) {
  3154. var d = data[ecmaVersion] = {
  3155. binary: wordsRegexp(unicodeBinaryProperties[ecmaVersion] + " " + unicodeGeneralCategoryValues),
  3156. nonBinary: {
  3157. General_Category: wordsRegexp(unicodeGeneralCategoryValues),
  3158. Script: wordsRegexp(unicodeScriptValues[ecmaVersion])
  3159. }
  3160. };
  3161. d.nonBinary.Script_Extensions = d.nonBinary.Script;
  3162. d.nonBinary.gc = d.nonBinary.General_Category;
  3163. d.nonBinary.sc = d.nonBinary.Script;
  3164. d.nonBinary.scx = d.nonBinary.Script_Extensions;
  3165. }
  3166. buildUnicodeData(9);
  3167. buildUnicodeData(10);
  3168. buildUnicodeData(11);
  3169. var pp$8 = Parser.prototype;
  3170. var RegExpValidationState = function RegExpValidationState(parser) {
  3171. this.parser = parser;
  3172. this.validFlags = "gim" + (parser.options.ecmaVersion >= 6 ? "uy" : "") + (parser.options.ecmaVersion >= 9 ? "s" : "");
  3173. this.unicodeProperties = data[parser.options.ecmaVersion >= 11 ? 11 : parser.options.ecmaVersion];
  3174. this.source = "";
  3175. this.flags = "";
  3176. this.start = 0;
  3177. this.switchU = false;
  3178. this.switchN = false;
  3179. this.pos = 0;
  3180. this.lastIntValue = 0;
  3181. this.lastStringValue = "";
  3182. this.lastAssertionIsQuantifiable = false;
  3183. this.numCapturingParens = 0;
  3184. this.maxBackReference = 0;
  3185. this.groupNames = [];
  3186. this.backReferenceNames = [];
  3187. };
  3188. RegExpValidationState.prototype.reset = function reset (start, pattern, flags) {
  3189. var unicode = flags.indexOf("u") !== -1;
  3190. this.start = start | 0;
  3191. this.source = pattern + "";
  3192. this.flags = flags;
  3193. this.switchU = unicode && this.parser.options.ecmaVersion >= 6;
  3194. this.switchN = unicode && this.parser.options.ecmaVersion >= 9;
  3195. };
  3196. RegExpValidationState.prototype.raise = function raise (message) {
  3197. this.parser.raiseRecoverable(this.start, ("Invalid regular expression: /" + (this.source) + "/: " + message));
  3198. };
  3199. // If u flag is given, this returns the code point at the index (it combines a surrogate pair).
  3200. // Otherwise, this returns the code unit of the index (can be a part of a surrogate pair).
  3201. RegExpValidationState.prototype.at = function at (i) {
  3202. var s = this.source;
  3203. var l = s.length;
  3204. if (i >= l) {
  3205. return -1
  3206. }
  3207. var c = s.charCodeAt(i);
  3208. if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {
  3209. return c
  3210. }
  3211. return (c << 10) + s.charCodeAt(i + 1) - 0x35FDC00
  3212. };
  3213. RegExpValidationState.prototype.nextIndex = function nextIndex (i) {
  3214. var s = this.source;
  3215. var l = s.length;
  3216. if (i >= l) {
  3217. return l
  3218. }
  3219. var c = s.charCodeAt(i);
  3220. if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {
  3221. return i + 1
  3222. }
  3223. return i + 2
  3224. };
  3225. RegExpValidationState.prototype.current = function current () {
  3226. return this.at(this.pos)
  3227. };
  3228. RegExpValidationState.prototype.lookahead = function lookahead () {
  3229. return this.at(this.nextIndex(this.pos))
  3230. };
  3231. RegExpValidationState.prototype.advance = function advance () {
  3232. this.pos = this.nextIndex(this.pos);
  3233. };
  3234. RegExpValidationState.prototype.eat = function eat (ch) {
  3235. if (this.current() === ch) {
  3236. this.advance();
  3237. return true
  3238. }
  3239. return false
  3240. };
  3241. function codePointToString(ch) {
  3242. if (ch <= 0xFFFF) { return String.fromCharCode(ch) }
  3243. ch -= 0x10000;
  3244. return String.fromCharCode((ch >> 10) + 0xD800, (ch & 0x03FF) + 0xDC00)
  3245. }
  3246. /**
  3247. * Validate the flags part of a given RegExpLiteral.
  3248. *
  3249. * @param {RegExpValidationState} state The state to validate RegExp.
  3250. * @returns {void}
  3251. */
  3252. pp$8.validateRegExpFlags = function(state) {
  3253. var validFlags = state.validFlags;
  3254. var flags = state.flags;
  3255. for (var i = 0; i < flags.length; i++) {
  3256. var flag = flags.charAt(i);
  3257. if (validFlags.indexOf(flag) === -1) {
  3258. this.raise(state.start, "Invalid regular expression flag");
  3259. }
  3260. if (flags.indexOf(flag, i + 1) > -1) {
  3261. this.raise(state.start, "Duplicate regular expression flag");
  3262. }
  3263. }
  3264. };
  3265. /**
  3266. * Validate the pattern part of a given RegExpLiteral.
  3267. *
  3268. * @param {RegExpValidationState} state The state to validate RegExp.
  3269. * @returns {void}
  3270. */
  3271. pp$8.validateRegExpPattern = function(state) {
  3272. this.regexp_pattern(state);
  3273. // The goal symbol for the parse is |Pattern[~U, ~N]|. If the result of
  3274. // parsing contains a |GroupName|, reparse with the goal symbol
  3275. // |Pattern[~U, +N]| and use this result instead. Throw a *SyntaxError*
  3276. // exception if _P_ did not conform to the grammar, if any elements of _P_
  3277. // were not matched by the parse, or if any Early Error conditions exist.
  3278. if (!state.switchN && this.options.ecmaVersion >= 9 && state.groupNames.length > 0) {
  3279. state.switchN = true;
  3280. this.regexp_pattern(state);
  3281. }
  3282. };
  3283. // https://www.ecma-international.org/ecma-262/8.0/#prod-Pattern
  3284. pp$8.regexp_pattern = function(state) {
  3285. state.pos = 0;
  3286. state.lastIntValue = 0;
  3287. state.lastStringValue = "";
  3288. state.lastAssertionIsQuantifiable = false;
  3289. state.numCapturingParens = 0;
  3290. state.maxBackReference = 0;
  3291. state.groupNames.length = 0;
  3292. state.backReferenceNames.length = 0;
  3293. this.regexp_disjunction(state);
  3294. if (state.pos !== state.source.length) {
  3295. // Make the same messages as V8.
  3296. if (state.eat(0x29 /* ) */)) {
  3297. state.raise("Unmatched ')'");
  3298. }
  3299. if (state.eat(0x5D /* [ */) || state.eat(0x7D /* } */)) {
  3300. state.raise("Lone quantifier brackets");
  3301. }
  3302. }
  3303. if (state.maxBackReference > state.numCapturingParens) {
  3304. state.raise("Invalid escape");
  3305. }
  3306. for (var i = 0, list = state.backReferenceNames; i < list.length; i += 1) {
  3307. var name = list[i];
  3308. if (state.groupNames.indexOf(name) === -1) {
  3309. state.raise("Invalid named capture referenced");
  3310. }
  3311. }
  3312. };
  3313. // https://www.ecma-international.org/ecma-262/8.0/#prod-Disjunction
  3314. pp$8.regexp_disjunction = function(state) {
  3315. this.regexp_alternative(state);
  3316. while (state.eat(0x7C /* | */)) {
  3317. this.regexp_alternative(state);
  3318. }
  3319. // Make the same message as V8.
  3320. if (this.regexp_eatQuantifier(state, true)) {
  3321. state.raise("Nothing to repeat");
  3322. }
  3323. if (state.eat(0x7B /* { */)) {
  3324. state.raise("Lone quantifier brackets");
  3325. }
  3326. };
  3327. // https://www.ecma-international.org/ecma-262/8.0/#prod-Alternative
  3328. pp$8.regexp_alternative = function(state) {
  3329. while (state.pos < state.source.length && this.regexp_eatTerm(state))
  3330. { }
  3331. };
  3332. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Term
  3333. pp$8.regexp_eatTerm = function(state) {
  3334. if (this.regexp_eatAssertion(state)) {
  3335. // Handle `QuantifiableAssertion Quantifier` alternative.
  3336. // `state.lastAssertionIsQuantifiable` is true if the last eaten Assertion
  3337. // is a QuantifiableAssertion.
  3338. if (state.lastAssertionIsQuantifiable && this.regexp_eatQuantifier(state)) {
  3339. // Make the same message as V8.
  3340. if (state.switchU) {
  3341. state.raise("Invalid quantifier");
  3342. }
  3343. }
  3344. return true
  3345. }
  3346. if (state.switchU ? this.regexp_eatAtom(state) : this.regexp_eatExtendedAtom(state)) {
  3347. this.regexp_eatQuantifier(state);
  3348. return true
  3349. }
  3350. return false
  3351. };
  3352. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Assertion
  3353. pp$8.regexp_eatAssertion = function(state) {
  3354. var start = state.pos;
  3355. state.lastAssertionIsQuantifiable = false;
  3356. // ^, $
  3357. if (state.eat(0x5E /* ^ */) || state.eat(0x24 /* $ */)) {
  3358. return true
  3359. }
  3360. // \b \B
  3361. if (state.eat(0x5C /* \ */)) {
  3362. if (state.eat(0x42 /* B */) || state.eat(0x62 /* b */)) {
  3363. return true
  3364. }
  3365. state.pos = start;
  3366. }
  3367. // Lookahead / Lookbehind
  3368. if (state.eat(0x28 /* ( */) && state.eat(0x3F /* ? */)) {
  3369. var lookbehind = false;
  3370. if (this.options.ecmaVersion >= 9) {
  3371. lookbehind = state.eat(0x3C /* < */);
  3372. }
  3373. if (state.eat(0x3D /* = */) || state.eat(0x21 /* ! */)) {
  3374. this.regexp_disjunction(state);
  3375. if (!state.eat(0x29 /* ) */)) {
  3376. state.raise("Unterminated group");
  3377. }
  3378. state.lastAssertionIsQuantifiable = !lookbehind;
  3379. return true
  3380. }
  3381. }
  3382. state.pos = start;
  3383. return false
  3384. };
  3385. // https://www.ecma-international.org/ecma-262/8.0/#prod-Quantifier
  3386. pp$8.regexp_eatQuantifier = function(state, noError) {
  3387. if ( noError === void 0 ) noError = false;
  3388. if (this.regexp_eatQuantifierPrefix(state, noError)) {
  3389. state.eat(0x3F /* ? */);
  3390. return true
  3391. }
  3392. return false
  3393. };
  3394. // https://www.ecma-international.org/ecma-262/8.0/#prod-QuantifierPrefix
  3395. pp$8.regexp_eatQuantifierPrefix = function(state, noError) {
  3396. return (
  3397. state.eat(0x2A /* * */) ||
  3398. state.eat(0x2B /* + */) ||
  3399. state.eat(0x3F /* ? */) ||
  3400. this.regexp_eatBracedQuantifier(state, noError)
  3401. )
  3402. };
  3403. pp$8.regexp_eatBracedQuantifier = function(state, noError) {
  3404. var start = state.pos;
  3405. if (state.eat(0x7B /* { */)) {
  3406. var min = 0, max = -1;
  3407. if (this.regexp_eatDecimalDigits(state)) {
  3408. min = state.lastIntValue;
  3409. if (state.eat(0x2C /* , */) && this.regexp_eatDecimalDigits(state)) {
  3410. max = state.lastIntValue;
  3411. }
  3412. if (state.eat(0x7D /* } */)) {
  3413. // SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-term
  3414. if (max !== -1 && max < min && !noError) {
  3415. state.raise("numbers out of order in {} quantifier");
  3416. }
  3417. return true
  3418. }
  3419. }
  3420. if (state.switchU && !noError) {
  3421. state.raise("Incomplete quantifier");
  3422. }
  3423. state.pos = start;
  3424. }
  3425. return false
  3426. };
  3427. // https://www.ecma-international.org/ecma-262/8.0/#prod-Atom
  3428. pp$8.regexp_eatAtom = function(state) {
  3429. return (
  3430. this.regexp_eatPatternCharacters(state) ||
  3431. state.eat(0x2E /* . */) ||
  3432. this.regexp_eatReverseSolidusAtomEscape(state) ||
  3433. this.regexp_eatCharacterClass(state) ||
  3434. this.regexp_eatUncapturingGroup(state) ||
  3435. this.regexp_eatCapturingGroup(state)
  3436. )
  3437. };
  3438. pp$8.regexp_eatReverseSolidusAtomEscape = function(state) {
  3439. var start = state.pos;
  3440. if (state.eat(0x5C /* \ */)) {
  3441. if (this.regexp_eatAtomEscape(state)) {
  3442. return true
  3443. }
  3444. state.pos = start;
  3445. }
  3446. return false
  3447. };
  3448. pp$8.regexp_eatUncapturingGroup = function(state) {
  3449. var start = state.pos;
  3450. if (state.eat(0x28 /* ( */)) {
  3451. if (state.eat(0x3F /* ? */) && state.eat(0x3A /* : */)) {
  3452. this.regexp_disjunction(state);
  3453. if (state.eat(0x29 /* ) */)) {
  3454. return true
  3455. }
  3456. state.raise("Unterminated group");
  3457. }
  3458. state.pos = start;
  3459. }
  3460. return false
  3461. };
  3462. pp$8.regexp_eatCapturingGroup = function(state) {
  3463. if (state.eat(0x28 /* ( */)) {
  3464. if (this.options.ecmaVersion >= 9) {
  3465. this.regexp_groupSpecifier(state);
  3466. } else if (state.current() === 0x3F /* ? */) {
  3467. state.raise("Invalid group");
  3468. }
  3469. this.regexp_disjunction(state);
  3470. if (state.eat(0x29 /* ) */)) {
  3471. state.numCapturingParens += 1;
  3472. return true
  3473. }
  3474. state.raise("Unterminated group");
  3475. }
  3476. return false
  3477. };
  3478. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedAtom
  3479. pp$8.regexp_eatExtendedAtom = function(state) {
  3480. return (
  3481. state.eat(0x2E /* . */) ||
  3482. this.regexp_eatReverseSolidusAtomEscape(state) ||
  3483. this.regexp_eatCharacterClass(state) ||
  3484. this.regexp_eatUncapturingGroup(state) ||
  3485. this.regexp_eatCapturingGroup(state) ||
  3486. this.regexp_eatInvalidBracedQuantifier(state) ||
  3487. this.regexp_eatExtendedPatternCharacter(state)
  3488. )
  3489. };
  3490. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-InvalidBracedQuantifier
  3491. pp$8.regexp_eatInvalidBracedQuantifier = function(state) {
  3492. if (this.regexp_eatBracedQuantifier(state, true)) {
  3493. state.raise("Nothing to repeat");
  3494. }
  3495. return false
  3496. };
  3497. // https://www.ecma-international.org/ecma-262/8.0/#prod-SyntaxCharacter
  3498. pp$8.regexp_eatSyntaxCharacter = function(state) {
  3499. var ch = state.current();
  3500. if (isSyntaxCharacter(ch)) {
  3501. state.lastIntValue = ch;
  3502. state.advance();
  3503. return true
  3504. }
  3505. return false
  3506. };
  3507. function isSyntaxCharacter(ch) {
  3508. return (
  3509. ch === 0x24 /* $ */ ||
  3510. ch >= 0x28 /* ( */ && ch <= 0x2B /* + */ ||
  3511. ch === 0x2E /* . */ ||
  3512. ch === 0x3F /* ? */ ||
  3513. ch >= 0x5B /* [ */ && ch <= 0x5E /* ^ */ ||
  3514. ch >= 0x7B /* { */ && ch <= 0x7D /* } */
  3515. )
  3516. }
  3517. // https://www.ecma-international.org/ecma-262/8.0/#prod-PatternCharacter
  3518. // But eat eager.
  3519. pp$8.regexp_eatPatternCharacters = function(state) {
  3520. var start = state.pos;
  3521. var ch = 0;
  3522. while ((ch = state.current()) !== -1 && !isSyntaxCharacter(ch)) {
  3523. state.advance();
  3524. }
  3525. return state.pos !== start
  3526. };
  3527. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedPatternCharacter
  3528. pp$8.regexp_eatExtendedPatternCharacter = function(state) {
  3529. var ch = state.current();
  3530. if (
  3531. ch !== -1 &&
  3532. ch !== 0x24 /* $ */ &&
  3533. !(ch >= 0x28 /* ( */ && ch <= 0x2B /* + */) &&
  3534. ch !== 0x2E /* . */ &&
  3535. ch !== 0x3F /* ? */ &&
  3536. ch !== 0x5B /* [ */ &&
  3537. ch !== 0x5E /* ^ */ &&
  3538. ch !== 0x7C /* | */
  3539. ) {
  3540. state.advance();
  3541. return true
  3542. }
  3543. return false
  3544. };
  3545. // GroupSpecifier[U] ::
  3546. // [empty]
  3547. // `?` GroupName[?U]
  3548. pp$8.regexp_groupSpecifier = function(state) {
  3549. if (state.eat(0x3F /* ? */)) {
  3550. if (this.regexp_eatGroupName(state)) {
  3551. if (state.groupNames.indexOf(state.lastStringValue) !== -1) {
  3552. state.raise("Duplicate capture group name");
  3553. }
  3554. state.groupNames.push(state.lastStringValue);
  3555. return
  3556. }
  3557. state.raise("Invalid group");
  3558. }
  3559. };
  3560. // GroupName[U] ::
  3561. // `<` RegExpIdentifierName[?U] `>`
  3562. // Note: this updates `state.lastStringValue` property with the eaten name.
  3563. pp$8.regexp_eatGroupName = function(state) {
  3564. state.lastStringValue = "";
  3565. if (state.eat(0x3C /* < */)) {
  3566. if (this.regexp_eatRegExpIdentifierName(state) && state.eat(0x3E /* > */)) {
  3567. return true
  3568. }
  3569. state.raise("Invalid capture group name");
  3570. }
  3571. return false
  3572. };
  3573. // RegExpIdentifierName[U] ::
  3574. // RegExpIdentifierStart[?U]
  3575. // RegExpIdentifierName[?U] RegExpIdentifierPart[?U]
  3576. // Note: this updates `state.lastStringValue` property with the eaten name.
  3577. pp$8.regexp_eatRegExpIdentifierName = function(state) {
  3578. state.lastStringValue = "";
  3579. if (this.regexp_eatRegExpIdentifierStart(state)) {
  3580. state.lastStringValue += codePointToString(state.lastIntValue);
  3581. while (this.regexp_eatRegExpIdentifierPart(state)) {
  3582. state.lastStringValue += codePointToString(state.lastIntValue);
  3583. }
  3584. return true
  3585. }
  3586. return false
  3587. };
  3588. // RegExpIdentifierStart[U] ::
  3589. // UnicodeIDStart
  3590. // `$`
  3591. // `_`
  3592. // `\` RegExpUnicodeEscapeSequence[?U]
  3593. pp$8.regexp_eatRegExpIdentifierStart = function(state) {
  3594. var start = state.pos;
  3595. var ch = state.current();
  3596. state.advance();
  3597. if (ch === 0x5C /* \ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state)) {
  3598. ch = state.lastIntValue;
  3599. }
  3600. if (isRegExpIdentifierStart(ch)) {
  3601. state.lastIntValue = ch;
  3602. return true
  3603. }
  3604. state.pos = start;
  3605. return false
  3606. };
  3607. function isRegExpIdentifierStart(ch) {
  3608. return isIdentifierStart(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */
  3609. }
  3610. // RegExpIdentifierPart[U] ::
  3611. // UnicodeIDContinue
  3612. // `$`
  3613. // `_`
  3614. // `\` RegExpUnicodeEscapeSequence[?U]
  3615. // <ZWNJ>
  3616. // <ZWJ>
  3617. pp$8.regexp_eatRegExpIdentifierPart = function(state) {
  3618. var start = state.pos;
  3619. var ch = state.current();
  3620. state.advance();
  3621. if (ch === 0x5C /* \ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state)) {
  3622. ch = state.lastIntValue;
  3623. }
  3624. if (isRegExpIdentifierPart(ch)) {
  3625. state.lastIntValue = ch;
  3626. return true
  3627. }
  3628. state.pos = start;
  3629. return false
  3630. };
  3631. function isRegExpIdentifierPart(ch) {
  3632. return isIdentifierChar(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */ || ch === 0x200C /* <ZWNJ> */ || ch === 0x200D /* <ZWJ> */
  3633. }
  3634. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-AtomEscape
  3635. pp$8.regexp_eatAtomEscape = function(state) {
  3636. if (
  3637. this.regexp_eatBackReference(state) ||
  3638. this.regexp_eatCharacterClassEscape(state) ||
  3639. this.regexp_eatCharacterEscape(state) ||
  3640. (state.switchN && this.regexp_eatKGroupName(state))
  3641. ) {
  3642. return true
  3643. }
  3644. if (state.switchU) {
  3645. // Make the same message as V8.
  3646. if (state.current() === 0x63 /* c */) {
  3647. state.raise("Invalid unicode escape");
  3648. }
  3649. state.raise("Invalid escape");
  3650. }
  3651. return false
  3652. };
  3653. pp$8.regexp_eatBackReference = function(state) {
  3654. var start = state.pos;
  3655. if (this.regexp_eatDecimalEscape(state)) {
  3656. var n = state.lastIntValue;
  3657. if (state.switchU) {
  3658. // For SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-atomescape
  3659. if (n > state.maxBackReference) {
  3660. state.maxBackReference = n;
  3661. }
  3662. return true
  3663. }
  3664. if (n <= state.numCapturingParens) {
  3665. return true
  3666. }
  3667. state.pos = start;
  3668. }
  3669. return false
  3670. };
  3671. pp$8.regexp_eatKGroupName = function(state) {
  3672. if (state.eat(0x6B /* k */)) {
  3673. if (this.regexp_eatGroupName(state)) {
  3674. state.backReferenceNames.push(state.lastStringValue);
  3675. return true
  3676. }
  3677. state.raise("Invalid named reference");
  3678. }
  3679. return false
  3680. };
  3681. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-CharacterEscape
  3682. pp$8.regexp_eatCharacterEscape = function(state) {
  3683. return (
  3684. this.regexp_eatControlEscape(state) ||
  3685. this.regexp_eatCControlLetter(state) ||
  3686. this.regexp_eatZero(state) ||
  3687. this.regexp_eatHexEscapeSequence(state) ||
  3688. this.regexp_eatRegExpUnicodeEscapeSequence(state) ||
  3689. (!state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state)) ||
  3690. this.regexp_eatIdentityEscape(state)
  3691. )
  3692. };
  3693. pp$8.regexp_eatCControlLetter = function(state) {
  3694. var start = state.pos;
  3695. if (state.eat(0x63 /* c */)) {
  3696. if (this.regexp_eatControlLetter(state)) {
  3697. return true
  3698. }
  3699. state.pos = start;
  3700. }
  3701. return false
  3702. };
  3703. pp$8.regexp_eatZero = function(state) {
  3704. if (state.current() === 0x30 /* 0 */ && !isDecimalDigit(state.lookahead())) {
  3705. state.lastIntValue = 0;
  3706. state.advance();
  3707. return true
  3708. }
  3709. return false
  3710. };
  3711. // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlEscape
  3712. pp$8.regexp_eatControlEscape = function(state) {
  3713. var ch = state.current();
  3714. if (ch === 0x74 /* t */) {
  3715. state.lastIntValue = 0x09; /* \t */
  3716. state.advance();
  3717. return true
  3718. }
  3719. if (ch === 0x6E /* n */) {
  3720. state.lastIntValue = 0x0A; /* \n */
  3721. state.advance();
  3722. return true
  3723. }
  3724. if (ch === 0x76 /* v */) {
  3725. state.lastIntValue = 0x0B; /* \v */
  3726. state.advance();
  3727. return true
  3728. }
  3729. if (ch === 0x66 /* f */) {
  3730. state.lastIntValue = 0x0C; /* \f */
  3731. state.advance();
  3732. return true
  3733. }
  3734. if (ch === 0x72 /* r */) {
  3735. state.lastIntValue = 0x0D; /* \r */
  3736. state.advance();
  3737. return true
  3738. }
  3739. return false
  3740. };
  3741. // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlLetter
  3742. pp$8.regexp_eatControlLetter = function(state) {
  3743. var ch = state.current();
  3744. if (isControlLetter(ch)) {
  3745. state.lastIntValue = ch % 0x20;
  3746. state.advance();
  3747. return true
  3748. }
  3749. return false
  3750. };
  3751. function isControlLetter(ch) {
  3752. return (
  3753. (ch >= 0x41 /* A */ && ch <= 0x5A /* Z */) ||
  3754. (ch >= 0x61 /* a */ && ch <= 0x7A /* z */)
  3755. )
  3756. }
  3757. // https://www.ecma-international.org/ecma-262/8.0/#prod-RegExpUnicodeEscapeSequence
  3758. pp$8.regexp_eatRegExpUnicodeEscapeSequence = function(state) {
  3759. var start = state.pos;
  3760. if (state.eat(0x75 /* u */)) {
  3761. if (this.regexp_eatFixedHexDigits(state, 4)) {
  3762. var lead = state.lastIntValue;
  3763. if (state.switchU && lead >= 0xD800 && lead <= 0xDBFF) {
  3764. var leadSurrogateEnd = state.pos;
  3765. if (state.eat(0x5C /* \ */) && state.eat(0x75 /* u */) && this.regexp_eatFixedHexDigits(state, 4)) {
  3766. var trail = state.lastIntValue;
  3767. if (trail >= 0xDC00 && trail <= 0xDFFF) {
  3768. state.lastIntValue = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
  3769. return true
  3770. }
  3771. }
  3772. state.pos = leadSurrogateEnd;
  3773. state.lastIntValue = lead;
  3774. }
  3775. return true
  3776. }
  3777. if (
  3778. state.switchU &&
  3779. state.eat(0x7B /* { */) &&
  3780. this.regexp_eatHexDigits(state) &&
  3781. state.eat(0x7D /* } */) &&
  3782. isValidUnicode(state.lastIntValue)
  3783. ) {
  3784. return true
  3785. }
  3786. if (state.switchU) {
  3787. state.raise("Invalid unicode escape");
  3788. }
  3789. state.pos = start;
  3790. }
  3791. return false
  3792. };
  3793. function isValidUnicode(ch) {
  3794. return ch >= 0 && ch <= 0x10FFFF
  3795. }
  3796. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-IdentityEscape
  3797. pp$8.regexp_eatIdentityEscape = function(state) {
  3798. if (state.switchU) {
  3799. if (this.regexp_eatSyntaxCharacter(state)) {
  3800. return true
  3801. }
  3802. if (state.eat(0x2F /* / */)) {
  3803. state.lastIntValue = 0x2F; /* / */
  3804. return true
  3805. }
  3806. return false
  3807. }
  3808. var ch = state.current();
  3809. if (ch !== 0x63 /* c */ && (!state.switchN || ch !== 0x6B /* k */)) {
  3810. state.lastIntValue = ch;
  3811. state.advance();
  3812. return true
  3813. }
  3814. return false
  3815. };
  3816. // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalEscape
  3817. pp$8.regexp_eatDecimalEscape = function(state) {
  3818. state.lastIntValue = 0;
  3819. var ch = state.current();
  3820. if (ch >= 0x31 /* 1 */ && ch <= 0x39 /* 9 */) {
  3821. do {
  3822. state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);
  3823. state.advance();
  3824. } while ((ch = state.current()) >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */)
  3825. return true
  3826. }
  3827. return false
  3828. };
  3829. // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClassEscape
  3830. pp$8.regexp_eatCharacterClassEscape = function(state) {
  3831. var ch = state.current();
  3832. if (isCharacterClassEscape(ch)) {
  3833. state.lastIntValue = -1;
  3834. state.advance();
  3835. return true
  3836. }
  3837. if (
  3838. state.switchU &&
  3839. this.options.ecmaVersion >= 9 &&
  3840. (ch === 0x50 /* P */ || ch === 0x70 /* p */)
  3841. ) {
  3842. state.lastIntValue = -1;
  3843. state.advance();
  3844. if (
  3845. state.eat(0x7B /* { */) &&
  3846. this.regexp_eatUnicodePropertyValueExpression(state) &&
  3847. state.eat(0x7D /* } */)
  3848. ) {
  3849. return true
  3850. }
  3851. state.raise("Invalid property name");
  3852. }
  3853. return false
  3854. };
  3855. function isCharacterClassEscape(ch) {
  3856. return (
  3857. ch === 0x64 /* d */ ||
  3858. ch === 0x44 /* D */ ||
  3859. ch === 0x73 /* s */ ||
  3860. ch === 0x53 /* S */ ||
  3861. ch === 0x77 /* w */ ||
  3862. ch === 0x57 /* W */
  3863. )
  3864. }
  3865. // UnicodePropertyValueExpression ::
  3866. // UnicodePropertyName `=` UnicodePropertyValue
  3867. // LoneUnicodePropertyNameOrValue
  3868. pp$8.regexp_eatUnicodePropertyValueExpression = function(state) {
  3869. var start = state.pos;
  3870. // UnicodePropertyName `=` UnicodePropertyValue
  3871. if (this.regexp_eatUnicodePropertyName(state) && state.eat(0x3D /* = */)) {
  3872. var name = state.lastStringValue;
  3873. if (this.regexp_eatUnicodePropertyValue(state)) {
  3874. var value = state.lastStringValue;
  3875. this.regexp_validateUnicodePropertyNameAndValue(state, name, value);
  3876. return true
  3877. }
  3878. }
  3879. state.pos = start;
  3880. // LoneUnicodePropertyNameOrValue
  3881. if (this.regexp_eatLoneUnicodePropertyNameOrValue(state)) {
  3882. var nameOrValue = state.lastStringValue;
  3883. this.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue);
  3884. return true
  3885. }
  3886. return false
  3887. };
  3888. pp$8.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {
  3889. if (!has(state.unicodeProperties.nonBinary, name))
  3890. { state.raise("Invalid property name"); }
  3891. if (!state.unicodeProperties.nonBinary[name].test(value))
  3892. { state.raise("Invalid property value"); }
  3893. };
  3894. pp$8.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {
  3895. if (!state.unicodeProperties.binary.test(nameOrValue))
  3896. { state.raise("Invalid property name"); }
  3897. };
  3898. // UnicodePropertyName ::
  3899. // UnicodePropertyNameCharacters
  3900. pp$8.regexp_eatUnicodePropertyName = function(state) {
  3901. var ch = 0;
  3902. state.lastStringValue = "";
  3903. while (isUnicodePropertyNameCharacter(ch = state.current())) {
  3904. state.lastStringValue += codePointToString(ch);
  3905. state.advance();
  3906. }
  3907. return state.lastStringValue !== ""
  3908. };
  3909. function isUnicodePropertyNameCharacter(ch) {
  3910. return isControlLetter(ch) || ch === 0x5F /* _ */
  3911. }
  3912. // UnicodePropertyValue ::
  3913. // UnicodePropertyValueCharacters
  3914. pp$8.regexp_eatUnicodePropertyValue = function(state) {
  3915. var ch = 0;
  3916. state.lastStringValue = "";
  3917. while (isUnicodePropertyValueCharacter(ch = state.current())) {
  3918. state.lastStringValue += codePointToString(ch);
  3919. state.advance();
  3920. }
  3921. return state.lastStringValue !== ""
  3922. };
  3923. function isUnicodePropertyValueCharacter(ch) {
  3924. return isUnicodePropertyNameCharacter(ch) || isDecimalDigit(ch)
  3925. }
  3926. // LoneUnicodePropertyNameOrValue ::
  3927. // UnicodePropertyValueCharacters
  3928. pp$8.regexp_eatLoneUnicodePropertyNameOrValue = function(state) {
  3929. return this.regexp_eatUnicodePropertyValue(state)
  3930. };
  3931. // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClass
  3932. pp$8.regexp_eatCharacterClass = function(state) {
  3933. if (state.eat(0x5B /* [ */)) {
  3934. state.eat(0x5E /* ^ */);
  3935. this.regexp_classRanges(state);
  3936. if (state.eat(0x5D /* [ */)) {
  3937. return true
  3938. }
  3939. // Unreachable since it threw "unterminated regular expression" error before.
  3940. state.raise("Unterminated character class");
  3941. }
  3942. return false
  3943. };
  3944. // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassRanges
  3945. // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRanges
  3946. // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRangesNoDash
  3947. pp$8.regexp_classRanges = function(state) {
  3948. while (this.regexp_eatClassAtom(state)) {
  3949. var left = state.lastIntValue;
  3950. if (state.eat(0x2D /* - */) && this.regexp_eatClassAtom(state)) {
  3951. var right = state.lastIntValue;
  3952. if (state.switchU && (left === -1 || right === -1)) {
  3953. state.raise("Invalid character class");
  3954. }
  3955. if (left !== -1 && right !== -1 && left > right) {
  3956. state.raise("Range out of order in character class");
  3957. }
  3958. }
  3959. }
  3960. };
  3961. // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtom
  3962. // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtomNoDash
  3963. pp$8.regexp_eatClassAtom = function(state) {
  3964. var start = state.pos;
  3965. if (state.eat(0x5C /* \ */)) {
  3966. if (this.regexp_eatClassEscape(state)) {
  3967. return true
  3968. }
  3969. if (state.switchU) {
  3970. // Make the same message as V8.
  3971. var ch$1 = state.current();
  3972. if (ch$1 === 0x63 /* c */ || isOctalDigit(ch$1)) {
  3973. state.raise("Invalid class escape");
  3974. }
  3975. state.raise("Invalid escape");
  3976. }
  3977. state.pos = start;
  3978. }
  3979. var ch = state.current();
  3980. if (ch !== 0x5D /* [ */) {
  3981. state.lastIntValue = ch;
  3982. state.advance();
  3983. return true
  3984. }
  3985. return false
  3986. };
  3987. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassEscape
  3988. pp$8.regexp_eatClassEscape = function(state) {
  3989. var start = state.pos;
  3990. if (state.eat(0x62 /* b */)) {
  3991. state.lastIntValue = 0x08; /* <BS> */
  3992. return true
  3993. }
  3994. if (state.switchU && state.eat(0x2D /* - */)) {
  3995. state.lastIntValue = 0x2D; /* - */
  3996. return true
  3997. }
  3998. if (!state.switchU && state.eat(0x63 /* c */)) {
  3999. if (this.regexp_eatClassControlLetter(state)) {
  4000. return true
  4001. }
  4002. state.pos = start;
  4003. }
  4004. return (
  4005. this.regexp_eatCharacterClassEscape(state) ||
  4006. this.regexp_eatCharacterEscape(state)
  4007. )
  4008. };
  4009. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassControlLetter
  4010. pp$8.regexp_eatClassControlLetter = function(state) {
  4011. var ch = state.current();
  4012. if (isDecimalDigit(ch) || ch === 0x5F /* _ */) {
  4013. state.lastIntValue = ch % 0x20;
  4014. state.advance();
  4015. return true
  4016. }
  4017. return false
  4018. };
  4019. // https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence
  4020. pp$8.regexp_eatHexEscapeSequence = function(state) {
  4021. var start = state.pos;
  4022. if (state.eat(0x78 /* x */)) {
  4023. if (this.regexp_eatFixedHexDigits(state, 2)) {
  4024. return true
  4025. }
  4026. if (state.switchU) {
  4027. state.raise("Invalid escape");
  4028. }
  4029. state.pos = start;
  4030. }
  4031. return false
  4032. };
  4033. // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalDigits
  4034. pp$8.regexp_eatDecimalDigits = function(state) {
  4035. var start = state.pos;
  4036. var ch = 0;
  4037. state.lastIntValue = 0;
  4038. while (isDecimalDigit(ch = state.current())) {
  4039. state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);
  4040. state.advance();
  4041. }
  4042. return state.pos !== start
  4043. };
  4044. function isDecimalDigit(ch) {
  4045. return ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */
  4046. }
  4047. // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigits
  4048. pp$8.regexp_eatHexDigits = function(state) {
  4049. var start = state.pos;
  4050. var ch = 0;
  4051. state.lastIntValue = 0;
  4052. while (isHexDigit(ch = state.current())) {
  4053. state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);
  4054. state.advance();
  4055. }
  4056. return state.pos !== start
  4057. };
  4058. function isHexDigit(ch) {
  4059. return (
  4060. (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) ||
  4061. (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) ||
  4062. (ch >= 0x61 /* a */ && ch <= 0x66 /* f */)
  4063. )
  4064. }
  4065. function hexToInt(ch) {
  4066. if (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) {
  4067. return 10 + (ch - 0x41 /* A */)
  4068. }
  4069. if (ch >= 0x61 /* a */ && ch <= 0x66 /* f */) {
  4070. return 10 + (ch - 0x61 /* a */)
  4071. }
  4072. return ch - 0x30 /* 0 */
  4073. }
  4074. // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-LegacyOctalEscapeSequence
  4075. // Allows only 0-377(octal) i.e. 0-255(decimal).
  4076. pp$8.regexp_eatLegacyOctalEscapeSequence = function(state) {
  4077. if (this.regexp_eatOctalDigit(state)) {
  4078. var n1 = state.lastIntValue;
  4079. if (this.regexp_eatOctalDigit(state)) {
  4080. var n2 = state.lastIntValue;
  4081. if (n1 <= 3 && this.regexp_eatOctalDigit(state)) {
  4082. state.lastIntValue = n1 * 64 + n2 * 8 + state.lastIntValue;
  4083. } else {
  4084. state.lastIntValue = n1 * 8 + n2;
  4085. }
  4086. } else {
  4087. state.lastIntValue = n1;
  4088. }
  4089. return true
  4090. }
  4091. return false
  4092. };
  4093. // https://www.ecma-international.org/ecma-262/8.0/#prod-OctalDigit
  4094. pp$8.regexp_eatOctalDigit = function(state) {
  4095. var ch = state.current();
  4096. if (isOctalDigit(ch)) {
  4097. state.lastIntValue = ch - 0x30; /* 0 */
  4098. state.advance();
  4099. return true
  4100. }
  4101. state.lastIntValue = 0;
  4102. return false
  4103. };
  4104. function isOctalDigit(ch) {
  4105. return ch >= 0x30 /* 0 */ && ch <= 0x37 /* 7 */
  4106. }
  4107. // https://www.ecma-international.org/ecma-262/8.0/#prod-Hex4Digits
  4108. // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigit
  4109. // And HexDigit HexDigit in https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence
  4110. pp$8.regexp_eatFixedHexDigits = function(state, length) {
  4111. var start = state.pos;
  4112. state.lastIntValue = 0;
  4113. for (var i = 0; i < length; ++i) {
  4114. var ch = state.current();
  4115. if (!isHexDigit(ch)) {
  4116. state.pos = start;
  4117. return false
  4118. }
  4119. state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);
  4120. state.advance();
  4121. }
  4122. return true
  4123. };
  4124. // Object type used to represent tokens. Note that normally, tokens
  4125. // simply exist as properties on the parser object. This is only
  4126. // used for the onToken callback and the external tokenizer.
  4127. var Token = function Token(p) {
  4128. this.type = p.type;
  4129. this.value = p.value;
  4130. this.start = p.start;
  4131. this.end = p.end;
  4132. if (p.options.locations)
  4133. { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }
  4134. if (p.options.ranges)
  4135. { this.range = [p.start, p.end]; }
  4136. };
  4137. // ## Tokenizer
  4138. var pp$9 = Parser.prototype;
  4139. // Move to the next token
  4140. pp$9.next = function() {
  4141. if (this.options.onToken)
  4142. { this.options.onToken(new Token(this)); }
  4143. this.lastTokEnd = this.end;
  4144. this.lastTokStart = this.start;
  4145. this.lastTokEndLoc = this.endLoc;
  4146. this.lastTokStartLoc = this.startLoc;
  4147. this.nextToken();
  4148. };
  4149. pp$9.getToken = function() {
  4150. this.next();
  4151. return new Token(this)
  4152. };
  4153. // If we're in an ES6 environment, make parsers iterable
  4154. if (typeof Symbol !== "undefined")
  4155. { pp$9[Symbol.iterator] = function() {
  4156. var this$1 = this;
  4157. return {
  4158. next: function () {
  4159. var token = this$1.getToken();
  4160. return {
  4161. done: token.type === types$1.eof,
  4162. value: token
  4163. }
  4164. }
  4165. }
  4166. }; }
  4167. // Toggle strict mode. Re-reads the next number or string to please
  4168. // pedantic tests (`"use strict"; 010;` should fail).
  4169. pp$9.curContext = function() {
  4170. return this.context[this.context.length - 1]
  4171. };
  4172. // Read a single token, updating the parser object's token-related
  4173. // properties.
  4174. pp$9.nextToken = function() {
  4175. var curContext = this.curContext();
  4176. if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }
  4177. this.start = this.pos;
  4178. if (this.options.locations) { this.startLoc = this.curPosition(); }
  4179. if (this.pos >= this.input.length) { return this.finishToken(types$1.eof) }
  4180. if (curContext.override) { return curContext.override(this) }
  4181. else { this.readToken(this.fullCharCodeAtPos()); }
  4182. };
  4183. pp$9.readToken = function(code) {
  4184. // Identifier or keyword. '\uXXXX' sequences are allowed in
  4185. // identifiers, so '\' also dispatches to that.
  4186. if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
  4187. { return this.readWord() }
  4188. return this.getTokenFromCode(code)
  4189. };
  4190. pp$9.fullCharCodeAtPos = function() {
  4191. var code = this.input.charCodeAt(this.pos);
  4192. if (code <= 0xd7ff || code >= 0xe000) { return code }
  4193. var next = this.input.charCodeAt(this.pos + 1);
  4194. return (code << 10) + next - 0x35fdc00
  4195. };
  4196. pp$9.skipBlockComment = function() {
  4197. var startLoc = this.options.onComment && this.curPosition();
  4198. var start = this.pos, end = this.input.indexOf("*/", this.pos += 2);
  4199. if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); }
  4200. this.pos = end + 2;
  4201. if (this.options.locations) {
  4202. lineBreakG.lastIndex = start;
  4203. var match;
  4204. while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
  4205. ++this.curLine;
  4206. this.lineStart = match.index + match[0].length;
  4207. }
  4208. }
  4209. if (this.options.onComment)
  4210. { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
  4211. startLoc, this.curPosition()); }
  4212. };
  4213. pp$9.skipLineComment = function(startSkip) {
  4214. var start = this.pos;
  4215. var startLoc = this.options.onComment && this.curPosition();
  4216. var ch = this.input.charCodeAt(this.pos += startSkip);
  4217. while (this.pos < this.input.length && !isNewLine(ch)) {
  4218. ch = this.input.charCodeAt(++this.pos);
  4219. }
  4220. if (this.options.onComment)
  4221. { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
  4222. startLoc, this.curPosition()); }
  4223. };
  4224. // Called at the start of the parse and after every token. Skips
  4225. // whitespace and comments, and.
  4226. pp$9.skipSpace = function() {
  4227. loop: while (this.pos < this.input.length) {
  4228. var ch = this.input.charCodeAt(this.pos);
  4229. switch (ch) {
  4230. case 32: case 160: // ' '
  4231. ++this.pos;
  4232. break
  4233. case 13:
  4234. if (this.input.charCodeAt(this.pos + 1) === 10) {
  4235. ++this.pos;
  4236. }
  4237. case 10: case 8232: case 8233:
  4238. ++this.pos;
  4239. if (this.options.locations) {
  4240. ++this.curLine;
  4241. this.lineStart = this.pos;
  4242. }
  4243. break
  4244. case 47: // '/'
  4245. switch (this.input.charCodeAt(this.pos + 1)) {
  4246. case 42: // '*'
  4247. this.skipBlockComment();
  4248. break
  4249. case 47:
  4250. this.skipLineComment(2);
  4251. break
  4252. default:
  4253. break loop
  4254. }
  4255. break
  4256. default:
  4257. if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
  4258. ++this.pos;
  4259. } else {
  4260. break loop
  4261. }
  4262. }
  4263. }
  4264. };
  4265. // Called at the end of every token. Sets `end`, `val`, and
  4266. // maintains `context` and `exprAllowed`, and skips the space after
  4267. // the token, so that the next one's `start` will point at the
  4268. // right position.
  4269. pp$9.finishToken = function(type, val) {
  4270. this.end = this.pos;
  4271. if (this.options.locations) { this.endLoc = this.curPosition(); }
  4272. var prevType = this.type;
  4273. this.type = type;
  4274. this.value = val;
  4275. this.updateContext(prevType);
  4276. };
  4277. // ### Token reading
  4278. // This is the function that is called to fetch the next token. It
  4279. // is somewhat obscure, because it works in character codes rather
  4280. // than characters, and because operator parsing has been inlined
  4281. // into it.
  4282. //
  4283. // All in the name of speed.
  4284. //
  4285. pp$9.readToken_dot = function() {
  4286. var next = this.input.charCodeAt(this.pos + 1);
  4287. if (next >= 48 && next <= 57) { return this.readNumber(true) }
  4288. var next2 = this.input.charCodeAt(this.pos + 2);
  4289. if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
  4290. this.pos += 3;
  4291. return this.finishToken(types$1.ellipsis)
  4292. } else {
  4293. ++this.pos;
  4294. return this.finishToken(types$1.dot)
  4295. }
  4296. };
  4297. pp$9.readToken_slash = function() { // '/'
  4298. var next = this.input.charCodeAt(this.pos + 1);
  4299. if (this.exprAllowed) { ++this.pos; return this.readRegexp() }
  4300. if (next === 61) { return this.finishOp(types$1.assign, 2) }
  4301. return this.finishOp(types$1.slash, 1)
  4302. };
  4303. pp$9.readToken_mult_modulo_exp = function(code) { // '%*'
  4304. var next = this.input.charCodeAt(this.pos + 1);
  4305. var size = 1;
  4306. var tokentype = code === 42 ? types$1.star : types$1.modulo;
  4307. // exponentiation operator ** and **=
  4308. if (this.options.ecmaVersion >= 7 && code === 42 && next === 42) {
  4309. ++size;
  4310. tokentype = types$1.starstar;
  4311. next = this.input.charCodeAt(this.pos + 2);
  4312. }
  4313. if (next === 61) { return this.finishOp(types$1.assign, size + 1) }
  4314. return this.finishOp(tokentype, size)
  4315. };
  4316. pp$9.readToken_pipe_amp = function(code) { // '|&'
  4317. var next = this.input.charCodeAt(this.pos + 1);
  4318. if (next === code) { return this.finishOp(code === 124 ? types$1.logicalOR : types$1.logicalAND, 2) }
  4319. if (next === 61) { return this.finishOp(types$1.assign, 2) }
  4320. return this.finishOp(code === 124 ? types$1.bitwiseOR : types$1.bitwiseAND, 1)
  4321. };
  4322. pp$9.readToken_caret = function() { // '^'
  4323. var next = this.input.charCodeAt(this.pos + 1);
  4324. if (next === 61) { return this.finishOp(types$1.assign, 2) }
  4325. return this.finishOp(types$1.bitwiseXOR, 1)
  4326. };
  4327. pp$9.readToken_plus_min = function(code) { // '+-'
  4328. var next = this.input.charCodeAt(this.pos + 1);
  4329. if (next === code) {
  4330. if (next === 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 62 &&
  4331. (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {
  4332. // A `-->` line comment
  4333. this.skipLineComment(3);
  4334. this.skipSpace();
  4335. return this.nextToken()
  4336. }
  4337. return this.finishOp(types$1.incDec, 2)
  4338. }
  4339. if (next === 61) { return this.finishOp(types$1.assign, 2) }
  4340. return this.finishOp(types$1.plusMin, 1)
  4341. };
  4342. pp$9.readToken_lt_gt = function(code) { // '<>'
  4343. var next = this.input.charCodeAt(this.pos + 1);
  4344. var size = 1;
  4345. if (next === code) {
  4346. size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
  4347. if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }
  4348. return this.finishOp(types$1.bitShift, size)
  4349. }
  4350. if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&
  4351. this.input.charCodeAt(this.pos + 3) === 45) {
  4352. // `<!--`, an XML-style comment that should be interpreted as a line comment
  4353. this.skipLineComment(4);
  4354. this.skipSpace();
  4355. return this.nextToken()
  4356. }
  4357. if (next === 61) { size = 2; }
  4358. return this.finishOp(types$1.relational, size)
  4359. };
  4360. pp$9.readToken_eq_excl = function(code) { // '=!'
  4361. var next = this.input.charCodeAt(this.pos + 1);
  4362. if (next === 61) { return this.finishOp(types$1.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) }
  4363. if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
  4364. this.pos += 2;
  4365. return this.finishToken(types$1.arrow)
  4366. }
  4367. return this.finishOp(code === 61 ? types$1.eq : types$1.prefix, 1)
  4368. };
  4369. pp$9.getTokenFromCode = function(code) {
  4370. switch (code) {
  4371. // The interpretation of a dot depends on whether it is followed
  4372. // by a digit or another two dots.
  4373. case 46: // '.'
  4374. return this.readToken_dot()
  4375. // Punctuation tokens.
  4376. case 40: ++this.pos; return this.finishToken(types$1.parenL)
  4377. case 41: ++this.pos; return this.finishToken(types$1.parenR)
  4378. case 59: ++this.pos; return this.finishToken(types$1.semi)
  4379. case 44: ++this.pos; return this.finishToken(types$1.comma)
  4380. case 91: ++this.pos; return this.finishToken(types$1.bracketL)
  4381. case 93: ++this.pos; return this.finishToken(types$1.bracketR)
  4382. case 123: ++this.pos; return this.finishToken(types$1.braceL)
  4383. case 125: ++this.pos; return this.finishToken(types$1.braceR)
  4384. case 58: ++this.pos; return this.finishToken(types$1.colon)
  4385. case 63: ++this.pos; return this.finishToken(types$1.question)
  4386. case 96: // '`'
  4387. if (this.options.ecmaVersion < 6) { break }
  4388. ++this.pos;
  4389. return this.finishToken(types$1.backQuote)
  4390. case 48: // '0'
  4391. var next = this.input.charCodeAt(this.pos + 1);
  4392. if (next === 120 || next === 88) { return this.readRadixNumber(16) } // '0x', '0X' - hex number
  4393. if (this.options.ecmaVersion >= 6) {
  4394. if (next === 111 || next === 79) { return this.readRadixNumber(8) } // '0o', '0O' - octal number
  4395. if (next === 98 || next === 66) { return this.readRadixNumber(2) } // '0b', '0B' - binary number
  4396. }
  4397. // Anything else beginning with a digit is an integer, octal
  4398. // number, or float.
  4399. case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
  4400. return this.readNumber(false)
  4401. // Quotes produce strings.
  4402. case 34: case 39: // '"', "'"
  4403. return this.readString(code)
  4404. // Operators are parsed inline in tiny state machines. '=' (61) is
  4405. // often referred to. `finishOp` simply skips the amount of
  4406. // characters it is given as second argument, and returns a token
  4407. // of the type given by its first argument.
  4408. case 47: // '/'
  4409. return this.readToken_slash()
  4410. case 37: case 42: // '%*'
  4411. return this.readToken_mult_modulo_exp(code)
  4412. case 124: case 38: // '|&'
  4413. return this.readToken_pipe_amp(code)
  4414. case 94: // '^'
  4415. return this.readToken_caret()
  4416. case 43: case 45: // '+-'
  4417. return this.readToken_plus_min(code)
  4418. case 60: case 62: // '<>'
  4419. return this.readToken_lt_gt(code)
  4420. case 61: case 33: // '=!'
  4421. return this.readToken_eq_excl(code)
  4422. case 126: // '~'
  4423. return this.finishOp(types$1.prefix, 1)
  4424. }
  4425. this.raise(this.pos, "Unexpected character '" + codePointToString$1(code) + "'");
  4426. };
  4427. pp$9.finishOp = function(type, size) {
  4428. var str = this.input.slice(this.pos, this.pos + size);
  4429. this.pos += size;
  4430. return this.finishToken(type, str)
  4431. };
  4432. pp$9.readRegexp = function() {
  4433. var escaped, inClass, start = this.pos;
  4434. for (;;) {
  4435. if (this.pos >= this.input.length) { this.raise(start, "Unterminated regular expression"); }
  4436. var ch = this.input.charAt(this.pos);
  4437. if (lineBreak.test(ch)) { this.raise(start, "Unterminated regular expression"); }
  4438. if (!escaped) {
  4439. if (ch === "[") { inClass = true; }
  4440. else if (ch === "]" && inClass) { inClass = false; }
  4441. else if (ch === "/" && !inClass) { break }
  4442. escaped = ch === "\\";
  4443. } else { escaped = false; }
  4444. ++this.pos;
  4445. }
  4446. var pattern = this.input.slice(start, this.pos);
  4447. ++this.pos;
  4448. var flagsStart = this.pos;
  4449. var flags = this.readWord1();
  4450. if (this.containsEsc) { this.unexpected(flagsStart); }
  4451. // Validate pattern
  4452. var state = this.regexpState || (this.regexpState = new RegExpValidationState(this));
  4453. state.reset(start, pattern, flags);
  4454. this.validateRegExpFlags(state);
  4455. this.validateRegExpPattern(state);
  4456. // Create Literal#value property value.
  4457. var value = null;
  4458. try {
  4459. value = new RegExp(pattern, flags);
  4460. } catch (e) {
  4461. // ESTree requires null if it failed to instantiate RegExp object.
  4462. // https://github.com/estree/estree/blob/a27003adf4fd7bfad44de9cef372a2eacd527b1c/es5.md#regexpliteral
  4463. }
  4464. return this.finishToken(types$1.regexp, {pattern: pattern, flags: flags, value: value})
  4465. };
  4466. // Read an integer in the given radix. Return null if zero digits
  4467. // were read, the integer value otherwise. When `len` is given, this
  4468. // will return `null` unless the integer has exactly `len` digits.
  4469. pp$9.readInt = function(radix, len) {
  4470. var start = this.pos, total = 0;
  4471. for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
  4472. var code = this.input.charCodeAt(this.pos), val = (void 0);
  4473. if (code >= 97) { val = code - 97 + 10; } // a
  4474. else if (code >= 65) { val = code - 65 + 10; } // A
  4475. else if (code >= 48 && code <= 57) { val = code - 48; } // 0-9
  4476. else { val = Infinity; }
  4477. if (val >= radix) { break }
  4478. ++this.pos;
  4479. total = total * radix + val;
  4480. }
  4481. if (this.pos === start || len != null && this.pos - start !== len) { return null }
  4482. return total
  4483. };
  4484. pp$9.readRadixNumber = function(radix) {
  4485. var start = this.pos;
  4486. this.pos += 2; // 0x
  4487. var val = this.readInt(radix);
  4488. if (val == null) { this.raise(this.start + 2, "Expected number in radix " + radix); }
  4489. if (this.options.ecmaVersion >= 11 && this.input.charCodeAt(this.pos) === 110) {
  4490. val = typeof BigInt !== "undefined" ? BigInt(this.input.slice(start, this.pos)) : null;
  4491. ++this.pos;
  4492. } else if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }
  4493. return this.finishToken(types$1.num, val)
  4494. };
  4495. // Read an integer, octal integer, or floating-point number.
  4496. pp$9.readNumber = function(startsWithDot) {
  4497. var start = this.pos;
  4498. if (!startsWithDot && this.readInt(10) === null) { this.raise(start, "Invalid number"); }
  4499. var octal = this.pos - start >= 2 && this.input.charCodeAt(start) === 48;
  4500. if (octal && this.strict) { this.raise(start, "Invalid number"); }
  4501. if (octal && /[89]/.test(this.input.slice(start, this.pos))) { octal = false; }
  4502. var next = this.input.charCodeAt(this.pos);
  4503. if (!octal && !startsWithDot && this.options.ecmaVersion >= 11 && next === 110) {
  4504. var str$1 = this.input.slice(start, this.pos);
  4505. var val$1 = typeof BigInt !== "undefined" ? BigInt(str$1) : null;
  4506. ++this.pos;
  4507. if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }
  4508. return this.finishToken(types$1.num, val$1)
  4509. }
  4510. if (next === 46 && !octal) { // '.'
  4511. ++this.pos;
  4512. this.readInt(10);
  4513. next = this.input.charCodeAt(this.pos);
  4514. }
  4515. if ((next === 69 || next === 101) && !octal) { // 'eE'
  4516. next = this.input.charCodeAt(++this.pos);
  4517. if (next === 43 || next === 45) { ++this.pos; } // '+-'
  4518. if (this.readInt(10) === null) { this.raise(start, "Invalid number"); }
  4519. }
  4520. if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }
  4521. var str = this.input.slice(start, this.pos);
  4522. var val = octal ? parseInt(str, 8) : parseFloat(str);
  4523. return this.finishToken(types$1.num, val)
  4524. };
  4525. // Read a string value, interpreting backslash-escapes.
  4526. pp$9.readCodePoint = function() {
  4527. var ch = this.input.charCodeAt(this.pos), code;
  4528. if (ch === 123) { // '{'
  4529. if (this.options.ecmaVersion < 6) { this.unexpected(); }
  4530. var codePos = ++this.pos;
  4531. code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
  4532. ++this.pos;
  4533. if (code > 0x10FFFF) { this.invalidStringToken(codePos, "Code point out of bounds"); }
  4534. } else {
  4535. code = this.readHexChar(4);
  4536. }
  4537. return code
  4538. };
  4539. function codePointToString$1(code) {
  4540. // UTF-16 Decoding
  4541. if (code <= 0xFFFF) { return String.fromCharCode(code) }
  4542. code -= 0x10000;
  4543. return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)
  4544. }
  4545. pp$9.readString = function(quote) {
  4546. var out = "", chunkStart = ++this.pos;
  4547. for (;;) {
  4548. if (this.pos >= this.input.length) { this.raise(this.start, "Unterminated string constant"); }
  4549. var ch = this.input.charCodeAt(this.pos);
  4550. if (ch === quote) { break }
  4551. if (ch === 92) { // '\'
  4552. out += this.input.slice(chunkStart, this.pos);
  4553. out += this.readEscapedChar(false);
  4554. chunkStart = this.pos;
  4555. } else {
  4556. if (isNewLine(ch, this.options.ecmaVersion >= 10)) { this.raise(this.start, "Unterminated string constant"); }
  4557. ++this.pos;
  4558. }
  4559. }
  4560. out += this.input.slice(chunkStart, this.pos++);
  4561. return this.finishToken(types$1.string, out)
  4562. };
  4563. // Reads template string tokens.
  4564. var INVALID_TEMPLATE_ESCAPE_ERROR = {};
  4565. pp$9.tryReadTemplateToken = function() {
  4566. this.inTemplateElement = true;
  4567. try {
  4568. this.readTmplToken();
  4569. } catch (err) {
  4570. if (err === INVALID_TEMPLATE_ESCAPE_ERROR) {
  4571. this.readInvalidTemplateToken();
  4572. } else {
  4573. throw err
  4574. }
  4575. }
  4576. this.inTemplateElement = false;
  4577. };
  4578. pp$9.invalidStringToken = function(position, message) {
  4579. if (this.inTemplateElement && this.options.ecmaVersion >= 9) {
  4580. throw INVALID_TEMPLATE_ESCAPE_ERROR
  4581. } else {
  4582. this.raise(position, message);
  4583. }
  4584. };
  4585. pp$9.readTmplToken = function() {
  4586. var out = "", chunkStart = this.pos;
  4587. for (;;) {
  4588. if (this.pos >= this.input.length) { this.raise(this.start, "Unterminated template"); }
  4589. var ch = this.input.charCodeAt(this.pos);
  4590. if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${'
  4591. if (this.pos === this.start && (this.type === types$1.template || this.type === types$1.invalidTemplate)) {
  4592. if (ch === 36) {
  4593. this.pos += 2;
  4594. return this.finishToken(types$1.dollarBraceL)
  4595. } else {
  4596. ++this.pos;
  4597. return this.finishToken(types$1.backQuote)
  4598. }
  4599. }
  4600. out += this.input.slice(chunkStart, this.pos);
  4601. return this.finishToken(types$1.template, out)
  4602. }
  4603. if (ch === 92) { // '\'
  4604. out += this.input.slice(chunkStart, this.pos);
  4605. out += this.readEscapedChar(true);
  4606. chunkStart = this.pos;
  4607. } else if (isNewLine(ch)) {
  4608. out += this.input.slice(chunkStart, this.pos);
  4609. ++this.pos;
  4610. switch (ch) {
  4611. case 13:
  4612. if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; }
  4613. case 10:
  4614. out += "\n";
  4615. break
  4616. default:
  4617. out += String.fromCharCode(ch);
  4618. break
  4619. }
  4620. if (this.options.locations) {
  4621. ++this.curLine;
  4622. this.lineStart = this.pos;
  4623. }
  4624. chunkStart = this.pos;
  4625. } else {
  4626. ++this.pos;
  4627. }
  4628. }
  4629. };
  4630. // Reads a template token to search for the end, without validating any escape sequences
  4631. pp$9.readInvalidTemplateToken = function() {
  4632. for (; this.pos < this.input.length; this.pos++) {
  4633. switch (this.input[this.pos]) {
  4634. case "\\":
  4635. ++this.pos;
  4636. break
  4637. case "$":
  4638. if (this.input[this.pos + 1] !== "{") {
  4639. break
  4640. }
  4641. // falls through
  4642. case "`":
  4643. return this.finishToken(types$1.invalidTemplate, this.input.slice(this.start, this.pos))
  4644. // no default
  4645. }
  4646. }
  4647. this.raise(this.start, "Unterminated template");
  4648. };
  4649. // Used to read escaped characters
  4650. pp$9.readEscapedChar = function(inTemplate) {
  4651. var ch = this.input.charCodeAt(++this.pos);
  4652. ++this.pos;
  4653. switch (ch) {
  4654. case 110: return "\n" // 'n' -> '\n'
  4655. case 114: return "\r" // 'r' -> '\r'
  4656. case 120: return String.fromCharCode(this.readHexChar(2)) // 'x'
  4657. case 117: return codePointToString$1(this.readCodePoint()) // 'u'
  4658. case 116: return "\t" // 't' -> '\t'
  4659. case 98: return "\b" // 'b' -> '\b'
  4660. case 118: return "\u000b" // 'v' -> '\u000b'
  4661. case 102: return "\f" // 'f' -> '\f'
  4662. case 13: if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; } // '\r\n'
  4663. case 10: // ' \n'
  4664. if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }
  4665. return ""
  4666. default:
  4667. if (ch >= 48 && ch <= 55) {
  4668. var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
  4669. var octal = parseInt(octalStr, 8);
  4670. if (octal > 255) {
  4671. octalStr = octalStr.slice(0, -1);
  4672. octal = parseInt(octalStr, 8);
  4673. }
  4674. this.pos += octalStr.length - 1;
  4675. ch = this.input.charCodeAt(this.pos);
  4676. if ((octalStr !== "0" || ch === 56 || ch === 57) && (this.strict || inTemplate)) {
  4677. this.invalidStringToken(
  4678. this.pos - 1 - octalStr.length,
  4679. inTemplate
  4680. ? "Octal literal in template string"
  4681. : "Octal literal in strict mode"
  4682. );
  4683. }
  4684. return String.fromCharCode(octal)
  4685. }
  4686. if (isNewLine(ch)) {
  4687. // Unicode new line characters after \ get removed from output in both
  4688. // template literals and strings
  4689. return ""
  4690. }
  4691. return String.fromCharCode(ch)
  4692. }
  4693. };
  4694. // Used to read character escape sequences ('\x', '\u', '\U').
  4695. pp$9.readHexChar = function(len) {
  4696. var codePos = this.pos;
  4697. var n = this.readInt(16, len);
  4698. if (n === null) { this.invalidStringToken(codePos, "Bad character escape sequence"); }
  4699. return n
  4700. };
  4701. // Read an identifier, and return it as a string. Sets `this.containsEsc`
  4702. // to whether the word contained a '\u' escape.
  4703. //
  4704. // Incrementally adds only escaped chars, adding other chunks as-is
  4705. // as a micro-optimization.
  4706. pp$9.readWord1 = function() {
  4707. this.containsEsc = false;
  4708. var word = "", first = true, chunkStart = this.pos;
  4709. var astral = this.options.ecmaVersion >= 6;
  4710. while (this.pos < this.input.length) {
  4711. var ch = this.fullCharCodeAtPos();
  4712. if (isIdentifierChar(ch, astral)) {
  4713. this.pos += ch <= 0xffff ? 1 : 2;
  4714. } else if (ch === 92) { // "\"
  4715. this.containsEsc = true;
  4716. word += this.input.slice(chunkStart, this.pos);
  4717. var escStart = this.pos;
  4718. if (this.input.charCodeAt(++this.pos) !== 117) // "u"
  4719. { this.invalidStringToken(this.pos, "Expecting Unicode escape sequence \\uXXXX"); }
  4720. ++this.pos;
  4721. var esc = this.readCodePoint();
  4722. if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
  4723. { this.invalidStringToken(escStart, "Invalid Unicode escape"); }
  4724. word += codePointToString$1(esc);
  4725. chunkStart = this.pos;
  4726. } else {
  4727. break
  4728. }
  4729. first = false;
  4730. }
  4731. return word + this.input.slice(chunkStart, this.pos)
  4732. };
  4733. // Read an identifier or keyword token. Will check for reserved
  4734. // words when necessary.
  4735. pp$9.readWord = function() {
  4736. var word = this.readWord1();
  4737. var type = types$1.name;
  4738. if (this.keywords.test(word)) {
  4739. if (this.containsEsc) { this.raiseRecoverable(this.start, "Escape sequence in keyword " + word); }
  4740. type = keywords$1[word];
  4741. }
  4742. return this.finishToken(type, word)
  4743. };
  4744. // Acorn is a tiny, fast JavaScript parser written in JavaScript.
  4745. var version = "7.1.0";
  4746. Parser.acorn = {
  4747. Parser: Parser,
  4748. version: version,
  4749. defaultOptions: defaultOptions,
  4750. Position: Position,
  4751. SourceLocation: SourceLocation,
  4752. getLineInfo: getLineInfo,
  4753. Node: Node,
  4754. TokenType: TokenType,
  4755. tokTypes: types$1,
  4756. keywordTypes: keywords$1,
  4757. TokContext: TokContext,
  4758. tokContexts: types$1$1,
  4759. isIdentifierChar: isIdentifierChar,
  4760. isIdentifierStart: isIdentifierStart,
  4761. Token: Token,
  4762. isNewLine: isNewLine,
  4763. lineBreak: lineBreak,
  4764. lineBreakG: lineBreakG,
  4765. nonASCIIwhitespace: nonASCIIwhitespace
  4766. };
  4767. /**
  4768. * Parse a js source to generate the AST
  4769. * @param {string} source - javascript source
  4770. * @param {Object} options - parser options
  4771. * @returns {AST} AST tree
  4772. */
  4773. function generateAST(source, options) {
  4774. return recast.parse(source, {
  4775. parser: {
  4776. parse(source, opts) {
  4777. return Parser.parse(source, {
  4778. ...opts,
  4779. ecmaVersion: 2020
  4780. })
  4781. }
  4782. },
  4783. ...options
  4784. })
  4785. }
  4786. const browserAPIs = Object.keys(globalScope.browser);
  4787. const builtinAPIs = Object.keys(globalScope.builtin);
  4788. const isIdentifier = n => namedTypes.Identifier.check(n);
  4789. const isLiteral = n => namedTypes.Literal.check(n);
  4790. const isExpressionStatement = n => namedTypes.ExpressionStatement.check(n);
  4791. const isThisExpression = n => namedTypes.ThisExpression.check(n);
  4792. const isNewExpression = n => namedTypes.NewExpression.check(n);
  4793. const isSequenceExpression = n => namedTypes.SequenceExpression.check(n);
  4794. const isBinaryExpression = n => namedTypes.BinaryExpression.check(n);
  4795. const isExportDefaultStatement = n => namedTypes.ExportDefaultDeclaration.check(n);
  4796. const isBrowserAPI = ({name}) => browserAPIs.includes(name);
  4797. const isBuiltinAPI = ({name}) => builtinAPIs.includes(name);
  4798. const isRaw = n => n && n.raw;
  4799. /**
  4800. * Find the export default statement
  4801. * @param { Array } body - tree structure containing the program code
  4802. * @returns { Object } node containing only the code of the export default statement
  4803. */
  4804. function findExportDefaultStatement(body) {
  4805. return body.find(isExportDefaultStatement)
  4806. }
  4807. /**
  4808. * Find all the code in an ast program except for the export default statements
  4809. * @param { Array } body - tree structure containing the program code
  4810. * @returns { Array } array containing all the program code except the export default expressions
  4811. */
  4812. function filterNonExportDefaultStatements(body) {
  4813. return body.filter(node => !isExportDefaultStatement(node))
  4814. }
  4815. /**
  4816. * Get the body of the AST structure
  4817. * @param { Object } ast - ast object generated by recast
  4818. * @returns { Array } array containing the program code
  4819. */
  4820. function getProgramBody(ast) {
  4821. return ast.body || ast.program.body
  4822. }
  4823. /**
  4824. * Extend the AST adding the new tag method containing our tag sourcecode
  4825. * @param { Object } ast - current output ast
  4826. * @param { Object } exportDefaultNode - tag export default node
  4827. * @returns { Object } the output ast having the "tag" key extended with the content of the export default
  4828. */
  4829. function extendTagProperty(ast, exportDefaultNode) {
  4830. types.visit(ast, {
  4831. visitProperty(path) {
  4832. if (path.value.key.value === TAG_LOGIC_PROPERTY) {
  4833. path.value.value = exportDefaultNode.declaration;
  4834. return false
  4835. }
  4836. this.traverse(path);
  4837. }
  4838. });
  4839. return ast
  4840. }
  4841. /**
  4842. * Generate the component javascript logic
  4843. * @param { Object } sourceNode - node generated by the riot compiler
  4844. * @param { string } source - original component source code
  4845. * @param { Object } meta - compilation meta information
  4846. * @param { AST } ast - current AST output
  4847. * @returns { AST } the AST generated
  4848. */
  4849. function javascript(sourceNode, source, meta, ast) {
  4850. const preprocessorName = getPreprocessorTypeByAttribute(sourceNode);
  4851. const javascriptNode = addLineOffset(sourceNode.text.text, source, sourceNode);
  4852. const { options } = meta;
  4853. const preprocessorOutput = preprocess('javascript', preprocessorName, meta, {
  4854. ...sourceNode,
  4855. text: javascriptNode
  4856. });
  4857. const inputSourceMap = sourcemapAsJSON(preprocessorOutput.map);
  4858. const generatedAst = generateAST(preprocessorOutput.code, {
  4859. sourceFileName: options.file,
  4860. inputSourceMap: isEmptySourcemap(inputSourceMap) ? null : inputSourceMap
  4861. });
  4862. const generatedAstBody = getProgramBody(generatedAst);
  4863. const bodyWithoutExportDefault = filterNonExportDefaultStatements(generatedAstBody);
  4864. const exportDefaultNode = findExportDefaultStatement(generatedAstBody);
  4865. const outputBody = getProgramBody(ast);
  4866. // add to the ast the "private" javascript content of our tag script node
  4867. outputBody.unshift(...bodyWithoutExportDefault);
  4868. // convert the export default adding its content to the "tag" property exported
  4869. if (exportDefaultNode) extendTagProperty(ast, exportDefaultNode);
  4870. return ast
  4871. }
  4872. const JAVASCRIPT_OUTPUT_NAME = 'javascript';
  4873. const CSS_OUTPUT_NAME = 'css';
  4874. const TEMPLATE_OUTPUT_NAME = 'template';
  4875. // Tag names
  4876. const JAVASCRIPT_TAG = 'script';
  4877. const STYLE_TAG = 'style';
  4878. const TEXTAREA_TAG = 'textarea';
  4879. // Boolean attributes
  4880. const IS_RAW = 'isRaw';
  4881. const IS_SELF_CLOSING = 'isSelfClosing';
  4882. const IS_VOID = 'isVoid';
  4883. const IS_BOOLEAN = 'isBoolean';
  4884. const IS_CUSTOM = 'isCustom';
  4885. const IS_SPREAD = 'isSpread';
  4886. var c = /*#__PURE__*/Object.freeze({
  4887. __proto__: null,
  4888. JAVASCRIPT_OUTPUT_NAME: JAVASCRIPT_OUTPUT_NAME,
  4889. CSS_OUTPUT_NAME: CSS_OUTPUT_NAME,
  4890. TEMPLATE_OUTPUT_NAME: TEMPLATE_OUTPUT_NAME,
  4891. JAVASCRIPT_TAG: JAVASCRIPT_TAG,
  4892. STYLE_TAG: STYLE_TAG,
  4893. TEXTAREA_TAG: TEXTAREA_TAG,
  4894. IS_RAW: IS_RAW,
  4895. IS_SELF_CLOSING: IS_SELF_CLOSING,
  4896. IS_VOID: IS_VOID,
  4897. IS_BOOLEAN: IS_BOOLEAN,
  4898. IS_CUSTOM: IS_CUSTOM,
  4899. IS_SPREAD: IS_SPREAD
  4900. });
  4901. /**
  4902. * Not all the types are handled in this module.
  4903. *
  4904. * @enum {number}
  4905. * @readonly
  4906. */
  4907. const TAG = 1; /* TAG */
  4908. const ATTR = 2; /* ATTR */
  4909. const TEXT = 3; /* TEXT */
  4910. const CDATA = 4; /* CDATA */
  4911. const COMMENT = 8; /* COMMENT */
  4912. const DOCUMENT = 9; /* DOCUMENT */
  4913. const DOCTYPE = 10; /* DOCTYPE */
  4914. const DOCUMENT_FRAGMENT = 11; /* DOCUMENT_FRAGMENT */
  4915. var types$2 = /*#__PURE__*/Object.freeze({
  4916. __proto__: null,
  4917. TAG: TAG,
  4918. ATTR: ATTR,
  4919. TEXT: TEXT,
  4920. CDATA: CDATA,
  4921. COMMENT: COMMENT,
  4922. DOCUMENT: DOCUMENT,
  4923. DOCTYPE: DOCTYPE,
  4924. DOCUMENT_FRAGMENT: DOCUMENT_FRAGMENT
  4925. });
  4926. const rootTagNotFound = 'Root tag not found.';
  4927. const unclosedTemplateLiteral = 'Unclosed ES6 template literal.';
  4928. const unexpectedEndOfFile = 'Unexpected end of file.';
  4929. const unclosedComment = 'Unclosed comment.';
  4930. const unclosedNamedBlock = 'Unclosed "%1" block.';
  4931. const duplicatedNamedTag = 'Duplicate tag "<%1>".';
  4932. const unexpectedCharInExpression = 'Unexpected character %1.';
  4933. const unclosedExpression = 'Unclosed expression.';
  4934. /**
  4935. * Matches the start of valid tags names; used with the first 2 chars after the `'<'`.
  4936. * @const
  4937. * @private
  4938. */
  4939. const TAG_2C = /^(?:\/[a-zA-Z]|[a-zA-Z][^\s>/]?)/;
  4940. /**
  4941. * Matches valid tags names AFTER the validation with `TAG_2C`.
  4942. * $1: tag name including any `'/'`, $2: non self-closing brace (`>`) w/o attributes.
  4943. * @const
  4944. * @private
  4945. */
  4946. const TAG_NAME = /(\/?[^\s>/]+)\s*(>)?/g;
  4947. /**
  4948. * Matches an attribute name-value pair (both can be empty).
  4949. * $1: attribute name, $2: value including any quotes.
  4950. * @const
  4951. * @private
  4952. */
  4953. const ATTR_START = /(\S[^>/=\s]*)(?:\s*=\s*([^>/])?)?/g;
  4954. /**
  4955. * Matches the spread operator
  4956. * it will be used for the spread attributes
  4957. * @type {RegExp}
  4958. */
  4959. const SPREAD_OPERATOR = /\.\.\./;
  4960. /**
  4961. * Matches the closing tag of a `script` and `style` block.
  4962. * Used by parseText fo find the end of the block.
  4963. * @const
  4964. * @private
  4965. */
  4966. const RE_SCRYLE = {
  4967. script: /<\/script\s*>/gi,
  4968. style: /<\/style\s*>/gi,
  4969. textarea: /<\/textarea\s*>/gi
  4970. };
  4971. // Do not touch text content inside this tags
  4972. const RAW_TAGS = /^\/?(?:pre|textarea)$/;
  4973. /**
  4974. * Add an item into a collection, if the collection is not an array
  4975. * we create one and add the item to it
  4976. * @param {Array} collection - target collection
  4977. * @param {*} item - item to add to the collection
  4978. * @returns {Array} array containing the new item added to it
  4979. */
  4980. function addToCollection(collection = [], item) {
  4981. collection.push(item);
  4982. return collection
  4983. }
  4984. /**
  4985. * Run RegExp.exec starting from a specific position
  4986. * @param {RegExp} re - regex
  4987. * @param {number} pos - last index position
  4988. * @param {string} string - regex target
  4989. * @returns {Array} regex result
  4990. */
  4991. function execFromPos(re, pos, string) {
  4992. re.lastIndex = pos;
  4993. return re.exec(string)
  4994. }
  4995. /**
  4996. * Escape special characters in a given string, in preparation to create a regex.
  4997. *
  4998. * @param {string} str - Raw string
  4999. * @returns {string} Escaped string.
  5000. */
  5001. var escapeStr = (str) => str.replace(/(?=[-[\](){^*+?.$|\\])/g, '\\');
  5002. function formatError(data, message, pos) {
  5003. if (!pos) {
  5004. pos = data.length;
  5005. }
  5006. // count unix/mac/win eols
  5007. const line = (data.slice(0, pos).match(/\r\n?|\n/g) || '').length + 1;
  5008. let col = 0;
  5009. while (--pos >= 0 && !/[\r\n]/.test(data[pos])) {
  5010. ++col;
  5011. }
  5012. return `[${line},${col}]: ${message}`
  5013. }
  5014. const $_ES6_BQ = '`';
  5015. /**
  5016. * Searches the next backquote that signals the end of the ES6 Template Literal
  5017. * or the "${" sequence that starts a JS expression, skipping any escaped
  5018. * character.
  5019. *
  5020. * @param {string} code - Whole code
  5021. * @param {number} pos - The start position of the template
  5022. * @param {string[]} stack - To save nested ES6 TL count
  5023. * @returns {number} The end of the string (-1 if not found)
  5024. */
  5025. function skipES6TL(code, pos, stack) {
  5026. // we are in the char following the backquote (`),
  5027. // find the next unescaped backquote or the sequence "${"
  5028. const re = /[`$\\]/g;
  5029. let c;
  5030. while (re.lastIndex = pos, re.exec(code)) {
  5031. pos = re.lastIndex;
  5032. c = code[pos - 1];
  5033. if (c === '`') {
  5034. return pos
  5035. }
  5036. if (c === '$' && code[pos++] === '{') {
  5037. stack.push($_ES6_BQ, '}');
  5038. return pos
  5039. }
  5040. // else this is an escaped char
  5041. }
  5042. throw formatError(code, unclosedTemplateLiteral, pos)
  5043. }
  5044. /**
  5045. * Custom error handler can be implemented replacing this method.
  5046. * The `state` object includes the buffer (`data`)
  5047. * The error position (`loc`) contains line (base 1) and col (base 0).
  5048. * @param {string} data - string containing the error
  5049. * @param {string} msg - Error message
  5050. * @param {number} pos - Position of the error
  5051. * @returns {undefined} throw an exception error
  5052. */
  5053. function panic$1(data, msg, pos) {
  5054. const message = formatError(data, msg, pos);
  5055. throw new Error(message)
  5056. }
  5057. // forked from https://github.com/aMarCruz/skip-regex
  5058. // safe characters to precced a regex (including `=>`, `**`, and `...`)
  5059. const beforeReChars = '[{(,;:?=|&!^~>%*/';
  5060. const beforeReSign = `${beforeReChars}+-`;
  5061. // keyword that can preceed a regex (`in` is handled as special case)
  5062. const beforeReWords = [
  5063. 'case',
  5064. 'default',
  5065. 'do',
  5066. 'else',
  5067. 'in',
  5068. 'instanceof',
  5069. 'prefix',
  5070. 'return',
  5071. 'typeof',
  5072. 'void',
  5073. 'yield'
  5074. ];
  5075. // Last chars of all the beforeReWords elements to speed up the process.
  5076. const wordsEndChar = beforeReWords.reduce((s, w) => s + w.slice(-1), '');
  5077. // Matches literal regex from the start of the buffer.
  5078. // The buffer to search must not include line-endings.
  5079. const RE_LIT_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/;
  5080. // Valid characters for JavaScript variable names and literal numbers.
  5081. const RE_JS_VCHAR = /[$\w]/;
  5082. // Match dot characters that could be part of tricky regex
  5083. const RE_DOT_CHAR = /.*/g;
  5084. /**
  5085. * Searches the position of the previous non-blank character inside `code`,
  5086. * starting with `pos - 1`.
  5087. *
  5088. * @param {string} code - Buffer to search
  5089. * @param {number} pos - Starting position
  5090. * @returns {number} Position of the first non-blank character to the left.
  5091. * @private
  5092. */
  5093. function _prev(code, pos) {
  5094. while (--pos >= 0 && /\s/.test(code[pos]));
  5095. return pos
  5096. }
  5097. /**
  5098. * Check if the character in the `start` position within `code` can be a regex
  5099. * and returns the position following this regex or `start+1` if this is not
  5100. * one.
  5101. *
  5102. * NOTE: Ensure `start` points to a slash (this is not checked).
  5103. *
  5104. * @function skipRegex
  5105. * @param {string} code - Buffer to test in
  5106. * @param {number} start - Position the first slash inside `code`
  5107. * @returns {number} Position of the char following the regex.
  5108. *
  5109. */
  5110. /* istanbul ignore next */
  5111. function skipRegex(code, start) {
  5112. let pos = RE_DOT_CHAR.lastIndex = start++;
  5113. // `exec()` will extract from the slash to the end of the line
  5114. // and the chained `match()` will match the possible regex.
  5115. const match = (RE_DOT_CHAR.exec(code) || ' ')[0].match(RE_LIT_REGEX);
  5116. if (match) {
  5117. const next = pos + match[0].length; // result comes from `re.match`
  5118. pos = _prev(code, pos);
  5119. let c = code[pos];
  5120. // start of buffer or safe prefix?
  5121. if (pos < 0 || beforeReChars.includes(c)) {
  5122. return next
  5123. }
  5124. // from here, `pos` is >= 0 and `c` is code[pos]
  5125. if (c === '.') {
  5126. // can be `...` or something silly like 5./2
  5127. if (code[pos - 1] === '.') {
  5128. start = next;
  5129. }
  5130. } else {
  5131. if (c === '+' || c === '-') {
  5132. // tricky case
  5133. if (code[--pos] !== c || // if have a single operator or
  5134. (pos = _prev(code, pos)) < 0 || // ...have `++` and no previous token
  5135. beforeReSign.includes(c = code[pos])) {
  5136. return next // ...this is a regex
  5137. }
  5138. }
  5139. if (wordsEndChar.includes(c)) { // looks like a keyword?
  5140. const end = pos + 1;
  5141. // get the complete (previous) keyword
  5142. while (--pos >= 0 && RE_JS_VCHAR.test(code[pos]));
  5143. // it is in the allowed keywords list?
  5144. if (beforeReWords.includes(code.slice(pos + 1, end))) {
  5145. start = next;
  5146. }
  5147. }
  5148. }
  5149. }
  5150. return start
  5151. }
  5152. /*
  5153. * Mini-parser for expressions.
  5154. * The main pourpose of this module is to find the end of an expression
  5155. * and return its text without the enclosing brackets.
  5156. * Does not works with comments, but supports ES6 template strings.
  5157. */
  5158. /**
  5159. * @exports exprExtr
  5160. */
  5161. const S_SQ_STR = /'[^'\n\r\\]*(?:\\(?:\r\n?|[\S\s])[^'\n\r\\]*)*'/.source;
  5162. /**
  5163. * Matches double quoted JS strings taking care about nested quotes
  5164. * and EOLs (escaped EOLs are Ok).
  5165. *
  5166. * @const
  5167. * @private
  5168. */
  5169. const S_STRING = `${S_SQ_STR}|${S_SQ_STR.replace(/'/g, '"')}`;
  5170. /**
  5171. * Regex cache
  5172. *
  5173. * @type {Object.<string, RegExp>}
  5174. * @const
  5175. * @private
  5176. */
  5177. const reBr = {};
  5178. /**
  5179. * Makes an optimal regex that matches quoted strings, brackets, backquotes
  5180. * and the closing brackets of an expression.
  5181. *
  5182. * @param {string} b - Closing brackets
  5183. * @returns {RegExp} - optimized regex
  5184. */
  5185. function _regex(b) {
  5186. let re = reBr[b];
  5187. if (!re) {
  5188. let s = escapeStr(b);
  5189. if (b.length > 1) {
  5190. s = `${s}|[`;
  5191. } else {
  5192. s = /[{}[\]()]/.test(b) ? '[' : `[${s}`;
  5193. }
  5194. reBr[b] = re = new RegExp(`${S_STRING}|${s}\`/\\{}[\\]()]`, 'g');
  5195. }
  5196. return re
  5197. }
  5198. /**
  5199. * Update the scopes stack removing or adding closures to it
  5200. * @param {Array} stack - array stacking the expression closures
  5201. * @param {string} char - current char to add or remove from the stack
  5202. * @param {string} idx - matching index
  5203. * @param {string} code - expression code
  5204. * @returns {Object} result
  5205. * @returns {Object} result.char - either the char received or the closing braces
  5206. * @returns {Object} result.index - either a new index to skip part of the source code,
  5207. * or 0 to keep from parsing from the old position
  5208. */
  5209. function updateStack(stack, char, idx, code) {
  5210. let index = 0;
  5211. switch (char) {
  5212. case '[':
  5213. case '(':
  5214. case '{':
  5215. stack.push(char === '[' ? ']' : char === '(' ? ')' : '}');
  5216. break
  5217. case ')':
  5218. case ']':
  5219. case '}':
  5220. if (char !== stack.pop()) {
  5221. panic$1(code, unexpectedCharInExpression.replace('%1', char), index);
  5222. }
  5223. if (char === '}' && stack[stack.length - 1] === $_ES6_BQ) {
  5224. char = stack.pop();
  5225. }
  5226. index = idx + 1;
  5227. break
  5228. case '/':
  5229. index = skipRegex(code, idx);
  5230. }
  5231. return { char, index }
  5232. }
  5233. /**
  5234. * Parses the code string searching the end of the expression.
  5235. * It skips braces, quoted strings, regexes, and ES6 template literals.
  5236. *
  5237. * @function exprExtr
  5238. * @param {string} code - Buffer to parse
  5239. * @param {number} start - Position of the opening brace
  5240. * @param {[string,string]} bp - Brackets pair
  5241. * @returns {Object} Expression's end (after the closing brace) or -1
  5242. * if it is not an expr.
  5243. */
  5244. function exprExtr(code, start, bp) {
  5245. const [openingBraces, closingBraces] = bp;
  5246. const offset = start + openingBraces.length; // skips the opening brace
  5247. const stack = []; // expected closing braces ('`' for ES6 TL)
  5248. const re = _regex(closingBraces);
  5249. re.lastIndex = offset; // begining of the expression
  5250. let end;
  5251. let match;
  5252. while (match = re.exec(code)) { // eslint-disable-line
  5253. const idx = match.index;
  5254. const str = match[0];
  5255. end = re.lastIndex;
  5256. // end the iteration
  5257. if (str === closingBraces && !stack.length) {
  5258. return {
  5259. text: code.slice(offset, idx),
  5260. start,
  5261. end
  5262. }
  5263. }
  5264. const { char, index } = updateStack(stack, str[0], idx, code);
  5265. // update the end value depending on the new index received
  5266. end = index || end;
  5267. // update the regex last index
  5268. re.lastIndex = char === $_ES6_BQ ? skipES6TL(code, end, stack) : end;
  5269. }
  5270. if (stack.length) {
  5271. panic$1(code, unclosedExpression, end);
  5272. }
  5273. }
  5274. /**
  5275. * Outputs the last parsed node. Can be used with a builder too.
  5276. *
  5277. * @param {ParserStore} store - Parsing store
  5278. * @returns {undefined} void function
  5279. * @private
  5280. */
  5281. function flush(store) {
  5282. const last = store.last;
  5283. store.last = null;
  5284. if (last && store.root) {
  5285. store.builder.push(last);
  5286. }
  5287. }
  5288. /**
  5289. * Get the code chunks from start and end range
  5290. * @param {string} source - source code
  5291. * @param {number} start - Start position of the chunk we want to extract
  5292. * @param {number} end - Ending position of the chunk we need
  5293. * @returns {string} chunk of code extracted from the source code received
  5294. * @private
  5295. */
  5296. function getChunk(source, start, end) {
  5297. return source.slice(start, end)
  5298. }
  5299. /**
  5300. * states text in the last text node, or creates a new one if needed.
  5301. *
  5302. * @param {ParserState} state - Current parser state
  5303. * @param {number} start - Start position of the tag
  5304. * @param {number} end - Ending position (last char of the tag)
  5305. * @param {Object} extra - extra properties to add to the text node
  5306. * @param {RawExpr[]} extra.expressions - Found expressions
  5307. * @param {string} extra.unescape - Brackets to unescape
  5308. * @returns {undefined} - void function
  5309. * @private
  5310. */
  5311. function pushText(state, start, end, extra = {}) {
  5312. const text = getChunk(state.data, start, end);
  5313. const expressions = extra.expressions;
  5314. const unescape = extra.unescape;
  5315. let q = state.last;
  5316. state.pos = end;
  5317. if (q && q.type === TEXT) {
  5318. q.text += text;
  5319. q.end = end;
  5320. } else {
  5321. flush(state);
  5322. state.last = q = { type: TEXT, text, start, end };
  5323. }
  5324. if (expressions && expressions.length) {
  5325. q.expressions = (q.expressions || []).concat(expressions);
  5326. }
  5327. if (unescape) {
  5328. q.unescape = unescape;
  5329. }
  5330. return TEXT
  5331. }
  5332. /**
  5333. * Find the end of the attribute value or text node
  5334. * Extract expressions.
  5335. * Detect if value have escaped brackets.
  5336. *
  5337. * @param {ParserState} state - Parser state
  5338. * @param {HasExpr} node - Node if attr, info if text
  5339. * @param {string} endingChars - Ends the value or text
  5340. * @param {number} start - Starting position
  5341. * @returns {number} Ending position
  5342. * @private
  5343. */
  5344. function expr(state, node, endingChars, start) {
  5345. const re = b0re(state, endingChars);
  5346. re.lastIndex = start; // reset re position
  5347. const { unescape, expressions, end } = parseExpressions(state, re);
  5348. if (node) {
  5349. if (unescape) {
  5350. node.unescape = unescape;
  5351. }
  5352. if (expressions.length) {
  5353. node.expressions = expressions;
  5354. }
  5355. } else {
  5356. pushText(state, start, end, {expressions, unescape});
  5357. }
  5358. return end
  5359. }
  5360. /**
  5361. * Parse a text chunk finding all the expressions in it
  5362. * @param {ParserState} state - Parser state
  5363. * @param {RegExp} re - regex to match the expressions contents
  5364. * @returns {Object} result containing the expression found, the string to unescape and the end position
  5365. */
  5366. function parseExpressions(state, re) {
  5367. const { data, options } = state;
  5368. const { brackets } = options;
  5369. const expressions = [];
  5370. let unescape, pos, match;
  5371. // Anything captured in $1 (closing quote or character) ends the loop...
  5372. while ((match = re.exec(data)) && !match[1]) {
  5373. // ...else, we have an opening bracket and maybe an expression.
  5374. pos = match.index;
  5375. if (data[pos - 1] === '\\') {
  5376. unescape = match[0]; // it is an escaped opening brace
  5377. } else {
  5378. const tmpExpr = exprExtr(data, pos, brackets);
  5379. if (tmpExpr) {
  5380. expressions.push(tmpExpr);
  5381. re.lastIndex = tmpExpr.end;
  5382. }
  5383. }
  5384. }
  5385. // Even for text, the parser needs match a closing char
  5386. if (!match) {
  5387. panic$1(data, unexpectedEndOfFile, pos);
  5388. }
  5389. return {
  5390. unescape,
  5391. expressions,
  5392. end: match.index
  5393. }
  5394. }
  5395. /**
  5396. * Creates a regex for the given string and the left bracket.
  5397. * The string is captured in $1.
  5398. *
  5399. * @param {ParserState} state - Parser state
  5400. * @param {string} str - String to search
  5401. * @returns {RegExp} Resulting regex.
  5402. * @private
  5403. */
  5404. function b0re(state, str) {
  5405. const { brackets } = state.options;
  5406. const re = state.regexCache[str];
  5407. if (re) return re
  5408. const b0 = escapeStr(brackets[0]);
  5409. // cache the regex extending the regexCache object
  5410. Object.assign(state.regexCache, { [str]: new RegExp(`(${str})|${b0}`, 'g') });
  5411. return state.regexCache[str]
  5412. }
  5413. // similar to _.uniq
  5414. const uniq = l => l.filter((x, i, a) => a.indexOf(x) === i);
  5415. /**
  5416. * SVG void elements that cannot be auto-closed and shouldn't contain child nodes.
  5417. * @const {Array}
  5418. */
  5419. const VOID_SVG_TAGS_LIST = [
  5420. 'circle',
  5421. 'ellipse',
  5422. 'line',
  5423. 'path',
  5424. 'polygon',
  5425. 'polyline',
  5426. 'rect',
  5427. 'stop',
  5428. 'use'
  5429. ];
  5430. /**
  5431. * List of html elements where the value attribute is allowed
  5432. * @type {Array}
  5433. */
  5434. const HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST = [
  5435. 'button',
  5436. 'data',
  5437. 'input',
  5438. 'select',
  5439. 'li',
  5440. 'meter',
  5441. 'option',
  5442. 'output',
  5443. 'progress',
  5444. 'textarea',
  5445. 'param'
  5446. ];
  5447. /**
  5448. * List of all the available svg tags
  5449. * @const {Array}
  5450. * @see {@link https://github.com/wooorm/svg-tag-names}
  5451. */
  5452. const SVG_TAGS_LIST = uniq([
  5453. 'a',
  5454. 'altGlyph',
  5455. 'altGlyphDef',
  5456. 'altGlyphItem',
  5457. 'animate',
  5458. 'animateColor',
  5459. 'animateMotion',
  5460. 'animateTransform',
  5461. 'animation',
  5462. 'audio',
  5463. 'canvas',
  5464. 'clipPath',
  5465. 'color-profile',
  5466. 'cursor',
  5467. 'defs',
  5468. 'desc',
  5469. 'discard',
  5470. 'feBlend',
  5471. 'feColorMatrix',
  5472. 'feComponentTransfer',
  5473. 'feComposite',
  5474. 'feConvolveMatrix',
  5475. 'feDiffuseLighting',
  5476. 'feDisplacementMap',
  5477. 'feDistantLight',
  5478. 'feDropShadow',
  5479. 'feFlood',
  5480. 'feFuncA',
  5481. 'feFuncB',
  5482. 'feFuncG',
  5483. 'feFuncR',
  5484. 'feGaussianBlur',
  5485. 'feImage',
  5486. 'feMerge',
  5487. 'feMergeNode',
  5488. 'feMorphology',
  5489. 'feOffset',
  5490. 'fePointLight',
  5491. 'feSpecularLighting',
  5492. 'feSpotLight',
  5493. 'feTile',
  5494. 'feTurbulence',
  5495. 'filter',
  5496. 'font',
  5497. 'font-face',
  5498. 'font-face-format',
  5499. 'font-face-name',
  5500. 'font-face-src',
  5501. 'font-face-uri',
  5502. 'foreignObject',
  5503. 'g',
  5504. 'glyph',
  5505. 'glyphRef',
  5506. 'handler',
  5507. 'hatch',
  5508. 'hatchpath',
  5509. 'hkern',
  5510. 'iframe',
  5511. 'image',
  5512. 'linearGradient',
  5513. 'listener',
  5514. 'marker',
  5515. 'mask',
  5516. 'mesh',
  5517. 'meshgradient',
  5518. 'meshpatch',
  5519. 'meshrow',
  5520. 'metadata',
  5521. 'missing-glyph',
  5522. 'mpath',
  5523. 'pattern',
  5524. 'prefetch',
  5525. 'radialGradient',
  5526. 'script',
  5527. 'set',
  5528. 'solidColor',
  5529. 'solidcolor',
  5530. 'style',
  5531. 'svg',
  5532. 'switch',
  5533. 'symbol',
  5534. 'tbreak',
  5535. 'text',
  5536. 'textArea',
  5537. 'textPath',
  5538. 'title',
  5539. 'tref',
  5540. 'tspan',
  5541. 'unknown',
  5542. 'video',
  5543. 'view',
  5544. 'vkern'
  5545. ].concat(VOID_SVG_TAGS_LIST)).sort();
  5546. /**
  5547. * HTML void elements that cannot be auto-closed and shouldn't contain child nodes.
  5548. * @type {Array}
  5549. * @see {@link http://www.w3.org/TR/html-markup/syntax.html#syntax-elements}
  5550. * @see {@link http://www.w3.org/TR/html5/syntax.html#void-elements}
  5551. */
  5552. const VOID_HTML_TAGS_LIST = [
  5553. 'area',
  5554. 'base',
  5555. 'br',
  5556. 'col',
  5557. 'embed',
  5558. 'hr',
  5559. 'img',
  5560. 'input',
  5561. 'keygen',
  5562. 'link',
  5563. 'menuitem',
  5564. 'meta',
  5565. 'param',
  5566. 'source',
  5567. 'track',
  5568. 'wbr'
  5569. ];
  5570. /**
  5571. * List of all the html tags
  5572. * @const {Array}
  5573. * @see {@link https://github.com/sindresorhus/html-tags}
  5574. */
  5575. const HTML_TAGS_LIST = uniq([
  5576. 'a',
  5577. 'abbr',
  5578. 'address',
  5579. 'article',
  5580. 'aside',
  5581. 'audio',
  5582. 'b',
  5583. 'bdi',
  5584. 'bdo',
  5585. 'blockquote',
  5586. 'body',
  5587. 'canvas',
  5588. 'caption',
  5589. 'cite',
  5590. 'code',
  5591. 'colgroup',
  5592. 'datalist',
  5593. 'dd',
  5594. 'del',
  5595. 'details',
  5596. 'dfn',
  5597. 'dialog',
  5598. 'div',
  5599. 'dl',
  5600. 'dt',
  5601. 'em',
  5602. 'fieldset',
  5603. 'figcaption',
  5604. 'figure',
  5605. 'footer',
  5606. 'form',
  5607. 'h1',
  5608. 'h2',
  5609. 'h3',
  5610. 'h4',
  5611. 'h5',
  5612. 'h6',
  5613. 'head',
  5614. 'header',
  5615. 'hgroup',
  5616. 'html',
  5617. 'i',
  5618. 'iframe',
  5619. 'ins',
  5620. 'kbd',
  5621. 'label',
  5622. 'legend',
  5623. 'main',
  5624. 'map',
  5625. 'mark',
  5626. 'math',
  5627. 'menu',
  5628. 'nav',
  5629. 'noscript',
  5630. 'object',
  5631. 'ol',
  5632. 'optgroup',
  5633. 'p',
  5634. 'picture',
  5635. 'pre',
  5636. 'q',
  5637. 'rb',
  5638. 'rp',
  5639. 'rt',
  5640. 'rtc',
  5641. 'ruby',
  5642. 's',
  5643. 'samp',
  5644. 'script',
  5645. 'section',
  5646. 'select',
  5647. 'slot',
  5648. 'small',
  5649. 'span',
  5650. 'strong',
  5651. 'style',
  5652. 'sub',
  5653. 'summary',
  5654. 'sup',
  5655. 'svg',
  5656. 'table',
  5657. 'tbody',
  5658. 'td',
  5659. 'template',
  5660. 'tfoot',
  5661. 'th',
  5662. 'thead',
  5663. 'time',
  5664. 'title',
  5665. 'tr',
  5666. 'u',
  5667. 'ul',
  5668. 'var',
  5669. 'video'
  5670. ]
  5671. .concat(VOID_HTML_TAGS_LIST)
  5672. .concat(HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST)
  5673. ).sort();
  5674. /**
  5675. * List of all boolean HTML attributes
  5676. * @const {RegExp}
  5677. * @see {@link https://www.w3.org/TR/html5/infrastructure.html#sec-boolean-attributes}
  5678. */
  5679. const BOOLEAN_ATTRIBUTES_LIST = [
  5680. 'disabled',
  5681. 'visible',
  5682. 'checked',
  5683. 'readonly',
  5684. 'required',
  5685. 'allowfullscreen',
  5686. 'autofocus',
  5687. 'autoplay',
  5688. 'compact',
  5689. 'controls',
  5690. 'default',
  5691. 'formnovalidate',
  5692. 'hidden',
  5693. 'ismap',
  5694. 'itemscope',
  5695. 'loop',
  5696. 'multiple',
  5697. 'muted',
  5698. 'noresize',
  5699. 'noshade',
  5700. 'novalidate',
  5701. 'nowrap',
  5702. 'open',
  5703. 'reversed',
  5704. 'seamless',
  5705. 'selected',
  5706. 'sortable',
  5707. 'truespeed',
  5708. 'typemustmatch'
  5709. ];
  5710. /**
  5711. * Join a list of items with the pipe symbol (usefull for regex list concatenation)
  5712. * @private
  5713. * @param {Array} list - list of strings
  5714. * @returns {string} the list received joined with pipes
  5715. */
  5716. function joinWithPipe(list) {
  5717. return list.join('|')
  5718. }
  5719. /**
  5720. * Convert list of strings to regex in order to test against it ignoring the cases
  5721. * @private
  5722. * @param {...Array} lists - array of strings
  5723. * @returns {RegExp} regex that will match all the strings in the array received ignoring the cases
  5724. */
  5725. function listsToRegex(...lists) {
  5726. return new RegExp(`^/?(?:${joinWithPipe(lists.map(joinWithPipe))})$`, 'i')
  5727. }
  5728. /**
  5729. * Regex matching all the html tags ignoring the cases
  5730. * @const {RegExp}
  5731. */
  5732. const HTML_TAGS_RE = listsToRegex(HTML_TAGS_LIST);
  5733. /**
  5734. * Regex matching all the svg tags ignoring the cases
  5735. * @const {RegExp}
  5736. */
  5737. const SVG_TAGS_RE = listsToRegex(SVG_TAGS_LIST);
  5738. /**
  5739. * Regex matching all the void html tags ignoring the cases
  5740. * @const {RegExp}
  5741. */
  5742. const VOID_HTML_TAGS_RE = listsToRegex(VOID_HTML_TAGS_LIST);
  5743. /**
  5744. * Regex matching all the void svg tags ignoring the cases
  5745. * @const {RegExp}
  5746. */
  5747. const VOID_SVG_TAGS_RE = listsToRegex(VOID_SVG_TAGS_LIST);
  5748. /**
  5749. * Regex matching all the html tags where the value tag is allowed
  5750. * @const {RegExp}
  5751. */
  5752. const HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_RE = listsToRegex(HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST);
  5753. /**
  5754. * Regex matching all the boolean attributes
  5755. * @const {RegExp}
  5756. */
  5757. const BOOLEAN_ATTRIBUTES_RE = listsToRegex(BOOLEAN_ATTRIBUTES_LIST);
  5758. /**
  5759. * True if it's a self closing tag
  5760. * @param {string} tag - test tag
  5761. * @returns {boolean} true if void
  5762. * @example
  5763. * isVoid('meta') // true
  5764. * isVoid('circle') // true
  5765. * isVoid('IMG') // true
  5766. * isVoid('div') // false
  5767. * isVoid('mask') // false
  5768. */
  5769. function isVoid(tag) {
  5770. return [
  5771. VOID_HTML_TAGS_RE,
  5772. VOID_SVG_TAGS_RE
  5773. ].some(r => r.test(tag))
  5774. }
  5775. /**
  5776. * True if it's not SVG nor a HTML known tag
  5777. * @param {string} tag - test tag
  5778. * @returns {boolean} true if custom element
  5779. * @example
  5780. * isCustom('my-component') // true
  5781. * isCustom('div') // false
  5782. */
  5783. function isCustom(tag) {
  5784. return [
  5785. HTML_TAGS_RE,
  5786. SVG_TAGS_RE
  5787. ].every(l => !l.test(tag))
  5788. }
  5789. /**
  5790. * True if the value attribute is allowed on this tag
  5791. * @param {string} tag - test tag
  5792. * @returns {boolean} true if the value attribute is allowed
  5793. * @example
  5794. * hasValueAttribute('input') // true
  5795. * hasValueAttribute('div') // false
  5796. */
  5797. function hasValueAttribute(tag) {
  5798. return HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_RE.test(tag)
  5799. }
  5800. /**
  5801. * True if it's a boolean attribute
  5802. * @param {string} attribute - test attribute
  5803. * @returns {boolean} true if the attribute is a boolean type
  5804. * @example
  5805. * isBoolAttribute('selected') // true
  5806. * isBoolAttribute('class') // false
  5807. */
  5808. function isBoolAttribute(attribute) {
  5809. return BOOLEAN_ATTRIBUTES_RE.test(attribute)
  5810. }
  5811. /**
  5812. * Memoization function
  5813. * @param {Function} fn - function to memoize
  5814. * @returns {*} return of the function to memoize
  5815. */
  5816. function memoize(fn) {
  5817. const cache = new WeakMap();
  5818. return (...args) => {
  5819. if (cache.has(args[0])) return cache.get(args[0])
  5820. const ret = fn(...args);
  5821. cache.set(args[0], ret);
  5822. return ret
  5823. }
  5824. }
  5825. const expressionsContentRe = memoize(brackets => RegExp(`(${brackets[0]}[^${brackets[1]}]*?${brackets[1]})`, 'g'));
  5826. const isSpreadAttribute = name => SPREAD_OPERATOR.test(name);
  5827. const isAttributeExpression = (name, brackets) => name[0] === brackets[0];
  5828. const getAttributeEnd = (state, attr) => expr(state, attr, '[>/\\s]', attr.start);
  5829. /**
  5830. * The more complex parsing is for attributes as it can contain quoted or
  5831. * unquoted values or expressions.
  5832. *
  5833. * @param {ParserStore} state - Parser state
  5834. * @returns {number} New parser mode.
  5835. * @private
  5836. */
  5837. function attr(state) {
  5838. const { data, last, pos, root } = state;
  5839. const tag = last; // the last (current) tag in the output
  5840. const _CH = /\S/g; // matches the first non-space char
  5841. const ch = execFromPos(_CH, pos, data);
  5842. switch (true) {
  5843. case !ch:
  5844. state.pos = data.length; // reaching the end of the buffer with
  5845. // NodeTypes.ATTR will generate error
  5846. break
  5847. case ch[0] === '>':
  5848. // closing char found. If this is a self-closing tag with the name of the
  5849. // Root tag, we need decrement the counter as we are changing mode.
  5850. state.pos = tag.end = _CH.lastIndex;
  5851. if (tag[IS_SELF_CLOSING]) {
  5852. state.scryle = null; // allow selfClosing script/style tags
  5853. if (root && root.name === tag.name) {
  5854. state.count--; // "pop" root tag
  5855. }
  5856. }
  5857. return TEXT
  5858. case ch[0] === '/':
  5859. state.pos = _CH.lastIndex; // maybe. delegate the validation
  5860. tag[IS_SELF_CLOSING] = true; // the next loop
  5861. break
  5862. default:
  5863. delete tag[IS_SELF_CLOSING]; // ensure unmark as selfclosing tag
  5864. setAttribute(state, ch.index, tag);
  5865. }
  5866. return ATTR
  5867. }
  5868. /**
  5869. * Parses an attribute and its expressions.
  5870. *
  5871. * @param {ParserStore} state - Parser state
  5872. * @param {number} pos - Starting position of the attribute
  5873. * @param {Object} tag - Current parent tag
  5874. * @returns {undefined} void function
  5875. * @private
  5876. */
  5877. function setAttribute(state, pos, tag) {
  5878. const { data } = state;
  5879. const expressionContent = expressionsContentRe(state.options.brackets);
  5880. const re = ATTR_START; // (\S[^>/=\s]*)(?:\s*=\s*([^>/])?)? g
  5881. const start = re.lastIndex = expressionContent.lastIndex = pos; // first non-whitespace
  5882. const attrMatches = re.exec(data);
  5883. const isExpressionName = isAttributeExpression(attrMatches[1], state.options.brackets);
  5884. const match = isExpressionName ? [null, expressionContent.exec(data)[1], null] : attrMatches;
  5885. if (match) {
  5886. const end = re.lastIndex;
  5887. const attr = parseAttribute(state, match, start, end, isExpressionName);
  5888. //assert(q && q.type === Mode.TAG, 'no previous tag for the attr!')
  5889. // Pushes the attribute and shifts the `end` position of the tag (`last`).
  5890. state.pos = tag.end = attr.end;
  5891. tag.attributes = addToCollection(tag.attributes, attr);
  5892. }
  5893. }
  5894. function parseNomalAttribute(state, attr, quote) {
  5895. const { data } = state;
  5896. let { end } = attr;
  5897. if (isBoolAttribute(attr.name)) {
  5898. attr[IS_BOOLEAN] = true;
  5899. }
  5900. // parse the whole value (if any) and get any expressions on it
  5901. if (quote) {
  5902. // Usually, the value's first char (`quote`) is a quote and the lastIndex
  5903. // (`end`) is the start of the value.
  5904. let valueStart = end;
  5905. // If it not, this is an unquoted value and we need adjust the start.
  5906. if (quote !== '"' && quote !== '\'') {
  5907. quote = ''; // first char of value is not a quote
  5908. valueStart--; // adjust the starting position
  5909. }
  5910. end = expr(state, attr, quote || '[>/\\s]', valueStart);
  5911. // adjust the bounds of the value and save its content
  5912. return Object.assign(attr, {
  5913. value: getChunk(data, valueStart, end),
  5914. valueStart,
  5915. end: quote ? ++end : end
  5916. })
  5917. }
  5918. return attr
  5919. }
  5920. /**
  5921. * Parse expression names <a {href}>
  5922. * @param {ParserStore} state - Parser state
  5923. * @param {Object} attr - attribute object parsed
  5924. * @returns {Object} normalized attribute object
  5925. */
  5926. function parseSpreadAttribute(state, attr) {
  5927. const end = getAttributeEnd(state, attr);
  5928. return {
  5929. [IS_SPREAD]: true,
  5930. start: attr.start,
  5931. expressions: attr.expressions.map(expr => Object.assign(expr, {
  5932. text: expr.text.replace(SPREAD_OPERATOR, '').trim()
  5933. })),
  5934. end: end
  5935. }
  5936. }
  5937. /**
  5938. * Parse expression names <a {href}>
  5939. * @param {ParserStore} state - Parser state
  5940. * @param {Object} attr - attribute object parsed
  5941. * @returns {Object} normalized attribute object
  5942. */
  5943. function parseExpressionNameAttribute(state, attr) {
  5944. const end = getAttributeEnd(state, attr);
  5945. return {
  5946. start: attr.start,
  5947. name: attr.expressions[0].text.trim(),
  5948. expressions: attr.expressions,
  5949. end: end
  5950. }
  5951. }
  5952. /**
  5953. * Parse the attribute values normalising the quotes
  5954. * @param {ParserStore} state - Parser state
  5955. * @param {Array} match - results of the attributes regex
  5956. * @param {number} start - attribute start position
  5957. * @param {number} end - attribute end position
  5958. * @param {boolean} isExpressionName - true if the attribute name is an expression
  5959. * @returns {Object} attribute object
  5960. */
  5961. function parseAttribute(state, match, start, end, isExpressionName) {
  5962. const attr = {
  5963. name: match[1],
  5964. value: '',
  5965. start,
  5966. end
  5967. };
  5968. const quote = match[2]; // first letter of value or nothing
  5969. switch (true) {
  5970. case isSpreadAttribute(attr.name):
  5971. return parseSpreadAttribute(state, attr)
  5972. case isExpressionName === true:
  5973. return parseExpressionNameAttribute(state, attr)
  5974. default:
  5975. return parseNomalAttribute(state, attr, quote)
  5976. }
  5977. }
  5978. /**
  5979. * Parses comments in long or short form
  5980. * (any DOCTYPE & CDATA blocks are parsed as comments).
  5981. *
  5982. * @param {ParserState} state - Parser state
  5983. * @param {string} data - Buffer to parse
  5984. * @param {number} start - Position of the '<!' sequence
  5985. * @returns {number} node type id
  5986. * @private
  5987. */
  5988. function comment(state, data, start) {
  5989. const pos = start + 2; // skip '<!'
  5990. const str = data.substr(pos, 2) === '--' ? '-->' : '>';
  5991. const end = data.indexOf(str, pos);
  5992. if (end < 0) {
  5993. panic$1(data, unclosedComment, start);
  5994. }
  5995. pushComment$1(state, start, end + str.length);
  5996. return TEXT
  5997. }
  5998. /**
  5999. * Parse a comment.
  6000. *
  6001. * @param {ParserState} state - Current parser state
  6002. * @param {number} start - Start position of the tag
  6003. * @param {number} end - Ending position (last char of the tag)
  6004. * @returns {undefined} void function
  6005. * @private
  6006. */
  6007. function pushComment$1(state, start, end) {
  6008. flush(state);
  6009. state.pos = end;
  6010. if (state.options.comments === true) {
  6011. state.last = { type: COMMENT, start, end };
  6012. }
  6013. }
  6014. /**
  6015. * Pushes a new *tag* and set `last` to this, so any attributes
  6016. * will be included on this and shifts the `end`.
  6017. *
  6018. * @param {ParserState} state - Current parser state
  6019. * @param {string} name - Name of the node including any slash
  6020. * @param {number} start - Start position of the tag
  6021. * @param {number} end - Ending position (last char of the tag + 1)
  6022. * @returns {undefined} - void function
  6023. * @private
  6024. */
  6025. function pushTag(state, name, start, end) {
  6026. const root = state.root;
  6027. const last = { type: TAG, name, start, end };
  6028. if (isCustom(name)) {
  6029. last[IS_CUSTOM] = true;
  6030. }
  6031. if (isVoid(name)) {
  6032. last[IS_VOID] = true;
  6033. }
  6034. state.pos = end;
  6035. if (root) {
  6036. if (name === root.name) {
  6037. state.count++;
  6038. } else if (name === root.close) {
  6039. state.count--;
  6040. }
  6041. flush(state);
  6042. } else {
  6043. // start with root (keep ref to output)
  6044. state.root = { name: last.name, close: `/${name}` };
  6045. state.count = 1;
  6046. }
  6047. state.last = last;
  6048. }
  6049. /**
  6050. * Parse the tag following a '<' character, or delegate to other parser
  6051. * if an invalid tag name is found.
  6052. *
  6053. * @param {ParserState} state - Parser state
  6054. * @returns {number} New parser mode
  6055. * @private
  6056. */
  6057. function tag(state) {
  6058. const { pos, data } = state; // pos of the char following '<'
  6059. const start = pos - 1; // pos of '<'
  6060. const str = data.substr(pos, 2); // first two chars following '<'
  6061. switch (true) {
  6062. case str[0] === '!':
  6063. return comment(state, data, start)
  6064. case TAG_2C.test(str):
  6065. return parseTag(state, start)
  6066. default:
  6067. return pushText(state, start, pos) // pushes the '<' as text
  6068. }
  6069. }
  6070. function parseTag(state, start) {
  6071. const { data, pos } = state;
  6072. const re = TAG_NAME; // (\/?[^\s>/]+)\s*(>)? g
  6073. const match = execFromPos(re, pos, data);
  6074. const end = re.lastIndex;
  6075. const name = match[1].toLowerCase(); // $1: tag name including any '/'
  6076. // script/style block is parsed as another tag to extract attributes
  6077. if (name in RE_SCRYLE) {
  6078. state.scryle = name; // used by parseText
  6079. }
  6080. pushTag(state, name, start, end);
  6081. // only '>' can ends the tag here, the '/' is handled in parseAttribute
  6082. if (!match[2]) {
  6083. return ATTR
  6084. }
  6085. return TEXT
  6086. }
  6087. /**
  6088. * Parses regular text and script/style blocks ...scryle for short :-)
  6089. * (the content of script and style is text as well)
  6090. *
  6091. * @param {ParserState} state - Parser state
  6092. * @returns {number} New parser mode.
  6093. * @private
  6094. */
  6095. function text(state) {
  6096. const { pos, data, scryle } = state;
  6097. switch (true) {
  6098. case typeof scryle === 'string': {
  6099. const name = scryle;
  6100. const re = RE_SCRYLE[name];
  6101. const match = execFromPos(re, pos, data);
  6102. if (!match) {
  6103. panic$1(data, unclosedNamedBlock.replace('%1', name), pos - 1);
  6104. }
  6105. const start = match.index;
  6106. const end = re.lastIndex;
  6107. state.scryle = null; // reset the script/style flag now
  6108. // write the tag content, if any
  6109. if (start > pos) {
  6110. parseSpecialTagsContent(state, name, match);
  6111. }
  6112. // now the closing tag, either </script> or </style>
  6113. pushTag(state, `/${name}`, start, end);
  6114. break
  6115. }
  6116. case data[pos] === '<':
  6117. state.pos++;
  6118. return TAG
  6119. default:
  6120. expr(state, null, '<', pos);
  6121. }
  6122. return TEXT
  6123. }
  6124. /**
  6125. * Parse the text content depending on the name
  6126. * @param {ParserState} state - Parser state
  6127. * @param {string} name - one of the tags matched by the RE_SCRYLE regex
  6128. * @param {Array} match - result of the regex matching the content of the parsed tag
  6129. * @returns {undefined} void function
  6130. */
  6131. function parseSpecialTagsContent(state, name, match) {
  6132. const { pos } = state;
  6133. const start = match.index;
  6134. if (name === TEXTAREA_TAG) {
  6135. expr(state, null, match[0], pos);
  6136. } else {
  6137. pushText(state, pos, start);
  6138. }
  6139. }
  6140. /*---------------------------------------------------------------------
  6141. * Tree builder for the riot tag parser.
  6142. *
  6143. * The output has a root property and separate arrays for `html`, `css`,
  6144. * and `js` tags.
  6145. *
  6146. * The root tag is included as first element in the `html` array.
  6147. * Script tags marked with "defer" are included in `html` instead `js`.
  6148. *
  6149. * - Mark SVG tags
  6150. * - Mark raw tags
  6151. * - Mark void tags
  6152. * - Split prefixes from expressions
  6153. * - Unescape escaped brackets and escape EOLs and backslashes
  6154. * - Compact whitespace (option `compact`) for non-raw tags
  6155. * - Create an array `parts` for text nodes and attributes
  6156. *
  6157. * Throws on unclosed tags or closing tags without start tag.
  6158. * Selfclosing and void tags has no nodes[] property.
  6159. */
  6160. /**
  6161. * Escape the carriage return and the line feed from a string
  6162. * @param {string} string - input string
  6163. * @returns {string} output string escaped
  6164. */
  6165. function escapeReturn(string) {
  6166. return string
  6167. .replace(/\r/g, '\\r')
  6168. .replace(/\n/g, '\\n')
  6169. }
  6170. /**
  6171. * Escape double slashes in a string
  6172. * @param {string} string - input string
  6173. * @returns {string} output string escaped
  6174. */
  6175. function escapeSlashes(string) {
  6176. return string.replace(/\\/g, '\\\\')
  6177. }
  6178. /**
  6179. * Replace the multiple spaces with only one
  6180. * @param {string} string - input string
  6181. * @returns {string} string without trailing spaces
  6182. */
  6183. function cleanSpaces(string) {
  6184. return string.replace(/\s+/g, ' ')
  6185. }
  6186. const TREE_BUILDER_STRUCT = Object.seal({
  6187. get() {
  6188. const store = this.store;
  6189. // The real root tag is in store.root.nodes[0]
  6190. return {
  6191. [TEMPLATE_OUTPUT_NAME]: store.root.nodes[0],
  6192. [CSS_OUTPUT_NAME]: store[STYLE_TAG],
  6193. [JAVASCRIPT_OUTPUT_NAME]: store[JAVASCRIPT_TAG]
  6194. }
  6195. },
  6196. /**
  6197. * Process the current tag or text.
  6198. * @param {Object} node - Raw pseudo-node from the parser
  6199. * @returns {undefined} void function
  6200. */
  6201. push(node) {
  6202. const store = this.store;
  6203. switch (node.type) {
  6204. case TEXT:
  6205. this.pushText(store, node);
  6206. break
  6207. case TAG: {
  6208. const name = node.name;
  6209. const closingTagChar = '/';
  6210. const [firstChar] = name;
  6211. if (firstChar === closingTagChar && !node.isVoid) {
  6212. this.closeTag(store, node, name);
  6213. } else if (firstChar !== closingTagChar) {
  6214. this.openTag(store, node);
  6215. }
  6216. break
  6217. }
  6218. }
  6219. },
  6220. closeTag(store, node) {
  6221. const last = store.scryle || store.last;
  6222. last.end = node.end;
  6223. if (store.scryle) {
  6224. store.scryle = null;
  6225. } else {
  6226. store.last = store.stack.pop();
  6227. }
  6228. },
  6229. openTag(store, node) {
  6230. const name = node.name;
  6231. const attrs = node.attributes;
  6232. if ([JAVASCRIPT_TAG, STYLE_TAG].includes(name)) {
  6233. // Only accept one of each
  6234. if (store[name]) {
  6235. panic$1(this.store.data, duplicatedNamedTag.replace('%1', name), node.start);
  6236. }
  6237. store[name] = node;
  6238. store.scryle = store[name];
  6239. } else {
  6240. // store.last holds the last tag pushed in the stack and this are
  6241. // non-void, non-empty tags, so we are sure the `lastTag` here
  6242. // have a `nodes` property.
  6243. const lastTag = store.last;
  6244. const newNode = node;
  6245. lastTag.nodes.push(newNode);
  6246. if (lastTag[IS_RAW] || RAW_TAGS.test(name)) {
  6247. node[IS_RAW] = true;
  6248. }
  6249. if (!node[IS_SELF_CLOSING] && !node[IS_VOID]) {
  6250. store.stack.push(lastTag);
  6251. newNode.nodes = [];
  6252. store.last = newNode;
  6253. }
  6254. }
  6255. if (attrs) {
  6256. this.attrs(attrs);
  6257. }
  6258. },
  6259. attrs(attributes) {
  6260. attributes.forEach(attr => {
  6261. if (attr.value) {
  6262. this.split(attr, attr.value, attr.valueStart, true);
  6263. }
  6264. });
  6265. },
  6266. pushText(store, node) {
  6267. const text = node.text;
  6268. const empty = !/\S/.test(text);
  6269. const scryle = store.scryle;
  6270. if (!scryle) {
  6271. // store.last always have a nodes property
  6272. const parent = store.last;
  6273. const pack = this.compact && !parent[IS_RAW];
  6274. if (pack && empty) {
  6275. return
  6276. }
  6277. this.split(node, text, node.start, pack);
  6278. parent.nodes.push(node);
  6279. } else if (!empty) {
  6280. scryle.text = node;
  6281. }
  6282. },
  6283. split(node, source, start, pack) {
  6284. const expressions = node.expressions;
  6285. const parts = [];
  6286. if (expressions) {
  6287. let pos = 0;
  6288. expressions.forEach(expr => {
  6289. const text = source.slice(pos, expr.start - start);
  6290. const code = expr.text;
  6291. parts.push(this.sanitise(node, text, pack), escapeReturn(escapeSlashes(code).trim()));
  6292. pos = expr.end - start;
  6293. });
  6294. if (pos < node.end) {
  6295. parts.push(this.sanitise(node, source.slice(pos), pack));
  6296. }
  6297. } else {
  6298. parts[0] = this.sanitise(node, source, pack);
  6299. }
  6300. node.parts = parts.filter(p => p); // remove the empty strings
  6301. },
  6302. // unescape escaped brackets and split prefixes of expressions
  6303. sanitise(node, text, pack) {
  6304. let rep = node.unescape;
  6305. if (rep) {
  6306. let idx = 0;
  6307. rep = `\\${rep}`;
  6308. while ((idx = text.indexOf(rep, idx)) !== -1) {
  6309. text = text.substr(0, idx) + text.substr(idx + 1);
  6310. idx++;
  6311. }
  6312. }
  6313. text = escapeSlashes(text);
  6314. return pack ? cleanSpaces(text) : escapeReturn(text)
  6315. }
  6316. });
  6317. function createTreeBuilder(data, options) {
  6318. const root = {
  6319. type: TAG,
  6320. name: '',
  6321. start: 0,
  6322. end: 0,
  6323. nodes: []
  6324. };
  6325. return Object.assign(Object.create(TREE_BUILDER_STRUCT), {
  6326. compact: options.compact !== false,
  6327. store: {
  6328. last: root,
  6329. stack: [],
  6330. scryle: null,
  6331. root,
  6332. style: null,
  6333. script: null,
  6334. data
  6335. }
  6336. })
  6337. }
  6338. /**
  6339. * Factory for the Parser class, exposing only the `parse` method.
  6340. * The export adds the Parser class as property.
  6341. *
  6342. * @param {Object} options - User Options
  6343. * @param {Function} customBuilder - Tree builder factory
  6344. * @returns {Function} Public Parser implementation.
  6345. */
  6346. function parser(options, customBuilder) {
  6347. const state = curry(createParserState)(options, customBuilder || createTreeBuilder);
  6348. return {
  6349. parse: (data) => parse(state(data))
  6350. }
  6351. }
  6352. /**
  6353. * Create a new state object
  6354. * @param {Object} userOptions - parser options
  6355. * @param {Function} builder - Tree builder factory
  6356. * @param {string} data - data to parse
  6357. * @returns {ParserState} it represents the current parser state
  6358. */
  6359. function createParserState(userOptions, builder, data) {
  6360. const options = Object.assign({
  6361. brackets: ['{', '}']
  6362. }, userOptions);
  6363. return {
  6364. options,
  6365. regexCache: {},
  6366. pos: 0,
  6367. count: -1,
  6368. root: null,
  6369. last: null,
  6370. scryle: null,
  6371. builder: builder(data, options),
  6372. data
  6373. }
  6374. }
  6375. /**
  6376. * It creates a raw output of pseudo-nodes with one of three different types,
  6377. * all of them having a start/end position:
  6378. *
  6379. * - TAG -- Opening or closing tags
  6380. * - TEXT -- Raw text
  6381. * - COMMENT -- Comments
  6382. *
  6383. * @param {ParserState} state - Current parser state
  6384. * @returns {ParserResult} Result, contains data and output properties.
  6385. */
  6386. function parse(state) {
  6387. const { data } = state;
  6388. walk(state);
  6389. flush(state);
  6390. if (state.count) {
  6391. panic$1(data, state.count > 0 ? unexpectedEndOfFile : rootTagNotFound, state.pos);
  6392. }
  6393. return {
  6394. data,
  6395. output: state.builder.get()
  6396. }
  6397. }
  6398. /**
  6399. * Parser walking recursive function
  6400. * @param {ParserState} state - Current parser state
  6401. * @param {string} type - current parsing context
  6402. * @returns {undefined} void function
  6403. */
  6404. function walk(state, type) {
  6405. const { data } = state;
  6406. // extend the state adding the tree builder instance and the initial data
  6407. const length = data.length;
  6408. // The "count" property is set to 1 when the first tag is found.
  6409. // This becomes the root and precedent text or comments are discarded.
  6410. // So, at the end of the parsing count must be zero.
  6411. if (state.pos < length && state.count) {
  6412. walk(state, eat(state, type));
  6413. }
  6414. }
  6415. /**
  6416. * Function to help iterating on the current parser state
  6417. * @param {ParserState} state - Current parser state
  6418. * @param {string} type - current parsing context
  6419. * @returns {string} parsing context
  6420. */
  6421. function eat(state, type) {
  6422. switch (type) {
  6423. case TAG:
  6424. return tag(state)
  6425. case ATTR:
  6426. return attr(state)
  6427. default:
  6428. return text(state)
  6429. }
  6430. }
  6431. /**
  6432. * Expose the internal constants
  6433. */
  6434. const constants = c;
  6435. /**
  6436. * The nodeTypes definition
  6437. */
  6438. const nodeTypes = types$2;
  6439. const BINDING_TYPES = 'bindingTypes';
  6440. const EACH_BINDING_TYPE = 'EACH';
  6441. const IF_BINDING_TYPE = 'IF';
  6442. const TAG_BINDING_TYPE = 'TAG';
  6443. const SLOT_BINDING_TYPE = 'SLOT';
  6444. const EXPRESSION_TYPES = 'expressionTypes';
  6445. const ATTRIBUTE_EXPRESSION_TYPE = 'ATTRIBUTE';
  6446. const VALUE_EXPRESSION_TYPE = 'VALUE';
  6447. const TEXT_EXPRESSION_TYPE = 'TEXT';
  6448. const EVENT_EXPRESSION_TYPE = 'EVENT';
  6449. const TEMPLATE_FN = 'template';
  6450. const SCOPE = 'scope';
  6451. const GET_COMPONENT_FN = 'getComponent';
  6452. // keys needed to create the DOM bindings
  6453. const BINDING_SELECTOR_KEY = 'selector';
  6454. const BINDING_GET_COMPONENT_KEY = 'getComponent';
  6455. const BINDING_TEMPLATE_KEY = 'template';
  6456. const BINDING_TYPE_KEY = 'type';
  6457. const BINDING_REDUNDANT_ATTRIBUTE_KEY = 'redundantAttribute';
  6458. const BINDING_CONDITION_KEY = 'condition';
  6459. const BINDING_ITEM_NAME_KEY = 'itemName';
  6460. const BINDING_GET_KEY_KEY = 'getKey';
  6461. const BINDING_INDEX_NAME_KEY = 'indexName';
  6462. const BINDING_EVALUATE_KEY = 'evaluate';
  6463. const BINDING_NAME_KEY = 'name';
  6464. const BINDING_SLOTS_KEY = 'slots';
  6465. const BINDING_EXPRESSIONS_KEY = 'expressions';
  6466. const BINDING_CHILD_NODE_INDEX_KEY = 'childNodeIndex';
  6467. // slots keys
  6468. const BINDING_BINDINGS_KEY = 'bindings';
  6469. const BINDING_ID_KEY = 'id';
  6470. const BINDING_HTML_KEY = 'html';
  6471. const BINDING_ATTRIBUTES_KEY = 'attributes';
  6472. // DOM directives
  6473. const IF_DIRECTIVE = 'if';
  6474. const EACH_DIRECTIVE = 'each';
  6475. const KEY_ATTRIBUTE = 'key';
  6476. const SLOT_ATTRIBUTE = 'slot';
  6477. const NAME_ATTRIBUTE = 'name';
  6478. const IS_DIRECTIVE = 'is';
  6479. // Misc
  6480. const DEFAULT_SLOT_NAME = 'default';
  6481. const TEXT_NODE_EXPRESSION_PLACEHOLDER = ' ';
  6482. const BINDING_SELECTOR_PREFIX = 'expr';
  6483. const SLOT_TAG_NODE_NAME = 'slot';
  6484. const PROGRESS_TAG_NODE_NAME = 'progress';
  6485. // Riot Parser constants
  6486. const IS_RAW_NODE = constants.IS_RAW;
  6487. const IS_VOID_NODE = constants.IS_VOID;
  6488. const IS_CUSTOM_NODE = constants.IS_CUSTOM;
  6489. const IS_BOOLEAN_ATTRIBUTE = constants.IS_BOOLEAN;
  6490. const IS_SPREAD_ATTRIBUTE = constants.IS_SPREAD;
  6491. /**
  6492. * True if the node has not expression set nor bindings directives
  6493. * @param {RiotParser.Node} node - riot parser node
  6494. * @returns {boolean} true only if it's a static node that doesn't need bindings or expressions
  6495. */
  6496. function isStaticNode(node) {
  6497. return [
  6498. hasExpressions,
  6499. findEachAttribute,
  6500. findIfAttribute,
  6501. isCustomNode,
  6502. isSlotNode
  6503. ].every(test => !test(node))
  6504. }
  6505. /**
  6506. * Check if a node name is part of the browser or builtin javascript api or it belongs to the current scope
  6507. * @param { types.NodePath } path - containing the current node visited
  6508. * @returns {boolean} true if it's a global api variable
  6509. */
  6510. function isGlobal({ scope, node }) {
  6511. // recursively find the identifier of this AST path
  6512. if (node.object) {
  6513. return isGlobal({ node: node.object, scope })
  6514. }
  6515. return Boolean(
  6516. isRaw(node) ||
  6517. isBuiltinAPI(node) ||
  6518. isBrowserAPI(node) ||
  6519. isNewExpression(node) ||
  6520. isNodeInScope(scope, node)
  6521. )
  6522. }
  6523. /**
  6524. * Checks if the identifier of a given node exists in a scope
  6525. * @param {Scope} scope - scope where to search for the identifier
  6526. * @param {types.Node} node - node to search for the identifier
  6527. * @returns {boolean} true if the node identifier is defined in the given scope
  6528. */
  6529. function isNodeInScope(scope, node) {
  6530. const traverse = (isInScope = false) => {
  6531. types.visit(node, {
  6532. visitIdentifier(path) {
  6533. if (scope.lookup(getName(path.node))) {
  6534. isInScope = true;
  6535. }
  6536. this.abort();
  6537. }
  6538. });
  6539. return isInScope
  6540. };
  6541. return traverse()
  6542. }
  6543. /**
  6544. * True if the node has the isCustom attribute set
  6545. * @param {RiotParser.Node} node - riot parser node
  6546. * @returns {boolean} true if either it's a riot component or a custom element
  6547. */
  6548. function isCustomNode(node) {
  6549. return !!(node[IS_CUSTOM_NODE] || hasIsAttribute(node))
  6550. }
  6551. /**
  6552. * True the node is <slot>
  6553. * @param {RiotParser.Node} node - riot parser node
  6554. * @returns {boolean} true if it's a slot node
  6555. */
  6556. function isSlotNode(node) {
  6557. return node.name === SLOT_TAG_NODE_NAME
  6558. }
  6559. /**
  6560. * True if the node has the isVoid attribute set
  6561. * @param {RiotParser.Node} node - riot parser node
  6562. * @returns {boolean} true if the node is self closing
  6563. */
  6564. function isVoidNode(node) {
  6565. return !!node[IS_VOID_NODE]
  6566. }
  6567. /**
  6568. * True if the riot parser did find a tag node
  6569. * @param {RiotParser.Node} node - riot parser node
  6570. * @returns {boolean} true only for the tag nodes
  6571. */
  6572. function isTagNode(node) {
  6573. return node.type === nodeTypes.TAG
  6574. }
  6575. /**
  6576. * True if the riot parser did find a text node
  6577. * @param {RiotParser.Node} node - riot parser node
  6578. * @returns {boolean} true only for the text nodes
  6579. */
  6580. function isTextNode(node) {
  6581. return node.type === nodeTypes.TEXT
  6582. }
  6583. /**
  6584. * True if the node parsed is the root one
  6585. * @param {RiotParser.Node} node - riot parser node
  6586. * @returns {boolean} true only for the root nodes
  6587. */
  6588. function isRootNode(node) {
  6589. return node.isRoot
  6590. }
  6591. /**
  6592. * True if the attribute parsed is of type spread one
  6593. * @param {RiotParser.Node} node - riot parser node
  6594. * @returns {boolean} true if the attribute node is of type spread
  6595. */
  6596. function isSpreadAttribute$1(node) {
  6597. return node[IS_SPREAD_ATTRIBUTE]
  6598. }
  6599. /**
  6600. * True if the node is an attribute and its name is "value"
  6601. * @param {RiotParser.Node} node - riot parser node
  6602. * @returns {boolean} true only for value attribute nodes
  6603. */
  6604. function isValueAttribute(node) {
  6605. return node.name === 'value'
  6606. }
  6607. /**
  6608. * True if the DOM node is a progress tag
  6609. * @param {RiotParser.Node} node - riot parser node
  6610. * @returns {boolean} true for the progress tags
  6611. */
  6612. function isProgressNode(node) {
  6613. return node.name === PROGRESS_TAG_NODE_NAME
  6614. }
  6615. /**
  6616. * True if the node is an attribute and a DOM handler
  6617. * @param {RiotParser.Node} node - riot parser node
  6618. * @returns {boolean} true only for dom listener attribute nodes
  6619. */
  6620. const isEventAttribute = (() => {
  6621. const EVENT_ATTR_RE = /^on/;
  6622. return node => EVENT_ATTR_RE.test(node.name)
  6623. })();
  6624. /**
  6625. * True if the node has expressions or expression attributes
  6626. * @param {RiotParser.Node} node - riot parser node
  6627. * @returns {boolean} ditto
  6628. */
  6629. function hasExpressions(node) {
  6630. return !!(
  6631. node.expressions ||
  6632. // has expression attributes
  6633. (getNodeAttributes(node).some(attribute => hasExpressions(attribute))) ||
  6634. // has child text nodes with expressions
  6635. (node.nodes && node.nodes.some(node => isTextNode(node) && hasExpressions(node)))
  6636. )
  6637. }
  6638. /**
  6639. * True if the node is a directive having its own template
  6640. * @param {RiotParser.Node} node - riot parser node
  6641. * @returns {boolean} true only for the IF EACH and TAG bindings
  6642. */
  6643. function hasItsOwnTemplate(node) {
  6644. return [
  6645. findEachAttribute,
  6646. findIfAttribute,
  6647. isCustomNode
  6648. ].some(test => test(node))
  6649. }
  6650. const hasIfAttribute = compose(Boolean, findIfAttribute);
  6651. const hasEachAttribute = compose(Boolean, findEachAttribute);
  6652. const hasIsAttribute = compose(Boolean, findIsAttribute);
  6653. const hasKeyAttribute = compose(Boolean, findKeyAttribute);
  6654. /**
  6655. * Find the attribute node
  6656. * @param { string } name - name of the attribute we want to find
  6657. * @param { riotParser.nodeTypes.TAG } node - a tag node
  6658. * @returns { riotParser.nodeTypes.ATTR } attribute node
  6659. */
  6660. function findAttribute(name, node) {
  6661. return node.attributes && node.attributes.find(attr => getName(attr) === name)
  6662. }
  6663. function findIfAttribute(node) {
  6664. return findAttribute(IF_DIRECTIVE, node)
  6665. }
  6666. function findEachAttribute(node) {
  6667. return findAttribute(EACH_DIRECTIVE, node)
  6668. }
  6669. function findKeyAttribute(node) {
  6670. return findAttribute(KEY_ATTRIBUTE, node)
  6671. }
  6672. function findIsAttribute(node) {
  6673. return findAttribute(IS_DIRECTIVE, node)
  6674. }
  6675. /**
  6676. * Find all the node attributes that are not expressions
  6677. * @param {RiotParser.Node} node - riot parser node
  6678. * @returns {Array} list of all the static attributes
  6679. */
  6680. function findStaticAttributes(node) {
  6681. return getNodeAttributes(node).filter(attribute => !hasExpressions(attribute))
  6682. }
  6683. /**
  6684. * Find all the node attributes that have expressions
  6685. * @param {RiotParser.Node} node - riot parser node
  6686. * @returns {Array} list of all the dynamic attributes
  6687. */
  6688. function findDynamicAttributes(node) {
  6689. return getNodeAttributes(node).filter(hasExpressions)
  6690. }
  6691. /**
  6692. * Create a simple attribute expression
  6693. * @param {RiotParser.Node.Attr} sourceNode - the custom tag
  6694. * @param {string} sourceFile - source file path
  6695. * @param {string} sourceCode - original source
  6696. * @returns {AST.Node} object containing the expression binding keys
  6697. */
  6698. function createAttributeExpression(sourceNode, sourceFile, sourceCode) {
  6699. return builders.objectExpression([
  6700. simplePropertyNode(BINDING_TYPE_KEY,
  6701. builders.memberExpression(
  6702. builders.identifier(EXPRESSION_TYPES),
  6703. builders.identifier(ATTRIBUTE_EXPRESSION_TYPE),
  6704. false
  6705. )
  6706. ),
  6707. simplePropertyNode(BINDING_NAME_KEY, isSpreadAttribute$1(sourceNode) ? nullNode() : builders.literal(sourceNode.name)),
  6708. simplePropertyNode(
  6709. BINDING_EVALUATE_KEY,
  6710. createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
  6711. )
  6712. ])
  6713. }
  6714. /**
  6715. * Create a simple event expression
  6716. * @param {RiotParser.Node.Attr} sourceNode - attribute containing the event handlers
  6717. * @param {string} sourceFile - source file path
  6718. * @param {string} sourceCode - original source
  6719. * @returns {AST.Node} object containing the expression binding keys
  6720. */
  6721. function createEventExpression(sourceNode, sourceFile, sourceCode) {
  6722. return builders.objectExpression([
  6723. simplePropertyNode(BINDING_TYPE_KEY,
  6724. builders.memberExpression(
  6725. builders.identifier(EXPRESSION_TYPES),
  6726. builders.identifier(EVENT_EXPRESSION_TYPE),
  6727. false
  6728. )
  6729. ),
  6730. simplePropertyNode(BINDING_NAME_KEY, builders.literal(sourceNode.name)),
  6731. simplePropertyNode(
  6732. BINDING_EVALUATE_KEY,
  6733. createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
  6734. )
  6735. ])
  6736. }
  6737. /**
  6738. * Unescape the user escaped chars
  6739. * @param {string} string - input string
  6740. * @param {string} char - probably a '{' or anything the user want's to escape
  6741. * @returns {string} cleaned up string
  6742. */
  6743. function unescapeChar(string, char) {
  6744. return string.replace(RegExp(`\\\\${char}`, 'gm'), char)
  6745. }
  6746. /**
  6747. * Generate the pure immutable string chunks from a RiotParser.Node.Text
  6748. * @param {RiotParser.Node.Text} node - riot parser text node
  6749. * @param {string} sourceCode sourceCode - source code
  6750. * @returns {Array} array containing the immutable string chunks
  6751. */
  6752. function generateLiteralStringChunksFromNode(node, sourceCode) {
  6753. return node.expressions.reduce((chunks, expression, index) => {
  6754. const start = index ? node.expressions[index - 1].end : node.start;
  6755. const string = sourceCode.substring(start, expression.start);
  6756. // trimStart the first string
  6757. chunks.push(index === 0 ? string.trimStart() : string);
  6758. // add the tail to the string
  6759. if (index === node.expressions.length - 1)
  6760. chunks.push(sourceCode.substring(expression.end, node.end).trimEnd());
  6761. return chunks
  6762. }, [])
  6763. .map(str => node.unescape ? unescapeChar(str, node.unescape) : str)
  6764. }
  6765. /**
  6766. * Simple bindings might contain multiple expressions like for example: "{foo} and {bar}"
  6767. * This helper aims to merge them in a template literal if it's necessary
  6768. * @param {RiotParser.Node} node - riot parser node
  6769. * @param {string} sourceFile - original tag file
  6770. * @param {string} sourceCode - original tag source code
  6771. * @returns { Object } a template literal expression object
  6772. */
  6773. function mergeNodeExpressions(node, sourceFile, sourceCode) {
  6774. if (node.parts.length === 1)
  6775. return transformExpression(node.expressions[0], sourceFile, sourceCode)
  6776. const pureStringChunks = generateLiteralStringChunksFromNode(node, sourceCode);
  6777. const stringsArray = pureStringChunks.reduce((acc, str, index) => {
  6778. const expr = node.expressions[index];
  6779. return [
  6780. ...acc,
  6781. builders.literal(str),
  6782. expr ? transformExpression(expr, sourceFile, sourceCode) : nullNode()
  6783. ]
  6784. }, [])
  6785. // filter the empty literal expressions
  6786. .filter(expr => !isLiteral(expr) || expr.value);
  6787. return createArrayString(stringsArray)
  6788. }
  6789. /**
  6790. * Create a text expression
  6791. * @param {RiotParser.Node.Text} sourceNode - text node to parse
  6792. * @param {string} sourceFile - source file path
  6793. * @param {string} sourceCode - original source
  6794. * @param {number} childNodeIndex - position of the child text node in its parent children nodes
  6795. * @returns {AST.Node} object containing the expression binding keys
  6796. */
  6797. function createTextExpression(sourceNode, sourceFile, sourceCode, childNodeIndex) {
  6798. return builders.objectExpression([
  6799. simplePropertyNode(BINDING_TYPE_KEY,
  6800. builders.memberExpression(
  6801. builders.identifier(EXPRESSION_TYPES),
  6802. builders.identifier(TEXT_EXPRESSION_TYPE),
  6803. false
  6804. )
  6805. ),
  6806. simplePropertyNode(
  6807. BINDING_CHILD_NODE_INDEX_KEY,
  6808. builders.literal(childNodeIndex)
  6809. ),
  6810. simplePropertyNode(
  6811. BINDING_EVALUATE_KEY,
  6812. wrapASTInFunctionWithScope(
  6813. mergeNodeExpressions(sourceNode, sourceFile, sourceCode)
  6814. )
  6815. )
  6816. ])
  6817. }
  6818. function createValueExpression(sourceNode, sourceFile, sourceCode) {
  6819. return builders.objectExpression([
  6820. simplePropertyNode(BINDING_TYPE_KEY,
  6821. builders.memberExpression(
  6822. builders.identifier(EXPRESSION_TYPES),
  6823. builders.identifier(VALUE_EXPRESSION_TYPE),
  6824. false
  6825. )
  6826. ),
  6827. simplePropertyNode(
  6828. BINDING_EVALUATE_KEY,
  6829. createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode)
  6830. )
  6831. ])
  6832. }
  6833. function createExpression(sourceNode, sourceFile, sourceCode, childNodeIndex, parentNode) {
  6834. switch (true) {
  6835. case isTextNode(sourceNode):
  6836. return createTextExpression(sourceNode, sourceFile, sourceCode, childNodeIndex)
  6837. // progress nodes value attributes will be rendered as attributes
  6838. // see https://github.com/riot/compiler/issues/122
  6839. case isValueAttribute(sourceNode) && hasValueAttribute(parentNode.name) && !isProgressNode(parentNode):
  6840. return createValueExpression(sourceNode, sourceFile, sourceCode)
  6841. case isEventAttribute(sourceNode):
  6842. return createEventExpression(sourceNode, sourceFile, sourceCode)
  6843. default:
  6844. return createAttributeExpression(sourceNode, sourceFile, sourceCode)
  6845. }
  6846. }
  6847. /**
  6848. * Create the attribute expressions
  6849. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  6850. * @param {string} sourceFile - source file path
  6851. * @param {string} sourceCode - original source
  6852. * @returns {Array} array containing all the attribute expressions
  6853. */
  6854. function createAttributeExpressions(sourceNode, sourceFile, sourceCode) {
  6855. return findDynamicAttributes(sourceNode)
  6856. .map(attribute => createExpression(attribute, sourceFile, sourceCode, 0, sourceNode))
  6857. }
  6858. const scope = builders.identifier(SCOPE);
  6859. const getName = node => node && node.name ? node.name : node;
  6860. /**
  6861. * Replace the path scope with a member Expression
  6862. * @param { types.NodePath } path - containing the current node visited
  6863. * @param { types.Node } property - node we want to prefix with the scope identifier
  6864. * @returns {undefined} this is a void function
  6865. */
  6866. function replacePathScope(path, property) {
  6867. path.replace(builders.memberExpression(
  6868. scope,
  6869. property,
  6870. false
  6871. ));
  6872. }
  6873. /**
  6874. * Change the nodes scope adding the `scope` prefix
  6875. * @param { types.NodePath } path - containing the current node visited
  6876. * @returns { boolean } return false if we want to stop the tree traversal
  6877. * @context { types.visit }
  6878. */
  6879. function updateNodeScope(path) {
  6880. if (!isGlobal(path)) {
  6881. replacePathScope(path, path.node);
  6882. return false
  6883. }
  6884. this.traverse(path);
  6885. }
  6886. /**
  6887. * Change the scope of the member expressions
  6888. * @param { types.NodePath } path - containing the current node visited
  6889. * @returns { boolean } return always false because we want to check only the first node object
  6890. */
  6891. function visitMemberExpression(path) {
  6892. if (!isGlobal(path)) {
  6893. if (path.value.computed) {
  6894. this.traverse(path);
  6895. } else if (isBinaryExpression(path.node.object) || path.node.object.computed) {
  6896. this.traverse(path.get('object'));
  6897. } else if (!path.node.object.callee) {
  6898. replacePathScope(path, isThisExpression(path.node.object) ? path.node.property : path.node);
  6899. } else {
  6900. this.traverse(path.get('object'));
  6901. }
  6902. }
  6903. return false
  6904. }
  6905. /**
  6906. * Objects properties should be handled a bit differently from the Identifier
  6907. * @param { types.NodePath } path - containing the current node visited
  6908. * @returns { boolean } return false if we want to stop the tree traversal
  6909. */
  6910. function visitProperty(path) {
  6911. const value = path.node.value;
  6912. if (isIdentifier(value)) {
  6913. updateNodeScope(path.get('value'));
  6914. } else {
  6915. this.traverse(path.get('value'));
  6916. }
  6917. return false
  6918. }
  6919. /**
  6920. * The this expressions should be replaced with the scope
  6921. * @param { types.NodePath } path - containing the current node visited
  6922. * @returns { boolean|undefined } return false if we want to stop the tree traversal
  6923. */
  6924. function visitThisExpression(path) {
  6925. path.replace(scope);
  6926. this.traverse(path);
  6927. }
  6928. /**
  6929. * Update the scope of the global nodes
  6930. * @param { Object } ast - ast program
  6931. * @returns { Object } the ast program with all the global nodes updated
  6932. */
  6933. function updateNodesScope(ast) {
  6934. const ignorePath = () => false;
  6935. types.visit(ast, {
  6936. visitIdentifier: updateNodeScope,
  6937. visitMemberExpression,
  6938. visitProperty,
  6939. visitThisExpression,
  6940. visitClassExpression: ignorePath
  6941. });
  6942. return ast
  6943. }
  6944. /**
  6945. * Convert any expression to an AST tree
  6946. * @param { Object } expression - expression parsed by the riot parser
  6947. * @param { string } sourceFile - original tag file
  6948. * @param { string } sourceCode - original tag source code
  6949. * @returns { Object } the ast generated
  6950. */
  6951. function createASTFromExpression(expression, sourceFile, sourceCode) {
  6952. const code = sourceFile ?
  6953. addLineOffset(expression.text, sourceCode, expression) :
  6954. expression.text;
  6955. return generateAST(`(${code})`, {
  6956. sourceFileName: sourceFile
  6957. })
  6958. }
  6959. /**
  6960. * Create the bindings template property
  6961. * @param {Array} args - arguments to pass to the template function
  6962. * @returns {ASTNode} a binding template key
  6963. */
  6964. function createTemplateProperty(args) {
  6965. return simplePropertyNode(
  6966. BINDING_TEMPLATE_KEY,
  6967. args ? callTemplateFunction(...args) : nullNode()
  6968. )
  6969. }
  6970. /**
  6971. * Try to get the expression of an attribute node
  6972. * @param { RiotParser.Node.Attribute } attribute - riot parser attribute node
  6973. * @returns { RiotParser.Node.Expression } attribute expression value
  6974. */
  6975. function getAttributeExpression(attribute) {
  6976. return attribute.expressions ? attribute.expressions[0] : {
  6977. // if no expression was found try to typecast the attribute value
  6978. ...attribute,
  6979. text: attribute.value
  6980. }
  6981. }
  6982. /**
  6983. * Wrap the ast generated in a function call providing the scope argument
  6984. * @param {Object} ast - function body
  6985. * @returns {FunctionExpresion} function having the scope argument injected
  6986. */
  6987. function wrapASTInFunctionWithScope(ast) {
  6988. return builders.functionExpression(
  6989. null,
  6990. [scope],
  6991. builders.blockStatement([builders.returnStatement(
  6992. ast
  6993. )])
  6994. )
  6995. }
  6996. /**
  6997. * Convert any parser option to a valid template one
  6998. * @param { RiotParser.Node.Expression } expression - expression parsed by the riot parser
  6999. * @param { string } sourceFile - original tag file
  7000. * @param { string } sourceCode - original tag source code
  7001. * @returns { Object } a FunctionExpression object
  7002. *
  7003. * @example
  7004. * toScopedFunction('foo + bar') // scope.foo + scope.bar
  7005. *
  7006. * @example
  7007. * toScopedFunction('foo.baz + bar') // scope.foo.baz + scope.bar
  7008. */
  7009. function toScopedFunction(expression, sourceFile, sourceCode) {
  7010. return compose(
  7011. wrapASTInFunctionWithScope,
  7012. transformExpression
  7013. )(expression, sourceFile, sourceCode)
  7014. }
  7015. /**
  7016. * Transform an expression node updating its global scope
  7017. * @param {RiotParser.Node.Expr} expression - riot parser expression node
  7018. * @param {string} sourceFile - source file
  7019. * @param {string} sourceCode - source code
  7020. * @returns {ASTExpression} ast expression generated from the riot parser expression node
  7021. */
  7022. function transformExpression(expression, sourceFile, sourceCode) {
  7023. return compose(
  7024. getExpressionAST,
  7025. updateNodesScope,
  7026. createASTFromExpression
  7027. )(expression, sourceFile, sourceCode)
  7028. }
  7029. /**
  7030. * Get the parsed AST expression of riot expression node
  7031. * @param {AST.Program} sourceAST - raw node parsed
  7032. * @returns {AST.Expression} program expression output
  7033. */
  7034. function getExpressionAST(sourceAST) {
  7035. const astBody = sourceAST.program.body;
  7036. return astBody[0] ? astBody[0].expression : astBody
  7037. }
  7038. /**
  7039. * Create the template call function
  7040. * @param {Array|string|Node.Literal} template - template string
  7041. * @param {Array<AST.Nodes>} bindings - template bindings provided as AST nodes
  7042. * @returns {Node.CallExpression} template call expression
  7043. */
  7044. function callTemplateFunction(template, bindings) {
  7045. return builders.callExpression(builders.identifier(TEMPLATE_FN), [
  7046. template ? builders.literal(template) : nullNode(),
  7047. bindings ? builders.arrayExpression(bindings) : nullNode()
  7048. ])
  7049. }
  7050. /**
  7051. * Convert any DOM attribute into a valid DOM selector useful for the querySelector API
  7052. * @param { string } attributeName - name of the attribute to query
  7053. * @returns { string } the attribute transformed to a query selector
  7054. */
  7055. const attributeNameToDOMQuerySelector = attributeName => `[${attributeName}]`;
  7056. /**
  7057. * Create the properties to query a DOM node
  7058. * @param { string } attributeName - attribute name needed to identify a DOM node
  7059. * @returns { Array<AST.Node> } array containing the selector properties needed for the binding
  7060. */
  7061. function createSelectorProperties(attributeName) {
  7062. return attributeName ? [
  7063. simplePropertyNode(BINDING_REDUNDANT_ATTRIBUTE_KEY, builders.literal(attributeName)),
  7064. simplePropertyNode(BINDING_SELECTOR_KEY,
  7065. compose(builders.literal, attributeNameToDOMQuerySelector)(attributeName)
  7066. )
  7067. ] : []
  7068. }
  7069. /**
  7070. * Clone the node filtering out the selector attribute from the attributes list
  7071. * @param {RiotParser.Node} node - riot parser node
  7072. * @param {string} selectorAttribute - name of the selector attribute to filter out
  7073. * @returns {RiotParser.Node} the node with the attribute cleaned up
  7074. */
  7075. function cloneNodeWithoutSelectorAttribute(node, selectorAttribute) {
  7076. return {
  7077. ...node,
  7078. attributes: getAttributesWithoutSelector(getNodeAttributes(node), selectorAttribute)
  7079. }
  7080. }
  7081. /**
  7082. * Get the node attributes without the selector one
  7083. * @param {Array<RiotParser.Attr>} attributes - attributes list
  7084. * @param {string} selectorAttribute - name of the selector attribute to filter out
  7085. * @returns {Array<RiotParser.Attr>} filtered attributes
  7086. */
  7087. function getAttributesWithoutSelector(attributes, selectorAttribute) {
  7088. if (selectorAttribute)
  7089. return attributes.filter(attribute => attribute.name !== selectorAttribute)
  7090. return attributes
  7091. }
  7092. /**
  7093. * Clean binding or custom attributes
  7094. * @param {RiotParser.Node} node - riot parser node
  7095. * @returns {Array<RiotParser.Node.Attr>} only the attributes that are not bindings or directives
  7096. */
  7097. function cleanAttributes(node) {
  7098. return getNodeAttributes(node).filter(attribute => ![
  7099. IF_DIRECTIVE,
  7100. EACH_DIRECTIVE,
  7101. KEY_ATTRIBUTE,
  7102. SLOT_ATTRIBUTE,
  7103. IS_DIRECTIVE
  7104. ].includes(attribute.name))
  7105. }
  7106. /**
  7107. * Create a root node proxing only its nodes and attributes
  7108. * @param {RiotParser.Node} node - riot parser node
  7109. * @returns {RiotParser.Node} root node
  7110. */
  7111. function createRootNode(node) {
  7112. return {
  7113. nodes: getChildrenNodes(node),
  7114. isRoot: true,
  7115. attributes: compose(
  7116. // root nodes should always have attribute expressions
  7117. transformStatiAttributesIntoExpressions,
  7118. // root nodes shuold't have directives
  7119. cleanAttributes
  7120. )(node)
  7121. }
  7122. }
  7123. /**
  7124. * Transform the static node attributes into expressions, useful for the root nodes
  7125. * @param {Array<RiotParser.Node.Attr>} attributes - riot parser node
  7126. * @returns {Array<RiotParser.Node.Attr>} all the attributes received as attribute expressions
  7127. */
  7128. function transformStatiAttributesIntoExpressions(attributes) {
  7129. return attributes.map(attribute => {
  7130. if (attribute.expressions) return attribute
  7131. return {
  7132. ...attribute,
  7133. expressions: [{
  7134. start: attribute.valueStart,
  7135. end: attribute.end,
  7136. text: `'${attribute.value}'`
  7137. }]
  7138. }
  7139. })
  7140. }
  7141. /**
  7142. * Get all the child nodes of a RiotParser.Node
  7143. * @param {RiotParser.Node} node - riot parser node
  7144. * @returns {Array<RiotParser.Node>} all the child nodes found
  7145. */
  7146. function getChildrenNodes(node) {
  7147. return node && node.nodes ? node.nodes : []
  7148. }
  7149. /**
  7150. * Get all the attributes of a riot parser node
  7151. * @param {RiotParser.Node} node - riot parser node
  7152. * @returns {Array<RiotParser.Node.Attribute>} all the attributes find
  7153. */
  7154. function getNodeAttributes(node) {
  7155. return node.attributes ? node.attributes : []
  7156. }
  7157. /**
  7158. * Get the name of a custom node transforming it into an expression node
  7159. * @param {RiotParser.Node} node - riot parser node
  7160. * @returns {RiotParser.Node.Attr} the node name as expression attribute
  7161. */
  7162. function getCustomNodeNameAsExpression(node) {
  7163. const isAttribute = findIsAttribute(node);
  7164. const toRawString = val => `'${val}'`;
  7165. if (isAttribute) {
  7166. return isAttribute.expressions ? isAttribute.expressions[0] : {
  7167. ...isAttribute,
  7168. text: toRawString(isAttribute.value)
  7169. }
  7170. }
  7171. return { ...node, text: toRawString(getName(node)) }
  7172. }
  7173. /**
  7174. * Convert all the node static attributes to strings
  7175. * @param {RiotParser.Node} node - riot parser node
  7176. * @returns {string} all the node static concatenated as string
  7177. */
  7178. function staticAttributesToString(node) {
  7179. return findStaticAttributes(node)
  7180. .map(attribute => attribute[IS_BOOLEAN_ATTRIBUTE] || !attribute.value ?
  7181. attribute.name :
  7182. `${attribute.name}="${unescapeNode(attribute, 'value').value}"`
  7183. ).join(' ')
  7184. }
  7185. /**
  7186. * Make sure that node escaped chars will be unescaped
  7187. * @param {RiotParser.Node} node - riot parser node
  7188. * @param {string} key - key property to unescape
  7189. * @returns {RiotParser.Node} node with the text property unescaped
  7190. */
  7191. function unescapeNode(node, key) {
  7192. if (node.unescape) {
  7193. return {
  7194. ...node,
  7195. [key]: unescapeChar(node[key], node.unescape)
  7196. }
  7197. }
  7198. return node
  7199. }
  7200. /**
  7201. * Convert a riot parser opening node into a string
  7202. * @param {RiotParser.Node} node - riot parser node
  7203. * @returns {string} the node as string
  7204. */
  7205. function nodeToString(node) {
  7206. const attributes = staticAttributesToString(node);
  7207. switch(true) {
  7208. case isTagNode(node):
  7209. return `<${node.name}${attributes ? ` ${attributes}` : ''}${isVoidNode(node) ? '/' : ''}>`
  7210. case isTextNode(node):
  7211. return hasExpressions(node) ? TEXT_NODE_EXPRESSION_PLACEHOLDER : unescapeNode(node, 'text').text
  7212. default:
  7213. return ''
  7214. }
  7215. }
  7216. /**
  7217. * Close an html node
  7218. * @param {RiotParser.Node} node - riot parser node
  7219. * @returns {string} the closing tag of the html tag node passed to this function
  7220. */
  7221. function closeTag(node) {
  7222. return node.name ? `</${node.name}>` : ''
  7223. }
  7224. /**
  7225. * Create a strings array with the `join` call to transform it into a string
  7226. * @param {Array} stringsArray - array containing all the strings to concatenate
  7227. * @returns {AST.CallExpression} array with a `join` call
  7228. */
  7229. function createArrayString(stringsArray) {
  7230. return builders.callExpression(
  7231. builders.memberExpression(
  7232. builders.arrayExpression(stringsArray),
  7233. builders.identifier('join'),
  7234. false
  7235. ),
  7236. [builders.literal('')]
  7237. )
  7238. }
  7239. /**
  7240. * Simple expression bindings might contain multiple expressions like for example: "class="{foo} red {bar}""
  7241. * This helper aims to merge them in a template literal if it's necessary
  7242. * @param {RiotParser.Attr} node - riot parser node
  7243. * @param {string} sourceFile - original tag file
  7244. * @param {string} sourceCode - original tag source code
  7245. * @returns { Object } a template literal expression object
  7246. */
  7247. function mergeAttributeExpressions(node, sourceFile, sourceCode) {
  7248. if (!node.parts || node.parts.length === 1) {
  7249. return transformExpression(node.expressions[0], sourceFile, sourceCode)
  7250. }
  7251. const stringsArray = [
  7252. ...node.parts.reduce((acc, str) => {
  7253. const expression = node.expressions.find(e => e.text.trim() === str);
  7254. return [
  7255. ...acc,
  7256. expression ? transformExpression(expression, sourceFile, sourceCode) : builders.literal(str)
  7257. ]
  7258. }, [])
  7259. ].filter(expr => !isLiteral(expr) || expr.value);
  7260. return createArrayString(stringsArray)
  7261. }
  7262. /**
  7263. * Create a selector that will be used to find the node via dom-bindings
  7264. * @param {number} id - temporary variable that will be increased anytime this function will be called
  7265. * @returns {string} selector attribute needed to bind a riot expression
  7266. */
  7267. const createBindingSelector = (function createSelector(id = 0) {
  7268. return () => `${BINDING_SELECTOR_PREFIX}${id++}`
  7269. }());
  7270. /**
  7271. * Create the AST array containing the attributes to bind to this node
  7272. * @param { RiotParser.Node.Tag } sourceNode - the custom tag
  7273. * @param { string } selectorAttribute - attribute needed to select the target node
  7274. * @param { string } sourceFile - source file path
  7275. * @param { string } sourceCode - original source
  7276. * @returns {AST.ArrayExpression} array containing the slot objects
  7277. */
  7278. function createBindingAttributes(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7279. return builders.arrayExpression([
  7280. ...compose(
  7281. attributes => attributes.map(attribute => createExpression(attribute, sourceFile, sourceCode, 0, sourceNode)),
  7282. attributes => getAttributesWithoutSelector(attributes, selectorAttribute),
  7283. cleanAttributes
  7284. )(sourceNode)
  7285. ])
  7286. }
  7287. /**
  7288. * Create an attribute evaluation function
  7289. * @param {RiotParser.Attr} sourceNode - riot parser node
  7290. * @param {string} sourceFile - original tag file
  7291. * @param {string} sourceCode - original tag source code
  7292. * @returns { AST.Node } an AST function expression to evaluate the attribute value
  7293. */
  7294. function createAttributeEvaluationFunction(sourceNode, sourceFile, sourceCode) {
  7295. return hasExpressions(sourceNode) ?
  7296. // dynamic attribute
  7297. wrapASTInFunctionWithScope(mergeAttributeExpressions(sourceNode, sourceFile, sourceCode)) :
  7298. // static attribute
  7299. builders.functionExpression(
  7300. null,
  7301. [],
  7302. builders.blockStatement([
  7303. builders.returnStatement(builders.literal(sourceNode.value || true))
  7304. ])
  7305. )
  7306. }
  7307. /**
  7308. * Simple clone deep function, do not use it for classes or recursive objects!
  7309. * @param {*} source - possibily an object to clone
  7310. * @returns {*} the object we wanted to clone
  7311. */
  7312. function cloneDeep(source) {
  7313. return JSON.parse(JSON.stringify(source))
  7314. }
  7315. const getEachItemName = expression => isSequenceExpression(expression.left) ? expression.left.expressions[0] : expression.left;
  7316. const getEachIndexName = expression => isSequenceExpression(expression.left) ? expression.left.expressions[1] : null;
  7317. const getEachValue = expression => expression.right;
  7318. const nameToliteral = compose(builders.literal, getName);
  7319. const generateEachItemNameKey = expression => simplePropertyNode(
  7320. BINDING_ITEM_NAME_KEY,
  7321. compose(nameToliteral, getEachItemName)(expression)
  7322. );
  7323. const generateEachIndexNameKey = expression => simplePropertyNode(
  7324. BINDING_INDEX_NAME_KEY,
  7325. compose(nameToliteral, getEachIndexName)(expression)
  7326. );
  7327. const generateEachEvaluateKey = (expression, eachExpression, sourceFile, sourceCode) => simplePropertyNode(
  7328. BINDING_EVALUATE_KEY,
  7329. compose(
  7330. e => toScopedFunction(e, sourceFile, sourceCode),
  7331. e => ({
  7332. ...eachExpression,
  7333. text: generateJavascript(e).code
  7334. }),
  7335. getEachValue
  7336. )(expression)
  7337. );
  7338. /**
  7339. * Get the each expression properties to create properly the template binding
  7340. * @param { DomBinding.Expression } eachExpression - original each expression data
  7341. * @param { string } sourceFile - original tag file
  7342. * @param { string } sourceCode - original tag source code
  7343. * @returns { Array } AST nodes that are needed to build an each binding
  7344. */
  7345. function generateEachExpressionProperties(eachExpression, sourceFile, sourceCode) {
  7346. const ast = createASTFromExpression(eachExpression, sourceFile, sourceCode);
  7347. const body = ast.program.body;
  7348. const firstNode = body[0];
  7349. if (!isExpressionStatement(firstNode)) {
  7350. panic(`The each directives supported should be of type "ExpressionStatement",you have provided a "${firstNode.type}"`);
  7351. }
  7352. const { expression } = firstNode;
  7353. return [
  7354. generateEachItemNameKey(expression),
  7355. generateEachIndexNameKey(expression),
  7356. generateEachEvaluateKey(expression, eachExpression, sourceFile, sourceCode)
  7357. ]
  7358. }
  7359. /**
  7360. * Transform a RiotParser.Node.Tag into an each binding
  7361. * @param { RiotParser.Node.Tag } sourceNode - tag containing the each attribute
  7362. * @param { string } selectorAttribute - attribute needed to select the target node
  7363. * @param { string } sourceFile - source file path
  7364. * @param { string } sourceCode - original source
  7365. * @returns { AST.Node } an each binding node
  7366. */
  7367. function createEachBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7368. const [ifAttribute, eachAttribute, keyAttribute] = [
  7369. findIfAttribute,
  7370. findEachAttribute,
  7371. findKeyAttribute
  7372. ].map(f => f(sourceNode));
  7373. const attributeOrNull = attribute => attribute ? toScopedFunction(getAttributeExpression(attribute), sourceFile, sourceCode) : nullNode();
  7374. return builders.objectExpression([
  7375. simplePropertyNode(BINDING_TYPE_KEY,
  7376. builders.memberExpression(
  7377. builders.identifier(BINDING_TYPES),
  7378. builders.identifier(EACH_BINDING_TYPE),
  7379. false
  7380. )
  7381. ),
  7382. simplePropertyNode(BINDING_GET_KEY_KEY, attributeOrNull(keyAttribute)),
  7383. simplePropertyNode(BINDING_CONDITION_KEY, attributeOrNull(ifAttribute)),
  7384. createTemplateProperty(createNestedBindings(sourceNode, sourceFile, sourceCode, selectorAttribute)),
  7385. ...createSelectorProperties(selectorAttribute),
  7386. ...compose(generateEachExpressionProperties, getAttributeExpression)(eachAttribute)
  7387. ])
  7388. }
  7389. /**
  7390. * Transform a RiotParser.Node.Tag into an if binding
  7391. * @param { RiotParser.Node.Tag } sourceNode - tag containing the if attribute
  7392. * @param { string } selectorAttribute - attribute needed to select the target node
  7393. * @param { stiring } sourceFile - source file path
  7394. * @param { string } sourceCode - original source
  7395. * @returns { AST.Node } an if binding node
  7396. */
  7397. function createIfBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7398. const ifAttribute = findIfAttribute(sourceNode);
  7399. return builders.objectExpression([
  7400. simplePropertyNode(BINDING_TYPE_KEY,
  7401. builders.memberExpression(
  7402. builders.identifier(BINDING_TYPES),
  7403. builders.identifier(IF_BINDING_TYPE),
  7404. false
  7405. )
  7406. ),
  7407. simplePropertyNode(
  7408. BINDING_EVALUATE_KEY,
  7409. toScopedFunction(ifAttribute.expressions[0], sourceFile, sourceCode)
  7410. ),
  7411. ...createSelectorProperties(selectorAttribute),
  7412. createTemplateProperty(createNestedBindings(sourceNode, sourceFile, sourceCode, selectorAttribute))
  7413. ])
  7414. }
  7415. /**
  7416. * Create the text node expressions
  7417. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7418. * @param {string} sourceFile - source file path
  7419. * @param {string} sourceCode - original source
  7420. * @returns {Array} array containing all the text node expressions
  7421. */
  7422. function createTextNodeExpressions(sourceNode, sourceFile, sourceCode) {
  7423. const childrenNodes = getChildrenNodes(sourceNode);
  7424. return childrenNodes
  7425. .filter(isTextNode)
  7426. .filter(hasExpressions)
  7427. .map(node => createExpression(
  7428. node,
  7429. sourceFile,
  7430. sourceCode,
  7431. childrenNodes.indexOf(node),
  7432. sourceNode
  7433. ))
  7434. }
  7435. /**
  7436. * Add a simple binding to a riot parser node
  7437. * @param { RiotParser.Node.Tag } sourceNode - tag containing the if attribute
  7438. * @param { string } selectorAttribute - attribute needed to select the target node
  7439. * @param { string } sourceFile - source file path
  7440. * @param { string } sourceCode - original source
  7441. * @returns { AST.Node } an each binding node
  7442. */
  7443. function createSimpleBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7444. return builders.objectExpression([
  7445. ...createSelectorProperties(selectorAttribute),
  7446. simplePropertyNode(
  7447. BINDING_EXPRESSIONS_KEY,
  7448. builders.arrayExpression([
  7449. ...createTextNodeExpressions(sourceNode, sourceFile, sourceCode),
  7450. ...createAttributeExpressions(sourceNode, sourceFile, sourceCode)
  7451. ])
  7452. )
  7453. ])
  7454. }
  7455. /**
  7456. * Transform a RiotParser.Node.Tag of type slot into a slot binding
  7457. * @param { RiotParser.Node.Tag } sourceNode - slot node
  7458. * @param { string } selectorAttribute - attribute needed to select the target node
  7459. * @param { string } sourceFile - source file path
  7460. * @param { string } sourceCode - original source
  7461. * @returns { AST.Node } a slot binding node
  7462. */
  7463. function createSlotBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7464. const slotNameAttribute = findAttribute(NAME_ATTRIBUTE, sourceNode);
  7465. const slotName = slotNameAttribute ? slotNameAttribute.value : DEFAULT_SLOT_NAME;
  7466. return builders.objectExpression([
  7467. simplePropertyNode(BINDING_TYPE_KEY,
  7468. builders.memberExpression(
  7469. builders.identifier(BINDING_TYPES),
  7470. builders.identifier(SLOT_BINDING_TYPE),
  7471. false
  7472. )
  7473. ),
  7474. simplePropertyNode(
  7475. BINDING_ATTRIBUTES_KEY,
  7476. createBindingAttributes({
  7477. ...sourceNode,
  7478. // filter the name attribute
  7479. attributes: getNodeAttributes(sourceNode)
  7480. .filter(attribute => getName(attribute) !== NAME_ATTRIBUTE)
  7481. },
  7482. selectorAttribute,
  7483. sourceFile,
  7484. sourceCode)
  7485. ),
  7486. simplePropertyNode(
  7487. BINDING_NAME_KEY,
  7488. builders.literal(slotName)
  7489. ),
  7490. ...createSelectorProperties(selectorAttribute)
  7491. ])
  7492. }
  7493. /**
  7494. * Find the slots in the current component and group them under the same id
  7495. * @param {RiotParser.Node.Tag} sourceNode - the custom tag
  7496. * @returns {Object} object containing all the slots grouped by name
  7497. */
  7498. function groupSlots(sourceNode) {
  7499. return getChildrenNodes(sourceNode).reduce((acc, node) => {
  7500. const slotAttribute = findSlotAttribute(node);
  7501. if (slotAttribute) {
  7502. acc[slotAttribute.value] = node;
  7503. } else {
  7504. acc.default = createRootNode({
  7505. nodes: [...getChildrenNodes(acc.default), node]
  7506. });
  7507. }
  7508. return acc
  7509. }, {
  7510. default: null
  7511. })
  7512. }
  7513. /**
  7514. * Create the slot entity to pass to the riot-dom bindings
  7515. * @param {string} id - slot id
  7516. * @param {RiotParser.Node.Tag} sourceNode - slot root node
  7517. * @param {string} sourceFile - source file path
  7518. * @param {string} sourceCode - original source
  7519. * @returns {AST.Node} ast node containing the slot object properties
  7520. */
  7521. function buildSlot(id, sourceNode, sourceFile, sourceCode) {
  7522. const cloneNode = {
  7523. ...sourceNode,
  7524. // avoid to render the slot attribute
  7525. attributes: getNodeAttributes(sourceNode).filter(attribute => attribute.name !== SLOT_ATTRIBUTE)
  7526. };
  7527. const [html, bindings] = build(cloneNode, sourceFile, sourceCode);
  7528. return builders.objectExpression([
  7529. simplePropertyNode(BINDING_ID_KEY, builders.literal(id)),
  7530. simplePropertyNode(BINDING_HTML_KEY, builders.literal(html)),
  7531. simplePropertyNode(BINDING_BINDINGS_KEY, builders.arrayExpression(bindings))
  7532. ])
  7533. }
  7534. /**
  7535. * Create the AST array containing the slots
  7536. * @param { RiotParser.Node.Tag } sourceNode - the custom tag
  7537. * @param { string } sourceFile - source file path
  7538. * @param { string } sourceCode - original source
  7539. * @returns {AST.ArrayExpression} array containing the attributes to bind
  7540. */
  7541. function createSlotsArray(sourceNode, sourceFile, sourceCode) {
  7542. return builders.arrayExpression([
  7543. ...compose(
  7544. slots => slots.map(([key, value]) => buildSlot(key, value, sourceFile, sourceCode)),
  7545. slots => slots.filter(([,value]) => value),
  7546. Object.entries,
  7547. groupSlots
  7548. )(sourceNode)
  7549. ])
  7550. }
  7551. /**
  7552. * Find the slot attribute if it exists
  7553. * @param {RiotParser.Node.Tag} sourceNode - the custom tag
  7554. * @returns {RiotParser.Node.Attr|undefined} the slot attribute found
  7555. */
  7556. function findSlotAttribute(sourceNode) {
  7557. return getNodeAttributes(sourceNode).find(attribute => attribute.name === SLOT_ATTRIBUTE)
  7558. }
  7559. /**
  7560. * Transform a RiotParser.Node.Tag into a tag binding
  7561. * @param { RiotParser.Node.Tag } sourceNode - the custom tag
  7562. * @param { string } selectorAttribute - attribute needed to select the target node
  7563. * @param { string } sourceFile - source file path
  7564. * @param { string } sourceCode - original source
  7565. * @returns { AST.Node } tag binding node
  7566. */
  7567. function createTagBinding(sourceNode, selectorAttribute, sourceFile, sourceCode) {
  7568. return builders.objectExpression([
  7569. simplePropertyNode(BINDING_TYPE_KEY,
  7570. builders.memberExpression(
  7571. builders.identifier(BINDING_TYPES),
  7572. builders.identifier(TAG_BINDING_TYPE),
  7573. false
  7574. )
  7575. ),
  7576. simplePropertyNode(BINDING_GET_COMPONENT_KEY, builders.identifier(GET_COMPONENT_FN)),
  7577. simplePropertyNode(
  7578. BINDING_EVALUATE_KEY,
  7579. toScopedFunction(getCustomNodeNameAsExpression(sourceNode), sourceFile, sourceCode)
  7580. ),
  7581. simplePropertyNode(BINDING_SLOTS_KEY, createSlotsArray(sourceNode, sourceFile, sourceCode)),
  7582. simplePropertyNode(
  7583. BINDING_ATTRIBUTES_KEY,
  7584. createBindingAttributes(sourceNode, selectorAttribute, sourceFile, sourceCode)
  7585. ),
  7586. ...createSelectorProperties(selectorAttribute)
  7587. ])
  7588. }
  7589. const BuildingState = Object.freeze({
  7590. html: [],
  7591. bindings: [],
  7592. parent: null
  7593. });
  7594. /**
  7595. * Nodes having bindings should be cloned and new selector properties should be added to them
  7596. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7597. * @param {string} bindingsSelector - temporary string to identify the current node
  7598. * @returns {RiotParser.Node} the original node parsed having the new binding selector attribute
  7599. */
  7600. function createBindingsTag(sourceNode, bindingsSelector) {
  7601. if (!bindingsSelector) return sourceNode
  7602. return {
  7603. ...sourceNode,
  7604. // inject the selector bindings into the node attributes
  7605. attributes: [{
  7606. name: bindingsSelector,
  7607. value: bindingsSelector
  7608. }, ...getNodeAttributes(sourceNode)]
  7609. }
  7610. }
  7611. /**
  7612. * Create a generic dynamic node (text or tag) and generate its bindings
  7613. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7614. * @param {string} sourceFile - source file path
  7615. * @param {string} sourceCode - original source
  7616. * @param {BuildingState} state - state representing the current building tree state during the recursion
  7617. * @returns {Array} array containing the html output and bindings for the current node
  7618. */
  7619. function createDynamicNode(sourceNode, sourceFile, sourceCode, state) {
  7620. switch (true) {
  7621. case isTextNode(sourceNode):
  7622. // text nodes will not have any bindings
  7623. return [nodeToString(sourceNode), []]
  7624. default:
  7625. return createTagWithBindings(sourceNode, sourceFile, sourceCode)
  7626. }
  7627. }
  7628. /**
  7629. * Create only a dynamic tag node with generating a custom selector and its bindings
  7630. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7631. * @param {string} sourceFile - source file path
  7632. * @param {string} sourceCode - original source
  7633. * @param {BuildingState} state - state representing the current building tree state during the recursion
  7634. * @returns {Array} array containing the html output and bindings for the current node
  7635. */
  7636. function createTagWithBindings(sourceNode, sourceFile, sourceCode) {
  7637. const bindingsSelector = isRootNode(sourceNode) ? null : createBindingSelector();
  7638. const cloneNode = createBindingsTag(sourceNode, bindingsSelector);
  7639. const tagOpeningHTML = nodeToString(cloneNode);
  7640. switch(true) {
  7641. // EACH bindings have prio 1
  7642. case hasEachAttribute(cloneNode):
  7643. return [tagOpeningHTML, [createEachBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
  7644. // IF bindings have prio 2
  7645. case hasIfAttribute(cloneNode):
  7646. return [tagOpeningHTML, [createIfBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
  7647. // TAG bindings have prio 3
  7648. case isCustomNode(cloneNode):
  7649. return [tagOpeningHTML, [createTagBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
  7650. // slot tag
  7651. case isSlotNode(cloneNode):
  7652. return [tagOpeningHTML, [createSlotBinding(cloneNode, bindingsSelector)]]
  7653. // this node has expressions bound to it
  7654. default:
  7655. return [tagOpeningHTML, [createSimpleBinding(cloneNode, bindingsSelector, sourceFile, sourceCode)]]
  7656. }
  7657. }
  7658. /**
  7659. * Parse a node trying to extract its template and bindings
  7660. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7661. * @param {string} sourceFile - source file path
  7662. * @param {string} sourceCode - original source
  7663. * @param {BuildingState} state - state representing the current building tree state during the recursion
  7664. * @returns {Array} array containing the html output and bindings for the current node
  7665. */
  7666. function parseNode(sourceNode, sourceFile, sourceCode, state) {
  7667. // static nodes have no bindings
  7668. if (isStaticNode(sourceNode)) return [nodeToString(sourceNode), []]
  7669. return createDynamicNode(sourceNode, sourceFile, sourceCode)
  7670. }
  7671. /**
  7672. * Create the tag binding
  7673. * @param { RiotParser.Node.Tag } sourceNode - tag containing the each attribute
  7674. * @param { string } sourceFile - source file path
  7675. * @param { string } sourceCode - original source
  7676. * @param { string } selector - binding selector
  7677. * @returns { Array } array with only the tag binding AST
  7678. */
  7679. function createNestedBindings(sourceNode, sourceFile, sourceCode, selector) {
  7680. const mightBeARiotComponent = isCustomNode(sourceNode);
  7681. const node = cloneNodeWithoutSelectorAttribute(sourceNode, selector);
  7682. return mightBeARiotComponent ? [null, [
  7683. createTagBinding(
  7684. node,
  7685. null,
  7686. sourceFile,
  7687. sourceCode
  7688. )]
  7689. ] : build(createRootNode(node), sourceFile, sourceCode)
  7690. }
  7691. /**
  7692. * Build the template and the bindings
  7693. * @param {RiotParser.Node} sourceNode - any kind of node parsed via riot parser
  7694. * @param {string} sourceFile - source file path
  7695. * @param {string} sourceCode - original source
  7696. * @param {BuildingState} state - state representing the current building tree state during the recursion
  7697. * @returns {Array} array containing the html output and the dom bindings
  7698. */
  7699. function build(
  7700. sourceNode,
  7701. sourceFile,
  7702. sourceCode,
  7703. state
  7704. ) {
  7705. if (!sourceNode) panic('Something went wrong with your tag DOM parsing, your tag template can\'t be created');
  7706. const [nodeHTML, nodeBindings] = parseNode(sourceNode, sourceFile, sourceCode);
  7707. const childrenNodes = getChildrenNodes(sourceNode);
  7708. const currentState = { ...cloneDeep(BuildingState), ...state };
  7709. // mutate the original arrays
  7710. currentState.html.push(...nodeHTML);
  7711. currentState.bindings.push(...nodeBindings);
  7712. // do recursion if
  7713. // this tag has children and it has no special directives bound to it
  7714. if (childrenNodes.length && !hasItsOwnTemplate(sourceNode)) {
  7715. childrenNodes.forEach(node => build(node, sourceFile, sourceCode, { parent: sourceNode, ...currentState }));
  7716. }
  7717. // close the tag if it's not a void one
  7718. if (isTagNode(sourceNode) && !isVoidNode(sourceNode)) {
  7719. currentState.html.push(closeTag(sourceNode));
  7720. }
  7721. return [
  7722. currentState.html.join(''),
  7723. currentState.bindings
  7724. ]
  7725. }
  7726. const templateFunctionArguments = [
  7727. TEMPLATE_FN,
  7728. EXPRESSION_TYPES,
  7729. BINDING_TYPES,
  7730. GET_COMPONENT_FN
  7731. ].map(builders.identifier);
  7732. /**
  7733. * Create the content of the template function
  7734. * @param { RiotParser.Node } sourceNode - node generated by the riot compiler
  7735. * @param { string } sourceFile - source file path
  7736. * @param { string } sourceCode - original source
  7737. * @returns {AST.BlockStatement} the content of the template function
  7738. */
  7739. function createTemplateFunctionContent(sourceNode, sourceFile, sourceCode) {
  7740. return builders.blockStatement([
  7741. builders.returnStatement(
  7742. callTemplateFunction(
  7743. ...build(
  7744. createRootNode(sourceNode),
  7745. sourceFile,
  7746. sourceCode
  7747. )
  7748. )
  7749. )
  7750. ])
  7751. }
  7752. /**
  7753. * Extend the AST adding the new template property containing our template call to render the component
  7754. * @param { Object } ast - current output ast
  7755. * @param { string } sourceFile - source file path
  7756. * @param { string } sourceCode - original source
  7757. * @param { RiotParser.Node } sourceNode - node generated by the riot compiler
  7758. * @returns { Object } the output ast having the "template" key
  7759. */
  7760. function extendTemplateProperty(ast, sourceFile, sourceCode, sourceNode) {
  7761. types.visit(ast, {
  7762. visitProperty(path) {
  7763. if (path.value.key.value === TAG_TEMPLATE_PROPERTY) {
  7764. path.value.value = builders.functionExpression(
  7765. null,
  7766. templateFunctionArguments,
  7767. createTemplateFunctionContent(sourceNode, sourceFile, sourceCode)
  7768. );
  7769. return false
  7770. }
  7771. this.traverse(path);
  7772. }
  7773. });
  7774. return ast
  7775. }
  7776. /**
  7777. * Generate the component template logic
  7778. * @param { RiotParser.Node } sourceNode - node generated by the riot compiler
  7779. * @param { string } source - original component source code
  7780. * @param { Object } meta - compilation meta information
  7781. * @param { AST } ast - current AST output
  7782. * @returns { AST } the AST generated
  7783. */
  7784. function template(sourceNode, source, meta, ast) {
  7785. const { options } = meta;
  7786. return extendTemplateProperty(ast, options.file, source, sourceNode)
  7787. }
  7788. const DEFAULT_OPTIONS = {
  7789. template: 'default',
  7790. file: '[unknown-source-file]',
  7791. scopedCss: true
  7792. };
  7793. /**
  7794. * Create the initial AST
  7795. * @param {string} tagName - the name of the component we have compiled
  7796. * @returns { AST } the initial AST
  7797. *
  7798. * @example
  7799. * // the output represents the following string in AST
  7800. */
  7801. function createInitialInput({tagName}) {
  7802. /*
  7803. generates
  7804. export default {
  7805. ${TAG_CSS_PROPERTY}: null,
  7806. ${TAG_LOGIC_PROPERTY}: null,
  7807. ${TAG_TEMPLATE_PROPERTY}: null
  7808. }
  7809. */
  7810. return builders.program([
  7811. builders.exportDefaultDeclaration(
  7812. builders.objectExpression([
  7813. simplePropertyNode(TAG_CSS_PROPERTY, nullNode()),
  7814. simplePropertyNode(TAG_LOGIC_PROPERTY, nullNode()),
  7815. simplePropertyNode(TAG_TEMPLATE_PROPERTY, nullNode()),
  7816. simplePropertyNode(TAG_NAME_PROPERTY, builders.literal(tagName))
  7817. ])
  7818. )]
  7819. )
  7820. }
  7821. /**
  7822. * Make sure the input sourcemap is valid otherwise we ignore it
  7823. * @param {SourceMapGenerator} map - preprocessor source map
  7824. * @returns {Object} sourcemap as json or nothing
  7825. */
  7826. function normaliseInputSourceMap(map) {
  7827. const inputSourceMap = sourcemapAsJSON(map);
  7828. return isEmptySourcemap(inputSourceMap) ? null : inputSourceMap
  7829. }
  7830. /**
  7831. * Override the sourcemap content making sure it will always contain the tag source code
  7832. * @param {Object} map - sourcemap as json
  7833. * @param {string} source - component source code
  7834. * @returns {Object} original source map with the "sourcesContent" property overriden
  7835. */
  7836. function overrideSourcemapContent(map, source) {
  7837. return {
  7838. ...map,
  7839. sourcesContent: [source]
  7840. }
  7841. }
  7842. /**
  7843. * Create the compilation meta object
  7844. * @param { string } source - source code of the tag we will need to compile
  7845. * @param { string } options - compiling options
  7846. * @returns {Object} meta object
  7847. */
  7848. function createMeta(source, options) {
  7849. return {
  7850. tagName: null,
  7851. fragments: null,
  7852. options: {
  7853. ...DEFAULT_OPTIONS,
  7854. ...options
  7855. },
  7856. source
  7857. }
  7858. }
  7859. /**
  7860. * Generate the output code source together with the sourcemap
  7861. * @param { string } source - source code of the tag we will need to compile
  7862. * @param { Object } opts - compiling options
  7863. * @returns { Output } object containing output code and source map
  7864. */
  7865. function compile(source, opts = {}) {
  7866. const meta = createMeta(source, opts);
  7867. const {options} = meta;
  7868. const { code, map } = execute$1('template', options.template, meta, source);
  7869. const { template: template$1, css: css$1, javascript: javascript$1 } = parser(options).parse(code).output;
  7870. // extend the meta object with the result of the parsing
  7871. Object.assign(meta, {
  7872. tagName: template$1.name,
  7873. fragments: { template: template$1, css: css$1, javascript: javascript$1 }
  7874. });
  7875. return compose(
  7876. result => ({ ...result, meta }),
  7877. result => execute(result, meta),
  7878. result => ({
  7879. ...result,
  7880. map: overrideSourcemapContent(result.map, source)
  7881. }),
  7882. ast => meta.ast = ast && generateJavascript(ast, {
  7883. sourceMapName: `${options.file}.map`,
  7884. inputSourceMap: normaliseInputSourceMap(map)
  7885. }),
  7886. hookGenerator(template, template$1, code, meta),
  7887. hookGenerator(javascript, javascript$1, code, meta),
  7888. hookGenerator(css, css$1, code, meta)
  7889. )(createInitialInput(meta))
  7890. }
  7891. /**
  7892. * Prepare the riot parser node transformers
  7893. * @param { Function } transformer - transformer function
  7894. * @param { Object } sourceNode - riot parser node
  7895. * @param { string } source - component source code
  7896. * @param { Object } meta - compilation meta information
  7897. * @returns { Promise<Output> } object containing output code and source map
  7898. */
  7899. function hookGenerator(transformer, sourceNode, source, meta) {
  7900. if (
  7901. // filter missing nodes
  7902. !sourceNode ||
  7903. // filter nodes without children
  7904. (sourceNode.nodes && !sourceNode.nodes.length) ||
  7905. // filter empty javascript and css nodes
  7906. (!sourceNode.nodes && !sourceNode.text)) {
  7907. return result => result
  7908. }
  7909. return curry(transformer)(sourceNode, source, meta)
  7910. }
  7911. // This function can be used to register new preprocessors
  7912. // a preprocessor can target either only the css or javascript nodes
  7913. // or the complete tag source file ('template')
  7914. const registerPreprocessor = register$1;
  7915. // This function can allow you to register postprocessors that will parse the output code
  7916. // here we can run prettifiers, eslint fixes...
  7917. const registerPostprocessor = register;
  7918. exports.compile = compile;
  7919. exports.createInitialInput = createInitialInput;
  7920. exports.registerPostprocessor = registerPostprocessor;
  7921. exports.registerPreprocessor = registerPreprocessor;