UNPKG

als-require

Version:

A utility for using CommonJS require in the browser and creating bundles.

420 lines (342 loc) 15.8 kB
# als-require `als-require` is a lightweight library for importing, modifying, and executing CommonJS modules in both browser and Node.js environments. It simplifies modular development by supporting dependency resolution, plugins for code transformation, and dynamic bundling. **Capabilities of `als-require`**: - Import and execute CommonJS modules in NodeJs and browser with dependency resolution. - Dynamically bundle modules into a single executable function. - Transform module code using custom plugins before execution. - Support for cyclic dependency detection and flexible logging options. ## In version 3 - code refactoring - improved getting node modules on NodeJs (not in browser) - Changed constructor - Now it has options (`{plugins=[],cyclicDependencies,logger}`) - No `build` and `bundle` methods. `fn` and `stringFn` methods instead - `fn(options = {scriptBefore='', scriptAfter='', parameters=[]}) ` - `stringFn(options = {scriptBefore='', scriptAfter='', parameters=[],name})` - no minifying and minifying options - The goal make it more simple. You can minify it separately - plugins for modifying content - no context - You can add it as parameter to fn and pass as parameter's value ## Installation To install `als-require`, use npm: ```bash npm install als-require ``` ## Importing Import in nodejs: ```js const Require = require('als-require') const fn = Require.getModule('./some/path') ``` Import in browser: ```html <script src="/node_modules/als-require/require.js"></script> or <script src="/node_modules/als-require/require.min.js"></script> <script> Require.version = '1.0'; // optional - adding version when fetching files Require.cyclicDependencies = true // false by default Require.logger = console // console by default Require.getModule('./some/path').then(fn => {fn()}) // or require('./some/path').then(result => {/* code */}) </script> ``` ## Usage `als-require` has two files for NodeJS and browser which has same structure and api. ### Constructor Constructor initiating new Require instance. On Nodejs, it's automaticly reading all modules and applying plugins. In browser, you should run async method `getContent` for reading modules and applying plugins. Constructor has two parameters: path and options. The path is a string relative path to modules. And options has `plugins`, `logger` and flag for allowing `cyclicDependencies`. ```js const plugins = [ function(mod) { const {content,children,path} = mod mod.content = content.replace(...) } ] const options = { plugins = [], cyclicDependencies = false, logger = console } ``` ### fn and stringFn parameters The `fn` method generates an executable function containing the bundled modules. This function accepts custom parameters and includes pre- and post-bundle code if specified. The `stringFn` method behaves similarly but returns the generated function as a string. This is particularly useful for embedding the function in HTML or scripts. Here are parameters: * `scriptBefore` - Should be script as string to run it before bundle * `scriptAfter` - Should be script as string to run it after bundle * In this stage available two variables: `modules` and `result`. * `modules` - the object which includes all modules ({path:content,...}) * `result` - the function to run bundle which returned after `scriptAfter` * In this stage, you can add return for returning something else * `parameters` - The array of strings which will include parameters to pass in result's fn * `name` - Only for `stringFn` for changing function name * For example, for icluding it in html and call by it's name ### Node.Js ```js // Import the Require class const Require = require('als-require'); // Create a new Require instance with options const mod = new Require('./relative/path/to/module', { plugins: [], cyclicDependencies: true, logger: console }); // Define custom parameters and scripts const parameters = ['name']; const scriptBefore = "const SomeVariableAvailableForAllModules = `Hello ${name}`;"; const scriptAfter = ` console.log('All modules processed.'); return result; `; // Generate the executable function const resultFn = mod.fn({ scriptBefore, scriptAfter, parameters }); // Generate the function as a string with a custom name const name = 'bundleFn'; const bundleString = mod.stringFn({ scriptBefore, scriptAfter, parameters, name }); // Execute the result function const result = resultFn('Alex'); console.log(result); // Example: Embed the bundle string in a script const bundle = `${bundleString}\n const result = ${name}('Alex');`; console.log(bundle); ``` ### Browser ```html <script src="/node_modules/als-require/require.js"></script> <script> const scriptBefore = "const globalVar = 'Hello, world!';"; const scriptAfter = "console.log('Bundle execution complete.');"; const parameters = ['customParam']; // Create a Require instance for the browser const mod = new Require('./relative/path/to/module'); // Fetch and load all module dependencies mod.getContent().then((mod) => { // Define parameters and custom scripts // Generate the executable function const resultFn = mod.fn({ scriptBefore, scriptAfter, parameters }); // Execute the generated function const result = resultFn('Value'); console.log(result); }); // or require('./relative/path/to/module',{ scriptBefore, scriptAfter, parameters },'Value') .then(result => console.log(result)) </script> ``` ## Require node_modules packages and node modules In case of path which not starts with `.`, Require will look for file in node_modules (by checking in package.json). If such package not found (for example `require('fs')`), result for this package will return empty object. ```js const somePackage = require('some-package'); const somePackage1 = require('./node_modules/some-package/index.js'); const fs = require('fs'); module.exports = {somePackage,somePackage1,fs} ``` In case above `somePackage` and `somePackage1` should return the package, but fs, should return empty object. Pay attention, in browser, using `'./node_modules/some-package/index.js'` instead `'some-package'`, you save extra request/readFile for looking the filename. ## API ### NodeJs version ```js /** * The `Require` class handles modular dynamic loading, * dependency resolution, and cyclic dependency detection. */ class Require { /** * Stores the contents of all loaded modules. * Each entry maps a module path to its content and children (dependencies). * @type {Object<string, { content: string, children: string[] }>} * @static */ static contents = {}; /** * List of global plugins to process module contents. * Plugins are functions that modify a module's content or children, without replacing the object reference. * For example, a plugin can transform `module.content` using `module.content.replace(...)`. * @type {Array<Function>} * @static */ static plugins = []; /** * Flag to enable or disable cyclic dependency. * When enabled, it allows loading modules that depend on each other recursively. * @type {boolean} * @static */ static cyclicDependencies = false; /** * Logger for warnings and error messages. * Defaults to the global `console` object but can be overridden with a custom logger. * @type {Console} * @static */ static logger = console; /** * Retrieves a module by its path and options. * This is a shortcut for instantiating a `Require` object and calling its `fn` method. * @param {string} path - Path to the module. * @param {Object} [options] - Options for the module loading. * @param {string} [options.scriptBefore] - Code to prepend before the module's execution. * @param {string} [options.scriptAfter] - Code to append after the module's execution. * @param {string[]} [options.parameters] - Parameters to pass into the generated function. * @param {Function[]} [options.plugins] - Array of plugin functions. * @param {boolean} [options.cyclicDependencies] - Whether to allow cyclic dependencies. * @param {Console} [options.logger] - Custom logger for warnings and errors. * @returns {Function} - The dynamically generated function for the module. * @static */ static getModule(path, options = {}) { // Creates a new Require instance and calls its `fn` method to generate the module's function. return new Require(path, options).fn(options); } /** * Creates a `Require` instance for managing a specific module and its dependencies. * The constructor handles loading the module's contents, resolving dependencies, and applying plugins. * @param {string} path - Path to the root module to be loaded. * @param {Object} [options] - Options for module handling. * @param {Function[]} [options.plugins] - Plugins to modify module content or structure. * @param {boolean} [options.cyclicDependencies] - Whether to detect cyclic dependencies. * @param {Console} [options.logger] - Custom logger for warnings and errors. */ constructor(path, options = {}) { const { plugins = [], cyclicDependencies = Require.cyclicDependencies, logger = Require.logger, } = options; // Merge global plugins with instance-specific plugins. const combinedPlugins = [...Require.plugins, ...plugins].filter(p => typeof p === 'function'); this.contents = {}; // Stores the current module's contents and dependencies. this.path = path; // Path to the root module. this.fullPath // will include full path for module // In NodeJs Require loads the module's contents and applies plugins. this.keys // Keys are the paths of all loaded modules, sorted in reverse order. } /** * Generates a function for the module that incorporates options for execution. * The function includes dynamically generated code that can be executed with custom parameters. * @param {Object} [options] - Options for function generation. * @param {string} [options.scriptBefore] - Code to prepend before the module's execution. * @param {string} [options.scriptAfter] - Code to append after the module's execution. * @param {string[]} [options.parameters] - Parameters to pass into the generated function. * @returns {Function} - The dynamically generated function for the module. */ fn(options = {}) {} /** * Generates a string representation of the function for the module. * Useful for embedding the module in scripts or HTML, with an optional function name. * @param {Object} [options] - Options for function generation. * @param {string} [options.scriptBefore] - Code to prepend before the module's execution. * @param {string} [options.scriptAfter] - Code to append after the module's execution. * @param {string[]} [options.parameters] - Parameters to pass into the generated function. * @param {string} [options.name] - If presented, replaces the anonymous function name with the provided name. * @returns {string} - The string representation of the function. */ stringFn(options = {}) {} } ``` ### Browser version ```js /** * The `Require` class provides modular loading, dependency resolution, * cyclic dependency detection, and fetch-based loading for both server and browser environments. */ class Require { /** * A list of standard Node.js modules that are automatically excluded from fetching. * @type {string[]} * @static */ static standartNodeModules = [...]; /** * Stores the contents of all loaded modules. * Each entry maps a module path to its content and dependencies. * @type {Object<string, { content: string, children: string[] }>} * @static */ static contents = {}; /** * List of global plugins to process module contents. * Plugins are functions that modify a module's content or structure. * @type {Array<Function>} * @static */ static plugins = []; /** * Flag to enable or disable cyclic dependency detection. * @type {boolean} * @static */ static cyclicDependencies = false; /** * Logger for warnings and error messages. * Default is the `console` object but can be replaced with a custom logger. * @type {Console} * @static */ static logger = console; /** * Version string to append to fetched module URLs for cache-busting. * @type {string | undefined} * @static */ static version; /** * Checks if a cyclic dependency exists between two modules and throws an error if detected. * @param {string} fullPath - The full path of the dependent module. * @param {string} path - The path of the current module. * @throws {Error} Throws an error if a cyclic dependency is detected and cyclicDependencies=false. * @static */ static isCyclyc(fullPath, path) {} /** * Fetches a resource from a given path and returns its content. * Supports different response types such as 'text', 'json', etc. * @param {string} path - The URL or path to fetch. * @param {string} [type='text'] - The type of the response (e.g., 'text', 'json'). * @returns {Promise<any>} Resolves with the fetched content in the specified format. * @static */ static async fetch(path, type = 'text') {} /** * Asynchronously loads a module and its dependencies, returning a function for execution. * @param {string} path - Path to the module. * @param {Object} [options] - Options for module loading. * @returns {Promise<Function>} Resolves to the dynamically generated function for the module. * @static */ static async getModule(path, options = {}) {} /** * Creates a `Require` instance for managing a specific module. * The constructor initializes the module's path, content readiness state, and options. * @param {string} path - Path to the root module to be loaded. * @param {Object} [options] - Options for module handling. */ constructor(path, options = {}) { this.contents = {}; // Stores the current module's contents and dependencies. this.path = path; // Path to the root module. this.fullPath; // Resolves the full path based on the current location. this.contentReady = false; // Indicates if the module's content has been fully loaded. this.options = options; // Custom options for module handling. } /** * Asynchronously loads the module's content and dependencies. * Applies plugins and detects cyclic dependencies if enabled. * @param {Object} [options] - Options for content loading. * @returns {Promise<Require>} Resolves to the current instance after loading content. */ async getContent(options = {}) {} /** * Generates a function for the module that incorporates execution options. * The function includes dynamically generated code that can be executed with custom parameters. * @param {Object} [options] - Options for function generation. * @returns {Function} - The dynamically generated function for the module. */ fn(options = {}) {} } module.exports = Require; ```