I've been researching about this topic, many things I read are overlapping with babel, webpack, rollup, and even browserify.
What's the standard way (or most common way) to easily publish source code written in es6 that can be consumed in browser with the script tag and in node with require?
Equally confusing is how many versions are actually needed? I read that there are three - umd, cjs and original source, but isn't umd covering cjs already? And then I also see there's an es version(folder), which I think is identical to the source, why is that needed?
If you are writing a library that supposes to run in the browser & in Node, you have several options:
Bundle it to UMD with rollup (it creates smaller bundles then Webpack, especially recommended for bundling libs).
This solution is the simplest one for you. The downside of it is if your lib allows partial imports (such as lodash allows to import only one function) then your consumers won't be able to do that, they will get your entire bundle.
Publish compiled files as esm + cjs when in package.json you point main field to cjs entry file & module field to the esm entry, in that why your lib consumers will able to import selectively parts of your lib.
The benefit of that option is that your consumers will be able to apply several optimizations to your code, such as tree shaking, module concat etc...
Related
I have three packages inside standard lerna monorepo.
client
react library
core
Core - is a shared component library with some utils (may or may not publish on npm).
React library is component library which will be shared on npm.
client is a bundled js library which will be consumed in browser with static html files.
core is a dependency in react-lib as well as client
Question 1 - How to setup core, should I transpile with tsc and bundle with tools such as rollup or vite (i personally prefer vite/rollup over webpack). or just leave it as is and import it in client and react-lib with absolute paths like 'core/src/*"?
Question 2 - can i build core in 'es' format and build client just like normal react app with either cra or vite. I tried this but i think i am missing something as final bundle doesn't seem to work in browser.
Any help would be really appreciated.
You have a few questions and I might not be able to answer them all but hopefully enough to guide you for the solution you're looking for.
Core - is a shared component library with some utils (may or may not publish on npm).
If you want to use Lerna then I guess you'll have to eventually publish the package on npm or a private repository. As an alternative, you could also use pnpm workspaces and their workspace: protocol which will allow you to link the packages in your workspace (monorepo) without ever downloading them from npm, for example if you use workspace:* then it will always use and link to the latest code from your local workspace. You could also use workspace: protocol with Lerna (or Lerna-Lite) since they both support it.
For your next Questions, I'll answer the last part of your Question 1 first because that affects the other portion of the question.
Question 1: ...or just leave it as is and import it in client and react-lib with absolute paths like 'core/src/*'?
Use absolute paths outside of the package is not a good thing to do since it will only work on your local project and you cannot publish that to npm since it will be broken for the other users. It's better to stick with the workspace and let the package use the main or exports entries defined in your package.json. In other words, it's preferable to always build/transpile and let your other package use the transpiled code and if you need to debug then make sure to also include sourcemap
Question 1: How to setup core, should I transpile with tsc and bundle with tools such as rollup or vite (i personally prefer vite/rollup over webpack)
It probably doesn't matter which one you use TypeScript, Rollup or WebPack, In one of my project I use TypeScript in watch mode, it will auto-transpile whenever you change your code, the downside is that the more packages you have then the more TypeScript threads are opened in watch mode (1x per package) but in your case if you only have 3 then it's fine, there's also this TypeScript issue that I'm following which will hopefully bring multi-threaded compilation in the future. You could also use Rollup and the concept would be the same, use it in watch mode for each package (I've done it with Vite/Rollup using vite build --watch
as explained in the next paragraph).
You can take a look at a project I've done Vue 3 and pnpm workspace using pnpm workspace with the workspace: protocol, it uses Rollup for transpiling and also uses Vite library mode which allows to bundle your library for distribution (on npm or others...), this allows you to bundle each package as a lib that is easily reusable by other projects. It's a Vue 3 project, so it's not a React project but it should give you enough ideas on how to do in React and it should help to answer your Question 2. Also that project is not using Lerna/Lerna-Lite but since it uses the workspace: protocol then it would be super easy to add Lerna on top of it in the future (basically just adding the lerna.json config should be enough)
I'm writing a library that exposes some React Components as part of its API.
I'm going to make a few assumptions here :
1 - It is going to be used inside react projects.
2 - Those projects will bundle their dependencies at some point.
3 - Those react projects can use JSX as a way of describing the UI.
4 - Their bundler of choice, Webpack if it's a create-react-app, will use babel in order to parse and transpile that JSX into vanilla JS.
Following that logic, I should be able export some JSX from an external package, because the package's code will be bundled, transpiled alongside the app.
However, when I do so in a create-react-app project, I get the following error :
SyntaxError: /Users/someone/Desktop/someproject/dist/esm/index.js: Support for the experimental syntax 'jsx' isn't currently enabled (35:13):
Add #babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add #babel/plugin-syntax-jsx (https://git.io/vb4yA) to the 'plugins' section to enable parsing.
Yes, I could transpile the JSX, maybe I will. I just don't see any reason to, if it's going to be bundled anyway. I prefer leaving transpiling tweaking and optimisation to the user.
My concern is that if I transpile it myself, I have 2 choices.
import React in scope, and using babel to turn <MyComponent/> into React.createElement(), but this will rule out the possibility to use the new JSX transform
use the new JSX transform myself, and figure out whether or not my code will be compatible with react versions prior to 17. And increase my own bundle size because there is a lot of code added by babel to make that work.
At this point, I think I'm quite excited about this issue because frankly I have no idea why I can't just export plain JSX from my package. I know I'm probably missing something obvious, like a semi-colon or whatever, but I really want to understand.
If you want some code / rollup - babel configs feel free to ask.
Tanks !
Your logic seems to make sense at first glance. But let's examine why this is a bad idea.
JSX is a special syntax that must be transpiled down to the lowest common demoninator to be understood in the browser, or by Nodejs. This is what bundlers do, and as you mentioned, anyone working with react in a node environment is almost certainly using a bundler to do this.
However, there are any number of wierd syntaxes that people may use in their code. When a bundler imports code from a node_module, if the code in those modules is not already transpiled, the bundler would need to transpile them as well. Considering un-transpiled modules may be in any number of strange syntaxes, each module would need its own transpilation instructions (think babel configuration). Having unique transpilation configurations for every node_module would be very unwieldy, not to mention having to transpile each node_module, potentially in a different way, would be bad for performance.
The generally accepted best practice for solve this problem is to simply build your package using a bunder which boils the code down to the lowest common denominator. This enables your package-user's bundler to just bring in the code in a node_module "as is".
While its probably possible to come up with some crazy babe/webpack/rollup instructions to custom-interperet your module as JSX, do you really want your library users to have to do that? Especially in the case of people using create-react-app, customizing the babel config of CRA is not natively supported, which means they will need to take extra-steps to get your library to work. Additionally, webpack defaults to excluding node_modules from js transpilations for obvious performance reasons, and CRA follows this convention. When publishing libraries, you want them to be universally useable as easy as possible to consume.
Transpiling, tweaking, and optimizing code is best left to the person who wrote it, which in the case of a react component library you're trying to publish, is you.
I am attempting to publish a small library of react UI components. These components have dependencies: Matarial UI, React Rotuer, etc;
When I build my code I get a warning about exceeding the recommended bundle size. I am at 451Kib. When I analyze my bundle I notice that 96.1% of it is dependencies that have been added to the bundle from node_modules.
Since I am only going to be publishing a handfull of components that are also going to be imported with webpack, is there a way to exclude the dependencies from my bundle and have them packaged in the bundle of any app that uses my components?
I think I need to use code-splitting, or lazy-loading, or something, but I am not sure of the proper way to get started.
Pieces of Advice I can give you:
Use webpack-node-externals to exclude node_modules from bundle. If your code depends on packages that won't be included in the user app - use whitelist to bundle them as well.
Also mark Matarial UI, React Rotuer (and probably react) as peerDependecies in package.json.
I'm trying to use webpack to refactor a backbone application that uses no modularization at all.
All the models and the views are added through script tags to index.html and are globally available.
I have read a couple of tutorials about refactoring with webpack but they only seem to take into consideration the use of webpack on applications that already use modules.
Do you know if there's a way of adding webpack to an app like mine without having to refactor every single js file?
Thanks
You will have to modify each of your js files some, but Webpack will automatically treat each file as a module for you (similar to wrapping every file in an IIFE http://benalman.com/news/2010/11/immediately-invoked-function-expression/).
What you have to do is define which modules have dependencies on other modules. Module's that have dependencies need to define those. Webpack supports many module syntax's, and comes with Commonjs and AMD out of the box. Commonjs is the recommended syntax for webpack. The best place to start is probably this guide:
https://github.com/petehunt/webpack-howto
I want to have a project structure like that:
./app/package.json
./app/src/index.js
./widget1/package.json
./widget1/src/index.js
./widget2/package.json
./widget2/src/index.js
./widget3/package.json
./widget3/src/index.js
As you can guess, this project is like an aggregation of NPM widget subprojects, and the app src folder is the real app, that uses the different widget subprojects.
I want a clear and strict separation between the widgets because my current experience shows me that developers tend to introduce unwanted cyclic dependencies between widgets even if I tell them not too.
In practice, all these widgets will be ReactJS widgets that have for most of them 3 common JS libs: React, JQuery and Lodash. Some will not need these 3 libs, some will need an additional lib like packery, react-slick or react-date-picker All these widgets should also share common test libraries like mocha.
They absolutly need to share the exact same common library versions (because obviously I don't want different versions of React in my project nor to increase my bundle size).
For example,
- widget1 could be a DatePicker widget, that depends on React + Lodash + react-date-picker (npm lib, not mine)
- The app project could depend on React + Redux + Normalizr + all the widgets and their transitive dependencies like react-date-picker
All the widgets will be Presentational components and do not need much more fancy dependencies. Some will only depend on React. Some may have a Redux reducer but as it is plain old JS code, there's no dependency needed.
The number of widgets can be quite big and I don't want to edit 200 package.json files on each library update (however I could eventually use some code to handle that).
When inside widget1/src, it should be forbidden to require code from widget2. If a widget had to be able to depend on another widget, this dependency must be set explicitly in package.json of the client widget.
I should be able to build with Webpack and test all the widgets independently. I should also be able to build and test at once the whole project, including all the subprojects, and having a single aggregated global test and code coverage report.
I don't want it to have a bad developer experience. React and Webpack permits to have Hot code reloading. If I have the real app in my browser and I modify code of a widget, I should be able to see the change live with react-hot-loader and not have to run npm commands nor hit F5.
What I look for is the equivalent of Java Maven's dependencyManagement system where you usually set versions for all your subprojects in parent POM, and versions are somehow inherited to all child projects. All child projects cannot see each others (unless explicit dependency is added), but they can say they depend on a library declared in parent project. All the lib version numbers can be set in the parent project.
Is it possible to do so with NPM and Webpack?
Or at least something close to it?
Thanks
You could put each widget in it's own git repo and install them with NPM via the repo URL. As long as each widget defines the libraries they use in their own NPM package, when they're used in the main project NPM will handle all of the dependencies for you.
Depending on the size of your team you could also sign up for NPM's private packages and host them there https://www.npmjs.com/npm/private-packages
You might want to try https://github.com/n8tz/layer-pack,
as it meets many of the needs mentioned
It allows to:
inherit several npm packages in 1 source tree
manage module inheritance ( drive node & webpack to resolve them well )
put all Webpack conf & profiles in a shared, inheritable and versioned package
etc...
Note that hot reloading with too many inherited packages can be slow.
Alternative is to use precompiled versions of your widgets & externalize theirs deps.