├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .madrun.js ├── .npmignore ├── .nycrc.json ├── .putout.json ├── .travis.yml ├── ChangeLog ├── LICENSE ├── README.md ├── fixture ├── context.js ├── default-frozen-function.mjs └── default-frozen-object.mjs ├── package.json ├── simport.js ├── simport.mjs ├── simport.spec.mjs └── workflows └── nodejs.yml /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "parserOptions": { 4 | "requireConfigFile": false 5 | }, 6 | "rules": { 7 | "node/no-unsupported-features/es-syntax": "off" 8 | }, 9 | "extends": [ 10 | "plugin:node/recommended", 11 | "plugin:putout/recommended" 12 | ], 13 | "plugins": [ 14 | "node", 15 | "putout" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | - **Version** (`redrun -v`): 7 | - **Node Version** `node -v`: 8 | - **OS** (`uname -a` on Linux): 9 | - **Browser name/version**: 10 | - **Used Command Line Parameters**: 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | - [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide") 7 | - [ ] `npm run codestyle` is OK 8 | - [ ] `npm test` is OK 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | coverage 4 | 5 | *.swp 6 | .nyc_output 7 | yarn-error.log 8 | .idea 9 | -------------------------------------------------------------------------------- /.madrun.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {run} = require('madrun'); 4 | 5 | module.exports = { 6 | 'test': () => 'tape *.spec.mjs', 7 | 'coverage': () => 'c8 npm test', 8 | 'lint': () => 'putout .', 9 | 'fresh:lint': () => run('lint', '--fresh'), 10 | 'lint:fresh': () => run('lint', '--fresh'), 11 | 'fix:lint': () => run('lint', '--fix'), 12 | 'report': () => 'c8 report --reporter=text-lcov | coveralls', 13 | 'watcher': () => 'nodemon -w test -w lib --exec', 14 | 'watch:test': async () => await run('watcher', `"${await run('test')}"`), 15 | 'watch:lint': async () => await run('watcher', `'npm run lint'`), 16 | 'watch:tape': () => 'nodemon -w test -w lib --exec tape', 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.swp 3 | *.spec.mjs 4 | 5 | yarn-error.log 6 | coverage 7 | 8 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-coverage": true, 3 | "all": true, 4 | "exclude": [ 5 | ".*", 6 | "*.spec.*", 7 | "fixture" 8 | ], 9 | "branches": 100, 10 | "lines": 100, 11 | "functions": 100, 12 | "statements": 100 13 | } 14 | -------------------------------------------------------------------------------- /.putout.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "nodejs/convert-dirname-to-url": "off" 4 | } 5 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 15 4 | - 14 5 | cache: false 6 | script: 7 | - npm run lint 8 | - npm run coverage && npm run report 9 | notifications: 10 | email: 11 | on_success: never 12 | on_failure: change 13 | sudo: false 14 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2021.04.20, v1.2.0 2 | feature: 3 | - (simport) add support of function constructors 4 | - (package) eslint-plugin-putout v7.4.0 5 | - (package) supertape v5.1.0 6 | - (package) putout v16.7.0 7 | 8 | 9 | 2021.01.25, v1.1.3 10 | 11 | fix: 12 | - (simport) default is frozen object (#1) 13 | - (simport) default is frozen function (#1) 14 | 15 | 16 | 2021.01.25, v1.1.2 17 | 18 | feature: 19 | - (simport) add importAbsolute 20 | 21 | 22 | 2021.01.23, v1.1.1 23 | 24 | fix: 25 | - (simport) namespaced imports 26 | 27 | 28 | 2021.01.22, v1.1.0 29 | 30 | feature: 31 | - (simport) improve windows support 32 | 33 | 34 | 2021.01.21, v1.0.7 35 | 36 | fix: 37 | - (simport) json support 38 | 39 | feature: 40 | - (package) putout v13.8.0 41 | 42 | 43 | 2021.01.20, v1.0.6 44 | 45 | fix: 46 | - (simport) ability to handle windows paths (coderaiser/cloudcmd#317) 47 | 48 | 49 | 2021.01.12, v1.0.5 50 | 51 | fix: 52 | - (simport) url processing 53 | 54 | 55 | 2020.12.28, v1.0.4 56 | 57 | fix: 58 | - (package) main: super-impomrt -> simport 59 | 60 | 61 | 2020.12.25, v1.0.3 62 | 63 | fix: 64 | - (simport) createSimport 65 | 66 | 67 | 2020.12.25, v1.0.2 68 | 69 | fix: 70 | - (package) rm lib 71 | 72 | 73 | 2020.12.25, v1.0.1 74 | 75 | feature: 76 | - (simport) add readjson 77 | 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) coderaiser 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 | # Simport [![License][LicenseIMGURL]][LicenseURL] [![NPM version][NPMIMGURL]][NPMURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Coverage Status][CoverageIMGURL]][CoverageURL] 2 | 3 | Use [dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports) just like plain old [require](https://nodejs.org/api/esm.html#esm_require). 4 | 5 | With simport you can: 6 | 7 | - get `require` 8 | - get `__filename` or `__dirname` 9 | - load json 10 | - avoid extensions 11 | - avoid destructuring default 12 | - pass `simport` into functions like [tryToCatch](https://github.com/coderaiser/try-to-catch) 13 | - use [absolute path in windows](https://github.com/nodejs/node/issues/31710#issuecomment-587434048) 14 | 15 | ## Install 16 | 17 | `npm i simport` 18 | 19 | ## API 20 | 21 | ### createSimport 22 | 23 | Commonjs: 24 | 25 | ```js 26 | const {createSimport} = require('simport'); 27 | const simport = createSimport(__filename); 28 | ``` 29 | 30 | ESM: 31 | 32 | ```js 33 | import {createSimport} from 'simport'; 34 | const simport = createSimport(import.meta.url); 35 | 36 | // you can import json 37 | await simport('./package.json'); 38 | // returns 39 | ({ 40 | name: simport, 41 | }); 42 | 43 | // you can avoid .js extension 44 | await simport('./server'); 45 | 46 | // you can avoid destructure default 47 | const validate = await simport('./validate'); 48 | // same as 49 | const {default: validate2} = await import('./validate.js'); 50 | ``` 51 | 52 | ### createCommons 53 | 54 | ```js 55 | import {createCommons} from 'simport'; 56 | 57 | const { 58 | __filename, 59 | __dirname, 60 | require, 61 | } = createCommons(import.meta.url); 62 | 63 | // now you have plain old CommonJS variables 64 | ``` 65 | 66 | ## License 67 | 68 | MIT 69 | 70 | [NPMIMGURL]: https://img.shields.io/npm/v/simport.svg?style=flat 71 | [BuildStatusIMGURL]: https://travis-ci.com/coderaiser/simport.svg?branch=master 72 | [LicenseIMGURL]: https://img.shields.io/badge/license-MIT-317BF9.svg?style=flat 73 | [NPMURL]: https://npmjs.org/package/simport "npm" 74 | [BuildStatusURL]: https://travis-ci.com/coderaiser/simport "Build Status" 75 | [LicenseURL]: https://tldrlegal.com/license/mit-license "MIT License" 76 | [CoverageURL]: https://coveralls.io/github/coderaiser/simport?branch=master 77 | [CoverageIMGURL]: https://coveralls.io/repos/coderaiser/simport/badge.svg?branch=master&service=github 78 | -------------------------------------------------------------------------------- /fixture/context.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return this.Compiler; 3 | } 4 | -------------------------------------------------------------------------------- /fixture/default-frozen-function.mjs: -------------------------------------------------------------------------------- 1 | export default Object.freeze((arg) => arg); 2 | export const bar = "baz"; 3 | -------------------------------------------------------------------------------- /fixture/default-frozen-object.mjs: -------------------------------------------------------------------------------- 1 | export default Object.freeze(Object.create(null)); 2 | export const foo = 123; 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simport", 3 | "version": "1.2.0", 4 | "type": "commonjs", 5 | "author": "coderaiser (https://github.com/coderaiser)", 6 | "description": "import like require but async", 7 | "homepage": "http://github.com/coderaiser/simport", 8 | "main": "simport.js", 9 | "exports": { 10 | ".": { 11 | "node": { 12 | "require": "./simport.js", 13 | "import": "./simport.mjs" 14 | }, 15 | "default": "./simiport.js" 16 | } 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/coderaiser/simport.git" 21 | }, 22 | "keywords": [ 23 | "dynamic", 24 | "import", 25 | "require", 26 | "commonjs", 27 | "esm" 28 | ], 29 | "scripts": { 30 | "test": "madrun test", 31 | "coverage": "madrun coverage", 32 | "lint": "madrun lint", 33 | "fix:lint": "madrun fix:lint", 34 | "report": "madrun report", 35 | "watcher": "madrun watcher", 36 | "watch:test": "madrun watch:test", 37 | "watch:lint": "madrun watch:lint", 38 | "watch:tape": "madrun watch:tape", 39 | "watch:coverage:base": "madrun watch:coverage:base", 40 | "watch:coverage:tape": "madrun watch:coverage:tape", 41 | "watch:coverage": "madrun watch:coverage" 42 | }, 43 | "dependencies": { 44 | "readjson": "^2.2.0", 45 | "try-to-catch": "^3.0.0", 46 | "import-meta-resolve": "^1.1.1" 47 | }, 48 | "license": "MIT", 49 | "devDependencies": { 50 | "@cloudcmd/stub": "^3.3.0", 51 | "c8": "^7.3.5", 52 | "coveralls": "^3.0.2", 53 | "eslint": "^8.4.1", 54 | "eslint-plugin-node": "^11.0.0", 55 | "eslint-plugin-putout": "^13.0.1", 56 | "madrun": "^8.2.0", 57 | "nodemon": "^2.0.2", 58 | "putout": "^24.1.0", 59 | "supertape": "^6.10.0" 60 | }, 61 | "engines": { 62 | "node": ">=12.2" 63 | }, 64 | "publishConfig": { 65 | "access": "public" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /simport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {pathToFileURL} = require('url'); 4 | const readjson = require('readjson'); 5 | const tryToCatch = require('try-to-catch'); 6 | 7 | const {assign} = Object; 8 | const isFn = (a) => typeof a === 'function'; 9 | const isObject = (a) => typeof a === 'object'; 10 | 11 | const maybeFrozenFunction = (a) => !isFn(a) ? a : function(...args) { 12 | return a.apply(this, args); 13 | }; 14 | 15 | const maybeFrozenObject = (a) => !isObject(a) ? a : assign({}, a); 16 | 17 | const importWithExt = async (a, ext = '') => await import(`${a}${ext}`); 18 | const extensions = [ 19 | '.js', 20 | '.cjs', 21 | '.mjs', 22 | ]; 23 | 24 | module.exports.createSimport = (url) => { 25 | if (!url.includes('file://')) 26 | url = pathToFileURL(url); 27 | 28 | return async (name) => { 29 | let resolved = name; 30 | const isRelative = /^\./.test(name); 31 | 32 | if (isRelative) { 33 | resolved = new URL(name, url); 34 | } 35 | 36 | if (/\.json$/.test(resolved)) 37 | return await readjson(resolved); 38 | 39 | if (/\.(js|mjs|cjs)$/.test(name)) { 40 | const processed = resolved.href || `file://${resolved}`; 41 | const imported = await import(processed); 42 | 43 | return buildExports(imported); 44 | } 45 | 46 | let imported; 47 | let error; 48 | 49 | if (/^[@a-z]/.test(name)) { 50 | imported = await importWithExt(resolved); 51 | } 52 | 53 | if (!imported) 54 | [error, imported] = await importAbsolute(resolved); 55 | 56 | if (error) 57 | throw error; 58 | 59 | return buildExports(imported); 60 | }; 61 | }; 62 | 63 | async function importAbsolute(resolved) { 64 | let error; 65 | let imported; 66 | 67 | for (const ext of extensions) { 68 | [error, imported] = await tryToCatch(importWithExt, resolved, ext); 69 | 70 | if (imported) 71 | break; 72 | } 73 | 74 | return [error, imported]; 75 | } 76 | 77 | function buildExports(imported) { 78 | let {default: exports = {}} = imported; 79 | 80 | exports = maybeFrozenFunction(exports); 81 | exports = maybeFrozenObject(exports); 82 | 83 | return assign(exports, imported); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /simport.mjs: -------------------------------------------------------------------------------- 1 | import {createRequire} from 'module'; 2 | import {fileURLToPath} from 'url'; 3 | import {dirname} from 'path'; 4 | 5 | import {createSimport} from './simport.js'; 6 | 7 | export { 8 | createSimport, 9 | }; 10 | 11 | export const createCommons = (url) => { 12 | const require = createRequire(url); 13 | const __filename = fileURLToPath(url); 14 | const __dirname = dirname(__filename); 15 | 16 | return { 17 | require, 18 | __filename, 19 | __dirname, 20 | }; 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /simport.spec.mjs: -------------------------------------------------------------------------------- 1 | import {join} from 'path'; 2 | import {fileURLToPath} from 'url'; 3 | import {dirname} from 'path'; 4 | 5 | import tryToCatch from 'try-to-catch'; 6 | 7 | import {createSimport} from './simport.js'; 8 | import {createCommons} from './simport.mjs'; 9 | 10 | import test from 'supertape'; 11 | 12 | const {url} = import.meta; 13 | 14 | const simport = createSimport(url); 15 | 16 | test('simport: default', async (t) => { 17 | const data = await import('supertape'); 18 | 19 | t.equal(typeof data.default, 'function'); 20 | t.end(); 21 | }); 22 | 23 | test('simport: named', async (t) => { 24 | const {stub} = await import('supertape'); 25 | const {stub: superStub} = await simport('supertape'); 26 | 27 | t.equal(stub, superStub); 28 | t.end(); 29 | }); 30 | 31 | test('simport: json', async (t) => { 32 | const json = await simport('./package.json'); 33 | 34 | t.equal(json.name, 'simport'); 35 | t.end(); 36 | }); 37 | 38 | test('simport: json: not relative', async (t) => { 39 | const {__dirname} = createCommons(url); 40 | const json = await simport(join(__dirname, 'package.json')); 41 | 42 | t.equal(json.name, 'simport'); 43 | t.end(); 44 | }); 45 | 46 | test('simport: no extension', async (t) => { 47 | const imported = await simport('./simport'); 48 | 49 | t.ok(imported, createSimport); 50 | t.end(); 51 | }); 52 | 53 | test('simport: createCommons: __filename', async (t) => { 54 | const {createCommons} = await simport('./simport.mjs'); 55 | const {__filename} = createCommons(url); 56 | 57 | t.equal(__filename, fileURLToPath(url)); 58 | t.end(); 59 | }); 60 | 61 | test('simport: createCommons: __dirname', async (t) => { 62 | const {createCommons} = await simport('./simport.mjs'); 63 | const {__filename, __dirname} = createCommons(url); 64 | 65 | t.equal(__dirname, dirname(__filename)); 66 | t.end(); 67 | }); 68 | 69 | test('simport: createCommons: require', async (t) => { 70 | const {createCommons} = await simport('./simport.mjs'); 71 | const {require} = createCommons(url); 72 | const {name} = require('./package'); 73 | 74 | t.equal(name, 'simport'); 75 | t.end(); 76 | }); 77 | 78 | test('simport: file name', async (t) => { 79 | const {__filename} = createCommons(url); 80 | const simport = createSimport(__filename); 81 | 82 | const result = await simport('./simport.mjs'); 83 | 84 | t.equal(result.createSimport, createSimport); 85 | t.end(); 86 | }); 87 | 88 | test('simport: not found', async (t) => { 89 | const {__filename} = createCommons(url); 90 | const simport = createSimport(__filename); 91 | 92 | const [error] = await tryToCatch(simport, './simport1'); 93 | 94 | t.ok(error, error.message); 95 | t.end(); 96 | }); 97 | 98 | test('simport: absolute', async (t) => { 99 | const {__filename} = createCommons(url); 100 | const simport = createSimport(__filename); 101 | 102 | const [error] = await tryToCatch(simport, __filename); 103 | 104 | t.notOk(error); 105 | t.end(); 106 | }); 107 | 108 | test('simport: external', async (t) => { 109 | const {__filename} = createCommons(url); 110 | const simport = createSimport(__filename); 111 | 112 | const [error] = await tryToCatch(simport, '@cloudcmd/stub'); 113 | 114 | t.notOk(error); 115 | t.end(); 116 | }); 117 | 118 | test('simport: default frozen function', async (t) => { 119 | const {__filename, __dirname} = createCommons(url); 120 | const simport = createSimport(__filename); 121 | const fixture = join(__dirname, 'fixture', 'default-frozen-function'); 122 | const [error] = await tryToCatch(simport, fixture); 123 | 124 | t.notOk(error); 125 | t.end(); 126 | }); 127 | 128 | test('simport: default frozen object', async (t) => { 129 | const {__filename, __dirname} = createCommons(url); 130 | const fixture = join(__dirname, 'fixture', 'default-frozen-object'); 131 | const simport = createSimport(__filename); 132 | 133 | const [error] = await tryToCatch(simport, fixture); 134 | 135 | t.notOk(error); 136 | t.end(); 137 | }); 138 | 139 | test('simport: context', async (t) => { 140 | const {__filename, __dirname} = createCommons(url); 141 | const simport = createSimport(__filename); 142 | const fixture = join(__dirname, 'fixture', 'context'); 143 | const [, stringify] = await tryToCatch(simport, fixture); 144 | 145 | const result = stringify.call({ 146 | Compiler: 'hello', 147 | }); 148 | 149 | t.equal(result, 'hello'); 150 | t.end(); 151 | }); 152 | -------------------------------------------------------------------------------- /workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 'on': 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: 11 | - 14.x 12 | - 16.x 13 | - 17.x 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: Install Redrun 21 | run: npm i redrun -g 22 | - name: Install 23 | run: npm install -f 24 | - name: Bootstrap 25 | run: redrun bootstrap 26 | - name: Lint 27 | run: redrun lint 28 | - name: Coverage 29 | run: redrun coverage report 30 | - name: Coveralls 31 | uses: coverallsapp/github-action@master 32 | with: 33 | github-token: ${{ secrets.GITHUB_TOKEN }} 34 | --------------------------------------------------------------------------------