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.
Related
I am currently using the babel compiler to compile my React component libraries. I made this decision based on the fact that Create React App also uses the babel compiler. However, I am now finding that using babel to create libraries is more complicated. You need to introduce the TypeScript compiler anyway to generate type definitions. Thus I need to maintain two sets of configurations, babel and tsconfig. This complexity gets further amplified by the fact that I have multiple such libraries in a monorepo.
So my question is:
Is there any good reason to use the Babel TypeScript compiler to compile React libraries (vs tsc)?
Am I going to loose any features if I get rid of Babel completely and rely only on tsc?
After a lot of research, it was clear to me that when creating a TypeScript library from scratch, it does not make sense to use the Babel compiler. Babel does not do any type checking of TS files. For that I have to run the TypeScript compiler anyway. Hence it is better to use the TypeScript compiler from the get go. For create-react-app, Babel was a better choice because they were. already using it for ES6 builds.
Furthermore, I picked up a CLI/template for creating my library. It's called TXDX. It really makes the job of creating a TS library very easy.
I'm starting journey with React and have a very important question for me : as I'm not familiar with Webpack can I add it later - after I finish my project? Won't it cause any problems in file structure and so on ?
You could add webpack once your project is complete, but this isn't really good practice.
Webpack allows you to work in ways which would not be possible without such a tool. Webpack does lots of things, but most of the things which it does are useful when you're developing code, not once you're done developing. If you were to do add webpack once you've finished your project, you would have set up your code in a way which doesn't take advantage of webpack.
For React, webpack makes it easy to split your components into multiple files, and it allows you to transform files containing jsx.
Webpack can be a pain to configure, but there are options to make your life easier, like create-react-app. Alternatively, parcel has a lot of the functionality of webpack, without the config.
I was looking at the wiki page of vue.js and saw this:
When compared to React development, Vue can be integrated to an
existing web application much more easily. Normally, a web application
can start using Vue immediately by simply including the Vue.js
JavaScript library. Usage with Webpack or Browserify, are not strictly
necessarily. This is in stark contrast to React development where
Usage with Webpack and Babel is unavoidable, therefore making
converting existing web application much more difficult.
Can someone explain why React needs webpack and babel? I thought you can just drop in the link to the CDN of React and it will work?
No, Babel and Webpack is not necessary for React stack. You can still find other alternatives to build your favourite stack such as Browserify and Gulp.
However, if you want to make things easier, I do recommend you learn and use Babel and Webpack together with React because:
You can use modules.
You can use JSX with ES6.
It support a lot of browsers
You can use more advanced features (async/await) etc
With webpack
You can use different loaders for sass, less, postcss etc
You can use different plugins to optimise your build such as Uglify, HotModuleReplacement, Chunks etc
There are many more advantages to use webpack which you can find here
Can someone explain why React needs webpack and babel? I thought you can just drop in the link to the CDN of React and it will work?
React doesn't "need" babel or webpack but the library is built on the concept of using ES6 javascript syntax and JSX (essentially HTML in JS).
React however can be used without ES6 and JSX which would remove the initial need for Babel but you would lose the potential benefits of ES6 and JSX.
Webpack is separate from React but commonly used in React projects for reasons MattYao mentioned.
In comparison to Vue, JSX to me brings the benefits of containing html, css and JS in one file/component which is what Single File Components in Vue also try to achieve. These would also require a build step using something like webpack as well.
React uses JSX syntax to make writing react more familiar to we the programmers. However, the browsers do not understand JSX. Therefore babel is required to convert your JSX into javascript which is then manipulated by ReactDom and then input into your DOM for the browsers to understand.
I am updating the build process for an Angular 1.5.8 application, to allow development on Typescript.
After an over-complicated experience with Grunt, the current build process is simple and only uses Gulp and Browserify to build two bundles: my-lib.js and my-app.js. This way, the library, which is bigger, but more stable than my application code, doesn't have to compile so often and the compilation of the application-domain code only takes 0.1 sec. I am happy with that -- as well as the other developers.
Now we are looking forward to migrate to Angular 2.0 and want to start development in Typescript, but I'm not sure on how to integrate it on the build process and even the best approach on how to do it: should it be preferred to use tsc only to compile Typescript into Javascript and let Browserify handle the dependencies? Or should I use tsc as my main build tool and let it resolve dependencies, create mapping files and make the bundles?
Both Typescript and Gulp are evolving very fast and I cannot find documentation for this use on their documentations (1, 2). I would appreciate feedback from experienced people also working on the latest versions of these technologies.
Moving comments to answer:
We have doing very similar exercise at my organization, towards the end of it.
We have used webpack for most of the stuff, though webpack feels little grunt'y and after gulp, I am not a big fan of grunt.
We have used webpack to convert ts to js, bunding, minification. We have not used it yet for html to js string or css in js.
regarding the di part, you will have to worry about only js to ts. ts to js is not an issue as it is taken care by angular's string based di. regarding ts to ts, you will need to defined needed interfaces. Those will help you in moving those js to ts in future. Better to start with core components with least dependencies.
EDIT
Just to answer the part regarding the advantages of having gulp: specifically in the migration scenarios is, the upgrade is not going to happen in one go, so whatever is moved to TS should be handled by tsc and remaining by gulp.
Also gulp is much larger than just ts to js, we are still using it to create deployment package, inject js into html, fixing boostrap font paths, converting small images to base64, etc etc....
tsc has one purpose: to transpile (compile) typescript files.
gulp, on the other hand, is a build tool, which means it can run various tasks including compiling typescript, sass, minification, concatenation etc.
You can look here for an example on how to incorporate typescript and browserify using gulp: https://www.typescriptlang.org/docs/handbook/gulp.html
Another approach, is not to use gulp at all, but rather use npm scripts, you can see a sample here: https://medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.a7lwcmpaf
I believe the absolute simplest way to do this is to use Zwitterion. You can read this article for a quick introduction.
Zwitterion allows you to include TypeScript directly into the browser through normal script tags. All features of TypeScript are automatically available, because the code is transpiled server-side on a file-by-file basis as it is served to the client. Under the hood, it uses SystemJS to emulate the real ES module loader behavior that will be standard in all browsers. If you need this to work in production, you can create a static build with Zwitterion. All of this eschews bundling, so you'll have to decide on what your needs are for performance. Personally, I'm betting on performance not being that big of an issue with HTTP2, and I prefer the simplest of builds to the complication that webpack and all of its friends bring.
As you are moving forward with typescript, my recommendation would be to integrate any module bundler such as webpack (my favorite) Internally it will use ts loader to transpile the code. Also along with compile, you would be able to use ts lint for static code analysis (webpack would take care of it). Webpack also helps if you want to have multiple modules created. Try out yomen https://github.com/FountainJS/generator-fountain-webapp . Once scaffolded you can refer to generated gulp files. It would give you an idea about typescript integration.
The best way to do this without overengineering your gulp setup is to use the plain typescript compiler:
1. Install typescript locally(it won't conflict with your global tsc):
npm i typescript --save-dev
2. Add tsc as npm script(inside package.json).
"scripts": {
"tsc": "tsc"
}
3. Create proper tsconfig.json for your needs and put it in the same folder as package.json
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
4. Use tsc compiler directly from gulp
var child_process = require("child_process");
gulp.task("build", function(cb) {
var tsc = child_process.spawn("npm", ["-s", "run", "tsc"], {stdio: "inherit", cwd: __dirname});
tsc.on("close", function(code) {
console.log("Tsc finished with code", code);
cb();
});
});
Tip. Use https://www.npmjs.com/package/gulp-sequence and vote up :)
It was suggested for a react-native plugin that code on npm is best transpiled before publishing instead of relying on the end-developer to transpile.
Seems fine, but react-native users probably tend to have a very homogeneous babel-based setup, so perhaps it wouldn't be so problematic to just leave it in ES6. Fancy uses similar to react-native-web could be an exception where ES6 is rather inconvenient. But then sometimes I go digging in node_modules to see what's going on inside modules. Transpiled code would make that somewhat more difficult.
Question is: It seems like there's probably a most favorable, consistent answer. Is transpiling before publishing the right move? Am I inconveniencing people by performing this step?
Question is not: how to do this. This question addresses that very well.
Thanks!
To my mind yes, transpiling is a good step. We used it in react-native-drawer-layout, so we didn't have our users to enable certain babel features. On the other hand, the more Safaris JS engine reaches spec compliance with ES6, the more I would tend to say no, as transpiled code is almost always slower.
I would say yes - transpile now. Transpile for better compatibility with todays browsers. Re-transpile your package in the future (if you care), when more ES6 features will be supported by browsers - dropping some (now) transpiled code and going with portions of native ES6.