├── .github ├── FUNDING.yml └── workflows │ └── validate.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── dist ├── index.d.ts ├── index.js ├── index.js.map └── src │ ├── BitbucketRegistryClient.d.ts │ ├── BitbucketRegistryClient.js │ ├── BitbucketRegistryClient.js.map │ ├── GithubRegistryClient.d.ts │ ├── GithubRegistryClient.js │ ├── GithubRegistryClient.js.map │ ├── NpmRegistryClient.d.ts │ ├── NpmRegistryClient.js │ ├── NpmRegistryClient.js.map │ ├── PackageInfo.d.ts │ ├── PackageInfo.js │ ├── PackageInfo.js.map │ ├── PluginInfo.d.ts │ ├── PluginInfo.js │ ├── PluginInfo.js.map │ ├── PluginManager.d.ts │ ├── PluginManager.js │ ├── PluginManager.js.map │ ├── PluginVm.d.ts │ ├── PluginVm.js │ ├── PluginVm.js.map │ ├── VersionManager.d.ts │ ├── VersionManager.js │ ├── VersionManager.js.map │ ├── fileSystem.d.ts │ ├── fileSystem.js │ ├── fileSystem.js.map │ ├── httpUtils.d.ts │ ├── httpUtils.js │ ├── httpUtils.js.map │ ├── tarballUtils.d.ts │ ├── tarballUtils.js │ └── tarballUtils.js.map ├── index.ts ├── package-lock.json ├── package.json ├── samples ├── README.md ├── basic.d.ts ├── basic.ts ├── complex.d.ts ├── express-react.d.ts ├── express-react.ts ├── express.d.ts ├── express.ts └── index.d.ts ├── src ├── BitbucketRegistryClient.ts ├── GithubRegistryClient.ts ├── NpmRegistryClient.ts ├── PackageInfo.ts ├── PluginInfo.ts ├── PluginManager.ts ├── PluginVm.ts ├── VersionManager.ts ├── fileSystem.ts ├── httpUtils.ts └── tarballUtils.ts ├── test ├── .gitignore ├── PluginManagerSuite.d.ts ├── PluginManagerSuite.js ├── PluginManagerSuite.js.map ├── PluginManagerSuite.ts ├── host-dependency@v1.0.1 │ ├── index.js │ └── package.json ├── host-dependency@v1 │ ├── index.js │ └── package.json ├── my-basic-plugin-scoped │ ├── index.js │ └── package.json ├── my-basic-plugin │ ├── index.js │ └── package.json ├── my-minimal-plugin │ ├── index.js │ └── package.json ├── my-plugin-a@v1 │ ├── index.js │ └── package.json ├── my-plugin-a@v2 │ ├── index.js │ └── package.json ├── my-plugin-b │ ├── index.js │ └── package.json ├── my-plugin-env-global │ ├── index.js │ ├── module2.js │ └── package.json ├── my-plugin-file-win-over-folder │ ├── index.js │ ├── lib.js │ ├── lib │ │ └── index.js │ └── package.json ├── my-plugin-scoped-with-dep │ ├── index.js │ └── package.json ├── my-plugin-with-abs-require │ ├── index.js │ ├── package.json │ └── subFolder │ │ └── b.js ├── my-plugin-with-bad-opt-dep │ ├── index.js │ └── package.json ├── my-plugin-with-circular-reference │ ├── a.js │ ├── b.js │ ├── index.js │ └── package.json ├── my-plugin-with-dep │ ├── index.js │ └── package.json ├── my-plugin-with-diff-dep │ ├── index.js │ └── package.json ├── my-plugin-with-folder-as-main │ ├── lib │ │ └── index.js │ └── package.json ├── my-plugin-with-git-dep │ ├── index.js │ └── package.json ├── my-plugin-with-host-dep │ ├── index.js │ └── package.json ├── my-plugin-with-npm-modules │ ├── index.js │ └── package.json ├── my-plugin-with-opt-dep │ ├── index.js │ └── package.json ├── my-plugin-with-scoped-dep │ ├── index.js │ └── package.json ├── my-plugin-x │ ├── index.js │ └── package.json ├── my-plugin-y │ ├── index.js │ ├── package.json │ └── subFile.js ├── my-plugin.js │ ├── index.js │ └── package.json ├── my-test-plugin │ ├── aJsonFile.json │ ├── index.js │ ├── package.json │ ├── subFolder │ │ ├── b.js │ │ └── index.js │ └── subFolder2 │ │ └── index.js └── tsconfig.json ├── tsconfig.json └── tslint.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [davideicardi] 4 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [16.x, 18.x, 20.x, 22.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'npm' 25 | - run: | 26 | node --version 27 | npm --version 28 | - run: npm ci --include=dev 29 | - run: npm run src-build 30 | - run: npm test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | plugin_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | .vscode 41 | .DS_Store 42 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | samples/ 2 | test/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | 5 | branches: 6 | only: 7 | - master 8 | - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Davide Icardi 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 | # live-plugin-manager 2 | 3 | [![npm version](https://badge.fury.io/js/live-plugin-manager.svg)](https://www.npmjs.com/package/live-plugin-manager) 4 | [![Build Status](https://travis-ci.org/davideicardi/live-plugin-manager.svg?branch=master)](https://travis-ci.org/davideicardi/live-plugin-manager) 5 | 6 | `live-plugin-manager` is a Node.js module that allows you to 7 | install, uninstall and load any node package at runtime from npm registry. 8 | 9 | My main goal is to allow any application to be easily extensible by 10 | installing and running any node package at runtime, without deployments or server customization. You can basically allow your users to decide what plugin/extension to install. 11 | 12 | Main features are: 13 | 14 | - Install and run Node packages at runtime (no deployment) 15 | - Install from npm registry (private or public) 16 | - Install from filesystem 17 | - Install from github (branch, commit or tag) 18 | - Update/uninstall packages 19 | - Any Node.js packages can be installed 20 | - No special configuration is required 21 | - Installed packages can have dependencies 22 | - dependencies are automatically installed 23 | - optionalDependencies are also installed (errors are ignored) 24 | - when updating a dependencies all dependents are reloaded 25 | - Support for concurrency operation on filesystem (cloud/web farm scenario where file system is shared) 26 | - A filesystem lock is used to prevent multiple instances to work on the same filesystem in the same moment 27 | - Each package run in an semi isolated environment (VM sandbox) 28 | - Set different environment variables for each package 29 | - Implemented in TypeScript 30 | - Fully tested (mocha tests) 31 | 32 | There are some **known limitations**, see section at the end. 33 | 34 | ## Installation 35 | 36 | ``` 37 | npm install live-plugin-manager 38 | ``` 39 | 40 | ## Usage 41 | 42 | ```js 43 | import {PluginManager} from "live-plugin-manager"; 44 | 45 | const manager = new PluginManager(); 46 | 47 | async function run() { 48 | await manager.install("moment"); 49 | 50 | const moment = manager.require("moment"); 51 | console.log(moment().format()); 52 | 53 | await manager.uninstall("moment"); 54 | } 55 | 56 | run(); 57 | ``` 58 | 59 | In the above code I install `moment` package at runtime, load and execute it. 60 | 61 | Plugins are installed inside the directory specified in the `PluginManager` constructor or in the `plugin_packages` directory if not specified. 62 | 63 | Each time your application start you should reinstall any packages that you need; already downloaded packages are not automatically installed, but installation is faster because no new file is downloaded. Typically I suggest to put the list of the installed packages in a database or any other central repository. 64 | 65 | Here another more complex scenario where I install `express` with all it's dependencies, just to demonstrate how many possibilities you can have: 66 | 67 | ```js 68 | 69 | import {PluginManager} from "live-plugin-manager"; 70 | 71 | const manager = new PluginManager(); 72 | 73 | async function run() { 74 | await manager.install("express"); 75 | const express = manager.require("express"); 76 | 77 | const app = express(); 78 | app.get("/", function(req: any, res: any) { 79 | res.send("Hello World!"); 80 | }); 81 | 82 | const server = app.listen(3000, function() { 83 | }); 84 | } 85 | 86 | run(); 87 | ``` 88 | 89 | 90 | ## Load plugins 91 | 92 | `live-plugin-manager` doesn't have any special code or convention to load plugins. 93 | When you require a plugin it just load the main file (taken from `package.json`) and execute it, exactly like standard node.js `require`. 94 | Often when working with plugins you need some extension point or convention to actually integrate your plugins inside your host application. Here are some possible solutions: 95 | 96 | - [c9/architect](https://github.com/c9/architect) 97 | - [easeway/js-plugins](https://github.com/easeway/js-plugins) 98 | 99 | Another solution is to load your plugins inside a Dependency Injection container. 100 | 101 | I'm working also on [shelf-dependency](https://www.npmjs.com/package/shelf-dependency), a simple dependency injection/inversion of control container that can be used to load plugins. 102 | 103 | ## Samples 104 | 105 | - See `./samples` and `./test` directories 106 | - Sample of an extensible web application: [lpm-admin](https://github.com/davideicardi/lpm-admin) 107 | 108 | ## Reference 109 | 110 | ### PluginManager.constructor(options?: Partial\) 111 | 112 | Create a new instance of `PluginManager`. Takes an optional `options` parameter with the following properties: 113 | 114 | - `cwd`: current working directory (default to `process.cwd()`) 115 | - `pluginsPath`: plugins installation directory (default to `.\plugin_packages`, see `cwd`). Directory is created if not exists 116 | - `versionsPath`: directory containing the individual versions of the plugins (default to `./plugin_packages/.versions`). Directory is created if not exists 117 | - `npmRegistryUrl`: npm registry to use (default to https://registry.npmjs.org) 118 | - `npmRegistryConfig`: npm registry configuration see [npm-registry-client config](https://github.com/npm/npm-registry-client) 119 | - `ignoredDependencies`: array of string or RegExp with the list of dependencies to ignore, default to `@types/*` 120 | - `staticDependencies`: object with an optional list of static dependencies that can be used to force a dependencies to be ignored (not installed when a plugin depend on it) and loaded from this list 121 | - `githubAuthentication`: Github authentication configuration (optional). See `installFromGithub` or [Github api authentication](https://github.com/octokit/node-github#authentication) for more info. 122 | - `lockWait`: A number of milliseconds to wait for locks when installing modules to expire before giving up. (default 2 min) 123 | - `lockStale`: A number of milliseconds before installations locks are considered to have expired. (default 3 min) 124 | 125 | ### pluginManager.install(name: string, version?: string): Promise\ 126 | 127 | Install the specified package from npm registry or directly from github repository. 128 | `version` parameter can be a version like `1.0.3` or a github repository in the format `owner/repository_name#ref` (like `expressjs/express#351396f`). 129 | If a version is specified package is downloaded from NPM. 130 | 131 | Dependencies are automatically installed (devDependencies are ignored). 132 | 133 | ### pluginManager.installFromNpm(name: string, version = "latest"): Promise\ 134 | 135 | Install the specified package from npm registry. Dependencies are automatically installed (devDependencies are ignored). 136 | By default is the package is already available in the download folder then and version is compatible then it is not requested again from npm. Change this behavior by setting `npmInstallMode: "noCache"` options. 137 | 138 | To setup authentication for private npm registry use: 139 | 140 | ```js 141 | const manager = new PluginManager({ 142 | npmRegistryUrl: "http://your-private-registry", 143 | npmRegistryConfig: { 144 | auth: { 145 | token: "your-token" 146 | } 147 | } 148 | }); 149 | ``` 150 | 151 | 152 | ### pluginManager.installFromGithub(repository: string): Promise\ 153 | 154 | Install the specified package from github. `repository` is specified in the format `owner/repository_name#ref` (like `expressjs/express#351396f`). 155 | Dependencies are automatically installed (devDependencies are ignored). 156 | 157 | Note: Github has an API rate limit of 60 calls if not authenticated. To authenticate calls just set the `githubAuthentication` property in the options: 158 | 159 | ```js 160 | manager = new PluginManager({ 161 | githubAuthentication: { 162 | "type": "basic", 163 | "username": "YOUR_USER", 164 | "password": "YOUR_PERSONAL_TOKEN" 165 | } 166 | }); 167 | ``` 168 | 169 | See [Github api authentication](https://github.com/octokit/node-github#authentication) for more info. 170 | 171 | ### installFromPath(location: string, options: {force: boolean} = {}): Promise\ 172 | 173 | Install the specified package from a filesystem location. Dependencies are automatically installed from npm. `node_modules` folder is excluded from source location. 174 | 175 | ### installFromCode(name: string, code: string, version?: string): Promise\ 176 | 177 | Install a package by specifying code directly. If no version is specified it will be always reinstalled. 178 | 179 | ### uninstall(name: string): Promise\ 180 | 181 | Uninstall the package. Dependencies are not uninstalled automatically. 182 | 183 | ### uninstallAll(): Promise\ 184 | 185 | Uninstall all installed packages. 186 | 187 | ### list(): Promise\ 188 | 189 | Get the list of installed packages. 190 | 191 | ### require(name: string): any 192 | 193 | Load and get the instance of the plugin. Node.js `require` rules are used to load modules. 194 | Calling require multiple times gives always the same instance until plugins changes. 195 | 196 | ### getInfo(name: string): IPluginInfo | undefined 197 | 198 | Get information about an installed package. 199 | 200 | ### runScript(code: string): any 201 | 202 | Run the specified Node.js javascript code with the same context of plugins. Script are executed using `vm` as with each plugin. 203 | 204 | ### async queryPackage(name: string, version?: string): Promise 205 | 206 | Get package/module info from npm registry or github depending of the version format. 207 | 208 | ### async queryPackageFromNpm(name: string, version = "latest"): Promise 209 | 210 | Get package/module info from npm registry. 211 | 212 | ### async queryPackageFromGithub(repository: string): Promise 213 | 214 | Get package/module info from github registry. 215 | 216 | ## Security 217 | 218 | Often is a bad idea for security to allow installation and execution of any node.js package inside your application. 219 | When installing a package it's code is executed with the same permissions of your host application and can potentially damage your entire server. 220 | I suggest usually to allow to install only a limited sets of plugins or only allow trusted administrator to install plugins. 221 | 222 | ## Sandbox 223 | 224 | For advanced scenarios you can customize the sandbox where each plugin run. Sandbox defined NodeJS `global` object. 225 | A typical scenario is to customize the environment variables `global.process.env`. To customize the sandbox you can set a single 226 | sandbox for all plugins using `options.sandbox` in the `PluginManager` constructor or set a different sandbox for each plugin, see 227 | `PluginManager.setSandboxTemplate` and `PluginManager.getSandboxTemplate` functions. 228 | 229 | ## Under to hood 230 | 231 | This project use the following dependencies to do it's job: 232 | 233 | - [vm](https://nodejs.org/api/vm.html): compiling and running plugin code within V8 Virtual Machine contexts 234 | - [lockfile](https://github.com/npm/lockfile): file system locking to prevent concurrent operations (see below) 235 | - [tar.gz](https://github.com/alanhoff/node-tar.gz): extract package file 236 | - [fs-extra](https://github.com/jprichardson/node-fs-extra): file system operations 237 | - [debug](https://github.com/visionmedia/debug): debug information 238 | - (removed for now because I have problems with getArchiveLink api, 302 ...) [github](https://www.npmjs.com/package/github) 239 | - (removed for now because for some dependencies issues) [npm-registry-client](https://github.com/npm/npm-registry-client): npm registry handling 240 | 241 | While I have tried to mimic the standard Node.js module and package architecture there are some differences. 242 | First of all is the fact that plugins by definition are installed at runtime in contrast with a standard Node.js application where modules are installed before executing the node.js process. 243 | Modules can be loaded one or more times, instead in standard Node.js they are loaded only the first time that you `require` it. 244 | Only one version of a plugin can be installed, also for dependencies, while in Node.js multiple version can be installed (each module can have it's own `node_modules`). 245 | 246 | ### Locking 247 | 248 | A file system locking is implemented using [lockfile](https://github.com/npm/lockfile) library to allow multiple app instances to share the file system. 249 | This is common in some cloud scenario (for example Azure) where file system is shared between instances. 250 | 251 | Locking is used to ensure that only one instance is installing/uninstalling plugins in a given moment. 252 | 253 | You can configure `lockWait` inside constructor to configure lock timeout, and `lockStale` to consider a file lock no more valid after that period. 254 | 255 | ## Git integration 256 | 257 | Remember to git ignore the directory where packages are installed, see `pluginsPath`. 258 | Usually you should add this to your `.gitignore` file: 259 | 260 | ``` 261 | plugin_packages 262 | ``` 263 | 264 | ## Known limitations 265 | 266 | There are some known limitations when installing a package: 267 | 268 | - `process.on` has been stubbed, so plugins that use these events may not work as expected. 269 | - No `pre/post-install` scripts are executed (some packages use this scripts to build assets or for platform specific installation, so for this reason some packages are not supported) 270 | - C/C++ packages (`.node`) are not supported 271 | - Plugin dependencies can be specified only as NPM dependencies (version number) or github dependencies (owner/repo), url or other kind of dependencies are not supported 272 | 273 | If you find other problems please open an issue. 274 | 275 | ## Development 276 | 277 | Compile typescript using: 278 | 279 | ``` 280 | npm run src-build 281 | ``` 282 | 283 | Run tests using: 284 | 285 | ``` 286 | npm run test 287 | ``` 288 | 289 | Due to some github rate limits, github related tests can fail if you don't specify a github token. To specify it use: 290 | 291 | ``` 292 | github_auth_username=YOURUSER github_auth_token=YOUR_TOKEN npm test 293 | ``` 294 | 295 | The token must only have public access permission. 296 | 297 | ## License (MIT) 298 | 299 | MIT License 300 | 301 | Copyright (c) 2021 Davide Icardi 302 | 303 | Permission is hereby granted, free of charge, to any person obtaining a copy 304 | of this software and associated documentation files (the "Software"), to deal 305 | in the Software without restriction, including without limitation the rights 306 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 307 | copies of the Software, and to permit persons to whom the Software is 308 | furnished to do so, subject to the following conditions: 309 | 310 | The above copyright notice and this permission notice shall be included in all 311 | copies or substantial portions of the Software. 312 | 313 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 314 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 315 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 316 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 317 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 318 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 319 | SOFTWARE. 320 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/PluginManager"; 2 | export { IPluginInfo } from "./src/PluginInfo"; 3 | export { PackageInfo, PackageJsonInfo } from "./src/PackageInfo"; 4 | export { NpmRegistryConfig, NpmRegistryAuthBasic, NpmRegistryAuthToken } from "./src/NpmRegistryClient"; 5 | export { GithubAuth } from "./src/GithubRegistryClient"; 6 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | Object.defineProperty(exports, "__esModule", { value: true }); 17 | __exportStar(require("./src/PluginManager"), exports); 18 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,sDAAoC"} -------------------------------------------------------------------------------- /dist/src/BitbucketRegistryClient.d.ts: -------------------------------------------------------------------------------- 1 | import { PackageJsonInfo } from "./PackageInfo"; 2 | export declare class BitbucketRegistryClient { 3 | private headers; 4 | constructor(auth?: BitbucketAuth); 5 | get(repository: string): Promise; 6 | download(destinationDirectory: string, packageInfo: PackageJsonInfo): Promise; 7 | isBitbucketRepo(version: string): boolean; 8 | } 9 | export interface BitbucketAuthBasic { 10 | type: "basic"; 11 | username: string; 12 | password: string; 13 | } 14 | export type BitbucketAuth = BitbucketAuthBasic; 15 | -------------------------------------------------------------------------------- /dist/src/BitbucketRegistryClient.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.BitbucketRegistryClient = void 0; 39 | const path = __importStar(require("path")); 40 | const fs = __importStar(require("./fileSystem")); 41 | const httpUtils_1 = require("./httpUtils"); 42 | const debug_1 = __importDefault(require("debug")); 43 | const tarballUtils_1 = require("./tarballUtils"); 44 | const debug = (0, debug_1.default)("live-plugin-manager.BitbucketRegistryClient"); 45 | class BitbucketRegistryClient { 46 | constructor(auth) { 47 | if (auth) { 48 | debug(`Authenticating Bitbucket api with ${auth.type}...`); 49 | switch (auth.type) { 50 | case "basic": 51 | this.headers = Object.assign(Object.assign({}, (0, httpUtils_1.headersBasicAuth)(auth.username, auth.password)), { "user-agent": "live-plugin-manager" }); 52 | break; 53 | default: 54 | throw new Error("Auth type not supported"); 55 | } 56 | } 57 | else { 58 | this.headers = {}; 59 | } 60 | } 61 | get(repository) { 62 | return __awaiter(this, void 0, void 0, function* () { 63 | const repoInfo = extractRepositoryInfo(repository); 64 | debug("Repository info: ", repoInfo); 65 | const urlPkg = `https://api.bitbucket.org/2.0/repositories/${repoInfo.owner}/${repoInfo.repo}/src/${repoInfo.ref}/package.json`; 66 | const pkgContent = yield (0, httpUtils_1.httpJsonGet)(urlPkg, Object.assign(Object.assign({}, this.headers), { accept: "application/json" })); 67 | if (!pkgContent || !pkgContent.name || !pkgContent.version) { 68 | throw new Error("Invalid plugin Bitbucket repository " + repository); 69 | } 70 | const urlArchiveLink = `https://bitbucket.org/${repoInfo.owner}/${repoInfo.repo}/get/${repoInfo.ref}.tar.gz`; 71 | pkgContent.dist = { tarball: urlArchiveLink }; 72 | return pkgContent; 73 | }); 74 | } 75 | download(destinationDirectory, packageInfo) { 76 | return __awaiter(this, void 0, void 0, function* () { 77 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 78 | throw new Error("Invalid dist.tarball property"); 79 | } 80 | const tgzFile = yield (0, tarballUtils_1.downloadTarball)(packageInfo.dist.tarball, this.headers); 81 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 82 | try { 83 | yield (0, tarballUtils_1.extractTarball)(tgzFile, pluginDirectory); 84 | } 85 | finally { 86 | yield fs.remove(tgzFile); 87 | } 88 | return pluginDirectory; 89 | }); 90 | } 91 | isBitbucketRepo(version) { 92 | return version.indexOf("/") > 0; 93 | } 94 | } 95 | exports.BitbucketRegistryClient = BitbucketRegistryClient; 96 | function extractRepositoryInfo(repository) { 97 | const parts = repository.split("/"); 98 | if (parts.length !== 2) { 99 | throw new Error("Invalid repository name"); 100 | } 101 | const repoParts = parts[1].split("#"); 102 | const repoInfo = { 103 | owner: parts[0], 104 | repo: repoParts[0], 105 | ref: repoParts[1] || "master" 106 | }; 107 | return repoInfo; 108 | } 109 | //# sourceMappingURL=BitbucketRegistryClient.js.map -------------------------------------------------------------------------------- /dist/src/BitbucketRegistryClient.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"BitbucketRegistryClient.js","sourceRoot":"","sources":["../../src/BitbucketRegistryClient.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/GithubRegistryClient.d.ts: -------------------------------------------------------------------------------- 1 | import { PackageJsonInfo } from "./PackageInfo"; 2 | export declare class GithubRegistryClient { 3 | private headers; 4 | constructor(auth?: GithubAuth); 5 | get(repository: string): Promise; 6 | download(destinationDirectory: string, packageInfo: PackageJsonInfo): Promise; 7 | isGithubRepo(version: string): boolean; 8 | } 9 | export interface GithubAuthUserToken { 10 | type: "token"; 11 | token: string; 12 | } 13 | export interface GithubAuthBasic { 14 | type: "basic"; 15 | username: string; 16 | password: string; 17 | } 18 | export type GithubAuth = GithubAuthUserToken | GithubAuthBasic; 19 | -------------------------------------------------------------------------------- /dist/src/GithubRegistryClient.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.GithubRegistryClient = void 0; 39 | const path = __importStar(require("path")); 40 | const fs = __importStar(require("./fileSystem")); 41 | const httpUtils_1 = require("./httpUtils"); 42 | const debug_1 = __importDefault(require("debug")); 43 | const tarballUtils_1 = require("./tarballUtils"); 44 | const debug = (0, debug_1.default)("live-plugin-manager.GithubRegistryClient"); 45 | class GithubRegistryClient { 46 | constructor(auth) { 47 | if (auth) { 48 | debug(`Authenticating github api with ${auth.type}...`); 49 | switch (auth.type) { 50 | case "token": 51 | this.headers = Object.assign(Object.assign({}, (0, httpUtils_1.headersTokenAuth)(auth.token)), { "user-agent": "live-plugin-manager" }); 52 | break; 53 | case "basic": 54 | this.headers = Object.assign(Object.assign({}, (0, httpUtils_1.headersBasicAuth)(auth.username, auth.password)), { "user-agent": "live-plugin-manager" }); 55 | break; 56 | default: 57 | throw new Error("Auth type not supported"); 58 | } 59 | } 60 | else { 61 | this.headers = {}; 62 | } 63 | } 64 | get(repository) { 65 | return __awaiter(this, void 0, void 0, function* () { 66 | const repoInfo = extractRepositoryInfo(repository); 67 | debug("Repository info: ", repoInfo); 68 | const urlPkg = `https://raw.githubusercontent.com/${repoInfo.owner}/${repoInfo.repo}/${repoInfo.ref}/package.json`; 69 | const pkgContent = yield (0, httpUtils_1.httpJsonGet)(urlPkg, Object.assign(Object.assign({}, this.headers), { accept: "application/vnd.github.v3+json" })); 70 | if (!pkgContent || !pkgContent.name || !pkgContent.version) { 71 | throw new Error("Invalid plugin github repository " + repository); 72 | } 73 | const urlArchiveLink = `https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/tarball/${repoInfo.ref}`; 74 | pkgContent.dist = { tarball: urlArchiveLink }; 75 | return pkgContent; 76 | }); 77 | } 78 | download(destinationDirectory, packageInfo) { 79 | return __awaiter(this, void 0, void 0, function* () { 80 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 81 | throw new Error("Invalid dist.tarball property"); 82 | } 83 | const tgzFile = yield (0, tarballUtils_1.downloadTarball)(packageInfo.dist.tarball); 84 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 85 | try { 86 | yield (0, tarballUtils_1.extractTarball)(tgzFile, pluginDirectory); 87 | } 88 | finally { 89 | yield fs.remove(tgzFile); 90 | } 91 | return pluginDirectory; 92 | }); 93 | } 94 | isGithubRepo(version) { 95 | return version.indexOf("/") > 0; 96 | } 97 | } 98 | exports.GithubRegistryClient = GithubRegistryClient; 99 | function extractRepositoryInfo(repository) { 100 | const parts = repository.split("/"); 101 | if (parts.length !== 2) { 102 | throw new Error("Invalid repository name"); 103 | } 104 | const repoParts = parts[1].split("#"); 105 | const repoInfo = { 106 | owner: parts[0], 107 | repo: repoParts[0], 108 | ref: repoParts[1] || "master" 109 | }; 110 | return repoInfo; 111 | } 112 | // | AuthOAuthToken 113 | // | AuthOAuthSecret 114 | // | AuthUserToken 115 | // | AuthJWT; 116 | // Implementation using github api 117 | // no more used because the new version doesn't have an easy way to get the download link 118 | // https://github.com/octokit/discussions/issues/12 119 | // import * as path from "path"; 120 | // import * as fs from "./fileSystem"; 121 | // import * as GitHubApi from "github"; 122 | // import * as Debug from "debug"; 123 | // import { downloadTarball, extractTarball } from "./tarballUtils"; 124 | // import { PackageJsonInfo } from "./PackageInfo"; 125 | // const debug = Debug("live-plugin-manager.GithubRegistryClient"); 126 | // export class GithubRegistryClient { 127 | // private readonly gitHubApi = new GitHubApi({followRedirects: false}); 128 | // constructor(auth?: GitHubApi.Auth) { 129 | // if (auth) { 130 | // debug(`Authenticating github api with ${auth.type}...`); 131 | // this.gitHubApi.authenticate(auth); 132 | // } 133 | // } 134 | // async get(repository: string): Promise { 135 | // const repoInfo = extractRepositoryInfo(repository); 136 | // debug("Repository info: ", repoInfo); 137 | // const response = await this.gitHubApi.repos.getContent({ 138 | // ...repoInfo, 139 | // path: "package.json" 140 | // }); 141 | // const contentBuff = new Buffer(response.data.content, "base64"); 142 | // const contentString = contentBuff.toString("utf-8"); 143 | // const pkgContent = JSON.parse(contentString) as PackageJsonInfo; 144 | // if (!pkgContent.name || !pkgContent.version) { 145 | // throw new Error("Invalid plugin github repository " + repository); 146 | // } 147 | // debug("Repository package info: ", pkgContent.name, pkgContent.version); 148 | // // https://github.com/jashkenas/underscore/archive/master.zip 149 | // // https://codeload.github.com/jashkenas/underscore/legacy.tar.gz/master 150 | // const archiveLinkResponse = await this.gitHubApi.repos.getArchiveLink({ 151 | // ...repoInfo, 152 | // archive_format: "tarball" 153 | // }); 154 | // const archiveLink = archiveLinkResponse.meta.location; 155 | // if (!(typeof archiveLink === "string")) { 156 | // throw new Error("Invalid archive link"); 157 | // } 158 | // debug("Repository package archive: ", archiveLink); 159 | // pkgContent.dist = { tarball: archiveLink }; 160 | // return pkgContent; 161 | // } 162 | // async download( 163 | // destinationDirectory: string, 164 | // packageInfo: PackageJsonInfo): Promise { 165 | // if (!packageInfo.dist || !packageInfo.dist.tarball) { 166 | // throw new Error("Invalid dist.tarball property"); 167 | // } 168 | // const tgzFile = await downloadTarball(packageInfo.dist.tarball); 169 | // const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 170 | // try { 171 | // await extractTarball(tgzFile, pluginDirectory); 172 | // } finally { 173 | // await fs.remove(tgzFile); 174 | // } 175 | // return pluginDirectory; 176 | // } 177 | // isGithubRepo(version: string): boolean { 178 | // return version.indexOf("/") > 0; 179 | // } 180 | // } 181 | // function extractRepositoryInfo(repository: string) { 182 | // const parts = repository.split("/"); 183 | // if (parts.length !== 2) { 184 | // throw new Error("Invalid repository name"); 185 | // } 186 | // const repoParts = parts[1].split("#"); 187 | // const repoInfo = { 188 | // owner: parts[0], 189 | // repo: repoParts[0], 190 | // ref: repoParts[1] || "master" 191 | // }; 192 | // return repoInfo; 193 | // } 194 | //# sourceMappingURL=GithubRegistryClient.js.map -------------------------------------------------------------------------------- /dist/src/GithubRegistryClient.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"GithubRegistryClient.js","sourceRoot":"","sources":["../../src/GithubRegistryClient.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/NpmRegistryClient.d.ts: -------------------------------------------------------------------------------- 1 | import * as httpUtils from "./httpUtils"; 2 | import { PackageInfo } from "./PackageInfo"; 3 | export declare class NpmRegistryClient { 4 | private readonly npmUrl; 5 | defaultHeaders: httpUtils.Headers; 6 | constructor(npmUrl: string, config: NpmRegistryConfig); 7 | get(name: string, versionOrTag?: string | null): Promise; 8 | download(destinationDirectory: string, packageInfo: PackageInfo): Promise; 9 | private getNpmData; 10 | } 11 | export interface NpmRegistryConfig { 12 | auth?: NpmRegistryAuthToken | NpmRegistryAuthBasic; 13 | userAgent?: string; 14 | } 15 | export interface NpmRegistryAuthToken { 16 | token: string; 17 | } 18 | export interface NpmRegistryAuthBasic { 19 | username: string; 20 | password: string; 21 | } 22 | -------------------------------------------------------------------------------- /dist/src/NpmRegistryClient.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.NpmRegistryClient = void 0; 39 | const urlJoin = require("url-join"); 40 | const path = __importStar(require("path")); 41 | const fs = __importStar(require("./fileSystem")); 42 | const tarballUtils_1 = require("./tarballUtils"); 43 | const semVer = __importStar(require("semver")); 44 | const httpUtils = __importStar(require("./httpUtils")); 45 | const debug_1 = __importDefault(require("debug")); 46 | const debug = (0, debug_1.default)("live-plugin-manager.NpmRegistryClient"); 47 | class NpmRegistryClient { 48 | constructor(npmUrl, config) { 49 | this.npmUrl = npmUrl; 50 | const staticHeaders = { 51 | // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md 52 | "accept-encoding": "gzip", 53 | "accept": "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*", 54 | "user-agent": config.userAgent || "live-plugin-manager" 55 | }; 56 | const authHeader = createAuthHeader(config.auth); 57 | this.defaultHeaders = Object.assign(Object.assign({}, staticHeaders), authHeader); 58 | } 59 | get(name, versionOrTag = "latest") { 60 | return __awaiter(this, void 0, void 0, function* () { 61 | debug(`Getting npm info for ${name}:${versionOrTag}...`); 62 | if (typeof versionOrTag !== "string") { 63 | versionOrTag = ""; 64 | } 65 | if (typeof name !== "string") { 66 | throw new Error("Invalid package name"); 67 | } 68 | const data = yield this.getNpmData(name); 69 | versionOrTag = versionOrTag.trim(); 70 | // check if there is a tag (es. latest) 71 | const distTags = data["dist-tags"]; 72 | let version = distTags && distTags[versionOrTag]; 73 | if (!version) { 74 | version = semVer.clean(versionOrTag) || versionOrTag; 75 | } 76 | // find correct version 77 | let pInfo = data.versions[version]; 78 | if (!pInfo) { 79 | // find compatible version 80 | for (const pVersion in data.versions) { 81 | if (!data.versions.hasOwnProperty(pVersion)) { 82 | continue; 83 | } 84 | const pVersionInfo = data.versions[pVersion]; 85 | if (!semVer.satisfies(pVersionInfo.version, version)) { 86 | continue; 87 | } 88 | if (!pInfo || semVer.gt(pVersionInfo.version, pInfo.version)) { 89 | pInfo = pVersionInfo; 90 | } 91 | } 92 | } 93 | if (!pInfo) { 94 | throw new Error(`Version '${versionOrTag} not found`); 95 | } 96 | return { 97 | dist: pInfo.dist, 98 | name: pInfo.name, 99 | version: pInfo.version 100 | }; 101 | }); 102 | } 103 | download(destinationDirectory, packageInfo) { 104 | return __awaiter(this, void 0, void 0, function* () { 105 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 106 | throw new Error("Invalid dist.tarball property"); 107 | } 108 | const tgzFile = yield (0, tarballUtils_1.downloadTarball)(packageInfo.dist.tarball, this.defaultHeaders); 109 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 110 | try { 111 | yield (0, tarballUtils_1.extractTarball)(tgzFile, pluginDirectory); 112 | } 113 | finally { 114 | yield fs.remove(tgzFile); 115 | } 116 | return pluginDirectory; 117 | }); 118 | } 119 | getNpmData(name) { 120 | return __awaiter(this, void 0, void 0, function* () { 121 | const regUrl = urlJoin(this.npmUrl, encodeNpmName(name)); 122 | const headers = this.defaultHeaders; 123 | try { 124 | const result = yield httpUtils.httpJsonGet(regUrl, headers); 125 | if (!result) { 126 | throw new Error("Response is empty"); 127 | } 128 | if (!result.versions 129 | || !result.name) { 130 | throw new Error("Invalid json format"); 131 | } 132 | return result; 133 | } 134 | catch (err) { 135 | if (err.message) { 136 | err.message = `Failed to get package '${name}' ${err.message}`; 137 | } 138 | throw err; 139 | } 140 | }); 141 | } 142 | } 143 | exports.NpmRegistryClient = NpmRegistryClient; 144 | function encodeNpmName(name) { 145 | return name.replace("/", "%2F"); 146 | } 147 | function createAuthHeader(auth) { 148 | if (!auth) { 149 | return {}; 150 | } 151 | if (isTokenAuth(auth)) { 152 | return httpUtils.headersBearerAuth(auth.token); // this should be a JWT I think... 153 | } 154 | else if (isBasicAuth(auth)) { 155 | return httpUtils.headersBasicAuth(auth.username, auth.password); 156 | } 157 | else { 158 | return {}; 159 | } 160 | } 161 | function isTokenAuth(arg) { 162 | return arg.token !== undefined; 163 | } 164 | function isBasicAuth(arg) { 165 | return arg.username !== undefined; 166 | } 167 | //# sourceMappingURL=NpmRegistryClient.js.map -------------------------------------------------------------------------------- /dist/src/NpmRegistryClient.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"NpmRegistryClient.js","sourceRoot":"","sources":["../../src/NpmRegistryClient.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/PackageInfo.d.ts: -------------------------------------------------------------------------------- 1 | export interface PackageJsonInfo extends PackageInfo { 2 | main?: string; 3 | dependencies?: { 4 | [name: string]: string; 5 | }; 6 | optionalDependencies?: { 7 | [name: string]: string; 8 | }; 9 | } 10 | export interface PackageInfo { 11 | name: string; 12 | version: string; 13 | dist?: { 14 | tarball: string; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /dist/src/PackageInfo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=PackageInfo.js.map -------------------------------------------------------------------------------- /dist/src/PackageInfo.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"PackageInfo.js","sourceRoot":"","sources":["../../src/PackageInfo.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/src/PluginInfo.d.ts: -------------------------------------------------------------------------------- 1 | import { PackageJsonInfo } from './PackageInfo'; 2 | export interface IPluginInfo { 3 | readonly mainFile: string; 4 | readonly location: string; 5 | readonly name: string; 6 | readonly version: string; 7 | readonly dependencies: { 8 | [name: string]: string; 9 | }; 10 | readonly optionalDependencies?: { 11 | [name: string]: string; 12 | }; 13 | readonly dependencyDetails?: { 14 | [name: string]: PackageJsonInfo | undefined; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /dist/src/PluginInfo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=PluginInfo.js.map -------------------------------------------------------------------------------- /dist/src/PluginInfo.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"PluginInfo.js","sourceRoot":"","sources":["../../src/PluginInfo.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/src/PluginManager.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | import { NpmRegistryConfig } from "./NpmRegistryClient"; 5 | import { IPluginInfo } from "./PluginInfo"; 6 | import { GithubAuth } from "./GithubRegistryClient"; 7 | import { BitbucketAuth } from "./BitbucketRegistryClient"; 8 | import { PackageInfo } from "./PackageInfo"; 9 | type IgnoreDependency = string | RegExp; 10 | type NodeJSGlobal = typeof global; 11 | export interface PluginManagerOptions { 12 | cwd: string; 13 | pluginsPath: string; 14 | versionsPath?: string; 15 | sandbox: PluginSandbox; 16 | npmRegistryUrl: string; 17 | npmRegistryConfig: NpmRegistryConfig; 18 | npmInstallMode: "useCache" | "noCache"; 19 | requireCoreModules: boolean; 20 | hostRequire?: NodeRequire; 21 | ignoredDependencies: IgnoreDependency[]; 22 | staticDependencies: { 23 | [key: string]: any; 24 | }; 25 | githubAuthentication?: GithubAuth; 26 | bitbucketAuthentication?: BitbucketAuth; 27 | lockWait: number; 28 | lockStale: number; 29 | } 30 | export interface PluginSandbox { 31 | env?: NodeJS.ProcessEnv; 32 | global?: NodeJSGlobal; 33 | } 34 | export interface InstallFromPathOptions { 35 | force: boolean; 36 | } 37 | export declare class PluginManager { 38 | readonly options: PluginManagerOptions; 39 | private versionManager; 40 | private readonly vm; 41 | private readonly installedPlugins; 42 | private readonly npmRegistry; 43 | private readonly githubRegistry; 44 | private readonly bitbucketRegistry; 45 | private readonly sandboxTemplates; 46 | constructor(options?: Partial); 47 | install(name: string, version?: string): Promise; 48 | /** 49 | * Install a package from npm 50 | * @param name name of the package 51 | * @param version version of the package, default to "latest" 52 | */ 53 | installFromNpm(name: string, version?: string): Promise; 54 | /** 55 | * Install a package from a local folder 56 | * @param location package local folder location 57 | * @param options options, if options.force == true then package is always reinstalled without version checking 58 | */ 59 | installFromPath(location: string, options?: Partial): Promise; 60 | installFromGithub(repository: string): Promise; 61 | installFromBitbucket(repository: string): Promise; 62 | /** 63 | * Install a package by specifiing code directly. If no version is specified it will be always reinstalled. 64 | * @param name plugin name 65 | * @param code code to be loaded, equivalent to index.js 66 | * @param version optional version, if omitted no version check is performed 67 | */ 68 | installFromCode(name: string, code: string, version?: string): Promise; 69 | uninstall(name: string): Promise; 70 | uninstallAll(): Promise; 71 | list(): IPluginInfo[]; 72 | require(fullName: string): any; 73 | setSandboxTemplate(name: string, sandbox: PluginSandbox | undefined): void; 74 | getSandboxTemplate(name: string): PluginSandbox | undefined; 75 | alreadyInstalled(name: string, version?: string, mode?: "satisfies" | "satisfiesOrGreater"): IPluginInfo | undefined; 76 | getInfo(name: string): IPluginInfo | undefined; 77 | queryPackage(name: string, version?: string): Promise; 78 | queryPackageFromNpm(name: string, version?: string): Promise; 79 | queryPackageFromGithub(repository: string): Promise; 80 | runScript(code: string): any; 81 | private uninstallLockFree; 82 | private installLockFree; 83 | private installFromPathLockFree; 84 | /** Install from npm or from cache if already available */ 85 | private installFromNpmLockFreeCache; 86 | /** Install from npm */ 87 | private installFromNpmLockFreeDirect; 88 | private installFromGithubLockFree; 89 | private installFromBitbucketLockFree; 90 | private installFromCodeLockFree; 91 | private installDependency; 92 | private listDependencies; 93 | private installDependencies; 94 | private linkDependencyToPlugin; 95 | private unloadDependents; 96 | private unloadWithDependents; 97 | private isModuleAvailableFromHost; 98 | private isValidPluginName; 99 | private validatePluginVersion; 100 | private getPluginLocation; 101 | private removeDownloaded; 102 | private isAlreadyDownloaded; 103 | private getDownloadedPackage; 104 | private readPackageJsonFromPath; 105 | private load; 106 | private unload; 107 | private addPlugin; 108 | /** 109 | * Unlink a plugin from the specified version of package. 110 | * 111 | * @param plugin A plugin information to unlink 112 | */ 113 | private unlinkModule; 114 | /** 115 | * Link a plugin to the specified version of package. 116 | * 117 | * @param plugin A plugin information to link 118 | * @returns A plugin information linked 119 | */ 120 | private linkModule; 121 | private deleteAndUnloadPlugin; 122 | private syncLock; 123 | private syncUnlock; 124 | private shouldIgnore; 125 | private createPluginInfo; 126 | /** 127 | * Create a plugin information from the specified location. 128 | * 129 | * @param location A location of the plugin 130 | * @param withDependencies If true, dependencies are also loaded 131 | * @returns 132 | */ 133 | private createPluginInfoFromPath; 134 | } 135 | export {}; 136 | -------------------------------------------------------------------------------- /dist/src/PluginVm.d.ts: -------------------------------------------------------------------------------- 1 | import { PluginManager } from "./PluginManager"; 2 | import { IPluginInfo } from "./PluginInfo"; 3 | export declare class PluginVm { 4 | private readonly manager; 5 | private requireCache; 6 | private sandboxCache; 7 | constructor(manager: PluginManager); 8 | unload(pluginContext: IPluginInfo): void; 9 | load(pluginContext: IPluginInfo, filePath: string): any; 10 | resolve(pluginContext: IPluginInfo, filePath: string): string; 11 | runScript(code: string): any; 12 | splitRequire(fullName: string): { 13 | pluginName: string; 14 | requiredPath: string | undefined; 15 | }; 16 | private getScopedInfo; 17 | private vmRunScriptInSandbox; 18 | private vmRunScriptInPlugin; 19 | private getCache; 20 | private setCache; 21 | private removeCache; 22 | private createModuleSandbox; 23 | private sandboxResolve; 24 | private sandboxRequire; 25 | private isCoreModule; 26 | private isPlugin; 27 | private hasDependency; 28 | private tryResolveAsFile; 29 | private tryResolveAsDirectory; 30 | private getPluginSandbox; 31 | private createGlobalSandbox; 32 | } 33 | -------------------------------------------------------------------------------- /dist/src/PluginVm.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | exports.PluginVm = void 0; 30 | const vm = __importStar(require("vm")); 31 | const fs = __importStar(require("fs-extra")); 32 | const path = __importStar(require("path")); 33 | const console = __importStar(require("console")); 34 | const debug_1 = __importDefault(require("debug")); 35 | const debug = (0, debug_1.default)("live-plugin-manager.PluginVm"); 36 | const SCOPED_REGEX = /^(@[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_]+)(.*)/; 37 | class PluginVm { 38 | constructor(manager) { 39 | this.manager = manager; 40 | this.requireCache = new Map(); 41 | this.sandboxCache = new Map(); 42 | } 43 | unload(pluginContext) { 44 | this.requireCache.delete(pluginContext); 45 | this.sandboxCache.delete(pluginContext); 46 | } 47 | load(pluginContext, filePath) { 48 | let moduleInstance = this.getCache(pluginContext, filePath); 49 | if (moduleInstance) { 50 | if (debug.enabled) { 51 | debug(`${filePath} loaded from cache`); 52 | } 53 | return moduleInstance.exports; 54 | } 55 | if (debug.enabled) { 56 | debug(`Loading ${filePath} ...`); 57 | } 58 | const sandbox = this.createModuleSandbox(pluginContext, filePath); 59 | moduleInstance = sandbox.module; 60 | const filePathExtension = path.extname(filePath).toLowerCase(); 61 | if (filePathExtension === ".js" || filePathExtension === ".cjs") { 62 | const code = fs.readFileSync(filePath, "utf8"); 63 | // note: I first put the object (before executing the script) in cache to support circular require 64 | this.setCache(pluginContext, filePath, moduleInstance); 65 | try { 66 | this.vmRunScriptInSandbox(sandbox, filePath, code); 67 | } 68 | catch (e) { 69 | // in case of error remove the cache 70 | this.removeCache(pluginContext, filePath); 71 | throw e; 72 | } 73 | } 74 | else if (filePathExtension === ".json") { 75 | sandbox.module.exports = fs.readJsonSync(filePath); 76 | this.setCache(pluginContext, filePath, moduleInstance); 77 | } 78 | else { 79 | throw new Error("Invalid javascript file " + filePath); 80 | } 81 | moduleInstance.loaded = true; 82 | return moduleInstance.exports; 83 | } 84 | resolve(pluginContext, filePath) { 85 | return this.sandboxResolve(pluginContext, pluginContext.location, filePath); 86 | } 87 | runScript(code) { 88 | const name = "dynamic-" + Date.now; 89 | const filePath = path.join(this.manager.options.pluginsPath, name + ".js"); 90 | const pluginContext = { 91 | location: path.join(this.manager.options.pluginsPath, name), 92 | mainFile: filePath, 93 | name, 94 | version: "1.0.0", 95 | dependencies: {}, 96 | dependencyDetails: {} 97 | }; 98 | try { 99 | return this.vmRunScriptInPlugin(pluginContext, filePath, code); 100 | } 101 | finally { 102 | this.unload(pluginContext); 103 | } 104 | } 105 | splitRequire(fullName) { 106 | const scopedInfo = this.getScopedInfo(fullName); 107 | if (scopedInfo) { 108 | return scopedInfo; 109 | } 110 | const slashPosition = fullName.indexOf("/"); 111 | let requiredPath; 112 | let pluginName = fullName; 113 | if (slashPosition > 0) { 114 | pluginName = fullName.substring(0, slashPosition); 115 | requiredPath = "." + fullName.substring(slashPosition); 116 | } 117 | return { pluginName, requiredPath }; 118 | } 119 | getScopedInfo(fullName) { 120 | const match = SCOPED_REGEX.exec(fullName); 121 | if (!match) { 122 | return undefined; 123 | } 124 | const requiredPath = match[2] 125 | ? "." + match[2] 126 | : undefined; 127 | return { 128 | pluginName: match[1], 129 | requiredPath 130 | }; 131 | } 132 | vmRunScriptInSandbox(moduleSandbox, filePath, code) { 133 | const moduleContext = vm.createContext(moduleSandbox); 134 | // For performance reasons wrap code in a Immediately-invoked function expression 135 | // https://60devs.com/executing-js-code-with-nodes-vm-module.html 136 | // I have also declared the exports variable to support the 137 | // `var app = exports = module.exports = {};` notation 138 | const iifeCode = ` 139 | (function(exports){ 140 | ${code} 141 | }(module.exports));`; 142 | const vmOptions = { displayErrors: true, filename: filePath }; 143 | const script = new vm.Script(iifeCode, vmOptions); 144 | script.runInContext(moduleContext, vmOptions); 145 | } 146 | vmRunScriptInPlugin(pluginContext, filePath, code) { 147 | const sandbox = this.createModuleSandbox(pluginContext, filePath); 148 | this.vmRunScriptInSandbox(sandbox, filePath, code); 149 | sandbox.module.loaded = true; 150 | return sandbox.module.exports; 151 | } 152 | getCache(pluginContext, filePath) { 153 | const moduleCache = this.requireCache.get(pluginContext); 154 | if (!moduleCache) { 155 | return undefined; 156 | } 157 | return moduleCache.get(filePath); 158 | } 159 | setCache(pluginContext, filePath, instance) { 160 | let moduleCache = this.requireCache.get(pluginContext); 161 | if (!moduleCache) { 162 | moduleCache = new Map(); 163 | this.requireCache.set(pluginContext, moduleCache); 164 | } 165 | moduleCache.set(filePath, instance); 166 | } 167 | removeCache(pluginContext, filePath) { 168 | const moduleCache = this.requireCache.get(pluginContext); 169 | if (!moduleCache) { 170 | return; 171 | } 172 | moduleCache.delete(filePath); 173 | } 174 | createModuleSandbox(pluginContext, filePath) { 175 | const pluginSandbox = this.getPluginSandbox(pluginContext); 176 | const moduleDirname = path.dirname(filePath); 177 | const moduleResolve = Object.assign((id) => { 178 | return this.sandboxResolve(pluginContext, moduleDirname, id); 179 | }, { 180 | paths: (_request) => null // TODO I should I populate this 181 | }); 182 | const moduleRequire = Object.assign((requiredName) => { 183 | if (debug.enabled) { 184 | debug(`Requiring '${requiredName}' from ${filePath}...`); 185 | } 186 | return this.sandboxRequire(pluginContext, moduleDirname, requiredName); 187 | }, { 188 | resolve: moduleResolve, 189 | cache: {}, 190 | extensions: {}, 191 | main: require.main // TODO assign the real main or consider main the current module (ie. module)? 192 | }); 193 | const myModule = { 194 | exports: {}, 195 | filename: filePath, 196 | id: filePath, 197 | loaded: false, 198 | require: moduleRequire, 199 | paths: [], 200 | parent: module, 201 | children: [], 202 | path: moduleDirname, 203 | isPreloading: false 204 | }; 205 | // assign missing https://nodejs.org/api/globals.html 206 | // and other "not real global" objects 207 | const moduleSandbox = Object.assign(Object.assign({}, pluginSandbox), { module: myModule, __dirname: moduleDirname, __filename: filePath, require: moduleRequire }); 208 | return moduleSandbox; 209 | } 210 | sandboxResolve(pluginContext, moduleDirName, requiredName) { 211 | // I try to use a similar logic of https://nodejs.org/api/modules.html#modules_modules 212 | // is a relative module or absolute path 213 | if (requiredName.startsWith(".") || path.isAbsolute(requiredName)) { 214 | const fullPath = path.resolve(moduleDirName, requiredName); 215 | // for security reason check to not load external files 216 | if (!fullPath.startsWith(pluginContext.location)) { 217 | throw new Error("Cannot require a module outside a plugin"); 218 | } 219 | const isFile = this.tryResolveAsFile(fullPath); 220 | if (isFile) { 221 | return isFile; 222 | } 223 | const isDirectory = this.tryResolveAsDirectory(fullPath); 224 | if (isDirectory) { 225 | return isDirectory; 226 | } 227 | throw new Error(`Cannot find ${requiredName} in plugin ${pluginContext.name}`); 228 | } 229 | if (this.hasDependency(pluginContext, requiredName)) { 230 | let fullPath = path.join(pluginContext.location, "node_modules", requiredName); 231 | if (!pluginContext.dependencyDetails) { 232 | throw new Error(`Dependencies not loaded for plugin ${pluginContext.name}`); 233 | } 234 | const packageJson = pluginContext.dependencyDetails[requiredName]; 235 | if (!packageJson) { 236 | throw new Error(`${pluginContext.name} does not include ${requiredName} in local dependencies`); 237 | } 238 | if (packageJson.main) { 239 | fullPath = path.join(fullPath, packageJson.main); 240 | } 241 | const isFile = this.tryResolveAsFile(fullPath); 242 | if (isFile) { 243 | return isFile; 244 | } 245 | const isDirectory = this.tryResolveAsDirectory(fullPath); 246 | if (isDirectory) { 247 | return isDirectory; 248 | } 249 | throw new Error(`Cannot find ${requiredName} in plugin ${pluginContext.name}`); 250 | } 251 | if (this.isPlugin(requiredName)) { 252 | return requiredName; 253 | } 254 | if (this.manager.options.staticDependencies[requiredName]) { 255 | return requiredName; 256 | } 257 | // this will fail if module is unknown 258 | if (this.isCoreModule(requiredName)) { 259 | return requiredName; 260 | } 261 | return requiredName; 262 | } 263 | sandboxRequire(pluginContext, moduleDirName, requiredName) { 264 | // I try to use a similar logic of https://nodejs.org/api/modules.html#modules_modules 265 | const fullName = this.sandboxResolve(pluginContext, moduleDirName, requiredName); 266 | // is an absolute file or directory that can be loaded 267 | if (path.isAbsolute(fullName)) { 268 | if (debug.enabled) { 269 | debug(`Resolved ${requiredName} as file ${fullName}`); 270 | } 271 | return this.load(pluginContext, fullName); 272 | } 273 | if (this.manager.options.staticDependencies[requiredName]) { 274 | if (debug.enabled) { 275 | debug(`Resolved ${requiredName} as static dependency`); 276 | } 277 | return this.manager.options.staticDependencies[requiredName]; 278 | } 279 | if (this.isPlugin(requiredName)) { 280 | if (debug.enabled) { 281 | debug(`Resolved ${requiredName} as plugin`); 282 | } 283 | return this.manager.require(requiredName); 284 | } 285 | if (this.isCoreModule(requiredName)) { 286 | if (debug.enabled) { 287 | debug(`Resolved ${requiredName} as core module`); 288 | } 289 | return require(requiredName); // I use system require 290 | } 291 | if (this.manager.options.hostRequire) { 292 | if (debug.enabled) { 293 | debug(`Resolved ${requiredName} as host module`); 294 | } 295 | return this.manager.options.hostRequire(requiredName); 296 | } 297 | throw new Error(`Module ${requiredName} not found, failed to load plugin ${pluginContext.name}`); 298 | } 299 | isCoreModule(requiredName) { 300 | return this.manager.options.requireCoreModules 301 | && require.resolve(requiredName) === requiredName; 302 | } 303 | isPlugin(requiredName) { 304 | const { pluginName } = this.splitRequire(requiredName); 305 | return !!this.manager.getInfo(pluginName); 306 | } 307 | hasDependency(pluginContext, requiredName) { 308 | const { dependencyDetails } = pluginContext; 309 | if (!dependencyDetails) { 310 | return false; 311 | } 312 | return !!dependencyDetails[requiredName]; 313 | } 314 | tryResolveAsFile(fullPath) { 315 | const parentPath = path.dirname(fullPath); 316 | if (checkPath(parentPath) !== "directory") { 317 | return undefined; 318 | } 319 | const reqPathKind = checkPath(fullPath); 320 | if (reqPathKind !== "file") { 321 | if (checkPath(fullPath + ".cjs") === "file") { 322 | return fullPath + ".cjs"; 323 | } 324 | if (checkPath(fullPath + ".js") === "file") { 325 | return fullPath + ".js"; 326 | } 327 | if (checkPath(fullPath + ".json") === "file") { 328 | return fullPath + ".json"; 329 | } 330 | return undefined; 331 | } 332 | if (reqPathKind === "file") { 333 | return fullPath; 334 | } 335 | return undefined; 336 | } 337 | tryResolveAsDirectory(fullPath) { 338 | if (checkPath(fullPath) !== "directory") { 339 | return undefined; 340 | } 341 | const indexCjs = path.join(fullPath, "index.cjs"); 342 | if (checkPath(indexCjs) === "file") { 343 | return indexCjs; 344 | } 345 | const indexJs = path.join(fullPath, "index.js"); 346 | if (checkPath(indexJs) === "file") { 347 | return indexJs; 348 | } 349 | const indexJson = path.join(fullPath, "index.json"); 350 | if (checkPath(indexJson) === "file") { 351 | return indexJson; 352 | } 353 | return undefined; 354 | } 355 | getPluginSandbox(pluginContext) { 356 | let pluginSandbox = this.sandboxCache.get(pluginContext); 357 | if (!pluginSandbox) { 358 | const srcSandboxTemplate = this.manager.getSandboxTemplate(pluginContext.name) 359 | || this.manager.options.sandbox; 360 | pluginSandbox = this.createGlobalSandbox(srcSandboxTemplate); 361 | this.sandboxCache.set(pluginContext, pluginSandbox); 362 | } 363 | return pluginSandbox; 364 | } 365 | createGlobalSandbox(sandboxTemplate) { 366 | const srcGlobal = sandboxTemplate.global || global; 367 | const sandbox = Object.assign({}, srcGlobal); 368 | // copy properties that are not copied automatically (don't know why..) 369 | // https://stackoverflow.com/questions/59009214/some-properties-of-the-global-instance-are-not-copied-by-spread-operator-or-by-o 370 | // (some of these properties are Node.js specific, like Buffer) 371 | // Function and Object should not be defined, otherwise we will have some unexpected behavior 372 | // Somewhat related to https://github.com/nodejs/node/issues/28823 373 | if (!sandbox.Buffer && srcGlobal.Buffer) { 374 | sandbox.Buffer = srcGlobal.Buffer; 375 | } 376 | if (!sandbox.URL && global.URL) { 377 | // cast to any because URL is not defined inside NodeJSGlobal, I don't understand why ... 378 | sandbox.URL = global.URL; 379 | } 380 | if (!sandbox.URLSearchParams && global.URLSearchParams) { 381 | // cast to any because URLSearchParams is not defined inside NodeJSGlobal, I don't understand why ... 382 | sandbox.URLSearchParams = global.URLSearchParams; 383 | } 384 | if (!sandbox.process && global.process) { 385 | sandbox.process = Object.assign({}, global.process); 386 | } 387 | if (sandbox.process) { 388 | // override env to "unlink" from original process 389 | const srcEnv = sandboxTemplate.env || global.process.env; 390 | sandbox.process.env = Object.assign({}, srcEnv); // copy properties 391 | sandbox.process.on = (event, callback) => { }; 392 | } 393 | // create global console 394 | if (!sandbox.console) { 395 | sandbox.console = new console.Console({ stdout: process.stdout, stderr: process.stderr }); 396 | } 397 | // override the global obj to "unlink" it from the original global obj 398 | // and make it unique for each sandbox 399 | sandbox.global = sandbox; 400 | return sandbox; 401 | } 402 | } 403 | exports.PluginVm = PluginVm; 404 | function checkPath(fullPath) { 405 | try { 406 | const stats = fs.statSync(fullPath); 407 | if (stats.isDirectory()) { 408 | return "directory"; 409 | } 410 | else if (stats.isFile()) { 411 | return "file"; 412 | } 413 | else { 414 | return "none"; 415 | } 416 | } 417 | catch (_a) { 418 | return "none"; 419 | } 420 | } 421 | //# sourceMappingURL=PluginVm.js.map -------------------------------------------------------------------------------- /dist/src/PluginVm.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"PluginVm.js","sourceRoot":"","sources":["../../src/PluginVm.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/VersionManager.d.ts: -------------------------------------------------------------------------------- 1 | import { IPluginInfo } from "./PluginInfo"; 2 | import { PackageJsonInfo, PackageInfo } from "./PackageInfo"; 3 | export interface VersionManagerOptions { 4 | cwd: string; 5 | rootPath: string; 6 | } 7 | export declare const DefaultMainFile = "index.js"; 8 | /** 9 | * A class to manage the versions of the downloaded packages. 10 | */ 11 | export declare class VersionManager { 12 | readonly options: VersionManagerOptions; 13 | constructor(options?: Partial); 14 | /** 15 | * Ensure the root path exists. 16 | */ 17 | ensureRootPath(): Promise; 18 | /** 19 | * Get the location for the specified package name and version. 20 | * 21 | * @param packageInfo A package information to get the location 22 | * @returns A location for the specified package name and version 23 | */ 24 | getPath(packageInfo: PackageInfo): string; 25 | /** 26 | * Resolve the path for the specified package name and version. 27 | * 28 | * @param name A package name to resolve 29 | * @param version A package version to resolve 30 | * @returns 31 | */ 32 | resolvePath(name: string, version: string): Promise; 33 | /** 34 | * Download a package using a downloader. 35 | * Downloaded files are stored in the rootPath as directory named as `name@version`. 36 | * 37 | * @param downloader A downloader object that implements the download method 38 | * @param registryInfo A package info to download 39 | * @returns A information for the downloaded package 40 | */ 41 | download(downloader: { 42 | download: (destinationDirectory: string, registryInfo: PackageJsonInfo) => Promise; 43 | }, registryInfo: PackageJsonInfo): Promise; 44 | /** 45 | * Uninstall packages which are not used by other packages. 46 | * 47 | * @param installedPlugins A list of the installed packages. 48 | * @returns A list of the uninstalled packages. 49 | */ 50 | uninstallOrphans(installedPlugins: Array): Promise; 51 | /** 52 | * Unload a version of a plugin if it is not used by any other plugin 53 | * 54 | * @param pluginInfo A plugin information to uninstall 55 | * @returns true if the version was unloaded, false if it was used by another plugin 56 | */ 57 | uninstallOrphan(pluginInfo: IPluginInfo): Promise; 58 | /** 59 | * Create a plugin information for the specified version. 60 | * 61 | * @param name A package name 62 | * @param version A package version 63 | * @param withDependencies A flag to load dependency packages 64 | * @returns A plugin information for the specified version 65 | */ 66 | createVersionInfo(name: string, version: string, withDependencies?: boolean): Promise; 67 | /** 68 | * Create a plugin information for the specified path. 69 | * 70 | * @param location A path to the package directory 71 | * @param withDependencies A flag to load dependency packages 72 | * @returns A plugin information for the specified path 73 | */ 74 | createVersionInfoFromPath(location: string, withDependencies?: boolean): Promise; 75 | /** 76 | * Check whether the filename is satisfied with the specified package name and version. 77 | * 78 | * @param filename A filename to check 79 | * @param name A package name to check 80 | * @param version A package version to check 81 | * @returns true if the filename is satisfied with the specified package name and version, otherwise false 82 | */ 83 | private checkModuleFilenameSatisfied; 84 | /** 85 | * Get the package information from the package directory. 86 | * 87 | * @param location A path to the package directory 88 | * @returns A package information for the package directory 89 | */ 90 | private readPackageJsonFromPath; 91 | /** 92 | * List package directories in the specified base directory. 93 | * 94 | * @param baseDir A base directory to list 95 | * @param scope A scope for packages 96 | * @returns A list of the package directories 97 | */ 98 | private listVersionDirs; 99 | /** 100 | * Check whether the package is used by other packages. 101 | * 102 | * @param packageInfo A package information to check 103 | * @param baseDir A base directory to check. If not specified, the rootPath is used. 104 | * @returns true if the package is used by other packages, otherwise false 105 | */ 106 | private checkVersionUsedInDir; 107 | /** 108 | * Check whether the package is used by the specified package. 109 | * 110 | * @param packageInfo A package information to check 111 | * @param packageDir A package directory to check 112 | * @returns true if the package is used by the specified package, otherwise false 113 | */ 114 | private checkVersionUsedFromPackage; 115 | /** 116 | * Uninstall all of the orphaned packages. 117 | * 118 | * @param installedPlugins A list of the installed packages 119 | * @returns A list of the uninstalled packages 120 | */ 121 | private uninstallOrphansLockFree; 122 | /** 123 | * Remove the specified version. 124 | * 125 | * @param pluginInfo A plugin information to remove 126 | */ 127 | private removeVersion; 128 | } 129 | -------------------------------------------------------------------------------- /dist/src/VersionManager.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"VersionManager.js","sourceRoot":"","sources":["../../src/VersionManager.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/src/fileSystem.d.ts: -------------------------------------------------------------------------------- 1 | export { createWriteStream } from "fs-extra"; 2 | export declare function remove(fsPath: string): Promise; 3 | export declare function directoryExists(fsPath: string): Promise; 4 | export declare function fileExists(fsPath: string): Promise; 5 | export declare function ensureDir(fsPath: string): Promise; 6 | export declare function readFile(fsPath: string, encoding: string): Promise; 7 | export declare function readJsonFile(fsPath: string): Promise; 8 | export declare function writeFile(fsPath: string, content: string, encoding?: string): Promise; 9 | export declare function copy(src: string, dest: string, options?: Partial): Promise; 10 | export interface CopyOptions { 11 | exclude: string[]; 12 | } 13 | export declare function pathExists(fsPath: string): Promise; 14 | export declare function access(fsPath: string, mode?: number): Promise; 15 | export declare function readdir(fsPath: string): Promise; 16 | export declare function rename(oldPath: string, newPath: string): Promise; 17 | export declare function symlink(target: string, path: string): Promise; 18 | -------------------------------------------------------------------------------- /dist/src/fileSystem.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | Object.defineProperty(exports, "__esModule", { value: true }); 35 | exports.symlink = exports.rename = exports.readdir = exports.access = exports.pathExists = exports.copy = exports.writeFile = exports.readJsonFile = exports.readFile = exports.ensureDir = exports.fileExists = exports.directoryExists = exports.remove = exports.createWriteStream = void 0; 36 | const fs = __importStar(require("fs-extra")); 37 | const path = __importStar(require("path")); 38 | var fs_extra_1 = require("fs-extra"); 39 | Object.defineProperty(exports, "createWriteStream", { enumerable: true, get: function () { return fs_extra_1.createWriteStream; } }); 40 | function remove(fsPath) { 41 | return fs.remove(fsPath); 42 | } 43 | exports.remove = remove; 44 | function directoryExists(fsPath) { 45 | return __awaiter(this, void 0, void 0, function* () { 46 | try { 47 | const stats = yield fs.stat(fsPath); 48 | return stats.isDirectory(); 49 | } 50 | catch (err) { 51 | if (err.code === "ENOENT") { 52 | return false; 53 | } 54 | throw err; 55 | } 56 | }); 57 | } 58 | exports.directoryExists = directoryExists; 59 | function fileExists(fsPath) { 60 | return __awaiter(this, void 0, void 0, function* () { 61 | try { 62 | const stats = yield fs.stat(fsPath); 63 | return stats.isFile(); 64 | } 65 | catch (err) { 66 | if (err.code === "ENOENT") { 67 | return false; 68 | } 69 | throw err; 70 | } 71 | }); 72 | } 73 | exports.fileExists = fileExists; 74 | function ensureDir(fsPath) { 75 | return fs.ensureDir(fsPath); 76 | } 77 | exports.ensureDir = ensureDir; 78 | function readFile(fsPath, encoding) { 79 | return fs.readFile(fsPath, encoding); 80 | } 81 | exports.readFile = readFile; 82 | function readJsonFile(fsPath) { 83 | return fs.readJson(fsPath); 84 | } 85 | exports.readJsonFile = readJsonFile; 86 | function writeFile(fsPath, content, encoding) { 87 | return fs.writeFile(fsPath, content, { encoding }); 88 | } 89 | exports.writeFile = writeFile; 90 | function copy(src, dest, options) { 91 | const excludeList = options && options.exclude 92 | ? options.exclude.map((f) => path.join(src, f).toLowerCase()) 93 | : []; 94 | const filter = (filterSrc, _filterDest) => { 95 | filterSrc = filterSrc.toLowerCase(); 96 | if (excludeList.indexOf(filterSrc) >= 0) { 97 | return false; 98 | } 99 | return true; 100 | }; 101 | return fs.copy(src, dest, { filter, dereference: true }); 102 | } 103 | exports.copy = copy; 104 | function pathExists(fsPath) { 105 | return fs.pathExists(fsPath); 106 | } 107 | exports.pathExists = pathExists; 108 | function access(fsPath, mode) { 109 | return fs.access(fsPath, mode); 110 | } 111 | exports.access = access; 112 | function readdir(fsPath) { 113 | return fs.readdir(fsPath); 114 | } 115 | exports.readdir = readdir; 116 | function rename(oldPath, newPath) { 117 | return fs.rename(oldPath, newPath); 118 | } 119 | exports.rename = rename; 120 | function symlink(target, path) { 121 | return fs.symlink(target, path); 122 | } 123 | exports.symlink = symlink; 124 | //# sourceMappingURL=fileSystem.js.map -------------------------------------------------------------------------------- /dist/src/fileSystem.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"fileSystem.js","sourceRoot":"","sources":["../../src/fileSystem.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAA+B;AAC/B,2CAA6B;AAE7B,qCAA2C;AAAnC,6GAAA,iBAAiB,OAAA;AAEzB,SAAgB,MAAM,CAAC,MAAc;IACpC,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAFD,wBAEC;AAED,SAAsB,eAAe,CAAC,MAAc;;QACnD,IAAI;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAEpC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;SAC3B;QAAC,OAAO,GAAQ,EAAE;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE;gBAC1B,OAAO,KAAK,CAAC;aACb;YAED,MAAM,GAAG,CAAC;SACV;IACF,CAAC;CAAA;AAZD,0CAYC;AAED,SAAsB,UAAU,CAAC,MAAc;;QAC9C,IAAI;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAEpC,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;SACtB;QAAC,OAAO,GAAQ,EAAE;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE;gBAC1B,OAAO,KAAK,CAAC;aACb;YAED,MAAM,GAAG,CAAC;SACV;IACF,CAAC;CAAA;AAZD,gCAYC;AAED,SAAgB,SAAS,CAAC,MAAc;IACvC,OAAO,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC;AAFD,8BAEC;AAED,SAAgB,QAAQ,CAAC,MAAc,EAAE,QAAgB;IACxD,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC;AAFD,4BAEC;AAED,SAAgB,YAAY,CAAC,MAAc;IAC1C,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAFD,oCAEC;AAED,SAAgB,SAAS,CAAC,MAAc,EAAE,OAAe,EAAE,QAAiB;IAC3E,OAAO,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,EAAC,QAAQ,EAAC,CAAC,CAAC;AAClD,CAAC;AAFD,8BAEC;AAED,SAAgB,IAAI,CAAC,GAAW,EAAE,IAAY,EAAE,OAA8B;IAE7E,MAAM,WAAW,GAAG,OAAO,IAAI,OAAO,CAAC,OAAO;QAC7C,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7D,CAAC,CAAC,EAAE,CAAC;IAEN,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAE,WAAmB,EAAE,EAAE;QACzD,SAAS,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QAEpC,IAAI,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;YACxC,OAAO,KAAK,CAAC;SACb;QACD,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;IAEF,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC;AAhBD,oBAgBC;AAMD,SAAgB,UAAU,CAAC,MAAc;IACxC,OAAO,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAFD,gCAEC;AAED,SAAgB,MAAM,CAAC,MAAc,EAAE,IAAa;IACnD,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAFD,wBAEC;AAED,SAAgB,OAAO,CAAC,MAAc;IACrC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAFD,0BAEC;AAED,SAAgB,MAAM,CAAC,OAAe,EAAE,OAAe;IACtD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAFD,wBAEC;AAED,SAAgB,OAAO,CAAC,MAAc,EAAE,IAAY;IACnD,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC;AAFD,0BAEC"} -------------------------------------------------------------------------------- /dist/src/httpUtils.d.ts: -------------------------------------------------------------------------------- 1 | export interface Headers { 2 | [name: string]: string; 3 | } 4 | export declare function headersBearerAuth(token: string): Headers; 5 | export declare function headersTokenAuth(token: string): Headers; 6 | export declare function headersBasicAuth(username: string, password: string): Headers; 7 | export declare function httpJsonGet(sourceUrl: string, headers?: Headers): Promise; 8 | export declare function httpDownload(sourceUrl: string, destinationFile: string, headers?: Headers): Promise; 9 | -------------------------------------------------------------------------------- /dist/src/httpUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.httpDownload = exports.httpJsonGet = exports.headersBasicAuth = exports.headersTokenAuth = exports.headersBearerAuth = void 0; 39 | const node_fetch_commonjs_1 = __importDefault(require("node-fetch-commonjs")); 40 | const fs = __importStar(require("./fileSystem")); 41 | const debug_1 = __importDefault(require("debug")); 42 | const proxy_agent_1 = require("proxy-agent"); 43 | const debug = (0, debug_1.default)("live-plugin-manager.HttpUtils"); 44 | const agent = new proxy_agent_1.ProxyAgent(); 45 | function headersBearerAuth(token) { 46 | return { 47 | Authorization: "Bearer " + token 48 | }; 49 | } 50 | exports.headersBearerAuth = headersBearerAuth; 51 | function headersTokenAuth(token) { 52 | return { 53 | Authorization: "token " + token 54 | }; 55 | } 56 | exports.headersTokenAuth = headersTokenAuth; 57 | function headersBasicAuth(username, password) { 58 | return { 59 | Authorization: "Basic " + Buffer.from(username + ":" + password).toString("base64") 60 | }; 61 | } 62 | exports.headersBasicAuth = headersBasicAuth; 63 | function httpJsonGet(sourceUrl, headers) { 64 | return __awaiter(this, void 0, void 0, function* () { 65 | if (debug.enabled) { 66 | debug(`Json GET ${sourceUrl} ...`); 67 | debug("HEADERS", headers); 68 | } 69 | const res = yield (0, node_fetch_commonjs_1.default)(sourceUrl, { agent, headers: Object.assign({}, headers) }); 70 | if (debug.enabled) { 71 | debug("Response HEADERS", res.headers); 72 | } 73 | if (!res.ok) { 74 | throw new Error(`Response error ${res.status} ${res.statusText}`); 75 | } 76 | return yield res.json(); 77 | }); 78 | } 79 | exports.httpJsonGet = httpJsonGet; 80 | function httpDownload(sourceUrl, destinationFile, headers) { 81 | return __awaiter(this, void 0, void 0, function* () { 82 | if (debug.enabled) { 83 | debug(`Download GET ${sourceUrl} ...`); 84 | debug("HEADERS", headers); 85 | } 86 | const res = yield (0, node_fetch_commonjs_1.default)(sourceUrl, { agent, headers: Object.assign({}, headers) }); 87 | if (debug.enabled) { 88 | debug("Response HEADERS", res.headers); 89 | } 90 | if (!res.ok) { 91 | throw new Error(`Response error ${res.status} ${res.statusText}`); 92 | } 93 | return new Promise((resolve, reject) => { 94 | const fileStream = fs.createWriteStream(destinationFile); 95 | res.body.pipe(fileStream); 96 | res.body.on("error", (err) => { 97 | fileStream.close(); 98 | fs.fileExists(destinationFile) 99 | .then(fExist => { 100 | if (fExist) { 101 | return fs.remove(destinationFile); 102 | } 103 | }) 104 | .catch((err) => debug(err)); 105 | ; 106 | reject(err); 107 | }); 108 | fileStream.on("finish", function () { 109 | fileStream.close(); 110 | resolve(); 111 | }); 112 | }); 113 | }); 114 | } 115 | exports.httpDownload = httpDownload; 116 | //# sourceMappingURL=httpUtils.js.map -------------------------------------------------------------------------------- /dist/src/httpUtils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"httpUtils.js","sourceRoot":"","sources":["../../src/httpUtils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8EAAwC;AACxC,iDAAmC;AACnC,kDAA0B;AAC1B,6CAAyC;AAEzC,MAAM,KAAK,GAAG,IAAA,eAAK,EAAC,+BAA+B,CAAC,CAAC;AAErD,MAAM,KAAK,GAAG,IAAI,wBAAU,EAAE,CAAC;AAM/B,SAAgB,iBAAiB,CAAC,KAAa;IAC9C,OAAO;QACN,aAAa,EAAE,SAAS,GAAG,KAAK;KAChC,CAAC;AACH,CAAC;AAJD,8CAIC;AAED,SAAgB,gBAAgB,CAAC,KAAa;IAC7C,OAAO;QACN,aAAa,EAAE,QAAQ,GAAG,KAAK;KAC/B,CAAC;AACH,CAAC;AAJD,4CAIC;AAED,SAAgB,gBAAgB,CAAC,QAAgB,EAAE,QAAgB;IAClE,OAAO;QACN,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACnF,CAAC;AACH,CAAC;AAJD,4CAIC;AAED,SAAsB,WAAW,CAAI,SAAiB,EAAE,OAAiB;;QACxE,IAAI,KAAK,CAAC,OAAO,EAAE;YAClB,KAAK,CAAC,YAAY,SAAS,MAAM,CAAC,CAAC;YACnC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;SAC1B;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,6BAAK,EAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,oBAAM,OAAO,CAAC,EAAE,CAAC,CAAC;QAErE,IAAI,KAAK,CAAC,OAAO,EAAE;YAClB,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;SACvC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;SAClE;QAED,OAAO,MAAM,GAAG,CAAC,IAAI,EAAqB,CAAC;IAC5C,CAAC;CAAA;AAhBD,kCAgBC;AAED,SAAsB,YAAY,CAAC,SAAiB,EAAE,eAAuB,EAAE,OAAiB;;QAC/F,IAAI,KAAK,CAAC,OAAO,EAAE;YAClB,KAAK,CAAC,gBAAgB,SAAS,MAAM,CAAC,CAAC;YACvC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;SAC1B;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,6BAAK,EAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,oBAAM,OAAO,CAAC,EAAE,CAAC,CAAC;QAErE,IAAI,KAAK,CAAC,OAAO,EAAE;YAClB,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;SACvC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;SAClE;QAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5C,MAAM,UAAU,GAAG,EAAE,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;YACzD,GAAG,CAAC,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAE3B,GAAG,CAAC,IAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC7B,UAAU,CAAC,KAAK,EAAE,CAAC;gBACnB,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;qBAC5B,IAAI,CAAC,MAAM,CAAC,EAAE;oBACd,IAAI,MAAM,EAAE;wBACX,OAAO,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;qBAClC;gBACF,CAAC,CAAC;qBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAA,CAAC;gBAC9B,MAAM,CAAC,GAAG,CAAC,CAAC;YACb,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE;gBACvB,UAAU,CAAC,KAAK,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACX,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;CAAA;AApCD,oCAoCC"} -------------------------------------------------------------------------------- /dist/src/tarballUtils.d.ts: -------------------------------------------------------------------------------- 1 | import * as httpUtils from "./httpUtils"; 2 | export declare function extractTarball(tgzFile: string, destinationDirectory: string): Promise; 3 | export declare function downloadTarball(url: string, headers?: httpUtils.Headers): Promise; 4 | -------------------------------------------------------------------------------- /dist/src/tarballUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.downloadTarball = exports.extractTarball = void 0; 39 | const os = __importStar(require("os")); 40 | const path = __importStar(require("path")); 41 | const fs = __importStar(require("./fileSystem")); 42 | const tar = __importStar(require("tar")); 43 | const debug_1 = __importDefault(require("debug")); 44 | const httpUtils = __importStar(require("./httpUtils")); 45 | const debug = (0, debug_1.default)("live-plugin-manager.TarballUtils"); 46 | function extractTarball(tgzFile, destinationDirectory) { 47 | return __awaiter(this, void 0, void 0, function* () { 48 | debug(`Extracting ${tgzFile} to ${destinationDirectory} ...`); 49 | yield fs.ensureDir(destinationDirectory); 50 | yield tar.extract({ 51 | file: tgzFile, 52 | cwd: destinationDirectory, 53 | strip: 1 54 | }); 55 | }); 56 | } 57 | exports.extractTarball = extractTarball; 58 | function downloadTarball(url, headers) { 59 | return __awaiter(this, void 0, void 0, function* () { 60 | const destinationFile = path.join(os.tmpdir(), Date.now().toString() + ".tgz"); 61 | // delete file if exists 62 | if (yield fs.fileExists(destinationFile)) { 63 | yield fs.remove(destinationFile); 64 | } 65 | debug(`Downloading ${url} to ${destinationFile} ...`); 66 | yield httpUtils.httpDownload(url, destinationFile, headers); 67 | return destinationFile; 68 | }); 69 | } 70 | exports.downloadTarball = downloadTarball; 71 | //# sourceMappingURL=tarballUtils.js.map -------------------------------------------------------------------------------- /dist/src/tarballUtils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tarballUtils.js","sourceRoot":"","sources":["../../src/tarballUtils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAC7B,iDAAmC;AACnC,yCAA2B;AAC3B,kDAA0B;AAC1B,uDAAyC;AACzC,MAAM,KAAK,GAAG,IAAA,eAAK,EAAC,kCAAkC,CAAC,CAAC;AAExD,SAAsB,cAAc,CAAC,OAAe,EAAE,oBAA4B;;QACjF,KAAK,CAAC,cAAc,OAAO,OAAO,oBAAoB,MAAM,CAAC,CAAC;QAE9D,MAAM,EAAE,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAEzC,MAAM,GAAG,CAAC,OAAO,CAAC;YACjB,IAAI,EAAE,OAAO;YACb,GAAG,EAAE,oBAAoB;YACzB,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;IACJ,CAAC;CAAA;AAVD,wCAUC;AAED,SAAsB,eAAe,CAAC,GAAW,EAAE,OAA2B;;QAC7E,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,CAAC;QAE/E,wBAAwB;QACxB,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE;YACzC,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;SACjC;QAED,KAAK,CAAC,eAAe,GAAG,OAAO,eAAe,MAAM,CAAC,CAAC;QAEtD,MAAM,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;QAE5D,OAAO,eAAe,CAAC;IACxB,CAAC;CAAA;AAbD,0CAaC"} -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from "./src/PluginManager"; 3 | export {IPluginInfo} from "./src/PluginInfo"; 4 | export {PackageInfo, PackageJsonInfo} from "./src/PackageInfo"; 5 | export {NpmRegistryConfig, NpmRegistryAuthBasic, NpmRegistryAuthToken} from "./src/NpmRegistryClient"; 6 | export {GithubAuth} from "./src/GithubRegistryClient"; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "live-plugin-manager", 3 | "version": "1.1.0", 4 | "description": "Install and uninstall any node package at runtime from npm registry", 5 | "keywords": [ 6 | "plugin", 7 | "package", 8 | "npm", 9 | "install" 10 | ], 11 | "main": "dist/index.js", 12 | "typings": "dist/index.d.ts", 13 | "scripts": { 14 | "src-build": "tsc", 15 | "src-build-w": "tsc -w", 16 | "develop": "npm run src-build-w", 17 | "pretest": "tsc -p test", 18 | "test": "mocha --require source-map-support/register" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/davideicardi/live-plugin-manager.git" 23 | }, 24 | "author": "Davide Icardi", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/davideicardi/live-plugin-manager/issues" 28 | }, 29 | "homepage": "https://github.com/davideicardi/live-plugin-manager#readme", 30 | "devDependencies": { 31 | "@types/chai": "^4.3.14", 32 | "@types/mocha": "^9.1.1", 33 | "@types/node": "^16.18.96", 34 | "chai": "^4.4.1", 35 | "mocha": "^9.2.2", 36 | "source-map-support": "^0.5.21", 37 | "typescript": "^4.9.5" 38 | }, 39 | "dependencies": { 40 | "@types/debug": "^4.1.12", 41 | "@types/fs-extra": "^9.0.13", 42 | "@types/lockfile": "^1.0.4", 43 | "@types/node-fetch": "^2.6.11", 44 | "@types/semver": "^7.5.8", 45 | "@types/tar": "^6.1.12", 46 | "@types/url-join": "^4.0.3", 47 | "debug": "^4.3.4", 48 | "fetch-blob": "^4.0.0", 49 | "formdata-polyfill": "^4.0.10", 50 | "fs-extra": "^10.1.0", 51 | "lockfile": "^1.0.4", 52 | "node-fetch-commonjs": "^3.3.2", 53 | "proxy-agent": "^6.4.0", 54 | "semver": "^7.6.0", 55 | "tar": "^6.2.1", 56 | "url-join": "^4.0.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # live-plugin-manager samples 2 | 3 | ## How to run samples 4 | 5 | The easiest way to run these samples is usint `ts-node`. 6 | 7 | ``` 8 | npm install -g ts-node 9 | ts-node ./samples/basic.ts 10 | ``` -------------------------------------------------------------------------------- /samples/basic.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /samples/basic.ts: -------------------------------------------------------------------------------- 1 | import {PluginManager} from "../index"; 2 | 3 | const manager = new PluginManager(); 4 | 5 | async function run() { 6 | await manager.install("moment"); 7 | await manager.install("lodash", "4.17.4"); 8 | 9 | const _ = manager.require("lodash"); 10 | console.log(_.defaults({ a: 1 }, { a: 3, b: 2 })); 11 | 12 | const moment = manager.require("moment"); 13 | console.log(moment().format()); 14 | 15 | await manager.uninstall("moment"); 16 | await manager.uninstall("lodash"); 17 | } 18 | 19 | run(); 20 | -------------------------------------------------------------------------------- /samples/complex.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /samples/express-react.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /samples/express-react.ts: -------------------------------------------------------------------------------- 1 | import {PluginManager} from "../index"; 2 | 3 | const manager = new PluginManager(); 4 | 5 | async function run() { 6 | console.log("Installing express..."); 7 | await manager.install("express", "4.16.2"); 8 | console.log("Installing react..."); 9 | await manager.install("react", "16.0.0"); 10 | console.log("Installing react-dom..."); 11 | await manager.install("react-dom", "16.0.0"); 12 | 13 | const express = manager.require("express"); 14 | const React = manager.require("react"); 15 | const ReactDOMServer = manager.require("react-dom/server"); 16 | 17 | const app = express(); 18 | 19 | app.get("/", function(req: any, res: any) { 20 | 21 | class Hello extends React.Component { 22 | render() { 23 | return React.createElement("div", null, `Hello ${this.props.toWhat} from React!`); 24 | } 25 | } 26 | 27 | const elementToRender = React.createElement(Hello, {toWhat: "World"}, null); 28 | const reactResult = ReactDOMServer.renderToString(elementToRender); 29 | res.send(reactResult); 30 | }); 31 | 32 | const server = app.listen(3000, function() { 33 | console.log("Example app listening on port 3000, closing after 20 secs.!"); 34 | }); 35 | 36 | setTimeout(async () => { 37 | server.close(); 38 | console.log("Uninstalling plugins..."); 39 | await manager.uninstallAll(); 40 | }, 20000); 41 | } 42 | 43 | run() 44 | .catch(console.error.bind(console)); 45 | -------------------------------------------------------------------------------- /samples/express.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /samples/express.ts: -------------------------------------------------------------------------------- 1 | import {PluginManager} from "../index"; 2 | 3 | const manager = new PluginManager(); 4 | 5 | async function run() { 6 | console.log("Installing express..."); 7 | await manager.install("express", "4.16.2"); 8 | 9 | const express = manager.require("express"); 10 | 11 | const app = express(); 12 | 13 | app.get("/", function(req: any, res: any) { 14 | res.send("Hello World!"); 15 | }); 16 | 17 | const server = app.listen(3000, function() { 18 | console.log("Example app listening on port 3000, closing after 20 secs.!"); 19 | }); 20 | 21 | setTimeout(async () => { 22 | server.close(); 23 | console.log("Uninstalling plugins..."); 24 | await manager.uninstallAll(); 25 | }, 20000); 26 | } 27 | 28 | run() 29 | .catch(console.error.bind(console)); 30 | -------------------------------------------------------------------------------- /samples/index.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davideicardi/live-plugin-manager/175ef9ab0ab64ece3644b0be874cd19c22f7ac58/samples/index.d.ts -------------------------------------------------------------------------------- /src/BitbucketRegistryClient.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as fs from "./fileSystem"; 3 | import {Headers, headersBasicAuth, httpJsonGet} from "./httpUtils"; 4 | import Debug from "debug"; 5 | import { downloadTarball, extractTarball } from "./tarballUtils"; 6 | import { PackageJsonInfo } from "./PackageInfo"; 7 | const debug = Debug("live-plugin-manager.BitbucketRegistryClient"); 8 | 9 | export class BitbucketRegistryClient { 10 | private headers: Headers; 11 | 12 | constructor(auth?: BitbucketAuth) { 13 | if (auth) { 14 | debug(`Authenticating Bitbucket api with ${auth.type}...`); 15 | 16 | switch (auth.type) { 17 | case "basic": 18 | this.headers = { 19 | ...headersBasicAuth(auth.username, auth.password), 20 | "user-agent": "live-plugin-manager" 21 | }; 22 | break; 23 | default: 24 | throw new Error("Auth type not supported"); 25 | } 26 | } else { 27 | this.headers = {}; 28 | } 29 | } 30 | 31 | async get(repository: string): Promise { 32 | const repoInfo = extractRepositoryInfo(repository); 33 | 34 | debug("Repository info: ", repoInfo); 35 | 36 | const urlPkg 37 | = `https://api.bitbucket.org/2.0/repositories/${repoInfo.owner}/${repoInfo.repo}/src/${repoInfo.ref}/package.json`; 38 | 39 | const pkgContent = await httpJsonGet( 40 | urlPkg, 41 | {...this.headers, accept: "application/json"}); 42 | if (!pkgContent || !pkgContent.name || !pkgContent.version) { 43 | throw new Error("Invalid plugin Bitbucket repository " + repository); 44 | } 45 | 46 | const urlArchiveLink 47 | = `https://bitbucket.org/${repoInfo.owner}/${repoInfo.repo}/get/${repoInfo.ref}.tar.gz`; 48 | 49 | pkgContent.dist = { tarball: urlArchiveLink }; 50 | 51 | return pkgContent; 52 | } 53 | 54 | async download( 55 | destinationDirectory: string, 56 | packageInfo: PackageJsonInfo): Promise { 57 | 58 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 59 | throw new Error("Invalid dist.tarball property"); 60 | } 61 | 62 | const tgzFile = await downloadTarball(packageInfo.dist.tarball, this.headers); 63 | 64 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 65 | 66 | try { 67 | await extractTarball(tgzFile, pluginDirectory); 68 | } finally { 69 | await fs.remove(tgzFile); 70 | } 71 | 72 | return pluginDirectory; 73 | } 74 | 75 | isBitbucketRepo(version: string): boolean { 76 | return version.indexOf("/") > 0; 77 | } 78 | } 79 | 80 | function extractRepositoryInfo(repository: string) { 81 | const parts = repository.split("/"); 82 | if (parts.length !== 2) { 83 | throw new Error("Invalid repository name"); 84 | } 85 | 86 | const repoParts = parts[1].split("#"); 87 | 88 | const repoInfo = { 89 | owner: parts[0], 90 | repo: repoParts[0], 91 | ref: repoParts[1] || "master" 92 | }; 93 | 94 | return repoInfo; 95 | } 96 | 97 | export interface BitbucketAuthBasic { 98 | type: "basic"; 99 | username: string; 100 | password: string; 101 | } 102 | 103 | export type BitbucketAuth = BitbucketAuthBasic 104 | -------------------------------------------------------------------------------- /src/GithubRegistryClient.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as fs from "./fileSystem"; 3 | import {Headers, headersBasicAuth, headersTokenAuth, httpJsonGet} from "./httpUtils"; 4 | import Debug from "debug"; 5 | import { downloadTarball, extractTarball } from "./tarballUtils"; 6 | import { PackageJsonInfo } from "./PackageInfo"; 7 | const debug = Debug("live-plugin-manager.GithubRegistryClient"); 8 | 9 | export class GithubRegistryClient { 10 | private headers: Headers; 11 | 12 | constructor(auth?: GithubAuth) { 13 | if (auth) { 14 | debug(`Authenticating github api with ${auth.type}...`); 15 | 16 | switch (auth.type) { 17 | case "token": 18 | this.headers = { 19 | ...headersTokenAuth(auth.token), 20 | "user-agent": "live-plugin-manager" 21 | }; 22 | break; 23 | case "basic": 24 | this.headers = { 25 | ...headersBasicAuth(auth.username, auth.password), 26 | "user-agent": "live-plugin-manager" 27 | }; 28 | break; 29 | default: 30 | throw new Error("Auth type not supported"); 31 | } 32 | } else { 33 | this.headers = {}; 34 | } 35 | } 36 | 37 | async get(repository: string): Promise { 38 | const repoInfo = extractRepositoryInfo(repository); 39 | 40 | debug("Repository info: ", repoInfo); 41 | 42 | const urlPkg 43 | = `https://raw.githubusercontent.com/${repoInfo.owner}/${repoInfo.repo}/${repoInfo.ref}/package.json`; 44 | 45 | const pkgContent = await httpJsonGet( 46 | urlPkg, 47 | {...this.headers, accept: "application/vnd.github.v3+json"}); 48 | if (!pkgContent || !pkgContent.name || !pkgContent.version) { 49 | throw new Error("Invalid plugin github repository " + repository); 50 | } 51 | 52 | const urlArchiveLink 53 | = `https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/tarball/${repoInfo.ref}`; 54 | 55 | pkgContent.dist = { tarball: urlArchiveLink }; 56 | 57 | return pkgContent; 58 | } 59 | 60 | async download( 61 | destinationDirectory: string, 62 | packageInfo: PackageJsonInfo): Promise { 63 | 64 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 65 | throw new Error("Invalid dist.tarball property"); 66 | } 67 | 68 | const tgzFile = await downloadTarball(packageInfo.dist.tarball); 69 | 70 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 71 | 72 | try { 73 | await extractTarball(tgzFile, pluginDirectory); 74 | } finally { 75 | await fs.remove(tgzFile); 76 | } 77 | 78 | return pluginDirectory; 79 | } 80 | 81 | isGithubRepo(version: string): boolean { 82 | return version.indexOf("/") > 0; 83 | } 84 | } 85 | 86 | function extractRepositoryInfo(repository: string) { 87 | const parts = repository.split("/"); 88 | if (parts.length !== 2) { 89 | throw new Error("Invalid repository name"); 90 | } 91 | 92 | const repoParts = parts[1].split("#"); 93 | 94 | const repoInfo = { 95 | owner: parts[0], 96 | repo: repoParts[0], 97 | ref: repoParts[1] || "master" 98 | }; 99 | 100 | return repoInfo; 101 | } 102 | 103 | export interface GithubAuthUserToken { 104 | type: "token"; 105 | token: string; 106 | } 107 | 108 | export interface GithubAuthBasic { 109 | type: "basic"; 110 | username: string; 111 | password: string; 112 | } 113 | 114 | export type GithubAuth = GithubAuthUserToken 115 | | GithubAuthBasic; 116 | // | AuthOAuthToken 117 | // | AuthOAuthSecret 118 | // | AuthUserToken 119 | // | AuthJWT; 120 | 121 | 122 | 123 | 124 | 125 | // Implementation using github api 126 | // no more used because the new version doesn't have an easy way to get the download link 127 | // https://github.com/octokit/discussions/issues/12 128 | 129 | // import * as path from "path"; 130 | // import * as fs from "./fileSystem"; 131 | // import * as GitHubApi from "github"; 132 | // import * as Debug from "debug"; 133 | // import { downloadTarball, extractTarball } from "./tarballUtils"; 134 | // import { PackageJsonInfo } from "./PackageInfo"; 135 | // const debug = Debug("live-plugin-manager.GithubRegistryClient"); 136 | 137 | // export class GithubRegistryClient { 138 | // private readonly gitHubApi = new GitHubApi({followRedirects: false}); 139 | 140 | // constructor(auth?: GitHubApi.Auth) { 141 | // if (auth) { 142 | // debug(`Authenticating github api with ${auth.type}...`); 143 | // this.gitHubApi.authenticate(auth); 144 | // } 145 | // } 146 | 147 | // async get(repository: string): Promise { 148 | // const repoInfo = extractRepositoryInfo(repository); 149 | 150 | // debug("Repository info: ", repoInfo); 151 | 152 | // const response = await this.gitHubApi.repos.getContent({ 153 | // ...repoInfo, 154 | // path: "package.json" 155 | // }); 156 | 157 | // const contentBuff = new Buffer(response.data.content, "base64"); 158 | // const contentString = contentBuff.toString("utf-8"); 159 | // const pkgContent = JSON.parse(contentString) as PackageJsonInfo; 160 | // if (!pkgContent.name || !pkgContent.version) { 161 | // throw new Error("Invalid plugin github repository " + repository); 162 | // } 163 | 164 | // debug("Repository package info: ", pkgContent.name, pkgContent.version); 165 | 166 | // // https://github.com/jashkenas/underscore/archive/master.zip 167 | // // https://codeload.github.com/jashkenas/underscore/legacy.tar.gz/master 168 | // const archiveLinkResponse = await this.gitHubApi.repos.getArchiveLink({ 169 | // ...repoInfo, 170 | // archive_format: "tarball" 171 | // }); 172 | 173 | // const archiveLink = archiveLinkResponse.meta.location; 174 | // if (!(typeof archiveLink === "string")) { 175 | // throw new Error("Invalid archive link"); 176 | // } 177 | 178 | // debug("Repository package archive: ", archiveLink); 179 | 180 | // pkgContent.dist = { tarball: archiveLink }; 181 | 182 | // return pkgContent; 183 | // } 184 | 185 | // async download( 186 | // destinationDirectory: string, 187 | // packageInfo: PackageJsonInfo): Promise { 188 | 189 | // if (!packageInfo.dist || !packageInfo.dist.tarball) { 190 | // throw new Error("Invalid dist.tarball property"); 191 | // } 192 | 193 | // const tgzFile = await downloadTarball(packageInfo.dist.tarball); 194 | 195 | // const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 196 | 197 | // try { 198 | // await extractTarball(tgzFile, pluginDirectory); 199 | // } finally { 200 | // await fs.remove(tgzFile); 201 | // } 202 | 203 | // return pluginDirectory; 204 | // } 205 | 206 | // isGithubRepo(version: string): boolean { 207 | // return version.indexOf("/") > 0; 208 | // } 209 | // } 210 | 211 | // function extractRepositoryInfo(repository: string) { 212 | // const parts = repository.split("/"); 213 | // if (parts.length !== 2) { 214 | // throw new Error("Invalid repository name"); 215 | // } 216 | 217 | // const repoParts = parts[1].split("#"); 218 | 219 | // const repoInfo = { 220 | // owner: parts[0], 221 | // repo: repoParts[0], 222 | // ref: repoParts[1] || "master" 223 | // }; 224 | 225 | // return repoInfo; 226 | // } 227 | -------------------------------------------------------------------------------- /src/NpmRegistryClient.ts: -------------------------------------------------------------------------------- 1 | import urlJoin = require("url-join"); 2 | import * as path from "path"; 3 | import * as fs from "./fileSystem"; 4 | import { downloadTarball, extractTarball } from "./tarballUtils"; 5 | import * as semVer from "semver"; 6 | import * as httpUtils from "./httpUtils"; 7 | import { PackageInfo } from "./PackageInfo"; 8 | import Debug from "debug"; 9 | const debug = Debug("live-plugin-manager.NpmRegistryClient"); 10 | 11 | export class NpmRegistryClient { 12 | defaultHeaders: httpUtils.Headers; 13 | constructor(private readonly npmUrl: string, config: NpmRegistryConfig) { 14 | const staticHeaders = { 15 | // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md 16 | "accept-encoding": "gzip", 17 | "accept": "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*", 18 | "user-agent": config.userAgent || "live-plugin-manager" 19 | }; 20 | 21 | const authHeader = createAuthHeader(config.auth); 22 | 23 | this.defaultHeaders = {...staticHeaders, ...authHeader}; 24 | } 25 | 26 | async get(name: string, versionOrTag: string | null = "latest"): Promise { 27 | debug(`Getting npm info for ${name}:${versionOrTag}...`); 28 | 29 | if (typeof versionOrTag !== "string") { 30 | versionOrTag = ""; 31 | } 32 | if (typeof name !== "string") { 33 | throw new Error("Invalid package name"); 34 | } 35 | 36 | const data = await this.getNpmData(name); 37 | versionOrTag = versionOrTag.trim(); 38 | 39 | // check if there is a tag (es. latest) 40 | const distTags = data["dist-tags"]; 41 | let version = distTags && distTags[versionOrTag]; 42 | 43 | if (!version) { 44 | version = semVer.clean(versionOrTag) || versionOrTag; 45 | } 46 | 47 | // find correct version 48 | let pInfo = data.versions[version]; 49 | if (!pInfo) { 50 | // find compatible version 51 | for (const pVersion in data.versions) { 52 | if (!data.versions.hasOwnProperty(pVersion)) { 53 | continue; 54 | } 55 | const pVersionInfo = data.versions[pVersion]; 56 | 57 | if (!semVer.satisfies(pVersionInfo.version, version)) { 58 | continue; 59 | } 60 | 61 | if (!pInfo || semVer.gt(pVersionInfo.version, pInfo.version)) { 62 | pInfo = pVersionInfo; 63 | } 64 | } 65 | } 66 | 67 | if (!pInfo) { 68 | throw new Error(`Version '${versionOrTag} not found`); 69 | } 70 | 71 | return { 72 | dist: pInfo.dist, 73 | name: pInfo.name, 74 | version: pInfo.version 75 | }; 76 | } 77 | 78 | async download( 79 | destinationDirectory: string, 80 | packageInfo: PackageInfo): Promise { 81 | 82 | if (!packageInfo.dist || !packageInfo.dist.tarball) { 83 | throw new Error("Invalid dist.tarball property"); 84 | } 85 | 86 | const tgzFile = await downloadTarball(packageInfo.dist.tarball, this.defaultHeaders); 87 | 88 | const pluginDirectory = path.join(destinationDirectory, packageInfo.name); 89 | try { 90 | await extractTarball(tgzFile, pluginDirectory); 91 | } finally { 92 | await fs.remove(tgzFile); 93 | } 94 | 95 | return pluginDirectory; 96 | } 97 | 98 | private async getNpmData(name: string): Promise { 99 | const regUrl = urlJoin(this.npmUrl, encodeNpmName(name)); 100 | const headers = this.defaultHeaders; 101 | try { 102 | const result = await httpUtils.httpJsonGet(regUrl, headers); 103 | if (!result) { 104 | throw new Error("Response is empty"); 105 | } 106 | if (!result.versions 107 | || !result.name) { 108 | throw new Error("Invalid json format"); 109 | } 110 | 111 | return result; 112 | } catch (err: any) { 113 | if (err.message) { 114 | err.message = `Failed to get package '${name}' ${err.message}`; 115 | } 116 | throw err; 117 | } 118 | } 119 | } 120 | 121 | // example: https://registry.npmjs.org/lodash/ 122 | // or https://registry.npmjs.org/@types%2Fnode (for scoped) 123 | interface NpmData { 124 | name: string; 125 | "dist-tags"?: { 126 | // "latest": "1.0.0"; 127 | [tag: string]: string; 128 | }; 129 | versions: { 130 | [version: string]: { 131 | dist: { 132 | tarball: string; 133 | }, 134 | name: string, 135 | version: string 136 | } 137 | }; 138 | } 139 | 140 | function encodeNpmName(name: string) { 141 | return name.replace("/", "%2F"); 142 | } 143 | 144 | export interface NpmRegistryConfig { 145 | // actually this is used in the params 146 | auth?: NpmRegistryAuthToken | NpmRegistryAuthBasic; 147 | 148 | userAgent?: string; 149 | } 150 | 151 | export interface NpmRegistryAuthToken { 152 | token: string; 153 | } 154 | 155 | export interface NpmRegistryAuthBasic { 156 | username: string; 157 | password: string; 158 | } 159 | 160 | function createAuthHeader(auth?: NpmRegistryAuthToken | NpmRegistryAuthBasic): httpUtils.Headers { 161 | if (!auth) { 162 | return {}; 163 | } 164 | 165 | if (isTokenAuth(auth)) { 166 | return httpUtils.headersBearerAuth(auth.token); // this should be a JWT I think... 167 | } else if (isBasicAuth(auth)) { 168 | return httpUtils.headersBasicAuth(auth.username, auth.password); 169 | } else { 170 | return {}; 171 | } 172 | } 173 | 174 | function isTokenAuth(arg: NpmRegistryAuthToken | NpmRegistryAuthBasic): arg is NpmRegistryAuthToken { 175 | return (arg as NpmRegistryAuthToken).token !== undefined; 176 | } 177 | 178 | function isBasicAuth(arg: NpmRegistryAuthToken | NpmRegistryAuthBasic): arg is NpmRegistryAuthBasic { 179 | return (arg as NpmRegistryAuthBasic).username !== undefined; 180 | } 181 | -------------------------------------------------------------------------------- /src/PackageInfo.ts: -------------------------------------------------------------------------------- 1 | export interface PackageJsonInfo extends PackageInfo { 2 | main?: string; 3 | dependencies?: { [name: string]: string }; 4 | optionalDependencies?: { [name: string]: string }; 5 | } 6 | 7 | export interface PackageInfo { 8 | name: string; 9 | version: string; 10 | dist?: { 11 | tarball: string 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/PluginInfo.ts: -------------------------------------------------------------------------------- 1 | import { PackageJsonInfo } from './PackageInfo'; 2 | 3 | export interface IPluginInfo { 4 | readonly mainFile: string; 5 | readonly location: string; 6 | readonly name: string; 7 | readonly version: string; 8 | readonly dependencies: { [name: string]: string }; 9 | readonly optionalDependencies?: { [name: string]: string }; 10 | readonly dependencyDetails?: { 11 | [name: string]: PackageJsonInfo | undefined; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/PluginVm.ts: -------------------------------------------------------------------------------- 1 | import * as vm from "vm"; 2 | import * as fs from "fs-extra"; 3 | import * as path from "path"; 4 | import * as console from "console"; 5 | import {PluginManager} from "./PluginManager"; 6 | import {IPluginInfo} from "./PluginInfo"; 7 | import Debug from "debug"; 8 | import { PluginSandbox } from "../index"; 9 | const debug = Debug("live-plugin-manager.PluginVm"); 10 | 11 | const SCOPED_REGEX = /^(@[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_]+)(.*)/; 12 | 13 | type NodeJSGlobal = typeof global; 14 | 15 | export class PluginVm { 16 | private requireCache = new Map>(); 17 | private sandboxCache = new Map(); 18 | 19 | constructor(private readonly manager: PluginManager) { 20 | } 21 | 22 | unload(pluginContext: IPluginInfo): void { 23 | this.requireCache.delete(pluginContext); 24 | this.sandboxCache.delete(pluginContext); 25 | } 26 | 27 | load(pluginContext: IPluginInfo, filePath: string): any { 28 | let moduleInstance = this.getCache(pluginContext, filePath); 29 | if (moduleInstance) { 30 | if (debug.enabled) { 31 | debug(`${filePath} loaded from cache`); 32 | } 33 | return moduleInstance.exports; 34 | } 35 | 36 | if (debug.enabled) { 37 | debug(`Loading ${filePath} ...`); 38 | } 39 | 40 | const sandbox = this.createModuleSandbox(pluginContext, filePath); 41 | moduleInstance = sandbox.module; 42 | 43 | const filePathExtension = path.extname(filePath).toLowerCase(); 44 | if (filePathExtension === ".js" || filePathExtension === ".cjs") { 45 | const code = fs.readFileSync(filePath, "utf8"); 46 | // note: I first put the object (before executing the script) in cache to support circular require 47 | this.setCache(pluginContext, filePath, moduleInstance); 48 | 49 | try { 50 | this.vmRunScriptInSandbox(sandbox, filePath, code); 51 | } catch (e) { 52 | // in case of error remove the cache 53 | this.removeCache(pluginContext, filePath); 54 | throw e; 55 | } 56 | } else if (filePathExtension === ".json") { 57 | sandbox.module.exports = fs.readJsonSync(filePath); 58 | this.setCache(pluginContext, filePath, moduleInstance); 59 | } else { 60 | throw new Error("Invalid javascript file " + filePath); 61 | } 62 | 63 | moduleInstance.loaded = true; 64 | 65 | return moduleInstance.exports; 66 | } 67 | 68 | resolve(pluginContext: IPluginInfo, filePath: string): string { 69 | return this.sandboxResolve(pluginContext, pluginContext.location, filePath); 70 | } 71 | 72 | runScript(code: string): any { 73 | const name = "dynamic-" + Date.now; 74 | const filePath = path.join(this.manager.options.pluginsPath, name + ".js"); 75 | const pluginContext: IPluginInfo = { 76 | location: path.join(this.manager.options.pluginsPath, name), 77 | mainFile: filePath, 78 | name, 79 | version: "1.0.0", 80 | dependencies: {}, 81 | dependencyDetails: {} 82 | }; 83 | 84 | try { 85 | return this.vmRunScriptInPlugin(pluginContext, filePath, code); 86 | } finally { 87 | this.unload(pluginContext); 88 | } 89 | } 90 | 91 | splitRequire(fullName: string) { 92 | const scopedInfo = this.getScopedInfo(fullName); 93 | if (scopedInfo) { 94 | return scopedInfo; 95 | } 96 | 97 | const slashPosition = fullName.indexOf("/"); 98 | let requiredPath: string | undefined; 99 | let pluginName = fullName; 100 | if (slashPosition > 0) { 101 | pluginName = fullName.substring(0, slashPosition); 102 | requiredPath = "." + fullName.substring(slashPosition); 103 | } 104 | 105 | return { pluginName, requiredPath }; 106 | } 107 | 108 | private getScopedInfo(fullName: string) { 109 | const match = SCOPED_REGEX.exec(fullName); 110 | if (!match) { 111 | return undefined; 112 | } 113 | 114 | const requiredPath = match[2] 115 | ? "." + match[2] 116 | : undefined; 117 | 118 | return { 119 | pluginName: match[1], 120 | requiredPath 121 | }; 122 | } 123 | 124 | private vmRunScriptInSandbox(moduleSandbox: ModuleSandbox, filePath: string, code: string): void { 125 | const moduleContext = vm.createContext(moduleSandbox); 126 | 127 | // For performance reasons wrap code in a Immediately-invoked function expression 128 | // https://60devs.com/executing-js-code-with-nodes-vm-module.html 129 | // I have also declared the exports variable to support the 130 | // `var app = exports = module.exports = {};` notation 131 | const iifeCode = ` 132 | (function(exports){ 133 | ${code} 134 | }(module.exports));`; 135 | 136 | const vmOptions = { displayErrors: true, filename: filePath }; 137 | const script = new vm.Script(iifeCode, vmOptions); 138 | 139 | script.runInContext(moduleContext, vmOptions); 140 | } 141 | 142 | private vmRunScriptInPlugin(pluginContext: IPluginInfo, filePath: string, code: string): any { 143 | const sandbox = this.createModuleSandbox(pluginContext, filePath); 144 | 145 | this.vmRunScriptInSandbox(sandbox, filePath, code); 146 | 147 | sandbox.module.loaded = true; 148 | 149 | return sandbox.module.exports; 150 | } 151 | 152 | private getCache(pluginContext: IPluginInfo, filePath: string): NodeModule | undefined { 153 | const moduleCache = this.requireCache.get(pluginContext); 154 | if (!moduleCache) { 155 | return undefined; 156 | } 157 | 158 | return moduleCache.get(filePath); 159 | } 160 | 161 | private setCache(pluginContext: IPluginInfo, filePath: string, instance: NodeModule): void { 162 | let moduleCache = this.requireCache.get(pluginContext); 163 | if (!moduleCache) { 164 | moduleCache = new Map(); 165 | this.requireCache.set(pluginContext, moduleCache); 166 | } 167 | 168 | moduleCache.set(filePath, instance); 169 | } 170 | 171 | private removeCache(pluginContext: IPluginInfo, filePath: string): void { 172 | const moduleCache = this.requireCache.get(pluginContext); 173 | if (!moduleCache) { 174 | return; 175 | } 176 | 177 | moduleCache.delete(filePath); 178 | } 179 | 180 | private createModuleSandbox(pluginContext: IPluginInfo, filePath: string): ModuleSandbox { 181 | 182 | const pluginSandbox = this.getPluginSandbox(pluginContext); 183 | 184 | const moduleDirname = path.dirname(filePath); 185 | 186 | const moduleResolve: RequireResolve = Object.assign( 187 | (id: string) => { 188 | return this.sandboxResolve(pluginContext, moduleDirname, id); 189 | }, 190 | { 191 | paths: (_request: string) => null // TODO I should I populate this 192 | } 193 | ); 194 | 195 | const moduleRequire: NodeRequire = Object.assign( 196 | (requiredName: string) => { 197 | if (debug.enabled) { 198 | debug(`Requiring '${requiredName}' from ${filePath}...`); 199 | } 200 | return this.sandboxRequire(pluginContext, moduleDirname, requiredName); 201 | }, 202 | { 203 | resolve: moduleResolve, 204 | cache: {}, // TODO This should be correctly populated 205 | extensions: {} as NodeJS.RequireExtensions, 206 | main: require.main // TODO assign the real main or consider main the current module (ie. module)? 207 | } 208 | ); 209 | 210 | const myModule: NodeModule = { 211 | exports: {}, 212 | filename: filePath, 213 | id: filePath, 214 | loaded: false, 215 | require: moduleRequire, 216 | paths: [], // TODO I should I populate this 217 | parent: module, // TODO I assign parent to the current module...it is correct? 218 | children: [], // TODO I should populate correctly this list... 219 | path: moduleDirname, 220 | isPreloading: false 221 | }; 222 | 223 | // assign missing https://nodejs.org/api/globals.html 224 | // and other "not real global" objects 225 | const moduleSandbox: ModuleSandbox = { 226 | ...pluginSandbox, 227 | module: myModule, 228 | __dirname: moduleDirname, 229 | __filename: filePath, 230 | require: moduleRequire 231 | }; 232 | 233 | return moduleSandbox; 234 | } 235 | 236 | private sandboxResolve(pluginContext: IPluginInfo, moduleDirName: string, requiredName: string): string { 237 | // I try to use a similar logic of https://nodejs.org/api/modules.html#modules_modules 238 | 239 | // is a relative module or absolute path 240 | if (requiredName.startsWith(".") || path.isAbsolute(requiredName)) { 241 | const fullPath = path.resolve(moduleDirName, requiredName); 242 | 243 | // for security reason check to not load external files 244 | if (!fullPath.startsWith(pluginContext.location)) { 245 | throw new Error("Cannot require a module outside a plugin"); 246 | } 247 | 248 | const isFile = this.tryResolveAsFile(fullPath); 249 | if (isFile) { 250 | return isFile; 251 | } 252 | 253 | const isDirectory = this.tryResolveAsDirectory(fullPath); 254 | if (isDirectory) { 255 | return isDirectory; 256 | } 257 | 258 | throw new Error(`Cannot find ${requiredName} in plugin ${pluginContext.name}`); 259 | } 260 | 261 | if (this.hasDependency(pluginContext, requiredName)) { 262 | let fullPath = path.join(pluginContext.location, "node_modules", requiredName); 263 | if (!pluginContext.dependencyDetails) { 264 | throw new Error(`Dependencies not loaded for plugin ${pluginContext.name}`); 265 | } 266 | const packageJson = pluginContext.dependencyDetails[requiredName]; 267 | if (!packageJson) { 268 | throw new Error(`${pluginContext.name} does not include ${requiredName} in local dependencies`); 269 | } 270 | if (packageJson.main) { 271 | fullPath = path.join(fullPath, packageJson.main); 272 | } 273 | 274 | const isFile = this.tryResolveAsFile(fullPath); 275 | if (isFile) { 276 | return isFile; 277 | } 278 | 279 | const isDirectory = this.tryResolveAsDirectory(fullPath); 280 | if (isDirectory) { 281 | return isDirectory; 282 | } 283 | 284 | throw new Error(`Cannot find ${requiredName} in plugin ${pluginContext.name}`); 285 | } 286 | 287 | if (this.isPlugin(requiredName)) { 288 | return requiredName; 289 | } 290 | 291 | if (this.manager.options.staticDependencies[requiredName]) { 292 | return requiredName; 293 | } 294 | 295 | // this will fail if module is unknown 296 | if (this.isCoreModule(requiredName)) { 297 | return requiredName; 298 | } 299 | 300 | return requiredName; 301 | } 302 | 303 | private sandboxRequire(pluginContext: IPluginInfo, moduleDirName: string, requiredName: string) { 304 | // I try to use a similar logic of https://nodejs.org/api/modules.html#modules_modules 305 | 306 | const fullName = this.sandboxResolve(pluginContext, moduleDirName, requiredName); 307 | 308 | // is an absolute file or directory that can be loaded 309 | if (path.isAbsolute(fullName)) { 310 | if (debug.enabled) { 311 | debug(`Resolved ${requiredName} as file ${fullName}`); 312 | } 313 | return this.load(pluginContext, fullName); 314 | } 315 | 316 | if (this.manager.options.staticDependencies[requiredName]) { 317 | if (debug.enabled) { 318 | debug(`Resolved ${requiredName} as static dependency`); 319 | } 320 | return this.manager.options.staticDependencies[requiredName]; 321 | } 322 | 323 | if (this.isPlugin(requiredName)) { 324 | if (debug.enabled) { 325 | debug(`Resolved ${requiredName} as plugin`); 326 | } 327 | return this.manager.require(requiredName); 328 | } 329 | 330 | if (this.isCoreModule(requiredName)) { 331 | if (debug.enabled) { 332 | debug(`Resolved ${requiredName} as core module`); 333 | } 334 | return require(requiredName); // I use system require 335 | } 336 | 337 | if (this.manager.options.hostRequire) { 338 | if (debug.enabled) { 339 | debug(`Resolved ${requiredName} as host module`); 340 | } 341 | return this.manager.options.hostRequire(requiredName); 342 | } 343 | 344 | throw new Error(`Module ${requiredName} not found, failed to load plugin ${pluginContext.name}`); 345 | } 346 | 347 | private isCoreModule(requiredName: string): boolean { 348 | return this.manager.options.requireCoreModules 349 | && require.resolve(requiredName) === requiredName; 350 | } 351 | 352 | private isPlugin(requiredName: string): boolean { 353 | const { pluginName } = this.splitRequire(requiredName); 354 | 355 | return !!this.manager.getInfo(pluginName); 356 | } 357 | 358 | private hasDependency(pluginContext: IPluginInfo, requiredName: string) { 359 | const { dependencyDetails } = pluginContext; 360 | if (!dependencyDetails) { 361 | return false; 362 | } 363 | return !!dependencyDetails[requiredName]; 364 | } 365 | 366 | private tryResolveAsFile(fullPath: string): string | undefined { 367 | 368 | const parentPath = path.dirname(fullPath); 369 | if (checkPath(parentPath) !== "directory") { 370 | return undefined; 371 | } 372 | 373 | const reqPathKind = checkPath(fullPath); 374 | 375 | if (reqPathKind !== "file") { 376 | if (checkPath(fullPath + ".cjs") === "file") { 377 | return fullPath + ".cjs"; 378 | } 379 | 380 | if (checkPath(fullPath + ".js") === "file") { 381 | return fullPath + ".js"; 382 | } 383 | 384 | if (checkPath(fullPath + ".json") === "file") { 385 | return fullPath + ".json"; 386 | } 387 | 388 | return undefined; 389 | } 390 | 391 | if (reqPathKind === "file") { 392 | return fullPath; 393 | } 394 | 395 | return undefined; 396 | } 397 | 398 | private tryResolveAsDirectory(fullPath: string): string | undefined { 399 | if (checkPath(fullPath) !== "directory") { 400 | return undefined; 401 | } 402 | 403 | const indexCjs = path.join(fullPath, "index.cjs"); 404 | if (checkPath(indexCjs) === "file") { 405 | return indexCjs; 406 | } 407 | 408 | const indexJs = path.join(fullPath, "index.js"); 409 | if (checkPath(indexJs) === "file") { 410 | return indexJs; 411 | } 412 | 413 | const indexJson = path.join(fullPath, "index.json"); 414 | if (checkPath(indexJson) === "file") { 415 | return indexJson; 416 | } 417 | 418 | return undefined; 419 | } 420 | 421 | private getPluginSandbox(pluginContext: IPluginInfo): NodeJSGlobal { 422 | let pluginSandbox = this.sandboxCache.get(pluginContext); 423 | if (!pluginSandbox) { 424 | const srcSandboxTemplate = this.manager.getSandboxTemplate(pluginContext.name) 425 | || this.manager.options.sandbox; 426 | 427 | pluginSandbox = this.createGlobalSandbox(srcSandboxTemplate); 428 | 429 | this.sandboxCache.set(pluginContext, pluginSandbox); 430 | } 431 | 432 | return pluginSandbox; 433 | } 434 | 435 | private createGlobalSandbox(sandboxTemplate: PluginSandbox): NodeJSGlobal { 436 | const srcGlobal = sandboxTemplate.global || global; 437 | 438 | const sandbox: NodeJSGlobal = {...srcGlobal}; 439 | 440 | // copy properties that are not copied automatically (don't know why..) 441 | // https://stackoverflow.com/questions/59009214/some-properties-of-the-global-instance-are-not-copied-by-spread-operator-or-by-o 442 | // (some of these properties are Node.js specific, like Buffer) 443 | // Function and Object should not be defined, otherwise we will have some unexpected behavior 444 | // Somewhat related to https://github.com/nodejs/node/issues/28823 445 | if (!sandbox.Buffer && srcGlobal.Buffer) { 446 | sandbox.Buffer = srcGlobal.Buffer; 447 | } 448 | if (!(sandbox as any).URL && global.URL) { 449 | // cast to any because URL is not defined inside NodeJSGlobal, I don't understand why ... 450 | (sandbox as any).URL = global.URL; 451 | } 452 | if (!(sandbox as any).URLSearchParams && global.URLSearchParams) { 453 | // cast to any because URLSearchParams is not defined inside NodeJSGlobal, I don't understand why ... 454 | (sandbox as any).URLSearchParams = global.URLSearchParams; 455 | } 456 | if (!sandbox.process && global.process) { 457 | sandbox.process = {...global.process}; 458 | } 459 | if (sandbox.process) { 460 | // override env to "unlink" from original process 461 | const srcEnv = sandboxTemplate.env || global.process.env; 462 | sandbox.process.env = {...srcEnv}; // copy properties 463 | (sandbox as any).process.on = (event: string, callback: any) => {}; 464 | } 465 | 466 | // create global console 467 | if (!sandbox.console) { 468 | sandbox.console = new console.Console({ stdout: process.stdout, stderr: process.stderr }); 469 | } 470 | 471 | // override the global obj to "unlink" it from the original global obj 472 | // and make it unique for each sandbox 473 | sandbox.global = sandbox; 474 | 475 | return sandbox; 476 | } 477 | } 478 | 479 | function checkPath(fullPath: string): "file" | "directory" | "none" { 480 | try { 481 | const stats = fs.statSync(fullPath); 482 | if (stats.isDirectory()) { 483 | return "directory"; 484 | } else if (stats.isFile()) { 485 | return "file"; 486 | } else { 487 | return "none"; 488 | } 489 | } catch { 490 | return "none"; 491 | } 492 | } 493 | 494 | interface ModuleSandbox extends NodeJSGlobal { 495 | module: NodeModule; 496 | __dirname: string; 497 | __filename: string; 498 | require: NodeRequire; 499 | } 500 | -------------------------------------------------------------------------------- /src/VersionManager.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "./fileSystem"; 2 | import * as path from "path"; 3 | import { IPluginInfo } from "./PluginInfo"; 4 | import * as semver from "semver"; 5 | import Debug from "debug"; 6 | import { PackageJsonInfo, PackageInfo } from "./PackageInfo"; 7 | const debug = Debug("live-plugin-manager"); 8 | 9 | export interface VersionManagerOptions { 10 | cwd: string; 11 | rootPath: string; 12 | } 13 | 14 | export const DefaultMainFile = "index.js"; 15 | 16 | const cwd = process.cwd(); 17 | function createDefaultOptions(): VersionManagerOptions { 18 | return { 19 | cwd, 20 | rootPath: path.join(cwd, "plugin_packages", ".versions"), 21 | }; 22 | } 23 | 24 | /** 25 | * A class to manage the versions of the downloaded packages. 26 | */ 27 | export class VersionManager { 28 | readonly options: VersionManagerOptions; 29 | 30 | constructor(options?: Partial) { 31 | if (options && !options.rootPath && options.cwd) { 32 | options.rootPath = path.join(options.cwd, "plugin_packages", ".versions"); 33 | } 34 | 35 | this.options = { ...createDefaultOptions(), ...(options || {}) }; 36 | } 37 | 38 | /** 39 | * Ensure the root path exists. 40 | */ 41 | public async ensureRootPath() { 42 | await fs.ensureDir(this.options.rootPath); 43 | } 44 | 45 | /** 46 | * Get the location for the specified package name and version. 47 | * 48 | * @param packageInfo A package information to get the location 49 | * @returns A location for the specified package name and version 50 | */ 51 | public getPath(packageInfo: PackageInfo): string { 52 | const { name, version } = packageInfo; 53 | return path.join(this.options.rootPath, `${name}@${version}`); 54 | } 55 | 56 | /** 57 | * Resolve the path for the specified package name and version. 58 | * 59 | * @param name A package name to resolve 60 | * @param version A package version to resolve 61 | * @returns 62 | */ 63 | public async resolvePath(name: string, version: string): Promise { 64 | await this.ensureRootPath(); 65 | let searchPath = this.options.rootPath; 66 | let moduleName = name; 67 | if (name.includes("/")) { 68 | const index = name.lastIndexOf("/"); 69 | const scope = name.substring(0, index); 70 | searchPath = path.join(searchPath, scope); 71 | moduleName = name.substring(index + 1); 72 | if (!(await fs.directoryExists(searchPath))) { 73 | return undefined; 74 | } 75 | } 76 | const files = await fs.readdir(searchPath); 77 | const filename = files.find((f) => this.checkModuleFilenameSatisfied(f, moduleName, version)); 78 | if (filename === undefined) { 79 | return undefined; 80 | } 81 | return path.join(searchPath, filename); 82 | } 83 | 84 | /** 85 | * Download a package using a downloader. 86 | * Downloaded files are stored in the rootPath as directory named as `name@version`. 87 | * 88 | * @param downloader A downloader object that implements the download method 89 | * @param registryInfo A package info to download 90 | * @returns A information for the downloaded package 91 | */ 92 | public async download(downloader: { 93 | download: (destinationDirectory: string, registryInfo: PackageJsonInfo) => Promise; 94 | }, registryInfo: PackageJsonInfo) { 95 | await this.ensureRootPath(); 96 | const destPath = this.options.rootPath; 97 | await fs.ensureDir(destPath); 98 | 99 | const destPackagePath = await downloader.download(destPath, registryInfo); 100 | const packageJson = await this.readPackageJsonFromPath(destPackagePath); 101 | if (!packageJson) { 102 | throw new Error(`Invalid plugin ${destPackagePath}, package.json is missing`); 103 | } 104 | const versionPath = path.join(destPath, `${packageJson.name}@${packageJson.version}`); 105 | await fs.rename(destPackagePath, versionPath); 106 | if (debug.enabled) { 107 | debug(`Downloaded package ${packageJson.name}@${packageJson.version} to ${versionPath}`); 108 | } 109 | const downloadedJson = await this.readPackageJsonFromPath(versionPath); 110 | if (!downloadedJson) { 111 | throw new Error(`Invalid plugin ${versionPath}, package.json is missing`); 112 | } 113 | return downloadedJson; 114 | } 115 | 116 | /** 117 | * Uninstall packages which are not used by other packages. 118 | * 119 | * @param installedPlugins A list of the installed packages. 120 | * @returns A list of the uninstalled packages. 121 | */ 122 | public async uninstallOrphans(installedPlugins: Array): Promise { 123 | await this.ensureRootPath(); 124 | return await this.uninstallOrphansLockFree(installedPlugins); 125 | } 126 | 127 | /** 128 | * Unload a version of a plugin if it is not used by any other plugin 129 | * 130 | * @param pluginInfo A plugin information to uninstall 131 | * @returns true if the version was unloaded, false if it was used by another plugin 132 | */ 133 | public async uninstallOrphan(pluginInfo: IPluginInfo): Promise { 134 | await this.ensureRootPath(); 135 | const used = await this.checkVersionUsedInDir(pluginInfo); 136 | if (used) { 137 | return false; 138 | } 139 | await this.removeVersion(pluginInfo); 140 | return true; 141 | } 142 | 143 | /** 144 | * Create a plugin information for the specified version. 145 | * 146 | * @param name A package name 147 | * @param version A package version 148 | * @param withDependencies A flag to load dependency packages 149 | * @returns A plugin information for the specified version 150 | */ 151 | public async createVersionInfo(name: string, version: string, withDependencies: boolean = false): Promise { 152 | const location = path.join(this.options.rootPath, `${name}@${version}`); 153 | return await this.createVersionInfoFromPath(location, withDependencies); 154 | } 155 | 156 | /** 157 | * Create a plugin information for the specified path. 158 | * 159 | * @param location A path to the package directory 160 | * @param withDependencies A flag to load dependency packages 161 | * @returns A plugin information for the specified path 162 | */ 163 | public async createVersionInfoFromPath(location: string, withDependencies: boolean = false): Promise { 164 | const packageJson = await this.readPackageJsonFromPath(location); 165 | if (!packageJson) { 166 | throw new Error(`Invalid plugin ${location}, package.json is missing`); 167 | } 168 | 169 | const mainFile = path.normalize(path.join(location, packageJson.main || DefaultMainFile)); 170 | if (!withDependencies) { 171 | return { 172 | name: packageJson.name, 173 | version: packageJson.version, 174 | location, 175 | mainFile, 176 | dependencies: packageJson.dependencies || {}, 177 | optionalDependencies: packageJson.optionalDependencies || {}, 178 | }; 179 | } 180 | 181 | const dependencies = packageJson.dependencies || {}; 182 | const dependencyNames = Object.keys(dependencies); 183 | const dependencyPackageJsons = await Promise.all(dependencyNames.map(async (name) => { 184 | const moduleLocation = path.join(location, "node_modules", name); 185 | return await this.readPackageJsonFromPath(moduleLocation); 186 | })); 187 | const dependencyDetails: { [name: string]: PackageJsonInfo | undefined } = {}; 188 | dependencyPackageJsons.forEach((p, i) => { 189 | dependencyDetails[dependencyNames[i]] = p; 190 | }); 191 | 192 | return { 193 | name: packageJson.name, 194 | version: packageJson.version, 195 | location, 196 | mainFile, 197 | dependencies, 198 | optionalDependencies: packageJson.optionalDependencies || {}, 199 | dependencyDetails, 200 | }; 201 | } 202 | 203 | /** 204 | * Check whether the filename is satisfied with the specified package name and version. 205 | * 206 | * @param filename A filename to check 207 | * @param name A package name to check 208 | * @param version A package version to check 209 | * @returns true if the filename is satisfied with the specified package name and version, otherwise false 210 | */ 211 | private checkModuleFilenameSatisfied(filename: string, name: string, version: string): boolean { 212 | const m = filename.match(/^(.+)@([^@]+)$/); 213 | if (!m) { 214 | return false; 215 | } 216 | if (m[1] !== name) { 217 | return false; 218 | } 219 | return semver.satisfies(m[2], version); 220 | } 221 | 222 | /** 223 | * Get the package information from the package directory. 224 | * 225 | * @param location A path to the package directory 226 | * @returns A package information for the package directory 227 | */ 228 | private async readPackageJsonFromPath(location: string): Promise { 229 | const packageJsonFile = path.join(location, "package.json"); 230 | if (!(await fs.fileExists(packageJsonFile))) { 231 | return undefined; 232 | } 233 | const packageJson = JSON.parse(await fs.readFile(packageJsonFile, "utf8")); 234 | 235 | if (!packageJson.name 236 | || !packageJson.version) { 237 | throw new Error( 238 | `Invalid plugin ${location}, 'main', 'name' and 'version' properties are required in package.json`); 239 | } 240 | 241 | return packageJson; 242 | } 243 | 244 | /** 245 | * List package directories in the specified base directory. 246 | * 247 | * @param baseDir A base directory to list 248 | * @param scope A scope for packages 249 | * @returns A list of the package directories 250 | */ 251 | private async listVersionDirs(baseDir: string, scope?: string): Promise { 252 | const files = await fs.readdir(baseDir); 253 | const versionDirs = []; 254 | for (const file of files) { 255 | if (file === "install.lock" || file === "node_modules") { 256 | continue; 257 | } 258 | const packageJsonPath = path.join(baseDir, file, "package.json"); 259 | if (await fs.fileExists(packageJsonPath)) { 260 | versionDirs.push(scope ? `${scope}/${file}` : file); 261 | continue; 262 | } 263 | const subDir = path.join(baseDir, file); 264 | const subDirs = await this.listVersionDirs(subDir, scope ? `${scope}/${file}` : file); 265 | versionDirs.push(...subDirs); 266 | } 267 | return versionDirs; 268 | } 269 | 270 | /** 271 | * Check whether the package is used by other packages. 272 | * 273 | * @param packageInfo A package information to check 274 | * @param baseDir A base directory to check. If not specified, the rootPath is used. 275 | * @returns true if the package is used by other packages, otherwise false 276 | */ 277 | private async checkVersionUsedInDir( 278 | packageInfo: PackageInfo, baseDir?: string, 279 | ): Promise { 280 | const { name, version } = packageInfo; 281 | const location = baseDir || this.options.rootPath; 282 | const files = await this.listVersionDirs(location); 283 | if (debug.enabled) { 284 | debug(`Checking ${name}@${version} in ${location}`); 285 | } 286 | for (const file of files) { 287 | if (debug.enabled) { 288 | debug(`Checking ${name}@${version} in ${file}`); 289 | } 290 | const used = await this.checkVersionUsedFromPackage(packageInfo, path.join(location, file)); 291 | if (used) { 292 | return true; 293 | } 294 | } 295 | return false; 296 | } 297 | 298 | /** 299 | * Check whether the package is used by the specified package. 300 | * 301 | * @param packageInfo A package information to check 302 | * @param packageDir A package directory to check 303 | * @returns true if the package is used by the specified package, otherwise false 304 | */ 305 | private async checkVersionUsedFromPackage( 306 | packageInfo: PackageInfo, packageDir: string, 307 | ): Promise { 308 | let packageJson: PackageJsonInfo | undefined; 309 | try { 310 | packageJson = await this.readPackageJsonFromPath(packageDir); 311 | } catch (e) { 312 | if (debug.enabled) { 313 | debug(`Cannot load package.json ${packageDir}`, e); 314 | } 315 | return false; 316 | } 317 | if (!packageJson) { 318 | return false; 319 | } 320 | if (!packageJson.dependencies) { 321 | return false; 322 | } 323 | const { name, version } = packageInfo; 324 | if (!packageJson.dependencies[name]) { 325 | return false; 326 | } 327 | if (!semver.validRange(packageJson.dependencies[name])) { 328 | if (debug.enabled) { 329 | debug(`Unexpected version range ${packageJson.dependencies[name]} for ${name}, treated as used.`); 330 | } 331 | return true; 332 | } 333 | if (semver.satisfies(version, packageJson.dependencies[name])) { 334 | if (debug.enabled) { 335 | debug(`Found ${name}@${version} in ${packageDir}`); 336 | } 337 | return true; 338 | } 339 | return false; 340 | } 341 | 342 | /** 343 | * Uninstall all of the orphaned packages. 344 | * 345 | * @param installedPlugins A list of the installed packages 346 | * @returns A list of the uninstalled packages 347 | */ 348 | private async uninstallOrphansLockFree(installedPlugins: Array): Promise { 349 | const rootPath = this.options.rootPath; 350 | const files = await this.listVersionDirs(rootPath); 351 | const orphans = []; 352 | if (debug.enabled) { 353 | debug(`Checking orphans in ${rootPath}`); 354 | } 355 | for (const file of files) { 356 | const fullPath = path.join(rootPath, file); 357 | if (file === "install.lock") { 358 | continue; 359 | } 360 | let packageJson: PackageJsonInfo | undefined; 361 | try { 362 | packageJson = await this.readPackageJsonFromPath(fullPath); 363 | } catch (e) { 364 | if (debug.enabled) { 365 | debug(`Cannot load package.json ${fullPath}`, e); 366 | } 367 | continue; 368 | } 369 | if (!packageJson) { 370 | continue; 371 | } 372 | if (installedPlugins 373 | .find((p) => packageJson && p.name === packageJson.name && p.version === packageJson.version)) { 374 | continue; 375 | } 376 | let used = false; 377 | for (const anotherFile of files) { 378 | if (anotherFile === file) { 379 | continue; 380 | } 381 | if (await this.checkVersionUsedFromPackage(packageJson, path.join(rootPath, anotherFile))) { 382 | used = true; 383 | break; 384 | } 385 | } 386 | if (used) { 387 | continue; 388 | } 389 | orphans.push(packageJson); 390 | } 391 | if (orphans.length === 0) { 392 | return []; 393 | } 394 | const uninstalled = []; 395 | for (const orphan of orphans) { 396 | const pluginInfo = await this.createVersionInfo(orphan.name, orphan.version); 397 | await this.removeVersion(pluginInfo); 398 | uninstalled.push(pluginInfo); 399 | } 400 | return uninstalled.concat(await this.uninstallOrphansLockFree(installedPlugins)); 401 | } 402 | 403 | /** 404 | * Remove the specified version. 405 | * 406 | * @param pluginInfo A plugin information to remove 407 | */ 408 | private async removeVersion(pluginInfo: IPluginInfo) { 409 | const pathSegments = pluginInfo.name.split("/"); 410 | pathSegments[pathSegments.length - 1] = `${pathSegments[pathSegments.length - 1]}@${pluginInfo.version}`; 411 | for (let i = 0; i < pathSegments.length; i++) { 412 | const pathToRemove = path.join(this.options.rootPath, ...pathSegments.slice(0, pathSegments.length - i)); 413 | if (debug.enabled) { 414 | debug(`Removing ${pathToRemove}`); 415 | } 416 | if (!(await fs.directoryExists(pathToRemove))) { 417 | continue; 418 | } 419 | if (i > 0) { 420 | // For scoped packages, need to check if the parent directory is empty 421 | const files = await fs.readdir(pathToRemove); 422 | if (files.length > 0) { 423 | if (debug.enabled) { 424 | debug(`Skip removing ${pathToRemove}, not empty`); 425 | } 426 | break; 427 | } 428 | } 429 | await fs.remove(pathToRemove); 430 | } 431 | } 432 | } -------------------------------------------------------------------------------- /src/fileSystem.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs-extra"; 2 | import * as path from "path"; 3 | 4 | export {createWriteStream} from "fs-extra"; 5 | 6 | export function remove(fsPath: string): Promise { 7 | return fs.remove(fsPath); 8 | } 9 | 10 | export async function directoryExists(fsPath: string): Promise { 11 | try { 12 | const stats = await fs.stat(fsPath); 13 | 14 | return stats.isDirectory(); 15 | } catch (err: any) { 16 | if (err.code === "ENOENT") { 17 | return false; 18 | } 19 | 20 | throw err; 21 | } 22 | } 23 | 24 | export async function fileExists(fsPath: string): Promise { 25 | try { 26 | const stats = await fs.stat(fsPath); 27 | 28 | return stats.isFile(); 29 | } catch (err: any) { 30 | if (err.code === "ENOENT") { 31 | return false; 32 | } 33 | 34 | throw err; 35 | } 36 | } 37 | 38 | export function ensureDir(fsPath: string): Promise { 39 | return fs.ensureDir(fsPath); 40 | } 41 | 42 | export function readFile(fsPath: string, encoding: string): Promise { 43 | return fs.readFile(fsPath, encoding); 44 | } 45 | 46 | export function readJsonFile(fsPath: string): Promise { 47 | return fs.readJson(fsPath); 48 | } 49 | 50 | export function writeFile(fsPath: string, content: string, encoding?: string): Promise { 51 | return fs.writeFile(fsPath, content, {encoding}); 52 | } 53 | 54 | export function copy(src: string, dest: string, options?: Partial): Promise { 55 | 56 | const excludeList = options && options.exclude 57 | ? options.exclude.map((f) => path.join(src, f).toLowerCase()) 58 | : []; 59 | 60 | const filter = (filterSrc: string, _filterDest: string) => { 61 | filterSrc = filterSrc.toLowerCase(); 62 | 63 | if (excludeList.indexOf(filterSrc) >= 0) { 64 | return false; 65 | } 66 | return true; 67 | }; 68 | 69 | return fs.copy(src, dest, { filter, dereference: true }); 70 | } 71 | 72 | export interface CopyOptions { 73 | exclude: string[]; 74 | } 75 | 76 | export function pathExists(fsPath: string): Promise { 77 | return fs.pathExists(fsPath); 78 | } 79 | 80 | export function access(fsPath: string, mode?: number): Promise { 81 | return fs.access(fsPath, mode); 82 | } 83 | 84 | export function readdir(fsPath: string): Promise { 85 | return fs.readdir(fsPath); 86 | } 87 | 88 | export function rename(oldPath: string, newPath: string): Promise { 89 | return fs.rename(oldPath, newPath); 90 | } 91 | 92 | export function symlink(target: string, path: string): Promise { 93 | return fs.symlink(target, path); 94 | } 95 | -------------------------------------------------------------------------------- /src/httpUtils.ts: -------------------------------------------------------------------------------- 1 | import fetch from "node-fetch-commonjs"; 2 | import * as fs from "./fileSystem"; 3 | import Debug from "debug"; 4 | import { ProxyAgent } from 'proxy-agent'; 5 | 6 | const debug = Debug("live-plugin-manager.HttpUtils"); 7 | 8 | const agent = new ProxyAgent(); 9 | 10 | export interface Headers { 11 | [name: string]: string; 12 | } 13 | 14 | export function headersBearerAuth(token: string): Headers { 15 | return { 16 | Authorization: "Bearer " + token 17 | }; 18 | } 19 | 20 | export function headersTokenAuth(token: string): Headers { 21 | return { 22 | Authorization: "token " + token 23 | }; 24 | } 25 | 26 | export function headersBasicAuth(username: string, password: string): Headers { 27 | return { 28 | Authorization: "Basic " + Buffer.from(username + ":" + password).toString("base64") 29 | }; 30 | } 31 | 32 | export async function httpJsonGet(sourceUrl: string, headers?: Headers): Promise { 33 | if (debug.enabled) { 34 | debug(`Json GET ${sourceUrl} ...`); 35 | debug("HEADERS", headers); 36 | } 37 | const res = await fetch(sourceUrl, { agent, headers: {...headers} }); 38 | 39 | if (debug.enabled) { 40 | debug("Response HEADERS", res.headers); 41 | } 42 | 43 | if (!res.ok) { 44 | throw new Error(`Response error ${res.status} ${res.statusText}`); 45 | } 46 | 47 | return await res.json() as (T | undefined); 48 | } 49 | 50 | export async function httpDownload(sourceUrl: string, destinationFile: string, headers?: Headers): Promise { 51 | if (debug.enabled) { 52 | debug(`Download GET ${sourceUrl} ...`); 53 | debug("HEADERS", headers); 54 | } 55 | const res = await fetch(sourceUrl, { agent, headers: {...headers} }); 56 | 57 | if (debug.enabled) { 58 | debug("Response HEADERS", res.headers); 59 | } 60 | 61 | if (!res.ok) { 62 | throw new Error(`Response error ${res.status} ${res.statusText}`); 63 | } 64 | 65 | return new Promise((resolve, reject) => { 66 | const fileStream = fs.createWriteStream(destinationFile); 67 | res.body!.pipe(fileStream); 68 | 69 | res.body!.on("error", (err) => { 70 | fileStream.close(); 71 | fs.fileExists(destinationFile) 72 | .then(fExist => { 73 | if (fExist) { 74 | return fs.remove(destinationFile); 75 | } 76 | }) 77 | .catch((err) => debug(err));; 78 | reject(err); 79 | }); 80 | 81 | fileStream.on("finish", function() { 82 | fileStream.close(); 83 | resolve(); 84 | }); 85 | }); 86 | } 87 | -------------------------------------------------------------------------------- /src/tarballUtils.ts: -------------------------------------------------------------------------------- 1 | import * as os from "os"; 2 | import * as path from "path"; 3 | import * as fs from "./fileSystem"; 4 | import * as tar from "tar"; 5 | import Debug from "debug"; 6 | import * as httpUtils from "./httpUtils"; 7 | const debug = Debug("live-plugin-manager.TarballUtils"); 8 | 9 | export async function extractTarball(tgzFile: string, destinationDirectory: string) { 10 | debug(`Extracting ${tgzFile} to ${destinationDirectory} ...`); 11 | 12 | await fs.ensureDir(destinationDirectory); 13 | 14 | await tar.extract({ 15 | file: tgzFile, 16 | cwd: destinationDirectory, 17 | strip: 1 18 | }); 19 | } 20 | 21 | export async function downloadTarball(url: string, headers?: httpUtils.Headers): Promise { 22 | const destinationFile = path.join(os.tmpdir(), Date.now().toString() + ".tgz"); 23 | 24 | // delete file if exists 25 | if (await fs.fileExists(destinationFile)) { 26 | await fs.remove(destinationFile); 27 | } 28 | 29 | debug(`Downloading ${url} to ${destinationFile} ...`); 30 | 31 | await httpUtils.httpDownload(url, destinationFile, headers); 32 | 33 | return destinationFile; 34 | } 35 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | github_auth.json -------------------------------------------------------------------------------- /test/PluginManagerSuite.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /test/host-dependency@v1.0.1/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "v1.0.1"; 4 | -------------------------------------------------------------------------------- /test/host-dependency@v1.0.1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "host-dependency", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/host-dependency@v1/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "v1.0.0"; 4 | -------------------------------------------------------------------------------- /test/host-dependency@v1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "host-dependency", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/my-basic-plugin-scoped/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.myVariable = "value1"; 3 | -------------------------------------------------------------------------------- /test/my-basic-plugin-scoped/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@myscope/my-basic-plugin-scoped", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-basic-plugin/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.myVariable = "value1"; 3 | exports.installDate = new Date(); 4 | -------------------------------------------------------------------------------- /test/my-basic-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-basic-plugin", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-minimal-plugin/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.myVariable = "value1"; 3 | -------------------------------------------------------------------------------- /test/my-minimal-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-minimal-plugin", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/my-plugin-a@v1/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "v1"; 4 | -------------------------------------------------------------------------------- /test/my-plugin-a@v1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-a", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/my-plugin-a@v2/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "v2"; 4 | -------------------------------------------------------------------------------- /test/my-plugin-a@v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-a", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/my-plugin-b/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const a = require("my-plugin-a"); 4 | 5 | module.exports = "a = " + a; 6 | -------------------------------------------------------------------------------- /test/my-plugin-b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-b", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "my-plugin-a": "1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-env-global/index.js: -------------------------------------------------------------------------------- 1 | 2 | // this is to test that global and env variables are shared between modules 3 | // in the same plugin 4 | 5 | process.env.HELLO_VAR = "Hello"; 6 | global.WORLD_VAR = "world"; 7 | global.EXCLAMATION_VAR = "!"; 8 | 9 | module.exports = require("./module2"); -------------------------------------------------------------------------------- /test/my-plugin-env-global/module2.js: -------------------------------------------------------------------------------- 1 | 2 | // env and globals are taken from index file 3 | // try to read global using globa. or directly 4 | module.exports = process.env.HELLO_VAR + " " + global.WORLD_VAR + EXCLAMATION_VAR; 5 | -------------------------------------------------------------------------------- /test/my-plugin-env-global/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-env-global", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-plugin-file-win-over-folder/index.js: -------------------------------------------------------------------------------- 1 | 2 | // there is both a file and a folder named lib, I expect that the file wins 3 | const lib = require("./lib"); 4 | 5 | module.exports = lib; 6 | -------------------------------------------------------------------------------- /test/my-plugin-file-win-over-folder/lib.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = "i-am-the-file"; 3 | -------------------------------------------------------------------------------- /test/my-plugin-file-win-over-folder/lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = "i-am-the-folder"; -------------------------------------------------------------------------------- /test/my-plugin-file-win-over-folder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-file-win-over-folder", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/my-plugin-scoped-with-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const a = require("my-plugin-a"); 4 | 5 | module.exports = "a = " + a; 6 | -------------------------------------------------------------------------------- /test/my-plugin-scoped-with-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@myscope/my-plugin-scoped-with-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "my-plugin-a": "1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-with-abs-require/index.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require("path"); 3 | 4 | exports.myVariableFromAbsoluteFile = require(path.join(__dirname, "subFolder", "b")); 5 | -------------------------------------------------------------------------------- /test/my-plugin-with-abs-require/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-abs-require", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-plugin-with-abs-require/subFolder/b.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = "value3"; 4 | -------------------------------------------------------------------------------- /test/my-plugin-with-bad-opt-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.ok = true; 3 | -------------------------------------------------------------------------------- /test/my-plugin-with-bad-opt-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-bad-opt-dep", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "optionalDependencies": { 6 | "some-non-existent-package-xyz": "1.0.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/my-plugin-with-circular-reference/a.js: -------------------------------------------------------------------------------- 1 | require("./b.js"); 2 | -------------------------------------------------------------------------------- /test/my-plugin-with-circular-reference/b.js: -------------------------------------------------------------------------------- 1 | require("./a.js"); 2 | -------------------------------------------------------------------------------- /test/my-plugin-with-circular-reference/index.js: -------------------------------------------------------------------------------- 1 | 2 | require("./a"); // a requires b, b requires a 3 | 4 | exports.myVariable = "value1"; 5 | -------------------------------------------------------------------------------- /test/my-plugin-with-circular-reference/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-circular-reference", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/my-plugin-with-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const moment = require("moment"); 4 | const debug = require("debug"); 5 | 6 | exports.testMoment = moment("19811006", "YYYYMMDD").format("YYYY/MM/DD"); 7 | exports.testDebug = debug; 8 | -------------------------------------------------------------------------------- /test/my-plugin-with-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/express": "4.17.13", 13 | "debug": "^4.3.3", 14 | "moment": "2.29.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/my-plugin-with-diff-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // I expect this debug reference to be diff than the host "debug" module 4 | // because in the host I have version 3, here I have version 2 5 | const debug = require("debug"); 6 | 7 | exports.testDebug = debug; 8 | -------------------------------------------------------------------------------- /test/my-plugin-with-diff-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-diff-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "debug": "2.6.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-with-folder-as-main/lib/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.myVariable = "value1"; 3 | -------------------------------------------------------------------------------- /test/my-plugin-with-folder-as-main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-folder-as-main", 3 | "version": "1.0.0", 4 | "main": "lib" 5 | } 6 | -------------------------------------------------------------------------------- /test/my-plugin-with-git-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const _ = require("underscore"); 4 | 5 | exports.testUnderscore = _.template("hello <%= name %>!")({ name: "underscore" }); 6 | -------------------------------------------------------------------------------- /test/my-plugin-with-git-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-git-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "underscore": "jashkenas/underscore#1.8.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-with-host-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const hostDependency = require("host-dependency"); 4 | 5 | exports.testHostDependency = hostDependency; 6 | -------------------------------------------------------------------------------- /test/my-plugin-with-host-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-host-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "host-dependency": "^1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-with-npm-modules/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.myVariable = "value1"; 3 | -------------------------------------------------------------------------------- /test/my-plugin-with-npm-modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-npm-modules", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-plugin-with-opt-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const moment = require("moment"); 3 | exports.testMoment = moment("19811006", "YYYYMMDD").format("YYYY/MM/DD"); 4 | -------------------------------------------------------------------------------- /test/my-plugin-with-opt-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-opt-dep", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "optionalDependencies": { 6 | "moment": "2.29.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/my-plugin-with-scoped-dep/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const pluginInstance = manager.require("@myscope/my-basic-plugin-scoped"); 4 | 5 | module.exports = pluginInstance; 6 | -------------------------------------------------------------------------------- /test/my-plugin-with-scoped-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-with-scoped-dep", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@myscope/my-basic-plugin-scoped": "1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-x/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const y = require("my-plugin-y"); 4 | const y_subFile = require("my-plugin-y/subFile"); 5 | 6 | exports.y = y; 7 | exports.y_subFile = y_subFile; 8 | -------------------------------------------------------------------------------- /test/my-plugin-x/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-x", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "my-plugin-y": "1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/my-plugin-y/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "y!"; 4 | -------------------------------------------------------------------------------- /test/my-plugin-y/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin-y", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/my-plugin-y/subFile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "y_subFile!"; 4 | -------------------------------------------------------------------------------- /test/my-plugin.js/index.js: -------------------------------------------------------------------------------- 1 | module.exports = "my-plugin.js!" 2 | -------------------------------------------------------------------------------- /test/my-plugin.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-plugin.js", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/my-test-plugin/aJsonFile.json: -------------------------------------------------------------------------------- 1 | { 2 | "loaded": "yes" 3 | } -------------------------------------------------------------------------------- /test/my-test-plugin/index.js: -------------------------------------------------------------------------------- 1 | exports.myGlobals = { 2 | __dirname, 3 | __filename, 4 | process, 5 | Buffer, 6 | console, 7 | Function, 8 | 9 | clearImmediate, 10 | clearInterval, 11 | clearTimeout, 12 | setImmediate, 13 | setInterval, 14 | setTimeout 15 | }; 16 | 17 | exports.myVariable = "value1"; 18 | module.exports.myVariable2 = "value2"; 19 | exports.myVariableFromSubFile = require("./subFolder/b.js"); 20 | exports.myVariableFromSubFolder = require("./subFolder"); 21 | exports.myJsonRequire = require("./aJsonFile.json"); 22 | 23 | // try to load the same module in different ways 24 | const myVariableDifferentStyleOfRequire = require("./subFolder2"); 25 | const myVariableDifferentStyleOfRequire2 = require("./subFolder2/index") 26 | const myVariableDifferentStyleOfRequire3 = require("./subFolder2/index.js") 27 | if (myVariableDifferentStyleOfRequire != myVariableDifferentStyleOfRequire2 28 | || myVariableDifferentStyleOfRequire != myVariableDifferentStyleOfRequire3) { 29 | throw new Error("Unexpected require value"); 30 | } 31 | exports.myVariableDifferentStyleOfRequire = myVariableDifferentStyleOfRequire; 32 | -------------------------------------------------------------------------------- /test/my-test-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-test-plugin", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/my-test-plugin/subFolder/b.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = "value3"; 4 | -------------------------------------------------------------------------------- /test/my-test-plugin/subFolder/index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = "value4"; 3 | -------------------------------------------------------------------------------- /test/my-test-plugin/subFolder2/index.js: -------------------------------------------------------------------------------- 1 | 2 | // this is to ensure that the require load the file just one time 3 | global.subFolder2Loaded = global.subFolder2Loaded || 0; 4 | global.subFolder2Loaded += 1; 5 | 6 | if (global.subFolder2Loaded !== 1) { 7 | throw new Error("Loaded too many times"); 8 | } 9 | 10 | module.exports = "value5"; -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | 10 | "sourceMap": true, 11 | "declaration": true, 12 | 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | 7 | "types": [ 8 | "node" 9 | ], 10 | 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | 14 | "sourceMap": true, 15 | "declaration": true, 16 | 17 | "esModuleInterop": true, 18 | "allowSyntheticDefaultImports": true, 19 | 20 | "outDir": "dist" 21 | }, 22 | "exclude": [ 23 | "samples", 24 | "test", 25 | "node_modules", 26 | "plugin_packages", 27 | "**/*.d.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest", 3 | "rules": { 4 | "indent": [ 5 | true, 6 | "tabs" 7 | ], 8 | "member-access": false, 9 | "ordered-imports": false, 10 | "trailing-comma": false, 11 | "max-classes-per-file": false, 12 | "no-consecutive-blank-lines": false, 13 | "no-empty": false, 14 | "object-literal-sort-keys": false, 15 | "only-arrow-functions": false, 16 | "interface-name": false, 17 | "no-var-requires": false 18 | } 19 | } 20 | --------------------------------------------------------------------------------