Node.js + Webpack + TypeScript: access to path of project with source files (not usage project) - node.js

Let's make clear once again: I don't need process.cwd in this question, I need
to access to absolute path of source project. E.g:
Source code: C:\Users\user1\projects\lib1\src\library.ts (becomes to Node Module in the future)
Project that uses Library: C:\Users\user1\projects\someProject\src\someProject.ts
So, I need to get the C:\Users\user1\projects\lib1\src inside library.ts.
I tried:
webpack.config.js
module.exports = {
// ...
target: 'node',
externals: [nodeExternals()],
plugins: [
new Webpack.DefinePlugin({
__PROJECT_ROUTE_ABSOLUTE_PATH__: __dirname
})
]
};
project-types.d.ts
declare var __PROJECT_ROUTE_ABSOLUTE_PATH__: string;
If to try console.log(__PROJECT_ROUTE_ABSOLUTE_PATH__) in library.ts, below invalid JavaScript
will be produced:
console.log(C:\Users\user1\projects\lib1);
The path is correct, but quotations are missing. I don't know how to explain it.
But anyway, how we can get right path?
There is also a strange phenomena: if to invoke __dirname, just / will be returned, so path.resolve(__dirname, 'fileName') givesC:\fileName `

You can directly use the node.js path module which is in built.
The path module provides utilities for working with file and directory paths. It can be accessed using:
const path = require('path');
__filename is the file name of the current module. This is the resolved absolute path of the current module file. (ex:/home/user/some/dir/file.js)
__dirname is the directory name of the current module. (ex:/home/user/some/dir)
fs.readFile(path.resolve(__dirname + 'fileName'))
This will resolve to the path of the file.

Related

How to combine multiple node js file into a single bundle using webpack

I am trying to create a single bundle from multiple javascript file in a nodejs application.
The configuration I am using looks somewhat like this:
const path = require('path')
const nodeExternals = require('webpack-node-externals')
'use strict';
module.exports = {
externals: [nodeExternals({})],
entry: './lib/index.js',
output: {
iife: false,
path: path.resolve(__dirname, 'lib'),
filename: 'bundle.js', // <-- Important
},
target: 'node', // <-- Important
};
The problem is when I run bundle.js command instead for it to do what the command says, i get the full source of the file streamed into the terminal.
It seems the file contains some sort of IIFE that gets executed immediately. I set iife: false to false in the webpack configuration but that also did not make any difference.
Any ideas what could be wrong?
Edit:
I am calling webpack by adding:
bundle: webpack --config webpack.config.js to script section in package.json and then I run npm run bundle
As an alternative, you could use https://www.npmjs.com/package/#vercel/ncc package to bundle all your Node.js code into one file.

How to dynamically import package.json dependencies based on environment variables?

How could I add a script to my package.json file that would allow me to dynamically use a local file instead of a package version based on an environment variable?
"dependencies": {
"dynamic-dependency": "$(process.env.NODE_ENV !== 'dev' ? '^1.0.7' : 'file:../local-path-to-package')"
}
You can't do this in package.json, which is non-executable JSON file. The JSON variant used in package.json doesn't even support comments :). The purpose of package.json is to specify which dependencies are installed into node_modules, and that's it. With those dependencies installed, they can be used by Node at runtime, which locates them using the module resolution algorithm:
If the module identifier passed to require() is not a core module, and does not begin with '/', '../', or './', then Node.js starts at the parent directory of the current module, and adds /node_modules, and attempts to load the module from that location. Node.js will not append node_modules to a path already ending in node_modules.
So you can't use NPM/package.json for this. But, I see that you tagged your question with React, so if you are using Webpack, you can solve this issue in your Webpack config. This can be done with resolve.alias:
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
'dynamic-dependency': process.env.NODE_ENV !== 'dev' ? 'dynamic-dependency' : path.resolve(__dirname, '../local-path-to-package'),
},
},
};
I have not used other JS bundlers, but I would have to think Parcel/Rollup etc support this kind of configuration as well.

How can I keep "require" working for dynamic expressions

I want to bundle a Node.js script, which somewhere calls require(expression). After bundling the script with webpack, require fails. This is a super simplified example:
// main.js
const x = require(process.argv[2])
console.log(x)
I would like to either have a "normal" require for this case or tell webpack to include a specific file which I know will be required in the future (after bundling). To stick with this example, I know the value of process.argv[2] ahead of bundling.
Note: The code doing the expression based require is a dependency, so I cannot tweak require code.
This is my webpack.config.js
module.exports = {
entry: './test.js',
output: {
filename: 'test.js'
},
target: 'node'
}
The require path is relative to the file it is used in. So you'll need to figure out the path from where require is executing to the file it's loading from the parameter. Then prepend the relative part to the parameter.

How does react avoid using require("../../Path/To/Module"), and just use require("Module")

As far as I've seen, npm modules can be require() without a path:
require("module") // --> npm module
and local modules are require() using a path:
require("./module") // --> local module, in this directory
require("../../path/to/module") // path to directory
In react.js, modules are required without a path. See here for example. I'm wondering how they achieve this.
Apparently it uses rewrite-modules Babel plugin with module-map module (see gulpfile.js.)
There's also this Babel plugin that you can use to achieve the same behavior.
If you're using Webpack, you can add path/to/modules into resolve.modulesDirectories array and it will work similarly to requiring from node_modules instead of using relative paths.
resolve: {
modulesDirectories: ['path/to/modules', 'node_modules'],
},
and then
var foo = require('foo');
// Instead of:
// var foo = require('/path/to/modules/foo');
// or
// var foo = require('../../foo');

How can I make webpack skip a require

How can I make webpack skip occurences of
require('shelljs/global');
in my source files? I want to make a bundle of my source files but keep the require('shelljs/global') in the files and not bundle shelljs/global.
If you store the path in a variable then IgnorePlugin will not work. Though you still could do:
const myCustomModule = eval('require')(myCustomPath)
for new comers, on webpack 2+ the way to do this is like so:
module.exports = {
entry: __dirname + '/src/app',
output: {
path: __dirname + '/dist',
libraryTarget: 'umd'
},
externals: {
'shelljs/globals': 'commonjs shelljs/global'
}
};
the bundle will contain a verbatim require:
require('shelljs/global');
read on more supported formats on webpack's config guide and some good examples here
You can use Ignore Plugin (webpack 1) / Ignore plugin (webpack 2).
Add plugin in webpack.config.js:
plugins: [
new webpack.IgnorePlugin(/shelljs\/global/),
],
If require is in the global namespace and this is why you want Webpack to ignore it, just do window.require()
This should be a last resort option, but if you are certain that your JS file is always parsed by Webpack, and nothing else:
You could replace your require() calls with __non_webpack_require__()
Webpack will parse and convert any occurrence of this and output a normal require() call. This will only work if you are executing in NodeJS OR in a browser if you have a global require library available to trigger.
If webpack does not parse the file, and the script is run by something else, then your code will try to call the non-converted __non_webpack_require__ which will fail. You could potentially write some code that checks earlier in your script if __non_webpack_require__ exists as a function and if not, have it pass the call on to require.
However, this should be temporary, and only to just avoid the build errors, until you can replace it with something like Webpack's own dynamic imports.
Here a trick
const require = module[`require`].bind(module);
Note the use of a template string
If some files contains nested requires and You want to ignore them, You can tell webpack to not do parsing for these specific files.
For example if swiper.js and vue-quill-editor.js had inner requires this would be how to ignore them.
module.exports = {
module: {
noParse: [
/swiper.js/,/quill/
],

Resources