Skip to content

feat($context): add "weak-context" asyncMode for universal rendering#5235

Merged
sokra merged 12 commits intowebpack:masterfrom
faceyspacey:master
Jul 26, 2017
Merged

feat($context): add "weak-context" asyncMode for universal rendering#5235
sokra merged 12 commits intowebpack:masterfrom
faceyspacey:master

Conversation

@faceyspacey
Copy link
Contributor

@faceyspacey faceyspacey commented Jul 7, 2017

What kind of change does this PR introduce?
Feature slash bugfix.

It makes it possible to achieve the "universal rendering" use-case that require.resolveWeak has become a common solution for in packages such as React Universal Component and React Loadable.

The use-case is to be able to synchronously render dynamic chunks (when available) without including them in the parent bundle. These additional dynamic chunks are then left up to the developer to detect and serve as needed. Webpack Flush Chunks offers a semi-automated solution to accomplish this.

Usage (use-case):

import universal from 'react-universal-component'

const Component = universal(props => import(/* webpackChunkName: 'async/[request]' */ `./async/${props.page}`), {
  resolve: props => require.context('./async', true, /^\.\/.*$/, true, 'async/[request]').resolve(`./${props.page}`),
  chunkName: props => `async/${props.page}`
})

<Component page='Foo' />

The 4th parameter of require.context is a boolean indicating you want to create a "weak-context" and the 5th parameter is a chunkName just like you supply as a "magic comment."

note: babel-plugin-universal-import allows you to leave out everything except the import expression. This whole train is ready to go, minus this PR :)

Did you add tests for your changes?

Coming soon...

I fixed the linting errors, but I'm getting this error when running the tests:

> [email protected] test /Users/jamesgillmore/React/webpack
> mocha test/*.test.js --max-old-space-size=4096 --harmony --check-leaks

module.js:487
    throw err;
    ^
Error: Cannot find module 'webpack/lib/Chunk'

Summary
Solves this issue:
#4993

Ideally we should also update the require.resolveWeak function, but I figured I'd get this out sooner than later, since all that really matters is the use-case. Please give me tips on how to update require.resolveWeak. I wasn't successful at my first go at trying to update that. My first attempt involved rewriting the __webpack_require__ replacement in ContextDependencyTemplateAsId to be require.context instead, but obviously it's being replaced too late in the game.

Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

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

Also do the following:

  • Add your new asyncModes to the magic comment for import().
  • Update the parser plugin for require.resolveWeak to set the asyncMode for the generated context.

}

getSourceString(asyncMode, outputOptions, requestShortener) {
if(asyncMode === "weak-context") {
Copy link
Member

Choose a reason for hiding this comment

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

omit the context. It's always a context. asnycMode === "weak"


// store the dependences in a different key than `this.dependences`
// to prevent them from being bundled in the parent
this.weakDependencies = dependencies;
Copy link
Member

Choose a reason for hiding this comment

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

Instead of doing this, you need to set a weak property to true on each of this.dependencies.


getSourceString(asyncMode, outputOptions, requestShortener) {
if(asyncMode === "weak-context") {
return this.getSyncSource(this.weakDependencies, this.id);
Copy link
Member

Choose a reason for hiding this comment

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

create a new method getWeakSyncSource.

You can copy the getSyncSource method, but add a check on require like this

function webpackContext(req) {
  var id = webpackContextResolve(req);
  if(!__webpack_require__.m[id])
    throw new Error("Module '" + id + "' is not available (weak dependency)");
  return __webpack_require__(id);
};

And do the same of getWeakEagerSource with asyncMode === "eager-weak". The difference to getWeakSyncSource is that it returns a Promise so it can be used for
import(/* webpackMode: "eager-weak" */`./templates/${expr}.js`)

// falls through
case 4:
{
const weakExpr = parser.evaluateExpression(expr.arguments[3]);
Copy link
Member

Choose a reason for hiding this comment

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

pass the asyncMode as argument instead.

@@ -0,0 +1,5124 @@
{
Copy link
Member

Choose a reason for hiding this comment

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

We use yarn. Don't add this file and update yarn.lock instead.

@webpack-bot
Copy link
Contributor

Hi @faceyspacey.

Just a little hint from a friendly bot about the best practice when submitting pull requests:

Don't submit pull request from your own master branch. It's recommended to create a feature branch for the PR.

You don't have to change it for this PR, just make sure to follow this hint the next time you submit a PR.

@TheLarkInn
Copy link
Member

This is very exciting @faceyspacey congrats and thank you for submitting your first webpack/webpack PR 🎉

@faceyspacey
Copy link
Contributor Author

faceyspacey commented Jul 8, 2017

thanks @TheLarkInn I'm excited to get this right! Thanks for helping push the SSR+Splitting movement along. Not sure if you saw, but I released something yesterday that provides a nice "stop gap" solution to sokra's eventual plan of dual css + js imports:

https://medium.com/faceyspacey/webpacks-import-will-soon-fetch-js-css-here-s-how-you-do-it-today-4eb5b4929852

Will help get developers ready for Webpack's future. Maybe I can submit some PRs related to that as well. Not sure where that stuff is being built though.

@TheLarkInn
Copy link
Member

Once you get this one taken care of and @sokra and @Kovensky get ya taken care of his, I'll ping ya and get ya involved with the ICSS2/style team.

@webpack-bot
Copy link
Contributor

@faceyspacey Thanks for your update.

I labeled the Pull Request so reviewers will review it again.

@sokra Please review the new changes.

@faceyspacey
Copy link
Contributor Author

faceyspacey commented Jul 25, 2017

The requested changes/additions have been implemented, as well as tests + docs. There's 4 features this PR addresses:

Features

  1. require.resolveWeak supports context!
  2. eager-weak has been implemented, as a magic comment to import()
  3. eager-weak also suports context.
  4. require.context now has a fourth argument, which currently only supports "weak" as an option. There's no longer a 5th argument for the chunkName (not needed).

Notes

  • regarding adding the different webpackModes, only eager-weak made sense to add, as weak is purely related to synchronous rendering for require.resolveWeak + require.context.
  • several parsing-related classes/files had to be added to support eager-weak
  • the docs for webpack.js.org have been updated to reflect all this: docs($api/module-methods): add eager-weak + notes to resolveWeak webpack.js.org#1453
  • there's a demo repo to try all 4 things (directions in the readme as well as below)

Tests

All tests passed on all CIs--not sure if the code-coverage decreases are related to my tests or all the commits since I opened the PR.

DEMO

The demo showcase the 4 features, as well as a 5th for babel-plugin-universal-import, which brings the final concept together. 🚀

Here's how you get it:

git clone https://github.com/webpack/webpack
cd webpack
git checkout https://github.com/webpack/webpack.js.org/pull/1453 # using hub cli tool
yarn

cd ..

git clone https://github.com/faceyspacey/universal-demo.git
cd universal-demo
git checkout resolve-weak
yarn

yarn start
# or
yarn run start:prod

the demo aliases webpack, which is expected to be a parallel directory named "webpack"

Additional notes are in the readme for the resolve-weak branch. In short, open src/components/App.js and comment/uncomment the 5 different blocks :)

Notes on new eager-weak asyncMode

The thing I interpreted from the eager-weak feature request was that it should also generate additional chunks like lazy does. Otherwise it would be the same as eager. However the use-case for this is rare, since import with its default lazy async mode is more powerful. lazy will fetch additional chunks from the server if it doesn't have them, and if it already does, it won't (even the first time it's called).

So essentially eager-weak is a crippled lazy async mode. But it's the only thing I could determine that matched the feature request.

The common use-case is the one that's the basis of react-universal-component and webpack-flush-chunks, which is serving all chunks rendered on the server so the client can also synchronously render, and then being able to get additional chunks on-demand as the user navigates the app. eager-weak will not be able to fetch additional chunks as the user navigates.

Either way, eager-weak is in, and perhaps there are some use-cases I'm not aware of.

@webpack-bot
Copy link
Contributor

Thank you for your pull request! The most important CI builds succeeded, we’ll review the pull request soon.

@webpack-bot
Copy link
Contributor

It looks like this Pull Request doesn't include enough test cases (based on Code Coverage analysis of the PR diff).

A PR need to be covered by tests if you add a new feature (we want to make sure that your feature is working) or if you fix a bug (we want to make sure that we don't run into a regression in future).

@faceyspacey Please check if this is appliable to your PR and if you can add more test cases.

Read the test readme for details how to write test cases.

@sokra sokra merged commit ed50812 into webpack:master Jul 26, 2017
@sokra
Copy link
Member

sokra commented Jul 26, 2017

Thanks

@dan-lee
Copy link

dan-lee commented Aug 8, 2017

Awesome work! 🎉
Is this going to be released sometime soon?

@dan-lee
Copy link

dan-lee commented Aug 8, 2017

image
Well, that was quick 😇

@faceyspacey
Copy link
Contributor Author

YAY!!

skipjack pushed a commit to webpack/webpack.js.org that referenced this pull request Aug 11, 2017
Add eager-weak + notes to the `resolveWeak` section of the
Module Methods page. The corresponding webpack PR that added
this functionality was merged and released:

webpack/webpack#5235
@sirlancelot
Copy link
Contributor

Is there a way to use the magic comment to name the chunks?

I've tried this and it doesn't work:

const pages = require.context(/* webpackChunkName: "[request]" */
	"@/pages/", true, /^\.\/.*\.vue$/, "lazy")

It seems that when chunks are created through require.context, they don't get assigned a chunk name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants