Systemjs Bundle Global Import Config

625 views Asked by At

I'm using Typescript, ES6 module syntax, and SystemJS / builder.

The basic requirements I'm trying to do are:

  • Enable typescript code completion by using @types packages (for global npm installs as well)
  • Using ES6 module import syntax (e.g. import * as _ from 'lodash' )
  • Have the builder exclude the globals from the build and still have them properly imported using CDN urls in dev / production.

The config I'm using for the build and dev / production env just to get it up and going:

System.config({
  meta: {
    "lodash": {
      "format": "global",
      "build": false,
      "exports": "_"
    }
    // ...more meta
  }, 
  map: {
    "lodash": "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js",
     // ...more maps
  }
});

From here I have a npm task which transpiles to ES6 and then bundles everything into a single file through babel plugin. The script is loaded directly on the production page fine and it loads. The issue is once a global dependency imports I keep getting errors like "_.clone is not a function" etc due to systemjs wrapping the CDN imports with an object like

{default: _ } //_ is the actual lodash export

I've been successful in changing the import to import _ from 'lodash' but then I get IDE errors since lodash (nor any other global script like angular) does not export a default value and I lose code completion.

What's the correct way to meet the requirements with systemjs / builder here?

As a side note I'm fine with using script tag loading instead of systemjs CDN imports if that works better.

1

There are 1 answers

3
Aluan Haddad On BEST ANSWER

TypeScript has a flag --allowSyntheticDefaultImports for just this scenario. Specifically, it informs the typechecker that another transpiler, loader, or bundler provides maps module.exports to exports.default in a later step.

You can specify this flag in under compiler options in tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "es2015",
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "baseUrl": "."
  }

Note that when the module format is set to "system", this flag is set implicitly.

Now you can write

import _ from "lodash";

and TypeScript will understand it, typecheck it, and validate that it is used correctly. However, this only affects typechecking. You still need, and in this case already have, a loader or intermediate transpiler that provides the runtime synthesis.

import * as _ from "lodash";

violates the proposed NodeJS -> ESM interop proposal and, if you call it as in _([1, 2]).map(x => x ** 2) also violates the ES spec which forbids a Module Namespace Object from being callable., so you are doing well to take advantage of the --allowSyntheticDefaultImports flag and the interop provided by SystemJS.