2

I have been trying to use NOTY with an Aurelia/Typescript application. Installed the package using NPM and use requireJS to pull it into the application.

No matter what I try, I have not been able to get it to work. For importing it in the file I need the reference for, I tried the following two methods


Attempt# 1

import * as Noty from 'noty';

This seems to create the right references and I can see that in code. When I try to use it, I do not get any build errors and everything seems ok.

enter image description here

But when I run this code, I get an error that states - "Noty is not a constructor"


Attempt 2

import Noty from 'noty'

enter image description here

This approach complains about no default exported members.

Another variation that I tried is import { Noty } from 'noty'; This gave me a similar response

enter image description here


Not sure what I am missing but any suggestions to implement this is highly appreciated. TIA


UPDATE#1

Added in aurelia.json file enter image description here

Noty package not loaded enter image description here

PS: Added link to NOTY if having a look at the index.d.ts file is needed.

Ron
  • 886
  • 13
  • 39
  • Have you added the type definitions for `Noty` (`.d.ts` file)? – adiga Feb 07 '19 at 14:06
  • @adiga: Do you mean this? `{ "name": "noty", "path": "../node_modules/noty", "main": "index" }` – Ron Feb 07 '19 at 14:27
  • Did you include a reference to the NOTY script in your aurelia template or base HTML file? Are you sure requireJS is successfully loading the NOTY script file? – JoyalToTheWorld Feb 11 '19 at 15:50
  • @JoyalToTheWorld - New to this - can you give some more info? I believe I did not. I was debugging further and see that the module is undefined at runtime. – Ron Feb 11 '19 at 16:41
  • @Ron maybe you can update your post with your requireJS config. This should be responsible for loading your scripts, you would need to map the 'NOTY' package name to the location of the script on disk, similar to what you posted above in reply to adiga. Also, you can check the dev tools see if there are any failed requests, compare the path in the request with what you actually see on disk. Hope that helps. – JoyalToTheWorld Feb 11 '19 at 16:55
  • @JoyalToTheWorld: Ok, so if I understand you correctly, I did add the reference you mention. I will update the question in a min. I do not see any errors during loading, but if I put a breakpoint in Chrome DEV on the line I mentioned, I see that the object is undefined. Same at the import location - The variable NOTY is undefined. – Ron Feb 11 '19 at 16:57
  • @Ron so if the script is being loaded correctly, but the module doesn't look right in the debugger, it sounds like your package mapping for require is pointing to the wrong file. I just looked at the 'noty' NPM package, looks like your mapping for requireJS should be something like `{ "name": "noty", "path: "../node_modules/noty/lib", main: "noty.min" }`. If requireJS is loading index.d.ts that would explain why your module is undefined when you inspect at the breakpoint. – JoyalToTheWorld Feb 11 '19 at 17:03

3 Answers3

1

It sounds like your package mapping for requireJS is pointing to the wrong file (possibly the index.d.ts file).

I just looked at the 'noty' NPM package, looks like your mapping for requireJS should be something like:

{ "name": "noty", "path": "../node_modules/noty/lib", "main": "noty.min" }

TypeScript cares about *.d.ts files, but requireJS isn't TypeScript, it handles loading files into the browser, so it only cares about Javascript files.

BTW you are forgiven for not immediately grokking the madness that is the web platform.

JoyalToTheWorld
  • 130
  • 1
  • 6
  • Thanks for putting this as an answer - I tried the above and still no luck – Ron Feb 11 '19 at 17:15
  • Ok, so looks like we are getting somewhere - I looked at their documentation and changed your suggestion a little by naming the module with a Caps 'N'. Now I see that its trying to find `noty.js` file in my `/src` folder and returning a `404` for that file – Ron Feb 11 '19 at 17:24
  • So your Approach #1 is correct. It looks like TS is happy, it recognizes the package name and is providing typing. This means the problem is somewhere in loading the package. I want to confirm that the code above is in the 'dependecies' section of you aurelia.json file, also your aurelia.json file should be under `/aurelia_project`, which is a sibling to `/node_modules`. – JoyalToTheWorld Feb 11 '19 at 18:19
  • All of the previous hold true. – Ron Feb 11 '19 at 19:35
  • In that case, it seems you are using the Aurelia CLI, my guess is this is forcing some sort of bundling process that is not generating the proper output. Without more information about your configuration it will be very difficult to help. I came across [this](https://stackoverflow.com/questions/39432962/how-to-add-tether-in-aurelia-cli-to-use-with-bootstrap-4), it may help you find a hack/workaround if nothing else. – JoyalToTheWorld Feb 11 '19 at 19:42
1

The noty lib has a named AMD define define('Noty',...) instead of normal anonymous define. It should work, but it seems my recent PR created a regression for cli-bundler on named AMD module, or maybe a new bug on named AMD module.

I will fix that regression. Update I did https://github.com/aurelia/cli/pull/1084

To work around now,

  1. create another file in your project patch/noty.js with content:
define('noty',['Noty'],function(m){return m;});

This patch creates alias from 'noty' to 'Noty'.

  1. add to aurelia.json prepend, has to be after requirejs.
  2. there is another problem with default main lib/noty.js:
ERROR [Bundle] Error: An error occurred while trying to read the map file at /Users/huocp/playground/nt/node_modules/noty/lib/es6-promise.map

It tries to load es6-promise.map but no such file.

Update: the error would not stop bundling.

{
    "name": "vendor-bundle.js",
    "prepend": [
      "node_modules/requirejs/require.js",
// add this line after requirejs
      "patch/noty.js"
    ],
    "dependencies": [
      "aurelia-bootstrapper",
      "aurelia-loader-default",
      "aurelia-pal-browser",
      {
        "name": "aurelia-testing",
        "env": "dev"
      },
      "text",
// optionally override noty main path, only if you want to get rid of the annoying es6-promise.map error
      {
        "name": "noty",
        "path": "../node_modules/noty",
        "main": "lib/noty.min"
      }

    ]
}

Then this import works, I tested.

import * as Noty from 'noty';

BTW, to forget about * as, use Microsoft recommended esModuleInterop compiler option. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html

huocp
  • 3,898
  • 1
  • 17
  • 29
0

The issue you got probably comes from the way NOTY exports their main functionality, which i guess was done in commonjs way:

module.exports = NOTY;

To import it correctly in your code, you should use the import module syntax:

import * as NOTY from 'noty';

It's the same for many other libraries that still keep the export style of the traditional way.

Update:

Based on their type definition export: https://github.com/needim/noty/blob/master/index.d.ts#L2

declare module 'noty' {
  exports = Noty;
}

This means you should always do like your attempt 1. But this is how it is actually shipped https://github.com/needim/noty/blob/master/lib/noty.js#L9-L18

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    ...
  else if(typeof exports === 'object')
    exports["Noty"] = factory();
...

Note the last part exports["Noty"] = factory(). I suspect this is what picked up, not the first one. Which means it equals to named export Noty

So it's probably the mismatch between that export thing that caused you issue. I would suggest you do this:

import * as $NOTY from 'noty';

// wrong: const NOTY = $NOTY as any as typeof $NOTY;
// wrong: const NOTY = $NOTY.Noty as any as typeof $NOTY;
// or 
// wrong: const NOTY = new (options: $NOTY.OptionsOrSomeThing): $NOTY;
const NOTY = ($NOTY as any).Noty as new (options: $NOTY.OptionsOrSomeThing): $NOTY;

// do your code here with NOTY
new NOTY();
bigopon
  • 1,924
  • 2
  • 14
  • 23
  • I tried the above and you can see that in Attempt#1 - This does seem to import things correctly, but when I try and use the constructor it does not like it and throws an error. Any suggestions ? – Ron Feb 09 '19 at 16:42
  • I've updated the answer, can you have a look and try? – bigopon Feb 12 '19 at 09:52
  • Thanks, this looked promising, but no dice - still the same "`Noty is not a constructor`" error pops up. – Ron Feb 12 '19 at 15:31
  • It got long a bit and then i didn't put all of my answer in last time. Can you check again – bigopon Feb 12 '19 at 20:18
  • It does not seem to recognize `const NOTY = $NOTY.Noty as any as typeof $NOTY;` `Property Noty does not exist on type 'typeof Noty'` – Ron Feb 13 '19 at 02:41
  • Yeah please check again. I fixed that. – bigopon Feb 13 '19 at 04:14
  • Still running into issues - at this point, I plan on dropping the plan to use this package with Aurelia/Typescript. I was able to make toastrjs work fine without a lot of issues and will continue to use that – Ron Feb 14 '19 at 20:55
  • No worries. Sorry to hear that. I've just never used it. Maybe I should try before recommending. – bigopon Feb 14 '19 at 22:10
  • 1
    Added the bounty to you for the help ! – Ron Feb 15 '19 at 18:19
  • Nice, thanks! I'd still recommend you tried out what I put there in the answer with my latest update though. – bigopon Feb 15 '19 at 22:25
  • 1
    I did try out the latest update and still running into errors - I will provide more details when I get on my computer. I am willing to spend more time and effort on this if you feel we can get somewhere with this :) – Ron Feb 17 '19 at 17:02