Skip to content

feat(ivy): fix and enable esm2015/esm5 processing in ngcc#25090

Closed
gkalpak wants to merge 8 commits into
angular:masterfrom
gkalpak:feat-ngcc-non-flat-formats
Closed

feat(ivy): fix and enable esm2015/esm5 processing in ngcc#25090
gkalpak wants to merge 8 commits into
angular:masterfrom
gkalpak:feat-ngcc-non-flat-formats

Conversation

@gkalpak
Copy link
Copy Markdown
Member

@gkalpak gkalpak commented Jul 25, 2018

Sits on top of #25060.

PR Checklist

PR Type

[x] Bugfix
[x] Feature
[x] Refactoring (no functional changes, no api changes)

Does this PR introduce a breaking change?

[ ] Yes
[x] No

@googlebot
Copy link
Copy Markdown

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@gkalpak gkalpak requested a review from petebacondarwin July 25, 2018 15:28
@gkalpak gkalpak added type: bug/fix feature Label used to distinguish feature request from other issues action: review The PR is still awaiting reviews from at least one requested reviewer refactoring Issue that involves refactoring or code-cleanup labels Jul 25, 2018
@mary-poppins
Copy link
Copy Markdown

You can preview 1ce3cc5 at https://pr25090-1ce3cc5.ngbuilds.io/.

@gkalpak gkalpak mentioned this pull request Jul 28, 2018
3 tasks
@gkalpak gkalpak force-pushed the feat-ngcc-non-flat-formats branch from 1ce3cc5 to 9cdcb36 Compare July 28, 2018 10:28
@gkalpak
Copy link
Copy Markdown
Member Author

gkalpak commented Jul 28, 2018

Rebased on #25060 (which was rebased on master, now that #24897 has been merged).
I also added back a commit per #24897 (comment).

@mary-poppins
Copy link
Copy Markdown

You can preview 9cdcb36 at https://pr25090-9cdcb36.ngbuilds.io/.

* Check whether the given node actually represents a class.
*/
isClass(node: ts.Declaration): boolean;
isClass(node: ts.Node): boolean;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you describe the rationale for this change? In my mind the class should always be a declaration.

In ES6:

class Foo {} // ts.ClassDeclaration

In ES5:

// ts.VariableDeclaration
var Foo = /** @class */ (function () {
    function Foo() {
    }
    return Foo;
}());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The rational is described in Esm5ReflectionHost's getClassSymbol() method: https://github.com/angular/angular/pull/25090/files#diff-ac5866175696e5b67c53651096f39094R49

Basically, in some cases we end up passing the inner function declaration (from inside the IIFE) to isClass().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe there is a better way to avoid that, but I couldn't find any.

// It might be the function expression inside the IIFE. We need to go 5 levels up...

// 1. IIFE body.
let outerNode = node.parent;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just a cautionary note - different versions of TypeScript generate slightly different syntax for class declarations ;)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

😞
No idea how to work around it other than having a different implementation for each version of TS 😒


return this.checker.getSymbolAtLocation(innerClassIdentifier);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

else if for clarity?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

const options: ts.CompilerOptions = {allowJs: true, rootDir: entryPointPath};
const options: ts.CompilerOptions = {
allowJs: true,
maxNodeModuleJsDepth: Infinity,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice catch on this one.

const barNode =
getDeclaration(program, FUNCTION_BODY_FILE.name, 'bar', ts.isFunctionDeclaration) !;
const barDef = host.getDefinitionOfFunction(barNode);
expect(barDef.node).toBe(barNode);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Geez, we need a better way to write tests like these. I'm gonna work on that.

const commonPath = path.join(nodeModulesPath, '@angular/common');
const exitCode = mainNgcc([commonPath, 'esm2015']);

console.warn(find('node_modules_ngtsc').filter(p => p.endsWith('.js') || p.endsWith('map')));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these console.warn statements intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes 😁
At this point, the e2e tests consist of us looking at the printed result and concluding it "looks reasonable". We will have better e2e tests soon 😁

(Note, the console.warns are not introduced in this PR btw; they are already present in other tests.)

} else if (entry instanceof Reference) {
if (!entry.expressable) {
throw new Error(`Value at position ${idx} in ${name} array is not expressable`);
} else if (!this.reflector.isClass(entry.node)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if we add an additional ts.isDeclaration(entry.node) here then we don't need to change the signature of isClass.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

But this will potentially hide errors (where entry.node is a Declaration but not a class) and not help in other places where isClass() might be called with the inner function declaration on ESM5.

Generally, I think this should be the reflector's concern.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I looked again and I agree that we need to change the isClass to take a ts.Node.

alxhub and others added 8 commits August 20, 2018 16:35
ngtsc's static resolver can evaluate function calls where parameters
have default values. In TypeScript code these default values live on the
function definition, but in ES5 code the default values are represented
by statements in the function body.

A new ReflectionHost method getDefinitionOfFunction() abstracts over
this difference, and allows the static reflector to more accurately
evaluate ES5 code.
In some code formats (e.g. ES5) methods can actually be function
expressions. For example:

```js
function MyClass() {}
// this static method is declared as a function expression
MyClass.staticMethod = function() { ... };
```
Since non-flat module formats (esm2015, esm5) have different structure
than their flat counterparts (and since we are operating on JS files
inside `node_modules/`, we need to configure TS to include deeply nested
JS files (by specifying a sufficiently high `maxNodeModuleJsDepth`).

Remains to be determined if this has any (noticeable) performance
implications.
@gkalpak gkalpak force-pushed the feat-ngcc-non-flat-formats branch from 9cdcb36 to af5a439 Compare August 20, 2018 13:47
@gkalpak
Copy link
Copy Markdown
Member Author

gkalpak commented Aug 20, 2018

Rebased on master and addressed (some) comments.

@mary-poppins
Copy link
Copy Markdown

You can preview af5a439 at https://pr25090-af5a439.ngbuilds.io/.

@petebacondarwin
Copy link
Copy Markdown
Contributor

@gkalpak we have rolled this PR into the ngcc-integration-test branch and associated PR: #25406

We need to add any changes you make here to that, because @alxhub is going to try to land the whole lot in one go.

@petebacondarwin
Copy link
Copy Markdown
Contributor

Superceded by #25203

@gkalpak gkalpak deleted the feat-ngcc-non-flat-formats branch August 22, 2018 16:01
@angular-automatic-lock-bot
Copy link
Copy Markdown

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 Sep 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: review The PR is still awaiting reviews from at least one requested reviewer cla: no feature Label used to distinguish feature request from other issues refactoring Issue that involves refactoring or code-cleanup type: bug/fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants