Compare commits

...

90 Commits

Author SHA1 Message Date
c34c223122 fix(ivy): avoid type coercion in binding-related instruction asserts (#29470)
ivy's bindingUpdated instruction is using the assertNotEqual check to make
sure that NO_CHANGE value (of type Object) is not passed as a value to be
dirty-checked. In practice it means that any value passed as a binding
value would be compared to the NO_CHANGE object.

It turns out that the assertNotEqual is using == and given
that binding values are of different type and we always compare it to the
NO_CHANGE object we were doing lots of type coercion. It resulted in calls
to expensive types conversions and calls to Object.toString().

A profiler reported ~15% of the self time spent in the assertNotEqual
but it turns out that removing type coercion speeds up Material Chips with
input scenario much more (~40ms down to ~20ms).

This PR introduces new assert method `assertNotSame` that uses strict equality
check. The new assertion is used in binding instructions to compare to
NO_CHANGE object reference.

PR Close #29470
2019-03-25 09:34:13 -07:00
26a8c5961e fix(router): support NgFactory promise in loadChildren typings (#29392)
The router loadChildren property already supports a promise that returns a NgModuleFactory, but the typings cause the compilation to fail.

PR Close #29392
2019-03-25 09:30:28 -07:00
41225289d7 ci: add codefresh badge (#29475)
Fix link in Codefresh ci readme
Add a public badge to show the last status of the last build

fix: cleanup

docs: move badge to the head

PR Close #29475
2019-03-25 09:23:48 -07:00
3612ddb433 refactor: remove extra line break in gulpfile (#29489)
PR Close #29489
2019-03-25 09:22:37 -07:00
09fab58109 fix(core): static-query schematic should detect queries in "ngDoCheck" and "ngOnChanges" (#29492)
Queries can be also used statically within the "ngDoCheck" and "ngOnChanges" lifecylce hook.
In order to properly detect all queries, we need to also respect these lifecycle hooks.

Resolves FW-1192

PR Close #29492
2019-03-25 09:21:35 -07:00
b3102b9de1 docs: changing "struture" to "structure" (#29497)
PR Close #29497
2019-03-25 09:21:10 -07:00
cfe6581fc8 test(ivy): remove passing test from blocklist (#29484)
Removes the test that was fixed by https://github.com/angular/material2/pull/15555 from the blocklist.

PR Close #29484
2019-03-22 16:46:00 -07:00
bef5043a5a fix(ivy): TestBed overriding custom ErrorHandler (#29482)
Fixes TestBed's default ErrorHandler overriding the one provided by the consumer via an `import`.

This PR resolves FW-1193.

PR Close #29482
2019-03-22 16:45:33 -07:00
b5295ad277 test(bazel): Add router to bazel integration test (#29459)
PR Close #29459
2019-03-22 13:17:01 -07:00
769d960db1 fix(bazel): workaround problem reading summary files from node_modules (#29459)
PR Close #29459
2019-03-22 13:17:00 -07:00
21be0fb926 fix(bazel): allow ng_module users to set createExternalSymbolFactoryReexports (#29459)
PR Close #29459
2019-03-22 13:17:00 -07:00
9bcc1e8dce ci: add .codefresh/ to the fw-dev-infra group (#29478)
PR Close #29478
2019-03-22 13:16:16 -07:00
a06f0340d2 docs(core): close tags in example (#29474)
PR Close #29474
2019-03-22 13:15:03 -07:00
66b72bfa58 fix(ivy): ViewContainerRef.destroy should properly clean the DOM (#29414)
PR Close #29414
2019-03-22 13:13:12 -07:00
00075647be fix(compiler): inherit param types when class has a constructor which takes no declared parameters and delegates up (#29232)
In ReflectionCapabilities, when checking for own parameters of a type, inherit the types properly for classes that do have a constructor, but the constructor takes no declared parameters and just delegates to super(...arguments). This removes the need to declare trivial constructors in such classes to make them work properly in JIT mode. Without this, DI fails and injectables are undefined.

PR Close #29232
2019-03-22 13:12:25 -07:00
ce789b75a4 docs: update developer guide for testing and IntelliJ (#29048)
provide the command for running all test suites
- don't make new contributors read the whole Bazel doc
add info about ClangFormatIJ plugin for IntelliJ
add info about Bazel plugin for IntelliJ
fix inconsistent casing of GitHub

PR Close #29048
2019-03-22 13:05:40 -07:00
7baf45fe88 docs: fix minor typo in testing.md (#29464)
PR Close #29464
2019-03-22 10:59:21 -07:00
f3e0cc89ed fix(ivy): properly check LView array size in binding asserts (#29476)
This fix corrects a bug where we were passing a binding _value_
in place of an expected binding index. This reulted in the binding
value being compared to an array length and buggy type coercion.

Fixing this bug speeds up test scenario by ~10-15%.

PR Close #29476
2019-03-22 10:59:00 -07:00
cfa6e8e008 docs: add Siddharth Ajmera to GDE resources (#28456)
PR Close #28456
2019-03-21 15:57:16 -07:00
02debebcff docs: add NG-DE 2019 to events page (#29079)
PR Close #29079
2019-03-21 15:55:15 -07:00
ed8d60d060 docs: add app shell guide (#28591)
PR Close #28591
2019-03-21 15:54:26 -07:00
9ea0d64d8b docs(core): indicate OnPush applies to children (#28320)
Indicate the OnPush change detection strategy applies to all child directives.
Resolves confusion from #12480.
PR Close #28320
2019-03-21 15:48:46 -07:00
dfcf759e33 docs: drop Coding Conventions section from style guide (#29331)
Closes #24153

PR Close #29331
2019-03-21 15:40:20 -07:00
21835af70c fix(ivy): handle class declarations consistently in ES5 code (#29209)
PR Close #29209
2019-03-21 22:20:24 +00:00
2790352d04 refactor(ivy): use ClassDeclaration in more ReflectionHost methods (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
bb6a3632f6 refactor(ivy): correctly type class declarations in ngtsc/ngcc (#29209)
Previously, several `ngtsc` and `ngcc` APIs dealing with class
declaration nodes used inconsistent types. For example, some methods of
the `DecoratorHandler` interface expected a `ts.Declaration` argument,
but actual `DecoratorHandler` implementations specified a stricter
`ts.ClassDeclaration` type.

As a result, the stricter methods would operate under the incorrect
assumption that their arguments were of type `ts.ClassDeclaration`,
while the actual arguments might be of different types (e.g. `ngcc`
would call them with `ts.FunctionDeclaration` or
`ts.VariableDeclaration` arguments, when compiling ES5 code).

Additionally, since we need those class declarations to be referenced in
other parts of the program, `ngtsc`/`ngcc` had to either repeatedly
check for `ts.isIdentifier(node.name)` or assume there was a `name`
identifier and use `node.name!`. While this assumption happens to be
true in the current implementation, working around type-checking is
error-prone (e.g. the assumption might stop being true in the future).

This commit fixes this by introducing a new type to be used for such
class declarations (`ts.Declaration & {name: ts.Identifier}`) and using
it consistently throughput the code.

PR Close #29209
2019-03-21 22:20:23 +00:00
2d859a8c3a refactor(ivy): implement DtsModuleScopeResolver from MetadataDtsModuleScopeResolver (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
70fffba054 refactor(ivy): remove unused code from TypeCheckContext (#29209)
PR Close #29209
2019-03-21 22:20:23 +00:00
e185d3a4ad ci: add codefresh (#29305)
PR Close #29305
2019-03-21 22:19:19 +00:00
6f3052b799 ci: disable sauce-connect ssl bumping (#29447)
By default we disable SSL bumping for all requests. This is because SSL
bumping is not needed for our test setup and in order to perform the SSL
bumping, Saucelabs intercepts all HTTP requests in the tunnel VM and modifies
them. This can cause flakiness as it makes all requests dependent on the SSL bumping
middleware.

See: https://wiki.saucelabs.com/display/DOCS/Troubleshooting+Sauce+Connect#TroubleshootingSauceConnect-DisablingSSLBumping

PR Close #29447
2019-03-21 22:18:18 +00:00
c18fa7b5bd build(docs-infra): upgrade cli command docs sources to 18d979cdc (#29437)
Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master).
Relevant changes in [commit range](cafa558cf...18d979cdc):

**Added**
- help/analytics.json

**Modified**
- help/add.json
- help/build.json
- help/generate.json
- help/new.json
- help/serve.json

PR Close #29437
2019-03-21 15:32:03 -04:00
693b350567 build: add @npm//jasmine-core dep back to jasmine_node_test in defaults.bzl (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
0b27c09b51 build(bazel): also back out of jasmine bootstrap simplification (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
861d6f1523 build(bazel): back out of @bazel/jasmine 0.27.7 with shard count (#29444)
PR Close #29444
2019-03-21 09:59:13 -07:00
dc10355d61 build(compiler-cli): enable full TypeScript strictness (#29436)
This commit enables strict: true in TypeScript builds of
//packages/compiler-cli.

PR Close #29436
2019-03-21 12:14:39 -04:00
6cd3743b44 refactor(service-worker): use Adapter#parseUrl() for all URL parsing (#27080)
This commit also ensures that the correct implementation is used on
environments that do not support `URL` (e.g. Node.js).

PR Close #27080
2019-03-21 12:07:56 -04:00
a24f4b51b3 test(service-worker): test support for multiple apps on different subpaths of a domain (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
84baa0bb08 test(service-worker): make it easy to use a different SW scope in tests (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
e721c08c7f feat(service-worker): support multiple apps on different subpaths of a domain (#27080)
Previously, it was not possible to have multiple apps (using
`@angular/service-worker`) on different subpaths of the same domain,
because each SW would overwrite the caches of the others (even though
their scope was different).

This commit fixes it by ensuring that the cache names created by the SW
are different for each scope.

Fixes #21388

PR Close #27080
2019-03-21 12:07:56 -04:00
37a154e4e6 test(service-worker): do not create testing artifacts on environments that do not support SW (#27080)
The tests will not be run anyway, so the artifacts are never used and
there might be errors if creating the testing artifacts relies on APIs
that are not available in that environment (e.g. `URL`).

PR Close #27080
2019-03-21 12:07:56 -04:00
415de9a291 test(service-worker): ensure SwTestHarness#parseUrl() behaves the same on browser and Node.js (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
b3dda0ebc1 refactor(service-worker): make second parameter to Adapter#parseUrl() optional (#27080)
PR Close #27080
2019-03-21 12:07:56 -04:00
41737bb4d3 docs: fix html tags in changelog (#29411)
PR Close #29411
2019-03-21 02:46:41 -04:00
afe3e72601 docs: add Luis Aviles to GDE resources (#29405)
PR Close #29405
2019-03-20 19:06:23 -04:00
5e3bbf79a6 build(bazel): add comment about advancing the version of yarn used under Bazel in the future (#29431)
PR Close #29431
2019-03-20 18:36:13 -04:00
d2b64cc008 build(bazel): revert back to yarn 1.12.1 under Bazel to fix Windows file-in-use issues (#29431)
PR Close #29431
2019-03-20 18:36:13 -04:00
9eb8274991 fix(ivy): emit generic type arguments in Pipe metadata (#29403)
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
2019-03-20 16:11:22 -04:00
17b3f11e07 fix(ivy): ChangeDetectorRef should be injectable on ng-container (#29424)
PR Close #29424
2019-03-20 15:14:21 -04:00
10734ac607 fix(ivy): Class selector directives execute properly on container elements (#29383)
PR Close #29383
2019-03-20 15:13:30 -04:00
68a9fe817c test: remove symlink workaround (#29426)
This is no longer required. And is causing some errors to some of our engineers

PR Close #29426
2019-03-20 15:13:09 -04:00
64e5628897 feat(ivy): ngcc - support creating a new copy of the entry-point format (#29092)
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
2019-03-20 14:45:55 -04:00
849b327986 refactor(ivy): ngcc - extract file writing out into a class (#29092)
This is in preparation of having different file writing strategies.

PR Close #29092
2019-03-20 14:45:55 -04:00
a827bc2e3a refactor(ivy): ngcc - mark target entry-point as processed even if ngcc was a noop (#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
2019-03-20 14:45:55 -04:00
083fb99033 fix(ivy): ngcc - fail build-marker check if any formats were compiled with different ngcc (#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
2019-03-20 14:45:55 -04:00
55ea8da6eb refactor(ivy): ngcc - clean up the public API and export hasBeenProcessed helper (#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
2019-03-20 14:45:55 -04:00
7ea0d1bd3a refactor(ivy): ngcc - use a fixed set of properties to compile if none provided (#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
2019-03-20 14:45:55 -04:00
bdcbd9ed4b fix(ivy): ngcc - teach Esm5ReflectionHost about aliased variables (#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
2019-03-20 14:45:55 -04:00
b48d6e1b13 fix(ivy): ngcc - empower Esm5ReflectionHost to analyze ModuleWithProviders functions (#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
2019-03-20 14:45:55 -04:00
68f9d705f8 build(ivy): ngcc - only test under no-ivy-aot mode (#29092)
We want ngcc to test non-AOT builds of the Angular packages,
so we filter the tests in using the `no-ivy-aot` tag.

PR Close #29092
2019-03-20 14:45:54 -04:00
229f035969 feat(ivy): ngcc - support only compiling the first format property to match (#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
2019-03-20 14:45:54 -04:00
c9f7cdaafd refactor(ivy): ngcc - simplify Transformer.transform API (#29092)
By ensuring that EntryPointBundle contains everything that `Transformer.transform()`
needs to do its work, we can simplify its signature.

PR Close #29092
2019-03-20 14:45:54 -04:00
7b55ba58b9 refactor(ivy): ngcc - remove flat-format and use AbsoluteFsPath (#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
2019-03-20 14:45:54 -04:00
cd449021c1 feat(ivy): ngcc - compile only specified package.json format properties (#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
2019-03-20 14:45:54 -04:00
4bb0259bc0 feat(ivy): ngcc - support targeting a start entry-point (#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
2019-03-20 14:45:54 -04:00
66239b9d09 refactor(ivy): expose ngcc programmatically (#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
2019-03-20 14:45:54 -04:00
a770aa231d refactor(ivy): move ngcc into a higher level folder (#29092)
PR Close #29092
2019-03-20 14:45:54 -04:00
cf4718c366 feat(ivy): ngcc - support dts compilation via ES5 bundles (#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
2019-03-20 14:45:54 -04:00
07aeafa75c style(ivy): ngcc - fix typo in comment (#29092)
PR Close #29092
2019-03-20 14:45:54 -04:00
7a67f8935d build: allow build-packages-dist.sh to be run from anywhere (#29092)
PR Close #29092
2019-03-20 14:45:54 -04:00
bc88816c10 test: ignore the generated demo folder in the bazel integration test (#29092)
PR Close #29092
2019-03-20 14:45:54 -04:00
0d0445063a ci: add devversion to fw-dev-infra (#29418)
PR Close #29418
2019-03-20 13:44:30 -04:00
ddfdf3cd26 docs: add alexeagle to @angular/fw-global-approvers (#29287)
This is for large-scale refactorings

PR Close #29287
2019-03-20 13:43:23 -04:00
d6d081e120 feat(bazel): Upgrade rules_nodejs and rules_sass (#29388)
PR Close #29388
2019-03-20 13:42:03 -04:00
38c778e371 docs: add docs team to Angular Team section on collaborators page (#29396)
PR Close #29396
2019-03-20 13:40:18 -04:00
3249020466 test(ivy): update root cause of failures in MatBadge (#29422)
PR Close #29422
2019-03-20 13:37:53 -04:00
d6e27a41ed test(ivy): remove passing snack bar test from blocklist (#29423)
Removes the test that was fixed by https://github.com/angular/material2/pull/15551 from the blocklist.

PR Close #29423
2019-03-20 13:35:15 -04:00
1a4d4a0e13 docs: fix typo (culd -> could) (#29412)
PR Close #29412
2019-03-20 13:34:21 -04:00
3121409957 Revert "build: add a nice Angular-themed color for the vscode status bar (#29407)" (#29419)
This reverts commit efcd6af17d.
Engineers on the team thought that the red color means something is
broken.
See slack discussion in #general

PR Close #29419
2019-03-20 13:31:27 -04:00
fd122b0739 ci: cache Material node_modules based on the lockfile checksum (#29417)
This will increase the cache hit rate for the `material-unit-tests` job.
Related to https://github.com/angular/angular/pull/29416#discussion_r267321140.

PR Close #29417
2019-03-20 13:13:27 -04:00
9a364a82fb docs: add Justin Schwartzenberger to GDE page (#29401)
PR Close #29401
2019-03-20 13:11:31 -04:00
a530ed11e8 ci: do not cache modified "node_modules" in "material-unit-tests" job (#29416)
Currently we cache the Material `node_modules` after
the `run_angular_material_unit_tests.sh` completed. This
means that the cache will incorrectly contain the Ivy NPM
package output which might be incompatible with the
other Material dependencies. e.g. the Material postinstall
command now uses a different NGC version that does not
work with the `typescript` version that has been specified in
the Material project.

PR Close #29416
2019-03-20 06:58:23 -07:00
2d7435daae build(bazel): fix router test failure (#29375)
PR Close #29375
2019-03-19 23:39:37 -04:00
b460b26308 build(bazel): update to nodejs rules 0.27.7 (#29375)
PR Close #29375
2019-03-19 23:39:37 -04:00
ea90435a6b build(bazel): fix jasmine_node_test defaults (#29375)
PR Close #29375
2019-03-19 23:39:37 -04:00
ea0e832e5f build(bazel): update to nodejs rules 0.27.6 (#29375)
PR Close #29375
2019-03-19 23:39:36 -04:00
7c4afb0da7 build: enable shard_count for some jasmine tests that have many specs (#29375)
PR Close #29375
2019-03-19 23:39:36 -04:00
603df13b14 build(bazel): update to @bazel/jasmine 0.27.4 (#29375)
PR Close #29375
2019-03-19 23:39:36 -04:00
f9b7b6d2f1 test(ivy): add new material tests to blocklist (#29409)
After rebasing Material on master, we found new tests that were
added and are still failing. This commit adds them to the blocklist
so we can fix them.

PR Close #29409
2019-03-19 23:28:38 -04:00
dafbbf8b64 fix(core): parse incorrect ML open tag as text (#29328)
This PR alligns markup language lexer with the previous behaviour in version 7.x:
https://stackblitz.com/edit/angular-iancj2

While this behaviour is not perfect (we should be giving users an error message
here about invalid HTML instead of assuming text node) this is probably best we
can do without more substential re-write of lexing / parsing infrastructure.

This PR just fixes #29231 and restores VE behaviour - a more elaborate fix will
be done in a separate PR as it requries non-trivial rewrites.

PR Close #29328
2019-03-19 23:23:31 -04:00
d59f02d902 docs: release notes for the v7.2.10 release 2019-03-19 20:19:14 -07:00
212 changed files with 4949 additions and 2835 deletions

View File

@ -573,24 +573,30 @@ jobs:
steps:
- *attach_workspace
- *init_environment
- run:
name: "Determining SHA of Material Ivy branch"
command: git ls-remote ${MATERIAL_REPO_URL} ${MATERIAL_REPO_BRANCH} | cut -f1 > material_repo_sha
- run:
name: "Cloning Material repository"
command: ./scripts/ci/clone_angular_material_repo.sh
- restore_cache:
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
# It needs to be hardcoded here, because env variables interpolation is not supported.
keys:
- v1-angular-material-{{ checksum "material_repo_sha" }}
- v1-angular-material-
- v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
- v2-angular-material-
- run:
name: Installing Material dependencies.
command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
# Save the cache before we run the Material unit tests script. This is necessary
# because we don't want to cache the node modules which have been modified to contain
# the attached Ivy package output.
- save_cache:
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
# It needs to be hardcoded here, because env variables interpolation is not supported.
key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
paths:
- "/tmp/material2/node_modules"
- run:
name: "Running Material unit tests"
command: ./scripts/ci/run_angular_material_unit_tests.sh
- save_cache:
key: v1-angular-material-{{ checksum "material_repo_sha" }}
paths:
# Needs to be hardcoded because environment variables are not interpolated here.
- "/tmp/material2/node_modules"
workflows:
version: 2

View File

@ -0,0 +1,103 @@
ARG core=mcr.microsoft.com/windows/servercore:1809
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
FROM $core as download
ARG node_version=10.13.0
ARG yarn_version=1.13.0
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV GPG_VERSION 2.3.4
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; \
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
RUN @( \
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', \
'FD3A5288F042B6850C66B31F09FE44734EB7990E', \
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', \
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', \
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', \
'B9AE9905FFD7803F25714661B63B535A4C206CA9', \
'77984A986EBC2AA786BC0F66B01FBB92821C587A', \
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', \
'4ED778F539E3634C779C87C6D7062848A1AB005C', \
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', \
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' \
) | foreach { \
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; \
}
ENV NODE_VERSION=$node_version
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; \
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; \
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; \
Expand-Archive node.zip -DestinationPath C:\ ; \
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
ENV YARN_VERSION=$yarn_version
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; \
$sig = Get-AuthenticodeSignature yarn.msi ; \
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; \
Write-Output $sig.SignerCertificate.Thumbprint ; \
if (@( \
'7E253367F8A102A91D04829E37F3410F14B68A5F', \
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' \
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; \
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
ENV GIT_VERSION 2.20.1
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; \
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; \
Expand-Archive git.zip -DestinationPath C:\git; \
Remove-Item git.zip
FROM $target as baseimage
ENV NPM_CONFIG_LOGLEVEL info
COPY --from=download /nodejs /nodejs
COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ]
COPY --from=download /git /git
ARG SETX=/M
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
CMD [ "node.exe" ]
FROM baseimage
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; \
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; \
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; \
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; \
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; \
Remove-Item msys2.tar.xz; \
Remove-Item msys2.tar; \
Remove-Item 7z.exe; \
Remove-Item -Recurse 7zip; \
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); \
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine); \
Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe; \
Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait; \
Remove-Item vc_redist.x64.exe
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
SHELL ["cmd", "/c"]
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
CMD ["cmd.exe"]

33
.codefresh/README.md Normal file
View File

@ -0,0 +1,33 @@
# CodeFresh configuration
[![Codefresh build status](https://g.codefresh.io/api/badges/pipeline/angular/angular%2Fangular%2Fangular?type=cf-1)](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
This folder contains configuration for the [CodeFresh](<https://codefresh.io/>) based CI checks for this repository.
## The build pipeline
CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository.
Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access.
## Caretaker
CodeFresh status can be found at <http://status.codefresh.io/>.
Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva.
## Rollout strategy
Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular).
After a week or two of running like this, we should reassess how stable and reliable it is.
Next steps include:
- building PRs
- showing build status publicly
- blocking PRs that break the build
- expanding the test suite

40
.codefresh/bazel.rc Normal file
View File

@ -0,0 +1,40 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Save downloaded repositories in a location that can be cached by CodeFresh. This helps us
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
# build --repository_cache=C:/codefresh/volume/bazel_repository_cache
# Don't be spammy in the logs
# TODO(gmagolan): Hide progress again once build performance improves
# Presently, CircleCI can timeout during bazel test ... with the following
# error: Too long with no output (exceeded 10m0s)
# build --noshow_progress
# Print all the options that apply to the build.
# This helps us diagnose which options override others
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
build --announce_rc
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CodeFresh VMs
# TODO(filipesilva): determine the correct memory limit
build --local_resources=8000,8.0,1.0
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true
# Include PATH in Windows build/tests
# https://github.com/bazelbuild/rules_typescript/pull/356
build --action_env=PATH
test --action_env=PATH --test_env=PATH
# Exclude tests known to not work on Windows.
# Chrome web tests are currently broken.
test --test_tag_filters=-browser:chromium-local

26
.codefresh/codefresh.yml Normal file
View File

@ -0,0 +1,26 @@
version: '1.0'
steps:
BuildImage:
type: build
image_name: node-bazel-windows
working_directory: ./.codefresh
no_cf_cache: true
build_arguments:
- node_version=10.13.0
- yarn_version=1.13.0
dockerfile: ./Dockerfile.win-1809
RunTests:
title: Run Example
image: ${{BuildImage}}
commands:
# Install dependencies
- yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress
# Create symlinks needed for Windows.
- scripts\windows\create-symlinks.cmd
# Add Bazel CI config
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
# Run tests
- yarn bazel test //tools/ts-api-guardian:all
- yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test

View File

@ -0,0 +1,6 @@
@echo off
REM Fix for https://github.com/docker/for-win/issues/2920
REM echo "Fixing msys64 folder..."
REM Touch all .dll files inside C:\msys64\
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
REM echo "Fixed msys64 folder."

4
.github/CODEOWNERS vendored
View File

@ -46,6 +46,7 @@
# andrewseguin - Andrew Seguin
# benlesh - Ben Lesh
# brandonroberts - Brandon Roberts
# devversion - Paul Gschwendtner
# filipesilva - Filipe Silva
# gkalpak - George Kalpakas
# hansl - Hans Larsen
@ -87,6 +88,7 @@
# - IgorMinar
# - kara
# - mhevery
# - alexeagle
# ===========================================================
@ -325,6 +327,7 @@
# ===========================================================
#
# - alexeagle
# - devversion
# - filipesilva
# - gkalpak
# - IgorMinar
@ -818,6 +821,7 @@ testing/** @angular/fw-test
/* @angular/fw-dev-infra
/.buildkite/** @angular/fw-dev-infra
/.circleci/** @angular/fw-dev-infra
/.codefresh/** @angular/fw-dev-infra
/.github/** @angular/fw-dev-infra
/.vscode/** @angular/fw-dev-infra
/docs/BAZEL.md @angular/fw-dev-infra

View File

@ -16,9 +16,6 @@
"**/dist/**": true,
"**/aio/src/generated/**": true,
},
"workbench.colorCustomizations": {
"statusBar.background" : "#CB2B38",
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,

View File

@ -23,7 +23,7 @@
### BREAKING CHANGES
* **core:** Certain elements (like `<tr>` or `<col>`) require parent elements to be of a certain type by the HTML specification
(ex. <tr> can only be inside <tbody> / <thead>). Before this change Angular template parser was auto-correcting
(ex. `<tr>` can only be inside `<tbody>` / `<thead>`). Before this change Angular template parser was auto-correcting
"invalid" HTML using the following rules:
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
@ -38,7 +38,7 @@ This meachanism of automatic wrapping / auto-correcting was problematic for seve
</projecting-tr-inside-tbody>
```
In the above example the `<projecting-tr-inside-tbody>` component culd be "surprised" to see additional
In the above example the `<projecting-tr-inside-tbody>` component could be "surprised" to see additional
`<tbody>` elements inserted by Angular HTML parser.
@ -48,6 +48,16 @@ Please update your TypeScript version to 3.3
<a name="7.2.10"></a>
## [7.2.10](https://github.com/angular/angular/compare/7.2.9...7.2.10) (2019-03-20)
### Bug Fixes
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([cf8d934](https://github.com/angular/angular/commit/cf8d934)), closes [/github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
<a name="8.0.0-beta.8"></a>
# [8.0.0-beta.8](https://github.com/angular/angular/compare/8.0.0-beta.7...8.0.0-beta.8) (2019-03-12)

View File

@ -15,8 +15,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch rules_nodejs so we can install our npm dependencies
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.4/rules_nodejs-0.27.4.tar.gz"],
sha256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.7/rules_nodejs-0.27.7.tar.gz"],
)
# Check the bazel version and download npm dependencies
@ -48,7 +48,15 @@ node_repositories(
node_version = "10.9.0",
package_json = ["//:package.json"],
preserve_symlinks = True,
yarn_version = "1.13.0",
# yarn 1.13.0 under Bazel has a regression on Windows that causes build errors on rebuilds:
# ```
# ERROR: Source forest creation failed: C:/.../fyuc5c3n/execroot/angular/external (Directory not empty)
# ```
# See https://github.com/angular/angular/pull/29431 for more information.
# It possible that versions of yarn past 1.13.0 do not have this issue, however, before
# advancing this version we need to test manually on Windows that the above error does not
# happen as the issue is not caught by CI.
yarn_version = "1.12.1",
)
yarn_install(

View File

@ -0,0 +1,59 @@
# App shell
App shell is a way to render a portion of your application via a route at build time.
It can improve the user experience by quickly launching a static rendered page (a skeleton common to all pages) while the browser downloads the full client version and switches to it automatically after the code loads.
This gives users a meaningful first paint of your application that appears quickly because the browser can simply render the HTML and CSS without the need to initialize any JavaScript.
Learn more in [The App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell).
## Step 1: Prepare the application
You can do this with the following CLI command:
<code-example format="." language="bash" linenums="false">
ng new my-app --routing
</code-example>
For an existing application, you have to manually add the `RouterModule` and defining a `<router-outlet>` within your application.
## Step 2: Create the app shell
Use the CLI to automatically create the app shell.
<code-example format="." language="bash" linenums="false">
ng generate app-shell --client-project my-app --universal-project server-app
</code-example>
* `my-app` takes the name of your client application.
* `server-app` takes the name of the Universal (or server) application.
After running this command you will notice that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
<code-example format="." language="none" linenums="false">
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/my-app-server",
"main": "src/main.server.ts",
"tsConfig": "tsconfig.server.json"
}
},
"app-shell": {
"builder": "@angular-devkit/build-angular:app-shell",
"options": {
"browserTarget": "my-app:build",
"serverTarget": "my-app:server",
"route": "shell"
}
}
</code-example>
## Step 3: Verify the app is built with the shell content
Use the CLI to build the `app-shell` target.
<code-example format="." language="bash" linenums="false">
ng run my-app:app-shell
</code-example>
To verify the build output, open `dist/my-app/index.html`. Look for default text `app-shell works!` to show that the app shell route was rendered as part of the output.

View File

@ -158,7 +158,7 @@ You can <a href="generated/zips/cli-quickstart/cli-quickstart.zip" target="_blan
</div>
For more information about Angular project files and the file structure, see [Workspace and project file struture](guide/file-structure).
For more information about Angular project files and the file structure, see [Workspace and project file structure](guide/file-structure).

View File

@ -1749,447 +1749,6 @@ A consistent class and file name convention make these modules easy to spot and
</table>
<a href="#toc">Back to top</a>
## Coding conventions
Have a consistent set of coding, naming, and whitespace conventions.
{@a 03-01}
### Classes
#### Style 03-01
<div class="s-rule do">
**Do** use upper camel case when naming classes.
</div>
<div class="s-why">
**Why?** Follows conventional thinking for class names.
</div>
<div class="s-why-last">
**Why?** Classes can be instantiated and construct an instance.
By convention, upper camel case indicates a constructable asset.
</div>
<code-example path="styleguide/src/03-01/app/core/exception.service.avoid.ts" region="example" header="app/shared/exception.service.ts">
</code-example>
<code-example path="styleguide/src/03-01/app/core/exception.service.ts" region="example" header="app/shared/exception.service.ts">
</code-example>
<a href="#toc">Back to top</a>
{@a 03-02}
### Constants
#### Style 03-02
<div class="s-rule do">
**Do** declare variables with `const` if their values should not change during the application lifetime.
</div>
<div class="s-why">
**Why?** Conveys to readers that the value is invariant.
</div>
<div class="s-why-last">
**Why?** TypeScript helps enforce that intent by requiring immediate initialization and by
preventing subsequent re-assignment.
</div>
<div class="s-rule consider">
**Consider** spelling `const` variables in lower camel case.
</div>
<div class="s-why">
**Why?** Lower camel case variable names (`heroRoutes`) are easier to read and understand
than the traditional UPPER_SNAKE_CASE names (`HERO_ROUTES`).
</div>
<div class="s-why-last">
**Why?** The tradition of naming constants in UPPER_SNAKE_CASE reflects
an era before the modern IDEs that quickly reveal the `const` declaration.
TypeScript prevents accidental reassignment.
</div>
<div class="s-rule do">
**Do** tolerate _existing_ `const` variables that are spelled in UPPER_SNAKE_CASE.
</div>
<div class="s-why-last">
**Why?** The tradition of UPPER_SNAKE_CASE remains popular and pervasive,
especially in third party modules.
It is rarely worth the effort to change them at the risk of breaking existing code and documentation.
</div>
<code-example path="styleguide/src/03-02/app/core/data.service.ts" header="app/shared/data.service.ts">
</code-example>
<a href="#toc">Back to top</a>
{@a 03-03}
### Interfaces
#### Style 03-03
<div class="s-rule do">
**Do** name an interface using upper camel case.
</div>
<div class="s-rule consider">
**Consider** naming an interface without an `I` prefix.
</div>
<div class="s-rule consider">
**Consider** using a class instead of an interface for services and declarables (components, directives, and pipes).
</div>
<div class="s-rule consider">
**Consider** using an interface for data models.
</div>
<div class="s-why">
**Why?** <a href="https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines">TypeScript guidelines</a>
discourage the `I` prefix.
</div>
<div class="s-why">
**Why?** A class alone is less code than a _class-plus-interface_.
</div>
<div class="s-why">
**Why?** A class can act as an interface (use `implements` instead of `extends`).
</div>
<div class="s-why-last">
**Why?** An interface-class can be a provider lookup token in Angular dependency injection.
</div>
<code-example path="styleguide/src/03-03/app/core/hero-collector.service.avoid.ts" region="example" header="app/shared/hero-collector.service.ts">
</code-example>
<code-example path="styleguide/src/03-03/app/core/hero-collector.service.ts" region="example" header="app/shared/hero-collector.service.ts">
</code-example>
<a href="#toc">Back to top</a>
{@a 03-04}
### Properties and methods
#### Style 03-04
<div class="s-rule do">
**Do** use lower camel case to name properties and methods.
</div>
<div class="s-rule avoid">
**Avoid** prefixing private properties and methods with an underscore.
</div>
<div class="s-why">
**Why?** Follows conventional thinking for properties and methods.
</div>
<div class="s-why">
**Why?** JavaScript lacks a true private property or method.
</div>
<div class="s-why-last">
**Why?** TypeScript tooling makes it easy to identify private vs. public properties and methods.
</div>
<code-example path="styleguide/src/03-04/app/core/toast.service.avoid.ts" region="example" header="app/shared/toast.service.ts">
</code-example>
<code-example path="styleguide/src/03-04/app/core/toast.service.ts" region="example" header="app/shared/toast.service.ts">
</code-example>
<a href="#toc">Back to top</a>
{@a 03-06}
### Import line spacing
#### Style 03-06
<div class="s-rule consider">
**Consider** leaving one empty line between third party imports and application imports.
</div>
<div class="s-rule consider">
**Consider** listing import lines alphabetized by the module.
</div>
<div class="s-rule consider">
**Consider** listing destructured imported symbols alphabetically.
</div>
<div class="s-why">
**Why?** The empty line separates _your_ stuff from _their_ stuff.
</div>
<div class="s-why-last">
**Why?** Alphabetizing makes it easier to read and locate symbols.
</div>
<code-example path="styleguide/src/03-06/app/heroes/shared/hero.service.avoid.ts" region="example" header="app/heroes/shared/hero.service.ts">
</code-example>
<code-example path="styleguide/src/03-06/app/heroes/shared/hero.service.ts" region="example" header="app/heroes/shared/hero.service.ts">
</code-example>
<a href="#toc">Back to top</a>

View File

@ -289,7 +289,7 @@ written without assistance from Angular testing utilities.
#### Services with dependencies
Services often depend on other services that Angular injects into the constructor.
In many cases, it easy to create and _inject_ these dependencies by hand while
In many cases, it's easy to create and _inject_ these dependencies by hand while
calling the service's constructor.
The `MasterService` is a simple example:
@ -318,7 +318,7 @@ Prefer spies as they are usually the easiest way to mock services.
These standard testing techniques are great for unit testing services in isolation.
However, you almost always inject service into application classes using Angular
However, you almost always inject services into application classes using Angular
dependency injection and you should have tests that reflect that usage pattern.
Angular testing utilities make it easy to investigate how injected services behave.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -673,17 +673,86 @@
"group": "Collaborator",
"picture": "cexbrayat.jpg",
"bio": "Author of `Become a ninja with Angular (2+)` https://books.ninja-squad.com/angular - Angular trainer and @Ninja-Squad co-founder"
},
},
"CaerusKaru": {
"name": "Adam Plumer",
"group": "Collaborator",
"mentor": "vikerman",
"picture": "CaerusKaru.jpg"
},
},
"jbedard": {
"name": "Jason Bedard",
"group": "Collaborator",
"mentor": "kyliau",
"picture": "jbedard.png"
},
"jschwarty": {
"name": "Justin Schwartzenberger",
"picture": "justinschwartzenberger.jpg",
"twitter": "schwarty",
"website": "https://schwarty.com",
"bio": "Justin (aka Schwarty) is a Google Developer Expert in Web Technologies and Angular, the host and maintainer of the weekly AngularAir live video broadcast, educator, writer and content creator. He has Angular courses available on LinkedIn Learning and Pluralsight and loves passing on years of full stack development knowledge to help empower others to find their inner awesomeness!",
"group": "GDE"
},
"brandonroberts": {
"name": "Brandon Roberts",
"picture": "brandonroberts.jpg",
"twitter": "brandontroberts",
"website": "https://brandonroberts.dev",
"bio": "Brandon is a developer and technical writer working on guides, tutorials, application development, and infrastructure for the Angular docs. He is also a maintainer of the NgRx project, building reactive libraries for Angular.",
"group": "Angular",
"lead": "dennispbrown"
},
"chembu": {
"name": "Sreevani Sreejith",
"picture": "sreevani.jpg",
"bio": "Sreevani is a tech writer with prior programming experience. She writes documentation for the Angular framework team. Outside of work, she likes practicing yoga, honing her skills on classical dance forms, and baking cakes.",
"group": "Angular",
"lead": "dennispbrown"
},
"dennispbrown": {
"name": "Denny Brown",
"picture": "denny.jpg",
"bio": "Denny is founder of Expert Support, a professional services firm specializing in technical communication, and leads the Angular technical writing team. His lifelong passion has been to reduce the time and effort required to understand complex technical information. Early on, he was Associate Chairman of the Computer Science Department at Stanford, where he taught introductory courses in programming. He also plays old-timers baseball in local leagues and national tournaments.",
"group": "Angular",
"lead": "bradlygreen"
},
"jbogarthyde": {
"name": "Judy Bogart",
"picture": "judy.png",
"group": "Angular",
"lead": "dennispbrown"
},
"jenniferfell": {
"name": "Jennifer Fell",
"picture": "jennifer.jpg",
"website": "http://silverpath.org",
"bio": "Jennifer is a technical content strategist, architect, designer, and writer. As lead of the Angular docs team, she's always interested in learning more about how developers learn and use Angular. Her offline persona is a horsewoman in Idaho.",
"group": "Angular",
"lead": "dennispbrown"
},
"kapunahelewong": {
"name": "Kapunahele Wong",
"picture": "kapunahele.jpg",
"twitter": "kapunahele",
"bio": "Kapunahele is a developer and Angular fan who works on the Angular docs writing guides and developing example apps. She also enjoys Native Hawaiian practices, textile arts, and marveling at little, inconspicuous plants growing in forgotten places outdoors.",
"group": "Angular",
"lead": "dennispbrown"
},
"luixaviles": {
"name": "Luis Aviles",
"picture": "luixaviles.jpg",
"twitter": "luixaviles",
"website": "https://luixaviles.com",
"bio": "Luis is an enthusiast of Open Source software and communities, as well as being a public speaker, a technology trainer and an author of courses and technical articles. He is the organizer of the Angular Bolivia community and NG Bolivia conference. When he's not coding, Luis is reading about Astronomy or nerding about outer space, photography or even doing Astrophotography.",
"group": "GDE"
},
"siddajmera": {
"name": "Siddharth Ajmera",
"picture": "sidd-ajmera.jpg",
"twitter": "SiddAjmera",
"website": "https://webstackup.com/",
"bio": "Siddharth is a Full Stack JavaScript Developer and a GDE in Angular. He's passionate about sharing his knowledge on Angular, Firebase and the Web in general. He's the organizer of WebStack, a local community of developers focused on Web, Mobile, Voice and Server related technologies in general. WebStack hosts free monthly meetups every 2nd or 3rd Saturday of the month. Siddharth is also an avid photographer and loves traveling. Find him anywhere on the Web with `SiddAjmera`.",
"group": "GDE"
}
}
}

View File

@ -31,6 +31,12 @@
<td>London, UK</td>
<td>September 19-20, 2019</td>
</tr>
<!-- NG-DE 2019-->
<tr>
<th><a href="https://ng-de.org/" title="NG-DE">NG-DE</a></th>
<td>Berlin, Germany</td>
<td>August 29th workshops, 30-31 conference, 2019</td>
</tr>
</tbody>
</table>

View File

@ -439,6 +439,11 @@
"title": "Getting Started",
"tooltip": "Enabling the service worker in a CLI project and observing behavior in the browser."
},
{
"url": "guide/app-shell",
"title": "App Shell",
"tooltip": "Enabling the App Shell in a CLI project."
},
{
"url": "guide/service-worker-communications",
"title": "Service Worker Communication",

View File

@ -17,9 +17,9 @@
"build": "yarn ~~build",
"prebuild-local": "yarn setup-local",
"build-local": "yarn ~~build",
"prebuild-with-ivy": "yarn setup-local && yarn ivy-ngcc --formats fesm2015 fesm5",
"prebuild-with-ivy": "yarn setup-local && yarn ivy-ngcc --properties module es2015",
"build-with-ivy": "node scripts/build-with-ivy",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js cafa558cf",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 18d979cdc",
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn update-webdriver",

View File

@ -12,80 +12,36 @@ const EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
const BOILERPLATE_PATHS = {
cli: [
'src/environments/environment.prod.ts',
'src/environments/environment.ts',
'src/assets/.gitkeep',
'src/browserslist',
'src/favicon.ico',
'src/karma.conf.js',
'src/polyfills.ts',
'src/test.ts',
'src/tsconfig.app.json',
'src/tsconfig.spec.json',
'src/tslint.json',
'e2e/src/app.po.ts',
'e2e/protractor.conf.js',
'e2e/tsconfig.e2e.json',
'.editorconfig',
'angular.json',
'package.json',
'tsconfig.json',
'tslint.json'
'src/environments/environment.prod.ts', 'src/environments/environment.ts',
'src/assets/.gitkeep', 'src/browserslist', 'src/favicon.ico', 'src/karma.conf.js',
'src/polyfills.ts', 'src/test.ts', 'src/tsconfig.app.json', 'src/tsconfig.spec.json',
'src/tslint.json', 'e2e/src/app.po.ts', 'e2e/protractor.conf.js', 'e2e/tsconfig.e2e.json',
'.editorconfig', 'angular.json', 'package.json', 'tsconfig.json', 'tslint.json'
],
systemjs: [
'src/systemjs-angular-loader.js',
'src/systemjs.config.js',
'src/tsconfig.json',
'bs-config.json',
'bs-config.e2e.json',
'package.json',
'tslint.json'
'src/systemjs-angular-loader.js', 'src/systemjs.config.js', 'src/tsconfig.json',
'bs-config.json', 'bs-config.e2e.json', 'package.json', 'tslint.json'
],
common: [
'src/styles.css'
]
common: ['src/styles.css']
};
// All paths in this tool are relative to the current boilerplate folder, i.e boilerplate/i18n
// This maps the CLI files that exists in a parent folder
const cliRelativePath = BOILERPLATE_PATHS.cli.map(file => `../cli/${file}`);
BOILERPLATE_PATHS.elements = [
...cliRelativePath,
'tsconfig.json'
];
BOILERPLATE_PATHS.elements = [...cliRelativePath, 'tsconfig.json'];
BOILERPLATE_PATHS.i18n = [
...cliRelativePath,
'angular.json',
'package.json'
];
BOILERPLATE_PATHS.i18n = [...cliRelativePath, 'angular.json', 'package.json'];
BOILERPLATE_PATHS['service-worker'] = [
...cliRelativePath,
'angular.json',
'package.json'
];
BOILERPLATE_PATHS['service-worker'] = [...cliRelativePath, 'angular.json', 'package.json'];
BOILERPLATE_PATHS.testing = [
...cliRelativePath,
'angular.json'
];
BOILERPLATE_PATHS.testing = [...cliRelativePath, 'angular.json'];
BOILERPLATE_PATHS.universal = [
...cliRelativePath,
'angular.json',
'package.json'
];
BOILERPLATE_PATHS.universal = [...cliRelativePath, 'angular.json', 'package.json'];
BOILERPLATE_PATHS.ivy = {
systemjs: [
'rollup-config.js',
'tsconfig-aot.json'
],
cli: [
'src/tsconfig.app.json'
]
systemjs: ['rollup-config.js', 'tsconfig-aot.json'],
cli: ['src/tsconfig.app.json']
};
BOILERPLATE_PATHS.schematics = [
@ -101,20 +57,19 @@ class ExampleBoilerPlate {
*/
add(ivy = false) {
// Get all the examples folders, indicated by those that contain a `example-config.json` file
const exampleFolders = this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
const exampleFolders =
this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) {
throw new Error(`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` +
`Perhaps you need to run "yarn example-use-npm" or "yarn example-use-local" to install the dependencies?`);
throw new Error(
`The shared node_modules folder for the examples (${SHARED_NODE_MODULES_PATH}) is missing.\n` +
`Perhaps you need to run "yarn example-use-npm" or "yarn example-use-local" to install the dependencies?`);
}
if (ivy) {
// We only need the "fesm5" bundles as the CLI webpack build does not need
// any other formats for building and serving. Ngcc currently only updates
// the module typings if we specified an "es2015" format. This means that
// we also need to build with "fesm2015" in order to get updated typings
// which are needed for compilation.
shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --formats fesm2015 fesm5`);
// any other formats for building and serving.
shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --properties module`);
}
exampleFolders.forEach(exampleFolder => {
@ -128,16 +83,20 @@ class ExampleBoilerPlate {
const boilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, boilerPlateType);
// Copy the boilerplate specific files
BOILERPLATE_PATHS[boilerPlateType].forEach(filePath => this.copyFile(boilerPlateBasePath, exampleFolder, filePath));
BOILERPLATE_PATHS[boilerPlateType].forEach(
filePath => this.copyFile(boilerPlateBasePath, exampleFolder, filePath));
// Copy the boilerplate common files
BOILERPLATE_PATHS.common.forEach(filePath => this.copyFile(BOILERPLATE_COMMON_BASE_PATH, exampleFolder, filePath));
BOILERPLATE_PATHS.common.forEach(
filePath => this.copyFile(BOILERPLATE_COMMON_BASE_PATH, exampleFolder, filePath));
// Copy Ivy specific files
if (ivy) {
const ivyBoilerPlateType = boilerPlateType === 'systemjs' ? 'systemjs' : 'cli';
const ivyBoilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, 'ivy', ivyBoilerPlateType);
BOILERPLATE_PATHS.ivy[ivyBoilerPlateType].forEach(filePath => this.copyFile(ivyBoilerPlateBasePath, exampleFolder, filePath));
const ivyBoilerPlateBasePath =
path.resolve(BOILERPLATE_BASE_PATH, 'ivy', ivyBoilerPlateType);
BOILERPLATE_PATHS.ivy[ivyBoilerPlateType].forEach(
filePath => this.copyFile(ivyBoilerPlateBasePath, exampleFolder, filePath));
}
});
}
@ -145,23 +104,20 @@ class ExampleBoilerPlate {
/**
* Remove all the boilerplate files from all the examples
*/
remove() {
shelljs.exec('git clean -xdfq', { cwd: EXAMPLES_BASE_PATH });
}
remove() { shelljs.exec('git clean -xdfq', {cwd: EXAMPLES_BASE_PATH}); }
main() {
yargs
.usage('$0 <cmd> [args]')
.command('add', 'add the boilerplate to each example', (yrgs) => this.add(yrgs.argv.ivy))
.command('remove', 'remove the boilerplate from each example', () => this.remove())
.demandCommand(1, 'Please supply a command from the list above')
.argv;
yargs.usage('$0 <cmd> [args]')
.command('add', 'add the boilerplate to each example', (yrgs) => this.add(yrgs.argv.ivy))
.command('remove', 'remove the boilerplate from each example', () => this.remove())
.demandCommand(1, 'Please supply a command from the list above')
.argv;
}
getFoldersContaining(basePath, filename, ignore) {
const pattern = path.resolve(basePath, '**', filename);
const ignorePattern = path.resolve(basePath, '**', ignore, '**');
return glob.sync(pattern, { ignore: [ignorePattern] }).map(file => path.dirname(file));
return glob.sync(pattern, {ignore: [ignorePattern]}).map(file => path.dirname(file));
}
copyFile(sourceFolder, destinationFolder, filePath) {
@ -171,13 +127,11 @@ class ExampleBoilerPlate {
filePath = this.normalizePath(filePath);
const destinationPath = path.resolve(destinationFolder, filePath);
fs.copySync(sourcePath, destinationPath, { overwrite: true });
fs.copySync(sourcePath, destinationPath, {overwrite: true});
fs.chmodSync(destinationPath, 444);
}
loadJsonFile(filePath) {
return fs.readJsonSync(filePath, { throws: false }) || {};
}
loadJsonFile(filePath) { return fs.readJsonSync(filePath, {throws: false}) || {}; }
normalizePath(filePath) {
// transform for example ../cli/src/tsconfig.app.json to src/tsconfig.app.json

View File

@ -85,12 +85,15 @@ To build Angular run:
Bazel is used as the primary tool for building and testing Angular. Building and testing is
incremental with Bazel, and it's possible to only run tests for an individual package instead
of for all packages.
of for all packages. Read more about this in the [BAZEL.md](./BAZEL.md) document.
Read more about this in the [BAZEL.md](./BAZEL.md) document. You should execute all test suites
before submitting a PR to Github.
You should execute all test suites before submitting a PR to GitHub:
- `yarn bazel test packages/...`
All the tests are executed on our Continuous Integration infrastructure and a PR could only be
**Note**: The first test run will be much slower than future runs. This is because future runs will
benefit from Bazel's capability to do incremental builds.
All the tests are executed on our Continuous Integration infrastructure. PRs can only be
merged if the code is formatted properly and all tests are passing.
## <a name="clang-format"></a> Formatting your source code
@ -109,6 +112,13 @@ A better way is to set up your IDE to format the changed file on each file save.
It will automatically pick up the settings from Angular's [settings.json](../.vscode/settings.json).
### WebStorm / IntelliJ
1. Install the [ClangFormatIJ](https://plugins.jetbrains.com/plugin/8396-clangformatij) plugin
1. Open `Preferences->Tools->clang-format`
1. Find the field named "PATH"
1. Add `<PATH_TO_YOUR_WORKSPACE>/angular/node_modules/clang-format/bin/<OS>/`
where the OS options are: `darwin_x64`, `linux_x64`, and `win32`.
## Linting/verifying your source code
You can check that your code is properly formatted and adheres to coding style by running:
@ -128,17 +138,17 @@ You may find that your un-merged change needs some validation from external part
Rather than requiring them to pull your Pull Request and build Angular locally, you can
publish the `*-builds` snapshots just like our CircleCI build does.
First time, you need to create the github repositories:
First time, you need to create the GitHub repositories:
``` shell
$ export TOKEN=[get one from https://github.com/settings/tokens]
$ CREATE_REPOS=1 ./scripts/ci/publish-build-artifacts.sh [github username]
$ CREATE_REPOS=1 ./scripts/ci/publish-build-artifacts.sh [GitHub username]
```
For subsequent snapshots, just run
``` shell
$ ./scripts/ci/publish-build-artifacts.sh [github username]
$ ./scripts/ci/publish-build-artifacts.sh [GitHub username]
```
The script will publish the build snapshot to a branch with the same name as your current branch,
@ -148,3 +158,9 @@ and create it if it doesn't exist.
### VS Code
1. Install [Bazel](https://marketplace.visualstudio.com/items?itemName=DevonDCarew.bazel-code) extension for VS Code.
### WebStorm / IntelliJ
1. Install the [Bazel](https://plugins.jetbrains.com/plugin/8609-bazel) plugin
1. You can find the settings under `Preferences->Other Settings->Bazel Settings`
It will automatically recognize `*.bazel` and `*.bzl` files.

View File

@ -8,7 +8,6 @@
'use strict';
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
// This is to ensure that we catch env issues before we error while requiring other dependencies.
const engines = require('./package.json').engines;

View File

@ -0,0 +1 @@
/demo

View File

@ -37,6 +37,7 @@ function testBazel() {
ng build
ng test
ng e2e
ng e2e --prod
if [ -e 'WORKSPACE' ] || [ -e 'BUILD.bazel' ]; then
echo 'WORKSPACE / BUILD.bazel file should not exist in project'
exit 1

View File

@ -68,18 +68,18 @@
rxjs "6.3.3"
"@angular/bazel@file:../../dist/packages-dist/bazel":
version "8.0.0-beta.7"
version "8.0.0-beta.9"
dependencies:
"@angular-devkit/architect" "^0.13.4"
"@angular-devkit/core" "^7.0.4"
"@angular-devkit/schematics" "^7.3.0-rc.0"
"@bazel/typescript" "^0.26.0"
"@bazel/typescript" "^0.27.7"
"@microsoft/api-extractor" "^7.0.21"
"@schematics/angular" "^7.3.5"
"@types/node" "6.0.84"
semver "^5.6.0"
shelljs "0.8.2"
tsickle "0.34.0"
tsickle "0.34.3"
"@angular/cli@file:../../node_modules/@angular/cli":
version "7.3.2"
@ -120,12 +120,11 @@
"@bazel/bazel-linux_x64" "0.23.0"
"@bazel/bazel-win32_x64" "0.23.0"
"@bazel/typescript@^0.26.0":
version "0.26.0"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.26.0.tgz#c06139d76c8b9d3a3ed98a721b776fedb4b11c82"
integrity sha512-dh/Y/SZzmeChsLap8FVHYl0FuaeLh/6t9WBVhm5nOgyVrqfEyVpNzy4W20E4NqnmJY2/PqmD5qncf+Oo1q9h1A==
"@bazel/typescript@^0.27.7":
version "0.27.7"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.27.7.tgz#ff2d6469357ff68faccc75976d435fec0175e988"
integrity sha512-Eeu42RM6Ss/se3Bdv+OBfY159N9uOlUZp4XHgZixq1PHwuohRDPIEdEnci/pe3TDsiY25YOi7hjemM90gu5ppg==
dependencies:
jasmine-core "2.8.0"
protobufjs "5.0.3"
semver "5.6.0"
source-map-support "0.5.9"
@ -1279,11 +1278,6 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
jasmine-core@2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=
jju@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
@ -2364,10 +2358,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
tsickle@0.34.0:
version "0.34.0"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.0.tgz#10187fa6401a288a65efb93a60bf28b2ff95f90b"
integrity sha512-O3wCPRtL18Hc/ZBnaiKwmmjVzeCWTOTpsi0btfC7FWL3RnXpxLPxD6hoJ0QEXuSfG/0QJk+MWNjqT9N6fOyyIg==
tsickle@0.34.3:
version "0.34.3"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.3.tgz#8085067a26d7bff466ddadb2eba18849b49159b8"
integrity sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==
dependencies:
minimist "^1.2.0"
mkdirp "^0.5.1"

View File

@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch rules_nodejs so we can install our npm dependencies
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.4/rules_nodejs-0.27.4.tar.gz"],
sha256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.7/rules_nodejs-0.27.7.tar.gz"],
)
# Fetch sass rules for compiling sass files
@ -37,7 +37,7 @@ Try running `yarn bazel` instead.
# Setup the Node.js toolchain
node_repositories(
node_version = "10.9.0",
yarn_version = "1.13.0",
yarn_version = "1.12.1",
)
# Install our npm dependencies into @npm

View File

@ -8,7 +8,9 @@
"es2015"
],
"experimentalDecorators": true,
"types": []
"types": [],
"module": "umd",
"moduleResolution": "node"
},
"include": [
"node_modules/@angular/**/*"
@ -16,6 +18,7 @@
"exclude": [
"node_modules/@angular/bazel/**",
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**"
"node_modules/@angular/**/testing/**",
"node_modules/@angular/router/upgrade*"
]
}

View File

@ -16,6 +16,7 @@ ng_module(
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/platform-browser",
"@npm//@angular/router",
"@npm//@types",
"@npm//rxjs",
],
@ -39,6 +40,8 @@ ts_devserver(
"@npm//node_modules/@angular/common:bundles/common-http.umd.js",
"@npm//node_modules/@angular/core:bundles/core.umd.js",
"@npm//node_modules/@angular/platform-browser:bundles/platform-browser.umd.js",
"@npm//node_modules/@angular/router:bundles/router.umd.js",
"@npm//node_modules/@angular/router:router.ngfactory.js",
"@npm//node_modules/tslib:tslib.js",
":rxjs_umd_modules",
],
@ -54,9 +57,12 @@ rollup_bundle(
entry_point = "src/main",
deps = [
"//src",
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/platform-browser",
# TODO(kyliau): These are not necessary. Bundle compiles fine without
# these deps.
# "@npm//@angular/common",
# "@npm//@angular/core",
# "@npm//@angular/platform-browser",
"@npm//@angular/router", # needed here due to additional imports generated by ngc
"@npm//rxjs",
],
)

View File

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
// TODO(kyliau): Empty routes is enough for now to expose the bug in
// https://github.com/angular/angular/issues/29454.
// Consider adding non-lazy loaded routes.
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -1,5 +1,5 @@
import {Component} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Component} from '@angular/core';
import {Observable} from 'rxjs';
import {map, startWith} from 'rxjs/operators';
@ -8,12 +8,13 @@ import {map, startWith} from 'rxjs/operators';
template: `
<hello-world-app></hello-world-app>
<div>The current time is {{ time$ | async }}</div>
`})
<router-outlet></router-outlet>
`
})
export class AppComponent {
constructor(private http: HttpClient) {
}
constructor(private http: HttpClient) {}
time$ = this.http.get('http://worldclockapi.com/api/json/pst/now').pipe(
map((result: any) => result.currentDateTime),
startWith(['...']));
time$: Observable<string> =
this.http.get('http://worldclockapi.com/api/json/pst/now')
.pipe(map((result: any) => result.currentDateTime), startWith(['...']));
}

View File

@ -1,14 +1,22 @@
import {CommonModule} from '@angular/common';
import {HttpClientModule} from '@angular/common/http';
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpClientModule} from '@angular/common/http';
import {CommonModule} from '@angular/common';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {HelloWorldModule} from './hello-world/hello-world.module';
@NgModule({
imports: [CommonModule, BrowserModule, HttpClientModule, HelloWorldModule],
imports: [
AppRoutingModule,
CommonModule,
BrowserModule,
HttpClientModule,
HelloWorldModule,
],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
export class AppModule {
}

View File

@ -1,5 +1,5 @@
import {Component, NgModule} from '@angular/core';
import {Component} from '@angular/core';
@Component({
selector: 'hello-world-app',
@ -7,8 +7,7 @@ import {Component, NgModule} from '@angular/core';
<div>Hello {{ name }}!</div>
<input type="text" [value]="name" (input)="name = $event.target.value"/>
`,
// TODO: might be better to point to .scss so this looks valid at design-time
styleUrls: ['./hello-world.component.css'],
styleUrls: ['./hello-world.component.scss'],
})
export class HelloWorldComponent {
name: string = 'world';

View File

@ -9,6 +9,7 @@
"@angular/core": "packages-dist:core",
"@angular/platform-browser": "packages-dist:platform-browser",
"@angular/platform-browser-dynamic": "packages-dist:platform-browser-dynamic",
"@angular/router": "packages-dist:router",
"reflect-metadata": "0.1.12",
"rxjs": "6.4.0",
"tslib": "1.9.3",
@ -18,7 +19,7 @@
"@angular/bazel": "packages-dist:bazel",
"@angular/compiler": "packages-dist:compiler",
"@angular/compiler-cli": "packages-dist:compiler-cli",
"@bazel/karma": "0.27.4",
"@bazel/karma": "0.27.7",
"@types/jasmine": "2.8.8",
"@types/source-map": "0.5.1",
"protractor": "5.1.2",
@ -28,4 +29,4 @@
"postinstall": "ngc -p ./angular-metadata.tsconfig.json",
"//": "TODO(gregmagolan): figure out how to keep dependencies here up to date with the root package.json"
}
}
}

View File

@ -2,36 +2,14 @@
# yarn lockfile v1
"@angular-devkit/architect@^0.10.6":
version "0.10.7"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.10.7.tgz#c74b9f6b7f1b4261ada2d24c832328aa4c394464"
integrity sha512-S49LSslNRxIflHzrIrEgK7mGQ7HzETr/FU0fyTbB0vubcmfzMoYTsgYdK7SUz583lovc+UvASoUAhPJI3e35ng==
"@angular-devkit/architect@^0.13.4":
version "0.13.6"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.13.6.tgz#7758ab9f288cfd4f64d8b88b0700bac5c4961d06"
integrity sha512-Cg9z4lmCvjt5uD00E/0tBRz3ESjYicmqT3NL/BIsNVNb+s1GwCCoPSOIM8Ss4nyGDtrdono1XKSOmkJnlzF3Cw==
dependencies:
"@angular-devkit/core" "7.0.7"
"@angular-devkit/core" "7.3.6"
rxjs "6.3.3"
"@angular-devkit/core@7.0.7":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.0.7.tgz#665176ad8421adfd5f3ea0b2c4a9a432a158b1bb"
integrity sha512-M8tTT9r3nUtWI3YyiyynHIQn+lQQgeKkxVZ+rdxvyvgE3U9+wn0yep5HkFLQETTuJetu9ARRRD94sD2XL3F/3A==
dependencies:
ajv "6.5.3"
chokidar "2.0.4"
fast-json-stable-stringify "2.0.0"
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/core@7.1.2", "@angular-devkit/core@^7.0.4":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.1.2.tgz#86b0e5a4cdeaa3198f6b1b50e7e114fac403e57c"
integrity sha512-LyjHGuLnLWrgX7SYDkKmc3eW4H5uuaoC+CXYjRfgx3qundrLfvTCRgNGC6FPjhQNnVXH9qar+j9P1aMmKFb4Lw==
dependencies:
ajv "6.5.3"
chokidar "2.0.4"
fast-json-stable-stringify "2.0.0"
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/core@7.3.3":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.3.tgz#cd6d5a8eca25ef07b6394bc2b08133d90d08d39f"
@ -43,12 +21,34 @@
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/schematics@7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.1.2.tgz#847639044417d044bf1bc87f64508a0c3f99fae2"
integrity sha512-NFhHLYWf9gpGQm0s19lq+nAw3CZ0udBpoBLzCm8Crlmu6+7aAXgw7Fv5P4ukWJ/e1m7NDGVids+B6kBGXaY6Ig==
"@angular-devkit/core@7.3.6":
version "7.3.6"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.6.tgz#bc11ca571187f0f0ce9df012332794907e0b8133"
integrity sha512-aoarMK0DJIdwjVA0OuQIN7b8nKPcF9n5vSMF7MFmhKpTw5/uV3SynQZbm3YCgylu/2CMuiTzKuAunnWWdli//g==
dependencies:
"@angular-devkit/core" "7.1.2"
ajv "6.9.1"
chokidar "2.0.4"
fast-json-stable-stringify "2.0.0"
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/core@^7.0.4":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.1.2.tgz#86b0e5a4cdeaa3198f6b1b50e7e114fac403e57c"
integrity sha512-LyjHGuLnLWrgX7SYDkKmc3eW4H5uuaoC+CXYjRfgx3qundrLfvTCRgNGC6FPjhQNnVXH9qar+j9P1aMmKFb4Lw==
dependencies:
ajv "6.5.3"
chokidar "2.0.4"
fast-json-stable-stringify "2.0.0"
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/schematics@7.3.6":
version "7.3.6"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.3.6.tgz#5580b730b1bae5397f5860cfb0d7270fec6e6deb"
integrity sha512-YXF7QusmMy3D9H0vNczc1n5BkuEHLwt7cW33euNeGNgTIsD0n6DrUhgClurXicnr2GNPSDYE5+3115lmJkhyrg==
dependencies:
"@angular-devkit/core" "7.3.6"
rxjs "6.3.3"
"@angular-devkit/schematics@^7.3.0-rc.0":
@ -59,32 +59,32 @@
"@angular-devkit/core" "7.3.3"
rxjs "6.3.3"
"@angular/animations@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/animations":
version "8.0.0-beta.6"
"@angular/animations@file:../../../dist/packages-dist/animations":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@angular/bazel@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/bazel":
version "8.0.0-beta.6"
"@angular/bazel@file:../../../dist/packages-dist/bazel":
version "8.0.0-beta.9"
dependencies:
"@angular-devkit/architect" "^0.10.6"
"@angular-devkit/architect" "^0.13.4"
"@angular-devkit/core" "^7.0.4"
"@angular-devkit/schematics" "^7.3.0-rc.0"
"@bazel/typescript" "^0.26.0"
"@microsoft/api-extractor" "^7.0.17"
"@schematics/angular" "^7.0.4"
"@bazel/typescript" "^0.27.7"
"@microsoft/api-extractor" "^7.0.21"
"@schematics/angular" "^7.3.5"
"@types/node" "6.0.84"
semver "^5.6.0"
shelljs "0.8.2"
tsickle "0.34.0"
tsickle "0.34.3"
"@angular/common@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/common":
version "8.0.0-beta.6"
"@angular/common@file:../../../dist/packages-dist/common":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/compiler-cli":
version "8.0.0-beta.6"
"@angular/compiler-cli@file:../../../dist/packages-dist/compiler-cli":
version "8.0.0-beta.9"
dependencies:
canonical-path "1.0.0"
chokidar "^2.1.1"
@ -98,30 +98,35 @@
tslib "^1.9.0"
yargs "9.0.1"
"@angular/compiler@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/compiler":
version "8.0.0-beta.6"
"@angular/compiler@file:../../../dist/packages-dist/compiler":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@angular/core@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/core":
version "8.0.0-beta.6"
"@angular/core@file:../../../dist/packages-dist/core":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/platform-browser-dynamic":
version "8.0.0-beta.6"
"@angular/platform-browser-dynamic@file:../../../dist/packages-dist/platform-browser-dynamic":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@file:../../../../../../../Users/greg/google/gregmagolan/angular-2/dist/packages-dist/platform-browser":
version "8.0.0-beta.6"
"@angular/platform-browser@file:../../../dist/packages-dist/platform-browser":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@bazel/karma@0.26.0":
version "0.26.0"
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-0.26.0.tgz#6ad796686f5775df33a96fa5ef3df76a66aed3b2"
integrity sha512-yZv0fgAjVrfrM0ld8e+wNaPIpYCpwBzVQi2GUErsKStUZXPUUofpBwBjlYsdY1Osn5/FmAF9e6xDkk1JWn/wSg==
"@angular/router@file:../../../dist/packages-dist/router":
version "8.0.0-beta.9"
dependencies:
tslib "^1.9.0"
"@bazel/karma@0.27.7":
version "0.27.7"
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-0.27.7.tgz#abe1cf48be97fbad84ad38e9d32b823e367b8d81"
integrity sha512-SlYtePQWoa1+xKMEZdp27n4WZsocfUaUR10p3jaZZ0uhOcoAvuOVTaG+sidUJCoVuF8s6IPjbSOGN4FWVgXUjg==
dependencies:
jasmine-core "2.8.0"
karma "^4.0.0"
@ -135,37 +140,44 @@
semver "5.6.0"
tmp "0.0.33"
"@bazel/typescript@^0.26.0":
version "0.26.0"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.26.0.tgz#c06139d76c8b9d3a3ed98a721b776fedb4b11c82"
integrity sha512-dh/Y/SZzmeChsLap8FVHYl0FuaeLh/6t9WBVhm5nOgyVrqfEyVpNzy4W20E4NqnmJY2/PqmD5qncf+Oo1q9h1A==
"@bazel/typescript@^0.27.7":
version "0.27.7"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.27.7.tgz#ff2d6469357ff68faccc75976d435fec0175e988"
integrity sha512-Eeu42RM6Ss/se3Bdv+OBfY159N9uOlUZp4XHgZixq1PHwuohRDPIEdEnci/pe3TDsiY25YOi7hjemM90gu5ppg==
dependencies:
jasmine-core "2.8.0"
protobufjs "5.0.3"
semver "5.6.0"
source-map-support "0.5.9"
tsutils "2.27.2"
"@microsoft/api-extractor@^7.0.17":
version "7.0.18"
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.0.18.tgz#4eb931436495177dfcef8f2d8da3d084d10eebb6"
integrity sha512-puQisjyoYK1A0I8DqyBoLPV9noyFUlxTE3WsjhgJw//TrmegGHYmsRlD3rnHeXcKPM1F7sd/VKJXeXC3IPTf2Q==
"@microsoft/api-extractor-model@7.0.28":
version "7.0.28"
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.0.28.tgz#691b077415143a9015ed1656bca6d0b9a77f58cb"
integrity sha512-kZJaWwdu3z5A1DugJpOZ9dI5+DjIEhqQJwHn2/kLTpsKT7gOyqNRbGHlDGG8xSiJ6/m994+cwh3qSGYDC17dtw==
dependencies:
"@microsoft/node-core-library" "3.10.0"
"@microsoft/ts-command-line" "4.2.3"
"@microsoft/tsdoc" "0.12.5"
"@microsoft/node-core-library" "3.13.0"
"@microsoft/tsdoc" "0.12.8"
"@types/node" "8.5.8"
"@types/z-schema" "3.16.31"
"@microsoft/api-extractor@^7.0.21":
version "7.0.31"
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.0.31.tgz#4d72755bf234e1c3a0afb22f678b7106158a9b89"
integrity sha512-7oiMvxaR9/qOO4SdlG2JFhdCZ1B+JtaX4pDb/aDGsm+7n+PMeLSVgQKeZwVXUIgslHlXxkxbX7zXIz41VqL8sQ==
dependencies:
"@microsoft/api-extractor-model" "7.0.28"
"@microsoft/node-core-library" "3.13.0"
"@microsoft/ts-command-line" "4.2.3"
"@microsoft/tsdoc" "0.12.8"
colors "~1.2.1"
lodash "~4.17.5"
resolve "1.8.1"
source-map "~0.6.1"
typescript "~3.1.6"
z-schema "~3.18.3"
"@microsoft/node-core-library@3.10.0":
version "3.10.0"
resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.10.0.tgz#70e089534d8e20f6a0f9c7a4a12a6aeafd6a1ddb"
integrity sha512-1SbU+XNYAabhV9noGXHtsUVPc5ELV+oEuJQtZQoCncbOd6WAMeTgB1xFwh96hmdEXyKQyML/pnByiKocmh/nbQ==
"@microsoft/node-core-library@3.13.0":
version "3.13.0"
resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.13.0.tgz#ba24e16182149dc817bf52a886d22aced5cd8070"
integrity sha512-mnsL/1ikVWHl8sPNssavaAgtUaIM3hkQ8zeySuApU5dNmsMPzovJPfx9m5JGiMvs1v5QNAIVeiS9jnWwe/7anw==
dependencies:
"@types/fs-extra" "5.0.4"
"@types/jju" "~1.4.0"
@ -186,19 +198,19 @@
argparse "~1.0.9"
colors "~1.2.1"
"@microsoft/tsdoc@0.12.5":
version "0.12.5"
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.5.tgz#c448a38902ccb5601c1b2ef3b1a105012ef7712c"
integrity sha512-xEAyvLXo4Cter/b0EMCWUZTgXOfLOPJ/Xr52WdjVclPx9eDmNTGFtZl8Pn/nqSnZsQBNcHL0eHk/YyRyyXXpiQ==
"@microsoft/tsdoc@0.12.8":
version "0.12.8"
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.8.tgz#e9646c91c650d536f37b04762eaa81afdc9a19d3"
integrity sha512-0smzAmVIUCsssAqDSPn9AfOPKUobq2WXMygbzC5JNswAJOs4uJK6DTZgfnHC8QLE2q374sPNwWU5D5LuoAJQSA==
"@schematics/angular@^7.0.4":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.1.2.tgz#b3eefbc81d12b0b53816896f6172eb613885826c"
integrity sha512-coypNxjRjCExCbkJ8Vser4iZbdksl3cNqgdokDlEtpXnnph3ZYvNDhDD9TBWYQ+cwDhCHAOzT3U3IjN4R2MCgQ==
"@schematics/angular@^7.3.5":
version "7.3.6"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.3.6.tgz#e827111e7d3cdf950efcca504389cbade3c9c59a"
integrity sha512-Q4VXAjVaCDb2zXFXoIdOfNPsn+EQjqDBHK4a97omytnSNAmu1erl3l2FkEMi6x/VuzK2mQSzBbmHJIgauMmOAA==
dependencies:
"@angular-devkit/core" "7.1.2"
"@angular-devkit/schematics" "7.1.2"
typescript "3.1.6"
"@angular-devkit/core" "7.3.6"
"@angular-devkit/schematics" "7.3.6"
typescript "3.2.4"
"@types/argparse@1.0.33":
version "1.0.33"
@ -3112,7 +3124,7 @@ source-map@^0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.0, source-map@^0.6.1:
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -3337,10 +3349,10 @@ tough-cookie@~2.4.3:
psl "^1.1.24"
punycode "^1.4.1"
tsickle@0.34.0:
version "0.34.0"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.0.tgz#10187fa6401a288a65efb93a60bf28b2ff95f90b"
integrity sha512-O3wCPRtL18Hc/ZBnaiKwmmjVzeCWTOTpsi0btfC7FWL3RnXpxLPxD6hoJ0QEXuSfG/0QJk+MWNjqT9N6fOyyIg==
tsickle@0.34.3:
version "0.34.3"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.34.3.tgz#8085067a26d7bff466ddadb2eba18849b49159b8"
integrity sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==
dependencies:
minimist "^1.2.0"
mkdirp "^0.5.1"
@ -3378,12 +3390,17 @@ type-is@~1.6.16:
media-typer "0.3.0"
mime-types "~2.1.18"
typescript@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96"
integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ==
typescript@3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==
typescript@3.1.6, typescript@~3.1.6:
typescript@3.3.3333:
version "3.3.3333"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
typescript@~3.1.6:
version "3.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==

View File

@ -45,5 +45,8 @@ protractor_web_test_suite(
],
on_prepare = ":ts_on_prepare",
server = "//src:prodserver",
# TODO(kyliau): Re-enable once we figure out why rollup complains about
# "Dynamic requires are not currently supported by rollup-plugin-commonjs"
tags = ["manual"],
deps = [":e2e"],
)

View File

@ -10,11 +10,25 @@ ivy-ngcc --help
ivy-ngcc
# Did it add the appropriate build markers?
# - fesm2015
ls node_modules/@angular/common | grep __modified_by_ngcc_for_fesm2015
if [[ $? != 0 ]]; then exit 1; fi
# - esm2015
ls node_modules/@angular/common | grep __modified_by_ngcc_for_esm2015
grep '"__processed_by_ivy_ngcc__":[^}]*"esm2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
# - fesm2015
grep '"__processed_by_ivy_ngcc__":[^}]*"fesm2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
grep '"__processed_by_ivy_ngcc__":[^}]*"es2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
# - esm5
grep '"__processed_by_ivy_ngcc__":[^}]*"esm5":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
# - fesm5
grep '"__processed_by_ivy_ngcc__":[^}]*"module":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
grep '"__processed_by_ivy_ngcc__":[^}]*"fesm5":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi
# Did it replace the PRE_R3 markers correctly?
@ -48,6 +62,9 @@ ivy-ngcc
# Can it be safely run again (as a noop)?
ivy-ngcc
# Does running it with --formats fail?
ivy-ngcc --formats fesm2015 && exit 1
# Now try compiling the app using the ngcc compiled libraries
ngc -p tsconfig-app.json

View File

@ -37,8 +37,8 @@
"@angular-devkit/schematics": "^7.3.2",
"@angular/bazel": "file:./tools/npm/@angular_bazel",
"@bazel/jasmine": "0.26.0",
"@bazel/karma": "0.27.4",
"@bazel/typescript": "0.27.4",
"@bazel/karma": "0.27.7",
"@bazel/typescript": "0.27.7",
"@microsoft/api-extractor": "^7.0.21",
"@schematics/angular": "^7.3.5",
"@types/angular": "^1.6.47",
@ -110,7 +110,7 @@
"// 3": "when updating @bazel/bazel version you also need to update the RBE settings in .bazelrc (see https://github.com/angular/angular/pull/27935)",
"devDependencies": {
"@angular/cli": "^7.3.2",
"@bazel/bazel": "0.23.0",
"@bazel/bazel": "0.23.2",
"@bazel/buildifier": "^0.19.2",
"@bazel/ibazel": "~0.9.0",
"@types/minimist": "^1.2.0",

View File

@ -22,7 +22,7 @@
"@angular-devkit/architect": "^0.13.4",
"@angular-devkit/core": "^7.0.4",
"@angular-devkit/schematics": "^7.3.0-rc.0",
"@bazel/typescript": "^0.27.4",
"@bazel/typescript": "^0.27.7",
"@microsoft/api-extractor": "^7.0.21",
"@schematics/angular": "^7.3.5",
"@types/node": "6.0.84",

View File

@ -12,18 +12,20 @@ workspace(name = "project")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_NODEJS_VERSION = "0.27.4"
RULES_NODEJS_VERSION = "0.27.7"
RULES_NODEJS_SHA256 = "fb87ed5965cef93188af9a7287511639403f4b0da418961ce6defb9dcf658f51"
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "251a023b6c5c5c97db1bfe24652dc19dad05f4da68f8e1821d92d911fa3f4ef4",
sha256 = RULES_NODEJS_SHA256,
url = "https://github.com/bazelbuild/rules_nodejs/releases/download/%s/rules_nodejs-%s.tar.gz" % (RULES_NODEJS_VERSION, RULES_NODEJS_VERSION),
)
# Rules for compiling sass
RULES_SASS_VERSION = "1.17.2"
RULES_SASS_VERSION = "1.17.3"
RULES_SASS_SHA256 = "ea79647e5cd36867568d80811a951c7b3170791058f50a5cbd3d542627e78881"
http_archive(
name = "io_bazel_rules_sass",
sha256 = "e5316ee8a09d1cbb732d3938b400836bf94dba91a27476e9e27706c4c0edae1f",
sha256 = RULES_SASS_SHA256,
url = "https://github.com/bazelbuild/rules_sass/archive/%s.zip" % RULES_SASS_VERSION,
strip_prefix = "rules_sass-%s" % RULES_SASS_VERSION,
)

View File

@ -105,6 +105,10 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
angularCompilerOptions.preserveWhitespaces = angularCompilerOptions.preserveWhitespaces ||
userConfig.angularCompilerOptions.preserveWhitespaces;
angularCompilerOptions.createExternalSymbolFactoryReexports =
angularCompilerOptions.createExternalSymbolFactoryReexports ||
userConfig.angularCompilerOptions.createExternalSymbolFactoryReexports;
}
}
@ -307,6 +311,31 @@ export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost,
// File does not exist or parse error. Ignore this case and continue onto the
// other methods of resolving the module below.
}
// It can happen that the ViewEngine compiler needs to write an import in a factory file,
// and is using an ngsummary file to get the symbols.
// The ngsummary comes from an upstream ng_module rule.
// The upstream rule based its imports on ngsummary file which was generated from a
// metadata.json file that was published to npm in an Angular library.
// However, the ngsummary doesn't propagate the 'importAs' from the original metadata.json
// so we would normally not be able to supply the correct module name for it.
// For example, if the rootDir-relative filePath is
// node_modules/@angular/material/toolbar/typings/index
// we would supply a module name
// @angular/material/toolbar/typings/index
// but there is no JavaScript file to load at this path.
// This is a workaround for https://github.com/angular/angular/issues/29454
if (importedFilePath.indexOf('node_modules') >= 0) {
const maybeMetadataFile = importedFilePath.replace(EXT, '') + '.metadata.json';
if (fs.existsSync(maybeMetadataFile)) {
const moduleName =
JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})).importAs;
if (moduleName) {
return moduleName;
}
}
}
if ((compilerOpts.module === ts.ModuleKind.UMD || compilerOpts.module === ts.ModuleKind.AMD) &&
ngHost.amdModuleName) {
return ngHost.amdModuleName({ fileName: importedFilePath } as ts.SourceFile);

View File

@ -49,7 +49,7 @@ function addDevDependenciesToPackageJson(options: Schema) {
'@angular/bazel': angularCoreVersion,
'@bazel/bazel': '^0.23.0',
'@bazel/ibazel': '^0.9.0',
'@bazel/karma': '^0.27.4',
'@bazel/karma': '^0.27.7',
};
const recorder = host.beginUpdate(packageJson);

View File

@ -34,9 +34,9 @@ ng_package(
visibility = [
"//packages/bazel/test/ng_package:__pkg__",
"//packages/compiler-cli/integrationtest:__pkg__",
"//packages/compiler-cli/ngcc/test:__pkg__",
"//packages/compiler-cli/test:__pkg__",
"//packages/compiler-cli/test/diagnostics:__pkg__",
"//packages/compiler-cli/test/ngcc:__pkg__",
"//packages/compiler-cli/test/transformers:__pkg__",
"//packages/compiler/test:__pkg__",
"//packages/language-service/test:__pkg__",

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Attribute, Component, Directive} from '@angular/core';
import {Attribute, Component, Directive, TemplateRef, ViewChild} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -26,7 +26,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
declarations: [TestComponent, ComplexComponent],
imports: [CommonModule],
});
});
@ -171,6 +171,20 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
getComponent().switchValue = 'b';
detectChangesAndExpectText('when b1;when b2;');
});
it('should support nested NgSwitch on ng-container with ngTemplateOutlet', () => {
fixture = TestBed.createComponent(ComplexComponent);
detectChangesAndExpectText('Foo');
fixture.componentInstance.state = 'case2';
detectChangesAndExpectText('Bar');
fixture.componentInstance.state = 'notACase';
detectChangesAndExpectText('Default');
fixture.componentInstance.state = 'case1';
detectChangesAndExpectText('Foo');
});
});
});
}
@ -182,6 +196,38 @@ class TestComponent {
when2: any = null;
}
@Component({
selector: 'complex-cmp',
template: `
<div [ngSwitch]="state">
<ng-container *ngSwitchCase="'case1'" [ngSwitch]="true">
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="foo"></ng-container>
<span *ngSwitchDefault>Should never render</span>
</ng-container>
<ng-container *ngSwitchCase="'case2'" [ngSwitch]="true">
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="bar"></ng-container>
<span *ngSwitchDefault>Should never render</span>
</ng-container>
<ng-container *ngSwitchDefault [ngSwitch]="false">
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="foo"></ng-container>
<span *ngSwitchDefault>Default</span>
</ng-container>
</div>
<ng-template #foo>
<span>Foo</span>
</ng-template>
<ng-template #bar>
<span>Bar</span>
</ng-template>
`
})
class ComplexComponent {
@ViewChild('foo') foo !: TemplateRef<any>;
@ViewChild('bar') bar !: TemplateRef<any>;
state: string = 'case1';
}
function createTestComponent(template: string): ComponentFixture<TestComponent> {
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
.createComponent(TestComponent);

View File

@ -60,6 +60,6 @@ npm_package(
],
deps = [
":compiler-cli",
"//packages/compiler-cli/src/ngcc",
"//packages/compiler-cli/ngcc",
],
)

View File

@ -10,7 +10,7 @@ This conversion will allow such "legacy" packages to be used by the Ivy renderin
The project is built using Bazel:
```bash
yarn bazel build //packages/compiler-cli/src/ngcc
yarn bazel build //packages/compiler-cli/ngcc
```
## Unit Testing
@ -18,7 +18,7 @@ yarn bazel build //packages/compiler-cli/src/ngcc
The unit tests are built and run using Bazel:
```bash
yarn bazel test //packages/compiler-cli/src/ngcc/test
yarn bazel test //packages/compiler-cli/ngcc/test
```
## Integration Testing
@ -26,5 +26,5 @@ yarn bazel test //packages/compiler-cli/src/ngcc/test
There are tests that check the behavior of the overall executable:
```bash
yarn bazel test //packages/compiler-cli/test/ngcc
yarn bazel test //packages/compiler-cli/ngcc/test:integration
```

View File

@ -0,0 +1,17 @@
/**
* @license
* Copyright Google Inc. 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 {hasBeenProcessed as _hasBeenProcessed} from './src/packages/build_marker';
import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entry_point';
export {NgccOptions, mainNgcc as process} from './src/main';
export function hasBeenProcessed(packageJson: object, format: string) {
// We are wrapping this function to hide the internal types.
return _hasBeenProcessed(packageJson as EntryPointPackageJson, format as EntryPointJsonProperty);
}

View File

@ -0,0 +1,65 @@
#!/usr/bin/env node
/**
* @license
* Copyright Google Inc. 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 path from 'canonical-path';
import * as yargs from 'yargs';
import {mainNgcc} from './src/main';
// CLI entry point
if (require.main === module) {
const args = process.argv.slice(2);
const options =
yargs
.option('s', {
alias: 'source',
describe:
'A path (relative to the working directory) of the `node_modules` folder to process.',
default: './node_modules'
})
.option('f', {alias: 'formats', hidden: true, array: true})
.option('p', {
alias: 'properties',
array: true,
describe:
'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' +
'Each of these properties should hold the path to a bundle-format.\n' +
'If provided, only the specified properties are considered for processing.\n' +
'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.'
})
.option('t', {
alias: 'target',
describe:
'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).',
})
.option('first-only', {
describe:
'If specified then only the first matching package.json property will be compiled',
type: 'boolean'
})
.help()
.parse(args);
if (options['f'] && options['f'].length) {
console.error(
'The formats option (-f/--formats) has been removed. Consider the properties option (-p/--properties) instead.');
process.exit(1);
}
const baseSourcePath = path.resolve(options['s'] || './node_modules');
const propertiesToConsider: string[] = options['p'];
const targetEntryPointPath = options['t'] ? options['t'] : undefined;
const compileAllFormats = !options['first-only'];
try {
mainNgcc(
{basePath: baseSourcePath, propertiesToConsider, targetEntryPointPath, compileAllFormats});
process.exitCode = 0;
} catch (e) {
console.error(e.stack || e.message);
process.exitCode = 1;
}
}

View File

@ -10,13 +10,13 @@ import * as path from 'canonical-path';
import * as fs from 'fs';
import * as ts from 'typescript';
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../ngtsc/annotations';
import {CycleAnalyzer, ImportGraph} from '../../../ngtsc/cycles';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../ngtsc/imports';
import {PartialEvaluator} from '../../../ngtsc/partial_evaluator';
import {AbsoluteFsPath, LogicalFileSystem} from '../../../ngtsc/path';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../ngtsc/scope';
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../ngtsc/transform';
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {AbsoluteFsPath, LogicalFileSystem} from '../../../src/ngtsc/path';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
import {DecoratedClass} from '../host/decorated_class';
import {NgccReflectionHost} from '../host/ngcc_host';
import {isDefined} from '../utils';

View File

@ -7,10 +7,10 @@
*/
import * as ts from 'typescript';
import {ReferencesRegistry} from '../../../ngtsc/annotations';
import {Reference} from '../../../ngtsc/imports';
import {Declaration} from '../../../ngtsc/reflection';
import {NgccReflectionHost} from '../host/ngcc_host';
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
import {Reference} from '../../../src/ngtsc/imports';
import {Declaration} from '../../../src/ngtsc/reflection';
import {ModuleWithProvidersFunction, NgccReflectionHost} from '../host/ngcc_host';
import {isDefined} from '../utils';
export interface ModuleWithProvidersInfo {
@ -38,7 +38,7 @@ export class ModuleWithProvidersAnalyzer {
rootFiles.forEach(f => {
const fns = this.host.getModuleWithProvidersFunctions(f);
fns && fns.forEach(fn => {
const dtsFn = this.getDtsDeclaration(fn.declaration);
const dtsFn = this.getDtsDeclarationForFunction(fn);
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
null;
@ -82,28 +82,27 @@ export class ModuleWithProvidersAnalyzer {
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
}
private getDtsDeclaration(fn: ts.SignatureDeclaration) {
private getDtsDeclarationForFunction(fn: ModuleWithProvidersFunction) {
let dtsFn: ts.Declaration|null = null;
const containerClass = this.host.getClassSymbol(fn.parent);
const fnName = fn.name && ts.isIdentifier(fn.name) && fn.name.text;
if (containerClass && fnName) {
const containerClass = fn.container && this.host.getClassSymbol(fn.container);
if (containerClass) {
const dtsClass = this.host.getDtsDeclaration(containerClass.valueDeclaration);
// Get the declaration of the matching static method
dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ?
dtsClass.members
.find(
member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) &&
member.name.text === fnName) as ts.Declaration :
member.name.text === fn.name) as ts.Declaration :
null;
} else {
dtsFn = this.host.getDtsDeclaration(fn);
dtsFn = this.host.getDtsDeclaration(fn.declaration);
}
if (!dtsFn) {
throw new Error(`Matching type declaration for ${fn.getText()} is missing`);
throw new Error(`Matching type declaration for ${fn.declaration.getText()} is missing`);
}
if (!isFunctionOrMethod(dtsFn)) {
throw new Error(
`Matching type declaration for ${fn.getText()} is not a function: ${dtsFn.getText()}`);
`Matching type declaration for ${fn.declaration.getText()} is not a function: ${dtsFn.getText()}`);
}
return dtsFn;
}

View File

@ -7,9 +7,9 @@
*/
import * as ts from 'typescript';
import {ReferencesRegistry} from '../../../ngtsc/annotations';
import {Reference} from '../../../ngtsc/imports';
import {Declaration, ReflectionHost} from '../../../ngtsc/reflection';
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
import {Reference} from '../../../src/ngtsc/imports';
import {Declaration, ReflectionHost} from '../../../src/ngtsc/reflection';
import {hasNameIdentifier} from '../utils';
/**

View File

@ -7,7 +7,7 @@
*/
import * as ts from 'typescript';
import {Declaration} from '../../../ngtsc/reflection';
import {Declaration} from '../../../src/ngtsc/reflection';
import {NgccReflectionHost} from '../host/ngcc_host';
import {hasNameIdentifier, isDefined} from '../utils';
import {NgccReferencesRegistry} from './ngcc_references_registry';

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {Decorator} from '../../../ngtsc/reflection';
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
/**
* A simple container that holds the details of a decorated class that has been
@ -18,9 +17,12 @@ export class DecoratedClass {
* Initialize a `DecoratedClass` that was found in a `DecoratedFile`.
* @param name The name of the class that has been found. This is mostly used
* for informational purposes.
* @param declaration The TypeScript AST node where this class is declared
* @param declaration The TypeScript AST node where this class is declared. In ES5 code, where a
* class can be represented by both a variable declaration and a function declaration (inside an
* IIFE), `declaration` will always refer to the outer variable declaration, which represents the
* class to the rest of the program.
* @param decorators The collection of decorators that have been found on this class.
*/
constructor(
public name: string, public declaration: ts.Declaration, public decorators: Decorator[], ) {}
public name: string, public declaration: ClassDeclaration, public decorators: Decorator[]) {}
}

View File

@ -8,9 +8,9 @@
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, CtorParameter, Decorator, Import, TypeScriptReflectionHost, reflectObjectLiteral} from '../../../ngtsc/reflection';
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Decorator, Import, TypeScriptReflectionHost, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
import {BundleProgram} from '../packages/bundle_program';
import {findAll, getNameText, isDefined} from '../utils';
import {findAll, getNameText, hasNameIdentifier, isDefined} from '../utils';
import {DecoratedClass} from './decorated_class';
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
@ -54,6 +54,37 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
this.dtsDeclarationMap = dts && this.computeDtsDeclarationMap(dts.path, dts.program) || null;
}
/**
* Find the declaration of a node that we think is a class.
* Classes should have a `name` identifier, because they may need to be referenced in other parts
* of the program.
*
* @param node the node that represents the class whose declaration we are finding.
* @returns the declaration of the class or `undefined` if it is not a "class".
*/
getClassDeclaration(node: ts.Node): ClassDeclaration|undefined {
if (ts.isVariableDeclaration(node) && node.initializer) {
node = node.initializer;
}
if (!ts.isClassDeclaration(node) && !ts.isClassExpression(node)) {
return undefined;
}
return hasNameIdentifier(node) ? node : undefined;
}
/**
* Find a symbol for a node that we think is a class.
* @param node the node whose symbol we are finding.
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
*/
getClassSymbol(declaration: ts.Node): ClassSymbol|undefined {
const classDeclaration = this.getClassDeclaration(declaration);
return classDeclaration &&
this.checker.getSymbolAtLocation(classDeclaration.name) as ClassSymbol;
}
/**
* Examine a declaration (for example, of a class or function) and return metadata about any
* decorators present on the declaration.
@ -79,89 +110,19 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* Examine a declaration which should be of a class, and return metadata about the members of the
* class.
*
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
* reflect. If the source is in ES6 format, this will be a `ts.ClassDeclaration` node. If the
* source is in ES5 format, this might be a `ts.VariableDeclaration` as classes in ES5 are
* represented as the result of an IIFE execution.
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
*
* @returns an array of `ClassMember` metadata representing the members of the class.
*
* @throws if `declaration` does not resolve to a class declaration.
*/
getMembersOfClass(clazz: ts.Declaration): ClassMember[] {
const members: ClassMember[] = [];
const symbol = this.getClassSymbol(clazz);
if (!symbol) {
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
const classSymbol = this.getClassSymbol(clazz);
if (!classSymbol) {
throw new Error(`Attempted to get members of a non-class: "${clazz.getText()}"`);
}
// The decorators map contains all the properties that are decorated
const decoratorsMap = this.getMemberDecorators(symbol);
// The member map contains all the method (instance and static); and any instance properties
// that are initialized in the class.
if (symbol.members) {
symbol.members.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
// The static property map contains all the static properties
if (symbol.exports) {
symbol.exports.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators, true);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
// If this class was declared as a VariableDeclaration then it may have static properties
// attached to the variable rather than the class itself
// For example:
// ```
// let MyClass = class MyClass {
// // no static properties here!
// }
// MyClass.staticProperty = ...;
// ```
if (ts.isVariableDeclaration(symbol.valueDeclaration.parent)) {
const variableSymbol = this.checker.getSymbolAtLocation(symbol.valueDeclaration.parent.name);
if (variableSymbol && variableSymbol.exports) {
variableSymbol.exports.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators, true);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
}
// Deal with any decorated properties that were not initialized in the class
decoratorsMap.forEach((value, key) => {
members.push({
implementation: null,
decorators: value,
isStatic: false,
kind: ClassMemberKind.Property,
name: key,
nameNode: null,
node: null,
type: null,
value: null
});
});
return members;
return this.getMembersOfSymbol(classSymbol);
}
/**
@ -170,10 +131,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* This method only looks at the constructor of a class directly and not at any inherited
* constructors.
*
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
* reflect. If the source is in ES6 format, this will be a `ts.ClassDeclaration` node. If the
* source is in ES5 format, this might be a `ts.VariableDeclaration` as classes in ES5 are
* represented as the result of an IIFE execution.
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
*
* @returns an array of `Parameter` metadata representing the parameters of the constructor, if
* a constructor exists. If the constructor exists and has 0 parameters, this array will be empty.
@ -181,7 +139,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
*
* @throws if `declaration` does not resolve to a class declaration.
*/
getConstructorParameters(clazz: ts.Declaration): CtorParameter[]|null {
getConstructorParameters(clazz: ClassDeclaration): CtorParameter[]|null {
const classSymbol = this.getClassSymbol(clazz);
if (!classSymbol) {
throw new Error(
@ -194,24 +152,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
return null;
}
/**
* Find a symbol for a node that we think is a class.
* @param node the node whose symbol we are finding.
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
*/
getClassSymbol(declaration: ts.Node): ts.Symbol|undefined {
if (ts.isClassDeclaration(declaration)) {
return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
}
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
declaration = declaration.initializer;
}
if (ts.isClassExpression(declaration)) {
return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
}
return undefined;
}
/**
* Search the given module for variable declarations in which the initializer
* is an identifier marked with the `PRE_R3_MARKER`.
@ -325,10 +265,12 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
/**
* Get the number of generic type parameters of a given class.
*
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
*
* @returns the number of type parameters of the class, if known, or `null` if the declaration
* is not a class or has an unknown number of type parameters.
*/
getGenericArityOfClass(clazz: ts.Declaration): number|null {
getGenericArityOfClass(clazz: ClassDeclaration): number|null {
const dtsDeclaration = this.getDtsDeclaration(clazz);
if (dtsDeclaration && ts.isClassDeclaration(dtsDeclaration)) {
return dtsDeclaration.typeParameters ? dtsDeclaration.typeParameters.length : 0;
@ -374,16 +316,20 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (this.isClass(declaration.node)) {
this.getMembersOfClass(declaration.node).forEach(member => {
if (member.isStatic) {
const info = this.parseForModuleWithProviders(member.node);
const info = this.parseForModuleWithProviders(
member.name, member.node, member.implementation, declaration.node);
if (info) {
infos.push(info);
}
}
});
} else {
const info = this.parseForModuleWithProviders(declaration.node);
if (info) {
infos.push(info);
if (isNamedDeclaration(declaration.node)) {
const info =
this.parseForModuleWithProviders(declaration.node.name.text, declaration.node);
if (info) {
infos.push(info);
}
}
}
});
@ -392,7 +338,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
///////////// Protected Helpers /////////////
protected getDecoratorsOfSymbol(symbol: ts.Symbol): Decorator[]|null {
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
if (decoratorsProperty) {
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
@ -401,7 +347,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
}
protected getDecoratedClassFromSymbol(symbol: ts.Symbol|undefined): DecoratedClass|null {
protected getDecoratedClassFromSymbol(symbol: ClassSymbol|undefined): DecoratedClass|null {
if (symbol) {
const decorators = this.getDecoratorsOfSymbol(symbol);
if (decorators && decorators.length) {
@ -439,7 +385,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @param propertyName the name of static property.
* @returns the symbol if it is found or `undefined` if not.
*/
protected getStaticProperty(symbol: ts.Symbol, propertyName: ts.__String): ts.Symbol|undefined {
protected getStaticProperty(symbol: ClassSymbol, propertyName: ts.__String): ts.Symbol|undefined {
return symbol.exports && symbol.exports.get(propertyName);
}
@ -485,7 +431,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @param symbol the class whose decorators we want to get.
* @returns an array of decorators or null if none where found.
*/
protected getClassDecoratorsFromHelperCall(symbol: ts.Symbol): Decorator[]|null {
protected getClassDecoratorsFromHelperCall(symbol: ClassSymbol): Decorator[]|null {
const decorators: Decorator[] = [];
const helperCalls = this.getHelperCallsForClass(symbol, '__decorate');
helperCalls.forEach(helperCall => {
@ -497,13 +443,91 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
return decorators.length ? decorators : null;
}
/**
* Examine a symbol which should be of a class, and return metadata about its members.
*
* @param symbol the `ClassSymbol` representing the class over which to reflect.
* @returns an array of `ClassMember` metadata representing the members of the class.
*/
protected getMembersOfSymbol(symbol: ClassSymbol): ClassMember[] {
const members: ClassMember[] = [];
// The decorators map contains all the properties that are decorated
const decoratorsMap = this.getMemberDecorators(symbol);
// The member map contains all the method (instance and static); and any instance properties
// that are initialized in the class.
if (symbol.members) {
symbol.members.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
// The static property map contains all the static properties
if (symbol.exports) {
symbol.exports.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators, true);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
// If this class was declared as a VariableDeclaration then it may have static properties
// attached to the variable rather than the class itself
// For example:
// ```
// let MyClass = class MyClass {
// // no static properties here!
// }
// MyClass.staticProperty = ...;
// ```
if (ts.isVariableDeclaration(symbol.valueDeclaration.parent)) {
const variableSymbol = this.checker.getSymbolAtLocation(symbol.valueDeclaration.parent.name);
if (variableSymbol && variableSymbol.exports) {
variableSymbol.exports.forEach((value, key) => {
const decorators = decoratorsMap.get(key as string);
const reflectedMembers = this.reflectMembers(value, decorators, true);
if (reflectedMembers) {
decoratorsMap.delete(key as string);
members.push(...reflectedMembers);
}
});
}
}
// Deal with any decorated properties that were not initialized in the class
decoratorsMap.forEach((value, key) => {
members.push({
implementation: null,
decorators: value,
isStatic: false,
kind: ClassMemberKind.Property,
name: key,
nameNode: null,
node: null,
type: null,
value: null
});
});
return members;
}
/**
* Get all the member decorators for the given class.
* @param classSymbol the class whose member decorators we are interested in.
* @returns a map whose keys are the name of the members and whose values are collections of
* decorators for the given member.
*/
protected getMemberDecorators(classSymbol: ts.Symbol): Map<string, Decorator[]> {
protected getMemberDecorators(classSymbol: ClassSymbol): Map<string, Decorator[]> {
const decoratorsProperty = this.getStaticProperty(classSymbol, PROP_DECORATORS);
if (decoratorsProperty) {
return this.getMemberDecoratorsFromStaticProperty(decoratorsProperty);
@ -559,7 +583,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @returns a map whose keys are the name of the members and whose values are collections of
* decorators for the given member.
*/
protected getMemberDecoratorsFromHelperCalls(classSymbol: ts.Symbol): Map<string, Decorator[]> {
protected getMemberDecoratorsFromHelperCalls(classSymbol: ClassSymbol): Map<string, Decorator[]> {
const memberDecoratorMap = new Map<string, Decorator[]>();
const helperCalls = this.getHelperCallsForClass(classSymbol, '__decorate');
helperCalls.forEach(helperCall => {
@ -659,8 +683,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* Matching statements will look like: `tslib_1.__decorate(...);`.
* @param statement the statement that may contain the call.
* @param helperName the name of the helper we are looking for.
* @returns the node that corresponds to the `__decorate(...)` call or null if the statement does
* not match.
* @returns the node that corresponds to the `__decorate(...)` call or null if the statement
* does not match.
*/
protected getHelperCall(statement: ts.Statement, helperName: string): ts.CallExpression|null {
if (ts.isExpressionStatement(statement)) {
@ -858,7 +882,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @returns an array of `ts.ParameterDeclaration` objects representing each of the parameters in
* the class's constructor or null if there is no constructor.
*/
protected getConstructorParameterDeclarations(classSymbol: ts.Symbol):
protected getConstructorParameterDeclarations(classSymbol: ClassSymbol):
ts.ParameterDeclaration[]|null {
const constructorSymbol = classSymbol.members && classSymbol.members.get(CONSTRUCTOR);
if (constructorSymbol) {
@ -887,7 +911,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @returns an array of constructor parameter info objects.
*/
protected getConstructorParamInfo(
classSymbol: ts.Symbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
const paramsProperty = this.getStaticProperty(classSymbol, CONSTRUCTOR_PARAMS);
const paramInfo: ParamInfo[]|null = paramsProperty ?
this.getParamInfoFromStaticProperty(paramsProperty) :
@ -961,7 +985,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @returns an array of objects containing the type and decorators for each parameter.
*/
protected getParamInfoFromHelperCall(
classSymbol: ts.Symbol, parameterNodes: ts.ParameterDeclaration[]): ParamInfo[] {
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): ParamInfo[] {
const parameters: ParamInfo[] =
parameterNodes.map(() => ({typeExpression: null, decorators: null}));
const helperCalls = this.getHelperCallsForClass(classSymbol, '__decorate');
@ -1004,10 +1028,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
/**
* Search statements related to the given class for calls to the specified helper.
* @param classSymbol the class whose helper calls we are interested in.
* @param helperName the name of the helper (e.g. `__decorate`) whose calls we are interested in.
* @param helperName the name of the helper (e.g. `__decorate`) whose calls we are interested
* in.
* @returns an array of CallExpression nodes for each matching helper call.
*/
protected getHelperCallsForClass(classSymbol: ts.Symbol, helperName: string):
protected getHelperCallsForClass(classSymbol: ClassSymbol, helperName: string):
ts.CallExpression[] {
return this.getStatementsForClass(classSymbol)
.map(statement => this.getHelperCall(statement, helperName))
@ -1023,7 +1048,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* @param classSymbol the class whose helper calls we are interested in.
* @returns an array of statements that may contain helper calls.
*/
protected getStatementsForClass(classSymbol: ts.Symbol): ts.Statement[] {
protected getStatementsForClass(classSymbol: ClassSymbol): ts.Statement[] {
return Array.from(classSymbol.valueDeclaration.getSourceFile().statements);
}
@ -1099,8 +1124,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* Only the first declaration with a given name is added to the map; subsequent classes will be
* ignored.
*
* We are most interested in classes that are publicly exported from the entry point, so these are
* added to the map first, to ensure that they are not ignored.
* We are most interested in classes that are publicly exported from the entry point, so these
* are added to the map first, to ensure that they are not ignored.
*
* @param dtsRootFileName The filename of the entry-point to the `dtsTypings` program.
* @param dtsProgram The program containing all the typings files.
@ -1126,15 +1151,23 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
}
/**
* Parse the given node, to see if it is a function that returns a `ModuleWithProviders` object.
* @param node a node to check to see if it is a function that returns a `ModuleWithProviders`
* object.
* Parse a function/method node (or its implementation), to see if it returns a
* `ModuleWithProviders` object.
* @param name The name of the function.
* @param node the node to check - this could be a function, a method or a variable declaration.
* @param implementation the actual function expression if `node` is a variable declaration.
* @param container the class that contains the function, if it is a method.
* @returns info about the function if it does return a `ModuleWithProviders` object; `null`
* otherwise.
*/
protected parseForModuleWithProviders(node: ts.Node|null): ModuleWithProvidersFunction|null {
const declaration =
node && (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) ? node : null;
protected parseForModuleWithProviders(
name: string, node: ts.Node|null, implementation: ts.Node|null = node,
container: ts.Declaration|null = null): ModuleWithProvidersFunction|null {
const declaration = implementation &&
(ts.isFunctionDeclaration(implementation) || ts.isMethodDeclaration(implementation) ||
ts.isFunctionExpression(implementation)) ?
implementation :
null;
const body = declaration ? this.getDefinitionOfFunction(declaration).body : null;
const lastStatement = body && body[body.length - 1];
const returnExpression =
@ -1147,7 +1180,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
const ngModule = ngModuleProperty && ts.isPropertyAssignment(ngModuleProperty) &&
ts.isIdentifier(ngModuleProperty.initializer) && ngModuleProperty.initializer ||
null;
return ngModule && declaration && {ngModule, declaration};
return ngModule && declaration && {name, ngModule, declaration, container};
}
}

View File

@ -8,12 +8,13 @@
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, reflectObjectLiteral} from '../../../ngtsc/reflection';
import {ClassDeclaration, ClassMember, ClassMemberKind, ClassSymbol, CtorParameter, Declaration, Decorator, FunctionDefinition, Parameter, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
import {getNameText, hasNameIdentifier} from '../utils';
import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignmentStatement} from './esm2015_host';
/**
* ESM5 packages contain ECMAScript IIFE functions that act like classes. For example:
*
@ -32,26 +33,26 @@ import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignme
*
*/
export class Esm5ReflectionHost extends Esm2015ReflectionHost {
constructor(isCore: boolean, checker: ts.TypeChecker) { super(isCore, checker); }
/**
* Check whether the given node actually represents a class.
*/
isClass(node: ts.Node): node is ts.NamedDeclaration {
return super.isClass(node) || !!this.getClassSymbol(node);
}
isClass(node: ts.Node): node is ClassDeclaration { return !!this.getClassDeclaration(node); }
/**
* Determines whether the given declaration has a base class.
* Determines whether the given declaration, which should be a "class", has a base "class".
*
* In ES5, we need to determine if the IIFE wrapper takes a `_super` parameter .
* In ES5 code, we need to determine if the IIFE wrapper takes a `_super` parameter .
*
* @param clazz a `ClassDeclaration` representing the class over which to reflect.
*/
hasBaseClass(node: ts.Declaration): boolean {
const classSymbol = this.getClassSymbol(node);
if (!classSymbol) return false;
hasBaseClass(clazz: ClassDeclaration): boolean {
if (super.hasBaseClass(clazz)) return true;
const iifeBody = classSymbol.valueDeclaration.parent;
if (!iifeBody || !ts.isBlock(iifeBody)) return false;
const classDeclaration = this.getClassDeclaration(clazz);
if (!classDeclaration) return false;
const iifeBody = getIifeBody(classDeclaration);
if (!iifeBody) return false;
const iife = iifeBody.parent;
if (!iife || !ts.isFunctionExpression(iife)) return false;
@ -60,38 +61,39 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
}
/**
* Find a symbol for a node that we think is a class.
* Find the declaration of a class given a node that we think represents the class.
*
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE.
* So we might need to dig around inside to get hold of the "class" symbol.
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE,
* whose value is assigned to a variable (which represents the class to the rest of the program).
* So we might need to dig around to get hold of the "class" declaration.
*
* `node` might be one of:
* - A class declaration (from a declaration file).
* - A class declaration (from a typings file).
* - The declaration of the outer variable, which is assigned the result of the IIFE.
* - The function declaration inside the IIFE, which is eventually returned and assigned to the
* outer variable.
*
* @param node the top level declaration that represents an exported class or the function
* expression inside the IIFE.
* @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
* The returned declaration is either the class declaration (from the typings file) or the outer
* variable declaration.
*
* @param node the node that represents the class whose declaration we are finding.
* @returns the declaration of the class or `undefined` if it is not a "class".
*/
getClassSymbol(node: ts.Node): ts.Symbol|undefined {
const symbol = super.getClassSymbol(node);
if (symbol) return symbol;
getClassDeclaration(node: ts.Node): ClassDeclaration|undefined {
const superDeclaration = super.getClassDeclaration(node);
if (superDeclaration) return superDeclaration;
if (ts.isVariableDeclaration(node)) {
const iifeBody = getIifeBody(node);
if (!iifeBody) return undefined;
const outerClass = getClassDeclarationFromInnerFunctionDeclaration(node);
if (outerClass) return outerClass;
const innerClassIdentifier = getReturnIdentifier(iifeBody);
if (!innerClassIdentifier) return undefined;
return this.checker.getSymbolAtLocation(innerClassIdentifier);
// At this point, `node` could be the outer variable declaration of an ES5 class.
// If so, ensure that it has a `name` identifier and the correct structure.
if (!isNamedVariableDeclaration(node) ||
!this.getInnerFunctionDeclarationFromClassDeclaration(node)) {
return undefined;
}
const outerClassNode = getClassDeclarationFromInnerFunctionDeclaration(node);
return outerClassNode && this.getClassSymbol(outerClassNode);
return node;
}
/**
@ -114,13 +116,30 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
// Get the identifier for the outer class node (if any).
const outerClassNode = getClassDeclarationFromInnerFunctionDeclaration(id.parent);
const declaration = super.getDeclarationOfIdentifier(outerClassNode ? outerClassNode.name : id);
if (outerClassNode && hasNameIdentifier(outerClassNode)) {
id = outerClassNode.name;
if (!declaration || !ts.isVariableDeclaration(declaration.node) ||
declaration.node.initializer !== undefined ||
// VariableDeclaration => VariableDeclarationList => VariableStatement => IIFE Block
!ts.isBlock(declaration.node.parent.parent.parent)) {
return declaration;
}
// Resolve the identifier to a Symbol, and return the declaration of that.
return super.getDeclarationOfIdentifier(id);
// We might have an alias to another variable declaration.
// Search the containing iife body for it.
const block = declaration.node.parent.parent.parent;
const aliasSymbol = this.checker.getSymbolAtLocation(declaration.node.name);
for (let i = 0; i < block.statements.length; i++) {
const statement = block.statements[i];
// Looking for statement that looks like: `AliasedVariable = OriginalVariable;`
if (isAssignmentStatement(statement) && ts.isIdentifier(statement.expression.left) &&
ts.isIdentifier(statement.expression.right) &&
this.checker.getSymbolAtLocation(statement.expression.left) === aliasSymbol) {
return this.getDeclarationOfIdentifier(statement.expression.right);
}
}
return declaration;
}
/**
@ -149,31 +168,149 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
return {node, body: statements || null, parameters};
}
/**
* Examine a declaration which should be of a class, and return metadata about the members of the
* class.
*
* @param declaration a TypeScript `ts.Declaration` node representing the class over which to
* reflect.
*
* @returns an array of `ClassMember` metadata representing the members of the class.
*
* @throws if `declaration` does not resolve to a class declaration.
*/
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
if (super.isClass(clazz)) return super.getMembersOfClass(clazz);
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
const innerFunctionSymbol = this.getInnerFunctionSymbolFromClassDeclaration(clazz);
if (!innerFunctionSymbol) {
throw new Error(
`Attempted to get members of a non-class: "${(clazz as ClassDeclaration).getText()}"`);
}
return this.getMembersOfSymbol(innerFunctionSymbol);
}
///////////// Protected Helpers /////////////
/**
* Get the inner function declaration of an ES5-style class.
*
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE
* and returned to be assigned to a variable outside the IIFE, which is what the rest of the
* program interacts with.
*
* Given the outer variable declaration, we want to get to the inner function declaration.
*
* @param node a node that could be the variable expression outside an ES5 class IIFE.
* @param checker the TS program TypeChecker
* @returns the inner function declaration or `undefined` if it is not a "class".
*/
protected getInnerFunctionDeclarationFromClassDeclaration(node: ts.Node): ts.FunctionDeclaration
|undefined {
if (!ts.isVariableDeclaration(node)) return undefined;
// Extract the IIFE body (if any).
const iifeBody = getIifeBody(node);
if (!iifeBody) return undefined;
// Extract the function declaration from inside the IIFE.
const functionDeclaration = iifeBody.statements.find(ts.isFunctionDeclaration);
if (!functionDeclaration) return undefined;
// Extract the return identifier of the IIFE.
const returnIdentifier = getReturnIdentifier(iifeBody);
const returnIdentifierSymbol =
returnIdentifier && this.checker.getSymbolAtLocation(returnIdentifier);
if (!returnIdentifierSymbol) return undefined;
// Verify that the inner function is returned.
if (returnIdentifierSymbol.valueDeclaration !== functionDeclaration) return undefined;
return functionDeclaration;
}
/**
* Get the identifier symbol of the inner function declaration of an ES5-style class.
*
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE
* and returned to be assigned to a variable outside the IIFE, which is what the rest of the
* program interacts with.
*
* Given the outer variable declaration, we want to get to the identifier symbol of the inner
* function declaration.
*
* @param clazz a node that could be the variable expression outside an ES5 class IIFE.
* @param checker the TS program TypeChecker
* @returns the inner function declaration identifier symbol or `undefined` if it is not a "class"
* or has no identifier.
*/
protected getInnerFunctionSymbolFromClassDeclaration(clazz: ClassDeclaration): ClassSymbol
|undefined {
const innerFunctionDeclaration = this.getInnerFunctionDeclarationFromClassDeclaration(clazz);
if (!innerFunctionDeclaration || !hasNameIdentifier(innerFunctionDeclaration)) return undefined;
return this.checker.getSymbolAtLocation(innerFunctionDeclaration.name) as ClassSymbol;
}
/**
* Find the declarations of the constructor parameters of a class identified by its symbol.
*
* In ESM5 there is no "class" so the constructor that we want is actually the declaration
* function itself.
* In ESM5, there is no "class" so the constructor that we want is actually the inner function
* declaration inside the IIFE, whose return value is assigned to the outer variable declaration
* (that represents the class to the rest of the program).
*
* @param classSymbol the class whose parameters we want to find.
* @param classSymbol the symbol of the class (i.e. the outer variable declaration) whose
* parameters we want to find.
* @returns an array of `ts.ParameterDeclaration` objects representing each of the parameters in
* the class's constructor or null if there is no constructor.
* the class's constructor or `null` if there is no constructor.
*/
protected getConstructorParameterDeclarations(classSymbol: ts.Symbol):
protected getConstructorParameterDeclarations(classSymbol: ClassSymbol):
ts.ParameterDeclaration[]|null {
const constructor = classSymbol.valueDeclaration as ts.FunctionDeclaration;
const constructor =
this.getInnerFunctionDeclarationFromClassDeclaration(classSymbol.valueDeclaration);
if (!constructor) return null;
if (constructor.parameters.length > 0) {
return Array.from(constructor.parameters);
}
if (isSynthesizedConstructor(constructor)) {
return null;
}
return [];
}
/**
* Get the parameter decorators of a class constructor.
*
* @param classSymbol the symbol of the class (i.e. the outer variable declaration) whose
* parameter info we want to get.
* @param parameterNodes the array of TypeScript parameter nodes for this class's constructor.
* @returns an array of constructor parameter info objects.
*/
protected getConstructorParamInfo(
classSymbol: ClassSymbol, parameterNodes: ts.ParameterDeclaration[]): CtorParameter[] {
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
const innerFunctionSymbol =
this.getInnerFunctionSymbolFromClassDeclaration(classSymbol.valueDeclaration);
if (!innerFunctionSymbol) return [];
return super.getConstructorParamInfo(innerFunctionSymbol, parameterNodes);
}
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
const innerFunctionSymbol =
this.getInnerFunctionSymbolFromClassDeclaration(symbol.valueDeclaration);
if (!innerFunctionSymbol) return null;
return super.getDecoratorsOfSymbol(innerFunctionSymbol);
}
/**
* Get the parameter type and decorators for the constructor of a class,
* where the information is stored on a static method of the class.
@ -289,10 +426,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
* to reference the inner identifier inside the IIFE.
* @returns an array of statements that may contain helper calls.
*/
protected getStatementsForClass(classSymbol: ts.Symbol): ts.Statement[] {
const classDeclaration = classSymbol.valueDeclaration;
return ts.isBlock(classDeclaration.parent) ? Array.from(classDeclaration.parent.statements) :
[];
protected getStatementsForClass(classSymbol: ClassSymbol): ts.Statement[] {
const classDeclarationParent = classSymbol.valueDeclaration.parent;
return ts.isBlock(classDeclarationParent) ? Array.from(classDeclarationParent.statements) : [];
}
}
@ -367,8 +503,8 @@ function readPropertyFunctionExpression(object: ts.ObjectLiteralExpression, name
* @param node a node that could be the function expression inside an ES5 class IIFE.
* @returns the outer variable declaration or `undefined` if it is not a "class".
*/
function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node): ts.VariableDeclaration|
undefined {
function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node):
ClassDeclaration<ts.VariableDeclaration>|undefined {
if (ts.isFunctionDeclaration(node)) {
// It might be the function expression inside the IIFE. We need to go 5 levels up...
@ -392,14 +528,16 @@ function getClassDeclarationFromInnerFunctionDeclaration(node: ts.Node): ts.Vari
outerNode = outerNode.parent;
if (!outerNode || !ts.isVariableDeclaration(outerNode)) return undefined;
return outerNode;
// Finally, ensure that the variable declaration has a `name` identifier.
return hasNameIdentifier(outerNode) ? outerNode : undefined;
}
return undefined;
}
function getIifeBody(declaration: ts.VariableDeclaration): ts.Block|undefined {
if (!declaration.initializer || !ts.isParenthesizedExpression(declaration.initializer)) {
export function getIifeBody(declaration: ts.Declaration): ts.Block|undefined {
if (!ts.isVariableDeclaration(declaration) || !declaration.initializer ||
!ts.isParenthesizedExpression(declaration.initializer)) {
return undefined;
}
const call = declaration.initializer;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {ReflectionHost} from '../../../ngtsc/reflection';
import {ClassSymbol, ReflectionHost} from '../../../src/ngtsc/reflection';
import {DecoratedClass} from './decorated_class';
export const PRE_R3_MARKER = '__PRE_R3__';
@ -24,10 +24,18 @@ export function isSwitchableVariableDeclaration(node: ts.Node):
* that return ModuleWithProviders objects.
*/
export interface ModuleWithProvidersFunction {
/**
* The name of the declared function.
*/
name: string;
/**
* The declaration of the function that returns the `ModuleWithProviders` object.
*/
declaration: ts.SignatureDeclaration;
/**
* Declaration of the containing class (if this is a method)
*/
container: ts.Declaration|null;
/**
* The identifier of the `ngModule` property on the `ModuleWithProviders` object.
*/
@ -44,7 +52,7 @@ export interface NgccReflectionHost extends ReflectionHost {
* @returns the symbol for the declaration or `undefined` if it is not
* a "class" or has no symbol.
*/
getClassSymbol(node: ts.Node): ts.Symbol|undefined;
getClassSymbol(node: ts.Node): ClassSymbol|undefined;
/**
* Search the given module for variable declarations in which the initializer

View File

@ -0,0 +1,154 @@
/**
* @license
* Copyright Google Inc. 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 {resolve} from 'canonical-path';
import {readFileSync} from 'fs';
import {AbsoluteFsPath} from '../../src/ngtsc/path';
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
import {DependencyHost} from './packages/dependency_host';
import {DependencyResolver} from './packages/dependency_resolver';
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
import {makeEntryPointBundle} from './packages/entry_point_bundle';
import {EntryPointFinder} from './packages/entry_point_finder';
import {Transformer} from './packages/transformer';
import {FileWriter} from './writing/file_writer';
import {InPlaceFileWriter} from './writing/in_place_file_writer';
import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer';
/**
* The options to configure the ngcc compiler.
*/
export interface NgccOptions {
/** The absolute path to the `node_modules` folder that contains the packages to process. */
basePath: string;
/**
* The path, relative to `basePath` to the primary package to be processed.
*
* All its dependencies will need to be processed too.
*/
targetEntryPointPath?: string;
/**
* Which entry-point properties in the package.json to consider when processing an entry-point.
* Each property should hold a path to the particular bundle format for the entry-point.
* Defaults to all the properties in the package.json.
*/
propertiesToConsider?: string[];
/**
* Whether to process all formats specified by (`propertiesToConsider`) or to stop processing
* this entry-point at the first matching format. Defaults to `true`.
*/
compileAllFormats?: boolean;
/**
* Whether to create new entry-points bundles rather than overwriting the original files.
*/
createNewEntryPointFormats?: boolean;
}
const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
/**
* This is the main entry-point into ngcc (aNGular Compatibility Compiler).
*
* You can call this function to process one or more npm packages, to ensure
* that they are compatible with the ivy compiler (ngtsc).
*
* @param options The options telling ngcc what to compile and how.
*/
export function mainNgcc(
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
compileAllFormats = true, createNewEntryPointFormats = false}: NgccOptions): void {
const transformer = new Transformer(basePath, basePath);
const host = new DependencyHost();
const resolver = new DependencyResolver(host);
const finder = new EntryPointFinder(resolver);
const fileWriter = getFileWriter(createNewEntryPointFormats);
const absoluteTargetEntryPointPath = targetEntryPointPath ?
AbsoluteFsPath.from(resolve(basePath, targetEntryPointPath)) :
undefined;
const {entryPoints} =
finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath);
if (absoluteTargetEntryPointPath && entryPoints.every(entryPoint => {
return entryPoint.path !== absoluteTargetEntryPointPath;
})) {
// If we get here, then the requested entry-point did not contain anything compiled by
// the old Angular compiler. Therefore there is nothing for ngcc to do.
// So mark all formats in this entry-point as processed so that clients of ngcc can avoid
// triggering ngcc for this entry-point in the future.
const packageJsonPath =
AbsoluteFsPath.from(resolve(absoluteTargetEntryPointPath, 'package.json'));
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
propertiesToConsider.forEach(formatProperty => {
if (packageJson[formatProperty])
markAsProcessed(packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
});
return;
}
entryPoints.forEach(entryPoint => {
// Are we compiling the Angular core?
const isCore = entryPoint.name === '@angular/core';
const compiledFormats = new Set<string>();
const entryPointPackageJson = entryPoint.packageJson;
const entryPointPackageJsonPath = AbsoluteFsPath.from(resolve(entryPoint.path, 'package.json'));
for (let i = 0; i < propertiesToConsider.length; i++) {
const property = propertiesToConsider[i] as EntryPointJsonProperty;
const formatPath = entryPointPackageJson[property];
const format = getEntryPointFormat(property);
// No format then this property is not supposed to be compiled.
if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue;
if (hasBeenProcessed(entryPointPackageJson, property)) {
compiledFormats.add(formatPath);
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
continue;
}
// We don't break if this if statement fails because we still want to mark
// the property as processed even if its underlying format has been built already.
if (!compiledFormats.has(formatPath) && (compileAllFormats || compiledFormats.size === 0)) {
const bundle = makeEntryPointBundle(
entryPoint.path, formatPath, entryPoint.typings, isCore, property, format,
compiledFormats.size === 0);
if (bundle) {
console.warn(`Compiling ${entryPoint.name} : ${property} as ${format}`);
const transformedFiles = transformer.transform(bundle);
fileWriter.writeBundle(entryPoint, bundle, transformedFiles);
compiledFormats.add(formatPath);
} else {
console.warn(
`Skipping ${entryPoint.name} : ${format} (no valid entry point file for this format).`);
}
} else if (!compileAllFormats) {
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
}
// Either this format was just compiled or its underlying format was compiled because of a
// previous property.
if (compiledFormats.has(formatPath)) {
markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, property);
}
}
if (compiledFormats.size === 0) {
throw new Error(
`Failed to compile any formats for entry-point at (${entryPoint.path}). Tried ${propertiesToConsider}.`);
}
});
}
function getFileWriter(createNewEntryPointFormats: boolean): FileWriter {
return createNewEntryPointFormats ? new NewEntryPointFileWriter() : new InPlaceFileWriter();
}

View File

@ -0,0 +1,57 @@
/**
* @license
* Copyright Google Inc. 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 {writeFileSync} from 'fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
/**
* Check whether ngcc has already processed a given entry-point format.
*
* The entry-point is defined by the package.json contents provided.
* The format is defined by the provided property name of the path to the bundle in the package.json
*
* @param packageJson The parsed contents of the package.json file for the entry-point.
* @param format The entry-point format property in the package.json to check.
* @returns true if the entry-point and format have already been processed with this ngcc version.
* @throws Error if the `packageJson` property is not an object.
* @throws Error if the entry-point has already been processed with a different ngcc version.
*/
export function hasBeenProcessed(
packageJson: EntryPointPackageJson, format: EntryPointJsonProperty): boolean {
if (!packageJson.__processed_by_ivy_ngcc__) {
return false;
}
if (Object.keys(packageJson.__processed_by_ivy_ngcc__)
.some(property => packageJson.__processed_by_ivy_ngcc__ ![property] !== NGCC_VERSION)) {
throw new Error(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
}
return packageJson.__processed_by_ivy_ngcc__[format] === NGCC_VERSION;
}
/**
* Write a build marker for the given entry-point and format property, to indicate that it has
* been compiled by this version of ngcc.
*
* @param entryPoint the entry-point to write a marker.
* @param format the property in the package.json of the format for which we are writing the marker.
*/
export function markAsProcessed(
packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
format: EntryPointJsonProperty) {
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
writeFileSync(packageJsonPath, JSON.stringify(packageJson), 'utf8');
}

View File

@ -10,6 +10,8 @@ import * as path from 'canonical-path';
import * as fs from 'fs';
import * as ts from 'typescript';
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
/**
* Helper functions for computing dependencies.
*/
@ -17,7 +19,8 @@ export class DependencyHost {
/**
* Get a list of the resolved paths to all the dependencies of this entry point.
* @param from An absolute path to the file whose dependencies we want to get.
* @param resolved A set that will have the absolute paths of resolved entry points added to it.
* @param dependencies A set that will have the absolute paths of resolved entry points added to
* it.
* @param missing A set that will have the dependencies that could not be found added to it.
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
* entry-points, i.e. deep-imports.
@ -25,11 +28,16 @@ export class DependencyHost {
* circular dependency loop.
*/
computeDependencies(
from: string, resolved: Set<string>, missing: Set<string>, deepImports: Set<string>,
internal: Set<string> = new Set()): void {
from: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath> = new Set(),
missing: Set<PathSegment> = new Set(), deepImports: Set<PathSegment> = new Set(),
internal: Set<AbsoluteFsPath> = new Set()): {
dependencies: Set<AbsoluteFsPath>,
missing: Set<PathSegment>,
deepImports: Set<PathSegment>
} {
const fromContents = fs.readFileSync(from, 'utf8');
if (!this.hasImportOrReexportStatements(fromContents)) {
return;
return {dependencies, missing, deepImports};
}
// Parse the source into a TypeScript AST and then walk it looking for imports and re-exports.
@ -41,7 +49,7 @@ export class DependencyHost {
// Grab the id of the module that is being imported
.map(stmt => stmt.moduleSpecifier.text)
// Resolve this module id into an absolute path
.forEach(importPath => {
.forEach((importPath: PathSegment) => {
if (importPath.startsWith('.')) {
// This is an internal import so follow it
const internalDependency = this.resolveInternal(from, importPath);
@ -49,12 +57,12 @@ export class DependencyHost {
if (!internal.has(internalDependency)) {
internal.add(internalDependency);
this.computeDependencies(
internalDependency, resolved, missing, deepImports, internal);
internalDependency, dependencies, missing, deepImports, internal);
}
} else {
const resolvedEntryPoint = this.tryResolveEntryPoint(from, importPath);
if (resolvedEntryPoint !== null) {
resolved.add(resolvedEntryPoint);
dependencies.add(resolvedEntryPoint);
} else {
// If the import could not be resolved as entry point, it either does not exist
// at all or is a deep import.
@ -67,6 +75,7 @@ export class DependencyHost {
}
}
});
return {dependencies, missing, deepImports};
}
/**
@ -75,11 +84,11 @@ export class DependencyHost {
* @param to the module specifier of the internal dependency to resolve
* @returns the resolved path to the import.
*/
resolveInternal(from: string, to: string): string {
resolveInternal(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath {
const fromDirectory = path.dirname(from);
// `fromDirectory` is absolute so we don't need to worry about telling `require.resolve`
// about it - unlike `tryResolve` below.
return require.resolve(path.resolve(fromDirectory, to));
// about it by adding it to a `paths` parameter - unlike `tryResolve` below.
return AbsoluteFsPath.from(require.resolve(path.resolve(fromDirectory, to)));
}
/**
@ -99,9 +108,9 @@ export class DependencyHost {
* @returns the resolved path to the entry point directory of the import or null
* if it cannot be resolved.
*/
tryResolveEntryPoint(from: string, to: string): string|null {
const entryPoint = this.tryResolve(from, `${to}/package.json`);
return entryPoint && path.dirname(entryPoint);
tryResolveEntryPoint(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
const entryPoint = this.tryResolve(from, `${to}/package.json` as PathSegment);
return entryPoint && AbsoluteFsPath.from(path.dirname(entryPoint));
}
/**
@ -112,9 +121,9 @@ export class DependencyHost {
* @returns an absolute path to the entry-point of the dependency or null if it could not be
* resolved.
*/
tryResolve(from: string, to: string): string|null {
tryResolve(from: AbsoluteFsPath, to: PathSegment): AbsoluteFsPath|null {
try {
return require.resolve(to, {paths: [from]});
return AbsoluteFsPath.from(require.resolve(to, {paths: [from]}));
} catch (e) {
return null;
}

View File

@ -6,9 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {resolve} from 'canonical-path';
import {DepGraph} from 'dependency-graph';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyHost} from './dependency_host';
import {EntryPoint} from './entry_point';
import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat} from './entry_point';
/**
@ -44,7 +47,7 @@ export interface IgnoredDependency {
}
/**
* The result of sorting the entry-points by their dependencies.
* A list of entry-points, sorted by their dependencies.
*
* The `entryPoints` array will be ordered so that no entry point depends upon an entry point that
* appears later in the array.
@ -67,28 +70,46 @@ export class DependencyResolver {
* Sort the array of entry points so that the dependant entry points always come later than
* their dependencies in the array.
* @param entryPoints An array entry points to sort.
* @returns the result of sorting the entry points.
* @param target If provided, only return entry-points depended on by this entry-point.
* @returns the result of sorting the entry points by dependency.
*/
sortEntryPointsByDependency(entryPoints: EntryPoint[]): SortedEntryPointsInfo {
sortEntryPointsByDependency(entryPoints: EntryPoint[], target?: EntryPoint):
SortedEntryPointsInfo {
const {invalidEntryPoints, ignoredDependencies, graph} = this.createDependencyInfo(entryPoints);
let sortedEntryPointNodes: string[];
if (target) {
sortedEntryPointNodes = graph.dependenciesOf(target.path);
sortedEntryPointNodes.push(target.path);
} else {
sortedEntryPointNodes = graph.overallOrder();
}
return {
entryPoints: sortedEntryPointNodes.map(path => graph.getNodeData(path)),
invalidEntryPoints,
ignoredDependencies,
};
}
/**
* Computes a dependency graph of the given entry-points.
*
* The graph only holds entry-points that ngcc cares about and whose dependencies
* (direct and transitive) all exist.
*/
private createDependencyInfo(entryPoints: EntryPoint[]) {
const invalidEntryPoints: InvalidEntryPoint[] = [];
const ignoredDependencies: IgnoredDependency[] = [];
const graph = new DepGraph<EntryPoint>();
// Add the entry ponts to the graph as nodes
// Add the entry points to the graph as nodes
entryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint));
// Now add the dependencies between them
entryPoints.forEach(entryPoint => {
const entryPointPath = entryPoint.fesm2015 || entryPoint.esm2015;
if (!entryPointPath) {
throw new Error(
`ESM2015 format (flat and non-flat) missing in '${entryPoint.path}' entry-point.`);
}
const dependencies = new Set<string>();
const missing = new Set<string>();
const deepImports = new Set<string>();
this.host.computeDependencies(entryPointPath, dependencies, missing, deepImports);
const entryPointPath = getEntryPointPath(entryPoint);
const {dependencies, missing, deepImports} = this.host.computeDependencies(entryPointPath);
if (missing.size > 0) {
// This entry point has dependencies that are missing
@ -119,13 +140,7 @@ export class DependencyResolver {
}
});
// The map now only holds entry-points that ngcc cares about and whose dependencies
// (direct and transitive) all exist.
return {
entryPoints: graph.overallOrder().map(path => graph.getNodeData(path)),
invalidEntryPoints,
ignoredDependencies
};
return {invalidEntryPoints, ignoredDependencies, graph};
function removeNodes(entryPoint: EntryPoint, missingDependencies: string[]) {
const nodesToRemove = [entryPoint.path, ...graph.dependantsOf(entryPoint.path)];
@ -136,3 +151,17 @@ export class DependencyResolver {
}
}
}
function getEntryPointPath(entryPoint: EntryPoint): AbsoluteFsPath {
const properties = Object.keys(entryPoint.packageJson);
for (let i = 0; i < properties.length; i++) {
const property = properties[i] as EntryPointJsonProperty;
const format = getEntryPointFormat(property);
if (format === 'esm2015' || format === 'esm5') {
const formatPath = entryPoint.packageJson[property] !;
return AbsoluteFsPath.from(resolve(entryPoint.path, formatPath));
}
}
throw new Error(`There is no format with import statements in '${entryPoint.path}' entry-point.`);
}

View File

@ -9,43 +9,32 @@
import * as path from 'canonical-path';
import * as fs from 'fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
/**
* An object containing paths to the entry-points for each format.
*/
export interface EntryPointPaths {
esm5?: string;
fesm5?: string;
esm2015?: string;
fesm2015?: string;
umd?: string;
}
/**
* The possible values for the format of an entry-point.
*/
export type EntryPointFormat = keyof(EntryPointPaths);
export type EntryPointFormat = 'esm5' | 'esm2015' | 'umd';
/**
* An object containing information about an entry-point, including paths
* to each of the possible entry-point formats.
*/
export interface EntryPoint extends EntryPointPaths {
export interface EntryPoint {
/** The name of the package (e.g. `@angular/core`). */
name: string;
/** The parsed package.json file for this entry-point. */
packageJson: EntryPointPackageJson;
/** The path to the package that contains this entry-point. */
package: string;
package: AbsoluteFsPath;
/** The path to this entry point. */
path: string;
path: AbsoluteFsPath;
/** The path to a typings (.d.ts) file for this entry-point. */
typings: string;
typings: AbsoluteFsPath;
}
/**
* The properties that may be loaded from the `package.json` file.
*/
interface EntryPointPackageJson {
name: string;
interface PackageJsonFormatProperties {
fesm2015?: string;
fesm5?: string;
es2015?: string; // if exists then it is actually FESM2015
@ -57,6 +46,90 @@ interface EntryPointPackageJson {
typings?: string; // TypeScript .d.ts files
}
/**
* The properties that may be loaded from the `package.json` file.
*/
export interface EntryPointPackageJson extends PackageJsonFormatProperties {
name: string;
__processed_by_ivy_ngcc__?: {[key: string]: string};
}
export type EntryPointJsonProperty = keyof(PackageJsonFormatProperties);
// We need to keep the elements of this const and the `EntryPointJsonProperty` type in sync.
export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module'];
/**
* Try to create an entry-point from the given paths and properties.
*
* @param packagePath the absolute path to the containing npm package
* @param entryPointPath the absolute path to the potential entry-point.
* @returns An entry-point if it is valid, `null` otherwise.
*/
export function getEntryPointInfo(
packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPoint|null {
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
return null;
}
const entryPointPackageJson = loadEntryPointPackage(packageJsonPath);
if (!entryPointPackageJson) {
return null;
}
// We must have a typings property
const typings = entryPointPackageJson.typings || entryPointPackageJson.types;
if (!typings) {
return null;
}
// Also there must exist a `metadata.json` file next to the typings entry-point.
const metadataPath =
path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
if (!fs.existsSync(metadataPath)) {
return null;
}
const entryPointInfo: EntryPoint = {
name: entryPointPackageJson.name,
packageJson: entryPointPackageJson,
package: packagePath,
path: entryPointPath,
typings: AbsoluteFsPath.from(path.resolve(entryPointPath, typings)),
};
return entryPointInfo;
}
/**
* Convert a package.json property into an entry-point format.
*
* @param property The property to convert to a format.
* @returns An entry-point format or `undefined` if none match the given property.
*/
export function getEntryPointFormat(property: string): EntryPointFormat|undefined {
switch (property) {
case 'fesm2015':
return 'esm2015';
case 'fesm5':
return 'esm5';
case 'es2015':
return 'esm2015';
case 'esm2015':
return 'esm2015';
case 'esm5':
return 'esm5';
case 'main':
return 'umd';
case 'module':
return 'esm5';
default:
return undefined;
}
}
/**
* Parses the JSON from a package.json file.
* @param packageJsonPath the absolute path to the package.json file.
@ -71,72 +144,3 @@ function loadEntryPointPackage(packageJsonPath: string): EntryPointPackageJson|n
return null;
}
}
/**
* Try to get an entry point from the given path.
* @param packagePath the absolute path to the containing npm package
* @param entryPointPath the absolute path to the potential entry point.
* @returns Info about the entry point if it is valid, `null` otherwise.
*/
export function getEntryPointInfo(packagePath: string, entryPointPath: string): EntryPoint|null {
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
return null;
}
const entryPointPackageJson = loadEntryPointPackage(packageJsonPath);
if (!entryPointPackageJson) {
return null;
}
// If there is `esm2015` then `es2015` will be FESM2015, otherwise ESM2015.
// If there is `esm5` then `module` will be FESM5, otherwise it will be ESM5.
const {
name,
module: modulePath,
types,
typings = types, // synonymous
es2015,
fesm2015 = es2015, // synonymous
fesm5 = modulePath, // synonymous
esm2015,
esm5,
main
} = entryPointPackageJson;
// Minimum requirement is that we have typings and one of esm2015 or fesm2015 formats.
if (!typings || !(fesm2015 || esm2015)) {
return null;
}
// Also there must exist a `metadata.json` file next to the typings entry-point.
const metadataPath =
path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
if (!fs.existsSync(metadataPath)) {
return null;
}
const entryPointInfo: EntryPoint = {
name,
package: packagePath,
path: entryPointPath,
typings: path.resolve(entryPointPath, typings),
};
if (esm2015) {
entryPointInfo.esm2015 = path.resolve(entryPointPath, esm2015);
}
if (fesm2015) {
entryPointInfo.fesm2015 = path.resolve(entryPointPath, fesm2015);
}
if (fesm5) {
entryPointInfo.fesm5 = path.resolve(entryPointPath, fesm5);
}
if (esm5) {
entryPointInfo.esm5 = path.resolve(entryPointPath, esm5);
}
if (main) {
entryPointInfo.umd = path.resolve(entryPointPath, main);
}
return entryPointInfo;
}

View File

@ -0,0 +1,63 @@
/**
* @license
* Copyright Google Inc. 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 {resolve} from 'canonical-path';
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {BundleProgram, makeBundleProgram} from './bundle_program';
import {EntryPointFormat, EntryPointJsonProperty} from './entry_point';
/**
* A bundle of files and paths (and TS programs) that correspond to a particular
* format of a package entry-point.
*/
export interface EntryPointBundle {
formatProperty: EntryPointJsonProperty;
format: EntryPointFormat;
isCore: boolean;
isFlatCore: boolean;
rootDirs: AbsoluteFsPath[];
src: BundleProgram;
dts: BundleProgram|null;
}
/**
* Get an object that describes a formatted bundle for an entry-point.
* @param entryPointPath The path to the entry-point that contains the bundle.
* @param formatPath The path to the source files for this bundle.
* @param typingsPath The path to the typings files if we should transform them with this bundle.
* @param isCore This entry point is the Angular core package.
* @param format The underlying format of the bundle.
* @param transformDts Whether to transform the typings along with this bundle.
*/
export function makeEntryPointBundle(
entryPointPath: string, formatPath: string, typingsPath: string, isCore: boolean,
formatProperty: EntryPointJsonProperty, format: EntryPointFormat,
transformDts: boolean): EntryPointBundle|null {
// Create the TS program and necessary helpers.
const options: ts.CompilerOptions = {
allowJs: true,
maxNodeModuleJsDepth: Infinity,
rootDir: entryPointPath,
};
const host = ts.createCompilerHost(options);
const rootDirs = [AbsoluteFsPath.from(entryPointPath)];
// Create the bundle programs, as necessary.
const src = makeBundleProgram(
isCore, resolve(entryPointPath, formatPath), 'r3_symbols.js', options, host);
const dts = transformDts ?
makeBundleProgram(
isCore, resolve(entryPointPath, typingsPath), 'r3_symbols.d.ts', options, host) :
null;
const isFlatCore = isCore && src.r3SymbolsFile === null;
return {format, formatProperty, rootDirs, isCore, isFlatCore, src, dts};
}

View File

@ -8,6 +8,7 @@
import * as path from 'canonical-path';
import * as fs from 'fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyResolver, SortedEntryPointsInfo} from './dependency_resolver';
import {EntryPoint, getEntryPointInfo} from './entry_point';
@ -18,9 +19,13 @@ export class EntryPointFinder {
* Search the given directory, and sub-directories, for Angular package entry points.
* @param sourceDirectory An absolute path to the directory to search for entry points.
*/
findEntryPoints(sourceDirectory: string): SortedEntryPointsInfo {
findEntryPoints(sourceDirectory: AbsoluteFsPath, targetEntryPointPath?: AbsoluteFsPath):
SortedEntryPointsInfo {
const unsortedEntryPoints = walkDirectoryForEntryPoints(sourceDirectory);
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints);
const targetEntryPoint = targetEntryPointPath ?
unsortedEntryPoints.find(entryPoint => entryPoint.path === targetEntryPointPath) :
undefined;
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints, targetEntryPoint);
}
}
@ -29,7 +34,7 @@ export class EntryPointFinder {
* The function will recurse into directories that start with `@...`, e.g. `@angular/...`.
* @param sourceDirectory An absolute path to the root directory where searching begins.
*/
function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
function walkDirectoryForEntryPoints(sourceDirectory: AbsoluteFsPath): EntryPoint[] {
const entryPoints: EntryPoint[] = [];
fs.readdirSync(sourceDirectory)
// Not interested in hidden files
@ -44,14 +49,15 @@ function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
.forEach(p => {
// Either the directory is a potential package or a namespace containing packages (e.g
// `@angular`).
const packagePath = path.join(sourceDirectory, p);
const packagePath = AbsoluteFsPath.from(path.join(sourceDirectory, p));
if (p.startsWith('@')) {
entryPoints.push(...walkDirectoryForEntryPoints(packagePath));
} else {
entryPoints.push(...getEntryPointsForPackage(packagePath));
// Also check for any nested node_modules in this package
const nestedNodeModulesPath = path.resolve(packagePath, 'node_modules');
const nestedNodeModulesPath =
AbsoluteFsPath.from(path.resolve(packagePath, 'node_modules'));
if (fs.existsSync(nestedNodeModulesPath)) {
entryPoints.push(...walkDirectoryForEntryPoints(nestedNodeModulesPath));
}
@ -65,7 +71,7 @@ function walkDirectoryForEntryPoints(sourceDirectory: string): EntryPoint[] {
* @param packagePath The absolute path to an npm package that may contain entry points
* @returns An array of entry points that were discovered.
*/
function getEntryPointsForPackage(packagePath: string): EntryPoint[] {
function getEntryPointsForPackage(packagePath: AbsoluteFsPath): EntryPoint[] {
const entryPoints: EntryPoint[] = [];
// Try to get an entry point from the top level package directory
@ -91,7 +97,7 @@ function getEntryPointsForPackage(packagePath: string): EntryPoint[] {
* @param dir the directory to recursively walk.
* @param fn the function to apply to each directory.
*/
function walkDirectory(dir: string, fn: (dir: string) => void) {
function walkDirectory(dir: AbsoluteFsPath, fn: (dir: AbsoluteFsPath) => void) {
return fs
.readdirSync(dir)
// Not interested in hidden files
@ -103,9 +109,9 @@ function walkDirectory(dir: string, fn: (dir: string) => void) {
const stat = fs.lstatSync(path.resolve(dir, p));
return stat.isDirectory() && !stat.isSymbolicLink();
})
.forEach(subdir => {
subdir = path.resolve(dir, subdir);
fn(subdir);
walkDirectory(subdir, fn);
.forEach(subDir => {
const resolvedSubDir = AbsoluteFsPath.from(path.resolve(dir, subDir));
fn(resolvedSubDir);
walkDirectory(resolvedSubDir, fn);
});
}

View File

@ -5,9 +5,6 @@
* 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 {dirname} from 'canonical-path';
import {existsSync, writeFileSync} from 'fs';
import {mkdir, mv} from 'shelljs';
import * as ts from 'typescript';
import {CompiledFile, DecorationAnalyzer} from '../analysis/decoration_analyzer';
@ -22,7 +19,6 @@ import {Esm5Renderer} from '../rendering/esm5_renderer';
import {EsmRenderer} from '../rendering/esm_renderer';
import {FileInfo, Renderer} from '../rendering/renderer';
import {EntryPoint} from './entry_point';
import {EntryPointBundle} from './entry_point_bundle';
@ -54,10 +50,10 @@ export class Transformer {
/**
* Transform the source (and typings) files of a bundle.
* @param bundle the bundle to transform.
* @returns information about the files that were transformed.
*/
transform(entryPoint: EntryPoint, isCore: boolean, bundle: EntryPointBundle): void {
console.warn(`Compiling ${entryPoint.name} - ${bundle.format}`);
transform(bundle: EntryPointBundle): FileInfo[] {
const isCore = bundle.isCore;
const reflectionHost = this.getHost(isCore, bundle);
// Parse and analyze the files.
@ -70,19 +66,16 @@ export class Transformer {
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
// Write out all the transformed files.
renderedFiles.forEach(file => this.writeFile(file));
return renderedFiles;
}
getHost(isCore: boolean, bundle: EntryPointBundle): NgccReflectionHost {
const typeChecker = bundle.src.program.getTypeChecker();
switch (bundle.format) {
case 'esm2015':
case 'fesm2015':
return new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts);
case 'esm5':
case 'fesm5':
return new Esm5ReflectionHost(isCore, typeChecker);
return new Esm5ReflectionHost(isCore, typeChecker, bundle.dts);
default:
throw new Error(`Reflection host for "${bundle.format}" not yet implemented.`);
}
@ -91,10 +84,8 @@ export class Transformer {
getRenderer(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle): Renderer {
switch (bundle.format) {
case 'esm2015':
case 'fesm2015':
return new EsmRenderer(host, isCore, bundle, this.sourcePath, this.targetPath);
case 'esm5':
case 'fesm5':
return new Esm5Renderer(host, isCore, bundle, this.sourcePath, this.targetPath);
default:
throw new Error(`Renderer for "${bundle.format}" not yet implemented.`);
@ -127,15 +118,6 @@ export class Transformer {
return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses};
}
writeFile(file: FileInfo): void {
mkdir('-p', dirname(file.path));
const backPath = file.path + '.bak';
if (existsSync(file.path) && !existsSync(backPath)) {
mv(file.path, backPath);
}
writeFileSync(file.path, file.contents, 'utf8');
}
}

View File

@ -7,6 +7,7 @@
*/
import * as ts from 'typescript';
import MagicString from 'magic-string';
import {getIifeBody} from '../host/esm5_host';
import {NgccReflectionHost} from '../host/ngcc_host';
import {CompiledClass} from '../analysis/decoration_analyzer';
import {EsmRenderer} from './esm_renderer';
@ -23,21 +24,18 @@ export class Esm5Renderer extends EsmRenderer {
* Add the definitions to each decorated class
*/
addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string): void {
const classSymbol = this.host.getClassSymbol(compiledClass.declaration);
if (!classSymbol) {
throw new Error(
`Compiled class does not have a valid symbol: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
}
const parent = classSymbol.valueDeclaration && classSymbol.valueDeclaration.parent;
if (!parent || !ts.isBlock(parent)) {
const iifeBody = getIifeBody(compiledClass.declaration);
if (!iifeBody) {
throw new Error(
`Compiled class declaration is not inside an IIFE: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
}
const returnStatement = parent.statements.find(statement => ts.isReturnStatement(statement));
const returnStatement = iifeBody.statements.find(ts.isReturnStatement);
if (!returnStatement) {
throw new Error(
`Compiled class wrapper IIFE does not have a return statement: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`);
}
const insertionPoint = returnStatement.getFullStart();
output.appendLeft(insertionPoint, '\n' + definitions);
}

View File

@ -13,7 +13,7 @@ import {CompiledClass} from '../analysis/decoration_analyzer';
import {RedundantDecoratorMap, Renderer, stripExtension} from './renderer';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {ExportInfo} from '../analysis/private_declarations_analyzer';
import {isDtsPath} from '../../../ngtsc/util/src/typescript';
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
export class EsmRenderer extends Renderer {
constructor(

View File

@ -7,7 +7,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ImportRewriter, validateAndRewriteCoreSymbol} from '../../../ngtsc/imports';
import {ImportRewriter, validateAndRewriteCoreSymbol} from '../../../src/ngtsc/imports';
export class NgccFlatImportRewriter implements ImportRewriter {
shouldImportSymbol(symbol: string, specifier: string): boolean {

View File

@ -15,7 +15,7 @@ import * as ts from 'typescript';
import {NoopImportRewriter, ImportRewriter, R3SymbolsImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER} from '@angular/compiler-cli/src/ngtsc/imports';
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
import {translateStatement, translateType, ImportManager} from '../../../src/ngtsc/translator';
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
@ -137,7 +137,8 @@ export abstract class Renderer {
if (compiledFile) {
const importManager = new ImportManager(
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlat), IMPORT_PREFIX);
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlatCore),
IMPORT_PREFIX);
// TODO: remove constructor param metadata and property decorators (we need info from the
// handlers to do this)
@ -480,7 +481,7 @@ export function renderConstantPool(
export function renderDefinitions(
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager): string {
const printer = ts.createPrinter();
const name = (compiledClass.declaration as ts.NamedDeclaration).name !;
const name = compiledClass.declaration.name;
const translate = (stmt: Statement) =>
translateStatement(stmt, imports, NOOP_DEFAULT_IMPORT_RECORDER);
const definitions =

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. 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 {EntryPoint} from '../packages/entry_point';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {FileInfo} from '../rendering/renderer';
/**
* Responsible for writing out the transformed files to disk.
*/
export interface FileWriter {
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]): void;
}

View File

@ -0,0 +1,40 @@
/**
* @license
* Copyright Google Inc. 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 {dirname} from 'canonical-path';
import {existsSync, writeFileSync} from 'fs';
import {mkdir, mv} from 'shelljs';
import {EntryPoint} from '../packages/entry_point';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {FileInfo} from '../rendering/renderer';
import {FileWriter} from './file_writer';
/**
* This FileWriter overwrites the transformed file, in-place, while creating
* a back-up of the original file with an extra `.bak` extension.
*/
export class InPlaceFileWriter implements FileWriter {
writeBundle(_entryPoint: EntryPoint, _bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
transformedFiles.forEach(file => this.writeFileAndBackup(file));
}
protected writeFileAndBackup(file: FileInfo): void {
mkdir('-p', dirname(file.path));
const backPath = file.path + '.__ivy_ngcc_bak';
if (existsSync(backPath)) {
throw new Error(
`Tried to overwrite ${backPath} with an ngcc back up file, which is disallowed.`);
}
if (existsSync(file.path)) {
mv(file.path, backPath);
}
writeFileSync(file.path, file.contents, 'utf8');
}
}

View File

@ -0,0 +1,72 @@
/**
* @license
* Copyright Google Inc. 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 {dirname, join, relative} from 'canonical-path';
import {writeFileSync} from 'fs';
import {cp, mkdir} from 'shelljs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {FileInfo} from '../rendering/renderer';
import {InPlaceFileWriter} from './in_place_file_writer';
const NGCC_DIRECTORY = '__ivy_ngcc__';
/**
* This FileWriter creates a copy of the original entry-point, then writes the transformed
* files onto the files in this copy, and finally updates the package.json with a new
* entry-point format property that points to this new entry-point.
*
* If there are transformed typings files in this bundle, they are updated in-place (see the
* `InPlaceFileWriter`).
*/
export class NewEntryPointFileWriter extends InPlaceFileWriter {
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
// The new folder is at the root of the overall package
const relativeEntryPointPath = relative(entryPoint.package, entryPoint.path);
const relativeNewDir = join(NGCC_DIRECTORY, relativeEntryPointPath);
const newDir = AbsoluteFsPath.fromUnchecked(join(entryPoint.package, relativeNewDir));
this.copyBundle(bundle, entryPoint.path, newDir);
transformedFiles.forEach(file => this.writeFile(file, entryPoint.path, newDir));
this.updatePackageJson(entryPoint, bundle.formatProperty, newDir);
}
protected copyBundle(
bundle: EntryPointBundle, entryPointPath: AbsoluteFsPath, newDir: AbsoluteFsPath) {
bundle.src.program.getSourceFiles().forEach(sourceFile => {
const relativePath = relative(entryPointPath, sourceFile.fileName);
const newFilePath = join(newDir, relativePath);
mkdir('-p', dirname(newFilePath));
cp(sourceFile.fileName, newFilePath);
});
}
protected writeFile(file: FileInfo, entryPointPath: AbsoluteFsPath, newDir: AbsoluteFsPath):
void {
if (isDtsPath(file.path)) {
super.writeFileAndBackup(file);
} else {
const relativePath = relative(entryPointPath, file.path);
const newFilePath = join(newDir, relativePath);
mkdir('-p', dirname(newFilePath));
writeFileSync(newFilePath, file.contents, 'utf8');
}
}
protected updatePackageJson(
entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, newDir: AbsoluteFsPath) {
const bundlePath = entryPoint.packageJson[formatProperty] !;
const newBundlePath = relative(entryPoint.path, join(newDir, bundlePath));
(entryPoint.packageJson as any)[formatProperty + '_ivy_ngcc'] = newBundlePath;
writeFileSync(join(entryPoint.path, 'package.json'), JSON.stringify(entryPoint.packageJson));
}
}

View File

@ -0,0 +1,75 @@
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
package(default_visibility = ["//visibility:public"])
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["**/*.ts"],
exclude = ["integration/**/*.ts"],
),
deps = [
"//packages/compiler-cli/ngcc",
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/testing",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/test:test_utils",
"@npm//@types/convert-source-map",
"@npm//@types/mock-fs",
"@npm//canonical-path",
"@npm//magic-string",
"@npm//typescript",
],
)
jasmine_node_test(
name = "test",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
deps = [
":test_lib",
"//tools/testing:node_no_angular",
"@npm//canonical-path",
"@npm//convert-source-map",
"@npm//shelljs",
],
)
ts_library(
name = "integration_lib",
testonly = True,
srcs = glob(
["integration/**/*.ts"],
),
deps = [
"//packages/compiler-cli/ngcc",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/test:test_utils",
"@npm//@types/mock-fs",
"@npm//rxjs",
],
)
jasmine_node_test(
name = "integration",
bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"],
data = [
"//packages/common:npm_package",
"//packages/core:npm_package",
"@npm//rxjs",
],
tags = [
# Disabled in AOT mode because we want ngcc to compile non-AOT Angular packages.
"no-ivy-aot",
],
deps = [
":integration_lib",
"//tools/testing:node_no_angular",
"@npm//canonical-path",
"@npm//convert-source-map",
"@npm//shelljs",
],
)

View File

@ -7,9 +7,9 @@
*/
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../ngtsc/path';
import {Decorator} from '../../../ngtsc/reflection';
import {DecoratorHandler, DetectResult} from '../../../ngtsc/transform';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {Decorator} from '../../../src/ngtsc/reflection';
import {DecoratorHandler, DetectResult} from '../../../src/ngtsc/transform';
import {CompiledClass, DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {Reference} from '../../../ngtsc/imports';
import {Reference} from '../../../src/ngtsc/imports';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';

View File

@ -8,10 +8,10 @@
import * as ts from 'typescript';
import {Reference} from '../../../ngtsc/imports';
import {PartialEvaluator} from '../../../ngtsc/partial_evaluator';
import {TypeScriptReflectionHost} from '../../../ngtsc/reflection';
import {getDeclaration, makeProgram} from '../../../ngtsc/testing/in_memory_typescript';
import {Reference} from '../../../src/ngtsc/imports';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection';
import {getDeclaration, makeProgram} from '../../../src/ngtsc/testing/in_memory_typescript';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
describe('NgccReferencesRegistry', () => {

View File

@ -7,15 +7,13 @@
*/
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../ngtsc/path';
import {makeProgram} from '../../../ngtsc/testing/in_memory_typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {makeProgram} from '../../../src/ngtsc/testing/in_memory_typescript';
import {BundleProgram} from '../../src/packages/bundle_program';
import {EntryPointFormat} from '../../src/packages/entry_point';
import {EntryPointFormat, EntryPointJsonProperty} from '../../src/packages/entry_point';
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
export {getDeclaration} from '../../../ngtsc/testing/in_memory_typescript';
export {getDeclaration} from '../../../src/ngtsc/testing/in_memory_typescript';
/**
*
@ -24,12 +22,17 @@ export {getDeclaration} from '../../../ngtsc/testing/in_memory_typescript';
* @param dtsFiles The typings files to include the bundle.
*/
export function makeTestEntryPointBundle(
format: EntryPointFormat, files: {name: string, contents: string, isRoot?: boolean}[],
formatProperty: EntryPointJsonProperty, format: EntryPointFormat, isCore: boolean,
files: {name: string, contents: string, isRoot?: boolean}[],
dtsFiles?: {name: string, contents: string, isRoot?: boolean}[]): EntryPointBundle {
const src = makeTestBundleProgram(files);
const dts = dtsFiles ? makeTestBundleProgram(dtsFiles) : null;
const isFlat = src.r3SymbolsFile === null;
return {format, rootDirs: [AbsoluteFsPath.fromUnchecked('/')], src, dts, isFlat};
const isFlatCore = isCore && src.r3SymbolsFile === null;
return {
formatProperty,
format,
rootDirs: [AbsoluteFsPath.fromUnchecked('/')], src, dts, isCore, isFlatCore
};
}
/**

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
import {ClassMemberKind, Import, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils';
@ -108,7 +108,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -132,7 +132,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
@ -148,7 +148,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const host = new Esm2015ReflectionHost(true, program.getTypeChecker());
const classNode = getDeclaration(
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -168,7 +168,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -186,7 +186,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
@ -200,7 +200,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticMethod = members.find(member => member.name === 'staticMethod') !;
@ -213,7 +213,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticProperty = members.find(member => member.name === 'staticProperty') !;
@ -230,7 +230,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
host.getMembersOfClass(classNode);
const identifiers = spy.calls.all().map(call => (call.args[0] as ts.Identifier).text);
@ -242,7 +242,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const host = new Esm2015ReflectionHost(true, program.getTypeChecker());
const classNode = getDeclaration(
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -257,7 +257,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toBeDefined();
@ -280,7 +280,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![2].decorators !;
@ -298,7 +298,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const ctrDecorators = host.getConstructorParameters(classNode) !;
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
local: true,
@ -318,7 +318,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
const decoratorNode = classDecorators[0].node;
const identifierOfDirective =
@ -328,7 +328,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => {
const expectedDeclarationNode = getDeclaration(
program, 'node_modules/@angular/core/index.ts', 'Directive',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
import {ClassMemberKind, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
@ -553,14 +553,14 @@ const MODULE_WITH_PROVIDERS_PROGRAM = [
{name: '/src/module', contents: 'export class ExternalModule {}'},
];
describe('Fesm2015ReflectionHost', () => {
describe('Esm2015ReflectionHost', () => {
describe('getDecoratorsOfDeclaration()', () => {
it('should find the decorators on a class', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -578,7 +578,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
const decorators = host.getDecoratorsOfDeclaration(functionNode);
expect(decorators).toBe(null);
});
@ -587,7 +587,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode);
expect(decorators).toBe(null);
});
@ -596,7 +596,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode);
expect(decorators).toEqual([]);
});
@ -605,7 +605,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -616,7 +616,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -627,7 +627,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -641,8 +641,8 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toEqual(1);
@ -657,7 +657,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isClassDeclaration);
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -670,7 +670,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isClassDeclaration);
isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -682,7 +682,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -696,8 +696,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find decorated properties on a class', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -714,8 +714,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find non decorated properties on a class', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
@ -729,7 +729,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(ACCESSORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', ts.isClassDeclaration);
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const [combinedSetter, combinedGetter] =
@ -749,8 +749,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find static methods on a class', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const staticMethod = members.find(member => member.name === 'staticMethod') !;
@ -762,8 +762,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find static properties on a class', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const staticProperty = members.find(member => member.name === 'staticProperty') !;
@ -777,7 +777,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
expect(() => {
host.getMembersOfClass(functionNode);
}).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`);
@ -787,7 +787,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
expect(members).toEqual([]);
@ -798,7 +798,8 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral',
isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
expect(members.map(member => member.name)).not.toContain('prop');
@ -809,7 +810,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp',
ts.isClassDeclaration);
isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -822,7 +823,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -835,7 +836,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -854,8 +855,8 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
expect(spy).toHaveBeenCalled();
@ -878,7 +879,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
ts.isClassDeclaration);
isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -893,7 +894,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isClassDeclaration);
isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -908,7 +909,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
ts.isClassDeclaration);
isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -924,8 +925,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find the decorated constructor parameters', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
expect(parameters).toBeDefined();
@ -939,7 +940,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
expect(() => { host.getConstructorParameters(functionNode); })
.toThrowError(
'Attempted to get constructor parameters of a non-class: "function foo() {}"');
@ -949,7 +950,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toBe(null);
});
@ -958,7 +959,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isClassDeclaration);
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
expect(parameters).toEqual(jasmine.any(Array));
@ -971,7 +972,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toEqual([]);
@ -981,7 +982,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
expect(parameters.length).toBe(1);
@ -995,7 +996,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
expect(parameters.length).toBe(1);
@ -1009,7 +1010,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
expect(parameters.length).toBe(1);
@ -1033,7 +1034,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(file);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isClassDeclaration);
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedClassDeclaration);
return host.getConstructorParameters(classNode);
}
@ -1088,7 +1089,8 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral',
isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters !.length).toBe(2);
@ -1106,7 +1108,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1118,7 +1120,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1134,7 +1136,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode) !;
const decorators = parameters[2].decorators !;
@ -1152,7 +1154,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
ts.isClassDeclaration);
isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters !.length).toBe(1);
const decorators = parameters ![0].decorators !;
@ -1167,7 +1169,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isClassDeclaration);
isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1181,7 +1183,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
ts.isClassDeclaration);
isNamedClassDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1198,7 +1200,7 @@ describe('Fesm2015ReflectionHost', () => {
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const fooNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !;
const fooDef = host.getDefinitionOfFunction(fooNode);
expect(fooDef.node).toBe(fooNode);
expect(fooDef.body !.length).toEqual(1);
@ -1208,7 +1210,7 @@ describe('Fesm2015ReflectionHost', () => {
expect(fooDef.parameters[0].initializer).toBe(null);
const barNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !;
const barDef = host.getDefinitionOfFunction(barNode);
expect(barDef.node).toBe(barNode);
expect(barDef.body !.length).toEqual(1);
@ -1221,7 +1223,7 @@ describe('Fesm2015ReflectionHost', () => {
expect(barDef.parameters[1].initializer !.getText()).toEqual('42');
const bazNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !;
const bazDef = host.getDefinitionOfFunction(bazNode);
expect(bazDef.node).toBe(bazNode);
expect(bazDef.body !.length).toEqual(3);
@ -1230,7 +1232,7 @@ describe('Fesm2015ReflectionHost', () => {
expect(bazDef.parameters[0].initializer).toBe(null);
const quxNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !;
const quxDef = host.getDefinitionOfFunction(quxNode);
expect(quxDef.node).toBe(quxNode);
expect(quxDef.body !.length).toEqual(2);
@ -1239,14 +1241,14 @@ describe('Fesm2015ReflectionHost', () => {
expect(quxDef.parameters[0].initializer).toBe(null);
const mooNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'moo', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'moo', isNamedFunctionDeclaration) !;
const mooDef = host.getDefinitionOfFunction(mooNode);
expect(mooDef.node).toBe(mooNode);
expect(mooDef.body !.length).toEqual(3);
expect(mooDef.parameters).toEqual([]);
const juuNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'juu', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'juu', isNamedFunctionDeclaration) !;
const juuDef = host.getDefinitionOfFunction(juuNode);
expect(juuDef.node).toBe(juuNode);
expect(juuDef.body !.length).toEqual(2);
@ -1259,7 +1261,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'b', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
@ -1269,7 +1271,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'c', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
@ -1279,7 +1281,7 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'd', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toBeNull();
@ -1290,8 +1292,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should return the declaration of a locally defined identifier', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const ctrDecorators = host.getConstructorParameters(classNode) !;
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
local: true,
@ -1300,7 +1302,7 @@ describe('Fesm2015ReflectionHost', () => {
}).expression;
const expectedDeclarationNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
@ -1310,15 +1312,15 @@ describe('Fesm2015ReflectionHost', () => {
it('should return the declaration of an externally defined identifier', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration);
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration);
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression)
.properties[0] as ts.PropertyAssignment)
.initializer as ts.Identifier;
const expectedDeclarationNode = getDeclaration(
program, 'node_modules/@angular/core/index.ts', 'Directive', ts.isVariableDeclaration);
program, 'node_modules/@angular/core/index.ts', 'Directive', isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
@ -1366,14 +1368,15 @@ describe('Fesm2015ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const node =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
expect(host.isClass(node)).toBe(true);
});
it('should return false if a given node is a TS function declaration', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
const node =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
expect(host.isClass(node)).toBe(false);
});
});
@ -1385,13 +1388,13 @@ describe('Fesm2015ReflectionHost', () => {
const dts = makeTestBundleProgram([ARITY_CLASSES[1]]);
const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dts);
const noTypeParamClass =
getDeclaration(program, '/src/class.js', 'NoTypeParam', ts.isClassDeclaration);
getDeclaration(program, '/src/class.js', 'NoTypeParam', isNamedClassDeclaration);
expect(host.getGenericArityOfClass(noTypeParamClass)).toBe(0);
const oneTypeParamClass =
getDeclaration(program, '/src/class.js', 'OneTypeParam', ts.isClassDeclaration);
getDeclaration(program, '/src/class.js', 'OneTypeParam', isNamedClassDeclaration);
expect(host.getGenericArityOfClass(oneTypeParamClass)).toBe(1);
const twoTypeParamsClass =
getDeclaration(program, '/src/class.js', 'TwoTypeParams', ts.isClassDeclaration);
getDeclaration(program, '/src/class.js', 'TwoTypeParams', isNamedClassDeclaration);
expect(host.getGenericArityOfClass(twoTypeParamsClass)).toBe(2);
});
});
@ -1440,7 +1443,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find the dts declaration that has the same relative path to the source file', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
const class1 =
getDeclaration(srcProgram, '/src/class1.js', 'Class1', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
@ -1450,7 +1454,8 @@ describe('Fesm2015ReflectionHost', () => {
it('should find the dts declaration for exported functions', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
const mooFn =
getDeclaration(srcProgram, '/src/func1.js', 'mooFn', isNamedFunctionDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
const dtsDeclaration = host.getDtsDeclaration(mooFn);
@ -1461,7 +1466,7 @@ describe('Fesm2015ReflectionHost', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const missingClass =
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration);
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
@ -1471,7 +1476,7 @@ describe('Fesm2015ReflectionHost', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const missingClass = getDeclaration(
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration);
srcProgram, '/src/missing-class.js', 'MissingClass2', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
@ -1482,7 +1487,7 @@ describe('Fesm2015ReflectionHost', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class1 =
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
@ -1493,7 +1498,7 @@ describe('Fesm2015ReflectionHost', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class3 =
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class3);
@ -1504,8 +1509,8 @@ describe('Fesm2015ReflectionHost', () => {
() => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const internalClass =
getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration);
const internalClass = getDeclaration(
srcProgram, '/src/internal.js', 'InternalClass', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(internalClass);
@ -1517,9 +1522,9 @@ describe('Fesm2015ReflectionHost', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class2 =
getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isClassDeclaration);
getDeclaration(srcProgram, '/src/class2.js', 'Class2', isNamedClassDeclaration);
const internalClass2 =
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration);
getDeclaration(srcProgram, '/src/internal.js', 'Class2', isNamedClassDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const class2DtsDeclaration = host.getDtsDeclaration(class2);
@ -1531,7 +1536,7 @@ describe('Fesm2015ReflectionHost', () => {
});
});
describe('getModuleWithProvidersFunctions', () => {
describe('getModuleWithProvidersFunctions()', () => {
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
() => {
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
import {ClassMemberKind, Import, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils';
@ -123,7 +123,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -147,7 +147,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
@ -163,7 +163,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const host = new Esm5ReflectionHost(true, program.getTypeChecker());
const classNode = getDeclaration(
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -183,7 +183,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -201,7 +201,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
@ -215,7 +215,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticMethod = members.find(member => member.name === 'staticMethod') !;
@ -228,7 +228,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticProperty = members.find(member => member.name === 'staticProperty') !;
@ -245,7 +245,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
host.getMembersOfClass(classNode);
const identifiers = spy.calls.all().map(call => (call.args[0] as ts.Identifier).text);
@ -257,7 +257,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const host = new Esm5ReflectionHost(true, program.getTypeChecker());
const classNode = getDeclaration(
program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -272,7 +272,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toBeDefined();
@ -295,7 +295,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![2].decorators !;
@ -334,7 +334,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const ctrDecorators = host.getConstructorParameters(classNode) !;
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
local: true,
@ -343,7 +343,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
}).expression;
const expectedDeclarationNode = getDeclaration(
program, '/some_directive.js', 'ViewContainerRef', ts.isVariableDeclaration);
program, '/some_directive.js', 'ViewContainerRef', isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
@ -354,7 +354,7 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const program = makeTestProgram(fileSystem.files[0]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration);
program, '/some_directive.js', 'SomeDirective', isNamedVariableDeclaration);
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
const decoratorNode = classDecorators[0].node;
@ -365,12 +365,24 @@ describe('Esm5ReflectionHost [import helper style]', () => {
const expectedDeclarationNode = getDeclaration(
program, 'node_modules/@angular/core/index.ts', 'Directive',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
expect(actualDeclaration !.viaModule).toBe('@angular/core');
});
it('should find the "actual" declaration of an aliased variable identifier', () => {
const program = makeTestProgram(fileSystem.files[2]);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const ngModuleRef = findIdentifier(
program.getSourceFile(fileSystem.files[2].name) !, 'HttpClientXsrfModule_1',
isNgModulePropertyAssignment);
const declaration = host.getDeclarationOfIdentifier(ngModuleRef !);
expect(declaration).not.toBe(null);
expect(declaration !.node.getText()).toContain('function HttpClientXsrfModule()');
});
});
});
@ -415,10 +427,26 @@ describe('Esm5ReflectionHost [import helper style]', () => {
if (!node) {
return;
}
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) &&
node.name.text === variableName) {
if (isNamedVariableDeclaration(node) && node.name.text === variableName) {
return node;
}
return node.forEachChild(node => findVariableDeclaration(node, variableName));
}
});
function findIdentifier(
node: ts.Node | undefined, identifierName: string,
requireFn: (node: ts.Identifier) => boolean): ts.Identifier|undefined {
if (!node) {
return undefined;
}
if (ts.isIdentifier(node) && node.text === identifierName && requireFn(node)) {
return node;
}
return node.forEachChild(node => findIdentifier(node, identifierName, requireFn));
}
function isNgModulePropertyAssignment(identifier: ts.Identifier): boolean {
return ts.isPropertyAssignment(identifier.parent) &&
identifier.parent.name.getText() === 'ngModule';
}

View File

@ -8,10 +8,10 @@
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../ngtsc/reflection';
import {ClassMemberKind, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
import {getDeclaration, makeTestProgram} from '../helpers/utils';
import {Esm5ReflectionHost, getIifeBody} from '../../src/host/esm5_host';
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
import {expectTypeValueReferencesForParameters} from './util';
@ -96,6 +96,13 @@ const ACCESSORS_FILE = {
`,
};
const SIMPLE_ES2015_CLASS_FILE = {
name: '/simple_es2015_class.d.ts',
contents: `
export class EmptyClass {}
`,
};
const SIMPLE_CLASS_FILE = {
name: '/simple_class.js',
contents: `
@ -529,6 +536,172 @@ const UNWANTED_PROTOTYPE_EXPORT_FILE = {
}`
};
const TYPINGS_SRC_FILES = [
{
name: '/src/index.js',
contents:
`import {InternalClass} from './internal'; export * from './class1'; export * from './class2';`
},
{
name: '/src/class1.js',
contents: `
var Class1 = (function() {
function Class1() {}
return Class1;
}());
var MissingClass1 = (function() {
function MissingClass1() {}
return MissingClass1;
}());
export {Class1, MissingClass1};
`
},
{
name: '/src/class2.js',
contents: `
var Class2 = (function() {
function Class2() {}
return Class2;
}());
export {Class2};
`
},
{name: '/src/func1.js', contents: 'function mooFn() {} export {mooFn}'}, {
name: '/src/internal.js',
contents: `
var InternalClass = (function() {
function InternalClass() {}
return InternalClass;
}());
var Class2 = (function() {
function Class2() {}
return Class2;
}());
export {InternalClass, Class2};
`
},
{
name: '/src/missing-class.js',
contents: `
var MissingClass2 = (function() {
function MissingClass2() {}
return MissingClass2;
}());
export {MissingClass2};
`
},
{
name: '/src/flat-file.js',
contents: `
var Class1 = (function() {
function Class1() {}
return Class1;
}());
var MissingClass1 = (function() {
function MissingClass1() {}
return MissingClass1;
}());
var MissingClass2 = (function() {
function MissingClass2() {}
return MissingClass2;
}());
var Class3 = (function() {
function Class3() {}
return Class3;
}());
export {Class1, Class3 as xClass3, MissingClass1, MissingClass2};
`
}
];
const TYPINGS_DTS_FILES = [
{
name: '/typings/index.d.ts',
contents:
`import {InternalClass} from './internal'; export * from './class1'; export * from './class2';`
},
{
name: '/typings/class1.d.ts',
contents: `export declare class Class1 {}\nexport declare class OtherClass {}`
},
{
name: '/typings/class2.d.ts',
contents:
`export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
},
{name: '/typings/func1.d.ts', contents: 'export declare function mooFn(): void;'},
{
name: '/typings/internal.d.ts',
contents: `export declare class InternalClass {}\nexport declare class Class2 {}`
},
{name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
];
const MODULE_WITH_PROVIDERS_PROGRAM = [
{
name: '/src/functions.js',
contents: `
import {ExternalModule} from './module';
var SomeService = (function() {
function SomeService() {}
return SomeService;
}());
var InternalModule = (function() {
function InternalModule() {}
return InternalModule;
}());
export function aNumber() { return 42; }
export function aString() { return 'foo'; }
export function emptyObject() { return {}; }
export function ngModuleIdentifier() { return { ngModule: InternalModule }; }
export function ngModuleWithEmptyProviders() { return { ngModule: InternalModule, providers: [] }; }
export function ngModuleWithProviders() { return { ngModule: InternalModule, providers: [SomeService] }; }
export function onlyProviders() { return { providers: [SomeService] }; }
export function ngModuleNumber() { return { ngModule: 42 }; }
export function ngModuleString() { return { ngModule: 'foo' }; }
export function ngModuleObject() { return { ngModule: { foo: 42 } }; }
export function externalNgModule() { return { ngModule: ExternalModule }; }
export {SomeService, InternalModule};
`
},
{
name: '/src/methods.js',
contents: `
import {ExternalModule} from './module';
var SomeService = (function() {
function SomeService() {}
return SomeService;
}());
var InternalModule = (function() {
function InternalModule() {}
InternalModule.prototype = {
instanceNgModuleIdentifier: function() { return { ngModule: InternalModule }; },
instanceNgModuleWithEmptyProviders: function() { return { ngModule: InternalModule, providers: [] }; },
instanceNgModuleWithProviders: function() { return { ngModule: InternalModule, providers: [SomeService] }; },
instanceExternalNgModule: function() { return { ngModule: ExternalModule }; },
};
InternalModule.aNumber = function() { return 42; };
InternalModule.aString = function() { return 'foo'; };
InternalModule.emptyObject = function() { return {}; };
InternalModule.ngModuleIdentifier = function() { return { ngModule: InternalModule }; };
InternalModule.ngModuleWithEmptyProviders = function() { return { ngModule: InternalModule, providers: [] }; };
InternalModule.ngModuleWithProviders = function() { return { ngModule: InternalModule, providers: [SomeService] }; };
InternalModule.onlyProviders = function() { return { providers: [SomeService] }; };
InternalModule.ngModuleNumber = function() { return { ngModule: 42 }; };
InternalModule.ngModuleString = function() { return { ngModule: 'foo' }; };
InternalModule.ngModuleObject = function() { return { ngModule: { foo: 42 } }; };
InternalModule.externalNgModule = function() { return { ngModule: ExternalModule }; };
return InternalModule;
}());
export {SomeService, InternalModule};
`
},
{name: '/src/module', contents: 'export class ExternalModule {}'},
];
describe('Esm5ReflectionHost', () => {
describe('getDecoratorsOfDeclaration()', () => {
@ -536,7 +709,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators).toBeDefined();
@ -554,7 +727,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
const decorators = host.getDecoratorsOfDeclaration(functionNode);
expect(decorators).toBe(null);
});
@ -563,7 +736,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode);
expect(decorators).toBe(null);
});
@ -572,7 +745,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode);
expect(decorators).toEqual([]);
});
@ -581,7 +754,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isVariableDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -592,7 +765,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -603,7 +776,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -618,7 +791,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toEqual(1);
@ -633,7 +806,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isVariableDeclaration);
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -646,7 +820,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -658,7 +832,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
isNamedVariableDeclaration);
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
expect(decorators.length).toBe(1);
@ -673,7 +848,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const input1 = members.find(member => member.name === 'input1') !;
@ -691,7 +866,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(ACCESSORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
getDeclaration(program, ACCESSORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const setter = members.find(member => member.name === 'setter') !;
@ -738,7 +913,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const instanceProperty = members.find(member => member.name === 'instanceProperty') !;
@ -752,7 +927,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticMethod = members.find(member => member.name === 'staticMethod') !;
@ -766,7 +941,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const staticProperty = members.find(member => member.name === 'staticProperty') !;
@ -780,7 +955,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
expect(() => {
host.getMembersOfClass(functionNode);
}).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`);
@ -790,7 +965,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
expect(members).toEqual([]);
@ -802,7 +977,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
expect(members.map(member => member.name)).not.toContain('prop');
@ -813,7 +988,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -826,7 +1001,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -839,7 +1014,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -858,7 +1033,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
expect(spy).toHaveBeenCalled();
@ -875,7 +1050,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -890,7 +1065,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -905,7 +1080,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const members = host.getMembersOfClass(classNode);
const prop = members.find(m => m.name === 'prop') !;
const decorators = prop.decorators !;
@ -921,18 +1096,18 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(UNWANTED_PROTOTYPE_EXPORT_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, UNWANTED_PROTOTYPE_EXPORT_FILE.name, 'SomeParam', ts.isClassDeclaration);
program, UNWANTED_PROTOTYPE_EXPORT_FILE.name, 'SomeParam', isNamedClassDeclaration);
const members = host.getMembersOfClass(classNode);
expect(members.find(m => m.name === 'prototype')).toBeUndefined();
});
});
describe('getConstructorParameters', () => {
describe('getConstructorParameters()', () => {
it('should find the decorated constructor parameters', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toBeDefined();
@ -950,7 +1125,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const functionNode =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
expect(() => { host.getConstructorParameters(functionNode); })
.toThrowError(
'Attempted to get constructor parameters of a non-class: "function foo() {}"');
@ -963,7 +1138,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isVariableDeclaration);
program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass',
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toEqual(jasmine.any(Array));
@ -976,7 +1152,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isVariableDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters).toEqual([]);
@ -989,7 +1165,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral',
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters !.length).toBe(1);
@ -1005,7 +1182,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters !.length).toBe(2);
@ -1023,7 +1200,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty',
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1035,7 +1213,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration);
program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier',
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1051,7 +1230,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![2].decorators !;
@ -1078,7 +1257,8 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(file);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isVariableDeclaration);
const classNode =
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
return host.getConstructorParameters(classNode);
}
@ -1147,7 +1327,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
expect(parameters !.length).toBe(1);
const decorators = parameters ![0].decorators !;
@ -1162,7 +1342,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1176,7 +1356,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral',
ts.isVariableDeclaration);
isNamedVariableDeclaration);
const parameters = host.getConstructorParameters(classNode);
const decorators = parameters ![0].decorators !;
@ -1193,7 +1373,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const fooNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !;
const fooDef = host.getDefinitionOfFunction(fooNode);
expect(fooDef.node).toBe(fooNode);
expect(fooDef.body !.length).toEqual(1);
@ -1203,7 +1383,7 @@ describe('Esm5ReflectionHost', () => {
expect(fooDef.parameters[0].initializer).toBe(null);
const barNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !;
const barDef = host.getDefinitionOfFunction(barNode);
expect(barDef.node).toBe(barNode);
expect(barDef.body !.length).toEqual(1);
@ -1216,7 +1396,7 @@ describe('Esm5ReflectionHost', () => {
expect(barDef.parameters[1].initializer !.getText()).toEqual('42');
const bazNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !;
const bazDef = host.getDefinitionOfFunction(bazNode);
expect(bazDef.node).toBe(bazNode);
expect(bazDef.body !.length).toEqual(3);
@ -1225,7 +1405,7 @@ describe('Esm5ReflectionHost', () => {
expect(bazDef.parameters[0].initializer).toBe(null);
const quxNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', ts.isFunctionDeclaration) !;
getDeclaration(program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !;
const quxDef = host.getDefinitionOfFunction(quxNode);
expect(quxDef.node).toBe(quxNode);
expect(quxDef.body !.length).toEqual(2);
@ -1235,12 +1415,12 @@ describe('Esm5ReflectionHost', () => {
});
});
describe('getImportOfIdentifier', () => {
describe('getImportOfIdentifier()', () => {
it('should find the import of an identifier', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'b', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
@ -1250,7 +1430,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'c', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toEqual({name: 'a', from: './a.js'});
@ -1260,19 +1440,19 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(...IMPORTS_FILES);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const variableNode =
getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration);
getDeclaration(program, IMPORTS_FILES[1].name, 'd', isNamedVariableDeclaration);
const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier);
expect(importOfIdent).toBeNull();
});
});
describe('getDeclarationOfIdentifier', () => {
describe('getDeclarationOfIdentifier()', () => {
it('should return the declaration of a locally defined identifier', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const ctrDecorators = host.getConstructorParameters(classNode) !;
const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{
local: true,
@ -1281,7 +1461,7 @@ describe('Esm5ReflectionHost', () => {
}).expression;
const expectedDeclarationNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
@ -1292,14 +1472,14 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(SOME_DIRECTIVE_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration);
program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration);
const classDecorators = host.getDecoratorsOfDeclaration(classNode) !;
const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression)
.properties[0] as ts.PropertyAssignment)
.initializer as ts.Identifier;
const expectedDeclarationNode = getDeclaration(
program, 'node_modules/@angular/core/index.ts', 'Directive', ts.isVariableDeclaration);
program, 'node_modules/@angular/core/index.ts', 'Directive', isNamedVariableDeclaration);
const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective);
expect(actualDeclaration).not.toBe(null);
expect(actualDeclaration !.node).toBe(expectedDeclarationNode);
@ -1314,7 +1494,7 @@ describe('Esm5ReflectionHost', () => {
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const outerDeclaration = getDeclaration(
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerDeclaration = (((outerDeclaration.initializer as ts.ParenthesizedExpression)
.expression as ts.CallExpression)
.expression as ts.FunctionExpression)
@ -1377,105 +1557,97 @@ describe('Esm5ReflectionHost', () => {
});
describe('getClassSymbol()', () => {
let superGetClassSymbolSpy: jasmine.Spy;
it('should return the class symbol for an ES2015 class', () => {
const program = makeTestProgram(SIMPLE_ES2015_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const node = getDeclaration(
program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration);
const classSymbol = host.getClassSymbol(node);
beforeEach(() => {
superGetClassSymbolSpy = spyOn(Esm2015ReflectionHost.prototype, 'getClassSymbol');
});
it('should return the class symbol returned by the superclass (if any)', () => {
const mockNode = {} as ts.Node;
const mockSymbol = {} as ts.Symbol;
superGetClassSymbolSpy.and.returnValue(mockSymbol);
const host = new Esm5ReflectionHost(false, {} as any);
expect(host.getClassSymbol(mockNode)).toBe(mockSymbol);
expect(superGetClassSymbolSpy).toHaveBeenCalledWith(mockNode);
expect(classSymbol).toBeDefined();
expect(classSymbol !.valueDeclaration).toBe(node);
});
it('should return the class symbol for an ES5 class (outer variable declaration)', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const node =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
expect(host.getClassSymbol(node)).toBeDefined();
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const classSymbol = host.getClassSymbol(node);
expect(classSymbol).toBeDefined();
expect(classSymbol !.valueDeclaration).toBe(node);
});
it('should return the class symbol for an ES5 class (inner function declaration)', () => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const outerNode =
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
const innerNode =
(((outerNode.initializer as ts.ParenthesizedExpression).expression as ts.CallExpression)
.expression as ts.FunctionExpression)
.body.statements.find(ts.isFunctionDeclaration) !;
getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !;
const classSymbol = host.getClassSymbol(innerNode);
expect(host.getClassSymbol(innerNode)).toBeDefined();
expect(classSymbol).toBeDefined();
expect(classSymbol !.valueDeclaration).toBe(outerNode);
});
it('should return the same class symbol (of the inner declaration) for outer and inner declarations',
it('should return the same class symbol (of the outer declaration) for outer and inner declarations',
() => {
const program = makeTestProgram(SIMPLE_CLASS_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const outerNode = getDeclaration(
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
const innerNode = (((outerNode.initializer as ts.ParenthesizedExpression)
.expression as ts.CallExpression)
.expression as ts.FunctionExpression)
.body.statements.find(ts.isFunctionDeclaration) !;
program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !;
expect(host.getClassSymbol(innerNode)).toBe(host.getClassSymbol(outerNode));
expect(host.getClassSymbol(innerNode) !.valueDeclaration).toBe(innerNode);
});
it('should return undefined if node is not an ES5 class', () => {
const program = makeTestProgram(FOO_FUNCTION_FILE);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration);
expect(host.getClassSymbol(node)).toBeUndefined();
const node =
getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration);
const classSymbol = host.getClassSymbol(node);
expect(classSymbol).toBeUndefined();
});
});
describe('isClass()', () => {
let host: Esm5ReflectionHost;
let mockNode: ts.Node;
let superIsClassSpy: jasmine.Spy;
let getClassSymbolSpy: jasmine.Spy;
let getClassDeclarationSpy: jasmine.Spy;
let superGetClassDeclarationSpy: jasmine.Spy;
beforeEach(() => {
host = new Esm5ReflectionHost(false, null as any);
mockNode = {} as any;
superIsClassSpy = spyOn(Esm2015ReflectionHost.prototype, 'isClass');
getClassSymbolSpy = spyOn(Esm5ReflectionHost.prototype, 'getClassSymbol');
getClassDeclarationSpy = spyOn(Esm5ReflectionHost.prototype, 'getClassDeclaration');
superGetClassDeclarationSpy = spyOn(Esm2015ReflectionHost.prototype, 'getClassDeclaration');
});
it('should return true if superclass returns true', () => {
superIsClassSpy.and.returnValue(true);
superGetClassDeclarationSpy.and.returnValue(true);
getClassDeclarationSpy.and.callThrough();
expect(host.isClass(mockNode)).toBe(true);
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
expect(getClassSymbolSpy).not.toHaveBeenCalled();
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
expect(superGetClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
});
it('should return true if it can find a symbol for the class', () => {
superIsClassSpy.and.returnValue(false);
getClassSymbolSpy.and.returnValue(true);
it('should return true if it can find a declaration for the class', () => {
getClassDeclarationSpy.and.returnValue(true);
expect(host.isClass(mockNode)).toBe(true);
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
expect(getClassSymbolSpy).toHaveBeenCalledWith(mockNode);
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
});
it('should return false if it cannot find a symbol for the class', () => {
superIsClassSpy.and.returnValue(false);
getClassSymbolSpy.and.returnValue(false);
it('should return false if it cannot find a declaration for the class', () => {
getClassDeclarationSpy.and.returnValue(false);
expect(host.isClass(mockNode)).toBe(false);
expect(superIsClassSpy).toHaveBeenCalledWith(mockNode);
expect(getClassSymbolSpy).toHaveBeenCalledWith(mockNode);
expect(getClassDeclarationSpy).toHaveBeenCalledWith(mockNode);
});
});
@ -1488,7 +1660,7 @@ describe('Esm5ReflectionHost', () => {
const program = makeTestProgram(file);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const classNode = getDeclaration(program, file.name, 'TestClass', ts.isVariableDeclaration);
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
return host.hasBaseClass(classNode);
}
@ -1547,4 +1719,142 @@ describe('Esm5ReflectionHost', () => {
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
});
});
describe('getDtsDeclarationsOfClass()', () => {
it('should find the dts declaration that has the same relative path to the source file', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class1 =
getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
it('should find the dts declaration for exported functions', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dtsProgram = makeTestBundleProgram(TYPINGS_DTS_FILES);
const mooFn = getDeclaration(srcProgram, '/src/func1.js', 'mooFn', ts.isFunctionDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dtsProgram);
const dtsDeclaration = host.getDtsDeclaration(mooFn);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/func1.d.ts');
});
it('should return null if there is no matching class in the matching dts file', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const missingClass =
getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
});
it('should return null if there is no matching dts file', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const missingClass = getDeclaration(
srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
expect(host.getDtsDeclaration(missingClass)).toBe(null);
});
it('should find the dts file that contains a matching class declaration, even if the source files do not match',
() => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class1 =
getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class1);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
});
it('should find aliased exports', () => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class3 =
getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(class3);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
});
it('should find the dts file that contains a matching class declaration, even if the class is not publicly exported',
() => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const internalClass = getDeclaration(
srcProgram, '/src/internal.js', 'InternalClass', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const dtsDeclaration = host.getDtsDeclaration(internalClass);
expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts');
});
it('should prefer the publicly exported class if there are multiple classes with the same name',
() => {
const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES);
const dts = makeTestBundleProgram(TYPINGS_DTS_FILES);
const class2 =
getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isVariableDeclaration);
const internalClass2 =
getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isVariableDeclaration);
const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts);
const class2DtsDeclaration = host.getDtsDeclaration(class2);
expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts');
const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2);
expect(internalClass2DtsDeclaration !.getSourceFile().fileName)
.toEqual('/typings/class2.d.ts');
});
});
describe('getModuleWithProvidersFunctions', () => {
it('should find every exported function that returns an object that looks like a ModuleWithProviders object',
() => {
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
const host = new Esm5ReflectionHost(false, srcProgram.getTypeChecker());
const file = srcProgram.getSourceFile('/src/functions.js') !;
const fns = host.getModuleWithProvidersFunctions(file);
expect(fns.map(info => [info.declaration.name !.getText(), info.ngModule.text])).toEqual([
['ngModuleIdentifier', 'InternalModule'],
['ngModuleWithEmptyProviders', 'InternalModule'],
['ngModuleWithProviders', 'InternalModule'],
['externalNgModule', 'ExternalModule'],
]);
});
it('should find every static method on exported classes that return an object that looks like a ModuleWithProviders object',
() => {
const srcProgram = makeTestProgram(...MODULE_WITH_PROVIDERS_PROGRAM);
const host = new Esm5ReflectionHost(false, srcProgram.getTypeChecker());
const file = srcProgram.getSourceFile('/src/methods.js') !;
const fn = host.getModuleWithProvidersFunctions(file);
expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.text])).toEqual([
[
'function() { return { ngModule: InternalModule }; }',
'InternalModule',
],
[
'function() { return { ngModule: InternalModule, providers: [] }; }',
'InternalModule',
],
[
'function() { return { ngModule: InternalModule, providers: [SomeService] }; }',
'InternalModule',
],
[
'function() { return { ngModule: ExternalModule }; }',
'ExternalModule',
],
]);
});
});
});

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript';
import {CtorParameter} from '../../../ngtsc/reflection';
import {CtorParameter} from '../../../src/ngtsc/reflection';
/**
* Check that a given list of `CtorParameter`s has `typeValueReference`s of specific `ts.Identifier`

View File

@ -0,0 +1,273 @@
/**
* @license
* Copyright Google Inc. 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 {existsSync, readFileSync, readdirSync, statSync} from 'fs';
import * as mockFs from 'mock-fs';
import {join} from 'path';
const Module = require('module');
import {mainNgcc} from '../../src/main';
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers';
import {EntryPointPackageJson} from '../../src/packages/entry_point';
describe('ngcc main()', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
it('should run ngcc without errors for esm2015', () => {
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']}))
.not.toThrow();
});
it('should run ngcc without errors for esm5', () => {
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm5']}))
.not.toThrow();
});
describe('with targetEntryPointPath', () => {
it('should only compile the given package entry-point (and its dependencies).', () => {
mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http'});
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `common` is a dependency of `common/http`, so is compiled.
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `core` is a dependency of `common`, so is compiled.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `common/testing` is not a dependency of `common/http` so is not compiled.
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined();
});
it('should mark a non-Angular package target as processed', () => {
mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'});
// `test-package` has no Angular but is marked as processed.
expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({
es2015: '0.0.0-PLACEHOLDER',
});
// * `core` is a dependency of `test-package`, but it is not processed, since test-package
// was not processed.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
});
});
describe('with propertiesToConsider', () => {
it('should only compile the entry-point formats given in the `propertiesToConsider` list',
() => {
mainNgcc({
basePath: '/node_modules',
propertiesToConsider: ['main', 'esm5', 'module', 'fesm5']
});
// * the `main` property is UMD, which is not yet supported.
// * none of the ES2015 formats are compiled as they are not on the `propertiesToConsider`
// list.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
});
});
describe('with compileAllFormats set to false', () => {
it('should only compile the first matching format', () => {
mainNgcc({
basePath: '/node_modules',
propertiesToConsider: ['main', 'module', 'fesm5', 'esm5'],
compileAllFormats: false
});
// * The `main` is UMD, which is not yet supported, and so is not compiled.
// * In the Angular packages fesm5 and module have the same underlying format,
// so both are marked as compiled.
// * The `esm5` is not compiled because we stopped after the `fesm5` format.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
});
});
describe('with createNewEntryPointFormats', () => {
it('should create new files rather than overwriting the originals', () => {
const ANGULAR_CORE_IMPORT_REGEX = /import \* as ɵngcc\d+ from '@angular\/core';/;
mainNgcc({
basePath: '/node_modules',
createNewEntryPointFormats: true,
propertiesToConsider: ['esm5']
});
// Updates the package.json
expect(loadPackage('@angular/common').esm5).toEqual('./esm5/common.js');
expect((loadPackage('@angular/common') as any).esm5_ivy_ngcc)
.toEqual('__ivy_ngcc__/esm5/common.js');
// Doesn't touch original files
expect(readFileSync(`/node_modules/@angular/common/esm5/src/common_module.js`, 'utf8'))
.not.toMatch(ANGULAR_CORE_IMPORT_REGEX);
// Or create a backup of the original
expect(existsSync(`/node_modules/@angular/common/esm5/src/common_module.js.__ivy_ngcc_bak`))
.toBe(false);
// Creates new files
expect(readFileSync(
`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/common_module.js`, 'utf8'))
.toMatch(ANGULAR_CORE_IMPORT_REGEX);
// Copies over files (unchanged) that did not need compiling
expect(existsSync(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`));
expect(readFileSync(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`, 'utf8'))
.toEqual(readFileSync(`/node_modules/@angular/common/esm5/src/version.js`, 'utf8'));
// Overwrites .d.ts files (as usual)
expect(readFileSync(`/node_modules/@angular/common/common.d.ts`, 'utf8'))
.toMatch(ANGULAR_CORE_IMPORT_REGEX);
expect(existsSync(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`)).toBe(true);
});
});
});
function createMockFileSystem() {
mockFs({
'/node_modules/@angular': loadAngularPackages(),
'/node_modules/rxjs': loadDirectory(resolveNpmTreeArtifact('rxjs', 'index.js')),
'/node_modules/tslib': loadDirectory(resolveNpmTreeArtifact('tslib', 'tslib.js')),
'/node_modules/test-package': {
'package.json': '{"name": "test-package", "es2015": "./index.js", "typings": "./index.d.ts"}',
'index.js':
'import {AppModule} from "@angular/common"; export class MyApp extends AppModule;',
'index.d.s':
'import {AppModule} from "@angular/common"; export declare class MyApp extends AppModule;',
}
});
spyOn(Module, '_resolveFilename').and.callFake(mockResolve);
}
function restoreRealFileSystem() {
mockFs.restore();
}
/** Load the built Angular packages into an in-memory structure. */
function loadAngularPackages(): Directory {
const packagesDirectory: Directory = {};
getAngularPackagesFromRunfiles().forEach(
({name, pkgPath}) => { packagesDirectory[name] = loadDirectory(pkgPath); });
return packagesDirectory;
}
/**
* Load real files from the filesystem into an "in-memory" structure,
* which can be used with `mock-fs`.
* @param directoryPath the path to the directory we want to load.
*/
function loadDirectory(directoryPath: string): Directory {
const directory: Directory = {};
readdirSync(directoryPath).forEach(item => {
const itemPath = join(directoryPath, item);
if (statSync(itemPath).isDirectory()) {
directory[item] = loadDirectory(itemPath);
} else {
directory[item] = readFileSync(itemPath, 'utf-8');
}
});
return directory;
}
interface Directory {
[pathSegment: string]: string|Directory;
}
/**
* A mock implementation of the node.js Module._resolveFilename function,
* which we are spying on to support mocking out the file-system in these tests.
*
* @param request the path to a module that needs resolving.
*/
function mockResolve(request: string): string|null {
if (existsSync(request)) {
const stat = statSync(request);
if (stat.isFile()) {
return request;
} else if (stat.isDirectory()) {
const pIndex = mockResolve(request + '/index');
if (pIndex && existsSync(pIndex)) {
return pIndex;
}
}
}
for (const ext of ['.js', '.d.ts']) {
if (existsSync(request + ext)) {
return request + ext;
}
}
if (request.indexOf('/node_modules') === 0) {
// We already tried adding node_modules so give up.
return null;
} else {
return mockResolve(join('/node_modules', request));
}
}
function loadPackage(packageName: string): EntryPointPackageJson {
return JSON.parse(readFileSync(`/node_modules/${packageName}/package.json`, 'utf8'));
}

View File

@ -0,0 +1,180 @@
/**
* @license
* Copyright Google Inc. 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 {readFileSync, writeFileSync} from 'fs';
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
import {EntryPoint} from '../../src/packages/entry_point';
function createMockFileSystem() {
mockFs({
'/node_modules/@angular/common': {
'package.json': `{
"fesm2015": "./fesm2015/common.js",
"fesm5": "./fesm5/common.js",
"typings": "./common.d.ts"
}`,
'fesm2015': {
'common.js': 'DUMMY CONTENT',
'http.js': 'DUMMY CONTENT',
'http/testing.js': 'DUMMY CONTENT',
'testing.js': 'DUMMY CONTENT',
},
'http': {
'package.json': `{
"fesm2015": "../fesm2015/http.js",
"fesm5": "../fesm5/http.js",
"typings": "./http.d.ts"
}`,
'testing': {
'package.json': `{
"fesm2015": "../../fesm2015/http/testing.js",
"fesm5": "../../fesm5/http/testing.js",
"typings": "../http/testing.d.ts"
}`,
},
},
'other': {
'package.json': '{ }',
},
'testing': {
'package.json': `{
"fesm2015": "../fesm2015/testing.js",
"fesm5": "../fesm5/testing.js",
"typings": "../testing.d.ts"
}`,
},
'node_modules': {
'tslib': {
'package.json': '{ }',
'node_modules': {
'other-lib': {
'package.json': '{ }',
},
},
},
},
},
'/node_modules/@angular/no-typings': {
'package.json': `{
"fesm2015": "./fesm2015/index.js"
}`,
'fesm2015': {
'index.js': 'DUMMY CONTENT',
'index.d.ts': 'DUMMY CONTENT',
},
},
'/node_modules/@angular/other': {
'not-package.json': '{ "fesm2015": "./fesm2015/other.js" }',
'package.jsonot': '{ "fesm5": "./fesm5/other.js" }',
},
'/node_modules/@angular/other2': {
'node_modules_not': {
'lib1': {
'package.json': '{ }',
},
},
'not_node_modules': {
'lib2': {
'package.json': '{ }',
},
},
},
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
describe('Marker files', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
const COMMON_PACKAGE_PATH = AbsoluteFsPath.from('/node_modules/@angular/common/package.json');
describe('markAsProcessed', () => {
it('should write a property in the package.json containing the version placeholder', () => {
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'esm5');
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
expect(pkg.__processed_by_ivy_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER');
});
it('should update the packageJson object in-place', () => {
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
});
});
describe('hasBeenProcessed', () => {
it('should return true if the marker exists for the given format property', () => {
expect(hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'fesm2015'))
.toBe(true);
});
it('should return false if the marker does not exist for the given format property', () => {
expect(hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'module'))
.toBe(false);
});
it('should return false if no markers exist',
() => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
it('should throw an Error if the format has been compiled with a different version.', () => {
expect(
() => hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
});
it('should throw an Error if any format has been compiled with a different version.', () => {
expect(
() => hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
expect(
() => hasBeenProcessed(
{
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'module'))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
expect(
() => hasBeenProcessed(
{
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'fesm2015'))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
});
});
});

View File

@ -9,6 +9,8 @@
import * as path from 'canonical-path';
import * as mockFs from 'mock-fs';
import * as ts from 'typescript';
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
import {DependencyHost} from '../../src/packages/dependency_host';
const Module = require('module');
@ -16,6 +18,8 @@ interface DepMap {
[path: string]: {resolved: string[], missing: string[]};
}
const _ = AbsoluteFsPath.from;
describe('DependencyHost', () => {
let host: DependencyHost;
beforeEach(() => host = new DependencyHost());
@ -27,7 +31,8 @@ describe('DependencyHost', () => {
it('should not generate a TS AST if the source does not contain any imports or re-exports',
() => {
spyOn(ts, 'createSourceFile');
host.computeDependencies('/no/imports/or/re-exports.js', new Set(), new Set(), new Set());
host.computeDependencies(
_('/no/imports/or/re-exports.js'), new Set(), new Set(), new Set());
expect(ts.createSourceFile).not.toHaveBeenCalled();
});
@ -37,7 +42,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/external/imports.js', resolved, missing, deepImports);
host.computeDependencies(_('/external/imports.js'), resolved, missing, deepImports);
expect(resolved.size).toBe(2);
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
@ -49,7 +54,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/external/re-exports.js', resolved, missing, deepImports);
host.computeDependencies(_('/external/re-exports.js'), resolved, missing, deepImports);
expect(resolved.size).toBe(2);
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
@ -64,7 +69,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/external/imports-missing.js', resolved, missing, deepImports);
host.computeDependencies(_('/external/imports-missing.js'), resolved, missing, deepImports);
expect(resolved.size).toBe(1);
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
expect(missing.size).toBe(1);
@ -84,7 +89,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/external/deep-import.js', resolved, missing, deepImports);
host.computeDependencies(_('/external/deep-import.js'), resolved, missing, deepImports);
expect(resolved.size).toBe(0);
expect(missing.size).toBe(0);
expect(deepImports.size).toBe(1);
@ -101,7 +106,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/internal/outer.js', resolved, missing, deepImports);
host.computeDependencies(_('/internal/outer.js'), resolved, missing, deepImports);
expect(getDependenciesSpy)
.toHaveBeenCalledWith('/internal/outer.js', resolved, missing, deepImports);
expect(getDependenciesSpy)
@ -121,7 +126,7 @@ describe('DependencyHost', () => {
const resolved = new Set();
const missing = new Set();
const deepImports = new Set();
host.computeDependencies('/internal/circular-a.js', resolved, missing, deepImports);
host.computeDependencies(_('/internal/circular-a.js'), resolved, missing, deepImports);
expect(resolved.size).toBe(2);
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
@ -144,14 +149,15 @@ describe('DependencyHost', () => {
describe('resolveInternal', () => {
it('should resolve the dependency via `Module._resolveFilename`', () => {
spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
const result = host.resolveInternal('/SOURCE/PATH/FILE', '../TARGET/PATH/FILE');
expect(result).toEqual('RESOLVED_PATH');
spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
const result = host.resolveInternal(
_('/SOURCE/PATH/FILE'), PathSegment.fromFsPath('../TARGET/PATH/FILE'));
expect(result).toEqual('/RESOLVED_PATH');
});
it('should first resolve the `to` on top of the `from` directory', () => {
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
host.resolveInternal('/SOURCE/PATH/FILE', '../TARGET/PATH/FILE');
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
host.resolveInternal(_('/SOURCE/PATH/FILE'), PathSegment.fromFsPath('../TARGET/PATH/FILE'));
expect(resolveSpy)
.toHaveBeenCalledWith('/SOURCE/TARGET/PATH/FILE', jasmine.any(Object), false, undefined);
});
@ -159,37 +165,39 @@ describe('DependencyHost', () => {
describe('tryResolveExternal', () => {
it('should call `tryResolve`, appending `package.json` to the target path', () => {
const tryResolveSpy = spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH');
expect(tryResolveSpy).toHaveBeenCalledWith('SOURCE_PATH', 'TARGET_PATH/package.json');
const tryResolveSpy = spyOn(host, 'tryResolve').and.returnValue('/PATH/TO/RESOLVED');
host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
expect(tryResolveSpy).toHaveBeenCalledWith('/SOURCE_PATH', 'TARGET_PATH/package.json');
});
it('should return the directory containing the result from `tryResolve', () => {
spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual('PATH/TO');
spyOn(host, 'tryResolve').and.returnValue('/PATH/TO/RESOLVED');
expect(host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH')))
.toEqual(_('/PATH/TO'));
});
it('should return null if `tryResolve` returns null', () => {
spyOn(host, 'tryResolve').and.returnValue(null);
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual(null);
expect(host.tryResolveEntryPoint(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH')))
.toEqual(null);
});
});
describe('tryResolve()', () => {
it('should resolve the dependency via `Module._resolveFilename`, passing the `from` path to the `paths` option',
() => {
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('RESOLVED_PATH');
const result = host.tryResolve('SOURCE_PATH', 'TARGET_PATH');
const resolveSpy = spyOn(Module, '_resolveFilename').and.returnValue('/RESOLVED_PATH');
const result = host.tryResolve(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
expect(resolveSpy).toHaveBeenCalledWith('TARGET_PATH', jasmine.any(Object), false, {
paths: ['SOURCE_PATH']
paths: ['/SOURCE_PATH']
});
expect(result).toEqual('RESOLVED_PATH');
expect(result).toEqual(_('/RESOLVED_PATH'));
});
it('should return null if `Module._resolveFilename` throws an error', () => {
const resolveSpy =
spyOn(Module, '_resolveFilename').and.throwError(`Cannot find module 'TARGET_PATH'`);
const result = host.tryResolve('SOURCE_PATH', 'TARGET_PATH');
const result = host.tryResolve(_('/SOURCE_PATH'), PathSegment.fromFsPath('TARGET_PATH'));
expect(result).toBe(null);
});
});

View File

@ -0,0 +1,135 @@
/**
* @license
* Copyright Google Inc. 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 {resolve} from 'canonical-path';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyHost} from '../../src/packages/dependency_host';
import {DependencyResolver, SortedEntryPointsInfo} from '../../src/packages/dependency_resolver';
import {EntryPoint} from '../../src/packages/entry_point';
const _ = AbsoluteFsPath.from;
describe('DependencyResolver', () => {
let host: DependencyHost;
let resolver: DependencyResolver;
beforeEach(() => {
host = new DependencyHost();
resolver = new DependencyResolver(host);
});
describe('sortEntryPointsByDependency()', () => {
const first = { path: _('/first'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
const second = { path: _('/second'), packageJson: {esm2015: 'sub/index.ts'} } as EntryPoint;
const third = { path: _('/third'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
const fourth = { path: _('/fourth'), packageJson: {esm2015: 'sub2/index.ts'} } as EntryPoint;
const fifth = { path: _('/fifth'), packageJson: {esm5: 'index.ts'} } as EntryPoint;
const dependencies = {
[_('/first/index.ts')]: {resolved: [second.path, third.path, '/ignored-1'], missing: []},
[_('/second/sub/index.ts')]: {resolved: [third.path, fifth.path], missing: []},
[_('/third/index.ts')]: {resolved: [fourth.path, '/ignored-2'], missing: []},
[_('/fourth/sub2/index.ts')]: {resolved: [fifth.path], missing: []},
[_('/fifth/index.ts')]: {resolved: [], missing: []},
};
it('should order the entry points by their dependency on each other', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]);
});
it('should remove entry-points that have missing direct dependencies', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.ts')]: {resolved: [], missing: ['/missing']},
[_('/second/sub/index.ts')]: {resolved: [], missing: []},
}));
const result = resolver.sortEntryPointsByDependency([first, second]);
expect(result.entryPoints).toEqual([second]);
expect(result.invalidEntryPoints).toEqual([
{entryPoint: first, missingDependencies: ['/missing']},
]);
});
it('should remove entry points that depended upon an invalid entry-point', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.ts')]: {resolved: [second.path], missing: []},
[_('/second/sub/index.ts')]: {resolved: [], missing: ['/missing']},
[_('/third/index.ts')]: {resolved: [], missing: []},
}));
// Note that we will process `first` before `second`, which has the missing dependency.
const result = resolver.sortEntryPointsByDependency([first, second, third]);
expect(result.entryPoints).toEqual([third]);
expect(result.invalidEntryPoints).toEqual([
{entryPoint: second, missingDependencies: ['/missing']},
{entryPoint: first, missingDependencies: ['/missing']},
]);
});
it('should remove entry points that will depend upon an invalid entry-point', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.ts')]: {resolved: [second.path], missing: []},
[_('/second/sub/index.ts')]: {resolved: [], missing: ['/missing']},
[_('/third/index.ts')]: {resolved: [], missing: []},
}));
// Note that we will process `first` after `second`, which has the missing dependency.
const result = resolver.sortEntryPointsByDependency([second, first, third]);
expect(result.entryPoints).toEqual([third]);
expect(result.invalidEntryPoints).toEqual([
{entryPoint: second, missingDependencies: ['/missing']},
{entryPoint: first, missingDependencies: [second.path]},
]);
});
it('should error if the entry point does not have either the esm5 nor esm2015 formats', () => {
expect(() => resolver.sortEntryPointsByDependency([
{ path: '/first', packageJson: {} } as EntryPoint
])).toThrowError(`There is no format with import statements in '/first' entry-point.`);
});
it('should capture any dependencies that were ignored', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.ignoredDependencies).toEqual([
{entryPoint: first, dependencyPath: '/ignored-1'},
{entryPoint: third, dependencyPath: '/ignored-2'},
]);
});
it('should only return dependencies of the target, if provided', () => {
spyOn(host, 'computeDependencies').and.callFake(createFakeComputeDependencies(dependencies));
const entryPoints = [fifth, first, fourth, second, third];
let sorted: SortedEntryPointsInfo;
sorted = resolver.sortEntryPointsByDependency(entryPoints, first);
expect(sorted.entryPoints).toEqual([fifth, fourth, third, second, first]);
sorted = resolver.sortEntryPointsByDependency(entryPoints, second);
expect(sorted.entryPoints).toEqual([fifth, fourth, third, second]);
sorted = resolver.sortEntryPointsByDependency(entryPoints, third);
expect(sorted.entryPoints).toEqual([fifth, fourth, third]);
sorted = resolver.sortEntryPointsByDependency(entryPoints, fourth);
expect(sorted.entryPoints).toEqual([fifth, fourth]);
sorted = resolver.sortEntryPointsByDependency(entryPoints, fifth);
expect(sorted.entryPoints).toEqual([fifth]);
});
interface DepMap {
[path: string]: {resolved: string[], missing: string[]};
}
function createFakeComputeDependencies(deps: DepMap) {
return (entryPoint: string) => {
const dependencies = new Set();
const missing = new Set();
const deepImports = new Set();
deps[entryPoint].resolved.forEach(dep => dependencies.add(dep));
deps[entryPoint].missing.forEach(dep => missing.add(dep));
return {dependencies, missing, deepImports};
};
}
});
});

View File

@ -7,11 +7,15 @@
*/
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyHost} from '../../src/packages/dependency_host';
import {DependencyResolver} from '../../src/packages/dependency_resolver';
import {EntryPoint} from '../../src/packages/entry_point';
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
const _ = AbsoluteFsPath.from;
describe('findEntryPoints()', () => {
let resolver: DependencyResolver;
let finder: EntryPointFinder;
@ -25,57 +29,57 @@ describe('findEntryPoints()', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
it('should find sub-entry-points within a package', () => {
const {entryPoints} = finder.findEntryPoints('/sub_entry_points');
it('should find sub-entry-points within a package', () => {
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
expect(entryPointPaths).toEqual([
['/sub_entry_points/common', '/sub_entry_points/common'],
['/sub_entry_points/common', '/sub_entry_points/common/http'],
['/sub_entry_points/common', '/sub_entry_points/common/http/testing'],
['/sub_entry_points/common', '/sub_entry_points/common/testing'],
[_('/sub_entry_points/common'), _('/sub_entry_points/common')],
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http')],
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http/testing')],
[_('/sub_entry_points/common'), _('/sub_entry_points/common/testing')],
]);
});
it('should find packages inside a namespace', () => {
const {entryPoints} = finder.findEntryPoints('/namespaced');
const {entryPoints} = finder.findEntryPoints(_('/namespaced'));
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
expect(entryPointPaths).toEqual([
['/namespaced/@angular/common', '/namespaced/@angular/common'],
['/namespaced/@angular/common', '/namespaced/@angular/common/http'],
['/namespaced/@angular/common', '/namespaced/@angular/common/http/testing'],
['/namespaced/@angular/common', '/namespaced/@angular/common/testing'],
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common')],
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http')],
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http/testing')],
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/testing')],
]);
});
it('should return an empty array if there are no packages', () => {
const {entryPoints} = finder.findEntryPoints('/no_packages');
const {entryPoints} = finder.findEntryPoints(_('/no_packages'));
expect(entryPoints).toEqual([]);
});
it('should return an empty array if there are no valid entry-points', () => {
const {entryPoints} = finder.findEntryPoints('/no_valid_entry_points');
const {entryPoints} = finder.findEntryPoints(_('/no_valid_entry_points'));
expect(entryPoints).toEqual([]);
});
it('should ignore folders starting with .', () => {
const {entryPoints} = finder.findEntryPoints('/dotted_folders');
const {entryPoints} = finder.findEntryPoints(_('/dotted_folders'));
expect(entryPoints).toEqual([]);
});
it('should ignore folders that are symlinked', () => {
const {entryPoints} = finder.findEntryPoints('/symlinked_folders');
const {entryPoints} = finder.findEntryPoints(_('/symlinked_folders'));
expect(entryPoints).toEqual([]);
});
it('should handle nested node_modules folders', () => {
const {entryPoints} = finder.findEntryPoints('/nested_node_modules');
const {entryPoints} = finder.findEntryPoints(_('/nested_node_modules'));
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
expect(entryPointPaths).toEqual([
['/nested_node_modules/outer', '/nested_node_modules/outer'],
[_('/nested_node_modules/outer'), _('/nested_node_modules/outer')],
// Note that the inner entry point does not get included as part of the outer package
[
'/nested_node_modules/outer/node_modules/inner',
'/nested_node_modules/outer/node_modules/inner'
_('/nested_node_modules/outer/node_modules/inner'),
_('/nested_node_modules/outer/node_modules/inner'),
],
]);
});

Some files were not shown because too many files have changed in this diff Show More