Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ngcc): several fixes #34169

Closed
wants to merge 6 commits into from
Closed

Conversation

gkalpak
Copy link
Member

@gkalpak gkalpak commented Dec 1, 2019

This PR includes several ngcc fixes. With these fixes, ngcc should be able to successfully process NativeScript. See individual commits for details.

Jira issue: FW-1689

TODO:

  • Determine whether it is problematic that the synthetically created function declaration for TS helpers will be recognized as non-synthetic in StaticInterpreter (and how to fix it).
  • Implement the CommonJS fixes for UMD as well.
  • Add integration tests.

@mary-poppins
Copy link

You can preview 5fdc4a2 at https://pr34169-5fdc4a2.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 263efda at https://pr34169-263efda.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 8077ce1 at https://pr34169-8077ce1.ngbuilds.io/.

@mary-poppins
Copy link

You can preview b0cc9d9 at https://pr34169-b0cc9d9.ngbuilds.io/.

@mary-poppins
Copy link

You can preview a45d1bb at https://pr34169-a45d1bb.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 363218f at https://pr34169-363218f.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 08f17b8 at https://pr34169-08f17b8.ngbuilds.io/.

@gkalpak gkalpak changed the title WIP - some ngcc fixes (ngcc): several ngcc fixes Dec 5, 2019
@gkalpak gkalpak changed the title (ngcc): several ngcc fixes fix(ngcc): several fixes Dec 5, 2019
@gkalpak gkalpak added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release type: bug/fix and removed state: WIP labels Dec 5, 2019
@gkalpak gkalpak marked this pull request as ready for review December 5, 2019 18:58
@gkalpak gkalpak requested review from a team as code owners December 5, 2019 18:58
@mary-poppins
Copy link

You can preview e28ac70 at https://pr34169-e28ac70.ngbuilds.io/.

@petebacondarwin
Copy link
Member

typo in the 2nd to last commit: findDepeendencies

@petebacondarwin
Copy link
Member

typos in the last commit:

In the case of emitted helpers, the tslib.__someHelper expression was
resolved to a function declaration of the form
export declare function __someHelper(...args: any[][]): any[];, which
allows getDefinitionOfFunction() to correctly map it to a TS helper.

I think this should be In the case of imported helpers, ....

And also functinos

@petebacondarwin
Copy link
Member

typo in 5th commit: By failing to recongize

Copy link
Member

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed all but the last comment.

// ([source](https://github.com/microsoft/TypeScript/blob/d7c83f023/src/compiler/transformers/module/module.ts#L1796-L1797)).
// So, theoretically, we only care about the formats `__export(require('...'))` and
// `tslib.__exportStar(require('...'), exports)`.
// The accepts the other two formats (`__exportStar(...)` and `tslib.__export(...)`) to be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: This accepts

fnName = statement.expression.expression.name.text;
}

// Esnure the called function is either `__export()` or `__exportStar()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typ: Ensure

// tslib_1.__exportStar(require('...'), exports);
reExportsWithImportedHelper?: string[];
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I prefer not to clutter the start of a test file with supporting stuff (like helper functions and interfaces) since it makes it more difficult to quickly get to the tests when browsing later. Since interfaces are always hoisted, can we move this down below the describe block?

ts.isPropertyAssignment(prop) && isRequireCall(prop.initializer))
.map(prop => prop.initializer);

requireCalls.push(...requireCallsFromProperties);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: this could be more simple and not require intermediate arrays to be created:

        stmt.expression.right.properties.forEach(prop => {
          if(ts.isPropertyAssignment(prop) && isRequireCall(prop.initializer)) {
            requireCalls.push(prop.initializer);
          }
        });

],
ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), undefined),
viaModule: null,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I would put this whole block into its own function (e..g createSynthesizedHelperDeclaration(id)) with the big comment converted to JSDOC comment. This will keep the calling function much smaller and more readable.

Copy link
Member

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR looks great! Regarding the last commit, I can accept this approach. I don't know what other ideas you had. One thought is if we could create a new thing called a LazyFunction, which we would match code of the form (this && this.<fn>) || function () { ... }, which could then be visited more intelligently in the StaticInterpreter?

@petebacondarwin
Copy link
Member

@gkalpak is this PR nearly ready? Is it worth splitting out the non-contentious bits so that we can land them this week?

@gkalpak
Copy link
Member Author

gkalpak commented Dec 12, 2019

I'll split the non-contentious bits into a separate PR this week 👍

Previously, the `CommonJsReflectionHost` would only recognize re-exports
of the form `__export(require('...'))`. This is what re-exports look
like, when the TS helpers are emitted inline (i.e. when compiling with
the default [TS compiler options][1] that include `noEmitHelpers: false`
and `importHelpers: false`).

However, when compiling with `importHelpers: true` and [tslib][2] (which
is the recommended way for optimized bundles), the re-exports will look
like: `tslib_1.__exportStar(require('...'), exports)`
These types of re-exports where previously not recognized by
`CommonJsReflectionHost` and thus ignored.

This commit fixes this by ensuring both re-export formats are
recognized.

[1]: https://www.typescriptlang.org/docs/handbook/compiler-options.html
[2]: https://www.npmjs.com/package/tslib
Previously, `CommonJsDependencyHost.findDependencies()` would only find
dependencies via imports of the form `var foo = require('...');` or
`var foo = require('...'), bar = require('...');` However, CommonJS
files can have imports in many different forms. By failing to recognize
other forms of imports, the associated dependencies were missed, which
in turn resulted in entry-points being compiled out-of-order and failing
due to that.

While we cannot easily capture all different types of imports, this
commit enhances `CommonJsDependencyHost` to recognize the following
common forms of imports:

- Imports in property assignments. E.g.:
  `exports.foo = require('...');` or
  `module.exports = {foo: require('...')};`

- Imports for side-effects only. E.g.:
  `require('...');`

- Star re-exports (with both emitted and imported heleprs). E.g.:
  `__export(require('...'));` or
  `tslib_1.__exportStar(require('...'), exports);`
In ES5 code, TypeScript requires certain helpers (such as
`__spreadArrays()`) to be able to support ES2015+ features. These
helpers can be either imported from `tslib` (by setting the
`importHelpers` TS compiler option to `true`) or emitted inline (by
setting the `importHelpers` and `noEmitHelpers` TS compiler options to
`false`, which is also the default value for both).

Ngtsc's `StaticInterpreter` (which is also used during ngcc processing)
is able to statically evaluate some of these helpers (currently
`__spread()` and `__spreadArrays()`), as long as
`ReflectionHost#getDefinitionOfFunction()` correctly detects the
declaration of the helper. For this to happen, the left-hand size of the
corresponding call expression (i.e. `__spread(...)` or
`tslib.__spread(...)`) must be evaluated as a function declaration for
`getDefinitionOfFunction()` to be called with.

In the case of imported helpers, the `tslib.__someHelper` expression was
resolved to a function declaration of the form
`export declare function __someHelper(...args: any[][]): any[];`, which
allows `getDefinitionOfFunction()` to correctly map it to a TS helper.

In contrast, in the case of emitted helpers (and regardless of the
module format: `CommonJS`, `ESNext`, `UMD`, etc.)), the `__someHelper`
identifier was resolved to a variable declaration of the form
`var __someHelper = (this && this.__someHelper) || function () { ... }`,
which upon further evaluation was categorized as a `DynamicValue`
(prohibiting further evaluation by the `getDefinitionOfFunction()`).

As a result of the above, TypeScript helpers were not evaluated in ES5
code.

---
This commit fixes this by detecting such helpers in
`Esm5ReflectionHost#getDeclarationOfIdentifier()` and (instead of the
actual variable declaration) return a synthesized function declaration,
identical to the one that would have been returned for the imported
helper.

This allows `getDefinitionOfFunction()` to correctly map it to a TS
helper, allowing `StaticInterpreter` to statically evaluate the
associated call expression.
@gkalpak
Copy link
Member Author

gkalpak commented Dec 17, 2019

@petebacondarwin: FYI, I have addressed your comments (in fixup commits - except for the commit message typos) and moved some of the commits into separate PRs: #34437, #34441

I need to further split this into more PRs (for easier reviewing) and add similar fixes for UMD (where appropriate).

@mary-poppins
Copy link

You can preview 8c37785 at https://pr34169-8c37785.ngbuilds.io/.

@gkalpak
Copy link
Member Author

gkalpak commented Dec 21, 2019

Further split this up into separate PRs per fix: #34512, #34527, #34528
(Each builds on top of the previous one, so you can either only review all commits on the last one or only review the new commits on each one.)

@gkalpak gkalpak added state: blocked and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Jan 10, 2020
@gkalpak
Copy link
Member Author

gkalpak commented Jan 14, 2020

With all the "spin-off" PRs merged, the only remaining fix for this PR is to correctly detect emitted TS helpers in ES5 (i.e. 0b89f17). (I need to clean it up or possibly rework the approach.)

This will also fix processing mobx-angular@3.1.0 (see angular/ngcc-validation#762).

@gkalpak
Copy link
Member Author

gkalpak commented Feb 6, 2020

The final fix is in #35139 #35191. This PR is now obsolete. Closing...

@gkalpak gkalpak closed this Feb 6, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 8, 2020
@gkalpak gkalpak deleted the fix-ngcc-nativescript branch October 2, 2020 09:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes state: blocked target: patch This PR is targeted for the next patch release type: bug/fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants