Skip to content

Cyclic import detection in ngtsc / for g3#28169

Closed
alxhub wants to merge 3 commits intoangular:masterfrom
alxhub:ngtsc-cyclic-imports
Closed

Cyclic import detection in ngtsc / for g3#28169
alxhub wants to merge 3 commits intoangular:masterfrom
alxhub:ngtsc-cyclic-imports

Conversation

@alxhub
Copy link
Member

@alxhub alxhub commented Jan 16, 2019

No description provided.

@alxhub alxhub requested review from a team January 16, 2019 00:53
@alxhub alxhub requested a review from mhevery January 16, 2019 00:53
@mary-poppins
Copy link

You can preview 2e5fe41 at https://pr28169-2e5fe41.ngbuilds.io/.

Copy link
Member

@JoostK JoostK left a comment

Choose a reason for hiding this comment

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

Looks great, some minor things.

Copy link
Member

Choose a reason for hiding this comment

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

Let's use _isCyclicImport 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Hahaha, fixed!

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this early return is correct here, but once you use _isCyclicImport the problem resolves itself

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

Choose a reason for hiding this comment

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

Why isn't directives simply mapped from scope.directives? There's no initial content to deal with so that should work just fine.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.

Copy link
Member

Choose a reason for hiding this comment

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

😍

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm actually debating whether I like this. It gets really annoying to write out example programs all the time, but having actual code to look at when you're debugging is nice. In this case I think it's okay because the programs being generated are very specific.

Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be pure, or all the hard work for nothing ;)

Copy link
Member Author

@alxhub alxhub Jan 16, 2019

Choose a reason for hiding this comment

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

Hahahaha. Good point! I'll actually invert the test so that we expect not to have a pure call there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Swapping these two if and else blocks would remove the need for the negation on !cycleDetected.

Copy link
Member Author

Choose a reason for hiding this comment

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

I actually wrote it this way on purpose - the common case is the if, the uncommon case is the else.

Copy link
Contributor

Choose a reason for hiding this comment

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

/shrug/ :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

This return here exits the whole resolve() method.
Are you saying that if a directive is an external expression but has no import we abort all resolving activity?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, as @JoostK pointed out this flow only makes sense within the _isCyclicImport function.

Copy link
Contributor

Choose a reason for hiding this comment

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

This method does not appear to be used...

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahah, I just realised that this is a WIP. The code above in the resolve method was copied from here, which is why there are the confusing return statements there.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep!

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 2e5fe41 to 5577728 Compare January 16, 2019 19:23
@alxhub alxhub added target: major This PR is targeted for the next major release action: review The PR is still awaiting reviews from at least one requested reviewer labels Jan 16, 2019
@mary-poppins
Copy link

You can preview 5577728 at https://pr28169-5577728.ngbuilds.io/.

Copy link
Member

Choose a reason for hiding this comment

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

nit:

const cycleDetected =
    scope.directives.some(meta => this._isCyclicImport(meta.directive, origin)) ||
    Array.from(scope.pipes.values()).some(pipe => this._isCyclicImport(pipe, origin));

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, nice idea.

Copy link
Member

Choose a reason for hiding this comment

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

The comment has become a bit inaccurate ;)

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed!

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 5577728 to 1e7bfee Compare January 16, 2019 19:46
@mary-poppins
Copy link

You can preview 1e7bfee at https://pr28169-1e7bfee.ngbuilds.io/.

Copy link
Member

@JoostK JoostK left a comment

Choose a reason for hiding this comment

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

The CI fails because of the incorrect private export, which indicates a surprising dependency on this feature within the Angular Router codebase. That's ok for now, let me look into refactoring the Router to avoid the cyclic dep in Ivy builds sometime.

Copy link
Member

Choose a reason for hiding this comment

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

Whoopsie, caught by failing CI :)

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 1e7bfee to d5867b3 Compare January 16, 2019 21:05
@mary-poppins
Copy link

You can preview d5867b3 at https://pr28169-d5867b3.ngbuilds.io/.

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from d5867b3 to 20bf80b Compare January 16, 2019 21:40
@mary-poppins
Copy link

You can preview 20bf80b at https://pr28169-20bf80b.ngbuilds.io/.

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 20bf80b to 5179313 Compare January 16, 2019 22:36
@mary-poppins
Copy link

You can preview 5179313 at https://pr28169-5179313.ngbuilds.io/.

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: may be use map here (and for directives too)?

const pipes: Expression[] = scope.pipes.map(pipe => pipe.toExpression(context) !);

@alxhub alxhub removed the request for review from mhevery January 18, 2019 21:33
Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch 3 times, most recently from 9804468 to 230c30e Compare January 23, 2019 18:47
@jasonaden jasonaden added the area: core Issues related to the framework runtime label Jan 24, 2019
@ngbot ngbot bot added this to the needsTriage milestone Jan 24, 2019
This commit implements a cycle detector which looks at the import graph of
TypeScript programs and can determine whether the addition of an edge is
sufficient to create a cycle. As part of the implementation, module name
to source file resolution is implemented via a ModuleResolver, using TS
APIs.
@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 230c30e to 2d633bd Compare January 25, 2019 20:07
By its nature, Ivy alters the import graph of a TS program, adding imports
where template dependencies exist. For example, if ComponentA uses PipeB
in its template, Ivy will insert an import of PipeB into the file in which
ComponentA is declared.

Any insertion of an import into a program has the potential to introduce a
cycle into the import graph. If for some reason the file in which PipeB is
declared imports the file in which ComponentA is declared (maybe it makes
use of a service or utility function that happens to be in the same file as
ComponentA) then this could create an import cycle. This turns out to
happen quite regularly in larger Angular codebases.

TypeScript and the Ivy runtime have no issues with such cycles. However,
other tools are not so accepting. In particular the Closure Compiler is
very anti-cycle.

To mitigate this problem, it's necessary to detect when the insertion of
an import would create a cycle. ngtsc can then use a different strategy,
known as "remote scoping", instead of directly writing a reference from
one component to another. Under remote scoping, a function
'setComponentScope' is called after the declaration of the component's
module, which does not require the addition of new imports.

FW-647 #resolve
@alxhub alxhub force-pushed the ngtsc-cyclic-imports branch from 2d633bd to 34b87a1 Compare January 25, 2019 20:59
@alxhub alxhub added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Jan 25, 2019
@jasonaden jasonaden closed this in a789a3f Jan 28, 2019
jasonaden pushed a commit that referenced this pull request Jan 28, 2019
This commit implements a cycle detector which looks at the import graph of
TypeScript programs and can determine whether the addition of an edge is
sufficient to create a cycle. As part of the implementation, module name
to source file resolution is implemented via a ModuleResolver, using TS
APIs.

PR Close #28169
jasonaden pushed a commit that referenced this pull request Jan 28, 2019
…ed (#28169)

By its nature, Ivy alters the import graph of a TS program, adding imports
where template dependencies exist. For example, if ComponentA uses PipeB
in its template, Ivy will insert an import of PipeB into the file in which
ComponentA is declared.

Any insertion of an import into a program has the potential to introduce a
cycle into the import graph. If for some reason the file in which PipeB is
declared imports the file in which ComponentA is declared (maybe it makes
use of a service or utility function that happens to be in the same file as
ComponentA) then this could create an import cycle. This turns out to
happen quite regularly in larger Angular codebases.

TypeScript and the Ivy runtime have no issues with such cycles. However,
other tools are not so accepting. In particular the Closure Compiler is
very anti-cycle.

To mitigate this problem, it's necessary to detect when the insertion of
an import would create a cycle. ngtsc can then use a different strategy,
known as "remote scoping", instead of directly writing a reference from
one component to another. Under remote scoping, a function
'setComponentScope' is called after the declaration of the component's
module, which does not require the addition of new imports.

FW-647 #resolve

PR Close #28169
vetom pushed a commit to vetom/angular that referenced this pull request Jan 31, 2019
This commit implements a cycle detector which looks at the import graph of
TypeScript programs and can determine whether the addition of an edge is
sufficient to create a cycle. As part of the implementation, module name
to source file resolution is implemented via a ModuleResolver, using TS
APIs.

PR Close angular#28169
vetom pushed a commit to vetom/angular that referenced this pull request Jan 31, 2019
…ed (angular#28169)

By its nature, Ivy alters the import graph of a TS program, adding imports
where template dependencies exist. For example, if ComponentA uses PipeB
in its template, Ivy will insert an import of PipeB into the file in which
ComponentA is declared.

Any insertion of an import into a program has the potential to introduce a
cycle into the import graph. If for some reason the file in which PipeB is
declared imports the file in which ComponentA is declared (maybe it makes
use of a service or utility function that happens to be in the same file as
ComponentA) then this could create an import cycle. This turns out to
happen quite regularly in larger Angular codebases.

TypeScript and the Ivy runtime have no issues with such cycles. However,
other tools are not so accepting. In particular the Closure Compiler is
very anti-cycle.

To mitigate this problem, it's necessary to detect when the insertion of
an import would create a cycle. ngtsc can then use a different strategy,
known as "remote scoping", instead of directly writing a reference from
one component to another. Under remote scoping, a function
'setComponentScope' is called after the declaration of the component's
module, which does not require the addition of new imports.

FW-647 #resolve

PR Close angular#28169
@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 Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime cla: yes merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: major This PR is targeted for the next major release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants