├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ ├── dev.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── lib ├── extension.js ├── normalize.js └── register.js ├── package.json └── test ├── extension.test.js ├── fixtures ├── .testrc ├── cwd │ └── test.foo ├── folder.with.dots │ └── test.stub ├── test.s └── test.stub ├── index.js └── require-stub.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gulp", 3 | "rules": { 4 | "max-statements": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: dev 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | env: 9 | CI: true 10 | 11 | jobs: 12 | prettier: 13 | name: Format code 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event_name == 'push' }} 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Prettier 22 | uses: gulpjs/prettier_action@v3.0 23 | with: 24 | commit_message: 'chore: Run prettier' 25 | prettier_options: '--write .' 26 | 27 | test: 28 | name: Tests for Node ${{ matrix.node }} on ${{ matrix.os }} 29 | runs-on: ${{ matrix.os }} 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | node: [10, 12, 14, 16] 35 | os: [ubuntu-latest, windows-latest, macos-latest] 36 | 37 | steps: 38 | - name: Clone repository 39 | uses: actions/checkout@v2 40 | 41 | - name: Set Node.js version 42 | uses: actions/setup-node@v2 43 | with: 44 | node-version: ${{ matrix.node }} 45 | 46 | - run: node --version 47 | - run: npm --version 48 | 49 | - name: Install npm dependencies 50 | run: npm install 51 | 52 | - name: Run lint 53 | run: npm run lint 54 | 55 | - name: Run tests 56 | run: npm test 57 | 58 | - name: Coveralls 59 | uses: coverallsapp/github-action@v1.1.2 60 | with: 61 | github-token: ${{ secrets.GITHUB_TOKEN }} 62 | flag-name: ${{matrix.os}}-node-${{ matrix.node }} 63 | parallel: true 64 | 65 | coveralls: 66 | needs: test 67 | name: Finish up 68 | 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Coveralls Finished 72 | uses: coverallsapp/github-action@v1.1.2 73 | with: 74 | github-token: ${{ secrets.GITHUB_TOKEN }} 75 | parallel-finished: true 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | 8 | jobs: 9 | release-please: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: GoogleCloudPlatform/release-please-action@v2 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | release-type: node 16 | package-name: release-please-action 17 | bump-minor-pre-major: true 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Garbage files 64 | .DS_Store 65 | 66 | # Test results 67 | test.xunit 68 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.8.0](https://www.github.com/gulpjs/rechoir/compare/v0.7.1...v0.8.0) (2021-07-24) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Normalize repository, dropping node <10.13 support (#40) 9 | 10 | ### Miscellaneous Chores 11 | 12 | * Normalize repository, dropping node <10.13 support ([#40](https://www.github.com/gulpjs/rechoir/issues/40)) ([00f5968](https://www.github.com/gulpjs/rechoir/commit/00f59689d0eb9668d939a85e06428a0906587a6f)) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019, 2021 Tyler Kellen , Blaine Bublitz , and Eric Schoffstall 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 |

2 | 3 | 4 | 5 |

6 | 7 | # rechoir 8 | 9 | [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url] 10 | 11 | Prepare a node environment to require files with different extensions. 12 | 13 | ## What is it? 14 | 15 | This module, in conjunction with [interpret]-like objects, can register any filetype the npm ecosystem has a module loader for. This library is a dependency of [liftoff]. 16 | 17 | **Note:** While `rechoir` will automatically load and register transpilers like `coffee-script`, you must provide a local installation. The transpilers are **not** bundled with this module. 18 | 19 | ## Usage 20 | 21 | ```js 22 | const config = require('interpret').extensions; 23 | const rechoir = require('rechoir'); 24 | rechoir.prepare(config, './test/fixtures/test.coffee'); 25 | rechoir.prepare(config, './test/fixtures/test.csv'); 26 | rechoir.prepare(config, './test/fixtures/test.toml'); 27 | 28 | console.log(require('./test/fixtures/test.coffee')); 29 | console.log(require('./test/fixtures/test.csv')); 30 | console.log(require('./test/fixtures/test.toml')); 31 | ``` 32 | 33 | ## API 34 | 35 | ### `prepare(config, filepath, [cwd], [noThrow])` 36 | 37 | Look for a module loader associated with the provided file and attempt require it. If necessary, run any setup required to inject it into [require.extensions]. 38 | 39 | `config` An [interpret]-like configuration object. 40 | 41 | `filepath` A file whose type you'd like to register a module loader for. 42 | 43 | `cwd` An optional path to start searching for the module required to load the requested file. Defaults to the directory of `filepath`. 44 | 45 | `noThrow` An optional boolean indicating if the method should avoid throwing. 46 | 47 | If calling this method is successful (e.g. it doesn't throw), you can now require files of the type you requested natively. 48 | 49 | An error with a `failures` property will be thrown if the module loader(s) configured for a given extension cannot be registered. 50 | 51 | If a loader is already registered, this will simply return `true`. 52 | 53 | ## License 54 | 55 | MIT 56 | 57 | 58 | [downloads-image]: https://img.shields.io/npm/dm/rechoir.svg?style=flat-square 59 | [npm-url]: https://www.npmjs.com/package/rechoir 60 | [npm-image]: https://img.shields.io/npm/v/rechoir.svg?style=flat-square 61 | 62 | [ci-url]: https://github.com/gulpjs/rechoir/actions?query=workflow:dev 63 | [ci-image]: https://img.shields.io/github/workflow/status/gulpjs/rechoir/dev?style=flat-square 64 | 65 | [coveralls-url]: https://coveralls.io/r/gulpjs/rechoir 66 | [coveralls-image]: https://img.shields.io/coveralls/gulpjs/rechoir/master.svg 67 | 68 | 69 | 70 | [interpret]: https://github.com/gulpjs/interpret 71 | [require.extensions]: https://nodejs.org/api/modules.html#modules_require_extensions 72 | [liftoff]: https://github.com/js-cli/js-liftoff 73 | 74 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var extension = require('./lib/extension'); 4 | var normalize = require('./lib/normalize'); 5 | var register = require('./lib/register'); 6 | 7 | exports.prepare = function (extensions, filepath, cwd, nothrow) { 8 | var config, usedExtension, err, option, attempt, error; 9 | var attempts = []; 10 | var onlyErrors = true; 11 | var exts = extension(filepath); 12 | 13 | if (exts) { 14 | exts.some(function (ext) { 15 | usedExtension = ext; 16 | config = normalize(extensions[ext]); 17 | return !!config; 18 | }); 19 | } 20 | 21 | if (Object.keys(require.extensions).indexOf(usedExtension) !== -1) { 22 | return true; 23 | } 24 | 25 | if (!config) { 26 | if (nothrow) { 27 | return; 28 | } 29 | 30 | throw new Error('No module loader found for "' + usedExtension + '".'); 31 | } 32 | 33 | if (!cwd) { 34 | cwd = path.dirname(path.resolve(filepath)); 35 | } 36 | if (!Array.isArray(config)) { 37 | config = [config]; 38 | } 39 | 40 | for (var i in config) { 41 | option = config[i]; 42 | attempt = register(cwd, option.module, option.register); 43 | error = attempt instanceof Error ? attempt : null; 44 | if (error) { 45 | attempt = null; 46 | } 47 | attempts.push({ 48 | moduleName: option.module, 49 | module: attempt, 50 | error: error, 51 | }); 52 | if (!error) { 53 | onlyErrors = false; 54 | break; 55 | } 56 | } 57 | if (onlyErrors) { 58 | err = new Error( 59 | 'Unable to use specified module loaders for "' + usedExtension + '".' 60 | ); 61 | err.failures = attempts; 62 | if (nothrow) { 63 | return err; 64 | } 65 | 66 | throw err; 67 | } 68 | return attempts; 69 | }; 70 | -------------------------------------------------------------------------------- /lib/extension.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | function getLongExtension(basename) { 6 | if (basename[basename.length - 1] === '.') { 7 | return null; 8 | } 9 | 10 | var startIndex = basename[0] === '.' ? 1 : 0; 11 | 12 | var dotIndex = basename.indexOf('.', startIndex); 13 | if (dotIndex <= startIndex) { 14 | return null; 15 | } 16 | 17 | return basename.slice(dotIndex); 18 | } 19 | 20 | function getPossibleExtensions(longExtension) { 21 | var arr = [longExtension]; 22 | var len = longExtension.length; 23 | var startIndex = 1; 24 | 25 | while (startIndex < len) { 26 | var dotIndex = longExtension.indexOf('.', startIndex); 27 | if (dotIndex < 0) { 28 | break; 29 | } 30 | arr.push(longExtension.slice(dotIndex)); 31 | startIndex = dotIndex + 1; 32 | } 33 | 34 | return arr; 35 | } 36 | 37 | module.exports = function (input) { 38 | var basename = path.basename(input); 39 | var longExtension = getLongExtension(basename); 40 | if (!longExtension) { 41 | return; 42 | } 43 | return getPossibleExtensions(longExtension); 44 | }; 45 | -------------------------------------------------------------------------------- /lib/normalize.js: -------------------------------------------------------------------------------- 1 | function normalizer(config) { 2 | if (typeof config === 'string') { 3 | return { module: config }; 4 | } 5 | return config; 6 | } 7 | 8 | module.exports = function (config) { 9 | if (Array.isArray(config)) { 10 | return config.map(normalizer); 11 | } 12 | return normalizer(config); 13 | }; 14 | -------------------------------------------------------------------------------- /lib/register.js: -------------------------------------------------------------------------------- 1 | var resolve = require('resolve'); 2 | 3 | module.exports = function (cwd, moduleName, register) { 4 | var result; 5 | try { 6 | var modulePath = resolve.sync(moduleName, { basedir: cwd }); 7 | result = require(modulePath); 8 | if (typeof register === 'function') { 9 | register(result); 10 | } 11 | } catch (e) { 12 | result = e; 13 | } 14 | return result; 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rechoir", 3 | "version": "0.8.0", 4 | "description": "Prepare a node environment to require files with different extensions.", 5 | "author": "Gulp Team (http://gulpjs.com/)", 6 | "contributors": [ 7 | "Blaine Bublitz ", 8 | "Tyler Kellen (http://goingslowly.com/)" 9 | ], 10 | "repository": "gulpjs/rechoir", 11 | "license": "MIT", 12 | "engines": { 13 | "node": ">= 10.13.0" 14 | }, 15 | "main": "index.js", 16 | "files": [ 17 | "LICENSE", 18 | "index.js", 19 | "lib/" 20 | ], 21 | "scripts": { 22 | "lint": "eslint .", 23 | "pretest": "rm -rf tmp/ && npm run lint", 24 | "test": "nyc mocha --async-only" 25 | }, 26 | "dependencies": { 27 | "resolve": "^1.20.0" 28 | }, 29 | "devDependencies": { 30 | "eslint": "^7.21.0", 31 | "eslint-config-gulp": "^5.0.1", 32 | "expect": "^27.0.0", 33 | "mocha": "^8.3.0", 34 | "nyc": "^15.1.0" 35 | }, 36 | "nyc": { 37 | "reporter": [ 38 | "lcov", 39 | "text-summary" 40 | ] 41 | }, 42 | "prettier": { 43 | "singleQuote": true 44 | }, 45 | "keywords": [ 46 | "require", 47 | "loader", 48 | "extension", 49 | "extensions", 50 | "prepare" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /test/extension.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('expect'); 4 | var extension = require('../lib/extension'); 5 | 6 | describe('Take out possible extensions from a file path', function () { 7 | it('should return an extension: ".js" from "app.js"', function (done) { 8 | expect(extension('app.js')).toEqual(['.js']); 9 | expect(extension('a/b/c/app.js')).toEqual(['.js']); 10 | done(); 11 | }); 12 | 13 | it('should return extensions: ".babel.js" and ".js" from "app.babel.js"', function (done) { 14 | expect(extension('app.babel.js')).toEqual(['.babel.js', '.js']); 15 | expect(extension('a/b/c/app.babel.js')).toEqual(['.babel.js', '.js']); 16 | done(); 17 | }); 18 | 19 | it('should return extensions: ".aaa.bbb.ccc", ".aaa.bbb" and ".ccc" from "app.aaa.bbb.ccc"', function (done) { 20 | expect(extension('app.aaa.bbb.ccc')).toEqual([ 21 | '.aaa.bbb.ccc', 22 | '.bbb.ccc', 23 | '.ccc', 24 | ]); 25 | expect(extension('a/b/c/app.aaa.bbb.ccc')).toEqual([ 26 | '.aaa.bbb.ccc', 27 | '.bbb.ccc', 28 | '.ccc', 29 | ]); 30 | done(); 31 | }); 32 | 33 | it('should return an extension: ".j" from "app.j"', function (done) { 34 | expect(extension('app.j')).toEqual(['.j']); 35 | expect(extension('a/b/c/app.j')).toEqual(['.j']); 36 | done(); 37 | }); 38 | 39 | it('should return extensions: ".b.j" and ".j" from "app.b.j"', function (done) { 40 | expect(extension('app.b.j')).toEqual(['.b.j', '.j']); 41 | expect(extension('a/b/c/app.b.j')).toEqual(['.b.j', '.j']); 42 | done(); 43 | }); 44 | 45 | it('should return extensions: ".a.b.c", ".a.b" and ".c" from "app.a.b.c"', function (done) { 46 | expect(extension('app.a.b.c')).toEqual(['.a.b.c', '.b.c', '.c']); 47 | expect(extension('a/b/c/app.a.b.c')).toEqual(['.a.b.c', '.b.c', '.c']); 48 | done(); 49 | }); 50 | 51 | it('should return undefined from "."', function (done) { 52 | expect(extension('.')).toBe(undefined); 53 | expect(extension('a/b/c/.')).toBe(undefined); 54 | done(); 55 | }); 56 | 57 | it('should return undefined from ".."', function (done) { 58 | expect(extension('..')).toBe(undefined); 59 | expect(extension('a/b/c/..')).toBe(undefined); 60 | done(); 61 | }); 62 | 63 | it('should return undefined from "..."', function (done) { 64 | expect(extension('...')).toBe(undefined); 65 | expect(extension('a/b/c/...')).toBe(undefined); 66 | done(); 67 | }); 68 | 69 | it('should return undefined from "a."', function (done) { 70 | expect(extension('a.')).toBe(undefined); 71 | expect(extension('a/b/c/a.')).toBe(undefined); 72 | done(); 73 | }); 74 | 75 | it('should return undefined from "app."', function (done) { 76 | expect(extension('app.')).toBe(undefined); 77 | expect(extension('a/b/c/app.')).toBe(undefined); 78 | done(); 79 | }); 80 | 81 | it('should return undefined from "a.b.c."', function (done) { 82 | expect(extension('a.b.c.')).toBe(undefined); 83 | expect(extension('a/b/c/a.b.c.')).toBe(undefined); 84 | done(); 85 | }); 86 | 87 | it('should return undefined from ".a"', function (done) { 88 | expect(extension('.a')).toBe(undefined); 89 | expect(extension('a/b/c/.a')).toBe(undefined); 90 | done(); 91 | }); 92 | 93 | it('should return undefined from ".app"', function (done) { 94 | expect(extension('.app')).toBe(undefined); 95 | expect(extension('a/b/c/.app')).toBe(undefined); 96 | done(); 97 | }); 98 | 99 | it('should return undefined from ".a."', function (done) { 100 | expect(extension('.a.')).toBe(undefined); 101 | expect(extension('a/b/c/.a.')).toBe(undefined); 102 | done(); 103 | }); 104 | 105 | it('should return undefined from ".app."', function (done) { 106 | expect(extension('.app.')).toBe(undefined); 107 | expect(extension('a/b/c/.app.')).toBe(undefined); 108 | done(); 109 | }); 110 | 111 | it('should return undefined from ".a.b.c."', function (done) { 112 | expect(extension('.a.b.c.')).toBe(undefined); 113 | expect(extension('a/b/c/.a.b.c.')).toBe(undefined); 114 | done(); 115 | }); 116 | 117 | it('should return ".b.c" and ".c" from ".a.b.c"', function (done) { 118 | expect(extension('.a.b.c')).toEqual(['.b.c', '.c']); 119 | expect(extension('a/b/c/.a.b.c')).toEqual(['.b.c', '.c']); 120 | done(); 121 | }); 122 | 123 | it('should return ".bb.cc" and ".cc" from ".aa.bb.cc"', function (done) { 124 | expect(extension('.aa.bb.cc')).toEqual(['.bb.cc', '.cc']); 125 | expect(extension('a/b/c/.aa.bb.cc')).toEqual(['.bb.cc', '.cc']); 126 | done(); 127 | }); 128 | 129 | it('should return "..b" and ".b" from ".a..b"', function (done) { 130 | expect(extension('.a..b')).toEqual(['..b', '.b']); 131 | expect(extension('a/b/c/.a..b')).toEqual(['..b', '.b']); 132 | done(); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /test/fixtures/.testrc: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | trueKey: true, 4 | falseKey: false, 5 | subKey: { 6 | subProp: 1 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/cwd/test.foo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/rechoir/1880cbb697c5027855a60c169202a2d8a03ce739/test/fixtures/cwd/test.foo -------------------------------------------------------------------------------- /test/fixtures/folder.with.dots/test.stub: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | trueKey: true, 4 | falseKey: false, 5 | subKey: { 6 | subProp: 1 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/test.s: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | trueKey: true, 4 | falseKey: false, 5 | subKey: { 6 | subProp: 1 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/test.stub: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | trueKey: true, 4 | falseKey: false, 5 | subKey: { 6 | subProp: 1 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var Module = require('module'); 3 | 4 | var expect = require('expect'); 5 | 6 | var rechoir = require('../'); 7 | var extension = require('../lib/extension'); 8 | var normalize = require('../lib/normalize'); 9 | var register = require('../lib/register'); 10 | 11 | // save the original Module._extensions 12 | var originalExtensions = Object.keys(Module._extensions); 13 | var original = originalExtensions.reduce(function (result, key) { 14 | result[key] = require.extensions[key]; 15 | return result; 16 | }, {}); 17 | // save the original cache keys 18 | var originalCacheKeys = Object.keys(require.cache); 19 | 20 | function cleanupCache(key) { 21 | if (originalCacheKeys.indexOf(key) === -1) { 22 | delete require.cache[key]; 23 | } 24 | } 25 | 26 | function cleanupExtensions(ext) { 27 | if (originalExtensions.indexOf(ext) === -1) { 28 | delete Module._extensions[ext]; 29 | } else { 30 | Module._extensions[ext] = original[ext]; 31 | } 32 | } 33 | 34 | function cleanup(done) { 35 | // restore the require.cache to startup state 36 | Object.keys(require.cache).forEach(cleanupCache); 37 | // restore the original Module._extensions 38 | Object.keys(Module._extensions).forEach(cleanupExtensions); 39 | 40 | done(); 41 | } 42 | 43 | describe('rechoir', function () { 44 | describe('extension', function () { 45 | it('should extract extension from filename/path from the first dot', function (done) { 46 | expect(extension('file.js')[0]).toEqual('.js'); 47 | expect(extension('file.tmp.dot.js')[0]).toEqual('.tmp.dot.js'); 48 | expect(extension('file.tmp.dot.js')[1]).toEqual('.dot.js'); 49 | expect(extension('file.tmp.dot.js')[2]).toEqual('.js'); 50 | expect(extension('relative/path/to/file.js')[0]).toEqual('.js'); 51 | expect(extension('relative/path/to/file.dot.js')[0]).toEqual('.dot.js'); 52 | expect(extension('relative/path/to/file.dot.js')[1]).toEqual('.js'); 53 | expect(extension('relative/path.with.dot/to/file.dot.js')[0]).toEqual( 54 | '.dot.js' 55 | ); 56 | expect(extension('relative/path.with.dot/to/file.dot.js')[1]).toEqual( 57 | '.js' 58 | ); 59 | 60 | done(); 61 | }); 62 | 63 | it('does not match any if the path ends in a dot', function (done) { 64 | expect(extension('file.js.')).toEqual(undefined); 65 | 66 | done(); 67 | }); 68 | 69 | it('treats additional dots as a separate extension', function (done) { 70 | // Double 71 | expect(extension('file.babel..js')).toEqual([ 72 | '.babel..js', 73 | '..js', 74 | '.js', 75 | ]); 76 | expect(extension('file..babel.js')).toEqual([ 77 | '..babel.js', 78 | '.babel.js', 79 | '.js', 80 | ]); 81 | // Triple 82 | expect(extension('file.babel...js')).toEqual([ 83 | '.babel...js', 84 | '...js', 85 | '..js', 86 | '.js', 87 | ]); 88 | expect(extension('file...babel.js')).toEqual([ 89 | '...babel.js', 90 | '..babel.js', 91 | '.babel.js', 92 | '.js', 93 | ]); 94 | 95 | done(); 96 | }); 97 | 98 | it('does not consider a leading dot to be an extension', function (done) { 99 | expect(extension('.config')).toEqual(undefined); 100 | 101 | done(); 102 | }); 103 | }); 104 | 105 | describe('normalize', function () { 106 | it('should convert a string input into array/object format', function (done) { 107 | expect(normalize('foo')).toEqual({ module: 'foo' }); 108 | 109 | done(); 110 | }); 111 | 112 | it('should convert object input into array format', function (done) { 113 | var input = { 114 | module: 'foo', 115 | }; 116 | expect(normalize(input)).toEqual(input); 117 | 118 | done(); 119 | }); 120 | 121 | it('should iterate an array, normalizing each item', function (done) { 122 | var input = [{ module: 'foo' }, 'bar']; 123 | expect(normalize(input)).toEqual([{ module: 'foo' }, { module: 'bar' }]); 124 | 125 | done(); 126 | }); 127 | }); 128 | 129 | describe('register', function () { 130 | it('should return the specified module relative to the provided cwd', function (done) { 131 | expect(register(__dirname, 'expect')).toEqual(expect); 132 | 133 | done(); 134 | }); 135 | 136 | it('should call a register function if provided, passing in the module', function (done) { 137 | register(__dirname, 'expect', function (attempt) { 138 | expect(attempt).toEqual(expect); 139 | }); 140 | 141 | done(); 142 | }); 143 | 144 | it('should return an error if the specified module cannot be registered', function (done) { 145 | expect(register(__dirname, 'whatev')).toBeInstanceOf(Error); 146 | 147 | done(); 148 | }); 149 | }); 150 | 151 | describe('prepare', function () { 152 | var testFilePath = path.join(__dirname, 'fixtures', 'test.stub'); 153 | 154 | beforeEach(cleanup); 155 | 156 | it('should throw if extension is unknown', function (done) { 157 | expect(function () { 158 | rechoir.prepare({}, './test/fixtures/test.whatever'); 159 | }).toThrow(/No module loader found for/); 160 | 161 | done(); 162 | }); 163 | 164 | it('should return undefined if an unknown extension is specified when nothrow is enabled', function (done) { 165 | expect( 166 | rechoir.prepare({}, './test/fixtures/.testrc', null, true) 167 | ).toEqual(undefined); 168 | 169 | done(); 170 | }); 171 | 172 | it('should throw if a module loader cannot be found or loaded', function (done) { 173 | expect(function () { 174 | rechoir.prepare( 175 | { 176 | '.stub': ['nothere'], 177 | }, 178 | testFilePath 179 | ); 180 | require(testFilePath); 181 | }).toThrow(); 182 | 183 | done(); 184 | }); 185 | 186 | describe('all module loaders that were attempted failed to load', function () { 187 | var exts = { 188 | '.stub': ['nothere', 'orhere'], 189 | }; 190 | 191 | // Check the failure entries in the thrown or returned error object. 192 | function checkFailures(e) { 193 | expect(e.failures).toBeInstanceOf(Array); 194 | expect(e.failures[0].error).toBeInstanceOf(Error); 195 | expect(e.failures[0].moduleName).toEqual('nothere'); 196 | expect(e.failures[0].module).toEqual(null); 197 | expect(e.failures[1].error).toBeInstanceOf(Error); 198 | expect(e.failures[1].moduleName).toEqual('orhere'); 199 | expect(e.failures[1].module).toEqual(null); 200 | } 201 | 202 | it('should throw error listing each module loader that was attempted when nothrow = false', function (done) { 203 | var err; 204 | try { 205 | rechoir.prepare(exts, testFilePath); 206 | } catch (e) { 207 | err = e; 208 | checkFailures(e); 209 | } 210 | expect(err).toBeInstanceOf(Error); 211 | 212 | done(); 213 | }); 214 | 215 | it('should return error listing each module loader that was attempted when nothrow = true', function (done) { 216 | checkFailures(rechoir.prepare(exts, testFilePath, null, true)); 217 | 218 | done(); 219 | }); 220 | }); 221 | 222 | it('should register a module loader for the specified extension', function (done) { 223 | rechoir.prepare( 224 | { 225 | '.stub': ['nothere', '../require-stub'], 226 | }, 227 | testFilePath 228 | ); 229 | expect(function () { 230 | require(testFilePath); 231 | }).not.toThrow(Error); 232 | 233 | done(); 234 | }); 235 | 236 | it('should return true if the module loader for the specified extension is already available', function (done) { 237 | rechoir.prepare( 238 | { 239 | '.stub': ['nothere', '../require-stub'], 240 | }, 241 | testFilePath 242 | ); 243 | expect( 244 | rechoir.prepare( 245 | { 246 | '.stub': ['nothere', '../require-stub'], 247 | }, 248 | testFilePath 249 | ) 250 | ).toEqual(true); 251 | 252 | done(); 253 | }); 254 | 255 | it('must not fail on folders with dots', function (done) { 256 | rechoir.prepare( 257 | { '.stub': '../../require-stub' }, 258 | './test/fixtures/folder.with.dots/test.stub' 259 | ); 260 | expect(require('./fixtures/folder.with.dots/test.stub')).toEqual({ 261 | data: { 262 | trueKey: true, 263 | falseKey: false, 264 | subKey: { 265 | subProp: 1, 266 | }, 267 | }, 268 | }); 269 | 270 | done(); 271 | }); 272 | 273 | it('should register a module loader even if the extension is single character (issue #38)', function (done) { 274 | var fpath = path.join(__dirname, 'fixtures', 'test.s'); 275 | rechoir.prepare( 276 | { 277 | '.s': ['nothere', '../require-stub'], 278 | }, 279 | fpath 280 | ); 281 | 282 | expect(function () { 283 | require(fpath); 284 | }).not.toThrow(Error); 285 | 286 | done(); 287 | }); 288 | 289 | it('should register a module loader for the specified extension with cwd', function (done) { 290 | rechoir.prepare( 291 | { 292 | '.foo': ['nothere', '../require-stub'], 293 | }, 294 | path.join('cwd', 'test.foo'), 295 | path.join(__dirname, 'fixtures') 296 | ); 297 | expect(function () { 298 | require(testFilePath); 299 | }).not.toThrow(Error); 300 | 301 | done(); 302 | }); 303 | }); 304 | }); 305 | -------------------------------------------------------------------------------- /test/require-stub.js: -------------------------------------------------------------------------------- 1 | require.extensions['.stub'] = require.extensions['.js']; 2 | --------------------------------------------------------------------------------