1"use strict"; 2(self["webpackChunkclean_ui"] = self["webpackChunkclean_ui"] || []).push([["polyfills"],{ 3 4/***/ 24050: 5/*!**************************!*\ 6 !*** ./src/polyfills.ts ***! 7 \**************************/ 8/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 9 10__webpack_require__.r(__webpack_exports__); 11/* harmony import */ var _angular_localize_init__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/localize/init */ 1643); 12/* harmony import */ var zone_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! zone.js */ 74124); 13/* harmony import */ var zone_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(zone_js__WEBPACK_IMPORTED_MODULE_1__); 14/*************************************************************************************************** 15 * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. 16 */ 17 18/** 19 * This file includes polyfills needed by Angular and is loaded before the app. 20 * You can add your own extra polyfills to this file. 21 * 22 * This file is divided into 2 sections: 23 * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 24 * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 25 * file. 26 * 27 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 28 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 29 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 30 * 31 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 32 */ 33/*************************************************************************************************** 34 * BROWSER POLYFILLS 35 */ 36/** IE9, IE10 and IE11 requires all of the following polyfills. **/ 37// import 'core-js/es6/symbol'; 38// import 'core-js/es6/object'; 39// import 'core-js/es6/function'; 40// import 'core-js/es6/parse-int'; 41// import 'core-js/es6/parse-float'; 42// import 'core-js/es6/number'; 43// import 'core-js/es6/math'; 44// import 'core-js/es6/string'; 45// import 'core-js/es6/date'; 46// import 'core-js/es6/array'; 47// import 'core-js/es6/regexp'; 48// import 'core-js/es6/map'; 49// import 'core-js/es6/set'; 50/** Evergreen browsers require these. **/ 51window.global = window; 52/*************************************************************************************************** 53 * Zone JS is required by Angular itself. 54 */ 55 // Included with Angular CLI. 56/*************************************************************************************************** 57 * APPLICATION IMPORTS 58 */ 59/** 60 * Date, currency, decimal and percent pipes. 61 * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 62 */ 63// import 'intl'; // Run `npm install --save intl`. 64 65/***/ }), 66 67/***/ 74124: 68/*!***********************************************!*\ 69 !*** ./node_modules/zone.js/fesm2015/zone.js ***! 70 \***********************************************/ 71/***/ (() => { 72 73 74 75/** 76 * @license Angular v<unknown> 77 * (c) 2010-2024 Google LLC. https://angular.io/ 78 * License: MIT 79 */ 80const global = globalThis; 81// __Zone_symbol_prefix global can be used to override the default zone 82// symbol prefix with a custom one if needed. 83function __symbol__(name) { 84 const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__'; 85 return symbolPrefix + name; 86} 87function initZone() { 88 const performance = global['performance']; 89 function mark(name) { 90 performance && performance['mark'] && performance['mark'](name); 91 } 92 function performanceMeasure(name, label) { 93 performance && performance['measure'] && performance['measure'](name, label); 94 } 95 mark('Zone'); 96 class ZoneImpl { 97 // tslint:disable-next-line:require-internal-with-underscore 98 static #_ = this.__symbol__ = __symbol__; 99 static assertZonePatched() { 100 if (global['Promise'] !== patches['ZoneAwarePromise']) { 101 throw new Error('Zone.js has detected that ZoneAwarePromise `(window|global).Promise` ' + 'has been overwritten.\n' + 'Most likely cause is that a Promise polyfill has been loaded ' + 'after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. ' + 'If you must load one, do so before loading zone.js.)'); 102 } 103 } 104 static get root() { 105 let zone = ZoneImpl.current; 106 while (zone.parent) { 107 zone = zone.parent; 108 } 109 return zone; 110 } 111 static get current() { 112 return _currentZoneFrame.zone; 113 } 114 static get currentTask() { 115 return _currentTask; 116 } 117 // tslint:disable-next-line:require-internal-with-underscore 118 static __load_patch(name, fn, ignoreDuplicate = false) { 119 if (patches.hasOwnProperty(name)) { 120 // `checkDuplicate` option is defined from global variable 121 // so it works for all modules. 122 // `ignoreDuplicate` can work for the specified module 123 const checkDuplicate = global[__symbol__('forceDuplicateZoneCheck')] === true; 124 if (!ignoreDuplicate && checkDuplicate) {
125 throw Error('Already loaded patch: ' + name); 126 } 127 } else if (!global['__Zone_disable_' + name]) { 128 const perfName = 'Zone:' + name; 129 mark(perfName); 130 patches[name] = fn(global, ZoneImpl, _api); 131 performanceMeasure(perfName, perfName); 132 } 133 } 134 get parent() { 135 return this._parent; 136 } 137 get name() { 138 return this._name; 139 } 140 constructor(parent, zoneSpec) { 141 this._parent = parent; 142 this._name = zoneSpec ? zoneSpec.name || 'unnamed' : '<root>'; 143 this._properties = zoneSpec && zoneSpec.properties || {}; 144 this._zoneDelegate = new _ZoneDelegate(this, this._parent && this._parent._zoneDelegate, zoneSpec); 145 } 146 get(key) { 147 const zone = this.getZoneWith(key); 148 if (zone) return zone._properties[key]; 149 } 150 getZoneWith(key) { 151 let current = this; 152 while (current) { 153 if (current._properties.hasOwnProperty(key)) { 154 return current; 155 } 156 current = current._parent; 157 } 158 return null; 159 } 160 fork(zoneSpec) { 161 if (!zoneSpec) throw new Error('ZoneSpec required!'); 162 return this._zoneDelegate.fork(this, zoneSpec); 163 } 164 wrap(callback, source) { 165 if (typeof callback !== 'function') { 166 throw new Error('Expecting function got: ' + callback); 167 } 168 const _callback = this._zoneDelegate.intercept(this, callback, source); 169 const zone = this; 170 return function () { 171 return zone.runGuarded(_callback, this, arguments, source); 172 }; 173 } 174 run(callback, applyThis, applyArgs, source) { 175 _currentZoneFrame = { 176 parent: _currentZoneFrame, 177 zone: this 178 }; 179 try { 180 return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); 181 } finally { 182 _currentZoneFrame = _currentZoneFrame.parent; 183 } 184 } 185 runGuarded(callback, applyThis = null, applyArgs, source) { 186 _currentZoneFrame = { 187 parent: _currentZoneFrame, 188 zone: this 189 }; 190 try { 191 try { 192 return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); 193 } catch (error) { 194 if (this._zoneDelegate.handleError(this, error)) { 195 throw error; 196 } 197 } 198 } finally { 199 _currentZoneFrame = _currentZoneFrame.parent; 200 } 201 } 202 runTask(task, applyThis, applyArgs) { 203 if (task.zone != this) { 204 throw new Error('A task can only be run in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); 205 } 206 // https://github.com/angular/zone.js/issues/778, sometimes eventTask 207 // will run in notScheduled(canceled) state, we should not try to 208 // run such kind of task but just return 209 if (task.state === notScheduled && (task.type === eventTask || task.type === macroTask)) { 210 return; 211 } 212 const reEntryGuard = task.state != running; 213 reEntryGuard && task._transitionTo(running, scheduled); 214 task.runCount++; 215 const previousTask = _currentTask; 216 _currentTask = task; 217 _currentZoneFrame = { 218 parent: _currentZoneFrame, 219 zone: this 220 }; 221 try { 222 if (task.type == macroTask && task.data && !task.data.isPeriodic) { 223 task.cancelFn = undefined; 224 } 225 try { 226 return this._zoneDelegate.invokeTask(this, task, applyThis, applyArgs); 227 } catch (error) { 228 if (this._zoneDelegate.handleError(this, error)) { 229 throw error; 230 } 231 } 232 } finally { 233 // if the task's state is notScheduled or unknown, then it has already been cancelled 234 // we should not reset the state to scheduled 235 if (task.state !== notScheduled && task.state !== unknown) { 236 if (task.type == eventTask || task.data && task.data.isPeriodic) { 237 reEntryGuard && task._transitionTo(scheduled, running); 238 } else { 239 task.runCount = 0; 240 this._updateTaskCount(task, -1); 241 reEntryGuard && task._transitionTo(notScheduled, running, notScheduled); 242 } 243 } 244 _currentZoneFrame = _currentZoneFrame.parent; 245 _currentTask = previousTask; 246 } 247 } 248 scheduleTask(task) { 249 if (task.zone && task.zone !== this) { 250 // check if the task was rescheduled, the newZone 251 // should not be the children of the original zone 252 let newZone = this; 253 while (newZone) { 254 if (newZone === task.zone) { 255 throw Error(`can not reschedule task to ${this.name} which is descendants of the original zone ${task.zone.name}`); 256 } 257 newZone = newZone.parent; 258 } 259 } 260 task._transitionTo(scheduling, notScheduled); 261 const zoneDelegates = []; 262 task._zoneDelegates = zoneDelegates; 263 task._zone = this;
vendor: 16,542 bytes, lines 264-662
264 try { 265 task = this._zoneDelegate.scheduleTask(this, task); 266 } catch (err) { 267 // should set task's state to unknown when scheduleTask throw error 268 // because the err may from reschedule, so the fromState maybe notScheduled 269 task._transitionTo(unknown, scheduling, notScheduled); 270 // TODO: @JiaLiPassion, should we check the result from handleError? 271 this._zoneDelegate.handleError(this, err); 272 throw err; 273 } 274 if (task._zoneDelegates === zoneDelegates) { 275 // we have to check because internally the delegate can reschedule the task. 276 this._updateTaskCount(task, 1); 277 } 278 if (task.state == scheduling) { 279 task._transitionTo(scheduled, scheduling); 280 } 281 return task; 282 } 283 scheduleMicroTask(source, callback, data, customSchedule) { 284 return this.scheduleTask(new ZoneTask(microTask, source, callback, data, customSchedule, undefined)); 285 } 286 scheduleMacroTask(source, callback, data, customSchedule, customCancel) { 287 return this.scheduleTask(new ZoneTask(macroTask, source, callback, data, customSchedule, customCancel)); 288 } 289 scheduleEventTask(source, callback, data, customSchedule, customCancel) { 290 return this.scheduleTask(new ZoneTask(eventTask, source, callback, data, customSchedule, customCancel)); 291 } 292 cancelTask(task) { 293 if (task.zone != this) throw new Error('A task can only be cancelled in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); 294 if (task.state !== scheduled && task.state !== running) { 295 return; 296 } 297 task._transitionTo(canceling, scheduled, running); 298 try { 299 this._zoneDelegate.cancelTask(this, task); 300 } catch (err) { 301 // if error occurs when cancelTask, transit the state to unknown 302 task._transitionTo(unknown, canceling); 303 this._zoneDelegate.handleError(this, err); 304 throw err; 305 } 306 this._updateTaskCount(task, -1); 307 task._transitionTo(notScheduled, canceling); 308 task.runCount = 0; 309 return task; 310 } 311 _updateTaskCount(task, count) { 312 const zoneDelegates = task._zoneDelegates; 313 if (count == -1) { 314 task._zoneDelegates = null; 315 } 316 for (let i = 0; i < zoneDelegates.length; i++) { 317 zoneDelegates[i]._updateTaskCount(task.type, count); 318 } 319 } 320 } 321 const DELEGATE_ZS = { 322 name: '', 323 onHasTask: (delegate, _, target, hasTaskState) => delegate.hasTask(target, hasTaskState), 324 onScheduleTask: (delegate, _, target, task) => delegate.scheduleTask(target, task), 325 onInvokeTask: (delegate, _, target, task, applyThis, applyArgs) => delegate.invokeTask(target, task, applyThis, applyArgs), 326 onCancelTask: (delegate, _, target, task) => delegate.cancelTask(target, task) 327 }; 328 class _ZoneDelegate { 329 get zone() { 330 return this._zone; 331 } 332 constructor(zone, parentDelegate, zoneSpec) { 333 this._taskCounts = { 334 'microTask': 0, 335 'macroTask': 0, 336 'eventTask': 0 337 }; 338 this._zone = zone; 339 this._parentDelegate = parentDelegate; 340 this._forkZS = zoneSpec && (zoneSpec && zoneSpec.onFork ? zoneSpec : parentDelegate._forkZS); 341 this._forkDlgt = zoneSpec && (zoneSpec.onFork ? parentDelegate : parentDelegate._forkDlgt); 342 this._forkCurrZone = zoneSpec && (zoneSpec.onFork ? this._zone : parentDelegate._forkCurrZone); 343 this._interceptZS = zoneSpec && (zoneSpec.onIntercept ? zoneSpec : parentDelegate._interceptZS); 344 this._interceptDlgt = zoneSpec && (zoneSpec.onIntercept ? parentDelegate : parentDelegate._interceptDlgt); 345 this._interceptCurrZone = zoneSpec && (zoneSpec.onIntercept ? this._zone : parentDelegate._interceptCurrZone); 346 this._invokeZS = zoneSpec && (zoneSpec.onInvoke ? zoneSpec : parentDelegate._invokeZS); 347 this._invokeDlgt = zoneSpec && (zoneSpec.onInvoke ? parentDelegate : parentDelegate._invokeDlgt); 348 this._invokeCurrZone = zoneSpec && (zoneSpec.onInvoke ? this._zone : parentDelegate._invokeCurrZone); 349 this._handleErrorZS = zoneSpec && (zoneSpec.onHandleError ? zoneSpec : parentDelegate._handleErrorZS); 350 this._handleErrorDlgt = zoneSpec && (zoneSpec.onHandleError ? parentDelegate : parentDelegate._handleErrorDlgt); 351 this._handleErrorCurrZone = zoneSpec && (zoneSpec.onHandleError ? this._zone : parentDelegate._handleErrorCurrZone); 352 this._scheduleTaskZS = zoneSpec && (zoneSpec.onScheduleTask ? zoneSpec : parentDelegate._scheduleTaskZS); 353 this._scheduleTaskDlgt = zoneSpec && (zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt); 354 this._scheduleTaskCurrZone = zoneSpec && (zoneSpec.onScheduleTask ? this._zone : parentDelegate._scheduleTaskCurrZone); 355 this._invokeTaskZS = zoneSpec && (zoneSpec.onInvokeTask ? zoneSpec : parentDelegate._invokeTaskZS); 356 this._invokeTaskDlgt = zoneSpec && (zoneSpec.onInvokeTask ? parentDelegate : parentDelegate._invokeTaskDlgt); 357 this._invokeTaskCurrZone = zoneSpec && (zoneSpec.onInvokeTask ? this._zone : parentDelegate._invokeTaskCurrZone); 358 this._cancelTaskZS = zoneSpec && (zoneSpec.onCancelTask ? zoneSpec : parentDelegate._cancelTaskZS); 359 this._cancelTaskDlgt = zoneSpec && (zoneSpec.onCancelTask ? parentDelegate : parentDelegate._cancelTaskDlgt); 360 this._cancelTaskCurrZone = zoneSpec && (zoneSpec.onCancelTask ? this._zone : parentDelegate._cancelTaskCurrZone); 361 this._hasTaskZS = null; 362 this._hasTaskDlgt = null; 363 this._hasTaskDlgtOwner = null; 364 this._hasTaskCurrZone = null; 365 const zoneSpecHasTask = zoneSpec && zoneSpec.onHasTask; 366 const parentHasTask = parentDelegate && parentDelegate._hasTaskZS; 367 if (zoneSpecHasTask || parentHasTask) { 368 // If we need to report hasTask, than this ZS needs to do ref counting on tasks. In such 369 // a case all task related interceptors must go through this ZD. We can't short circuit it. 370 this._hasTaskZS = zoneSpecHasTask ? zoneSpec : DELEGATE_ZS; 371 this._hasTaskDlgt = parentDelegate; 372 this._hasTaskDlgtOwner = this; 373 this._hasTaskCurrZone = this._zone; 374 if (!zoneSpec.onScheduleTask) { 375 this._scheduleTaskZS = DELEGATE_ZS; 376 this._scheduleTaskDlgt = parentDelegate; 377 this._scheduleTaskCurrZone = this._zone; 378 } 379 if (!zoneSpec.onInvokeTask) { 380 this._invokeTaskZS = DELEGATE_ZS; 381 this._invokeTaskDlgt = parentDelegate; 382 this._invokeTaskCurrZone = this._zone; 383 } 384 if (!zoneSpec.onCancelTask) { 385 this._cancelTaskZS = DELEGATE_ZS; 386 this._cancelTaskDlgt = parentDelegate; 387 this._cancelTaskCurrZone = this._zone; 388 } 389 } 390 } 391 fork(targetZone, zoneSpec) { 392 return this._forkZS ? this._forkZS.onFork(this._forkDlgt, this.zone, targetZone, zoneSpec) : new ZoneImpl(targetZone, zoneSpec); 393 } 394 intercept(targetZone, callback, source) { 395 return this._interceptZS ? this._interceptZS.onIntercept(this._interceptDlgt, this._interceptCurrZone, targetZone, callback, source) : callback; 396 } 397 invoke(targetZone, callback, applyThis, applyArgs, source) { 398 return this._invokeZS ? this._invokeZS.onInvoke(this._invokeDlgt, this._invokeCurrZone, targetZone, callback, applyThis, applyArgs, source) : callback.apply(applyThis, applyArgs); 399 } 400 handleError(targetZone, error) { 401 return this._handleErrorZS ? this._handleErrorZS.onHandleError(this._handleErrorDlgt, this._handleErrorCurrZone, targetZone, error) : true; 402 } 403 scheduleTask(targetZone, task) { 404 let returnTask = task; 405 if (this._scheduleTaskZS) { 406 if (this._hasTaskZS) { 407 returnTask._zoneDelegates.push(this._hasTaskDlgtOwner); 408 } 409 returnTask = this._scheduleTaskZS.onScheduleTask(this._scheduleTaskDlgt, this._scheduleTaskCurrZone, targetZone, task); 410 if (!returnTask) returnTask = task; 411 } else { 412 if (task.scheduleFn) { 413 task.scheduleFn(task); 414 } else if (task.type == microTask) { 415 scheduleMicroTask(task); 416 } else { 417 throw new Error('Task is missing scheduleFn.'); 418 } 419 } 420 return returnTask; 421 } 422 invokeTask(targetZone, task, applyThis, applyArgs) { 423 return this._invokeTaskZS ? this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt, this._invokeTaskCurrZone, targetZone, task, applyThis, applyArgs) : task.callback.apply(applyThis, applyArgs); 424 } 425 cancelTask(targetZone, task) { 426 let value; 427 if (this._cancelTaskZS) { 428 value = this._cancelTaskZS.onCancelTask(this._cancelTaskDlgt, this._cancelTaskCurrZone, targetZone, task); 429 } else { 430 if (!task.cancelFn) { 431 throw Error('Task is not cancelable'); 432 } 433 value = task.cancelFn(task); 434 } 435 return value; 436 } 437 hasTask(targetZone, isEmpty) { 438 // hasTask should not throw error so other ZoneDelegate 439 // can still trigger hasTask callback 440 try { 441 this._hasTaskZS && this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty); 442 } catch (err) { 443 this.handleError(targetZone, err); 444 } 445 } 446 // tslint:disable-next-line:require-internal-with-underscore 447 _updateTaskCount(type, count) { 448 const counts = this._taskCounts; 449 const prev = counts[type]; 450 const next = counts[type] = prev + count; 451 if (next < 0) { 452 throw new Error('More tasks executed then were scheduled.'); 453 } 454 if (prev == 0 || next == 0) { 455 const isEmpty = { 456 microTask: counts['microTask'] > 0, 457 macroTask: counts['macroTask'] > 0, 458 eventTask: counts['eventTask'] > 0, 459 change: type 460 }; 461 this.hasTask(this._zone, isEmpty); 462 } 463 } 464 } 465 class ZoneTask { 466 constructor(type, source, callback, options, scheduleFn, cancelFn) { 467 // tslint:disable-next-line:require-internal-with-underscore 468 this._zone = null; 469 this.runCount = 0; 470 // tslint:disable-next-line:require-internal-with-underscore 471 this._zoneDelegates = null; 472 // tslint:disable-next-line:require-internal-with-underscore 473 this._state = 'notScheduled'; 474 this.type = type; 475 this.source = source; 476 this.data = options; 477 this.scheduleFn = scheduleFn; 478 this.cancelFn = cancelFn; 479 if (!callback) { 480 throw new Error('callback is not defined'); 481 } 482 this.callback = callback; 483 const self = this; 484 // TODO: @JiaLiPassion options should have interface 485 if (type === eventTask && options && options.useG) { 486 this.invoke = ZoneTask.invokeTask; 487 } else { 488 this.invoke = function () { 489 return ZoneTask.invokeTask.call(global, self, this, arguments); 490 }; 491 } 492 } 493 static invokeTask(task, target, args) { 494 if (!task) { 495 task = this; 496 } 497 _numberOfNestedTaskFrames++; 498 try { 499 task.runCount++; 500 return task.zone.runTask(task, target, args); 501 } finally { 502 if (_numberOfNestedTaskFrames == 1) { 503 drainMicroTaskQueue(); 504 } 505 _numberOfNestedTaskFrames--; 506 } 507 } 508 get zone() { 509 return this._zone; 510 } 511 get state() { 512 return this._state; 513 } 514 cancelScheduleRequest() { 515 this._transitionTo(notScheduled, scheduling); 516 } 517 // tslint:disable-next-line:require-internal-with-underscore 518 _transitionTo(toState, fromState1, fromState2) { 519 if (this._state === fromState1 || this._state === fromState2) { 520 this._state = toState; 521 if (toState == notScheduled) { 522 this._zoneDelegates = null; 523 } 524 } else { 525 throw new Error(`${this.type} '${this.source}': can not transition to '${toState}', expecting state '${fromState1}'${fromState2 ? " or '" + fromState2 + "'" : ''}, was '${this._state}'.`); 526 } 527 } 528 toString() { 529 if (this.data && typeof this.data.handleId !== 'undefined') { 530 return this.data.handleId.toString(); 531 } else { 532 return Object.prototype.toString.call(this); 533 } 534 } 535 // add toJSON method to prevent cyclic error when 536 // call JSON.stringify(zoneTask) 537 toJSON() { 538 return { 539 type: this.type, 540 state: this.state, 541 source: this.source, 542 zone: this.zone.name, 543 runCount: this.runCount 544 }; 545 } 546 } 547 ////////////////////////////////////////////////////// 548 ////////////////////////////////////////////////////// 549 /// MICROTASK QUEUE 550 ////////////////////////////////////////////////////// 551 ////////////////////////////////////////////////////// 552 const symbolSetTimeout = __symbol__('setTimeout'); 553 const symbolPromise = __symbol__('Promise'); 554 const symbolThen = __symbol__('then'); 555 let _microTaskQueue = []; 556 let _isDrainingMicrotaskQueue = false; 557 let nativeMicroTaskQueuePromise; 558 function nativeScheduleMicroTask(func) { 559 if (!nativeMicroTaskQueuePromise) { 560 if (global[symbolPromise]) { 561 nativeMicroTaskQueuePromise = global[symbolPromise].resolve(0); 562 } 563 } 564 if (nativeMicroTaskQueuePromise) { 565 let nativeThen = nativeMicroTaskQueuePromise[symbolThen]; 566 if (!nativeThen) { 567 // native Promise is not patchable, we need to use `then` directly 568 // issue 1078 569 nativeThen = nativeMicroTaskQueuePromise['then']; 570 } 571 nativeThen.call(nativeMicroTaskQueuePromise, func); 572 } else { 573 global[symbolSetTimeout](func, 0); 574 } 575 } 576 function scheduleMicroTask(task) { 577 // if we are not running in any task, and there has not been anything scheduled 578 // we must bootstrap the initial task creation by manually scheduling the drain 579 if (_numberOfNestedTaskFrames === 0 && _microTaskQueue.length === 0) { 580 // We are not running in Task, so we need to kickstart the microtask queue. 581 nativeScheduleMicroTask(drainMicroTaskQueue); 582 } 583 task && _microTaskQueue.push(task); 584 } 585 function drainMicroTaskQueue() { 586 if (!_isDrainingMicrotaskQueue) { 587 _isDrainingMicrotaskQueue = true; 588 while (_microTaskQueue.length) { 589 const queue = _microTaskQueue; 590 _microTaskQueue = []; 591 for (let i = 0; i < queue.length; i++) { 592 const task = queue[i]; 593 try { 594 task.zone.runTask(task, null, null); 595 } catch (error) { 596 _api.onUnhandledError(error); 597 } 598 } 599 } 600 _api.microtaskDrainDone(); 601 _isDrainingMicrotaskQueue = false; 602 } 603 } 604 ////////////////////////////////////////////////////// 605 ////////////////////////////////////////////////////// 606 /// BOOTSTRAP 607 ////////////////////////////////////////////////////// 608 ////////////////////////////////////////////////////// 609 const NO_ZONE = { 610 name: 'NO ZONE' 611 }; 612 const notScheduled = 'notScheduled', 613 scheduling = 'scheduling', 614 scheduled = 'scheduled', 615 running = 'running', 616 canceling = 'canceling', 617 unknown = 'unknown'; 618 const microTask = 'microTask', 619 macroTask = 'macroTask', 620 eventTask = 'eventTask'; 621 const patches = {}; 622 const _api = { 623 symbol: __symbol__, 624 currentZoneFrame: () => _currentZoneFrame, 625 onUnhandledError: noop, 626 microtaskDrainDone: noop, 627 scheduleMicroTask: scheduleMicroTask, 628 showUncaughtError: () => !ZoneImpl[__symbol__('ignoreConsoleErrorUncaughtError')], 629 patchEventTarget: () => [], 630 patchOnProperties: noop, 631 patchMethod: () => noop, 632 bindArguments: () => [], 633 patchThen: () => noop, 634 patchMacroTask: () => noop, 635 patchEventPrototype: () => noop, 636 isIEOrEdge: () => false, 637 getGlobalObjects: () => undefined, 638 ObjectDefineProperty: () => noop, 639 ObjectGetOwnPropertyDescriptor: () => undefined, 640 ObjectCreate: () => undefined, 641 ArraySlice: () => [], 642 patchClass: () => noop, 643 wrapWithCurrentZone: () => noop, 644 filterProperties: () => [], 645 attachOriginToPatched: () => noop, 646 _redefineProperty: () => noop, 647 patchCallbacks: () => noop, 648 nativeScheduleMicroTask: nativeScheduleMicroTask 649 }; 650 let _currentZoneFrame = { 651 parent: null, 652 zone: new ZoneImpl(null, null) 653 }; 654 let _currentTask = null; 655 let _numberOfNestedTaskFrames = 0; 656 function noop() {} 657 performanceMeasure('Zone', 'Zone'); 658 return ZoneImpl; 659} 660function loadZone() { 661 // if global['Zone'] already exists (maybe zone.js was already loaded or 662 // some other lib also registered a global object named Zone), we may
662need 663 // to throw an error, but sometimes user may not want this error. 664 // For example, 665 // we have two web pages, page1 includes zone.js, page2 doesn't. 666 // and the 1st time user load page1 and page2, everything work fine, 667 // but when user load page2 again, error occurs because global['Zone'] already exists. 668 // so we add a flag to let user choose whether to throw this error or not. 669 // By default, if existing Zone is from zone.js, we will not throw the error. 670 const global = globalThis; 671 const checkDuplicate = global[__symbol__('forceDuplicateZoneCheck')] === true; 672 if (global['Zone'] && (checkDuplicate || typeof global['Zone'].__symbol__ !== 'function')) { 673 throw new Error('Zone already loaded.'); 674 } 675 // Initialize global `Zone` constant. 676 global['Zone'] ??= initZone(); 677 return global['Zone']; 678} 679 680/** 681 * Suppress closure compiler errors about unknown 'Zone' variable 682 * @fileoverview 683 * @suppress {undefinedVars,globalThis,missingRequire} 684 */ 685// issue #989, to reduce bundle size, use short name 686/** Object.getOwnPropertyDescriptor */ 687const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 688/** Object.defineProperty */ 689const ObjectDefineProperty = Object.defineProperty; 690/** Object.getPrototypeOf */ 691const ObjectGetPrototypeOf = Object.getPrototypeOf; 692/** Object.create */ 693const ObjectCreate = Object.create; 694/** Array.prototype.slice */ 695const ArraySlice = Array.prototype.slice; 696/** addEventListener string const */ 697const ADD_EVENT_LISTENER_STR = 'addEventListener'; 698/** removeEventListener string const */ 699const REMOVE_EVENT_LISTENER_STR = 'removeEventListener'; 700/** zoneSymbol addEventListener */ 701const ZONE_SYMBOL_ADD_EVENT_LISTENER = __symbol__(ADD_EVENT_LISTENER_STR); 702/** zoneSymbol removeEventListener */
703const ZONE_SYMBOL_REMOVE_EVENT_LISTENER = __symbol__(REMOVE_EVENT_LISTENER_STR); 704/** true string const */ 705const TRUE_STR = 'true'; 706/** false string const */ 707const FALSE_STR = 'false'; 708/** Zone symbol prefix string const. */ 709const ZONE_SYMBOL_PREFIX = __symbol__(''); 710function wrapWithCurrentZone(callback, source) { 711 return Zone.current.wrap(callback, source); 712} 713function scheduleMacroTaskWithCurrentZone(source, callback, data, customSchedule, customCancel) { 714 return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel); 715} 716const zoneSymbol = __symbol__; 717const isWindowExists = typeof window !== 'undefined'; 718const internalWindow = isWindowExists ? window : undefined; 719const _global = isWindowExists && internalWindow || globalThis; 720const REMOVE_ATTRIBUTE = 'removeAttribute'; 721function bindArguments(args, source) { 722 for (let i = args.length - 1; i >= 0; i--) { 723 if (typeof args[i] === 'function') { 724 args[i] = wrapWithCurrentZone(args[i], source + '_' + i); 725 } 726 } 727 return args; 728} 729function patchPrototype(prototype, fnNames) { 730 const source = prototype.constructor['name']; 731 for (let i = 0; i < fnNames.length; i++) { 732 const name = fnNames[i]; 733 const delegate = prototype[name]; 734 if (delegate) { 735 const prototypeDesc = ObjectGetOwnPropertyDescriptor(prototype, name); 736 if (!isPropertyWritable(prototypeDesc)) { 737 continue; 738 } 739 prototype[name] = (delegate => { 740 const patched = function () { 741 return delegate.apply(this, bindArguments(arguments, source + '.' + name)); 742 }; 743 attachOriginToPatched(patched, delegate); 744 return patched; 745 })(delegate); 746 } 747 } 748} 749function isPropertyWritable(propertyDesc) { 750 if (!propertyDesc) { 751 return true; 752 } 753 if (propertyDesc.writable === false) { 754 return false; 755 } 756 return !(typeof propertyDesc.get === 'function' && typeof propertyDesc.set === 'undefined'); 757} 758const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; 759// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify 760// this code. 761const isNode = !('nw' in _global) && typeof _global.process !== 'undefined' && _global.process.toString() === '[object process]'; 762const isBrowser = !isNode && !isWebWorker && !!(isWindowExists && internalWindow['HTMLElement']); 763// we are in electron of nw, so we are both browser and nodejs 764// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify 765// this code. 766const isMix = typeof _global.process !== 'undefined' && _global.process.toString() === '[object process]' && !isWebWorker && !!(isWindowExists && internalWindow['HTMLElement']); 767const zoneSymbolEventNames$1 = {}; 768const wrapFn = function (event) { 769 // https://github.com/angular/zone.js/issues/911, in IE, sometimes 770 // event will be undefined, so we need to use window.event 771 event = event || _global.event; 772 if (!event) { 773 return; 774 } 775 let eventNameSymbol = zoneSymbolEventNames$1[event.type]; 776 if (!eventNameSymbol) { 777 eventNameSymbol = zoneSymbolEventNames$1[event.type] = zoneSymbol('ON_PROPERTY' + event.type); 778 } 779 const target = this || event.target || _global; 780 const listener = target[eventNameSymbol]; 781 let result; 782 if (isBrowser && target === internalWindow && event.type === 'error') { 783 // window.onerror have different signature 784 // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror 785 // and onerror callback will prevent default when callback return true 786 const errorEvent = event; 787 result = listener && listener.call(this, errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error); 788 if (result === true) { 789 event.preventDefault(); 790 } 791 } else { 792 result = listener && listener.apply(this, arguments); 793 if (result != undefined && !result) { 794 event.preventDefault(); 795 } 796 } 797 return result; 798}; 799function patchProperty(obj, prop, prototype) { 800 let desc = ObjectGetOwnPropertyDescriptor(obj, prop); 801 if (!desc && prototype) { 802 // when patch window object, use prototype to check prop exist or not
vendor: 36,293 bytes, lines 803-1702
803 const prototypeDesc = ObjectGetOwnPropertyDescriptor(prototype, prop); 804 if (prototypeDesc) { 805 desc = { 806 enumerable: true, 807 configurable: true 808 }; 809 } 810 } 811 // if the descriptor not exists or is not configurable 812 // just return 813 if (!desc || !desc.configurable) { 814 return; 815 } 816 const onPropPatchedSymbol = zoneSymbol('on' + prop + 'patched'); 817 if (obj.hasOwnProperty(onPropPatchedSymbol) && obj[onPropPatchedSymbol]) { 818 return; 819 } 820 // A property descriptor cannot have getter/setter and be writable 821 // deleting the writable and value properties avoids this error: 822 // 823 // TypeError: property descriptors must not specify a value or be writable when a 824 // getter or setter has been specified 825 delete desc.writable; 826 delete desc.value; 827 const originalDescGet = desc.get; 828 const originalDescSet = desc.set; 829 // slice(2) cuz 'onclick' -> 'click', etc 830 const eventName = prop.slice(2); 831 let eventNameSymbol = zoneSymbolEventNames$1[eventName]; 832 if (!eventNameSymbol) { 833 eventNameSymbol = zoneSymbolEventNames$1[eventName] = zoneSymbol('ON_PROPERTY' + eventName); 834 } 835 desc.set = function (newValue) { 836 // in some of windows's onproperty callback, this is undefined 837 // so we need to check it 838 let target = this; 839 if (!target && obj === _global) { 840 target = _global; 841 } 842 if (!target) { 843 return; 844 } 845 const previousValue = target[eventNameSymbol]; 846 if (typeof previousValue === 'function') { 847 target.removeEventListener(eventName, wrapFn); 848 } 849 // issue #978, when onload handler was added before loading zone.js 850 // we should remove it with originalDescSet 851 originalDescSet && originalDescSet.call(target, null); 852 target[eventNameSymbol] = newValue; 853 if (typeof newValue === 'function') { 854 target.addEventListener(eventName, wrapFn, false); 855 } 856 }; 857 // The getter would return undefined for unassigned properties but the default value of an 858 // unassigned property is null 859 desc.get = function () { 860 // in some of windows's onproperty callback, this is undefined 861 // so we need to check it 862 let target = this; 863 if (!target && obj === _global) { 864 target = _global; 865 } 866 if (!target) { 867 return null; 868 } 869 const listener = target[eventNameSymbol]; 870 if (listener) { 871 return listener; 872 } else if (originalDescGet) { 873 // result will be null when use inline event attribute, 874 // such as <button onclick="func();">OK</button> 875 // because the onclick function is internal raw uncompiled handler 876 // the onclick will be evaluated when first time event was triggered or 877 // the property is accessed, https://github.com/angular/zone.js/issues/525 878 // so we should use original native get to retrieve the handler 879 let value = originalDescGet.call(this); 880 if (value) { 881 desc.set.call(this, value); 882 if (typeof target[REMOVE_ATTRIBUTE] === 'function') { 883 target.removeAttribute(prop); 884 } 885 return value; 886 } 887 } 888 return null; 889 }; 890 ObjectDefineProperty(obj, prop, desc); 891 obj[onPropPatchedSymbol] = true; 892} 893function patchOnProperties(obj, properties, prototype) { 894 if (properties) { 895 for (let i = 0; i < properties.length; i++) { 896 patchProperty(obj, 'on' + properties[i], prototype); 897 } 898 } else { 899 const onProperties = []; 900 for (const prop in obj) { 901 if (prop.slice(0, 2) == 'on') { 902 onProperties.push(prop); 903 } 904 } 905 for (let j = 0; j < onProperties.length; j++) { 906 patchProperty(obj, onProperties[j], prototype); 907 } 908 } 909} 910const originalInstanceKey = zoneSymbol('originalInstance'); 911// wrap some native API on `window` 912function patchClass(className) { 913 const OriginalClass = _global[className]; 914 if (!OriginalClass) return; 915 // keep original class in global 916 _global[zoneSymbol(className)] = OriginalClass; 917 _global[className] = function () { 918 const a = bindArguments(arguments, className); 919 switch (a.length) { 920 case 0: 921 this[originalInstanceKey] = new OriginalClass(); 922 break; 923 case 1: 924 this[originalInstanceKey] = new OriginalClass(a[0]); 925 break; 926 case 2: 927 this[originalInstanceKey] = new OriginalClass(a[0], a[1]); 928 break; 929 case 3: 930 this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2]); 931 break; 932 case 4: 933 this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2], a[3]); 934 break; 935 default: 936 throw new Error('Arg list too long.'); 937 } 938 }; 939 // attach original delegate to patched function 940 attachOriginToPatched(_global[className], OriginalClass); 941 const instance = new OriginalClass(function () {}); 942 let prop; 943 for (prop in instance) { 944 // https://bugs.webkit.org/show_bug.cgi?id=44721 945 if (className === 'XMLHttpRequest' && prop === 'responseBlob') continue; 946 (function (prop) { 947 if (typeof instance[prop] === 'function') { 948 _global[className].prototype[prop] = function () { 949 return this[originalInstanceKey][prop].apply(this[originalInstanceKey], arguments); 950 }; 951 } else { 952 ObjectDefineProperty(_global[className].prototype, prop, { 953 set: function (fn) { 954 if (typeof fn === 'function') { 955 this[originalInstanceKey][prop] = wrapWithCurrentZone(fn, className + '.' + prop); 956 // keep callback in wrapped function so we can 957 // use it in Function.prototype.toString to return 958 // the native one. 959 attachOriginToPatched(this[originalInstanceKey][prop], fn); 960 } else { 961 this[originalInstanceKey][prop] = fn; 962 } 963 }, 964 get: function () { 965 return this[originalInstanceKey][prop]; 966 } 967 }); 968 } 969 })(prop); 970 } 971 for (prop in OriginalClass) { 972 if (prop !== 'prototype' && OriginalClass.hasOwnProperty(prop)) { 973 _global[className][prop] = OriginalClass[prop]; 974 } 975 } 976} 977function patchMethod(target, name, patchFn) { 978 let proto = target; 979 while (proto && !proto.hasOwnProperty(name)) { 980 proto = ObjectGetPrototypeOf(proto); 981 } 982 if (!proto && target[name]) { 983 // somehow we did not find it, but we can see it. This happens on IE for Window properties. 984 proto = target; 985 } 986 const delegateName = zoneSymbol(name); 987 let delegate = null; 988 if (proto && (!(delegate = proto[delegateName]) || !proto.hasOwnProperty(delegateName))) { 989 delegate = proto[delegateName] = proto[name]; 990 // check whether proto[name] is writable 991 // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob 992 const desc = proto && ObjectGetOwnPropertyDescriptor(proto, name); 993 if (isPropertyWritable(desc)) { 994 const patchDelegate = patchFn(delegate, delegateName, name); 995 proto[name] = function () { 996 return patchDelegate(this, arguments); 997 }; 998 attachOriginToPatched(proto[name], delegate); 999 } 1000 } 1001 return delegate; 1002} 1003// TODO: @JiaLiPassion, support cancel task later if necessary 1004function patchMacroTask(obj, funcName, metaCreator) { 1005 let setNative = null; 1006 function scheduleTask(task) { 1007 const data = task.data; 1008 data.args[data.cbIdx] = function () { 1009 task.invoke.apply(this, arguments); 1010 }; 1011 setNative.apply(data.target, data.args); 1012 return task; 1013 } 1014 setNative = patchMethod(obj, funcName, delegate => function (self, args) { 1015 const meta = metaCreator(self, args); 1016 if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === 'function') { 1017 return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask); 1018 } else { 1019 // cause an error by calling it directly. 1020 return delegate.apply(self, args); 1021 } 1022 }); 1023} 1024function attachOriginToPatched(patched, original) { 1025 patched[zoneSymbol('OriginalDelegate')] = original; 1026} 1027let isDetectedIEOrEdge = false; 1028let ieOrEdge = false; 1029function isIE() { 1030 try { 1031 const ua = internalWindow.navigator.userAgent; 1032 if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1) { 1033 return true; 1034 } 1035 } catch (error) {} 1036 return false; 1037} 1038function isIEOrEdge() { 1039 if (isDetectedIEOrEdge) { 1040 return ieOrEdge; 1041 } 1042 isDetectedIEOrEdge = true; 1043 try { 1044 const ua = internalWindow.navigator.userAgent; 1045 if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1 || ua.indexOf('Edge/') !== -1) { 1046 ieOrEdge = true; 1047 } 1048 } catch (error) {} 1049 return ieOrEdge; 1050} 1051 1052/** 1053 * @fileoverview 1054 * @suppress {missingRequire} 1055 */ 1056let passiveSupported = false; 1057if (typeof window !== 'undefined') { 1058 try { 1059 const options = Object.defineProperty({}, 'passive', { 1060 get: function () { 1061 passiveSupported = true; 1062 } 1063 }); 1064 // Note: We pass the `options` object as the event handler too. This is not compatible with the 1065 // signature of `addEventListener` or `removeEventListener` but enables us to remove the handler 1066 // without an actual handler. 1067 window.addEventListener('test', options, options); 1068 window.removeEventListener('test', options, options); 1069 } catch (err) { 1070 passiveSupported = false; 1071 } 1072} 1073// an identifier to tell ZoneTask do not create a new invoke closure 1074const OPTIMIZED_ZONE_EVENT_TASK_DATA = { 1075 useG: true 1076}; 1077const zoneSymbolEventNames = {}; 1078const globalSources = {}; 1079const EVENT_NAME_SYMBOL_REGX = new RegExp('^' + ZONE_SYMBOL_PREFIX + '(\\w+)(true|false)$'); 1080const IMMEDIATE_PROPAGATION_SYMBOL = zoneSymbol('propagationStopped'); 1081function prepareEventNames(eventName, eventNameToString) { 1082 const falseEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + FALSE_STR; 1083 const trueEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + TRUE_STR; 1084 const symbol = ZONE_SYMBOL_PREFIX + falseEventName; 1085 const symbolCapture = ZONE_SYMBOL_PREFIX + trueEventName; 1086 zoneSymbolEventNames[eventName] = {}; 1087 zoneSymbolEventNames[eventName][FALSE_STR] = symbol; 1088 zoneSymbolEventNames[eventName][TRUE_STR] = symbolCapture; 1089} 1090function patchEventTarget(_global, api, apis, patchOptions) { 1091 const ADD_EVENT_LISTENER = patchOptions && patchOptions.add || ADD_EVENT_LISTENER_STR; 1092 const REMOVE_EVENT_LISTENER = patchOptions && patchOptions.rm || REMOVE_EVENT_LISTENER_STR; 1093 const LISTENERS_EVENT_LISTENER = patchOptions && patchOptions.listeners || 'eventListeners'; 1094 const REMOVE_ALL_LISTENERS_EVENT_LISTENER = patchOptions && patchOptions.rmAll || 'removeAllListeners'; 1095 const zoneSymbolAddEventListener = zoneSymbol(ADD_EVENT_LISTENER); 1096 const ADD_EVENT_LISTENER_SOURCE = '.' + ADD_EVENT_LISTENER + ':'; 1097 const PREPEND_EVENT_LISTENER = 'prependListener'; 1098 const PREPEND_EVENT_LISTENER_SOURCE = '.' + PREPEND_EVENT_LISTENER + ':'; 1099 const invokeTask = function (task, target, event) { 1100 // for better performance, check isRemoved which is set 1101 // by removeEventListener 1102 if (task.isRemoved) { 1103 return; 1104 } 1105 const delegate = task.callback; 1106 if (typeof delegate === 'object' && delegate.handleEvent) { 1107 // create the bind version of handleEvent when invoke 1108 task.callback = event => delegate.handleEvent(event); 1109 task.originalDelegate = delegate; 1110 } 1111 // invoke static task.invoke 1112 // need to try/catch error here, otherwise, the error in one event listener 1113 // will break the executions of the other event listeners. Also error will 1114 // not remove the event listener when `once` options is true. 1115 let error; 1116 try { 1117 task.invoke(task, target, [event]); 1118 } catch (err) { 1119 error = err; 1120 } 1121 const options = task.options; 1122 if (options && typeof options === 'object' && options.once) { 1123 // if options.once is true, after invoke once remove listener here 1124 // only browser need to do this, nodejs eventEmitter will cal removeListener 1125 // inside EventEmitter.once 1126 const delegate = task.originalDelegate ? task.originalDelegate : task.callback; 1127 target[REMOVE_EVENT_LISTENER].call(target, event.type, delegate, options); 1128 } 1129 return error; 1130 }; 1131 function globalCallback(context, event, isCapture) { 1132 // https://github.com/angular/zone.js/issues/911, in IE, sometimes 1133 // event will be undefined, so we need to use window.event 1134 event = event || _global.event; 1135 if (!event) { 1136 return; 1137 } 1138 // event.target is needed for Samsung TV and SourceBuffer 1139 // || global is needed https://github.com/angular/zone.js/issues/190 1140 const target = context || event.target || _global; 1141 const tasks = target[zoneSymbolEventNames[event.type][isCapture ? TRUE_STR : FALSE_STR]]; 1142 if (tasks) { 1143 const errors = []; 1144 // invoke all tasks which attached to current target with given event.type and capture = false 1145 // for performance concern, if task.length === 1, just invoke 1146 if (tasks.length === 1) { 1147 const err = invokeTask(tasks[0], target, event); 1148 err && errors.push(err); 1149 } else { 1150 // https://github.com/angular/zone.js/issues/836 1151 // copy the tasks array before invoke, to avoid 1152 // the callback will remove itself or other listener 1153 const copyTasks = tasks.slice(); 1154 for (let i = 0; i < copyTasks.length; i++) { 1155 if (event && event[IMMEDIATE_PROPAGATION_SYMBOL] === true) { 1156 break; 1157 } 1158 const err = invokeTask(copyTasks[i], target, event); 1159 err && errors.push(err); 1160 } 1161 } 1162 // Since there is only one error, we don't need to schedule microTask 1163 // to throw the error. 1164 if (errors.length === 1) { 1165 throw errors[0]; 1166 } else { 1167 for (let i = 0; i < errors.length; i++) { 1168 const err = errors[i]; 1169 api.nativeScheduleMicroTask(() => { 1170 throw err; 1171 }); 1172 } 1173 } 1174 } 1175 } 1176 // global shared zoneAwareCallback to handle all event callback with capture = false 1177 const globalZoneAwareCallback = function (event) { 1178 return globalCallback(this, event, false); 1179 }; 1180 // global shared zoneAwareCallback to handle all event callback with capture = true 1181 const globalZoneAwareCaptureCallback = function (event) { 1182 return globalCallback(this, event, true); 1183 }; 1184 function patchEventTargetMethods(obj, patchOptions) { 1185 if (!obj) { 1186 return false; 1187 } 1188 let useGlobalCallback = true; 1189 if (patchOptions && patchOptions.useG !== undefined) { 1190 useGlobalCallback = patchOptions.useG; 1191 } 1192 const validateHandler = patchOptions && patchOptions.vh; 1193 let checkDuplicate = true; 1194 if (patchOptions && patchOptions.chkDup !== undefined) { 1195 checkDuplicate = patchOptions.chkDup; 1196 } 1197 let returnTarget = false; 1198 if (patchOptions && patchOptions.rt !== undefined) { 1199 returnTarget = patchOptions.rt; 1200 } 1201 let proto = obj; 1202 while (proto && !proto.hasOwnProperty(ADD_EVENT_LISTENER)) { 1203 proto = ObjectGetPrototypeOf(proto); 1204 } 1205 if (!proto && obj[ADD_EVENT_LISTENER]) { 1206 // somehow we did not find it, but we can see it. This happens on IE for Window properties. 1207 proto = obj; 1208 } 1209 if (!proto) { 1210 return false; 1211 } 1212 if (proto[zoneSymbolAddEventListener]) { 1213 return false; 1214 } 1215 const eventNameToString = patchOptions && patchOptions.eventNameToString; 1216 // a shared global taskData to pass data for scheduleEventTask 1217 // so we do not need to create a new object just for pass some data 1218 const taskData = {}; 1219 const nativeAddEventListener = proto[zoneSymbolAddEventListener] = proto[ADD_EVENT_LISTENER]; 1220 const nativeRemoveEventListener = proto[zoneSymbol(REMOVE_EVENT_LISTENER)] = proto[REMOVE_EVENT_LISTENER]; 1221 const nativeListeners = proto[zoneSymbol(LISTENERS_EVENT_LISTENER)] = proto[LISTENERS_EVENT_LISTENER]; 1222 const nativeRemoveAllListeners = proto[zoneSymbol(REMOVE_ALL_LISTENERS_EVENT_LISTENER)] = proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER]; 1223 let nativePrependEventListener; 1224 if (patchOptions && patchOptions.prepend) { 1225 nativePrependEventListener = proto[zoneSymbol(patchOptions.prepend)] = proto[patchOptions.prepend]; 1226 } 1227 /** 1228 * This util function will build an option object with passive option 1229 * to handle all possible input from the user. 1230 */ 1231 function buildEventListenerOptions(options, passive) { 1232 if (!passiveSupported && typeof options === 'object' && options) { 1233 // doesn't support passive but user want to pass an object as options. 1234 // this will not work on some old browser, so we just pass a boolean 1235 // as useCapture parameter 1236 return !!options.capture; 1237 } 1238 if (!passiveSupported || !passive) { 1239 return options; 1240 } 1241 if (typeof options === 'boolean') { 1242 return { 1243 capture: options, 1244 passive: true 1245 }; 1246 } 1247 if (!options) { 1248 return { 1249 passive: true 1250 }; 1251 } 1252 if (typeof options === 'object' && options.passive !== false) { 1253 return { 1254 ...options, 1255 passive: true 1256 }; 1257 } 1258 return options; 1259 } 1260 const customScheduleGlobal = function (task) { 1261 // if there is already a task for the eventName + capture, 1262 // just return, because we use the shared globalZoneAwareCallback here. 1263 if (taskData.isExisting) { 1264 return; 1265 } 1266 return nativeAddEventListener.call(taskData.target, taskData.eventName, taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, taskData.options); 1267 }; 1268 const customCancelGlobal = function (task) { 1269 // if task is not marked as isRemoved, this call is directly 1270 // from Zone.prototype.cancelTask, we should remove the task 1271 // from tasksList of target first 1272 if (!task.isRemoved) { 1273 const symbolEventNames = zoneSymbolEventNames[task.eventName]; 1274 let symbolEventName; 1275 if (symbolEventNames) { 1276 symbolEventName = symbolEventNames[task.capture ? TRUE_STR : FALSE_STR]; 1277 } 1278 const existingTasks = symbolEventName && task.target[symbolEventName]; 1279 if (existingTasks) { 1280 for (let i = 0; i < existingTasks.length; i++) { 1281 const existingTask = existingTasks[i]; 1282 if (existingTask === task) { 1283 existingTasks.splice(i, 1); 1284 // set isRemoved to data for faster invokeTask check 1285 task.isRemoved = true; 1286 if (existingTasks.length === 0) { 1287 // all tasks for the eventName + capture have gone, 1288 // remove globalZoneAwareCallback and remove the task cache from target 1289 task.allRemoved = true; 1290 task.target[symbolEventName] = null; 1291 } 1292 break; 1293 } 1294 } 1295 } 1296 } 1297 // if all tasks for the eventName + capture have gone, 1298 // we will really remove the global event callback, 1299 // if not, return 1300 if (!task.allRemoved) { 1301 return; 1302 } 1303 return nativeRemoveEventListener.call(task.target, task.eventName, task.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, task.options); 1304 }; 1305 const customScheduleNonGlobal = function (task) { 1306 return nativeAddEventListener.call(taskData.target, taskData.eventName, task.invoke, taskData.options); 1307 }; 1308 const customSchedulePrepend = function (task) { 1309 return nativePrependEventListener.call(taskData.target, taskData.eventName, task.invoke, taskData.options); 1310 }; 1311 const customCancelNonGlobal = function (task) { 1312 return nativeRemoveEventListener.call(task.target, task.eventName, task.invoke, task.options); 1313 }; 1314 const customSchedule = useGlobalCallback ? customScheduleGlobal : customScheduleNonGlobal; 1315 const customCancel = useGlobalCallback ? customCancelGlobal : customCancelNonGlobal; 1316 const compareTaskCallbackVsDelegate = function (task, delegate) { 1317 const typeOfDelegate = typeof delegate; 1318 return typeOfDelegate === 'function' && task.callback === delegate || typeOfDelegate === 'object' && task.originalDelegate === delegate; 1319 }; 1320 const compare = patchOptions && patchOptions.diff ? patchOptions.diff : compareTaskCallbackVsDelegate; 1321 const unpatchedEvents = Zone[zoneSymbol('UNPATCHED_EVENTS')]; 1322 const passiveEvents = _global[zoneSymbol('PASSIVE_EVENTS')]; 1323 const makeAddListener = function (nativeListener, addSource, customScheduleFn, customCancelFn, returnTarget = false, prepend = false) { 1324 return function () { 1325 const target = this || _global; 1326 let eventName = arguments[0]; 1327 if (patchOptions && patchOptions.transferEventName) { 1328 eventName = patchOptions.transferEventName(eventName); 1329 } 1330 let delegate = arguments[1]; 1331 if (!delegate) { 1332 return nativeListener.apply(this, arguments); 1333 } 1334 if (isNode && eventName === 'uncaughtException') { 1335 // don't patch uncaughtException of nodejs to prevent endless loop 1336 return nativeListener.apply(this, arguments); 1337 } 1338 // don't create the bind delegate function for handleEvent 1339 // case here to improve addEventListener performance 1340 // we will create the bind delegate when invoke 1341 let isHandleEvent = false; 1342 if (typeof delegate !== 'function') { 1343 if (!delegate.handleEvent) { 1344 return nativeListener.apply(this, arguments); 1345 } 1346 isHandleEvent = true; 1347 } 1348 if (validateHandler && !validateHandler(nativeListener, delegate, target, arguments)) { 1349 return; 1350 } 1351 const passive = passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1; 1352 const options = buildEventListenerOptions(arguments[2], passive); 1353 const signal = options?.signal; 1354 if (signal?.aborted) { 1355 // the signal is an aborted one, just return without attaching the event listener. 1356 return; 1357 } 1358 if (unpatchedEvents) { 1359 // check unpatched list 1360 for (let i = 0; i < unpatchedEvents.length; i++) { 1361 if (eventName === unpatchedEvents[i]) { 1362 if (passive) { 1363 return nativeListener.call(target, eventName, delegate, options); 1364 } else { 1365 return nativeListener.apply(this, arguments); 1366 } 1367 } 1368 } 1369 } 1370 const capture = !options ? false : typeof options === 'boolean' ? true : options.capture; 1371 const once = options && typeof options === 'object' ? options.once : false; 1372 const zone = Zone.current; 1373 let symbolEventNames = zoneSymbolEventNames[eventName]; 1374 if (!symbolEventNames) { 1375 prepareEventNames(eventName, eventNameToString); 1376 symbolEventNames = zoneSymbolEventNames[eventName]; 1377 } 1378 const symbolEventName = symbolEventNames[capture ? TRUE_STR : FALSE_STR]; 1379 let existingTasks = target[symbolEventName]; 1380 let isExisting = false; 1381 if (existingTasks) { 1382 // already have task registered 1383 isExisting = true; 1384 if (checkDuplicate) { 1385 for (let i = 0; i < existingTasks.length; i++) { 1386 if (compare(existingTasks[i], delegate)) { 1387 // same callback, same capture, same event name, just return 1388 return; 1389 } 1390 } 1391 } 1392 } else { 1393 existingTasks = target[symbolEventName] = []; 1394 } 1395 let source; 1396 const constructorName = target.constructor['name']; 1397 const targetSource = globalSources[constructorName]; 1398 if (targetSource) { 1399 source = targetSource[eventName]; 1400 } 1401 if (!source) { 1402 source = constructorName + addSource + (eventNameToString ? eventNameToString(eventName) : eventName); 1403 } 1404 // do not create a new object as task.data to pass those things 1405 // just use the global shared one 1406 taskData.options = options; 1407 if (once) { 1408 // if addEventListener with once options, we don't pass it to 1409 // native addEventListener, instead we keep the once setting 1410 // and handle ourselves. 1411 taskData.options.once = false; 1412 } 1413 taskData.target = target; 1414 taskData.capture = capture; 1415 taskData.eventName = eventName; 1416 taskData.isExisting = isExisting; 1417 const data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined; 1418 // keep taskData into data to allow onScheduleEventTask to access the task information 1419 if (data) { 1420 data.taskData = taskData; 1421 } 1422 if (signal) { 1423 // if addEventListener with signal options, we don't pass it to 1424 // native addEventListener, instead we keep the signal setting 1425 // and handle ourselves. 1426 taskData.options.signal = undefined; 1427 } 1428 const task = zone.scheduleEventTask(source, delegate, data, customScheduleFn, customCancelFn); 1429 if (signal) { 1430 // after task is scheduled, we need to store the signal back to task.options 1431 taskData.options.signal = signal; 1432 // Wrapping `task` in a weak reference would not prevent memory leaks. Weak references are 1433 // primarily used for preventing strong references cycles. `onAbort` is always reachable 1434 // as it's an event listener, so its closure retains a strong reference to the `task`. 1435 const onAbort = () => task.zone.cancelTask(task); 1436 nativeListener.call(signal, 'abort', onAbort, { 1437 once: true 1438 }); 1439 // We need to remove the `abort` listener when the event listener is going to be removed, 1440 // as it creates a closure that captures `task`. This closure retains a reference to the 1441 // `task` object even after it goes out of scope, preventing `task` from being garbage 1442 // collected. 1443 if (data) { 1444 data.removeAbortListener = () => signal.removeEventListener('abort', onAbort); 1445 } 1446 } 1447 // should clear taskData.target to avoid memory leak 1448 // issue, https://github.com/angular/angular/issues/20442 1449 taskData.target = null; 1450 // need to clear up taskData because it is a global object 1451 if (data) { 1452 data.taskData = null; 1453 } 1454 // have to save those information to task in case 1455 // application may call task.zone.cancelTask() directly 1456 if (once) { 1457 options.once = true; 1458 } 1459 if (!(!passiveSupported && typeof task.options === 'boolean')) { 1460 // if not support passive, and we pass an option object 1461 // to addEventListener, we should save the options to task 1462 task.options = options; 1463 } 1464 task.target = target; 1465 task.capture = capture; 1466 task.eventName = eventName; 1467 if (isHandleEvent) { 1468 // save original delegate for compare to check duplicate 1469 task.originalDelegate = delegate; 1470 } 1471 if (!prepend) { 1472 existingTasks.push(task); 1473 } else { 1474 existingTasks.unshift(task); 1475 } 1476 if (returnTarget) { 1477 return target; 1478 } 1479 }; 1480 }; 1481 proto[ADD_EVENT_LISTENER] = makeAddListener(nativeAddEventListener, ADD_EVENT_LISTENER_SOURCE, customSchedule, customCancel, returnTarget); 1482 if (nativePrependEventListener) { 1483 proto[PREPEND_EVENT_LISTENER] = makeAddListener(nativePrependEventListener, PREPEND_EVENT_LISTENER_SOURCE, customSchedulePrepend, customCancel, returnTarget, true); 1484 } 1485 proto[REMOVE_EVENT_LISTENER] = function () { 1486 const target = this || _global; 1487 let eventName = arguments[0]; 1488 if (patchOptions && patchOptions.transferEventName) { 1489 eventName = patchOptions.transferEventName(eventName); 1490 } 1491 const options = arguments[2]; 1492 const capture = !options ? false : typeof options === 'boolean' ? true : options.capture; 1493 const delegate = arguments[1]; 1494 if (!delegate) { 1495 return nativeRemoveEventListener.apply(this, arguments); 1496 } 1497 if (validateHandler && !validateHandler(nativeRemoveEventListener, delegate, target, arguments)) { 1498 return; 1499 } 1500 const symbolEventNames = zoneSymbolEventNames[eventName]; 1501 let symbolEventName; 1502 if (symbolEventNames) { 1503 symbolEventName = symbolEventNames[capture ? TRUE_STR : FALSE_STR]; 1504 } 1505 const existingTasks = symbolEventName && target[symbolEventName]; 1506 if (existingTasks) { 1507 for (let i = 0; i < existingTasks.length; i++) { 1508 const existingTask = existingTasks[i]; 1509 if (compare(existingTask, delegate)) { 1510 existingTasks.splice(i, 1); 1511 // set isRemoved to data for faster invokeTask check 1512 existingTask.isRemoved = true; 1513 if (existingTasks.length === 0) { 1514 // all tasks for the eventName + capture have gone, 1515 // remove globalZoneAwareCallback and remove the task cache from target 1516 existingTask.allRemoved = true; 1517 target[symbolEventName] = null; 1518 // in the target, we have an event listener which is added by on_property 1519 // such as target.onclick = function() {}, so we need to clear this internal 1520 // property too if all delegates with capture=false were removed 1521 // https:// github.com/angular/angular/issues/31643 1522 // https://github.com/angular/angular/issues/54581 1523 if (!capture && typeof eventName === 'string') { 1524 const onPropertySymbol = ZONE_SYMBOL_PREFIX + 'ON_PROPERTY' + eventName; 1525 target[onPropertySymbol] = null; 1526 } 1527 } 1528 // Note that `removeAllListeners` would ultimately call `removeEventListener`, 1529 // so we're safe to remove the abort listener only once here. 1530 const taskData = existingTask.data; 1531 if (taskData?.removeAbortListener) { 1532 taskData.removeAbortListener(); 1533 taskData.removeAbortListener = null; 1534 } 1535 existingTask.zone.cancelTask(existingTask); 1536 if (returnTarget) { 1537 return target; 1538 } 1539 return; 1540 } 1541 } 1542 } 1543 // issue 930, didn't find the event name or callback 1544 // from zone kept existingTasks, the callback maybe 1545 // added outside of zone, we need to call native removeEventListener 1546 // to try to remove it. 1547 return nativeRemoveEventListener.apply(this, arguments); 1548 }; 1549 proto[LISTENERS_EVENT_LISTENER] = function () { 1550 const target = this || _global; 1551 let eventName = arguments[0]; 1552 if (patchOptions && patchOptions.transferEventName) { 1553 eventName = patchOptions.transferEventName(eventName); 1554 } 1555 const listeners = []; 1556 const tasks = findEventTasks(target, eventNameToString ? eventNameToString(eventName) : eventName); 1557 for (let i = 0; i < tasks.length; i++) { 1558 const task = tasks[i]; 1559 let delegate = task.originalDelegate ? task.originalDelegate : task.callback; 1560 listeners.push(delegate); 1561 } 1562 return listeners; 1563 }; 1564 proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () { 1565 const target = this || _global; 1566 let eventName = arguments[0]; 1567 if (!eventName) { 1568 const keys = Object.keys(target); 1569 for (let i = 0; i < keys.length; i++) { 1570 const prop = keys[i]; 1571 const match = EVENT_NAME_SYMBOL_REGX.exec(prop); 1572 let evtName = match && match[1]; 1573 // in nodejs EventEmitter, removeListener event is 1574 // used for monitoring the removeListener call, 1575 // so just keep removeListener eventListener until 1576 // all other eventListeners are removed 1577 if (evtName && evtName !== 'removeListener') { 1578 this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName); 1579 } 1580 } 1581 // remove removeListener listener finally 1582 this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener'); 1583 } else { 1584 if (patchOptions && patchOptions.transferEventName) { 1585 eventName = patchOptions.transferEventName(eventName); 1586 } 1587 const symbolEventNames = zoneSymbolEventNames[eventName]; 1588 if (symbolEventNames) { 1589 const symbolEventName = symbolEventNames[FALSE_STR]; 1590 const symbolCaptureEventName = symbolEventNames[TRUE_STR]; 1591 const tasks = target[symbolEventName]; 1592 const captureTasks = target[symbolCaptureEventName]; 1593 if (tasks) { 1594 const removeTasks = tasks.slice(); 1595 for (let i = 0; i < removeTasks.length; i++) { 1596 const task = removeTasks[i]; 1597 let delegate = task.originalDelegate ? task.originalDelegate : task.callback; 1598 this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options); 1599 } 1600 } 1601 if (captureTasks) { 1602 const removeTasks = captureTasks.slice(); 1603 for (let i = 0; i < removeTasks.length; i++) { 1604 const task = removeTasks[i]; 1605 let delegate = task.originalDelegate ? task.originalDelegate : task.callback; 1606 this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options); 1607 } 1608 } 1609 } 1610 } 1611 if (returnTarget) { 1612 return this; 1613 } 1614 }; 1615 // for native toString patch 1616 attachOriginToPatched(proto[ADD_EVENT_LISTENER], nativeAddEventListener); 1617 attachOriginToPatched(proto[REMOVE_EVENT_LISTENER], nativeRemoveEventListener); 1618 if (nativeRemoveAllListeners) { 1619 attachOriginToPatched(proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER], nativeRemoveAllListeners); 1620 } 1621 if (nativeListeners) { 1622 attachOriginToPatched(proto[LISTENERS_EVENT_LISTENER], nativeListeners); 1623 } 1624 return true; 1625 } 1626 let results = []; 1627 for (let i = 0; i < apis.length; i++) { 1628 results[i] = patchEventTargetMethods(apis[i], patchOptions); 1629 } 1630 return results; 1631} 1632function findEventTasks(target, eventName) { 1633 if (!eventName) { 1634 const foundTasks = []; 1635 for (let prop in target) { 1636 const match = EVENT_NAME_SYMBOL_REGX.exec(prop); 1637 let evtName = match && match[1]; 1638 if (evtName && (!eventName || evtName === eventName)) { 1639 const tasks = target[prop]; 1640 if (tasks) { 1641 for (let i = 0; i < tasks.length; i++) { 1642 foundTasks.push(tasks[i]); 1643 } 1644 } 1645 } 1646 } 1647 return foundTasks; 1648 } 1649 let symbolEventName = zoneSymbolEventNames[eventName]; 1650 if (!symbolEventName) { 1651 prepareEventNames(eventName); 1652 symbolEventName = zoneSymbolEventNames[eventName]; 1653 } 1654 const captureFalseTasks = target[symbolEventName[FALSE_STR]]; 1655 const captureTrueTasks = target[symbolEventName[TRUE_STR]]; 1656 if (!captureFalseTasks) { 1657 return captureTrueTasks ? captureTrueTasks.slice() : []; 1658 } else { 1659 return captureTrueTasks ? captureFalseTasks.concat(captureTrueTasks) : captureFalseTasks.slice(); 1660 } 1661} 1662function patchEventPrototype(global, api) { 1663 const Event = global['Event']; 1664 if (Event && Event.prototype) { 1665 api.patchMethod(Event.prototype, 'stopImmediatePropagation', delegate => function (self, args) { 1666 self[IMMEDIATE_PROPAGATION_SYMBOL] = true; 1667 // we need to call the native stopImmediatePropagation 1668 // in case in some hybrid application, some part of 1669 // application will be controlled by zone, some are not 1670 delegate && delegate.apply(self, args); 1671 }); 1672 } 1673} 1674 1675/** 1676 * @fileoverview 1677 * @suppress {missingRequire} 1678 */ 1679function patchQueueMicrotask(global, api) { 1680 api.patchMethod(global, 'queueMicrotask', delegate => { 1681 return function (self, args) { 1682 Zone.current.scheduleMicroTask('queueMicrotask', args[0]); 1683 }; 1684 }); 1685} 1686 1687/** 1688 * @fileoverview 1689 * @suppress {missingRequire} 1690 */ 1691const taskSymbol = zoneSymbol('zoneTask'); 1692function patchTimer(window, setName, cancelName, nameSuffix) { 1693 let setNative = null; 1694 let clearNative = null; 1695 setName += nameSuffix; 1696 cancelName += nameSuffix; 1697 const tasksByHandleId = {}; 1698 function scheduleTask(task) { 1699 const data = task.data; 1700 data.args[0] = function () { 1701 return task.invoke.apply(this, arguments); 1702 };
1703 data.handleId = setNative.apply(window, data.args); 1704 return task; 1705 } 1706 function clearTask(task) { 1707 return clearNative.call(window, task.data.handleId); 1708 } 1709 setNative = patchMethod(window, setName, delegate => function (self, args) { 1710 if (typeof args[0] === 'function') { 1711 const options = { 1712 isPeriodic: nameSuffix === 'Interval', 1713 delay: nameSuffix === 'Timeout' || nameSuffix === 'Interval' ? args[1] || 0 : undefined, 1714 args: args 1715 }; 1716 const callback = args[0]; 1717 args[0] = function timer() { 1718 try { 1719 return callback.apply(this, arguments); 1720 } finally { 1721 // issue-934, task will be cancelled 1722 // even it is a periodic task such as 1723 // setInterval 1724 // https://github.com/angular/angular/issues/40387 1725 // Cleanup tasksByHandleId should be handled before scheduleTask 1726 // Since some zoneSpec may intercept and doesn't trigger 1727 // scheduleFn(scheduleTask) provided here. 1728 if (!options.isPeriodic) { 1729 if (typeof options.handleId === 'number') { 1730 // in non-nodejs env, we remove timerId 1731 // from local cache 1732 delete tasksByHandleId[options.handleId]; 1733 } else if (options.handleId) { 1734 // Node returns complex objects as handleIds 1735 // we remove task reference from timer object 1736 options.handleId[taskSymbol] = null; 1737 } 1738 } 1739 } 1740 }; 1741 const task = scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask); 1742 if (!task) { 1743 return task; 1744 } 1745 // Node.js must additionally support the ref and unref functions. 1746 const handle = task.data.handleId; 1747 if (typeof handle === 'number') { 1748 // for non nodejs env, we save handleId: task 1749 // mapping in local cache for clearTimeout 1750 tasksByHandleId[handle] = task; 1751 } else if (handle) { 1752 // for nodejs env, we save task 1753 // reference in timerId Object for clearTimeout 1754 handle[taskSymbol] = task; 1755 } 1756 // check whether handle is null, because some polyfill or browser 1757 // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame 1758 if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && typeof handle.unref === 'function') { 1759 task.ref = handle.ref.bind(handle); 1760 task.unref = handle.unref.bind(handle); 1761 } 1762 if (typeof handle === 'number' || handle) { 1763 return handle; 1764 } 1765 return task; 1766 } else { 1767 // cause an error by calling it directly. 1768 return delegate.apply(window, args); 1769 } 1770 }); 1771 clearNative = patchMethod(window, cancelName, delegate => function (self, args) { 1772 const id = args[0]; 1773 let task; 1774 if (typeof id === 'number') { 1775 // non nodejs env. 1776 task = tasksByHandleId[id]; 1777 } else { 1778 // nodejs env. 1779 task = id && id[taskSymbol]; 1780 // other environments. 1781 if (!task) { 1782 task = id; 1783 } 1784 } 1785 if (task && typeof task.type === 'string') { 1786 if (task.state !== 'notScheduled' && (task.cancelFn && task.data.isPeriodic || task.runCount === 0)) { 1787 if (typeof id === 'number') { 1788 delete tasksByHandleId[id]; 1789 } else if (id) { 1790 id[taskSymbol] = null; 1791 } 1792 // Do not cancel already canceled functions 1793 task.zone.cancelTask(task); 1794 } 1795 } else { 1796 // cause an error by calling it directly. 1797 delegate.apply(window, args); 1798 } 1799 }); 1800} 1801function patchCustomElements(_global, api) { 1802 const { 1803 isBrowser, 1804 isMix 1805 } = api.getGlobalObjects(); 1806 if (!isBrowser && !isMix || !_global['customElements'] || !('customElements' in _global)) { 1807 return; 1808 } 1809 // https://html.spec.whatwg.org/multipage/custom-elements.html#concept-custom-element-definition-lifecycle-callbacks 1810 const callbacks = ['connectedCallback', 'disconnectedCallback', 'adoptedCallback', 'attributeChangedCallback', 'formAssociatedCallback', 'formDisabledCallback', 'formResetCallback', 'formStateRestoreCallback']; 1811 api.patchCallbacks(api, _global.customElements, 'customElements', 'define', callbacks); 1812} 1813function eventTargetPatch(_global, api) {
vendor: 36,783 bytes, lines 1814-2707
1814 if (Zone[api.symbol('patchEventTarget')]) { 1815 // EventTarget is already patched. 1816 return; 1817 } 1818 const { 1819 eventNames, 1820 zoneSymbolEventNames, 1821 TRUE_STR, 1822 FALSE_STR, 1823 ZONE_SYMBOL_PREFIX 1824 } = api.getGlobalObjects(); 1825 // predefine all __zone_symbol__ + eventName + true/false string 1826 for (let i = 0; i < eventNames.length; i++) { 1827 const eventName = eventNames[i]; 1828 const falseEventName = eventName + FALSE_STR; 1829 const trueEventName = eventName + TRUE_STR; 1830 const symbol = ZONE_SYMBOL_PREFIX + falseEventName; 1831 const symbolCapture = ZONE_SYMBOL_PREFIX + trueEventName; 1832 zoneSymbolEventNames[eventName] = {}; 1833 zoneSymbolEventNames[eventName][FALSE_STR] = symbol; 1834 zoneSymbolEventNames[eventName][TRUE_STR] = symbolCapture; 1835 } 1836 const EVENT_TARGET = _global['EventTarget']; 1837 if (!EVENT_TARGET || !EVENT_TARGET.prototype) { 1838 return; 1839 } 1840 api.patchEventTarget(_global, api, [EVENT_TARGET && EVENT_TARGET.prototype]); 1841 return true; 1842} 1843function patchEvent(global, api) { 1844 api.patchEventPrototype(global, api); 1845} 1846 1847/** 1848 * @fileoverview 1849 * @suppress {globalThis} 1850 */ 1851function filterProperties(target, onProperties, ignoreProperties) { 1852 if (!ignoreProperties || ignoreProperties.length === 0) { 1853 return onProperties; 1854 } 1855 const tip = ignoreProperties.filter(ip => ip.target === target); 1856 if (!tip || tip.length === 0) { 1857 return onProperties; 1858 } 1859 const targetIgnoreProperties = tip[0].ignoreProperties; 1860 return onProperties.filter(op => targetIgnoreProperties.indexOf(op) === -1); 1861} 1862function patchFilteredProperties(target, onProperties, ignoreProperties, prototype) { 1863 // check whether target is available, sometimes target will be undefined 1864 // because different browser or some 3rd party plugin. 1865 if (!target) { 1866 return; 1867 } 1868 const filteredProperties = filterProperties(target, onProperties, ignoreProperties); 1869 patchOnProperties(target, filteredProperties, prototype); 1870} 1871/** 1872 * Get all event name properties which the event name startsWith `on` 1873 * from the target object itself, inherited properties are not considered. 1874 */ 1875function getOnEventNames(target) { 1876 return Object.getOwnPropertyNames(target).filter(name => name.startsWith('on') && name.length > 2).map(name => name.substring(2)); 1877} 1878function propertyDescriptorPatch(api, _global) { 1879 if (isNode && !isMix) { 1880 return; 1881 } 1882 if (Zone[api.symbol('patchEvents')]) { 1883 // events are already been patched by legacy patch. 1884 return; 1885 } 1886 const ignoreProperties = _global['__Zone_ignore_on_properties']; 1887 // for browsers that we can patch the descriptor: Chrome & Firefox 1888 let patchTargets = []; 1889 if (isBrowser) { 1890 const internalWindow = window; 1891 patchTargets = patchTargets.concat(['Document', 'SVGElement', 'Element', 'HTMLElement', 'HTMLBodyElement', 'HTMLMediaElement', 'HTMLFrameSetElement', 'HTMLFrameElement', 'HTMLIFrameElement', 'HTMLMarqueeElement', 'Worker']); 1892 const ignoreErrorProperties = isIE() ? [{ 1893 target: internalWindow, 1894 ignoreProperties: ['error'] 1895 }] : []; 1896 // in IE/Edge, onProp not exist in window object, but in WindowPrototype 1897 // so we need to pass WindowPrototype to check onProp exist or not 1898 patchFilteredProperties(internalWindow, getOnEventNames(internalWindow), ignoreProperties ? ignoreProperties.concat(ignoreErrorProperties) : ignoreProperties, ObjectGetPrototypeOf(internalWindow)); 1899 } 1900 patchTargets = patchTargets.concat(['XMLHttpRequest', 'XMLHttpRequestEventTarget', 'IDBIndex', 'IDBRequest', 'IDBOpenDBRequest', 'IDBDatabase', 'IDBTransaction', 'IDBCursor', 'WebSocket']); 1901 for (let i = 0; i < patchTargets.length; i++) { 1902 const target = _global[patchTargets[i]]; 1903 target && target.prototype && patchFilteredProperties(target.prototype, getOnEventNames(target.prototype), ignoreProperties); 1904 } 1905} 1906 1907/** 1908 * @fileoverview 1909 * @suppress {missingRequire} 1910 */ 1911function patchBrowser(Zone) { 1912 Zone.__load_patch('legacy', global => { 1913 const legacyPatch = global[Zone.__symbol__('legacyPatch')]; 1914 if (legacyPatch) { 1915 legacyPatch(); 1916 } 1917 }); 1918 Zone.__load_patch('timers', global => { 1919 const set = 'set'; 1920 const clear = 'clear'; 1921 patchTimer(global, set, clear, 'Timeout'); 1922 patchTimer(global, set, clear, 'Interval'); 1923 patchTimer(global, set, clear, 'Immediate'); 1924 }); 1925 Zone.__load_patch('requestAnimationFrame', global => { 1926 patchTimer(global, 'request', 'cancel', 'AnimationFrame'); 1927 patchTimer(global, 'mozRequest', 'mozCancel', 'AnimationFrame'); 1928 patchTimer(global, 'webkitRequest', 'webkitCancel', 'AnimationFrame'); 1929 }); 1930 Zone.__load_patch('blocking', (global, Zone) => { 1931 const blockingMethods = ['alert', 'prompt', 'confirm']; 1932 for (let i = 0; i < blockingMethods.length; i++) { 1933 const name = blockingMethods[i]; 1934 patchMethod(global, name, (delegate, symbol, name) => { 1935 return function (s, args) { 1936 return Zone.current.run(delegate, global, args, name); 1937 }; 1938 }); 1939 } 1940 }); 1941 Zone.__load_patch('EventTarget', (global, Zone, api) => { 1942 patchEvent(global, api); 1943 eventTargetPatch(global, api); 1944 // patch XMLHttpRequestEventTarget's addEventListener/removeEventListener 1945 const XMLHttpRequestEventTarget = global['XMLHttpRequestEventTarget']; 1946 if (XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype) { 1947 api.patchEventTarget(global, api, [XMLHttpRequestEventTarget.prototype]); 1948 } 1949 }); 1950 Zone.__load_patch('MutationObserver', (global, Zone, api) => { 1951 patchClass('MutationObserver'); 1952 patchClass('WebKitMutationObserver'); 1953 }); 1954 Zone.__load_patch('IntersectionObserver', (global, Zone, api) => { 1955 patchClass('IntersectionObserver'); 1956 }); 1957 Zone.__load_patch('FileReader', (global, Zone, api) => { 1958 patchClass('FileReader'); 1959 }); 1960 Zone.__load_patch('on_property', (global, Zone, api) => { 1961 propertyDescriptorPatch(api, global); 1962 }); 1963 Zone.__load_patch('customElements', (global, Zone, api) => { 1964 patchCustomElements(global, api); 1965 }); 1966 Zone.__load_patch('XHR', (global, Zone) => { 1967 // Treat XMLHttpRequest as a macrotask. 1968 patchXHR(global); 1969 const XHR_TASK = zoneSymbol('xhrTask'); 1970 const XHR_SYNC = zoneSymbol('xhrSync'); 1971 const XHR_LISTENER = zoneSymbol('xhrListener'); 1972 const XHR_SCHEDULED = zoneSymbol('xhrScheduled'); 1973 const XHR_URL = zoneSymbol('xhrURL'); 1974 const XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol('xhrErrorBeforeScheduled'); 1975 function patchXHR(window) { 1976 const XMLHttpRequest = window['XMLHttpRequest']; 1977 if (!XMLHttpRequest) { 1978 // XMLHttpRequest is not available in service worker 1979 return; 1980 } 1981 const XMLHttpRequestPrototype = XMLHttpRequest.prototype; 1982 function findPendingTask(target) { 1983 return target[XHR_TASK]; 1984 } 1985 let oriAddListener = XMLHttpRequestPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER]; 1986 let oriRemoveListener = XMLHttpRequestPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; 1987 if (!oriAddListener) { 1988 const XMLHttpRequestEventTarget = window['XMLHttpRequestEventTarget']; 1989 if (XMLHttpRequestEventTarget) { 1990 const XMLHttpRequestEventTargetPrototype = XMLHttpRequestEventTarget.prototype; 1991 oriAddListener = XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER]; 1992 oriRemoveListener = XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; 1993 } 1994 } 1995 const READY_STATE_CHANGE = 'readystatechange'; 1996 const SCHEDULED = 'scheduled'; 1997 function scheduleTask(task) { 1998 const data = task.data; 1999 const target = data.target; 2000 target[XHR_SCHEDULED] = false; 2001 target[XHR_ERROR_BEFORE_SCHEDULED] = false; 2002 // remove existing event listener 2003 const listener = target[XHR_LISTENER]; 2004 if (!oriAddListener) { 2005 oriAddListener = target[ZONE_SYMBOL_ADD_EVENT_LISTENER]; 2006 oriRemoveListener = target[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; 2007 } 2008 if (listener) { 2009 oriRemoveListener.call(target, READY_STATE_CHANGE, listener); 2010 } 2011 const newListener = target[XHR_LISTENER] = () => { 2012 if (target.readyState === target.DONE) { 2013 // sometimes on some browsers XMLHttpRequest will fire onreadystatechange with 2014 // readyState=4 multiple times, so we need to check task state here 2015 if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) { 2016 // check whether the xhr has registered onload listener 2017 // if that is the case, the task should invoke after all 2018 // onload listeners finish. 2019 // Also if the request failed without response (status = 0), the load event handler 2020 // will not be triggered, in that case, we should also invoke the placeholder callback 2021 // to close the XMLHttpRequest::send macroTask. 2022 // https://github.com/angular/angular/issues/38795 2023 const loadTasks = target[Zone.__symbol__('loadfalse')]; 2024 if (target.status !== 0 && loadTasks && loadTasks.length > 0) { 2025 const oriInvoke = task.invoke; 2026 task.invoke = function () { 2027 // need to load the tasks again, because in other 2028 // load listener, they may remove themselves 2029 const loadTasks = target[Zone.__symbol__('loadfalse')]; 2030 for (let i = 0; i < loadTasks.length; i++) { 2031 if (loadTasks[i] === task) { 2032 loadTasks.splice(i, 1); 2033 } 2034 } 2035 if (!data.aborted && task.state === SCHEDULED) { 2036 oriInvoke.call(task); 2037 } 2038 }; 2039 loadTasks.push(task); 2040 } else { 2041 task.invoke(); 2042 } 2043 } else if (!data.aborted && target[XHR_SCHEDULED] === false) { 2044 // error occurs when xhr.send() 2045 target[XHR_ERROR_BEFORE_SCHEDULED] = true; 2046 } 2047 } 2048 }; 2049 oriAddListener.call(target, READY_STATE_CHANGE, newListener); 2050 const storedTask = target[XHR_TASK]; 2051 if (!storedTask) { 2052 target[XHR_TASK] = task; 2053 } 2054 sendNative.apply(target, data.args); 2055 target[XHR_SCHEDULED] = true; 2056 return task; 2057 } 2058 function placeholderCallback() {} 2059 function clearTask(task) { 2060 const data = task.data; 2061 // Note - ideally, we would call data.target.removeEventListener here, but it's too late 2062 // to prevent it from firing. So instead, we store info for the event listener. 2063 data.aborted = true; 2064 return abortNative.apply(data.target, data.args); 2065 } 2066 const openNative = patchMethod(XMLHttpRequestPrototype, 'open', () => function (self, args) { 2067 self[XHR_SYNC] = args[2] == false; 2068 self[XHR_URL] = args[1]; 2069 return openNative.apply(self, args); 2070 }); 2071 const XMLHTTPREQUEST_SOURCE = 'XMLHttpRequest.send'; 2072 const fetchTaskAborting = zoneSymbol('fetchTaskAborting'); 2073 const fetchTaskScheduling = zoneSymbol('fetchTaskScheduling'); 2074 const sendNative = patchMethod(XMLHttpRequestPrototype, 'send', () => function (self, args) { 2075 if (Zone.current[fetchTaskScheduling] === true) { 2076 // a fetch is scheduling, so we are using xhr to polyfill fetch 2077 // and because we already schedule macroTask for fetch, we should 2078 // not schedule a macroTask for xhr again 2079 return sendNative.apply(self, args); 2080 } 2081 if (self[XHR_SYNC]) { 2082 // if the XHR is sync there is no task to schedule, just execute the code. 2083 return sendNative.apply(self, args); 2084 } else { 2085 const options = { 2086 target: self, 2087 url: self[XHR_URL], 2088 isPeriodic: false, 2089 args: args, 2090 aborted: false 2091 }; 2092 const task = scheduleMacroTaskWithCurrentZone(XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask); 2093 if (self && self[XHR_ERROR_BEFORE_SCHEDULED] === true && !options.aborted && task.state === SCHEDULED) { 2094 // xhr request throw error when send 2095 // we should invoke task instead of leaving a scheduled 2096 // pending macroTask 2097 task.invoke(); 2098 } 2099 } 2100 }); 2101 const abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', () => function (self, args) { 2102 const task = findPendingTask(self); 2103 if (task && typeof task.type == 'string') { 2104 // If the XHR has already completed, do nothing. 2105 // If the XHR has already been aborted, do nothing. 2106 // Fix #569, call abort multiple times before done will cause 2107 // macroTask task count be negative number 2108 if (task.cancelFn == null || task.data && task.data.aborted) { 2109 return; 2110 } 2111 task.zone.cancelTask(task); 2112 } else if (Zone.current[fetchTaskAborting] === true) { 2113 // the abort is called from fetch polyfill, we need to call native abort of XHR. 2114 return abortNative.apply(self, args); 2115 } 2116 // Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no 2117 // task 2118 // to cancel. Do nothing. 2119 }); 2120 } 2121 }); 2122 Zone.__load_patch('geolocation', global => { 2123 /// GEO_LOCATION 2124 if (global['navigator'] && global['navigator'].geolocation) { 2125 patchPrototype(global['navigator'].geolocation, ['getCurrentPosition', 'watchPosition']); 2126 } 2127 }); 2128 Zone.__load_patch('PromiseRejectionEvent', (global, Zone) => { 2129 // handle unhandled promise rejection 2130 function findPromiseRejectionHandler(evtName) { 2131 return function (e) { 2132 const eventTasks = findEventTasks(global, evtName); 2133 eventTasks.forEach(eventTask => { 2134 // windows has added unhandledrejection event listener 2135 // trigger the event listener 2136 const PromiseRejectionEvent = global['PromiseRejectionEvent']; 2137 if (PromiseRejectionEvent) { 2138 const evt = new PromiseRejectionEvent(evtName, { 2139 promise: e.promise, 2140 reason: e.rejection 2141 }); 2142 eventTask.invoke(evt); 2143 } 2144 }); 2145 }; 2146 } 2147 if (global['PromiseRejectionEvent']) { 2148 Zone[zoneSymbol('unhandledPromiseRejectionHandler')] = findPromiseRejectionHandler('unhandledrejection'); 2149 Zone[zoneSymbol('rejectionHandledHandler')] = findPromiseRejectionHandler('rejectionhandled'); 2150 } 2151 }); 2152 Zone.__load_patch('queueMicrotask', (global, Zone, api) => { 2153 patchQueueMicrotask(global, api); 2154 }); 2155} 2156function patchPromise(Zone) { 2157 Zone.__load_patch('ZoneAwarePromise', (global, Zone, api) => { 2158 const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 2159 const ObjectDefineProperty = Object.defineProperty; 2160 function readableObjectToString(obj) { 2161 if (obj && obj.toString === Object.prototype.toString) { 2162 const className = obj.constructor && obj.constructor.name; 2163 return (className ? className : '') + ': ' + JSON.stringify(obj); 2164 } 2165 return obj ? obj.toString() : Object.prototype.toString.call(obj); 2166 } 2167 const __symbol__ = api.symbol; 2168 const _uncaughtPromiseErrors = []; 2169 const isDisableWrappingUncaughtPromiseRejection = global[__symbol__('DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION')] !== false; 2170 const symbolPromise = __symbol__('Promise'); 2171 const symbolThen = __symbol__('then'); 2172 const creationTrace = '__creationTrace__'; 2173 api.onUnhandledError = e => { 2174 if (api.showUncaughtError()) { 2175 const rejection = e && e.rejection; 2176 if (rejection) { 2177 console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); 2178 } else { 2179 console.error(e); 2180 } 2181 } 2182 }; 2183 api.microtaskDrainDone = () => { 2184 while (_uncaughtPromiseErrors.length) { 2185 const uncaughtPromiseError = _uncaughtPromiseErrors.shift(); 2186 try { 2187 uncaughtPromiseError.zone.runGuarded(() => { 2188 if (uncaughtPromiseError.throwOriginal) { 2189 throw uncaughtPromiseError.rejection; 2190 } 2191 throw uncaughtPromiseError; 2192 }); 2193 } catch (error) { 2194 handleUnhandledRejection(error); 2195 } 2196 } 2197 }; 2198 const UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); 2199 function handleUnhandledRejection(e) { 2200 api.onUnhandledError(e); 2201 try { 2202 const handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; 2203 if (typeof handler === 'function') { 2204 handler.call(this, e); 2205 } 2206 } catch (err) {} 2207 } 2208 function isThenable(value) { 2209 return value && value.then; 2210 } 2211 function forwardResolution(value) { 2212 return value; 2213 } 2214 function forwardRejection(rejection) { 2215 return ZoneAwarePromise.reject(rejection); 2216 } 2217 const symbolState = __symbol__('state'); 2218 const symbolValue = __symbol__('value'); 2219 const symbolFinally = __symbol__('finally'); 2220 const symbolParentPromiseValue = __symbol__('parentPromiseValue'); 2221 const symbolParentPromiseState = __symbol__('parentPromiseState'); 2222 const source = 'Promise.then'; 2223 const UNRESOLVED = null; 2224 const RESOLVED = true; 2225 const REJECTED = false; 2226 const REJECTED_NO_CATCH = 0; 2227 function makeResolver(promise, state) { 2228 return v => { 2229 try { 2230 resolvePromise(promise, state, v); 2231 } catch (err) { 2232 resolvePromise(promise, false, err); 2233 } 2234 // Do not return value or you will break the Promise spec. 2235 }; 2236 } 2237 const once = function () { 2238 let wasCalled = false; 2239 return function wrapper(wrappedFunction) { 2240 return function () { 2241 if (wasCalled) { 2242 return; 2243 } 2244 wasCalled = true; 2245 wrappedFunction.apply(null, arguments); 2246 }; 2247 }; 2248 }; 2249 const TYPE_ERROR = 'Promise resolved with itself'; 2250 const CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace'); 2251 // Promise Resolution 2252 function resolvePromise(promise, state, value) { 2253 const onceWrapper = once(); 2254 if (promise === value) { 2255 throw new TypeError(TYPE_ERROR); 2256 } 2257 if (promise[symbolState] === UNRESOLVED) { 2258 // should only get value.then once based on promise spec. 2259 let then = null; 2260 try { 2261 if (typeof value === 'object' || typeof value === 'function') { 2262 then = value && value.then; 2263 } 2264 } catch (err) { 2265 onceWrapper(() => { 2266 resolvePromise(promise, false, err); 2267 })(); 2268 return promise; 2269 } 2270 // if (value instanceof ZoneAwarePromise) { 2271 if (state !== REJECTED && value instanceof ZoneAwarePromise && value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && value[symbolState] !== UNRESOLVED) { 2272 clearRejectedNoCatch(value); 2273 resolvePromise(promise, value[symbolState], value[symbolValue]); 2274 } else if (state !== REJECTED && typeof then === 'function') { 2275 try { 2276 then.call(value, onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false))); 2277 } catch (err) { 2278 onceWrapper(() => { 2279 resolvePromise(promise, false, err); 2280 })(); 2281 } 2282 } else { 2283 promise[symbolState] = state; 2284 const queue = promise[symbolValue]; 2285 promise[symbolValue] = value; 2286 if (promise[symbolFinally] === symbolFinally) { 2287 // the promise is generated by Promise.prototype.finally 2288 if (state === RESOLVED) { 2289 // the state is resolved, should ignore the value 2290 // and use parent promise value 2291 promise[symbolState] = promise[symbolParentPromiseState]; 2292 promise[symbolValue] = promise[symbolParentPromiseValue]; 2293 } 2294 } 2295 // record task information in value when error occurs, so we can 2296 // do some additional work such as render longStackTrace 2297 if (state === REJECTED && value instanceof Error) { 2298 // check if longStackTraceZone is here 2299 const trace = Zone.currentTask && Zone.currentTask.data && Zone.currentTask.data[creationTrace]; 2300 if (trace) { 2301 // only keep the long stack trace into error when in longStackTraceZone 2302 ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { 2303 configurable: true, 2304 enumerable: false, 2305 writable: true, 2306 value: trace 2307 }); 2308 } 2309 } 2310 for (let i = 0; i < queue.length;) { 2311 scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); 2312 } 2313 if (queue.length == 0 && state == REJECTED) { 2314 promise[symbolState] = REJECTED_NO_CATCH; 2315 let uncaughtPromiseError = value; 2316 try { 2317 // Here we throws a new Error to print more readable error log 2318 // and if the value is not an error, zone.js builds an `Error` 2319 // Object here to attach the stack information. 2320 throw new Error('Uncaught (in promise): ' + readableObjectToString(value) + (value && value.stack ? '\n' + value.stack : '')); 2321 } catch (err) { 2322 uncaughtPromiseError = err; 2323 } 2324 if (isDisableWrappingUncaughtPromiseRejection) { 2325 // If disable wrapping uncaught promise reject 2326 // use the value instead of wrapping it. 2327 uncaughtPromiseError.throwOriginal = true; 2328 } 2329 uncaughtPromiseError.rejection = value; 2330 uncaughtPromiseError.promise = promise; 2331 uncaughtPromiseError.zone = Zone.current; 2332 uncaughtPromiseError.task = Zone.currentTask; 2333 _uncaughtPromiseErrors.push(uncaughtPromiseError); 2334 api.scheduleMicroTask(); // to make sure that it is running 2335 } 2336 } 2337 } 2338 // Resolving an already resolved promise is a noop. 2339 return promise; 2340 } 2341 const REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); 2342 function clearRejectedNoCatch(promise) { 2343 if (promise[symbolState] === REJECTED_NO_CATCH) { 2344 // if the promise is rejected no catch status 2345 // and queue.length > 0, means there is a error handler 2346 // here to handle the rejected promise, we should trigger 2347 // windows.rejectionhandled eventHandler or nodejs rejectionHandled 2348 // eventHandler 2349 try { 2350 const handler = Zone[REJECTION_HANDLED_HANDLER]; 2351 if (handler && typeof handler === 'function') { 2352 handler.call(this, { 2353 rejection: promise[symbolValue], 2354 promise: promise 2355 }); 2356 } 2357 } catch (err) {} 2358 promise[symbolState] = REJECTED; 2359 for (let i = 0; i < _uncaughtPromiseErrors.length; i++) { 2360 if (promise === _uncaughtPromiseErrors[i].promise) { 2361 _uncaughtPromiseErrors.splice(i, 1); 2362 } 2363 } 2364 } 2365 } 2366 function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { 2367 clearRejectedNoCatch(promise); 2368 const promiseState = promise[symbolState]; 2369 const delegate = promiseState ? typeof onFulfilled === 'function' ? onFulfilled : forwardResolution : typeof onRejected === 'function' ? onRejected : forwardRejection; 2370 zone.scheduleMicroTask(source, () => { 2371 try { 2372 const parentPromiseValue = promise[symbolValue]; 2373 const isFinallyPromise = !!chainPromise && symbolFinally === chainPromise[symbolFinally]; 2374 if (isFinallyPromise) { 2375 // if the promise is generated from finally call, keep parent promise's state and value 2376 chainPromise[symbolParentPromiseValue] = parentPromiseValue; 2377 chainPromise[symbolParentPromiseState] = promiseState; 2378 } 2379 // should not pass value to finally callback 2380 const value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]); 2381 resolvePromise(chainPromise, true, value); 2382 } catch (error) { 2383 // if error occurs, should always return this error 2384 resolvePromise(chainPromise, false, error); 2385 } 2386 }, chainPromise); 2387 } 2388 const ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; 2389 const noop = function () {}; 2390 const AggregateError = global.AggregateError; 2391 class ZoneAwarePromise { 2392 static toString() { 2393 return ZONE_AWARE_PROMISE_TO_STRING; 2394 } 2395 static resolve(value) { 2396 if (value instanceof ZoneAwarePromise) { 2397 return value; 2398 } 2399 return resolvePromise(new this(null), RESOLVED, value); 2400 } 2401 static reject(error) { 2402 return resolvePromise(new this(null), REJECTED, error); 2403 } 2404 static withResolvers() { 2405 const result = {}; 2406 result.promise = new ZoneAwarePromise((res, rej) => { 2407 result.resolve = res; 2408 result.reject = rej; 2409 }); 2410 return result; 2411 } 2412 static any(values) { 2413 if (!values || typeof values[Symbol.iterator] !== 'function') { 2414 return Promise.reject(new AggregateError([], 'All promises were rejected')); 2415 } 2416 const promises = []; 2417 let count = 0; 2418 try { 2419 for (let v of values) { 2420 count++; 2421 promises.push(ZoneAwarePromise.resolve(v)); 2422 } 2423 } catch (err) { 2424 return Promise.reject(new AggregateError([], 'All promises were rejected')); 2425 } 2426 if (count === 0) { 2427 return Promise.reject(new AggregateError([], 'All promises were rejected')); 2428 } 2429 let finished = false; 2430 const errors = []; 2431 return new ZoneAwarePromise((resolve, reject) => { 2432 for (let i = 0; i < promises.length; i++) { 2433 promises[i].then(v => { 2434 if (finished) { 2435 return; 2436 } 2437 finished = true; 2438 resolve(v); 2439 }, err => { 2440 errors.push(err); 2441 count--; 2442 if (count === 0) { 2443 finished = true; 2444 reject(new AggregateError(errors, 'All promises were rejected')); 2445 } 2446 }); 2447 } 2448 }); 2449 } 2450 static race(values) { 2451 let resolve; 2452 let reject; 2453 let promise = new this((res, rej) => { 2454 resolve = res; 2455 reject = rej; 2456 }); 2457 function onResolve(value) { 2458 resolve(value); 2459 } 2460 function onReject(error) { 2461 reject(error); 2462 } 2463 for (let value of values) { 2464 if (!isThenable(value)) { 2465 value = this.resolve(value); 2466 } 2467 value.then(onResolve, onReject); 2468 } 2469 return promise; 2470 } 2471 static all(values) { 2472 return ZoneAwarePromise.allWithCallback(values); 2473 } 2474 static allSettled(values) { 2475 const P = this && this.prototype instanceof ZoneAwarePromise ? this : ZoneAwarePromise; 2476 return P.allWithCallback(values, { 2477 thenCallback: value => ({ 2478 status: 'fulfilled', 2479 value 2480 }), 2481 errorCallback: err => ({ 2482 status: 'rejected', 2483 reason: err 2484 }) 2485 }); 2486 } 2487 static allWithCallback(values, callback) { 2488 let resolve; 2489 let reject; 2490 let promise = new this((res, rej) => { 2491 resolve = res; 2492 reject = rej; 2493 }); 2494 // Start at 2 to prevent prematurely resolving if .then is called immediately. 2495 let unresolvedCount = 2; 2496 let valueIndex = 0; 2497 const resolvedValues = []; 2498 for (let value of values) { 2499 if (!isThenable(value)) { 2500 value = this.resolve(value); 2501 } 2502 const curValueIndex = valueIndex; 2503 try { 2504 value.then(value => { 2505 resolvedValues[curValueIndex] = callback ? callback.thenCallback(value) : value; 2506 unresolvedCount--; 2507 if (unresolvedCount === 0) { 2508 resolve(resolvedValues); 2509 } 2510 }, err => { 2511 if (!callback) { 2512 reject(err); 2513 } else { 2514 resolvedValues[curValueIndex] = callback.errorCallback(err); 2515 unresolvedCount--; 2516 if (unresolvedCount === 0) { 2517 resolve(resolvedValues); 2518 } 2519 } 2520 }); 2521 } catch (thenErr) { 2522 reject(thenErr); 2523 } 2524 unresolvedCount++; 2525 valueIndex++; 2526 } 2527 // Make the unresolvedCount zero-based again. 2528 unresolvedCount -= 2; 2529 if (unresolvedCount === 0) { 2530 resolve(resolvedValues); 2531 } 2532 return promise; 2533 } 2534 constructor(executor) { 2535 const promise = this; 2536 if (!(promise instanceof ZoneAwarePromise)) { 2537 throw new Error('Must be an instanceof Promise.'); 2538 } 2539 promise[symbolState] = UNRESOLVED; 2540 promise[symbolValue] = []; // queue; 2541 try { 2542 const onceWrapper = once(); 2543 executor && executor(onceWrapper(makeResolver(promise, RESOLVED)), onceWrapper(makeResolver(promise, REJECTED))); 2544 } catch (error) { 2545 resolvePromise(promise, false, error); 2546 } 2547 } 2548 get [Symbol.toStringTag]() { 2549 return 'Promise'; 2550 } 2551 get [Symbol.species]() { 2552 return ZoneAwarePromise; 2553 } 2554 then(onFulfilled, onRejected) { 2555 // We must read `Symbol.species` safely because `this` may be anything. For instance, `this` 2556 // may be an object without a prototype (created through `Object.create(null)`); thus 2557 // `this.constructor` will be undefined. One of the use cases is SystemJS creating 2558 // prototype-less objects (modules) via `Object.create(null)`. The SystemJS creates an empty 2559 // object and copies promise properties into that object (within the `getOrCreateLoad` 2560 // function). The zone.js then checks if the resolved value has the `then` method and 2561 // invokes it with the `value` context. Otherwise, this will throw an error: `TypeError: 2562 // Cannot read properties of undefined (reading 'Symbol(Symbol.species)')`. 2563 let C = this.constructor?.[Symbol.species]; 2564 if (!C || typeof C !== 'function') { 2565 C = this.constructor || ZoneAwarePromise; 2566 } 2567 const chainPromise = new C(noop); 2568 const zone = Zone.current; 2569 if (this[symbolState] == UNRESOLVED) { 2570 this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); 2571 } else { 2572 scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); 2573 } 2574 return chainPromise; 2575 } 2576 catch(onRejected) { 2577 return this.then(null, onRejected); 2578 } 2579 finally(onFinally) { 2580 // See comment on the call to `then` about why thee `Symbol.species` is safely accessed. 2581 let C = this.constructor?.[Symbol.species]; 2582 if (!C || typeof C !== 'function') { 2583 C = ZoneAwarePromise; 2584 } 2585 const chainPromise = new C(noop); 2586 chainPromise[symbolFinally] = symbolFinally; 2587 const zone = Zone.current; 2588 if (this[symbolState] == UNRESOLVED) { 2589 this[symbolValue].push(zone, chainPromise, onFinally, onFinally); 2590 } else { 2591 scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally); 2592 } 2593 return chainPromise; 2594 } 2595 } 2596 // Protect against aggressive optimizers dropping seemingly unused properties. 2597 // E.g. Closure Compiler in advanced mode. 2598 ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; 2599 ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; 2600 ZoneAwarePromise['race'] = ZoneAwarePromise.race; 2601 ZoneAwarePromise['all'] = ZoneAwarePromise.all; 2602 const NativePromise = global[symbolPromise] = global['Promise']; 2603 global['Promise'] = ZoneAwarePromise; 2604 const symbolThenPatched = __symbol__('thenPatched'); 2605 function patchThen(Ctor) { 2606 const proto = Ctor.prototype; 2607 const prop = ObjectGetOwnPropertyDescriptor(proto, 'then'); 2608 if (prop && (prop.writable === false || !prop.configurable)) { 2609 // check Ctor.prototype.then propertyDescriptor is writable or not 2610 // in meteor env, writable is false, we should ignore such case 2611 return; 2612 } 2613 const originalThen = proto.then; 2614 // Keep a reference to the original method. 2615 proto[symbolThen] = originalThen; 2616 Ctor.prototype.then = function (onResolve, onReject) { 2617 const wrapped = new ZoneAwarePromise((resolve, reject) => { 2618 originalThen.call(this, resolve, reject); 2619 }); 2620 return wrapped.then(onResolve, onReject); 2621 }; 2622 Ctor[symbolThenPatched] = true; 2623 } 2624 api.patchThen = patchThen; 2625 function zoneify(fn) { 2626 return function (self, args) { 2627 let resultPromise = fn.apply(self, args); 2628 if (resultPromise instanceof ZoneAwarePromise) { 2629 return resultPromise; 2630 } 2631 let ctor = resultPromise.constructor; 2632 if (!ctor[symbolThenPatched]) { 2633 patchThen(ctor); 2634 } 2635 return resultPromise; 2636 }; 2637 } 2638 if (NativePromise) { 2639 patchThen(NativePromise); 2640 patchMethod(global, 'fetch', delegate => zoneify(delegate)); 2641 } 2642 // This is not part of public API, but it is useful for tests, so we expose it. 2643 Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; 2644 return ZoneAwarePromise; 2645 }); 2646} 2647function patchToString(Zone) { 2648 // override Function.prototype.toString to make zone.js patched function 2649 // look like native function 2650 Zone.__load_patch('toString', global => { 2651 // patch Func.prototype.toString to let them look like native 2652 const originalFunctionToString = Function.prototype.toString; 2653 const ORIGINAL_DELEGATE_SYMBOL = zoneSymbol('OriginalDelegate'); 2654 const PROMISE_SYMBOL = zoneSymbol('Promise'); 2655 const ERROR_SYMBOL = zoneSymbol('Error'); 2656 const newFunctionToString = function toString() { 2657 if (typeof this === 'function') { 2658 const originalDelegate = this[ORIGINAL_DELEGATE_SYMBOL]; 2659 if (originalDelegate) { 2660 if (typeof originalDelegate === 'function') { 2661 return originalFunctionToString.call(originalDelegate); 2662 } else { 2663 return Object.prototype.toString.call(originalDelegate); 2664 } 2665 } 2666 if (this === Promise) { 2667 const nativePromise = global[PROMISE_SYMBOL]; 2668 if (nativePromise) { 2669 return originalFunctionToString.call(nativePromise); 2670 } 2671 } 2672 if (this === Error) { 2673 const nativeError = global[ERROR_SYMBOL]; 2674 if (nativeError) { 2675 return originalFunctionToString.call(nativeError); 2676 } 2677 } 2678 } 2679 return originalFunctionToString.call(this); 2680 }; 2681 newFunctionToString[ORIGINAL_DELEGATE_SYMBOL] = originalFunctionToString; 2682 Function.prototype.toString = newFunctionToString; 2683 // patch Object.prototype.toString to let them look like native 2684 const originalObjectToString = Object.prototype.toString; 2685 const PROMISE_OBJECT_TO_STRING = '[object Promise]'; 2686 Object.prototype.toString = function () { 2687 if (typeof Promise === 'function' && this instanceof Promise) { 2688 return PROMISE_OBJECT_TO_STRING; 2689 } 2690 return originalObjectToString.call(this); 2691 }; 2692 }); 2693} 2694function patchCallbacks(api, target, targetName, method, callbacks) { 2695 const symbol = Zone.__symbol__(method); 2696 if (target[symbol]) { 2697 return; 2698 } 2699 const nativeDelegate = target[symbol] = target[method]; 2700 target[method] = function (name, opts, options) { 2701 if (opts && opts.prototype) { 2702 callbacks.forEach(function (callback) { 2703 const source = `${targetName}.${method}::` + callback; 2704 const prototype = opts.prototype; 2705 // Note: the `patchCallbacks` is used for patching the `document.registerElement` and 2706 // `customElements.define`. We explicitly wrap the patching code into try-catch since 2707 // callbacks may be already patched by other web components frameworks (e.g. LWC
2707), and they 2708 // make those properties non-writable. This means that patching callback will throw an error 2709 // `cannot assign to read-only property`. See this code as an example: 2710 // https://github.com/salesforce/lwc/blob/master/packages/@lwc/engine-core/src/framework/base-bridge-element.ts#L180-L186 2711 // We don't want to stop the application rendering if we couldn't patch some 2712 // callback, e.g. `attributeChangedCallback`. 2713 try { 2714 if (prototype.hasOwnProperty(callback)) { 2715 const descriptor = api.ObjectGetOwnPropertyDescriptor(prototype, callback); 2716 if (descriptor && descriptor.value) { 2717 descriptor.value = api.wrapWithCurrentZone(descriptor.value, source); 2718 api._redefineProperty(opts.prototype, callback, descriptor); 2719 } else if (prototype[callback]) { 2720 prototype[callback] = api.wrapWithCurrentZone(prototype[callback], source); 2721 } 2722 } else if (prototype[callback]) { 2723 prototype[callback] = api.wrapWithCurrentZone(prototype[callback], source); 2724 } 2725 } catch {
2726 // Note: we leave the catch block empty since there's no way to handle the error related 2727 // to non-writable property. 2728 } 2729 }); 2730 } 2731 return nativeDelegate.call(target, name, opts, options); 2732 }; 2733 api.attachOriginToPatched(target[method], nativeDelegate); 2734} 2735function patchUtil(Zone) { 2736 Zone.__load_patch('util', (global, Zone, api) => { 2737 // Collect native event names by looking at properties 2738 // on the global namespace, e.g. 'onclick'. 2739 const eventNames = getOnEventNames(global); 2740 api.patchOnProperties = patchOnProperties; 2741 api.patchMethod = patchMethod; 2742 api.bindArguments = bindArguments; 2743 api.patchMacroTask = patchMacroTask; 2744 // In earlier version of zone.js (<0.9.0), we use env name `__zone_symbol__BLACK_LISTED_EVENTS` 2745 // to define which events will not be patched by `Zone.js`. In newer version (>=0.9.0), we 2746 // change the env name to `__zone_symbol__UNPATCHED_EVENTS` to keep the name consistent with 2747 // angular repo. The `__zone_symbol__BLACK_LISTED_EVENTS` is deprecated, but it is still be 2748 // supported for backwards compatibility. 2749 const SYMBOL_BLACK_LISTED_EVENTS = Zone.__symbol__('BLACK_LISTED_EVENTS'); 2750 const SYMBOL_UNPATCHED_EVENTS = Zone.__symbol__('UNPATCHED_EVENTS'); 2751 if (global[SYMBOL_UNPATCHED_EVENTS]) { 2752 global[SYMBOL_BLACK_LISTED_EVENTS] = global[SYMBOL_UNPATCHED_EVENTS]; 2753 } 2754 if (global[SYMBOL_BLACK_LISTED_EVENTS]) { 2755 Zone[SYMBOL_BLACK_LISTED_EVENTS] = Zone[SYMBOL_UNPATCHED_EVENTS] = global[SYMBOL_BLACK_LISTED_EVENTS]; 2756 } 2757 api.patchEventPrototype = patchEventPrototype; 2758 api.patchEventTarget = patchEventTarget; 2759 api.isIEOrEdge = isIEOrEdge; 2760 api.ObjectDefineProperty = ObjectDefineProperty; 2761 api.ObjectGetOwnPropertyDescriptor = ObjectGetOwnPropertyDescriptor; 2762 api.ObjectCreate = ObjectCreate; 2763 api.ArraySlice = ArraySlice; 2764 api.patchClass = patchClass; 2765 api.wrapWithCurrentZone = wrapWithCurrentZone; 2766 api.filterProperties = filterProperties; 2767 api.attachOriginToPatched = attachOriginToPatched; 2768 api._redefineProperty = Object.defineProperty; 2769 api.patchCallbacks = patchCallbacks; 2770 api.getGlobalObjects = () => ({ 2771 globalSources, 2772 zoneSymbolEventNames, 2773 eventNames, 2774 isBrowser, 2775 isMix, 2776 isNode, 2777 TRUE_STR, 2778 FALSE_STR, 2779 ZONE_SYMBOL_PREFIX, 2780 ADD_EVENT_LISTENER_STR, 2781 REMOVE_EVENT_LISTENER_STR 2782 }); 2783 }); 2784} 2785function patchCommon(Zone) { 2786 patchPromise(Zone); 2787 patchToString(Zone); 2788 patchUtil(Zone); 2789} 2790const Zone$1 = loadZone(); 2791patchCommon(Zone$1); 2792patchBrowser(Zone$1); 2793 2794/***/ }), 2795 2796/***/ 45568: 2797/*!**************************************************************!*\ 2798 !*** ./node_modules/@angular/compiler/fesm2022/compiler.mjs ***! 2799 \**************************************************************/ 2800/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 2801 2802__webpack_require__.r(__webpack_exports__); 2803/* harmony export */ __webpack_require__.d(__webpack_exports__, { 2804/* harmony export */ AST: () => (/* binding */ AST), 2805/* harmony export */ ASTWithName: () => (/* binding */ ASTWithName), 2806/* harmony export */ ASTWithSource: () => (/* binding */ ASTWithSource), 2807/* harmony export */ AbsoluteSourceSpan: () => (/* binding */ AbsoluteSourceSpan), 2808/* harmony export */ ArrayType: () => (/* binding */ ArrayType), 2809/* harmony export */ ArrowFunctionExpr: () => (/* binding */ ArrowFunctionExpr), 2810/* harmony export */ AstMemoryEfficientTransformer: () => (/* binding */ AstMemoryEfficientTransformer), 2811/* harmony export */ AstTransformer: () => (/* binding */ AstTransformer), 2812/* harmony export */ Attribute: () => (/* binding */ Attribute), 2813/* harmony export */ Binary: () => (/* binding */ Binary), 2814/* harmony export */ BinaryOperator: () => (/* binding */ BinaryOperator), 2815/* harmony export */ BinaryOperatorExpr: () => (/* binding */ BinaryOperatorExpr), 2816/* harmony export */ BindingPipe: () => (/* binding */ BindingPipe), 2817/* harmony export */ Block: () => (/* binding */ Block), 2818/* harmony export */ BlockParameter: () => (/* binding */ BlockParameter), 2819/* harmony export */ BoundElementProperty: () => (/* binding */ BoundElementProperty), 2820/* harmony export */ BuiltinType: () => (/* binding */ BuiltinType), 2821/* harmony export */ BuiltinTypeName: () => (/* binding */ BuiltinTypeName), 2822/* harmony export */ CUSTOM_ELEMENTS_SCHEMA: () => (/* binding */ CUSTOM_ELEMENTS_SCHEMA), 2823/* harmony export */ Call: () => (/* binding */ Call), 2824/* harmony export */ Chain: () => (/* binding */ Chain), 2825/* harmony export */ ChangeDetectionStrategy: () => (/* binding */ ChangeDetectionStrategy), 2826/* harmony export */ CommaExpr: () => (/* binding */ CommaExpr), 2827/* harmony export */ Comment: () => (/* binding */ Comment), 2828/* harmony export */ CompilerConfig: () => (/* binding */ CompilerConfig), 2829/* harmony export */ Conditional: () => (/* binding */ Conditional), 2830/* harmony export */ ConditionalExpr: () => (/* binding */ ConditionalExpr), 2831/* harmony export */ ConstantPool: () => (/* binding */ ConstantPool), 2832/* harmony export */ CssSelector: () => (/* binding */ CssSelector), 2833/* harmony export */ DEFAULT_INTERPOLATION_CONFIG: () => (/* binding */ DEFAULT_INTERPOLATION_CONFIG), 2834/* harmony export */ DYNAMIC_TYPE: () => (/* binding */ DYNAMIC_TYPE), 2835/* harmony export */ DeclareFunctionStmt: () => (/* binding */ DeclareFunctionStmt), 2836/* harmony export */ DeclareVarStmt: () => (/* binding */ DeclareVarStmt), 2837/* harmony export */ DomElementSchemaRegistry: () => (/* binding */ DomElementSchemaRegistry), 2838/* harmony export */ DynamicImportExpr: () => (/* binding */ DynamicImportExpr), 2839/* harmony export */ EOF: () => (/* binding */ EOF), 2840/* harmony export */ Element: () => (/* binding */ Element), 2841/* harmony export */ ElementSchemaRegistry: () => (/* binding */ ElementSchemaRegistry), 2842/* harmony export */ EmitterVisitorContext: () => (/* binding */ EmitterVisitorContext), 2843/* harmony export */ EmptyExpr: () => (/* binding */ EmptyExpr$1), 2844/* harmony export */ Expansion: () => (/* binding */ Expansion), 2845/* harmony export */ ExpansionCase: () => (/* binding */ ExpansionCase), 2846/* harmony export */ Expression: () => (/* binding */ Expression), 2847/* harmony export */ ExpressionBinding: () => (/* binding */ ExpressionBinding),
2848/* harmony export */ ExpressionStatement: () => (/* binding */ ExpressionStatement), 2849/* harmony export */ ExpressionType: () => (/* binding */ ExpressionType), 2850/* harmony export */ ExternalExpr: () => (/* binding */ ExternalExpr), 2851/* harmony export */ ExternalReference: () => (/* binding */ ExternalReference), 2852/* harmony export */ FactoryTarget: () => (/* binding */ FactoryTarget$1), 2853/* harmony export */ FunctionExpr: () => (/* binding */ FunctionExpr), 2854/* harmony export */ HtmlParser: () => (/* binding */ HtmlParser), 2855/* harmony export */ HtmlTagDefinition: () => (/* binding */ HtmlTagDefinition), 2856/* harmony export */ I18NHtmlParser: () => (/* binding */ I18NHtmlParser), 2857/* harmony export */ IfStmt: () => (/* binding */ IfStmt), 2858/* harmony export */ ImplicitReceiver: () => (/* binding */ ImplicitReceiver), 2859/* harmony export */ InstantiateExpr: () => (/* binding */ InstantiateExpr), 2860/* harmony export */ Interpolation: () => (/* binding */ Interpolation$1), 2861/* harmony export */ InterpolationConfig: () => (/* binding */ InterpolationConfig), 2862/* harmony export */ InvokeFunctionExpr: () => (/* binding */ InvokeFunctionExpr), 2863/* harmony export */ JSDocComment: () => (/* binding */ JSDocComment), 2864/* harmony export */ JitEvaluator: () => (/* binding */ JitEvaluator), 2865/* harmony export */ KeyedRead: () => (/* binding */ KeyedRead), 2866/* harmony export */ KeyedWrite: () => (/* binding */ KeyedWrite), 2867/* harmony export */ LeadingComment: () => (/* binding */ LeadingComment), 2868/* harmony export */ Lexer: () => (/* binding */ Lexer), 2869/* harmony export */ LiteralArray: () => (/* binding */ LiteralArray), 2870/* harmony export */ LiteralArrayExpr: () => (/* binding */ LiteralArrayExpr), 2871/* harmony export */ LiteralExpr: () => (/* binding */ LiteralExpr), 2872/* harmony export */ LiteralMap: () => (/* binding */ LiteralMap), 2873/* harmony export */ LiteralMapExpr: () => (/* binding */ LiteralMapExpr), 2874/* harmony export */ LiteralPrimitive: () => (/* binding */ LiteralPrimitive), 2875/* harmony export */ LocalizedString: () => (/* binding */ LocalizedString), 2876/* harmony export */ MapType: () => (/* binding */ MapType), 2877/* harmony export */ MessageBundle: () => (/* binding */ MessageBundle), 2878/* harmony export */ NONE_TYPE: () => (/* binding */ NONE_TYPE), 2879/* harmony export */ NO_ERRORS_SCHEMA: () => (/* binding */ NO_ERRORS_SCHEMA), 2880/* harmony export */ NodeWithI18n: () => (/* binding */ NodeWithI18n), 2881/* harmony export */ NonNullAssert: () => (/* binding */ NonNullAssert), 2882/* harmony export */ NotExpr: () => (/* binding */ NotExpr), 2883/* harmony export */ ParseError: () => (/* binding */ ParseError), 2884/* harmony export */ ParseErrorLevel: () => (/* binding */ ParseErrorLevel), 2885/* harmony export */ ParseLocation: () => (/* binding */ ParseLocation), 2886/* harmony export */ ParseSourceFile: () => (/* binding */ ParseSourceFile), 2887/* harmony export */ ParseSourceSpan: () => (/* binding */ ParseSourceSpan), 2888/* harmony export */ ParseSpan: () => (/* binding */ ParseSpan), 2889/* harmony export */ ParseTreeResult: () => (/* binding */ ParseTreeResult), 2890/* harmony export */ ParsedEvent: () => (/* binding */ ParsedEvent), 2891/* harmony export */ ParsedProperty: () => (/* binding */ ParsedProperty), 2892/* harmony export */ ParsedPropertyType: () => (/* binding */ ParsedPropertyType), 2893/* harmony export */ ParsedVariable: () => (/* binding */ ParsedVariable), 2894/* harmony export */ Parser: () => (/* binding */ Parser$1), 2895/* harmony export */ ParserError: () => (/* binding */ ParserError), 2896/* harmony export */ PrefixNot: () => (/* binding */ PrefixNot), 2897/* harmony export */ PropertyRead: () => (/* binding */ PropertyRead), 2898/* harmony export */ PropertyWrite: () => (/* binding */ PropertyWrite), 2899/* harmony export */ R3BoundTarget: () => (/* binding */ R3BoundTarget), 2900/* harmony export */ R3Identifiers: () => (/* binding */ Identifiers), 2901/* harmony export */ R3NgModuleMetadataKind: () => (/* binding */ R3NgModuleMetadataKind), 2902/* harmony export */ R3SelectorScopeMode: () => (/* binding */ R3SelectorScopeMode), 2903/* harmony export */ R3TargetBinder: () => (/* binding */ R3TargetBinder), 2904/* harmony export */ R3TemplateDependencyKind: () => (/* binding */ R3TemplateDependencyKind), 2905/* harmony export */ ReadKeyExpr: () => (/* binding */ ReadKeyExpr),
2906/* harmony export */ ReadPropExpr: () => (/* binding */ ReadPropExpr), 2907/* harmony export */ ReadVarExpr: () => (/* binding */ ReadVarExpr), 2908/* harmony export */ RecursiveAstVisitor: () => (/* binding */ RecursiveAstVisitor), 2909/* harmony export */ RecursiveVisitor: () => (/* binding */ RecursiveVisitor), 2910/* harmony export */ ResourceLoader: () => (/* binding */ ResourceLoader), 2911/* harmony export */ ReturnStatement: () => (/* binding */ ReturnStatement), 2912/* harmony export */ STRING_TYPE: () => (/* binding */ STRING_TYPE), 2913/* harmony export */ SafeCall: () => (/* binding */ SafeCall), 2914/* harmony export */ SafeKeyedRead: () => (/* binding */ SafeKeyedRead), 2915/* harmony export */ SafePropertyRead: () => (/* binding */ SafePropertyRead), 2916/* harmony export */ SelectorContext: () => (/* binding */ SelectorContext), 2917/* harmony export */ SelectorListContext: () => (/* binding */ SelectorListContext), 2918/* harmony export */ SelectorMatcher: () => (/* binding */ SelectorMatcher), 2919/* harmony export */ Serializer: () => (/* binding */ Serializer), 2920/* harmony export */ SplitInterpolation: () => (/* binding */ SplitInterpolation), 2921/* harmony export */ Statement: () => (/* binding */ Statement), 2922/* harmony export */ StmtModifier: () => (/* binding */ StmtModifier), 2923/* harmony export */ TagContentType: () => (/* binding */ TagContentType), 2924/* harmony export */ TaggedTemplateExpr: () => (/* binding */ TaggedTemplateExpr), 2925/* harmony export */ TemplateBindingParseResult: () => (/* binding */ TemplateBindingParseResult), 2926/* harmony export */ TemplateLiteral: () => (/* binding */ TemplateLiteral), 2927/* harmony export */ TemplateLiteralElement: () => (/* binding */ TemplateLiteralElement), 2928/* harmony export */ Text: () => (/* binding */ Text), 2929/* harmony export */ ThisReceiver: () => (/* binding */ ThisReceiver), 2930/* harmony export */ TmplAstBlockNode: () => (/* binding */ BlockNode), 2931/* harmony export */ TmplAstBoundAttribute: () => (/* binding */ BoundAttribute), 2932/* harmony export */ TmplAstBoundDeferredTrigger: () => (/* binding */ BoundDeferredTrigger), 2933/* harmony export */ TmplAstBoundEvent: () => (/* binding */ BoundEvent), 2934/* harmony export */ TmplAstBoundText: () => (/* binding */ BoundText), 2935/* harmony export */ TmplAstContent: () => (/* binding */ Content), 2936/* harmony export */ TmplAstDeferredBlock: () => (/* binding */ DeferredBlock), 2937/* harmony export */ TmplAstDeferredBlockError: () => (/* binding */ DeferredBlockError), 2938/* harmony export */ TmplAstDeferredBlockLoading: () => (/* binding */ DeferredBlockLoading), 2939/* harmony export */ TmplAstDeferredBlockPlaceholder: () => (/* binding */ DeferredBlockPlaceholder), 2940/* harmony export */ TmplAstDeferredTrigger: () => (/* binding */ DeferredTrigger), 2941/* harmony export */ TmplAstElement: () => (/* binding */ Element$1), 2942/* harmony export */ TmplAstForLoopBlock: () => (/* binding */ ForLoopBlock), 2943/* harmony export */ TmplAstForLoopBlockEmpty: () => (/* binding */ ForLoopBlockEmpty), 2944/* harmony export */ TmplAstHoverDeferredTrigger: () => (/* binding */ HoverDeferredTrigger), 2945/* harmony export */ TmplAstIcu: () => (/* binding */ Icu$1), 2946/* harmony export */ TmplAstIdleDeferredTrigger: () => (/* binding */ IdleDeferredTrigger), 2947/* harmony export */ TmplAstIfBlock: () => (/* binding */ IfBlock), 2948/* harmony export */ TmplAstIfBlockBranch: () => (/* binding */ IfBlockBranch), 2949/* harmony export */ TmplAstImmediateDeferredTrigger: () => (/* binding */ ImmediateDeferredTrigger), 2950/* harmony export */ TmplAstInteractionDeferredTrigger: () => (/* binding */ InteractionDeferredTrigger), 2951/* harmony export */ TmplAstRecursiveVisitor: () => (/* binding */ RecursiveVisitor$1), 2952/* harmony export */ TmplAstReference: () => (/* binding */ Reference), 2953/* harmony export */ TmplAstSwitchBlock: () => (/* binding */ SwitchBlock), 2954/* harmony export */ TmplAstSwitchBlockCase: () => (/* binding */ SwitchBlockCase), 2955/* harmony export */ TmplAstTemplate: () => (/* binding */ Template), 2956/* harmony export */ TmplAstText: () => (/* binding */ Text$3), 2957/* harmony export */ TmplAstTextAttribute: () => (/* binding */ TextAttribute), 2958/* harmony export */ TmplAstTimerDeferredTrigger: () => (/* binding */ TimerDeferredTrigger), 2959/* harmony export */ TmplAstUnknownBlock: () => (/* binding */ UnknownBlock), 2960/* harmony export */ TmplAstVariable: () => (/* binding */ Variable), 2961/* harmony export */ TmplAstViewportDeferredTrigger: () => (/* binding */ ViewportDeferredTrigger), 2962/* harmony export */ Token: () => (/* binding */ Token), 2963/* harmony export */ TokenType: () => (/* binding */ TokenType), 2964/* harmony export */ TransplantedType: () => (/* binding */ TransplantedType), 2965/* harmony export */ TreeError: () => (/* binding */ TreeError), 2966/* harmony export */ Type: () => (/* binding */ Type), 2967/* harmony export */ TypeModifier: () => (/* binding */ TypeModifier), 2968/* harmony export */ TypeofExpr: () => (/* binding */ TypeofExpr), 2969/* harmony export */ Unary: () => (/* binding */ Unary), 2970/* harmony export */ UnaryOperator: () => (/* binding */ UnaryOperator), 2971/* harmony export */ UnaryOperatorExpr: () => (/* binding */ UnaryOperatorExpr), 2972/* harmony export */ VERSION: () => (/* binding */ VERSION), 2973/* harmony export */ VariableBinding: () => (/* binding */ VariableBinding),
2974/* harmony export */ Version: () => (/* binding */ Version), 2975/* harmony export */ ViewEncapsulation: () => (/* binding */ ViewEncapsulation), 2976/* harmony export */ WrappedNodeExpr: () => (/* binding */ WrappedNodeExpr), 2977/* harmony export */ WriteKeyExpr: () => (/* binding */ WriteKeyExpr), 2978/* harmony export */ WritePropExpr: () => (/* binding */ WritePropExpr), 2979/* harmony export */ WriteVarExpr: () => (/* binding */ WriteVarExpr), 2980/* harmony export */ Xliff: () => (/* binding */ Xliff), 2981/* harmony export */ Xliff2: () => (/* binding */ Xliff2), 2982/* harmony export */ Xmb: () => (/* binding */ Xmb), 2983/* harmony export */ XmlParser: () => (/* binding */ XmlParser), 2984/* harmony export */ Xtb: () => (/* binding */ Xtb), 2985/* harmony export */ compileClassDebugInfo: () => (/* binding */ compileClassDebugInfo), 2986/* harmony export */ compileClassMetadata: () => (/* binding */ compileClassMetadata), 2987/* harmony export */ compileComponentClassMetadata: () => (/* binding */ compileComponentClassMetadata), 2988/* harmony export */ compileComponentFromMetadata: () => (/* binding */ compileComponentFromMetadata), 2989/* harmony export */ compileDeclareClassMetadata: () => (/* binding */ compileDeclareClassMetadata), 2990/* harmony export */ compileDeclareComponentFromMetadata: () => (/* binding */ compileDeclareComponentFromMetadata), 2991/* harmony export */ compileDeclareDirectiveFromMetadata: () => (/* binding */ compileDeclareDirectiveFromMetadata), 2992/* harmony export */ compileDeclareFactoryFunction: () => (/* binding */ compileDeclareFactoryFunction), 2993/* harmony export */ compileDeclareInjectableFromMetadata: () => (/* binding */ compileDeclareInjectableFromMetadata), 2994/* harmony export */ compileDeclareInjectorFromMetadata: () => (/* binding */ compileDeclareInjectorFromMetadata), 2995/* harmony export */ compileDeclareNgModuleFromMetadata: () => (/* binding */ compileDeclareNgModuleFromMetadata), 2996/* harmony export */ compileDeclarePipeFromMetadata: () => (/* binding */ compileDeclarePipeFromMetadata), 2997/* harmony export */ compileDirectiveFromMetadata: () => (/* binding */ compileDirectiveFromMetadata), 2998/* harmony export */ compileFactoryFunction: () => (/* binding */ compileFactoryFunction), 2999/* harmony export */ compileInjectable: () => (/* binding */ compileInjectable), 3000/* harmony export */ compileInjector: () => (/* binding */ compileInjector), 3001/* harmony export */ compileNgModule: () => (/* binding */ compileNgModule), 3002/* harmony export */ compilePipeFromMetadata: () => (/* binding */ compilePipeFromMetadata), 3003/* harmony export */ computeMsgId: () => (/* binding */ computeMsgId), 3004/* harmony export */ core: () => (/* binding */ core), 3005/* harmony export */ createCssSelectorFromNode: () => (/* binding */ createCssSelectorFromNode), 3006/* harmony export */ createInjectableType: () => (/* binding */ createInjectableType), 3007/* harmony export */ createMayBeForwardRefExpression: () => (/* binding */ createMayBeForwardRefExpression), 3008/* harmony export */ devOnlyGuardedExpression: () => (/* binding */ devOnlyGuardedExpression), 3009/* harmony export */ emitDistinctChangesOnlyDefaultValue: () => (/* binding */ emitDistinctChangesOnlyDefaultValue), 3010/* harmony export */ encapsulateStyle: () => (/* binding */ encapsulateStyle), 3011/* harmony export */ getHtmlTagDefinition: () => (/* binding */ getHtmlTagDefinition), 3012/* harmony export */ getNsPrefix: () => (/* binding */ getNsPrefix), 3013/* harmony export */ getSafePropertyAccessString: () => (/* binding */ getSafePropertyAccessString), 3014/* harmony export */ identifierName: () => (/* binding */ identifierName), 3015/* harmony export */ isIdentifier: () => (/* binding */ isIdentifier), 3016/* harmony export */ isNgContainer: () => (/* binding */ isNgContainer), 3017/* harmony export */ isNgContent: () => (/* binding */ isNgContent), 3018/* harmony export */ isNgTemplate: () => (/* binding */ isNgTemplate), 3019/* harmony export */ jsDocComment: () => (/* binding */ jsDocComment), 3020/* harmony export */ leadingComment: () => (/* binding */ leadingComment), 3021/* harmony export */ literal: () => (/* binding */ literal), 3022/* harmony export */ literalMap: () => (/* binding */ literalMap), 3023/* harmony export */ makeBindingParser: () => (/* binding */ makeBindingParser), 3024/* harmony export */ mergeNsAndName: () => (/* binding */ mergeNsAndName), 3025/* harmony export */ outputAst: () => (/* binding */ output_ast),
3026/* harmony export */ parseHostBindings: () => (/* binding */ parseHostBindings), 3027/* harmony export */ parseTemplate: () => (/* binding */ parseTemplate), 3028/* harmony export */ preserveWhitespacesDefault: () => (/* binding */ preserveWhitespacesDefault), 3029/* harmony export */ publishFacade: () => (/* binding */ publishFacade), 3030/* harmony export */ r3JitTypeSourceSpan: () => (/* binding */ r3JitTypeSourceSpan), 3031/* harmony export */ sanitizeIdentifier: () => (/* binding */ sanitizeIdentifier), 3032/* harmony export */ splitNsName: () => (/* binding */ splitNsName), 3033/* harmony export */ tmplAstVisitAll: () => (/* binding */ visitAll$1), 3034/* harmony export */ verifyHostBindings: () => (/* binding */ verifyHostBindings), 3035/* harmony export */ visitAll: () => (/* binding */ visitAll) 3036/* harmony export */ }); 3037/** 3038 * @license Angular v17.3.9 3039 * (c) 2010-2024 Google LLC. https://angular.io/ 3040 * License: MIT 3041 */ 3042 3043const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + 3044// 1: ":not(" 3045'(([\\.\\#]?)[-\\w]+)|' + 3046// 2: "tag"; 3: "."/"#"; 3047// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range 3048// 4: attribute; 5: attribute_string; 6: attribute_value 3049'(?:\\[([-.\\w*\\\\$]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + 3050// "[name]", "[name=value]", 3051// "[name="value"]", 3052// "[name='value']" 3053'(\\))|' + 3054// 7: ")" 3055'(\\s*,\\s*)', 3056// 8: "," 3057'g'); 3058/** 3059 * A css selector contains an element name, 3060 * css classes and attribute/value pairs with the purpose 3061 * of selecting subsets out of them. 3062 */ 3063class CssSelector { 3064 constructor() { 3065 this.element = null; 3066 this.classNames = []; 3067 /** 3068 * The selectors are encoded in pairs where: 3069 * - even locations are attribute names 3070 * - odd locations are attribute values. 3071 * 3072 * Example: 3073 * Selector: `[key1=value1][key2]` would parse to: 3074 * ``` 3075 * ['key1', 'value1', 'key2', ''] 3076 * ``` 3077 */ 3078 this.attrs = []; 3079 this.notSelectors = []; 3080 } 3081 static parse(selector) { 3082 const results = []; 3083 const _addResult = (res, cssSel) => { 3084 if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 && cssSel.attrs.length == 0) { 3085 cssSel.element = '*'; 3086 } 3087 res.push(cssSel); 3088 }; 3089 let cssSelector = new CssSelector(); 3090 let match; 3091 let current = cssSelector; 3092 let inNot = false; 3093 _SELECTOR_REGEXP.lastIndex = 0; 3094 while (match = _SELECTOR_REGEXP.exec(selector)) { 3095 if (match[1 /* SelectorRegexp.NOT */]) { 3096 if (inNot) { 3097 throw new Error('Nesting :not in a selector is not allowed'); 3098 } 3099 inNot = true; 3100 current = new CssSelector(); 3101 cssSelector.notSelectors.push(current); 3102 } 3103 const tag = match[2 /* SelectorRegexp.TAG */]; 3104 if (tag) { 3105 const prefix = match[3 /* SelectorRegexp.PREFIX */]; 3106 if (prefix === '#') { 3107 // #hash 3108 current.addAttribute('id', tag.slice(1)); 3109 } else if (prefix === '.') { 3110 // Class 3111 current.addClassName(tag.slice(1)); 3112 } else { 3113 // Element 3114 current.setElement(tag); 3115 } 3116 } 3117 const attribute = match[4 /* SelectorRegexp.ATTRIBUTE */]; 3118 if (attribute) { 3119 current.addAttribute(current.unescapeAttribute(attribute), match[6 /* SelectorRegexp.ATTRIBUTE_VALUE */]); 3120 } 3121 if (match[7 /* SelectorRegexp.NOT_END */]) { 3122 inNot = false; 3123 current = cssSelector; 3124 } 3125 if (match[8 /* SelectorRegexp.SEPARATOR */]) { 3126 if (inNot) { 3127 throw new Error('Multiple selectors in :not are not supported'); 3128 } 3129 _addResult(results, cssSelector); 3130 cssSelector = current = new CssSelector(); 3131 } 3132 } 3133 _addResult(results, cssSelector); 3134 return results; 3135 } 3136 /** 3137 * Unescape `\$` sequences from the CSS attribute selector. 3138 * 3139 * This is needed because `$` can have a special meaning in CSS selectors, 3140 * but we might want to match an attribute that contains `$`. 3141 * [MDN web link for more 3142 * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). 3143 * @param attr the attribute to unescape. 3144 * @returns the unescaped string. 3145 */ 3146 unescapeAttribute(attr) { 3147 let result = ''; 3148 let escaping = false;
3149 for (let i = 0; i < attr.length; i++) { 3150 const char = attr.charAt(i); 3151 if (char === '\\') { 3152 escaping = true; 3153 continue; 3154 } 3155 if (char === '$' && !escaping) { 3156 throw new Error(`Error in attribute selector "${attr}". ` + `Unescaped "$" is not supported. Please escape with "\\$".`); 3157 } 3158 escaping = false; 3159 result += char; 3160 } 3161 return result; 3162 } 3163 /** 3164 * Escape `$` sequences from the CSS attribute selector. 3165 * 3166 * This is needed because `$` can have a special meaning in CSS selectors, 3167 * with this method we are escaping `$` with `\$'. 3168 * [MDN web link for more 3169 * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). 3170 * @param attr the attribute to escape. 3171 * @returns the escaped string. 3172 */ 3173 escapeAttribute(attr) { 3174 return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$'); 3175 } 3176 isElementSelector() { 3177 return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 && this.notSelectors.length === 0; 3178 } 3179 hasElementSelector() { 3180 return !!this.element; 3181 } 3182 setElement(element = null) { 3183 this.element = element; 3184 } 3185 getAttrs() { 3186 const result = []; 3187 if (this.classNames.length > 0) { 3188 result.push('class', this.classNames.join(' ')); 3189 } 3190 return result.concat(this.attrs); 3191 } 3192 addAttribute(name, value = '') { 3193 this.attrs.push(name, value && value.toLowerCase() || ''); 3194 } 3195 addClassName(name) { 3196 this.classNames.push(name.toLowerCase()); 3197 } 3198 toString() { 3199 let res = this.element || ''; 3200 if (this.classNames) { 3201 this.classNames.forEach(klass => res += `.${klass}`); 3202 } 3203 if (this.attrs) { 3204 for (let i = 0; i < this.attrs.length; i += 2) { 3205 const name = this.escapeAttribute(this.attrs[i]); 3206 const value = this.attrs[i + 1]; 3207 res += `[${name}${value ? '=' + value : ''}]`; 3208 } 3209 } 3210 this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`); 3211 return res; 3212 } 3213} 3214/** 3215 * Reads a list of CssSelectors and allows to calculate which ones 3216 * are contained in a given CssSelector. 3217 */ 3218class SelectorMatcher { 3219 constructor() { 3220 this._elementMap = new Map(); 3221 this._elementPartialMap = new Map(); 3222 this._classMap = new Map(); 3223 this._classPartialMap = new Map(); 3224 this._attrValueMap = new Map(); 3225 this._attrValuePartialMap = new Map(); 3226 this._listContexts = []; 3227 } 3228 static createNotMatcher(notSelectors) { 3229 const notMatcher = new SelectorMatcher(); 3230 notMatcher.addSelectables(notSelectors, null); 3231 return notMatcher; 3232 } 3233 addSelectables(cssSelectors, callbackCtxt) { 3234 let listContext = null; 3235 if (cssSelectors.length > 1) { 3236 listContext = new SelectorListContext(cssSelectors); 3237 this._listContexts.push(listContext); 3238 } 3239 for (let i = 0; i < cssSelectors.length; i++) { 3240 this._addSelectable(cssSelectors[i], callbackCtxt, listContext); 3241 } 3242 } 3243 /** 3244 * Add an object that can be found later on by calling `match`. 3245 * @param cssSelector A css selector 3246 * @param callbackCtxt An opaque object that will be given to the callback of the `match` function 3247 */ 3248 _addSelectable(cssSelector, callbackCtxt, listContext) { 3249 let matcher = this; 3250 const element = cssSelector.element; 3251 const classNames = cssSelector.classNames; 3252 const attrs = cssSelector.attrs; 3253 const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); 3254 if (element) { 3255 const isTerminal = attrs.length === 0 && classNames.length === 0; 3256 if (isTerminal) { 3257 this._addTerminal(matcher._elementMap, element, selectable); 3258 } else { 3259 matcher = this._addPartial(matcher._elementPartialMap, element); 3260 } 3261 } 3262 if (classNames) { 3263 for (let i = 0; i < classNames.length; i++) { 3264 const isTerminal = attrs.length === 0 && i === classNames.length - 1; 3265 const className = classNames[i]; 3266 if (isTerminal) { 3267 this._addTerminal(matcher._classMap, className, selectable); 3268 } else { 3269 matcher = this._addPartial(matcher._classPartialMap, className); 3270 } 3271 } 3272 } 3273 if (attrs) { 3274 for (let i = 0; i < attrs.length; i += 2) { 3275 const isTerminal = i === attrs.length - 2; 3276 const name = attrs[i]; 3277 const value = attrs[i + 1]; 3278 if (isTerminal) { 3279 const terminalMap = matcher._attrValueMap;
3280 let terminalValuesMap = terminalMap.get(name); 3281 if (!terminalValuesMap) { 3282 terminalValuesMap = new Map(); 3283 terminalMap.set(name, terminalValuesMap); 3284 } 3285 this._addTerminal(terminalValuesMap, value, selectable); 3286 } else { 3287 const partialMap = matcher._attrValuePartialMap; 3288 let partialValuesMap = partialMap.get(name); 3289 if (!partialValuesMap) { 3290 partialValuesMap = new Map(); 3291 partialMap.set(name, partialValuesMap); 3292 } 3293 matcher = this._addPartial(partialValuesMap, value); 3294 } 3295 } 3296 } 3297 } 3298 _addTerminal(map, name, selectable) { 3299 let terminalList = map.get(name); 3300 if (!terminalList) { 3301 terminalList = []; 3302 map.set(name, terminalList); 3303 } 3304 terminalList.push(selectable); 3305 } 3306 _addPartial(map, name) { 3307 let matcher = map.get(name); 3308 if (!matcher) { 3309 matcher = new SelectorMatcher(); 3310 map.set(name, matcher); 3311 } 3312 return matcher; 3313 } 3314 /** 3315 * Find the objects that have been added via `addSelectable` 3316 * whose css selector is contained in the given css selector. 3317 * @param cssSelector A css selector 3318 * @param matchedCallback This callback will be called with the object handed into `addSelectable` 3319 * @return boolean true if a match was found 3320 */ 3321 match(cssSelector, matchedCallback) { 3322 let result = false;
3323 const element = cssSelector.element; 3324 const classNames = cssSelector.classNames; 3325 const attrs = cssSelector.attrs; 3326 for (let i = 0; i < this._listContexts.length; i++) { 3327 this._listContexts[i].alreadyMatched = false; 3328 } 3329 result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result; 3330 result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result; 3331 if (classNames) { 3332 for (let i = 0; i < classNames.length; i++) { 3333 const className = classNames[i]; 3334 result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result; 3335 result = this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || result; 3336 } 3337 } 3338 if (attrs) { 3339 for (let i = 0; i < attrs.length; i += 2) { 3340 const name = attrs[i]; 3341 const value = attrs[i + 1]; 3342 const terminalValuesMap = this._attrValueMap.get(name); 3343 if (value) { 3344 result = this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; 3345 } 3346 result = this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; 3347 const partialValuesMap = this._attrValuePartialMap.get(name); 3348 if (value) { 3349 result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; 3350 } 3351 result = this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result; 3352 } 3353 } 3354 return result; 3355 } 3356 /** @internal */ 3357 _matchTerminal(map, name, cssSelector, matchedCallback) { 3358 if (!map || typeof name !== 'string') { 3359 return false; 3360 } 3361 let selectables = map.get(name) || []; 3362 const starSelectables = map.get('*'); 3363 if (starSelectables) { 3364 selectables = selectables.concat(starSelectables); 3365 } 3366 if (selectables.length === 0) { 3367 return false; 3368 } 3369 let selectable; 3370 let result = false;
3371 for (let i = 0; i < selectables.length; i++) { 3372 selectable = selectables[i]; 3373 result = selectable.finalize(cssSelector, matchedCallback) || result; 3374 } 3375 return result; 3376 } 3377 /** @internal */ 3378 _matchPartial(map, name, cssSelector, matchedCallback) { 3379 if (!map || typeof name !== 'string') { 3380 return false; 3381 } 3382 const nestedSelector = map.get(name); 3383 if (!nestedSelector) { 3384 return false; 3385 } 3386 // TODO(perf): get rid of recursion and measure again 3387 // TODO(perf): don't pass the whole selector into the recursion, 3388 // but only the not processed parts 3389 return nestedSelector.match(cssSelector, matchedCallback); 3390 } 3391} 3392class SelectorListContext { 3393 constructor(selectors) { 3394 this.selectors = selectors; 3395 this.alreadyMatched = false; 3396 } 3397} 3398// Store context to pass back selector and context when a selector is matched 3399class SelectorContext { 3400 constructor(selector, cbContext, listContext) { 3401 this.selector = selector; 3402 this.cbContext = cbContext; 3403 this.listContext = listContext; 3404 this.notSelectors = selector.notSelectors; 3405 } 3406 finalize(cssSelector, callback) { 3407 let result = true; 3408 if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) { 3409 const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); 3410 result = !notMatcher.match(cssSelector, null); 3411 } 3412 if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) { 3413 if (this.listContext) { 3414 this.listContext.alreadyMatched = true; 3415 } 3416 callback(this.selector, this.cbContext); 3417 } 3418 return result; 3419 } 3420} 3421 3422// Attention: 3423// Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not 3424// explicitly set. 3425const emitDistinctChangesOnlyDefaultValue = true; 3426var ViewEncapsulation; 3427(function (ViewEncapsulation) { 3428 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated"; 3429 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. 3430 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None"; 3431 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; 3432})(ViewEncapsulation || (ViewEncapsulation = {})); 3433var ChangeDetectionStrategy; 3434(function (ChangeDetectionStrategy) { 3435 ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush"; 3436 ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default"; 3437})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {})); 3438/** Flags describing an input for a directive. */ 3439var InputFlags; 3440(function (InputFlags) { 3441 InputFlags[InputFlags["None"] = 0] = "None"; 3442 InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased"; 3443 InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform"; 3444})(InputFlags || (InputFlags = {})); 3445const CUSTOM_ELEMENTS_SCHEMA = { 3446 name: 'custom-elements' 3447}; 3448const NO_ERRORS_SCHEMA = { 3449 name: 'no-errors-schema' 3450}; 3451const Type$1 = Function; 3452var SecurityContext; 3453(function (SecurityContext) { 3454 SecurityContext[SecurityContext["NONE"] = 0] = "NONE"; 3455 SecurityContext[SecurityContext["HTML"] = 1] = "HTML"; 3456 SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE"; 3457 SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT"; 3458 SecurityContext[SecurityContext["URL"] = 4] = "URL"; 3459 SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL"; 3460})(SecurityContext || (SecurityContext = {})); 3461var MissingTranslationStrategy; 3462(function (MissingTranslationStrategy) { 3463 MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error"; 3464 MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning"; 3465 MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore"; 3466})(MissingTranslationStrategy || (MissingTranslationStrategy = {})); 3467function parserSelectorToSimpleSelector(selector) { 3468 const classes = selector.classNames && selector.classNames.length ? [8 /* SelectorFlags.CLASS */, ...selector.classNames] : []; 3469 const elementName = selector.element && selector.element !== '*' ? selector.element : ''; 3470 return [elementName, ...selector.attrs, ...classes]; 3471} 3472function parserSelectorToNegativeSelector(selector) { 3473 const classes = selector.classNames && selector.classNames.length ? [8 /* SelectorFlags.CLASS */, ...selector.classNames] : []; 3474 if (selector.element) { 3475 return [1 /* SelectorFlags.NOT */ | 4 /* SelectorFlags.ELEMENT */, selector.element, ...selector.attrs, ...classes]; 3476 } else if (selector.attrs.length) { 3477 return [1 /* SelectorFlags.NOT */ | 2 /* SelectorFlags.ATTRIBUTE */, ...selector.attrs, ...classes]; 3478 } else { 3479 return selector.classNames && selector.classNames.length ? [1 /* SelectorFlags.NOT */ | 8 /* SelectorFlags.CLASS */, ...selector.classNames] : []; 3480 } 3481} 3482function parserSelectorToR3Selector(selector) { 3483 const positive = parserSelectorToSimpleSelector(selector); 3484 const negative = selector.notSelectors && selector.notSelectors.length ? selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) : []; 3485 return positive.concat(...negative); 3486} 3487function parseSelectorToR3Selector(selector) { 3488 return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : []; 3489}
3490var core = /*#__PURE__*/Object.freeze({ 3491 __proto__: null, 3492 emitDistinctChangesOnlyDefaultValue: emitDistinctChangesOnlyDefaultValue, 3493 get ViewEncapsulation() { 3494 return ViewEncapsulation; 3495 }, 3496 get ChangeDetectionStrategy() { 3497 return ChangeDetectionStrategy; 3498 }, 3499 get InputFlags() { 3500 return InputFlags; 3501 }, 3502 CUSTOM_ELEMENTS_SCHEMA: CUSTOM_ELEMENTS_SCHEMA, 3503 NO_ERRORS_SCHEMA: NO_ERRORS_SCHEMA, 3504 Type: Type$1, 3505 get SecurityContext() { 3506 return SecurityContext; 3507 }, 3508 get MissingTranslationStrategy() { 3509 return MissingTranslationStrategy; 3510 }, 3511 parseSelectorToR3Selector: parseSelectorToR3Selector 3512}); 3513 3514/** 3515 * A lazily created TextEncoder instance for converting strings into UTF-8 bytes 3516 */ 3517let textEncoder; 3518/** 3519 * Return the message id or compute it using the XLIFF1 digest. 3520 */ 3521function digest$1(message) { 3522 return message.id || computeDigest(message); 3523} 3524/** 3525 * Compute the message id using the XLIFF1 digest. 3526 */ 3527function computeDigest(message) { 3528 return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`); 3529} 3530/** 3531 * Return the message id or compute it using the XLIFF2/XMB/$localize digest. 3532 */ 3533function decimalDigest(message) { 3534 return message.id || computeDecimalDigest(message); 3535} 3536/** 3537 * Compute the message id using the XLIFF2/XMB/$localize digest. 3538 */ 3539function computeDecimalDigest(message) { 3540 const visitor = new _SerializerIgnoreIcuExpVisitor(); 3541 const parts = message.nodes.map(a => a.visit(visitor, null)); 3542 return computeMsgId(parts.join(''), message.meaning); 3543} 3544/** 3545 * Serialize the i18n ast to something xml-like in order to generate an UID. 3546 * 3547 * The visitor is also used in the i18n parser tests 3548 * 3549 * @internal 3550 */ 3551class _SerializerVisitor { 3552 visitText(text, context) { 3553 return text.value; 3554 } 3555 visitContainer(container, context) { 3556 return `[${container.children.map(child => child.visit(this)).join(', ')}]`; 3557 } 3558 visitIcu(icu, context) { 3559 const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 3560 return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`; 3561 } 3562 visitTagPlaceholder(ph, context) { 3563 return ph.isVoid ? `<ph tag name="${ph.startName}"/>` : `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; 3564 } 3565 visitPlaceholder(ph, context) { 3566 return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`; 3567 } 3568 visitIcuPlaceholder(ph, context) { 3569 return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`; 3570 } 3571 visitBlockPlaceholder(ph, context) { 3572 return `<ph block name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; 3573 } 3574} 3575const serializerVisitor$1 = new _SerializerVisitor(); 3576function serializeNodes(nodes) { 3577 return nodes.map(a => a.visit(serializerVisitor$1, null)); 3578} 3579/** 3580 * Serialize the i18n ast to something xml-like in order to generate an UID. 3581 * 3582 * Ignore the ICU expressions so that message IDs stays identical if only the expression changes. 3583 * 3584 * @internal 3585 */ 3586class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor { 3587 visitIcu(icu, context) { 3588 let strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 3589 // Do not take the expression into account 3590 return `{${icu.type}, ${strCases.join(', ')}}`; 3591 } 3592} 3593/** 3594 * Compute the SHA1 of the given string 3595 * 3596 * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf 3597 * 3598 * WARNING: this function has not been designed not tested with security in mind. 3599 * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT. 3600 */ 3601function sha1(str) { 3602 textEncoder ??= new TextEncoder(); 3603 const utf8 = [...textEncoder.encode(str)]; 3604 const words32 = bytesToWords32(utf8, Endian.Big); 3605 const len = utf8.length * 8; 3606 const w = new Uint32Array(80); 3607 let a = 0x67452301, 3608 b = 0xefcdab89, 3609 c = 0x98badcfe, 3610 d = 0x10325476, 3611 e = 0xc3d2e1f0; 3612 words32[len >> 5] |= 0x80 << 24 - len % 32; 3613 words32[(len + 64 >> 9 << 4) + 15] = len; 3614 for (let i = 0; i < words32.length; i += 16) { 3615 const h0 = a, 3616 h1 = b, 3617 h2 = c, 3618 h3 = d, 3619 h4 = e; 3620 for (let j = 0; j < 80; j++) { 3621 if (j < 16) { 3622 w[j] = words32[i + j]; 3623 } else { 3624 w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); 3625 } 3626 const fkVal = fk(j, b, c, d); 3627 const f = fkVal[0];
3628 const k = fkVal[1]; 3629 const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32); 3630 e = d; 3631 d = c; 3632 c = rol32(b, 30); 3633 b = a; 3634 a = temp; 3635 } 3636 a = add32(a, h0); 3637 b = add32(b, h1); 3638 c = add32(c, h2); 3639 d = add32(d, h3); 3640 e = add32(e, h4); 3641 } 3642 // Convert the output parts to a 160-bit hexadecimal string 3643 return toHexU32(a) + toHexU32(b) + toHexU32(c) + toHexU32(d) + toHexU32(e); 3644} 3645/** 3646 * Convert and format a number as a string representing a 32-bit unsigned hexadecimal number. 3647 * @param value The value to format as a string. 3648 * @returns A hexadecimal string representing the value. 3649 */ 3650function toHexU32(value) { 3651 // unsigned right shift of zero ensures an unsigned 32-bit number 3652 return (value >>> 0).toString(16).padStart(8, '0'); 3653} 3654function fk(index, b, c, d) { 3655 if (index < 20) { 3656 return [b & c | ~b & d, 0x5a827999]; 3657 } 3658 if (index < 40) { 3659 return [b ^ c ^ d, 0x6ed9eba1]; 3660 } 3661 if (index < 60) { 3662 return [b & c | b & d | c & d, 0x8f1bbcdc]; 3663 } 3664 return [b ^ c ^ d, 0xca62c1d6]; 3665} 3666/** 3667 * Compute the fingerprint of the given string 3668 * 3669 * The output is 64 bit number encoded as a decimal string 3670 * 3671 * based on: 3672 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java 3673 */ 3674function fingerprint(str) { 3675 textEncoder ??= new TextEncoder(); 3676 const utf8 = textEncoder.encode(str); 3677 const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength); 3678 let hi = hash32(view, utf8.length, 0); 3679 let lo = hash32(view, utf8.length, 102072); 3680 if (hi == 0 && (lo == 0 || lo == 1)) { 3681 hi = hi ^ 0x130f9bef; 3682 lo = lo ^ -0x6b5f56d8; 3683 } 3684 return BigInt.asUintN(32, BigInt(hi)) << BigInt(32) | BigInt.asUintN(32, BigInt(lo)); 3685} 3686function computeMsgId(msg, meaning = '') { 3687 let msgFingerprint = fingerprint(msg); 3688 if (meaning) { 3689 // Rotate the 64-bit message fingerprint one bit to the left and then add the meaning 3690 // fingerprint. 3691 msgFingerprint = BigInt.asUintN(64, msgFingerprint << BigInt(1)) | msgFingerprint >> BigInt(63) & BigInt(1); 3692 msgFingerprint += fingerprint(meaning); 3693 } 3694 return BigInt.asUintN(63, msgFingerprint).toString(); 3695} 3696function hash32(view, length, c) { 3697 let a = 0x9e3779b9, 3698 b = 0x9e3779b9; 3699 let index = 0; 3700 const end = length - 12; 3701 for (; index <= end; index += 12) { 3702 a += view.getUint32(index, true); 3703 b += view.getUint32(index + 4, true); 3704 c += view.getUint32(index + 8, true); 3705 const res = mix(a, b, c); 3706 a = res[0], b = res[1], c = res[2]; 3707 } 3708 const remainder = length - index; 3709 // the first byte of c is reserved for the length 3710 c += length; 3711 if (remainder >= 4) { 3712 a += view.getUint32(index, true); 3713 index += 4; 3714 if (remainder >= 8) { 3715 b += view.getUint32(index, true); 3716 index += 4; 3717 // Partial 32-bit word for c 3718 if (remainder >= 9) { 3719 c += view.getUint8(index++) << 8; 3720 } 3721 if (remainder >= 10) { 3722 c += view.getUint8(index++) << 16; 3723 } 3724 if (remainder === 11) { 3725 c += view.getUint8(index++) << 24; 3726 } 3727 } else { 3728 // Partial 32-bit word for b 3729 if (remainder >= 5) { 3730 b += view.getUint8(index++); 3731 } 3732 if (remainder >= 6) { 3733 b += view.getUint8(index++) << 8; 3734 } 3735 if (remainder === 7) { 3736 b += view.getUint8(index++) << 16; 3737 } 3738 } 3739 } else { 3740 // Partial 32-bit word for a 3741 if (remainder >= 1) { 3742 a += view.getUint8(index++); 3743 } 3744 if (remainder >= 2) { 3745 a += view.getUint8(index++) << 8; 3746 } 3747 if (remainder === 3) { 3748 a += view.getUint8(index++) << 16; 3749 } 3750 } 3751 return mix(a, b, c)[2]; 3752} 3753// clang-format off 3754function mix(a, b, c) { 3755 a -= b; 3756 a -= c; 3757 a ^= c >>> 13; 3758 b -= c; 3759 b -= a; 3760 b ^= a << 8; 3761 c -= a; 3762 c -= b; 3763 c ^= b >>> 13; 3764 a -= b; 3765 a -= c; 3766 a ^= c >>> 12; 3767 b -= c; 3768 b -= a; 3769 b ^= a << 16; 3770 c -= a; 3771 c -= b; 3772 c ^= b >>> 5; 3773 a -= b; 3774 a -= c; 3775 a ^= c >>> 3; 3776 b -= c; 3777 b -= a; 3778 b ^= a << 10; 3779 c -= a; 3780 c -= b; 3781 c ^= b >>> 15; 3782 return [a, b, c]; 3783} 3784// clang-format on 3785// Utils 3786var Endian; 3787(function (Endian) { 3788 Endian[Endian["Little"] = 0] = "Little"; 3789 Endian[Endian["Big"] = 1] = "Big"; 3790})(Endian || (Endian = {})); 3791function add32(a, b) { 3792 return add32to64(a, b)[1]; 3793} 3794function add32to64(a, b) { 3795 const low = (a & 0xffff) + (b & 0xffff); 3796 const high = (a >>> 16) + (b >>> 16) + (low >>> 16); 3797 return [high >>> 16, high << 16 | low & 0xffff]; 3798} 3799// Rotate a 32b number left `count` position 3800function rol32(a, count) { 3801 return a << count | a >>> 32 - count; 3802} 3803function bytesToWords32(bytes, endian) { 3804 const size = bytes.length + 3 >>> 2;
3805 const words32 = []; 3806 for (let i = 0; i < size; i++) { 3807 words32[i] = wordAt(bytes, i * 4, endian); 3808 } 3809 return words32; 3810} 3811function byteAt(bytes, index) { 3812 return index >= bytes.length ? 0 : bytes[index]; 3813} 3814function wordAt(bytes, index, endian) { 3815 let word = 0; 3816 if (endian === Endian.Big) { 3817 for (let i = 0; i < 4; i++) { 3818 word += byteAt(bytes, index + i) << 24 - 8 * i; 3819 } 3820 } else { 3821 for (let i = 0; i < 4; i++) { 3822 word += byteAt(bytes, index + i) << 8 * i; 3823 } 3824 } 3825 return word; 3826} 3827 3828//// Types 3829var TypeModifier; 3830(function (TypeModifier) { 3831 TypeModifier[TypeModifier["None"] = 0] = "None"; 3832 TypeModifier[TypeModifier["Const"] = 1] = "Const"; 3833})(TypeModifier || (TypeModifier = {})); 3834class Type { 3835 constructor(modifiers = TypeModifier.None) { 3836 this.modifiers = modifiers; 3837 } 3838 hasModifier(modifier) { 3839 return (this.modifiers & modifier) !== 0; 3840 } 3841} 3842var BuiltinTypeName; 3843(function (BuiltinTypeName) { 3844 BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic"; 3845 BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool"; 3846 BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String"; 3847 BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int"; 3848 BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number"; 3849 BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function"; 3850 BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred"; 3851 BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None"; 3852})(BuiltinTypeName || (BuiltinTypeName = {})); 3853class BuiltinType extends Type { 3854 constructor(name, modifiers) { 3855 super(modifiers); 3856 this.name = name; 3857 } 3858 visitType(visitor, context) { 3859 return visitor.visitBuiltinType(this, context); 3860 } 3861} 3862class ExpressionType extends Type { 3863 constructor(value, modifiers, typeParams = null) { 3864 super(modifiers); 3865 this.value = value; 3866 this.typeParams = typeParams; 3867 } 3868 visitType(visitor, context) { 3869 return visitor.visitExpressionType(this, context); 3870 } 3871} 3872class ArrayType extends Type { 3873 constructor(of, modifiers) { 3874 super(modifiers); 3875 this.of = of; 3876 } 3877 visitType(visitor, context) { 3878 return visitor.visitArrayType(this, context); 3879 } 3880} 3881class MapType extends Type { 3882 constructor(valueType, modifiers) { 3883 super(modifiers); 3884 this.valueType = valueType || null; 3885 } 3886 visitType(visitor, context) { 3887 return visitor.visitMapType(this, context); 3888 } 3889} 3890class TransplantedType extends Type { 3891 constructor(type, modifiers) { 3892 super(modifiers); 3893 this.type = type; 3894 } 3895 visitType(visitor, context) { 3896 return visitor.visitTransplantedType(this, context); 3897 } 3898} 3899const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic); 3900const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred); 3901const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool); 3902const INT_TYPE = new BuiltinType(BuiltinTypeName.Int); 3903const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number); 3904const STRING_TYPE = new BuiltinType(BuiltinTypeName.String); 3905const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function); 3906const NONE_TYPE = new BuiltinType(BuiltinTypeName.None); 3907///// Expressions 3908var UnaryOperator; 3909(function (UnaryOperator) { 3910 UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus"; 3911 UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus"; 3912})(UnaryOperator || (UnaryOperator = {})); 3913var BinaryOperator; 3914(function (BinaryOperator) { 3915 BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals"; 3916 BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals"; 3917 BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical"; 3918 BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical"; 3919 BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus"; 3920 BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus"; 3921 BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide"; 3922 BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply"; 3923 BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo"; 3924 BinaryOperator[BinaryOperator["And"] = 9] = "And"; 3925 BinaryOperator[BinaryOperator["Or"] = 10] = "Or"; 3926 BinaryOperator[BinaryOperator["BitwiseOr"] = 11] = "BitwiseOr"; 3927 BinaryOperator[BinaryOperator["BitwiseAnd"] = 12] = "BitwiseAnd"; 3928 BinaryOperator[BinaryOperator["Lower"] = 13] = "Lower"; 3929 BinaryOperator[BinaryOperator["LowerEquals"] = 14] = "LowerEquals"; 3930 BinaryOperator[BinaryOperator["Bigger"] = 15] = "Bigger"; 3931 BinaryOperator[BinaryOperator["BiggerEquals"] = 16] = "BiggerEquals"; 3932 BinaryOperator[BinaryOperator["NullishCoalesce"] = 17] = "NullishCoalesce"; 3933})(BinaryOperator || (BinaryOperator = {})); 3934function nullSafeIsEquivalent(base, other) { 3935 if (base == null || other == null) { 3936 return base == other; 3937 } 3938 return base.isEquivalent(other); 3939} 3940function areAllEquivalentPredicate(base, other, equivalentPredicate) { 3941 const len = base.length; 3942 if (len !== other.length) { 3943 return false;
3944 } 3945 for (let i = 0; i < len; i++) { 3946 if (!equivalentPredicate(base[i], other[i])) { 3947 return false; 3948 } 3949 } 3950 return true; 3951} 3952function areAllEquivalent(base, other) { 3953 return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement)); 3954} 3955class Expression { 3956 constructor(type, sourceSpan) { 3957 this.type = type || null; 3958 this.sourceSpan = sourceSpan || null; 3959 } 3960 prop(name, sourceSpan) { 3961 return new ReadPropExpr(this, name, null, sourceSpan); 3962 } 3963 key(index, type, sourceSpan) { 3964 return new ReadKeyExpr(this, index, type, sourceSpan); 3965 } 3966 callFn(params, sourceSpan, pure) { 3967 return new InvokeFunctionExpr(this, params, null, sourceSpan, pure); 3968 } 3969 instantiate(params, type, sourceSpan) { 3970 return new InstantiateExpr(this, params, type, sourceSpan); 3971 } 3972 conditional(trueCase, falseCase = null, sourceSpan) { 3973 return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan); 3974 } 3975 equals(rhs, sourceSpan) { 3976 return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan); 3977 } 3978 notEquals(rhs, sourceSpan) { 3979 return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan); 3980 } 3981 identical(rhs, sourceSpan) { 3982 return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan); 3983 } 3984 notIdentical(rhs, sourceSpan) { 3985 return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan); 3986 } 3987 minus(rhs, sourceSpan) { 3988 return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan); 3989 } 3990 plus(rhs, sourceSpan) { 3991 return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan); 3992 } 3993 divide(rhs, sourceSpan) { 3994 return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan); 3995 } 3996 multiply(rhs, sourceSpan) { 3997 return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan); 3998 } 3999 modulo(rhs, sourceSpan) { 4000 return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan); 4001 } 4002 and(rhs, sourceSpan) { 4003 return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan); 4004 } 4005 bitwiseOr(rhs, sourceSpan, parens = true) { 4006 return new BinaryOperatorExpr(BinaryOperator.BitwiseOr, this, rhs, null, sourceSpan, parens); 4007 } 4008 bitwiseAnd(rhs, sourceSpan, parens = true) { 4009 return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens); 4010 } 4011 or(rhs, sourceSpan) { 4012 return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan); 4013 } 4014 lower(rhs, sourceSpan) { 4015 return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan); 4016 } 4017 lowerEquals(rhs, sourceSpan) { 4018 return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan); 4019 } 4020 bigger(rhs, sourceSpan) { 4021 return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan); 4022 } 4023 biggerEquals(rhs, sourceSpan) { 4024 return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan); 4025 } 4026 isBlank(sourceSpan) { 4027 // Note: We use equals by purpose here to compare to null and undefined in JS. 4028 // We use the typed null to allow strictNullChecks to narrow types. 4029 return this.equals(TYPED_NULL_EXPR, sourceSpan); 4030 } 4031 nullishCoalesce(rhs, sourceSpan) { 4032 return new BinaryOperatorExpr(BinaryOperator.NullishCoalesce, this, rhs, null, sourceSpan); 4033 } 4034 toStmt() { 4035 return new ExpressionStatement(this, null); 4036 } 4037} 4038class ReadVarExpr extends Expression { 4039 constructor(name, type, sourceSpan) { 4040 super(type, sourceSpan); 4041 this.name = name; 4042 } 4043 isEquivalent(e) { 4044 return e instanceof ReadVarExpr && this.name === e.name; 4045 } 4046 isConstant() { 4047 return false; 4048 } 4049 visitExpression(visitor, context) { 4050 return visitor.visitReadVarExpr(this, context); 4051 } 4052 clone() { 4053 return new ReadVarExpr(this.name, this.type, this.sourceSpan); 4054 } 4055 set(value) { 4056 return new WriteVarExpr(this.name, value, null, this.sourceSpan); 4057 } 4058} 4059class TypeofExpr extends Expression { 4060 constructor(expr, type, sourceSpan) { 4061 super(type, sourceSpan); 4062 this.expr = expr; 4063 } 4064 visitExpression(visitor, context) { 4065 return visitor.visitTypeofExpr(this, context); 4066 } 4067 isEquivalent(e) { 4068 return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr); 4069 } 4070 isConstant() { 4071 return this.expr.isConstant(); 4072 } 4073 clone() { 4074 return new TypeofExpr(this.expr.clone()); 4075 } 4076}
4077class WrappedNodeExpr extends Expression { 4078 constructor(node, type, sourceSpan) { 4079 super(type, sourceSpan); 4080 this.node = node; 4081 } 4082 isEquivalent(e) { 4083 return e instanceof WrappedNodeExpr && this.node === e.node; 4084 } 4085 isConstant() { 4086 return false; 4087 } 4088 visitExpression(visitor, context) { 4089 return visitor.visitWrappedNodeExpr(this, context); 4090 } 4091 clone() { 4092 return new WrappedNodeExpr(this.node, this.type, this.sourceSpan); 4093 } 4094} 4095class WriteVarExpr extends Expression { 4096 constructor(name, value, type, sourceSpan) { 4097 super(type || value.type, sourceSpan); 4098 this.name = name; 4099 this.value = value; 4100 } 4101 isEquivalent(e) { 4102 return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value); 4103 } 4104 isConstant() { 4105 return false; 4106 } 4107 visitExpression(visitor, context) { 4108 return visitor.visitWriteVarExpr(this, context); 4109 } 4110 clone() { 4111 return new WriteVarExpr(this.name, this.value.clone(), this.type, this.sourceSpan); 4112 } 4113 toDeclStmt(type, modifiers) { 4114 return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan); 4115 } 4116 toConstDecl() { 4117 return this.toDeclStmt(INFERRED_TYPE, StmtModifier.Final); 4118 } 4119} 4120class WriteKeyExpr extends Expression { 4121 constructor(receiver, index, value, type, sourceSpan) { 4122 super(type || value.type, sourceSpan); 4123 this.receiver = receiver; 4124 this.index = index; 4125 this.value = value; 4126 } 4127 isEquivalent(e) { 4128 return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) && this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value); 4129 } 4130 isConstant() { 4131 return false; 4132 } 4133 visitExpression(visitor, context) { 4134 return visitor.visitWriteKeyExpr(this, context); 4135 } 4136 clone() { 4137 return new WriteKeyExpr(this.receiver.clone(), this.index.clone(), this.value.clone(), this.type, this.sourceSpan); 4138 } 4139} 4140class WritePropExpr extends Expression { 4141 constructor(receiver, name, value, type, sourceSpan) { 4142 super(type || value.type, sourceSpan); 4143 this.receiver = receiver; 4144 this.name = name; 4145 this.value = value; 4146 } 4147 isEquivalent(e) { 4148 return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) && this.name === e.name && this.value.isEquivalent(e.value); 4149 } 4150 isConstant() { 4151 return false; 4152 } 4153 visitExpression(visitor, context) { 4154 return visitor.visitWritePropExpr(this, context); 4155 } 4156 clone() { 4157 return new WritePropExpr(this.receiver.clone(), this.name, this.value.clone(), this.type, this.sourceSpan); 4158 } 4159} 4160class InvokeFunctionExpr extends Expression { 4161 constructor(fn, args, type, sourceSpan, pure = false) { 4162 super(type, sourceSpan); 4163 this.fn = fn; 4164 this.args = args; 4165 this.pure = pure; 4166 } 4167 // An alias for fn, which allows other logic to handle calls and property reads together. 4168 get receiver() { 4169 return this.fn; 4170 } 4171 isEquivalent(e) { 4172 return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) && areAllEquivalent(this.args, e.args) && this.pure === e.pure; 4173 } 4174 isConstant() { 4175 return false; 4176 } 4177 visitExpression(visitor, context) { 4178 return visitor.visitInvokeFunctionExpr(this, context); 4179 } 4180 clone() { 4181 return new InvokeFunctionExpr(this.fn.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan, this.pure); 4182 } 4183} 4184class TaggedTemplateExpr extends Expression { 4185 constructor(tag, template, type, sourceSpan) { 4186 super(type, sourceSpan); 4187 this.tag = tag; 4188 this.template = template; 4189 } 4190 isEquivalent(e) { 4191 return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) && areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) && areAllEquivalent(this.template.expressions, e.template.expressions); 4192 } 4193 isConstant() { 4194 return false; 4195 } 4196 visitExpression(visitor, context) { 4197 return visitor.visitTaggedTemplateExpr(this, context); 4198 } 4199 clone() { 4200 return new TaggedTemplateExpr(this.tag.clone(), this.template.clone(), this.type, this.sourceSpan); 4201 } 4202} 4203class InstantiateExpr extends Expression { 4204 constructor(classExpr, args, type, sourceSpan) { 4205 super(type, sourceSpan); 4206 this.classExpr = classExpr; 4207 this.args = args; 4208 } 4209 isEquivalent(e) { 4210 return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) && areAllEquivalent(this.args, e.args); 4211 } 4212 isConstant() { 4213 return false;
4214 } 4215 visitExpression(visitor, context) { 4216 return visitor.visitInstantiateExpr(this, context); 4217 } 4218 clone() { 4219 return new InstantiateExpr(this.classExpr.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan); 4220 } 4221} 4222class LiteralExpr extends Expression { 4223 constructor(value, type, sourceSpan) { 4224 super(type, sourceSpan); 4225 this.value = value; 4226 } 4227 isEquivalent(e) { 4228 return e instanceof LiteralExpr && this.value === e.value; 4229 } 4230 isConstant() { 4231 return true; 4232 } 4233 visitExpression(visitor, context) { 4234 return visitor.visitLiteralExpr(this, context); 4235 } 4236 clone() { 4237 return new LiteralExpr(this.value, this.type, this.sourceSpan); 4238 } 4239} 4240class TemplateLiteral { 4241 constructor(elements, expressions) { 4242 this.elements = elements; 4243 this.expressions = expressions; 4244 } 4245 clone() { 4246 return new TemplateLiteral(this.elements.map(el => el.clone()), this.expressions.map(expr => expr.clone())); 4247 } 4248} 4249class TemplateLiteralElement { 4250 constructor(text, sourceSpan, rawText) { 4251 this.text = text; 4252 this.sourceSpan = sourceSpan; 4253 // If `rawText` is not provided, try to extract the raw string from its 4254 // associated `sourceSpan`. If that is also not available, "fake" the raw 4255 // string instead by escaping the following control sequences: 4256 // - "\" would otherwise indicate that the next character is a control character. 4257 // - "`" and "${" are template string control sequences that would otherwise prematurely 4258 // indicate the end of the template literal element. 4259 this.rawText = rawText ?? sourceSpan?.toString() ?? escapeForTemplateLiteral(escapeSlashes(text)); 4260 } 4261 clone() { 4262 return new TemplateLiteralElement(this.text, this.sourceSpan, this.rawText); 4263 } 4264} 4265class LiteralPiece { 4266 constructor(text, sourceSpan) { 4267 this.text = text; 4268 this.sourceSpan = sourceSpan; 4269 } 4270} 4271class PlaceholderPiece { 4272 /** 4273 * Create a new instance of a `PlaceholderPiece`. 4274 * 4275 * @param text the name of this placeholder (e.g. `PH_1`). 4276 * @param sourceSpan the location of this placeholder in its localized message the source code. 4277 * @param associatedMessage reference to another message that this placeholder is associated with. 4278 * The `associatedMessage` is mainly used to provide a relationship to an ICU message that has 4279 * been extracted out from the message containing the placeholder. 4280 */ 4281 constructor(text, sourceSpan, associatedMessage) { 4282 this.text = text; 4283 this.sourceSpan = sourceSpan; 4284 this.associatedMessage = associatedMessage; 4285 } 4286} 4287const MEANING_SEPARATOR$1 = '|'; 4288const ID_SEPARATOR$1 = '@@'; 4289const LEGACY_ID_INDICATOR = 'â'; 4290class LocalizedString extends Expression { 4291 constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) { 4292 super(STRING_TYPE, sourceSpan); 4293 this.metaBlock = metaBlock; 4294 this.messageParts = messageParts; 4295 this.placeHolderNames = placeHolderNames; 4296 this.expressions = expressions; 4297 } 4298 isEquivalent(e) { 4299 // return e instanceof LocalizedString && this.message === e.message; 4300 return false; 4301 } 4302 isConstant() { 4303 return false; 4304 } 4305 visitExpression(visitor, context) { 4306 return visitor.visitLocalizedString(this, context); 4307 } 4308 clone() { 4309 return new LocalizedString(this.metaBlock, this.messageParts, this.placeHolderNames, this.expressions.map(expr => expr.clone()), this.sourceSpan); 4310 } 4311 /** 4312 * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used 4313 * in a `$localize` tagged string. The format of the metadata is the same as that parsed by 4314 * `parseI18nMeta()`. 4315 * 4316 * @param meta The metadata to serialize 4317 * @param messagePart The first part of the tagged string 4318 */ 4319 serializeI18nHead() { 4320 let metaBlock = this.metaBlock.description || ''; 4321 if (this.metaBlock.meaning) { 4322 metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR$1}${metaBlock}`; 4323 } 4324 if (this.metaBlock.customId) { 4325 metaBlock = `${metaBlock}${ID_SEPARATOR$1}${this.metaBlock.customId}`; 4326 } 4327 if (this.metaBlock.legacyIds) { 4328 this.metaBlock.legacyIds.forEach(legacyId => { 4329 metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; 4330 }); 4331 } 4332 return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0)); 4333 } 4334 getMessagePartSourceSpan(i) { 4335 return this.messageParts[i]?.sourceSpan ?? this.sourceSpan; 4336 } 4337 getPlaceholderSourceSpan(i) { 4338 return this.placeHolderNames[i]?.sourceSpan ?? this.expressions[i]?.sourceSpan ?? this.sourceSpan; 4339 } 4340 /** 4341 * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" str
4341ings that 4342 * can be used in a `$localize` tagged string. 4343 * 4344 * The format is `:<placeholder-name>[@@<associated-id>]:`. 4345 * 4346 * The `associated-id` is the message id of the (usually an ICU) message to which this placeholder 4347 * refers. 4348 * 4349 * @param partIndex The index of the message part to serialize. 4350 */ 4351 serializeI18nTemplatePart(partIndex) { 4352 const placeholder = this.placeHolderNames[partIndex - 1]; 4353 const messagePart = this.messageParts[partIndex]; 4354 let metaBlock = placeholder.text; 4355 if (placeholder.associatedMessage?.legacyIds.length === 0) { 4356 metaBlock += `${ID_SEPARATOR$1}${computeMsgId(placeholder.associatedMessage.messageString, placeholder.associatedMessage.meaning)}`; 4357 } 4358 return createCookedRawString(metaBlock, messagePart.text, this.getMessagePartSourceSpan(partIndex)); 4359 } 4360} 4361const escapeSlashes = str => str.replace(/\\/g, '\\\\'); 4362const escapeStartingColon = str => str.replace(/^:/, '\\:'); 4363const escapeColons = str => str.replace(/:/g, '\\:'); 4364const escapeForTemplateLiteral = str => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{'); 4365/** 4366 * Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`. 4367 * 4368 * The `raw` text must have various character sequences escaped: 4369 * * "\" would otherwise indicate that the next character is a control character. 4370 * * "`" and "${" are template string control sequences that would otherwise prematurely indicate 4371 * the end of a message part. 4372 * * ":" inside a metablock would prematurely indicate the end of the metablock. 4373 * * ":" at the start of a messagePart with no metablock would erroneously indicate the start of a 4374 * metablock. 4375 * 4376 * @param metaBlock Any metadata that should be prepended to the string 4377 * @param messagePart The message part of the string 4378 */ 4379function createCookedRawString(metaBlock, messagePart, range) { 4380 if (metaBlock === '') { 4381 return { 4382 cooked: messagePart, 4383 raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))), 4384 range 4385 }; 4386 } else { 4387 return { 4388 cooked: `:${metaBlock}:${messagePart}`, 4389 raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`), 4390 range 4391 }; 4392 } 4393} 4394class ExternalExpr extends Expression { 4395 constructor(value, type, typeParams = null, sourceSpan) { 4396 super(type, sourceSpan); 4397 this.value = value; 4398 this.typeParams = typeParams; 4399 } 4400 isEquivalent(e) { 4401 return e instanceof ExternalExpr && this.value.name === e.value.name && this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime; 4402 } 4403 isConstant() { 4404 return false; 4405 } 4406 visitExpression(visitor, context) { 4407 return visitor.visitExternalExpr(this, context); 4408 } 4409 clone() { 4410 return new ExternalExpr(this.value, this.type, this.typeParams, this.sourceSpan); 4411 } 4412} 4413class ExternalReference { 4414 constructor(moduleName, name, runtime) { 4415 this.moduleName = moduleName; 4416 this.name = name; 4417 this.runtime = runtime; 4418 } 4419} 4420class ConditionalExpr extends Expression { 4421 constructor(condition, trueCase, falseCase = null, type, sourceSpan) { 4422 super(type || trueCase.type, sourceSpan); 4423 this.condition = condition; 4424 this.falseCase = falseCase; 4425 this.trueCase = trueCase; 4426 } 4427 isEquivalent(e) { 4428 return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) && this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase); 4429 } 4430 isConstant() { 4431 return false; 4432 } 4433 visitExpression(visitor, context) { 4434 return visitor.visitConditionalExpr(this, context); 4435 } 4436 clone() { 4437 return new ConditionalExpr(this.condition.clone(), this.trueCase.clone(), this.falseCase?.clone(), this.type, this.sourceSpan); 4438 } 4439} 4440class DynamicImportExpr extends Expression { 4441 constructor(url, sourceSpan) { 4442 super(null, sourceSpan); 4443 this.url = url; 4444 } 4445 isEquivalent(e) { 4446 return e instanceof DynamicImportExpr && this.url === e.url; 4447 } 4448 isConstant() { 4449 return false; 4450 } 4451 visitExpression(visitor, context) { 4452 return visitor.visitDynamicImportExpr(this, context); 4453 } 4454 clone() { 4455 return new DynamicImportExpr(this.url, this.sourceSpan); 4456 } 4457}
4458class NotExpr extends Expression { 4459 constructor(condition, sourceSpan) { 4460 super(BOOL_TYPE, sourceSpan); 4461 this.condition = condition; 4462 } 4463 isEquivalent(e) { 4464 return e instanceof NotExpr && this.condition.isEquivalent(e.condition); 4465 } 4466 isConstant() { 4467 return false; 4468 } 4469 visitExpression(visitor, context) { 4470 return visitor.visitNotExpr(this, context); 4471 } 4472 clone() { 4473 return new NotExpr(this.condition.clone(), this.sourceSpan); 4474 } 4475} 4476class FnParam { 4477 constructor(name, type = null) { 4478 this.name = name; 4479 this.type = type; 4480 } 4481 isEquivalent(param) { 4482 return this.name === param.name; 4483 } 4484 clone() { 4485 return new FnParam(this.name, this.type); 4486 } 4487} 4488class FunctionExpr extends Expression { 4489 constructor(params, statements, type, sourceSpan, name) { 4490 super(type, sourceSpan); 4491 this.params = params; 4492 this.statements = statements; 4493 this.name = name; 4494 } 4495 isEquivalent(e) { 4496 return (e instanceof FunctionExpr || e instanceof DeclareFunctionStmt) && areAllEquivalent(this.params, e.params) && areAllEquivalent(this.statements, e.statements); 4497 } 4498 isConstant() { 4499 return false; 4500 } 4501 visitExpression(visitor, context) { 4502 return visitor.visitFunctionExpr(this, context); 4503 } 4504 toDeclStmt(name, modifiers) { 4505 return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan); 4506 } 4507 clone() { 4508 // TODO: Should we deep clone statements? 4509 return new FunctionExpr(this.params.map(p => p.clone()), this.statements, this.type, this.sourceSpan, this.name); 4510 } 4511} 4512class ArrowFunctionExpr extends Expression { 4513 // Note that `body: Expression` represents `() => expr` whereas 4514 // `body: Statement[]` represents `() => { expr }`. 4515 constructor(params, body, type, sourceSpan) { 4516 super(type, sourceSpan); 4517 this.params = params; 4518 this.body = body; 4519 } 4520 isEquivalent(e) { 4521 if (!(e instanceof ArrowFunctionExpr) || !areAllEquivalent(this.params, e.params)) { 4522 return false; 4523 } 4524 if (this.body instanceof Expression && e.body instanceof Expression) { 4525 return this.body.isEquivalent(e.body); 4526 } 4527 if (Array.isArray(this.body) && Array.isArray(e.body)) { 4528 return areAllEquivalent(this.body, e.body); 4529 } 4530 return false; 4531 } 4532 isConstant() { 4533 return false; 4534 } 4535 visitExpression(visitor, context) { 4536 return visitor.visitArrowFunctionExpr(this, context); 4537 } 4538 clone() { 4539 // TODO: Should we deep clone statements? 4540 return new ArrowFunctionExpr(this.params.map(p => p.clone()), Array.isArray(this.body) ? this.body : this.body.clone(), this.type, this.sourceSpan); 4541 } 4542 toDeclStmt(name, modifiers) { 4543 return new DeclareVarStmt(name, this, INFERRED_TYPE, modifiers, this.sourceSpan); 4544 } 4545} 4546class UnaryOperatorExpr extends Expression { 4547 constructor(operator, expr, type, sourceSpan, parens = true) { 4548 super(type || NUMBER_TYPE, sourceSpan); 4549 this.operator = operator; 4550 this.expr = expr; 4551 this.parens = parens; 4552 } 4553 isEquivalent(e) { 4554 return e instanceof UnaryOperatorExpr && this.operator === e.operator && this.expr.isEquivalent(e.expr); 4555 } 4556 isConstant() { 4557 return false; 4558 } 4559 visitExpression(visitor, context) { 4560 return visitor.visitUnaryOperatorExpr(this, context); 4561 } 4562 clone() { 4563 return new UnaryOperatorExpr(this.operator, this.expr.clone(), this.type, this.sourceSpan, this.parens); 4564 } 4565} 4566class BinaryOperatorExpr extends Expression { 4567 constructor(operator, lhs, rhs, type, sourceSpan, parens = true) { 4568 super(type || lhs.type, sourceSpan); 4569 this.operator = operator; 4570 this.rhs = rhs; 4571 this.parens = parens; 4572 this.lhs = lhs; 4573 } 4574 isEquivalent(e) { 4575 return e instanceof BinaryOperatorExpr && this.operator === e.operator && this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs); 4576 } 4577 isConstant() { 4578 return false; 4579 } 4580 visitExpression(visitor, context) { 4581 return visitor.visitBinaryOperatorExpr(this, context); 4582 } 4583 clone() { 4584 return new BinaryOperatorExpr(this.operator, this.lhs.clone(), this.rhs.clone(), this.type, this.sourceSpan, this.parens); 4585 } 4586}
4587class ReadPropExpr extends Expression { 4588 constructor(receiver, name, type, sourceSpan) { 4589 super(type, sourceSpan); 4590 this.receiver = receiver; 4591 this.name = name; 4592 } 4593 // An alias for name, which allows other logic to handle property reads and keyed reads together. 4594 get index() { 4595 return this.name; 4596 } 4597 isEquivalent(e) { 4598 return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) && this.name === e.name; 4599 } 4600 isConstant() { 4601 return false; 4602 } 4603 visitExpression(visitor, context) { 4604 return visitor.visitReadPropExpr(this, context); 4605 } 4606 set(value) { 4607 return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan); 4608 } 4609 clone() { 4610 return new ReadPropExpr(this.receiver.clone(), this.name, this.type, this.sourceSpan); 4611 } 4612} 4613class ReadKeyExpr extends Expression { 4614 constructor(receiver, index, type, sourceSpan) { 4615 super(type, sourceSpan); 4616 this.receiver = receiver; 4617 this.index = index; 4618 } 4619 isEquivalent(e) { 4620 return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) && this.index.isEquivalent(e.index); 4621 } 4622 isConstant() { 4623 return false; 4624 } 4625 visitExpression(visitor, context) { 4626 return visitor.visitReadKeyExpr(this, context); 4627 } 4628 set(value) { 4629 return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan); 4630 } 4631 clone() { 4632 return new ReadKeyExpr(this.receiver.clone(), this.index.clone(), this.type, this.sourceSpan); 4633 } 4634} 4635class LiteralArrayExpr extends Expression { 4636 constructor(entries, type, sourceSpan) { 4637 super(type, sourceSpan); 4638 this.entries = entries; 4639 } 4640 isConstant() { 4641 return this.entries.every(e => e.isConstant()); 4642 } 4643 isEquivalent(e) { 4644 return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries); 4645 } 4646 visitExpression(visitor, context) { 4647 return visitor.visitLiteralArrayExpr(this, context); 4648 } 4649 clone() { 4650 return new LiteralArrayExpr(this.entries.map(e => e.clone()), this.type, this.sourceSpan); 4651 } 4652} 4653class LiteralMapEntry { 4654 constructor(key, value, quoted) { 4655 this.key = key; 4656 this.value = value; 4657 this.quoted = quoted; 4658 } 4659 isEquivalent(e) { 4660 return this.key === e.key && this.value.isEquivalent(e.value); 4661 } 4662 clone() { 4663 return new LiteralMapEntry(this.key, this.value.clone(), this.quoted); 4664 } 4665} 4666class LiteralMapExpr extends Expression { 4667 constructor(entries, type, sourceSpan) { 4668 super(type, sourceSpan); 4669 this.entries = entries; 4670 this.valueType = null; 4671 if (type) { 4672 this.valueType = type.valueType; 4673 } 4674 } 4675 isEquivalent(e) { 4676 return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries); 4677 } 4678 isConstant() { 4679 return this.entries.every(e => e.value.isConstant()); 4680 } 4681 visitExpression(visitor, context) { 4682 return visitor.visitLiteralMapExpr(this, context); 4683 } 4684 clone() { 4685 const entriesClone = this.entries.map(entry => entry.clone()); 4686 return new LiteralMapExpr(entriesClone, this.type, this.sourceSpan); 4687 } 4688} 4689class CommaExpr extends Expression { 4690 constructor(parts, sourceSpan) { 4691 super(parts[parts.length - 1].type, sourceSpan); 4692 this.parts = parts; 4693 } 4694 isEquivalent(e) { 4695 return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts); 4696 } 4697 isConstant() { 4698 return false; 4699 } 4700 visitExpression(visitor, context) { 4701 return visitor.visitCommaExpr(this, context); 4702 } 4703 clone() { 4704 return new CommaExpr(this.parts.map(p => p.clone())); 4705 } 4706} 4707const NULL_EXPR = new LiteralExpr(null, null, null); 4708const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null); 4709//// Statements 4710var StmtModifier; 4711(function (StmtModifier) { 4712 StmtModifier[StmtModifier["None"] = 0] = "None"; 4713 StmtModifier[StmtModifier["Final"] = 1] = "Final"; 4714 StmtModifier[StmtModifier["Private"] = 2] = "Private"; 4715 StmtModifier[StmtModifier["Exported"] = 4] = "Exported"; 4716 StmtModifier[StmtModifier["Static"] = 8] = "Static"; 4717})(StmtModifier || (StmtModifier = {})); 4718class LeadingComment { 4719 constructor(text, multiline, trailingNewline) { 4720 this.text = text; 4721 this.multiline = multiline; 4722 this.trailingNewline = trailingNewline; 4723 } 4724 toString() { 4725 return this.multiline ? ` ${this.text} ` : this.text; 4726 } 4727} 4728class JSDocComment extends LeadingComment {
4729 constructor(tags) { 4730 super('', /* multiline */true, /* trailingNewline */true); 4731 this.tags = tags; 4732 } 4733 toString() { 4734 return serializeTags(this.tags); 4735 } 4736} 4737class Statement { 4738 constructor(modifiers = StmtModifier.None, sourceSpan = null, leadingComments) { 4739 this.modifiers = modifiers; 4740 this.sourceSpan = sourceSpan; 4741 this.leadingComments = leadingComments; 4742 } 4743 hasModifier(modifier) { 4744 return (this.modifiers & modifier) !== 0; 4745 } 4746 addLeadingComment(leadingComment) { 4747 this.leadingComments = this.leadingComments ?? []; 4748 this.leadingComments.push(leadingComment); 4749 } 4750} 4751class DeclareVarStmt extends Statement { 4752 constructor(name, value, type, modifiers, sourceSpan, leadingComments) { 4753 super(modifiers, sourceSpan, leadingComments); 4754 this.name = name; 4755 this.value = value; 4756 this.type = type || value && value.type || null; 4757 } 4758 isEquivalent(stmt) { 4759 return stmt instanceof DeclareVarStmt && this.name === stmt.name && (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value); 4760 } 4761 visitStatement(visitor, context) { 4762 return visitor.visitDeclareVarStmt(this, context); 4763 } 4764} 4765class DeclareFunctionStmt extends Statement { 4766 constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) { 4767 super(modifiers, sourceSpan, leadingComments); 4768 this.name = name; 4769 this.params = params; 4770 this.statements = statements; 4771 this.type = type || null; 4772 } 4773 isEquivalent(stmt) { 4774 return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) && areAllEquivalent(this.statements, stmt.statements); 4775 } 4776 visitStatement(visitor, context) { 4777 return visitor.visitDeclareFunctionStmt(this, context); 4778 } 4779} 4780class ExpressionStatement extends Statement { 4781 constructor(expr, sourceSpan, leadingComments) { 4782 super(StmtModifier.None, sourceSpan, leadingComments); 4783 this.expr = expr; 4784 } 4785 isEquivalent(stmt) { 4786 return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr); 4787 } 4788 visitStatement(visitor, context) { 4789 return visitor.visitExpressionStmt(this, context); 4790 } 4791} 4792class ReturnStatement extends Statement { 4793 constructor(value, sourceSpan = null, leadingComments) { 4794 super(StmtModifier.None, sourceSpan, leadingComments); 4795 this.value = value; 4796 } 4797 isEquivalent(stmt) { 4798 return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value); 4799 } 4800 visitStatement(visitor, context) { 4801 return visitor.visitReturnStmt(this, context); 4802 } 4803} 4804class IfStmt extends Statement { 4805 constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) { 4806 super(StmtModifier.None, sourceSpan, leadingComments); 4807 this.condition = condition; 4808 this.trueCase = trueCase; 4809 this.falseCase = falseCase; 4810 } 4811 isEquivalent(stmt) { 4812 return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) && areAllEquivalent(this.trueCase, stmt.trueCase) && areAllEquivalent(this.falseCase, stmt.falseCase); 4813 } 4814 visitStatement(visitor, context) { 4815 return visitor.visitIfStmt(this, context); 4816 } 4817} 4818class RecursiveAstVisitor$1 { 4819 visitType(ast, context) { 4820 return ast; 4821 } 4822 visitExpression(ast, context) { 4823 if (ast.type) { 4824 ast.type.visitType(this, context); 4825 } 4826 return ast; 4827 } 4828 visitBuiltinType(type, context) { 4829 return this.visitType(type, context); 4830 } 4831 visitExpressionType(type, context) { 4832 type.value.visitExpression(this, context); 4833 if (type.typeParams !== null) { 4834 type.typeParams.forEach(param => this.visitType(param, context)); 4835 } 4836 return this.visitType(type, context); 4837 } 4838 visitArrayType(type, context) { 4839 return this.visitType(type, context); 4840 } 4841 visitMapType(type, context) { 4842 return this.visitType(type, context); 4843 } 4844 visitTransplantedType(type, context) { 4845 return type; 4846 } 4847 visitWrappedNodeExpr(ast, context) { 4848 return ast; 4849 } 4850 visitTypeofExpr(ast, context) { 4851 return this.visitExpression(ast, context); 4852 } 4853 visitReadVarExpr(ast, context) { 4854 return this.visitExpression(ast, context); 4855 } 4856 visitWriteVarExpr(ast, context) { 4857 ast.value.visitExpression(this, context); 4858 return this.visitExpression(ast, context); 4859 } 4860 visitWriteKeyExpr(ast, context) { 4861 ast.receiver.visitExpression(this, context); 4862 ast.index.visitExpression(this, context); 4863 ast.value.visitExpression(this, context); 4864 return this.visitExpression(ast, context); 4865 } 4866 visitWritePropExpr(ast, context) { 4867 ast.receiver.visitExpression(this, context); 4868 ast.value.visitExpression(this, context); 4869 return this.visitExpression(ast, context); 4870 } 4871 visitDynamicImportExpr(ast, context) { 4872 return this.visitExpression(ast, context); 4873 } 4874 visitInvokeFunctionExpr(ast, context) { 4875 ast.fn.visitExpression(this, context); 4876 this.visitAllExpressions(ast.args, context); 4877 return this.visitExpression(ast, context); 4878 } 4879 visitTaggedTemplateExpr(ast, context) { 4880 ast.tag.visitExpression(this, context); 4881 this.visitAllExpressions(ast.template.expressions, context); 4882 return this.visitExpression(ast, context); 4883 } 4884 visitInstantiateExpr(ast, context) { 4885 ast.classExpr.visitExpression(this, context); 4886 this.visitAllExpressions(ast.args, context); 4887 return this.visitExpression(ast, context); 4888 } 4889 visitLiteralExpr(ast, context) { 4890 return this.visitExpression(ast, context); 4891 } 4892 visitLocalizedString(ast, context) { 4893 return this.visitExpression(ast, context); 4894 }
4895 visitExternalExpr(ast, context) { 4896 if (ast.typeParams) { 4897 ast.typeParams.forEach(type => type.visitType(this, context)); 4898 } 4899 return this.visitExpression(ast, context); 4900 } 4901 visitConditionalExpr(ast, context) { 4902 ast.condition.visitExpression(this, context); 4903 ast.trueCase.visitExpression(this, context); 4904 ast.falseCase.visitExpression(this, context); 4905 return this.visitExpression(ast, context); 4906 } 4907 visitNotExpr(ast, context) { 4908 ast.condition.visitExpression(this, context); 4909 return this.visitExpression(ast, context); 4910 } 4911 visitFunctionExpr(ast, context) { 4912 this.visitAllStatements(ast.statements, context); 4913 return this.visitExpression(ast, context); 4914 } 4915 visitArrowFunctionExpr(ast, context) { 4916 if (Array.isArray(ast.body)) { 4917 this.visitAllStatements(ast.body, context); 4918 } else { 4919 this.visitExpression(ast.body, context); 4920 } 4921 return this.visitExpression(ast, context); 4922 } 4923 visitUnaryOperatorExpr(ast, context) { 4924 ast.expr.visitExpression(this, context); 4925 return this.visitExpression(ast, context); 4926 } 4927 visitBinaryOperatorExpr(ast, context) { 4928 ast.lhs.visitExpression(this, context); 4929 ast.rhs.visitExpression(this, context); 4930 return this.visitExpression(ast, context); 4931 } 4932 visitReadPropExpr(ast, context) { 4933 ast.receiver.visitExpression(this, context); 4934 return this.visitExpression(ast, context); 4935 } 4936 visitReadKeyExpr(ast, context) { 4937 ast.receiver.visitExpression(this, context); 4938 ast.index.visitExpression(this, context); 4939 return this.visitExpression(ast, context); 4940 } 4941 visitLiteralArrayExpr(ast, context) { 4942 this.visitAllExpressions(ast.entries, context); 4943 return this.visitExpression(ast, context); 4944 } 4945 visitLiteralMapExpr(ast, context) { 4946 ast.entries.forEach(entry => entry.value.visitExpression(this, context)); 4947 return this.visitExpression(ast, context); 4948 } 4949 visitCommaExpr(ast, context) { 4950 this.visitAllExpressions(ast.parts, context); 4951 return this.visitExpression(ast, context); 4952 } 4953 visitAllExpressions(exprs, context) { 4954 exprs.forEach(expr => expr.visitExpression(this, context)); 4955 } 4956 visitDeclareVarStmt(stmt, context) { 4957 if (stmt.value) { 4958 stmt.value.visitExpression(this, context); 4959 } 4960 if (stmt.type) { 4961 stmt.type.visitType(this, context); 4962 } 4963 return stmt; 4964 } 4965 visitDeclareFunctionStmt(stmt, context) { 4966 this.visitAllStatements(stmt.statements, context); 4967 if (stmt.type) { 4968 stmt.type.visitType(this, context); 4969 } 4970 return stmt; 4971 } 4972 visitExpressionStmt(stmt, context) { 4973 stmt.expr.visitExpression(this, context); 4974 return stmt; 4975 } 4976 visitReturnStmt(stmt, context) { 4977 stmt.value.visitExpression(this, context); 4978 return stmt; 4979 } 4980 visitIfStmt(stmt, context) { 4981 stmt.condition.visitExpression(this, context); 4982 this.visitAllStatements(stmt.trueCase, context); 4983 this.visitAllStatements(stmt.falseCase, context); 4984 return stmt; 4985 } 4986 visitAllStatements(stmts, context) { 4987 stmts.forEach(stmt => stmt.visitStatement(this, context)); 4988 } 4989} 4990function leadingComment(text, multiline = false, trailingNewline = true) { 4991 return new LeadingComment(text, multiline, trailingNewline); 4992} 4993function jsDocComment(tags = []) { 4994 return new JSDocComment(tags); 4995} 4996function variable(name, type, sourceSpan) { 4997 return new ReadVarExpr(name, type, sourceSpan); 4998} 4999function importExpr(id, typeParams = null, sourceSpan) { 5000 return new ExternalExpr(id, null, typeParams, sourceSpan); 5001} 5002function importType(id, typeParams, typeModifiers) { 5003 return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null; 5004} 5005function expressionType(expr, typeModifiers, typeParams) { 5006 return new ExpressionType(expr, typeModifiers, typeParams); 5007} 5008function transplantedType(type, typeModifiers) { 5009 return new TransplantedType(type, typeModifiers); 5010} 5011function typeofExpr(expr) { 5012 return new TypeofExpr(expr); 5013} 5014function literalArr(values, type, sourceSpan) { 5015 return new LiteralArrayExpr(values, type, sourceSpan); 5016} 5017function literalMap(values, type = null) { 5018 return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null); 5019} 5020function unary(operator, expr, type, sourceSpan) { 5021 return new UnaryOperatorExpr(operator, expr, type, sourceSpan); 5022} 5023function not(expr, sourceSpan) { 5024 return new NotExpr(expr, sourceSpan); 5025} 5026function fn(params, body, type, sourceSpan, name) { 5027 return new FunctionExpr(params, body, type, sourceSpan, name); 5028} 5029function arrowFn(params, body, type, sourceSpan) { 5030 return new ArrowFunctionExpr(params, body, type, sourceSpan); 5031} 5032function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) { 5033 return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments); 5034} 5035function taggedTemplate(tag, template, type, sourceSpan) { 5036 return new TaggedTemplateExpr(tag, template, type, sourceSpan); 5037} 5038function literal(value, type, sourceSpan) { 5039 return new LiteralExpr(value, type, sourceSpan); 5040} 5041function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) { 5042 return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan); 5043} 5044function isNull(exp) { 5045 return exp instanceof LiteralExpr && exp.value === null; 5046} 5047/* 5048 * Serializes a `Tag` into a string. 5049 * Returns a string like " @foo {bar} baz" (note the leading whitespace before `@foo`).
5050 */ 5051function tagToString(tag) { 5052 let out = ''; 5053 if (tag.tagName) { 5054 out += ` @${tag.tagName}`; 5055 } 5056 if (tag.text) { 5057 if (tag.text.match(/\/\*|\*\//)) { 5058 throw new Error('JSDoc text cannot contain "/*" and "*/"'); 5059 } 5060 out += ' ' + tag.text.replace(/@/g, '\\@'); 5061 } 5062 return out; 5063} 5064function serializeTags(tags) { 5065 if (tags.length === 0) return ''; 5066 if (tags.length === 1 && tags[0].tagName && !tags[0].text) { 5067 // The JSDOC comment is a single simple tag: e.g `/** @tagname */`. 5068 return `*${tagToString(tags[0])} `; 5069 } 5070 let out = '*\n'; 5071 for (const tag of tags) { 5072 out += ' *'; 5073 // If the tagToString is multi-line, insert " * " prefixes on lines. 5074 out += tagToString(tag).replace(/\n/g, '\n * '); 5075 out += '\n'; 5076 } 5077 out += ' '; 5078 return out; 5079} 5080var output_ast = /*#__PURE__*/Object.freeze({ 5081 __proto__: null, 5082 get TypeModifier() { 5083 return TypeModifier; 5084 }, 5085 Type: Type, 5086 get BuiltinTypeName() { 5087 return BuiltinTypeName; 5088 }, 5089 BuiltinType: BuiltinType, 5090 ExpressionType: ExpressionType, 5091 ArrayType: ArrayType, 5092 MapType: MapType, 5093 TransplantedType: TransplantedType, 5094 DYNAMIC_TYPE: DYNAMIC_TYPE, 5095 INFERRED_TYPE: INFERRED_TYPE, 5096 BOOL_TYPE: BOOL_TYPE, 5097 INT_TYPE: INT_TYPE, 5098 NUMBER_TYPE: NUMBER_TYPE, 5099 STRING_TYPE: STRING_TYPE, 5100 FUNCTION_TYPE: FUNCTION_TYPE, 5101 NONE_TYPE: NONE_TYPE, 5102 get UnaryOperator() { 5103 return UnaryOperator; 5104 }, 5105 get BinaryOperator() { 5106 return BinaryOperator; 5107 }, 5108 nullSafeIsEquivalent: nullSafeIsEquivalent, 5109 areAllEquivalent: areAllEquivalent, 5110 Expression: Expression, 5111 ReadVarExpr: ReadVarExpr, 5112 TypeofExpr: TypeofExpr, 5113 WrappedNodeExpr: WrappedNodeExpr, 5114 WriteVarExpr: WriteVarExpr, 5115 WriteKeyExpr: WriteKeyExpr, 5116 WritePropExpr: WritePropExpr, 5117 InvokeFunctionExpr: InvokeFunctionExpr, 5118 TaggedTemplateExpr: TaggedTemplateExpr, 5119 InstantiateExpr: InstantiateExpr, 5120 LiteralExpr: LiteralExpr, 5121 TemplateLiteral: TemplateLiteral, 5122 TemplateLiteralElement: TemplateLiteralElement, 5123 LiteralPiece: LiteralPiece, 5124 PlaceholderPiece: PlaceholderPiece, 5125 LocalizedString: LocalizedString, 5126 ExternalExpr: ExternalExpr, 5127 ExternalReference: ExternalReference, 5128 ConditionalExpr: ConditionalExpr, 5129 DynamicImportExpr: DynamicImportExpr, 5130 NotExpr: NotExpr, 5131 FnParam: FnParam, 5132 FunctionExpr: FunctionExpr, 5133 ArrowFunctionExpr: ArrowFunctionExpr, 5134 UnaryOperatorExpr: UnaryOperatorExpr, 5135 BinaryOperatorExpr: BinaryOperatorExpr, 5136 ReadPropExpr: ReadPropExpr, 5137 ReadKeyExpr: ReadKeyExpr, 5138 LiteralArrayExpr: LiteralArrayExpr, 5139 LiteralMapEntry: LiteralMapEntry, 5140 LiteralMapExpr: LiteralMapExpr, 5141 CommaExpr: CommaExpr, 5142 NULL_EXPR: NULL_EXPR, 5143 TYPED_NULL_EXPR: TYPED_NULL_EXPR, 5144 get StmtModifier() { 5145 return StmtModifier; 5146 }, 5147 LeadingComment: LeadingComment, 5148 JSDocComment: JSDocComment, 5149 Statement: Statement, 5150 DeclareVarStmt: DeclareVarStmt, 5151 DeclareFunctionStmt: DeclareFunctionStmt, 5152 ExpressionStatement: ExpressionStatement, 5153 ReturnStatement: ReturnStatement, 5154 IfStmt: IfStmt, 5155 RecursiveAstVisitor: RecursiveAstVisitor$1, 5156 leadingComment: leadingComment, 5157 jsDocComment: jsDocComment, 5158 variable: variable, 5159 importExpr: importExpr, 5160 importType: importType, 5161 expressionType: expressionType, 5162 transplantedType: transplantedType, 5163 typeofExpr: typeofExpr, 5164 literalArr: literalArr, 5165 literalMap: literalMap, 5166 unary: unary, 5167 not: not, 5168 fn: fn, 5169 arrowFn: arrowFn, 5170 ifStmt: ifStmt, 5171 taggedTemplate: taggedTemplate, 5172 literal: literal, 5173 localizedString: localizedString, 5174 isNull: isNull 5175}); 5176const CONSTANT_PREFIX = '_c'; 5177/** 5178 * `ConstantPool` tries to reuse literal factories when two or more literals are identical. 5179 * We determine whether literals are identical by creating a key out of their AST using the 5180 * `KeyVisitor`. This constant is used to replace dynamic expressions which can't be safely 5181 * converted into a key. E.g. given an expression `{foo: bar()}`, since we don't know what 5182 * the result of `bar` will be, we create a key that looks like `{foo: <unknown>}`. Note 5183 * that we use a variable, rather than something like `null` in order to avoid collisions. 5184 */ 5185const UNKNOWN_VALUE_KEY = variable('<unknown>'); 5186/** 5187 * Context to use when producing a key. 5188 * 5189 * This ensures we see the constant not the reference variable when producing 5190 * a key. 5191 */ 5192const KEY_CONTEXT = {}; 5193/** 5194 * Generally all primitive values are excluded from the `ConstantPool`, but there is an exclusion 5195 * for strings that reach a certain length threshold. This constant defines the length threshold for 5196 * strings. 5197 */ 5198const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50; 5199/** 5200 * A node that is a place-holder that allows the node to be replaced when the actual 5201 * node is known. 5202 * 5203 * This allows the constant pool to change an expression from a direct reference to 5204 * a constant to a shared constant. It returns a fix-up node that is later allowed to 5205 * change the referenced expression. 5206 */
5207class FixupExpression extends Expression { 5208 constructor(resolved) { 5209 super(resolved.type); 5210 this.resolved = resolved; 5211 this.shared = false; 5212 this.original = resolved; 5213 } 5214 visitExpression(visitor, context) { 5215 if (context === KEY_CONTEXT) { 5216 // When producing a key we want to traverse the constant not the 5217 // variable used to refer to it. 5218 return this.original.visitExpression(visitor, context); 5219 } else { 5220 return this.resolved.visitExpression(visitor, context); 5221 } 5222 } 5223 isEquivalent(e) { 5224 return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved); 5225 } 5226 isConstant() { 5227 return true; 5228 } 5229 clone() { 5230 throw new Error(`Not supported.`); 5231 } 5232 fixup(expression) { 5233 this.resolved = expression; 5234 this.shared = true; 5235 } 5236} 5237/** 5238 * A constant pool allows a code emitter to share constant in an output context. 5239 * 5240 * The constant pool also supports sharing access to ivy definitions references. 5241 */ 5242class ConstantPool { 5243 constructor(isClosureCompilerEnabled = false) { 5244 this.isClosureCompilerEnabled = isClosureCompilerEnabled; 5245 this.statements = []; 5246 this.literals = new Map(); 5247 this.literalFactories = new Map(); 5248 this.sharedConstants = new Map(); 5249 /** 5250 * Constant pool also tracks claimed names from {@link uniqueName}. 5251 * This is useful to avoid collisions if variables are intended to be 5252 * named a certain way- but may conflict. We wouldn't want to always suffix 5253 * them with unique numbers. 5254 */ 5255 this._claimedNames = new Map(); 5256 this.nextNameIndex = 0; 5257 } 5258 getConstLiteral(literal, forceShared) { 5259 if (literal instanceof LiteralExpr && !isLongStringLiteral(literal) || literal instanceof FixupExpression) { 5260 // Do no put simple literals into the constant pool or try to produce a constant for a 5261 // reference to a constant. 5262 return literal; 5263 } 5264 const key = GenericKeyFn.INSTANCE.keyOf(literal); 5265 let fixup = this.literals.get(key); 5266 let newValue = false; 5267 if (!fixup) { 5268 fixup = new FixupExpression(literal); 5269 this.literals.set(key, fixup); 5270 newValue = true; 5271 } 5272 if (!newValue && !fixup.shared || newValue && forceShared) { 5273 // Replace the expression with a variable 5274 const name = this.freshName(); 5275 let definition; 5276 let usage; 5277 if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) { 5278 // For string literals, Closure will **always** inline the string at 5279 // **all** usages, duplicating it each time. For large strings, this 5280 // unnecessarily bloats bundle size. To work around this restriction, we 5281 // wrap the string in a function, and call that function for each usage. 5282 // This tricks Closure into using inline logic for functions instead of 5283 // string literals. Function calls are only inlined if the body is small 5284 // enough to be worth it. By doing this, very large strings will be 5285 // shared across multiple usages, rather than duplicating the string at 5286 // each usage site. 5287 // 5288 // const myStr = function() { return "very very very long string"; }; 5289 // const usage1 = myStr(); 5290 // const usage2 = myStr(); 5291 definition = variable(name).set(new FunctionExpr([], 5292 // Params. 5293 [ 5294 // Statements.
5295 new ReturnStatement(literal)])); 5296 usage = variable(name).callFn([]); 5297 } else { 5298 // Just declare and use the variable directly, without a function call 5299 // indirection. This saves a few bytes and avoids an unnecessary call. 5300 definition = variable(name).set(literal); 5301 usage = variable(name); 5302 } 5303 this.statements.push(definition.toDeclStmt(INFERRED_TYPE, StmtModifier.Final)); 5304 fixup.fixup(usage); 5305 } 5306 return fixup; 5307 } 5308 getSharedConstant(def, expr) { 5309 const key = def.keyOf(expr); 5310 if (!this.sharedConstants.has(key)) { 5311 const id = this.freshName(); 5312 this.sharedConstants.set(key, variable(id)); 5313 this.statements.push(def.toSharedConstantDeclaration(id, expr)); 5314 } 5315 return this.sharedConstants.get(key); 5316 } 5317 getLiteralFactory(literal) { 5318 // Create a pure function that builds an array of a mix of constant and variable expressions 5319 if (literal instanceof LiteralArrayExpr) { 5320 const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY); 5321 const key = GenericKeyFn.INSTANCE.keyOf(literalArr(argumentsForKey)); 5322 return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries)); 5323 } else { 5324 const expressionForKey = literalMap(literal.entries.map(e => ({ 5325 key: e.key, 5326 value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY, 5327 quoted: e.quoted 5328 }))); 5329 const key = GenericKeyFn.INSTANCE.keyOf(expressionForKey); 5330 return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({ 5331 key: literal.entries[index].key, 5332 value, 5333 quoted: literal.entries[index].quoted 5334 })))); 5335 } 5336 } 5337 // TODO: useUniqueName(false) is necessary for naming compatibility with 5338 // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default. 5339 getSharedFunctionReference(fn, prefix, useUniqueName = true) { 5340 const isArrow = fn instanceof ArrowFunctionExpr; 5341 for (const current of this.statements) { 5342 // Arrow functions are saved as variables so we check if the 5343 // value of the variable is the same as the arrow function. 5344 if (isArrow && current instanceof DeclareVarStmt && current.value?.isEquivalent(fn)) { 5345 return variable(current.name); 5346 } 5347 // Function declarations are saved as function statements 5348 // so we compare them directly to the passed-in function. 5349 if (!isArrow && current instanceof DeclareFunctionStmt && fn.isEquivalent(current)) { 5350 return variable(current.name); 5351 } 5352 } 5353 // Otherwise declare the function. 5354 const name = useUniqueName ? this.uniqueName(prefix) : prefix; 5355 this.statements.push(fn.toDeclStmt(name, StmtModifier.Final)); 5356 return variable(name); 5357 } 5358 _getLiteralFactory(key, values, resultMap) { 5359 let literalFactory = this.literalFactories.get(key); 5360 const literalFactoryArguments = values.filter(e => !e.isConstant()); 5361 if (!literalFactory) { 5362 const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`)); 5363 const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE)); 5364 const pureFunctionDeclaration = arrowFn(parameters, resultMap(resultExpressions), INFERRED_TYPE); 5365 const name = this.freshName(); 5366 this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, StmtModifier.Final)); 5367 literalFactory = variable(name); 5368 this.literalFactories.set(key, literalFactory); 5369 } 5370 return { 5371 literalFactory, 5372 literalFactoryArguments 5373 }; 5374 } 5375 /** 5376 * Produce a unique name in the context of this pool. 5377 * 5378 * The name might be unique among different prefixes if any of the prefixes end in 5379 * a digit so the prefix should be a constant string (not based on user input) and 5380 * must not end in a digit. 5381 */ 5382 uniqueName(name, alwaysIncludeSuffix = true) { 5383 const count = this._claimedNames.get(name) ?? 0; 5384 const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`; 5385 this._claimedNames.set(name, count + 1); 5386 return result; 5387 } 5388 freshName() { 5389 return this.uniqueName(CONSTANT_PREFIX); 5390 } 5391} 5392class GenericKeyFn { 5393 static #_ = this.INSTANCE = new GenericKeyFn(); 5394 keyOf(expr) { 5395 if (expr instanceof LiteralExpr && typeof expr.value === 'string') { 5396 return `"${expr.value}"`; 5397 } else if (expr instanceof LiteralExpr) { 5398 return String(expr.value); 5399 } else if (expr instanceof LiteralArrayExpr) { 5400 const entries = []; 5401 for (const entry of expr.entries) {
5402 entries.push(this.keyOf(entry)); 5403 } 5404 return `[${entries.join(',')}]`; 5405 } else if (expr instanceof LiteralMapExpr) { 5406 const entries = []; 5407 for (const entry of expr.entries) { 5408 let key = entry.key; 5409 if (entry.quoted) { 5410 key = `"${key}"`; 5411 } 5412 entries.push(key + ':' + this.keyOf(entry.value)); 5413 } 5414 return `{${entries.join(',')}}`; 5415 } else if (expr instanceof ExternalExpr) { 5416 return `import("${expr.value.moduleName}", ${expr.value.name})`; 5417 } else if (expr instanceof ReadVarExpr) { 5418 return `read(${expr.name})`; 5419 } else if (expr instanceof TypeofExpr) { 5420 return `typeof(${this.keyOf(expr.expr)})`; 5421 } else { 5422 throw new Error(`${this.constructor.name} does not handle expressions of type ${expr.constructor.name}`); 5423 } 5424 } 5425} 5426function isVariable(e) { 5427 return e instanceof ReadVarExpr; 5428} 5429function isLongStringLiteral(expr) { 5430 return expr instanceof LiteralExpr && typeof expr.value === 'string' && expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS; 5431} 5432const CORE = '@angular/core'; 5433class Identifiers { 5434 /* Methods */ 5435 static #_ = this.NEW_METHOD = 'factory'; 5436 static #_2 = this.TRANSFORM_METHOD = 'transform'; 5437 static #_3 = this.PATCH_DEPS = 'patchedDeps'; 5438 static #_4 = this.core = { 5439 name: null, 5440 moduleName: CORE 5441 }; 5442 /* Instructions */ 5443 static #_5 = this.namespaceHTML = { 5444 name: 'ɵɵnamespaceHTML', 5445 moduleName: CORE 5446 }; 5447 static #_6 = this.namespaceMathML = { 5448 name: 'ɵɵnamespaceMathML', 5449 moduleName: CORE 5450 }; 5451 static #_7 = this.namespaceSVG = { 5452 name: 'ɵɵnamespaceSVG', 5453 moduleName: CORE 5454 }; 5455 static #_8 = this.element = { 5456 name: 'ɵɵelement', 5457 moduleName: CORE 5458 }; 5459 static #_9 = this.elementStart = { 5460 name: 'ɵɵelementStart', 5461 moduleName: CORE 5462 }; 5463 static #_10 = this.elementEnd = { 5464 name: 'ɵɵelementEnd', 5465 moduleName: CORE 5466 }; 5467 static #_11 = this.advance = { 5468 name: 'ɵɵadvance', 5469 moduleName: CORE 5470 }; 5471 static #_12 = this.syntheticHostProperty = { 5472 name: 'ɵɵsyntheticHostProperty', 5473 moduleName: CORE 5474 }; 5475 static #_13 = this.syntheticHostListener = { 5476 name: 'ɵɵsyntheticHostListener', 5477 moduleName: CORE 5478 }; 5479 static #_14 = this.attribute = { 5480 name: 'ɵɵattribute', 5481 moduleName: CORE 5482 }; 5483 static #_15 = this.attributeInterpolate1 = { 5484 name: 'ɵɵattributeInterpolate1', 5485 moduleName: CORE 5486 }; 5487 static #_16 = this.attributeInterpolate2 = { 5488 name: 'ɵɵattributeInterpolate2', 5489 moduleName: CORE 5490 }; 5491 static #_17 = this.attributeInterpolate3 = { 5492 name: 'ɵɵattributeInterpolate3', 5493 moduleName: CORE 5494 }; 5495 static #_18 = this.attributeInterpolate4 = { 5496 name: 'ɵɵattributeInterpolate4', 5497 moduleName: CORE 5498 }; 5499 static #_19 = this.attributeInterpolate5 = { 5500 name: 'ɵɵattributeInterpolate5', 5501 moduleName: CORE 5502 }; 5503 static #_20 = this.attributeInterpolate6 = { 5504 name: 'ɵɵattributeInterpolate6', 5505 moduleName: CORE 5506 }; 5507 static #_21 = this.attributeInterpolate7 = { 5508 name: 'ɵɵattributeInterpolate7', 5509 moduleName: CORE 5510 }; 5511 static #_22 = this.attributeInterpolate8 = { 5512 name: 'ɵɵattributeInterpolate8', 5513 moduleName: CORE 5514 }; 5515 static #_23 = this.attributeInterpolateV = { 5516 name: 'ɵɵattributeInterpolateV', 5517 moduleName: CORE 5518 }; 5519 static #_24 = this.classProp = { 5520 name: 'ɵɵclassProp', 5521 moduleName: CORE 5522 }; 5523 static #_25 = this.elementContainerStart = { 5524 name: 'ɵɵelementContainerStart', 5525 moduleName: CORE 5526 }; 5527 static #_26 = this.elementContainerEnd = { 5528 name: 'ɵɵelementContainerEnd', 5529 moduleName: CORE 5530 }; 5531 static #_27 = this.elementContainer = { 5532 name: 'ɵɵelementContainer', 5533 moduleName: CORE 5534 }; 5535 static #_28 = this.styleMap = { 5536 name: 'ɵɵstyleMap', 5537 moduleName: CORE 5538 }; 5539 static #_29 = this.styleMapInterpolate1 = { 5540 name: 'ɵɵstyleMapInterpolate1', 5541 moduleName: CORE 5542 }; 5543 static #_30 = this.styleMapInterpolate2 = { 5544 name: 'ɵɵstyleMapInterpolate2', 5545 moduleName: CORE 5546 }; 5547 static #_31 = this.styleMapInterpolate3 = { 5548 name: 'ɵɵstyleMapInterpolate3', 5549 moduleName: CORE 5550 }; 5551 static #_32 = this.styleMapInterpolate4 = { 5552 name: 'ɵɵstyleMapInterpolate4', 5553 moduleName: CORE 5554 }; 5555 static #_33 = this.styleMapInterpolate5 = { 5556 name: 'ɵɵstyleMapInterpolate5', 5557 moduleName: CORE 5558 }; 5559 static #_34 = this.styleMapInterpolate6 = { 5560 name: 'ɵɵstyleMapInterpolate6', 5561 moduleName: CORE 5562 }; 5563 static #_35 = this.styleMapInterpolate7 = { 5564 name: 'ɵɵstyleMapInterpolate7', 5565 moduleName: CORE 5566 }; 5567 static #_36 = this.styleMapInterpolate8 = { 5568 name: 'ɵɵstyleMapInterpolate8', 5569 moduleName: CORE 5570 };
5571 static #_37 = this.styleMapInterpolateV = { 5572 name: 'ɵɵstyleMapInterpolateV', 5573 moduleName: CORE 5574 }; 5575 static #_38 = this.classMap = { 5576 name: 'ɵɵclassMap', 5577 moduleName: CORE 5578 }; 5579 static #_39 = this.classMapInterpolate1 = { 5580 name: 'ɵɵclassMapInterpolate1', 5581 moduleName: CORE 5582 }; 5583 static #_40 = this.classMapInterpolate2 = { 5584 name: 'ɵɵclassMapInterpolate2', 5585 moduleName: CORE 5586 }; 5587 static #_41 = this.classMapInterpolate3 = { 5588 name: 'ɵɵclassMapInterpolate3', 5589 moduleName: CORE 5590 }; 5591 static #_42 = this.classMapInterpolate4 = { 5592 name: 'ɵɵclassMapInterpolate4', 5593 moduleName: CORE 5594 }; 5595 static #_43 = this.classMapInterpolate5 = { 5596 name: 'ɵɵclassMapInterpolate5', 5597 moduleName: CORE 5598 }; 5599 static #_44 = this.classMapInterpolate6 = { 5600 name: 'ɵɵclassMapInterpolate6', 5601 moduleName: CORE 5602 }; 5603 static #_45 = this.classMapInterpolate7 = { 5604 name: 'ɵɵclassMapInterpolate7', 5605 moduleName: CORE 5606 }; 5607 static #_46 = this.classMapInterpolate8 = { 5608 name: 'ɵɵclassMapInterpolate8', 5609 moduleName: CORE 5610 }; 5611 static #_47 = this.classMapInterpolateV = { 5612 name: 'ɵɵclassMapInterpolateV', 5613 moduleName: CORE 5614 }; 5615 static #_48 = this.styleProp = { 5616 name: 'ɵɵstyleProp', 5617 moduleName: CORE 5618 }; 5619 static #_49 = this.stylePropInterpolate1 = { 5620 name: 'ɵɵstylePropInterpolate1', 5621 moduleName: CORE 5622 }; 5623 static #_50 = this.stylePropInterpolate2 = { 5624 name: 'ɵɵstylePropInterpolate2', 5625 moduleName: CORE 5626 }; 5627 static #_51 = this.stylePropInterpolate3 = { 5628 name: 'ɵɵstylePropInterpolate3', 5629 moduleName: CORE 5630 }; 5631 static #_52 = this.stylePropInterpolate4 = { 5632 name: 'ɵɵstylePropInterpolate4', 5633 moduleName: CORE 5634 }; 5635 static #_53 = this.stylePropInterpolate5 = { 5636 name: 'ɵɵstylePropInterpolate5', 5637 moduleName: CORE 5638 }; 5639 static #_54 = this.stylePropInterpolate6 = { 5640 name: 'ɵɵstylePropInterpolate6', 5641 moduleName: CORE 5642 }; 5643 static #_55 = this.stylePropInterpolate7 = { 5644 name: 'ɵɵstylePropInterpolate7', 5645 moduleName: CORE 5646 }; 5647 static #_56 = this.stylePropInterpolate8 = { 5648 name: 'ɵɵstylePropInterpolate8', 5649 moduleName: CORE 5650 }; 5651 static #_57 = this.stylePropInterpolateV = { 5652 name: 'ɵɵstylePropInterpolateV', 5653 moduleName: CORE 5654 }; 5655 static #_58 = this.nextContext = { 5656 name: 'ɵɵnextContext', 5657 moduleName: CORE 5658 }; 5659 static #_59 = this.resetView = { 5660 name: 'ɵɵresetView', 5661 moduleName: CORE 5662 }; 5663 static #_60 = this.templateCreate = { 5664 name: 'ɵɵtemplate', 5665 moduleName: CORE 5666 }; 5667 static #_61 = this.defer = { 5668 name: 'ɵɵdefer', 5669 moduleName: CORE 5670 }; 5671 static #_62 = this.deferWhen = { 5672 name: 'ɵɵdeferWhen', 5673 moduleName: CORE 5674 }; 5675 static #_63 = this.deferOnIdle = { 5676 name: 'ɵɵdeferOnIdle', 5677 moduleName: CORE 5678 }; 5679 static #_64 = this.deferOnImmediate = { 5680 name: 'ɵɵdeferOnImmediate', 5681 moduleName: CORE 5682 }; 5683 static #_65 = this.deferOnTimer = { 5684 name: 'ɵɵdeferOnTimer', 5685 moduleName: CORE 5686 }; 5687 static #_66 = this.deferOnHover = { 5688 name: 'ɵɵdeferOnHover', 5689 moduleName: CORE 5690 }; 5691 static #_67 = this.deferOnInteraction = { 5692 name: 'ɵɵdeferOnInteraction', 5693 moduleName: CORE 5694 }; 5695 static #_68 = this.deferOnViewport = { 5696 name: 'ɵɵdeferOnViewport', 5697 moduleName: CORE 5698 }; 5699 static #_69 = this.deferPrefetchWhen = { 5700 name: 'ɵɵdeferPrefetchWhen', 5701 moduleName: CORE 5702 }; 5703 static #_70 = this.deferPrefetchOnIdle = { 5704 name: 'ɵɵdeferPrefetchOnIdle', 5705 moduleName: CORE 5706 }; 5707 static #_71 = this.deferPrefetchOnImmediate = { 5708 name: 'ɵɵdeferPrefetchOnImmediate', 5709 moduleName: CORE 5710 }; 5711 static #_72 = this.deferPrefetchOnTimer = { 5712 name: 'ɵɵdeferPrefetchOnTimer', 5713 moduleName: CORE 5714 }; 5715 static #_73 = this.deferPrefetchOnHover = { 5716 name: 'ɵɵdeferPrefetchOnHover', 5717 moduleName: CORE 5718 }; 5719 static #_74 = this.deferPrefetchOnInteraction = { 5720 name: 'ɵɵdeferPrefetchOnInteraction', 5721 moduleName: CORE 5722 }; 5723 static #_75 = this.deferPrefetchOnViewport = { 5724 name: 'ɵɵdeferPrefetchOnViewport', 5725 moduleName: CORE 5726 }; 5727 static #_76 = this.deferEnableTimerScheduling = { 5728 name: 'ɵɵdeferEnableTimerScheduling', 5729 moduleName: CORE 5730 }; 5731 static #_77 = this.conditional = { 5732 name: 'ɵɵconditional', 5733 moduleName: CORE 5734 }; 5735 static #_78 = this.repeater = { 5736 name: 'ɵɵrepeater', 5737 moduleName: CORE 5738 }; 5739 static #_79 = this.repeaterCreate = { 5740 name: 'ɵɵrepeaterCreate', 5741 moduleName: CORE 5742 }; 5743 static #_80 = this.repeaterTrackByIndex = { 5744 name: 'ɵɵrepeaterTrackByIndex', 5745 moduleName: CORE 5746 }; 5747 static #_81 = this.repeaterTrackByIdentity = { 5748 name: 'ɵɵrepeaterTrackByIdentity', 5749 moduleName: CORE 5750 }; 5751 static #_82 = this.componentInstance = { 5752 name: 'ɵɵcomponentInstance', 5753 moduleName: CORE 5754 }; 5755 static #_83 = this.text = { 5756 name: 'ɵɵtext', 5757 moduleName: CORE 5758 }; 5759 static #_84 = this.enableBindings = { 5760 name: 'ɵɵenableBindings', 5761 moduleName: CORE 5762 }; 5763 static #_85 = this.disableBindings = { 5764 name: 'ɵɵdisableBindings', 5765 moduleName: CORE 5766 }; 5767 static #_86 = this.getCurrentView = { 5768 name: 'ɵɵgetCurrentView', 5769 moduleName: CORE 5770 }; 5771 static #_87 = this.textInterpolate = { 5772 name: 'ɵɵtextInterpolate', 5773 moduleName: CORE 5774 }; 5775 static #_88 = this.textInterpolate1 = { 5776 name: 'ɵɵtextInterpolate1', 5777 moduleName: CORE 5778 }; 5779 static #_89 = this.textInterpolate2 = { 5780 name: 'ɵɵtextInterpolate2', 5781 moduleName: CORE 5782 }; 5783 static #_90 = this.textInterpolate3 = { 5784 name: 'ɵɵtextInterpolate3', 5785 moduleName: CORE 5786 }; 5787 static #_91 = this.textInterpolate4 = { 5788 name: 'ɵɵtextInterpolate4', 5789 moduleName: CORE 5790 }; 5791 static #_92 = this.textInterpolate5 = { 5792 name: 'ɵɵtextInterpolate5', 5793 moduleName: CORE 5794 }; 5795 static #_93 = this.textInterpolate6 = { 5796 name: 'ɵɵtextInterpolate6', 5797 moduleName: CORE 5798 }; 5799 static #_94 = this.textInterpolate7 = { 5800 name: 'ɵɵtextInterpolate7', 5801 moduleName: CORE 5802 }; 5803 static #_95 = this.textInterpolate8 = { 5804 name: 'ɵɵtextInterpolate8', 5805 moduleName: CORE 5806 }; 5807 static #_96 = this.textInterpolateV = { 5808 name: 'ɵɵtextInterpolateV', 5809 moduleName: CORE 5810 }; 5811 static #_97 = this.restoreView = { 5812 name: 'ɵɵrestoreView', 5813 moduleName: CORE 5814 }; 5815 static #_98 = this.pureFunction0 = { 5816 name: 'ɵɵpureFunction0', 5817 moduleName: CORE 5818 }; 5819 static #_99 = this.pureFunction1 = { 5820 name: 'ɵɵpureFunction1', 5821 moduleName: CORE 5822 }; 5823 static #_100 = this.pureFunction2 = { 5824 name: 'ɵɵpureFunction2', 5825 moduleName: CORE 5826 }; 5827 static #_101 = this.pureFunction3 = { 5828 name: 'ɵɵpureFunction3', 5829 moduleName: CORE 5830 }; 5831 static #_102 = this.pureFunction4 = { 5832 name: 'ɵɵpureFunction4', 5833 moduleName: CORE 5834 }; 5835 static #_103 = this.pureFunction5 = { 5836 name: 'ɵɵpureFunction5', 5837 moduleName: CORE 5838 }; 5839 static #_104 = this.pureFunction6 = { 5840 name: 'ɵɵpureFunction6', 5841 moduleName: CORE 5842 }; 5843 static #_105 = this.pureFunction7 = { 5844 name: 'ɵɵpureFunction7', 5845 moduleName: CORE 5846 }; 5847 static #_106 = this.pureFunction8 = { 5848 name: 'ɵɵpureFunction8', 5849 moduleName: CORE 5850 }; 5851 static #_107 = this.pureFunctionV = { 5852 name: 'ɵɵpureFunctionV', 5853 moduleName: CORE 5854 }; 5855 static #_108 = this.pipeBind1 = { 5856 name: 'ɵɵpipeBind1', 5857 moduleName: CORE 5858 }; 5859 static #_109 = this.pipeBind2 = { 5860 name: 'ɵɵpipeBind2', 5861 moduleName: CORE 5862 }; 5863 static #_110 = this.pipeBind3 = { 5864 name: 'ɵɵpipeBind3', 5865 moduleName: CORE 5866 }; 5867 static #_111 = this.pipeBind4 = { 5868 name: 'ɵɵpipeBind4', 5869 moduleName: CORE 5870 }; 5871 static #_112 = this.pipeBindV = { 5872 name: 'ɵɵpipeBindV', 5873 moduleName: CORE 5874 }; 5875 static #_113 = this.hostProperty = { 5876 name: 'ɵɵhostProperty', 5877 moduleName: CORE 5878 }; 5879 static #_114 = this.property = { 5880 name: 'ɵɵproperty', 5881 moduleName: CORE 5882 }; 5883 static #_115 = this.propertyInterpolate = { 5884 name: 'ɵɵpropertyInterpolate', 5885 moduleName: CORE 5886 }; 5887 static #_116 = this.propertyInterpolate1 = { 5888 name: 'ɵɵpropertyInterpolate1', 5889 moduleName: CORE 5890 }; 5891 static #_117 = this.propertyInterpolate2 = { 5892 name: 'ɵɵpropertyInterpolate2', 5893 moduleName: CORE 5894 }; 5895 static #_118 = this.propertyInterpolate3 = { 5896 name: 'ɵɵpropertyInterpolate3', 5897 moduleName: CORE 5898 }; 5899 static #_119 = this.propertyInterpolate4 = { 5900 name: 'ɵɵpropertyInterpolate4', 5901 moduleName: CORE 5902 }; 5903 static #_120 = this.propertyInterpolate5 = { 5904 name: 'ɵɵpropertyInterpolate5', 5905 moduleName: CORE 5906 }; 5907 static #_121 = this.propertyInterpolate6 = { 5908 name: 'ɵɵpropertyInterpolate6', 5909 moduleName: CORE 5910 }; 5911 static #_122 = this.propertyInterpolate7 = { 5912 name: 'ɵɵpropertyInterpolate7', 5913 moduleName: CORE 5914 }; 5915 static #_123 = this.propertyInterpolate8 = { 5916 name: 'ɵɵpropertyInterpolate8', 5917 moduleName: CORE 5918 }; 5919 static #_124 = this.propertyInterpolateV = { 5920 name: 'ɵɵpropertyInterpolateV', 5921 moduleName: CORE 5922 }; 5923 static #_125 = this.i18n = { 5924 name: 'ɵɵi18n', 5925 moduleName: CORE 5926 }; 5927 static #_126 = this.i18nAttributes = { 5928 name: 'ɵɵi18nAttributes', 5929 moduleName: CORE 5930 }; 5931 static #_127 = this.i18nExp = { 5932 name: 'ɵɵi18nExp', 5933 moduleName: CORE 5934 }; 5935 static #_128 = this.i18nStart = { 5936 name: 'ɵɵi18nStart', 5937 moduleName: CORE 5938 }; 5939 static #_129 = this.i18nEnd = { 5940 name: 'ɵɵi18nEnd', 5941 moduleName: CORE 5942 }; 5943 static #_130 = this.i18nApply = { 5944 name: 'ɵɵi18nApply', 5945 moduleName: CORE 5946 }; 5947 static #_131 = this.i18nPostprocess = { 5948 name: 'ɵɵi18nPostprocess', 5949 moduleName: CORE 5950 }; 5951 static #_132 = this.pipe = { 5952 name: 'ɵɵpipe', 5953 moduleName: CORE 5954 }; 5955 static #_133 = this.projection = { 5956 name: 'ɵɵprojection', 5957 moduleName: CORE 5958 }; 5959 static #_134 = this.projectionDef = { 5960 name: 'ɵɵprojectionDef', 5961 moduleName: CORE 5962 }; 5963 static #_135 = this.reference = { 5964 name: 'ɵɵreference', 5965 moduleName: CORE 5966 }; 5967 static #_136 = this.inject = { 5968 name: 'ɵɵinject', 5969 moduleName: CORE 5970 }; 5971 static #_137 = this.injectAttribute = { 5972 name: 'ɵɵinjectAttribute', 5973 moduleName: CORE 5974 }; 5975 static #_138 = this.directiveInject = { 5976 name: 'ɵɵdirectiveInject', 5977 moduleName: CORE 5978 }; 5979 static #_139 = this.invalidFactory = { 5980 name: 'ɵɵinvalidFactory', 5981 moduleName: CORE 5982 }; 5983 static #_140 = this.invalidFactoryDep = { 5984 name: 'ɵɵinvalidFactoryDep', 5985 moduleName: CORE 5986 }; 5987 static #_141 = this.templateRefExtractor = { 5988 name: 'ɵɵtemplateRefExtractor', 5989 moduleName: CORE 5990 }; 5991 static #_142 = this.forwardRef = { 5992 name: 'forwardRef', 5993 moduleName: CORE 5994 }; 5995 static #_143 = this.resolveForwardRef = { 5996 name: 'resolveForwardRef', 5997 moduleName: CORE 5998 }; 5999 static #_144 = this.ɵɵdefineInjectable = { 6000 name: 'ɵɵdefineInjectable', 6001 moduleName: CORE 6002 }; 6003 static #_145 = this.declareInjectable = { 6004 name: 'ɵɵngDeclareInjectable', 6005 moduleName: CORE 6006 }; 6007 static #_146 = this.InjectableDeclaration = { 6008 name: 'ɵɵInjectableDeclaration', 6009 moduleName: CORE 6010 }; 6011 static #_147 = this.resolveWindow = { 6012 name: 'ɵɵresolveWindow', 6013 moduleName: CORE 6014 }; 6015 static #_148 = this.resolveDocument = { 6016 name: 'ɵɵresolveDocument', 6017 moduleName: CORE 6018 }; 6019 static #_149 = this.resolveBody = { 6020 name: 'ɵɵresolveBody', 6021 moduleName: CORE 6022 }; 6023 static #_150 = this.getComponentDepsFactory = { 6024 name: 'ɵɵgetComponentDepsFactory', 6025 moduleName: CORE 6026 }; 6027 static #_151 = this.defineComponent = { 6028 name: 'ɵɵdefineComponent', 6029 moduleName: CORE 6030 }; 6031 static #_152 = this.declareComponent = { 6032 name: 'ɵɵngDeclareComponent', 6033 moduleName: CORE 6034 }; 6035 static #_153 = this.setComponentScope = { 6036 name: 'ɵɵsetComponentScope', 6037 moduleName: CORE 6038 }; 6039 static #_154 = this.ChangeDetectionStrategy = { 6040 name: 'ChangeDetectionStrategy', 6041 moduleName: CORE 6042 }; 6043 static #_155 = this.ViewEncapsulation = { 6044 name: 'ViewEncapsulation', 6045 moduleName: CORE 6046 }; 6047 static #_156 = this.ComponentDeclaration = { 6048 name: 'ɵɵComponentDeclaration', 6049 moduleName: CORE 6050 }; 6051 static #_157 = this.FactoryDeclaration = { 6052 name: 'ɵɵFactoryDeclaration', 6053 moduleName: CORE 6054 }; 6055 static #_158 = this.declareFactory = { 6056 name: 'ɵɵngDeclareFactory', 6057 moduleName: CORE 6058 }; 6059 static #_159 = this.FactoryTarget = { 6060 name: 'ɵɵFactoryTarget', 6061 moduleName: CORE 6062 }; 6063 static #_160 = this.defineDirective = { 6064 name: 'ɵɵdefineDirective', 6065 moduleName: CORE 6066 }; 6067 static #_161 = this.declareDirective = { 6068 name: 'ɵɵngDeclareDirective', 6069 moduleName: CORE 6070 }; 6071 static #_162 = this.DirectiveDeclaration = { 6072 name: 'ɵɵDirectiveDeclaration', 6073 moduleName: CORE 6074 }; 6075 static #_163 = this.InjectorDef = { 6076 name: 'ɵɵInjectorDef', 6077 moduleName: CORE 6078 }; 6079 static #_164 = this.InjectorDeclaration = { 6080 name: 'ɵɵInjectorDeclaration', 6081 moduleName: CORE 6082 }; 6083 static #_165 = this.defineInjector = { 6084 name: 'ɵɵdefineInjector', 6085 moduleName: CORE 6086 }; 6087 static #_166 = this.declareInjector = { 6088 name: 'ɵɵngDeclareInjector', 6089 moduleName: CORE 6090 }; 6091 static #_167 = this.NgModuleDeclaration = { 6092 name: 'ɵɵNgModuleDeclaration', 6093 moduleName: CORE 6094 }; 6095 static #_168 = this.ModuleWithProviders = { 6096 name: 'ModuleWithProviders', 6097 moduleName: CORE 6098 }; 6099 static #_169 = this.defineNgModule = { 6100 name: 'ɵɵdefineNgModule', 6101 moduleName: CORE 6102 }; 6103 static #_170 = this.declareNgModule = { 6104 name: 'ɵɵngDeclareNgModule', 6105 moduleName: CORE 6106 }; 6107 static #_171 = this.setNgModuleScope = { 6108 name: 'ɵɵsetNgModuleScope', 6109 moduleName: CORE 6110 }; 6111 static #_172 = this.registerNgModuleType = { 6112 name: 'ɵɵregisterNgModuleType', 6113 moduleName: CORE 6114 }; 6115 static #_173 = this.PipeDeclaration = { 6116 name: 'ɵɵPipeDeclaration', 6117 moduleName: CORE 6118 }; 6119 static #_174 = this.definePipe = { 6120 name: 'ɵɵdefinePipe', 6121 moduleName: CORE 6122 }; 6123 static #_175 = this.declarePipe = { 6124 name: 'ɵɵngDeclarePipe', 6125 moduleName: CORE 6126 }; 6127 static #_176 = this.declareClassMetadata = { 6128 name: 'ɵɵngDeclareClassMetadata', 6129 moduleName: CORE 6130 }; 6131 static #_177 = this.setClassMetadata = { 6132 name: 'ɵsetClassMetadata', 6133 moduleName: CORE 6134 }; 6135 static #_178 = this.setClassMetadataAsync = { 6136 name: 'ɵsetClassMetadataAsync', 6137 moduleName: CORE 6138 }; 6139 static #_179 = this.setClassDebugInfo = { 6140 name: 'ɵsetClassDebugInfo', 6141 moduleName: CORE 6142 }; 6143 static #_180 = this.queryRefresh = { 6144 name: 'ɵɵqueryRefresh', 6145 moduleName: CORE 6146 }; 6147 static #_181 = this.viewQuery = { 6148 name: 'ɵɵviewQuery', 6149 moduleName: CORE 6150 }; 6151 static #_182 = this.loadQuery = { 6152 name: 'ɵɵloadQuery', 6153 moduleName: CORE 6154 }; 6155 static #_183 = this.contentQuery = { 6156 name: 'ɵɵcontentQuery', 6157 moduleName: CORE 6158 }; // Signal queries 6159 static #_184 = this.viewQuerySignal = { 6160 name: 'ɵɵviewQuerySignal', 6161 moduleName: CORE 6162 }; 6163 static #_185 = this.contentQuerySignal = { 6164 name: 'ɵɵcontentQuerySignal', 6165 moduleName: CORE 6166 }; 6167 static #_186 = this.queryAdvance = { 6168 name: 'ɵɵqueryAdvance', 6169 moduleName: CORE 6170 }; // Two-way bindings 6171 static #_187 = this.twoWayProperty = { 6172 name: 'ɵɵtwoWayProperty', 6173 moduleName: CORE 6174 }; 6175 static #_188 = this.twoWayBindingSet = { 6176 name: 'ɵɵtwoWayBindingSet', 6177 moduleName: CORE 6178 }; 6179 static #_189 = this.twoWayListener = { 6180 name: 'ɵɵtwoWayListener', 6181 moduleName: CORE 6182 }; 6183 static #_190 = this.NgOnChangesFeature = { 6184 name: 'ɵɵNgOnChangesFeature', 6185 moduleName: CORE 6186 }; 6187 static #_191 = this.InheritDefinitionFeature = { 6188 name: 'ɵɵInheritDefinitionFeature', 6189 moduleName: CORE 6190 }; 6191 static #_192 = this.CopyDefinitionFeature = { 6192 name: 'ɵɵCopyDefinitionFeature', 6193 moduleName: CORE 6194 }; 6195 static #_193 = this.StandaloneFeature = { 6196 name: 'ɵɵStandaloneFeature', 6197 moduleName: CORE 6198 }; 6199 static #_194 = this.ProvidersFeature = { 6200 name: 'ɵɵProvidersFeature', 6201 moduleName: CORE 6202 }; 6203 static #_195 = this.HostDirectivesFeature = { 6204 name: 'ɵɵHostDirectivesFeature', 6205 moduleName: CORE 6206 }; 6207 static #_196 = this.InputTransformsFeatureFeature = { 6208 name: 'ɵɵInputTransformsFeature', 6209 moduleName: CORE 6210 }; 6211 static #_197 = this.listener = { 6212 name: 'ɵɵlistener', 6213 m
6213oduleName: CORE 6214 }; 6215 static #_198 = this.getInheritedFactory = { 6216 name: 'ɵɵgetInheritedFactory', 6217 moduleName: CORE 6218 }; 6219 static #_199 = this.InputFlags = { 6220 name: 'ɵɵInputFlags', 6221 moduleName: CORE 6222 }; // sanitization-related functions 6223 static #_200 = this.sanitizeHtml = { 6224 name: 'ɵɵsanitizeHtml', 6225 moduleName: CORE 6226 }; 6227 static #_201 = this.sanitizeStyle = { 6228 name: 'ɵɵsanitizeStyle', 6229 moduleName: CORE 6230 }; 6231 static #_202 = this.sanitizeResourceUrl = { 6232 name: 'ɵɵsanitizeResourceUrl', 6233 moduleName: CORE 6234 }; 6235 static #_203 = this.sanitizeScript = { 6236 name: 'ɵɵsanitizeScript', 6237 moduleName: CORE 6238 }; 6239 static #_204 = this.sanitizeUrl = { 6240 name: 'ɵɵsanitizeUrl', 6241 moduleName: CORE 6242 }; 6243 static #_205 = this.sanitizeUrlOrResourceUrl = { 6244 name: 'ɵɵsanitizeUrlOrResourceUrl', 6245 moduleName: CORE 6246 }; 6247 static #_206 = this.trustConstantHtml = { 6248 name: 'ɵɵtrustConstantHtml', 6249 moduleName: CORE 6250 }; 6251 static #_207 = this.trustConstantResourceUrl = { 6252 name: 'ɵɵtrustConstantResourceUrl', 6253 moduleName: CORE 6254 }; 6255 static #_208 = this.validateIframeAttribute = { 6256 name: 'ɵɵvalidateIframeAttribute', 6257 moduleName: CORE 6258 }; // type-checking 6259 static #_209 = this.InputSignalBrandWriteType = { 6260 name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', 6261 moduleName: CORE 6262 }; 6263 static #_210 = this.UnwrapDirectiveSignalInputs = { 6264 name: 'ɵUnwrapDirectiveSignalInputs', 6265 moduleName: CORE 6266 }; 6267 static #_211 = this.unwrapWritableSignal = { 6268 name: 'ɵunwrapWritableSignal', 6269 moduleName: CORE 6270 }; 6271} 6272const DASH_CASE_REGEXP = /-+([a-z0-9])/g; 6273function dashCaseToCamelCase(input) { 6274 return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase()); 6275} 6276function splitAtColon(input, defaultValues) { 6277 return _splitAt(input, ':', defaultValues); 6278} 6279function splitAtPeriod(input, defaultValues) { 6280 return _splitAt(input, '.', defaultValues); 6281} 6282function _splitAt(input, character, defaultValues) { 6283 const characterIndex = input.indexOf(character); 6284 if (characterIndex == -1) return defaultValues; 6285 return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()]; 6286} 6287function noUndefined(val) { 6288 return val === undefined ? null : val; 6289} 6290function error(msg) { 6291 throw new Error(`Internal Error: ${msg}`); 6292} 6293// Escape characters that have a special meaning in Regular Expressions 6294function escapeRegExp(s) { 6295 return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 6296} 6297function utf8Encode(str) { 6298 let encoded = []; 6299 for (let index = 0; index < str.length; index++) {
6300 let codePoint = str.charCodeAt(index); 6301 // decode surrogate 6302 // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae 6303 if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > index + 1) { 6304 const low = str.charCodeAt(index + 1); 6305 if (low >= 0xdc00 && low <= 0xdfff) { 6306 index++; 6307 codePoint = (codePoint - 0xd800 << 10) + low - 0xdc00 + 0x10000; 6308 } 6309 } 6310 if (codePoint <= 0x7f) { 6311 encoded.push(codePoint); 6312 } else if (codePoint <= 0x7ff) { 6313 encoded.push(codePoint >> 6 & 0x1F | 0xc0, codePoint & 0x3f | 0x80); 6314 } else if (codePoint <= 0xffff) { 6315 encoded.push(codePoint >> 12 | 0xe0, codePoint >> 6 & 0x3f | 0x80, codePoint & 0x3f | 0x80); 6316 } else if (codePoint <= 0x1fffff) { 6317 encoded.push(codePoint >> 18 & 0x07 | 0xf0, codePoint >> 12 & 0x3f | 0x80, codePoint >> 6 & 0x3f | 0x80, codePoint & 0x3f | 0x80); 6318 } 6319 } 6320 return encoded; 6321} 6322function stringify(token) { 6323 if (typeof token === 'string') { 6324 return token; 6325 } 6326 if (Array.isArray(token)) { 6327 return '[' + token.map(stringify).join(', ') + ']'; 6328 } 6329 if (token == null) { 6330 return '' + token; 6331 } 6332 if (token.overriddenName) { 6333 return `${token.overriddenName}`; 6334 } 6335 if (token.name) { 6336 return `${token.name}`; 6337 } 6338 if (!token.toString) { 6339 return 'object'; 6340 } 6341 // WARNING: do not try to `JSON.stringify(token)` here 6342 // see https://github.com/angular/angular/issues/23440 6343 const res = token.toString(); 6344 if (res == null) { 6345 return '' + res; 6346 } 6347 const newLineIndex = res.indexOf('\n'); 6348 return newLineIndex === -1 ? res : res.substring(0, newLineIndex); 6349} 6350class Version { 6351 constructor(full) { 6352 this.full = full; 6353 const splits = full.split('.'); 6354 this.major = splits[0]; 6355 this.minor = splits[1]; 6356 this.patch = splits.slice(2).join('.'); 6357 } 6358} 6359const _global = globalThis; 6360function newArray(size, value) { 6361 const list = []; 6362 for (let i = 0; i < size; i++) { 6363 list.push(value); 6364 } 6365 return list; 6366} 6367/** 6368 * Partitions a given array into 2 arrays, based on a boolean value returned by the condition 6369 * function. 6370 * 6371 * @param arr Input array that should be partitioned 6372 * @param conditionFn Condition function that is called for each item in a given array and returns a 6373 * boolean value. 6374 */ 6375function partitionArray(arr, conditionFn) { 6376 const truthy = []; 6377 const falsy = []; 6378 for (const item of arr) { 6379 (conditionFn(item) ? truthy : falsy).push(item); 6380 } 6381 return [truthy, falsy]; 6382} 6383 6384// https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit 6385const VERSION$1 = 3; 6386const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,'; 6387class SourceMapGenerator { 6388 constructor(file = null) { 6389 this.file = file; 6390 this.sourcesContent = new Map(); 6391 this.lines = []; 6392 this.lastCol0 = 0; 6393 this.hasMappings = false; 6394 } 6395 // The content is `null` when the content is expected to be loaded using the URL 6396 addSource(url, content = null) { 6397 if (!this.sourcesContent.has(url)) { 6398 this.sourcesContent.set(url, content); 6399 } 6400 return this; 6401 } 6402 addLine() { 6403 this.lines.push([]); 6404 this.lastCol0 = 0; 6405 return this; 6406 } 6407 addMapping(col0, sourceUrl, sourceLine0, sourceCol0) { 6408 if (!this.currentLine) { 6409 throw new Error(`A line must be added before mappings can be added`); 6410 } 6411 if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) { 6412 throw new Error(`Unknown source file "${sourceUrl}"`); 6413 } 6414 if (col0 == null) { 6415 throw new Error(`The column in the generated code must be provided`); 6416 } 6417 if (col0 < this.lastCol0) { 6418 throw new Error(`Mapping should be added in output order`); 6419 } 6420 if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) { 6421 throw new Error(`The source location must be provided when a source url is provided`); 6422 } 6423 this.hasMappings = true; 6424 this.lastCol0 = col0; 6425 this.currentLine.push({ 6426 col0, 6427 sourceUrl, 6428 sourceLine0, 6429 sourceCol0 6430 }); 6431 return this; 6432 } 6433 /** 6434 * @internal strip this from published d.ts files due to 6435 * https://github.com/microsoft/TypeScript/issues/36216 6436 */ 6437 get currentLine() { 6438 return this.lines.slice(-1)[0]; 6439 } 6440 toJSON() { 6441 if (!this.hasMappings) { 6442 return null; 6443 } 6444 const sourcesIndex = new Map(); 6445 const sources = []; 6446 const sourcesContent = [];
6447 Array.from(this.sourcesContent.keys()).forEach((url, i) => { 6448 sourcesIndex.set(url, i); 6449 sources.push(url); 6450 sourcesContent.push(this.sourcesContent.get(url) || null); 6451 }); 6452 let mappings = ''; 6453 let lastCol0 = 0; 6454 let lastSourceIndex = 0; 6455 let lastSourceLine0 = 0; 6456 let lastSourceCol0 = 0; 6457 this.lines.forEach(segments => { 6458 lastCol0 = 0; 6459 mappings += segments.map(segment => { 6460 // zero-based starting column of the line in the generated code 6461 let segAsStr = toBase64VLQ(segment.col0 - lastCol0); 6462 lastCol0 = segment.col0; 6463 if (segment.sourceUrl != null) { 6464 // zero-based index into the âsourcesâ list 6465 segAsStr += toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex); 6466 lastSourceIndex = sourcesIndex.get(segment.sourceUrl); 6467 // the zero-based starting line in the original source 6468 segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0); 6469 lastSourceLine0 = segment.sourceLine0; 6470 // the zero-based starting column in the original source 6471 segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0); 6472 lastSourceCol0 = segment.sourceCol0; 6473 } 6474 return segAsStr; 6475 }).join(','); 6476 mappings += ';'; 6477 }); 6478 mappings = mappings.slice(0, -1); 6479 return { 6480 'file': this.file || '', 6481 'version': VERSION$1, 6482 'sourceRoot': '', 6483 'sources': sources, 6484 'sourcesContent': sourcesContent, 6485 'mappings': mappings 6486 }; 6487 } 6488 toJsComment() { 6489 return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) : ''; 6490 } 6491} 6492function toBase64String(value) { 6493 let b64 = ''; 6494 const encoded = utf8Encode(value); 6495 for (let i = 0; i < encoded.length;) { 6496 const i1 = encoded[i++]; 6497 const i2 = i < encoded.length ? encoded[i++] : null; 6498 const i3 = i < encoded.length ? encoded[i++] : null; 6499 b64 += toBase64Digit(i1 >> 2); 6500 b64 += toBase64Digit((i1 & 3) << 4 | (i2 === null ? 0 : i2 >> 4)); 6501 b64 += i2 === null ? '=' : toBase64Digit((i2 & 15) << 2 | (i3 === null ? 0 : i3 >> 6)); 6502 b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63); 6503 } 6504 return b64; 6505} 6506function toBase64VLQ(value) { 6507 value = value < 0 ? (-value << 1) + 1 : value << 1; 6508 let out = ''; 6509 do { 6510 let digit = value & 31; 6511 value = value >> 5; 6512 if (value > 0) { 6513 digit = digit | 32; 6514 } 6515 out += toBase64Digit(digit); 6516 } while (value > 0); 6517 return out; 6518} 6519const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 6520function toBase64Digit(value) { 6521 if (value < 0 || value >= 64) { 6522 throw new Error(`Can only encode value in the range [0, 63]`); 6523 } 6524 return B64_DIGITS[value]; 6525} 6526const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g; 6527const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i; 6528const _INDENT_WITH = ' '; 6529class _EmittedLine { 6530 constructor(indent) { 6531 this.indent = indent; 6532 this.partsLength = 0; 6533 this.parts = []; 6534 this.srcSpans = []; 6535 } 6536} 6537class EmitterVisitorContext { 6538 static createRoot() { 6539 return new EmitterVisitorContext(0); 6540 } 6541 constructor(_indent) { 6542 this._indent = _indent; 6543 this._lines = [new _EmittedLine(_indent)]; 6544 } 6545 /** 6546 * @internal strip this from published d.ts files due to 6547 * https://github.com/microsoft/TypeScript/issues/36216 6548 */ 6549 get _currentLine() { 6550 return this._lines[this._lines.length - 1]; 6551 } 6552 println(from, lastPart = '') { 6553 this.print(from || null, lastPart, true); 6554 } 6555 lineIsEmpty() { 6556 return this._currentLine.parts.length === 0; 6557 } 6558 lineLength() { 6559 return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength; 6560 } 6561 print(from, part, newLine = false) { 6562 if (part.length > 0) { 6563 this._currentLine.parts.push(part); 6564 this._currentLine.partsLength += part.length; 6565 this._currentLine.srcSpans.push(from && from.sourceSpan || null); 6566 } 6567 if (newLine) { 6568 this._lines.push(new _EmittedLine(this._indent)); 6569 } 6570 } 6571 removeEmptyLastLine() { 6572 if (this.lineIsEmpty()) { 6573 this._lines.pop(); 6574 } 6575 } 6576 incIndent() { 6577 this._indent++; 6578 if (this.lineIsEmpty()) { 6579 this._currentLine.indent = this._indent; 6580 } 6581 } 6582 decIndent() { 6583 this._indent--; 6584 if (this.lineIsEmpty()) { 6585 this._currentLine.indent = this._indent; 6586 } 6587 } 6588 toSource() { 6589 return this.sourceLines.map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '').join('\n'); 6590 } 6591 toSourceMapGenerator(genFilePath, startsAtLine = 0) { 6592 const map = new SourceMapGenerator(genFilePath); 6593 let firstOffsetMapped = false;
6594 const mapFirstOffsetIfNeeded = () => { 6595 if (!firstOffsetMapped) { 6596 // Add a single space so that tools won't try to load the file from disk. 6597 // Note: We are using virtual urls like `ng:///`, so we have to 6598 // provide a content here. 6599 map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0); 6600 firstOffsetMapped = true; 6601 } 6602 }; 6603 for (let i = 0; i < startsAtLine; i++) { 6604 map.addLine(); 6605 mapFirstOffsetIfNeeded(); 6606 } 6607 this.sourceLines.forEach((line, lineIdx) => { 6608 map.addLine(); 6609 const spans = line.srcSpans; 6610 const parts = line.parts; 6611 let col0 = line.indent * _INDENT_WITH.length; 6612 let spanIdx = 0; 6613 // skip leading parts without source spans 6614 while (spanIdx < spans.length && !spans[spanIdx]) { 6615 col0 += parts[spanIdx].length; 6616 spanIdx++; 6617 } 6618 if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) { 6619 firstOffsetMapped = true; 6620 } else { 6621 mapFirstOffsetIfNeeded(); 6622 } 6623 while (spanIdx < spans.length) { 6624 const span = spans[spanIdx]; 6625 const source = span.start.file; 6626 const sourceLine = span.start.line; 6627 const sourceCol = span.start.col; 6628 map.addSource(source.url, source.content).addMapping(col0, source.url, sourceLine, sourceCol); 6629 col0 += parts[spanIdx].length; 6630 spanIdx++; 6631 // assign parts without span or the same span to the previous segment 6632 while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) { 6633 col0 += parts[spanIdx].length; 6634 spanIdx++; 6635 } 6636 } 6637 }); 6638 return map; 6639 } 6640 spanOf(line, column) { 6641 const emittedLine = this._lines[line]; 6642 if (emittedLine) { 6643 let columnsLeft = column - _createIndent(emittedLine.indent).length; 6644 for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) { 6645 const part = emittedLine.parts[partIndex]; 6646 if (part.length > columnsLeft) { 6647 return emittedLine.srcSpans[partIndex]; 6648 } 6649 columnsLeft -= part.length; 6650 } 6651 } 6652 return null; 6653 } 6654 /** 6655 * @internal strip this from published d.ts files due to 6656 * https://github.com/microsoft/TypeScript/issues/36216 6657 */ 6658 get sourceLines() { 6659 if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) { 6660 return this._lines.slice(0, -1); 6661 } 6662 return this._lines; 6663 } 6664} 6665class AbstractEmitterVisitor { 6666 constructor(_escapeDollarInStrings) { 6667 this._escapeDollarInStrings = _escapeDollarInStrings; 6668 } 6669 printLeadingComments(stmt, ctx) { 6670 if (stmt.leadingComments === undefined) { 6671 return; 6672 } 6673 for (const comment of stmt.leadingComments) { 6674 if (comment instanceof JSDocComment) { 6675 ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline); 6676 } else { 6677 if (comment.multiline) { 6678 ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline); 6679 } else { 6680 comment.text.split('\n').forEach(line => { 6681 ctx.println(stmt, `// ${line}`); 6682 }); 6683 } 6684 } 6685 } 6686 } 6687 visitExpressionStmt(stmt, ctx) { 6688 this.printLeadingComments(stmt, ctx); 6689 stmt.expr.visitExpression(this, ctx); 6690 ctx.println(stmt, ';'); 6691 return null; 6692 } 6693 visitReturnStmt(stmt, ctx) { 6694 this.printLeadingComments(stmt, ctx); 6695 ctx.print(stmt, `return `); 6696 stmt.value.visitExpression(this, ctx); 6697 ctx.println(stmt, ';'); 6698 return null; 6699 } 6700 visitIfStmt(stmt, ctx) { 6701 this.printLeadingComments(stmt, ctx); 6702 ctx.print(stmt, `if (`); 6703 stmt.condition.visitExpression(this, ctx); 6704 ctx.print(stmt, `) {`); 6705 const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0; 6706 if (stmt.trueCase.length <= 1 && !hasElseCase) { 6707 ctx.print(stmt, ` `); 6708 this.visitAllStatements(stmt.trueCase, ctx); 6709 ctx.removeEmptyLastLine(); 6710 ctx.print(stmt, ` `); 6711 } else { 6712 ctx.println(); 6713 ctx.incIndent(); 6714 this.visitAllStatements(stmt.trueCase, ctx); 6715 ctx.decIndent(); 6716 if (hasElseCase) { 6717 ctx.println(stmt, `} else {`); 6718 ctx.incIndent(); 6719 this.visitAllStatements(stmt.falseCase, ctx); 6720 ctx.decIndent(); 6721 } 6722 } 6723 ctx.println(stmt, `}`); 6724 return null; 6725 }
6726 visitWriteVarExpr(expr, ctx) { 6727 const lineWasEmpty = ctx.lineIsEmpty(); 6728 if (!lineWasEmpty) { 6729 ctx.print(expr, '('); 6730 } 6731 ctx.print(expr, `${expr.name} = `); 6732 expr.value.visitExpression(this, ctx); 6733 if (!lineWasEmpty) { 6734 ctx.print(expr, ')'); 6735 } 6736 return null; 6737 } 6738 visitWriteKeyExpr(expr, ctx) { 6739 const lineWasEmpty = ctx.lineIsEmpty(); 6740 if (!lineWasEmpty) { 6741 ctx.print(expr, '('); 6742 } 6743 expr.receiver.visitExpression(this, ctx); 6744 ctx.print(expr, `[`); 6745 expr.index.visitExpression(this, ctx); 6746 ctx.print(expr, `] = `); 6747 expr.value.visitExpression(this, ctx); 6748 if (!lineWasEmpty) { 6749 ctx.print(expr, ')'); 6750 } 6751 return null; 6752 } 6753 visitWritePropExpr(expr, ctx) { 6754 const lineWasEmpty = ctx.lineIsEmpty(); 6755 if (!lineWasEmpty) { 6756 ctx.print(expr, '('); 6757 } 6758 expr.receiver.visitExpression(this, ctx); 6759 ctx.print(expr, `.${expr.name} = `); 6760 expr.value.visitExpression(this, ctx); 6761 if (!lineWasEmpty) { 6762 ctx.print(expr, ')'); 6763 } 6764 return null; 6765 } 6766 visitInvokeFunctionExpr(expr, ctx) { 6767 const shouldParenthesize = expr.fn instanceof ArrowFunctionExpr; 6768 if (shouldParenthesize) { 6769 ctx.print(expr.fn, '('); 6770 } 6771 expr.fn.visitExpression(this, ctx); 6772 if (shouldParenthesize) { 6773 ctx.print(expr.fn, ')'); 6774 } 6775 ctx.print(expr, `(`); 6776 this.visitAllExpressions(expr.args, ctx, ','); 6777 ctx.print(expr, `)`); 6778 return null; 6779 } 6780 visitTaggedTemplateExpr(expr, ctx) { 6781 expr.tag.visitExpression(this, ctx); 6782 ctx.print(expr, '`' + expr.template.elements[0].rawText); 6783 for (let i = 1; i < expr.template.elements.length; i++) { 6784 ctx.print(expr, '${'); 6785 expr.template.expressions[i - 1].visitExpression(this, ctx); 6786 ctx.print(expr, `}${expr.template.elements[i].rawText}`); 6787 } 6788 ctx.print(expr, '`'); 6789 return null; 6790 } 6791 visitWrappedNodeExpr(ast, ctx) { 6792 throw new Error('Abstract emitter cannot visit WrappedNodeExpr.'); 6793 } 6794 visitTypeofExpr(expr, ctx) { 6795 ctx.print(expr, 'typeof '); 6796 expr.expr.visitExpression(this, ctx); 6797 } 6798 visitReadVarExpr(ast, ctx) { 6799 ctx.print(ast, ast.name); 6800 return null; 6801 } 6802 visitInstantiateExpr(ast, ctx) { 6803 ctx.print(ast, `new `); 6804 ast.classExpr.visitExpression(this, ctx); 6805 ctx.print(ast, `(`); 6806 this.visitAllExpressions(ast.args, ctx, ','); 6807 ctx.print(ast, `)`); 6808 return null; 6809 } 6810 visitLiteralExpr(ast, ctx) { 6811 const value = ast.value; 6812 if (typeof value === 'string') { 6813 ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings)); 6814 } else { 6815 ctx.print(ast, `${value}`); 6816 } 6817 return null; 6818 } 6819 visitLocalizedString(ast, ctx) { 6820 const head = ast.serializeI18nHead(); 6821 ctx.print(ast, '$localize `' + head.raw); 6822 for (let i = 1; i < ast.messageParts.length; i++) { 6823 ctx.print(ast, '${'); 6824 ast.expressions[i - 1].visitExpression(this, ctx); 6825 ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`); 6826 } 6827 ctx.print(ast, '`'); 6828 return null; 6829 } 6830 visitConditionalExpr(ast, ctx) { 6831 ctx.print(ast, `(`); 6832 ast.condition.visitExpression(this, ctx); 6833 ctx.print(ast, '? '); 6834 ast.trueCase.visitExpression(this, ctx); 6835 ctx.print(ast, ': '); 6836 ast.falseCase.visitExpression(this, ctx); 6837 ctx.print(ast, `)`); 6838 return null; 6839 } 6840 visitDynamicImportExpr(ast, ctx) { 6841 ctx.print(ast, `import(${ast.url})`); 6842 } 6843 visitNotExpr(ast, ctx) { 6844 ctx.print(ast, '!'); 6845 ast.condition.visitExpression(this, ctx); 6846 return null; 6847 } 6848 visitUnaryOperatorExpr(ast, ctx) { 6849 let opStr; 6850 switch (ast.operator) { 6851 case UnaryOperator.Plus: 6852 opStr = '+'; 6853 break; 6854 case UnaryOperator.Minus: 6855 opStr = '-'; 6856 break; 6857 default: 6858 throw new Error(`Unknown operator ${ast.operator}`); 6859 } 6860 if (ast.parens) ctx.print(ast, `(`); 6861 ctx.print(ast, opStr); 6862 ast.expr.visitExpression(this, ctx); 6863 if (ast.parens) ctx.print(ast, `)`); 6864 return null; 6865 } 6866 visitBinaryOperatorExpr(ast, ctx) { 6867 let opStr; 6868 switch (ast.operator) { 6869 case BinaryOperator.Equals: 6870 opStr = '=='; 6871 break; 6872 case BinaryOperator.Identical: 6873 opStr = '==='; 6874 break; 6875 case BinaryOperator.NotEquals: 6876 opStr = '!='; 6877 break; 6878 case BinaryOperator.NotIdentical: 6879 opStr = '!=='; 6880 break; 6881 case BinaryOperator.And: 6882 opStr = '&&'; 6883 break; 6884 case BinaryOperator.BitwiseOr: 6885 opStr = '|'; 6886 break; 6887 case BinaryOperator.BitwiseAnd: 6888 opStr = '&'; 6889 break; 6890 case BinaryOperator.Or: 6891 opStr = '||'; 6892 break; 6893 case BinaryOperator.Plus: 6894 opStr = '+'; 6895 break; 6896 case BinaryOperator.Minus: 6897 opStr = '-'; 6898 break; 6899 case BinaryOperator.Divide: 6900 opStr = '/'; 6901 break; 6902 case BinaryOperator.Multiply: 6903 opStr = '*'; 6904 break; 6905 case BinaryOperator.Modulo: 6906 opStr = '%'; 6907 break; 6908 case BinaryOperator.Lower: 6909 opStr = '<'; 6910 break; 6911 case BinaryOperator.LowerEquals: 6912 opStr = '<='; 6913 break; 6914 case BinaryOperator.Bigger: 6915 opStr = '>'; 6916 break; 6917 case BinaryOperator.BiggerEquals: 6918 opStr = '>='; 6919 break; 6920 case BinaryOperator.NullishCoalesce: 6921 opStr = '??'; 6922 break; 6923 default: 6924 throw new Error(`Unknown operator ${ast.operator}`); 6925 } 6926 if (ast.parens) ctx.print(ast, `(`); 6927 ast.lhs.visitExpression(this, ctx); 6928 ctx.print(ast, ` ${opStr} `); 6929 ast.rhs.visitExpression(this, ctx); 6930 if (ast.parens) ctx.print(ast, `)`); 6931 return null; 6932 } 6933 visitReadPropExpr(ast, ctx) { 6934 ast.receiver.visitExpression(this, ctx); 6935 ctx.print(ast, `.`); 6936 ctx.print(ast, ast.name); 6937 return null; 6938 } 6939 visitReadKeyExpr(ast, ctx) { 6940 ast.receiver.visitExpression(this, ctx); 6941 ctx.print(ast, `[`); 6942 ast.index.visitExpression(this, ctx); 6943 ctx.print(ast, `]`); 6944 return null; 6945 } 6946 visitLiteralArrayExpr(ast, ctx) { 6947 ctx.print(ast, `[`); 6948 this.visitAllExpressions(ast.entries, ctx, ','); 6949 ctx.print(ast, `]`); 6950 return null; 6951 } 6952 visitLiteralMapExpr(ast, ctx) { 6953 ctx.print(ast, `{`); 6954 this.visitAllObjects(entry => { 6955 ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`); 6956 entry.value.visitExpression(this, ctx); 6957 }, ast.entries, ctx, ','); 6958 ctx.print(ast, `}`); 6959 return null; 6960 } 6961 visitCommaExpr(ast, ctx) { 6962 ctx.print(ast, '('); 6963 this.visitAllExpressions(ast.parts, ctx, ','); 6964 ctx.print(ast, ')'); 6965 return null; 6966 } 6967 visitAllExpressions(expressions, ctx, separator) { 6968 this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator); 6969 } 6970 visitAllObjects(handler, expressions, ctx, separator) { 6971 let incrementedIndent = false;
6972 for (let i = 0; i < expressions.length; i++) { 6973 if (i > 0) { 6974 if (ctx.lineLength() > 80) { 6975 ctx.print(null, separator, true); 6976 if (!incrementedIndent) { 6977 // continuation are marked with double indent. 6978 ctx.incIndent(); 6979 ctx.incIndent(); 6980 incrementedIndent = true; 6981 } 6982 } else { 6983 ctx.print(null, separator, false); 6984 } 6985 } 6986 handler(expressions[i]); 6987 } 6988 if (incrementedIndent) { 6989 // continuation are marked with double indent. 6990 ctx.decIndent(); 6991 ctx.decIndent(); 6992 } 6993 } 6994 visitAllStatements(statements, ctx) { 6995 statements.forEach(stmt => stmt.visitStatement(this, ctx)); 6996 } 6997} 6998function escapeIdentifier(input, escapeDollar, alwaysQuote = true) { 6999 if (input == null) { 7000 return null; 7001 } 7002 const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => { 7003 if (match[0] == '$') { 7004 return escapeDollar ? '\\$' : '$'; 7005 } else if (match[0] == '\n') { 7006 return '\\n'; 7007 } else if (match[0] == '\r') { 7008 return '\\r'; 7009 } else { 7010 return `\\${match[0]}`; 7011 } 7012 }); 7013 const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body); 7014 return requiresQuotes ? `'${body}'` : body; 7015} 7016function _createIndent(count) { 7017 let res = ''; 7018 for (let i = 0; i < count; i++) { 7019 res += _INDENT_WITH; 7020 } 7021 return res; 7022} 7023function typeWithParameters(type, numParams) { 7024 if (numParams === 0) { 7025 return expressionType(type); 7026 } 7027 const params = []; 7028 for (let i = 0; i < numParams; i++) { 7029 params.push(DYNAMIC_TYPE); 7030 } 7031 return expressionType(type, undefined, params); 7032} 7033const ANIMATE_SYMBOL_PREFIX = '@'; 7034function prepareSyntheticPropertyName(name) { 7035 return `${ANIMATE_SYMBOL_PREFIX}${name}`; 7036} 7037function prepareSyntheticListenerName(name, phase) { 7038 return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`; 7039} 7040function getSafePropertyAccessString(accessor, name) { 7041 const escapedName = escapeIdentifier(name, false, false); 7042 return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`; 7043} 7044function prepareSyntheticListenerFunctionName(name, phase) { 7045 return `animation_${name}_${phase}`; 7046} 7047function jitOnlyGuardedExpression(expr) { 7048 return guardedExpression('ngJitMode', expr); 7049} 7050function devOnlyGuardedExpression(expr) { 7051 return guardedExpression('ngDevMode', expr); 7052} 7053function guardedExpression(guard, expr) { 7054 const guardExpr = new ExternalExpr({ 7055 name: guard, 7056 moduleName: null 7057 }); 7058 const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined')); 7059 const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, /* type */undefined, /* sourceSpan */undefined, true); 7060 return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr); 7061} 7062function wrapReference(value) { 7063 const wrapped = new WrappedNodeExpr(value); 7064 return { 7065 value: wrapped, 7066 type: wrapped 7067 }; 7068} 7069function refsToArray(refs, shouldForwardDeclare) { 7070 const values = literalArr(refs.map(ref => ref.value)); 7071 return shouldForwardDeclare ? arrowFn([], values) : values; 7072} 7073function createMayBeForwardRefExpression(expression, forwardRef) { 7074 return { 7075 expression, 7076 forwardRef 7077 }; 7078} 7079/** 7080 * Convert a `MaybeForwardRefExpression` to an `Expression`, possibly wrapping its expression in a 7081 * `forwardRef()` call. 7082 * 7083 * If `MaybeForwardRefExpression.forwardRef` is `ForwardRefHandling.Unwrapped` then the expression 7084 * was originally wrapped in a `forwardRef()` call to prevent the value from being eagerly evaluated 7085 * in the code. 7086 * 7087 * See `packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts` and 7088 * `packages/compiler/src/jit_compiler_facade.ts` for more information. 7089 */ 7090function convertFromMaybeForwardRefExpression({ 7091 expression, 7092 forwardRef 7093}) { 7094 switch (forwardRef) { 7095 case 0 /* ForwardRefHandling.None */: 7096 case 1 /* ForwardRefHandling.Wrapped */: 7097 return expression; 7098 case 2 /* ForwardRefHandling.Unwrapped */: 7099 return generateForwardRef(expression); 7100 } 7101} 7102/** 7103 * Generate an expression that has the given `expr` wrapped in the following form: 7104 * 7105 * ``` 7106 * forwardRef(() => expr) 7107 * ``` 7108 */
7109function generateForwardRef(expr) { 7110 return importExpr(Identifiers.forwardRef).callFn([arrowFn([], expr)]); 7111} 7112var R3FactoryDelegateType; 7113(function (R3FactoryDelegateType) { 7114 R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class"; 7115 R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function"; 7116})(R3FactoryDelegateType || (R3FactoryDelegateType = {})); 7117var FactoryTarget$1; 7118(function (FactoryTarget) { 7119 FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive"; 7120 FactoryTarget[FactoryTarget["Component"] = 1] = "Component"; 7121 FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable"; 7122 FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe"; 7123 FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule"; 7124})(FactoryTarget$1 || (FactoryTarget$1 = {})); 7125/** 7126 * Construct a factory function expression for the given `R3FactoryMetadata`. 7127 */ 7128function compileFactoryFunction(meta) { 7129 const t = variable('t'); 7130 let baseFactoryVar = null; 7131 // The type to instantiate via constructor invocation. If there is no delegated factory, meaning 7132 // this type is always created by constructor invocation, then this is the type-to-create 7133 // parameter provided by the user (t) if specified, or the current type if not. If there is a 7134 // delegated factory (which is used to create the current type) then this is only the type-to- 7135 // create parameter (t). 7136 const typeForCtor = !isDelegatedFactoryMetadata(meta) ? new BinaryOperatorExpr(BinaryOperator.Or, t, meta.type.value) : t; 7137 let ctorExpr = null; 7138 if (meta.deps !== null) { 7139 // There is a constructor (either explicitly or implicitly defined). 7140 if (meta.deps !== 'invalid') { 7141 ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.target)); 7142 } 7143 } else { 7144 // There is no constructor, use the base class' factory to construct typeForCtor. 7145 baseFactoryVar = variable(`ɵ${meta.name}_BaseFactory`); 7146 ctorExpr = baseFactoryVar.callFn([typeForCtor]); 7147 } 7148 const body = []; 7149 let retExpr = null; 7150 function makeConditionalFactory(nonCtorExpr) { 7151 const r = variable('r'); 7152 body.push(r.set(NULL_EXPR).toDeclStmt()); 7153 const ctorStmt = ctorExpr !== null ? r.set(ctorExpr).toStmt() : importExpr(Identifiers.invalidFactory).callFn([]).toStmt(); 7154 body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()])); 7155 return r; 7156 } 7157 if (isDelegatedFactoryMetadata(meta)) { 7158 // This type is created with a delegated factory. If a type parameter is not specified, call 7159 // the factory instead. 7160 const delegateArgs = injectDependencies(meta.delegateDeps, meta.target); 7161 // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType. 7162 const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ? InstantiateExpr : InvokeFunctionExpr)(meta.delegate, delegateArgs); 7163 retExpr = makeConditionalFactory(factoryExpr); 7164 } else if (isExpressionFactoryMetadata(meta)) { 7165 // TODO(alxhub): decide whether to lower the value here or in the caller 7166 retExpr = makeConditionalFactory(meta.expression); 7167 } else { 7168 retExpr = ctorExpr; 7169 } 7170 if (retExpr === null) { 7171 // The expression cannot be formed so render an `ɵɵinvalidFactory()` call. 7172 body.push(importExpr(Identifiers.invalidFactory).callFn([]).toStmt()); 7173 } else if (baseFactoryVar !== null) { 7174 // This factory uses a base factory, so call `ɵɵgetInheritedFactory()` to compute it. 7175 const getInheritedFactoryCall = importExpr(Identifiers.getInheritedFactory).callFn([meta.type.value]); 7176 // Memoize the base factoryFn: `baseFactory || (baseFactory = ɵɵgetInheritedFactory(...))` 7177 const baseFactory = new BinaryOperatorExpr(BinaryOperator.Or, baseFactoryVar, baseFactoryVar.set(getInheritedFactoryCall));
7178 body.push(new ReturnStatement(baseFactory.callFn([typeForCtor]))); 7179 } else { 7180 // This is straightforward factory, just return it. 7181 body.push(new ReturnStatement(retExpr)); 7182 } 7183 let factoryFn = fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`); 7184 if (baseFactoryVar !== null) { 7185 // There is a base factory variable so wrap its declaration along with the factory function into 7186 // an IIFE. 7187 factoryFn = arrowFn([], [new DeclareVarStmt(baseFactoryVar.name), new ReturnStatement(factoryFn)]).callFn([], /* sourceSpan */undefined, /* pure */true); 7188 } 7189 return { 7190 expression: factoryFn, 7191 statements: [], 7192 type: createFactoryType(meta) 7193 }; 7194} 7195function createFactoryType(meta) { 7196 const ctorDepsType = meta.deps !== null && meta.deps !== 'invalid' ? createCtorDepsType(meta.deps) : NONE_TYPE; 7197 return expressionType(importExpr(Identifiers.FactoryDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType])); 7198} 7199function injectDependencies(deps, target) { 7200 return deps.map((dep, index) => compileInjectDependency(dep, target, index)); 7201} 7202function compileInjectDependency(dep, target, index) { 7203 // Interpret the dependency according to its resolved type. 7204 if (dep.token === null) { 7205 return importExpr(Identifiers.invalidFactoryDep).callFn([literal(index)]); 7206 } else if (dep.attributeNameType === null) { 7207 // Build up the injection flags according to the metadata. 7208 const flags = 0 /* InjectFlags.Default */ | (dep.self ? 2 /* InjectFlags.Self */ : 0) | (dep.skipSelf ? 4 /* InjectFlags.SkipSelf */ : 0) | (dep.host ? 1 /* InjectFlags.Host */ : 0) | (dep.optional ? 8 /* InjectFlags.Optional */ : 0) | (target === FactoryTarget$1.Pipe ? 16 /* InjectFlags.ForPipe */ : 0); 7209 // If this dependency is optional or otherwise has non-default flags, then additional 7210 // parameters describing how to inject the dependency must be passed to the inject function 7211 // that's being used. 7212 let flagsParam = flags !== 0 /* InjectFlags.Default */ || dep.optional ? literal(flags) : null; 7213 // Build up the arguments to the injectFn call. 7214 const injectArgs = [dep.token]; 7215 if (flagsParam) { 7216 injectArgs.push(flagsParam); 7217 } 7218 const injectFn = getInjectFn(target); 7219 return importExpr(injectFn).callFn(injectArgs); 7220 } else { 7221 // The `dep.attributeTypeName` value is defined, which indicates that this is an `@Attribute()` 7222 // type dependency. For the generated JS we still want to use the `dep.token` value in case the 7223 // name given for the attribute is not a string literal. For example given `@Attribute(foo())`, 7224 // we want to generate `ɵɵinjectAttribute(foo())`. 7225 // 7226 // The `dep.attributeTypeName` is only actually used (in `createCtorDepType()`) to generate 7227 // typings. 7228 return importExpr(Identifiers.injectAttribute).callFn([dep.token]); 7229 } 7230} 7231function createCtorDepsType(deps) { 7232 let hasTypes = false; 7233 const attributeTypes = deps.map(dep => { 7234 const type = createCtorDepType(dep); 7235 if (type !== null) { 7236 hasTypes = true; 7237 return type; 7238 } else { 7239 return literal(null); 7240 } 7241 }); 7242 if (hasTypes) { 7243 return expressionType(literalArr(attributeTypes)); 7244 } else { 7245 return NONE_TYPE; 7246 } 7247} 7248function createCtorDepType(dep) { 7249 const entries = []; 7250 if (dep.attributeNameType !== null) { 7251 entries.push({ 7252 key: 'attribute', 7253 value: dep.attributeNameType, 7254 quoted: false 7255 }); 7256 } 7257 if (dep.optional) { 7258 entries.push({ 7259 key: 'optional', 7260 value: literal(true), 7261 quoted: false 7262 }); 7263 } 7264 if (dep.host) { 7265 entries.push({ 7266 key: 'host', 7267 value: literal(true), 7268 quoted: false 7269 }); 7270 } 7271 if (dep.self) { 7272 entries.push({ 7273 key: 'self', 7274 value: literal(true), 7275 quoted: false 7276 }); 7277 } 7278 if (dep.skipSelf) { 7279 entries.push({ 7280 key: 'skipSelf', 7281 value: literal(true), 7282 quoted: false 7283 }); 7284 } 7285 return entries.length > 0 ? literalMap(entries) : null; 7286} 7287function isDelegatedFactoryMetadata(meta) { 7288 return meta.delegateType !== undefined; 7289} 7290function isExpressionFactoryMetadata(meta) { 7291 return meta.expression !== undefined; 7292} 7293function getInjectFn(target) { 7294 switch (target) { 7295 case FactoryTarget$1.Component: 7296 case FactoryTarget$1.Directive: 7297 case FactoryTarget$1.Pipe: 7298 return Identifiers.directiveInject; 7299 case FactoryTarget$1.NgModule: 7300 case FactoryTarget$1.Injectable: 7301 default: 7302 return Identifiers.inject; 7303 } 7304} 7305var TagContentType; 7306(function (TagContentType) { 7307 TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT"; 7308 TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT"; 7309 TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA"; 7310})(TagContentType || (TagContentType = {})); 7311function splitNsName(elementName, fatal = true) { 7312 if (elementName[0] != ':') { 7313 return [null, elementName]; 7314 } 7315 const colonIndex = elementName.indexOf(':', 1); 7316 if (colonIndex === -1) { 7317 if (fatal) { 7318 throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); 7319 } else { 7320 return [null, elementName]; 7321 } 7322 } 7323 return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)]; 7324} 7325// `<ng-container>` tags work the same regardless the namespace 7326function isNgContainer(tagName) { 7327 return splitNsName(tagName)[1] === 'ng-container'; 7328} 7329// `<ng-content>` tags work the same regardless the namespace 7330function isNgContent(tagName) { 7331 return splitNsName(tagName)[1] === 'ng-content'; 7332} 7333// `<ng-template>` tags work the same regardless the namespace 7334function isNgTemplate(tagName) { 7335 return splitNsName(tagName)[1] === 'ng-template'; 7336} 7337function getNsPrefix(fullName) { 7338 return fullName === null ? null : splitNsName(fullName)[0]; 7339} 7340function mergeNsAndName(prefix, localName) { 7341 return prefix ? `:${prefix}:${localName}` : localName; 7342} 7343
7344/** 7345 * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently 7346 * require the implementation of a visitor for Comments as they are only collected at 7347 * the top-level of the R3 AST, and only if `Render3ParseOptions['collectCommentNodes']` 7348 * is true. 7349 */ 7350class Comment$1 { 7351 constructor(value, sourceSpan) { 7352 this.value = value; 7353 this.sourceSpan = sourceSpan; 7354 } 7355 visit(_visitor) { 7356 throw new Error('visit() not implemented for Comment'); 7357 } 7358} 7359class Text$3 { 7360 constructor(value, sourceSpan) { 7361 this.value = value; 7362 this.sourceSpan = sourceSpan; 7363 } 7364 visit(visitor) { 7365 return visitor.visitText(this); 7366 } 7367} 7368class BoundText { 7369 constructor(value, sourceSpan, i18n) { 7370 this.value = value; 7371 this.sourceSpan = sourceSpan; 7372 this.i18n = i18n; 7373 } 7374 visit(visitor) { 7375 return visitor.visitBoundText(this); 7376 } 7377} 7378/** 7379 * Represents a text attribute in the template. 7380 * 7381 * `valueSpan` may not be present in cases where there is no value `<div a></div>`. 7382 * `keySpan` may also not be present for synthetic attributes from ICU expansions. 7383 */ 7384class TextAttribute { 7385 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) { 7386 this.name = name; 7387 this.value = value; 7388 this.sourceSpan = sourceSpan; 7389 this.keySpan = keySpan; 7390 this.valueSpan = valueSpan; 7391 this.i18n = i18n; 7392 } 7393 visit(visitor) { 7394 return visitor.visitTextAttribute(this); 7395 } 7396} 7397class BoundAttribute { 7398 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) { 7399 this.name = name; 7400 this.type = type; 7401 this.securityContext = securityContext; 7402 this.value = value; 7403 this.unit = unit; 7404 this.sourceSpan = sourceSpan; 7405 this.keySpan = keySpan; 7406 this.valueSpan = valueSpan; 7407 this.i18n = i18n; 7408 } 7409 static fromBoundElementProperty(prop, i18n) { 7410 if (prop.keySpan === undefined) { 7411 throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`); 7412 } 7413 return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n); 7414 } 7415 visit(visitor) { 7416 return visitor.visitBoundAttribute(this); 7417 } 7418} 7419class BoundEvent { 7420 constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) { 7421 this.name = name; 7422 this.type = type; 7423 this.handler = handler; 7424 this.target = target; 7425 this.phase = phase; 7426 this.sourceSpan = sourceSpan; 7427 this.handlerSpan = handlerSpan; 7428 this.keySpan = keySpan; 7429 } 7430 static fromParsedEvent(event) { 7431 const target = event.type === 0 /* ParsedEventType.Regular */ ? event.targetOrPhase : null; 7432 const phase = event.type === 1 /* ParsedEventType.Animation */ ? event.targetOrPhase : null; 7433 if (event.keySpan === undefined) { 7434 throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`); 7435 } 7436 return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan); 7437 } 7438 visit(visitor) { 7439 return visitor.visitBoundEvent(this); 7440 } 7441} 7442class Element$1 { 7443 constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) { 7444 this.name = name; 7445 this.attributes = attributes; 7446 this.inputs = inputs; 7447 this.outputs = outputs; 7448 this.children = children; 7449 this.references = references; 7450 this.sourceSpan = sourceSpan; 7451 this.startSourceSpan = startSourceSpan; 7452 this.endSourceSpan = endSourceSpan; 7453 this.i18n = i18n; 7454 } 7455 visit(visitor) { 7456 return visitor.visitElement(this); 7457 } 7458} 7459class DeferredTrigger { 7460 constructor(nameSpan, sourceSpan, prefetchSpan, whenOrOnSourceSpan) { 7461 this.nameSpan = nameSpan; 7462 this.sourceSpan = sourceSpan; 7463 this.prefetchSpan = prefetchSpan; 7464 this.whenOrOnSourceSpan = whenOrOnSourceSpan; 7465 } 7466 visit(visitor) { 7467 return visitor.visitDeferredTrigger(this); 7468 } 7469} 7470class BoundDeferredTrigger extends DeferredTrigger { 7471 constructor(value, sourceSpan, prefetchSpan, whenSourceSpan) { 7472 // BoundDeferredTrigger is for 'when' triggers. These aren't really "triggers" and don't have a 7473 // nameSpan. Trigger names are the built in event triggers like hover, interaction, etc. 7474 super( /** nameSpan */null, sourceSpan, prefetchSpan, whenSourceSpan); 7475 this.value = value; 7476 } 7477} 7478class IdleDeferredTrigger extends DeferredTrigger {} 7479class ImmediateDeferredTrigger extends DeferredTrigger {} 7480class HoverDeferredTrigger extends DeferredTrigger {
7481 constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 7482 super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 7483 this.reference = reference; 7484 } 7485} 7486class TimerDeferredTrigger extends DeferredTrigger { 7487 constructor(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 7488 super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 7489 this.delay = delay; 7490 } 7491} 7492class InteractionDeferredTrigger extends DeferredTrigger { 7493 constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 7494 super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 7495 this.reference = reference; 7496 } 7497} 7498class ViewportDeferredTrigger extends DeferredTrigger { 7499 constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 7500 super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 7501 this.reference = reference; 7502 } 7503} 7504class BlockNode { 7505 constructor(nameSpan, sourceSpan, startSourceSpan, endSourceSpan) { 7506 this.nameSpan = nameSpan; 7507 this.sourceSpan = sourceSpan; 7508 this.startSourceSpan = startSourceSpan; 7509 this.endSourceSpan = endSourceSpan; 7510 } 7511} 7512class DeferredBlockPlaceholder extends BlockNode { 7513 constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) { 7514 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7515 this.children = children; 7516 this.minimumTime = minimumTime; 7517 this.i18n = i18n; 7518 } 7519 visit(visitor) { 7520 return visitor.visitDeferredBlockPlaceholder(this); 7521 } 7522} 7523class DeferredBlockLoading extends BlockNode { 7524 constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) { 7525 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7526 this.children = children; 7527 this.afterTime = afterTime; 7528 this.minimumTime = minimumTime; 7529 this.i18n = i18n; 7530 } 7531 visit(visitor) { 7532 return visitor.visitDeferredBlockLoading(this); 7533 } 7534} 7535class DeferredBlockError extends BlockNode { 7536 constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) { 7537 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7538 this.children = children; 7539 this.i18n = i18n; 7540 } 7541 visit(visitor) { 7542 return visitor.visitDeferredBlockError(this); 7543 } 7544} 7545class DeferredBlock extends BlockNode { 7546 constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, i18n) { 7547 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7548 this.children = children; 7549 this.placeholder = placeholder; 7550 this.loading = loading; 7551 this.error = error; 7552 this.mainBlockSpan = mainBlockSpan; 7553 this.i18n = i18n; 7554 this.triggers = triggers; 7555 this.prefetchTriggers = prefetchTriggers; 7556 // We cache the keys since we know that they won't change and we 7557 // don't want to enumarate them every time we're traversing the AST. 7558 this.definedTriggers = Object.keys(triggers); 7559 this.definedPrefetchTriggers = Object.keys(prefetchTriggers); 7560 } 7561 visit(visitor) { 7562 return visitor.visitDeferredBlock(this); 7563 } 7564 visitAll(visitor) { 7565 this.visitTriggers(this.definedTriggers, this.triggers, visitor); 7566 this.visitTriggers(this.definedPrefetchTriggers, this.prefetchTriggers, visitor); 7567 visitAll$1(visitor, this.children); 7568 const remainingBlocks = [this.placeholder, this.loading, this.error].filter(x => x !== null); 7569 visitAll$1(visitor, remainingBlocks); 7570 } 7571 visitTriggers(keys, triggers, visitor) { 7572 visitAll$1(visitor, keys.map(k => triggers[k])); 7573 } 7574} 7575class SwitchBlock extends BlockNode { 7576 constructor(expression, cases, 7577 /** 7578 * These blocks are only captured to allow for autocompletion in the language service. They 7579 * aren't meant to be processed in any other way. 7580 */ 7581 unknownBlocks, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) { 7582 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7583 this.expression = expression; 7584 this.cases = cases; 7585 this.unknownBlocks = unknownBlocks; 7586 } 7587 visit(visitor) { 7588 return visitor.visitSwitchBlock(this); 7589 } 7590} 7591class SwitchBlockCase extends BlockNode { 7592 constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) { 7593 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7594 this.expression = expression; 7595 this.children = children; 7596 this.i18n = i18n; 7597 } 7598 visit(visitor) { 7599 return visitor.visitSwitchBlockCase(this); 7600 } 7601} 7602class ForLoopBlock extends BlockNode { 7603 constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) { 7604 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7605 this.item = item; 7606 this.expression = expression; 7607 this.trackBy = trackBy; 7608 this.trackKeywordSpan = trackKeywordSpan; 7609 this.contextVariables = contextVariables; 7610 this.children = children; 7611 this.empty = empty; 7612 this.mainBlockSpan = mainBlockSpan; 7613 this.i18n = i18n; 7614 } 7615 visit(visitor) { 7616 return visitor.visitForLoopBlock(this); 7617 } 7618} 7619class ForLoopBlockEmpty extends BlockNode { 7620 constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) { 7621 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7622 this.children = children; 7623 this.i18n = i18n; 7624 } 7625 visit(visitor) { 7626 return visitor.visitForLoopBlockEmpty(this); 7627 } 7628} 7629class IfBlock extends BlockNode { 7630 constructor(branches, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) { 7631 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7632 this.branches = branches; 7633 } 7634 visit(visitor) { 7635 return visitor.visitIfBlock(this); 7636 } 7637} 7638class IfBlockBranch extends BlockNode { 7639 constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) { 7640 super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan); 7641 this.expression = expression; 7642 this.children = children; 7643 this.expressionAlias = expressionAlias; 7644 this.i18n = i18n; 7645 } 7646 visit(visitor) { 7647 return visitor.visitIfBlockBranch(this); 7648 } 7649} 7650class UnknownBlock { 7651 constructor(name, sourceSpan, nameSpan) { 7652 this.name = name; 7653 this.sourceSpan = sourceSpan; 7654 this.nameSpan = nameSpan; 7655 } 7656 visit(visitor) { 7657 return visitor.visitUnknownBlock(this); 7658 } 7659} 7660class Template { 7661 constructor( 7662 // tagName is the name of the container element, if applicable. 7663 // `null` is a special case for when there is a structural directive on an `ng-template` so 7664 // the renderer can differentiate between the synthetic template and the one written in the 7665 // file. 7666 tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) { 7667 this.tagName = tagName; 7668 this.attributes = attributes;
7669 this.inputs = inputs; 7670 this.outputs = outputs; 7671 this.templateAttrs = templateAttrs; 7672 this.children = children; 7673 this.references = references; 7674 this.variables = variables; 7675 this.sourceSpan = sourceSpan; 7676 this.startSourceSpan = startSourceSpan; 7677 this.endSourceSpan = endSourceSpan; 7678 this.i18n = i18n; 7679 } 7680 visit(visitor) { 7681 return visitor.visitTemplate(this); 7682 } 7683} 7684class Content { 7685 constructor(selector, attributes, sourceSpan, i18n) { 7686 this.selector = selector; 7687 this.attributes = attributes; 7688 this.sourceSpan = sourceSpan; 7689 this.i18n = i18n; 7690 this.name = 'ng-content'; 7691 } 7692 visit(visitor) { 7693 return visitor.visitContent(this); 7694 } 7695} 7696class Variable { 7697 constructor(name, value, sourceSpan, keySpan, valueSpan) { 7698 this.name = name; 7699 this.value = value; 7700 this.sourceSpan = sourceSpan; 7701 this.keySpan = keySpan; 7702 this.valueSpan = valueSpan; 7703 } 7704 visit(visitor) { 7705 return visitor.visitVariable(this); 7706 } 7707} 7708class Reference { 7709 constructor(name, value, sourceSpan, keySpan, valueSpan) { 7710 this.name = name; 7711 this.value = value; 7712 this.sourceSpan = sourceSpan; 7713 this.keySpan = keySpan; 7714 this.valueSpan = valueSpan; 7715 } 7716 visit(visitor) { 7717 return visitor.visitReference(this); 7718 } 7719} 7720class Icu$1 { 7721 constructor(vars, placeholders, sourceSpan, i18n) { 7722 this.vars = vars; 7723 this.placeholders = placeholders; 7724 this.sourceSpan = sourceSpan; 7725 this.i18n = i18n; 7726 } 7727 visit(visitor) { 7728 return visitor.visitIcu(this); 7729 } 7730} 7731class RecursiveVisitor$1 { 7732 visitElement(element) { 7733 visitAll$1(this, element.attributes); 7734 visitAll$1(this, element.inputs); 7735 visitAll$1(this, element.outputs); 7736 visitAll$1(this, element.children); 7737 visitAll$1(this, element.references); 7738 } 7739 visitTemplate(template) { 7740 visitAll$1(this, template.attributes); 7741 visitAll$1(this, template.inputs); 7742 visitAll$1(this, template.outputs); 7743 visitAll$1(this, template.children); 7744 visitAll$1(this, template.references); 7745 visitAll$1(this, template.variables); 7746 } 7747 visitDeferredBlock(deferred) { 7748 deferred.visitAll(this); 7749 } 7750 visitDeferredBlockPlaceholder(block) { 7751 visitAll$1(this, block.children); 7752 } 7753 visitDeferredBlockError(block) { 7754 visitAll$1(this, block.children); 7755 } 7756 visitDeferredBlockLoading(block) { 7757 visitAll$1(this, block.children); 7758 } 7759 visitSwitchBlock(block) { 7760 visitAll$1(this, block.cases); 7761 } 7762 visitSwitchBlockCase(block) { 7763 visitAll$1(this, block.children); 7764 } 7765 visitForLoopBlock(block) { 7766 const blockItems = [block.item, ...Object.values(block.contextVariables), ...block.children]; 7767 block.empty && blockItems.push(block.empty); 7768 visitAll$1(this, blockItems); 7769 } 7770 visitForLoopBlockEmpty(block) { 7771 visitAll$1(this, block.children); 7772 } 7773 visitIfBlock(block) { 7774 visitAll$1(this, block.branches); 7775 } 7776 visitIfBlockBranch(block) { 7777 const blockItems = block.children; 7778 block.expressionAlias && blockItems.push(block.expressionAlias); 7779 visitAll$1(this, blockItems); 7780 } 7781 visitContent(content) {} 7782 visitVariable(variable) {} 7783 visitReference(reference) {} 7784 visitTextAttribute(attribute) {} 7785 visitBoundAttribute(attribute) {} 7786 visitBoundEvent(attribute) {} 7787 visitText(text) {} 7788 visitBoundText(text) {} 7789 visitIcu(icu) {} 7790 visitDeferredTrigger(trigger) {} 7791 visitUnknownBlock(block) {} 7792} 7793function visitAll$1(visitor, nodes) { 7794 const result = []; 7795 if (visitor.visit) { 7796 for (const node of nodes) { 7797 visitor.visit(node) || node.visit(visitor); 7798 } 7799 } else { 7800 for (const node of nodes) { 7801 const newNode = node.visit(visitor); 7802 if (newNode) { 7803 result.push(newNode); 7804 } 7805 } 7806 } 7807 return result; 7808} 7809class Message { 7810 /** 7811 * @param nodes message AST 7812 * @param placeholders maps placeholder names to static content and their source spans 7813 * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages) 7814 * @param meaning 7815 * @param description 7816 * @param customId 7817 */ 7818 constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) { 7819 this.nodes = nodes; 7820 this.placeholders = placeholders; 7821 this.placeholderToMessage = placeholderToMessage; 7822 this.meaning = meaning; 7823 this.description = description; 7824 this.customId = customId; 7825 /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */ 7826 this.legacyIds = []; 7827 this.id = this.customId; 7828 this.messageString = serializeMessage(this.nodes); 7829 if (nodes.length) { 7830 this.sources = [{ 7831 filePath: nodes[0].sourceSpan.start.file.url, 7832 startLine: nodes[0].sourceSpan.start.line + 1, 7833 startCol: nodes[0].sourceSpan.start.col + 1, 7834 endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1, 7835 endCol: nodes[0].sourceSpan.start.col + 1 7836 }]; 7837 } else { 7838 this.sources = []; 7839 } 7840 } 7841} 7842class Text$2 { 7843 constructor(value, sourceSpan) { 7844 this.value = value; 7845 this.sourceSpan = sourceSpan; 7846 } 7847 visit(visitor, context) { 7848 return visitor.visitText(this, context); 7849 } 7850} 7851// TODO(vicb): do we really need this node (vs an array) ? 7852class Container { 7853 constructor(children, sourceSpan) { 7854 this.children = children; 7855 this.sourceSpan = sourceSpan; 7856 } 7857 visit(visitor, context) { 7858 return visitor.visitContainer(this, context); 7859 } 7860} 7861class Icu { 7862 constructor(expression, type, cases, sourceSpan, expressionPlaceholder) { 7863 this.expression = expression; 7864 this.type = type; 7865 this.cases = cases; 7866 this.sourceSpan = sourceSpan; 7867 this.expressionPlaceholder = expressionPlaceholder; 7868 } 7869 visit(visitor, context) { 7870 return visitor.visitIcu(this, context); 7871 } 7872} 7873class TagPlaceholder { 7874 constructor(tag, attrs, startName, closeName, children, isVoid, 7875 // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan) 7876 sourceSpan, startSourceSpan, endSourceSpan) { 7877 this.tag = tag; 7878 this.attrs = attrs; 7879 this.startName = startName; 7880 this.closeName = closeName; 7881 this.children = children; 7882 this.isVoid = isVoid; 7883 this.sourceSpan = sourceSpan; 7884 this.startSourceSpan = startSourceSpan; 7885 this.endSourceSpan = endSourceSpan; 7886 } 7887 visit(visitor, context) { 7888 return visitor.visitTagPlaceholder(this, context); 7889 } 7890} 7891class Placeholder { 7892 constructor(value, name, sourceSpan) { 7893 this.value = value; 7894 this.name = name; 7895 this.sourceSpan = sourceSpan; 7896 } 7897 visit(visitor, context) { 7898 return visitor.visitPlaceholder(this, context); 7899 } 7900} 7901class IcuPlaceholder { 7902 constructor(value, name, sourceSpan) { 7903 this.value = value; 7904 this.name = name; 7905 this.sourceSpan = sourceSpan; 7906 } 7907 visit(visitor, context) { 7908 return visitor.visitIcuPlaceholder(this, context); 7909 } 7910} 7911class BlockPlaceholder { 7912 constructor(name, parameters, startName, closeName, children, sourceSpan, startSourceSpan, endSourceSpan) { 7913 this.name = name; 7914 this.parameters = parameters; 7915 this.startName = startName; 7916 this.closeName = closeName; 7917 this.children = children; 7918 this.sourceSpan = sourceSpan; 7919 this.startSourceSpan = startSourceSpan; 7920 this.endSourceSpan = endSourceSpan; 7921 } 7922 visit(visitor, context) { 7923 return visitor.visitBlockPlaceholder(this, context); 7924 } 7925} 7926// Clone the AST 7927class CloneVisitor { 7928 visitText(text, context) { 7929 return new Text$2(text.value, text.sourceSpan); 7930 } 7931 visitContainer(container, context) { 7932 const children = container.children.map(n => n.visit(this, context)); 7933 return new Container(children, container.sourceSpan); 7934 } 7935 visitIcu(icu, context) { 7936 const cases = {};
7937 Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, context)); 7938 const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan, icu.expressionPlaceholder); 7939 return msg; 7940 } 7941 visitTagPlaceholder(ph, context) { 7942 const children = ph.children.map(n => n.visit(this, context)); 7943 return new TagPlaceholder(ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan); 7944 } 7945 visitPlaceholder(ph, context) { 7946 return new Placeholder(ph.value, ph.name, ph.sourceSpan); 7947 } 7948 visitIcuPlaceholder(ph, context) { 7949 return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan); 7950 } 7951 visitBlockPlaceholder(ph, context) { 7952 const children = ph.children.map(n => n.visit(this, context)); 7953 return new BlockPlaceholder(ph.name, ph.parameters, ph.startName, ph.closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan); 7954 } 7955} 7956// Visit all the nodes recursively 7957class RecurseVisitor { 7958 visitText(text, context) {} 7959 visitContainer(container, context) { 7960 container.children.forEach(child => child.visit(this)); 7961 } 7962 visitIcu(icu, context) { 7963 Object.keys(icu.cases).forEach(k => { 7964 icu.cases[k].visit(this); 7965 }); 7966 } 7967 visitTagPlaceholder(ph, context) { 7968 ph.children.forEach(child => child.visit(this)); 7969 } 7970 visitPlaceholder(ph, context) {} 7971 visitIcuPlaceholder(ph, context) {} 7972 visitBlockPlaceholder(ph, context) { 7973 ph.children.forEach(child => child.visit(this)); 7974 } 7975} 7976/** 7977 * Serialize the message to the Localize backtick string format that would appear in compiled code. 7978 */ 7979function serializeMessage(messageNodes) { 7980 const visitor = new LocalizeMessageStringVisitor(); 7981 const str = messageNodes.map(n => n.visit(visitor)).join(''); 7982 return str; 7983} 7984class LocalizeMessageStringVisitor { 7985 visitText(text) { 7986 return text.value; 7987 } 7988 visitContainer(container) { 7989 return container.children.map(child => child.visit(this)).join(''); 7990 } 7991 visitIcu(icu) { 7992 const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 7993 return `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`; 7994 } 7995 visitTagPlaceholder(ph) { 7996 const children = ph.children.map(child => child.visit(this)).join(''); 7997 return `{$${ph.startName}}${children}{$${ph.closeName}}`; 7998 } 7999 visitPlaceholder(ph) { 8000 return `{$${ph.name}}`; 8001 } 8002 visitIcuPlaceholder(ph) { 8003 return `{$${ph.name}}`; 8004 } 8005 visitBlockPlaceholder(ph) { 8006 const children = ph.children.map(child => child.visit(this)).join(''); 8007 return `{$${ph.startName}}${children}{$${ph.closeName}}`; 8008 } 8009} 8010class Serializer { 8011 // Creates a name mapper, see `PlaceholderMapper` 8012 // Returning `null` means that no name mapping is used. 8013 createNameMapper(message) { 8014 return null; 8015 } 8016} 8017/** 8018 * A simple mapper that take a function to transform an internal name to a public name 8019 */ 8020class SimplePlaceholderMapper extends RecurseVisitor { 8021 // create a mapping from the message 8022 constructor(message, mapName) { 8023 super(); 8024 this.mapName = mapName; 8025 this.internalToPublic = {}; 8026 this.publicToNextId = {}; 8027 this.publicToInternal = {}; 8028 message.nodes.forEach(node => node.visit(this)); 8029 } 8030 toPublicName(internalName) { 8031 return this.internalToPublic.hasOwnProperty(internalName) ? this.internalToPublic[internalName] : null; 8032 } 8033 toInternalName(publicName) { 8034 return this.publicToInternal.hasOwnProperty(publicName) ? this.publicToInternal[publicName] : null; 8035 } 8036 visitText(text, context) { 8037 return null; 8038 } 8039 visitTagPlaceholder(ph, context) { 8040 this.visitPlaceholderName(ph.startName); 8041 super.visitTagPlaceholder(ph, context); 8042 this.visitPlaceholderName(ph.closeName); 8043 } 8044 visitPlaceholder(ph, context) { 8045 this.visitPlaceholderName(ph.name); 8046 } 8047 visitBlockPlaceholder(ph, context) { 8048 this.visitPlaceholderName(ph.startName); 8049 super.visitBlockPlaceholder(ph, context); 8050 this.visitPlaceholderName(ph.closeName); 8051 } 8052 visitIcuPlaceholder(ph, context) { 8053 this.visitPlaceholderName(ph.name); 8054 } 8055 // XMB placeholders could only contains A-Z, 0-9 and _ 8056 visitPlaceholderName(internalName) { 8057 if (!internalName || this.internalToPublic.hasOwnProperty(internalName)) { 8058 return; 8059 } 8060 let publicName = this.mapName(internalName); 8061 if (this.publicToInternal.hasOwnProperty(publicName)) { 8062 // Create a new XMB when it has already been used 8063 const nextId = this.publicToNextId[publicName]; 8064 this.publicToNextId[publicName] = nextId + 1; 8065 publicName = `${publicName}_${nextId}`; 8066 } else { 8067 this.publicToNextId[publicName] = 1; 8068 } 8069 this.internalToPublic[internalName] = publicName; 8070 this.publicToInternal[publicName] = internalName; 8071 } 8072} 8073class _Visitor$2 { 8074 visitTag(tag) { 8075 const strAttrs = this._serializeAttributes(tag.attrs); 8076 if (tag.children.length == 0) { 8077 return `<${tag.name}${strAttrs}/>`; 8078 } 8079 const strChildren = tag.children.map(node =>
8079 node.visit(this)); 8080 return `<${tag.name}${strAttrs}>${strChildren.join('')}</${tag.name}>`; 8081 } 8082 visitText(text) { 8083 return text.value; 8084 } 8085 visitDeclaration(decl) { 8086 return `<?xml${this._serializeAttributes(decl.attrs)} ?>`; 8087 } 8088 _serializeAttributes(attrs) { 8089 const strAttrs = Object.keys(attrs).map(name => `${name}="${attrs[name]}"`).join(' '); 8090 return strAttrs.length > 0 ? ' ' + strAttrs : ''; 8091 } 8092 visitDoctype(doctype) { 8093 return `<!DOCTYPE ${doctype.rootTag} [\n${doctype.dtd}\n]>`; 8094 } 8095} 8096const _visitor = new _Visitor$2(); 8097function serialize(nodes) { 8098 return nodes.map(node => node.visit(_visitor)).join(''); 8099} 8100class Declaration { 8101 constructor(unescapedAttrs) { 8102 this.attrs = {}; 8103 Object.keys(unescapedAttrs).forEach(k => { 8104 this.attrs[k] = escapeXml(unescapedAttrs[k]); 8105 }); 8106 } 8107 visit(visitor) { 8108 return visitor.visitDeclaration(this); 8109 } 8110} 8111class Doctype { 8112 constructor(rootTag, dtd) { 8113 this.rootTag = rootTag; 8114 this.dtd = dtd; 8115 } 8116 visit(visitor) { 8117 return visitor.visitDoctype(this); 8118 } 8119} 8120class Tag { 8121 constructor(name, unescapedAttrs = {}, children = []) { 8122 this.name = name; 8123 this.children = children; 8124 this.attrs = {}; 8125 Object.keys(unescapedAttrs).forEach(k => { 8126 this.attrs[k] = escapeXml(unescapedAttrs[k]); 8127 }); 8128 } 8129 visit(visitor) { 8130 return visitor.visitTag(this); 8131 } 8132} 8133class Text$1 { 8134 constructor(unescapedValue) { 8135 this.value = escapeXml(unescapedValue); 8136 } 8137 visit(visitor) { 8138 return visitor.visitText(this); 8139 } 8140} 8141class CR extends Text$1 { 8142 constructor(ws = 0) { 8143 super(`\n${new Array(ws + 1).join(' ')}`); 8144 } 8145} 8146const _ESCAPED_CHARS = [[/&/g, '&'], [/"/g, '"'], [/'/g, '''], [/</g, '<'], [/>/g, '>']]; 8147// Escape `_ESCAPED_CHARS` characters in the given text with encoded entities 8148function escapeXml(text) { 8149 return _ESCAPED_CHARS.reduce((text, entry) => text.replace(entry[0], entry[1]), text); 8150} 8151const _MESSAGES_TAG = 'messagebundle'; 8152const _MESSAGE_TAG = 'msg'; 8153const _PLACEHOLDER_TAG$3 = 'ph'; 8154const _EXAMPLE_TAG = 'ex'; 8155const _SOURCE_TAG$2 = 'source'; 8156const _DOCTYPE = `<!ELEMENT messagebundle (msg)*> 8157<!ATTLIST messagebundle class CDATA #IMPLIED> 8158 8159<!ELEMENT msg (#PCDATA|ph|source)*> 8160<!ATTLIST msg id CDATA #IMPLIED> 8161<!ATTLIST msg seq CDATA #IMPLIED> 8162<!ATTLIST msg name CDATA #IMPLIED> 8163<!ATTLIST msg desc CDATA #IMPLIED> 8164<!ATTLIST msg meaning CDATA #IMPLIED> 8165<!ATTLIST msg obsolete (obsolete) #IMPLIED> 8166<!ATTLIST msg xml:space (default|preserve) "default"> 8167<!ATTLIST msg is_hidden CDATA #IMPLIED> 8168 8169<!ELEMENT source (#PCDATA)> 8170 8171<!ELEMENT ph (#PCDATA|ex)*> 8172<!ATTLIST ph name CDATA #REQUIRED> 8173 8174<!ELEMENT ex (#PCDATA)>`; 8175class Xmb extends Serializer { 8176 write(messages, locale) { 8177 const exampleVisitor = new ExampleVisitor(); 8178 const visitor = new _Visitor$1(); 8179 let rootNode = new Tag(_MESSAGES_TAG); 8180 messages.forEach(message => { 8181 const attrs = { 8182 id: message.id 8183 }; 8184 if (message.description) { 8185 attrs['desc'] = message.description; 8186 } 8187 if (message.meaning) { 8188 attrs['meaning'] = message.meaning; 8189 } 8190 let sourceTags = []; 8191 message.sources.forEach(source => { 8192 sourceTags.push(new Tag(_SOURCE_TAG$2, {}, [new Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)])); 8193 }); 8194 rootNode.children.push(new CR(2), new Tag(_MESSAGE_TAG, attrs, [...sourceTags, ...visitor.serialize(message.nodes)])); 8195 }); 8196 rootNode.children.push(new CR()); 8197 return serialize([new Declaration({ 8198 version: '1.0', 8199 encoding: 'UTF-8' 8200 }), new CR(), new Doctype(_MESSAGES_TAG, _DOCTYPE), new CR(), exampleVisitor.addDefaultExamples(rootNode), new CR()]); 8201 } 8202 load(content, url) { 8203 throw new Error('Unsupported'); 8204 } 8205 digest(message) { 8206 return digest(message); 8207 } 8208 createNameMapper(message) { 8209 return new SimplePlaceholderMapper(message, toPublicName); 8210 } 8211} 8212class _Visitor$1 { 8213 visitText(text, context) { 8214 return [new Text$1(text.value)]; 8215 } 8216 visitContainer(container, context) { 8217 const nodes = [];
8218 container.children.forEach(node => nodes.push(...node.visit(this))); 8219 return nodes; 8220 } 8221 visitIcu(icu, context) { 8222 const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)]; 8223 Object.keys(icu.cases).forEach(c => { 8224 nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `)); 8225 }); 8226 nodes.push(new Text$1(`}`)); 8227 return nodes; 8228 } 8229 visitTagPlaceholder(ph, context) { 8230 const startTagAsText = new Text$1(`<${ph.tag}>`); 8231 const startEx = new Tag(_EXAMPLE_TAG, {}, [startTagAsText]); 8232 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8233 const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { 8234 name: ph.startName 8235 }, [startEx, startTagAsText]); 8236 if (ph.isVoid) { 8237 // void tags have no children nor closing tags 8238 return [startTagPh]; 8239 } 8240 const closeTagAsText = new Text$1(`</${ph.tag}>`); 8241 const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeTagAsText]); 8242 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8243 const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { 8244 name: ph.closeName 8245 }, [closeEx, closeTagAsText]); 8246 return [startTagPh, ...this.serialize(ph.children), closeTagPh]; 8247 } 8248 visitPlaceholder(ph, context) { 8249 const interpolationAsText = new Text$1(`{{${ph.value}}}`); 8250 // Example tag needs to be not-empty for TC. 8251 const exTag = new Tag(_EXAMPLE_TAG, {}, [interpolationAsText]); 8252 return [ 8253 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8254 new Tag(_PLACEHOLDER_TAG$3, { 8255 name: ph.name 8256 }, [exTag, interpolationAsText])]; 8257 } 8258 visitBlockPlaceholder(ph, context) { 8259 const startAsText = new Text$1(`@${ph.name}`); 8260 const startEx = new Tag(_EXAMPLE_TAG, {}, [startAsText]); 8261 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8262 const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { 8263 name: ph.startName 8264 }, [startEx, startAsText]); 8265 const closeAsText = new Text$1(`}`); 8266 const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeAsText]); 8267 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8268 const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { 8269 name: ph.closeName 8270 }, [closeEx, closeAsText]); 8271 return [startTagPh, ...this.serialize(ph.children), closeTagPh]; 8272 } 8273 visitIcuPlaceholder(ph, context) { 8274 const icuExpression = ph.value.expression; 8275 const icuType = ph.value.type; 8276 const icuCases = Object.keys(ph.value.cases).map(value => value + ' {...}').join(' '); 8277 const icuAsText = new Text$1(`{${icuExpression}, ${icuType}, ${icuCases}}`); 8278 const exTag = new Tag(_EXAMPLE_TAG, {}, [icuAsText]); 8279 return [ 8280 // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. 8281 new Tag(_PLACEHOLDER_TAG$3, { 8282 name: ph.name 8283 }, [exTag, icuAsText])]; 8284 } 8285 serialize(nodes) { 8286 return [].concat(...nodes.map(node => node.visit(this))); 8287 } 8288} 8289function digest(message) { 8290 return decimalDigest(message); 8291} 8292// TC requires at least one non-empty example on placeholders 8293class ExampleVisitor { 8294 addDefaultExamples(node) { 8295 node.visit(this); 8296 return node; 8297 } 8298 visitTag(tag) { 8299 if (tag.name === _PLACEHOLDER_TAG$3) { 8300 if (!tag.children || tag.children.length == 0) { 8301 const exText = new Text$1(tag.attrs['name'] || '...'); 8302 tag.children = [new Tag(_EXAMPLE_TAG, {}, [exText])]; 8303 } 8304 } else if (tag.children) { 8305 tag.children.forEach(node => node.visit(this)); 8306 } 8307 } 8308 visitText(text) {} 8309 visitDeclaration(decl) {} 8310 visitDoctype(doctype) {} 8311} 8312// XMB/XTB placeholders can only contain A-Z, 0-9 and _ 8313function toPublicName(internalName) { 8314 return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_'); 8315} 8316 8317/* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */ 8318const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_'; 8319/** 8320 * Prefix for non-`goog.getMsg` i18n-related vars. 8321 * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that 8322 * considers variables like `I18N_0` as constants and throws an error when their value changes. 8323 */ 8324const TRANSLATION_VAR_PREFIX$1 = 'i18n_'; 8325/** Name of the i18n attributes **/ 8326const I18N_ATTR = 'i18n'; 8327const I18N_ATTR_PREFIX = 'i18n-'; 8328/** Prefix of var expressions used in ICUs */ 8329const I18N_ICU_VAR_PREFIX = 'VAR_'; 8330/** Prefix of ICU expressions for post processing */ 8331const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
8332/** Placeholder wrapper for i18n expressions **/ 8333const I18N_PLACEHOLDER_SYMBOL = '�'; 8334function isI18nAttribute(name) { 8335 return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX); 8336} 8337function isI18nRootNode(meta) { 8338 return meta instanceof Message; 8339} 8340function isSingleI18nIcu(meta) { 8341 return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu; 8342} 8343function hasI18nMeta(node) { 8344 return !!node.i18n; 8345} 8346function hasI18nAttrs(element) { 8347 return element.attrs.some(attr => isI18nAttribute(attr.name)); 8348} 8349function icuFromI18nMessage(message) { 8350 return message.nodes[0]; 8351} 8352function wrapI18nPlaceholder(content, contextId = 0) { 8353 const blockId = contextId > 0 ? `:${contextId}` : ''; 8354 return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`; 8355} 8356function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) { 8357 if (!strings.length) return ''; 8358 let acc = ''; 8359 const lastIdx = strings.length - 1; 8360 for (let i = 0; i < lastIdx; i++) { 8361 acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`; 8362 } 8363 acc += strings[lastIdx]; 8364 return acc; 8365} 8366function getSeqNumberGenerator(startsAt = 0) { 8367 let current = startsAt; 8368 return () => current++; 8369} 8370function placeholdersToParams(placeholders) { 8371 const params = {}; 8372 placeholders.forEach((values, key) => { 8373 params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]); 8374 }); 8375 return params; 8376} 8377function updatePlaceholderMap(map, name, ...values) { 8378 const current = map.get(name) || []; 8379 current.push(...values); 8380 map.set(name, current); 8381} 8382function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) { 8383 const startIdx = bindingStartIndex; 8384 const placeholders = new Map(); 8385 const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta; 8386 if (node) { 8387 node.children.filter(child => child instanceof Placeholder).forEach((child, idx) => { 8388 const content = wrapI18nPlaceholder(startIdx + idx, contextId); 8389 updatePlaceholderMap(placeholders, child.name, content); 8390 }); 8391 } 8392 return placeholders; 8393} 8394/** 8395 * Format the placeholder names in a map of placeholders to expressions. 8396 * 8397 * The placeholder names are converted from "internal" format (e.g. `START_TAG_DIV_1`) to "external" 8398 * format (e.g. `startTagDiv_1`). 8399 * 8400 * @param params A map of placeholder names to expressions. 8401 * @param useCamelCase whether to camelCase the placeholder name when formatting. 8402 * @returns A new map of formatted placeholder names to expressions. 8403 */ 8404function formatI18nPlaceholderNamesInMap(params = {}, useCamelCase) { 8405 const _params = {}; 8406 if (params && Object.keys(params).length) { 8407 Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]); 8408 } 8409 return _params; 8410} 8411/** 8412 * Converts internal placeholder names to public-facing format 8413 * (for example to use in goog.getMsg call). 8414 * Example: `START_TAG_DIV_1` is converted to `startTagDiv_1`. 8415 * 8416 * @param name The placeholder name that should be formatted 8417 * @returns Formatted placeholder name 8418 */ 8419function formatI18nPlaceholderName(name, useCamelCase = true) { 8420 const publicName = toPublicName(name); 8421 if (!useCamelCase) { 8422 return publicName; 8423 } 8424 const chunks = publicName.split('_'); 8425 if (chunks.length === 1) { 8426 // if no "_" found - just lowercase the value 8427 return name.toLowerCase(); 8428 } 8429 let postfix; 8430 // eject last element if it's a number 8431 if (/^\d+$/.test(chunks[chunks.length - 1])) { 8432 postfix = chunks.pop(); 8433 } 8434 let raw = chunks.shift().toLowerCase(); 8435 if (chunks.length) { 8436 raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join(''); 8437 } 8438 return postfix ? `${raw}_${postfix}` : raw; 8439} 8440/** 8441 * Generates a prefix for translation const name. 8442 * 8443 * @param extra Additional local prefix that should be injected into translation var name 8444 * @returns Complete translation const prefix 8445 */ 8446function getTranslationConstPrefix(extra) { 8447 return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase(); 8448} 8449/** 8450 * Generate AST to declare a variable. E.g. `var I18N_1;`. 8451 * @param variable the name of the variable to declare. 8452 */ 8453function declareI18nVariable(variable) { 8454 return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan); 8455} 8456 8457/** 8458 * Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in 8459 * quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may 8460 * not work in some cases when object keys are mangled by a minifier. 8461 * 8462 * TODO(FW-1136): this is a temporary solution, we need to come up with a better way of working with 8463 * inputs that contain potentially unsafe chars. 8464 */ 8465const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/; 8466/** Name of the temporary to use during data binding */ 8467const TEMPORARY_NAME = '_t'; 8468/** Name of the context parameter passed into a template function */ 8469const CONTEXT_NAME = 'ctx'; 8470/** Name of the RenderFlag passed into a template function */ 8471const RENDER_FLAGS = 'rf'; 8472/** The prefix reference variables */ 8473const REFERENCE_PREFIX = '_r'; 8474/** The name of the implicit context reference */ 8475const IMPLICIT_REFERENCE = '$implicit'; 8476/** Non bindable attribute name **/ 8477const NON_BINDABLE_ATTR = 'ngNonBindable'; 8478/** Name for the variable keeping track of the context returned by `ɵɵrestoreView`. */ 8479const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx'; 8480/** Special value representing a direct access to a template's context. */ 8481const DIRECT_CONTEXT_REFERENCE = '#context'; 8482/** 8483 * Maximum length of a single instruction chain. Because our output AST uses recursion, we're 8484 * limited in how many expressions we can nest before we reach the call stack limit. This 8485 * length is set very conservatively in order to reduce the chance of problems. 8486 */ 8487const MAX_CHAIN_LENGTH = 500; 8488/** Instructions that support chaining. */ 8489const CHAINABLE_INSTRUCTIONS = new Set([Identifiers.element, Identifiers.elementStart, Identifiers.elementEnd, Identifiers.elementContainer, Identifiers.elementContainerStart, Identifiers.elementContainerEnd, Identifiers.i18nExp, Identifiers.listener, Identifiers.classProp, Identifiers.syntheticHostListener, Identifiers.hostProperty, Identifiers.syntheticHostProperty, Identifiers.property, Identifiers.propertyInterpolate1, Identifiers.propertyInterpolate2, Identifiers.propertyInterpolate3, Identifiers.propertyInterpolate4, Identifiers.propertyInterpolate5, Identifiers.propertyInterpolate6, Identifiers.propertyInterpolate7, Identifiers.propertyInterpolate8, Identifiers.propertyInterpolateV, Identifiers.attribute, Identifiers.attributeInterpolate1, Identifiers.attributeInterpolate2, Identifiers.attributeInterpolate3, Identifiers.attributeInterpolate4, Identifiers.attributeInterpolate5, Identifiers.attributeInterpolate6, Identifiers.attributeInterpolate7, Identifiers.attributeInterpolate8, Identifiers.attributeInterpolateV, Identifiers.styleProp, Identifiers.stylePropInterpolate1, Identifiers.stylePropInterpolate2, Identifiers.stylePropInterpolate3, Identifiers.stylePropInterpolate4, Identifiers.stylePropInterpolate5, Identifiers.stylePropInterpolate6, Identifiers.stylePropInterpolate7, Identifiers.stylePropInterpolate8, Identifiers.stylePropInterpolateV, Identifiers.textInterpolate, Identifiers.textInterpolate1, Identifiers.textInterpolate2, Identifiers.textInterpolate3, Identifiers.textInterpolate4, Identifiers.textInterpolate5, Identifiers.textInterpolate6, Identifiers.textInterpolate7, Identifiers.textInterpolate8, Identifiers.textInterpolateV, Identifiers.templateCreate, Identifiers.twoWayProperty, Identifiers.twoWayListener]); 8490/** Generates a call to a single instruction. */
8491function invokeInstruction(span, reference, params) { 8492 return importExpr(reference, null, span).callFn(params, span); 8493} 8494/** 8495 * Creates an allocator for a temporary variable. 8496 * 8497 * A variable declaration is added to the statements the first time the allocator is invoked. 8498 */ 8499function temporaryAllocator(pushStatement, name) { 8500 let temp = null; 8501 return () => { 8502 if (!temp) { 8503 pushStatement(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE)); 8504 temp = variable(name); 8505 } 8506 return temp; 8507 }; 8508} 8509function invalid(arg) { 8510 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`); 8511} 8512function asLiteral(value) { 8513 if (Array.isArray(value)) { 8514 return literalArr(value.map(asLiteral)); 8515 } 8516 return literal(value, INFERRED_TYPE); 8517} 8518/** 8519 * Serializes inputs and outputs for `defineDirective` and `defineComponent`. 8520 * 8521 * This will attempt to generate optimized data structures to minimize memory or 8522 * file size of fully compiled applications. 8523 */ 8524function conditionallyCreateDirectiveBindingLiteral(map, forInputs) { 8525 const keys = Object.getOwnPropertyNames(map); 8526 if (keys.length === 0) { 8527 return null; 8528 } 8529 return literalMap(keys.map(key => { 8530 const value = map[key]; 8531 let declaredName; 8532 let publicName; 8533 let minifiedName; 8534 let expressionValue; 8535 if (typeof value === 'string') { 8536 // canonical syntax: `dirProp: publicProp` 8537 declaredName = key; 8538 minifiedName = key; 8539 publicName = value; 8540 expressionValue = asLiteral(publicName); 8541 } else { 8542 minifiedName = key; 8543 declaredName = value.classPropertyName; 8544 publicName = value.bindingPropertyName; 8545 const differentDeclaringName = publicName !== declaredName; 8546 const hasDecoratorInputTransform = value.transformFunction !== null; 8547 // Build up input flags 8548 let flags = null; 8549 if (value.isSignal) { 8550 flags = bitwiseOrInputFlagsExpr(InputFlags.SignalBased, flags); 8551 } 8552 if (hasDecoratorInputTransform) { 8553 flags = bitwiseOrInputFlagsExpr(InputFlags.HasDecoratorInputTransform, flags); 8554 } 8555 // Inputs, compared to outputs, will track their declared name (for `ngOnChanges`), support 8556 // decorator input transform functions, or store flag information if there is any. 8557 if (forInputs && (differentDeclaringName || hasDecoratorInputTransform || flags !== null)) { 8558 const flagsExpr = flags ?? importExpr(Identifiers.InputFlags).prop(InputFlags[InputFlags.None]); 8559 const result = [flagsExpr, asLiteral(publicName)]; 8560 if (differentDeclaringName || hasDecoratorInputTransform) { 8561 result.push(asLiteral(declaredName)); 8562 if (hasDecoratorInputTransform) { 8563 result.push(value.transformFunction); 8564 } 8565 } 8566 expressionValue = literalArr(result); 8567 } else { 8568 expressionValue = asLiteral(publicName); 8569 } 8570 } 8571 return { 8572 key: minifiedName, 8573 // put quotes around keys that contain potentially unsafe characters 8574 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName), 8575 value: expressionValue 8576 }; 8577 })); 8578} 8579/** Gets an output AST expression referencing the given flag. */ 8580function getInputFlagExpr(flag) { 8581 return importExpr(Identifiers.InputFlags).prop(InputFlags[flag]); 8582} 8583/** Combines a given input flag with an existing flag expression, if present. */ 8584function bitwiseOrInputFlagsExpr(flag, expr) { 8585 if (expr === null) { 8586 return getInputFlagExpr(flag); 8587 } 8588 return getInputFlagExpr(flag).bitwiseOr(expr); 8589} 8590/** 8591 * Remove trailing null nodes as they are implied. 8592 */ 8593function trimTrailingNulls(parameters) { 8594 while (isNull(parameters[parameters.length - 1])) { 8595 parameters.pop(); 8596 } 8597 return parameters; 8598} 8599/** 8600 * A representation for an object literal used during codegen of definition objects. The generic 8601 * type `T` allows to reference a documented type of the generated structure, such that the 8602 * property names that are set can be resolved to their documented declaration. 8603 */ 8604class DefinitionMap { 8605 constructor() { 8606 this.values = []; 8607 } 8608 set(key, value) { 8609 if (value) { 8610 const existing = this.values.find(value => value.key === key); 8611 if (existing) { 8612 existing.value = value; 8613 } else { 8614 this.values.push({ 8615 key: key, 8616 value, 8617 quoted: false 8618 }); 8619 } 8620 } 8621 } 8622 toLiteralMap() { 8623 return literalMap(this.values); 8624 } 8625} 8626/** 8627 * Creates a `CssSelector` from an AST node. 8628 */ 8629function createCssSelectorFromNode(node) { 8630 const elementName = node instanceof Element$1 ? node.name : 'ng-template'; 8631 const attributes = getAttrsForDirectiveMatching(node); 8632 const cssSelector = new CssSelector(); 8633 const elementNameNoNs = splitNsName(elementName)[1]; 8634 cssSelector.setElement(elementNameNoNs);
8635 Object.getOwnPropertyNames(attributes).forEach(name => { 8636 const nameNoNs = splitNsName(name)[1]; 8637 const value = attributes[name]; 8638 cssSelector.addAttribute(nameNoNs, value); 8639 if (name.toLowerCase() === 'class') { 8640 const classes = value.trim().split(/\s+/); 8641 classes.forEach(className => cssSelector.addClassName(className)); 8642 } 8643 }); 8644 return cssSelector; 8645} 8646/** 8647 * Extract a map of properties to values for a given element or template node, which can be used 8648 * by the directive matching machinery. 8649 * 8650 * @param elOrTpl the element or template in question 8651 * @return an object set up for directive matching. For attributes on the element/template, this 8652 * object maps a property name to its (static) value. For any bindings, this map simply maps the 8653 * property name to an empty string. 8654 */ 8655function getAttrsForDirectiveMatching(elOrTpl) { 8656 const attributesMap = {}; 8657 if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') { 8658 elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = ''); 8659 } else { 8660 elOrTpl.attributes.forEach(a => { 8661 if (!isI18nAttribute(a.name)) { 8662 attributesMap[a.name] = a.value; 8663 } 8664 }); 8665 elOrTpl.inputs.forEach(i => { 8666 if (i.type === 0 /* BindingType.Property */ || i.type === 5 /* BindingType.TwoWay */) { 8667 attributesMap[i.name] = ''; 8668 } 8669 }); 8670 elOrTpl.outputs.forEach(o => { 8671 attributesMap[o.name] = ''; 8672 }); 8673 } 8674 return attributesMap; 8675} 8676/** 8677 * Gets the number of arguments expected to be passed to a generated instruction in the case of 8678 * interpolation instructions. 8679 * @param interpolation An interpolation ast 8680 */ 8681function getInterpolationArgsLength(interpolation) { 8682 const { 8683 expressions, 8684 strings 8685 } = interpolation; 8686 if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') { 8687 // If the interpolation has one interpolated value, but the prefix and suffix are both empty 8688 // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or 8689 // `textInterpolate`. 8690 return 1; 8691 } else { 8692 return expressions.length + strings.length; 8693 } 8694} 8695/** 8696 * Generates the final instruction call statements based on the passed in configuration. 8697 * Will try to chain instructions as much as possible, if chaining is supported. 8698 */ 8699function getInstructionStatements(instructions) { 8700 const statements = []; 8701 let pendingExpression = null; 8702 let pendingExpressionType = null; 8703 let chainLength = 0; 8704 for (const current of instructions) { 8705 const resolvedParams = (typeof current.paramsOrFn === 'function' ? current.paramsOrFn() : current.paramsOrFn) ?? []; 8706 const params = Array.isArray(resolvedParams) ? resolvedParams : [resolvedParams]; 8707 // If the current instruction is the same as the previous one 8708 // and it can be chained, add another call to the chain. 8709 if (chainLength < MAX_CHAIN_LENGTH && pendingExpressionType === current.reference && CHAINABLE_INSTRUCTIONS.has(pendingExpressionType)) { 8710 // We'll always have a pending expression when there's a pending expression type. 8711 pendingExpression = pendingExpression.callFn(params, pendingExpression.sourceSpan); 8712 chainLength++; 8713 } else { 8714 if (pendingExpression !== null) { 8715 statements.push(pendingExpression.toStmt()); 8716 } 8717 pendingExpression = invokeInstruction(current.span, current.reference, params); 8718 pendingExpressionType = current.reference; 8719 chainLength = 0; 8720 } 8721 } 8722 // Since the current instruction adds the previous one to the statements, 8723 // we may be left with the final one at the end that is still pending. 8724 if (pendingExpression !== null) { 8725 statements.push(pendingExpression.toStmt()); 8726 } 8727 return statements; 8728} 8729function compileInjectable(meta, resolveForwardRefs) { 8730 let result = null; 8731 const factoryMeta = { 8732 name: meta.name, 8733 type: meta.type, 8734 typeArgumentCount: meta.typeArgumentCount, 8735 deps: [], 8736 target: FactoryTarget$1.Injectable 8737 }; 8738 if (meta.useClass !== undefined) { 8739 // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is 8740 // used to instantiate the class with dependencies injected, or deps are not specified and 8741 // the factory of the class is used to instantiate it. 8742 // 8743 // A special case exists for useClass: Type where Type is the injectable type itself and n
8743o 8744 // deps are specified, in which case 'useClass' is effectively ignored. 8745 const useClassOnSelf = meta.useClass.expression.isEquivalent(meta.type.value); 8746 let deps = undefined; 8747 if (meta.deps !== undefined) { 8748 deps = meta.deps; 8749 } 8750 if (deps !== undefined) { 8751 // factory: () => new meta.useClass(...deps) 8752 result = compileFactoryFunction({ 8753 ...factoryMeta, 8754 delegate: meta.useClass.expression, 8755 delegateDeps: deps, 8756 delegateType: R3FactoryDelegateType.Class 8757 }); 8758 } else if (useClassOnSelf) { 8759 result = compileFactoryFunction(factoryMeta); 8760 } else { 8761 result = { 8762 statements: [], 8763 expression: delegateToFactory(meta.type.value, meta.useClass.expression, resolveForwardRefs) 8764 }; 8765 } 8766 } else if (meta.useFactory !== undefined) { 8767 if (meta.deps !== undefined) { 8768 result = compileFactoryFunction({ 8769 ...factoryMeta, 8770 delegate: meta.useFactory, 8771 delegateDeps: meta.deps || [], 8772 delegateType: R3FactoryDelegateType.Function 8773 }); 8774 } else { 8775 result = { 8776 statements: [], 8777 expression: arrowFn([], meta.useFactory.callFn([])) 8778 }; 8779 } 8780 } else if (meta.useValue !== undefined) { 8781 // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for 8782 // client code because meta.useValue is an Expression which will be defined even if the actual 8783 // value is undefined. 8784 result = compileFactoryFunction({ 8785 ...factoryMeta, 8786 expression: meta.useValue.expression 8787 }); 8788 } else if (meta.useExisting !== undefined) { 8789 // useExisting is an `inject` call on the existing token. 8790 result = compileFactoryFunction({ 8791 ...factoryMeta, 8792 expression: importExpr(Identifiers.inject).callFn([meta.useExisting.expression]) 8793 }); 8794 } else { 8795 result = { 8796 statements: [], 8797 expression: delegateToFactory(meta.type.value, meta.type.value, resolveForwardRefs) 8798 }; 8799 } 8800 const token = meta.type.value; 8801 const injectableProps = new DefinitionMap(); 8802 injectableProps.set('token', token); 8803 injectableProps.set('factory', result.expression); 8804 // Only generate providedIn property if it has a non-null value 8805 if (meta.providedIn.expression.value !== null) { 8806 injectableProps.set('providedIn', convertFromMaybeForwardRefExpression(meta.providedIn)); 8807 } 8808 const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([injectableProps.toLiteralMap()], undefined, true); 8809 return { 8810 expression, 8811 type: createInjectableType(meta), 8812 statements: result.statements 8813 }; 8814} 8815function createInjectableType(meta) { 8816 return new ExpressionType(importExpr(Identifiers.InjectableDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount)])); 8817} 8818function delegateToFactory(type, useType, unwrapForwardRefs) { 8819 if (type.node === useType.node) { 8820 // The types are the same, so we can simply delegate directly to the type's factory. 8821 // ``` 8822 // factory: type.ɵfac 8823 // ``` 8824 return useType.prop('ɵfac'); 8825 } 8826 if (!unwrapForwardRefs) { 8827 // The type is not wrapped in a `forwardRef()`, so we create a simple factory function that 8828 // accepts a sub-type as an argument. 8829 // ``` 8830 // factory: function(t) { return useType.ɵfac(t); } 8831 // ``` 8832 return createFactoryFunction(useType); 8833 } 8834 // The useType is actually wrapped in a `forwardRef()` so we need to resolve that before 8835 // calling its factory. 8836 // ``` 8837 // factory: function(t) { return core.resolveForwardRef(type).ɵfac(t); } 8838 // ``` 8839 const unwrappedType = importExpr(Identifiers.resolveForwardRef).callFn([useType]); 8840 return createFactoryFunction(unwrappedType); 8841} 8842function createFactoryFunction(type) { 8843 return arrowFn([new FnParam('t', DYNAMIC_TYPE)], type.prop('ɵfac').callFn([variable('t')])); 8844} 8845const UNUSABLE_INTERPOLATION_REGEXPS = [/^\s*$/, 8846// empty 8847/[<>]/, 8848// html tag 8849/^[{}]$/, 8850// i18n expansion 8851/&(#|[a-z])/i, 8852// character reference, 8853/^\/\// // comment 8854]; 8855function assertInterpolationSymbols(identifier, value) { 8856 if (value != null && !(Array.isArray(value) && value.length == 2)) { 8857 throw new Error(`Expected '${identifier}' to be an array, [start, end].`); 8858 } else if (value != null) { 8859 const start = value[0]; 8860 const end = value[1]; 8861 // Check for unusable interpolation symbols
8862 UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => { 8863 if (regexp.test(start) || regexp.test(end)) { 8864 throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`); 8865 } 8866 }); 8867 } 8868} 8869class InterpolationConfig { 8870 static fromArray(markers) { 8871 if (!markers) { 8872 return DEFAULT_INTERPOLATION_CONFIG; 8873 } 8874 assertInterpolationSymbols('interpolation', markers); 8875 return new InterpolationConfig(markers[0], markers[1]); 8876 } 8877 constructor(start, end) { 8878 this.start = start; 8879 this.end = end; 8880 } 8881} 8882const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}'); 8883const DEFAULT_CONTAINER_BLOCKS = new Set(['switch']); 8884const $EOF = 0; 8885const $BSPACE = 8; 8886const $TAB = 9; 8887const $LF = 10; 8888const $VTAB = 11; 8889const $FF = 12; 8890const $CR = 13; 8891const $SPACE = 32; 8892const $BANG = 33; 8893const $DQ = 34; 8894const $HASH = 35; 8895const $$ = 36; 8896const $PERCENT = 37; 8897const $AMPERSAND = 38; 8898const $SQ = 39; 8899const $LPAREN = 40; 8900const $RPAREN = 41; 8901const $STAR = 42; 8902const $PLUS = 43; 8903const $COMMA = 44; 8904const $MINUS = 45; 8905const $PERIOD = 46; 8906const $SLASH = 47; 8907const $COLON = 58; 8908const $SEMICOLON = 59; 8909const $LT = 60; 8910const $EQ = 61; 8911const $GT = 62; 8912const $QUESTION = 63; 8913const $0 = 48; 8914const $7 = 55; 8915const $9 = 57; 8916const $A = 65; 8917const $E = 69; 8918const $F = 70; 8919const $X = 88; 8920const $Z = 90; 8921const $LBRACKET = 91; 8922const $BACKSLASH = 92; 8923const $RBRACKET = 93; 8924const $CARET = 94; 8925const $_ = 95; 8926const $a = 97; 8927const $b = 98; 8928const $e = 101; 8929const $f = 102; 8930const $n = 110; 8931const $r = 114; 8932const $t = 116; 8933const $u = 117; 8934const $v = 118; 8935const $x = 120; 8936const $z = 122; 8937const $LBRACE = 123; 8938const $BAR = 124; 8939const $RBRACE = 125; 8940const $NBSP = 160; 8941const $PIPE = 124; 8942const $TILDA = 126; 8943const $AT = 64; 8944const $BT = 96; 8945function isWhitespace(code) { 8946 return code >= $TAB && code <= $SPACE || code == $NBSP; 8947} 8948function isDigit(code) { 8949 return $0 <= code && code <= $9; 8950} 8951function isAsciiLetter(code) { 8952 return code >= $a && code <= $z || code >= $A && code <= $Z; 8953} 8954function isAsciiHexDigit(code) { 8955 return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code); 8956} 8957function isNewLine(code) { 8958 return code === $LF || code === $CR; 8959} 8960function isOctalDigit(code) { 8961 return $0 <= code && code <= $7; 8962} 8963function isQuote(code) { 8964 return code === $SQ || code === $DQ || code === $BT; 8965} 8966class ParseLocation { 8967 constructor(file, offset, line, col) { 8968 this.file = file; 8969 this.offset = offset; 8970 this.line = line; 8971 this.col = col; 8972 } 8973 toString() { 8974 return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url; 8975 } 8976 moveBy(delta) { 8977 const source = this.file.content; 8978 const len = source.length; 8979 let offset = this.offset; 8980 let line = this.line; 8981 let col = this.col; 8982 while (offset > 0 && delta < 0) { 8983 offset--; 8984 delta++; 8985 const ch = source.charCodeAt(offset); 8986 if (ch == $LF) { 8987 line--; 8988 const priorLine = source.substring(0, offset - 1).lastIndexOf(String.fromCharCode($LF)); 8989 col = priorLine > 0 ? offset - priorLine : offset; 8990 } else { 8991 col--; 8992 } 8993 } 8994 while (offset < len && delta > 0) { 8995 const ch = source.charCodeAt(offset); 8996 offset++; 8997 delta--; 8998 if (ch == $LF) { 8999 line++; 9000 col = 0; 9001 } else { 9002 col++; 9003 } 9004 } 9005 return new ParseLocation(this.file, offset, line, col); 9006 } 9007 // Return the source around the location 9008 // Up to `maxChars` or `maxLines` on each side of the location 9009 getContext(maxChars, maxLines) { 9010 const content = this.file.content; 9011 let startOffset = this.offset; 9012 if (startOffset != null) { 9013 if (startOffset > content.length - 1) { 9014 startOffset = content.length - 1; 9015 } 9016 let endOffset = startOffset; 9017 let ctxChars = 0; 9018 let ctxLines = 0; 9019 while (ctxChars < maxChars && startOffset > 0) { 9020 startOffset--; 9021 ctxChars++; 9022 if (content[startOffset] == '\n') { 9023 if (++ctxLines == maxLines) { 9024 break; 9025 } 9026 } 9027 } 9028 ctxChars = 0; 9029 ctxLines = 0; 9030 while (ctxChars < maxChars && endOffset < content.length - 1) { 9031 endOffset++; 9032 ctxChars++; 9033 if (content[endOffset] == '\n') { 9034 if (++ctxLines == maxLines) { 9035 break; 9036 } 9037 } 9038 } 9039 return { 9040 before: content.substring(startOffset, this.offset), 9041 after: content.substring(this.offset, endOffset + 1) 9042 }; 9043 } 9044 return null; 9045 } 9046} 9047class ParseSourceFile {
9048 constructor(content, url) { 9049 this.content = content; 9050 this.url = url; 9051 } 9052} 9053class ParseSourceSpan { 9054 /** 9055 * Create an object that holds information about spans of tokens/nodes captured during 9056 * lexing/parsing of text. 9057 * 9058 * @param start 9059 * The location of the start of the span (having skipped leading trivia). 9060 * Skipping leading trivia makes source-spans more "user friendly", since things like HTML 9061 * elements will appear to begin at the start of the opening tag, rather than at the start of any 9062 * leading trivia, which could include newlines. 9063 * 9064 * @param end 9065 * The location of the end of the span. 9066 * 9067 * @param fullStart 9068 * The start of the token without skipping the leading trivia. 9069 * This is used by tooling that splits tokens further, such as extracting Angular interpolations 9070 * from text tokens. Such tooling creates new source-spans relative to the original token's 9071 * source-span. If leading trivia characters have been skipped then the new source-spans may be 9072 * incorrectly offset. 9073 * 9074 * @param details 9075 * Additional information (such as identifier names) that should be associated with the span. 9076 */ 9077 constructor(start, end, fullStart = start, details = null) { 9078 this.start = start; 9079 this.end = end; 9080 this.fullStart = fullStart; 9081 this.details = details; 9082 } 9083 toString() { 9084 return this.start.file.content.substring(this.start.offset, this.end.offset); 9085 } 9086} 9087var ParseErrorLevel; 9088(function (ParseErrorLevel) { 9089 ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING"; 9090 ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR"; 9091})(ParseErrorLevel || (ParseErrorLevel = {})); 9092class ParseError { 9093 constructor(span, msg, level = ParseErrorLevel.ERROR) { 9094 this.span = span; 9095 this.msg = msg; 9096 this.level = level; 9097 } 9098 contextualMessage() { 9099 const ctx = this.span.start.getContext(100, 3); 9100 return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` : this.msg; 9101 } 9102 toString() { 9103 const details = this.span.details ? `, ${this.span.details}` : ''; 9104 return `${this.contextualMessage()}: ${this.span.start}${details}`; 9105 } 9106} 9107/** 9108 * Generates Source Span object for a given R3 Type for JIT mode. 9109 * 9110 * @param kind Component or Directive. 9111 * @param typeName name of the Component or Directive. 9112 * @param sourceUrl reference to Component or Directive source. 9113 * @returns instance of ParseSourceSpan that represent a given Component or Directive. 9114 */ 9115function r3JitTypeSourceSpan(kind, typeName, sourceUrl) { 9116 const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`; 9117 const sourceFile = new ParseSourceFile('', sourceFileName); 9118 return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1)); 9119} 9120let _anonymousTypeIndex = 0; 9121function identifierName(compileIdentifier) { 9122 if (!compileIdentifier || !compileIdentifier.reference) { 9123 return null; 9124 } 9125 const ref = compileIdentifier.reference; 9126 if (ref['__anonymousType']) { 9127 return ref['__anonymousType']; 9128 } 9129 if (ref['__forward_ref__']) { 9130 // We do not want to try to stringify a `forwardRef()` function because that would cause the 9131 // inner function to be evaluated too early, defeating the whole point of the `forwardRef`. 9132 return '__forward_ref__';
9133 } 9134 let identifier = stringify(ref); 9135 if (identifier.indexOf('(') >= 0) { 9136 // case: anonymous functions! 9137 identifier = `anonymous_${_anonymousTypeIndex++}`; 9138 ref['__anonymousType'] = identifier; 9139 } else { 9140 identifier = sanitizeIdentifier(identifier); 9141 } 9142 return identifier; 9143} 9144function sanitizeIdentifier(name) { 9145 return name.replace(/\W/g, '_'); 9146} 9147 9148/** 9149 * In TypeScript, tagged template functions expect a "template object", which is an array of 9150 * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is 9151 * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not 9152 * be available in all environments. 9153 * 9154 * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise 9155 * creates an inline helper with the same functionality. 9156 * 9157 * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw` 9158 * array. 9159 */ 9160const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})'; 9161class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { 9162 constructor() { 9163 super(false); 9164 } 9165 visitWrappedNodeExpr(ast, ctx) { 9166 throw new Error('Cannot emit a WrappedNodeExpr in Javascript.'); 9167 } 9168 visitDeclareVarStmt(stmt, ctx) { 9169 ctx.print(stmt, `var ${stmt.name}`); 9170 if (stmt.value) { 9171 ctx.print(stmt, ' = '); 9172 stmt.value.visitExpression(this, ctx); 9173 } 9174 ctx.println(stmt, `;`); 9175 return null; 9176 } 9177 visitTaggedTemplateExpr(ast, ctx) { 9178 // The following convoluted piece of code is effectively the downlevelled equivalent of 9179 // ``` 9180 // tag`...` 9181 // ``` 9182 // which is effectively like: 9183 // ```
9184 // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...); 9185 // ``` 9186 const elements = ast.template.elements; 9187 ast.tag.visitExpression(this, ctx); 9188 ctx.print(ast, `(${makeTemplateObjectPolyfill}(`); 9189 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `); 9190 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`); 9191 ast.template.expressions.forEach(expression => { 9192 ctx.print(ast, ', '); 9193 expression.visitExpression(this, ctx); 9194 }); 9195 ctx.print(ast, ')'); 9196 return null; 9197 } 9198 visitFunctionExpr(ast, ctx) { 9199 ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`); 9200 this._visitParams(ast.params, ctx); 9201 ctx.println(ast, `) {`); 9202 ctx.incIndent(); 9203 this.visitAllStatements(ast.statements, ctx); 9204 ctx.decIndent(); 9205 ctx.print(ast, `}`); 9206 return null; 9207 } 9208 visitArrowFunctionExpr(ast, ctx) { 9209 ctx.print(ast, '('); 9210 this._visitParams(ast.params, ctx); 9211 ctx.print(ast, ') =>'); 9212 if (Array.isArray(ast.body)) { 9213 ctx.println(ast, `{`); 9214 ctx.incIndent(); 9215 this.visitAllStatements(ast.body, ctx); 9216 ctx.decIndent(); 9217 ctx.print(ast, `}`); 9218 } else { 9219 const isObjectLiteral = ast.body instanceof LiteralMapExpr; 9220 if (isObjectLiteral) { 9221 ctx.print(ast, '('); 9222 } 9223 ast.body.visitExpression(this, ctx); 9224 if (isObjectLiteral) { 9225 ctx.print(ast, ')'); 9226 } 9227 } 9228 return null; 9229 } 9230 visitDeclareFunctionStmt(stmt, ctx) { 9231 ctx.print(stmt, `function ${stmt.name}(`); 9232 this._visitParams(stmt.params, ctx); 9233 ctx.println(stmt, `) {`); 9234 ctx.incIndent(); 9235 this.visitAllStatements(stmt.statements, ctx); 9236 ctx.decIndent(); 9237 ctx.println(stmt, `}`); 9238 return null; 9239 } 9240 visitLocalizedString(ast, ctx) { 9241 // The following convoluted piece of code is effectively the downlevelled equivalent of 9242 // ``` 9243 // $localize `...` 9244 // ``` 9245 // which is effectively like: 9246 // ```
9247 // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...); 9248 // ``` 9249 ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`); 9250 const parts = [ast.serializeI18nHead()]; 9251 for (let i = 1; i < ast.messageParts.length; i++) { 9252 parts.push(ast.serializeI18nTemplatePart(i)); 9253 } 9254 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `); 9255 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`); 9256 ast.expressions.forEach(expression => { 9257 ctx.print(ast, ', '); 9258 expression.visitExpression(this, ctx); 9259 }); 9260 ctx.print(ast, ')'); 9261 return null; 9262 } 9263 _visitParams(params, ctx) { 9264 this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ','); 9265 } 9266} 9267 9268/** 9269 * @fileoverview 9270 * A module to facilitate use of a Trusted Types policy within the JIT 9271 * compiler. It lazily constructs the Trusted Types policy, providing helper 9272 * utilities for promoting strings to Trusted Types. When Trusted Types are not 9273 * available, strings are used as a fallback. 9274 * @security All use of this module is security-sensitive and should go through 9275 * security review. 9276 */ 9277/** 9278 * The Trusted Types policy, or null if Trusted Types are not 9279 * enabled/supported, or undefined if the policy has not been created yet. 9280 */ 9281let policy; 9282/** 9283 * Returns the Trusted Types policy, or null if Trusted Types are not 9284 * enabled/supported. The first call to this function will create the policy. 9285 */ 9286function getPolicy() { 9287 if (policy === undefined) { 9288 const trustedTypes = _global['trustedTypes']; 9289 policy = null; 9290 if (trustedTypes) { 9291 try { 9292 policy = trustedTypes.createPolicy('angular#unsafe-jit', { 9293 createScript: s => s 9294 }); 9295 } catch { 9296 // trustedTypes.createPolicy throws if called with a name that is 9297 // already registered, even in report-only mode. Until the API changes, 9298 // catch the error not to break the applications functionally. In such 9299 // cases, the code will fall back to using strings. 9300 } 9301 } 9302 } 9303 return policy; 9304} 9305/** 9306 * Unsafely promote a string to a TrustedScript, falling back to strings when 9307 * Trusted Types are not available. 9308 * @security In particular, it must be assured that the provided string will 9309 * never cause an XSS vulnerability if used in a context that will be 9310 * interpreted and executed as a script by a browser, e.g. when calling eval. 9311 */ 9312function trustedScriptFromString(script) { 9313 return getPolicy()?.createScript(script) || script; 9314} 9315/** 9316 * Unsafely call the Function constructor with the given string arguments. 9317 * @security This is a security-sensitive function; any use of this function 9318 * must go through security review. In particular, it must be assured that it 9319 * is only called from the JIT compiler, as use in other code can lead to XSS 9320 * vulnerabilities. 9321 */ 9322function newTrustedFunctionForJIT(...args) { 9323 if (!_global['trustedTypes']) { 9324 // In environments that don't support Trusted Types, fall back to the most 9325 // straightforward implementation: 9326 return new Function(...args); 9327 } 9328 // Chrome currently does not support passing TrustedScript to the Function 9329 // constructor. The following implements the workaround proposed on the page 9330 // below, where the Chromium bug is also referenced: 9331 // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor 9332 const fnArgs = args.slice(0, -1).join(','); 9333 const fnBody = args[args.length - 1]; 9334 const body = `(function anonymous(${fnArgs} 9335) { ${fnBody} 9336})`; 9337 // Using eval directly confuses the compiler and prevents this module from 9338 // being stripped out of JS binaries even if not used. The global['eval'] 9339 // indirection fixes that. 9340 const fn = _global['eval'](trustedScriptFromString(body)); 9341 if (fn.bind === undefined) { 9342 // Workaround for a browser bug that only exists in Chrome 83, where passing 9343 // a TrustedScript to eval just returns the TrustedScript back without 9344 // evaluating it. In that case, fall back to the most straightforward 9345 // implementation: 9346 return new Function(...args); 9347 } 9348 // To completely mimic the behavior of calling "new Function", two more 9349 // things need to happen: 9350 // 1. Stringifying the resulting function should return its source code 9351 fn.toString = () => body; 9352 // 2. When calling the resulting function, `this` should refer to `global` 9353 return fn.bind(_global); 9354 // When Trusted Types support in Function constructors is widely available, 9355 // the implementation of this function can be simplified to: 9356 // return new Function(...args.map(a => trustedScriptFromString(a))); 9357} 9358 9359/** 9360 * A helper class to manage the evaluation of JIT generated code. 9361 */ 9362class JitEvaluator { 9363 /** 9364 * 9365 * @param sourceUrl The URL of the generated code. 9366 * @param statements An array of Angular statement AST nodes to be evaluated. 9367 * @param refResolver Resolves `o.ExternalReference`s into values. 9368 * @param createSourceMaps If true then create a source-map for the generated code and include it 9369 * inline as a source-map comment. 9370 * @returns A map of all the variables in the generated code. 9371 */ 9372 evaluateStatements(sourceUrl, statements, refResolver, createSourceMaps) { 9373 const converter = new JitEmitterVisitor(refResolver); 9374 const ctx = EmitterVisitorContext.createRoot(); 9375 // Ensure generated code is in strict mode 9376 if (statements.length >
9376 0 && !isUseStrictStatement(statements[0])) { 9377 statements = [literal('use strict').toStmt(), ...statements]; 9378 } 9379 converter.visitAllStatements(statements, ctx); 9380 converter.createReturnStmt(ctx); 9381 return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps); 9382 } 9383 /** 9384 * Evaluate a piece of JIT generated code. 9385 * @param sourceUrl The URL of this generated code. 9386 * @param ctx A context object that contains an AST of the code to be evaluated. 9387 * @param vars A map containing the names and values of variables that the evaluated code might 9388 * reference. 9389 * @param createSourceMap If true then create a source-map for the generated code and include it 9390 * inline as a source-map comment. 9391 * @returns The result of evaluating the code. 9392 */ 9393 evaluateCode(sourceUrl, ctx, vars, createSourceMap) { 9394 let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`; 9395 const fnArgNames = []; 9396 const fnArgValues = []; 9397 for (const argName in vars) { 9398 fnArgValues.push(vars[argName]); 9399 fnArgNames.push(argName); 9400 } 9401 if (createSourceMap) { 9402 // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise 9403 // E.g. ``` 9404 // function anonymous(a,b,c 9405 // /**/) { ... }``` 9406 // We don't want to hard code this fact, so we auto detect it via an empty function first. 9407 const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString(); 9408 const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1; 9409 fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`; 9410 } 9411 const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody)); 9412 return this.executeFunction(fn, fnArgValues); 9413 } 9414 /** 9415 * Execute a JIT generated function by calling it. 9416 * 9417 * This method can be overridden in tests to capture the functions that are generated 9418 * by this `JitEvaluator` class. 9419 * 9420 * @param fn A function to execute. 9421 * @param args The arguments to pass to the function being executed. 9422 * @returns The return value of the executed function. 9423 */ 9424 executeFunction(fn, args) { 9425 return fn(...args); 9426 } 9427} 9428/** 9429 * An Angular AST visitor that converts AST nodes into executable JavaScript code. 9430 */ 9431class JitEmitterVisitor extends AbstractJsEmitterVisitor { 9432 constructor(refResolver) { 9433 super(); 9434 this.refResolver = refResolver; 9435 this._evalArgNames = []; 9436 this._evalArgValues = []; 9437 this._evalExportedVars = []; 9438 } 9439 createReturnStmt(ctx) {
9440 const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false)))); 9441 stmt.visitStatement(this, ctx); 9442 } 9443 getArgs() { 9444 const result = {}; 9445 for (let i = 0; i < this._evalArgNames.length; i++) { 9446 result[this._evalArgNames[i]] = this._evalArgValues[i]; 9447 } 9448 return result; 9449 } 9450 visitExternalExpr(ast, ctx) { 9451 this._emitReferenceToExternal(ast, this.refResolver.resolveExternalReference(ast.value), ctx); 9452 return null; 9453 } 9454 visitWrappedNodeExpr(ast, ctx) { 9455 this._emitReferenceToExternal(ast, ast.node, ctx); 9456 return null; 9457 } 9458 visitDeclareVarStmt(stmt, ctx) { 9459 if (stmt.hasModifier(StmtModifier.Exported)) { 9460 this._evalExportedVars.push(stmt.name); 9461 } 9462 return super.visitDeclareVarStmt(stmt, ctx); 9463 } 9464 visitDeclareFunctionStmt(stmt, ctx) { 9465 if (stmt.hasModifier(StmtModifier.Exported)) { 9466 this._evalExportedVars.push(stmt.name); 9467 } 9468 return super.visitDeclareFunctionStmt(stmt, ctx); 9469 } 9470 _emitReferenceToExternal(ast, value, ctx) { 9471 let id = this._evalArgValues.indexOf(value); 9472 if (id === -1) { 9473 id = this._evalArgValues.length; 9474 this._evalArgValues.push(value); 9475 const name = identifierName({ 9476 reference: value 9477 }) || 'val'; 9478 this._evalArgNames.push(`jit_${name}_${id}`); 9479 } 9480 ctx.print(ast, this._evalArgNames[id]); 9481 } 9482} 9483function isUseStrictStatement(statement) { 9484 return statement.isEquivalent(literal('use strict').toStmt()); 9485} 9486function compileInjector(meta) { 9487 const definitionMap = new DefinitionMap(); 9488 if (meta.providers !== null) { 9489 definitionMap.set('providers', meta.providers); 9490 } 9491 if (meta.imports.length > 0) { 9492 definitionMap.set('imports', literalArr(meta.imports)); 9493 } 9494 const expression = importExpr(Identifiers.defineInjector).callFn([definitionMap.toLiteralMap()], undefined, true); 9495 const type = createInjectorType(meta); 9496 return { 9497 expression, 9498 type, 9499 statements: [] 9500 }; 9501} 9502function createInjectorType(meta) { 9503 return new ExpressionType(importExpr(Identifiers.InjectorDeclaration, [new ExpressionType(meta.type.type)])); 9504} 9505 9506/** 9507 * Implementation of `CompileReflector` which resolves references to @angular/core 9508 * symbols at runtime, according to a consumer-provided mapping. 9509 * 9510 * Only supports `resolveExternalReference`, all other methods throw. 9511 */ 9512class R3JitReflector { 9513 constructor(context) { 9514 this.context = context; 9515 } 9516 resolveExternalReference(ref) { 9517 // This reflector only handles @angular/core imports. 9518 if (ref.moduleName !== '@angular/core') { 9519 throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`); 9520 }
9521 if (!this.context.hasOwnProperty(ref.name)) { 9522 throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`); 9523 } 9524 return this.context[ref.name]; 9525 } 9526} 9527 9528/** 9529 * How the selector scope of an NgModule (its declarations, imports, and exports) should be emitted 9530 * as a part of the NgModule definition. 9531 */ 9532var R3SelectorScopeMode; 9533(function (R3SelectorScopeMode) { 9534 /** 9535 * Emit the declarations inline into the module definition. 9536 * 9537 * This option is useful in certain contexts where it's known that JIT support is required. The 9538 * tradeoff here is that this emit style prevents directives and pipes from being tree-shaken if 9539 * they are unused, but the NgModule is used. 9540 */ 9541 R3SelectorScopeMode[R3SelectorScopeMode["Inline"] = 0] = "Inline"; 9542 /** 9543 * Emit the declarations using a side effectful function call, `ɵɵsetNgModuleScope`, that is 9544 * guarded with the `ngJitMode` flag. 9545 * 9546 * This form of emit supports JIT and can be optimized away if the `ngJitMode` flag is set to 9547 * false, which allows unused directives and pipes to be tree-shaken. 9548 */ 9549 R3SelectorScopeMode[R3SelectorScopeMode["SideEffect"] = 1] = "SideEffect"; 9550 /** 9551 * Don't generate selector scopes at all. 9552 * 9553 * This is useful for contexts where JIT support is known to be unnecessary. 9554 */ 9555 R3SelectorScopeMode[R3SelectorScopeMode["Omit"] = 2] = "Omit"; 9556})(R3SelectorScopeMode || (R3SelectorScopeMode = {})); 9557/** 9558 * The type of the NgModule meta data. 9559 * - Global: Used for full and partial compilation modes which mainly includes R3References. 9560 * - Local: Used for the local compilation mode which mainly includes the raw expressions as appears 9561 * in the NgModule decorator. 9562 */ 9563var R3NgModuleMetadataKind; 9564(function (R3NgModuleMetadataKind) { 9565 R3NgModuleMetadataKind[R3NgModuleMetadataKind["Global"] = 0] = "Global"; 9566 R3NgModuleMetadataKind[R3NgModuleMetadataKind["Local"] = 1] = "Local"; 9567})(R3NgModuleMetadataKind || (R3NgModuleMetadataKind = {})); 9568/** 9569 * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`. 9570 */ 9571function compileNgModule(meta) { 9572 const statements = []; 9573 const definitionMap = new DefinitionMap(); 9574 definitionMap.set('type', meta.type.value); 9575 // Assign bootstrap definition. In local compilation mode (i.e., for 9576 // `R3NgModuleMetadataKind.LOCAL`) we assign the bootstrap field using the runtime 9577 // `ɵɵsetNgModuleScope`. 9578 if (meta.kind === R3NgModuleMetadataKind.Global && meta.bootstrap.length > 0) { 9579 definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls)); 9580 } 9581 if (meta.selectorScopeMode === R3SelectorScopeMode.Inline) { 9582 // If requested to emit scope information inline, pass the `declarations`, `imports` and 9583 // `exports` to the `ɵɵdefineNgModule()` call directly. 9584 if (meta.declarations.length > 0) { 9585 definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls)); 9586 } 9587 if (meta.imports.length > 0) { 9588 definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls)); 9589 } 9590 if (meta.exports.length > 0) { 9591 definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls)); 9592 } 9593 } else if (meta.selectorScopeMode === R3SelectorScopeMode.SideEffect) { 9594 // In this mode, scope information is not passed into `ɵɵdefineNgModule` as it 9595 // would prevent tree-shaking of the declarations, imports and exports references. Instead, it's 9596 // patched onto the NgModule definition with a `ɵɵsetNgModuleScope` call that's guarded by the 9597 // `ngJitMode` flag. 9598 const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta); 9599 if (setNgModuleScopeCall !== null) { 9600 statements.push(setNgModuleScopeCall); 9601 } 9602 } else { 9603 // Selector scope emit was not requested, so skip it. 9604 } 9605 if (meta.schemas !== null && meta.schemas.length > 0) { 9606 definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value))); 9607 } 9608 if (meta.id !== null) { 9609 definitionMap.set('id', meta.id); 9610 // Generate a side-effectful call to register this NgModule by its id, as per the semantics of 9611 // NgModule ids. 9612 statements.push(importExpr(Identifiers.registerNgModuleType).callFn([meta.type.value, meta.id]).toStmt()); 9613 } 9614 const expression = importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true); 9615 const type = createNgModuleType(meta); 9616 return { 9617 expression, 9618 type, 9619 statements 9620 }; 9621} 9622/** 9623 * This function is used in JIT mode to generate the call to `ɵɵdefineNgModule(
9623)` from a call to 9624 * `ɵɵngDeclareNgModule()`. 9625 */ 9626function compileNgModuleDeclarationExpression(meta) { 9627 const definitionMap = new DefinitionMap(); 9628 definitionMap.set('type', new WrappedNodeExpr(meta.type)); 9629 if (meta.bootstrap !== undefined) { 9630 definitionMap.set('bootstrap', new WrappedNodeExpr(meta.bootstrap)); 9631 } 9632 if (meta.declarations !== undefined) { 9633 definitionMap.set('declarations', new WrappedNodeExpr(meta.declarations)); 9634 } 9635 if (meta.imports !== undefined) { 9636 definitionMap.set('imports', new WrappedNodeExpr(meta.imports)); 9637 } 9638 if (meta.exports !== undefined) { 9639 definitionMap.set('exports', new WrappedNodeExpr(meta.exports)); 9640 } 9641 if (meta.schemas !== undefined) { 9642 definitionMap.set('schemas', new WrappedNodeExpr(meta.schemas)); 9643 } 9644 if (meta.id !== undefined) { 9645 definitionMap.set('id', new WrappedNodeExpr(meta.id)); 9646 } 9647 return importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()]); 9648} 9649function createNgModuleType(meta) { 9650 if (meta.kind === R3NgModuleMetadataKind.Local) { 9651 return new ExpressionType(meta.type.value); 9652 } 9653 const { 9654 type: moduleType, 9655 declarations, 9656 exports, 9657 imports, 9658 includeImportTypes, 9659 publicDeclarationTypes 9660 } = meta; 9661 return new ExpressionType(importExpr(Identifiers.NgModuleDeclaration, [new ExpressionType(moduleType.type), publicDeclarationTypes === null ? tupleTypeOf(declarations) : tupleOfTypes(publicDeclarationTypes), includeImportTypes ? tupleTypeOf(imports) : NONE_TYPE, tupleTypeOf(exports)])); 9662} 9663/** 9664 * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the 9665 * transitive module scope can be computed during runtime in JIT mode. This call is marked pure 9666 * such that the references to declarations, imports and exports may be elided causing these 9667 * symbols to become tree-shakeable. 9668 */ 9669function generateSetNgModuleScopeCall(meta) { 9670 const scopeMap = new DefinitionMap(); 9671 if (meta.kind === R3NgModuleMetadataKind.Global) { 9672 if (meta.declarations.length > 0) { 9673 scopeMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls)); 9674 } 9675 } else { 9676 if (meta.declarationsExpression) { 9677 scopeMap.set('declarations', meta.declarationsExpression); 9678 } 9679 } 9680 if (meta.kind === R3NgModuleMetadataKind.Global) { 9681 if (meta.imports.length > 0) { 9682 scopeMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls)); 9683 } 9684 } else { 9685 if (meta.importsExpression) { 9686 scopeMap.set('imports', meta.importsExpression); 9687 } 9688 } 9689 if (meta.kind === R3NgModuleMetadataKind.Global) { 9690 if (meta.exports.length > 0) { 9691 scopeMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls)); 9692 } 9693 } else { 9694 if (meta.exportsExpression) { 9695 scopeMap.set('exports', meta.exportsExpression); 9696 } 9697 } 9698 if (meta.kind === R3NgModuleMetadataKind.Local && meta.bootstrapExpression) { 9699 scopeMap.set('bootstrap', meta.bootstrapExpression); 9700 } 9701 if (Object.keys(scopeMap.values).length === 0) { 9702 return null; 9703 } 9704 // setNgModuleScope(...) 9705 const fnCall = new InvokeFunctionExpr( /* fn */importExpr(Identifiers.setNgModuleScope), /* args */[meta.type.value, scopeMap.toLiteralMap()]); 9706 // (ngJitMode guard) && setNgModuleScope(...) 9707 const guardedCall = jitOnlyGuardedExpression(fnCall); 9708 // function() { (ngJitMode guard) && setNgModuleScope(...); } 9709 const iife = new FunctionExpr( /* params */[], /* statements */[guardedCall.toStmt()]); 9710 // (function() { (ngJitMode guard) && setNgModuleScope(...); })() 9711 const iifeCall = new InvokeFunctionExpr( /* fn */iife, /* args */[]); 9712 return iifeCall.toStmt(); 9713} 9714function tupleTypeOf(exp) { 9715 const types = exp.map(ref => typeofExpr(ref.type)); 9716 return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE; 9717} 9718function tupleOfTypes(types) { 9719 const typeofTypes = types.map(type => typeofExpr(type)); 9720 return types.length > 0 ? expressionType(literalArr(typeofTypes)) : NONE_TYPE; 9721} 9722function compilePipeFromMetadata(metadata) { 9723 const definitionMapValues = []; 9724 // e.g. `name: 'myPipe'` 9725 definitionMapValues.push({ 9726 key: 'name', 9727 value: literal(metadata.pipeName), 9728 quoted: false 9729 }); 9730 // e.g. `type: MyPipe` 9731 definitionMapValues.push({ 9732 key: 'type', 9733 value: metadata.type.value, 9734 quoted: false 9735 }); 9736 // e.g. `pure: true` 9737 definitionMapValues.push({ 9738 key: 'pure', 9739 value: literal(metadata.pure), 9740 quoted: false 9741 }); 9742 if (metadata.isStandalone) { 9743 definitionMapValues.push({ 9744 key: 'standalone', 9745 value: literal(true), 9746 quoted: false 9747 }); 9748 } 9749 const expression = importExpr(Identifiers.definePipe).callFn([literalMap(definitionMapValues)], undefined, true); 9750 const type = createPipeType(metadata); 9751 return { 9752 expression, 9753 type, 9754 statements: [] 9755 }; 9756} 9757function createPipeType(metadata) { 9758 return new ExpressionType(importExpr(Identifiers.PipeDeclaration, [typeWithParameters(metadata.type.type, metadata.typeArgumentCount), new ExpressionType(new LiteralExpr(metadata.pipeName)), new ExpressionType(new LiteralExpr(metadata.isStandalone))])); 9759} 9760var R3TemplateDependencyKind; 9761(function (R3TemplateDependencyKind) { 9762 R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive"; 9763 R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe"; 9764 R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule"; 9765})(R3TemplateDependencyKind || (R3TemplateDependencyKind = {})); 9766class ParserError { 9767 constructor(message, input, errLocation, ctxLocation) { 9768 this.input = input; 9769 this.errLocation = errLocation; 9770 this.ctxLocation = ctxLocation; 9771 this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`; 9772 } 9773} 9774class ParseSpan { 9775 constructor(start, end) { 9776 this.start = start; 9777 this.end = end; 9778 } 9779 toAbsolute(absoluteOffset) { 9780 return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end); 9781 } 9782} 9783class AST { 9784 constructor(span, 9785 /** 9786 * Absolute location of the expression AST in a source code file. 9787 */ 9788 sourceSpan) { 9789 this.span = span; 9790 this.sourceSpan = sourceSpan; 9791 } 9792 toString() { 9793 return 'AST'; 9794 } 9795} 9796class ASTWithName extends AST { 9797 constructor(span, sourceSpan, nameSpan) { 9798 super(span, sourceSpan); 9799 this.nameSpan = nameSpan; 9800 } 9801} 9802class EmptyExpr$1 extends AST { 9803 visit(visitor, context = null) { 9804 // do nothing 9805 } 9806} 9807class ImplicitReceiver extends AST { 9808 visit(visitor, context = null) { 9809 return visitor.visitImplicitReceiver(this, context); 9810 } 9811} 9812/** 9813 * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class 9814 * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the 9815 * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"` 9816 * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated 9817 * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`. 9818 * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future. 9819 */ 9820class ThisReceiver extends ImplicitReceiver { 9821 visit(visitor, context = null) { 9822 return visitor.visitThisReceiver?.(this, context); 9823 } 9824} 9825/** 9826 * Multiple expressions separated by a semicolon. 9827 */ 9828class Chain extends AST { 9829 constructor(span, sourceSpan, expressions) { 9830 super(span, sourceSpan); 9831 this.expressions = expressions; 9832 } 9833 visit(visitor, context = null) { 9834 return visitor.visitChain(this, context); 9835 } 9836} 9837class Conditional extends AST { 9838 constructor(span, sourceSpan, condition, trueExp, falseExp) { 9839 super(span, sourceSpan); 9840 this.condition = condition; 9841 this.trueExp = trueExp; 9842 this.falseExp = falseExp; 9843 } 9844 visit(visitor, context = null) { 9845 return visitor.visitConditional(this, context); 9846 } 9847} 9848class PropertyRead extends ASTWithName { 9849 constructor(span, sourceSpan, nameSpan, receiver, name) { 9850 super(span, sourceSpan, nameSpan); 9851 this.receiver = receiver; 9852 this.name = name; 9853 } 9854 visit(visitor, context = null) { 9855 return visitor.visitPropertyRead(this, context); 9856 } 9857} 9858class PropertyWrite extends ASTWithName { 9859 constructor(span, sourceSpan, nameSpan, receiver, name, value) { 9860 super(span, sourceSpan, nameSpan); 9861 this.receiver = receiver; 9862 this.name = name; 9863 this.value = value; 9864 } 9865 visit(visitor, context = null) { 9866 return visitor.visitPropertyWrite(this, context); 9867 } 9868} 9869class SafePropertyRead extends ASTWithName { 9870 constructor(span, sourceSpan, nameSpan, receiver, name) { 9871 super(span, sourceSpan, nameSpan); 9872 this.receiver = receiver; 9873 this.name = name; 9874 } 9875 visit(visitor, context = null) { 9876 return visitor.visitSafePropertyRead(this, context); 9877 } 9878} 9879class KeyedRead extends AST { 9880 constructor(span, sourceSpan, receiver, key) { 9881 super(span, sourceSpan); 9882 this.receiver = receiver; 9883 this.key = key; 9884 } 9885 visit(visitor, context = null) { 9886 return visitor.visitKeyedRead(this, context); 9887 } 9888} 9889class SafeKeyedRead extends AST { 9890 constructor(span, sourceSpan, receiver, key) { 9891 super(span, sourceSpan); 9892 this.receiver = receiver; 9893 this.key = key; 9894 } 9895 visit(visitor, context = null) { 9896 return visitor.visitSafeKeyedRead(this, context); 9897 } 9898} 9899class KeyedWrite extends AST { 9900 constructor(span, sourceSpan, receiver, key, value) { 9901 super(span, sourceSpan); 9902 this.receiver = receiver; 9903 this.key = key; 9904 this.value = value; 9905 } 9906 visit(visitor, context = null) { 9907 return visitor.visitKeyedWrite(this, context); 9908 } 9909} 9910class BindingPipe extends ASTWithName { 9911 constructor(span, sourceSpan, exp, name, args, nameSpan) { 9912 super(span, sourceSpan, nameSpan); 9913 this.exp = exp; 9914 this.name = name; 9915 this.args = args; 9916 } 9917 visit(visitor, context = null) { 9918 return visitor.visitPipe(this, context); 9919 } 9920} 9921class LiteralPrimitive extends AST { 9922 constructor(span, sourceSpan, value) { 9923 super(span, sourceSpan); 9924 this.value = value; 9925 } 9926 visit(visitor, context = null) { 9927 return visitor.visitLiteralPrimitive(this, context); 9928 } 9929} 9930class LiteralArray extends AST { 9931 constructor(span, sourceSpan, expressions) { 9932 super(span, sourceSpan); 9933 this.expressions = expressions; 9934 } 9935 visit(visitor, context = null) { 9936 return visitor.visitLiteralArray(this, context); 9937 } 9938} 9939class LiteralMap extends AST { 9940 constructor(span, sourceSpan, keys, values) { 9941 super(span, sourceSpan); 9942 this.keys = keys; 9943 this.values = values; 9944 } 9945 visit(visitor, context = null) { 9946 return visitor.visitLiteralMap(this, context); 9947 } 9948} 9949class Interpolation$1 extends AST { 9950 constructor(span, sourceSpan, strings, expressions) { 9951 super(span, sourceSpan); 9952 this.strings = strings; 9953 this.expressions = expressions; 9954 } 9955 visit(visitor, context = null) { 9956 return visitor.visitInterpolation(this, context); 9957 } 9958} 9959class Binary extends AST { 9960 constructor(span, sourceSpan, operation, left, right) { 9961 super(span, sourceSpan); 9962 this.operation = operation; 9963 this.left = left; 9964 this.right = right; 9965 } 9966 visit(visitor, context = null) { 9967 return visitor.visitBinary(this, context); 9968 } 9969} 9970/** 9971 * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST 9972 * node that was originally used. This inheritance relation can be deleted in some future major, 9973 * after consumers have been given a chance to fully support Unary. 9974 */ 9975class Unary extends Binary { 9976 /** 9977 * Creates a unary minus expression "-x", represented as `Binary` using "0 - x". 9978 */ 9979 static createMinus(span, sourceSpan, expr) { 9980 return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr); 9981 } 9982 /** 9983 * Creates a unary plus expression "+x", represented as `Binary` using "x - 0". 9984 */ 9985 static createPlus(span, sourceSpan, expr) { 9986 return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0)); 9987 } 9988 /** 9989 * During the deprecation period this constructor is private, to avoid consumers from creating 9990 * a `Unary` with the fallback properties for `Binary`. 9991 */ 9992 constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) { 9993 super(span, sourceSpan, binaryOp, binaryLeft, binaryRight); 9994 this.operator = operator; 9995 this.expr = expr; 9996 // Redeclare the properties that are inherited from `Binary` as `never`, as consumers should not 9997 // depend on these fields when operating on `Unary`. 9998 this.left = null; 9999 this.right = null; 10000 this.operation = null; 10001 } 10002 visit(visitor, context = null) { 10003 if (visitor.visitUnary !== undefined) { 10004 return visitor.visitUnary(this, context); 10005 } 10006 return visitor.visitBinary(this, context); 10007 } 10008} 10009class PrefixNot extends AST { 10010 constructor(span, sourceSpan, expression) { 10011 super(span, sourceSpan); 10012 this.expression = expression; 10013 } 10014 visit(visitor, context = null) { 10015 return visitor.visitPrefixNot(this, context); 10016 } 10017} 10018class NonNullAssert extends AST { 10019 constructor(span, sourceSpan, expression) { 10020 super(span, sourceSpan); 10021 this.expression = expression; 10022 } 10023 visit(visitor, context = null) { 10024 return visitor.visitNonNullAssert(this, context); 10025 } 10026} 10027class Call extends AST { 10028 constructor(span, sourceSpan, receiver, args, argumentSpan) { 10029 super(span, sourceSpan); 10030 this.receiver = receiver; 10031 this.args = args; 10032 this.argumentSpan = argumentSpan; 10033 } 10034 visit(visitor, context = null) { 10035 return visitor.visitCall(this, context); 10036 } 10037} 10038class SafeCall extends AST { 10039 constructor(span, sourceSpan, receiver, args, argumentSpan) { 10040 super(span, sourceSpan); 10041 this.receiver = receiver; 10042 this.args = args; 10043 this.argumentSpan = argumentSpan; 10044 } 10045 visit(visitor, context = null) { 10046 return visitor.visitSafeCall(this, context); 10047 } 10048} 10049/** 10050 * Records the absolute position of a text span in a source file, where `start` and `end` are the 10051 * starting and ending byte offsets, respectively, of the text span in a source file. 10052 */ 10053class AbsoluteSourceSpan { 10054 constructor(start, end) { 10055 this.start = start; 10056 this.end = end; 10057 } 10058} 10059class ASTWithSource extends AST { 10060 constructor(ast, source, location, absoluteOffset, errors) { 10061 super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length)); 10062 this.ast = ast; 10063 this.source = source; 10064 this.location = location; 10065 this.errors = errors; 10066 } 10067 visit(visitor, context = null) { 10068 if (visitor.visitASTWithSource) { 10069 return visitor.visitASTWithSource(this, context); 10070 } 10071 return this.ast.visit(visitor, context); 10072 } 10073 toString() { 10074 return `${this.source} in ${this.location}`; 10075 } 10076} 10077class VariableBinding { 10078 /** 10079 * @param sourceSpan entire span of the binding. 10080 * @param key name of the LHS along with its span. 10081 * @param value optional value for the RHS along with its span. 10082 */ 10083 constructor(sourceSpan, key, value) { 10084 this.sourceSpan = sourceSpan; 10085 this.key = key; 10086 this.value = value; 10087 } 10088} 10089class ExpressionBinding { 10090 /** 10091 * @param sourceSpan entire span of the binding. 10092 * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its 10093 * span. Note that the length of the span may not be the same as 10094 * `key.source.length`. For example,
10095 * 1. key.source = ngFor, key.span is for "ngFor" 10096 * 2. key.source = ngForOf, key.span is for "of" 10097 * 3. key.source = ngForTrackBy, key.span is for "trackBy" 10098 * @param value optional expression for the RHS. 10099 */ 10100 constructor(sourceSpan, key, value) { 10101 this.sourceSpan = sourceSpan; 10102 this.key = key; 10103 this.value = value; 10104 } 10105} 10106class RecursiveAstVisitor { 10107 visit(ast, context) { 10108 // The default implementation just visits every node. 10109 // Classes that extend RecursiveAstVisitor should override this function 10110 // to selectively visit the specified node. 10111 ast.visit(this, context); 10112 } 10113 visitUnary(ast, context) { 10114 this.visit(ast.expr, context); 10115 } 10116 visitBinary(ast, context) { 10117 this.visit(ast.left, context); 10118 this.visit(ast.right, context); 10119 } 10120 visitChain(ast, context) { 10121 this.visitAll(ast.expressions, context); 10122 } 10123 visitConditional(ast, context) { 10124 this.visit(ast.condition, context); 10125 this.visit(ast.trueExp, context); 10126 this.visit(ast.falseExp, context); 10127 } 10128 visitPipe(ast, context) { 10129 this.visit(ast.exp, context); 10130 this.visitAll(ast.args, context); 10131 } 10132 visitImplicitReceiver(ast, context) {} 10133 visitThisReceiver(ast, context) {} 10134 visitInterpolation(ast, context) { 10135 this.visitAll(ast.expressions, context); 10136 } 10137 visitKeyedRead(ast, context) { 10138 this.visit(ast.receiver, context); 10139 this.visit(ast.key, context); 10140 } 10141 visitKeyedWrite(ast, context) { 10142 this.visit(ast.receiver, context); 10143 this.visit(ast.key, context); 10144 this.visit(ast.value, context); 10145 } 10146 visitLiteralArray(ast, context) { 10147 this.visitAll(ast.expressions, context); 10148 } 10149 visitLiteralMap(ast, context) { 10150 this.visitAll(ast.values, context); 10151 } 10152 visitLiteralPrimitive(ast, context) {} 10153 visitPrefixNot(ast, context) { 10154 this.visit(ast.expression, context); 10155 } 10156 visitNonNullAssert(ast, context) { 10157 this.visit(ast.expression, context); 10158 } 10159 visitPropertyRead(ast, context) { 10160 this.visit(ast.receiver, context); 10161 } 10162 visitPropertyWrite(ast, context) { 10163 this.visit(ast.receiver, context); 10164 this.visit(ast.value, context); 10165 } 10166 visitSafePropertyRead(ast, context) { 10167 this.visit(ast.receiver, context); 10168 } 10169 visitSafeKeyedRead(ast, context) { 10170 this.visit(ast.receiver, context); 10171 this.visit(ast.key, context); 10172 } 10173 visitCall(ast, context) { 10174 this.visit(ast.receiver, context); 10175 this.visitAll(ast.args, context); 10176 } 10177 visitSafeCall(ast, context) { 10178 this.visit(ast.receiver, context); 10179 this.visitAll(ast.args, context); 10180 } 10181 // This is not part of the AstVisitor interface, just a helper method 10182 visitAll(asts, context) { 10183 for (const ast of asts) { 10184 this.visit(ast, context); 10185 } 10186 } 10187} 10188class AstTransformer { 10189 visitImplicitReceiver(ast, context) { 10190 return ast; 10191 } 10192 visitThisReceiver(ast, context) { 10193 return ast; 10194 } 10195 visitInterpolation(ast, context) { 10196 return new Interpolation$1(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions)); 10197 } 10198 visitLiteralPrimitive(ast, context) { 10199 return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value); 10200 } 10201 visitPropertyRead(ast, context) { 10202 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name); 10203 } 10204 visitPropertyWrite(ast, context) { 10205 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this)); 10206 } 10207 visitSafePropertyRead(ast, context) { 10208 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name); 10209 } 10210 visitLiteralArray(ast, context) { 10211 return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions)); 10212 } 10213 visitLiteralMap(ast, context) { 10214 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values)); 10215 } 10216 visitUnary(ast, context) { 10217 switch (ast.operator) { 10218 case '+': 10219 return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this)); 10220 case '-': 10221 return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this)); 10222 default: 10223 throw new Error(`Unknown unary operator ${ast.operator}`); 10224 } 10225 } 10226 visitBinary(ast, context) { 10227 return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this)); 10228 } 10229 visitPrefixNot(ast, context) { 10230 return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this)); 10231 } 10232 visitNonNullAssert(ast, context) { 10233 return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this)); 10234 } 10235 visitConditional(ast, context) { 10236 return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this)); 10237 } 10238 visitPipe(ast, context) { 10239 return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan); 10240 } 10241 visitKeyedRead(ast, context) { 10242 return new KeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this)); 10243 } 10244 visitKeyedWrite(ast, context) { 10245 return new KeyedWrite(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this), ast.value.visit(this)); 10246 } 10247 visitCall(ast, context) { 10248 return new Call(ast.span, ast.sourceSpan, ast.receiver.visit(this), this.visitAll(ast.args), ast.argumentSpan); 10249 } 10250 visitSafeCall(ast, context) { 10251 return new SafeCall(ast.span, ast.sourceSpan, ast.receiver.visit(this), this.visitAll(ast.args), ast.argumentSpan); 10252 } 10253 visitAll(asts) { 10254 const res = []; 10255 for (let i = 0; i < asts.length; ++i) { 10256 res[i] = asts[i].visit(this); 10257 } 10258 return res; 10259 } 10260 visitChain(ast, context) { 10261 return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions)); 10262 } 10263 visitSafeKeyedRead(ast, context) { 10264 return new SafeKeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this)); 10265 } 10266} 10267// A transformer that only creates new nodes if the transformer makes a change or 10268// a change is made a child node. 10269class AstMemoryEfficientTransformer { 10270 visitImplicitReceiver(ast, context) { 10271 return ast; 10272 } 10273 visitThisReceiver(ast, context) { 10274 return ast; 10275 } 10276 visitInterpolation(ast, context) { 10277 const expressions = this.visitAll(ast.expressions); 10278 if (expressions !== ast.expressions) return new Interpolation$1(ast.span, ast.sourceSpan, ast.strings, expressions); 10279 return ast; 10280 } 10281 visitLiteralPrimitive(ast, context) { 10282 return ast; 10283 } 10284 visitPropertyRead(ast, context) { 10285 const receiver = ast.receiver.visit(this); 10286 if (receiver !== ast.receiver) { 10287 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name); 10288 } 10289 return ast; 10290 } 10291 visitPropertyWrite(ast, context) { 10292 const receiver = ast.receiver.visit(this); 10293 const value = ast.value.visit(this); 10294 if (receiver !== ast.receiver || value !== ast.value) { 10295 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value); 10296 } 10297 return ast; 10298 } 10299 visitSafePropertyRead(ast, context) { 10300 const receiver = ast.receiver.visit(this); 10301 if (receiver !== ast.receiver) { 10302 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name); 10303 } 10304 return ast; 10305 } 10306 visitLiteralArray(ast, context) { 10307 const expressions = this.visitAll(ast.expressions); 10308 if (expressions !== ast.expressions) { 10309 return new LiteralArray(ast.span, ast.sourceSpan, expressions); 10310 } 10311 return ast; 10312 } 10313 visitLiteralMap(ast, context) { 10314 const values = this.visitAll(ast.values); 10315 if (values !== ast.values) { 10316 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values); 10317 } 10318 return ast; 10319 } 10320 visitUnary(ast, context) { 10321 const expr = ast.expr.visit(this); 10322 if (expr !== ast.expr) { 10323 switch (ast.operator) { 10324 case '+': 10325 return Unary.createPlus(ast.span, ast.sourceSpan, expr); 10326 case '-': 10327 return Unary.createMinus(ast.span, ast.sourceSpan, expr); 10328 default: 10329 throw new Error(`Unknown unary operator ${ast.operator}`); 10330 } 10331 } 10332 return ast; 10333 } 10334 visitBinary(ast, context) { 10335 const left = ast.left.visit(this); 10336 const right = ast.right.visit(this); 10337 if (left !== ast.left || right !== ast.right) { 10338 return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right); 10339 } 10340 return ast; 10341 } 10342 visitPrefixNot(ast, context) { 10343 const expression = ast.expression.visit(this); 10344 if (expression !== ast.expression) { 10345 return new PrefixNot(ast.span, ast.sourceSpan, expression); 10346 } 10347 return ast; 10348 } 10349 visitNonNullAssert(ast, context) { 10350 const expression = ast.expression.visit(this); 10351 if (expression !== ast.expression) { 10352 return new NonNullAssert(ast.span, ast.sourceSpan, expression); 10353 } 10354 return ast; 10355 } 10356 visitConditional(ast, context) { 10357 const condition = ast.condition.visit(this);
10358 const trueExp = ast.trueExp.visit(this); 10359 const falseExp = ast.falseExp.visit(this); 10360 if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) { 10361 return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp); 10362 } 10363 return ast; 10364 } 10365 visitPipe(ast, context) { 10366 const exp = ast.exp.visit(this); 10367 const args = this.visitAll(ast.args); 10368 if (exp !== ast.exp || args !== ast.args) { 10369 return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan); 10370 } 10371 return ast; 10372 } 10373 visitKeyedRead(ast, context) { 10374 const obj = ast.receiver.visit(this); 10375 const key = ast.key.visit(this); 10376 if (obj !== ast.receiver || key !== ast.key) { 10377 return new KeyedRead(ast.span, ast.sourceSpan, obj, key); 10378 } 10379 return ast; 10380 } 10381 visitKeyedWrite(ast, context) { 10382 const obj = ast.receiver.visit(this); 10383 const key = ast.key.visit(this); 10384 const value = ast.value.visit(this); 10385 if (obj !== ast.receiver || key !== ast.key || value !== ast.value) { 10386 return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value); 10387 } 10388 return ast; 10389 } 10390 visitAll(asts) { 10391 const res = []; 10392 let modified = false; 10393 for (let i = 0; i < asts.length; ++i) { 10394 const original = asts[i]; 10395 const value = original.visit(this); 10396 res[i] = value; 10397 modified = modified || value !== original; 10398 } 10399 return modified ? res : asts; 10400 } 10401 visitChain(ast, context) { 10402 const expressions = this.visitAll(ast.expressions); 10403 if (expressions !== ast.expressions) { 10404 return new Chain(ast.span, ast.sourceSpan, expressions); 10405 } 10406 return ast; 10407 } 10408 visitCall(ast, context) { 10409 const receiver = ast.receiver.visit(this); 10410 const args = this.visitAll(ast.args); 10411 if (receiver !== ast.receiver || args !== ast.args) { 10412 return new Call(ast.span, ast.sourceSpan, receiver, args, ast.argumentSpan); 10413 } 10414 return ast; 10415 } 10416 visitSafeCall(ast, context) { 10417 const receiver = ast.receiver.visit(this); 10418 const args = this.visitAll(ast.args); 10419 if (receiver !== ast.receiver || args !== ast.args) { 10420 return new SafeCall(ast.span, ast.sourceSpan, receiver, args, ast.argumentSpan); 10421 } 10422 return ast; 10423 } 10424 visitSafeKeyedRead(ast, context) { 10425 const obj = ast.receiver.visit(this); 10426 const key = ast.key.visit(this); 10427 if (obj !== ast.receiver || key !== ast.key) { 10428 return new SafeKeyedRead(ast.span, ast.sourceSpan, obj, key); 10429 } 10430 return ast; 10431 } 10432} 10433// Bindings 10434class ParsedProperty { 10435 constructor(name, expression, type, sourceSpan, keySpan, valueSpan) { 10436 this.name = name; 10437 this.expression = expression; 10438 this.type = type; 10439 this.sourceSpan = sourceSpan; 10440 this.keySpan = keySpan; 10441 this.valueSpan = valueSpan; 10442 this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR; 10443 this.isAnimation = this.type === ParsedPropertyType.ANIMATION; 10444 } 10445} 10446var ParsedPropertyType; 10447(function (ParsedPropertyType) { 10448 ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT"; 10449 ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR"; 10450 ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION"; 10451 ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY"; 10452})(ParsedPropertyType || (ParsedPropertyType = {})); 10453class ParsedEvent { 10454 constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) { 10455 this.name = name; 10456 this.targetOrPhase = targetOrPhase; 10457 this.type = type; 10458 this.handler = handler; 10459 this.sourceSpan = sourceSpan; 10460 this.handlerSpan = handlerSpan; 10461 this.keySpan = keySpan; 10462 } 10463} 10464/** 10465 * ParsedVariable represents a variable declaration in a microsyntax expression. 10466 */ 10467class ParsedVariable { 10468 constructor(name, value, sourceSpan, keySpan, valueSpan) { 10469 this.name = name; 10470 this.value = value; 10471 this.sourceSpan = sourceSpan; 10472 this.keySpan = keySpan; 10473 this.valueSpan = valueSpan; 10474 } 10475} 10476class BoundElementProperty { 10477 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) { 10478 this.name = name; 10479 this.type = type; 10480 this.securityContext = securityContext; 10481 this.value = value; 10482 this.unit = unit; 10483 this.sourceSpan = sourceSpan; 10484 this.keySpan = keySpan; 10485 this.valueSpan = valueSpan; 10486 } 10487} 10488class EventHandlerVars { 10489 static #_ = this.event = variable('$event'); 10490} 10491/** 10492 * Converts the given expression AST into an executable output AST, assuming the expression is 10493 * used in an action binding (e.g. an event handler). 10494 */ 10495function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) { 10496 localResolver ??= new DefaultLocalResolver(globals); 10497 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */false, baseSourceSpan, implicitReceiverAccesses); 10498 const actionStmts = []; 10499 flattenStatements(convertActionBuiltins(action).visit(visitor, _Mode.Statement), actionStmts); 10500 prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts); 10501 if (visitor.usesImplicitReceiver) { 10502 localResolver.notifyImplicitReceiverUse(); 10503 } 10504 const lastIndex = actionStmts.length - 1; 10505 if (lastIndex >= 0) { 10506 const lastStatement = actionStmts[lastIndex]; 10507 // Ensure that the value of the last expression statement is returned 10508 if (lastStatement instanceof ExpressionStatement) {
10509 actionStmts[lastIndex] = new ReturnStatement(lastStatement.expr); 10510 } 10511 } 10512 return actionStmts; 10513} 10514function convertAssignmentActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) { 10515 localResolver ??= new DefaultLocalResolver(globals); 10516 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */false, baseSourceSpan, implicitReceiverAccesses); 10517 let convertedAction = convertActionBuiltins(action).visit(visitor, _Mode.Statement); 10518 // This should already have been asserted in the parser, but we verify it here just in case. 10519 if (!(convertedAction instanceof ExpressionStatement)) { 10520 throw new Error(`Illegal state: unsupported expression in two-way action binding.`); 10521 } 10522 // Converts `[(ngModel)]="name"` to `twoWayBindingSet(ctx.name, $event) || (ctx.name = $event)`. 10523 convertedAction = wrapAssignmentAction(convertedAction.expr).toStmt(); 10524 const actionStmts = []; 10525 flattenStatements(convertedAction, actionStmts); 10526 prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts); 10527 // Assignment events always return `$event`.
10528 actionStmts.push(new ReturnStatement(EventHandlerVars.event)); 10529 implicitReceiverAccesses?.add(EventHandlerVars.event.name); 10530 if (visitor.usesImplicitReceiver) { 10531 localResolver.notifyImplicitReceiverUse(); 10532 } 10533 return actionStmts; 10534} 10535function wrapAssignmentReadExpression(ast) { 10536 return new ExternalExpr(Identifiers.twoWayBindingSet).callFn([ast, EventHandlerVars.event]).or(ast.set(EventHandlerVars.event)); 10537} 10538function isReadExpression$1(value) { 10539 return value instanceof ReadPropExpr || value instanceof ReadKeyExpr; 10540} 10541function wrapAssignmentAction(ast) { 10542 // The only officially supported expressions inside of a two-way binding are read expressions. 10543 if (isReadExpression$1(ast)) { 10544 return wrapAssignmentReadExpression(ast); 10545 } 10546 // However, historically the expression parser was handling two-way events by appending `=$event` 10547 // to the raw string before attempting to parse it. This has led to bugs over the years (see 10548 // #37809) and to unintentionally supporting unassignable events in the two-way binding. The 10549 // logic below aims to emulate the old behavior while still supporting the new output format 10550 // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense 10551 // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"` 10552 // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used 10553 // to generate before #54154. 10554 if (ast instanceof BinaryOperatorExpr && isReadExpression$1(ast.rhs)) { 10555 // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)` 10556 return new BinaryOperatorExpr(ast.operator, ast.lhs, wrapAssignmentReadExpression(ast.rhs)); 10557 } 10558 // Note: this also supports nullish coalescing expressions which 10559 // would've been downleveled to ternary expressions by this point. 10560 if (ast instanceof ConditionalExpr && isReadExpression$1(ast.falseCase)) { 10561 // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)` 10562 return new ConditionalExpr(ast.condition, ast.trueCase, wrapAssignmentReadExpression(ast.falseCase)); 10563 } 10564 // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)` 10565 // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping 10566 // node doesn't affect the result so we don't need to carry it over. 10567 if (ast instanceof NotExpr) { 10568 let expr = ast.condition; 10569 while (true) { 10570 if (expr instanceof NotExpr) { 10571 expr = expr.condition; 10572 } else { 10573 if (isReadExpression$1(expr)) { 10574 return wrapAssignmentReadExpression(expr); 10575 } 10576 break; 10577 } 10578 } 10579 } 10580 throw new Error(`Illegal state: unsupported expression in two-way action binding.`); 10581} 10582function convertPropertyBindingBuiltins(converterFactory, ast) { 10583 return convertBuiltins(converterFactory, ast); 10584} 10585class ConvertPropertyBindingResult { 10586 constructor(stmts, currValExpr) { 10587 this.stmts = stmts; 10588 this.currValExpr = currValExpr; 10589 } 10590} 10591/** 10592 * Converts the given expression AST into an executable output AST, assuming the expression 10593 * is used in property binding. The expression has to be preprocessed via 10594 * `convertPropertyBindingBuiltins`. 10595 */ 10596function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId) { 10597 if (!localResolver) { 10598 localResolver = new DefaultLocalResolver(); 10599 } 10600 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */false); 10601 const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression); 10602 const stmts = getStatementsFromVisitor(visitor, bindingId); 10603 if (visitor.usesImplicitReceiver) { 10604 localResolver.notifyImplicitReceiverUse(); 10605 } 10606 return new ConvertPropertyBindingResult(stmts, outputExpr); 10607} 10608/** Converts an AST to a pure function that may have access to the component scope. */ 10609function convertPureComponentScopeFunction(ast, localResolver, implicitReceiver, bindingId) { 10610 const converted = convertPropertyBindingBuiltins({ 10611 createLiteralArrayConverter: () => args => literalArr(args), 10612 createLiteralMapConverter: keys => values => literalMap(keys.map((key, index) => { 10613 return { 10614 key: key.key, 10615 value: values[index], 10616 quoted: key.quoted 10617 }; 10618 })), 10619 createPipeConverter: () => { 10620 throw new Error('Illegal State: Pipes are not allowed in this context'); 10621 } 10622 }, ast); 10623 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, false); 10624 const statements = []; 10625 flattenStatements(converted.visit(visitor, _Mode.Statement), statements); 10626 return statements; 10627} 10628/** 10629 * Given some expression, such as a binding or interpolation expression, and a context expression to 10630 * look values up on, visit each facet of the given expression resolving values from the context 10631 * expression such that a list of arguments can be derived from the found values that can be used as 10632 * arguments to an external update instruction. 10633 * 10634 * @param localResolver The resolver to use to look up expressions by name appropriately 10635 * @param contextVariableExpression The expression representing the context variable used to create 10636 * the final argument expressions 10637 * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to 10638 * be resolved and what arguments list to build. 10639 * @param bindingId A name prefix used to create temporary variable names if they're needed for the 10640 * arguments generated 10641 * @returns An array of expressions that can be passed as arguments to instruction expressions like 10642 * `o.importExpr(R3.propertyInterpolate).callFn(result)` 10643 */ 10644function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) { 10645 const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, /* supportsInterpolation */true); 10646 const outputExpr = visitor.visitInterpolation(expressionWithArgumentsToExtract, _Mode.Expression); 10647 if (visitor.usesImplicitReceiver) { 10648 localResolver.notifyImplicitReceiverUse(); 10649 } 10650 const stmts = getStatementsFromVisitor(visitor, bindingId); 10651 const args = outputExpr.args; 10652 return { 10653 stmts, 10654 args 10655 }; 10656} 10657function getStatementsFromVisitor(visitor, bindingId) { 10658 const stmts = []; 10659 for (let i = 0; i < visitor.temporaryCount; i++) { 10660 stmts.push(temporaryDeclaration(bindingId, i)); 10661 } 10662 return stmts; 10663} 10664function convertBuiltins(converterFactory, ast) { 10665 const visitor = new _BuiltinAstConverter(converterFactory); 10666 return ast.visit(visitor); 10667} 10668function convertActionBuiltins(action) { 10669 const converterFactory = { 10670 createLiteralArrayConverter: () => {
10671 // Note: no caching for literal arrays in actions. 10672 return args => literalArr(args); 10673 }, 10674 createLiteralMapConverter: keys => { 10675 // Note: no caching for literal maps in actions. 10676 return values => { 10677 const entries = keys.map((k, i) => ({ 10678 key: k.key, 10679 value: values[i], 10680 quoted: k.quoted 10681 })); 10682 return literalMap(entries); 10683 }; 10684 }, 10685 createPipeConverter: name => { 10686 throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`); 10687 } 10688 }; 10689 return convertPropertyBindingBuiltins(converterFactory, action); 10690} 10691function temporaryName(bindingId, temporaryNumber) { 10692 return `tmp_${bindingId}_${temporaryNumber}`; 10693} 10694function temporaryDeclaration(bindingId, temporaryNumber) { 10695 return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber)); 10696} 10697function prependTemporaryDecls(temporaryCount, bindingId, statements) { 10698 for (let i = temporaryCount - 1; i >= 0; i--) { 10699 statements.unshift(temporaryDeclaration(bindingId, i)); 10700 } 10701} 10702var _Mode; 10703(function (_Mode) { 10704 _Mode[_Mode["Statement"] = 0] = "Statement"; 10705 _Mode[_Mode["Expression"] = 1] = "Expression"; 10706})(_Mode || (_Mode = {})); 10707function ensureStatementMode(mode, ast) { 10708 if (mode !== _Mode.Statement) { 10709 throw new Error(`Expected a statement, but saw ${ast}`); 10710 } 10711} 10712function ensureExpressionMode(mode, ast) { 10713 if (mode !== _Mode.Expression) { 10714 throw new Error(`Expected an expression, but saw ${ast}`); 10715 } 10716} 10717function convertToStatementIfNeeded(mode, expr) { 10718 if (mode === _Mode.Statement) { 10719 return expr.toStmt(); 10720 } else { 10721 return expr; 10722 } 10723} 10724class _BuiltinAstConverter extends AstTransformer { 10725 constructor(_converterFactory) { 10726 super(); 10727 this._converterFactory = _converterFactory; 10728 } 10729 visitPipe(ast, context) { 10730 const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context)); 10731 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length)); 10732 } 10733 visitLiteralArray(ast, context) { 10734 const args = ast.expressions.map(ast => ast.visit(this, context)); 10735 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length)); 10736 } 10737 visitLiteralMap(ast, context) { 10738 const args = ast.values.map(ast => ast.visit(this, context)); 10739 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys)); 10740 } 10741} 10742class _AstToIrVisitor { 10743 constructor(_localResolver, _implicitReceiver, bindingId, supportsInterpolation, baseSourceSpan, implicitReceiverAccesses) { 10744 this._localResolver = _localResolver; 10745 this._implicitReceiver = _implicitReceiver; 10746 this.bindingId = bindingId; 10747 this.supportsInterpolation = supportsInterpolation; 10748 this.baseSourceSpan = baseSourceSpan; 10749 this.implicitReceiverAccesses = implicitReceiverAccesses; 10750 this._nodeMap = new Map(); 10751 this._resultMap = new Map(); 10752 this._currentTemporary = 0; 10753 this.temporaryCount = 0; 10754 this.usesImplicitReceiver = false; 10755 } 10756 visitUnary(ast, mode) { 10757 let op; 10758 switch (ast.operator) { 10759 case '+': 10760 op = UnaryOperator.Plus; 10761 break; 10762 case '-': 10763 op = UnaryOperator.Minus; 10764 break; 10765 default: 10766 throw new Error(`Unsupported operator ${ast.operator}`); 10767 } 10768 return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span))); 10769 } 10770 visitBinary(ast, mode) { 10771 let op; 10772 switch (ast.operation) { 10773 case '+': 10774 op = BinaryOperator.Plus; 10775 break; 10776 case '-': 10777 op = BinaryOperator.Minus; 10778 break; 10779 case '*': 10780 op = BinaryOperator.Multiply; 10781 break; 10782 case '/': 10783 op = BinaryOperator.Divide; 10784 break; 10785 case '%': 10786 op = BinaryOperator.Modulo; 10787 break; 10788 case '&&': 10789 op = BinaryOperator.And; 10790 break; 10791 case '||': 10792 op = BinaryOperator.Or; 10793 break; 10794 case '==': 10795 op = BinaryOperator.Equals; 10796 break; 10797 case '!=': 10798 op = BinaryOperator.NotEquals; 10799 break; 10800 case '===': 10801 op = BinaryOperator.Identical; 10802 break; 10803 case '!==': 10804 op = BinaryOperator.NotIdentical; 10805 break; 10806 case '<': 10807 op = BinaryOperator.Lower;
10808 break; 10809 case '>': 10810 op = BinaryOperator.Bigger; 10811 break; 10812 case '<=': 10813 op = BinaryOperator.LowerEquals; 10814 break; 10815 case '>=': 10816 op = BinaryOperator.BiggerEquals; 10817 break; 10818 case '??': 10819 return this.convertNullishCoalesce(ast, mode); 10820 default: 10821 throw new Error(`Unsupported operation ${ast.operation}`); 10822 } 10823 return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span))); 10824 } 10825 visitChain(ast, mode) { 10826 ensureStatementMode(mode, ast); 10827 return this.visitAll(ast.expressions, mode); 10828 } 10829 visitConditional(ast, mode) { 10830 const value = this._visit(ast.condition, _Mode.Expression); 10831 return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span))); 10832 } 10833 visitPipe(ast, mode) { 10834 throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`); 10835 } 10836 visitImplicitReceiver(ast, mode) { 10837 ensureExpressionMode(mode, ast); 10838 this.usesImplicitReceiver = true; 10839 return this._implicitReceiver; 10840 } 10841 visitThisReceiver(ast, mode) { 10842 return this.visitImplicitReceiver(ast, mode); 10843 } 10844 visitInterpolation(ast, mode) { 10845 if (!this.supportsInterpolation) { 10846 throw new Error('Unexpected interpolation'); 10847 } 10848 ensureExpressionMode(mode, ast); 10849 let args = []; 10850 for (let i = 0; i < ast.strings.length - 1; i++) { 10851 args.push(literal(ast.strings[i])); 10852 args.push(this._visit(ast.expressions[i], _Mode.Expression)); 10853 } 10854 args.push(literal(ast.strings[ast.strings.length - 1])); 10855 // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the 10856 // args returned to just the value, because we're going to pass it to a special instruction. 10857 const strings = ast.strings; 10858 if (strings.length === 2 && strings[0] === '' && strings[1] === '') { 10859 // Single argument interpolate instructions. 10860 args = [args[1]]; 10861 } else if (ast.expressions.length >= 9) { 10862 // 9 or more arguments must be passed to the `interpolateV`-style instructions, which accept 10863 // an array of arguments 10864 args = [literalArr(args)]; 10865 } 10866 return new InterpolationExpression(args); 10867 } 10868 visitKeyedRead(ast, mode) { 10869 const leftMostSafe = this.leftMostSafeNode(ast); 10870 if (leftMostSafe) { 10871 return this.convertSafeAccess(ast, leftMostSafe, mode); 10872 } else { 10873 return convertToStatementIfNeeded(mode, this._visit(ast.receiver, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression))); 10874 } 10875 } 10876 visitKeyedWrite(ast, mode) { 10877 const obj = this._visit(ast.receiver, _Mode.Expression); 10878 const key = this._visit(ast.key, _Mode.Expression); 10879 const value = this._visit(ast.value, _Mode.Expression); 10880 if (obj === this._implicitReceiver) { 10881 this._localResolver.maybeRestoreView(); 10882 } 10883 return convertToStatementIfNeeded(mode, obj.key(key).set(value)); 10884 } 10885 visitLiteralArray(ast, mode) { 10886 throw new Error(`Illegal State: literal arrays should have been converted into functions`); 10887 } 10888 visitLiteralMap(ast, mode) { 10889 throw new Error(`Illegal State: literal maps should have been converted into functions`); 10890 } 10891 visitLiteralPrimitive(ast, mode) { 10892 // For literal values of null, undefined, true, or false allow type interference 10893 // to infer the type. 10894 const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ? INFERRED_TYPE : undefined; 10895 return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span))); 10896 } 10897 _getLocal(name, receiver) { 10898 if (this._localResolver.globals?.has(name) && receiver instanceof ThisReceiver) { 10899 return null; 10900 } 10901 return this._localResolver.getLocal(name); 10902 } 10903 visitPrefixNot(ast, mode) { 10904 return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression))); 10905 } 10906 visitNonNullAssert(ast, mode) { 10907 return convertToStatementIfNeeded(mode, this._visit(ast.expression, _Mode.Expression)); 10908 } 10909 visitPropertyRead(ast, mode) { 10910 const leftMostSafe = this.leftMostSafeNode(ast); 10911 if (leftMostSafe) { 10912 return this.convertSafeAccess(ast, leftMostSafe, mode); 10913 } else { 10914 let result = null; 10915 const prevUsesImplicitReceiver = this.usesImplicitReceiver; 10916 const receiver = this._visit(ast.receiver, _Mode.Expression); 10917 if (receiver === this._implicitReceiver) { 10918 result = this._getLocal(ast.name, ast.receiver); 10919 if (result) { 10920 // Restore the previous "usesImplicitReceiver" state since the implicit 10921 // receiver has been replaced with a resolved local expression. 10922 this.usesImplicitReceiver = prevUsesImplicitReceiver; 10923 this.addImplicitReceiverAccess(ast.name); 10924 } 10925 } 10926 if (result == null) { 10927 result = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); 10928 } 10929 return convertToStatementIfNeeded(mode, result); 10930 } 10931 } 10932 visitPropertyWrite(ast, mode) { 10933 const receiver = this._visit(ast.receiver, _Mode.Expression); 10934 const prevUsesImplicitReceiver = this.usesImplicitReceiver; 10935 let varExpr = null; 10936 if (receiver === this._implicitReceiver) { 10937 const localExpr = this._getLocal(ast.name, ast.receiver); 10938 if (localExpr) { 10939 if (localExpr instanceof ReadPropExpr) { 10940 // If the local variable is a property read expression, it's a reference 10941 // to a 'context.property' value and will be used as the target of the 10942 // write expression. 10943 varExpr = localExpr; 10944 // Restore the previous "usesImplicitReceiver" state since the implicit 10945 // receiver has been replaced with a resolved local expression. 10946 this.usesImplicitReceiver = prevUsesImplicitReceiver; 10947 this.addImplicitReceiverAccess(ast.name); 10948 } else { 10949 // Otherwise it's an error. 10950 const receiver = ast.name; 10951 const value = ast.value instanceof PropertyRead ? ast.value.name : undefined; 10952 throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`); 10953 } 10954 } 10955 } 10956 // If no local expression could be produced, use the original receiver's 10957 // property as the target. 10958 if (varExpr === null) { 10959 varExpr = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); 10960 } 10961 return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression))); 10962 } 10963 visitSafePropertyRead(ast, mode) { 10964 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); 10965 } 10966 visitSafeKeyedRead(ast, mode) { 10967 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); 10968 } 10969 visitAll(asts, mode) { 10970 return asts.map(ast => this._visit(ast, mode)); 10971 } 10972 visitCall(ast, mode) { 10973 const leftMostSafe = this.leftMostSafeNode(ast); 10974 if (leftMostSafe) { 10975 return this.convertSafeAccess(ast, leftMostSafe, mode); 10976 } 10977 const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
10978 if (ast instanceof BuiltinFunctionCall) { 10979 return convertToStatementIfNeeded(mode, ast.converter(convertedArgs)); 10980 } 10981 const receiver = ast.receiver; 10982 if (receiver instanceof PropertyRead && receiver.receiver instanceof ImplicitReceiver && !(receiver.receiver instanceof ThisReceiver) && receiver.name === '$any') { 10983 if (convertedArgs.length !== 1) { 10984 throw new Error(`Invalid call to $any, expected 1 argument but received ${convertedArgs.length || 'none'}`); 10985 } 10986 return convertToStatementIfNeeded(mode, convertedArgs[0]); 10987 } 10988 const call = this._visit(receiver, _Mode.Expression).callFn(convertedArgs, this.convertSourceSpan(ast.span)); 10989 return convertToStatementIfNeeded(mode, call); 10990 } 10991 visitSafeCall(ast, mode) { 10992 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); 10993 } 10994 _visit(ast, mode) { 10995 const result = this._resultMap.get(ast); 10996 if (result) return result; 10997 return (this._nodeMap.get(ast) || ast).visit(this, mode); 10998 } 10999 convertSafeAccess(ast, leftMostSafe, mode) { 11000 // If the expression contains a safe access node on the left it needs to be converted to 11001 // an expression that guards the access to the member by checking the receiver for blank. As 11002 // execution proceeds from left to right, the left most part of the expression must be guarded 11003 // first but, because member access is left associative, the right side of the expression is at 11004 // the top of the AST. The desired result requires lifting a copy of the left part of the 11005 // expression up to test it for blank before generating the unguarded version. 11006 // Consider, for example the following expression: a?.b.c?.d.e 11007 // This results in the ast: 11008 // . 11009 // / \ 11010 // ?. e 11011 // / \ 11012 // . d 11013 // / \ 11014 // ?. c 11015 // / \ 11016 // a b 11017 // The following tree should be generated: 11018 // 11019 // /---- ? ----\ 11020 // / | \ 11021 // a /--- ? ---\ null 11022 // / | \ 11023 // . . null 11024 // / \ / \ 11025 // . c . e 11026 // / \ / \ 11027 // a b . d 11028 // / \ 11029 // . c 11030 // / \ 11031 // a b 11032 // 11033 // Notice that the first guard condition is the left hand of the left most safe access node 11034 // which comes in as leftMostSafe to this routine. 11035 let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression); 11036 let temporary = undefined; 11037 if (this.needsTemporaryInSafeAccess(leftMostSafe.receiver)) { 11038 // If the expression has method calls or pipes then we need to save the result into a 11039 // temporary variable to avoid calling stateful or impure code more than once. 11040 temporary = this.allocateTemporary(); 11041 // Preserve the result in the temporary variable 11042 guardedExpression = temporary.set(guardedExpression); 11043 // Ensure all further references to the guarded expression refer to the temporary instead. 11044 this._resultMap.set(leftMostSafe.receiver, temporary); 11045 } 11046 const condition = guardedExpression.isBlank(); 11047 // Convert the ast to an unguarded access to the receiver's member. The map will substitute 11048 // leftMostNode with its unguarded version in the call to `this.visit()`. 11049 if (leftMostSafe instanceof SafeCall) { 11050 this._nodeMap.set(leftMostSafe, new Call(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.args, leftMostSafe.argumentSpan)); 11051 } else if (leftMostSafe instanceof SafeKeyedRead) { 11052 this._nodeMap.set(leftMostSafe, new KeyedRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.key)); 11053 } else { 11054 this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name)); 11055 } 11056 // Recursively convert the node now without the guarded member access. 11057 const access = this._visit(ast, _Mode.Expression); 11058 // Remove the mapping. This is not strictly required as the converter only traverses each node 11059 // once but is safer if the conversion is changed to traverse the nodes more than once. 11060 this._nodeMap.delete(leftMostSafe); 11061 // If we allocated a temporary, release it. 11062 if (temporary) { 11063 this.releaseTemporary(temporary); 11064 } 11065 // Produce the conditional 11066 return convertToStatementIfNeeded(mode, condition.conditional(NULL_EXPR, access)); 11067 } 11068 convertNullishCoalesce(ast, mode) { 11069 const left = this._visit(ast.left, _Mode.Expression); 11070 const right = this._visit(ast.right, _Mode.Expression); 11071 const temporary = this.allocateTemporary(); 11072 this.releaseTemporary(temporary); 11073 // Generate the following expression. It is identical to how TS 11074 // transpiles binary expressions with a nullish coalescing operator. 11075 // let temp; 11076 // (temp = a) !== null && temp !== undefined ? temp : b; 11077 return convertToStatementIfNeeded(mode, temporary.set(left).notIdentical(NULL_EXPR).and(temporary.notIdentical(literal(undefined))).conditional(temporary, right)); 11078 } 11079 // Given an expression of the form a?.b.c?.d.e then the left most safe node is 11080 // the (a?.b). The . and ?. are left associative thus can be rewritten as: 11081 // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or 11082 // safe method call as this needs to be transformed initially to: 11083 // a == null ? null : a.c.b.c?.d.e 11084 // then to: 11085 // a == null ? null : a.b.c == null ? null : a.b.c.d.e 11086 leftMostSafeNode(ast) { 11087 const visit = (visitor, ast) => { 11088 return (this._nodeMap.get(ast) || ast).visit(visitor); 11089 }; 11090 return ast.visit({ 11091 visitUnary(ast) { 11092 return null; 11093 }, 11094 visitBinary(ast) { 11095 return null; 11096 },
11097 visitChain(ast) { 11098 return null; 11099 }, 11100 visitConditional(ast) { 11101 return null; 11102 }, 11103 visitCall(ast) { 11104 return visit(this, ast.receiver); 11105 }, 11106 visitSafeCall(ast) { 11107 return visit(this, ast.receiver) || ast; 11108 }, 11109 visitImplicitReceiver(ast) { 11110 return null; 11111 }, 11112 visitThisReceiver(ast) { 11113 return null; 11114 }, 11115 visitInterpolation(ast) { 11116 return null; 11117 }, 11118 visitKeyedRead(ast) { 11119 return visit(this, ast.receiver); 11120 }, 11121 visitKeyedWrite(ast) { 11122 return null; 11123 }, 11124 visitLiteralArray(ast) { 11125 return null; 11126 }, 11127 visitLiteralMap(ast) { 11128 return null; 11129 }, 11130 visitLiteralPrimitive(ast) { 11131 return null; 11132 }, 11133 visitPipe(ast) { 11134 return null; 11135 }, 11136 visitPrefixNot(ast) { 11137 return null; 11138 }, 11139 visitNonNullAssert(ast) { 11140 return visit(this, ast.expression); 11141 }, 11142 visitPropertyRead(ast) { 11143 return visit(this, ast.receiver); 11144 }, 11145 visitPropertyWrite(ast) { 11146 return null; 11147 }, 11148 visitSafePropertyRead(ast) { 11149 return visit(this, ast.receiver) || ast; 11150 }, 11151 visitSafeKeyedRead(ast) { 11152 return visit(this, ast.receiver) || ast; 11153 } 11154 }); 11155 } 11156 // Returns true of the AST includes a method or a pipe indicating that, if the 11157 // expression is used as the target of a safe property or method access then 11158 // the expression should be stored into a temporary variable. 11159 needsTemporaryInSafeAccess(ast) { 11160 const visit = (visitor, ast) => { 11161 return ast && (this._nodeMap.get(ast) || ast).visit(visitor); 11162 }; 11163 const visitSome = (visitor, ast) => { 11164 return ast.some(ast => visit(visitor, ast)); 11165 }; 11166 return ast.visit({ 11167 visitUnary(ast) { 11168 return visit(this, ast.expr); 11169 }, 11170 visitBinary(ast) { 11171 return visit(this, ast.left) || visit(this, ast.right); 11172 }, 11173 visitChain(ast) { 11174 return false; 11175 }, 11176 visitConditional(ast) { 11177 return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp); 11178 }, 11179 visitCall(ast) { 11180 return true; 11181 }, 11182 visitSafeCall(ast) { 11183 return true; 11184 }, 11185 visitImplicitReceiver(ast) { 11186 return false; 11187 }, 11188 visitThisReceiver(ast) { 11189 return false; 11190 }, 11191 visitInterpolation(ast) { 11192 return visitSome(this, ast.expressions); 11193 }, 11194 visitKeyedRead(ast) { 11195 return false; 11196 }, 11197 visitKeyedWrite(ast) { 11198 return false; 11199 }, 11200 visitLiteralArray(ast) { 11201 return true; 11202 }, 11203 visitLiteralMap(ast) { 11204 return true; 11205 }, 11206 visitLiteralPrimitive(ast) { 11207 return false; 11208 }, 11209 visitPipe(ast) { 11210 return true; 11211 }, 11212 visitPrefixNot(ast) { 11213 return visit(this, ast.expression); 11214 }, 11215 visitNonNullAssert(ast) { 11216 return visit(this, ast.expression); 11217 }, 11218 visitPropertyRead(ast) { 11219 return false; 11220 }, 11221 visitPropertyWrite(ast) { 11222 return false; 11223 }, 11224 visitSafePropertyRead(ast) { 11225 return false; 11226 }, 11227 visitSafeKeyedRead(ast) { 11228 return false; 11229 } 11230 }); 11231 } 11232 allocateTemporary() { 11233 const tempNumber = this._currentTemporary++; 11234 this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount); 11235 return new ReadVarExpr(temporaryName(this.bindingId, tempNumber)); 11236 } 11237 releaseTemporary(temporary) { 11238 this._currentTemporary--; 11239 if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) { 11240 throw new Error(`Temporary ${temporary.name} released out of order`); 11241 } 11242 } 11243 /** 11244 * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`. 11245 * 11246 * `ParseSpan` objects are relative to the start of the expression. 11247 * This method converts these to full `ParseSourceSpan` objects that 11248 * show where the span is within the overall source file. 11249 * 11250 * @param span the relative span to convert. 11251 * @returns a `ParseSourceSpan` for the given span or null if no 11252 * `baseSourceSpan` was provided to this class. 11253 */ 11254 convertSourceSpan(span) { 11255 if (this.baseSourceSpan) { 11256 const start = this.baseSourceSpan.start.moveBy(span.start); 11257 const end = this.baseSourceSpan.start.moveBy(span.end); 11258 const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start); 11259 return new ParseSourceSpan(start, end, fullStart); 11260 } else { 11261 return null; 11262 } 11263 } 11264 /** Adds the name of an AST to the list of implicit receiver accesses. */ 11265 addImplicitReceiverAccess(name) {
11266 if (this.implicitReceiverAccesses) { 11267 this.implicitReceiverAccesses.add(name); 11268 } 11269 } 11270} 11271function flattenStatements(arg, output) { 11272 if (Array.isArray(arg)) { 11273 arg.forEach(entry => flattenStatements(entry, output)); 11274 } else { 11275 output.push(arg); 11276 } 11277} 11278function unsupported() { 11279 throw new Error('Unsupported operation'); 11280} 11281class InterpolationExpression extends Expression { 11282 constructor(args) { 11283 super(null, null); 11284 this.args = args; 11285 this.isConstant = unsupported; 11286 this.isEquivalent = unsupported; 11287 this.visitExpression = unsupported; 11288 this.clone = unsupported; 11289 } 11290} 11291class DefaultLocalResolver { 11292 constructor(globals) { 11293 this.globals = globals; 11294 } 11295 notifyImplicitReceiverUse() {} 11296 maybeRestoreView() {} 11297 getLocal(name) { 11298 if (name === EventHandlerVars.event.name) { 11299 return EventHandlerVars.event; 11300 } 11301 return null; 11302 } 11303} 11304class BuiltinFunctionCall extends Call { 11305 constructor(span, sourceSpan, args, converter) { 11306 super(span, sourceSpan, new EmptyExpr$1(span, sourceSpan), args, null); 11307 this.converter = converter; 11308 } 11309} 11310 11311// ================================================================================================= 11312// ================================================================================================= 11313// =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P =========== 11314// ================================================================================================= 11315// ================================================================================================= 11316// 11317// DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW! 11318// Reach out to mprobst for details. 11319// 11320// ================================================================================================= 11321/** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */ 11322let _SECURITY_SCHEMA; 11323function SECURITY_SCHEMA() { 11324 if (!_SECURITY_SCHEMA) { 11325 _SECURITY_SCHEMA = {}; 11326 // Case is insignificant below, all element and attribute names are lower-cased for lookup. 11327 registerContext(SecurityContext.HTML, ['iframe|srcdoc', '*|innerHTML', '*|outerHTML']); 11328 registerContext(SecurityContext.STYLE, ['*|style']); 11329 // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them. 11330 registerContext(SecurityContext.URL, ['*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href', 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action', 'img|src', 'input|src', 'ins|cite', 'q|cite', 'source|src', 'track|src', 'video|poster', 'video|src']); 11331 registerContext(SecurityContext.RESOURCE_URL, ['applet|code', 'applet|codebase', 'base|href', 'embed|src', 'frame|src', 'head|profile', 'html|manifest', 'iframe|src', 'link|href', 'media|src', 'object|codebase', 'object|data', 'script|src']); 11332 } 11333 return _SECURITY_SCHEMA; 11334} 11335function registerContext(ctx, specs) { 11336 for (const spec of specs) _SECURITY_SCHEMA[spec.toLowerCase()] = ctx; 11337} 11338/** 11339 * The set of security-sensitive attributes of an `<iframe>` that *must* be 11340 * applied as a static attribute only. This ensures that all security-sensitive 11341 * attributes are taken into account while creating an instance of an `<iframe>` 11342 * at runtime. 11343 * 11344 * Note: avoid using this set directly, use the `isIframeSecuritySensitiveAttr` function 11345 * in the code instead. 11346 */ 11347const IFRAME_SECURITY_SENSITIVE_ATTRS = new Set(['sandbox', 'allow', 'allowfullscreen', 'referrerpolicy', 'csp', 'fetchpriority']); 11348/** 11349 * Checks whether a given attribute name might represent a security-sensitive 11350 * attribute of an <iframe>. 11351 */ 11352function isIframeSecuritySensitiveAttr(attrName) { 11353 // The `setAttribute` DOM API is case-insensitive, so we lowercase the value 11354 // before checking it against a known security-sensitive attributes. 11355 return IFRAME_SECURITY_SENSITIVE_ATTRS.has(attrName.toLowerCase()); 11356} 11357 11358/** 11359 * The following set contains all keywords that can be used in the animation css shorthand 11360 * property and is used during the scoping of keyframes to make sure such keywords 11361 * are not modified. 11362 */ 11363const animationKeywords = new Set([ 11364// global values 11365'inherit', 'initial', 'revert', 'unset', 11366// animation-direction 11367'alternate', 'alternate-reverse', 'normal', 'reverse', 11368// animation-fill-mode 11369'backwards', 'both', 'forwards', 'none', 11370// animation-play-state 11371'paused', 'running', 11372// animation-timing-function 11373'ease', 'ease-in', 'ease-in-out', 'ease-out', 'linear', 'step-start', 'step-end', 11374// `steps()` function 11375'end', 'jump-both', 'jump-end', 'jump-none', 'jump-start', 'start']); 11376/** 11377 * The following array contains all of the CSS at-rule identifiers which are scoped. 11378 */ 11379const scopedAtRuleIdentifiers = ['@media', '@supports', '@document', '@layer', '@container', '@scope', '@starting-style']; 11380/** 11381 * The following class has its origin from a port of shadowCSS from webcomponents.js to TypeScript. 11382 * It has since diverge in many ways to tailor Angular's needs. 11383 * 11384 * Source: 11385 * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js 11386 * 11387 * The original file level comment is reproduced below 11388 */ 11389/* 11390 This is a limited shim for ShadowDOM css styling. 11391 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles 11392 11393 The intention here is to support only the styling features which can be 11394 relatively simply implemented. The goal is to allow users to avoid the 11395 most obvious pitfalls and do so without compromising performance significantly. 11396 For ShadowDOM styling that's not covered here, a set of best practices 11397 can be provided that should allow users to accomplish more complex styling. 11398 11399 The following is a list of specific ShadowDOM styling features and a brief 11400 discussion of the approach used to shim. 11401 11402 Shimmed features: 11403 11404 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host 11405 element using the :host rule. To shim this feature, the :host styles are 11406 reformatted and prefixed with a given scope name and promoted to a 11407 document level stylesheet. 11408 For example, given a scope name of .foo, a rule like this: 11409 11410 :host { 11411 background: red; 11412 } 11413 } 11414 11415 becomes: 11416 11417 .foo { 11418 background: red; 11419 } 11420 11421 * encapsulation: Styles defined within ShadowDOM, apply only to 11422 dom inside the ShadowDOM. 11423 The selectors are scoped by adding an attribute selector suffix to each
11424 simple selector that contains the host element tag name. Each element 11425 in the element's ShadowDOM template is also given the scope attribute. 11426 Thus, these rules match only elements that have the scope attribute. 11427 For example, given a scope name of x-foo, a rule like this: 11428 11429 div { 11430 font-weight: bold; 11431 } 11432 11433 becomes: 11434 11435 div[x-foo] { 11436 font-weight: bold; 11437 } 11438 11439 Note that elements that are dynamically added to a scope must have the scope 11440 selector added to them manually. 11441 11442 * upper/lower bound encapsulation: Styles which are defined outside a 11443 shadowRoot should not cross the ShadowDOM boundary and should not apply 11444 inside a shadowRoot. 11445 11446 This styling behavior is not emulated. Some possible ways to do this that 11447 were rejected due to complexity and/or performance concerns include: (1) reset 11448 every possible property for every possible selector for a given scope name; 11449 (2) re-implement css in javascript. 11450 11451 As an alternative, users should make sure to use selectors 11452 specific to the scope in which they are working. 11453 11454 * ::distributed: This behavior is not emulated. It's often not necessary 11455 to style the contents of a specific insertion point and instead, descendants 11456 of the host element can be styled selectively. Users can also create an 11457 extra node around an insertion point and style that node's contents 11458 via descendent selectors. For example, with a shadowRoot like this: 11459 11460 <style> 11461 ::content(div) { 11462 background: red; 11463 } 11464 </style> 11465 <content></content> 11466 11467 could become: 11468 11469 <style> 11470 / *@polyfill .content-container div * / 11471 ::content(div) { 11472 background: red; 11473 } 11474 </style> 11475 <div class="content-container"> 11476 <content></content> 11477 </div> 11478 11479 Note the use of @polyfill in the comment above a ShadowDOM specific style 11480 declaration. This is a directive to the styling shim to use the selector 11481 in comments in lieu of the next selector when running under polyfill. 11482*/ 11483class ShadowCss { 11484 constructor() { 11485 /** 11486 * Regular expression used to extrapolate the possible keyframes from an 11487 * animation declaration (with possibly multiple animation definitions) 11488 * 11489 * The regular expression can be divided in three parts 11490 * - (^|\s+) 11491 * simply captures how many (if any) leading whitespaces are present 11492 * - (?:(?:(['"])((?:\\\\|\\\2|(?!\2).)+)\2)|(-?[A-Za-z][\w\-]*)) 11493 * captures two different possible keyframes, ones which are quoted or ones which are valid css 11494 * idents (custom properties excluded) 11495 * - (?=[,\s;]|$) 11496 * simply matches the end of the possible keyframe, valid endings are: a comma, a space, a 11497 * semicolon or the end of the string 11498 */ 11499 this._animationDeclarationKeyframesRe = /(^|\s+)(?:(?:(['"])((?:\\\\|\\\2|(?!\2).)+)\2)|(-?[A-Za-z][\w\-]*))(?=[,\s]|$)/g; 11500 } 11501 /* 11502 * Shim some cssText with the given selector. Returns cssText that can be included in the document 11503 * 11504 * The selector is the attribute added to all elements inside the host, 11505 * The hostSelector is the attribute added to the host itself. 11506 */ 11507 shimCssText(cssText, selector, hostSelector = '') { 11508 // **NOTE**: Do not strip comments as this will cause component sourcemaps to break 11509 // due to shift in lines. 11510 // Collect comments and replace them with a placeholder, this is done to avoid complicating 11511 // the rule parsing RegExp and keep it safer. 11512 const comments = []; 11513 cssText = cssText.replace(_commentRe, m => { 11514 if (m.match(_commentWithHashRe)) { 11515 comments.push(m); 11516 } else { 11517 // Replace non hash comments with empty lines. 11518 // This is done so that we do not leak any senstive data in comments. 11519 const newLinesMatches = m.match(_newLinesRe); 11520 comments.push((newLinesMatches?.join('') ?? '') + '\n'); 11521 } 11522 return COMMENT_PLACEHOLDER; 11523 }); 11524 cssText = this._insertDirectives(cssText); 11525 const scopedCssText = this._scopeCssText(cssText, selector, hostSelector); 11526 // Add back comments at the original position. 11527 let commentIdx = 0; 11528 return scopedCssText.replace(_commentWithHashPlaceHolderRe, () => comments[commentIdx++]); 11529 } 11530 _insertDirectives(cssText) { 11531 cssText = this._insertPolyfillDirectivesInCssText(cssText); 11532 return this._insertPolyfillRulesInCssText(cssText); 11533 } 11534 /** 11535 * Process styles to add scope to keyframes. 11536 * 11537 * Modify both the names of the keyframes defined in the component styles and also the css 11538 * animation rules using them. 11539 * 11540 * Animation rules using keyframes defined elsewhere are not modified to allow for globally 11541 * defined keyframes. 11542 * 11543 * For example, we convert this css: 11544 * 11545 * ``` 11546 * .box { 11547 * animation: box-animation 1s forwards; 11548 * } 11549 * 11550 * @keyframes box-animation { 11551 * to { 11552 * background-color: green; 11553 * } 11554 * } 11555 * ``` 11556 * 11557 * to this: 11558 * 11559 * ``` 11560 * .box { 11561 * animation: scopeName_box-animation 1s forwards; 11562 * } 11563 * 11564 * @keyframes scopeName_box-animation { 11565 * to { 11566 * background-color: green; 11567 * } 11568 * } 11569 * ``` 11570 * 11571 * @param cssText the component's css text that needs to be scoped. 11572 * @param scopeSelector the component's scope selector. 11573 * 11574 * @returns the scoped css text. 11575 */ 11576 _scopeKeyframesRelatedCss(cssText, scopeSelector) { 11577 const unscopedKeyframesSet = new Set(); 11578 const scopedKeyframesCssText = processRules(cssText, rule => this._scopeLocalKeyframeDeclarations(rule, scopeSelector, unscopedKeyframesSet)); 11579 return processRules(scopedKeyframesCssText, rule => this._scopeAnimationRule(rule, scopeSelector, unscopedKeyframesSet)); 11580 } 11581 /** 11582 * Scopes local keyframes names, returning the updated css rule and it also 11583 * adds the original keyframe name to a provided set to collect all keyframes names 11584 * so that it can later be used to scope the animation rules. 11585 * 11586 * For example, it takes a rule such as: 11587 * 11588 * ``` 11589 * @keyframes box-animation { 11590 * to { 11591 * background-color: green; 11592 * } 11593 * } 11594 * ``` 11595 * 11596 * and returns: 11597 * 11598 * ``` 11599 * @keyframes scopeName_box-animation { 11600 * to { 11601 * background-color: green; 11602 * } 11603 * } 11604 * ``` 11605 * and as a side effect it adds "box-animation" to the `unscopedKeyframesSet` set 11606 * 11607 * @param cssRule the css rule to process. 11608 * @param scopeSelector the component's scope selector. 11609 * @param unscopedKeyframesSet the set of unscoped keyframes names (which can be 11610 * modified as a side effect) 11611 * 11612 * @returns the css rule modified with the scoped keyframes name. 11613 */ 11614 _scopeLocalKeyframeDeclarations(rule, scopeSelector, unscopedKeyframesSet) { 11615 return { 11616 ...rule, 11617 selector: rule.selector.replace(/(^@(?:-webkit-)?keyframes(?:\s+))(['"]?)(.+)\2(\s*)$/, (_, start, quote, keyframeName, endSpaces) => { 11618 unscopedKeyframesSet.add(unescapeQuotes(keyframeName, quote)); 11619 return `${start}${quote}${scopeSelector}_${keyframeName}${quote}${endSpaces}`; 11620 }) 11621 }; 11622 } 11623 /** 11624 * Function used to scope a keyframes name (obtained from an animation declaration) 11625 * using an existing set of unscopedKeyframes names to discern if the scoping needs to be 11626 * performed (keyframes names of keyframes not defined in the component's css need not to be 11627 * scoped). 11628 * 11629 * @param keyframe the keyframes name to check. 11630 * @param scopeSelector the component's scope selector. 11631 * @param unscopedKeyframesSet the set of unscoped keyframes names. 11632 * 11633 * @returns the scoped name of the keyframe, or the original name is the name need not to be 11634 * scoped. 11635 */ 11636 _scopeAnimationKeyframe(keyframe, scopeSelector, unscopedKeyframesSet) { 11637 return keyframe.replace(/^(\s*)(['"]?)(.+?)\2(\s*)$/, (_, spaces1, quote, name, spaces2) => { 11638 name = `${unscopedKeyframesSet.has(unescapeQuotes(name, quote)) ? scopeSelector + '_' : ''}${name}`; 11639 return `${spaces1}${quote}${name}${quote}${spaces2}`; 11640 }); 11641 } 11642 /** 11643 * Scope an animation rule so that the keyframes mentioned in such rule 11644 * are scoped if defined in the component's css and left untouched otherwise. 11645 * 11646 * It can scope values of both the 'animation' and 'animation-name' properties. 11647 * 11648 * @param rule css rule to scope. 11649 * @param scopeSelector the component's scope selector. 11650 * @param unscopedKeyframesSet the set of unscoped keyframes names. 11651 * 11652 * @returns the updated css rule. 11653 **/ 11654 _scopeAnimationRule(rule, scopeSelector, unscopedKeyframesSet) {
11655 let content = rule.content.replace(/((?:^|\s+|;)(?:-webkit-)?animation(?:\s*):(?:\s*))([^;]+)/g, (_, start, animationDeclarations) => start + animationDeclarations.replace(this._animationDeclarationKeyframesRe, (original, leadingSpaces, quote = '', quotedName, nonQuotedName) => { 11656 if (quotedName) { 11657 return `${leadingSpaces}${this._scopeAnimationKeyframe(`${quote}${quotedName}${quote}`, scopeSelector, unscopedKeyframesSet)}`; 11658 } else { 11659 return animationKeywords.has(nonQuotedName) ? original : `${leadingSpaces}${this._scopeAnimationKeyframe(nonQuotedName, scopeSelector, unscopedKeyframesSet)}`; 11660 } 11661 })); 11662 content = content.replace(/((?:^|\s+|;)(?:-webkit-)?animation-name(?:\s*):(?:\s*))([^;]+)/g, (_match, start, commaSeparatedKeyframes) => `${start}${commaSeparatedKeyframes.split(',').map(keyframe => this._scopeAnimationKeyframe(keyframe, scopeSelector, unscopedKeyframesSet)).join(',')}`); 11663 return { 11664 ...rule, 11665 content 11666 }; 11667 } 11668 /* 11669 * Process styles to convert native ShadowDOM rules that will trip 11670 * up the css parser; we rely on decorating the stylesheet with inert rules. 11671 * 11672 * For example, we convert this rule: 11673 * 11674 * polyfill-next-selector { content: ':host menu-item'; } 11675 * ::content menu-item { 11676 * 11677 * to this: 11678 * 11679 * scopeName menu-item { 11680 * 11681 **/ 11682 _insertPolyfillDirectivesInCssText(cssText) { 11683 return cssText.replace(_cssContentNextSelectorRe, function (...m) { 11684 return m[2] + '{'; 11685 }); 11686 } 11687 /* 11688 * Process styles to add rules which will only apply under the polyfill 11689 * 11690 * For example, we convert this rule: 11691 * 11692 * polyfill-rule { 11693 * content: ':host menu-item'; 11694 * ... 11695 * } 11696 * 11697 * to this: 11698 * 11699 * scopeName menu-item {...} 11700 * 11701 **/ 11702 _insertPolyfillRulesInCssText(cssText) { 11703 return cssText.replace(_cssContentRuleRe, (...m) => { 11704 const rule = m[0].replace(m[1], '').replace(m[2], ''); 11705 return m[4] + rule; 11706 }); 11707 } 11708 /* Ensure styles are scoped. Pseudo-scoping takes a rule like: 11709 * 11710 * .foo {... } 11711 * 11712 * and converts this to 11713 * 11714 * scopeName .foo { ... } 11715 */ 11716 _scopeCssText(cssText, scopeSelector, hostSelector) { 11717 const unscopedRules = this._extractUnscopedRulesFromCssText(cssText); 11718 // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively 11719 cssText = this._insertPolyfillHostInCssText(cssText); 11720 cssText = this._convertColonHost(cssText); 11721 cssText = this._convertColonHostContext(cssText); 11722 cssText = this._convertShadowDOMSelectors(cssText); 11723 if (scopeSelector) { 11724 cssText = this._scopeKeyframesRelatedCss(cssText, scopeSelector); 11725 cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector); 11726 } 11727 cssText = cssText + '\n' + unscopedRules; 11728 return cssText.trim(); 11729 } 11730 /* 11731 * Process styles to add rules which will only apply under the polyfill 11732 * and do not process via CSSOM. (CSSOM is destructive to rules on rare 11733 * occasions, e.g. -webkit-calc on Safari.) 11734 * For example, we convert this rule: 11735 * 11736 * @polyfill-unscoped-rule { 11737 * content: 'menu-item'; 11738 * ... } 11739 * 11740 * to this: 11741 * 11742 * menu-item {...} 11743 * 11744 **/ 11745 _extractUnscopedRulesFromCssText(cssText) { 11746 let r = ''; 11747 let m; 11748 _cssContentUnscopedRuleRe.lastIndex = 0; 11749 while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) { 11750 const rule = m[0].replace(m[2], '').replace(m[1], m[4]); 11751 r += rule + '\n\n'; 11752 } 11753 return r; 11754 } 11755 /* 11756 * convert a rule like :host(.foo) > .bar { } 11757 * 11758 * to 11759 * 11760 * .foo<scopeName> > .bar 11761 */ 11762 _convertColonHost(cssText) { 11763 return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => { 11764 if (hostSelectors) { 11765 const convertedSelectors = []; 11766 const hostSelectorArray = hostSelectors.split(',').map(p => p.trim()); 11767 for (const hostSelector of hostSelectorArray) { 11768 if (!hostSelector) break; 11769 const convertedSelector = _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors; 11770 convertedSelectors.push(convertedSelector); 11771 } 11772 return convertedSelectors.join(','); 11773 } else { 11774 return _polyfillHostNoCombinator + otherSelectors; 11775 } 11776 }); 11777 } 11778 /* 11779 * convert a rule like :host-context(.foo) > .bar { } 11780 * 11781 * to 11782 * 11783 * .foo<scopeName> > .bar, .foo <scopeName> > .bar { } 11784 * 11785 * and 11786 * 11787 * :host-context(.foo:host) .bar { ... } 11788 * 11789 * to 11790 * 11791 * .foo<scopeName> .bar { ... } 11792 */
11793 _convertColonHostContext(cssText) { 11794 return cssText.replace(_cssColonHostContextReGlobal, selectorText => { 11795 // We have captured a selector that contains a `:host-context` rule. 11796 // For backward compatibility `:host-context` may contain a comma separated list of selectors. 11797 // Each context selector group will contain a list of host-context selectors that must match 11798 // an ancestor of the host. 11799 // (Normally `contextSelectorGroups` will only contain a single array of context selectors.) 11800 const contextSelectorGroups = [[]]; 11801 // There may be more than `:host-context` in this selector so `selectorText` could look like: 11802 // `:host-context(.one):host-context(.two)`. 11803 // Execute `_cssColonHostContextRe` over and over until we have extracted all the 11804 // `:host-context` selectors from this selector. 11805 let match; 11806 while (match = _cssColonHostContextRe.exec(selectorText)) { 11807 // `match` = [':host-context(<selectors>)<rest>', <selectors>, <rest>] 11808 // The `<selectors>` could actually be a comma separated list: `:host-context(.one, .two)`. 11809 const newContextSelectors = (match[1] ?? '').trim().split(',').map(m => m.trim()).filter(m => m !== ''); 11810 // We must duplicate the current selector group for each of these new selectors. 11811 // For example if the current groups are: 11812 // ``` 11813 // [ 11814 // ['a', 'b', 'c'], 11815 // ['x', 'y', 'z'], 11816 // ] 11817 // ``` 11818 // And we have a new set of comma separated selectors: `:host-context(m,n)` then the new 11819 // groups are: 11820 // ``` 11821 // [ 11822 // ['a', 'b', 'c', 'm'], 11823 // ['x', 'y', 'z', 'm'], 11824 // ['a', 'b', 'c', 'n'], 11825 // ['x', 'y', 'z', 'n'], 11826 // ] 11827 // ``` 11828 const contextSelectorGroupsLength = contextSelectorGroups.length; 11829 repeatGroups(contextSelectorGroups, newContextSelectors.length); 11830 for (let i = 0; i < newContextSelectors.length; i++) { 11831 for (let j = 0; j < contextSelectorGroupsLength; j++) { 11832 contextSelectorGroups[j + i * contextSelectorGroupsLength].push(newContextSelectors[i]); 11833 } 11834 } 11835 // Update the `selectorText` and see repeat to see if there are more `:host-context`s. 11836 selectorText = match[2]; 11837 } 11838 // The context selectors now must be combined with each other to capture all the possible 11839 // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more 11840 // info about how this is done. 11841 return contextSelectorGroups.map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText)).join(', '); 11842 }); 11843 } 11844 /* 11845 * Convert combinators like ::shadow and pseudo-elements like ::content 11846 * by replacing with space. 11847 */ 11848 _convertShadowDOMSelectors(cssText) { 11849 return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText); 11850 } 11851 // change a selector like 'div' to 'name div' 11852 _scopeSelectors(cssText, scopeSelector, hostSelector) { 11853 return processRules(cssText, rule => { 11854 let selector = rule.selector; 11855 let content = rule.content; 11856 if (rule.selector[0] !== '@') { 11857 selector = this._scopeSelector(rule.selector, scopeSelector, hostSelector); 11858 } else if (scopedAtRuleIdentifiers.some(atRule => rule.selector.startsWith(atRule))) { 11859 content = this._scopeSelectors(rule.content, scopeSelector, hostSelector); 11860 } else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) { 11861 content = this._stripScopingSelectors(rule.content); 11862 } 11863 return new CssRule(selector, content); 11864 }); 11865 } 11866 /** 11867 * Handle a css text that is within a rule that should not contain scope selectors by simply 11868 * removing them! An example of such a rule is `@font-face`. 11869 * 11870 * `@font-face` rules cannot contain nested selectors. Nor can they be nested under a selector. 11871 * Normally this would be a syntax error by the author of the styles. But in some rare cases, such 11872 * as importing styles from a library, and applying `:host ::ng-deep` to the imported styles, we 11873 * can end up with broken css if the imported styles happen to contain @font-face rules. 11874 * 11875 * For example: 11876 * 11877 * ``` 11878 * :host ::ng-deep { 11879 * import 'some/lib/containing/font-face'; 11880 * } 11881 * 11882 * Similar logic applies to `@page` rules which can contain a particular set of properties, 11883 * as well as some specific at-rules. Since they can't be encapsulated, we have to strip 11884 * any scoping selectors from them. For more information: https://www.w3.org/TR/css-page-3 11885 * ``` 11886 */ 11887 _stripScopingSelectors(cssText) { 11888 return processRules(cssText, rule => { 11889 const selector = rule.selector.replace(_shadowDeepSelectors, ' ').replace(_polyfillHostNoCombinatorRe, ' '); 11890 return new CssRule(selector, rule.content); 11891 }); 11892 } 11893 _scopeSelector(selector, scopeSelector, hostSelector) { 11894 return selector.split(',').map(part => part.trim().split(_shadowDeepSelectors)).map(deepParts => { 11895 const [shallowPart, ...otherParts] = deepParts; 11896 const applyScope = shallowPart => { 11897 if (this._selectorNeedsScoping(shallowPart, scopeSelector)) { 11898 return this._applySelectorScope(shallowPart, scopeSelector, hostSelector); 11899 } else { 11900 return shallowPart; 11901 } 11902 }; 11903 return [applyScope(shallowPart), ...otherParts].join(' '); 11904 }).join(', '); 11905 } 11906 _selectorNeedsScoping(selector, scopeSelector) { 11907 const re = this._makeScopeMatcher(scopeSelector); 11908 return !re.test(selector); 11909 } 11910 _makeScopeMatcher(scopeSelector) { 11911 const lre = /\[/g; 11912 const rre = /\]/g; 11913 scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]'); 11914 return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm'); 11915 }
11916 // scope via name and [is=name] 11917 _applySimpleSelectorScope(selector, scopeSelector, hostSelector) { 11918 // In Android browser, the lastIndex is not reset when the regex is used in String.replace() 11919 _polyfillHostRe.lastIndex = 0; 11920 if (_polyfillHostRe.test(selector)) { 11921 const replaceBy = `[${hostSelector}]`; 11922 return selector.replace(_polyfillHostNoCombinatorRe, (hnc, selector) => { 11923 return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => { 11924 return before + replaceBy + colon + after; 11925 }); 11926 }).replace(_polyfillHostRe, replaceBy + ' '); 11927 } 11928 return scopeSelector + ' ' + selector; 11929 } 11930 // return a selector with [name] suffix on each simple selector 11931 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */ 11932 _applySelectorScope(selector, scopeSelector, hostSelector) { 11933 const isRe = /\[is=([^\]]*)\]/g; 11934 scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]); 11935 const attrName = '[' + scopeSelector + ']'; 11936 const _scopeSelectorPart = p => { 11937 let scopedP = p.trim(); 11938 if (!scopedP) { 11939 return ''; 11940 } 11941 if (p.indexOf(_polyfillHostNoCombinator) > -1) { 11942 scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector); 11943 } else { 11944 // remove :host since it should be unnecessary 11945 const t = p.replace(_polyfillHostRe, ''); 11946 if (t.length > 0) { 11947 const matches = t.match(/([^:]*)(:*)(.*)/); 11948 if (matches) { 11949 scopedP = matches[1] + attrName + matches[2] + matches[3]; 11950 } 11951 } 11952 } 11953 return scopedP; 11954 }; 11955 const safeContent = new SafeSelector(selector); 11956 selector = safeContent.content(); 11957 let scopedSelector = ''; 11958 let startIndex = 0; 11959 let res; 11960 const sep = /( |>|\+|~(?!=))\s*/g; 11961 // If a selector appears before :host it should not be shimmed as it 11962 // matches on ancestor elements and not on elements in the host's shadow 11963 // `:host-context(div)` is transformed to 11964 // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator` 11965 // the `div` is not part of the component in the 2nd selectors and should not be scoped. 11966 // Historically `component-tag:host` was matching the component so we also want to preserve 11967 // this behavior to avoid breaking legacy apps (it should not match). 11968 // The behavior should be: 11969 // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything) 11970 // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a 11971 // `:host-context(tag)`) 11972 const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1; 11973 // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present 11974 let shouldScope = !hasHost; 11975 while ((res = sep.exec(selector)) !== null) { 11976 const separator = res[1]; 11977 const part = selector.slice(startIndex, res.index).trim(); 11978 // A space following an escaped hex value and followed by another hex character 11979 // (ie: ".\fc ber" for ".über") is not a separator between 2 selectors 11980 // also keep in mind that backslashes are replaced by a placeholder by SafeSelector 11981 // These escaped selectors happen for example when esbuild runs with optimization.minify. 11982 if (part.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) { 11983 continue; 11984 } 11985 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1; 11986 const scopedPart = shouldScope ? _scopeSelectorPart(part) : part; 11987 scopedSelector += `${scopedPart} ${separator} `; 11988 startIndex = sep.lastIndex; 11989 } 11990 const part = selector.substring(startIndex); 11991 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1; 11992 scopedSelector += shouldScope ? _scopeSelectorPart(part) : part; 11993 // replace the placeholders with their original values 11994 return safeContent.restore(scopedSelector); 11995 } 11996 _insertPolyfillHostInCssText(selector) { 11997 return selector.replace(_colonHostContextRe, _polyfillHostContext).replace(_colonHostRe, _polyfillHost); 11998 } 11999} 12000class SafeSelector { 12001 constructor(selector) { 12002 this.placeholders = []; 12003 this.index = 0; 12004 // Replaces attribute selectors with placeholders. 12005 // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator. 12006 selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g); 12007 // CSS allows for certain special characters to be used in selectors if they're escaped. 12008 // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a 12009 // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped. 12010 // Replace all escape sequences (`\` followed by a character) with a placeholder so 12011 // that our handling of pseudo-selectors doesn't mess with them. 12012 // Escaped characters have a specific placeholder so they can be detected separately. 12013 selector = selector.replace(/(\\.)/g, (_, keep) => { 12014 const replaceBy = `__esc-ph-${this.index}__`; 12015 this.placeholders.push(keep); 12016 this.index++; 12017 return replaceBy; 12018 }); 12019 // Replaces the expression in `:nth-child(2n + 1)` with a placeholder. 12020 // WS and "+" would otherwise be interpreted as selector separators. 12021 this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => { 12022 const replaceBy = `__ph-${this.index}__`; 12023 this.placeholders.push(exp); 12024 this.index++; 12025 return pseudo + replaceBy; 12026 }); 12027 } 12028 restore(content) { 12029 return content.replace(/__(?:ph|esc-ph)-(\d+)__/g, (_ph, index) => this.placeholders[+index]); 12030 } 12031 content() { 12032 return this._content; 12033 } 12034 /** 12035 * Replaces all of the substrings that match a regex within a 12036 * special string (e.g. `__ph-0__`, `__ph-1__`, etc). 12037 */ 12038 _escapeRegexMatches(content, pattern) { 12039 return content.replace(pattern, (_, keep) => { 12040 const replaceBy = `__ph-${this.index}__`; 12041 this.placeholders.push(keep); 12042 this.index++; 12043 return replaceBy; 12044 }); 12045 } 12046} 12047const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim; 12048const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
12049const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim; 12050const _polyfillHost = '-shadowcsshost'; 12051// note: :host-context pre-processed to -shadowcsshostcontext. 12052const _polyfillHostContext = '-shadowcsscontext'; 12053const _parenSuffix = '(?:\\((' + '(?:\\([^)(]*\\)|[^)(]*)+?' + ')\\))?([^,{]*)'; 12054const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim'); 12055const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim'); 12056const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im'); 12057const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator'; 12058const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/; 12059const _shadowDOMSelectorsRe = [/::shadow/g, /::content/g, 12060// Deprecated selectors 12061/\/shadow-deep\//g, /\/shadow\//g]; 12062// The deep combinator is deprecated in the CSS spec 12063// Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future. 12064// see https://github.com/angular/angular/pull/17677 12065const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g; 12066const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$'; 12067const _polyfillHostRe = /-shadowcsshost/gim; 12068const _colonHostRe = /:host/gim; 12069const _colonHostContextRe = /:host-context/gim; 12070const _newLinesRe = /\r?\n/g; 12071const _commentRe = /\/\*[\s\S]*?\*\//g; 12072const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=/g; 12073const COMMENT_PLACEHOLDER = '%COMMENT%'; 12074const _commentWithHashPlaceHolderRe = new RegExp(COMMENT_PLACEHOLDER, 'g'); 12075const BLOCK_PLACEHOLDER = '%BLOCK%'; 12076const _ruleRe = new RegExp(`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`, 'g'); 12077const CONTENT_PAIRS = new Map([['{', '}']]); 12078const COMMA_IN_PLACEHOLDER = '%COMMA_IN_PLACEHOLDER%'; 12079const SEMI_IN_PLACEHOLDER = '%SEMI_IN_PLACEHOLDER%'; 12080const COLON_IN_PLACEHOLDER = '%COLON_IN_PLACEHOLDER%'; 12081const _cssCommaInPlaceholderReGlobal = new RegExp(COMMA_IN_PLACEHOLDER, 'g'); 12082const _cssSemiInPlaceholderReGlobal = new RegExp(SEMI_IN_PLACEHOLDER, 'g'); 12083const _cssColonInPlaceholderReGlobal = new RegExp(COLON_IN_PLACEHOLDER, 'g'); 12084class CssRule { 12085 constructor(selector, content) { 12086 this.selector = selector; 12087 this.content = content; 12088 } 12089} 12090function processRules(input, ruleCallback) { 12091 const escaped = escapeInStrings(input); 12092 const inputWithEscapedBlocks = escapeBlocks(escaped, CONTENT_PAIRS, BLOCK_PLACEHOLDER); 12093 let nextBlockIndex = 0; 12094 const escapedResult = inputWithEscapedBlocks.escapedString.replace(_ruleRe, (...m) => { 12095 const selector = m[2]; 12096 let content = ''; 12097 let suffix = m[4]; 12098 let contentPrefix = ''; 12099 if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) { 12100 content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; 12101 suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1); 12102 contentPrefix = '{'; 12103 } 12104 const rule = ruleCallback(new CssRule(selector, content)); 12105 return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`; 12106 }); 12107 return unescapeInStrings(escapedResult); 12108} 12109class StringWithEscapedBlocks { 12110 constructor(escapedString, blocks) { 12111 this.escapedString = escapedString; 12112 this.blocks = blocks; 12113 } 12114} 12115function escapeBlocks(input, charPairs, placeholder) { 12116 const resultParts = []; 12117 const escapedBlocks = []; 12118 let openCharCount = 0; 12119 let nonBlockStartIndex = 0; 12120 let blockStartIndex = -1; 12121 let openChar; 12122 let closeChar; 12123 for (let i = 0; i < input.length; i++) { 12124 const char = input[i]; 12125 if (char === '\\') { 12126 i++; 12127 } else if (char === closeChar) { 12128 openCharCount--; 12129 if (openCharCount === 0) { 12130 escapedBlocks.push(input.substring(blockStartIndex, i)); 12131 resultParts.push(placeholder); 12132 nonBlockStartIndex = i; 12133 blockStartIndex = -1; 12134 openChar = closeChar = undefined; 12135 } 12136 } else if (char === openChar) { 12137 openCharCount++; 12138 } else if (openCharCount === 0 && charPairs.has(char)) { 12139 openChar = char; 12140 closeChar = charPairs.get(char); 12141 openCharCount = 1; 12142 blockStartIndex = i + 1; 12143 resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex)); 12144 } 12145 } 12146 if (blockStartIndex !== -1) { 12147 escapedBlocks.push(input.substring(blockStartIndex)); 12148 resultParts.push(placeholder); 12149 } else { 12150 resultParts.push(input.substring(nonBlockStartIndex)); 12151 } 12152 return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks); 12153} 12154/** 12155 * Object containing as keys characters that should be substituted by placeholders 12156 * when found in strings during the css text parsing, and as values the respective 12157 * placeholders 12158 */ 12159const ESCAPE_IN_STRING_MAP = { 12160 ';': SEMI_IN_PLACEHOLDER, 12161 ',': COMMA_IN_PLACEHOLDER, 12162 ':': COLON_IN_PLACEHOLDER 12163}; 12164/** 12165 * Parse the provided css text and inside strings (meaning, inside pairs of unescaped single or 12166 * double quotes) replace specific characters with their respective placeholders as indicated 12167 * by the `ESCAPE_IN_STRING_MAP` map. 12168 * 12169 * For example convert the text 12170 * `animation: "my-anim:at\"ion" 1s;` 12171 * to 12172 * `animation: "my-anim%COLON_IN_PLACEHOLDER%at\"ion" 1s;` 12173 * 12174 * This is necessary in order to remove the meaning of some characters when found inside strings 12175 * (for example `;` indicates the end of a css declaration, `,` the sequence of values and `:` the 12176 * division between property and value during a declaration, none of these meanings apply when such
12177 * characters are within strings and so in order to prevent parsing issues they need to be replaced 12178 * with placeholder text for the duration of the css manipulation process). 12179 * 12180 * @param input the original css text. 12181 * 12182 * @returns the css text with specific characters in strings replaced by placeholders. 12183 **/ 12184function escapeInStrings(input) { 12185 let result = input; 12186 let currentQuoteChar = null; 12187 for (let i = 0; i < result.length; i++) { 12188 const char = result[i]; 12189 if (char === '\\') { 12190 i++; 12191 } else { 12192 if (currentQuoteChar !== null) { 12193 // index i is inside a quoted sub-string 12194 if (char === currentQuoteChar) { 12195 currentQuoteChar = null; 12196 } else { 12197 const placeholder = ESCAPE_IN_STRING_MAP[char]; 12198 if (placeholder) { 12199 result = `${result.substr(0, i)}${placeholder}${result.substr(i + 1)}`; 12200 i += placeholder.length - 1; 12201 } 12202 } 12203 } else if (char === '\'' || char === '"') { 12204 currentQuoteChar = char; 12205 } 12206 } 12207 } 12208 return result; 12209} 12210/** 12211 * Replace in a string all occurrences of keys in the `ESCAPE_IN_STRING_MAP` map with their 12212 * original representation, this is simply used to revert the changes applied by the 12213 * escapeInStrings function. 12214 * 12215 * For example it reverts the text: 12216 * `animation: "my-anim%COLON_IN_PLACEHOLDER%at\"ion" 1s;` 12217 * to it's original form of: 12218 * `animation: "my-anim:at\"ion" 1s;` 12219 * 12220 * Note: For the sake of simplicity this function does not check that the placeholders are 12221 * actually inside strings as it would anyway be extremely unlikely to find them outside of strings. 12222 * 12223 * @param input the css text containing the placeholders. 12224 * 12225 * @returns the css text without the placeholders. 12226 */ 12227function unescapeInStrings(input) { 12228 let result = input.replace(_cssCommaInPlaceholderReGlobal, ','); 12229 result = result.replace(_cssSemiInPlaceholderReGlobal, ';'); 12230 result = result.replace(_cssColonInPlaceholderReGlobal, ':'); 12231 return result; 12232} 12233/** 12234 * Unescape all quotes present in a string, but only if the string was actually already 12235 * quoted. 12236 * 12237 * This generates a "canonical" representation of strings which can be used to match strings 12238 * which would otherwise only differ because of differently escaped quotes. 12239 * 12240 * For example it converts the string (assumed to be quoted): 12241 * `this \\"is\\" a \\'\\\\'test` 12242 * to: 12243 * `this "is" a '\\\\'test` 12244 * (note that the latter backslashes are not removed as they are not actually escaping the single 12245 * quote) 12246 * 12247 * 12248 * @param input the string possibly containing escaped quotes. 12249 * @param isQuoted boolean indicating whether the string was quoted inside a bigger string (if not 12250 * then it means that it doesn't represent an inner string and thus no unescaping is required) 12251 * 12252 * @returns the string in the "canonical" representation without escaped quotes. 12253 */ 12254function unescapeQuotes(str, isQuoted) { 12255 return !isQuoted ? str : str.replace(/((?:^|[^\\])(?:\\\\)*)\\(?=['"])/g, '$1'); 12256} 12257/** 12258 * Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors` 12259 * to create a selector that matches the same as `:host-context()`. 12260 * 12261 * Given a single context selector `A` we need to output selectors that match on the host and as an 12262 * ancestor of the host: 12263 * 12264 * ``` 12265 * A <hostMarker>, A<hostMarker> {} 12266 * ``` 12267 * 12268 * When there is more than one context selector we also have to create combinations of those 12269 * selectors with each other. For example if there are `A` and `B` selectors the output is: 12270 * 12271 * ``` 12272 * AB<hostMarker>, AB <hostMarker>, A B<hostMarker>, 12273 * B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {} 12274 * ``` 12275 * 12276 * And so on... 12277 * 12278 * @param contextSelectors an array of context selectors that will be combined. 12279 * @param otherSelectors the rest of the selectors that are not context selectors. 12280 */ 12281function combineHostContextSelectors(contextSelectors, otherSelectors) { 12282 const hostMarker = _polyfillHostNoCombinator; 12283 _polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test 12284 const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors); 12285 // If there are no context selectors then just output a host marker 12286 if (contextSelectors.length === 0) { 12287 return hostMarker + otherSelectors; 12288 } 12289 const combined = [contextSelectors.pop() || '']; 12290 while (contextSelectors.length > 0) { 12291 const length = combined.length; 12292 const contextSelector = contextSelectors.pop(); 12293 for (let i = 0; i < length; i++) { 12294 const previousSelectors = combined[i]; 12295 // Add the new selector as a descendant of the previous selectors 12296 combined[length * 2 + i] = previousSelectors + ' ' + contextSelector; 12297 // Add the new selector as an ancestor of the previous selectors 12298 combined[length + i] = contextSelector + ' ' + previousSelectors; 12299 // Add the new selector to act on the same element as the previous selectors 12300 combined[i] = contextSelector + previousSelectors; 12301 } 12302 } 12303 // Finally connect the selector to the `hostMarker`s: either acting directly on the host 12304 // (A<hostMarker>) or as an ancestor (A <hostMarker>). 12305 return combined.map(s => otherSelectorsHasHost ? `${s}${otherSelectors}` : `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`).join(','); 12306} 12307/** 12308 * Mutate the given `groups` array so that there are `multiples` clones of the original array 12309 * stored. 12310 * 12311 * For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the 12312 * newly added groups will be clones of the original. 12313 *
12314 * @param groups An array of groups of strings that will be repeated. This array is mutated 12315 * in-place. 12316 * @param multiples The number of times the current groups should appear. 12317 */ 12318function repeatGroups(groups, multiples) { 12319 const length = groups.length; 12320 for (let i = 1; i < multiples; i++) { 12321 for (let j = 0; j < length; j++) { 12322 groups[j + i * length] = groups[j].slice(0); 12323 } 12324 } 12325} 12326 12327/** 12328 * Distinguishes different kinds of IR operations. 12329 * 12330 * Includes both creation and update operations. 12331 */ 12332var OpKind; 12333(function (OpKind) { 12334 /** 12335 * A special operation type which is used to represent the beginning and end nodes of a linked 12336 * list of operations. 12337 */ 12338 OpKind[OpKind["ListEnd"] = 0] = "ListEnd"; 12339 /** 12340 * An operation which wraps an output AST statement. 12341 */ 12342 OpKind[OpKind["Statement"] = 1] = "Statement"; 12343 /** 12344 * An operation which declares and initializes a `SemanticVariable`. 12345 */ 12346 OpKind[OpKind["Variable"] = 2] = "Variable"; 12347 /** 12348 * An operation to begin rendering of an element. 12349 */ 12350 OpKind[OpKind["ElementStart"] = 3] = "ElementStart"; 12351 /** 12352 * An operation to render an element with no children. 12353 */ 12354 OpKind[OpKind["Element"] = 4] = "Element"; 12355 /** 12356 * An operation which declares an embedded view. 12357 */ 12358 OpKind[OpKind["Template"] = 5] = "Template"; 12359 /** 12360 * An operation to end rendering of an element previously started with `ElementStart`. 12361 */ 12362 OpKind[OpKind["ElementEnd"] = 6] = "ElementEnd"; 12363 /** 12364 * An operation to begin an `ng-container`. 12365 */ 12366 OpKind[OpKind["ContainerStart"] = 7] = "ContainerStart"; 12367 /** 12368 * An operation for an `ng-container` with no children. 12369 */ 12370 OpKind[OpKind["Container"] = 8] = "Container"; 12371 /** 12372 * An operation to end an `ng-container`. 12373 */ 12374 OpKind[OpKind["ContainerEnd"] = 9] = "ContainerEnd"; 12375 /** 12376 * An operation disable binding for subsequent elements, which are descendants of a non-bindable 12377 * node. 12378 */ 12379 OpKind[OpKind["DisableBindings"] = 10] = "DisableBindings"; 12380 /** 12381 * An op to conditionally render a template. 12382 */ 12383 OpKind[OpKind["Conditional"] = 11] = "Conditional"; 12384 /** 12385 * An operation to re-enable binding, after it was previously disabled. 12386 */ 12387 OpKind[OpKind["EnableBindings"] = 12] = "EnableBindings"; 12388 /** 12389 * An operation to render a text node. 12390 */ 12391 OpKind[OpKind["Text"] = 13] = "Text"; 12392 /** 12393 * An operation declaring an event listener for an element. 12394 */ 12395 OpKind[OpKind["Listener"] = 14] = "Listener"; 12396 /** 12397 * An operation to interpolate text into a text node. 12398 */ 12399 OpKind[OpKind["InterpolateText"] = 15] = "InterpolateText"; 12400 /** 12401 * An intermediate binding op, that has not yet been processed into an individual property, 12402 * attribute, style, etc. 12403 */ 12404 OpKind[OpKind["Binding"] = 16] = "Binding"; 12405 /** 12406 * An operation to bind an expression to a property of an element. 12407 */ 12408 OpKind[OpKind["Property"] = 17] = "Property"; 12409 /** 12410 * An operation to bind an expression to a style property of an element. 12411 */ 12412 OpKind[OpKind["StyleProp"] = 18] = "StyleProp"; 12413 /** 12414 * An operation to bind an expression to a class property of an element. 12415 */ 12416 OpKind[OpKind["ClassProp"] = 19] = "ClassProp"; 12417 /** 12418 * An operation to bind an expression to the styles of an element. 12419 */ 12420 OpKind[OpKind["StyleMap"] = 20] = "StyleMap"; 12421 /** 12422 * An operation to bind an expression to the classes of an element. 12423 */ 12424 OpKind[OpKind["ClassMap"] = 21] = "ClassMap"; 12425 /** 12426 * An operation to advance the runtime's implicit slot context during the update phase of a view. 12427 */ 12428 OpKind[OpKind["Advance"] = 22] = "Advance"; 12429 /** 12430 * An operation to instantiate a pipe. 12431 */ 12432 OpKind[OpKind["Pipe"] = 23] = "Pipe"; 12433 /** 12434 * An operation to associate an attribute with an element. 12435 */ 12436 OpKind[OpKind["Attribute"] = 24] = "Attribute"; 12437 /** 12438 * An attribute that has been extracted for inclusion in the consts array. 12439 */ 12440 OpKind[OpKind["ExtractedAttribute"] = 25] = "ExtractedAttribute"; 12441 /** 12442 * An operation that configures a `@defer` block. 12443 */ 12444 OpKind[OpKind["Defer"] = 26] = "Defer"; 12445 /** 12446 * An operation that controls when a `@defer` loads. 12447 */ 12448 OpKind[OpKind["DeferOn"] = 27] = "DeferOn"; 12449 /** 12450 * An operation that controls when a `@defer` loads, using a custom expression as the condition. 12451 */ 12452 OpKind[OpKind["DeferWhen"] = 28] = "DeferWhen"; 12453 /** 12454 * An i18n message that has been extracted for inclusion in the consts array. 12455 */ 12456 OpKind[OpKind["I18nMessage"] = 29] = "I18nMessage"; 12457 /** 12458 * A host binding property. 12459 */ 12460 OpKind[OpKind["HostProperty"] = 30] = "HostProperty"; 12461 /** 12462 * A namespace change, which causes the subsequent elements to be processed as either HTML or SVG. 12463 */ 12464 OpKind[OpKind["Namespace"] = 31] = "Namespace"; 12465 /** 12466 * Configure a content projeciton definition for the view. 12467 */ 12468 OpKind[OpKind["ProjectionDef"] = 32] = "ProjectionDef"; 12469 /** 12470 * Create a content projection slot. 12471 */ 12472 OpKind[OpKind["Projection"] = 33] = "Projection"; 12473 /** 12474 * Create a repeater creation instruction op. 12475 */ 12476 OpKind[OpKind["RepeaterCreate"] = 34] = "RepeaterCreate"; 12477 /** 12478 * An update up for a repeater. 12479 */ 12480 OpKind[OpKind["Repeater"] = 35] = "Repeater"; 12481 /** 12482 * An operation to bind an expression to the property side of a two-way binding. 12483 */ 12484 OpKind[OpKind["TwoWayProperty"] = 36] = "TwoWayProperty"; 12485 /** 12486 * An operation declaring the event side of a two-way binding. 12487 */ 12488 OpKind[OpKind["TwoWayListener"] = 37] = "TwoWayListener"; 12489 /** 12490 * The start of an i18n block. 12491 */ 12492 OpKind[OpKind["I18nStart"] = 38] = "I18nStart"; 12493 /**
12494 * A self-closing i18n on a single element. 12495 */ 12496 OpKind[OpKind["I18n"] = 39] = "I18n"; 12497 /** 12498 * The end of an i18n block. 12499 */ 12500 OpKind[OpKind["I18nEnd"] = 40] = "I18nEnd"; 12501 /** 12502 * An expression in an i18n message. 12503 */ 12504 OpKind[OpKind["I18nExpression"] = 41] = "I18nExpression"; 12505 /** 12506 * An instruction that applies a set of i18n expressions. 12507 */ 12508 OpKind[OpKind["I18nApply"] = 42] = "I18nApply"; 12509 /** 12510 * An instruction to create an ICU expression. 12511 */ 12512 OpKind[OpKind["IcuStart"] = 43] = "IcuStart"; 12513 /** 12514 * An instruction to update an ICU expression. 12515 */ 12516 OpKind[OpKind["IcuEnd"] = 44] = "IcuEnd"; 12517 /** 12518 * An instruction representing a placeholder in an ICU expression. 12519 */ 12520 OpKind[OpKind["IcuPlaceholder"] = 45] = "IcuPlaceholder"; 12521 /** 12522 * An i18n context containing information needed to generate an i18n message. 12523 */ 12524 OpKind[OpKind["I18nContext"] = 46] = "I18nContext"; 12525 /** 12526 * A creation op that corresponds to i18n attributes on an element. 12527 */ 12528 OpKind[OpKind["I18nAttributes"] = 47] = "I18nAttributes"; 12529})(OpKind || (OpKind = {})); 12530/** 12531 * Distinguishes different kinds of IR expressions. 12532 */ 12533var ExpressionKind; 12534(function (ExpressionKind) { 12535 /** 12536 * Read of a variable in a lexical scope. 12537 */ 12538 ExpressionKind[ExpressionKind["LexicalRead"] = 0] = "LexicalRead"; 12539 /** 12540 * A reference to the current view context. 12541 */ 12542 ExpressionKind[ExpressionKind["Context"] = 1] = "Context"; 12543 /** 12544 * A reference to the view context, for use inside a track function. 12545 */ 12546 ExpressionKind[ExpressionKind["TrackContext"] = 2] = "TrackContext"; 12547 /** 12548 * Read of a variable declared in a `VariableOp`. 12549 */ 12550 ExpressionKind[ExpressionKind["ReadVariable"] = 3] = "ReadVariable"; 12551 /** 12552 * Runtime operation to navigate to the next view context in the view hierarchy. 12553 */ 12554 ExpressionKind[ExpressionKind["NextContext"] = 4] = "NextContext"; 12555 /** 12556 * Runtime operation to retrieve the value of a local reference. 12557 */ 12558 ExpressionKind[ExpressionKind["Reference"] = 5] = "Reference"; 12559 /** 12560 * Runtime operation to snapshot the current view context. 12561 */ 12562 ExpressionKind[ExpressionKind["GetCurrentView"] = 6] = "GetCurrentView"; 12563 /** 12564 * Runtime operation to restore a snapshotted view. 12565 */ 12566 ExpressionKind[ExpressionKind["RestoreView"] = 7] = "RestoreView"; 12567 /** 12568 * Runtime operation to reset the current view context after `RestoreView`. 12569 */ 12570 ExpressionKind[ExpressionKind["ResetView"] = 8] = "ResetView"; 12571 /** 12572 * Defines and calls a function with change-detected arguments. 12573 */
12574 ExpressionKind[ExpressionKind["PureFunctionExpr"] = 9] = "PureFunctionExpr"; 12575 /** 12576 * Indicates a positional parameter to a pure function definition. 12577 */ 12578 ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 10] = "PureFunctionParameterExpr"; 12579 /** 12580 * Binding to a pipe transformation. 12581 */ 12582 ExpressionKind[ExpressionKind["PipeBinding"] = 11] = "PipeBinding"; 12583 /** 12584 * Binding to a pipe transformation with a variable number of arguments. 12585 */ 12586 ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 12] = "PipeBindingVariadic"; 12587 /* 12588 * A safe property read requiring expansion into a null check. 12589 */ 12590 ExpressionKind[ExpressionKind["SafePropertyRead"] = 13] = "SafePropertyRead"; 12591 /** 12592 * A safe keyed read requiring expansion into a null check. 12593 */ 12594 ExpressionKind[ExpressionKind["SafeKeyedRead"] = 14] = "SafeKeyedRead"; 12595 /** 12596 * A safe function call requiring expansion into a null check. 12597 */ 12598 ExpressionKind[ExpressionKind["SafeInvokeFunction"] = 15] = "SafeInvokeFunction"; 12599 /** 12600 * An intermediate expression that will be expanded from a safe read into an explicit ternary. 12601 */ 12602 ExpressionKind[ExpressionKind["SafeTernaryExpr"] = 16] = "SafeTernaryExpr"; 12603 /** 12604 * An empty expression that will be stipped before generating the final output. 12605 */ 12606 ExpressionKind[ExpressionKind["EmptyExpr"] = 17] = "EmptyExpr"; 12607 /* 12608 * An assignment to a temporary variable. 12609 */ 12610 ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 18] = "AssignTemporaryExpr"; 12611 /** 12612 * A reference to a temporary variable. 12613 */ 12614 ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr"; 12615 /** 12616 * An expression that will cause a literal slot index to be emitted. 12617 */ 12618 ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 20] = "SlotLiteralExpr"; 12619 /** 12620 * A test expression for a conditional op. 12621 */ 12622 ExpressionKind[ExpressionKind["ConditionalCase"] = 21] = "ConditionalCase"; 12623 /** 12624 * An expression that will be automatically extracted to the component const array. 12625 */ 12626 ExpressionKind[ExpressionKind["ConstCollected"] = 22] = "ConstCollected"; 12627 /** 12628 * Operation that sets the value of a two-way binding. 12629 */ 12630 ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 23] = "TwoWayBindingSet"; 12631})(ExpressionKind || (ExpressionKind = {})); 12632var VariableFlags; 12633(function (VariableFlags) { 12634 VariableFlags[VariableFlags["None"] = 0] = "None"; 12635 /** 12636 * Always inline this variable, regardless of the number of times it's used. 12637 * An `AlwaysInline` variable may not depend on context, because doing so may cause side effects 12638 * that are illegal when multi-inlined. (The optimizer will enforce this constraint.) 12639 */ 12640 VariableFlags[VariableFlags["AlwaysInline"] = 1] = "AlwaysInline"; 12641})(VariableFlags || (VariableFlags = {})); 12642/** 12643 * Distinguishes between different kinds of `SemanticVariable`s. 12644 */ 12645var SemanticVariableKind; 12646(function (SemanticVariableKind) { 12647 /** 12648 * Represents the context of a particular view. 12649 */ 12650 SemanticVariableKind[SemanticVariableKind["Context"] = 0] = "Context"; 12651 /** 12652 * Represents an identifier declared in the lexical scope of a view. 12653 */ 12654 SemanticVariableKind[SemanticVariableKind["Identifier"] = 1] = "Identifier"; 12655 /** 12656 * Represents a saved state that can be used to restore a view in a listener handler function. 12657 */ 12658 SemanticVariableKind[SemanticVariableKind["SavedView"] = 2] = "SavedView"; 12659 /** 12660 * An alias generated by a special embedded view type (e.g. a `@for` block). 12661 */ 12662 SemanticVariableKind[SemanticVariableKind["Alias"] = 3] = "Alias"; 12663})(SemanticVariableKind || (SemanticVariableKind = {})); 12664/** 12665 * Whether to compile in compatibilty mode. In compatibility mode, the template pipeline will 12666 * attempt to match the output of `TemplateDefinitionBuilder` as exactly as possible, at the cost 12667 * of producing quirky or larger code in some cases. 12668 */ 12669var CompatibilityMode; 12670(function (CompatibilityMode) { 12671 CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal"; 12672 CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder"; 12673})(CompatibilityMode || (CompatibilityMode = {})); 12674/** 12675 * Enumeration of the types of attributes which can be applied to an element. 12676 */ 12677var BindingKind; 12678(function (BindingKind) { 12679 /** 12680 * Static attributes. 12681 */ 12682 BindingKind[BindingKind["Attribute"] = 0] = "Attribute"; 12683 /** 12684 * Class bindings. 12685 */ 12686 BindingKind[BindingKind["ClassName"] = 1] = "ClassName"; 12687 /** 12688 * Style bindings. 12689 */ 12690 BindingKind[BindingKind["StyleProperty"] = 2] = "StyleProperty"; 12691 /** 12692 * Dynamic property bindings. 12693 */ 12694 BindingKind[BindingKind["Property"] = 3] = "Property"; 12695 /** 12696 * Property or attribute bindings on a template. 12697 */ 12698 BindingKind[BindingKind["Template"] = 4] = "Template"; 12699 /** 12700 * Internationalized attributes. 12701 */ 12702 BindingKind[BindingKind["I18n"] = 5] = "I18n"; 12703 /** 12704 * Animation property bindings. 12705 */ 12706 BindingKind[BindingKind["Animation"] = 6] = "Animation"; 12707 /** 12708 * Property side of a two-way binding. 12709 */ 12710 BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty"; 12711})(BindingKind || (BindingKind = {})); 12712/** 12713 * Enumeration of possible times i18n params can be resolved. 12714 */ 12715var I18nParamResolutionTime; 12716(function (I18nParamResolutionTime) { 12717 /** 12718 * Param is resolved at message creation time. Most params should be resolved at message creation 12719 * time. However, ICU params need to be handled in post-processing. 12720 */ 12721 I18nParamResolutionTime[I18nParamResolutionTime["Creation"] = 0] = "Creation"; 12722 /** 12723 * Param is resolved during post-processing. This should be used for params whose value comes from 12724 * an ICU. 12725 */ 12726 I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing"; 12727})(I18nParamResolutionTime || (I18nParamResolutionTime = {})); 12728/** 12729 * The contexts in which an i18n expression can be used. 12730 */ 12731var I18nExpressionFor; 12732(function (I18nExpressionFor) { 12733 /** 12734 * This expression is used as a value (i.e. inside an i18n block). 12735 */ 12736 I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText"; 12737 /** 12738 * This expression is used in a binding. 12739 */ 12740 I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute"; 12741})(I18nExpressionFor || (I18nExpressionFor = {})); 12742/** 12743 * Flags that describe what an i18n param value. These determine how the value is serialized into 12744 * the final map. 12745 */ 12746var I18nParamValueFlags; 12747(function (I18nParamValueFlags) { 12748 I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None"; 12749 /** 12750 * This value represents an element tag. 12751 */ 12752 I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag"; 12753 /** 12754 * This value represents a template tag. 12755 */ 12756 I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag"; 12757 /** 12758 * This value represents the opening of a tag. 12759 */ 12760 I18nParamValueFlags[I18nParamValueFlags["OpenTag"] = 4] = "OpenTag"; 12761 /** 12762 * This value represents the closing of a tag. 12763 */ 12764 I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag"; 12765 /** 12766 * This value represents an i18n expression index. 12767 */ 12768 I18nParamValueFlags[I18nParamValueFlags["ExpressionIndex"] = 16] = "ExpressionIndex"; 12769})(I18nParamValueFlags || (I18nParamValueFlags = {})); 12770/** 12771 * Whether the active namespace is HTML, MathML, or SVG mode. 12772 */ 12773var Namespace; 12774(function (Namespace) { 12775 Namespace[Namespace["HTML"] = 0] = "HTML"; 12776 Namespace[Namespace["SVG"] = 1] = "SVG"; 12777 Namespace[Namespace["Math"] = 2] = "Math"; 12778})(Namespace || (Namespace = {})); 12779/** 12780 * The type of a `@defer` trigger, for use in the ir. 12781 */ 12782var DeferTriggerKind; 12783(function (DeferTriggerKind) { 12784 DeferTriggerKind[DeferTriggerKind["Idle"] = 0] = "Idle"; 12785 DeferTriggerKind[DeferTriggerKind["Immediate"] = 1] = "Immediate"; 12786 DeferTriggerKind[DeferTriggerKind["Timer"] = 2] = "Timer"; 12787 DeferTriggerKind[DeferTriggerKind["Hover"] = 3] = "Hover"; 12788 DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction"; 12789 DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport"; 12790})(DeferTriggerKind || (DeferTriggerKind = {})); 12791/** 12792 * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs. 12793 */ 12794var I18nContextKind; 12795(function (I18nContextKind) { 12796 I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n"; 12797 I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu"; 12798 I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr"; 12799})(I18nContextKind || (I18nContextKind = {})); 12800var TemplateKind; 12801(function (TemplateKind) { 12802 TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate"; 12803 TemplateKind[TemplateKind["Structural"] = 1] = "Structural"; 12804 TemplateKind[TemplateKind["Block"] = 2] = "Block"; 12805})(TemplateKind || (TemplateKind = {})); 12806 12807/** 12808 * Marker symbol for `ConsumesSlotOpTrait`. 12809 */ 12810const ConsumesSlot = Symbol('ConsumesSlot'); 12811/** 12812 * Marker symbol for `DependsOnSlotContextOpTrait`. 12813 */ 12814const DependsOnSlotContext = Symbol('DependsOnSlotContext'); 12815/** 12816 * Marker symbol for `ConsumesVars` trait. 12817 */ 12818const ConsumesVarsTrait = Symbol('ConsumesVars'); 12819/** 12820 * Marker symbol for `UsesVarOffset` trait. 12821 */ 12822const UsesVarOffset = Symbol('UsesVarOffset'); 12823/** 12824 * Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize 12825 * implementors of the trait). 12826 */ 12827const TRAIT_CONSUMES_SLOT = { 12828 [ConsumesSlot]: true, 12829 numSlotsUsed: 1 12830}; 12831/** 12832 * Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to 12833 * initialize implementors of the trait). 12834 */ 12835const TRAIT_DEPENDS_ON_SLOT_CONTEXT = { 12836 [DependsOnSlotContext]: true 12837}; 12838/** 12839 * Default values for `UsesVars` fields (used with the spread operator to initialize 12840 * implementors of the trait). 12841 */ 12842const TRAIT_CONSUMES_VARS = { 12843 [ConsumesVarsTrait]: true 12844}; 12845/** 12846 * Test whether an operation implements `ConsumesSlotOpTrait`. 12847 */ 12848function hasConsumesSlotTrait(op) { 12849 return op[ConsumesSlot] === true; 12850} 12851/** 12852 * Test whether an operation implements `DependsOnSlotContextOpTrait`. 12853 */ 12854function hasDependsOnSlotContextTrait(op) { 12855 return op[DependsOnSlotContext] === true; 12856} 12857function hasConsumesVarsTrait(value) { 12858 return value[ConsumesVarsTrait] === true; 12859} 12860/** 12861 * Test whether an expression implements `UsesVarOffsetTrait`. 12862 */ 12863function hasUsesVarOffsetTrait(expr) { 12864 return expr[UsesVarOffset] === true; 12865} 12866 12867/** 12868 * Create a `StatementOp`. 12869 */ 12870function createStatementOp(statement) { 12871 return { 12872 kind: OpKind.Statement, 12873 statement, 12874 ...NEW_OP 12875 }; 12876} 12877/** 12878 * Create a `VariableOp`. 12879 */ 12880function createVariableOp(xref, variable, initializer, flags) { 12881 return { 12882 kind: OpKind.Variable, 12883 xref, 12884 variable, 12885 initializer, 12886 flags, 12887 ...NEW_OP 12888 }; 12889} 12890/** 12891 * Static structure shared by all operations. 12892 * 12893 * Used as a convenience via the spread operator (`...NEW_OP`) when creating new operations, and 12894 * ensures the fields are always in the same order. 12895 */ 12896const NEW_OP = { 12897 debugListId: null, 12898 prev: null, 12899 next: null 12900}; 12901 12902/** 12903 * Create an `InterpolationTextOp`. 12904 */ 12905function createInterpolateTextOp(xref, interpolation, sourceSpan) { 12906 return { 12907 kind: OpKind.InterpolateText, 12908 target: xref, 12909 interpolation, 12910 sourceSpan, 12911 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 12912 ...TRAIT_CONSUMES_VARS, 12913 ...NEW_OP 12914 }; 12915} 12916class Interpolation { 12917 constructor(strings, expressions, i18nPlaceholders) { 12918 this.strings = strings; 12919 this.expressions = expressions; 12920 this.i18nPlaceholders = i18nPlaceholders; 12921 if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) { 12922 throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`); 12923 } 12924 } 12925} 12926/** 12927 * Create a `BindingOp`, not yet transformed into a particular type of binding. 12928 */ 12929function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStru
12929cturalTemplateAttribute, templateKind, i18nMessage, sourceSpan) { 12930 return { 12931 kind: OpKind.Binding, 12932 bindingKind: kind, 12933 target, 12934 name, 12935 expression, 12936 unit, 12937 securityContext, 12938 isTextAttribute, 12939 isStructuralTemplateAttribute, 12940 templateKind, 12941 i18nContext: null, 12942 i18nMessage, 12943 sourceSpan, 12944 ...NEW_OP 12945 }; 12946} 12947/** 12948 * Create a `PropertyOp`. 12949 */ 12950function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) { 12951 return { 12952 kind: OpKind.Property, 12953 target, 12954 name, 12955 expression, 12956 isAnimationTrigger, 12957 securityContext, 12958 sanitizer: null, 12959 isStructuralTemplateAttribute, 12960 templateKind, 12961 i18nContext, 12962 i18nMessage, 12963 sourceSpan, 12964 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 12965 ...TRAIT_CONSUMES_VARS, 12966 ...NEW_OP 12967 }; 12968} 12969/** 12970 * Create a `TwoWayPropertyOp`. 12971 */ 12972function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) { 12973 return { 12974 kind: OpKind.TwoWayProperty, 12975 target, 12976 name, 12977 expression, 12978 securityContext, 12979 sanitizer: null, 12980 isStructuralTemplateAttribute, 12981 templateKind, 12982 i18nContext, 12983 i18nMessage, 12984 sourceSpan, 12985 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 12986 ...TRAIT_CONSUMES_VARS, 12987 ...NEW_OP 12988 }; 12989} 12990/** Create a `StylePropOp`. */ 12991function createStylePropOp(xref, name, expression, unit, sourceSpan) { 12992 return { 12993 kind: OpKind.StyleProp, 12994 target: xref, 12995 name, 12996 expression, 12997 unit, 12998 sourceSpan, 12999 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13000 ...TRAIT_CONSUMES_VARS, 13001 ...NEW_OP 13002 }; 13003} 13004/** 13005 * Create a `ClassPropOp`. 13006 */ 13007function createClassPropOp(xref, name, expression, sourceSpan) { 13008 return { 13009 kind: OpKind.ClassProp, 13010 target: xref, 13011 name, 13012 expression, 13013 sourceSpan, 13014 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13015 ...TRAIT_CONSUMES_VARS, 13016 ...NEW_OP 13017 }; 13018} 13019/** Create a `StyleMapOp`. */ 13020function createStyleMapOp(xref, expression, sourceSpan) { 13021 return { 13022 kind: OpKind.StyleMap, 13023 target: xref, 13024 expression, 13025 sourceSpan, 13026 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13027 ...TRAIT_CONSUMES_VARS, 13028 ...NEW_OP 13029 }; 13030} 13031/** 13032 * Create a `ClassMapOp`. 13033 */ 13034function createClassMapOp(xref, expression, sourceSpan) { 13035 return { 13036 kind: OpKind.ClassMap, 13037 target: xref, 13038 expression, 13039 sourceSpan, 13040 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13041 ...TRAIT_CONSUMES_VARS, 13042 ...NEW_OP 13043 }; 13044} 13045/** 13046 * Create an `AttributeOp`. 13047 */ 13048function createAttributeOp(target, namespace, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) { 13049 return { 13050 kind: OpKind.Attribute, 13051 target, 13052 namespace, 13053 name, 13054 expression, 13055 securityContext, 13056 sanitizer: null, 13057 isTextAttribute, 13058 isStructuralTemplateAttribute, 13059 templateKind, 13060 i18nContext: null, 13061 i18nMessage, 13062 sourceSpan, 13063 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13064 ...TRAIT_CONSUMES_VARS, 13065 ...NEW_OP 13066 }; 13067} 13068/** 13069 * Create an `AdvanceOp`. 13070 */ 13071function createAdvanceOp(delta, sourceSpan) { 13072 return { 13073 kind: OpKind.Advance, 13074 delta, 13075 sourceSpan, 13076 ...NEW_OP 13077 }; 13078} 13079/** 13080 * Create a conditional op, which will display an embedded view according to a condtion. 13081 */ 13082function createConditionalOp(target, targetSlot, test, conditions, sourceSpan) { 13083 return { 13084 kind: OpKind.Conditional, 13085 target, 13086 targetSlot, 13087 test, 13088 conditions, 13089 processed: null, 13090 sourceSpan, 13091 contextValue: null, 13092 ...NEW_OP, 13093 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13094 ...TRAIT_CONSUMES_VARS 13095 }; 13096} 13097function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) { 13098 return { 13099 kind: OpKind.Repeater, 13100 target: repeaterCreate, 13101 targetSlot, 13102 collection, 13103 sourceSpan, 13104 ...NEW_OP, 13105 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT 13106 }; 13107} 13108function createDeferWhenOp(target, expr, prefetch, sourceSpan) { 13109 return { 13110 kind: OpKind.DeferWhen, 13111 target, 13112 expr, 13113 prefetch, 13114 sourceSpan, 13115 ...NEW_OP, 13116 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, 13117 ...TRAIT_CONSUMES_VARS 13118 }; 13119} 13120/**
13121 * Create an i18n expression op. 13122 */ 13123function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) { 13124 return { 13125 kind: OpKind.I18nExpression, 13126 context, 13127 target, 13128 i18nOwner, 13129 handle, 13130 expression, 13131 icuPlaceholder, 13132 i18nPlaceholder, 13133 resolutionTime, 13134 usage, 13135 name, 13136 sourceSpan, 13137 ...NEW_OP, 13138 ...TRAIT_CONSUMES_VARS, 13139 ...TRAIT_DEPENDS_ON_SLOT_CONTEXT 13140 }; 13141} 13142/** 13143 * Creates an op to apply i18n expression ops. 13144 */ 13145function createI18nApplyOp(owner, handle, sourceSpan) { 13146 return { 13147 kind: OpKind.I18nApply, 13148 owner, 13149 handle, 13150 sourceSpan, 13151 ...NEW_OP 13152 }; 13153} 13154var _a, _b, _c, _d, _e, _f; 13155/** 13156 * Check whether a given `o.Expression` is a logical IR expression type. 13157 */ 13158function isIrExpression(expr) { 13159 return expr instanceof ExpressionBase; 13160} 13161/** 13162 * Base type used for all logical IR expressions. 13163 */ 13164class ExpressionBase extends Expression { 13165 constructor(sourceSpan = null) { 13166 super(null, sourceSpan); 13167 } 13168} 13169/** 13170 * Logical expression representing a lexical read of a variable name. 13171 */ 13172class LexicalReadExpr extends ExpressionBase { 13173 constructor(name) { 13174 super(); 13175 this.name = name; 13176 this.kind = ExpressionKind.LexicalRead; 13177 } 13178 visitExpression(visitor, context) {} 13179 isEquivalent(other) { 13180 // We assume that the lexical reads are in the same context, which must be true for parent 13181 // expressions to be equivalent. 13182 // TODO: is this generally safe? 13183 return this.name === other.name; 13184 } 13185 isConstant() { 13186 return false; 13187 } 13188 transformInternalExpressions() {} 13189 clone() { 13190 return new LexicalReadExpr(this.name); 13191 } 13192} 13193/** 13194 * Runtime operation to retrieve the value of a local reference. 13195 */ 13196class ReferenceExpr extends ExpressionBase { 13197 constructor(target, targetSlot, offset) { 13198 super(); 13199 this.target = target; 13200 this.targetSlot = targetSlot; 13201 this.offset = offset; 13202 this.kind = ExpressionKind.Reference; 13203 } 13204 visitExpression() {} 13205 isEquivalent(e) { 13206 return e instanceof ReferenceExpr && e.target === this.target; 13207 } 13208 isConstant() { 13209 return false; 13210 } 13211 transformInternalExpressions() {} 13212 clone() { 13213 return new ReferenceExpr(this.target, this.targetSlot, this.offset); 13214 } 13215} 13216/** 13217 * A reference to the current view context (usually the `ctx` variable in a template function). 13218 */ 13219class ContextExpr extends ExpressionBase { 13220 constructor(view) { 13221 super(); 13222 this.view = view; 13223 this.kind = ExpressionKind.Context; 13224 } 13225 visitExpression() {} 13226 isEquivalent(e) { 13227 return e instanceof ContextExpr && e.view === this.view; 13228 } 13229 isConstant() { 13230 return false; 13231 } 13232 transformInternalExpressions() {} 13233 clone() { 13234 return new ContextExpr(this.view); 13235 } 13236} 13237/** 13238 * A reference to the current view context inside a track function. 13239 */ 13240class TrackContextExpr extends ExpressionBase { 13241 constructor(view) { 13242 super(); 13243 this.view = view; 13244 this.kind = ExpressionKind.TrackContext; 13245 } 13246 visitExpression() {} 13247 isEquivalent(e) { 13248 return e instanceof TrackContextExpr && e.view === this.view; 13249 } 13250 isConstant() { 13251 return false; 13252 } 13253 transformInternalExpressions() {} 13254 clone() { 13255 return new TrackContextExpr(this.view); 13256 } 13257} 13258/** 13259 * Runtime operation to navigate to the next view context in the view hierarchy. 13260 */ 13261class NextContextExpr extends ExpressionBase { 13262 constructor() { 13263 super(); 13264 this.kind = ExpressionKind.NextContext; 13265 this.steps = 1; 13266 } 13267 visitExpression() {} 13268 isEquivalent(e) { 13269 return e instanceof NextContextExpr && e.steps === this.steps; 13270 } 13271 isConstant() { 13272 return false; 13273 } 13274 transformInternalExpressions() {} 13275 clone() { 13276 const expr = new NextContextExpr(); 13277 expr.steps = this.steps; 13278 return expr; 13279 } 13280} 13281/** 13282 * Runtime operation to snapshot the current view context. 13283 * 13284 * The result of this operation can be stored in a variable and later used with the `RestoreView` 13285 * operation. 13286 */ 13287class GetCurrentViewExpr extends ExpressionBase { 13288 constructor() { 13289 super(); 13290 this.kind = ExpressionKind.GetCurrentView; 13291 } 13292 visitExpression() {} 13293 isEquivalent(e) { 13294 return e instanceof GetCurrentViewExpr; 13295 } 13296 isConstant() { 13297 return false;
13298 } 13299 transformInternalExpressions() {} 13300 clone() { 13301 return new GetCurrentViewExpr(); 13302 } 13303} 13304/** 13305 * Runtime operation to restore a snapshotted view. 13306 */ 13307class RestoreViewExpr extends ExpressionBase { 13308 constructor(view) { 13309 super(); 13310 this.view = view; 13311 this.kind = ExpressionKind.RestoreView; 13312 } 13313 visitExpression(visitor, context) { 13314 if (typeof this.view !== 'number') { 13315 this.view.visitExpression(visitor, context); 13316 } 13317 } 13318 isEquivalent(e) { 13319 if (!(e instanceof RestoreViewExpr) || typeof e.view !== typeof this.view) { 13320 return false; 13321 } 13322 if (typeof this.view === 'number') { 13323 return this.view === e.view; 13324 } else { 13325 return this.view.isEquivalent(e.view); 13326 } 13327 } 13328 isConstant() { 13329 return false; 13330 } 13331 transformInternalExpressions(transform, flags) { 13332 if (typeof this.view !== 'number') { 13333 this.view = transformExpressionsInExpression(this.view, transform, flags); 13334 } 13335 } 13336 clone() { 13337 return new RestoreViewExpr(this.view instanceof Expression ? this.view.clone() : this.view); 13338 } 13339} 13340/** 13341 * Runtime operation to reset the current view context after `RestoreView`. 13342 */ 13343class ResetViewExpr extends ExpressionBase { 13344 constructor(expr) { 13345 super(); 13346 this.expr = expr; 13347 this.kind = ExpressionKind.ResetView; 13348 } 13349 visitExpression(visitor, context) { 13350 this.expr.visitExpression(visitor, context); 13351 } 13352 isEquivalent(e) { 13353 return e instanceof ResetViewExpr && this.expr.isEquivalent(e.expr); 13354 } 13355 isConstant() { 13356 return false; 13357 } 13358 transformInternalExpressions(transform, flags) { 13359 this.expr = transformExpressionsInExpression(this.expr, transform, flags); 13360 } 13361 clone() { 13362 return new ResetViewExpr(this.expr.clone()); 13363 } 13364} 13365class TwoWayBindingSetExpr extends ExpressionBase { 13366 constructor(target, value) { 13367 super(); 13368 this.target = target; 13369 this.value = value; 13370 this.kind = ExpressionKind.TwoWayBindingSet; 13371 } 13372 visitExpression(visitor, context) { 13373 this.target.visitExpression(visitor, context); 13374 this.value.visitExpression(visitor, context); 13375 } 13376 isEquivalent(other) { 13377 return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value); 13378 } 13379 isConstant() { 13380 return false; 13381 } 13382 transformInternalExpressions(transform, flags) { 13383 this.target = transformExpressionsInExpression(this.target, transform, flags); 13384 this.value = transformExpressionsInExpression(this.value, transform, flags); 13385 } 13386 clone() { 13387 return new TwoWayBindingSetExpr(this.target, this.value); 13388 } 13389} 13390/** 13391 * Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`. 13392 */ 13393class ReadVariableExpr extends ExpressionBase { 13394 constructor(xref) { 13395 super(); 13396 this.xref = xref; 13397 this.kind = ExpressionKind.ReadVariable; 13398 this.name = null; 13399 } 13400 visitExpression() {} 13401 isEquivalent(other) { 13402 return other instanceof ReadVariableExpr && other.xref === this.xref; 13403 } 13404 isConstant() { 13405 return false; 13406 } 13407 transformInternalExpressions() {} 13408 clone() { 13409 const expr = new ReadVariableExpr(this.xref); 13410 expr.name = this.name; 13411 return expr; 13412 } 13413} 13414class PureFunctionExpr extends ExpressionBase { 13415 static #_ = (_a = ConsumesVarsTrait, _b = UsesVarOffset); 13416 constructor(expression, args) { 13417 super(); 13418 this.kind = ExpressionKind.PureFunctionExpr; 13419 this[_a] = true; 13420 this[_b] = true; 13421 this.varOffset = null; 13422 /** 13423 * Once extracted to the `ConstantPool`, a reference to the function which defines the computation 13424 * of `body`. 13425 */ 13426 this.fn = null; 13427 this.body = expression; 13428 this.args = args; 13429 } 13430 visitExpression(visitor, context) { 13431 this.body?.visitExpression(visitor, context); 13432 for (const arg of this.args) { 13433 arg.visitExpression(visitor, context); 13434 } 13435 } 13436 isEquivalent(other) { 13437 if (!(other instanceof PureFunctionExpr) || other.args.length !== this.args.length) { 13438 return false; 13439 } 13440 return other.body !== null && this.body !== null && other.body.isEquivalent(this.body) && other.args.every((arg, idx) => arg.isEquivalent(this.args[idx])); 13441 } 13442 isConstant() { 13443 return false;
13444 } 13445 transformInternalExpressions(transform, flags) { 13446 if (this.body !== null) { 13447 // TODO: figure out if this is the right flag to pass here. 13448 this.body = transformExpressionsInExpression(this.body, transform, flags | VisitorContextFlag.InChildOperation); 13449 } else if (this.fn !== null) { 13450 this.fn = transformExpressionsInExpression(this.fn, transform, flags); 13451 } 13452 for (let i = 0; i < this.args.length; i++) { 13453 this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags); 13454 } 13455 } 13456 clone() { 13457 const expr = new PureFunctionExpr(this.body?.clone() ?? null, this.args.map(arg => arg.clone())); 13458 expr.fn = this.fn?.clone() ?? null; 13459 expr.varOffset = this.varOffset; 13460 return expr; 13461 } 13462} 13463class PureFunctionParameterExpr extends ExpressionBase { 13464 constructor(index) { 13465 super(); 13466 this.index = index; 13467 this.kind = ExpressionKind.PureFunctionParameterExpr; 13468 } 13469 visitExpression() {} 13470 isEquivalent(other) { 13471 return other instanceof PureFunctionParameterExpr && other.index === this.index; 13472 } 13473 isConstant() { 13474 return true; 13475 } 13476 transformInternalExpressions() {} 13477 clone() { 13478 return new PureFunctionParameterExpr(this.index); 13479 } 13480} 13481class PipeBindingExpr extends ExpressionBase { 13482 static #_ = (_c = ConsumesVarsTrait, _d = UsesVarOffset); 13483 constructor(target, targetSlot, name, args) { 13484 super(); 13485 this.target = target; 13486 this.targetSlot = targetSlot; 13487 this.name = name; 13488 this.args = args; 13489 this.kind = ExpressionKind.PipeBinding; 13490 this[_c] = true; 13491 this[_d] = true; 13492 this.varOffset = null; 13493 } 13494 visitExpression(visitor, context) { 13495 for (const arg of this.args) { 13496 arg.visitExpression(visitor, context); 13497 } 13498 } 13499 isEquivalent() { 13500 return false; 13501 } 13502 isConstant() { 13503 return false; 13504 } 13505 transformInternalExpressions(transform, flags) { 13506 for (let idx = 0; idx < this.args.length; idx++) { 13507 this.args[idx] = transformExpressionsInExpression(this.args[idx], transform, flags); 13508 } 13509 } 13510 clone() { 13511 const r = new PipeBindingExpr(this.target, this.targetSlot, this.name, this.args.map(a => a.clone())); 13512 r.varOffset = this.varOffset; 13513 return r; 13514 } 13515} 13516class PipeBindingVariadicExpr extends ExpressionBase { 13517 static #_ = (_e = ConsumesVarsTrait, _f = UsesVarOffset); 13518 constructor(target, targetSlot, name, args, numArgs) { 13519 super(); 13520 this.target = target; 13521 this.targetSlot = targetSlot; 13522 this.name = name; 13523 this.args = args; 13524 this.numArgs = numArgs; 13525 this.kind = ExpressionKind.PipeBindingVariadic; 13526 this[_e] = true; 13527 this[_f] = true; 13528 this.varOffset = null; 13529 } 13530 visitExpression(visitor, context) { 13531 this.args.visitExpression(visitor, context); 13532 } 13533 isEquivalent() { 13534 return false; 13535 } 13536 isConstant() { 13537 return false; 13538 } 13539 transformInternalExpressions(transform, flags) { 13540 this.args = transformExpressionsInExpression(this.args, transform, flags); 13541 } 13542 clone() { 13543 const r = new PipeBindingVariadicExpr(this.target, this.targetSlot, this.name, this.args.clone(), this.numArgs); 13544 r.varOffset = this.varOffset; 13545 return r; 13546 } 13547} 13548class SafePropertyReadExpr extends ExpressionBase { 13549 constructor(receiver, name) { 13550 super(); 13551 this.receiver = receiver; 13552 this.name = name; 13553 this.kind = ExpressionKind.SafePropertyRead; 13554 } 13555 // An alias for name, which allows other logic to handle property reads and keyed reads together. 13556 get index() { 13557 return this.name; 13558 } 13559 visitExpression(visitor, context) { 13560 this.receiver.visitExpression(visitor, context); 13561 } 13562 isEquivalent() { 13563 return false; 13564 } 13565 isConstant() { 13566 return false; 13567 } 13568 transformInternalExpressions(transform, flags) { 13569 this.receiver = transformExpressionsInExpression(this.receiver, transform, flags); 13570 } 13571 clone() { 13572 return new SafePropertyReadExpr(this.receiver.clone(), this.name); 13573 } 13574} 13575class SafeKeyedReadExpr extends ExpressionBase { 13576 constructor(receiver, index, sourceSpan) { 13577 super(sourceSpan); 13578 this.receiver = receiver; 13579 this.index = index; 13580 this.kind = ExpressionKind.SafeKeyedRead; 13581 } 13582 visitExpression(visitor, context) { 13583 this.receiver.visitExpression(visitor, context); 13584 this.index.visitExpression(visitor, context); 13585 } 13586 isEquivalent() { 13587 return false;
13588 } 13589 isConstant() { 13590 return false; 13591 } 13592 transformInternalExpressions(transform, flags) { 13593 this.receiver = transformExpressionsInExpression(this.receiver, transform, flags); 13594 this.index = transformExpressionsInExpression(this.index, transform, flags); 13595 } 13596 clone() { 13597 return new SafeKeyedReadExpr(this.receiver.clone(), this.index.clone(), this.sourceSpan); 13598 } 13599} 13600class SafeInvokeFunctionExpr extends ExpressionBase { 13601 constructor(receiver, args) { 13602 super(); 13603 this.receiver = receiver; 13604 this.args = args; 13605 this.kind = ExpressionKind.SafeInvokeFunction; 13606 } 13607 visitExpression(visitor, context) { 13608 this.receiver.visitExpression(visitor, context); 13609 for (const a of this.args) { 13610 a.visitExpression(visitor, context); 13611 } 13612 } 13613 isEquivalent() { 13614 return false; 13615 } 13616 isConstant() { 13617 return false; 13618 } 13619 transformInternalExpressions(transform, flags) { 13620 this.receiver = transformExpressionsInExpression(this.receiver, transform, flags); 13621 for (let i = 0; i < this.args.length; i++) { 13622 this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags); 13623 } 13624 } 13625 clone() { 13626 return new SafeInvokeFunctionExpr(this.receiver.clone(), this.args.map(a => a.clone())); 13627 } 13628} 13629class SafeTernaryExpr extends ExpressionBase { 13630 constructor(guard, expr) { 13631 super(); 13632 this.guard = guard; 13633 this.expr = expr; 13634 this.kind = ExpressionKind.SafeTernaryExpr; 13635 } 13636 visitExpression(visitor, context) { 13637 this.guard.visitExpression(visitor, context); 13638 this.expr.visitExpression(visitor, context); 13639 } 13640 isEquivalent() { 13641 return false; 13642 } 13643 isConstant() { 13644 return false; 13645 } 13646 transformInternalExpressions(transform, flags) { 13647 this.guard = transformExpressionsInExpression(this.guard, transform, flags); 13648 this.expr = transformExpressionsInExpression(this.expr, transform, flags); 13649 } 13650 clone() { 13651 return new SafeTernaryExpr(this.guard.clone(), this.expr.clone()); 13652 } 13653} 13654class EmptyExpr extends ExpressionBase { 13655 constructor() { 13656 super(...arguments); 13657 this.kind = ExpressionKind.EmptyExpr; 13658 } 13659 visitExpression(visitor, context) {} 13660 isEquivalent(e) { 13661 return e instanceof EmptyExpr; 13662 } 13663 isConstant() { 13664 return true; 13665 } 13666 clone() { 13667 return new EmptyExpr(); 13668 } 13669 transformInternalExpressions() {} 13670} 13671class AssignTemporaryExpr extends ExpressionBase { 13672 constructor(expr, xref) { 13673 super(); 13674 this.expr = expr; 13675 this.xref = xref; 13676 this.kind = ExpressionKind.AssignTemporaryExpr; 13677 this.name = null; 13678 } 13679 visitExpression(visitor, context) { 13680 this.expr.visitExpression(visitor, context); 13681 } 13682 isEquivalent() { 13683 return false; 13684 } 13685 isConstant() { 13686 return false; 13687 } 13688 transformInternalExpressions(transform, flags) { 13689 this.expr = transformExpressionsInExpression(this.expr, transform, flags); 13690 } 13691 clone() { 13692 const a = new AssignTemporaryExpr(this.expr.clone(), this.xref); 13693 a.name = this.name; 13694 return a; 13695 } 13696} 13697class ReadTemporaryExpr extends ExpressionBase { 13698 constructor(xref) { 13699 super(); 13700 this.xref = xref; 13701 this.kind = ExpressionKind.ReadTemporaryExpr; 13702 this.name = null; 13703 } 13704 visitExpression(visitor, context) {} 13705 isEquivalent() { 13706 return this.xref === this.xref; 13707 } 13708 isConstant() { 13709 return false; 13710 } 13711 transformInternalExpressions(transform, flags) {} 13712 clone() { 13713 const r = new ReadTemporaryExpr(this.xref); 13714 r.name = this.name; 13715 return r; 13716 } 13717} 13718class SlotLiteralExpr extends ExpressionBase { 13719 constructor(slot) { 13720 super(); 13721 this.slot = slot; 13722 this.kind = ExpressionKind.SlotLiteralExpr; 13723 } 13724 visitExpression(visitor, context) {} 13725 isEquivalent(e) { 13726 return e instanceof SlotLiteralExpr && e.slot === this.slot; 13727 } 13728 isConstant() { 13729 return true; 13730 } 13731 clone() { 13732 return new SlotLiteralExpr(this.slot); 13733 } 13734 transformInternalExpressions() {} 13735} 13736class ConditionalCaseExpr extends ExpressionBase { 13737 /** 13738 * Create an expression for one branch of a conditional. 13739 * @param expr The expression to be tested for this case. Might be null, as in an `else` case. 13740 * @param target The Xref of the view to be displayed if this condition is true. 13741 */ 13742 constructor(expr, target, targetSlot, alias = null) { 13743 super(); 13744 this.expr = expr; 13745 this.target = target; 13746 this.targetSlot = targetSlot; 13747 this.alias = alias; 13748 this.kind = ExpressionKind.ConditionalCase; 13749 } 13750 visitExpression(visitor, context) { 13751 if (this.expr !== null) { 13752 this.expr.visitExpression(visitor, context); 13753 } 13754 } 13755 isEquivalent(e) { 13756 return e instanceof ConditionalCaseExpr && e.expr === this.expr; 13757 } 13758 isConstant() { 13759 return true; 13760 } 13761 clone() { 13762 return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot); 13763 } 13764 transformInternalExpressions(transform, flags) { 13765 if (this.expr !== null) { 13766 this.expr = transformExpressionsInExpression(this.expr, transform, flags); 13767 } 13768 } 13769}
13770class ConstCollectedExpr extends ExpressionBase { 13771 constructor(expr) { 13772 super(); 13773 this.expr = expr; 13774 this.kind = ExpressionKind.ConstCollected; 13775 } 13776 transformInternalExpressions(transform, flags) { 13777 this.expr = transform(this.expr, flags); 13778 } 13779 visitExpression(visitor, context) { 13780 this.expr.visitExpression(visitor, context); 13781 } 13782 isEquivalent(e) { 13783 if (!(e instanceof ConstCollectedExpr)) { 13784 return false; 13785 } 13786 return this.expr.isEquivalent(e.expr); 13787 } 13788 isConstant() { 13789 return this.expr.isConstant(); 13790 } 13791 clone() { 13792 return new ConstCollectedExpr(this.expr); 13793 } 13794} 13795/** 13796 * Visits all `Expression`s in the AST of `op` with the `visitor` function. 13797 */ 13798function visitExpressionsInOp(op, visitor) { 13799 transformExpressionsInOp(op, (expr, flags) => { 13800 visitor(expr, flags); 13801 return expr; 13802 }, VisitorContextFlag.None); 13803} 13804var VisitorContextFlag; 13805(function (VisitorContextFlag) { 13806 VisitorContextFlag[VisitorContextFlag["None"] = 0] = "None"; 13807 VisitorContextFlag[VisitorContextFlag["InChildOperation"] = 1] = "InChildOperation"; 13808})(VisitorContextFlag || (VisitorContextFlag = {})); 13809function transformExpressionsInInterpolation(interpolation, transform, flags) { 13810 for (let i = 0; i < interpolation.expressions.length; i++) { 13811 interpolation.expressions[i] = transformExpressionsInExpression(interpolation.expressions[i], transform, flags); 13812 } 13813} 13814/** 13815 * Transform all `Expression`s in the AST of `op` with the `transform` function. 13816 * 13817 * All such operations will be replaced with the result of applying `transform`, which may be an 13818 * identity transformation. 13819 */ 13820function transformExpressionsInOp(op, transform, flags) { 13821 switch (op.kind) { 13822 case OpKind.StyleProp: 13823 case OpKind.StyleMap: 13824 case OpKind.ClassProp: 13825 case OpKind.ClassMap: 13826 case OpKind.Binding: 13827 if (op.expression instanceof Interpolation) { 13828 transformExpressionsInInterpolation(op.expression, transform, flags); 13829 } else { 13830 op.expression = transformExpressionsInExpression(op.expression, transform, flags); 13831 } 13832 break; 13833 case OpKind.Property: 13834 case OpKind.HostProperty: 13835 case OpKind.Attribute: 13836 if (op.expression instanceof Interpolation) { 13837 transformExpressionsInInterpolation(op.expression, transform, flags); 13838 } else { 13839 op.expression = transformExpressionsInExpression(op.expression, transform, flags); 13840 } 13841 op.sanitizer = op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags); 13842 break; 13843 case OpKind.TwoWayProperty: 13844 op.expression = transformExpressionsInExpression(op.expression, transform, flags); 13845 op.sanitizer = op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags); 13846 break; 13847 case OpKind.I18nExpression: 13848 op.expression = transformExpressionsInExpression(op.expression, transform, flags); 13849 break; 13850 case OpKind.InterpolateText: 13851 transformExpressionsInInterpolation(op.interpolation, transform, flags); 13852 break; 13853 case OpKind.Statement: 13854 transformExpressionsInStatement(op.statement, transform, flags); 13855 break; 13856 case OpKind.Variable: 13857 op.initializer = transformExpressionsInExpression(op.initializer, transform, flags); 13858 break; 13859 case OpKind.Conditional: 13860 for (const condition of op.conditions) { 13861 if (condition.expr === null) { 13862 // This is a default case. 13863 continue; 13864 } 13865 condition.expr = transformExpressionsInExpression(condition.expr, transform, flags); 13866 } 13867 if (op.processed !== null) { 13868 op.processed = transformExpressionsInExpression(op.processed, transform, flags); 13869 } 13870 if (op.contextValue !== null) { 13871 op.contextValue = transformExpressionsInExpression(op.contextValue, transform, flags); 13872 } 13873 break; 13874 case OpKind.Listener: 13875 case OpKind.TwoWayListener: 13876 for (const innerOp of op.handlerOps) { 13877 transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation); 13878 } 13879 break; 13880 case OpKind.ExtractedAttribute: 13881 op.expression = op.expression && transformExpressionsInExpression(op.expression, transform, flags); 13882 op.trustedValueFn = op.trustedValueFn && transformExpressionsInExpression(op.trustedValueFn, transform, flags); 13883 break; 13884 case OpKind.RepeaterCreate: 13885 op.track = transformExpressionsInExpression(op.track, transform, flags); 13886 if (op.trackByFn !== null) { 13887 op.trackByFn = transformExpressionsInExpression(op.trackByFn, transform, flags); 13888 } 13889 break; 13890 case OpKind.Repeater: 13891 op.collection = transformExpressionsInExpression(op.collection, transform, flags); 13892 break; 13893 case OpKind.Defer: 13894 if (op.loadingConfig !== null) { 13895 op.loadingConfig = transformExpressionsInExpression(op.loadingConfig, transform, flags); 13896 } 13897 if (op.placeholderConfig !== null) { 13898 op.placeholderConfig = transformExpressionsInExpression(op.placeholderConfig, transform, flags); 13899 } 13900 if (op.resolverFn !== null) { 13901 op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags); 13902 } 13903 break; 13904 case OpKind.I18nMessage: 13905 for (const [placeholder, expr] of op.params) { 13906 op.params.set(placeholder, transformExpressionsInExpression(expr, transform, flags)); 13907 } 13908 for (const [placeholder, expr] of op.postprocessingParams) { 13909 op.postprocessingParams.set(placeholder, transformExpressionsInExpression(expr, transform, flags)); 13910 } 13911 break; 13912 case OpKind.DeferWhen: 13913 op.expr = transformExpressionsInExpression(op.expr, transform, flags); 13914 break; 13915 case OpKind.Advance: 13916 case OpKind.Container: 13917 case OpKind.ContainerEnd: 13918 case OpKind.ContainerStart: 13919 case OpKind.DeferOn: 13920 case OpKind.DisableBindings: 13921 case OpKind.Element: 13922 case OpKind.ElementEnd: 13923 case OpKind.ElementStart: 13924 case OpKind.EnableBindings: 13925 case OpKind.I18n: 13926 case OpKind.I18nApply: 13927 case OpKind.I18nContext: 13928 case OpKind.I18nEnd: 13929 case OpKind.I18nStart: 13930 case OpKind.IcuEnd: 13931 case OpKind.IcuStart: 13932 case OpKind.Namespace: 13933 case OpKind.Pipe: 13934 case OpKind.Projection: 13935 case OpKind.ProjectionDef: 13936 case OpKind.Template: 13937 case OpKind.Text: 13938 case OpKind.I18nAttributes: 13939 case OpKind.IcuPlaceholder: 13940 // These operations contain no expressions. 13941 break; 13942 default: 13943 throw new Error(`AssertionError: transformExpressionsInOp doesn't handle ${OpKind[op.kind]}`); 13944 } 13945} 13946/** 13947 * Transform all `Expression`s in the AST of `expr` with the `transform` function. 13948 * 13949 * All such operations will be replaced with the result of applying `transform`, which may be an 13950 * identity transformation. 13951 */ 13952function transformExpressionsInExpression(expr, transform, flags) { 13953 if (expr instanceof ExpressionBase) { 13954 expr.transformInternalExpressions(transform, flags); 13955 } else if (expr instanceof BinaryOperatorExpr) { 13956 expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags); 13957 expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags); 13958 } else if (expr instanceof UnaryOperatorExpr) { 13959 expr.expr = transformExpressionsInExpression(expr.expr, transform, flags); 13960 } else if (expr instanceof ReadPropExpr) { 13961 expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags); 13962 } else if (expr instanceof ReadKeyExpr) { 13963 expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags); 13964 expr.index = transformExpressionsInExpression(expr.index, transform, flags); 13965 } else if (expr instanceof WritePropExpr) { 13966 expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags); 13967 expr.value = transformExpressionsInExpression(expr.value, transform, flags); 13968 } else if (expr instanceof WriteKeyExpr) { 13969 expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags); 13970 expr.index = transformExpressionsInExpression(expr.index, transform, flags); 13971 expr.value = transformExpressionsInExpression(expr.value, transform, flags); 13972 } else if (expr instanceof InvokeFunctionExpr) { 13973 expr.fn = transformExpressionsInExpression(expr.fn, transform, flags); 13974 for (let i = 0; i < expr.args.length; i++) { 13975 expr.args[i] = transformExpressionsInExpression(expr.args[i], transform, flags); 13976 } 13977 } else if (expr instanceof LiteralArrayExpr) { 13978 for (let i = 0; i < expr.entries.length; i++) { 13979 expr.entries[i] = transformExpressionsInExpression(expr.entries[i], transform, flags); 13980 } 13981 } else if (expr instanceof LiteralMapExpr) { 13982 for (let i = 0; i < expr.entries.length; i++) { 13983 expr.entries[i].value = transformExpressionsInExpression(expr.entries[i].value, transform, flags); 13984 } 13985 } else if (expr instanceof ConditionalExpr) { 13986 expr.condition = transformExpressionsInExpression(expr.condition, transform, flags); 13987 expr.trueCase = transformExpressionsInExpression(expr.trueCase, transform, flags); 13988 if (expr.falseCase !== null) { 13989 expr.falseCase = transformExpressionsInExpression(expr.falseCase, transform, flags); 13990 } 13991 } else if (expr instanceof TypeofExpr) { 13992 expr.expr = transformExpressionsInExpression(expr.expr, transform, flags); 13993 }
13993 else if (expr instanceof WriteVarExpr) { 13994 expr.value = transformExpressionsInExpression(expr.value, transform, flags); 13995 } else if (expr instanceof LocalizedString) { 13996 for (let i = 0; i < expr.expressions.length; i++) { 13997 expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags); 13998 } 13999 } else if (expr instanceof NotExpr) { 14000 expr.condition = transformExpressionsInExpression(expr.condition, transform, flags); 14001 } else if (expr instanceof TaggedTemplateExpr) { 14002 expr.tag = transformExpressionsInExpression(expr.tag, transform, flags); 14003 expr.template.expressions = expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags)); 14004 } else if (expr instanceof ArrowFunctionExpr) { 14005 if (Array.isArray(expr.body)) { 14006 for (let i = 0; i < expr.body.length; i++) { 14007 transformExpressionsInStatement(expr.body[i], transform, flags); 14008 } 14009 } else { 14010 expr.body = transformExpressionsInExpression(expr.body, transform, flags); 14011 } 14012 } else if (expr instanceof WrappedNodeExpr) { 14013 // TODO: Do we need to transform any TS nodes nested inside of this expression? 14014 } else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr || expr instanceof LiteralExpr) { 14015 // No action for these types. 14016 } else { 14017 throw new Error(`Unhandled expression kind: ${expr.constructor.name}`); 14018 } 14019 return transform(expr, flags); 14020} 14021/** 14022 * Transform all `Expression`s in the AST of `stmt` with the `transform` function. 14023 * 14024 * All such operations will be replaced with the result of applying `transform`, which may be an 14025 * identity transformation. 14026 */ 14027function transformExpressionsInStatement(stmt, transform, flags) { 14028 if (stmt instanceof ExpressionStatement) { 14029 stmt.expr = transformExpressionsInExpression(stmt.expr, transform, flags); 14030 } else if (stmt instanceof ReturnStatement) { 14031 stmt.value = transformExpressionsInExpression(stmt.value, transform, flags); 14032 } else if (stmt instanceof DeclareVarStmt) { 14033 if (stmt.value !== undefined) { 14034 stmt.value = transformExpressionsInExpression(stmt.value, transform, flags); 14035 } 14036 } else if (stmt instanceof IfStmt) { 14037 stmt.condition = transformExpressionsInExpression(stmt.condition, transform, flags); 14038 for (const caseStatement of stmt.trueCase) { 14039 transformExpressionsInStatement(caseStatement, transform, flags); 14040 } 14041 for (const caseStatement of stmt.falseCase) { 14042 transformExpressionsInStatement(caseStatement, transform, flags); 14043 } 14044 } else { 14045 throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`); 14046 } 14047} 14048/** 14049 * Checks whether the given expression is a string literal. 14050 */ 14051function isStringLiteral(expr) { 14052 return expr instanceof LiteralExpr && typeof expr.value === 'string'; 14053} 14054 14055/** 14056 * A linked list of `Op` nodes of a given subtype. 14057 * 14058 * @param OpT specific subtype of `Op` nodes which this list contains. 14059 */ 14060class OpList { 14061 static #_ = this.nextListId = 0; 14062 constructor() { 14063 /** 14064 * Debug ID of this `OpList` instance. 14065 */ 14066 this.debugListId = OpList.nextListId++; 14067 // OpList uses static head/tail nodes of a special `ListEnd` type. 14068 // This avoids the need for special casing of the first and last list 14069 // elements in all list operations. 14070 this.head = { 14071 kind: OpKind.ListEnd, 14072 next: null, 14073 prev: null, 14074 debugListId: this.debugListId 14075 }; 14076 this.tail = { 14077 kind: OpKind.ListEnd, 14078 next: null, 14079 prev: null, 14080 debugListId: this.debugListId 14081 }; 14082 // Link `head` and `tail` together at the start (list is empty). 14083 this.head.next = this.tail; 14084 this.tail.prev = this.head; 14085 } 14086 /** 14087 * Push a new operation to the tail of the list. 14088 */ 14089 push(op) { 14090 if (Array.isArray(op)) { 14091 for (const o of op) { 14092 this.push(o); 14093 } 14094 return; 14095 } 14096 OpList.assertIsNotEnd(op); 14097 OpList.assertIsUnowned(op); 14098 op.debugListId = this.debugListId; 14099 // The old "previous" node (which might be the head, if the list is empty). 14100 const oldLast = this.tail.prev; 14101 // Insert `op` following the old last node. 14102 op.prev = oldLast; 14103 oldLast.next = op; 14104 // Connect `op` with the list tail. 14105 op.next = this.tail; 14106 this.tail.prev = op; 14107 } 14108 /** 14109 * Prepend one or more nodes to the start of the list. 14110 */ 14111 prepend(ops) { 14112 if (ops.length === 0) { 14113 return; 14114 } 14115 for (const op of ops) { 14116 OpList.assertIsNotEnd(op); 14117 OpList.assertIsUnowned(op); 14118 op.debugListId = this.debugListId; 14119 } 14120 const first = this.head.next; 14121 let prev = this.head; 14122 for (const op of ops) { 14123 prev.next = op; 14124 op.prev = prev; 14125 prev = op; 14126 } 14127 prev.next = first; 14128 first.prev = prev; 14129 } 14130 /** 14131 * `OpList` is iterable via the iteration protocol. 14132 * 14133 * It's safe to mutate the part of the list that has already been returned by the iterator, up to 14134 * and including the last operation returned. Mutations beyond that point _may_ be safe, but may 14135 * also corrupt the iteration position and should be avoided. 14136 */ 14137 *[Symbol.iterator]() {
14138 let current = this.head.next; 14139 while (current !== this.tail) { 14140 // Guards against corruption of the iterator state by mutations to the tail of the list during 14141 // iteration. 14142 OpList.assertIsOwned(current, this.debugListId); 14143 const next = current.next; 14144 yield current; 14145 current = next; 14146 } 14147 } 14148 *reversed() { 14149 let current = this.tail.prev; 14150 while (current !== this.head) { 14151 OpList.assertIsOwned(current, this.debugListId); 14152 const prev = current.prev; 14153 yield current; 14154 current = prev; 14155 } 14156 } 14157 /** 14158 * Replace `oldOp` with `newOp` in the list. 14159 */ 14160 static replace(oldOp, newOp) { 14161 OpList.assertIsNotEnd(oldOp); 14162 OpList.assertIsNotEnd(newOp); 14163 OpList.assertIsOwned(oldOp); 14164 OpList.assertIsUnowned(newOp); 14165 newOp.debugListId = oldOp.debugListId; 14166 if (oldOp.prev !== null) { 14167 oldOp.prev.next = newOp; 14168 newOp.prev = oldOp.prev; 14169 } 14170 if (oldOp.next !== null) { 14171 oldOp.next.prev = newOp; 14172 newOp.next = oldOp.next; 14173 } 14174 oldOp.debugListId = null; 14175 oldOp.prev = null; 14176 oldOp.next = null; 14177 } 14178 /** 14179 * Replace `oldOp` with some number of new operations in the list (which may include `oldOp`). 14180 */ 14181 static replaceWithMany(oldOp, newOps) { 14182 if (newOps.length === 0) { 14183 // Replacing with an empty list -> pure removal. 14184 OpList.remove(oldOp); 14185 return; 14186 } 14187 OpList.assertIsNotEnd(oldOp); 14188 OpList.assertIsOwned(oldOp); 14189 const listId = oldOp.debugListId; 14190 oldOp.debugListId = null; 14191 for (const newOp of newOps) { 14192 OpList.assertIsNotEnd(newOp); 14193 // `newOp` might be `oldOp`, but at this point it's been marked as unowned. 14194 OpList.assertIsUnowned(newOp); 14195 } 14196 // It should be safe to reuse `oldOp` in the `newOps` list - maybe you want to sandwich an 14197 // operation between two new ops. 14198 const { 14199 prev: oldPrev, 14200 next: oldNext 14201 } = oldOp; 14202 oldOp.prev = null; 14203 oldOp.next = null; 14204 let prev = oldPrev; 14205 for (const newOp of newOps) { 14206 this.assertIsUnowned(newOp); 14207 newOp.debugListId = listId; 14208 prev.next = newOp; 14209 newOp.prev = prev; 14210 // This _should_ be the case, but set it just in case. 14211 newOp.next = null; 14212 prev = newOp; 14213 } 14214 // At the end of iteration, `prev` holds the last node in the list. 14215 const first = newOps[0]; 14216 const last = prev; 14217 // Replace `oldOp` with the chain `first` -> `last`. 14218 if (oldPrev !== null) { 14219 oldPrev.next = first; 14220 first.prev = oldPrev; 14221 } 14222 if (oldNext !== null) { 14223 oldNext.prev = last; 14224 last.next = oldNext; 14225 } 14226 } 14227 /** 14228 * Remove the given node from the list which contains it. 14229 */ 14230 static remove(op) { 14231 OpList.assertIsNotEnd(op); 14232 OpList.assertIsOwned(op); 14233 op.prev.next = op.next; 14234 op.next.prev = op.prev; 14235 // Break any link between the node and this list to safeguard against its usage in future 14236 // operations. 14237 op.debugListId = null; 14238 op.prev = null; 14239 op.next = null; 14240 } 14241 /** 14242 * Insert `op` before `target`. 14243 */ 14244 static insertBefore(op, target) { 14245 if (Array.isArray(op)) { 14246 for (const o of op) { 14247 this.insertBefore(o, target); 14248 } 14249 return; 14250 } 14251 OpList.assertIsOwned(target); 14252 if (target.prev === null) { 14253 throw new Error(`AssertionError: illegal operation on list start`); 14254 } 14255 OpList.assertIsNotEnd(op); 14256 OpList.assertIsUnowned(op); 14257 op.debugListId = target.debugListId; 14258 // Just in case. 14259 op.prev = null; 14260 target.prev.next = op; 14261 op.prev = target.prev; 14262 op.next = target; 14263 target.prev = op; 14264 } 14265 /** 14266 * Insert `op` after `target`. 14267 */ 14268 static insertAfter(op, target) { 14269 OpList.assertIsOwned(target); 14270 if (target.next === null) { 14271 throw new Error(`AssertionError: illegal operation on list end`); 14272 } 14273 OpList.assertIsNotEnd(op); 14274 OpList.assertIsUnowned(op); 14275 op.debugListId = target.debugListId; 14276 target.next.prev = op; 14277 op.next = target.next; 14278 op.prev = target; 14279 target.next = op; 14280 } 14281 /** 14282 * Asserts that `op` does not currently belong to a list. 14283 */ 14284 static assertIsUnowned(op) { 14285 if (op.debugListId !== null) { 14286 throw new Error(`AssertionError: illegal operation on owned node: ${OpKind[op.kind]}`); 14287 } 14288 } 14289 /** 14290 * Asserts that `op` currently belongs to a list. If `byList` is passed, `op` is asserted to 14291 * specifically belong to that list. 14292 */ 14293 static assertIsOwned(op, byList) { 14294 if (op.debugListId === null) { 14295 throw new Error(`AssertionError: illegal operation on unowned node: ${OpKind[op.kind]}`); 14296 } else if (byList !== undefined && op.debugListId !== byList) { 14297 throw new Error(`AssertionError: node belongs to the wrong list (expected ${byList}, actual ${op.debugListId})`); 14298 } 14299 } 14300 /** 14301 * Asserts that `op` is not a special `ListEnd` node. 14302 */ 14303 static assertIsNotEnd(op) { 14304 if (op.kind === OpKind.ListEnd) { 14305 throw new Error(`AssertionError: illegal operation on list head or tail`); 14306 } 14307 } 14308} 14309class SlotHandle { 14310 constructor() { 14311 this.slot = null; 14312 } 14313} 14314 14315/** 14316 * The set of OpKinds that represent the creation of an element or container 14317 */ 14318const elementContainerOpKinds = new Set([OpKind.Element, OpKind.ElementStart, OpKind.Container, OpKind.ContainerStart, OpKind.Template, OpKind.RepeaterCreate]); 14319/** 14320 * Checks whether the given operation represents the creation of an element or container. 14321 */ 14322function isElementOrContainerOp(op) { 14323 return elementContainerOpKinds.has(op.kind); 14324} 14325/** 14326 * Create an `ElementStartOp`. 14327 */ 14328function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) { 14329 return { 14330 kind: OpKind.ElementStart, 14331 xref, 14332 tag, 14333 handle: new SlotHandle(), 14334 attributes: null, 14335 localRefs: [], 14336 nonBindable: false, 14337 namespace, 14338 i18nPlaceholder, 14339 startSourceSpan, 14340 wholeSourceSpan, 14341 ...TRAIT_CONSUMES_SLOT, 14342 ...NEW_OP 14343 }; 14344} 14345/** 14346 * Create a `TemplateOp`. 14347 */ 14348function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) { 14349 return { 14350 kind: OpKind.Template, 14351 xref, 14352 templateKind, 14353 attributes: null, 14354 tag, 14355 handle: new SlotHandle(), 14356 functionNameSuffix, 14357 decls: null, 14358 vars: null, 14359 localRefs: [], 14360 nonBindable: false, 14361 namespace, 14362 i18nPlaceholder, 14363 startSourceSpan, 14364 wholeSourceSpan, 14365 ...TRAIT_CONSUMES_SLOT, 14366 ...NEW_OP 14367 }; 14368} 14369function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) { 14370 return { 14371 kind: OpKind.RepeaterCreate, 14372 attributes: null, 14373 xref: primaryView, 14374 handle: new SlotHandle(), 14375 emptyView, 14376 track, 14377 trackByFn: null, 14378 tag, 14379 emptyTag, 14380 emptyAttributes: null, 14381 functionNameSuffix: 'For', 14382 namespace: Namespace.HTML, 14383 nonBindable: false, 14384 localRefs: [], 14385 decls: null, 14386 vars: null, 14387 varNames, 14388 usesComponentInstance: false, 14389 i18nPlaceholder, 14390 emptyI18nPlaceholder, 14391 startSourceSpan, 14392 wholeSourceSpan, 14393 ...TRAIT_CONSUMES_SLOT, 14394 ...NEW_OP, 14395 ...TRAIT_CONSUMES_VARS, 14396 numSlotsUsed: emptyView === null ? 2 : 3 14397 }; 14398} 14399/** 14400 * Create an `ElementEndOp`. 14401 */ 14402function createElementEndOp(xref, sourceSpan) { 14403 return { 14404 kind: OpKind.ElementEnd, 14405 xref, 14406 sourceSpan, 14407 ...NEW_OP 14408 }; 14409} 14410function createDisableBindingsOp(xref) { 14411 return { 14412 kind: OpKind.DisableBindings, 14413 xref, 14414 ...NEW_OP 14415 }; 14416} 14417function createEnableBindingsOp(xref) { 14418 return { 14419 kind: OpKind.EnableBindings, 14420 xref, 14421 ...NEW_OP 14422 }; 14423} 14424/** 14425 * Create a `TextOp`. 14426 */ 14427function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) { 14428 return { 14429 kind: OpKind.Text, 14430 xref, 14431 handle: new SlotHandle(), 14432 initialValue, 14433 icuPlaceholder, 14434 sourceSpan, 14435 ...TRAIT_CONSUMES_SLOT, 14436 ...NEW_OP 14437 }; 14438} 14439/** 14440 * Create a `ListenerOp`. Host bindings reuse all the listener logic. 14441 */ 14442function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) { 14443 const handlerList = new OpList(); 14444 handlerList.push(handlerOps); 14445 return { 14446 kind: OpKind.Listener, 14447 target, 14448 targetSlot, 14449 tag, 14450 hostListener, 14451 name, 14452 handlerOps: handlerList, 14453 handlerFnName: null, 14454 consumesDollarEvent: false, 14455 isAnimationListener: animationPhase !== null, 14456 animationPhase, 14457 eventTarget, 14458 sourceSpan, 14459 ...NEW_OP 14460 }; 14461} 14462/** 14463 * Create a `TwoWayListenerOp`. 14464 */ 14465function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) { 14466 const handlerList = new OpList(); 14467 handlerList.push(handlerOps); 14468 return { 14469 kind: OpKind.TwoWayListener, 14470 target, 14471 targetSlot, 14472 tag, 14473 name, 14474 handlerOps: handlerList, 14475 handlerFnName: null, 14476 sourceSpan, 14477 ...NEW_OP 14478 }; 14479} 14480function createPipeOp(xref, slot, name) { 14481 return { 14482 kind: OpKind.Pipe, 14483 xref, 14484 handle: slot, 14485 name, 14486 ...NEW_OP, 14487 ...TRAIT_CONSUMES_SLOT 14488 }; 14489} 14490function createNamespaceOp(namespace) { 14491 return { 14492 kind: OpKind.Namespace, 14493 active: namespace, 14494 ...NEW_OP 14495 }; 14496} 14497function createProjectionDefOp(def) { 14498 return { 14499 kind: OpKind.ProjectionDef, 14500 def, 14501 ...NEW_OP 14502 }; 14503} 14504function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) { 14505 return { 14506 kind: OpKind.Projection, 14507 xref, 14508 handle: new SlotHandle(), 14509 selector, 14510 i18nPlaceholder, 14511 projectionSlotIndex: 0, 14512 attributes: null, 14513 localRefs: [], 14514 sourceSpan, 14515 ...NEW_OP, 14516 ...TRAIT_CONSUMES_SLOT 14517 }; 14518} 14519/** 14520 * Create an `ExtractedAttributeOp`. 14521 */ 14522function createExtractedAttributeOp(target, bindingKind, namespace, name, expression, i18nContext, i18nMessage, securityContext) { 14523 return { 14524 kind: OpKind.ExtractedAttribute, 14525 target, 14526 bindingKind, 14527 namespace, 14528 name, 14529 expression, 14530 i18nContext, 14531 i18nMessage, 14532 securityContext,
14533 trustedValueFn: null, 14534 ...NEW_OP 14535 }; 14536} 14537function createDeferOp(xref, main, mainSlot, metadata, resolverFn, sourceSpan) { 14538 return { 14539 kind: OpKind.Defer, 14540 xref, 14541 handle: new SlotHandle(), 14542 mainView: main, 14543 mainSlot, 14544 loadingView: null, 14545 loadingSlot: null, 14546 loadingConfig: null, 14547 loadingMinimumTime: null, 14548 loadingAfterTime: null, 14549 placeholderView: null, 14550 placeholderSlot: null, 14551 placeholderConfig: null, 14552 placeholderMinimumTime: null, 14553 errorView: null, 14554 errorSlot: null, 14555 metadata, 14556 resolverFn, 14557 sourceSpan, 14558 ...NEW_OP, 14559 ...TRAIT_CONSUMES_SLOT, 14560 numSlotsUsed: 2 14561 }; 14562} 14563function createDeferOnOp(defer, trigger, prefetch, sourceSpan) { 14564 return { 14565 kind: OpKind.DeferOn, 14566 defer, 14567 trigger, 14568 prefetch, 14569 sourceSpan, 14570 ...NEW_OP 14571 }; 14572} 14573/** 14574 * Create an `ExtractedMessageOp`. 14575 */ 14576function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) { 14577 return { 14578 kind: OpKind.I18nMessage, 14579 xref, 14580 i18nContext, 14581 i18nBlock, 14582 message, 14583 messagePlaceholder, 14584 params, 14585 postprocessingParams, 14586 needsPostprocessing, 14587 subMessages: [], 14588 ...NEW_OP 14589 }; 14590} 14591/** 14592 * Create an `I18nStartOp`. 14593 */ 14594function createI18nStartOp(xref, message, root, sourceSpan) { 14595 return { 14596 kind: OpKind.I18nStart, 14597 xref, 14598 handle: new SlotHandle(), 14599 root: root ?? xref, 14600 message, 14601 messageIndex: null, 14602 subTemplateIndex: null, 14603 context: null, 14604 sourceSpan, 14605 ...NEW_OP, 14606 ...TRAIT_CONSUMES_SLOT 14607 }; 14608} 14609/** 14610 * Create an `I18nEndOp`. 14611 */ 14612function createI18nEndOp(xref, sourceSpan) { 14613 return { 14614 kind: OpKind.I18nEnd, 14615 xref, 14616 sourceSpan, 14617 ...NEW_OP 14618 }; 14619} 14620/** 14621 * Creates an ICU start op. 14622 */ 14623function createIcuStartOp(xref, message, messagePlaceholder, sourceSpan) { 14624 return { 14625 kind: OpKind.IcuStart, 14626 xref, 14627 message, 14628 messagePlaceholder, 14629 context: null, 14630 sourceSpan, 14631 ...NEW_OP 14632 }; 14633} 14634/** 14635 * Creates an ICU end op. 14636 */ 14637function createIcuEndOp(xref) { 14638 return { 14639 kind: OpKind.IcuEnd, 14640 xref, 14641 ...NEW_OP 14642 }; 14643} 14644/** 14645 * Creates an ICU placeholder op. 14646 */ 14647function createIcuPlaceholderOp(xref, name, strings) { 14648 return { 14649 kind: OpKind.IcuPlaceholder, 14650 xref, 14651 name, 14652 strings, 14653 expressionPlaceholders: [], 14654 ...NEW_OP 14655 }; 14656} 14657function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) { 14658 if (i18nBlock === null && contextKind !== I18nContextKind.Attr) { 14659 throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.'); 14660 } 14661 return { 14662 kind: OpKind.I18nContext, 14663 contextKind, 14664 xref, 14665 i18nBlock, 14666 message, 14667 sourceSpan, 14668 params: new Map(), 14669 postprocessingParams: new Map(), 14670 ...NEW_OP 14671 }; 14672} 14673function createI18nAttributesOp(xref, handle, target) { 14674 return { 14675 kind: OpKind.I18nAttributes, 14676 xref, 14677 handle, 14678 target, 14679 i18nAttributesConfig: null, 14680 ...NEW_OP, 14681 ...TRAIT_CONSUMES_SLOT 14682 }; 14683} 14684function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) { 14685 return { 14686 kind: OpKind.HostProperty, 14687 name, 14688 expression, 14689 isAnimationTrigger, 14690 i18nContext, 14691 securityContext, 14692 sanitizer: null, 14693 sourceSpan, 14694 ...TRAIT_CONSUMES_VARS, 14695 ...NEW_OP 14696 }; 14697} 14698 14699/** 14700 * When referenced in the template's context parameters, this indicates a reference to the entire 14701 * context object, rather than a specific parameter. 14702 */ 14703const CTX_REF = 'CTX_REF_MARKER'; 14704var CompilationJobKind; 14705(function (CompilationJobKind) { 14706 CompilationJobKind[CompilationJobKind["Tmpl"] = 0] = "Tmpl"; 14707 CompilationJobKind[CompilationJobKind["Host"] = 1] = "Host"; 14708 CompilationJobKind[CompilationJobKind["Both"] = 2] = "Both"; 14709})(CompilationJobKind || (CompilationJobKind = {})); 14710/** 14711 * An entire ongoing compilation, which will result in one or more template functions when complete. 14712 * Contains one or more corresponding compilation units. 14713 */ 14714class CompilationJob { 14715 constructor(componentName, pool, compatibility) { 14716 this.componentName = componentName; 14717 this.pool = pool; 14718 this.compatibility = compatibility; 14719 this.kind = CompilationJobKind.Both; 14720 /** 14721 * Tracks the next `ir.XrefId` which can be assigned as template structures are ingested. 14722 */ 14723 this.nextXrefId = 0; 14724 } 14725 /** 14726 * Generate a new unique `ir.XrefId` in this job. 14727 */ 14728 allocateXrefId() { 14729 return this.nextXrefId++; 14730 } 14731} 14732/** 14733 * Compilation-in-progress of a whole component's template, including the main template and any 14734 * embedded views or host bindings. 14735 */ 14736class ComponentCompilationJob extends CompilationJob { 14737 constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) { 14738 super(componentName, pool, compatibility); 14739 this.relativeContextFilePath = relativeContextFilePath; 14740 this.i18nUseExternalIds = i18nUseExternalIds; 14741 this.deferBlocksMeta = deferBlocksMeta; 14742 this.allDeferrableDepsFn = allDeferrableDepsFn; 14743 this.kind = CompilationJobKind.Tmpl; 14744 this.fnSuffix = 'Template'; 14745 this.views = new Map(); 14746 /** 14747 * Causes ngContentSelectors to be emitted, for content projection slots in the view. Possibly a 14748 * reference into the constant pool. 14749 */ 14750 this.contentSelectors = null; 14751 /** 14752 * Constant expressions used by operations within this component's compilation. 14753 * 14754 * This will eventually become the `consts` array in the component definition. 14755 */ 14756 this.consts = []; 14757 /** 14758 * Initialization statements needed to set up the consts. 14759 */
14760 this.constsInitializers = []; 14761 this.root = new ViewCompilationUnit(this, this.allocateXrefId(), null); 14762 this.views.set(this.root.xref, this.root); 14763 } 14764 /** 14765 * Add a `ViewCompilation` for a new embedded view to this compilation. 14766 */ 14767 allocateView(parent) { 14768 const view = new ViewCompilationUnit(this, this.allocateXrefId(), parent); 14769 this.views.set(view.xref, view); 14770 return view; 14771 } 14772 get units() { 14773 return this.views.values(); 14774 } 14775 /** 14776 * Add a constant `o.Expression` to the compilation and return its index in the `consts` array. 14777 */ 14778 addConst(newConst, initializers) { 14779 for (let idx = 0; idx < this.consts.length; idx++) { 14780 if (this.consts[idx].isEquivalent(newConst)) { 14781 return idx; 14782 } 14783 } 14784 const idx = this.consts.length; 14785 this.consts.push(newConst); 14786 if (initializers) { 14787 this.constsInitializers.push(...initializers); 14788 } 14789 return idx; 14790 } 14791} 14792/** 14793 * A compilation unit is compiled into a template function. Some example units are views and host 14794 * bindings. 14795 */ 14796class CompilationUnit { 14797 constructor(xref) { 14798 this.xref = xref; 14799 /** 14800 * List of creation operations for this view. 14801 * 14802 * Creation operations may internally contain other operations, including update operations. 14803 */ 14804 this.create = new OpList(); 14805 /** 14806 * List of update operations for this view. 14807 */ 14808 this.update = new OpList(); 14809 /** 14810 * Name of the function which will be generated for this unit. 14811 * 14812 * May be `null` if not yet determined. 14813 */ 14814 this.fnName = null; 14815 /** 14816 * Number of variable slots used within this view, or `null` if variables have not yet been 14817 * counted. 14818 */ 14819 this.vars = null; 14820 } 14821 /** 14822 * Iterate over all `ir.Op`s within this view. 14823 * 14824 * Some operations may have child operations, which this iterator will visit. 14825 */ 14826 *ops() { 14827 for (const op of this.create) { 14828 yield op; 14829 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 14830 for (const listenerOp of op.handlerOps) { 14831 yield listenerOp; 14832 } 14833 } 14834 } 14835 for (const op of this.update) { 14836 yield op; 14837 } 14838 } 14839} 14840/** 14841 * Compilation-in-progress of an individual view within a template. 14842 */ 14843class ViewCompilationUnit extends CompilationUnit { 14844 constructor(job, xref, parent) { 14845 super(xref); 14846 this.job = job; 14847 this.parent = parent; 14848 /** 14849 * Map of declared variables available within this view to the property on the context object 14850 * which they alias. 14851 */ 14852 this.contextVariables = new Map(); 14853 /** 14854 * Set of aliases available within this view. An alias is a variable whose provided expression is 14855 * inlined at every location it is used. It may also depend on context variables, by name. 14856 */ 14857 this.aliases = new Set(); 14858 /** 14859 * Number of declaration slots used within this view, or `null` if slots have not yet been 14860 * allocated. 14861 */ 14862 this.decls = null; 14863 } 14864} 14865/** 14866 * Compilation-in-progress of a host binding, which contains a single unit for that host binding. 14867 */ 14868class HostBindingCompilationJob extends CompilationJob { 14869 constructor(componentName, pool, compatibility) { 14870 super(componentName, pool, compatibility); 14871 this.kind = CompilationJobKind.Host; 14872 this.fnSuffix = 'HostBindings'; 14873 this.root = new HostBindingCompilationUnit(this); 14874 } 14875 get units() { 14876 return [this.root]; 14877 } 14878} 14879class HostBindingCompilationUnit extends CompilationUnit { 14880 constructor(job) { 14881 super(0); 14882 this.job = job; 14883 /** 14884 * Much like an element can have attributes, so can a host binding function. 14885 */ 14886 this.attributes = null; 14887 } 14888} 14889 14890/** 14891 * Find any function calls to `$any`, excluding `this.$any`, and delete them, since they have no 14892 * runtime effects. 14893 */ 14894function deleteAnyCasts(job) { 14895 for (const unit of job.units) { 14896 for (const op of unit.ops()) { 14897 transformExpressionsInOp(op, removeAnys, VisitorContextFlag.None); 14898 } 14899 } 14900} 14901function removeAnys(e) { 14902 if (e instanceof InvokeFunctionExpr && e.fn instanceof LexicalReadExpr && e.fn.name === '$any') { 14903 if (e.args.length !== 1) { 14904 throw new Error('The $any builtin function expects exactly one argument.'); 14905 } 14906 return e.args[0]; 14907 } 14908 return e; 14909} 14910
14911/** 14912 * Adds apply operations after i18n expressions. 14913 */ 14914function applyI18nExpressions(job) { 14915 const i18nContexts = new Map(); 14916 for (const unit of job.units) { 14917 for (const op of unit.create) { 14918 if (op.kind === OpKind.I18nContext) { 14919 i18nContexts.set(op.xref, op); 14920 } 14921 } 14922 } 14923 for (const unit of job.units) { 14924 for (const op of unit.update) { 14925 // Only add apply after expressions that are not followed by more expressions. 14926 if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) { 14927 // TODO: what should be the source span for the apply op? 14928 OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op); 14929 } 14930 } 14931 } 14932} 14933/** 14934 * Checks whether the given expression op needs to be followed with an apply op. 14935 */ 14936function needsApplication(i18nContexts, op) { 14937 // If the next op is not another expression, we need to apply. 14938 if (op.next?.kind !== OpKind.I18nExpression) { 14939 return true; 14940 } 14941 const context = i18nContexts.get(op.context); 14942 const nextContext = i18nContexts.get(op.next.context); 14943 if (context === undefined) { 14944 throw new Error('AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp\'s context'); 14945 } 14946 if (nextContext === undefined) { 14947 throw new Error('AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp\'s context'); 14948 } 14949 // If the next op is an expression targeting a different i18n block (or different element, in the 14950 // case of i18n attributes), we need to apply. 14951 // First, handle the case of i18n blocks. 14952 if (context.i18nBlock !== null) { 14953 // This is a block context. Compare the blocks. 14954 if (context.i18nBlock !== nextContext.i18nBlock) { 14955 return true; 14956 } 14957 return false; 14958 } 14959 // Second, handle the case of i18n attributes. 14960 if (op.i18nOwner !== op.next.i18nOwner) { 14961 return true; 14962 } 14963 return false; 14964} 14965 14966/** 14967 * Updates i18n expression ops to target the last slot in their owning i18n block, and moves them 14968 * after the last update instruction that depends on that slot. 14969 */ 14970function assignI18nSlotDependencies(job) { 14971 for (const unit of job.units) { 14972 // The first update op. 14973 let updateOp = unit.update.head; 14974 // I18n expressions currently being moved during the iteration. 14975 let i18nExpressionsInProgress = []; 14976 // Non-null while we are iterating through an i18nStart/i18nEnd pair 14977 let state = null; 14978 for (const createOp of unit.create) { 14979 if (createOp.kind === OpKind.I18nStart) { 14980 state = { 14981 blockXref: createOp.xref, 14982 lastSlotConsumer: createOp.xref 14983 }; 14984 } else if (createOp.kind === OpKind.I18nEnd) { 14985 for (const op of i18nExpressionsInProgress) { 14986 op.target = state.lastSlotConsumer; 14987 OpList.insertBefore(op, updateOp); 14988 } 14989 i18nExpressionsInProgress.length = 0; 14990 state = null; 14991 } 14992 if (hasConsumesSlotTrait(createOp)) { 14993 if (state !== null) { 14994 state.lastSlotConsumer = createOp.xref; 14995 } 14996 while (true) { 14997 if (updateOp.next === null) { 14998 break; 14999 } 15000 if (state !== null && updateOp.kind === OpKind.I18nExpression && updateOp.usage === I18nExpressionFor.I18nText && updateOp.i18nOwner === state.blockXref) { 15001 const opToRemove = updateOp; 15002 updateOp = updateOp.next; 15003 OpList.remove(opToRemove); 15004 i18nExpressionsInProgress.push(opToRemove); 15005 continue; 15006 } 15007 if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) { 15008 break; 15009 } 15010 updateOp = updateOp.next; 15011 } 15012 } 15013 } 15014 } 15015} 15016 15017/** 15018 * Gets a map of all elements in the given view by their xref id. 15019 */ 15020function createOpXrefMap(unit) { 15021 const map = new Map(); 15022 for (const op of unit.create) { 15023 if (!hasConsumesSlotTrait(op)) { 15024 continue; 15025 } 15026 map.set(op.xref, op); 15027 // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here, 15028 // because the slot consumer trait currently only supports one slot per consumer and we
15029 // need two. This should be revisited when making the refactors mentioned in: 15030 // https://github.com/angular/angular/pull/53620#discussion_r1430918822 15031 if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) { 15032 map.set(op.emptyView, op); 15033 } 15034 } 15035 return map; 15036} 15037 15038/** 15039 * Find all extractable attribute and binding ops, and create ExtractedAttributeOps for them. 15040 * In cases where no instruction needs to be generated for the attribute or binding, it is removed. 15041 */ 15042function extractAttributes(job) { 15043 for (const unit of job.units) { 15044 const elements = createOpXrefMap(unit); 15045 for (const op of unit.ops()) { 15046 switch (op.kind) { 15047 case OpKind.Attribute: 15048 extractAttributeOp(unit, op, elements); 15049 break; 15050 case OpKind.Property: 15051 if (!op.isAnimationTrigger) { 15052 let bindingKind; 15053 if (op.i18nMessage !== null && op.templateKind === null) { 15054 // If the binding has an i18n context, it is an i18n attribute, and should have that 15055 // kind in the consts array. 15056 bindingKind = BindingKind.I18n; 15057 } else if (op.isStructuralTemplateAttribute) { 15058 bindingKind = BindingKind.Template; 15059 } else { 15060 bindingKind = BindingKind.Property; 15061 } 15062 OpList.insertBefore( 15063 // Deliberately null i18nMessage value 15064 createExtractedAttributeOp(op.target, bindingKind, null, op.name, /* expression */null, /* i18nContext */null, /* i18nMessage */null, op.securityContext), lookupElement$2(elements, op.target)); 15065 } 15066 break; 15067 case OpKind.TwoWayProperty: 15068 OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, /* expression */null, /* i18nContext */null, /* i18nMessage */null, op.securityContext), lookupElement$2(elements, op.target)); 15069 break; 15070 case OpKind.StyleProp: 15071 case OpKind.ClassProp: 15072 // TODO: Can style or class bindings be i18n attributes? 15073 // The old compiler treated empty style bindings as regular bindings for the purpose of 15074 // directive matching. That behavior is incorrect, but we emulate it in compatibility 15075 // mode. 15076 if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder && op.expression instanceof EmptyExpr) { 15077 OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */null, /* i18nContext */null, /* i18nMessage */null, SecurityContext.STYLE), lookupElement$2(elements, op.target)); 15078 } 15079 break; 15080 case OpKind.Listener: 15081 if (!op.isAnimationListener) { 15082 const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */null, /* i18nContext */null, /* i18nMessage */null, SecurityContext.NONE); 15083 if (job.kind === CompilationJobKind.Host) { 15084 if (job.compatibility) { 15085 // TemplateDefinitionBuilder does not extract listener bindings to the const array 15086 // (which is honestly pretty inconsistent). 15087 break; 15088 } 15089 // This attribute will apply to the enclosing host binding compilation unit, so order 15090 // doesn't matter. 15091 unit.create.push(extractedAttributeOp); 15092 } else { 15093 OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target)); 15094 } 15095 } 15096 break; 15097 case OpKind.TwoWayListener: 15098 // Two-way listeners aren't supported in host bindings. 15099 if (job.kind !== CompilationJobKind.Host) { 15100 const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */null, /* i18nContext */null, /* i18nMessage */null, SecurityContext.NONE); 15101 OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target)); 15102 } 15103 break; 15104 } 15105 } 15106 } 15107} 15108/** 15109 * Looks up an element in the given map by xref ID. 15110 */ 15111function lookupElement$2(elements, xref) { 15112 const el = elements.get(xref); 15113 if (el === undefined) { 15114 throw new Error('All attributes should have an element-like target.'); 15115 } 15116 return el; 15117} 15118/** 15119 * Extracts an attribute binding. 15120 */ 15121function extractAttributeOp(unit, op, elements) { 15122 if (op.expression instanceof Interpolation) { 15123 return; 15124 } 15125 let extractable = op.isTextAttribute || op.expression.isConstant(); 15126 if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) { 15127 // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute 15128 // bindings, even if they are constants. 15129 extractable &&= op.isTextAttribute; 15130 } 15131 if (extractable) { 15132 const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.namespace, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext); 15133 if (unit.job.kind === CompilationJobKind.Host) { 15134 // This attribute will apply to the enclosing host binding compilation unit, so order doesn't 15135 // matter. 15136 unit.create.push(extractedAttributeOp); 15137 } else { 15138 const ownerOp = lookupElement$2(elements, op.target); 15139 OpList.insertBefore(extractedAttributeOp, ownerOp); 15140 } 15141 OpList.remove(op); 15142 } 15143} 15144 15145/** 15146 * Looks up an element in the given map by xref ID. 15147 */ 15148function lookupElement$1(elements, xref) { 15149 const el = elements.get(xref); 15150 if (el === undefined) { 15151 throw new Error('All attributes should have an element-like target.'); 15152 } 15153 return el; 15154} 15155function specializeBindings(job) { 15156 const elements = new Map(); 15157 for (const unit of job.units) { 15158 for (const op of unit.create) { 15159 if (!isElementOrContainerOp(op)) { 15160 continue; 15161 } 15162 elements.set(op.xref, op); 15163 } 15164 } 15165 for (const unit of job.units) { 15166 for (const op of unit.ops()) { 15167 if (op.kind !== OpKind.Binding) { 15168 continue; 15169 } 15170 switch (op.bindingKind) { 15171 case BindingKind.Attribute: 15172 if (op.name === 'ngNonBindable') { 15173 OpList.remove(op); 15174 const target = lookupElement$1(elements, op.target); 15175 target.nonBindable = true; 15176 } else { 15177 const [namespace, name] = splitNsName(op.name); 15178 OpList.replace(op, createAttributeOp(op.target, namespace, name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan)); 15179 } 15180 break; 15181 case BindingKind.Property: 15182 case BindingKind.Animation: 15183 if (job.kind === CompilationJobKind.Host) { 15184 OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan)); 15185 } else { 15186 OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan)); 15187 } 15188 break; 15189 case BindingKind.TwoWayProperty: 15190 if (!(op.expression instanceof Expression)) { 15191 // We shouldn't be able to hit this code path since interpolations in two-way bindings 15192 // result in a parser error. We assert here so that downstream we can assume that 15193 // the value is always an expression. 15194 throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`); 15195 } 15196 OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan)); 15197 break; 15198 case BindingKind.I18n: 15199 case BindingKind.ClassName: 15200 case BindingKind.StyleProperty: 15201 throw new Error(`Unhandled binding of kind ${BindingKind[op.bindingKind]}`); 15202 } 15203 } 15204 } 15205} 15206const CHAINABLE = new Set([Identifiers.attribute, Identifiers.classProp, Identifiers.element, Identifiers.elementContainer, Identifiers.elementContainerEnd, Identifiers.elementContainerStart, Identifiers.elementEnd, Identifiers.elementStart, Identifiers.hostProperty, Identifiers.i18nExp, Identifiers.listener, Identifiers.listener, Identifiers.property, Identifiers.styleProp, Identifiers.stylePropInterpolate1, Identifiers.stylePropInterpolate2, Identifiers.stylePropInterpolate3, Identifiers.stylePropInterpolate4, Identifiers.stylePropInterpolate5, Identifiers.stylePropInterpolate6, Identifiers.stylePropInterpolate7, Identifiers.stylePropInterpolate8, Identifiers.stylePropInterpolateV, Identifiers.syntheticHostListener, Identifiers.syntheticHostProperty, Identifiers.templateCreate, Identifiers.twoWayProperty, Identifiers.twoWayListener]); 15207/** 15208 * Post-process a reified view compilation and convert sequential calls to chainable instructions 15209 * into chain calls. 15210 * 15211 * For example, two `elementStart` operations in sequence: 15212 * 15213 * ```typescript 15214 * elementStart(0, 'div'); 15215 * elementStart(1, 'span'); 15216 * ``` 15217 * 15218 * Can be called as a chain instead: 15219 * 15220 * ```typescript 15221 * elementStart(0, 'div')(1, 'span'); 15222 * ``` 15223 */ 15224function chain(job) { 15225 for (const unit of job.units) { 15226 chainOperationsInList(unit.create); 15227 chainOperationsInList(unit.update); 15228 } 15229} 15230function chainOperationsInList(opList) { 15231 let chain = null; 15232 for (const op of opList) { 15233 if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement)) { 15234 // This type of statement isn't chainable. 15235 chain = null; 15236 continue; 15237 } 15238 if (!(op.statement.expr instanceof InvokeFunctionExpr) || !(op.statement.expr.fn instanceof ExternalExpr)) { 15239 // This is a statement, but not an instruction-type call, so not chainable. 15240 chain = null; 15241 continue; 15242 } 15243 const instruction = op.statement.expr.fn.value;
15244 if (!CHAINABLE.has(instruction)) { 15245 // This instruction isn't chainable. 15246 chain = null; 15247 continue; 15248 } 15249 // This instruction can be chained. It can either be added on to the previous chain (if 15250 // compatible) or it can be the start of a new chain. 15251 if (chain !== null && chain.instruction === instruction) { 15252 // This instruction can be added onto the previous chain. 15253 const expression = chain.expression.callFn(op.statement.expr.args, op.statement.expr.sourceSpan, op.statement.expr.pure); 15254 chain.expression = expression; 15255 chain.op.statement = expression.toStmt(); 15256 OpList.remove(op); 15257 } else { 15258 // Leave this instruction alone for now, but consider it the start of a new chain. 15259 chain = { 15260 op, 15261 instruction, 15262 expression: op.statement.expr 15263 }; 15264 } 15265 } 15266} 15267 15268/** 15269 * Attribute interpolations of the form `[attr.foo]="{{foo}}""` should be "collapsed" into a plain 15270 * attribute instruction, instead of an `attributeInterpolate` instruction. 15271 * 15272 * (We cannot do this for singleton property interpolations, because `propertyInterpolate` 15273 * stringifies its expression.) 15274 * 15275 * The reification step is also capable of performing this transformation, but doing it early in the 15276 * pipeline allows other phases to accurately know what instruction will be emitted. 15277 */ 15278function collapseSingletonInterpolations(job) { 15279 for (const unit of job.units) { 15280 for (const op of unit.update) { 15281 const eligibleOpKind = op.kind === OpKind.Attribute; 15282 if (eligibleOpKind && op.expression instanceof Interpolation && op.expression.strings.length === 2 && op.expression.strings.every(s => s === '')) { 15283 op.expression = op.expression.expressions[0]; 15284 } 15285 } 15286 } 15287} 15288 15289/** 15290 * Collapse the various conditions of conditional ops (if, switch) into a single test expression. 15291 */ 15292function generateConditionalExpressions(job) { 15293 for (const unit of job.units) { 15294 for (const op of unit.ops()) { 15295 if (op.kind !== OpKind.Conditional) { 15296 continue; 15297 } 15298 let test; 15299 // Any case with a `null` condition is `default`. If one exists, default to it instead. 15300 const defaultCase = op.conditions.findIndex(cond => cond.expr === null); 15301 if (defaultCase >= 0) { 15302 const slot = op.conditions.splice(defaultCase, 1)[0].targetSlot; 15303 test = new SlotLiteralExpr(slot); 15304 } else { 15305 // By default, a switch evaluates to `-1`, causing no template to be displayed. 15306 test = literal(-1); 15307 } 15308 // Switch expressions assign their main test to a temporary, to avoid re-executing it. 15309 let tmp = op.test == null ? null : new AssignTemporaryExpr(op.test, job.allocateXrefId()); 15310 // For each remaining condition, test whether the temporary satifies the check. (If no temp is 15311 // present, just check each expression directly.) 15312 for (let i = op.conditions.length - 1; i >= 0; i--) { 15313 let conditionalCase = op.conditions[i]; 15314 if (conditionalCase.expr === null) { 15315 continue; 15316 } 15317 if (tmp !== null) { 15318 const useTmp = i === 0 ? tmp : new ReadTemporaryExpr(tmp.xref); 15319 conditionalCase.expr = new BinaryOperatorExpr(BinaryOperator.Identical, useTmp, conditionalCase.expr); 15320 } else if (conditionalCase.alias !== null) { 15321 const caseExpressionTemporaryXref = job.allocateXrefId(); 15322 conditionalCase.expr = new AssignTemporaryExpr(conditionalCase.expr, caseExpressionTemporaryXref); 15323 op.contextValue = new ReadTemporaryExpr(caseExpressionTemporaryXref); 15324 } 15325 test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.targetSlot), test); 15326 } 15327 // Save the resulting aggregate Joost-expression. 15328 op.processed = test; 15329 // Clear the original conditions array, since we no longer need it, and don't want it to 15330 // affect subsequent phases (e.g. pipe creation). 15331 op.conditions = []; 15332 } 15333 } 15334} 15335const BINARY_OPERATORS = new Map([['&&', BinaryOperator.And], ['>', BinaryOperator.Bigger], ['>=', BinaryOperator.BiggerEquals], ['|', BinaryOperator.BitwiseOr], ['&', BinaryOperator.BitwiseAnd], ['/', BinaryOperator.Divide], ['==', BinaryOperator.Equals], ['===', BinaryOperator.Identical], ['<', BinaryOperator.Lower], ['<=', BinaryOperator.LowerEquals], ['-', BinaryOperator.Minus], ['%', BinaryOperator.Modulo], ['*', BinaryOperator.Multiply], ['!=', BinaryOperator.NotEquals], ['!==', BinaryOperator.NotIdentical], ['??', BinaryOperator.NullishCoalesce], ['||', BinaryOperator.Or], ['+', BinaryOperator.Plus]]); 15336function namespaceForKey(namespacePrefixKey) {
15337 const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]); 15338 if (namespacePrefixKey === null) { 15339 return Namespace.HTML; 15340 } 15341 return NAMESPACES.get(namespacePrefixKey) ?? Namespace.HTML; 15342} 15343function keyForNamespace(namespace) { 15344 const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]); 15345 for (const [k, n] of NAMESPACES.entries()) { 15346 if (n === namespace) { 15347 return k; 15348 } 15349 } 15350 return null; // No namespace prefix for HTML 15351} 15352function prefixWithNamespace(strippedTag, namespace) { 15353 if (namespace === Namespace.HTML) { 15354 return strippedTag; 15355 } 15356 return `:${keyForNamespace(namespace)}:${strippedTag}`; 15357} 15358function literalOrArrayLiteral(value) { 15359 if (Array.isArray(value)) { 15360 return literalArr(value.map(literalOrArrayLiteral)); 15361 } 15362 return literal(value); 15363} 15364 15365/** 15366 * Converts the semantic attributes of element-like operations (elements, templates) into constant 15367 * array expressions, and lifts them into the overall component `consts`. 15368 */ 15369function collectElementConsts(job) { 15370 // Collect all extracted attributes. 15371 const allElementAttributes = new Map(); 15372 for (const unit of job.units) { 15373 for (const op of unit.create) { 15374 if (op.kind === OpKind.ExtractedAttribute) { 15375 const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility); 15376 allElementAttributes.set(op.target, attributes); 15377 attributes.add(op.bindingKind, op.name, op.expression, op.namespace, op.trustedValueFn); 15378 OpList.remove(op); 15379 } 15380 } 15381 } 15382 // Serialize the extracted attributes into the const array. 15383 if (job instanceof ComponentCompilationJob) { 15384 for (const unit of job.units) { 15385 for (const op of unit.create) { 15386 // TODO: Simplify and combine these cases. 15387 if (op.kind == OpKind.Projection) { 15388 const attributes = allElementAttributes.get(op.xref); 15389 if (attributes !== undefined) { 15390 const attrArray = serializeAttributes(attributes); 15391 if (attrArray.entries.length > 0) { 15392 op.attributes = attrArray; 15393 } 15394 } 15395 } else if (isElementOrContainerOp(op)) { 15396 op.attributes = getConstIndex(job, allElementAttributes, op.xref); 15397 // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here, 15398 // because the slot consumer trait currently only supports one slot per consumer and we 15399 // need two. This should be revisited when making the refactors mentioned in: 15400 // https://github.com/angular/angular/pull/53620#discussion_r1430918822 15401 if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) { 15402 op.emptyAttributes = getConstIndex(job, allElementAttributes, op.emptyView); 15403 } 15404 } 15405 } 15406 } 15407 } else if (job instanceof HostBindingCompilationJob) { 15408 // TODO: If the host binding case further diverges, we may want to split it into
15408its own 15409 // phase. 15410 for (const [xref, attributes] of allElementAttributes.entries()) { 15411 if (xref !== job.root.xref) { 15412 throw new Error(`An attribute would be const collected into the host binding's template function, but is not associated with the root xref.`); 15413 } 15414 const attrArray = serializeAttributes(attributes); 15415 if (attrArray.entries.length > 0) { 15416 job.root.attributes = attrArray; 15417 } 15418 } 15419 } 15420} 15421function getConstIndex(job, allElementAttributes, xref) { 15422 const attributes = allElementAttributes.get(xref); 15423 if (attributes !== undefined) { 15424 const attrArray = serializeAttributes(attributes); 15425 if (attrArray.entries.length > 0) { 15426 return job.addConst(attrArray); 15427 } 15428 } 15429 return null; 15430} 15431/** 15432 * Shared instance of an empty array to avoid unnecessary array allocations. 15433 */ 15434const FLYWEIGHT_ARRAY = Object.freeze([]); 15435/** 15436 * Container for all of the various kinds of attributes which are applied on an element. 15437 */ 15438class ElementAttributes { 15439 get attributes() { 15440 return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY; 15441 } 15442 get classes() { 15443 return this.byKind.get(BindingKind.ClassName) ?? FLYWEIGHT_ARRAY; 15444 } 15445 get styles() { 15446 return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY; 15447 } 15448 get bindings() { 15449 return this.propertyBindings ?? FLYWEIGHT_ARRAY; 15450 } 15451 get template() { 15452 return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY; 15453 } 15454 get i18n() { 15455 return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY; 15456 } 15457 constructor(compatibility) { 15458 this.compatibility = compatibility; 15459 this.known = new Map(); 15460 this.byKind = new Map(); 15461 this.propertyBindings = null; 15462 this.projectAs = null; 15463 } 15464 isKnown(kind, name) { 15465 const nameToValue = this.known.get(kind) ?? new Set(); 15466 this.known.set(kind, nameToValue); 15467 if (nameToValue.has(name)) { 15468 return true; 15469 } 15470 nameToValue.add(name); 15471 return false; 15472 } 15473 add(kind, name, value, namespace, trustedValueFn) { 15474 // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts 15475 // array. This seems inefficient, we can probably keep just the first one or the last value 15476 // (whichever actually gets applied when multiple values are listed for the same attribute). 15477 const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder && (kind === BindingKind.Attribute || kind === BindingKind.ClassName || kind === BindingKind.StyleProperty); 15478 if (!allowDuplicates && this.isKnown(kind, name)) { 15479 return; 15480 } 15481 // TODO: Can this be its own phase 15482 if (name === 'ngProjectAs') { 15483 if (value === null || !(value instanceof LiteralExpr) || value.value == null || typeof value.value?.toString() !== 'string') { 15484 throw Error('ngProjectAs must have a string literal value'); 15485 } 15486 this.projectAs = value.value.toString(); 15487 // TODO: TemplateDefinitionBuilder allows `ngProjectAs` to also be assigned as a literal 15488 // attribute. Is this sane? 15489 } 15490 const array = this.arrayFor(kind); 15491 array.push(...getAttributeNameLiterals$1(namespace, name)); 15492 if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) { 15493 if (value === null) { 15494 throw Error('Attribute, i18n attribute, & style element attributes must have a value'); 15495 } 15496 if (trustedValueFn !== null) { 15497 if (!isStringLiteral(value)) { 15498 throw Error('AssertionError: extracted attribute value should be string literal'); 15499 } 15500 array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan)); 15501 } else { 15502 array.push(value); 15503 } 15504 } 15505 } 15506 arrayFor(kind) { 15507 if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) { 15508 this.propertyBindings ??= []; 15509 return this.propertyBindings; 15510 } else { 15511 if (!this.byKind.has(kind)) { 15512 this.byKind.set(kind, []); 15513 } 15514 return this.byKind.get(kind); 15515 } 15516 } 15517} 15518/** 15519 * Gets an array of literal expressions representing the attribute's namespaced name. 15520 */
15521function getAttributeNameLiterals$1(namespace, name) { 15522 const nameLiteral = literal(name); 15523 if (namespace) { 15524 return [literal(0 /* core.AttributeMarker.NamespaceURI */), literal(namespace), nameLiteral]; 15525 } 15526 return [nameLiteral]; 15527} 15528/** 15529 * Serializes an ElementAttributes object into an array expression. 15530 */ 15531function serializeAttributes({ 15532 attributes, 15533 bindings, 15534 classes, 15535 i18n, 15536 projectAs, 15537 styles, 15538 template 15539}) { 15540 const attrArray = [...attributes]; 15541 if (projectAs !== null) { 15542 // Parse the attribute value into a CssSelectorList. Note that we only take the 15543 // first selector, because we don't support multiple selectors in ngProjectAs. 15544 const parsedR3Selector = parseSelectorToR3Selector(projectAs)[0]; 15545 attrArray.push(literal(5 /* core.AttributeMarker.ProjectAs */), literalOrArrayLiteral(parsedR3Selector)); 15546 } 15547 if (classes.length > 0) { 15548 attrArray.push(literal(1 /* core.AttributeMarker.Classes */), ...classes); 15549 } 15550 if (styles.length > 0) { 15551 attrArray.push(literal(2 /* core.AttributeMarker.Styles */), ...styles); 15552 } 15553 if (bindings.length > 0) { 15554 attrArray.push(literal(3 /* core.AttributeMarker.Bindings */), ...bindings); 15555 } 15556 if (template.length > 0) { 15557 attrArray.push(literal(4 /* core.AttributeMarker.Template */), ...template); 15558 } 15559 if (i18n.length > 0) { 15560 attrArray.push(literal(6 /* core.AttributeMarker.I18n */), ...i18n); 15561 } 15562 return literalArr(attrArray); 15563} 15564 15565/** 15566 * Some binding instructions in the update block may actually correspond to i18n bindings. In that 15567 * case, they should be replaced with i18nExp instructions for the dynamic portions. 15568 */ 15569function convertI18nBindings(job) { 15570 const i18nAttributesByElem = new Map(); 15571 for (const unit of job.units) { 15572 for (const op of unit.create) { 15573 if (op.kind === OpKind.I18nAttributes) { 15574 i18nAttributesByElem.set(op.target, op); 15575 } 15576 } 15577 for (const op of unit.update) { 15578 switch (op.kind) { 15579 case OpKind.Property: 15580 case OpKind.Attribute: 15581 if (op.i18nContext === null) { 15582 continue; 15583 } 15584 if (!(op.expression instanceof Interpolation)) { 15585 continue; 15586 } 15587 const i18nAttributesForElem = i18nAttributesByElem.get(op.target); 15588 if (i18nAttributesForElem === undefined) { 15589 throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction'); 15590 } 15591 if (i18nAttributesForElem.target !== op.target) { 15592 throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element'); 15593 } 15594 const ops = []; 15595 for (let i = 0; i < op.expression.expressions.length; i++) { 15596 const expr = op.expression.expressions[i]; 15597 if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) { 15598 throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`); 15599 } 15600 ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan)); 15601 } 15602 OpList.replaceWithMany(op, ops); 15603 break; 15604 } 15605 } 15606 } 15607} 15608 15609/** 15610 * Create extracted deps functions for defer ops. 15611 */ 15612function createDeferDepsFns(job) { 15613 for (const unit of job.units) { 15614 for (const op of unit.create) { 15615 if (op.kind === OpKind.Defer) { 15616 if (op.metadata.deps.length === 0) { 15617 continue; 15618 } 15619 if (op.resolverFn !== null) { 15620 continue; 15621 } 15622 const dependencies = []; 15623 for (const dep of op.metadata.deps) { 15624 if (dep.isDeferrable) { 15625 // Callback function, e.g. `m () => m.MyCmp;`. 15626 const innerFn = arrowFn( 15627 // Default imports are always accessed through the `default` property. 15628 [new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName)); 15629 // Dynamic import, e.g. `import('./a').then(...)`. 15630 const importExpr = new DynamicImportExpr(dep.importPath).prop('then').callFn([innerFn]); 15631 dependencies.push(importExpr); 15632 } else { 15633 // Non-deferrable symbol, just use a reference to the type. 15634 dependencies.push(dep.type); 15635 } 15636 } 15637 const depsFnExpr = arrowFn([], literalArr(dependencies)); 15638 if (op.handle.slot === null) { 15639 throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions'); 15640 } 15641 const fullPathName = unit.fnName?.replace(`_Template`, ``); 15642 op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`, /* Don't use unique names for TDB compatibility */false); 15643 } 15644 } 15645 } 15646} 15647 15648/** 15649 * Create one helper context op per i18n block (including generate descending blocks). 15650 * 15651 * Also, if an ICU exists inside an i18n block that also contains other localizable content (such as 15652 * string), create an additional helper context op for the ICU. 15653 * 15654 * These context ops are later used for generating i18n messages. (Although we generate at least one
15655 * context op per nested view, we will collect them up the tree later, to generate a top-level 15656 * message.) 15657 */ 15658function createI18nContexts(job) { 15659 // Create i18n context ops for i18n attrs. 15660 const attrContextByMessage = new Map(); 15661 for (const unit of job.units) { 15662 for (const op of unit.ops()) { 15663 switch (op.kind) { 15664 case OpKind.Binding: 15665 case OpKind.Property: 15666 case OpKind.Attribute: 15667 case OpKind.ExtractedAttribute: 15668 if (op.i18nMessage === null) { 15669 continue; 15670 } 15671 if (!attrContextByMessage.has(op.i18nMessage)) { 15672 const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null); 15673 unit.create.push(i18nContext); 15674 attrContextByMessage.set(op.i18nMessage, i18nContext.xref); 15675 } 15676 op.i18nContext = attrContextByMessage.get(op.i18nMessage); 15677 break; 15678 } 15679 } 15680 } 15681 // Create i18n context ops for root i18n blocks. 15682 const blockContextByI18nBlock = new Map(); 15683 for (const unit of job.units) { 15684 for (const op of unit.create) { 15685 switch (op.kind) { 15686 case OpKind.I18nStart: 15687 if (op.xref === op.root) { 15688 const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null); 15689 unit.create.push(contextOp); 15690 op.context = contextOp.xref; 15691 blockContextByI18nBlock.set(op.xref, contextOp); 15692 } 15693 break; 15694 } 15695 } 15696 } 15697 // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they 15698 // should inherit from their root i18n block. 15699 for (const unit of job.units) { 15700 for (const op of unit.create) { 15701 if (op.kind === OpKind.I18nStart && op.xref !== op.root) { 15702 const rootContext = blockContextByI18nBlock.get(op.root); 15703 if (rootContext === undefined) { 15704 throw Error('AssertionError: Root i18n block i18n context should have been created.'); 15705 } 15706 op.context = rootContext.xref; 15707 blockContextByI18nBlock.set(op.xref, rootContext); 15708 } 15709 } 15710 } 15711 // Create or assign i18n contexts for ICUs. 15712 let currentI18nOp = null; 15713 for (const unit of job.units) { 15714 for (const op of unit.create) { 15715 switch (op.kind) { 15716 case OpKind.I18nStart: 15717 currentI18nOp = op; 15718 break; 15719 case OpKind.I18nEnd: 15720 currentI18nOp = null; 15721 break; 15722 case OpKind.IcuStart: 15723 if (currentI18nOp === null) { 15724 throw Error('AssertionError: Unexpected ICU outside of an i18n block.'); 15725 } 15726 if (op.message.id !== currentI18nOp.message.id) { 15727 // This ICU is a sub-message inside its parent i18n block message. We need to give it 15728 // its own context. 15729 const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.root, op.message, null); 15730 unit.create.push(contextOp); 15731 op.context = contextOp.xref; 15732 } else { 15733 // This ICU is the only translatable content in its parent i18n block. We need to 15734 // convert the parent's context into an ICU context. 15735 op.context = currentI18nOp.context; 15736 blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu; 15737 } 15738 break; 15739 } 15740 } 15741 } 15742} 15743 15744/** 15745 * Deduplicate text bindings, e.g. <div class="cls1" class="cls2"> 15746 */ 15747function deduplicateTextBindings(job) { 15748 const seen = new Map(); 15749 for (const unit of job.units) { 15750 for (const op of unit.update.reversed()) { 15751 if (op.kind === OpKind.Binding && op.isTextAttribute) { 15752 const seenForElement = seen.get(op.target) || new Set(); 15753 if (seenForElement.has(op.name)) { 15754 if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) { 15755 // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in 15756 // the consts array. However, for style and class attributes it only keeps the last one. 15757 // We replicate that behavior here since it has actual consequences for apps with 15758 // duplicate class or style attrs. 15759 if (op.name === 'style' || op.name === 'class') { 15760 OpList.remove(op); 15761 } 15762 } else { 15763 // TODO: Determine the correct behavior. It would probably make sense to merge multiple 15764 // style and class attributes. Alternatively we could just throw an error, as HTML 15765 // doesn't permit duplicate attributes. 15766 } 15767 } 15768 seenForElement.add(op.name); 15769 seen.set(op.target, seenForElement); 15770 } 15771 } 15772 } 15773} 15774 15775/** 15776 * Defer instructions take a configuration array, which should be collected into the component 15777 * consts. This phase finds the config options, and creates the corresponding const array. 15778 */
15779function configureDeferInstructions(job) { 15780 for (const unit of job.units) { 15781 for (const op of unit.create) { 15782 if (op.kind !== OpKind.Defer) { 15783 continue; 15784 } 15785 if (op.placeholderMinimumTime !== null) { 15786 op.placeholderConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.placeholderMinimumTime])); 15787 } 15788 if (op.loadingMinimumTime !== null || op.loadingAfterTime !== null) { 15789 op.loadingConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.loadingMinimumTime, op.loadingAfterTime])); 15790 } 15791 } 15792 } 15793} 15794 15795/** 15796 * Some `defer` conditions can reference other elements in the template, using their local reference 15797 * names. However, the semantics are quite different from the normal local reference system: in 15798 * particular, we need to look at local reference names in enclosing views. This phase resolves 15799 * all such references to actual xrefs. 15800 */ 15801function resolveDeferTargetNames(job) { 15802 const scopes = new Map(); 15803 function getScopeForView(view) { 15804 if (scopes.has(view.xref)) { 15805 return scopes.get(view.xref); 15806 } 15807 const scope = new Scope$1(); 15808 for (const op of view.create) { 15809 // add everything that can be referenced. 15810 if (!isElementOrContainerOp(op) || op.localRefs === null) { 15811 continue; 15812 } 15813 if (!Array.isArray(op.localRefs)) { 15814 throw new Error('LocalRefs were already processed, but were needed to resolve defer targets.'); 15815 } 15816 for (const ref of op.localRefs) { 15817 if (ref.target !== '') { 15818 continue; 15819 } 15820 scope.targets.set(ref.name, { 15821 xref: op.xref, 15822 slot: op.handle 15823 }); 15824 } 15825 } 15826 scopes.set(view.xref, scope); 15827 return scope; 15828 } 15829 function resolveTrigger(deferOwnerView, op, placeholderView) { 15830 switch (op.trigger.kind) { 15831 case DeferTriggerKind.Idle: 15832 case DeferTriggerKind.Immediate: 15833 case DeferTriggerKind.Timer: 15834 return; 15835 case DeferTriggerKind.Hover: 15836 case DeferTriggerKind.Interaction: 15837 case DeferTriggerKind.Viewport: 15838 if (op.trigger.targetName === null) { 15839 // A `null` target name indicates we should default to the first element in the 15840 // placeholder block. 15841 if (placeholderView === null) { 15842 throw new Error('defer on trigger with no target name must have a placeholder block'); 15843 } 15844 const placeholder = job.views.get(placeholderView); 15845 if (placeholder == undefined) { 15846 throw new Error('AssertionError: could not find placeholder view for defer on trigger'); 15847 } 15848 for (const placeholderOp of placeholder.create) { 15849 if (hasConsumesSlotTrait(placeholderOp) && (isElementOrContainerOp(placeholderOp) || placeholderOp.kind === OpKind.Projection)) { 15850 op.trigger.targetXref = placeholderOp.xref; 15851 op.trigger.targetView = placeholderView; 15852 op.trigger.targetSlotViewSteps = -1; 15853 op.trigger.targetSlot = placeholderOp.handle; 15854 return; 15855 } 15856 } 15857 return; 15858 } 15859 let view = placeholderView !== null ? job.views.get(placeholderView) : deferOwnerView; 15860 let step = placeholderView !== null ? -1 : 0; 15861 while (view !== null) { 15862 const scope = getScopeForView(view); 15863 if (scope.targets.has(op.trigger.targetName)) { 15864 const { 15865 xref, 15866 slot 15867 } = scope.targets.get(op.trigger.targetName); 15868 op.trigger.targetXref = xref; 15869 op.trigger.targetView = view.xref; 15870 op.trigger.targetSlotViewSteps = step; 15871 op.trigger.targetSlot = slot; 15872 return; 15873 } 15874 view = view.parent !== null ? job.views.get(view.parent) : null; 15875 step++; 15876 } 15877 break; 15878 default: 15879 throw new Error(`Trigger kind ${op.trigger.kind} not handled`); 15880 } 15881 } 15882 // Find the defer ops, and assign the data about their targets. 15883 for (const unit of job.units) { 15884 const defers = new Map(); 15885 for (const op of unit.create) { 15886 switch (op.kind) { 15887 case OpKind.Defer: 15888 defers.set(op.xref, op); 15889 break; 15890 case OpKind.DeferOn: 15891 const deferOp = defers.get(op.defer); 15892 resolveTrigger(unit, op, deferOp.placeholderView); 15893 break; 15894 } 15895 } 15896 } 15897} 15898class Scope$1 { 15899 constructor() { 15900 this.targets = new Map(); 15901 } 15902} 15903const REPLACEMENTS = new Map([[OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]], [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]], [OpKind.I18nEnd, [OpKind.I18nStart, OpKind.I18n]]]); 15904/** 15905 * Op kinds that should not prevent merging of start/end ops. 15906 */ 15907const IGNORED_OP_KINDS = new Set([OpKind.Pipe]); 15908/** 15909 * Replace sequences of mergable instructions (e.g. `ElementStart` and `ElementEnd`) with a 15910 * consolidated instruction (e.g. `Element`). 15911 */ 15912function collapseEmptyInstructions(job) { 15913 for (const unit of job.units) { 15914 for (const op of unit.create) { 15915 // Find end ops that may be able to be merged. 15916 const opReplacements = REPLACEMENTS.get(op.kind); 15917 if (opReplacements === undefined) { 15918 continue; 15919 } 15920 const [startKind, mergedKind] = opReplacements; 15921 // Locate the previous (non-ignored) op. 15922 let prevOp = op.prev; 15923 while (prevOp !== null && IGNORED_OP_KINDS.has(prevOp.kind)) { 15924 prevOp = prevOp.prev; 15925 } 15926 // If the previous op is the corresponding start op, we can megre. 15927 if (prevOp !== null && prevOp.kind === startKind) { 15928 // Transmute the start instruction to the merged version. This is safe as they're designed 15929 // to be identical apart from the `kind`. 15930 prevOp.kind = mergedKind; 15931 // Remove the end instruction. 15932 OpList.remove(op); 15933 } 15934 } 15935 } 15936} 15937 15938/** 15939 * Safe read expressions such as `a?.b` have different semantics in Angular templates as 15940 * compared to JavaScript. In particular, they default to `null` instead of `undefined`. This phase 15941 * finds all unresolved safe read expressions, and converts them into the appropriate output AST 15942 * reads, guarded by null checks. We generate temporaries as needed, to avoid re-evaluating the same 15943 * sub-expression multiple times. 15944 */ 15945function expandSafeReads(job) { 15946 for (const unit of job.units) { 15947 for (const op of unit.ops()) { 15948 transformExpressionsInOp(op, e => safeTransform(e, { 15949 job 15950 }), VisitorContextFlag.None); 15951 transformExpressionsInOp(op, ternaryTransform, VisitorContextFlag.None); 15952 } 15953 } 15954} 15955// A lookup set of all the expression kinds that require a temporary variable to be generated. 15956const requiresTemporary = [InvokeFunctionExpr, LiteralArrayExpr, LiteralMapExpr, SafeInvokeFunctionExpr, PipeBindingExpr].map(e => e.constructor.name); 15957function needsTemporaryInSafeAccess(e) { 15958 // TODO: We probably want to use an expression visitor to recursively visit all descendents. 15959 // However, that would potentially do a lot of extra work (because it cannot short circuit), so we 15960 // implement the logic ourselves for now. 15961 if (e instanceof UnaryOperatorExpr) { 15962 return needsTemporaryInSafeAccess(e.expr); 15963 } else if (e instanceof BinaryOperatorExpr) { 15964 return needsTemporaryInSafeAccess(e.lhs) || needsTemporaryInSafeAccess(e.rhs); 15965 } else if (e instanceof ConditionalExpr) { 15966 if (e.falseCase && needsTemporaryInSafeAccess(e.falseCase)) return true; 15967 return needsTemporaryInSafeAccess(e.condition) || needsTemporaryInSafeAccess(e.trueCase); 15968 } else if (e instanceof NotExpr) { 15969 return needsTemporaryInSafeAccess(e.condition); 15970 } else if (e instanceof AssignTemporaryExpr) { 15971 return needsTemporaryInSafeAccess(e.expr); 15972 } else if (e instanceof ReadPropExpr) { 15973 return needsTemporaryInSafeAccess(e.receiver); 15974 } else if (e instanceof ReadKeyExpr) { 15975 return needsTemporaryInSafeAccess(e.receiver) || needsTemporaryInSafeAccess(e.index); 15976 } 15977 // TODO: Switch to a method which is exhaustive of newly added expression subtypes. 15978 return e instanceof InvokeFunctionExpr || e instanceof LiteralArrayExpr || e instanceof LiteralMapExpr || e instanceof SafeInvokeFunctionExpr || e instanceof PipeBindingExpr; 15979} 15980function temporariesIn(e) { 15981 const temporaries = new Set(); 15982 // TODO: Although it's not currently supported by the transform helper, we should be able to 15983 // short-circuit exploring the tree to do less work. In particular, we don't have to penetrate 15984 // into the subexpressions of temporary assignments. 15985 transformExpressionsInExpression(e, e => { 15986 if (e instanceof AssignTemporaryExpr) { 15987 temporaries.add(e.xref); 15988 } 15989 return e; 15990 }, VisitorContextFlag.None); 15991 return temporaries; 15992} 15993function eliminateTemporaryAssignments(e, tmps, ctx) { 15994 // TODO: We can be more efficient than the transform helper here. We don't need to visit any
15995 // descendents of temporary assignments. 15996 transformExpressionsInExpression(e, e => { 15997 if (e instanceof AssignTemporaryExpr && tmps.has(e.xref)) { 15998 const read = new ReadTemporaryExpr(e.xref); 15999 // `TemplateDefinitionBuilder` has the (accidental?) behavior of generating assignments of 16000 // temporary variables to themselves. This happens because some subexpression that the 16001 // temporary refers to, possibly through nested temporaries, has a function call. We copy that 16002 // behavior here. 16003 return ctx.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder ? new AssignTemporaryExpr(read, read.xref) : read; 16004 } 16005 return e; 16006 }, VisitorContextFlag.None); 16007 return e; 16008} 16009/** 16010 * Creates a safe ternary guarded by the input expression, and with a body generated by the provided 16011 * callback on the input expression. Generates a temporary variable assignment if needed, and 16012 * deduplicates nested temporary assignments if needed. 16013 */ 16014function safeTernaryWithTemporary(guard, body, ctx) { 16015 let result; 16016 if (needsTemporaryInSafeAccess(guard)) { 16017 const xref = ctx.job.allocateXrefId(); 16018 result = [new AssignTemporaryExpr(guard, xref), new ReadTemporaryExpr(xref)]; 16019 } else { 16020 result = [guard, guard.clone()]; 16021 // Consider an expression like `a?.[b?.c()]?.d`. The `b?.c()` will be transformed first, 16022 // introducing a temporary assignment into the key. Then, as part of expanding the `?.d`. That 16023 // assignment will be duplicated into both the guard and expression sides. We de-duplicate it, 16024 // by transforming it from an assignment into a read on the expression side. 16025 eliminateTemporaryAssignments(result[1], temporariesIn(result[0]), ctx); 16026 } 16027 return new SafeTernaryExpr(result[0], body(result[1])); 16028} 16029function isSafeAccessExpression(e) { 16030 return e instanceof SafePropertyReadExpr || e instanceof SafeKeyedReadExpr || e instanceof SafeInvokeFunctionExpr; 16031} 16032function isUnsafeAccessExpression(e) { 16033 return e instanceof ReadPropExpr || e instanceof ReadKeyExpr || e instanceof InvokeFunctionExpr; 16034} 16035function isAccessExpression(e) { 16036 return isSafeAccessExpression(e) || isUnsafeAccessExpression(e); 16037} 16038function deepestSafeTernary(e) { 16039 if (isAccessExpression(e) && e.receiver instanceof SafeTernaryExpr) { 16040 let st = e.receiver; 16041 while (st.expr instanceof SafeTernaryExpr) { 16042 st = st.expr; 16043 } 16044 return st; 16045 } 16046 return null; 16047} 16048// TODO: When strict compatibility with TemplateDefinitionBuilder is not required, we can use `&&` 16049// instead to save some code size. 16050function safeTransform(e, ctx) { 16051 if (!isAccessExpression(e)) { 16052 return e; 16053 } 16054 const dst = deepestSafeTernary(e); 16055 if (dst) { 16056 if (e instanceof InvokeFunctionExpr) { 16057 dst.expr = dst.expr.callFn(e.args); 16058 return e.receiver; 16059 } 16060 if (e instanceof ReadPropExpr) { 16061 dst.expr = dst.expr.prop(e.name); 16062 return e.receiver; 16063 } 16064 if (e instanceof ReadKeyExpr) { 16065 dst.expr = dst.expr.key(e.index); 16066 return e.receiver; 16067 } 16068 if (e instanceof SafeInvokeFunctionExpr) { 16069 dst.expr = safeTernaryWithTemporary(dst.expr, r => r.callFn(e.args), ctx); 16070 return e.receiver; 16071 } 16072 if (e instanceof SafePropertyReadExpr) { 16073 dst.expr = safeTernaryWithTemporary(dst.expr, r => r.prop(e.name), ctx); 16074 return e.receiver; 16075 } 16076 if (e instanceof SafeKeyedReadExpr) { 16077 dst.expr = safeTernaryWithTemporary(dst.expr, r => r.key(e.index), ctx); 16078 return e.receiver; 16079 } 16080 } else { 16081 if (e instanceof SafeInvokeFunctionExpr) { 16082 return safeTernaryWithTemporary(e.receiver, r => r.callFn(e.args), ctx); 16083 } 16084 if (e instanceof SafePropertyReadExpr) { 16085 return safeTernaryWithTemporary(e.receiver, r => r.prop(e.name), ctx); 16086 } 16087 if (e instanceof SafeKeyedReadExpr) { 16088 return safeTernaryWithTemporary(e.receiver, r => r.key(e.index), ctx); 16089 } 16090 } 16091 return e; 16092} 16093function ternaryTransform(e) { 16094 if (!(e instanceof SafeTernaryExpr)) { 16095 return e; 16096 } 16097 return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr); 16098} 16099 16100/** 16101 * The escape sequence used indicate message param values. 16102 */ 16103const ESCAPE$1 = '\uFFFD'; 16104/** 16105 * Marker used to indicate an element tag. 16106 */ 16107const ELEMENT_MARKER = '#'; 16108/** 16109 * Marker used to indicate a template tag. 16110 */ 16111const TEMPLATE_MARKER = '*'; 16112/** 16113 * Marker used to indicate closing of an element or template tag. 16114 */ 16115const TAG_CLOSE_MARKER = '/'; 16116/** 16117 * Marker used to indicate the sub-template context. 16118 */ 16119const CONTEXT_MARKER = ':'; 16120/** 16121 * Marker used to indicate the start of a list of values. 16122 */ 16123const LIST_START_MARKER = '['; 16124/** 16125 * Marker used to indicate the end of a list of values. 16126 */ 16127const LIST_END_MARKER = ']'; 16128/** 16129 * Delimiter used to separate multiple values in a list. 16130 */ 16131const LIST_DELIMITER = '|'; 16132/** 16133 * Formats the param maps on extracted message ops into a maps of `Expression` objects that can be 16134 * used in the final output. 16135 */ 16136function extractI18nMessages(job) { 16137 // Create an i18n message for each context. 16138 // TODO: Merge the context op with the message op since they're 1:1 anyways. 16139 const i18nMessagesByContext = new Map(); 16140 const i18nBlocks = new Map(); 16141 const i18nContexts = new Map(); 16142 for (const unit of job.units) { 16143 for (const op of unit.create) { 16144 switch (op.kind) { 16145 case OpKind.I18nContext: 16146 const i18nMessageOp = createI18nMessage(job, op); 16147 unit.create.push(i18nMessageOp); 16148 i18nMessagesByContext.set(op.xref, i18nMessageOp); 16149 i18nContexts.set(op.xref, op); 16150 break; 16151 case OpKind.I18nStart: 16152 i18nBlocks.set(op.xref, op); 16153 break; 16154 } 16155 } 16156 } 16157 // Associate sub-messages for ICUs with their root message. At this point we can also remove the 16158 // ICU start/end ops, as they are no longer needed. 16159 let currentIcu = null; 16160 for (const unit of job.units) { 16161 for (const op of unit.create) { 16162 switch (op.kind) { 16163 case OpKind.IcuStart: 16164 currentIcu = op; 16165 OpList.remove(op); 16166 // Skip any contexts not associated with an ICU. 16167 const icuContext = i18nContexts.get(op.context); 16168 if (icuContext.contextKind !== I18nContextKind.Icu) { 16169 continue; 16170 } 16171 // Skip ICUs that share a context with their i18n message. These represent root-level 16172 // ICUs, not sub-messages.
16173 const i18nBlock = i18nBlocks.get(icuContext.i18nBlock); 16174 if (i18nBlock.context === icuContext.xref) { 16175 continue; 16176 } 16177 // Find the root message and push this ICUs message as a sub-message. 16178 const rootI18nBlock = i18nBlocks.get(i18nBlock.root); 16179 const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context); 16180 if (rootMessage === undefined) { 16181 throw Error('AssertionError: ICU sub-message should belong to a root message.'); 16182 } 16183 const subMessage = i18nMessagesByContext.get(icuContext.xref); 16184 subMessage.messagePlaceholder = op.messagePlaceholder; 16185 rootMessage.subMessages.push(subMessage.xref); 16186 break; 16187 case OpKind.IcuEnd: 16188 currentIcu = null; 16189 OpList.remove(op); 16190 break; 16191 case OpKind.IcuPlaceholder: 16192 // Add ICU placeholders to the message, then remove the ICU placeholder ops. 16193 if (currentIcu === null || currentIcu.context == null) { 16194 throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context'); 16195 } 16196 const msg = i18nMessagesByContext.get(currentIcu.context); 16197 msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op))); 16198 OpList.remove(op); 16199 break; 16200 } 16201 } 16202 } 16203} 16204/** 16205 * Create an i18n message op from an i18n context op. 16206 */ 16207function createI18nMessage(job, context, messagePlaceholder) { 16208 let formattedParams = formatParams(context.params); 16209 const formattedPostprocessingParams = formatParams(context.postprocessingParams); 16210 let needsPostprocessing = [...context.params.values()].some(v => v.length > 1); 16211 return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing); 16212} 16213/** 16214 * Formats an ICU placeholder into a single string with expression placeholders. 16215 */ 16216function formatIcuPlaceholder(op) { 16217 if (op.strings.length !== op.expressionPlaceholders.length + 1) { 16218 throw Error(`AssertionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`); 16219 } 16220 const values = op.expressionPlaceholders.map(formatValue); 16221 return op.strings.flatMap((str, i) => [str, values[i] || '']).join(''); 16222} 16223/** 16224 * Formats a map of `I18nParamValue[]` values into a map of `Expression` values. 16225 */ 16226function formatParams(params) { 16227 const formattedParams = new Map(); 16228 for (const [placeholder, placeholderValues] of params) { 16229 const serializedValues = formatParamValues(placeholderValues); 16230 if (serializedValues !== null) { 16231 formattedParams.set(placeholder, literal(serializedValues)); 16232 } 16233 } 16234 return formattedParams; 16235} 16236/** 16237 * Formats an `I18nParamValue[]` into a string (or null for empty array). 16238 */ 16239function formatParamValues(values) { 16240 if (values.length === 0) { 16241 return null; 16242 } 16243 const serializedValues = values.map(value => formatValue(value)); 16244 return serializedValues.length === 1 ? serializedValues[0] : `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`; 16245} 16246/** 16247 * Formats a single `I18nParamValue` into a string 16248 */ 16249function formatValue(value) { 16250 // Element tags with a structural directive use a special form that concatenates the element and 16251 // template values. 16252 if (value.flags & I18nParamValueFlags.ElementTag && value.flags & I18nParamValueFlags.TemplateTag) { 16253 if (typeof value.value !== 'object') { 16254 throw Error('AssertionError: Expected i18n param value to have an element and template slot'); 16255 } 16256 const elementValue = formatValue({ 16257 ...value, 16258 value: value.value.element, 16259 flags: value.flags & ~I18nParamValueFlags.TemplateTag 16260 }); 16261 const templateValue = formatValue({ 16262 ...value, 16263 value: value.value.template, 16264 flags: value.flags & ~I18nParamValueFlags.ElementTag 16265 }); 16266 // TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to 16267 // record the template value twice. For now I'm re-implementing the behavior here to keep the 16268 // output consistent with TemplateDefinitionBuilder. 16269 if (value.flags & I18nParamValueFlags.OpenTag && value.flags & I18nParamValueFlags.CloseTag) { 16270 return `${templateValue}${elementValue}${templateValue}`; 16271 } 16272 // To match the TemplateDefinitionBuilder output, flip the order depending on whether the 16273 // values represent a closing or opening tag (or both). 16274 // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality, 16275 // or the resulting message ID. If not, we can remove the special-casing in the future. 16276 return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` : `${templateValue}${elementValue}`; 16277 } 16278 // Self-closing tags use a special form that concatenates the start and close tag values. 16279 if (value.flags & I18nParamValueFlags.OpenTag && value.flags & I18nParamValueFlags.CloseTag) { 16280 return `${formatValue({ 16281 ...value, 16282 flags: value.flags & ~I18nParamValueFlags.CloseTag 16283 })}${formatValue({ 16284 ...value, 16285 flags: value.flags & ~I18nParamValueFlags.OpenTag 16286 })}`; 16287 } 16288 // If there are no special flags, just return the raw value. 16289 if (value.flags === I18nParamValueFlags.None) { 16290 return `${value.value}`; 16291 } 16292 // Encode the remaining flags as part of the value. 16293 let tagMarker = ''; 16294 let closeMarker = ''; 16295 if (value.flags & I18nParamValueFlags.ElementTag) { 16296 tagMarker = ELEMENT_MARKER; 16297 } else if (value.flags & I18nParamValueFlags.TemplateTag) { 16298 tagMarker = TEMPLATE_MARKER; 16299 } 16300 if (tagMarker !== '') { 16301 closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : ''; 16302 }
16303 const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`; 16304 return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`; 16305} 16306 16307/** 16308 * Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot 16309 * context will be advanced correctly. 16310 */ 16311function generateAdvance(job) { 16312 for (const unit of job.units) { 16313 // First build a map of all of the declarations in the view that have assigned slots. 16314 const slotMap = new Map(); 16315 for (const op of unit.create) { 16316 if (!hasConsumesSlotTrait(op)) { 16317 continue; 16318 } else if (op.handle.slot === null) { 16319 throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`); 16320 } 16321 slotMap.set(op.xref, op.handle.slot); 16322 } 16323 // Next, step through the update operations and generate `ir.AdvanceOp`s as required to ensure 16324 // the runtime's implicit slot counter will be set to the correct slot before executing each 16325 // update operation which depends on it. 16326 // 16327 // To do that, we track what the runtime's slot counter will be through the update operations. 16328 let slotContext = 0; 16329 for (const op of unit.update) { 16330 if (!hasDependsOnSlotContextTrait(op)) { 16331 // `op` doesn't depend on the slot counter, so it can be skipped. 16332 continue; 16333 } else if (!slotMap.has(op.target)) { 16334 // We expect ops that _do_ depend on the slot counter to point at declarations that exist in 16335 // the `slotMap`. 16336 throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`); 16337 } 16338 const slot = slotMap.get(op.target); 16339 // Does the slot counter need to be adjusted? 16340 if (slotContext !== slot) { 16341 // If so, generate an `ir.AdvanceOp` to advance the counter. 16342 const delta = slot - slotContext; 16343 if (delta < 0) { 16344 throw new Error(`AssertionError: slot counter should never need to move backwards`); 16345 } 16346 OpList.insertBefore(createAdvanceOp(delta, op.sourceSpan), op); 16347 slotContext = slot; 16348 } 16349 } 16350 } 16351} 16352 16353/** 16354 * Locate projection slots, populate the each component's `ngContentSelectors` literal field, 16355 * populate `project` arguments, and generate the required `projectionDef` instruction for the job's 16356 * root view. 16357 */ 16358function generateProjectionDefs(job) { 16359 // TODO: Why does TemplateDefinitionBuilder force a shared constant? 16360 const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder; 16361 // Collect all selectors from this component, and its nested views. Also, assign each projection a 16362 // unique ascending projection slot index. 16363 const selectors = []; 16364 let projectionSlotIndex = 0; 16365 for (const unit of job.units) { 16366 for (const op of unit.create) { 16367 if (op.kind === OpKind.Projection) { 16368 selectors.push(op.selector); 16369 op.projectionSlotIndex = projectionSlotIndex++; 16370 } 16371 } 16372 } 16373 if (selectors.length > 0) { 16374 // Create the projectionDef array. If we only found a single wildcard selector, then we use the 16375 // default behavior with no arguments instead. 16376 let defExpr = null; 16377 if (selectors.length > 1 || selectors[0] !== '*') { 16378 const def = selectors.map(s => s === '*' ? s : parseSelectorToR3Selector(s)); 16379 defExpr = job.pool.getConstLiteral(literalOrArrayLiteral(def), share); 16380 } 16381 // Create the ngContentSelectors constant. 16382 job.contentSelectors = job.pool.getConstLiteral(literalOrArrayLiteral(selectors), share); 16383 // The projection def instruction goes at the beginning of the root view, before any 16384 // `projection` instructions. 16385 job.root.create.prepend([createProjectionDefOp(defExpr)]); 16386 } 16387} 16388 16389/** 16390 * Generate a preamble sequence for each view creation block and listener function which declares 16391 * any variables that be referenced in other operations in the block. 16392 * 16393 * Variables generated include: 16394 * * a saved view context to be used to restore the current view in event listeners. 16395 * * the context of the restored view within event listener handlers. 16396 * * context variables from the current view as well as all parent views (including the root 16397 * context if needed). 16398 * * local references from elements within the current view and any lexical parents. 16399 * 16400 * Variables are generated here unconditionally, and may optimized away in future operations if it 16401 * turns out their values (and any side effects) are unused. 16402 */ 16403function generateVariables(job) { 16404 recursivelyProcessView(job.root, /* there is no parent scope for the root view */null); 16405} 16406/** 16407 * Process the given `ViewCompilation` and generate preambles for it and any listeners that it 16408 * declares. 16409 * 16410 * @param `parentScope` a scope extracted from the parent view which captures any variables which 16411 * should be inherited by this view. `null` if the current view is the root view. 16412 */ 16413function recursivelyProcessView(view, parentScope) { 16414 // Extract a `Scope` from this view. 16415 const scope = getScopeForView(view, parentScope); 16416 for (const op of view.create) { 16417 switch (op.kind) { 16418 case OpKind.Template: 16419 // Descend into child embedded views. 16420 recursivelyProcessView(view.job.views.get(op.xref), scope); 16421 break; 16422 case OpKind.RepeaterCreate: 16423 // Descend into child embedded views. 16424 recursivelyProcessView(view.job.views.get(op.xref), scope); 16425 if (op.emptyView) { 16426 recursivelyProcessView(view.job.views.get(op.emptyView), scope); 16427 } 16428 break; 16429 case OpKind.Listener: 16430 case OpKind.TwoWayListener: 16431 // Prepend variables to listener handler functions. 16432 op.handlerOps.prepend(generateVariablesInScopeForView(view, scope)); 16433 break; 16434 } 16435 } 16436 // Prepend the declarations for all available variables in scope to the `update` block. 16437 const preambleOps = generateVariablesInScopeForView(view, scope); 16438 view.update.prepend(preambleOps); 16439} 16440/** 16441 * Process a view and generate a `Scope` representing the variables available for reference within 16442 * that view. 16443 */ 16444function getScopeForView(view, parent) { 16445 const scope = { 16446 view: view.xref, 16447 viewContextVariable: { 16448 kind: SemanticVariableKind.Context, 16449 name: null, 16450 view: view.xref 16451 }, 16452 contextVariables: new Map(),
16453 aliases: view.aliases, 16454 references: [], 16455 parent 16456 }; 16457 for (const identifier of view.contextVariables.keys()) { 16458 scope.contextVariables.set(identifier, { 16459 kind: SemanticVariableKind.Identifier, 16460 name: null, 16461 identifier 16462 }); 16463 } 16464 for (const op of view.create) { 16465 switch (op.kind) { 16466 case OpKind.ElementStart: 16467 case OpKind.Template: 16468 if (!Array.isArray(op.localRefs)) { 16469 throw new Error(`AssertionError: expected localRefs to be an array`); 16470 } 16471 // Record available local references from this element. 16472 for (let offset = 0; offset < op.localRefs.length; offset++) { 16473 scope.references.push({ 16474 name: op.localRefs[offset].name, 16475 targetId: op.xref, 16476 targetSlot: op.handle, 16477 offset, 16478 variable: { 16479 kind: SemanticVariableKind.Identifier, 16480 name: null, 16481 identifier: op.localRefs[offset].name 16482 } 16483 }); 16484 } 16485 break; 16486 } 16487 } 16488 return scope; 16489} 16490/** 16491 * Generate declarations for all variables that are in scope for a given view. 16492 * 16493 * This is a recursive process, as views inherit variables available from their parent view, which 16494 * itself may have inherited variables, etc. 16495 */ 16496function generateVariablesInScopeForView(view, scope) { 16497 const newOps = []; 16498 if (scope.view !== view.xref) { 16499 // Before generating variables for a parent view, we need to switch to the context of the parent 16500 // view with a `nextContext` expression. This context switching operation itself declares a 16501 // variable, because the context of the view may be referenced directly. 16502 newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr(), VariableFlags.None)); 16503 }
16504 // Add variables for all context variables available in this scope's view. 16505 const scopeView = view.job.views.get(scope.view); 16506 for (const [name, value] of scopeView.contextVariables) { 16507 const context = new ContextExpr(scope.view); 16508 // We either read the context, or, if the variable is CTX_REF, use the context directly. 16509 const variable = value === CTX_REF ? context : new ReadPropExpr(context, value); 16510 // Add the variable declaration. 16511 newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable, VariableFlags.None)); 16512 } 16513 for (const alias of scopeView.aliases) { 16514 newOps.push(createVariableOp(view.job.allocateXrefId(), alias, alias.expression.clone(), VariableFlags.AlwaysInline)); 16515 } 16516 // Add variables for all local references declared for elements in this scope. 16517 for (const ref of scope.references) { 16518 newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.targetSlot, ref.offset), VariableFlags.None)); 16519 } 16520 if (scope.parent !== null) { 16521 // Recursively add variables from the parent scope. 16522 newOps.push(...generateVariablesInScopeForView(view, scope.parent)); 16523 } 16524 return newOps; 16525} 16526 16527/** 16528 * `ir.ConstCollectedExpr` may be present in any IR expression. This means that expression needs to 16529 * be lifted into the component const array, and replaced with a reference to the const array at its 16530 * 16531 * usage site. This phase walks the IR and performs this transformation. 16532 */ 16533function collectConstExpressions(job) { 16534 for (const unit of job.units) { 16535 for (const op of unit.ops()) { 16536 transformExpressionsInOp(op, expr => { 16537 if (!(expr instanceof ConstCollectedExpr)) { 16538 return expr; 16539 } 16540 return literal(job.addConst(expr.expr)); 16541 }, VisitorContextFlag.None); 16542 } 16543 } 16544} 16545const STYLE_DOT = 'style.'; 16546const CLASS_DOT = 'class.'; 16547const STYLE_BANG = 'style!'; 16548const CLASS_BANG = 'class!'; 16549const BANG_IMPORTANT = '!important'; 16550/** 16551 * Host bindings are compiled using a different parser entrypoint, and are parsed quite differently 16552 * as a result. Therefore, we need to do some extra parsing for host style properties, as compared 16553 * to non-host style properties. 16554 * TODO: Unify host bindings and non-host bindings in the parser. 16555 */ 16556function parseHostStyleProperties(job) { 16557 for (const op of job.root.update) { 16558 if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) { 16559 continue; 16560 } 16561 if (op.name.endsWith(BANG_IMPORTANT)) { 16562 // Delete any `!important` suffixes from the binding name. 16563 op.name = op.name.substring(0, op.name.length - BANG_IMPORTANT.length); 16564 } 16565 if (op.name.startsWith(STYLE_DOT)) { 16566 op.bindingKind = BindingKind.StyleProperty; 16567 op.name = op.name.substring(STYLE_DOT.length); 16568 if (!isCssCustomProperty$1(op.name)) { 16569 op.name = hyphenate$1(op.name); 16570 } 16571 const { 16572 property, 16573 suffix 16574 } = parseProperty$1(op.name); 16575 op.name = property; 16576 op.unit = suffix; 16577 } else if (op.name.startsWith(STYLE_BANG)) { 16578 op.bindingKind = BindingKind.StyleProperty; 16579 op.name = 'style'; 16580 } else if (op.name.startsWith(CLASS_DOT)) { 16581 op.bindingKind = BindingKind.ClassName; 16582 op.name = parseProperty$1(op.name.substring(CLASS_DOT.length)).property; 16583 } else if (op.name.startsWith(CLASS_BANG)) { 16584 op.bindingKind = BindingKind.ClassName; 16585 op.name = parseProperty$1(op.name.substring(CLASS_BANG.length)).property; 16586 } 16587 } 16588} 16589/** 16590 * Checks whether property name is a custom CSS property. 16591 * See: https://www.w3.org/TR/css-variables-1 16592 */ 16593function isCssCustomProperty$1(name) { 16594 return name.startsWith('--'); 16595} 16596function hyphenate$1(value) { 16597 return value.replace(/[a-z][A-Z]/g, v => { 16598 return v.charAt(0) + '-' + v.charAt(1); 16599 }).toLowerCase(); 16600} 16601function parseProperty$1(name) { 16602 const overrideIndex = name.indexOf('!important'); 16603 if (overrideIndex !== -1) { 16604 name = overrideIndex > 0 ? name.substring(0, overrideIndex) : ''; 16605 } 16606 let suffix = null; 16607 let property = name; 16608 const unitIndex = name.lastIndexOf('.'); 16609 if (unitIndex > 0) { 16610 suffix = name.slice(unitIndex + 1); 16611 property = name.substring(0, unitIndex); 16612 } 16613 return { 16614 property, 16615 suffix 16616 }; 16617} 16618function mapEntry(key, value) { 16619 return { 16620 key, 16621 value, 16622 quoted: false 16623 }; 16624} 16625function mapLiteral(obj, quoted = false) { 16626 return literalMap(Object.keys(obj).map(key => ({ 16627 key, 16628 quoted,
16629 value: obj[key] 16630 }))); 16631} 16632class IcuSerializerVisitor { 16633 visitText(text) { 16634 return text.value; 16635 } 16636 visitContainer(container) { 16637 return container.children.map(child => child.visit(this)).join(''); 16638 } 16639 visitIcu(icu) { 16640 const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 16641 const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`; 16642 return result; 16643 } 16644 visitTagPlaceholder(ph) { 16645 return ph.isVoid ? this.formatPh(ph.startName) : `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; 16646 } 16647 visitPlaceholder(ph) { 16648 return this.formatPh(ph.name); 16649 } 16650 visitBlockPlaceholder(ph) { 16651 return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; 16652 } 16653 visitIcuPlaceholder(ph, context) { 16654 return this.formatPh(ph.name); 16655 } 16656 formatPh(value) { 16657 return `{${formatI18nPlaceholderName(value, /* useCamelCase */false)}}`; 16658 } 16659} 16660const serializer = new IcuSerializerVisitor(); 16661function serializeIcuNode(icu) { 16662 return icu.visit(serializer); 16663} 16664var TokenType; 16665(function (TokenType) { 16666 TokenType[TokenType["Character"] = 0] = "Character"; 16667 TokenType[TokenType["Identifier"] = 1] = "Identifier"; 16668 TokenType[TokenType["PrivateIdentifier"] = 2] = "PrivateIdentifier"; 16669 TokenType[TokenType["Keyword"] = 3] = "Keyword"; 16670 TokenType[TokenType["String"] = 4] = "String"; 16671 TokenType[TokenType["Operator"] = 5] = "Operator"; 16672 TokenType[TokenType["Number"] = 6] = "Number"; 16673 TokenType[TokenType["Error"] = 7] = "Error"; 16674})(TokenType || (TokenType = {})); 16675const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this']; 16676class Lexer { 16677 tokenize(text) { 16678 const scanner = new _Scanner(text); 16679 const tokens = []; 16680 let token = scanner.scanToken(); 16681 while (token != null) { 16682 tokens.push(token); 16683 token = scanner.scanToken(); 16684 } 16685 return tokens; 16686 } 16687} 16688class Token { 16689 constructor(index, end, type, numValue, strValue) { 16690 this.index = index; 16691 this.end = end; 16692 this.type = type; 16693 this.numValue = numValue; 16694 this.strValue = strValue; 16695 } 16696 isCharacter(code) { 16697 return this.type == TokenType.Character && this.numValue == code; 16698 } 16699 isNumber() { 16700 return this.type == TokenType.Number; 16701 } 16702 isString() { 16703 return this.type == TokenType.String; 16704 } 16705 isOperator(operator) { 16706 return this.type == TokenType.Operator && this.strValue == operator; 16707 } 16708 isIdentifier() { 16709 return this.type == TokenType.Identifier; 16710 } 16711 isPrivateIdentifier() { 16712 return this.type == TokenType.PrivateIdentifier; 16713 } 16714 isKeyword() { 16715 return this.type == TokenType.Keyword; 16716 } 16717 isKeywordLet() { 16718 return this.type == TokenType.Keyword && this.strValue == 'let'; 16719 } 16720 isKeywordAs() { 16721 return this.type == TokenType.Keyword && this.strValue == 'as'; 16722 } 16723 isKeywordNull() { 16724 return this.type == TokenType.Keyword && this.strValue == 'null'; 16725 } 16726 isKeywordUndefined() { 16727 return this.type == TokenType.Keyword && this.strValue == 'undefined'; 16728 } 16729 isKeywordTrue() { 16730 return this.type == TokenType.Keyword && this.strValue == 'true'; 16731 } 16732 isKeywordFalse() { 16733 return this.type == TokenType.Keyword && this.strValue == 'false'; 16734 } 16735 isKeywordThis() { 16736 return this.type == TokenType.Keyword && this.strValue == 'this'; 16737 } 16738 isError() { 16739 return this.type == TokenType.Error; 16740 } 16741 toNumber() { 16742 return this.type == TokenType.Number ? this.numValue : -1; 16743 } 16744 toString() { 16745 switch (this.type) { 16746 case TokenType.Character: 16747 case TokenType.Identifier: 16748 case TokenType.Keyword: 16749 case TokenType.Operator: 16750 case TokenType.PrivateIdentifier: 16751 case TokenType.String: 16752 case TokenType.Error: 16753 return this.strValue; 16754 case TokenType.Number: 16755 return this.numValue.toString(); 16756 default: 16757 return null; 16758 } 16759 } 16760} 16761function newCharacterToken(index, end, code) { 16762 return new Token(index, end, TokenType.Character, code, String.fromCharCode(code)); 16763} 16764function newIdentifierToken(index, end, text) { 16765 return new Token(index, end, TokenType.Identifier, 0, text); 16766} 16767function newPrivateIdentifierToken(index, end, text) { 16768 return new Token(index, end, TokenType.PrivateIdentifier, 0, text); 16769} 16770function newKeywordToken(index, end, text) { 16771 return new Token(index, end, TokenType.Keyword, 0, text); 16772} 16773function newOperatorToken(index, end, text) { 16774 return new Token(index, end, TokenType.Operator, 0, text); 16775} 16776function newStringToken(index, end, text) { 16777 return new Token(index, end, TokenType.String, 0, text); 16778} 16779function newNumberToken(index, end, n) { 16780 return new Token(index, end, TokenType.Number, n, ''); 16781} 16782function newErrorToken(index, end, message) { 16783 return new Token(index, end, TokenType.Error, 0, message); 16784} 16785const EOF = new Token(-1, -1, TokenType.Character, 0, ''); 16786class _Scanner { 16787 constructor(input) { 16788 this.input = input; 16789 this.peek = 0; 16790 this.index = -1; 16791 this.length = input.length; 16792 this.advance(); 16793 } 16794 advance() { 16795 this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index); 16796 } 16797 scanToken() { 16798 const input = this.input, 16799 length = this.length; 16800 let peek = this.peek, 16801 index = this.index; 16802 // Skip whitespace. 16803 while (peek <= $SPACE) { 16804 if (++index >= length) { 16805 peek = $EOF; 16806 break; 16807 } else { 16808 peek = input.charCodeAt(index); 16809 } 16810 } 16811 this.peek = peek; 16812 this.index = index; 16813 if (index >= length) { 16814 return null; 16815 } 16816 // Handle identifiers and numbers. 16817 if (isIdentifierStart(peek)) return this.scanIdentifier(); 16818 if (isDigit(peek)) return this.scanNumber(index); 16819 const start = index; 16820 switch (peek) { 16821 case $PERIOD: 16822 this.advance(); 16823 return isDigit(this.peek) ? this.scanNumber(start) : newCharacterToken(start, this.index, $PERIOD); 16824 case $LPAREN: 16825 case $RPAREN: 16826 case $LBRACE: 16827 case $RBRACE: 16828 case $LBRACKET: 16829 case $RBRACKET: 16830 case $COMMA: 16831 case $COLON: 16832 case $SEMICOLON: 16833 return this.scanCharacter(start, peek); 16834 case $SQ: 16835 case $DQ: 16836 return this.scanString(); 16837 case $HASH: 16838 return this.scanPrivateIdentifier(); 16839 case $PLUS: 16840 case $MINUS: 16841 case $STAR: 16842 case $SLASH: 16843 case $PERCENT: 16844 case $CARET:
16845 return this.scanOperator(start, String.fromCharCode(peek)); 16846 case $QUESTION: 16847 return this.scanQuestion(start); 16848 case $LT: 16849 case $GT: 16850 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '='); 16851 case $BANG: 16852 case $EQ: 16853 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '='); 16854 case $AMPERSAND: 16855 return this.scanComplexOperator(start, '&', $AMPERSAND, '&'); 16856 case $BAR: 16857 return this.scanComplexOperator(start, '|', $BAR, '|'); 16858 case $NBSP: 16859 while (isWhitespace(this.peek)) this.advance(); 16860 return this.scanToken(); 16861 } 16862 this.advance(); 16863 return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0); 16864 } 16865 scanCharacter(start, code) { 16866 this.advance(); 16867 return newCharacterToken(start, this.index, code); 16868 } 16869 scanOperator(start, str) { 16870 this.advance(); 16871 return newOperatorToken(start, this.index, str); 16872 } 16873 /** 16874 * Tokenize a 2/3 char long operator 16875 * 16876 * @param start start index in the expression 16877 * @param one first symbol (always part of the operator) 16878 * @param twoCode code point for the second symbol 16879 * @param two second symbol (part of the operator when the second code point matches) 16880 * @param threeCode code point for the third symbol 16881 * @param three third symbol (part of the operator when provided and matches source expression) 16882 */ 16883 scanComplexOperator(start, one, twoCode, two, threeCode, three) { 16884 this.advance(); 16885 let str = one; 16886 if (this.peek == twoCode) { 16887 this.advance(); 16888 str += two; 16889 } 16890 if (threeCode != null && this.peek == threeCode) { 16891 this.advance(); 16892 str += three; 16893 } 16894 return newOperatorToken(start, this.index, str); 16895 } 16896 scanIdentifier() { 16897 const start = this.index; 16898 this.advance(); 16899 while (isIdentifierPart(this.peek)) this.advance(); 16900 const str = this.input.substring(start, this.index); 16901 return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) : newIdentifierToken(start, this.index, str); 16902 } 16903 /** Scans an ECMAScript private identifier. */ 16904 scanPrivateIdentifier() { 16905 const start = this.index; 16906 this.advance(); 16907 if (!isIdentifierStart(this.peek)) { 16908 return this.error('Invalid character [#]', -1); 16909 } 16910 while (isIdentifierPart(this.peek)) this.advance(); 16911 const identifierName = this.input.substring(start, this.index); 16912 return newPrivateIdentifierToken(start, this.index, identifierName); 16913 } 16914 scanNumber(start) { 16915 let simple = this.index === start; 16916 let hasSeparators = false; 16917 this.advance(); // Skip initial digit. 16918 while (true) { 16919 if (isDigit(this.peek)) { 16920 // Do nothing. 16921 } else if (this.peek === $_) { 16922 // Separators are only valid when they're surrounded by digits. E.g. `1_0_1` is 16923 // valid while `_101` and `101_` are not. The separator can't be next to the decimal 16924 // point or another separator either. Note that it's unlikely that we'll hit a case where 16925 // the underscore is at the start, because that's a valid identifier and it will be picked 16926 // up earlier in the parsing. We validate for it anyway just in case. 16927 if (!isDigit(this.input.charCodeAt(this.index - 1)) || !isDigit(this.input.charCodeAt(this.index + 1))) { 16928 return this.error('Invalid numeric separator', 0); 16929 } 16930 hasSeparators = true; 16931 } else if (this.peek === $PERIOD) { 16932 simple = false; 16933 } else if (isExponentStart(this.peek)) { 16934 this.advance(); 16935 if (isExponentSign(this.peek)) this.advance(); 16936 if (!isDigit(this.peek)) return this.error('Invalid exponent', -1); 16937 simple = false; 16938 } else { 16939 break; 16940 } 16941 this.advance(); 16942 } 16943 let str = this.input.substring(start, this.index); 16944 if (hasSeparators) { 16945 str = str.replace(/_/g, ''); 16946 } 16947 const value = simple ? parseIntAutoRadix(str) : parseFloat(str); 16948 return newNumberToken(start, this.index, value); 16949 } 16950 scanString() { 16951 const start = this.index; 16952 const quote = this.peek; 16953 this.advance(); // Skip initial quote. 16954 let buffer = ''; 16955 let marker = this.index; 16956 const input = this.input; 16957 while (this.peek != quote) { 16958 if (this.peek == $BACKSLASH) { 16959 buffer += input.substring(marker, this.index); 16960 let unescapedCode; 16961 this.advance(); // mutates this.peek 16962 // @ts-expect-error see microsoft/TypeScript#9998 16963 if (this.peek == $u) { 16964 // 4 character hex code for unicode character. 16965 const hex = input.substring(this.index + 1, this.index + 5); 16966 if (/^[0-9a-f]+$/i.test(hex)) { 16967 unescapedCode = parseInt(hex, 16); 16968 } else { 16969 return this.error(`Invalid unicode escape [\\u${hex}]`, 0); 16970 } 16971 for (let i = 0; i < 5; i++) { 16972 this.advance(); 16973 } 16974 } else { 16975 unescapedCode = unescape(this.peek); 16976 this.advance(); 16977 } 16978 buffer += String.fromCharCode(unescapedCode); 16979 marker = this.index; 16980 } else if (this.peek == $EOF) {
16981 return this.error('Unterminated quote', 0); 16982 } else { 16983 this.advance(); 16984 } 16985 } 16986 const last = input.substring(marker, this.index); 16987 this.advance(); // Skip terminating quote. 16988 return newStringToken(start, this.index, buffer + last); 16989 } 16990 scanQuestion(start) { 16991 this.advance(); 16992 let str = '?'; 16993 // Either `a ?? b` or 'a?.b'. 16994 if (this.peek === $QUESTION || this.peek === $PERIOD) { 16995 str += this.peek === $PERIOD ? '.' : '?'; 16996 this.advance(); 16997 } 16998 return newOperatorToken(start, this.index, str); 16999 } 17000 error(message, offset) { 17001 const position = this.index + offset; 17002 return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`); 17003 } 17004} 17005function isIdentifierStart(code) { 17006 return $a <= code && code <= $z || $A <= code && code <= $Z || code == $_ || code == $$; 17007} 17008function isIdentifier(input) { 17009 if (input.length == 0) return false; 17010 const scanner = new _Scanner(input); 17011 if (!isIdentifierStart(scanner.peek)) return false; 17012 scanner.advance(); 17013 while (scanner.peek !== $EOF) { 17014 if (!isIdentifierPart(scanner.peek)) return false; 17015 scanner.advance(); 17016 } 17017 return true; 17018} 17019function isIdentifierPart(code) { 17020 return isAsciiLetter(code) || isDigit(code) || code == $_ || code == $$; 17021} 17022function isExponentStart(code) { 17023 return code == $e || code == $E; 17024} 17025function isExponentSign(code) { 17026 return code == $MINUS || code == $PLUS; 17027} 17028function unescape(code) { 17029 switch (code) { 17030 case $n: 17031 return $LF; 17032 case $f: 17033 return $FF; 17034 case $r: 17035 return $CR; 17036 case $t: 17037 return $TAB; 17038 case $v: 17039 return $VTAB; 17040 default: 17041 return code; 17042 } 17043} 17044function parseIntAutoRadix(text) { 17045 const result = parseInt(text); 17046 if (isNaN(result)) { 17047 throw new Error('Invalid integer literal when parsing ' + text); 17048 } 17049 return result; 17050} 17051class SplitInterpolation { 17052 constructor(strings, expressions, offsets) { 17053 this.strings = strings; 17054 this.expressions = expressions; 17055 this.offsets = offsets; 17056 } 17057} 17058class TemplateBindingParseResult { 17059 constructor(templateBindings, warnings, errors) { 17060 this.templateBindings = templateBindings; 17061 this.warnings = warnings; 17062 this.errors = errors; 17063 } 17064} 17065class Parser$1 { 17066 constructor(_lexer) { 17067 this._lexer = _lexer; 17068 this.errors = []; 17069 } 17070 parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 17071 this._checkNoInterpolation(input, location, interpolationConfig); 17072 const sourceToLex = this._stripComments(input); 17073 const tokens = this._lexer.tokenize(sourceToLex); 17074 const ast = new _ParseAST(input, location, absoluteOffset, tokens, 1 /* ParseFlags.Action */, this.errors, 0).parseChain(); 17075 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors); 17076 } 17077 parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 17078 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig); 17079 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors); 17080 } 17081 checkSimpleExpression(ast) { 17082 const checker = new SimpleExpressionChecker(); 17083 ast.visit(checker); 17084 return checker.errors; 17085 } 17086 // Host bindings parsed here 17087 parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 17088 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig); 17089 const errors = this.checkSimpleExpression(ast); 17090 if (errors.length > 0) { 17091 this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location); 17092 } 17093 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors); 17094 } 17095 _reportError(message, input, errLocation, ctxLocation) { 17096 this.errors.push(new ParserError(message, input, errLocation, ctxLocation)); 17097 } 17098 _parseBindingAst(input, location, absoluteOffset, interpolationConfig) { 17099 this._checkNoInterpolation(input, location, interpolationConfig); 17100 const sourceToLex = this._stripComments(input); 17101 const tokens = this._lexer.tokenize(sourceToLex); 17102 return new _ParseAST(input, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0).parseChain(); 17103 } 17104 /** 17105 * Parse microsyntax template expression and return a list of bindings or
17106 * parsing errors in case the given expression is invalid. 17107 * 17108 * For example, 17109 * ``` 17110 * <div *ngFor="let item of items"> 17111 * ^ ^ absoluteValueOffset for `templateValue` 17112 * absoluteKeyOffset for `templateKey` 17113 * ``` 17114 * contains three bindings: 17115 * 1. ngFor -> null 17116 * 2. item -> NgForOfContext.$implicit 17117 * 3. ngForOf -> items 17118 * 17119 * This is apparent from the de-sugared template: 17120 * ``` 17121 * <ng-template ngFor let-item [ngForOf]="items"> 17122 * ``` 17123 * 17124 * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor 17125 * @param templateValue RHS of the microsyntax attribute 17126 * @param templateUrl template filename if it's external, component filename if it's inline 17127 * @param absoluteKeyOffset start of the `templateKey` 17128 * @param absoluteValueOffset start of the `templateValue` 17129 */ 17130 parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) { 17131 const tokens = this._lexer.tokenize(templateValue); 17132 const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0 /* relative offset */); 17133 return parser.parseTemplateBindings({ 17134 source: templateKey, 17135 span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length) 17136 }); 17137 } 17138 parseInterpolation(input, location, absoluteOffset, interpolatedTokens, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 17139 const { 17140 strings, 17141 expressions, 17142 offsets 17143 } = this.splitInterpolation(input, location, interpolatedTokens, interpolationConfig); 17144 if (expressions.length === 0) return null; 17145 const expressionNodes = []; 17146 for (let i = 0; i < expressions.length; ++i) { 17147 const expressionText = expressions[i].text; 17148 const sourceToLex = this._stripComments(expressionText); 17149 const tokens = this._lexer.tokenize(sourceToLex); 17150 const ast = new _ParseAST(input, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, offsets[i]).parseChain(); 17151 expressionNodes.push(ast); 17152 } 17153 return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset); 17154 } 17155 /** 17156 * Similar to `parseInterpolation`, but treats the provided string as a single expression 17157 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`). 17158 * This is used for parsing the switch expression in ICUs. 17159 */ 17160 parseInterpolationExpression(expression, location, absoluteOffset) { 17161 const sourceToLex = this._stripComments(expression); 17162 const tokens = this._lexer.tokenize(sourceToLex); 17163 const ast = new _ParseAST(expression, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0).parseChain(); 17164 const strings = ['', '']; // The prefix and suffix strings are both empty 17165 return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset); 17166 } 17167 createInterpolationAst(strings, expressions, input, location, absoluteOffset) { 17168 const span = new ParseSpan(0, input.length); 17169 const interpolation = new Interpolation$1(span, span.toAbsolute(absoluteOffset), strings, expressions); 17170 return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors); 17171 } 17172 /** 17173 * Splits a string of text into "raw" text segments and expressions present in interpolations in 17174 * the string. 17175 * Returns `null` if there are no interpolations, otherwise a 17176 * `SplitInterpolation` with splits that look like 17177 * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text> 17178 */ 17179 splitInterpolation(input, location, interpolatedTokens, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 17180 const strings = []; 17181 const expressions = []; 17182 const offsets = []; 17183 const inputToTemplateIndexMap = interpolatedTokens ? getIndexMapForOriginalTemplate(interpolatedTokens) : null; 17184 let i = 0; 17185 let atInterpolation = false; 17186 let extendLastString = false; 17187 let { 17188 start: interpStart, 17189 end: interpEnd 17190 } = interpolationConfig; 17191 while (i < input.length) { 17192 if (!atInterpolation) { 17193 // parse until starting {{ 17194 const start = i; 17195 i = input.indexOf(interpStart, i); 17196 if (i === -1) { 17197 i = input.length; 17198 } 17199 const text = input.substring(start, i);
17200 strings.push({ 17201 text, 17202 start, 17203 end: i 17204 }); 17205 atInterpolation = true; 17206 } else { 17207 // parse from starting {{ to ending }} while ignoring content inside quotes. 17208 const fullStart = i; 17209 const exprStart = fullStart + interpStart.length; 17210 const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart); 17211 if (exprEnd === -1) { 17212 // Could not find the end of the interpolation; do not parse an expression. 17213 // Instead we should extend the content on the last raw string. 17214 atInterpolation = false; 17215 extendLastString = true; 17216 break; 17217 } 17218 const fullEnd = exprEnd + interpEnd.length; 17219 const text = input.substring(exprStart, exprEnd); 17220 if (text.trim().length === 0) { 17221 this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location); 17222 } 17223 expressions.push({ 17224 text, 17225 start: fullStart, 17226 end: fullEnd 17227 }); 17228 const startInOriginalTemplate = inputToTemplateIndexMap?.get(fullStart) ?? fullStart; 17229 const offset = startInOriginalTemplate + interpStart.length; 17230 offsets.push(offset); 17231 i = fullEnd; 17232 atInterpolation = false; 17233 } 17234 } 17235 if (!atInterpolation) { 17236 // If we are now at a text section, add the remaining content as a raw string. 17237 if (extendLastString) { 17238 const piece = strings[strings.length - 1]; 17239 piece.text += input.substring(i); 17240 piece.end = input.length; 17241 } else { 17242 strings.push({ 17243 text: input.substring(i), 17244 start: i, 17245 end: input.length 17246 }); 17247 } 17248 } 17249 return new SplitInterpolation(strings, expressions, offsets); 17250 } 17251 wrapLiteralPrimitive(input, location, absoluteOffset) { 17252 const span = new ParseSpan(0, input == null ? 0 : input.length); 17253 return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors); 17254 } 17255 _stripComments(input) { 17256 const i = this._commentStart(input); 17257 return i != null ? input.substring(0, i) : input; 17258 } 17259 _commentStart(input) { 17260 let outerQuote = null; 17261 for (let i = 0; i < input.length - 1; i++) { 17262 const char = input.charCodeAt(i); 17263 const nextChar = input.charCodeAt(i + 1); 17264 if (char === $SLASH && nextChar == $SLASH && outerQuote == null) return i; 17265 if (outerQuote === char) { 17266 outerQuote = null; 17267 } else if (outerQuote == null && isQuote(char)) { 17268 outerQuote = char; 17269 } 17270 } 17271 return null; 17272 } 17273 _checkNoInterpolation(input, location, { 17274 start, 17275 end 17276 }) { 17277 let startIndex = -1; 17278 let endIndex = -1; 17279 for (const charIndex of this._forEachUnquotedChar(input, 0)) { 17280 if (startIndex === -1) { 17281 if (input.startsWith(start)) { 17282 startIndex = charIndex; 17283 } 17284 } else { 17285 endIndex = this._getInterpolationEndIndex(input, end, charIndex); 17286 if (endIndex > -1) { 17287 break; 17288 } 17289 } 17290 } 17291 if (startIndex > -1 && endIndex > -1) { 17292 this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location); 17293 } 17294 } 17295 /** 17296 * Finds the index of the end of an interpolation expression 17297 * while ignoring comments and quoted content. 17298 */ 17299 _getInterpolationEndIndex(input, expressionEnd, start) { 17300 for (const charIndex of this._forEachUnquotedChar(input, start)) { 17301 if (input.startsWith(expressionEnd, charIndex)) { 17302 return charIndex; 17303 } 17304 // Nothing else in the expression matters after we've 17305 // hit a comment so look directly for the end token. 17306 if (input.startsWith('//', charIndex)) { 17307 return input.indexOf(expressionEnd, charIndex); 17308 } 17309 } 17310 return -1; 17311 } 17312 /** 17313 * Generator used to iterate over the character indexes of a string that are outside of quotes. 17314 * @param input String to loop through. 17315 * @param start Index within the string at which to start. 17316 */ 17317 *_forEachUnquotedChar(input, start) {
17318 let currentQuote = null; 17319 let escapeCount = 0; 17320 for (let i = start; i < input.length; i++) { 17321 const char = input[i]; 17322 // Skip the characters inside quotes. Note that we only care about the outer-most 17323 // quotes matching up and we need to account for escape characters. 17324 if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) && escapeCount % 2 === 0) { 17325 currentQuote = currentQuote === null ? char : null; 17326 } else if (currentQuote === null) { 17327 yield i; 17328 } 17329 escapeCount = char === '\\' ? escapeCount + 1 : 0; 17330 } 17331 } 17332} 17333/** Describes a stateful context an expression parser is in. */ 17334var ParseContextFlags; 17335(function (ParseContextFlags) { 17336 ParseContextFlags[ParseContextFlags["None"] = 0] = "None"; 17337 /** 17338 * A Writable context is one in which a value may be written to an lvalue. 17339 * For example, after we see a property access, we may expect a write to the 17340 * property via the "=" operator. 17341 * prop 17342 * ^ possible "=" after 17343 */ 17344 ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable"; 17345})(ParseContextFlags || (ParseContextFlags = {})); 17346class _ParseAST { 17347 constructor(input, location, absoluteOffset, tokens, parseFlags, errors, offset) { 17348 this.input = input; 17349 this.location = location; 17350 this.absoluteOffset = absoluteOffset; 17351 this.tokens = tokens; 17352 this.parseFlags = parseFlags; 17353 this.errors = errors; 17354 this.offset = offset; 17355 this.rparensExpected = 0; 17356 this.rbracketsExpected = 0; 17357 this.rbracesExpected = 0; 17358 this.context = ParseContextFlags.None; 17359 // Cache of expression start and input indeces to the absolute source span they map to, used to 17360 // prevent creating superfluous source spans in `sourceSpan`. 17361 // A serial of the expression start and input index is used for mapping because both are stateful 17362 // and may change for subsequent expressions visited by the parser. 17363 this.sourceSpanCache = new Map(); 17364 this.index = 0; 17365 } 17366 peek(offset) { 17367 const i = this.index + offset; 17368 return i < this.tokens.length ? this.tokens[i] : EOF; 17369 } 17370 get next() { 17371 return this.peek(0); 17372 } 17373 /** Whether all the parser input has been processed. */ 17374 get atEOF() { 17375 return this.index >= this.tokens.length; 17376 } 17377 /** 17378 * Index of the next token to be processed, or the end of the last token if all have been 17379 * processed. 17380 */ 17381 get inputIndex() { 17382 return this.atEOF ? this.currentEndIndex : this.next.index + this.offset; 17383 } 17384 /** 17385 * End index of the last processed token, or the start of the first token if none have been 17386 * processed. 17387 */ 17388 get currentEndIndex() { 17389 if (this.index > 0) { 17390 const curToken = this.peek(-1); 17391 return curToken.end + this.offset; 17392 } 17393 // No tokens have been processed yet; return the next token's start or the length of the input 17394 // if there is no token. 17395 if (this.tokens.length === 0) { 17396 return this.input.length + this.offset; 17397 } 17398 return this.next.index + this.offset; 17399 } 17400 /** 17401 * Returns the absolute offset of the start of the current token. 17402 */ 17403 get currentAbsoluteOffset() { 17404 return this.absoluteOffset + this.inputIndex; 17405 } 17406 /** 17407 * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if 17408 * provided). 17409 * 17410 * @param start Position from which the `ParseSpan` will start. 17411 * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the 17412 * natural ending index) 17413 */ 17414 span(start, artificialEndIndex) { 17415 let endIndex = this.currentEndIndex; 17416 if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) { 17417 endIndex = artificialEndIndex; 17418 } 17419 // In some unusual parsing scenarios (like when certain tokens are missing and an `EmptyExpr` is 17420 // being created), the current token may already be advanced beyond the `currentEndIndex`. This 17421 // appears to be a deep-seated parser bug. 17422 // 17423 // As a workaround for now, swap the start and end indices to ensure a valid `ParseSpan`. 17424 // TODO(alxhub): fix the bug upstream in the parser state, and remove this workaround. 17425 if (start > endIndex) { 17426 const tmp = endIndex; 17427 endIndex = start; 17428 start = tmp; 17429 } 17430 return new ParseSpan(start, endIndex); 17431 } 17432 sourceSpan(start, artificialEndIndex) { 17433 const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`; 17434 if (!this.sourceSpanCache.has(serial)) { 17435 this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset)); 17436 } 17437 return this.sourceSpanCache.get(serial); 17438 } 17439 advance() { 17440 this.index++; 17441 } 17442 /** 17443 * Executes a callback in the provided context. 17444 */ 17445 withContext(context, cb) { 17446 this.context |= context; 17447 const ret = cb(); 17448 this.context ^= context; 17449 return ret; 17450 } 17451 consumeOptionalCharacter(code) { 17452 if (this.next.isCharacter(code)) { 17453 this.advance(); 17454 return true; 17455 } else { 17456 return false;
17457 } 17458 } 17459 peekKeywordLet() { 17460 return this.next.isKeywordLet(); 17461 } 17462 peekKeywordAs() { 17463 return this.next.isKeywordAs(); 17464 } 17465 /** 17466 * Consumes an expected character, otherwise emits an error about the missing expected character 17467 * and skips over the token stream until reaching a recoverable point. 17468 * 17469 * See `this.error` and `this.skip` for more details. 17470 */ 17471 expectCharacter(code) { 17472 if (this.consumeOptionalCharacter(code)) return; 17473 this.error(`Missing expected ${String.fromCharCode(code)}`); 17474 } 17475 consumeOptionalOperator(op) { 17476 if (this.next.isOperator(op)) { 17477 this.advance(); 17478 return true; 17479 } else { 17480 return false; 17481 } 17482 } 17483 expectOperator(operator) { 17484 if (this.consumeOptionalOperator(operator)) return; 17485 this.error(`Missing expected operator ${operator}`); 17486 } 17487 prettyPrintToken(tok) { 17488 return tok === EOF ? 'end of input' : `token ${tok}`; 17489 } 17490 expectIdentifierOrKeyword() { 17491 const n = this.next; 17492 if (!n.isIdentifier() && !n.isKeyword()) { 17493 if (n.isPrivateIdentifier()) { 17494 this._reportErrorForPrivateIdentifier(n, 'expected identifier or keyword'); 17495 } else { 17496 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`); 17497 } 17498 return null; 17499 } 17500 this.advance(); 17501 return n.toString(); 17502 } 17503 expectIdentifierOrKeywordOrString() { 17504 const n = this.next; 17505 if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) { 17506 if (n.isPrivateIdentifier()) { 17507 this._reportErrorForPrivateIdentifier(n, 'expected identifier, keyword or string'); 17508 } else { 17509 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`); 17510 } 17511 return ''; 17512 } 17513 this.advance(); 17514 return n.toString(); 17515 } 17516 parseChain() { 17517 const exprs = []; 17518 const start = this.inputIndex; 17519 while (this.index < this.tokens.length) { 17520 const expr = this.parsePipe(); 17521 exprs.push(expr); 17522 if (this.consumeOptionalCharacter($SEMICOLON)) { 17523 if (!(this.parseFlags & 1 /* ParseFlags.Action */)) { 17524 this.error('Binding expression cannot contain chained expression'); 17525 } 17526 while (this.consumeOptionalCharacter($SEMICOLON)) {} // read all semicolons 17527 } else if (this.index < this.tokens.length) { 17528 const errorIndex = this.index; 17529 this.error(`Unexpected token '${this.next}'`); 17530 // The `error` call above will skip ahead to the next recovery point in an attempt to 17531 // recover part of the expression, but that might be the token we started from which will 17532 // lead to an infinite loop. If that's the case, break the loop assuming that we can't 17533 // parse further. 17534 if (this.index === errorIndex) { 17535 break; 17536 } 17537 } 17538 } 17539 if (exprs.length === 0) { 17540 // We have no expressions so create an empty expression that spans the entire input length 17541 const artificialStart = this.offset; 17542 const artificialEnd = this.offset + this.input.length; 17543 return new EmptyExpr$1(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd)); 17544 } 17545 if (exprs.length == 1) return exprs[0]; 17546 return new Chain(this.span(start), this.sourceSpan(start), exprs); 17547 } 17548 parsePipe() { 17549 const start = this.inputIndex; 17550 let result = this.parseExpression(); 17551 if (this.consumeOptionalOperator('|')) { 17552 if (this.parseFlags & 1 /* ParseFlags.Action */) { 17553 this.error(`Cannot have a pipe in an action expression`); 17554 } 17555 do { 17556 const nameStart = this.inputIndex; 17557 let nameId = this.expectIdentifierOrKeyword(); 17558 let nameSpan; 17559 let fullSpanEnd = undefined; 17560 if (nameId !== null) { 17561 nameSpan = this.sourceSpan(nameStart); 17562 } else { 17563 // No valid identifier was found, so we'll assume an empty pipe name (''). 17564 nameId = ''; 17565 // However, there may have been whitespace present between the pipe character and the next 17566 // token in the sequence (or the end of input). We want to track this whitespace so that 17567 // the `BindingPipe` we produce covers not just the pipe character, but any trailing 17568 // whitespace beyond it. Another way of thinking about this is that the zero-length name 17569 // is assumed to be at the end of any whitespace beyond the pipe character. 17570 // 17571 // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the 17572 // beginning of the next token, or until the end of input if the next token is EOF.
17573 fullSpanEnd = this.next.index !== -1 ? this.next.index : this.input.length + this.offset; 17574 // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace 17575 // beyond the pipe character. 17576 nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset); 17577 } 17578 const args = []; 17579 while (this.consumeOptionalCharacter($COLON)) { 17580 args.push(this.parseExpression()); 17581 // If there are additional expressions beyond the name, then the artificial end for the 17582 // name is no longer relevant. 17583 } 17584 result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan); 17585 } while (this.consumeOptionalOperator('|')); 17586 } 17587 return result; 17588 } 17589 parseExpression() { 17590 return this.parseConditional(); 17591 } 17592 parseConditional() { 17593 const start = this.inputIndex; 17594 const result = this.parseLogicalOr(); 17595 if (this.consumeOptionalOperator('?')) { 17596 const yes = this.parsePipe(); 17597 let no; 17598 if (!this.consumeOptionalCharacter($COLON)) { 17599 const end = this.inputIndex; 17600 const expression = this.input.substring(start, end); 17601 this.error(`Conditional expression ${expression} requires all 3 expressions`); 17602 no = new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17603 } else { 17604 no = this.parsePipe(); 17605 } 17606 return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no); 17607 } else { 17608 return result; 17609 } 17610 } 17611 parseLogicalOr() { 17612 // '||' 17613 const start = this.inputIndex; 17614 let result = this.parseLogicalAnd(); 17615 while (this.consumeOptionalOperator('||')) { 17616 const right = this.parseLogicalAnd(); 17617 result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right); 17618 } 17619 return result; 17620 } 17621 parseLogicalAnd() { 17622 // '&&' 17623 const start = this.inputIndex; 17624 let result = this.parseNullishCoalescing(); 17625 while (this.consumeOptionalOperator('&&')) { 17626 const right = this.parseNullishCoalescing(); 17627 result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right); 17628 } 17629 return result; 17630 } 17631 parseNullishCoalescing() { 17632 // '??' 17633 const start = this.inputIndex; 17634 let result = this.parseEquality(); 17635 while (this.consumeOptionalOperator('??')) { 17636 const right = this.parseEquality(); 17637 result = new Binary(this.span(start), this.sourceSpan(start), '??', result, right); 17638 } 17639 return result; 17640 } 17641 parseEquality() { 17642 // '==','!=','===','!==' 17643 const start = this.inputIndex; 17644 let result = this.parseRelational(); 17645 while (this.next.type == TokenType.Operator) { 17646 const operator = this.next.strValue; 17647 switch (operator) { 17648 case '==': 17649 case '===': 17650 case '!=': 17651 case '!==': 17652 this.advance(); 17653 const right = this.parseRelational(); 17654 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); 17655 continue; 17656 } 17657 break; 17658 } 17659 return result; 17660 } 17661 parseRelational() { 17662 // '<', '>', '<=', '>=' 17663 const start = this.inputIndex; 17664 let result = this.parseAdditive(); 17665 while (this.next.type == TokenType.Operator) { 17666 const operator = this.next.strValue; 17667 switch (operator) { 17668 case '<': 17669 case '>': 17670 case '<=': 17671 case '>=': 17672 this.advance(); 17673 const right = this.parseAdditive(); 17674 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); 17675 continue; 17676 } 17677 break; 17678 } 17679 return result; 17680 } 17681 parseAdditive() { 17682 // '+', '-' 17683 const start = this.inputIndex; 17684 let result = this.parseMultiplicative(); 17685 while (this.next.type == TokenType.Operator) { 17686 const operator = this.next.strValue; 17687 switch (operator) { 17688 case '+': 17689 case '-': 17690 this.advance(); 17691 let right = this.parseMultiplicative(); 17692 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); 17693 continue; 17694 } 17695 break; 17696 } 17697 return result; 17698 } 17699 parseMultiplicative() { 17700 // '*', '%', '/' 17701 const start = this.inputIndex; 17702 let result = this.parsePrefix(); 17703 while (this.next.type == TokenType.Operator) { 17704 const operator = this.next.strValue; 17705 switch (operator) { 17706 case '*': 17707 case '%': 17708 case '/': 17709 this.advance(); 17710 let right = this.parsePrefix(); 17711 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); 17712 continue; 17713 } 17714 break; 17715 } 17716 return result; 17717 } 17718 parsePrefix() { 17719 if (this.next.type == TokenType.Operator) { 17720 const start = this.inputIndex; 17721 const operator = this.next.strValue; 17722 let result; 17723 switch (operator) { 17724 case '+': 17725 this.advance(); 17726 result = this.parsePrefix(); 17727 return Unary.createPlus(this.span(start), this.sourceSpan(start), result); 17728 case '-': 17729 this.advance(); 17730 result = this.parsePrefix(); 17731 return Unary.createMinus(this.span(start), this.sourceSpan(start), result); 17732 case '!': 17733 this.advance(); 17734 result = this.parsePrefix(); 17735 return new PrefixNot(this.span(start), this.sourceSpan(start), result); 17736 } 17737 } 17738 return this.parseCallChain(); 17739 } 17740 parseCallChain() { 17741 const start = this.inputIndex; 17742 let result = this.parsePrimary(); 17743 while (true) { 17744 if (this.consumeOptionalCharacter($PERIOD)) { 17745 result = this.parseAccessMember(result, start, false); 17746 } else if (this.consumeOptionalOperator('?.')) { 17747 if (this.consumeOptionalCharacter($LPAREN)) { 17748 result = this.parseCall(result, start, true); 17749 } else { 17750 result = this.consumeOptionalCharacter($LBRACKET) ? this.parseKeyedReadOrWrite(result, start, true) : this.parseAccessMember(result, start, true); 17751 } 17752 } else if (this.consumeOptionalCharacter($LBRACKET)) { 17753 result = this.parseKeyedReadOrWrite(result, start, false); 17754 } else if (this.consumeOptionalCharacter($LPAREN)) { 17755 result = this.parseCall(result, start, false); 17756 } else if (this.consumeOptionalOperator('!')) { 17757 result = new NonNullAssert(this.span(start), this.sourceSpan(start), result); 17758 } else { 17759 return result; 17760 } 17761 } 17762 } 17763 parsePrimary() { 17764 const start = this.inputIndex; 17765 if (this.consumeOptionalCharacter($LPAREN)) { 17766 this.rparensExpected++; 17767 const result = this.parsePipe(); 17768 this.rparensExpected--; 17769 this.expectCharacter($RPAREN); 17770 return result; 17771 } else if (this.next.isKeywordNull()) { 17772 this.advance(); 17773 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null); 17774 } else if (this.next.isKeywordUndefined()) { 17775 this.advance(); 17776 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0); 17777 } else if (this.next.isKeywordTrue()) { 17778 this.advance(); 17779 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true); 17780 } else if (this.next.isKeywordFalse()) { 17781 this.advance(); 17782 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false); 17783 } else if (this.next.isKeywordThis()) { 17784 this.advance(); 17785 return new ThisReceiver(this.span(start), this.sourceSpan(start)); 17786 } else if (this.consumeOptionalCharacter($LBRACKET)) { 17787 this.rbracketsExpected++; 17788 const elements = this.parseExpressionList($RBRACKET); 17789 this.rbracketsExpected--; 17790 this.expectCharacter($RBRACKET); 17791 return new LiteralArray(this.span(start), this.sourceSpan(start), elements); 17792 } else if (this.next.isCharacter($LBRACE)) { 17793 return this.parseLiteralMap(); 17794 } else if (this.next.isIdentifier()) { 17795 return this.parseAccessMember(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false); 17796 } else if (this.next.isNumber()) { 17797 const value = this.next.toNumber(); 17798 this.advance(); 17799 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value); 17800 } else if (this.next.isString()) { 17801 const literalValue = this.next.toString(); 17802 this.advance(); 17803 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue); 17804 } else if (this.next.isPrivateIdentifier()) { 17805 this._reportErrorForPrivateIdentifier(this.next, null); 17806 return new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17807 } else if (this.index >= this.tokens.length) { 17808 this.error(`Unexpected end of expression: ${this.input}`); 17809 return new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17810 } else { 17811 this.error(`Unexpected token ${this.next}`); 17812 return new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17813 } 17814 } 17815 parseExpressionList(terminator) { 17816 const result = []; 17817 do { 17818 if (!this.next.isCharacter(terminator)) { 17819 result.push(this.parsePipe()); 17820 } else { 17821 break; 17822 } 17823 } while (this.consumeOptionalCharacter($COMMA)); 17824 return result; 17825 } 17826 parseLiteralMap() {
17827 const keys = []; 17828 const values = []; 17829 const start = this.inputIndex; 17830 this.expectCharacter($LBRACE); 17831 if (!this.consumeOptionalCharacter($RBRACE)) { 17832 this.rbracesExpected++; 17833 do { 17834 const keyStart = this.inputIndex; 17835 const quoted = this.next.isString(); 17836 const key = this.expectIdentifierOrKeywordOrString(); 17837 keys.push({ 17838 key, 17839 quoted 17840 }); 17841 // Properties with quoted keys can't use the shorthand syntax. 17842 if (quoted) { 17843 this.expectCharacter($COLON); 17844 values.push(this.parsePipe()); 17845 } else if (this.consumeOptionalCharacter($COLON)) { 17846 values.push(this.parsePipe()); 17847 } else { 17848 const span = this.span(keyStart); 17849 const sourceSpan = this.sourceSpan(keyStart); 17850 values.push(new PropertyRead(span, sourceSpan, sourceSpan, new ImplicitReceiver(span, sourceSpan), key)); 17851 } 17852 } while (this.consumeOptionalCharacter($COMMA) && !this.next.isCharacter($RBRACE)); 17853 this.rbracesExpected--; 17854 this.expectCharacter($RBRACE); 17855 } 17856 return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values); 17857 } 17858 parseAccessMember(readReceiver, start, isSafe) { 17859 const nameStart = this.inputIndex; 17860 const id = this.withContext(ParseContextFlags.Writable, () => { 17861 const id = this.expectIdentifierOrKeyword() ?? ''; 17862 if (id.length === 0) { 17863 this.error(`Expected identifier for property access`, readReceiver.span.end); 17864 } 17865 return id; 17866 }); 17867 const nameSpan = this.sourceSpan(nameStart); 17868 let receiver; 17869 if (isSafe) { 17870 if (this.consumeOptionalOperator('=')) { 17871 this.error('The \'?.\' operator cannot be used in the assignment'); 17872 receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17873 } else { 17874 receiver = new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id); 17875 } 17876 } else { 17877 if (this.consumeOptionalOperator('=')) { 17878 if (!(this.parseFlags & 1 /* ParseFlags.Action */)) { 17879 this.error('Bindings cannot contain assignments'); 17880 return new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 17881 } 17882 const value = this.parseConditional(); 17883 receiver = new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id, value); 17884 } else { 17885 receiver = new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id); 17886 } 17887 } 17888 return receiver; 17889 } 17890 parseCall(receiver, start, isSafe) { 17891 const argumentStart = this.inputIndex; 17892 this.rparensExpected++; 17893 const args = this.parseCallArguments(); 17894 const argumentSpan = this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset); 17895 this.expectCharacter($RPAREN); 17896 this.rparensExpected--; 17897 const span = this.span(start); 17898 const sourceSpan = this.sourceSpan(start); 17899 return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) : new Call(span, sourceSpan, receiver, args, argumentSpan); 17900 } 17901 parseCallArguments() { 17902 if (this.next.isCharacter($RPAREN)) return []; 17903 const positionals = []; 17904 do { 17905 positionals.push(this.parsePipe()); 17906 } while (this.consumeOptionalCharacter($COMMA)); 17907 return positionals; 17908 } 17909 /** 17910 * Parses an identifier, a keyword, a string with an optional `-` in between, 17911 * and returns the string along with its absolute source span. 17912 */ 17913 expectTemplateBindingKey() { 17914 let result = ''; 17915 let operatorFound = false; 17916 const start = this.currentAbsoluteOffset; 17917 do { 17918 result += this.expectIdentifierOrKeywordOrString(); 17919 operatorFound = this.consumeOptionalOperator('-'); 17920 if (operatorFound) { 17921 result += '-'; 17922 } 17923 } while (operatorFound); 17924 return { 17925 source: result, 17926 span: new AbsoluteSourceSpan(start, start + result.length) 17927 }; 17928 } 17929 /** 17930 * Parse microsyntax template expression and return a list of bindings or 17931 * parsing errors in case the given expression is invalid. 17932 * 17933 * For example, 17934 * ``` 17935 * <div *ngFor="let item of items; index as i; trackBy: func"> 17936 * ``` 17937 * contains five bindings: 17938 * 1. ngFor -> null 17939 * 2. item -> NgForOfContext.$implicit 17940 * 3. ngForOf -> items 17941 * 4. i ->
17941 NgForOfContext.index 17942 * 5. ngForTrackBy -> func 17943 * 17944 * For a full description of the microsyntax grammar, see 17945 * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855 17946 * 17947 * @param templateKey name of the microsyntax directive, like ngIf, ngFor, 17948 * without the *, along with its absolute span. 17949 */ 17950 parseTemplateBindings(templateKey) { 17951 const bindings = []; 17952 // The first binding is for the template key itself 17953 // In *ngFor="let item of items", key = "ngFor", value = null 17954 // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe" 17955 bindings.push(...this.parseDirectiveKeywordBindings(templateKey)); 17956 while (this.index < this.tokens.length) { 17957 // If it starts with 'let', then this must be variable declaration 17958 const letBinding = this.parseLetBinding(); 17959 if (letBinding) { 17960 bindings.push(letBinding); 17961 } else { 17962 // Two possible cases here, either `value "as" key` or 17963 // "directive-keyword expression". We don't know which case, but both 17964 // "value" and "directive-keyword" are template binding key, so consume 17965 // the key first. 17966 const key = this.expectTemplateBindingKey(); 17967 // Peek at the next token, if it is "as" then this must be variable 17968 // declaration. 17969 const binding = this.parseAsBinding(key); 17970 if (binding) { 17971 bindings.push(binding); 17972 } else { 17973 // Otherwise the key must be a directive keyword, like "of". Transform 17974 // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy 17975 key.source = templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1); 17976 bindings.push(...this.parseDirectiveKeywordBindings(key)); 17977 } 17978 } 17979 this.consumeStatementTerminator(); 17980 } 17981 return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors); 17982 } 17983 parseKeyedReadOrWrite(receiver, start, isSafe) { 17984 return this.withContext(ParseContextFlags.Writable, () => { 17985 this.rbracketsExpected++; 17986 const key = this.parsePipe(); 17987 if (key instanceof EmptyExpr$1) { 17988 this.error(`Key access cannot be empty`); 17989 } 17990 this.rbracketsExpected--; 17991 this.expectCharacter($RBRACKET); 17992 if (this.consumeOptionalOperator('=')) { 17993 if (isSafe) { 17994 this.error('The \'?.\' operator cannot be used in the assignment'); 17995 } else { 17996 const value = this.parseConditional(); 17997 return new KeyedWrite(this.span(start), this.sourceSpan(start), receiver, key, value); 17998 } 17999 } else { 18000 return isSafe ? new SafeKeyedRead(this.span(start), this.sourceSpan(start), receiver, key) : new KeyedRead(this.span(start), this.sourceSpan(start), receiver, key); 18001 } 18002 return new EmptyExpr$1(this.span(start), this.sourceSpan(start)); 18003 }); 18004 } 18005 /** 18006 * Parse a directive keyword, followed by a mandatory expression. 18007 * For example, "of items", "trackBy: func". 18008 * The bindings are: ngForOf -> items, ngForTrackBy -> func 18009 * There could be an optional "as" binding that follows the expression. 18010 * For example, 18011 * ``` 18012 * *ngFor="let item of items | slice:0:1 as collection". 18013 * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ 18014 * keyword bound target optional 'as' binding 18015 * ``` 18016 * 18017 * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its 18018 * absolute span. 18019 */ 18020 parseDirectiveKeywordBindings(key) { 18021 const bindings = []; 18022 this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction 18023 const value = this.getDirectiveBoundTarget(); 18024 let spanEnd = this.currentAbsoluteOffset; 18025 // The binding could optionally be followed by "as". For example, 18026 // *ngIf="cond | pipe as x". In this case, the key in the "as" binding 18027 // is "x" and the value is the template key itself ("ngIf"). Note that the 18028 // 'key' in the current context now becomes the "value" in the next binding. 18029 const asBinding = this.parseAsBinding(key); 18030 if (!asBinding) { 18031 this.consumeStatementTerminator(); 18032 spanEnd = this.currentAbsoluteOffset; 18033 } 18034 const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd); 18035 bindings.push(new ExpressionBinding(sourceSpan, key, value)); 18036 if (asBinding) { 18037 bindings.push(asBinding); 18038 } 18039 return bindings; 18040 } 18041 /** 18042 * Return the expression AST for the bound target of a directive keyword 18043 * binding. For example, 18044 * ``` 18045 * *ngIf="condition | pipe" 18046 * ^^^^^^^^^^^^^^^^ bound target for "ngIf" 18047 * *ngFor="let item of items" 18048 * ^^^^^ bound target for "ngForOf" 18049 * ``` 18050 */ 18051 getDirectiveBoundTarget() { 18052 if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) { 18053 return null; 18054 } 18055 const ast = this.parsePipe(); // example: "condition | async" 18056 const { 18057 start, 18058 end 18059 } = ast.span; 18060 const value = this.input.substring(start, end); 18061 return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors); 18062 } 18063 /** 18064 * Return the binding for a variable declared using `as`. Note that the order 18065 * of the key-value pair in this declaration is reversed. For example, 18066 * ``` 18067 * *ngFor="let item of items; index as i" 18068 * ^^^^^ ^ 18069 * value key 18070 * ``` 18071 * 18072 * @param value name of the value in the declaration, "ngIf" in the example 18073 * above, along with its absolute span. 18074 */ 18075 parseAsBinding(value) { 18076 if (!this.peekKeywordAs()) { 18077 return null; 18078 } 18079 this.advance(); // consume the 'as' keyword
18080 const key = this.expectTemplateBindingKey(); 18081 this.consumeStatementTerminator(); 18082 const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset); 18083 return new VariableBinding(sourceSpan, key, value); 18084 } 18085 /** 18086 * Return the binding for a variable declared using `let`. For example, 18087 * ``` 18088 * *ngFor="let item of items; let i=index;" 18089 * ^^^^^^^^ ^^^^^^^^^^^ 18090 * ``` 18091 * In the first binding, `item` is bound to `NgForOfContext.$implicit`. 18092 * In the second binding, `i` is bound to `NgForOfContext.index`. 18093 */ 18094 parseLetBinding() { 18095 if (!this.peekKeywordLet()) { 18096 return null; 18097 } 18098 const spanStart = this.currentAbsoluteOffset; 18099 this.advance(); // consume the 'let' keyword 18100 const key = this.expectTemplateBindingKey(); 18101 let value = null; 18102 if (this.consumeOptionalOperator('=')) { 18103 value = this.expectTemplateBindingKey(); 18104 } 18105 this.consumeStatementTerminator(); 18106 const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset); 18107 return new VariableBinding(sourceSpan, key, value); 18108 } 18109 /** 18110 * Consume the optional statement terminator: semicolon or comma. 18111 */ 18112 consumeStatementTerminator() { 18113 this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA); 18114 } 18115 /** 18116 * Records an error and skips over the token stream until reaching a recoverable point. See 18117 * `this.skip` for more details on token skipping. 18118 */ 18119 error(message, index = null) { 18120 this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location)); 18121 this.skip(); 18122 } 18123 locationText(index = null) { 18124 if (index == null) index = this.index; 18125 return index < this.tokens.length ? `at column ${this.tokens[index].index + 1} in` : `at the end of the expression`; 18126 } 18127 /** 18128 * Records an error for an unexpected private identifier being discovered. 18129 * @param token Token representing a private identifier. 18130 * @param extraMessage Optional additional message being appended to the error. 18131 */ 18132 _reportErrorForPrivateIdentifier(token, extraMessage) { 18133 let errorMessage = `Private identifiers are not supported. Unexpected private identifier: ${token}`; 18134 if (extraMessage !== null) { 18135 errorMessage += `, ${extraMessage}`; 18136 } 18137 this.error(errorMessage); 18138 } 18139 /** 18140 * Error recovery should skip tokens until it encounters a recovery point. 18141 * 18142 * The following are treated as unconditional recovery points: 18143 * - end of input 18144 * - ';' (parseChain() is always the root production, and it expects a ';') 18145 * - '|' (since pipes may be chained and each pipe expression may be treated independently) 18146 * 18147 * The following are conditional recovery points: 18148 * - ')', '}', ']' if one of calling productions is expecting one of these symbols 18149 * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to 18150 * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins 18151 * an '(' <expr> ')' production). 18152 * The recovery points of grouping symbols must be conditional as they must be skipped if 18153 * none of the calling productions are not expecting the closing token else we will never 18154 * make progress in the case of an extraneous group closing symbol (such as a stray ')'). 18155 * That is, we skip a closing symbol if we are not in a grouping production. 18156 * - '=' in a `Writable` context 18157 * - In this context, we are able to recover after seeing the `=` operator, which 18158 * signals the presence of an independent rvalue expression following the `=` operator. 18159 * 18160 * If a production expects one of these token it increments the corresponding nesting count, 18161 * and then decrements it just prior to checking if the token is in the input. 18162 */ 18163 skip() { 18164 let n = this.next; 18165 while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) && !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) && (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) && (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) && (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) { 18166 if (this.next.isError()) { 18167 this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location)); 18168 } 18169 this.advance(); 18170 n = this.next; 18171 } 18172 } 18173} 18174class SimpleExpressionChecker extends RecursiveAstVisitor { 18175 constructor() { 18176 super(...arguments); 18177 this.errors = []; 18178 } 18179 visitPipe() { 18180 this.errors.push('pipes'); 18181 } 18182} 18183/** 18184 * Computes the real offset in the original template for indexes in an interpolation. 18185 * 18186 * Because templates can have encoded HTML entities and the input passed to the parser at this stage 18187 * of the compiler is the _decoded_ value, we need to compute the real offset using the original 18188 * encoded values in the interpolated tokens. Note that this is only a special case handling for 18189 * `MlParserTokenType.ENCODED_ENTITY` token types. All other interpolated tokens are expected to 18190 * have parts which exactly match the input string for parsing the interpolation. 18191 * 18192 * @param interpolatedTokens The tokens for the interpolated value. 18193 * 18194 * @returns A map of index locations in the decoded template to indexes in the original template 18195 */ 18196function getIndexMapForOriginalTemplate(interpolatedTokens) { 18197 let offsetMap = new Map(); 18198 let consumedInOriginalTemplate = 0; 18199 let consumedInInput = 0; 18200 let tokenIndex = 0; 18201 while (tokenIndex < interpolatedTokens.length) { 18202 const currentToken = interpolatedTokens[tokenIndex]; 18203 if (currentToken.type === 9 /* MlParserTokenType.ENCODED_ENTITY */) { 18204 const [decoded, encoded] = currentToken.parts; 18205 consumedInOriginalTemplate += encoded.length; 18206 consumedInInput += decoded.length; 18207 } else { 18208 const lengthOfParts = currentToken.parts.reduce((sum, current) => sum + current.length, 0); 18209 consumedInInput += lengthOfParts; 18210 consumedInOriginalTemplate += lengthOfParts; 18211 } 18212 offsetMap.set(consumedInInput, consumedInOriginalTemplate); 18213 tokenIndex++; 18214 } 18215 return offsetMap; 18216} 18217class NodeWithI18n { 18218 constructor(sourceSpan, i18n) { 18219 this.sourceSpan = sourceSpan; 18220 this.i18n = i18n; 18221 } 18222} 18223class Text extends NodeWithI18n { 18224 constructor(value, sourceSpan, tokens, i18n) { 18225 super(sourceSpan, i18n); 18226 this.value = value; 18227 this.tokens = tokens; 18228 } 18229 visit(visitor, context) { 18230 return visitor.visitText(this, context); 18231 } 18232} 18233class Expansion extends NodeWithI18n { 18234 constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) { 18235 super(sourceSpan, i18n); 18236 this.switchValue = switchValue; 18237 this.type = type; 18238 this.cases = cases; 18239 this.switchValueSourceSpan = switchValueSourceSpan; 18240 } 18241 visit(visitor, context) { 18242 return visitor.visitExpansion(this, context); 18243 } 18244} 18245class ExpansionCase {
18246 constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) { 18247 this.value = value; 18248 this.expression = expression; 18249 this.sourceSpan = sourceSpan; 18250 this.valueSourceSpan = valueSourceSpan; 18251 this.expSourceSpan = expSourceSpan; 18252 } 18253 visit(visitor, context) { 18254 return visitor.visitExpansionCase(this, context); 18255 } 18256} 18257class Attribute extends NodeWithI18n { 18258 constructor(name, value, sourceSpan, keySpan, valueSpan, valueTokens, i18n) { 18259 super(sourceSpan, i18n); 18260 this.name = name; 18261 this.value = value; 18262 this.keySpan = keySpan; 18263 this.valueSpan = valueSpan; 18264 this.valueTokens = valueTokens; 18265 } 18266 visit(visitor, context) { 18267 return visitor.visitAttribute(this, context); 18268 } 18269} 18270class Element extends NodeWithI18n { 18271 constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) { 18272 super(sourceSpan, i18n); 18273 this.name = name; 18274 this.attrs = attrs; 18275 this.children = children; 18276 this.startSourceSpan = startSourceSpan; 18277 this.endSourceSpan = endSourceSpan; 18278 } 18279 visit(visitor, context) { 18280 return visitor.visitElement(this, context); 18281 } 18282} 18283class Comment { 18284 constructor(value, sourceSpan) { 18285 this.value = value; 18286 this.sourceSpan = sourceSpan; 18287 } 18288 visit(visitor, context) { 18289 return visitor.visitComment(this, context); 18290 } 18291} 18292class Block extends NodeWithI18n { 18293 constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null, i18n) { 18294 super(sourceSpan, i18n); 18295 this.name = name; 18296 this.parameters = parameters; 18297 this.children = children; 18298 this.nameSpan = nameSpan; 18299 this.startSourceSpan = startSourceSpan; 18300 this.endSourceSpan = endSourceSpan; 18301 } 18302 visit(visitor, context) { 18303 return visitor.visitBlock(this, context); 18304 } 18305} 18306class BlockParameter { 18307 constructor(expression, sourceSpan) { 18308 this.expression = expression; 18309 this.sourceSpan = sourceSpan; 18310 } 18311 visit(visitor, context) { 18312 return visitor.visitBlockParameter(this, context); 18313 } 18314} 18315function visitAll(visitor, nodes, context = null) { 18316 const result = []; 18317 const visit = visitor.visit ? ast => visitor.visit(ast, context) || ast.visit(visitor, context) : ast => ast.visit(visitor, context); 18318 nodes.forEach(ast => { 18319 const astResult = visit(ast); 18320 if (astResult) { 18321 result.push(astResult); 18322 } 18323 }); 18324 return result; 18325} 18326class RecursiveVisitor { 18327 constructor() {} 18328 visitElement(ast, context) { 18329 this.visitChildren(context, visit => { 18330 visit(ast.attrs); 18331 visit(ast.children); 18332 }); 18333 } 18334 visitAttribute(ast, context) {} 18335 visitText(ast, context) {} 18336 visitComment(ast, context) {} 18337 visitExpansion(ast, context) { 18338 return this.visitChildren(context, visit => { 18339 visit(ast.cases); 18340 }); 18341 } 18342 visitExpansionCase(ast, context) {} 18343 visitBlock(block, context) { 18344 this.visitChildren(context, visit => { 18345 visit(block.parameters); 18346 visit(block.children); 18347 }); 18348 } 18349 visitBlockParameter(ast, context) {} 18350 visitChildren(context, cb) { 18351 let results = []; 18352 let t = this; 18353 function visit(children) { 18354 if (children) results.push(visitAll(t, children, context)); 18355 } 18356 cb(visit); 18357 return Array.prototype.concat.apply([], results); 18358 } 18359} 18360class ElementSchemaRegistry {} 18361const BOOLEAN = 'boolean'; 18362const NUMBER = 'number'; 18363const STRING = 'string'; 18364const OBJECT = 'object'; 18365/** 18366 * This array represents the DOM schema. It encodes inheritance, properties, and events. 18367 * 18368 * ## Overview 18369 * 18370 * Each line represents one kind of element. The `element_inheritance` and properties are joined 18371 * using `element_inheritance|properties` syntax. 18372 * 18373 * ## Element Inheritance 18374 * 18375 * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`. 18376 * Here the individual elements are separated by `,` (commas). Every element in the list 18377 * has identical properties. 18378 * 18379 * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is 18380 * specified then `""` (blank) element is assumed. 18381 * 18382 * NOTE: The blank element inherits from root `[Element]` element, the super element of all 18383 * elements. 18384 * 18385 * NOTE an element prefix such as `:svg:` has no special meaning to the schema. 18386 * 18387 * ## Properties 18388 * 18389 * Each element has a set of properties separated by `,` (commas). Each property can be prefixed 18390 * by a special character designating its type: 18391 * 18392 * - (no prefix): property is a string. 18393 * - `*`: property represents an event. 18394 * - `!`: property is a boolean. 18395 * - `#`: property is a number. 18396 * - `%`: property is an object. 18397 * 18398 * ## Query 18399 * 18400 * The class creates an internal squas representation which allows to easily answer the query of 18401 * if a given property exist on a given element. 18402 * 18403 * NOTE: We don't yet support querying for types or events. 18404 * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder, 18405 * see dom_element_schema_registry_spec.ts 18406 */ 18407// ================================================================================================= 18408// ================================================================================================= 18409// =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P =========== 18410// ================================================================================================= 18411// ================================================================================================= 18412// 18413// DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW! 18414// 18415// Newly added properties must be security reviewed and assigned an appropriate SecurityContext in 18416// dom_security_schema.ts. Reach out to mprobst & rjamet for details. 18417// 18418// =================================================================================================
18419const SCHEMA = ['[Element]|textContent,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColSpan,%ariaCurrent,%ariaDescription,%ariaDisabled,%ariaExpanded,%ariaHasPopup,%ariaHidden,%ariaKeyShortcuts,%ariaLabel,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%ariaRowSpan,%ariaSelected,%ariaSetSize,%ariaSort,%ariaValueMax,%ariaValueMin,%ariaValueNow,%ariaValueText,%classList,className,elementTiming,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*fullscreenchange,*fullscreenerror,*search,*webkitfullscreenchange,*webkitfullscreenerror,outerHTML,%part,#scrollLeft,#scrollTop,slot' + /* added manually to avoid breaking changes */ 18420',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored', '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,!inert,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume', ':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*p
18420ointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex', ':svg:graphics^:svg:|', ':svg:animation^:svg:|*begin,*end,*repeat', ':svg:geometry^:svg:|', ':svg:componentTransferFunction^:svg:|', ':svg:gradient^:svg:|', ':svg:textContent^:svg:graphics|', ':svg:textPositioning^:svg:textContent|', 'a^[HTMLElement]|charset,coords,download,hash,host,hostname,href,hreflang,name,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,rev,search,shape,target,text,type,username', 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,search,shape,target,username', 'audio^media|', 'br^[HTMLElement]|clear', 'base^[HTMLElement]|href,target', 'body^[HTMLElement]|aLink,background,bgColor,link,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink', 'button^[HTMLElement]|!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value', 'canvas^[HTMLElement]|#height,#width', 'content^[HTMLElement]|select', 'dl^[HTMLElement]|!compact', 'data^[HTMLElement]|value', 'datalist^[HTMLElement]|', 'details^[HTMLElement]|!open', 'dialog^[HTMLElement]|!open,returnValue', 'dir^[HTMLElement]|!compact', 'div^[HTMLElement]|align', 'embed^[HTMLElement]|align,height,name,src,type,width', 'fieldset^[HTMLElement]|!disabled,name', 'font^[HTMLElement]|color,face,size', 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target', 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src', 'frameset^[HTMLElement]|cols,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows', 'hr^[HTMLElement]|align,color,!noShade,size,width', 'head^[HTMLElement]|', 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align', 'html^[HTMLElement]|version', 'iframe^[HTMLElement]|align,allow,!allowFullscreen,!allowPaymentRequest,csp,frameBorder,height,loading,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width', 'img^[HTMLElement]|align,alt,border,%crossOrigin,decoding,#height,#hspace,!isMap,loading,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width', 'input^[HTMLElement]|accept,align,alt,autocomplete,!checked,!defaultChecked,defaultValue,dirName,!disabled,%files,formAction,formEnctype,formMethod,!formNoValidate,formTarget,#height,!incremental,!indeterminate,max,#maxLength,min,#minLength,!multiple,name,pattern,placeholder,!readOnly,!required,selectionDirection,#selectionEnd,#selectionStart,#size,src,step,type,useMap,value,%valueAsDate,#valueAsNumber,#width', 'li^[HTMLElement]|type,#value', 'label^[HTMLElement]|htmlFor', 'legend^[HTMLElement]|align', 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,imageSizes,imageSrcset,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type', 'map^[HTMLElement]|name', 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width', 'menu^[HTMLElement]|!compact', 'meta^[HTMLElement]|content,httpEquiv,media,name,scheme', 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value', 'ins,del^[HTMLElement]|cite,dateTime', 'ol^[HTMLElement]|!compact,!reversed,#start,type', 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width', 'optgroup^[HTMLElement]|!disabled,label', 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value', 'output^[HTMLElement]|defaultValue,%htmlFor,name,value', 'p^[HTMLElement]|align', 'param^[HTMLElement]|name,type,value,valueType', 'picture^[HTMLElement]|', 'pre^[HTMLElement]|#width', 'progress^[HTMLElement]|#max,#value', 'q,blockquote,cite^[HTMLElement]|', 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,!noModule,%referrerPolicy,src,text,type', 'select^[HTMLElement]|autocomplete,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value', 'slot^[HTMLElement]|name', 'source^[HTMLElement]|#height,media,sizes,src,srcset,type,#width', 'span^[HTMLElement]|', 'style^[HTMLElement]|!disabled,media,type', 'caption^[HTMLElement]|align', 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width', 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width', 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width', 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign', 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign', 'template^[HTMLElement]|', 'textarea^[HTMLElement]|autocomplete,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', 'time^[HTMLElement]|dateTime', 'title^[HTMLElement]|text', 'track^[HTMLElement]|!default,kind,label,src,srclang', 'ul^[HTMLElement]|!compact,type', 'unknown^[HTMLElement]|', 'video^media|!disablePictureInPicture,#height,*enterpictureinpicture,*leavepictureinpicture,!playsInline,poster,#width', ':svg:a^:svg:graphics|', ':svg:animate^:svg:animation|', ':svg:animateMotion^:svg:animation|', ':svg:animateTransform^:svg:animation|', ':svg:circle^:svg:geometry|', ':svg:clipPath^:svg:graphics|', ':svg:defs^:svg:graphics|', ':svg:desc^:svg:|', ':svg:discard^:svg:|', ':svg:ellipse^:svg:geometry|', ':svg:feBlend^:svg:|', ':svg:feColorMatrix^:svg:|', ':svg:feComponentTransfer^:svg:|', ':svg:feComposite^:svg:|', ':svg:feConvolveMatrix^:svg:|', ':svg:feDiffuseLighting^:svg:|', ':svg:feDisplacementMap^:svg:|', ':svg:feDistantLight^:svg:|', ':svg:feDropShadow^:svg:|', ':svg:feFlood^:svg:|', ':svg:feFuncA^:svg:componentTransferFunction|', ':svg:feFuncB^:svg:componentTransferFunction|', ':svg:feFuncG^:svg:componentTransferFunction|', ':svg:feFuncR^:svg:componentTransferFunction|', ':svg:feGaussianBlur^:svg:|', ':svg:feImage^:svg:|', ':svg:feMerge^:svg:|', ':svg:feMergeNode^:svg:|', ':svg:feMorphology^:svg:|', ':svg:feOffset^:svg:|', ':svg:fePointLight^:svg:|', ':svg:feSpecularLighting^:svg:|', ':svg:feSpotLight^:svg:|', ':svg:feTile^:svg:|', ':svg:feTurbulence^:svg:|', ':svg:filter^:svg:|', ':svg:foreignObject^:svg:graphics|', ':svg:g^:svg:graphics|', ':svg:image^:svg:graphics|decoding', ':svg:line^:svg:geometry|', ':svg:linearGradient^:svg:gradient|', ':svg:mpath^:svg:|', ':svg:marker^:svg:|', ':svg:mask^:svg:|', ':svg:metadata^:svg:|', ':svg:path^:svg:geometry|', ':svg:pattern^:svg:|', ':svg:polygon^:svg:geometry|', ':svg:polyline^:svg:geometry|', ':svg:radialGradient^:svg:gradient|', ':svg:rect^:svg:geometry|', ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan', ':svg:script^:svg:|type', ':svg:set^:svg:animation|', ':svg:stop^:svg:|', ':svg:style^:svg:|!disabled,media,title,type', ':svg:switch^:svg:graphics|', ':svg:symbol^:svg:|', ':svg:tspan^:svg:textPositioning|', ':svg:text^:svg:textPositioning|', ':svg:textPath^:svg:textContent|', ':svg:title^:svg:|', ':svg:use^:svg:graphics|', ':svg:view^:svg:|#zoomAndPan', 'data^[HTMLElement]|value', 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name', 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default', 'summary^[HTMLElement]|', 'time^[HTMLElement]|dateTime', ':svg:cursor^:svg:|', ':math:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxcl
18420ick,*beforeinput,*beforematch,*beforetoggle,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contentvisibilityautostatechange,*contextlost,*contextmenu,*contextrestored,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*scrollend,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex', ':math:math^:math:|', ':math:maction^:math:|', ':math:menclose^:math:|', ':math:merror^:math:|', ':math:mfenced^:math:|', ':math:mfrac^:math:|', ':math:mi^:math:|', ':math:mmultiscripts^:math:|', ':math:mn^:math:|', ':math:mo^:math:|', ':math:mover^:math:|', ':math:mpadded^:math:|', ':math:mphantom^:math:|', ':math:mroot^:math:|', ':math:mrow^:math:|', ':math:ms^
18420:math:|', ':math:mspace^:math:|', ':math:msqrt^:math:|', ':math:mstyle^:math:|', ':math:msub^:math:|', ':math:msubsup^:math:|', ':math:msup^:math:|', ':math:mtable^:math:|', ':math:mtd^:math:|', ':math:mtext^:math:|', ':math:mtr^:math:|', ':math:munder^:math:|', ':math:munderover^:math:|', ':math:semantics^:math:|']; 18421const _ATTR_TO_PROP = new Map(Object.entries({ 18422 'class': 'className', 18423 'for': 'htmlFor', 18424 'formaction': 'formAction', 18425 'innerHtml': 'innerHTML', 18426 'readonly': 'readOnly', 18427 'tabindex': 'tabIndex' 18428})); 18429// Invert _ATTR_TO_PROP. 18430const _PROP_TO_ATTR = Array.from(_ATTR_TO_PROP).reduce((inverted, [propertyName, attributeName]) => { 18431 inverted.set(propertyName, attributeName); 18432 return inverted; 18433}, new Map()); 18434class DomElementSchemaRegistry extends ElementSchemaRegistry { 18435 constructor() { 18436 super(); 18437 this._schema = new Map(); 18438 // We don't allow binding to events for security reasons. Allowing event bindings would almost 18439 // certainly introduce bad XSS vulnerabilities. Instead, we store events in a separate schema. 18440 this._eventSchema = new Map(); 18441 SCHEMA.forEach(encodedType => { 18442 const type = new Map(); 18443 const events = new Set(); 18444 const [strType, strProperties] = encodedType.split('|'); 18445 const properties = strProperties.split(','); 18446 const [typeNames, superName] = strType.split('^'); 18447 typeNames.split(',').forEach(tag => { 18448 this._schema.set(tag.toLowerCase(), type); 18449 this._eventSchema.set(tag.toLowerCase(), events); 18450 }); 18451 const superType = superName && this._schema.get(superName.toLowerCase()); 18452 if (superType) { 18453 for (const [prop, value] of superType) { 18454 type.set(prop, value); 18455 } 18456 for (const superEvent of this._eventSchema.get(superName.toLowerCase())) { 18457 events.add(superEvent); 18458 } 18459 } 18460 properties.forEach(property => { 18461 if (property.length > 0) { 18462 switch (property[0]) { 18463 case '*': 18464 events.add(property.substring(1)); 18465 break; 18466 case '!': 18467 type.set(property.substring(1), BOOLEAN); 18468 break; 18469 case '#': 18470 type.set(property.substring(1), NUMBER); 18471 break; 18472 case '%': 18473 type.set(property.substring(1), OBJECT); 18474 break; 18475 default: 18476 type.set(property, STRING); 18477 } 18478 } 18479 }); 18480 }); 18481 } 18482 hasProperty(tagName, propName, schemaMetas) { 18483 if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) { 18484 return true; 18485 } 18486 if (tagName.indexOf('-') > -1) { 18487 if (isNgContainer(tagName) || isNgContent(tagName)) { 18488 return false; 18489 } 18490 if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) { 18491 // Can't tell now as we don't know which properties a custom element will get 18492 // once it is instantiated 18493 return true; 18494 } 18495 } 18496 const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown'); 18497 return elementProperties.has(propName); 18498 } 18499 hasElement(tagName, schemaMetas) { 18500 if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) { 18501 return true; 18502 } 18503 if (tagName.indexOf('-') > -1) { 18504 if (isNgContainer(tagName) || isNgContent(tagName)) { 18505 return true; 18506 } 18507 if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) { 18508 // Allow any custom elements 18509 return true; 18510 } 18511 } 18512 return this._schema.has(tagName.toLowerCase()); 18513 } 18514 /** 18515 * securityContext returns the security context for the given property on the given DOM tag. 18516 * 18517 * Tag and property name are statically known and cannot change at runtime, i.e. it is not 18518 * possible to bind a value into a changing attribute or tag name. 18519 * 18520 * The filtering is based on a list of allowed tags|attributes. All attributes in the schema 18521 * above are assumed to have the 'NONE' security context, i.e. that they are safe inert 18522 * string values. Only specific well known attack vectors are assigned their appropriate context. 18523 */ 18524 securityContext(tagName, propName, isAttribute) { 18525 if (isAttribute) { 18526 // NB: For security purposes, use the mapped property name, not the attribute name. 18527 propName = this.getMappedPropName(propName); 18528 } 18529 // Make sure comparisons are case insensitive, so that case differences between attribute and 18530 // property names do not have a security impact. 18531 tagName = tagName.toLowerCase(); 18532 propName = propName.toLowerCase();
18533 let ctx = SECURITY_SCHEMA()[tagName + '|' + propName]; 18534 if (ctx) { 18535 return ctx; 18536 } 18537 ctx = SECURITY_SCHEMA()['*|' + propName]; 18538 return ctx ? ctx : SecurityContext.NONE; 18539 } 18540 getMappedPropName(propName) { 18541 return _ATTR_TO_PROP.get(propName) ?? propName; 18542 } 18543 getDefaultComponentElementName() { 18544 return 'ng-component'; 18545 } 18546 validateProperty(name) { 18547 if (name.toLowerCase().startsWith('on')) { 18548 const msg = `Binding to event property '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...` + `\nIf '${name}' is a directive input, make sure the directive is imported by the` + ` current module.`; 18549 return { 18550 error: true, 18551 msg: msg 18552 }; 18553 } else { 18554 return { 18555 error: false 18556 }; 18557 } 18558 } 18559 validateAttribute(name) { 18560 if (name.toLowerCase().startsWith('on')) { 18561 const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...`; 18562 return { 18563 error: true, 18564 msg: msg 18565 }; 18566 } else { 18567 return { 18568 error: false 18569 }; 18570 } 18571 } 18572 allKnownElementNames() { 18573 return Array.from(this._schema.keys()); 18574 } 18575 allKnownAttributesOfElement(tagName) { 18576 const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown'); 18577 // Convert properties to attributes. 18578 return Array.from(elementProperties.keys()).map(prop => _PROP_TO_ATTR.get(prop) ?? prop); 18579 } 18580 allKnownEventsOfElement(tagName) { 18581 return Array.from(this._eventSchema.get(tagName.toLowerCase()) ?? []); 18582 } 18583 normalizeAnimationStyleProperty(propName) { 18584 return dashCaseToCamelCase(propName); 18585 } 18586 normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) { 18587 let unit = ''; 18588 const strVal = val.toString().trim(); 18589 let errorMsg = null; 18590 if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') { 18591 if (typeof val === 'number') { 18592 unit = 'px'; 18593 } else { 18594 const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/); 18595 if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) { 18596 errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`; 18597 } 18598 } 18599 } 18600 return { 18601 error: errorMsg, 18602 value: strVal + unit 18603 }; 18604 } 18605} 18606function _isPixelDimensionStyle(prop) { 18607 switch (prop) { 18608 case 'width': 18609 case 'height': 18610 case 'minWidth': 18611 case 'minHeight': 18612 case 'maxWidth': 18613 case 'maxHeight': 18614 case 'left': 18615 case 'top': 18616 case 'bottom': 18617 case 'right': 18618 case 'fontSize': 18619 case 'outlineWidth': 18620 case 'outlineOffset': 18621 case 'paddingTop': 18622 case 'paddingLeft': 18623 case 'paddingBottom': 18624 case 'paddingRight': 18625 case 'marginTop': 18626 case 'marginLeft': 18627 case 'marginBottom': 18628 case 'marginRight': 18629 case 'borderRadius': 18630 case 'borderWidth': 18631 case 'borderTopWidth': 18632 case 'borderLeftWidth': 18633 case 'borderRightWidth': 18634 case 'borderBottomWidth': 18635 case 'textIndent': 18636 return true; 18637 default: 18638 return false; 18639 } 18640} 18641class HtmlTagDefinition { 18642 constructor({ 18643 closedByChildren, 18644 implicitNamespacePrefix, 18645 contentType = TagContentType.PARSABLE_DATA, 18646 closedByParent = false, 18647 isVoid = false, 18648 ignoreFirstLf = false, 18649 preventNamespaceInheritance = false, 18650 canSelfClose = false 18651 } = {}) { 18652 this.closedByChildren = {}; 18653 this.closedByParent = false; 18654 if (closedByChildren && closedByChildren.length > 0) { 18655 closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); 18656 } 18657 this.isVoid = isVoid; 18658 this.closedByParent = closedByParent || isVoid; 18659 this.implicitNamespacePrefix = implicitNamespacePrefix || null; 18660 this.contentType = contentType; 18661 this.ignoreFirstLf = ignoreFirstLf; 18662 this.preventNamespaceInheritance = preventNamespaceInheritance; 18663 this.canSelfClose = canSelfClose ?? isVoid; 18664 } 18665 isClosedByChild(name) { 18666 return this.isVoid || name.toLowerCase() in this.closedByChildren; 18667 } 18668 getContentType(prefix) { 18669 if (typeof this.contentType === 'object') { 18670 const overrideType = prefix === undefined ? undefined : this.contentType[prefix]; 18671 return overrideType ?? this.contentType.default; 18672 } 18673 return this.contentType; 18674 } 18675} 18676let DEFAULT_TAG_DEFINITION; 18677// see https://www.w3.org/TR/html51/syntax.html#optional-tags 18678// This implementation does not fully conform to the HTML5 spec. 18679let TAG_DEFINITIONS; 18680function getHtmlTagDefinition(tagName) { 18681 if (!TAG_DEFINITIONS) { 18682 DEFAULT_TAG_DEFINITION = new HtmlTagDefinition({ 18683 canSelfClose: true 18684 }); 18685 TAG_DEFINITIONS = Object.assign(Object.create(null), { 18686 'base': new HtmlTagDefinition({ 18687 isVoid: true 18688 }), 18689 'meta': new HtmlTagDefinition({ 18690 isVoid: true 18691 }), 18692 'area': new HtmlTagDefinition({ 18693 isVoid: true 18694 }), 18695 'embed': new HtmlTagDefinition({ 18696 isVoid: true 18697 }), 18698 'link': new HtmlTagDefinition({ 18699 isVoid: true 18700 }), 18701 'img': new HtmlTagDefinition({ 18702 isVoid: true 18703 }), 18704 'input': new HtmlTagDefinition({ 18705 isVoid: true 18706 }), 18707 'param': new HtmlTagDefinition({ 18708 isVoid: true 18709 }), 18710 'hr': new HtmlTagDefinition({ 18711 isVoid: true 18712 }), 18713 'br': new HtmlTagDefinition({ 18714 isVoid: true 18715 }), 18716 'source': new HtmlTagDefinition({ 18717 isVoid: true 18718 }), 18719 'track': new HtmlTagDefinition({ 18720 isVoid: true 18721 }), 18722 'wbr': new HtmlTagDefinition({ 18723 isVoid: true 18724 }), 18725 'p': new HtmlTagDefinition({ 18726 closedByChildren: ['address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'],
18727 closedByParent: true 18728 }), 18729 'thead': new HtmlTagDefinition({ 18730 closedByChildren: ['tbody', 'tfoot'] 18731 }), 18732 'tbody': new HtmlTagDefinition({ 18733 closedByChildren: ['tbody', 'tfoot'], 18734 closedByParent: true 18735 }), 18736 'tfoot': new HtmlTagDefinition({ 18737 closedByChildren: ['tbody'], 18738 closedByParent: true 18739 }), 18740 'tr': new HtmlTagDefinition({ 18741 closedByChildren: ['tr'], 18742 closedByParent: true 18743 }), 18744 'td': new HtmlTagDefinition({ 18745 closedByChildren: ['td', 'th'], 18746 closedByParent: true 18747 }), 18748 'th': new HtmlTagDefinition({ 18749 closedByChildren: ['td', 'th'], 18750 closedByParent: true 18751 }), 18752 'col': new HtmlTagDefinition({ 18753 isVoid: true 18754 }), 18755 'svg': new HtmlTagDefinition({ 18756 implicitNamespacePrefix: 'svg' 18757 }), 18758 'foreignObject': new HtmlTagDefinition({ 18759 // Usually the implicit namespace here would be redundant since it will be inherited from 18760 // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser 18761 // works is that the parent node of an end tag is its own start tag which means that 18762 // the `preventNamespaceInheritance` on `foreignObject` would have it default to the 18763 // implicit namespace which is `html`, unless specified otherwise. 18764 implicitNamespacePrefix: 'svg', 18765 // We want to prevent children of foreignObject from inheriting its namespace, because 18766 // the point of the element is to allow nodes from other namespaces to be inserted. 18767 preventNamespaceInheritance: true 18768 }), 18769 'math': new HtmlTagDefinition({ 18770 implicitNamespacePrefix: 'math' 18771 }), 18772 'li': new HtmlTagDefinition({ 18773 closedByChildren: ['li'], 18774 closedByParent: true 18775 }), 18776 'dt': new HtmlTagDefinition({ 18777 closedByChildren: ['dt', 'dd'] 18778 }), 18779 'dd': new HtmlTagDefinition({ 18780 closedByChildren: ['dt', 'dd'], 18781 closedByParent: true 18782 }), 18783 'rb': new HtmlTagDefinition({ 18784 closedByChildren: ['rb', 'rt', 'rtc', 'rp'], 18785 closedByParent: true 18786 }), 18787 'rt': new HtmlTagDefinition({ 18788 closedByChildren: ['rb', 'rt', 'rtc', 'rp'], 18789 closedByParent: true 18790 }), 18791 'rtc': new HtmlTagDefinition({ 18792 closedByChildren: ['rb', 'rtc', 'rp'], 18793 closedByParent: true 18794 }), 18795 'rp': new HtmlTagDefinition({ 18796 closedByChildren: ['rb', 'rt', 'rtc', 'rp'], 18797 closedByParent: true 18798 }), 18799 'optgroup': new HtmlTagDefinition({ 18800 closedByChildren: ['optgroup'], 18801 closedByParent: true 18802 }), 18803 'option': new HtmlTagDefinition({ 18804 closedByChildren: ['option', 'optgroup'], 18805 closedByParent: true 18806 }), 18807 'pre': new HtmlTagDefinition({ 18808 ignoreFirstLf: true 18809 }), 18810 'listing': new HtmlTagDefinition({ 18811 ignoreFirstLf: true 18812 }), 18813 'style': new HtmlTagDefinition({ 18814 contentType: TagContentType.RAW_TEXT 18815 }), 18816 'script': new HtmlTagDefinition({ 18817 contentType: TagContentType.RAW_TEXT 18818 }), 18819 'title': new HtmlTagDefinition({ 18820 // The browser supports two separate `title` tags which have to use 18821 // a different content type: `HTMLTitleElement` and `SVGTitleElement` 18822 contentType: { 18823 default: TagContentType.ESCAPABLE_RAW_TEXT, 18824 svg: TagContentType.PARSABLE_DATA 18825 } 18826 }), 18827 'textarea': new HtmlTagDefinition({ 18828 contentType: TagContentType.ESCAPABLE_RAW_TEXT, 18829 ignoreFirstLf: true 18830 }) 18831 }); 18832 new DomElementSchemaRegistry().allKnownElementNames().forEach(knownTagName => { 18833 if (!TAG_DEFINITIONS[knownTagName] && getNsPrefix(knownTagName) === null) { 18834 TAG_DEFINITIONS[knownTagName] = new HtmlTagDefinition({ 18835 canSelfClose: false 18836 }); 18837 } 18838 }); 18839 } 18840 // We have to make both a case-sensitive and a case-insensitive lookup, because 18841 // HTML tag names are case insensitive, whereas some SVG tags are case sensitive. 18842 return TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ?? DEFAULT_TAG_DEFINITION; 18843} 18844const TAG_TO_PLACEHOLDER_NAMES = { 18845 'A': 'LINK', 18846 'B': 'BOLD_TEXT', 18847 'BR': 'LINE_BREAK', 18848 'EM': 'EMPHASISED_TEXT', 18849 'H1': 'HEADING_LEVEL1', 18850 'H2': 'HEADING_LEVEL2', 18851 'H3': 'HEADING_LEVEL3', 18852 'H4': 'HEADING_LEVEL4', 18853 'H5': 'HEADING_LEVEL5', 18854 'H6': 'HEADING_LEVEL6', 18855 'HR': 'HORIZONTAL_RULE', 18856 'I': 'ITALIC_TEXT', 18857 'LI': 'LIST_ITEM', 18858 'LINK': 'MEDIA_LINK', 18859 'OL': 'ORDERED_LIST', 18860 'P': 'PARAGRAPH', 18861 'Q': 'QUOTATION', 18862 'S': 'STRIKETHROUGH_TEXT', 18863 'SMALL': 'SMALL_TEXT', 18864 'SUB': 'SUBSTRIPT', 18865 'SUP': 'SUPERSCRIPT', 18866 'TBODY': 'TABLE_BODY', 18867 'TD': 'TABLE_CELL', 18868 'TFOOT': 'TABLE_FOOTER', 18869 'TH': 'TABLE_HEADER_CELL', 18870 'THEAD': 'TABLE_HEADER', 18871 'TR': 'TABLE_ROW', 18872 'TT': 'MONOSPACED_TEXT', 18873 'U': 'UNDERLINED_TEXT', 18874 'UL': 'UNORDERED_LIST' 18875}; 18876/**
18877 * Creates unique names for placeholder with different content. 18878 * 18879 * Returns the same placeholder name when the content is identical. 18880 */ 18881class PlaceholderRegistry { 18882 constructor() { 18883 // Count the occurrence of the base name top generate a unique name 18884 this._placeHolderNameCounts = {}; 18885 // Maps signature to placeholder names 18886 this._signatureToName = {}; 18887 } 18888 getStartTagPlaceholderName(tag, attrs, isVoid) { 18889 const signature = this._hashTag(tag, attrs, isVoid); 18890 if (this._signatureToName[signature]) { 18891 return this._signatureToName[signature]; 18892 } 18893 const upperTag = tag.toUpperCase(); 18894 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`; 18895 const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`); 18896 this._signatureToName[signature] = name; 18897 return name; 18898 } 18899 getCloseTagPlaceholderName(tag) { 18900 const signature = this._hashClosingTag(tag); 18901 if (this._signatureToName[signature]) { 18902 return this._signatureToName[signature]; 18903 } 18904 const upperTag = tag.toUpperCase(); 18905 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`; 18906 const name = this._generateUniqueName(`CLOSE_${baseName}`); 18907 this._signatureToName[signature] = name; 18908 return name; 18909 } 18910 getPlaceholderName(name, content) { 18911 const upperName = name.toUpperCase(); 18912 const signature = `PH: ${upperName}=${content}`; 18913 if (this._signatureToName[signature]) { 18914 return this._signatureToName[signature]; 18915 } 18916 const uniqueName = this._generateUniqueName(upperName); 18917 this._signatureToName[signature] = uniqueName; 18918 return uniqueName; 18919 } 18920 getUniquePlaceholder(name) { 18921 return this._generateUniqueName(name.toUpperCase()); 18922 } 18923 getStartBlockPlaceholderName(name, parameters) { 18924 const signature = this._hashBlock(name, parameters); 18925 if (this._signatureToName[signature]) { 18926 return this._signatureToName[signature]; 18927 } 18928 const placeholder = this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(name)}`); 18929 this._signatureToName[signature] = placeholder; 18930 return placeholder; 18931 } 18932 getCloseBlockPlaceholderName(name) { 18933 const signature = this._hashClosingBlock(name); 18934 if (this._signatureToName[signature]) { 18935 return this._signatureToName[signature]; 18936 } 18937 const placeholder = this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(name)}`); 18938 this._signatureToName[signature] = placeholder; 18939 return placeholder; 18940 } 18941 // Generate a hash for a tag - does not take attribute order into account 18942 _hashTag(tag, attrs, isVoid) { 18943 const start = `<${tag}`; 18944 const strAttrs = Object.keys(attrs).sort().map(name => ` ${name}=${attrs[name]}`).join(''); 18945 const end = isVoid ? '/>' : `></${tag}>`; 18946 return start + strAttrs + end; 18947 } 18948 _hashClosingTag(tag) { 18949 return this._hashTag(`/${tag}`, {}, false); 18950 } 18951 _hashBlock(name, parameters) { 18952 const params = parameters.length === 0 ? '' : ` (${parameters.sort().join('; ')})`; 18953 return `@${name}${params} {}`; 18954 } 18955 _hashClosingBlock(name) { 18956 return this._hashBlock(`close_${name}`, []); 18957 } 18958 _toSnakeCase(name) { 18959 return name.toUpperCase().replace(/[^A-Z0-9]/g, '_'); 18960 } 18961 _generateUniqueName(base) { 18962 const seen = this._placeHolderNameCounts.hasOwnProperty(base); 18963 if (!seen) { 18964 this._placeHolderNameCounts[base] = 1; 18965 return base; 18966 } 18967 const id = this._placeHolderNameCounts[base]; 18968 this._placeHolderNameCounts[base] = id + 1; 18969 return `${base}_${id}`; 18970 } 18971} 18972const _expParser = new Parser$1(new Lexer()); 18973/** 18974 * Returns a function converting html nodes to an i18n Message given an interpolationConfig 18975 */ 18976function createI18nMessageFactory(interpolationConfig, containerBlocks) { 18977 const visitor = new _I18nVisitor(_expParser, interpolationConfig, containerBlocks); 18978 return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn); 18979} 18980function noopVisitNodeFn(_html, i18n) { 18981 return i18n; 18982} 18983class _I18nVisitor { 18984 constructor(_expressionParser, _interpolationConfig, _containerBlocks) { 18985 this._expressionParser = _expressionParser; 18986 this._interpolationConfig = _interpolationConfig; 18987 this._containerBlocks = _containerBlocks; 18988 } 18989 toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) { 18990 const context = { 18991 isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
18992 icuDepth: 0, 18993 placeholderRegistry: new PlaceholderRegistry(), 18994 placeholderToContent: {}, 18995 placeholderToMessage: {}, 18996 visitNodeFn: visitNodeFn || noopVisitNodeFn 18997 }; 18998 const i18nodes = visitAll(this, nodes, context); 18999 return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId); 19000 } 19001 visitElement(el, context) { 19002 const children = visitAll(this, el.children, context); 19003 const attrs = {}; 19004 el.attrs.forEach(attr => { 19005 // Do not visit the attributes, translatable ones are top-level ASTs 19006 attrs[attr.name] = attr.value; 19007 }); 19008 const isVoid = getHtmlTagDefinition(el.name).isVoid; 19009 const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid); 19010 context.placeholderToContent[startPhName] = { 19011 text: el.startSourceSpan.toString(), 19012 sourceSpan: el.startSourceSpan 19013 }; 19014 let closePhName = ''; 19015 if (!isVoid) { 19016 closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name); 19017 context.placeholderToContent[closePhName] = { 19018 text: `</${el.name}>`, 19019 sourceSpan: el.endSourceSpan ?? el.sourceSpan 19020 }; 19021 } 19022 const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan); 19023 return context.visitNodeFn(el, node); 19024 } 19025 visitAttribute(attribute, context) { 19026 const node = attribute.valueTokens === undefined || attribute.valueTokens.length === 1 ? new Text$2(attribute.value, attribute.valueSpan || attribute.sourceSpan) : this._visitTextWithInterpolation(attribute.valueTokens, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n); 19027 return context.visitNodeFn(attribute, node); 19028 } 19029 visitText(text, context) { 19030 const node = text.tokens.length === 1 ? new Text$2(text.value, text.sourceSpan) : this._visitTextWithInterpolation(text.tokens, text.sourceSpan, context, text.i18n); 19031 return context.visitNodeFn(text, node); 19032 } 19033 visitComment(comment, context) { 19034 return null; 19035 } 19036 visitExpansion(icu, context) { 19037 context.icuDepth++; 19038 const i18nIcuCases = {}; 19039 const i18nIcu = new Icu(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan); 19040 icu.cases.forEach(caze => { 19041 i18nIcuCases[caze.value] = new Container(caze.expression.map(node => node.visit(this, context)), caze.expSourceSpan); 19042 }); 19043 context.icuDepth--; 19044 if (context.isIcu || context.icuDepth > 0) { 19045 // Returns an ICU node when: 19046 // - the message (vs a part of the message) is an ICU message, or 19047 // - the ICU message is nested. 19048 const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`); 19049 i18nIcu.expressionPlaceholder = expPh; 19050 context.placeholderToContent[expPh] = { 19051 text: icu.switchValue, 19052 sourceSpan: icu.switchValueSourceSpan 19053 }; 19054 return context.visitNodeFn(icu, i18nIcu); 19055 } 19056 // Else returns a placeholder 19057 // ICU placeholders should not be replaced with their original content but with the their 19058 // translations. 19059 // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg 19060 const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString()); 19061 context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined); 19062 const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan); 19063 return context.visitNodeFn(icu, node); 19064 } 19065 visitExpansionCase(_icuCase, _context) { 19066 throw new Error('Unreachable code'); 19067 } 19068 visitBlock(block, context) { 19069 const children = visitAll(this, block.children, context); 19070 if (this._containerBlocks.has(block.name)) { 19071 return new Container(children, block.sourceSpan); 19072 } 19073 const parameters = block.parameters.map(param => param.expression); 19074 const startPhName = context.placeholderRegistry.getStartBlockPlaceholderName(block.name, parameters); 19075 const closePhName = context.placeholderRegistry.getCloseBlockPlaceholderName(block.name); 19076 context.placeholderToContent[startPhName] = { 19077 text: block.startSourceSpan.toString(), 19078 sourceSpan: block.startSourceSpan 19079 }; 19080 context.placeholderToContent[closePhName] = { 19081 text: block.endSourceSpan ? block.endSourceSpan.toString() : '}', 19082 sourceSpan: block.endSourceSpan ?? block.sourceSpan 19083 }; 19084 const node = new BlockPlaceholder(block.name, parameters, startPhName, closePhName, children, block.sourceSpan, block.startSourceSpan, block.endSourceSpan); 19085 return context.visitNodeFn(block, node); 19086 } 19087 visitBlockParameter(_parameter, _context) { 19088 throw new Error('Unreachable code'); 19089 } 19090 /** 19091 * Convert, text and interpolated tokens up into text and placeholder pieces. 19092 * 19093 * @param tokens The text and interpolated tokens. 19094 * @param sourceSpan The span of the whole of the `text` string. 19095 * @param context The current context of the visitor, used to compute and store placeholders. 19096 * @param previousI18n Any i18n metadata associated with this `text` from a previous pass. 19097 */ 19098 _visitTextWithInterpolation(tokens, sourceSpan, context, previousI18n) { 19099 // Return a sequence of `Text` and `Placeholder` nodes grouped in a `C
19099ontainer`. 19100 const nodes = []; 19101 // We will only create a container if there are actually interpolations, 19102 // so this flag tracks that. 19103 let hasInterpolation = false; 19104 for (const token of tokens) { 19105 switch (token.type) { 19106 case 8 /* TokenType.INTERPOLATION */: 19107 case 17 /* TokenType.ATTR_VALUE_INTERPOLATION */: 19108 hasInterpolation = true; 19109 const expression = token.parts[1]; 19110 const baseName = extractPlaceholderName(expression) || 'INTERPOLATION'; 19111 const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression); 19112 context.placeholderToContent[phName] = { 19113 text: token.parts.join(''), 19114 sourceSpan: token.sourceSpan 19115 }; 19116 nodes.push(new Placeholder(expression, phName, token.sourceSpan)); 19117 break; 19118 default: 19119 if (token.parts[0].length > 0) { 19120 // This token is text or an encoded entity. 19121 // If it is following on from a previous text node then merge it into that node 19122 // Otherwise, if it is following an interpolation, then add a new node. 19123 const previous = nodes[nodes.length - 1]; 19124 if (previous instanceof Text$2) { 19125 previous.value += token.parts[0]; 19126 previous.sourceSpan = new ParseSourceSpan(previous.sourceSpan.start, token.sourceSpan.end, previous.sourceSpan.fullStart, previous.sourceSpan.details); 19127 } else { 19128 nodes.push(new Text$2(token.parts[0], token.sourceSpan)); 19129 } 19130 } 19131 break; 19132 } 19133 } 19134 if (hasInterpolation) { 19135 // Whitespace removal may have invalidated the interpolation source-spans. 19136 reusePreviousSourceSpans(nodes, previousI18n); 19137 return new Container(nodes, sourceSpan); 19138 } else { 19139 return nodes[0]; 19140 } 19141 } 19142} 19143/** 19144 * Re-use the source-spans from `previousI18n` metadata for the `nodes`. 19145 * 19146 * Whitespace removal can invalidate the source-spans of interpolation nodes, so we 19147 * reuse the source-span stored from a previous pass before the whitespace was removed. 19148 * 19149 * @param nodes The `Text` and `Placeholder` nodes to be processed. 19150 * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass. 19151 */ 19152function reusePreviousSourceSpans(nodes, previousI18n) { 19153 if (previousI18n instanceof Message) { 19154 // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n 19155 // metadata. The `Message` should consist only of a single `Container` that contains the 19156 // parts (`Text` and `Placeholder`) to process. 19157 assertSingleContainerMessage(previousI18n); 19158 previousI18n = previousI18n.nodes[0]; 19159 } 19160 if (previousI18n instanceof Container) { 19161 // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass 19162 // after whitespace has been removed from the AST nodes. 19163 assertEquivalentNodes(previousI18n.children, nodes); 19164 // Reuse the source-spans from the first pass. 19165 for (let i = 0; i < nodes.length; i++) { 19166 nodes[i].sourceSpan = previousI18n.children[i].sourceSpan; 19167 } 19168 } 19169} 19170/** 19171 * Asserts that the `message` contains exactly one `Container` node. 19172 */ 19173function assertSingleContainerMessage(message) { 19174 const nodes = message.nodes; 19175 if (nodes.length !== 1 || !(nodes[0] instanceof Container)) { 19176 throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.'); 19177 } 19178} 19179/** 19180 * Asserts that the `previousNodes` and `node` collections have the same number of elements and 19181 * corresponding elements have the same node type. 19182 */ 19183function assertEquivalentNodes(previousNodes, nodes) { 19184 if (previousNodes.length !== nodes.length) { 19185 throw new Error('The number of i18n message children changed between first and second pass.'); 19186 } 19187 if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) { 19188 throw new Error('The types of the i18n message children changed between first and second pass.'); 19189 } 19190} 19191const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g; 19192function extractPlaceholderName(input) { 19193 return input.split(_CUSTOM_PH_EXP)[2]; 19194} 19195 19196/** 19197 * An i18n error. 19198 */ 19199class I18nError extends ParseError { 19200 constructor(span, msg) { 19201 super(span, msg); 19202 } 19203} 19204 19205// Mapping between all HTML entity names and their unicode representation. 19206// Generated from https://html.spec.whatwg.org/multipage/entities.json by stripping 19207// the `&` and `;` from the keys and removing the duplicates. 19208// see https://www.w3.org/TR/html51/syntax.html#named-character-references 19209const NAMED_ENTITIES = { 19210 'AElig': '\u00C6', 19211 'AMP': '\u0026', 19212 'amp': '\u0026', 19213 'Aacute': '\u00C1', 19214 'Abreve': '\u0102', 19215 'Acirc': '\u00C2', 19216 'Acy': '\u0410', 19217 'Afr': '\uD835\uDD04', 19218 'Agrave': '\u00C0', 19219 'Alpha': '\u0391', 19220 'Amacr': '\u0100', 19221 'And': '\u2A53', 19222 'Aogon': '\u0104', 19223 'Aopf': '\uD835\uDD38', 19224 'ApplyFunction': '\u2061', 19225 'af': '\u2061', 19226 'Aring': '\u00C5', 19227 'angst': '\u00C5', 19228 'Ascr': '\uD835\uDC9C', 19229 'Assign': '\u2254', 19230 'colone': '\u2254', 19231 'coloneq': '\u2254', 19232 'Atilde': '\u00C3', 19233 'Auml': '\u00C4', 19234 'Backslash': '\u2216', 19235 'setminus': '\u2216', 19236 'setmn': '\u2216', 19237 'smallsetminus': '\u2216', 19238 'ssetmn': '\u2216', 19239 'Barv': '\u2AE7', 19240 'Barwed': '\u2306', 19241 'doublebarwedge': '\u2306', 19242 'Bcy': '\u0411', 19243 'Because': '\u2235', 19244 'becaus': '\u2235', 19245 'because': '\u2235', 19246 'Bernoullis': '\u212C', 19247 'Bscr': '\u212C', 19248 'bernou': '\u212C', 19249 'Beta': '\u0392', 19250 'Bfr': '\uD835\uDD05', 19251 'Bopf': '\uD835\uDD39', 19252 'Breve': '\u02D8', 19253 'breve': '\u02D8', 19254 'Bumpeq': '\u224E', 19255 'HumpDownHump': '\u224E', 19256 'bump': '\u224E', 19257 'CHcy': '\u0427', 19258 'COPY': '\u00A9', 19259 'copy': '\u00A9', 19260 'Cacute': '\u0106', 19261 'Cap': '\u22D2', 19262 'CapitalDifferentialD': '\u2145', 19263 'DD': '\u2145', 19264 'Cayleys': '\u212D', 19265 'Cfr': '\u212D', 19266 'Ccaron': '\u010C', 19267 'Ccedil': '\u00C7', 19268 'Ccirc': '\u0108', 19269 'Cconint': '\u2230', 19270 'Cdot': '\u010A', 19271 'Cedilla': '\u00B8', 19272 'cedil': '\u00B8', 19273 'CenterDot': '\u00B7', 19274 'centerdot': '\u00B7', 19275 'middot': '\u00B7', 19276 'Chi': '\u03A7', 19277 'CircleDot': '\u2299', 19278 'odot': '\u2299', 19279 'CircleMinus': '\u2296', 19280 'ominus': '\u2296', 19281 'CirclePlus': '\u2295', 19282 'oplus': '\u2295', 19283 'CircleTimes': '\u2297', 19284 'otimes': '\u2297', 19285 'ClockwiseContourIntegral': '\u2232', 19286 'cwconint': '\u2232', 19287 'CloseCurlyDoubleQuote': '\u201D', 19288 'rdquo': '\u201D', 19289 'rdquor': '\u201D', 19290 'CloseCurlyQuote': '\u2019', 19291 'rsquo': '\u2019', 19292 'rsquor': '\u2019', 19293 'Colon': '\u2237', 19294 'Proportion': '\u2237', 19295 'Colone': '\u2A74', 19296 'Congruent': '\u2261', 19297 'equiv': '\u2261', 19298 'Conint': '\u222F', 19299 'DoubleContourIntegral': '\u222F', 19300 'ContourIntegral': '\u222E', 19301 'conint': '\u222E', 19302 'oint': '\u222E', 19303 'Copf': '\u2102', 19304 'complexes': '\u2102', 19305 'Coproduct': '\u2210', 19306 'coprod': '\u2210', 19307 'CounterClockwiseContourIntegral': '\u2233', 19308 'awconint': '\u2233', 19309 'Cross': '\u2A2F', 19310 'Cscr': '\uD835\uDC9E', 19311 'Cup': '\u22D3', 19312 'CupCap': '\u224D', 19313 'asympeq': '\u224D', 19314 'DDotrahd': '\u2911', 19315 'DJcy': '\u0402', 19316 'DScy': '\u0405', 19317 'DZcy': '\u040F', 19318 'Dagger': '\u2021', 19319 'ddagger': '\u2021', 19320 'Darr': '\u21A1', 19321 'Dashv': '\u2AE4', 19322 'DoubleLeftTee': '\u2AE4', 19323 'Dcaron': '\u010E', 19324 'Dcy': '\u0414', 19325 'Del': '\u2207', 19326 'nabla': '\u2207', 19327 'Delta': '\u0394', 19328 'Dfr': '\uD835\uDD07', 19329 'DiacriticalAcute': '\u00B4', 19330 'acute': '\u00B4', 19331 'DiacriticalDot': '\u02D9', 19332 'dot': '\u02D9', 19333 'DiacriticalDoubleAcute': '\u02DD', 19334 'dblac': '\u02DD', 19335 'DiacriticalGrave': '\u0060', 19336 'grave': '\u0060', 19337 'DiacriticalTilde': '\u02DC', 19338 'tilde': '\u02DC', 19339 'Diamond': '\u22C4', 19340 'diam': '\u22C4', 19341 'diamond': '\u22C4', 19342 'DifferentialD': '\u2146', 19343 'dd': '\u2146', 19344 'Dopf': '\uD835\uDD3B', 19345 'Dot': '\u00A8', 19346 'DoubleDot': '\u00A8', 19347 'die': '\u00A8', 19348 'uml': '\u00A8', 19349 'DotDot': '\u20DC', 19350 'DotEqual': '\u2250', 19351 'doteq': '\u2250', 19352 'esdot': '\u2250', 19353 'DoubleDownArrow': '\u21D3', 19354 'Downarrow': '\u21D3', 19355 'dArr': '\u21D3', 19356 'DoubleLeftArrow': '\u21D0', 19357 'Leftarrow': '\u21D0', 19358 'lArr': '\u21D0', 19359 'DoubleLeftRightArrow': '\u21D4', 19360 'Leftrightarrow': '\u21D4', 19361 'hArr': '\u21D4', 19362 'iff': '\u21D4', 19363 'DoubleLongLeftArrow': '\u27F8', 19364 'Longleftarrow': '\u27F8', 19365 'xlArr': '\u27F8', 19366 'DoubleLongLeftRightArrow': '\u27FA', 19367 'Longleftrightarrow': '\u27FA', 19368 'xhArr': '\u27FA', 19369 'DoubleLongRightArrow': '\u27F9', 19370 'Longrightarrow': '\u27F9', 19371 'xrArr': '\u27F9', 19372 'DoubleRightArrow': '\u21D2', 19373 'Implies': '\u21D2', 19374 'Rightarrow': '\u21D2', 19375 'rArr': '\u21D2', 19376 'DoubleRightTee': '\u22A8', 19377 'vDash': '\u22A8', 19378 'DoubleUpArrow': '\u21D1', 19379 'Uparrow': '\u21D1', 19380 'uArr': '\u21D1', 19381 'DoubleUpDownArrow': '\u21D5', 19382 'Updownarrow': '\u21D5', 19383 'vArr': '\u21D5', 19384 'DoubleVerticalBar': '\u2225', 19385 'par': '\u2225', 19386 'parallel': '\u2225', 19387 'shortparallel': '\u2225', 19388 'spar': '\u2225', 19389 'DownArrow': '\u2193', 19390 'ShortDownArrow': '\u2193', 19391 'darr': '\u2193', 19392 'downarrow': '\u2193', 19393 'DownArrowBar': '\u2913', 19394 'DownArrowUpArrow': '\u21F5', 19395 'duarr': '\u21F5', 19396 'DownBreve': '\u0311', 19397 'DownLeftRightVector': '\u2950', 19398 'DownLeftTeeVector': '\u295E', 19399 'DownLeftVector': '\u21BD', 19400 'leftharpoondown': '\u21BD', 19401 'lhard': '\u21BD', 19402 'DownLeftVectorBar': '\u2956', 19403 'DownRightTeeVector': '\u295F', 19404 'DownRightVector': '\u21C1', 19405 'rhard': '\u21C1', 19406 'rightharpoondown': '\u21C1', 19407 'DownRightVectorBar': '\u2957', 19408 'DownTee': '\u22A4', 19409 'top': '\u22A4', 19410 'DownTeeArrow': '\u21A7', 19411 'mapstodown': '\u21A7', 19412 'Dscr': '\uD835\uDC9F', 19413 'Dstrok': '\u0110', 19414 'ENG': '\u014A', 19415 'ETH': '\u00D0', 19416 'Eacute': '\u00C9', 19417 'Ecaron': '\u011A', 19418 'Ecirc': '\u00CA', 19419 'Ecy': '\u042D', 19420 'Edot': '\u0116', 19421 'Efr': '\uD835\uDD08', 19422 'Egrave': '\u00C8', 19423 'Element': '\u2208', 19424 'in': '\u2208', 19425 'isin': '\u2208', 19426 'isinv': '\u2208', 19427 'Emacr': '\u0112', 19428 'EmptySmallSquare': '\u25FB', 19429 'EmptyVerySmallSquare': '\u25AB', 19430 'Eogon': '\u0118', 19431 'Eopf': '\uD835\uDD3C', 19432 'Epsilon': '\u0395', 19433 'Equal': '\u2A75', 19434 'EqualTilde': '\u2242', 19435 'eqsim': '\u2242', 19436 'esim': '\u2242', 19437 'Equilibrium': '\u21CC', 19438 'rightleftharpoons': '\u21CC', 19439 'rlhar': '\u21CC', 19440 'Escr': '\u2130', 19441 'expectation': '\u2130', 19442 'Esim': '\u2A73', 19443 'Eta': '\u0397', 19444 'Euml': '\u00CB', 19445 'Exists': '\u2203', 19446 'exist': '\u2203', 19447 'ExponentialE': '\u2147', 19448 'ee': '\u2147', 19449 'exponentiale': '\u2147', 19450 'Fcy': '\u0424', 19451 'Ffr': '\uD835\uDD09', 19452 'FilledSmallSquare': '\u25FC', 19453 'FilledVerySmallSquare': '\u25AA', 19454 'blacksquare': '\u25AA', 19455 'squarf': '\u25AA', 19456 'squf': '\u25AA', 19457 'Fopf': '\uD835\uDD3D', 19458 'ForAll': '\u2200', 19459 'forall': '\u2200', 19460 'Fouriertrf': '\u2131', 19461 'Fscr': '\u2131', 19462 'GJcy': '\u0403', 19463 'GT': '\u003E', 19464 'gt': '\u003E', 19465 'Gamma': '\u0393', 19466 'Gammad': '\u03DC', 19467 'Gbreve': '\u011E', 19468 'Gcedil': '\u0122', 19469 'Gcirc': '\u011C', 19470 'Gcy': '\u0413', 19471 'Gdot': '\u0120', 19472 'Gfr': '\uD835\uDD0A', 19473 'Gg': '\u22D9', 19474 'ggg': '\u22D9', 19475 'Gopf': '\uD835\uDD3E', 19476 'GreaterEqual': '\u2265', 19477 'ge': '\u2265', 19478 'geq': '\u2265', 19479 'GreaterEqualLess': '\u22DB', 19480 'gel': '\u22DB', 19481 'gtreqless': '\u22DB', 19482 'GreaterFullEqual': '\u2267', 19483 'gE': '\u2267', 19484 'geqq': '\u2267', 19485 'GreaterGreater': '\u2AA2', 19486 'GreaterLess': '\u2277', 19487 'gl': '\u2277', 19488 'gtrless': '\u2277', 19489 'GreaterSlantEqual': '\u2A7E', 19490 'geqslant': '\u2A7E', 19491 'ges': '\u2A7E', 19492 'GreaterTilde': '\u2273', 19493 'gsim': '\u2273', 19494 'gtrsim': '\u2273', 19495 'Gscr': '\uD835\uDCA2', 19496 'Gt': '\u226B', 19497 'NestedGreaterGreater': '\u226B', 19498 'gg': '\u226B', 19499 'HARDcy': '\u042A', 19500 'Hacek': '\u02C7', 19501 'caron': '\u02C7', 19502 'Hat': '\u005E', 19503 'Hcirc': '\u0124', 19504 'Hfr': '\u210C', 19505 'Poincareplane': '\u210C', 19506 'HilbertSpace': '\u210B', 19507 'Hscr': '\u210B', 19508 'hamilt': '\u210B', 19509 'Hopf': '\u210D', 19510 'quaternions': '\u210D', 19511 'HorizontalLine': '\u2500', 19512 'boxh': '\u2500', 19513 'Hstrok': '\u0126', 19514 'HumpEqual': '\u224F', 19515 'bumpe': '\u224F', 19516 'bumpeq': '\u224F', 19517 'IEcy': '\u0415', 19518 'IJlig': '\u0132', 19519 'IOcy': '\u0401', 19520 'Iacute': '\u00CD', 19521 'Icirc': '\u00CE', 19522 'Icy': '\u0418', 19523 'Idot': '\u0130', 19524 'Ifr': '\u2111', 19525 'Im': '\u2111', 19526 'image': '\u2111', 19527 'imagpart': '\u2111', 19528 'Igrave': '\u00CC', 19529 'Imacr': '\u012A', 19530 'ImaginaryI': '\u2148', 19531 'ii': '\u2148', 19532 'Int': '\u222C', 19533 'Integral': '\u222B', 19534 'int': '\u222B', 19535 'Intersection': '\u22C2', 19536 'bigcap': '\u22C2', 19537 'xcap': '\u22C2', 19538 'InvisibleComma': '\u2063', 19539 'ic': '\u2063', 19540 'InvisibleTimes': '\u2062', 19541 'it': '\u2062', 19542 'Iogon': '\u012E', 19543 'Iopf': '\uD835\uDD40', 19544 'Iota': '\u0399', 19545 'Iscr': '\u2110', 19546 'imagline': '\u2110', 19547 'Itilde': '\u0128', 19548 'Iukcy': '\u0406', 19549 'Iuml': '\u00CF', 19550 'Jcirc': '\u0134', 19551 'Jcy': '\u0419', 19552 'Jfr': '\uD835\uDD0D', 19553 'Jopf': '\uD835\uDD41', 19554 'Jscr': '\uD835\uDCA5', 19555 'Jsercy': '\u0408', 19556 'Jukcy': '\u0404', 19557 'KHcy': '\u0425', 19558 'KJcy': '\u040C', 19559 'Kappa': '\u039A', 19560 'Kcedil': '\u0136', 19561 'Kcy': '\u041A', 19562 'Kfr': '\uD835\uDD0E', 19563 'Kopf': '\uD835\uDD42', 19564 'Kscr': '\uD835\uDCA6', 19565 'LJcy': '\u0409', 19566 'LT': '\u003C', 19567 'lt': '\u003C', 19568 'Lacute': '\u0139', 19569 'Lambda': '\u039B', 19570 'Lang': '\u27EA', 19571 'Laplacetrf': '\u2112', 19572 'Lscr': '\u2112', 19573 'lagran': '\u2112', 19574 'Larr': '\u219E', 19575 'twoheadleftarrow': '\u219E', 19576 'Lcaron': '\u013D', 19577 'Lcedil': '\u013B', 19578 'Lcy': '\u041B', 19579 'LeftAngleBracket': '\u27E8', 19580 'lang': '\u27E8', 19581 'langle': '\u27E8', 19582 'LeftArrow': '\u2190', 19583 'ShortLeftArrow': '\u2190', 19584 'larr': '\u2190', 19585 'leftarrow': '\u2190', 19586 'slarr': '\u2190', 19587 'LeftArrowBar': '\u21E4', 19588 'larrb': '\u21E4', 19589 'LeftArrowRightArrow': '\u21C6', 19590 'leftrightarrows': '\u21C6', 19591 'lrarr': '\u21C6', 19592 'LeftCeiling': '\u2308', 19593 'lceil': '\u2308', 19594 'LeftDoubleBracket': '\u27E6', 19595 'lobrk': '\u27E6', 19596 'LeftDownTeeVector': '\u2961', 19597 'LeftDownVector': '\u21C3', 19598 'dharl': '\u21C3', 19599 'downharpoonleft': '\u21C3', 19600 'LeftDownVectorBar': '\u2959', 19601 'LeftFloor': '\u230A', 19602 'lfloor': '\u230A', 19603 'LeftRightArrow': '\u2194', 19604 'harr': '\u2194', 19605 'leftrightarrow': '\u2194', 19606 'LeftRightVector': '\u294E', 19607 'LeftTee': '\u22A3', 19608 'dashv': '\u22A3', 19609 'LeftTeeArrow': '\u21A4', 19610 'mapstoleft': '\u21A4', 19611 'LeftTeeVector': '\u295A', 19612 'LeftTriangle': '\u22B2', 19613 'vartriangleleft': '\u22B2', 19614 'vltri': '\u22B2', 19615 'LeftTriangleBar': '\u29CF', 19616 'LeftTriangleEqual': '\u22B4', 19617 'ltrie': '\u22B4', 19618 'trianglelefteq': '\u22B4', 19619 'LeftUpDownVector': '\u2951', 19620 'LeftUpTeeVector': '\u2960', 19621 'LeftUpVector': '\u21BF', 19622 'uharl': '\u21BF', 19623 'upharpoonleft': '\u21BF', 19624 'LeftUpVectorBar': '\u2958', 19625 'LeftVector': '\u21BC', 19626 'leftharpoonup': '\u21BC', 19627 'lharu': '\u21BC', 19628 'LeftVectorBar': '\u2952', 19629 'LessEqualGreater': '\u22DA', 19630 'leg': '\u22DA', 19631 'lesseqgtr': '\u22DA', 19632 'LessFullEqual': '\u2266', 19633 'lE': '\u2266', 19634 'leqq': '\u2266', 19635 'LessGreater': '\u2276', 19636 'lessgtr': '\u2276', 19637 'lg': '\u2276', 19638 'LessLess': '\u2AA1', 19639 'LessSlantEqual': '\u2A7D', 19640 'leqslant': '\u2A7D', 19641 'les': '\u2A7D', 19642 'LessTilde': '\u2272', 19643 'lesssim': '\u2272', 19644 'lsim': '\u2272', 19645 'Lfr': '\uD835\uDD0F', 19646 'Ll': '\u22D8', 19647 'Lleftarrow': '\u21DA', 19648 'lAarr': '\u21DA', 19649 'Lmidot': '\u013F', 19650 'LongLeftArrow': '\u27F5', 19651 'longleftarrow': '\u27F5', 19652 'xlarr': '\u27F5', 19653 'LongLeftRightArrow': '\u27F7', 19654 'longleftrightarrow': '\u27F7', 19655 'xharr': '\u27F7', 19656 'LongRightArrow': '\u27F6', 19657 'longrightarrow': '\u27F6', 19658 'xrarr': '\u27F6', 19659 'Lopf': '\uD835\uDD43', 19660 'LowerLeftArrow': '\u2199', 19661 'swarr': '\u2199', 19662 'swarrow': '\u2199', 19663 'LowerRightArrow': '\u2198', 19664 'searr': '\u2198', 19665 'searrow': '\u2198', 19666 'Lsh': '\u21B0', 19667 'lsh': '\u21B0', 19668 'Lstrok': '\u0141', 19669 'Lt': '\u226A', 19670 'NestedLessLess': '\u226A', 19671 'll': '\u226A', 19672 'Map': '\u2905', 19673 'Mcy': '\u041C', 19674 'MediumSpace': '\u205F', 19675 'Mellintrf': '\u2133', 19676 'Mscr': '\u2133', 19677 'phmmat': '\u2133', 19678 'Mfr': '\uD835\uDD10', 19679 'MinusPlus': '\u2213', 19680 'mnplus': '\u2213', 19681 'mp': '\u2213', 19682 'Mopf': '\uD835\uDD44', 19683 'Mu': '\u039C', 19684 'NJcy': '\u040A', 19685 'Nacute': '\u0143', 19686 'Ncaron': '\u0147', 19687 'Ncedil': '\u0145', 19688 'Ncy': '\u041D', 19689 'NegativeMediumSpace': '\u200B', 19690 'NegativeThickSpace': '\u200B', 19691 'NegativeThinSpace': '\u200B', 19692 'NegativeVeryThinSpace': '\u200B', 19693 'ZeroWidthSpace': '\u200B', 19694 'NewLine': '\u000A', 19695 'Nfr': '\uD835\uDD11', 19696 'NoBreak': '\u2060', 19697 'NonBreakingSpace': '\u00A0', 19698 'nbsp': '\u00A0',
19698 19699 'Nopf': '\u2115', 19700 'naturals': '\u2115', 19701 'Not': '\u2AEC', 19702 'NotCongruent': '\u2262', 19703 'nequiv': '\u2262', 19704 'NotCupCap': '\u226D', 19705 'NotDoubleVerticalBar': '\u2226', 19706 'npar': '\u2226', 19707 'nparallel': '\u2226', 19708 'nshortparallel': '\u2226', 19709 'nspar': '\u2226', 19710 'NotElement': '\u2209', 19711 'notin': '\u2209', 19712 'notinva': '\u2209', 19713 'NotEqual': '\u2260', 19714 'ne': '\u2260', 19715 'NotEqualTilde': '\u2242\u0338', 19716 'nesim': '\u2242\u0338', 19717 'NotExists': '\u2204', 19718 'nexist': '\u2204', 19719 'nexists': '\u2204', 19720 'NotGreater': '\u226F', 19721 'ngt': '\u226F', 19722 'ngtr': '\u226F', 19723 'NotGreaterEqual': '\u2271', 19724 'nge': '\u2271', 19725 'ngeq': '\u2271', 19726 'NotGreaterFullEqual': '\u2267\u0338', 19727 'ngE': '\u2267\u0338', 19728 'ngeqq': '\u2267\u0338', 19729 'NotGreaterGreater': '\u226B\u0338', 19730 'nGtv': '\u226B\u0338', 19731 'NotGreaterLess': '\u2279', 19732 'ntgl': '\u2279', 19733 'NotGreaterSlantEqual': '\u2A7E\u0338', 19734 'ngeqslant': '\u2A7E\u0338', 19735 'nges': '\u2A7E\u0338', 19736 'NotGreaterTilde': '\u2275', 19737 'ngsim': '\u2275', 19738 'NotHumpDownHump': '\u224E\u0338', 19739 'nbump': '\u224E\u0338', 19740 'NotHumpEqual': '\u224F\u0338', 19741 'nbumpe': '\u224F\u0338', 19742 'NotLeftTriangle': '\u22EA', 19743 'nltri': '\u22EA', 19744 'ntriangleleft': '\u22EA', 19745 'NotLeftTriangleBar': '\u29CF\u0338', 19746 'NotLeftTriangleEqual': '\u22EC', 19747 'nltrie': '\u22EC', 19748 'ntrianglelefteq': '\u22EC', 19749 'NotLess': '\u226E', 19750 'nless': '\u226E', 19751 'nlt': '\u226E', 19752 'NotLessEqual': '\u2270', 19753 'nle': '\u2270', 19754 'nleq': '\u2270', 19755 'NotLessGreater': '\u2278', 19756 'ntlg': '\u2278', 19757 'NotLessLess': '\u226A\u0338', 19758 'nLtv': '\u226A\u0338', 19759 'NotLessSlantEqual': '\u2A7D\u0338', 19760 'nleqslant': '\u2A7D\u0338', 19761 'nles': '\u2A7D\u0338', 19762 'NotLessTilde': '\u2274', 19763 'nlsim': '\u2274', 19764 'NotNestedGreaterGreater': '\u2AA2\u0338', 19765 'NotNestedLessLess': '\u2AA1\u0338', 19766 'NotPrecedes': '\u2280', 19767 'npr': '\u2280', 19768 'nprec': '\u2280', 19769 'NotPrecedesEqual': '\u2AAF\u0338', 19770 'npre': '\u2AAF\u0338', 19771 'npreceq': '\u2AAF\u0338', 19772 'NotPrecedesSlantEqual': '\u22E0', 19773 'nprcue': '\u22E0', 19774 'NotReverseElement': '\u220C', 19775 'notni': '\u220C', 19776 'notniva': '\u220C', 19777 'NotRightTriangle': '\u22EB', 19778 'nrtri': '\u22EB', 19779 'ntriangleright': '\u22EB', 19780 'NotRightTriangleBar': '\u29D0\u0338', 19781 'NotRightTriangleEqual': '\u22ED', 19782 'nrtrie': '\u22ED', 19783 'ntrianglerighteq': '\u22ED', 19784 'NotSquareSubset': '\u228F\u0338', 19785 'NotSquareSubsetEqual': '\u22E2', 19786 'nsqsube': '\u22E2', 19787 'NotSquareSuperset': '\u2290\u0338', 19788 'NotSquareSupersetEqual': '\u22E3', 19789 'nsqsupe': '\u22E3', 19790 'NotSubset': '\u2282\u20D2', 19791 'nsubset': '\u2282\u20D2', 19792 'vnsub': '\u2282\u20D2', 19793 'NotSubsetEqual': '\u2288', 19794 'nsube': '\u2288', 19795 'nsubseteq': '\u2288', 19796 'NotSucceeds': '\u2281', 19797 'nsc': '\u2281', 19798 'nsucc': '\u2281', 19799 'NotSucceedsEqual': '\u2AB0\u0338', 19800 'nsce': '\u2AB0\u0338', 19801 'nsucceq': '\u2AB0\u0338', 19802 'NotSucceedsSlantEqual': '\u22E1', 19803 'nsccue': '\u22E1', 19804 'NotSucceedsTilde': '\u227F\u0338', 19805 'NotSuperset': '\u2283\u20D2', 19806 'nsupset': '\u2283\u20D2', 19807 'vnsup': '\u2283\u20D2', 19808 'NotSupersetEqual': '\u2289', 19809 'nsupe': '\u2289', 19810 'nsupseteq': '\u2289', 19811 'NotTilde': '\u2241', 19812 'nsim': '\u2241', 19813 'NotTildeEqual': '\u2244', 19814 'nsime': '\u2244', 19815 'nsimeq': '\u2244', 19816 'NotTildeFullEqual': '\u2247', 19817 'ncong': '\u2247', 19818 'NotTildeTilde': '\u2249', 19819 'nap': '\u2249', 19820 'napprox': '\u2249', 19821 'NotVerticalBar': '\u2224', 19822 'nmid': '\u2224', 19823 'nshortmid': '\u2224', 19824 'nsmid': '\u2224', 19825 'Nscr': '\uD835\uDCA9', 19826 'Ntilde': '\u00D1', 19827 'Nu': '\u039D', 19828 'OElig': '\u0152', 19829 'Oacute': '\u00D3', 19830 'Ocirc': '\u00D4', 19831 'Ocy': '\u041E', 19832 'Odblac': '\u0150', 19833 'Ofr': '\uD835\uDD12', 19834 'Ograve': '\u00D2', 19835 'Omacr': '\u014C', 19836 'Omega': '\u03A9', 19837 'ohm': '\u03A9', 19838 'Omicron': '\u039F', 19839 'Oopf': '\uD835\uDD46', 19840 'OpenCurlyDoubleQuote': '\u201C', 19841 'ldquo': '\u201C', 19842 'OpenCurlyQuote': '\u2018', 19843 'lsquo': '\u2018', 19844 'Or': '\u2A54', 19845 'Oscr': '\uD835\uDCAA', 19846 'Oslash': '\u00D8', 19847 'Otilde': '\u00D5', 19848 'Otimes': '\u2A37', 19849 'Ouml': '\u00D6', 19850 'OverBar': '\u203E', 19851 'oline': '\u203E', 19852 'OverBrace': '\u23DE', 19853 'OverBracket': '\u23B4', 19854 'tbrk': '\u23B4', 19855 'OverParenthesis': '\u23DC', 19856 'PartialD': '\u2202', 19857 'part': '\u2202', 19858 'Pcy': '\u041F', 19859 'Pfr': '\uD835\uDD13', 19860 'Phi': '\u03A6', 19861 'Pi': '\u03A0', 19862 'PlusMinus': '\u00B1', 19863 'plusmn': '\u00B1', 19864 'pm': '\u00B1', 19865 'Popf': '\u2119', 19866 'primes': '\u2119', 19867 'Pr': '\u2ABB', 19868 'Precedes': '\u227A', 19869 'pr': '\u227A', 19870 'prec': '\u227A', 19871 'PrecedesEqual': '\u2AAF', 19872 'pre': '\u2AAF', 19873 'preceq': '\u2AAF', 19874 'PrecedesSlantEqual': '\u227C', 19875 'prcue': '\u227C', 19876 'preccurlyeq': '\u227C', 19877 'PrecedesTilde': '\u227E', 19878 'precsim': '\u227E', 19879 'prsim': '\u227E', 19880 'Prime': '\u2033', 19881 'Product': '\u220F', 19882 'prod': '\u220F', 19883 'Proportional': '\u221D', 19884 'prop': '\u221D', 19885 'propto': '\u221D', 19886 'varpropto': '\u221D', 19887 'vprop': '\u221D', 19888 'Pscr': '\uD835\uDCAB', 19889 'Psi': '\u03A8', 19890 'QUOT': '\u0022', 19891 'quot': '\u0022', 19892 'Qfr': '\uD835\uDD14', 19893 'Qopf': '\u211A', 19894 'rationals': '\u211A', 19895 'Qscr': '\uD835\uDCAC', 19896 'RBarr': '\u2910', 19897 'drbkarow': '\u2910', 19898 'REG': '\u00AE', 19899 'circledR': '\u00AE', 19900 'reg': '\u00AE', 19901 'Racute': '\u0154', 19902 'Rang': '\u27EB', 19903 'Rarr': '\u21A0', 19904 'twoheadrightarrow': '\u21A0', 19905 'Rarrtl': '\u2916', 19906 'Rcaron': '\u0158', 19907 'Rcedil': '\u0156', 19908 'Rcy': '\u0420', 19909 'Re': '\u211C', 19910 'Rfr': '\u211C', 19911 'real': '\u211C', 19912 'realpart': '\u211C', 19913 'ReverseElement': '\u220B', 19914 'SuchThat': '\u220B', 19915 'ni': '\u220B', 19916 'niv': '\u220B', 19917 'ReverseEquilibrium': '\u21CB', 19918 'leftrightharpoons': '\u21CB', 19919 'lrhar': '\u21CB', 19920 'ReverseUpEquilibrium': '\u296F', 19921 'duhar': '\u296F', 19922 'Rho': '\u03A1', 19923 'RightAngleBracket': '\u27E9', 19924 'rang': '\u27E9', 19925 'rangle': '\u27E9', 19926 'RightArrow': '\u2192', 19927 'ShortRightArrow': '\u2192', 19928 'rarr': '\u2192', 19929 'rightarrow': '\u2192', 19930 'srarr': '\u2192', 19931 'RightArrowBar': '\u21E5', 19932 'rarrb': '\u21E5', 19933 'RightArrowLeftArrow': '\u21C4', 19934 'rightleftarrows': '\u21C4', 19935 'rlarr': '\u21C4', 19936 'RightCeiling': '\u2309',
19937 'rceil': '\u2309', 19938 'RightDoubleBracket': '\u27E7', 19939 'robrk': '\u27E7', 19940 'RightDownTeeVector': '\u295D', 19941 'RightDownVector': '\u21C2', 19942 'dharr': '\u21C2', 19943 'downharpoonright': '\u21C2', 19944 'RightDownVectorBar': '\u2955', 19945 'RightFloor': '\u230B', 19946 'rfloor': '\u230B', 19947 'RightTee': '\u22A2', 19948 'vdash': '\u22A2', 19949 'RightTeeArrow': '\u21A6', 19950 'map': '\u21A6', 19951 'mapsto': '\u21A6', 19952 'RightTeeVector': '\u295B', 19953 'RightTriangle': '\u22B3', 19954 'vartriangleright': '\u22B3', 19955 'vrtri': '\u22B3', 19956 'RightTriangleBar': '\u29D0', 19957 'RightTriangleEqual': '\u22B5', 19958 'rtrie': '\u22B5', 19959 'trianglerighteq': '\u22B5', 19960 'RightUpDownVector': '\u294F', 19961 'RightUpTeeVector': '\u295C', 19962 'RightUpVector': '\u21BE', 19963 'uharr': '\u21BE', 19964 'upharpoonright': '\u21BE', 19965 'RightUpVectorBar': '\u2954', 19966 'RightVector': '\u21C0', 19967 'rharu': '\u21C0', 19968 'rightharpoonup': '\u21C0', 19969 'RightVectorBar': '\u2953', 19970 'Ropf': '\u211D', 19971 'reals': '\u211D', 19972 'RoundImplies': '\u2970', 19973 'Rrightarrow': '\u21DB', 19974 'rAarr': '\u21DB', 19975 'Rscr': '\u211B', 19976 'realine': '\u211B', 19977 'Rsh': '\u21B1', 19978 'rsh': '\u21B1', 19979 'RuleDelayed': '\u29F4', 19980 'SHCHcy': '\u0429', 19981 'SHcy': '\u0428', 19982 'SOFTcy': '\u042C', 19983 'Sacute': '\u015A', 19984 'Sc': '\u2ABC', 19985 'Scaron': '\u0160', 19986 'Scedil': '\u015E', 19987 'Scirc': '\u015C', 19988 'Scy': '\u0421', 19989 'Sfr': '\uD835\uDD16', 19990 'ShortUpArrow': '\u2191', 19991 'UpArrow': '\u2191', 19992 'uarr': '\u2191', 19993 'uparrow': '\u2191', 19994 'Sigma': '\u03A3', 19995 'SmallCircle': '\u2218', 19996 'compfn': '\u2218', 19997 'Sopf': '\uD835\uDD4A', 19998 'Sqrt': '\u221A', 19999 'radic': '\u221A', 20000 'Square': '\u25A1', 20001 'squ': '\u25A1', 20002 'square': '\u25A1', 20003 'SquareIntersection': '\u2293', 20004 'sqcap': '\u2293', 20005 'SquareSubset': '\u228F', 20006 'sqsub': '\u228F', 20007 'sqsubset': '\u228F', 20008 'SquareSubsetEqual': '\u2291', 20009 'sqsube': '\u2291', 20010 'sqsubseteq': '\u2291', 20011 'SquareSuperset': '\u2290', 20012 'sqsup': '\u2290', 20013 'sqsupset': '\u2290', 20014 'SquareSupersetEqual': '\u2292', 20015 'sqsupe': '\u2292', 20016 'sqsupseteq': '\u2292', 20017 'SquareUnion': '\u2294', 20018 'sqcup': '\u2294', 20019 'Sscr': '\uD835\uDCAE', 20020 'Star': '\u22C6', 20021 'sstarf': '\u22C6', 20022 'Sub': '\u22D0', 20023 'Subset': '\u22D0', 20024 'SubsetEqual': '\u2286', 20025 'sube': '\u2286', 20026 'subseteq': '\u2286', 20027 'Succeeds': '\u227B', 20028 'sc': '\u227B', 20029 'succ': '\u227B', 20030 'SucceedsEqual': '\u2AB0', 20031 'sce': '\u2AB0', 20032 'succeq': '\u2AB0', 20033 'SucceedsSlantEqual': '\u227D', 20034 'sccue': '\u227D', 20035 'succcurlyeq': '\u227D', 20036 'SucceedsTilde': '\u227F', 20037 'scsim': '\u227F', 20038 'succsim': '\u227F', 20039 'Sum': '\u2211', 20040 'sum': '\u2211', 20041 'Sup': '\u22D1', 20042 'Supset': '\u22D1', 20043 'Superset': '\u2283', 20044 'sup': '\u2283', 20045 'supset': '\u2283', 20046 'SupersetEqual': '\u2287', 20047 'supe': '\u2287', 20048 'supseteq': '\u2287', 20049 'THORN': '\u00DE', 20050 'TRADE': '\u2122', 20051 'trade': '\u2122', 20052 'TSHcy': '\u040B', 20053 'TScy': '\u0426', 20054 'Tab': '\u0009', 20055 'Tau': '\u03A4', 20056 'Tcaron': '\u0164', 20057 'Tcedil': '\u0162', 20058 'Tcy': '\u0422', 20059 'Tfr': '\uD835\uDD17', 20060 'Therefore': '\u2234', 20061 'there4': '\u2234', 20062 'therefore': '\u2234', 20063 'Theta': '\u0398', 20064 'ThickSpace': '\u205F\u200A', 20065 'ThinSpace': '\u2009', 20066 'thinsp': '\u2009', 20067 'Tilde': '\u223C', 20068 'sim': '\u223C', 20069 'thicksim': '\u223C', 20070 'thksim': '\u223C', 20071 'TildeEqual': '\u2243', 20072 'sime': '\u2243', 20073 'simeq': '\u2243', 20074 'TildeFullEqual': '\u2245', 20075 'cong': '\u2245', 20076 'TildeTilde': '\u2248', 20077 'ap': '\u2248', 20078 'approx': '\u2248', 20079 'asymp': '\u2248', 20080 'thickapprox': '\u2248', 20081 'thkap': '\u2248', 20082 'Topf': '\uD835\uDD4B', 20083 'TripleDot': '\u20DB', 20084 'tdot': '\u20DB', 20085 'Tscr': '\uD835\uDCAF', 20086 'Tstrok': '\u0166', 20087 'Uacute': '\u00DA', 20088 'Uarr': '\u219F', 20089 'Uarrocir': '\u2949', 20090 'Ubrcy': '\u040E', 20091 'Ubreve': '\u016C', 20092 'Ucirc': '\u00DB', 20093 'Ucy': '\u0423', 20094 'Udblac': '\u0170', 20095 'Ufr': '\uD835\uDD18', 20096 'Ugrave': '\u00D9', 20097 'Umacr': '\u016A', 20098 'UnderBar': '\u005F', 20099 'lowbar': '\u005F', 20100 'UnderBrace': '\u23DF', 20101 'UnderBracket': '\u23B5', 20102 'bbrk': '\u23B5', 20103 'UnderParenthesis': '\u23DD', 20104 'Union': '\u22C3', 20105 'bigcup': '\u22C3', 20106 'xcup': '\u22C3', 20107 'UnionPlus': '\u228E', 20108 'uplus': '\u228E', 20109 'Uogon': '\u0172', 20110 'Uopf': '\uD835\uDD4C', 20111 'UpArrowBar': '\u2912', 20112 'UpArrowDownArrow': '\u21C5', 20113 'udarr': '\u21C5', 20114 'UpDownArrow': '\u2195', 20115 'updownarrow': '\u2195', 20116 'varr': '\u2195', 20117 'UpEquilibrium': '\u296E', 20118 'udhar': '\u296E', 20119 'UpTee': '\u22A5', 20120 'bot': '\u22A5', 20121 'bottom': '\u22A5', 20122 'perp': '\u22A5', 20123 'UpTeeArrow': '\u21A5', 20124 'mapstoup': '\u21A5', 20125 'UpperLeftArrow': '\u2196', 20126 'nwarr': '\u2196', 20127 'nwarrow': '\u2196', 20128 'UpperRightArrow': '\u2197', 20129 'nearr': '\u2197', 20130 'nearrow': '\u2197', 20131 'Upsi': '\u03D2', 20132 'upsih': '\u03D2', 20133 'Upsilon': '\u03A5', 20134 'Uring': '\u016E', 20135 'Uscr': '\uD835\uDCB0', 20136 'Utilde': '\u0168', 20137 'Uuml': '\u00DC',
20138 'VDash': '\u22AB', 20139 'Vbar': '\u2AEB', 20140 'Vcy': '\u0412', 20141 'Vdash': '\u22A9', 20142 'Vdashl': '\u2AE6', 20143 'Vee': '\u22C1', 20144 'bigvee': '\u22C1', 20145 'xvee': '\u22C1', 20146 'Verbar': '\u2016', 20147 'Vert': '\u2016', 20148 'VerticalBar': '\u2223', 20149 'mid': '\u2223', 20150 'shortmid': '\u2223', 20151 'smid': '\u2223', 20152 'VerticalLine': '\u007C', 20153 'verbar': '\u007C', 20154 'vert': '\u007C', 20155 'VerticalSeparator': '\u2758', 20156 'VerticalTilde': '\u2240', 20157 'wr': '\u2240', 20158 'wreath': '\u2240', 20159 'VeryThinSpace': '\u200A', 20160 'hairsp': '\u200A', 20161 'Vfr': '\uD835\uDD19', 20162 'Vopf': '\uD835\uDD4D', 20163 'Vscr': '\uD835\uDCB1', 20164 'Vvdash': '\u22AA', 20165 'Wcirc': '\u0174', 20166 'Wedge': '\u22C0', 20167 'bigwedge': '\u22C0', 20168 'xwedge': '\u22C0', 20169 'Wfr': '\uD835\uDD1A', 20170 'Wopf': '\uD835\uDD4E', 20171 'Wscr': '\uD835\uDCB2', 20172 'Xfr': '\uD835\uDD1B', 20173 'Xi': '\u039E', 20174 'Xopf': '\uD835\uDD4F', 20175 'Xscr': '\uD835\uDCB3', 20176 'YAcy': '\u042F', 20177 'YIcy': '\u0407', 20178 'YUcy': '\u042E', 20179 'Yacute': '\u00DD', 20180 'Ycirc': '\u0176', 20181 'Ycy': '\u042B', 20182 'Yfr': '\uD835\uDD1C', 20183 'Yopf': '\uD835\uDD50', 20184 'Yscr': '\uD835\uDCB4', 20185 'Yuml': '\u0178', 20186 'ZHcy': '\u0416', 20187 'Zacute': '\u0179', 20188 'Zcaron': '\u017D', 20189 'Zcy': '\u0417', 20190 'Zdot': '\u017B', 20191 'Zeta': '\u0396', 20192 'Zfr': '\u2128', 20193 'zeetrf': '\u2128', 20194 'Zopf': '\u2124', 20195 'integers': '\u2124', 20196 'Zscr': '\uD835\uDCB5', 20197 'aacute': '\u00E1', 20198 'abreve': '\u0103', 20199 'ac': '\u223E', 20200 'mstpos': '\u223E', 20201 'acE': '\u223E\u0333', 20202 'acd': '\u223F', 20203 'acirc': '\u00E2', 20204 'acy': '\u0430', 20205 'aelig': '\u00E6', 20206 'afr': '\uD835\uDD1E', 20207 'agrave': '\u00E0', 20208 'alefsym': '\u2135', 20209 'aleph': '\u2135', 20210 'alpha': '\u03B1', 20211 'amacr': '\u0101', 20212 'amalg': '\u2A3F', 20213 'and': '\u2227', 20214 'wedge': '\u2227', 20215 'andand': '\u2A55', 20216 'andd': '\u2A5C', 20217 'andslope': '\u2A58', 20218 'andv': '\u2A5A', 20219 'ang': '\u2220', 20220 'angle': '\u2220', 20221 'ange': '\u29A4', 20222 'angmsd': '\u2221', 20223 'measuredangle': '\u2221', 20224 'angmsdaa': '\u29A8', 20225 'angmsdab': '\u29A9', 20226 'angmsdac': '\u29AA', 20227 'angmsdad': '\u29AB', 20228 'angmsdae': '\u29AC', 20229 'angmsdaf': '\u29AD', 20230 'angmsdag': '\u29AE', 20231 'angmsdah': '\u29AF', 20232 'angrt': '\u221F', 20233 'angrtvb': '\u22BE', 20234 'angrtvbd': '\u299D', 20235 'angsph': '\u2222', 20236 'angzarr': '\u237C', 20237 'aogon': '\u0105', 20238 'aopf': '\uD835\uDD52', 20239 'apE': '\u2A70', 20240 'apacir': '\u2A6F', 20241 'ape': '\u224A', 20242 'approxeq': '\u224A', 20243 'apid': '\u224B', 20244 'apos': '\u0027', 20245 'aring': '\u00E5', 20246 'ascr': '\uD835\uDCB6', 20247 'ast': '\u002A', 20248 'midast': '\u002A', 20249 'atilde': '\u00E3', 20250 'auml': '\u00E4', 20251 'awint': '\u2A11', 20252 'bNot': '\u2AED', 20253 'backcong': '\u224C', 20254 'bcong': '\u224C', 20255 'backepsilon': '\u03F6', 20256 'bepsi': '\u03F6', 20257 'backprime': '\u2035', 20258 'bprime': '\u2035', 20259 'backsim': '\u223D', 20260 'bsim': '\u223D', 20261 'backsimeq': '\u22CD', 20262 'bsime': '\u22CD', 20263 'barvee': '\u22BD', 20264 'barwed': '\u2305', 20265 'barwedge': '\u2305', 20266 'bbrktbrk': '\u23B6', 20267 'bcy': '\u0431', 20268 'bdquo': '\u201E', 20269 'ldquor': '\u201E', 20270 'bemptyv': '\u29B0', 20271 'beta': '\u03B2', 20272 'beth': '\u2136', 20273 'between': '\u226C', 20274 'twixt': '\u226C', 20275 'bfr': '\uD835\uDD1F', 20276 'bigcirc': '\u25EF', 20277 'xcirc': '\u25EF', 20278 'bigodot': '\u2A00', 20279 'xodot': '\u2A00', 20280 'bigoplus': '\u2A01', 20281 'xoplus': '\u2A01', 20282 'bigotimes': '\u2A02', 20283 'xotime': '\u2A02', 20284 'bigsqcup': '\u2A06', 20285 'xsqcup': '\u2A06', 20286 'bigstar': '\u2605', 20287 'starf': '\u2605', 20288 'bigtriangledown': '\u25BD', 20289 'xdtri': '\u25BD', 20290 'bigtriangleup': '\u25B3', 20291 'xutri': '\u25B3', 20292 'biguplus': '\u2A04', 20293 'xuplus': '\u2A04', 20294 'bkarow': '\u290D', 20295 'rbarr': '\u290D', 20296 'blacklozenge': '\u29EB', 20297 'lozf': '\u29EB', 20298 'blacktriangle': '\u25B4', 20299 'utrif': '\u25B4', 20300 'blacktriangledown': '\u25BE', 20301 'dtrif': '\u25BE', 20302 'blacktriangleleft': '\u25C2', 20303 'ltrif': '\u25C2', 20304 'blacktriangleright': '\u25B8', 20305 'rtrif': '\u25B8', 20306 'blank': '\u2423', 20307 'blk12': '\u2592', 20308 'blk14': '\u2591', 20309 'blk34': '\u2593', 20310 'block': '\u2588', 20311 'bne': '\u003D\u20E5', 20312 'bnequiv': '\u2261\u20E5', 20313 'bnot': '\u2310', 20314 'bopf': '\uD835\uDD53', 20315 'bowtie': '\u22C8', 20316 'boxDL': '\u2557', 20317 'boxDR': '\u2554', 20318 'boxDl': '\u2556', 20319 'boxDr': '\u2553', 20320 'boxH': '\u2550', 20321 'boxHD': '\u2566', 20322 'boxHU': '\u2569', 20323 'boxHd': '\u2564', 20324 'boxHu': '\u2567', 20325 'boxUL': '\u255D', 20326 'boxUR': '\u255A', 20327 'boxUl': '\u255C', 20328 'boxUr': '\u2559', 20329 'boxV': '\u2551', 20330 'boxVH': '\u256C', 20331 'boxVL': '\u2563', 20332 'boxVR': '\u2560', 20333 'boxVh': '\u256B', 20334 'boxVl': '\u2562', 20335 'boxVr': '\u255F', 20336 'boxbox': '\u29C9', 20337 'boxdL': '\u2555', 20338 'boxdR': '\u2552', 20339 'boxdl': '\u2510', 20340 'boxdr': '\u250C', 20341 'boxhD': '\u2565', 20342 'boxhU': '\u2568', 20343 'boxhd': '\u252C', 20344 'boxhu': '\u2534', 20345 'boxminus': '\u229F', 20346 'minusb': '\u229F', 20347 'boxplus': '\u229E', 20348 'plusb': '\u229E', 20349 'boxtimes': '\u22A0', 20350 'timesb': '\u22A0', 20351 'boxuL': '\u255B', 20352 'boxuR': '\u2558', 20353 'boxul': '\u2518', 20354 'boxur': '\u2514', 20355 'boxv': '\u2502', 20356 'boxvH': '\u256A', 20357 'boxvL': '\u2561', 20358 'boxvR': '\u255E', 20359 'boxvh': '\u253C', 20360 'boxvl': '\u2524', 20361 'boxvr': '\u251C', 20362 'brvbar': '\u00A6', 20363 'bscr': '\uD835\uDCB7', 20364 'bsemi': '\u204F', 20365 'bsol': '\u005C', 20366 'bsolb': '\u29C5', 20367 'bsolhsub': '\u27C8', 20368 'bull': '\u2022', 20369 'bullet': '\u2022', 20370 'bumpE': '\u2AAE', 20371 'cacute': '\u0107', 20372 'cap': '\u2229', 20373 'capand': '\u2A44', 20374 'capbrcup': '\u2A49', 20375 'capcap': '\u2A4B', 20376 'capcup': '\u2A47', 20377 'capdot': '\u2A40', 20378 'caps': '\u2229\uFE00', 20379 'caret': '\u2041', 20380 'ccaps': '\u2A4D', 20381 'ccaron': '\u010D', 20382 'ccedil': '\u00E7', 20383 'ccirc': '\u0109', 20384 'ccups': '\u2A4C', 20385 'ccupssm': '\u2A50', 20386 'cdot': '\u010B', 20387 'cemptyv': '\u29B2', 20388 'cent': '\u00A2', 20389 'cfr': '\uD835\uDD20', 20390 'chcy': '\u0447', 20391 'check': '\u2713', 20392 'checkmark': '\u2713', 20393 'chi': '\u03C7', 20394 'cir': '\u25CB', 20395 'cirE': '\u29C3', 20396 'circ': '\u02C6', 20397 'circeq': '\u2257', 20398 'cire': '\u2257', 20399 'circlearrowleft': '\u21BA', 20400 'olarr': '\u21BA', 20401 'circlearrowright': '\u21BB', 20402 'orarr': '\u21BB', 20403 'circledS': '\u24C8', 20404 'oS': '\u24C8', 20405 'circledast': '\u229B', 20406 'oast': '\u229B', 20407 'circledcirc': '\u229A', 20408 'ocir': '\u229A', 20409 'circleddash': '\u229D', 20410 'odash': '\u229D', 20411 'cirfnint': '\u2A10', 20412 'cirmid': '\u2AEF', 20413 'cirscir': '\u29C2', 20414 'clubs': '\u2663', 20415 'clubsuit': '\u2663', 20416 'colon': '\u003A', 20417 'comma': '\u002C', 20418 'commat': '\u0040', 20419 'comp': '\u2201', 20420 'complement': '\u2201', 20421 'congdot': '\u2A6D', 20422 'copf': '\uD835\uDD54', 20423 'copysr': '\u2117', 20424 'crarr': '\u21B5', 20425 'cross': '\u2717', 20426 'cscr': '\uD835\uDCB8', 20427 'csub': '\u2ACF', 20428 'csube': '\u2AD1', 20429 'csup': '\u2AD0', 20430 'csupe': '\u2AD2', 20431 'ctdot': '\u22EF', 20432 'cudarrl': '\u2938', 20433 'cudarrr': '\u2935', 20434 'cuepr': '\u22DE', 20435 'curlyeqprec': '\u22DE', 20436 'cuesc': '\u22DF', 20437 'curlyeqsucc': '\u22DF', 20438 'cularr': '\u21B6', 20439 'curvearrowleft': '\u21B6', 20440 'cularrp': '\u293D', 20441 'cup': '\u222A', 20442 'cupbrcap': '\u2A48', 20443 'cupcap': '\u2A46', 20444 'cupcup': '\u2A4A', 20445 'cupdot': '\u228D', 20446 'cupor': '\u2A45', 20447 'cups': '\u222A\uFE00', 20448 'curarr': '\u21B7', 20449 'curvearrowright': '\u21B7', 20450 'curarrm': '\u293C', 20451 'curlyvee': '\u22CE', 20452 'cuvee': '\u22CE', 20453 'curlywedge': '\u22CF', 20454 'cuwed': '\u22CF', 20455 'curren': '\u00A4', 20456 'cwint': '\u2231', 20457 'cylcty': '\u232D', 20458 'dHar': '\u2965', 20459 'dagger': '\u2020', 20460 'daleth': '\u2138', 20461 'dash': '\u2010', 20462 'hyphen': '\u2010', 20463 'dbkarow': '\u290F', 20464 'rBarr': '\u290F', 20465 'dcaron': '\u010F', 20466 'dcy': '\u0434', 20467 'ddarr': '\u21CA', 20468 'downdownarrows': '\u21CA', 20469 'ddotseq': '\u2A77', 20470 'eDDot': '\u2A77', 20471 'deg': '\u00B0', 20472 'delta': '\u03B4', 20473 'demptyv': '\u29B1', 20474 'dfisht': '\u297F', 20475 'dfr': '\uD835\uDD21', 20476 'diamondsuit': '\u2666', 20477 'diams': '\u2666', 20478 'digamma': '\u03DD', 20479 'gammad': '\u03DD', 20480 'disin': '\u22F2', 20481 'div': '\u00F7', 20482 'divide': '\u00F7', 20483 'divideontimes': '\u22C7', 20484 'divonx': '\u22C7', 20485 'djcy': '\u0452', 20486 'dlcorn': '\u231E', 20487 'llcorner': '\u231E', 20488 'dlcrop': '\u230D', 20489 'dollar': '\u0024', 20490 'dopf': '\uD835\uDD55', 20491 'doteqdot': '\u2251', 20492 'eDot': '\u2251', 20493 'dotminus': '\u2238', 20494 'minusd': '\u2238', 20495 'dotplus': '\u2214', 20496 'plusdo': '\u2214', 20497 'dotsquare': '\u22A1', 20498 'sdotb': '\u22A1', 20499 'drcorn': '\u231F', 20500 'lrcorner': '\u231F', 20501 'drcrop': '\u230C', 20502 'dscr': '\uD835\uDCB9', 20503 'dscy': '\u0455', 20504 'dsol': '\u29F6', 20505 'dstrok': '\u0111', 20506 'dtdot': '\u22F1', 20507 'dtri': '\u25BF', 20508 'triangledown': '\u25BF', 20509 'dwangle': '\u29A6', 20510 'dzcy': '\u045F', 20511 'dzigrarr': '\u27FF', 20512 'eacute': '\u00E9', 20513 'easter': '\u2A6E', 20514 'ecaron': '\u011B', 20515 'ecir': '\u2256', 20516 'eqcirc': '\u2256', 20517 'ecirc': '\u00EA', 20518 'ecolon': '\u2255', 20519 'eqcolon': '\u2255', 20520 'ecy': '\u044D', 20521 'edot': '\u0117', 20522 'efDot': '\u2252', 20523 'fallingdotseq': '\u2252', 20524 'efr': '\uD835\uDD22', 20525 'eg': '\u2A9A', 20526 'egrave': '\u00E8', 20527 'egs': '\u2A96', 20528 'eqslantgtr': '\u2A96', 20529 'egsdot': '\u2A98', 20530 'el': '\u2A99', 20531 'elinters': '\u23E7', 20532 'ell': '\u2113', 20533 'els': '\u2A95', 20534 'eqslantless': '\u2A95', 20535 'elsdot': '\u2A97', 20536 'emacr': '\u0113', 20537 'empty': '\u2205', 20538 'emptyset': '\u2205', 20539 'emptyv': '\u2205', 20540 'varnothing': '\u2205', 20541 'emsp13': '\u2004', 20542 'emsp14': '\u2005', 20543 'emsp': '\u2003', 20544 'eng': '\u014B', 20545 'ensp': '\u2002', 20546 'eogon': '\u0119', 20547 'eopf': '\uD835\uDD56', 20548 'epar': '\u22D5', 20549 'eparsl': '\u29E3', 20550 'eplus': '\u2A71', 20551 'epsi': '\u03B5', 20552 'epsilon': '\u03B5', 20553 'epsiv': '\u03F5', 20554 'straightepsilon': '\u03F5', 20555 'varepsilon': '\u03F5', 20556 'equals': '\u003D', 20557 'equest': '\u225F', 20558 'questeq': '\u225F', 20559 'equivDD': '\u2A78', 20560 'eqvparsl': '\u29E5', 20561 'erDot': '\u2253', 20562 'risingdotseq': '\u2253', 20563 'erarr': '\u2971', 20564 'escr': '\u212F', 20565 'eta': '\u03B7', 20566 'eth': '\u00F0', 20567 'euml': '\u00EB', 20568 'euro': '\u20AC', 20569 'excl': '\u0021', 20570 'fcy': '\u0444', 20571 'female': '\u2640', 20572 'ffilig': '\uFB03', 20573 'fflig': '\uFB00', 20574 'ffllig': '\uFB04', 20575 'ffr': '\uD835\uDD23', 20576 'filig': '\uFB01', 20577 'fjlig': '\u0066\u006A', 20578 'flat': '\u266D', 20579 'fllig': '\uFB02', 20580 'fltns': '\u25B1', 20581 'fnof': '\u0192', 20582 'fopf': '\uD835\uDD57', 20583 'fork': '\u22D4', 20584 'pitchfork': '\u22D4', 20585 'forkv': '\u2AD9', 20586 'fpartint': '\u2A0D', 20587 'frac12': '\u00BD', 20588 'half': '\u00BD', 20589 'frac13': '\u2153', 20590 'frac14': '\u00BC', 20591 'frac15': '\u2155', 20592 'frac16': '\u2159', 20593 'frac18': '\u215B', 20594 'frac23': '\u2154', 20595 'frac25': '\u2156', 20596 'frac34': '\u00BE', 20597 'frac35': '\u2157', 20598 'frac38': '\u215C', 20599 'frac45': '\u2158', 20600 'frac56': '\u215A',
20601 'frac58': '\u215D', 20602 'frac78': '\u215E', 20603 'frasl': '\u2044', 20604 'frown': '\u2322', 20605 'sfrown': '\u2322', 20606 'fscr': '\uD835\uDCBB', 20607 'gEl': '\u2A8C', 20608 'gtreqqless': '\u2A8C', 20609 'gacute': '\u01F5', 20610 'gamma': '\u03B3', 20611 'gap': '\u2A86', 20612 'gtrapprox': '\u2A86', 20613 'gbreve': '\u011F', 20614 'gcirc': '\u011D', 20615 'gcy': '\u0433', 20616 'gdot': '\u0121', 20617 'gescc': '\u2AA9', 20618 'gesdot': '\u2A80', 20619 'gesdoto': '\u2A82', 20620 'gesdotol': '\u2A84', 20621 'gesl': '\u22DB\uFE00', 20622 'gesles': '\u2A94', 20623 'gfr': '\uD835\uDD24', 20624 'gimel': '\u2137', 20625 'gjcy': '\u0453', 20626 'glE': '\u2A92', 20627 'gla': '\u2AA5', 20628 'glj': '\u2AA4', 20629 'gnE': '\u2269', 20630 'gneqq': '\u2269', 20631 'gnap': '\u2A8A', 20632 'gnapprox': '\u2A8A', 20633 'gne': '\u2A88', 20634 'gneq': '\u2A88', 20635 'gnsim': '\u22E7', 20636 'gopf': '\uD835\uDD58', 20637 'gscr': '\u210A', 20638 'gsime': '\u2A8E', 20639 'gsiml': '\u2A90', 20640 'gtcc': '\u2AA7', 20641 'gtcir': '\u2A7A', 20642 'gtdot': '\u22D7', 20643 'gtrdot': '\u22D7', 20644 'gtlPar': '\u2995', 20645 'gtquest': '\u2A7C', 20646 'gtrarr': '\u2978', 20647 'gvertneqq': '\u2269\uFE00', 20648 'gvnE': '\u2269\uFE00', 20649 'hardcy': '\u044A', 20650 'harrcir': '\u2948', 20651 'harrw': '\u21AD', 20652 'leftrightsquigarrow': '\u21AD', 20653 'hbar': '\u210F', 20654 'hslash': '\u210F', 20655 'planck': '\u210F', 20656 'plankv': '\u210F', 20657 'hcirc': '\u0125', 20658 'hearts': '\u2665', 20659 'heartsuit': '\u2665', 20660 'hellip': '\u2026', 20661 'mldr': '\u2026', 20662 'hercon': '\u22B9', 20663 'hfr': '\uD835\uDD25', 20664 'hksearow': '\u2925', 20665 'searhk': '\u2925', 20666 'hkswarow': '\u2926', 20667 'swarhk': '\u2926', 20668 'hoarr': '\u21FF', 20669 'homtht': '\u223B', 20670 'hookleftarrow': '\u21A9', 20671 'larrhk': '\u21A9', 20672 'hookrightarrow': '\u21AA', 20673 'rarrhk': '\u21AA', 20674 'hopf': '\uD835\uDD59', 20675 'horbar': '\u2015', 20676 'hscr': '\uD835\uDCBD', 20677 'hstrok': '\u0127', 20678 'hybull': '\u2043', 20679 'iacute': '\u00ED', 20680 'icirc': '\u00EE', 20681 'icy': '\u0438', 20682 'iecy': '\u0435', 20683 'iexcl': '\u00A1', 20684 'ifr': '\uD835\uDD26', 20685 'igrave': '\u00EC', 20686 'iiiint': '\u2A0C', 20687 'qint': '\u2A0C', 20688 'iiint': '\u222D', 20689 'tint': '\u222D', 20690 'iinfin': '\u29DC', 20691 'iiota': '\u2129', 20692 'ijlig': '\u0133', 20693 'imacr': '\u012B', 20694 'imath': '\u0131', 20695 'inodot': '\u0131', 20696 'imof': '\u22B7', 20697 'imped': '\u01B5', 20698 'incare': '\u2105', 20699 'infin': '\u221E', 20700 'infintie': '\u29DD', 20701 'intcal': '\u22BA', 20702 'intercal': '\u22BA', 20703 'intlarhk': '\u2A17', 20704 'intprod': '\u2A3C', 20705 'iprod': '\u2A3C', 20706 'iocy': '\u0451', 20707 'iogon': '\u012F', 20708 'iopf': '\uD835\uDD5A', 20709 'iota': '\u03B9', 20710 'iquest': '\u00BF', 20711 'iscr': '\uD835\uDCBE', 20712 'isinE': '\u22F9', 20713 'isindot': '\u22F5', 20714 'isins': '\u22F4', 20715 'isinsv': '\u22F3', 20716 'itilde': '\u0129', 20717 'iukcy': '\u0456', 20718 'iuml': '\u00EF', 20719 'jcirc': '\u0135', 20720 'jcy': '\u0439', 20721 'jfr': '\uD835\uDD27', 20722 'jmath': '\u0237', 20723 'jopf': '\uD835\uDD5B', 20724 'jscr': '\uD835\uDCBF', 20725 'jsercy': '\u0458', 20726 'jukcy': '\u0454', 20727 'kappa': '\u03BA', 20728 'kappav': '\u03F0', 20729 'varkappa': '\u03F0', 20730 'kcedil': '\u0137', 20731 'kcy': '\u043A', 20732 'kfr': '\uD835\uDD28', 20733 'kgreen': '\u0138', 20734 'khcy': '\u0445', 20735 'kjcy': '\u045C', 20736 'kopf': '\uD835\uDD5C', 20737 'kscr': '\uD835\uDCC0', 20738 'lAtail': '\u291B', 20739 'lBarr': '\u290E', 20740 'lEg': '\u2A8B', 20741 'lesseqqgtr': '\u2A8B', 20742 'lHar': '\u2962', 20743 'lacute': '\u013A', 20744 'laemptyv': '\u29B4', 20745 'lambda': '\u03BB', 20746 'langd': '\u2991', 20747 'lap': '\u2A85', 20748 'lessapprox': '\u2A85', 20749 'laquo': '\u00AB', 20750 'larrbfs': '\u291F', 20751 'larrfs': '\u291D', 20752 'larrlp': '\u21AB', 20753 'looparrowleft': '\u21AB', 20754 'larrpl': '\u2939', 20755 'larrsim': '\u2973', 20756 'larrtl': '\u21A2', 20757 'leftarrowtail': '\u21A2', 20758 'lat': '\u2AAB', 20759 'latail': '\u2919', 20760 'late': '\u2AAD', 20761 'lates': '\u2AAD\uFE00', 20762 'lbarr': '\u290C', 20763 'lbbrk': '\u2772', 20764 'lbrace': '\u007B', 20765 'lcub': '\u007B', 20766 'lbrack': '\u005B', 20767 'lsqb': '\u005B', 20768 'lbrke': '\u298B', 20769 'lbrksld': '\u298F', 20770 'lbrkslu': '\u298D', 20771 'lcaron': '\u013E', 20772 'lcedil': '\u013C', 20773 'lcy': '\u043B', 20774 'ldca': '\u2936', 20775 'ldrdhar': '\u2967', 20776 'ldrushar': '\u294B', 20777 'ldsh': '\u21B2', 20778 'le': '\u2264', 20779 'leq': '\u2264', 20780 'leftleftarrows': '\u21C7', 20781 'llarr': '\u21C7', 20782 'leftthreetimes': '\u22CB', 20783 'lthree': '\u22CB', 20784 'lescc': '\u2AA8', 20785 'lesdot': '\u2A7F', 20786 'lesdoto': '\u2A81', 20787 'lesdotor': '\u2A83', 20788 'lesg': '\u22DA\uFE00', 20789 'lesges': '\u2A93', 20790 'lessdot': '\u22D6', 20791 'ltdot': '\u22D6', 20792 'lfisht': '\u297C', 20793 'lfr': '\uD835\uDD29', 20794 'lgE': '\u2A91', 20795 'lharul': '\u296A', 20796 'lhblk': '\u2584', 20797 'ljcy': '\u0459', 20798 'llhard': '\u296B', 20799 'lltri': '\u25FA', 20800 'lmidot': '\u0140', 20801 'lmoust': '\u23B0', 20802 'lmoustache': '\u23B0', 20803 'lnE': '\u2268', 20804 'lneqq': '\u2268', 20805 'lnap': '\u2A89', 20806 'lnapprox': '\u2A89', 20807 'lne': '\u2A87', 20808 'lneq': '\u2A87', 20809 'lnsim': '\u22E6', 20810 'loang': '\u27EC', 20811 'loarr': '\u21FD', 20812 'longmapsto': '\u27FC', 20813 'xmap': '\u27FC', 20814 'looparrowright': '\u21AC', 20815 'rarrlp': '\u21AC', 20816 'lopar': '\u2985', 20817 'lopf': '\uD835\uDD5D', 20818 'loplus': '\u2A2D', 20819 'lotimes': '\u2A34', 20820 'lowast': '\u2217', 20821 'loz': '\u25CA', 20822 'lozenge': '\u25CA', 20823 'lpar': '\u0028', 20824 'lparlt': '\u2993', 20825 'lrhard': '\u296D', 20826 'lrm': '\u200E', 20827 'lrtri': '\u22BF', 20828 'lsaquo': '\u2039', 20829 'lscr': '\uD835\uDCC1', 20830 'lsime': '\u2A8D', 20831 'lsimg': '\u2A8F', 20832 'lsquor': '\u201A', 20833 'sbquo': '\u201A', 20834 'lstrok': '\u0142', 20835 'ltcc': '\u2AA6', 20836 'ltcir': '\u2A79', 20837 'ltimes': '\u22C9', 20838 'ltlarr': '\u2976', 20839 'ltquest': '\u2A7B', 20840 'ltrPar': '\u2996', 20841 'ltri': '\u25C3', 20842 'triangleleft': '\u25C3', 20843 'lurdshar': '\u294A', 20844 'luruhar': '\u2966', 20845 'lvertneqq': '\u2268\uFE00', 20846 'lvnE': '\u2268\uFE00', 20847 'mDDot': '\u223A', 20848 'macr': '\u00AF', 20849 'strns': '\u00AF', 20850 'male': '\u2642', 20851 'malt': '\u2720', 20852 'maltese': '\u2720', 20853 'marker': '\u25AE', 20854 'mcomma': '\u2A29', 20855 'mcy': '\u043C', 20856 'mdash': '\u2014', 20857 'mfr': '\uD835\uDD2A', 20858 'mho': '\u2127', 20859 'micro': '\u00B5', 20860 'midcir': '\u2AF0', 20861 'minus': '\u2212', 20862 'minusdu': '\u2A2A', 20863 'mlcp': '\u2ADB', 20864 'models': '\u22A7', 20865 'mopf': '\uD835\uDD5E', 20866 'mscr': '\uD835\uDCC2', 20867 'mu': '\u03BC', 20868 'multimap': '\u22B8', 20869 'mumap': '\u22B8', 20870 'nGg': '\u22D9\u0338', 20871 'nGt': '\u226B\u20D2', 20872 'nLeftarrow': '\u21CD', 20873 'nlArr': '\u21CD', 20874 'nLeftrightarrow': '\u21CE', 20875 'nhArr': '\u21CE', 20876 'nLl': '\u22D8\u0338', 20877 'nLt': '\u226A\u20D2', 20878 'nRightarrow': '\u21CF', 20879 'nrArr': '\u21CF', 20880 'nVDash': '\u22AF', 20881 'nVdash': '\u22AE', 20882 'nacute': '\u0144', 20883 'nang': '\u2220\u20D2', 20884 'napE': '\u2A70\u0338', 20885 'napid': '\u224B\u0338', 20886 'napos': '\u0149', 20887 'natur': '\u266E', 20888 'natural': '\u266E', 20889 'ncap': '\u2A43', 20890 'ncaron': '\u0148', 20891 'ncedil': '\u0146', 20892 'ncongdot': '\u2A6D\u0338', 20893 'ncup': '\u2A42', 20894 'ncy': '\u043D', 20895 'ndash': '\u2013', 20896 'neArr': '\u21D7', 20897 'nearhk': '\u2924', 20898 'nedot': '\u2250\u0338', 20899 'nesear': '\u2928', 20900 'toea': '\u2928', 20901 'nfr': '\uD835\uDD2B', 20902 'nharr': '\u21AE', 20903 'nleftrightarrow': '\u21AE', 20904 'nhpar': '\u2AF2', 20905 'nis': '\u22FC', 20906 'nisd': '\u22FA', 20907 'njcy': '\u045A', 20908 'nlE': '\u2266\u0338', 20909 'nleqq': '\u2266\u0338', 20910 'nlarr': '\u219A', 20911 'nleftarrow': '\u219A', 20912 'nldr': '\u2025', 20913 'nopf': '\uD835\uDD5F', 20914 'not': '\u00AC', 20915 'notinE': '\u22F9\u0338', 20916 'notindot': '\u22F5\u0338', 20917 'notinvb': '\u22F7', 20918 'notinvc': '\u22F6', 20919 'notnivb': '\u22FE', 20920 'notnivc': '\u22FD', 20921 'nparsl': '\u2AFD\u20E5',
20922 'npart': '\u2202\u0338', 20923 'npolint': '\u2A14', 20924 'nrarr': '\u219B', 20925 'nrightarrow': '\u219B', 20926 'nrarrc': '\u2933\u0338', 20927 'nrarrw': '\u219D\u0338', 20928 'nscr': '\uD835\uDCC3', 20929 'nsub': '\u2284', 20930 'nsubE': '\u2AC5\u0338', 20931 'nsubseteqq': '\u2AC5\u0338', 20932 'nsup': '\u2285', 20933 'nsupE': '\u2AC6\u0338', 20934 'nsupseteqq': '\u2AC6\u0338', 20935 'ntilde': '\u00F1', 20936 'nu': '\u03BD', 20937 'num': '\u0023', 20938 'numero': '\u2116', 20939 'numsp': '\u2007', 20940 'nvDash': '\u22AD', 20941 'nvHarr': '\u2904', 20942 'nvap': '\u224D\u20D2', 20943 'nvdash': '\u22AC', 20944 'nvge': '\u2265\u20D2', 20945 'nvgt': '\u003E\u20D2', 20946 'nvinfin': '\u29DE', 20947 'nvlArr': '\u2902', 20948 'nvle': '\u2264\u20D2', 20949 'nvlt': '\u003C\u20D2', 20950 'nvltrie': '\u22B4\u20D2', 20951 'nvrArr': '\u2903', 20952 'nvrtrie': '\u22B5\u20D2', 20953 'nvsim': '\u223C\u20D2', 20954 'nwArr': '\u21D6', 20955 'nwarhk': '\u2923', 20956 'nwnear': '\u2927', 20957 'oacute': '\u00F3', 20958 'ocirc': '\u00F4', 20959 'ocy': '\u043E', 20960 'odblac': '\u0151', 20961 'odiv': '\u2A38', 20962 'odsold': '\u29BC', 20963 'oelig': '\u0153', 20964 'ofcir': '\u29BF', 20965 'ofr': '\uD835\uDD2C', 20966 'ogon': '\u02DB', 20967 'ograve': '\u00F2', 20968 'ogt': '\u29C1', 20969 'ohbar': '\u29B5', 20970 'olcir': '\u29BE', 20971 'olcross': '\u29BB', 20972 'olt': '\u29C0', 20973 'omacr': '\u014D', 20974 'omega': '\u03C9', 20975 'omicron': '\u03BF', 20976 'omid': '\u29B6', 20977 'oopf': '\uD835\uDD60', 20978 'opar': '\u29B7', 20979 'operp': '\u29B9', 20980 'or': '\u2228', 20981 'vee': '\u2228', 20982 'ord': '\u2A5D', 20983 'order': '\u2134', 20984 'orderof': '\u2134', 20985 'oscr': '\u2134', 20986 'ordf': '\u00AA', 20987 'ordm': '\u00BA', 20988 'origof': '\u22B6', 20989 'oror': '\u2A56', 20990 'orslope': '\u2A57', 20991 'orv': '\u2A5B', 20992 'oslash': '\u00F8', 20993 'osol': '\u2298', 20994 'otilde': '\u00F5', 20995 'otimesas': '\u2A36', 20996 'ouml': '\u00F6', 20997 'ovbar': '\u233D', 20998 'para': '\u00B6', 20999 'parsim': '\u2AF3', 21000 'parsl': '\u2AFD', 21001 'pcy': '\u043F', 21002 'percnt': '\u0025', 21003 'period': '\u002E', 21004 'permil': '\u2030', 21005 'pertenk': '\u2031', 21006 'pfr': '\uD835\uDD2D', 21007 'phi': '\u03C6', 21008 'phiv': '\u03D5', 21009 'straightphi': '\u03D5', 21010 'varphi': '\u03D5', 21011 'phone': '\u260E', 21012 'pi': '\u03C0', 21013 'piv': '\u03D6', 21014 'varpi': '\u03D6', 21015 'planckh': '\u210E', 21016 'plus': '\u002B', 21017 'plusacir': '\u2A23', 21018 'pluscir': '\u2A22', 21019 'plusdu': '\u2A25', 21020 'pluse': '\u2A72', 21021 'plussim': '\u2A26', 21022 'plustwo': '\u2A27', 21023 'pointint': '\u2A15', 21024 'popf': '\uD835\uDD61', 21025 'pound': '\u00A3', 21026 'prE': '\u2AB3', 21027 'prap': '\u2AB7', 21028 'precapprox': '\u2AB7', 21029 'precnapprox': '\u2AB9', 21030 'prnap': '\u2AB9', 21031 'precneqq': '\u2AB5', 21032 'prnE': '\u2AB5', 21033 'precnsim': '\u22E8', 21034 'prnsim': '\u22E8', 21035 'prime': '\u2032', 21036 'profalar': '\u232E', 21037 'profline': '\u2312', 21038 'profsurf': '\u2313', 21039 'prurel': '\u22B0', 21040 'pscr': '\uD835\uDCC5', 21041 'psi': '\u03C8', 21042 'puncsp': '\u2008', 21043 'qfr': '\uD835\uDD2E', 21044 'qopf': '\uD835\uDD62', 21045 'qprime': '\u2057', 21046 'qscr': '\uD835\uDCC6', 21047 'quatint': '\u2A16', 21048 'quest': '\u003F', 21049 'rAtail': '\u291C', 21050 'rHar': '\u2964', 21051 'race': '\u223D\u0331', 21052 'racute': '\u0155', 21053 'raemptyv': '\u29B3', 21054 'rangd': '\u2992', 21055 'range': '\u29A5', 21056 'raquo': '\u00BB', 21057 'rarrap': '\u2975', 21058 'rarrbfs': '\u2920', 21059 'rarrc': '\u2933', 21060 'rarrfs': '\u291E', 21061 'rarrpl': '\u2945', 21062 'rarrsim': '\u2974', 21063 'rarrtl': '\u21A3', 21064 'rightarrowtail': '\u21A3', 21065 'rarrw': '\u219D', 21066 'rightsquigarrow': '\u219D', 21067 'ratail': '\u291A', 21068 'ratio': '\u2236', 21069 'rbbrk': '\u2773', 21070 'rbrace': '\u007D', 21071 'rcub': '\u007D', 21072 'rbrack': '\u005D', 21073 'rsqb': '\u005D', 21074 'rbrke': '\u298C', 21075 'rbrksld': '\u298E', 21076 'rbrkslu': '\u2990', 21077 'rcaron': '\u0159', 21078 'rcedil': '\u0157', 21079 'rcy': '\u0440', 21080 'rdca': '\u2937', 21081 'rdldhar': '\u2969', 21082 'rdsh': '\u21B3', 21083 'rect': '\u25AD', 21084 'rfisht': '\u297D', 21085 'rfr': '\uD835\uDD2F', 21086 'rharul': '\u296C', 21087 'rho': '\u03C1', 21088 'rhov': '\u03F1', 21089 'varrho': '\u03F1', 21090 'rightrightarrows': '\u21C9', 21091 'rrarr': '\u21C9', 21092 'rightthreetimes': '\u22CC', 21093 'rthree': '\u22CC', 21094 'ring': '\u02DA', 21095 'rlm': '\u200F', 21096 'rmoust': '\u23B1', 21097 'rmoustache': '\u23B1', 21098 'rnmid': '\u2AEE', 21099 'roang': '\u27ED', 21100 'roarr': '\u21FE', 21101 'ropar': '\u2986', 21102 'ropf': '\uD835\uDD63', 21103 'roplus': '\u2A2E', 21104 'rotimes': '\u2A35', 21105 'rpar': '\u0029', 21106 'rpargt': '\u2994', 21107 'rppolint': '\u2A12', 21108 'rsaquo': '\u203A', 21109 'rscr': '\uD835\uDCC7', 21110 'rtimes': '\u22CA', 21111 'rtri': '\u25B9', 21112 'triangleright': '\u25B9', 21113 'rtriltri': '\u29CE', 21114 'ruluhar': '\u2968', 21115 'rx': '\u211E', 21116 'sacute': '\u015B', 21117 'scE': '\u2AB4', 21118 'scap': '\u2AB8', 21119 'succapprox': '\u2AB8', 21120 'scaron': '\u0161', 21121 'scedil': '\u015F', 21122 'scirc': '\u015D', 21123 'scnE': '\u2AB6', 21124 'succneqq': '\u2AB6', 21125 'scnap': '\u2ABA', 21126 'succnapprox': '\u2ABA', 21127 'scnsim': '\u22E9', 21128 'succnsim': '\u22E9', 21129 'scpolint': '\u2A13', 21130 'scy': '\u0441', 21131 'sdot': '\u22C5', 21132 'sdote': '\u2A66', 21133 'seArr': '\u21D8', 21134 'sect': '\u00A7', 21135 'semi': '\u003B', 21136 'seswar': '\u2929', 21137 'tosa': '\u2929', 21138 'sext': '\u2736', 21139 'sfr': '\uD835\uDD30', 21140 'sharp': '\u266F', 21141 'shchcy': '\u0449', 21142 'shcy': '\u0448', 21143 'shy': '\u00AD', 21144 'sigma': '\u03C3', 21145 'sigmaf': '\u03C2', 21146 'sigmav': '\u03C2', 21147 'varsigma': '\u03C2', 21148 'simdot': '\u2A6A', 21149 'simg': '\u2A9E', 21150 'simgE': '\u2AA0', 21151 'siml': '\u2A9D', 21152 'simlE': '\u2A9F', 21153 'simne': '\u2246', 21154 'simplus': '\u2A24', 21155 'simrarr': '\u2972', 21156 'smashp': '\u2A33', 21157 'smeparsl': '\u29E4', 21158 'smile': '\u2323', 21159 'ssmile': '\u2323', 21160 'smt': '\u2AAA', 21161 'smte': '\u2AAC', 21162 'smtes': '\u2AAC\uFE00', 21163 'softcy': '\u044C', 21164 'sol': '\u002F', 21165 'solb': '\u29C4', 21166 'solbar': '\u233F', 21167 'sopf': '\uD835\uDD64', 21168 'spades': '\u2660', 21169 'spadesuit': '\u2660', 21170 'sqcaps': '\u2293\uFE00', 21171 'sqcups': '\u2294\uFE00', 21172 'sscr': '\uD835\uDCC8', 21173 'star': '\u2606', 21174 'sub': '\u2282', 21175 'subset': '\u2282', 21176 'subE': '\u2AC5', 21177 'subseteqq': '\u2AC5', 21178 'subdot': '\u2ABD', 21179 'subedot': '\u2AC3', 21180 'submult': '\u2AC1', 21181 'subnE': '\u2ACB', 21182 'subsetneqq': '\u2ACB', 21183 'subne': '\u228A', 21184 'subsetneq': '\u228A', 21185 'subplus': '\u2ABF', 21186 'subrarr': '\u2979', 21187 'subsim': '\u2AC7', 21188 'subsub': '\u2AD5', 21189 'subsup': '\u2AD3', 21190 'sung': '\u266A', 21191 'sup1': '\u00B9', 21192 'sup2': '\u00B2', 21193 'sup3': '\u00B3', 21194 'supE': '\u2AC6', 21195 'supseteqq': '\u2AC6', 21196 'supdot': '\u2ABE', 21197 'supdsub': '\u2AD8', 21198 'supedot': '\u2AC4', 21199 'suphsol': '\u27C9', 21200 'suphsub': '\u2AD7', 21201 'suplarr': '\u297B',
21202 'supmult': '\u2AC2', 21203 'supnE': '\u2ACC', 21204 'supsetneqq': '\u2ACC', 21205 'supne': '\u228B', 21206 'supsetneq': '\u228B', 21207 'supplus': '\u2AC0', 21208 'supsim': '\u2AC8', 21209 'supsub': '\u2AD4', 21210 'supsup': '\u2AD6', 21211 'swArr': '\u21D9', 21212 'swnwar': '\u292A', 21213 'szlig': '\u00DF', 21214 'target': '\u2316', 21215 'tau': '\u03C4', 21216 'tcaron': '\u0165', 21217 'tcedil': '\u0163', 21218 'tcy': '\u0442', 21219 'telrec': '\u2315', 21220 'tfr': '\uD835\uDD31', 21221 'theta': '\u03B8', 21222 'thetasym': '\u03D1', 21223 'thetav': '\u03D1', 21224 'vartheta': '\u03D1', 21225 'thorn': '\u00FE', 21226 'times': '\u00D7', 21227 'timesbar': '\u2A31', 21228 'timesd': '\u2A30', 21229 'topbot': '\u2336', 21230 'topcir': '\u2AF1', 21231 'topf': '\uD835\uDD65', 21232 'topfork': '\u2ADA', 21233 'tprime': '\u2034', 21234 'triangle': '\u25B5', 21235 'utri': '\u25B5', 21236 'triangleq': '\u225C', 21237 'trie': '\u225C', 21238 'tridot': '\u25EC', 21239 'triminus': '\u2A3A', 21240 'triplus': '\u2A39', 21241 'trisb': '\u29CD', 21242 'tritime': '\u2A3B', 21243 'trpezium': '\u23E2', 21244 'tscr': '\uD835\uDCC9', 21245 'tscy': '\u0446', 21246 'tshcy': '\u045B', 21247 'tstrok': '\u0167', 21248 'uHar': '\u2963', 21249 'uacute': '\u00FA', 21250 'ubrcy': '\u045E', 21251 'ubreve': '\u016D', 21252 'ucirc': '\u00FB', 21253 'ucy': '\u0443', 21254 'udblac': '\u0171', 21255 'ufisht': '\u297E', 21256 'ufr': '\uD835\uDD32', 21257 'ugrave': '\u00F9', 21258 'uhblk': '\u2580', 21259 'ulcorn': '\u231C', 21260 'ulcorner': '\u231C', 21261 'ulcrop': '\u230F', 21262 'ultri': '\u25F8', 21263 'umacr': '\u016B', 21264 'uogon': '\u0173', 21265 'uopf': '\uD835\uDD66', 21266 'upsi': '\u03C5', 21267 'upsilon': '\u03C5', 21268 'upuparrows': '\u21C8', 21269 'uuarr': '\u21C8', 21270 'urcorn': '\u231D', 21271 'urcorner': '\u231D', 21272 'urcrop': '\u230E', 21273 'uring': '\u016F', 21274 'urtri': '\u25F9', 21275 'uscr': '\uD835\uDCCA', 21276 'utdot': '\u22F0', 21277 'utilde': '\u0169', 21278 'uuml': '\u00FC', 21279 'uwangle': '\u29A7', 21280 'vBar': '\u2AE8', 21281 'vBarv': '\u2AE9', 21282 'vangrt': '\u299C', 21283 'varsubsetneq': '\u228A\uFE00', 21284 'vsubne': '\u228A\uFE00', 21285 'varsubsetneqq': '\u2ACB\uFE00', 21286 'vsubnE': '\u2ACB\uFE00', 21287 'varsupsetneq': '\u228B\uFE00', 21288 'vsupne': '\u228B\uFE00', 21289 'varsupsetneqq': '\u2ACC\uFE00', 21290 'vsupnE': '\u2ACC\uFE00', 21291 'vcy': '\u0432', 21292 'veebar': '\u22BB', 21293 'veeeq': '\u225A', 21294 'vellip': '\u22EE', 21295 'vfr': '\uD835\uDD33', 21296 'vopf': '\uD835\uDD67', 21297 'vscr': '\uD835\uDCCB', 21298 'vzigzag': '\u299A', 21299 'wcirc': '\u0175', 21300 'wedbar': '\u2A5F', 21301 'wedgeq': '\u2259', 21302 'weierp': '\u2118', 21303 'wp': '\u2118', 21304 'wfr': '\uD835\uDD34', 21305 'wopf': '\uD835\uDD68', 21306 'wscr': '\uD835\uDCCC', 21307 'xfr': '\uD835\uDD35', 21308 'xi': '\u03BE', 21309 'xnis': '\u22FB', 21310 'xopf': '\uD835\uDD69', 21311 'xscr': '\uD835\uDCCD', 21312 'yacute': '\u00FD', 21313 'yacy': '\u044F', 21314 'ycirc': '\u0177', 21315 'ycy': '\u044B', 21316 'yen': '\u00A5', 21317 'yfr': '\uD835\uDD36', 21318 'yicy': '\u0457', 21319 'yopf': '\uD835\uDD6A', 21320 'yscr': '\uD835\uDCCE', 21321 'yucy': '\u044E', 21322 'yuml': '\u00FF', 21323 'zacute': '\u017A', 21324 'zcaron': '\u017E', 21325 'zcy': '\u0437', 21326 'zdot': '\u017C', 21327 'zeta': '\u03B6', 21328 'zfr': '\uD835\uDD37', 21329 'zhcy': '\u0436', 21330 'zigrarr': '\u21DD', 21331 'zopf': '\uD835\uDD6B', 21332 'zscr': '\uD835\uDCCF', 21333 'zwj': '\u200D', 21334 'zwnj': '\u200C' 21335}; 21336// The &ngsp; pseudo-entity is denoting a space. 21337// 0xE500 is a PUA (Private Use Areas) unicode character 21338// This is inspired by the Angular Dart implementation. 21339const NGSP_UNICODE = '\uE500'; 21340NAMED_ENTITIES['ngsp'] = NGSP_UNICODE; 21341class TokenError extends ParseError { 21342 constructor(errorMsg, tokenType, span) { 21343 super(span, errorMsg); 21344 this.tokenType = tokenType; 21345 } 21346} 21347class TokenizeResult { 21348 constructor(tokens, errors, nonNormalizedIcuExpressions) { 21349 this.tokens = tokens; 21350 this.errors = errors; 21351 this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions; 21352 } 21353} 21354function tokenize(source, url, getTagDefinition, options = {}) { 21355 const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options); 21356 tokenizer.tokenize(); 21357 return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions); 21358} 21359const _CR_OR_CRLF_REGEXP = /\r\n?/g; 21360function _unexpectedCharacterErrorMsg(charCode) { 21361 const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode); 21362 return `Unexpected character "${char}"`; 21363} 21364function _unknownEntityErrorMsg(entitySrc) { 21365 return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`; 21366} 21367function _unparsableEntityErrorMsg(type, entityStr) { 21368 return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`; 21369} 21370var CharacterReferenceType; 21371(function (CharacterReferenceType) { 21372 CharacterReferenceType["HEX"] = "hexadecimal"; 21373 CharacterReferenceType["DEC"] = "decimal"; 21374})(CharacterReferenceType || (CharacterReferenceType = {})); 21375class _ControlFlowError { 21376 constructor(error) { 21377 this.error = error; 21378 } 21379} 21380// See https://www.w3.org/TR/html51/syntax.html#writing-html-documents 21381class _Tokenizer { 21382 /** 21383 * @param _file The html source file being tokenized. 21384 * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name. 21385 * @param options Configuration of the tokenization. 21386 */ 21387 constructor(_file, _getTagDefinition, options) { 21388 this._getTagDefinition = _getTagDefinition; 21389 this._currentTokenStart = null; 21390 this._currentTokenType = null; 21391 this._expansionCaseStack = []; 21392 this._inInterpolation = false;
21393 this.tokens = []; 21394 this.errors = []; 21395 this.nonNormalizedIcuExpressions = []; 21396 this._tokenizeIcu = options.tokenizeExpansionForms || false; 21397 this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG; 21398 this._leadingTriviaCodePoints = options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0); 21399 const range = options.range || { 21400 endPos: _file.content.length, 21401 startPos: 0, 21402 startLine: 0, 21403 startCol: 0 21404 }; 21405 this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) : new PlainCharacterCursor(_file, range); 21406 this._preserveLineEndings = options.preserveLineEndings || false; 21407 this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false; 21408 this._tokenizeBlocks = options.tokenizeBlocks ?? true; 21409 try { 21410 this._cursor.init(); 21411 } catch (e) { 21412 this.handleError(e); 21413 } 21414 } 21415 _processCarriageReturns(content) { 21416 if (this._preserveLineEndings) { 21417 return content; 21418 } 21419 // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream 21420 // In order to keep the original position in the source, we can not 21421 // pre-process it. 21422 // Instead CRs are processed right before instantiating the tokens. 21423 return content.replace(_CR_OR_CRLF_REGEXP, '\n'); 21424 } 21425 tokenize() { 21426 while (this._cursor.peek() !== $EOF) { 21427 const start = this._cursor.clone(); 21428 try { 21429 if (this._attemptCharCode($LT)) { 21430 if (this._attemptCharCode($BANG)) { 21431 if (this._attemptCharCode($LBRACKET)) { 21432 this._consumeCdata(start); 21433 } else if (this._attemptCharCode($MINUS)) { 21434 this._consumeComment(start); 21435 } else { 21436 this._consumeDocType(start); 21437 } 21438 } else if (this._attemptCharCode($SLASH)) { 21439 this._consumeTagClose(start); 21440 } else { 21441 this._consumeTagOpen(start); 21442 } 21443 } else if (this._tokenizeBlocks && this._attemptCharCode($AT)) { 21444 this._consumeBlockStart(start); 21445 } else if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansionCase() && !this._isInExpansionForm() && this._attemptCharCode($RBRACE)) { 21446 this._consumeBlockEnd(start); 21447 } else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) { 21448 // In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while 21449 // the premature end of an interpolation is given by the start of a new HTML element. 21450 this._consumeWithInterpolation(5 /* TokenType.TEXT */, 8 /* TokenType.INTERPOLATION */, () => this._isTextEnd(), () => this._isTagStart()); 21451 } 21452 } catch (e) { 21453 this.handleError(e); 21454 } 21455 } 21456 this._beginToken(29 /* TokenType.EOF */); 21457 this._endToken([]); 21458 } 21459 _getBlockName() { 21460 // This allows us to capture up something like `@else if`, but not `@ if`. 21461 let spacesInNameAllowed = false; 21462 const nameCursor = this._cursor.clone(); 21463 this._attemptCharCodeUntilFn(code => { 21464 if (isWhitespace(code)) { 21465 return !spacesInNameAllowed; 21466 } 21467 if (isBlockNameChar(code)) { 21468 spacesInNameAllowed = true; 21469 return false; 21470 } 21471 return true; 21472 }); 21473 return this._cursor.getChars(nameCursor).trim(); 21474 } 21475 _consumeBlockStart(start) { 21476 this._beginToken(24 /* TokenType.BLOCK_OPEN_START */, start); 21477 const startToken = this._endToken([this._getBlockName()]); 21478 if (this._cursor.peek() === $LPAREN) { 21479 // Advance past the opening paren. 21480 this._cursor.advance(); 21481 // Capture the parameters. 21482 this._consumeBlockParameters(); 21483 // Allow spaces before the closing paren. 21484 this._attemptCharCodeUntilFn(isNotWhitespace); 21485 if (this._attemptCharCode($RPAREN)) { 21486 // Allow spaces after the paren. 21487 this._attemptCharCodeUntilFn(isNotWhitespace); 21488 } else { 21489 startToken.type = 28 /* TokenType.INCOMPLETE_BLOCK_OPEN */; 21490 return; 21491 } 21492 } 21493 if (this._attemptCharCode($LBRACE)) { 21494 this._beginToken(25 /* TokenType.BLOCK_OPEN_END */); 21495 this._endToken([]); 21496 } else { 21497 startToken.type = 28 /* TokenType.INCOMPLETE_BLOCK_OPEN */; 21498 } 21499 } 21500 _consumeBlockEnd(start) { 21501 this._beginToken(26 /* TokenType.BLOCK_CLOSE */, start); 21502 this._endToken([]); 21503 } 21504 _consumeBlockParameters() { 21505 // Trim the whitespace until the first parameter. 21506 this._attemptCharCodeUntilFn(isBlockParameterChar); 21507 while (this._cursor.peek() !== $RPAREN && this._cursor.peek() !== $EOF) { 21508 this._beginToken(27 /* TokenType.BLOCK_PARAMETER */); 21509 const start = this._cursor.clone(); 21510 let inQuote = null; 21511 let openParens = 0; 21512 // Consume the parameter until the next semicolon or brace. 21513 // Note that we skip over semicolons/braces inside of strings. 21514 while (this._cursor.peek() !== $SEMICOLON && this._cursor.peek() !== $EOF || inQuote !== null) { 21515 const char = this._cursor.peek(); 21516 // Skip to the next character if it was escaped. 21517 if (char === $BACKSLASH) { 21518 this._cursor.advance(); 21519 } else if (char === inQuote) { 21520 inQuote = null; 21521 } else if (inQuote === null && isQuote(char)) { 21522 inQuote = char; 21523 } else if (char === $LPAREN && inQuote === null) { 21524 openParens++; 21525 } else if (char === $RPAREN && inQuote === null) { 21526 if (openParens === 0) { 21527 break; 21528 } else if (openParens > 0) { 21529 openParens--; 21530 } 21531 } 21532 this._cursor.advance(); 21533 } 21534 this._endToken([this._cursor.getChars(start)]); 21535 // Skip to the next parameter. 21536 this._attemptCharCodeUntilFn(isBlockParameterChar); 21537 } 21538 } 21539 /** 21540 * @returns whether an ICU token has been created 21541 * @internal 21542 */ 21543 _tokenizeExpansionForm() { 21544 if (this.isExpansionFormStart()) { 21545 this._consumeExpansionFormStart(); 21546 return true; 21547 } 21548 if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) { 21549 this._consumeExpansionCaseStart(); 21550 return true; 21551 } 21552 if (this._cursor.peek() === $RBRACE) { 21553 if (this._isInExpansionCase()) { 21554 this._consumeExpansionCaseEnd(); 21555 return true; 21556 } 21557 if (this._isInExpansionForm()) { 21558 this._consumeExpansionFormEnd(); 21559 return true; 21560 } 21561 } 21562 return false;
21563 } 21564 _beginToken(type, start = this._cursor.clone()) { 21565 this._currentTokenStart = start; 21566 this._currentTokenType = type; 21567 } 21568 _endToken(parts, end) { 21569 if (this._currentTokenStart === null) { 21570 throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end)); 21571 } 21572 if (this._currentTokenType === null) { 21573 throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart)); 21574 } 21575 const token = { 21576 type: this._currentTokenType, 21577 parts, 21578 sourceSpan: (end ?? this._cursor).getSpan(this._currentTokenStart, this._leadingTriviaCodePoints) 21579 }; 21580 this.tokens.push(token); 21581 this._currentTokenStart = null; 21582 this._currentTokenType = null; 21583 return token; 21584 } 21585 _createError(msg, span) { 21586 if (this._isInExpansionForm()) { 21587 msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`; 21588 } 21589 const error = new TokenError(msg, this._currentTokenType, span); 21590 this._currentTokenStart = null; 21591 this._currentTokenType = null; 21592 return new _ControlFlowError(error); 21593 } 21594 handleError(e) { 21595 if (e instanceof CursorError) { 21596 e = this._createError(e.msg, this._cursor.getSpan(e.cursor)); 21597 } 21598 if (e instanceof _ControlFlowError) { 21599 this.errors.push(e.error); 21600 } else { 21601 throw e; 21602 } 21603 } 21604 _attemptCharCode(charCode) { 21605 if (this._cursor.peek() === charCode) { 21606 this._cursor.advance(); 21607 return true; 21608 } 21609 return false; 21610 } 21611 _attemptCharCodeCaseInsensitive(charCode) { 21612 if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCo
21612de)) { 21613 this._cursor.advance(); 21614 return true; 21615 } 21616 return false; 21617 } 21618 _requireCharCode(charCode) { 21619 const location = this._cursor.clone(); 21620 if (!this._attemptCharCode(charCode)) { 21621 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location)); 21622 } 21623 } 21624 _attemptStr(chars) { 21625 const len = chars.length; 21626 if (this._cursor.charsLeft() < len) { 21627 return false; 21628 } 21629 const initialPosition = this._cursor.clone(); 21630 for (let i = 0; i < len; i++) { 21631 if (!this._attemptCharCode(chars.charCodeAt(i))) { 21632 // If attempting to parse the string fails, we want to reset the parser 21633 // to where it was before the attempt 21634 this._cursor = initialPosition; 21635 return false; 21636 } 21637 } 21638 return true; 21639 } 21640 _attemptStrCaseInsensitive(chars) { 21641 for (let i = 0; i < chars.length; i++) { 21642 if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) { 21643 return false; 21644 } 21645 } 21646 return true; 21647 } 21648 _requireStr(chars) { 21649 const location = this._cursor.clone(); 21650 if (!this._attemptStr(chars)) { 21651 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location)); 21652 } 21653 } 21654 _attemptCharCodeUntilFn(predicate) { 21655 while (!predicate(this._cursor.peek())) { 21656 this._cursor.advance(); 21657 } 21658 } 21659 _requireCharCodeUntilFn(predicate, len) { 21660 const start = this._cursor.clone(); 21661 this._attemptCharCodeUntilFn(predicate); 21662 if (this._cursor.diff(start) < len) { 21663 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start)); 21664 } 21665 } 21666 _attemptUntilChar(char) { 21667 while (this._cursor.peek() !== char) { 21668 this._cursor.advance(); 21669 } 21670 } 21671 _readChar() { 21672 // Don't rely upon reading directly from `_input` as the actual char value 21673 // may have been generated from an escape sequence. 21674 const char = String.fromCodePoint(this._cursor.peek()); 21675 this._cursor.advance(); 21676 return char; 21677 } 21678 _consumeEntity(textTokenType) { 21679 this._beginToken(9 /* TokenType.ENCODED_ENTITY */); 21680 const start = this._cursor.clone(); 21681 this._cursor.advance(); 21682 if (this._attemptCharCode($HASH)) { 21683 const isHex = this._attemptCharCode($x) || this._attemptCharCode($X); 21684 const codeStart = this._cursor.clone(); 21685 this._attemptCharCodeUntilFn(isDigitEntityEnd); 21686 if (this._cursor.peek() != $SEMICOLON) { 21687 // Advance cursor to include the peeked character in the string provided to the error 21688 // message. 21689 this._cursor.advance(); 21690 const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC; 21691 throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan()); 21692 } 21693 const strNum = this._cursor.getChars(codeStart); 21694 this._cursor.advance(); 21695 try { 21696 const charCode = parseInt(strNum, isHex ? 16 : 10); 21697 this._endToken([String.fromCharCode(charCode), this._cursor.getChars(start)]); 21698 } catch { 21699 throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan()); 21700 } 21701 } else { 21702 const nameStart = this._cursor.clone(); 21703 this._attemptCharCodeUntilFn(isNamedEntityEnd); 21704 if (this._cursor.peek() != $SEMICOLON) { 21705 // No semicolon was found so abort the encoded entity token that was in progress, and treat 21706 // this as a text token 21707 this._beginToken(textTokenType, start); 21708 this._cursor = nameStart; 21709 this._endToken(['&']); 21710 } else { 21711 const name = this._cursor.getChars(nameStart); 21712 this._cursor.advance(); 21713 const char = NAMED_ENTITIES[name]; 21714 if (!char) { 21715 throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start)); 21716 } 21717 this._endToken([char, `&${name};`]); 21718 } 21719 } 21720 } 21721 _consumeRawText(consumeEntities, endMarkerPredicate) { 21722 this._beginToken(consumeEntities ? 6 /* TokenType.ESCAPABLE_RAW_TEXT */ : 7 /* TokenType.RAW_TEXT */); 21723 const parts = []; 21724 while (true) { 21725 const tagCloseStart = this._cursor.clone(); 21726 const foundEndMarker = endMarkerPredicate(); 21727 this._cursor = tagCloseStart; 21728 if (foundEndMarker) { 21729 break; 21730 } 21731 if (consumeEntities && this._cursor.peek() === $AMPERSAND) { 21732 this._endToken([this._processCarriageReturns(parts.join(''))]); 21733 parts.length = 0; 21734 this._consumeEntity(6 /* TokenType.ESCAPABLE_RAW_TEXT */); 21735 this._beginToken(6 /* TokenType.ESCAPABLE_RAW_TEXT */); 21736 } else { 21737 parts.push(this._readChar()); 21738 } 21739 } 21740 this._endToken([this._processCarriageReturns(parts.join(''))]); 21741 } 21742 _consumeComment(start) { 21743 this._beginToken(10 /* TokenType.COMMENT_START */, start); 21744 this._requireCharCode($MINUS); 21745 this._endToken([]); 21746 this._consumeRawText(false, () => this._attemptStr('-->')); 21747 this._beginToken(11 /* TokenType.COMMENT_END */); 21748 this._requireStr('-->'); 21749 this._endToken([]); 21750 } 21751 _consumeCdata(start) { 21752 this._beginToken(12 /* TokenType.CDATA_START */, start); 21753 this._requireStr('CDATA['); 21754 this._endToken([]); 21755 this._consumeRawText(false, () => this._attemptStr(']]>')); 21756 this._beginToken(13 /* TokenType.CDATA_END */); 21757 this._requireStr(']]>'); 21758 this._endToken([]); 21759 } 21760 _consumeDocType(start) { 21761 this._beginToken(18 /* TokenType.DOC_TYPE */, start); 21762 const contentStart = this._cursor.clone(); 21763 this._attemptUntilChar($GT); 21764 const content = this._cursor.getChars(contentStart); 21765 this._cursor.advance(); 21766 this._endToken([content]); 21767 } 21768 _consumePrefixAndName() { 21769 const nameOrPrefixStart = this._cursor.clone(); 21770 let prefix = ''; 21771 while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) { 21772 this._cursor.advance(); 21773 } 21774 let nameStart; 21775 if (this._cursor.peek() === $COLON) { 21776 prefix = this._cursor.getChars(nameOrPrefixStart); 21777 this._cursor.advance(); 21778 nameStart = this._cursor.clone(); 21779 } else { 21780 nameStart = nameOrPrefixStart; 21781 } 21782 this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1); 21783 const name = this._cursor.getChars(nameStart); 21784 return [prefix, name]; 21785 } 21786 _consumeTagOpen(start) { 21787 let tagName; 21788 let prefix; 21789 let openTagToken; 21790 try { 21791 if (!isAsciiLetter(this._cursor.peek())) { 21792 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start)); 21793 } 21794 openTagToken = this._consumeTagOpenStart(start); 21795 prefix = openTagToken.parts[0]; 21796 tagName = openTagToken.parts[1]; 21797 this._attemptCharCodeUntilFn(isNotWhitespace); 21798 while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT && this._cursor.peek() !== $LT && this._cursor.peek() !== $EOF) { 21799 this._consumeAttributeName(); 21800 this._attemptCharCodeUntilFn(isNotWhitespace); 21801 if (this._attemptCharCode($EQ)) { 21802 this._attemptCharCodeUntilFn(isNotWhitespace); 21803 this._consumeAttributeValue(); 21804 } 21805 this._attemptCharCodeUntilFn(isNotWhitespace); 21806 } 21807 this._consumeTagOpenEnd(); 21808 } catch (e) { 21809 if (e instanceof _ControlFlowError) { 21810 if (openTagToken) { 21811 // We errored before we could close the opening tag, so it is incomplete. 21812 openTagToken.type = 4 /* TokenType.INCOMPLETE_TAG_OPEN */; 21813 } else {
21814 // When the start tag is invalid, assume we want a "<" as text. 21815 // Back to back text tokens are merged at the end. 21816 this._beginToken(5 /* TokenType.TEXT */, start); 21817 this._endToken(['<']); 21818 } 21819 return; 21820 } 21821 throw e; 21822 } 21823 const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix); 21824 if (contentTokenType === TagContentType.RAW_TEXT) { 21825 this._consumeRawTextWithTagClose(prefix, tagName, false); 21826 } else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) { 21827 this._consumeRawTextWithTagClose(prefix, tagName, true); 21828 } 21829 } 21830 _consumeRawTextWithTagClose(prefix, tagName, consumeEntities) { 21831 this._consumeRawText(consumeEntities, () => { 21832 if (!this._attemptCharCode($LT)) return false; 21833 if (!this._attemptCharCode($SLASH)) return false; 21834 this._attemptCharCodeUntilFn(isNotWhitespace); 21835 if (!this._attemptStrCaseInsensitive(tagName)) return false; 21836 this._attemptCharCodeUntilFn(isNotWhitespace); 21837 return this._attemptCharCode($GT); 21838 }); 21839 this._beginToken(3 /* TokenType.TAG_CLOSE */); 21840 this._requireCharCodeUntilFn(code => code === $GT, 3); 21841 this._cursor.advance(); // Consume the `>` 21842 this._endToken([prefix, tagName]); 21843 } 21844 _consumeTagOpenStart(start) { 21845 this._beginToken(0 /* TokenType.TAG_OPEN_START */, start); 21846 const parts = this._consumePrefixAndName(); 21847 return this._endToken(parts); 21848 } 21849 _consumeAttributeName() { 21850 const attrNameStart = this._cursor.peek(); 21851 if (attrNameStart === $SQ || attrNameStart === $DQ) { 21852 throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan()); 21853 } 21854 this._beginToken(14 /* TokenType.ATTR_NAME */); 21855 const prefixAndName = this._consumePrefixAndName(); 21856 this._endToken(prefixAndName); 21857 } 21858 _consumeAttributeValue() { 21859 if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) { 21860 const quoteChar = this._cursor.peek(); 21861 this._consumeQuote(quoteChar); 21862 // In an attribute then end of the attribute value and the premature end to an interpolation 21863 // are both triggered by the `quoteChar`. 21864 const endPredicate = () => this._cursor.peek() === quoteChar; 21865 this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate); 21866 this._consumeQuote(quoteChar); 21867 } else { 21868 const endPredicate = () => isNameEnd(this._cursor.peek()); 21869 this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate); 21870 } 21871 } 21872 _consumeQuote(quoteChar) { 21873 this._beginToken(15 /* TokenType.ATTR_QUOTE */); 21874 this._requireCharCode(quoteChar); 21875 this._endToken([String.fromCodePoint(quoteChar)]); 21876 } 21877 _consumeTagOpenEnd() { 21878 const tokenType = this._attemptCharCode($SLASH) ? 2 /* TokenType.TAG_OPEN_END_VOID */ : 1 /* TokenType.TAG_OPEN_END */; 21879 this._beginToken(tokenType); 21880 this._requireCharCode($GT); 21881 this._endToken([]); 21882 } 21883 _consumeTagClose(start) { 21884 this._beginToken(3 /* TokenType.TAG_CLOSE */, start); 21885 this._attemptCharCodeUntilFn(isNotWhitespace); 21886 const prefixAndName = this._consumePrefixAndName(); 21887 this._attemptCharCodeUntilFn(isNotWhitespace); 21888 this._requireCharCode($GT); 21889 this._endToken(prefixAndName); 21890 } 21891 _consumeExpansionFormStart() { 21892 this._beginToken(19 /* TokenType.EXPANSION_FORM_START */); 21893 this._requireCharCode($LBRACE); 21894 this._endToken([]); 21895 this._expansionCaseStack.push(19 /* TokenType.EXPANSION_FORM_START */); 21896 this._beginToken(7 /* TokenType.RAW_TEXT */); 21897 const condition = this._readUntil($COMMA); 21898 const normalizedCondition = this._processCarriageReturns(condition); 21899 if (this._i18nNormalizeLineEndingsInICUs) { 21900 // We explicitly want to normalize line endings for this text. 21901 this._endToken([normalizedCondition]); 21902 } else { 21903 // We are not normalizing line endings. 21904 const conditionToken = this._endToken([condition]); 21905 if (normalizedCondition !== condition) { 21906 this.nonNormalizedIcuExpressions.push(conditionToken); 21907 } 21908 } 21909 this._requireCharCode($COMMA); 21910 this._attemptCharCodeUntilFn(isNotWhitespace); 21911 this._beginToken(7 /* TokenType.RAW_TEXT */); 21912 const type = this._readUntil($COMMA); 21913 this._endToken([type]); 21914 this._requireCharCode($COMMA); 21915 this._attemptCharCodeUntilFn(isNotWhitespace); 21916 } 21917 _consumeExpansionCaseStart() { 21918 this._beginToken(20 /* TokenType.EXPANSION_CASE_VALUE */); 21919 const value = this._readUntil($LBRACE).trim(); 21920 this._endToken([value]); 21921 this._attemptCharCodeUntilFn(isNotWhitespace); 21922 this._beginToken(21 /* TokenType.EXPANSION_CASE_EXP_START */); 21923 this._requireCharCode($LBRACE); 21924 this._endToken([]); 21925 this._attemptCharCodeUntilFn(isNotWhitespace); 21926 this._expansionCaseStack.push(21 /* TokenType.EXPANSION_CASE_EXP_START */); 21927 } 21928 _consumeExpansionCaseEnd() { 21929 this._beginToken(22 /* TokenType.EXPANSION_CASE_EXP_END */); 21930 this._requireCharCode($RBRACE); 21931 this._endToken([]); 21932 this._attemptCharCodeUntilFn(isNotWhitespace); 21933 this._expansionCaseStack.pop(); 21934 } 21935 _consumeExpansionFormEnd() { 21936 this._beginToken(23 /* TokenType.EXPANSION_FORM_END */); 21937 this._requireCharCode($RBRACE); 21938 this._endToken([]); 21939 this._expansionCaseStack.pop(); 21940 } 21941 /** 21942 * Consume a string that may contain interpolation expressions. 21943 * 21944 * The first token consumed will be of `tokenType` and then there will be alternating 21945 * `interpolationTokenType` and `tokenType` tokens until the `endPredicate()` returns true. 21946 * 21947 * If an interpolation token ends prematurely it will have no end marker in its `parts` array. 21948 * 21949 * @param textTokenType the kind of tokens to interleave around interpolation tokens. 21950 * @param interpolationTokenType the kind of tokens that contain interpolation. 21951 * @param endPredicate a function that should return true when we should stop consuming. 21952 * @param endInterpolation a function that should return true if there is a premature end to an 21953 * interpolation expression - i.e. before we get to the normal interpolation closing marker. 21954 */
21955 _consumeWithInterpolation(textTokenType, interpolationTokenType, endPredicate, endInterpolation) { 21956 this._beginToken(textTokenType); 21957 const parts = []; 21958 while (!endPredicate()) { 21959 const current = this._cursor.clone(); 21960 if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) { 21961 this._endToken([this._processCarriageReturns(parts.join(''))], current); 21962 parts.length = 0; 21963 this._consumeInterpolation(interpolationTokenType, current, endInterpolation); 21964 this._beginToken(textTokenType); 21965 } else if (this._cursor.peek() === $AMPERSAND) { 21966 this._endToken([this._processCarriageReturns(parts.join(''))]); 21967 parts.length = 0; 21968 this._consumeEntity(textTokenType); 21969 this._beginToken(textTokenType); 21970 } else { 21971 parts.push(this._readChar()); 21972 } 21973 } 21974 // It is possible that an interpolation was started but not ended inside this text token. 21975 // Make sure that we reset the state of the lexer correctly. 21976 this._inInterpolation = false; 21977 this._endToken([this._processCarriageReturns(parts.join(''))]); 21978 } 21979 /** 21980 * Consume a block of text that has been interpreted as an Angular interpolation. 21981 * 21982 * @param interpolationTokenType the type of the interpolation token to generate. 21983 * @param interpolationStart a cursor that points to the start of this interpolation. 21984 * @param prematureEndPredicate a function that should return true if the next characters indicate 21985 * an end to the interpolation before its normal closing marker. 21986 */ 21987 _consumeInterpolation(interpolationTokenType, interpolationStart, prematureEndPredicate) { 21988 const parts = []; 21989 this._beginToken(interpolationTokenType, interpolationStart); 21990 parts.push(this._interpolationConfig.start); 21991 // Find the end of the interpolation, ignoring content inside quotes. 21992 const expressionStart = this._cursor.clone(); 21993 let inQuote = null; 21994 let inComment = false; 21995 while (this._cursor.peek() !== $EOF && (prematureEndPredicate === null || !prematureEndPredicate())) { 21996 const current = this._cursor.clone(); 21997 if (this._isTagStart()) { 21998 // We are starting what looks like an HTML element in the middle of this interpolation. 21999 // Reset the cursor to before the `<` character and end the interpolation token. 22000 // (This is actually wrong but here for backward compatibility). 22001 this._cursor = current; 22002 parts.push(this._getProcessedChars(expressionStart, current)); 22003 this._endToken(parts); 22004 return; 22005 } 22006 if (inQuote === null) { 22007 if (this._attemptStr(this._interpolationConfig.end)) { 22008 // We are not in a string, and we hit the end interpolation marker 22009 parts.push(this._getProcessedChars(expressionStart, current)); 22010 parts.push(this._interpolationConfig.end); 22011 this._endToken(parts); 22012 return; 22013 } else if (this._attemptStr('//')) { 22014 // Once we are in a comment we ignore any quotes 22015 inComment = true; 22016 } 22017 } 22018 const char = this._cursor.peek(); 22019 this._cursor.advance(); 22020 if (char === $BACKSLASH) { 22021 // Skip the next character because it was escaped. 22022 this._cursor.advance(); 22023 } else if (char === inQuote) { 22024 // Exiting the current quoted string 22025 inQuote = null; 22026 } else if (!inComment && inQuote === null && isQuote(char)) { 22027 // Entering a new quoted string 22028 inQuote = char; 22029 } 22030 } 22031 // We hit EOF without finding a closing interpolation marker 22032 parts.push(this._getProcessedChars(expressionStart, this._cursor)); 22033 this._endToken(parts); 22034 } 22035 _getProcessedChars(start, end) { 22036 return this._processCarriageReturns(end.getChars(start)); 22037 } 22038 _isTextEnd() { 22039 if (this._isTagStart() || this._cursor.peek() === $EOF) { 22040 return true; 22041 } 22042 if (this._tokenizeIcu && !this._inInterpolation) { 22043 if (this.isExpansionFormStart()) { 22044 // start of an expansion form 22045 return true; 22046 } 22047 if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) { 22048 // end of and expansion case 22049 return true; 22050 } 22051 } 22052 if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansion() && (this._cursor.peek() === $AT || this._cursor.peek() === $RBRACE)) { 22053 return true; 22054 } 22055 return false;
22056 } 22057 /** 22058 * Returns true if the current cursor is pointing to the start of a tag 22059 * (opening/closing/comments/cdata/etc). 22060 */ 22061 _isTagStart() { 22062 if (this._cursor.peek() === $LT) { 22063 // We assume that `<` followed by whitespace is not the start of an HTML element. 22064 const tmp = this._cursor.clone(); 22065 tmp.advance(); 22066 // If the next character is alphabetic, ! nor / then it is a tag start 22067 const code = tmp.peek(); 22068 if ($a <= code && code <= $z || $A <= code && code <= $Z || code === $SLASH || code === $BANG) { 22069 return true; 22070 } 22071 } 22072 return false; 22073 } 22074 _readUntil(char) { 22075 const start = this._cursor.clone(); 22076 this._attemptUntilChar(char); 22077 return this._cursor.getChars(start); 22078 } 22079 _isInExpansion() { 22080 return this._isInExpansionCase() || this._isInExpansionForm(); 22081 } 22082 _isInExpansionCase() { 22083 return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 21 /* TokenType.EXPANSION_CASE_EXP_START */; 22084 } 22085 _isInExpansionForm() { 22086 return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 19 /* TokenType.EXPANSION_FORM_START */; 22087 } 22088 isExpansionFormStart() { 22089 if (this._cursor.peek() !== $LBRACE) { 22090 return false; 22091 } 22092 if (this._interpolationConfig) { 22093 const start = this._cursor.clone(); 22094 const isInterpolation = this._attemptStr(this._interpolationConfig.start); 22095 this._cursor = start; 22096 return !isInterpolation; 22097 } 22098 return true; 22099 } 22100} 22101function isNotWhitespace(code) { 22102 return !isWhitespace(code) || code === $EOF; 22103} 22104function isNameEnd(code) { 22105 return isWhitespace(code) || code === $GT || code === $LT || code === $SLASH || code === $SQ || code === $DQ || code === $EQ || code === $EOF; 22106} 22107function isPrefixEnd(code) { 22108 return (code < $a || $z < code) && (code < $A || $Z < code) && (code < $0 || code > $9); 22109} 22110function isDigitEntityEnd(code) { 22111 return code === $SEMICOLON || code === $EOF || !isAsciiHexDigit(code); 22112} 22113function isNamedEntityEnd(code) { 22114 return code === $SEMICOLON || code === $EOF || !isAsciiLetter(code); 22115} 22116function isExpansionCaseStart(peek) { 22117 return peek !== $RBRACE; 22118} 22119function compareCharCodeCaseInsensitive(code1, code2) { 22120 return toUpperCaseCharCode(code1) === toUpperCaseCharCode(code2); 22121} 22122function toUpperCaseCharCode(code) { 22123 return code >= $a && code <= $z ? code - $a + $A : code; 22124} 22125function isBlockNameChar(code) { 22126 return isAsciiLetter(code) || isDigit(code) || code === $_; 22127} 22128function isBlockParameterChar(code) { 22129 return code !== $SEMICOLON && isNotWhitespace(code); 22130} 22131function mergeTextTokens(srcTokens) { 22132 const dstTokens = []; 22133 let lastDstToken = undefined; 22134 for (let i = 0; i < srcTokens.length; i++) { 22135 const token = srcTokens[i]; 22136 if (lastDstToken && lastDstToken.type === 5 /* TokenType.TEXT */ && token.type === 5 /* TokenType.TEXT */ || lastDstToken && lastDstToken.type === 16 /* TokenType.ATTR_VALUE_TEXT */ && token.type === 16 /* TokenType.ATTR_VALUE_TEXT */) { 22137 lastDstToken.parts[0] += token.parts[0]; 22138 lastDstToken.sourceSpan.end = token.sourceSpan.end; 22139 } else { 22140 lastDstToken = token; 22141 dstTokens.push(lastDstToken); 22142 } 22143 } 22144 return dstTokens; 22145} 22146class PlainCharacterCursor { 22147 constructor(fileOrCursor, range) { 22148 if (fileOrCursor instanceof PlainCharacterCursor) { 22149 this.file = fileOrCursor.file; 22150 this.input = fileOrCursor.input; 22151 this.end = fileOrCursor.end; 22152 const state = fileOrCursor.state; 22153 // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty. 22154 // In ES5 bundles the object spread operator is translated into the `__assign` helper, which 22155 // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is 22156 // called in tight loops, this difference matters. 22157 this.state = { 22158 peek: state.peek, 22159 offset: state.offset, 22160 line: state.line, 22161 column: state.column 22162 }; 22163 } else { 22164 if (!range) { 22165 throw new Error('Programming error: the range argument must be provided with a file argument.'); 22166 } 22167 this.file = fileOrCursor; 22168 this.input = fileOrCursor.content; 22169 this.end = range.endPos; 22170 this.state = { 22171 peek: -1, 22172 offset: range.startPos, 22173 line: range.startLine, 22174 column: range.startCol 22175 }; 22176 } 22177 } 22178 clone() { 22179 return new PlainCharacterCursor(this); 22180 } 22181 peek() { 22182 return this.state.peek; 22183 } 22184 charsLeft() {
22185 return this.end - this.state.offset; 22186 } 22187 diff(other) { 22188 return this.state.offset - other.state.offset; 22189 } 22190 advance() { 22191 this.advanceState(this.state); 22192 } 22193 init() { 22194 this.updatePeek(this.state); 22195 } 22196 getSpan(start, leadingTriviaCodePoints) { 22197 start = start || this; 22198 let fullStart = start; 22199 if (leadingTriviaCodePoints) { 22200 while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) { 22201 if (fullStart === start) { 22202 start = start.clone(); 22203 } 22204 start.advance(); 22205 } 22206 } 22207 const startLocation = this.locationFromCursor(start); 22208 const endLocation = this.locationFromCursor(this); 22209 const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation; 22210 return new ParseSourceSpan(startLocation, endLocation, fullStartLocation); 22211 } 22212 getChars(start) { 22213 return this.input.substring(start.state.offset, this.state.offset); 22214 } 22215 charAt(pos) { 22216 return this.input.charCodeAt(pos); 22217 } 22218 advanceState(state) { 22219 if (state.offset >= this.end) { 22220 this.state = state; 22221 throw new CursorError('Unexpected character "EOF"', this); 22222 } 22223 const currentChar = this.charAt(state.offset); 22224 if (currentChar === $LF) { 22225 state.line++; 22226 state.column = 0; 22227 } else if (!isNewLine(currentChar)) { 22228 state.column++; 22229 } 22230 state.offset++; 22231 this.updatePeek(state); 22232 } 22233 updatePeek(state) { 22234 state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset); 22235 } 22236 locationFromCursor(cursor) { 22237 return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column); 22238 } 22239} 22240class EscapedCharacterCursor extends PlainCharacterCursor { 22241 constructor(fileOrCursor, range) { 22242 if (fileOrCursor instanceof EscapedCharacterCursor) { 22243 super(fileOrCursor); 22244 this.internalState = { 22245 ...fileOrCursor.internalState 22246 }; 22247 } else { 22248 super(fileOrCursor, range); 22249 this.internalState = this.state; 22250 } 22251 } 22252 advance() { 22253 this.state = this.internalState; 22254 super.advance(); 22255 this.processEscapeSequence(); 22256 } 22257 init() { 22258 super.init(); 22259 this.processEscapeSequence(); 22260 } 22261 clone() { 22262 return new EscapedCharacterCursor(this); 22263 } 22264 getChars(start) { 22265 const cursor = start.clone(); 22266 let chars = ''; 22267 while (cursor.internalState.offset < this.internalState.offset) { 22268 chars += String.fromCodePoint(cursor.peek()); 22269 cursor.advance(); 22270 } 22271 return chars; 22272 } 22273 /** 22274 * Process the escape sequence that starts at the current position in the text. 22275 * 22276 * This method is called to ensure that `peek` has the unescaped value of escape sequences. 22277 */ 22278 processEscapeSequence() { 22279 const peek = () => this.internalState.peek; 22280 if (peek() === $BACKSLASH) { 22281 // We have hit an escape sequence so we need the internal state to become independent 22282 // of the external state. 22283 this.internalState = { 22284 ...this.state 22285 }; 22286 // Move past the backslash 22287 this.advanceState(this.internalState); 22288 // First check for standard control char sequences 22289 if (peek() === $n) { 22290 this.state.peek = $LF; 22291 } else if (peek() === $r) { 22292 this.state.peek = $CR; 22293 } else if (peek() === $v) { 22294 this.state.peek = $VTAB; 22295 } else if (peek() === $t) { 22296 this.state.peek = $TAB; 22297 } else if (peek() === $b) { 22298 this.state.peek = $BSPACE; 22299 } else if (peek() === $f) { 22300 this.state.peek = $FF; 22301 } 22302 // Now consider more complex sequences 22303 else if (peek() === $u) { 22304 // Unicode code-point sequence 22305 this.advanceState(this.internalState); // advance past the `u` char 22306 if (peek() === $LBRACE) { 22307 // Variable length Unicode, e.g. `\x{123}` 22308 this.advanceState(this.internalState); // advance past the `{` char 22309 // Advance past the variable number of hex digits until we hit a `}` char 22310 const digitStart = this.clone(); 22311 let length = 0; 22312 while (peek() !== $RBRACE) { 22313 this.advanceState(this.internalState); 22314 length++; 22315 } 22316 this.state.peek = this.decodeHexDigits(digitStart, length); 22317 } else { 22318 // Fixed length Unicode, e.g. `\u1234` 22319 const digitStart = this.clone(); 22320 this.advanceState(this.internalState); 22321 this.advanceState(this.internalState); 22322 this.advanceState(this.internalState); 22323 this.state.peek = this.decodeHexDigits(digitStart, 4); 22324 } 22325 } else if (peek() === $x) { 22326 // Hex char code, e.g. `\x2F` 22327 this.advanceState(this.internalState); // advance past the `x` char 22328 const digitStart = this.clone(); 22329 this.advanceState(this.internalState); 22330 this.state.peek = this.decodeHexDigits(digitStart, 2); 22331 } else if (isOctalDigit(peek())) { 22332 // Octal char code, e.g. `\012`, 22333 let octal = ''; 22334 let length = 0; 22335 let previous = this.clone(); 22336 while (isOctalDigit(peek()) && length < 3) { 22337 previous = this.clone(); 22338 octal += String.fromCodePoint(peek()); 22339 this.advanceState(this.internalState); 22340 length++; 22341 } 22342 this.state.peek = parseInt(octal, 8); 22343 // Backup one char 22344 this.internalState = previous.internalState; 22345 } else if (isNewLine(this.internalState.peek)) { 22346 // Line continuation `\` followed by a new line 22347 this.advanceState(this.internalState); // advance over the newline 22348 this.state = this.internalState; 22349 } else { 22350 // If none of the `if` blocks were executed then we just have an escaped normal character. 22351 // In that case we just, effectively, skip the backslash from the character. 22352 this.state.peek = this.internalState.peek; 22353 } 22354 } 22355 } 22356 decodeHexDigits(start, length) { 22357 const hex = this.input.slice(start.internalState.offset, start.internalState.offset + length);
22358 const charCode = parseInt(hex, 16); 22359 if (!isNaN(charCode)) { 22360 return charCode; 22361 } else { 22362 start.state = start.internalState; 22363 throw new CursorError('Invalid hexadecimal escape sequence', start); 22364 } 22365 } 22366} 22367class CursorError { 22368 constructor(msg, cursor) { 22369 this.msg = msg; 22370 this.cursor = cursor; 22371 } 22372} 22373class TreeError extends ParseError { 22374 static create(elementName, span, msg) { 22375 return new TreeError(elementName, span, msg); 22376 } 22377 constructor(elementName, span, msg) { 22378 super(span, msg); 22379 this.elementName = elementName; 22380 } 22381} 22382class ParseTreeResult { 22383 constructor(rootNodes, errors) { 22384 this.rootNodes = rootNodes; 22385 this.errors = errors; 22386 } 22387} 22388class Parser { 22389 constructor(getTagDefinition) { 22390 this.getTagDefinition = getTagDefinition; 22391 } 22392 parse(source, url, options) { 22393 const tokenizeResult = tokenize(source, url, this.getTagDefinition, options); 22394 const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition); 22395 parser.build(); 22396 return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors)); 22397 } 22398} 22399class _TreeBuilder { 22400 constructor(tokens, getTagDefinition) { 22401 this.tokens = tokens; 22402 this.getTagDefinition = getTagDefinition; 22403 this._index = -1; 22404 this._containerStack = []; 22405 this.rootNodes = []; 22406 this.errors = []; 22407 this._advance(); 22408 } 22409 build() { 22410 while (this._peek.type !== 29 /* TokenType.EOF */) { 22411 if (this._peek.type === 0 /* TokenType.TAG_OPEN_START */ || this._peek.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) { 22412 this._consumeStartTag(this._advance()); 22413 } else if (this._peek.type === 3 /* TokenType.TAG_CLOSE */) { 22414 this._consumeEndTag(this._advance()); 22415 } else if (this._peek.type === 12 /* TokenType.CDATA_START */) { 22416 this._closeVoidElement(); 22417 this._consumeCdata(this._advance()); 22418 } else if (this._peek.type === 10 /* TokenType.COMMENT_START */) { 22419 this._closeVoidElement(); 22420 this._consumeComment(this._advance()); 22421 } else if (this._peek.type === 5 /* TokenType.TEXT */ || this._peek.type === 7 /* TokenType.RAW_TEXT */ || this._peek.type === 6 /* TokenType.ESCAPABLE_RAW_TEXT */) { 22422 this._closeVoidElement(); 22423 this._consumeText(this._advance()); 22424 } else if (this._peek.type === 19 /* TokenType.EXPANSION_FORM_START */) { 22425 this._consumeExpansion(this._advance()); 22426 } else if (this._peek.type === 24 /* TokenType.BLOCK_OPEN_START */) { 22427 this._closeVoidElement(); 22428 this._consumeBlockOpen(this._advance()); 22429 } else if (this._peek.type === 26 /* TokenType.BLOCK_CLOSE */) { 22430 this._closeVoidElement(); 22431 this._consumeBlockClose(this._advance()); 22432 } else if (this._peek.type === 28 /* TokenType.INCOMPLETE_BLOCK_OPEN */) { 22433 this._closeVoidElement(); 22434 this._consumeIncompleteBlock(this._advance()); 22435 } else { 22436 // Skip all other tokens... 22437 this._advance(); 22438 } 22439 } 22440 for (const leftoverContainer of this._containerStack) { 22441 // Unlike HTML elements, blocks aren't closed implicitly by the end of the file. 22442 if (leftoverContainer instanceof Block) { 22443 this.errors.push(TreeError.create(leftoverContainer.name, leftoverContainer.sourceSpan, `Unclosed block "${leftoverContainer.name}"`)); 22444 } 22445 } 22446 } 22447 _advance() { 22448 const prev = this._peek; 22449 if (this._index < this.tokens.length - 1) { 22450 // Note: there is always an EOF token at the end 22451 this._index++; 22452 } 22453 this._peek = this.tokens[this._index]; 22454 return prev; 22455 } 22456 _advanceIf(type) { 22457 if (this._peek.type === type) { 22458 return this._advance(); 22459 } 22460 return null; 22461 } 22462 _consumeCdata(_startToken) { 22463 this._consumeText(this._advance()); 22464 this._advanceIf(13 /* TokenType.CDATA_END */); 22465 } 22466 _consumeComment(token) { 22467 const text = this._advanceIf(7 /* TokenType.RAW_TEXT */); 22468 const endToken = this._advanceIf(11 /* TokenType.COMMENT_END */); 22469 const value = text != null ? text.parts[0].trim() : null; 22470 const sourceSpan = endToken == null ? token.sourceSpan : new ParseSourceSpan(token.sourceSpan.start, endToken.sourceSpan.end, token.sourceSpan.fullStart); 22471 this._addToParent(new Comment(value, sourceSpan)); 22472 } 22473 _consumeExpansion(token) { 22474 const switchValue = this._advance(); 22475 const type = this._advance(); 22476 const cases = []; 22477 // read = 22478 while (this._peek.type === 20 /* TokenType.EXPANSION_CASE_VALUE */) { 22479 const expCase = this._parseExpansionCase(); 22480 if (!expCase) return; // error 22481 cases.push(expCase); 22482 } 22483 // read the final } 22484 if (this._peek.type !== 23 /* TokenType.EXPANSION_FORM_END */) { 22485 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`)); 22486 return; 22487 } 22488 const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart); 22489 this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan)); 22490 this._advance(); 22491 } 22492 _parseExpansionCase() { 22493 const value = this._advance(); 22494 // read { 22495 if (this._peek.type !== 21 /* TokenType.EXPANSION_CASE_EXP_START */) { 22496 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`)); 22497 return null; 22498 } 22499 // read until } 22500 const start = this._advance(); 22501 const exp = this._collectExpansionExpTokens(start); 22502 if (!exp) return null; 22503 const end = this._advance(); 22504 exp.push({ 22505 type: 29 /* TokenType.EOF */, 22506 parts: [], 22507 sourceSpan: end.sourceSpan 22508 }); 22509 // parse everything in between { and } 22510 const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition); 22511 expansionCaseParser.build(); 22512 if (expansionCaseParser.errors.length > 0) { 22513 this.errors = this.errors.concat(expansionCaseParser.errors); 22514 return null; 22515 } 22516 const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart); 22517 const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart); 22518 return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan); 22519 } 22520 _collectExpansionExpTokens(start) { 22521 const exp = []; 22522 const expansionFormStack = [21 /* TokenType.EXPANSION_CASE_EXP_START */]; 22523 while (true) { 22524 if (this._peek.type === 19 /* TokenType.EXPANSION_FORM_START */ || this._peek.type === 21 /* TokenType.EXPANSION_CASE_EXP_START */) { 22525 expansionFormStack.push(this._peek.type); 22526 } 22527 if (this._peek.type === 22 /* TokenType.EXPANSION_CASE_EXP_END */) { 22528 if (lastOnStack(expansionFormStack, 21 /* TokenType.EXPANSION_CASE_EXP_START */)) { 22529 expansionFormStack.pop(); 22530 if (expansionFormStack.length === 0) return exp; 22531 } else { 22532 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`)); 22533 return null; 22534 } 22535 } 22536 if (this._peek.type === 23 /* TokenType.EXPANSION_FORM_END */) { 22537 if (lastOnStack(expansionFormStack, 19 /* TokenType.EXPANSION_FORM_START */)) { 22538 expansionFormStack.pop(); 22539 } else { 22540 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`)); 22541 return null; 22542 } 22543 } 22544 if (this._peek.type === 29 /* TokenType.EOF */) { 22545 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`)); 22546 return null; 22547 } 22548 exp.push(this._advance()); 22549 } 22550 } 22551 _consumeText(token) { 22552 const tokens = [token]; 22553 const startSpan = token.sourceSpan; 22554 let text = token.parts[0]; 22555 if (text.length > 0 && text[0] === '\n') { 22556 const parent = this._getContainer(); 22557 if (parent != null && parent.children.length === 0 && this.getTagDefinition(parent.name).ignoreFirstLf) { 22558 text = text.substring(1); 22559 tokens[0] = { 22560 type: token.type, 22561 sourceSpan: token.sourceSpan, 22562 parts: [text] 22563 }; 22564 } 22565 } 22566 while (this._peek.type === 8 /* TokenType.INTERPOLATION */ || this._peek.type === 5 /* TokenType.TEXT */ || this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) { 22567 token = this._advance(); 22568 tokens.push(token); 22569 if (token.type === 8 /* TokenType.INTERPOLATION */) { 22570 // For backward compatibility we decode HTML entities that appear in interpolation 22571 // expressions. This is arguably a bug, but it could be a considerable breaking change to 22572 // fix it. It should be addressed in a larger project to refactor the entire parser/lexer 22573 // chain after View Engine has been removed. 22574 text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity); 22575 } else if (token.type === 9 /* TokenType.ENCODED_ENTITY */) { 22576 text += token.parts[0]; 22577 } else { 22578 text += token.parts.join(''); 22579 } 22580 } 22581 if (text.length > 0) { 22582 const endSpan = token.sourceSpan; 22583 this._addToParent(new Text(text, new ParseSourceSpan(start
22583Span.start, endSpan.end, startSpan.fullStart, startSpan.details), tokens)); 22584 } 22585 } 22586 _closeVoidElement() { 22587 const el = this._getContainer(); 22588 if (el instanceof Element && this.getTagDefinition(el.name).isVoid) { 22589 this._containerStack.pop(); 22590 } 22591 } 22592 _consumeStartTag(startTagToken) { 22593 const [prefix, name] = startTagToken.parts; 22594 const attrs = []; 22595 while (this._peek.type === 14 /* TokenType.ATTR_NAME */) { 22596 attrs.push(this._consumeAttr(this._advance())); 22597 } 22598 const fullName = this._getElementFullName(prefix, name, this._getClosestParentElement()); 22599 let selfClosing = false; 22600 // Note: There could have been a tokenizer error 22601 // so that we don't get a token for the end tag... 22602 if (this._peek.type === 2 /* TokenType.TAG_OPEN_END_VOID */) { 22603 this._advance(); 22604 selfClosing = true; 22605 const tagDef = this.getTagDefinition(fullName); 22606 if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) { 22607 this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void, custom and foreign elements can be self closed "${startTagToken.parts[1]}"`)); 22608 } 22609 } else if (this._peek.type === 1 /* TokenType.TAG_OPEN_END */) { 22610 this._advance(); 22611 selfClosing = false; 22612 } 22613 const end = this._peek.sourceSpan.fullStart; 22614 const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart); 22615 // Create a separate `startSpan` because `span` will be modified when there is an `end` span. 22616 const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart); 22617 const el = new Element(fullName, attrs, [], span, startSpan, undefined); 22618 const parentEl = this._getContainer(); 22619 this._pushContainer(el, parentEl instanceof Element && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)); 22620 if (selfClosing) { 22621 // Elements that are self-closed have their `endSourceSpan` set to the full span, as the 22622 // element start tag also represents the end tag. 22623 this._popContainer(fullName, Element, span); 22624 } else if (startTagToken.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) { 22625 // We already know the opening tag is not complete, so it is unlikely it has a corresponding 22626 // close tag. Let's optimistically parse it as a full element and emit an error. 22627 this._popContainer(fullName, Element, null); 22628 this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`)); 22629 } 22630 } 22631 _pushContainer(node, isClosedByChild) { 22632 if (isClosedByChild) { 22633 this._containerStack.pop(); 22634 } 22635 this._addToParent(node); 22636 this._containerStack.push(node); 22637 } 22638 _consumeEndTag(endTagToken) { 22639 const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getClosestParentElement()); 22640 if (this.getTagDefinition(fullName).isVoid) { 22641 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`)); 22642 } else if (!this._popContainer(fullName, Element, endTagToken.sourceSpan)) { 22643 const errMsg = `Unexpected closing tag "${fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`; 22644 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg)); 22645 } 22646 } 22647 /** 22648 * Closes the nearest element with the tag name `fullName` in the parse tree. 22649 * `endSourceSpan` is the span of the closing tag, or null if the element does 22650 * not have a closing tag (for example, this happens when an incomplete 22651 * opening tag is recovered). 22652 */ 22653 _popContainer(expectedName, expectedType, endSourceSpan) { 22654 let unexpectedCloseTagDetected = false; 22655 for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) { 22656 const node = this._containerStack[stackIndex]; 22657 if ((node.name === expectedName || expectedName === null) && node instanceof expectedType) { 22658 // Record the parse span with the element that is being closed. Any elements that are 22659 // removed from the element stack at this point are closed implicitly, so they won't get 22660 // an end source span (as there is no explicit closing element). 22661 node.endSourceSpan = endSourceSpan; 22662 node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end; 22663 this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex); 22664 return !unexpectedCloseTagDetected; 22665 } 22666 // Blocks and most elements are not self closing. 22667 if (node instanceof Block || node instanceof Element && !this.getTagDefinition(node.name).closedByParent) { 22668 // Note that we encountered an unexpected close tag but continue processing the element 22669 // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this 22670 // end tag in the stack. 22671 unexpectedCloseTagDetected = true; 22672 } 22673 } 22674 return false;
22675 } 22676 _consumeAttr(attrName) { 22677 const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]); 22678 let attrEnd = attrName.sourceSpan.end; 22679 // Consume any quote 22680 if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) { 22681 this._advance(); 22682 } 22683 // Consume the attribute value 22684 let value = ''; 22685 const valueTokens = []; 22686 let valueStartSpan = undefined; 22687 let valueEnd = undefined; 22688 // NOTE: We need to use a new variable `nextTokenType` here to hide the actual type of 22689 // `_peek.type` from TS. Otherwise TS will narrow the type of `_peek.type` preventing it from 22690 // being able to consider `ATTR_VALUE_INTERPOLATION` as an option. This is because TS is not 22691 // able to see that `_advance()` will actually mutate `_peek`. 22692 const nextTokenType = this._peek.type; 22693 if (nextTokenType === 16 /* TokenType.ATTR_VALUE_TEXT */) { 22694 valueStartSpan = this._peek.sourceSpan; 22695 valueEnd = this._peek.sourceSpan.end; 22696 while (this._peek.type === 16 /* TokenType.ATTR_VALUE_TEXT */ || this._peek.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */ || this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) { 22697 const valueToken = this._advance(); 22698 valueTokens.push(valueToken); 22699 if (valueToken.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */) { 22700 // For backward compatibility we decode HTML entities that appear in interpolation 22701 // expressions. This is arguably a bug, but it could be a considerable breaking change to 22702 // fix it. It should be addressed in a larger project to refactor the entire parser/lexer 22703 // chain after View Engine has been removed. 22704 value += valueToken.parts.join('').replace(/&([^;]+);/g, decodeEntity); 22705 } else if (valueToken.type === 9 /* TokenType.ENCODED_ENTITY */) { 22706 value += valueToken.parts[0]; 22707 } else { 22708 value += valueToken.parts.join(''); 22709 } 22710 valueEnd = attrEnd = valueToken.sourceSpan.end; 22711 } 22712 } 22713 // Consume any quote 22714 if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) { 22715 const quoteToken = this._advance(); 22716 attrEnd = quoteToken.sourceSpan.end; 22717 } 22718 const valueSpan = valueStartSpan && valueEnd && new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart); 22719 return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined); 22720 } 22721 _consumeBlockOpen(token) { 22722 const parameters = []; 22723 while (this._peek.type === 27 /* TokenType.BLOCK_PARAMETER */) { 22724 const paramToken = this._advance(); 22725 parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan)); 22726 } 22727 if (this._peek.type === 25 /* TokenType.BLOCK_OPEN_END */) { 22728 this._advance(); 22729 } 22730 const end = this._peek.sourceSpan.fullStart; 22731 const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart); 22732 // Create a separate `startSpan` because `span` will be modified when there is an `end` span. 22733 const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart); 22734 const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan); 22735 this._pushContainer(block, false); 22736 } 22737 _consumeBlockClose(token) { 22738 if (!this._popContainer(null, Block, token.sourceSpan)) { 22739 this.errors.push(TreeError.create(null, token.sourceSpan, `Unexpected closing block. The block may have been closed earlier. ` + `If you meant to write the } character, you should use the "}" ` + `HTML entity instead.`)); 22740 } 22741 } 22742 _consumeIncompleteBlock(token) { 22743 const parameters = []; 22744 while (this._peek.type === 27 /* TokenType.BLOCK_PARAMETER */) { 22745 const paramToken = this._advance(); 22746 parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan)); 22747 } 22748 const end = this._peek.sourceSpan.fullStart; 22749 const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart); 22750 // Create a separate `startSpan` because `span` will be modified when there is an `end` span. 22751 const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart); 22752 const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan); 22753 this._pushContainer(block, false); 22754 // Incomplete blocks don't have children so we close them immediately and report an error. 22755 this._popContainer(null, Block, null); 22756 this.errors.push(TreeError.create(token.parts[0], span, `Incomplete block "${token.parts[0]}". If you meant to write the @ character, ` + `you should use the "@" HTML entity instead.`)); 22757 } 22758 _getContainer() { 22759 return this._containerStack.length > 0 ? this._containerStack[this._containerStack.length - 1] : null; 22760 } 22761 _getClosestParentElement() { 22762 for (let i = this._containerStack.length - 1; i > -1; i--) { 22763 if (this._containerStack[i] instanceof Element) { 22764 return this._containerStack[i]; 22765 } 22766 } 22767 return null; 22768 } 22769 _addToParent(node) { 22770 const parent = this._getContainer(); 22771 if (parent === null) { 22772 this.rootNodes.push(node); 22773 } else { 22774 parent.children.push(node); 22775 } 22776 } 22777 _getElementFullName(prefix, localName, parentElement) { 22778 if (prefix === '') { 22779 prefix = this.getTagDefinition(localName).implicitNamespacePrefix || ''; 22780 if (prefix === '' && parentElement != null) { 22781 const parentTagName = splitNsName(parentElement.name)[1]; 22782 const parentTagDefinition = this.getTagDefinition(parentTagName); 22783 if (!parentTagDefinition.preventNamespaceInheritance) { 22784 prefix = getNsPrefix(parentElement.name); 22785 } 22786 } 22787 } 22788 return mergeNsAndName(prefix, localName); 22789 } 22790} 22791function lastOnStack(stack, element) { 22792 return stack.length > 0 && stack[stack.length - 1] === element; 22793} 22794/** 22795 * Decode the `entity` string, which we believe is the contents of an HTML entity. 22796 * 22797 * If the string is not actually a valid/known entity then just return the original `match` string. 22798 */ 22799function decodeEntity(match, entity) { 22800 if (NAMED_ENTITIES[entity] !== undefined) { 22801 return NAMED_ENTITIES[entity] || match; 22802 } 22803 if (/^#x[a-f0-9]+$/i.test(entity)) { 22804 return String.fromCodePoint(parseInt(entity.slice(2), 16)); 22805 } 22806 if (/^#\d+$/.test(entity)) { 22807 return String.fromCodePoint(parseInt(entity.slice(1), 10)); 22808 } 22809 return match; 22810} 22811 22812/** 22813 * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all 22814 * tags use '*'. 22815 * 22816 * Extracted from, and should be kept in sync with 22817 * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations 22818 */ 22819const TRUSTED_TYPES_SINKS = new Set([ 22820// NOTE: All strings in this set *must* be lowercase! 22821// TrustedHTML 22822'iframe|srcdoc', '*|innerhtml', '*|outerhtml', 22823// NB: no TrustedScript here, as the corresponding tags are stripped by the compiler. 22824// TrustedScriptURL 22825'embed|src', 'object|codebase', 'object|data']); 22826/** 22827 * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types 22828 * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular 22829 * Trusted Type is required for values passed to the sink: 22830 * - SecurityContext.HTML corresponds to TrustedHTML 22831 * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL 22832 */ 22833function isTrustedTypesSink(tagName, propName) { 22834 // Make sure comparisons are case insensitive, so that case differences between attribute and 22835 // property names do not have a security impact. 22836 tagName = tagName.toLowerCase(); 22837 propName = propName.toLowerCase(); 22838 return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) || TRUSTED_TYPES_SINKS.has('*|' + propName); 22839} 22840const setI18nRefs = (htmlNode, i18nNode) => { 22841 if (htmlNode instanceof NodeWithI18n) { 22842 if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) { 22843 // This html node represents an ICU but this is a second processing pass, and the legacy id 22844 // was computed in the previous pass and stored in the `i18n` property as a message. 22845 // We are about to wipe out that property so capture the previous message to be reused when 22846 // generating the message for this ICU later. See `_generateI18
22846nMessage()`. 22847 i18nNode.previousMessage = htmlNode.i18n; 22848 } 22849 htmlNode.i18n = i18nNode; 22850 } 22851 return i18nNode; 22852}; 22853/** 22854 * This visitor walks over HTML parse tree and converts information stored in 22855 * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is 22856 * stored with other element's and attribute's information. 22857 */ 22858class I18nMetaVisitor { 22859 constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false, containerBlocks = DEFAULT_CONTAINER_BLOCKS) { 22860 this.interpolationConfig = interpolationConfig; 22861 this.keepI18nAttrs = keepI18nAttrs; 22862 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat; 22863 this.containerBlocks = containerBlocks; 22864 // whether visited nodes contain i18n information 22865 this.hasI18nMeta = false; 22866 this._errors = []; 22867 } 22868 _generateI18nMessage(nodes, meta = '', visitNodeFn) { 22869 const { 22870 meaning, 22871 description, 22872 customId 22873 } = this._parseMetadata(meta); 22874 const createI18nMessage = createI18nMessageFactory(this.interpolationConfig, this.containerBlocks); 22875 const message = createI18nMessage(nodes, meaning, description, customId, visitNodeFn); 22876 this._setMessageId(message, meta); 22877 this._setLegacyIds(message, meta); 22878 return message; 22879 } 22880 visitAllWithErrors(nodes) { 22881 const result = nodes.map(node => node.visit(this, null)); 22882 return new ParseTreeResult(result, this._errors); 22883 } 22884 visitElement(element) { 22885 let message = undefined; 22886 if (hasI18nAttrs(element)) { 22887 this.hasI18nMeta = true; 22888 const attrs = []; 22889 const attrsMeta = {}; 22890 for (const attr of element.attrs) { 22891 if (attr.name === I18N_ATTR) { 22892 // root 'i18n' node attribute 22893 const i18n = element.i18n || attr.value; 22894 message = this._generateI18nMessage(element.children, i18n, setI18nRefs); 22895 if (message.nodes.length === 0) { 22896 // Ignore the message if it is empty. 22897 message = undefined; 22898 } 22899 // Store the message on the element 22900 element.i18n = message; 22901 } else if (attr.name.startsWith(I18N_ATTR_PREFIX)) { 22902 // 'i18n-*' attributes 22903 const name = attr.name.slice(I18N_ATTR_PREFIX.length); 22904 if (isTrustedTypesSink(element.name, name)) { 22905 this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`); 22906 } else { 22907 attrsMeta[name] = attr.value; 22908 } 22909 } else { 22910 // non-i18n attributes 22911 attrs.push(attr); 22912 } 22913 } 22914 // set i18n meta for attributes 22915 if (Object.keys(attrsMeta).length) { 22916 for (const attr of attrs) { 22917 const meta = attrsMeta[attr.name]; 22918 // do not create translation for empty attributes 22919 if (meta !== undefined && attr.value) { 22920 attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta); 22921 } 22922 } 22923 } 22924 if (!this.keepI18nAttrs) { 22925 // update element's attributes, 22926 // keeping only non-i18n related ones 22927 element.attrs = attrs; 22928 } 22929 } 22930 visitAll(this, element.children, message); 22931 return element; 22932 } 22933 visitExpansion(expansion, currentMessage) { 22934 let message; 22935 const meta = expansion.i18n; 22936 this.hasI18nMeta = true; 22937 if (meta instanceof IcuPlaceholder) { 22938 // set ICU placeholder name (e.g. "ICU_1"), 22939 // generated while processing root element contents, 22940 // so we can reference it when we output translation 22941 const name = meta.name; 22942 message = this._generateI18nMessage([expansion], meta); 22943 const icu = icuFromI18nMessage(message); 22944 icu.name = name; 22945 if (currentMessage !== null) { 22946 // Also update the placeholderToMessage map with this new message 22947 currentMessage.placeholderToMessage[name] = message; 22948 } 22949 } else { 22950 // ICU is a top level message, try to use metadata from container element if provided via 22951 // `context` argument. Note: context may not be available for standalone ICUs (without 22952 // wrapping element), so fallback to ICU metadata in this case.
22953 message = this._generateI18nMessage([expansion], currentMessage || meta); 22954 } 22955 expansion.i18n = message; 22956 return expansion; 22957 } 22958 visitText(text) { 22959 return text; 22960 } 22961 visitAttribute(attribute) { 22962 return attribute; 22963 } 22964 visitComment(comment) { 22965 return comment; 22966 } 22967 visitExpansionCase(expansionCase) { 22968 return expansionCase; 22969 } 22970 visitBlock(block, context) { 22971 visitAll(this, block.children, context); 22972 return block; 22973 } 22974 visitBlockParameter(parameter, context) { 22975 return parameter; 22976 } 22977 /** 22978 * Parse the general form `meta` passed into extract the explicit metadata needed to create a 22979 * `Message`. 22980 * 22981 * There are three possibilities for the `meta` variable 22982 * 1) a string from an `i18n` template attribute: parse it to extract the metadata values. 22983 * 2) a `Message` from a previous processing pass: reuse the metadata values in the message. 22984 * 4) other: ignore this and just process the message metadata as normal 22985 * 22986 * @param meta the bucket that holds information about the message 22987 * @returns the parsed metadata. 22988 */ 22989 _parseMetadata(meta) { 22990 return typeof meta === 'string' ? parseI18nMeta(meta) : meta instanceof Message ? meta : {}; 22991 } 22992 /** 22993 * Generate (or restore) message id if not specified already. 22994 */ 22995 _setMessageId(message, meta) { 22996 if (!message.id) { 22997 message.id = meta instanceof Message && meta.id || decimalDigest(message); 22998 } 22999 } 23000 /** 23001 * Update the `message` with a `legacyId` if necessary. 23002 * 23003 * @param message the message whose legacy id should be set 23004 * @param meta information about the message being processed 23005 */ 23006 _setLegacyIds(message, meta) { 23007 if (this.enableI18nLegacyMessageIdFormat) { 23008 message.legacyIds = [computeDigest(message), computeDecimalDigest(message)]; 23009 } else if (typeof meta !== 'string') { 23010 // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in 23011 // `packages/compiler/src/render3/view/template.ts`). 23012 // In that case we want to reuse the legacy message generated in the 1st pass (see 23013 // `setI18nRefs()`). 23014 const previousMessage = meta instanceof Message ? meta : meta instanceof IcuPlaceholder ? meta.previousMessage : undefined; 23015 message.legacyIds = previousMessage ? previousMessage.legacyIds : []; 23016 } 23017 } 23018 _reportError(node, msg) { 23019 this._errors.push(new I18nError(node.sourceSpan, msg)); 23020 } 23021} 23022/** I18n separators for metadata **/ 23023const I18N_MEANING_SEPARATOR = '|'; 23024const I18N_ID_SEPARATOR = '@@'; 23025/** 23026 * Parses i18n metas like: 23027 * - "@@id", 23028 * - "description[@@id]", 23029 * - "meaning|description[@@id]" 23030 * and returns an object with parsed output. 23031 * 23032 * @param meta String that represents i18n meta 23033 * @returns Object with id, meaning and description fields 23034 */ 23035function parseI18nMeta(meta = '') { 23036 let customId; 23037 let meaning; 23038 let description; 23039 meta = meta.trim(); 23040 if (meta) { 23041 const idIndex = meta.indexOf(I18N_ID_SEPARATOR); 23042 const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR); 23043 let meaningAndDesc; 23044 [meaningAndDesc, customId] = idIndex > -1 ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, '']; 23045 [meaning, description] = descIndex > -1 ? [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : ['', meaningAndDesc]; 23046 } 23047 return { 23048 customId, 23049 meaning, 23050 description 23051 }; 23052} 23053// Converts i18n meta information for a message (id, description, meaning) 23054// to a JsDoc statement formatted as expected by the Closure compiler. 23055function i18nMetaToJSDoc(meta) { 23056 const tags = []; 23057 if (meta.description) { 23058 tags.push({ 23059 tagName: "desc" /* o.JSDocTagName.Desc */, 23060 text: meta.description 23061 }); 23062 } else { 23063 // Suppress the JSCompiler warning that a `@desc` was not given for this message. 23064 tags.push({ 23065 tagName: "suppress" /* o.JSDocTagName.Suppress */, 23066 text: '{msgDescriptions}' 23067 }); 23068 } 23069 if (meta.meaning) { 23070 tags.push({ 23071 tagName: "meaning" /* o.JSDocTagName.Meaning */, 23072 text: meta.meaning 23073 }); 23074 } 23075 return jsDocComment(tags); 23076} 23077 23078/** Closure uses `goog.getMsg(message)` to lookup translations */ 23079const GOOG_GET_MSG = 'goog.getMsg'; 23080/** 23081 * Generates a `goog.getMsg()` statement and reassignment. The template: 23082 * 23083 * ```html 23084 * <div i18n>Sent from {{ sender }} to <span class="receiver">{{ receiver }}</span></div> 23085 * ``` 23086 * 23087 * Generates: 23088 * 23089 * ```typescript 23090 * const MSG_FOO = goog.getMsg(
23091 * // Message template. 23092 * 'Sent from {$interpolation} to {$startTagSpan}{$interpolation_1}{$closeTagSpan}.', 23093 * // Placeholder values, set to magic strings which get replaced by the Angular runtime. 23094 * { 23095 * 'interpolation': '\uFFFD0\uFFFD', 23096 * 'startTagSpan': '\uFFFD1\uFFFD', 23097 * 'interpolation_1': '\uFFFD2\uFFFD', 23098 * 'closeTagSpan': '\uFFFD3\uFFFD', 23099 * }, 23100 * // Options bag. 23101 * { 23102 * // Maps each placeholder to the original Angular source code which generates it's value. 23103 * original_code: { 23104 * 'interpolation': '{{ sender }}', 23105 * 'startTagSpan': '<span class="receiver">', 23106 * 'interpolation_1': '{{ receiver }}', 23107 * 'closeTagSpan': '</span>', 23108 * }, 23109 * }, 23110 * ); 23111 * const I18N_0 = MSG_FOO; 23112 * ``` 23113 */ 23114function createGoogleGetMsgStatements(variable$1, message, closureVar, placeholderValues) { 23115 const messageString = serializeI18nMessageForGetMsg(message); 23116 const args = [literal(messageString)]; 23117 if (Object.keys(placeholderValues).length) { 23118 // Message template parameters containing the magic strings replaced by the Angular runtime with 23119 // real data, e.g. `{'interpolation': '\uFFFD0\uFFFD'}`. 23120 args.push(mapLiteral(formatI18nPlaceholderNamesInMap(placeholderValues, true /* useCamelCase */), true /* quoted */)); 23121 // Message options object, which contains original source code for placeholders (as they are 23122 // present in a template, e.g. 23123 // `{original_code: {'interpolation': '{{ name }}', 'startTagSpan': '<span>'}}`. 23124 args.push(mapLiteral({ 23125 original_code: literalMap(Object.keys(placeholderValues).map(param => ({ 23126 key: formatI18nPlaceholderName(param), 23127 quoted: true, 23128 value: message.placeholders[param] ? 23129 // Get source span for typical placeholder if it exists. 23130 literal(message.placeholders[param].sourceSpan.toString()) : 23131 // Otherwise must be an ICU expression, get it's source span. 23132 literal(message.placeholderToMessage[param].nodes.map(node => node.sourceSpan.toString()).join('')) 23133 }))) 23134 })); 23135 } 23136 // /** 23137 // * @desc description of message 23138 // * @meaning meaning of message 23139 // */ 23140 // const MSG_... = goog.getMsg(..); 23141 // I18N_X = MSG_...; 23142 const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl(); 23143 googGetMsgStmt.addLeadingComment(i18nMetaToJSDoc(message)); 23144 const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar)); 23145 return [googGetMsgStmt, i18nAssignmentStmt]; 23146} 23147/** 23148 * This visitor walks over i18n tree and generates its string representation, including ICUs and 23149 * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format. 23150 */ 23151class GetMsgSerializerVisitor { 23152 formatPh(value) { 23153 return `{$${formatI18nPlaceholderName(value)}}`; 23154 } 23155 visitText(text) { 23156 return text.value; 23157 } 23158 visitContainer(container) { 23159 return container.children.map(child => child.visit(this)).join(''); 23160 } 23161 visitIcu(icu) { 23162 return serializeIcuNode(icu); 23163 } 23164 visitTagPlaceholder(ph) { 23165 return ph.isVoid ? this.formatPh(ph.startName) : `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; 23166 } 23167 visitPlaceholder(ph) { 23168 return this.formatPh(ph.name); 23169 } 23170 visitBlockPlaceholder(ph) { 23171 return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; 23172 } 23173 visitIcuPlaceholder(ph, context) { 23174 return this.formatPh(ph.name); 23175 } 23176} 23177const serializerVisitor = new GetMsgSerializerVisitor(); 23178function serializeI18nMessageForGetMsg(message) { 23179 return message.nodes.map(node => node.visit(serializerVisitor, null)).join(''); 23180} 23181function createLocalizeStatements(variable, message, params) { 23182 const { 23183 messageParts, 23184 placeHolders 23185 } = serializeI18nMessageForLocalize(message); 23186 const sourceSpan = getSourceSpan(message); 23187 const expressions = placeHolders.map(ph => params[ph.text]); 23188 const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan); 23189 const variableInitialization = variable.set(localizedString$1);
23190 return [new ExpressionStatement(variableInitialization)]; 23191} 23192/** 23193 * This visitor walks over an i18n tree, capturing literal strings and placeholders. 23194 * 23195 * The result can be used for generating the `$localize` tagged template literals. 23196 */ 23197class LocalizeSerializerVisitor { 23198 constructor(placeholderToMessage, pieces) { 23199 this.placeholderToMessage = placeholderToMessage; 23200 this.pieces = pieces; 23201 } 23202 visitText(text) { 23203 if (this.pieces[this.pieces.length - 1] instanceof LiteralPiece) { 23204 // Two literal pieces in a row means that there was some comment node in-between. 23205 this.pieces[this.pieces.length - 1].text += text.value; 23206 } else { 23207 const sourceSpan = new ParseSourceSpan(text.sourceSpan.fullStart, text.sourceSpan.end, text.sourceSpan.fullStart, text.sourceSpan.details); 23208 this.pieces.push(new LiteralPiece(text.value, sourceSpan)); 23209 } 23210 } 23211 visitContainer(container) { 23212 container.children.forEach(child => child.visit(this)); 23213 } 23214 visitIcu(icu) { 23215 this.pieces.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan)); 23216 } 23217 visitTagPlaceholder(ph) { 23218 this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan)); 23219 if (!ph.isVoid) { 23220 ph.children.forEach(child => child.visit(this)); 23221 this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan)); 23222 } 23223 } 23224 visitPlaceholder(ph) { 23225 this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan)); 23226 } 23227 visitBlockPlaceholder(ph) { 23228 this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan)); 23229 ph.children.forEach(child => child.visit(this)); 23230 this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan)); 23231 } 23232 visitIcuPlaceholder(ph) { 23233 this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan, this.placeholderToMessage[ph.name])); 23234 } 23235 createPlaceholderPiece(name, sourceSpan, associatedMessage) { 23236 return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */false), sourceSpan, associatedMessage); 23237 } 23238} 23239/** 23240 * Serialize an i18n message into two arrays: messageParts and placeholders. 23241 * 23242 * These arrays will be used to generate `$localize` tagged template literals. 23243 * 23244 * @param message The message to be serialized.
23245 * @returns an object containing the messageParts and placeholders. 23246 */ 23247function serializeI18nMessageForLocalize(message) { 23248 const pieces = []; 23249 const serializerVisitor = new LocalizeSerializerVisitor(message.placeholderToMessage, pieces); 23250 message.nodes.forEach(node => node.visit(serializerVisitor)); 23251 return processMessagePieces(pieces); 23252} 23253function getSourceSpan(message) { 23254 const startNode = message.nodes[0]; 23255 const endNode = message.nodes[message.nodes.length - 1]; 23256 return new ParseSourceSpan(startNode.sourceSpan.fullStart, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details); 23257} 23258/** 23259 * Convert the list of serialized MessagePieces into two arrays. 23260 * 23261 * One contains the literal string pieces and the other the placeholders that will be replaced by 23262 * expressions when rendering `$localize` tagged template literals. 23263 * 23264 * @param pieces The pieces to process. 23265 * @returns an object containing the messageParts and placeholders. 23266 */ 23267function processMessagePieces(pieces) { 23268 const messageParts = []; 23269 const placeHolders = []; 23270 if (pieces[0] instanceof PlaceholderPiece) { 23271 // The first piece was a placeholder so we need to add an initial empty message part. 23272 messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start)); 23273 } 23274 for (let i = 0; i < pieces.length; i++) { 23275 const part = pieces[i]; 23276 if (part instanceof LiteralPiece) { 23277 messageParts.push(part); 23278 } else { 23279 placeHolders.push(part); 23280 if (pieces[i - 1] instanceof PlaceholderPiece) { 23281 // There were two placeholders in a row, so we need to add an empty message part. 23282 messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end)); 23283 } 23284 } 23285 } 23286 if (pieces[pieces.length - 1] instanceof PlaceholderPiece) { 23287 // The last piece was a placeholder so we need to add a final empty message part. 23288 messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end)); 23289 } 23290 return { 23291 messageParts, 23292 placeHolders 23293 }; 23294} 23295function createEmptyMessagePart(location) { 23296 return new LiteralPiece('', new ParseSourceSpan(location, location)); 23297} 23298 23299/** Name of the global variable that is used to determine if we use Closure translations or not */ 23300const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode'; 23301/** 23302 * Prefix for non-`goog.getMsg` i18n-related vars. 23303 * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that 23304 * considers variables like `I18N_0` as constants and throws an error when their value changes. 23305 */ 23306const TRANSLATION_VAR_PREFIX = 'i18n_'; 23307/** Prefix of ICU expressions for post processing */ 23308const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_'; 23309/** 23310 * The escape sequence used for message param values. 23311 */ 23312const ESCAPE = '\uFFFD'; 23313/** 23314 * Lifts i18n properties into the consts array. 23315 * TODO: Can we use `ConstCollectedExpr`? 23316 * TODO: The way the various attributes are linked together is very complex. Perhaps we
23316could 23317 * simplify the process, maybe by combining the context and message ops? 23318 */ 23319function collectI18nConsts(job) { 23320 const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_'; 23321 // Step One: Build up various lookup maps we need to collect all the consts. 23322 // Context Xref -> Extracted Attribute Ops 23323 const extractedAttributesByI18nContext = new Map(); 23324 // Element/ElementStart Xref -> I18n Attributes config op 23325 const i18nAttributesByElement = new Map(); 23326 // Element/ElementStart Xref -> All I18n Expression ops for attrs on that target 23327 const i18nExpressionsByElement = new Map(); 23328 // I18n Message Xref -> I18n Message Op (TODO: use a central op map) 23329 const messages = new Map(); 23330 for (const unit of job.units) { 23331 for (const op of unit.ops()) { 23332 if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) { 23333 const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? []; 23334 attributes.push(op); 23335 extractedAttributesByI18nContext.set(op.i18nContext, attributes); 23336 } else if (op.kind === OpKind.I18nAttributes) { 23337 i18nAttributesByElement.set(op.target, op); 23338 } else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) { 23339 const expressions = i18nExpressionsByElement.get(op.target) ?? []; 23340 expressions.push(op); 23341 i18nExpressionsByElement.set(op.target, expressions); 23342 } else if (op.kind === OpKind.I18nMessage) { 23343 messages.set(op.xref, op); 23344 } 23345 } 23346 } 23347 // Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into 23348 // the const array. 23349 // 23350 // Also, each i18n message will have a variable expression that can refer to its 23351 // value. Store these expressions in the appropriate place: 23352 // 1. For normal i18n content, it also goes in the const array. We save the const index to use 23353 // later. 23354 // 2. For extracted attributes, it becomes the value of the extracted attribute instruction. 23355 // 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just 23356 // save it. 23357 const i18nValuesByContext = new Map(); 23358 const messageConstIndices = new Map(); 23359 for (const unit of job.units) { 23360 for (const op of unit.create) { 23361 if (op.kind === OpKind.I18nMessage) { 23362 if (op.messagePlaceholder === null) { 23363 const { 23364 mainVar, 23365 statements 23366 } = collectMessage(job, fileBasedI18nSuffix, messages, op); 23367 if (op.i18nBlock !== null) { 23368 // This is a regular i18n message with a corresponding i18n block. Collect it into the 23369 // const array. 23370 const i18nConst = job.addConst(mainVar, statements); 23371 messageConstIndices.set(op.i18nBlock, i18nConst); 23372 } else { 23373 // This is an i18n attribute. Extract the initializers into the const pool. 23374 job.constsInitializers.push(...statements); 23375 // Save the i18n variable value for later. 23376 i18nValuesByContext.set(op.i18nContext, mainVar); 23377 // This i18n message may correspond to an individual extracted attribute. If so, The 23378 // value of that attribute is updated to read the extracted i18n variable. 23379 const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext); 23380 if (attributesForMessage !== undefined) { 23381 for (const attr of attributesForMessage) { 23382 attr.expression = mainVar.clone(); 23383 } 23384 } 23385 } 23386 } 23387 OpList.remove(op); 23388 } 23389 } 23390 } 23391 // Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes 23392 // instruction has a config array, which contains k-v pairs describing each binding name, and the 23393 // i18n variable that provides the value. 23394 for (const unit of job.units) { 23395 for (const elem of unit.create) { 23396 if (isElementOrContainerOp(elem)) { 23397 const i18nAttributes = i18nAttributesByElement.get(elem.xref); 23398 if (i18nAttributes === undefined) { 23399 // This element is not associated with an i18n attributes configuration instruction. 23400 continue; 23401 } 23402 let i18nExpressions = i18nExpressionsByElement.get(elem.xref); 23403 if (i18nExpressions === undefined) { 23404 // Unused i18nAttributes should have already been removed. 23405 // TODO: Should the removal of those dead instructions be merged with this phase? 23406 throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction'); 23407 } 23408 // Find expressions for all the unique property names, removing duplicates. 23409 const seenPropertyNames = new Set(); 23410 i18nExpressions = i18nExpressions.filter(i18nExpr => { 23411 const seen = seenPropertyNames.has(i18nExpr.name); 23412 seenPropertyNames.add(i18nExpr.name); 23413 return !seen; 23414 }); 23415 const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => { 23416 const i18nExprValue = i18nValuesByContext.get(i18nExpr.context); 23417 if (i18nExprValue === undefined) { 23418 throw new Error('AssertionError: Could not find i18n expression\'s value'); 23419 } 23420 return [literal(i18nExpr.name), i18nExprValue]; 23421 }); 23422 i18nAttributes.i18nAttributesConfig = job.addConst(new LiteralArrayExpr(i18nAttributeConfig)); 23423 } 23424 } 23425 } 23426 // Step Four: Propagate the extracted const index into i18n ops that messages were extracted from. 23427 for (const unit of job.units) { 23428 for (const op of unit.create) { 23429 if (op.kind === OpKind.I18nStart) { 23430 const msgIndex = messageConstIndices.get(op.root); 23431 if (msgIndex === undefined) { 23432 throw new Error('AssertionError: Could not find corresponding i18n block
23432index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?'); 23433 } 23434 op.messageIndex = msgIndex; 23435 } 23436 } 23437 } 23438} 23439/** 23440 * Collects the given message into a set of statements that can be added to the const array. 23441 * This will recursively collect any sub-messages referenced from the parent message as well. 23442 */ 23443function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) { 23444 // Recursively collect any sub-messages, record each sub-message's main variable under its 23445 // placeholder so that we can add them to the params for the parent message. It is possible 23446 // that multiple sub-messages will share the same placeholder, so we need to track an array of 23447 // variables for each placeholder. 23448 const statements = []; 23449 const subMessagePlaceholders = new Map(); 23450 for (const subMessageId of messageOp.subMessages) { 23451 const subMessage = messages.get(subMessageId); 23452 const { 23453 mainVar: subMessageVar, 23454 statements: subMessageStatements 23455 } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage); 23456 statements.push(...subMessageStatements); 23457 const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? []; 23458 subMessages.push(subMessageVar); 23459 subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages); 23460 } 23461 addSubMessageParams(messageOp, subMessagePlaceholders); 23462 // Sort the params for consistency with TemaplateDefinitionBuilder output. 23463 messageOp.params = new Map([...messageOp.params.entries()].sort()); 23464 const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX)); 23465 // Closure Compiler requires const names to start with `MSG_` but disallows any other 23466 // const to start with `MSG_`. We define a variable starting with `MSG_` just for the 23467 // `goog.getMsg` call 23468 const closureVar = i18nGenerateClosureVar(job.pool, messageOp.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds); 23469 let transformFn = undefined; 23470 // If nescessary, add a post-processing step and resolve any placeholder params that are 23471 // set in post-processing. 23472 if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) { 23473 // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output. 23474 const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort()); 23475 const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */false); 23476 const extraTransformFnParams = []; 23477 if (messageOp.postprocessingParams.size > 0) { 23478 extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */true)); 23479 } 23480 transformFn = expr => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]); 23481 } 23482 // Add the message's statements 23483 statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn)); 23484 return { 23485 mainVar, 23486 statements 23487 }; 23488} 23489/** 23490 * Adds the given subMessage placeholders to the given message op. 23491 * 23492 * If a placeholder only corresponds to a single sub-message variable, we just set that variable 23493 * as the param value. However, if the placeholder corresponds to multiple sub-message 23494 * variables, we need to add a special placeholder value that is handled by the post-processing 23495 * step. We then add the array of variables as a post-processing param. 23496 */ 23497function addSubMessageParams(messageOp, subMessagePlaceholders) { 23498 for (const [placeholder, subMessages] of subMessagePlaceholders) { 23499 if (subMessages.length === 1) { 23500 messageOp.params.set(placeholder, subMessages[0]); 23501 } else { 23502 messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`)); 23503 messageOp.postprocessingParams.set(placeholder, literalArr(subMessages)); 23504 } 23505 } 23506} 23507/** 23508 * Generate statements that define a given translation message. 23509 * 23510 * ``` 23511 * var I18N_1; 23512 * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) { 23513 * var MSG_EXTERNAL_XXX = goog.getMsg( 23514 * "Some message with {$interpolation}!", 23515 * { "interpolation": "\uFFFD0\uFFFD" } 23516 * ); 23517 * I18N_1 = MSG_EXTERNAL_XXX; 23518 * } 23519 * else { 23520 * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`; 23521 * } 23522 * ``` 23523 * 23524 * @param message The original i18n AST message node 23525 * @param variable The variable that will be assigned the translation, e.g. `I18N_1`. 23526 * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`. 23527 * @param params Object mapping placeholder names to their values (e.g. 23528 * `{ "interpolation": "\uFFFD0\uFFFD" }`). 23529 * @param transformFn Optional transformation function that will be applied to the translation 23530 * (e.g. 23531 * post-processing). 23532 * @returns An array of statements that defined a given translation. 23533 */ 23534function getTranslationDeclStmts$1(message, variable, closureVar, params, transformFn) { 23535 const paramsObject = Object.fromEntries(params); 23536 const statements = [declareI18nVariable(variable), ifStmt(createClosureModeGuard$1(), createGoogleGetMsgStatements(variable, message, closureVar, paramsObject), createLocalizeStatements(variable, message, formatI18nPlaceholderNamesInMap(paramsObject, /* useCamelCase */false)))]; 23537 if (transformFn) {
23538 statements.push(new ExpressionStatement(variable.set(transformFn(variable)))); 23539 } 23540 return statements; 23541} 23542/** 23543 * Create the expression that will be used to guard the closure mode block 23544 * It is equivalent to: 23545 * 23546 * ``` 23547 * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode 23548 * ``` 23549 */ 23550function createClosureModeGuard$1() { 23551 return typeofExpr(variable(NG_I18N_CLOSURE_MODE$1)).notIdentical(literal('undefined', STRING_TYPE)).and(variable(NG_I18N_CLOSURE_MODE$1)); 23552} 23553/** 23554 * Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`). 23555 */ 23556function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExternalIds) { 23557 let name; 23558 const suffix = fileBasedI18nSuffix; 23559 if (useExternalIds) { 23560 const prefix = getTranslationConstPrefix(`EXTERNAL_`); 23561 const uniqueSuffix = pool.uniqueName(suffix); 23562 name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`; 23563 } else { 23564 const prefix = getTranslationConstPrefix(suffix); 23565 name = pool.uniqueName(prefix); 23566 } 23567 return variable(name); 23568} 23569 23570/** 23571 * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message. 23572 * Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions, 23573 * which will be applied later. 23574 */ 23575function convertI18nText(job) { 23576 for (const unit of job.units) { 23577 // Remove all text nodes within i18n blocks, their content is already captured in the i18n 23578 // message. 23579 let currentI18n = null; 23580 let currentIcu = null; 23581 const textNodeI18nBlocks = new Map(); 23582 const textNodeIcus = new Map(); 23583 const icuPlaceholderByText = new Map(); 23584 for (const op of unit.create) { 23585 switch (op.kind) { 23586 case OpKind.I18nStart: 23587 if (op.context === null) { 23588 throw Error('I18n op should have its context set.'); 23589 } 23590 currentI18n = op; 23591 break; 23592 case OpKind.I18nEnd: 23593 currentI18n = null; 23594 break; 23595 case OpKind.IcuStart: 23596 if (op.context === null) { 23597 throw Error('Icu op should have its context set.'); 23598 } 23599 currentIcu = op; 23600 break; 23601 case OpKind.IcuEnd: 23602 currentIcu = null; 23603 break; 23604 case OpKind.Text: 23605 if (currentI18n !== null) { 23606 textNodeI18nBlocks.set(op.xref, currentI18n); 23607 textNodeIcus.set(op.xref, currentIcu); 23608 if (op.icuPlaceholder !== null) { 23609 // Create an op to represent the ICU placeholder. Initially set its static text to the 23610 // value of the text op, though this may be overwritten later if this text op is a 23611 // placeholder for an interpolation. 23612 const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]); 23613 OpList.replace(op, icuPlaceholderOp); 23614 icuPlaceholderByText.set(op.xref, icuPlaceholderOp); 23615 } else { 23616 // Otherwise just remove the text op, since its value is already accounted for in the 23617 // translated message. 23618 OpList.remove(op); 23619 } 23620 } 23621 break; 23622 } 23623 } 23624 // Update any interpolations to the removed text, and instead represent them as a series of i18n 23625 // expressions that we then apply. 23626 for (const op of unit.update) { 23627 switch (op.kind) { 23628 case OpKind.InterpolateText: 23629 if (!textNodeI18nBlocks.has(op.target)) { 23630 continue; 23631 } 23632 const i18nOp = textNodeI18nBlocks.get(op.target); 23633 const icuOp = textNodeIcus.get(op.target); 23634 const icuPlaceholder = icuPlaceholderByText.get(op.target); 23635 const contextId = icuOp ? icuOp.context : i18nOp.context; 23636 const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing : I18nParamResolutionTime.Creation; 23637 const ops = []; 23638 for (let i = 0; i < op.interpolation.expressions.length; i++) { 23639 const expr = op.interpolation.expressions[i]; 23640 // For now, this i18nExpression depends on the slot context of the enclosing i18n block. 23641 // Later, we will modify this, and advance to a different point. 23642 ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan)); 23643 } 23644 OpList.replaceWithMany(op, ops); 23645 // If this interpolation is part of an ICU placeholder, add the strings and expressions to 23646 // the placeholder. 23647 if (icuPlaceholder !== undefined) { 23648 icuPlaceholder.strings = op.interpolation.strings; 23649 } 23650 break; 23651 } 23652 } 23653 } 23654} 23655 23656/** 23657 * Lifts local reference declarations on element-like structures within each view into an entry in 23658 * the `consts` array for the whole component. 23659 */ 23660function liftLocalRefs(job) { 23661 for (const unit of job.units) { 23662 for (const op of unit.create) { 23663 switch (op.kind) { 23664 case OpKind.ElementStart: 23665 case OpKind.Template: 23666 if (!Array.isArray(op.localRefs)) { 23667 throw new Error(`AssertionError: expected localRefs to be an array still`); 23668 } 23669 op.numSlotsUsed += op.localRefs.length; 23670 if (op.localRefs.length > 0) { 23671 const localRefs = serializeLocalRefs(op.localRefs); 23672 op.localRefs = job.addConst(localRefs); 23673 } else { 23674 op.localRefs = null; 23675 } 23676 break; 23677 } 23678 } 23679 } 23680} 23681function serializeLocalRefs(refs) { 23682 const constRefs = []; 23683 for (const ref of refs) { 23684 constRefs.push(literal(ref.name), literal(ref.target)); 23685 } 23686 return literalArr(constRefs); 23687} 23688 23689/** 23690 * Change namespaces between HTML, SVG and MathML, depending on the next element. 23691 */ 23692function emitNamespaceChanges(job) { 23693 for (const unit of job.units) { 23694 let activeNamespace = Namespace.HTML; 23695 for (const op of unit.create) { 23696 if (op.kind !== OpKind.ElementStart) { 23697 continue; 23698 } 23699 if (op.namespace !== activeNamespace) { 23700 OpList.insertBefore(createNamespaceOp(op.namespace), op); 23701 activeNamespace = op.namespace; 23702 } 23703 } 23704 } 23705} 23706 23707/** 23708 * Parses string representation of a style and converts it into object literal. 23709 * 23710 * @param value string representation of style as used in the `style` attribute in HTML. 23711 * Example: `color: red; height: auto`. 23712 * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height', 23713 * 'auto']` 23714 */ 23715function parse(value) { 23716 // we use a string array here instead of a string map 23717 // because a string-map is not guaranteed to retain the 23718 // order of the entries whereas a string array can be
23719 // constructed in a [key, value, key, value] format. 23720 const styles = []; 23721 let i = 0; 23722 let parenDepth = 0; 23723 let quote = 0 /* Char.QuoteNone */; 23724 let valueStart = 0; 23725 let propStart = 0; 23726 let currentProp = null; 23727 while (i < value.length) { 23728 const token = value.charCodeAt(i++); 23729 switch (token) { 23730 case 40 /* Char.OpenParen */: 23731 parenDepth++; 23732 break; 23733 case 41 /* Char.CloseParen */: 23734 parenDepth--; 23735 break; 23736 case 39 /* Char.QuoteSingle */: 23737 // valueStart needs to be there since prop values don't 23738 // have quotes in CSS 23739 if (quote === 0 /* Char.QuoteNone */) { 23740 quote = 39 /* Char.QuoteSingle */; 23741 } else if (quote === 39 /* Char.QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) { 23742 quote = 0 /* Char.QuoteNone */; 23743 } 23744 break; 23745 case 34 /* Char.QuoteDouble */: 23746 // same logic as above 23747 if (quote === 0 /* Char.QuoteNone */) { 23748 quote = 34 /* Char.QuoteDouble */; 23749 } else if (quote === 34 /* Char.QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) { 23750 quote = 0 /* Char.QuoteNone */; 23751 } 23752 break; 23753 case 58 /* Char.Colon */: 23754 if (!currentProp && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) { 23755 // TODO: Do not hyphenate CSS custom property names like: `--intentionallyCamelCase` 23756 currentProp = hyphenate(value.substring(propStart, i - 1).trim()); 23757 valueStart = i; 23758 } 23759 break; 23760 case 59 /* Char.Semicolon */: 23761 if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) { 23762 const styleVal = value.substring(valueStart, i - 1).trim(); 23763 styles.push(currentProp, styleVal); 23764 propStart = i; 23765 valueStart = 0; 23766 currentProp = null; 23767 } 23768 break; 23769 } 23770 } 23771 if (currentProp && valueStart) { 23772 const styleVal = value.slice(valueStart).trim(); 23773 styles.push(currentProp, styleVal); 23774 } 23775 return styles; 23776} 23777function hyphenate(value) { 23778 return value.replace(/[a-z][A-Z]/g, v => { 23779 return v.charAt(0) + '-' + v.charAt(1); 23780 }).toLowerCase(); 23781} 23782 23783/** 23784 * Generate names for functions and variables across all views. 23785 * 23786 * This includes propagating those names into any `ir.ReadVariableExpr`s of those variables, so that 23787 * the reads can be emitted correctly. 23788 */ 23789function nameFunctionsAndVariables(job) { 23790 addNamesToView(job.root, job.componentName, { 23791 index: 0 23792 }, job.compatibility === CompatibilityMode.TemplateDefinitionBuilder); 23793} 23794function addNamesToView(unit, baseName, state, compatibility) { 23795 if (unit.fnName === null) { 23796 // Ensure unique names for view units. This is necessary because there might be multiple 23797 // components with same names in the context of the same pool. Only add the suffix 23798 // if really needed. 23799 unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), /* alwaysIncludeSuffix */false); 23800 } 23801 // Keep track of the names we assign to variables in the view. We'll need to propagate these 23802 // into reads of those variables afterwards. 23803 const varNames = new Map(); 23804 for (const op of unit.ops()) { 23805 switch (op.kind) { 23806 case OpKind.Property: 23807 case OpKind.HostProperty: 23808 if (op.isAnimationTrigger) { 23809 op.name = '@' + op.name; 23810 } 23811 break; 23812 case OpKind.Listener: 23813 if (op.handlerFnName !== null) { 23814 break; 23815 } 23816 if (!op.hostListener && op.targetSlot.slot === null) { 23817 throw new Error(`Expected a slot to be assigned`); 23818 } 23819 let animation = ''; 23820 if (op.isAnimationListener) { 23821 op.name = `@${op.name}.${op.animationPhase}`; 23822 animation = 'animation'; 23823 } 23824 if (op.hostListener) { 23825 op.handlerFnName = `${baseName}_${animation}${op.name}_HostBindingHandler`; 23826 } else { 23827 op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot.slot}_listener`; 23828 } 23829 op.handlerFnName = sanitizeIdentifier(op.handlerFnName); 23830 break; 23831 case OpKind.TwoWayListener: 23832 if (op.handlerFnName !== null) { 23833 break; 23834 } 23835 if (op.targetSlot.slot === null) { 23836 throw new Error(`Expected a slot to be assigned`); 23837 } 23838 op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`); 23839 break; 23840 case OpKind.Variable: 23841 varNames.set(op.xref, getVariableName(unit, op.variable, state)); 23842 break; 23843 case OpKind.RepeaterCreate: 23844 if (!(unit instanceof ViewCompilationUnit)) { 23845 throw new Error(`AssertionError: must be compiling a component`); 23846 } 23847 if (op.handle.slot === null) { 23848 throw new Error(`Expected slot to be assigned`); 23849 } 23850 if (op.emptyView !== null) { 23851 const emptyView = unit.job.views.get(op.emptyView); 23852 // Repeater empty view function is at slot +2 (metadata is in the first slot). 23853 addNamesToView(emptyView, `${baseName}_${op.functionNameSuffix}Empty_${op.handle.slot + 2}`, state, compatibility); 23854 } 23855 // Repeater primary view function is at slot +1 (metadata is in the first slot). 23856 addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility); 23857 break; 23858 case OpKind.Template: 23859 if (!(unit instanceof ViewCompilationUnit)) { 23860 throw new Error(`AssertionError: must be compiling a component`); 23861 } 23862 const childView = unit.job.views.get(op.xref); 23863 if (op.handle.slot === null) { 23864 throw new Error(`Expected slot to be assigned`); 23865 } 23866 const suffix = op.functionNameSuffix.length === 0 ? '' : `_${op.functionNameSuffix}`; 23867 addNamesToView(childView, `${baseName}${suffix}_${op.handle.slot}`, state, compatibility); 23868 break; 23869 case OpKind.StyleProp: 23870 op.name = normalizeStylePropName(op.name); 23871 if (compatibility) { 23872 op.name = stripImportant(op.name); 23873 } 23874 break; 23875 case OpKind.ClassProp: 23876 if (compatibility) { 23877 op.name = stripImportant(op.name); 23878 } 23879 break; 23880 } 23881 } 23882 // Having named all variables declared in the view, now we can push those names into the 23883 // `ir.ReadVariableExpr` expressions which represent reads of those variables. 23884 for (const op of unit.ops()) { 23885 visitExpressionsInOp(op, expr => { 23886 if (!(expr instanceof ReadVariableExpr) || expr.name !== null) { 23887 return; 23888 } 23889 if (!varNames.has(expr.xref)) { 23890 throw new Error(`Variable ${expr.xref} not yet named`); 23891 } 23892 expr.name = varNames.get(expr.xref); 23893 }); 23894 } 23895} 23896function getVariableName(unit, variable, state) { 23897 if (variable.name === null) { 23898 switch (variable.kind) { 23899 case SemanticVariableKind.Context: 23900 variable.name = `ctx_r${state.index++}`; 23901 break; 23902 case SemanticVariableKind.Identifier: 23903 if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) { 23904 // TODO: Prefix increment and `_r` are for compatiblity with the old naming scheme. 23905 // This has the potential to cause collisions when `ctx` is the identifier, so we need a 23906 // special check for that as well.
23907 const compatPrefix = variable.identifier === 'ctx' ? 'i' : ''; 23908 variable.name = `${variable.identifier}_${compatPrefix}r${++state.index}`; 23909 } else { 23910 variable.name = `${variable.identifier}_i${state.index++}`; 23911 } 23912 break; 23913 default: 23914 // TODO: Prefix increment for compatibility only. 23915 variable.name = `_r${++state.index}`; 23916 break; 23917 } 23918 } 23919 return variable.name; 23920} 23921/** 23922 * Normalizes a style prop name by hyphenating it (unless its a CSS variable). 23923 */ 23924function normalizeStylePropName(name) { 23925 return name.startsWith('--') ? name : hyphenate(name); 23926} 23927/** 23928 * Strips `!important` out of the given style or class name. 23929 */ 23930function stripImportant(name) { 23931 const importantIndex = name.indexOf('!important'); 23932 if (importantIndex > -1) { 23933 return name.substring(0, importantIndex); 23934 } 23935 return name; 23936} 23937 23938/** 23939 * Merges logically sequential `NextContextExpr` operations. 23940 * 23941 * `NextContextExpr` can be referenced repeatedly, "popping" the runtime's context stack each time. 23942 * When two such expressions appear back-to-back, it's possible to merge them together into a single 23943 * `NextContextExpr` that steps multiple contexts. This merging is possible if all conditions are 23944 * met: 23945 * 23946 * * The result of the `NextContextExpr` that's folded into the subsequent one is not stored (that 23947 * is, the call is purely side-effectful). 23948 * * No operations in between them uses the implicit context. 23949 */ 23950function mergeNextContextExpressions(job) { 23951 for (const unit of job.units) { 23952 for (const op of unit.create) { 23953 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 23954 mergeNextContextsInOps(op.handlerOps); 23955 } 23956 } 23957 mergeNextContextsInOps(unit.update); 23958 } 23959} 23960function mergeNextContextsInOps(ops) { 23961 for (const op of ops) { 23962 // Look for a candidate operation to maybe merge. 23963 if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement) || !(op.statement.expr instanceof NextContextExpr)) { 23964 continue; 23965 } 23966 const mergeSteps = op.statement.expr.steps; 23967 // Try to merge this `ir.NextContextExpr`. 23968 let tryToMerge = true; 23969 for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) { 23970 visitExpressionsInOp(candidate, (expr, flags) => { 23971 if (!isIrExpression(expr)) { 23972 return expr; 23973 } 23974 if (!tryToMerge) { 23975 // Either we've already merged, or failed to merge. 23976 return; 23977 } 23978 if (flags & VisitorContextFlag.InChildOperation) { 23979 // We cannot merge into child operations. 23980 return; 23981 } 23982 switch (expr.kind) { 23983 case ExpressionKind.NextContext: 23984 // Merge the previous `ir.NextContextExpr` into this one. 23985 expr.steps += mergeSteps; 23986 OpList.remove(op); 23987 tryToMerge = false; 23988 break; 23989 case ExpressionKind.GetCurrentView: 23990 case ExpressionKind.Reference: 23991 // Can't merge past a dependency on the context. 23992 tryToMerge = false; 23993 break; 23994 } 23995 return; 23996 }); 23997 } 23998 } 23999} 24000const CONTAINER_TAG = 'ng-container'; 24001/** 24002 * Replace an `Element` or `ElementStart` whose tag is `ng-container` with a specific op. 24003 */ 24004function generateNgContainerOps(job) { 24005 for (const unit of job.units) { 24006 const updatedElementXrefs = new Set(); 24007 for (const op of unit.create) { 24008 if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) { 24009 // Transmute the `ElementStart` instruction to `ContainerStart`. 24010 op.kind = OpKind.ContainerStart; 24011 updatedElementXrefs.add(op.xref); 24012 } 24013 if (op.kind === OpKind.ElementEnd && updatedElementXrefs.has(op.xref)) { 24014 // This `ElementEnd` is associated with an `ElementStart` we already transmuted. 24015 op.kind = OpKind.ContainerEnd; 24016 } 24017 } 24018 } 24019} 24020 24021/** 24022 * Looks up an element in the given map by xref ID. 24023 */ 24024function lookupElement(elements, xref) { 24025 const el = elements.get(xref); 24026 if (el === undefined) { 24027 throw new Error('All attributes should have an element-like target.'); 24028 } 24029 return el; 24030} 24031/** 24032 * When a container is marked with `ngNonBindable`, the non-bindable characteristic also applies to 24033 * all descendants of that container. Therefore, we must emit `disableBindings` and `enableBindings` 24034 * instructions for every such container. 24035 */ 24036function disableBindings$1(job) { 24037 const elements = new Map(); 24038 for (const view of job.units) { 24039 for (const op of view.create) { 24040 if (!isElementOrContainerOp(op)) { 24041 continue; 24042 } 24043 elements.set(op.xref, op); 24044 } 24045 } 24046 for (const unit of job.units) { 24047 for (const op of unit.create) { 24048 if ((op.kind === OpKind.ElementStart || op.kind === OpKind.ContainerStart) && op.nonBindable) { 24049 OpList.insertAfter(createDisableBindingsOp(op.xref), op); 24050 } 24051 if ((op.kind === OpKind.ElementEnd || op.kind === OpKind.ContainerEnd) && lookupElement(elements, op.xref).nonBindable) { 24052 OpList.insertBefore(createEnableBindingsOp(op.xref), op); 24053 } 24054 } 24055 } 24056} 24057 24058/** 24059 * Nullish coalescing expressions such as `a ?? b` have different semantics in Angular templates as 24060 * compared to JavaScript. In particular, they default to `null` instead of `undefined`. Therefore, 24061 * we replace them with ternary expressions, assigning temporaries as needed to avoid re-evaluating 24062 * the same sub-expression multiple times. 24063 */ 24064function generateNullishCoalesceExpressions(job) { 24065 for (const unit of job.units) { 24066 for (const op of unit.ops()) { 24067 transformExpressionsInOp(op, expr => { 24068 if (!(expr instanceof BinaryOperatorExpr) || expr.operator !== BinaryOperator.NullishCoalesce) { 24069 return expr; 24070 } 24071 const assignment = new AssignTemporaryExpr(expr.lhs.clone(), job.allocateXrefId()); 24072 const read = new ReadTemporaryExpr(assignment.xref); 24073 // TODO: When not in compatibility mode for TemplateDefinitionBuilder, we can just emit 24074 // `t != null` instead of including an undefined check as well. 24075 return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.And, new BinaryOperatorExpr(BinaryOperator.NotIdentical, assignment, NULL_EXPR), new BinaryOperatorExpr(BinaryOperator.NotIdentical, read, new LiteralExpr(undefined))), read.clone(), expr.rhs); 24076 }, VisitorContextFlag.None); 24077 } 24078 } 24079} 24080function kindTest(kind) { 24081 return op => op.kind === kind; 24082} 24083function kindWithInterpolationTest(kind, interpolation) { 24084 return op => { 24085 return op.kind === kind && interpolation === op.expression instanceof Interpolation; 24086 }; 24087} 24088function basicListenerKindTest(op) { 24089 return op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) || op.
24089kind === OpKind.TwoWayListener; 24090} 24091function nonInterpolationPropertyKindTest(op) { 24092 return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) && !(op.expression instanceof Interpolation); 24093} 24094/** 24095 * Defines the groups based on `OpKind` that ops will be divided into, for the various create 24096 * op kinds. Ops will be collected into groups, then optionally transformed, before recombining 24097 * the groups in the order defined here. 24098 */ 24099const CREATE_ORDERING = [{ 24100 test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener 24101}, { 24102 test: basicListenerKindTest 24103}]; 24104/** 24105 * Defines the groups based on `OpKind` that ops will be divided into, for the various update 24106 * op kinds. 24107 */ 24108const UPDATE_ORDERING = [{ 24109 test: kindTest(OpKind.StyleMap), 24110 transform: keepLast 24111}, { 24112 test: kindTest(OpKind.ClassMap), 24113 transform: keepLast 24114}, { 24115 test: kindTest(OpKind.StyleProp) 24116}, { 24117 test: kindTest(OpKind.ClassProp) 24118}, { 24119 test: kindWithInterpolationTest(OpKind.Attribute, true) 24120}, { 24121 test: kindWithInterpolationTest(OpKind.Property, true) 24122}, { 24123 test: nonInterpolationPropertyKindTest 24124}, { 24125 test: kindWithInterpolationTest(OpKind.Attribute, false) 24126}]; 24127/** 24128 * Host bindings have their own update ordering. 24129 */ 24130const UPDATE_HOST_ORDERING = [{ 24131 test: kindWithInterpolationTest(OpKind.HostProperty, true) 24132}, { 24133 test: kindWithInterpolationTest(OpKind.HostProperty, false) 24134}, { 24135 test: kindTest(OpKind.Attribute) 24136}, { 24137 test: kindTest(OpKind.StyleMap), 24138 transform: keepLast 24139}, { 24140 test: kindTest(OpKind.ClassMap), 24141 transform: keepLast 24142}, { 24143 test: kindTest(OpKind.StyleProp) 24144}, { 24145 test: kindTest(OpKind.ClassProp) 24146}]; 24147/** 24148 * The set of all op kinds we handle in the reordering phase. 24149 */ 24150const handledOpKinds = new Set([OpKind.Listener, OpKind.TwoWayListener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty, OpKind.HostProperty, OpKind.Attribute]); 24151/** 24152 * Many type of operations have ordering constraints that must be respected. For example, a 24153 * `ClassMap` instruction must be ordered after a `StyleMap` instruction, in order to have 24154 * predictable semantics that match TemplateDefinitionBuilder and don't break applications. 24155 */ 24156function orderOps(job) { 24157 for (const unit of job.units) { 24158 // First, we pull out ops that need to be ordered. Then, when we encounter an op that shouldn't 24159 // be reordered, put the ones we've pulled so far back in the correct order. Finally, if we 24160 // still have ops pulled at the end, put them back in the correct order. 24161 // Create mode: 24162 orderWithin(unit.create, CREATE_ORDERING); 24163 // Update mode: 24164 const ordering = unit.job.kind === CompilationJobKind.Host ? UPDATE_HOST_ORDERING : UPDATE_ORDERING; 24165 orderWithin(unit.update, ordering); 24166 } 24167} 24168/** 24169 * Order all the ops within the specified group. 24170 */ 24171function orderWithin(opList, ordering) { 24172 let opsToOrder = []; 24173 // Only reorder ops that target the same xref; do not mix ops that target different xrefs. 24174 let firstTargetInGroup = null; 24175 for (const op of opList) { 24176 const currentTarget = hasDependsOnSlotContextTrait(op) ? op.target : null; 24177 if (!handledOpKinds.has(op.kind) || currentTarget !== firstTargetInGroup && firstTargetInGroup !== null && currentTarget !== null) { 24178 OpList.insertBefore(reorder(opsToOrder, ordering), op); 24179 opsToOrder = []; 24180 firstTargetInGroup = null; 24181 } 24182 if (handledOpKinds.has(op.kind)) { 24183 opsToOrder.push(op); 24184 OpList.remove(op); 24185 firstTargetInGroup = currentTarget ?? firstTargetInGroup; 24186 } 24187 } 24188 opList.push(reorder(opsToOrder, ordering)); 24189} 24190/** 24191 * Reorders the given list of ops according to the ordering defined by `ORDERING`. 24192 */ 24193function reorder(ops, ordering) { 24194 // Break the ops list into groups based on OpKind. 24195 const groups = Array.from(ordering, () => new Array()); 24196 for (const op of ops) { 24197 const groupIndex = ordering.findIndex(o => o.test(op)); 24198 groups[groupIndex].push(op); 24199 } 24200 // Reassemble the groups into a single list, in the correct order. 24201 return groups.flatMap((group, i) => { 24202 const transform = ordering[i].transform; 24203 return transform ? transform(group) : group; 24204 }); 24205} 24206/** 24207 * Keeps only the last op in a list of ops. 24208 */ 24209function keepLast(ops) { 24210 return ops.slice(ops.length - 1); 24211} 24212 24213/** 24214 * Parses extracted style and class attributes into separate ExtractedAttributeOps per style or 24215 * class property. 24216 */ 24217function parseExtractedStyles(job) { 24218 const elements = new Map(); 24219 for (const unit of job.units) { 24220 for (const op of unit.create) { 24221 if (isElementOrContainerOp(op)) { 24222 elements.set(op.xref, op); 24223 } 24224 } 24225 } 24226 for (const unit of job.units) { 24227 for (const op of unit.create) { 24228 if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute && isStringLiteral(op.expression)) { 24229 const target = elements.get(op.target); 24230 if (target !== undefined && target.kind === OpKind.Template && target.templateKind === TemplateKind.Structural) { 24231 // TemplateDefinitionBuilder will not apply class and style bindings to structural 24232 // directives; instead, it will leave them as attributes. 24233 // (It's not clear what that would mean, anyway -- classes and styles on a structural 24234 // element should probably be a parse error.) 24235 // TODO: We may be able to remove this once Template Pipeline is the default. 24236 continue; 24237 } 24238 if (op.name === 'style') { 24239 const parsedStyles = parse(op.expression.value); 24240 for (let i = 0; i < parsedStyles.length - 1; i += 2) { 24241 OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, null, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op); 24242 } 24243 OpList.remove(op); 24244 } else if (op.name === 'class') {
24245 const parsedClasses = op.expression.value.trim().split(/\s+/g); 24246 for (const parsedClass of parsedClasses) { 24247 OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, null, parsedClass, null, null, null, SecurityContext.NONE), op); 24248 } 24249 OpList.remove(op); 24250 } 24251 } 24252 } 24253 } 24254} 24255 24256/** 24257 * Attributes of `ng-content` named 'select' are specifically removed, because they control which 24258 * content matches as a property of the `projection`, and are not a plain attribute. 24259 */ 24260function removeContentSelectors(job) { 24261 for (const unit of job.units) { 24262 const elements = createOpXrefMap(unit); 24263 for (const op of unit.ops()) { 24264 switch (op.kind) { 24265 case OpKind.Binding: 24266 const target = lookupInXrefMap(elements, op.target); 24267 if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) { 24268 OpList.remove(op); 24269 } 24270 break; 24271 } 24272 } 24273 } 24274} 24275function isSelectAttribute(name) { 24276 return name.toLowerCase() === 'select'; 24277} 24278/** 24279 * Looks up an element in the given map by xref ID. 24280 */ 24281function lookupInXrefMap(map, xref) { 24282 const el = map.get(xref); 24283 if (el === undefined) { 24284 throw new Error('All attributes should have an slottable target.'); 24285 } 24286 return el; 24287} 24288 24289/** 24290 * This phase generates pipe creation instructions. We do this based on the pipe bindings found in 24291 * the update block, in the order we see them. 24292 * 24293 * When not in compatibility mode, we can simply group all these creation instructions together, to 24294 * maximize chaining opportunities. 24295 */ 24296function createPipes(job) { 24297 for (const unit of job.units) { 24298 processPipeBindingsInView(unit); 24299 } 24300} 24301function processPipeBindingsInView(unit) { 24302 for (const updateOp of unit.update) { 24303 visitExpressionsInOp(updateOp, (expr, flags) => { 24304 if (!isIrExpression(expr)) { 24305 return; 24306 } 24307 if (expr.kind !== ExpressionKind.PipeBinding) { 24308 return; 24309 } 24310 if (flags & VisitorContextFlag.InChildOperation) { 24311 throw new Error(`AssertionError: pipe bindings should not appear in child expressions`); 24312 } 24313 if (unit.job.compatibility) { 24314 // TODO: We can delete this cast and check once compatibility mode is removed. 24315 const slotHandle = updateOp.target; 24316 if (slotHandle == undefined) { 24317 throw new Error(`AssertionError: expected slot handle to be assigned for pipe creation`); 24318 } 24319 addPipeToCreationBlock(unit, updateOp.target, expr); 24320 } else { 24321 // When not in compatibility mode, we just add the pipe to the end of the create block. This 24322 // is not only simpler and faster, but allows more chaining opportunities for other 24323 // instructions. 24324 unit.create.push(createPipeOp(expr.target, expr.targetSlot, expr.name)); 24325 } 24326 }); 24327 } 24328} 24329function addPipeToCreationBlock(unit, afterTargetXref, binding) { 24330 // Find the appropriate point to insert the Pipe creation operation. 24331 // We're looking for `afterTargetXref` (and also want to insert after any other pipe operations 24332 // which might be beyond it). 24333 for (let op = unit.create.head.next; op.kind !== OpKind.ListEnd; op = op.next) { 24334 if (!hasConsumesSlotTrait(op)) { 24335 continue; 24336 } 24337 if (op.xref !== afterTargetXref) { 24338 continue; 24339 } 24340 // We've found a tentative insertion point; however, we also want to skip past any _other_ pipe 24341 // operations present. 24342 while (op.next.kind === OpKind.Pipe) { 24343 op = op.next; 24344 } 24345 const pipe = createPipeOp(binding.target, binding.targetSlot, binding.name); 24346 OpList.insertBefore(pipe, op.next); 24347 // This completes adding the pipe to the creation block. 24348 return; 24349 } 24350 // At this point, we've failed to add the pipe to the creation block. 24351 throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`); 24352} 24353 24354/** 24355 * Pipes that accept more than 4 arguments are variadic, and are handled with a different runtime 24356 * instruction. 24357 */ 24358function createVariadicPipes(job) { 24359 for (const unit of job.units) { 24360 for (const op of unit.update) { 24361 transformExpressionsInOp(op, expr => { 24362 if (!(expr instanceof PipeBindingExpr)) { 24363 return expr; 24364 } 24365 // Pipes are variadic if they have more than 4 arguments. 24366 if (expr.args.length <= 4) { 24367 return expr; 24368 } 24369 return new PipeBindingVariadicExpr(expr.target, expr.targetSlot, expr.name, literalArr(expr.args), expr.args.length); 24370 }, VisitorContextFlag.None); 24371 } 24372 } 24373} 24374 24375/** 24376 * Propagate i18n blocks down through child templates that act as placeholders in the root i18n 24377 * message. Specifically, perform an in-order traversal of all the views, and add i18nStart/i18nEnd 24378 * op pairs into descending views. Also, assign an increasing sub-template index to each
24379 * descending view. 24380 */ 24381function propagateI18nBlocks(job) { 24382 propagateI18nBlocksToTemplates(job.root, 0); 24383} 24384/** 24385 * Propagates i18n ops in the given view through to any child views recursively. 24386 */ 24387function propagateI18nBlocksToTemplates(unit, subTemplateIndex) { 24388 let i18nBlock = null; 24389 for (const op of unit.create) { 24390 switch (op.kind) { 24391 case OpKind.I18nStart: 24392 op.subTemplateIndex = subTemplateIndex === 0 ? null : subTemplateIndex; 24393 i18nBlock = op; 24394 break; 24395 case OpKind.I18nEnd: 24396 // When we exit a root-level i18n block, reset the sub-template index counter. 24397 if (i18nBlock.subTemplateIndex === null) { 24398 subTemplateIndex = 0; 24399 } 24400 i18nBlock = null; 24401 break; 24402 case OpKind.Template: 24403 subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex); 24404 break; 24405 case OpKind.RepeaterCreate: 24406 // Propagate i18n blocks to the @for template. 24407 const forView = unit.job.views.get(op.xref); 24408 subTemplateIndex = propagateI18nBlocksForView(forView, i18nBlock, op.i18nPlaceholder, subTemplateIndex); 24409 // Then if there's an @empty template, propagate the i18n blocks for it as well. 24410 if (op.emptyView !== null) { 24411 subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex); 24412 } 24413 break; 24414 } 24415 } 24416 return subTemplateIndex; 24417} 24418/** 24419 * Propagate i18n blocks for a view. 24420 */ 24421function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) { 24422 // We found an <ng-template> inside an i18n block; increment the sub-template counter and 24423 // wrap the template's view in a child i18n block. 24424 if (i18nPlaceholder !== undefined) { 24425 if (i18nBlock === null) { 24426 throw Error('Expected template with i18n placeholder to be in an i18n block.'); 24427 } 24428 subTemplateIndex++; 24429 wrapTemplateWithI18n(view, i18nBlock); 24430 } 24431 // Continue traversing inside the template's view. 24432 return propagateI18nBlocksToTemplates(view, subTemplateIndex); 24433} 24434/** 24435 * Wraps a template view with i18n start and end ops. 24436 */ 24437function wrapTemplateWithI18n(unit, parentI18n) { 24438 // Only add i18n ops if they have not already been propagated to this template. 24439 if (unit.create.head.next?.kind !== OpKind.I18nStart) { 24440 const id = unit.job.allocateXrefId(); 24441 OpList.insertAfter( 24442 // Nested ng-template i18n start/end ops should not recieve source spans. 24443 createI18nStartOp(id, parentI18n.message, parentI18n.root, null), unit.create.head); 24444 OpList.insertBefore(createI18nEndOp(id, null), unit.create.tail); 24445 } 24446} 24447function extractPureFunctions(job) { 24448 for (const view of job.units) { 24449 for (const op of view.ops()) { 24450 visitExpressionsInOp(op, expr => { 24451 if (!(expr instanceof PureFunctionExpr) || expr.body === null) { 24452 return; 24453 } 24454 const constantDef = new PureFunctionConstant(expr.args.length); 24455 expr.fn = job.pool.getSharedConstant(constantDef, expr.body); 24456 expr.body = null; 24457 }); 24458 } 24459 } 24460} 24461class PureFunctionConstant extends GenericKeyFn { 24462 constructor(numArgs) { 24463 super(); 24464 this.numArgs = numArgs; 24465 } 24466 keyOf(expr) { 24467 if (expr instanceof PureFunctionParameterExpr) { 24468 return `param(${expr.index})`; 24469 } else { 24470 return super.keyOf(expr); 24471 } 24472 } 24473 // TODO: Use the new pool method `getSharedFunctionReference` 24474 toSharedConstantDeclaration(declName, keyExpr) { 24475 const fnParams = []; 24476 for (let idx = 0; idx < this.numArgs; idx++) { 24477 fnParams.push(new FnParam('a' + idx)); 24478 } 24479 // We will never visit `ir.PureFunctionParameterExpr`s that don't belong to us, because this 24480 // transform runs inside another visitor which will visit nested pure functions before this one. 24481 const returnExpr = transformExpressionsInExpression(keyExpr, expr => { 24482 if (!(expr instanceof PureFunctionParameterExpr)) { 24483 return expr; 24484 } 24485 return variable('a' + expr.index); 24486 }, VisitorContextFlag.None); 24487 return new DeclareVarStmt(declName, new ArrowFunctionExpr(fnParams, returnExpr), undefined, StmtModifier.Final); 24488 } 24489} 24490function generatePureLiteralStructures(job) { 24491 for (const unit of job.units) { 24492 for (const op of unit.update) { 24493 transformExpressionsInOp(op, (expr, flags) => { 24494 if (flags & VisitorContextFlag.InChildOperation) { 24495 return expr; 24496 } 24497 if (expr instanceof LiteralArrayExpr) { 24498 return transformLiteralArray(expr); 24499 } else if (expr instanceof LiteralMapExpr) { 24500 return transformLiteralMap(expr); 24501 } 24502 return expr; 24503 }, VisitorContextFlag.None); 24504 } 24505 } 24506} 24507function transformLiteralArray(expr) { 24508 const derivedEntries = []; 24509 const nonConstantArgs = []; 24510 for (const entry of expr.entries) { 24511 if (entry.isConstant()) { 24512 derivedEntries.push(entry); 24513 } else { 24514 const idx = nonConstantArgs.length; 24515 nonConstantArgs.push(entry); 24516 derivedEntries.push(new PureFunctionParameterExpr(idx)); 24517 } 24518 } 24519 return new PureFunctionExpr(literalArr(derivedEntries), nonConstantArgs); 24520} 24521function transformLiteralMap(expr) { 24522 let derivedEntries = []; 24523 const nonConstantArgs = []; 24524 for (const entry of expr.entries) { 24525 if (entry.value.isConstant()) { 24526 derivedEntries.push(entry); 24527 } else { 24528 const idx = nonConstantArgs.length; 24529 nonConstantArgs.push(entry.value);
24530 derivedEntries.push(new LiteralMapEntry(entry.key, new PureFunctionParameterExpr(idx), entry.quoted)); 24531 } 24532 } 24533 return new PureFunctionExpr(literalMap(derivedEntries), nonConstantArgs); 24534} 24535 24536// This file contains helpers for generating calls to Ivy instructions. In particular, each 24537// instruction type is represented as a function, which may select a specific instruction variant 24538// depending on the exact arguments. 24539function element(slot, tag, constIndex, localRefIndex, sourceSpan) { 24540 return elementOrContainerBase(Identifiers.element, slot, tag, constIndex, localRefIndex, sourceSpan); 24541} 24542function elementStart(slot, tag, constIndex, localRefIndex, sourceSpan) { 24543 return elementOrContainerBase(Identifiers.elementStart, slot, tag, constIndex, localRefIndex, sourceSpan); 24544} 24545function elementOrContainerBase(instruction, slot, tag, constIndex, localRefIndex, sourceSpan) { 24546 const args = [literal(slot)]; 24547 if (tag !== null) { 24548 args.push(literal(tag)); 24549 } 24550 if (localRefIndex !== null) { 24551 args.push(literal(constIndex), 24552 // might be null, but that's okay. 24553 literal(localRefIndex)); 24554 } else if (constIndex !== null) { 24555 args.push(literal(constIndex)); 24556 } 24557 return call(instruction, args, sourceSpan); 24558} 24559function elementEnd(sourceSpan) { 24560 return call(Identifiers.elementEnd, [], sourceSpan); 24561} 24562function elementContainerStart(slot, constIndex, localRefIndex, sourceSpan) { 24563 return elementOrContainerBase(Identifiers.elementContainerStart, slot, /* tag */null, constIndex, localRefIndex, sourceSpan); 24564} 24565function elementContainer(slot, constIndex, localRefIndex, sourceSpan) { 24566 return elementOrContainerBase(Identifiers.elementContainer, slot, /* tag */null, constIndex, localRefIndex, sourceSpan); 24567} 24568function elementContainerEnd() { 24569 return call(Identifiers.elementContainerEnd, [], null); 24570} 24571function template(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) { 24572 const args = [literal(slot), templateFnRef, literal(decls), literal(vars), literal(tag), literal(constIndex)]; 24573 if (localRefs !== null) { 24574 args.push(literal(localRefs)); 24575 args.push(importExpr(Identifiers.templateRefExtractor)); 24576 } 24577 while (args[args.length - 1].isEquivalent(NULL_EXPR)) { 24578 args.pop(); 24579 } 24580 return call(Identifiers.templateCreate, args, sourceSpan); 24581} 24582function disableBindings() { 24583 return call(Identifiers.disableBindings, [], null); 24584} 24585function enableBindings() { 24586 return call(Identifiers.enableBindings, [], null); 24587} 24588function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) { 24589 const args = [literal(name), handlerFn]; 24590 if (eventTargetResolver !== null) { 24591 args.push(literal(false)); // `useCapture` flag, defaults to `false` 24592 args.push(importExpr(eventTargetResolver)); 24593 } 24594 return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan); 24595} 24596function twoWayBindingSet(target, value) { 24597 return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]); 24598} 24599function twoWayListener(name, handlerFn, sourceSpan) { 24600 return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan); 24601} 24602function pipe(slot, name) { 24603 return call(Identifiers.pipe, [literal(slot), literal(name)], null); 24604} 24605function namespaceHTML() { 24606 return call(Identifiers.namespaceHTML, [], null); 24607} 24608function namespaceSVG() { 24609 return call(Identifiers.namespaceSVG, [], null); 24610} 24611function namespaceMath() { 24612 return call(Identifiers.namespaceMathML, [], null); 24613} 24614function advance(delta, sourceSpan) { 24615 return call(Identifiers.advance, delta > 1 ? [literal(delta)] : [], sourceSpan); 24616} 24617function reference(slot) { 24618 return importExpr(Identifiers.reference).callFn([literal(slot)]); 24619} 24620function nextContext(steps) { 24621 return importExpr(Identifiers.nextContext).callFn(steps === 1 ? [] : [literal(steps)]); 24622} 24623function getCurrentView() { 24624 return importExpr(Identifiers.getCurrentView).callFn([]); 24625} 24626function restoreView(savedView) { 24627 return importExpr(Identifiers.restoreView).callFn([savedView]); 24628} 24629function resetView(returnValue) { 24630 return importExpr(Identifiers.resetView).callFn([returnValue]); 24631} 24632function text(slot, initialValue, sourceSpan) { 24633 const args = [literal(slot, null)]; 24634 if (initialValue !== '') { 24635 args.push(literal(initialValue)); 24636 } 24637 return call(Identifiers.text, args, sourceSpan); 24638} 24639function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan) { 24640 const args = [literal(selfSlot), literal(primarySlot), dependencyResolverFn ?? literal(null), literal(loadingSlot), literal(placeholderSlot), literal(errorSlot), loadingConfig ?? literal(null), placeholderConfig ?? literal(null), enableTimerScheduling ? importExpr(Identifiers.deferEnableTimerScheduling) : literal(null)]; 24641 let expr; 24642 while ((expr = args[args.length - 1]) !== null && expr instanceof LiteralExpr && expr.value === null) { 24643 args.pop(); 24644 } 24645 return call(Identifiers.defer, args, sourceSpan); 24646}
24647const deferTriggerToR3TriggerInstructionsMap = new Map([[DeferTriggerKind.Idle, [Identifiers.deferOnIdle, Identifiers.deferPrefetchOnIdle]], [DeferTriggerKind.Immediate, [Identifiers.deferOnImmediate, Identifiers.deferPrefetchOnImmediate]], [DeferTriggerKind.Timer, [Identifiers.deferOnTimer, Identifiers.deferPrefetchOnTimer]], [DeferTriggerKind.Hover, [Identifiers.deferOnHover, Identifiers.deferPrefetchOnHover]], [DeferTriggerKind.Interaction, [Identifiers.deferOnInteraction, Identifiers.deferPrefetchOnInteraction]], [DeferTriggerKind.Viewport, [Identifiers.deferOnViewport, Identifiers.deferPrefetchOnViewport]]]); 24648function deferOn(trigger, args, prefetch, sourceSpan) { 24649 const instructions = deferTriggerToR3TriggerInstructionsMap.get(trigger); 24650 if (instructions === undefined) { 24651 throw new Error(`Unable to determine instruction for trigger ${trigger}`); 24652 } 24653 const instructionToCall = prefetch ? instructions[1] : instructions[0]; 24654 return call(instructionToCall, args.map(a => literal(a)), sourceSpan); 24655} 24656function projectionDef(def) { 24657 return call(Identifiers.projectionDef, def ? [def] : [], null); 24658} 24659function projection(slot, projectionSlotIndex, attributes, sourceSpan) { 24660 const args = [literal(slot)]; 24661 if (projectionSlotIndex !== 0 || attributes !== null) { 24662 args.push(literal(projectionSlotIndex)); 24663 if (attributes !== null) { 24664 args.push(attributes); 24665 } 24666 } 24667 return call(Identifiers.projection, args, sourceSpan); 24668} 24669function i18nStart(slot, constIndex, subTemplateIndex, sourceSpan) { 24670 const args = [literal(slot), literal(constIndex)]; 24671 if (subTemplateIndex !== null) { 24672 args.push(literal(subTemplateIndex)); 24673 } 24674 return call(Identifiers.i18nStart, args, sourceSpan); 24675} 24676function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, emptyTag, emptyConstIndex, sourceSpan) { 24677 const args = [literal(slot), variable(viewFnName), literal(decls), literal(vars), literal(tag), literal(constIndex), trackByFn]; 24678 if (trackByUsesComponentInstance || emptyViewFnName !== null) { 24679 args.push(literal(trackByUsesComponentInstance)); 24680 if (emptyViewFnName !== null) { 24681 args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars)); 24682 if (emptyTag !== null || emptyConstIndex !== null) { 24683 args.push(literal(emptyTag)); 24684 } 24685 if (emptyConstIndex !== null) { 24686 args.push(literal(emptyConstIndex)); 24687 } 24688 } 24689 } 24690 return call(Identifiers.repeaterCreate, args, sourceSpan); 24691} 24692function repeater(collection, sourceSpan) { 24693 return call(Identifiers.repeater, [collection], sourceSpan); 24694} 24695function deferWhen(prefetch, expr, sourceSpan) { 24696 return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan); 24697} 24698function i18n(slot, constIndex, subTemplateIndex, sourceSpan) { 24699 const args = [literal(slot), literal(constIndex)]; 24700 if (subTemplateIndex) { 24701 args.push(literal(subTemplateIndex)); 24702 } 24703 return call(Identifiers.i18n, args, sourceSpan); 24704} 24705function i18nEnd(endSourceSpan) { 24706 return call(Identifiers.i18nEnd, [], endSourceSpan); 24707} 24708function i18nAttributes(slot, i18nAttributesConfig) { 24709 const args = [literal(slot), literal(i18nAttributesConfig)]; 24710 return call(Identifiers.i18nAttributes, args, null); 24711} 24712function property(name, expression, sanitizer, sourceSpan) { 24713 const args = [literal(name), expression]; 24714 if (sanitizer !== null) { 24715 args.push(sanitizer); 24716 } 24717 return call(Identifiers.property, args, sourceSpan); 24718} 24719function twoWayProperty(name, expression, sanitizer, sourceSpan) { 24720 const args = [literal(name), expression]; 24721 if (sanitizer !== null) { 24722 args.push(sanitizer); 24723 } 24724 return call(Identifiers.twoWayProperty, args, sourceSpan); 24725} 24726function attribute(name, expression, sanitizer, namespace) { 24727 const args = [literal(name), expression]; 24728 if (sanitizer !== null || namespace !== null) { 24729 args.push(sanitizer ?? literal(null)); 24730 } 24731 if (namespace !== null) { 24732 args.push(literal(namespace)); 24733 } 24734 return call(Identifiers.attribute, args, null); 24735} 24736function styleProp(name, expression, unit, sourceSpan) { 24737 const args = [literal(name), expression]; 24738 if (unit !== null) { 24739 args.push(literal(unit)); 24740 } 24741 return call(Identifiers.styleProp, args, sourceSpan); 24742} 24743function classProp(name, expression, sourceSpan) { 24744 return call(Identifiers.classProp, [literal(name), expression], sourceSpan); 24745} 24746function styleMap(expression, sourceSpan) { 24747 return call(Identifiers.styleMap, [expression], sourceSpan); 24748} 24749function classMap(expression, sourceSpan) { 24750 return call(Identifiers.classMap, [expression], sourceSpan); 24751} 24752const PIPE_BINDINGS = [Identifiers.pipeBind1, Identifiers.pipeBind2, Identifiers.pipeBind3, Identifiers.pipeBind4];
24753function pipeBind(slot, varOffset, args) { 24754 if (args.length < 1 || args.length > PIPE_BINDINGS.length) { 24755 throw new Error(`pipeBind() argument count out of bounds`); 24756 } 24757 const instruction = PIPE_BINDINGS[args.length - 1]; 24758 return importExpr(instruction).callFn([literal(slot), literal(varOffset), ...args]); 24759} 24760function pipeBindV(slot, varOffset, args) { 24761 return importExpr(Identifiers.pipeBindV).callFn([literal(slot), literal(varOffset), args]); 24762} 24763function textInterpolate(strings, expressions, sourceSpan) { 24764 if (strings.length < 1 || expressions.length !== strings.length - 1) { 24765 throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`); 24766 } 24767 const interpolationArgs = []; 24768 if (expressions.length === 1 && strings[0] === '' && strings[1] === '') { 24769 interpolationArgs.push(expressions[0]); 24770 } else { 24771 let idx; 24772 for (idx = 0; idx < expressions.length; idx++) { 24773 interpolationArgs.push(literal(strings[idx]), expressions[idx]); 24774 } 24775 // idx points at the last string. 24776 interpolationArgs.push(literal(strings[idx])); 24777 } 24778 return callVariadicInstruction(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan); 24779} 24780function i18nExp(expr, sourceSpan) { 24781 return call(Identifiers.i18nExp, [expr], sourceSpan); 24782} 24783function i18nApply(slot, sourceSpan) { 24784 return call(Identifiers.i18nApply, [literal(slot)], sourceSpan); 24785} 24786function propertyInterpolate(name, strings, expressions, sanitizer, sourceSpan) { 24787 const interpolationArgs = collateInterpolationArgs(strings, expressions); 24788 const extraArgs = []; 24789 if (sanitizer !== null) { 24790 extraArgs.push(sanitizer); 24791 } 24792 return callVariadicInstruction(PROPERTY_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs, extraArgs, sourceSpan); 24793} 24794function attributeInterpolate(name, strings, expressions, sanitizer, sourceSpan) { 24795 const interpolationArgs = collateInterpolationArgs(strings, expressions); 24796 const extraArgs = []; 24797 if (sanitizer !== null) { 24798 extraArgs.push(sanitizer); 24799 } 24800 return callVariadicInstruction(ATTRIBUTE_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs, extraArgs, sourceSpan); 24801} 24802function stylePropInterpolate(name, strings, expressions, unit, sourceSpan) { 24803 const interpolationArgs = collateInterpolationArgs(strings, expressions); 24804 const extraArgs = []; 24805 if (unit !== null) { 24806 extraArgs.push(literal(unit)); 24807 } 24808 return callVariadicInstruction(STYLE_PROP_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs, extraArgs, sourceSpan); 24809} 24810function styleMapInterpolate(strings, expressions, sourceSpan) { 24811 const interpolationArgs = collateInterpolationArgs(strings, expressions); 24812 return callVariadicInstruction(STYLE_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan); 24813} 24814function classMapInterpolate(strings, expressions, sourceSpan) { 24815 const interpolationArgs = collateInterpolationArgs(strings, expressions); 24816 return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan); 24817} 24818function hostProperty(name, expression, sanitizer, sourceSpan) { 24819 const args = [literal(name), expression]; 24820 if (sanitizer !== null) { 24821 args.push(sanitizer); 24822 } 24823 return call(Identifiers.hostProperty, args, sourceSpan); 24824} 24825function syntheticHostProperty(name, expression, sourceSpan) { 24826 return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan); 24827} 24828function pureFunction(varOffset, fn, args) { 24829 return callVariadicInstructionExpr(PURE_FUNCTION_CONFIG, [literal(varOffset), fn], args, [], null); 24830} 24831/** 24832 * Collates the string an expression arguments for an interpolation instruction. 24833 */ 24834function collateInterpolationArgs(strings, expressions) { 24835 if (strings.length < 1 || expressions.length !== strings.length - 1) { 24836 throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`); 24837 } 24838 const interpolationArgs = []; 24839 if (expressions.length === 1 && strings[0] === '' && strings[1] === '') { 24840 interpolationArgs.push(expressions[0]); 24841 } else { 24842 let idx; 24843 for (idx = 0; idx < expressions.length; idx++) { 24844 interpolationArgs.push(literal(strings[idx]), expressions[idx]); 24845 } 24846 // idx points at the last string. 24847 interpolationArgs.push(literal(strings[idx])); 24848 } 24849 return interpolationArgs; 24850} 24851function call(instruction, args, sourceSpan) { 24852 const expr = importExpr(instruction).callFn(args, sourceSpan);
24853 return createStatementOp(new ExpressionStatement(expr, sourceSpan)); 24854} 24855function conditional(slot, condition, contextValue, sourceSpan) { 24856 const args = [literal(slot), condition]; 24857 if (contextValue !== null) { 24858 args.push(contextValue); 24859 } 24860 return call(Identifiers.conditional, args, sourceSpan); 24861} 24862/** 24863 * `InterpolationConfig` for the `textInterpolate` instruction. 24864 */ 24865const TEXT_INTERPOLATE_CONFIG = { 24866 constant: [Identifiers.textInterpolate, Identifiers.textInterpolate1, Identifiers.textInterpolate2, Identifiers.textInterpolate3, Identifiers.textInterpolate4, Identifiers.textInterpolate5, Identifiers.textInterpolate6, Identifiers.textInterpolate7, Identifiers.textInterpolate8], 24867 variable: Identifiers.textInterpolateV, 24868 mapping: n => { 24869 if (n % 2 === 0) { 24870 throw new Error(`Expected odd number of arguments`); 24871 } 24872 return (n - 1) / 2; 24873 } 24874}; 24875/** 24876 * `InterpolationConfig` for the `propertyInterpolate` instruction. 24877 */ 24878const PROPERTY_INTERPOLATE_CONFIG = { 24879 constant: [Identifiers.propertyInterpolate, Identifiers.propertyInterpolate1, Identifiers.propertyInterpolate2, Identifiers.propertyInterpolate3, Identifiers.propertyInterpolate4, Identifiers.propertyInterpolate5, Identifiers.propertyInterpolate6, Identifiers.propertyInterpolate7, Identifiers.propertyInterpolate8], 24880 variable: Identifiers.propertyInterpolateV, 24881 mapping: n => { 24882 if (n % 2 === 0) { 24883 throw new Error(`Expected odd number of arguments`); 24884 } 24885 return (n - 1) / 2; 24886 } 24887}; 24888/** 24889 * `InterpolationConfig` for the `stylePropInterpolate` instruction. 24890 */ 24891const STYLE_PROP_INTERPOLATE_CONFIG = { 24892 constant: [Identifiers.styleProp, Identifiers.stylePropInterpolate1, Identifiers.stylePropInterpolate2, Identifiers.stylePropInterpolate3, Identifiers.stylePropInterpolate4, Identifiers.stylePropInterpolate5, Identifiers.stylePropInterpolate6, Identifiers.stylePropInterpolate7, Identifiers.stylePropInterpolate8], 24893 variable: Identifiers.stylePropInterpolateV, 24894 mapping: n => { 24895 if (n % 2 === 0) { 24896 throw new Error(`Expected odd number of arguments`); 24897 } 24898 return (n - 1) / 2; 24899 } 24900}; 24901/** 24902 * `InterpolationConfig` for the `attributeInterpolate` instruction. 24903 */ 24904const ATTRIBUTE_INTERPOLATE_CONFIG = { 24905 constant: [Identifiers.attribute, Identifiers.attributeInterpolate1, Identifiers.attributeInterpolate2, Identifiers.attributeInterpolate3, Identifiers.attributeInterpolate4, Identifiers.attributeInterpolate5, Identifiers.attributeInterpolate6, Identifiers.attributeInterpolate7, Identifiers.attributeInterpolate8], 24906 variable: Identifiers.attributeInterpolateV, 24907 mapping: n => { 24908 if (n % 2 === 0) { 24909 throw new Error(`Expected odd number of arguments`); 24910 } 24911 return (n - 1) / 2; 24912 } 24913}; 24914/** 24915 * `InterpolationConfig` for the `styleMapInterpolate` instruction. 24916 */ 24917const STYLE_MAP_INTERPOLATE_CONFIG = { 24918 constant: [Identifiers.styleMap, Identifiers.styleMapInterpolate1, Identifiers.styleMapInterpolate2, Identifiers.styleMapInterpolate3, Identifiers.styleMapInterpolate4, Identifiers.styleMapInterpolate5, Identifiers.styleMapInterpolate6, Identifiers.styleMapInterpolate7, Identifiers.styleMapInterpolate8], 24919 variable: Identifiers.styleMapInterpolateV, 24920 mapping: n => { 24921 if (n % 2 === 0) { 24922 throw new Error(`Expected odd number of arguments`); 24923 } 24924 return (n - 1) / 2; 24925 } 24926}; 24927/** 24928 * `InterpolationConfig` for the `classMapInterpolate` instruction. 24929 */ 24930const CLASS_MAP_INTERPOLATE_CONFIG = { 24931 constant: [Identifiers.classMap, Identifiers.classMapInterpolate1, Identifiers.classMapInterpolate2, Identifiers.classMapInterpolate3, Identifiers.classMapInterpolate4, Identifiers.classMapInterpolate5, Identifiers.classMapInterpolate6, Identifiers.classMapInterpolate7, Identifiers.classMapInterpolate8], 24932 variable: Identifiers.classMapInterpolateV, 24933 mapping: n => { 24934 if (n % 2 === 0) { 24935 throw new Error(`Expected odd number of arguments`); 24936 } 24937 return (n - 1) / 2; 24938 } 24939}; 24940const PURE_FUNCTION_CONFIG = { 24941 constant: [Identifiers.pureFunction0, Identifiers.pureFunction1, Identifiers.pureFunction2, Identifiers.pureFunction3, Identifiers.pureFunction4, Identifiers.pureFunction5, Identifiers.pureFunction6, Identifiers.pureFunction7, Identifiers.pureFunction8], 24942 variable: Identifiers.pureFunctionV, 24943 mapping: n => n 24944}; 24945function callVariadicInstructionExpr(config, baseArgs, interpolationArgs, extraArgs, sourceSpan) { 24946 const n = config.mapping(interpolationArgs.length); 24947 if (n < config.constant.length) { 24948 // Constant calling pattern. 24949 return importExpr(config.constant[n]).callFn([...baseArgs, ...interpolationArgs, ...extraArgs], sourceSpan); 24950 } else if (config.variable !== null) {
24951 // Variable calling pattern. 24952 return importExpr(config.variable).callFn([...baseArgs, literalArr(interpolationArgs), ...extraArgs], sourceSpan); 24953 } else { 24954 throw new Error(`AssertionError: unable to call variadic function`); 24955 } 24956} 24957function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs, sourceSpan) { 24958 return createStatementOp(callVariadicInstructionExpr(config, baseArgs, interpolationArgs, extraArgs, sourceSpan).toStmt()); 24959} 24960 24961/** 24962 * Map of target resolvers for event listeners. 24963 */ 24964const GLOBAL_TARGET_RESOLVERS$1 = new Map([['window', Identifiers.resolveWindow], ['document', Identifiers.resolveDocument], ['body', Identifiers.resolveBody]]); 24965/** 24966 * Compiles semantic operations across all views and generates output `o.Statement`s with actual 24967 * runtime calls in their place. 24968 * 24969 * Reification replaces semantic operations with selected Ivy instructions and other generated code 24970 * structures. After reification, the create/update operation lists of all views should only contain 24971 * `ir.StatementOp`s (which wrap generated `o.Statement`s). 24972 */ 24973function reify(job) { 24974 for (const unit of job.units) { 24975 reifyCreateOperations(unit, unit.create); 24976 reifyUpdateOperations(unit, unit.update); 24977 } 24978} 24979/** 24980 * This function can be used a sanity check -- it walks every expression in the const pool, and 24981 * every expression reachable from an op, and makes sure that there are no IR expressions 24982 * left. This is nice to use for debugging mysterious failures where an IR expression cannot be 24983 * output from the output AST code. 24984 */ 24985function ensureNoIrForDebug(job) { 24986 for (const stmt of job.pool.statements) { 24987 transformExpressionsInStatement(stmt, expr => { 24988 if (isIrExpression(expr)) { 24989 throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`); 24990 } 24991 return expr; 24992 }, VisitorContextFlag.None); 24993 } 24994 for (const unit of job.units) { 24995 for (const op of unit.ops()) { 24996 visitExpressionsInOp(op, expr => { 24997 if (isIrExpression(expr)) { 24998 throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`); 24999 } 25000 }); 25001 } 25002 } 25003} 25004function reifyCreateOperations(unit, ops) { 25005 for (const op of ops) { 25006 transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None); 25007 switch (op.kind) { 25008 case OpKind.Text: 25009 OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan)); 25010 break; 25011 case OpKind.ElementStart: 25012 OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan)); 25013 break; 25014 case OpKind.Element: 25015 OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan)); 25016 break; 25017 case OpKind.ElementEnd: 25018 OpList.replace(op, elementEnd(op.sourceSpan)); 25019 break; 25020 case OpKind.ContainerStart: 25021 OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan)); 25022 break; 25023 case OpKind.Container: 25024 OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan)); 25025 break; 25026 case OpKind.ContainerEnd: 25027 OpList.replace(op, elementContainerEnd()); 25028 break; 25029 case OpKind.I18nStart: 25030 OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan)); 25031 break; 25032 case OpKind.I18nEnd: 25033 OpList.replace(op, i18nEnd(op.sourceSpan)); 25034 break; 25035 case OpKind.I18n: 25036 OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan)); 25037 break; 25038 case OpKind.I18nAttributes: 25039 if (op.i18nAttributesConfig === null) { 25040 throw new Error(`AssertionError: i18nAttributesConfig was not set`); 25041 } 25042 OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig)); 25043 break; 25044 case OpKind.Template: 25045 if (!(unit instanceof ViewCompilationUnit)) { 25046 throw new Error(`AssertionError: must be compiling a component`); 25047 } 25048 if (Array.isArray(op.localRefs)) { 25049 throw new Error(`AssertionError: local refs array should have been extracted into a constant`); 25050 } 25051 const childView = unit.job.views.get(op.xref);
25052 OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan)); 25053 break; 25054 case OpKind.DisableBindings: 25055 OpList.replace(op, disableBindings()); 25056 break; 25057 case OpKind.EnableBindings: 25058 OpList.replace(op, enableBindings()); 25059 break; 25060 case OpKind.Pipe: 25061 OpList.replace(op, pipe(op.handle.slot, op.name)); 25062 break; 25063 case OpKind.Listener: 25064 const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent); 25065 const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null; 25066 if (eventTargetResolver === undefined) { 25067 throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`); 25068 } 25069 OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan)); 25070 break; 25071 case OpKind.TwoWayListener: 25072 OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan)); 25073 break; 25074 case OpKind.Variable: 25075 if (op.variable.name === null) { 25076 throw new Error(`AssertionError: unnamed variable ${op.xref}`); 25077 } 25078 OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final))); 25079 break; 25080 case OpKind.Namespace: 25081 switch (op.active) { 25082 case Namespace.HTML: 25083 OpList.replace(op, namespaceHTML()); 25084 break; 25085 case Namespace.SVG: 25086 OpList.replace(op, namespaceSVG()); 25087 break; 25088 case Namespace.Math: 25089 OpList.replace(op, namespaceMath()); 25090 break; 25091 } 25092 break; 25093 case OpKind.Defer: 25094 const timerScheduling = !!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime; 25095 OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, op.resolverFn, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan)); 25096 break; 25097 case OpKind.DeferOn: 25098 let args = []; 25099 switch (op.trigger.kind) { 25100 case DeferTriggerKind.Idle: 25101 case DeferTriggerKind.Immediate: 25102 break; 25103 case DeferTriggerKind.Timer: 25104 args = [op.trigger.delay]; 25105 break; 25106 case DeferTriggerKind.Interaction: 25107 case DeferTriggerKind.Hover: 25108 case DeferTriggerKind.Viewport: 25109 if (op.trigger.targetSlot?.slot == null || op.trigger.targetSlotViewSteps === null) { 25110 throw new Error(`Slot or view steps not set in trigger reification for trigger kind ${op.trigger.kind}`); 25111 } 25112 args = [op.trigger.targetSlot.slot]; 25113 if (op.trigger.targetSlotViewSteps !== 0) { 25114 args.push(op.trigger.targetSlotViewSteps); 25115 } 25116 break; 25117 default: 25118 throw new Error(`AssertionError: Unsupported reification of defer trigger kind ${op.trigger.kind}`); 25119 } 25120 OpList.replace(op, deferOn(op.trigger.kind, args, op.prefetch, op.sourceSpan)); 25121 break; 25122 case OpKind.ProjectionDef: 25123 OpList.replace(op, projectionDef(op.def)); 25124 break; 25125 case OpKind.Projection: 25126 if (op.handle.slot === null) { 25127 throw new Error('No slot was assigned for project instruction'); 25128 } 25129 OpList.replace(op, projection(op.handle.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan)); 25130 break; 25131 case OpKind.RepeaterCreate: 25132 if (op.handle.slot === null) { 25133 throw new Error('No slot was assigned for repeater instruction'); 25134 } 25135 if (!(unit instanceof ViewCompilationUnit)) { 25136 throw new Error(`AssertionError: must be compiling a component`); 25137 } 25138 const repeaterView = unit.job.views.get(op.xref); 25139 if (repeaterView.fnName === null) { 25140 throw new Error(`AssertionError: expected repeater primary view to have been named`); 25141 } 25142 let emptyViewFnName = null;
25143 let emptyDecls = null; 25144 let emptyVars = null; 25145 if (op.emptyView !== null) { 25146 const emptyView = unit.job.views.get(op.emptyView); 25147 if (emptyView === undefined) { 25148 throw new Error('AssertionError: repeater had empty view xref, but empty view was not found'); 25149 } 25150 if (emptyView.fnName === null || emptyView.decls === null || emptyView.vars === null) { 25151 throw new Error(`AssertionError: expected repeater empty view to have been named and counted`); 25152 } 25153 emptyViewFnName = emptyView.fnName; 25154 emptyDecls = emptyView.decls; 25155 emptyVars = emptyView.vars; 25156 } 25157 OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.emptyTag, op.emptyAttributes, op.wholeSourceSpan)); 25158 break; 25159 case OpKind.Statement: 25160 // Pass statement operations directly through. 25161 break; 25162 default: 25163 throw new Error(`AssertionError: Unsupported reification of create op ${OpKind[op.kind]}`); 25164 } 25165 } 25166} 25167function reifyUpdateOperations(_unit, ops) { 25168 for (const op of ops) { 25169 transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None); 25170 switch (op.kind) { 25171 case OpKind.Advance: 25172 OpList.replace(op, advance(op.delta, op.sourceSpan)); 25173 break; 25174 case OpKind.Property: 25175 if (op.expression instanceof Interpolation) { 25176 OpList.replace(op, propertyInterpolate(op.name, op.expression.strings, op.expression.expressions, op.sanitizer, op.sourceSpan)); 25177 } else { 25178 OpList.replace(op, property(op.name, op.expression, op.sanitizer, op.sourceSpan)); 25179 } 25180 break; 25181 case OpKind.TwoWayProperty: 25182 OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan)); 25183 break; 25184 case OpKind.StyleProp: 25185 if (op.expression instanceof Interpolation) { 25186 OpList.replace(op, stylePropInterpolate(op.name, op.expression.strings, op.expression.expressions, op.unit, op.sourceSpan)); 25187 } else { 25188 OpList.replace(op, styleProp(op.name, op.expression, op.unit, op.sourceSpan)); 25189 } 25190 break; 25191 case OpKind.ClassProp: 25192 OpList.replace(op, classProp(op.name, op.expression, op.sourceSpan)); 25193 break; 25194 case OpKind.StyleMap: 25195 if (op.expression instanceof Interpolation) { 25196 OpList.replace(op, styleMapInterpolate(op.expression.strings, op.expression.expressions, op.sourceSpan)); 25197 } else { 25198 OpList.replace(op, styleMap(op.expression, op.sourceSpan)); 25199 } 25200 break; 25201 case OpKind.ClassMap: 25202 if (op.expression instanceof Interpolation) { 25203 OpList.replace(op, classMapInterpolate(op.expression.strings, op.expression.expressions, op.sourceSpan)); 25204 } else { 25205 OpList.replace(op, classMap(op.expression, op.sourceSpan)); 25206 } 25207 break; 25208 case OpKind.I18nExpression: 25209 OpList.replace(op, i18nExp(op.expression, op.sourceSpan)); 25210 break; 25211 case OpKind.I18nApply: 25212 OpList.replace(op, i18nApply(op.handle.slot, op.sourceSpan)); 25213 break; 25214 case OpKind.InterpolateText: 25215 OpList.replace(op, textInterpolate(op.interpolation.strings, op.interpolation.expressions, op.sourceSpan)); 25216 break; 25217 case OpKind.Attribute: 25218 if (op.expression instanceof Interpolation) { 25219 OpList.replace(op, attributeInterpolate(op.name, op.expression.strings, op.expression.expressions, op.sanitizer, op.sourceSpan)); 25220 } else { 25221 OpList.replace(op, attribute(op.name, op.expression, op.sanitizer, op.namespace)); 25222 } 25223 break; 25224 case OpKind.HostProperty: 25225 if (op.expression instanceof Interpolation) { 25226 throw new Error('not yet handled'); 25227 } else { 25228 if (op.isAnimationTrigger) { 25229 OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan)); 25230 } else { 25231 OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan)); 25232 } 25233 } 25234 break; 25235 case OpKind.Variable: 25236 if (op.variable.name === null) { 25237 throw new Error(`AssertionError: unnamed variable ${op.xref}`); 25238 } 25239 OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final))); 25240 break; 25241 case OpKind.Conditional: 25242 if (op.processed === null) { 25243 throw new Error(`Conditional test was not set.`); 25244 } 25245 if (op.targetSlot.slot === null) { 25246 throw new Error(`Conditional slot was not set.`); 25247 } 25248 OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan)); 25249 break; 25250 case OpKind.Repeater: 25251 OpList.replace(op, repeater(op.collection, op.sourceSpan)); 25252 break; 25253 case OpKind.DeferWhen: 25254 OpList.replace(op, deferWhen(op.prefetch, op.expr, op.sourceSpan)); 25255 break; 25256 case OpKind.Statement: 25257 // Pass statement operations directly through. 25258 break; 25259 default: 25260 throw new Error(`AssertionError: Unsupported reification of update op ${OpKind[op.kind]}`); 25261 } 25262 } 25263} 25264function reifyIrExpression(expr) { 25265 if (!isIrExpression(expr)) { 25266 return expr; 25267 } 25268 switch (expr.kind) { 25269 case ExpressionKind.NextContext: 25270 return nextContext(expr.steps); 25271 case ExpressionKind.Reference: 25272 return reference(expr.targetSlot.slot + 1 + expr.offset); 25273 case ExpressionKind.LexicalRead: 25274 throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`); 25275 case ExpressionKind.TwoWayBindingSet: 25276 throw new Error(`AssertionError: unresolved TwoWayBindingSet`); 25277 case ExpressionKind.RestoreView: 25278 if (typeof expr.view === 'number') { 25279 throw new Error(`AssertionError: unresolved RestoreView`); 25280 } 25281 return restoreView(expr.view); 25282 case ExpressionKind.ResetView: 25283 return resetView(expr.expr); 25284 case ExpressionKind.GetCurrentView: 25285 return getCurrentView(); 25286 case ExpressionKind.ReadVariable: 25287 if (expr.name === null) { 25288 throw new Error(`Read of unnamed variable ${expr.xref}`); 25289 } 25290 return variable(expr.name); 25291 case ExpressionKind.ReadTemporaryExpr: 25292 if (expr.name === null) { 25293 throw new Error(`Read of unnamed temporary ${expr.xref}`); 25294 } 25295 return variable(expr.name); 25296 case ExpressionKind.AssignTemporaryExpr: 25297 if (expr.name === null) { 25298 throw new Error(`Assign of unnamed temporary ${expr.xref}`); 25299 } 25300 return variable(expr.name).set(expr.expr); 25301 case ExpressionKind.PureFunctionExpr: 25302 if (expr.fn === null) { 25303 throw new Error(`AssertionError: expected PureFunctions to have been extracted`); 25304 } 25305 return pureFunction(expr.varOffset, expr.fn, expr.args); 25306 case ExpressionKind.PureFunctionParameterExpr: 25307 throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`); 25308 case ExpressionKind.PipeBinding: 25309 return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args); 25310 case ExpressionKind.PipeBindingVariadic: 25311 return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args); 25312 case ExpressionKind.SlotLiteralExpr: 25313 return literal(expr.slot.slot); 25314 default: 25315 throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`); 25316 } 25317} 25318/** 25319 * Listeners get turned into a function expression, which may or may not have the `$event` 25320 * parameter defined. 25321 */ 25322function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) { 25323 // First, reify all instruction calls within `handlerOps`. 25324 reifyUpdateOperations(unit, handlerOps); 25325 // Next, extract all the `o.Statement`s from the reified operations. We can expect that at this 25326 // point, all operations have been converted to statements. 25327 const handlerStmts = []; 25328 for (const op of handlerOps) { 25329 if (op.kind !== OpKind.Statement) { 25330 throw new Error(`AssertionError: expected reified statements, but found op ${OpKind[op.kind]}`); 25331 } 25332 handlerStmts.push(op.statement); 25333 } 25334 // If `$event` is referenced, we need to generate it as a parameter. 25335 const params = []; 25336 if (consumesDollarEvent) { 25337 // We need the `$event` parameter. 25338 params.push(new FnParam('$event')); 25339 } 25340 return fn(params, handlerStmts, undefined, undefined, name); 25341} 25342 25343/** 25344 * Binding with no content can be safely deleted. 25345 */ 25346function removeEmptyBindings(job) { 25347 for (const unit of job.units) { 25348 for (const op of unit.update) { 25349 switch (op.kind) { 25350 case OpKind.Attribute: 25351 case OpKind.Binding: 25352 case OpKind.ClassProp: 25353 case OpKind.ClassMap: 25354 case OpKind.Property: 25355 case OpKind.StyleProp: 25356 case OpKind.StyleMap: 25357 if (op.expression instanceof EmptyExpr) { 25358 OpList.remove(op); 25359 } 25360 break; 25361 } 25362 } 25363 } 25364} 25365 25366/** 25367 * Remove the i18n context ops after they are no longer needed, and null out references to them to 25368 * be safe. 25369 */ 25370function removeI18nContexts(job) { 25371 for (const unit of job.units) { 25372 for (const op of unit.create) { 25373 switch (op.kind) { 25374 case OpKind.I18nContext: 25375 OpList.remove(op); 25376 break; 25377 case OpKind.I18nStart: 25378 op.context = null; 25379 break; 25380 } 25381 } 25382 } 25383} 25384 25385/** 25386 * i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues 25387 * will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary. 25388 */ 25389function removeUnusedI18nAttributesOps(job) { 25390 for (const unit of job.units) { 25391 const ownersWithI18nExpressions = new Set(); 25392 for (const op of unit.update) { 25393 switch (op.kind) { 25394 case OpKind.I18nExpression: 25395 ownersWithI18nExpressions.add(op.i18nOwner); 25396 } 25397 } 25398 for (const op of unit.create) { 25399 switch (op.kind) { 25400 case OpKind.I18nAttributes: 25401 if (ownersWithI18nExpressions.has(op.xref)) { 25402 continue; 25403 } 25404 OpList.remove(op); 25405 } 25406 } 25407 } 25408} 25409 25410/** 25411 * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to 25412 * either the `ctx` parameter to component functions (for the current view context) or to variables 25413 * that store those contexts (for contexts accessed via the `nextContext()` instruction). 25414 */
25415function resolveContexts(job) { 25416 for (const unit of job.units) { 25417 processLexicalScope$1(unit, unit.create); 25418 processLexicalScope$1(unit, unit.update); 25419 } 25420} 25421function processLexicalScope$1(view, ops) { 25422 // Track the expressions used to access all available contexts within the current view, by the 25423 // view `ir.XrefId`. 25424 const scope = new Map(); 25425 // The current view's context is accessible via the `ctx` parameter. 25426 scope.set(view.xref, variable('ctx')); 25427 for (const op of ops) { 25428 switch (op.kind) { 25429 case OpKind.Variable: 25430 switch (op.variable.kind) { 25431 case SemanticVariableKind.Context: 25432 scope.set(op.variable.view, new ReadVariableExpr(op.xref)); 25433 break; 25434 } 25435 break; 25436 case OpKind.Listener: 25437 case OpKind.TwoWayListener: 25438 processLexicalScope$1(view, op.handlerOps); 25439 break; 25440 } 25441 } 25442 if (view === view.job.root) { 25443 // Prefer `ctx` of the root view to any variables which happen to contain the root context. 25444 scope.set(view.xref, variable('ctx')); 25445 } 25446 for (const op of ops) { 25447 transformExpressionsInOp(op, expr => { 25448 if (expr instanceof ContextExpr) { 25449 if (!scope.has(expr.view)) { 25450 throw new Error(`No context found for reference to view ${expr.view} from view ${view.xref}`); 25451 } 25452 return scope.get(expr.view); 25453 } else { 25454 return expr; 25455 } 25456 }, VisitorContextFlag.None); 25457 } 25458} 25459 25460/** 25461 * Any variable inside a listener with the name `$event` will be transformed into a output lexical 25462 * read immediately, and does not participate in any of the normal logic for handling variables. 25463 */ 25464function resolveDollarEvent(job) { 25465 for (const unit of job.units) { 25466 transformDollarEvent(unit.create); 25467 transformDollarEvent(unit.update); 25468 } 25469} 25470function transformDollarEvent(ops) { 25471 for (const op of ops) { 25472 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 25473 transformExpressionsInOp(op, expr => { 25474 if (expr instanceof LexicalReadExpr && expr.name === '$event') { 25475 // Two-way listeners always consume `$event` so they omit this field. 25476 if (op.kind === OpKind.Listener) { 25477 op.consumesDollarEvent = true; 25478 } 25479 return new ReadVarExpr(expr.name); 25480 } 25481 return expr; 25482 }, VisitorContextFlag.InChildOperation); 25483 } 25484 } 25485} 25486 25487/** 25488 * Resolve the element placeholders in i18n messages. 25489 */ 25490function resolveI18nElementPlaceholders(job) { 25491 // Record all of the element and i18n context ops for use later. 25492 const i18nContexts = new Map(); 25493 const elements = new Map(); 25494 for (const unit of job.units) { 25495 for (const op of unit.create) { 25496 switch (op.kind) { 25497 case OpKind.I18nContext: 25498 i18nContexts.set(op.xref, op); 25499 break; 25500 case OpKind.ElementStart: 25501 elements.set(op.xref, op); 25502 break; 25503 } 25504 } 25505 } 25506 resolvePlaceholdersForView(job, job.root, i18nContexts, elements); 25507} 25508/** 25509 * Recursively resolves element and template tag placeholders in the given view. 25510 */ 25511function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) { 25512 // Track the current i18n op and corresponding i18n context op as we step through the creation 25513 // IR. 25514 let currentOps = null; 25515 let pendingStructuralDirectiveCloses = new Map(); 25516 for (const op of unit.create) { 25517 switch (op.kind) { 25518 case OpKind.I18nStart: 25519 if (!op.context) { 25520 throw Error('Could not find i18n context for i18n op'); 25521 } 25522 currentOps = { 25523 i18nBlock: op, 25524 i18nContext: i18nContexts.get(op.context) 25525 }; 25526 break; 25527 case OpKind.I18nEnd: 25528 currentOps = null; 25529 break; 25530 case OpKind.ElementStart: 25531 // For elements with i18n placeholders, record its slot value in the params map under the 25532 // corresponding tag start placeholder. 25533 if (op.i18nPlaceholder !== undefined) { 25534 if (currentOps === null) { 25535 throw Error('i18n tag placeholder should only occur inside an i18n block'); 25536 } 25537 recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pe
25537ndingStructuralDirective); 25538 // If there is a separate close tag placeholder for this element, save the pending 25539 // structural directive so we can pass it to the closing tag as well. 25540 if (pendingStructuralDirective && op.i18nPlaceholder.closeName) { 25541 pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective); 25542 } 25543 // Clear out the pending structural directive now that its been accounted for. 25544 pendingStructuralDirective = undefined; 25545 } 25546 break; 25547 case OpKind.ElementEnd: 25548 // For elements with i18n placeholders, record its slot value in the params map under the 25549 // corresponding tag close placeholder. 25550 const startOp = elements.get(op.xref); 25551 if (startOp && startOp.i18nPlaceholder !== undefined) { 25552 if (currentOps === null) { 25553 throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block'); 25554 } 25555 recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref)); 25556 // Clear out the pending structural directive close that was accounted for. 25557 pendingStructuralDirectiveCloses.delete(op.xref); 25558 } 25559 break; 25560 case OpKind.Projection: 25561 // For content projections with i18n placeholders, record its slot value in the params map 25562 // under the corresponding tag start and close placeholders. 25563 if (op.i18nPlaceholder !== undefined) { 25564 if (currentOps === null) { 25565 throw Error('i18n tag placeholder should only occur inside an i18n block'); 25566 } 25567 recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25568 recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25569 // Clear out the pending structural directive now that its been accounted for. 25570 pendingStructuralDirective = undefined; 25571 } 25572 break; 25573 case OpKind.Template: 25574 const view = job.views.get(op.xref); 25575 if (op.i18nPlaceholder === undefined) { 25576 // If there is no i18n placeholder, just recurse into the view in case it contains i18n 25577 // blocks. 25578 resolvePlaceholdersForView(job, view, i18nContexts, elements); 25579 } else { 25580 if (currentOps === null) { 25581 throw Error('i18n tag placeholder should only occur inside an i18n block'); 25582 } 25583 if (op.templateKind === TemplateKind.Structural) { 25584 // If this is a structural directive template, don't record anything yet. Instead pass 25585 // the current template as a pending structural directive to be recorded when we find 25586 // the element, content, or template it belongs to. This allows us to create combined 25587 // values that represent, e.g. the start of a template and element at the same time. 25588 resolvePlaceholdersForView(job, view, i18nContexts, elements, op); 25589 } else { 25590 // If this is some other kind of template, we can record its start, recurse into its 25591 // view, and then record its end. 25592 recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25593 resolvePlaceholdersForView(job, view, i18nContexts, elements); 25594 recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25595 pendingStructuralDirective = undefined; 25596 } 25597 } 25598 break; 25599 case OpKind.RepeaterCreate: 25600 if (pendingStructuralDirective !== undefined) { 25601 throw Error('AssertionError: Unexpected structural directive associated with @for block'); 25602 } 25603 // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for 25604 // template and the (optional) third is for the @empty template. 25605 const forSlot = op.handle.slot + 1; 25606 const forView = job.views.get(op.xref); 25607 // First record all of the placeholders for the @for template. 25608 if (op.i18nPlaceholder === undefined) { 25609 // If there is no i18n placeholder, just recurse into the view in case it contains i18n 25610 // blocks. 25611 resolvePlaceholdersForView(job, forView, i18nContexts, elements); 25612 } else { 25613 if (currentOps === null) { 25614 throw Error('i18n tag placeholder should only occur inside an i18n block'); 25615 } 25616 recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pe
25616ndingStructuralDirective); 25617 resolvePlaceholdersForView(job, forView, i18nContexts, elements); 25618 recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25619 pendingStructuralDirective = undefined; 25620 } 25621 // Then if there's an @empty template, add its placeholders as well. 25622 if (op.emptyView !== null) { 25623 // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for 25624 // template and the (optional) third is for the @empty template. 25625 const emptySlot = op.handle.slot + 2; 25626 const emptyView = job.views.get(op.emptyView); 25627 if (op.emptyI18nPlaceholder === undefined) { 25628 // If there is no i18n placeholder, just recurse into the view in case it contains i18n 25629 // blocks. 25630 resolvePlaceholdersForView(job, emptyView, i18nContexts, elements); 25631 } else { 25632 if (currentOps === null) { 25633 throw Error('i18n tag placeholder should only occur inside an i18n block'); 25634 } 25635 recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25636 resolvePlaceholdersForView(job, emptyView, i18nContexts, elements); 25637 recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective); 25638 pendingStructuralDirective = undefined; 25639 } 25640 } 25641 break; 25642 } 25643 } 25644} 25645/** 25646 * Records an i18n param value for the start of an element. 25647 */ 25648function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) { 25649 const { 25650 startName, 25651 closeName 25652 } = op.i18nPlaceholder;
25653 let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag; 25654 let value = op.handle.slot; 25655 // If the element is associated with a structural directive, start it as well. 25656 if (structuralDirective !== undefined) { 25657 flags |= I18nParamValueFlags.TemplateTag; 25658 value = { 25659 element: value, 25660 template: structuralDirective.handle.slot 25661 }; 25662 } 25663 // For self-closing tags, there is no close tag placeholder. Instead, the start tag 25664 // placeholder accounts for the start and close of the element. 25665 if (!closeName) { 25666 flags |= I18nParamValueFlags.CloseTag; 25667 } 25668 addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags); 25669} 25670/** 25671 * Records an i18n param value for the closing of an element. 25672 */ 25673function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) { 25674 const { 25675 closeName 25676 } = op.i18nPlaceholder; 25677 // Self-closing tags don't have a closing tag placeholder, instead the element closing is 25678 // recorded via an additional flag on the element start value. 25679 if (closeName) {
25680 let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag; 25681 let value = op.handle.slot; 25682 // If the element is associated with a structural directive, close it as well. 25683 if (structuralDirective !== undefined) { 25684 flags |= I18nParamValueFlags.TemplateTag; 25685 value = { 25686 element: value, 25687 template: structuralDirective.handle.slot 25688 }; 25689 } 25690 addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags); 25691 } 25692} 25693/** 25694 * Records an i18n param value for the start of a template. 25695 */ 25696function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) { 25697 let { 25698 startName, 25699 closeName 25700 } = i18nPlaceholder; 25701 let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag; 25702 // For self-closing tags, there is no close tag placeholder. Instead, the start tag 25703 // placeholder accounts for the start and close of the element. 25704 if (!closeName) { 25705 flags |= I18nParamValueFlags.CloseTag; 25706 } 25707 // If the template is associated with a structural directive, record the structural directive's 25708 // start first. Since this template must be in the structural directive's view, we can just 25709 // directly use the current i18n block's sub-template index. 25710 if (structuralDirective !== undefined) { 25711 addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags); 25712 } 25713 // Record the start of the template. For the sub-template index, pass the index for the template's 25714 // view, rather than the current i18n block's index. 25715 addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags); 25716} 25717/** 25718 * Records an i18n param value for the closing of a template. 25719 */ 25720function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) { 25721 const { 25722 closeName 25723 } = i18nPlaceholder; 25724 const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag; 25725 // Self-closing tags don't have a closing tag placeholder, instead the template's closing is 25726 // recorded via an additional flag on the template start value. 25727 if (closeName) { 25728 // Record the closing of the template. For the sub-template index, pass the index for the 25729 // template's view, rather than the current i18n block's index. 25730 addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags); 25731 // If the template is associated with a structural directive, record the structural directive's 25732 // closing after. Since this template must be in the structural directive's view, we can just 25733 // directly use the current i18n block's sub-template index. 25734 if (structuralDirective !== undefined) { 25735 addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags); 25736 } 25737 } 25738} 25739/** 25740 * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
25741 * the child i18n block inside the template. 25742 */ 25743function getSubTemplateIndexForTemplateTag(job, i18nOp, view) { 25744 for (const childOp of view.create) { 25745 if (childOp.kind === OpKind.I18nStart) { 25746 return childOp.subTemplateIndex; 25747 } 25748 } 25749 return i18nOp.subTemplateIndex; 25750} 25751/** 25752 * Add a param value to the given params map. 25753 */ 25754function addParam(params, placeholder, value, subTemplateIndex, flags) { 25755 const values = params.get(placeholder) ?? []; 25756 values.push({ 25757 value, 25758 subTemplateIndex, 25759 flags 25760 }); 25761 params.set(placeholder, values); 25762} 25763 25764/** 25765 * Resolve the i18n expression placeholders in i18n messages. 25766 */ 25767function resolveI18nExpressionPlaceholders(job) { 25768 // Record all of the i18n context ops, and the sub-template index for each i18n op. 25769 const subTemplateIndices = new Map(); 25770 const i18nContexts = new Map(); 25771 const icuPlaceholders = new Map(); 25772 for (const unit of job.units) { 25773 for (const op of unit.create) { 25774 switch (op.kind) { 25775 case OpKind.I18nStart: 25776 subTemplateIndices.set(op.xref, op.subTemplateIndex); 25777 break; 25778 case OpKind.I18nContext: 25779 i18nContexts.set(op.xref, op); 25780 break; 25781 case OpKind.IcuPlaceholder: 25782 icuPlaceholders.set(op.xref, op); 25783 break; 25784 } 25785 } 25786 } 25787 // Keep track of the next available expression index for each i18n message. 25788 const expressionIndices = new Map(); 25789 // Keep track of a reference index for each expression. 25790 // We use different references for normal i18n expressio and attribute i18n expressions. This is 25791 // because child i18n blocks in templates don't get their own context, since they're rolled into 25792 // the translated message of the parent, but they may target a different slot. 25793 const referenceIndex = op => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context; 25794 for (const unit of job.units) { 25795 for (const op of unit.update) { 25796 if (op.kind === OpKind.I18nExpression) { 25797 const index = expressionIndices.get(referenceIndex(op)) || 0; 25798 const subTemplateIndex = subTemplateIndices.get(op.i18nOwner) ?? null; 25799 const value = { 25800 value: index, 25801 subTemplateIndex: subTemplateIndex, 25802 flags: I18nParamValueFlags.ExpressionIndex 25803 }; 25804 updatePlaceholder(op, value, i18nContexts, icuPlaceholders); 25805 expressionIndices.set(referenceIndex(op), index + 1); 25806 } 25807 } 25808 } 25809} 25810function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) { 25811 if (op.i18nPlaceholder !== null) { 25812 const i18nContext = i18nContexts.get(op.context); 25813 const params = op.resolutionTime === I18nParamResolutionTime.Creation ? i18nContext.params : i18nContext.postprocessingParams; 25814 const values = params.get(op.i18nPlaceholder) || []; 25815 values.push(value); 25816 params.set(op.i18nPlaceholder, values); 25817 } 25818 if (op.icuPlaceholder !== null) { 25819 const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder); 25820 icuPlaceholderOp?.expressionPlaceholders.push(value); 25821 } 25822} 25823 25824/** 25825 * Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to 25826 * property reads on the top-level component context. 25827 * 25828 * Also matches `ir.RestoreViewExpr` expressions with the variables of their corresponding saved 25829 * views. 25830 */ 25831function resolveNames(job) { 25832 for (const unit of job.units) { 25833 processLexicalScope(unit, unit.create, null); 25834 processLexicalScope(unit, unit.update, null); 25835 } 25836} 25837function processLexicalScope(unit, ops, savedView) { 25838 // Maps names defined in the lexical scope of this template to the `ir.XrefId`s of the variable 25839 // declarations which represent those values. 25840 // 25841 // Since variables are generated in each view for the entire lexical scope (including any 25842 // identifiers from parent templates) only local variables need be considered here. 25843 const scope = new Map(); 25844 // First, step through the operations list and: 25845 // 1) build up the `scope` mapping 25846 // 2) recurse into any listener functions 25847 for (const op of ops) { 25848 switch (op.kind) { 25849 case OpKind.Variable: 25850 switch (op.variable.kind) { 25851 case SemanticVariableKind.Identifier: 25852 case SemanticVariableKind.Alias: 25853 // This variable represents some kind of identifier which can be used in the template.
25854 if (scope.has(op.variable.identifier)) { 25855 continue; 25856 } 25857 scope.set(op.variable.identifier, op.xref); 25858 break; 25859 case SemanticVariableKind.SavedView: 25860 // This variable represents a snapshot of the current view context, and can be used to 25861 // restore that context within listener functions. 25862 savedView = { 25863 view: op.variable.view, 25864 variable: op.xref 25865 }; 25866 break; 25867 } 25868 break; 25869 case OpKind.Listener: 25870 case OpKind.TwoWayListener: 25871 // Listener functions have separate variable declarations, so process them as a separate 25872 // lexical scope. 25873 processLexicalScope(unit, op.handlerOps, savedView); 25874 break; 25875 } 25876 } 25877 // Next, use the `scope` mapping to match `ir.LexicalReadExpr` with defined names in the lexical 25878 // scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context 25879 // variable. 25880 for (const op of ops) { 25881 if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener) { 25882 // Listeners were already processed above with their own scopes. 25883 continue; 25884 } 25885 transformExpressionsInOp(op, expr => { 25886 if (expr instanceof LexicalReadExpr) { 25887 // `expr` is a read of a name within the lexical scope of this view. 25888 // Either that name is defined within the current view, or it represents a property from the 25889 // main component context. 25890 if (scope.has(expr.name)) { 25891 // This was a defined variable in the current scope. 25892 return new ReadVariableExpr(scope.get(expr.name)); 25893 } else { 25894 // Reading from the component context. 25895 return new ReadPropExpr(new ContextExpr(unit.job.root.xref), expr.name); 25896 } 25897 } else if (expr instanceof RestoreViewExpr && typeof expr.view === 'number') { 25898 // `ir.RestoreViewExpr` happens in listener functions and restores a saved view from the 25899 // parent creation list. We expect to find that we captured the `savedView` previously, and 25900 // that it matches the expected view to be restored. 25901 if (savedView === null || savedView.view !== expr.view) { 25902 throw new Error(`AssertionError: no saved view ${expr.view} from view ${unit.xref}`); 25903 } 25904 expr.view = new ReadVariableExpr(savedView.variable); 25905 return expr; 25906 } else { 25907 return expr; 25908 } 25909 }, VisitorContextFlag.None); 25910 } 25911 for (const op of ops) { 25912 visitExpressionsInOp(op, expr => { 25913 if (expr instanceof LexicalReadExpr) { 25914 throw new Error(`AssertionError: no lexical reads should remain, but found read of ${expr.name}`); 25915 } 25916 }); 25917 } 25918} 25919 25920/** 25921 * Map of security contexts to their sanitizer function. 25922 */ 25923const sanitizerFns = new Map([[SecurityContext.HTML, Identifiers.sanitizeHtml], [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl], [SecurityContext.SCRIPT, Identifiers.sanitizeScript], [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]]); 25924/** 25925 * Map of security contexts to their trusted value function. 25926 */ 25927const trustedValueFns = new Map([[SecurityContext.HTML, Identifiers.trustConstantHtml], [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl]]); 25928/** 25929 * Resolves sanitization functions for ops that need them. 25930 */ 25931function resolveSanitizers(job) { 25932 for (const unit of job.units) { 25933 const elements = createOpXrefMap(unit); 25934 // For normal element bindings we create trusted values for security sensitive constant 25935 // attributes. However, for host bindings we skip this step (this matches what 25936 // TemplateDefinitionBuilder does). 25937 // TODO: Is the TDB behavior correct here? 25938 if (job.kind !== CompilationJobKind.Host) { 25939 for (const op of unit.create) { 25940 if (op.kind === OpKind.ExtractedAttribute) { 25941 const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null; 25942 op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null; 25943 } 25944 } 25945 } 25946 for (const op of unit.update) { 25947 switch (op.kind) { 25948 case OpKind.Property: 25949 case OpKind.Attribute: 25950 case OpKind.HostProperty: 25951 let sanitizerFn = null; 25952 if (Array.isArray(op.securityContext) && op.securityContext.length === 2 && op.securityContext.indexOf(SecurityContext.URL) > -1 && op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) { 25953 // When the host element isn't known, some URL attributes (such as "src" and "href") may 25954 // be part of multiple different security contexts. In this case we use special 25955 // sanitization function and select the actual sanitizer at runtime based on a tag name 25956 // that is provided while invoking sanitization function. 25957 sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl; 25958 } else { 25959 sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null; 25960 } 25961 op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null; 25962 // If there was no sanitization function found based on the security context of an 25963 // attribute/property, check whether this attribute/property is one of the 25964 // security-sensitive <iframe> attributes (and that the current element is actually an 25965 // <iframe>). 25966 if (op.sanitizer === null) { 25967 let isIframe = false;
25968 if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) { 25969 // Note: for host bindings defined on a directive, we do not try to find all 25970 // possible places where it can be matched, so we can not determine whether 25971 // the host element is an <iframe>. In this case, we just assume it is and append a 25972 // validation function, which is invoked at runtime and would have access to the 25973 // underlying DOM element to check if it's an <iframe> and if so - run extra checks. 25974 isIframe = true; 25975 } else { 25976 // For a normal binding we can just check if the element its on is an iframe. 25977 const ownerOp = elements.get(op.target); 25978 if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) { 25979 throw Error('Property should have an element-like owner'); 25980 } 25981 isIframe = isIframeElement$1(ownerOp); 25982 } 25983 if (isIframe && isIframeSecuritySensitiveAttr(op.name)) { 25984 op.sanitizer = importExpr(Identifiers.validateIframeAttribute); 25985 } 25986 } 25987 break; 25988 } 25989 } 25990 } 25991} 25992/** 25993 * Checks whether the given op represents an iframe element. 25994 */ 25995function isIframeElement$1(op) { 25996 return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe'; 25997} 25998/** 25999 * Asserts that there is only a single security context and returns it. 26000 */ 26001function getOnlySecurityContext(securityContext) { 26002 if (Array.isArray(securityContext)) { 26003 if (securityContext.length > 1) { 26004 // TODO: What should we do here? TDB just took the first one, but this feels like something we 26005 // would want to know about and create a special case for like we did for Url/ResourceUrl. My 26006 // guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there 26007 // do turn out to be other cases, throwing an error until we can address it feels safer. 26008 throw Error(`AssertionError: Ambiguous security context`); 26009 } 26010 return securityContext[0] || SecurityContext.NONE; 26011 } 26012 return securityContext; 26013} 26014 26015/** 26016 * Transforms a `TwoWayBindingSet` expression into an expression that either 26017 * sets a value through the `twoWayBindingSet` instruction or falls back to setting 26018 * the value directly. E.g. the expression `TwoWayBindingSet(target, value)` becomes: 26019 * `ng.twoWayBindingSet(target, value) || (target = value)`. 26020 */ 26021function transformTwoWayBindingSet(job) { 26022 for (const unit of job.units) { 26023 for (const op of unit.create) { 26024 if (op.kind === OpKind.TwoWayListener) { 26025 transformExpressionsInOp(op, expr => { 26026 if (expr instanceof TwoWayBindingSetExpr) { 26027 return wrapAction(expr.target, expr.value); 26028 } 26029 return expr; 26030 }, VisitorContextFlag.InChildOperation); 26031 } 26032 } 26033 } 26034} 26035function wrapSetOperation(target, value) { 26036 // ASSUMPTION: here we're assuming that `ReadVariableExpr` will be a reference 26037 // to a local template variable. This appears to be the case at the time of writing. 26038 // If the expression is targeting a variable read, we only emit the `twoWayBindingSet` since 26039 // the fallback would be attempting to write into a constant. Invalid usages will be flagged 26040 // during template type checking. 26041 if (target instanceof ReadVariableExpr) { 26042 return twoWayBindingSet(target, value); 26043 } 26044 return twoWayBindingSet(target, value).or(target.set(value)); 26045} 26046function isReadExpression(value) { 26047 return value instanceof ReadPropExpr || value instanceof ReadKeyExpr || value instanceof ReadVariableExpr; 26048} 26049function wrapAction(target, value) { 26050 // The only officially supported expressions inside of a two-way binding are read expressions. 26051 if (isReadExpression(target)) { 26052 return wrapSetOperation(target, value); 26053 } 26054 // However, historically the expression parser was handling two-way events by appending `=$event` 26055 // to the raw string before attempting to parse it. This has led to bugs over the years (see 26056 // #37809) and to unintentionally supporting unassignable events in the two-way binding. The 26057 // logic below aims to emulate the old behavior while still supporting the new output format 26058 // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense 26059 // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"` 26060 // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used 26061 // to generate before #54154. 26062 if (target instanceof BinaryOperatorExpr && isReadExpression(target.rhs)) { 26063 // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)` 26064 return new BinaryOperatorExpr(target.operator, target.lhs, wrapSetOperation(target.rhs, value)); 26065 } 26066 // Note: this also supports nullish coalescing expressions which 26067 // would've been downleveled to ternary expressions by this point. 26068 if (target instanceof ConditionalExpr && isReadExpression(target.falseCase)) { 26069 // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)` 26070 return new ConditionalExpr(target.condition, target.trueCase, wrapSetOperation(target.falseCase, value)); 26071 } 26072 // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)` 26073 // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping 26074 // node doesn't affect the result so we don't need to carry it over. 26075 if (target instanceof NotExpr) { 26076 let expr = target.condition; 26077 while (true) { 26078 if (expr instanceof NotExpr) { 26079 expr = expr.condition; 26080 } else { 26081 if (isReadExpression(expr)) { 26082 return wrapSetOperation(expr, value); 26083 } 26084 break; 26085 } 26086 } 26087 } 26088 throw new Error(`Unsupported expression in two-way action binding.`); 26089} 26090 26091/**
26092 * When inside of a listener, we may need access to one or more enclosing views. Therefore, each 26093 * view should save the current view, and each listener must have the ability to restore the 26094 * appropriate view. We eagerly generate all save view variables; they will be optimized away later. 26095 */ 26096function saveAndRestoreView(job) { 26097 for (const unit of job.units) { 26098 unit.create.prepend([createVariableOp(unit.job.allocateXrefId(), { 26099 kind: SemanticVariableKind.SavedView, 26100 name: null, 26101 view: unit.xref 26102 }, new GetCurrentViewExpr(), VariableFlags.None)]); 26103 for (const op of unit.create) { 26104 if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener) { 26105 continue; 26106 } 26107 // Embedded views always need the save/restore view operation. 26108 let needsRestoreView = unit !== job.root; 26109 if (!needsRestoreView) { 26110 for (const handlerOp of op.handlerOps) { 26111 visitExpressionsInOp(handlerOp, expr => { 26112 if (expr instanceof ReferenceExpr) { 26113 // Listeners that reference() a local ref need the save/restore view operation. 26114 needsRestoreView = true; 26115 } 26116 }); 26117 } 26118 } 26119 if (needsRestoreView) { 26120 addSaveRestoreViewOperationToListener(unit, op); 26121 } 26122 } 26123 } 26124} 26125function addSaveRestoreViewOperationToListener(unit, op) { 26126 op.handlerOps.prepend([createVariableOp(unit.job.allocateXrefId(), { 26127 kind: SemanticVariableKind.Context, 26128 name: null, 26129 view: unit.xref 26130 }, new RestoreViewExpr(unit.xref), VariableFlags.None)]); 26131 // The "restore view" operation in listeners requires a call to `resetView` to reset the 26132 // context prior to returning from the listener operation. Find any `return` statements in 26133 // the listener body and wrap them in a call to reset the view. 26134 for (const handlerOp of op.handlerOps) { 26135 if (handlerOp.kind === OpKind.Statement && handlerOp.statement instanceof ReturnStatement) { 26136 handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value); 26137 } 26138 } 26139} 26140 26141/** 26142 * Assign data slots for all operations which implement `ConsumesSlotOpTrait`, and propagate the 26143 * assigned data slots of those operations to any expressions which reference them via 26144 * `UsesSlotIndexTrait`. 26145 * 26146 * This phase is also responsible for counting the number of slots used for each view (its `decls`) 26147 * and propagating that number into the `Template` operations which declare embedded views. 26148 */ 26149function allocateSlots(job) { 26150 // Map of all declarations in all views within the component which require an assigned slot index. 26151 // This map needs to be global (across all views within the component) since it's possible to 26152 // reference a slot from one view from an expression within another (e.g. local references work 26153 // this way). 26154 const slotMap = new Map(); 26155 // Process all views in the component and assign slot indexes. 26156 for (const unit of job.units) { 26157 // Slot indices start at 0 for each view (and are not unique between views). 26158 let slotCount = 0; 26159 for (const op of unit.create) { 26160 // Only consider declarations which consume data slots. 26161 if (!hasConsumesSlotTrait(op)) { 26162 continue; 26163 } 26164 // Assign slots to this declaration starting at the current `slotCount`. 26165 op.handle.slot = slotCount; 26166 // And track its assigned slot in the `slotMap`. 26167 slotMap.set(op.xref, op.handle.slot); 26168 // Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number 26169 // of slots required. 26170 slotCount += op.numSlotsUsed; 26171 } 26172 // Record the total number of slots used on the view itself. This will later be propagated into 26173 // `ir.TemplateOp`s which declare those views (except for the root view). 26174 unit.decls = slotCount; 26175 } 26176 // After slot assignment, `slotMap` now contains slot assignments for every declaration in the 26177 // whole template, across all views. Next, look for expressions which implement 26178 // `UsesSlotIndexExprTrait` and propagate the assigned slot indexes into them. 26179 // Additionally, this second scan allows us to find `ir.TemplateOp`s which declare views and 26180 // propagate the number of slots used for each view into the operation which declares it. 26181 for (const unit of job.units) { 26182 for (const op of unit.ops()) { 26183 if (op.kind === OpKind.Template || op.kind === OpKind.RepeaterCreate) { 26184 // Record the number of slots used by the view this `ir.TemplateOp` declares in the 26185 // operation itself, so it can be emitted later. 26186 const childView = job.views.get(op.xref); 26187 op.decls = childView.decls; 26188 // TODO: currently we handle the decls for the RepeaterCreate empty template in the reify 26189 // phase. We should handle that here instead. 26190 } 26191 } 26192 } 26193} 26194 26195/** 26196 * Transforms special-case bindings with 'style' or 'class' in their names. Must run before the 26197 * main binding specialization pass. 26198 */ 26199function specializeStyleBindings(job) { 26200 for (const unit of job.units) { 26201 for (const op of unit.update) { 26202 if (op.kind !== OpKind.Binding) { 26203 continue; 26204 } 26205 switch (op.bindingKind) { 26206 case BindingKind.ClassName: 26207 if (op.expression instanceof Interpolation) { 26208 throw new Error(`Unexpected interpolation in ClassName binding`); 26209 } 26210 OpList.replace(op, createClassPropOp(op.target, op.name, op.expression, op.sourceSpan)); 26211 break; 26212 case BindingKind.StyleProperty: 26213 OpList.replace(op, createStylePropOp(op.target, op.name, op.expression, op.unit, op.sourceSpan)); 26214 break; 26215 case BindingKind.Property: 26216 case BindingKind.Template: 26217 if (op.name === 'style') { 26218 OpList.replace(op, createStyleMapOp(op.target, op.expression, op.sourceSpan)); 26219 } else if (op.name === 'class') { 26220 OpList.replace(op, createClassMapOp(op.target, op.expression, op.sourceSpan)); 26221 } 26222 break; 26223 } 26224 } 26225 } 26226} 26227 26228/** 26229 * Find all assignments and usages of temporary variables, which are linked to each other with cross 26230 * references. Generate names for each cross-reference, and add a `DeclareVarStmt` to initialize 26231 * them at the beginning of the update block. 26232 * 26233 * TODO: Sometimes, it will be possible to reuse names across different subexpressions. For example, 26234 * in the double keyed read `a?.[f()]?.[f()]`, the two function calls have non-overlapping scopes. 26235 * Implement an algorithm for reuse. 26236 */ 26237function generateTemporaryVariables(job) { 26238 for (const unit of job.units) { 26239 unit.create.prepend(generateTemporaries(unit.create)); 26240 unit.update.prepend(generateTemporaries(unit.update)); 26241 } 26242} 26243function generateTemporaries(ops) { 26244 let opCount = 0; 26245 let generatedStatements = []; 26246 // For each op, search for any variables that are assigned or read. For each variable, generate a 26247 // name and produce a `DeclareVarStmt` to the beginning of the block. 26248 for (const op of ops) { 26249 // Identify the final time each temp var is read. 26250 const finalReads = new Map(); 26251 visitExpressionsInOp(op, (expr, flag) => { 26252 if (flag & VisitorContextFlag.InChildOperation) { 26253 return; 26254 } 26255 if (expr instanceof ReadTemporaryExpr) { 26256 finalReads.set(expr.xref, expr); 26257 } 26258 }); 26259 // Name the temp vars, accounting for the fact that a name can be reused after it has been 26260 // read for the final time.
26261 let count = 0; 26262 const assigned = new Set(); 26263 const released = new Set(); 26264 const defs = new Map(); 26265 visitExpressionsInOp(op, (expr, flag) => { 26266 if (flag & VisitorContextFlag.InChildOperation) { 26267 return; 26268 } 26269 if (expr instanceof AssignTemporaryExpr) { 26270 if (!assigned.has(expr.xref)) { 26271 assigned.add(expr.xref); 26272 // TODO: Exactly replicate the naming scheme used by `TemplateDefinitionBuilder`. 26273 // It seems to rely on an expression index instead of an op index. 26274 defs.set(expr.xref, `tmp_${opCount}_${count++}`); 26275 } 26276 assignName(defs, expr); 26277 } else if (expr instanceof ReadTemporaryExpr) { 26278 if (finalReads.get(expr.xref) === expr) { 26279 released.add(expr.xref); 26280 count--; 26281 } 26282 assignName(defs, expr); 26283 } 26284 }); 26285 // Add declarations for the temp vars. 26286 generatedStatements.push(...Array.from(new Set(defs.values())).map(name => createStatementOp(new DeclareVarStmt(name)))); 26287 opCount++; 26288 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 26289 op.handlerOps.prepend(generateTemporaries(op.handlerOps)); 26290 } 26291 } 26292 return generatedStatements; 26293} 26294/** 26295 * Assigns a name to the temporary variable in the given temporary variable expression. 26296 */ 26297function assignName(names, expr) { 26298 const name = names.get(expr.xref); 26299 if (name === undefined) { 26300 throw new Error(`Found xref with unassigned name: ${expr.xref}`); 26301 } 26302 expr.name = name; 26303} 26304 26305/** 26306 * Generate track functions that need to be extracted to the constant pool. This entails wrapping 26307 * them in an arrow (or traditional) function, replacing context reads with `this.`, and storing 26308 * them in the constant pool. 26309 * 26310 * Note that, if a track function was previously optimized, it will not need to be extracted, and 26311 * this phase is a no-op. 26312 */ 26313function generateTrackFns(job) { 26314 for (const unit of job.units) { 26315 for (const op of unit.create) { 26316 if (op.kind !== OpKind.RepeaterCreate) { 26317 continue; 26318 } 26319 if (op.trackByFn !== null) { 26320 // The final track function was already set, probably because it was optimized. 26321 continue; 26322 } 26323 // Find all component context reads. 26324 let usesComponentContext = false; 26325 op.track = transformExpressionsInExpression(op.track, expr => { 26326 if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) { 26327 throw new Error(`Illegal State: Pipes are not allowed in this context`); 26328 } 26329 if (expr instanceof TrackContextExpr) { 26330 usesComponentContext = true; 26331 return variable('this'); 26332 } 26333 return expr; 26334 }, VisitorContextFlag.None); 26335 let fn; 26336 const fnParams = [new FnParam('$index'), new FnParam('$item')]; 26337 if (usesComponentContext) {
26338 fn = new FunctionExpr(fnParams, [new ReturnStatement(op.track)]); 26339 } else { 26340 fn = arrowFn(fnParams, op.track); 26341 } 26342 op.trackByFn = job.pool.getSharedFunctionReference(fn, '_forTrack'); 26343 } 26344 } 26345} 26346 26347/** 26348 * `track` functions in `for` repeaters can sometimes be "optimized," i.e. transformed into inline 26349 * expressions, in lieu of an external function call. For example, tracking by `$index` can be be 26350 * optimized into an inline `trackByIndex` reference. This phase checks track expressions for 26351 * optimizable cases. 26352 */ 26353function optimizeTrackFns(job) { 26354 for (const unit of job.units) { 26355 for (const op of unit.create) { 26356 if (op.kind !== OpKind.RepeaterCreate) { 26357 continue; 26358 } 26359 if (op.track instanceof ReadVarExpr && op.track.name === '$index') { 26360 // Top-level access of `$index` uses the built in `repeaterTrackByIndex`. 26361 op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex); 26362 } else if (op.track instanceof ReadVarExpr && op.track.name === '$item') { 26363 // Top-level access of the item uses the built in `repeaterTrackByIdentity`. 26364 op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity); 26365 } else if (isTrackByFunctionCall(job.root.xref, op.track)) { 26366 // Mark the function as using the component instance to play it safe 26367 // since the method might be using `this` internally (see #53628). 26368 op.usesComponentInstance = true; 26369 // Top-level method calls in the form of `fn($index, item)` can be passed in directly. 26370 if (op.track.receiver.receiver.view === unit.xref) { 26371 // TODO: this may be wrong 26372 op.trackByFn = op.track.receiver; 26373 } else { 26374 // This is a plain method call, but not in the component's root view. 26375 // We need to get the component instance, and then call the method on it. 26376 op.trackByFn = importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name); 26377 // Because the context is not avaiable (without a special function), we don't want to 26378 // try to resolve it later. Let's get rid of it by overwriting the original track 26379 // expression (which won't be used anyway). 26380 op.track = op.trackByFn; 26381 } 26382 } else { 26383 // The track function could not be optimized. 26384 // Replace context reads with a special IR expression, since context reads in a track 26385 // function are emitted specially. 26386 op.track = transformExpressionsInExpression(op.track, expr => { 26387 if (expr instanceof ContextExpr) { 26388 op.usesComponentInstance = true; 26389 return new TrackContextExpr(expr.view); 26390 } 26391 return expr; 26392 }, VisitorContextFlag.None); 26393 } 26394 } 26395 } 26396} 26397function isTrackByFunctionCall(rootView, expr) { 26398 if (!(expr instanceof InvokeFunctionExpr) || expr.args.length !== 2) { 26399 return false; 26400 } 26401 if (!(expr.receiver instanceof ReadPropExpr && expr.receiver.receiver instanceof ContextExpr) || expr.receiver.receiver.view !== rootView) { 26402 return false; 26403 } 26404 const [arg0, arg1] = expr.args; 26405 if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') { 26406 return false; 26407 } 26408 if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') { 26409 return false; 26410 } 26411 return true; 26412} 26413 26414/** 26415 * Inside the `track` expression on a `for` repeater, the `$index` and `$item` variables are 26416 * ambiently available. In this phase, we find those variable usages, and replace them with the 26417 * appropriate output read. 26418 */ 26419function generateTrackVariables(job) { 26420 for (const unit of job.units) { 26421 for (const op of unit.create) { 26422 if (op.kind !== OpKind.RepeaterCreate) { 26423 continue; 26424 } 26425 op.track = transformExpressionsInExpression(op.track, expr => { 26426 if (expr instanceof LexicalReadExpr) { 26427 if (expr.name === op.varNames.$index) { 26428 return variable('$index'); 26429 } else if (expr.name === op.varNames.$implicit) { 26430 return variable('$item'); 26431 } 26432 // TODO: handle prohibited context variables (emit as globals?) 26433 } 26434 return expr; 26435 }, VisitorContextFlag.None); 26436 } 26437 } 26438} 26439 26440/** 26441 * Counts the number of variable slots used within each view, and stores that on the view itself, as 26442 * well as propagates it to the `ir.TemplateOp` for embedded views. 26443 */ 26444function countVariables(job) { 26445 // First, count the vars used in each view, and update the view-level counter. 26446 for (const unit of job.units) { 26447 let varCount = 0; 26448 // Count variables on top-level ops first. Don't explore nested expressions just yet. 26449 for (const op of unit.ops()) { 26450 if (hasConsumesVarsTrait(op)) { 26451 varCount += varsUsedByOp(op); 26452 } 26453 } 26454 // Count variables on expressions inside ops. We do this later because some of these expressions 26455 // might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere 26456 // with indices for top-level binding slots (e.g. `property`). 26457 for (const op of unit.ops()) { 26458 visitExpressionsInOp(op, expr => { 26459 if (!isIrExpression(expr)) { 26460 return; 26461 } 26462 // TemplateDefinitionBuilder assigns variable offsets for everything but pure functions 26463 // first, and then assigns offsets to pure functions lazily. We emulate that behavior b
26463y 26464 // assigning offsets in two passes instead of one, only in compatibility mode. 26465 if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder && expr instanceof PureFunctionExpr) { 26466 return; 26467 } 26468 // Some expressions require knowledge of the number of variable slots consumed. 26469 if (hasUsesVarOffsetTrait(expr)) { 26470 expr.varOffset = varCount; 26471 } 26472 if (hasConsumesVarsTrait(expr)) { 26473 varCount += varsUsedByIrExpression(expr); 26474 } 26475 }); 26476 } 26477 // Compatibility mode pass for pure function offsets (as explained above). 26478 if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) { 26479 for (const op of unit.ops()) { 26480 visitExpressionsInOp(op, expr => { 26481 if (!isIrExpression(expr) || !(expr instanceof PureFunctionExpr)) { 26482 return; 26483 } 26484 // Some expressions require knowledge of the number of variable slots consumed. 26485 if (hasUsesVarOffsetTrait(expr)) { 26486 expr.varOffset = varCount; 26487 } 26488 if (hasConsumesVarsTrait(expr)) { 26489 varCount += varsUsedByIrExpression(expr); 26490 } 26491 }); 26492 } 26493 } 26494 unit.vars = varCount; 26495 } 26496 if (job instanceof ComponentCompilationJob) { 26497 // Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is 26498 // an embedded view). 26499 for (const unit of job.units) { 26500 for (const op of unit.create) { 26501 if (op.kind !== OpKind.Template && op.kind !== OpKind.RepeaterCreate) { 26502 continue; 26503 } 26504 const childView = job.views.get(op.xref); 26505 op.vars = childView.vars; 26506 // TODO: currently we handle the vars for the RepeaterCreate empty template in the reify 26507 // phase. We should handle that here instead. 26508 } 26509 } 26510 } 26511} 26512/** 26513 * Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so 26514 * count the variables used by any particular `op`. 26515 */ 26516function varsUsedByOp(op) { 26517 let slots; 26518 switch (op.kind) { 26519 case OpKind.Property: 26520 case OpKind.HostProperty: 26521 case OpKind.Attribute: 26522 // All of these bindings use 1 variable slot, plus 1 slot for every interpolated expression, 26523 // if any. 26524 slots = 1; 26525 if (op.expression instanceof Interpolation && !isSingletonInterpolation(op.expression)) { 26526 slots += op.expression.expressions.length; 26527 } 26528 return slots; 26529 case OpKind.TwoWayProperty: 26530 // Two-way properties can only have expressions so they only need one variable slot. 26531 return 1; 26532 case OpKind.StyleProp: 26533 case OpKind.ClassProp: 26534 case OpKind.StyleMap: 26535 case OpKind.ClassMap: 26536 // Style & class bindings use 2 variable slots, plus 1 slot for every interpolated expression, 26537 // if any. 26538 slots = 2; 26539 if (op.expression instanceof Interpolation) { 26540 slots += op.expression.expressions.length; 26541 } 26542 return slots; 26543 case OpKind.InterpolateText: 26544 // `ir.InterpolateTextOp`s use a variable slot for each dynamic expression. 26545 return op.interpolation.expressions.length; 26546 case OpKind.I18nExpression: 26547 case OpKind.Conditional: 26548 case OpKind.DeferWhen: 26549 return 1; 26550 case OpKind.RepeaterCreate: 26551 // Repeaters may require an extra variable binding slot, if they have an empty view, for the 26552 // empty block tracking. 26553 // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can 26554 // find a way to use the Repeater update op instead. 26555 return op.emptyView ? 1 : 0; 26556 default: 26557 throw new Error(`Unhandled op: ${OpKind[op.kind]}`); 26558 } 26559} 26560function varsUsedByIrExpression(expr) { 26561 switch (expr.kind) { 26562 case ExpressionKind.PureFunctionExpr: 26563 return 1 + expr.args.length; 26564 case ExpressionKind.PipeBinding: 26565 return 1 + expr.args.length; 26566 case ExpressionKind.PipeBindingVariadic: 26567 return 1 + expr.numArgs; 26568 default: 26569 throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`); 26570 } 26571} 26572function isSingletonInterpolation(expr) { 26573 if (expr.expressions.length !== 1 || expr.strings.length !== 2) { 26574 return false;
26575 } 26576 if (expr.strings[0] !== '' || expr.strings[1] !== '') { 26577 return false; 26578 } 26579 return true; 26580} 26581 26582/** 26583 * Optimize variables declared and used in the IR. 26584 * 26585 * Variables are eagerly generated by pipeline stages for all possible values that could be 26586 * referenced. This stage processes the list of declared variables and all variable usages, 26587 * and optimizes where possible. It performs 3 main optimizations: 26588 * 26589 * * It transforms variable declarations to side effectful expressions when the 26590 * variable is not used, but its initializer has global effects which other 26591 * operations rely upon. 26592 * * It removes variable declarations if those variables are not referenced and 26593 * either they do not have global effects, or nothing relies on them. 26594 * * It inlines variable declarations when those variables are only used once 26595 * and the inlining is semantically safe. 26596 * 26597 * To guarantee correctness, analysis of "fences" in the instruction lists is used to determine 26598 * which optimizations are safe to perform. 26599 */ 26600function optimizeVariables(job) { 26601 for (const unit of job.units) { 26602 inlineAlwaysInlineVariables(unit.create); 26603 inlineAlwaysInlineVariables(unit.update); 26604 for (const op of unit.create) { 26605 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 26606 inlineAlwaysInlineVariables(op.handlerOps); 26607 } 26608 } 26609 optimizeVariablesInOpList(unit.create, job.compatibility); 26610 optimizeVariablesInOpList(unit.update, job.compatibility); 26611 for (const op of unit.create) { 26612 if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) { 26613 optimizeVariablesInOpList(op.handlerOps, job.compatibility); 26614 } 26615 } 26616 } 26617} 26618/** 26619 * A [fence](https://en.wikipedia.org/wiki/Memory_barrier) flag for an expression which indicates 26620 * how that expression can be optimized in relation to other expressions or instructions. 26621 * 26622 * `Fence`s are a bitfield, so multiple flags may be set on a single expression. 26623 */ 26624var Fence; 26625(function (Fence) { 26626 /** 26627 * Empty flag (no fence exists). 26628 */ 26629 Fence[Fence["None"] = 0] = "None"; 26630 /** 26631 * A context read fence, meaning that the expression in question reads from the "current view" 26632 * context of the runtime. 26633 */ 26634 Fence[Fence["ViewContextRead"] = 1] = "ViewContextRead"; 26635 /** 26636 * A context write fence, meaning that the expression in question writes to the "current view" 26637 * context of the runtime. 26638 * 26639 * Note that all `ContextWrite` fences are implicitly `ContextRead` fences as operations which 26640 * change the view context do so based on the current one. 26641 */ 26642 Fence[Fence["ViewContextWrite"] = 2] = "ViewContextWrite"; 26643 /** 26644 * Indicates that a call is required for its side-effects, even if nothing reads its result. 26645 * 26646 * This is also true of `ViewContextWrite` operations **if** they are followed by a 26647 * `ViewContextRead`. 26648 */ 26649 Fence[Fence["SideEffectful"] = 4] = "SideEffectful"; 26650})(Fence || (Fence = {})); 26651function inlineAlwaysInlineVariables(ops) { 26652 const vars = new Map(); 26653 for (const op of ops) { 26654 if (op.kind === OpKind.Variable && op.flags & VariableFlags.AlwaysInline) { 26655 visitExpressionsInOp(op, expr => { 26656 if (isIrExpression(expr) && fencesForIrExpression(expr) !== Fence.None) { 26657 throw new Error(`AssertionError: A context-sensitive variable was marked AlwaysInline`); 26658 } 26659 }); 26660 vars.set(op.xref, op); 26661 } 26662 transformExpressionsInOp(op, expr => { 26663 if (expr instanceof ReadVariableExpr && vars.has(expr.xref)) { 26664 const varOp = vars.get(expr.xref); 26665 // Inline by cloning, because we might inline into multiple places. 26666 return varOp.initializer.clone(); 26667 } 26668 return expr; 26669 }, VisitorContextFlag.None); 26670 } 26671 for (const op of vars.values()) { 26672 OpList.remove(op); 26673 } 26674} 26675/** 26676 * Process a list of operations and optimize variables within that list. 26677 */ 26678function optimizeVariablesInOpList(ops, compatibility) { 26679 const varDecls = new Map(); 26680 const varUsages = new Map(); 26681 // Track variables that are used outside of the immediate operation list. For example, within 26682 // `ListenerOp` handler operations of listeners in the current operation list. 26683 const varRemoteUsages = new Set(); 26684 const opMap = new Map(); 26685 // First, extract information about variables declared or used within the whole list. 26686 for (const op of ops) { 26687 if (op.kind === OpKind.Variable) { 26688 if (varDecls.has(op.xref) || varUsages.has(op.xref)) { 26689 throw new Error(`Should not see two declarations of the same variable: ${op.xref}`); 26690 } 26691 varDecls.set(op.xref, op); 26692 varUsages.set(op.xref, 0); 26693 } 26694 opMap.set(op, collectOpInfo(op)); 26695 countVariableUsages(op, varUsages, varRemoteUsages); 26696 } 26697 // The next step is to remove any variable declarations for variables that aren't used. The 26698 // variable initializer expressions may be side-effectful, so they may need to be retained as 26699 // expression statements. 26700 // Track whether we've seen an operation which reads from the view context yet. This is used to 26701 // determine whether a write to the view context in a variable initializer can be observed. 26702 let contextIsUsed = false;
26703 // Note that iteration through the list happens in reverse, which guarantees that we'll process 26704 // all reads of a variable prior to processing its declaration. 26705 for (const op of ops.reversed()) { 26706 const opInfo = opMap.get(op); 26707 if (op.kind === OpKind.Variable && varUsages.get(op.xref) === 0) { 26708 // This variable is unused and can be removed. We might need to keep the initializer around, 26709 // though, if something depends on it running. 26710 if (contextIsUsed && opInfo.fences & Fence.ViewContextWrite || opInfo.fences & Fence.SideEffectful) { 26711 // This variable initializer has a side effect which must be retained. Either: 26712 // * it writes to the view context, and we know there is a future operation which depends 26713 // on that write, or 26714 // * it's an operation which is inherently side-effectful. 26715 // We can't remove the initializer, but we can remove the variable declaration itself and 26716 // replace it with a side-effectful statement. 26717 const stmtOp = createStatementOp(op.initializer.toStmt()); 26718 opMap.set(stmtOp, opInfo); 26719 OpList.replace(op, stmtOp); 26720 } else { 26721 // It's safe to delete this entire variable declaration as nothing depends on it, even 26722 // side-effectfully. Note that doing this might make other variables unused. Since we're 26723 // iterating in reverse order, we should always be processing usages before declarations 26724 // and therefore by the time we get to a declaration, all removable usages will have been 26725 // removed. 26726 uncountVariableUsages(op, varUsages); 26727 OpList.remove(op); 26728 } 26729 opMap.delete(op); 26730 varDecls.delete(op.xref); 26731 varUsages.delete(op.xref); 26732 continue; 26733 } 26734 // Does this operation depend on the view context? 26735 if (opInfo.fences & Fence.ViewContextRead) { 26736 contextIsUsed = true; 26737 } 26738 } 26739 // Next, inline any remaining variables with exactly one usage. 26740 const toInline = []; 26741 for (const [id, count] of varUsages) { 26742 const decl = varDecls.get(id); 26743 // We can inline variables that: 26744 // - are used exactly once, and 26745 // - are not used remotely 26746 // OR 26747 // - are marked for always inlining 26748 const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline); 26749 if (count !== 1 || isAlwaysInline) { 26750 // We can't inline this variable as it's used more than once. 26751 continue; 26752 } 26753 if (varRemoteUsages.has(id)) { 26754 // This variable is used once, but across an operation boundary, so it can't be inlined. 26755 continue; 26756 } 26757 toInline.push(id); 26758 } 26759 let candidate; 26760 while (candidate = toInline.pop()) { 26761 // We will attempt to inline this variable. If inlining fails (due to fences for example), 26762 // no future operation will make inlining legal. 26763 const decl = varDecls.get(candidate); 26764 const varInfo = opMap.get(decl); 26765 const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline); 26766 if (isAlwaysInline) { 26767 throw new Error(`AssertionError: Found an 'AlwaysInline' variable after the always inlining pass.`); 26768 } 26769 // Scan operations following the variable declaration and look for the point where that variable 26770 // is used. There should only be one usage given the precondition above. 26771 for (let targetOp = decl.next; targetOp.kind !== OpKind.ListEnd; targetOp = targetOp.next) { 26772 const opInfo = opMap.get(targetOp); 26773 // Is the variable used in this operation? 26774 if (opInfo.variablesUsed.has(candidate)) { 26775 if (compatibility === CompatibilityMode.TemplateDefinitionBuilder && !allowConservativeInlining(decl, targetOp)) { 26776 // We're in conservative mode, and this variable is not eligible for inlining into the 26777 // target operation in this mode. 26778 break; 26779 } 26780 // Yes, try to inline it. Inlining may not be successful if fences in this operation before 26781 // the variable's usage cannot be safely crossed. 26782 if (tryInlineVariableInitializer(candidate, decl.initializer, targetOp, varInfo.fences)) { 26783 // Inlining was successful! Update the tracking structures to reflect the inlined 26784 // variable. 26785 opInfo.variablesUsed.delete(candidate); 26786 // Add all variables used in the variable's initializer to its new usage site. 26787 for (const id of varInfo.variablesUsed) { 26788 opInfo.variablesUsed.add(id); 26789 } 26790 // Merge fences in the variable's initializer into its new usage site. 26791 opInfo.fences |= varInfo.fences; 26792 // Delete tracking info related to the declaration. 26793 varDecls.delete(candidate); 26794 varUsages.delete(candidate);
26795 opMap.delete(decl); 26796 // And finally, delete the original declaration from the operation list. 26797 OpList.remove(decl); 26798 } 26799 // Whether inlining succeeded or failed, we're done processing this variable. 26800 break; 26801 } 26802 // If the variable is not used in this operation, then we'd need to inline across it. Check if 26803 // that's safe to do. 26804 if (!safeToInlinePastFences(opInfo.fences, varInfo.fences)) { 26805 // We can't safely inline this variable beyond this operation, so don't proceed with 26806 // inlining this variable. 26807 break; 26808 } 26809 } 26810 } 26811} 26812/** 26813 * Given an `ir.Expression`, returns the `Fence` flags for that expression type. 26814 */ 26815function fencesForIrExpression(expr) { 26816 switch (expr.kind) { 26817 case ExpressionKind.NextContext: 26818 return Fence.ViewContextRead | Fence.ViewContextWrite; 26819 case ExpressionKind.RestoreView: 26820 return Fence.ViewContextRead | Fence.ViewContextWrite | Fence.SideEffectful; 26821 case ExpressionKind.Reference: 26822 return Fence.ViewContextRead; 26823 default: 26824 return Fence.None; 26825 } 26826} 26827/** 26828 * Build the `OpInfo` structure for the given `op`. This performs two operations: 26829 * 26830 * * It tracks which variables are used in the operation's expressions. 26831 * * It rolls up fence flags for expressions within the operation. 26832 */ 26833function collectOpInfo(op) { 26834 let fences = Fence.None; 26835 const variablesUsed = new Set(); 26836 visitExpressionsInOp(op, expr => { 26837 if (!isIrExpression(expr)) { 26838 return; 26839 } 26840 switch (expr.kind) { 26841 case ExpressionKind.ReadVariable: 26842 variablesUsed.add(expr.xref); 26843 break; 26844 default: 26845 fences |= fencesForIrExpression(expr); 26846 } 26847 }); 26848 return { 26849 fences, 26850 variablesUsed 26851 }; 26852} 26853/** 26854 * Count the number of usages of each variable, being careful to track whether those usages are 26855 * local or remote. 26856 */ 26857function countVariableUsages(op, varUsages, varRemoteUsage) { 26858 visitExpressionsInOp(op, (expr, flags) => { 26859 if (!isIrExpression(expr)) { 26860 return; 26861 } 26862 if (expr.kind !== ExpressionKind.ReadVariable) { 26863 return; 26864 } 26865 const count = varUsages.get(expr.xref); 26866 if (count === undefined) { 26867 // This variable is declared outside the current scope of optimization. 26868 return; 26869 } 26870 varUsages.set(expr.xref, count + 1); 26871 if (flags & VisitorContextFlag.InChildOperation) { 26872 varRemoteUsage.add(expr.xref); 26873 } 26874 }); 26875} 26876/** 26877 * Remove usages of a variable in `op` from the `varUsages` tracking. 26878 */ 26879function uncountVariableUsages(op, varUsages) { 26880 visitExpressionsInOp(op, expr => { 26881 if (!isIrExpression(expr)) { 26882 return; 26883 } 26884 if (expr.kind !== ExpressionKind.ReadVariable) { 26885 return; 26886 } 26887 const count = varUsages.get(expr.xref); 26888 if (count === undefined) { 26889 // This variable is declared outside the current scope of optimization. 26890 return; 26891 } else if (count === 0) { 26892 throw new Error(`Inaccurate variable count: ${expr.xref} - found another read but count is already 0`); 26893 } 26894 varUsages.set(expr.xref, count - 1); 26895 }); 26896} 26897/** 26898 * Checks whether it's safe to inline a variable across a particular operation. 26899 * 26900 * @param fences the fences of the operation which the inlining will cross 26901 * @param declFences the fences of the variable being inlined. 26902 */ 26903function safeToInlinePastFences(fences, declFences) { 26904 if (fences & Fence.ViewContextWrite) { 26905 // It's not safe to inline context reads across context writes. 26906 if (declFences & Fence.ViewContextRead) { 26907 return false; 26908 } 26909 } else if (fences & Fence.ViewContextRead) { 26910 // It's not safe to inline context writes across context reads. 26911 if (declFences & Fence.ViewContextWrite) { 26912 return false; 26913 } 26914 } 26915 return true; 26916} 26917/** 26918 * Attempt to inline the initializer of a variable into a target operation's expressions. 26919 * 26920 * This may or may not be safe to do. For example, the variable could be read following the 26921 * execution of an expression with fences that don't permit the variable to be inlined across them. 26922 */ 26923function tryInlineVariableInitializer(id, initializer, target, declFences) { 26924 // We use `ir.transformExpressionsInOp` to walk the expressions and inline the variable if 26925 // possible. Since this operation is callback-based, once inlining succeeds or fails we can't 26926 // "stop" the expression processing, and have to keep track of whether inlining has succeeded or 26927 // is no longer allowed. 26928 let inlined = false;
26929 let inliningAllowed = true; 26930 transformExpressionsInOp(target, (expr, flags) => { 26931 if (!isIrExpression(expr)) { 26932 return expr; 26933 } 26934 if (inlined || !inliningAllowed) { 26935 // Either the inlining has already succeeded, or we've passed a fence that disallows inlining 26936 // at this point, so don't try. 26937 return expr; 26938 } else if (flags & VisitorContextFlag.InChildOperation && declFences & Fence.ViewContextRead) { 26939 // We cannot inline variables that are sensitive to the current context across operation 26940 // boundaries. 26941 return expr; 26942 } 26943 switch (expr.kind) { 26944 case ExpressionKind.ReadVariable: 26945 if (expr.xref === id) { 26946 // This is the usage site of the variable. Since nothing has disallowed inlining, it's 26947 // safe to inline the initializer here. 26948 inlined = true; 26949 return initializer; 26950 } 26951 break; 26952 default: 26953 // For other types of `ir.Expression`s, whether inlining is allowed depends on their fences. 26954 const exprFences = fencesForIrExpression(expr); 26955 inliningAllowed = inliningAllowed && safeToInlinePastFences(exprFences, declFences); 26956 break; 26957 } 26958 return expr; 26959 }, VisitorContextFlag.None); 26960 return inlined; 26961} 26962/** 26963 * Determines whether inlining of `decl` should be allowed in "conservative" mode. 26964 * 26965 * In conservative mode, inlining behavior is limited to those operations which the 26966 * `TemplateDefinitionBuilder` supported, with the goal of producing equivalent output. 26967 */ 26968function allowConservativeInlining(decl, target) { 26969 // TODO(alxhub): understand exactly how TemplateDefinitionBuilder approaches inlining, and record 26970 // that behavior here. 26971 switch (decl.variable.kind) { 26972 case SemanticVariableKind.Identifier: 26973 if (decl.initializer instanceof ReadVarExpr && decl.initializer.name === 'ctx') { 26974 // Although TemplateDefinitionBuilder is cautious about inlining, we still want to do so 26975 // when the variable is the context, to imitate its behavior with aliases in control flow 26976 // blocks. This quirky behavior will become dead code once compatibility mode is no longer 26977 // supported. 26978 return true; 26979 } 26980 return false; 26981 case SemanticVariableKind.Context: 26982 // Context can only be inlined into other variables. 26983 return target.kind === OpKind.Variable; 26984 default: 26985 return true; 26986 } 26987} 26988 26989/**
26990 * Wraps ICUs that do not already belong to an i18n block in a new i18n block. 26991 */ 26992function wrapI18nIcus(job) { 26993 for (const unit of job.units) { 26994 let currentI18nOp = null; 26995 let addedI18nId = null; 26996 for (const op of unit.create) { 26997 switch (op.kind) { 26998 case OpKind.I18nStart: 26999 currentI18nOp = op; 27000 break; 27001 case OpKind.I18nEnd: 27002 currentI18nOp = null; 27003 break; 27004 case OpKind.IcuStart: 27005 if (currentI18nOp === null) { 27006 addedI18nId = job.allocateXrefId(); 27007 // ICU i18n start/end ops should not recieve source spans. 27008 OpList.insertBefore(createI18nStartOp(addedI18nId, op.message, undefined, null), op); 27009 } 27010 break; 27011 case OpKind.IcuEnd: 27012 if (addedI18nId !== null) { 27013 OpList.insertAfter(createI18nEndOp(addedI18nId, null), op); 27014 addedI18nId = null; 27015 } 27016 break; 27017 } 27018 } 27019 } 27020} 27021 27022/** 27023 * 27024 * @license 27025 * Copyright Google LLC All Rights Reserved. 27026 * 27027 * Use of this source code is governed by an MIT-style license that can be 27028 * found in the LICENSE file at https://angular.io/license 27029 */ 27030const phases = [{ 27031 kind: CompilationJobKind.Tmpl, 27032 fn: removeContentSelectors 27033}, { 27034 kind: CompilationJobKind.Host, 27035 fn: parseHostStyleProperties 27036}, { 27037 kind: CompilationJobKind.Tmpl, 27038 fn: emitNamespaceChanges 27039}, { 27040 kind: CompilationJobKind.Tmpl, 27041 fn: propagateI18nBlocks 27042}, { 27043 kind: CompilationJobKind.Tmpl, 27044 fn: wrapI18nIcus 27045}, { 27046 kind: CompilationJobKind.Both, 27047 fn: deduplicateTextBindings 27048}, { 27049 kind: CompilationJobKind.Both, 27050 fn: specializeStyleBindings 27051}, { 27052 kind: CompilationJobKind.Both, 27053 fn: specializeBindings 27054}, { 27055 kind: CompilationJobKind.Both, 27056 fn: extractAttributes 27057}, { 27058 kind: CompilationJobKind.Tmpl, 27059 fn: createI18nContexts 27060}, { 27061 kind: CompilationJobKind.Both, 27062 fn: parseExtractedStyles 27063}, { 27064 kind: CompilationJobKind.Tmpl, 27065 fn: removeEmptyBindings 27066}, { 27067 kind: CompilationJobKind.Both, 27068 fn: collapseSingletonInterpolations 27069}, { 27070 kind: CompilationJobKind.Both, 27071 fn: orderOps 27072}, { 27073 kind: CompilationJobKind.Tmpl, 27074 fn: generateConditionalExpressions 27075}, { 27076 kind: CompilationJobKind.Tmpl, 27077 fn: createPipes 27078}, { 27079 kind: CompilationJobKind.Tmpl,
27080 fn: configureDeferInstructions 27081}, { 27082 kind: CompilationJobKind.Tmpl, 27083 fn: convertI18nText 27084}, { 27085 kind: CompilationJobKind.Tmpl, 27086 fn: convertI18nBindings 27087}, { 27088 kind: CompilationJobKind.Tmpl, 27089 fn: removeUnusedI18nAttributesOps 27090}, { 27091 kind: CompilationJobKind.Tmpl, 27092 fn: assignI18nSlotDependencies 27093}, { 27094 kind: CompilationJobKind.Tmpl, 27095 fn: applyI18nExpressions 27096}, { 27097 kind: CompilationJobKind.Tmpl, 27098 fn: createVariadicPipes 27099}, { 27100 kind: CompilationJobKind.Both, 27101 fn: generatePureLiteralStructures 27102}, { 27103 kind: CompilationJobKind.Tmpl, 27104 fn: generateProjectionDefs 27105}, { 27106 kind: CompilationJobKind.Tmpl, 27107 fn: generateVariables 27108}, { 27109 kind: CompilationJobKind.Tmpl, 27110 fn: saveAndRestoreView 27111}, { 27112 kind: CompilationJobKind.Both, 27113 fn: deleteAnyCasts 27114}, { 27115 kind: CompilationJobKind.Both, 27116 fn: resolveDollarEvent 27117}, { 27118 kind: CompilationJobKind.Tmpl, 27119 fn: generateTrackVariables 27120}, { 27121 kind: CompilationJobKind.Both, 27122 fn: resolveNames 27123}, { 27124 kind: CompilationJobKind.Tmpl, 27125 fn: resolveDeferTargetNames 27126}, { 27127 kind: CompilationJobKind.Tmpl, 27128 fn: transformTwoWayBindingSet 27129}, { 27130 kind: CompilationJobKind.Tmpl, 27131 fn: optimizeTrackFns 27132}, { 27133 kind: CompilationJobKind.Both, 27134 fn: resolveContexts 27135}, { 27136 kind: CompilationJobKind.Both, 27137 fn: resolveSanitizers 27138}, { 27139 kind: CompilationJobKind.Tmpl, 27140 fn: liftLocalRefs 27141}, { 27142 kind: CompilationJobKind.Both, 27143 fn: generateNullishCoalesceExpressions 27144}, { 27145 kind: CompilationJobKind.Both, 27146 fn: expandSafeReads 27147}, { 27148 kind: CompilationJobKind.Both, 27149 fn: generateTemporaryVariables 27150}, { 27151 kind: CompilationJobKind.Tmpl, 27152 fn: allocateSlots 27153}, { 27154 kind: CompilationJobKind.Tmpl, 27155 fn: resolveI18nElementPlaceholders 27156}, { 27157 kind: CompilationJobKind.Tmpl, 27158 fn: resolveI18nExpressionPlaceholders 27159}, { 27160 kind: CompilationJobKind.Tmpl, 27161 fn: extractI18nMessages 27162}, { 27163 kind: CompilationJobKind.Tmpl, 27164 fn: generateTrackFns 27165}, { 27166 kind: CompilationJobKind.Tmpl, 27167 fn: collectI18nConsts 27168}, { 27169 kind: CompilationJobKind.Tmpl, 27170 fn: collectConstExpressions 27171}, { 27172 kind: CompilationJobKind.Both, 27173 fn: collectElementConsts 27174}, { 27175 kind: CompilationJobKind.Tmpl, 27176 fn: removeI18nContexts 27177}, { 27178 kind: CompilationJobKind.Both, 27179 fn: countVariables 27180}, { 27181 kind: CompilationJobKind.Tmpl, 27182 fn: generateAdvance 27183}, { 27184 kind: CompilationJobKind.Both, 27185 fn: optimizeVariables 27186}, { 27187 kind: CompilationJobKind.Both, 27188 fn: nameFunctionsAndVariables 27189}, { 27190 kind: CompilationJobKind.Tmpl, 27191 fn: createDeferDepsFns 27192}, { 27193 kind: CompilationJobKind.Tmpl, 27194 fn: mergeNextContextExpressions 27195}, { 27196 kind: CompilationJobKind.Tmpl, 27197 fn: generateNgContainerOps 27198}, { 27199 kind: CompilationJobKind.Tmpl, 27200 fn: collapseEmptyInstructions 27201}, { 27202 kind: CompilationJobKind.Tmpl, 27203 fn: disableBindings$1 27204}, { 27205 kind: CompilationJobKind.Both, 27206 fn: extractPureFunctions 27207}, { 27208 kind: CompilationJobKind.Both, 27209 fn: reify 27210}, { 27211 kind: CompilationJobKind.Both, 27212 fn: chain 27213}]; 27214/** 27215 * Run all transformation phases in the correct order against a compilation job. After this 27216 * processing, the compilation should be in a state where it can be emitted. 27217 */ 27218function transform(job, kind) { 27219 for (const phase of phases) { 27220 if (phase.kind === kind || phase.kind === CompilationJobKind.Both) { 27221 // The type of `Phase` above ensures it is impossible to call a phase that doesn't support the 27222 // job kind. 27223 phase.fn(job); 27224 } 27225 } 27226} 27227/** 27228 * Compile all views in the given `ComponentCompilation` into the final template function, which may 27229 * reference constants defined in a `ConstantPool`. 27230 */ 27231function emitTemplateFn(tpl, pool) { 27232 const rootFn = emitView(tpl.root); 27233 emitChildViews(tpl.root, pool); 27234 return rootFn; 27235} 27236function emitChildViews(parent, pool) { 27237 for (const unit of parent.job.units) { 27238 if (unit.parent !== parent.xref) { 27239 continue; 27240 } 27241 // Child views are emitted depth-first. 27242 emitChildViews(unit, pool); 27243 const viewFn = emitView(unit); 27244 pool.statements.push(viewFn.toDeclStmt(viewFn.name)); 27245 } 27246} 27247/** 27248 * Emit a template function for an individual `ViewCompilation` (which may be either the root view 27249 * or an embedded view). 27250 */ 27251function emitView(view) { 27252 if (view.fnName === null) { 27253 throw new Error(`AssertionError: view ${view.xref} is unnamed`); 27254 } 27255 const createStatements = []; 27256 for (const op of view.create) { 27257 if (op.kind !== OpKind.Statement) { 27258 throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`); 27259 } 27260 createStatements.push(op.statement); 27261 } 27262 const updateStatements = []; 27263 for (const op of view.update) { 27264 if (op.kind !== OpKind.Statement) { 27265 throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`); 27266 } 27267 updateStatements.push(op.statement); 27268 } 27269 const createCond = maybeGenerateRfBlock(1, createStatements); 27270 const updateCond = maybeGenerateRfBlock(2, updateStatements); 27271 return fn([new FnParam('rf'), new FnParam('ctx')], [...createCond, ...updateCond], /* type */undefined, /* sourceSpan */undefined, view.fnName); 27272} 27273function maybeGenerateRfBlock(flag, statements) { 27274 if (statements.length === 0) { 27275 return []; 27276 } 27277 return [ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, variable('rf'), literal(flag)), statements)]; 27278} 27279function emitHostBindingFunction(job) { 27280 if (job.root.fnName === null) { 27281 throw new Error(`AssertionError: host binding function is unnamed`); 27282 } 27283 const createStatements = []; 27284 for (const op of job.root.create) { 27285 if (op.kind !== OpKind.Statement) { 27286 throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`); 27287 } 27288 createStatements.push(op.statement); 27289 } 27290 const updateStatements = []; 27291 for (const op of job.root.update) { 27292 if (op.kind !== OpKind.Statement) { 27293 throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`); 27294 } 27295 updateStatements.push(op.statement); 27296 } 27297 if (createStatements.length === 0 && updateStatements.length === 0) { 27298 return null; 27299 } 27300 const createCond = maybeGenerateRfBlock(1, createStatements); 27301 const updateCond = maybeGenerateRfBlock(2, updateStatements); 27302 return fn([new FnParam('rf'), new FnParam('ctx')], [...createCond, ...updateCond], /* type */undefined, /* sourceSpan */undefined, job.root.fnName); 27303} 27304const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder; 27305// Schema containing DOM elements and their properties. 27306const domSchema = new DomElementSchemaRegistry(); 27307// Tag name of the `ng-template` element. 27308const NG_TEMPLATE_TAG_NAME$1 = 'ng-template'; 27309/** 27310 * Process a template AST and convert it into a `ComponentCompilation` in the intermediate 27311 * representation.
27312 * TODO: Refactor more of the ingestion code into phases. 27313 */ 27314function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) { 27315 const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn); 27316 ingestNodes(job.root, template); 27317 return job; 27318} 27319/** 27320 * Process a host binding AST and convert it into a `HostBindingCompilationJob` in the intermediate 27321 * representation. 27322 */ 27323function ingestHostBinding(input, bindingParser, constantPool) { 27324 const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode); 27325 for (const property of input.properties ?? []) { 27326 let bindingKind = BindingKind.Property; 27327 // TODO: this should really be handled in the parser. 27328 if (property.name.startsWith('attr.')) { 27329 property.name = property.name.substring('attr.'.length); 27330 bindingKind = BindingKind.Attribute; 27331 } 27332 if (property.isAnimation) { 27333 bindingKind = BindingKind.Animation; 27334 } 27335 const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute).filter(context => context !== SecurityContext.NONE); 27336 ingestHostProperty(job, property, bindingKind, securityContexts); 27337 } 27338 for (const [name, expr] of Object.entries(input.attributes) ?? []) { 27339 const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true).filter(context => context !== SecurityContext.NONE); 27340 ingestHostAttribute(job, name, expr, securityContexts); 27341 } 27342 for (const event of input.events ?? []) { 27343 ingestHostEvent(job, event); 27344 } 27345 return job; 27346} 27347// TODO: We should refactor the parser to use the same types and structures for host bindings as 27348// with ordinary components. This would allow us to share a lot more ingestion code. 27349function ingestHostProperty(job, property, bindingKind, securityContexts) { 27350 let expression; 27351 const ast = property.expression.ast; 27352 if (ast instanceof Interpolation$1) { 27353 expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []); 27354 } else { 27355 expression = convertAst(ast, job, property.sourceSpan); 27356 } 27357 job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, null, /* TODO: How do Host bindings handle i18n attrs? */null, property.sourceSpan)); 27358} 27359function ingestHostAttribute(job, name, value, securityContexts) { 27360 const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, 27361 /* Host attributes should always be extracted to const hostAttrs, even if they are not 27362 *strictly* text literals */ 27363 true, false, null, /* TODO */null, /** TODO: May be null? */value.sourceSpan); 27364 job.root.update.push(attrBinding); 27365} 27366function ingestHostEvent(job, event) { 27367 const [phase, target] = event.type !== 1 /* e.ParsedEventType.Animation */ ? [null, event.targetOrPhase] : [event.targetOrPhase, null]; 27368 const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan); 27369 job.root.create.push(eventBinding); 27370} 27371/** 27372 * Ingest the nodes of a template AST into the given `ViewCompilation`. 27373 */ 27374function ingestNodes(unit, template) { 27375 for (const node of template) { 27376 if (node instanceof Element$1) { 27377 ingestElement(unit, node); 27378 } else if (node instanceof Template) { 27379 ingestTemplate(unit, node); 27380 } else if (node instanceof Content) { 27381 ingestContent(unit, node); 27382 } else if (node instanceof Text$3) { 27383 ingestText(unit, node, null); 27384 } else if (node instanceof BoundText) { 27385 ingestBoundText(unit, node, null); 27386 } else if (node instanceof IfBlock) { 27387 ingestIfBlock(unit, node); 27388 } else if (node instanceof SwitchBlock) { 27389 ingestSwitchBlock(unit, node); 27390 } else if (node instanceof DeferredBlock) { 27391 ingestDeferBlock(unit, node); 27392 } else if (node instanceof Icu$1) { 27393 ingestIcu(unit, node); 27394 } else if (node instanceof ForLoopBlock) { 27395 ingestForBlock(unit, node); 27396 } else { 27397 throw new Error(`Unsupported template node: ${node.constructor.name}`); 27398 } 27399 } 27400} 27401/** 27402 * Ingest an element AST from the template into the given `ViewCompilation`. 27403 */ 27404function ingestElement(unit, element) { 27405 if (element.i18n !== undefined && !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) { 27406 throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`); 27407 } 27408 const id = unit.job.allocateXrefId(); 27409 const [namespaceKey, elementName] = splitNsName(element.name); 27410 const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan); 27411 unit.create.push(startOp); 27412 ingestElementBindings(unit, startOp, element); 27413 ingestReferences(startOp, element); 27414 // Start i18n, if needed, goes after the element create and bindings, but before the nodes 27415 let i18nBlockId = null; 27416 if (element.i18n instanceof Message) { 27417 i18nBlockId = unit.job.allocateXrefId(); 27418 unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan)); 27419 } 27420 ingestNodes(unit, element.children); 27421 // The source span for the end op is typically the element closing tag. However, if no closing tag 27422 // exists, such as in `<input>`, we use the start source span instead. Usually the start and end 27423 // instructions will be collapsed into one `element` instruction, negating the purpose of this 27424 // fallback, but in cases when it is not collapsed (such as an input with a binding), we still 27425 // want to map the end instruction to the main element. 27426 const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan); 27427 unit.create.push(endOp); 27428 // If there is an i18n message associated with this element, insert i18n start and end ops. 27429 if (i18nBlockId !== null) { 27430 OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp); 27431 } 27432} 27433/** 27434 * Ingest an `ng-template` node from the AST into the given `ViewCompilation`. 27435 */ 27436function ingestTemplate(unit, tmpl) { 27437 if (tmpl.i18n !== undefined && !(tmpl.i18n instanceof Message || tmpl.i18n instanceof TagPlaceholder)) { 27438 throw Error(`Unhandled i18n metadata type for template: ${tmpl.i18n.constructor.name}`); 27439 } 27440 const childView = unit.job.allocateView(unit.xref); 27441 let tagNameWithoutNamespace = tmpl.tagName; 27442 let namespacePrefix = ''; 27443 if (tmpl.tagName) { 27444 [namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName); 27445 } 27446 const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined; 27447 const namespace = namespaceForKey(namespacePrefix); 27448 const functionNameSuffix = tagNameWithoutNamespace === null ? '' : prefixWithNamespace(tagNameWithoutNamespace, namespace); 27449 const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural; 27450 const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan); 27451 unit.create.push(templateOp); 27452 ingestTemplateBindings(unit, templateOp, tmpl, templateKind); 27453 ingestReferences(templateOp, tmpl); 27454 ingestNodes(childView, tmpl.children); 27455 for (const { 27456 name, 27457 value 27458 } of tmpl.variables) { 27459 childView.contextVariables.set(name, value !== '' ? value : '$implicit'); 27460 } 27461 // If this is a plain template and there is an i18n message associated with it, insert i18n start 27462 // and end ops. For structural directive templates, the i18n ops will be added when ingesting the 27463 // element/template the directive is placed on. 27464 if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) { 27465 const id = unit.job.allocateXrefId(); 27466 OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head); 27467 OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail); 27468 } 27469} 27470/** 27471 * Ingest a content node from the AST into the given `ViewCompilation`. 27472 */ 27473function ingestContent(unit, content) { 27474 if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) { 27475 throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`); 27476 } 27477 const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan); 27478 for (const attr of content.attributes) { 27479 const securityContext = domSchema.securityContext(content.name, attr.name, true); 27480 unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan)); 27481 } 27482 unit.create.push(op); 27483} 27484/** 27485 * Ingest a literal text node from the AST into the given `ViewCompilation`. 27486 */ 27487function ingestText(unit, text, icuPlaceholder) { 27488 unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan)); 27489} 27490/** 27491 * Ingest an interpolated text node from the AST into the given `ViewCompilation`. 27492 */ 27493function ingestBoundText(unit, text, icuPlaceholder) { 27494 let value = text.value; 27495 if (value instanceof ASTWithSource) { 27496 value = value.ast; 27497 } 27498 if (!(value instanceof Interpolation$1)) { 27499 throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`); 27500 } 27501 if (text.i18n !== undefined && !(text.i18n instanceof Container)) { 27502 throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`); 27503 } 27504 const i18nPlaceholders = text.i18n instanceof Container ? text.i18n.children.filter(node =>
27504 node instanceof Placeholder).map(placeholder => placeholder.name) : []; 27505 if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) { 27506 throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`); 27507 } 27508 const textXref = unit.job.allocateXrefId(); 27509 unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan)); 27510 // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an 27511 // interpolation. We copy that behavior in compatibility mode. 27512 // TODO: is it actually correct to generate these extra maps in modern mode? 27513 const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan; 27514 unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan)); 27515} 27516/** 27517 * Ingest an `@if` block into the given `ViewCompilation`. 27518 */ 27519function ingestIfBlock(unit, ifBlock) { 27520 let firstXref = null; 27521 let firstSlotHandle = null; 27522 let conditions = []; 27523 for (let i = 0; i < ifBlock.branches.length; i++) { 27524 const ifCase = ifBlock.branches[i]; 27525 const cView = unit.job.allocateView(unit.xref); 27526 let tagName = null; 27527 // Only the first branch can be used for projection, because the conditional 27528 // uses the container of the first branch as the insertion point for all branches. 27529 if (i === 0) { 27530 tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase); 27531 } 27532 if (ifCase.expressionAlias !== null) { 27533 cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF); 27534 } 27535 let ifCaseI18nMeta = undefined; 27536 if (ifCase.i18n !== undefined) { 27537 if (!(ifCase.i18n instanceof BlockPlaceholder)) { 27538 throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`); 27539 } 27540 ifCaseI18nMeta = ifCase.i18n; 27541 } 27542 const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan); 27543 unit.create.push(templateOp); 27544 if (firstXref === null) { 27545 firstXref = cView.xref; 27546 firstSlotHandle = templateOp.handle; 27547 } 27548 const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null; 27549 const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias); 27550 conditions.push(conditionalCaseExpr); 27551 ingestNodes(cView, ifCase.children); 27552 } 27553 const conditional = createConditionalOp(firstXref, firstSlotHandle, null, conditions, ifBlock.sourceSpan); 27554 unit.update.push(conditional); 27555} 27556/** 27557 * Ingest an `@switch` block into the given `ViewCompilation`. 27558 */ 27559function ingestSwitchBlock(unit, switchBlock) { 27560 // Don't ingest empty switches since they won't render anything. 27561 if (switchBlock.cases.length === 0) { 27562 return; 27563 } 27564 let firstXref = null; 27565 let firstSlotHandle = null; 27566 let conditions = []; 27567 for (const switchCase of switchBlock.cases) { 27568 const cView = unit.job.allocateView(unit.xref); 27569 let switchCaseI18nMeta = undefined; 27570 if (switchCase.i18n !== undefined) { 27571 if (!(switchCase.i18n instanceof BlockPlaceholder)) { 27572 throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`); 27573 } 27574 switchCaseI18nMeta = switchCase.i18n; 27575 } 27576 const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan); 27577 unit.create.push(templateOp); 27578 if (firstXref === null) { 27579 firstXref = cView.xref; 27580 firstSlotHandle = templateOp.handle; 27581 } 27582 const caseExpr = switchCase.expression ? convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) : null; 27583 const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle); 27584 conditions.push(conditionalCaseExpr); 27585 ingestNodes(cView, switchCase.children); 27586 } 27587 const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan); 27588 unit.update.push(conditional); 27589} 27590function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) { 27591 if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) { 27592 throw Error('Unhandled i18n metadata type for defer block'); 27593 } 27594 if (children === undefined) { 27595 return null; 27596 } 27597 const secondaryView = unit.job.allocateView(unit.xref); 27598 ingestNodes(secondaryView, children); 27599 const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan); 27600 unit.create.push(templateOp); 27601 return templateOp; 27602} 27603function ingestDeferBlock(unit, deferBlock) { 27604 const blockMeta = unit.job.deferBlocksMeta.get(deferBlock); 27605 if (blockMeta === undefined) { 27606 throw new Error(`AssertionError: unable to find metadata for deferred block`); 27607 } 27608 // Generate the defer main view and all secondary views.
27609 const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan); 27610 const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan); 27611 const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan); 27612 const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan); 27613 // Create the main defer op, and ops for all secondary views. 27614 const deferXref = unit.job.allocateXrefId(); 27615 const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan); 27616 deferOp.placeholderView = placeholder?.xref ?? null; 27617 deferOp.placeholderSlot = placeholder?.handle ?? null; 27618 deferOp.loadingSlot = loading?.handle ?? null; 27619 deferOp.errorSlot = error?.handle ?? null; 27620 deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null; 27621 deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null; 27622 deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null; 27623 unit.create.push(deferOp); 27624 // Configure all defer `on` conditions. 27625 // TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will 27626 // make it easier to refactor prefetch behavior in the future. 27627 let prefetch = false; 27628 let deferOnOps = []; 27629 let deferWhenOps = []; 27630 for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) { 27631 if (triggers.idle !== undefined) { 27632 const deferOnOp = createDeferOnOp(deferXref, { 27633 kind: DeferTriggerKind.Idle 27634 }, prefetch, triggers.idle.sourceSpan); 27635 deferOnOps.push(deferOnOp); 27636 } 27637 if (triggers.immediate !== undefined) { 27638 const deferOnOp = createDeferOnOp(deferXref, { 27639 kind: DeferTriggerKind.Immediate 27640 }, prefetch, triggers.immediate.sourceSpan); 27641 deferOnOps.push(deferOnOp); 27642 } 27643 if (triggers.timer !== undefined) { 27644 const deferOnOp = createDeferOnOp(deferXref, { 27645 kind: DeferTriggerKind.Timer, 27646 delay: triggers.timer.delay 27647 }, prefetch, triggers.timer.sourceSpan); 27648 deferOnOps.push(deferOnOp); 27649 } 27650 if (triggers.hover !== undefined) { 27651 const deferOnOp = createDeferOnOp(deferXref, { 27652 kind: DeferTriggerKind.Hover, 27653 targetName: triggers.hover.reference, 27654 targetXref: null, 27655 targetSlot: null, 27656 targetView: null, 27657 targetSlotViewSteps: null 27658 }, prefetch, triggers.hover.sourceSpan); 27659 deferOnOps.push(deferOnOp); 27660 } 27661 if (triggers.interaction !== undefined) { 27662 const deferOnOp = createDeferOnOp(deferXref, { 27663 kind: DeferTriggerKind.Interaction, 27664 targetName: triggers.interaction.reference, 27665 targetXref: null, 27666 targetSlot: null, 27667 targetView: null, 27668 targetSlotViewSteps: null 27669 }, prefetch, triggers.interaction.sourceSpan); 27670 deferOnOps.push(deferOnOp); 27671 } 27672 if (triggers.viewport !== undefined) { 27673 const deferOnOp = createDeferOnOp(deferXref, { 27674 kind: DeferTriggerKind.Viewport, 27675 targetName: triggers.viewport.reference, 27676 targetXref: null, 27677 targetSlot: null, 27678 targetView: null, 27679 targetSlotViewSteps: null 27680 }, prefetch, triggers.viewport.sourceSpan); 27681 deferOnOps.push(deferOnOp); 27682 } 27683 if (triggers.when !== undefined) { 27684 if (triggers.when.value instanceof Interpolation$1) { 27685 // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it 27686 // even mean? 27687 throw new Error(`Unexpected interpolation in defer block when trigger`); 27688 } 27689 const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan); 27690 deferWhenOps.push(deferOnOp); 27691 } 27692 // If no (non-prefetching) defer triggers were provided, default to `idle`. 27693 if (deferOnOps.length === 0 && deferWhenOps.length === 0) { 27694 deferOnOps.push(createDeferOnOp(deferXref, { 27695 kind: DeferTriggerKind.Idle 27696 }, false, null)); 27697 } 27698 prefetch = true; 27699 } 27700 unit.create.push(deferOnOps); 27701 unit.update.push(deferWhenOps); 27702} 27703function ingestIcu(unit, icu) { 27704 if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) { 27705 const xref = unit.job.allocateXrefId(); 27706 unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null)); 27707 for (const [placeholder, text] of Object.entries({ 27708 ...icu.vars, 27709 ...icu.placeholders 27710 })) { 27711 if (text instanceof BoundText) { 27712 ingestBoundText(unit, text, placeholder); 27713 } else { 27714 ingestText(unit, text, placeholder); 27715 } 27716 } 27717 unit.create.push(createIcuEndOp(xref)); 27718 } else { 27719 throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`); 27720 } 27721} 27722/** 27723 * Ingest an `@for` block into the given `ViewCompilation`. 27724 */ 27725function ingestForBlock(unit, forBlock) { 27726 const repeaterView = unit.job.allocateView(unit.xref); 27727 // Set all the context variables and aliases available in the repeater. 27728 repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value); 27729 repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value); 27730 repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value); 27731 // We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` that are 27732 // suffixed with special information, to disambiguate which level of nested loop the below aliases 27733 // refer to. 27734 // TODO: We should refactor Template Pipeline's variable phases to gracefully handle shadowing, 27735 // and arbitrarily many levels of variables depending on each other. 27736 const indexName = `ɵ${forBlock.contextVariables.$index.name}_${repeaterView.xref}`; 27737 const countName = `ɵ${forBlock.contextVariables.$count.name}_${repeaterView.xref}`; 27738 repeaterView.contextVariables.set(indexName, forBlock.contextVariables.$index.value); 27739 repeaterView.contextVariables.set(countName, forBlock.contextVariables.$count.value); 27740 repeaterView.aliases.add({ 27741 kind: SemanticVariableKind.Alias, 27742 name: null, 27743 identifier: forBlock.contextVariables.$first.name, 27744 expression: new LexicalReadExpr(indexName).identical(literal(0)) 27745 }); 27746 repeaterView.aliases.add({ 27747 kind: SemanticVariableKind.Alias, 27748 name: null, 27749 identifier: forBlock.contextVariables.$last.name, 27750 expression: new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1))) 27751 }); 27752 repeaterView.aliases.add({ 27753 kind: SemanticVariableKind.Alias, 27754 name: null, 27755 identifier: forBlock.contextVariables.$even.name, 27756 expression: new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0)) 27757 }); 27758 repeaterView.aliases.add({ 27759 kind: SemanticVariableKind.Alias, 27760 name: null, 27761 identifier: forBlock.contextVariables.$odd.name, 27762 expression: new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0)) 27763 }); 27764 const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan); 27765 const track = convertAst(forBlock.trackBy, unit.job, sourceSpan); 27766 ingestNodes(repeaterView, forBlock.children); 27767 let emptyView = null; 27768 let emptyTagName = null; 27769 if (forBlock.empty !== null) { 27770 emptyView = unit.job.allocateView(unit.xref); 27771 ingestNodes(emptyView, forBlock.empty.children); 27772 emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty); 27773 } 27774 const varNames = { 27775 $index: forBlock.contextVariables.$index.name, 27776 $count: forBlock.contextVariables.$count.name, 27777 $first: forBlock.contextVariables.$first.name, 27778 $last: forBlock.contextVariables.$last.name, 27779 $even: forBlock.contextVariables.$even.name, 27780 $odd: forBlock.contextVariables.$odd.name, 27781 $implicit: forBlock.item.name 27782 }; 27783 if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) { 27784 throw Error('AssertionError: Unhandled i18n metadata type or @for'); 27785 } 27786 if (forBlock.empty?.i18n !== undefined && !(forBlock.empty.i18n instanceof BlockPlaceholder)) { 27787 throw Error('AssertionError: Unhandled i18n metadata type or @empty'); 27788 } 27789 const i18nPlaceholder = forBlock.i18n; 27790 const emptyI18nPlaceholder = forBlock.empty?.i18n; 27791 const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock); 27792 const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan); 27793 unit.create.push(repeaterCreate); 27794 const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan)); 27795 const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan); 27796 unit.update.push(repeater); 27797} 27798/** 27799 * Convert a template AST expression into an output AST expression. 27800 */ 27801function convertAst(ast, job, baseSourceSpan) { 27802 if (ast instanceof ASTWithSource) { 27803 return convertAst(ast.ast, job, baseSourceSpan); 27804 } else if (ast instanceof PropertyRead) { 27805 const isThisReceiver = ast.receiver instanceof ThisReceiver; 27806 // Whether this is an implicit receiver, *excluding* explicit reads of `this`. 27807 const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver); 27808 // Whether the name of the read is a node that should be never retain its explicit this 27809 // receiver. 27810 const isSpecialNode = ast.name === '$any' || ast.name === '$event'; 27811 // TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only 27812 // actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and 27813 // the Typecheck block!) both have the same bug, in which they also consider explicit `this` 27814 // reads to be implicit. This causes problems when the explicit `this` read is inside a 27815 // template with a context that also provides the variable name being read: 27816 // ``` 27817 // <ng-template let-a>{{this.a}}</ng-template> 27818 // ``` 27819 // The whole point of the explicit `this` was to access the class property, but TDB and the 27820 // current TCB treat the read as implicit, and give you the context property instead! 27821 // 27822 // For now, we emulate this old behvaior by aggressively convert
27822ing explicit reads to to 27823 // implicit reads, except for the special cases that TDB and the current TCB protect. However, 27824 // it would be an improvement to fix this. 27825 // 27826 // See also the corresponding comment for the TCB, in `type_check_block.ts`. 27827 if (isImplicitReceiver || isThisReceiver && !isSpecialNode) { 27828 return new LexicalReadExpr(ast.name); 27829 } else { 27830 return new ReadPropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, null, convertSourceSpan(ast.span, baseSourceSpan)); 27831 } 27832 } else if (ast instanceof PropertyWrite) { 27833 if (ast.receiver instanceof ImplicitReceiver) { 27834 return new WritePropExpr( 27835 // TODO: Is it correct to always use the root context in place of the implicit receiver? 27836 new ContextExpr(job.root.xref), ast.name, convertAst(ast.value, job, baseSourceSpan), null, convertSourceSpan(ast.span, baseSourceSpan)); 27837 } 27838 return new WritePropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27839 } else if (ast instanceof KeyedWrite) { 27840 return new WriteKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27841 } else if (ast instanceof Call) { 27842 if (ast.receiver instanceof ImplicitReceiver) { 27843 throw new Error(`Unexpected ImplicitReceiver`); 27844 } else { 27845 return new InvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(arg => convertAst(arg, job, baseSourceSpan)), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27846 } 27847 } else if (ast instanceof LiteralPrimitive) { 27848 return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27849 } else if (ast instanceof Unary) { 27850 switch (ast.operator) { 27851 case '+': 27852 return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27853 case '-': 27854 return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27855 default: 27856 throw new Error(`AssertionError: unknown unary operator ${ast.operator}`); 27857 } 27858 } else if (ast instanceof Binary) { 27859 const operator = BINARY_OPERATORS.get(ast.operation); 27860 if (operator === undefined) { 27861 throw new Error(`AssertionError: unknown binary operator ${ast.operation}`); 27862 } 27863 return new BinaryOperatorExpr(operator, convertAst(ast.left, job, baseSourceSpan), convertAst(ast.right, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27864 } else if (ast instanceof ThisReceiver) { 27865 // TODO: should context expressions have source maps? 27866 return new ContextExpr(job.root.xref); 27867 } else if (ast instanceof KeyedRead) { 27868 return new ReadKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27869 } else if (ast instanceof Chain) { 27870 throw new Error(`AssertionError: Chain in unknown context`); 27871 } else if (ast instanceof LiteralMap) { 27872 const entries = ast.keys.map((key, idx) => { 27873 const value = ast.values[idx]; 27874 // TODO: should literals have source maps, or do we just map the whole surrounding 27875 // expression? 27876 return new LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted); 27877 }); 27878 return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan)); 27879 } else if (ast instanceof LiteralArray) { 27880 // TODO: should literals have source maps, or do we just map the whole surrounding expression? 27881 return new LiteralArrayExpr(ast.expressions.map(expr => convertAst(expr, job, baseSourceSpan))); 27882 } else if (ast instanceof Conditional) { 27883 return new ConditionalExpr(convertAst(ast.condition, job, baseSourceSpan), convertAst(ast.trueExp, job, baseSourceSpan), convertAst(ast.falseExp, job, baseSourceSpan), undefined, c
27883onvertSourceSpan(ast.span, baseSourceSpan)); 27884 } else if (ast instanceof NonNullAssert) { 27885 // A non-null assertion shouldn't impact generated instructions, so we can just drop it. 27886 return convertAst(ast.expression, job, baseSourceSpan); 27887 } else if (ast instanceof BindingPipe) { 27888 // TODO: pipes should probably have source maps; figure out details. 27889 return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [convertAst(ast.exp, job, baseSourceSpan), ...ast.args.map(arg => convertAst(arg, job, baseSourceSpan))]); 27890 } else if (ast instanceof SafeKeyedRead) { 27891 return new SafeKeyedReadExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan)); 27892 } else if (ast instanceof SafePropertyRead) { 27893 // TODO: source span 27894 return new SafePropertyReadExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name); 27895 } else if (ast instanceof SafeCall) { 27896 // TODO: source span 27897 return new SafeInvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(a => convertAst(a, job, baseSourceSpan))); 27898 } else if (ast instanceof EmptyExpr$1) { 27899 return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan)); 27900 } else if (ast instanceof PrefixNot) { 27901 return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan)); 27902 } else { 27903 throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`); 27904 } 27905} 27906function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) { 27907 let expression; 27908 if (value instanceof Interpolation$1) { 27909 expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {})); 27910 } else if (value instanceof AST) { 27911 expression = convertAst(value, job, sourceSpan ?? null); 27912 } else { 27913 expression = literal(value); 27914 } 27915 return expression; 27916} 27917// TODO: Can we populate Template binding kinds in ingest? 27918const BINDING_KINDS = new Map([[0 /* e.BindingType.Property */, BindingKind.Property], [5 /* e.BindingType.TwoWay */, BindingKind.TwoWayProperty], [1 /* e.BindingType.Attribute */, BindingKind.Attribute], [2 /* e.BindingType.Class */, BindingKind.ClassName], [3 /* e.BindingType.Style */, BindingKind.StyleProperty], [4 /* e.BindingType.Animation */, BindingKind.Animation]]); 27919/** 27920 * Checks whether the given template is a plain ng-template (as opposed to another kind of template 27921 * such as a structural directive template or control flow template). This is checked based on the 27922 * tagName. We can expect that only plain ng-templates will come through with a tagName of 27923 * 'ng-template'. 27924 * 27925 * Here are some of the cases we expect: 27926 * 27927 * | Angular HTML | Template tagName | 27928 * | ---------------------------------- | ------------------ | 27929 * | `<ng-template>` | 'ng-template' | 27930 * | `<div *ngIf="true">` | 'div' | 27931 * | `<svg><ng-template>` | 'svg:ng-template' | 27932 * | `@if (true) {` | 'Conditional' | 27933 * | `<ng-template *ngIf>` (plain) | 'ng-template' | 27934 * | `<ng-template *ngIf>` (structural) | null | 27935 */ 27936function isPlainTemplate(tmpl) { 27937 return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1; 27938} 27939/** 27940 * Ensures that the i18nMeta, if provided, is an i18n.Message. 27941 */ 27942function asMessage(i18nMeta) { 27943 if (i18nMeta == null) { 27944 return null; 27945 } 27946 if (!(i18nMeta instanceof Message)) { 27947 throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`); 27948 } 27949 return i18nMeta; 27950} 27951/** 27952 * Process all of the bindings on an element in the template AST and convert them to their IR 27953 * representation. 27954 */ 27955function ingestElementBindings(unit, op, element) { 27956 let bindings = new Array(); 27957 let i18nAttributeBindingNames = new Set(); 27958 for (const attr of element.attributes) { 27959 // Attribute literal bindings, such as `attr.foo="bar"`. 27960 const securityContext = domSchema.securityContext(element.name, attr.name, true); 27961 bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan)); 27962 if (attr.i18n) { 27963 i18nAttributeBindingNames.add(attr.name); 27964 } 27965 } 27966 for (const input of element.inputs) { 27967 if (i18nAttributeBindingNames.has(input.name)) { 27968 console.error(`On component ${unit.job.componentName}, the binding ${input.name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`); 27969 } 27970 // All dynamic bindings (both attribute and property bindings). 27971 bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan)); 27972 } 27973 unit.create.push(bindings.filter(b => b?.kind === OpKind.ExtractedAttribute)); 27974 unit.update.push(bindings.filter(b => b?.kind === OpKind.Binding)); 27975 for (const output of element.outputs) { 27976 if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) { 27977 throw Error('Animation listener should have a phase'); 27978 } 27979 if (output.type === 2 /* e.ParsedEventType.TwoWay */) { 27980 unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan)); 27981 } else { 27982 unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan)); 27983 } 27984 } 27985 // If any of the bindings on this element have an i18n message, then an i18n attrs configuration 27986 // op is also required. 27987 if (bindings.some(b => b?.i18nMessage) !== null) { 27988 unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref)); 27989 } 27990} 27991/** 27992 * Process all of the bindings on a template in the template AST and convert them to their IR 27993 * representation. 27994 */ 27995function ingestTemplateBindings(unit, op, template, templateKind) { 27996 let bindings = new Array(); 27997 for (const attr of template.templateAttrs) { 27998 if (attr instanceof TextAttribute) { 27999 const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true); 28000 bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan)); 28001 } else { 28002 bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan)); 28003 } 28004 } 28005 for (const attr of template.attributes) { 28006 // Attribute literal bindings, such as `attr.foo="bar"`. 28007 const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true); 28008 bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan)); 28009 } 28010 for (const input of template.inputs) { 28011 // Dynamic bindings (both attribute and property bindings). 28012 bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan)); 28013 } 28014 unit.create.push(bindings.filter(b => b?.kind === OpKind.ExtractedAttribute)); 28015 unit.update.push(bindings.filter(b => b?.kind === OpKind.Binding)); 28016 for (const output of template.outputs) { 28017 if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) { 28018 throw Error('Animation listener should have a phase'); 28019 } 28020 if (templateKind === TemplateKind.NgTemplate) { 28021 if (output.type === 2 /* e.ParsedEventType.TwoWay */) { 28022 unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan)); 28023 } else { 28024 unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan)); 28025 } 28026 } 28027 if (templateKind === TemplateKind.Structural && output.type !== 1 /* e.ParsedEventType.Animation */) { 28028 // Animation bindings are excluded from the structural template's const array. 28029 const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false); 28030 unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext)); 28031 } 28032 }
28033 // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.) 28034 if (bindings.some(b => b?.i18nMessage) !== null) { 28035 unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref)); 28036 } 28037} 28038/** 28039 * Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an 28040 * implicit template created via structural directive. 28041 * 28042 * Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge 28043 * cases into this function, and to comment it well to document the behavior. 28044 * 28045 * Some of this behavior is intuitively incorrect, and we should consider changing it in the future. 28046 * 28047 * @param view The compilation unit for the view containing the template. 28048 * @param xref The xref of the template op. 28049 * @param type The binding type, according to the parser. This is fairly reasonable, e.g. both 28050 * dynamic and static attributes have e.BindingType.Attribute. 28051 * @param name The binding's name. 28052 * @param value The bindings's value, which will either be an input AST expression, or a string 28053 * literal. Note that the input AST expression may or may not be const -- it will only be a 28054 * string literal if the parser considered it a text binding. 28055 * @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit. 28056 * @param securityContext The security context of the binding. 28057 * @param isStructuralTemplateAttribute Whether this binding actually applies to the structural 28058 * ng-template. For example, an `ngFor` would actually apply to the structural template. (Most 28059 * bindings on structural elements target the inner element, not the template.) 28060 * @param templateKind Whether this is an explicit `ng-template` or an implicit template created by 28061 * a structural directive. This should never be a block template. 28062 * @param i18nMessage The i18n metadata for the binding, if any. 28063 * @param sourceSpan The source span of the binding. 28064 * @returns An IR binding op, or null if the binding should be skipped. 28065 */ 28066function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) { 28067 const isTextBinding = typeof value === 'string'; 28068 // If this is a structural template, then several kinds of bindings should not result in an 28069 // update instruction. 28070 if (templateKind === TemplateKind.Structural) { 28071 if (!isStructuralTemplateAttribute) { 28072 switch (type) { 28073 case 0 /* e.BindingType.Property */: 28074 case 2 /* e.BindingType.Class */: 28075 case 3 /* e.BindingType.Style */: 28076 // Because this binding doesn't really target the ng-template, it must be a binding on an 28077 // inner node of a structural template. We can't skip it entirely, because we still need 28078 // it on the ng-template's consts (e.g. for the purposes of directive matching). However, 28079 // we should not generate an update instruction for it. 28080 return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext); 28081 case 5 /* e.BindingType.TwoWay */: 28082 return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext); 28083 } 28084 } 28085 if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) { 28086 // Again, this binding doesn't really target the ng-template; it actually targets the element 28087 // inside the structural template. In the case of non-text attribute or animation bindings, 28088 // the binding doesn't even show up on the ng-template const array, so we just skip it 28089 // entirely. 28090 return null; 28091 } 28092 } 28093 let bindingType = BINDING_KINDS.get(type); 28094 if (templateKind === TemplateKind.NgTemplate) { 28095 // We know we are dealing with bindings directly on an explicit ng-template. 28096 // Static attribute bindings should be collected into the const array as k/v pairs. Property 28097 // bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const 28098 // entries. 28099 // 28100 // The difficulty is with dynamic attribute, style, and class bindings. These don't really make 28101 // sense on an `ng-template` and should probably be parser errors. However, 28102 // TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as 28103 // well. 28104 // 28105 // Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although 28106 // TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles 28107 // and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic 28108 // attribute bindings are missing from the consts entirely. We choose to emit them into the 28109 // consts field anyway, to avoid creating special cases for something so arcane and nonsensical. 28110 if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ || type === 1 /* e.BindingType.Attribute */ && !isTextBinding) { 28111 // TODO: These cases should be parse errors. 28112 bindingType = BindingKind.Property; 28113 } 28114 } 28115 return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStru
28115cturalTemplateAttribute, templateKind, i18nMessage, sourceSpan); 28116} 28117function makeListenerHandlerOps(unit, handler, handlerSpan) { 28118 handler = astOf(handler); 28119 const handlerOps = new Array(); 28120 let handlerExprs = handler instanceof Chain ? handler.expressions : [handler]; 28121 if (handlerExprs.length === 0) { 28122 throw new Error('Expected listener to have non-empty expression list.'); 28123 } 28124 const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan)); 28125 const returnExpr = expressions.pop(); 28126 handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan)))); 28127 handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan))); 28128 return handlerOps; 28129} 28130function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) { 28131 handler = astOf(handler); 28132 const handlerOps = new Array(); 28133 if (handler instanceof Chain) { 28134 if (handler.expressions.length === 1) { 28135 handler = handler.expressions[0]; 28136 } else { 28137 // This is validated during parsing already, but we do it here just in case. 28138 throw new Error('Expected two-way listener to have a single expression.'); 28139 } 28140 } 28141 const handlerExpr = convertAst(handler, unit.job, handlerSpan); 28142 const eventReference = new LexicalReadExpr('$event'); 28143 const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference); 28144 handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
28145 handlerOps.push(createStatementOp(new ReturnStatement(eventReference))); 28146 return handlerOps; 28147} 28148function astOf(ast) { 28149 return ast instanceof ASTWithSource ? ast.ast : ast; 28150} 28151/** 28152 * Process all of the local references on an element-like structure in the template AST and 28153 * convert them to their IR representation. 28154 */ 28155function ingestReferences(op, element) { 28156 assertIsArray(op.localRefs); 28157 for (const { 28158 name, 28159 value 28160 } of element.references) { 28161 op.localRefs.push({ 28162 name, 28163 target: value 28164 }); 28165 } 28166} 28167/** 28168 * Assert that the given value is an array. 28169 */ 28170function assertIsArray(value) { 28171 if (!Array.isArray(value)) { 28172 throw new Error(`AssertionError: expected an array`); 28173 } 28174} 28175/** 28176 * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`. 28177 * 28178 * `ParseSpan` objects are relative to the start of the expression. 28179 * This method converts these to full `ParseSourceSpan` objects that 28180 * show where the span is within the overall source file. 28181 * 28182 * @param span the relative span to convert. 28183 * @param baseSourceSpan a span corresponding to the base of the expression tree. 28184 * @returns a `ParseSourceSpan` for the given span or null if no `baseSourceSpan` was provided. 28185 */ 28186function convertSourceSpan(span, baseSourceSpan) { 28187 if (baseSourceSpan === null) { 28188 return null; 28189 } 28190 const start = baseSourceSpan.start.moveBy(span.start); 28191 const end = baseSourceSpan.start.moveBy(span.end); 28192 const fullStart = baseSourceSpan.fullStart.moveBy(span.start); 28193 return new ParseSourceSpan(start, end, fullStart); 28194} 28195/** 28196 * With the directive-based control flow users were able to conditionally project content using 28197 * the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into 28198 * `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are 28199 * copied to the template via the template creation instruction. With `@if` and `@for` that is 28200 * not the case, because the conditional is placed *around* elements, rather than *on* them. 28201 * The result is that content projection won't work in the same way if a user converts from 28202 * `*ngIf` to `@if`. 28203 * 28204 * This function aims to cover the most common case by doing the same copying when a control flow 28205 * node has *one and only one* root element or template node. 28206 * 28207 * This approach comes with some caveats: 28208 * 1. As soon as any other node is added to the root, the copying behavior won't work anymore. 28209 * A diagnostic will be added to flag cases like this and to explain how to work around it. 28210 * 2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this 28211 * workaround, because it'll include an additional text node as the first child. We can work 28212 * around it here, but in a discussion it was decided not to, because the user explicitly opted 28213 * into preserving the whitespace and we would have to drop it from the generated code. 28214 * The diagnostic mentioned point #1 will flag such cases to users. 28215 * 28216 * @returns Tag name to be used for the control flow template. 28217 */ 28218function ingestControlFlowInsertionPoint(unit, xref, node) { 28219 let root = null; 28220 for (const child of node.children) { 28221 // Skip over comment nodes. 28222 if (child instanceof Comment$1) { 28223 continue; 28224 } 28225 // We can only infer the tag name/attributes if there's a single root node. 28226 if (root !== null) { 28227 return null; 28228 } 28229 // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`). 28230 if (child instanceof Element$1 || child instanceof Template && child.tagName !== null) { 28231 root = child; 28232 } 28233 } 28234 // If we've found a single root node, its tag name and attributes can be 28235 // copied to the surrounding template to be used for content projection. 28236 if (root !== null) { 28237 // Collect the static attributes for content projection purposes. 28238 for (const attr of root.attributes) { 28239 const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true); 28240 unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan)); 28241 } 28242 // Also collect the inputs since they participate in content projection as well. 28243 // Note that TDB used to collect the outputs as well, but it wasn't passing them into 28244 // the template instruction. Here we just don't collect them. 28245 for (const attr of root.inputs) { 28246 if (attr.type !== 4 /* e.BindingType.Animation */ && attr.type !== 1 /* e.BindingType.Attribute */) { 28247 const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true); 28248 unit.create.push(createExtractedAttributeOp(xref, BindingKind.Property, null, attr.name, null, null, null, securityContext)); 28249 } 28250 } 28251 const tagName = root instanceof Element$1 ? root.name : root.tagName; 28252 // Don't pass along `ng-template` tag name since it enables directive matching. 28253 return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName; 28254 } 28255 return null; 28256} 28257const USE_TEMPLATE_PIPELINE = false;
28258class HtmlParser extends Parser { 28259 constructor() { 28260 super(getHtmlTagDefinition); 28261 } 28262 parse(source, url, options) { 28263 return super.parse(source, url, options); 28264 } 28265} 28266const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces'; 28267const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']); 28268// Equivalent to \s with \u00a0 (non-breaking space) excluded. 28269// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp 28270const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'; 28271const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`); 28272const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g'); 28273function hasPreserveWhitespacesAttr(attrs) { 28274 return attrs.some(attr => attr.name === PRESERVE_WS_ATTR_NAME); 28275} 28276/** 28277 * &ngsp; is a placeholder for non-removable space 28278 * &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character 28279 * and later on replaced by a space. 28280 */ 28281function replaceNgsp(value) { 28282 // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE 28283 return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' '); 28284} 28285/** 28286 * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules: 28287 * - consider spaces, tabs and new lines as whitespace characters; 28288 * - drop text nodes consisting of whitespace characters only; 28289 * - for all other text nodes replace consecutive whitespace characters with one space; 28290 * - convert &ngsp; pseudo-entity to a single space; 28291 * 28292 * Removal and trimming of whitespaces have positive performance impact (less code to generate 28293 * while compiling templates, faster view creation). At the same time it can be "destructive" 28294 * in some cases (whitespaces can influence layout). Because of the potential of breaking layout 28295 * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for 28296 * whitespace removal. The default option for whitespace removal will be revisited in Angular 6 28297 * and might be changed to "on" by default. 28298 */ 28299class WhitespaceVisitor { 28300 visitElement(element, context) { 28301 if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) { 28302 // don't descent into elements where we need to preserve whitespaces 28303 // but still visit all attributes to eliminate one used as a market to preserve WS 28304 return new Element(element.name, visitAll(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); 28305 } 28306 return new Element(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); 28307 } 28308 visitAttribute(attribute, context) { 28309 return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null; 28310 } 28311 visitText(text, context) { 28312 const isNotBlank = text.value.match(NO_WS_REGEXP); 28313 const hasExpansionSibling = context && (context.prev instanceof Expansion || context.next instanceof Expansion); 28314 if (isNotBlank || hasExpansionSibling) { 28315 // Process the whitespace in the tokens of this Text node 28316 const tokens = text.tokens.map(token => token.type === 5 /* TokenType.TEXT */ ? createWhitespaceProcessedTextToken(token) : token); 28317 // Process the whitespace of the value of this Text node 28318 const value = processWhitespace(text.value); 28319 return new Text(value, text.sourceSpan, tokens, text.i18n); 28320 } 28321 return null; 28322 } 28323 visitComment(comment, context) { 28324 return comment; 28325 } 28326 visitExpansion(expansion, context) { 28327 return expansion; 28328 } 28329 visitExpansionCase(expansionCase, context) { 28330 return expansionCase; 28331 } 28332 visitBlock(block, context) { 28333 return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.nameSpan, block.startSourceSpan, block.endSourceSpan); 28334 } 28335 visitBlockParameter(parameter, context) { 28336 return parameter; 28337 } 28338} 28339function createWhitespaceProcessedTextToken({ 28340 type, 28341 parts, 28342 sourceSpan 28343}) { 28344 return { 28345 type, 28346 parts: [processWhitespace(parts[0])], 28347 sourceSpan 28348 }; 28349} 28350function processWhitespace(text) { 28351 return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' '); 28352}
28353function removeWhitespaces(htmlAstWithErrors) { 28354 return new ParseTreeResult(visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors); 28355} 28356function visitAllWithSiblings(visitor, nodes) { 28357 const result = []; 28358 nodes.forEach((ast, i) => { 28359 const context = { 28360 prev: nodes[i - 1], 28361 next: nodes[i + 1] 28362 }; 28363 const astResult = ast.visit(visitor, context); 28364 if (astResult) { 28365 result.push(astResult); 28366 } 28367 }); 28368 return result; 28369} 28370const PROPERTY_PARTS_SEPARATOR = '.'; 28371const ATTRIBUTE_PREFIX = 'attr'; 28372const CLASS_PREFIX = 'class'; 28373const STYLE_PREFIX = 'style'; 28374const TEMPLATE_ATTR_PREFIX$1 = '*'; 28375const ANIMATE_PROP_PREFIX = 'animate-'; 28376/** 28377 * Parses bindings in templates and in the directive host area. 28378 */ 28379class BindingParser { 28380 constructor(_exprParser, _interpolationConfig, _schemaRegistry, errors) { 28381 this._exprParser = _exprParser; 28382 this._interpolationConfig = _interpolationConfig; 28383 this._schemaRegistry = _schemaRegistry; 28384 this.errors = errors; 28385 } 28386 get interpolationConfig() { 28387 return this._interpolationConfig; 28388 } 28389 createBoundHostProperties(properties, sourceSpan) { 28390 const boundProps = []; 28391 for (const propName of Object.keys(properties)) { 28392 const expression = properties[propName]; 28393 if (typeof expression === 'string') { 28394 this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [], 28395 // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the 28396 // sourceSpan, as it represents the sourceSpan of the host itself rather than the 28397 // source of the host binding (which doesn't exist in the template). Regardless, 28398 // neither of these values are used in Ivy but are only here to satisfy the function 28399 // signature. This should likely be refactored in the future so that `sourceSpan` 28400 // isn't being used inaccurately. 28401 boundProps, sourceSpan); 28402 } else { 28403 this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan); 28404 } 28405 } 28406 return boundProps; 28407 } 28408 createDirectiveHostEventAsts(hostListeners, sourceSpan) { 28409 const targetEvents = []; 28410 for (const propName of Object.keys(hostListeners)) { 28411 const expression = hostListeners[propName]; 28412 if (typeof expression === 'string') { 28413 // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but 28414 // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself 28415 // rather than the source of the host binding (which doesn't exist in the template). 28416 // Regardless, neither of these values are used in Ivy but are only here to satisfy the 28417 // function signature. This should likely be refactored in the future so that `sourceSpan` 28418 // isn't being used inaccurately. 28419 this.parseEvent(propName, expression, /* isAssignmentEvent */false, sourceSpan, sourceSpan, [], targetEvents, sourceSpan); 28420 } else { 28421 this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan); 28422 } 28423 } 28424 return targetEvents; 28425 } 28426 parseInterpolation(value, sourceSpan, interpolatedTokens) { 28427 const sourceInfo = sourceSpan.start.toString(); 28428 const absoluteOffset = sourceSpan.fullStart.offset; 28429 try { 28430 const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, interpolatedTokens, this._interpolationConfig); 28431 if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); 28432 return ast; 28433 } catch (e) { 28434 this._reportError(`${e}`, sourceSpan); 28435 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); 28436 } 28437 } 28438 /** 28439 * Similar to `parseInterpolation`, but treats the provided string as a single expression 28440 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`). 28441 * This is used for parsing the switch expression in ICUs. 28442 */ 28443 parseInterpolationExpression(expression, sourceSpan) { 28444 const sourceInfo = sourceSpan.start.toString(); 28445 const absoluteOffset = sourceSpan.start.offset; 28446 try { 28447 const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset); 28448 if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); 28449 return ast; 28450 } catch (e) { 28451 this._reportError(`${e}`, sourceSpan); 28452 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); 28453 } 28454 } 28455 /** 28456 * Parses the bindings in a microsyntax expression, and converts them to 28457 * `ParsedProperty` or `ParsedVariable`. 28458 * 28459 * @param tplKey template binding name 28460 * @param tplValue template binding value 28461 * @param sourceSpan span of template binding relative to entire the template 28462 * @param absoluteValueOffset start of the tplValue relative to the entire template 28463 * @param targetMatchableAttrs potential attributes to match in the template 28464 * @param targetProps target property bindings in the template 28465 * @param targetVars target variables in the template 28466 */ 28467 parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) { 28468 const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX$1.length; 28469 const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset); 28470 for (const binding of bindings) { 28471 // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular 28472 // binding within the microsyntax expression so it's more narrow than sourceSpan. 28473 const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
28474 const key = binding.key.source; 28475 const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span); 28476 if (binding instanceof VariableBinding) { 28477 const value = binding.value ? binding.value.source : '$implicit'; 28478 const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined; 28479 targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan)); 28480 } else if (binding.value) { 28481 const srcSpan = isIvyAst ? bindingSpan : sourceSpan; 28482 const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan); 28483 this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); 28484 } else { 28485 targetMatchableAttrs.push([key, '' /* value */]); 28486 // Since this is a literal attribute with no RHS, source span should be 28487 // just the key span. 28488 this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan); 28489 } 28490 } 28491 } 28492 /** 28493 * Parses the bindings in a microsyntax expression, e.g. 28494 * ``` 28495 * <tag *tplKey="let value1 = prop; let value2 = localVar"> 28496 * ``` 28497 * 28498 * @param tplKey template binding name 28499 * @param tplValue template binding value 28500 * @param sourceSpan span of template binding relative to entire the template 28501 * @param absoluteKeyOffset start of the `tplKey` 28502 * @param absoluteValueOffset start of the `tplValue` 28503 */ 28504 _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) { 28505 const sourceInfo = sourceSpan.start.toString(); 28506 try { 28507 const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset); 28508 this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
28509 bindingsResult.warnings.forEach(warning => { 28510 this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); 28511 }); 28512 return bindingsResult.templateBindings; 28513 } catch (e) { 28514 this._reportError(`${e}`, sourceSpan); 28515 return []; 28516 } 28517 } 28518 parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) { 28519 if (isAnimationLabel(name)) { 28520 name = name.substring(1); 28521 if (keySpan !== undefined) { 28522 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); 28523 } 28524 if (value) { 28525 this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR); 28526 } 28527 this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps); 28528 } else { 28529 targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan)); 28530 } 28531 } 28532 parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) { 28533 if (name.length === 0) { 28534 this._reportError(`Property name is missing in binding`, sourceSpan); 28535 } 28536 let isAnimationProp = false; 28537 if (name.startsWith(ANIMATE_PROP_PREFIX)) { 28538 isAnimationProp = true; 28539 name = name.substring(ANIMATE_PROP_PREFIX.length); 28540 if (keySpan !== undefined) { 28541 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset)); 28542 } 28543 } else if (isAnimationLabel(name)) { 28544 isAnimationProp = true; 28545 name = name.substring(1); 28546 if (keySpan !== undefined) { 28547 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); 28548 } 28549 } 28550 if (isAnimationProp) { 28551 this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps); 28552 } else { 28553 this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); 28554 } 28555 } 28556 parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) { 28557 const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens); 28558 if (expr) { 28559 this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); 28560 return true; 28561 } 28562 return false; 28563 } 28564 _parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) { 28565 targetMatchableAttrs.push([name, ast.source]); 28566 targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? ParsedPropertyType.TWO_WAY : ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan)); 28567 } 28568 _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) { 28569 if (name.length === 0) { 28570 this._reportError('Animation trigger is missing', sourceSpan); 28571 } 28572 // This will occur when a @trigger is not paired with an expression. 28573 // For animations it is valid to not have an expression since */void 28574 // states will be applied by angular when the element is attached/detached 28575 const ast = this.parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset); 28576 targetMatchableAttrs.push([name, ast.source]); 28577 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan)); 28578 } 28579 parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) { 28580 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString(); 28581 try { 28582 const ast = isHostBinding ? this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) : this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig); 28583 if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); 28584 return ast; 28585 } catch (e) { 28586 this._reportError(`${e}`, sourceSpan); 28587 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); 28588 } 28589 } 28590 createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) { 28591 if (boundProp.isAnimation) { 28592 return new BoundElementProperty(boundProp.name, 4 /* BindingType.Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan); 28593 } 28594 let unit = null; 28595 let bindingType = undefined; 28596 let boundPropertyName = null; 28597 const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR); 28598 let securityContexts = undefined; 28599 // Check for special cases (prefix style, attr, class) 28600 if (parts.length > 1) { 28601 if (parts[0] == ATTRIBUTE_PREFIX) { 28602 boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR); 28603 if (!skipValidation) {
28604 this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true); 28605 } 28606 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true); 28607 const nsSeparatorIdx = boundPropertyName.indexOf(':'); 28608 if (nsSeparatorIdx > -1) { 28609 const ns = boundPropertyName.substring(0, nsSeparatorIdx); 28610 const name = boundPropertyName.substring(nsSeparatorIdx + 1); 28611 boundPropertyName = mergeNsAndName(ns, name); 28612 } 28613 bindingType = 1 /* BindingType.Attribute */; 28614 } else if (parts[0] == CLASS_PREFIX) { 28615 boundPropertyName = parts[1]; 28616 bindingType = 2 /* BindingType.Class */; 28617 securityContexts = [SecurityContext.NONE]; 28618 } else if (parts[0] == STYLE_PREFIX) { 28619 unit = parts.length > 2 ? parts[2] : null; 28620 boundPropertyName = parts[1]; 28621 bindingType = 3 /* BindingType.Style */; 28622 securityContexts = [SecurityContext.STYLE]; 28623 } 28624 } 28625 // If not a special case, use the full property name 28626 if (boundPropertyName === null) { 28627 const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name); 28628 boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name; 28629 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false); 28630 bindingType = boundProp.type === ParsedPropertyType.TWO_WAY ? 5 /* BindingType.TwoWay */ : 0 /* BindingType.Property */; 28631 if (!skipValidation) { 28632 this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false); 28633 } 28634 } 28635 return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan); 28636 } 28637 // TODO: keySpan should be required but was made optional to avoid changing VE parser. 28638 parseEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) { 28639 if (name.length === 0) { 28640 this._reportError(`Event name is missing in binding`, sourceSpan); 28641 } 28642 if (isAnimationLabel(name)) { 28643 name = name.slice(1); 28644 if (keySpan !== undefined) { 28645 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); 28646 } 28647 this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan); 28648 } else { 28649 this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan); 28650 } 28651 } 28652 calcPossibleSecurityContexts(selector, propName, isAttribute) { 28653 const prop = this._schemaRegistry.getMappedPropName(propName); 28654 return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute); 28655 } 28656 _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) { 28657 const matches = splitAtPeriod(name, [name, '']); 28658 const eventName = matches[0]; 28659 const phase = matches[1].toLowerCase(); 28660 const ast = this._parseAction(expression, handlerSpan); 28661 targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan)); 28662 if (eventName.length === 0) { 28663 this._reportError(`Animation event name is missing in binding`, sourceSpan); 28664 } 28665 if (phase) { 28666 if (phase !== 'start' && phase !== 'done') { 28667 this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan); 28668 } 28669 } else { 28670 this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan); 28671 } 28672 } 28673 _parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) { 28674 // long format: 'target: eventName' 28675 const [target, eventName] = splitAtColon(name, [null, name]); 28676 const prevErrorCount = this.errors.length; 28677 const ast = this._parseAction(expression, handlerSpan); 28678 const isValid = this.errors.length === prevErrorCount; 28679 targetMatchableAttrs.push([name, ast.source]); 28680 // Don't try to validate assignment events if there were other 28681 // parsing errors to avoid adding more noise to the error logs. 28682 if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) { 28683 this._reportError('Unsupported expression in a two-way binding', sourceSpan); 28684 } 28685 targetEvents.push(new ParsedEvent(eventName, target, isAssignmentEvent ? 2 /* ParsedEventType.TwoWay */ : 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan)); 28686 // Don't detect directives for event names for now, 28687 // so don't add the event name to the matchableAttrs 28688 } 28689 _parseAction(value, sourceSpan) { 28690 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString(); 28691 const absoluteOffset = sourceSpan && sourceSpan.start ? sourceSpan.start.offset : 0; 28692 try { 28693 const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig); 28694 if (ast) { 28695 this._reportExpressionParserErrors(ast.errors, sourceSpan); 28696 } 28697 if (!ast || ast.ast instanceof EmptyExpr$1) { 28698 this._reportError(`Empty expressions are not allowed`, sourceSpan); 28699 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); 28700 } 28701 return ast; 28702 } catch (e) { 28703 this._reportError(`${e}`, sourceSpan); 28704 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); 28705 } 28706 } 28707 _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) { 28708 this.errors.push(new ParseError(sourceSpan, message, level)); 28709 } 28710 _reportExpressionParserErrors(errors, sourceSpan) { 28711 for (const error of errors) { 28712 this._reportError(error.message, sourceSpan); 28713 } 28714 } 28715 /** 28716 * @param propName the name of the property / attribute 28717 * @param sourceSpan 28718 * @param isAttr true when binding to an attribute 28719 */ 28720 _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) { 28721 const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : this._schemaRegistry.validateProperty(propName); 28722 if (report.error) { 28723 this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR); 28724 } 28725 } 28726 /** 28727 * Returns whether a parsed AST is allowed to be used within the event side of a two-way binding. 28728 * @param ast Parsed AST to be checked. 28729 */ 28730 _isAllowedAssignmentEvent(ast) { 28731 if (ast instanceof ASTWithSource) { 28732 return this._isAllowedAssignmentEvent(ast.ast); 28733 } 28734 if (ast instanceof NonNullAssert) { 28735 return this._isAllowedAssignmentEvent(ast.expression); 28736 } 28737 if (ast instanceof PropertyRead || ast instanceof KeyedRead) { 28738 return true; 28739 } 28740 if (ast instanceof Binary) { 28741 return (ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') && (ast.right instanceof PropertyRead || ast.right instanceof KeyedRead); 28742 } 28743 return ast instanceof Conditional || ast instanceof PrefixNot; 28744 } 28745} 28746class PipeCollector extends RecursiveAstVisitor { 28747 constructor() { 28748 super(...arguments); 28749 this.pipes = new Map(); 28750 } 28751 visitPipe(ast, context) { 28752 this.pipes.set(ast.name, ast); 28753 ast.exp.visit(this); 28754 this.visitAll(ast.args, context); 28755 return null; 28756 } 28757} 28758function isAnimationLabel(name) { 28759 return name[0] == '@'; 28760} 28761function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) { 28762 const ctxs = []; 28763 CssSelector.parse(selector).forEach(selector => { 28764 const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames(); 28765 const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector()).map(selector => selector.element)); 28766 const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName)); 28767 ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute))); 28768 }); 28769 return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort(); 28770} 28771/** 28772 * Compute a new ParseSourceSpan based off an original `sourceSpan` by using 28773 * absolute offsets from the specified `absoluteSpan`. 28774 * 28775 * @param sourceSpan original source span 28776 * @param absoluteSpan absolute source span to move to 28777 */ 28778function moveParseSourceSpan(sourceSpan, absoluteSpan) { 28779 // The difference of two absolute offsets provide the relative offset 28780 const startDiff = absoluteSpan.start - sourceSpan.start.offset;
28781 const endDiff = absoluteSpan.end - sourceSpan.end.offset; 28782 return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details); 28783} 28784 28785// Some of the code comes from WebComponents.JS 28786// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js 28787function isStyleUrlResolvable(url) { 28788 if (url == null || url.length === 0 || url[0] == '/') return false; 28789 const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP); 28790 return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset'; 28791} 28792const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/; 28793const NG_CONTENT_SELECT_ATTR$1 = 'select'; 28794const LINK_ELEMENT = 'link'; 28795const LINK_STYLE_REL_ATTR = 'rel'; 28796const LINK_STYLE_HREF_ATTR = 'href'; 28797const LINK_STYLE_REL_VALUE = 'stylesheet'; 28798const STYLE_ELEMENT = 'style'; 28799const SCRIPT_ELEMENT = 'script'; 28800const NG_NON_BINDABLE_ATTR = 'ngNonBindable'; 28801const NG_PROJECT_AS = 'ngProjectAs'; 28802function preparseElement(ast) { 28803 let selectAttr = null; 28804 let hrefAttr = null; 28805 let relAttr = null; 28806 let nonBindable = false; 28807 let projectAs = ''; 28808 ast.attrs.forEach(attr => { 28809 const lcAttrName = attr.name.toLowerCase(); 28810 if (lcAttrName == NG_CONTENT_SELECT_ATTR$1) { 28811 selectAttr = attr.value; 28812 } else if (lcAttrName == LINK_STYLE_HREF_ATTR) { 28813 hrefAttr = attr.value; 28814 } else if (lcAttrName == LINK_STYLE_REL_ATTR) { 28815 relAttr = attr.value; 28816 } else if (attr.name == NG_NON_BINDABLE_ATTR) { 28817 nonBindable = true; 28818 } else if (attr.name == NG_PROJECT_AS) { 28819 if (attr.value.length > 0) { 28820 projectAs = attr.value; 28821 } 28822 } 28823 }); 28824 selectAttr = normalizeNgContentSelect(selectAttr); 28825 const nodeName = ast.name.toLowerCase(); 28826 let type = PreparsedElementType.OTHER; 28827 if (isNgContent(nodeName)) { 28828 type = PreparsedElementType.NG_CONTENT; 28829 } else if (nodeName == STYLE_ELEMENT) { 28830 type = PreparsedElementType.STYLE; 28831 } else if (nodeName == SCRIPT_ELEMENT) { 28832 type = PreparsedElementType.SCRIPT; 28833 } else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) { 28834 type = PreparsedElementType.STYLESHEET; 28835 } 28836 return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs); 28837} 28838var PreparsedElementType; 28839(function (PreparsedElementType) { 28840 PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT"; 28841 PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE"; 28842 PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET"; 28843 PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT"; 28844 PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER"; 28845})(PreparsedElementType || (PreparsedElementType = {})); 28846class PreparsedElement { 28847 constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) { 28848 this.type = type; 28849 this.selectAttr = selectAttr; 28850 this.hrefAttr = hrefAttr; 28851 this.nonBindable = nonBindable; 28852 this.projectAs = projectAs; 28853 } 28854} 28855function normalizeNgContentSelect(selectAttr) { 28856 if (selectAttr === null || selectAttr.length === 0) { 28857 return '*'; 28858 } 28859 return selectAttr; 28860} 28861 28862/** Pattern for the expression in a for loop block. */ 28863const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/; 28864/** Pattern for the tracking expression in a for loop block. */ 28865const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/; 28866/** Pattern for the `as` expression in a conditional block. */ 28867const CONDITIONAL_ALIAS_PATTERN = /^(as\s)+(.*)/; 28868/** Pattern used to identify an `else if` block. */ 28869const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/; 28870/** Pattern used to identify a `let` parameter. */ 28871const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/; 28872/** 28873 * Pattern to group a string into leading whitespace, non whitespace, and trailing whitespace. 28874 * Useful for getting the variable name span when a span can contain leading and trailing space. 28875 */ 28876const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/; 28877/** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */ 28878const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']); 28879/** 28880 * Predicate function that determines if a block with 28881 * a specific name cam be connected to a `for` block. 28882 */ 28883function isConnectedForLoopBlock(name) { 28884 return name === 'empty'; 28885} 28886/** 28887 * Predicate function that determines if a block with 28888 * a specific name cam be connected to an `if` block. 28889 */ 28890function isConnectedIfLoopBlock(name) { 28891 return name === 'else' || ELSE_IF_PATTERN.test(name); 28892} 28893/** Creates an `if` loop block from an HTML AST node. */ 28894function createIfBlock(ast, connectedBlocks, visitor, bindingParser) { 28895 const errors = validateIfConnectedBlocks(connectedBlocks); 28896 const branches = []; 28897 const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser); 28898 if (mainBlockParams !== null) { 28899 branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan, ast.i18n)); 28900 } 28901 for (const block of connectedBlocks) { 28902 if (ELSE_IF_PATTERN.test(block.name)) { 28903 const params = parseConditionalBlockParameters(block, errors, bindingParser); 28904 if (params !== null) { 28905 const children = visitAll(visitor, block.children, block.children); 28906 branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n)); 28907 } 28908 } else if (block.name === 'else') { 28909 const children = visitAll(visitor, block.children, block.children); 28910 branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n)); 28911 } 28912 } 28913 // The outer IfBlock should have a span that encapsulates all branches. 28914 const ifBlockStartSourceSpan = branches.length > 0 ? branches[0].startSourceSpan : ast.startSourceSpan; 28915 const ifBlockEndSourceSpan = branches.length > 0 ? branches[branches.length - 1].endSourceSpan : ast.endSourceSpan; 28916 let wholeSourceSpan = ast.sourceSpan; 28917 const lastBranch = branches[branches.length - 1]; 28918 if (lastBranch !== undefined) { 28919 wholeSourceSpan = new ParseSourceSpan(ifBlockStartSourceSpan.start, lastBranch.sourceSpan.end); 28920 } 28921 return { 28922 node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan, ast.nameSpan), 28923 errors 28924 }; 28925} 28926/** Creates a `for` loop block from an HTML AST node. */ 28927function createForLoop(ast, connectedBlocks, visitor, bindingParser) { 28928 const errors = []; 28929 const params = parseForLoopParameters(ast, errors, bindingParser); 28930 let node = null; 28931 let empty = null; 28932 for (const block of connectedBlocks) { 28933 if (block.name === 'empty') { 28934 if (empty !== null) { 28935 errors.push(new ParseError(block.sourceSpan, '@for loop can only have one @empty block')); 28936 } else if (block.parameters.length > 0) { 28937 errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters')); 28938 } else { 28939 empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n); 28940 } 28941 } else { 28942 errors.push(new ParseError(block.sourceSpan, `Unrecognized @for loop block "${block.name}"`)); 28943 } 28944 } 28945 if (params !== null) { 28946 if (params.trackBy === null) { 28947 // TODO: We should not fail here, and instead try to produce some AST for the language 28948 // service. 28949 errors.push(new ParseError(ast.sourceSpan, '@for loop must have a "track" expression')); 28950 } else { 28951 // The `for` block has a main span that includes the `empty` branch. For only the span of the 28952 // main `for` body, use `mainSourceSpan`. 28953 const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan; 28954 const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end); 28955 node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan, ast.i18n); 28956 } 28957 } 28958 return { 28959 node, 28960 errors 28961 }; 28962} 28963/** Creates a switch block from an HTML AST node. */ 28964function createSwitchBlock(ast, visitor, bindingParser) { 28965 const errors = validateSwitchBlock(ast); 28966 const primaryExpression = ast.parameters.length > 0 ? parseBlockParameterToBinding(ast.parameters[0], bindingParser) : bindingParser.parseBinding('', false, ast.sourceSpan, 0); 28967 const cases = []; 28968 const unknownBlocks = []; 28969 let defaultCase = null; 28970 // Here we assume that all the blocks are valid given that we validated them above. 28971 for (const node of ast.children) { 28972 if (!(node instanceof Block)) { 28973 continue; 28974 } 28975 if ((node.name !== 'case' || node.parameters.length === 0) && node.name !== 'default') { 28976 unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan, node.nameSpan)); 28977 continue; 28978 } 28979 const expression = node.name === 'case' ? parseBlockParameterToBinding(node.parameters[0], bindingParser) : null; 28980 const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan, node.i18n); 28981 if (expression === null) { 28982 defaultCase = ast; 28983 } else { 28984 cases.push(ast); 28985 } 28986 } 28987 // Ensure that the default case is last in the array. 28988 if (defaultCase !== null) { 28989 cases.push(defaultCase); 28990 } 28991 return { 28992 node: new SwitchBlock(primaryExpression, cases, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan), 28993 errors 28994 }; 28995} 28996/** Parses the parameters of a `for` loop block. */ 28997function parseForLoopParameters(block, errors, bindingParser) { 28998 if (block.parameters.length === 0) { 28999 errors.push(new ParseError(block.sourceSpan, '@for loop does not have an expression')); 29000 return null; 29001 } 29002 const [expressionParam, ...secondaryParams] = block.parameters; 29003 const match = stripOptionalParentheses(expressionParam, errors)?.match(FOR_LOOP_EXPRESSION_PATTERN); 29004 if (!match || match[2].trim().length === 0) { 29005 errors.push(new ParseError(expressionParam.sourceSpan, 'Cannot parse expression. @for loop expression must match the pattern "<identifier> of <expression>"')); 29006 return null; 29007 } 29008 const [, itemName, rawExpression] = match; 29009 // `expressionParam.expression` contains the variable declaration and the expression of the 29010 // for...of statement, i.e. 'user of users' The variable of a ForOfStatement is _only_ the "const 29011 // user" part and does not include "of x". 29012 const variableName = expressionParam.expression.split(' ')[0]; 29013 const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length)); 29014 const result = { 29015 itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan), 29016 trackBy: null, 29017 expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression), 29018 context: {} 29019 }; 29020 for (const param of secondaryParams) { 29021 const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN); 29022 if (letMatch !== null) { 29023 const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end); 29024 parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, result.context, errors); 29025 continue; 29026 } 29027 const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN); 29028 if (trackMatch !== null) { 29029 if (result.trackBy !== null) { 29030 errors.push(new ParseError(param.sourceSpan, '@for loop can only have one "track" expression')); 29031 } else { 29032 const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]); 29033 if (expression.ast instanceof EmptyExpr$1) { 29034 errors.push(new ParseError(param.sourceSpan, '@for loop must have a "track" expression')); 29035 }
29036 const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length)); 29037 result.trackBy = { 29038 expression, 29039 keywordSpan 29040 }; 29041 } 29042 continue; 29043 } 29044 errors.push(new ParseError(param.sourceSpan, `Unrecognized @for loop paramater "${param.expression}"`)); 29045 } 29046 // Fill out any variables that haven't been defined explicitly. 29047 for (const variableName of ALLOWED_FOR_LOOP_LET_VARIABLES) { 29048 if (!result.context.hasOwnProperty(variableName)) { 29049 // Give ambiently-available context variables empty spans at the end of the start of the `for` 29050 // block, since they are not explicitly defined. 29051 const emptySpanAfterForBlockStart = new ParseSourceSpan(block.startSourceSpan.end, block.startSourceSpan.end); 29052 result.context[variableName] = new Variable(variableName, variableName, emptySpanAfterForBlockStart, emptySpanAfterForBlockStart); 29053 } 29054 } 29055 return result; 29056} 29057/** Parses the `let` parameter of a `for` loop block. */ 29058function parseLetParameter(sourceSpan, expression, span, context, errors) { 29059 const parts = expression.split(','); 29060 let startSpan = span.start; 29061 for (const part of parts) { 29062 const expressionParts = part.split('='); 29063 const name = expressionParts.length === 2 ? expressionParts[0].trim() : ''; 29064 const variableName = expressionParts.length === 2 ? expressionParts[1].trim() : ''; 29065 if (name.length === 0 || variableName.length === 0) { 29066 errors.push(new ParseError(sourceSpan, `Invalid @for loop "let" parameter. Parameter should match the pattern "<name> = <variable name>"`)); 29067 } else if (!ALLOWED_FOR_LOOP_LET_VARIABLES.has(variableName)) { 29068 errors.push(new ParseError(sourceSpan, `Unknown "let" parameter variable "${variableName}". The allowed variables are: ${Array.from(ALLOWED_FOR_LOOP_LET_VARIABLES).join(', ')}`)); 29069 } else if (context.hasOwnProperty(variableName)) { 29070 errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`)); 29071 } else { 29072 const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? []; 29073 const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ? new ParseSourceSpan( /* strip leading spaces */ 29074 startSpan.moveBy(keyLeadingWhitespace.length), /* advance to end of the variable name */ 29075 startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) : span; 29076 let valueSpan = undefined; 29077 if (expressionParts.length === 2) { 29078 const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? []; 29079 valueSpan = valueLeadingWhitespace !== undefined ? new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length + implicit.length)) : undefined; 29080 } 29081 const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end); 29082 context[variableName] = new Variable(name, variableName, sourceSpan, keySpan, valueSpan); 29083 } 29084 startSpan = startSpan.moveBy(part.length + 1 /* add 1 to move past the comma */); 29085 } 29086} 29087/** 29088 * Checks that the shape of the blocks connected to an 29089 * `@if` block is correct. Returns an array of errors. 29090 */ 29091function validateIfConnectedBlocks(connectedBlocks) { 29092 const errors = []; 29093 let hasElse = false; 29094 for (let i = 0; i < connectedBlocks.length; i++) { 29095 const block = connectedBlocks[i]; 29096 if (block.name === 'else') { 29097 if (hasElse) { 29098 errors.push(new ParseError(block.sourceSpan, 'Conditional can only have one @else block')); 29099 } else if (connectedBlocks.length > 1 && i < connectedBlocks.length - 1) { 29100 errors.push(new ParseError(block.sourceSpan, '@else block must be last inside the conditional')); 29101 } else if (block.parameters.length > 0) { 29102 errors.push(new ParseError(block.sourceSpan, '@else block cannot have parameters')); 29103 } 29104 hasElse = true; 29105 } else if (!ELSE_IF_PATTERN.test(block.name)) { 29106 errors.push(new ParseError(block.sourceSpan, `Unrecognized conditional block @${block.name}`)); 29107 } 29108 } 29109 return errors; 29110} 29111/** Checks that the shape of a `switch` block is valid. Returns an array of errors. */ 29112function validateSwitchBlock(ast) { 29113 const errors = []; 29114 let hasDefault = false;
29115 if (ast.parameters.length !== 1) { 29116 errors.push(new ParseError(ast.sourceSpan, '@switch block must have exactly one parameter')); 29117 return errors; 29118 } 29119 for (const node of ast.children) { 29120 // Skip over comments and empty text nodes inside the switch block. 29121 // Empty text nodes can be used for formatting while comments don't affect the runtime. 29122 if (node instanceof Comment || node instanceof Text && node.value.trim().length === 0) { 29123 continue; 29124 } 29125 if (!(node instanceof Block) || node.name !== 'case' && node.name !== 'default') { 29126 errors.push(new ParseError(node.sourceSpan, '@switch block can only contain @case and @default blocks')); 29127 continue; 29128 } 29129 if (node.name === 'default') { 29130 if (hasDefault) { 29131 errors.push(new ParseError(node.sourceSpan, '@switch block can only have one @default block')); 29132 } else if (node.parameters.length > 0) { 29133 errors.push(new ParseError(node.sourceSpan, '@default block cannot have parameters')); 29134 } 29135 hasDefault = true; 29136 } else if (node.name === 'case' && node.parameters.length !== 1) { 29137 errors.push(new ParseError(node.sourceSpan, '@case block must have exactly one parameter')); 29138 } 29139 } 29140 return errors; 29141} 29142/** 29143 * Parses a block parameter into a binding AST. 29144 * @param ast Block parameter that should be parsed. 29145 * @param bindingParser Parser that the expression should be parsed with. 29146 * @param part Specific part of the expression that should be parsed. 29147 */ 29148function parseBlockParameterToBinding(ast, bindingParser, part) { 29149 let start; 29150 let end; 29151 if (typeof part === 'string') { 29152 // Note: `lastIndexOf` here should be enough to know the start index of the expression, 29153 // because we know that it'll be at the end of the param. Ideally we could use the `d` 29154 // flag when matching via regex and get the index from `match.indices`, but it's unclear 29155 // if we can use it yet since it's a relatively new feature. See: 29156 // https://github.com/tc39/proposal-regexp-match-indices 29157 start = Math.max(0, ast.expression.lastIndexOf(part)); 29158 end = start + part.length; 29159 } else { 29160 start = 0; 29161 end = ast.expression.length; 29162 } 29163 return bindingParser.parseBinding(ast.expression.slice(start, end), false, ast.sourceSpan, ast.sourceSpan.start.offset + start); 29164} 29165/** Parses the parameter of a conditional block (`if` or `else if`). */ 29166function parseConditionalBlockParameters(block, errors, bindingParser) { 29167 if (block.parameters.length === 0) { 29168 errors.push(new ParseError(block.sourceSpan, 'Conditional block does not have an expression')); 29169 return null; 29170 } 29171 const expression = parseBlockParameterToBinding(block.parameters[0], bindingParser); 29172 let expressionAlias = null; 29173 // Start from 1 since we processed the first parameter already. 29174 for (let i = 1; i < block.parameters.length; i++) { 29175 const param = block.parameters[i]; 29176 const aliasMatch = param.expression.match(CONDITIONAL_ALIAS_PATTERN); 29177 // For now conditionals can only have an `as` parameter. 29178 // We may want to rework this later if we add more. 29179 if (aliasMatch === null) { 29180 errors.push(new ParseError(param.sourceSpan, `Unrecognized conditional paramater "${param.expression}"`)); 29181 } else if (block.name !== 'if') { 29182 errors.push(new ParseError(param.sourceSpan, '"as" expression is only allowed on the primary @if block')); 29183 } else if (expressionAlias !== null) { 29184 errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression')); 29185 } else { 29186 const name = aliasMatch[2].trim(); 29187 const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length); 29188 const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length)); 29189 expressionAlias = new Variable(name, name, variableSpan, variableSpan); 29190 } 29191 } 29192 return { 29193 expression, 29194 expressionAlias 29195 }; 29196} 29197/** Strips optional parentheses around from a control from expression parameter. */ 29198function stripOptionalParentheses(param, errors) { 29199 const expression = param.expression; 29200 const spaceRegex = /^\s$/; 29201 let openParens = 0; 29202 let start = 0; 29203 let end = expression.length - 1; 29204 for (let i = 0; i < expression.length; i++) { 29205 const char = expression[i]; 29206 if (char === '(') { 29207 start = i + 1; 29208 openParens++; 29209 }
29209 else if (spaceRegex.test(char)) { 29210 continue; 29211 } else { 29212 break; 29213 } 29214 } 29215 if (openParens === 0) { 29216 return expression; 29217 } 29218 for (let i = expression.length - 1; i > -1; i--) { 29219 const char = expression[i]; 29220 if (char === ')') { 29221 end = i; 29222 openParens--; 29223 if (openParens === 0) { 29224 break; 29225 } 29226 } else if (spaceRegex.test(char)) { 29227 continue; 29228 } else { 29229 break; 29230 } 29231 } 29232 if (openParens !== 0) { 29233 errors.push(new ParseError(param.sourceSpan, 'Unclosed parentheses in expression')); 29234 return null; 29235 } 29236 return expression.slice(start, end); 29237} 29238 29239/** Pattern for a timing value in a trigger. */ 29240const TIME_PATTERN = /^\d+\.?\d*(ms|s)?$/; 29241/** Pattern for a separator between keywords in a trigger expression. */ 29242const SEPARATOR_PATTERN = /^\s$/; 29243/** Pairs of characters that form syntax that is comma-delimited. */ 29244const COMMA_DELIMITED_SYNTAX = new Map([[$LBRACE, $RBRACE], 29245// Object literals 29246[$LBRACKET, $RBRACKET], 29247// Array literals 29248[$LPAREN, $RPAREN] // Function calls 29249]); 29250/** Possible types of `on` triggers. */ 29251var OnTriggerType; 29252(function (OnTriggerType) { 29253 OnTriggerType["IDLE"] = "idle"; 29254 OnTriggerType["TIMER"] = "timer"; 29255 OnTriggerType["INTERACTION"] = "interaction"; 29256 OnTriggerType["IMMEDIATE"] = "immediate"; 29257 OnTriggerType["HOVER"] = "hover"; 29258 OnTriggerType["VIEWPORT"] = "viewport"; 29259})(OnTriggerType || (OnTriggerType = {})); 29260/** Parses a `when` deferred trigger. */ 29261function parseWhenTrigger({ 29262 expression, 29263 sourceSpan 29264}, bindingParser, triggers, errors) { 29265 const whenIndex = expression.indexOf('when'); 29266 const whenSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(whenIndex), sourceSpan.start.moveBy(whenIndex + 'when'.length)); 29267 const prefetchSpan = getPrefetchSpan(expression, sourceSpan); 29268 // This is here just to be safe, we shouldn't enter this function 29269 // in the first place if a block doesn't have the "when" keyword. 29270 if (whenIndex === -1) { 29271 errors.push(new ParseError(sourceSpan, `Could not find "when" keyword in expression`)); 29272 } else { 29273 const start = getTriggerParametersStart(expression, whenIndex + 1); 29274 const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start); 29275 trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan, prefetchSpan, whenSourceSpan)); 29276 } 29277} 29278/** Parses an `on` trigger */ 29279function parseOnTrigger({ 29280 expression, 29281 sourceSpan 29282}, triggers, errors, placeholder) { 29283 const onIndex = expression.indexOf('on'); 29284 const onSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(onIndex), sourceSpan.start.moveBy(onIndex + 'on'.length)); 29285 const prefetchSpan = getPrefetchSpan(expression, sourceSpan); 29286 // This is here just to be safe, we shouldn't enter this function 29287 // in the first place if a block doesn't have the "on" keyword. 29288 if (onIndex === -1) { 29289 errors.push(new ParseError(sourceSpan, `Could not find "on" keyword in expression`)); 29290 } else { 29291 const start = getTriggerParametersStart(expression, onIndex + 1); 29292 const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors, placeholder, prefetchSpan, onSourceSpan); 29293 parser.parse(); 29294 } 29295} 29296function getPrefetchSpan(expression, sourceSpan) { 29297 if (!expression.startsWith('prefetch')) { 29298 return null; 29299 } 29300 return new ParseSourceSpan(sourceSpan.start, sourceSpan.start.moveBy('prefetch'.length)); 29301} 29302class OnTriggerParser { 29303 constructor(expression, start, span, triggers, errors, placeholder, prefetchSpan, onSourceSpan) { 29304 this.expression = expression; 29305 this.start = start; 29306 this.span = span; 29307 this.triggers = triggers; 29308 this.errors = errors; 29309 this.placeholder = placeholder; 29310 this.prefetchSpan = prefetchSpan; 29311 this.onSourceSpan = onSourceSpan; 29312 this.index = 0; 29313 this.tokens = new Lexer().tokenize(expression.slice(start)); 29314 } 29315 parse() { 29316 while (this.tokens.length > 0 && this.index < this.tokens.length) { 29317 const token = this.token(); 29318 if (!token.isIdentifier()) { 29319 this.unexpectedToken(token); 29320 break; 29321 } 29322 // An identifier immediately followed by a comma or the end of 29323 // the expression cannot have parameters so we can exit early. 29324 if (this.isFollowedByOrLast($COMMA)) { 29325 this.consumeTrigger(token, []); 29326 this.advance(); 29327 } else if (this.isFollowedByOrLast($LPAREN)) { 29328 this.advance(); // Advance to the opening paren. 29329 const prevErrors = this.errors.length; 29330 const parameters = this.consumeParameters(); 29331 if (this.errors.length !== prevErrors) { 29332 break; 29333 } 29334 this.consumeTrigger(token, parameters); 29335 this.advance(); // Advance past the closing paren. 29336 } else if (this.index < this.tokens.length - 1) { 29337 this.unexpectedToken(this.tokens[this.index + 1]); 29338 } 29339 this.advance(); 29340 } 29341 } 29342 advance() { 29343 this.index++; 29344 } 29345 isFollowedByOrLast(char) { 29346 if (this.index === this.tokens.length - 1) { 29347 return true; 29348 } 29349 return this.tokens[this.index + 1].isCharacter(char); 29350 } 29351 token() { 29352 return this.tokens[Math.min(this.index, this.tokens.length - 1)]; 29353 } 29354 consumeTrigger(identifier, parameters) { 29355 const triggerNameStartSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index); 29356 const nameSpan = new ParseSourceSpan(triggerNameStartSpan, triggerNameStartSpan.moveBy(identifier.strValue.length)); 29357 const endSpan = triggerNameStartSpan.moveBy(this.token().end - identifier.index); 29358 // Put the prefetch and on spans with the first trigger 29359 // This should maybe be refactored to have something like an outer OnGroup AST 29360 // Since triggers can be grouped with commas "on hover(x), interaction(y)" 29361 const isFirstTrigger = identifier.index === 0; 29362 const onSourceSpan = isFirstTrigger ? this.onSourceSpan : null; 29363 const prefetchSourceSpan = isFirstTrigger ? this.prefetchSpan : null; 29364 const sourceSpan = new ParseSourceSpan(isFirstTrigger ? this.span.start : triggerNameStartSpan, endSpan); 29365 try { 29366 switch (identifier.toString()) { 29367 case OnTriggerType.IDLE: 29368 this.trackTrigger('idle', createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSourceSpan, onSourceSpan)); 29369 break; 29370 case OnTriggerType.TIMER: 29371 this.trackTrigger('timer', createTimerTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan)); 29372 break; 29373 case OnTriggerType.INTERACTION: 29374 this.trackTrigger('interaction', createInteractionTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder)); 29375 break; 29376 case OnTriggerType.IMMEDIATE: 29377 this.trackTrigger('immediate', createImmediateTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan)); 29378 break; 29379 case OnTriggerType.HOVER: 29380 this.trackTrigger('hover', createHoverTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder)); 29381 break; 29382 case OnTriggerType.VIEWPORT: 29383 this.trackTrigger('viewport', createViewportTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder)); 29384 break; 29385 default: 29386 throw new Error(`Unrecognized trigger type "${identifier}"`); 29387 } 29388 } catch (e) { 29389 this.error(identifier, e.message); 29390 } 29391 } 29392 consumeParameters() { 29393 const parameters = []; 29394 if (!this.token().isCharacter($LPAREN)) { 29395 this.unexpectedToken(this.token()); 29396 return parameters; 29397 } 29398 this.advance(); 29399 const commaDelimStack = [];
29400 let current = ''; 29401 while (this.index < this.tokens.length) { 29402 const token = this.token(); 29403 // Stop parsing if we've hit the end character and we're outside of a comma-delimited syntax. 29404 // Note that we don't need to account for strings here since the lexer already parsed them 29405 // into string tokens. 29406 if (token.isCharacter($RPAREN) && commaDelimStack.length === 0) { 29407 if (current.length) { 29408 parameters.push(current); 29409 } 29410 break; 29411 } 29412 // In the `on` microsyntax "top-level" commas (e.g. ones outside of an parameters) separate 29413 // the different triggers (e.g. `on idle,timer(500)`). This is problematic, because the 29414 // function-like syntax also implies that multiple parameters can be passed into the 29415 // individual trigger (e.g. `on foo(a, b)`). To avoid tripping up the parser with commas that 29416 // are part of other sorts of syntax (object literals, arrays), we treat anything inside 29417 // a comma-delimited syntax block as plain text. 29418 if (token.type === TokenType.Character && COMMA_DELIMITED_SYNTAX.has(token.numValue)) { 29419 commaDelimStack.push(COMMA_DELIMITED_SYNTAX.get(token.numValue)); 29420 } 29421 if (commaDelimStack.length > 0 && token.isCharacter(commaDelimStack[commaDelimStack.length - 1])) { 29422 commaDelimStack.pop(); 29423 } 29424 // If we hit a comma outside of a comma-delimited syntax, it means 29425 // that we're at the top level and we're starting a new parameter. 29426 if (commaDelimStack.length === 0 && token.isCharacter($COMMA) && current.length > 0) { 29427 parameters.push(current); 29428 current = ''; 29429 this.advance(); 29430 continue; 29431 } 29432 // Otherwise treat the token as a plain text character in the current parameter. 29433 current += this.tokenText(); 29434 this.advance(); 29435 } 29436 if (!this.token().isCharacter($RPAREN) || commaDelimStack.length > 0) { 29437 this.error(this.token(), 'Unexpected end of expression'); 29438 } 29439 if (this.index < this.tokens.length - 1 && !this.tokens[this.index + 1].isCharacter($COMMA)) { 29440 this.unexpectedToken(this.tokens[this.index + 1]); 29441 } 29442 return parameters; 29443 } 29444 tokenText() { 29445 // Tokens have a toString already which we could use, but for string tokens it omits the quotes. 29446 // Eventually we could expose this information on the token directly. 29447 return this.expression.slice(this.start + this.token().index, this.start + this.token().end); 29448 } 29449 trackTrigger(name, trigger) { 29450 trackTrigger(name, this.triggers, this.errors, trigger); 29451 } 29452 error(token, message) { 29453 const newStart = this.span.start.moveBy(this.start + token.index); 29454 const newEnd = newStart.moveBy(token.end - token.index); 29455 this.errors.push(new ParseError(new ParseSourceSpan(newStart, newEnd), message)); 29456 } 29457 unexpectedToken(token) { 29458 this.error(token, `Unexpected token "${token}"`); 29459 } 29460} 29461/** Adds a trigger to a map of triggers. */ 29462function trackTrigger(name, allTriggers, errors, trigger) { 29463 if (allTriggers[name]) { 29464 errors.push(new ParseError(trigger.sourceSpan, `Duplicate "${name}" trigger is not allowed`)); 29465 } else { 29466 allTriggers[name] = trigger; 29467 } 29468} 29469function createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 29470 if (parameters.length > 0) { 29471 throw new Error(`"${OnTriggerType.IDLE}" trigger cannot have parameters`); 29472 } 29473 return new IdleDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29474} 29475function createTimerTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 29476 if (parameters.length !== 1) { 29477 throw new Error(`"${OnTriggerType.TIMER}" trigger must have exactly one parameter`); 29478 } 29479 const delay = parseDeferredTime(parameters[0]); 29480 if (delay === null) { 29481 throw new Error(`Could not parse time value of trigger "${OnTriggerType.TIMER}"`); 29482 } 29483 return new TimerDeferredTrigger(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29484} 29485function createImmediateTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) { 29486 if (parameters.length > 0) { 29487 throw new Error(`"${OnTriggerType.IMMEDIATE}" trigger cannot have parameters`); 29488 } 29489 return new ImmediateDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29490} 29491function createHoverTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) { 29492 validateReferenceBasedTrigger(OnTriggerType.HOVER, parameters, placeholder); 29493 return new HoverDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29494} 29495function createInteractionTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) { 29496 validateReferenceBasedTrigger(OnTriggerType.INTERACTION, parameters, placeholder); 29497 return new InteractionDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29498} 29499function createViewportTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) { 29500 validateReferenceBasedTrigger(OnTriggerType.VIEWPORT, parameters, placeholder); 29501 return new ViewportDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan); 29502} 29503function validateReferenceBasedTrigger(type, parameters, placeholder) { 29504 if (parameters.length > 1) { 29505 throw new Error(`"${type}" trigger can only have zero or one parameters`); 29506 } 29507 if (parameters.length === 0) { 29508 if (placeholder === null) { 29509 throw new Error(`"${type}" trigger with no parameters can only be placed on an @defer that has a @placeholder block`); 29510 } 29511 if (placeholder.children.length !== 1 || !(placeholder.children[0] instanceof Element$1)) { 29512 throw new Error(`"${type}" trigger with no parameters can only be placed on an @defer that has a ` + `@placeholder block with exactly one root element node`); 29513 } 29514 } 29515} 29516/** Gets the index within an expression at which the trigger parameters start. */ 29517function getTriggerParametersStart(value, startPosition = 0) { 29518 let hasFoundSeparator = false;
29519 for (let i = startPosition; i < value.length; i++) { 29520 if (SEPARATOR_PATTERN.test(value[i])) { 29521 hasFoundSeparator = true; 29522 } else if (hasFoundSeparator) { 29523 return i; 29524 } 29525 } 29526 return -1; 29527} 29528/** 29529 * Parses a time expression from a deferred trigger to 29530 * milliseconds. Returns null if it cannot be parsed. 29531 */ 29532function parseDeferredTime(value) { 29533 const match = value.match(TIME_PATTERN); 29534 if (!match) { 29535 return null; 29536 } 29537 const [time, units] = match; 29538 return parseFloat(time) * (units === 's' ? 1000 : 1); 29539} 29540 29541/** Pattern to identify a `prefetch when` trigger. */ 29542const PREFETCH_WHEN_PATTERN = /^prefetch\s+when\s/; 29543/** Pattern to identify a `prefetch on` trigger. */ 29544const PREFETCH_ON_PATTERN = /^prefetch\s+on\s/; 29545/** Pattern to identify a `minimum` parameter in a block. */ 29546const MINIMUM_PARAMETER_PATTERN = /^minimum\s/; 29547/** Pattern to identify a `after` parameter in a block. */ 29548const AFTER_PARAMETER_PATTERN = /^after\s/; 29549/** Pattern to identify a `when` parameter in a block. */ 29550const WHEN_PARAMETER_PATTERN = /^when\s/; 29551/** Pattern to identify a `on` parameter in a block. */ 29552const ON_PARAMETER_PATTERN = /^on\s/; 29553/** 29554 * Predicate function that determines if a block with 29555 * a specific name cam be connected to a `defer` block. 29556 */ 29557function isConnectedDeferLoopBlock(name) { 29558 return name === 'placeholder' || name === 'loading' || name === 'error'; 29559} 29560/** Creates a deferred block from an HTML AST node. */ 29561function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) { 29562 const errors = []; 29563 const { 29564 placeholder, 29565 loading, 29566 error 29567 } = parseConnectedBlocks(connectedBlocks, errors, visitor); 29568 const { 29569 triggers, 29570 prefetchTriggers 29571 } = parsePrimaryTriggers(ast.parameters, bindingParser, errors, placeholder); 29572 // The `defer` block has a main span encompassing all of the connected branches as well. 29573 let lastEndSourceSpan = ast.endSourceSpan; 29574 let endOfLastSourceSpan = ast.sourceSpan.end; 29575 if (connectedBlocks.length > 0) { 29576 const lastConnectedBlock = connectedBlocks[connectedBlocks.length - 1]; 29577 lastEndSourceSpan = lastConnectedBlock.endSourceSpan; 29578 endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end; 29579 } 29580 const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan); 29581 const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan, ast.i18n); 29582 return { 29583 node, 29584 errors 29585 }; 29586} 29587function parseConnectedBlocks(connectedBlocks, errors, visitor) { 29588 let placeholder = null; 29589 let loading = null; 29590 let error = null; 29591 for (const block of connectedBlocks) { 29592 try { 29593 if (!isConnectedDeferLoopBlock(block.name)) { 29594 errors.push(new ParseError(block.startSourceSpan, `Unrecognized block "@${block.name}"`)); 29595 break; 29596 } 29597 switch (block.name) { 29598 case 'placeholder': 29599 if (placeholder !== null) { 29600 errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @placeholder block`)); 29601 } else { 29602 placeholder = parsePlaceholderBlock(block, visitor); 29603 } 29604 break; 29605 case 'loading': 29606 if (loading !== null) { 29607 errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @loading block`)); 29608 } else { 29609 loading = parseLoadingBlock(block, visitor); 29610 } 29611 break; 29612 case 'error': 29613 if (error !== null) { 29614 errors.push(new ParseError(block.startSourceSpan, `@defer block can only have one @error block`)); 29615 } else { 29616 error = parseErrorBlock(block, visitor); 29617 } 29618 break; 29619 } 29620 } catch (e) { 29621 errors.push(new ParseError(block.startSourceSpan, e.message)); 29622 } 29623 } 29624 return { 29625 placeholder, 29626 loading, 29627 error 29628 }; 29629} 29630function parsePlaceholderBlock(ast, visitor) { 29631 let minimumTime = null; 29632 for (const param of ast.parameters) { 29633 if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) { 29634 if (minimumTime != null) { 29635 throw new Error(`@placeholder block can only have one "minimum" parameter`); 29636 } 29637 const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression))); 29638 if (parsedTime === null) { 29639 throw new Error(`Could not parse time value of parameter "minimum"`); 29640 } 29641 minimumTime = parsedTime; 29642 } else { 29643 throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`); 29644 } 29645 } 29646 return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n); 29647} 29648function parseLoadingBlock(ast, visitor) { 29649 let afterTime = null; 29650 let minimumTime = null; 29651 for (const param of ast.parameters) { 29652 if (AFTER_PARAMETER_PATTERN.test(param.expression)) { 29653 if (afterTime != null) { 29654 throw new Error(`@loading block can only have one "after" parameter`); 29655 } 29656 const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression))); 29657 if (parsedTime === null) { 29658 throw new Error(`Could not parse time value of parameter "after"`); 29659 } 29660 afterTime = parsedTime; 29661 } else if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) { 29662 if (minimumTime != null) { 29663 throw new Error(`@loading block can only have one "minimum" parameter`); 29664 } 29665 const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression))); 29666 if (parsedTime === null) { 29667 throw new Error(`Could not parse time value of parameter "minimum"`); 29668 } 29669 minimumTime = parsedTime; 29670 } else { 29671 throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`); 29672 } 29673 } 29674 return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n); 29675} 29676function parseErrorBlock(ast, visitor) { 29677 if (ast.parameters.length > 0) { 29678 throw new Error(`@error block cannot have parameters`); 29679 } 29680 return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n); 29681} 29682function parsePrimaryTriggers(params, bindingParser, errors, placeholder) { 29683 const triggers = {}; 29684 const prefetchTriggers = {}; 29685 for (const param of params) {
29686 // The lexer ignores the leading spaces so we can assume 29687 // that the expression starts with a keyword. 29688 if (WHEN_PARAMETER_PATTERN.test(param.expression)) { 29689 parseWhenTrigger(param, bindingParser, triggers, errors); 29690 } else if (ON_PARAMETER_PATTERN.test(param.expression)) { 29691 parseOnTrigger(param, triggers, errors, placeholder); 29692 } else if (PREFETCH_WHEN_PATTERN.test(param.expression)) { 29693 parseWhenTrigger(param, bindingParser, prefetchTriggers, errors); 29694 } else if (PREFETCH_ON_PATTERN.test(param.expression)) { 29695 parseOnTrigger(param, prefetchTriggers, errors, placeholder); 29696 } else { 29697 errors.push(new ParseError(param.sourceSpan, 'Unrecognized trigger')); 29698 } 29699 } 29700 return { 29701 triggers, 29702 prefetchTriggers 29703 }; 29704} 29705const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/; 29706// Group 1 = "bind-" 29707const KW_BIND_IDX = 1; 29708// Group 2 = "let-" 29709const KW_LET_IDX = 2; 29710// Group 3 = "ref-/#" 29711const KW_REF_IDX = 3; 29712// Group 4 = "on-" 29713const KW_ON_IDX = 4; 29714// Group 5 = "bindon-" 29715const KW_BINDON_IDX = 5; 29716// Group 6 = "@" 29717const KW_AT_IDX = 6; 29718// Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@" 29719const IDENT_KW_IDX = 7; 29720const BINDING_DELIMS = { 29721 BANANA_BOX: { 29722 start: '[(', 29723 end: ')]' 29724 }, 29725 PROPERTY: { 29726 start: '[', 29727 end: ']' 29728 }, 29729 EVENT: { 29730 start: '(', 29731 end: ')' 29732 } 29733}; 29734const TEMPLATE_ATTR_PREFIX = '*'; 29735function htmlAstToRender3Ast(htmlNodes, bindingParser, options) { 29736 const transformer = new HtmlAstToIvyAst(bindingParser, options); 29737 const ivyNodes = visitAll(transformer, htmlNodes, htmlNodes); 29738 // Errors might originate in either the binding parser or the html to ivy transformer 29739 const allErrors = bindingParser.errors.concat(transformer.errors); 29740 const result = { 29741 nodes: ivyNodes, 29742 errors: allErrors, 29743 styleUrls: transformer.styleUrls, 29744 styles: transformer.styles, 29745 ngContentSelectors: transformer.ngContentSelectors 29746 }; 29747 if (options.collectCommentNodes) { 29748 result.commentNodes = transformer.commentNodes; 29749 } 29750 return result; 29751} 29752class HtmlAstToIvyAst { 29753 constructor(bindingParser, options) { 29754 this.bindingParser = bindingParser; 29755 this.options = options; 29756 this.errors = []; 29757 this.styles = []; 29758 this.styleUrls = []; 29759 this.ngContentSelectors = []; 29760 // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true 29761 this.commentNodes = []; 29762 this.inI18nBlock = false; 29763 /** 29764 * Keeps track of the nodes that have been processed already when previous nodes were visited. 29765 * These are typically blocks connected to other blocks or text nodes between connected blocks. 29766 */ 29767 this.processedNodes = new Set(); 29768 } 29769 // HTML visitor 29770 visitElement(element) { 29771 const isI18nRootElement = isI18nRootNode(element.i18n); 29772 if (isI18nRootElement) { 29773 if (this.inI18nBlock) { 29774 this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan); 29775 } 29776 this.inI18nBlock = true; 29777 } 29778 const preparsedElement = preparseElement(element); 29779 if (preparsedElement.type === PreparsedElementType.SCRIPT) { 29780 return null; 29781 } else if (preparsedElement.type === PreparsedElementType.STYLE) { 29782 const contents = textContents(element); 29783 if (contents !== null) { 29784 this.styles.push(contents); 29785 } 29786 return null; 29787 } else if (preparsedElement.type === PreparsedElementType.STYLESHEET && isStyleUrlResolvable(preparsedElement.hrefAttr)) { 29788 this.styleUrls.push(preparsedElement.hrefAttr); 29789 return null; 29790 } 29791 // Whether the element is a `<ng-template>` 29792 const isTemplateElement = isNgTemplate(element.name); 29793 const parsedProperties = []; 29794 const boundEvents = []; 29795 const variables = []; 29796 const references = []; 29797 const attributes = []; 29798 const i18nAttrsMeta = {}; 29799 const templateParsedProperties = []; 29800 const templateVariables = []; 29801 // Whether the element has any *-attribute 29802 let elementHasInlineTemplate = false; 29803 for (const attribute of element.attrs) { 29804 let hasBinding = false;
29805 const normalizedName = normalizeAttributeName(attribute.name); 29806 // `*attr` defines template bindings 29807 let isTemplateBinding = false; 29808 if (attribute.i18n) { 29809 i18nAttrsMeta[attribute.name] = attribute.i18n; 29810 } 29811 if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) { 29812 // *-attributes 29813 if (elementHasInlineTemplate) { 29814 this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan); 29815 } 29816 isTemplateBinding = true; 29817 elementHasInlineTemplate = true; 29818 const templateValue = attribute.value; 29819 const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length); 29820 const parsedVariables = []; 29821 const absoluteValueOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : 29822 // If there is no value span the attribute does not have a value, like `attr` in 29823 //`<div attr></div>`. In this case, point to one character beyond the last character of 29824 // the attribute name. 29825 attribute.sourceSpan.start.offset + attribute.name.length; 29826 this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIv
29826yAst */); 29827 templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan))); 29828 } else { 29829 // Check for variables, events, property bindings, interpolation 29830 hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references); 29831 } 29832 if (!hasBinding && !isTemplateBinding) { 29833 // don't include the bindings as attributes as well in the AST 29834 attributes.push(this.visitAttribute(attribute)); 29835 } 29836 } 29837 let children; 29838 if (preparsedElement.nonBindable) { 29839 // The `NonBindableVisitor` may need to return an array of nodes for blocks so we need 29840 // to flatten the array here. Avoid doing this for the `HtmlAstToIvyAst` since `flat` creates 29841 // a new array. 29842 children = visitAll(NON_BINDABLE_VISITOR, element.children).flat(Infinity); 29843 } else { 29844 children = visitAll(this, element.children, element.children); 29845 } 29846 let parsedElement; 29847 if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { 29848 // `<ng-content>` 29849 if (element.children && !element.children.every(node => isEmptyTextNode(node) || isCommentNode(node))) { 29850 this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan); 29851 } 29852 const selector = preparsedElement.selectAttr; 29853 const attrs = element.attrs.map(attr => this.visitAttribute(attr)); 29854 parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n); 29855 this.ngContentSelectors.push(selector); 29856 } else if (isTemplateElement) { 29857 // `<ng-template>` 29858 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta); 29859 parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [/* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); 29860 } else { 29861 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta); 29862 parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); 29863 } 29864 if (elementHasInlineTemplate) { 29865 // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
29866 // node that contains this node. 29867 // Moreover, if the node is an element, then we need to hoist its attributes to the template 29868 // node for matching against content projection selectors. 29869 const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta); 29870 const templateAttrs = []; 29871 attrs.literal.forEach(attr => templateAttrs.push(attr)); 29872 attrs.bound.forEach(attr => templateAttrs.push(attr)); 29873 const hoistedAttrs = parsedElement instanceof Element$1 ? { 29874 attributes: parsedElement.attributes, 29875 inputs: parsedElement.inputs, 29876 outputs: parsedElement.outputs 29877 } : { 29878 attributes: [], 29879 inputs: [], 29880 outputs: [] 29881 }; 29882 // For <ng-template>s with structural directives on them, avoid passing i18n information to 29883 // the wrapping template to prevent unnecessary i18n instructions from being generated. The 29884 // necessary i18n meta information will be extracted from child elements. 29885 const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n; 29886 const name = parsedElement instanceof Template ? null : parsedElement.name; 29887 parsedElement = new Template(name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [/* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n); 29888 } 29889 if (isI18nRootElement) { 29890 this.inI18nBlock = false; 29891 } 29892 return parsedElement; 29893 } 29894 visitAttribute(attribute) { 29895 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n); 29896 } 29897 visitText(text) { 29898 return this.processedNodes.has(text) ? null : this._visitTextWithInterpolation(text.value, text.sourceSpan, text.tokens, text.i18n); 29899 } 29900 visitExpansion(expansion) { 29901 if (!expansion.i18n) { 29902 // do not generate Icu in case it was created 29903 // outside of i18n block in a template 29904 return null; 29905 } 29906 if (!isI18nRootNode(expansion.i18n)) { 29907 throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`); 29908 } 29909 const message = expansion.i18n; 29910 const vars = {}; 29911 const placeholders = {}; 29912 // extract VARs from ICUs - we process them separately while 29913 // assembling resulting message via goog.getMsg function, since 29914 // we need to pass them to top-level goog.getMsg call 29915 Object.keys(message.placeholders).forEach(key => { 29916 const value = message.placeholders[key]; 29917 if (key.startsWith(I18N_ICU_VAR_PREFIX)) { 29918 // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g. 29919 // `{count, select , ...}`), these spaces are also included into the key names in ICU vars 29920 // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be 29921 // converted into `_` symbols while normalizing placeholder names, which might lead to 29922 // mismatches at runtime (i.e. placeholder will not be replaced with the correct value). 29923 const formattedKey = key.trim(); 29924 const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan); 29925 vars[formattedKey] = new BoundText(ast, value.sourceSpan); 29926 } else { 29927 placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan, null); 29928 } 29929 }); 29930 return new Icu$1(vars, placeholders, expansion.sourceSpan, message); 29931 } 29932 visitExpansionCase(expansionCase) { 29933 return null; 29934 } 29935 visitComment(comment) { 29936 if (this.options.collectCommentNodes) { 29937 this.commentNodes.push(new Comment$1(comment.value || '', comment.sourceSpan)); 29938 } 29939 return null; 29940 } 29941 visitBlockParameter() { 29942 return null; 29943 } 29944 visitBlock(block, context) { 29945 const index = Array.isArray(context) ? context.indexOf(block) : -1; 29946 if (index === -1) { 29947 throw new Error('Visitor invoked incorrectly. Expecting visitBlock to be invoked siblings array as its context'); 29948 } 29949 // Connected blocks may have been processed as a part of the previous block. 29950 if (this.processedNodes.has(block)) { 29951 return null; 29952 } 29953 let result = null; 29954 switch (block.name) { 29955 case 'defer': 29956 result = createDeferredBlock(block, this.findConnectedBlocks(index, context, isConnectedDeferLoopBlock), this, this.bindingParser); 29957 break; 29958 case 'switch': 29959 result = createSwitchBlock(block, this, this.bindingParser); 29960 break; 29961 case 'for': 29962 result = createForLoop(block, this.findConnectedBlocks(index, context, isConnectedForLoopBlock), this, this.bindingParser); 29963 break; 29964 case 'if': 29965 result = createIfBlock(block, this.findConnectedBlocks(index, context, isConnectedIfLoopBlock), this, this.bindingParser); 29966 break; 29967 default: 29968 let errorMessage; 29969 if (isConnectedDeferLoopBlock(block.name)) { 29970 errorMessage = `@${block.name} block can only be used after an @defer block.`; 29971 this.processedNodes.add(block); 29972 } else if (isConnectedForLoopBlock(block.name)) { 29973 errorMessage = `@${block.name} block can only be used after an @for block.`; 29974 this.processedNodes.add(block); 29975 } else if (isConnectedIfLoopBlock(block.name)) { 29976 errorMessage = `@${block.name} block can only be used after an @if or @else if block.`; 29977 this.processedNodes.add(block); 29978 } else { 29979 errorMessage = `Unrecognized block @${block.name}.`; 29980 } 29981 result = { 29982 node: new UnknownBlock(block.name, block.sourceSpan, block.nameSpan), 29983 errors: [new ParseError(block.sourceSpan, errorMessage)] 29984 }; 29985 break; 29986 } 29987 this.errors.push(...result.errors); 29988 return result.node; 29989 } 29990 findConnectedBlocks(primaryBlockIndex, siblings, predicate) { 29991 const relatedBlocks = []; 29992 for (let i = primaryBlockIndex + 1; i < siblings.length; i++) { 29993 const node = siblings[i]; 29994 // Ignore empty text nodes between blocks. 29995 if (node instanceof Text && node.value.trim().length === 0) { 29996 // Add the text node to the processed nodes since we don't want 29997 // it to be generated between the connected nodes. 29998 this.processedNodes.add(node); 29999 continue; 30000 } 30001 // Stop searching as soon as we hit a non-block node or a block that is unrelated. 30002 if (!(node instanceof Block) || !predicate(node.name)) { 30003 break; 30004 } 30005 relatedBlocks.push(node); 30006 this.processedNodes.add(node); 30007 } 30008 return relatedBlocks; 30009 } 30010 // convert view engine `ParsedProperty` to a format suitable for IVY 30011 extractAttributes(elementName, properties, i18nPropsMeta) { 30012 const bound = []; 30013 const literal = [];
30014 properties.forEach(prop => { 30015 const i18n = i18nPropsMeta[prop.name]; 30016 if (prop.isLiteral) { 30017 literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n)); 30018 } else { 30019 // Note that validation is skipped and property mapping is disabled 30020 // due to the fact that we need to make sure a given prop is not an 30021 // input of a directive and directive matching happens at runtime. 30022 const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */true, /* mapPropertyName */false); 30023 bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n)); 30024 } 30025 }); 30026 return { 30027 bound, 30028 literal 30029 }; 30030 } 30031 parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) { 30032 const name = normalizeAttributeName(attribute.name); 30033 const value = attribute.value; 30034 const srcSpan = attribute.sourceSpan; 30035 const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset; 30036 function createKeySpan(srcSpan, prefix, identifier) { 30037 // We need to adjust the start location for the keySpan to account for the removed 'data-' 30038 // prefix from `normalizeAttributeName`. 30039 const normalizationAdjustment = attribute.name.length - name.length; 30040 const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment); 30041 const keySpanEnd = keySpanStart.moveBy(identifier.length); 30042 return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier); 30043 }
30044 const bindParts = name.match(BIND_NAME_REGEXP); 30045 if (bindParts) { 30046 if (bindParts[KW_BIND_IDX] != null) { 30047 const identifier = bindParts[IDENT_KW_IDX]; 30048 const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier); 30049 this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); 30050 } else if (bindParts[KW_LET_IDX]) { 30051 if (isTemplateElement) { 30052 const identifier = bindParts[IDENT_KW_IDX]; 30053 const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier); 30054 this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables); 30055 } else { 30056 this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan); 30057 } 30058 } else if (bindParts[KW_REF_IDX]) { 30059 const identifier = bindParts[IDENT_KW_IDX]; 30060 const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier); 30061 this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references); 30062 } else if (bindParts[KW_ON_IDX]) { 30063 const events = []; 30064 const identifier = bindParts[IDENT_KW_IDX]; 30065 const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier); 30066 this.bindingParser.parseEvent(identifier, value, /* isAssignmentEvent */false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan); 30067 addEvents(events, boundEvents); 30068 } else if (bindParts[KW_BINDON_IDX]) { 30069 const identifier = bindParts[IDENT_KW_IDX]; 30070 const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier); 30071 this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); 30072 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan); 30073 } else if (bindParts[KW_AT_IDX]) { 30074 const keySpan = createKeySpan(srcSpan, '', name); 30075 this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); 30076 } 30077 return true; 30078 } 30079 // We didn't see a kw-prefixed property binding, but we have not yet checked 30080 // for the []/()/[()] syntax. 30081 let delims = null; 30082 if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) { 30083 delims = BINDING_DELIMS.BANANA_BOX; 30084 } else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) { 30085 delims = BINDING_DELIMS.PROPERTY; 30086 } else if (name.startsWith(BINDING_DELIMS.EVENT.start)) { 30087 delims = BINDING_DELIMS.EVENT; 30088 } 30089 if (delims !== null && 30090 // NOTE: older versions of the parser would match a start/end delimited 30091 // binding iff the property name was terminated by the ending delimiter 30092 // and the identifier in the binding was non-empty. 30093 // TODO(ayazhafiz): update this to handle malformed bindings. 30094 name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) { 30095 const identifier = name.substring(delims.start.length, name.length - delims.end.length); 30096 const keySpan = createKeySpan(srcSpan, delims.start, identifier); 30097 if (delims.start === BINDING_DELIMS.BANANA_BOX.start) { 30098 this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); 30099 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan); 30100 } else if (delims.start === BINDING_DELIMS.PROPERTY.start) { 30101 this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); 30102 } else { 30103 const events = []; 30104 this.bindingParser.parseEvent(identifier, value, /* isAssignmentEvent */false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan); 30105 addEvents(events, boundEvents); 30106 } 30107 return true; 30108 } 30109 // No explicit binding found.
30110 const keySpan = createKeySpan(srcSpan, '' /* prefix */, name); 30111 const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan, attribute.valueTokens ?? null); 30112 return hasBinding; 30113 } 30114 _visitTextWithInterpolation(value, sourceSpan, interpolatedTokens, i18n) { 30115 const valueNoNgsp = replaceNgsp(value); 30116 const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan, interpolatedTokens); 30117 return expr ? new BoundText(expr, sourceSpan, i18n) : new Text$3(valueNoNgsp, sourceSpan); 30118 } 30119 parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) { 30120 if (identifier.indexOf('-') > -1) { 30121 this.reportError(`"-" is not allowed in variable names`, sourceSpan); 30122 } else if (identifier.length === 0) { 30123 this.reportError(`Variable does not have a name`, sourceSpan); 30124 } 30125 variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan)); 30126 } 30127 parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) { 30128 if (identifier.indexOf('-') > -1) { 30129 this.reportError(`"-" is not allowed in reference names`, sourceSpan); 30130 } else if (identifier.length === 0) { 30131 this.reportError(`Reference does not have a name`, sourceSpan); 30132 } else if (references.some(reference => reference.name === identifier)) { 30133 this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan); 30134 } 30135 references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan)); 30136 } 30137 parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) { 30138 const events = []; 30139 this.bindingParser.parseEvent(`${name}Change`, expression, /* isAssignmentEvent */true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan); 30140 addEvents(events, boundEvents); 30141 } 30142 reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) { 30143 this.errors.push(new ParseError(sourceSpan, message, level)); 30144 } 30145} 30146class NonBindableVisitor { 30147 visitElement(ast) { 30148 const preparsedElement = preparseElement(ast); 30149 if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE || preparsedElement.type === PreparsedElementType.STYLESHEET) { 30150 // Skipping <script> for security reasons 30151 // Skipping <style> and stylesheets as we already processed them 30152 // in the StyleCompiler 30153 return null; 30154 } 30155 const children = visitAll(this, ast.children, null); 30156 return new Element$1(ast.name, visitAll(this, ast.attrs), /* inputs */[], /* outputs */[], children, /* references */[], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan); 30157 } 30158 visitComment(comment) { 30159 return null; 30160 } 30161 visitAttribute(attribute) { 30162 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n); 30163 } 30164 visitText(text) { 30165 return new Text$3(text.value, text.sourceSpan); 30166 } 30167 visitExpansion(expansion) { 30168 return null; 30169 } 30170 visitExpansionCase(expansionCase) { 30171 return null; 30172 } 30173 visitBlock(block, context) { 30174 const nodes = [ 30175 // In an ngNonBindable context we treat the opening/closing tags of block as plain text. 30176 // This is the as if the `tokenizeBlocks` option was disabled. 30177 new Text$3(block.startSourceSpan.toString(), block.startSourceSpan), ...visitAll(this, block.children)]; 30178 if (block.endSourceSpan !== null) { 30179 nodes.push(new Text$3(block.endSourceSpan.toString(), block.endSourceSpan)); 30180 } 30181 return nodes; 30182 } 30183 visitBlockParameter(parameter, context) { 30184 return null; 30185 } 30186} 30187const NON_BINDABLE_VISITOR = new NonBindableVisitor(); 30188function normalizeAttributeName(attrName) { 30189 return /^data-/i.test(attrName) ? attrName.substring(5) : attrName; 30190} 30191function addEvents(events, boundEvents) { 30192 boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e))); 30193} 30194function isEmptyTextNode(node) { 30195 return node instanceof Text && node.value.trim().length == 0; 30196} 30197function isCommentNode(node) { 30198 return node instanceof Comment; 30199} 30200function textContents(node) { 30201 if (node.children.length !== 1 || !(node.children[0] instanceof Text)) { 30202 return null; 30203 } else { 30204 return node.children[0].value; 30205 } 30206} 30207var TagType; 30208(function (TagType) { 30209 TagType[TagType["ELEMENT"] = 0] = "ELEMENT"; 30210 TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE"; 30211})(TagType || (TagType = {})); 30212/** 30213 * Generates an object that is used as a shared state between parent and all child contexts. 30214 */ 30215function setupRegistry() { 30216 return { 30217 getUniqueId: getSeqNumberGenerator(), 30218 icus: new Map() 30219 }; 30220} 30221/** 30222 * I18nContext is a helper class which keeps track of all i18n-related as
30222pects 30223 * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions. 30224 * 30225 * When we enter a nested template, the top-level context is being passed down 30226 * to the nested component, which uses this context to generate a child instance 30227 * of I18nContext class (to handle nested template) and at the end, reconciles it back 30228 * with the parent context. 30229 * 30230 * @param index Instruction index of i18nStart, which initiates this context 30231 * @param ref Reference to a translation const that represents the content if thus context 30232 * @param level Nesting level defined for child contexts 30233 * @param templateIndex Instruction index of a template which this context belongs to 30234 * @param meta Meta information (id, meaning, description, etc) associated with this context 30235 */ 30236class I18nContext { 30237 constructor(index, ref, level = 0, templateIndex = null, meta, registry) { 30238 this.index = index; 30239 this.ref = ref; 30240 this.level = level; 30241 this.templateIndex = templateIndex; 30242 this.meta = meta; 30243 this.registry = registry; 30244 this.bindings = new Set(); 30245 this.placeholders = new Map(); 30246 this.isEmitted = false; 30247 this._unresolvedCtxCount = 0; 30248 this._registry = registry || setupRegistry(); 30249 this.id = this._registry.getUniqueId(); 30250 } 30251 appendTag(type, node, index, closed) { 30252 if (node.isVoid && closed) { 30253 return; // ignore "close" for void tags 30254 } 30255 const ph = node.isVoid || !closed ? node.startName : node.closeName; 30256 const content = { 30257 type, 30258 index, 30259 ctx: this.id, 30260 isVoid: node.isVoid, 30261 closed 30262 }; 30263 updatePlaceholderMap(this.placeholders, ph, content); 30264 } 30265 appendBlockPart(node, index, closed) { 30266 const ph = closed ? node.closeName : node.startName; 30267 const content = { 30268 type: TagType.TEMPLATE, 30269 index, 30270 ctx: this.id, 30271 closed 30272 }; 30273 updatePlaceholderMap(this.placeholders, ph, content); 30274 } 30275 get icus() { 30276 return this._registry.icus; 30277 } 30278 get isRoot() { 30279 return this.level === 0; 30280 } 30281 get isResolved() { 30282 return this._unresolvedCtxCount === 0; 30283 } 30284 getSerializedPlaceholders() { 30285 const result = new Map(); 30286 this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue))); 30287 return result; 30288 } 30289 // public API to accumulate i18n-related content 30290 appendBinding(binding) { 30291 this.bindings.add(binding); 30292 } 30293 appendIcu(name, ref) { 30294 updatePlaceholderMap(this._registry.icus, name, ref); 30295 } 30296 appendBoundText(node) { 30297 const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id); 30298 phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values)); 30299 } 30300 appendTemplate(node, index) { 30301 // add open and close tags at the same time, 30302 // since we process nested templates separately 30303 this.appendTag(TagType.TEMPLATE, node, index, false); 30304 this.appendTag(TagType.TEMPLATE, node, index, true); 30305 this._unresolvedCtxCount++; 30306 } 30307 appendBlock(node, index) { 30308 // add open and close tags at the same time, 30309 // since we process nested templates separately 30310 this.appendBlockPart(node, index, false); 30311 this.appendBlockPart(node, index, true); 30312 this._unresolvedCtxCount++; 30313 } 30314 appendElement(node, index, closed) { 30315 this.appendTag(TagType.ELEMENT, node, index, closed); 30316 } 30317 appendProjection(node, index) { 30318 // Add open and close tags at the same time, since `<ng-content>` has no content, 30319 // so when we come across `<ng-content>` we can register both open and close tags. 30320 // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and 30321 // regular element tag placeholders, so we generate element placeholders for both types. 30322 this.appendTag(TagType.ELEMENT, node, index, false); 30323 this.appendTag(TagType.ELEMENT, node, index, true); 30324 } 30325 /** 30326 * Generates an instance of a child context based on the root one, 30327 * when we enter a nested template within I18n section. 30328 * 30329 * @param index Instruction index of corresponding i18nStart, which initiates this context 30330 * @param templateIndex Instruction index of a template which this context belongs to 30331 * @param meta Meta information (id, meaning, description, etc) associated with this context 30332 * 30333 * @returns I18nContext instance 30334 */ 30335 forkChildContext(index, templateIndex, meta) { 30336 return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry); 30337 } 30338 /** 30339 * Reconciles child context into parent one once the end of the i18n block
30339is reached (i18nEnd). 30340 * 30341 * @param context Child I18nContext instance to be reconciled with parent context. 30342 */ 30343 reconcileChildContext(context) { 30344 // set the right context id for open and close 30345 // template tags, so we can use it as sub-block ids 30346 ['start', 'close'].forEach(op => { 30347 const key = context.meta[`${op}Name`]; 30348 const phs = this.placeholders.get(key) || []; 30349 const tag = phs.find(findTemplateFn(this.id, context.templateIndex)); 30350 if (tag) { 30351 tag.ctx = context.id; 30352 } 30353 }); 30354 // reconcile placeholders 30355 const childPhs = context.placeholders; 30356 childPhs.forEach((values, key) => { 30357 const phs = this.placeholders.get(key); 30358 if (!phs) { 30359 this.placeholders.set(key, values); 30360 return; 30361 } 30362 // try to find matching template... 30363 const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex)); 30364 if (tmplIdx >= 0) { 30365 // ... if found - replace it with nested template content 30366 const isCloseTag = key.startsWith('CLOSE'); 30367 const isTemplateTag = key.endsWith('NG-TEMPLATE'); 30368 if (isTemplateTag) { 30369 // current template's content is placed before or after 30370 // parent template tag, depending on the open/close attribute 30371 phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values); 30372 } else { 30373 const idx = isCloseTag ? values.length - 1 : 0; 30374 values[idx].tmpl = phs[tmplIdx]; 30375 phs.splice(tmplIdx, 1, ...values); 30376 } 30377 } else { 30378 // ... otherwise just append content to placeholder value 30379 phs.push(...values); 30380 } 30381 this.placeholders.set(key, phs); 30382 }); 30383 this._unresolvedCtxCount--; 30384 } 30385} 30386// 30387// Helper methods 30388// 30389function wrap(symbol, index, contextId, closed) { 30390 const state = closed ? '/' : ''; 30391 return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId); 30392} 30393function wrapTag(symbol, { 30394 index, 30395 ctx, 30396 isVoid 30397}, closed) { 30398 return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) : wrap(symbol, index, ctx, closed); 30399} 30400function findTemplateFn(ctx, templateIndex) { 30401 return token => typeof token === 'object' && token.type === TagType.TEMPLATE && token.index === templateIndex && token.ctx === ctx; 30402} 30403function serializePlaceholderValue(value) { 30404 const element = (data, closed) => wrapTag('#', data, closed); 30405 const template = (data, closed) => wrapTag('*', data, closed); 30406 switch (value.type) { 30407 case TagType.ELEMENT: 30408 // close element tag 30409 if (value.closed) { 30410 return element(value, true) + (value.tmpl ? template(value.tmpl, true) : ''); 30411 } 30412 // open element tag that also initiates a template 30413 if (value.tmpl) { 30414 return template(value.tmpl) + element(value) + (value.isVoid ? template(value.tmpl, true) : ''); 30415 } 30416 return element(value); 30417 case TagType.TEMPLATE: 30418 return template(value, value.closed); 30419 default: 30420 return value; 30421 } 30422} 30423const IMPORTANT_FLAG = '!important'; 30424/** 30425 * Minimum amount of binding slots required in the runtime for style/class bindings. 30426 * 30427 * Styling in Angular uses up two slots in the runtime LView/TData data structures to 30428 * record binding data, property information and metadata. 30429 * 30430 * When a binding is registered it will place the following information in the `LView`: 30431 * 30432 * slot 1) binding value 30433 * slot 2) cached value (all other values collected before it in string form) 30434 * 30435 * When a binding is registered it will place the following information in the `TData`: 30436 * 30437 * slot 1) prop name 30438 * slot 2) binding index that points to the previous style/class binding (and some extra config 30439 * values) 30440 * 30441 * Let's imagine we have a binding that looks like so: 30442 * 30443 * ``` 30444 * <div [style.width]="x" [style.height]="y"> 30445 * ``` 30446 * 30447 * Our `LView` and `TData` data-structures look like so: 30448 * 30449 * ```typescript 30450 * LView = [ 30451 * // ... 30452 * x, // value of x 30453 * "width: x", 30454 * 30455 * y, // value of y 30456 * "width: x; height: y", 30457 * // ... 30458 * ]; 30459 * 30460 * TData = [ 30461 * // ... 30462 * "width", // binding slot 20 30463 * 0, 30464 * 30465 * "height", 30466 * 20, 30467 * // ... 30468 * ]; 30469 * ``` 30470 * 30471 * */ 30472const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2; 30473/** 30474 * Produces creation/update instructions for all styling bindings (class and style) 30475 * 30476 * It also produces the creation instruction to register all initial styling values 30477 * (which are all the static class="..." and style="..." attribute values that exist 30478 * on an element within a template). 30479 * 30480 * The builder class below handles producing instructions for the following cases: 30481 * 30482 * - Static style/class attributes (style="..." and class="...") 30483 * - Dynamic style/class map bindings ([style]="map" and [class]="map|string") 30484 * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp") 30485 * 30486 * Due to the complex relationship of all of these cases, the instructions generated 30487 * for these attributes/properties/bindings must be done so in the correct order. The 30488 * order which these must be generated is as follows: 30489 * 30490 * if (createMode) { 30491 * styling(...) 30492 * } 30493 * if (updateMode) { 30494 * styleMap(...) 30495 * classMap(...) 30496 * styleProp(...) 30497 * classProp(...) 30498 * } 30499 * 30500 * The creation/update methods within the builder class produce these instructions. 30501 */ 30502class StylingBuilder { 30503 constructor(_directiveExpr) { 30504 this._directiveExpr = _directiveExpr; 30505 /** Whether or not there are any static styling values present */ 30506 this._hasInitialValues = false;
30507 /** 30508 * Whether or not there are any styling bindings present 30509 * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`) 30510 */ 30511 this.hasBindings = false; 30512 this.hasBindingsWithPipes = false; 30513 /** the input for [class] (if it exists) */ 30514 this._classMapInput = null; 30515 /** the input for [style] (if it exists) */ 30516 this._styleMapInput = null; 30517 /** an array of each [style.prop] input */ 30518 this._singleStyleInputs = null; 30519 /** an array of each [class.name] input */ 30520 this._singleClassInputs = null; 30521 this._lastStylingInput = null; 30522 this._firstStylingInput = null; 30523 // maps are used instead of hash maps because a Map will 30524 // retain the ordering of the keys 30525 /** 30526 * Represents the location of each style binding in the template 30527 * (e.g. `<div [style.width]="w" [style.height]="h">` implies 30528 * that `width=0` and `height=1`) 30529 */ 30530 this._stylesIndex = new Map(); 30531 /** 30532 * Represents the location of each class binding in the template 30533 * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies 30534 * that `big=0` and `hidden=1`) 30535 */ 30536 this._classesIndex = new Map(); 30537 this._initialStyleValues = []; 30538 this._initialClassValues = []; 30539 } 30540 /** 30541 * Registers a given input to the styling builder to be later used when producing AOT code. 30542 * 30543 * The code below will only accept the input if it is somehow tied to styling (whether it be 30544 * style/class bindings or static style/class attributes). 30545 */ 30546 registerBoundInput(input) { 30547 // [attr.style] or [attr.class] are skipped in the code below, 30548 // they should not be treated as styling-based bindings since 30549 // they are intended to be written directly to the attr and 30550 // will therefore skip all style/class resolution that is present 30551 // with style="", [style]="" and [style.prop]="", class="", 30552 // [class.prop]="". [class]="" assignments 30553 let binding = null; 30554 let name = input.name; 30555 switch (input.type) { 30556 case 0 /* BindingType.Property */: 30557 binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan); 30558 break; 30559 case 3 /* BindingType.Style */: 30560 binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit); 30561 break; 30562 case 2 /* BindingType.Class */: 30563 binding = this.registerClassInput(name, false, input.value, input.sourceSpan); 30564 break; 30565 } 30566 return binding ? true : false; 30567 } 30568 registerInputBasedOnName(name, expression, sourceSpan) { 30569 let binding = null; 30570 const prefix = name.substring(0, 6); 30571 const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!'; 30572 const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!'); 30573 if (isStyle || isClass) { 30574 const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no 30575 const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1 30576 if (isStyle) { 30577 binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan); 30578 } else { 30579 binding = this.registerClassInput(property, isMapBased, expression, sourceSpan); 30580 } 30581 } 30582 return binding; 30583 } 30584 registerStyleInput(name, isMapBased, value, sourceSpan, suffix) { 30585 if (isEmptyExpression(value)) { 30586 return null; 30587 } 30588 // CSS custom properties are case-sensitive so we shouldn't normalize them. 30589 // See: https://www.w3.org/TR/css-variables-1/#defining-variables 30590 if (!isCssCustomProperty(name)) { 30591 name = hyphenate(name); 30592 } 30593 const { 30594 property, 30595 hasOverrideFlag, 30596 suffix: bindingSuffix 30597 } = parseProperty(name); 30598 suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix; 30599 const entry = { 30600 name: property, 30601 suffix: suffix, 30602 value, 30603 sourceSpan, 30604 hasOverrideFlag 30605 }; 30606 if (isMapBased) { 30607 this._styleMapInput = entry; 30608 } else { 30609 (this._singleStyleInputs = this._singleStyleInputs || []).push(entry); 30610 registerIntoMap(this._stylesIndex, property); 30611 } 30612 this._lastStylingInput = entry; 30613 this._firstStylingInput = this._firstStylingInput || entry; 30614 this._checkForPipes(value); 30615 this.hasBindings = true; 30616 return entry; 30617 } 30618 registerClassInput(name, isMapBased, value, sourceSpan) { 30619 if (isEmptyExpression(value)) { 30620 return null; 30621 } 30622 const { 30623 property, 30624 hasOverrideFlag 30625 } = parseProperty(name); 30626 const entry = { 30627 name: property, 30628 value, 30629 sourceSpan, 30630 hasOverrideFlag, 30631 suffix: null 30632 }; 30633 if (isMapBased) { 30634 this._classMapInput = entry; 30635 } else { 30636 (this._singleClassInputs = this._singleClassInputs || []).push(entry); 30637 registerIntoMap(this._classesIndex, property); 30638 } 30639 this._lastStylingInput = entry; 30640 this._firstStylingInput = this._firstStylingInput || entry; 30641 this._checkForPipes(value); 30642 this.hasBindings = true; 30643 return entry; 30644 } 30645 _checkForPipes(value) { 30646 if (value instanceof ASTWithSource && value.ast instanceof BindingPipe) { 30647 this.hasBindingsWithPipes = true; 30648 } 30649 } 30650 /** 30651 * Registers the element's static style string value to the builder. 30652 * 30653 * @param value the style string (e.g. `width:100px; height:200px;`) 30654 */ 30655 registerStyleAttr(value) { 30656 this._initialStyleValues = parse(value); 30657 this._hasInitialValues = true; 30658 } 30659 /** 30660 * Registers the element's static class string value to the builder. 30661 * 30662 * @param value the className string (e.g. `disabled gold zoom`) 30663 */ 30664 registerClassAttr(value) { 30665 this._initialClassValues = value.trim().split(/\s+/g); 30666 this._hasInitialValues = true; 30667 } 30668 /** 30669 * Appends all styling-related expressions to the provided attrs array. 30670 * 30671 * @param attrs an existing array where each of the styling expressions 30672 * will be inserted into. 30673 */ 30674 populateInitialStylingAttrs(attrs) { 30675 // [CLASS_MARKER, 'foo', 'bar', 'baz' ...] 30676 if (this._initialClassValues.length) { 30677 attrs.push(literal(1 /* AttributeMarker.Classes */)); 30678 for (let i = 0; i < this._initialClassValues.length; i++) { 30679 attrs.push(literal(this._initialClassValues[i])); 30680 } 30681 } 30682 // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...] 30683 if (this._initialStyleValues.length) {
30684 attrs.push(literal(2 /* AttributeMarker.Styles */)); 30685 for (let i = 0; i < this._initialStyleValues.length; i += 2) { 30686 attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1])); 30687 } 30688 } 30689 } 30690 /** 30691 * Builds an instruction with all the expressions and parameters for `elementHostAttrs`. 30692 * 30693 * The instruction generation code below is used for producing the AOT statement code which is 30694 * responsible for registering initial styles (within a directive hostBindings' creation block), 30695 * as well as any of the provided attribute values, to the directive host element. 30696 */ 30697 assignHostAttrs(attrs, definitionMap) { 30698 if (this._directiveExpr && (attrs.length || this._hasInitialValues)) { 30699 this.populateInitialStylingAttrs(attrs); 30700 definitionMap.set('hostAttrs', literalArr(attrs)); 30701 } 30702 } 30703 /** 30704 * Builds an instruction with all the expressions and parameters for `classMap`. 30705 * 30706 * The instruction data will contain all expressions for `classMap` to function 30707 * which includes the `[class]` expression params. 30708 */ 30709 buildClassMapInstruction(valueConverter) { 30710 if (this._classMapInput) { 30711 return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput); 30712 } 30713 return null; 30714 } 30715 /** 30716 * Builds an instruction with all the expressions and parameters for `styleMap`. 30717 * 30718 * The instruction data will contain all expressions for `styleMap` to function 30719 * which includes the `[style]` expression params. 30720 */ 30721 buildStyleMapInstruction(valueConverter) { 30722 if (this._styleMapInput) { 30723 return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput); 30724 } 30725 return null; 30726 } 30727 _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) { 30728 // each styling binding value is stored in the LView 30729 // map-based bindings allocate two slots: one for the 30730 // previous binding value and another for the previous 30731 // className or style attribute value. 30732 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED; 30733 // these values must be outside of the update block so that they can 30734 // be evaluated (the AST visit call) during creation time so that any 30735 // pipes can be picked up in time before the template is built 30736 const mapValue = stylingInput.value.visit(valueConverter); 30737 let reference; 30738 if (mapValue instanceof Interpolation$1) { 30739 totalBindingSlotsRequired += mapValue.expressions.length; 30740 reference = isClassBased ? getClassMapInterpolationExpression(mapValue) : getStyleMapInterpolationExpression(mapValue); 30741 } else { 30742 reference = isClassBased ? Identifiers.classMap : Identifiers.styleMap; 30743 } 30744 return { 30745 reference, 30746 calls: [{ 30747 supportsInterpolation: true, 30748 sourceSpan: stylingInput.sourceSpan, 30749 allocateBindingSlots: totalBindingSlotsRequired, 30750 params: convertFn => { 30751 const convertResult = convertFn(mapValue); 30752 const params = Array.isArray(convertResult) ? convertResult : [convertResult]; 30753 return params; 30754 } 30755 }] 30756 }; 30757 } 30758 _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) { 30759 const instructions = []; 30760 inputs.forEach(input => { 30761 const previousInstruction = instructions[instructions.length - 1]; 30762 const value = input.value.visit(valueConverter); 30763 let referenceForCall = reference; 30764 // each styling binding value is stored in the LView 30765 // but there are two values stored for each binding: 30766 // 1) the value itself 30767 // 2) an intermediate value (concatenation of style up to this point). 30768 // We need to store the intermediate value so that we don't allocate 30769 // the strings on each CD. 30770 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED; 30771 if (value instanceof Interpolation$1) { 30772 totalBindingSlotsRequired += value.expressions.length; 30773 if (getInterpolationExpressionFn) { 30774 referenceForCall = getInterpolationExpressionFn(value); 30775 } 30776 } 30777 const call = {
30778 sourceSpan: input.sourceSpan, 30779 allocateBindingSlots: totalBindingSlotsRequired, 30780 supportsInterpolation: !!getInterpolationExpressionFn, 30781 params: convertFn => { 30782 // params => stylingProp(propName, value, suffix) 30783 const params = []; 30784 params.push(literal(input.name)); 30785 const convertResult = convertFn(value); 30786 if (Array.isArray(convertResult)) { 30787 params.push(...convertResult); 30788 } else { 30789 params.push(convertResult); 30790 } 30791 // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore, 30792 // if that is detected then we need to pass that in as an optional param. 30793 if (!isClassBased && input.suffix !== null) { 30794 params.push(literal(input.suffix)); 30795 } 30796 return params; 30797 } 30798 }; 30799 // If we ended up generating a call to the same instruction as the previous styling property 30800 // we can chain the calls together safely to save some bytes, otherwise we have to generate 30801 // a separate instruction call. This is primarily a concern with interpolation instructions 30802 // where we may start off with one `reference`, but end up using another based on the 30803 // number of interpolations. 30804 if (previousInstruction && previousInstruction.reference === referenceForCall) { 30805 previousInstruction.calls.push(call); 30806 } else { 30807 instructions.push({ 30808 reference: referenceForCall, 30809 calls: [call] 30810 }); 30811 } 30812 }); 30813 return instructions; 30814 } 30815 _buildClassInputs(valueConverter) { 30816 if (this._singleClassInputs) { 30817 return this._buildSingleInputs(Identifiers.classProp, this._singleClassInputs, valueConverter, null, true); 30818 } 30819 return []; 30820 } 30821 _buildStyleInputs(valueConverter) { 30822 if (this._singleStyleInputs) { 30823 return this._buildSingleInputs(Identifiers.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false); 30824 } 30825 return []; 30826 } 30827 /** 30828 * Constructs all instructions which contain the expressions that will be placed 30829 * into the update block of a template function or a directive hostBindings function. 30830 */ 30831 buildUpdateLevelInstructions(valueConverter) { 30832 const instructions = []; 30833 if (this.hasBindings) { 30834 const styleMapInstruction = this.buildStyleMapInstruction(valueConverter); 30835 if (styleMapInstruction) { 30836 instructions.push(styleMapInstruction); 30837 } 30838 const classMapInstruction = this.buildClassMapInstruction(valueConverter); 30839 if (classMapInstruction) { 30840 instructions.push(classMapInstruction); 30841 } 30842 instructions.push(...this._buildStyleInputs(valueConverter)); 30843 instructions.push(...this._buildClassInputs(valueConverter)); 30844 } 30845 return instructions; 30846 } 30847} 30848function registerIntoMap(map, key) { 30849 if (!map.has(key)) { 30850 map.set(key, map.size); 30851 } 30852} 30853function parseProperty(name) { 30854 let hasOverrideFlag = false; 30855 const overrideIndex = name.indexOf(IMPORTANT_FLAG); 30856 if (overrideIndex !== -1) { 30857 name = overrideIndex > 0 ? name.substring(0, overrideIndex) : ''; 30858 hasOverrideFlag = true; 30859 } 30860 let suffix = null; 30861 let property = name; 30862 const unitIndex = name.lastIndexOf('.'); 30863 if (unitIndex > 0) { 30864 suffix = name.slice(unitIndex + 1); 30865 property = name.substring(0, unitIndex); 30866 } 30867 return { 30868 property, 30869 suffix, 30870 hasOverrideFlag 30871 }; 30872} 30873/** 30874 * Gets the instruction to generate for an interpolated class map. 30875 * @param interpolation An Interpolation AST 30876 */ 30877function getClassMapInterpolationExpression(interpolation) { 30878 switch (getInterpolationArgsLength(interpolation)) { 30879 case 1: 30880 return Identifiers.classMap; 30881 case 3: 30882 return Identifiers.classMapInterpolate1; 30883 case 5: 30884 return Identifiers.classMapInterpolate2; 30885 case 7: 30886 return Identifiers.classMapInterpolate3; 30887 case 9: 30888 return Identifiers.classMapInterpolate4; 30889 case 11: 30890 return Identifiers.classMapInterpolate5; 30891 case 13: 30892 return Identifiers.classMapInterpolate6; 30893 case 15: 30894 return Identifiers.classMapInterpolate7; 30895 case 17: 30896 return Identifiers.classMapInterpolate8; 30897 default: 30898 return Identifiers.classMapInterpolateV; 30899 } 30900} 30901/** 30902 * Gets the instruction to generate for an interpolated style map. 30903 * @param interpolation An Interpolation AST 30904 */ 30905function getStyleMapInterpolationExpression(interpolation) { 30906 switch (getInterpolationArgsLength(interpolation)) { 30907 case 1: 30908 return Identifiers.styleMap; 30909 case 3: 30910 return Identifiers.styleMapInterpolate1; 30911 case 5: 30912 return Identifiers.styleMapInterpolate2; 30913 case 7: 30914 return Identifiers.styleMapInterpolate3; 30915 case 9: 30916 return Identifiers.styleMapInterpolate4; 30917 case 11: 30918 return Identifiers.styleMapInterpolate5; 30919 case 13: 30920 return Identifiers.styleMapInterpolate6; 30921 case 15: 30922 return Identifiers.styleMapInterpolate7; 30923 case 17: 30924 return Identifiers.styleMapInterpolate8; 30925 default: 30926 return Identifiers.styleMapInterpolateV; 30927 } 30928} 30929/** 30930 * Gets the instruction to generate for an interpolated style prop. 30931 * @param interpolation An Interpolation AST 30932 */ 30933function getStylePropInterpolationExpression(interpolation) { 30934 switch (getInterpolationArgsLength(interpolation)) { 30935 case 1: 30936 return Identifiers.styleProp; 30937 case 3: 30938 return Identifiers.stylePropInterpolate1; 30939 case 5: 30940 return Identifiers.stylePropInterpolate2; 30941 case 7: 30942 return Identifiers.stylePropInterpolate3; 30943 case 9: 30944 return Identifiers.stylePropInterpolate4; 30945 case 11: 30946 return Identifiers.stylePropInterpolate5; 30947 case 13: 30948 return Identifiers.stylePropInterpolate6; 30949 case 15: 30950 return Identifiers.stylePropInterpolate7; 30951 case 17: 30952 return Identifiers.stylePropInterpolate8; 30953 default: 30954 return Identifiers.stylePropInterpolateV; 30955 } 30956} 30957/** 30958 * Checks whether property name is a custom CSS property. 30959 * See: https://www.w3.org/TR/css-variables-1 30960 */ 30961function isCssCustomProperty(name) { 30962 return name.startsWith('--'); 30963} 30964function isEmptyExpression(ast) { 30965 if (ast instanceof ASTWithSource) { 30966 ast = ast.ast; 30967 } 30968 return ast instanceof EmptyExpr$1; 30969} 30970 30971// Selector attribute name of `<ng-content>` 30972const NG_CONTENT_SELECT_ATTR = 'select';
30973// Attribute name of `ngProjectAs`. 30974const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs'; 30975// Global symbols available only inside event bindings. 30976const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']); 30977// Tag name of the `ng-template` element. 30978const NG_TEMPLATE_TAG_NAME = 'ng-template'; 30979// List of supported global targets for event listeners 30980const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers.resolveWindow], ['document', Identifiers.resolveDocument], ['body', Identifiers.resolveBody]]); 30981const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t']; 30982// if (rf & flags) { .. } 30983function renderFlagCheckIfStmt(flags, statements) { 30984 return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null, false), statements); 30985} 30986function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) { 30987 const { 30988 type, 30989 name, 30990 target, 30991 phase, 30992 handler 30993 } = eventAst; 30994 if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) { 30995 throw new Error(`Unexpected global target '${target}' defined for '${name}' event. 30996 Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`); 30997 } 30998 const eventArgumentName = '$event'; 30999 const implicitReceiverAccesses = new Set(); 31000 const implicitReceiverExpr = scope === null || scope.bindingLevel === 0 ? variable(CONTEXT_NAME) : scope.getOrCreateSharedContextVar(0); 31001 const bindingStatements = eventAst.type === 2 /* ParsedEventType.TwoWay */ ? convertAssignmentActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS) : convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS); 31002 const statements = []; 31003 const variableDeclarations = scope?.variableDeclarations();
31004 const restoreViewStatement = scope?.restoreViewStatement(); 31005 if (variableDeclarations) { 31006 // `variableDeclarations` needs to run first, because 31007 // `restoreViewStatement` depends on the result. 31008 statements.push(...variableDeclarations); 31009 } 31010 statements.push(...bindingStatements); 31011 if (restoreViewStatement) { 31012 statements.unshift(restoreViewStatement); 31013 // If there's a `restoreView` call, we need to reset the view at the end of the listener 31014 // in order to avoid a leak. If there's a `return` statement already, we wrap it in the 31015 // call, e.g. `return resetView(ctx.foo())`. Otherwise we add the call as the last statement. 31016 const lastStatement = statements[statements.length - 1]; 31017 if (lastStatement instanceof ReturnStatement) { 31018 statements[statements.length - 1] = new ReturnStatement(invokeInstruction(lastStatement.value.sourceSpan, Identifiers.resetView, [lastStatement.value])); 31019 } else { 31020 statements.push(new ExpressionStatement(invokeInstruction(null, Identifiers.resetView, []))); 31021 } 31022 } 31023 const eventName = type === 1 /* ParsedEventType.Animation */ ? prepareSyntheticListenerName(name, phase) : name; 31024 const fnName = handlerName && sanitizeIdentifier(handlerName); 31025 const fnArgs = []; 31026 if (implicitReceiverAccesses.has(eventArgumentName)) { 31027 fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE)); 31028 } 31029 const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName); 31030 const params = [literal(eventName), handlerFn]; 31031 if (target) { 31032 params.push(literal(false), 31033 // `useCapture` flag, defaults to `false` 31034 importExpr(GLOBAL_TARGET_RESOLVERS.get(target))); 31035 } 31036 return params; 31037} 31038function createComponentDefConsts() { 31039 return { 31040 prepareStatements: [], 31041 constExpressions: [], 31042 i18nVarRefsCache: new Map() 31043 }; 31044} 31045class TemplateData { 31046 constructor(name, index, scope, visitor) { 31047 this.name = name; 31048 this.index = index; 31049 this.scope = scope; 31050 this.visitor = visitor; 31051 } 31052 getConstCount() { 31053 return this.visitor.getConstCount(); 31054 } 31055 getVarCount() { 31056 return this.visitor.getVarCount(); 31057 } 31058} 31059class TemplateDefinitionBuilder { 31060 constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, _namespace, relativeContextFilePath, i18nUseExternalIds, deferBlocks, elementLocations, allDeferrableDepsFn, _constants = createComponentDefConsts()) { 31061 this.constantPool = constantPool; 31062 this.level = level; 31063 this.contextName = contextName; 31064 this.i18nContext = i18nContext; 31065 this.templateIndex = templateIndex; 31066 this.templateName = templateName; 31067 this._namespace = _namespace; 31068 this.i18nUseExternalIds = i18nUseExternalIds; 31069 this.deferBlocks = deferBlocks; 31070 this.elementLocations = elementLocations; 31071 this.allDeferrableDepsFn = allDeferrableDepsFn; 31072 this._constants = _constants; 31073 this._dataIndex = 0; 31074 this._bindingContext = 0; 31075 this._prefixCode = []; 31076 /** 31077 * List of callbacks to generate creation mode instructions. We store them here as we process 31078 * the template so bindings in listeners are resolved only once all nodes have been visited. 31079 * This ensures all local refs and context variables are available for matching. 31080 */ 31081 this._creationCodeFns = []; 31082 /** 31083 * List of callbacks to generate update mode instructions. We store them here as we process 31084 * the template so bindings are resolved only once all nodes have been visited. This ensures 31085 * all local refs and context variables are available for matching. 31086 */ 31087 this._updateCodeFns = []; 31088 /** Index of the currently-selected node. */ 31089 this._currentIndex = 0; 31090 /** Temporary variable declarations generated from visiting pipes, literals, etc. */ 31091 this._tempVariables = []; 31092 /** 31093 * Temporary variable used to store state between control flow instructions. 31094 * Should be accessed via the `allocateControlFlowTempVariable` method. 31095 */ 31096 this._controlFlowTempVariable = null; 31097 /** 31098 * List of callbacks to build nested templates. Nested templates must not be visited until 31099 * after the parent template has finished visiting all of its nodes. This ensures that all 31100 * local ref bindings in nested templates are able to find local ref values if the refs 31101 * are defined after the template declaration. 31102 */ 31103 this._nestedTemplateFns = []; 31104 // i18n context local to this template 31105 this.i18n = null; 31106 // Number of slots to reserve for pureFunctions 31107 this._pureFunctionSlots = 0; 31108 // Number of binding slots 31109 this._bindingSlots = 0; 31110 // Projection slots found in the template. Projection slots can distribute projected 31111 // nodes based on a selector, or can just use the wildcard selector to match 31112 // all nodes which aren't matching any selector. 31113 this._ngContentReservedSlots = []; 31114 // Number of non-default selectors found in all parent templates of this template. We need to 31115 // track it to properly adjust projection slot index in the `projection` instruction. 31116 this._ngContentSelectorsOffset = 0; 31117 // Expression that should be used as implicit receiver when convert
31117ing template 31118 // expressions to output AST. 31119 this._implicitReceiverExpr = null; 31120 // These should be handled in the template or element directly. 31121 this.visitReference = invalid; 31122 this.visitVariable = invalid; 31123 this.visitTextAttribute = invalid; 31124 this.visitBoundAttribute = invalid; 31125 this.visitBoundEvent = invalid; 31126 this.visitDeferredTrigger = invalid; 31127 this.visitDeferredBlockError = invalid; 31128 this.visitDeferredBlockLoading = invalid; 31129 this.visitDeferredBlockPlaceholder = invalid; 31130 this.visitIfBlockBranch = invalid; 31131 this.visitSwitchBlockCase = invalid; 31132 this.visitForLoopBlockEmpty = invalid; 31133 this.visitUnknownBlock = invalid; 31134 this._bindingScope = parentBindingScope.nestedScope(level); 31135 // Turn the relative context file path into an identifier by replacing non-alphanumeric 31136 // characters with underscores. 31137 this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_'; 31138 this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), numSlots => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => { 31139 this._bindingScope.set(this.level, localName, value); 31140 this.creationInstruction(null, Identifiers.pipe, [literal(slot), literal(name)]); 31141 }); 31142 } 31143 buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n, variableAliases) { 31144 this._ngContentSelectorsOffset = ngContentSelectorsOffset; 31145 if (this._namespace !== Identifiers.namespaceHTML) { 31146 this.creationInstruction(null, this._namespace); 31147 } 31148 // Create variable bindings
31149 variables.forEach(v => { 31150 const alias = variableAliases?.[v.name]; 31151 this.registerContextVariables(v.name, v.value); 31152 if (alias) { 31153 this.registerContextVariables(alias, v.value); 31154 } 31155 }); 31156 // Initiate i18n context in case: 31157 // - this template has parent i18n context 31158 // - or the template has i18n meta associated with it, 31159 // but it's not initiated by the Element (e.g. <ng-template i18n>) 31160 const initI18nContext = this.i18nContext || isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) && !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n); 31161 const selfClosingI18nInstruction = hasTextChildrenOnly(nodes); 31162 if (initI18nContext) { 31163 this.i18nStart(null, i18n, selfClosingI18nInstruction); 31164 } 31165 // This is the initial pass through the nodes of this template. In this pass, we 31166 // queue all creation mode and update mode instructions for generation in the second 31167 // pass. It's necessary to separate the passes to ensure local refs are defined before 31168 // resolving bindings. We also count bindings in this pass as we walk bound expressions. 31169 visitAll$1(this, nodes); 31170 // Add total binding count to pure function count so pure function instructions are 31171 // generated with the correct slot offset when update instructions are processed. 31172 this._pureFunctionSlots += this._bindingSlots; 31173 // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and 31174 // `pipeBind` update instructions), so we have to update the slot offsets manually 31175 // to account for bindings. 31176 this._valueConverter.updatePipeSlotOffsets(this._bindingSlots); 31177 // Nested templates must be processed before creation instructions so template() 31178 // instructions can be generated with the correct internal const count. 31179 this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn()); 31180 // Output the `projectionDef` instruction when some `<ng-content>` tags are present. 31181 // The `projectionDef` instruction is only emitted for the component template and 31182 // is skipped for nested templates (<ng-template> tags). 31183 if (this.level === 0 && this._ngContentReservedSlots.length) { 31184 const parameters = []; 31185 // By default the `projectionDef` instructions creates one slot for the wildcard 31186 // selector if no parameters are passed. Therefore we only want to allocate a new 31187 // array for the projection slots if the default projection slot is not sufficient. 31188 if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') { 31189 const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s); 31190 parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true)); 31191 } 31192 // Since we accumulate ngContent selectors while processing template elements, 31193 // we *prepend* `projectionDef` to creation instructions block, to put it before 31194 // any `projection` instructions 31195 this.creationInstruction(null, Identifiers.projectionDef, parameters, /* prepend */true); 31196 } 31197 if (initI18nContext) { 31198 this.i18nEnd(null, selfClosingI18nInstruction); 31199 } 31200 // Generate all the creation mode instructions (e.g. resolve bindings in listeners) 31201 const creationStatements = getInstructionStatements(this._creationCodeFns); 31202 // Generate all the update mode instructions (e.g. resolve property or text bindings) 31203 const updateStatements = getInstructionStatements(this._updateCodeFns); 31204 // Variable declaration must occur after binding resolution so we can generate context 31205 // instructions that build on each other. 31206 // e.g. const b = nextContext().$implicit(); const b = nextContext(); 31207 const creationVariables = this._bindingScope.viewSnapshotStatements(); 31208 const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables); 31209 const creationBlock = creationStatements.length > 0 ? [renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, creationVariables.concat(creationStatements))] : []; 31210 const updateBlock = updateStatements.length > 0 ? [renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateV
31210ariables.concat(updateStatements))] : []; 31211 return fn( 31212 // i.e. (rf: RenderFlags, ctx: any) 31213 [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [ 31214 // Temporary variable declarations for query refresh (i.e. let _t: any;) 31215 ...this._prefixCode, 31216 // Creating mode (i.e. if (rf & RenderFlags.Create) { ... }) 31217 ...creationBlock, 31218 // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...}) 31219 ...updateBlock], INFERRED_TYPE, null, this.templateName); 31220 } 31221 // LocalResolver 31222 getLocal(name) { 31223 return this._bindingScope.get(name); 31224 } 31225 // LocalResolver 31226 notifyImplicitReceiverUse() { 31227 this._bindingScope.notifyImplicitReceiverUse(); 31228 } 31229 // LocalResolver 31230 maybeRestoreView() { 31231 this._bindingScope.maybeRestoreView(); 31232 } 31233 i18nTranslate(message, params = {}, ref, transformFn) { 31234 const _ref = ref || this.i18nGenerateMainBlockVar(); 31235 // Closure Compiler requires const names to start with `MSG_` but disallows any other const to 31236 // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call 31237 const closureVar = this.i18nGenerateClosureVar(message.id); 31238 const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn); 31239 this._constants.prepareStatements.push(...statements); 31240 return _ref; 31241 } 31242 registerContextVariables(name, value) { 31243 const scopedName = this._bindingScope.freshReferenceName(); 31244 const retrievalLevel = this.level; 31245 const isDirect = value === DIRECT_CONTEXT_REFERENCE; 31246 const lhs = variable(name + scopedName); 31247 this._bindingScope.set(retrievalLevel, name, scope => { 31248 // If we're at the top level and we're referring to the context variable directly, we 31249 // can do so through the implicit receiver, instead of renaming it. Note that this does 31250 // not apply to listeners, because they need to restore the context. 31251 return isDirect && scope.bindingLevel === retrievalLevel && !scope.isListenerScope() ? variable(CONTEXT_NAME) : lhs; 31252 }, 1 /* DeclarationPriority.CONTEXT */, (scope, relativeLevel) => { 31253 let rhs; 31254 if (scope.bindingLevel === retrievalLevel) { 31255 if (scope.isListenerScope() && scope.hasRestoreViewVariable()) { 31256 // e.g. restoredCtx. 31257 // We have to get the context from a view reference, if one is available, because 31258 // the context that was passed in during creation may not be correct anymore. 31259 // For more information see: https://github.com/angular/angular/pull/40360. 31260 rhs = variable(RESTORED_VIEW_CONTEXT_NAME); 31261 scope.notifyRestoredViewContextUse(); 31262 } else if (isDirect) { 31263 // If we have a direct read of the context at the top level we don't need to 31264 // declare any variables and we can refer to it directly. 31265 return []; 31266 } else { 31267 // e.g. ctx 31268 rhs = variable(CONTEXT_NAME); 31269 } 31270 } else { 31271 const sharedCtxVar = scope.getSharedContextName(retrievalLevel); 31272 // e.g. ctx_r0 OR x(2); 31273 rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel); 31274 } 31275 return [ 31276 // e.g. const $items$ = x(2) for direct context references and 31277 // const $item$ = x(2).$implicit for indirect ones. 31278 lhs.set(isDirect ? rhs : rhs.prop(value || IMPLICIT_REFERENCE)).toConstDecl()]; 31279 }); 31280 } 31281 i18nAppendBindings(expressions) { 31282 if (expressions.length > 0) { 31283 expressions.forEach(expression => this.i18n.appendBinding(expression)); 31284 } 31285 } 31286 i18nBindProps(props) { 31287 const bound = {}; 31288 Object.keys(props).forEach(key => { 31289 const prop = props[key]; 31290 if (prop instanceof Text$3) { 31291 bound[key] = literal(prop.value); 31292 } else { 31293 const value = prop.value.visit(this._valueConverter); 31294 this.allocateBindingSlots(value); 31295 if (value instanceof Interpolation$1) { 31296 const { 31297 strings, 31298 expressions 31299 } = value; 31300 const { 31301 id, 31302 bindings 31303 } = this.i18n; 31304 const label = assembleI18nBoundString(strings, bindings.size, id); 31305 this.i18nAppendBindings(expressions); 31306 bound[key] = literal(label); 31307 } 31308 } 31309 }); 31310 return bound; 31311 } 31312 // Generates top level vars for i18n blocks (i.e. `i18n_N`). 31313 i18nGenerateMainBlockVar() { 31314 return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX$1)); 31315 } 31316 // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`). 31317 i18nGenerateClosureVar(messageId) { 31318 let name; 31319 const suffix = this.fileBasedI18nSuffix.toUpperCase(); 31320 if (this.i18nUseExternalIds) { 31321 const prefix = getTranslationConstPrefix(`EXTERNAL_`); 31322 const uniqueSuffix = this.constantPool.uniqueName(suffix); 31323 name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`; 31324 } else { 31325 const prefix = getTranslationConstPrefix(suffix); 31326 name = this.constantPool.uniqueName(prefix); 31327 } 31328 return variable(name); 31329 } 31330 i18nUpdateRef(context) { 31331 const { 31332 icus, 31333 meta, 31334 isRoot, 31335 isResolved, 31336 isEmitted 31337 } = context; 31338 if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
31339 context.isEmitted = true; 31340 const placeholders = context.getSerializedPlaceholders(); 31341 let icuMapping = {}; 31342 let params = placeholders.size ? placeholdersToParams(placeholders) : {}; 31343 if (icus.size) { 31344 icus.forEach((refs, key) => { 31345 if (refs.length === 1) { 31346 // if we have one ICU defined for a given 31347 // placeholder - just output its reference 31348 params[key] = refs[0]; 31349 } else { 31350 // ... otherwise we need to activate post-processing 31351 // to replace ICU placeholders with proper values 31352 const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`); 31353 params[key] = literal(placeholder); 31354 icuMapping[key] = literalArr(refs); 31355 } 31356 }); 31357 } 31358 // translation requires post processing in 2 cases: 31359 // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...]) 31360 // - if we have multiple ICUs that refer to the same placeholder name 31361 const needsPostprocessing = Array.from(placeholders.values()).some(value => value.length > 1) || Object.keys(icuMapping).length; 31362 let transformFn; 31363 if (needsPostprocessing) { 31364 transformFn = raw => { 31365 const args = [raw]; 31366 if (Object.keys(icuMapping).length) { 31367 args.push(mapLiteral(icuMapping, true)); 31368 } 31369 return invokeInstruction(null, Identifiers.i18nPostprocess, args); 31370 }; 31371 } 31372 this.i18nTranslate(meta, params, context.ref, transformFn); 31373 } 31374 } 31375 i18nStart(span = null, meta, selfClosing) { 31376 const index = this.allocateDataSlot(); 31377 this.i18n = this.i18nContext ? this.i18nContext.forkChildContext(index, this.templateIndex, meta) : new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta); 31378 // generate i18nStart instruction 31379 const { 31380 id, 31381 ref 31382 } = this.i18n; 31383 const params = [literal(index), this.addToConsts(ref)]; 31384 if (id > 0) { 31385 // do not push 3rd argument (sub-block id) 31386 // into i18nStart call for top level i18n context 31387 params.push(literal(id)); 31388 } 31389 this.creationInstruction(span, selfClosing ? Identifiers.i18n : Identifiers.i18nStart, params); 31390 } 31391 i18nEnd(span = null, selfClosing) { 31392 if (!this.i18n) { 31393 throw new Error('i18nEnd is executed with no i18n context present'); 31394 } 31395 if (this.i18nContext) { 31396 this.i18nContext.reconcileChildContext(this.i18n); 31397 this.i18nUpdateRef(this.i18nContext); 31398 } else { 31399 this.i18nUpdateRef(this.i18n); 31400 } 31401 // setup accumulated bindings 31402 const { 31403 index, 31404 bindings 31405 } = this.i18n; 31406 if (bindings.size) { 31407 for (const binding of bindings) { 31408 // for i18n block, advance to the most recent element index (by taking the current number of 31409 // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the 31410 // necessary lifecycle hooks of components/directives are properly flushed. 31411 this.updateInstructionWithAdvance(this.getConstCount() - 1, span, Identifiers.i18nExp, () => this.convertPropertyBinding(binding)); 31412 } 31413 this.updateInstruction(span, Identifiers.i18nApply, [literal(index)]); 31414 } 31415 if (!selfClosing) { 31416 this.creationInstruction(span, Identifiers.i18nEnd); 31417 } 31418 this.i18n = null; // reset local i18n context 31419 } 31420 i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) { 31421 let hasBindings = false; 31422 const i18nAttrArgs = []; 31423 attrs.forEach(attr => { 31424 const message = attr.i18n; 31425 const converted = attr.value.visit(this._valueConverter); 31426 this.allocateBindingSlots(converted); 31427 if (converted instanceof Interpolation$1) { 31428 const placeholders = assembleBoundTextPlaceholders(message); 31429 const params = placeholdersToParams(placeholders); 31430 i18nAttrArgs.push(literal(attr.name), this.i18nTranslate(message, params)); 31431 converted.expressions.forEach(expression => { 31432 hasBindings = true; 31433 this.updateInstructionWithAdvance(nodeIndex, sourceSpan, Identifiers.i18nExp, () => this.convertPropertyBinding(expression)); 31434 }); 31435 } 31436 }); 31437 if (i18nAttrArgs.length > 0) { 31438 const index = literal(this.allocateDataSlot()); 31439 const constIndex = this.addToConsts(literalArr(i18nAttrArgs)); 31440 this.creationInstruction(sourceSpan, Identifiers.i18nAttributes, [index, constIndex]); 31441 if (hasBindings) { 31442 this.updateInstruction(sourceSpan, Identifiers.i18nApply, [index]); 31443 } 31444 } 31445 }
31446 getNamespaceInstruction(namespaceKey) { 31447 switch (namespaceKey) { 31448 case 'math': 31449 return Identifiers.namespaceMathML; 31450 case 'svg': 31451 return Identifiers.namespaceSVG; 31452 default: 31453 return Identifiers.namespaceHTML; 31454 } 31455 } 31456 addNamespaceInstruction(nsInstruction, element) { 31457 this._namespace = nsInstruction; 31458 this.creationInstruction(element.startSourceSpan, nsInstruction); 31459 } 31460 /** 31461 * Adds an update instruction for an interpolated property or attribute, such as 31462 * `prop="{{value}}"` or `attr.title="{{value}}"` 31463 */ 31464 interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) { 31465 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]); 31466 } 31467 visitContent(ngContent) { 31468 const slot = this.allocateDataSlot(); 31469 const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length; 31470 const parameters = [literal(slot)]; 31471 this._ngContentReservedSlots.push(ngContent.selector); 31472 const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR); 31473 const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []); 31474 if (attributes.length > 0) { 31475 parameters.push(literal(projectionSlotIdx), literalArr(attributes)); 31476 } else if (projectionSlotIdx !== 0) { 31477 parameters.push(literal(projectionSlotIdx)); 31478 } 31479 this.creationInstruction(ngContent.sourceSpan, Identifiers.projection, parameters); 31480 if (this.i18n) { 31481 this.i18n.appendProjection(ngContent.i18n, slot); 31482 } 31483 } 31484 visitElement(element) { 31485 const elementIndex = this.allocateDataSlot(); 31486 const stylingBuilder = new StylingBuilder(null); 31487 this.elementLocations.set(element, { 31488 index: elementIndex, 31489 level: this.level 31490 }); 31491 let isNonBindableMode = false; 31492 const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n); 31493 const outputAttrs = []; 31494 const [namespaceKey, elementName] = splitNsName(element.name); 31495 const isNgContainer$1 = isNgContainer(element.name); 31496 // Handle styling, i18n, ngNonBindable attributes 31497 for (const attr of element.attributes) { 31498 const { 31499 name, 31500 value 31501 } = attr; 31502 if (name === NON_BINDABLE_ATTR) { 31503 isNonBindableMode = true; 31504 } else if (name === 'style') { 31505 stylingBuilder.registerStyleAttr(value); 31506 } else if (name === 'class') { 31507 stylingBuilder.registerClassAttr(value); 31508 } else { 31509 outputAttrs.push(attr); 31510 } 31511 } 31512 // Regular element or ng-container creation mode 31513 const parameters = [literal(elementIndex)]; 31514 if (!isNgContainer$1) { 31515 parameters.push(literal(elementName)); 31516 } 31517 // Add the attributes 31518 const allOtherInputs = []; 31519 const boundI18nAttrs = []; 31520 element.inputs.forEach(input => { 31521 const stylingInputWasSet = stylingBuilder.registerBoundInput(input); 31522 if (!stylingInputWasSet) { 31523 if ((input.type === 0 /* BindingType.Property */ || input.type === 5 /* BindingType.TwoWay */) && input.i18n) { 31524 boundI18nAttrs.push(input); 31525 } else { 31526 allOtherInputs.push(input); 31527 } 31528 } 31529 }); 31530 // add attributes for directive and projection matching purposes 31531 const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs); 31532 parameters.push(this.addAttrsToConsts(attributes)); 31533 // local refs (ex.: <div #foo #bar="baz">) 31534 const refs = this.prepareRefsArray(element.references); 31535 parameters.push(this.addToConsts(refs)); 31536 const wasInNamespace = this._namespace; 31537 const currentNamespace = this.getNamespaceInstruction(namespaceKey); 31538 // If the namespace is changing now, include an instruction to change it 31539 // during element creation. 31540 if (currentNamespace !== wasInNamespace) { 31541 this.addNamespaceInstruction(currentNamespace, element); 31542 } 31543 if (this.i18n) { 31544 this.i18n.appendElement(element.i18n, elementIndex); 31545 } 31546 // Note that we do not append text node instructions and ICUs inside i18n section, 31547 // so we exclude them while calculating whether current element has children 31548 const hasChildren = !isI18nRootElement && this.i18n ? !hasTextChildrenOnly(element.children) : element.children.length > 0; 31549 const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes && element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren; 31550 const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children); 31551 if (createSelfClosingInstruction) { 31552 this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers.elementContainer : Identifiers.element, trimTrailingNulls(parameters)); 31553 } else { 31554 this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers.elementContainerStart : Identifiers.elementStart, trimTrailingNulls(parameters)); 31555 if (isNonBindableMode) { 31556 this.creationInstruction(element.startSourceSpan, Identifiers.disableBindings); 31557 } 31558 if (boundI18nAttrs.length > 0) { 31559 this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, element.startSourceSpan ?? element.sourceSpan); 31560 } 31561 // Generate Listeners (outputs) 31562 if (element.outputs.length > 0) { 31563 for (const outputAst of element.outputs) { 31564 this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex)); 31565 } 31566 } 31567 // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and 31568 // listeners, to make sure i18nAttributes instruction targets current element at runtime. 31569 if (isI18nRootElement) { 31570 this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction); 31571 } 31572 } 31573 // the code here will collect all update-level styling instructions and add them to the 31574 // update block of the template function AOT code. Instructions like `styleProp`, 31575 // `styleMap`, `classMap`, `classProp` 31576 // are all generated and assigned in the code below. 31577 const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter); 31578 const limit = stylingInstructions.length - 1; 31579 for (let i = 0; i <= limit; i++) { 31580 const instruction = stylingInstructions[i]; 31581 this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction); 31582 } 31583 // the reason why `undefined` is used is because the renderer understands this as a 31584 // special value to symbolize that there is no RHS to this binding 31585 // TODO (matsko): revisit this once FW-959 is approached 31586 const emptyValueBindInstruction = literal(undefined); 31587 const propertyBindings = []; 31588 const attributeBindings = []; 31589 // Generate element input bindings
31590 allOtherInputs.forEach(input => { 31591 const inputType = input.type; 31592 if (inputType === 4 /* BindingType.Animation */) { 31593 const value = input.value.visit(this._valueConverter); 31594 // animation bindings can be presented in the following formats: 31595 // 1. [@binding]="fooExp" 31596 // 2. [@binding]="{value:fooExp, params:{...}}" 31597 // 3. [@binding] 31598 // 4. @binding 31599 // All formats will be valid for when a synthetic binding is created. 31600 // The reasoning for this is because the renderer should get each 31601 // synthetic binding value in the order of the array that they are 31602 // defined in... 31603 const hasValue = value instanceof LiteralPrimitive ? !!value.value : true; 31604 this.allocateBindingSlots(value); 31605 propertyBindings.push({ 31606 span: input.sourceSpan, 31607 reference: Identifiers.property, 31608 paramsOrFn: getBindingFunctionParams(() => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction, prepareSyntheticPropertyName(input.name)) 31609 }); 31610 } else { 31611 // we must skip attributes with associated i18n context, since these attributes are handled 31612 // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated 31613 if (input.i18n) return; 31614 const value = input.value.visit(this._valueConverter); 31615 if (value !== undefined) { 31616 const params = []; 31617 const [attrNamespace, attrName] = splitNsName(input.name); 31618 const isAttributeBinding = inputType === 1 /* BindingType.Attribute */; 31619 let sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding); 31620 if (!sanitizationRef) { 31621 // If there was no sanitization function found based on the security context 31622 // of an attribute/property - check whether this attribute/property is 31623 // one of the security-sensitive <iframe> attributes (and that the current 31624 // element is actually an <iframe>). 31625 if (isIframeElement(element.name) && isIframeSecuritySensitiveAttr(input.name)) { 31626 sanitizationRef = importExpr(Identifiers.validateIframeAttribute); 31627 } 31628 } 31629 if (sanitizationRef) { 31630 params.push(sanitizationRef); 31631 } 31632 if (attrNamespace) { 31633 const namespaceLiteral = literal(attrNamespace); 31634 if (sanitizationRef) { 31635 params.push(namespaceLiteral); 31636 } else { 31637 // If there wasn't a sanitization ref, we need to add 31638 // an extra param so that we can pass in the namespace. 31639 params.push(literal(null), namespaceLiteral); 31640 } 31641 } 31642 this.allocateBindingSlots(value); 31643 // Note: we don't separate two-way property bindings and regular ones, 31644 // because their assignment order needs to be maintained. 31645 if (inputType === 0 /* BindingType.Property */ || inputType === 5 /* BindingType.TwoWay */) { 31646 if (value instanceof Interpolation$1) { 31647 // prop="{{value}}" and friends 31648 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params); 31649 } else { 31650 // [prop]="value" 31651 // Collect all the properties so that we can chain into a single function at the end. 31652 propertyBindings.push({ 31653 span: input.sourceSpan, 31654 reference: inputType === 5 /* BindingType.TwoWay */ ? Identifiers.twoWayProperty : Identifiers.property, 31655 paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), attrName, params) 31656 }); 31657 } 31658 } else if (inputType === 1 /* BindingType.Attribute */) { 31659 if (value instanceof Interpolation$1 && getInterpolationArgsLength(value) > 1) { 31660 // attr.name="text{{value}}" and friends 31661 this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params); 31662 } else { 31663 const boundValue = value instanceof Interpolation$1 ? value.expressions[0] : value; 31664 // [attr.name]="value" or attr.name="{{value}}" 31665 // Collect the attribute bindings so that they can be chained at the end. 31666 attributeBindings.push({
31667 span: input.sourceSpan, 31668 paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(boundValue), attrName, params) 31669 }); 31670 } 31671 } else { 31672 // class prop 31673 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers.classProp, () => { 31674 return [literal(elementIndex), literal(attrName), this.convertPropertyBinding(value), ...params]; 31675 }); 31676 } 31677 } 31678 } 31679 }); 31680 for (const propertyBinding of propertyBindings) { 31681 this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, propertyBinding.reference, propertyBinding.paramsOrFn); 31682 } 31683 for (const attributeBinding of attributeBindings) { 31684 this.updateInstructionWithAdvance(elementIndex, attributeBinding.span, Identifiers.attribute, attributeBinding.paramsOrFn); 31685 } 31686 // Traverse element child nodes 31687 visitAll$1(this, element.children); 31688 if (!isI18nRootElement && this.i18n) { 31689 this.i18n.appendElement(element.i18n, elementIndex, true); 31690 } 31691 if (!createSelfClosingInstruction) { 31692 // Finish element construction mode. 31693 const span = element.endSourceSpan ?? element.sourceSpan; 31694 if (isI18nRootElement) { 31695 this.i18nEnd(span, createSelfClosingI18nInstruction); 31696 } 31697 if (isNonBindableMode) { 31698 this.creationInstruction(span, Identifiers.enableBindings); 31699 } 31700 this.creationInstruction(span, isNgContainer$1 ? Identifiers.elementContainerEnd : Identifiers.elementEnd); 31701 } 31702 } 31703 prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18nMeta, variableAliases) { 31704 const index = this.allocateDataSlot(); 31705 if (this.i18n && i18nMeta) { 31706 if (i18nMeta instanceof BlockPlaceholder) { 31707 this.i18n.appendBlock(i18nMeta, index); 31708 } else { 31709 this.i18n.appendTemplate(i18nMeta, index); 31710 } 31711 } 31712 const contextName = `${this.contextName}${contextNameSuffix}_${index}`; 31713 // Note: For the unique name, we don't include an unique suffix, unless really needed. 31714 // This keeps the generated output more clean as most of the time, we don't expect conflicts. 31715 const name = this.constantPool.uniqueName(`${contextName}_Template`, /** alwaysIncludeSuffix */false); 31716 // Create the template function 31717 const visitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks, this.elementLocations, this.allDeferrableDepsFn, this._constants); 31718 // Nested templates must not be visited until after their parent templates have completed 31719 // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't 31720 // be able to support bindings in nested templates to local refs that occur after the 31721 // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div> 31722 this._nestedTemplateFns.push(() => { 31723 const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18nMeta, variableAliases); 31724 this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(name)); 31725 if (visitor._ngContentReservedSlots.length) { 31726 this._ngContentReservedSlots.push(...visitor._ngContentReservedSlots); 31727 } 31728 }); 31729 return new TemplateData(name, index, visitor._bindingScope, visitor); 31730 } 31731 createEmbeddedTemplateFn(tagName, children, contextNameSuffix, sourceSpan, variables = [], attrsExprs, references, i18n) { 31732 const data = this.prepareEmbeddedTemplateFn(children, contextNameSuffix, variables, i18n); 31733 const parameters = [literal(data.index), variable(data.name), literal(tagName), this.addAttrsToConsts(attrsExprs || null)]; 31734 // local refs (ex.: <ng-template #foo>) 31735 if (references && references.length > 0) { 31736 const refs = this.prepareRefsArray(references); 31737 parameters.push(this.addToConsts(refs)); 31738 parameters.push(importExpr(Identifiers.templateRefExtractor)); 31739 } 31740 // e.g. template(1, MyComp_Template_1) 31741 this.creationInstruction(sourceSpan, Identifiers.templateCreate, () => { 31742 parameters.splice(2, 0, literal(data.getConstCount()), literal(data.getVarCount())); 31743 return trimTrailingNulls(parameters); 31744 }); 31745 return data.index; 31746 } 31747 visitTemplate(template) { 31748 // We don't care about the tag's namespace here, because we infer 31749 // it based on the parent nodes inside the template instruction. 31750 const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
31751 const contextNameSuffix = template.tagName ? '_' + sanitizeIdentifier(template.tagName) : ''; 31752 // prepare attributes parameter (including attributes used for directive matching) 31753 const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs); 31754 const templateIndex = this.createEmbeddedTemplateFn(tagNameWithoutNamespace, template.children, contextNameSuffix, template.sourceSpan, template.variables, attrsExprs, template.references, template.i18n); 31755 // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al; 31756 this.templatePropertyBindings(templateIndex, template.templateAttrs); 31757 // Only add normal input/output binding instructions on explicit <ng-template> elements. 31758 if (tagNameWithoutNamespace === NG_TEMPLATE_TAG_NAME) { 31759 const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta); 31760 // Add i18n attributes that may act as inputs to directives. If such attributes are present, 31761 // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template> 31762 // elements, in case of inline templates, corresponding instructions will be generated in the 31763 // nested template function. 31764 if (i18nInputs.length > 0) { 31765 this.i18nAttributesInstruction(templateIndex, i18nInputs, template.startSourceSpan ?? template.sourceSpan); 31766 } 31767 // Add the input bindings 31768 if (inputs.length > 0) { 31769 this.templatePropertyBindings(templateIndex, inputs); 31770 } 31771 // Generate listeners for directive output 31772 for (const outputAst of template.outputs) { 31773 this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex)); 31774 } 31775 } 31776 } 31777 visitBoundText(text) { 31778 if (this.i18n) { 31779 const value = text.value.visit(this._valueConverter); 31780 this.allocateBindingSlots(value); 31781 if (value instanceof Interpolation$1) { 31782 this.i18n.appendBoundText(text.i18n); 31783 this.i18nAppendBindings(value.expressions); 31784 } 31785 return; 31786 } 31787 const nodeIndex = this.allocateDataSlot(); 31788 this.creationInstruction(text.sourceSpan, Identifiers.text, [literal(nodeIndex)]); 31789 const value = text.value.visit(this._valueConverter); 31790 this.allocateBindingSlots(value); 31791 if (value instanceof Interpolation$1) { 31792 this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value)); 31793 } else { 31794 error('Text nodes should be interpolated and never bound directly.'); 31795 } 31796 } 31797 visitText(text) { 31798 // when a text element is located within a translatable 31799 // block, we exclude this text element from instructions set, 31800 // since it will be captured in i18n content and processed at runtime 31801 if (!this.i18n) { 31802 this.creationInstruction(text.sourceSpan, Identifiers.text, [literal(this.allocateDataSlot()), literal(text.value)]); 31803 } 31804 } 31805 visitIcu(icu) { 31806 let initWasInvoked = false; 31807 // if an ICU was created outside of i18n block, we still treat 31808 // it as a translatable entity and invoke i18nStart and i18nEnd 31809 // to generate i18n context and the necessary instructions 31810 if (!this.i18n) { 31811 initWasInvoked = true; 31812 this.i18nStart(null, icu.i18n, true); 31813 } 31814 const i18n = this.i18n; 31815 const vars = this.i18nBindProps(icu.vars); 31816 const placeholders = this.i18nBindProps(icu.placeholders); 31817 // output ICU directly and keep ICU reference in context 31818 const message = icu.i18n; 31819 // we always need post-processing function for ICUs, to make sure that: 31820 // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note: 31821 // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders 31822 // inside ICUs) 31823 // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values 31824 const transformFn = raw => { 31825 // Sort the map entries in the compiled output. This makes it easy to acheive identical output 31826 // in the template pipeline compiler. 31827 const params = Object.fromEntries(Object.entries({ 31828 ...vars, 31829 ...placeholders 31830 }).sort()); 31831 const formatted = formatI18nPlaceholderNamesInMap(params, /* useCamelCase */false);
31832 return invokeInstruction(null, Identifiers.i18nPostprocess, [raw, mapLiteral(formatted, true)]); 31833 }; 31834 // in case the whole i18n message is a single ICU - we do not need to 31835 // create a separate top-level translation, we can use the root ref instead 31836 // and make this ICU a top-level translation 31837 // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function 31838 // separately, so we do not pass placeholders into `i18nTranslate` function. 31839 if (isSingleI18nIcu(i18n.meta)) { 31840 this.i18nTranslate(message, /* placeholders */{}, i18n.ref, transformFn); 31841 } else { 31842 // output ICU directly and keep ICU reference in context 31843 const ref = this.i18nTranslate(message, /* placeholders */{}, /* ref */undefined, transformFn); 31844 i18n.appendIcu(icuFromI18nMessage(message).name, ref); 31845 } 31846 if (initWasInvoked) { 31847 this.i18nEnd(null, true); 31848 } 31849 return null; 31850 } 31851 visitIfBlock(block) { 31852 // Allocate one slot for the result of the expression. 31853 this.allocateBindingSlots(null); 31854 // We have to process the block in two steps: once here and again in the update instruction 31855 // callback in order to generate the correct expressions when pipes or pure functions are 31856 // used inside the branch expressions. 31857 const branchData = block.branches.map((branch, branchIndex) => { 31858 const { 31859 expression, 31860 expressionAlias, 31861 children, 31862 sourceSpan 31863 } = branch; 31864 // If the branch has an alias, it'll be assigned directly to the container's context. 31865 // We define a variable referring directly to the context so that any nested usages can be 31866 // rewritten to refer to it. 31867 const variables = expressionAlias !== null ? [new Variable(expressionAlias.name, DIRECT_CONTEXT_REFERENCE, expressionAlias.sourceSpan, expressionAlias.keySpan)] : undefined; 31868 let tagName = null; 31869 let attrsExprs; 31870 // Only the first branch can be used for projection, because the conditional 31871 // uses the container of the first branch as the insertion point for all branches. 31872 if (branchIndex === 0) { 31873 const inferredData = this.inferProjectionDataFromInsertionPoint(branch); 31874 tagName = inferredData.tagName; 31875 attrsExprs = inferredData.attrsExprs; 31876 } 31877 // Note: the template needs to be created *before* we process the expression, 31878 // otherwise pipes injecting some symbols won't work (see #52102). 31879 const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs, undefined, branch.i18n); 31880 const processedExpression = expression === null ? null : expression.visit(this._valueConverter); 31881 return { 31882 index: templateIndex, 31883 expression: processedExpression, 31884 alias: expressionAlias 31885 }; 31886 }); 31887 // Use the index of the first block as the index for the entire container. 31888 const containerIndex = branchData[0].index; 31889 const paramsCallback = () => { 31890 let contextVariable = null; 31891 const generateBranch = branchIndex => { 31892 // If we've gone beyond the last branch, return the special -1 value which means that no 31893 // view will be rendered. Note that we don't need to reset the context here, because -1 31894 // won't render a view so the passed-in context won't be captured. 31895 if (branchIndex > branchData.length - 1) { 31896 return literal(-1); 31897 } 31898 const { 31899 index, 31900 expression, 31901 alias 31902 } = branchData[branchIndex]; 31903 // If the branch has no expression, it means that it's the final `else`. 31904 // Return its index and stop the recursion. Assumes that there's only one 31905 // `else` condition and that it's the last branch. 31906 if (expression === null) { 31907 return literal(index); 31908 } 31909 let comparisonTarget; 31910 if (alias) { 31911 // If the branch is aliased, we need to assign the expression value to the temporary 31912 // variable and then pass it into `conditional`. E.g. for the expression: 31913 // `@if (foo(); as alias) {...}` we have to generate: 31914 // ``` 31915 // let temp; 31916 // conditional(0, (temp = ctx.foo()) ? 0 : -1, temp); 31917 // ``` 31918 contextVariable = this.allocateControlFlowTempVariable(); 31919 comparisonTarget = contextVariable.set(this.convertPropertyBinding(expression)); 31920 } else { 31921 comparisonTarget = this.convertPropertyBinding(expression); 31922 } 31923 return comparisonTarget.conditional(literal(index), generateBranch(branchIndex + 1)); 31924 }; 31925 const params = [literal(containerIndex), generateBranch(0)]; 31926 if (contextVariable !== null) { 31927 params.push(contextVariable); 31928 } 31929 return params; 31930 }; 31931 this.updateInstructionWithAdvance(containerIndex, block.branches[0].sourceSpan, Identifiers.conditional, paramsCallback); 31932 } 31933 visitSwitchBlock(block) { 31934 if (block.cases.length === 0) { 31935 return; 31936 } 31937 // We have to process the block in two steps: once here and again in the update instruction 31938 // callback in order to generate the correct expressions when pipes or pure functions are used. 31939 const caseData = block.cases.map(currentCase => { 31940 const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan, undefined, undefined, undefined, currentCase.i18n); 31941 const expression = currentCase.expression === null ? null : currentCase.expression.visit(this._valueConverter); 31942 return { 31943 index, 31944 expression 31945 }; 31946 }); 31947 // Use the index of the first block as the index for the entire container. 31948 const containerIndex = caseData[0].index; 31949 // Note: the expression needs to be processed *after* the template, 31950 // otherwise pipes injecting some symbols won't work (see #52102). 31951 const blockExpression = block.expression.visit(this._valueConverter); 31952 this.allocateBindingSlots(null); // Allocate a slot for the primary block expression. 31953 this.updateInstructionWithAdvance(containerIndex, block.sourceSpan, Identifiers.conditional, () => { 31954 const generateCases = caseIndex => { 31955 // If we've gone beyond the last branch, return the special -1 31956 // value which means that no view will be rendered. 31957 if (caseIndex > caseData.length - 1) { 31958 return literal(-1); 31959 } 31960 const { 31961 index, 31962 expression 31963 } = caseData[caseIndex]; 31964 // If the case has no expression, it means that it's the `default` case. 31965 // Return its index and stop the recursion. Assumes that there's only one 31966 // `default` condition and that it's defined last. 31967 if (expression === null) { 31968 return literal(index); 31969 } 31970 // If this is the very first comparison, we need to assign the value of the primary 31971 // expression as a part of the comparison so the remaining cases can reuse it. In practice 31972 // this looks as follows: 31973 // ``` 31974 // let temp; 31975 // conditional(1, (temp = ctx.foo) === 1 ? 1 : temp === 2 ? 2 : temp === 3 ? 3 : 4); 31976 // ``` 31977 const comparisonTarget = caseIndex === 0 ? this.allocateControlFlowTempVariable().set(this.convertPropertyBinding(blockExpression)) : this.allocateControlFlowTempVariable(); 31978 return comparisonTarget.identical(this.convertPropertyBinding(expression)).conditional(literal(index), generateCases(caseIndex + 1)); 31979 }; 31980 return [literal(containerIndex), generateCases(0)]; 31981 }); 31982 } 31983 visitDeferredBlock(deferred) { 31984 const { 31985 loading, 31986 placeholder, 31987 error, 31988 triggers, 31989 prefetchTriggers 31990 } = deferred; 31991 const metadata = this.deferBlocks.get(deferred); 31992 if (!metadata) { 31993 throw new Error('Could not resolve `defer` block metadata. Block may need to be analyzed.'); 31994 } 31995 const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan, undefined, undefined, undefined, deferred.i18n); 31996 const loadingIndex = loading ? this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan, undefined, undefined, undefined, loading.i18n) : null; 31997 const loadingConsts = loading ? trimTrailingNulls([literal(loading.minimumTime), literal(loading.afterTime)]) : null; 31998 const placeholderIndex = placeholder ? this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan, undefined, undefined, undefined, placeholder.i18n) : null; 31999 const placeholderConsts = placeholder && placeholder.minimumTime !== null ? 32000 // TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts` 32001 // since the placeholder block can only have one parameter? 32002 literalArr([literal(placeholder.minimumTime)]) : null; 32003 const errorIndex = error ? this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan, undefined, undefined, undefined, error.i18n) : null; 32004 // Note: we generate this last so the index matches the instruction order. 32005 const deferredIndex = this.allocateDataSlot(); 32006 const depsFnName = `${this.contextName}_Defer_${deferredIndex}_DepsFn`; 32007 // e.g. `defer(1, 0, MyComp_Defer_1_DepsFn, ...)` 32008 this.creationInstruction(deferred.sourceSpan, Identifiers.defer, trimTrailingNulls([literal(deferredIndex), literal(primaryTemplateIndex), this.allDeferrableDepsFn ?? this.createDeferredDepsFunction(depsFnName, metadata), literal(loadingIndex), literal(placeholderIndex), literal(errorIndex), loadingConsts?.length ? this.addToConsts(literalArr(loadingConsts)) : TYPED_NULL_EXPR, placeholderConsts ? this.addToConsts(placeholderConsts) : TYPED_NULL_EXPR, loadingConsts?.length || placeholderConsts ? importExpr(Identifiers.deferEnableTimerScheduling) : TYPED_NULL_EXPR])); 32009 // Allocate an extra data slot right after a defer block slot to store 32010 // instance-specific state of that defer block at runtime. 32011 this.allocateDataSlot(); 32012 // Note: the triggers need to be processed *after* the various templates, 32013 // otherwise pipes injecting some symbols won't work (see #52102).
32014 this.createDeferTriggerInstructions(deferredIndex, triggers, metadata, false); 32015 this.createDeferTriggerInstructions(deferredIndex, prefetchTriggers, metadata, true); 32016 } 32017 createDeferredDepsFunction(name, metadata) { 32018 if (metadata.deps.length === 0) { 32019 return TYPED_NULL_EXPR; 32020 } 32021 // This defer block has deps for which we need to generate dynamic imports. 32022 const dependencyExp = []; 32023 for (const deferredDep of metadata.deps) { 32024 if (deferredDep.isDeferrable) { 32025 // Callback function, e.g. `m () => m.MyCmp;`. 32026 const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], 32027 // Default imports are always accessed through the `default` property. 32028 variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName)); 32029 // Dynamic import, e.g. `import('./a').then(...)`. 32030 const importExpr = new DynamicImportExpr(deferredDep.importPath).prop('then').callFn([innerFn]); 32031 dependencyExp.push(importExpr); 32032 } else { 32033 // Non-deferrable symbol, just use a reference to the type. 32034 dependencyExp.push(deferredDep.type); 32035 } 32036 } 32037 const depsFnExpr = arrowFn([], literalArr(dependencyExp)); 32038 this.constantPool.statements.push(depsFnExpr.toDeclStmt(name, StmtModifier.Final)); 32039 return variable(name); 32040 } 32041 createDeferTriggerInstructions(deferredIndex, triggers, metadata, prefetch) { 32042 const { 32043 when, 32044 idle, 32045 immediate, 32046 timer, 32047 hover, 32048 interaction, 32049 viewport 32050 } = triggers; 32051 // `deferWhen(ctx.someValue)` 32052 if (when) { 32053 const value = when.value.visit(this._valueConverter); 32054 this.allocateBindingSlots(value); 32055 this.updateInstructionWithAdvance(deferredIndex, when.sourceSpan, prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, () => this.convertPropertyBinding(value)); 32056 } 32057 // Note that we generate an implicit `on idle` if the `deferred` block has no triggers. 32058 // `deferOnIdle()` 32059 if (idle || !prefetch && Object.keys(triggers).length === 0) { 32060 this.creationInstruction(idle?.sourceSpan || null, prefetch ? Identifiers.deferPrefetchOnIdle : Identifiers.deferOnIdle); 32061 } 32062 // `deferOnImmediate()` 32063 if (immediate) { 32064 this.creationInstruction(immediate.sourceSpan, prefetch ? Identifiers.deferPrefetchOnImmediate : Identifiers.deferOnImmediate); 32065 } 32066 // `deferOnTimer(1337)` 32067 if (timer) { 32068 this.creationInstruction(timer.sourceSpan, prefetch ? Identifiers.deferPrefetchOnTimer : Identifiers.deferOnTimer, [literal(timer.delay)]); 32069 } 32070 // `deferOnHover(index, walkUpTimes)` 32071 if (hover) { 32072 this.domNodeBasedTrigger('hover', hover, metadata, prefetch ? Identifiers.deferPrefetchOnHover : Identifiers.deferOnHover); 32073 } 32074 // `deferOnInteraction(index, walkUpTimes)` 32075 if (interaction) { 32076 this.domNodeBasedTrigger('interaction', interaction, metadata, prefetch ? Identifiers.deferPrefetchOnInteraction : Identifiers.deferOnInteraction); 32077 } 32078 // `deferOnViewport(index, walkUpTimes)` 32079 if (viewport) { 32080 this.domNodeBasedTrigger('viewport', viewport, metadata, prefetch ? Identifiers.deferPrefetchOnViewport : Identifiers.deferOnViewport); 32081 } 32082 } 32083 domNodeBasedTrigger(name, trigger, metadata, instructionRef) { 32084 const triggerEl = metadata.triggerElements.get(trigger); 32085 // Don't generate anything if a trigger cannot be resolved. 32086 // We'll have template diagnostics to surface these to users. 32087 if (!triggerEl) { 32088 return; 32089 } 32090 this.creationInstruction(trigger.sourceSpan, instructionRef, () => { 32091 const location = this.elementLocations.get(triggerEl); 32092 if (!location) { 32093 throw new Error(`Could not determine location of reference passed into ` + `'${name}' trigger. Template may not have been fully analyzed.`); 32094 } 32095 // A negative depth means that the trigger is inside the placeholder. 32096 // Cap it at -1 since we only care whether or not it's negative. 32097 const depth = Math.max(this.level - location.level, -1); 32098 const params = [literal(location.index)]; 32099 // The most common case should be a trigger within the view so we can omit a depth of 32100 // zero. For triggers in parent views and in the placeholder we need to pass it in. 32101 if (depth !== 0) { 32102 params.push(literal(depth)); 32103 } 32104 return params; 32105 }); 32106 } 32107 /**
32108 * Infers the data used for content projection (tag name and attributes) from the content of a 32109 * node. 32110 * @param node Node for which to infer the projection data. 32111 */ 32112 inferProjectionDataFromInsertionPoint(node) { 32113 let root = null; 32114 let tagName = null; 32115 let attrsExprs; 32116 for (const child of node.children) { 32117 // Skip over comment nodes. 32118 if (child instanceof Comment$1) { 32119 continue; 32120 } 32121 // We can only infer the tag name/attributes if there's a single root node. 32122 if (root !== null) { 32123 root = null; 32124 break; 32125 } 32126 // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`). 32127 if (child instanceof Element$1 || child instanceof Template && child.tagName !== null) { 32128 root = child; 32129 } 32130 } 32131 // If we've found a single root node, its tag name and *static* attributes can be copied 32132 // to the surrounding template to be used for content projection. Note that it's important 32133 // that we don't copy any bound attributes since they don't participate in content projection 32134 // and they can be used in directive matching (in the case of `Template.templateAttrs`). 32135 if (root !== null) { 32136 const name = root instanceof Element$1 ? root.name : root.tagName; 32137 // Don't pass along `ng-template` tag name since it enables directive matching. 32138 tagName = name === NG_TEMPLATE_TAG_NAME ? null : name; 32139 attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, root.attributes, root.inputs, []); 32140 } 32141 return { 32142 tagName, 32143 attrsExprs 32144 }; 32145 } 32146 allocateDataSlot() { 32147 return this._dataIndex++; 32148 } 32149 visitForLoopBlock(block) { 32150 // Allocate one slot for the repeater metadata. The slots for the primary and empty block 32151 // are implicitly inferred by the runtime to index + 1 and index + 2. 32152 const blockIndex = this.allocateDataSlot(); 32153 const { 32154 tagName, 32155 attrsExprs 32156 } = this.inferProjectionDataFromInsertionPoint(block); 32157 const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count], block.i18n, { 32158 // We need to provide level-specific versions of `$index` and `$count`, because 32159 // they're used when deriving the remaining variables (`$odd`, `$even` etc.) while at the 32160 // same time being available implicitly. Without these aliases, we wouldn't be able to 32161 // access the `$index` of a parent loop from inside of a nested loop. 32162 [block.contextVariables.$index.name]: this.getLevelSpecificVariableName('$index', this.level + 1), 32163 [block.contextVariables.$count.name]: this.getLevelSpecificVariableName('$count', this.level + 1) 32164 }); 32165 const { 32166 expression: trackByExpression, 32167 usesComponentInstance: trackByUsesComponentInstance 32168 } = this.createTrackByFunction(block); 32169 let emptyData = null; 32170 let emptyTagName = null; 32171 let emptyAttrsExprs; 32172 if (block.empty !== null) { 32173 const emptyInferred = this.inferProjectionDataFromInsertionPoint(block.empty); 32174 emptyTagName = emptyInferred.tagName; 32175 emptyAttrsExprs = emptyInferred.attrsExprs; 32176 emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n); 32177 // Allocate an extra slot for the empty block tracking. 32178 this.allocateBindingSlots(null); 32179 } 32180 this.registerComputedLoopVariables(block, primaryData.scope); 32181 // `repeaterCreate(0, ...)` 32182 this.creationInstruction(block.sourceSpan, Identifiers.repeaterCreate, () => { 32183 const params = [literal(blockIndex), variable(primaryData.name), literal(primaryData.getConstCount()), literal(primaryData.getVarCount()), literal(tagName), this.addAttrsToConsts(attrsExprs || null), trackByExpression]; 32184 if (emptyData !== null) { 32185 params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()), literal(emptyTagName), this.addAttrsToConsts(emptyAttrsExprs || null)); 32186 } else if (trackByUsesComponentInstance) { 32187 // If the tracking function doesn't use the component instance, we can omit the flag. 32188 params.push(literal(trackByUsesComponentInstance)); 32189 } 32190 return trimTrailingNulls(params); 32191 }); 32192 // Note: the expression needs to be processed *after* the template, 32193 // otherwise pipes injecting some symbols won't work (see #52102). 32194 // Note: we don't allocate binding slots for this expression, 32195 // because its value isn't stored in the LView. 32196 const value = block.expression.visit(this._valueConverter); 32197 // `advance(x); repeater(iterable)` 32198 this.updateInstructionWithAdvance(blockIndex, block.sourceSpan, Identifiers.repeater, () => [this.convertPropertyBinding(value)]); 32199 } 32200 registerComputedLoopVariables(block, bindingScope) { 32201 const level = bindingScope.bindingLevel; 32202 bindingScope.set(level, block.contextVariables.$odd.name, (scope, retrievalLevel) => { 32203 return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index').modulo(literal(2)).notIdentical(literal(0)); 32204 }); 32205 bindingScope.set(level, block.contextVariables.$even.name, (scope, retrievalLevel) => { 32206 return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index').modulo(literal(2)).identical(literal(0)); 32207 }); 32208 bindingScope.set(level, block.contextVariables.$first.name, (scope, retrievalLevel) => { 32209 return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index').identical(literal(0)); 32210 }); 32211 bindingScope.set(level, block.contextVariables.$last.name, (scope, retrievalLevel) => { 32212 const index = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index'); 32213 const count = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$count'); 32214 return index.identical(count.minus(literal(1))); 32215 }); 32216 } 32217 getLevelSpecificVariableName(name, level) { 32218 // We use the `ɵ` here to ensure that there are no name conflicts with user-defined variables. 32219 return `ɵ${name}_${level}`; 32220 } 32221 /** 32222 * Gets the name of a for loop variable at a specific binding level. This allows us to look 32223 * up implicitly shadowed variables like `$index` and `$count` at a specific level. 32224 */ 32225 getLevelSpecificForLoopVariable(block, scope, retrievalLevel, name) { 32226 const scopeName = scope.bindingLevel === retrievalLevel ? block.contextVariables[name].name : this.getLevelSpecificVariableName(name, retrievalLevel); 32227 return scope.get(scopeName); 32228 } 32229 optimizeTrackByFunction(block) { 32230 const indexLocalName = block.contextVariables.$index.name; 32231 const itemName = block.item.name; 32232 const ast = block.trackBy.ast; 32233 // Top-level access of `$index` uses the built in `repeaterTrackByIndex`. 32234 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver && ast.name === indexLocalName) { 32235 return { 32236 expression: importExpr(Identifiers.repeaterTrackByIndex), 32237 usesComponentInstance: false 32238 }; 32239 } 32240 // Top-level access of the item uses the built in `repeaterTrackByIdentity`. 32241 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver && ast.name === itemName) { 32242 return { 32243 expression: importExpr(Identifiers.repeaterTrackByIdentity), 32244 usesComponentInstance: false 32245 }; 32246 } 32247 // Top-level calls in the form of `fn($index, item)` can be passed in directly. 32248 if (ast instanceof Call && ast.receiver instanceof PropertyRead && ast.receiver.receiver instanceof ImplicitReceiver && ast.args.length === 2) { 32249 const firstIsIndex = ast.args[0] instanceof PropertyRead && ast.args[0].receiver instanceof ImplicitReceiver && ast.args[0].name === indexLocalName; 32250 const secondIsItem = ast.args[1] instanceof PropertyRead && ast.args[1].receiver instanceof ImplicitReceiver && ast.args[1].name === itemName; 32251 if (firstIsIndex && secondIsItem) { 32252 // If we're in the top-level component, we can access directly through `ctx`, 32253 // otherwise we have to get a hold of the component through `componentInstance()`. 32254 const receiver = this.level === 0 ? variable(CONTEXT_NAME) : new ExternalExpr(Identifiers.componentInstance).callFn([]); 32255 return { 32256 expression: receiver.prop(ast.receiver.name), 32257 usesComponentInstance: false 32258 }; 32259 } 32260 } 32261 return null; 32262 } 32263 createTrackByFunction(block) { 32264 const optimizedFn = this.optimizeTrackByFunction(block); 32265 // If the tracking function can be optimized, we don't need any further processing. 32266 if (optimizedFn !== null) { 32267 return optimizedFn; 32268 } 32269 const contextVars = block.contextVariables; 32270 const scope = new TrackByBindingScope(this._bindingScope, { 32271 // Alias `$index` and the item name to `$index` and `$item` respectively. 32272 // This allows us to reuse pure functions that may have different item names, 32273 // but are otherwise identical. 32274 [contextVars.$index.name]: '$index', 32275 [block.item.name]: '$item', 32276 // Accessing these variables in a tracking function will result in a template diagnostic. 32277 // We define them as globals so that their accesses are preserved verbatim instead of being 32278 // rewritten to the actual accesses. 32279 [contextVars.$count.name]: contextVars.$count.name, 32280 [contextVars.$first.name]: contextVars.$first.name, 32281 [contextVars.$last.name]: contextVars.$last.name, 32282 [contextVars.$even.name]: contextVars.$even.name, 32283 [contextVars.$odd.name]: contextVars.$odd.name 32284 }); 32285 const params = [new FnParam('$index'), new FnParam('$item')]; 32286 const stmts = convertPureComponentScopeFunction(block.trackBy.ast, scope, variable(CONTEXT_NAME), 'track');
32287 const usesComponentInstance = scope.getComponentAccessCount() > 0; 32288 let fn$1; 32289 if (!usesComponentInstance && stmts.length === 1 && stmts[0] instanceof ExpressionStatement) { 32290 fn$1 = arrowFn(params, stmts[0].expr); 32291 } else { 32292 // The last statement is returned implicitly. 32293 if (stmts.length > 0) { 32294 const lastStatement = stmts[stmts.length - 1]; 32295 if (lastStatement instanceof ExpressionStatement) { 32296 stmts[stmts.length - 1] = new ReturnStatement(lastStatement.expr); 32297 } 32298 } 32299 // This has to be a function expression, because `.bind` doesn't work on arrow functions. 32300 fn$1 = fn(params, stmts); 32301 } 32302 return { 32303 expression: this.constantPool.getSharedFunctionReference(fn$1, '_forTrack'), 32304 usesComponentInstance 32305 }; 32306 } 32307 getConstCount() { 32308 return this._dataIndex; 32309 } 32310 getVarCount() { 32311 return this._pureFunctionSlots; 32312 } 32313 getConsts() { 32314 return this._constants; 32315 } 32316 getNgContentSelectors() { 32317 return this._ngContentReservedSlots.length ? this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) : null; 32318 } 32319 bindingContext() { 32320 return `${this._bindingContext++}`; 32321 } 32322 templatePropertyBindings(templateIndex, attrs) { 32323 const propertyBindings = []; 32324 for (const input of attrs) { 32325 if (!(input instanceof BoundAttribute)) { 32326 continue; 32327 } 32328 const value = input.value.visit(this._valueConverter); 32329 if (value === undefined) { 32330 continue; 32331 } 32332 this.allocateBindingSlots(value); 32333 if (value instanceof Interpolation$1) { 32334 // Params typically contain attribute namespace and value sanitizer, which is applicable 32335 // for regular HTML elements, but not applicable for <ng-template> (since props act as 32336 // inputs to directives), so keep params array empty. 32337 const params = []; 32338 // prop="{{value}}" case 32339 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params); 32340 } else { 32341 // [prop]="value" case 32342 propertyBindings.push({ 32343 span: input.sourceSpan, 32344 paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), input.name) 32345 }); 32346 } 32347 } 32348 for (const propertyBinding of propertyBindings) { 32349 this.updateInstructionWithAdvance(templateIndex, propertyBinding.span, Identifiers.property, propertyBinding.paramsOrFn); 32350 } 32351 } 32352 // Bindings must only be resolved after all local refs have been visited, so all 32353 // instructions are queued in callbacks that execute once the initial pass has completed. 32354 // Otherwise, we wouldn't be able to support local refs that are defined after their 32355 // bindings. e.g. {{ foo }} <div #foo></div> 32356 instructionFn(fns, span, reference, paramsOrFn, prepend = false) { 32357 fns[prepend ? 'unshift' : 'push']({ 32358 span, 32359 reference, 32360 paramsOrFn 32361 }); 32362 } 32363 processStylingUpdateInstruction(elementIndex, instruction) { 32364 let allocateBindingSlots = 0; 32365 if (instruction) { 32366 for (const call of instruction.calls) { 32367 allocateBindingSlots += call.allocateBindingSlots; 32368 this.updateInstructionWithAdvance(elementIndex, call.sourceSpan, instruction.reference, () => call.params(value => call.supportsInterpolation && value instanceof Interpolation$1 ? this.getUpdateInstructionArguments(value) : this.convertPropertyBinding(value))); 32369 } 32370 } 32371 return allocateBindingSlots; 32372 } 32373 creationInstruction(span, reference, paramsOrFn, prepend) { 32374 this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend); 32375 } 32376 updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) { 32377 this.addAdvanceInstructionIfNecessary(nodeIndex, span); 32378 this.updateInstruction(span, reference, paramsOrFn); 32379 } 32380 updateInstruction(span, reference, paramsOrFn) { 32381 this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []); 32382 } 32383 addAdvanceInstructionIfNecessary(nodeIndex, span) { 32384 if (nodeIndex !== this._currentIndex) { 32385 const delta = nodeIndex - this._currentIndex; 32386 if (delta < 1) { 32387 throw new Error('advance instruction can only go forwards'); 32388 } 32389 this.instructionFn(this._updateCodeFns, span, Identifiers.advance, delta > 1 ? [literal(delta)] : []); 32390 this._currentIndex = nodeIndex; 32391 } 32392 } 32393 allocatePureFunctionSlots(numSlots) { 32394 const originalSlots = this._pureFunctionSlots; 32395 this._pureFunctionSlots += numSlots; 32396 return originalSlots; 32397 } 32398 allocateBindingSlots(value) { 32399 this._bindingSlots += value instanceof Interpolation$1 ? value.expressions.length : 1; 32400 } 32401 /** 32402 * Gets an expression that refers to the implicit receiver. The implicit 32403 * receiver is always the root level context. 32404 */ 32405 getImplicitReceiverExpr() { 32406 if (this._implicitReceiverExpr) { 32407 return this._implicitReceiverExpr; 32408 } 32409 return this._implicitReceiverExpr = this.level === 0 ? variable(CONTEXT_NAME) : this._bindingScope.getOrCreateSharedContextVar(0); 32410 } 32411 convertPropertyBinding(value) { 32412 const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext()); 32413 const valExpr = convertedPropertyBinding.currValExpr; 32414 this._tempVariables.push(...convertedPropertyBinding.stmts); 32415 return valExpr; 32416 } 32417 /** 32418 * Gets a list of argument expressions to pass to an update instruction expression. Also updates 32419 * the temp variables state with temp variables that were identified as needing to be created 32420 * while visiting the arguments. 32421 * @param value The original expression we will be resolving an arguments list from. 32422 */ 32423 getUpdateInstructionArguments(value) { 32424 const { 32425 args, 32426 stmts 32427 } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext()); 32428 this._tempVariables.push(...stmts); 32429 return args; 32430 } 32431 /** 32432 * Creates and returns a variable that can be used to 32433 * store the state between control flow instructions. 32434 */ 32435 allocateControlFlowTempVariable() { 32436 // Note: the assumption here is that we'll only need one temporary variable for all c
32436ontrol 32437 // flow instructions. It's expected that any instructions will overwrite it before passing it 32438 // into the parameters. 32439 if (this._controlFlowTempVariable === null) { 32440 const name = `${this.contextName}_contFlowTmp`; 32441 this._tempVariables.push(new DeclareVarStmt(name)); 32442 this._controlFlowTempVariable = variable(name); 32443 } 32444 return this._controlFlowTempVariable; 32445 } 32446 /** 32447 * Prepares all attribute expression values for the `TAttributes` array. 32448 * 32449 * The purpose of this function is to properly construct an attributes array that 32450 * is passed into the `elementStart` (or just `element`) functions. Because there 32451 * are many different types of attributes, the array needs to be constructed in a 32452 * special way so that `elementStart` can properly evaluate them. 32453 * 32454 * The format looks like this: 32455 * 32456 * ``` 32457 * attrs = [prop, value, prop2, value2, 32458 * PROJECT_AS, selector, 32459 * CLASSES, class1, class2, 32460 * STYLES, style1, value1, style2, value2, 32461 * BINDINGS, name1, name2, name3, 32462 * TEMPLATE, name4, name5, name6, 32463 * I18N, name7, name8, ...] 32464 * ``` 32465 * 32466 * Note that this function will fully ignore all synthetic (@foo) attribute values 32467 * because those values are intended to always be generated as property instructions. 32468 */ 32469 getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) { 32470 const alreadySeen = new Set(); 32471 const attrExprs = []; 32472 let ngProjectAsAttr; 32473 for (const attr of renderAttributes) { 32474 if (attr.name === NG_PROJECT_AS_ATTR_NAME) { 32475 ngProjectAsAttr = attr; 32476 } 32477 // Note that static i18n attributes aren't in the i18n array, 32478 // because they're treated in the same way as regular attributes. 32479 if (attr.i18n) { 32480 // When i18n attributes are present on elements with structural directives 32481 // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating 32482 // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction 32483 // attributes. So we do a cache lookup to see if suitable i18n translation block 32484 // already exists. 32485 const { 32486 i18nVarRefsCache 32487 } = this._constants; 32488 let i18nVarRef; 32489 if (i18nVarRefsCache.has(attr.i18n)) { 32490 i18nVarRef = i18nVarRefsCache.get(attr.i18n); 32491 } else { 32492 i18nVarRef = this.i18nTranslate(attr.i18n); 32493 i18nVarRefsCache.set(attr.i18n, i18nVarRef); 32494 } 32495 attrExprs.push(literal(attr.name), i18nVarRef); 32496 } else { 32497 attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr)); 32498 } 32499 } 32500 // Keep ngProjectAs next to the other name, value pairs so we can verify that we match 32501 // ngProjectAs marker in the attribute name slot. 32502 if (ngProjectAsAttr) { 32503 attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr)); 32504 } 32505 function addAttrExpr(key, value) { 32506 if (typeof key === 'string') { 32507 if (!alreadySeen.has(key)) { 32508 attrExprs.push(...getAttributeNameLiterals(key)); 32509 value !== undefined && attrExprs.push(value); 32510 alreadySeen.add(key); 32511 } 32512 } else { 32513 attrExprs.push(literal(key)); 32514 } 32515 } 32516 // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart` 32517 // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as 32518 // as single property value cell by cell. 32519 if (styles) { 32520 styles.populateInitialStylingAttrs(attrExprs); 32521 } 32522 if (inputs.length || outputs.length) { 32523 const attrsLengthBeforeInputs = attrExprs.length; 32524 for (let i = 0; i < inputs.length; i++) { 32525 const input = inputs[i]; 32526 // We don't want the animation and attribute bindings in the 32527 // attributes array since they aren't used for directive matching. 32528 if (input.type !== 4 /* BindingType.Animation */ && input.type !== 1 /* BindingType.Attribute */) { 32529 addAttrExpr(input.name); 32530 } 32531 } 32532 for (let i = 0; i < outputs.length; i++) { 32533 const output = outputs[i]; 32534 if (output.type !== 1 /* ParsedEventType.Animation */) { 32535 addAttrExpr(output.name); 32536 } 32537 } 32538 // this is a cheap way of adding the marker only after all the input/output 32539 // values have been filtered (by not including the animation ones) and added
32540 // to the expressions. The marker is important because it tells the runtime 32541 // code that this is where attributes without values start... 32542 if (attrExprs.length !== attrsLengthBeforeInputs) { 32543 attrExprs.splice(attrsLengthBeforeInputs, 0, literal(3 /* core.AttributeMarker.Bindings */)); 32544 } 32545 } 32546 if (templateAttrs.length) { 32547 attrExprs.push(literal(4 /* core.AttributeMarker.Template */)); 32548 templateAttrs.forEach(attr => addAttrExpr(attr.name)); 32549 } 32550 if (boundI18nAttrs.length) { 32551 attrExprs.push(literal(6 /* core.AttributeMarker.I18n */)); 32552 boundI18nAttrs.forEach(attr => addAttrExpr(attr.name)); 32553 } 32554 return attrExprs; 32555 } 32556 addToConsts(expression) { 32557 if (isNull(expression)) { 32558 return TYPED_NULL_EXPR; 32559 } 32560 const consts = this._constants.constExpressions; 32561 // Try to reuse a literal that's already in the array, if possible. 32562 for (let i = 0; i < consts.length; i++) { 32563 if (consts[i].isEquivalent(expression)) { 32564 return literal(i); 32565 } 32566 } 32567 return literal(consts.push(expression) - 1); 32568 } 32569 addAttrsToConsts(attrs) { 32570 return attrs !== null && attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR; 32571 } 32572 prepareRefsArray(references) { 32573 if (!references || references.length === 0) { 32574 return TYPED_NULL_EXPR; 32575 } 32576 const refsParam = references.flatMap(reference => { 32577 const slot = this.allocateDataSlot(); 32578 // Generate the update temporary. 32579 const variableName = this._bindingScope.freshReferenceName(); 32580 const retrievalLevel = this.level; 32581 const lhs = variable(variableName); 32582 this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DeclarationPriority.DEFAULT */, (scope, relativeLevel) => { 32583 // e.g. nextContext(2); 32584 const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : []; 32585 // e.g. const $foo$ = reference(1); 32586 const refExpr = lhs.set(importExpr(Identifiers.reference).callFn([literal(slot)])); 32587 return nextContextStmt.concat(refExpr.toConstDecl()); 32588 }, true); 32589 return [reference.name, reference.value]; 32590 }); 32591 return asLiteral(refsParam); 32592 } 32593 prepareListenerParameter(tagName, outputAst, index) { 32594 return () => { 32595 const eventName = outputAst.name; 32596 const bindingFnName = outputAst.type === 1 /* ParsedEventType.Animation */ ? 32597 // synthetic @listener.foo values are treated the exact same as are standard listeners 32598 prepareSyntheticListenerFunctionName(eventName, outputAst.phase) : sanitizeIdentifier(eventName); 32599 const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`; 32600 const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS); 32601 return prepareEventListenerParameters(outputAst, handlerName, scope); 32602 }; 32603 } 32604} 32605class ValueConverter extends AstMemoryEfficientTransformer { 32606 constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) { 32607 super(); 32608 this.constantPool = constantPool; 32609 this.allocateSlot = allocateSlot; 32610 this.allocatePureFunctionSlots = allocatePureFunctionSlots; 32611 this.definePipe = definePipe; 32612 this._pipeBindExprs = []; 32613 } 32614 // AstMemoryEfficientTransformer 32615 visitPipe(pipe, context) { 32616 // Allocate a slot to create the pipe 32617 const slot = this.allocateSlot(); 32618 const slotPseudoLocal = `PIPE:${slot}`; 32619 // Allocate one slot for the result plus one slot per pipe argument 32620 const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length); 32621 const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal); 32622 const { 32623 identifier, 32624 isVarLength 32625 } = pipeBindingCallInfo(pipe.args); 32626 this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier)); 32627 const args = [pipe.exp, ...pipe.args]; 32628 const convertedArgs = isVarLength ? this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) : this.visitAll(args); 32629 const pipeBindExpr = new Call(pipe.span, pipe.sourceSpan, target, [new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot), new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot), ...convertedArgs], null); 32630 this._pipeBindExprs.push(pipeBindExpr); 32631 return pipeBindExpr; 32632 } 32633 updatePipeSlotOffsets(bindingSlots) {
32634 this._pipeBindExprs.forEach(pipe => { 32635 // update the slot offset arg (index 1) to account for binding slots 32636 const slotOffset = pipe.args[1]; 32637 slotOffset.value += bindingSlots; 32638 }); 32639 } 32640 visitLiteralArray(array, context) { 32641 return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => { 32642 // If the literal has calculated (non-literal) elements transform it into 32643 // calls to literal factories that compose the literal and will cache intermediate 32644 // values. 32645 const literal = literalArr(values); 32646 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots); 32647 }); 32648 } 32649 visitLiteralMap(map, context) { 32650 return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => { 32651 // If the literal has calculated (non-literal) elements transform it into 32652 // calls to literal factories that compose the literal and will cache intermediate 32653 // values. 32654 const literal = literalMap(values.map((value, index) => ({ 32655 key: map.keys[index].key, 32656 value, 32657 quoted: map.keys[index].quoted 32658 }))); 32659 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots); 32660 }); 32661 } 32662} 32663// Pipes always have at least one parameter, the value they operate on 32664const pipeBindingIdentifiers = [Identifiers.pipeBind1, Identifiers.pipeBind2, Identifiers.pipeBind3, Identifiers.pipeBind4]; 32665function pipeBindingCallInfo(args) { 32666 const identifier = pipeBindingIdentifiers[args.length]; 32667 return { 32668 identifier: identifier || Identifiers.pipeBindV, 32669 isVarLength: !identifier 32670 }; 32671} 32672const pureFunctionIdentifiers = [Identifiers.pureFunction0, Identifiers.pureFunction1, Identifiers.pureFunction2, Identifiers.pureFunction3, Identifiers.pureFunction4, Identifiers.pureFunction5, Identifiers.pureFunction6, Identifiers.pureFunction7, Identifiers.pureFunction8]; 32673function pureFunctionCallInfo(args) { 32674 const identifier = pureFunctionIdentifiers[args.length]; 32675 return { 32676 identifier: identifier || Identifiers.pureFunctionV, 32677 isVarLength: !identifier 32678 }; 32679} 32680// e.g. x(2); 32681function generateNextContextExpr(relativeLevelDiff) { 32682 return importExpr(Identifiers.nextContext).callFn(relativeLevelDiff > 1 ? [literal(relativeLevelDiff)] : []); 32683} 32684function getLiteralFactory(constantPool, literal$1, allocateSlots) { 32685 const { 32686 literalFactory, 32687 literalFactoryArguments 32688 } = constantPool.getLiteralFactory(literal$1); 32689 // Allocate 1 slot for the result plus 1 per argument 32690 const startSlot = allocateSlots(1 + literalFactoryArguments.length); 32691 const { 32692 identifier, 32693 isVarLength 32694 } = pureFunctionCallInfo(literalFactoryArguments); 32695 // Literal factories are pure functions that only need to be re-invoked when the parameters 32696 // change. 32697 const args = [literal(startSlot), literalFactory]; 32698 if (isVarLength) { 32699 args.push(literalArr(literalFactoryArguments)); 32700 } else { 32701 args.push(...literalFactoryArguments); 32702 } 32703 return importExpr(identifier).callFn(args); 32704} 32705/** 32706 * Gets an array of literals that can be added to an expression 32707 * to represent the name and namespace of an attribute. E.g. 32708 * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`. 32709 * 32710 * @param name Name of the attribute, including the namespace. 32711 */ 32712function getAttributeNameLiterals(name) { 32713 const [attributeNamespace, attributeName] = splitNsName(name); 32714 const nameLiteral = literal(attributeName); 32715 if (attributeNamespace) { 32716 return [literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral]; 32717 } 32718 return [nameLiteral]; 32719} 32720/** The prefix used to get a shared context in BindingScope's map. */ 32721const SHARED_CONTEXT_KEY = '$$shared_ctx$$'; 32722class BindingScope { 32723 static createRootScope() { 32724 return new BindingScope(); 32725 } 32726 constructor(bindingLevel = 0, parent = null, globals) { 32727 this.bindingLevel = bindingLevel; 32728 this.parent = parent; 32729 this.globals = globals; 32730 /** Keeps a map from local variables to their BindingData. */ 32731 this.map = new Map(); 32732 this.referenceNameIndex = 0; 32733 this.restoreViewVariable = null; 32734 this.usesRestoredViewContext = false;
32735 if (globals !== undefined) { 32736 for (const name of globals) { 32737 this.set(0, name, variable(name)); 32738 } 32739 } 32740 } 32741 get(name) { 32742 let current = this; 32743 while (current) { 32744 let value = current.map.get(name); 32745 if (value != null) { 32746 if (current !== this) { 32747 // make a local copy and reset the `declare` state 32748 value = { 32749 retrievalLevel: value.retrievalLevel, 32750 lhs: value.lhs, 32751 declareLocalCallback: value.declareLocalCallback, 32752 declare: false, 32753 priority: value.priority 32754 }; 32755 // Cache the value locally. 32756 this.map.set(name, value); 32757 // Possibly generate a shared context var 32758 this.maybeGenerateSharedContextVar(value); 32759 this.maybeRestoreView(); 32760 } 32761 if (value.declareLocalCallback && !value.declare) { 32762 value.declare = true; 32763 } 32764 return typeof value.lhs === 'function' ? value.lhs(this, value.retrievalLevel) : value.lhs; 32765 } 32766 current = current.parent; 32767 } 32768 // If we get to this point, we are looking for a property on the top level component 32769 // - If level === 0, we are on the top and don't need to re-declare `ctx`. 32770 // - If level > 0, we are in an embedded view. We need to retrieve the name of the 32771 // local var we used to store the component context, e.g. const $comp$ = x(); 32772 return this.bindingLevel === 0 ? null : this.getComponentProperty(name); 32773 } 32774 /** Checks whether a variable exists locally on the current scope. */ 32775 hasLocal(name) { 32776 return this.map.has(name); 32777 } 32778 /** 32779 * Create a local variable for later reference. 32780 * 32781 * @param retrievalLevel The level from which this value can be retrieved 32782 * @param name Name of the variable. 32783 * @param lhs AST representing the left hand side of the `let lhs = rhs;`. 32784 * @param priority The sorting priority of this var 32785 * @param declareLocalCallback The callback to invoke when declaring this local var 32786 * @param localRef Whether or not this is a local ref 32787 */ 32788 set(retrievalLevel, name, lhs, priority = 0 /* DeclarationPriority.DEFAULT */, declareLocalCallback, localRef) { 32789 if (this.map.has(name)) { 32790 if (localRef) { 32791 // Do not throw an error if it's a local ref and do not update existing value, 32792 // so the first defined ref is always returned. 32793 return this; 32794 } 32795 error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`); 32796 } 32797 this.map.set(name, { 32798 retrievalLevel: retrievalLevel, 32799 lhs: lhs, 32800 declare: false, 32801 declareLocalCallback: declareLocalCallback, 32802 priority: priority 32803 }); 32804 return this; 32805 } 32806 // Implemented as part of LocalResolver. 32807 getLocal(name) { 32808 return this.get(name); 32809 } 32810 // Implemented as part of LocalResolver. 32811 notifyImplicitReceiverUse() { 32812 if (this.bindingLevel !== 0) { 32813 // Since the implicit receiver is accessed in an embedded view, we need to 32814 // ensure that we declare a shared context variable for the current template 32815 // in the update variables. 32816 this.map.get(SHARED_CONTEXT_KEY + 0).declare = true; 32817 } 32818 } 32819 nestedScope(level, globals) { 32820 const newScope = new BindingScope(level, this, globals); 32821 if (level > 0) newScope.generateSharedContextVar(0); 32822 return newScope; 32823 } 32824 /** 32825 * Gets or creates a shared context variable and returns its expression. Note that 32826 * this does not mean that the shared variable will be declared. Variables in the 32827 * binding scope will be only declared if they are used. 32828 */ 32829 getOrCreateSharedContextVar(retrievalLevel) { 32830 const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel; 32831 if (!this.map.has(bindingKey)) { 32832 this.generateSharedContextVar(retrievalLevel); 32833 } 32834 // Shared context variables are always generated as "ReadVarExpr". 32835 return this.map.get(bindingKey).lhs; 32836 } 32837 getSharedContextName(retrievalLevel) { 32838 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel); 32839 // Shared context variables are always generated as "ReadVarExpr". 32840 return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null; 32841 } 32842 maybeGenerateSharedContextVar(value) { 32843 if (value.priority === 1 /* DeclarationPriority.CONTEXT */ && value.retrievalLevel < this.bindingLevel) { 32844 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel); 32845 if (sharedCtxObj) { 32846 sharedCtxObj.declare = true; 32847 } else { 32848 this.generateSharedContextVar(value.retrievalLevel); 32849 } 32850 } 32851 } 32852 generateSharedContextVar(retrievalLevel) { 32853 const lhs = variable(CONTEXT_NAME + this.freshReferenceName()); 32854 this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, { 32855 retrievalLevel: retrievalLevel, 32856 lhs: lhs, 32857 declareLocalCallback: (scope, relativeLevel) => { 32858 // const ctx_r0 = nextContext(2); 32859 return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()]; 32860 }, 32861 declare: false, 32862 priority: 2 /* DeclarationPriority.SHARED_CONTEXT */ 32863 }); 32864 } 32865 getComponentProperty(name) { 32866 const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0); 32867 componentValue.declare = true; 32868 this.maybeRestoreView(); 32869 const lhs = typeof componentValue.lhs === 'function' ? componentValue.lhs(this, componentValue.retrievalLevel) : componentValue.lhs; 32870 return name === DIRECT_CONTEXT_REFERENCE ? lhs : lhs.prop(name); 32871 } 32872 maybeRestoreView() { 32873 // View restoration is required for listener instructions inside embedded views, because 32874 // they only run in creation mode and they can have references to the context object. 32875 // If the context object changes in update mode, the reference will be incorrect, because 32876 // it was established during creation. 32877 if (this.isListenerScope()) { 32878 if (!this.parent.restoreViewVariable) { 32879 // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction 32880 this.parent.restoreViewVariable = variable(this.parent.freshReferenceName()); 32881 } 32882 this.restoreViewVariable = this.parent.restoreViewVariable; 32883 } 32884 }
32885 restoreViewStatement() { 32886 if (this.restoreViewVariable) { 32887 const restoreCall = invokeInstruction(null, Identifiers.restoreView, [this.restoreViewVariable]); 32888 // Either `const restoredCtx = restoreView($state$);` or `restoreView($state$);` 32889 // depending on whether it is being used. 32890 return this.usesRestoredViewContext ? variable(RESTORED_VIEW_CONTEXT_NAME).set(restoreCall).toConstDecl() : restoreCall.toStmt(); 32891 } 32892 return null; 32893 } 32894 viewSnapshotStatements() { 32895 // const $state$ = getCurrentView(); 32896 return this.restoreViewVariable ? [this.restoreViewVariable.set(invokeInstruction(null, Identifiers.getCurrentView, [])).toConstDecl()] : []; 32897 } 32898 isListenerScope() { 32899 return this.parent && this.parent.bindingLevel === this.bindingLevel; 32900 } 32901 variableDeclarations() { 32902 let currentContextLevel = 0; 32903 return Array.from(this.map.values()).filter(value => value.declare).sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority).reduce((stmts, value) => { 32904 const levelDiff = this.bindingLevel - value.retrievalLevel; 32905 const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel); 32906 currentContextLevel = levelDiff; 32907 return stmts.concat(currStmts); 32908 }, []); 32909 } 32910 freshReferenceName() {
32911 let current = this; 32912 // Find the top scope as it maintains the global reference count 32913 while (current.parent) current = current.parent; 32914 const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`; 32915 return ref; 32916 } 32917 hasRestoreViewVariable() { 32918 return !!this.restoreViewVariable; 32919 } 32920 notifyRestoredViewContextUse() { 32921 this.usesRestoredViewContext = true; 32922 } 32923} 32924/** Binding scope of a `track` function inside a `for` loop block. */ 32925class TrackByBindingScope extends BindingScope { 32926 constructor(parentScope, globalOverrides) { 32927 super(parentScope.bindingLevel + 1, parentScope); 32928 this.globalOverrides = globalOverrides; 32929 this.componentAccessCount = 0; 32930 } 32931 get(name) { 32932 // Intercept any overridden globals. 32933 if (this.globalOverrides.hasOwnProperty(name)) { 32934 return variable(this.globalOverrides[name]); 32935 } 32936 let current = this.parent; 32937 // Prevent accesses of template variables outside the `for` loop. 32938 while (current) { 32939 if (current.hasLocal(name)) { 32940 return null; 32941 } 32942 current = current.parent; 32943 } 32944 // When the component scope is accessed, we redirect it through `this`. 32945 this.componentAccessCount++; 32946 return variable('this').prop(name); 32947 } 32948 /** Gets the number of times the host component has been accessed through the scope. */ 32949 getComponentAccessCount() { 32950 return this.componentAccessCount; 32951 } 32952} 32953/** 32954 * Creates an array of expressions out of an `ngProjectAs` attributes 32955 * which can be added to the instruction parameters. 32956 */ 32957function getNgProjectAsLiteral(attribute) { 32958 // Parse the attribute value into a CssSelectorList. Note that we only take the 32959 // first selector, because we don't support multiple selectors in ngProjectAs. 32960 const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0]; 32961 return [literal(5 /* core.AttributeMarker.ProjectAs */), asLiteral(parsedR3Selector)]; 32962} 32963/** 32964 * Gets the instruction to generate for an interpolated property 32965 * @param interpolation An Interpolation AST 32966 */ 32967function getPropertyInterpolationExpression(interpolation) { 32968 switch (getInterpolationArgsLength(interpolation)) { 32969 case 1: 32970 return Identifiers.propertyInterpolate; 32971 case 3: 32972 return Identifiers.propertyInterpolate1; 32973 case 5: 32974 return Identifiers.propertyInterpolate2; 32975 case 7: 32976 return Identifiers.propertyInterpolate3; 32977 case 9: 32978 return Identifiers.propertyInterpolate4; 32979 case 11: 32980 return Identifiers.propertyInterpolate5; 32981 case 13: 32982 return Identifiers.propertyInterpolate6; 32983 case 15: 32984 return Identifiers.propertyInterpolate7; 32985 case 17: 32986 return Identifiers.propertyInterpolate8; 32987 default: 32988 return Identifiers.propertyInterpolateV; 32989 } 32990} 32991/** 32992 * Gets the instruction to generate for an interpolated attribute 32993 * @param interpolation An Interpolation AST 32994 */ 32995function getAttributeInterpolationExpression(interpolation) { 32996 switch (getInterpolationArgsLength(interpolation)) { 32997 case 3: 32998 return Identifiers.attributeInterpolate1; 32999 case 5: 33000 return Identifiers.attributeInterpolate2; 33001 case 7: 33002 return Identifiers.attributeInterpolate3; 33003 case 9: 33004 return Identifiers.attributeInterpolate4; 33005 case 11: 33006 return Identifiers.attributeInterpolate5; 33007 case 13: 33008 return Identifiers.attributeInterpolate6; 33009 case 15: 33010 return Identifiers.attributeInterpolate7; 33011 case 17: 33012 return Identifiers.attributeInterpolate8; 33013 default: 33014 return Identifiers.attributeInterpolateV; 33015 } 33016} 33017/** 33018 * Gets the instruction to generate for interpolated text. 33019 * @param interpolation An Interpolation AST 33020 */ 33021function getTextInterpolationExpression(interpolation) { 33022 switch (getInterpolationArgsLength(interpolation)) { 33023 case 1: 33024 return Identifiers.textInterpolate; 33025 case 3: 33026 return Identifiers.textInterpolate1; 33027 case 5: 33028 return Identifiers.textInterpolate2; 33029 case 7: 33030 return Identifiers.textInterpolate3; 33031 case 9: 33032 return Identifiers.textInterpolate4; 33033 case 11: 33034 return Identifiers.textInterpolate5; 33035 case 13: 33036 return Identifiers.textInterpolate6; 33037 case 15: 33038 return Identifiers.textInterpolate7; 33039 case 17: 33040 return Identifiers.textInterpolate8; 33041 default: 33042 return Identifiers.textInterpolateV; 33043 } 33044} 33045/** 33046 * Parse a template into render3 `Node`s and additional metadata, with no other dependencies. 33047 * 33048 * @param template text of the template to parse 33049 * @param templateUrl URL to use for source mapping of the parsed template 33050 * @param options options to modify how the template is parsed 33051 */ 33052function parseTemplate(template, templateUrl, options = {}) { 33053 const { 33054 interpolationConfig,
33055 preserveWhitespaces, 33056 enableI18nLegacyMessageIdFormat 33057 } = options; 33058 const bindingParser = makeBindingParser(interpolationConfig); 33059 const htmlParser = new HtmlParser(); 33060 const parseResult = htmlParser.parse(template, templateUrl, { 33061 leadingTriviaChars: LEADING_TRIVIA_CHARS, 33062 ...options, 33063 tokenizeExpansionForms: true, 33064 tokenizeBlocks: options.enableBlockSyntax ?? true 33065 }); 33066 if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors && parseResult.errors.length > 0) { 33067 const parsedTemplate = { 33068 interpolationConfig, 33069 preserveWhitespaces, 33070 errors: parseResult.errors, 33071 nodes: [], 33072 styleUrls: [], 33073 styles: [], 33074 ngContentSelectors: [] 33075 }; 33076 if (options.collectCommentNodes) { 33077 parsedTemplate.commentNodes = []; 33078 } 33079 return parsedTemplate; 33080 } 33081 let rootNodes = parseResult.rootNodes; 33082 // process i18n meta information (scan attributes, generate ids) 33083 // before we run whitespace removal process, because existing i18n 33084 // extraction process (ng extract-i18n) relies on a raw content to generate 33085 // message ids 33086 const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */!preserveWhitespaces, enableI18nLegacyMessageIdFormat); 33087 const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes); 33088 if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors && i18nMetaResult.errors.length > 0) { 33089 const parsedTemplate = { 33090 interpolationConfig, 33091 preserveWhitespaces, 33092 errors: i18nMetaResult.errors, 33093 nodes: [], 33094 styleUrls: [], 33095 styles: [], 33096 ngContentSelectors: [] 33097 }; 33098 if (options.collectCommentNodes) { 33099 parsedTemplate.commentNodes = []; 33100 } 33101 return parsedTemplate; 33102 } 33103 rootNodes = i18nMetaResult.rootNodes; 33104 if (!preserveWhitespaces) { 33105 rootNodes = visitAll(new WhitespaceVisitor(), rootNodes); 33106 // run i18n meta visitor again in case whitespaces are removed (because that might affect 33107 // generated i18n message content) and first pass indicated that i18n content is present in a 33108 // template. During this pass i18n IDs generated at the first pass will be preserved, so we can 33109 // mimic existing extraction process (ng extract-i18n) 33110 if (i18nMetaVisitor.hasI18nMeta) { 33111 rootNodes = visitAll(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */false), rootNodes); 33112 } 33113 } 33114 const { 33115 nodes, 33116 errors, 33117 styleUrls, 33118 styles, 33119 ngContentSelectors, 33120 commentNodes 33121 } = htmlAstToRender3Ast(rootNodes, bindingParser, { 33122 collectCommentNodes: !!options.collectCommentNodes 33123 }); 33124 errors.push(...parseResult.errors, ...i18nMetaResult.errors); 33125 const parsedTemplate = { 33126 interpolationConfig, 33127 preserveWhitespaces, 33128 errors: errors.length > 0 ? errors : null, 33129 nodes, 33130 styleUrls, 33131 styles, 33132 ngContentSelectors 33133 }; 33134 if (options.collectCommentNodes) { 33135 parsedTemplate.commentNodes = commentNodes; 33136 } 33137 return parsedTemplate; 33138} 33139const elementRegistry = new DomElementSchemaRegistry(); 33140/** 33141 * Construct a `BindingParser` with a default configuration. 33142 */ 33143function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) { 33144 return new BindingParser(new Parser$1(new Lexer()), interpolationConfig, elementRegistry, []); 33145} 33146function resolveSanitizationFn(context, isAttribute) { 33147 switch (context) { 33148 case SecurityContext.HTML: 33149 return importExpr(Identifiers.sanitizeHtml); 33150 case SecurityContext.SCRIPT: 33151 return importExpr(Identifiers.sanitizeScript); 33152 case SecurityContext.STYLE: 33153 // the compiler does not fill in an instruction for [style.prop?] binding 33154 // values because the style algorithm knows internally what props are subject 33155 // to sanitization (only [attr.style] values are explicitly sanitized) 33156 return isAttribute ? importExpr(Identifiers.sanitizeStyle) : null; 33157 case SecurityContext.URL: 33158 return importExpr(Identifiers.sanitizeUrl); 33159 case SecurityContext.RESOURCE_URL: 33160 return importExpr(Identifiers.sanitizeResourceUrl); 33161 default: 33162 return null; 33163 } 33164} 33165function trustedConstAttribute(tagName, attr) { 33166 const value = asLiteral(attr.value); 33167 if (isTrustedTypesSink(tagName, attr.name)) { 33168 switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */true)) { 33169 case SecurityContext.HTML: 33170 return taggedTemplate(importExpr(Identifiers.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan); 33171 // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler. 33172 case SecurityContext.RESOURCE_URL: 33173 return taggedTemplate(importExpr(Identifiers.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan); 33174 default: 33175 return value; 33176 } 33177 } else { 33178 return value; 33179 } 33180} 33181function isSingleElementTemplate(children) { 33182 return children.length === 1 && children[0] instanceof Element$1; 33183} 33184function isTextNode(node) { 33185 return node instanceof Text$3 || node instanceof BoundText || node instanceof Icu$1; 33186} 33187function isIframeElement(tagName) { 33188 return tagName.toLowerCase() === 'iframe'; 33189} 33190function hasTextChildrenOnly(children) { 33191 return children.every(isTextNode); 33192} 33193function getBindingFunctionParams(deferredParams, name, eagerParams) { 33194 return () => { 33195 const value = deferredParams(); 33196 const fnParams = Array.isArray(value) ? value : [value]; 33197 if (eagerParams) { 33198 fnParams.push(...eagerParams); 33199 } 33200 if (name) { 33201 // We want the property name to always be the first function parameter. 33202 fnParams.unshift(literal(name)); 33203 } 33204 return fnParams; 33205 }; 33206} 33207/** Name of the global variable that is used to determine if we use Closure translations or not */ 33208const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode'; 33209/** 33210 * Generate statements that define a given translation message. 33211 * 33212 * ``` 33213 * var I18N_1; 33214 * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) { 33215 * var MSG_EXTERNAL_XXX = goog.getMsg( 33216 * "Some message with {$interpolation}!", 33217 * { "interpolation": "\uFFFD0\uFFFD" } 33218 * ); 33219 * I18N_1 = MSG_EXTERNAL_XXX; 33220 * } 33221 * else { 33222 * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`; 33223 * } 33224 * ``` 33225 * 33226 * @param message The original i18n AST message node 33227 * @param variable The variable that will be assigned the translation, e.g. `I18N_1`. 33228 * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`. 33229 * @param params Object mapping placeholder names to their values (e.g. 33230 * `{ "interpolation": "\uFFFD0\uFFFD" }`). 33231 * @param transformFn Optional transformation function that will be applied to the translation (e.g. 33232 * post-processing). 33233 * @returns An array of statements that defined a given translation. 33234 */ 33235function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) { 33236 // Sort the map entries in the compiled output. This makes it easy to acheive identical output in 33237 // the template pipeline compiler. 33238 params = Object.fromEntries(Object.entries(params).sort()); 33239 const statements = [declareI18nVariable(variable), ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, params), createLocalizeStatements(variable, message, formatI18nPlaceholderNamesInMap(params, /* useCamelCase */false)))]; 33240 if (transformFn) {
33241 statements.push(new ExpressionStatement(variable.set(transformFn(variable)))); 33242 } 33243 return statements; 33244} 33245/** 33246 * Create the expression that will be used to guard the closure mode block 33247 * It is equivalent to: 33248 * 33249 * ``` 33250 * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode 33251 * ``` 33252 */ 33253function createClosureModeGuard() { 33254 return typeofExpr(variable(NG_I18N_CLOSURE_MODE)).notIdentical(literal('undefined', STRING_TYPE)).and(variable(NG_I18N_CLOSURE_MODE)); 33255} 33256 33257/** 33258 * Translates query flags into `TQueryFlags` type in 33259 * packages/core/src/render3/interfaces/query.ts 33260 * @param query 33261 */ 33262function toQueryFlags(query) { 33263 return (query.descendants ? 1 /* QueryFlags.descendants */ : 0 /* QueryFlags.none */) | (query.static ? 2 /* QueryFlags.isStatic */ : 0 /* QueryFlags.none */) | (query.emitDistinctChangesOnly ? 4 /* QueryFlags.emitDistinctChangesOnly */ : 0 /* QueryFlags.none */); 33264} 33265function getQueryPredicate(query, constantPool) { 33266 if (Array.isArray(query.predicate)) { 33267 let predicate = []; 33268 query.predicate.forEach(selector => { 33269 // Each item in predicates array may contain strings with comma-separated refs 33270 // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them 33271 // as separate array entities 33272 const selectors = selector.split(',').map(token => literal(token.trim())); 33273 predicate.push(...selectors); 33274 }); 33275 return constantPool.getConstLiteral(literalArr(predicate), true); 33276 } else { 33277 // The original predicate may have been wrapped in a `forwardRef()` call. 33278 switch (query.predicate.forwardRef) { 33279 case 0 /* ForwardRefHandling.None */: 33280 case 2 /* ForwardRefHandling.Unwrapped */: 33281 return query.predicate.expression; 33282 case 1 /* ForwardRefHandling.Wrapped */: 33283 return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]); 33284 } 33285 } 33286} 33287function createQueryCreateCall(query, constantPool, queryTypeFns, prependParams) { 33288 const parameters = []; 33289 if (prependParams !== undefined) { 33290 parameters.push(...prependParams); 33291 } 33292 if (query.isSignal) { 33293 parameters.push(new ReadPropExpr(variable(CONTEXT_NAME), query.propertyName)); 33294 } 33295 parameters.push(getQueryPredicate(query, constantPool), literal(toQueryFlags(query))); 33296 if (query.read) { 33297 parameters.push(query.read); 33298 } 33299 const queryCreateFn = query.isSignal ? queryTypeFns.signalBased : queryTypeFns.nonSignal; 33300 return importExpr(queryCreateFn).callFn(parameters); 33301} 33302const queryAdvancePlaceholder = Symbol('queryAdvancePlaceholder'); 33303/** 33304 * Collapses query advance placeholders in a list of statements. 33305 * 33306 * This allows for less generated code because multiple sibling query advance 33307 * statements can be collapsed into a single call with the count as argument. 33308 * 33309 * e.g. 33310 * 33311 * ```ts 33312 * bla(); 33313 * queryAdvance(); 33314 * queryAdvance(); 33315 * bla(); 33316 * ``` 33317 * 33318 * --> will turn into 33319 * 33320 * ``` 33321 * bla(); 33322 * queryAdvance(2); 33323 * bla(); 33324 * ``` 33325 */ 33326function collapseAdvanceStatements(statements) { 33327 const result = []; 33328 let advanceCollapseCount = 0; 33329 const flushAdvanceCount = () => { 33330 if (advanceCollapseCount > 0) { 33331 result.unshift(importExpr(Identifiers.queryAdvance).callFn(advanceCollapseCount === 1 ? [] : [literal(advanceCollapseCount)]).toStmt()); 33332 advanceCollapseCount = 0; 33333 } 33334 }; 33335 // Iterate through statements in reverse and collapse advance placeholders. 33336 for (let i = statements.length - 1; i >= 0; i--) { 33337 const st = statements[i]; 33338 if (st === queryAdvancePlaceholder) { 33339 advanceCollapseCount++; 33340 } else { 33341 flushAdvanceCount(); 33342 result.unshift(st); 33343 } 33344 } 33345 flushAdvanceCount(); 33346 return result; 33347} 33348// Define and update any view queries 33349function createViewQueriesFunction(viewQueries, constantPool, name) { 33350 const createStatements = []; 33351 const updateStatements = []; 33352 const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME); 33353 viewQueries.forEach(query => { 33354 // creation call, e.g. r3.viewQuery(somePredicate, true) or 33355 // r3.viewQuerySignal(ctx.prop, somePredicate, true); 33356 const queryDefinitionCall = createQueryCreateCall(query, constantPool, { 33357 signalBased: Identifiers.viewQuerySignal, 33358 nonSignal: Identifiers.viewQuery 33359 }); 33360 createStatements.push(queryDefinitionCall.toStmt()); 33361 // Signal queries update lazily and we just advance the index. 33362 if (query.isSignal) { 33363 updateStatements.push(queryAdvancePlaceholder); 33364 return; 33365 } 33366 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp)); 33367 const temporary = tempAllocator(); 33368 const getQueryList = importExpr(Identifiers.loadQuery).callFn([]); 33369 const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]); 33370 const updateDirective = variable(CONTEXT_NAME).prop(query.propertyName).set(query.first ? temporary.prop('first') : temporary); 33371 updateStatements.push(refresh.and(updateDirective).toStmt()); 33372 }); 33373 const viewQueryFnName = name ? `${name}_Query` : null; 33374 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [renderFlagCheckIfStmt(1 /* core.RenderFlags.
33374Create */, createStatements), renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements))], INFERRED_TYPE, null, viewQueryFnName); 33375} 33376// Define and update any content queries 33377function createContentQueriesFunction(queries, constantPool, name) { 33378 const createStatements = []; 33379 const updateStatements = []; 33380 const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME); 33381 for (const query of queries) { 33382 // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null) or 33383 // r3.contentQuerySignal(dirIndex, propName, somePredicate, <flags>, <read>). 33384 createStatements.push(createQueryCreateCall(query, constantPool, { 33385 nonSignal: Identifiers.contentQuery, 33386 signalBased: Identifiers.contentQuerySignal 33387 }, /* prependParams */[variable('dirIndex')]).toStmt()); 33388 // Signal queries update lazily and we just advance the index. 33389 if (query.isSignal) { 33390 updateStatements.push(queryAdvancePlaceholder); 33391 continue; 33392 } 33393 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp)); 33394 const temporary = tempAllocator(); 33395 const getQueryList = importExpr(Identifiers.loadQuery).callFn([]); 33396 const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]); 33397 const updateDirective = variable(CONTEXT_NAME).prop(query.propertyName).set(query.first ? temporary.prop('first') : temporary); 33398 updateStatements.push(refresh.and(updateDirective).toStmt()); 33399 } 33400 const contentQueriesFnName = name ? `${name}_ContentQueries` : null; 33401 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null), new FnParam('dirIndex', null)], [renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements), renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements))], INFERRED_TYPE, null, contentQueriesFnName); 33402} 33403 33404// This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required" 33405// If there is a match, the first matching group will contain the attribute name to bind. 33406const ATTR_REGEX = /attr\.([^\]]+)/;
33407const COMPONENT_VARIABLE = '%COMP%'; 33408const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; 33409const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; 33410function baseDirectiveFields(meta, constantPool, bindingParser) { 33411 const definitionMap = new DefinitionMap(); 33412 const selectors = parseSelectorToR3Selector(meta.selector); 33413 // e.g. `type: MyDirective` 33414 definitionMap.set('type', meta.type.value); 33415 // e.g. `selectors: [['', 'someDir', '']]` 33416 if (selectors.length > 0) { 33417 definitionMap.set('selectors', asLiteral(selectors)); 33418 } 33419 if (meta.queries.length > 0) { 33420 // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... } 33421 definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name)); 33422 } 33423 if (meta.viewQueries.length) { 33424 definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name)); 33425 } 33426 // e.g. `hostBindings: (rf, ctx) => { ... } 33427 definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap)); 33428 // e.g 'inputs: {a: 'a'}` 33429 definitionMap.set('inputs', conditionallyCreateDirectiveBindingLiteral(meta.inputs, true)); 33430 // e.g 'outputs: {a: 'a'}` 33431 definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs)); 33432 if (meta.exportAs !== null) { 33433 definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e)))); 33434 } 33435 if (meta.isStandalone) { 33436 definitionMap.set('standalone', literal(true)); 33437 } 33438 if (meta.isSignal) { 33439 definitionMap.set('signals', literal(true)); 33440 } 33441 return definitionMap; 33442} 33443/** 33444 * Add features to the definition map. 33445 */ 33446function addFeatures(definitionMap, meta) { 33447 // e.g. `features: [NgOnChangesFeature]` 33448 const features = []; 33449 const providers = meta.providers; 33450 const viewProviders = meta.viewProviders; 33451 const inputKeys = Object.keys(meta.inputs); 33452 if (providers || viewProviders) { 33453 const args = [providers || new LiteralArrayExpr([])]; 33454 if (viewProviders) { 33455 args.push(viewProviders); 33456 } 33457 features.push(importExpr(Identifiers.ProvidersFeature).callFn(args)); 33458 } 33459 for (const key of inputKeys) { 33460 if (meta.inputs[key].transformFunction !== null) { 33461 features.push(importExpr(Identifiers.InputTransformsFeatureFeature)); 33462 break; 33463 } 33464 } 33465 // Note: host directives feature needs to be inserted before the 33466 // inheritance feature to ensure the correct execution order. 33467 if (meta.hostDirectives?.length) { 33468 features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)])); 33469 } 33470 if (meta.usesInheritance) { 33471 features.push(importExpr(Identifiers.InheritDefinitionFeature)); 33472 } 33473 if (meta.fullInheritance) { 33474 features.push(importExpr(Identifiers.CopyDefinitionFeature)); 33475 } 33476 if (meta.lifecycle.usesOnChanges) { 33477 features.push(importExpr(Identifiers.NgOnChangesFeature)); 33478 } 33479 // TODO: better way of differentiating component vs directive metadata. 33480 if (meta.hasOwnProperty('template') && meta.isStandalone) { 33481 features.push(importExpr(Identifiers.StandaloneFeature)); 33482 } 33483 if (features.length) { 33484 definitionMap.set('features', literalArr(features)); 33485 } 33486} 33487/** 33488 * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`. 33489 */ 33490function compileDirectiveFromMetadata(meta, constantPool, bindingParser) { 33491 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser); 33492 addFeatures(definitionMap, meta); 33493 const expression = importExpr(Identifiers.defineDirective).callFn([definitionMap.toLiteralMap()], undefined, true); 33494 const type = createDirectiveType(meta); 33495 return { 33496 expression, 33497 type, 33498 statements: [] 33499 }; 33500} 33501/** 33502 * Creates an AST for a function that contains dynamic imports representing 33503 * deferrable dependencies. 33504 */ 33505function createDeferredDepsFunction(constantPool, name, deps) { 33506 // This defer block has deps for which we need to generate dynamic imports. 33507 const dependencyExp = []; 33508 for (const [symbolName, { 33509 importPath, 33510 isDefaultImport 33511 }] of deps) { 33512 // Callback function, e.g. `m () => m.MyCmp;`. 33513 const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName)); 33514 // Dynamic import, e.g. `import('./a').then(...)`. 33515 const importExpr = new DynamicImportExpr(importPath).prop('then').callFn([innerFn]); 33516 dependencyExp.push(importExpr); 33517 } 33518 const depsFnExpr = arrowFn([], literalArr(dependencyExp)); 33519 constantPool.statements.push(depsFnExpr.toDeclStmt(name, StmtModifier.Final)); 33520 return variable(name); 33521} 33522/** 33523 * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`. 33524 */ 33525function compileComponentFromMetadata(meta, constantPool, bindingParser) { 33526 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser); 33527 addFeatures(definitionMap, meta); 33528 const selector = meta.selector && CssSelector.parse(meta.selector); 33529 const firstSelector = selector && selector[0]; 33530 // e.g. `attr: ["class", ".my.app"]` 33531 // This is optional an only included if the first selector of a component specifies attributes. 33532 if (firstSelector) { 33533 const selectorAttributes = firstSelector.getAttrs(); 33534 if (selectorAttributes.length) { 33535 definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))), /* forceShared */true)); 33536 } 33537 } 33538 // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}` 33539 const templateTypeName = meta.name; 33540 const templateName = templateTypeName ? `${templateTypeName}_Template` : null; 33541 let allDeferrableDepsFn = null; 33542 if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 && meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) { 33543 const fnName = `${templateTypeName}_DeferFn`; 33544 allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes); 33545 } 33546 // Template compilation is currently conditional as we're in the process of rewriting it. 33547 if (!USE_TEMPLATE_PIPELINE && !meta.useTemplatePipeline) { 33548 // This is the main path currently used in compilation, which compiles the template with the 33549 // legacy `TemplateDefinitionBuilder`. 33550 const template = meta.template; 33551 const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, new Map(), allDeferrableDepsFn); 33552 const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []); 33553 // We need to provide this so that dynamically generated components know what 33554 // projected content blocks to pass through to the component when it is 33555 // instantiated. 33556 const ngContentSelectors = templateBuilder.getNgContentSelectors(); 33557 if (ngContentSelectors) { 33558 definitionMap.set('ngContentSelectors', ngContentSelectors); 33559 } 33560 // e.g. `decls: 2` 33561 // definitionMap.set('decls', o.literal(tpl.root.decls!)); 33562 definitionMap.set('decls', literal(templateBuilder.getConstCount())); 33563 // e.g. `vars: 2` 33564 // definitionMap.set('vars', o.literal(tpl.root.vars!)); 33565 definitionMap.set('vars', literal(templateBuilder.getVarCount())); 33566 // Generate `consts` section of ComponentDef: 33567 // - either as an array: 33568 // `consts: [['one', 'two'], ['three', 'four']]` 33569 // - or as a factory function in case additional statements are present (to support i18n): 33570 // `consts: () => { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; 33571 // }` 33572 const { 33573 constExpressions, 33574 prepareStatements 33575 } = templateBuilder.getConsts(); 33576 if (constExpressions.length > 0) { 33577 let constsExpr = literalArr(constExpressions); 33578 // Prepare statements are present - turn `consts` into a function. 33579 if (prepareStatements.length > 0) { 33580 constsExpr = arrowFn([], [...prepareStatements, new ReturnStatement
33580(constsExpr)]); 33581 } 33582 definitionMap.set('consts', constsExpr); 33583 } 33584 definitionMap.set('template', templateFunctionExpression); 33585 } else { 33586 // This path compiles the template using the prototype template pipeline. First the template is 33587 // ingested into IR: 33588 const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn); 33589 // Then the IR is transformed to prepare it for cod egeneration. 33590 transform(tpl, CompilationJobKind.Tmpl); 33591 // Finally we emit the template function: 33592 const templateFn = emitTemplateFn(tpl, constantPool); 33593 if (tpl.contentSelectors !== null) { 33594 definitionMap.set('ngContentSelectors', tpl.contentSelectors); 33595 } 33596 definitionMap.set('decls', literal(tpl.root.decls)); 33597 definitionMap.set('vars', literal(tpl.root.vars)); 33598 if (tpl.consts.length > 0) { 33599 if (tpl.constsInitializers.length > 0) { 33600 definitionMap.set('consts', arrowFn([], [...tpl.constsInitializers, new ReturnStatement
33600(literalArr(tpl.consts))])); 33601 } else { 33602 definitionMap.set('consts', literalArr(tpl.consts)); 33603 } 33604 } 33605 definitionMap.set('template', templateFn); 33606 } 33607 if (meta.declarationListEmitMode !== 3 /* DeclarationListEmitMode.RuntimeResolved */ && meta.declarations.length > 0) { 33608 definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode)); 33609 } else if (meta.declarationListEmitMode === 3 /* DeclarationListEmitMode.RuntimeResolved */) { 33610 const args = [meta.type.value]; 33611 if (meta.rawImports) { 33612 args.push(meta.rawImports); 33613 } 33614 definitionMap.set('dependencies', importExpr(Identifiers.getComponentDepsFactory).callFn(args)); 33615 } 33616 if (meta.encapsulation === null) { 33617 meta.encapsulation = ViewEncapsulation.Emulated; 33618 } 33619 // e.g. `styles: [str1, str2]` 33620 if (meta.styles && meta.styles.length) { 33621 const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ? compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) : meta.styles; 33622 const styleNodes = styleValues.reduce((result, style) => { 33623 if (style.trim().length > 0) { 33624 result.push(constantPool.getConstLiteral(literal(style))); 33625 } 33626 return result; 33627 }, []); 33628 if (styleNodes.length > 0) { 33629 definitionMap.set('styles', literalArr(styleNodes)); 33630 } 33631 } else if (meta.encapsulation === ViewEncapsulation.Emulated) { 33632 // If there is no style, don't generate css selectors on elements 33633 meta.encapsulation = ViewEncapsulation.None; 33634 } 33635 // Only set view encapsulation if it's not the default value 33636 if (meta.encapsulation !== ViewEncapsulation.Emulated) { 33637 definitionMap.set('encapsulation', literal(meta.encapsulation)); 33638 } 33639 // e.g. `animation: [trigger('123', [])]` 33640 if (meta.animations !== null) { 33641 definitionMap.set('data', literalMap([{ 33642 key: 'animation', 33643 value: meta.animations, 33644 quoted: false 33645 }])); 33646 } 33647 // Setting change detection flag 33648 if (meta.changeDetection !== null) { 33649 if (typeof meta.changeDetection === 'number' && meta.changeDetection !== ChangeDetectionStrategy.Default) { 33650 // changeDetection is resolved during analysis. Only set it if not the default. 33651 definitionMap.set('changeDetection', literal(meta.changeDetection)); 33652 } else if (typeof meta.changeDetection === 'object') { 33653 // changeDetection is not resolved during analysis (e.g., we are in local compilation mode). 33654 // So place it as is. 33655 definitionMap.set('changeDetection', meta.changeDetection); 33656 } 33657 } 33658 const expression = importExpr(Identifiers.defineComponent).callFn([definitionMap.toLiteralMap()], undefined, true); 33659 const type = createComponentType(meta); 33660 return { 33661 expression, 33662 type, 33663 statements: [] 33664 }; 33665} 33666/** 33667 * Creates the type specification from the component meta. This type is inserted into .d.ts files 33668 * to be consumed by upstream compilations. 33669 */ 33670function createComponentType(meta) { 33671 const typeParams = createBaseDirectiveTypeParams(meta); 33672 typeParams.push(stringArrayAsType(meta.template.ngContentSelectors)); 33673 typeParams.push(expressionType(literal(meta.isStandalone))); 33674 typeParams.push(createHostDirectivesType(meta)); 33675 // TODO(signals): Always include this metadata starting with v17. Right 33676 // now Angular v16.0.x does not support this field and library distributions 33677 // would then be incompatible with v16.0.x framework users. 33678 if (meta.isSignal) { 33679 typeParams.push(expressionType(literal(meta.isSignal))); 33680 } 33681 return expressionType(importExpr(Identifiers.ComponentDeclaration, typeParams)); 33682} 33683/** 33684 * Compiles the array literal of declarations into an expression according to the provided emit 33685 * mode. 33686 */ 33687function compileDeclarationList(list, mode) { 33688 switch (mode) { 33689 case 0 /* DeclarationListEmitMode.Direct */: 33690 // directives: [MyDir], 33691 return list; 33692 case 1 /* DeclarationListEmitMode.Closure */: 33693 // directives: function () { return [MyDir]; } 33694 return arrowFn([], list); 33695 case 2 /* DeclarationListEmitMode.ClosureResolved */: 33696 // directives: function () { return [MyDir].map(ng.resolveForwardRef); } 33697 const resolvedList = list.prop('map').callFn([importExpr(Identifiers.resolveForwardRef)]); 33698 return arrowFn([], resolvedList); 33699 case 3 /* DeclarationListEmitMode.RuntimeResolved */: 33700 throw new Error(`Unsupported with an array of pre-resolved dependencies`); 33701 } 33702} 33703function convertAttributesToExpressions(attributes) { 33704 const values = []; 33705 for (let key of Object.getOwnPropertyNames(attributes)) { 33706 const value = attributes[key]; 33707 values.push(literal(key), value); 33708 } 33709 return values; 33710} 33711function stringAsType(str) { 33712 return expressionType(literal(str)); 33713} 33714function stringMapAsLiteralExpression(map) { 33715 const mapValues = Object.keys(map).map(key => { 33716 const value = Array.isArray(map[key]) ? map[key][0] : map[key]; 33717 return { 33718 key, 33719 value: literal(value), 33720 quoted: true 33721 }; 33722 });
33723 return literalMap(mapValues); 33724} 33725function stringArrayAsType(arr) { 33726 return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) : NONE_TYPE; 33727} 33728function createBaseDirectiveTypeParams(meta) { 33729 // On the type side, remove newlines from the selector as it will need to fit into a TypeScript 33730 // string literal, which must be on one line. 33731 const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null; 33732 return [typeWithParameters(meta.type.type, meta.typeArgumentCount), selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE, meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE, expressionType(getInputsTypeExpression(meta)), expressionType(stringMapAsLiteralExpression(meta.outputs)), stringArrayAsType(meta.queries.map(q => q.propertyName))]; 33733} 33734function getInputsTypeExpression(meta) { 33735 return literalMap(Object.keys(meta.inputs).map(key => { 33736 const value = meta.inputs[key]; 33737 const values = [{ 33738 key: 'alias', 33739 value: literal(value.bindingPropertyName), 33740 quoted: true 33741 }, { 33742 key: 'required', 33743 value: literal(value.required), 33744 quoted: true 33745 }]; 33746 // TODO(legacy-partial-output-inputs): Consider always emitting this information, 33747 // or leaving it as is. 33748 if (value.isSignal) { 33749 values.push({ 33750 key: 'isSignal', 33751 value: literal(value.isSignal), 33752 quoted: true 33753 }); 33754 } 33755 return { 33756 key, 33757 value: literalMap(values), 33758 quoted: true 33759 }; 33760 })); 33761} 33762/** 33763 * Creates the type specification from the directive meta. This type is inserted into .d.ts files 33764 * to be consumed by upstream compilations. 33765 */ 33766function createDirectiveType(meta) { 33767 const typeParams = createBaseDirectiveTypeParams(meta); 33768 // Directives have no NgContentSelectors slot, but instead express a `never` type 33769 // so that future fields align. 33770 typeParams.push(NONE_TYPE); 33771 typeParams.push(expressionType(literal(meta.isStandalone))); 33772 typeParams.push(createHostDirectivesType(meta)); 33773 // TODO(signals): Always include this metadata starting with v17. Right 33774 // now Angular v16.0.x does not support this field and library distributions 33775 // would then be incompatible with v16.0.x framework users. 33776 if (meta.isSignal) { 33777 typeParams.push(expressionType(literal(meta.isSignal))); 33778 } 33779 return expressionType(importExpr(Identifiers.DirectiveDeclaration, typeParams)); 33780} 33781// Return a host binding function or null if one is not necessary. 33782function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) { 33783 const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan); 33784 // Calculate host event bindings 33785 const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan); 33786 if (USE_TEMPLATE_PIPELINE || hostBindingsMetadata.useTemplatePipeline) { 33787 // The parser for host bindings treats class and style attributes specially -- they are 33788 // extracted into these separate fields. This is not the case for templates, so the compiler can 33789 // actually already handle these special attributes internally. Therefore, we just drop them 33790 // into the attributes map. 33791 if (hostBindingsMetadata.specialAttributes.styleAttr) { 33792 hostBindingsMetadata.attributes['style'] = literal(hostBindingsMetadata.specialAttributes.styleAttr); 33793 } 33794 if (hostBindingsMetadata.specialAttributes.classAttr) { 33795 hostBindingsMetadata.attributes['class'] = literal(hostBindingsMetadata.specialAttributes.classAttr); 33796 } 33797 const hostJob = ingestHostBinding({ 33798 componentName: name, 33799 componentSelector: selector, 33800 properties: bindings, 33801 events: eventBindings, 33802 attributes: hostBindingsMetadata.attributes 33803 }, bindingParser, constantPool); 33804 transform(hostJob, CompilationJobKind.Host); 33805 definitionMap.set('hostAttrs', hostJob.root.attributes); 33806 const varCount = hostJob.root.vars; 33807 if (varCount !== null && varCount > 0) { 33808 definitionMap.set('hostVars', literal(varCount)); 33809 } 33810 return emitHostBindingFunction(hostJob); 33811 } 33812 let bindingId = 0; 33813 const getNextBindingId = () => `${bindingId++}`; 33814 const bindingContext = variable(CONTEXT_NAME); 33815 const styleBuilder = new StylingBuilder(bindingContext); 33816 const { 33817 styleAttr, 33818 classAttr 33819 }
33819 = hostBindingsMetadata.specialAttributes; 33820 if (styleAttr !== undefined) { 33821 styleBuilder.registerStyleAttr(styleAttr); 33822 } 33823 if (classAttr !== undefined) { 33824 styleBuilder.registerClassAttr(classAttr); 33825 } 33826 const createInstructions = []; 33827 const updateInstructions = []; 33828 const updateVariables = []; 33829 const hostBindingSourceSpan = typeSourceSpan; 33830 if (eventBindings && eventBindings.length) { 33831 createInstructions.push(...createHostListeners(eventBindings, name)); 33832 } 33833 // Calculate the host property bindings 33834 const allOtherBindings = []; 33835 // We need to calculate the total amount of binding slots required by 33836 // all the instructions together before any value conversions happen. 33837 // Value conversions may require additional slots for interpolation and 33838 // bindings with pipes. These calculates happen after this block. 33839 let totalHostVarsCount = 0; 33840 bindings && bindings.forEach(binding => { 33841 const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan); 33842 if (stylingInputWasSet) { 33843 totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED; 33844 } else { 33845 allOtherBindings.push(binding); 33846 totalHostVarsCount++; 33847 } 33848 }); 33849 let valueConverter; 33850 const getValueConverter = () => { 33851 if (!valueConverter) { 33852 const hostVarsCountFn = numSlots => { 33853 const originalVarsCount = totalHostVarsCount; 33854 totalHostVarsCount += numSlots; 33855 return originalVarsCount; 33856 }; 33857 valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), 33858 // new nodes are illegal here 33859 hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here 33860 } 33861 return valueConverter; 33862 }; 33863 const propertyBindings = []; 33864 const attributeBindings = []; 33865 const syntheticHostBindings = []; 33866 for (const binding of allOtherBindings) { 33867 // resolve literal arrays and literal objects 33868 const value = binding.expression.visit(getValueConverter()); 33869 const bindingExpr = bindingFn(bindingContext, value, getNextBindingId); 33870 const { 33871 bindingName, 33872 instruction, 33873 isAttribute 33874 } = getBindingNameAndInstruction(binding); 33875 const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute).filter(context => context !== SecurityContext.NONE); 33876 let sanitizerFn = null; 33877 if (securityContexts.length) { 33878 if (securityContexts.length === 2 && securityContexts.indexOf(SecurityContext.URL) > -1 && securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) { 33879 // Special case for some URL attributes (such as "src" and "href") that may be a part 33880 // of different security contexts. In this case we use special sanitization function and 33881 // select the actual sanitizer at runtime based on a tag name that is provided while 33882 // invoking sanitization function. 33883 sanitizerFn = importExpr(Identifiers.sanitizeUrlOrResourceUrl); 33884 } else { 33885 sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute); 33886 } 33887 } 33888 const instructionParams = [literal(bindingName), bindingExpr.currValExpr]; 33889 if (sanitizerFn) { 33890 instructionParams.push(sanitizerFn); 33891 } else { 33892 // If there was no sanitization function found based on the security context 33893 // of an attribute/property binding - check whether this attribute/property is 33894 // one of the security-sensitive <iframe> attributes. 33895 // Note: for host bindings defined on a directive, we do not try to find all 33896 // possible places where it can be matched, so we can not determine whether 33897 // the host element is an <iframe>. In this case, if an attribute/binding 33898 // name is in the `IFRAME_SECURITY_SENSITIVE_ATTRS` set - append a validation 33899 // function, which would be invoked at runtime and would have access to the 33900 // underlying DOM element, check if it's an <iframe> and if so - runs extra checks. 33901 if (isIframeSecuritySensitiveAttr(bindingName)) { 33902 instructionParams.push(importExpr(Identifiers.validateIframeAttribute)); 33903 } 33904 }
33905 updateVariables.push(...bindingExpr.stmts); 33906 if (instruction === Identifiers.hostProperty) { 33907 propertyBindings.push(instructionParams); 33908 } else if (instruction === Identifiers.attribute) { 33909 attributeBindings.push(instructionParams); 33910 } else if (instruction === Identifiers.syntheticHostProperty) { 33911 syntheticHostBindings.push(instructionParams); 33912 } else { 33913 updateInstructions.push({ 33914 reference: instruction, 33915 paramsOrFn: instructionParams, 33916 span: null 33917 }); 33918 } 33919 } 33920 for (const bindingParams of propertyBindings) { 33921 updateInstructions.push({ 33922 reference: Identifiers.hostProperty, 33923 paramsOrFn: bindingParams, 33924 span: null 33925 }); 33926 } 33927 for (const bindingParams of attributeBindings) { 33928 updateInstructions.push({ 33929 reference: Identifiers.attribute, 33930 paramsOrFn: bindingParams, 33931 span: null 33932 }); 33933 } 33934 for (const bindingParams of syntheticHostBindings) { 33935 updateInstructions.push({ 33936 reference: Identifiers.syntheticHostProperty, 33937 paramsOrFn: bindingParams, 33938 span: null 33939 }); 33940 } 33941 // since we're dealing with directives/components and both have hostBinding 33942 // functions, we need to generate a special hostAttrs instruction that deals 33943 // with both the assignment of styling as well as static attributes to the host 33944 // element. The instruction below will instruct all initial styling (styling 33945 // that is inside of a host binding within a directive/component) to be attached 33946 // to the host element alongside any of the provided host attributes that were 33947 // collected earlier. 33948 const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes); 33949 styleBuilder.assignHostAttrs(hostAttrs, definitionMap); 33950 if (styleBuilder.hasBindings) { 33951 // finally each binding that was registered in the statement above will need to be added to 33952 // the update block of a component/directive templateFn/hostBindingsFn so that the bindings 33953 // are evaluated and updated for the element. 33954 styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => { 33955 for (const call of instruction.calls) { 33956 // we subtract a value of `1` here because the binding slot was already allocated 33957 // at the top of this method when all the input bindings were counted. 33958 totalHostVarsCount += Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0); 33959 const { 33960 params, 33961 stmts 33962 } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId); 33963 updateVariables.push(...stmts); 33964 updateInstructions.push({ 33965 reference: instruction.reference, 33966 paramsOrFn: params, 33967 span: null 33968 }); 33969 } 33970 }); 33971 } 33972 if (totalHostVarsCount) { 33973 definitionMap.set('hostVars', literal(totalHostVarsCount)); 33974 } 33975 if (createInstructions.length > 0 || updateInstructions.length > 0) { 33976 const hostBindingsFnName = name ? `${name}_HostBindings` : null; 33977 const statements = []; 33978 if (createInstructions.length > 0) { 33979 statements.push(renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, getInstructionStatements(createInstructions))); 33980 } 33981 if (updateInstructions.length > 0) { 33982 statements.push(renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateVariables.concat(getInstructionStatements(updateInstructions)))); 33983 } 33984 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName); 33985 } 33986 return null; 33987} 33988function bindingFn(implicit, value, getNextBindingIdFn) { 33989 return convertPropertyBinding(null, implicit, value, getNextBindingIdFn()); 33990} 33991function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) { 33992 const stmts = []; 33993 const params = call.params(value => { 33994 const result = bindingFn(bindingContext, value, getNextBindingIdFn); 33995 if (Array.isArray(result.stmts) && result.stmts.length > 0) { 33996 stmts.push(...result.stmts); 33997 } 33998 return result.currValExpr; 33999 }); 34000 return { 34001 params, 34002 stmts 34003 }; 34004} 34005function getBindingNameAndInstruction(binding) { 34006 let bindingName = binding.name; 34007 let instruction; 34008 // Check to see if this is an attr binding or a property binding 34009 const attrMatches = bindingName.match(ATTR_REGEX); 34010 if (attrMatches) { 34011 bindingName = attrMatches[1]; 34012 instruction = Identifiers.attribute; 34013 } else { 34014 if (binding.isAnimation) { 34015 bindingName = prepareSyntheticPropertyName(bindingName); 34016 // host bindings that have a synthetic property (e.g. @foo) should always be rendered 34017 // in the context of the component and not the parent. Therefore there is a special 34018 // compatibility instruction available for this purpose. 34019 instruction = Identifiers.syntheticHostProperty; 34020 } else { 34021 instruction = Identifiers.hostProperty; 34022 } 34023 } 34024 return { 34025 bindingName, 34026 instruction, 34027 isAttribute: !!attrMatches 34028 }; 34029} 34030function createHostListeners(eventBindings, name) { 34031 const listenerParams = []; 34032 const syntheticListenerParams = []; 34033 const instructions = []; 34034 for (const binding of eventBindings) { 34035 let bindingName = binding.name && sanitizeIdentifier(binding.name); 34036 const bindingFnName = binding.type === 1 /* ParsedEventType.Animation */ ? prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) : bindingName; 34037 const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null; 34038 const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName); 34039 if (binding.type == 1 /* ParsedEventType.Animation */) { 34040 syntheticListenerParams.push(params); 34041 } else { 34042 listenerParams.push(params); 34043 } 34044 } 34045 for (const params of syntheticListenerParams) { 34046 instructions.push({ 34047 reference: Identifiers.syntheticHostListener, 34048 paramsOrFn: params, 34049 span: null 34050 }); 34051 } 34052 for (const params of listenerParams) { 34053 instructions.push({ 34054 reference: Identifiers.listener, 34055 paramsOrFn: params, 34056 span: null 34057 }); 34058 } 34059 return instructions; 34060} 34061const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/; 34062function parseHostBindings(host) { 34063 const attributes = {}; 34064 const listeners = {}; 34065 const properties = {}; 34066 const specialAttributes = {}; 34067 for (const key of Object.keys(host)) { 34068 const value = host[key]; 34069 const matches = key.match(HOST_REG_EXP); 34070 if (matches === null) { 34071 switch (key) { 34072 case 'class': 34073 if (typeof value !== 'string') { 34074 // TODO(alxhub): make this a diagnostic. 34075 throw new Error(`Class binding must be string`); 34076 } 34077 specialAttributes.classAttr = value; 34078 break; 34079 case 'style': 34080 if (typeof value !== 'string') { 34081 // TODO(alxhub): make this a diagnostic. 34082 throw new Error(`Style binding must be string`); 34083 } 34084 specialAttributes.styleAttr = value; 34085 break; 34086 default: 34087 if (typeof value === 'string') { 34088 attributes[key] = literal(value); 34089 } else { 34090 attributes[key] = value; 34091 } 34092 } 34093 } else if (matches[1 /* HostBindingGroup.Binding */] != null) { 34094 if (typeof value !== 'string') { 34095 // TODO(alxhub): make this a diagnostic. 34096 throw new Error(`Property binding must be string`); 34097 } 34098 // synthetic properties (the ones that have a `@` as a prefix) 34099 // are still treated the same as regular properties. Therefore 34100 // there is no point in storing them in a separate map. 34101 properties[matches[1 /* HostBindingGroup.Binding */]] = value; 34102 } else if (matches[2 /* HostBindingGroup.Event */] != null) { 34103 if (typeof value !== 'string') { 34104 // TODO(alxhub): make this a diagnostic. 34105 throw new Error(`Event binding must be string`); 34106 } 34107 listeners[matches[2 /* HostBindingGroup.Event */]] = value; 34108 } 34109 } 34110 return { 34111 attributes, 34112 listeners, 34113 properties, 34114 specialAttributes 34115 }; 34116} 34117/** 34118 * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a 34119 * given set of host bindings has no errors. 34120 * 34121 * @param bindings set of host bindings to verify. 34122 * @param sourceSpan source span where host bindings were defined. 34123 * @returns array of errors associated with a given set of host bindings. 34124 */ 34125function verifyHostBindings(bindings, sourceSpan) { 34126 // TODO: abstract out host bindings verification logic and use it instead of 34127 // creating events and properties ASTs to detect errors (FW-996) 34128 const bindingParser = makeBindingParser(); 34129 bindingParser.createDirectiveHostEventAsts(bindings.listeners, sourceSpan); 34130 bindingParser.createBoundHostProperties(bindings.properties, sourceSpan); 34131 return bindingParser.errors; 34132} 34133function compileStyles(styles, selector, hostSelector) { 34134 const shadowCss = new ShadowCss(); 34135 return styles.map(style => { 34136 return shadowCss.shimCssText(style, selector, hostSelector); 34137 }); 34138} 34139/**
34140 * Encapsulates a CSS stylesheet with emulated view encapsulation. 34141 * This allows a stylesheet to be used with an Angular component that 34142 * is using the `ViewEncapsulation.Emulated` mode. 34143 * 34144 * @param style The content of a CSS stylesheet. 34145 * @returns The encapsulated content for the style. 34146 */ 34147function encapsulateStyle(style) { 34148 const shadowCss = new ShadowCss(); 34149 return shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR); 34150} 34151function createHostDirectivesType(meta) { 34152 if (!meta.hostDirectives?.length) { 34153 return NONE_TYPE; 34154 } 34155 return expressionType(literalArr(meta.hostDirectives.map(hostMeta => literalMap([{ 34156 key: 'directive', 34157 value: typeofExpr(hostMeta.directive.type), 34158 quoted: false 34159 }, { 34160 key: 'inputs', 34161 value: stringMapAsLiteralExpression(hostMeta.inputs || {}), 34162 quoted: false 34163 }, { 34164 key: 'outputs', 34165 value: stringMapAsLiteralExpression(hostMeta.outputs || {}), 34166 quoted: false 34167 }])))); 34168} 34169function createHostDirectivesFeatureArg(hostDirectives) { 34170 const expressions = []; 34171 let hasForwardRef = false; 34172 for (const current of hostDirectives) { 34173 // Use a shorthand if there are no inputs or outputs. 34174 if (!current.inputs && !current.outputs) { 34175 expressions.push(current.directive.type); 34176 } else { 34177 const keys = [{ 34178 key: 'directive', 34179 value: current.directive.type, 34180 quoted: false 34181 }]; 34182 if (current.inputs) { 34183 const inputsLiteral = createHostDirectivesMappingArray(current.inputs); 34184 if (inputsLiteral) { 34185 keys.push({ 34186 key: 'inputs', 34187 value: inputsLiteral, 34188 quoted: false 34189 }); 34190 } 34191 } 34192 if (current.outputs) { 34193 const outputsLiteral = createHostDirectivesMappingArray(current.outputs); 34194 if (outputsLiteral) { 34195 keys.push({ 34196 key: 'outputs', 34197 value: outputsLiteral, 34198 quoted: false 34199 }); 34200 } 34201 } 34202 expressions.push(literalMap(keys)); 34203 } 34204 if (current.isForwardReference) { 34205 hasForwardRef = true; 34206 } 34207 } 34208 // If there's a forward reference, we generate a `function() { return [HostDir] }`, 34209 // otherwise we can save some bytes by using a plain array, e.g. `[HostDir]`. 34210 return hasForwardRef ? new FunctionExpr([], [new ReturnStatement
34210(literalArr(expressions))]) : literalArr(expressions); 34211} 34212/** 34213 * Converts an input/output mapping object literal into an array where the even keys are the 34214 * public name of the binding and the odd ones are the name it was aliased to. E.g. 34215 * `{inputOne: 'aliasOne', inputTwo: 'aliasTwo'}` will become 34216 * `['inputOne', 'aliasOne', 'inputTwo', 'aliasTwo']`. 34217 * 34218 * This conversion is necessary, because hosts bind to the public name of the host directive and 34219 * keeping the mapping in an object literal will break for apps using property renaming. 34220 */ 34221function createHostDirectivesMappingArray(mapping) { 34222 const elements = []; 34223 for (const publicName in mapping) { 34224 if (mapping.hasOwnProperty(publicName)) { 34225 elements.push(literal(publicName), literal(mapping[publicName])); 34226 } 34227 } 34228 return elements.length > 0 ? literalArr(elements) : null; 34229} 34230 34231/** 34232 * Processes `Target`s with a given set of directives and performs a binding operation, which 34233 * returns an object similar to TypeScript's `ts.TypeChecker` that contains knowledge about the 34234 * target. 34235 */ 34236class R3TargetBinder { 34237 constructor(directiveMatcher) { 34238 this.directiveMatcher = directiveMatcher; 34239 } 34240 /** 34241 * Perform a binding operation on the given `Target` and return a `BoundTarget` which contains 34242 * metadata about the types referenced in the template. 34243 */ 34244 bind(target) { 34245 if (!target.template) { 34246 // TODO(alxhub): handle targets which contain things like HostBindings, etc. 34247 throw new Error('Binding without a template not yet supported'); 34248 } 34249 // First, parse the template into a `Scope` structure. This operation captures the syntactic 34250 // scopes in the template and makes them available for later use. 34251 const scope = Scope.apply(target.template); 34252 // Use the `Scope` to extract the entities present at every level of the template. 34253 const scopedNodeEntities = extractScopedNodeEntities(scope); 34254 // Next, perform directive matching on the template using the `DirectiveBinder`. This returns: 34255 // - directives: Map of nodes (elements & ng-templates) to the directives on them. 34256 // - bindings: Map of inputs, outputs, and attributes to the directive/element that claims 34257 // them. TODO(alxhub): handle multiple directives claiming an input/output/etc. 34258 // - references: Map of #references to their targets. 34259 const { 34260 directives, 34261 eagerDirectives, 34262 bindings, 34263 references 34264 } = DirectiveBinder.apply(target.template, this.directiveMatcher); 34265 // Finally, run the TemplateBinder to bind references, variables, and other entities within the 34266 // template. This extracts all the metadata that doesn't depend on directive matching. 34267 const { 34268 expressions, 34269 symbols, 34270 nestingLevel, 34271 usedPipes, 34272 eagerPipes, 34273 deferBlocks 34274 } = TemplateBinder.applyWithScope(target.template, scope); 34275 return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks); 34276 } 34277} 34278/** 34279 * Represents a binding scope within a template. 34280 * 34281 * Any variables, references, or other named entities declared within the template will 34282 * be captured and available by name in `namedEntities`. Additionally, child templates will 34283 * be analyzed and have their child `Scope`s available in `childScopes`. 34284 */ 34285class Scope { 34286 constructor(parentScope, rootNode) { 34287 this.parentScope = parentScope; 34288 this.rootNode = rootNode; 34289 /** 34290 * Named members of the `Scope`, such as `Reference`s or `Variable`s. 34291 */ 34292 this.namedEntities = new Map(); 34293 /** 34294 * Set of elements that belong to this scope. 34295 */ 34296 this.elementsInScope = new Set(); 34297 /** 34298 * Child `Scope`s for immediately nested `ScopedNode`s. 34299 */ 34300 this.childScopes = new Map(); 34301 this.isDeferred = parentScope !== null && parentScope.isDeferred ? true : rootNode instanceof DeferredBlock; 34302 } 34303 static newRootScope() { 34304 return new Scope(null, null); 34305 } 34306 /** 34307 * Process a template (either as a `Template` sub-template with variables, or a plain array of 34308 * template `Node`s) and construct its `Scope`. 34309 */ 34310 static apply(template) { 34311 const scope = Scope.newRootScope();
34312 scope.ingest(template); 34313 return scope; 34314 } 34315 /** 34316 * Internal method to process the scoped node and populate the `Scope`. 34317 */ 34318 ingest(nodeOrNodes) { 34319 if (nodeOrNodes instanceof Template) { 34320 // Variables on an <ng-template> are defined in the inner scope. 34321 nodeOrNodes.variables.forEach(node => this.visitVariable(node)); 34322 // Process the nodes of the template. 34323 nodeOrNodes.children.forEach(node => node.visit(this)); 34324 } else if (nodeOrNodes instanceof IfBlockBranch) { 34325 if (nodeOrNodes.expressionAlias !== null) { 34326 this.visitVariable(nodeOrNodes.expressionAlias); 34327 } 34328 nodeOrNodes.children.forEach(node => node.visit(this)); 34329 } else if (nodeOrNodes instanceof ForLoopBlock) { 34330 this.visitVariable(nodeOrNodes.item); 34331 Object.values(nodeOrNodes.contextVariables).forEach(v => this.visitVariable(v)); 34332 nodeOrNodes.children.forEach(node => node.visit(this)); 34333 } else if (nodeOrNodes instanceof SwitchBlockCase || nodeOrNodes instanceof ForLoopBlockEmpty || nodeOrNodes instanceof DeferredBlock || nodeOrNodes instanceof DeferredBlockError || nodeOrNodes instanceof DeferredBlockPlaceholder || nodeOrNodes instanceof DeferredBlockLoading) { 34334 nodeOrNodes.children.forEach(node => node.visit(this)); 34335 } else { 34336 // No overarching `Template` instance, so process the nodes directly. 34337 nodeOrNodes.forEach(node => node.visit(this)); 34338 } 34339 } 34340 visitElement(element) { 34341 // `Element`s in the template may have `Reference`s which are captured in the scope. 34342 element.references.forEach(node => this.visitReference(node)); 34343 // Recurse into the `Element`'s children. 34344 element.children.forEach(node => node.visit(this)); 34345 this.elementsInScope.add(element); 34346 } 34347 visitTemplate(template) { 34348 // References on a <ng-template> are defined in the outer scope, so capture them before 34349 // processing the template's child scope. 34350 template.references.forEach(node => this.visitReference(node)); 34351 // Next, create an inner scope and process the template within it. 34352 this.ingestScopedNode(template); 34353 } 34354 visitVariable(variable) { 34355 // Declare the variable if it's not already. 34356 this.maybeDeclare(variable); 34357 } 34358 visitReference(reference) { 34359 // Declare the variable if it's not already. 34360 this.maybeDeclare(reference); 34361 } 34362 visitDeferredBlock(deferred) { 34363 this.ingestScopedNode(deferred); 34364 deferred.placeholder?.visit(this); 34365 deferred.loading?.visit(this); 34366 deferred.error?.visit(this); 34367 } 34368 visitDeferredBlockPlaceholder(block) { 34369 this.ingestScopedNode(block); 34370 } 34371 visitDeferredBlockError(block) { 34372 this.ingestScopedNode(block); 34373 } 34374 visitDeferredBlockLoading(block) { 34375 this.ingestScopedNode(block); 34376 } 34377 visitSwitchBlock(block) { 34378 block.cases.forEach(node => node.visit(this)); 34379 } 34380 visitSwitchBlockCase(block) { 34381 this.ingestScopedNode(block); 34382 } 34383 visitForLoopBlock(block) { 34384 this.ingestScopedNode(block); 34385 block.empty?.visit(this); 34386 } 34387 visitForLoopBlockEmpty(block) { 34388 this.ingestScopedNode(block); 34389 } 34390 visitIfBlock(block) { 34391 block.branches.forEach(node => node.visit(this)); 34392 } 34393 visitIfBlockBranch(block) { 34394 this.ingestScopedNode(block); 34395 } 34396 // Unused visitors. 34397 visitContent(content) {} 34398 visitBoundAttribute(attr) {} 34399 visitBoundEvent(event) {} 34400 visitBoundText(text) {} 34401 visitText(text) {} 34402 visitTextAttribute(attr) {} 34403 visitIcu(icu) {} 34404 visitDeferredTrigger(trigger) {} 34405 visitUnknownBlock(block) {} 34406 maybeDeclare(thing) { 34407 // Declare something with a name, as long as that name isn't taken. 34408 if (!this.namedEntities.has(thing.name)) { 34409 this.namedEntities.set(thing.name, thing); 34410 } 34411 } 34412 /** 34413 * Look up a variable within this `Scope`. 34414 * 34415 * This can recurse into a parent `Scope` if it's available. 34416 */ 34417 lookup(name) { 34418 if (this.namedEntities.has(name)) { 34419 // Found in the local scope. 34420 return this.namedEntities.get(name); 34421 } else if (this.parentScope !== null) { 34422 // Not in the local scope, but there's a parent scope so check there. 34423 return this.parentScope.lookup(name); 34424 } else { 34425 // At the top level and it wasn't found. 34426 return null; 34427 } 34428 } 34429 /** 34430 * Get the child scope for a `ScopedNode`. 34431 * 34432 * This should always be defined. 34433 */ 34434 getChildScope(node) { 34435 const res = this.childScopes.get(node); 34436 if (res === undefined) { 34437 throw new Error(`Assertion error: child scope for ${node} not found`); 34438 } 34439 return res; 34440 } 34441 ingestScopedNode(node) { 34442 const scope = new Scope(this, node);
34443 scope.ingest(node); 34444 this.childScopes.set(node, scope); 34445 } 34446} 34447/** 34448 * Processes a template and matches directives on nodes (elements and templates). 34449 * 34450 * Usually used via the static `apply()` method. 34451 */ 34452class DirectiveBinder { 34453 constructor(matcher, directives, eagerDirectives, bindings, references) { 34454 this.matcher = matcher; 34455 this.directives = directives; 34456 this.eagerDirectives = eagerDirectives; 34457 this.bindings = bindings; 34458 this.references = references; 34459 // Indicates whether we are visiting elements within a `defer` block 34460 this.isInDeferBlock = false; 34461 } 34462 /** 34463 * Process a template (list of `Node`s) and perform directive matching against each node. 34464 * 34465 * @param template the list of template `Node`s to match (recursively). 34466 * @param selectorMatcher a `SelectorMatcher` containing the directives that are in scope for 34467 * this template. 34468 * @returns three maps which contain information about directives in the template: the 34469 * `directives` map which lists directives matched on each node, the `bindings` map which 34470 * indicates which directives claimed which bindings (inputs, outputs, etc), and the `references` 34471 * map which resolves #references (`Reference`s) within the template to the named directive or 34472 * template node. 34473 */ 34474 static apply(template, selectorMatcher) { 34475 const directives = new Map(); 34476 const bindings = new Map(); 34477 const references = new Map(); 34478 const eagerDirectives = []; 34479 const matcher = new DirectiveBinder(selectorMatcher, directives, eagerDirectives, bindings, references); 34480 matcher.ingest(template); 34481 return { 34482 directives, 34483 eagerDirectives, 34484 bindings, 34485 references 34486 }; 34487 } 34488 ingest(template) { 34489 template.forEach(node => node.visit(this)); 34490 } 34491 visitElement(element) { 34492 this.visitElementOrTemplate(element); 34493 } 34494 visitTemplate(template) { 34495 this.visitElementOrTemplate(template); 34496 } 34497 visitElementOrTemplate(node) { 34498 // First, determine the HTML shape of the node for the purpose of directive matching. 34499 // Do this by building up a `CssSelector` for the node. 34500 const cssSelector = createCssSelectorFromNode(node); 34501 // Next, use the `SelectorMatcher` to get the list of directives on the node. 34502 const directives = []; 34503 this.matcher.match(cssSelector, (_selector, results) => directives.push(...results)); 34504 if (directives.length > 0) { 34505 this.directives.set(node, directives); 34506 if (!this.isInDeferBlock) { 34507 this.eagerDirectives.push(...directives); 34508 } 34509 } 34510 // Resolve any references that are created on this node. 34511 node.references.forEach(ref => { 34512 let dirTarget = null; 34513 // If the reference expression is empty, then it matches the "primary" directive on the node 34514 // (if there is one). Otherwise it matches the host node itself (either an element or 34515 // <ng-template> node). 34516 if (ref.value.trim() === '') { 34517 // This could be a reference to a component if there is one. 34518 dirTarget = directives.find(dir => dir.isComponent) || null; 34519 } else { 34520 // This should be a reference to a directive exported via exportAs. 34521 dirTarget = directives.find(dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) || null; 34522 // Check if a matching directive was found. 34523 if (dirTarget === null) { 34524 // No matching directive was found - this reference points to an unknown target. Leave it 34525 // unmapped. 34526 return; 34527 } 34528 } 34529 if (dirTarget !== null) { 34530 // This reference points to a directive. 34531 this.references.set(ref, { 34532 directive: dirTarget, 34533 node 34534 }); 34535 } else { 34536 // This reference points to the node itself. 34537 this.references.set(ref, node); 34538 } 34539 }); 34540 const setAttributeBinding = (attribute, ioType) => { 34541 const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name)); 34542 const binding = dir !== undefined ? dir : node; 34543 this.bindings.set(attribute, binding); 34544 }; 34545 // Node inputs (bound attributes) and text attributes can be bound to an 34546 // input on a directive.
34547 node.inputs.forEach(input => setAttributeBinding(input, 'inputs')); 34548 node.attributes.forEach(attr => setAttributeBinding(attr, 'inputs')); 34549 if (node instanceof Template) { 34550 node.templateAttrs.forEach(attr => setAttributeBinding(attr, 'inputs')); 34551 } 34552 // Node outputs (bound events) can be bound to an output on a directive. 34553 node.outputs.forEach(output => setAttributeBinding(output, 'outputs')); 34554 // Recurse into the node's children. 34555 node.children.forEach(child => child.visit(this)); 34556 } 34557 visitDeferredBlock(deferred) { 34558 const wasInDeferBlock = this.isInDeferBlock; 34559 this.isInDeferBlock = true; 34560 deferred.children.forEach(child => child.visit(this)); 34561 this.isInDeferBlock = wasInDeferBlock; 34562 deferred.placeholder?.visit(this); 34563 deferred.loading?.visit(this); 34564 deferred.error?.visit(this); 34565 } 34566 visitDeferredBlockPlaceholder(block) { 34567 block.children.forEach(child => child.visit(this)); 34568 } 34569 visitDeferredBlockError(block) { 34570 block.children.forEach(child => child.visit(this)); 34571 } 34572 visitDeferredBlockLoading(block) { 34573 block.children.forEach(child => child.visit(this)); 34574 } 34575 visitSwitchBlock(block) { 34576 block.cases.forEach(node => node.visit(this)); 34577 } 34578 visitSwitchBlockCase(block) { 34579 block.children.forEach(node => node.visit(this)); 34580 } 34581 visitForLoopBlock(block) { 34582 block.item.visit(this); 34583 Object.values(block.contextVariables).forEach(v => v.visit(this)); 34584 block.children.forEach(node => node.visit(this)); 34585 block.empty?.visit(this); 34586 } 34587 visitForLoopBlockEmpty(block) { 34588 block.children.forEach(node => node.visit(this)); 34589 } 34590 visitIfBlock(block) { 34591 block.branches.forEach(node => node.visit(this)); 34592 } 34593 visitIfBlockBranch(block) { 34594 block.expressionAlias?.visit(this); 34595 block.children.forEach(node => node.visit(this)); 34596 } 34597 // Unused visitors. 34598 visitContent(content) {} 34599 visitVariable(variable) {} 34600 visitReference(reference) {} 34601 visitTextAttribute(attribute) {} 34602 visitBoundAttribute(attribute) {} 34603 visitBoundEvent(attribute) {} 34604 visitBoundAttributeOrEvent(node) {} 34605 visitText(text) {} 34606 visitBoundText(text) {} 34607 visitIcu(icu) {} 34608 visitDeferredTrigger(trigger) {} 34609 visitUnknownBlock(block) {} 34610} 34611/** 34612 * Processes a template and extract metadata about expressions and symbols within. 34613 * 34614 * This is a companion to the `DirectiveBinder` that doesn't require knowledge of directives matched 34615 * within the template in order to operate. 34616 * 34617 * Expressions are visited by the superclass `RecursiveAstVisitor`, with custom logic provided 34618 * by overridden methods from that visitor. 34619 */ 34620class TemplateBinder extends RecursiveAstVisitor { 34621 constructor(bindings, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, rootNode, level) { 34622 super(); 34623 this.bindings = bindings; 34624 this.symbols = symbols; 34625 this.usedPipes = usedPipes; 34626 this.eagerPipes = eagerPipes; 34627 this.deferBlocks = deferBlocks; 34628 this.nestingLevel = nestingLevel; 34629 this.scope = scope; 34630 this.rootNode = rootNode; 34631 this.level = level; 34632 // Save a bit of processing time by constructing this closure in advance. 34633 this.visitNode = node => node.visit(this); 34634 } 34635 // This method is defined to reconcile the type of TemplateBinder since both 34636 // RecursiveAstVisitor and Visitor define the visit() method in their 34637 // interfaces. 34638 visit(node, context) { 34639 if (node instanceof AST) { 34640 node.visit(this, context); 34641 } else { 34642 node.visit(this); 34643 } 34644 } 34645 /** 34646 * Process a template and extract metadata about expressions and symbols within. 34647 * 34648 * @param nodes the nodes of the template to process 34649 * @param scope the `Scope` of the template being processed. 34650 * @returns three maps which contain metadata about the template: `expressions` which interprets 34651 * special `AST` nodes in expressions as pointing to references or variables declared within the 34652 * template, `symbols` which maps those variables and references to the nested `Template` which 34653 * declares them, if any, and `nestingLevel` which associates each `Template` with a integer 34654 * nesting level (how many levels deep within the template structure the `Template` is), starting 34655 * at 1. 34656 */ 34657 static applyWithScope(nodes, scope) { 34658 const expressions = new Map(); 34659 const symbols = new Map(); 34660 const nestingLevel = new Map(); 34661 const usedPipes = new Set(); 34662 const eagerPipes = new Set(); 34663 const template = nodes instanceof Template ? nodes : null; 34664 const deferBlocks = new Map(); 34665 // The top-level template has nesting level 0. 34666 const binder = new TemplateBinder(expressions, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, template, 0);
34667 binder.ingest(nodes); 34668 return { 34669 expressions, 34670 symbols, 34671 nestingLevel, 34672 usedPipes, 34673 eagerPipes, 34674 deferBlocks 34675 }; 34676 } 34677 ingest(nodeOrNodes) { 34678 if (nodeOrNodes instanceof Template) { 34679 // For <ng-template>s, process only variables and child nodes. Inputs, outputs, templateAttrs, 34680 // and references were all processed in the scope of the containing template. 34681 nodeOrNodes.variables.forEach(this.visitNode); 34682 nodeOrNodes.children.forEach(this.visitNode); 34683 // Set the nesting level. 34684 this.nestingLevel.set(nodeOrNodes, this.level); 34685 } else if (nodeOrNodes instanceof IfBlockBranch) { 34686 if (nodeOrNodes.expressionAlias !== null) { 34687 this.visitNode(nodeOrNodes.expressionAlias); 34688 } 34689 nodeOrNodes.children.forEach(this.visitNode); 34690 this.nestingLevel.set(nodeOrNodes, this.level); 34691 } else if (nodeOrNodes instanceof ForLoopBlock) { 34692 this.visitNode(nodeOrNodes.item); 34693 Object.values(nodeOrNodes.contextVariables).forEach(v => this.visitNode(v)); 34694 nodeOrNodes.trackBy.visit(this); 34695 nodeOrNodes.children.forEach(this.visitNode); 34696 this.nestingLevel.set(nodeOrNodes, this.level); 34697 } else if (nodeOrNodes instanceof DeferredBlock) { 34698 if (this.scope.rootNode !== nodeOrNodes) { 34699 throw new Error(`Assertion error: resolved incorrect scope for deferred block ${nodeOrNodes}`); 34700 } 34701 this.deferBlocks.set(nodeOrNodes, this.scope); 34702 nodeOrNodes.children.forEach(node => node.visit(this)); 34703 this.nestingLevel.set(nodeOrNodes, this.level); 34704 } else if (nodeOrNodes instanceof SwitchBlockCase || nodeOrNodes instanceof ForLoopBlockEmpty || nodeOrNodes instanceof DeferredBlockError || nodeOrNodes instanceof DeferredBlockPlaceholder || nodeOrNodes instanceof DeferredBlockLoading) { 34705 nodeOrNodes.children.forEach(node => node.visit(this)); 34706 this.nestingLevel.set(nodeOrNodes, this.level); 34707 } else { 34708 // Visit each node from the top-level template. 34709 nodeOrNodes.forEach(this.visitNode); 34710 } 34711 } 34712 visitElement(element) { 34713 // Visit the inputs, outputs, and children of the element. 34714 element.inputs.forEach(this.visitNode); 34715 element.outputs.forEach(this.visitNode); 34716 element.children.forEach(this.visitNode); 34717 element.references.forEach(this.visitNode); 34718 } 34719 visitTemplate(template) { 34720 // First, visit inputs, outputs and template attributes of the template node. 34721 template.inputs.forEach(this.visitNode); 34722 template.outputs.forEach(this.visitNode); 34723 template.templateAttrs.forEach(this.visitNode); 34724 template.references.forEach(this.visitNode); 34725 // Next, recurse into the template. 34726 this.ingestScopedNode(template); 34727 } 34728 visitVariable(variable) { 34729 // Register the `Variable` as a symbol in the current `Template`. 34730 if (this.rootNode !== null) { 34731 this.symbols.set(variable, this.rootNode); 34732 } 34733 } 34734 visitReference(reference) { 34735 // Register the `Reference` as a symbol in the current `Template`. 34736 if (this.rootNode !== null) { 34737 this.symbols.set(reference, this.rootNode); 34738 } 34739 } 34740 // Unused template visitors 34741 visitText(text) {} 34742 visitContent(content) {} 34743 visitTextAttribute(attribute) {} 34744 visitUnknownBlock(block) {} 34745 visitDeferredTrigger() {} 34746 visitIcu(icu) { 34747 Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this)); 34748 Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this)); 34749 } 34750 // The remaining visitors are concerned with processing AST expressions within template bindings 34751 visitBoundAttribute(attribute) { 34752 attribute.value.visit(this); 34753 } 34754 visitBoundEvent(event) { 34755 event.handler.visit(this); 34756 } 34757 visitDeferredBlock(deferred) { 34758 this.ingestScopedNode(deferred); 34759 deferred.triggers.when?.value.visit(this); 34760 deferred.prefetchTriggers.when?.value.visit(this); 34761 deferred.placeholder && this.visitNode(deferred.placeholder); 34762 deferred.loading && this.visitNode(deferred.loading); 34763 deferred.error && this.visitNode(deferred.error); 34764 } 34765 visitDeferredBlockPlaceholder(block) { 34766 this.ingestScopedNode(block); 34767 } 34768 visitDeferredBlockError(block) { 34769 this.ingestScopedNode(block); 34770 } 34771 visitDeferredBlockLoading(block) { 34772 this.ingestScopedNode(block); 34773 } 34774 visitSwitchBlock(block) { 34775 block.expression.visit(this);
34776 block.cases.forEach(this.visitNode); 34777 } 34778 visitSwitchBlockCase(block) { 34779 block.expression?.visit(this); 34780 this.ingestScopedNode(block); 34781 } 34782 visitForLoopBlock(block) { 34783 block.expression.visit(this); 34784 this.ingestScopedNode(block); 34785 block.empty?.visit(this); 34786 } 34787 visitForLoopBlockEmpty(block) { 34788 this.ingestScopedNode(block); 34789 } 34790 visitIfBlock(block) { 34791 block.branches.forEach(node => node.visit(this)); 34792 } 34793 visitIfBlockBranch(block) { 34794 block.expression?.visit(this); 34795 this.ingestScopedNode(block); 34796 } 34797 visitBoundText(text) { 34798 text.value.visit(this); 34799 } 34800 visitPipe(ast, context) { 34801 this.usedPipes.add(ast.name); 34802 if (!this.scope.isDeferred) { 34803 this.eagerPipes.add(ast.name); 34804 } 34805 return super.visitPipe(ast, context); 34806 } 34807 // These five types of AST expressions can refer to expression roots, which could be variables 34808 // or references in the current scope. 34809 visitPropertyRead(ast, context) { 34810 this.maybeMap(ast, ast.name); 34811 return super.visitPropertyRead(ast, context); 34812 } 34813 visitSafePropertyRead(ast, context) { 34814 this.maybeMap(ast, ast.name); 34815 return super.visitSafePropertyRead(ast, context); 34816 } 34817 visitPropertyWrite(ast, context) { 34818 this.maybeMap(ast, ast.name); 34819 return super.visitPropertyWrite(ast, context); 34820 } 34821 ingestScopedNode(node) { 34822 const childScope = this.scope.getChildScope(node); 34823 const binder = new TemplateBinder(this.bindings, this.symbols, this.usedPipes, this.eagerPipes, this.deferBlocks, this.nestingLevel, childScope, node, this.level + 1); 34824 binder.ingest(node); 34825 } 34826 maybeMap(ast, name) { 34827 // If the receiver of the expression isn't the `ImplicitReceiver`, this isn't the root of an 34828 // `AST` expression that maps to a `Variable` or `Reference`. 34829 if (!(ast.receiver instanceof ImplicitReceiver)) { 34830 return; 34831 } 34832 // Check whether the name exists in the current scope. If so, map it. Otherwise, the name is 34833 // probably a property on the top-level component context. 34834 let target = this.scope.lookup(name); 34835 if (target !== null) { 34836 this.bindings.set(ast, target); 34837 } 34838 } 34839} 34840/** 34841 * Metadata container for a `Target` that allows queries for specific bits of metadata. 34842 * 34843 * See `BoundTarget` for documentation on the individual methods. 34844 */ 34845class R3BoundTarget { 34846 constructor(target, directives, eagerDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks) { 34847 this.target = target; 34848 this.directives = directives; 34849 this.eagerDirectives = eagerDirectives; 34850 this.bindings = bindings; 34851 this.references = references; 34852 this.exprTargets = exprTargets; 34853 this.symbols = symbols; 34854 this.nestingLevel = nestingLevel; 34855 this.scopedNodeEntities = scopedNodeEntities; 34856 this.usedPipes = usedPipes; 34857 this.eagerPipes = eagerPipes; 34858 this.deferBlocks = deferBlocks; 34859 } 34860 getEntitiesInScope(node) { 34861 return this.scopedNodeEntities.get(node) ?? new Set(); 34862 } 34863 getDirectivesOfNode(node) { 34864 return this.directives.get(node) || null; 34865 } 34866 getReferenceTarget(ref) { 34867 return this.references.get(ref) || null; 34868 } 34869 getConsumerOfBinding(binding) { 34870 return this.bindings.get(binding) || null; 34871 } 34872 getExpressionTarget(expr) { 34873 return this.exprTargets.get(expr) || null; 34874 } 34875 getDefinitionNodeOfSymbol(symbol) { 34876 return this.symbols.get(symbol) || null; 34877 } 34878 getNestingLevel(node) { 34879 return this.nestingLevel.get(node) || 0; 34880 } 34881 getUsedDirectives() { 34882 const set = new Set(); 34883 this.directives.forEach(dirs => dirs.forEach(dir => set.add(dir))); 34884 return Array.from(set.values()); 34885 } 34886 getEagerlyUsedDirectives() { 34887 const set = new Set(this.eagerDirectives); 34888 return Array.from(set.values()); 34889 } 34890 getUsedPipes() { 34891 return Array.from(this.usedPipes); 34892 } 34893 getEagerlyUsedPipes() { 34894 return Array.from(this.eagerPipes); 34895 } 34896 getDeferBlocks() { 34897 return Array.from(this.deferBlocks.keys()); 34898 } 34899 getDeferredTriggerTarget(block, trigger) { 34900 // Only triggers that refer to DOM nodes can be resolved. 34901 if (!(trigger instanceof InteractionDeferredTrigger) && !(trigger instanceof ViewportDeferredTrigger) && !(trigger instanceof HoverDeferredTrigger)) { 34902 return null; 34903 } 34904 const name = trigger.reference; 34905 if (name === null) { 34906 let trigger = null; 34907 if (block.placeholder !== null) { 34908 for (const child of block.placeholder.children) { 34909 // Skip over comment nodes. Currently by default the template parser doesn't capture 34910 // comments, but we have a safeguard here just in case since it can be enabled. 34911 if (child instanceof Comment$1) { 34912 continue; 34913 } 34914 // We can only infer the trigger if there's one root element node. Any other 34915 // nodes at the root make it so that we can't infer the trigger anymore. 34916 if (trigger !== null) { 34917 return null; 34918 } 34919 if (child instanceof Element$1) { 34920 trigger = child; 34921 } 34922 } 34923 } 34924 return trigger; 34925 } 34926 const outsideRef = this.findEntityInScope(block, name); 34927 // First try to resolve the target in the scope of the main deferred block. Note that we 34928 // skip triggers defined inside the main block itself, because they might not exist yet. 34929 if (outsideRef instanceof Reference && this.getDefinitionNodeOfSymbol(outsideRef) !== block) { 34930 const target = this.getReferenceTarget(outsideRef); 34931 if (target !== null) { 34932 return this.referenceTargetToElement(target); 34933 } 34934 } 34935 // If the trigger couldn't be found in the main block, check the 34936 // placeholder block which is shown before the main block has loaded. 34937 if (block.placeholder !== null) { 34938 const refInPlaceholder = this.findEntityInScope(block.placeholder, name); 34939 const targetInPlaceholder = refInPlaceholder instanceof Reference ? this.getReferenceTarget(refInPlaceholder) : null; 34940 if (targetInPlaceholder !== null) { 34941 return this.referenceTargetToElement(targetInPlaceholder); 34942 } 34943 } 34944 return null; 34945 } 34946 isDeferred(element) { 34947 for (const deferredScope of this.deferBlocks.values()) { 34948 const stack = [deferredScope]; 34949 while (stack.length > 0) { 34950 const current = stack.pop(); 34951 if (current.elementsInScope.has(element)) { 34952 return true; 34953 } 34954 stack.push(...current.childScopes.values()); 34955 } 34956 } 34957 return false;
34958 } 34959 /** 34960 * Finds an entity with a specific name in a scope. 34961 * @param rootNode Root node of the scope. 34962 * @param name Name of the entity. 34963 */ 34964 findEntityInScope(rootNode, name) { 34965 const entities = this.getEntitiesInScope(rootNode); 34966 for (const entitity of entities) { 34967 if (entitity.name === name) { 34968 return entitity; 34969 } 34970 } 34971 return null; 34972 } 34973 /** Coerces a `ReferenceTarget` to an `Element`, if possible. */ 34974 referenceTargetToElement(target) { 34975 if (target instanceof Element$1) { 34976 return target; 34977 } 34978 if (target instanceof Template) { 34979 return null; 34980 } 34981 return this.referenceTargetToElement(target.node); 34982 } 34983} 34984function extractScopedNodeEntities(rootScope) { 34985 const entityMap = new Map(); 34986 function extractScopeEntities(scope) { 34987 if (entityMap.has(scope.rootNode)) { 34988 return entityMap.get(scope.rootNode); 34989 } 34990 const currentEntities = scope.namedEntities; 34991 let entities; 34992 if (scope.parentScope !== null) { 34993 entities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]); 34994 } else { 34995 entities = new Map(currentEntities); 34996 } 34997 entityMap.set(scope.rootNode, entities); 34998 return entities; 34999 } 35000 const scopesToProcess = [rootScope]; 35001 while (scopesToProcess.length > 0) { 35002 const scope = scopesToProcess.pop(); 35003 for (const childScope of scope.childScopes.values()) { 35004 scopesToProcess.push(childScope); 35005 } 35006 extractScopeEntities(scope); 35007 } 35008 const templateEntities = new Map(); 35009 for (const [template, entities] of entityMap) { 35010 templateEntities.set(template, new Set(entities.values())); 35011 } 35012 return templateEntities; 35013} 35014 35015/** 35016 * An interface for retrieving documents by URL that the compiler uses to 35017 * load templates. 35018 * 35019 * This is an abstract class, rather than an interface, so that it can be used 35020 * as injection token. 35021 */ 35022class ResourceLoader {} 35023const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = true; 35024class CompilerFacadeImpl { 35025 constructor(jitEvaluator = new JitEvaluator()) { 35026 this.jitEvaluator = jitEvaluator; 35027 this.FactoryTarget = FactoryTarget$1; 35028 this.ResourceLoader = ResourceLoader; 35029 this.elementSchemaRegistry = new DomElementSchemaRegistry(); 35030 } 35031 compilePipe(angularCoreEnv, sourceMapUrl, facade) { 35032 const metadata = { 35033 name: facade.name, 35034 type: wrapReference(facade.type), 35035 typeArgumentCount: 0, 35036 deps: null, 35037 pipeName: facade.pipeName, 35038 pure: facade.pure, 35039 isStandalone: facade.isStandalone 35040 }; 35041 const res = compilePipeFromMetadata(metadata); 35042 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); 35043 } 35044 compilePipeDeclaration(angularCoreEnv, sourceMapUrl, declaration) { 35045 const meta = convertDeclarePipeFacadeToMetadata(declaration); 35046 const res = compilePipeFromMetadata(meta); 35047 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); 35048 } 35049 compileInjectable(angularCoreEnv, sourceMapUrl, facade) { 35050 const { 35051 expression, 35052 statements 35053 } = compileInjectable({ 35054 name: facade.name, 35055 type: wrapReference(facade.type), 35056 typeArgumentCount: facade.typeArgumentCount, 35057 providedIn: computeProvidedIn(facade.providedIn), 35058 useClass: convertToProviderExpression(facade, 'useClass'), 35059 useFactory: wrapExpression(facade, 'useFactory'), 35060 useValue: convertToProviderExpression(facade, 'useValue'), 35061 useExisting: convertToProviderExpression(facade, 'useExisting'), 35062 deps: facade.deps?.map(convertR3DependencyMetadata) 35063 }, /* resolveForwardRefs */true); 35064 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements); 35065 } 35066 compileInjectableDeclaration(angularCoreEnv, sourceMapUrl, facade) { 35067 const { 35068 expression, 35069 statements 35070 } = compileInjectable({ 35071 name: facade.type.name, 35072 type: wrapReference(facade.type), 35073 typeArgumentCount: 0, 35074 providedIn: computeProvidedIn(facade.providedIn), 35075 useClass: convertToProviderExpression(facade, 'useClass'), 35076 useFactory: wrapExpression(facade, 'useFactory'), 35077 useValue: convertToProviderExpression(facade, 'useValue'), 35078 useExisting: convertToProviderExpression(facade, 'useExisting'), 35079 deps: facade.deps?.map(convertR3DeclareDependencyMetadata) 35080 }, /* resolveForwardRefs */true); 35081 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements); 35082 } 35083 compileInjector(angularCoreEnv, sourceMapUrl, facade) { 35084 const meta = { 35085 name: facade.name, 35086 type: wrapReference(facade.type), 35087 providers: facade.providers && facade.providers.length > 0 ? new WrappedNodeExpr(facade.providers) : null, 35088 imports: facade.imports.map(i => new WrappedNodeExpr(i)) 35089 }; 35090 const res = compileInjector(meta); 35091 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); 35092 } 35093 compileInjectorDeclaration(angularCoreEnv, sourceMapUrl, declaration) { 35094 const meta = convertDeclareInjectorFacadeToMetadata(declaration); 35095 const res = compileInjector(meta); 35096 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); 35097 } 35098 compileNgModule(angularCoreEnv, sourceMapUrl, facade) { 35099 const meta = { 35100 kind: R3NgModuleMetadataKind.Global, 35101 type: wrapReference(facade.type), 35102 bootstrap: facade.bootstrap.map(wrapReference), 35103 declarations: facade.declarations.map(wrapReference), 35104 publicDeclarationTypes: null, 35105 // only needed for types in AOT 35106 imports: facade.imports.map(wrapReference), 35107 includeImportTypes: true, 35108 exports: facade.exports.map(wrapReference), 35109 selectorScopeMode: R3SelectorScopeMode.Inline, 35110 containsForwardDecls: false, 35111 schemas: facade.schemas ? facade.schemas.map(wrapReference) : null, 35112 id: facade.id ? new WrappedNodeExpr(facade.id) : null 35113 }; 35114 const res = compileNgModule(meta); 35115 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); 35116 } 35117 compileNgModuleDeclaration(angularCoreEnv, sourceMapUrl, declaration) { 35118 const expression = compileNgModuleDeclarationExpression(declaration); 35119 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, []); 35120 } 35121 compileDirective(angularCoreEnv, sourceMapUrl, facade) { 35122 const meta = convertDirectiveFacadeToMetadata(facade); 35123 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta); 35124 } 35125 compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) { 35126 const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl); 35127 const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan); 35128 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta); 35129 } 35130 compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) { 35131 const constantPool = new ConstantPool(); 35132 const bindingParser = makeBindingParser(); 35133 const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser); 35134 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements); 35135 } 35136 compileComponent(angularCoreEnv, sourceMapUrl, facade) { 35137 // Parse the template and check for errors. 35138 const { 35139 template, 35140 interpolation, 35141 deferBlocks 35142 } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespace
35142s, facade.interpolation); 35143 // Compile the component metadata, including template, into an expression. 35144 const meta = { 35145 ...facade, 35146 ...convertDirectiveFacadeToMetadata(facade), 35147 selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(), 35148 template, 35149 declarations: facade.declarations.map(convertDeclarationFacadeToMetadata), 35150 declarationListEmitMode: 0 /* DeclarationListEmitMode.Direct */, 35151 deferBlocks, 35152 deferrableTypes: new Map(), 35153 deferrableDeclToImportDecl: new Map(), 35154 deferBlockDepsEmitMode: 0 /* DeferBlockDepsEmitMode.PerBlock */, 35155 styles: [...facade.styles, ...template.styles], 35156 encapsulation: facade.encapsulation, 35157 interpolation, 35158 changeDetection: facade.changeDetection ?? null, 35159 animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null, 35160 viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) : null, 35161 relativeContextFilePath: '', 35162 i18nUseExternalIds: true, 35163 useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT 35164 }; 35165 const jitExpressionSourceMap = `ng:///${facade.name}.js`; 35166 return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta); 35167 } 35168 compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) { 35169 const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl); 35170 const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl); 35171 return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta); 35172 } 35173 compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) { 35174 const constantPool = new ConstantPool(); 35175 const bindingParser = makeBindingParser(meta.interpolation); 35176 const res = compileComponentFromMetadata(meta, constantPool, bindingParser); 35177 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements); 35178 } 35179 compileFactory(angularCoreEnv, sourceMapUrl, meta) { 35180 const factoryRes = compileFactoryFunction({ 35181 name: meta.name, 35182 type: wrapReference(meta.type), 35183 typeArgumentCount: meta.typeArgumentCount, 35184 deps: convertR3DependencyMetadataArray(meta.deps), 35185 target: meta.target 35186 }); 35187 return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements); 35188 } 35189 compileFactoryDeclaration(angularCoreEnv, sourceMapUrl, meta) { 35190 const factoryRes = compileFactoryFunction({ 35191 name: meta.type.name, 35192 type: wrapReference(meta.type), 35193 typeArgumentCount: 0, 35194 deps: Array.isArray(meta.deps) ? meta.deps.map(convertR3DeclareDependencyMetadata) : meta.deps, 35195 target: meta.target 35196 }); 35197 return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements); 35198 } 35199 createParseSourceSpan(kind, typeName, sourceUrl) { 35200 return r3JitTypeSourceSpan(kind, typeName, sourceUrl); 35201 } 35202 /** 35203 * JIT compiles an expression and returns the result of executing that expression. 35204 * 35205 * @param def the definition which will be compiled and executed to get the value to patch 35206 * @param context an object map of @angular/core symbol names to symbols which will be available 35207 * in the context of the compiled expression 35208 * @param sourceUrl a URL to use for the source map of the compiled expression 35209 * @param preStatements a collection of statements that should be evaluated before the expression. 35210 */ 35211 jitExpression(def, context, sourceUrl, preStatements) { 35212 // The ConstantPool may contain Statements which declare variables used in the final expression. 35213 // Therefore, its statements need to precede the actual JIT operation. The final statement is a 35214 // declaration of $def which is set to the expression being compiled. 35215 const statements = [...preStatements, new DeclareVarStmt('$def', def, undefined, StmtModifier.Exported)]; 35216 const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */true); 35217 return res['$def']; 35218 } 35219} 35220function convertToR3QueryMetadata(facade) { 35221 return { 35222 ...facade, 35223 isSignal: facade.isSignal, 35224 predicate: convertQueryPredicate(facade.predicate), 35225 read: facade.read ? new WrappedNodeExpr(facade.read) : null, 35226 static: facade.static, 35227 emitDistinctChangesOnly: facade.emitDistinctChangesOnly 35228 }; 35229} 35230function convertQueryDeclarationToMetadata(declaration) { 35231 return { 35232 propertyName: declaration.propertyName, 35233 first: declaration.first ?? false, 35234 predicate: convertQueryPredicate(declaration.predicate), 35235 descendants: declaration.descendants ?? false, 35236 read: declaration.read ? new WrappedNodeExpr(declaration.read) : null, 35237 static: declaration.static ?? false, 35238 emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true, 35239 isSignal: !!declaration.isSignal 35240 }; 35241} 35242function convertQueryPredicate(predicate) { 35243 return Array.isArray(predicate) ?
35244 // The predicate is an array of strings so pass it through. 35245 predicate : 35246 // The predicate is a type - assume that we will need to unwrap any `forwardRef()` calls. 35247 createMayBeForwardRefExpression(new WrappedNodeExpr(predicate), 1 /* ForwardRefHandling.Wrapped */); 35248} 35249function convertDirectiveFacadeToMetadata(facade) { 35250 const inputsFromMetadata = parseInputsArray(facade.inputs || []); 35251 const outputsFromMetadata = parseMappingStringArray(facade.outputs || []); 35252 const propMetadata = facade.propMetadata; 35253 const inputsFromType = {}; 35254 const outputsFromType = {}; 35255 for (const field in propMetadata) { 35256 if (propMetadata.hasOwnProperty(field)) { 35257 propMetadata[field].forEach(ann => { 35258 if (isInput(ann)) { 35259 inputsFromType[field] = { 35260 bindingPropertyName: ann.alias || field, 35261 classPropertyName: field, 35262 required: ann.required || false, 35263 // For JIT, decorators are used to declare signal inputs. That is because of 35264 // a technical limitation where it's not possible to statically reflect class 35265 // members of a directive/component at runtime before instantiating the class. 35266 isSignal: !!ann.isSignal, 35267 transformFunction: ann.transform != null ? new WrappedNodeExpr(ann.transform) : null 35268 }; 35269 } else if (isOutput(ann)) { 35270 outputsFromType[field] = ann.alias || field; 35271 } 35272 }); 35273 } 35274 } 35275 return { 35276 ...facade, 35277 typeArgumentCount: 0, 35278 typeSourceSpan: facade.typeSourceSpan, 35279 type: wrapReference(facade.type), 35280 deps: null, 35281 host: { 35282 ...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), 35283 useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT 35284 }, 35285 inputs: { 35286 ...inputsFromMetadata, 35287 ...inputsFromType 35288 }, 35289 outputs: { 35290 ...outputsFromMetadata, 35291 ...outputsFromType 35292 }, 35293 queries: facade.queries.map(convertToR3QueryMetadata), 35294 providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null, 35295 viewQueries: facade.viewQueries.map(convertToR3QueryMetadata), 35296 fullInheritance: false, 35297 hostDirectives: convertHostDirectivesToMetadata(facade) 35298 }; 35299} 35300function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) { 35301 return { 35302 name: declaration.type.name, 35303 type: wrapReference(declaration.type), 35304 typeSourceSpan, 35305 selector: declaration.selector ?? null, 35306 inputs: declaration.inputs ? inputsPartialMetadataToInputMetadata(declaration.inputs) : {}, 35307 outputs: declaration.outputs ?? {}, 35308 host: convertHostDeclarationToMetadata(declaration.host), 35309 queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata), 35310 viewQueries: (declaration.viewQueries ?? []).map(convertQueryDeclarationToMetadata), 35311 providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) : null, 35312 exportAs: declaration.exportAs ?? null, 35313 usesInheritance: declaration.usesInheritance ?? false, 35314 lifecycle: { 35315 usesOnChanges: declaration.usesOnChanges ?? false 35316 }, 35317 deps: null, 35318 typeArgumentCount: 0, 35319 fullInheritance: false, 35320 isStandalone: declaration.isStandalone ?? false, 35321 isSignal: declaration.isSignal ?? false, 35322 hostDirectives: convertHostDirectivesToMetadata(declaration) 35323 }; 35324} 35325function convertHostDeclarationToMetadata(host = {}) { 35326 return { 35327 attributes: convertOpaqueValuesToExpressions(host.attributes ?? {}), 35328 listeners: host.listeners ?? {}, 35329 properties: host.properties ?? {}, 35330 specialAttributes: { 35331 classAttr: host.classAttribute, 35332 styleAttr: host.styleAttribute 35333 }, 35334 useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT 35335 }; 35336} 35337function convertHostDirectivesToMetadata(metadata) { 35338 if (metadata.hostDirectives?.length) { 35339 return metadata.hostDirectives.map(hostDirective => { 35340 return typeof hostDirective === 'function' ? { 35341 directive: wrapReference(hostDirective), 35342 inputs: null, 35343 outputs: null, 35344 isForwardReference: false 35345 } : { 35346 directive: wrapReference(hostDirective.directive), 35347 isForwardReference: false, 35348 inputs: hostDirective.inputs ? parseMappingStringArray(hostDirective.inputs) : null, 35349 outputs: hostDirective.outputs ? parseMappingStringArray(hostDirective.outputs) : null 35350 }; 35351 }); 35352 } 35353 return null; 35354} 35355function convertOpaqueValuesToExpressions(obj) { 35356 const result = {}; 35357 for (const key of Object.keys(obj)) { 35358 result[key] = new WrappedNodeExpr(obj[key]); 35359 } 35360 return result; 35361} 35362function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMapUrl) { 35363 const { 35364 template, 35365 interpolation, 35366 deferBlocks 35367 }
35367 = parseJitTemplate(decl.template, decl.type.name, sourceMapUrl, decl.preserveWhitespaces ?? false, decl.interpolation); 35368 const declarations = []; 35369 if (decl.dependencies) { 35370 for (const innerDep of decl.dependencies) { 35371 switch (innerDep.kind) { 35372 case 'directive': 35373 case 'component': 35374 declarations.push(convertDirectiveDeclarationToMetadata(innerDep)); 35375 break; 35376 case 'pipe': 35377 declarations.push(convertPipeDeclarationToMetadata(innerDep)); 35378 break; 35379 } 35380 } 35381 } else if (decl.components || decl.directives || decl.pipes) { 35382 // Existing declarations on NPM may not be using the new `dependencies` merged field, and may 35383 // have separate fields for dependencies instead. Unify them for JIT compilation. 35384 decl.components && declarations.push(...decl.components.map(dir => convertDirectiveDeclarationToMetadata(dir, /* isComponent */true))); 35385 decl.directives && declarations.push(...decl.directives.map(dir => convertDirectiveDeclarationToMetadata(dir))); 35386 decl.pipes && declarations.push(...convertPipeMapToMetadata(decl.pipes)); 35387 } 35388 return { 35389 ...convertDeclareDirectiveFacadeToMetadata(decl, typeSourceSpan), 35390 template, 35391 styles: decl.styles ?? [], 35392 declarations, 35393 viewProviders: decl.viewProviders !== undefined ? new WrappedNodeExpr(decl.viewProviders) : null, 35394 animations: decl.animations !== undefined ? new WrappedNodeExpr(decl.animations) : null, 35395 deferBlocks, 35396 deferrableTypes: new Map(), 35397 deferrableDeclToImportDecl: new Map(), 35398 deferBlockDepsEmitMode: 0 /* DeferBlockDepsEmitMode.PerBlock */, 35399 changeDetection: decl.changeDetection ?? ChangeDetectionStrategy.Default, 35400 encapsulation: decl.encapsulation ?? ViewEncapsulation.Emulated, 35401 interpolation, 35402 declarationListEmitMode: 2 /* DeclarationListEmitMode.ClosureResolved */, 35403 relativeContextFilePath: '', 35404 i18nUseExternalIds: true, 35405 useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT 35406 }; 35407} 35408function convertDeclarationFacadeToMetadata(declaration) { 35409 return { 35410 ...declaration, 35411 type: new WrappedNodeExpr(declaration.type) 35412 }; 35413} 35414function convertDirectiveDeclarationToMetadata(declaration, isComponent = null) { 35415 return { 35416 kind: R3TemplateDependencyKind.Directive, 35417 isComponent: isComponent || declaration.kind === 'component', 35418 selector: declaration.selector, 35419 type: new WrappedNodeExpr(declaration.type), 35420 inputs: declaration.inputs ?? [], 35421 outputs: declaration.outputs ?? [], 35422 exportAs: declaration.exportAs ?? null 35423 }; 35424} 35425function convertPipeMapToMetadata(pipes) { 35426 if (!pipes) { 35427 return []; 35428 } 35429 return Object.keys(pipes).map(name => { 35430 return { 35431 kind: R3TemplateDependencyKind.Pipe, 35432 name, 35433 type: new WrappedNodeExpr(pipes[name]) 35434 }; 35435 }); 35436} 35437function convertPipeDeclarationToMetadata(pipe) { 35438 return { 35439 kind: R3TemplateDependencyKind.Pipe, 35440 name: pipe.name, 35441 type: new WrappedNodeExpr(pipe.type) 35442 }; 35443} 35444function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) { 35445 const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG; 35446 // Parse the template and check for errors. 35447 const parsed = parseTemplate(template, sourceMapUrl, { 35448 preserveWhitespaces, 35449 interpolationConfig 35450 }); 35451 if (parsed.errors !== null) { 35452 const errors = parsed.errors.map(err => err.toString()).join(', '); 35453 throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`); 35454 } 35455 const binder = new R3TargetBinder(new SelectorMatcher()); 35456 const boundTarget = binder.bind({ 35457 template: parsed.nodes 35458 }); 35459 return { 35460 template: parsed, 35461 interpolation: interpolationConfig, 35462 deferBlocks: createR3DeferredMetadata(boundTarget) 35463 }; 35464} 35465/** 35466 * Convert the expression, if present to an `R3ProviderExpression`. 35467 * 35468 * In JIT mode we do not want the compiler to wrap the expression in a `forwardRef()` call because, 35469 * if it is referencing a type that has not yet been defined, it will have already been wrapped in 35470 * a `forwardRef()` - either by the application developer or during partial-compilation. Thus we can 35471 * use `ForwardRefHandling.None`. 35472 */ 35473function convertToProviderExpression(obj, property) { 35474 if (obj.hasOwnProperty(property)) { 35475 return createMayBeForwardRefExpression(new WrappedNodeExpr(obj[property]), 0 /* ForwardRefHandling.None */); 35476 } else { 35477 return undefined; 35478 } 35479} 35480function wrapExpression(obj, property) { 35481 if (obj.hasOwnProperty(property)) { 35482 return new WrappedNodeExpr(obj[property]); 35483 } else { 35484 return undefined; 35485 } 35486} 35487function computeProvidedIn(providedIn) { 35488 const expression = typeof providedIn === 'function' ? new WrappedNodeExpr(providedIn) : new LiteralExpr(providedIn ?? null); 35489 // See `convertToProviderExpression()` for why this uses `ForwardRefHandling.None`. 35490 return createMayBeForwardRefExpression(expression, 0 /* ForwardRefHandling.None */); 35491} 35492function convertR3DependencyMetadataArray(facades) { 35493 return facades == null ? null : facades.map(convertR3DependencyMetadata); 35494} 35495function convertR3DependencyMetadata(facade) { 35496 const isAttributeDep = facade.attribute != null; // both `null` and `undefined` 35497 const rawToken = facade.token === null ? null : new WrappedNodeExpr(facade.token); 35498 // In JIT mode, if the dep is an `@Attribute()` then we use the attribute name given in 35499 // `attribute` rather than the `token`. 35500 const token = isAttributeDep ? new WrappedNodeExpr(facade.attribute) : rawToken;
35501 return createR3DependencyMetadata(token, isAttributeDep, facade.host, facade.optional, facade.self, facade.skipSelf); 35502} 35503function convertR3DeclareDependencyMetadata(facade) { 35504 const isAttributeDep = facade.attribute ?? false; 35505 const token = facade.token === null ? null : new WrappedNodeExpr(facade.token); 35506 return createR3DependencyMetadata(token, isAttributeDep, facade.host ?? false, facade.optional ?? false, facade.self ?? false, facade.skipSelf ?? false); 35507} 35508function createR3DependencyMetadata(token, isAttributeDep, host, optional, self, skipSelf) { 35509 // If the dep is an `@Attribute()` the `attributeNameType` ought to be the `unknown` type. 35510 // But types are not available at runtime so we just use a literal `"<unknown>"` string as a dummy 35511 // marker. 35512 const attributeNameType = isAttributeDep ? literal('unknown') : null; 35513 return { 35514 token, 35515 attributeNameType, 35516 host, 35517 optional, 35518 self, 35519 skipSelf 35520 }; 35521} 35522function createR3DeferredMetadata(boundTarget) { 35523 const deferredBlocks = boundTarget.getDeferBlocks(); 35524 const meta = new Map(); 35525 for (const block of deferredBlocks) { 35526 const triggerElements = new Map(); 35527 resolveDeferTriggers(block, block.triggers, boundTarget, triggerElements); 35528 resolveDeferTriggers(block, block.prefetchTriggers, boundTarget, triggerElements); 35529 // TODO: leaving `deps` empty in JIT mode for now, to be implemented as one of the next steps. 35530 meta.set(block, { 35531 deps: [], 35532 triggerElements 35533 }); 35534 } 35535 return meta; 35536} 35537function resolveDeferTriggers(block, triggers, boundTarget, triggerElements) { 35538 Object.keys(triggers).forEach(key => { 35539 const trigger = triggers[key]; 35540 triggerElements.set(trigger, boundTarget.getDeferredTriggerTarget(block, trigger)); 35541 }); 35542} 35543function extractHostBindings(propMetadata, sourceSpan, host) { 35544 // First parse the declarations from the metadata. 35545 const bindings = parseHostBindings(host || {}); 35546 // After that check host bindings for errors 35547 const errors = verifyHostBindings(bindings, sourceSpan); 35548 if (errors.length) { 35549 throw new Error(errors.map(error => error.msg).join('\n')); 35550 } 35551 // Next, loop over the properties of the object, looking for @HostBinding and @HostListener. 35552 for (const field in propMetadata) { 35553 if (propMetadata.hasOwnProperty(field)) { 35554 propMetadata[field].forEach(ann => { 35555 if (isHostBinding(ann)) { 35556 // Since this is a decorator, we know that the value is a class member. Always access it 35557 // through `this` so that further down the line it can't be confused for a literal value 35558 // (e.g. if there's a property called `true`). 35559 bindings.properties[ann.hostPropertyName || field] = getSafePropertyAccessString('this', field); 35560 } else if (isHostListener(ann)) { 35561 bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`; 35562 } 35563 }); 35564 } 35565 } 35566 return bindings; 35567} 35568function isHostBinding(value) { 35569 return value.ngMetadataName === 'HostBinding'; 35570} 35571function isHostListener(value) { 35572 return value.ngMetadataName === 'HostListener'; 35573} 35574function isInput(value) { 35575 return value.ngMetadataName === 'Input'; 35576} 35577function isOutput(value) { 35578 return value.ngMetadataName === 'Output'; 35579} 35580function inputsPartialMetadataToInputMetadata(inputs) { 35581 return Object.keys(inputs).reduce((result, minifiedClassName) => { 35582 const value = inputs[minifiedClassName]; 35583 // Handle legacy partial input output. 35584 if (typeof value === 'string' || Array.isArray(value)) { 35585 result[minifiedClassName] = parseLegacyInputPartialOutput(value); 35586 } else { 35587 result[minifiedClassName] = { 35588 bindingPropertyName: value.publicName, 35589 classPropertyName: minifiedClassName, 35590 transformFunction: value.transformFunction !== null ? new WrappedNodeExpr(value.transformFunction) : null, 35591 required: value.isRequired, 35592 isSignal: value.isSignal 35593 }; 35594 } 35595 return result; 35596 }, {}); 35597} 35598/** 35599 * Parses the legacy input partial output. For more details see `partial/directive.ts`. 35600 * TODO(legacy-partial-output-inputs): Remove in v18. 35601 */ 35602function parseLegacyInputPartialOutput(value) { 35603 if (typeof value === 'string') { 35604 return { 35605 bindingPropertyName: value, 35606 classPropertyName: value, 35607 transformFunction: null, 35608 required: false, 35609 // legacy partial output does not capture signal inputs. 35610 isSignal: false 35611 }; 35612 } 35613 return { 35614 bindingPropertyName: value[0], 35615 classPropertyName: value[1], 35616 transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null, 35617 required: false, 35618 // legacy partial output does not capture signal inputs. 35619 isSignal: false 35620 }; 35621} 35622function parseInputsArray(values) { 35623 return values.reduce((results, value) => { 35624 if (typeof value === 'string') { 35625 const [bindingPropertyName, classPropertyName] = parseMappingString(value); 35626 results[classPropertyName] = { 35627 bindingPropertyName, 35628 classPropertyName, 35629 required: false, 35630 // Signal inputs not supported for the inputs array. 35631 isSignal: false, 35632 transformFunction: null 35633 }; 35634 } else { 35635 results[value.name] = { 35636 bindingPropertyName: value.alias || value.name, 35637 classPropertyName: value.name, 35638 required: value.required || false, 35639 // Signal inputs not supported for the inputs array. 35640 isSignal: false, 35641 transformFunction: value.transform != null ? new WrappedNodeExpr(value.transform) : null 35642 }; 35643 } 35644 return results; 35645 }, {}); 35646} 35647function parseMappingStringArray(values) { 35648 return values.reduce((results, value) => { 35649 const [alias, fieldName] = parseMappingString(value); 35650 results[fieldName] = alias; 35651 return results; 35652 }, {}); 35653} 35654function parseMappingString(value) { 35655 // Either the value is 'field' or 'field: property'. In the first case, `property` will 35656 // be undefined, in which case the field name should also be used as the property name. 35657 const [fieldName, bindingPropertyName] = value.split(':', 2).map(str => str.trim()); 35658 return [bindingPropertyName ?? fieldName, fieldName]; 35659} 35660function convertDeclarePipeFacadeToMetadata(declaration) { 35661 return { 35662 name: declaration.type.name, 35663 type: wrapReference(declaration.type), 35664 typeArgumentCount: 0, 35665 pipeName: declaration.name, 35666 deps: null, 35667 pure: declaration.pure ?? true, 35668 isStandalone: declaration.isStandalone ?? false 35669 }; 35670} 35671function convertDeclareInjectorFacadeToMetadata(declaration) { 35672 return { 35673 name: declaration.type.name, 35674 type: wrapReference(declaration.type), 35675 providers: declaration.providers !== undefined && declaration.providers.length > 0 ? new WrappedNodeExpr(declaration.providers) : null, 35676 imports: declaration.imports !== undefined ? declaration.imports.map(i => new WrappedNodeExpr(i)) : [] 35677 }; 35678} 35679function publishFacade(global) { 35680 const ng = global.ng || (global.ng = {}); 35681 ng.ɵcompilerFacade = new CompilerFacadeImpl(); 35682} 35683 35684/** 35685 * @module 35686 * @description 35687 * Entry point for all public APIs of the compiler package. 35688 */ 35689const VERSION = new Version('17.3.9'); 35690class CompilerConfig { 35691 constructor({ 35692 defaultEncapsulation = ViewEncapsulation.Emulated,
35693 preserveWhitespaces, 35694 strictInjectionParameters 35695 } = {}) { 35696 this.defaultEncapsulation = defaultEncapsulation; 35697 this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces)); 35698 this.strictInjectionParameters = strictInjectionParameters === true; 35699 } 35700} 35701function preserveWhitespacesDefault(preserveWhitespacesOption, defaultSetting = false) { 35702 return preserveWhitespacesOption === null ? defaultSetting : preserveWhitespacesOption; 35703} 35704const _I18N_ATTR = 'i18n'; 35705const _I18N_ATTR_PREFIX = 'i18n-'; 35706const _I18N_COMMENT_PREFIX_REGEXP = /^i18n:?/; 35707const MEANING_SEPARATOR = '|'; 35708const ID_SEPARATOR = '@@'; 35709let i18nCommentsWarned = false; 35710/** 35711 * Extract translatable messages from an html AST 35712 */ 35713function extractMessages(nodes, interpolationConfig, implicitTags, implicitAttrs) { 35714 const visitor = new _Visitor(implicitTags, implicitAttrs); 35715 return visitor.extract(nodes, interpolationConfig); 35716} 35717function mergeTranslations(nodes, translations, interpolationConfig, implicitTags, implicitAttrs) { 35718 const visitor = new _Visitor(implicitTags, implicitAttrs); 35719 return visitor.merge(nodes, translations, interpolationConfig); 35720} 35721class ExtractionResult { 35722 constructor(messages, errors) { 35723 this.messages = messages; 35724 this.errors = errors; 35725 } 35726} 35727var _VisitorMode; 35728(function (_VisitorMode) { 35729 _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract"; 35730 _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge"; 35731})(_VisitorMode || (_VisitorMode = {})); 35732/** 35733 * This Visitor is used: 35734 * 1. to extract all the translatable strings from an html AST (see `extract()`), 35735 * 2. to replace the translatable strings with the actual translations (see `merge()`) 35736 * 35737 * @internal 35738 */ 35739class _Visitor { 35740 constructor(_implicitTags, _implicitAttrs) { 35741 this._implicitTags = _implicitTags; 35742 this._implicitAttrs = _implicitAttrs; 35743 } 35744 /** 35745 * Extracts the messages from the tree 35746 */ 35747 extract(nodes, interpolationConfig) { 35748 this._init(_VisitorMode.Extract, interpolationConfig); 35749 nodes.forEach(node => node.visit(this, null)); 35750 if (this._inI18nBlock) { 35751 this._reportError(nodes[nodes.length - 1], 'Unclosed block'); 35752 } 35753 return new ExtractionResult(this._messages, this._errors); 35754 } 35755 /** 35756 * Returns a tree where all translatable nodes are translated 35757 */ 35758 merge(nodes, translations, interpolationConfig) { 35759 this._init(_VisitorMode.Merge, interpolationConfig); 35760 this._translations = translations; 35761 // Construct a single fake root element 35762 const wrapper = new Element('wrapper', [], nodes, undefined, undefined, undefined); 35763 const translatedNode = wrapper.visit(this, null); 35764 if (this._inI18nBlock) { 35765 this._reportError(nodes[nodes.length - 1], 'Unclosed block'); 35766 } 35767 return new ParseTreeResult(translatedNode.children, this._errors); 35768 } 35769 visitExpansionCase(icuCase, context) { 35770 // Parse cases for translatable html attributes 35771 const expression = visitAll(this, icuCase.expression, context); 35772 if (this._mode === _VisitorMode.Merge) { 35773 return new ExpansionCase(icuCase.value, expression, icuCase.sourceSpan, icuCase.valueSourceSpan, icuCase.expSourceSpan); 35774 } 35775 } 35776 visitExpansion(icu, context) { 35777 this._mayBeAddBlockChildren(icu); 35778 const wasInIcu = this._inIcu; 35779 if (!this._inIcu) { 35780 // nested ICU messages should not be extracted but top-level translated as a whole 35781 if (this._isInTranslatableSection) { 35782 this._addMessage([icu]); 35783 } 35784 this._inIcu = true; 35785 } 35786 const cases = visitAll(this, icu.cases, context); 35787 if (this._mode === _VisitorMode.Merge) { 35788 icu = new Expansion(icu.switchValue, icu.type, cases, icu.sourceSpan, icu.switchValueSourceSpan); 35789 } 35790 this._inIcu = wasInIcu; 35791 return icu; 35792 } 35793 visitComment(comment, context) { 35794 const isOpening = _isOpeningComment(comment); 35795 if (isOpening && this._isInTranslatableSection) { 35796 this._reportError(comment, 'Could not start a block inside a translatable section'); 35797 return; 35798 } 35799 const isClosing = _isClosingComment(comment); 35800 if (isClosing && !this._inI18nBlock) { 35801 this._reportError(comment, 'Trying to close an unopened block'); 35802 return; 35803 } 35804 if (!this._inI18nNode && !this._inIcu) { 35805 if (!this._inI18nBlock) { 35806 if (isOpening) { 35807 // deprecated from v5 you should use <ng-container i18n> instead of i18n comments 35808 if (!i18nCommentsWarned && console && console.warn) { 35809 i18nCommentsWarned = true; 35810 const details = comment.sourceSpan.details ? `, ${comment.sourceSpan.details}` : ''; 35811 // TODO(ocombe): use a log service once there is a public one available 35812 console.warn(`I18n comments are deprecated, use an <ng-container> element instead (${comment.sourceSpan.start}${details})`); 35813 } 35814 this._inI18nBlock = true; 35815 this._blockStartDepth = this._depth; 35816 this._blockChildren = []; 35817 this._blockMeaningAndDesc = comment.value.replace(_I18N_COMMENT_PREFIX_REGEXP, '').trim(); 35818 this._openTranslatableSection(comment); 35819 } 35820 } else { 35821 if (isClosing) { 35822 if (this._depth == this._blockStartDepth) { 35823 this._closeTranslatableSection(comment, this._blockChildren); 35824 this._inI18nBlock = false;
35825 const message = this._addMessage(this._blockChildren, this._blockMeaningAndDesc); 35826 // merge attributes in sections 35827 const nodes = this._translateMessage(comment, message); 35828 return visitAll(this, nodes); 35829 } else { 35830 this._reportError(comment, 'I18N blocks should not cross element boundaries'); 35831 return; 35832 } 35833 } 35834 } 35835 } 35836 } 35837 visitText(text, context) { 35838 if (this._isInTranslatableSection) { 35839 this._mayBeAddBlockChildren(text); 35840 } 35841 return text; 35842 } 35843 visitElement(el, context) { 35844 this._mayBeAddBlockChildren(el); 35845 this._depth++; 35846 const wasInI18nNode = this._inI18nNode; 35847 const wasInImplicitNode = this._inImplicitNode; 35848 let childNodes = []; 35849 let translatedChildNodes = undefined; 35850 // Extract: 35851 // - top level nodes with the (implicit) "i18n" attribute if not already in a section 35852 // - ICU messages 35853 const i18nAttr = _getI18nAttr(el); 35854 const i18nMeta = i18nAttr ? i18nAttr.value : ''; 35855 const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu && !this._isInTranslatableSection; 35856 const isTopLevelImplicit = !wasInImplicitNode && isImplicit; 35857 this._inImplicitNode = wasInImplicitNode || isImplicit; 35858 if (!this._isInTranslatableSection && !this._inIcu) { 35859 if (i18nAttr || isTopLevelImplicit) { 35860 this._inI18nNode = true; 35861 const message = this._addMessage(el.children, i18nMeta); 35862 translatedChildNodes = this._translateMessage(el, message); 35863 } 35864 if (this._mode == _VisitorMode.Extract) { 35865 const isTranslatable = i18nAttr || isTopLevelImplicit; 35866 if (isTranslatable) this._openTranslatableSection(el); 35867 visitAll(this, el.children); 35868 if (isTranslatable) this._closeTranslatableSection(el, el.children); 35869 } 35870 } else { 35871 if (i18nAttr || isTopLevelImplicit) { 35872 this._reportError(el, 'Could not mark an element as translatable inside a translatable section'); 35873 } 35874 if (this._mode == _VisitorMode.Extract) { 35875 // Descend into child nodes for extraction 35876 visitAll(this, el.children); 35877 } 35878 } 35879 if (this._mode === _VisitorMode.Merge) { 35880 const visitNodes = translatedChildNodes || el.children; 35881 visitNodes.forEach(child => { 35882 const visited = child.visit(this, context); 35883 if (visited && !this._isInTranslatableSection) { 35884 // Do not add the children from translatable sections (= i18n blocks here) 35885 // They will be added later in this loop when the block closes (i.e. on `<!-- /i18n -->`) 35886 childNodes = childNodes.concat(visited); 35887 } 35888 }); 35889 } 35890 this._visitAttributesOf(el); 35891 this._depth--; 35892 this._inI18nNode = wasInI18nNode; 35893 this._inImplicitNode = wasInImplicitNode; 35894 if (this._mode === _VisitorMode.Merge) { 35895 const translatedAttrs = this._translateAttributes(el); 35896 return new Element(el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan, el.endSourceSpan); 35897 } 35898 return null; 35899 } 35900 visitAttribute(attribute, context) { 35901 throw new Error('unreachable code'); 35902 } 35903 visitBlock(block, context) { 35904 visitAll(this, block.children, context); 35905 } 35906 visitBlockParameter(parameter, context) {} 35907 _init(mode, interpolationConfig) { 35908 this._mode = mode; 35909 this._inI18nBlock = false; 35910 this._inI18nNode = false; 35911 this._depth = 0; 35912 this._inIcu = false; 35913 this._msgCountAtSectionStart = undefined; 35914 this._errors = []; 35915 this._messages = []; 35916 this._inImplicitNode = false; 35917 this._createI18nMessage = createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS); 35918 } 35919 // looks for translatable attributes 35920 _visitAttributesOf(el) { 35921 const explicitAttrNameToValue = {}; 35922 const implicitAttrNames = this._implicitAttrs[el.name] || []; 35923 el.attrs.filter(attr => attr.name.startsWith(_I18N_ATTR_PREFIX)).forEach(attr => explicitAttrNameToValue[attr.name.slice(_I18N_ATTR_PREFIX.length)] = attr.value); 35924 el.attrs.forEach(attr => { 35925 if (attr.name in explicitAttrNameToValue) { 35926 this._addMessage([attr], explicitAttrNameToValue[attr.name]); 35927 } else if (implicitAttrNames.some(name => attr.name === name)) { 35928 this._addMessage([attr]); 35929 } 35930 }); 35931 } 35932 // add a translatable message 35933 _addMessage(ast, msgMeta) { 35934 if (ast.length == 0 || ast.length == 1 && ast[0] instanceof Attribute && !ast[0].value) {
35935 // Do not create empty messages 35936 return null; 35937 } 35938 const { 35939 meaning, 35940 description, 35941 id 35942 } = _parseMessageMeta(msgMeta); 35943 const message = this._createI18nMessage(ast, meaning, description, id); 35944 this._messages.push(message); 35945 return message; 35946 } 35947 // Translates the given message given the `TranslationBundle` 35948 // This is used for translating elements / blocks - see `_translateAttributes` for attributes 35949 // no-op when called in extraction mode (returns []) 35950 _translateMessage(el, message) { 35951 if (message && this._mode === _VisitorMode.Merge) { 35952 const nodes = this._translations.get(message); 35953 if (nodes) { 35954 return nodes; 35955 } 35956 this._reportError(el, `Translation unavailable for message id="${this._translations.digest(message)}"`); 35957 } 35958 return []; 35959 } 35960 // translate the attributes of an element and remove i18n specific attributes 35961 _translateAttributes(el) { 35962 const attributes = el.attrs; 35963 const i18nParsedMessageMeta = {}; 35964 attributes.forEach(attr => { 35965 if (attr.name.startsWith(_I18N_ATTR_PREFIX)) { 35966 i18nParsedMessageMeta[attr.name.slice(_I18N_ATTR_PREFIX.length)] = _parseMessageMeta(attr.value); 35967 } 35968 }); 35969 const translatedAttributes = []; 35970 attributes.forEach(attr => { 35971 if (attr.name === _I18N_ATTR || attr.name.startsWith(_I18N_ATTR_PREFIX)) { 35972 // strip i18n specific attributes 35973 return; 35974 } 35975 if (attr.value && attr.value != '' && i18nParsedMessageMeta.hasOwnProperty(attr.name)) { 35976 const { 35977 meaning, 35978 description, 35979 id 35980 } = i18nParsedMessageMeta[attr.name]; 35981 const message = this._createI18nMessage([attr], meaning, description, id); 35982 const nodes = this._translations.get(message); 35983 if (nodes) { 35984 if (nodes.length == 0) { 35985 translatedAttributes.push(new Attribute(attr.name, '', attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)); 35986 } else if (nodes[0] instanceof Text) { 35987 const value = nodes[0].value; 35988 translatedAttributes.push(new Attribute(attr.name, value, attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)); 35989 } else { 35990 this._reportError(el, `Unexpected translation for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`); 35991 } 35992 } else { 35993 this._reportError(el, `Translation unavailable for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`); 35994 } 35995 } else { 35996 translatedAttributes.push(attr); 35997 } 35998 }); 35999 return translatedAttributes; 36000 } 36001 /** 36002 * Add the node as a child of the block when: 36003 * - we are in a block, 36004 * - we are not inside a ICU message (those are handled separately), 36005 * - the node is a "direct child" of the block 36006 */ 36007 _mayBeAddBlockChildren(node) { 36008 if (this._inI18nBlock && !this._inIcu && this._depth == this._blockStartDepth) { 36009 this._blockChildren.push(node); 36010 } 36011 } 36012 /** 36013 * Marks the start of a section, see `_closeTranslatableSection` 36014 */ 36015 _openTranslatableSection(node) { 36016 if (this._isInTranslatableSection) { 36017 this._reportError(node, 'Unexpected section start'); 36018 } else { 36019 this._msgCountAtSectionStart = this._messages.length; 36020 } 36021 } 36022 /** 36023 * A translatable section could be: 36024 * - the content of translatable element, 36025 * - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments 36026 */ 36027 get _isInTranslatableSection() { 36028 return this._msgCountAtSectionStart !== void 0; 36029 } 36030 /** 36031 * Terminates a section. 36032 * 36033 * If a section has only one significant children (comments not significant) then we should not 36034 * keep the message from this children: 36035 * 36036 * `<p i18n="meaning|description">{ICU message}</p>` would produce two messages: 36037 * - one for the <p> content with meaning and description, 36038 * - another one for the ICU message. 36039 * 36040 * In this case the last message is discarded as it contains less information (the AST is 36041 * otherwise identical). 36042 * 36043 * Note that we should still keep messages extracted from attributes inside the section (ie in the 36044 * ICU message here) 36045 */ 36046 _closeTranslatableSection(node, directChildren) { 36047 if (!this._isInTranslatableSection) { 36048 this._reportError(node, 'Unexpected section end'); 36049 return; 36050 } 36051 const startIndex = this._msgCountAtSectionStart; 36052 const significantChildren = directChildren.reduce((count, node) => count + (node instanceof Comment ? 0 : 1), 0); 36053 if (significantChildren == 1) { 36054 for (let i = this._messages.length - 1; i >= startIndex; i--) { 36055 const ast = this._messages[i].nodes; 36056 if (!(ast.length == 1 && ast[0] instanceof Text$2)) { 36057 this._messages.splice(i, 1); 36058 break; 36059 } 36060 } 36061 } 36062 this._msgCountAtSectionStart = undefined; 36063 } 36064 _reportError(node, msg) { 36065 this._errors.push(new I18nError(node.sourceSpan, msg)); 36066 } 36067} 36068function _isOpeningComment(n) { 36069 return !!(n instanceof Comment && n.value && n.value.startsWith('i18n')); 36070} 36071function _isClosingComment(n) { 36072 return !!(n instanceof Comment && n.value && n.value === '/i18n'); 36073} 36074function _getI18nAttr(p) { 36075 return p.attrs.find(attr => attr.name === _I18N_ATTR) || null; 36076} 36077function _parseMessageMeta(i18n) { 36078 if (!i18n) return { 36079 meaning: '', 36080 description: '', 36081 id: '' 36082 }; 36083 const idIndex = i18n.indexOf(ID_SEPARATOR); 36084 const descIndex = i18n.indexOf(MEANING_SEPARATOR); 36085 const [meaningAndDesc, id] = idIndex > -1 ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, '']; 36086 const [meaning, description] = descIndex > -1 ? [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : ['', meaningAndDesc]; 36087 return { 36088 meaning, 36089 description, 36090 id: id.trim() 36091 }; 36092} 36093class XmlTagDefinition { 36094 constructor() { 36095 this.closedByParent = false;
36096 this.implicitNamespacePrefix = null; 36097 this.isVoid = false; 36098 this.ignoreFirstLf = false; 36099 this.canSelfClose = true; 36100 this.preventNamespaceInheritance = false; 36101 } 36102 requireExtraParent(currentParent) { 36103 return false; 36104 } 36105 isClosedByChild(name) { 36106 return false; 36107 } 36108 getContentType() { 36109 return TagContentType.PARSABLE_DATA; 36110 } 36111} 36112const _TAG_DEFINITION = new XmlTagDefinition(); 36113function getXmlTagDefinition(tagName) { 36114 return _TAG_DEFINITION; 36115} 36116class XmlParser extends Parser { 36117 constructor() { 36118 super(getXmlTagDefinition); 36119 } 36120 parse(source, url, options = {}) { 36121 // Blocks aren't supported in an XML context. 36122 return super.parse(source, url, { 36123 ...options, 36124 tokenizeBlocks: false 36125 }); 36126 } 36127} 36128const _VERSION$1 = '1.2'; 36129const _XMLNS$1 = 'urn:oasis:names:tc:xliff:document:1.2'; 36130// TODO(vicb): make this a param (s/_/-/) 36131const _DEFAULT_SOURCE_LANG$1 = 'en'; 36132const _PLACEHOLDER_TAG$2 = 'x'; 36133const _MARKER_TAG$1 = 'mrk'; 36134const _FILE_TAG = 'file'; 36135const _SOURCE_TAG$1 = 'source'; 36136const _SEGMENT_SOURCE_TAG = 'seg-source'; 36137const _ALT_TRANS_TAG = 'alt-trans'; 36138const _TARGET_TAG$1 = 'target'; 36139const _UNIT_TAG$1 = 'trans-unit'; 36140const _CONTEXT_GROUP_TAG = 'context-group'; 36141const _CONTEXT_TAG = 'context'; 36142// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html 36143// https://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html 36144class Xliff extends Serializer { 36145 write(messages, locale) { 36146 const visitor = new _WriteVisitor$1(); 36147 const transUnits = []; 36148 messages.forEach(message => { 36149 let contextTags = []; 36150 message.sources.forEach(source => { 36151 let contextGroupTag = new Tag(_CONTEXT_GROUP_TAG, { 36152 purpose: 'location' 36153 }); 36154 contextGroupTag.children.push(new CR(10), new Tag(_CONTEXT_TAG, { 36155 'context-type': 'sourcefile' 36156 }, [new Text$1(source.filePath)]), new CR(10), new Tag(_CONTEXT_TAG, { 36157 'context-type': 'linenumber' 36158 }, [new Text$1(`${source.startLine}`)]), new CR(8)); 36159 contextTags.push(new CR(8), contextGroupTag); 36160 }); 36161 const transUnit = new Tag(_UNIT_TAG$1, { 36162 id: message.id, 36163 datatype: 'html' 36164 }); 36165 transUnit.children.push(new CR(8), new Tag(_SOURCE_TAG$1, {}, visitor.serialize(message.nodes)), ...contextTags); 36166 if (message.description) { 36167 transUnit.children.push(new CR(8), new Tag('note', { 36168 priority: '1', 36169 from: 'description' 36170 }, [new Text$1(message.description)])); 36171 } 36172 if (message.meaning) { 36173 transUnit.children.push(new CR(8), new Tag('note', { 36174 priority: '1', 36175 from: 'meaning' 36176 }, [new Text$1(message.meaning)])); 36177 } 36178 transUnit.children.push(new CR(6)); 36179 transUnits.push(new CR(6), transUnit); 36180 }); 36181 const body = new Tag('body', {}, [...transUnits, new CR(4)]); 36182 const file = new Tag('file', { 36183 'source-language': locale || _DEFAULT_SOURCE_LANG$1, 36184 datatype: 'plaintext', 36185 original: 'ng2.template' 36186 }, [new CR(4), body, new CR(2)]); 36187 const xliff = new Tag('xliff', { 36188 version: _VERSION$1, 36189 xmlns: _XMLNS$1 36190 }, [new CR(2), file, new CR()]); 36191 return serialize([new Declaration({ 36192 version: '1.0', 36193 encoding: 'UTF-8' 36194 }), new CR(), xliff, new CR()]); 36195 } 36196 load(content, url) { 36197 // xliff to xml nodes 36198 const xliffParser = new XliffParser(); 36199 const { 36200 locale, 36201 msgIdToHtml, 36202 errors 36203 } = xliffParser.parse(content, url); 36204 // xml nodes to i18n nodes 36205 const i18nNodesByMsgId = {}; 36206 const converter = new XmlToI18n$2();
36207 Object.keys(msgIdToHtml).forEach(msgId => { 36208 const { 36209 i18nNodes, 36210 errors: e 36211 } = converter.convert(msgIdToHtml[msgId], url); 36212 errors.push(...e); 36213 i18nNodesByMsgId[msgId] = i18nNodes; 36214 }); 36215 if (errors.length) { 36216 throw new Error(`xliff parse errors:\n${errors.join('\n')}`); 36217 } 36218 return { 36219 locale: locale, 36220 i18nNodesByMsgId 36221 }; 36222 } 36223 digest(message) { 36224 return digest$1(message); 36225 } 36226} 36227class _WriteVisitor$1 { 36228 visitText(text, context) { 36229 return [new Text$1(text.value)]; 36230 } 36231 visitContainer(container, context) { 36232 const nodes = []; 36233 container.children.forEach(node => nodes.push(...node.visit(this))); 36234 return nodes; 36235 } 36236 visitIcu(icu, context) { 36237 const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)]; 36238 Object.keys(icu.cases).forEach(c => { 36239 nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `)); 36240 }); 36241 nodes.push(new Text$1(`}`)); 36242 return nodes; 36243 } 36244 visitTagPlaceholder(ph, context) { 36245 const ctype = getCtypeForTag(ph.tag); 36246 if (ph.isVoid) { 36247 // void tags have no children nor closing tags 36248 return [new Tag(_PLACEHOLDER_TAG$2, { 36249 id: ph.startName, 36250 ctype, 36251 'equiv-text': `<${ph.tag}/>` 36252 })]; 36253 } 36254 const startTagPh = new Tag(_PLACEHOLDER_TAG$2, { 36255 id: ph.startName, 36256 ctype, 36257 'equiv-text': `<${ph.tag}>` 36258 }); 36259 const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, { 36260 id: ph.closeName, 36261 ctype, 36262 'equiv-text': `</${ph.tag}>` 36263 }); 36264 return [startTagPh, ...this.serialize(ph.children), closeTagPh]; 36265 } 36266 visitPlaceholder(ph, context) { 36267 return [new Tag(_PLACEHOLDER_TAG$2, { 36268 id: ph.name, 36269 'equiv-text': `{{${ph.value}}}` 36270 })]; 36271 } 36272 visitBlockPlaceholder(ph, context) { 36273 const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`; 36274 const startTagPh = new Tag(_PLACEHOLDER_TAG$2, { 36275 id: ph.startName, 36276 ctype, 36277 'equiv-text': `@${ph.name}` 36278 }); 36279 const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, { 36280 id: ph.closeName, 36281 ctype, 36282 'equiv-text': `}` 36283 }); 36284 return [startTagPh, ...this.serialize(ph.children), closeTagPh]; 36285 } 36286 visitIcuPlaceholder(ph, context) { 36287 const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map(value => value + ' {...}').join(' ')}}`; 36288 return [new Tag(_PLACEHOLDER_TAG$2, { 36289 id: ph.name, 36290 'equiv-text': equivText 36291 })]; 36292 } 36293 serialize(nodes) { 36294 return [].concat(...nodes.map(node => node.visit(this))); 36295 } 36296} 36297// TODO(vicb): add error management (structure) 36298// Extract messages as xml nodes from the xliff file 36299class XliffParser { 36300 constructor() { 36301 this._locale = null; 36302 } 36303 parse(xliff, url) { 36304 this._unitMlString = null; 36305 this._msgIdToHtml = {}; 36306 const xml = new XmlParser().parse(xliff, url); 36307 this._errors = xml.errors; 36308 visitAll(this, xml.rootNodes, null); 36309 return { 36310 msgIdToHtml: this._msgIdToHtml, 36311 errors: this._errors, 36312 locale: this._locale 36313 }; 36314 } 36315 visitElement(element, context) { 36316 switch (element.name) { 36317 case _UNIT_TAG$1: 36318 this._unitMlString = null; 36319 const idAttr = element.attrs.find(attr => attr.name === 'id'); 36320 if (!idAttr) { 36321 this._addError(element, `<${_UNIT_TAG$1}> misses the "id" attribute`); 36322 } else { 36323 const id = idAttr.value; 36324 if (this._msgIdToHtml.hasOwnProperty(id)) { 36325 this._addError(element, `Duplicated translations for msg ${id}`); 36326 } else { 36327 visitAll(this, element.children, null); 36328 if (typeof this._unitMlString === 'string') { 36329 this._msgIdToHtml[id] = this._unitMlString; 36330 } else { 36331 this._addError(element, `Message ${id} misses a translation`); 36332 } 36333 } 36334 } 36335 break; 36336 // ignore those tags 36337 case _SOURCE_TAG$1: 36338 case _SEGMENT_SOURCE_TAG: 36339 case _ALT_TRANS_TAG: 36340 break; 36341 case _TARGET_TAG$1: 36342 const innerTextStart = element.startSourceSpan.end.offset; 36343 const innerTextEnd = element.endSourceSpan.start.offset; 36344 const content = element.startSourceSpan.start.file.content; 36345 const innerText = content.slice(innerTextStart, innerTextEnd); 36346 this._unitMlString = innerText; 36347 break; 36348 case _FILE_TAG: 36349 const localeAttr = element.attrs.find(attr => attr.name === 'target-language'); 36350 if (localeAttr) { 36351 this._locale = localeAttr.value; 36352 } 36353 visitAll(this, element.children, null); 36354 break; 36355 default: 36356 // TODO(vicb): assert file structure, xliff version 36357 // For now only recurse on unhandled nodes 36358 visitAll(this, element.children, null); 36359 } 36360 } 36361 visitAttribute(attribute, context) {} 36362 visitText(text, context) {} 36363 visitComment(comment, context) {} 36364 visitExpansion(expansion, context) {} 36365 visitExpansionCase(expansionCase, context) {} 36366 visitBlock(block, context) {} 36367 visitBlockParameter(parameter, context) {} 36368 _addError(node, message) { 36369 this._errors.push(new I18nError(node.sourceSpan, message)); 36370 } 36371} 36372// Convert ml nodes (xliff syntax) to i18n nodes 36373class XmlToI18n$2 { 36374 convert(message, url) { 36375 const xmlIcu = new XmlParser().parse(message, url, { 36376 tokenizeExpansionForms: true 36377 }); 36378 this._errors = xmlIcu.errors; 36379 const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : [].concat(...visitAll(this, xmlIcu.rootNodes)); 36380 return { 36381 i18nNodes: i18nNodes, 36382 errors: this._errors 36383 }; 36384 } 36385 visitText(text, context) { 36386 return new Text$2(text.value, text.sourceSpan); 36387 } 36388 visitElement(el, context) { 36389 if (el.name === _PLACEHOLDER_TAG$2) { 36390 const nameAttr = el.attrs.find(attr => attr.name === 'id'); 36391 if (nameAttr) { 36392 return new Placeholder('', nameAttr.value, el.sourceSpan); 36393 } 36394 this._addError(el, `<${_PLACEHOLDER_TAG$2}> misses the "id" attribute`); 36395 return null; 36396 } 36397 if (el.name === _MARKER_TAG$1) { 36398 return [].concat(...visitAll(this, el.children)); 36399 } 36400 this._addError(el, `Unexpected tag`); 36401 return null; 36402 } 36403 visitExpansion(icu, context) { 36404 const caseMap = {};
36405 visitAll(this, icu.cases).forEach(c => { 36406 caseMap[c.value] = new Container(c.nodes, icu.sourceSpan); 36407 }); 36408 return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan); 36409 } 36410 visitExpansionCase(icuCase, context) { 36411 return { 36412 value: icuCase.value, 36413 nodes: visitAll(this, icuCase.expression) 36414 }; 36415 } 36416 visitComment(comment, context) {} 36417 visitAttribute(attribute, context) {} 36418 visitBlock(block, context) {} 36419 visitBlockParameter(parameter, context) {} 36420 _addError(node, message) { 36421 this._errors.push(new I18nError(node.sourceSpan, message)); 36422 } 36423} 36424function getCtypeForTag(tag) { 36425 switch (tag.toLowerCase()) { 36426 case 'br': 36427 return 'lb'; 36428 case 'img': 36429 return 'image'; 36430 default: 36431 return `x-${tag}`; 36432 } 36433} 36434const _VERSION = '2.0'; 36435const _XMLNS = 'urn:oasis:names:tc:xliff:document:2.0'; 36436// TODO(vicb): make this a param (s/_/-/) 36437const _DEFAULT_SOURCE_LANG = 'en'; 36438const _PLACEHOLDER_TAG$1 = 'ph'; 36439const _PLACEHOLDER_SPANNING_TAG = 'pc'; 36440const _MARKER_TAG = 'mrk'; 36441const _XLIFF_TAG = 'xliff'; 36442const _SOURCE_TAG = 'source'; 36443const _TARGET_TAG = 'target'; 36444const _UNIT_TAG = 'unit'; 36445// https://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html 36446class Xliff2 extends Serializer { 36447 write(messages, locale) { 36448 const visitor = new _WriteVisitor(); 36449 const units = []; 36450 messages.forEach(message => { 36451 const unit = new Tag(_UNIT_TAG, { 36452 id: message.id 36453 }); 36454 const notes = new Tag('notes'); 36455 if (message.description || message.meaning) { 36456 if (message.description) { 36457 notes.children.push(new CR(8), new Tag('note', { 36458 category: 'description' 36459 }, [new Text$1(message.description)])); 36460 } 36461 if (message.meaning) { 36462 notes.children.push(new CR(8), new Tag('note', { 36463 category: 'meaning' 36464 }, [new Text$1(message.meaning)])); 36465 } 36466 } 36467 message.sources.forEach(source => { 36468 notes.children.push(new CR(8), new Tag('note', { 36469 category: 'location' 36470 }, [new Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)])); 36471 }); 36472 notes.children.push(new CR(6)); 36473 unit.children.push(new CR(6), notes); 36474 const segment = new Tag('segment'); 36475 segment.children.push(new CR(8), new Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), new CR(6)); 36476 unit.children.push(new CR(6), segment, new CR(4)); 36477 units.push(new CR(4), unit); 36478 }); 36479 const file = new Tag('file', { 36480 'original': 'ng.template', 36481 id: 'ngi18n' 36482 }, [...units, new CR(2)]); 36483 const xliff = new Tag(_XLIFF_TAG, { 36484 version: _VERSION, 36485 xmlns: _XMLNS, 36486 srcLang: locale || _DEFAULT_SOURCE_LANG 36487 }, [new CR(2), file, new CR()]); 36488 return serialize([new Declaration({ 36489 version: '1.0', 36490 encoding: 'UTF-8' 36491 }), new CR(), xliff, new CR()]); 36492 } 36493 load(content, url) { 36494 // xliff to xml nodes 36495 const xliff2Parser = new Xliff2Parser(); 36496 const { 36497 locale, 36498 msgIdToHtml, 36499 errors 36500 } = xliff2Parser.parse(content, url); 36501 // xml nodes to i18n nodes 36502 const i18nNodesByMsgId = {}; 36503 const converter = new XmlToI18n$1(); 36504 Object.keys(msgIdToHtml).forEach(msgId => { 36505 const { 36506 i18nNodes, 36507 errors: e 36508 } = converter.convert(msgIdToHtml[msgId], url); 36509 errors.push(...e); 36510 i18nNodesByMsgId[msgId] = i18nNodes; 36511 }); 36512 if (errors.length) { 36513 throw new Error(`xliff2 parse errors:\n${errors.join('\n')}`); 36514 } 36515 return { 36516 locale: locale, 36517 i18nNodesByMsgId 36518 }; 36519 } 36520 digest(message) { 36521 return decimalDigest(message); 36522 } 36523} 36524class _WriteVisitor { 36525 constructor() { 36526 this._nextPlaceholderId = 0; 36527 } 36528 visitText(text, context) { 36529 return [new Text$1(text.value)]; 36530 } 36531 visitContainer(container, context) { 36532 const nodes = []; 36533 container.children.forEach(node => nodes.push(...node.visit(this))); 36534 return nodes; 36535 } 36536 visitIcu(icu, context) { 36537 const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
36538 Object.keys(icu.cases).forEach(c => { 36539 nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `)); 36540 }); 36541 nodes.push(new Text$1(`}`)); 36542 return nodes; 36543 } 36544 visitTagPlaceholder(ph, context) { 36545 const type = getTypeForTag(ph.tag); 36546 if (ph.isVoid) { 36547 const tagPh = new Tag(_PLACEHOLDER_TAG$1, { 36548 id: (this._nextPlaceholderId++).toString(), 36549 equiv: ph.startName, 36550 type: type, 36551 disp: `<${ph.tag}/>` 36552 }); 36553 return [tagPh]; 36554 } 36555 const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, { 36556 id: (this._nextPlaceholderId++).toString(), 36557 equivStart: ph.startName, 36558 equivEnd: ph.closeName, 36559 type: type, 36560 dispStart: `<${ph.tag}>`, 36561 dispEnd: `</${ph.tag}>` 36562 }); 36563 const nodes = [].concat(...ph.children.map(node => node.visit(this))); 36564 if (nodes.length) { 36565 nodes.forEach(node => tagPc.children.push(node)); 36566 } else { 36567 tagPc.children.push(new Text$1('')); 36568 } 36569 return [tagPc]; 36570 } 36571 visitPlaceholder(ph, context) { 36572 const idStr = (this._nextPlaceholderId++).toString(); 36573 return [new Tag(_PLACEHOLDER_TAG$1, { 36574 id: idStr, 36575 equiv: ph.name, 36576 disp: `{{${ph.value}}}` 36577 })]; 36578 } 36579 visitBlockPlaceholder(ph, context) { 36580 const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, { 36581 id: (this._nextPlaceholderId++).toString(), 36582 equivStart: ph.startName, 36583 equivEnd: ph.closeName, 36584 type: 'other', 36585 dispStart: `@${ph.name}`, 36586 dispEnd: `}` 36587 }); 36588 const nodes = [].concat(...ph.children.map(node => node.visit(this))); 36589 if (nodes.length) { 36590 nodes.forEach(node => tagPc.children.push(node)); 36591 } else { 36592 tagPc.children.push(new Text$1('')); 36593 } 36594 return [tagPc]; 36595 } 36596 visitIcuPlaceholder(ph, context) { 36597 const cases = Object.keys(ph.value.cases).map(value => value + ' {...}').join(' '); 36598 const idStr = (this._nextPlaceholderId++).toString(); 36599 return [new Tag(_PLACEHOLDER_TAG$1, { 36600 id: idStr, 36601 equiv: ph.name, 36602 disp: `{${ph.value.expression}, ${ph.value.type}, ${cases}}` 36603 })]; 36604 } 36605 serialize(nodes) { 36606 this._nextPlaceholderId = 0; 36607 return [].concat(...nodes.map(node => node.visit(this))); 36608 } 36609} 36610// Extract messages as xml nodes from the xliff file 36611class Xliff2Parser { 36612 constructor() { 36613 this._locale = null; 36614 } 36615 parse(xliff, url) { 36616 this._unitMlString = null; 36617 this._msgIdToHtml = {}; 36618 const xml = new XmlParser().parse(xliff, url); 36619 this._errors = xml.errors; 36620 visitAll(this, xml.rootNodes, null); 36621 return { 36622 msgIdToHtml: this._msgIdToHtml, 36623 errors: this._errors, 36624 locale: this._locale 36625 }; 36626 } 36627 visitElement(element, context) { 36628 switch (element.name) { 36629 case _UNIT_TAG: 36630 this._unitMlString = null; 36631 const idAttr = element.attrs.find(attr => attr.name === 'id'); 36632 if (!idAttr) { 36633 this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`); 36634 } else { 36635 const id = idAttr.value; 36636 if (this._msgIdToHtml.hasOwnProperty(id)) { 36637 this._addError(element, `Duplicated translations for msg ${id}`); 36638 } else { 36639 visitAll(this, element.children, null); 36640 if (typeof this._unitMlString === 'string') { 36641 this._msgIdToHtml[id] = this._unitMlString; 36642 } else { 36643 this._addError(element, `Message ${id} misses a translation`); 36644 } 36645 } 36646 } 36647 break; 36648 case _SOURCE_TAG: 36649 // ignore source message 36650 break; 36651 case _TARGET_TAG: 36652 const innerTextStart = element.startSourceSpan.end.offset; 36653 const innerTextEnd = element.endSourceSpan.start.offset; 36654 const content = element.startSourceSpan.start.file.content; 36655 const innerText = content.slice(innerTextStart, innerTextEnd); 36656 this._unitMlString = innerText; 36657 break; 36658 case _XLIFF_TAG: 36659 const localeAttr = element.attrs.find(attr => attr.name === 'trgLang'); 36660 if (localeAttr) { 36661 this._locale = localeAttr.value; 36662 } 36663 const versionAttr = element.attrs.find(attr => attr.name === 'version'); 36664 if (versionAttr) { 36665 const version = versionAttr.value; 36666 if (version !== '2.0') { 36667 this._addError(element, `The XLIFF file version ${version} is not compatible with XLIFF 2.0 serializer`); 36668 } else { 36669 visitAll(this, element.children, null); 36670 } 36671 } 36672 break; 36673 default: 36674 visitAll(this, element.children, null); 36675 } 36676 } 36677 visitAttribute(attribute, context) {} 36678 visitText(text, context) {} 36679 visitComment(comment, context) {} 36680 visitExpansion(expansion, context) {} 36681 visitExpansionCase(expansionCase, context) {} 36682 visitBlock(block, context) {} 36683 visitBlockParameter(parameter, context) {} 36684 _addError(node, message) { 36685 this._errors.push(new I18nError(node.sourceSpan, message)); 36686 } 36687} 36688// Convert ml nodes (xliff syntax) to i18n nodes 36689class XmlToI18n$1 { 36690 convert(message, url) { 36691 const xmlIcu = new XmlParser().parse(message, url, { 36692 tokenizeExpansionForms: true 36693 }); 36694 this._errors = xmlIcu.errors; 36695 const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : [].concat(...visitAll(this, xmlIcu.rootNodes)); 36696 return { 36697 i18nNodes, 36698 errors: this._errors 36699 }; 36700 } 36701 visitText(text, context) { 36702 return new Text$2(text.value, text.sourceSpan); 36703 } 36704 visitElement(el, context) { 36705 switch (el.name) { 36706 case _PLACEHOLDER_TAG$1: 36707 const nameAttr = el.attrs.find(attr => attr.name === 'equiv'); 36708 if (nameAttr) { 36709 return [new Placeholder('', nameAttr.value, el.sourceSpan)]; 36710 } 36711 this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equiv" attribute`); 36712 break; 36713 case _PLACEHOLDER_SPANNING_TAG: 36714 const startAttr = el.attrs.find(attr => attr.name === 'equivStart'); 36715 const endAttr = el.attrs.find(attr => attr.name === 'equivEnd'); 36716 if (!startAttr) { 36717 this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equivStart" attribute`); 36718 } else if (!endAttr) { 36719 this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "equivEnd" attribute`); 36720 } else { 36721 const startId = startAttr.value; 36722 const endId = endAttr.value; 36723 const nodes = []; 36724 return nodes.concat(new Placeholder('', startId, el.sourceSpan), ...el.children.map(node =>
36724 node.visit(this, null)), new Placeholder('', endId, el.sourceSpan)); 36725 } 36726 break; 36727 case _MARKER_TAG: 36728 return [].concat(...visitAll(this, el.children)); 36729 default: 36730 this._addError(el, `Unexpected tag`); 36731 } 36732 return null; 36733 } 36734 visitExpansion(icu, context) { 36735 const caseMap = {}; 36736 visitAll(this, icu.cases).forEach(c => { 36737 caseMap[c.value] = new Container(c.nodes, icu.sourceSpan); 36738 }); 36739 return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan); 36740 } 36741 visitExpansionCase(icuCase, context) { 36742 return { 36743 value: icuCase.value, 36744 nodes: [].concat(...visitAll(this, icuCase.expression)) 36745 }; 36746 } 36747 visitComment(comment, context) {} 36748 visitAttribute(attribute, context) {} 36749 visitBlock(block, context) {} 36750 visitBlockParameter(parameter, context) {} 36751 _addError(node, message) { 36752 this._errors.push(new I18nError(node.sourceSpan, message)); 36753 } 36754} 36755function getTypeForTag(tag) { 36756 switch (tag.toLowerCase()) { 36757 case 'br': 36758 case 'b': 36759 case 'i': 36760 case 'u': 36761 return 'fmt'; 36762 case 'img': 36763 return 'image'; 36764 case 'a': 36765 return 'link'; 36766 default: 36767 return 'other'; 36768 } 36769} 36770const _TRANSLATIONS_TAG = 'translationbundle'; 36771const _TRANSLATION_TAG = 'translation'; 36772const _PLACEHOLDER_TAG = 'ph'; 36773class Xtb extends Serializer { 36774 write(messages, locale) { 36775 throw new Error('Unsupported'); 36776 } 36777 load(content, url) { 36778 // xtb to xml nodes 36779 const xtbParser = new XtbParser(); 36780 const { 36781 locale, 36782 msgIdToHtml, 36783 errors 36784 } = xtbParser.parse(content, url); 36785 // xml nodes to i18n nodes 36786 const i18nNodesByMsgId = {}; 36787 const converter = new XmlToI18n(); 36788 // Because we should be able to load xtb files that rely on features not supported by angular, 36789 // we need to delay the conversion of html to i18n nodes so that non angular messages are not 36790 // converted 36791 Object.keys(msgIdToHtml).forEach(msgId => { 36792 const valueFn = function () { 36793 const { 36794 i18nNodes, 36795 errors 36796 } = converter.convert(msgIdToHtml[msgId], url); 36797 if (errors.length) { 36798 throw new Error(`xtb parse errors:\n${errors.join('\n')}`); 36799 } 36800 return i18nNodes; 36801 }; 36802 createLazyProperty(i18nNodesByMsgId, msgId, valueFn); 36803 }); 36804 if (errors.length) { 36805 throw new Error(`xtb parse errors:\n${errors.join('\n')}`); 36806 } 36807 return { 36808 locale: locale, 36809 i18nNodesByMsgId 36810 }; 36811 } 36812 digest(message) { 36813 return digest(message); 36814 } 36815 createNameMapper(message) { 36816 return new SimplePlaceholderMapper(message, toPublicName); 36817 } 36818} 36819function createLazyProperty(messages, id, valueFn) { 36820 Object.defineProperty(messages, id, { 36821 configurable: true, 36822 enumerable: true, 36823 get: function () { 36824 const value = valueFn(); 36825 Object.defineProperty(messages, id, { 36826 enumerable: true, 36827 value 36828 }); 36829 return value; 36830 }, 36831 set: _ => { 36832 throw new Error('Could not overwrite an XTB translation'); 36833 } 36834 }); 36835} 36836// Extract messages as xml nodes from the xtb file 36837class XtbParser { 36838 constructor() { 36839 this._locale = null; 36840 } 36841 parse(xtb, url) { 36842 this._bundleDepth = 0; 36843 this._msgIdToHtml = {}; 36844 // We can not parse the ICU messages at this point as some messages might not originate 36845 // from Angular that could not be lex'd. 36846 const xml = new XmlParser().parse(xtb, url); 36847 this._errors = xml.errors; 36848 visitAll(this, xml.rootNodes); 36849 return { 36850 msgIdToHtml: this._msgIdToHtml, 36851 errors: this._errors, 36852 locale: this._locale 36853 }; 36854 } 36855 visitElement(element, context) { 36856 switch (element.name) { 36857 case _TRANSLATIONS_TAG: 36858 this._bundleDepth++; 36859 if (this._bundleDepth > 1) { 36860 this._addError(element, `<${_TRANSLATIONS_TAG}> elements can not be nested`); 36861 } 36862 const langAttr = element.attrs.find(attr => attr.name === 'lang'); 36863 if (langAttr) { 36864 this._locale = langAttr.value; 36865 } 36866 visitAll(this, element.children, null); 36867 this._bundleDepth--; 36868 break; 36869 case _TRANSLATION_TAG: 36870 const idAttr = element.attrs.find(attr => attr.name === 'id'); 36871 if (!idAttr) { 36872 this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`); 36873 } else { 36874 const id = idAttr.value; 36875 if (this._msgIdToHtml.hasOwnProperty(id)) { 36876 this._addError(element, `Duplicated translations for msg ${id}`); 36877 } else { 36878 const innerTextStart = element.startSourceSpan.end.offset; 36879 const innerTextEnd = element.endSourceSpan.start.offset; 36880 const content = element.startSourceSpan.start.file.content; 36881 const innerText = content.slice(innerTextStart, innerTextEnd); 36882 this._msgIdToHtml[id] = innerText; 36883 } 36884 } 36885 break; 36886 default: 36887 this._addError(element, 'Unexpected tag'); 36888 } 36889 } 36890 visitAttribute(attribute, context) {} 36891 visitText(text, context) {} 36892 visitComment(comment, context) {} 36893 visitExpansion(expansion, context) {} 36894 visitExpansionCase(expansionCase, context) {} 36895 visitBlock(block, context) {} 36896 visitBlockParameter(block, context) {} 36897 _addError(node, message) { 36898 this._errors.push(new I18nError(node.sourceSpan, message)); 36899 } 36900} 36901// Convert ml nodes (xtb syntax) to i18n nodes 36902class XmlToI18n { 36903 convert(message, url) { 36904 const xmlIcu = new XmlParser().parse(message, url, { 36905 tokenizeExpansionForms: true 36906 }); 36907 this._errors = xmlIcu.errors; 36908 const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? [] : visitAll(this, xmlIcu.rootNodes); 36909 return { 36910 i18nNodes, 36911 errors: this._errors 36912 }; 36913 } 36914 visitText(text, context) { 36915 return new Text$2(text.value, text.sourceSpan); 36916 } 36917 visitExpansion(icu, context) { 36918 const caseMap = {};
36919 visitAll(this, icu.cases).forEach(c => { 36920 caseMap[c.value] = new Container(c.nodes, icu.sourceSpan); 36921 }); 36922 return new Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan); 36923 } 36924 visitExpansionCase(icuCase, context) { 36925 return { 36926 value: icuCase.value, 36927 nodes: visitAll(this, icuCase.expression) 36928 }; 36929 } 36930 visitElement(el, context) { 36931 if (el.name === _PLACEHOLDER_TAG) { 36932 const nameAttr = el.attrs.find(attr => attr.name === 'name'); 36933 if (nameAttr) { 36934 return new Placeholder('', nameAttr.value, el.sourceSpan); 36935 } 36936 this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`); 36937 } else { 36938 this._addError(el, `Unexpected tag`); 36939 } 36940 return null; 36941 } 36942 visitComment(comment, context) {} 36943 visitAttribute(attribute, context) {} 36944 visitBlock(block, context) {} 36945 visitBlockParameter(block, context) {} 36946 _addError(node, message) { 36947 this._errors.push(new I18nError(node.sourceSpan, message)); 36948 } 36949} 36950 36951/** 36952 * A container for translated messages 36953 */ 36954class TranslationBundle { 36955 constructor(_i18nNodesByMsgId = {}, locale, digest, mapperFactory, missingTranslationStrategy = MissingTranslationStrategy.Warning, console) { 36956 this._i18nNodesByMsgId = _i18nNodesByMsgId; 36957 this.digest = digest; 36958 this.mapperFactory = mapperFactory; 36959 this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, locale, digest, mapperFactory, missingTranslationStrategy, console); 36960 } 36961 // Creates a `TranslationBundle` by parsing the given `content` with the `serializer`. 36962 static load(content, url, serializer, missingTranslationStrategy, console) { 36963 const { 36964 locale, 36965 i18nNodesByMsgId 36966 } = serializer.load(content, url); 36967 const digestFn = m => serializer.digest(m); 36968 const mapperFactory = m => serializer.createNameMapper(m); 36969 return new TranslationBundle(i18nNodesByMsgId, locale, digestFn, mapperFactory, missingTranslationStrategy, console); 36970 } 36971 // Returns the translation as HTML nodes from the given source message. 36972 get(srcMsg) { 36973 const html = this._i18nToHtml.convert(srcMsg); 36974 if (html.errors.length) { 36975 throw new Error(html.errors.join('\n')); 36976 } 36977 return html.nodes; 36978 } 36979 has(srcMsg) { 36980 return this.digest(srcMsg) in this._i18nNodesByMsgId; 36981 } 36982} 36983class I18nToHtmlVisitor { 36984 constructor(_i18nNodesByMsgId = {}, _locale, _digest, _mapperFactory, _missingTranslationStrategy, _console) { 36985 this._i18nNodesByMsgId = _i18nNodesByMsgId; 36986 this._locale = _locale; 36987 this._digest = _digest; 36988 this._mapperFactory = _mapperFactory; 36989 this._missingTranslationStrategy = _missingTranslationStrategy; 36990 this._console = _console; 36991 this._errors = []; 36992 this._contextStack = []; 36993 } 36994 convert(srcMsg) { 36995 this._contextStack.length = 0; 36996 this._errors.length = 0; 36997 // i18n to text 36998 const text = this._convertToText(srcMsg); 36999 // text to html 37000 const url = srcMsg.nodes[0].sourceSpan.start.file.url; 37001 const html = new HtmlParser().parse(text, url, { 37002 tokenizeExpansionForms: true 37003 }); 37004 return { 37005 nodes: html.rootNodes, 37006 errors: [...this._errors, ...html.errors] 37007 }; 37008 } 37009 visitText(text, context) { 37010 // `convert()` uses an `HtmlParser` to return `html.Node`s 37011 // we should then make sure that any special characters are escaped 37012 return escapeXml(text.value); 37013 } 37014 visitContainer(container, context) { 37015 return container.children.map(n => n.visit(this)).join(''); 37016 } 37017 visitIcu(icu, context) { 37018 const cases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 37019 // TODO(vicb): Once all format switch to using expression placeholders 37020 // we should throw when the placeholder is not in the source message 37021 const exp = this._srcMsg.placeholders.hasOwnProperty(icu.expression) ? this._srcMsg.placeholders[icu.expression].text : icu.expression; 37022 return `{${exp}, ${icu.type}, ${cases.join(' ')}}`; 37023 } 37024 visitPlaceholder(ph, context) { 37025 const phName = this._mapper(ph.name); 37026 if (this._srcMsg.placeholders.hasOwnProperty(phName)) { 37027 return this._srcMsg.placeholders[phName].text; 37028 } 37029 if (this._srcMsg.placeholderToMessage.hasOwnProperty(phName)) { 37030 return this._convertToText(this._srcMsg.placeholderToMessage[phName]); 37031 } 37032 this._addError(ph, `Unknown placeholder "${ph.name}"`); 37033 return ''; 37034 } 37035 // Loaded message contains only placeholders (vs tag and icu placeholders). 37036 // However when a translation can not be found, we need to serialize the source message 37037 // which can contain tag placeholders 37038 visitTagPlaceholder(ph, context) { 37039 const tag = `${ph.tag}`; 37040 const attrs = Object.keys(ph.attrs).map(name => `${name}="${ph.attrs[name]}"`).join(' '); 37041 if (ph.isVoid) { 37042 return `<${tag} ${attrs}/>`; 37043 } 37044 const children = ph.children.map(c => c.visit(this)).join(''); 37045 return `<${tag} ${attrs}>${children}</${tag}>`; 37046 } 37047 // Loaded message contains only placeholders (vs tag and icu placeholders). 37048 // However when a translation can not be found, we need to serialize the source message 37049 // which can contain tag placeholders 37050 visitIcuPlaceholder(ph, context) { 37051 // An ICU placeholder references the source message to be serialized 37052 return this._convertToText(this._srcMsg.placeholderToMessage[ph.name]); 37053 } 37054 visitBlockPlaceholder(ph, context) { 37055 const params = ph.parameters.length === 0 ? '' : ` (${ph.parameters.join('; ')})`; 37056 const children = ph.children.map(c => c.visit(this)).join(''); 37057 return `@${ph.name}${params} {${children}}`; 37058 } 37059 /**
37060 * Convert a source message to a translated text string: 37061 * - text nodes are replaced with their translation, 37062 * - placeholders are replaced with their content, 37063 * - ICU nodes are converted to ICU expressions. 37064 */ 37065 _convertToText(srcMsg) { 37066 const id = this._digest(srcMsg); 37067 const mapper = this._mapperFactory ? this._mapperFactory(srcMsg) : null; 37068 let nodes; 37069 this._contextStack.push({ 37070 msg: this._srcMsg, 37071 mapper: this._mapper 37072 }); 37073 this._srcMsg = srcMsg; 37074 if (this._i18nNodesByMsgId.hasOwnProperty(id)) { 37075 // When there is a translation use its nodes as the source 37076 // And create a mapper to convert serialized placeholder names to internal names 37077 nodes = this._i18nNodesByMsgId[id]; 37078 this._mapper = name => mapper ? mapper.toInternalName(name) : name; 37079 } else { 37080 // When no translation has been found 37081 // - report an error / a warning / nothing, 37082 // - use the nodes from the original message 37083 // - placeholders are already internal and need no mapper 37084 if (this._missingTranslationStrategy === MissingTranslationStrategy.Error) { 37085 const ctx = this._locale ? ` for locale "${this._locale}"` : ''; 37086 this._addError(srcMsg.nodes[0], `Missing translation for message "${id}"${ctx}`); 37087 } else if (this._console && this._missingTranslationStrategy === MissingTranslationStrategy.Warning) { 37088 const ctx = this._locale ? ` for locale "${this._locale}"` : ''; 37089 this._console.warn(`Missing translation for message "${id}"${ctx}`); 37090 } 37091 nodes = srcMsg.nodes; 37092 this._mapper = name => name; 37093 } 37094 const text = nodes.map(node => node.visit(this)).join(''); 37095 const context = this._contextStack.pop(); 37096 this._srcMsg = context.msg; 37097 this._mapper = context.mapper; 37098 return text; 37099 } 37100 _addError(el, msg) { 37101 this._errors.push(new I18nError(el.sourceSpan, msg)); 37102 } 37103} 37104class I18NHtmlParser { 37105 constructor(_htmlParser, translations, translationsFormat, missingTranslation = MissingTranslationStrategy.Warning, console) { 37106 this._htmlParser = _htmlParser; 37107 if (translations) { 37108 const serializer = createSerializer(translationsFormat); 37109 this._translationBundle = TranslationBundle.load(translations, 'i18n', serializer, missingTranslation, console); 37110 } else { 37111 this._translationBundle = new TranslationBundle({}, null, digest$1, undefined, missingTranslation, console); 37112 } 37113 } 37114 parse(source, url, options = {}) { 37115 const interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG; 37116 const parseResult = this._htmlParser.parse(source, url, { 37117 interpolationConfig, 37118 ...options 37119 }); 37120 if (parseResult.errors.length) { 37121 return new ParseTreeResult(parseResult.rootNodes, parseResult.errors); 37122 } 37123 return mergeTranslations(parseResult.rootNodes, this._translationBundle, interpolationConfig, [], {}); 37124 } 37125} 37126function createSerializer(format) { 37127 format = (format || 'xlf').toLowerCase(); 37128 switch (format) { 37129 case 'xmb': 37130 return new Xmb(); 37131 case 'xtb': 37132 return new Xtb(); 37133 case 'xliff2': 37134 case 'xlf2': 37135 return new Xliff2(); 37136 case 'xliff': 37137 case 'xlf': 37138 default: 37139 return new Xliff(); 37140 } 37141} 37142 37143/** 37144 * A container for message extracted from the templates. 37145 */ 37146class MessageBundle { 37147 constructor(_htmlParser, _implicitTags, _implicitAttrs, _locale = null) { 37148 this._htmlParser = _htmlParser; 37149 this._implicitTags = _implicitTags; 37150 this._implicitAttrs = _implicitAttrs; 37151 this._locale = _locale; 37152 this._messages = []; 37153 } 37154 updateFromTemplate(html, url, interpolationConfig) { 37155 const htmlParserResult = this._htmlParser.parse(html, url, { 37156 tokenizeExpansionForms: true, 37157 interpolationConfig 37158 }); 37159 if (htmlParserResult.errors.length) { 37160 return htmlParserResult.errors; 37161 } 37162 const i18nParserResult = extractMessages(htmlParserResult.rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs); 37163 if (i18nParserResult.errors.length) { 37164 return i18nParserResult.errors; 37165 } 37166 this._messages.push(...i18nParserResult.messages); 37167 return []; 37168 } 37169 // Return the message in the internal format 37170 // The public (serialized) format might be different, see the `write` method. 37171 getMessages() { 37172 return this._messages; 37173 }
37174 write(serializer, filterSources) { 37175 const messages = {}; 37176 const mapperVisitor = new MapPlaceholderNames(); 37177 // Deduplicate messages based on their ID 37178 this._messages.forEach(message => { 37179 const id = serializer.digest(message); 37180 if (!messages.hasOwnProperty(id)) { 37181 messages[id] = message; 37182 } else { 37183 messages[id].sources.push(...message.sources); 37184 } 37185 }); 37186 // Transform placeholder names using the serializer mapping 37187 const msgList = Object.keys(messages).map(id => { 37188 const mapper = serializer.createNameMapper(messages[id]); 37189 const src = messages[id]; 37190 const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes; 37191 let transformedMessage = new Message(nodes, {}, {}, src.meaning, src.description, id); 37192 transformedMessage.sources = src.sources; 37193 if (filterSources) { 37194 transformedMessage.sources.forEach(source => source.filePath = filterSources(source.filePath)); 37195 } 37196 return transformedMessage; 37197 }); 37198 return serializer.write(msgList, this._locale); 37199 } 37200} 37201// Transform an i18n AST by renaming the placeholder nodes with the given mapper 37202class MapPlaceholderNames extends CloneVisitor { 37203 convert(nodes, mapper) { 37204 return mapper ? nodes.map(n => n.visit(this, mapper)) : nodes; 37205 } 37206 visitTagPlaceholder(ph, mapper) { 37207 const startName = mapper.toPublicName(ph.startName); 37208 const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName; 37209 const children = ph.children.map(n => n.visit(this, mapper)); 37210 return new TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan); 37211 } 37212 visitBlockPlaceholder(ph, mapper) { 37213 const startName = mapper.toPublicName(ph.startName); 37214 const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName; 37215 const children = ph.children.map(n => n.visit(this, mapper)); 37216 return new BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan); 37217 } 37218 visitPlaceholder(ph, mapper) { 37219 return new Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan); 37220 } 37221 visitIcuPlaceholder(ph, mapper) { 37222 return new IcuPlaceholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan); 37223 } 37224} 37225var FactoryTarget; 37226(function (FactoryTarget) { 37227 FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive"; 37228 FactoryTarget[FactoryTarget["Component"] = 1] = "Component"; 37229 FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable"; 37230 FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe"; 37231 FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule"; 37232})(FactoryTarget || (FactoryTarget = {})); 37233function compileClassMetadata(metadata) { 37234 // Generate an ngDevMode guarded call to setClassMetadata with the class identifier and its 37235 // metadata. 37236 const fnCall = importExpr(Identifiers.setClassMetadata).callFn([metadata.type, metadata.decorators, metadata.ctorParameters ?? literal(null), metadata.propDecorators ?? literal(null)]); 37237 const iife = arrowFn([], [devOnlyGuardedExpression(fnCall).toStmt()]); 37238 return iife.callFn([]); 37239} 37240/** 37241 * Wraps the `setClassMetadata` function with extra logic that dynamically 37242 * loads dependencies from `@defer` blocks. 37243 * 37244 * Generates a call like this: 37245 * ``` 37246 * setClassMetadataAsync(type, () => [ 37247 * import('./cmp-a').then(m => m.CmpA); 37248 * import('./cmp-b').then(m => m.CmpB); 37249 * ], (CmpA, CmpB) => { 37250 * setClassMetadata(type, decorators, ctorParameters, propParameters); 37251 * }); 37252 * ``` 37253 * 37254 * Similar to the `setClassMetadata` call, it's wrapped into the `ngDevMode` 37255 * check to tree-shake away this code in production mode. 37256 */ 37257function compileComponentClassMetadata(metadata, deferrableTypes) { 37258 if (deferrableTypes === null || deferrableTypes.size === 0) { 37259 // If there are no deferrable symbols - just generate a regular `setClassMetadata` call. 37260 return compileClassMetadata(metadata); 37261 } 37262 const dynamicImports = []; 37263 const importedSymbols = []; 37264 for (const [symbolName, { 37265 importPath, 37266 isDefaultImport 37267 }] of deferrableTypes) { 37268 // e.g. `(m) => m.CmpA` 37269 const innerFn = 37270 // Default imports are always accessed through the `default` property. 37271 arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName)); 37272 // e.g. `import('./cmp-a').then(...)` 37273 const importExpr = new DynamicImportExpr(importPath).prop('then').callFn([innerFn]); 37274 dynamicImports.push(importExpr); 37275 importedSymbols.push(new FnParam(symbolName, DYNAMIC_TYPE)); 37276 } 37277 // e.g. `() => [ ... ];` 37278 const dependencyLoadingFn = arrowFn([], literalArr(dynamicImports)); 37279 // e.g. `setClassMetadata(...)` 37280 const setClassMetadataCall = importExpr(Identifiers.setClassMetadata).callFn([metadata.type, metadata.decorators, metadata.ctorParameters ?? literal(null), metadata.propDecorators ?? literal(null)]); 37281 // e.g. `(CmpA) => setClassMetadata(...)` 37282 const setClassMetaWrapper = arrowFn(importedSymbols, [setClassMetadataCall.toStmt()]); 37283 // Final `setClassMetadataAsync()` call with all arguments 37284 const setClassMetaAsync = importExpr(Identifiers.setClassMetadataAsync).callFn([metadata.type, dependencyLoadingFn, setClassMetaWrapper]); 37285 // Generate an ngDevMode guarded call to `setClassMetadataAsync` with 37286 // the class identifier and its metadata, so that this call can be tree-shaken. 37287 const iife = arrowFn([], [devOnlyGuardedExpression(setClassMetaAsync).toStmt()]); 37288 return iife.callFn([]); 37289} 37290 37291/** 37292 * Generate an ngDevMode guarded call to setClassDebugInfo with the debug info about the class
37293 * (e.g., the file name in which the class is defined) 37294 */ 37295function compileClassDebugInfo(debugInfo) { 37296 const debugInfoObject = { 37297 className: debugInfo.className 37298 }; 37299 // Include file path and line number only if the file relative path is calculated successfully. 37300 if (debugInfo.filePath) { 37301 debugInfoObject.filePath = debugInfo.filePath; 37302 debugInfoObject.lineNumber = debugInfo.lineNumber; 37303 } 37304 // Include forbidOrphanRendering only if it's set to true (to reduce generated code) 37305 if (debugInfo.forbidOrphanRendering) { 37306 debugInfoObject.forbidOrphanRendering = literal(true); 37307 } 37308 const fnCall = importExpr(Identifiers.setClassDebugInfo).callFn([debugInfo.type, mapLiteral(debugInfoObject)]); 37309 const iife = arrowFn([], [devOnlyGuardedExpression(fnCall).toStmt()]); 37310 return iife.callFn([]); 37311} 37312 37313/** 37314 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37315 * must update this constant to prevent old partial-linkers from incorrectly processing the 37316 * declaration. 37317 * 37318 * Do not include any prerelease in these versions as they are ignored. 37319 */ 37320const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0'; 37321function compileDeclareClassMetadata(metadata) { 37322 const definitionMap = new DefinitionMap(); 37323 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5)); 37324 definitionMap.set('version', literal('17.3.9')); 37325 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37326 definitionMap.set('type', metadata.type); 37327 definitionMap.set('decorators', metadata.decorators); 37328 definitionMap.set('ctorParameters', metadata.ctorParameters); 37329 definitionMap.set('propDecorators', metadata.propDecorators); 37330 return importExpr(Identifiers.declareClassMetadata).callFn([definitionMap.toLiteralMap()]); 37331} 37332 37333/** 37334 * Creates an array literal expression from the given array, mapping all values to an expression 37335 * using the provided mapping function. If the array is empty or null, then null is returned. 37336 * 37337 * @param values The array to transfer into literal array expression. 37338 * @param mapper The logic to use for creating an expression for the array's values. 37339 * @returns An array literal expression representing `values`, or null if `values` is empty or 37340 * is itself null. 37341 */ 37342function toOptionalLiteralArray(values, mapper) { 37343 if (values === null || values.length === 0) { 37344 return null; 37345 } 37346 return literalArr(values.map(value => mapper(value))); 37347} 37348/** 37349 * Creates an object literal expression from the given object, mapping all values to an expression 37350 * using the provided mapping function. If the object has no keys, then null is returned. 37351 * 37352 * @param object The object to transfer into an object literal expression. 37353 * @param mapper The logic to use for creating an expression for the object's values. 37354 * @returns An object literal expression representing `object`, or null if `object` does not have 37355 * any keys. 37356 */ 37357function toOptionalLiteralMap(object, mapper) { 37358 const entries = Object.keys(object).map(key => { 37359 const value = object[key]; 37360 return { 37361 key, 37362 value: mapper(value), 37363 quoted: true 37364 }; 37365 }); 37366 if (entries.length > 0) { 37367 return literalMap(entries); 37368 } else { 37369 return null; 37370 } 37371} 37372function compileDependencies(deps) { 37373 if (deps === 'invalid') { 37374 // The `deps` can be set to the string "invalid" by the `unwrapConstructorDependencies()` 37375 // function, which tries to convert `ConstructorDeps` into `R3DependencyMetadata[]`. 37376 return literal('invalid'); 37377 } else if (deps === null) { 37378 return literal(null); 37379 } else { 37380 return literalArr(deps.map(compileDependency)); 37381 } 37382} 37383function compileDependency(dep) { 37384 const depMeta = new DefinitionMap(); 37385 depMeta.set('token', dep.token); 37386 if (dep.attributeNameType !== null) { 37387 depMeta.set('attribute', literal(true)); 37388 } 37389 if (dep.host) { 37390 depMeta.set('host', literal(true)); 37391 } 37392 if (dep.optional) { 37393 depMeta.set('optional', literal(true)); 37394 } 37395 if (dep.self) { 37396 depMeta.set('self', literal(true)); 37397 } 37398 if (dep.skipSelf) { 37399 depMeta.set('skipSelf', literal(true)); 37400 } 37401 return depMeta.toLiteralMap(); 37402} 37403 37404/** 37405 * Compile a directive declaration defined by the `R3DirectiveMetadata`. 37406 */ 37407function compileDeclareDirectiveFromMetadata(meta) { 37408 const definitionMap = createDirectiveDefinitionMap(meta); 37409 const expression = importExpr(Identifiers.declareDirective).callFn([definitionMap.toLiteralMap()]); 37410 const type = createDirectiveType(meta); 37411 return { 37412 expression, 37413 type, 37414 statements: [] 37415 }; 37416} 37417/** 37418 * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing
37419 * this logic for components, as they extend the directive metadata. 37420 */ 37421function createDirectiveDefinitionMap(meta) { 37422 const definitionMap = new DefinitionMap(); 37423 const minVersion = getMinimumVersionForPartialOutput(meta); 37424 definitionMap.set('minVersion', literal(minVersion)); 37425 definitionMap.set('version', literal('17.3.9')); 37426 // e.g. `type: MyDirective` 37427 definitionMap.set('type', meta.type.value); 37428 if (meta.isStandalone) { 37429 definitionMap.set('isStandalone', literal(meta.isStandalone)); 37430 } 37431 if (meta.isSignal) { 37432 definitionMap.set('isSignal', literal(meta.isSignal)); 37433 } 37434 // e.g. `selector: 'some-dir'` 37435 if (meta.selector !== null) { 37436 definitionMap.set('selector', literal(meta.selector)); 37437 } 37438 definitionMap.set('inputs', needsNewInputPartialOutput(meta) ? createInputsPartialMetadata(meta.inputs) : legacyInputsPartialMetadata(meta.inputs)); 37439 definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs)); 37440 definitionMap.set('host', compileHostMetadata(meta.host)); 37441 definitionMap.set('providers', meta.providers); 37442 if (meta.queries.length > 0) { 37443 definitionMap.set('queries', literalArr(meta.queries.map(compileQuery))); 37444 } 37445 if (meta.viewQueries.length > 0) { 37446 definitionMap.set('viewQueries', literalArr(meta.viewQueries.map(compileQuery))); 37447 } 37448 if (meta.exportAs !== null) { 37449 definitionMap.set('exportAs', asLiteral(meta.exportAs)); 37450 } 37451 if (meta.usesInheritance) { 37452 definitionMap.set('usesInheritance', literal(true)); 37453 } 37454 if (meta.lifecycle.usesOnChanges) { 37455 definitionMap.set('usesOnChanges', literal(true)); 37456 } 37457 if (meta.hostDirectives?.length) { 37458 definitionMap.set('hostDirectives', createHostDirectives(meta.hostDirectives)); 37459 } 37460 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37461 return definitionMap; 37462} 37463/** 37464 * Determines the minimum linker version for the partial output 37465 * generated for this directive. 37466 * 37467 * Every time we make a breaking change to the declaration interface or partial-linker 37468 * behavior, we must update the minimum versions to prevent old partial-linkers from 37469 * incorrectly processing the declaration. 37470 * 37471 * NOTE: Do not include any prerelease in these versions as they are ignored. 37472 */ 37473function getMinimumVersionForPartialOutput(meta) { 37474 // We are starting with the oldest minimum version that can work for common 37475 // directive partial compilation output. As we discover usages of new features 37476 // that require a newer partial output emit, we bump the `minVersion`. Our goal 37477 // is to keep libraries as much compatible with older linker versions as possible. 37478 let minVersion = '14.0.0'; 37479 // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in 37480 // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced 37481 // in 16.1 is actually used. 37482 const hasDecoratorTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null); 37483 if (hasDecoratorTransformFunctions) { 37484 minVersion = '16.1.0'; 37485 } 37486 // If there are input flags and we need the new emit, use the actual minimum version, 37487 // where this was introduced. i.e. in 17.1.0 37488 // TODO(legacy-partial-output-inputs): Remove in v18. 37489 if (needsNewInputPartialOutput(meta)) { 37490 minVersion = '17.1.0'; 37491 } 37492 // If there are signal-based queries, partial output generates an extra field 37493 // that should be parsed by linkers. Ensure a proper minimum linker version. 37494 if (meta.queries.some(q => q.isSignal) || meta.viewQueries.some(q => q.isSignal)) { 37495 minVersion = '17.2.0'; 37496 } 37497 return minVersion; 37498} 37499/** 37500 * Gets whether the given directive needs the new input partial output structure 37501 * that can hold additional metadata like `isRequired`, `isSignal` etc. 37502 */ 37503function needsNewInputPartialOutput(meta) { 37504 return Object.values(meta.inputs).some(input => input.isSignal); 37505} 37506/** 37507 * Compiles the metadata of a single query into its partial declaration form as declared 37508 * by `R3DeclareQueryMetadata`. 37509 */ 37510function compileQuery(query) { 37511 const meta = new DefinitionMap(); 37512 meta.set('propertyName', literal(query.propertyName)); 37513 if (query.first) { 37514 meta.set('first', literal(true)); 37515 } 37516 meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : convertFromMaybeForwardRefExpression(query.predicate)); 37517 if (!query.emitDistinctChangesOnly) { 37518 // `emitDistinctChangesOnly` is special because we expect it to be `true`. 37519 // Therefore we explicitly emit the field, and explicitly place it only when it's `false`. 37520 meta.set('emitDistinctChangesOnly', literal(false)); 37521 } else { 37522 // The linker will assume that an absent `emitDistinctChangesOnly` flag is by default `true`. 37523 } 37524 if (query.descendants) { 37525 meta.set('descendants', literal(true)); 37526 } 37527 meta.set('read', query.read); 37528 if (query.static) { 37529 meta.set('static', literal(true)); 37530 } 37531 if (query.isSignal) { 37532 meta.set('isSignal', literal(true)); 37533 } 37534 return meta.toLiteralMap(); 37535} 37536/** 37537 * Compiles the host metadata into its partial declaration form as declared 37538 * in `R3DeclareDirectiveMetadata['host']` 37539 */ 37540function compileHostMetadata(meta) { 37541 const hostMetadata = new DefinitionMap(); 37542 hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression)); 37543 hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, literal)); 37544 hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, literal)); 37545 if (meta.specialAttributes.styleAttr) { 37546 hostMetadata.set('styleAttribute', literal(meta.specialAttributes.styleAttr)); 37547 } 37548 if (meta.specialAttributes.classAttr) { 37549 hostMetadata.set('classAttribute', literal(meta.specialAttributes.classAttr)); 37550 } 37551 if (hostMetadata.values.length > 0) { 37552 return hostMetadata.toLiteralMap(); 37553 } else { 37554 return null; 37555 } 37556} 37557function createHostDirectives(hostDirectives) { 37558 const expressions = hostDirectives.map(current => {
37559 const keys = [{ 37560 key: 'directive', 37561 value: current.isForwardReference ? generateForwardRef(current.directive.type) : current.directive.type, 37562 quoted: false 37563 }]; 37564 const inputsLiteral = current.inputs ? createHostDirectivesMappingArray(current.inputs) : null; 37565 const outputsLiteral = current.outputs ? createHostDirectivesMappingArray(current.outputs) : null; 37566 if (inputsLiteral) { 37567 keys.push({ 37568 key: 'inputs', 37569 value: inputsLiteral, 37570 quoted: false 37571 }); 37572 } 37573 if (outputsLiteral) { 37574 keys.push({ 37575 key: 'outputs', 37576 value: outputsLiteral, 37577 quoted: false 37578 }); 37579 } 37580 return literalMap(keys); 37581 }); 37582 // If there's a forward reference, we generate a `function() { return [{directive: HostDir}] }`, 37583 // otherwise we can save some bytes by using a plain array, e.g. `[{directive: HostDir}]`. 37584 return literalArr(expressions); 37585} 37586/** 37587 * Generates partial output metadata for inputs of a directive. 37588 * 37589 * The generated structure is expected to match `R3DeclareDirectiveFacade['inputs']`. 37590 */ 37591function createInputsPartialMetadata(inputs) { 37592 const keys = Object.getOwnPropertyNames(inputs); 37593 if (keys.length === 0) { 37594 return null; 37595 } 37596 return literalMap(keys.map(declaredName => { 37597 const value = inputs[declaredName]; 37598 return { 37599 key: declaredName, 37600 // put quotes around keys that contain potentially unsafe characters 37601 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName), 37602 value: literalMap([{ 37603 key: 'classPropertyName', 37604 quoted: false, 37605 value: asLiteral(value.classPropertyName) 37606 }, { 37607 key: 'publicName', 37608 quoted: false, 37609 value: asLiteral(value.bindingPropertyName) 37610 }, { 37611 key: 'isSignal', 37612 quoted: false, 37613 value: asLiteral(value.isSignal) 37614 }, { 37615 key: 'isRequired', 37616 quoted: false, 37617 value: asLiteral(value.required) 37618 }, { 37619 key: 'transformFunction', 37620 quoted: false, 37621 value: value.transformFunction ?? NULL_EXPR 37622 }]) 37623 }; 37624 })); 37625} 37626/** 37627 * Pre v18 legacy partial output for inputs. 37628 * 37629 * Previously, inputs did not capture metadata like `isSignal` in the partial compilation output. 37630 * To enable capturing such metadata, we restructured how input metadata is communicated in the 37631 * partial output. This would make libraries incompatible with older Angular FW versions where the 37632 * linker would not know how to handle this new "format". For this reason, if we know this metadata 37633 * does not need to be captured- we fall back to the old format. This is what this function 37634 * generates. 37635 * 37636 * See: 37637 * https://github.com/angular/angular/blob/d4b423690210872b5c32a322a6090beda30b05a3/packages/core/src/compiler/compiler_facade_interface.ts#L197-L199 37638 */ 37639function legacyInputsPartialMetadata(inputs) { 37640 // TODO(legacy-partial-output-inputs): Remove function in v18. 37641 const keys = Object.getOwnPropertyNames(inputs); 37642 if (keys.length === 0) { 37643 return null; 37644 } 37645 return literalMap(keys.map(declaredName => { 37646 const value = inputs[declaredName]; 37647 const publicName = value.bindingPropertyName; 37648 const differentDeclaringName = publicName !== declaredName; 37649 let result; 37650 if (differentDeclaringName || value.transformFunction !== null) { 37651 const values = [asLiteral(publicName), asLiteral(declaredName)]; 37652 if (value.transformFunction !== null) { 37653 values.push(value.transformFunction); 37654 } 37655 result = literalArr(values); 37656 } else { 37657 result = asLiteral(publicName); 37658 } 37659 return { 37660 key: declaredName, 37661 // put quotes around keys that contain potentially unsafe characters 37662 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName), 37663 value: result 37664 }; 37665 })); 37666} 37667 37668/** 37669 * Compile a component declaration defined by the `R3ComponentMetadata`. 37670 */ 37671function compileDeclareComponentFromMetadata(meta, template, additionalTemplateInfo) { 37672 const definitionMap = createComponentDefinitionMap(meta, template, additionalTemplateInfo); 37673 const expression = importExpr(Identifiers.declareComponent).callFn([definitionMap.toLiteralMap()]); 37674 const type = createComponentType(meta); 37675 return { 37676 expression, 37677 type, 37678 statements: [] 37679 }; 37680} 37681/** 37682 * Gathers the declaration fields for a component into a `DefinitionMap`. 37683 */ 37684function createComponentDefinitionMap(meta, template, templateInfo) { 37685 const definitionMap = createDirectiveDefinitionMap(meta); 37686 const blockVisitor = new BlockPresenceVisitor(); 37687 visitAll$1(blockVisitor, template.nodes); 37688 definitionMap.set('template', getTemplateExpression(template, templateInfo)); 37689 if (templateInfo.isInline) { 37690 definitionMap.set('isInline', literal(true)); 37691 } 37692 // Set the minVersion to 17.0.0 if the component is using at least one block in its template. 37693 // We don't do this for templates without blocks, in order to preserve backwards compatibility. 37694 if (blockVisitor.hasBlocks) { 37695 definitionMap.set('minVersion', literal('17.0.0')); 37696 } 37697 definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal)); 37698 definitionMap.set('dependencies', compileUsedDependenciesMetadata(meta)); 37699 definitionMap.set('viewProviders', meta.viewProviders); 37700 definitionMap.set('animations', meta.animations); 37701 if (meta.changeDetection !== null) { 37702 if (typeof meta.changeDetection === 'object') { 37703 throw new Error('Impossible state! Change detection flag is not resolved!'); 37704 } 37705 definitionMap.set('changeDetection', importExpr(Identifiers.ChangeDetectionStrategy).prop(ChangeDetectionStrategy[meta.changeDetection])); 37706 } 37707 if (meta.encapsulation !== ViewEncapsulation.Emulated) { 37708 definitionMap.set('encapsulation', importExpr(Identifiers.ViewEncapsulation).prop(ViewEncapsulation[meta.encapsulation])); 37709 } 37710 if (meta.interpolation !== DEFAULT_INTERPOLATION_CONFIG) { 37711 definitionMap.set('interpolation', literalArr([literal(meta.interpolation.start), literal(meta.interpolation.end)])); 37712 }
37713 if (template.preserveWhitespaces === true) { 37714 definitionMap.set('preserveWhitespaces', literal(true)); 37715 } 37716 return definitionMap; 37717} 37718function getTemplateExpression(template, templateInfo) { 37719 // If the template has been defined using a direct literal, we use that expression directly 37720 // without any modifications. This is ensures proper source mapping from the partially 37721 // compiled code to the source file declaring the template. Note that this does not capture 37722 // template literals referenced indirectly through an identifier. 37723 if (templateInfo.inlineTemplateLiteralExpression !== null) { 37724 return templateInfo.inlineTemplateLiteralExpression; 37725 } 37726 // If the template is defined inline but not through a literal, the template has been resolved 37727 // through static interpretation. We create a literal but cannot provide any source span. Note 37728 // that we cannot use the expression defining the template because the linker expects the template 37729 // to be defined as a literal in the declaration. 37730 if (templateInfo.isInline) { 37731 return literal(templateInfo.content, null, null); 37732 } 37733 // The template is external so we must synthesize an expression node with 37734 // the appropriate source-span. 37735 const contents = templateInfo.content; 37736 const file = new ParseSourceFile(contents, templateInfo.sourceUrl); 37737 const start = new ParseLocation(file, 0, 0, 0); 37738 const end = computeEndLocation(file, contents); 37739 const span = new ParseSourceSpan(start, end); 37740 return literal(contents, null, span); 37741} 37742function computeEndLocation(file, contents) { 37743 const length = contents.length; 37744 let lineStart = 0; 37745 let lastLineStart = 0; 37746 let line = 0; 37747 do { 37748 lineStart = contents.indexOf('\n', lastLineStart); 37749 if (lineStart !== -1) { 37750 lastLineStart = lineStart + 1; 37751 line++; 37752 } 37753 } while (lineStart !== -1); 37754 return new ParseLocation(file, length, line, length - lastLineStart); 37755} 37756function compileUsedDependenciesMetadata(meta) { 37757 const wrapType = meta.declarationListEmitMode !== 0 /* DeclarationListEmitMode.Direct */ ? generateForwardRef : expr => expr; 37758 if (meta.declarationListEmitMode === 3 /* DeclarationListEmitMode.RuntimeResolved */) { 37759 throw new Error(`Unsupported emit mode`); 37760 } 37761 return toOptionalLiteralArray(meta.declarations, decl => { 37762 switch (decl.kind) { 37763 case R3TemplateDependencyKind.Directive: 37764 const dirMeta = new DefinitionMap(); 37765 dirMeta.set('kind', literal(decl.isComponent ? 'component' : 'directive')); 37766 dirMeta.set('type', wrapType(decl.type)); 37767 dirMeta.set('selector', literal(decl.selector)); 37768 dirMeta.set('inputs', toOptionalLiteralArray(decl.inputs, literal)); 37769 dirMeta.set('outputs', toOptionalLiteralArray(decl.outputs, literal)); 37770 dirMeta.set('exportAs', toOptionalLiteralArray(decl.exportAs, literal)); 37771 return dirMeta.toLiteralMap(); 37772 case R3TemplateDependencyKind.Pipe: 37773 const pipeMeta = new DefinitionMap(); 37774 pipeMeta.set('kind', literal('pipe')); 37775 pipeMeta.set('type', wrapType(decl.type)); 37776 pipeMeta.set('name', literal(decl.name)); 37777 return pipeMeta.toLiteralMap(); 37778 case R3TemplateDependencyKind.NgModule: 37779 const ngModuleMeta = new DefinitionMap(); 37780 ngModuleMeta.set('kind', literal('ngmodule')); 37781 ngModuleMeta.set('type', wrapType(decl.type)); 37782 return ngModuleMeta.toLiteralMap(); 37783 } 37784 }); 37785} 37786class BlockPresenceVisitor extends RecursiveVisitor$1 { 37787 constructor() { 37788 super(...arguments); 37789 this.hasBlocks = false; 37790 } 37791 visitDeferredBlock() { 37792 this.hasBlocks = true; 37793 } 37794 visitDeferredBlockPlaceholder() { 37795 this.hasBlocks = true; 37796 } 37797 visitDeferredBlockLoading() { 37798 this.hasBlocks = true; 37799 } 37800 visitDeferredBlockError() { 37801 this.hasBlocks = true; 37802 } 37803 visitIfBlock() { 37804 this.hasBlocks = true; 37805 } 37806 visitIfBlockBranch() { 37807 this.hasBlocks = true; 37808 } 37809 visitForLoopBlock() { 37810 this.hasBlocks = true; 37811 } 37812 visitForLoopBlockEmpty() { 37813 this.hasBlocks = true; 37814 } 37815 visitSwitchBlock() { 37816 this.hasBlocks = true; 37817 } 37818 visitSwitchBlockCase() { 37819 this.hasBlocks = true; 37820 } 37821} 37822 37823/** 37824 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37825 * must update this constant to prevent old partial-linkers from incorrectly processing the 37826 * declaration. 37827 * 37828 * Do not include any prerelease in these versions as they are ignored. 37829 */ 37830const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0'; 37831function compileDeclareFactoryFunction(meta) { 37832 const definitionMap = new DefinitionMap(); 37833 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4)); 37834 definitionMap.set('version', literal('17.3.9')); 37835 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37836 definitionMap.set('type', meta.type.value); 37837 definitionMap.set('deps', compileDependencies(meta.deps)); 37838 definitionMap.set('target', importExpr(Identifiers.FactoryTarget).prop(FactoryTarget$1[meta.target])); 37839 return { 37840 expression: importExpr(Identifiers.declareFactory).callFn([definitionMap.toLiteralMap()]), 37841 statements: [], 37842 type: createFactoryType(meta) 37843 }; 37844} 37845 37846/** 37847 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37848 * must update this constant to prevent old partial-linkers from incorrectly processing the 37849 * declaration. 37850 * 37851 * Do not include any prerelease in these versions as they are ignored. 37852 */ 37853const MINIMUM_PARTIAL_LINKER_VERSION$3 = '12.0.0'; 37854/** 37855 * Compile a Injectable declaration defined by the `R3InjectableMetadata`. 37856 */ 37857function compileDeclareInjectableFromMetadata(meta) { 37858 const definitionMap = createInjectableDefinitionMap(meta); 37859 const expression = importExpr(Identifiers.declareInjectable).callFn([definitionMap.toLiteralMap()]); 37860 const type = createInjectableType(meta); 37861 return { 37862 expression, 37863 type, 37864 statements: [] 37865 }; 37866} 37867/** 37868 * Gathers the declaration fields for a Injectable into a `DefinitionMap`. 37869 */ 37870function createInjectableDefinitionMap(meta) { 37871 const definitionMap = new DefinitionMap(); 37872 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3)); 37873 definitionMap.set('version', literal('17.3.9')); 37874 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37875 definitionMap.set('type', meta.type.value); 37876 // Only generate providedIn property if it has a non-null value 37877 if (meta.providedIn !== undefined) { 37878 const providedIn = convertFromMaybeForwardRefExpression(meta.providedIn); 37879 if (providedIn.value !== null) { 37880 definitionMap.set('providedIn', providedIn); 37881 } 37882 } 37883 if (meta.useClass !== undefined) { 37884 definitionMap.set('useClass', convertFromMaybeForwardRefExpression(meta.useClass)); 37885 } 37886 if (meta.useExisting !== undefined) { 37887 definitionMap.set('useExisting', convertFromMaybeForwardRefExpression(meta.useExisting)); 37888 } 37889 if (meta.useValue !== undefined) { 37890 definitionMap.set('useValue', convertFromMaybeForwardRefExpression(meta.useValue)); 37891 } 37892 // Factories do not contain `ForwardRef`s since any types are already wrapped in a function call 37893 // so the types will not be eagerly evaluated. Therefore we do not need to process this expression 37894 // with `convertFromProviderExpression()`. 37895 if (meta.useFactory !== undefined) { 37896 definitionMap.set('useFactory', meta.useFactory); 37897 } 37898 if (meta.deps !== undefined) { 37899 definitionMap.set('deps', literalArr(meta.deps.map(compileDependency))); 37900 } 37901 return definitionMap; 37902} 37903 37904/** 37905 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37906 * must update this constant to prevent old partial-linkers from incorrectly processing the 37907 * declaration. 37908 * 37909 * Do not include any prerelease in these versions as they are ignored. 37910 */ 37911const MINIMUM_PARTIAL_LINKER_VERSION$2 = '12.0.0'; 37912function compileDeclareInjectorFromMetadata(meta) { 37913 const definitionMap = createInjectorDefinitionMap(meta); 37914 const expression = importExpr(Identifiers.declareInjector).callFn([definitionMap.toLiteralMap()]); 37915 const type = createInjectorType(meta); 37916 return { 37917 expression, 37918 type, 37919 statements: [] 37920 }; 37921} 37922/** 37923 * Gathers the declaration fields for an Injector into a `DefinitionMap`. 37924 */ 37925function createInjectorDefinitionMap(meta) { 37926 const definitionMap = new DefinitionMap(); 37927 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2)); 37928 definitionMap.set('version', literal('17.3.9')); 37929 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37930 definitionMap.set('type', meta.type.value); 37931 definitionMap.set('providers', meta.providers); 37932 if (meta.imports.length > 0) { 37933 definitionMap.set('imports', literalArr(meta.imports)); 37934 } 37935 return definitionMap; 37936} 37937 37938/** 37939 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37940 * must update this constant to prevent old partial-linkers from incorrectly processing the 37941 * declaration. 37942 * 37943 * Do not include any prerelease in these versions as they are ignored. 37944 */ 37945const MINIMUM_PARTIAL_LINKER_VERSION$1 = '14.0.0'; 37946function compileDeclareNgModuleFromMetadata(meta) { 37947 const definitionMap = createNgModuleDefinitionMap(meta); 37948 const expression = importExpr(Identifiers.declareNgModule).callFn([definitionMap.toLiteralMap()]); 37949 const type = createNgModuleType(meta); 37950 return { 37951 expression, 37952 type, 37953 statements: [] 37954 }; 37955} 37956/** 37957 * Gathers the declaration fields for an NgModule into a `DefinitionMap`. 37958 */ 37959function createNgModuleDefinitionMap(meta) { 37960 const definitionMap = new DefinitionMap(); 37961 if (meta.kind === R3NgModuleMetadataKind.Local) { 37962 throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path'); 37963 } 37964 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1)); 37965 definitionMap.set('version', literal('17.3.9')); 37966 definitionMap.set('ngImport', importExpr(Identifiers.core)); 37967 definitionMap.set('type', meta.type.value); 37968 // We only generate the keys in the metadata if the arrays contain values. 37969 // We must wrap the arrays inside a function if any of the values are a forward reference to a 37970 // not-yet-declared class. This is to support JIT execution of the `ɵɵngDeclareNgModule(
37970)` call. 37971 // In the linker these wrappers are stripped and then reapplied for the `ɵɵdefineNgModule()` call. 37972 if (meta.bootstrap.length > 0) { 37973 definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls)); 37974 } 37975 if (meta.declarations.length > 0) { 37976 definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls)); 37977 } 37978 if (meta.imports.length > 0) { 37979 definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls)); 37980 } 37981 if (meta.exports.length > 0) { 37982 definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls)); 37983 } 37984 if (meta.schemas !== null && meta.schemas.length > 0) { 37985 definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value))); 37986 } 37987 if (meta.id !== null) { 37988 definitionMap.set('id', meta.id); 37989 } 37990 return definitionMap; 37991} 37992 37993/** 37994 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we 37995 * must update this constant to prevent old partial-linkers from incorrectly processing the 37996 * declaration. 37997 * 37998 * Do not include any prerelease in these versions as they are ignored. 37999 */ 38000const MINIMUM_PARTIAL_LINKER_VERSION = '14.0.0'; 38001/** 38002 * Compile a Pipe declaration defined by the `R3PipeMetadata`. 38003 */ 38004function compileDeclarePipeFromMetadata(meta) { 38005 const definitionMap = createPipeDefinitionMap(meta); 38006 const expression = importExpr(Identifiers.declarePipe).callFn([definitionMap.toLiteralMap()]); 38007 const type = createPipeType(meta); 38008 return { 38009 expression, 38010 type, 38011 statements: [] 38012 }; 38013} 38014/** 38015 * Gathers the declaration fields for a Pipe into a `DefinitionMap`. 38016 */ 38017function createPipeDefinitionMap(meta) { 38018 const definitionMap = new DefinitionMap(); 38019 definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION)); 38020 definitionMap.set('version', literal('17.3.9')); 38021 definitionMap.set('ngImport', importExpr(Identifiers.core)); 38022 // e.g. `type: MyPipe` 38023 definitionMap.set('type', meta.type.value); 38024 if (meta.isStandalone) { 38025 definitionMap.set('isStandalone', literal(meta.isStandalone)); 38026 } 38027 // e.g. `name: "myPipe"` 38028 definitionMap.set('name', literal(meta.pipeName)); 38029 if (meta.pure === false) { 38030 // e.g. `pure: false` 38031 definitionMap.set('pure', literal(meta.pure)); 38032 } 38033 return definitionMap; 38034} 38035 38036////////////////////////////////////// 38037// This file only reexports content of the `src` folder. Keep it that way. 38038// This function call has a global side effects and publishes the compiler into global namespace for 38039// the late binding of the Compiler to the @angular/core for jit compilation. 38040publishFacade(_global); 38041 38042/** 38043 * @module 38044 * @description 38045 * Entry point for all public APIs of this package. 38046 */ 38047// This file only reexports content of the `src` folder. Keep it that way. 38048 38049// This file is not used to build this module. It is only used during editing 38050 38051// This file is not used to build this module. It is only used during editing 38052 38053 38054 38055/***/ }), 38056 38057/***/ 1643: 38058/*!**********************************************************!*\ 38059 !*** ./node_modules/@angular/localize/fesm2022/init.mjs ***! 38060 \**********************************************************/ 38061/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 38062 38063__webpack_require__.r(__webpack_exports__); 38064/* harmony export */ __webpack_require__.d(__webpack_exports__, { 38065/* harmony export */ $localize: () => (/* reexport safe */ _angular_localize__WEBPACK_IMPORTED_MODULE_0__["ɵ$localize"]) 38066/* harmony export */ }); 38067/* harmony import */ var _angular_localize__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/localize */ 95116); 38068/** 38069 * @license Angular v17.3.9 38070 * (c) 2010-2024 Google LLC. https://angular.io/ 38071 * License: MIT 38072 */ 38073 38074 38075 38076 38077// Attach $localize to the global context, as a side-effect of this module. 38078globalThis.$localize = _angular_localize__WEBPACK_IMPORTED_MODULE_0__["ɵ$localize"]; 38079 38080/***/ }), 38081 38082/***/ 95116: 38083/*!**************************************************************!*\ 38084 !*** ./node_modules/@angular/localize/fesm2022/localize.mjs ***! 38085 \**************************************************************/ 38086/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 38087 38088__webpack_require__.r(__webpack_exports__); 38089/* harmony export */ __webpack_require__.d(__webpack_exports__, { 38090/* harmony export */ clearTranslations: () => (/* binding */ clearTranslations), 38091/* harmony export */ loadTranslations: () => (/* binding */ loadTranslations), 38092/* harmony export */ "ɵ$localize": () => (/* binding */ $localize$1), 38093/* harmony export */ "ɵMissingTranslationError": () => (/* binding */ MissingTranslationError), 38094/* harmony export */ "ɵcomputeMsgId": () => (/* binding */ computeMsgId), 38095/* harmony export */ "ɵfindEndOfBlock": () =>
38095 (/* binding */ findEndOfBlock), 38096/* harmony export */ "ɵisMissingTranslationError": () => (/* binding */ isMissingTranslationError), 38097/* harmony export */ "ɵmakeParsedTranslation": () => (/* binding */ makeParsedTranslation), 38098/* harmony export */ "ɵmakeTemplateObject": () => (/* binding */ makeTemplateObject), 38099/* harmony export */ "ɵparseMessage": () => (/* binding */ parseMessage), 38100/* harmony export */ "ɵparseMetadata": () => (/* binding */ parseMetadata), 38101/* harmony export */ "ɵparseTranslation": () => (/* binding */ parseTranslation), 38102/* harmony export */ "ɵsplitBlock": () => (/* binding */ splitBlock), 38103/* harmony export */ "ɵtranslate": () => (/* binding */ translate$1) 38104/* harmony export */ }); 38105/** 38106 * @license Angular v17.3.9 38107 * (c) 2010-2024 Google LLC. https://angular.io/ 38108 * License: MIT 38109 */ 38110 38111/** 38112 * The character used to mark the start and end of a "block" in a `$localize` tagged string. 38113 * A block can indicate metadata about the message or specify a name of a placeholder for a 38114 * substitution expressions. 38115 * 38116 * For example: 38117 * 38118 * ```ts 38119 * $localize`Hello, ${title}:title:!`; 38120 * $localize`:meaning|description@@id:source message text`; 38121 * ``` 38122 */ 38123const BLOCK_MARKER$1 = ':'; 38124/** 38125 * The marker used to separate a message's "meaning" from its "description" in a metadata block. 38126 * 38127 * For example: 38128 * 38129 * ```ts 38130 * $localize `:correct|Indicates that the user got the answer correct: Right!`; 38131 * $localize `:movement|Button label for moving to the right: Right!`; 38132 * ``` 38133 */ 38134const MEANING_SEPARATOR = '|'; 38135/** 38136 * The marker used to separate a message's custom "id" from its "description" in a metadata block. 38137 * 38138 * For example: 38139 * 38140 * ```ts 38141 * $localize `:A welcome message on the home page@@myApp-homepage-welcome: Welcome!`; 38142 * ``` 38143 */ 38144const ID_SEPARATOR = '@@'; 38145/** 38146 * The marker used to separate legacy message ids from the rest of a metadata block. 38147 * 38148 * For example: 38149 * 38150 * ```ts 38151 * $localize `:@@custom-idâ2df64767cd895a8fabe3e18b94b5b6b6f9e2e3f0: Welcome!`; 38152 * ``` 38153 * 38154 * Note that this character is the "symbol for the unit separator" (â) not the "unit separator 38155 * character" itself, since that has no visual representation. See https://graphemica.com/%E2%90%9F. 38156 * 38157 * Here is some background for the original "unit separator character": 38158 * https://stackoverflow.com/questions/8695118/whats-the-file-group-record-unit-separator-control-characters-and-its-usage 38159 */ 38160const LEGACY_ID_INDICATOR = '\u241F'; 38161 38162/** 38163 * A lazily created TextEncoder instance for converting strings into UTF-8 bytes 38164 */ 38165let textEncoder; 38166/** 38167 * Return the message id or compute it using the XLIFF1 digest. 38168 */ 38169function digest(message) { 38170 return message.id || computeDigest(message); 38171} 38172/** 38173 * Compute the message id using the XLIFF1 digest. 38174 */ 38175function computeDigest(message) { 38176 return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`); 38177} 38178/** 38179 * Return the message id or compute it using the XLIFF2/XMB/$localize digest. 38180 */ 38181function decimalDigest(message) { 38182 return message.id || computeDecimalDigest(message); 38183} 38184/** 38185 * Compute the message id using the XLIFF2/XMB/$localize digest. 38186 */ 38187function computeDecimalDigest(message) { 38188 const visitor = new _SerializerIgnoreIcuExpVisitor(); 38189 const parts = message.nodes.map(a => a.visit(visitor, null)); 38190 return computeMsgId(parts.join(''), message.meaning); 38191} 38192/** 38193 * Serialize the i18n ast to something xml-like in order to generate an UID. 38194 * 38195 * The visitor is also used in the i18n parser tests 38196 * 38197 * @internal 38198 */ 38199class _SerializerVisitor { 38200 visitText(text, context) { 38201 return text.value; 38202 } 38203 visitContainer(container, context) { 38204 return `[${container.children.map(child => child.visit(this)).join(', ')}]`; 38205 } 38206 visitIcu(icu, context) { 38207 const strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 38208 return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`; 38209 } 38210 visitTagPlaceholder(ph, context) { 38211 return ph.isVoid ? `<ph tag name="${ph.startName}"/>` : `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; 38212 } 38213 visitPlaceholder(ph, context) { 38214 return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`; 38215 } 38216 visitIcuPlaceholder(ph, context) { 38217 return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`; 38218 } 38219 visitBlockPlaceholder(ph, context) { 38220 return `<ph block name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; 38221 } 38222} 38223const serializerVisitor = new _SerializerVisitor();
38224function serializeNodes(nodes) { 38225 return nodes.map(a => a.visit(serializerVisitor, null)); 38226} 38227/** 38228 * Serialize the i18n ast to something xml-like in order to generate an UID. 38229 * 38230 * Ignore the ICU expressions so that message IDs stays identical if only the expression changes. 38231 * 38232 * @internal 38233 */ 38234class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor { 38235 visitIcu(icu, context) { 38236 let strCases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`); 38237 // Do not take the expression into account 38238 return `{${icu.type}, ${strCases.join(', ')}}`; 38239 } 38240} 38241/** 38242 * Compute the SHA1 of the given string 38243 * 38244 * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf 38245 * 38246 * WARNING: this function has not been designed not tested with security in mind. 38247 * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT. 38248 */ 38249function sha1(str) { 38250 textEncoder ??= new TextEncoder(); 38251 const utf8 = [...textEncoder.encode(str)]; 38252 const words32 = bytesToWords32(utf8, Endian.Big); 38253 const len = utf8.length * 8; 38254 const w = new Uint32Array(80); 38255 let a = 0x67452301, 38256 b = 0xefcdab89, 38257 c = 0x98badcfe, 38258 d = 0x10325476, 38259 e = 0xc3d2e1f0; 38260 words32[len >> 5] |= 0x80 << 24 - len % 32; 38261 words32[(len + 64 >> 9 << 4) + 15] = len; 38262 for (let i = 0; i < words32.length; i += 16) { 38263 const h0 = a, 38264 h1 = b, 38265 h2 = c, 38266 h3 = d, 38267 h4 = e; 38268 for (let j = 0; j < 80; j++) { 38269 if (j < 16) { 38270 w[j] = words32[i + j]; 38271 } else { 38272 w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); 38273 } 38274 const fkVal = fk(j, b, c, d); 38275 const f = fkVal[0]; 38276 const k = fkVal[1]; 38277 const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32); 38278 e = d; 38279 d = c; 38280 c = rol32(b, 30); 38281 b = a; 38282 a = temp; 38283 } 38284 a = add32(a, h0); 38285 b = add32(b, h1); 38286 c = add32(c, h2); 38287 d = add32(d, h3); 38288 e = add32(e, h4); 38289 } 38290 // Convert the output parts to a 160-bit hexadecimal string 38291 return toHexU32(a) + toHexU32(b) + toHexU32(c) + toHexU32(d) + toHexU32(e); 38292} 38293/** 38294 * Convert and format a number as a string representing a 32-bit unsigned hexadecimal number. 38295 * @param value The value to format as a string. 38296 * @returns A hexadecimal string representing the value. 38297 */ 38298function toHexU32(value) { 38299 // unsigned right shift of zero ensures an unsigned 32-bit number 38300 return (value >>> 0).toString(16).padStart(8, '0'); 38301} 38302function fk(index, b, c, d) { 38303 if (index < 20) { 38304 return [b & c | ~b & d, 0x5a827999]; 38305 } 38306 if (index < 40) { 38307 return [b ^ c ^ d, 0x6ed9eba1]; 38308 } 38309 if (index < 60) { 38310 return [b & c | b & d | c & d, 0x8f1bbcdc]; 38311 } 38312 return [b ^ c ^ d, 0xca62c1d6]; 38313} 38314/** 38315 * Compute the fingerprint of the given string 38316 * 38317 * The output is 64 bit number encoded as a decimal string 38318 * 38319 * based on: 38320 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java 38321 */ 38322function fingerprint(str) { 38323 textEncoder ??= new TextEncoder(); 38324 const utf8 = textEncoder.encode(str); 38325 const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength); 38326 let hi = hash32(view, utf8.length, 0); 38327 let lo = hash32(view, utf8.length, 102072); 38328 if (hi == 0 && (lo == 0 || lo == 1)) { 38329 hi = hi ^ 0x130f9bef; 38330 lo = lo ^ -0x6b5f56d8; 38331 } 38332 return BigInt.asUintN(32, BigInt(hi)) << BigInt(32) | BigInt.asUintN(32, BigInt(lo)); 38333} 38334function computeMsgId(msg, meaning = '') { 38335 let msgFingerprint = fingerprint(msg); 38336 if (meaning) { 38337 // Rotate the 64-bit message fingerprint one bit to the left and then add the meaning 38338 // fingerprint. 38339 msgFingerprint = BigInt.asUintN(64, msgFingerprint << BigInt(1)) | msgFingerprint >> BigInt(63) & BigInt(1); 38340 msgFingerprint += fingerprint(meaning); 38341 } 38342 return BigInt.asUintN(63, msgFingerprint).toString(); 38343} 38344function hash32(view, length, c) { 38345 let a = 0x9e3779b9, 38346 b = 0x9e3779b9; 38347 let index = 0; 38348 const end = length - 12; 38349 for (; index <= end; index += 12) { 38350 a += view.getUint32(index, true); 38351 b += view.getUint32(index + 4, true); 38352 c += view.getUint32(index + 8, true); 38353 const res = mix(a, b, c); 38354 a = res[0], b = res[1], c = res[2]; 38355 } 38356 const remainder = length - index; 38357 // the first byte of c is reserved for the length 38358 c += length; 38359 if (remainder >= 4) { 38360 a += view.getUint32(index, true); 38361 index += 4; 38362 if (remainder >= 8) { 38363 b += view.getUint32(index, true); 38364 index += 4; 38365 // Partial 32-bit word for c 38366 if (remainder >= 9) { 38367 c += view.getUint8(index++) << 8; 38368 } 38369 if (remainder >= 10) { 38370 c += view.getUint8(index++) << 16; 38371 } 38372 if (remainder === 11) { 38373 c += view.getUint8(index++) << 24; 38374 } 38375 } else { 38376 // Partial 32-bit word for b 38377 if (remainder >= 5) { 38378 b += view.getUint8(index++); 38379 } 38380 if (remainder >= 6) { 38381 b += view.getUint8(index++) << 8; 38382 } 38383 if (remainder === 7) { 38384 b += view.getUint8(index++) << 16; 38385 } 38386 } 38387 } else { 38388 // Partial 32-bit word for a 38389 if (remainder >= 1) { 38390 a += view.getUint8(index++); 38391 } 38392 if (remainder >= 2) { 38393 a += view.getUint8(index++) << 8; 38394 } 38395 if (remainder === 3) { 38396 a += view.getUint8(index++) << 16; 38397 } 38398 } 38399 return mix(a, b, c)[2]; 38400} 38401// clang-format off 38402function mix(a, b, c) { 38403 a -= b; 38404 a -= c; 38405 a ^= c >>> 13; 38406 b -= c; 38407 b -= a; 38408 b ^= a << 8; 38409 c -= a; 38410 c -= b;
38411 c ^= b >>> 13; 38412 a -= b; 38413 a -= c; 38414 a ^= c >>> 12; 38415 b -= c; 38416 b -= a; 38417 b ^= a << 16; 38418 c -= a; 38419 c -= b; 38420 c ^= b >>> 5; 38421 a -= b; 38422 a -= c; 38423 a ^= c >>> 3; 38424 b -= c; 38425 b -= a; 38426 b ^= a << 10; 38427 c -= a; 38428 c -= b; 38429 c ^= b >>> 15; 38430 return [a, b, c]; 38431} 38432// clang-format on 38433// Utils 38434var Endian; 38435(function (Endian) { 38436 Endian[Endian["Little"] = 0] = "Little"; 38437 Endian[Endian["Big"] = 1] = "Big"; 38438})(Endian || (Endian = {})); 38439function add32(a, b) { 38440 return add32to64(a, b)[1]; 38441} 38442function add32to64(a, b) { 38443 const low = (a & 0xffff) + (b & 0xffff); 38444 const high = (a >>> 16) + (b >>> 16) + (low >>> 16); 38445 return [high >>> 16, high << 16 | low & 0xffff]; 38446} 38447// Rotate a 32b number left `count` position 38448function rol32(a, count) { 38449 return a << count | a >>> 32 - count; 38450} 38451function bytesToWords32(bytes, endian) { 38452 const size = bytes.length + 3 >>> 2; 38453 const words32 = []; 38454 for (let i = 0; i < size; i++) { 38455 words32[i] = wordAt(bytes, i * 4, endian); 38456 } 38457 return words32; 38458} 38459function byteAt(bytes, index) { 38460 return index >= bytes.length ? 0 : bytes[index]; 38461} 38462function wordAt(bytes, index, endian) { 38463 let word = 0; 38464 if (endian === Endian.Big) { 38465 for (let i = 0; i < 4; i++) { 38466 word += byteAt(bytes, index + i) << 24 - 8 * i; 38467 } 38468 } else { 38469 for (let i = 0; i < 4; i++) { 38470 word += byteAt(bytes, index + i) << 8 * i; 38471 } 38472 } 38473 return word; 38474} 38475 38476// This module specifier is intentionally a relative path to allow bundling the code directly 38477/** 38478 * Parse a `$localize` tagged string into a structure that can be used for translation or 38479 * extraction. 38480 * 38481 * See `ParsedMessage` for an example. 38482 */ 38483function parseMessage(messageParts, expressions, location, messagePartLocations, expressionLocations = []) { 38484 const substitutions = {}; 38485 const substitutionLocations = {}; 38486 const associatedMessageIds = {}; 38487 const metadata = parseMetadata(messageParts[0], messageParts.raw[0]); 38488 const cleanedMessageParts = [metadata.text]; 38489 const placeholderNames = []; 38490 let messageString = metadata.text; 38491 for (let i = 1; i < messageParts.length; i++) { 38492 const { 38493 messagePart, 38494 placeholderName = computePlaceholderName(i), 38495 associatedMessageId 38496 } = parsePlaceholder(messageParts[i], messageParts.raw[i]); 38497 messageString += `{$${placeholderName}}${messagePart}`; 38498 if (expressions !== undefined) { 38499 substitutions[placeholderName] = expressions[i - 1]; 38500 substitutionLocations[placeholderName] = expressionLocations[i - 1]; 38501 } 38502 placeholderNames.push(placeholderName); 38503 if (associatedMessageId !== undefined) { 38504 associatedMessageIds[placeholderName] = associatedMessageId; 38505 } 38506 cleanedMessageParts.push(messagePart); 38507 } 38508 const messageId = metadata.customId || computeMsgId(messageString, metadata.meaning || ''); 38509 const legacyIds = metadata.legacyIds ? metadata.legacyIds.filter(id => id !== messageId) : []; 38510 return { 38511 id: messageId, 38512 legacyIds, 38513 substitutions, 38514 substitutionLocations, 38515 text: messageString, 38516 customId: metadata.customId, 38517 meaning: metadata.meaning || '', 38518 description: metadata.description || '', 38519 messageParts: cleanedMessageParts, 38520 messagePartLocations, 38521 placeholderNames, 38522 associatedMessageIds, 38523 location 38524 }; 38525} 38526/** 38527 * Parse the given message part (`cooked` + `raw`) to extract the message metadata from the text. 38528 * 38529 * If the message part has a metadata block this function will extract the `meaning`, 38530 * `description`, `customId` and `legacyId` (if provided) from the block. These metadata properties 38531 * are serialized in the string delimited by `|`, `@@` and `â` respectively. 38532 * 38533 * (Note that `â` is the `LEGACY_ID_INDICATOR` - see `constants.ts`.) 38534 * 38535 * For example: 38536 * 38537 * ```ts 38538 * `:meaning|description@@custom-id:` 38539 * `:meaning|@@custom-id:` 38540 * `:meaning|description:` 38541 * `:description@@custom-id:` 38542 * `:meaning|:` 38543 * `:description:` 38544 * `:@@custom-id:` 38545 * `:meaning|description@@custom-idâlegacy-id-1âlegacy-id-2:` 38546 * ``` 38547 * 38548 * @param cooked The cooked version of the message part to parse. 38549 * @param raw The raw version of the message part to parse.
38550 * @returns A object containing any metadata that was parsed from the message part. 38551 */ 38552function parseMetadata(cooked, raw) { 38553 const { 38554 text: messageString, 38555 block 38556 } = splitBlock(cooked, raw); 38557 if (block === undefined) { 38558 return { 38559 text: messageString 38560 }; 38561 } else { 38562 const [meaningDescAndId, ...legacyIds] = block.split(LEGACY_ID_INDICATOR); 38563 const [meaningAndDesc, customId] = meaningDescAndId.split(ID_SEPARATOR, 2); 38564 let [meaning, description] = meaningAndDesc.split(MEANING_SEPARATOR, 2); 38565 if (description === undefined) { 38566 description = meaning; 38567 meaning = undefined; 38568 } 38569 if (description === '') { 38570 description = undefined; 38571 } 38572 return { 38573 text: messageString, 38574 meaning, 38575 description, 38576 customId, 38577 legacyIds 38578 }; 38579 } 38580} 38581/** 38582 * Parse the given message part (`cooked` + `raw`) to extract any placeholder metadata from the 38583 * text. 38584 * 38585 * If the message part has a metadata block this function will extract the `placeholderName` and 38586 * `associatedMessageId` (if provided) from the block. 38587 * 38588 * These metadata properties are serialized in the string delimited by `@@`. 38589 * 38590 * For example: 38591 * 38592 * ```ts 38593 * `:placeholder-name@@associated-id:` 38594 * ``` 38595 * 38596 * @param cooked The cooked version of the message part to parse. 38597 * @param raw The raw version of the message part to parse. 38598 * @returns A object containing the metadata (`placeholderName` and `associatedMessageId`) of the 38599 * preceding placeholder, along with the static text that follows. 38600 */ 38601function parsePlaceholder(cooked, raw) { 38602 const { 38603 text: messagePart, 38604 block 38605 } = splitBlock(cooked, raw); 38606 if (block === undefined) { 38607 return { 38608 messagePart 38609 }; 38610 } else { 38611 const [placeholderName, associatedMessageId] = block.split(ID_SEPARATOR); 38612 return { 38613 messagePart, 38614 placeholderName, 38615 associatedMessageId 38616 }; 38617 } 38618} 38619/** 38620 * Split a message part (`cooked` + `raw`) into an optional delimited "block" off the front and the 38621 * rest of the text of the message part. 38622 * 38623 * Blocks appear at the start of message parts. They are delimited by a colon `:` character at the 38624 * start and end of the block. 38625 * 38626 * If the block is in the first message part then it will be metadata about the whole message: 38627 * meaning, description, id. Otherwise it will be metadata about the immediately preceding 38628 * substitution: placeholder name. 38629 * 38630 * Since blocks are optional, it is possible that the content of a message block actually starts 38631 * with a block marker. In this case the marker must be escaped `\:`. 38632 * 38633 * @param cooked The cooked version of the message part to parse. 38634 * @param raw The raw version of the message part to parse. 38635 * @returns An object containing the `text` of the message part and the text of the `block`, if it 38636 * exists. 38637 * @throws an error if the `block` is unterminated 38638 */ 38639function splitBlock(cooked, raw) { 38640 if (raw.charAt(0) !== BLOCK_MARKER$1) { 38641 return { 38642 text: cooked 38643 }; 38644 } else { 38645 const endOfBlock = findEndOfBlock(cooked, raw); 38646 return { 38647 block: cooked.substring(1, endOfBlock), 38648 text: cooked.substring(endOfBlock + 1) 38649 }; 38650 } 38651} 38652function computePlaceholderName(index) { 38653 return index === 1 ? 'PH' : `PH_${index - 1}`; 38654} 38655/** 38656 * Find the end of a "marked block" indicated by the first non-escaped colon. 38657 * 38658 * @param cooked The cooked string (where escaped chars have been processed) 38659 * @param raw The raw string (where escape sequences are still in place) 38660 * 38661 * @returns the index of the end of block marker 38662 * @throws an error if the block is unterminated 38663 */ 38664function findEndOfBlock(cooked, raw) { 38665 for (let cookedIndex = 1, rawIndex = 1; cookedIndex < cooked.length; cookedIndex++, rawIndex++) { 38666 if (raw[rawIndex] === '\\') { 38667 rawIndex++; 38668 } else if (cooked[cookedIndex] === BLOCK_MARKER$1) { 38669 return cookedIndex; 38670 } 38671 } 38672 throw new Error(`Unterminated $localize metadata block in "${raw}".`); 38673} 38674class MissingTranslationError extends Error { 38675 constructor(parsedMessage) { 38676 super(`No translation found for ${describeMessage(parsedMessage)}.`); 38677 this.parsedMessage = parsedMessage; 38678 this.type = 'MissingTranslationError'; 38679 } 38680} 38681function isMissingTranslationError(e) { 38682 return e.type === 'MissingTranslationError'; 38683} 38684/** 38685 * Translate the text of the `$localize` tagged-string (i.e. `messageParts` and 38686 * `substitutions`) using the given `translations`. 38687 * 38688 * The tagged-string is parsed to extract its `messageId` which is used to find an appropriate 38689 * `ParsedTranslation`. If this doesn't match and there are legacy ids then try matching a 38690 * translation using those. 38691 * 38692 * If one is found then it is used to translate the message into a
38692new set of `messageParts` and 38693 * `substitutions`. 38694 * The translation may reorder (or remove) substitutions as appropriate. 38695 * 38696 * If there is no translation with a matching message id then an error is thrown. 38697 * If a translation contains a placeholder that is not found in the message being translated then an 38698 * error is thrown. 38699 */ 38700function translate$1(translations, messageParts, substitutions) { 38701 const message = parseMessage(messageParts, substitutions); 38702 // Look up the translation using the messageId, and then the legacyId if available. 38703 let translation = translations[message.id]; 38704 // If the messageId did not match a translation, try matching the legacy ids instead 38705 if (message.legacyIds !== undefined) { 38706 for (let i = 0; i < message.legacyIds.length && translation === undefined; i++) { 38707 translation = translations[message.legacyIds[i]]; 38708 } 38709 } 38710 if (translation === undefined) { 38711 throw new MissingTranslationError(message); 38712 } 38713 return [translation.messageParts, translation.placeholderNames.map(placeholder => { 38714 if (message.substitutions.hasOwnProperty(placeholder)) { 38715 return message.substitutions[placeholder]; 38716 } else { 38717 throw new Error(`There is a placeholder name mismatch with the translation provided for the message ${describeMessage(message)}.\n` + `The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`); 38718 } 38719 })]; 38720} 38721/** 38722 * Parse the `messageParts` and `placeholderNames` out of a target `message`. 38723 * 38724 * Used by `loadTranslations()` to convert target message strings into a structure that is more 38725 * appropriate for doing translation. 38726 * 38727 * @param message the message to be parsed. 38728 */ 38729function parseTranslation(messageString) { 38730 const parts = messageString.split(/{\$([^}]*)}/); 38731 const messageParts = [parts[0]]; 38732 const placeholderNames = []; 38733 for (let i = 1; i < parts.length - 1; i += 2) { 38734 placeholderNames.push(parts[i]); 38735 messageParts.push(`${parts[i + 1]}`); 38736 } 38737 const rawMessageParts = messageParts.map(part => part.charAt(0) === BLOCK_MARKER$1 ? '\\' + part : part); 38738 return { 38739 text: messageString,
38740 messageParts: makeTemplateObject(messageParts, rawMessageParts), 38741 placeholderNames 38742 }; 38743} 38744/** 38745 * Create a `ParsedTranslation` from a set of `messageParts` and `placeholderNames`. 38746 * 38747 * @param messageParts The message parts to appear in the ParsedTranslation. 38748 * @param placeholderNames The names of the placeholders to intersperse between the `messageParts`. 38749 */ 38750function makeParsedTranslation(messageParts, placeholderNames = []) { 38751 let messageString = messageParts[0]; 38752 for (let i = 0; i < placeholderNames.length; i++) { 38753 messageString += `{$${placeholderNames[i]}}${messageParts[i + 1]}`; 38754 } 38755 return { 38756 text: messageString, 38757 messageParts: makeTemplateObject(messageParts, messageParts), 38758 placeholderNames 38759 }; 38760} 38761/** 38762 * Create the specialized array that is passed to tagged-string tag functions. 38763 * 38764 * @param cooked The message parts with their escape codes processed. 38765 * @param raw The message parts with their escaped codes as-is. 38766 */ 38767function makeTemplateObject(cooked, raw) { 38768 Object.defineProperty(cooked, 'raw', { 38769 value: raw 38770 }); 38771 return cooked; 38772} 38773function describeMessage(message) { 38774 const meaningString = message.meaning && ` - "${message.meaning}"`; 38775 const legacy = message.legacyIds && message.legacyIds.length > 0 ? ` [${message.legacyIds.map(l => `"${l}"`).join(', ')}]` : ''; 38776 return `"${message.id}"${legacy} ("${message.text}"${meaningString})`; 38777} 38778 38779/** 38780 * Load translations for use by `$localize`, if doing runtime translation. 38781 * 38782 * If the `$localize` tagged strings are not going to be replaced at compiled time, it is possible 38783 * to load a set of translations that will be applied to the `$localize` tagged strings at runtime, 38784 * in the browser. 38785 * 38786 * Loading a new translation will overwrite a previous translation if it has the same `MessageId`. 38787 * 38788 * Note that `$localize` messages are only processed once, when the tagged string is first 38789 * encountered, and does not provide dynamic language changing without refreshing the browser. 38790 * Loading new translations later in the application life-cycle will not change the translated text 38791 * of messages that have already been translated. 38792 * 38793 * The message IDs and translations are in the same format as that rendered to "simple JSON" 38794 * translation files when extracting messages. In particular, placeholders in messages are rendered 38795 * using the `{$PLACEHOLDER_NAME}` syntax. For example the message from the following template: 38796 * 38797 * ```html 38798 * <div i18n>pre<span>inner-pre<b>bold</b>inner-post</span>post</div> 38799 * ``` 38800 * 38801 * would have the following form in the `translations` map: 38802 * 38803 * ```ts 38804 * { 38805 * "2932901491976224757": 38806 * "pre{$START_TAG_SPAN}inner-pre{$START_BOLD_TEXT}bold{$CLOSE_BOLD_TEXT}inner-post{$CLOSE_TAG_SPAN}post" 38807 * } 38808 * ``` 38809 * 38810 * @param translations A map from message ID to translated message. 38811 * 38812 * These messages are processed and added to a lookup based on their `MessageId`. 38813 * 38814 * @see {@link clearTranslations} for removing translations loaded using this function. 38815 * @see {@link $localize} for tagging messages as needing to be translated. 38816 * @publicApi 38817 */ 38818function loadTranslations(translations) { 38819 // Ensure the translate function exists 38820 if (!$localize.translate) { 38821 $localize.translate = translate; 38822 } 38823 if (!$localize.TRANSLATIONS) { 38824 $localize.TRANSLATIONS = {}; 38825 } 38826 Object.keys(translations).forEach(key => { 38827 $localize.TRANSLATIONS[key] = parseTranslation(translations[key]); 38828 }); 38829} 38830/** 38831 * Remove all translations for `$localize`, if doing runtime translation. 38832 * 38833 * All translations that had been loading into memory using `loadTranslations()` will be removed. 38834 * 38835 * @see {@link loadTranslations} for loading translations at runtime. 38836 * @see {@link $localize} for tagging messages as needing to be translated. 38837 * 38838 * @publicApi 38839 */ 38840function clearTranslations() { 38841 $localize.translate = undefined; 38842 $localize.TRANSLATIONS = {}; 38843} 38844/** 38845 * Translate the text of the given message, using the loaded translations. 38846 * 38847 * This function may reorder (or remove) substitutions as indicated in the matching translation. 38848 */ 38849function translate(messageParts, substitutions) { 38850 try { 38851 return translate$1($localize.TRANSLATIONS, messageParts, substitutions); 38852 } catch (e) { 38853 console.warn(e.message); 38854 return [messageParts, substitutions]; 38855 } 38856} 38857 38858/** 38859 * Tag a template literal string for localization. 38860 * 38861 * For example: 38862 * 38863 * ```ts 38864 * $localize `some string to localize` 38865 * ``` 38866 * 38867 * **Providing meaning, description and id** 38868 * 38869 * You can optionally specify one or more of `meaning`, `description` and `id` for a localized 38870 * string by pre-pending it with a colon delimited block of the form: 38871 * 38872 * ```ts 38873 * $localize`:meaning|description@@id:source message text`; 38874 * 38875 * $localize`:meaning|:source message text`; 38876 * $localize`:description:source message text`; 38877 * $localize`:@@id:source message text`; 38878 * ``` 38879 * 38880 * This format is the same as that used for `i18n` markers in Angular templates. See the 38881 * [Angular i18n guide](guide/i18n-common-prepare#mark-text-in-component-template). 38882 * 38883 * **Naming placeholders** 38884 * 38885 * If the template literal string contains expressions, then the expressions will be automatically 38886 * associated with placeholder names for you. 38887 * 38888 * For example: 38889 * 38890 * ```ts 38891 * $localize `Hi ${name}! There are ${items.length} items.`; 38892 * ``` 38893 * 38894 * will generate a message-source of `Hi {$PH}! There are {$PH_1} items`. 38895 * 38896 * The recommended practice is to name the placeholder associated with each expression though. 38897 * 38898 * Do this by providing the placeholder name wrapped in `:` characters directly after the 38899 * expression. These placeholder names are stripped out of the rendered localized string. 38900 * 38901 * For example, to name the `items.length` expression placeholder `itemCount` you write: 38902 * 38903 * ```ts 38904 * $localize `There are ${items.length}:itemCount: items`; 38905 * ``` 38906 * 38907 * **Escaping colon markers** 38908 * 38909 * If you need to use a `:` character directly at the start of a tagged string that has no 38910 * metadata block, or directly after a substitution expression that has no name you must escape 38911 * the `:` by preceding it with a backslash: 38912 * 38913 * For example: 38914 * 38915 * ```ts 38916 * // message has a metadata block so no need to escape colon 38917 * $localize `:some description::this message starts with a colon (:)`; 38918 * // no metadata block so the colon must be escaped 38919 * $localize `\:this message starts with a colon (:)`; 38920 * ``` 38921 * 38922 * ```ts 38923 * // named substitution so no need to escape colon 38924 * $localize `${label}:label:: ${}` 38925 * // anonymous substitution so colon must be escaped 38926 * $localize `${label}\: ${}` 38927 * ``` 38928 * 38929 * **Processing localized strings:** 38930 * 38931 * There are three scenarios: 38932 * 38933 * * **compile-time inlining**: the `$localize` tag is transformed at compile time by a 38934 * transpiler, removing the tag and replacing the template literal string with a translated 38935 * literal string from a collection of translations provided to the transpilation tool. 38936 * 38937 * * **run-time evaluation**: the `$localize` tag is a run-time function that replaces and 38938 * reorders the parts (static strings and expressions) of the template literal string with strings 38939 * from a collection of translations loaded at run-time. 38940 * 38941 * * **pass-through evaluation**: the `$localize` tag is a run-time function that simply evaluates 38942 * the original template literal string without applying any translations to the parts. This 38943 * version is used during development or where there is no need to translate the localized 38944 * template literals. 38945 * 38946 * @param messageParts a collection of the static parts of the template string. 38947 * @param expressions a collection of the values of each placeholder in the template string. 38948 * @returns the translated string, with the `messageParts` and `expressions` interleaved together. 38949 * 38950 * @globalApi 38951 * @publicApi 38952 */ 38953const $localize$1 = function (messageParts, ...expressions) { 38954 if ($localize$1.translate) { 38955 // Don't use array expansion here to avoid the compiler adding `__read()` helper unnecessarily. 38956 const translation = $localize$1.translate(messageParts, expressions); 38957 messageParts = translation[0]; 38958 expressions = translation[1]; 38959 } 38960 let message = stripBlock(messageParts[0], messageParts.raw[0]); 38961 for (let i = 1; i < messageParts.length; i++) { 38962 message += expressions[i - 1] + stripBlock(messageParts[i], messageParts.raw[i]); 38963 } 38964 return message; 38965}; 38966const BLOCK_MARKER = ':'; 38967/** 38968 * Strip a delimited "block" from the start of the `messagePart`, if it is found. 38969 * 38970 * If a marker character (:) actually appears in the content at the start of a tagged string or 38971 * after a substitution expression, where a block has not been provided the character must be 38972 * escaped with a backslash, `\:`. This function checks for this by looking at the `raw` 38973 * messagePart, which should still contain the backslash. 38974 * 38975 * @param messagePart The cooked message part to process. 38976 * @param rawMessagePart The raw message part to check. 38977 * @returns the message part with the placeholder name stripped, if found. 38978 * @throws an error if the block is unterminated 38979 */ 38980function stripBlock(messagePart, rawMessagePart) { 38981 return rawMessagePart.charAt(0) === BLOCK_MARKER ? messagePart.substring(findEndOfBlock(messagePart, rawMessagePart) + 1) : messagePart; 38982} 38983 38984// This file exports all the `utils` as private exports so that other parts of `@angular/localize` 38985 38986// This file contains the public API of the `@angular/localize` entry-point 38987 38988// DO NOT ADD public exports to this file. 38989 38990 38991 38992/***/ }) 38993 38994}, 38995/******/ __webpack_require__ => { // webpackRuntimeModules 38996/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) 38997/******/ var __webpack_exports__ = (__webpack_exec__(24050), __webpack_exec__(45568)); 38998/******/ } 38999]); 39000//# sourceMappingURL=polyfills.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.