Automatically export components from build folder - node.js

i am new to node and react so please forgive me if this does not sound correct.
I will try to explain this as simple as possible, but would like to know if this process can be automated somehow during a script/build process.
I create a component called 'button.js' in lib folder.
i added a script in the package.json file. the script is "build": "babel lib -d build",
I run npm run build which creates a build folder with my 'button' component built.
i export the component in an index.js file like so:
export { default as Button } from './build/components/Button'
I am wondering if the final 2 steps can be combined into a single step i.e. build the component then export it into the index.js file? If so, could you please point me in the right direction of how this can be done?

It sounds like you're trying to manually manage a "monorepos", whereby you have lots of individual packages that are utilised as node modules within other packages in your solution.
I'd suggest checking out Lerna (https://github.com/lerna/lerna) which takes away some of this pain. It allows you to include internal modules into your packages as if they were normal, published npm modules.

I suggest you google on webpack. It's how most people turn a bunch of js files into a bundle.js file. This is making your website faster too, as clients only need to load 1 file.
Webpack is a big subject so look for a "webpack babel react" guide.

Related

Within in a monorepo, is it possible to configure a package to 'use the uncompiled code if you can'?

I'm playing around with Yarn 2, and I want to do something like this.
I have a monorepo of the structure:
/
packages/
shared-ts/
package.json
src/
lib*/
app-ts/
package.json
src/
lib*/
app-js/
package.json
src/
lib*/
where lib* denotes that the folder is gitignored, but is where the compiled code will live.
In this example, I have a dependency library shared-ts that is used by two apps, app-ts and app-js.
The conventional approach
The conventional approach to configuring a monorepo like this, is that in shared-ts I would have a package.json like:
"main": "lib/index.js"
"scripts" : {
"build": "tsc"
}
Where the build script will build index.js and index.d.ts into the lib folder.
When both app-ts and app-js then resolve the package, they look in the lib folder and find the index.js and in app-ts's case - the index.d.ts.
This works fine, except that the developers need to remember to run the build script if they have made changes to shared-ts in order for the changes to propagate across.
Where this could potentially become problematic is where there are many layers of dependencies.
Attempted work around 1 - point main to src/index.ts.
I can change shared-ts package.json to
"main": "src/index.ts"
"scripts" : {
"build": "tsc"
}
This generally won't work, a plain node process won't be able to parse the syntax in the .ts file (eg. the import keyword).
Potential workaround - publishConfig
So something I'm considering, but haven't tried yet is, using the publishConfig
fields in the package.json
This field contains various settings that are only taken into consideration when a package is generated from your local sources (either through yarn pack or one of the publish commands like yarn npm publish).
"main": "src/index.ts",
"publishConfig": {
"main": "lib/index.js"
}
The idea being that:
When you publish a package to npm, lib/index.js will be used as main. 👍 code is ready for consumption, no compilation required.
If being used directly in the monorepo src/index.ts will be used as main. 😕 This kind of works as if you were running app-ts with ts-node for example.
However, where this starts breaking down is:
Running app-js in a development environment (where you don't have any additional syntax parsing set up).
Practical current best solution
My current best solution is to 'just give up on this 'no compile' aspiration' - if a developer makes changes to some code, they need to re-run build for the changes to propagate across.
How about using this?:
import someValue from 'some-package/src/index';
I can do this in my monorepo like the image below
I believe using nx will be good choice here. While it won't help you run the uncompiled code, it has pretty good features. In particular, you can automatically run the affected:apps on certain changes. For example, if you have a start command, it will run the start command for all the affected apps.
I wanted the same thing but had to compromise on the "Automatic compilation on changes" option in my JetBrains IDE.
It allows me to debug with ts-node as well as run the code using the native node binary.

Does Next js production build need node_modules folder in the server?

I am fairly new to Next js and its deployment process. I have recently converted one of my react js projects to Next js in order to advantage of the server-side rendering feature Next js offers. Now comes the deployment time, I figured out the Next build won't deploy without the node_modules folder present in the server. I use getServerSideProps in my pages and build with "build": "next build" command in package.json. The problem is my node_modules folder is close to 300MB (.next build adds about another 10MB) and I don't think it is the best practice to accompany this much weight with each deployment (I intend to have different instances of this build deployed, therefore 310MB X number of instances) in the server.
Am I doing something wrong here or is this the only way to accomplish this? I appreciate any answers. Thanks...
Update (11/14/22):
The previously mentioned "output standalone" feature is no longer experimental:
// next.config.js
module.exports = {
output: 'standalone',
}
Previous answer:
The latest Next.js documentation has a good example about how to create a standalone build with an excellent example of how to accomplish it with docker. As of now, the feature is experimental.
// next.config.js
module.exports = {
experimental: {
outputStandalone: true,
},
}
After a bit of research, I am answering my own question. If you use getServerSideProps you cannot static export your project. Therefore the node_modules folder is required to deploy the project on the server. That doesn't mean you have to FTP the node_modules, you can do the npm build on the server-side which will download node_modules to the deployment folder. In my case, the deployment folder weighs around 310MB where the node_modules folder itself is around 300MB.
You can use the [#varcel/nnc][1] package to compile the project.
In short:
Install package:
npm i --save-dev #vercel/ncc
Change build command script inside package.json
From: "build": "nest build",
To: "build": "ncc build src/main.ts -o dist",
P.S. In my case entry point for application is src/main.ts
[1]: https://www.npmjs.com/package/#vercel/ncc

Pointing the main field in package.json conditionally

I have a Monorepo under Lerna and Yarn Workspaces. The repo has packages which are published to npm and consumed outside the monorepo as well as within the monorepo. While developing in the monorepo we would like the main field of package.json for all such packages to point to the src directory, while when a package is used outside the monorepo, we would like the consumer to use the transpiled code in the dist folder.
I want this to be consistent across all uses of the packages, My current solution is to have the main field point to the dist folder. Then for each of the tools within the monorepo, namely jest, tsc, webpack, parcel I've had to come up with a different tool specific solution for aliasing the src directory instead of the dist directory. But I don't like the fact that I've had to do this work for each of these tools. It just doesn't seem scalable.
Has anybody come up with a lower level solution, where a module resolves to a different folder based on the environment?
Thank you.
If your internal code base is always transpiling the sources, why not just import { thing } from "my-package/src/main.js?
Then you can just leave the main field as dist for the consumers who ideally shouldn't have to keep track of additional paths when importing your packages.
There are a lot of details left our in your answer, but assuming you're using a single webpack/other instance to compile all your packages.
Another approach, since you're already coupled all your packages via the same compilation step, why not just use relative paths between the packages? That way you'll never have to act as a consumer but with slightly different needs.
And finally the third approach, which I think sounds a bit convoluted but should do exactly what you're asking for. Create a script that uses globby or some other npm package to grab all package.json files in your repository (excluding node_modules!). require() / iterate through these package.json manifest files and set the main field to an input value (say "dist"). Then, create two bin js files (hint: bin field) called set-main-dist and set-main-src, and possibly a third called unset-main.
Next, no matter what scripts you run in your package.json files at the root (or using lerna run), make sure to let the script look either like this:
"prebuild": "set-main-src"
or like this
"build": "set-main-src && build etc"
Hope one of these options work out for you. Remember that it's rarely worth going against the stream of usual patterns in tooling and what not. Make this one worth it.
I had exactly the same dilemma but with yarn3.
The solution importing always from source dint worked for my case, as the package itself might get published to npm too.
So after digging around I luckily found the package.json property publishConfig.main https://yarnpkg.com/configuration/manifest#publishConfig
With it I can change the main field from source to dist on npm publish.
Basically only for publishing we use a modified package.json.
Implemented in my package.json it looks like this:
{
"main": "./src/index.ts",
"publishConfig": {
"main": "./dist/index.js"
}
}
and when I run yarn npm publish or yarn pack the main field will be replaced temporary for the zip.
While all my tooling (jest and ts) can still rely on the main field pointing to the source.

require.context for node modules does not work with linked package

I created kind of a plug-in web site. Each plug-in follows a certain name standard and a contract. Inside my main.js I load all packages "dynamically" following the name standard:
const context = require.context("../../node_modules", true, /plugin\-\w+\/dist\/(index\.js|styles\.css)$/)
This requires a context throughout node_modules folder and loads all modules named "plugin-X". From within those modules it looks for "dist/index.js" and "dist/styles.css". Those later get imported:
context.keys().forEach(path => {/* do stuff */ })
This works super nice as long as the packages are installed using npm install path/to/tgz. However this does not give a pleasant developing experience. So in my plugin-X folder I use "npm link" and in my web site I use "npm link plugin-X".
when I start webpack now the whole thing explodes after creating 15K file handles. If I remove the node_modules folder inside "plugin-X" it works. However I need node_modules for building with babel and other stuff to recognize changes in src folder to rebuild and put new scripts into dist folder.
Is there any way I could do this? Or maybe another option I missed during my research on how this could be done?
Regards

How do I setup Babel 6 with Node JS to use ES6 in my Server Side code?

I have read several times the documentation provided at :
Node API Babel 6 Docs
I'm starting out learning pg-promise following the Learn by Example tutorial and would prefer to work with ES6 and transpile to ES5 with Babel but am unsure of a few things :
After installing babel-core, what preset do I use and where/how do I configure this to work?
The documentation was unclear to me about which file I put: require("babel-core").transform("code", options); into and what parts of that code are place holders. When I use that code, do I just use it one time somewhere and then I can use ES6 in every other file? How would this be achieved?
I read about this .babelrc file and would like to confirm if the actual filename is ".babelrc" or if that is just the file extension and where in relation to the root directory of my project do I put that file.. and how do I link to it?
If I'm using pg-promise should I be using ES6 and Babel or will running : npm install as described under the Testing section for pg-promise be enough and trying to use ES6 with this create more problems?
I was hoping to take advantage of let and const if the need came up during my server side development.
Is there a standard file structure for a node+babel+pg-promise server setup?
Edit
Worth noting that I have also read Node JS with Babel-Node and saw that using this should be avoided. The final answer at the very bottom didn't really make sense to me for similar reasons I'm having trouble following the actual documentation provided by Babel.
1.a What Preset is needed?
You will need to install Babel firstly with npm install babel-core --save-dev in the root directory of your project using a Terminal window like Command Prompt.
Once installed, you will need to install the es2015 preset with npm install babel-preset-es2015 --save-dev. Babel-Core is Promises/A+ Compliant but not ideal for usage due to poor error handling so a library such as Bluebird should be used instead for this purpose. In order to transpile, babel-core will still need to be installed and es2015 enables ES6->ES5 transpiling so you can use fancy things like let and const etc.
1.b Where to put require("babel-core");?
instead, use require("babel-core/register"); and place it inside your Entry file typically called, "server.js". The server.js file will need to use CommonJS (ES5) exclusively.
By using the "require" statement it will apply all relevant transforms to all code being required into the Entry file and all files being required/included into those files.
You point to the Entry file inside package.json under the "main": section.
Package.json is created when you initialise the project with npm init at the root directory of your project inside the Terminal Window
One approach to this would be :
Entry File - server.js
server.js - requires {babel-core and the main ES6 file : config.js/jsx/es6/es}
config.es6 - uses ES6 and has includes(requires) for all other project files that can also use ES6 as they get transpiled by being loaded into the "config" file which is being directly transpiled by babel-core.
2. What is .babelrc?
.babelrc is the filename and should be placed in the same folder as your package.json file (normally the root directory) and will automatically "load" when babel-core is required to determine which preset(s) or plugins are to be used.
Inside .babelrc , you will need to add the following code :
{
"presets": ["es2015"]
}
3. pg-promise Testing Section
A direct quote from the developer recently answered this
You do not need to worry about steps in the Tests, use only the steps in the install. The one in tests relates to the dev dependency installation, in order to run tests. The pg-promise can work with any promise library compliant with Promises/A+ spec.
4. Standard File/Folder Structure for Server Side Projects?
There is no standard way to achieve this task as each project has unique demands. A good starting point would be to place the Entry file in the project root directory, the ES6 Config file in a "scripts" or "src" sub-folder and individual components in folders below that.
e.g.
ROOT/server.js
ROOT/src/config.es6
ROOT/src/component1/files.es6
ROOT/src/component2/files.es6
With this in place, Babel will successfully transpile all ES6 to ES5 and enable support of A+ compliant promises.
To begin using the node.js webserver This Guide provides a bit more insight and in the context of this answer the code shown would be placed into the ES6 config.es6 file and the following code would go into the Entry server.js file :
require("babel-core/register");
require("./src/config.es6");
The process for building Isomorphic web applications is different to this and would likely use things like grunt, gulp, webpack, babel-loader etc another example of which can be Found Here.
This answer is the combination of several key points provided by other answers to this question as well as contributions from experienced developers and my own personal research and testing. Thank you to all who assisted in the production of this answer.
This answer uses this simple directory structure
project/server/src/index.js => your server file
project/server/dist/ => where babel will put your transpiled file
Install babel dependencies
npm install -g babel nodemon
npm install --save-dev babel-core babel-preset-es2015
Add these npm scripts to your package.json file
"scripts": {
"compile": "babel server/src --out-dir server/dist",
"server": "nodemon server/dist/index.js
}
Create a .babelrc file in your project root directory
{
"presets": "es2015"
}
Transpile your directory with
npm run compile
Run your server with
npm run server
I think you should use a tool like grunt or gulp to manage all your "build" tasks. It will automate it for you, and you won't make errors.
In one command, you can transpile your code into babel ES2015 et start your application.
I suggest you to take a look at this simple project. (just install node_modules and launch npm start to start the app.js file)
However, if you really want to use babel manually,
.babelrc is the name of the file, you can see one in this project (redux) to have an example
.babelrc is a config file, if you want to see how it works, you can check this package.json (always redux)
There's actually no standard way that I know. You can use the project skeleton below if needed, and send pull request to improve it :-)
#makeitsimple
Step: 1
npm install nodemon --save
In project directory
Step: 2
yarn add babel-cli
yarn add babel-preset-es2015
Step: 2
In package.json-> scipts change 'start' to the following
start: "nodemon src/server.js --exec babel-node --presets es2015"
Step: 3
yarn start

Resources