New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Next batch of ngtsc changes #24862
Next batch of ngtsc changes #24862
Conversation
0e78341
to
3b182aa
Compare
You can preview 0e78341 at https://pr24862-0e78341.ngbuilds.io/. |
You can preview 3b182aa at https://pr24862-3b182aa.ngbuilds.io/. |
3b182aa
to
3dd7c1d
Compare
You can preview 3dd7c1d at https://pr24862-3dd7c1d.ngbuilds.io/. |
@@ -80,17 +80,19 @@ export function extractDirectiveMetadata( | |||
const decoratedElements = | |||
members.filter(member => !member.isStatic && member.decorators !== null); | |||
|
|||
const coreModule = isCore && '@angular/core' || undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write isCore ? '@angular/core' : undefined
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
// Construct the list of view queries. | ||
const coreModule = this.isCore && '@angular/core' || undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
You can preview 6fcbc8f at https://pr24862-6fcbc8f.ngbuilds.io/. |
6fcbc8f
to
9e3851c
Compare
You can preview 9e3851c at https://pr24862-9e3851c.ngbuilds.io/. |
9e3851c
to
e34dec4
Compare
You can preview e34dec4 at https://pr24862-e34dec4.ngbuilds.io/. |
e34dec4
to
504f626
Compare
@@ -87,3 +87,10 @@ export function referenceToExpression(ref: Reference, context: ts.SourceFile): E | |||
export function isAngularCore(decorator: Decorator): boolean { | |||
return decorator.import !== null && decorator.import.from === '@angular/core'; | |||
} | |||
|
|||
export function unwrapExpression(node: ts.Expression): ts.Expression { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a one liner comment ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
You can preview 504f626 at https://pr24862-504f626.ngbuilds.io/. |
* Given a `FunctionDeclaration` or `MethodDeclaration`, check if it is typed as a | ||
* `ModuleWithProviders` and return an expression referencing the module if available. | ||
*/ | ||
private _resolveModuleWithProviders(node: ts.FunctionDeclaration| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the name doesn't reflect what the method does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to _extractModuleFromModuleWithProvidersFn
node, {absoluteModuleName: null, scope: new Map<ts.ParameterDeclaration, ResolvedValue>()}); | ||
export function staticallyResolve( | ||
node: ts.Expression, checker: ts.TypeChecker, | ||
foreignFunctionResolver?: (node: ts.FunctionDeclaration | ts.MethodDeclaration) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls update the api doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -356,6 +358,22 @@ class StaticInterpreter { | |||
return this.visitSymbol(symbol, context); | |||
} | |||
|
|||
private visitTemplateExpression(node: ts.TemplateExpression, context: Context): ResolvedValue { | |||
const pieces: string[] = [node.head.text]; | |||
for (let i = 0; i < node.templateSpans.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about tagged template ?
if they are of the same type you should return DYNAMIC_VALUE
too.
Add a comment / test ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tagged templates are not handled yet, so they'll fall through and return DYNAMIC_VALUE
. I don't even know if ngc
handles them.
predicate = new WrappedNodeExpr(args[0]); | ||
} else if (typeof arg === 'string') { | ||
predicate = [arg]; | ||
} else if (assertIsStringArray(arg, '@' + name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert
are often debug time checks only, rename/refactor ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to isStringArrayOrDie()
} else if (assertIsStringArray(arg, '@' + name)) { | ||
predicate = arg as string[]; | ||
} | ||
if (predicate === null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be a final else {}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
if (options.has('descendants')) { | ||
const descendantsValue = staticallyResolve(options.get('descendants') !, checker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove the local variable and use descendants
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assignment isn't legal until after the typeof
check.
|
||
// Extract the read and descendants options. | ||
let read: Expression|null = null; | ||
let descendants: boolean = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't descendants
be a function of first
? (I think the behavior is different, can't remember exactly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's part of the options argument, as far as I know. first
means whether it's ViewChild
or ViewChildren
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See
angular/packages/core/src/metadata/di.ts
Line 192 in 0b28732
({selector, first: false, isViewQuery: false, descendants: false, ...data}), |
ContentChild
and ContentChildren
have a different default (ViewChild
and ViewChildren
both default to true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
if (decorator.args.length === 2) { | ||
const resolvedArgs = staticallyResolve(decorator.args[1], checker); | ||
if (!assertIsStringArray(resolvedArgs, '@HostListener.args')) { | ||
throw new Error(`@HostListener arguments must be a string array`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: arguments
-> second argument
would be clearer IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
}) | ||
class FooCmp { | ||
@ContentChild('bar', {read: TemplateRef}) child: any; | ||
@ContentChildren(TemplateRef) children: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add tests for @View...
and getters / setters ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
template: 'Test', | ||
host: { | ||
'[attr.hello]': 'foo', | ||
'(click)': 'onClick($event)', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add test for args ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -128,7 +128,7 @@ export function extractDirectiveMetadata( | |||
|
|||
// Determine if `ngOnChanges` is a lifecycle hook defined on the component. | |||
const usesOnChanges = members.find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use members.some()
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
export class Context { | ||
constructor(readonly isStatement: boolean) {} | ||
|
||
get setExpressionMode(): Context { return this.isStatement ? new Context(false) : this; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: weird that a getter as side effect. make it a regular method ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to withExpressionMode
/ withStatementMode
.
The getters are pure and do not have side effects (they do not modify the current context class). They are, however, lazy - a new Context
is only created if it would be a change from the current Context
.
504f626
to
ffec1f1
Compare
This commit adds the ivy-local tag to //packages/router. Since the router depends on //packages/upgrade, it makes that package compatible with ngtsc as well. PR Close #24862
…4862) Within an @NgModule it's common to include in the imports a call to a ModuleWithProviders function, for example RouterModule.forRoot(). The old ngc compiler was able to handle this pattern because it had global knowledge of metadata of not only the input compilation unit but also all dependencies. The ngtsc compiler for Ivy doesn't have this knowledge, so the pattern of ModuleWithProviders functions is more difficult. ngtsc must be able to determine which module is imported via the function in order to expand the selector scope and properly tree-shake directives and pipes. This commit implements a solution to this problem, by adding a type parameter to ModuleWithProviders through which the actual module type can be passed between compilation units. The provider side isn't a problem because the imports are always copied directly to the ngInjectorDef. PR Close #24862
This commit adds support for template substitution expressions for ngtsc static resolution. PR Close #24862
Previously ngtsc had a few bugs handling special token types: * Injector was not properly translated to INJECTOR * ChangeDetectorRef was not injected via injectChangeDetectorRef() This commit fixes these two bugs, and also adds a test to ensure they continue to work correctly. PR Close #24862
@NgModule()s get compiled to two fields: ngModuleDef and ngInjectorDef. Both fields contain imports, as both selector scopes and injectors have the concept of composed units of configuration. Previously these fields were generated by static resolution of imports and exports in metadata. Support for ModuleWithProviders requires they be generated differently. ngModuleDef's imports/exports are generated as resolved lists of types, whereas ngInjectorDef's imports should reflect the raw expressions that the developer wrote in the metadata. This change modifies the NgModule handler and properly copies raw nodes for the imports and exports into the ngInjectorDef. PR Close #24862
ngInjectorDef.imports is generated from @NgModule.imports plus @NgModule.exports. A problem arises as a result, because @NgModule exports contain not only other modules (which will have ngInjectorDef fields), but components, directives, and pipes as well. Because of locality, it's difficult for the compiler to filter these out at build time. It's not impossible, but for now filtering them out at runtime will allow testing of the compiler against complex applications. PR Close #24862
This commit adds support for @ContentChild[ren] and @ViewChild[ren] in ngtsc. Previously queries were ignored. PR Close #24862
This change adds support for host bindings to ngtsc, and parses them both from decorators and from the metadata in the top-level annotation. PR Close #24862
Previously, the static resolver did its own interpretation of statements in the TypeScript AST, which only functioned on TypeScript code. ES5 code in particular would not work with the resolver as it had hard-coded assumptions about AST structure. This commit changes the resolver to use a ReflectionHost instead, which abstracts away understanding of the structural side of the AST. It adds 3 new methods to the ReflectionHost in support of this functionality: * getDeclarationOfIdentifier * getExportsOfModule * isClass PR Close #24862
Previously ngtsc had a bug where it would only detect the presence of ngOnChanges as a static method. This commit flips the condition and only recognizes ngOnChanges as a non-static method. PR Close #24862
…24862) Previously, references had the concept of an identifier, but would not properly detect whether the identifier should be used or not when generating an expression. This change fixes that logic. Additionally, now whenever an identifier resolves to a reference (even one imported from another module) as part of resolving an expression, the reference is updated to use that identifier. This ensures that for a class Foo declared in foo.ts, but referenced in an expression in bar.ts, the Reference returned includes the identifier from bar.ts, meaning that writing an expression in bar.ts for the Reference will not generate an import. PR Close #24862
…24862) Previously, when translating an assignment expression (e.g. x = 3), the translator would always print the statement as X = Y. However, if the expression is included in a larger expression (X = (Y = Z)), the translator would print "X = Y = Z" without regard for the outer expression context. Now, the translator understands when it's printing an expression statement (X = Y;) vs an expression in a larger context (X = (Y = Z);) and encapsulates the latter in parentheses. PR Close #24862
This commit moves the compiler compliance tests into compiler-cli, and uses ngtsc to run them instead of the custom compilation pipeline used before. Testing against ngtsc allows for validation of the real compiler output. This commit also fixes a few small issues that prevented the tests from passing. PR Close #24862
Previously, some of the *Def symbols were not exported or were exported as public API. This commit ensures every definition type is in the private export namespace. PR Close #24862
Previously ngtsc would use a tuple of class types for listing metadata in .d.ts files. For example, an @NgModule's declarations might be represented with the type: [NgIf, NgForOf, NgClass] If the module had no declarations, an empty tuple [] would be produced. This has two problems. 1. If the class type has generic type parameters, TypeScript will complain that they're not provided. 2. The empty tuple type is not actually legal. This commit addresses both problems. 1. Class types are now represented using the `typeof` operator, so the above declarations would be represented as: [typeof NgIf, typeof NgForOf, typeof NgClass]. Since typeof operates on a value, it doesn't require generic type arguments. 2. Instead of an empty tuple, `never` is used to indicate no metadata. PR Close #24862
Ivy definition types have a generic type which specifies the return type of the factory function. For example: static ngDirectiveDef<NgForOf, '[ngFor][ngForOf]'> However, in this case NgForOf itself has a type parameter <T>. Thus, writing the above is incorrect. This commit modifies ngtsc to understand the genericness of NgForOf and to write the following: static ngDirectiveDef<NgForOf<any>, '[ngFor][ngForOf]'> PR Close #24862
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
No description provided.