loop in a module export javascript/sequelize - javascript

I have an index.js script that contains sequelize models
here is the tree structure of my folder
models/
files/
xml_files/
csv_files/
txt_files/
index.js
server.js
this my index.js code :
const generate_files = require('./files')
const File = generate_files(sequelize, DataTypes)
const generate_xml_files = require('./xml_files')
const Xml_File = generate_xml_files(sequelize, DataTypes)
const generate_csv_files = require('./csv_files')
const Csv_File = generate_csv_files(sequelize, DataTypes)
const generate_txt_files = require('./txt_files')
const Txt_File = generate_txt_files(sequelize, DataTypes)
module.exports = {
Files, Xml_File, Csv_File, Txt_File
}
in `` server.js ``` I imported each model like this :
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
Now in server.js I want to get the name of the module sequelize then I check if the name of the module matches the name of the table like this :
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
if (Files.name == TableName){
Files.findAll()
}
if (Xml_File.name == TableName){
Xml_File.findAll()
}
if (Csv_File.name == TableName){
Csv_File.findAll()
}
....
how can I avoid doing tests every time ?

You can do:
const Example = require('./index')
Example.Files
As for making a loop, I'm not sure you can do that, but you can do:
export const File = generate_files(sequelize, DataTypes)
Which is the same thing as:
module.exports = {File}

You can create an object and loop through the entries, like so.
var exports = {
'File': './files',
'Xml_File': './xml_files',
'Csv_File': './csv_files',
'Txt_File': './txt_files'
};
var e = {};
Object.entries(exports).forEach(ex=>{
e[ex[0]] = require(ex[1])(sequelize, DataTypes);
});
module.exports = e;
Then you'll want to import them from this index file...
const {Files, Xml_File, Csv_File, Txt_File} = require('./models/index.js')
Per your update..
const Models = require('./models/index.js');
Object.entries(Models).forEach(model=>{
if (model.name == TableName){
model.findAll();
}
}

To simplify your index.js you could also do it like this:
module.exports = {
Files: require('./files')(sequelize, DataTypes),
Xml_File: require('./xml_files')(sequelize, DataTypes),
Csv_File: require('./csv_files')(sequelize, DataTypes),
Txt_File: require('./txt_files')(sequelize, DataTypes)
}
it's easier to read, at least for me.
Another way is to loop over the dir so you never need to change the code in index.js even if you add another folder like /xxx_files
const Fs = require('fs');
var dir = Fs.readdirSync(__dirname, { withFileTypes: true });
var exports = {};
dir.forEach(d => {
if (d.isDirectory())
exports[d.name] = require('./' + d)(sequelize, DataTypes);
});
// !!!!!!! the names of the exports will be names of the directories
module.exports = exports;
And to avoid the checks if (Files.name == TableName)
all you need is to not import like this:
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
but rather like this:
const models = require('./models')
// models = { files, xml_files, csv_files, txt_files }
then you can do this:
var db = models[TableName] // assuming TableName is files, xml_files etc.
if (db)
doSomething();

Related

NodeJS : Dynamic import modules

Is there any way in JS to import dynamically some modules ?
For example, this is my architecture :
| - index.js
| - modules
| - SomeModule
| - router.js
| - controller.js
| - SomeOtherModule
| - SubModule
| - router.js
| - controller.js
| - controller.js
My goal is to import all the router.js modules in the index.js file so I was thinking about something like this :
import fs from "fs"
import path from "path"
function scanDirForRouters (dirPath, name = "") {
let routers = []
const files = fs.readdirSync(dirPath)
for(const file of files) {
const isDirectory = fs.statSync(path.join(dirPath, file)).isDirectory()
if(isDirectory) {
routers = [...routers, ...scanDirForRouters(path.join(dirPath, file), name + file)]
}
else if(file === "router.js") {
routers.push(`import ${name}Router from ${path.join(dirPath, file)}`)
}
}
return routers
}
let allRouters = scanDirForRouters(path.join(path.dirname("."), "modules"))
So if I do a console.log(allRouters) it gives me :
[
'import SomeModuleRouter from modules/SomeModule/Router.js',
'import SomeOtherModuleSubModuleRouter from modules/SomeOtherModule/SubModule/Router.js'
]
So i wish there is a way to execute these command in my script now ... or maybe another way to do it?
Thanks a lot
Thanks to #joprocoorp i found the answer using directly the import function in the loop.
This is my code :
import fs from "fs"
import path from "path"
async function scanDirForRouters (dirPath, name = "") {
let routers = []
const files = fs.readdirSync(dirPath)
for(const file of files) {
const isDirectory = fs.statSync(path.join(dirPath, file)).isDirectory()
if(isDirectory) {
routers = [...routers, ...(async scanDirForRouters(path.join(dirPath, file), name + file))]
}
else if(file === "router.js") {
const router = await import(`./${path.join(dirPath, file)}`)
routers.push(router)
}
}
return routers
}
let allRouters = await scanDirForRouters(path.join(path.dirname("."), "modules"))
then I can make a loop on the routers and do whatever i want :)
Please, check eloader https://www.npmjs.com/package/eloader
It's load entire folders(recursive) or object instance.
const eloader = require('eloader');
const express = require('express');
let options = {debug: false};
eloader.addObject('options', options) //New methods
.addServices('services') //This will be loaded first.
.addRoutes('routes') //This third
.run('loginRoute'); //This second
You could use require instead. I implemented it by replacing the routers.push line :
function scanDirForRouters (dirPath, name = "") {
let routers = []
const files = fs.readdirSync(dirPath)
for(const file of files) {
const isDirectory = fs.statSync(path.join(dirPath, file)).isDirectory()
if(isDirectory) {
routers = [...routers, ...scanDirForRouters(path.join(dirPath, file), name + file)]
}
else if(file === "router.js") {
require(`${path.join(dirPath, file)}`)
}
}
return routers
}
let allRouters = scanDirForRouters(path.join(path.dirname("."), "modules"))

proxyquire Error: ENOENT: no such file or directory, scandir

Hello I am new to testing with mocha/chai/sinon and sequelize-test-helpers
Trying to use proxyquire to override the require but having issues
Getting this following error about the path:
Error: ENOENT: no such file or directory, scandir 'C:<local-directories-path>\ecommerce-pern-app\server\src\models'
I dont get why there is a src folder when I don't have a src folder at all I am using the proxyquire in the test file and its path is from the server directory would be:
server/specs/services/user-service.spec.js
"use strict";
const chai = require('chai');
const {match, stub, resetHistory, spy} = require('sinon');
const proxyquire = require('proxyquire');
const path = require('path');
const service = path.resolve('./services/userService.js')
var sinonChai = require("sinon-chai");
chai.should();
chai.use(sinonChai);
console.log(service)
const {makeMockModels, sequelize, dataTypes,} = require('sequelize-test-helpers');
describe('Idea Controller', function () {
const uid = '6a88e9b5-33a2-403f-ac3d-e86413ac101d'
const data = {
email: 'testface#test.com',
password: '123456',
is_admin: false,
first_name: 'Testy',
last_name: 'McTestface',
google_id: null,
facebook_id: null
}
describe('findAll()', function () {
it('Success case ', function () {
const mockResponse = () => {
const res = {};
res.json = stub().returns(res);
return res;
};
let res = mockResponse();
const User = {findAll: stub()};
const mockModels = makeMockModels({User});
Idea.findAll.resolves(data);
const UserService = proxyquire(service, {
"save": {}
});
UserService.findAll({}, res);
Idea.findAll.should.have.been.called; // passes
res.json.should.have.been.called; //fails
});
})
});
In the above code I am using the proxyquire like this:
const proxyquire = require('proxyquire');
const path = require('path');
const service = path.resolve('./services/userService.js')
const {makeMockModel} = require('sequelize-test-helpers');
const mockModels = makeMockModels({User});
const UserService = proxyquire(service, {
"../models": mockModels
});
As I am trying to use the path to find the server/service/userService.js file which is relatively located from the test file at ../../services/userService.js. I have got this bug that there is src folder there when I do not have a src directory at all even!
As the bug is saying:
Error: ENOENT: no such file or directory, scandir 'C:<local-directories-path>\ecommerce-pern-app\server\src\models'
Whatever I try about file path is not working I tried path.resolve, path.join and directly typing the path into it as like ../../services/userService.js
here is the
server/services/userService.js
const Models = require('../models');
const { User } = Models;
const save = async ({ id, ...data }) => {
const user = await User.findOne({ where: { uid: id } })
if (user) return await user.update(data)
return null
}
module.exports = save;
I just want the path to with proxyquire to work
What is this \src\models path from the error, I dont have a src/models path route at all!
This is a quote from sequelize-test-helpers's readme.
As a convenience, makeMockModels will automatically populate your mockModels with mocks of all of the models defined in your src/models folder (or if you have a .sequelizerc file it will look for the models-path in that). Simply override any of the specific models you need to do stuff with.
So you need to provide .sequelizerc file and define models-path.

Configuring Cypress.env() variable to an imported module file

I'm trying to set & config a new env variable in Cypress.io. In the file cypress/plugins/index.js, I have imported another file like so:
const { getBranch } = require('path/to/file/goes/here/getBranch');
Then, I'm trying to set the new env variable like so:
// cypress/plugins/index.js
module.exports = (on, config) => {
terminalReport.installPlugin(on);
const configuration = config;
configuration.env.injectMainBranchId = getBranch('develop');
// more code below...
However, this is not working. However, if I hardcode the value it does work:
configuration.env.injectMainBranchId = 'develop';
You see the result in the screenshot below:
This is the dummy content of getBranch module:
const getBranch = branch => {
return branch;
};
module.export = { getBranch };
What am I doing wrong?
Typo in getBranch:
const getBranch = branch => {
return branch;
};
module.export = { getBranch };
export should be changed to exports

How can I make static variable in `module.exports = class` in node.js

How can I initialize a static variable in module.exports = class in node.js.
Basically, what I'm trying to achieve is, if StaticVariable is null, Ill get data from a json file. Then store it in StaticVariable.
module.exports = class Config {
static fetch() {
if ( StaticVariable === null ) {
const fs = require('fs');
const data = fs.readFileSync('./config.json');
const config = JSON.parse(data);
StaticVariable = config;
}
return StaticVariable;
}
}
Function fetch() will be called several times so it is unnecessary to readFileSync every call.
Static-only class is an antipattern in JavaScript because a class is never instantiated.
In case there's a need to have a method that lazily loads JSON file, a plain object can be used. There's already such object in module scope, module.exports:
const fs = require('fs');
let StaticVariable;
exports.fetch = () => {
if ( StaticVariable == undefined ) { // not "=== null"
const data = fs.readFileSync('./config.json');
const config = JSON.parse(data);
StaticVariable = config;
}
return StaticVariable;
}
There may be no need to parse it manually because this could be handled by require('./config.json') one-liner and with more consistent relative paths.
In case JSON file can be eagerly loaded, this can be simplified to:
exports.config = require('./config.json');
If there's a need for Config class and it should access configuration object, it can refer to it, e.g.:
exports.Config = class Config {
constructor() {
this.config = deepClone(exports.config);
}
modify() {
// modify this.config
}
};
I can think of several ways to achieve what you are asking.
Saving it in a global variable
//initialise it here
var StaticVariable = null;
//however if you initialise it here, it makes more sense to just load it once
const fs = require('fs');
const data = fs.readFileSync('./config.json');
const config = JSON.parse(data);
StaticVariable = config;
module.exports = class Config {
static fetch() {
return StaticVariable;
}
}
Or just use require. Require will do the same thing what you want to do. It will read the file config.json, try to parse it as a valid json and it will do this only once.
module.exports = class Config {
static fetch() {
return require('./config.json');
}
}
Starting from (node 15.2.1) ES2020, static private class fields is supported. So from now on static class may not be anti pattern and you can instantiate a class using new keywords. ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
module.exports = class Config {
static #StaticVariable = null;
static fetch() {
if ( StaticVariable === null ) {
const fs = require('fs');
const data = fs.readFileSync('./config.json');
const config = JSON.parse(data);
StaticVariable = config;
}
return StaticVariable;
}
}
Where # sign means private more reference can be found in https://node.green, but still the easiest way is described in other answers
exports.config = require('./config.json');

Does having the same `require` in multiple files increase runtime

So I'm planning to separate my functions into separate files and then import them into a single index.js which then becomes the main exporter. So I'm wondering if having something like var bcrypt = require('bcrypt') in several of my files be slower than just having it in one file.
Here's how I'm planning to group and export in index.js
const fs = require('fs');
const path = require('path')
const modules = {}
const files = fs.readdirSync(__dirname)
files.forEach(file => {
if (file === 'index.js') return
let temp = require(path.join(__dirname, file))
for (let key in temp) {
modules[key] = temp[key]
}
});
module.exports = modules
As an example of what I mean:
file1.js
var bcrypt = require("bcrypt");
module.exports.file1test = "hi"
file2.js
var bcrypt = require("bcrypt");
module.exports.file2test = "bye"
No, it does not. Whenever a module is required for the first time, the module's code runs, assigns something to its exports, and those exports are returned. Further requires of that module simply reference those exports again. The logic is similar to this:
const importModule = (() => {
const exports = {};
return (name) => {
if (!exports[name]) exports[name] = runModule(name);
return exports[name];
};
})();
So, multiple imports of the same module is no more expensive than referencing an object multiple times.

Categories

Resources