Skip to content

ryardley/indexr

Repository files navigation

Indexr

Coverage Status Coverage Status

Dynamic index module boilerplate creator for your Node or client packaged ES6 submodules. Indexr overcomes some of the limits of ES6 modules by autogenerating module index boilerplate code usually as part of a precompilation stage in your build process.

##Contents

Background

When creating great applications it is important to provide systems that allow features to be implemented and maintained as discrete units of code.

It is also important to avoid magic runtime binding where functionality is implied by folder structure or some other magic variables or constants.

ES6 modules have come along and they are a great way to organise JavaScript code. They are declarative and statically analysable. Very importantly what you see is what you get.

However they lack the ability to autoload modules on the fly based on a dynamic folder structure such as the kind you would see in a modular framework that needs to bind dynamic packages of code together.

It is for this reason I created the Indexr open source project.

What Indexr does is it takes a folder, searches for any 'module' folders based on globs you provide and generates index files that export all the 'submodules' within the 'module' folders in whatever way you choose.

Let's look at an example:

$ indexr .  \
  --out reducers.js \
  --modules **/modules/ \
  --submodules */reducer.js \
  --direct-import
  --named-exports

This example does the following:

  1. Use the current folder as a root folder
  2. Find module folders nested within this root folder that have the name modules
  3. Within each modules folder find subfolder that contains an reducer.js file
  4. Create an ES6 reducers.js file within each 'modules' folder that directly imports the submodule's reducer.js file.
  5. Export the reducers as named exports.

You can also do the same thing via Node API

import indexr from 'indexr';
indexr(__dirname, {
  outputFilename: 'reducers.js',
  modules: '**/modules/',
  submodules: '*/reducer.js',
  directImport: true,
  namedExports: true,
});

http://github.com/ryardley/indexr

Installation

Install locally and use the node API or use indexr in npm scripts (recommended).

# better
npm install indexr --save

Tip Try adding ./node_modules/.bin to your path for your terminal. That way you can get access to local cli programs as you enter your npm projects.

# ~/.bash_profile
...
PATH=$PATH:./node_modules/.bin
...

Alternatively if you have to you can install globally and use indexr in the bash prompt.

# You probably don't want to do this
npm install indexr -g

Usage

You can use indexr as either a command-line program or a node API.

Assuming we have a folder tree like this:

./modules
 ├── bar
 ├── baz
 └── foo

We run this in the root:

$ indexr .

Within any subfolder it finds called 'modules' you will find it will create a file called ./index.r.js that contains the following:

/**
  This file is autogenerated by indexr.
  Check this file into source control.
  Do not edit this file.
  For more information: http://github.com/ryardley/indexr
**/
import bar from './bar';
import baz from './baz';
import foo from './foo';
export default [
  bar,
  baz,
  foo
];
/** End autogenerated content **/

Alternatively if we run this:

$ indexr . --named-exports

Within any subfolder it finds called 'modules' you will find it will create a file called ./index.r.js that contains the following index with named exports:

/**
  This file is autogenerated by indexr.
  Check this file into source control.
  Do not edit this file.
  For more information: http://github.com/ryardley/indexr
**/
export { default as bar } from './bar';
export { default as baz } from './baz';
export { default as foo } from './foo';
/** End autogenerated content **/

CLI Usage

For help with the commandline program you can try the help flag:

$ indexr --help

  Usage: indexr <rootFolder> [options]

  Options:

    -h, --help                       output usage information
    -V, --version                    output the version number
    -e --ext <string>                Remove this extension from imports.
    -o --out <filename>              The name of the output file.
    -d --direct-import               Directly import files as opposed to folders.
    -m --modules <string>            Glob string that determine which folders hold modules.
    -i --modules-ignore <string>     Glob string that determine which folders are ignored.
    -5 --es5                         Use ES5 template for index output.
    -n --named-exports               Use named exports instead of arrays.
    -s --submodules <string>         Glob string that determine what is a submodule.
    -g --submodules-ignore <string>  Glob string that determine which submodules are ignored.
    -w --watch [string]              Files to watch as a glob string.

Syntax Example

NOTE: All commandline globs must be enclosed in quotes!!

The following example will look in the ./app folder for modules folders identified by '**/modules/' and then identify submodules given by '*/server.js' and use them to write a file to ./app/modules/server.js.

$ indexr ./app --out 'server.js' --modules '**/modules/' --submodules '*/server.js'

Change the modules folder

We can change the glob used to find the modules folder which is useful if you don't want to have your modules under 'modules'.

$ indexr . --modules '**/features/'

Change the output filename

We can change the output filename of the file indexr produces.

$ indexr . --out 'index.js'

Change what qualifies as a module.

We can also filter which modules and entry files we want by setting some options. the following will only include modules which contain reducer.js files.

$ indexr . --submodules '*/reducer.js'

ES5 module output

$ indexr . --es5

If the es5 flag is set Indexr can export the template in es5/common js style.

/**
  This file is autogenerated by indexr.
  Check this file into source control.
  Do not edit this file.
  For more information: http://github.com/ryardley/indexr
**/
var foo = require('./foo');
var bar = require('./bar');
var baz = require('./baz');
module.exports = [foo, bar, baz];
/** End autogenerated content **/

Use named Exports.

By using the --named-exports flag it will export the submodules as named exports:

$ indexr . --named-exports
export { default as bar } from './bar';
export { default as baz } from './baz';
export { default as foo } from './foo';

Including the globbed files.

By using the --direct-import flag it will include the searched files specifically in the import statements:

$ indexr . --submodules '*/server.js' --direct-import
import foo from './foo/server.js';
import bar from './bar/server.js';
export default [foo, bar];

File Watching

You can watch files using the --watch flag:

$ indexr . --watch

Node API

Signature

indexr(rootFolder:String, options?:Object):Promise

Arguments

argument notes
rootFolder The root folder to work from.
options An object containing configuration options

Available options

option default notes
directImport false Include the searched files in the import statements.
es5 false Boolean flag to use es5 commonjs style modules over es6. This is overridden if a template function
exts [] Remove this extension from the imported files. A usefull example might be ['js'] which you would use if you would prefer to import ./foo/server instead of ./foo/server.js
modules '\*\*/modules/' A glob or array of globs pathed to the rootFolder that will determine which folders are module holders. If this is ommitted defaults to **/modules/.
modulesIgnore undefined A glob pathed to the rootFolder that will determine which folders are not module holders. If this is ommitted nothing is ignored.
namedExports false This flag will ensure that indexes use named exports instead of arrays.
submodules '\*/' A glob pathed to each module holder folder that will determine which submodules are imported to the index. Defaults to */index.js
submodulesIgnore undefined A glob pathed to the rootFolder that will determine which folders are not considered submodules. If this is ommitted nothing is ignored.
template indexr's es6 template A template function the function should takes an array of relative module paths and output the module file as a string
outputFilename 'index.r.js' The name of the output file. This file will be added to each module folder.
watch false Either a boolean value or a glob that represents files for chokdir to watch.

Example

Here is an example

import indexr from 'indexr';
import es6 from 'indexr/dist/modules/template/es6'; // This will change don't do this.

indexr(__dirname, {
  es5: false,
  modules: '**/modules/',
  submodules: '*/index.js',
  directImport: true,
  exts: ['js', 'jsx'],
  namedExports: false,
  outputFilename: 'index.js',
  template: es6, // or some function that takes an array of module paths and spits out a template
  watch: false,
})
.then((err, result) => {
  console.log('Files have been indexed!');
});

NOTE: dont load the template from the dist file as it's location may change. This is only for illustration purposes.

Contributing

Start the gulp watcher.

$ gulp

Run the tests

$ npm run test-watch

Edit the code.

Feedback?

  1. Submit an issue

Found a bug?

  1. Submit an issue or
  2. Submit a pull request!

If anything is unclear or wrong in these docs please let me know by submitting an issue