Using Rollup for Angular 2's AoT compiler and importing Moment.js

谁说胖子不能爱 提交于 2019-11-29 23:57:10

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()
]

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)

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')

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

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.

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

As of version 2.13.0,

import * as moment from 'moment';

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

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

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\';'))
     ...;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!