Skip to content

feat(compiler-cli): add resource inlining to ngc #22615

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

Closed
wants to merge 4 commits into from

Conversation

alexeagle
Copy link
Contributor

@alexeagle alexeagle commented Mar 6, 2018

When angularCompilerOptions { enableResourceInlining: true }, we replace all templateUrl / styleUrls properties in @Component with template / styles

@alexeagle alexeagle requested a review from chuckjaz March 6, 2018 22:53
@alexeagle alexeagle force-pushed the ngc_resource_inlining branch from dd1a51d to 4b18d71 Compare March 6, 2018 22:57
@alexeagle alexeagle added the area: core Issues related to the framework runtime label Mar 6, 2018
@mary-poppins
Copy link

You can preview dd1a51d at https://pr22615-dd1a51d.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 4b18d71 at https://pr22615-4b18d71.ngbuilds.io/.

@alexeagle
Copy link
Contributor Author

@chuckjaz : the ngc_spec test fails because the tsickle transformer runs first and erases the decorators. I could register my transformer in main.ts instead, where I have access to the beforeTsickle argument to emitWithTsickle, does that seem right? Then when I do that, tsickle fails to transform the resulting sourcemap, maybe I have to do something special to fix them up?

: TypeError: Cannot read property 'text' of undefined
 at ClassDeclaration in /tmp/tmp.968164/my.component.ts:3:9
 at SourceFile in /tmp/tmp.968164/my.component.ts:2:9
    at Object.getTokenPosOfNode (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:7515:72)
    at NodeObject.getStart (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:94843:23)
    at /usr/local/google/home/alexeagle/Projects/src/transformer_sourcemap.ts:162:17
    at visitNodes (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:12699:30)
    at Object.forEachChild (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:12945:24)
    at NodeObject.forEachChild (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:94981:23)
    at NodeSourceMapper.addMapping (/usr/local/google/home/alexeagle/Projects/src/transformer_sourcemap.ts:161:18)
    at DecoratorRewriter.Rewriter.writeRange (/usr/local/google/home/alexeagle/Projects/src/rewriter.ts:159:23)
    at DecoratorRewriter.Rewriter.writeLeadingTrivia (/usr/local/google/home/alexeagle/Projects/src/rewriter.ts:133:10)
    at DecoratorRewriter.maybeProcess (/usr/local/google/home/alexeagle/Projects/src/decorator-annotator.ts:377:14)
    at DecoratorRewriter.Rewriter.visit (/usr/local/google/home/alexeagle/Projects/src/rewriter.ts:72:17)
    at /usr/local/google/home/alexeagle/Projects/src/rewriter.ts:120:12
    at visitNodes (/usr/local/google/home/alexeagle/Projects/angular/node_modules/typescript/lib/typescript.js:12699:30)

@alfaproject
Copy link
Contributor

@alexeagle will this respect TS path mapping?
ref: angular/angular-cli#9766

@alexeagle alexeagle force-pushed the ngc_resource_inlining branch from 4b18d71 to 191689a Compare March 8, 2018 23:33
@alexeagle
Copy link
Contributor Author

@alfaproject this uses the existing resource loader in ngc so the behavior should be the same - so no :(

@mary-poppins
Copy link

You can preview 191689a at https://pr22615-191689a.ngbuilds.io/.

When angularCompilerOptions { enableResourceInlining: true }, we replace all templateUrl and styleUrls properties in @component with template/styles
@alexeagle alexeagle force-pushed the ngc_resource_inlining branch from 191689a to 627be1d Compare March 8, 2018 23:44
@mary-poppins
Copy link

You can preview 627be1d at https://pr22615-627be1d.ngbuilds.io/.

Copy link
Contributor

@chuckjaz chuckjaz left a comment

Choose a reason for hiding this comment

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

NITs, otherwise LGTM


updateDecoratorMetadata(arg: MetadataObject): MetadataObject {
if (arg['templateUrl']) {
const templateUrl = arg['templateUrl'];
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Consider factoring out the code duplication between templateUrl and styleUrls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


const declaration = symbol.declarations[0] !;

if (!ts.isImportSpecifier(declaration)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding !declarations || here instead of the ! on 217.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


const name = (declaration.propertyName || declaration.name).text;
const moduleId =
(declaration.parent !.parent !.parent !.moduleSpecifier as ts.StringLiteral).text;
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a comment that parents are guaranteed to be set in the AST given to a transformer and the depth of the tree is guaranteed by the the traversal by which arrives at this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

*/
function updateComponentProperties(
args: ts.NodeArray<ts.Expression>, host: ResourceLoader): ts.NodeArray<ts.Expression> {
if (args.length !== 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for redundant checks as you can emit on error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't follow - are you saying this code is good as written since it's tolerant of incorrect input?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes!

return node;

default:
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

return node;?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, missing a test for that

@alfaproject
Copy link
Contributor

@alexeagle sorry if it's off-topic, but did you guys ever considered that? o:

@alexeagle
Copy link
Contributor Author

@alfaproject I can understand how you might load resources dynamically, and have a different design-time module layout on disk than you have at runtime when fetching resources, and so you have exactly the same use case that TypeScript path mapping was designed for. And it reduces overall complexity for users if the various ways to express a dependency from one file on another use the same semantics. I think it's reasonable to file a feature request for that.

@alexeagle alexeagle added target: major This PR is targeted for the next major release action: merge The PR is ready for merge by the caretaker labels Mar 9, 2018
@mary-poppins
Copy link

You can preview d7a9dd6 at https://pr22615-d7a9dd6.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 0148f5b at https://pr22615-0148f5b.ngbuilds.io/.

@alexeagle alexeagle added action: merge The PR is ready for merge by the caretaker and removed action: merge The PR is ready for merge by the caretaker labels Mar 9, 2018
@alexeagle
Copy link
Contributor Author

alexeagle commented Mar 9, 2018

FYI
@juristr let's try this in your example lib as soon as it's live in the snapshot builds
@dherges you might want to use this in ng-packagr in place of your workaround, once you rely on dep on angular v6

@kara kara closed this in b5be18f Mar 9, 2018
@juristr
Copy link
Contributor

juristr commented Mar 9, 2018

@alexeagle oh, nice. Yep lets try it, currently I inlined the templates manually in the bazel branch

Copy link
Contributor

@dherges dherges left a comment

Choose a reason for hiding this comment

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

Hey Alex, it will be a big win!

import {MetadataTransformer, ValueTransform} from './metadata_cache';

export type ResourceLoader = {
loadResource(path: string): Promise<string>| string;
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 Perfect! The Promise<string> | string allows to integrate external tools like scss, less and other preprocessors preprocessors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope, if it doesn't return string this transform fails because the typescript compiler is a synchronous API. Under this toolchain the preprocessors are expected to run before ngc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm 😐 Nevertheless, I'll get rid of the horrible replace source text by string-position-marker thing that you'll solved in a more elegant way in ngc.

// The number of parent references here match the recursion depth at this point.
const moduleId =
(declaration.parent !.parent !.parent !.moduleSpecifier as ts.StringLiteral).text;
return moduleId === '@angular/core' && name === 'Component';
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, Angular has a build-time dependency on devkit but not yet a runtime dependency... will have to think about that.
What's the failing test case look like?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should be:

import * as ngCore from '@angular/core';

@ngCore.Component({
  templateUrl: `..`
})
export class Something {}

https://github.com/dherges/ng-packagr/blob/master/src/lib/ts/ng-ts-ast.spec.ts#L64-L66

return false;
}

const name = (declaration.propertyName || declaration.name).text;
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 I think this accounts for named imports – import { Component } from '@angular/core' – and aliased imports – import { Component as Foo } from '@angular/core'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I should add tests for those cases though - I borrowed some of this code from you but dropped all the conditional logic I wasn't testing

@dherges
Copy link
Contributor

dherges commented Mar 10, 2018

For users: the transformer's calls to loadResource() are forwarded to the compiler host's readResource()

@alippai
Copy link
Contributor

alippai commented Mar 12, 2018

Wasn't inlining already possible with the *ngfactory files? What's the difference with this PR?

@alexeagle
Copy link
Contributor Author

@alippai other tools were doing inlining on the resulting JS outputs of the Angular compiler before. Now we make this a first-party feature. (Under Bazel, we never plan to do the workaround of modifying JS outputs) Also it means source-maps should be more correct.

@alippai
Copy link
Contributor

alippai commented Mar 12, 2018

@alexeagle awesome, thanks!

@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 13, 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 target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants