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.

8932 lines
292 KiB

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