typescriptsystemjssystemjs-builder

Systemjs Bundle Global Import Config


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

The basic requirements I'm trying to do are:

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.


Solution

  • 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.