Can't subclass with ES6 / babel-node - node.js

I have the following files: gist
The index.js attempts instantiate a base "Auth" class but in it's constructor the auth class acts as an object factory and passes back a subclass of Auth instead.
'use strict';
import Auth from './Auth';
let o = new Auth({type:'Oauth1'});
console.log(o);
o.getToken();
The Auth.js class definition is as follows:
'use strict';
import Oauth1 from './Oauth1';
export default class Auth {
constructor(config) {
if (this instanceof Auth) {
return new Oauth1(config);
} else {
this.config = config;
}
}
getToken() {
console.log('Error: the getToken module must be implemented in the subclass');
}
}
And the Oauth1.js class definition is:
'use strict';
import Auth from './Auth';
export default class Oauth1 extends Auth {
getToken() {
console.log('Auth: ', Auth);
}
}
When running with babel-node index.js I get the following error:
TypeError: Super expression must either be null or a function, not undefined
at _inherits (/repos/mine/test-app/Oauth1.js:1:14)
at /repos/mine/test-app/Oauth1.js:4:28
at Object.<anonymous> (/repos/mine/test-app/Oauth1.js:4:28)
at Module._compile (module.js:434:26)
at normalLoader (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/api/register/node.js:199:5)
at Object.require.extensions.(anonymous function) [as .js] (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/api/register/node.js:216:7)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Module.require (module.js:365:17)
at require (module.js:384:17)
If I remove the extends expression from the Oauth1 class it executes but then I am not getting the inheritance I want.

Your issue has nothing to do with babel. The real problem is that you have circular dependencies in your code.
To resolve this issue you should remove Oauth1 dependency from its parent Auth class:
'use strict';
export default class Auth {
constructor(config) {
this.config = config;
}
getToken() {
console.log('Error: the getToken module must be implemented in the subclass');
}
}
'use strict';
import Auth from './Auth';
export default class Oauth1 extends Auth {
getToken() {
console.log('Auth: ', Auth);
}
}
If you don't want to remove this instanceof Auth check from your base class, you could require your Oauth1 subclass in run-time instead of importing it during module initialization:
constructor(config) {
if (this instanceof Auth) {
let Oauth1 = require('./Oauth1');
return new Oauth1(config);
}
this.config = config;
}

Related

Jest mock a const

I want to test a js file where it has a reference of a const coming from another import (parser.js)
const { cp } = CML
How can I mock this and only this and not the rest of functions?. It is throwing this error:
ReferenceError: CML is not defined
at Object.<anonymous> (src/state/lib/parser.js:2:16)
at Object.<anonymous> (src/state/reducers/stateReducer.js:2:1)
at Object.<anonymous> (src/state/reducers/index.js:4:1)
at Object.<anonymous> (src/state/store/index.js:4:1)
at Object.<anonymous> (src/state/store/store.spec.js:4:1)
CML is a var defined in other js resource file.
This is the parser.js file:
/* global CML */
const { cp } = CML;
// Massaging approvals array data
// Adding status and trimming unused values
export default {
approvals: (approvals = [], globalActions = []) => (
approvals.map(approval => {
let status = 'default';
let rejected = false;
let reviewed = 0;
...
And in stateReducer class this is the import of the parser:
import parser from '../lib/parser';
jest.mock('./state/lib/parser', function() { // Put the exact path you've imported in your file
return {
CML: 123,
};
});

Variable becomes undefined when access in a class method

I defined a class variable in a constructor which holds a reference to an imported class instance.
However, when I try to access the variable in my class methods I keep getting undefined for that variable.
Exported class
import models from '../../database/models'
const {User} = models;
class UserRepository {
constructor(){
this.model = User;
}
async create({firstname, lastname, email, isadmin, password}){
return await this.model.create({firstname, lastname, email, isadmin, password})
}
}
export default new UserRepository();
Class using the exported class
import repository from './repository';
import { sendSuccessMessage } from '../../utils'
class UserController {
constructor() {
this.model = repository;
}
async createUser({ body }, res, next) {
try {
const result = await this.model.create(body);
sendSuccessMessage(res, 200, result);
} catch( e ){
next(e)
}
}
}
export default new UserController();
However, using the variable repository directory in createUser as specified below:
const result = await repository.create(body);
works as expected.
Current call chain
// routes.js file
import controller from './controller'
export default [
{
path: '/signup',
method: 'post',
handlers: [controller.createUser] // createUser from class UserController
}
]
// utitlity.js file
export const routesLoader = (routes, router) => {
for (const route of routes) {
const { path, handlers, method } = route;
(router)[method](path, handlers);
}
};
// app.js file
import express from 'express';
import routes from './routes.js';
import { routesLoader } from './utils';
const router = express.Router();
routesLoader(routes, router);
// server.js file
import http from 'http';
import app from './app';
const { PORT = 3000 } = process.env;
const server = http.createServer(app);
server.listen(PORT, () => {
console.log(`server listening on localhost: ${PORT}`);
});
Error
TypeError: Cannot read property 'create' of undefined
at _callee$ (C:\Users\MYPC\Downloads\moch-shop-api-master\components\user/controller.js:16:39)
at tryCatch (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:45:40)
at Generator.invoke [as _invoke] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:271:22)
at Generator.prototype.<computed> [as next] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:97:21)
at asyncGeneratorStep (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:3:24)
at _next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:25:9)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:32:7
at new Promise (<anonymous>)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:21:12
at createUser (C:\Users\MYPC\Downloads\moch-shop-api-master\components\user\controller.js:70:28)
at Layer.handle [as handle_request] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:275:10)
at cors (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:188:7)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:224:17
at originCallback (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:214:15)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:219:13
at optionsCallback (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:199:9)
Technologies used
#babel/node: "^7.8.4"
terminal: git bash
Os: windows
How is UserController.createUser called? Sounds like its this is not set up correctly. Let's say we initialized the class like this:
const userController = new UserController();
Then, one source for missing this can be if you only pass an exact reference to the method
setImmediate(userController.createUser);
instead of a way that retains this:
setImmediate(() => {
userController.createUser()
});
In other words, you want to invocate the method using dot notation and involve the class instance.
Your method create is an instance method. It needs to be static for this to work
First, you need to import UserRepository properly before its utilization.
import UserRepository from 'path to UserRepository class';
then
Use self in your function as follows:
async createUser({ body }, res, next) {
const self = this;
try {
const result = await self.model.create(body);
sendSuccessMessage(res, 200, result);
} catch( e ){
next(e)
}
}
I think better to use self as compared to this in your createUser() function.

Nest.js - Imported service not recognized by another service

I am attempting to reuse a service (Inventory) in another service (Order) class like so
order.module.ts
import { InventoryModule } from '../inventory/inventory.module';
#Module({
imports: [TypeOrmModule.forFeature([OrderEntity]), InventoryModule],
controllers: [OrderController],
providers: [OrderService],
})
export class OrderModule {}
and then use InventoryService methods inside OrderService like so
#Injectable()
export class OrderService {
constructor(
#InjectRepository(OrderEntity) private orderRepository: Repository<OrderEntity>,
private inventoryService: InventoryService) {}
/**
* Add order item
* #param data Object
*/
async add(data: OrderDTO) {
const { inventory_item_id } = data
const inventoryItem = await this.inventoryService.getOne(inventory_item_id)
if (inventoryItem.quantity_available <= 0) {
throw new Error('Cannot add item to order, insufficient quantities!')
}
const orderItem = await this.orderRepository.create(data)
if (orderItem) {
const updatedInventoryItemQuantity = inventoryItem.quantity_available - 1
const updatedInventoryItem = await this.inventoryService.edit(inventory_item_id, {
quantity_available: updatedInventoryItemQuantity,
})
}
await this.orderRepository.save(orderItem)
return orderItem
}
but I get the following stack trace:
Require stack:
- /Users/jred/workspace/projects/my-proj/dist/order/order.service.js
- /Users/jred/workspace/projects/my-proj/dist/order/order.controller.js
- /Users/jred/workspace/projects/my-proj/dist/order/order.module.js
- /Users/jred/workspace/projects/my-proj/dist/app.module.js
- /Users/jred/workspace/projects/my-proj/dist/main.js
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:623:15)
at Function.Module._load (internal/modules/cjs/loader.js:527:27)
at Module.require (internal/modules/cjs/loader.js:681:19)
at require (internal/modules/cjs/helpers.js:16:16)
at Object.<anonymous> (/Users/jred/workspace/projects/my-proj/dist/order/order.service.js:19:29)
at Module._compile (internal/modules/cjs/loader.js:774:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:785:10)
at Module.load (internal/modules/cjs/loader.js:641:32)
at Function.Module._load (internal/modules/cjs/loader.js:556:12)
at Module.require (internal/modules/cjs/loader.js:681:19) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/Users/jred/workspace/projects/my-proj/dist/order/order.service.js',
'/Users/jred/workspace/projects/my-proj/dist/order/order.controller.js',
'/Users/jred/workspace/projects/my-proj/dist/order/order.module.js',
'/Users/jred/workspace/projects/my-proj/dist/app.module.js',
'/Users/jred/workspace/projects/my-proj/dist/main.js'
]
}
anyone experience this? what is the correct way to call an imported service in another module?
Check your app.module.ts.
Make sure you added InventoryModule in the imports array.
And also, make sure you are exporting InventoryService from InventoryModule.
// app.module.ts
#Module({
imports: [InventoryModule, ...othermodules],
exports: [],
providers: []
})
export class ApplicationModule {}
// inventory.module.ts
#Module({
imports: [...yourModules],
exports: [InventoryService],
providers: [InventoryService]
})
export class InventoryModule {}
the problem was within my Order.service.ts file with the auto import syntax
this import that included src
import { InventoryService } from 'src/inventory/inventory.service';
had to be changed to this
import { InventoryService } from '../inventory/inventory.service';

Webpack & Babel : Server Side rendering of React Component "Unexpected token '<'"

I've been investigating this issue for 3 days now but cannot get it working.
The full error is :
C:\Users\XXXXXX\WebstormProjects\XXXX\server\routes\auth.jsx:58
return res.send(ReactDOMServer.renderToString(<LoginPage />));
^
SyntaxError: Unexpected token <
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (C:\Users\XXXXXX\WebstormProjects\XXXX\index.js:10:20)
Here is my webpack.config.js :
const path = require('path');
module.exports = {
entry: path.join(__dirname, '/client/src/app.jsx'),
output: {
path: path.join(__dirname, '/client/dist/js'),
filename: 'app.js',
publicPath: "/"
},
module: {
loaders: [{
test: /\.jsx?$/,
include: [
path.join(__dirname, '/client/src'),
path.join(__dirname, '/server/routes')
],
loader: 'babel-loader',
query: {
babelrc: false,
presets: ['es2015', 'stage-2', 'react']
}
}],
},
devServer: {
historyApiFallback: true
},
watch: true
};
Now the /server/routes/auth.jsx file :
const express = require('express');
const validator = require('validator');
const router = new express.Router();
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const LoginPage = require('../../client/src/containers/LoginPage.jsx');
function validateLoginForm(payload) {
const errors = {};
let isFormValid = true;
let message = '';
if (!payload || typeof payload.email !== 'string' || payload.email.trim().length === 0) {
isFormValid = false;
errors.email = 'Please provide your email address.';
}
if (!payload || typeof payload.password !== 'string' || payload.password.trim().length === 0) {
isFormValid = false;
errors.password = 'Please provide your password.';
}
if (!payload || typeof payload.password !== 'string' || payload.password.trim().length <= 8)
{
isFormValid = false;
errors.password = 'Please provide a password that\'s more than 8 char';
}
if (!isFormValid) {
message = 'Check the form for errors.';
}
return {
success: isFormValid,
message,
errors
};
}
router.post('/login', (req, res) => {
console.log("lol");
const validationResult = validateLoginForm(req.body);
if (!validationResult.success) {
return res.status(400).json({
success: false,
message: validationResult.message,
errors: validationResult.errors
});
}
console.log("Went through validationResult without problems");
return res.status(200).end();
});
router.get('/login', (req, res) => {
console.log(req.url);
return res.send(ReactDOMServer.renderToString(<LoginPage />)); // THE PROBLEM
});
router.get('/', (req, res) => {
console.log(req.url);
console.log("lmao")
});
module.exports = router;
Finally the /client/src/containers/LoginPage.jsx :
import React from 'react';
import LoginForm from '../components/LoginForm.jsx';
class LoginPage extends React.Component{
/**
* Class constructor.
*/
constructor(props) {
super(props);
// set the initial component state
this.state = {
errors: {},
user: {
email: '',
password: ''
}
};
this.processForm = this.processForm.bind(this);
this.changeUser = this.changeUser.bind(this);
}
/**
* Process the form.
*
* #param {object} event - the JavaScript event object
*/
processForm(event) {
// prevent default action. in this case, action is the form submission event
event.preventDefault();
const email = encodeURIComponent(this.state.user.email);
const password = encodeURIComponent(this.state.user.password);
const formData = `email=${email}&password=${password}`;
// create an AJAX request
const xhr = new XMLHttpRequest();
xhr.open('post', '/login');
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.responseType = 'json';
xhr.addEventListener('load', () => {
if (xhr.status === 200) {
// success
// change the component-container state
this.setState({
errors: {}
});
console.log('The form is valid');
} else {
// failure
// change the component state
const errors = xhr.response.errors ? xhr.response.errors : {};
errors.summary = xhr.response.message;
this.setState({
errors
});
}
});
xhr.send(formData);
}
/**
* Change the user object.
*
* #param {object} event - the JavaScript event object
*/
changeUser(event) {
const field = event.target.name;
const user = this.state.user;
user[field] = event.target.value;
this.setState({
user
});
}
/**
* Render the component.
*/
render() {
return (
<LoginForm
onSubmit={this.processForm}
onChange={this.changeUser}
errors={this.state.errors}
user={this.state.user}
/>
);
}
}
export default LoginPage;
I first added the path.join(__dirname, '/server/routes') in order to tell Webpack and babel to also search for this folder to transpile jsx, but it fails no matter what.
I then replaced return res.send(ReactDOMServer.renderToString(<LoginPage />)); in auth.jsx by :
var html = ReactDOMServer.renderToString(React.createElement(LoginPage));
return res.send(ReactDOMServer.renderToString('base', html));
but, by doing this, node gives me another error which is :
C:\Users\XXXXXX\WebstormProjects\XXXX\client\src\containers\LoginPage.jsx:1
(function (exports, require, module, __filename, __dirname) { import React from 'react';
^^^^^^
SyntaxError: Unexpected token import
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (C:\Users\XXXXXX\WebstormProjects\XXXX\server\routes\auth.jsx:8:19)
which is, again, a transpiling problem.
Note that without that route in auth.jsx, the web App works all fine, except that I can't access /login via URL.
What am I doing wrong ?
I'm using the latest versions of Express, React, React Router and Node. My OS is Windows 7.
Thanks in advance
I think I know what is the problem here.
You indeed compile your jsx file and your webpack.config.js seems perfect (it includes react presets, so it should work).
But you are only compiling for the client it seems, and you are trying to consume the uncompiled file on the server.
But node cannot read jsx on its own.
The option I would suggest here, it to allow babel to also compile server side files.
To do that, you can use babel-cli.
Simply add this in your package.json
"scripts": {
"start": "babel-node index.js --presets es2015,react"
}
And start you server like this
npm start
Then it should first compile the files with babel and start your server.
Here is an example you can inspire from https://github.com/babel/example-node-server
This should fix your problem

node.js simple project: ReferenceError: <ClassName> is not defined

I try to learn Node.js (ES6) but fail on require
This is my structure:
baseModel.js
"use strict";
class BaseModel {
constructor(options = {}, data = []) { // class constructor
this.name = 'Base'
this.url = 'http://azat.co/api'
this.data = data
this.options = options
}
getName() { // class method
console.log(`Class name: ${this.name}`)
}
}
AccountModel.js
"use strict";
require('./baseModel.js');
class AccountModel extends BaseModel {
constructor(options, data) {
super({private: true}, ['32113123123', '524214691']) //call the parent method with super
this.name += 'Account Model'
this.url +='/accounts/'
}
get accountsData() { //calculated attribute getter
// ... make XHR
return this.data
}
}
main.js
"use strict";
require('./AccountModel.js');
let accounts = new AccountModel(5)
accounts.getName()
console.log('Data is %s', accounts.accountsData);
Now I run: node --harmony-default-parameters main.js
and get error:
ReferenceError: BaseModel is not defined
at Object. (/Users/tamirscherzer/POC/projects/NodeJS/tutorials/classes/AccountModel.js:5:28)
at Module._compile (module.js:397:26)
at Object.Module._extensions..js (module.js:404:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object. (/Users/tamirscherzer/POC/projects/NodeJS/tutorials/classes/main.js:5:1)
at Module._compile (module.js:397:26)
at Object.Module._extensions..js (module.js:404:10)
Really strange, if I change require('./baseModel.js'); to other name, I get error that file not found so the path is written properly.
Also defined permissions 777 - same thing, BaseModel is not defined
Any Ideas?
When you define a variable in Node, it isn't added to the global scope like it would be in a browser - it's local to that one file/module. Therefore, you can't simply import a file and expect the things you defined inside it to be available - you explicitly have to export and import them.
BaseModel.js:
class BaseModel {
constructor(options = {}, data = []) { // class constructor
this.name = 'Base'
this.url = 'http://azat.co/api'
this.data = data
this.options = options
}
getName() { // class method
console.log(`Class name: ${this.name}`)
}
}
module.exports = BaseModel;
AccountModel.js:
"use strict";
let BaseModel = require('./baseModel.js');
class AccountModel extends BaseModel {
constructor(options, data) {
super({private: true}, ['32113123123', '524214691']) //call the parent method with super
this.name += 'Account Model'
this.url +='/accounts/'
}
get accountsData() { //calculated attribute getter
// ... make XHR
return this.data
}
}
module.exports = AccountModel;
main.js:
"use strict";
let AccountModel = require('./AccountModel.js');
let accounts = new AccountModel(5)
accounts.getName()
console.log('Data is %s', accounts.accountsData);

Resources