Search code examples
javascripttypescriptnpmtsc

How to build Typescript library with high compatibility for users?


What I want to do

I want to create a library that can be used as JS developer and as Typescript developer (frontend, no nodejs). That means that JS developer can use the library as inline script: <script type="text/javascript" src="mylib.js">var myLib = new MyLib();</script> and Typescript developers can use the library via imports.

What I need

I want to develop this library in Typescript and that's why I need a (build) solution that meets the following requirements:

  1. The library should be developed in Typescript.
  2. The build should contain all declarations automatically. Typescript developers should be able to import parts of this library.
  3. For Javascript users the library should be used "the old-fashioned-way" as inline code in a <script>-Tag
  4. The builds should be generated automatically if npm run build is called.
  5. Generally the compatibility to older browsers of this library should be as high as possible.

What I tried so far

  • I created a typescript library and experimented with some settings in the tsconfig.json. The only way I found is to set the "module" attribute to "none", but then I can't use imports, exports in my code and have to merge all my Typescript files into one file. That would make the developement more difficult.
  • I looked on the repository e.g. of Konva.js, which does what I want to do. But I don't understand how it works. It seems the developer converts the code to umd and then uses rollup.js and at the end of the build there exists a javascript file as long as all Typescript code that is needed.

Comment

I'm looking for a solution for some days, but it's hard to find the perfect term that represents what I'm looking for. That's why I ask it here and hope someone can help me.


Solution

  • I went deeper into package builder and found out: iife is the term I was looking for. A iife build allows to load a library directly via the script tag without anything else.

    After hours of testing I found a perfect solution to build my library that fits my needs: rollup.js

    The core npm packages are:

    My solution creates a package with this structure:

    |dist
    |- js
    |-- MyLibrary.js
    |- es
    |-- // ES files
    |- package.json
    

    The MyLibrary.js file can be imported easily within a <script> tag and the Typescript related files are in the "es" folder. The package automatically directs to the es/index.js that means Typescript developers should be able to use auto-complete for type suggestions in their IDE.

    You can find a sample repository here: https://github.com/julianpoemp/rolltsup-sample

    package.json:

    {
      "name": "my-lib",
      "version": "0.0.1",
      "description": "",
      "main": "src/main.js",
      "dependencies": {
        "rollup": "^2.38.5"
      },
      "devDependencies": {
        "@rollup/plugin-commonjs": "^17.1.0",
        "@rollup/plugin-typescript": "^8.1.1",
        "rollup-plugin-generate-package-json": "^3.2.0",
        "tslib": "^2.1.0",
        "typescript": "^4.1.3"
      },
      "scripts": {
        "build": "rollup --config rollup.config.js && rollup --config rollup.config_es.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      }
    }
    

    rollup.config.js

    import typescript from '@rollup/plugin-typescript';
    
    // rollup.config.js
    export default {
        input: 'src/index.ts',
        output: {
            file: 'dist/js/myLibrary.js',
            name: 'MyLibrary',
            format: 'iife'
        },
        plugins: [
            typescript({
                target: "ES5",
                declaration: true,
                outDir: "dist/js",
                rootDir: "src"
            })
        ]
    };
    

    rollup.config_es.js

     import typescript from '@rollup/plugin-typescript';
    import generatePackageJson from 'rollup-plugin-generate-package-json'
    
    // rollup.config.js
    export default {
        input: 'src/index.ts',
        output: {
            dir: 'dist/es',
            name: 'MyLibrary',
            format: 'es'
        },
        plugins: [
            typescript({
                target: "ES5",
                declaration: true,
                outDir: "dist/es",
                rootDir: "src"
            }),
            generatePackageJson({
                outputFolder: "dist",
                baseContents: (pkg) => {
                    pkg.main = "es/index.js";
                    pkg.scripts = undefined;
                    return pkg;
                }
            })
        ]
    };