how to share code between Webpack build and NodeJS server process? - node.js

My application has a directory structure more or less like this:
src-program/ - contains frontend code including package.json and webpack.config.js
src-server/ - contains backend code including a different package.json and .babelrc
shared/foo.js - is JavaScript code that is needed by both the frontend and the backend
All code uses ES2015 syntax and thus is transpiled using Babel.
For the frontend the "transpilation" is done during the Webpack build by using the babel-loader.
For the backend it is done on-the-fly by babel-register.
shared/foo.js requires other modules, that are found in the package.json files of both the frontend and the backend.
Due to how NodeJS/Webpack resolve modules, the shared module isn't found normally.
For Webpack I solved this in a somewhat hacky way using this configuration:
resolve: {
root: __dirname,
fallback: [
__dirname + "/../shared",
__dirname + "/node_modules"
],
extensions: ['', '.js', '.jsx']
},
The first fallback makes sure that the "shared" module is resolved and the second fallback makes sure that modules required by the shared module are still resolved to the frontend node_modules directory.
This allows including the shared module as simple as this:
import * as foo from 'foo';
However, I'm having difficulties to make the backend (ie. NodeJS) resolve the shared module the same way.
I tried with app-module-path, which makes foo.js resolve, but then the file is either not processed by Babel or additional Babel modules like transform-runtime (indirectly needed by foo.js) cannot be resolved since they reside in src-server/node_modules...
I could probably work around the problem by pre-transpiling the code instead of using babe-register but it all doesn't feel right anyway.
So, what is a good way to share code between a Webpack build and the NodeJS server process?

Can you package the shared module up as an npm package (even a package just residing on your filesystem)? Then your src-program and src-server projects can add it as a dependency in their package.json, and it will get copied into their respective node_modules folders.
See: how to specify local modules as npm package dependencies

Related

Using NPM packages without Webpack

I am used to using NPM packages with Webpack, but I'm wondering how you're supposed to use NPM packages without Webpack.
I know how to install packages. I just don't know how to use them, since you can't just import modules in plain js.
Webpack compiles a bunch of javascript files and combines them into a single one for web distribution. NPM downloads javascript files through packages.
Here's some scenarios where you might use NPM without webpack
You are doing Node.js server-side javascript development. There's no webpack here
You are using a webpack alternative like rollup or browserify
You directly do anything else with the files npm downloads. Maybe you concatenate, throw them in a Makefile or maybe you expose node_modules directly to the world and reference their full paths directly.
Most of my web and server-side development is without webpack.
Why you can't import in plain js?
If you correctly define the package entry point like
"main": "dist/index.js",
"module": "dist/index.js",
Those files can be plain ES6 javascript with named exports or export default, and you can import them after intalling your package with regular import.
You don't need webpack nor babel to make an mpm module. Just put in any folder the files you want to distribute, specifying the main entry point and export elements on that file.
Now... in an angular or react application for example, they may install your component and will use babel and webpack to first transpile your component to ES5 with babel, and then bundle your code together with the rest of their app using webpack.
For front-end, not node.js but still NPM modules.
HTML can import directly ES6 modules but the file must be in .mjs format and provide export default, Module.exports in regular .js file wont't work. This is not a common thing and you'll run into problems if there are subdependencies that don't use ES6 modules. If you find a module that supports it. i.e. some-module
npm install some-module
And in the same directory next to node_modules create index.html pointing straight to the modular bundle
<h1>I'm HTML</h1>
<script type="module">
import SomeModule from './node_modules/some-module/bundle.mjs';
const mod = new SomeModule();
mod.doStuff();
</script>
Here's an article about this https://medium.com/passpill-project/files-with-mjs-extension-for-javascript-modules-ced195d7c84a
npm init
To create a package.json file
npm install --save <package>
To install a package and save it in the package.json file

Vue - Better to publish .vue file or a compiled component?

When writing custom components is it better to publish the .vue file directly or to publish a compiled version using webpack/other-bundling-tool ?
Bonus: Is there an official document regarding conventions to follow when publishing custom components?
EDIT: What are the pros and cons of either method?
I've published a few open source projects and from experience I can say that it's better to publish your code - or rather, set the main entry point - as a compiled distributable for a few reasons:
Firstly, by outputting a UMD module you are creating a distributable that works across all environments (webpack, browserify, CDN, AMD) and it's as simple as adding the following to your webpack config:
output: {
...
library: 'MyPackageName',
libraryTarget: 'umd',
umdNamedDefine: true
},
Secondly, most developers using webpack will exclude babel-loader from compiling scripts in their node_modules folder by doing something like:
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
So, if developers need to compile your code themselves and you have anything in your project that is not a .vue file that uses ES2015 (e.g. a mixin) then you would need to tell developers to apply babel-loader to your project folder in their webpack config.
As for browserifydevelopers having to compile your project, you would need to add vueify and babelify as transforms to package.json (they actually can't set this up themselves) and tell those developers that those are dependencies and get them to set up an appropriate .babelrc file.
All that setup can be a nightmare for devs, many will have little knowledge about their build process, so won't know about excludes, they won't know about transforms, they will just get a bunch of errors and either remove your package or create issues on your repo.
And that's just for the two most common build processes, you will still probably want a CDN and you will still want to allow those using AMD modules to use your package, so a UMD module is the way to go.
That said, you should still distribute the .vue files themselves, which will also allow devs to compile your project if they have advanced configuration requirements.

Electron putting node_modules in asar fails module dependencies

I've got an Electron project in node.js and am currently attempting to make distribution easier by collapsing the massive node_modules hierarchy into an asar. This way I can have app.asar and modules.asar, as suggested by various places such as grunt-asar.
The problem is that this needs me to change all require() statements to have the asar in the path, like: require('modules.asar/package'). That's fine for modules I'm including from my code but when a module itself uses require('dependency-package') that then fails to resolve. As I understand it, node.js will resolve in the "node_modules" folder for these but there is no "node_modules" directory anymore due to packaging modules into an asar.
Is there anything I can do to help this?

Why does webpack skip my local module?

I put some common code in this local module called 'client-common'. In one of the app project using this common module, I add this to package.json dependencies:
"client-common": "../client-common"
After npm install the module gets copied to node_modules. When I run (it's an Ionic project) ionic serve, it builds all fine, but at runtime in the browser I get
Runtime Error
Cannot find module "client-common"
I'm assuming this is a webpack issue? The question is why is the local module treated differently and not bundled at all?
It seems that I was supposed to supply an index.js in the common module. After adding an index.js duplicated from the existing index.d.ts, exporting everything, it just works now.

Automatic generation of Require dependencies from Node-modules

In the data-main require js file, we write like this:
paths: {
jquery: 'lib/jquery',
underscore: 'lib/underscore'
}
What I did was manually download the row JS library files and make "lib" folder and move the file into the folder and change the file name if necessary.
I use Nodejs for server, and I am wondering if there's any tool to create these client-side Require path files automatically from the installed Node-Modules. Browserify does a similar job if I don't user Require (creating one JS file, and call it in the other browser JS files.) But it seems like Browserify cannot be used as a path in Require.
Any thoughts? Thanks.
An alternative solution (to browserify, with which I'm not familiar) is to use bower for managing client side libraries. It is similar to node/npm, but is geared towards browser libraries.
It will not copy or rename libraries, because that step isn't necessary. Instead the libraries will be placed in a directory called bower_components. The paths config would look like
paths: {
jquery: "../../bower_components/jquery/dist/jquery",
bootstrap: "../../bower_components/bootstrap/dist/js/bootstrap",
...
}
(the actual number of .. in the path depends on values of other requirejs options).
In development, when all dependencies are loaded asynchronously as separate files they will be loaded from bower_components and requirejs optimizer will find them there when generating the optimized single source.
Adding the dependency paths to the config file can be half-automated with grunt plugin grunt-bower-requirejs. The idea is that after a library is installed using bower install LIBRARY it's path can be added with grunt bower.

Resources