npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

amidala

v1.4.0

Published

Use both AMD/CommonJS modules in the browser/Node, with dynamic loading for browser

Downloads

9

Readme

Amidala: AMD for Node and the browser

Use AMD and CommonJS modules in both Node and the browser.

This package adapts CommonJS modules to AMD, providing a dynamic module loader (for browser use), but also makes AMD modules usable from Node.

Usage

var amidala = require('amidala');
var amd = amidala();

// Use a mixture of AMD and CommonJS modules
amd.require(['foo', 'bar'], function (foo, bar) {...});

var app = require('express')();
// Dynamic loading from web - location is arbitrary
app.use('/amidala/', amd.dynamic);
<script src="/amidala/"></script>
<script>
    // a CommonJS module installed via NPM
    require('uri-templates', function (UriTemplate) {
        ...
    });
</script>

By default, the AMD loader script figures out where to load modules from by inspecting its own URL. This only works if you're loading it from a <script> tag in the page, otherwise you need to provide the .urls option.

What does it do?

On the server:

  • provides loading of AMD modules in Node, using the .require() method
  • provides a web-request handler function (.dynamic) which returns modules (including wrapper for NPM/CommonJS)

In the browser:

  • supplies define() or require() functions (following the AMD spec) which dynamically load dependencies from the server.

Security

Absolute and relative paths to modules are always disallowed.

This means that (by default) the only files this module loader will use/return will be from dependencies from node_modules/.

However, using the .path option it is possible to specify locations for modules (including relative modules). If you write an entry in .path that redirects to a folder, this gives users access to all suitable files in that folder, so make sure nothing sensitive is in there (even in hidden files).

Methods

First, construct an AMD loader using the options described below:

var amidala = require('amidala');
var amd = amidala({...});

The loader now has the following methods:

.require()

This method lets you use AMD modules on Node.js:

amd.require(['foo', 'bar'], function (Foo, Bar) {
    ...
});

The search locations for these modules can be specified using the .path option.

.dynamic

This provides a request listener (compatible with Express or http.createServer()). This listener:

  • provides a module loader (define() and require()) with dynamic loading
  • dynamically resolves and returns AMD/CommonJS modules

Options

.loader

This is the URL at which the main loader is accessible in the browser.

If the module is loaded in a subdirectory by Express, this URL is relative to that.

// loader URL is "/amidala/loader.js"
app.use('/amidala/', amidala({
    loader: '/loader.js'
}).dynamic);

If this is not specified, then it is taken to be the root:

// loader URL is "/amidala.js"
app.use('/amidala.js', amidala().dynamic);

.urls

This defines the URLs from which modules are requested/served This is passed to the client-side library, and it is not relative to any mounted roots.

By default, the loader will inspect its own URL (only works if loaded via a <script> element in the HTML source) and construct a template based on that:

// modules are loaded from "/amidala.js/..."
app.use(amidala({
    loader: '/amidala.js'
}).dynamic);

If this is a string, it's interpreted as a URI Template, using the variable name:

// modules are loaded from "/amd-modules/..."
app.use(amidala({
    loader: '/amidala.js',
    urls: '/amd-modules/{name}'
}).dynamic);

If it is an object or an array, it is taken to define a URI map between module names and URIs.

// modules are loaded from either "/amd-modules/..." or "/amd-local/..."
app.use(amidala({
    loader: '/amidala.js',
    urls: [
        // Separate rule for local modules, to prevent leading "." from disappearing
        {'./{+module}': '/amd-local/{+module}'},
        {'{+module}': '/amd-modules/{+module}'}
    ]
}).dynamic);

.path

With this option, you can specify locations (as files or folders) for some or all modules:

amd.dynamic({
    path: {
        "ui-components": __dirname + "/ui"
    }
})

These path definitions include submodules - so with the above, a call to require(['ui-components/xyz'], ...) will resolve to ..../ui/xyz instead.

You can provide an array for each entry, and it will attempt to resolve them in order:

amd.dynamic({
    path: {
        "./": [__dirname + '/static/amd/', __dirname + '/shared']
    }
})

The longest matches are checked first. You can use the empty string ("") to catch all modules not otherwise matched:

amd.dynamic({
    path: {
        "": __dirname + '/public/bower_components'
        "fs": "level-fs-browser"
    }
})

If you specify a string (or array) for .path, this is equivalent to using the empty string key (matches all modules):

amd.dynamic({
    path: __dirname + '/public/bower_components'
})

.extensions

This lets you provide a map from (lower-case) extensions to functions that assemble code from their contents. The result must contain a define() call for that module, but the module name can be implicit.

The functions take four arguments: sourceText, moduleName, moduleRelativeName, and callback. Use of callback is optional - if the function returns a string, then it will take that to be the result:

amd.dynamic({
    extensions: {
        "txt": function (sourceText) {
            return 'define([],' + JSON.stringify(sourceText) + ');';
        },
        "async-file": function (sourceText, name, relativeName, callback) {
            someAsyncOperation(function (error, code) {
                callback(error, code);
            });
        }
    }
});

If you need to resolve any relative module names, do that relative to moduleRelativeName instead.

For example, if the main entry point for a module some-module is /lib/main.js instead of /index.js, then moduleRelativeName will point to some-module/lib/main.js. This allows ./other.js to correctly resolve to /lib/other.js.

.transform

This lets you provide a function that transforms the module code before it is sent (for example, minification). This occurs after the any conversion in .extensions.

You can either return a transformed code string, or use an asynchronous callback:

var amd = amidala({
    transform: function (jsCode) {
        return '/*AMD*/\n' + jsCode;
    }
});

var amd = amidala({
    transform: function (jsCode, callback) {
        someAsyncApi(jsCode, {...}, callback);
    }
});

Enabling relative modules

Relative modules are not allowed - however, if they match an entry in .path, they can be rewritten to absolute locations:

amd.dynamic({
    path: {
        "./shared": __dirname + "/shared"
    }
});

This can be useful if you don't want to mess with the global namespace (e.g. occupying the global shared module).

However (as the security note above mentions), if the target is a directory then this gives users access to all files in that directory.