/jquery-plugin-typescript-example

Example project to demonstrate how to write a jQuery plugin using TypeScript

Primary LanguageTypeScriptMIT LicenseMIT

jQuery Plugin TypeScript Example

This example project demonstrates how to write a jQuery plugin using TypeScript. Experienced developers can get started quickly by following the brief instructions in the Getting Started section. For those who are new to TypeScript or those who want to learn more about the concepts behind the project structure there is a Step-by-Step Tutorial which explains the project setup in detail.

Introduction

This is an example project that demonstrates what I currently find a best practice project setup for the development of JavaScript code using the TypeScript language. Even if it shows the basic ideas by writing a jQuery plugin those concepts are not limited to jQuery at all. You can use this setup for any kind of frontend JavaScript project. To keep the example simple and understandable it focuses only on the most essentials aspects of the development process:

  • All the source code should be written in TypeScript with all the benefits like type checking, code completion etc.

  • The TypeScript source code should be structured into various modules to allow separation of concerns and reuse of certain classes or functions.

  • The TypeScript source code should be tested automatically using Karma and Jasmine with a minimum of extra configuration.

  • For usage in modern browsers the TypeScript source code should be compiled into ES2015 modules.

  • For better compatibility with older browsers and to facilitate loading in the web page the ES2015 modules should be bundled into a single JavaScript file using rollup.js.

  • The JavaScript bundle should be minified using UglifyJS to reduce load time.

  • All compiled resources (ES2015 modules, JavaScript bundles) should provide sourcemaps for easier debugging in the browser.

See the section Getting Started for quick instructions how to run the build process and how to try the example jQuery plugin.

See the Step-by-Step Tutorial for detailed explanations of the basic ideas behind this project setup.

Getting Started

  1. Clone the Git repository.

  2. Open a terminal in the project root.

  3. Make sure you have installed a recent version of Node.js and NPM.

  4. Install the dependencies by running: npm install.

  5. Build the project by running: npm run build.

  6. Execute the tests by running: npm test.

  7. Open the file dist/example.html in your browser. See the jQuery plugin in action.

Step-by-Step Tutorial

Setting up the development environment

TypeScript code cannot be executed in the browser directly. Before it can be used in a web page it must be compiled into pure JavaScript code. So we need to set up some kind of build process for the TypeScript compilation.

The TypeScript compiler is available as a NPM module and therefore it is obvious the create a NPM project and install the compiler as a development dependency. All we need to do is:

  1. Install a recent version of Node.js. I would recommend to use the LTS version because it is quite settled and should work with most of the libraries you may use in your project. The installation of Node.js also includes NPM, the package manager that we will be using for the build process and dependency management.

  2. Create a new directory where we would like to store our project in.

  3. Open a terminal in our project directory. Leave it open as we will have to execute various commands throughout this tutorial.

  4. Run the command: npm init. This will prompt for some basic project information which can be changed at any time later on. Keep pressing ENTER if you are not sure what to do.

Finally, we will have a file package.json in our project directory. This file is typical for all NPM projects and contains meta information about our project as well as the build scripts and a list of libraries that are required by the project.

Throughout this tutorial you will have to edit quite a lot of text files containing different kinds of source code (JavaScript, JSON). Even though it is possible to do all that with a simple text editor I would recommend to install a more sophisticated source code editor. You can start with the free and yet powerful Visual Studio Code if you are not sure what to choose. It has fantastic TypeScript support out-of-the-box.

Installing the TypeScript compiler

Once we have initialized our NPM project we can install the TypeScript compiler.

  1. Go to the terminal in our project directory.

  2. Run the command: npm install typescript --save-dev.

You will notice a new directory node_modules in our project. It is managed by NPM and contains all the libraries that our project depends on. Do not add or remove anything within this directory on your own because it is all handled by NPM.

Tip
You should exclude node_modules from your source code management. If you are using Git you should add a .gitignore file in your project directory which defines /node_modules as a resource to exclude.

Since the compiler is installed locally to our project we have to create a NPM script to be able to run the compilation from the command line.

  1. Open the file package.json with the text editor.

  2. Add a new propery compile with the value tsc in the scripts object:

package.json
{
  ...
  "scripts": {
    ...
    "compile": "tsc"
  }
  ...
}

This allows us to run the TypeScript compiler from the command line via npm run compile. But do not execute it for now since we have not created a configuration yet.

Configuring the TypeScript compiler

We run the TypeScript compiler by simply executing the tsc command in our project. Therefore the compiler needs some more information about where to look for source files and how the generated JavaScript code should look like. A very convenient way to configure the compiler is to put a tsconfig.json file directly in our project directory.

  1. Create a file tsconfig.json in our project directory.

  2. Open the file with the text editor and insert the following code:

tsconfig.json
{
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist",
    "target": "es2015",
    "module": "es2015",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "allowSyntheticDefaultImports": true,
    "removeComments": true,
    "sourceMap": true
  }
}
"rootDir": "src"

Uses the src directory as the root directory for the resolution of source files. This ensures that our compiled JavaScript files in the dist directory are organized in the same directory structure as in the src directory.

"outDir": "dist"

Writes the compiled JavaScript files to the dist directory.

"target": "es2015"

Compiles the TypeScript code to JavaScript code that still utilizes ES2015 language features like lambda expressions. This language level is not supported by all browsers yet but we will convert it to a more compatible level later on.

"module": "es2015"

Compiles the TypeScript modules to ES2015 modules using the standardized import/export syntax. This module format is required because we want to use the compiled JavaScript files with rollup.js to create a single JavaScript file.

"moduleResolution": "node"

Instructs the compiler to resolve non-relative module imports like import $ from 'jquery' with the Node.js mechanism thus looking for modules in the node_modules directory. This allows us to use modules installed with NPM in our TypeScript code. See the TypeScript handbook for further details.

"noImplicitAny": true

Raises a compile error if any variable does not have an explicit type declaration and therefore the compiler would have to assume any for it. This helps to improve code stability because it enforces strict type checking.

"noImplicitReturns": true

Raises a compile error if any function with a return type other than void or any has branches that do not explicitly return any result. This may result in unexpected behavior and therefore it is reasonable to let the compiler check for such mistakes.

"noImplicitThis": true

Raises a compile error if the keyword this is used in the code and the compiler does not know what kind of object it will be in that context. This can happen when you use this in functions which are not declared as methods of a class but by an interface. See the TypeScript handbook for an example and how to tell the compiler what this means in a function.

"allowSyntheticDefaultImports": true

Forces the compiler to accept default imports of modules which actually do not have any default export as it is the case with the jQuery type declaration. Without that option the statement import $ from 'jquery' would yield a compile error.

"removeComments": true

Removes the source code comments from the compiled JavaScript files. This reduces their size and we still have sourcemaps for debugging, so comments are not needed in the output.

"sourceMap": true

Generates .js.map sourcemap files for all generated JavaScript files. This makes it easier to debug our source code when it runs in the browser.

Tip
If you want to dive into the TypeScript compiler options further please have a look at the documentation.

Creating a TypeScript source file

Now that we have configured the TypeScript compiler we are ready to create a first source code file. Since we told the compiler to expect source files in the src directory we should put all TypeScript files there (or in subdirectories beneath).

  1. Create a directory src in our project directory.

  2. Create a new file example-service.ts in the src directory.

  3. Open the new TypeScript file in the text editor and insert the following code:

example-service.ts
export class ExampleService {
  getExampleMessage(name: string): string {
    return 'Hello, ' + name + '!';
  }
}

This source code file defines a simple class which could be used in any project. Notice there is no dependency on jQuery yet. The keyword export ensures that we can import this class in other modules as we will do later. Also pay attention to the type declarations on the method parameter and the method itself. This allows for explicit type checking which can help identify errors in the code.

Now we can try a first build of our project.

  1. Go to the terminal in our project directory.

  2. Run the command: npm run compile. This will launch the TypeScript compiler since we provided the compile script in the package.json.

After the command has been completed we should find a new directory dist in our project directory. It contains the compiled JavaScript file example-service.js and its corresponding sourcemap file example-service.js.map.

Installing the test frameworks

Right from the beginning of our project we should write tests for all our source code. This will help us keeping the project stable even when it grows over time and makes it ready for continuous delivery.

We will use the JavaScript library Jasmine to define test cases and leverage the Karma runner to execute those tests. Explaining the two frameworks in detail is beyond the scope of this tutorial. We will focus on what is needed in our project to create a basic unit test.

  1. Go to the terminal in our project directory.

  2. Run the command: npm install jasmine-core --save-dev. This will install the Jasmine framework that we will use to define our test cases.

  3. Run the command: npm install @types/jasmine --save-dev. This will install the TypeScript declaration files for Jasmine. Those are required because Jasmine itself is not written in TypeScript but we want to write our unit tests in TypeScript as well. The declaration files provide hints for the TypeScript compiler so that it recogizes all the functions provided by Jasmine.

  4. Run the command: npm install karma --save-dev. This will install the Karma test runner. Think of it as a framework that creates a synthetic HTML page which loads our JavaScript files, starts an embedded web server to provide all those files, launches a browser to open the test page thus running the tests and collects results from test frameworks like Jasmine.

  5. Run the command: npm install karma-jasmine --save-dev. This will install the Jasmine framework support for Karma.

  6. Run the command: npm install karma-phantomjs-launcher --save-dev. This will install the PhantomJS browser support for Karma. PhantomJS is a headless browser which does not open any windows and works perfectly for the execution of JavaScript unit tests. No need to install that browser on our own. The launcher pulls it as a dependency.

  7. Run the command: npm install karma-typescript --save-dev. This will install a TypeScript preprocessor for Karma. So we can send our TypeScript source code directly to the test runner without having to compile them to JavaScript in advance. It also provides a reporter which generates a test coverage report to help us identify what source code is executed by our tests.

Once we have installed all the tools and frameworks required for running the tests we must create a configuration file karma.conf.js to give the Karma runner some hints which files to include and how to run our tests.

  1. Create a file karma.conf.js in our project directory.

  2. Open the configuration file with the text editor and insert the following code:

karma.conf.js
module.exports = function (config) {
  config.set({
    basePath: '',
    browsers: ['PhantomJS'],
    frameworks: ['phantomjs-shim', 'jasmine', 'karma-typescript'],
    files: [
      'src/**/*.ts'
    ],
    preprocessors: {
      '**/*.ts': ['karma-typescript']
    },
    karmaTypescriptConfig: {
      compilerOptions: {
        noImplicitAny: true,
        noImplicitReturns: true,
        noImplicitThis: true,
        allowSyntheticDefaultImports: true,
        lib: ['DOM', 'ES5', 'ScriptHost', 'ES2015.Core', 'ES2015.Iterable']
      }
    },
    reporters: ['progress', 'karma-typescript'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    singleRun: true,
    concurrency: Infinity
  })
}

The configuration is exported as a typical Node.js JavaScript module. Some noteworthy entries:

browsers: ['PhantomJS']

Uses the PhantomJS browser to execute our tests. This is especially useful for continous integration because we do not have to install more complex browsers like Chrome or Firefox in our CI server.

frameworks: ['phantomjs-shim', 'jasmine', 'karma-typescript']

Loads the framework plugins for ES2015 shims, Jasmine and TypeScript which we have installed as separate NPM modules.

files: ['src/**/*.ts']

Includes all TypeScript source files beneath the src directory in the test runner.

preprocessors: {'**/*.ts': ['karma-typescript']}

Sends all TypeScript files to the preprocessor which automagically transforms them into JavaScript that can be loaded in the test page.

karmaTypescriptConfig: {…​}

Configures the TypeScript preprocessor. We have to make sure that it uses the same compiler restrictions as our normal compilation process because it does not honor the tsconfig.json file in our project directory. CAUTION: Do not override the target or module options since the preprocessor must set its own values to work properly. Nevertheless, we have to tell the compiler that some newer ES2015 features are available in the browser because we use shims.

reporters: ['progress', 'karma-typescript']

Processes the test results with the built-in progress reporter to display test progress in the console and with the TypeScript reporter to create a coverage report.

port: 9876

Starts the embedded web server on port 9876. If this conflicts with your system for some reason you should change it to a different value.

Tip
If you want to learn more about the Karma configuration please have a look at the documentation.

Since Karma is also installed locally in our project we will have to create NPM script to run the tests from the command line.

  1. Open the file package.json with the text editor.

  2. There should already be a property test in the scripts object (if not create a new property). Set the value of the test script to karma start:

package.json
{
  ...
  "scripts": {
    ...
    "test": "karma start"
  }
  ...
}

This allows us to run Karma from the command line via npm test. Note that there is no run command here because test is a default NPM script. Do not run the test now because we have not created a specification yet.

Creating a test specification

I would recommend to create a test specification file next to each TypeScript source file that contains testable code. The file should be named exactly like the source code file but with .spec.ts extension.

  1. Create the file example-service.spec.ts in the src directory of our project.

  2. Open the specification file with the text editor and insert the following code:

example-service.spec.ts
import { ExampleService } from './example-service';
describe('ExampleService', () => {
  it('should return a greeting for the given name', () => {
    let exampleService = new ExampleService();
    let messageText = exampleService.getExampleMessage('Frank');
    expect(messageText).toBe('Hello, Frank!');
  });
});

We imported the service class from the previously created module using an appropriate import statement. Notice the ./ in the module name which is a relative reference and so the compiler knows that the module file can be found in the same directory as the specification file. Then we used the describe function of the Jasmine framework to define a test suite. Typically it should be named like the class or function under test because that will produce nicely readable output if tests fail. Finally, we created a first test case by using Jasmine’s it function. It should be given a brief but understandable description of the expected behavior that is verified by this test and a function to execute the test steps. Within a test case we can make assertions by using the expect function.

Now the time has come to run the first test case.

  1. Go to the terminal in our project directory.

  2. Run the command: npm test. This will fire up the Karma test runner with the configuration we defined in the karma.conf.js.

Finally, we should see a message that one test has been executed successfully. Like this:

Executed 1 of 1 SUCCESS (0.004 secs / 0.001 secs)

You will notice a new directory coverage in our project directory which contains the test coverage report. There we can see which functions have been executed during the tests and where we should better add some more test cases.

Structuring the project with modules

As the project grows larger it is desirable to split the source code across multiple files for several reasons:

  • Separation of concerns: As a best practice we should write classes and functions that are dedicated to a well-defined and manageable purpose. For example, why should some higher level busines logic care about how data is fetched from the server? There should be some other module which simply provides functions to retrieve business data and handles all the technical stuff under the hood.

  • Code reuse: We might write some more general classes or functions which can be reused across multiple parts of our project or even reused in other projects as well.

  • Better testability: Small and dedicated functions can be tested more easily.

  • Allow developers to work more independently on certain parts of the code. Having one large source code file would lead to merge conflicts all the time.

TypeScript and ES2015 support the concept of modules to allow source code to be split across multiple files. As we have done already in the first test case we can import classes, functions or even simple values from other source files by using respective import statements. Note that we can only import resources that the modules export by using the export keyword. See the TypeScript handbook for more insights on modules.

Let’s create another TypeScript file which makes use of the class we previously created.

  1. Create a new file example-plugin.ts in the src directory of our project.

  2. Open the new TypeScript file in the text editor and insert the following code:

example-plugin.ts
import { ExampleService } from './example-service';
let example = function () {
  let exampleService = new ExampleService();
  let messageText = exampleService.getExampleMessage('Frank');
};

So we import the class ExampleService from the module file ./example-service.ts to use it in our plugin module. Our plugin only depends on the API that is exposed by the service class, so as long as we keep the API stable we ca change the internals of the service without having to touch the plugin.

Writing a jQuery plugin

Once we have created a separate module for our jQuery plugin we can use jQuery itself to register an extension.

  1. Go to the terminal in our project directory.

  2. Run the command npm install jquery --save. This will install jQuery as a normal dependency to our project.

  3. Run the command: npm install @types/jquery --save-dev. This will install the type declarations for jQuery to allow the TypeScript compiler to recogize all the functions provided by the framework.

  4. Open the file example-plugin.ts in the text editor and insert the following code:

example-plugin.ts
import $ from 'jquery';
import { ExampleService } from './example-service';
$.fn.examplePlugin = function (this: JQuery): JQuery {
  let exampleService = new ExampleService();
  let messageText = exampleService.getExampleMessage(this.text());
  this.text(messageText);
  return this;
};
Important
Extending the $.fn object using TypeScript requires some additional declaration to be present in the project (see src/example-plugin-interface.d.ts). This is required to tell the compiler that a new property/function will be available on the type JQuery and how it looks like.

The new thing here is the import of the jquery module which allows us to make use of the well-known dollar function. With the statement $.fn.examplePlugin = function …​ we register an extension that can be used on any jQuery object like this:

$('.some-selector').examplePlugin();  // Executes our plugin function

Notice the this parameter in our function declaration is not a real parameter but a TypeScript helper to define the type of this within that function. This paramater is removed by the TypeScript compiler when it generates the JavaScript file.

A jQuery plugin function should typically return this to allow chaining like that:

$('.some-selector').examplePlugin().css('color', 'red');
Important
TypeScript lambda expressions are not suitable for the declaration of jQuery plugin functions because they override the this context of the function so that we would not be able to access the jQuery object anymore.

Bundling the JavaScript modules

So far we have created the two modules example-service.ts and example-plugin.ts which can be compiled to their respective ES2015 modules by running npm run compile on the command line. But how can these modules be used in a normal web page? Up to now there is no native browser support for JavaScript modules (no matter which format), so we have to choose one of the following solutions:

  • Include a module loader into our web page (e.g. SystemJS) and configure it to load our ES2015 modules. This will typically generate one AJAX request per module because the loader requests each module as soon as it is imported.

  • Use a module bundler (e.g. rollup.js) during our build process to create a bundle JavaScript file which contains all of our modules and can be included into our web page with a simple <script> tag.

There are pros and cons for each of these approaches. While the first one seems promising to me once all major browsers natively support ES2015 modules and HTTP/2 has been rolled out, I would stick to the second solution for now because this makes it really easy to integrate our jQuery plugin into any web page without the hassle of a cumbersome module loader configuration.

We will be using rollup.js to package everything together. This requires some configuration in our project.

  1. Go to the terminal in our project directory.

  2. Run the command: npm install rollup --save-dev. This will install the rollup.js module bundler as a development dependency.

  3. Run the command: npm install rollup-plugin-babel --save-dev. This installs the Babel plugin for rollup.js that we will use to transpile our ES2015 modules to the more compatible ES5 language level.

  4. Run the command: npm install rollup-plugin-sourcemaps --save-dev. This installs the Sourcemap plugin for rollup.js so that we can still debug our TypeScript code in the browser even when we are using the JavaScript bundle.

  5. Run the commands: npm install babel-preset-env --save-dev and npm install babel-plugin-external-helpers --save-dev. This will install a preset and helper modules for Babel which be used by the Babel plugin.

As with the TypeScript compiler and Karma we need to configure rollup.js with a separate configuration file to tell the bundler which module to use as the entry point and what kind of bundle we would like to generate.

  1. Create a new file rollup.config.js in our project directory.

  2. Open the new configuration file with the text editor and insert the following code:

rollup.config.js
import babel from 'rollup-plugin-babel';
import sourcemaps from 'rollup-plugin-sourcemaps';

export default {
  input: 'dist/example-plugin.js',
  output: {
    file: 'dist/example-plugin-bundle.js',
    format: 'iife',
    sourcemap: true,
    globals: {
      jquery: 'jQuery'
    }
  },
  external: [
    'jquery'
  ],
  plugins: [
    babel({
      exclude: 'node_modules/**',
    }),
    sourcemaps()
  ]
};

The configuration is a Node.js-style module which exports an object with the configuration values.

input: 'dist/example-plugin.js'

Uses the compiled ES2015 module of our jQuery plugin (compiler output of example-plugin.ts) as the entry point to start the bundling process. Starting from this module the bundler will work through the imports and package everything together.

output.file: 'dist/example-plugin-bundle.js'

Writes the generated JavaScript bundle to the file example-plugin-bundle.js in the dist directory of our project.

output.format: 'iife'

Instructs rollup.js to generate the bundle in IIFE format (Immediately Invoked Function Expression) which is the right format to include the JavaScript file in a HTML page using a simple <script> tag.

output.sourcemap: true

Produces a sourcemap file for the JavaScript bundle to allow easier debugging of our code even when only the single JavaScript file is loaded in the browser.

external: ['jquery'], output.globals: {jquery: 'jQuery'}

Tells the bundler that the module jquery is an external dependency which should not be included in the bundle and that this external module is defined in the global variable jQuery. So we will have to include jQuery with a separate <script> tag into our web page before we include our bundle.

plugins: […​]

Enables the Babel plugin to transpile our JavaScript bundle to the more compatible ES5 language level and to load the sourcemaps of the ES2015 modules we are using as input. This ensures that we can even debug our TypeScript code when we use the bundle in a web page.

Tip
For more details on the configuration options please refer to the documentation.

Since we use the Babel plugin to scale the JavaScript language level down to ES5 we have to tell the transpiler what preset to use.

  1. Create a new file .babelrc in our project directory.

  2. Open the new configuration file with the text editor and insert the following code:

.babelrc
{
  "presets": [
    ["env", {
      "modules": false
    }]
  ],
  "plugins": [
    "external-helpers"
  ]
}

Since rollup.js is installed locally to our project we have to create a NPM script to allow bundling our modules from the command line.

  1. Open the file package.json with the text editor.

  2. Add a new propery bundle with the value rollup -c in the scripts object:

package.json
{
  ...
  "scripts": {
    ...
    "bundle": "rollup -c"
  }
  ...
}

Now we can start rollup.js from the command line via npm run bundle. But be aware that this script requires our TypeScript sources to have been compiled. So we would always have to run npm run compile first before we can create the bundle. Therefore it is convenient to create another script build in our package.json which executes both scripts as a sequence:

package.json
{
  ...
  "scripts": {
    ...
    "build": "npm run compile && npm run bundle"
  }
  ...
}

So let’s give it a try.

  1. Go to the terminal in our project directory.

  2. Run the command: npm run build. This should first execute the TypeScript compilation and once it has been completed successfully it will start the rollup.js bundling.

Finally, there should be the bundle JavaScript file example-plugin-bundle.js in the dist directory of our project. This file can be included right away in any web page that also includes jQuery. For example:

<!DOCTYPE html>
<html>
<body>
  <p>Frank</p>
  <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
  <script src="example-plugin-bundle.js"></script>
  <script>
    $('p').examplePlugin();
  </script>
</body>
</html>

Minifying the JavaScript bundle

So far, so good. The JavaScript bundle we have created can be used as it is but it is still quite large because the JavaScript code is pretty formatted. This is not needed in a production environment. So we should further reduce the bundle size by creating a minified version with UglifyJS.

  1. Go to the terminal in our project directory.

  2. Run the command: npm install uglify-js --save-dev. This will install UglifyJS as a development dependency in our project.

  3. Open the file package.json with the text editor.

  4. Add a new propery minify in the scripts object:

package.json
{
  ...
  "scripts": {
    ...
    "minify": "uglifyjs dist/example-plugin-bundle.js --output dist/example-plugin-bundle.min.js --source-map \"filename='dist/example-plugin-bundle.min.js.map',url='example-plugin-bundle.min.js.map',content='dist/example-plugin-bundle.js.map'\""
  }
  ...
}

As you can see the script is a little bit more complicated because UglifyJS does not provide any built-in way to use a separate configuration file.

First parameter dist/example-plugin-bundle.js

Defines the input file that should be minified. We use our JavaScript bundle produced by rollup.js.

--output dist/example-plugin-bundle.min.js

Writes the minified version of the JavaScript bundle to the file example-plugin-bundle.min.js in the dist directory.

--source-map "filename=,url=,content="

Configures various options for the generation of sourcemaps which help debugging in the browser even when we use the minified JavaScript bundle in the web page. The filename option tells UglifyJS to generate a separate sourcemap file example-plugin-bundle.min.js.map for the minified JavaScript. The url option defines which URL should be written to the minified JavaScript to reference the sourcemap file. The content option tells UglifyJS that there is already a sourcemap for the input file generated by rollup.js which should be used to even map the minified code directly to the TypeScript sources.

Once the script has been added to the package.json file we can run the minification from the command line via npm run minify. We can also integrate it into the build script sequence as an additional task:

package.json
{
  ...
  "scripts": {
    ...
    "build": "npm run compile && npm run bundle && npm run minify"
  }
  ...
}

Now see the whole build process in action.

  1. Go to the terminal in our project directory.

  2. Run the command: npm run build.

There should be an additional file example-plugin-bundle.min.js in the dist directory of our project. This minified version of the JavaScript bundle can be included in any web page like the normal one:

<!DOCTYPE html>
<html>
<body>
  <p>Frank</p>
  <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
  <script src="example-plugin-bundle.min.js"></script>
  <script>
    $('p').examplePlugin();
  </script>
</body>
</html>

Final considerations

It is debatable if that workflow with three steps (TypeScript compilation, rollup.js bundling, UglifyJS minification) is really a good solution, especially if all you want is the minified bundle. There are plugins for rollup.js which could be used to reduce everything to a single step:

  • TypeScript plugin for rollup.js to allow for direct processing of TypeScript source files.

  • UglifyJS plugin for rollup.js to allow for direct minification of the produced bundle.

Moreover, there are additional development helpers which have not been demonstrated in this example project to keep it simple. It is always a good idea to have some kind of live reload server which really helps during development when you have to make frequent changes to your source code. See Lite-Server for an easy solution.

License

MIT