parallax / jsPDF

Client-side JavaScript PDF generation for everyone.
https://parall.ax/products/jspdf
MIT License
28.72k stars 4.62k forks source link

Create modular build of jsPDF #839

Open agilgur5 opened 7 years ago

agilgur5 commented 7 years ago

Right now nearly all jsPDF plugins and polyfills are packaged together with the main build, resulting in the minified bundle being ~300KB in size and growing.

Developers should choose which plugins and polyfills they want, instead of jsPDF choosing for them. I only use images, text, and pages, and only need FileSaver as a polyfill. The rest of the functionality is entirely unnecessary to me and unnecessarily uses up the network bandwidth and CPU when a user's browser downloads + parses the giant file (not to mention it's just one of many other dependencies in a project). There's no need for me to have to install and package up all of html2canvas and downloadify when I don't even use them. Or all of requirejs (which causes compatibility problems very easily), Blob.js, and the canvas implementation, if I don't need them. etc. etc.

With a modular build I would do something like:

import jsPDF from 'jsPDF' // basic jsPDF functionality
import 'jsPDF-addImage' // add addImage functionality to jsPDF
import 'file-saver' // FileSaver polyfill

This would drastically reduce the size of the core jsPDF library, abstract out all the logic that is handled by plugins, make creating, updating, and managing plugins significantly easier, and entirely remove polyfills from the codebase. People would also be able to easily fork and create their own versions of existing plugins (like https://www.npmjs.com/package/jspdf-autotable)

As the plugins + polyfills are all in separate directories already and imported very similarly in https://github.com/MrRio/jsPDF/blob/master/main.js , this doesn't seem like it would be altogether difficult to implement

MrRio commented 7 years ago

Yeah this is an interesting problem. How do other libraries tackle it?

agilgur5 commented 7 years ago

There are several actionable steps

Further steps would involve automated testing and automated builds, such that a new version can be automatically released on NPM as soon as any change happens with very little hassle, among other steps.

I can get started on this perhaps by next week, as there is noticeable lag my users currently see due to the load time increase by this library. It would have to be done as several PRs and as the current maintainer it's up to you how you want each of the plugin repositories to be created (under an umbrella organization or not) and who the maintainers should be.

simonbengtsson commented 7 years ago

I have been thinking of this as well. What is the status of this now @agilgur5? Any progress?

Inspired by angular and bootstrap I suggest we export umd modules of jspdf core, plugins and third party code into a new dist folder called for example lib. These could then be used like @agilgur5 proposed above with es2015 modules or something like this with commonjs:

var jsPDF = require('jspdf/lib/core');
require('jspdf/lib/total-pages');
require('jspdf/lib/polyfills');
// Note that require('jspdf') would still import `jspdf.debug.js` and therefore preserve backwards compatibility and would be easier to use for developers

Since this only adds a folder with additional dist files it could be done with complete backwards compatibility. Would you accept a PR adding this folder @MrRio?

Discussion

After reading @agilgur5 comment above I looked up which and where third party code are actually in use by jspdf. Here is a categorized list of current (v1.3.2) included third party code:

Dependencies

Plugin dependencies

Polyfills

@agilgur5 also mentions other interesting things, but I think simply enabling importing parts of jspdf would be a good first step.

agilgur5 commented 7 years ago

@simonbengtsson I have not started this. This requires a fundamental shift in how the library is maintained moving forward (otherwise the proposal is useless), as well as potentially the creation of an umbrella organization, and therefore all the maintainers should agree to this. As you can see, not a single one, including @MrRio, has even replied to my proposal. This is the way all modern libraries are organized, so the hold up is unfortunate.

Exporting to a dist or lib folder is also possible, though I think it's far easier for plugin maintainers to be able to have separate repositories for their plugins. A plugin should not need to be PR'ed and approved against the core codebase as it's entirely independent of the core codebase (aside from a peerDependency to it).

That being said, I like the idea of keeping "complete backwards compatibility" (jspdf for whole, jspdf/lib/core for just core) for some deprecation window.

Thanks for listing out all the dependencies @simonbengtsson! File-saving and compression I believe should also be opt-in and can also be made as plugins, which would move the first two to "Plugin dependencies". Good note that Blob.js is extraneous! I personally think all of the polyfills are now extraneous as Microsoft no longer supports < IE11 -- if a developer needs/wants to support them, they can import polyfills however they want (e.g. ES5 core-js) instead of using polyfill.js which may be duplicative of existing polyfills in their own codebase.

sgelb commented 7 years ago

I'd really appreciate some work in this direction. Can you comment on this idea, @MrRio?

simonbengtsson commented 7 years ago

I agree with all your points @agilgur5. However I'm leaning towards that the complexity of having different repos would not be worth it. It might be easier to go for the monorepo approach for now. At least until an active maintainer wants to take responsibility of a certain plugin.

agilgur5 commented 7 years ago

@simonbengtsson using lerna and managing a monorepo has it's own overhead as well that should not be ignored -- it's not altogether different from one group maintaining multiple repositories/plugins except that issues are all in one place (which may not be preferable for a package that gets so many issues already, many of which are low-quality/not descriptive). The monorepo approach namely works well for Babel because the codebase is split into lots of tiny packages + plugins that are meant to be composed together. Very few codebases are similar in structure, and many of the jsPDF plugins are quite large. Also, it's important to note that exporting to a dist or lib folder is different from a monorepo -- the former means when you npm install/yarn add jsPDF, you have to download all plugins and plugin dependencies in one go (which in some cases are quite large), the latter means you choose what you want to download. Both mean you choose what you use.

In any case, I don't see much value in discussing repository structure unless one of us will be an active maintainer, as all the current maintainers are inactive and don't follow any modern repository structure techniques yet.

MrRio commented 6 years ago

I think we need to fix this soon. I agree that we need to modernise the way jsPDF can be imported. Thank you for your comments so far.

MrRio commented 6 years ago

Our current plan is to try and get any currently working and mergeable PRs merged before the refactor starts. The main one is utf-8 support. We’ll then format using StandardJS or a popular ESLint config, then we’ll start modernising each file.

The way the plugins are currently make them very ‘pick and mix’. We should try slim down the core also, but we need to run some analysis on which plugins rely on which feature.

Uzlopak commented 6 years ago

@MrRio Can I help you somehow with this task?

Uzlopak commented 6 years ago

Something I coded for fun and PoC https://arasabbasi.github.io/jsPDF/builder/index.html

alex2wong commented 5 years ago

Any update on modular build of jsPDF ? currently the jspdf.min.js are still over 300Kb. I know this refactor work would involve much work, not sure how long it would take to modernize the structure of core and plugins.

Uzlopak commented 5 years ago

2356

github-actions[bot] commented 4 years ago

This issue is stale because it has been open 90 days with no activity. It will be closed soon. Please comment/reopen if this issue is still relevant.

simonbengtsson commented 4 years ago

That would be sad so will comment to keep it open.

HackbrettXXX commented 4 years ago

Yes, this is definitively a good idea but impossible without extensive help from the community. #2804 is a small step in the right direction (at least the polyfills are no longer bundled). Let's keep this open for the future.

mahenk1 commented 4 years ago

This is an amazing idea ! Posting to keep it alive !

simonbengtsson commented 4 years ago

Would a PR with the libs dist folder discussed above be considered for merge? If so I can make an attempt.

HackbrettXXX commented 4 years ago

@simonbengtsson a pull request would be very much appreciated :)

You can probably base on the changes I made in #2804 and add a couple more entry points to the rollup config for the files in the dist/lib folder.

Things to discuss:

I think the first two options are better than the third one, because the folders won't be as cluttered in code completion, etc.

What are your thoughts?

Before you start with the pull request, I think it is best if I merge #2804 first.

HackbrettXXX commented 4 years ago

2804 is merged now.

HackbrettXXX commented 4 years ago

The getTextDimensions method from cell.js should probably be moved the core or split_text_to_size. With #2824 it will have a dependency on split_text_to_size.

esaesa commented 1 year ago

Good idea