├── .babelrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── lerna.json ├── package.json └── packages ├── babel-plugin-polypackage ├── package.json └── src │ └── index.js ├── demo ├── .babelrc ├── .gitignore ├── config │ └── rollup.config.js ├── package.json └── src │ ├── bar.js │ ├── foo.js │ └── index.js ├── polypackage-cli ├── bin │ └── cli.js ├── get-rollup-babel-config.js ├── index.js └── package.json └── polypackage-core ├── package.json └── src ├── generate-commonjs-entry.js ├── index.js ├── is-valid-export.js ├── parse-polypackage-entry.js ├── stringify-entry-ast.js └── test └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | npm-debug.log.* 4 | lerna-debug.log 5 | dist/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - 4 5 | - 6 6 | before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi 7 | script: 8 | - npm run bootstrap 9 | - npm run test 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ryan Tsao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # polypackage 2 | [![build status][build-badge]][build-href] 3 | [![npm version][npm-badge]][npm-href] 4 | 5 | **npm packages done right.** Use ES2015+ in your package source and get single npm package optimized for both ES Module and CommonJS consumers, including Weback, Browserify, Rollup, and Node.js. Includes progressive enhancements for tree-shaking bundlers (e.g. Webpack 2 and Rollup) to [reduce module overhead](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/). 6 | 7 | ### Features 8 | * Universal granular imports for consumers lacking a tree-shaking bundler 9 | * **Idiomatic CommonJS consumption** 10 | * No `require('my-package').default` 11 | * Consumption of individual submodules **without exposing transpilation paths** 12 | * `require('my-package/submodule')` rather than `require('my-package/lib/submodule)` 13 | * Granular ES2015+ consumption of submodules akin to CommonJS when not using a tree-shaking bundler 14 | * `import submodule from 'my-package/submodule'` without tree shaking 15 | * Future-proof idiomatic ES2015+ consumption via destructured imports, i.e. `import {submodule} from 'my-package'` 16 | * **Progressive enhacement for consumers using a tree-shaking bundler** via a 17 | * Rolled-up "module" entry file enables use of idiomatic ES2015+ consumption via destructured imports with less module overhead 18 | * **Support for `npm link` and Lerna** 19 | 20 | Polypackage is loosely based on the "poly-packages" idea proposed in https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md#poly-packages 21 | 22 | ## What it looks like 23 | 24 | #### src/index.js 25 | ```js 26 | export {default as foo} from './foo'; 27 | export {bar, baz} from './other/submodule'; 28 | ``` 29 | 30 | Now consumers of your package can do any of the following: 31 | 32 | #### CommonJS (idiomatic submodules) 33 | No referencing `lib/` or `dist/`! No `.default` is needed! 34 | ```js 35 | const foo = require('my-package/foo'); 36 | const bar = require('my-package/bar'); 37 | ``` 38 | 39 | #### CommonJS (entire module) 40 | No `.default` is needed! 41 | ```js 42 | const {foo, bar, baz} = require('my-package'); 43 | ``` 44 | 45 | #### ES Modules (idiomatic destructuring imports) 46 | With a tree-shaking bundler, you can do this without a potential bundle size penalty. Additionally, because the bundler will resolve a Rolled-up ES module, there's even [less module overhead](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/)! 47 | ```js 48 | import {foo, bar} from 'my-package'; 49 | ``` 50 | 51 | #### ES Modules (CommonJS-style submodule imports) 52 | Without a tree-shaking bundler, this might be preferred 53 | ```js 54 | import foo from 'my-package/foo'; 55 | import bar from 'my-package/bar'; 56 | ``` 57 | 58 | ## How it works 59 | 60 | 1. Create a polypackage "index" entry with ES2015 export syntax. 61 | - This file must conform to the following format: 62 | - Must only contain named export declarations (no other kind of statements are allowed) 63 | - All exports must be named (no default exports) and have a source 64 | - This ensures a bijective mapping between named exports and CommonJS submodules 65 | 2. Set the package.json "main" field to `dist/index.js` and "module" field to `dist-es/index.js` 66 | 3. Add `/*.js` to your gitignore, which ignores the generated files at your package root 67 | 4. A CommonJS submodule entry will be created at the root for any named export 68 | 69 | #### Package structure: 70 | 71 | * package.json - (`"main": "dist/index.js"` and `"module": "dist-es/index.js"`) 72 | * .gitignore - (`dist/`, `dist-es/`, `/*.js`) 73 | * .npmignore - (`src/`, `.*`) 74 | * src/ 75 | * index.js 76 | * foo.js 77 | * other/ 78 | * bar.js 79 | 80 | #### Resulting polypackage: 81 | 82 | * **foo.js** 83 | * **bar.js** 84 | * *package.json* 85 | * *.gitignore* 86 | * *.npmignore* 87 | * **dist/** 88 | * **index.js** 89 | * **other/** 90 | * **bar.js** 91 | * **dist-es/** 92 | * **index.js** - *(Rolled up ES Module)* 93 | * *src/* 94 | * *index.js* 95 | * *foo.js* 96 | * *other/* 97 | * *bar.js* 98 | 99 | 100 | 101 | [build-badge]: https://travis-ci.org/rtsao/polypackage.svg?branch=master 102 | [build-href]: https://travis-ci.org/rtsao/polypackage 103 | [npm-badge]: https://badge.fury.io/js/polypackage-core.svg 104 | [npm-href]: https://www.npmjs.com/package/polypackage-core 105 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-beta.30", 3 | "version": "2.0.1-beta.1" 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polypackage-monorepo", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Polypackage monorepo", 6 | "main": "index.js", 7 | "scripts": { 8 | "pretest": "lerna run transpile", 9 | "test": "lerna run test", 10 | "bootstrap": "lerna bootstrap", 11 | "clean": "lerna clean" 12 | }, 13 | "author": "Ryan Tsao ", 14 | "devDependencies": { 15 | "babel-preset-es2015": "^6.14.0", 16 | "lerna": "2.0.0-beta.30", 17 | "tape": "^4.6.2" 18 | }, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /packages/babel-plugin-polypackage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-polypackage", 3 | "version": "2.0.1-beta.0", 4 | "description": "Babel plugin for polypackage", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "transpile": "babel src -d dist", 8 | "prepublish": "npm run transpile" 9 | }, 10 | "dependencies": { 11 | "polypackage-core": "^2.0.1-beta.0" 12 | }, 13 | "author": "Ryan Tsao ", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "babel-cli": "^6.14.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/babel-plugin-polypackage/src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const polypackage = require('polypackage-core'); 4 | 5 | module.exports = function() { 6 | return { 7 | visitor: { 8 | Program: function({node}, state) { 9 | const rootModule = state.opts.rootModule || 'src/index.js'; 10 | if (state.file.opts.filename !== rootModule) { 11 | // ignore everything but polypackage entry 12 | return false; 13 | } 14 | const files = polypackage(node.body, { 15 | dirname: state.opts.transpileDir, 16 | preserveCase: state.opts.preserveCase 17 | }); 18 | Object.keys(files).forEach(filename => { 19 | const content = files[filename]; 20 | if (!state.opts.silent) { 21 | console.log('polypackage:', filename); 22 | } 23 | fs.writeFileSync(filename, content, 'utf8'); 24 | }); 25 | } 26 | } 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/demo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["polypackage"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | dist-es/ 4 | /*.js 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /packages/demo/config/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | 3 | export default { 4 | entry: 'src/index.js', 5 | dest: 'dist-es/index.js', 6 | format: 'es', 7 | plugins: [ 8 | babel({ 9 | babelrc: false, 10 | presets: [ 11 | ['babel-preset-es2015', { 12 | modules: false 13 | }] 14 | ], 15 | plugins: ['external-helpers'] 16 | }) 17 | ] 18 | }; 19 | -------------------------------------------------------------------------------- /packages/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-package", 3 | "version": "2.0.1-beta.0", 4 | "module": "src/index.js", 5 | "scripts": { 6 | "transpile": "babel src -d dist/", 7 | "rollup": "rollup -c config/rollup.config.js", 8 | "prepublish": "npm run transpile && npm run rollup" 9 | }, 10 | "files": [ 11 | "dist", 12 | "dist-es" 13 | ], 14 | "devDependencies": { 15 | "babel-cli": "^6.11.4", 16 | "babel-plugin-external-helpers": "^6.8.0", 17 | "babel-plugin-polypackage": "^2.0.1-beta.0", 18 | "babel-preset-es2015": "^6.13.2", 19 | "rollup": "^0.34.10", 20 | "rollup-plugin-babel": "^2.6.1" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /packages/demo/src/bar.js: -------------------------------------------------------------------------------- 1 | export default 'Bar'; 2 | 3 | export const hello = 'Hello world'; 4 | -------------------------------------------------------------------------------- /packages/demo/src/foo.js: -------------------------------------------------------------------------------- 1 | export function foo(arg) { 2 | return `foo ${arg}`; 3 | } 4 | -------------------------------------------------------------------------------- /packages/demo/src/index.js: -------------------------------------------------------------------------------- 1 | export {foo} from './foo'; 2 | export {default as bar, hello} from './bar'; 3 | export {default as external, other as somethingElse} from 'external-module'; 4 | -------------------------------------------------------------------------------- /packages/polypackage-cli/bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../index.js'); 4 | -------------------------------------------------------------------------------- /packages/polypackage-cli/get-rollup-babel-config.js: -------------------------------------------------------------------------------- 1 | const {OptionManager} = require('babel-core'); 2 | 3 | const commonJSPlugin = OptionManager.normalisePlugin( 4 | require('babel-plugin-transform-es2015-modules-commonjs') 5 | ); 6 | 7 | module.exports = function getBabelConfig(filepath) { 8 | const instance = new OptionManager(); 9 | instance.init({filename: filepath}); 10 | 11 | const options = instance.options; 12 | 13 | // attempt to find index of commonJSPlugin 14 | let indices = []; 15 | options.plugins.forEach((p, i) => { 16 | if (p[0] === commonJSPlugin) { 17 | indices.push(i); 18 | } 19 | }); 20 | indices.forEach(index => { 21 | // delete index 22 | options.plugins.splice(index, 1); 23 | }); 24 | 25 | return { 26 | plugins: options.plugins, 27 | // ignore .babelrc files 28 | babelrc: false 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /packages/polypackage-cli/index.js: -------------------------------------------------------------------------------- 1 | const {spawn} = require('child_process'); 2 | const rollup = require('rollup'); 3 | const path = require('path'); 4 | const minimist = require('minimist'); 5 | const getConfig = require('./get-rollup-babel-config'); 6 | const babel = require('rollup-plugin-babel'); 7 | const resolveBin = require('resolve-bin'); 8 | const readPkgUp = require('read-pkg-up'); 9 | 10 | const babelCli = resolveBin.sync('babel-cli', {executable: 'babel'}); 11 | 12 | const args = process.argv.slice(2); 13 | 14 | const optsWhitelist = { 15 | 'out-dir': true, 16 | 'd': true, 17 | 'copy-files': true, 18 | '_': true 19 | }; 20 | 21 | const opts = minimist(args, { 22 | // NOTE: this is incomplete 23 | alias: { 24 | 'd': 'out-dir' 25 | } 26 | }); 27 | 28 | Object.keys(opts).forEach(name => { 29 | if (!optsWhitelist[name]) { 30 | throw new Error(`Error: Babel option ${name} not yet supported`); 31 | } 32 | }); 33 | 34 | if (opts._.length < 1) { 35 | throw new Error('Babel src argument is required'); 36 | } 37 | 38 | const src = opts._[0]; 39 | 40 | const info = readPkgUp.sync({cwd: path.dirname(src)}); 41 | const pkg = info.pkg; 42 | const pkgDir = path.dirname(info.path); 43 | const destDir = path.resolve(opts['out-dir']); 44 | const pkgMainDestDir = path.resolve(pkgDir, path.dirname(pkg.main)); 45 | 46 | if (pkgMainDestDir !== destDir) { 47 | throw new Error(`Babel out dir: ${opts['out-dir']} does not correspond to package.json main entry: ${pkg.main}`); 48 | } 49 | 50 | if (!pkg.module) { 51 | throw new Error('no module field in package.json!'); 52 | } 53 | 54 | const srcEntryPath = path.resolve(src, path.basename(pkg.main)); 55 | 56 | spawn(babelCli, args, {stdio: 'inherit'}); 57 | 58 | rollup.rollup({ 59 | entry: srcEntryPath, 60 | plugins: [ 61 | babel(getConfig(path.resolve(src))) 62 | ] 63 | }).then(bundle => { 64 | bundle.write({ 65 | format: 'es', 66 | dest: path.resolve(pkg.module) 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/polypackage-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polypackage-cli", 3 | "version": "2.0.1-beta.1", 4 | "description": "", 5 | "bin": "bin/cli.js", 6 | "scripts": {}, 7 | "author": "Ryan Tsao ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "babel-core": "6.x", 11 | "minimist": "^1.2.0", 12 | "read-pkg-up": "^2.0.0", 13 | "rollup": "^0.36.3", 14 | "rollup-plugin-babel": "^2.6.1" 15 | }, 16 | "peerDependencies": { 17 | "babel-cli": "6.x" 18 | }, 19 | "devDependencies": { 20 | "babel-cli": "^6.11.4", 21 | "babylon": "^6.11.4" 22 | }, 23 | "engines": { 24 | "node": ">=4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/polypackage-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polypackage-core", 3 | "version": "2.0.1-beta.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "pretest": "npm run transpile", 8 | "test": "node dist/test/index.js", 9 | "transpile": "babel src -d dist", 10 | "prepublish": "npm run transpile" 11 | }, 12 | "author": "Ryan Tsao ", 13 | "license": "MIT", 14 | "dependencies": { 15 | "babel-core": "^6.13.2", 16 | "param-case": "^2.1.0" 17 | }, 18 | "devDependencies": { 19 | "babel-cli": "^6.11.4", 20 | "babylon": "^6.11.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/generate-commonjs-entry.js: -------------------------------------------------------------------------------- 1 | module.exports = generateEntry; 2 | 3 | /** 4 | * Generates CommonJS entries from a polypackage-compliant "main" entry 5 | * @param {ExportNamedDeclaration} declaration - Source declaration AST 6 | * @param {function} pathTransformer - Function that return new path 7 | * @return {ExpressionStatement} - AST for CommonJS entry export 8 | */ 9 | function generateEntry({exportedName, localName, source}) { 10 | return { 11 | type: 'ExpressionStatement', 12 | expression: { 13 | type: 'AssignmentExpression', 14 | operator: '=', 15 | left: { 16 | type: 'MemberExpression', 17 | object: { 18 | type: 'Identifier', 19 | name: 'module' 20 | }, 21 | property: { 22 | type: 'Identifier', 23 | name: 'exports' 24 | } 25 | }, 26 | right: { 27 | type: 'MemberExpression', 28 | object: { 29 | type: 'CallExpression', 30 | callee: { 31 | type: 'Identifier', 32 | name: 'require' 33 | }, 34 | arguments: [ 35 | { 36 | type: 'StringLiteral', 37 | value: source 38 | } 39 | ] 40 | }, 41 | property: { 42 | type: 'Identifier', 43 | name: localName 44 | } 45 | } 46 | } 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const parseMain = require('./parse-polypackage-entry'); 3 | const generateCommonJsEntry = require('./generate-commonjs-entry'); 4 | const stringify = require('./stringify-entry-ast'); 5 | const kebab = require('param-case'); 6 | 7 | module.exports = moduleAstToCommonJs; 8 | 9 | function moduleAstToCommonJs(body, opts = {}) { 10 | const exported = parseMain(body); 11 | // transformers 12 | const files = Object.keys(exported).reduce((acc, exportedName) => { 13 | const filename = getFilename(exportedName, opts.preserveCase); 14 | if (acc[filename]) { 15 | // TODO: Friendlier errors 16 | throw Error('commonJS filenames must be unique'); 17 | } 18 | const {localName, source} = exported[exportedName]; 19 | const ast = generateCommonJsEntry({ 20 | source: getTranspiledPath(source, opts.transpileDir), 21 | exportedName, 22 | localName 23 | }); 24 | acc[filename] = stringify(ast); 25 | return acc; 26 | }, {}); 27 | return files; 28 | } 29 | 30 | function getFilename(identifier, preserveCase = false) { 31 | const name = preserveCase ? identifier : kebab(identifier); 32 | return `${name}.js`; 33 | } 34 | 35 | function getTranspiledPath(str, transpileDir = 'dist') { 36 | return pathIsRelative(str) ? `./${path.join(transpileDir, str)}` : str; 37 | } 38 | 39 | /** 40 | * Returns whether path is relative or not 41 | * Adapted from: https://github.com/nodejs/node/blob/55852e14212f81b3849508e7e8862b3077316a99/lib/module.js#L305 42 | * @param {string} request - The path 43 | * @return {boolean} - Whether path is relative or not 44 | */ 45 | function pathIsRelative(request) { 46 | return ( 47 | !(request.length < 2 || 48 | request.charCodeAt(0) !== 46/*.*/ || 49 | (request.charCodeAt(1) !== 46/*.*/ && 50 | request.charCodeAt(1) !== 47/*/*/)) 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/is-valid-export.js: -------------------------------------------------------------------------------- 1 | module.exports = isValidExport; 2 | 3 | function isValidExport(item) { 4 | // only support non-declaration named exports 5 | if ( 6 | // support only named exports 7 | item.type !== 'ExportNamedDeclaration' || 8 | // do not support declaration exports 9 | item.declaration || 10 | // export must have a source 11 | !item.source 12 | ) { 13 | return false; 14 | } 15 | // disallow default exports 16 | return item.specifiers.every(specifier => specifier.exported.name !== 'default'); 17 | } 18 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/parse-polypackage-entry.js: -------------------------------------------------------------------------------- 1 | const isValidExport = require('./is-valid-export'); 2 | 3 | module.exports = parseMain; 4 | 5 | function parseMain(body) { 6 | const exportsMap = body.reduce((acc, statement) => { 7 | if (!isValidExport(statement)) { 8 | // TODO: output line number 9 | throw Error('not a valid export'); 10 | } 11 | const source = statement.source.value; 12 | statement.specifiers.forEach(specifier => { 13 | const exportedName = specifier.exported.name; 14 | if (acc[exportedName]) { 15 | // TODO output duplicate name 16 | throw Error('named exports must be unique'); 17 | } 18 | acc[exportedName] = { 19 | localName: specifier.local.name, 20 | source 21 | }; 22 | }); 23 | return acc; 24 | }, {}); 25 | return exportsMap; 26 | } 27 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/stringify-entry-ast.js: -------------------------------------------------------------------------------- 1 | const babel = require('babel-core'); 2 | 3 | module.exports = stringifyCommonJSEntry; 4 | 5 | function stringifyCommonJSEntry(exportStatement) { 6 | return babel.transformFromAst({ 7 | type: 'File', 8 | program: { 9 | type: 'Program', 10 | sourceType: 'module', 11 | body: [exportStatement] 12 | } 13 | }).code; 14 | } 15 | -------------------------------------------------------------------------------- /packages/polypackage-core/src/test/index.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const babylon = require('babylon'); 3 | 4 | function parse(src) { 5 | return babylon.parse(src, { 6 | sourceType: 'module' 7 | }).program.body; 8 | } 9 | 10 | const core = require('../index.js'); 11 | 12 | test('basic functionality', t => { 13 | const ast = parse(` 14 | export {foo} from './foo'; 15 | export {bar} from 'bar'; 16 | export {baz, qux} from 'baz'`); 17 | const entries = core(ast); 18 | const filenames = Object.keys(entries); 19 | t.equal(filenames.length, 4); 20 | t.equal(filenames[0], 'foo.js'); 21 | t.equal(entries[filenames[0]], 'module.exports = require("./dist/foo").foo;'); 22 | t.equal(filenames[1], 'bar.js'); 23 | t.equal(entries[filenames[1]], 'module.exports = require("bar").bar;'); 24 | t.equal(filenames[2], 'baz.js'); 25 | t.equal(entries[filenames[2]], 'module.exports = require("baz").baz;'); 26 | t.equal(filenames[3], 'qux.js'); 27 | t.equal(entries[filenames[3]], 'module.exports = require("baz").qux;'); 28 | t.end(); 29 | }); 30 | --------------------------------------------------------------------------------