29

I'm trying to follow the official AoT guide for Angular 2, and I'm using Moment.js in my application. Moment.js is on my packages.json file, and I'm using version 2.15.0. I've been importing it like this so far:

import * as moment from 'moment';

But when I get to the part where I have to run rollup, I end up with the following error:

Cannot call a namespace ('moment')

Which appears to be related to the way I import moment according to this. So, how am I supposed to do this? I can't seem to import moment any other way. If I use

import moment from 'moment'

I get the compile error

External module ''moment'' has no default export

4
  • were you able to figure this out? Commented Sep 23, 2016 at 21:59
  • +1 Having the same issue with import * as HighchartsMore from "highcharts/highcharts-more"; HighChartsMore(Highcharts) Commented Oct 27, 2016 at 5:08
  • I am also stuck on the same error. Any progress on your side 1 month later? Commented Oct 31, 2016 at 15:13
  • I had problems too with loading moment.. see this answer, maybe it can help: stackoverflow.com/a/35350484/1354222 Commented Nov 22, 2016 at 10:09

9 Answers 9

22

I have finally managed to get rid of both errors. Indeed to avoid:

Cannot call a namespace ('moment')

You need to use:

import moment from 'moment'

Then to avoid

"moment" has no default export

You have to add into your tsconfig.json (compilerOptions):

"allowSyntheticDefaultImports": true

EDIT 17/11/2016

I also had to add the following to my rollup-config.js file:

plugins: [
  nodeResolve({jsnext: true, module: true}),
  commonjs({
    include: [
        'node_modules/rxjs/**',
        'node_modules/moment/**'
      ]
    }
  }),
  uglify()
]
Sign up to request clarification or add additional context in comments.

5 Comments

This works for a AoT rollup config but does NOT work using systemjs in debug/dev build mode.
@MarkPerry yes I am trying still trying with SystemJS and Webpack as well. But this thread was about rollup. I will let you know if I find something for SystemJS.
Marked this as accepted answer because it fixes AoT, but I really needed a solution that would work for both AoT and JIT without modifications...
It does not work: import moment from 'moment' will give the error moment_1.default is not a function at runtime with typescript 2.1.6.
for AoT I ended up having two different rollup config files; one for JIT and the other for AoT. I also do not use SystemJS and it works fine for me.
13

I found a nice solution for the problem at hand:

Npm-install additional package moment-es6 which provides a default export. Then import from 'moment-es6' instead of from 'moment':

import moment from 'moment-es6';

  • For use with systemjs, add the following to the systemjs.config.js map section:
    'moment-es6': 'npm:moment-es6/index.js'

  • add 'node_modules/moment-es6/**' to the include's of your rollup configs commonjs section (rollup-plugin-commonjs)

2 Comments

Interesting, this worked for system js and typescript (without additional compiler flag), but rollup is giving me an error: 🚨 'default' is not exported by node_modules\moment-es6\index.js
I think I just need to specify the named export - right?
7

Here is what I did to make work moment with typescript (at 2.1.6) and rollup (0.41.4).

  1. To import moment, keep the standard way:

    import * as moment from 'moment';

import moment from 'moment'; is non-standard for a package without a default export, it will result an error at runtime: moment_1.default is not a function

  1. In typescript use moment with by casting moment as any, and call the default function:

    var momentFunc = (moment as any).default ? (moment as any).default : moment;
    var newFormat = momentFunc(value).format( format );
    

moment(value).format(format) will result an error at rollup tree shaking: Cannot call a namespace ('moment')

1 Comment

Thank you! This have worked for me. import * as momentLib from "moment"; const moment = (momentLib as any).default ? (momentLib as any).default : momentLib;
5

We had a similar issue with ng-packagr which uses rollup to generate a module that can be published in an npm repo. Our project was built-up using @angular-cli (using webpack).

We have 2 dependencies that are imported using the asteriks method:

 import * as dataUrl from 'dataurl';

Worked fine, is used like:

 dataUrl.parse(url)

Another import gave the error (Cannot call a namespace) because the exported object is to be used as a function:

 import * as svgPanZoom from 'svg-pan-zoom';
 svgPanZoom(element); <== error: Cannot call a namespace

We could workaround this by assigning the exported initializer function to another const and use that in the code:

 import * as svgPanZoomImport from 'svg-pan-zoom';
 const svgPanZoom = svgPanZoomImport;

 svgPanZoom(element);

We also made the tsconfig.json config change as described above.

Versions: ng-packagr: 1.4.1 rollup: 0.50.0 typescript: 2.3.5 @angular/cli: 1.4.8 webpack: 3.7.1

Hope this help,

Rob

1 Comment

The alias made the magic, even without the tsconfig.json changes. Thanks!
3

I was having the same problems described above.

import * as moment from 'moment'; - worked when developing and loading via systemjs, but not during rollup.

import moment from 'moment'; - worked in a rollup build but not during development.

To avoid having to change code depending on build type, I just added moment as a global and created a helper function that I import everywhere I need to use it instead of importing moment.

This means the same code works for both types of scenarios. It's not particularly pretty though, if there's a better way please let me/us know!

Here's the helper function, added to it's own file momentLoader.ts

import { default as mom } from 'moment';
export default function moment(args?: any): mom.Moment {
    let m = window["moment"];
    if (!m) { 
        console.error("moment does not exist globally.");
        return undefined;
    }
    return m(args);
}

To use moment in other classes I just import the function and call it as if I'd imported moment directly:

import moment from '../../momentLoader';

let d = moment().utc("1995-12-25");

let m = moment("1995-12-25");

To get systemjs to load it as a global, I just followed these steps. http://momentjs.com/docs/#/use-it/system-js/

In my case the moment config for systemjs looks like this:

let meta = {
    'lib:moment/moment.js': { format: 'global' }
};

System.config({
    paths: paths,
    map: map,
    packages: packages,
    meta: meta
});

System.import('lib:moment/moment.js');

For the rollup build you'll have to make sure moment.js is added to the page somewhere via a script tag, as it won't get included in the rollup build file unfortunately.

4 Comments

I have the same problem as you and I really don't understant why everybody is not complaining about this... Creating an extra loading function is an absurd requirement, especially when we are already using tons of different loaders!
Have just wasted the whole evening trying to rollup my website, and only now I've solved the problem thanks to your precious post. I've done the same for lodash: // lodash-loader.ts import { default as lodash } from "lodash"; export default window[""] as _lodash.LoDashStatic;
Neat idea, but I find it completely unacceptable having to use this. I just want to use an external library for Christ's sake. *sigh, moments like these (no pun intended) make me regret using Angular 2...
Totally agree that this is not ideal and an absolute PITA. Just needed something to let me keep on working until there's better options. It's still all 'fairly' new so I'm sure this will get sorted eventually....well hopefully anyway :).
1

Going by this thread, import moment from 'moment'; should work.

1 Comment

this worked well for me, the only thing i had to add is in tsconfig.json under compilerOptions "allowSyntheticDefaultImports": true
0

As of version 2.13.0,

import * as moment from 'moment';

1 Comment

That gives Module [...] has no exported member 'default'.
0

I tried all the solutions above, but none worked for me. What worked was

import moment from 'moment-with-locales-es6';

Comments

0

Had same problem with momentJs (2.24) usage in my Angular 5 (5.2.9) project (upgraded from Ng2) with Gulp and Rollup (0.58.0) for prod build.

As guys mentioned earlier here import * as moment from 'moment'; works only for deving (via SystemJS) with referring momentJs in packages list:

{
  name: 'moment',
  path: 'node_modules/moment/min/moment.min.js'
}

Other case is Rollup usage (Production build) - momentJs have its code as ES6 module (in moment/src), but it exports in different way (then usual export). That's why import moment from 'moment'; works with Rollup's

include: [
  'node_modules/rxjs/**',
]

and ES6 modules import.

Yes, to use ES6 wrapper (such as moment-es6 or so) is simple solution. But it always requires momentJs. Same time there is one more simple solution for this issue - replace your import row from Dev to Prod. For example, Gulp can use gulp-replace at some step:

return gulp.src([
     ...
    ])
    .pipe(replace('import * as moment from \'moment\';', 'import moment from \'moment\';'))
     ...;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.