Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/morris/samd

A tiny, static AMD API implementation
https://github.com/morris/samd

amd es6 javascript module-bundler typescript web

Last synced: 22 days ago
JSON representation

A tiny, static AMD API implementation

Awesome Lists containing this project

README

        

# SAMD

A tiny, static [AMD](https://github.com/amdjs/amdjs-api) API implementation
that allows __including AMD modules in regular script tags.__

*SAMD is currently experimental/awaiting feedback.*

- __1.3 KB__ minified & gzipped
- No dynamic loading; all scripts are __loaded by the browser__
- Works in __every browser alive__
- Supports `` & `<script async>`
- Allows progressive optimizations without impact on development

When combined with [TypeScript](typescriptlang.org),
SAMD may be used as an alternative to traditional JS bundling
(e.g. `webpack` or `rollup`).
This is the main use case for SAMD, as described below.
An example project can be inspected under `/example`.

[Try the example online](https://rawcdn.githack.com/morris/samd/0.1.0/example/public/index.html)

## Usage

SAMD enables inclusion of UMD/AMD modules in regular `script` tags:

```html
<script src="https://unpkg.com/samd@0/dist/samd.js" crossorigin>

require(['moment', 'lodash'], function (moment, _) {
// use dependencies
});

```

No additional script loading or special markup is required.

## Usage with TypeScript

Given a regular ES6/TypeScript project, with NPM dependencies and
[TypeScript configuration](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
in place, TypeScript is able to produce one-file AMD builds with wide browser
compatibility out-of-the-box:

```sh
tsc --outFile scripts/dist/app.js --target ES5 --module AMD
```

This generates an ES5 AMD bundle of the project, not including NPM modules but
explicitly referencing NPM imports as AMD dependencies.
Note that TypeScript is able to handle JS/JSX projects by using `--allowJs`.

This _application bundle_ can be used directly in a website
by including SAMD and UMD builds of the application dependencies from
e.g. [unpkg](https://unpkg.com/):

```html

// assuming the entry point is index.ts/index.js and it exports an init() function:
require(["scripts/index"], function (index) {
index.init();
});

```

SAMD infers module IDs from script `src` attributes, allowing
the original NPM imports to be resolved from the application bundle.

This setup is sufficient for development:

- It works in modern browsers.
- Application code is based on modern JS/TypeScript with ES6 imports.
- TypeScript's watch mode may be used to continuously build the application bundle.
- Source maps are supported.

Note that CommonJS-only dependencies cannot be used.
Dependencies must provide UMD/AMD builds (most popular libraries do, though).

### Production

In production environments, the respective production/minified builds of
dependencies should be used. Polyfills may be included using e.g.
[polyfill.io](https://polyfill.io/). For further optimization, the application
bundle may be minified through [terser](https://terser.org/).

```html

```

It is reasonable to serve separate HTML documents for development and
production environments. Employ templating techniques at build-time or on
server-side as needed.

Because of `unpkg`'s CDN, this setup is fairly performant when using popular
libraries. It is also widely compatible regarding browser support, depending on
the chosen TypeScript target.

### Self-hosting

If vendor dependencies need to be self-hosted, it is recommended
to copy the respective builds from `node_modules` to e.g. `scripts/vendor`.
Compiling the set of polyfills is out of scope for this document.

```html

```

It is encouraged to automate copying and other build steps through one or
more shell scripts (see `build.sh` in the example project).
Remember that SAMD infers AMD module IDs from script filenames
(after last `/`, before first `.`, while still allowing `@org/package`).

When served with HTTP/2, this setup is sufficiently performant for a wide range
of websites.

### Bundling

When having many vendor dependencies or when HTTP/2 is unavailable,
all scripts may be concatenated into a single file
(optionally running it through `terser` again).

```html

```

However, because UMD modules usually define anonymous modules,
SAMD is not able to identify individual module IDs of a bundle without user
input, so bundling cannot work by simple concatenation.

For this use case, SAMD ships with a CLI to infer and inject module IDs into
given UMD module files (see `build.sh` in the example project).
This CLI can also be used for concatenation.

```sh
samd -o scripts/dist/bundle.js \
scripts/vendor/polyfills.min.js \
scripts/vendor/samd.min.js \
scripts/vendor/react.production.min.js \
scripts/vendor/react-dom.production.min.js \
scripts/dist/app.min.js
terser scripts/dist/bundle.js -o scripts.dist.bundle.js
```

Note that this only has to be done in bundling scenarios.

### Libraries

SAMD is only useful for applications.
Packages for the web should be distributed as UMD for maximum compatibility.

## Conclusion

The presented TypeScript/SAMD-based workflow has useful properties and is fully
compatible with the web platform.

- Works with modern ES6/TypeScript projects, with little implications.
- Wide browser compatibility (even Internet Explorer)
- Loading of scripts is handled and optimized by the browser.
- Production optimizations are progressive and don't impact development.
- Bundling is (almost) reduced to simple concatenation.
- Bundling does not require configuration; simple shell scripts are sufficient
and provide additional flexibility.

At any point of the workflow, it is advised to measure page load performance
and user experience, and only proceed optimizing if needed.

## Limitations

- Dependencies must provide AMD/UMD builds.
- Multiple versions of dependencies are not supported.
- CommonJS is not supported.
- No tree-shaking.
- Relative module dependencies are not supported (yet).