What is the "Node way" to split code across multiple files? - node.js

I am currently working on a NodeJS/Typescript project, it is a HTML5 client with a NodeJS server that communicates via web-sockets. Coming from a C# background, I like to keep my code separated into different files for different things, including a shared file for objects to be serialised and de-serialised for sending/receiving data in an organised & well defined manner.
Currently at the server side, i have the build options set to compile it down to a single JavaScript file and I have that as my startup script, but I believe this to be a messy solution to my problem. To fix an issue with the order of the output file, i have also had to put an ordered list of references to the various TypeScript files at the top of my "main" typescript file.
This seems like the complete wrong way to do it, is it possible to still separate out different Typescript(/Javascript) files so that different areas of logic are in a dedicated place, whilst still being able to share a file be between my HTML client & my NodeJS server, or is this just a workaround i am going to have to learn to live with?

You could use webpack module bundler to avoid your ordered list of references. Webpack takes care of all dependency resolution and referencing. To share code between server and client, you could seperate it out in a common module/package and import it on both sides.
I use webpack to compile my Typescript code to ES6 code for the server code and with an extra build step (babel.io) to browser compatible code.
The following example configuration shows the usage of webpack with Typescript:
var path = require('path');
module.exports = {
entry: {
app: [ path.join(__dirname, './src/App.ts') ]
},
devtool: 'source-map',
output: {
path: path.join(__dirname, './build'),
filename: 'js/bundle.[name].js'
},
module: {
loaders: [{
// The loader that handles ts and tsx files. These are compiled
// with the ts-loader and the output is then passed through to the
// babel-loader. The babel-loader uses the es2015 and react presets
// in order that jsx and es6 are processed.
test: /\.ts(x?)$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015!ts-loader'
}, {
// The loader that handles any js files presented alone.
// It passes these to the babel-loader which (again) uses the es2015
// and react presets.
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015']
}
}]
},
resolve: {
extensions: ['', '.ts', '.tsx', '.js'],
modulesDirectories: ['node_modules', 'src', 'lib']
}
};

Related

How to bundle node module CSS into a vscode extension

My Visual Studio Code extension uses the node module highlight.js which comes with a folder full of CSS files. These provide colour schemes for syntax colouring. It has become necessary to bundle some of the CSS files.
It's about bundling an asset
The objective is to bundle a CSS file and at run-time access the file content as a string. If that can be achieved without an import statement that would be perfect. Normally, how exactly one accesses the content of the bundled file would be a separate question, but I have a feeling that content retrieval and how one should go about bundling the asset are closely entwined.
I freely admit to having a weak understanding of WebPack.
The story so far
The bundler is specified in package.json as "webpack": "^5.4.0" but I don't know how to ascertain what is actually present. It is conceivable that there is something wrong with my setup: when I try to run webpack --version on a command prompt in the project folder, it responds
CLI for webpack must be installed.
webpack-cli (https://github.com/webpack/webpack-cli)
We will use "npm" to install the CLI via "npm install -D webpack-cli".
Do you want to install 'webpack-cli' (yes/no):
The first time this happened I responded yes. After a flurry of installation and another try the same thing happened. However, vsce package has no trouble using webpack for a production build and pressing F5 to debug successfully puts together a development build in a dist folder with an unminified file I can examine (which is how I know the file mentioned below is being bundled).
Moving on from there I've modified webpack.config.js like so
//#ts-check
'use strict';
const path = require('path');
/**#type {import('webpack').Configuration}*/
const config = {
target: 'node', // vscode extensions run in a Node.js-context -> https://webpack.js.org/configuration/node/
entry: './src/extension.ts', // the entry point of this extension, -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2',
devtoolModuleFilenameTemplate: '../[resource-path]'
},
devtool: 'source-map',
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, -> https://webpack.js.org/configuration/externals/
},
resolve: {
// support reading TypeScript and JavaScript files, -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js', '.css']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
};
module.exports = config;
As you can see there are rules and loaders for CSS.
When I add this import
import "../node_modules/highlight.js/styles/atelier-dune-light.css";
webpack happily builds the bundle and when I inspect it I can find the bundled CSS.
However, when I try to load the extension in the extension debug host, it fails to load, with this message.
Activating extension 'pdconsec.vscode-print' failed: document is not defined.
Enabling break on caught exceptions reveals this rather surprising exception.
Exception has occurred: Error: Cannot find module 'supports-color'
Require stack:
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\node_modules\debug\src\node.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\node_modules\debug\src\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\dist\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\vscode-proxy-agent\out\agent.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\vscode-proxy-agent\out\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\out\bootstrap-amd.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\out\bootstrap-fork.js
OK, so activation failed because the loader barfed. But WTF does importing CSS have to do with support-color?
Remove the import and it runs just fine. I really don't know how to respond to this; it's not clear to me why a demand for a stylesheet should cause that error. At this point I look to others for guidance and advice.
Remove style-loader from webpack.config.js to fix the error.
Pull the CSS as a string like this. Note the abbreviated path.
const cssPath: string = "highlight.js/styles/atelier-dune-light.css";
const theCss: string = require(cssPath).default.toString();
You do not need the import statement, the use of require will cause Webpack to bundle the files, but you still have to remove style-loader to avoid the loader error.

AWS: Steps to pass a node.js application to EC2

I'm newbie with AWS and I'm developing a web application with node.js and react.js. My application works fine in my laptop but I want to upload it to AWS EC2.
When I simulate a production environment in my laptop, I have a /dist folder where are all the code of the front end and the server code is in /src/server folder.
I have uploaded my app to EC2 and now I'm a little bit lost about the next steps.
First, I would like if there is any way to download the modules only if they are not installed
Second, I would like to know if its mandatory to use babel in this environment, because in all tutorial that I have followed to make the development these modules are always installed like a dev depencies. So, is it now mandatory to move all babel modules to dependencies? Right now, my script to this two steps is:
npm -i --production && cross-env NODE_ENV=production babel-node src/server/server.js
If I change babel-node for node then I've got an error with "import" command because I'm not using babel.
My scripts are:
Is there to make a build like I do for the front-end code but for the server code? How can I do it?
Fourt, the server who will be listening the calls to the api will be node server and this will get when I finish to make correctly my aws-start script. But ... Which is the best option to server the front-end pages?
Sorry, I've got too many questions because this is my first app in AWS.
Edit I:
Following the wise advices of #Corrie MacDonald when I build my code I've got this files and folders:
Next, I modify my aws-start script:
npm i --production && cross-env NODE_ENV=production node dist/assets/js/bundle.js
But, I've got this error:
What am I doing wrong?
Edit II:
My webpack.config.babel.js file is:
import path from "path";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
const devMode = process.env.NODE_ENV !== "production";
console.log("devMode: " + devMode);
module.exports = {
entry: "./src/client/index.js", //set entry file
// Resolve to output directory and set file
output: {
path: path.resolve("dist/assets"),
filename: "js/bundle.js",
publicPath: "/assets" //It's mandatory to define this publicPath to get access to the website when we reload pages
//or we access to them directly with url's which have directories of second level like
//http://localhost:4000/directory-level-1/directory-level-2
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/client/index.html", //where is our template
filename: "../index.html", //where we are going to put our index.html inside the output directory
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
}),
new MiniCssExtractPlugin({
filename: "css/bundle.css",
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
})
],
//It help us to detect errors.
devtool: "source-map",
// Set dev-server configuration
devServer: {
inline: true,
contentBase: './dist',
port: 3000,
historyApiFallback: true
},
// Add babel-loader to transpile js and jsx files
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use:[
{
loader: "babel-loader",
query: {
presets: [
"#babel/preset-react"
]
}
}
]
},
{
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader"],
test: /\.css$/
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
sourceMap: true
}
},
{
loader: "saas-loader",
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "url-loader",
options: {
limit: 10000,
publicPath: "/assets/images/",
outputPath: "./images/"
}
},
{
test: /\.(eot|ttf|woff|woff2)$/,
loader: "url-loader",
options: {
limit: 10000,
publicPath: "/assets/fonts/", //It's mandatory to get access to the fonts when we reload pages or access directly
outputPath: "./fonts/"
}
}
]
}
}
Edit III:
This are the folders of my development environment:
How you can see when I make a build I create the /dist folder with the front-end code, but my server code still being in /src/server folder. How can I create a /dist folder for my server code? Is that possible?
Without going into a lot of detail about automated building procedures, the steps usually go as follows:
Build Code
-- Here, your source code is built and transpiled into a distributable format, which usually goes into a dist/ folder.
Upload your distributable code.
-- Here, all of the files you have built should be uploaded (manually or automatically) to your EC2 instance.
Run a startup script
-- Here, any project startup code should be run in order to actually start your server.
You don't need babel in production because your project should already have been built by that point. However, if you are building on the EC2 instance, instead of just uploading your dist, then you will need it.
In order to turn your EC2 into a routable, reachable web server, you will need to configure some security and routing policies on AWS. You will need to ensure that the instance has a routable IP (or you can use the automatically generated DNS provided by AWS). Secondly, you'll need to ensure that your security policy allows port 80 (at the very least, and any additional ports you need to interact with the server - for HTTPS, SSH or something else.)
Once you have all this in place, you should be good.
EDIT
If you want to serve static HTML pages, you will have to ensure that you have set up your EC2 container as a web server with something like Apache. However, I would recommend that you run your Node Server exclusively from the server and host your static webpack bundle on S3 as a static website.
EDIT 2
Here's an introduction to setting up your EC2 instance for node. - https://medium.com/#nishankjaintdk/setting-up-a-node-js-app-on-a-linux-ami-on-an-aws-ec2-instance-with-nginx-59cbc1bcc68c
Here's an introduction to setting up a static website with S3. - https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html

Vue/Quasar, Webpack, These relative modules were not found in node_modules

I've bootstrapped an app with quasar framework (based on vuejs 2). I installed some package which need some dependencies. When i try to use the package (leboncoin-api) i got this error :
These relative modules were not found:
* ./types/other in ./node_modules/mime/index.js
* ./types/standard in ./node_modules/mime/index.js
I have checked in node_modules and those files exist. They are imported in index file in mime looking like this :
'use strict';
var Mime = require('./Mime');
module.exports = new Mime(require('./types/standard'), require('./types/other'));
I tried quickly with create-react-app and the package is working. I've read that it can come from webpack, but i'm struggling to correct it. I have no idea where to look :/
Here is the part of webpack that maybe can help :
extendWebpack(cfg) {
cfg.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/,
});
},
I tried with and without excluding node_modules, same error
Thanks in advance from the community for the help !
EDIT :
I added to wepack.config
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
Which change the error to :
This relative module was not found:
./package in ./node_modules/leboncoin-api/node_modules/cheerio/index.js

Babel not transpiling ES6 module in node_modules despite proper exclude

In a react project I used react-boilerplate but had private modules to include in the frontend that needed transpiling. In order for babel to transpile those I set exclude to the following function in the webpack config that relates to babel:
rules: [
{
test: /\.js$/, // Transform all .js files required somewhere with Babel
// eslint-disable-next-line object-shorthand, func-names
exclude: function (modulePath) {
return /node_modules/.test(modulePath) &&
!/node_modules\/#trade-quorum\/tq-helpers/.test(modulePath);
},
use: {
loader: 'babel-loader',
options: options.babelQuery,
},
},
And it worked perfectly.
Now I used that same trick in a different project but this time, in the resulting bundle tq-helpers is included but not transpiled into ES5 - the ES6 code is directly in the bundle and the build raises an error (more specifically UglifyJS).
There must be a reason around the dependencies to this package that are not the same in the two projects but hard to find. I was wondering whether there is a way to debug in details what babel does for a specific package in order to find the reason.
Thanks you for your help,
Best,
Didier

How to build a ES6 express app with webpack 2 and babel?

Given the following Webpack 2 configuration, I want to write a express app using ES6. My .babelrc has only the es2015 preset:
const path = require('path');
module.exports = {
target: 'node',
entry: path.resolve(__dirname, 'src', 'server.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'server.bundle.js',
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
],
},
};
Running webpack and starting the server work fine, but when accessing the URL through the browser I get the following error:
Error: Cannot find module "."
at webpackMissingModule (/Users/Me/project/dist/server.bundle.js:18208:74)
I don't know whether it's due to the . in a require('./some/other/file') call (not all files are ES6, so these one use require() instead of import); or maybe some configuration I'm missing.
And also, given that stack trace I can't really map it to the original source file.
Thanks in advance
Update 1
I just realize I'm trying to use webpack to transpile ES6 code because I'm trying to replicate another build task (the one in mern.io), but I could do the same just with babel. Maybe there's no reason for using webpack on the serverside.
You need to install and add in webpack.config.js ExternalsPlugin:
plugins: [
new ExternalsPlugin({
type: 'commonjs',
include: path.join(__dirname, './node_modules/'),
}),
],
It helped me, I hope you too, because I spent a lot of time for solutions this problem)
Maybe the reason is that you are using target: "node" on webpack config. So that means it's creating your package for nodejs environment. Can you test after changing it to target: "web" for client transpile?
https://webpack.js.org/concepts/targets/

Resources