@khatastroffik/one-ts-for-all
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

One Typescript for all

This is a PoC demonstrating a "single source" Typescript library project generating automatically multiple versions of the script (CJS, ESM, TS) and a sample CLI: ONE-FOR-ALL

Table-of-Content

Overview

one-typescript-for-all Architecture

Objectives

SINGLE TYPESCRIPT INPUT → MULTIPLE OUTPUT

This is the main objective: ONE-FOR-ALL!

  • Implement a library using Typescript exclusively.
  • Use the library with any client which is able to use/run CommonJS, ECMAScript or Typescript modules.

RUN ESM LIBRARIES IN THE BROWSER

A browser app/client may use the ESM library directly. In this case, a modern browser is required. This use case is demonstrated in this PoC.

Older browser would require to adapt the scripts before integration, using a supplemental "transpiler" to output compatible scripts - hence requiring a further implementation step. Such older browsers aren't in the focus of this project.

Requirements

Node.js and NPM (or yarn) should be "globally" installed on your system to ease the installation of the packages and/or source files. Preferably:

  • node: >=13.x
  • npm: >=6.x

Typescript may be globally present as well, thus is not required, since the PoC will use its own (local) version:

  • tsc: >=3.9.5 (earlier version like 3.8.x should be ok - not tested, though)

Project Development versus Package Publication

The PoC is configured (package.json and .gitignore) to separate development and usage of the lib/app: "GitHub" versus "NPM".

  1. The bare sources (typescript only) of the library and application are stored in the Github Repository. use this if you'd like to develop, adapt etc. the library/application

  2. The transpiled i.e. usable library artifact (ESM, CommonJS, Typescript and *.d.ts types declaration files ) is stored in the NPM repository - though without the source files.
    use this if you'd like to use the library/application in your project.

This separation is intended:

  • to reduce the size of the published package to the min possible and
  • to clearly separate the different concerns.

Note: the artifacts i.e the files could be minified and thus reduce the size of the package furthermore. This is not implemented here, though.

How to implement "one-for-all"?

This section summarize the techniques i.e. engineering procedures used to realize the "one-for-all" objective.

Sources

The sources files are all contained in the src folder. No exception.

Typescript compiler (TSC) configuration

The output (multiple modules i.e. CJS, ESM...) of TSC is driven by the different tsconfig.*.jsonfiles and by the package.json file:

  1. There's intentionally NO default tsconfig.json file in this project! Instead, builds are created on purpose i.e specifically through targeting a specific tsconfig.
  2. The tsconfig.common.json is declaring the configuration shared by all targets. Hence, it correspond to a traditional tsconfig.json, though rename on purpose (see above).
  3. The tsconfig.bin.json is targeting the generation/output of the CLI application in the bin folder.
  4. The tsconfig.esm.json is targeting the generation/output of the ESM scripts version in the lib/esm/ folder.
  5. The tsconfig.cjs.json is targeting the generation/output of the ESM scripts version in the lib/esm/ folder.
  6. The tsconfig.types.json is generating the types declaration *.d.ts files for the Typescript scripts version in the lib/ts/ folder. This is required to provide those files in the npm package.

.gitignore file

Since the source code repository should NOT contain the "output" of the compilation/distribution, the .gitignore file excludes the corresponding folders: /lib and /bin.

npm scripts

In order to generate the output i.e to produce the different artifacts (ESJ, ESM, CLI and TS) to be published in a NPM repository easily, the package.json defines some npm scripts. Those scripts can be executed using:

> npm run <name-of-the-script>

The most important npm scripts of the project are described below:

{
  "scripts": {

    // ===== EXECUTE CLI =============================

    // RUN the CLI application using TS-NODE (no artifact is generated)
    "start": "ts-node --project tsconfig.common.json src/application.ts -d",

    // START the COMPILED CLI application (prior build is required)
    "node": "node bin/application.js",

    // ===== BUILD ARTIFACTS ========================

    // BUILD the single artifacts
    "build:bin": "tsc -p tsconfig.bin.json",
    "build:cjs": "tsc -p tsconfig.cjs.json",
    "build:esm": "tsc -p tsconfig.esm.json",
    "build:types": "tsc -p tsconfig.types.json",

    // BUILD ALL artifacts at once
    "build": "npm run build:types && npm run build:bin && npm run build:cjs && npm run build:esm",

    // ===== PACKAGE PUBLICATION======================

    // PREPARE and GENERATE all artifacts for publishing usage
    "prod": "npm run cleanup && npm run build && npm run copyts && npm run test:prod",

    // check the content of the NPM package i.e. the list of artifacts that should be published
    "checkpkg": "pkgfiles",

    // Pre-publishing (this script will be executed by NPM automatically when publishing)
    "prepublishOnly": "npm run prod",

    // ===== DEVELOPMENT ONLY ========================

    // generate the artifacts and link the project in the local npm repository
    "local:publish": "npm run prod && npm link",
    // undo the local npm repository link
    "local:unpublish": "npm unlink && npm run cleanup:nodemodules && npm install",

    // ===== HELPERS =================================

    // delete the folders containing the published artifacts
    "cleanup": "rimraf ./lib ./bin ./coverage",
    // delete the node_modules folder (e.g. when un-linking the project to local npm repository)
    "cleanup:nodemodules": "npx rimraf ./node_modules",
    // copy the Typescript artifacts to be published in the npm package
    "copyts": "copyfiles -u 2 src/lib/**/* -e **/*.spec.ts lib/ts"
  }
}

Note: Using this configuration allows to publish the library on NPM without hassle. Just call > npm publish! The configured npm scripts will do the rest.

The package.json targets

Once the artifacts are produced and published, a client project intending to use the library package will operate a lookup in the package definition to find and use an appropriate library artifact:

this automatic lookup is using the following fields (if available) as defined in the package.sjon file:

{
  // This define the DEFAULT artifact of the package. Here: the TYPESCRIPT version
  "main": "lib/ts/library.ts",

  // Tells the client project where the type declarations are located
  "types": "lib/ts/library.d.ts",

  // Locate the main ESM artifact for an ESM capable environment (e.g. node >= v13.* if configured approprietly)
  "module": "lib/esm/library.js",

  // Locate the main ESM artifact for Browser environment...
  "browser": "lib/esm/library.js",

  // This location is NOT standard, thus included here for documentation purposes.
  // The value could be used in place of the "main" defined above = to publish a CJS-BY-DEFAULT package
  "node": "lib/cjs/library.js",

  // Locate the CLI app artifact. Used when installing the package to register the CLI on the system (localy or globaly). The CLI executable is registered as "ts4all" here.
  "bin": {
    "ts4all": "bin/application.js"
  },
}

Notes: The PoC package is defining the "Typescript artifact" as the default. Instead, the "CJS artifact" could have been declared as default through the key-value "main": "lib/cjs/library.js".

Important !

The "main", "module" and "browser" keys specified in the library (i.e. in the package.json of the library) do impact the binding of the library in any client project: the configuration in the library entails the suitable usage of the import ... from ...; and ... = require(...); statements (including suitable paths) in the client projects!

Thus, it is recommended to never change this specification after the library package has been published. Otherwise, a "BREAKING CHANGE" should be commited and a major version released! (See BREAKING CHANGES in commits)

Project documentation

Package Sidebar

Install

npm i @khatastroffik/one-ts-for-all

Weekly Downloads

0

Version

1.0.0

License

MIT

Unpacked Size

25.7 kB

Total Files

11

Last publish

Collaborators

  • khatastroffik