├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── e2e ├── index.js └── modules │ ├── bar.html │ ├── bar.js │ ├── foo.html │ ├── foo.js │ ├── index-absolute.js │ ├── index-module.js │ └── index-relative.js ├── index.js ├── package.json └── test └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | max_line_length = 120 10 | trim_trailing_whitespace = true 11 | 12 | [{*.json,.*rc}] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "spartez" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional eslint cache 42 | .eslintcache 43 | 44 | # Optional REPL history 45 | .node_repl_history 46 | 47 | # Output of 'npm pack' 48 | *.tgz 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '6' 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Module Mapping Webpack Plugin [![Build Status](https://travis-ci.org/spartez/module-mapping-webpack-plugin.svg?branch=master)](http://travis-ci.org/spartez/module-mapping-webpack-plugin) 2 | 3 | This is a [webpack](https://webpack.github.io) plugin for mapping modules onto different files. 4 | 5 | ## Installation 6 | 7 | Install the plugin with npm: 8 | 9 | ```sh 10 | $ npm install module-mapping-webpack-plugin --save-dev 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | // webpack.config.js 17 | const webpack = require('webpack'); 18 | const ModuleMappingPlugin = require('module-mapping-webpack-plugin'); 19 | 20 | module.exports = { 21 | // ... 22 | plugins: [ 23 | new ModuleMappingPlugin({ 24 | './foo.js': './foo-spartez.js', 25 | // ... 26 | }) 27 | ] 28 | }; 29 | 30 | // foo.js 31 | export default () => console.log('Hello World!'); 32 | 33 | // foo-spartez.js 34 | export default () => console.log('Hello Spartez!'); 35 | 36 | // index.js 37 | import foo from './foo'; 38 | foo(); // → 'Hello Spartez!'; 39 | ``` 40 | ## License 41 | 42 | The MIT License 43 | 44 | Copyright :copyright: 2017 Spartez, https://spartez.com 45 | -------------------------------------------------------------------------------- /e2e/index.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import webpack from 'webpack'; 5 | import ModuleMappingPlugin from '../'; 6 | 7 | const outputPath = path.resolve('../dist'); 8 | 9 | async function build(config) { 10 | return new Promise((resolve, reject) => webpack(config, (err, stats) => { 11 | if (err || stats.hasErrors()) { 12 | reject(err || stats.toJson('errors-only') 13 | .errors); 14 | return; 15 | } 16 | resolve(`${config.output.path}/${config.output.filename}`); 17 | })); 18 | } 19 | 20 | function createConfig(entry, pluginConfig) { 21 | return { 22 | entry, 23 | output: { 24 | path: outputPath, 25 | filename: `bundle_${Date.now()}.js`, 26 | library: 'test', 27 | libraryTarget: 'commonjs2' 28 | }, 29 | resolve: { 30 | root: [path.resolve('./modules')] 31 | }, 32 | module: { 33 | loaders: [ 34 | { test: /\.html$/, loader: 'raw' } 35 | ] 36 | }, 37 | plugins: [ 38 | ModuleMappingPlugin(pluginConfig) 39 | ] 40 | }; 41 | } 42 | 43 | function testPathType(pathType) { 44 | 45 | test(`should not map anything when using ${pathType} paths`, async t => { 46 | const bundlePath = await build(createConfig(`./modules/index-${pathType}.js`, {})); 47 | const testModule = require(bundlePath); 48 | t.is(testModule.fn(), 'foo'); 49 | }); 50 | 51 | 52 | test(`should map foo.js to bar.js when using ${pathType} paths`, async t => { 53 | const bundlePath = await build(createConfig(`./modules/index-${pathType}.js`, { 54 | './modules/foo.js': './modules/bar.js' 55 | })); 56 | const testModule = require(bundlePath); 57 | t.is(testModule.fn(), 'bar'); 58 | t.is(testModule.html.trim(), '
foo
'); 59 | }); 60 | 61 | 62 | test(`should map foo.html to bar.html when using ${pathType} paths`, async t => { 63 | const bundlePath = await build(createConfig(`./modules/index-${pathType}.js`, { 64 | './modules/foo.html': './modules/bar.html' 65 | })); 66 | const testModule = require(bundlePath); 67 | t.is(testModule.fn(), 'foo'); 68 | t.is(testModule.html.trim(), '
bar
'); 69 | }); 70 | 71 | 72 | test(`should map both foo.js and foo.html when using ${pathType} paths`, async t => { 73 | const bundlePath = await build(createConfig(`./modules/index-${pathType}.js`, { 74 | './modules/foo.js': './modules/bar.js', 75 | './modules/foo.html': './modules/bar.html' 76 | })); 77 | const testModule = require(bundlePath); 78 | t.is(testModule.fn(), 'bar'); 79 | t.is(testModule.html.trim(), '
bar
'); 80 | }); 81 | 82 | } 83 | 84 | testPathType('relative'); 85 | testPathType('absolute'); 86 | testPathType('module'); 87 | 88 | test.after.always('cleanup dist folder', t => fs.remove(outputPath)); 89 | -------------------------------------------------------------------------------- /e2e/modules/bar.html: -------------------------------------------------------------------------------- 1 |
bar
2 | -------------------------------------------------------------------------------- /e2e/modules/bar.js: -------------------------------------------------------------------------------- 1 | module.exports = () => 'bar'; 2 | -------------------------------------------------------------------------------- /e2e/modules/foo.html: -------------------------------------------------------------------------------- 1 |
foo
2 | -------------------------------------------------------------------------------- /e2e/modules/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = () => 'foo'; 2 | -------------------------------------------------------------------------------- /e2e/modules/index-absolute.js: -------------------------------------------------------------------------------- 1 | module.exports.fn = require(__dirname + '/foo'); 2 | module.exports.html = require(__dirname + '/foo.html'); 3 | -------------------------------------------------------------------------------- /e2e/modules/index-module.js: -------------------------------------------------------------------------------- 1 | module.exports.fn = require('foo'); 2 | module.exports.html = require('foo.html'); 3 | -------------------------------------------------------------------------------- /e2e/modules/index-relative.js: -------------------------------------------------------------------------------- 1 | module.exports.fn = require('./foo'); 2 | module.exports.html = require('./foo.html'); 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | function ModuleMappingPlugin(mappings) { 4 | const absoluteMappings = Object.keys(mappings) 5 | .reduce((current, key) => Object.assign({}, current, { 6 | [path.resolve(key)]: path.resolve(mappings[key]) 7 | }), {}); 8 | 9 | const mappingExists = data => !!absoluteMappings[data.resource]; 10 | 11 | const overrideResource = data => Object.assign({}, data, { 12 | userRequest: absoluteMappings[data.resource], 13 | resource: absoluteMappings[data.resource] 14 | }); 15 | 16 | return { 17 | apply: compiler => { 18 | compiler.plugin('normal-module-factory', moduleFactory => { 19 | moduleFactory.plugin('after-resolve', (data, callback) => { 20 | mappingExists(data) ? callback(null, overrideResource(data)) : callback(null, data); 21 | }); 22 | }); 23 | } 24 | }; 25 | } 26 | 27 | module.exports = ModuleMappingPlugin; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module-mapping-webpack-plugin", 3 | "version": "1.1.1", 4 | "description": "Webpack plugin for mapping modules to different files", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "ava && eslint ." 8 | }, 9 | "keywords": [ 10 | "webpack", 11 | "plugin", 12 | "mapping", 13 | "modules" 14 | ], 15 | "author": "Spartez", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "ava": "^0.16.0", 19 | "eslint-config-spartez": "^1.0.0", 20 | "fs-extra": "^0.30.0", 21 | "raw-loader": "^0.5.1", 22 | "webpack": "^1.13.2" 23 | }, 24 | "ava": { 25 | "files": [ 26 | "test/index.js", 27 | "e2e/index.js" 28 | ], 29 | "verbose": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import path from 'path'; 3 | import ModuleMappingPlugin from '../'; 4 | 5 | function mock(pluginFn) { 6 | return { 7 | plugin: pluginFn 8 | }; 9 | } 10 | 11 | function handleRequest(fn, request) { 12 | return new Promise((resolve, reject) => fn({ 13 | userRequest: request, 14 | resource: request 15 | }, (err, data) => { 16 | if (err) { 17 | reject(err); 18 | } else { 19 | resolve(data); 20 | } 21 | })); 22 | } 23 | 24 | test.cb('should map foo.js to bar.js', t => { 25 | const plugin = ModuleMappingPlugin({ 26 | './foo.js': './bar.js' 27 | }); 28 | 29 | const input = [{ 30 | request: path.resolve('./foo.js'), 31 | expected: path.resolve('./bar.js') 32 | }]; 33 | 34 | const moduleFactory = mock((name, fn) => { 35 | t.is(name, 'after-resolve'); 36 | Promise.all( 37 | input.map(({ request, expected }) => 38 | handleRequest(fn, request) 39 | .then(data => { 40 | t.is(data.userRequest, expected); 41 | t.is(data.resource, expected); 42 | }) 43 | )) 44 | .then(() => t.end()) 45 | .catch(t.end); 46 | }); 47 | 48 | const compiler = mock((name, fn) => { 49 | t.is(name, 'normal-module-factory'); 50 | fn(moduleFactory); 51 | }); 52 | 53 | plugin.apply(compiler); 54 | }); 55 | --------------------------------------------------------------------------------