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

Allow injecting Acorn plugins #1857

Merged
merged 3 commits into from
Jan 23, 2018
Merged

Conversation

stasm
Copy link
Contributor

@stasm stasm commented Jan 9, 2018

This PR makes it possible to pass Acorn plugins into Rollup.

Acorn doesn't support non-Stage 4 proposals. There is a number of plugins which add support for the upcoming ECMAScript features. Inspired by comments in #1512 I'd like to propose to make it possible to add plugins to Acorn via rollup.config.js.

I added a new optional field in Rollup input options called acornInjects. If specified, it should be an array of inject functions which hook plugins into Acorn.

Example rollup.config.js:

import acornAsyncIteration from 'acorn-async-iteration/inject';

export default {
    // … other options …
    acorn: {
        ecmaVersion: 9,
        plugins: { asyncIteration: true }
    },
    acornInjectPlugins: [
        acornAsyncIteration
    ]
};

I originally wrote this for my own use. I'm submitting this PR to see if you think it's a good addition. I'll be happy to add tests to this PR if you do.

Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

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

Would be great to have this feature, thanks for the PR.

src/Module.ts Outdated
@@ -45,9 +45,10 @@ export interface CommentDescription { block: boolean, text: string, start: numbe
export interface ExportDescription { localName: string, identifier?: string }
export interface ReexportDescription { localName: string, start: number, source: string, module: Module }

function tryParse (module: Module, acornOptions: Object) {
function tryParse (module: Module, acornOptions: Object, acornInjects: Function[]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

These injections don't need to happen for every module parse - rather the acornInjects.reduce(acc, plugin) => plugin(acc)) step can be done in the Graph constructor I think rather?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Acorn plugins are not composable in that they expect the acorn object to be passed as an argument (example). I think I'll need to import acorn in Graph.js, produce the final acorn object there, and then pass it to tryParse. Sound good?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds sensible! Alternatively because we have live bindings in ES Modules, it could be possible to have Graph.ts explicitly export its own acorn variable which is imported by Module.ts.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually that said, much better to have class-level encapsulation actually here.

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 wasn't able to figure out how to pass the entire acorn object to tryParse. I think it's related to the fact that it's declared as a namespace in @types/acorn, but I don't know TypeScript well enough to know for sure.

I ended up passing the parse function instead.

@@ -61,6 +61,7 @@ export interface InputOptions {
};

acorn?: {};
acornInjects?: Function[];
Copy link
Contributor

Choose a reason for hiding this comment

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

How about just calling this acornPlugins?

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'm happy to change the name as you see fit. I chose acornInjects to avoid the confusion between acorn.plugins (where plugins are enabled, e.g. asyncIteration: true) and acornPlugins (where plugin functions would be passed).

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm perhaps acornInjectPlugins to be clearer then?

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 renamed this field to acornInjectPlugins. I can also rename it to acronPlugins if you prefer; perhaps it's not really all that confusing. I'll leave the final decision to you. Thanks.

@stasm stasm force-pushed the acorn-injects branch 2 times, most recently from 8f63cd8 to 34b2022 Compare January 10, 2018 12:28
@stasm
Copy link
Contributor Author

stasm commented Jan 10, 2018

I added a few tests. I wasn't quite sure where to put them, so I went for test/misc :)

@stasm
Copy link
Contributor Author

stasm commented Jan 10, 2018

@guybedford This is ready for your review. Once approved, I'd like to squash this into a single commit and update the commit message with the new name for acornInjects. For now, I've left all commits separate to make it easier to review the changes made since yesterday.

@guybedford
Copy link
Contributor

@stasm looks great to me and thanks for padding out the tests.

There's on issue with the internal plugin we're applying for dynamic import - the line at https://github.com/rollup/rollup/pull/1857/files#diff-356410863c3a18aee7d09896d52d6b08R1 should be possible to be removed since Acorn is passed through, and also the line at https://github.com/rollup/rollup/pull/1857/files#diff-356410863c3a18aee7d09896d52d6b08R41 should be able to be moved into Graph.ts to be the first plugin that is attached when dynamic import is enabled.

Let me know if that makes sense, or if you have any questions on this further.

@guybedford
Copy link
Contributor

Actually I see that the acorn import is stilll used for the type in the Module.ts file. Perhaps just alter this to import { IParse } from 'acorn' instead?

@stasm
Copy link
Contributor Author

stasm commented Jan 10, 2018

Yeah, I wasn't sure what wrapDynamicImportPlugin was doing. I'll try to make the changes you suggested, thanks!

@stasm
Copy link
Contributor Author

stasm commented Jan 10, 2018

Nevermind, Github didn't do a good job showing me the lines you linked to. I think I understand, let me try.

@stasm
Copy link
Contributor Author

stasm commented Jan 11, 2018

To the best of my understanding setModuleDynamicImportsReturnBinding still needs to be called in Module.ts because it's used to bind the Module's dynamicImports array to the plugin. I exposed it on the Graph instance so that at least the wrapping is done in Graph.ts.

Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

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

Thanks for adding the extra code. This looks great to merge to me.

@stasm
Copy link
Contributor Author

stasm commented Jan 11, 2018

I squashed the commits and rebased on top of the current master. Thanks for helping to get this ready to merge and for the review!

@guybedford
Copy link
Contributor

Right after the last comment I ended up working on this - 4d97152, which alters this dynamic import application process.

So depending on which lands first we'll just need to adjust the conflict here.

@lukastaegert
Copy link
Member

Sorry, missed this one for the latest release. Will probably release this together with #1841 once I have reviewed that. Thanks!

@lukastaegert
Copy link
Member

You probably want to rebase to latest master

Add a new field in Rollup input options: `acornInjectPlugins`. If specified, it
should be an array of `inject` functions which hook plugins into Acorn.

Example `rollup.config.js`:

```js
import acornAsyncIteration from 'acorn-async-iteration/inject';

export default {
    acorn: {
        ecmaVersion: 9,
        plugins: { asyncIteration: true }
    },
    acornInjectPlugins: [
        acornAsyncIteration
    ]
};
```
@stasm
Copy link
Contributor Author

stasm commented Jan 12, 2018

Rebased. @guybedford you might want to take another look. Your changes in #1816 are a bit over my head :) I'm still moving wrapDynamicImportPlugin(acorn); to Graph.ts after the rebase; is that cool?

@guybedford guybedford mentioned this pull request Jan 12, 2018
src/Graph.ts Outdated
this.acornParse = ensureArray(options.acornInjectPlugins).reduce(
(acc, plugin) => plugin(acc),
acorn
).parse;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we could add the dynamic import plugin via something like [...(this.dynamicImport ? [injectDynamicImportPlugin] : []) , ...ensureArray(options.acornInjectPlugins)].reduce(..., altering wrapDynamicImport to retun the acorn instance to match the pattern.

Not having the dynamicImport check on the plugin is my oversight though, no pressure to add that here you've done a lot already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I was thinking yesterday that it would be nice to use the same system to inject both internal and user-defined plugins. With your recent changes, it should be easy to do. Let me try.

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 in d58553b. I wasn't sure how to declare the return type of injectDynamicImportPlugin, but leaving it undeclared worked too.

@guybedford
Copy link
Contributor

LGTM.

@guybedford
Copy link
Contributor

👍 perfect!

@stasm
Copy link
Contributor Author

stasm commented Jan 12, 2018

Thanks :)

I wasn't sure what you meant by:

Not having the dynamicImport check on the plugin is my oversight though, no pressure to add that here you've done a lot already.

Would you like the plugin to check the value of acornOptions.plugins.dynamicImport? If so, I don't think it's needed. Looking at the source, Acorn uses this object to enable plugins, but plugins don't use it in any way.

@guybedford
Copy link
Contributor

I was referring to only wrapping the injection with dynamic import enabled, exactly as you did in https://github.com/rollup/rollup/pull/1857/files#diff-7c1e6304262c178e7fe268b62060e26bR166.

@stasm
Copy link
Contributor Author

stasm commented Jan 12, 2018

Ah, I see. Cool, I think this is ready to merge, then! It might make sense to keep it as two separate commits: the first one adds a new feature and the second one moves an existing feature to use the new one. Feel free to squash if you prefer, though.

Copy link
Member

@lukastaegert lukastaegert left a comment

Choose a reason for hiding this comment

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

Very nice!

@lukastaegert lukastaegert changed the base branch from master to release-0.55.0 January 21, 2018 20:51
@lukastaegert lukastaegert added this to the 0.55.0 milestone Jan 21, 2018
@stasm
Copy link
Contributor Author

stasm commented Jan 22, 2018

@lukastaegert How would you like to proceed wrt. the merge conflicts? Should I try to rebase on top of release-0.55.0 or can we simply merge it into my branch first?

@lukastaegert
Copy link
Member

It's ok if you just merge into your branch. I will use merges anyway for the final assembly of the release as there are very many different PRs that need to be consolidated. Thanks!

@lukastaegert lukastaegert merged commit 4a70558 into rollup:release-0.55.0 Jan 23, 2018
@stasm stasm deleted the acorn-injects branch January 23, 2018 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants