As part of my project, I am enhancing an existing library to make it compatible with TypeScript. To showcase this modification, I have condensed it into a succinct Minimal working example
The specified requirements
- To ensure backward compatibility, the library should remain importable using a straightforward
<script>
tag. - To simplify future usage, importing the library with a single TypeScript
import
statement (without requiring<script>
tags in HTML) should be possible. - I opted for rollup for compilation, but I welcome alternative methods such as gulp or webpack to achieve the desired outcome.
Current progress update
The Library Structure
Organization of files:
│ package.json
│ rollup.config.js
│ tsconfig.json
│ yarn-error.log
│ yarn.lock
│
└───src
lib.ts
options.ts
package.json
:
{
"name": "library",
"version": "1.0.8",
"description": "example library",
"main": "dist/lib.umd.js",
"module":"dist/lib.esm.js",
"types":"dist/types/lib.d.ts",
"license": "MIT",
"private": true,
"devDependencies": {
"@rollup/plugin-typescript": "^8.3.0",
"rollup": "^2.67.0",
"tslib": "^2.3.1",
"typescript": "^4.5.5"
},
"exports": {
"import": "./dist/lib.esm.js",
"require": "./dist/lib.umd.js"
},
"scripts": {
"build:types": "tsc -d --emitDeclarationOnly",
"build:js": "rollup -c rollup.config.js",
"build:minjs:umd": "terser dist/index.umd.js --compress --mangle > dist/index.umd.min.js",
"build:minjs:esm": "terser dist/index.esm.js --compress --mangle > dist/index.esm.min.js",
"build:minjs": "npm run build:minjs:esm -s && npm run build:minjs:umd -s",
"build": "npm run build:js -s && npm run build:minjs -s && npm run build:types -s",
"prepublishOnly": "npm run lint -s && npm test -s && npm run build",
"semantic-release": "semantic-release"
},
"type":"module"
}
rollup.config.js
:
import typescript from '@rollup/plugin-typescript';
export default {
input: 'src/lib.ts',
output: [
{
file: 'dist/lib.esm.js',
format: 'es',
},
{
file: 'dist/lib.umd.js',
format: 'umd',
name: 'Lib',
},
],
plugins: [typescript({tsconfig:'./tsconfig.json'})],
};
tsconfig.json
:
{
"compilerOptions": {
"target": "es2016",
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"outDir": "./types",
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
}
src/lib.ts
:
import Options from "./options";
export default class Lib{
constructor(options:Options){
console.log("It works!");
console.log(options.message);
}
}
options.ts
:
export default interface Options {
message: string;
}
To compile all these components, I utilize yarn build:js
.
Library Implementation
Using <script>
tags
Upon embedding the resultant lib.umd.js
into a directory and setting up an index.html
:
<script src=lib.umd.js></script>
<script>
var a = new Lib({message:"message here"});
</script>
The functionality is operational without issues at this stage.
Incorporating into another TypeScript project
Subsequently, I developed a basic TypeScript project that integrates my modified library.
Folder structure:
│ gulpfile.js
│ package.json
│ tsconfig.json
│ yarn.lock
│
├───dist
│ index.html
│
└───Scripts
Index.ts
In package.json
, I include my library as a dependency, alongside jQuery to eliminate any configuration discrepancies within this project:
{
"private": true,
"version": "1.0.0",
"scripts": {
"preinstall": "npx use-yarn",
"gulp": "node_modules/.bin/gulp"
},
"name": "ts-import",
"devDependencies": {
"@types/jquery": "^3.5.13",
"gulp": "^4.0.2",
"gulp-browserify": "^0.5.1",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-sourcemaps": "^3.0.0",
"gulp-typescript": "^6.0.0-alpha.1",
"gulp-uglify": "^3.0.2",
"typescript": "^4.5.5",
"vinyl-source-stream": "^2.0.0"
},
"dependencies": {
"jquery": "*",
"library": "file:../../library"
}
}
tsconfig.json
:
{
"compilerOptions": {
"noEmitOnError": true,
"noImplicitAny": true,
"sourceMap": true,
"target": "es5",
"moduleResolution": "node",
"outDir":"./js"
},
"compileOnSave": true,
"exclude": [
"**/node_modules/**"
],
"include":[
"./Scripts"
]
}
Gulp is employed for TypeScript compilation and import resolution - gulpfile.js
:
const gulp = require('gulp');
const {series} = require('gulp');
const clean = require('gulp-clean');
const ts = require('gulp-typescript');
const sourcemaps = require('gulp-sourcemaps');
const browserify = require('gulp-browserify');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
function cleanAll(cb) {
return gulp.src([
"./tmp/",
"./dist/js"
], { read: false, allowEmpty: true })
.pipe(clean());
}
function transpileTS() {
const tsproject = ts.createProject('./tsconfig.json');
return tsproject
.src()
.pipe(sourcemaps.init())
.pipe(tsproject()).js
.pipe(sourcemaps.write('./sourcemaps'))
.pipe(gulp.dest('./tmp/js'));
}
function minJS() {
return gulp
.src('./tmp/js/Index.js')
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(browserify())
// .pipe(uglify())
.pipe(concat('index.min.js'))
.pipe(sourcemaps.write('./sourcemaps'))
.pipe(gulp.dest('./dist/js'))
}
exports.default = series( cleanAll, transpileTS, minJS );
The import and utilization of my library are done as follows - Scripts/Index.ts
:
import * as $ from "jquery";
import Lib from "library";
$(()=>{
console.log("creating lib instance.");
new Lib({message:"example message here"});
});
However, upon launching this in a browser - dist/index.html
:
<script src="js/Index.min.js"></script>
An error occurs:
Uncaught TypeError: library_1.default is not a constructor
Indeed, library_1.default
is nonexistent while library_1
exists:
> library_1
< class Lib {
constructor(options) {
console.log("It works!");
console.log(options.message);
}
}
How can this issue be rectified? It seems likely that the error resides within the library, though pinpointing the exact origin eludes me.