Sometimes I may need to share some of my logic\algorithm with other developers. In this case I might need to build a library.

That library might be used in the customer's browser, or might be referenced by other front-end projects, may be based on Angular or React.

So how can we build a package with TypeScript that benefits from strong type while can be installed\reference\used by others?

Full example after all those steps that you can download directly: Download zip

Manual steps

After creating a new folder, do some initialization commands:

$ git init
$ npm init

Strongly suggest pushing that repo to GitHub and assign a license first.

$ git add . && git commit -m "Init project" && git remote add origin https://github.com/your-name && git push -u origin main

Create a .gitignore file to ignore those dependencies:

node_modules
dist

Then we gonna install some packages for building the project.

$ npm install --save-dev terser-webpack-plugin ts-loader typescript webpack webpack-cli

After installing, you package.json file might be looking like this:

{
  "name": "my-project", // name your project.
  "version": "1.0.0",
  "description": "my project",
  "scripts": {
    "build": "webpack"
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com/myname/myrepo"
  },
  "keywords": [
    "disable-with"
  ],
  "author": "anduin@aiursoft.com",
  "license": "MIT",
  "homepage": "https://github.com/Anduin2017/jquery-disable-with#readme",
  "devDependencies": {
    "terser-webpack-plugin": "^5.1.2",
    "ts-loader": "^9.2.2",
    "typescript": "^4.3.2",
    "webpack": "^5.38.0",
    "webpack-cli": "^4.7.0"
  }
}

Since we are building a TypeScript project, create a new file name: tsconfig.json

{
    "compileOnSave": true,
    "compilerOptions": {
      "outDir": "./dist/out-tsc",
      "sourceMap": true,
      "declaration": false,
      "moduleResolution": "node",
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "target": "es5",
      "typeRoots": [
        "node_modules/@types"
      ],
      "lib": [
        "es2016",
        "dom"
      ],
      "noUnusedLocals": true,
      "noUnusedParameters": true
    }
  }
  

Now your repo might be looking like this:

Now, create a new folder name src. And you can put your entry in it.

Put a new file with .ts extension under the src folder.

Now you can start building your new library. Of course, you can add multiple typescript files and import them each other.

Sample class:

class DisableWith {
    constructor(property: string) {
        this.initDisableWith(property);
    }

    initDisableWith(property: string): void {
        document.querySelectorAll(`*[${property}]`).forEach(element => {
            let value = element.getAttribute(property);
            alert(value);
        });
    }
}

export default DisableWith;

Do export your class as default!

After that, creating a new file named: webpack.config.js in the root folder of the repo.

Modify the content to make it buildable.

Only in production mode, will it call terser plugin to optimize the package. And in normal mode shall it load your ts file  from the entry and package it with umd config.

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const production = process.env.NODE_ENV === 'production' || false;

module.exports = {
    entry: ['./src/jquery-disable-with.ts'],  //  <- Modify it to your entry name.
    module: {
        rules: [
            {
                test: /\.ts?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    mode: 'production',
    output: {
        filename: production ? 'jquery-disable-with.min.js' : 'jquery-disable-with.js',
        path: path.resolve(__dirname, 'dist'),
        globalObject: 'this',
        library: 'DisableWith',
        libraryExport: 'default',
        libraryTarget: 'umd'
    },
    optimization: {
        minimize: production,
        minimizer: [
            new TerserPlugin({})
        ]
    }
};

Finally, don't forget to modify your package.json file to specify the entry.

Add this:

  "main": "dist/jquery-disable-with.js"

Now you can try to build your project with command:

$ npm run build
$ npm run build --production

Hopefully, you can see there gonna be new generated file located in your dist folder.

You can now try to build a sample here. Just reference your file in your dist folder.

Finally, we gonna publish this project to npm.

AzureAD+AnduinXue@Aiursoft-SS MINGW64 ~/source/repos/Anduin2017/jquery-disable-with (master)
$ npm login
npm notice Log in on https://registry.npmjs.org/
Username: anduin2017
Password: ******************************
Email: (this IS public) anduin@aiursoft.com
Enter one-time password from your authenticator app: 233333
Logged in as anduin2017 on https://registry.npmjs.org/.

$ npm publish
npm notice
npm notice package: jquery-disable-with@2.2.3
npm notice === Tarball Contents ===
npm notice 1.1kB LICENSE
npm notice 1.6kB README.md
npm notice 126B  build.sh
npm notice 2.7kB dist/jquery-disable-with.js
npm notice 1.0kB dist/jquery-disable-with.min.js
npm notice 396B  example.html
npm notice 709B  package.json
npm notice 1.9kB src/jquery-disable-with.ts
npm notice 464B  tsconfig.json
npm notice 887B  webpack.config.js
npm notice === Tarball Details ===
npm notice name:          jquery-disable-with
npm notice version:       2.2.3
npm notice filename:      jquery-disable-with-2.2.3.tgz
npm notice package size:  4.0 kB
npm notice unpacked size: 11.0 kB
npm notice shasum:        asdfasdfasdfasdfasdfasdfadfasf
npm notice integrity:     sha512-asdfasdfasdfasdf==
npm notice total files:   10

And your customers can install your project via:

$ npm install --save your-package

<script src="node_modules/jquery-disable-with/dist/jquery-disable-with.js"></script>

Or he can import it after installing:

import DisableWith from 'jquery-disable-with';
new DisableWith('data-disable-with');