How to get original file path in the script with webpack? - javascript

An example code:
//in the file app.module.js
module.exports = framework.module("app", [
require('./api/api.module').name
])
//in the file app/api/api.module.js
module.exports = framework.module("app.api", [
])
Here are two dependent modules named 'app' and 'api'.
Module name is always same as file path to the module file (except module.js part, e.g. for file at app/api/api.module.js module name is 'app.api').
Is it possible to make webpack provide a filename of the included file during compilation, so following can be done?
//in the file app.module.js
module.exports = framework.module(__filename, [
require('./api/api.module').name
])
//in the file app/api/api.module.js
module.exports = framework.module(__filename, [
])
Where __filename is an actual path to the file folder.
It does not really matter what's format of name of the module, but it should be unique (for framework reasons) and lead to the module location (for debug reasons).
Update:
I've solved it for myself - this can be done by custom webpack loader which substitutes a certain placeholder with file path string. But anyway question is still open.

I know you said you resolved this yourself, yet, here's my take on it.
Your solution includes using a custom loader, however, maybe you could have solved it in a different way.
First step, in your webpack.config add these in the config object:
context: __dirname, //set the context of your app to be the project directory
node: {
__dirname: true //Allow use of __dirname in modules, based on context
},
Then, add this in your list of plugins:
new webpack.DefinePlugin({
SEPARATOR: JSON.stringify(path.sep)
})
This will replace all SEPARATOR instances in your modules with whatever the correct path separator is, for the system you are working on (you will have to require('path') in your webpack.config for this to work)
And finally in whatever module you want you can now get its name by doing
var moduleName = __dirname.replace(new RegExp(SEPARATOR, 'g'), '.');
So, for example
//in the file app.module.js
var moduleName = __dirname.replace(new RegExp(SEPARATOR, 'g'), '.');
module.exports = framework.module(moduleName, [
require('./api/api.module').name
])
//in the file app/api/api.module.js
var moduleName = __dirname.replace(new RegExp(SEPARATOR, 'g'), '.');
module.exports = framework.module(moduleName, [
])

Related

Update (write to) an object in a separate JS file using Node

I'm fairly new to Node, and am wracking my brains on how to achieve the following:
I have a config file that looks something like this:
// various es imports
export default {
input: {
index: 'src/index.ts',
Button: 'src/Button/index.ts',
Spinner: 'src/Spinner/index.ts',
'icons/Notification': 'src/_shared/components/icons/Notification.tsx',
'icons/Heart': 'src/_shared/components/icons/Heart.tsx',
},
//.. other properties
}
From my node script, i need to somehow read this file and do the following:
Delete any entries in the input object that have a key starting
with icons/
Append new entries to the input object.
Write these changes back to the original config file.
Is there a recommended way to do this in Node, i've been looking at a couple of libs, like replace-in-file but none seem to be suited to this particular case.
Just faced the same concern, here is how I solved it :
1. Gets your file content
If it is not a .js file, then use fs.readFileSync (or fs.readFile) like so :
const fs = require('fs');
const path = require('path');
const myObjectAsString = fs.readFileSync(
path.join( process.cwd(), 'my-file.txt' ), // use path.join for cross-platform
'utf-8' // Otherwise you'll get buffer instead of a plain string
);
// process myObjectAsString to get it as something you can manipulate
Here I am using process.cwd(), in case of a CLI app, it will gives you the current working directory path.
If it is a .js file (eg. a JavaScript config file like webpack.config.js for instance), then simply use require native function, as you would do with regular internal file or NPM module, and you will get all module.export content :
const path = require('path');
const myObject = require( path.join( process.cwd(), 'myFile.js') );
2. Modify your object as you want
// ...
myObject.options.foo = 'An awesome option value';
3. Then rewrite it back to the file
You can simply use fs.writeFileSync to achieve that :
// ...
fs.writeFileSync( path.join(process.cwd(), 'my-file.txt', myObject );
If you want to write a plain JavaScript Object, then you can use util.inspect() native method and you may also use fs.appendFileSync :
// ...
// If we wants to adds the 'module.exports = ' part before
fs.writeFileSync( process.cwd() + '/file.js', 'module.exports = ');
// Writes the plain object to the file
fs.appendFileSync( process.cwd() + '/file.js', util.inspect(options));

How to get current filename without path and extension with webpack?

I can get the relative file path with __filename, and sure I could hack it apart to get just the basename with some JS-fu, but I want to do this at compile-time.
DefinePlugin will let me inject some globals like I want, but AFAICT I can't have a "dynamic" global based on the current file.
So how can I do this?
e.g. given I am in the file assets/scripts/lib/components/bpm/RecordAttendancePopup.jsx, how can I get webpack to inject a constant like __basename that evaluates to "RecordAttendancePopup"?
Generic version
If you're using modules or any supported bundler, you can access the pre-existing import.meta.url and manually extract the filename:
const filename = import.meta.url // Get current path
.split(/[\\/]/).pop() // Get current filename
.replace(/\.[^.]+$/, ''); // Drop extension
or you can use a better parser (which may or may not work depending on your config)
const path = require('path');
const filename = path.parse(import.meta.url).name;
Webpack-specific version
This is what I use in my webpack.config.js:
const path = require('path');
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
__filebasename: webpack.DefinePlugin.runtimeValue(
info => JSON.stringify(path.parse(info.module.resource).name)
)
})
]
};
and then I'll have the __filebasename variable available everywhere in my source. Works in Webpack 4 and 5.
I guess there is no other way than creating your own "DefinePlugin" based on https://github.com/webpack/webpack/blob/master/lib/DefinePlugin.js to get what you want.

my config.js file is not being recognized

In my index.js file, I have const config = require('config'); written as one of the first lines.
And I have a file in my project folder called config.js
But I keep having my console tell my that it Cannot find module 'config'
My config file is this basically:
module.exports = {
'secretKey': 'mySecretCode12232',
'mongoUrl' : 'mongodb://localhost:27017/test'
};
This doesn't make any sense it should be working.
const config = require( path.join(__dirname, 'config'+'.js' ) );
I also have own function which loads atomaticaly from specified subdirectory at it's definition, it saves a lot of time.
When you don't provide any path selector in the require statement (eg. require('./config')), your code will search for the package named config and fail as it cannot find this specific one, as require will assume that it was the package name that was provided (and will start searching e.g. in your node_modules etc. - search path for it is not a trivial topic :) ).
If you want to require the module from another file, you have to provide a correct path to it, so assuming your config.js resides in the same catalog as your other file, the correct statement would be:
const config = require('./config'); // Extension can be omitted

node.js require from parent folder

I have the following structure:
-- node_modules
-- websites
---- common
------- config.js
---- testing
------- test.js
Inside config I have some variables set, which are being exported using module.export.
I am trying to retrieve those variables when running node test.js from config.js using the following codes:
var configData = require('./common/config.js')
var configData = require('../common/config.js')
None of them work. What can I do to retrieve the data from the other folder?
var configData = require('./../common/config.js');
./ is testing/
./../ is websites/
./../common/ is websites/common/
./../common/config.js is websites/common/config.js
from test.js:
const configData = require('../common/config');
You can safely omit '.js'.
As documentation say:
File Modules
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js,
.json, and finally .node.
.js files are interpreted as JavaScript text files, and .json files
are parsed as JSON text files. .node files are interpreted as compiled
addon modules loaded with dlopen.
A required module prefixed with '/' is an absolute path to the file.
For example, require('/home/marco/foo.js') will load the file at
/home/marco/foo.js.
A required module prefixed with './' is relative to the file calling
require(). That is, circle.js must be in the same directory as foo.js
for require('./circle') to find it.
Without a leading '/', './', or '../' to indicate a file, the module
must either be a core module or is loaded from a node_modules folder.
If the given path does not exist, require() will throw an Error with
its code property set to 'MODULE_NOT_FOUND'.
More info about how require() work here.
const cheerio = require('../node_modules/cheerio');
const request = require('../node_modules/request-promise');
const vl = require('../node_modules/validator');
const crypto = require('crypto');
const fs = require('fs');

Injecting variables into webpack

I'm trying to inject a variable into each module within my webpack bundle in order to have debugging information for JS errors per file. I've enabled
node: {
__filename: true
}
Current file path in webpack
in my webpack.config, but I would like to inject something like
var filename = 'My filename is: ' + __filename;
into each module before compilation. I've seen the Banner Plugin with the raw option but it seems this would only inject the banner outside of the webpack closure rather than my desired result of injecting script into each module.
I use variables to resolve a couple of variables in my webpack.config.js file:
plugins: [
new webpack.DefinePlugin({
ENVIRONMENT: JSON.stringify(process.env.NODE_ENV || 'development'),
VERSION: JSON.stringify(require('./package.json').version)
})
]
It might not be dynamic enough, but if it is, then you might be able to bypass that loader solution.
Write your own loader:
my_project/my_loaders/filename-loader.js:
module.exports = function(source) {
var injection = 'var __filename = "' + this.resourcePath + '";\n';
return injection + source;
};
Add it to your pipeline and make sure to add also the configuration:
resolveLoader: {
modulesDirectories: ["my_loaders", "node_modules"]
}
See the documentation on how to write a loader.

Categories