user3006967
user3006967

Reputation: 3545

angular4 aot throw require is not defined error

I spent quire a while to do AOT with angular 4, and always got this error: Uncaught ReferenceError: require is not defined

I got this error because I am using jquery in my app, and in the application, I am having this code:

var jQuery = require('jquery');
jQuery("xxxx").val()

If I remove this, the whole AOT worked like a charm.

My tsconfig-aot.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
"lib": ["es5", "dom"],
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  },
 "files": [
    "src/app/app.module.ts",
    "src/main.ts"
  ],

  "angularCompilerOptions": {
   "genDir": "aot",
   "skipMetadataEmit" : true
 }
}

And my rollup config:

import rollup      from 'rollup'
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs    from 'rollup-plugin-commonjs';
import uglify      from 'rollup-plugin-uglify'

export default {
  entry: 'src/main.js',
  dest: 'public/build.js', // output a single application bundle
  sourceMap: false,
  format: 'iife',
  onwarn: function(warning) {
    // Skip certain warnings

    // should intercept ... but doesn't in some rollup versions
    if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
    // intercepts in some rollup versions
    if ( warning.indexOf("The 'this' keyword is equivalent to 'undefined'") > -1 ) { return; }

    // console.warn everything else
    console.warn( warning.message );
  },
  plugins: [
      nodeResolve({jsnext: true, module: true}),
      commonjs({
        include: [ 'node_modules/rxjs/**','node_modules/jquery/**']
      }),
      uglify()
  ]
}

Does this mean, if you are using require() inside your ng4 app, then AOT will not work?

Thanks and hope to hear your advice.

I tried to use this: import * as jQuery from 'jquery'; Then it worked same as require, and in JIT mode, it worked fine. And also, ngc package is fine. But when I use roll-up, the out put has this error:

🚨   Cannot call a namespace ('jQuery')
src/app/app.component.js (14:8)
12:     }
13:     AppComponent.prototype.ngOnInit = function () {
14:         jQuery('h1').html('New Content');
            ^
15:     };
16:     return AppComponent;

This is updated code:

import  * as jQuery from 'jquery';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./scss/app.component.scss']
})
export class AppComponent implements OnInit{

    ngOnInit(){

        jQuery('h1').html('New Content');

    }    

}

Any idea?

Upvotes: 1

Views: 4527

Answers (1)

Aluan Haddad
Aluan Haddad

Reputation: 31873

You can't use require with --module es2015.

It will cause a TypeScript compile error. The only reason it doesn't is that you are using the var..require form instead of the correct, in TypeScript, import..require form.

The only reason this doesn't raise a compile error is that there is an ambient declaration of require somewhere such as in an @types package.

Still it is very unlikely to work because require is not part of the ignition module syntax and since you're using Rollup it's definitely not going to be recognized, at least not adding a bunch of plugins and configuration.

Try

import jQuery from 'jquery';

If the above throws at run time that jQuery is not defined, try

import * as jQuery from 'jquery';

Looking at your updated question I see you are receiving the error

Cannot call a namespace ('jQuery') Which is actually the correct spec compliant behavior when using the form

import * as ns from 'module';

to import from a CommonJS module that assigns to module.exports (export = in TypeScript) in order to export its value.

Interop with CommonJS modules is supposed to make the value of module.exports available under the default export which will translate into the default property of the module namespace object that is imported. That is to say the form

import jQuery from 'jquery';

is actually just a shorthand for

import {default as jQuery} from 'jquery';

Unfortunately, interop is far from smooth with many environments, transpilers, loaders, and bundlers handling it differently.

NodeJS itself does not even implement it.

This is one extremely rare area where TypeScript does the categorically wrong thing. It doesn't handle this case at all. Instead recommending the

import * as ns from 'module';

form which will fail in a spec compliant environment for

ns()

because the imported module namespace object has no [[Call]] slot.

Fortunately, you can sidestep this behavior, as you are currently doing, by having TypeScript output es2015 modules and processing them with another tool, such as Rollup.

Another option is to use TypeScript's system module option and SystemJS.

This may seem a bit overly detailed and at the same time a bit hand waving, but I think it is important to realize how broken interop actually is so that we can fix it someday (I hope).

Update:

The TypeScript team is actively looking into the issue and it appears that the correct syntax

import jQuery from 'jquery';

will eventually be supported by TypeScript without the need for another transpilation layer.

One day, your children will be able write web applications without having to know about this pitfall!

Upvotes: 2

Related Questions