-
Notifications
You must be signed in to change notification settings - Fork 1
[16] Introduce "load strategies" to the webpack package, and update react-pointcuts to cater for dynamic import using "React.lazy". #38
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
Draft
TomStrepsil
wants to merge
96
commits into
ASOS:main
Choose a base branch
from
TomStrepsil:feature/16-support-lazy-splits
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* rename to proper module namespace * update docs links * update versions * web toggle point in readme title * fixup changelog from revised 0.x range * 2.0.0 -> 0.5.0 in oss version scheme * fix broken link syntax in CHANGELOG * consistent quoting * more version history issues * fixup module name in jsdoc * add web remove sdkInstanceProvider * remove SDKInstanceProvider * fixup jsdoc dedupe * tweak * clarity re: ssr package * casing etc
* update workflows * version * typo * update chromium linux snaps * versions for serve update * package.json repository field * update root package.lock * bugs & directories/doc fields * fix changelog --------- Co-authored-by: Tom Pereira <[email protected]>
Co-authored-by: Tom Pereira <[email protected]>
ensure import.meta.filename works for commonjs output of react pointcuts package
correct errant lint change for useContentEditable remove needless Suspense wrapping content-management example
…tory add note re: use of node scoping in NextJs
|
Phoar, this is a big one 🚨 |
|
Draft - waiting for merge down after #55 is merged, since it carves significant chunks from this PR |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Issue
Allow for different module loading strategies, adding the ability to code-split variation code.
resolves #16
Details
webpackpackage updatesUpdate the webpack plugin to ingest a strategy for generating import code, such that each point cut may define how code is accessed at run-time.
At present, all code is statically required at application bootstrap, via the use of
import.meta.webpackContext. Unadorned, this method causes all variation modules in a potential join points to be statically imported, thus all side-effects of all modules are run. This requires toggled code to be side-effect free, or have side-effects that do not interfere with each other / can act idempotently, when identical. It also means all the varied code is included in the bundle that contains the base/varied files, potentially bloating the base experience.Although the
import.meta.webpackContextmethod supports adornment with a "mode" parameter that can takelazyandeageras a means of deferring import, these necessarily output aPromise, thus can only be compatible with consuming code that is asynchronously importing the module to be varied.To support other schemes, such as a deferred
require, the plugin has been updated to accept an optionalloadStrategyconfiguration parameter. These strategies manually provide that which the webpack context module api afforded, preparing all potential codepaths based on the variations found by the point cut configuration.load strategy factories
Three factories for such strategies are provided, as package exports:
staticLoadStrategyFactoryThis produces code equivalent to the
import.meta.webpackContext/ context module api scheme (so parity with the current version), whereby all variant modules are statically imported at the point the chunk entrypoint is imported. At this time, all side-effects will execute in series.This is no longer the default option, since the common case assumes the overhead and impact of side-effects for an irrelevant module selection should be avoided. However, if parity with the existing scheme is deemed desirable, perhaps to avoid costly just-in-time code execution, this option is still available.
deferredRequireLoadStrategyFactoryThis produces modules wrapped in a synchronous function, that will
requirethe module at the point that it's selected.Despite using
require(a commonJs feature), it appears compatible with output.module, with theserveexample updated to use this output format.This is the default load strategy, if one is not specified.
N.B. Due to oddness in NextJs 14 see here this produces
Promiseout of the Webpack build, so appears incompatible. Next 15 (app or pages router) does not appear to suffer the same.deferredDynamicImportLoadStrategyFactoryThis produces modules wrapped in an asynchronous function that dynamically imports the module at the point that it's selected. This hence returns a
Promise, and the toggle point needs to be compatible with asynchronous access.Webpack, unless overridden, will code-split the point cuts, creating non-initial chunks. This should ensure that the "base" version of the application has no increased initial chunk size.
Strategy factory modules
Since a strategy needs to represent both code that is executed at compile time, and provide code that is baked into the compilation to be executed at run-time, the interface of these modules is somewhat strange.
The default export represents the factory for a
importCodeGenerator, used to generate the import code itself at compile-time.It can also have named exports that are baked into the compilation to "pack" and "unpack" the modules that the
importCodeGeneratorhas accessed. This provides the mean to defer import and/or execution, or otherwise mutate the module at the point of storage in "feature store", and mutate back into an executable form, when it's deemed the module is relevant for the toggle state.react-pointcutspackage updatesThe package provides it's own load strategy factory, that composes the
deferredDynamicImportLoadStrategyFactory, for itsimportCodeGeneratormethod, but setting the "pack" option to wrap the dynamic import statement (comprising a load function) inReact.lazy.The
withToggleHandlerFactoryis updated to detect lazy-wrapped modules, wrapping in Suspense if detected. The suspense boundary is backed by anullfallback, to allow server-side rendering to maintain the existing markup as a lazy bundle is downloaded prior to hydration.Where available (React 18+) it also wraps the component in
useDeferredValuesuch that reactive feature stores can change the selected variation whilst maintaining the mount of the prior variation as new chunks are downloaded. This avoids any "flash of no content" as the variant selection is changing.Updated the
withToggledHookFactoryto support "unpacking" of modules.nextexample updatesAdded a new "content management" example as a fixture for the previously un-tested
withToggledHookFactoryAdded note regarding NextJS-compiled
react-isalias'ingScout Rule
webackpackageTogglePointInjectionplugin to have aPluginsuffix, supporting a standard webpack naming conventiontoggleHandlerpackage exports to be "factories", now available under atoggleHandlerFactories/exports pathnextpeer dependency, there's no reason to be explicitnode_modulespart of the app root, and something odd has happened) are skippedreact-pointcutspackagetest/automationuiscripts for playwright ui modefeaturespackagessrBackedReactContextstoreserveexampleproductionwebpack mode withsource-mapdevtool andsource-map-loader, for clarity when using dev toolsnextexampleREADME.mdxfilestoggle-point.d.tsfromtsconfig.jsonexpressexamplesource-mapdevtool andsource-map-loader, for clarity when using dev toolsoutput.module, to help demonstrate this compatibilityrepo root
reactandreact-domfrom repo root package.json, no longer appears required20.8sinceimport.meta.resolveused in some packages / exampleseslint-plugin-workspacesto0.11.0after addition of ESLint9 supportUpgrade guide
This change will represent breaking changes in consumers of the webpack package.
TogglePointInjectionnamed export is now namedTogglePointInjectionPluginThe point cut configuration needs to be modified for the plugin thus:
togglePointModuleoption is renamed totogglePointModuleSpecifiertoggleHandleroption is renamed totoggleHandlerFactoryModuleSpecifierN.B. The existing code has a "load strategy" based on
import.meta.webpackContext.For complete parity with this, a strategy built by the
staticLoadStrategyFactoryshould be specified (as theloadStrategy), but would suggest trying the new default which should be functionally identical, and avoid needless side-effects. However, the pages router in NextJs 14 and below appears to convertrequire()intoimport()(see issue, and above), so the default should be avoided in this setup.CheckList
main.