import.meta.url breaking after packing - javascript

My workspace looks like this:
├─dist
│ └─index.mjs
└─src
└─utils
└─index.ts
I use import.meta.url in /src/utils/index.ts like this:
const __path_src_root = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../',
);
const __path_cache = path.resolve(
__path_src_root,
'../node_modules/.cache/#setup',
);
After packaging, import.meta.url will change due to directory changes, breaking the __path_cache. It changes from /path/to/cli/node_modules/.cache/#setup to /path/to/node_modules/.cache/#setup.
Is there a way to resolve this?
I don't want to use env var like process.env.prod ? ... : ...😂
By the way, I use rollup to pack my code and tried to find plugin to solve it but couldn't find anything.

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

Coffeeify won't parse more than the entry file

I'm quite a beginner with Browserify. I tried to integrate it into gulp.js with Watchify (for performance reasons) and Coffeeify. I tried at least five more or less different approaches I found in Google, the last of them being an official recipe from the gulp docs.
Problem now is the transforming part. I want to use Coffeeify to parse my code which is mostly written in CoffeeScript.
Coffeeify successfully parses my entry file app.coffee but when I require a ./foo.coffee from there, that file seems not to be transformed which will naturally result in a parsing error by Browserify about unexpected tokens etc.
Anyone got an idea how to fix that?
Here's the relevant part of my gulpfile, mostly the same as in the link above, just with added transformations.
var gulp = require( 'gulp' );
var gutil = require( 'gulp-util' );
var browserify = require( 'browserify' );
var watchify = require( 'watchify' );
var coffeeify = require( 'coffeeify' );
var source = require( 'vinyl-source-stream' );
var b = watchify( browserify({
entries: [ './coffee/app.coffee' ],
extensions: [ '.coffee' ],
debug: true,
cache: false,
packageCache: false
}) );
b.transform( coffeeify ); // as described in the gulp docs
gulp.task( 'bundle', bundle );
b.on( 'update', bundle );
b.on( 'log', gutil.log );
function bundle() {
return b.bundle()
.on( 'error', gutil.log.bind( gutil, 'Browserify Error' ) )
.pipe( source( 'bundle.js' ) )
// I actually tried to pipe in coffeeify here (gulp-coffeeify as well), didn't help
.pipe( gulp.dest( './js' ) );
};
Okay, this was totally my bad. I abstracted my code a bit too far for this post so the actual problem wasn't reflected here:
app.coffee does not actually require ./foo.coffee directly but requires foo/foo from a node_modules directory, just to make things easier.
Which was exactly the problem: Transforms will not apply to required packages. This is intended behavior.
There seem to be two ways around that:
Solution 1: A browserify option called global-transform.
In the API it's used like b.transform( coffeeify, { global: true } ). This will apply the coffeeify transform to every required module.
Solution 2: Package-wise definition of transforms. That means: Adding something like this to each modules' package.json:
{
"browserify": {
"transform": ["coffeeify"]
}
}
I decided to use the first one because my "packages" actually don't even have a package.json file, they're just in the node_modules directory to be easily accessible by browserify.

How does one specify a custom search path with Browserify?

I have a large project that consists of hundreds of source files broken into several folders.
Something like this:
src/
AAA.js
subdir/
DDD.js
I would like to be able to specify dependencies with non-relative paths.
For instance, in DDD.js I would like to do this:
var AAA = require('AAA');
...rather than this:
var AAA = require('../AAA');
How can I achieve this with Browserify?
As stated in the documentation, Browserify uses browser-resolve under the hood.
When using the node API (as opposed to the CLI), you can specify a paths option which contains a list of directories to pass to browser-resolve.
The solution for my example would thus be something like this:
var browserify = require('browserify');
var b = browserify({
paths: [
__dirname + '/src'
]
});
b.add(__dirname + '/src/AAA.js');
b.bundle().pipe(process.stdout);
Or if you want to do it from the command line you can add your directory to the node search path:
NODE_MODULES=$NODE_MODULES:src browserify -o output.js input.js

Categories