├── .gitignore ├── .npmignore ├── .prettierrc ├── README.md ├── esbuild.cjs ├── package-lock.json ├── package.json ├── src └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | *~ 41 | 42 | @types 43 | lib 44 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | *~ 41 | 42 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | useTabs: true, 3 | tabWidth: 4, 4 | trailingComma: "es5", 5 | "overrides": [ 6 | { 7 | "files": "*.yml", 8 | "options": { 9 | "tabWidth": 2, 10 | "useTabs": false 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # locreq 2 | 3 | `locreq` is an answer to the problem of [requiring local paths in Node.js](https://gist.github.com/branneman/8048520). It allows you to easily require modules by specifying their paths relative to your project root, not relative to the file they're `require`d from. 4 | 5 | Assume the following directory structure: 6 | 7 | ``` 8 | - lib 9 | - collectionA 10 | - moduleA1.js 11 | - moduleA2.js 12 | - collectionB 13 | - collectionB1 14 | - moduleB1a.js 15 | ``` 16 | 17 | Now, to require module `A2.js` from module `B1a.js`, using regular `require`: 18 | 19 | ```javascript 20 | require("../../collectionA/moduleA2.js"); 21 | ``` 22 | 23 | There are a few problems with the above example: 24 | 25 | * it has a high cognitive overhead; 26 | * when you move the `B1a.js` file, you have to update the argument to `require`; 27 | * it's hard to search for all files that require the `A2` module. 28 | 29 | With `locreq`, it's easier: 30 | 31 | ```javascript 32 | const locreq = require("locreq")(__dirname); 33 | locreq("lib/collectionA/moduleA2.js"); 34 | ``` 35 | 36 | If you have lots of dependencies, `locreq` can really make a difference. 37 | 38 | ## Installation & usage 39 | 40 | To install `locreq`, use: 41 | 42 | ``` 43 | npm install --save locreq 44 | ``` 45 | 46 | To use the module, require it like so: 47 | 48 | ```javascript 49 | const locreq = require("locreq")(__dirname); 50 | ``` 51 | 52 | The `(__dirname);` part is very important, don't forget it! 53 | 54 | Next, simply use `locreq` instead of `require` for your local modules, giving a path relative to the root of your package (that is, relative to the directory where the `package.json` of your project is): 55 | 56 | ```javascript 57 | var moduleA = locreq("lib/my-modules/moduleA.js"); 58 | ``` 59 | 60 | Similar to regular `require`, you can also use the `locreq.resolve` method: 61 | 62 | ``` 63 | const module_path = locreq.resolve("lib/my-modules/moduleA.js"); //returns the absolute path to the module 64 | ``` 65 | 66 | ## How does it work? 67 | 68 | 1. `locreq` goes up the directory hierarchy, parent directory by parent directory 69 | 2. Once it finds a `package.json` it stops the search and treats the directory as the root directory of the package 70 | 3. It then performs a regular `require` on a path that's resolved from combining the package root directory and the path given as an argument, and then returns that. 71 | 72 | ## Advantages 73 | 74 | * **it works even if your package is `require`d by a different package (which is not the case for the `require.main.require` trick)**; 75 | * it doesn't mess with the global scope; 76 | * it doesn't need changes in environment variables; 77 | * it doesn't need any additional start-up scripts; 78 | * it doesn't overwrite the default `require` behavior. 79 | -------------------------------------------------------------------------------- /esbuild.cjs: -------------------------------------------------------------------------------- 1 | const { build } = require("esbuild"); 2 | const glob = require("tiny-glob"); 3 | 4 | const watch = process.argv.at(-1) === "--watch"; 5 | 6 | (async () => { 7 | let entryPoints = Object.fromEntries( 8 | (await glob("./src/**/*.ts")).map((e) => [ 9 | e.replace(/\.ts$/, ""), 10 | e, 11 | ]) 12 | ); 13 | build({ 14 | entryPoints, 15 | sourcemap: true, 16 | outdir: "./lib", 17 | logLevel: "info", 18 | platform: "node", 19 | watch, 20 | target: "node16", 21 | format: "esm", 22 | }); 23 | })(); -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "locreq", 3 | "version": "2.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "locreq", 9 | "version": "2.1.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/node": "^14.14.16", 13 | "esbuild": "0.14.10", 14 | "tiny-glob": "0.2.9", 15 | "ts-node": "^10.9.2", 16 | "typescript": "5.5" 17 | } 18 | }, 19 | "node_modules/@cspotcode/source-map-support": { 20 | "version": "0.8.1", 21 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 22 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 23 | "dependencies": { 24 | "@jridgewell/trace-mapping": "0.3.9" 25 | }, 26 | "engines": { 27 | "node": ">=12" 28 | } 29 | }, 30 | "node_modules/@jridgewell/resolve-uri": { 31 | "version": "3.1.2", 32 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 33 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 34 | "engines": { 35 | "node": ">=6.0.0" 36 | } 37 | }, 38 | "node_modules/@jridgewell/sourcemap-codec": { 39 | "version": "1.5.0", 40 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 41 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" 42 | }, 43 | "node_modules/@jridgewell/trace-mapping": { 44 | "version": "0.3.9", 45 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 46 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 47 | "dependencies": { 48 | "@jridgewell/resolve-uri": "^3.0.3", 49 | "@jridgewell/sourcemap-codec": "^1.4.10" 50 | } 51 | }, 52 | "node_modules/@tsconfig/node10": { 53 | "version": "1.0.11", 54 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", 55 | "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" 56 | }, 57 | "node_modules/@tsconfig/node12": { 58 | "version": "1.0.11", 59 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 60 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" 61 | }, 62 | "node_modules/@tsconfig/node14": { 63 | "version": "1.0.3", 64 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 65 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" 66 | }, 67 | "node_modules/@tsconfig/node16": { 68 | "version": "1.0.4", 69 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 70 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" 71 | }, 72 | "node_modules/@types/node": { 73 | "version": "14.14.16", 74 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", 75 | "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==" 76 | }, 77 | "node_modules/acorn": { 78 | "version": "8.14.0", 79 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 80 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 81 | "bin": { 82 | "acorn": "bin/acorn" 83 | }, 84 | "engines": { 85 | "node": ">=0.4.0" 86 | } 87 | }, 88 | "node_modules/acorn-walk": { 89 | "version": "8.3.4", 90 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", 91 | "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", 92 | "dependencies": { 93 | "acorn": "^8.11.0" 94 | }, 95 | "engines": { 96 | "node": ">=0.4.0" 97 | } 98 | }, 99 | "node_modules/arg": { 100 | "version": "4.1.3", 101 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 102 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 103 | }, 104 | "node_modules/create-require": { 105 | "version": "1.1.1", 106 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 107 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 108 | }, 109 | "node_modules/diff": { 110 | "version": "4.0.2", 111 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 112 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 113 | "engines": { 114 | "node": ">=0.3.1" 115 | } 116 | }, 117 | "node_modules/esbuild": { 118 | "version": "0.14.10", 119 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.10.tgz", 120 | "integrity": "sha512-ibZb+NwFqBwHHJlpnFMtg4aNmVK+LUtYMFC9CuKs6lDCBEvCHpqCFZFEirpqt1jOugwKGx8gALNGvX56lQyfew==", 121 | "hasInstallScript": true, 122 | "bin": { 123 | "esbuild": "bin/esbuild" 124 | }, 125 | "optionalDependencies": { 126 | "esbuild-android-arm64": "0.14.10", 127 | "esbuild-darwin-64": "0.14.10", 128 | "esbuild-darwin-arm64": "0.14.10", 129 | "esbuild-freebsd-64": "0.14.10", 130 | "esbuild-freebsd-arm64": "0.14.10", 131 | "esbuild-linux-32": "0.14.10", 132 | "esbuild-linux-64": "0.14.10", 133 | "esbuild-linux-arm": "0.14.10", 134 | "esbuild-linux-arm64": "0.14.10", 135 | "esbuild-linux-mips64le": "0.14.10", 136 | "esbuild-linux-ppc64le": "0.14.10", 137 | "esbuild-linux-s390x": "0.14.10", 138 | "esbuild-netbsd-64": "0.14.10", 139 | "esbuild-openbsd-64": "0.14.10", 140 | "esbuild-sunos-64": "0.14.10", 141 | "esbuild-windows-32": "0.14.10", 142 | "esbuild-windows-64": "0.14.10", 143 | "esbuild-windows-arm64": "0.14.10" 144 | } 145 | }, 146 | "node_modules/esbuild-android-arm64": { 147 | "version": "0.14.10", 148 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.10.tgz", 149 | "integrity": "sha512-vzkTafHKoiMX4uIN1kBnE/HXYLpNT95EgGanVk6DHGeYgDolU0NBxjO7yZpq4ZGFPOx8384eAdDrBYhO11TAlQ==", 150 | "cpu": [ 151 | "arm64" 152 | ], 153 | "optional": true, 154 | "os": [ 155 | "android" 156 | ] 157 | }, 158 | "node_modules/esbuild-darwin-64": { 159 | "version": "0.14.10", 160 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.10.tgz", 161 | "integrity": "sha512-DJwzFVB95ZV7C3PQbf052WqaUuuMFXJeZJ0LKdnP1w+QOU0rlbKfX0tzuhoS//rOXUj1TFIwRuRsd0FX6skR7A==", 162 | "cpu": [ 163 | "x64" 164 | ], 165 | "optional": true, 166 | "os": [ 167 | "darwin" 168 | ] 169 | }, 170 | "node_modules/esbuild-darwin-arm64": { 171 | "version": "0.14.10", 172 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.10.tgz", 173 | "integrity": "sha512-RNaaoZDg3nsqs5z56vYCjk/VJ76npf752W0rOaCl5lO5TsgV9zecfdYgt7dtUrIx8b7APhVaNYud+tGsDOVC9g==", 174 | "cpu": [ 175 | "arm64" 176 | ], 177 | "optional": true, 178 | "os": [ 179 | "darwin" 180 | ] 181 | }, 182 | "node_modules/esbuild-freebsd-64": { 183 | "version": "0.14.10", 184 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.10.tgz", 185 | "integrity": "sha512-10B3AzW894u6bGZZhWiJOHw1uEHb4AFbUuBdyml1Ht0vIqd+KqWW+iY/yMwQAzILr2WJZqEhbOXRkJtY8aRqOw==", 186 | "cpu": [ 187 | "x64" 188 | ], 189 | "optional": true, 190 | "os": [ 191 | "freebsd" 192 | ] 193 | }, 194 | "node_modules/esbuild-freebsd-arm64": { 195 | "version": "0.14.10", 196 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.10.tgz", 197 | "integrity": "sha512-mSQrKB7UaWvuryBTCo9leOfY2uEUSimAvcKIaUWbk5Hth9Sg+Try+qNA/NibPgs/vHkX0KFo/Rce6RPea+P15g==", 198 | "cpu": [ 199 | "arm64" 200 | ], 201 | "optional": true, 202 | "os": [ 203 | "freebsd" 204 | ] 205 | }, 206 | "node_modules/esbuild-linux-32": { 207 | "version": "0.14.10", 208 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.10.tgz", 209 | "integrity": "sha512-lktF09JgJLZ63ANYHIPdYe339PDuVn19Q/FcGKkXWf+jSPkn5xkYzAabboNGZNUgNqSJ/vY7VrOn6UrBbJjgYA==", 210 | "cpu": [ 211 | "ia32" 212 | ], 213 | "optional": true, 214 | "os": [ 215 | "linux" 216 | ] 217 | }, 218 | "node_modules/esbuild-linux-64": { 219 | "version": "0.14.10", 220 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.10.tgz", 221 | "integrity": "sha512-K+gCQz2oLIIBI8ZM77e9sYD5/DwEpeYCrOQ2SYXx+R4OU2CT9QjJDi4/OpE7ko4AcYMlMW7qrOCuLSgAlEj4Wg==", 222 | "cpu": [ 223 | "x64" 224 | ], 225 | "optional": true, 226 | "os": [ 227 | "linux" 228 | ] 229 | }, 230 | "node_modules/esbuild-linux-arm": { 231 | "version": "0.14.10", 232 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.10.tgz", 233 | "integrity": "sha512-BYa60dZ/KPmNKYxtHa3LSEdfKWHcm/RzP0MjB4AeBPhjS0D6/okhaBesZIY9kVIGDyeenKsJNOmnVt4+dhNnvQ==", 234 | "cpu": [ 235 | "arm" 236 | ], 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ] 241 | }, 242 | "node_modules/esbuild-linux-arm64": { 243 | "version": "0.14.10", 244 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.10.tgz", 245 | "integrity": "sha512-+qocQuQvcp5wo/V+OLXxqHPc+gxHttJEvbU/xrCGE03vIMqraL4wMua8JQx0SWEnJCWP+Nhf//v8OSwz1Xr5kA==", 246 | "cpu": [ 247 | "arm64" 248 | ], 249 | "optional": true, 250 | "os": [ 251 | "linux" 252 | ] 253 | }, 254 | "node_modules/esbuild-linux-mips64le": { 255 | "version": "0.14.10", 256 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.10.tgz", 257 | "integrity": "sha512-nmUd2xoBXpGo4NJCEWoaBj+n4EtDoLEvEYc8Z3aSJrY0Oa6s04czD1flmhd0I/d6QEU8b7GQ9U0g/rtBfhtxBg==", 258 | "cpu": [ 259 | "mips64el" 260 | ], 261 | "optional": true, 262 | "os": [ 263 | "linux" 264 | ] 265 | }, 266 | "node_modules/esbuild-linux-ppc64le": { 267 | "version": "0.14.10", 268 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.10.tgz", 269 | "integrity": "sha512-vsOWZjm0rZix7HSmqwPph9arRVCyPtUpcURdayQDuIhMG2/UxJxpbdRaa//w4zYqcJzAWwuyH2PAlyy0ZNuxqQ==", 270 | "cpu": [ 271 | "ppc64" 272 | ], 273 | "optional": true, 274 | "os": [ 275 | "linux" 276 | ] 277 | }, 278 | "node_modules/esbuild-linux-s390x": { 279 | "version": "0.14.10", 280 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.10.tgz", 281 | "integrity": "sha512-knArKKZm0ypIYWOWyOT7+accVwbVV1LZnl2FWWy05u9Tyv5oqJ2F5+X2Vqe/gqd61enJXQWqoufXopvG3zULOg==", 282 | "cpu": [ 283 | "s390x" 284 | ], 285 | "optional": true, 286 | "os": [ 287 | "linux" 288 | ] 289 | }, 290 | "node_modules/esbuild-netbsd-64": { 291 | "version": "0.14.10", 292 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.10.tgz", 293 | "integrity": "sha512-6Gg8neVcLeyq0yt9bZpReb8ntZ8LBEjthxrcYWVrBElcltnDjIy1hrzsujt0+sC2rL+TlSsE9dzgyuvlDdPp2w==", 294 | "cpu": [ 295 | "x64" 296 | ], 297 | "optional": true, 298 | "os": [ 299 | "netbsd" 300 | ] 301 | }, 302 | "node_modules/esbuild-openbsd-64": { 303 | "version": "0.14.10", 304 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.10.tgz", 305 | "integrity": "sha512-9rkHZzp10zI90CfKbFrwmQjqZaeDmyQ6s9/hvCwRkbOCHuto6RvMYH9ghQpcr5cUxD5OQIA+sHXi0zokRNXjcg==", 306 | "cpu": [ 307 | "x64" 308 | ], 309 | "optional": true, 310 | "os": [ 311 | "openbsd" 312 | ] 313 | }, 314 | "node_modules/esbuild-sunos-64": { 315 | "version": "0.14.10", 316 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.10.tgz", 317 | "integrity": "sha512-mEU+pqkhkhbwpJj5DiN3vL0GUFR/yrL3qj8ER1amIVyRibKbj02VM1QaIuk1sy5DRVIKiFXXgCaHvH3RNWCHIw==", 318 | "cpu": [ 319 | "x64" 320 | ], 321 | "optional": true, 322 | "os": [ 323 | "sunos" 324 | ] 325 | }, 326 | "node_modules/esbuild-windows-32": { 327 | "version": "0.14.10", 328 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.10.tgz", 329 | "integrity": "sha512-Z5DieUL1N6s78dOSdL95KWf8Y89RtPGxIoMF+LEy8ChDsX+pZpz6uAVCn+YaWpqQXO+2TnrcbgBIoprq2Mco1g==", 330 | "cpu": [ 331 | "ia32" 332 | ], 333 | "optional": true, 334 | "os": [ 335 | "win32" 336 | ] 337 | }, 338 | "node_modules/esbuild-windows-64": { 339 | "version": "0.14.10", 340 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.10.tgz", 341 | "integrity": "sha512-LE5Mm62y0Bilu7RDryBhHIX8rK3at5VwJ6IGM3BsASidCfOBTzqcs7Yy0/Vkq39VKeTmy9/66BAfVoZRNznoDw==", 342 | "cpu": [ 343 | "x64" 344 | ], 345 | "optional": true, 346 | "os": [ 347 | "win32" 348 | ] 349 | }, 350 | "node_modules/esbuild-windows-arm64": { 351 | "version": "0.14.10", 352 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.10.tgz", 353 | "integrity": "sha512-OJOyxDtabvcUYTc+O4dR0JMzLBz6G9+gXIHA7Oc5d5Fv1xiYa0nUeo8+W5s2e6ZkPRdIwOseYoL70rZz80S5BA==", 354 | "cpu": [ 355 | "arm64" 356 | ], 357 | "optional": true, 358 | "os": [ 359 | "win32" 360 | ] 361 | }, 362 | "node_modules/globalyzer": { 363 | "version": "0.1.0", 364 | "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", 365 | "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" 366 | }, 367 | "node_modules/globrex": { 368 | "version": "0.1.2", 369 | "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", 370 | "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" 371 | }, 372 | "node_modules/make-error": { 373 | "version": "1.3.6", 374 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 375 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 376 | }, 377 | "node_modules/tiny-glob": { 378 | "version": "0.2.9", 379 | "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", 380 | "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", 381 | "dependencies": { 382 | "globalyzer": "0.1.0", 383 | "globrex": "^0.1.2" 384 | } 385 | }, 386 | "node_modules/ts-node": { 387 | "version": "10.9.2", 388 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 389 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 390 | "dependencies": { 391 | "@cspotcode/source-map-support": "^0.8.0", 392 | "@tsconfig/node10": "^1.0.7", 393 | "@tsconfig/node12": "^1.0.7", 394 | "@tsconfig/node14": "^1.0.0", 395 | "@tsconfig/node16": "^1.0.2", 396 | "acorn": "^8.4.1", 397 | "acorn-walk": "^8.1.1", 398 | "arg": "^4.1.0", 399 | "create-require": "^1.1.0", 400 | "diff": "^4.0.1", 401 | "make-error": "^1.1.1", 402 | "v8-compile-cache-lib": "^3.0.1", 403 | "yn": "3.1.1" 404 | }, 405 | "bin": { 406 | "ts-node": "dist/bin.js", 407 | "ts-node-cwd": "dist/bin-cwd.js", 408 | "ts-node-esm": "dist/bin-esm.js", 409 | "ts-node-script": "dist/bin-script.js", 410 | "ts-node-transpile-only": "dist/bin-transpile.js", 411 | "ts-script": "dist/bin-script-deprecated.js" 412 | }, 413 | "peerDependencies": { 414 | "@swc/core": ">=1.2.50", 415 | "@swc/wasm": ">=1.2.50", 416 | "@types/node": "*", 417 | "typescript": ">=2.7" 418 | }, 419 | "peerDependenciesMeta": { 420 | "@swc/core": { 421 | "optional": true 422 | }, 423 | "@swc/wasm": { 424 | "optional": true 425 | } 426 | } 427 | }, 428 | "node_modules/typescript": { 429 | "version": "5.5.4", 430 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", 431 | "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", 432 | "bin": { 433 | "tsc": "bin/tsc", 434 | "tsserver": "bin/tsserver" 435 | }, 436 | "engines": { 437 | "node": ">=14.17" 438 | } 439 | }, 440 | "node_modules/v8-compile-cache-lib": { 441 | "version": "3.0.1", 442 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 443 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" 444 | }, 445 | "node_modules/yn": { 446 | "version": "3.1.1", 447 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 448 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 449 | "engines": { 450 | "node": ">=6" 451 | } 452 | } 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "locreq", 3 | "version": "2.1.0", 4 | "description": "Require local modules without all that '../../../' BS", 5 | "main": "lib/src/index.js", 6 | "scripts": { 7 | "build": "node ./esbuild.cjs", 8 | "prepare": "rm -rf @types && npm run typecheck && npm run build-declarations && npm run build", 9 | "build-declarations": "tsc --emitDeclarationOnly", 10 | "typecheck": "tsc --noemit" 11 | }, 12 | "prepare": "npm run build", 13 | "type": "module", 14 | 15 | "types": "./@types/index.d.ts", 16 | "author": "Kuba Orlik (kontakt@jakub-orlik.pl)", 17 | "license": "ISC", 18 | "dependencies": { 19 | "@types/node": "^14.14.16", 20 | "esbuild": "0.14.10", 21 | "tiny-glob": "0.2.9", 22 | "ts-node": "^10.9.2", 23 | "typescript": "5.5" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/sealcode/locreq.git" 28 | }, 29 | "keywords": [ 30 | "local", 31 | "require", 32 | "path" 33 | ], 34 | "bugs": { 35 | "url": "https://github.com/sealcode/locreq/issues" 36 | }, 37 | "homepage": "https://github.com/sealcode/locreq#readme" 38 | } 39 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | import fsPromises from "fs/promises"; 4 | 5 | async function file_exists(filePath: string) { 6 | try { 7 | return (await fsPromises.stat(filePath)).isFile(); 8 | } catch (err) { 9 | return false; 10 | } 11 | } 12 | 13 | function file_exists_sync(filePath: string) { 14 | try { 15 | return fs.statSync(filePath).isFile(); 16 | } catch (err) { 17 | return false; 18 | } 19 | } 20 | 21 | async function search_for_file_upwards(start: string, filename: string) { 22 | let current_dir = start; 23 | let file_path = path.resolve(current_dir, filename); 24 | while (!(await file_exists(file_path))) { 25 | if (current_dir == "/") { 26 | throw new Error( 27 | "Could not find '" + 28 | filename + 29 | "' in any of the parent directories." 30 | ); 31 | } 32 | current_dir = path.resolve(current_dir, "../"); 33 | file_path = path.resolve(current_dir, filename); 34 | } 35 | return file_path; 36 | } 37 | 38 | function search_for_file_upwards_sync(start: string, filename: string) { 39 | let current_dir = start; 40 | let file_path = path.resolve(current_dir, filename); 41 | while (!file_exists_sync(file_path)) { 42 | if (current_dir == "/") { 43 | throw new Error( 44 | "Could not find '" + 45 | filename + 46 | "' in any of the parent directories." 47 | ); 48 | } 49 | current_dir = path.resolve(current_dir, "../"); 50 | file_path = path.resolve(current_dir, filename); 51 | } 52 | return file_path; 53 | } 54 | 55 | async function locreq_resolve(caller_path: string, module_path: string) { 56 | const package_path = await search_for_file_upwards( 57 | caller_path, 58 | "package.json" 59 | ); 60 | return path.resolve(package_path, "../", module_path); 61 | } 62 | 63 | function locreq_resolve_sync(caller_path: string, module_path: string) { 64 | const package_path = search_for_file_upwards_sync( 65 | caller_path, 66 | "package.json" 67 | ); 68 | return path.resolve(package_path, "../", module_path); 69 | } 70 | 71 | async function locreq( 72 | caller_path: string, 73 | module_path: string 74 | ): Promise { 75 | const resolvedPath = await locreq_resolve(caller_path, module_path); 76 | return import(resolvedPath); 77 | } 78 | 79 | function locreq_sync(caller_path: string, module_path: string): unknown { 80 | const resolvedPath = locreq_resolve_sync(caller_path, module_path); 81 | return import(resolvedPath); 82 | } 83 | 84 | function curry_locreq(caller_path: string) { 85 | let curried: { 86 | (module_path: string): Promise; 87 | resolve: (module_path: string) => Promise; 88 | } = (() => { 89 | const f = async function (module_path: string) { 90 | return await locreq(caller_path, module_path); 91 | }; 92 | f.resolve = async function (module_path: string) { 93 | return await locreq_resolve(caller_path, module_path); 94 | }; 95 | return f; 96 | })(); 97 | 98 | return curried; 99 | } 100 | 101 | function curry_locreq_sync(caller_path: string) { 102 | let curried: { 103 | (module_path: string): unknown; 104 | resolve: (module_path: string) => string; 105 | } = (() => { 106 | const f = function (module_path: string) { 107 | return locreq_sync(caller_path, module_path); 108 | }; 109 | f.resolve = function (module_path: string) { 110 | return locreq_resolve_sync(caller_path, module_path); 111 | }; 112 | return f; 113 | })(); 114 | 115 | return curried; 116 | } 117 | 118 | //curry_locreq -> Asynchronous 119 | //curry_locreq_sync -> Synchronous 120 | export default curry_locreq; 121 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "noImplicitAny": true, 6 | "noImplicitThis": true, 7 | "strictNullChecks": true, 8 | "target": "ES2020", 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "lib": ["es6", "esnext"], 12 | "outDir": "lib", 13 | "checkJs": true, 14 | "allowJs": true, 15 | "declarationDir": "@types", 16 | "resolveJsonModule": true, 17 | "sourceMap": true 18 | }, 19 | "include": ["src/**/*"] 20 | } 21 | --------------------------------------------------------------------------------