Commit Graph
86 Commits
Author SHA1 Message Date
Paul Gschwendtner 4f7c971ee7 fix(ivy): ngtsc throws if "flatModuleOutFile" is set to null (#32235)
In ngc is was valid to set the "flatModuleOutFile" option to "null". This is sometimes
necessary if a tsconfig extends from another one but the "fatModuleOutFile" option
needs to be unset (note that "undefined" does not exist as value in JSON)

Now if ngtsc is used to compile the project, ngtsc will fail with an error because it
tries to do string manipulation on the "flatModuleOutFile". This happens because
ngtsc only skips flat module indices if the option is set to "undefined".

Since this is not compatible with what was supported in ngc and such exceptions
should be avoided, the flat module check is now aligned with ngc.

```
TypeError: Cannot read property 'replace' of null
    at Object.normalizeSeparators (/home/circleci/project/node_modules/@angular/compiler-cli/src/ngtsc/util/src/path.js:35:21)
    at new NgtscProgram (/home/circleci/project/node_modules/@angular/compiler-cli/src/ngtsc/program.js:126:52)
```

Additionally setting the `flatModuleOutFile` option to an empty string
currently results in unexpected behavior. No errors is thrown, but the
flat module index file will be `.ts` (no file name; just extension).

This is now also fixed by treating an empty string similarly to
`null`.

PR Close #32235
2019-08-22 10:14:38 -07:00
Alex Rickabaugh 0677cf0cbe feat(ivy): use the schema registry to check DOM bindings (#32171)
Previously, ngtsc attempted to use the .d.ts schema for HTML elements to
check bindings to DOM properties. However, the TypeScript lib.dom.d.ts
schema does not perfectly align with the Angular DomElementSchemaRegistry,
and these inconsistencies would cause issues in apps. There is also the
concern of supporting both CUSTOM_ELEMENTS_SCHEMA and NO_ERRORS_SCHEMA which
would have been very difficult to do in the existing system.

With this commit, the DomElementSchemaRegistry is employed in ngtsc to check
bindings to the DOM. Previous work on producing template diagnostics is used
to support generation of this different kind of error with the same high
quality of error message.

PR Close #32171
2019-08-22 10:12:45 -07:00
Alex Rickabaugh 0287b234ea feat(ivy): convert all ngtsc diagnostics to ts.Diagnostics (#31952)
Historically, the Angular Compiler has produced both native TypeScript
diagnostics (called ts.Diagnostics) and its own internal Diagnostic format
(called an api.Diagnostic). This was done because TypeScript ts.Diagnostics
cannot be produced for files not in the ts.Program, and template type-
checking diagnostics are naturally produced for external .html template
files.

This design isn't optimal for several reasons:

1) Downstream tooling (such as the CLI) must support multiple formats of
diagnostics, adding to the maintenance burden.

2) ts.Diagnostics have gotten a lot better in recent releases, with support
for suggested changes, highlighting of the code in question, etc. None of
these changes have been of any benefit for api.Diagnostics, which have
continued to be reported in a very primitive fashion.

3) A future plugin model will not support anything but ts.Diagnostics, so
generating api.Diagnostics is a blocker for ngtsc-as-a-plugin.

4) The split complicates both the typings and the testing of ngtsc.

To fix this issue, this commit changes template type-checking to produce
ts.Diagnostics instead. Instead of reporting a special kind of diagnostic
for external template files, errors in a template are always reported in
a ts.Diagnostic that highlights the portion of the template which contains
the error. When this template text is distinct from the source .ts file
(for example, when the template is parsed from an external resource file),
additional contextual information links the error back to the originating
component.

A template error can thus be reported in 3 separate ways, depending on how
the template was configured:

1) For inline template strings which can be directly mapped to offsets in
the TS code, ts.Diagnostics point to real ranges in the source.

This is the case if an inline template is used with a string literal or a
"no-substitution" string. For example:

```typescript
@Component({..., template: `
<p>Bar: {{baz}}</p>
`})
export class TestCmp {
  bar: string;
}
```

The above template contains an error (no 'baz' property of `TestCmp`). The
error produced by TS will look like:

```
<p>Bar: {{baz}}</p>
          ~~~

test.ts:2:11 - error TS2339: Property 'baz' does not exist on type 'TestCmp'. Did you mean 'bar'?
```

2) For template strings which cannot be directly mapped to offsets in the
TS code, a logical offset into the template string will be included in
the error message. For example:

```typescript
const SOME_TEMPLATE = '<p>Bar: {{baz}}</p>';

@Component({..., template: SOME_TEMPLATE})
export class TestCmp {
  bar: string;
}
```

Because the template is a reference to another variable and is not an
inline string constant, the compiler will not be able to use "absolute"
positions when parsing the template. As a result, errors will report logical
offsets into the template string:

```
<p>Bar: {{baz}}</p>
          ~~~

test.ts (TestCmp template):2:15 - error TS2339: Property 'baz' does not exist on type 'TestCmp'.

  test.ts:3:28
    @Component({..., template: TEMPLATE})
                               ~~~~~~~~

    Error occurs in the template of component TestCmp.
```

This error message uses logical offsets into the template string, and also
gives a reference to the `TEMPLATE` expression from which the template was
parsed. This helps in locating the component which contains the error.

3) For external templates (templateUrl), the error message is delivered
within the HTML template file (testcmp.html) instead, and additional
information contextualizes the error on the templateUrl expression from
which the template file was determined:

```
<p>Bar: {{baz}}</p>
          ~~~

testcmp.html:2:15 - error TS2339: Property 'baz' does not exist on type 'TestCmp'.

  test.ts:10:31
    @Component({..., templateUrl: './testcmp.html'})
                                  ~~~~~~~~~~~~~~~~

    Error occurs in the template of component TestCmp.
```

PR Close #31952
2019-08-21 10:51:59 -07:00
atscott cfed0c0cf1 fix(ivy): Support selector-less directive as base classes (#32125)
Following #31379, this adds support for directives without a selector to
Ivy.

PR Close #32125
2019-08-20 09:56:54 -07:00
Pete Bacon Darwin eb5412d76f fix(ivy): reuse compilation scope for incremental template changes. (#31932)
Previously if only a component template changed then we would know to
rebuild its component source file. But the compilation was incorrect if the
component was part of an NgModule, since we were not capturing the
compilation scope information that had a been acquired from the NgModule
and was not being regenerated since we were not needing to recompile
the NgModule.

Now we register compilation scope information for each component, via the
`ComponentScopeRegistry` interface, so that it is available for incremental
compilation.

The `ComponentDecoratorHandler` now reads the compilation scope from a
`ComponentScopeReader` interface which is implemented as a compound
reader composed of the original `LocalModuleScopeRegistry` and the
`IncrementalState`.

Fixes #31654

PR Close #31932
2019-08-09 10:50:40 -07:00
JoostK 3a2b195a58 feat(ivy): translate type-check diagnostics to their original source (#30181)
PR Close #30181
2019-07-25 16:36:32 -07:00
Pete Bacon Darwin 7186f9c016 refactor(ivy): implement a virtual file-system layer in ngtsc + ngcc (#30921)
To improve cross platform support, all file access (and path manipulation)
is now done through a well known interface (`FileSystem`).

For testing a number of `MockFileSystem` implementations are provided.
These provide an in-memory file-system which emulates operating systems
like OS/X, Unix and Windows.

The current file system is always available via the static method,
`FileSystem.getFileSystem()`. This is also used by a number of static
methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass
`FileSystem` objects around all the time. The result of this is that one
must be careful to ensure that the file-system has been initialized before
using any of these static methods. To prevent this happening accidentally
the current file system always starts out as an instance of `InvalidFileSystem`,
which will throw an error if any of its methods are called.

You can set the current file-system by calling `FileSystem.setFileSystem()`.
During testing you can call the helper function `initMockFileSystem(os)`
which takes a string name of the OS to emulate, and will also monkey-patch
aspects of the TypeScript library to ensure that TS is also using the
current file-system.

Finally there is the `NgtscCompilerHost` to be used for any TypeScript
compilation, which uses a given file-system.

All tests that interact with the file-system should be tested against each
of the mock file-systems. A series of helpers have been provided to support
such tests:

* `runInEachFileSystem()` - wrap your tests in this helper to run all the
wrapped tests in each of the mock file-systems.
* `addTestFilesToFileSystem()` - use this to add files and their contents
to the mock file system for testing.
* `loadTestFilesFromDisk()` - use this to load a mirror image of files on
disk into the in-memory mock file-system.
* `loadFakeCore()` - use this to load a fake version of `@angular/core`
into the mock file-system.

All ngcc and ngtsc source and tests now use this virtual file-system setup.

PR Close #30921
2019-06-25 16:25:24 -07:00
Ayaz Hafiz 74f4f5dfab feat(ivy): integrate indexing pipeline with NgtscProgram (#31151)
Add an IndexingContext class to store indexing information and a
transformer module to generate indexing analysis. Integrate the indexing
module with the rest of NgtscProgram and add integration tests.

Closes #30959

PR Close #31151
2019-06-24 18:47:56 -07:00
Pete Bacon Darwin 48def92cad fix(ivy): ensure that changes to component resources trigger incremental builds (#30954)
Optimizations to skip compiling source files that had not changed
did not account for the case where only a resource file changes,
such as an external template or style file.

Now we track such dependencies and trigger a recompilation
if any of the previously tracked resources have changed.

This will require a change on the CLI side to provide the list of
resource files that changed to trigger the current compilation by
implementing `CompilerHost.getModifiedResourceFiles()`.

Closes #30947

PR Close #30954
2019-06-21 10:13:46 -07:00
Ayaz Hafiz 4ad323a4d6 feat(ivy): setup boilerplate for component indexing API (#30961)
Set up the skeleton for a compiler API that indexes components and their
templates on an independent indexing step.

Part of #30959

PR Close #30961
2019-06-14 10:48:12 -07:00
Olivier Combe 5e0f982961 feat(ivy): use i18n locale data to determine the plural form of ICU expressions (#29249)
Plural ICU expressions depend on the locale (different languages have different plural forms). Until now the locale was hard coded as `en-US`.
For compatibility reasons, if you use ivy with AOT and bootstrap your app with `bootstrapModule` then the `LOCALE_ID` token will be set automatically for ivy, which is then used to get the correct plural form.
If you use JIT, you need to define the `LOCALE_ID` provider on the module that you bootstrap.
For `TestBed` you can use either `configureTestingModule` or `overrideProvider` to define that provider.
If you don't use the compat mode and start your app with `renderComponent` you need to call `ɵsetLocaleId` manually to define the `LOCALE_ID` before bootstrap. We expect this to change once we start adding the new i18n APIs, so don't rely on this function (there's a reason why it's a private export).
PR Close #29249
2019-05-30 15:09:02 -04:00
Pete Bacon Darwin 76391f8999 fix(ivy): use ReflectionHost in AbsoluteModuleStrategy (#30200)
The AbsoluteModuleStrategy in ngtsc assumed that the source code is
formatted as TypeScript with regards to module exports.

In ngcc this is not always the case, so this commit changes
`AbsoluteModuleStrategy` so that it relies upon a `ReflectionHost`  to
compute the exports of a module.

PR Close #30200
2019-05-22 16:24:14 -07:00
Pete Bacon Darwin fbff03b476 feat(ivy): skip analysis of unchanged components (#30238)
Now that the dependent files and compilation scopes are being tracked in
the incremental state, we can skip analysing and emitting source files if
none of their dependent files have changed since the last compile.

The computation of what files (and their dependencies) are unchanged is
computed during reconciliation.

This commit also removes the previous emission skipping logic, since this
approach covers those cases already.

PR Close #30238
2019-05-10 12:10:40 -07:00
Pete Bacon Darwin 411524d341 feat(ivy): track compilation scope dependencies for components (#30238)
To support skipping analysis of a file containing a component
we need to know that none of the declarations that might affect
its ngtsc compilation have not changed. The files that we need to
check are those that contain classes from the `CompilationScope`
of the component. These classes are already tracked in the
`LocalModuleScopeRegistry`.

This commit modifies the `IvyCompilation` class to record the
files that are in each declared class's `CompilationScope` via
a new method, `recordNgModuleScopeDependencies()`, that is called
after all the handlers have been "resolved".

Further, if analysis is skipped for a declared class, then we need
to recover the analysis from the previous compilation run. To
support this, the `IncrementalState` class has been updated to
expose the `MetadataReader` and `MetadataRegistry` interfaces.
This is included in the `metaRegistry` object to capture these analyses,
and also in the `localMetaReader` as a fallback to use if the
current compilation analysis was skipped.

PR Close #30238
2019-05-10 12:10:40 -07:00
Pete Bacon Darwin 0a0b4c1d8f feat(ivy): track file dependencies due to partial evaluation (#30238)
As part of incremental compilation performance improvements, we need
to track the dependencies of files due to expressions being evaluated by
the `PartialEvaluator`.

The `PartialEvaluator` now accepts a `DependencyTracker` object, which is
used to track which files are visited when evaluating an expression.
The interpreter computes this `originatingFile` and stores it in the evaluation
`Context` so it can pass this to the `DependencyTracker.

The `IncrementalState` object implements this interface, which allows it to be
passed to the `PartialEvaluator` and so capture the file dependencies.

PR Close #30238
2019-05-10 12:10:40 -07:00
Pete Bacon Darwin 5b80ab372d fix(ivy): use CompilerHost.resolveModuleNames() if available (#30017)
Sometimes we need to override module resolution behaviour.
We do this by implementing the optional method `resolveModuleNames()`
on `CompilerHost`.

This commit ensures that we always try this method first before falling
back to the standard `ts.resolveModuleName`

PR Close #30017
2019-05-01 15:41:53 -07:00
Alex Rickabaugh d316a18dc6 fix(ivy): don't include query fields in type constructors (#30094)
Previously, ngtsc included query fields in the list of fields which can
affect the type of a directive via its type constructor. This feature
however has yet to be built, and View Engine in default mode does not
do this inference.

This caused an unexpected bug where private query fields (which should be
an error but are allowed by View Engine) cause the type constructor
signature to be invalid. This commit fixes that issue by disabling the
logic to include query fields.

PR Close #30094
2019-04-24 17:10:21 -07:00
Alex Rickabaugh 3938563565 fix(ivy): don't reuse a ts.Program more than once in ngtsc (#30090)
ngtsc previously could attempt to reuse the main ts.Program twice. This
occurred when template type-checking was enabled and then an incremental
build was performed. This breaks a TypeScript invariant - ts.Programs can
only be reused once.

The creation of the template type-checking program reuses the main program,
rendering it moot. Then, on the next incremental build the main program
would be subject to reuse again, which would crash inside TypeScript.

This commit fixes the issue by reusing the template type-checking program
from the previous run on the next incremental build. Since under normal
circumstances the files in the type-checking program aren't changed, this
should be just as fast.

Testing strategy: a test is added in the incremental_spec which validates
that program reuse with type-checking turned on does not crash the compiler.

Fixes #30079

PR Close #30090
2019-04-24 11:41:21 -07:00
Alex Rickabaugh d9ce8a4ab5 feat(ivy): introduce a flag to control template type-checking for Ivy (#29698)
Template type-checking is enabled by default in the View Engine compiler.
The feature in Ivy is not quite ready for this yet, so this flag will
temporarily control whether templates are type-checked in ngtsc.

The goal is to remove this flag after rolling out template type-checking in
google3 in Ivy mode, and making sure the feature is as compatible with the
View Engine implementation as possible.

Initially, the default value of the flag will leave checking disabled.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh 5268ae61a0 feat(ivy): support for template type-checking pipe bindings (#29698)
This commit adds support for template type-checking a pipe binding which
previously was not handled by the type-checking engine. In compatibility
mode, the arguments to transform() are not checked and the type returned
by a pipe is 'any'. In full type-checking mode, the transform() method's
type signature is used to check the pipe usage and infer the return type
of the pipe.

Testing strategy: TCB tests included.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh 98f86de8da perf(ivy): template type-check the entire program in 1 file if possible (#29698)
The template type-checking engine previously would assemble a type-checking
program by inserting Type Check Blocks (TCBs) into existing user files. This
approach proved expensive, as TypeScript has to re-parse and re-type-check
those files when processing the type-checking program.

Instead, a far more performant approach is to augment the program with a
single type-checking file, into which all TCBs are generated. Additionally,
type constructors are also inlined into this file.

This is not always possible - both TCBs and type constructors can sometimes
require inlining into user code, particularly if bound generic type
parameters are present, so the approach taken is actually a hybrid. These
operations are inlined if necessary, but are otherwise generated in a single
file.

It is critically important that the original program also include an empty
version of the type-checking file, otherwise the shape of the two programs
will be different and TypeScript will throw away all the old program
information. This leads to a painfully slow type checking pass, on the same
order as the original program creation. A shim to generate this file in the
original program is therefore added.

Testing strategy: this commit is largely a refactor with no externally
observable behavioral differences, and thus no tests are needed.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh f4c536ae36 feat(ivy): logical not and safe navigation operation handling in TCBs (#29698)
This commit adds support in the template type-checking engine for handling
the logical not operation and the safe navigation operation.

Safe navigation in particular is tricky, as the View Engine implementation
has a rather inconvenient flaw. View Engine checks a safe navigation
operation `a?.b` as:

```typescript
(a != null ? a!.b : null as any)
```

The type of this expression is always 'any', as the false branch of the
ternary has type 'any'. Thus, using null-safe navigation throws away the
type of the result, and breaks type-checking for the rest of the expression.

A flag is introduced in the type-checking configuration to allow Ivy to
mimic this behavior when needed.

Testing strategy: TCB tests included.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh 182e2c7449 feat(ivy): add backwards compatibility config to template type-checking (#29698)
View Engine's implementation of naive template type-checking is less
advanced than the current Ivy implementation. As a result, Ivy catches lots
of typing bugs which VE does not. As a result, it's necessary to tone down
the Ivy template type-checker in the default case.

This commit introduces a mechanism for doing that, by passing a config to
the template type-checking engine. Through this configuration, particular
checks can be loosened or disabled entirely.

Testing strategy: TCB tests included.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh cd1277cfb7 fix(ivy): include directive base class metadata when generating TCBs (#29698)
Previously the template type-checking code only considered the metadata of
directive classes actually referenced in the template. If those directives
had base classes, any inputs/outputs/etc of the base classes were not
tracked when generating the TCB. This resulted in bindings to those inputs
being incorrectly attributed to the host component or element.

This commit uses the new metadata package to follow directive inheritance
chains and use the full metadata for a directive for TCB generation.

Testing strategy: Template type-checking tests included.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh 9277afce61 refactor(ivy): move metadata registration to its own package (#29698)
Previously, metadata registration (the recording of collected metadata
during analysis of directives, pipes, and NgModules) was only used to
produce the `LocalModuleScope`, and thus was handled by the
`LocalModuleScopeRegistry`.

However, the template type-checker also needs information about registered
directives, outside of the NgModule scope determinations. Rather than
reuse the scope registry for an unintended purpose, this commit introduces
new abstractions for metadata registration and lookups in a separate
'metadata' package, which the scope registry implements.

This paves the way for a future commit to make use of this metadata for the
template type-checking system.

Testing strategy: this commit is a refactoring which introduces no new
functionality, so existing tests are sufficient.

PR Close #29698
2019-04-19 11:15:25 -07:00
Alex Rickabaugh 9147092a15 Revert "feat(ivy): use i18n locale data to determine the plural form of ICU expressions (#29249)" (#29918)
This reverts commit 6a8cca7975.

PR Close #29918
2019-04-15 16:55:51 -07:00
Olivier Combe 6a8cca7975 feat(ivy): use i18n locale data to determine the plural form of ICU expressions (#29249)
Plural ICU expressions depend on the locale (different languages have different plural forms). Until now the locale was hard coded as `en-US`.
For compatibility reasons, if you use ivy with AOT and bootstrap your app with `bootstrapModule` then the `LOCALE_ID` token will be set automatically for ivy, which is then used to get the correct plural form.
If you use JIT, you need to define the `LOCALE_ID` provider on the module that you bootstrap.
For `TestBed` you can use either `configureTestingModule` or `overrideProvider` to define that provider.
If you don't use the compat mode and start your app with `renderComponent` you need to call `ɵsetLocaleId` manually to define the `LOCALE_ID` before bootstrap. We expect this to change once we start adding the new i18n APIs, so don't rely on this function (there's a reason why it's a private export).
PR Close #29249
2019-04-15 10:40:26 -07:00
Alex Rickabaugh 7041e61562 perf(ivy): basic incremental compilation for ngtsc (#29380)
This commit introduces a mechanism for incremental compilation to the ngtsc
compiler.

Previously, incremental information was used in the construction of the
ts.Program for subsequent compilations, but was not used in ngtsc itself.

This commit adds an IncrementalState class, which tracks state between ngtsc
compilations. Currently, this supports skipping the TypeScript emit step
when the compiler can prove the contents of emit have not changed.

This is implemented for @Injectables as well as for files which don't
contain any Angular decorated types. These are the only files which can be
proven to be safe today.

See ngtsc/incremental/README.md for more details.

PR Close #29380
2019-04-01 15:13:56 -07:00
Alex Rickabaugh aaa16f286d feat(ivy): performance trace mechanism for ngtsc (#29380)
This commit adds a `tracePerformance` option for tsconfig.json. When
specified, it causes a JSON file with timing information from the ngtsc
compiler to be emitted at the specified path.

This tracing system is used to instrument the analysis/emit phases of
compilation, and will be useful in debugging future integration work with
@angular/cli.

See ngtsc/perf/README.md for more details.

PR Close #29380
2019-04-01 15:13:55 -07:00
Paul Gschwendtner 1e5a818719 fix(ivy): ngtsc is unable to detect flat module entry-point on windows (#29453)
Currently when building an Angular project with `ngtsc`
and `flatModuleOutFile` enabled, the Ngtsc build will fail
if there are multiple source files as root file names.

Ngtsc and NGC currently determine the entry-point for multiple
root file names by looking for files ending with `/index.ts`.

This functionality is technically deprecated, but still supported
and currently breaks on Windows as the root file names are not
guaranteed to be normalized POSIX-like paths.

In order to make this logic more reliable in the future, this commit
also switches the shim generators and entry-point logic to the branded
path types. This ensures that we don't break this in the future.

PR Close #29453
2019-03-27 13:46:37 -07:00