inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.34k stars 719 forks source link

getFunctionName throws, regexp returns null #385

Closed stjepangolemac closed 8 years ago

stjepangolemac commented 8 years ago

Function getFunctionName throws when its called by baseClassDependencyCount which passes following function.

function (options) {
  events.EventEmitter.call(this);
  this.configure(options);
}

Expected Behavior

I don't know what should happen when it receives function with no name because I don't know what is that function there for.

Current Behavior

getFunctionName receives function with no name and throws. Here is the output. return v.name ? v.name : v.toString().match(/^function\s*([^\s(]+)/)[1]; TypeError: Cannot read property '1' of null

Possible Solution

I don't know if my implementation is causing this but if it's not, function should check does the name ([1] in the array) exist after the regexp is done and continue accordingly.

Context

I'm trying to resolve my Logger class that depends on Settings class.

Logger

import { injectable, inject } from "inversify";
import "reflect-metadata";
import * as INTERFACES from "../interfaces";
import * as winston from "winston";

/**
 * Asynchronous server logger
 */
@injectable()
export default class Logger extends winston.Logger implements INTERFACES.ILogger {
  public settings: INTERFACES.ISettings;

  constructor(
    @inject("Settings") settings: INTERFACES.ISettings
  ) {
      super();
      this.settings = settings;
      this.setLogger();
  }

  /**
   * Sets logging outputs based on settings
   */
  private setLogger() {
    if (this.settings.logConsole) {
      this.add(winston.transports.Console,
        {
          level: this.settings.logConsoleLvl,
        }
      );
    }

    if (this.settings.logFile) {
      this.add(winston.transports.File,
        {
          filename: this.settings.logFilename,
          level: this.settings.logFileLvl,
        }
      );
    }
  }
}

Settings

import { injectable } from "inversify";
import "reflect-metadata";
import * as INTERFACES from "../interfaces";

/**
 * Settings for server
 */
@injectable()
export default class Settings implements INTERFACES.ISettings {
  public port: number;
  public logConsole: boolean;
  public logConsoleLvl: string;
  public logFile: boolean;
  public logFileLvl: string;
  public logFilename: string;
  public dbUrl: string;
  public dbUser: string;
  public dbPass: string;

  constructor() {
    this.setup(process.env.CONFIG);
  }

  /**
   * Reads the settings from configuration variable
   */
  private setup = (config: string) => {
    let configuration: any;
    try {
      configuration = require("../../configuration/" + config + ".js");
    } catch (error) {
      console.log("Settings cannot find", config, "configuration file");
      throw error;
    }

    this.port = configuration.port;
    this.logConsole = configuration.logconsole;
    this.logConsoleLvl = configuration.logconsolelvl;
    this.logFile = configuration.logfile;
    this.logFileLvl = configuration.logfilelvl;
    this.logFilename = configuration.logfilename;
    this.dbUrl = configuration.dburl;
    this.dbUser = configuration.dbuser;
    this.dbPass = configuration.dbpass;
  }
}

Kernel

import Settings from "./classes/settings";
import Logger from "./classes/logger";

import { Kernel } from "inversify";

let kernel = new Kernel();
kernel.bind<INTERFACES.ISettings>("Settings").to(Settings);
kernel.bind<INTERFACES.ILogger>("Logger").to(Logger);

kernel.get<INTERFACES.ILogger>("Logger"); //  <= breaks here
export default kernel;

Your Environment

return v.name ? v.name : v.toString().match(/^function\s*([^\s(]+)/)[1]; ^

TypeError: Cannot read property '1' of null at Object.getFunctionName (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/utils/utils.js:5:70) at Planner._getTargets (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/planning/planner.js:132:39) at Planner._baseClassDepencencyCount (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/planning/planner.js:195:32) at Planner._getDependencies (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/planning/planner.js:184:45) at Planner.createPlan (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/planning/planner.js:23:37) at Kernel._createContext (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/kernel/kernel.js:220:23) at /home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/kernel/kernel.js:214:26 at Array.map (native) at Kernel._plan (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/kernel/kernel.js:213:33) at Kernel._planAndResolve (/home/stjepan/Development/houseremote2.1/node_modules/inversify/lib/kernel/kernel.js:173:29)

remojansen commented 8 years ago

Hi, thanks a lot for reporting this issue. I'm about to release inversify@2.0.1 which contains a fix for this issue.

However, getFunctionName is only called when there is an issue (e.g. a missing annotation). At the moment you are not able to see the error message because it throws but after upgrading to 2.0.1 you will get some error. The error description should help you but feel free to ask any questions if you need additional help.

remojansen commented 8 years ago

I just released inversify@2.0.1 please let me know if it solves your issue 😄

stjepangolemac commented 8 years ago

It solves that error but now I have another one (caused by my implementation). My logger class extends winston logger which doesn't have injectable decorator. How am I supposed to fix this?

Here is the error.

Error: Missing required @injectable annotation in: Anonymous function: function (options) {
  events.EventEmitter.call(this);
  this.configure(options);
}

And it seems to point to this function in the winston logger https://github.com/winstonjs/winston/blob/master/lib/winston/logger.js line 25

var Logger = exports.Logger = function (options) {
  events.EventEmitter.call(this);
  this.configure(options);
};
remojansen commented 8 years ago

Hi @stjepangolemac we have a small guide about this issue in our wiki https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module hope it solves your issue

stjepangolemac commented 8 years ago

Yes, that helped me a lot. You are very responsive and helpful, keep up the good work!