Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/suchipi/commonjs-standalone
Standalone CommonJS loader for any JS engine
https://github.com/suchipi/commonjs-standalone
browser commonjs embedded-js export import javascript loader module node-js require
Last synced: 4 months ago
JSON representation
Standalone CommonJS loader for any JS engine
- Host: GitHub
- URL: https://github.com/suchipi/commonjs-standalone
- Owner: suchipi
- License: mit
- Created: 2018-10-23T21:15:44.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2022-04-19T04:25:27.000Z (almost 3 years ago)
- Last Synced: 2024-10-01T19:41:01.379Z (4 months ago)
- Topics: browser, commonjs, embedded-js, export, import, javascript, loader, module, node-js, require
- Language: JavaScript
- Size: 244 KB
- Stars: 5
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `commonjs-standalone`
`commonjs-standalone` is a standalone CommonJS loader for use in any JavaScript engine. You give it a way to **resolve**, **read**, and **run** modules, and it will give you the `module`, `exports`, `require`, `__filename`, `__dirname` system you're familiar with from Node.js.
## How it works
This module exports one function called `requireMain`. Given the absolute path to a module and a `Delegate` object, `requireMain` will load the module at that path. You must provide your own `Delegate` object that `commonjs-standalone` will use to **resolve**, **read**, and **run** modules. A `Delegate` object has this shape:
```js
type Delegate = {
// A module at filepath `fromFilePath` is trying to require `id`.
// Resolve `id` into an absolute path, or throw an error if it can't be found.
resolve(id: string, fromFilePath: string): string,// Read the contents of the file at `filepath` and return them as a string.
read(filepath: string): string,// Run this code using the provided module environment object. The filepath
// is provided for your information; for configuring stack traces, or if you
// want to compile JSON to JS, etc.
run(
code: string,
moduleEnv: {
module: Object,
exports: Object,
require: Function,
__filename: string,
__dirname: string
},
filepath: string
): void
};
```How you **resolve**, **read**, and **run** modules will vary depending on your engine, which is why those things are left up to you.
## Examples
Here's an example of a very simple `Delegate` that keeps modules in a JavaScript object:
```js
// This delegate loads modules from a JavaScript Object.
const modules = {
"module-one": "require('module-two');",
"module-two": "console.log('hi from module-two');"
};const delegate = {
resolve(id, fromFilePath) {
// Normally you would use `fromFilePath` to resolve relative file paths,
// but in this example, only absolute paths are supported, so nothing
// needs to be resolved.
return id;
},read(filepath) {
return modules[filepath];
},run(code, moduleEnv, filepath) {
const wrapper = eval(
"(function (exports, require, module, __filename, __dirname) { " +
code +
"\n})"
);
wrapper(
moduleEnv.exports,
moduleEnv.require,
moduleEnv.module,
moduleEnv.__filename,
moduleEnv.__dirname
);
}
};requireMain("module-one", delegate); // logs 'hi from module-two'
```Here's a more complex `Delegate` that uses [the `resolve` package from npm](https://npm.im/resolve) to resolve modules and node's `fs` and `vm` modules to read and run them:
```js
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const resolve = require("resolve");const delegate = {
resolve(id, fromFilePath) {
return resolve.sync(id, {
basedir: path.dirname(fromFilePath),
preserveSymlinks: false
});
},read(filepath) {
return fs.readFileSync(filepath, "utf-8");
},run(code, moduleEnv, filepath) {
const wrapper = vm.runInThisContext(
"(function (exports, require, module, __filename, __dirname) { " +
code +
"\n})",
{ filename: filepath }
);
wrapper(
moduleEnv.exports,
moduleEnv.require,
moduleEnv.module,
moduleEnv.__filename,
moduleEnv.__dirname
);
}
};
```## API Documentation
### `requireMain(filepath: string, delegate: Delegate): void`
`requireMain` is exported from `commonjs-standalone` as a named export. It loads the first module (also called the main module), which will load other modules using its `require` function.
It should be called with an **absolute** path to a file to load, and a `Delegate` that it will use to **resolve**, **read**, and **run** modules.
```js
function requireMain(filepath: string, delegate: Delegate);
```### `Delegate`
A `Delegate` is an object with three functions on it: `resolve`, `read`, and `run`. Each are documented here.
### `Delegate.resolve(id: string, fromFilePath: string): string`
This function is called when a module at the absolute filepath `fromFilePath`
is trying to require `id`. **Resolve** `id` into an absolute path, or throw an error if it can't be found.For example, if `/Users/suchipi/my-package/index.js` contained:
```js
require("./foo");
```Then `Delegate.resolve` would be called with an `id` of `"./foo"` and a `fromFilePath` of `"/Users/suchipi/my-package/index.js"`.
In that example, you would probably want to return `"/Users/suchipi/my-package/foo.js"` (assuming it exists).
### `Delegate.read(filepath: string): string`
This function is called when the module system wants to **read** the contents of the file at the absolute path `filepath`. You should read them and return the code as a string.
### `Delegate.run(code: string, moduleEnv: Object, filepath: string): void`
This function is called when the module system wants to **run** some code. It's called with:
- The code to run,
- a `ModuleEnvironment` object, and
- the absolute path to where the module came from.The `ModuleEnvironment` object has 5 properties on it that you should expose to the running code: `module`, `exports`, `require`, `__filename`, and `__dirname`.
One way to expose these to your code is to wrap your code in a function:
```js
const wrappedCode =
"(function (exports, require, module, __filename, __dirname) { " +
code +
"\n})";
```Then you can pass everything from the `ModuleEnvironment` object into the function wrapper after you compile it:
```js
// You probably shouldn't use `eval`, since it leaks local variables into the scope. There is probably a way to
// run this cleanly from your JavaScript Engine's API.
const wrapperFunction = eval(wrappedCode);wrapperFunction(
moduleEnv.exports,
moduleEnv.require,
moduleEnv.module,
moduleEnv.__filename,
moduleEnv.__dirname
);
```## Supported features
- `module`
- `module.id`
- `module.exports`
- `require`
- `require.resolve`
- `require.cache` and deleting from `require.cache`
- `exports`
- `__filename`
- `__dirname`
- Circular dependencies## License
MIT