In the below script would I like to read the content of config.toml, and have marked where I would like to do that. What confuses me is how to include the toml.js file which contains the toml reader function.
Throughout the script will I need to read 3 different toml files.
Question
How should I include the toml.js function and how to reuse the function to read 3 different files?
Disclaimer: I am sorry for this super noob question, but this is my first project, and with 6 different ways to write a function, I find it very confusing.
index.js
'use strict'
var minimist = require('minimist')
const toml = require('./src/toml')
module.exports = () => {
var argv = minimist(process.argv.slice(2), {
string: 'input',
string: 'project',
boolean: ['validate'],
boolean: ['help'],
alias: { i: 'input', v: 'validate', h: 'help', p: 'project' },
unknown: function () { console.log('Unkown argument') }
})
if (argv.help || argv.h) {
// help output goes here
}
// read config.toml
}
src/toml.js
module.exports = (filename) => {
const TOML = require('#iarna/toml')
const fs = require('fs');
return TOML.parse(fs.readFileSync(filename, 'utf-8'));
}
It looks like you have everything set up correctly. Inside index.js you should be able to use toml('filename.toml'). This function returns the contents of filename.toml as an object.
src/toml.js exports a function that parses a .toml file. When you use const toml = require('./src/toml') inside index.js, you are assigning toml to the exports of ./src/toml.js (which is the parsing function). This means that in index.js, toml represents the function in ./src/toml.js.
You can then use the toml('filename.toml') as many times as you want in index.js.
Here is your index.js code modified to read the config.toml file and store the object in config...
'use strict'
var minimist = require('minimist')
const toml = require('./src/toml')
module.exports = () => {
var argv = minimist(process.argv.slice(2), {
string: 'input',
string: 'project',
boolean: ['validate'],
boolean: ['help'],
alias: { i: 'input', v: 'validate', h: 'help', p: 'project' },
unknown: function () { console.log('Unkown argument') }
})
if (argv.help || argv.h) {
// help output goes here
}
// read config.toml
const config = toml('config.toml')
}
You should be able to call toml('path/to/config/that/you/want/to/read.toml')
You have required a module src/toml.js. This module exports a function - doesn't really matter how this function is declared in this case. Whenever you import that module - you are given this function.
So:
const iCanCallThisReferenceHoweverIWant = require('./src/toml');
iCanCallThisReferenceHoweverIWant('path/to/a/toml/file.toml');
I think you need to require your dependencies in the toml.js outside of the function definition - I assume it may yell at you about that, but I'm not super-duper-confident about it :)
Proposition + some refactoring:
src/toml.js
const TOML = require('#iarna/toml')
const fs = require('fs');
const readTOML = (filename) => TOML.parse(fs.readFileSync(filename, 'utf-8'));
module.exports = readTOML;
Related
I have a large amount of javascript files split into 4 subdirectory in my app. In grunt I grab all of them and compile them into one file. These files do not have a module.exports function.
I want to use webpack and split it into 4 parts. I don't want to manually go in and require all my files.
I would like to create a plugin that on compilation walks the directory trees, then grabs all the .js file names and paths, then requires all files in the sub directories and adds it to the output.
I want all the files in each directories compiled into a module that I could then require from my entry point file, or include in the assets that http://webpack.github.io/docs/plugins.html mentions.
When adding a new file I just want to drop it in the correct directory and know it will be included.
Is there a way to do this with webpack or a plugin that someone has written to do this?
This is what I did to achieve this:
function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./modules/', true, /\.js$/));
In my app file I ended up putting the require
require.context(
"./common", // context folder
true, // include subdirectories
/.*/ // RegExp
)("./" + expr + "")
courtesy of this post: https://github.com/webpack/webpack/issues/118
It is now adding all my files. I have a loader for html and css and it seems to work great.
How about a map of all the files in a folder?
// {
// './image1.png': 'data:image/png;base64,iVBORw0KGgoAAAANS',
// './image2.png': 'data:image/png;base64,iVBP7aCASUUASf892',
// }
Do this:
const allFiles = (ctx => {
let keys = ctx.keys();
let values = keys.map(ctx);
return keys.reduce((o, k, i) => { o[k] = values[i]; return o; }, {});
})(require.context('./path/to/folder', true, /.*/));
Example of how to get a map of all images in the current folder.
const IMAGES_REGEX = /\.(png|gif|ico|jpg|jpeg)$/;
function mapFiles(context) {
const keys = context.keys();
const values = keys.map(context);
return keys.reduce((accumulator, key, index) => ({
...accumulator,
[key]: values[index],
}), {});
}
const allImages = mapFiles(require.context('./', true, IMAGES_REGEX));
All merits to #splintor (thanks).
But here it is my own derived version.
Benefits:
What modules export is gathered under a {module_name: exports_obj} object.
module_name is build from its file name.
...without extension and replacing slashes by underscores (in case of subdirectory scanning).
Added comments to ease customization.
I.e. you may want to not include files in subdirectories if, say, they are there to be manually required for root level modules.
EDIT: If, like me, you're sure your modules won't return anything else than (at least at root level) a regular javascript object, you
can also "mount" them replicating their original directory structure
(see Code (Deep Version) section at the end).
Code (Original Version):
function requireAll(r) {
return Object.fromEntries(
r.keys().map(function(mpath, ...args) {
const result = r(mpath, ...args);
const name = mpath
.replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
.replace(/\//g, '_') // Relace '/'s by '_'s
;
return [name, result];
})
);
};
const allModules = requireAll(require.context(
// Any kind of variables cannot be used here
'#models' // (Webpack based) path
, true // Use subdirectories
, /\.js$/ // File name pattern
));
Example:
Sample output for eventual console.log(allModules);:
{
main: { title: 'Webpack Express Playground' },
views_home: {
greeting: 'Welcome to Something!!',
title: 'Webpack Express Playground'
}
}
Directory tree:
models
├── main.js
└── views
└── home.js
Code (Deep Version):
function jsonSet(target, path, value) {
let current = target;
path = [...path]; // Detach
const item = path.pop();
path.forEach(function(key) {
(current[key] || (current[key] = {}));
current = current[key];
});
current[item] = value;
return target;
};
function requireAll(r) {
const gather = {};
r.keys().forEach(function(mpath, ...args) {
const result = r(mpath, ...args);
const path = mpath
.replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
.split('/')
;
jsonSet(gather, path, result);
});
return gather;
};
const models = requireAll(require.context(
// Any kind of variables cannot be used here
'#models' // (Webpack based) path
, true // Use subdirectories
, /\.js$/ // File name pattern
));
Example:
Result of previous example using this version:
{
main: { title: 'Webpack Express Playground' },
views: {
home: {
greeting: 'Welcome to Something!!',
title: 'Webpack Express Playground'
}
}
}
this works for me :
function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./js/', true, /\.js$/));
NOTE: this can require .js files in subdirs of ./js/ recursively.
I'm trying to mock a function and not sure what i'm doing wrong here. I have this function "getGroups"
getGroups:
export const getGroups = async () => {
try {
const groupApiUrl = getDashboardPath(GROUPS_TAB_INDEX);
const data = await fetch(groupApiUrl, { cache: 'force-cache' });
const userData = await data.json();
return userData;
} catch (error) {
throw Error(error);
}
};
___mocks___/getGroups.js:
export default async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
getGroups.test.js:
jest.mock('./getGroups.js');
// eslint-disable-next-line import/first
import { getGroups } from './getGroups';
const fakeRespose = {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
describe('getGroups', () => {
it('returns data', async () => {
const data = await getGroups();
console.log('DATA', data); <---- UNDEFINED?
expect(data).toBeDefined();
expect(data).toMatchObject(fakeRespose);
});
it('handles error', async () => {
// const data = await getGroups();
await getGroups().toThrow('Failed');
});
});
What are you doing wrong here?
Default export in your mock instead of named as in the implementation
In your implementation you're using named export and you're importing { getGroups } so to make it work you need to change your mock like this
__mocks__\getGroups.js
export const getGroups = async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
working example
TL;DR
Testing mock
There is no point at all to test mock function. This does not proves your implementation is working. Even if you change your implementation your tests will still pass.
Use mocks only for the dependencies of your implementation
Use jest.genMockFromModule
It will create jest.fn() for each of the module's exported methods and will preserve the constants, allowing you to change the return value/implementation for some test cases and will also be able to write assertions if the function have been called
__mocks__\getGroups.js
const mock = jest.genMockFromModule('../getGroups');
mock.getGroups.mockResolvedValue({
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
})
module.exports = mock;
Jest will automatically hoist jest.mock calls (read more...)
So you can safely leave the import statements first and then call jest.mock
From Jest Docs, here's an example of a Mock.
jest.mock('../moduleName', () => {
return jest.fn(() => 42);
});
// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';
In your case data is undefined, because you haven't actually supplied a mocked implementation for the function or the mock hasn't worked and you're still calling the original function.
Example Reference: https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options
However, in your simple case you could also solve this with a spy, either jest.spyOn or jest.fn(). Here are two solutions to what you're trying to achieve. You can look at the code and run it here: https://repl.it/repls/FairUnsungMice
UPDATE after comment:
Manual mocks are defined by writing a module in a __mocks__/
subdirectory immediately adjacent to the module. For example, to mock
a module called user in the models directory, create a file called
user.js and put it in the models/__mocks__ directory. Note that the
__mocks__ folder is case-sensitive, so naming the directory __MOCKS__ will break on some systems.
Double check the naming, directory structure & type of exports you've setup - they should match. Also, it's worth checking this out: https://github.com/facebook/jest/issues/6127 - looks like an open issue with jest. If you need a solution, look at using a different approach as I mentioned.
Reference: https://jestjs.io/docs/en/manual-mocks
I am trying to mock a non-exported function via 'jest' and 're-wire'.
Here I am trying to mock 'iAmBatman' (no-pun-intended) but it is not exported.
So I use rewire, which does it job well.
But jest.mock doesn't work as expected.
Am I missing something here or Is there an easy way to achieve the same ?
The error message given by jest is :
Cannot spy the property because it is not a function; undefined given instead
service.js
function iAmBatman() {
return "Its not who I am underneath";
}
function makeACall() {
service.someServiceCall(req => {
iAmBatman();
});
return "response";
}
module.export = {
makeACall : makeACall;
}
jest.js
const services = require('./service');
const rewire = require('rewire');
const app = rewire('./service');
const generateDeepVoice = app.__get__('iAmBatman');
const mockDeepVoice = jest.spyOn(services, generateDeepVoice);
mockDeepVoice.mockImplementation(_ => {
return "But what I do that defines me";
});
describe(`....', () => {
test('....', done => {
services.makeACall(response, () => {
});
});
})
It is not entirely clear what your goal is, but if you look at the documentation of jest.spyOn, you see that it takes a methodName as the second argument, not the method itself:
jest.spyOn(object, methodName)
This explains your error: you didn't give the function name, but the function itself.
In this case, using jest.spyOn(services, 'iAmBatman') wouldn't work, since iAmBatman is not exported, and therefore services.iAmBatman is not defined.
Luckily, you don't need spyOn, as you can simply make a new mock function, and then inject that with rewire's __set__ as follows:
(note that I deleted the undefined service.someServiceCall in your first file, and fixed some typos and redundant imports)
// service.js
function iAmBatman() {
return "Its not who I am underneath";
}
function makeACall() {
return iAmBatman();
}
module.exports = {
makeACall: makeACall
}
// service.test.js
const rewire = require('rewire');
const service = rewire('./service.js');
const mockDeepVoice = jest.fn(() => "But what I do that defines me")
service.__set__('iAmBatman', mockDeepVoice)
describe('service.js', () => {
test('makeACall should call iAmBatman', () => {
service.makeACall();
expect(mockDeepVoice).toHaveBeenCalled();
});
})
Another option would be to restructure your code with iAmBatman in a seperate module, and then mock the module import with Jest. See documentation of jest.mock.
Background
I have Mongoose Schema about Surveys, that needs to check if the Survey belongs to a set of countries that is in another collection.
Code
To check this, I have a surveySchema, a countrySchema, and a file where I create the models and connect to the DB.
To perform the check that a survey belongs to a valid country, I am using Mongoose async validators in surveySchema like the following:
surveySchema.js:
"use strict";
const mongoose = require("mongoose");
const surveySchema = {
subject: { type: String, required: true },
country: {
type: String,
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {
const {
Country
} = require("./models.js").getModels();
const countriesNum = await Country.find({"isoCodes.alpha2": val}).count();
callback(countriesNum === 1);
}
},
message: "The country {VALUE} is not available in the DB at the moment."
}
}
};
module.exports = new mongoose.Schema(surveySchema);
module.exports.surveySchema = surveySchema;
countrySchema.js:
"use strict";
const mongoose = require("mongoose");
const countrySchema = {
name: { type: String, required: true },
isoCodes:{
alpha2: { type: String, required: true }
}
}
};
module.exports = new mongoose.Schema(countrySchema);
module.exports.countrySchema = countrySchema;
models.js:
"use strict";
const mongoose = require("mongoose");
const fs = require("fs");
const DB_CONFIG = "./config/dbConfig.json";
/**
* Module responsible for initializing the Models. Should be a Singleton.
*/
module.exports = (function() {
let models;
const initialize = () => {
//Connect to DB
const {
dbConnectionURL
} = JSON.parse(fs.readFileSync(DB_CONFIG, "utf8"));
mongoose.connect(dbConnectionURL);
mongoose.Promise = global.Promise;
//Build Models Object
models = {
Country: mongoose.model('Country', require("./countrySchema.js")),
Survey: mongoose.model('Survey', require("./surveySchema.js"))
};
};
const getModels = () => {
if (models === undefined)
initialize();
return models;
};
return Object.freeze({
getModels
});
}());
The idea here is that I am using the models.js file in other places as well. Because this file is also responsible for connecting to the DB, I decided to make it a Singleton. This way, I should only connect once, and all further requests will always return the same Models, which would be ideal.
Problem
The problem here is that I have a circular dependency that results in:
RangeError: Maximum call stack size exceeded at exports.isMongooseObject (/home/ubuntu/workspace/server/node_modules/mongoose/lib/utils.js:537:12)
...
The flow of code leading to this error is:
Code runs getModels()`
getModels() checks that models is undefined and runs initialize()
initialize() tries to create the models.
When creating the Survey model Survey: mongoose.model('Survey', require("./surveySchema.js")) it runs into the validator function, which again requires models.js
Infinite loop begins
Questions
Is there any other way to check if a Survey's country is part of the county's collection without making a async validation?
How can I structure/change my code so this doesn't happen?
As said in the comments, I think you are a bit confused about how you are using your models.js module. I think this is what is happening:
You are exporting a single function from models.js:
models.js
module.exports = function() { ... };
Therefore, when you require it, you just get that single function:
surveySchema.js
const models = require("./models.js");
models is now a function. Which means every time you call it, you run through the code in models.js and create a new variable let models;, and also new functions initialize() and getModels().
You could move the let models out of the anonymous function into the global scope which would probably fix it, but for my money you only want to run the anonymous function in models.js once, so I would invoke it immediately and set the exports of the module to its result:
models.js
module.exports = (function() {
// codez here
return Object.freeze({
getModels
});
})(); // immediately invoke the function.
Use it:
// models is now the frozen object returned
const { Survey } = models.getModels();
As for options to validation, there's no reason why you can't add your own middleware validation code if normal async validation doesn't do it for you using serial or parallel mechanisms as described in the docs.
Update after comments
The second problem as you point out is that during first execution of getModels() -> initialize() you call require('./surveySchema.js'), but this calls getModels() which is still in the process of being called and hasn't yet initialized models, so initialize() is re-entered.
I think what you're trying to achieve is fine (survey schema depends on customer model), because you can still draw an object graph without any circular dependencies, and it's just the way you've implemented it that you've ended up with one. The simplest way to deal with this I think is actually to keep the circular reference, but defer the point at which you call getModels() in surveySchema.js:
"use strict";
const mongoose = require("mongoose");
const models = require("./models.js");
const surveySchema = {
subject: { type: String, required: true },
country: {
type: String,
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {
// !! moved from the global scope to here, where we actually use it
const {
Country
} = models.getModels();
const countries = await Country.find({"isoCodes.alpha2": val});
callback(countries.length === 1);
}
},
message: "The country {VALUE} is not available in the DB at the moment."
}
}
};
module.exports = new mongoose.Schema(surveySchema);
module.exports.surveySchema = surveySchema;
A neater and probably more extensible way of approaching it, though, might be to separate out the connection code from the models code, since it's a different concept altogether.
Update #2 after more comments
The infinite stack you're seeing is because you have not used the API correctly. You have:
const surveySchema = {
country: {
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {...}
},
},
...
}
};
You should have:
const surveySchema = {
country: {
validate: {
isAsync: true,
validator: async function(val, callback) {...}
},
...
}
};
As per the docs.
I have a large amount of javascript files split into 4 subdirectory in my app. In grunt I grab all of them and compile them into one file. These files do not have a module.exports function.
I want to use webpack and split it into 4 parts. I don't want to manually go in and require all my files.
I would like to create a plugin that on compilation walks the directory trees, then grabs all the .js file names and paths, then requires all files in the sub directories and adds it to the output.
I want all the files in each directories compiled into a module that I could then require from my entry point file, or include in the assets that http://webpack.github.io/docs/plugins.html mentions.
When adding a new file I just want to drop it in the correct directory and know it will be included.
Is there a way to do this with webpack or a plugin that someone has written to do this?
This is what I did to achieve this:
function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./modules/', true, /\.js$/));
In my app file I ended up putting the require
require.context(
"./common", // context folder
true, // include subdirectories
/.*/ // RegExp
)("./" + expr + "")
courtesy of this post: https://github.com/webpack/webpack/issues/118
It is now adding all my files. I have a loader for html and css and it seems to work great.
How about a map of all the files in a folder?
// {
// './image1.png': 'data:image/png;base64,iVBORw0KGgoAAAANS',
// './image2.png': 'data:image/png;base64,iVBP7aCASUUASf892',
// }
Do this:
const allFiles = (ctx => {
let keys = ctx.keys();
let values = keys.map(ctx);
return keys.reduce((o, k, i) => { o[k] = values[i]; return o; }, {});
})(require.context('./path/to/folder', true, /.*/));
Example of how to get a map of all images in the current folder.
const IMAGES_REGEX = /\.(png|gif|ico|jpg|jpeg)$/;
function mapFiles(context) {
const keys = context.keys();
const values = keys.map(context);
return keys.reduce((accumulator, key, index) => ({
...accumulator,
[key]: values[index],
}), {});
}
const allImages = mapFiles(require.context('./', true, IMAGES_REGEX));
All merits to #splintor (thanks).
But here it is my own derived version.
Benefits:
What modules export is gathered under a {module_name: exports_obj} object.
module_name is build from its file name.
...without extension and replacing slashes by underscores (in case of subdirectory scanning).
Added comments to ease customization.
I.e. you may want to not include files in subdirectories if, say, they are there to be manually required for root level modules.
EDIT: If, like me, you're sure your modules won't return anything else than (at least at root level) a regular javascript object, you
can also "mount" them replicating their original directory structure
(see Code (Deep Version) section at the end).
Code (Original Version):
function requireAll(r) {
return Object.fromEntries(
r.keys().map(function(mpath, ...args) {
const result = r(mpath, ...args);
const name = mpath
.replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
.replace(/\//g, '_') // Relace '/'s by '_'s
;
return [name, result];
})
);
};
const allModules = requireAll(require.context(
// Any kind of variables cannot be used here
'#models' // (Webpack based) path
, true // Use subdirectories
, /\.js$/ // File name pattern
));
Example:
Sample output for eventual console.log(allModules);:
{
main: { title: 'Webpack Express Playground' },
views_home: {
greeting: 'Welcome to Something!!',
title: 'Webpack Express Playground'
}
}
Directory tree:
models
├── main.js
└── views
└── home.js
Code (Deep Version):
function jsonSet(target, path, value) {
let current = target;
path = [...path]; // Detach
const item = path.pop();
path.forEach(function(key) {
(current[key] || (current[key] = {}));
current = current[key];
});
current[item] = value;
return target;
};
function requireAll(r) {
const gather = {};
r.keys().forEach(function(mpath, ...args) {
const result = r(mpath, ...args);
const path = mpath
.replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
.split('/')
;
jsonSet(gather, path, result);
});
return gather;
};
const models = requireAll(require.context(
// Any kind of variables cannot be used here
'#models' // (Webpack based) path
, true // Use subdirectories
, /\.js$/ // File name pattern
));
Example:
Result of previous example using this version:
{
main: { title: 'Webpack Express Playground' },
views: {
home: {
greeting: 'Welcome to Something!!',
title: 'Webpack Express Playground'
}
}
}
this works for me :
function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./js/', true, /\.js$/));
NOTE: this can require .js files in subdirs of ./js/ recursively.