feat(compiler-cli): support for partial compilation of components (#39707)
This commit implements partial compilation of components, together with linking the partial declaration into its full AOT output. This commit does not yet enable accurate source maps into external templates. This requires additional work to account for escape sequences which is non-trivial. Inline templates that were represented using a string or template literal are transplated into the partial declaration output, so their source maps should be accurate. Note, however, that the accuracy of source maps is not currently verified in tests; this is also left as future work. The golden files of partial compilation output have been updated to reflect the generated code for components. Please note that the current output should not yet be considered stable. PR Close #39707
This commit is contained in:
parent
f6be161a3c
commit
e75244ec00
|
@ -18,7 +18,7 @@ export const NO_STATEMENTS: Readonly<any[]> = [] as const;
|
||||||
* This class is responsible for linking all the partial declarations found in a single file.
|
* This class is responsible for linking all the partial declarations found in a single file.
|
||||||
*/
|
*/
|
||||||
export class FileLinker<TConstantScope, TStatement, TExpression> {
|
export class FileLinker<TConstantScope, TStatement, TExpression> {
|
||||||
private linkerSelector = new PartialLinkerSelector<TExpression>();
|
private linkerSelector = new PartialLinkerSelector<TExpression>(this.linkerEnvironment.options);
|
||||||
private emitScopes = new Map<TConstantScope, EmitScope<TStatement, TExpression>>();
|
private emitScopes = new Map<TConstantScope, EmitScope<TStatement, TExpression>>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -5,20 +5,182 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ConstantPool} from '@angular/compiler';
|
import {compileComponentFromMetadata, ConstantPool, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3UsedDirectiveMetadata} from '@angular/compiler';
|
||||||
|
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/compiler/src/core';
|
||||||
import * as o from '@angular/compiler/src/output/output_ast';
|
import * as o from '@angular/compiler/src/output/output_ast';
|
||||||
|
|
||||||
import {AstObject} from '../../ast/ast_value';
|
import {Range} from '../../ast/ast_host';
|
||||||
|
import {AstObject, AstValue} from '../../ast/ast_value';
|
||||||
|
import {FatalLinkerError} from '../../fatal_linker_error';
|
||||||
|
import {LinkerOptions} from '../linker_options';
|
||||||
|
|
||||||
|
import {toR3DirectiveMeta} from './partial_directive_linker_1';
|
||||||
import {PartialLinker} from './partial_linker';
|
import {PartialLinker} from './partial_linker';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `PartialLinker` that is designed to process `ɵɵngDeclareComponent()` call expressions.
|
* A `PartialLinker` that is designed to process `ɵɵngDeclareComponent()` call expressions.
|
||||||
*/
|
*/
|
||||||
export class PartialComponentLinkerVersion1<TExpression> implements PartialLinker<TExpression> {
|
export class PartialComponentLinkerVersion1<TExpression> implements PartialLinker<TExpression> {
|
||||||
|
constructor(private readonly options: LinkerOptions) {}
|
||||||
|
|
||||||
linkPartialDeclaration(
|
linkPartialDeclaration(
|
||||||
sourceUrl: string, code: string, constantPool: ConstantPool,
|
sourceUrl: string, code: string, constantPool: ConstantPool,
|
||||||
metaObj: AstObject<TExpression>): o.Expression {
|
metaObj: AstObject<TExpression>): o.Expression {
|
||||||
throw new Error('Not implemented.');
|
const meta = toR3ComponentMeta(metaObj, code, sourceUrl, this.options);
|
||||||
|
const def = compileComponentFromMetadata(meta, constantPool, makeBindingParser());
|
||||||
|
return def.expression;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function derives the `R3ComponentMetadata` from the provided AST object.
|
||||||
|
*/
|
||||||
|
export function toR3ComponentMeta<TExpression>(
|
||||||
|
metaObj: AstObject<TExpression>, code: string, sourceUrl: string,
|
||||||
|
options: LinkerOptions): R3ComponentMetadata {
|
||||||
|
let interpolation = DEFAULT_INTERPOLATION_CONFIG;
|
||||||
|
if (metaObj.has('interpolation')) {
|
||||||
|
interpolation = InterpolationConfig.fromArray(
|
||||||
|
metaObj.getArray('interpolation').map(entry => entry.getString()) as [string, string]);
|
||||||
|
}
|
||||||
|
const templateObj = metaObj.getObject('template');
|
||||||
|
const templateSource = templateObj.getValue('source');
|
||||||
|
const range = getTemplateRange(templateSource, code);
|
||||||
|
const isInline = templateObj.getBoolean('isInline');
|
||||||
|
|
||||||
|
// We always normalize line endings if the template is inline.
|
||||||
|
const i18nNormalizeLineEndingsInICUs = isInline || options.i18nNormalizeLineEndingsInICUs;
|
||||||
|
|
||||||
|
const template = parseTemplate(code, sourceUrl, {
|
||||||
|
escapedString: true,
|
||||||
|
interpolationConfig: interpolation,
|
||||||
|
range,
|
||||||
|
enableI18nLegacyMessageIdFormat: options.enableI18nLegacyMessageIdFormat,
|
||||||
|
preserveWhitespaces:
|
||||||
|
metaObj.has('preserveWhitespaces') ? metaObj.getBoolean('preserveWhitespaces') : false,
|
||||||
|
i18nNormalizeLineEndingsInICUs,
|
||||||
|
isInline,
|
||||||
|
});
|
||||||
|
if (template.errors !== null) {
|
||||||
|
const errors = template.errors.map(err => err.toString()).join('\n');
|
||||||
|
throw new FatalLinkerError(
|
||||||
|
templateSource.expression, `Errors found in the template:\n${errors}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wrapDirectivesAndPipesInClosure = false;
|
||||||
|
|
||||||
|
const directives: R3UsedDirectiveMetadata[] = metaObj.has('directives') ?
|
||||||
|
metaObj.getArray('directives').map(directive => {
|
||||||
|
const directiveExpr = directive.getObject();
|
||||||
|
const type = directiveExpr.getValue('type');
|
||||||
|
const selector = directiveExpr.getString('selector');
|
||||||
|
|
||||||
|
let typeExpr = type.getOpaque();
|
||||||
|
if (type.isFunction()) {
|
||||||
|
typeExpr = type.getFunctionReturnValue().getOpaque();
|
||||||
|
wrapDirectivesAndPipesInClosure = true;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: typeExpr,
|
||||||
|
selector: selector,
|
||||||
|
inputs: directiveExpr.has('inputs') ?
|
||||||
|
directiveExpr.getArray('inputs').map(input => input.getString()) :
|
||||||
|
[],
|
||||||
|
outputs: directiveExpr.has('outputs') ?
|
||||||
|
directiveExpr.getArray('outputs').map(input => input.getString()) :
|
||||||
|
[],
|
||||||
|
exportAs: directiveExpr.has('exportAs') ?
|
||||||
|
directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) :
|
||||||
|
null,
|
||||||
|
};
|
||||||
|
}) :
|
||||||
|
[];
|
||||||
|
|
||||||
|
const pipes = metaObj.has('pipes') ? metaObj.getObject('pipes').toMap(value => {
|
||||||
|
if (value.isFunction()) {
|
||||||
|
wrapDirectivesAndPipesInClosure = true;
|
||||||
|
return value.getFunctionReturnValue().getOpaque();
|
||||||
|
} else {
|
||||||
|
return value.getOpaque();
|
||||||
|
}
|
||||||
|
}) :
|
||||||
|
new Map<string, o.Expression>();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toR3DirectiveMeta(metaObj, code, sourceUrl),
|
||||||
|
viewProviders: metaObj.has('viewProviders') ? metaObj.getOpaque('viewProviders') : null,
|
||||||
|
template: {
|
||||||
|
nodes: template.nodes,
|
||||||
|
ngContentSelectors: template.ngContentSelectors,
|
||||||
|
},
|
||||||
|
wrapDirectivesAndPipesInClosure,
|
||||||
|
styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) : [],
|
||||||
|
encapsulation: metaObj.has('encapsulation') ?
|
||||||
|
parseEncapsulation(metaObj.getValue('encapsulation')) :
|
||||||
|
ViewEncapsulation.Emulated,
|
||||||
|
interpolation,
|
||||||
|
changeDetection: metaObj.has('changeDetection') ?
|
||||||
|
parseChangeDetectionStrategy(metaObj.getValue('changeDetection')) :
|
||||||
|
ChangeDetectionStrategy.Default,
|
||||||
|
animations: metaObj.has('animations') ? metaObj.getOpaque('animations') : null,
|
||||||
|
relativeContextFilePath: sourceUrl,
|
||||||
|
i18nUseExternalIds: options.i18nUseExternalIds,
|
||||||
|
pipes,
|
||||||
|
directives,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the `ViewEncapsulation` mode from the AST value's symbol name.
|
||||||
|
*/
|
||||||
|
function parseEncapsulation<TExpression>(encapsulation: AstValue<TExpression>): ViewEncapsulation {
|
||||||
|
const symbolName = encapsulation.getSymbolName();
|
||||||
|
if (symbolName === null) {
|
||||||
|
throw new FatalLinkerError(
|
||||||
|
encapsulation.expression, 'Expected encapsulation to have a symbol name');
|
||||||
|
}
|
||||||
|
const enumValue = ViewEncapsulation[symbolName as keyof typeof ViewEncapsulation];
|
||||||
|
if (enumValue === undefined) {
|
||||||
|
throw new FatalLinkerError(encapsulation.expression, 'Unsupported encapsulation');
|
||||||
|
}
|
||||||
|
return enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the `ChangeDetectionStrategy` from the AST value's symbol name.
|
||||||
|
*/
|
||||||
|
function parseChangeDetectionStrategy<TExpression>(changeDetectionStrategy: AstValue<TExpression>):
|
||||||
|
ChangeDetectionStrategy {
|
||||||
|
const symbolName = changeDetectionStrategy.getSymbolName();
|
||||||
|
if (symbolName === null) {
|
||||||
|
throw new FatalLinkerError(
|
||||||
|
changeDetectionStrategy.expression,
|
||||||
|
'Expected change detection strategy to have a symbol name');
|
||||||
|
}
|
||||||
|
const enumValue = ChangeDetectionStrategy[symbolName as keyof typeof ChangeDetectionStrategy];
|
||||||
|
if (enumValue === undefined) {
|
||||||
|
throw new FatalLinkerError(
|
||||||
|
changeDetectionStrategy.expression, 'Unsupported change detection strategy');
|
||||||
|
}
|
||||||
|
return enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the range to remove the start and end chars, which should be quotes around the template.
|
||||||
|
*/
|
||||||
|
function getTemplateRange<TExpression>(templateNode: AstValue<TExpression>, code: string): Range {
|
||||||
|
const {startPos, endPos, startLine, startCol} = templateNode.getRange();
|
||||||
|
|
||||||
|
if (!/["'`]/.test(code[startPos]) || code[startPos] !== code[endPos - 1]) {
|
||||||
|
throw new FatalLinkerError(
|
||||||
|
templateNode.expression,
|
||||||
|
`Expected the template string to be wrapped in quotes but got: ${
|
||||||
|
code.substring(startPos, endPos)}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
startPos: startPos + 1,
|
||||||
|
endPos: endPos - 1,
|
||||||
|
startLine,
|
||||||
|
startCol: startCol + 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {LinkerOptions} from '../linker_options';
|
||||||
|
|
||||||
import {PartialComponentLinkerVersion1} from './partial_component_linker_1';
|
import {PartialComponentLinkerVersion1} from './partial_component_linker_1';
|
||||||
import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1';
|
import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1';
|
||||||
import {PartialLinker} from './partial_linker';
|
import {PartialLinker} from './partial_linker';
|
||||||
|
@ -15,10 +17,12 @@ export class PartialLinkerSelector<TExpression> {
|
||||||
1: new PartialDirectiveLinkerVersion1(),
|
1: new PartialDirectiveLinkerVersion1(),
|
||||||
},
|
},
|
||||||
'ɵɵngDeclareComponent': {
|
'ɵɵngDeclareComponent': {
|
||||||
1: new PartialComponentLinkerVersion1(),
|
1: new PartialComponentLinkerVersion1(this.options),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constructor(private options: LinkerOptions) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there are `PartialLinker` classes that can handle functions with this name.
|
* Returns true if there are `PartialLinker` classes that can handle functions with this name.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,15 +6,22 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {LinkerOptions} from '../../..';
|
||||||
import {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1';
|
import {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1';
|
||||||
import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1';
|
import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1';
|
||||||
import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector';
|
import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector';
|
||||||
|
|
||||||
describe('PartialLinkerSelector', () => {
|
describe('PartialLinkerSelector', () => {
|
||||||
|
const options: LinkerOptions = {
|
||||||
|
i18nNormalizeLineEndingsInICUs: true,
|
||||||
|
enableI18nLegacyMessageIdFormat: false,
|
||||||
|
i18nUseExternalIds: false,
|
||||||
|
};
|
||||||
|
|
||||||
describe('supportsDeclaration()', () => {
|
describe('supportsDeclaration()', () => {
|
||||||
it('should return true if there is at least one linker that matches the given function name',
|
it('should return true if there is at least one linker that matches the given function name',
|
||||||
() => {
|
() => {
|
||||||
const selector = new PartialLinkerSelector();
|
const selector = new PartialLinkerSelector(options);
|
||||||
expect(selector.supportsDeclaration('ɵɵngDeclareDirective')).toBe(true);
|
expect(selector.supportsDeclaration('ɵɵngDeclareDirective')).toBe(true);
|
||||||
expect(selector.supportsDeclaration('ɵɵngDeclareComponent')).toBe(true);
|
expect(selector.supportsDeclaration('ɵɵngDeclareComponent')).toBe(true);
|
||||||
expect(selector.supportsDeclaration('$foo')).toBe(false);
|
expect(selector.supportsDeclaration('$foo')).toBe(false);
|
||||||
|
@ -23,7 +30,7 @@ describe('PartialLinkerSelector', () => {
|
||||||
|
|
||||||
describe('getLinker()', () => {
|
describe('getLinker()', () => {
|
||||||
it('should return the linker that matches the name and version number', () => {
|
it('should return the linker that matches the name and version number', () => {
|
||||||
const selector = new PartialLinkerSelector();
|
const selector = new PartialLinkerSelector(options);
|
||||||
expect(selector.getLinker('ɵɵngDeclareDirective', 1))
|
expect(selector.getLinker('ɵɵngDeclareDirective', 1))
|
||||||
.toBeInstanceOf(PartialDirectiveLinkerVersion1);
|
.toBeInstanceOf(PartialDirectiveLinkerVersion1);
|
||||||
expect(selector.getLinker('ɵɵngDeclareComponent', 1))
|
expect(selector.getLinker('ɵɵngDeclareComponent', 1))
|
||||||
|
@ -31,7 +38,7 @@ describe('PartialLinkerSelector', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if there is no linker that matches the given name or version', () => {
|
it('should throw an error if there is no linker that matches the given name or version', () => {
|
||||||
const selector = new PartialLinkerSelector();
|
const selector = new PartialLinkerSelector(options);
|
||||||
expect(() => selector.getLinker('$foo', 1))
|
expect(() => selector.getLinker('$foo', 1))
|
||||||
.toThrowError('Unknown partial declaration function $foo.');
|
.toThrowError('Unknown partial declaration function $foo.');
|
||||||
expect(() => selector.getLinker('ɵɵngDeclareDirective', 2))
|
expect(() => selector.getLinker('ɵɵngDeclareDirective', 2))
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
|
import {compileComponentFromMetadata, compileDeclareComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentDef, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, R3UsedDirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {CycleAnalyzer} from '../../cycles';
|
import {CycleAnalyzer} from '../../cycles';
|
||||||
|
@ -506,11 +506,15 @@ export class ComponentDecoratorHandler implements
|
||||||
const bound = binder.bind({template: metadata.template.nodes});
|
const bound = binder.bind({template: metadata.template.nodes});
|
||||||
|
|
||||||
// The BoundTarget knows which directives and pipes matched the template.
|
// The BoundTarget knows which directives and pipes matched the template.
|
||||||
const usedDirectives = bound.getUsedDirectives().map(directive => {
|
type UsedDirective = R3UsedDirectiveMetadata&{ref: Reference};
|
||||||
|
const usedDirectives: UsedDirective[] = bound.getUsedDirectives().map(directive => {
|
||||||
return {
|
return {
|
||||||
selector: directive.selector,
|
|
||||||
expression: this.refEmitter.emit(directive.ref, context),
|
|
||||||
ref: directive.ref,
|
ref: directive.ref,
|
||||||
|
type: this.refEmitter.emit(directive.ref, context),
|
||||||
|
selector: directive.selector,
|
||||||
|
inputs: directive.inputs.propertyNames,
|
||||||
|
outputs: directive.outputs.propertyNames,
|
||||||
|
exportAs: directive.exportAs,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -529,15 +533,14 @@ export class ComponentDecoratorHandler implements
|
||||||
|
|
||||||
// Scan through the directives/pipes actually used in the template and check whether any
|
// Scan through the directives/pipes actually used in the template and check whether any
|
||||||
// import which needs to be generated would create a cycle.
|
// import which needs to be generated would create a cycle.
|
||||||
const cycleDetected =
|
const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.type, context)) ||
|
||||||
usedDirectives.some(dir => this._isCyclicImport(dir.expression, context)) ||
|
|
||||||
usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context));
|
usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context));
|
||||||
|
|
||||||
if (!cycleDetected) {
|
if (!cycleDetected) {
|
||||||
// No cycle was detected. Record the imports that need to be created in the cycle detector
|
// No cycle was detected. Record the imports that need to be created in the cycle detector
|
||||||
// so that future cyclic import checks consider their production.
|
// so that future cyclic import checks consider their production.
|
||||||
for (const {expression} of usedDirectives) {
|
for (const {type} of usedDirectives) {
|
||||||
this._recordSyntheticImport(expression, context);
|
this._recordSyntheticImport(type, context);
|
||||||
}
|
}
|
||||||
for (const {expression} of usedPipes) {
|
for (const {expression} of usedPipes) {
|
||||||
this._recordSyntheticImport(expression, context);
|
this._recordSyntheticImport(expression, context);
|
||||||
|
@ -548,7 +551,7 @@ export class ComponentDecoratorHandler implements
|
||||||
// declared after this component.
|
// declared after this component.
|
||||||
const wrapDirectivesAndPipesInClosure =
|
const wrapDirectivesAndPipesInClosure =
|
||||||
usedDirectives.some(
|
usedDirectives.some(
|
||||||
dir => isExpressionForwardReference(dir.expression, node.name, context)) ||
|
dir => isExpressionForwardReference(dir.type, node.name, context)) ||
|
||||||
usedPipes.some(
|
usedPipes.some(
|
||||||
pipe => isExpressionForwardReference(pipe.expression, node.name, context));
|
pipe => isExpressionForwardReference(pipe.expression, node.name, context));
|
||||||
|
|
||||||
|
@ -599,18 +602,35 @@ export class ComponentDecoratorHandler implements
|
||||||
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
|
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
|
||||||
resolution: Readonly<ComponentResolutionData>, pool: ConstantPool): CompileResult[] {
|
resolution: Readonly<ComponentResolutionData>, pool: ConstantPool): CompileResult[] {
|
||||||
const meta: R3ComponentMetadata = {...analysis.meta, ...resolution};
|
const meta: R3ComponentMetadata = {...analysis.meta, ...resolution};
|
||||||
const res = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
||||||
const factoryRes = compileNgFactoryDefField(
|
return this.compileComponent(analysis, def);
|
||||||
{...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component});
|
}
|
||||||
|
|
||||||
|
compilePartial(
|
||||||
|
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
|
||||||
|
resolution: Readonly<ComponentResolutionData>): CompileResult[] {
|
||||||
|
const meta: R3ComponentMetadata = {...analysis.meta, ...resolution};
|
||||||
|
const def = compileDeclareComponentFromMetadata(meta, analysis.template);
|
||||||
|
return this.compileComponent(analysis, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
private compileComponent(
|
||||||
|
analysis: Readonly<ComponentAnalysisData>,
|
||||||
|
{expression: initializer, type}: R3ComponentDef): CompileResult[] {
|
||||||
|
const factoryRes = compileNgFactoryDefField({
|
||||||
|
...analysis.meta,
|
||||||
|
injectFn: Identifiers.directiveInject,
|
||||||
|
target: R3FactoryTarget.Component,
|
||||||
|
});
|
||||||
if (analysis.metadataStmt !== null) {
|
if (analysis.metadataStmt !== null) {
|
||||||
factoryRes.statements.push(analysis.metadataStmt);
|
factoryRes.statements.push(analysis.metadataStmt);
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
factoryRes, {
|
factoryRes, {
|
||||||
name: 'ɵcmp',
|
name: 'ɵcmp',
|
||||||
initializer: res.expression,
|
initializer,
|
||||||
statements: [],
|
statements: [],
|
||||||
type: res.type,
|
type,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -737,7 +757,8 @@ export class ComponentDecoratorHandler implements
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = this._parseTemplate(
|
const template = this._parseTemplate(
|
||||||
component, templateStr, sourceMapUrl(resourceUrl), /* templateRange */ undefined,
|
component, templateStr, /* templateLiteral */ null, sourceMapUrl(resourceUrl),
|
||||||
|
/* templateRange */ undefined,
|
||||||
/* escapedString */ false);
|
/* escapedString */ false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -763,6 +784,7 @@ export class ComponentDecoratorHandler implements
|
||||||
const templateExpr = component.get('template')!;
|
const templateExpr = component.get('template')!;
|
||||||
|
|
||||||
let templateStr: string;
|
let templateStr: string;
|
||||||
|
let templateLiteral: ts.Node|null = null;
|
||||||
let templateUrl: string = '';
|
let templateUrl: string = '';
|
||||||
let templateRange: LexerRange|undefined = undefined;
|
let templateRange: LexerRange|undefined = undefined;
|
||||||
let sourceMapping: TemplateSourceMapping;
|
let sourceMapping: TemplateSourceMapping;
|
||||||
|
@ -774,6 +796,7 @@ export class ComponentDecoratorHandler implements
|
||||||
// strip
|
// strip
|
||||||
templateRange = getTemplateRange(templateExpr);
|
templateRange = getTemplateRange(templateExpr);
|
||||||
templateStr = templateExpr.getSourceFile().text;
|
templateStr = templateExpr.getSourceFile().text;
|
||||||
|
templateLiteral = templateExpr;
|
||||||
templateUrl = containingFile;
|
templateUrl = containingFile;
|
||||||
escapedString = true;
|
escapedString = true;
|
||||||
sourceMapping = {
|
sourceMapping = {
|
||||||
|
@ -795,15 +818,16 @@ export class ComponentDecoratorHandler implements
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const template =
|
const template = this._parseTemplate(
|
||||||
this._parseTemplate(component, templateStr, templateUrl, templateRange, escapedString);
|
component, templateStr, templateLiteral, templateUrl, templateRange, escapedString);
|
||||||
|
|
||||||
return {...template, sourceMapping};
|
return {...template, sourceMapping};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseTemplate(
|
private _parseTemplate(
|
||||||
component: Map<string, ts.Expression>, templateStr: string, templateUrl: string,
|
component: Map<string, ts.Expression>, templateStr: string, templateLiteral: ts.Node|null,
|
||||||
templateRange: LexerRange|undefined, escapedString: boolean): ParsedComponentTemplate {
|
templateUrl: string, templateRange: LexerRange|undefined,
|
||||||
|
escapedString: boolean): ParsedComponentTemplate {
|
||||||
let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces;
|
let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces;
|
||||||
if (component.has('preserveWhitespaces')) {
|
if (component.has('preserveWhitespaces')) {
|
||||||
const expr = component.get('preserveWhitespaces')!;
|
const expr = component.get('preserveWhitespaces')!;
|
||||||
|
@ -829,6 +853,7 @@ export class ComponentDecoratorHandler implements
|
||||||
// We always normalize line endings if the template has been escaped (i.e. is inline).
|
// We always normalize line endings if the template has been escaped (i.e. is inline).
|
||||||
const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
|
const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
|
||||||
|
|
||||||
|
const isInline = component.has('template');
|
||||||
const parsedTemplate = parseTemplate(templateStr, templateUrl, {
|
const parsedTemplate = parseTemplate(templateStr, templateUrl, {
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
interpolationConfig,
|
interpolationConfig,
|
||||||
|
@ -836,6 +861,7 @@ export class ComponentDecoratorHandler implements
|
||||||
escapedString,
|
escapedString,
|
||||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||||
i18nNormalizeLineEndingsInICUs,
|
i18nNormalizeLineEndingsInICUs,
|
||||||
|
isInline,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unfortunately, the primary parse of the template above may not contain accurate source map
|
// Unfortunately, the primary parse of the template above may not contain accurate source map
|
||||||
|
@ -859,14 +885,15 @@ export class ComponentDecoratorHandler implements
|
||||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||||
i18nNormalizeLineEndingsInICUs,
|
i18nNormalizeLineEndingsInICUs,
|
||||||
leadingTriviaChars: [],
|
leadingTriviaChars: [],
|
||||||
|
isInline,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...parsedTemplate,
|
...parsedTemplate,
|
||||||
diagNodes,
|
diagNodes,
|
||||||
template: templateStr,
|
template: templateLiteral !== null ? new WrappedNodeExpr(templateLiteral) : templateStr,
|
||||||
templateUrl,
|
templateUrl,
|
||||||
isInline: component.has('template'),
|
isInline,
|
||||||
file: new ParseSourceFile(templateStr, templateUrl),
|
file: new ParseSourceFile(templateStr, templateUrl),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,18 +6,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"], ["cx", "20", "cy", "30", "r", "50"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello"><svg><circle cx="20" cy="30" r="50"/></svg><p>test</p></div>', isInline: true } });
|
||||||
i0.ɵɵelementStart(0, "div", 0);
|
|
||||||
i0.ɵɵnamespaceSVG();
|
|
||||||
i0.ɵɵelementStart(1, "svg");
|
|
||||||
i0.ɵɵelement(2, "circle", 1);
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵnamespaceHTML();
|
|
||||||
i0.ɵɵelementStart(3, "p");
|
|
||||||
i0.ɵɵtext(4, "test");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -56,18 +45,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello"><math><infinity/></math><p>test</p></div>', isInline: true } });
|
||||||
i0.ɵɵelementStart(0, "div", 0);
|
|
||||||
i0.ɵɵnamespaceMathML();
|
|
||||||
i0.ɵɵelementStart(1, "math");
|
|
||||||
i0.ɵɵelement(2, "infinity");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵnamespaceHTML();
|
|
||||||
i0.ɵɵelementStart(3, "p");
|
|
||||||
i0.ɵɵtext(4, "test");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -106,15 +84,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello">Hello <b>World</b>!</div>', isInline: true } });
|
||||||
i0.ɵɵelementStart(0, "div", 0);
|
|
||||||
i0.ɵɵtext(1, "Hello ");
|
|
||||||
i0.ɵɵelementStart(2, "b");
|
|
||||||
i0.ɵɵtext(3, "World");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵtext(4, "!");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -153,15 +123,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [[0, "xmlns", "foo", "http://someuri/foo", 0, "foo", "bar", "baz", "title", "Hello", 0, "foo", "qux", "quacks", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div xmlns:foo="http://someuri/foo" class="my-app" foo:bar="baz" title="Hello" foo:qux="quacks">Hello <b>World</b>!</div>', isInline: true } });
|
||||||
i0.ɵɵelementStart(0, "div", 0);
|
|
||||||
i0.ɵɵtext(1, "Hello ");
|
|
||||||
i0.ɵɵelementStart(2, "b");
|
|
||||||
i0.ɵɵtext(3, "World");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵtext(4, "!");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -200,14 +162,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 4, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<ng-container><span>in a </span>container</ng-container>', isInline: true } });
|
||||||
i0.ɵɵelementContainerStart(0);
|
|
||||||
i0.ɵɵelementStart(1, "span");
|
|
||||||
i0.ɵɵtext(2, "in a ");
|
|
||||||
i0.ɵɵelementEnd();
|
|
||||||
i0.ɵɵtext(3, "container");
|
|
||||||
i0.ɵɵelementContainerEnd();
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -246,9 +201,7 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<ng-container></ng-container>', isInline: true } });
|
||||||
i0.ɵɵelementContainer(0);
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{ selector: 'my-component', template: '<ng-container></ng-container>' }]
|
args: [{ selector: 'my-component', template: '<ng-container></ng-container>' }]
|
||||||
|
@ -287,11 +240,7 @@ export class MyComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 1, consts: [[3, "id"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div [id]="id"></div>', isInline: true } });
|
||||||
i0.ɵɵelement(0, "div", 0);
|
|
||||||
} if (rf & 2) {
|
|
||||||
i0.ɵɵproperty("id", ctx.id);
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{ selector: 'my-component', template: '<div [id]="id"></div>' }]
|
args: [{ selector: 'my-component', template: '<div [id]="id"></div>' }]
|
||||||
|
@ -325,20 +274,18 @@ export declare class MyModule {
|
||||||
****************************************************************************************************/
|
****************************************************************************************************/
|
||||||
import { Component, NgModule } from '@angular/core';
|
import { Component, NgModule } from '@angular/core';
|
||||||
import * as i0 from "@angular/core";
|
import * as i0 from "@angular/core";
|
||||||
const _c0 = function (a0) { return [a0]; };
|
|
||||||
const _c1 = function () { return [0]; };
|
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.id = 'one';
|
this.id = 'one';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, vars: 15, consts: [[3, "ternary", "pipe", "and", "or"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `<div
|
||||||
i0.ɵɵelement(0, "div", 0);
|
[ternary]="cond ? [a] : [0]"
|
||||||
i0.ɵɵpipe(1, "pipe");
|
[pipe]="value | pipe:1:2"
|
||||||
} if (rf & 2) {
|
[and]="cond && [b]"
|
||||||
i0.ɵɵproperty("ternary", ctx.cond ? i0.ɵɵpureFunction1(8, _c0, ctx.a) : i0.ɵɵpureFunction0(10, _c1))("pipe", i0.ɵɵpipeBind3(1, 4, ctx.value, 1, 2))("and", ctx.cond && i0.ɵɵpureFunction1(11, _c0, ctx.b))("or", ctx.cond || i0.ɵɵpureFunction1(13, _c0, ctx.c));
|
[or]="cond || [c]"
|
||||||
} }, encapsulation: 2 });
|
></div>`, isInline: true } });
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -380,20 +327,13 @@ export declare class MyModule {
|
||||||
****************************************************************************************************/
|
****************************************************************************************************/
|
||||||
import { Component, Input, NgModule } from '@angular/core';
|
import { Component, Input, NgModule } from '@angular/core';
|
||||||
import * as i0 from "@angular/core";
|
import * as i0 from "@angular/core";
|
||||||
const _c0 = function (a0, a1) { return { collapsedHeight: a0, expandedHeight: a1 }; };
|
|
||||||
const _c1 = function (a0, a1) { return { value: a0, params: a1 }; };
|
|
||||||
const _c2 = function (a0, a1) { return { collapsedWidth: a0, expandedWidth: a1 }; };
|
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
getExpandedState() {
|
getExpandedState() {
|
||||||
return 'expanded';
|
return 'expanded';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], hostVars: 14, hostBindings: function MyComponent_HostBindings(rf, ctx) { if (rf & 2) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, host: { properties: { "@expansionHeight": "{\n value: getExpandedState(),\n params: {\n collapsedHeight: collapsedHeight,\n expandedHeight: expandedHeight\n }\n }", "@expansionWidth": "{\n value: getExpandedState(),\n params: {\n collapsedWidth: collapsedWidth,\n expandedWidth: expandedWidth\n }\n }" } }, ngImport: i0, template: { source: '...', isInline: true } });
|
||||||
i0.ɵɵsyntheticHostProperty("@expansionHeight", i0.ɵɵpureFunction2(5, _c1, ctx.getExpandedState(), i0.ɵɵpureFunction2(2, _c0, ctx.collapsedHeight, ctx.expandedHeight)))("@expansionWidth", i0.ɵɵpureFunction2(11, _c1, ctx.getExpandedState(), i0.ɵɵpureFunction2(8, _c2, ctx.collapsedWidth, ctx.expandedWidth)));
|
|
||||||
} }, inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
|
||||||
i0.ɵɵtext(0, "...");
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -465,12 +405,7 @@ export class MyComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 4, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div [class.error]="error" [style.background-color]="color"></div>', isInline: true } });
|
||||||
i0.ɵɵelement(0, "div");
|
|
||||||
} if (rf & 2) {
|
|
||||||
i0.ɵɵstyleProp("background-color", ctx.color);
|
|
||||||
i0.ɵɵclassProp("error", ctx.error);
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
@ -511,10 +446,10 @@ import * as i0 from "@angular/core";
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, vars: 0, consts: [["title", "hi"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `
|
||||||
i0.ɵɵelement(0, "div", 0);
|
<div title="hi"></div>
|
||||||
i0.ɵɵelement(1, "span", 0);
|
<span title="hi"></span>
|
||||||
} }, encapsulation: 2 });
|
`, isInline: true } });
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
|
|
@ -9,11 +9,7 @@ export class MyApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
|
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
|
||||||
MyApp.ɵcmp = i0.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, vars: 9, template: function MyApp_Template(rf, ctx) { if (rf & 1) {
|
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyApp, selector: "my-app", ngImport: i0, template: { source: ' {{list[0]}} {{list[1]}} {{list[2]}} {{list[3]}} {{list[4]}} {{list[5]}} {{list[6]}} {{list[7]}} {{list[8]}} ', isInline: true } });
|
||||||
i0.ɵɵtext(0);
|
|
||||||
} if (rf & 2) {
|
|
||||||
i0.ɵɵtextInterpolateV([" ", ctx.list[0], " ", ctx.list[1], " ", ctx.list[2], " ", ctx.list[3], " ", ctx.list[4], " ", ctx.list[5], " ", ctx.list[6], " ", ctx.list[7], " ", ctx.list[8], " "]);
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyApp, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyApp, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{
|
args: [{
|
||||||
|
|
|
@ -14,9 +14,7 @@ I18nDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: 1, type: I18nDirectiv
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
}
|
}
|
||||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div i18n></div>', isInline: true } });
|
||||||
i0.ɵɵelement(0, "div");
|
|
||||||
} }, encapsulation: 2 });
|
|
||||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||||
type: Component,
|
type: Component,
|
||||||
args: [{ selector: 'my-component', template: '<div i18n></div>' }]
|
args: [{ selector: 'my-component', template: '<div i18n></div>' }]
|
||||||
|
|
|
@ -102,6 +102,7 @@ export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compile
|
||||||
export {makeBindingParser, ParsedTemplate, parseTemplate, ParseTemplateOptions} from './render3/view/template';
|
export {makeBindingParser, ParsedTemplate, parseTemplate, ParseTemplateOptions} from './render3/view/template';
|
||||||
export {R3Reference} from './render3/util';
|
export {R3Reference} from './render3/util';
|
||||||
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, ParsedHostBindings, verifyHostBindings} from './render3/view/compiler';
|
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, ParsedHostBindings, verifyHostBindings} from './render3/view/compiler';
|
||||||
|
export {compileDeclareComponentFromMetadata} from './render3/partial/component';
|
||||||
export {compileDeclareDirectiveFromMetadata} from './render3/partial/directive';
|
export {compileDeclareDirectiveFromMetadata} from './render3/partial/directive';
|
||||||
export {publishFacade} from './jit_compiler_facade';
|
export {publishFacade} from './jit_compiler_facade';
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
|
|
|
@ -154,7 +154,7 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
preserveWhitespaces: boolean;
|
preserveWhitespaces: boolean;
|
||||||
animations: any[]|undefined;
|
animations: any[]|undefined;
|
||||||
pipes: Map<string, any>;
|
pipes: Map<string, any>;
|
||||||
directives: {selector: string, expression: any}[];
|
directives: R3UsedDirectiveMetadata[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
viewProviders: Provider[]|null;
|
viewProviders: Provider[]|null;
|
||||||
|
@ -162,6 +162,14 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
changeDetection?: ChangeDetectionStrategy;
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface R3UsedDirectiveMetadata {
|
||||||
|
selector: string;
|
||||||
|
inputs: string[];
|
||||||
|
outputs: string[];
|
||||||
|
exportAs: string[]|null;
|
||||||
|
type: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface R3FactoryDefMetadataFacade {
|
export interface R3FactoryDefMetadataFacade {
|
||||||
name: string;
|
name: string;
|
||||||
type: any;
|
type: any;
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {R3JitReflector} from './render3/r3_jit';
|
||||||
import {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
import {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
||||||
import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
||||||
import {R3Reference} from './render3/util';
|
import {R3Reference} from './render3/util';
|
||||||
import {R3DirectiveMetadata, R3QueryMetadata} from './render3/view/api';
|
import {R3ComponentMetadata, R3DirectiveMetadata, R3QueryMetadata} from './render3/view/api';
|
||||||
import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler';
|
import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler';
|
||||||
import {makeBindingParser, parseTemplate} from './render3/view/template';
|
import {makeBindingParser, parseTemplate} from './render3/view/template';
|
||||||
import {ResourceLoader} from './resource_loader';
|
import {ResourceLoader} from './resource_loader';
|
||||||
|
@ -136,7 +136,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
||||||
|
|
||||||
// Compile the component metadata, including template, into an expression.
|
// Compile the component metadata, including template, into an expression.
|
||||||
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
||||||
const metadata = {
|
const metadata: R3ComponentMetadata = {
|
||||||
...facade as R3ComponentMetadataFacadeNoPropAndWhitespace,
|
...facade as R3ComponentMetadataFacadeNoPropAndWhitespace,
|
||||||
...convertDirectiveFacadeToMetadata(facade),
|
...convertDirectiveFacadeToMetadata(facade),
|
||||||
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {ChangeDetectionStrategy, ViewEncapsulation} from '../../core';
|
||||||
|
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,6 +114,106 @@ export interface R3DeclareDirectiveMetadata {
|
||||||
ngImport: o.Expression;
|
ngImport: o.Expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of `R3DeclareDirectiveMetadata` that declares the shape of a partial declaration of
|
||||||
|
* a component.
|
||||||
|
*/
|
||||||
|
export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata {
|
||||||
|
/**
|
||||||
|
* Information about the component's template.
|
||||||
|
*/
|
||||||
|
template: {
|
||||||
|
/**
|
||||||
|
* The component's unparsed template string as opaque expression. The template is represented
|
||||||
|
* using either a string literal or template literal without substitutions, but its value is
|
||||||
|
* not read directly. Instead, the template parser is given the full source file's text and
|
||||||
|
* the range of this expression to parse directly from source.
|
||||||
|
*/
|
||||||
|
source: o.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the template was inline (using `template`) or external (using `templateUrl`).
|
||||||
|
*/
|
||||||
|
isInline: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS from inline styles and included styleUrls.
|
||||||
|
*/
|
||||||
|
styles?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of directives which matched in the template, including sufficient
|
||||||
|
* metadata for each directive to attribute bindings and references within
|
||||||
|
* the template to each directive specifically, if the runtime instructions
|
||||||
|
* support this.
|
||||||
|
*/
|
||||||
|
directives?: {
|
||||||
|
/**
|
||||||
|
* Selector of the directive.
|
||||||
|
*/
|
||||||
|
selector: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the directive class (possibly a forward reference).
|
||||||
|
*/
|
||||||
|
type: o.Expression | (() => o.Expression);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property names of the directive's inputs.
|
||||||
|
*/
|
||||||
|
inputs?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event names of the directive's outputs.
|
||||||
|
*/
|
||||||
|
outputs?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names by which this directive exports itself for references.
|
||||||
|
*/
|
||||||
|
exportAs?: string[];
|
||||||
|
}[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of pipe names to an expression referencing the pipe type (possibly a forward reference)
|
||||||
|
* which are used in the template.
|
||||||
|
*/
|
||||||
|
pipes?: {[pipeName: string]: o.Expression|(() => o.Expression)};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of view providers defined in the component.
|
||||||
|
*/
|
||||||
|
viewProviders?: o.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of animation triggers that will be used in the component template.
|
||||||
|
*/
|
||||||
|
animations?: o.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy used for detecting changes in the component.
|
||||||
|
* Defaults to `ChangeDetectionStrategy.Default`.
|
||||||
|
*/
|
||||||
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An encapsulation policy for the template and CSS styles.
|
||||||
|
* Defaults to `ViewEncapsulation.Emulated`.
|
||||||
|
*/
|
||||||
|
encapsulation?: ViewEncapsulation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the default interpolation start and end delimiters. Defaults to {{ and }}.
|
||||||
|
*/
|
||||||
|
interpolation?: InterpolationConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether whitespace in the template should be preserved. Defaults to false.
|
||||||
|
*/
|
||||||
|
preserveWhitespaces?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface R3DeclareQueryMetadata {
|
export interface R3DeclareQueryMetadata {
|
||||||
/**
|
/**
|
||||||
* Name of the property on the class to update with query results.
|
* Name of the property on the class to update with query results.
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import * as core from '../../core';
|
||||||
|
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||||
|
import * as o from '../../output/output_ast';
|
||||||
|
import {Identifiers as R3} from '../r3_identifiers';
|
||||||
|
import {R3ComponentDef, R3ComponentMetadata} from '../view/api';
|
||||||
|
import {createComponentType} from '../view/compiler';
|
||||||
|
import {ParsedTemplate} from '../view/template';
|
||||||
|
import {DefinitionMap} from '../view/util';
|
||||||
|
|
||||||
|
import {createDirectiveDefinitionMap} from './directive';
|
||||||
|
import {toOptionalLiteralArray} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile a component declaration defined by the `R3ComponentMetadata`.
|
||||||
|
*/
|
||||||
|
export function compileDeclareComponentFromMetadata(
|
||||||
|
meta: R3ComponentMetadata, template: ParsedTemplate): R3ComponentDef {
|
||||||
|
const definitionMap = createComponentDefinitionMap(meta, template);
|
||||||
|
|
||||||
|
const expression = o.importExpr(R3.declareComponent).callFn([definitionMap.toLiteralMap()]);
|
||||||
|
const type = createComponentType(meta);
|
||||||
|
|
||||||
|
return {expression, type};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gathers the declaration fields for a component into a `DefinitionMap`.
|
||||||
|
*/
|
||||||
|
export function createComponentDefinitionMap(
|
||||||
|
meta: R3ComponentMetadata, template: ParsedTemplate): DefinitionMap {
|
||||||
|
const definitionMap = createDirectiveDefinitionMap(meta);
|
||||||
|
|
||||||
|
const templateMap = compileTemplateDefinition(template);
|
||||||
|
|
||||||
|
definitionMap.set('template', templateMap);
|
||||||
|
|
||||||
|
definitionMap.set('styles', toOptionalLiteralArray(meta.styles, o.literal));
|
||||||
|
definitionMap.set('directives', compileUsedDirectiveMetadata(meta));
|
||||||
|
definitionMap.set('pipes', compileUsedPipeMetadata(meta));
|
||||||
|
definitionMap.set('viewProviders', meta.viewProviders);
|
||||||
|
definitionMap.set('animations', meta.animations);
|
||||||
|
|
||||||
|
if (meta.changeDetection !== undefined) {
|
||||||
|
definitionMap.set(
|
||||||
|
'changeDetection',
|
||||||
|
o.importExpr(R3.ChangeDetectionStrategy)
|
||||||
|
.prop(core.ChangeDetectionStrategy[meta.changeDetection]));
|
||||||
|
}
|
||||||
|
if (meta.encapsulation !== core.ViewEncapsulation.Emulated) {
|
||||||
|
definitionMap.set(
|
||||||
|
'encapsulation',
|
||||||
|
o.importExpr(R3.ViewEncapsulation).prop(core.ViewEncapsulation[meta.encapsulation]));
|
||||||
|
}
|
||||||
|
if (meta.interpolation !== DEFAULT_INTERPOLATION_CONFIG) {
|
||||||
|
definitionMap.set(
|
||||||
|
'interpolation',
|
||||||
|
o.literalArr([o.literal(meta.interpolation.start), o.literal(meta.interpolation.end)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template.preserveWhitespaces === true) {
|
||||||
|
definitionMap.set('preserveWhitespaces', o.literal(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles the provided template into its partial definition.
|
||||||
|
*/
|
||||||
|
function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr {
|
||||||
|
const templateMap = new DefinitionMap();
|
||||||
|
const templateExpr =
|
||||||
|
typeof template.template === 'string' ? o.literal(template.template) : template.template;
|
||||||
|
templateMap.set('source', templateExpr);
|
||||||
|
templateMap.set('isInline', o.literal(template.isInline));
|
||||||
|
return templateMap.toLiteralMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles the directives as registered in the component metadata into an array literal of the
|
||||||
|
* individual directives. If the component does not use any directives, then null is returned.
|
||||||
|
*/
|
||||||
|
function compileUsedDirectiveMetadata(meta: R3ComponentMetadata): o.LiteralArrayExpr|null {
|
||||||
|
const wrapType = meta.wrapDirectivesAndPipesInClosure ?
|
||||||
|
(expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) :
|
||||||
|
(expr: o.Expression) => expr;
|
||||||
|
|
||||||
|
return toOptionalLiteralArray(meta.directives, directive => {
|
||||||
|
const dirMeta = new DefinitionMap();
|
||||||
|
dirMeta.set('type', wrapType(directive.type));
|
||||||
|
dirMeta.set('selector', o.literal(directive.selector));
|
||||||
|
dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, o.literal));
|
||||||
|
dirMeta.set('outputs', toOptionalLiteralArray(directive.outputs, o.literal));
|
||||||
|
dirMeta.set('exportAs', toOptionalLiteralArray(directive.exportAs, o.literal));
|
||||||
|
return dirMeta.toLiteralMap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles the pipes as registered in the component metadata into an object literal, where the
|
||||||
|
* pipe's name is used as key and a reference to its type as value. If the component does not use
|
||||||
|
* any pipes, then null is returned.
|
||||||
|
*/
|
||||||
|
function compileUsedPipeMetadata(meta: R3ComponentMetadata): o.LiteralMapExpr|null {
|
||||||
|
if (meta.pipes.size === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapType = meta.wrapDirectivesAndPipesInClosure ?
|
||||||
|
(expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) :
|
||||||
|
(expr: o.Expression) => expr;
|
||||||
|
|
||||||
|
const entries = [];
|
||||||
|
for (const [name, pipe] of meta.pipes) {
|
||||||
|
entries.push({key: name, value: wrapType(pipe), quoted: true});
|
||||||
|
}
|
||||||
|
return o.literalMap(entries);
|
||||||
|
}
|
|
@ -234,9 +234,19 @@ export class Identifiers {
|
||||||
static resolveBody: o.ExternalReference = {name: 'ɵɵresolveBody', moduleName: CORE};
|
static resolveBody: o.ExternalReference = {name: 'ɵɵresolveBody', moduleName: CORE};
|
||||||
|
|
||||||
static defineComponent: o.ExternalReference = {name: 'ɵɵdefineComponent', moduleName: CORE};
|
static defineComponent: o.ExternalReference = {name: 'ɵɵdefineComponent', moduleName: CORE};
|
||||||
|
static declareComponent: o.ExternalReference = {name: 'ɵɵngDeclareComponent', moduleName: CORE};
|
||||||
|
|
||||||
static setComponentScope: o.ExternalReference = {name: 'ɵɵsetComponentScope', moduleName: CORE};
|
static setComponentScope: o.ExternalReference = {name: 'ɵɵsetComponentScope', moduleName: CORE};
|
||||||
|
|
||||||
|
static ChangeDetectionStrategy: o.ExternalReference = {
|
||||||
|
name: 'ChangeDetectionStrategy',
|
||||||
|
moduleName: CORE,
|
||||||
|
};
|
||||||
|
static ViewEncapsulation: o.ExternalReference = {
|
||||||
|
name: 'ViewEncapsulation',
|
||||||
|
moduleName: CORE,
|
||||||
|
};
|
||||||
|
|
||||||
static ComponentDefWithMeta: o.ExternalReference = {
|
static ComponentDefWithMeta: o.ExternalReference = {
|
||||||
name: 'ɵɵComponentDefWithMeta',
|
name: 'ɵɵComponentDefWithMeta',
|
||||||
moduleName: CORE,
|
moduleName: CORE,
|
||||||
|
|
|
@ -149,7 +149,7 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||||
* A list of directive selectors and an expression referencing the directive type which are in the
|
* A list of directive selectors and an expression referencing the directive type which are in the
|
||||||
* scope of the compilation.
|
* scope of the compilation.
|
||||||
*/
|
*/
|
||||||
directives: {selector: string, expression: o.Expression}[];
|
directives: R3UsedDirectiveMetadata[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to wrap the 'directives' and/or `pipes` array, if one is generated, in a closure.
|
* Whether to wrap the 'directives' and/or `pipes` array, if one is generated, in a closure.
|
||||||
|
@ -206,6 +206,37 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||||
changeDetection?: ChangeDetectionStrategy;
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a directive that is used in a component template. Only the stable, public
|
||||||
|
* facing information of the directive is stored here.
|
||||||
|
*/
|
||||||
|
export interface R3UsedDirectiveMetadata {
|
||||||
|
/**
|
||||||
|
* The type of the directive as an expression.
|
||||||
|
*/
|
||||||
|
type: o.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selector of the directive.
|
||||||
|
*/
|
||||||
|
selector: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The binding property names of the inputs of the directive.
|
||||||
|
*/
|
||||||
|
inputs: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The binding property names of the outputs of the directive.
|
||||||
|
*/
|
||||||
|
outputs: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name under which the directive is exported, if any (exportAs in Angular). Null otherwise.
|
||||||
|
*/
|
||||||
|
exportAs: string[]|null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information needed to compile a query (view or content).
|
* Information needed to compile a query (view or content).
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -159,8 +159,8 @@ export function compileComponentFromMetadata(
|
||||||
|
|
||||||
if (meta.directives.length > 0) {
|
if (meta.directives.length > 0) {
|
||||||
const matcher = new SelectorMatcher();
|
const matcher = new SelectorMatcher();
|
||||||
for (const {selector, expression} of meta.directives) {
|
for (const {selector, type} of meta.directives) {
|
||||||
matcher.addSelectables(CssSelector.parse(selector), expression);
|
matcher.addSelectables(CssSelector.parse(selector), type);
|
||||||
}
|
}
|
||||||
directiveMatcher = matcher;
|
directiveMatcher = matcher;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2032,6 +2032,11 @@ export interface ParseTemplateOptions {
|
||||||
* The default is `false`, but this will be switched in a future major release.
|
* The default is `false`, but this will be switched in a future major release.
|
||||||
*/
|
*/
|
||||||
i18nNormalizeLineEndingsInICUs?: boolean;
|
i18nNormalizeLineEndingsInICUs?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the template was inline.
|
||||||
|
*/
|
||||||
|
isInline?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2044,6 +2049,7 @@ export interface ParseTemplateOptions {
|
||||||
export function parseTemplate(
|
export function parseTemplate(
|
||||||
template: string, templateUrl: string, options: ParseTemplateOptions = {}): ParsedTemplate {
|
template: string, templateUrl: string, options: ParseTemplateOptions = {}): ParsedTemplate {
|
||||||
const {interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat} = options;
|
const {interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat} = options;
|
||||||
|
const isInline = options.isInline ?? false;
|
||||||
const bindingParser = makeBindingParser(interpolationConfig);
|
const bindingParser = makeBindingParser(interpolationConfig);
|
||||||
const htmlParser = new HtmlParser();
|
const htmlParser = new HtmlParser();
|
||||||
const parseResult = htmlParser.parse(
|
const parseResult = htmlParser.parse(
|
||||||
|
@ -2057,6 +2063,7 @@ export function parseTemplate(
|
||||||
interpolationConfig,
|
interpolationConfig,
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
template,
|
template,
|
||||||
|
isInline,
|
||||||
errors: parseResult.errors,
|
errors: parseResult.errors,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
styleUrls: [],
|
styleUrls: [],
|
||||||
|
@ -2081,6 +2088,7 @@ export function parseTemplate(
|
||||||
interpolationConfig,
|
interpolationConfig,
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
template,
|
template,
|
||||||
|
isInline,
|
||||||
errors: i18nMetaResult.errors,
|
errors: i18nMetaResult.errors,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
styleUrls: [],
|
styleUrls: [],
|
||||||
|
@ -2112,6 +2120,7 @@ export function parseTemplate(
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
errors: errors.length > 0 ? errors : null,
|
errors: errors.length > 0 ? errors : null,
|
||||||
template,
|
template,
|
||||||
|
isInline,
|
||||||
nodes,
|
nodes,
|
||||||
styleUrls,
|
styleUrls,
|
||||||
styles,
|
styles,
|
||||||
|
@ -2269,12 +2278,18 @@ export interface ParsedTemplate {
|
||||||
interpolationConfig?: InterpolationConfig;
|
interpolationConfig?: InterpolationConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The string contents of the template.
|
* The string contents of the template, or an expression that represents the string/template
|
||||||
|
* literal as it occurs in the source.
|
||||||
*
|
*
|
||||||
* This is the "logical" template string, after expansion of any escaped characters (for inline
|
* This is the "logical" template string, after expansion of any escaped characters (for inline
|
||||||
* templates). This may differ from the actual template bytes as they appear in the .ts file.
|
* templates). This may differ from the actual template bytes as they appear in the .ts file.
|
||||||
*/
|
*/
|
||||||
template: string;
|
template: string|o.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the template was inline (using `template`) or external (using `templateUrl`).
|
||||||
|
*/
|
||||||
|
isInline: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any errors from parsing the template the first time.
|
* Any errors from parsing the template the first time.
|
||||||
|
|
|
@ -154,7 +154,7 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
preserveWhitespaces: boolean;
|
preserveWhitespaces: boolean;
|
||||||
animations: any[]|undefined;
|
animations: any[]|undefined;
|
||||||
pipes: Map<string, any>;
|
pipes: Map<string, any>;
|
||||||
directives: {selector: string, expression: any}[];
|
directives: R3UsedDirectiveMetadata[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
viewProviders: Provider[]|null;
|
viewProviders: Provider[]|null;
|
||||||
|
@ -162,6 +162,14 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
changeDetection?: ChangeDetectionStrategy;
|
changeDetection?: ChangeDetectionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface R3UsedDirectiveMetadata {
|
||||||
|
selector: string;
|
||||||
|
inputs: string[];
|
||||||
|
outputs: string[];
|
||||||
|
exportAs: string[]|null;
|
||||||
|
type: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface R3FactoryDefMetadataFacade {
|
export interface R3FactoryDefMetadataFacade {
|
||||||
name: string;
|
name: string;
|
||||||
type: any;
|
type: any;
|
||||||
|
|
|
@ -165,6 +165,7 @@ export {
|
||||||
ɵɵnamespaceMathML,
|
ɵɵnamespaceMathML,
|
||||||
ɵɵnamespaceSVG,
|
ɵɵnamespaceSVG,
|
||||||
ɵɵnextContext,
|
ɵɵnextContext,
|
||||||
|
ɵɵngDeclareComponent,
|
||||||
ɵɵngDeclareDirective,
|
ɵɵngDeclareDirective,
|
||||||
ɵɵNgOnChangesFeature,
|
ɵɵNgOnChangesFeature,
|
||||||
ɵɵpipe,
|
ɵɵpipe,
|
||||||
|
|
|
@ -135,6 +135,7 @@ export {
|
||||||
} from './interfaces/node';
|
} from './interfaces/node';
|
||||||
export {CssSelectorList, ProjectionSlots} from './interfaces/projection';
|
export {CssSelectorList, ProjectionSlots} from './interfaces/projection';
|
||||||
export {
|
export {
|
||||||
|
ɵɵngDeclareComponent,
|
||||||
ɵɵngDeclareDirective,
|
ɵɵngDeclareDirective,
|
||||||
} from './jit/partial';
|
} from './jit/partial';
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -10,8 +10,6 @@ import {ɵɵinject, ɵɵinvalidFactoryDep} from '../../di/injector_compatibility
|
||||||
import {ɵɵdefineInjectable, ɵɵdefineInjector} from '../../di/interface/defs';
|
import {ɵɵdefineInjectable, ɵɵdefineInjector} from '../../di/interface/defs';
|
||||||
import * as sanitization from '../../sanitization/sanitization';
|
import * as sanitization from '../../sanitization/sanitization';
|
||||||
import * as r3 from '../index';
|
import * as r3 from '../index';
|
||||||
import * as partial from './partial';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,6 +167,4 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||||
'ɵɵsanitizeUrlOrResourceUrl': sanitization.ɵɵsanitizeUrlOrResourceUrl,
|
'ɵɵsanitizeUrlOrResourceUrl': sanitization.ɵɵsanitizeUrlOrResourceUrl,
|
||||||
'ɵɵtrustConstantHtml': sanitization.ɵɵtrustConstantHtml,
|
'ɵɵtrustConstantHtml': sanitization.ɵɵtrustConstantHtml,
|
||||||
'ɵɵtrustConstantResourceUrl': sanitization.ɵɵtrustConstantResourceUrl,
|
'ɵɵtrustConstantResourceUrl': sanitization.ɵɵtrustConstantResourceUrl,
|
||||||
|
|
||||||
'ɵɵngDeclareDirective': partial.ɵɵngDeclareDirective,
|
|
||||||
}))();
|
}))();
|
||||||
|
|
|
@ -14,3 +14,12 @@
|
||||||
export function ɵɵngDeclareDirective(decl: unknown): unknown {
|
export function ɵɵngDeclareDirective(decl: unknown): unknown {
|
||||||
throw new Error('Not yet implemented');
|
throw new Error('Not yet implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a partial component declaration object into a full component definition object.
|
||||||
|
*
|
||||||
|
* @codeGenApi
|
||||||
|
*/
|
||||||
|
export function ɵɵngDeclareComponent(decl: unknown): unknown {
|
||||||
|
throw new Error('Not yet implemented');
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,17 @@ const INTERFACE_EXCEPTIONS = new Set<string>([
|
||||||
'ModuleWithProviders',
|
'ModuleWithProviders',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following symbols are only referenced from partial declaration compilation outputs, which
|
||||||
|
* will never be emitted by the JIT compiler so are allowed to be omitted from the JIT environment.
|
||||||
|
*/
|
||||||
|
const PARTIAL_ONLY = new Set<string>([
|
||||||
|
'ɵɵngDeclareDirective',
|
||||||
|
'ɵɵngDeclareComponent',
|
||||||
|
'ChangeDetectionStrategy',
|
||||||
|
'ViewEncapsulation',
|
||||||
|
]);
|
||||||
|
|
||||||
describe('r3 jit environment', () => {
|
describe('r3 jit environment', () => {
|
||||||
// This test keeps render3/jit/environment and r3_identifiers in the compiler in sync, ensuring
|
// This test keeps render3/jit/environment and r3_identifiers in the compiler in sync, ensuring
|
||||||
// that if the compiler writes a reference to a render3 symbol, it will be resolvable at runtime
|
// that if the compiler writes a reference to a render3 symbol, it will be resolvable at runtime
|
||||||
|
@ -33,7 +44,7 @@ describe('r3 jit environment', () => {
|
||||||
// A few such properties are string constants. Ignore them, and focus on ExternalReferences.
|
// A few such properties are string constants. Ignore them, and focus on ExternalReferences.
|
||||||
.filter(isExternalReference)
|
.filter(isExternalReference)
|
||||||
// Some references are to interface types. Only take properties which have runtime values.
|
// Some references are to interface types. Only take properties which have runtime values.
|
||||||
.filter(sym => !INTERFACE_EXCEPTIONS.has(sym.name))
|
.filter(sym => !INTERFACE_EXCEPTIONS.has(sym.name) && !PARTIAL_ONLY.has(sym.name))
|
||||||
.forEach(sym => {
|
.forEach(sym => {
|
||||||
// Assert that angularCoreEnv has a reference to the runtime symbol.
|
// Assert that angularCoreEnv has a reference to the runtime symbol.
|
||||||
expect(angularCoreEnv.hasOwnProperty(sym.name))
|
expect(angularCoreEnv.hasOwnProperty(sym.name))
|
||||||
|
|
Loading…
Reference in New Issue