Class is not a constructor - javascript

I'm quite new to Javascript. I had to switch my project to type "Module" because an import required it. However it totally broke my code.
I am using an AutoLoader to load in all my created classes:
const modules = new Autoloader(path.resolve('./modules'), {
ignore: [
'command.js',
'service.js',
'event.js'
]
});
Normally when I printed out modules it would look like:
{ commands: { invite: class Invite extends Command } }
Now it doesnt recognize the class any more:
{ commands: { invite: undefined } }
The autoloader loads all the classes out (for example) my commands directory.
const api = require('../../api');
const Command = require('./command');
class Invite extends Command {
prefix = '/invite';
handle(message) {
let args = this.stripWithoutLower(message);
if (message.isGroupMsg !== this.groupOnly) {
return;
}
if (typeof args[1] !== 'undefined') {
let inviteCode = args[1].split('/');
if (typeof inviteCode[3] !== 'undefined') {
this.client.joinGroup(inviteCode[3]);
}
}
}
}
module.exports = Invite;
Normally I would use module.exports to export the class. Inside my main.js file I would them loop through the classes I've autoloaded to initiate them. For example:
const commandList = [];
Object.keys(modules['commands']).forEach(key => {
commandList.push(new modules['commands'][key](client));
});
The above coded was working until I had to switch everything to type "Module". Im very new to javascript forgive me about any mis typings.
I've read online that I had to switch module.exports to exports.default = Invite; or to export Class. Unfortunately both didn't worked.
What error am I getting:
TypeError: modules.commands[key] is not a constructor

Related

What is the best way to import/export variables in typescript?

I'm developing test automation scripts in Cucumber + Puppeteer + Typescript. I'm facing the problem of importing variables being declared in the main module, something like index.js. First, a few words about what I want to achieve:
I'd like to run my tests by executing test-runner.ts, not by npm run cucumber because the requirements need to have more control on the flow. Draft of the test-runner.ts module looks like this:
const exec = require('child_process').exec;
const commandLineArgs = require('command-line-args');
export let launchUrl: string;
const optionDefinitions: Array<object> = [
{ name: 'country', alias: 'c' },
{ name: 'environment', alias: 'e' },
{ name: 'headless', alias: 'h' },
];
function initGlobals() {
const options = commandLineArgs(optionDefinitions);
if (options.environment === 'integration') {
launchUrl = 'https://example.url.com';
}
}
function main() {
let cucumber: any;
let cucumberHtmlReporter: any;
cucumber = exec('./node_modules/.bin/cucumber-js', (stdout: any, err: any) => {
console.log(err);
console.log(`stdout: ${stdout}`);
});
cucumber.on('exit', () => {
cucumberHtmlReporter = exec('node cucumber-html-reporter.js', (stdout: any, err: any) => {
console.log(err);
console.log(`stdout: ${stdout}`);
});
});
}
initGlobals();
main();
So, as you see it mainly parse arguments, run Cucumber and exports a variable. The variable is imported in step definition file. Theoretically, it should work fine but unfortunately, it doesn't. While importing the whole function is executed once again. That means every time when import { launchUrl } from ../test-runner is executed, a new Cucumber application is run and some kind of loop happens.
The question is: how should I export the variables to achieve my goal and avoid the situation like that?
Its best practice anyway to store your constants in a seperate config file that you can export your launchUrl from. Your test-runner.ts would than import it and mutate it as needed.
export const URL_CONFIG = { launchUrl: '' };
then in your test-runner:
import { URL_CONFIG } from './config';
URL_CONFIG.launchUrl = 'foo'; //Everywhere in the ap launchUrl is 'foo'

fs.readFile is not a function

I'm new to Angular and Nodejs. I'm currently developing a Web Application using Angular 5. I want to create a node module and publish it to npm and use it in my app. This module should read from an xml file to create some objects. It always gives me the same error ( fs_1.readFile is not a function). I don't know if it 's possible to read from xml inside the node module.
I'm trying to create an npm package, within this package there will be an xml file. inside one of the classes of this package I want to read the xml file.
Here is screenshot from #types/node module. The function exists
Here is my index.js :
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const xml_js_1= require("xml-js");
class PasswordsConfiguration{
constructor(){
this.passwords=new Array();
this.companies=new Array();
this._json = undefined;
this.readXML()
}
readXML(){
var name,isFunction, maskAND, maskOR, key, defaultValue,companyName,displayedName;
try{
fs_1.readFile('./passwords-config.xml', 'utf8', (err, data) => {
// just neglect the rest of the code, it doesn#t enter this function.
if (err) {
}
else {
this._json = xml_js_1.xml2js(data, {
compact: true,
nativeType: true,
ignoreDeclaration: true,
ignoreComment: true
});
this._json.Configuration.Passwords.every((password) => {
name =password._attributes.name;
isFunction=Boolean(password._attributes.isFunction);
maskAND = parseInt(password._attributes.maskAND,16);
maskOR = parseInt(password._attributes.maskOR,16);
key = password._attributes.key;
defaultValue = parseInt(password._attributes.defaultValue,16);
this.passwords.push(new Password(name,isFunction, maskAND, maskOR, key, defaultValue));
});
this._json.Configuration.Companies.every((company) => {
companyName =company._attributes.name;
displayedName =company._attributes.displayedName;
this.companies.push(new Company(companyName,displayedName));
});
}
});
}catch(e){
}
}
}
exports.PasswordsConfiguration = PasswordsConfiguration;
Here is the index.d.ts
export declare class PasswordsConfiguration {
private _json:any;
passwords:Array<Password>;
companies:Array<Company>;
constructor();
readXML():void;
}
So is this functionality applicable? What's wrong in the code?

Calling an object in main.js from a module in Electron

I have an Electron app with 2 modules, one of them being the standard Menu. When I click a menu item, I want it to call an instantiated module's function.
The only solution I've found is to have my instantiated object being a property of the main electron.app object, which is globally available.
Here's my example:
main.js
const electron = require('electron');
const app = electron.app;
const WindowManager = require('components/WindowManager');
let windowManager = new WindowManager(); // <- I want my menu item to call a function from this object
const MainMenu = require('components/MainMenu');
let mainMenu = new MainMenu();
function initApp() {
let menuTemplate = mainMenu.getTemplate();
let menuBuilt = electron.Menu.buildFromTemplate(menuTemplate);
electron.Menu.setApplicationMenu(menuBuilt);
}
function mainTestFileOpen() {
console.log('File open test function in main.js');
}
// I'm trying to avoid doing this
app.testFileOpen = function() {
console.log('Function is part of "app" so globally accessible...');
}
// I'm trying to avoid doing this too
app.appWindowManager = new WindowManager();
// Start the app
app.on('ready', initApp);
components/WindowManager.js
class WindowManager {
constructor() {
this.doFileOpen = this.doFileOpen.bind(this);
}
doFileOpen() {
console.log('File open from WinwdowManager module');
}
}
module.exports = WindowManager;
components/MainMenu.js
const electron = require('electron');
class MainMenu {
constructor() {
this.template = [];
this.init = this.init.bind(this);
this.getTemplate = this.getTemplate.bind(this);
// Initialize
this.init();
}
getTemplate() {
return this.template;
}
init() {
this.template = [{
label: 'File',
submenu: [{
label: "Open File",
click() {
/** Calling a function in main.js does NOT work **/
mainTestFileOpen();
/** Calling an object in main.js doe NOT work **/
windowManager.doFileOpen();
/** If the function is part of "app" then it works **/
electron.app.testFileOpen();
/** If the instantiated object is part of "app" then it works **/
electron.app.appWindowManager.doFileOpen();
}
}]
}]
}
}
module.exports = MainMenu;
I think what I am not getting is the scope of click() in an Electron Menu template.
You're trying to call a function from a different module (every file in Node is its own module, which is different from the typical JS environment) without first importing the module.
It's not enough to just write a function in the main.js module:
function mainTestFileOpen() {
console.log('File open test function in main.js');
}
And expect to call it from the MainMenu.js module. You must first export it:
export function mainTestFileOpen() { ... }
Then, in MainMenu.js, you can import it at the top:
import { mainTestFileOpen } from "../main";
Same thing with windowManager. It doesn't look like you're doing anything with WindowManager from main.js, so just move the import and instantiation to MainMenu.js:
import { WindowManager } from "./WindowManager";
let windowManager = new WindowManager();
And then you'll be able to do:
windowManager.doFileOpen();
Side Note:
You do stuff like this in your constructor: this.doFileOpen = this.doFileOpen.bind(this);
There is no need for this as the only way somebody could call doFileOpen is by calling it on the windowManager instance like so: windowManager.doFileOpen(...).
The same applies to:
this.init = this.init.bind(this);
this.getTemplate = this.getTemplate.bind(this);

vuejs configuration: using a global variable?

This seems dumb, but I have it setup like this:
in config/index.js:
module.exports = {
API_LOCATION: 'http://localhost:8080/api/'
}
then in src/app.js I have:
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource';
Vue.use(VueRouter);
Vue.use(VueResource);
const App = require("./app.vue");
const home = require("./components/home.vue");
const config = require('../config');
window.config = config;
Then in src/components/home.vue, I have a script block that uses it like so:
<script>
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(config.API_LOCAITON + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>
This works but it strikes me as a bad idea to use window to handle an application configuration. What's the more canonical approach here?
Import it.
<script>
import config from "../config"
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(config.API_LOCATION + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>
Or just the location.
<script>
import { API_LOCATION } from "../config"
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(API_LOCATION + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>
PROD: config/prod.env.js append your VAR='"value"'
'use strict'
module.exports = {
NODE_ENV: '"production"',
API_LOCATION: '"https://production URL"'
}
DEV: config/dev.env.js append your VAR='"value"'
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API_LOCATION: '"http://localhost"'
})
Your variable will available in process.env.API_LOCATION or
process.env.VAR_NAME
TL;DR; Global configuration in Vue is done using the .env and .env.production files that you create in the route folder of the app (next to package.json)
I can confirm that in Vue using the Vue CLI .env file gets loaded automatically when you run npm run serve
But keep in mind the following that I checked:
Variables in the .env files have to start with VUE_APP prefix to be automatically picked up and available as process.env.VUE_APP_MYVAR in the code
If you're defining JS objects such as 'VUE_APP_MY_OBJECT={a:100}then you'll have to parse it to JSON before using it in the codeJSON.parse(process.env.VUE_APP_MY_OBJECT)`
If you're not using Webpack you might have to fiddle a bit with it to pick these config files up. According to this answer it looks like webpack should do it automatically. Dunno
Simply set ip path(or localhost) in local storage when login successfull and get value from local storage where you need through out the project.
here how you set value in localstrage.
// Set value in IpAdress
localstorage.setItem('IpAddress','192.168.100.100:8080');
// Get value from IpAddress
localstorage.getItem('IpAddress');
in my case whole path looks like:
localstorage.getItem('IpAddress')+/api/controller/method|

Using proxyquire in a browserify factor bundle

Stuck with this one.
I am using laravel elxir with tsify to generate my js. I run the typescript through factor-bundle to split common js modules into a seperate files. I don't think though that will be a problem in this case because everything is in a spec.js
spec.ts
/// <reference path="../../../typings/index.d.ts" />
import "jasmine-jquery";
// #start widgets
import "./widgets/common/widget-factory/test";
factory-widget/index.ts
export class WidgetFactory {
.... this contains a require call to browser.service which i need to mock
}
factory-widget/test.ts
...
import {WidgetFactory} from "./index";
const proxyRequire = require("proxyquire");
it("should output the factory items", ()=> {
proxyRequire('./widgets/browser.service/index',{
"#global": true,
});
}
browser-service.ts
...
export class BrowserService implements IBrowserService{
//details
}
Getting an error Uncaught TypeError: require.resolve is not a function on line 262.
Here is the code ( yeah it's over 20,000 lines ) how else are you supposed to debug this stuff . ¯_(ツ)_/¯
I've looked at Stubbing with proxyquire. I am not holding my breath getting an answer on this one.
Edit: 06-09-2016
Proxquire is needed to overide the require call in the boot method of the WidgetFactory class
In factory-widget/index.ts:
boot(output = true):any {
let required = {};
if (this._sorted.length) {
this._sorted.forEach((key)=> {
if (output) {
console.log(`${this._path}${key}/index`);
// this is where is need to overide the call to require.
required[key] = require(`${this._path}${key}/index`);
}
});
this._sorted.forEach((key)=> {
let dependencies = {},
module = this._factory[key];
if (module.hasOwnProperty(this.dependencyKey)) {
module[this.dependencyKey].map((key)=> {
dependencies[_.camelCase(key)] = this.isService(module) ? new required[key] : key;
});
}
if (this.isTag(module)) {
if (output) {
document.addEventListener("DOMContentLoaded", ()=> {
riot.mount(key, dependencies);
});
}
//console.log(key,dependencies);
}
else {
}
})
}
}
I've added a proxyquireify example to the tsify GitHub repo. It's based on the simple example in the proxyquireify README.md.
The significant parts are the re-definition of require to call proxyquire in foo-spec.ts:
const proxyquire = require('proxyquireify')(require);
require = function (name) {
const stubs = {
'./bar': {
kinder: function () { return 'schokolade'; },
wunder: function () { return 'wirklich wunderbar'; }
}
};
return proxyquire(name, stubs);
} as NodeRequire;
and the configuration of the proxyquire plugin in build.js:
browserify()
.plugin(tsify)
.plugin(proxyquire.plugin)
.require(require.resolve('./src/foo-spec.ts'), { entry: true })
.bundle()
.pipe(process.stdout);
If you build the bundle.js and run it under Node.js, you should see that the message written to the console includes strings returned by the functions in the stubbed ./bar module.

Categories

Resources