Previously, only directives and services with generic type parameters
would emit `any` as generic type when emitting Ivy metadata into .d.ts
files. Pipes can also have generic type parameters but did not emit
`any` for all type parameters, resulting in the omission of those
parameters which causes compilation errors.
This commit adds support for pipes with generic type arguments and emits
`any` as generic type in the Ivy metadata.
Fixes#29400
PR Close#29403
This commit adds a `NewEntryPointFileWriter` that will be used in
webpack integration. Instead of overwriting files in-place, this `FileWriter`
will make a copy of the TS program files and write the transformed files
there. It also updates the package.json with new properties that can be
used to access the new entry-point format.
FW-1121
PR Close#29092
If `targetEntryPointPath` is provided to `mainNgcc` then we will now mark all
the `propertiesToConsider` for that entry-point if we determine that
it does not contain code that was compiled by Angular (for instance it has
no `...metadata.json` file).
The commit also renames `__modified_by_ngcc__` to `__processed_by_ivy_ngcc__`, since
there may be entry-points that are marked despite ngcc not actually compiling anything.
PR Close#29092
Now we check the build-marker version for all the formats
rather than just the one we are going to compile.
This way we don't get into the situation where one format was
built with one version of ngcc and another format was built with
another version.
PR Close#29092
Now the public API does not contain internal types, such as `AbsoluteFsPath` and
`EntryPointJsonProperty`. Instead we just accept strings and then guard them in
`mainNgcc` as appropriate.
A new public API function (`hasBeenProcessed`) has been exported to allow programmatic
checking of the build marker when the package.json contents are already known.
PR Close#29092
Previously we always considered all the properties in the package.json
if no `propertiesToConsidere` were provided.
But this results in computing a new set of properties for each entry-point
plus iterating through many of the package.json properties that are
not related to bundle-format paths.
PR Close#29092
Sometimes, in ESM5 code, aliases to exported variables are used internally
to refer to the exported value. This prevented some analysis from being
able to match up a reference to an export to the actual export itself.
For example in the following code:
```
var HttpClientXsrfModule = /** @class */ (function () {
function HttpClientXsrfModule() {
}
HttpClientXsrfModule_1 = HttpClientXsrfModule;
HttpClientXsrfModule.withOptions = function (options) {
if (options === void 0) { options = {}; }
return {
ngModule: HttpClientXsrfModule_1,
providers: [],
};
};
var HttpClientXsrfModule_1;
HttpClientXsrfModule = HttpClientXsrfModule_1 = tslib_1.__decorate([
NgModule({
providers: [],
})
], HttpClientXsrfModule);
return HttpClientXsrfModule;
}());
```
We were not able to tell that the `ngModule: HttpClientXsrfModule_1` property
assignment was actually meant to refer to the `function HttpClientXrsfModule()`
declaration. This caused the `ModuleWithProviders` processing to fail.
This commit ensures that we can compile typings files using the ESM5
format, so we can now update the examples boilerplate tool so that it
does not need to compile the ESM2015 format at all.
PR Close#29092
In ESM5 code, static methods appear as property assignments onto the constructor
function. For example:
```
var MyClass = (function() {
function MyClass () {}
MyClass.staticMethod = function() {};
return MyClass;
})();
```
This commit teaches ngcc how to process these forms when searching
for `ModuleWithProviders` functions that need to be updated in the typings
files.
PR Close#29092
By default ngcc will compile all the format properties specified. With this
change you can configure ngcc so that it will stop compiling an entry-point
after the first property that matches the `propertiesToConsider`.
PR Close#29092
By ensuring that EntryPointBundle contains everything that `Transformer.transform()`
needs to do its work, we can simplify its signature.
PR Close#29092
Now that we are using package.json properties to indicate which
entry-point format to compile, it turns out that we don't really
need to distinguish between flat and non-flat formats, unless we
are compiling `@angular/core`.
PR Close#29092
You can now specify a list of properties in the package.json that
should be considered (in order) to find the path to the format to compile.
The build marker system has been updated to store the markers in
the package.json rather than an additional external file.
Also instead of tracking the underlying bundle format that was compiled,
it now tracks the package.json property.
BREAKING CHANGE:
The `proertiesToConsider` option replaces the previous `formats` option,
which specified the final bundle format, rather than the property in the
package.json.
If you were using this option to compile only specific bundle formats,
you must now modify your usage to pass in the properties in the package.json
that map to the format that you wish to compile.
In the CLI, the `--formats` is no longer available. Instead use the
`--properties` option.
FW-1120
PR Close#29092
You can now, programmatically, specify an entry-point where
the ngcc compilation will occur.
Only this entry-point and its dependencies will be compiled.
FW-1119
PR Close#29092
The `mainNgcc()` function has been refactored to make it easier to call
ngcc from JavaScript, rather than via the command line.
For example, the `yargs` argument parsing and the exception
handling/logging have moved to the `main-ngcc.ts`
file so that it is only used for the command line version.
FW-1118
PR Close#29092
Previously we only compiled the typings files, in ngcc, if there was
an ES2015 formatted bundle avaiable. This turns out to be an artificial
constraint and we can also support typings compilation via ES5 formats
too.
This commit changes the ngcc compiler to attempt typings compilation
via ES5 if necessary. The order of the formats to consider is now:
FESM2015, FESM5, ESM2015, ESM5.
FW-1122
PR Close#29092
This patch is the first of a few patches which separates the
styling logic between template bindings (e.g. <div [style])
from host bindings (e.g. @HostBinding('style')). This patch
in particular introduces a series of host-specific styling
instructions and changes the existing set of template styling
instructions not to accept directives. The underyling code (which
communicates with the styling algorithm) still works as it did
before.
This PR also separates the styling instruction code into a separate
file and moves over all other instructions into an dedicated
instructions directory.
PR Close#29292
This fix is for a bug in the ngtsc PartialEvaluator, which statically
evaluates expressions.
Sometimes, evaluating a reference requires resolving a function which is
declared in another module, and thus no function body is available. To
support this case, the PartialEvaluator has the concept of a foreign
function resolver.
This allows the interpretation of expressions like:
const router = RouterModule.forRoot([]);
even though the definition of the 'forRoot' function has no body. In
ngtsc today, this will be resolved to a Reference to RouterModule itself,
via the ModuleWithProviders foreign function resolver.
However, the PartialEvaluator also associates any Identifiers in the path
of this resolution with the Reference. This is done so that if the user
writes
const x = imported.y;
'x' can be generated as a local identifier instead of adding an import for
'y'.
This was at the heart of a bug. In the above case with 'router', the
PartialEvaluator added the identifier 'router' to the Reference generated
(through FFR) to RouterModule.
This is not correct. References that result from FFR expressions may not
have the same value at runtime as they do at compile time (indeed, this is
not the case for ModuleWithProviders). The Reference generated via FFR is
"synthetic" in the sense that it's constructed based on a useful
interpretation of the code, not an accurate representation of the runtime
value. Therefore, it may not be legal to refer to the Reference via the
'router' identifier.
This commit adds the ability to mark such a Reference as 'synthetic', which
allows the PartialEvaluator to not add the 'router' identifier down the
line. Tests are included for both the PartialEvaluator itself as well as the
resultant buggy behavior in ngtsc overall.
PR Close#29387
The `resolve` phase (run after all handlers have analyzed) was
introduced in 7d954dffd, but `ngcc` was not updated to run the handlers'
`resolve()` methods. As a result, certain operations (such as listing
directives used in component templates) would not be performed by
`ngcc`.
This commit fixes it by running the `resolve()` methods once analysis
has been completed.
PR Close#28963
Currently if an Angular library has multiple unnamed module re-exports, NGC will
generate incorrect metdata if the project is using the flat-module bundle option.
e.g.
_public-api.ts_
```ts
export * from '@mypkg/secondary1';
export * from '@mypkg/secondary2';
```
There are clearly two unnamed re-exports in the `public-api.ts` file. NGC right now
accidentally overwrites all previous re-exports with the last one. Resulting in the
generated metadata only containing a reference to `@mypkg/secondary2`.
This is problematic as it is common for primary library entry-points to have
multiple re-exports (e.g. Material re-exporting all public symbols; or flex-layout
exporting all public symbols from their secondary entry-points).
Currently Angular Material works around this issue by manually creating
a metadata file that declares the re-exports from all unnamed re-exports.
(see: https://github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85)
This workaround works fine currently, but is no longer easily integrated when
building the package output with Bazel. In order to be able to build such
libraries with Bazel (Material/flex-layout), we need to make sure that NGC
generates the proper flat-module metadata bundle.
PR Close#29360
Previously, ngtsc would resolve forward references while evaluating the
bootstrap, declaration, imports, and exports fields of NgModule types.
However, when generating the resulting ngModuleDef, the forward nature of
these references was not taken into consideration, and so the generated JS
code would incorrectly reference types not yet declared.
This commit fixes this issue by introducing function closures in the
NgModuleDef type, similarly to how NgComponentDef uses them for forward
declarations of its directives and pipes arrays. ngtsc will then generate
closures when required, and the runtime will unwrap them if present.
PR Close#29198
This fixes an issue with commit b6f6b117. In this commit, default imports
processed in a type-to-value conversion were recorded as non-local imports
with a '*' name, and the ImportManager generated a new default import for
them. When transpiled to ES2015 modules, this resulted in the following
correct code:
import i3 from './module';
// somewhere in the file, a value reference of i3:
{type: i3}
However, when the AST with this synthetic import and reference was
transpiled to non-ES2015 modules (for example, to commonjs) an issue
appeared:
var module_1 = require('./module');
{type: i3}
TypeScript renames the imported identifier from i3 to module_1, but doesn't
substitute later references to i3. This is because the import and reference
are both synthetic, and never went through the TypeScript AST step of
"binding" which associates the reference to its import. This association is
important during emit when the identifiers might change.
Synthetic (transformer-added) imports will never be bound properly. The only
possible solution is to reuse the user's original import and the identifier
from it, which will be properly downleveled. The issue with this approach
(which prompted the fix in b6f6b117) is that if the import is only used in a
type position, TypeScript will mark it for deletion in the generated JS,
even though additional non-type usages are added in the transformer. This
again would leave a dangling import.
To work around this, it's necessary for the compiler to keep track of
identifiers that it emits which came from default imports, and tell TS not
to remove those imports during transpilation. A `DefaultImportTracker` class
is implemented to perform this tracking. It implements a
`DefaultImportRecorder` interface, which is used to record two significant
pieces of information:
* when a WrappedNodeExpr is generated which refers to a default imported
value, the ts.Identifier is associated to the ts.ImportDeclaration via
the recorder.
* when that WrappedNodeExpr is later emitted as part of the statement /
expression translators, the fact that the ts.Identifier was used is
also recorded.
Combined, this tracking gives the `DefaultImportTracker` enough information
to implement another TS transformer, which can recognize default imports
which were used in the output of the Ivy transform and can prevent them
from being elided. This is done by creating a new ts.ImportDeclaration for
the imports with the same ts.ImportClause. A test verifies that this works.
PR Close#29266
Prior to this change default selector for Components was not applied in case selector is missing or defined as an empty string. This update aligns this behavior between Ivy and VE: now default selector is used for Components when it's needed. Directives with empty selector are not allowed and trigger a compile-time error in both Ivy and VE.
PR Close#29239
`api-extractor` binary is required for external consumers of `ng_module` that want to use the `bundle_dts` flag.
This also sets a different api-exttractor binary to use for ng_module, based if it's internal or external.
PR Close#29202
Prior to this change the code didn't take into account the fact that decorators can be aliases while importing into a script. As a result, these decorators were not recognized by Angular and various failures happened because of that. Now we take aliases into account and resolve decorator name properly.
PR Close#29195
`getCurrentDirectory` directory doesn't return a posix separated normalized path. While `rootDir` and `rootDirs` should return posix separated paths, it's best to not assume as other paths within the compiler options can be returned not posix separated such as `basePath`
See: https://github.com/Microsoft/TypeScript/blob/master/src/compiler/sys.ts#L635
This partially fixes#29140, however there needs to be a change in the CLI as well to handle this, as at the moment we are leaking devkit paths which is not correct.
Fixes#29140
PR Close#29151
Sometimes declarations are not exported publicly but are exported under
a private name. In this case, rather than adding a completely new
export to the entry point, we should create an export that aliases the
private name back to the original public name.
This is important when the typings files have been rolled-up using a tool
such as the [API Extractor](https://api-extractor.com/). In this case
the internal type of an aliased private export will be removed completely
from the typings file, so there is no "original" type to re-export.
For example:
If there are the following TS files:
**entry-point.ts**
```ts
export {Internal as External} from './internal';
```
**internal.ts**
```ts
export class Internal {
foo(): void;
}
```
Then the API Extractor might roll up the .d.ts files into:
```ts
export declare class External {
foo(): void;
}
```
In this case ngcc should add an export so the file looks like:
```ts
export declare class External {
foo(): void;
}
export {External as Internal};
```
PR Close#28735
ngsummary files were generated with an export for each class declaration.
However, some Angular code declares classes (class Foo) and exports them
(export {Foo}) separately, which was causing incomplete summary files.
This commit expands the set of symbol names for which summary exports will
be generated, fixing this issue.
PR Close#29193
Previously, when the NgModule scope resolver discovered semantic errors
within a users NgModules, it would throw assertion errors. TODOs in the
codebase indicated these should become ts.Diagnostics eventually.
Besides producing better-looking errors, there is another reason to make
this change asap: these assertions were shadowing actual errors, via an
interesting mechanism:
1) a component would produce a ts.Diagnostic during its analyze() step
2) as a result, it wouldn't register component metadata with the scope
resolver
3) the NgModule for the component references it in exports, which was
detected as an invalid export (no metadata registering it as a
component).
4) the resulting assertion error would crash the compiler, hiding the
real cause of the problem (an invalid component).
This commit should mitigate this problem by converting scoping errors to
proper ts.Diagnostics. Additionally, we should consider registering some
marker indicating a class is a directive/component/pipe without actually
requiring full metadata to be produced for it, which would allow suppression
of errors like "invalid export" for such invalid types.
PR Close#29191
At the moment, certain tests relies on resolving the module with an index.d.ts, this root cause might be some implementations are missing from the mocks.
Similar to: 58b4045359
PR Close#28884
This commit refactors and expands ngtsc's support for generating imports of
values from imports of types (this is used for example when importing a
class referenced in a type annotation in a constructor).
Previously, this logic handled "import {Foo} from" and "import * as foo
from" style imports, but failed on imports of default values ("import
Foo from"). This commit moves the type-to-value logic to a separate file and
expands it to cover the default import case. Doing this also required
augmenting the ImportManager to track default as well as non-default import
generation. The APIs were made a little cleaner at the same time.
PR Close#29146
In the TypeScript compiler API, emit() can be performed either on a single
ts.SourceFile or on the entire ts.Program simultaneously.
ngtsc previously used whole-program emit, which was convenient to use while
spinning up the project but has a significant drawback: it causes a type
checking operation to occur for the whole program, including .d.ts files.
In large Bazel environments (such as Google's codebase), an ngtsc invocation
can have a few .ts files and thousands of .d.ts inputs. This unwanted type
checking is therefore a significant drain on performance.
This commit switches ngtsc to emit each .ts file individually, avoiding the
unwanted type checking.
PR Close#29147
When processing a JavaScript program, TS may come across a symbol that has
been imported from a TypeScript typings file.
In this case the compiler may pass the ReflectionHost a `prototype` symbol
as an export of the class.
This pseudo-member symbol has no declarations, which previously caused the
code in `Esm5ReflectionHost.reflectMembers()` to crash.
Now we just quietly ignore such a symbol and leave `Esm2015ReflectionHost`
to deal with it.
(As it happens `Esm2015ReflectionHost` also quietly ignores this symbol).
PR Close#29158
Prior to this commit, i18n instructions (i18n, i18nStart) were generated before listener instructions. As a result, event listeners were attached to the wrong element (text node, not the parent element). This change updates the order of instructions and puts i18n ones after listeners, to make sure listeners are attached to the right elements.
PR Close#29173
For the template type checking to work correctly, it needs to know
what attributes are bound to expressions or directives, which may
require expressions in the template to be evaluated in a different
scope.
In inline templates, there are attributes that are now marked as
"Template" attributes. We need to ensure that the template
type checking code looks at these "bound" attributes as well as the
"input" attributes.
PR Close#29041