How to convert a clever require to an import? - javascript

Previously I was using a clever index.js file:
var all = { ..... };
module.exports = _.merge(all,
require('./' + process.env.NODE_ENV + '.js') || {});
To import my env settings baed on the NODE_ENV, and where I have files such as:
development.js
production.js
shared.js
...
And when I did a require() for this index.js file, I recieved the env variables I wanted based on my NODE_ENV setting.
Is there any way to do this with ES6 and import???

In the end, I needed to use a different solution for ES6. You can do a declare require(package: string); which will be bound to the correct method after the translation to normal js for the browser. Then do a:
let foo_package = require('foo');
but this seems a bit more difficult. Instead I created:
development.ts
test.ts,
production.ts
and
import development from 'development';
import test from 'test';
import production from 'production';
export function choose(env: string) {
switch(env) {
case 'development':
return development;
case 'test'
return test;
case 'production':
return production;
default:
throw Error(`choose 'development', 'test', or 'production'!);
}
}
This worked just as well and didn't need plain js;

Related

Load a CJS module config inside another file

I'm trying to load a config inside a node module but I'm not sure which is a good practice to do it, I have this scenario:
A config my.config.js:
module.exports = {
content: [
'./src/**/*.{tsx,ts}',
],
}
Then I have a module cli.mjs is supposed to load it:
import arg from 'arg'
import { readFile } from 'fs/promises'
import path from 'path'
let configPath = './my.config.js'
const args = arg({
'--config': String,
'-c': '--config',
})
if (args['--config']) {
configPath = args['--config']
}
console.log(readFile(path.resolve(configPath), { encoding: 'utf8' }))
This will just return a simple string with my config inside, not a javascript object:
`
module.exports = {
content: [
'./src/**/*.{tsx,ts}',
],
}
`
How should I load my config in the right way?
I've saw basically everyone use configs like that in TypeScript or CJS projects, but it's hard to me see how and where they parse these configs, probably I'm missing some basic information about this?

Importing custom CommonJS module fails

I created a CommonJS module in project A in the following way:
const { WebElement } = require('selenium-webdriver');
const { By } = require('selenium-webdriver');
class VlElement extends WebElement {
constructor(driver, selector) {
...
}
async getClassList() {
...
}
}
module.exports = VlElement;
In project B I use the following code:
const VlElement = require('projectA');
class VlButton extends VlElement {
constructor(driver, selector) {
super(driver, selector);
}
...
}
module.exports = VlButton;
When running the code, VLElemlent cannot be found.
It is in my package.json and I can see VLElement under projectB > node_modules > projectA.
What am I doing wrong with my exports?
Thanks in advance.
Regards
Make sure you have a projectB/mode_modules/package.json with a main which points to the file that defines/exports VlElement, like this:
"main": "path/to/file/with/VlElement.js",
When you call require('projectA'); this has to be resolved to a file inside projectA so that it can be evaluated to (and return) the exports from that file. The main entry in the package.json allows this (but defaults to index.js, so if you are using that you don't need package.json, probably, but you should have it anyway).
You can have multiple files with various exports, but remember require('projectA'); can still only return one thing, so the way to do that is usually to have an index.js which looks something like:
module.exports = {
'something': require('./something.js'),
'otherthing': require('./otherthing.js'),
'etc': require('./etc.js'),
};

Strip dynamic requires from webpack

I have a code similar to this:
export default {
something: true,
mockData: process.env.USE_MOCK && require('./mocks/something.js').default
};
process.env.USE_MOCK is set with webpack.DefinePlugin to either true or false. I use this to start my app with USE_MOCK=true npm run dev or npm run dev to either run with mock data or not.
I want Webpack to remove these dynamic require in the build process with UglifyJS' dead code elimination, but I noticed that they remain there and so something.js will be in the built bundle.
In my case the output is similar to:
module.exports = {
something: true,
mockData: (false) && __webpack_require__(181).default
};
Is there a way how I can remove that import completely from the bundle?
Btw. I think something like this works:
let mockData;
if (process.env.USE_MOCK) {
mockData = require('./mocks/something.js').default;
}
export default {
something: true,
mockData
};
I'd prefer the inline require though, as I've got that pattern quite a few times.
Try something like this:
export default {
something: true,
mockData: process.env.USE_MOCK ? require('./mocks/something.js').default : undefined
};
Or build module export separately:
let module_export;
module_export.something = true;
if (process.env.USE_MOCK) {
module_export.mockData = require('./mocks/something.js').default;
}
export module_export;

Fetch requirejs.config from Gruntfile

Is there any way to import a requirejs config in to my grunt config file? Right now I have to keep two identical versions, one in app/main.js and one in my Gruntfile.js:
module.exports = function(grunt) {
// can I import app/main.js requireConfig here?
var requireConfig = {
paths: {
jquery: 'lib/jquery'
// etc...
}
};
});
My main.js looks something like this:
requirejs.config({
paths: {
jquery: 'lib/jquery'
// etc...
}
});
define(['app'], function(app){
app.start();
});
You can use standard module pattern which supports different type of module system like following.
Your requirejs config file like this
amd-config.js
(function(factory) {
if (typeof define === 'function' && define.amd) {
// Register as an AMD module if available...
define('amd-config', [], factory());
} else if (typeof exports === 'object') {
// Next for Node.js, CommonJS, browserify...
module.exports = factory();
} else {
// setting browser global when none of the above are available
window.amdConfig = factory();
}
}
(function() {
var amdConfig = {
baseUrl: 'scripts',
paths: {
//Paths here
}
};
return amdConfig;
}));
In gruntfile you can just require like any other module.
var requireConfig = require('amd-config');
Include it normally like you do in index.html with script tag before app.js
and then in app.js use it like following.
requirejs.config(window.amdConfig);
define(['app'], function(app){
app.start();
});
PS: There are cleaner way of including it in app.js.
More cleaner than second, create global variable require and include the script before requirejs script. requirejs checks if there is global variable with name require containing object. If its there, it is used as a config object. So you dont have to call requirejs.config yourself.
You can require the file like you require other files. In that case it will be treated as a require module and you will receive the object in require callback. call your requirejs.config like following.
```
require(['amd-config'], function(amdConfig){
requirejs.config(amdConfig);
require(['app'], function(app){
app.start();
});
});
```
A simpler approach you could use, if you are using grunt to build the project. You can simply use:
options:{
mainConfigFile: "path/to/Config.js"
}
granted you need to use:
https://github.com/gruntjs/grunt-contrib-requirejs
You can try something like this:
function getRequireConfig(requireFilePath) {
var config;
var configFileContent,
_require;
_require = require;
require = {
data: {},
config : function (configParam) {
this.data = configParam;
},
get : function () {
return this.data;
}
};
configFileContent = readFileSync(requireFilePath);
eval(configFileContent);
config = require.get();
require = _require;
return config;
}
What it is doing is:
Override require definition to a custom implementation
Load require config file
Eval it so that the config function of custom implementation will be
called Get the config object from data

Change hard coded url constants for different environments via webpack

I have a ApiCaller.js module which generate calls to our api server to get data. It has const field API_URL which points to server url.
This API_URL const changes for dev and prod environments.
So when I need to deploy to dev environment I need to change that url (API_URL) manually to point to dev-api-server and vice-versa.
I want these configuration parameters outside the code and during build process I want to change them dynamically so that I can build with different settings.
I am using webpack to bundle my javascript, html, css files.
You can store your API_URL in webpack config:
// this config can be in webpack.config.js or other file with constants
var API_URL = {
production: JSON.stringify('prod-url'),
development: JSON.stringify('dev-url')
}
// check environment mode
var environment = process.env.NODE_ENV === 'production' ? 'production' : 'development';
// webpack config
module.exports = {
// ...
plugins: [
new webpack.DefinePlugin({
'API_URL': API_URL[environment]
})
],
// ...
}
Now in your ApiCaller you can use API_URL as defined variable, which it will be different depend on process.env.NODE_ENV:
ajax(API_URL).then(/*...*/);
(edit) If I have more than production/development config for different environment constants?
Imagine that you have API_URL like in above answer, API_URL_2 and API_URL_3 which should support different environment settings production/development/test
var API_URL = {
production: JSON.stringify('prod-url'),
development: JSON.stringify('dev-url')
};
var API_URL_2 = {
production: JSON.stringify('prod-url-2'),
development: JSON.stringify('dev-url-2'),
test: JSON.stringify('test-url-2')
};
var API_URL_3 = {
production: JSON.stringify('prod-url-3'),
development: JSON.stringify('dev-url-3'),
test: JSON.stringify('test-url-3')
};
// get available environment setting
var environment = function () {
switch(process.env.NODE_ENV) {
case 'production':
return 'production';
case 'development':
return 'development';
case 'test':
return 'test';
default: // in case ...
return 'production';
};
};
// default map for supported all production/development/test settings
var mapEnvToSettings = function (settingsConsts) {
return settingsConsts[environment()];
};
// special map for not supported all production/development/test settings
var mapAPI_URLtoSettings = function () {
switch(environment()) {
case 'production':
return API_URL.production;
case 'development':
return API_URL.development;
case 'test': // don't have special test case
return API_URL.development;
};
};
// webpack config
module.exports = {
// ...
plugins: [
new webpack.DefinePlugin({
'API_URL': mapAPI_URLtoSettings(),
'API_URL_2': mapEnvToSettings(API_URL_2),
'API_URL_3': mapEnvToSettings(API_URL_3)
})
],
// ...
}
(edit 2)
If you pass string as a environment constant you should use JSON.stringify.
You don't need to define new webpack.DefinePlugin multiple times. You can do it in one object passed to new webpack.DefinePlugin - it looks cleaner.
You could set the define plugin to define a PRODUCTION variable as follows (or alternatively to true if you use different configuration files for the builds):
new webpack.DefinePlugin({
PRODUCTION: process.env.NODE_ENV === 'production'
})
Then in your code you will write something like:
var API_URL = PRODUCTION ? 'my-production-url' : 'my-development-url';
During compilation webpack will replace PRODUCTION with its value (so either true or false), and this should allow UglifyJS to minify our expression:
var API_URL = <true/false> ? 'my-production-url' : 'my-development-url';
The worst case scenario is uglify not being able to minify the conditional expression leaving it as is.

Categories