-
-
Notifications
You must be signed in to change notification settings - Fork 101
perf(store:vite config): Improvements for Dynamic Loading and Bundle Optimization #131
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
base: main
Are you sure you want to change the base?
perf(store:vite config): Improvements for Dynamic Loading and Bundle Optimization #131
Conversation
✅ Deploy Preview for ap-template-playground ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
ef41d90 to
d01837e
Compare
|
Thanks @nitro56565 Looks great to me! The Asynchronous plugin loader was of great help here, Noticed the modifications you made to the store initialization process and use. Could you please explain the use case of the new library added in the |
|
Hey @Vinyl-Davyl Thank you for going through my code, |
|
Thanks @nitro56565 this PR looks quite extensive and helpful. Can please help with some clarity on the following:
Do you have any example where error handling was improved because of this?
What does this optimise?
I don't think we take input to load any sample that the app may want to match to. All samples are hard coded as of for now. How does this improve modularity?
Afaik know template-mark in |
vite.config.ts
Outdated
| optimizeDeps: { | ||
| include: ["immer"], | ||
| include: ["immer", "zustand"], | ||
| exclude: ["typescript"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vite.config.ts
Outdated
| }, | ||
| build: { | ||
| rollupOptions: { | ||
| external: ["typescript"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even this for that matter.
|
Hi @sanketshevkar Thank you for taking out time to review my PR.
by dynamically importing the rebuild processing libraries, we now have a single point of failure (loadMarkdownProcessingLibs()), which makes it easier to add centralized error handling even though I haven't added a try catch block around the loadMarkdownProcessingLibs() in this change, I can work around implementing that. but as of previous implementation static imports meant that any issue with these libraries could cause failures throughout the entire app lifecycle. which wont happen with the current code as the structure of the code now allows for better error isolation.
These changes are reallly about future-proofing the code. By starting with empty state values, the store isn’t tied to template preset defaults, making it easier to adjust and avoid assumptions about initial template.
I did check for the code and also tested it, and yes you are correct about it I looked for alternatives but haven’t found any viable ones yet, therefore I'll put the typescript back in the bundle (this would degrade the performance of the whole application but we need this at runtime rightnow to process the markdown), I also check if we could dynamically load the typescript from Frontend instead of package and use that for the markdown but I dont think that would technically work. |
Signed-off-by: nitro56565 <[email protected]>
Signed-off-by: nitro56565 <[email protected]>
Signed-off-by: nitro56565 <[email protected]>
d01837e to
9387446
Compare
| async function loadMarkdownProcessingLibs() { | ||
| try { | ||
| const [{ ModelManager }, { TemplateMarkInterpreter }, { TemplateMarkTransformer }, { transform }] = | ||
| await Promise.all([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nitro56565 good implementation, just that lets say even if once fails then due to promise.all, all the import should fail. Can we use promise.allSettled in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Owaiseimdad If any one of the packages fail then there is no reason to hold up with other packages as they all are dependent on each other and for fallback I have put a catch block to handle the errors
| try { | ||
| const [{ ModelManager }, { TemplateMarkInterpreter }, { TemplateMarkTransformer }, { transform }] = | ||
| await Promise.all([ | ||
| import("@accordproject/concerto-core"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not scalable, use a array and create a map of it inside the promise.
Ex:
`
var mod = [
"@accordproject/concerto-core",
"@accordproject/template-engine",
... more
]
await Promise.all(mod.map(module => import(module)))
`
Something like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sanketshevkar do we have any plans of scaling up on core packages, I am not sure if we need to implement it by mapping each package?
src/store/store.ts
Outdated
| editorModelCto: playground.MODEL, | ||
| data: JSON.stringify(playground.DATA, null, 2), | ||
| editorAgreementData: JSON.stringify(playground.DATA, null, 2), | ||
| sampleName: "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use of playground was to avoid using the empty string in first place. Any reason why playground is removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Owaiseimdad the initial load is kept empty considering future plans of pulling up the record from local storage and keeping it persistent even after refresh , also this approach will also help us while implementing handling AI tuned data (future plan to implement upload file and populate system).
| console.error("Error rebuilding the template:", error); | ||
| throw new Error("Failed to process the template"); | ||
| } | ||
| }, 500); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 500ms and not 300ms or more than 500ms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we already had 500ms from before, I did'nt change the value but It would be a great suggestion checking for lower latency as well.
| } | ||
| } | ||
|
|
||
| const rebuild = debounce(async (template: string, model: string, dataString: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use throttle here? Any particular reason for debounce?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can experiment with throttling, but let's keep it for a separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sanketshevkar @Owaiseimdad I believe debounce suites best over here, reason behind it is that user may type fast and if we implement throttling then after every interval it will update the UI instead of waiting even if the user is still typing, incase of debouncing the interval will start after the user has stopped typing therefore I think debouncing would be better here.
|
Hi @sanketshevkar I have made some changes and updated this PR, can you go though it? |
I'm still confused, you've mentioned centralised error handling in the start and in the end you've mentioned that the changes allow better error isolation. Is there any example where I can see this, where an actual error is thrown?
Do you have an example of this and how your changes fixes that failure? What kind of failures are we talking about? |
Is there any numerical evidence for this slowdown? From what I understand, even with your changes typescript from |
|
@nitro56565 can you also please address @Owaiseimdad 's comments? |
|
Hi @sanketshevkar, Let me clarify the points around centralized error handling and error isolation: Error Isolation was not in term of separating the errors but to handle failures as deferred until runtime. If any one of the libraries failed to load example a corrupted module, missing dependency, or a breaking library update, the entire app would fail on startup, leading to a white screen, allowing the app to not stay functional. Currently I dont have an example to demonstrate an actual package failure but I can simulate an example |
@sanketshevkar When I had excluded the typescript which was coming from template engine it was not actually getting bundled up with with our UI bundle and typescript is a pretty heavy package, I am attaching some before and after visuals of what exactly the performance degradation happend. With Typescript from Template Engine |
Ohh I guess there is some confusion here, I was trying to remove the typescript before this PR at that time I was trying to manipulate the template engine from that repo by pushing the typescript as dev deps but when you told me about the need of typescript(for logic) from the above message and through discord communication I stopped working on that part and I only excluded the typescript from playground bundle and not template engine bundle. note - Currently as you mentioned that we do need typescript I have put back the typescript package back to playground bundle. |















Summary
This PR introduces changes aimed at reducing the initial bundle size and improving runtime performance by deferring heavy module imports until they are needed. The changes also streamline sample loading and enhance the Vite build configuration.
Changes
Dynamic Import of Markdown Processing Libraries:
@accordproject/concerto-core,@accordproject/template-engine,@accordproject/markdown-template, and@accordproject/markdown-transformwith a newloadMarkdownProcessingLibs()function that loads these modules on-demand.rebuildfunction is called.Refactored Rebuild Function:
rebuildfunction is now debounced directly around an async function that leverages dynamic imports.Sample Handling Enhancements:
sampleName,templateMarkdown) instead of preset values from the playground module.loadSamplemethod to dynamically import the playground sample if a matching sample isn’t found, improving modularity.Vite Config Improvements:
getPlugins()) to dynamically load thevite-plugin-chunk-splitplugin. This helps in code-splitting and optimizing bundle chunks.optimizeDepsandbuildoptions to include and exclude dependencies appropriately for now I have excludedtypescriptas that was one of the biggest chunk from@accordproject/template-engine/node_modules/typescript/lib/typescript.jswhich isn't required as of now in the runtime, leading to more efficient dependency management and build performance.mergeConfigfor better maintainability between Vite and Vitest setups.Screenshots or Video
GTmetrix-report-playground.accordproject.org-20250226T050752-dxnPRm2W.pdf
GTmetrix-report-regal-nougat-d1db8f.netlify.app-20250226T044504-DVcLGT7h.pdf
Related Issues
Author Checklist
--signoffoption of git commit.mainfromfork:branchname