@angular/core 16.0.0-next.1 → 16.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/src/application_config.mjs +21 -0
- package/esm2020/src/application_init.mjs +21 -30
- package/esm2020/src/application_ref.mjs +123 -115
- package/esm2020/src/application_tokens.mjs +28 -33
- package/esm2020/src/change_detection/change_detection.mjs +2 -2
- package/esm2020/src/change_detection/constants.mjs +1 -49
- package/esm2020/src/compiler/compiler_facade_interface.mjs +1 -1
- package/esm2020/src/core.mjs +4 -3
- package/esm2020/src/core_private_export.mjs +6 -9
- package/esm2020/src/core_render3_private_export.mjs +2 -1
- package/esm2020/src/debug/debug_node.mjs +1 -5
- package/esm2020/src/di/contextual.mjs +37 -0
- package/esm2020/src/di/index.mjs +2 -1
- package/esm2020/src/di/r3_injector.mjs +1 -1
- package/esm2020/src/errors.mjs +1 -1
- package/esm2020/src/hydration/annotate.mjs +271 -0
- package/esm2020/src/hydration/api.mjs +128 -0
- package/esm2020/src/hydration/cleanup.mjs +50 -0
- package/esm2020/src/hydration/error_handling.mjs +37 -0
- package/esm2020/src/hydration/interfaces.mjs +17 -0
- package/esm2020/src/hydration/node_lookup_utils.mjs +83 -0
- package/esm2020/src/hydration/skip_hydration.mjs +49 -0
- package/esm2020/src/hydration/tokens.mjs +25 -0
- package/esm2020/src/hydration/utils.mjs +206 -0
- package/esm2020/src/hydration/views.mjs +80 -0
- package/esm2020/src/linker/template_ref.mjs +19 -4
- package/esm2020/src/linker/view_container_ref.mjs +110 -35
- package/esm2020/src/render3/component_ref.mjs +17 -10
- package/esm2020/src/render3/definition.mjs +114 -45
- package/esm2020/src/render3/i18n/i18n_util.mjs +3 -3
- package/esm2020/src/render3/instructions/element.mjs +58 -6
- package/esm2020/src/render3/instructions/element_container.mjs +53 -9
- package/esm2020/src/render3/instructions/listener.mjs +3 -3
- package/esm2020/src/render3/instructions/shared.mjs +71 -17
- package/esm2020/src/render3/instructions/template.mjs +57 -6
- package/esm2020/src/render3/instructions/text.mjs +36 -5
- package/esm2020/src/render3/interfaces/container.mjs +3 -2
- package/esm2020/src/render3/interfaces/node.mjs +1 -1
- package/esm2020/src/render3/interfaces/renderer_dom.mjs +1 -1
- package/esm2020/src/render3/interfaces/type_checks.mjs +2 -2
- package/esm2020/src/render3/interfaces/view.mjs +3 -2
- package/esm2020/src/render3/jit/directive.mjs +1 -2
- package/esm2020/src/render3/jit/module.mjs +3 -2
- package/esm2020/src/render3/ng_module_ref.mjs +9 -5
- package/esm2020/src/render3/node_manipulation.mjs +12 -1
- package/esm2020/src/render3/state.mjs +45 -1
- package/esm2020/src/render3/util/discovery_utils.mjs +3 -2
- package/esm2020/src/transfer_state.mjs +15 -10
- package/esm2020/src/util/lang.mjs +1 -11
- package/esm2020/src/util/ng_dev_mode.mjs +4 -1
- package/esm2020/src/version.mjs +1 -1
- package/esm2020/src/zone/ng_zone.mjs +62 -1
- package/esm2020/testing/src/logger.mjs +3 -3
- package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
- package/esm2020/testing/src/test_bed_compiler.mjs +4 -4
- package/fesm2015/core.mjs +2990 -1683
- package/fesm2015/core.mjs.map +1 -1
- package/fesm2015/testing.mjs +3615 -2533
- package/fesm2015/testing.mjs.map +1 -1
- package/fesm2020/core.mjs +2831 -1520
- package/fesm2020/core.mjs.map +1 -1
- package/fesm2020/testing.mjs +2390 -1305
- package/fesm2020/testing.mjs.map +1 -1
- package/index.d.ts +636 -584
- package/package.json +2 -2
- package/schematics/migrations/relative-link-resolution/bundle.js +7 -7
- package/schematics/migrations/router-link-with-href/bundle.js +10 -10
- package/schematics/ng-generate/standalone-migration/bundle.js +882 -758
- package/schematics/ng-generate/standalone-migration/bundle.js.map +4 -4
- package/testing/index.d.ts +1 -1
package/fesm2020/testing.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v16.0.0-next.
|
|
2
|
+
* @license Angular v16.0.0-next.3
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ɵisEnvironmentProviders, ModuleWithComponentFactories, ɵconvertToBitFlags, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
|
|
7
|
+
import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, ɵprovideNgZoneChangeDetection, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ɵisEnvironmentProviders, ModuleWithComponentFactories, ɵconvertToBitFlags, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
|
|
8
8
|
import { ResourceLoader } from '@angular/compiler';
|
|
9
9
|
import { Subject, Subscription } from 'rxjs';
|
|
10
10
|
|
|
@@ -1635,6 +1635,9 @@ function ngDevModeResetPerfCounters() {
|
|
|
1635
1635
|
rendererAppendChild: 0,
|
|
1636
1636
|
rendererInsertBefore: 0,
|
|
1637
1637
|
rendererCreateComment: 0,
|
|
1638
|
+
hydratedNodes: 0,
|
|
1639
|
+
hydratedComponents: 0,
|
|
1640
|
+
dehydratedViewsRemoved: 0,
|
|
1638
1641
|
};
|
|
1639
1642
|
// Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
|
1640
1643
|
const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
|
|
@@ -2114,54 +2117,6 @@ var ChangeDetectionStrategy;
|
|
|
2114
2117
|
*/
|
|
2115
2118
|
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
|
2116
2119
|
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
|
|
2117
|
-
/**
|
|
2118
|
-
* Defines the possible states of the default change detector.
|
|
2119
|
-
* @see `ChangeDetectorRef`
|
|
2120
|
-
*/
|
|
2121
|
-
var ChangeDetectorStatus;
|
|
2122
|
-
(function (ChangeDetectorStatus) {
|
|
2123
|
-
/**
|
|
2124
|
-
* A state in which, after calling `detectChanges()`, the change detector
|
|
2125
|
-
* state becomes `Checked`, and must be explicitly invoked or reactivated.
|
|
2126
|
-
*/
|
|
2127
|
-
ChangeDetectorStatus[ChangeDetectorStatus["CheckOnce"] = 0] = "CheckOnce";
|
|
2128
|
-
/**
|
|
2129
|
-
* A state in which change detection is skipped until the change detector mode
|
|
2130
|
-
* becomes `CheckOnce`.
|
|
2131
|
-
*/
|
|
2132
|
-
ChangeDetectorStatus[ChangeDetectorStatus["Checked"] = 1] = "Checked";
|
|
2133
|
-
/**
|
|
2134
|
-
* A state in which change detection continues automatically until explicitly
|
|
2135
|
-
* deactivated.
|
|
2136
|
-
*/
|
|
2137
|
-
ChangeDetectorStatus[ChangeDetectorStatus["CheckAlways"] = 2] = "CheckAlways";
|
|
2138
|
-
/**
|
|
2139
|
-
* A state in which a change detector sub tree is not a part of the main tree and
|
|
2140
|
-
* should be skipped.
|
|
2141
|
-
*/
|
|
2142
|
-
ChangeDetectorStatus[ChangeDetectorStatus["Detached"] = 3] = "Detached";
|
|
2143
|
-
/**
|
|
2144
|
-
* Indicates that the change detector encountered an error checking a binding
|
|
2145
|
-
* or calling a directive lifecycle method and is now in an inconsistent state. Change
|
|
2146
|
-
* detectors in this state do not detect changes.
|
|
2147
|
-
*/
|
|
2148
|
-
ChangeDetectorStatus[ChangeDetectorStatus["Errored"] = 4] = "Errored";
|
|
2149
|
-
/**
|
|
2150
|
-
* Indicates that the change detector has been destroyed.
|
|
2151
|
-
*/
|
|
2152
|
-
ChangeDetectorStatus[ChangeDetectorStatus["Destroyed"] = 5] = "Destroyed";
|
|
2153
|
-
})(ChangeDetectorStatus || (ChangeDetectorStatus = {}));
|
|
2154
|
-
/**
|
|
2155
|
-
* Reports whether a given strategy is currently the default for change detection.
|
|
2156
|
-
* @param changeDetectionStrategy The strategy to check.
|
|
2157
|
-
* @returns True if the given strategy is the current default, false otherwise.
|
|
2158
|
-
* @see `ChangeDetectorStatus`
|
|
2159
|
-
* @see `ChangeDetectorRef`
|
|
2160
|
-
*/
|
|
2161
|
-
function isDefaultChangeDetectionStrategy(changeDetectionStrategy) {
|
|
2162
|
-
return changeDetectionStrategy == null ||
|
|
2163
|
-
changeDetectionStrategy === ChangeDetectionStrategy.Default;
|
|
2164
|
-
}
|
|
2165
2120
|
|
|
2166
2121
|
/**
|
|
2167
2122
|
* Defines the CSS styles encapsulation policies for the {@link Component} decorator's
|
|
@@ -2243,330 +2198,1033 @@ const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafe
|
|
|
2243
2198
|
*/
|
|
2244
2199
|
const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
|
|
2245
2200
|
|
|
2246
|
-
/** Counter used to generate unique IDs for component definitions. */
|
|
2247
|
-
let componentDefCount = 0;
|
|
2248
2201
|
/**
|
|
2249
|
-
*
|
|
2202
|
+
* Returns an index of `classToSearch` in `className` taking token boundaries into account.
|
|
2250
2203
|
*
|
|
2204
|
+
* `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
|
|
2251
2205
|
*
|
|
2252
|
-
*
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
* // [Symbol] syntax will not be supported by TypeScript until v2.7
|
|
2257
|
-
* static ɵcmp = defineComponent({
|
|
2258
|
-
* ...
|
|
2259
|
-
* });
|
|
2260
|
-
* }
|
|
2261
|
-
* ```
|
|
2262
|
-
* @codeGenApi
|
|
2206
|
+
* @param className A string containing classes (whitespace separated)
|
|
2207
|
+
* @param classToSearch A class name to locate
|
|
2208
|
+
* @param startingIndex Starting location of search
|
|
2209
|
+
* @returns an index of the located class (or -1 if not found)
|
|
2263
2210
|
*/
|
|
2264
|
-
function
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
hostAttrs: componentDefinition.hostAttrs || null,
|
|
2284
|
-
contentQueries: componentDefinition.contentQueries || null,
|
|
2285
|
-
declaredInputs: declaredInputs,
|
|
2286
|
-
inputs: null,
|
|
2287
|
-
outputs: null,
|
|
2288
|
-
exportAs: componentDefinition.exportAs || null,
|
|
2289
|
-
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
|
|
2290
|
-
directiveDefs: null,
|
|
2291
|
-
pipeDefs: null,
|
|
2292
|
-
standalone,
|
|
2293
|
-
dependencies: standalone && componentDefinition.dependencies || null,
|
|
2294
|
-
getStandaloneInjector: null,
|
|
2295
|
-
selectors: componentDefinition.selectors || EMPTY_ARRAY,
|
|
2296
|
-
viewQuery: componentDefinition.viewQuery || null,
|
|
2297
|
-
features: componentDefinition.features || null,
|
|
2298
|
-
data: componentDefinition.data || {},
|
|
2299
|
-
encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
|
|
2300
|
-
id: `c${componentDefCount++}`,
|
|
2301
|
-
styles: componentDefinition.styles || EMPTY_ARRAY,
|
|
2302
|
-
_: null,
|
|
2303
|
-
setInput: null,
|
|
2304
|
-
schemas: componentDefinition.schemas || null,
|
|
2305
|
-
tView: null,
|
|
2306
|
-
findHostDirectiveDefs: null,
|
|
2307
|
-
hostDirectives: null,
|
|
2308
|
-
};
|
|
2309
|
-
const dependencies = componentDefinition.dependencies;
|
|
2310
|
-
const feature = componentDefinition.features;
|
|
2311
|
-
def.inputs = invertObject(componentDefinition.inputs, declaredInputs),
|
|
2312
|
-
def.outputs = invertObject(componentDefinition.outputs),
|
|
2313
|
-
feature && feature.forEach((fn) => fn(def));
|
|
2314
|
-
def.directiveDefs = dependencies ?
|
|
2315
|
-
(() => (typeof dependencies === 'function' ? dependencies() : dependencies)
|
|
2316
|
-
.map(extractDirectiveDef)
|
|
2317
|
-
.filter(nonNull)) :
|
|
2318
|
-
null;
|
|
2319
|
-
def.pipeDefs = dependencies ?
|
|
2320
|
-
(() => (typeof dependencies === 'function' ? dependencies() : dependencies)
|
|
2321
|
-
.map(getPipeDef$1)
|
|
2322
|
-
.filter(nonNull)) :
|
|
2323
|
-
null;
|
|
2324
|
-
return def;
|
|
2325
|
-
});
|
|
2211
|
+
function classIndexOf(className, classToSearch, startingIndex) {
|
|
2212
|
+
ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
|
|
2213
|
+
let end = className.length;
|
|
2214
|
+
while (true) {
|
|
2215
|
+
const foundIndex = className.indexOf(classToSearch, startingIndex);
|
|
2216
|
+
if (foundIndex === -1)
|
|
2217
|
+
return foundIndex;
|
|
2218
|
+
if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
|
|
2219
|
+
// Ensure that it has leading whitespace
|
|
2220
|
+
const length = classToSearch.length;
|
|
2221
|
+
if (foundIndex + length === end ||
|
|
2222
|
+
className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
|
|
2223
|
+
// Ensure that it has trailing whitespace
|
|
2224
|
+
return foundIndex;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
// False positive, keep searching from where we left off.
|
|
2228
|
+
startingIndex = foundIndex + 1;
|
|
2229
|
+
}
|
|
2326
2230
|
}
|
|
2231
|
+
|
|
2327
2232
|
/**
|
|
2328
|
-
*
|
|
2329
|
-
* definition, when generating a direct reference in the component file would otherwise create an
|
|
2330
|
-
* import cycle.
|
|
2233
|
+
* Assigns all attribute values to the provided element via the inferred renderer.
|
|
2331
2234
|
*
|
|
2332
|
-
*
|
|
2235
|
+
* This function accepts two forms of attribute entries:
|
|
2333
2236
|
*
|
|
2334
|
-
*
|
|
2237
|
+
* default: (key, value):
|
|
2238
|
+
* attrs = [key1, value1, key2, value2]
|
|
2239
|
+
*
|
|
2240
|
+
* namespaced: (NAMESPACE_MARKER, uri, name, value)
|
|
2241
|
+
* attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
|
|
2242
|
+
*
|
|
2243
|
+
* The `attrs` array can contain a mix of both the default and namespaced entries.
|
|
2244
|
+
* The "default" values are set without a marker, but if the function comes across
|
|
2245
|
+
* a marker value then it will attempt to set a namespaced value. If the marker is
|
|
2246
|
+
* not of a namespaced value then the function will quit and return the index value
|
|
2247
|
+
* where it stopped during the iteration of the attrs array.
|
|
2248
|
+
*
|
|
2249
|
+
* See [AttributeMarker] to understand what the namespace marker value is.
|
|
2250
|
+
*
|
|
2251
|
+
* Note that this instruction does not support assigning style and class values to
|
|
2252
|
+
* an element. See `elementStart` and `elementHostAttrs` to learn how styling values
|
|
2253
|
+
* are applied to an element.
|
|
2254
|
+
* @param renderer The renderer to be used
|
|
2255
|
+
* @param native The element that the attributes will be assigned to
|
|
2256
|
+
* @param attrs The attribute array of values that will be assigned to the element
|
|
2257
|
+
* @returns the index value that was last accessed in the attributes array
|
|
2335
2258
|
*/
|
|
2336
|
-
function
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2259
|
+
function setUpAttributes(renderer, native, attrs) {
|
|
2260
|
+
let i = 0;
|
|
2261
|
+
while (i < attrs.length) {
|
|
2262
|
+
const value = attrs[i];
|
|
2263
|
+
if (typeof value === 'number') {
|
|
2264
|
+
// only namespaces are supported. Other value types (such as style/class
|
|
2265
|
+
// entries) are not supported in this function.
|
|
2266
|
+
if (value !== 0 /* AttributeMarker.NamespaceURI */) {
|
|
2267
|
+
break;
|
|
2268
|
+
}
|
|
2269
|
+
// we just landed on the marker value ... therefore
|
|
2270
|
+
// we should skip to the next entry
|
|
2271
|
+
i++;
|
|
2272
|
+
const namespaceURI = attrs[i++];
|
|
2273
|
+
const attrName = attrs[i++];
|
|
2274
|
+
const attrVal = attrs[i++];
|
|
2275
|
+
ngDevMode && ngDevMode.rendererSetAttribute++;
|
|
2276
|
+
renderer.setAttribute(native, attrName, attrVal, namespaceURI);
|
|
2277
|
+
}
|
|
2278
|
+
else {
|
|
2279
|
+
// attrName is string;
|
|
2280
|
+
const attrName = value;
|
|
2281
|
+
const attrVal = attrs[++i];
|
|
2282
|
+
// Standard attributes
|
|
2283
|
+
ngDevMode && ngDevMode.rendererSetAttribute++;
|
|
2284
|
+
if (isAnimationProp(attrName)) {
|
|
2285
|
+
renderer.setProperty(native, attrName, attrVal);
|
|
2286
|
+
}
|
|
2287
|
+
else {
|
|
2288
|
+
renderer.setAttribute(native, attrName, attrVal);
|
|
2289
|
+
}
|
|
2290
|
+
i++;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
// another piece of code may iterate over the same attributes array. Therefore
|
|
2294
|
+
// it may be helpful to return the exact spot where the attributes array exited
|
|
2295
|
+
// whether by running into an unsupported marker or if all the static values were
|
|
2296
|
+
// iterated over.
|
|
2297
|
+
return i;
|
|
2346
2298
|
}
|
|
2347
2299
|
/**
|
|
2348
|
-
*
|
|
2300
|
+
* Test whether the given value is a marker that indicates that the following
|
|
2301
|
+
* attribute values in a `TAttributes` array are only the names of attributes,
|
|
2302
|
+
* and not name-value pairs.
|
|
2303
|
+
* @param marker The attribute marker to test.
|
|
2304
|
+
* @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
|
|
2349
2305
|
*/
|
|
2350
|
-
function
|
|
2351
|
-
return
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
schemas: def.schemas || null,
|
|
2360
|
-
id: def.id || null,
|
|
2361
|
-
};
|
|
2362
|
-
return res;
|
|
2363
|
-
});
|
|
2306
|
+
function isNameOnlyAttributeMarker(marker) {
|
|
2307
|
+
return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
|
|
2308
|
+
marker === 6 /* AttributeMarker.I18n */;
|
|
2309
|
+
}
|
|
2310
|
+
function isAnimationProp(name) {
|
|
2311
|
+
// Perf note: accessing charCodeAt to check for the first character of a string is faster as
|
|
2312
|
+
// compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
|
|
2313
|
+
// charCodeAt doesn't allocate memory to return a substring.
|
|
2314
|
+
return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
|
|
2364
2315
|
}
|
|
2365
2316
|
/**
|
|
2366
|
-
*
|
|
2367
|
-
* existing module definition.
|
|
2317
|
+
* Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
|
|
2368
2318
|
*
|
|
2369
|
-
*
|
|
2370
|
-
* marked pure to tree-shake it from the bundle, allowing for all referenced declarations
|
|
2371
|
-
* to become eligible for tree-shaking as well.
|
|
2319
|
+
* This merge function keeps the order of attrs same.
|
|
2372
2320
|
*
|
|
2373
|
-
* @
|
|
2321
|
+
* @param dst Location of where the merged `TAttributes` should end up.
|
|
2322
|
+
* @param src `TAttributes` which should be appended to `dst`
|
|
2374
2323
|
*/
|
|
2375
|
-
function
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2324
|
+
function mergeHostAttrs(dst, src) {
|
|
2325
|
+
if (src === null || src.length === 0) {
|
|
2326
|
+
// do nothing
|
|
2327
|
+
}
|
|
2328
|
+
else if (dst === null || dst.length === 0) {
|
|
2329
|
+
// We have source, but dst is empty, just make a copy.
|
|
2330
|
+
dst = src.slice();
|
|
2331
|
+
}
|
|
2332
|
+
else {
|
|
2333
|
+
let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
|
|
2334
|
+
for (let i = 0; i < src.length; i++) {
|
|
2335
|
+
const item = src[i];
|
|
2336
|
+
if (typeof item === 'number') {
|
|
2337
|
+
srcMarker = item;
|
|
2338
|
+
}
|
|
2339
|
+
else {
|
|
2340
|
+
if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
|
|
2341
|
+
// Case where we need to consume `key1`, `key2`, `value` items.
|
|
2342
|
+
}
|
|
2343
|
+
else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
|
|
2344
|
+
srcMarker === 2 /* AttributeMarker.Styles */) {
|
|
2345
|
+
// Case where we have to consume `key1` and `value` only.
|
|
2346
|
+
mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
|
|
2347
|
+
}
|
|
2348
|
+
else {
|
|
2349
|
+
// Case where we have to consume `key1` only.
|
|
2350
|
+
mergeHostAttribute(dst, srcMarker, item, null, null);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
return dst;
|
|
2382
2356
|
}
|
|
2383
2357
|
/**
|
|
2384
|
-
*
|
|
2385
|
-
* minified keys, are part of the values, and the values are parsed so that
|
|
2386
|
-
* the publicName of the property is the new key
|
|
2387
|
-
*
|
|
2388
|
-
* e.g. for
|
|
2389
|
-
*
|
|
2390
|
-
* ```
|
|
2391
|
-
* class Comp {
|
|
2392
|
-
* @Input()
|
|
2393
|
-
* propName1: string;
|
|
2394
|
-
*
|
|
2395
|
-
* @Input('publicName2')
|
|
2396
|
-
* declaredPropName2: number;
|
|
2397
|
-
* }
|
|
2398
|
-
* ```
|
|
2399
|
-
*
|
|
2400
|
-
* will be serialized as
|
|
2358
|
+
* Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
|
|
2401
2359
|
*
|
|
2402
|
-
*
|
|
2403
|
-
*
|
|
2404
|
-
*
|
|
2405
|
-
*
|
|
2406
|
-
*
|
|
2407
|
-
* ```
|
|
2408
|
-
*
|
|
2409
|
-
* which is than translated by the minifier as:
|
|
2410
|
-
*
|
|
2411
|
-
* ```
|
|
2412
|
-
* {
|
|
2413
|
-
* minifiedPropName1: 'propName1',
|
|
2414
|
-
* minifiedPropName2: ['publicName2', 'declaredPropName2'],
|
|
2415
|
-
* }
|
|
2416
|
-
* ```
|
|
2417
|
-
*
|
|
2418
|
-
* becomes: (public name => minifiedName)
|
|
2419
|
-
*
|
|
2420
|
-
* ```
|
|
2421
|
-
* {
|
|
2422
|
-
* 'propName1': 'minifiedPropName1',
|
|
2423
|
-
* 'publicName2': 'minifiedPropName2',
|
|
2424
|
-
* }
|
|
2425
|
-
* ```
|
|
2426
|
-
*
|
|
2427
|
-
* Optionally the function can take `secondary` which will result in: (public name => declared name)
|
|
2428
|
-
*
|
|
2429
|
-
* ```
|
|
2430
|
-
* {
|
|
2431
|
-
* 'propName1': 'propName1',
|
|
2432
|
-
* 'publicName2': 'declaredPropName2',
|
|
2433
|
-
* }
|
|
2434
|
-
* ```
|
|
2435
|
-
*
|
|
2436
|
-
|
|
2360
|
+
* @param dst `TAttributes` to append to.
|
|
2361
|
+
* @param marker Region where the `key`/`value` should be added.
|
|
2362
|
+
* @param key1 Key to add to `TAttributes`
|
|
2363
|
+
* @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
|
|
2364
|
+
* @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
|
|
2437
2365
|
*/
|
|
2438
|
-
function
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2366
|
+
function mergeHostAttribute(dst, marker, key1, key2, value) {
|
|
2367
|
+
let i = 0;
|
|
2368
|
+
// Assume that new markers will be inserted at the end.
|
|
2369
|
+
let markerInsertPosition = dst.length;
|
|
2370
|
+
// scan until correct type.
|
|
2371
|
+
if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
|
|
2372
|
+
markerInsertPosition = -1;
|
|
2373
|
+
}
|
|
2374
|
+
else {
|
|
2375
|
+
while (i < dst.length) {
|
|
2376
|
+
const dstValue = dst[i++];
|
|
2377
|
+
if (typeof dstValue === 'number') {
|
|
2378
|
+
if (dstValue === marker) {
|
|
2379
|
+
markerInsertPosition = -1;
|
|
2380
|
+
break;
|
|
2381
|
+
}
|
|
2382
|
+
else if (dstValue > marker) {
|
|
2383
|
+
// We need to save this as we want the markers to be inserted in specific order.
|
|
2384
|
+
markerInsertPosition = i - 1;
|
|
2385
|
+
break;
|
|
2386
|
+
}
|
|
2449
2387
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
// search until you find place of insertion
|
|
2391
|
+
while (i < dst.length) {
|
|
2392
|
+
const item = dst[i];
|
|
2393
|
+
if (typeof item === 'number') {
|
|
2394
|
+
// since `i` started as the index after the marker, we did not find it if we are at the next
|
|
2395
|
+
// marker
|
|
2396
|
+
break;
|
|
2397
|
+
}
|
|
2398
|
+
else if (item === key1) {
|
|
2399
|
+
// We already have same token
|
|
2400
|
+
if (key2 === null) {
|
|
2401
|
+
if (value !== null) {
|
|
2402
|
+
dst[i + 1] = value;
|
|
2403
|
+
}
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
else if (key2 === dst[i + 1]) {
|
|
2407
|
+
dst[i + 2] = value;
|
|
2408
|
+
return;
|
|
2453
2409
|
}
|
|
2454
2410
|
}
|
|
2411
|
+
// Increment counter.
|
|
2412
|
+
i++;
|
|
2413
|
+
if (key2 !== null)
|
|
2414
|
+
i++;
|
|
2415
|
+
if (value !== null)
|
|
2416
|
+
i++;
|
|
2417
|
+
}
|
|
2418
|
+
// insert at location.
|
|
2419
|
+
if (markerInsertPosition !== -1) {
|
|
2420
|
+
dst.splice(markerInsertPosition, 0, marker);
|
|
2421
|
+
i = markerInsertPosition + 1;
|
|
2422
|
+
}
|
|
2423
|
+
dst.splice(i++, 0, key1);
|
|
2424
|
+
if (key2 !== null) {
|
|
2425
|
+
dst.splice(i++, 0, key2);
|
|
2426
|
+
}
|
|
2427
|
+
if (value !== null) {
|
|
2428
|
+
dst.splice(i++, 0, value);
|
|
2455
2429
|
}
|
|
2456
|
-
return newLookup;
|
|
2457
2430
|
}
|
|
2431
|
+
|
|
2432
|
+
const NG_TEMPLATE_SELECTOR = 'ng-template';
|
|
2458
2433
|
/**
|
|
2459
|
-
*
|
|
2460
|
-
*
|
|
2461
|
-
* # Example
|
|
2462
|
-
* ```ts
|
|
2463
|
-
* class MyDirective {
|
|
2464
|
-
* // Generated by Angular Template Compiler
|
|
2465
|
-
* // [Symbol] syntax will not be supported by TypeScript until v2.7
|
|
2466
|
-
* static ɵdir = ɵɵdefineDirective({
|
|
2467
|
-
* ...
|
|
2468
|
-
* });
|
|
2469
|
-
* }
|
|
2470
|
-
* ```
|
|
2471
|
-
*
|
|
2472
|
-
* @codeGenApi
|
|
2473
|
-
*/
|
|
2474
|
-
const ɵɵdefineDirective = ɵɵdefineComponent;
|
|
2475
|
-
/**
|
|
2476
|
-
* Create a pipe definition object.
|
|
2477
|
-
*
|
|
2478
|
-
* # Example
|
|
2479
|
-
* ```
|
|
2480
|
-
* class MyPipe implements PipeTransform {
|
|
2481
|
-
* // Generated by Angular Template Compiler
|
|
2482
|
-
* static ɵpipe = definePipe({
|
|
2483
|
-
* ...
|
|
2484
|
-
* });
|
|
2485
|
-
* }
|
|
2486
|
-
* ```
|
|
2487
|
-
* @param pipeDef Pipe definition generated by the compiler
|
|
2434
|
+
* Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
|
|
2488
2435
|
*
|
|
2489
|
-
* @
|
|
2436
|
+
* @param attrs `TAttributes` to search through.
|
|
2437
|
+
* @param cssClassToMatch class to match (lowercase)
|
|
2438
|
+
* @param isProjectionMode Whether or not class matching should look into the attribute `class` in
|
|
2439
|
+
* addition to the `AttributeMarker.Classes`.
|
|
2490
2440
|
*/
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2441
|
+
function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
|
|
2442
|
+
// TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
|
|
2443
|
+
// It is strange to me that sometimes the class information comes in form of `class` attribute
|
|
2444
|
+
// and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
|
|
2445
|
+
// if that is the right behavior.
|
|
2446
|
+
ngDevMode &&
|
|
2447
|
+
assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
|
|
2448
|
+
let i = 0;
|
|
2449
|
+
while (i < attrs.length) {
|
|
2450
|
+
let item = attrs[i++];
|
|
2451
|
+
if (isProjectionMode && item === 'class') {
|
|
2452
|
+
item = attrs[i];
|
|
2453
|
+
if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
else if (item === 1 /* AttributeMarker.Classes */) {
|
|
2458
|
+
// We found the classes section. Start searching for the class.
|
|
2459
|
+
while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
|
|
2460
|
+
// while we have strings
|
|
2461
|
+
if (item.toLowerCase() === cssClassToMatch)
|
|
2462
|
+
return true;
|
|
2463
|
+
}
|
|
2464
|
+
return false;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
return false;
|
|
2500
2468
|
}
|
|
2501
2469
|
/**
|
|
2502
|
-
*
|
|
2503
|
-
*
|
|
2504
|
-
*
|
|
2470
|
+
* Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
|
|
2471
|
+
*
|
|
2472
|
+
* @param tNode current TNode
|
|
2505
2473
|
*/
|
|
2506
|
-
function
|
|
2507
|
-
return type
|
|
2508
|
-
}
|
|
2509
|
-
function getDirectiveDef(type) {
|
|
2510
|
-
return type[NG_DIR_DEF] || null;
|
|
2511
|
-
}
|
|
2512
|
-
function getPipeDef$1(type) {
|
|
2513
|
-
return type[NG_PIPE_DEF] || null;
|
|
2474
|
+
function isInlineTemplate(tNode) {
|
|
2475
|
+
return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
|
|
2514
2476
|
}
|
|
2515
2477
|
/**
|
|
2516
|
-
*
|
|
2517
|
-
* This will return false if passed anything other than a Component, Directive, or Pipe class
|
|
2518
|
-
* See this guide for additional information: https://angular.io/guide/standalone-components
|
|
2478
|
+
* Function that checks whether a given tNode matches tag-based selector and has a valid type.
|
|
2519
2479
|
*
|
|
2520
|
-
*
|
|
2521
|
-
*
|
|
2480
|
+
* Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
|
|
2481
|
+
* directive matching mode:
|
|
2482
|
+
* - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
|
|
2483
|
+
* different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
|
|
2484
|
+
* tag name was extracted from * syntax so we would match the same directive twice);
|
|
2485
|
+
* - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
|
|
2486
|
+
* (applicable to TNodeType.Container only).
|
|
2522
2487
|
*/
|
|
2523
|
-
function
|
|
2524
|
-
const
|
|
2525
|
-
return
|
|
2526
|
-
}
|
|
2527
|
-
function getNgModuleDef(type, throwNotFound) {
|
|
2528
|
-
const ngModuleDef = type[NG_MOD_DEF] || null;
|
|
2529
|
-
if (!ngModuleDef && throwNotFound === true) {
|
|
2530
|
-
throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
|
|
2531
|
-
}
|
|
2532
|
-
return ngModuleDef;
|
|
2488
|
+
function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
|
|
2489
|
+
const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
|
|
2490
|
+
return currentSelector === tagNameToCompare;
|
|
2533
2491
|
}
|
|
2534
|
-
|
|
2535
|
-
// Below are constants for LView indices to help us look up LView members
|
|
2536
|
-
// without having to remember the specific indices.
|
|
2537
|
-
// Uglify will inline these when minifying so there shouldn't be a cost.
|
|
2538
|
-
const HOST = 0;
|
|
2539
|
-
const TVIEW = 1;
|
|
2540
|
-
const FLAGS = 2;
|
|
2541
|
-
const PARENT = 3;
|
|
2542
|
-
const NEXT = 4;
|
|
2543
|
-
const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
|
|
2544
|
-
const T_HOST = 6;
|
|
2545
|
-
const CLEANUP = 7;
|
|
2546
|
-
const CONTEXT = 8;
|
|
2547
|
-
const INJECTOR$1 = 9;
|
|
2548
|
-
const RENDERER_FACTORY = 10;
|
|
2549
|
-
const RENDERER = 11;
|
|
2550
|
-
const SANITIZER = 12;
|
|
2551
|
-
const CHILD_HEAD = 13;
|
|
2552
|
-
const CHILD_TAIL = 14;
|
|
2553
|
-
// FIXME(misko): Investigate if the three declarations aren't all same thing.
|
|
2554
|
-
const DECLARATION_VIEW = 15;
|
|
2555
|
-
const DECLARATION_COMPONENT_VIEW = 16;
|
|
2556
|
-
const DECLARATION_LCONTAINER = 17;
|
|
2557
|
-
const PREORDER_HOOK_FLAGS = 18;
|
|
2558
|
-
const QUERIES = 19;
|
|
2559
|
-
const ID = 20;
|
|
2560
|
-
const EMBEDDED_VIEW_INJECTOR = 21;
|
|
2561
|
-
const ON_DESTROY_HOOKS = 22;
|
|
2562
2492
|
/**
|
|
2563
|
-
*
|
|
2493
|
+
* A utility function to match an Ivy node static data against a simple CSS selector
|
|
2564
2494
|
*
|
|
2565
|
-
*
|
|
2495
|
+
* @param node static data of the node to match
|
|
2496
|
+
* @param selector The selector to try matching against the node.
|
|
2497
|
+
* @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
|
|
2498
|
+
* directive matching.
|
|
2499
|
+
* @returns true if node matches the selector.
|
|
2500
|
+
*/
|
|
2501
|
+
function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
|
|
2502
|
+
ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
|
|
2503
|
+
let mode = 4 /* SelectorFlags.ELEMENT */;
|
|
2504
|
+
const nodeAttrs = tNode.attrs || [];
|
|
2505
|
+
// Find the index of first attribute that has no value, only a name.
|
|
2506
|
+
const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
|
|
2507
|
+
// When processing ":not" selectors, we skip to the next ":not" if the
|
|
2508
|
+
// current one doesn't match
|
|
2509
|
+
let skipToNextSelector = false;
|
|
2510
|
+
for (let i = 0; i < selector.length; i++) {
|
|
2511
|
+
const current = selector[i];
|
|
2512
|
+
if (typeof current === 'number') {
|
|
2513
|
+
// If we finish processing a :not selector and it hasn't failed, return false
|
|
2514
|
+
if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
|
|
2515
|
+
return false;
|
|
2516
|
+
}
|
|
2517
|
+
// If we are skipping to the next :not() and this mode flag is positive,
|
|
2518
|
+
// it's a part of the current :not() selector, and we should keep skipping
|
|
2519
|
+
if (skipToNextSelector && isPositive(current))
|
|
2520
|
+
continue;
|
|
2521
|
+
skipToNextSelector = false;
|
|
2522
|
+
mode = current | (mode & 1 /* SelectorFlags.NOT */);
|
|
2523
|
+
continue;
|
|
2524
|
+
}
|
|
2525
|
+
if (skipToNextSelector)
|
|
2526
|
+
continue;
|
|
2527
|
+
if (mode & 4 /* SelectorFlags.ELEMENT */) {
|
|
2528
|
+
mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
|
|
2529
|
+
if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
|
|
2530
|
+
current === '' && selector.length === 1) {
|
|
2531
|
+
if (isPositive(mode))
|
|
2532
|
+
return false;
|
|
2533
|
+
skipToNextSelector = true;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
else {
|
|
2537
|
+
const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
|
|
2538
|
+
// special case for matching against classes when a tNode has been instantiated with
|
|
2539
|
+
// class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
|
|
2540
|
+
if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
|
|
2541
|
+
if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
|
|
2542
|
+
if (isPositive(mode))
|
|
2543
|
+
return false;
|
|
2544
|
+
skipToNextSelector = true;
|
|
2545
|
+
}
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
|
|
2549
|
+
const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
|
|
2550
|
+
if (attrIndexInNode === -1) {
|
|
2551
|
+
if (isPositive(mode))
|
|
2552
|
+
return false;
|
|
2553
|
+
skipToNextSelector = true;
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2556
|
+
if (selectorAttrValue !== '') {
|
|
2557
|
+
let nodeAttrValue;
|
|
2558
|
+
if (attrIndexInNode > nameOnlyMarkerIdx) {
|
|
2559
|
+
nodeAttrValue = '';
|
|
2560
|
+
}
|
|
2561
|
+
else {
|
|
2562
|
+
ngDevMode &&
|
|
2563
|
+
assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
|
|
2564
|
+
// we lowercase the attribute value to be able to match
|
|
2565
|
+
// selectors without case-sensitivity
|
|
2566
|
+
// (selectors are already in lowercase when generated)
|
|
2567
|
+
nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
|
|
2568
|
+
}
|
|
2569
|
+
const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
|
|
2570
|
+
if (compareAgainstClassName &&
|
|
2571
|
+
classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
|
|
2572
|
+
mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
|
|
2573
|
+
if (isPositive(mode))
|
|
2574
|
+
return false;
|
|
2575
|
+
skipToNextSelector = true;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return isPositive(mode) || skipToNextSelector;
|
|
2581
|
+
}
|
|
2582
|
+
function isPositive(mode) {
|
|
2583
|
+
return (mode & 1 /* SelectorFlags.NOT */) === 0;
|
|
2584
|
+
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Examines the attribute's definition array for a node to find the index of the
|
|
2587
|
+
* attribute that matches the given `name`.
|
|
2588
|
+
*
|
|
2589
|
+
* NOTE: This will not match namespaced attributes.
|
|
2590
|
+
*
|
|
2591
|
+
* Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
|
|
2592
|
+
* The following table summarizes which types of attributes we attempt to match:
|
|
2593
|
+
*
|
|
2594
|
+
* ===========================================================================================================
|
|
2595
|
+
* Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
|
|
2596
|
+
* Attributes
|
|
2597
|
+
* ===========================================================================================================
|
|
2598
|
+
* Inline + Projection | YES | YES | NO | YES
|
|
2599
|
+
* -----------------------------------------------------------------------------------------------------------
|
|
2600
|
+
* Inline + Directive | NO | NO | YES | NO
|
|
2601
|
+
* -----------------------------------------------------------------------------------------------------------
|
|
2602
|
+
* Non-inline + Projection | YES | YES | NO | YES
|
|
2603
|
+
* -----------------------------------------------------------------------------------------------------------
|
|
2604
|
+
* Non-inline + Directive | YES | YES | NO | YES
|
|
2605
|
+
* ===========================================================================================================
|
|
2606
|
+
*
|
|
2607
|
+
* @param name the name of the attribute to find
|
|
2608
|
+
* @param attrs the attribute array to examine
|
|
2609
|
+
* @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
|
|
2610
|
+
* rather than a manually expanded template node (e.g `<ng-template>`).
|
|
2611
|
+
* @param isProjectionMode true if we are matching against content projection otherwise we are
|
|
2612
|
+
* matching against directives.
|
|
2613
|
+
*/
|
|
2614
|
+
function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
|
|
2615
|
+
if (attrs === null)
|
|
2616
|
+
return -1;
|
|
2617
|
+
let i = 0;
|
|
2618
|
+
if (isProjectionMode || !isInlineTemplate) {
|
|
2619
|
+
let bindingsMode = false;
|
|
2620
|
+
while (i < attrs.length) {
|
|
2621
|
+
const maybeAttrName = attrs[i];
|
|
2622
|
+
if (maybeAttrName === name) {
|
|
2623
|
+
return i;
|
|
2624
|
+
}
|
|
2625
|
+
else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
|
|
2626
|
+
bindingsMode = true;
|
|
2627
|
+
}
|
|
2628
|
+
else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
|
|
2629
|
+
let value = attrs[++i];
|
|
2630
|
+
// We should skip classes here because we have a separate mechanism for
|
|
2631
|
+
// matching classes in projection mode.
|
|
2632
|
+
while (typeof value === 'string') {
|
|
2633
|
+
value = attrs[++i];
|
|
2634
|
+
}
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
|
|
2638
|
+
// We do not care about Template attributes in this scenario.
|
|
2639
|
+
break;
|
|
2640
|
+
}
|
|
2641
|
+
else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
|
|
2642
|
+
// Skip the whole namespaced attribute and value. This is by design.
|
|
2643
|
+
i += 4;
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
// In binding mode there are only names, rather than name-value pairs.
|
|
2647
|
+
i += bindingsMode ? 1 : 2;
|
|
2648
|
+
}
|
|
2649
|
+
// We did not match the attribute
|
|
2650
|
+
return -1;
|
|
2651
|
+
}
|
|
2652
|
+
else {
|
|
2653
|
+
return matchTemplateAttribute(attrs, name);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
|
|
2657
|
+
for (let i = 0; i < selector.length; i++) {
|
|
2658
|
+
if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
|
|
2659
|
+
return true;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return false;
|
|
2663
|
+
}
|
|
2664
|
+
function getProjectAsAttrValue(tNode) {
|
|
2665
|
+
const nodeAttrs = tNode.attrs;
|
|
2666
|
+
if (nodeAttrs != null) {
|
|
2667
|
+
const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
|
|
2668
|
+
// only check for ngProjectAs in attribute names, don't accidentally match attribute's value
|
|
2669
|
+
// (attribute names are stored at even indexes)
|
|
2670
|
+
if ((ngProjectAsAttrIdx & 1) === 0) {
|
|
2671
|
+
return nodeAttrs[ngProjectAsAttrIdx + 1];
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
return null;
|
|
2675
|
+
}
|
|
2676
|
+
function getNameOnlyMarkerIndex(nodeAttrs) {
|
|
2677
|
+
for (let i = 0; i < nodeAttrs.length; i++) {
|
|
2678
|
+
const nodeAttr = nodeAttrs[i];
|
|
2679
|
+
if (isNameOnlyAttributeMarker(nodeAttr)) {
|
|
2680
|
+
return i;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
return nodeAttrs.length;
|
|
2684
|
+
}
|
|
2685
|
+
function matchTemplateAttribute(attrs, name) {
|
|
2686
|
+
let i = attrs.indexOf(4 /* AttributeMarker.Template */);
|
|
2687
|
+
if (i > -1) {
|
|
2688
|
+
i++;
|
|
2689
|
+
while (i < attrs.length) {
|
|
2690
|
+
const attr = attrs[i];
|
|
2691
|
+
// Return in case we checked all template attrs and are switching to the next section in the
|
|
2692
|
+
// attrs array (that starts with a number that represents an attribute marker).
|
|
2693
|
+
if (typeof attr === 'number')
|
|
2694
|
+
return -1;
|
|
2695
|
+
if (attr === name)
|
|
2696
|
+
return i;
|
|
2697
|
+
i++;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return -1;
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Checks whether a selector is inside a CssSelectorList
|
|
2704
|
+
* @param selector Selector to be checked.
|
|
2705
|
+
* @param list List in which to look for the selector.
|
|
2706
|
+
*/
|
|
2707
|
+
function isSelectorInSelectorList(selector, list) {
|
|
2708
|
+
selectorListLoop: for (let i = 0; i < list.length; i++) {
|
|
2709
|
+
const currentSelectorInList = list[i];
|
|
2710
|
+
if (selector.length !== currentSelectorInList.length) {
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
for (let j = 0; j < selector.length; j++) {
|
|
2714
|
+
if (selector[j] !== currentSelectorInList[j]) {
|
|
2715
|
+
continue selectorListLoop;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
return true;
|
|
2719
|
+
}
|
|
2720
|
+
return false;
|
|
2721
|
+
}
|
|
2722
|
+
function maybeWrapInNotSelector(isNegativeMode, chunk) {
|
|
2723
|
+
return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
|
|
2724
|
+
}
|
|
2725
|
+
function stringifyCSSSelector(selector) {
|
|
2726
|
+
let result = selector[0];
|
|
2727
|
+
let i = 1;
|
|
2728
|
+
let mode = 2 /* SelectorFlags.ATTRIBUTE */;
|
|
2729
|
+
let currentChunk = '';
|
|
2730
|
+
let isNegativeMode = false;
|
|
2731
|
+
while (i < selector.length) {
|
|
2732
|
+
let valueOrMarker = selector[i];
|
|
2733
|
+
if (typeof valueOrMarker === 'string') {
|
|
2734
|
+
if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
|
|
2735
|
+
const attrValue = selector[++i];
|
|
2736
|
+
currentChunk +=
|
|
2737
|
+
'[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
|
|
2738
|
+
}
|
|
2739
|
+
else if (mode & 8 /* SelectorFlags.CLASS */) {
|
|
2740
|
+
currentChunk += '.' + valueOrMarker;
|
|
2741
|
+
}
|
|
2742
|
+
else if (mode & 4 /* SelectorFlags.ELEMENT */) {
|
|
2743
|
+
currentChunk += ' ' + valueOrMarker;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
else {
|
|
2747
|
+
//
|
|
2748
|
+
// Append current chunk to the final result in case we come across SelectorFlag, which
|
|
2749
|
+
// indicates that the previous section of a selector is over. We need to accumulate content
|
|
2750
|
+
// between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
|
|
2751
|
+
// ```
|
|
2752
|
+
// ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
|
|
2753
|
+
// ```
|
|
2754
|
+
// should be transformed to `.classA :not(.classB .classC)`.
|
|
2755
|
+
//
|
|
2756
|
+
// Note: for negative selector part, we accumulate content between flags until we find the
|
|
2757
|
+
// next negative flag. This is needed to support a case where `:not()` rule contains more than
|
|
2758
|
+
// one chunk, e.g. the following selector:
|
|
2759
|
+
// ```
|
|
2760
|
+
// ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
|
|
2761
|
+
// ```
|
|
2762
|
+
// should be stringified to `:not(p.foo) :not(.bar)`
|
|
2763
|
+
//
|
|
2764
|
+
if (currentChunk !== '' && !isPositive(valueOrMarker)) {
|
|
2765
|
+
result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
|
2766
|
+
currentChunk = '';
|
|
2767
|
+
}
|
|
2768
|
+
mode = valueOrMarker;
|
|
2769
|
+
// According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
|
2770
|
+
// mode is maintained for remaining chunks of a selector.
|
|
2771
|
+
isNegativeMode = isNegativeMode || !isPositive(mode);
|
|
2772
|
+
}
|
|
2773
|
+
i++;
|
|
2774
|
+
}
|
|
2775
|
+
if (currentChunk !== '') {
|
|
2776
|
+
result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
|
2777
|
+
}
|
|
2778
|
+
return result;
|
|
2779
|
+
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Generates string representation of CSS selector in parsed form.
|
|
2782
|
+
*
|
|
2783
|
+
* ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
|
|
2784
|
+
* additional parsing at runtime (for example, for directive matching). However in some cases (for
|
|
2785
|
+
* example, while bootstrapping a component), a string version of the selector is required to query
|
|
2786
|
+
* for the host element on the page. This function takes the parsed form of a selector and returns
|
|
2787
|
+
* its string representation.
|
|
2788
|
+
*
|
|
2789
|
+
* @param selectorList selector in parsed form
|
|
2790
|
+
* @returns string representation of a given selector
|
|
2791
|
+
*/
|
|
2792
|
+
function stringifyCSSSelectorList(selectorList) {
|
|
2793
|
+
return selectorList.map(stringifyCSSSelector).join(',');
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Extracts attributes and classes information from a given CSS selector.
|
|
2797
|
+
*
|
|
2798
|
+
* This function is used while creating a component dynamically. In this case, the host element
|
|
2799
|
+
* (that is created dynamically) should contain attributes and classes specified in component's CSS
|
|
2800
|
+
* selector.
|
|
2801
|
+
*
|
|
2802
|
+
* @param selector CSS selector in parsed form (in a form of array)
|
|
2803
|
+
* @returns object with `attrs` and `classes` fields that contain extracted information
|
|
2804
|
+
*/
|
|
2805
|
+
function extractAttrsAndClassesFromSelector(selector) {
|
|
2806
|
+
const attrs = [];
|
|
2807
|
+
const classes = [];
|
|
2808
|
+
let i = 1;
|
|
2809
|
+
let mode = 2 /* SelectorFlags.ATTRIBUTE */;
|
|
2810
|
+
while (i < selector.length) {
|
|
2811
|
+
let valueOrMarker = selector[i];
|
|
2812
|
+
if (typeof valueOrMarker === 'string') {
|
|
2813
|
+
if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
|
|
2814
|
+
if (valueOrMarker !== '') {
|
|
2815
|
+
attrs.push(valueOrMarker, selector[++i]);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
else if (mode === 8 /* SelectorFlags.CLASS */) {
|
|
2819
|
+
classes.push(valueOrMarker);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
else {
|
|
2823
|
+
// According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
|
2824
|
+
// mode is maintained for remaining chunks of a selector. Since attributes and classes are
|
|
2825
|
+
// extracted only for "positive" part of the selector, we can stop here.
|
|
2826
|
+
if (!isPositive(mode))
|
|
2827
|
+
break;
|
|
2828
|
+
mode = valueOrMarker;
|
|
2829
|
+
}
|
|
2830
|
+
i++;
|
|
2831
|
+
}
|
|
2832
|
+
return { attrs, classes };
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
/**
|
|
2836
|
+
* Create a component definition object.
|
|
2837
|
+
*
|
|
2838
|
+
*
|
|
2839
|
+
* # Example
|
|
2840
|
+
* ```
|
|
2841
|
+
* class MyComponent {
|
|
2842
|
+
* // Generated by Angular Template Compiler
|
|
2843
|
+
* // [Symbol] syntax will not be supported by TypeScript until v2.7
|
|
2844
|
+
* static ɵcmp = defineComponent({
|
|
2845
|
+
* ...
|
|
2846
|
+
* });
|
|
2847
|
+
* }
|
|
2848
|
+
* ```
|
|
2849
|
+
* @codeGenApi
|
|
2850
|
+
*/
|
|
2851
|
+
function ɵɵdefineComponent(componentDefinition) {
|
|
2852
|
+
return noSideEffects(() => {
|
|
2853
|
+
// Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
|
|
2854
|
+
// See the `initNgDevMode` docstring for more information.
|
|
2855
|
+
(typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
|
|
2856
|
+
const baseDef = getNgDirectiveDef(componentDefinition);
|
|
2857
|
+
const def = {
|
|
2858
|
+
...baseDef,
|
|
2859
|
+
decls: componentDefinition.decls,
|
|
2860
|
+
vars: componentDefinition.vars,
|
|
2861
|
+
template: componentDefinition.template,
|
|
2862
|
+
consts: componentDefinition.consts || null,
|
|
2863
|
+
ngContentSelectors: componentDefinition.ngContentSelectors,
|
|
2864
|
+
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
|
|
2865
|
+
directiveDefs: null,
|
|
2866
|
+
pipeDefs: null,
|
|
2867
|
+
dependencies: baseDef.standalone && componentDefinition.dependencies || null,
|
|
2868
|
+
getStandaloneInjector: null,
|
|
2869
|
+
data: componentDefinition.data || {},
|
|
2870
|
+
encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
|
|
2871
|
+
styles: componentDefinition.styles || EMPTY_ARRAY,
|
|
2872
|
+
_: null,
|
|
2873
|
+
schemas: componentDefinition.schemas || null,
|
|
2874
|
+
tView: null,
|
|
2875
|
+
id: '',
|
|
2876
|
+
};
|
|
2877
|
+
initFeatures(def);
|
|
2878
|
+
const dependencies = componentDefinition.dependencies;
|
|
2879
|
+
def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false);
|
|
2880
|
+
def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true);
|
|
2881
|
+
def.id = getComponentId(def);
|
|
2882
|
+
return def;
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Generated next to NgModules to monkey-patch directive and pipe references onto a component's
|
|
2887
|
+
* definition, when generating a direct reference in the component file would otherwise create an
|
|
2888
|
+
* import cycle.
|
|
2889
|
+
*
|
|
2890
|
+
* See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
|
|
2891
|
+
*
|
|
2892
|
+
* @codeGenApi
|
|
2893
|
+
*/
|
|
2894
|
+
function ɵɵsetComponentScope(type, directives, pipes) {
|
|
2895
|
+
const def = type.ɵcmp;
|
|
2896
|
+
def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
|
|
2897
|
+
def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
|
|
2898
|
+
}
|
|
2899
|
+
function extractDirectiveDef(type) {
|
|
2900
|
+
return getComponentDef$1(type) || getDirectiveDef(type);
|
|
2901
|
+
}
|
|
2902
|
+
function nonNull(value) {
|
|
2903
|
+
return value !== null;
|
|
2904
|
+
}
|
|
2905
|
+
/**
|
|
2906
|
+
* @codeGenApi
|
|
2907
|
+
*/
|
|
2908
|
+
function ɵɵdefineNgModule(def) {
|
|
2909
|
+
return noSideEffects(() => {
|
|
2910
|
+
const res = {
|
|
2911
|
+
type: def.type,
|
|
2912
|
+
bootstrap: def.bootstrap || EMPTY_ARRAY,
|
|
2913
|
+
declarations: def.declarations || EMPTY_ARRAY,
|
|
2914
|
+
imports: def.imports || EMPTY_ARRAY,
|
|
2915
|
+
exports: def.exports || EMPTY_ARRAY,
|
|
2916
|
+
transitiveCompileScopes: null,
|
|
2917
|
+
schemas: def.schemas || null,
|
|
2918
|
+
id: def.id || null,
|
|
2919
|
+
};
|
|
2920
|
+
return res;
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* Adds the module metadata that is necessary to compute the module's transitive scope to an
|
|
2925
|
+
* existing module definition.
|
|
2926
|
+
*
|
|
2927
|
+
* Scope metadata of modules is not used in production builds, so calls to this function can be
|
|
2928
|
+
* marked pure to tree-shake it from the bundle, allowing for all referenced declarations
|
|
2929
|
+
* to become eligible for tree-shaking as well.
|
|
2930
|
+
*
|
|
2931
|
+
* @codeGenApi
|
|
2932
|
+
*/
|
|
2933
|
+
function ɵɵsetNgModuleScope(type, scope) {
|
|
2934
|
+
return noSideEffects(() => {
|
|
2935
|
+
const ngModuleDef = getNgModuleDef(type, true);
|
|
2936
|
+
ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
|
|
2937
|
+
ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
|
|
2938
|
+
ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Inverts an inputs or outputs lookup such that the keys, which were the
|
|
2943
|
+
* minified keys, are part of the values, and the values are parsed so that
|
|
2944
|
+
* the publicName of the property is the new key
|
|
2945
|
+
*
|
|
2946
|
+
* e.g. for
|
|
2947
|
+
*
|
|
2948
|
+
* ```
|
|
2949
|
+
* class Comp {
|
|
2950
|
+
* @Input()
|
|
2951
|
+
* propName1: string;
|
|
2952
|
+
*
|
|
2953
|
+
* @Input('publicName2')
|
|
2954
|
+
* declaredPropName2: number;
|
|
2955
|
+
* }
|
|
2956
|
+
* ```
|
|
2957
|
+
*
|
|
2958
|
+
* will be serialized as
|
|
2959
|
+
*
|
|
2960
|
+
* ```
|
|
2961
|
+
* {
|
|
2962
|
+
* propName1: 'propName1',
|
|
2963
|
+
* declaredPropName2: ['publicName2', 'declaredPropName2'],
|
|
2964
|
+
* }
|
|
2965
|
+
* ```
|
|
2966
|
+
*
|
|
2967
|
+
* which is than translated by the minifier as:
|
|
2968
|
+
*
|
|
2969
|
+
* ```
|
|
2970
|
+
* {
|
|
2971
|
+
* minifiedPropName1: 'propName1',
|
|
2972
|
+
* minifiedPropName2: ['publicName2', 'declaredPropName2'],
|
|
2973
|
+
* }
|
|
2974
|
+
* ```
|
|
2975
|
+
*
|
|
2976
|
+
* becomes: (public name => minifiedName)
|
|
2977
|
+
*
|
|
2978
|
+
* ```
|
|
2979
|
+
* {
|
|
2980
|
+
* 'propName1': 'minifiedPropName1',
|
|
2981
|
+
* 'publicName2': 'minifiedPropName2',
|
|
2982
|
+
* }
|
|
2983
|
+
* ```
|
|
2984
|
+
*
|
|
2985
|
+
* Optionally the function can take `secondary` which will result in: (public name => declared name)
|
|
2986
|
+
*
|
|
2987
|
+
* ```
|
|
2988
|
+
* {
|
|
2989
|
+
* 'propName1': 'propName1',
|
|
2990
|
+
* 'publicName2': 'declaredPropName2',
|
|
2991
|
+
* }
|
|
2992
|
+
* ```
|
|
2993
|
+
*
|
|
2994
|
+
|
|
2995
|
+
*/
|
|
2996
|
+
function invertObject(obj, secondary) {
|
|
2997
|
+
if (obj == null)
|
|
2998
|
+
return EMPTY_OBJ;
|
|
2999
|
+
const newLookup = {};
|
|
3000
|
+
for (const minifiedKey in obj) {
|
|
3001
|
+
if (obj.hasOwnProperty(minifiedKey)) {
|
|
3002
|
+
let publicName = obj[minifiedKey];
|
|
3003
|
+
let declaredName = publicName;
|
|
3004
|
+
if (Array.isArray(publicName)) {
|
|
3005
|
+
declaredName = publicName[1];
|
|
3006
|
+
publicName = publicName[0];
|
|
3007
|
+
}
|
|
3008
|
+
newLookup[publicName] = minifiedKey;
|
|
3009
|
+
if (secondary) {
|
|
3010
|
+
(secondary[publicName] = declaredName);
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
return newLookup;
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Create a directive definition object.
|
|
3018
|
+
*
|
|
3019
|
+
* # Example
|
|
3020
|
+
* ```ts
|
|
3021
|
+
* class MyDirective {
|
|
3022
|
+
* // Generated by Angular Template Compiler
|
|
3023
|
+
* // [Symbol] syntax will not be supported by TypeScript until v2.7
|
|
3024
|
+
* static ɵdir = ɵɵdefineDirective({
|
|
3025
|
+
* ...
|
|
3026
|
+
* });
|
|
3027
|
+
* }
|
|
3028
|
+
* ```
|
|
3029
|
+
*
|
|
3030
|
+
* @codeGenApi
|
|
3031
|
+
*/
|
|
3032
|
+
function ɵɵdefineDirective(directiveDefinition) {
|
|
3033
|
+
return noSideEffects(() => {
|
|
3034
|
+
const def = getNgDirectiveDef(directiveDefinition);
|
|
3035
|
+
initFeatures(def);
|
|
3036
|
+
return def;
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Create a pipe definition object.
|
|
3041
|
+
*
|
|
3042
|
+
* # Example
|
|
3043
|
+
* ```
|
|
3044
|
+
* class MyPipe implements PipeTransform {
|
|
3045
|
+
* // Generated by Angular Template Compiler
|
|
3046
|
+
* static ɵpipe = definePipe({
|
|
3047
|
+
* ...
|
|
3048
|
+
* });
|
|
3049
|
+
* }
|
|
3050
|
+
* ```
|
|
3051
|
+
* @param pipeDef Pipe definition generated by the compiler
|
|
3052
|
+
*
|
|
3053
|
+
* @codeGenApi
|
|
3054
|
+
*/
|
|
3055
|
+
function ɵɵdefinePipe(pipeDef) {
|
|
3056
|
+
return {
|
|
3057
|
+
type: pipeDef.type,
|
|
3058
|
+
name: pipeDef.name,
|
|
3059
|
+
factory: null,
|
|
3060
|
+
pure: pipeDef.pure !== false,
|
|
3061
|
+
standalone: pipeDef.standalone === true,
|
|
3062
|
+
onDestroy: pipeDef.type.prototype.ngOnDestroy || null
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* The following getter methods retrieve the definition from the type. Currently the retrieval
|
|
3067
|
+
* honors inheritance, but in the future we may change the rule to require that definitions are
|
|
3068
|
+
* explicit. This would require some sort of migration strategy.
|
|
3069
|
+
*/
|
|
3070
|
+
function getComponentDef$1(type) {
|
|
3071
|
+
return type[NG_COMP_DEF] || null;
|
|
3072
|
+
}
|
|
3073
|
+
function getDirectiveDef(type) {
|
|
3074
|
+
return type[NG_DIR_DEF] || null;
|
|
3075
|
+
}
|
|
3076
|
+
function getPipeDef$1(type) {
|
|
3077
|
+
return type[NG_PIPE_DEF] || null;
|
|
3078
|
+
}
|
|
3079
|
+
/**
|
|
3080
|
+
* Checks whether a given Component, Directive or Pipe is marked as standalone.
|
|
3081
|
+
* This will return false if passed anything other than a Component, Directive, or Pipe class
|
|
3082
|
+
* See this guide for additional information: https://angular.io/guide/standalone-components
|
|
3083
|
+
*
|
|
3084
|
+
* @param type A reference to a Component, Directive or Pipe.
|
|
3085
|
+
* @publicApi
|
|
3086
|
+
*/
|
|
3087
|
+
function isStandalone(type) {
|
|
3088
|
+
const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
|
|
3089
|
+
return def !== null ? def.standalone : false;
|
|
3090
|
+
}
|
|
3091
|
+
function getNgModuleDef(type, throwNotFound) {
|
|
3092
|
+
const ngModuleDef = type[NG_MOD_DEF] || null;
|
|
3093
|
+
if (!ngModuleDef && throwNotFound === true) {
|
|
3094
|
+
throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
|
|
3095
|
+
}
|
|
3096
|
+
return ngModuleDef;
|
|
3097
|
+
}
|
|
3098
|
+
function getNgDirectiveDef(directiveDefinition) {
|
|
3099
|
+
const declaredInputs = {};
|
|
3100
|
+
return {
|
|
3101
|
+
type: directiveDefinition.type,
|
|
3102
|
+
providersResolver: null,
|
|
3103
|
+
factory: null,
|
|
3104
|
+
hostBindings: directiveDefinition.hostBindings || null,
|
|
3105
|
+
hostVars: directiveDefinition.hostVars || 0,
|
|
3106
|
+
hostAttrs: directiveDefinition.hostAttrs || null,
|
|
3107
|
+
contentQueries: directiveDefinition.contentQueries || null,
|
|
3108
|
+
declaredInputs,
|
|
3109
|
+
exportAs: directiveDefinition.exportAs || null,
|
|
3110
|
+
standalone: directiveDefinition.standalone === true,
|
|
3111
|
+
selectors: directiveDefinition.selectors || EMPTY_ARRAY,
|
|
3112
|
+
viewQuery: directiveDefinition.viewQuery || null,
|
|
3113
|
+
features: directiveDefinition.features || null,
|
|
3114
|
+
setInput: null,
|
|
3115
|
+
findHostDirectiveDefs: null,
|
|
3116
|
+
hostDirectives: null,
|
|
3117
|
+
inputs: invertObject(directiveDefinition.inputs, declaredInputs),
|
|
3118
|
+
outputs: invertObject(directiveDefinition.outputs),
|
|
3119
|
+
};
|
|
3120
|
+
}
|
|
3121
|
+
function initFeatures(definition) {
|
|
3122
|
+
definition.features?.forEach((fn) => fn(definition));
|
|
3123
|
+
}
|
|
3124
|
+
function extractDefListOrFactory(dependencies, pipeDef) {
|
|
3125
|
+
if (!dependencies) {
|
|
3126
|
+
return null;
|
|
3127
|
+
}
|
|
3128
|
+
const defExtractor = pipeDef ? getPipeDef$1 : extractDirectiveDef;
|
|
3129
|
+
return () => (typeof dependencies === 'function' ? dependencies() : dependencies)
|
|
3130
|
+
.map(dep => defExtractor(dep))
|
|
3131
|
+
.filter(nonNull);
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* A map that contains the generated component IDs and type.
|
|
3135
|
+
*/
|
|
3136
|
+
const GENERATED_COMP_IDS = new Map();
|
|
3137
|
+
/**
|
|
3138
|
+
* A method can returns a component ID from the component definition using a variant of DJB2 hash
|
|
3139
|
+
* algorithm.
|
|
3140
|
+
*/
|
|
3141
|
+
function getComponentId(componentDef) {
|
|
3142
|
+
let hash = 0;
|
|
3143
|
+
// We cannot rely solely on the component selector as the same selector can be used in different
|
|
3144
|
+
// modules.
|
|
3145
|
+
//
|
|
3146
|
+
// `componentDef.style` is not used, due to it causing inconsistencies. Ex: when server
|
|
3147
|
+
// component styles has no sourcemaps and browsers do.
|
|
3148
|
+
//
|
|
3149
|
+
// Example:
|
|
3150
|
+
// https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248
|
|
3151
|
+
// https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32
|
|
3152
|
+
const hashSelectors = [
|
|
3153
|
+
componentDef.selectors,
|
|
3154
|
+
componentDef.ngContentSelectors,
|
|
3155
|
+
componentDef.hostVars,
|
|
3156
|
+
componentDef.hostAttrs,
|
|
3157
|
+
componentDef.consts,
|
|
3158
|
+
componentDef.vars,
|
|
3159
|
+
componentDef.decls,
|
|
3160
|
+
componentDef.encapsulation,
|
|
3161
|
+
componentDef.standalone,
|
|
3162
|
+
// We cannot use 'componentDef.type.name' as the name of the symbol will change and will not
|
|
3163
|
+
// match in the server and browser bundles.
|
|
3164
|
+
Object.getOwnPropertyNames(componentDef.type.prototype),
|
|
3165
|
+
!!componentDef.contentQueries,
|
|
3166
|
+
!!componentDef.viewQuery,
|
|
3167
|
+
].join('|');
|
|
3168
|
+
for (const char of hashSelectors) {
|
|
3169
|
+
hash = Math.imul(31, hash) + char.charCodeAt(0) << 0;
|
|
3170
|
+
}
|
|
3171
|
+
// Force positive number hash.
|
|
3172
|
+
// 2147483647 = equivalent of Integer.MAX_VALUE.
|
|
3173
|
+
hash += 2147483647 + 1;
|
|
3174
|
+
const compId = 'c' + hash;
|
|
3175
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
3176
|
+
if (GENERATED_COMP_IDS.has(compId)) {
|
|
3177
|
+
const previousCompDefType = GENERATED_COMP_IDS.get(compId);
|
|
3178
|
+
if (previousCompDefType !== componentDef.type) {
|
|
3179
|
+
// TODO: use `formatRuntimeError` to have an error code and we can later on create an error
|
|
3180
|
+
// guide to explain this further.
|
|
3181
|
+
console.warn(`Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
|
|
3182
|
+
.selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
else {
|
|
3186
|
+
GENERATED_COMP_IDS.set(compId, componentDef.type);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
return compId;
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
// Below are constants for LView indices to help us look up LView members
|
|
3193
|
+
// without having to remember the specific indices.
|
|
3194
|
+
// Uglify will inline these when minifying so there shouldn't be a cost.
|
|
3195
|
+
const HOST = 0;
|
|
3196
|
+
const TVIEW = 1;
|
|
3197
|
+
const FLAGS = 2;
|
|
3198
|
+
const PARENT = 3;
|
|
3199
|
+
const NEXT = 4;
|
|
3200
|
+
const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
|
|
3201
|
+
const T_HOST = 6;
|
|
3202
|
+
const CLEANUP = 7;
|
|
3203
|
+
const CONTEXT = 8;
|
|
3204
|
+
const INJECTOR$1 = 9;
|
|
3205
|
+
const RENDERER_FACTORY = 10;
|
|
3206
|
+
const RENDERER = 11;
|
|
3207
|
+
const SANITIZER = 12;
|
|
3208
|
+
const CHILD_HEAD = 13;
|
|
3209
|
+
const CHILD_TAIL = 14;
|
|
3210
|
+
// FIXME(misko): Investigate if the three declarations aren't all same thing.
|
|
3211
|
+
const DECLARATION_VIEW = 15;
|
|
3212
|
+
const DECLARATION_COMPONENT_VIEW = 16;
|
|
3213
|
+
const DECLARATION_LCONTAINER = 17;
|
|
3214
|
+
const PREORDER_HOOK_FLAGS = 18;
|
|
3215
|
+
const QUERIES = 19;
|
|
3216
|
+
const ID = 20;
|
|
3217
|
+
const EMBEDDED_VIEW_INJECTOR = 21;
|
|
3218
|
+
const ON_DESTROY_HOOKS = 22;
|
|
3219
|
+
const HYDRATION = 23;
|
|
3220
|
+
/**
|
|
3221
|
+
* Size of LView's header. Necessary to adjust for it when setting slots.
|
|
3222
|
+
*
|
|
3223
|
+
* IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
|
|
2566
3224
|
* instruction index into `LView` index. All other indexes should be in the `LView` index space and
|
|
2567
3225
|
* there should be no need to refer to `HEADER_OFFSET` anywhere else.
|
|
2568
3226
|
*/
|
|
2569
|
-
const HEADER_OFFSET =
|
|
3227
|
+
const HEADER_OFFSET = 24;
|
|
2570
3228
|
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
|
2571
3229
|
// failure based on types.
|
|
2572
3230
|
const unusedValueExportToPlacateAjd$4 = 1;
|
|
@@ -2598,13 +3256,14 @@ const HAS_TRANSPLANTED_VIEWS = 2;
|
|
|
2598
3256
|
const NATIVE = 7;
|
|
2599
3257
|
const VIEW_REFS = 8;
|
|
2600
3258
|
const MOVED_VIEWS = 9;
|
|
3259
|
+
const DEHYDRATED_VIEWS = 10;
|
|
2601
3260
|
/**
|
|
2602
3261
|
* Size of LContainer's header. Represents the index after which all views in the
|
|
2603
3262
|
* container will be inserted. We need to keep a record of current views so we know
|
|
2604
3263
|
* which views are already in the DOM (and don't need to be re-added) and so we can
|
|
2605
3264
|
* remove views from the DOM when they are no longer required.
|
|
2606
3265
|
*/
|
|
2607
|
-
const CONTAINER_HEADER_OFFSET =
|
|
3266
|
+
const CONTAINER_HEADER_OFFSET = 11;
|
|
2608
3267
|
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
|
2609
3268
|
// failure based on types.
|
|
2610
3269
|
const unusedValueExportToPlacateAjd$3 = 1;
|
|
@@ -2633,7 +3292,7 @@ function isDirectiveHost(tNode) {
|
|
|
2633
3292
|
return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
|
|
2634
3293
|
}
|
|
2635
3294
|
function isComponentDef(def) {
|
|
2636
|
-
return def.template
|
|
3295
|
+
return !!def.template;
|
|
2637
3296
|
}
|
|
2638
3297
|
function isRootView(target) {
|
|
2639
3298
|
return (target[FLAGS] & 256 /* LViewFlags.IsRoot */) !== 0;
|
|
@@ -3055,6 +3714,7 @@ function storeLViewOnDestroy(lView, onDestroyCallback) {
|
|
|
3055
3714
|
const instructionState = {
|
|
3056
3715
|
lFrame: createLFrame(null),
|
|
3057
3716
|
bindingsEnabled: true,
|
|
3717
|
+
skipHydrationRootTNode: null,
|
|
3058
3718
|
};
|
|
3059
3719
|
/**
|
|
3060
3720
|
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
|
|
@@ -3085,6 +3745,21 @@ function decreaseElementDepthCount() {
|
|
|
3085
3745
|
function getBindingsEnabled() {
|
|
3086
3746
|
return instructionState.bindingsEnabled;
|
|
3087
3747
|
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Returns true if currently inside a skip hydration block.
|
|
3750
|
+
* @returns boolean
|
|
3751
|
+
*/
|
|
3752
|
+
function isInSkipHydrationBlock$1() {
|
|
3753
|
+
return instructionState.skipHydrationRootTNode !== null;
|
|
3754
|
+
}
|
|
3755
|
+
/**
|
|
3756
|
+
* Returns true if this is the root TNode of the skip hydration block.
|
|
3757
|
+
* @param tNode the current TNode
|
|
3758
|
+
* @returns boolean
|
|
3759
|
+
*/
|
|
3760
|
+
function isSkipHydrationRootTNode(tNode) {
|
|
3761
|
+
return instructionState.skipHydrationRootTNode === tNode;
|
|
3762
|
+
}
|
|
3088
3763
|
/**
|
|
3089
3764
|
* Enables directive matching on elements.
|
|
3090
3765
|
*
|
|
@@ -3107,6 +3782,13 @@ function getBindingsEnabled() {
|
|
|
3107
3782
|
function ɵɵenableBindings() {
|
|
3108
3783
|
instructionState.bindingsEnabled = true;
|
|
3109
3784
|
}
|
|
3785
|
+
/**
|
|
3786
|
+
* Sets a flag to specify that the TNode is in a skip hydration block.
|
|
3787
|
+
* @param tNode the current TNode
|
|
3788
|
+
*/
|
|
3789
|
+
function enterSkipHydrationBlock(tNode) {
|
|
3790
|
+
instructionState.skipHydrationRootTNode = tNode;
|
|
3791
|
+
}
|
|
3110
3792
|
/**
|
|
3111
3793
|
* Disables directive matching on element.
|
|
3112
3794
|
*
|
|
@@ -3129,6 +3811,12 @@ function ɵɵenableBindings() {
|
|
|
3129
3811
|
function ɵɵdisableBindings() {
|
|
3130
3812
|
instructionState.bindingsEnabled = false;
|
|
3131
3813
|
}
|
|
3814
|
+
/**
|
|
3815
|
+
* Clears the root skip hydration node when leaving a skip hydration block.
|
|
3816
|
+
*/
|
|
3817
|
+
function leaveSkipHydrationBlock() {
|
|
3818
|
+
instructionState.skipHydrationRootTNode = null;
|
|
3819
|
+
}
|
|
3132
3820
|
/**
|
|
3133
3821
|
* Return the current `LView`.
|
|
3134
3822
|
*/
|
|
@@ -3553,6 +4241,21 @@ function namespaceHTMLInternal() {
|
|
|
3553
4241
|
function getNamespace$1() {
|
|
3554
4242
|
return instructionState.lFrame.currentNamespace;
|
|
3555
4243
|
}
|
|
4244
|
+
let _wasLastNodeCreated = true;
|
|
4245
|
+
/**
|
|
4246
|
+
* Retrieves a global flag that indicates whether the most recent DOM node
|
|
4247
|
+
* was created or hydrated.
|
|
4248
|
+
*/
|
|
4249
|
+
function wasLastNodeCreated() {
|
|
4250
|
+
return _wasLastNodeCreated;
|
|
4251
|
+
}
|
|
4252
|
+
/**
|
|
4253
|
+
* Sets a global flag to indicate whether the most recent DOM node
|
|
4254
|
+
* was created or hydrated.
|
|
4255
|
+
*/
|
|
4256
|
+
function lastNodeWasCreated(flag) {
|
|
4257
|
+
_wasLastNodeCreated = flag;
|
|
4258
|
+
}
|
|
3556
4259
|
|
|
3557
4260
|
/**
|
|
3558
4261
|
* Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
|
|
@@ -3991,206 +4694,6 @@ function assertPureTNodeType(type) {
|
|
|
3991
4694
|
}
|
|
3992
4695
|
}
|
|
3993
4696
|
|
|
3994
|
-
/**
|
|
3995
|
-
* Assigns all attribute values to the provided element via the inferred renderer.
|
|
3996
|
-
*
|
|
3997
|
-
* This function accepts two forms of attribute entries:
|
|
3998
|
-
*
|
|
3999
|
-
* default: (key, value):
|
|
4000
|
-
* attrs = [key1, value1, key2, value2]
|
|
4001
|
-
*
|
|
4002
|
-
* namespaced: (NAMESPACE_MARKER, uri, name, value)
|
|
4003
|
-
* attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
|
|
4004
|
-
*
|
|
4005
|
-
* The `attrs` array can contain a mix of both the default and namespaced entries.
|
|
4006
|
-
* The "default" values are set without a marker, but if the function comes across
|
|
4007
|
-
* a marker value then it will attempt to set a namespaced value. If the marker is
|
|
4008
|
-
* not of a namespaced value then the function will quit and return the index value
|
|
4009
|
-
* where it stopped during the iteration of the attrs array.
|
|
4010
|
-
*
|
|
4011
|
-
* See [AttributeMarker] to understand what the namespace marker value is.
|
|
4012
|
-
*
|
|
4013
|
-
* Note that this instruction does not support assigning style and class values to
|
|
4014
|
-
* an element. See `elementStart` and `elementHostAttrs` to learn how styling values
|
|
4015
|
-
* are applied to an element.
|
|
4016
|
-
* @param renderer The renderer to be used
|
|
4017
|
-
* @param native The element that the attributes will be assigned to
|
|
4018
|
-
* @param attrs The attribute array of values that will be assigned to the element
|
|
4019
|
-
* @returns the index value that was last accessed in the attributes array
|
|
4020
|
-
*/
|
|
4021
|
-
function setUpAttributes(renderer, native, attrs) {
|
|
4022
|
-
let i = 0;
|
|
4023
|
-
while (i < attrs.length) {
|
|
4024
|
-
const value = attrs[i];
|
|
4025
|
-
if (typeof value === 'number') {
|
|
4026
|
-
// only namespaces are supported. Other value types (such as style/class
|
|
4027
|
-
// entries) are not supported in this function.
|
|
4028
|
-
if (value !== 0 /* AttributeMarker.NamespaceURI */) {
|
|
4029
|
-
break;
|
|
4030
|
-
}
|
|
4031
|
-
// we just landed on the marker value ... therefore
|
|
4032
|
-
// we should skip to the next entry
|
|
4033
|
-
i++;
|
|
4034
|
-
const namespaceURI = attrs[i++];
|
|
4035
|
-
const attrName = attrs[i++];
|
|
4036
|
-
const attrVal = attrs[i++];
|
|
4037
|
-
ngDevMode && ngDevMode.rendererSetAttribute++;
|
|
4038
|
-
renderer.setAttribute(native, attrName, attrVal, namespaceURI);
|
|
4039
|
-
}
|
|
4040
|
-
else {
|
|
4041
|
-
// attrName is string;
|
|
4042
|
-
const attrName = value;
|
|
4043
|
-
const attrVal = attrs[++i];
|
|
4044
|
-
// Standard attributes
|
|
4045
|
-
ngDevMode && ngDevMode.rendererSetAttribute++;
|
|
4046
|
-
if (isAnimationProp(attrName)) {
|
|
4047
|
-
renderer.setProperty(native, attrName, attrVal);
|
|
4048
|
-
}
|
|
4049
|
-
else {
|
|
4050
|
-
renderer.setAttribute(native, attrName, attrVal);
|
|
4051
|
-
}
|
|
4052
|
-
i++;
|
|
4053
|
-
}
|
|
4054
|
-
}
|
|
4055
|
-
// another piece of code may iterate over the same attributes array. Therefore
|
|
4056
|
-
// it may be helpful to return the exact spot where the attributes array exited
|
|
4057
|
-
// whether by running into an unsupported marker or if all the static values were
|
|
4058
|
-
// iterated over.
|
|
4059
|
-
return i;
|
|
4060
|
-
}
|
|
4061
|
-
/**
|
|
4062
|
-
* Test whether the given value is a marker that indicates that the following
|
|
4063
|
-
* attribute values in a `TAttributes` array are only the names of attributes,
|
|
4064
|
-
* and not name-value pairs.
|
|
4065
|
-
* @param marker The attribute marker to test.
|
|
4066
|
-
* @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
|
|
4067
|
-
*/
|
|
4068
|
-
function isNameOnlyAttributeMarker(marker) {
|
|
4069
|
-
return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
|
|
4070
|
-
marker === 6 /* AttributeMarker.I18n */;
|
|
4071
|
-
}
|
|
4072
|
-
function isAnimationProp(name) {
|
|
4073
|
-
// Perf note: accessing charCodeAt to check for the first character of a string is faster as
|
|
4074
|
-
// compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
|
|
4075
|
-
// charCodeAt doesn't allocate memory to return a substring.
|
|
4076
|
-
return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
|
|
4077
|
-
}
|
|
4078
|
-
/**
|
|
4079
|
-
* Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
|
|
4080
|
-
*
|
|
4081
|
-
* This merge function keeps the order of attrs same.
|
|
4082
|
-
*
|
|
4083
|
-
* @param dst Location of where the merged `TAttributes` should end up.
|
|
4084
|
-
* @param src `TAttributes` which should be appended to `dst`
|
|
4085
|
-
*/
|
|
4086
|
-
function mergeHostAttrs(dst, src) {
|
|
4087
|
-
if (src === null || src.length === 0) {
|
|
4088
|
-
// do nothing
|
|
4089
|
-
}
|
|
4090
|
-
else if (dst === null || dst.length === 0) {
|
|
4091
|
-
// We have source, but dst is empty, just make a copy.
|
|
4092
|
-
dst = src.slice();
|
|
4093
|
-
}
|
|
4094
|
-
else {
|
|
4095
|
-
let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
|
|
4096
|
-
for (let i = 0; i < src.length; i++) {
|
|
4097
|
-
const item = src[i];
|
|
4098
|
-
if (typeof item === 'number') {
|
|
4099
|
-
srcMarker = item;
|
|
4100
|
-
}
|
|
4101
|
-
else {
|
|
4102
|
-
if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
|
|
4103
|
-
// Case where we need to consume `key1`, `key2`, `value` items.
|
|
4104
|
-
}
|
|
4105
|
-
else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
|
|
4106
|
-
srcMarker === 2 /* AttributeMarker.Styles */) {
|
|
4107
|
-
// Case where we have to consume `key1` and `value` only.
|
|
4108
|
-
mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
|
|
4109
|
-
}
|
|
4110
|
-
else {
|
|
4111
|
-
// Case where we have to consume `key1` only.
|
|
4112
|
-
mergeHostAttribute(dst, srcMarker, item, null, null);
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
return dst;
|
|
4118
|
-
}
|
|
4119
|
-
/**
|
|
4120
|
-
* Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
|
|
4121
|
-
*
|
|
4122
|
-
* @param dst `TAttributes` to append to.
|
|
4123
|
-
* @param marker Region where the `key`/`value` should be added.
|
|
4124
|
-
* @param key1 Key to add to `TAttributes`
|
|
4125
|
-
* @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
|
|
4126
|
-
* @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
|
|
4127
|
-
*/
|
|
4128
|
-
function mergeHostAttribute(dst, marker, key1, key2, value) {
|
|
4129
|
-
let i = 0;
|
|
4130
|
-
// Assume that new markers will be inserted at the end.
|
|
4131
|
-
let markerInsertPosition = dst.length;
|
|
4132
|
-
// scan until correct type.
|
|
4133
|
-
if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
|
|
4134
|
-
markerInsertPosition = -1;
|
|
4135
|
-
}
|
|
4136
|
-
else {
|
|
4137
|
-
while (i < dst.length) {
|
|
4138
|
-
const dstValue = dst[i++];
|
|
4139
|
-
if (typeof dstValue === 'number') {
|
|
4140
|
-
if (dstValue === marker) {
|
|
4141
|
-
markerInsertPosition = -1;
|
|
4142
|
-
break;
|
|
4143
|
-
}
|
|
4144
|
-
else if (dstValue > marker) {
|
|
4145
|
-
// We need to save this as we want the markers to be inserted in specific order.
|
|
4146
|
-
markerInsertPosition = i - 1;
|
|
4147
|
-
break;
|
|
4148
|
-
}
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
}
|
|
4152
|
-
// search until you find place of insertion
|
|
4153
|
-
while (i < dst.length) {
|
|
4154
|
-
const item = dst[i];
|
|
4155
|
-
if (typeof item === 'number') {
|
|
4156
|
-
// since `i` started as the index after the marker, we did not find it if we are at the next
|
|
4157
|
-
// marker
|
|
4158
|
-
break;
|
|
4159
|
-
}
|
|
4160
|
-
else if (item === key1) {
|
|
4161
|
-
// We already have same token
|
|
4162
|
-
if (key2 === null) {
|
|
4163
|
-
if (value !== null) {
|
|
4164
|
-
dst[i + 1] = value;
|
|
4165
|
-
}
|
|
4166
|
-
return;
|
|
4167
|
-
}
|
|
4168
|
-
else if (key2 === dst[i + 1]) {
|
|
4169
|
-
dst[i + 2] = value;
|
|
4170
|
-
return;
|
|
4171
|
-
}
|
|
4172
|
-
}
|
|
4173
|
-
// Increment counter.
|
|
4174
|
-
i++;
|
|
4175
|
-
if (key2 !== null)
|
|
4176
|
-
i++;
|
|
4177
|
-
if (value !== null)
|
|
4178
|
-
i++;
|
|
4179
|
-
}
|
|
4180
|
-
// insert at location.
|
|
4181
|
-
if (markerInsertPosition !== -1) {
|
|
4182
|
-
dst.splice(markerInsertPosition, 0, marker);
|
|
4183
|
-
i = markerInsertPosition + 1;
|
|
4184
|
-
}
|
|
4185
|
-
dst.splice(i++, 0, key1);
|
|
4186
|
-
if (key2 !== null) {
|
|
4187
|
-
dst.splice(i++, 0, key2);
|
|
4188
|
-
}
|
|
4189
|
-
if (value !== null) {
|
|
4190
|
-
dst.splice(i++, 0, value);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
|
|
4194
4697
|
/// Parent Injector Utils ///////////////////////////////////////////////////////////////
|
|
4195
4698
|
function hasParentInjector(parentLocation) {
|
|
4196
4699
|
return parentLocation !== NO_PARENT_INJECTOR;
|
|
@@ -6607,6 +7110,17 @@ function nativeRemoveNode(renderer, rNode, isHostElement) {
|
|
|
6607
7110
|
nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
|
|
6608
7111
|
}
|
|
6609
7112
|
}
|
|
7113
|
+
/**
|
|
7114
|
+
* Removes the contents of a given RElement using a given renderer.
|
|
7115
|
+
*
|
|
7116
|
+
* @param renderer A renderer to be used
|
|
7117
|
+
* @param rElement the native RElement to be cleared
|
|
7118
|
+
*/
|
|
7119
|
+
function clearElementContents(renderer, rElement) {
|
|
7120
|
+
while (rElement.firstChild) {
|
|
7121
|
+
nativeRemoveChild(renderer, rElement, rElement.firstChild, false);
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
6610
7124
|
/**
|
|
6611
7125
|
* Performs the operation of `action` on the node. Typically this involves inserting or removing
|
|
6612
7126
|
* nodes on the LView or projection boundary.
|
|
@@ -8587,802 +9101,794 @@ function forEachSingleProvider(providers, fn) {
|
|
|
8587
9101
|
}
|
|
8588
9102
|
|
|
8589
9103
|
/**
|
|
8590
|
-
*
|
|
8591
|
-
*
|
|
8592
|
-
*
|
|
8593
|
-
*
|
|
8594
|
-
* @publicApi
|
|
8595
|
-
*/
|
|
8596
|
-
class ComponentRef$1 {
|
|
8597
|
-
}
|
|
8598
|
-
/**
|
|
8599
|
-
* Base class for a factory that can create a component dynamically.
|
|
8600
|
-
* Instantiate a factory for a given type of component with `resolveComponentFactory()`.
|
|
8601
|
-
* Use the resulting `ComponentFactory.create()` method to create a component of that type.
|
|
9104
|
+
* A [DI token](guide/glossary#di-token "DI token definition") representing a string ID, used
|
|
9105
|
+
* primarily for prefixing application attributes and CSS styles when
|
|
9106
|
+
* {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used.
|
|
8602
9107
|
*
|
|
8603
|
-
*
|
|
9108
|
+
* The token is needed in cases when multiple applications are bootstrapped on a page
|
|
9109
|
+
* (for example, using `bootstrapApplication` calls). In this case, ensure that those applications
|
|
9110
|
+
* have different `APP_ID` value setup. For example:
|
|
8604
9111
|
*
|
|
8605
|
-
*
|
|
9112
|
+
* ```
|
|
9113
|
+
* bootstrapApplication(ComponentA, {
|
|
9114
|
+
* providers: [
|
|
9115
|
+
* { provide: APP_ID, useValue: 'app-a' },
|
|
9116
|
+
* // ... other providers ...
|
|
9117
|
+
* ]
|
|
9118
|
+
* });
|
|
8606
9119
|
*
|
|
8607
|
-
*
|
|
8608
|
-
*
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
|
|
8615
|
-
error[ERROR_COMPONENT] = component;
|
|
8616
|
-
return error;
|
|
8617
|
-
}
|
|
8618
|
-
const ERROR_COMPONENT = 'ngComponent';
|
|
8619
|
-
function getComponent$1(error) {
|
|
8620
|
-
return error[ERROR_COMPONENT];
|
|
8621
|
-
}
|
|
8622
|
-
class _NullComponentFactoryResolver {
|
|
8623
|
-
resolveComponentFactory(component) {
|
|
8624
|
-
throw noComponentFactoryError(component);
|
|
8625
|
-
}
|
|
8626
|
-
}
|
|
8627
|
-
/**
|
|
8628
|
-
* A simple registry that maps `Components` to generated `ComponentFactory` classes
|
|
8629
|
-
* that can be used to create instances of components.
|
|
8630
|
-
* Use to obtain the factory for a given component type,
|
|
8631
|
-
* then use the factory's `create()` method to create a component of that type.
|
|
9120
|
+
* bootstrapApplication(ComponentB, {
|
|
9121
|
+
* providers: [
|
|
9122
|
+
* { provide: APP_ID, useValue: 'app-b' },
|
|
9123
|
+
* // ... other providers ...
|
|
9124
|
+
* ]
|
|
9125
|
+
* });
|
|
9126
|
+
* ```
|
|
8632
9127
|
*
|
|
8633
|
-
*
|
|
8634
|
-
*
|
|
8635
|
-
* does **not** require resolving component factory: component class can be used directly.
|
|
9128
|
+
* By default, when there is only one application bootstrapped, you don't need to provide the
|
|
9129
|
+
* `APP_ID` token (the `ng` will be used as an app ID).
|
|
8636
9130
|
*
|
|
8637
9131
|
* @publicApi
|
|
8638
|
-
*
|
|
8639
|
-
* @deprecated Angular no longer requires Component factories. Please use other APIs where
|
|
8640
|
-
* Component class can be used directly.
|
|
8641
|
-
*/
|
|
8642
|
-
class ComponentFactoryResolver$1 {
|
|
8643
|
-
}
|
|
8644
|
-
ComponentFactoryResolver$1.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver());
|
|
8645
|
-
|
|
8646
|
-
/**
|
|
8647
|
-
* Creates an ElementRef from the most recent node.
|
|
8648
|
-
*
|
|
8649
|
-
* @returns The ElementRef instance to use
|
|
8650
|
-
*/
|
|
8651
|
-
function injectElementRef() {
|
|
8652
|
-
return createElementRef(getCurrentTNode(), getLView());
|
|
8653
|
-
}
|
|
8654
|
-
/**
|
|
8655
|
-
* Creates an ElementRef given a node.
|
|
8656
|
-
*
|
|
8657
|
-
* @param tNode The node for which you'd like an ElementRef
|
|
8658
|
-
* @param lView The view to which the node belongs
|
|
8659
|
-
* @returns The ElementRef instance to use
|
|
8660
9132
|
*/
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
9133
|
+
const APP_ID = new InjectionToken('AppId', {
|
|
9134
|
+
providedIn: 'root',
|
|
9135
|
+
factory: () => DEFAULT_APP_ID,
|
|
9136
|
+
});
|
|
9137
|
+
/** Default value of the `APP_ID` token. */
|
|
9138
|
+
const DEFAULT_APP_ID = 'ng';
|
|
8664
9139
|
/**
|
|
8665
|
-
* A
|
|
8666
|
-
*
|
|
8667
|
-
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
|
8668
|
-
* element.
|
|
8669
|
-
*
|
|
8670
|
-
* @security Permitting direct access to the DOM can make your application more vulnerable to
|
|
8671
|
-
* XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
|
|
8672
|
-
* [Security Guide](https://g.co/ng/security).
|
|
8673
|
-
*
|
|
9140
|
+
* A function that is executed when a platform is initialized.
|
|
8674
9141
|
* @publicApi
|
|
8675
9142
|
*/
|
|
8676
|
-
|
|
8677
|
-
// i.e. users have to ask for what they need. With that, we can build better analysis tools
|
|
8678
|
-
// and could do better codegen in the future.
|
|
8679
|
-
class ElementRef {
|
|
8680
|
-
constructor(nativeElement) {
|
|
8681
|
-
this.nativeElement = nativeElement;
|
|
8682
|
-
}
|
|
8683
|
-
}
|
|
9143
|
+
const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
|
|
8684
9144
|
/**
|
|
8685
|
-
*
|
|
8686
|
-
* @
|
|
9145
|
+
* A token that indicates an opaque platform ID.
|
|
9146
|
+
* @publicApi
|
|
8687
9147
|
*/
|
|
8688
|
-
|
|
9148
|
+
const PLATFORM_ID = new InjectionToken('Platform ID', {
|
|
9149
|
+
providedIn: 'platform',
|
|
9150
|
+
factory: () => 'unknown', // set a default platform name, when none set explicitly
|
|
9151
|
+
});
|
|
8689
9152
|
/**
|
|
8690
|
-
*
|
|
8691
|
-
*
|
|
8692
|
-
* @
|
|
8693
|
-
* @returns `nativeElement` if `ElementRef` otherwise returns value as is.
|
|
9153
|
+
* A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
|
|
9154
|
+
* the application
|
|
9155
|
+
* @publicApi
|
|
8694
9156
|
*/
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
9157
|
+
const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
|
|
9158
|
+
// We keep this token here, rather than the animations package, so that modules that only care
|
|
9159
|
+
// about which animations module is loaded (e.g. the CDK) can retrieve it without having to
|
|
9160
|
+
// include extra dependencies. See #44970 for more context.
|
|
8699
9161
|
/**
|
|
8700
|
-
*
|
|
8701
|
-
*
|
|
9162
|
+
* A [DI token](guide/glossary#di-token "DI token definition") that indicates which animations
|
|
9163
|
+
* module has been loaded.
|
|
8702
9164
|
* @publicApi
|
|
8703
9165
|
*/
|
|
8704
|
-
|
|
9166
|
+
const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType');
|
|
9167
|
+
|
|
9168
|
+
function escapeTransferStateContent(text) {
|
|
9169
|
+
const escapedText = {
|
|
9170
|
+
'&': '&a;',
|
|
9171
|
+
'"': '&q;',
|
|
9172
|
+
'\'': '&s;',
|
|
9173
|
+
'<': '&l;',
|
|
9174
|
+
'>': '&g;',
|
|
9175
|
+
};
|
|
9176
|
+
return text.replace(/[&"'<>]/g, s => escapedText[s]);
|
|
9177
|
+
}
|
|
9178
|
+
function unescapeTransferStateContent(text) {
|
|
9179
|
+
const unescapedText = {
|
|
9180
|
+
'&a;': '&',
|
|
9181
|
+
'&q;': '"',
|
|
9182
|
+
'&s;': '\'',
|
|
9183
|
+
'&l;': '<',
|
|
9184
|
+
'&g;': '>',
|
|
9185
|
+
};
|
|
9186
|
+
return text.replace(/&[^;]+;/g, s => unescapedText[s]);
|
|
8705
9187
|
}
|
|
8706
9188
|
/**
|
|
8707
|
-
*
|
|
8708
|
-
* renders a template into DOM. You can use custom rendering to intercept
|
|
8709
|
-
* rendering calls, or to render to something other than DOM.
|
|
8710
|
-
*
|
|
8711
|
-
* Create your custom renderer using `RendererFactory2`.
|
|
9189
|
+
* Create a `StateKey<T>` that can be used to store value of type T with `TransferState`.
|
|
8712
9190
|
*
|
|
8713
|
-
*
|
|
8714
|
-
* make custom UI changes that can't be expressed declaratively.
|
|
8715
|
-
* For example if you need to set a property or an attribute whose name is
|
|
8716
|
-
* not statically known, use the `setProperty()` or
|
|
8717
|
-
* `setAttribute()` method.
|
|
9191
|
+
* Example:
|
|
8718
9192
|
*
|
|
8719
|
-
*
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
}
|
|
8723
|
-
/**
|
|
8724
|
-
* @internal
|
|
8725
|
-
* @nocollapse
|
|
8726
|
-
*/
|
|
8727
|
-
Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2();
|
|
8728
|
-
/** Injects a Renderer2 for the current component. */
|
|
8729
|
-
function injectRenderer2() {
|
|
8730
|
-
// We need the Renderer to be based on the component that it's being injected into, however since
|
|
8731
|
-
// DI happens before we've entered its view, `getLView` will return the parent view instead.
|
|
8732
|
-
const lView = getLView();
|
|
8733
|
-
const tNode = getCurrentTNode();
|
|
8734
|
-
const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
|
|
8735
|
-
return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
|
|
8736
|
-
}
|
|
8737
|
-
|
|
8738
|
-
/**
|
|
8739
|
-
* Sanitizer is used by the views to sanitize potentially dangerous values.
|
|
9193
|
+
* ```
|
|
9194
|
+
* const COUNTER_KEY = makeStateKey<number>('counter');
|
|
9195
|
+
* let value = 10;
|
|
8740
9196
|
*
|
|
8741
|
-
*
|
|
8742
|
-
|
|
8743
|
-
class Sanitizer {
|
|
8744
|
-
}
|
|
8745
|
-
/** @nocollapse */
|
|
8746
|
-
Sanitizer.ɵprov = ɵɵdefineInjectable({
|
|
8747
|
-
token: Sanitizer,
|
|
8748
|
-
providedIn: 'root',
|
|
8749
|
-
factory: () => null,
|
|
8750
|
-
});
|
|
8751
|
-
|
|
8752
|
-
/**
|
|
8753
|
-
* @description Represents the version of Angular
|
|
9197
|
+
* transferState.set(COUNTER_KEY, value);
|
|
9198
|
+
* ```
|
|
8754
9199
|
*
|
|
8755
9200
|
* @publicApi
|
|
8756
9201
|
*/
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
this.full = full;
|
|
8760
|
-
this.major = full.split('.')[0];
|
|
8761
|
-
this.minor = full.split('.')[1];
|
|
8762
|
-
this.patch = full.split('.').slice(2).join('.');
|
|
8763
|
-
}
|
|
8764
|
-
}
|
|
8765
|
-
/**
|
|
8766
|
-
* @publicApi
|
|
8767
|
-
*/
|
|
8768
|
-
const VERSION = new Version('16.0.0-next.1');
|
|
8769
|
-
|
|
8770
|
-
// This default value is when checking the hierarchy for a token.
|
|
8771
|
-
//
|
|
8772
|
-
// It means both:
|
|
8773
|
-
// - the token is not provided by the current injector,
|
|
8774
|
-
// - only the element injectors should be checked (ie do not check module injectors
|
|
8775
|
-
//
|
|
8776
|
-
// mod1
|
|
8777
|
-
// /
|
|
8778
|
-
// el1 mod2
|
|
8779
|
-
// \ /
|
|
8780
|
-
// el2
|
|
8781
|
-
//
|
|
8782
|
-
// When requesting el2.injector.get(token), we should check in the following order and return the
|
|
8783
|
-
// first found value:
|
|
8784
|
-
// - el2.injector.get(token, default)
|
|
8785
|
-
// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
|
8786
|
-
// - mod2.injector.get(token, default)
|
|
8787
|
-
const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
|
8788
|
-
|
|
8789
|
-
const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
|
8790
|
-
function wrappedError(message, originalError) {
|
|
8791
|
-
const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
|
8792
|
-
const error = Error(msg);
|
|
8793
|
-
error[ERROR_ORIGINAL_ERROR] = originalError;
|
|
8794
|
-
return error;
|
|
9202
|
+
function makeStateKey(key) {
|
|
9203
|
+
return key;
|
|
8795
9204
|
}
|
|
8796
|
-
function
|
|
8797
|
-
|
|
9205
|
+
function initTransferState() {
|
|
9206
|
+
const transferState = new TransferState();
|
|
9207
|
+
transferState.store = retrieveTransferredState(getDocument(), inject$1(APP_ID));
|
|
9208
|
+
return transferState;
|
|
8798
9209
|
}
|
|
8799
|
-
|
|
8800
9210
|
/**
|
|
8801
|
-
*
|
|
8802
|
-
*
|
|
8803
|
-
* The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
|
8804
|
-
* intercept error handling, write a custom exception handler that replaces this default as
|
|
8805
|
-
* appropriate for your app.
|
|
8806
|
-
*
|
|
8807
|
-
* @usageNotes
|
|
8808
|
-
* ### Example
|
|
9211
|
+
* A key value store that is transferred from the application on the server side to the application
|
|
9212
|
+
* on the client side.
|
|
8809
9213
|
*
|
|
8810
|
-
*
|
|
8811
|
-
*
|
|
8812
|
-
*
|
|
8813
|
-
*
|
|
8814
|
-
* }
|
|
8815
|
-
* }
|
|
9214
|
+
* The `TransferState` is available as an injectable token.
|
|
9215
|
+
* On the client, just inject this token using DI and use it, it will be lazily initialized.
|
|
9216
|
+
* On the server it's already included if `renderApplication` function is used. Otherwise, import
|
|
9217
|
+
* the `ServerTransferStateModule` module to make the `TransferState` available.
|
|
8816
9218
|
*
|
|
8817
|
-
*
|
|
8818
|
-
*
|
|
8819
|
-
*
|
|
8820
|
-
* class MyModule {}
|
|
8821
|
-
* ```
|
|
9219
|
+
* The values in the store are serialized/deserialized using JSON.stringify/JSON.parse. So only
|
|
9220
|
+
* boolean, number, string, null and non-class objects will be serialized and deserialized in a
|
|
9221
|
+
* non-lossy manner.
|
|
8822
9222
|
*
|
|
8823
9223
|
* @publicApi
|
|
8824
9224
|
*/
|
|
8825
|
-
class
|
|
9225
|
+
class TransferState {
|
|
8826
9226
|
constructor() {
|
|
8827
|
-
/**
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
this._console = console;
|
|
9227
|
+
/** @internal */
|
|
9228
|
+
this.store = {};
|
|
9229
|
+
this.onSerializeCallbacks = {};
|
|
8831
9230
|
}
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
9231
|
+
/**
|
|
9232
|
+
* Get the value corresponding to a key. Return `defaultValue` if key is not found.
|
|
9233
|
+
*/
|
|
9234
|
+
get(key, defaultValue) {
|
|
9235
|
+
return this.store[key] !== undefined ? this.store[key] : defaultValue;
|
|
9236
|
+
}
|
|
9237
|
+
/**
|
|
9238
|
+
* Set the value corresponding to a key.
|
|
9239
|
+
*/
|
|
9240
|
+
set(key, value) {
|
|
9241
|
+
this.store[key] = value;
|
|
9242
|
+
}
|
|
9243
|
+
/**
|
|
9244
|
+
* Remove a key from the store.
|
|
9245
|
+
*/
|
|
9246
|
+
remove(key) {
|
|
9247
|
+
delete this.store[key];
|
|
9248
|
+
}
|
|
9249
|
+
/**
|
|
9250
|
+
* Test whether a key exists in the store.
|
|
9251
|
+
*/
|
|
9252
|
+
hasKey(key) {
|
|
9253
|
+
return this.store.hasOwnProperty(key);
|
|
9254
|
+
}
|
|
9255
|
+
/**
|
|
9256
|
+
* Indicates whether the state is empty.
|
|
9257
|
+
*/
|
|
9258
|
+
get isEmpty() {
|
|
9259
|
+
return Object.keys(this.store).length === 0;
|
|
9260
|
+
}
|
|
9261
|
+
/**
|
|
9262
|
+
* Register a callback to provide the value for a key when `toJson` is called.
|
|
9263
|
+
*/
|
|
9264
|
+
onSerialize(key, callback) {
|
|
9265
|
+
this.onSerializeCallbacks[key] = callback;
|
|
9266
|
+
}
|
|
9267
|
+
/**
|
|
9268
|
+
* Serialize the current state of the store to JSON.
|
|
9269
|
+
*/
|
|
9270
|
+
toJson() {
|
|
9271
|
+
// Call the onSerialize callbacks and put those values into the store.
|
|
9272
|
+
for (const key in this.onSerializeCallbacks) {
|
|
9273
|
+
if (this.onSerializeCallbacks.hasOwnProperty(key)) {
|
|
9274
|
+
try {
|
|
9275
|
+
this.store[key] = this.onSerializeCallbacks[key]();
|
|
9276
|
+
}
|
|
9277
|
+
catch (e) {
|
|
9278
|
+
console.warn('Exception in onSerialize callback: ', e);
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
8837
9281
|
}
|
|
9282
|
+
return JSON.stringify(this.store);
|
|
8838
9283
|
}
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
9284
|
+
}
|
|
9285
|
+
/** @nocollapse */
|
|
9286
|
+
TransferState.ɵprov =
|
|
9287
|
+
/** @pureOrBreakMyCode */ ɵɵdefineInjectable({
|
|
9288
|
+
token: TransferState,
|
|
9289
|
+
providedIn: 'root',
|
|
9290
|
+
factory: initTransferState,
|
|
9291
|
+
});
|
|
9292
|
+
function retrieveTransferredState(doc, appId) {
|
|
9293
|
+
// Locate the script tag with the JSON data transferred from the server.
|
|
9294
|
+
// The id of the script tag is set to the Angular appId + 'state'.
|
|
9295
|
+
const script = doc.getElementById(appId + '-state');
|
|
9296
|
+
let initialState = {};
|
|
9297
|
+
if (script && script.textContent) {
|
|
9298
|
+
try {
|
|
9299
|
+
// Avoid using any here as it triggers lint errors in google3 (any is not allowed).
|
|
9300
|
+
initialState = JSON.parse(unescapeTransferStateContent(script.textContent));
|
|
9301
|
+
}
|
|
9302
|
+
catch (e) {
|
|
9303
|
+
console.warn('Exception while restoring TransferState for app ' + appId, e);
|
|
8844
9304
|
}
|
|
8845
|
-
return e || null;
|
|
8846
9305
|
}
|
|
9306
|
+
return initialState;
|
|
8847
9307
|
}
|
|
8848
9308
|
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
9309
|
+
/**
|
|
9310
|
+
* Keys within serialized view data structure to represent various
|
|
9311
|
+
* parts. See the `SerializedView` interface below for additional information.
|
|
9312
|
+
*/
|
|
9313
|
+
const ELEMENT_CONTAINERS = 'e';
|
|
9314
|
+
const TEMPLATES = 't';
|
|
9315
|
+
const CONTAINERS = 'c';
|
|
9316
|
+
const NUM_ROOT_NODES = 'r';
|
|
9317
|
+
const TEMPLATE_ID = 'i'; // as it's also an "id"
|
|
9318
|
+
|
|
9319
|
+
/**
|
|
9320
|
+
* The name of the key used in the TransferState collection,
|
|
9321
|
+
* where hydration information is located.
|
|
9322
|
+
*/
|
|
9323
|
+
const TRANSFER_STATE_TOKEN_ID = '__ɵnghData__';
|
|
9324
|
+
/**
|
|
9325
|
+
* Lookup key used to reference DOM hydration data (ngh) in `TransferState`.
|
|
9326
|
+
*/
|
|
9327
|
+
const NGH_DATA_KEY = makeStateKey(TRANSFER_STATE_TOKEN_ID);
|
|
9328
|
+
/**
|
|
9329
|
+
* The name of the attribute that would be added to host component
|
|
9330
|
+
* nodes and contain a reference to a particular slot in transferred
|
|
9331
|
+
* state that contains the necessary hydration info for this component.
|
|
9332
|
+
*/
|
|
9333
|
+
const NGH_ATTR_NAME = 'ngh';
|
|
9334
|
+
/**
|
|
9335
|
+
* Reference to a function that reads `ngh` attribute value from a given RNode
|
|
9336
|
+
* and retrieves hydration information from the TransferState using that value
|
|
9337
|
+
* as an index. Returns `null` by default, when hydration is not enabled.
|
|
9338
|
+
*
|
|
9339
|
+
* @param rNode Component's host element.
|
|
9340
|
+
* @param injector Injector that this component has access to.
|
|
9341
|
+
*/
|
|
9342
|
+
let _retrieveHydrationInfoImpl = (rNode, injector) => null;
|
|
9343
|
+
function retrieveHydrationInfoImpl(rNode, injector) {
|
|
9344
|
+
const nghAttrValue = rNode.getAttribute(NGH_ATTR_NAME);
|
|
9345
|
+
if (nghAttrValue == null)
|
|
9346
|
+
return null;
|
|
9347
|
+
let data = {};
|
|
9348
|
+
// An element might have an empty `ngh` attribute value (e.g. `<comp ngh="" />`),
|
|
9349
|
+
// which means that no special annotations are required. Do not attempt to read
|
|
9350
|
+
// from the TransferState in this case.
|
|
9351
|
+
if (nghAttrValue !== '') {
|
|
9352
|
+
const transferState = injector.get(TransferState, null, { optional: true });
|
|
9353
|
+
if (transferState !== null) {
|
|
9354
|
+
const nghData = transferState.get(NGH_DATA_KEY, []);
|
|
9355
|
+
// The nghAttrValue is always a number referencing an index
|
|
9356
|
+
// in the hydration TransferState data.
|
|
9357
|
+
data = nghData[Number(nghAttrValue)];
|
|
9358
|
+
// If the `ngh` attribute exists and has a non-empty value,
|
|
9359
|
+
// the hydration info *must* be present in the TransferState.
|
|
9360
|
+
// If there is no data for some reasons, this is an error.
|
|
9361
|
+
ngDevMode && assertDefined(data, 'Unable to retrieve hydration info from the TransferState.');
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
const dehydratedView = {
|
|
9365
|
+
data,
|
|
9366
|
+
firstChild: rNode.firstChild ?? null,
|
|
9367
|
+
};
|
|
9368
|
+
// The `ngh` attribute is cleared from the DOM node now
|
|
9369
|
+
// that the data has been retrieved.
|
|
9370
|
+
rNode.removeAttribute(NGH_ATTR_NAME);
|
|
9371
|
+
// Note: don't check whether this node was claimed for hydration,
|
|
9372
|
+
// because this node might've been previously claimed while processing
|
|
9373
|
+
// template instructions.
|
|
9374
|
+
ngDevMode && markRNodeAsClaimedByHydration(rNode, /* checkIfAlreadyClaimed */ false);
|
|
9375
|
+
ngDevMode && ngDevMode.hydratedComponents++;
|
|
9376
|
+
return dehydratedView;
|
|
8853
9377
|
}
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
9378
|
+
/**
|
|
9379
|
+
* Sets the implementation for the `retrieveHydrationInfo` function.
|
|
9380
|
+
*/
|
|
9381
|
+
function enableRetrieveHydrationInfoImpl() {
|
|
9382
|
+
_retrieveHydrationInfoImpl = retrieveHydrationInfoImpl;
|
|
8857
9383
|
}
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
9384
|
+
/**
|
|
9385
|
+
* Retrieves hydration info by reading the value from the `ngh` attribute
|
|
9386
|
+
* and accessing a corresponding slot in TransferState storage.
|
|
9387
|
+
*/
|
|
9388
|
+
function retrieveHydrationInfo(rNode, injector) {
|
|
9389
|
+
return _retrieveHydrationInfoImpl(rNode, injector);
|
|
9390
|
+
}
|
|
9391
|
+
/**
|
|
9392
|
+
* Retrieves an instance of a component LView from a given ViewRef.
|
|
9393
|
+
* Returns an instance of a component LView or `null` in case of an embedded view.
|
|
9394
|
+
*/
|
|
9395
|
+
function getComponentLViewForHydration(viewRef) {
|
|
9396
|
+
// Reading an internal field from `ViewRef` instance.
|
|
9397
|
+
let lView = viewRef._lView;
|
|
9398
|
+
const tView = lView[TVIEW];
|
|
9399
|
+
// A registered ViewRef might represent an instance of an
|
|
9400
|
+
// embedded view, in which case we do not need to annotate it.
|
|
9401
|
+
if (tView.type === 2 /* TViewType.Embedded */) {
|
|
9402
|
+
return null;
|
|
8862
9403
|
}
|
|
8863
|
-
|
|
8864
|
-
|
|
9404
|
+
// Check if it's a root view and if so, retrieve component's
|
|
9405
|
+
// LView from the first slot after the header.
|
|
9406
|
+
if (isRootView(lView)) {
|
|
9407
|
+
lView = lView[HEADER_OFFSET];
|
|
8865
9408
|
}
|
|
9409
|
+
return lView;
|
|
8866
9410
|
}
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
9411
|
+
function getTextNodeContent(node) {
|
|
9412
|
+
return node.textContent?.replace(/\s/gm, '');
|
|
9413
|
+
}
|
|
9414
|
+
/**
|
|
9415
|
+
* Restores text nodes and separators into the DOM that were lost during SSR
|
|
9416
|
+
* serialization. The hydration process replaces empty text nodes and text
|
|
9417
|
+
* nodes that are immediately adjacent to other text nodes with comment nodes
|
|
9418
|
+
* that this method filters on to restore those missing nodes that the
|
|
9419
|
+
* hydration process is expecting to be present.
|
|
9420
|
+
*
|
|
9421
|
+
* @param node The app's root HTML Element
|
|
9422
|
+
*/
|
|
9423
|
+
function processTextNodeMarkersBeforeHydration(node) {
|
|
9424
|
+
const doc = getDocument();
|
|
9425
|
+
const commentNodesIterator = doc.createNodeIterator(node, NodeFilter.SHOW_COMMENT, {
|
|
9426
|
+
acceptNode(node) {
|
|
9427
|
+
const content = getTextNodeContent(node);
|
|
9428
|
+
const isTextNodeMarker = content === "ngetn" /* TextNodeMarker.EmptyNode */ || content === "ngtns" /* TextNodeMarker.Separator */;
|
|
9429
|
+
return isTextNodeMarker ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
9430
|
+
}
|
|
9431
|
+
});
|
|
9432
|
+
let currentNode;
|
|
9433
|
+
// We cannot modify the DOM while using the commentIterator,
|
|
9434
|
+
// because it throws off the iterator state.
|
|
9435
|
+
// So we collect all marker nodes first and then follow up with
|
|
9436
|
+
// applying the changes to the DOM: either inserting an empty node
|
|
9437
|
+
// or just removing the marker if it was used as a separator.
|
|
9438
|
+
const nodes = [];
|
|
9439
|
+
while (currentNode = commentNodesIterator.nextNode()) {
|
|
9440
|
+
nodes.push(currentNode);
|
|
9441
|
+
}
|
|
9442
|
+
for (const node of nodes) {
|
|
9443
|
+
if (node.textContent === "ngetn" /* TextNodeMarker.EmptyNode */) {
|
|
9444
|
+
node.replaceWith(doc.createTextNode(''));
|
|
9445
|
+
}
|
|
9446
|
+
else {
|
|
9447
|
+
node.remove();
|
|
9448
|
+
}
|
|
8877
9449
|
}
|
|
8878
9450
|
}
|
|
8879
|
-
/**
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
9451
|
+
/**
|
|
9452
|
+
* Marks a node as "claimed" by hydration process.
|
|
9453
|
+
* This is needed to make assessments in tests whether
|
|
9454
|
+
* the hydration process handled all nodes.
|
|
9455
|
+
*/
|
|
9456
|
+
function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) {
|
|
9457
|
+
if (!ngDevMode) {
|
|
9458
|
+
throw new Error('Calling `markRNodeAsClaimedByHydration` in prod mode ' +
|
|
9459
|
+
'is not supported and likely a mistake.');
|
|
9460
|
+
}
|
|
9461
|
+
if (checkIfAlreadyClaimed && isRNodeClaimedForHydration(node)) {
|
|
9462
|
+
throw new Error('Trying to claim a node, which was claimed already.');
|
|
8884
9463
|
}
|
|
9464
|
+
node.__claimed = true;
|
|
9465
|
+
ngDevMode.hydratedNodes++;
|
|
8885
9466
|
}
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
|
|
8889
|
-
`${stringifyForError(first)} and ` +
|
|
8890
|
-
`${stringifyForError(second)}`);
|
|
9467
|
+
function isRNodeClaimedForHydration(node) {
|
|
9468
|
+
return !!node.__claimed;
|
|
8891
9469
|
}
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
9470
|
+
function setSegmentHead(hydrationInfo, index, node) {
|
|
9471
|
+
hydrationInfo.segmentHeads ?? (hydrationInfo.segmentHeads = {});
|
|
9472
|
+
hydrationInfo.segmentHeads[index] = node;
|
|
9473
|
+
}
|
|
9474
|
+
function getSegmentHead(hydrationInfo, index) {
|
|
9475
|
+
return hydrationInfo.segmentHeads?.[index] ?? null;
|
|
9476
|
+
}
|
|
9477
|
+
/**
|
|
9478
|
+
* Returns the size of an <ng-container>, using either the information
|
|
9479
|
+
* serialized in `ELEMENT_CONTAINERS` (element container size) or by
|
|
9480
|
+
* computing the sum of root nodes in all dehydrated views in a given
|
|
9481
|
+
* container (in case this `<ng-container>` was also used as a view
|
|
9482
|
+
* container host node, e.g. <ng-container *ngIf>).
|
|
9483
|
+
*/
|
|
9484
|
+
function getNgContainerSize(hydrationInfo, index) {
|
|
9485
|
+
const data = hydrationInfo.data;
|
|
9486
|
+
let size = data[ELEMENT_CONTAINERS]?.[index] ?? null;
|
|
9487
|
+
// If there is no serialized information available in the `ELEMENT_CONTAINERS` slot,
|
|
9488
|
+
// check if we have info about view containers at this location (e.g.
|
|
9489
|
+
// `<ng-container *ngIf>`) and use container size as a number of root nodes in this
|
|
9490
|
+
// element container.
|
|
9491
|
+
if (size === null && data[CONTAINERS]?.[index]) {
|
|
9492
|
+
size = calcSerializedContainerSize(hydrationInfo, index);
|
|
8900
9493
|
}
|
|
8901
|
-
|
|
9494
|
+
return size;
|
|
8902
9495
|
}
|
|
8903
|
-
function
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
9496
|
+
function getSerializedContainerViews(hydrationInfo, index) {
|
|
9497
|
+
return hydrationInfo.data[CONTAINERS]?.[index] ?? null;
|
|
9498
|
+
}
|
|
9499
|
+
/**
|
|
9500
|
+
* Computes the size of a serialized container (the number of root nodes)
|
|
9501
|
+
* by calculating the sum of root nodes in all dehydrated views in this container.
|
|
9502
|
+
*/
|
|
9503
|
+
function calcSerializedContainerSize(hydrationInfo, index) {
|
|
9504
|
+
const views = getSerializedContainerViews(hydrationInfo, index) ?? [];
|
|
9505
|
+
let numNodes = 0;
|
|
9506
|
+
for (let view of views) {
|
|
9507
|
+
numNodes += view[NUM_ROOT_NODES];
|
|
8910
9508
|
}
|
|
8911
|
-
return
|
|
9509
|
+
return numNodes;
|
|
8912
9510
|
}
|
|
9511
|
+
|
|
8913
9512
|
/**
|
|
8914
|
-
*
|
|
8915
|
-
*
|
|
8916
|
-
*
|
|
9513
|
+
* Represents a component created by a `ComponentFactory`.
|
|
9514
|
+
* Provides access to the component instance and related objects,
|
|
9515
|
+
* and provides the means of destroying the instance.
|
|
8917
9516
|
*
|
|
8918
|
-
*
|
|
8919
|
-
* function description.
|
|
9517
|
+
* @publicApi
|
|
8920
9518
|
*/
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
9519
|
+
class ComponentRef$1 {
|
|
9520
|
+
}
|
|
9521
|
+
/**
|
|
9522
|
+
* Base class for a factory that can create a component dynamically.
|
|
9523
|
+
* Instantiate a factory for a given type of component with `resolveComponentFactory()`.
|
|
9524
|
+
* Use the resulting `ComponentFactory.create()` method to create a component of that type.
|
|
9525
|
+
*
|
|
9526
|
+
* @see [Dynamic Components](guide/dynamic-component-loader)
|
|
9527
|
+
*
|
|
9528
|
+
* @publicApi
|
|
9529
|
+
*
|
|
9530
|
+
* @deprecated Angular no longer requires Component factories. Please use other APIs where
|
|
9531
|
+
* Component class can be used directly.
|
|
9532
|
+
*/
|
|
9533
|
+
class ComponentFactory$1 {
|
|
9534
|
+
}
|
|
9535
|
+
|
|
9536
|
+
function noComponentFactoryError(component) {
|
|
9537
|
+
const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
|
|
9538
|
+
error[ERROR_COMPONENT] = component;
|
|
9539
|
+
return error;
|
|
9540
|
+
}
|
|
9541
|
+
const ERROR_COMPONENT = 'ngComponent';
|
|
9542
|
+
function getComponent$1(error) {
|
|
9543
|
+
return error[ERROR_COMPONENT];
|
|
9544
|
+
}
|
|
9545
|
+
class _NullComponentFactoryResolver {
|
|
9546
|
+
resolveComponentFactory(component) {
|
|
9547
|
+
throw noComponentFactoryError(component);
|
|
8950
9548
|
}
|
|
8951
|
-
return { propName: undefined, oldValue, newValue };
|
|
8952
9549
|
}
|
|
9550
|
+
/**
|
|
9551
|
+
* A simple registry that maps `Components` to generated `ComponentFactory` classes
|
|
9552
|
+
* that can be used to create instances of components.
|
|
9553
|
+
* Use to obtain the factory for a given component type,
|
|
9554
|
+
* then use the factory's `create()` method to create a component of that type.
|
|
9555
|
+
*
|
|
9556
|
+
* Note: since v13, dynamic component creation via
|
|
9557
|
+
* [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
|
|
9558
|
+
* does **not** require resolving component factory: component class can be used directly.
|
|
9559
|
+
*
|
|
9560
|
+
* @publicApi
|
|
9561
|
+
*
|
|
9562
|
+
* @deprecated Angular no longer requires Component factories. Please use other APIs where
|
|
9563
|
+
* Component class can be used directly.
|
|
9564
|
+
*/
|
|
9565
|
+
class ComponentFactoryResolver$1 {
|
|
9566
|
+
}
|
|
9567
|
+
ComponentFactoryResolver$1.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver());
|
|
8953
9568
|
|
|
8954
9569
|
/**
|
|
8955
|
-
*
|
|
9570
|
+
* Creates an ElementRef from the most recent node.
|
|
8956
9571
|
*
|
|
8957
|
-
*
|
|
9572
|
+
* @returns The ElementRef instance to use
|
|
9573
|
+
*/
|
|
9574
|
+
function injectElementRef() {
|
|
9575
|
+
return createElementRef(getCurrentTNode(), getLView());
|
|
9576
|
+
}
|
|
9577
|
+
/**
|
|
9578
|
+
* Creates an ElementRef given a node.
|
|
8958
9579
|
*
|
|
8959
|
-
* @param
|
|
8960
|
-
* @param
|
|
8961
|
-
* @
|
|
8962
|
-
* @returns an index of the located class (or -1 if not found)
|
|
9580
|
+
* @param tNode The node for which you'd like an ElementRef
|
|
9581
|
+
* @param lView The view to which the node belongs
|
|
9582
|
+
* @returns The ElementRef instance to use
|
|
8963
9583
|
*/
|
|
8964
|
-
function
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
9584
|
+
function createElementRef(tNode, lView) {
|
|
9585
|
+
return new ElementRef(getNativeByTNode(tNode, lView));
|
|
9586
|
+
}
|
|
9587
|
+
/**
|
|
9588
|
+
* A wrapper around a native element inside of a View.
|
|
9589
|
+
*
|
|
9590
|
+
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
|
9591
|
+
* element.
|
|
9592
|
+
*
|
|
9593
|
+
* @security Permitting direct access to the DOM can make your application more vulnerable to
|
|
9594
|
+
* XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
|
|
9595
|
+
* [Security Guide](https://g.co/ng/security).
|
|
9596
|
+
*
|
|
9597
|
+
* @publicApi
|
|
9598
|
+
*/
|
|
9599
|
+
// Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
|
|
9600
|
+
// i.e. users have to ask for what they need. With that, we can build better analysis tools
|
|
9601
|
+
// and could do better codegen in the future.
|
|
9602
|
+
class ElementRef {
|
|
9603
|
+
constructor(nativeElement) {
|
|
9604
|
+
this.nativeElement = nativeElement;
|
|
8982
9605
|
}
|
|
8983
9606
|
}
|
|
9607
|
+
/**
|
|
9608
|
+
* @internal
|
|
9609
|
+
* @nocollapse
|
|
9610
|
+
*/
|
|
9611
|
+
ElementRef.__NG_ELEMENT_ID__ = injectElementRef;
|
|
9612
|
+
/**
|
|
9613
|
+
* Unwraps `ElementRef` and return the `nativeElement`.
|
|
9614
|
+
*
|
|
9615
|
+
* @param value value to unwrap
|
|
9616
|
+
* @returns `nativeElement` if `ElementRef` otherwise returns value as is.
|
|
9617
|
+
*/
|
|
9618
|
+
function unwrapElementRef(value) {
|
|
9619
|
+
return value instanceof ElementRef ? value.nativeElement : value;
|
|
9620
|
+
}
|
|
8984
9621
|
|
|
8985
|
-
const NG_TEMPLATE_SELECTOR = 'ng-template';
|
|
8986
9622
|
/**
|
|
8987
|
-
*
|
|
9623
|
+
* Creates and initializes a custom renderer that implements the `Renderer2` base class.
|
|
8988
9624
|
*
|
|
8989
|
-
* @
|
|
8990
|
-
* @param cssClassToMatch class to match (lowercase)
|
|
8991
|
-
* @param isProjectionMode Whether or not class matching should look into the attribute `class` in
|
|
8992
|
-
* addition to the `AttributeMarker.Classes`.
|
|
9625
|
+
* @publicApi
|
|
8993
9626
|
*/
|
|
8994
|
-
|
|
8995
|
-
// TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
|
|
8996
|
-
// It is strange to me that sometimes the class information comes in form of `class` attribute
|
|
8997
|
-
// and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
|
|
8998
|
-
// if that is the right behavior.
|
|
8999
|
-
ngDevMode &&
|
|
9000
|
-
assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
|
|
9001
|
-
let i = 0;
|
|
9002
|
-
while (i < attrs.length) {
|
|
9003
|
-
let item = attrs[i++];
|
|
9004
|
-
if (isProjectionMode && item === 'class') {
|
|
9005
|
-
item = attrs[i];
|
|
9006
|
-
if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
|
|
9007
|
-
return true;
|
|
9008
|
-
}
|
|
9009
|
-
}
|
|
9010
|
-
else if (item === 1 /* AttributeMarker.Classes */) {
|
|
9011
|
-
// We found the classes section. Start searching for the class.
|
|
9012
|
-
while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
|
|
9013
|
-
// while we have strings
|
|
9014
|
-
if (item.toLowerCase() === cssClassToMatch)
|
|
9015
|
-
return true;
|
|
9016
|
-
}
|
|
9017
|
-
return false;
|
|
9018
|
-
}
|
|
9019
|
-
}
|
|
9020
|
-
return false;
|
|
9627
|
+
class RendererFactory2 {
|
|
9021
9628
|
}
|
|
9022
9629
|
/**
|
|
9023
|
-
*
|
|
9630
|
+
* Extend this base class to implement custom rendering. By default, Angular
|
|
9631
|
+
* renders a template into DOM. You can use custom rendering to intercept
|
|
9632
|
+
* rendering calls, or to render to something other than DOM.
|
|
9024
9633
|
*
|
|
9025
|
-
*
|
|
9634
|
+
* Create your custom renderer using `RendererFactory2`.
|
|
9635
|
+
*
|
|
9636
|
+
* Use a custom renderer to bypass Angular's templating and
|
|
9637
|
+
* make custom UI changes that can't be expressed declaratively.
|
|
9638
|
+
* For example if you need to set a property or an attribute whose name is
|
|
9639
|
+
* not statically known, use the `setProperty()` or
|
|
9640
|
+
* `setAttribute()` method.
|
|
9641
|
+
*
|
|
9642
|
+
* @publicApi
|
|
9026
9643
|
*/
|
|
9027
|
-
|
|
9028
|
-
return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
|
|
9644
|
+
class Renderer2 {
|
|
9029
9645
|
}
|
|
9030
9646
|
/**
|
|
9031
|
-
*
|
|
9647
|
+
* @internal
|
|
9648
|
+
* @nocollapse
|
|
9649
|
+
*/
|
|
9650
|
+
Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2();
|
|
9651
|
+
/** Injects a Renderer2 for the current component. */
|
|
9652
|
+
function injectRenderer2() {
|
|
9653
|
+
// We need the Renderer to be based on the component that it's being injected into, however since
|
|
9654
|
+
// DI happens before we've entered its view, `getLView` will return the parent view instead.
|
|
9655
|
+
const lView = getLView();
|
|
9656
|
+
const tNode = getCurrentTNode();
|
|
9657
|
+
const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
|
|
9658
|
+
return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
|
|
9659
|
+
}
|
|
9660
|
+
|
|
9661
|
+
/**
|
|
9662
|
+
* Sanitizer is used by the views to sanitize potentially dangerous values.
|
|
9032
9663
|
*
|
|
9033
|
-
*
|
|
9034
|
-
* directive matching mode:
|
|
9035
|
-
* - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
|
|
9036
|
-
* different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
|
|
9037
|
-
* tag name was extracted from * syntax so we would match the same directive twice);
|
|
9038
|
-
* - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
|
|
9039
|
-
* (applicable to TNodeType.Container only).
|
|
9664
|
+
* @publicApi
|
|
9040
9665
|
*/
|
|
9041
|
-
|
|
9042
|
-
const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
|
|
9043
|
-
return currentSelector === tagNameToCompare;
|
|
9666
|
+
class Sanitizer {
|
|
9044
9667
|
}
|
|
9668
|
+
/** @nocollapse */
|
|
9669
|
+
Sanitizer.ɵprov = ɵɵdefineInjectable({
|
|
9670
|
+
token: Sanitizer,
|
|
9671
|
+
providedIn: 'root',
|
|
9672
|
+
factory: () => null,
|
|
9673
|
+
});
|
|
9674
|
+
|
|
9045
9675
|
/**
|
|
9046
|
-
*
|
|
9676
|
+
* @description Represents the version of Angular
|
|
9047
9677
|
*
|
|
9048
|
-
* @
|
|
9049
|
-
* @param selector The selector to try matching against the node.
|
|
9050
|
-
* @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
|
|
9051
|
-
* directive matching.
|
|
9052
|
-
* @returns true if node matches the selector.
|
|
9678
|
+
* @publicApi
|
|
9053
9679
|
*/
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
// When processing ":not" selectors, we skip to the next ":not" if the
|
|
9061
|
-
// current one doesn't match
|
|
9062
|
-
let skipToNextSelector = false;
|
|
9063
|
-
for (let i = 0; i < selector.length; i++) {
|
|
9064
|
-
const current = selector[i];
|
|
9065
|
-
if (typeof current === 'number') {
|
|
9066
|
-
// If we finish processing a :not selector and it hasn't failed, return false
|
|
9067
|
-
if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
|
|
9068
|
-
return false;
|
|
9069
|
-
}
|
|
9070
|
-
// If we are skipping to the next :not() and this mode flag is positive,
|
|
9071
|
-
// it's a part of the current :not() selector, and we should keep skipping
|
|
9072
|
-
if (skipToNextSelector && isPositive(current))
|
|
9073
|
-
continue;
|
|
9074
|
-
skipToNextSelector = false;
|
|
9075
|
-
mode = current | (mode & 1 /* SelectorFlags.NOT */);
|
|
9076
|
-
continue;
|
|
9077
|
-
}
|
|
9078
|
-
if (skipToNextSelector)
|
|
9079
|
-
continue;
|
|
9080
|
-
if (mode & 4 /* SelectorFlags.ELEMENT */) {
|
|
9081
|
-
mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
|
|
9082
|
-
if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
|
|
9083
|
-
current === '' && selector.length === 1) {
|
|
9084
|
-
if (isPositive(mode))
|
|
9085
|
-
return false;
|
|
9086
|
-
skipToNextSelector = true;
|
|
9087
|
-
}
|
|
9088
|
-
}
|
|
9089
|
-
else {
|
|
9090
|
-
const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
|
|
9091
|
-
// special case for matching against classes when a tNode has been instantiated with
|
|
9092
|
-
// class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
|
|
9093
|
-
if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
|
|
9094
|
-
if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
|
|
9095
|
-
if (isPositive(mode))
|
|
9096
|
-
return false;
|
|
9097
|
-
skipToNextSelector = true;
|
|
9098
|
-
}
|
|
9099
|
-
continue;
|
|
9100
|
-
}
|
|
9101
|
-
const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
|
|
9102
|
-
const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
|
|
9103
|
-
if (attrIndexInNode === -1) {
|
|
9104
|
-
if (isPositive(mode))
|
|
9105
|
-
return false;
|
|
9106
|
-
skipToNextSelector = true;
|
|
9107
|
-
continue;
|
|
9108
|
-
}
|
|
9109
|
-
if (selectorAttrValue !== '') {
|
|
9110
|
-
let nodeAttrValue;
|
|
9111
|
-
if (attrIndexInNode > nameOnlyMarkerIdx) {
|
|
9112
|
-
nodeAttrValue = '';
|
|
9113
|
-
}
|
|
9114
|
-
else {
|
|
9115
|
-
ngDevMode &&
|
|
9116
|
-
assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
|
|
9117
|
-
// we lowercase the attribute value to be able to match
|
|
9118
|
-
// selectors without case-sensitivity
|
|
9119
|
-
// (selectors are already in lowercase when generated)
|
|
9120
|
-
nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
|
|
9121
|
-
}
|
|
9122
|
-
const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
|
|
9123
|
-
if (compareAgainstClassName &&
|
|
9124
|
-
classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
|
|
9125
|
-
mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
|
|
9126
|
-
if (isPositive(mode))
|
|
9127
|
-
return false;
|
|
9128
|
-
skipToNextSelector = true;
|
|
9129
|
-
}
|
|
9130
|
-
}
|
|
9131
|
-
}
|
|
9680
|
+
class Version {
|
|
9681
|
+
constructor(full) {
|
|
9682
|
+
this.full = full;
|
|
9683
|
+
this.major = full.split('.')[0];
|
|
9684
|
+
this.minor = full.split('.')[1];
|
|
9685
|
+
this.patch = full.split('.').slice(2).join('.');
|
|
9132
9686
|
}
|
|
9133
|
-
return isPositive(mode) || skipToNextSelector;
|
|
9134
9687
|
}
|
|
9135
|
-
|
|
9136
|
-
|
|
9688
|
+
/**
|
|
9689
|
+
* @publicApi
|
|
9690
|
+
*/
|
|
9691
|
+
const VERSION = new Version('16.0.0-next.3');
|
|
9692
|
+
|
|
9693
|
+
// This default value is when checking the hierarchy for a token.
|
|
9694
|
+
//
|
|
9695
|
+
// It means both:
|
|
9696
|
+
// - the token is not provided by the current injector,
|
|
9697
|
+
// - only the element injectors should be checked (ie do not check module injectors
|
|
9698
|
+
//
|
|
9699
|
+
// mod1
|
|
9700
|
+
// /
|
|
9701
|
+
// el1 mod2
|
|
9702
|
+
// \ /
|
|
9703
|
+
// el2
|
|
9704
|
+
//
|
|
9705
|
+
// When requesting el2.injector.get(token), we should check in the following order and return the
|
|
9706
|
+
// first found value:
|
|
9707
|
+
// - el2.injector.get(token, default)
|
|
9708
|
+
// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
|
9709
|
+
// - mod2.injector.get(token, default)
|
|
9710
|
+
const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
|
9711
|
+
|
|
9712
|
+
const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
|
9713
|
+
function wrappedError(message, originalError) {
|
|
9714
|
+
const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
|
9715
|
+
const error = Error(msg);
|
|
9716
|
+
error[ERROR_ORIGINAL_ERROR] = originalError;
|
|
9717
|
+
return error;
|
|
9718
|
+
}
|
|
9719
|
+
function getOriginalError(error) {
|
|
9720
|
+
return error[ERROR_ORIGINAL_ERROR];
|
|
9137
9721
|
}
|
|
9722
|
+
|
|
9138
9723
|
/**
|
|
9139
|
-
*
|
|
9140
|
-
* attribute that matches the given `name`.
|
|
9724
|
+
* Provides a hook for centralized exception handling.
|
|
9141
9725
|
*
|
|
9142
|
-
*
|
|
9726
|
+
* The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
|
9727
|
+
* intercept error handling, write a custom exception handler that replaces this default as
|
|
9728
|
+
* appropriate for your app.
|
|
9143
9729
|
*
|
|
9144
|
-
*
|
|
9145
|
-
*
|
|
9730
|
+
* @usageNotes
|
|
9731
|
+
* ### Example
|
|
9146
9732
|
*
|
|
9147
|
-
*
|
|
9148
|
-
*
|
|
9149
|
-
*
|
|
9150
|
-
*
|
|
9151
|
-
*
|
|
9152
|
-
*
|
|
9153
|
-
* Inline + Directive | NO | NO | YES | NO
|
|
9154
|
-
* -----------------------------------------------------------------------------------------------------------
|
|
9155
|
-
* Non-inline + Projection | YES | YES | NO | YES
|
|
9156
|
-
* -----------------------------------------------------------------------------------------------------------
|
|
9157
|
-
* Non-inline + Directive | YES | YES | NO | YES
|
|
9158
|
-
* ===========================================================================================================
|
|
9733
|
+
* ```
|
|
9734
|
+
* class MyErrorHandler implements ErrorHandler {
|
|
9735
|
+
* handleError(error) {
|
|
9736
|
+
* // do something with the exception
|
|
9737
|
+
* }
|
|
9738
|
+
* }
|
|
9159
9739
|
*
|
|
9160
|
-
* @
|
|
9161
|
-
*
|
|
9162
|
-
*
|
|
9163
|
-
*
|
|
9164
|
-
*
|
|
9165
|
-
*
|
|
9740
|
+
* @NgModule({
|
|
9741
|
+
* providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
|
9742
|
+
* })
|
|
9743
|
+
* class MyModule {}
|
|
9744
|
+
* ```
|
|
9745
|
+
*
|
|
9746
|
+
* @publicApi
|
|
9166
9747
|
*/
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
while (i < attrs.length) {
|
|
9174
|
-
const maybeAttrName = attrs[i];
|
|
9175
|
-
if (maybeAttrName === name) {
|
|
9176
|
-
return i;
|
|
9177
|
-
}
|
|
9178
|
-
else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
|
|
9179
|
-
bindingsMode = true;
|
|
9180
|
-
}
|
|
9181
|
-
else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
|
|
9182
|
-
let value = attrs[++i];
|
|
9183
|
-
// We should skip classes here because we have a separate mechanism for
|
|
9184
|
-
// matching classes in projection mode.
|
|
9185
|
-
while (typeof value === 'string') {
|
|
9186
|
-
value = attrs[++i];
|
|
9187
|
-
}
|
|
9188
|
-
continue;
|
|
9189
|
-
}
|
|
9190
|
-
else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
|
|
9191
|
-
// We do not care about Template attributes in this scenario.
|
|
9192
|
-
break;
|
|
9193
|
-
}
|
|
9194
|
-
else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
|
|
9195
|
-
// Skip the whole namespaced attribute and value. This is by design.
|
|
9196
|
-
i += 4;
|
|
9197
|
-
continue;
|
|
9198
|
-
}
|
|
9199
|
-
// In binding mode there are only names, rather than name-value pairs.
|
|
9200
|
-
i += bindingsMode ? 1 : 2;
|
|
9201
|
-
}
|
|
9202
|
-
// We did not match the attribute
|
|
9203
|
-
return -1;
|
|
9204
|
-
}
|
|
9205
|
-
else {
|
|
9206
|
-
return matchTemplateAttribute(attrs, name);
|
|
9748
|
+
class ErrorHandler {
|
|
9749
|
+
constructor() {
|
|
9750
|
+
/**
|
|
9751
|
+
* @internal
|
|
9752
|
+
*/
|
|
9753
|
+
this._console = console;
|
|
9207
9754
|
}
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
if (
|
|
9212
|
-
|
|
9755
|
+
handleError(error) {
|
|
9756
|
+
const originalError = this._findOriginalError(error);
|
|
9757
|
+
this._console.error('ERROR', error);
|
|
9758
|
+
if (originalError) {
|
|
9759
|
+
this._console.error('ORIGINAL ERROR', originalError);
|
|
9213
9760
|
}
|
|
9214
9761
|
}
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
|
|
9221
|
-
// only check for ngProjectAs in attribute names, don't accidentally match attribute's value
|
|
9222
|
-
// (attribute names are stored at even indexes)
|
|
9223
|
-
if ((ngProjectAsAttrIdx & 1) === 0) {
|
|
9224
|
-
return nodeAttrs[ngProjectAsAttrIdx + 1];
|
|
9762
|
+
/** @internal */
|
|
9763
|
+
_findOriginalError(error) {
|
|
9764
|
+
let e = error && getOriginalError(error);
|
|
9765
|
+
while (e && getOriginalError(e)) {
|
|
9766
|
+
e = getOriginalError(e);
|
|
9225
9767
|
}
|
|
9768
|
+
return e || null;
|
|
9226
9769
|
}
|
|
9227
|
-
return null;
|
|
9228
9770
|
}
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9771
|
+
|
|
9772
|
+
const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
|
|
9773
|
+
/**
|
|
9774
|
+
* Internal token that specifies whether hydration is enabled.
|
|
9775
|
+
*/
|
|
9776
|
+
const IS_HYDRATION_FEATURE_ENABLED = new InjectionToken(NG_DEV_MODE ? 'IS_HYDRATION_FEATURE_ENABLED' : '');
|
|
9777
|
+
// By default (in client rendering mode), we remove all the contents
|
|
9778
|
+
// of the host element and render an application after that.
|
|
9779
|
+
const PRESERVE_HOST_CONTENT_DEFAULT = false;
|
|
9780
|
+
/**
|
|
9781
|
+
* Internal token that indicates whether host element content should be
|
|
9782
|
+
* retained during the bootstrap.
|
|
9783
|
+
*/
|
|
9784
|
+
const PRESERVE_HOST_CONTENT = new InjectionToken(NG_DEV_MODE ? 'PRESERVE_HOST_CONTENT' : '', {
|
|
9785
|
+
providedIn: 'root',
|
|
9786
|
+
factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
|
|
9787
|
+
});
|
|
9788
|
+
|
|
9789
|
+
function normalizeDebugBindingName(name) {
|
|
9790
|
+
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
|
|
9791
|
+
name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
|
|
9792
|
+
return `ng-reflect-${name}`;
|
|
9793
|
+
}
|
|
9794
|
+
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
|
9795
|
+
function camelCaseToDashCase(input) {
|
|
9796
|
+
return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
|
|
9797
|
+
}
|
|
9798
|
+
function normalizeDebugBindingValue(value) {
|
|
9799
|
+
try {
|
|
9800
|
+
// Limit the size of the value as otherwise the DOM just gets polluted.
|
|
9801
|
+
return value != null ? value.toString().slice(0, 30) : value;
|
|
9802
|
+
}
|
|
9803
|
+
catch (e) {
|
|
9804
|
+
return '[ERROR] Exception while trying to serialize the value';
|
|
9235
9805
|
}
|
|
9236
|
-
return nodeAttrs.length;
|
|
9237
9806
|
}
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
if (attr === name)
|
|
9249
|
-
return i;
|
|
9250
|
-
i++;
|
|
9251
|
-
}
|
|
9807
|
+
|
|
9808
|
+
/** Verifies that a given type is a Standalone Component. */
|
|
9809
|
+
function assertStandaloneComponentType(type) {
|
|
9810
|
+
assertComponentDef(type);
|
|
9811
|
+
const componentDef = getComponentDef$1(type);
|
|
9812
|
+
if (!componentDef.standalone) {
|
|
9813
|
+
throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
|
|
9814
|
+
`but Angular expects to have a standalone component here. ` +
|
|
9815
|
+
`Please make sure the ${stringifyForError(type)} component has ` +
|
|
9816
|
+
`the \`standalone: true\` flag in the decorator.`);
|
|
9252
9817
|
}
|
|
9253
|
-
return -1;
|
|
9254
9818
|
}
|
|
9255
|
-
/**
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
function isSelectorInSelectorList(selector, list) {
|
|
9261
|
-
selectorListLoop: for (let i = 0; i < list.length; i++) {
|
|
9262
|
-
const currentSelectorInList = list[i];
|
|
9263
|
-
if (selector.length !== currentSelectorInList.length) {
|
|
9264
|
-
continue;
|
|
9265
|
-
}
|
|
9266
|
-
for (let j = 0; j < selector.length; j++) {
|
|
9267
|
-
if (selector[j] !== currentSelectorInList[j]) {
|
|
9268
|
-
continue selectorListLoop;
|
|
9269
|
-
}
|
|
9270
|
-
}
|
|
9271
|
-
return true;
|
|
9819
|
+
/** Verifies whether a given type is a component */
|
|
9820
|
+
function assertComponentDef(type) {
|
|
9821
|
+
if (!getComponentDef$1(type)) {
|
|
9822
|
+
throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
|
|
9823
|
+
`make sure it has the \`@Component\` decorator.`);
|
|
9272
9824
|
}
|
|
9273
|
-
return false;
|
|
9274
9825
|
}
|
|
9275
|
-
|
|
9276
|
-
|
|
9826
|
+
/** Called when there are multiple component selectors that match a given node */
|
|
9827
|
+
function throwMultipleComponentError(tNode, first, second) {
|
|
9828
|
+
throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
|
|
9829
|
+
`${stringifyForError(first)} and ` +
|
|
9830
|
+
`${stringifyForError(second)}`);
|
|
9277
9831
|
}
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
let
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
if (typeof valueOrMarker === 'string') {
|
|
9287
|
-
if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
|
|
9288
|
-
const attrValue = selector[++i];
|
|
9289
|
-
currentChunk +=
|
|
9290
|
-
'[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
|
|
9291
|
-
}
|
|
9292
|
-
else if (mode & 8 /* SelectorFlags.CLASS */) {
|
|
9293
|
-
currentChunk += '.' + valueOrMarker;
|
|
9294
|
-
}
|
|
9295
|
-
else if (mode & 4 /* SelectorFlags.ELEMENT */) {
|
|
9296
|
-
currentChunk += ' ' + valueOrMarker;
|
|
9297
|
-
}
|
|
9298
|
-
}
|
|
9299
|
-
else {
|
|
9300
|
-
//
|
|
9301
|
-
// Append current chunk to the final result in case we come across SelectorFlag, which
|
|
9302
|
-
// indicates that the previous section of a selector is over. We need to accumulate content
|
|
9303
|
-
// between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
|
|
9304
|
-
// ```
|
|
9305
|
-
// ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
|
|
9306
|
-
// ```
|
|
9307
|
-
// should be transformed to `.classA :not(.classB .classC)`.
|
|
9308
|
-
//
|
|
9309
|
-
// Note: for negative selector part, we accumulate content between flags until we find the
|
|
9310
|
-
// next negative flag. This is needed to support a case where `:not()` rule contains more than
|
|
9311
|
-
// one chunk, e.g. the following selector:
|
|
9312
|
-
// ```
|
|
9313
|
-
// ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
|
|
9314
|
-
// ```
|
|
9315
|
-
// should be stringified to `:not(p.foo) :not(.bar)`
|
|
9316
|
-
//
|
|
9317
|
-
if (currentChunk !== '' && !isPositive(valueOrMarker)) {
|
|
9318
|
-
result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
|
9319
|
-
currentChunk = '';
|
|
9320
|
-
}
|
|
9321
|
-
mode = valueOrMarker;
|
|
9322
|
-
// According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
|
9323
|
-
// mode is maintained for remaining chunks of a selector.
|
|
9324
|
-
isNegativeMode = isNegativeMode || !isPositive(mode);
|
|
9325
|
-
}
|
|
9326
|
-
i++;
|
|
9327
|
-
}
|
|
9328
|
-
if (currentChunk !== '') {
|
|
9329
|
-
result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
|
9832
|
+
/** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
|
|
9833
|
+
function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName) {
|
|
9834
|
+
const field = propName ? ` for '${propName}'` : '';
|
|
9835
|
+
let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`;
|
|
9836
|
+
if (creationMode) {
|
|
9837
|
+
msg +=
|
|
9838
|
+
` It seems like the view has been created after its parent and its children have been dirty checked.` +
|
|
9839
|
+
` Has it been created in a change detection hook?`;
|
|
9330
9840
|
}
|
|
9331
|
-
|
|
9841
|
+
throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
|
|
9332
9842
|
}
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
* @param selectorList selector in parsed form
|
|
9343
|
-
* @returns string representation of a given selector
|
|
9344
|
-
*/
|
|
9345
|
-
function stringifyCSSSelectorList(selectorList) {
|
|
9346
|
-
return selectorList.map(stringifyCSSSelector).join(',');
|
|
9843
|
+
function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
|
|
9844
|
+
const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
|
|
9845
|
+
let oldValue = prefix, newValue = prefix;
|
|
9846
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
9847
|
+
const slotIdx = rootIndex + i;
|
|
9848
|
+
oldValue += `${lView[slotIdx]}${chunks[i]}`;
|
|
9849
|
+
newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
|
|
9850
|
+
}
|
|
9851
|
+
return { propName, oldValue, newValue };
|
|
9347
9852
|
}
|
|
9348
9853
|
/**
|
|
9349
|
-
*
|
|
9350
|
-
*
|
|
9351
|
-
*
|
|
9352
|
-
* (that is created dynamically) should contain attributes and classes specified in component's CSS
|
|
9353
|
-
* selector.
|
|
9854
|
+
* Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
|
|
9855
|
+
* - property name (for property bindings or interpolations)
|
|
9856
|
+
* - old and new values, enriched using information from metadata
|
|
9354
9857
|
*
|
|
9355
|
-
*
|
|
9356
|
-
*
|
|
9858
|
+
* More information on the metadata storage format can be found in `storePropertyBindingMetadata`
|
|
9859
|
+
* function description.
|
|
9357
9860
|
*/
|
|
9358
|
-
function
|
|
9359
|
-
const
|
|
9360
|
-
const
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
if (typeof valueOrMarker === 'string') {
|
|
9366
|
-
if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
|
|
9367
|
-
if (valueOrMarker !== '') {
|
|
9368
|
-
attrs.push(valueOrMarker, selector[++i]);
|
|
9369
|
-
}
|
|
9370
|
-
}
|
|
9371
|
-
else if (mode === 8 /* SelectorFlags.CLASS */) {
|
|
9372
|
-
classes.push(valueOrMarker);
|
|
9373
|
-
}
|
|
9861
|
+
function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
|
|
9862
|
+
const tData = lView[TVIEW].data;
|
|
9863
|
+
const metadata = tData[bindingIndex];
|
|
9864
|
+
if (typeof metadata === 'string') {
|
|
9865
|
+
// metadata for property interpolation
|
|
9866
|
+
if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
|
|
9867
|
+
return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
|
|
9374
9868
|
}
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9869
|
+
// metadata for property binding
|
|
9870
|
+
return { propName: metadata, oldValue, newValue };
|
|
9871
|
+
}
|
|
9872
|
+
// metadata is not available for this expression, check if this expression is a part of the
|
|
9873
|
+
// property interpolation by going from the current binding index left and look for a string that
|
|
9874
|
+
// contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
|
|
9875
|
+
// [..., 'id�Prefix � and � suffix', null, null, null, ...]
|
|
9876
|
+
if (metadata === null) {
|
|
9877
|
+
let idx = bindingIndex - 1;
|
|
9878
|
+
while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
|
|
9879
|
+
idx--;
|
|
9880
|
+
}
|
|
9881
|
+
const meta = tData[idx];
|
|
9882
|
+
if (typeof meta === 'string') {
|
|
9883
|
+
const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
|
|
9884
|
+
// first interpolation delimiter separates property name from interpolation parts (in case of
|
|
9885
|
+
// property interpolations), so we subtract one from total number of found delimiters
|
|
9886
|
+
if (matches && (matches.length - 1) > bindingIndex - idx) {
|
|
9887
|
+
return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
|
|
9888
|
+
}
|
|
9382
9889
|
}
|
|
9383
|
-
i++;
|
|
9384
9890
|
}
|
|
9385
|
-
return {
|
|
9891
|
+
return { propName: undefined, oldValue, newValue };
|
|
9386
9892
|
}
|
|
9387
9893
|
|
|
9388
9894
|
/** A special value which designates that a value has not changed. */
|
|
@@ -9441,6 +9947,33 @@ function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
|
|
|
9441
9947
|
setSelectedIndex(index);
|
|
9442
9948
|
}
|
|
9443
9949
|
|
|
9950
|
+
/**
|
|
9951
|
+
* Runs the given function in the context of the given `Injector`.
|
|
9952
|
+
*
|
|
9953
|
+
* Within the function's stack frame, `inject` can be used to inject dependencies from the given
|
|
9954
|
+
* `Injector`. Note that `inject` is only usable synchronously, and cannot be used in any
|
|
9955
|
+
* asynchronous callbacks or after any `await` points.
|
|
9956
|
+
*
|
|
9957
|
+
* @param injector the injector which will satisfy calls to `inject` while `fn` is executing
|
|
9958
|
+
* @param fn the closure to be run in the context of `injector`
|
|
9959
|
+
* @returns the return value of the function, if any
|
|
9960
|
+
* @publicApi
|
|
9961
|
+
*/
|
|
9962
|
+
function runInInjectionContext(injector, fn) {
|
|
9963
|
+
if (injector instanceof R3Injector) {
|
|
9964
|
+
injector.assertNotDestroyed();
|
|
9965
|
+
}
|
|
9966
|
+
const prevInjector = setCurrentInjector(injector);
|
|
9967
|
+
const previousInjectImplementation = setInjectImplementation(undefined);
|
|
9968
|
+
try {
|
|
9969
|
+
return fn();
|
|
9970
|
+
}
|
|
9971
|
+
finally {
|
|
9972
|
+
setCurrentInjector(prevInjector);
|
|
9973
|
+
setInjectImplementation(previousInjectImplementation);
|
|
9974
|
+
}
|
|
9975
|
+
}
|
|
9976
|
+
|
|
9444
9977
|
/**
|
|
9445
9978
|
* A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
|
|
9446
9979
|
*
|
|
@@ -10496,7 +11029,7 @@ function renderChildComponents(hostLView, components) {
|
|
|
10496
11029
|
renderComponent(hostLView, components[i]);
|
|
10497
11030
|
}
|
|
10498
11031
|
}
|
|
10499
|
-
function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector) {
|
|
11032
|
+
function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector, hydrationInfo) {
|
|
10500
11033
|
const lView = tView.blueprint.slice();
|
|
10501
11034
|
lView[HOST] = host;
|
|
10502
11035
|
lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 64 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */;
|
|
@@ -10516,6 +11049,7 @@ function createLView(parentLView, tView, context, flags, host, tHostNode, render
|
|
|
10516
11049
|
lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null;
|
|
10517
11050
|
lView[T_HOST] = tHostNode;
|
|
10518
11051
|
lView[ID] = getUniqueLViewId();
|
|
11052
|
+
lView[HYDRATION] = hydrationInfo;
|
|
10519
11053
|
lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector;
|
|
10520
11054
|
ngDevMode &&
|
|
10521
11055
|
assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
|
|
@@ -10878,7 +11412,7 @@ function getOrCreateComponentTView(def) {
|
|
|
10878
11412
|
// Declaration node here is null since this function is called when we dynamically create a
|
|
10879
11413
|
// component and hence there is no declaration.
|
|
10880
11414
|
const declTNode = null;
|
|
10881
|
-
return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts);
|
|
11415
|
+
return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts, def.id);
|
|
10882
11416
|
}
|
|
10883
11417
|
return tView;
|
|
10884
11418
|
}
|
|
@@ -10895,7 +11429,7 @@ function getOrCreateComponentTView(def) {
|
|
|
10895
11429
|
* @param schemas Schemas for this view
|
|
10896
11430
|
* @param consts Constants for this view
|
|
10897
11431
|
*/
|
|
10898
|
-
function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) {
|
|
11432
|
+
function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory, ssrId) {
|
|
10899
11433
|
ngDevMode && ngDevMode.tView++;
|
|
10900
11434
|
const bindingStartIndex = HEADER_OFFSET + decls;
|
|
10901
11435
|
// This length does not yet contain host bindings from child directives because at this point,
|
|
@@ -10934,7 +11468,8 @@ function createTView(type, declTNode, templateFn, decls, vars, directives, pipes
|
|
|
10934
11468
|
firstChild: null,
|
|
10935
11469
|
schemas: schemas,
|
|
10936
11470
|
consts: consts,
|
|
10937
|
-
incompleteFirstPass: false
|
|
11471
|
+
incompleteFirstPass: false,
|
|
11472
|
+
ssrId,
|
|
10938
11473
|
};
|
|
10939
11474
|
if (ngDevMode) {
|
|
10940
11475
|
// For performance reasons it is important that the tView retains the same shape during runtime.
|
|
@@ -10954,14 +11489,58 @@ function createViewBlueprint(bindingStartIndex, initialViewLength) {
|
|
|
10954
11489
|
/**
|
|
10955
11490
|
* Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
|
|
10956
11491
|
*
|
|
10957
|
-
* @param
|
|
11492
|
+
* @param renderer the renderer used to locate the element.
|
|
10958
11493
|
* @param elementOrSelector Render element or CSS selector to locate the element.
|
|
10959
11494
|
* @param encapsulation View Encapsulation defined for component that requests host element.
|
|
11495
|
+
* @param injector Root view injector instance.
|
|
11496
|
+
*/
|
|
11497
|
+
function locateHostElement(renderer, elementOrSelector, encapsulation, injector) {
|
|
11498
|
+
// Note: we use default value for the `PRESERVE_HOST_CONTENT` here even though it's a
|
|
11499
|
+
// tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic
|
|
11500
|
+
// component creation (after calling ViewContainerRef.createComponent) when an injector
|
|
11501
|
+
// instance can be provided. The injector instance might be disconnected from the main DI
|
|
11502
|
+
// tree, thus the `PRESERVE_HOST_CONTENT` woild not be able to instantiate. In this case, the
|
|
11503
|
+
// default value will be used.
|
|
11504
|
+
const preserveHostContent = injector.get(PRESERVE_HOST_CONTENT, PRESERVE_HOST_CONTENT_DEFAULT);
|
|
11505
|
+
// When using native Shadow DOM, do not clear host element to allow native slot
|
|
11506
|
+
// projection.
|
|
11507
|
+
const preserveContent = preserveHostContent || encapsulation === ViewEncapsulation.ShadowDom;
|
|
11508
|
+
const rootElement = renderer.selectRootElement(elementOrSelector, preserveContent);
|
|
11509
|
+
applyRootElementTransform(rootElement);
|
|
11510
|
+
return rootElement;
|
|
11511
|
+
}
|
|
11512
|
+
/**
|
|
11513
|
+
* Applies any root element transformations that are needed. If hydration is enabled,
|
|
11514
|
+
* this will process corrupted text nodes.
|
|
11515
|
+
*
|
|
11516
|
+
* @param rootElement the app root HTML Element
|
|
11517
|
+
*/
|
|
11518
|
+
function applyRootElementTransform(rootElement) {
|
|
11519
|
+
_applyRootElementTransformImpl(rootElement);
|
|
11520
|
+
}
|
|
11521
|
+
/**
|
|
11522
|
+
* Reference to a function that applies transformations to the root HTML element
|
|
11523
|
+
* of an app. When hydration is enabled, this processes any corrupt text nodes
|
|
11524
|
+
* so they are properly hydratable on the client.
|
|
11525
|
+
*
|
|
11526
|
+
* @param rootElement the app root HTML Element
|
|
11527
|
+
*/
|
|
11528
|
+
let _applyRootElementTransformImpl = (rootElement) => null;
|
|
11529
|
+
/**
|
|
11530
|
+
* Processes text node markers before hydration begins. This replaces any special comment
|
|
11531
|
+
* nodes that were added prior to serialization are swapped out to restore proper text
|
|
11532
|
+
* nodes before hydration.
|
|
11533
|
+
*
|
|
11534
|
+
* @param rootElement the app root HTML Element
|
|
11535
|
+
*/
|
|
11536
|
+
function applyRootElementTransformImpl(rootElement) {
|
|
11537
|
+
processTextNodeMarkersBeforeHydration(rootElement);
|
|
11538
|
+
}
|
|
11539
|
+
/**
|
|
11540
|
+
* Sets the implementation for the `applyRootElementTransform` function.
|
|
10960
11541
|
*/
|
|
10961
|
-
function
|
|
10962
|
-
|
|
10963
|
-
const preserveContent = encapsulation === ViewEncapsulation.ShadowDom;
|
|
10964
|
-
return renderer.selectRootElement(elementOrSelector, preserveContent);
|
|
11542
|
+
function enableApplyRootElementTransformImpl() {
|
|
11543
|
+
_applyRootElementTransformImpl = applyRootElementTransformImpl;
|
|
10965
11544
|
}
|
|
10966
11545
|
/**
|
|
10967
11546
|
* Saves context for this cleanup function in LView.cleanupInstances.
|
|
@@ -10973,9 +11552,9 @@ function locateHostElement(renderer, elementOrSelector, encapsulation) {
|
|
|
10973
11552
|
function storeCleanupWithContext(tView, lView, context, cleanupFn) {
|
|
10974
11553
|
const lCleanup = getOrCreateLViewCleanup(lView);
|
|
10975
11554
|
// Historically the `storeCleanupWithContext` was used to register both framework-level and
|
|
10976
|
-
// user-defined cleanup callbacks, but over time those two types of cleanups were separated.
|
|
10977
|
-
// dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
|
|
10978
|
-
// reserved for framework-specific hooks.
|
|
11555
|
+
// user-defined cleanup callbacks, but over time those two types of cleanups were separated.
|
|
11556
|
+
// This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
|
|
11557
|
+
// structures reserved for framework-specific hooks.
|
|
10979
11558
|
ngDevMode &&
|
|
10980
11559
|
assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks');
|
|
10981
11560
|
lCleanup.push(context);
|
|
@@ -11017,7 +11596,7 @@ function createTNode(tView, tParent, type, index, value, attrs) {
|
|
|
11017
11596
|
initialInputs: undefined,
|
|
11018
11597
|
inputs: null,
|
|
11019
11598
|
outputs: null,
|
|
11020
|
-
|
|
11599
|
+
tView: null,
|
|
11021
11600
|
next: null,
|
|
11022
11601
|
prev: null,
|
|
11023
11602
|
projectionNext: null,
|
|
@@ -11560,7 +12139,7 @@ function addComponentLogic(lView, hostTNode, def) {
|
|
|
11560
12139
|
// Only component views should be added to the view tree directly. Embedded views are
|
|
11561
12140
|
// accessed through their containers because they may be removed / re-added later.
|
|
11562
12141
|
const rendererFactory = lView[RENDERER_FACTORY];
|
|
11563
|
-
const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null));
|
|
12142
|
+
const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null, null));
|
|
11564
12143
|
// Component view will always be created before any injected LContainers,
|
|
11565
12144
|
// so this is a regular element, wrap it with the component view
|
|
11566
12145
|
lView[hostTNode.index] = componentView;
|
|
@@ -11693,7 +12272,8 @@ function createLContainer(hostNative, currentView, native, tNode) {
|
|
|
11693
12272
|
tNode,
|
|
11694
12273
|
native,
|
|
11695
12274
|
null,
|
|
11696
|
-
null,
|
|
12275
|
+
null,
|
|
12276
|
+
null, // dehydrated views
|
|
11697
12277
|
];
|
|
11698
12278
|
ngDevMode &&
|
|
11699
12279
|
assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
|
|
@@ -11805,6 +12385,11 @@ function renderComponent(hostLView, componentHostIdx) {
|
|
|
11805
12385
|
const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
|
11806
12386
|
const componentTView = componentView[TVIEW];
|
|
11807
12387
|
syncViewWithBlueprint(componentTView, componentView);
|
|
12388
|
+
const hostRNode = componentView[HOST];
|
|
12389
|
+
// Populate an LView with hydration info retrieved from the DOM via TransferState.
|
|
12390
|
+
if (hostRNode !== null && componentView[HYDRATION] === null) {
|
|
12391
|
+
componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
|
|
12392
|
+
}
|
|
11808
12393
|
renderView(componentTView, componentView, componentView[CONTEXT]);
|
|
11809
12394
|
}
|
|
11810
12395
|
/**
|
|
@@ -12505,13 +13090,13 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
12505
13090
|
// dynamically. Default to 'div' if this component did not specify any tag name in its selector.
|
|
12506
13091
|
const elementName = this.componentDef.selectors[0][0] || 'div';
|
|
12507
13092
|
const hostRNode = rootSelectorOrNode ?
|
|
12508
|
-
locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
|
|
13093
|
+
locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation, rootViewInjector) :
|
|
12509
13094
|
createElementNode(hostRenderer, elementName, getNamespace(elementName));
|
|
12510
13095
|
const rootFlags = this.componentDef.onPush ? 32 /* LViewFlags.Dirty */ | 256 /* LViewFlags.IsRoot */ :
|
|
12511
13096
|
16 /* LViewFlags.CheckAlways */ | 256 /* LViewFlags.IsRoot */;
|
|
12512
13097
|
// Create the root view. Uses empty TView and ContentTemplate.
|
|
12513
|
-
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null);
|
|
12514
|
-
const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null);
|
|
13098
|
+
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null, null);
|
|
13099
|
+
const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null, null);
|
|
12515
13100
|
// rootView is the parent when bootstrapping
|
|
12516
13101
|
// TODO(misko): it looks like we are entering view here but we don't really need to as
|
|
12517
13102
|
// `renderView` does that. However as the code is written it is needed because
|
|
@@ -12622,7 +13207,7 @@ function createRootComponentTNode(lView, rNode) {
|
|
|
12622
13207
|
/**
|
|
12623
13208
|
* Creates the root component view and the root component node.
|
|
12624
13209
|
*
|
|
12625
|
-
* @param
|
|
13210
|
+
* @param hostRNode Render host element.
|
|
12626
13211
|
* @param rootComponentDef ComponentDef
|
|
12627
13212
|
* @param rootView The parent view where the host node is stored
|
|
12628
13213
|
* @param rendererFactory Factory to be used for creating child renderers.
|
|
@@ -12631,11 +13216,17 @@ function createRootComponentTNode(lView, rNode) {
|
|
|
12631
13216
|
*
|
|
12632
13217
|
* @returns Component view created
|
|
12633
13218
|
*/
|
|
12634
|
-
function createRootComponentView(tNode,
|
|
13219
|
+
function createRootComponentView(tNode, hostRNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
|
|
12635
13220
|
const tView = rootView[TVIEW];
|
|
12636
|
-
applyRootComponentStyling(rootDirectives, tNode,
|
|
12637
|
-
|
|
12638
|
-
|
|
13221
|
+
applyRootComponentStyling(rootDirectives, tNode, hostRNode, hostRenderer);
|
|
13222
|
+
// Hydration info is on the host element and needs to be retreived
|
|
13223
|
+
// and passed to the component LView.
|
|
13224
|
+
let hydrationInfo = null;
|
|
13225
|
+
if (hostRNode !== null) {
|
|
13226
|
+
hydrationInfo = retrieveHydrationInfo(hostRNode, rootView[INJECTOR$1]);
|
|
13227
|
+
}
|
|
13228
|
+
const viewRenderer = rendererFactory.createRenderer(hostRNode, rootComponentDef);
|
|
13229
|
+
const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null, hydrationInfo);
|
|
12639
13230
|
if (tView.firstCreatePass) {
|
|
12640
13231
|
markAsComponentHost(tView, tNode, rootDirectives.length - 1);
|
|
12641
13232
|
}
|
|
@@ -13789,15 +14380,122 @@ function detectChanges(component) {
|
|
|
13789
14380
|
detectChangesInternal(view[TVIEW], view, component);
|
|
13790
14381
|
}
|
|
13791
14382
|
|
|
14383
|
+
/**
|
|
14384
|
+
* Verifies whether a given node matches an expected criteria,
|
|
14385
|
+
* based on internal data structure state.
|
|
14386
|
+
*/
|
|
14387
|
+
function validateMatchingNode(node, nodeType, tagName, lView, tNode) {
|
|
14388
|
+
validateNodeExists(node);
|
|
14389
|
+
if (node.nodeType !== nodeType ||
|
|
14390
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
14391
|
+
node.tagName.toLowerCase() !== tagName?.toLowerCase()) {
|
|
14392
|
+
// TODO: improve error message and use RuntimeError instead.
|
|
14393
|
+
throw new Error(`Unexpected node found during hydration.`);
|
|
14394
|
+
}
|
|
14395
|
+
}
|
|
14396
|
+
/**
|
|
14397
|
+
* Verifies whether next sibling node exists.
|
|
14398
|
+
*/
|
|
14399
|
+
function validateSiblingNodeExists(node) {
|
|
14400
|
+
validateNodeExists(node);
|
|
14401
|
+
if (!node.nextSibling) {
|
|
14402
|
+
// TODO: improve error message and use RuntimeError instead.
|
|
14403
|
+
throw new Error(`Unexpected state: insufficient number of sibling nodes.`);
|
|
14404
|
+
}
|
|
14405
|
+
}
|
|
14406
|
+
function validateNodeExists(node) {
|
|
14407
|
+
if (!node) {
|
|
14408
|
+
// TODO: improve error message and use RuntimeError instead.
|
|
14409
|
+
throw new Error(`Hydration expected an element to be present at this location.`);
|
|
14410
|
+
}
|
|
14411
|
+
}
|
|
14412
|
+
|
|
14413
|
+
/** Whether current TNode is a first node in an <ng-container>. */
|
|
14414
|
+
function isFirstElementInNgContainer(tNode) {
|
|
14415
|
+
return !tNode.prev && tNode.parent?.type === 8 /* TNodeType.ElementContainer */;
|
|
14416
|
+
}
|
|
14417
|
+
/**
|
|
14418
|
+
* Locate a node in DOM tree that corresponds to a given TNode.
|
|
14419
|
+
*
|
|
14420
|
+
* @param hydrationInfo The hydration annotation data
|
|
14421
|
+
* @param tView the current tView
|
|
14422
|
+
* @param lView the current lView
|
|
14423
|
+
* @param tNode the current tNode
|
|
14424
|
+
* @returns an RNode that represents a given tNode
|
|
14425
|
+
*/
|
|
14426
|
+
function locateNextRNode(hydrationInfo, tView, lView, tNode) {
|
|
14427
|
+
let native = null;
|
|
14428
|
+
if (tView.firstChild === tNode) {
|
|
14429
|
+
// We create a first node in this view, so we use a reference
|
|
14430
|
+
// to the first child in this DOM segment.
|
|
14431
|
+
native = hydrationInfo.firstChild;
|
|
14432
|
+
}
|
|
14433
|
+
else {
|
|
14434
|
+
// Locate a node based on a previous sibling or a parent node.
|
|
14435
|
+
const previousTNodeParent = tNode.prev === null;
|
|
14436
|
+
const previousTNode = (tNode.prev ?? tNode.parent);
|
|
14437
|
+
ngDevMode &&
|
|
14438
|
+
assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
|
|
14439
|
+
'to the previous node or a parent node.');
|
|
14440
|
+
if (isFirstElementInNgContainer(tNode)) {
|
|
14441
|
+
const noOffsetParentIndex = tNode.parent.index - HEADER_OFFSET;
|
|
14442
|
+
native = getSegmentHead(hydrationInfo, noOffsetParentIndex);
|
|
14443
|
+
}
|
|
14444
|
+
else {
|
|
14445
|
+
let previousRElement = getNativeByTNode(previousTNode, lView);
|
|
14446
|
+
if (previousTNodeParent) {
|
|
14447
|
+
native = previousRElement.firstChild;
|
|
14448
|
+
}
|
|
14449
|
+
else {
|
|
14450
|
+
// If the previous node is an element, but it also has container info,
|
|
14451
|
+
// this means that we are processing a node like `<div #vcrTarget>`, which is
|
|
14452
|
+
// represented in the DOM as `<div></div>...<!--container-->`.
|
|
14453
|
+
// In this case, there are nodes *after* this element and we need to skip
|
|
14454
|
+
// all of them to reach an element that we are looking for.
|
|
14455
|
+
const noOffsetPrevSiblingIndex = previousTNode.index - HEADER_OFFSET;
|
|
14456
|
+
const segmentHead = getSegmentHead(hydrationInfo, noOffsetPrevSiblingIndex);
|
|
14457
|
+
if (previousTNode.type === 2 /* TNodeType.Element */ && segmentHead) {
|
|
14458
|
+
const numRootNodesToSkip = calcSerializedContainerSize(hydrationInfo, noOffsetPrevSiblingIndex);
|
|
14459
|
+
// `+1` stands for an anchor comment node after all the views in this container.
|
|
14460
|
+
const nodesToSkip = numRootNodesToSkip + 1;
|
|
14461
|
+
// First node after this segment.
|
|
14462
|
+
native = siblingAfter(nodesToSkip, segmentHead);
|
|
14463
|
+
}
|
|
14464
|
+
else {
|
|
14465
|
+
native = previousRElement.nextSibling;
|
|
14466
|
+
}
|
|
14467
|
+
}
|
|
14468
|
+
}
|
|
14469
|
+
}
|
|
14470
|
+
return native;
|
|
14471
|
+
}
|
|
14472
|
+
/**
|
|
14473
|
+
* Skips over a specified number of nodes and returns the next sibling node after that.
|
|
14474
|
+
*/
|
|
14475
|
+
function siblingAfter(skip, from) {
|
|
14476
|
+
let currentNode = from;
|
|
14477
|
+
for (let i = 0; i < skip; i++) {
|
|
14478
|
+
ngDevMode && validateSiblingNodeExists(currentNode);
|
|
14479
|
+
currentNode = currentNode.nextSibling;
|
|
14480
|
+
}
|
|
14481
|
+
return currentNode;
|
|
14482
|
+
}
|
|
14483
|
+
|
|
13792
14484
|
function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
|
|
13793
14485
|
ngDevMode && assertFirstCreatePass(tView);
|
|
13794
14486
|
ngDevMode && ngDevMode.firstCreatePass++;
|
|
13795
14487
|
const tViewConsts = tView.consts;
|
|
14488
|
+
let ssrId = null;
|
|
14489
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14490
|
+
if (hydrationInfo) {
|
|
14491
|
+
const noOffsetIndex = index - HEADER_OFFSET;
|
|
14492
|
+
ssrId = (hydrationInfo && hydrationInfo.data[TEMPLATES]?.[noOffsetIndex]) ?? null;
|
|
14493
|
+
}
|
|
13796
14494
|
// TODO(pk): refactor getOrCreateTNode to have the "create" only version
|
|
13797
14495
|
const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
|
|
13798
14496
|
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
|
|
13799
14497
|
registerPostOrderHooks(tView, tNode);
|
|
13800
|
-
const embeddedTView = tNode.
|
|
14498
|
+
const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, ssrId);
|
|
13801
14499
|
if (tView.queries !== null) {
|
|
13802
14500
|
tView.queries.template(tView, tNode);
|
|
13803
14501
|
embeddedTView.queries = tView.queries.embeddedTView(tNode);
|
|
@@ -13830,8 +14528,10 @@ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, local
|
|
|
13830
14528
|
const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) :
|
|
13831
14529
|
tView.data[adjustedIndex];
|
|
13832
14530
|
setCurrentTNode(tNode, false);
|
|
13833
|
-
const comment =
|
|
13834
|
-
|
|
14531
|
+
const comment = _locateOrCreateContainerAnchor(tView, lView, tNode, index);
|
|
14532
|
+
if (wasLastNodeCreated()) {
|
|
14533
|
+
appendChild(tView, lView, comment, tNode);
|
|
14534
|
+
}
|
|
13835
14535
|
attachPatchData(comment, lView);
|
|
13836
14536
|
addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
|
|
13837
14537
|
if (isDirectiveHost(tNode)) {
|
|
@@ -13841,6 +14541,45 @@ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, local
|
|
|
13841
14541
|
saveResolvedLocalsInData(lView, tNode, localRefExtractor);
|
|
13842
14542
|
}
|
|
13843
14543
|
}
|
|
14544
|
+
let _locateOrCreateContainerAnchor = createContainerAnchorImpl;
|
|
14545
|
+
/**
|
|
14546
|
+
* Regular creation mode for LContainers and their anchor (comment) nodes.
|
|
14547
|
+
*/
|
|
14548
|
+
function createContainerAnchorImpl(tView, lView, tNode, index) {
|
|
14549
|
+
lastNodeWasCreated(true);
|
|
14550
|
+
return lView[RENDERER].createComment(ngDevMode ? 'container' : '');
|
|
14551
|
+
}
|
|
14552
|
+
/**
|
|
14553
|
+
* Enables hydration code path (to lookup existing elements in DOM)
|
|
14554
|
+
* in addition to the regular creation mode for LContainers and their
|
|
14555
|
+
* anchor (comment) nodes.
|
|
14556
|
+
*/
|
|
14557
|
+
function locateOrCreateContainerAnchorImpl(tView, lView, tNode, index) {
|
|
14558
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14559
|
+
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
|
|
14560
|
+
lastNodeWasCreated(isNodeCreationMode);
|
|
14561
|
+
// Regular creation mode.
|
|
14562
|
+
if (isNodeCreationMode) {
|
|
14563
|
+
return createContainerAnchorImpl(tView, lView, tNode, index);
|
|
14564
|
+
}
|
|
14565
|
+
// Hydration mode, looking up existing elements in DOM.
|
|
14566
|
+
const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
14567
|
+
ngDevMode && validateNodeExists(currentRNode);
|
|
14568
|
+
const viewContainerSize = calcSerializedContainerSize(hydrationInfo, index);
|
|
14569
|
+
// If this container is non-empty, store the first node as a segment head,
|
|
14570
|
+
// otherwise, this node is an anchor and segment head doesn't exist (thus `null`).
|
|
14571
|
+
const segmentHead = viewContainerSize > 0 ? currentRNode : null;
|
|
14572
|
+
setSegmentHead(hydrationInfo, index, segmentHead);
|
|
14573
|
+
const comment = siblingAfter(viewContainerSize, currentRNode);
|
|
14574
|
+
if (ngDevMode) {
|
|
14575
|
+
validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
|
|
14576
|
+
markRNodeAsClaimedByHydration(comment);
|
|
14577
|
+
}
|
|
14578
|
+
return comment;
|
|
14579
|
+
}
|
|
14580
|
+
function enableLocateOrCreateContainerAnchorImpl() {
|
|
14581
|
+
_locateOrCreateContainerAnchor = locateOrCreateContainerAnchorImpl;
|
|
14582
|
+
}
|
|
13844
14583
|
|
|
13845
14584
|
/** Store a value in the `data` at a given `index`. */
|
|
13846
14585
|
function store(tView, lView, index, value) {
|
|
@@ -13867,6 +14606,48 @@ function ɵɵreference(index) {
|
|
|
13867
14606
|
return load(contextLView, HEADER_OFFSET + index);
|
|
13868
14607
|
}
|
|
13869
14608
|
|
|
14609
|
+
/**
|
|
14610
|
+
* The name of an attribute that can be added to the hydration boundary node
|
|
14611
|
+
* (component host node) to disable hydration for the content within that boundary.
|
|
14612
|
+
*/
|
|
14613
|
+
const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration';
|
|
14614
|
+
/**
|
|
14615
|
+
* Helper function to check if a given node has the 'ngSkipHydration' attribute
|
|
14616
|
+
*/
|
|
14617
|
+
function hasNgSkipHydrationAttr(tNode) {
|
|
14618
|
+
const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = SKIP_HYDRATION_ATTR_NAME.toLowerCase();
|
|
14619
|
+
const attrs = tNode.mergedAttrs;
|
|
14620
|
+
if (attrs === null)
|
|
14621
|
+
return false;
|
|
14622
|
+
// only ever look at the attribute name and skip the values
|
|
14623
|
+
for (let i = 0; i < attrs.length; i += 2) {
|
|
14624
|
+
const value = attrs[i];
|
|
14625
|
+
// This is a marker, which means that the static attributes section is over,
|
|
14626
|
+
// so we can exit early.
|
|
14627
|
+
if (typeof value === 'number')
|
|
14628
|
+
return false;
|
|
14629
|
+
if (typeof value === 'string' && value.toLowerCase() === SKIP_HYDRATION_ATTR_NAME_LOWER_CASE) {
|
|
14630
|
+
return true;
|
|
14631
|
+
}
|
|
14632
|
+
}
|
|
14633
|
+
return false;
|
|
14634
|
+
}
|
|
14635
|
+
/**
|
|
14636
|
+
* Helper function that determines if a given node is within a skip hydration block
|
|
14637
|
+
* by navigating up the TNode tree to see if any parent nodes have skip hydration
|
|
14638
|
+
* attribute.
|
|
14639
|
+
*/
|
|
14640
|
+
function isInSkipHydrationBlock(tNode) {
|
|
14641
|
+
let currentTNode = tNode.parent;
|
|
14642
|
+
while (currentTNode) {
|
|
14643
|
+
if (hasNgSkipHydrationAttr(currentTNode)) {
|
|
14644
|
+
return true;
|
|
14645
|
+
}
|
|
14646
|
+
currentTNode = currentTNode.parent;
|
|
14647
|
+
}
|
|
14648
|
+
return false;
|
|
14649
|
+
}
|
|
14650
|
+
|
|
13870
14651
|
/**
|
|
13871
14652
|
* Update a property on a selected element.
|
|
13872
14653
|
*
|
|
@@ -13951,14 +14732,15 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
|
|
|
13951
14732
|
const tNode = tView.firstCreatePass ?
|
|
13952
14733
|
elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) :
|
|
13953
14734
|
tView.data[adjustedIndex];
|
|
13954
|
-
const native = lView
|
|
14735
|
+
const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
|
|
14736
|
+
lView[adjustedIndex] = native;
|
|
13955
14737
|
const hasDirectives = isDirectiveHost(tNode);
|
|
13956
14738
|
if (ngDevMode && tView.firstCreatePass) {
|
|
13957
14739
|
validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives);
|
|
13958
14740
|
}
|
|
13959
14741
|
setCurrentTNode(tNode, true);
|
|
13960
14742
|
setupStaticAttributes(renderer, native, tNode);
|
|
13961
|
-
if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
|
|
14743
|
+
if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */ && wasLastNodeCreated()) {
|
|
13962
14744
|
// In the i18n case, the translation may have removed this element, so only add it if it is not
|
|
13963
14745
|
// detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
|
|
13964
14746
|
appendChild(tView, lView, native, tNode);
|
|
@@ -13998,6 +14780,9 @@ function ɵɵelementEnd() {
|
|
|
13998
14780
|
}
|
|
13999
14781
|
const tNode = currentTNode;
|
|
14000
14782
|
ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
|
|
14783
|
+
if (isSkipHydrationRootTNode(tNode)) {
|
|
14784
|
+
leaveSkipHydrationBlock();
|
|
14785
|
+
}
|
|
14001
14786
|
decreaseElementDepthCount();
|
|
14002
14787
|
const tView = getTView();
|
|
14003
14788
|
if (tView.firstCreatePass) {
|
|
@@ -14030,6 +14815,50 @@ function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
|
|
|
14030
14815
|
ɵɵelementEnd();
|
|
14031
14816
|
return ɵɵelement;
|
|
14032
14817
|
}
|
|
14818
|
+
let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
|
|
14819
|
+
lastNodeWasCreated(true);
|
|
14820
|
+
return createElementNode(renderer, name, getNamespace$1());
|
|
14821
|
+
};
|
|
14822
|
+
/**
|
|
14823
|
+
* Enables hydration code path (to lookup existing elements in DOM)
|
|
14824
|
+
* in addition to the regular creation mode of element nodes.
|
|
14825
|
+
*/
|
|
14826
|
+
function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, index) {
|
|
14827
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14828
|
+
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
|
|
14829
|
+
lastNodeWasCreated(isNodeCreationMode);
|
|
14830
|
+
// Regular creation mode.
|
|
14831
|
+
if (isNodeCreationMode) {
|
|
14832
|
+
return createElementNode(renderer, name, getNamespace$1());
|
|
14833
|
+
}
|
|
14834
|
+
// Hydration mode, looking up an existing element in DOM.
|
|
14835
|
+
const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
14836
|
+
ngDevMode && validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
|
|
14837
|
+
ngDevMode && markRNodeAsClaimedByHydration(native);
|
|
14838
|
+
// This element might also be an anchor of a view container.
|
|
14839
|
+
if (getSerializedContainerViews(hydrationInfo, index)) {
|
|
14840
|
+
// Important note: this element acts as an anchor, but it's **not** a part
|
|
14841
|
+
// of the embedded view, so we start the segment **after** this element, taking
|
|
14842
|
+
// a reference to the next sibling. For example, the following template:
|
|
14843
|
+
// `<div #vcrTarget>` is represented in the DOM as `<div></div>...<!--container-->`,
|
|
14844
|
+
// so while processing a `<div>` instruction, point to the next sibling as a
|
|
14845
|
+
// start of a segment.
|
|
14846
|
+
ngDevMode && validateNodeExists(native.nextSibling);
|
|
14847
|
+
setSegmentHead(hydrationInfo, index, native.nextSibling);
|
|
14848
|
+
}
|
|
14849
|
+
// Checks if the skip hydration attribute is present during hydration so we know to
|
|
14850
|
+
// skip attempting to hydrate this block.
|
|
14851
|
+
if (hydrationInfo && hasNgSkipHydrationAttr(tNode)) {
|
|
14852
|
+
enterSkipHydrationBlock(tNode);
|
|
14853
|
+
// Since this isn't hydratable, we need to empty the node
|
|
14854
|
+
// so there's no duplicate content after render
|
|
14855
|
+
clearElementContents(renderer, native);
|
|
14856
|
+
}
|
|
14857
|
+
return native;
|
|
14858
|
+
}
|
|
14859
|
+
function enableLocateOrCreateElementNodeImpl() {
|
|
14860
|
+
_locateOrCreateElementNode = locateOrCreateElementNodeImpl;
|
|
14861
|
+
}
|
|
14033
14862
|
|
|
14034
14863
|
function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) {
|
|
14035
14864
|
ngDevMode && ngDevMode.firstCreatePass++;
|
|
@@ -14075,10 +14904,12 @@ function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
|
|
|
14075
14904
|
tView.data[adjustedIndex];
|
|
14076
14905
|
setCurrentTNode(tNode, true);
|
|
14077
14906
|
ngDevMode && ngDevMode.rendererCreateComment++;
|
|
14078
|
-
const
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14907
|
+
const comment = _locateOrCreateElementContainerNode(tView, lView, tNode, index);
|
|
14908
|
+
lView[adjustedIndex] = comment;
|
|
14909
|
+
if (wasLastNodeCreated()) {
|
|
14910
|
+
appendChild(tView, lView, comment, tNode);
|
|
14911
|
+
}
|
|
14912
|
+
attachPatchData(comment, lView);
|
|
14082
14913
|
if (isDirectiveHost(tNode)) {
|
|
14083
14914
|
createDirectivesInstances(tView, lView, tNode);
|
|
14084
14915
|
executeContentQueries(tView, tNode, lView);
|
|
@@ -14130,6 +14961,45 @@ function ɵɵelementContainer(index, attrsIndex, localRefsIndex) {
|
|
|
14130
14961
|
ɵɵelementContainerEnd();
|
|
14131
14962
|
return ɵɵelementContainer;
|
|
14132
14963
|
}
|
|
14964
|
+
let _locateOrCreateElementContainerNode = (tView, lView, tNode, index) => {
|
|
14965
|
+
lastNodeWasCreated(true);
|
|
14966
|
+
return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
|
|
14967
|
+
};
|
|
14968
|
+
/**
|
|
14969
|
+
* Enables hydration code path (to lookup existing elements in DOM)
|
|
14970
|
+
* in addition to the regular creation mode of comment nodes that
|
|
14971
|
+
* represent <ng-container>'s anchor.
|
|
14972
|
+
*/
|
|
14973
|
+
function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
|
|
14974
|
+
let comment;
|
|
14975
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14976
|
+
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
|
|
14977
|
+
lastNodeWasCreated(isNodeCreationMode);
|
|
14978
|
+
// Regular creation mode.
|
|
14979
|
+
if (isNodeCreationMode) {
|
|
14980
|
+
return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
|
|
14981
|
+
}
|
|
14982
|
+
// Hydration mode, looking up existing elements in DOM.
|
|
14983
|
+
const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
14984
|
+
ngDevMode && validateNodeExists(currentRNode);
|
|
14985
|
+
const ngContainerSize = getNgContainerSize(hydrationInfo, index);
|
|
14986
|
+
ngDevMode &&
|
|
14987
|
+
assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
|
|
14988
|
+
'but no hydration info is available.');
|
|
14989
|
+
// If this container is non-empty, store the first node as a segment head,
|
|
14990
|
+
// otherwise, this node is an anchor and segment head doesn't exist (thus `null`).
|
|
14991
|
+
const segmentHead = ngContainerSize > 0 ? currentRNode : null;
|
|
14992
|
+
setSegmentHead(hydrationInfo, index, segmentHead);
|
|
14993
|
+
comment = siblingAfter(ngContainerSize, currentRNode);
|
|
14994
|
+
if (ngDevMode) {
|
|
14995
|
+
validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
|
|
14996
|
+
markRNodeAsClaimedByHydration(comment);
|
|
14997
|
+
}
|
|
14998
|
+
return comment;
|
|
14999
|
+
}
|
|
15000
|
+
function enableLocateOrCreateElementContainerNodeImpl() {
|
|
15001
|
+
_locateOrCreateElementContainerNode = locateOrCreateElementContainerNode;
|
|
15002
|
+
}
|
|
14133
15003
|
|
|
14134
15004
|
/**
|
|
14135
15005
|
* Returns the current OpaqueViewState instance.
|
|
@@ -14158,16 +15028,6 @@ function isPromise(obj) {
|
|
|
14158
15028
|
function isSubscribable(obj) {
|
|
14159
15029
|
return !!obj && typeof obj.subscribe === 'function';
|
|
14160
15030
|
}
|
|
14161
|
-
/**
|
|
14162
|
-
* Determine if the argument is an Observable
|
|
14163
|
-
*
|
|
14164
|
-
* Strictly this tests that the `obj` is `Subscribable`, since `Observable`
|
|
14165
|
-
* types need additional methods, such as `lift()`. But it is adequate for our
|
|
14166
|
-
* needs since within the Angular framework code we only ever need to use the
|
|
14167
|
-
* `subscribe()` method, and RxJS has mechanisms to wrap `Subscribable` objects
|
|
14168
|
-
* into `Observable` as needed.
|
|
14169
|
-
*/
|
|
14170
|
-
const isObservable = isSubscribable;
|
|
14171
15031
|
|
|
14172
15032
|
/**
|
|
14173
15033
|
* Adds an event listener to the current node.
|
|
@@ -14330,7 +15190,7 @@ function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn,
|
|
|
14330
15190
|
const minifiedName = props[i + 1];
|
|
14331
15191
|
const directiveInstance = lView[index];
|
|
14332
15192
|
const output = directiveInstance[minifiedName];
|
|
14333
|
-
if (ngDevMode && !
|
|
15193
|
+
if (ngDevMode && !isSubscribable(output)) {
|
|
14334
15194
|
throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
|
|
14335
15195
|
}
|
|
14336
15196
|
const subscription = output.subscribe(listenerFn);
|
|
@@ -16474,11 +17334,39 @@ function ɵɵtext(index, value = '') {
|
|
|
16474
17334
|
const tNode = tView.firstCreatePass ?
|
|
16475
17335
|
getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
|
|
16476
17336
|
tView.data[adjustedIndex];
|
|
16477
|
-
const textNative =
|
|
16478
|
-
|
|
17337
|
+
const textNative = _locateOrCreateTextNode(tView, lView, tNode, value);
|
|
17338
|
+
lView[adjustedIndex] = textNative;
|
|
17339
|
+
if (wasLastNodeCreated()) {
|
|
17340
|
+
appendChild(tView, lView, textNative, tNode);
|
|
17341
|
+
}
|
|
16479
17342
|
// Text nodes are self closing.
|
|
16480
17343
|
setCurrentTNode(tNode, false);
|
|
16481
17344
|
}
|
|
17345
|
+
let _locateOrCreateTextNode = (tView, lView, tNode, value) => {
|
|
17346
|
+
lastNodeWasCreated(true);
|
|
17347
|
+
return createTextNode(lView[RENDERER], value);
|
|
17348
|
+
};
|
|
17349
|
+
/**
|
|
17350
|
+
* Enables hydration code path (to lookup existing elements in DOM)
|
|
17351
|
+
* in addition to the regular creation mode of text nodes.
|
|
17352
|
+
*/
|
|
17353
|
+
function locateOrCreateTextNodeImpl(tView, lView, tNode, value) {
|
|
17354
|
+
const hydrationInfo = lView[HYDRATION];
|
|
17355
|
+
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
|
|
17356
|
+
lastNodeWasCreated(isNodeCreationMode);
|
|
17357
|
+
// Regular creation mode.
|
|
17358
|
+
if (isNodeCreationMode) {
|
|
17359
|
+
return createTextNode(lView[RENDERER], value);
|
|
17360
|
+
}
|
|
17361
|
+
// Hydration mode, looking up an existing element in DOM.
|
|
17362
|
+
const textNative = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
17363
|
+
ngDevMode && validateMatchingNode(textNative, Node.TEXT_NODE, null, lView, tNode);
|
|
17364
|
+
ngDevMode && markRNodeAsClaimedByHydration(textNative);
|
|
17365
|
+
return textNative;
|
|
17366
|
+
}
|
|
17367
|
+
function enableLocateOrCreateTextNodeImpl() {
|
|
17368
|
+
_locateOrCreateTextNode = locateOrCreateTextNodeImpl;
|
|
17369
|
+
}
|
|
16482
17370
|
|
|
16483
17371
|
/**
|
|
16484
17372
|
*
|
|
@@ -18156,7 +19044,7 @@ function getTIcu(tView, index) {
|
|
|
18156
19044
|
if (value === null || typeof value === 'string')
|
|
18157
19045
|
return null;
|
|
18158
19046
|
if (ngDevMode &&
|
|
18159
|
-
!(value.hasOwnProperty('
|
|
19047
|
+
!(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
|
|
18160
19048
|
throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
|
|
18161
19049
|
}
|
|
18162
19050
|
// Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
|
|
@@ -18185,7 +19073,7 @@ function getTIcu(tView, index) {
|
|
|
18185
19073
|
function setTIcu(tView, index, tIcu) {
|
|
18186
19074
|
const tNode = tView.data[index];
|
|
18187
19075
|
ngDevMode &&
|
|
18188
|
-
assertEqual(tNode === null || tNode.hasOwnProperty('
|
|
19076
|
+
assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
|
|
18189
19077
|
if (tNode === null) {
|
|
18190
19078
|
tView.data[index] = tIcu;
|
|
18191
19079
|
}
|
|
@@ -20132,7 +21020,7 @@ class NgModuleFactory$1 {
|
|
|
20132
21020
|
* @publicApi
|
|
20133
21021
|
*/
|
|
20134
21022
|
function createNgModule(ngModule, parentInjector) {
|
|
20135
|
-
return new NgModuleRef(ngModule, parentInjector ?? null);
|
|
21023
|
+
return new NgModuleRef(ngModule, parentInjector ?? null, []);
|
|
20136
21024
|
}
|
|
20137
21025
|
/**
|
|
20138
21026
|
* The `createNgModule` function alias for backwards-compatibility.
|
|
@@ -20142,7 +21030,7 @@ function createNgModule(ngModule, parentInjector) {
|
|
|
20142
21030
|
*/
|
|
20143
21031
|
const createNgModuleRef = createNgModule;
|
|
20144
21032
|
class NgModuleRef extends NgModuleRef$1 {
|
|
20145
|
-
constructor(ngModuleType, _parent) {
|
|
21033
|
+
constructor(ngModuleType, _parent, additionalProviders) {
|
|
20146
21034
|
super();
|
|
20147
21035
|
this._parent = _parent;
|
|
20148
21036
|
// tslint:disable-next-line:require-internal-with-underscore
|
|
@@ -20163,7 +21051,8 @@ class NgModuleRef extends NgModuleRef$1 {
|
|
|
20163
21051
|
{ provide: NgModuleRef$1, useValue: this }, {
|
|
20164
21052
|
provide: ComponentFactoryResolver$1,
|
|
20165
21053
|
useValue: this.componentFactoryResolver
|
|
20166
|
-
}
|
|
21054
|
+
},
|
|
21055
|
+
...additionalProviders
|
|
20167
21056
|
], stringify(ngModuleType), new Set(['environment']));
|
|
20168
21057
|
// We need to resolve the injector types separately from the injector creation, because
|
|
20169
21058
|
// the module might be trying to use this ref in its constructor for DI which will cause a
|
|
@@ -20192,9 +21081,12 @@ class NgModuleFactory extends NgModuleFactory$1 {
|
|
|
20192
21081
|
this.moduleType = moduleType;
|
|
20193
21082
|
}
|
|
20194
21083
|
create(parentInjector) {
|
|
20195
|
-
return new NgModuleRef(this.moduleType, parentInjector);
|
|
21084
|
+
return new NgModuleRef(this.moduleType, parentInjector, []);
|
|
20196
21085
|
}
|
|
20197
21086
|
}
|
|
21087
|
+
function createNgModuleRefWithProviders(moduleType, parentInjector, additionalProviders) {
|
|
21088
|
+
return new NgModuleRef(moduleType, parentInjector, additionalProviders);
|
|
21089
|
+
}
|
|
20198
21090
|
class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 {
|
|
20199
21091
|
constructor(providers, parent, source) {
|
|
20200
21092
|
super();
|
|
@@ -20642,7 +21534,8 @@ function sortListeners(a, b) {
|
|
|
20642
21534
|
* See call site for more info.
|
|
20643
21535
|
*/
|
|
20644
21536
|
function isDirectiveDefHack(obj) {
|
|
20645
|
-
return obj.type !== undefined && obj.
|
|
21537
|
+
return obj.type !== undefined && obj.declaredInputs !== undefined &&
|
|
21538
|
+
obj.findHostDirectiveDefs !== undefined;
|
|
20646
21539
|
}
|
|
20647
21540
|
/**
|
|
20648
21541
|
* Retrieve the component `LView` from component/element.
|
|
@@ -21479,9 +22372,24 @@ const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
|
|
|
21479
22372
|
this._declarationTContainer = _declarationTContainer;
|
|
21480
22373
|
this.elementRef = elementRef;
|
|
21481
22374
|
}
|
|
22375
|
+
/**
|
|
22376
|
+
* Returns an `ssrId` associated with a TView, which was used to
|
|
22377
|
+
* create this instance of the `TemplateRef`.
|
|
22378
|
+
*
|
|
22379
|
+
* @internal
|
|
22380
|
+
*/
|
|
22381
|
+
get ssrId() {
|
|
22382
|
+
return this._declarationTContainer.tView?.ssrId || null;
|
|
22383
|
+
}
|
|
21482
22384
|
createEmbeddedView(context, injector) {
|
|
21483
|
-
|
|
21484
|
-
|
|
22385
|
+
return this.createEmbeddedViewImpl(context, injector, null);
|
|
22386
|
+
}
|
|
22387
|
+
/**
|
|
22388
|
+
* @internal
|
|
22389
|
+
*/
|
|
22390
|
+
createEmbeddedViewImpl(context, injector, hydrationInfo) {
|
|
22391
|
+
const embeddedTView = this._declarationTContainer.tView;
|
|
22392
|
+
const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null, hydrationInfo || null);
|
|
21485
22393
|
const declarationLContainer = this._declarationLView[this._declarationTContainer.index];
|
|
21486
22394
|
ngDevMode && assertLContainer(declarationLContainer);
|
|
21487
22395
|
embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
|
|
@@ -21510,12 +22418,118 @@ function injectTemplateRef() {
|
|
|
21510
22418
|
*/
|
|
21511
22419
|
function createTemplateRef(hostTNode, hostLView) {
|
|
21512
22420
|
if (hostTNode.type & 4 /* TNodeType.Container */) {
|
|
21513
|
-
ngDevMode && assertDefined(hostTNode.
|
|
22421
|
+
ngDevMode && assertDefined(hostTNode.tView, 'TView must be allocated');
|
|
21514
22422
|
return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView));
|
|
21515
22423
|
}
|
|
21516
22424
|
return null;
|
|
21517
22425
|
}
|
|
21518
22426
|
|
|
22427
|
+
/**
|
|
22428
|
+
* Removes all dehydrated views from a given LContainer:
|
|
22429
|
+
* both in internal data structure, as well as removing
|
|
22430
|
+
* corresponding DOM nodes that belong to that dehydrated view.
|
|
22431
|
+
*/
|
|
22432
|
+
function removeDehydratedViews(lContainer) {
|
|
22433
|
+
const views = lContainer[DEHYDRATED_VIEWS] ?? [];
|
|
22434
|
+
const parentLView = lContainer[PARENT];
|
|
22435
|
+
const renderer = parentLView[RENDERER];
|
|
22436
|
+
for (const view of views) {
|
|
22437
|
+
removeDehydratedView(view, renderer);
|
|
22438
|
+
ngDevMode && ngDevMode.dehydratedViewsRemoved++;
|
|
22439
|
+
}
|
|
22440
|
+
// Reset the value to an empty array to indicate that no
|
|
22441
|
+
// further processing of dehydrated views is needed for
|
|
22442
|
+
// this view container (i.e. do not trigger the lookup process
|
|
22443
|
+
// once again in case a `ViewContainerRef` is created later).
|
|
22444
|
+
lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
|
|
22445
|
+
}
|
|
22446
|
+
/**
|
|
22447
|
+
* Helper function to remove all nodes from a dehydrated view.
|
|
22448
|
+
*/
|
|
22449
|
+
function removeDehydratedView(dehydratedView, renderer) {
|
|
22450
|
+
let nodesRemoved = 0;
|
|
22451
|
+
let currentRNode = dehydratedView.firstChild;
|
|
22452
|
+
if (currentRNode) {
|
|
22453
|
+
const numNodes = dehydratedView.data[NUM_ROOT_NODES];
|
|
22454
|
+
while (nodesRemoved < numNodes) {
|
|
22455
|
+
ngDevMode && validateSiblingNodeExists(currentRNode);
|
|
22456
|
+
const nextSibling = currentRNode.nextSibling;
|
|
22457
|
+
nativeRemoveNode(renderer, currentRNode, false);
|
|
22458
|
+
currentRNode = nextSibling;
|
|
22459
|
+
nodesRemoved++;
|
|
22460
|
+
}
|
|
22461
|
+
}
|
|
22462
|
+
}
|
|
22463
|
+
|
|
22464
|
+
/**
|
|
22465
|
+
* Given a current DOM node and a serialized information about the views
|
|
22466
|
+
* in a container, walks over the DOM structure, collecting the list of
|
|
22467
|
+
* dehydrated views.
|
|
22468
|
+
*/
|
|
22469
|
+
function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
|
|
22470
|
+
const dehydratedViews = [];
|
|
22471
|
+
for (const serializedView of serializedViews) {
|
|
22472
|
+
const view = {
|
|
22473
|
+
data: serializedView,
|
|
22474
|
+
firstChild: null,
|
|
22475
|
+
};
|
|
22476
|
+
if (serializedView[NUM_ROOT_NODES] > 0) {
|
|
22477
|
+
// Keep reference to the first node in this view,
|
|
22478
|
+
// so it can be accessed while invoking template instructions.
|
|
22479
|
+
view.firstChild = currentRNode;
|
|
22480
|
+
// Move over to the next node after this view, which can
|
|
22481
|
+
// either be a first node of the next view or an anchor comment
|
|
22482
|
+
// node after the last view in a container.
|
|
22483
|
+
currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
|
|
22484
|
+
}
|
|
22485
|
+
dehydratedViews.push(view);
|
|
22486
|
+
}
|
|
22487
|
+
return [currentRNode, dehydratedViews];
|
|
22488
|
+
}
|
|
22489
|
+
/**
|
|
22490
|
+
* Reference to a function that searches for a matching dehydrated views
|
|
22491
|
+
* stored on a given lContainer.
|
|
22492
|
+
* Returns `null` by default, when hydration is not enabled.
|
|
22493
|
+
*/
|
|
22494
|
+
let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
|
|
22495
|
+
/**
|
|
22496
|
+
* Retrieves the next dehydrated view from the LContainer and verifies that
|
|
22497
|
+
* it matches a given template id (from the TView that was used to create this
|
|
22498
|
+
* instance of a view). If the id doesn't match, that means that we are in an
|
|
22499
|
+
* unexpected state and can not complete the reconciliation process. Thus,
|
|
22500
|
+
* all dehydrated views from this LContainer are removed (including corresponding
|
|
22501
|
+
* DOM nodes) and the rendering is performed as if there were no dehydrated views
|
|
22502
|
+
* in this container.
|
|
22503
|
+
*/
|
|
22504
|
+
function findMatchingDehydratedViewImpl(lContainer, template) {
|
|
22505
|
+
const views = lContainer[DEHYDRATED_VIEWS] ?? [];
|
|
22506
|
+
if (!template || views.length === 0) {
|
|
22507
|
+
return null;
|
|
22508
|
+
}
|
|
22509
|
+
const view = views[0];
|
|
22510
|
+
// Verify whether the first dehydrated view in the container matches
|
|
22511
|
+
// the template id passed to this function (that originated from a TView
|
|
22512
|
+
// that was used to create an instance of an embedded or component views.
|
|
22513
|
+
if (view.data[TEMPLATE_ID] === template) {
|
|
22514
|
+
// If the template id matches - extract the first view and return it.
|
|
22515
|
+
return views.shift();
|
|
22516
|
+
}
|
|
22517
|
+
else {
|
|
22518
|
+
// Otherwise, we are at the state when reconciliation can not be completed,
|
|
22519
|
+
// thus we remove all dehydrated views within this container (remove them
|
|
22520
|
+
// from internal data structures as well as delete associated elements from
|
|
22521
|
+
// the DOM tree).
|
|
22522
|
+
removeDehydratedViews(lContainer);
|
|
22523
|
+
return null;
|
|
22524
|
+
}
|
|
22525
|
+
}
|
|
22526
|
+
function enableFindMatchingDehydratedViewImpl() {
|
|
22527
|
+
_findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
|
|
22528
|
+
}
|
|
22529
|
+
function findMatchingDehydratedView(lContainer, template) {
|
|
22530
|
+
return _findMatchingDehydratedViewImpl(lContainer, template);
|
|
22531
|
+
}
|
|
22532
|
+
|
|
21519
22533
|
/**
|
|
21520
22534
|
* Represents a container where one or more views can be attached to a component.
|
|
21521
22535
|
*
|
|
@@ -21600,8 +22614,9 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|
|
21600
22614
|
index = indexOrOptions.index;
|
|
21601
22615
|
injector = indexOrOptions.injector;
|
|
21602
22616
|
}
|
|
21603
|
-
const
|
|
21604
|
-
|
|
22617
|
+
const hydrationInfo = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
|
|
22618
|
+
const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, hydrationInfo);
|
|
22619
|
+
this.insertImpl(viewRef, index, !!hydrationInfo);
|
|
21605
22620
|
return viewRef;
|
|
21606
22621
|
}
|
|
21607
22622
|
createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
|
|
@@ -21671,11 +22686,17 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|
|
21671
22686
|
environmentInjector = result;
|
|
21672
22687
|
}
|
|
21673
22688
|
}
|
|
21674
|
-
const
|
|
21675
|
-
this.
|
|
22689
|
+
const componentDef = getComponentDef$1(componentFactory.componentType ?? {});
|
|
22690
|
+
const dehydratedView = findMatchingDehydratedView(this._lContainer, componentDef?.id ?? null);
|
|
22691
|
+
const rNode = dehydratedView?.firstChild ?? null;
|
|
22692
|
+
const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
|
|
22693
|
+
this.insertImpl(componentRef.hostView, index, !!dehydratedView);
|
|
21676
22694
|
return componentRef;
|
|
21677
22695
|
}
|
|
21678
22696
|
insert(viewRef, index) {
|
|
22697
|
+
return this.insertImpl(viewRef, index, false);
|
|
22698
|
+
}
|
|
22699
|
+
insertImpl(viewRef, index, skipDomInsertion) {
|
|
21679
22700
|
const lView = viewRef._lView;
|
|
21680
22701
|
const tView = lView[TVIEW];
|
|
21681
22702
|
if (ngDevMode && viewRef.destroyed) {
|
|
@@ -21706,11 +22727,13 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|
|
21706
22727
|
const lContainer = this._lContainer;
|
|
21707
22728
|
insertView(tView, lView, lContainer, adjustedIdx);
|
|
21708
22729
|
// Physical operation of adding the DOM nodes.
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
21712
|
-
|
|
21713
|
-
|
|
22730
|
+
if (!skipDomInsertion) {
|
|
22731
|
+
const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer);
|
|
22732
|
+
const renderer = lView[RENDERER];
|
|
22733
|
+
const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
|
|
22734
|
+
if (parentRNode !== null) {
|
|
22735
|
+
addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
|
|
22736
|
+
}
|
|
21714
22737
|
}
|
|
21715
22738
|
viewRef.attachToViewContainerRef();
|
|
21716
22739
|
addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
|
|
@@ -21767,8 +22790,6 @@ function getOrCreateViewRefs(lContainer) {
|
|
|
21767
22790
|
/**
|
|
21768
22791
|
* Creates a ViewContainerRef and stores it on the injector.
|
|
21769
22792
|
*
|
|
21770
|
-
* @param ViewContainerRefToken The ViewContainerRef type
|
|
21771
|
-
* @param ElementRefToken The ElementRef type
|
|
21772
22793
|
* @param hostTNode The node that is requesting a ViewContainerRef
|
|
21773
22794
|
* @param hostLView The view to which the node belongs
|
|
21774
22795
|
* @returns The ViewContainerRef instance to use
|
|
@@ -21782,31 +22803,94 @@ function createContainerRef(hostTNode, hostLView) {
|
|
|
21782
22803
|
lContainer = slotValue;
|
|
21783
22804
|
}
|
|
21784
22805
|
else {
|
|
21785
|
-
|
|
21786
|
-
//
|
|
21787
|
-
//
|
|
21788
|
-
|
|
21789
|
-
|
|
21790
|
-
if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
|
|
21791
|
-
commentNode = unwrapRNode(slotValue);
|
|
21792
|
-
}
|
|
21793
|
-
else {
|
|
21794
|
-
// If the host is a regular element, we have to insert a comment node manually which will
|
|
21795
|
-
// be used as an anchor when inserting elements. In this specific case we use low-level DOM
|
|
21796
|
-
// manipulation to insert it.
|
|
21797
|
-
const renderer = hostLView[RENDERER];
|
|
21798
|
-
ngDevMode && ngDevMode.rendererCreateComment++;
|
|
21799
|
-
commentNode = renderer.createComment(ngDevMode ? 'container' : '');
|
|
21800
|
-
const hostNative = getNativeByTNode(hostTNode, hostLView);
|
|
21801
|
-
const parentOfHostNative = nativeParentNode(renderer, hostNative);
|
|
21802
|
-
nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
|
|
21803
|
-
}
|
|
21804
|
-
hostLView[hostTNode.index] = lContainer =
|
|
21805
|
-
createLContainer(slotValue, hostLView, commentNode, hostTNode);
|
|
22806
|
+
// An LContainer anchor can not be `null`, but we set it here temporarily
|
|
22807
|
+
// and update to the actual value later in this function (see
|
|
22808
|
+
// `_locateOrCreateAnchorNode`).
|
|
22809
|
+
lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
|
|
22810
|
+
hostLView[hostTNode.index] = lContainer;
|
|
21806
22811
|
addToViewTree(hostLView, lContainer);
|
|
21807
22812
|
}
|
|
22813
|
+
_locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
|
|
21808
22814
|
return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
|
|
21809
22815
|
}
|
|
22816
|
+
/**
|
|
22817
|
+
* Creates and inserts a comment node that acts as an anchor for a view container.
|
|
22818
|
+
*
|
|
22819
|
+
* If the host is a regular element, we have to insert a comment node manually which will
|
|
22820
|
+
* be used as an anchor when inserting elements. In this specific case we use low-level DOM
|
|
22821
|
+
* manipulation to insert it.
|
|
22822
|
+
*/
|
|
22823
|
+
function insertAnchorNode(hostLView, hostTNode) {
|
|
22824
|
+
const renderer = hostLView[RENDERER];
|
|
22825
|
+
ngDevMode && ngDevMode.rendererCreateComment++;
|
|
22826
|
+
const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
|
|
22827
|
+
const hostNative = getNativeByTNode(hostTNode, hostLView);
|
|
22828
|
+
const parentOfHostNative = nativeParentNode(renderer, hostNative);
|
|
22829
|
+
nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
|
|
22830
|
+
return commentNode;
|
|
22831
|
+
}
|
|
22832
|
+
let _locateOrCreateAnchorNode = createAnchorNode;
|
|
22833
|
+
/**
|
|
22834
|
+
* Regular creation mode: an anchor is created and
|
|
22835
|
+
* assigned to the `lContainer[NATIVE]` slot.
|
|
22836
|
+
*/
|
|
22837
|
+
function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
|
|
22838
|
+
// We already have a native element (anchor) set, return.
|
|
22839
|
+
if (lContainer[NATIVE])
|
|
22840
|
+
return;
|
|
22841
|
+
let commentNode;
|
|
22842
|
+
// If the host is an element container, the native host element is guaranteed to be a
|
|
22843
|
+
// comment and we can reuse that comment as anchor element for the new LContainer.
|
|
22844
|
+
// The comment node in question is already part of the DOM structure so we don't need to append
|
|
22845
|
+
// it again.
|
|
22846
|
+
if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
|
|
22847
|
+
commentNode = unwrapRNode(slotValue);
|
|
22848
|
+
}
|
|
22849
|
+
else {
|
|
22850
|
+
commentNode = insertAnchorNode(hostLView, hostTNode);
|
|
22851
|
+
}
|
|
22852
|
+
lContainer[NATIVE] = commentNode;
|
|
22853
|
+
}
|
|
22854
|
+
/**
|
|
22855
|
+
* Hydration logic that looks up:
|
|
22856
|
+
* - an anchor node in the DOM and stores the node in `lContainer[NATIVE]`
|
|
22857
|
+
* - all dehydrated views in this container and puts them into `lContainer[DEHYDRATED_VIEWS]`
|
|
22858
|
+
*/
|
|
22859
|
+
function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
|
|
22860
|
+
// We already have a native element (anchor) set and the process
|
|
22861
|
+
// of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
|
|
22862
|
+
// is not null), exit early.
|
|
22863
|
+
if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS])
|
|
22864
|
+
return;
|
|
22865
|
+
const hydrationInfo = hostLView[HYDRATION];
|
|
22866
|
+
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock(hostTNode);
|
|
22867
|
+
// Regular creation mode.
|
|
22868
|
+
if (isNodeCreationMode) {
|
|
22869
|
+
return createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
|
|
22870
|
+
}
|
|
22871
|
+
// Hydration mode, looking up an anchor node and dehydrated views in DOM.
|
|
22872
|
+
const index = hostTNode.index - HEADER_OFFSET;
|
|
22873
|
+
const currentRNode = getSegmentHead(hydrationInfo, index);
|
|
22874
|
+
const serializedViews = hydrationInfo.data[CONTAINERS]?.[index];
|
|
22875
|
+
ngDevMode &&
|
|
22876
|
+
assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
|
|
22877
|
+
'which represents a view container.');
|
|
22878
|
+
const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
|
|
22879
|
+
if (ngDevMode) {
|
|
22880
|
+
validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, hostTNode);
|
|
22881
|
+
// Do not throw in case this node is already claimed (thus `false` as a second
|
|
22882
|
+
// argument). If this container is created based on an `<ng-template>`, the comment
|
|
22883
|
+
// node would be already claimed from the `template` instruction. If an element acts
|
|
22884
|
+
// as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
|
|
22885
|
+
// so we need to claim it here.
|
|
22886
|
+
markRNodeAsClaimedByHydration(commentNode, false);
|
|
22887
|
+
}
|
|
22888
|
+
lContainer[NATIVE] = commentNode;
|
|
22889
|
+
lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
|
|
22890
|
+
}
|
|
22891
|
+
function enableLocateOrCreateContainerRefImpl() {
|
|
22892
|
+
_locateOrCreateAnchorNode = locateOrCreateAnchorNode;
|
|
22893
|
+
}
|
|
21810
22894
|
|
|
21811
22895
|
class LQuery_ {
|
|
21812
22896
|
constructor(queryList) {
|
|
@@ -22784,6 +23868,7 @@ function resetCompiledComponents() {
|
|
|
22784
23868
|
ownerNgModule = new WeakMap();
|
|
22785
23869
|
verifiedNgModule = new WeakMap();
|
|
22786
23870
|
moduleQueue.length = 0;
|
|
23871
|
+
GENERATED_COMP_IDS.clear();
|
|
22787
23872
|
}
|
|
22788
23873
|
/**
|
|
22789
23874
|
* Computes the combined declarations of explicit declarations, as well as declarations inherited by
|
|
@@ -23371,7 +24456,7 @@ class TestBedCompiler {
|
|
|
23371
24456
|
// every component.
|
|
23372
24457
|
this.componentToModuleScope.clear();
|
|
23373
24458
|
const parentInjector = this.platform.injector;
|
|
23374
|
-
this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector);
|
|
24459
|
+
this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector, []);
|
|
23375
24460
|
// ApplicationInitStatus.runInitializers() is marked @internal to core.
|
|
23376
24461
|
// Cast it to any before accessing it.
|
|
23377
24462
|
this.testModuleRef.injector.get(ApplicationInitStatus).runInitializers();
|
|
@@ -23795,7 +24880,7 @@ class TestBedCompiler {
|
|
|
23795
24880
|
});
|
|
23796
24881
|
const ngZone = new NgZone({ enableLongStackTrace: true });
|
|
23797
24882
|
const providers = [
|
|
23798
|
-
|
|
24883
|
+
ɵprovideNgZoneChangeDetection(ngZone),
|
|
23799
24884
|
{ provide: Compiler, useFactory: () => new R3TestCompiler(this) },
|
|
23800
24885
|
...this.providers,
|
|
23801
24886
|
...this.providerOverrides,
|