alexisvincent / systemjs-tools

(dev)tools for working with SystemJS
MIT License
43 stars 5 forks source link

Support optional bundle expression config #20

Closed kwesterfeld closed 7 years ago

kwesterfeld commented 7 years ago

As discussed regarding performance of rebuilding my large app and retain HMR from issue #14, I decided I really needed to be able to specify a bundle expression as part of the config.

I will submit a PR for this tweak.

alexisvincent commented 7 years ago

@kwesterfeld Thanks for this. This is actually probably a good opportunity to do the dynamic building I was taking about, which would solve your problem and fits in with the long term goal of the project. Let me know if you want to give this a shot. Essentially when dynamic builds hit I would want to remove any config specified bundle arithmetic.

Update: Later comment basically invalidates this comments ideas

Here's the outline: When a request comes in, instead of using the bundleTrigger we just listen for /bundle?expression=(somemodule.js+sdfsdf.js)&options={sdfsdfsdf} and then dynamically perform this bundle operation.

The problem with this is that the bundle arithmetic uses a bunch of characters that are reserved for query strings [ ] & + * ( ) / and options would also cause issues here.

Not completely sure how to solve this. Escaping the characters will lead to unreadable url's which we don't really want. Optionally in systemjs-tools we could provide a helper tool that takes bundle commands and spits out encoded urls, but this could be annoying for people.

Could also have in your index.html

// (secret.js - app.js) & [moo-cow.js]; {sourceMaps: true}
<script scr="/bundle?expression=%28secret.js+-+app.js%29+%26+%5Bmoo-cow.js%5D&options=%7BsourceMaps%3A+true%7D">

Then you could paste the // (secret.js - app.js) & [moo-cow.js]; {sourceMaps: true} expression into the helper and it could generate the url.

The nice thing about this is that the bundler can now return arbitrary bundles dynamically (with auto caching).

Another way, although I like this less since the loss of flexibility is to have an entry in the config for dynamic builds:

// config

{
  bundleMappings: {
    'app-dependencies.js': {
        expression: '(secret.js - app.js) & [moo-cow.js]',
        options: {sourceMaps: true}
    }
  }
}

Then in script <script src="/app-dependencies.js">

Let me know what you think or if you have some other ideas

alexisvincent commented 7 years ago

Both of these should be trivial to implement via a simple extension to the bundle handler

alexisvincent commented 7 years ago

After thinking about it a bit I actually think that using bundleMappings is a far better idea. This would also tie better into a production build story.

So I'm thinking that we augment the config with a mappings key containing expression and options (for bundle overrides) keys. This could later be extended to include other kinds of file mappings.

{
  ...,
  mappings: [
    {
        match: 'app-dependencies.js', // string for now, maybe a function later
        builder: {
            expression: '(secret.js - app.js) & [moo-cow.js]',
            options: {
              sourceMaps: true
            }
        }
    }
  ]
}

thinking this can later be extended so that it allows for arbitrary handlers for the matched expression. Where builder is just a plugin.

Anyway, in this form it should be quick to extend the builder function to support this and we can deprecate bundleTrigger and remove it from doc's.

This will also mean we can implement a bundle command that takes all mappings with a builder key and spits out the bundles.

kwesterfeld commented 7 years ago

I can rework my changes to conform to the "mappings" spec you outlined here. I like the flexibility too.

The dynamic bundling idea is kind of cool, but really didn't sit well with me because I would not want my .html page, even in dev mode, to have that type of configuration. What I like about my jspm workflow is that I can do a bundle --inject command for my app and vendor .js bundles, and most everything stays the same. The mappings are handled in jspm.browser.js.

With the bundle mappings here, I can express how to reconstruct the two bundles here. I would find it completely awesome if I could somehow create a mechanism with this tooling that I could configure to do the app/vendor split on the fly. The issue is, the bundle injection to the jspm.browser.js file is a chicken/egg issue, because you don't know what mappings there will be prior to the split, in order for the app to trigger anything. Perhaps I could noodle this a bit more and couple the "lazy" flag with the initial bundling on startup, and inject mapping config on the fly there.

On Mon, Sep 18, 2017 at 4:19 AM, λex Vincent notifications@github.com wrote:

After thinking about it a bit I actually think that using bundleMappings is a far better idea. This would also tie better into a production build story.

So I'm thinking that we augment the config with a mappings key containing expression and options (for bundle overrides) keys. This could later be extended to include other kinds of file mappings.

{ ..., mappings: [ { match: 'app-dependencies.js', // string for now, maybe a function later builder: { expression: '(secret.js - app.js) & [moo-cow.js]', options: { sourceMaps: true } } } ] }

thinking this can later be extended so that it allows for arbitrary handlers for the matched expression. Where builder is just a plugin.

Anyway, in this form it should be quick to extend the builder function to support this and we can deprecate bundleTrigger and remove it from doc's.

This will also mean we can implement a bundle command that takes all mappings with a builder key and spits out the bundles.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/alexisvincent/systemjs-tools/issues/20#issuecomment-330154973, or mute the thread https://github.com/notifications/unsubscribe-auth/AAq8l5xzhN_ogERcY3-9fhA6cuCaUFLuks5sjid4gaJpZM4PaVj_ .

kwesterfeld commented 7 years ago

OK, this is working well. After implementing this idea, here's how my application config ended up looking:

  mappings: [
    {
      match: 'smc/bundle/dependencies.js',
      builder: {
        expression: 'app/main.js - bundle/components.js',
        options: {
          outFile: 'smc/bundle/dependencies.js'
        }
      }
    },
    {
      match: 'smc/bundle/components.js',
      builder: {
        expression: 'app/**/* - [app/**/*] - [app/**/*.html] - [app/**/*.css] - [app/**/*!text]',
        options: {
          outFile: 'smc/bundle/components.js'
        }
      }
    },
  ],

About the only oddity is the requirement to individually specify outFile per mapping, which I can live with. I also ended up implementing regex mapping types, and adhering to the original simple workflow where dependencies.js is an implied default for the bundle trigger.

I'll repush my changes to my branch for your review.