├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── docco.css ├── index.html └── public │ ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── roboto-black.eot │ ├── roboto-black.ttf │ └── roboto-black.woff │ └── stylesheets │ └── normalize.css ├── install.js ├── package-lock.json ├── package.json ├── scripts ├── docs.sh └── prepublish.sh └── test └── run.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | install.min.js 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test 3 | /docs 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "12" 5 | - "11" 6 | - "10" 7 | - "9" 8 | - "8" 9 | - "7" 10 | - "6" 11 | - "5" 12 | - "4" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Benjamin Newman 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # install [![Build Status](https://travis-ci.org/benjamn/install.svg?branch=master)](https://travis-ci.org/benjamn/install) [![Greenkeeper badge](https://badges.greenkeeper.io/benjamn/install.svg)](https://greenkeeper.io/) 2 | 3 | The [CommonJS module syntax](http://wiki.commonjs.org/wiki/Modules/1.1) is one of the most widely accepted conventions in the JavaScript ecosystem. Everyone seems to agree that `require` and `exports` are a reasonable way of expressing module dependencies and interfaces, and the tools for managing modular code are getting better all the time. 4 | 5 | Much less of a consensus has developed around the best way to deliver CommonJS modules to a web browser, where the synchronous semantics of `require` pose a non-trivial implementation challenge. This module loader contributes to that confusion, yet also demonstrates that an amply-featured module loader need not stretch into the hundreds or thousands of lines. 6 | 7 | Installation 8 | --- 9 | From NPM: 10 | 11 | npm install install 12 | 13 | From GitHub: 14 | 15 | cd path/to/node_modules 16 | git clone git://github.com/benjamn/install.git 17 | cd install 18 | npm install . 19 | 20 | Usage 21 | --- 22 | 23 | The first step is to create an `install` function by calling the 24 | `makeInstaller` method. Note that all of the options described below are 25 | optional: 26 | 27 | ```js 28 | var install = require("install").makeInstaller({ 29 | // Optional list of file extensions to be appended to required module 30 | // identifiers if they do not exactly match an installed module. 31 | extensions: [".js", ".json"], 32 | 33 | // If defined, the options.fallback function will be called when no 34 | // installed module is found for a required module identifier. Often 35 | // options.fallback will be implemented in terms of the native Node 36 | // require function, which has the ability to load binary modules. 37 | fallback, 38 | 39 | // Boolean flag indicating whether the installed code will be running in 40 | // a web browser. 41 | browser, 42 | 43 | // List of fields to look for in package.json files to determine the 44 | // main entry module of the package. The first field listed here whose 45 | // value is a string will be used to resolve the entry module. Defaults 46 | // to just ["main"], or ["browser", "main"] if options.browser is true. 47 | mainFields: ["browser", "main"], 48 | }); 49 | ``` 50 | 51 | The second step is to install some modules by passing a nested tree of 52 | objects and functions to the `install` function: 53 | 54 | ```js 55 | var require = install({ 56 | "main.js"(require, exports, module) { 57 | // On the client, the "assert" module should be install-ed just like 58 | // any other module. On the server, since "assert" is a built-in Node 59 | // module, it may make sense to let the options.fallback function 60 | // handle such requirements. Both ways work equally well. 61 | var assert = require("assert"); 62 | 63 | assert.strictEqual( 64 | // This require function uses the same lookup rules as Node, so it 65 | // will find "package" in the "node_modules" directory below. 66 | require("package").name, 67 | "/node_modules/package/entry.js" 68 | ); 69 | 70 | exports.name = module.id; 71 | }, 72 | 73 | node_modules: { 74 | package: { 75 | // If package.json is not defined, a module called "index.js" will 76 | // be used as the main entry point for the package. Otherwise the 77 | // exports.main property will identify the entry point. 78 | "package.json"(require, exports, module) { 79 | exports.name = "package"; 80 | exports.version = "0.1.0"; 81 | exports.main = "entry.js"; 82 | }, 83 | 84 | "entry.js"(require, exports, module) { 85 | exports.name = module.id; 86 | } 87 | } 88 | } 89 | }); 90 | ``` 91 | 92 | Note that the `install` function merely installs modules without 93 | evaluating them, so the third and final step is to `require` any entry 94 | point modules that you wish to evaluate: 95 | 96 | ```js 97 | console.log(require("./main").name); 98 | // => "/main.js" 99 | ``` 100 | 101 | This is the "root" `require` function returned by the `install` 102 | function. If you're using the `install` package in a CommonJS environment 103 | like Node, be careful that you don't overwrite the `require` function 104 | provided by that system. 105 | 106 | If you need to change the behavior of the `module` object that each module 107 | function receives as its third parameter, the shared `Module` constructor 108 | is exposed as a property of the `install` function returned by the 109 | `makeInstaller` factory: 110 | 111 | ```js 112 | var install = makeInstaller(options); 113 | var proto = install.Module.prototype; 114 | 115 | // Wrap all Module.prototype.require calls with some sort of logging. 116 | proto.require = wrapWithLogging(proto.require); 117 | 118 | // Add a new method available to all modules via module.newMethod(...). 119 | proto.newMethod = function () {...}; 120 | ``` 121 | 122 | Many more examples of how to use the `install` package can be found in the 123 | [tests](https://github.com/benjamn/install/blob/master/test/run.js). 124 | -------------------------------------------------------------------------------- /docs/docco.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Typography ----------------------------*/ 2 | 3 | @font-face { 4 | font-family: 'aller-light'; 5 | src: url('public/fonts/aller-light.eot'); 6 | src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), 7 | url('public/fonts/aller-light.woff') format('woff'), 8 | url('public/fonts/aller-light.ttf') format('truetype'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | @font-face { 14 | font-family: 'aller-bold'; 15 | src: url('public/fonts/aller-bold.eot'); 16 | src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), 17 | url('public/fonts/aller-bold.woff') format('woff'), 18 | url('public/fonts/aller-bold.ttf') format('truetype'); 19 | font-weight: normal; 20 | font-style: normal; 21 | } 22 | 23 | @font-face { 24 | font-family: 'roboto-black'; 25 | src: url('public/fonts/roboto-black.eot'); 26 | src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'), 27 | url('public/fonts/roboto-black.woff') format('woff'), 28 | url('public/fonts/roboto-black.ttf') format('truetype'); 29 | font-weight: normal; 30 | font-style: normal; 31 | } 32 | 33 | /*--------------------- Layout ----------------------------*/ 34 | html { height: 100%; } 35 | body { 36 | font-family: "aller-light"; 37 | font-size: 14px; 38 | line-height: 18px; 39 | color: #30404f; 40 | margin: 0; padding: 0; 41 | height:100%; 42 | } 43 | #container { min-height: 100%; } 44 | 45 | a { 46 | color: #000; 47 | } 48 | 49 | b, strong { 50 | font-weight: normal; 51 | font-family: "aller-bold"; 52 | } 53 | 54 | p { 55 | margin: 15px 0 0px; 56 | } 57 | .annotation ul, .annotation ol { 58 | margin: 25px 0; 59 | } 60 | .annotation ul li, .annotation ol li { 61 | font-size: 14px; 62 | line-height: 18px; 63 | margin: 10px 0; 64 | } 65 | 66 | h1, h2, h3, h4, h5, h6 { 67 | color: #112233; 68 | line-height: 1em; 69 | font-weight: normal; 70 | font-family: "roboto-black"; 71 | text-transform: uppercase; 72 | margin: 30px 0 15px 0; 73 | } 74 | 75 | h1 { 76 | margin-top: 40px; 77 | } 78 | h2 { 79 | font-size: 1.26em; 80 | } 81 | 82 | hr { 83 | border: 0; 84 | background: 1px #ddd; 85 | height: 1px; 86 | margin: 20px 0; 87 | } 88 | 89 | pre, tt, code { 90 | font-size: 12px; line-height: 16px; 91 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 92 | margin: 0; padding: 0; 93 | } 94 | .annotation pre { 95 | display: block; 96 | margin: 0; 97 | padding: 7px 10px; 98 | background: #fcfcfc; 99 | -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 100 | -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 101 | box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 102 | overflow-x: auto; 103 | } 104 | .annotation pre code { 105 | border: 0; 106 | padding: 0; 107 | background: transparent; 108 | } 109 | 110 | 111 | blockquote { 112 | border-left: 5px solid #ccc; 113 | margin: 0; 114 | padding: 1px 0 1px 1em; 115 | } 116 | .sections blockquote p { 117 | font-family: Menlo, Consolas, Monaco, monospace; 118 | font-size: 12px; line-height: 16px; 119 | color: #999; 120 | margin: 10px 0 0; 121 | white-space: pre-wrap; 122 | } 123 | 124 | ul.sections { 125 | list-style: none; 126 | padding:0 0 5px 0;; 127 | margin:0; 128 | } 129 | 130 | /* 131 | Force border-box so that % widths fit the parent 132 | container without overlap because of margin/padding. 133 | 134 | More Info : http://www.quirksmode.org/css/box.html 135 | */ 136 | ul.sections > li > div { 137 | -moz-box-sizing: border-box; /* firefox */ 138 | -ms-box-sizing: border-box; /* ie */ 139 | -webkit-box-sizing: border-box; /* webkit */ 140 | -khtml-box-sizing: border-box; /* konqueror */ 141 | box-sizing: border-box; /* css3 */ 142 | } 143 | 144 | 145 | /*---------------------- Jump Page -----------------------------*/ 146 | #jump_to, #jump_page { 147 | margin: 0; 148 | background: white; 149 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 150 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 151 | font: 16px Arial; 152 | cursor: pointer; 153 | text-align: right; 154 | list-style: none; 155 | } 156 | 157 | #jump_to a { 158 | text-decoration: none; 159 | } 160 | 161 | #jump_to a.large { 162 | display: none; 163 | } 164 | #jump_to a.small { 165 | font-size: 22px; 166 | font-weight: bold; 167 | color: #676767; 168 | } 169 | 170 | #jump_to, #jump_wrapper { 171 | position: fixed; 172 | right: 0; top: 0; 173 | padding: 10px 15px; 174 | margin:0; 175 | } 176 | 177 | #jump_wrapper { 178 | display: none; 179 | padding:0; 180 | } 181 | 182 | #jump_to:hover #jump_wrapper { 183 | display: block; 184 | } 185 | 186 | #jump_page_wrapper{ 187 | position: fixed; 188 | right: 0; 189 | top: 0; 190 | bottom: 0; 191 | } 192 | 193 | #jump_page { 194 | padding: 5px 0 3px; 195 | margin: 0 0 25px 25px; 196 | max-height: 100%; 197 | overflow: auto; 198 | } 199 | 200 | #jump_page .source { 201 | display: block; 202 | padding: 15px; 203 | text-decoration: none; 204 | border-top: 1px solid #eee; 205 | } 206 | 207 | #jump_page .source:hover { 208 | background: #f5f5ff; 209 | } 210 | 211 | #jump_page .source:first-child { 212 | } 213 | 214 | /*---------------------- Low resolutions (> 320px) ---------------------*/ 215 | @media only screen and (min-width: 320px) { 216 | .pilwrap { display: none; } 217 | 218 | ul.sections > li > div { 219 | display: block; 220 | padding:5px 10px 0 10px; 221 | } 222 | 223 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { 224 | padding-left: 30px; 225 | } 226 | 227 | ul.sections > li > div.content { 228 | overflow-x:auto; 229 | -webkit-box-shadow: inset 0 0 5px #e5e5ee; 230 | box-shadow: inset 0 0 5px #e5e5ee; 231 | border: 1px solid #dedede; 232 | margin:5px 10px 5px 10px; 233 | padding-bottom: 5px; 234 | } 235 | 236 | ul.sections > li > div.annotation pre { 237 | margin: 7px 0 7px; 238 | padding-left: 15px; 239 | } 240 | 241 | ul.sections > li > div.annotation p tt, .annotation code { 242 | background: #f8f8ff; 243 | border: 1px solid #dedede; 244 | font-size: 12px; 245 | padding: 0 0.2em; 246 | } 247 | } 248 | 249 | /*---------------------- (> 481px) ---------------------*/ 250 | @media only screen and (min-width: 481px) { 251 | #container { 252 | position: relative; 253 | } 254 | body { 255 | background-color: #F5F5FF; 256 | font-size: 15px; 257 | line-height: 21px; 258 | } 259 | pre, tt, code { 260 | line-height: 18px; 261 | } 262 | p, ul, ol { 263 | margin: 0 0 15px; 264 | } 265 | 266 | 267 | #jump_to { 268 | padding: 5px 10px; 269 | } 270 | #jump_wrapper { 271 | padding: 0; 272 | } 273 | #jump_to, #jump_page { 274 | font: 10px Arial; 275 | text-transform: uppercase; 276 | } 277 | #jump_page .source { 278 | padding: 5px 10px; 279 | } 280 | #jump_to a.large { 281 | display: inline-block; 282 | } 283 | #jump_to a.small { 284 | display: none; 285 | } 286 | 287 | 288 | 289 | #background { 290 | position: absolute; 291 | top: 0; bottom: 0; 292 | width: 350px; 293 | background: #fff; 294 | border-right: 1px solid #e5e5ee; 295 | z-index: -1; 296 | } 297 | 298 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { 299 | padding-left: 40px; 300 | } 301 | 302 | ul.sections > li { 303 | white-space: nowrap; 304 | } 305 | 306 | ul.sections > li > div { 307 | display: inline-block; 308 | } 309 | 310 | ul.sections > li > div.annotation { 311 | max-width: 350px; 312 | min-width: 350px; 313 | min-height: 5px; 314 | padding: 13px; 315 | overflow-x: hidden; 316 | white-space: normal; 317 | vertical-align: top; 318 | text-align: left; 319 | } 320 | ul.sections > li > div.annotation pre { 321 | margin: 15px 0 15px; 322 | padding-left: 15px; 323 | } 324 | 325 | ul.sections > li > div.content { 326 | padding: 13px; 327 | vertical-align: top; 328 | border: none; 329 | -webkit-box-shadow: none; 330 | box-shadow: none; 331 | } 332 | 333 | .pilwrap { 334 | position: relative; 335 | display: inline; 336 | } 337 | 338 | .pilcrow { 339 | font: 12px Arial; 340 | text-decoration: none; 341 | color: #454545; 342 | position: absolute; 343 | top: 3px; left: -20px; 344 | padding: 1px 2px; 345 | opacity: 0; 346 | -webkit-transition: opacity 0.2s linear; 347 | } 348 | .for-h1 .pilcrow { 349 | top: 47px; 350 | } 351 | .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow { 352 | top: 35px; 353 | } 354 | 355 | ul.sections > li > div.annotation:hover .pilcrow { 356 | opacity: 1; 357 | } 358 | } 359 | 360 | /*---------------------- (> 1025px) ---------------------*/ 361 | @media only screen and (min-width: 1025px) { 362 | 363 | body { 364 | font-size: 16px; 365 | line-height: 24px; 366 | } 367 | 368 | #background { 369 | width: 525px; 370 | } 371 | ul.sections > li > div.annotation { 372 | max-width: 525px; 373 | min-width: 525px; 374 | padding: 10px 25px 1px 50px; 375 | } 376 | ul.sections > li > div.content { 377 | padding: 9px 15px 16px 25px; 378 | } 379 | } 380 | 381 | /*---------------------- Syntax Highlighting -----------------------------*/ 382 | 383 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 384 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 385 | /* 386 | 387 | github.com style (c) Vasily Polovnyov 388 | 389 | */ 390 | 391 | pre code { 392 | display: block; padding: 0.5em; 393 | color: #000; 394 | background: #f8f8ff 395 | } 396 | 397 | pre .hljs-comment, 398 | pre .hljs-template_comment, 399 | pre .hljs-diff .hljs-header, 400 | pre .hljs-javadoc { 401 | color: #408080; 402 | font-style: italic 403 | } 404 | 405 | pre .hljs-keyword, 406 | pre .hljs-assignment, 407 | pre .hljs-literal, 408 | pre .hljs-css .hljs-rule .hljs-keyword, 409 | pre .hljs-winutils, 410 | pre .hljs-javascript .hljs-title, 411 | pre .hljs-lisp .hljs-title, 412 | pre .hljs-subst { 413 | color: #954121; 414 | /*font-weight: bold*/ 415 | } 416 | 417 | pre .hljs-number, 418 | pre .hljs-hexcolor { 419 | color: #40a070 420 | } 421 | 422 | pre .hljs-string, 423 | pre .hljs-tag .hljs-value, 424 | pre .hljs-phpdoc, 425 | pre .hljs-tex .hljs-formula { 426 | color: #219161; 427 | } 428 | 429 | pre .hljs-title, 430 | pre .hljs-id { 431 | color: #19469D; 432 | } 433 | pre .hljs-params { 434 | color: #00F; 435 | } 436 | 437 | pre .hljs-javascript .hljs-title, 438 | pre .hljs-lisp .hljs-title, 439 | pre .hljs-subst { 440 | font-weight: normal 441 | } 442 | 443 | pre .hljs-class .hljs-title, 444 | pre .hljs-haskell .hljs-label, 445 | pre .hljs-tex .hljs-command { 446 | color: #458; 447 | font-weight: bold 448 | } 449 | 450 | pre .hljs-tag, 451 | pre .hljs-tag .hljs-title, 452 | pre .hljs-rules .hljs-property, 453 | pre .hljs-django .hljs-tag .hljs-keyword { 454 | color: #000080; 455 | font-weight: normal 456 | } 457 | 458 | pre .hljs-attribute, 459 | pre .hljs-variable, 460 | pre .hljs-instancevar, 461 | pre .hljs-lisp .hljs-body { 462 | color: #008080 463 | } 464 | 465 | pre .hljs-regexp { 466 | color: #B68 467 | } 468 | 469 | pre .hljs-class { 470 | color: #458; 471 | font-weight: bold 472 | } 473 | 474 | pre .hljs-symbol, 475 | pre .hljs-ruby .hljs-symbol .hljs-string, 476 | pre .hljs-ruby .hljs-symbol .hljs-keyword, 477 | pre .hljs-ruby .hljs-symbol .hljs-keymethods, 478 | pre .hljs-lisp .hljs-keyword, 479 | pre .hljs-tex .hljs-special, 480 | pre .hljs-input_number { 481 | color: #990073 482 | } 483 | 484 | pre .hljs-builtin, 485 | pre .hljs-constructor, 486 | pre .hljs-built_in, 487 | pre .hljs-lisp .hljs-title { 488 | color: #0086b3 489 | } 490 | 491 | pre .hljs-preprocessor, 492 | pre .hljs-pi, 493 | pre .hljs-doctype, 494 | pre .hljs-shebang, 495 | pre .hljs-cdata { 496 | color: #999; 497 | font-weight: bold 498 | } 499 | 500 | pre .hljs-deletion { 501 | background: #fdd 502 | } 503 | 504 | pre .hljs-addition { 505 | background: #dfd 506 | } 507 | 508 | pre .hljs-diff .hljs-change { 509 | background: #0086b3 510 | } 511 | 512 | pre .hljs-chunk { 513 | color: #aaa 514 | } 515 | 516 | pre .hljs-tex .hljs-formula { 517 | opacity: 0.5; 518 | } 519 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | install.js 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 |
    15 | 16 |
  • 17 |
    18 |

    install.js

    19 |
    20 |
  • 21 | 22 | 23 | 24 |
  • 25 |
    26 | 27 |
    28 | 29 |
    30 | 31 |
    32 | 33 |
    makeInstaller = function (options) {
      34 |   "use strict";
      35 | 
      36 |   options = options || {};
    37 | 38 |
  • 39 | 40 | 41 |
  • 42 |
    43 | 44 |
    45 | 46 |
    47 |

    These file extensions will be appended to required module identifiers 48 | if they do not exactly match an installed module.

    49 | 50 |
    51 | 52 |
      var defaultExtensions = options.extensions || [".js", ".json"];
    53 | 54 |
  • 55 | 56 | 57 |
  • 58 |
    59 | 60 |
    61 | 62 |
    63 |

    If defined, the options.fallback function will be called when no 64 | installed module is found for a required module identifier. Often 65 | options.fallback will be implemented in terms of the native Node 66 | require function, which has the ability to load binary modules.

    67 | 68 |
    69 | 70 |
      var fallback = options.fallback;
    71 | 72 |
  • 73 | 74 | 75 |
  • 76 |
    77 | 78 |
    79 | 80 |
    81 |

    List of fields to look for in package.json files to determine the 82 | main entry module of the package. The first field listed here whose 83 | value is a string will be used to resolve the entry module.

    84 | 85 |
    86 | 87 |
      var mainFields = options.mainFields ||
    88 | 89 |
  • 90 | 91 | 92 |
  • 93 |
    94 | 95 |
    96 | 97 |
    98 |

    If options.mainFields is absent and options.browser is truthy, 99 | package resolution will prefer the “browser” field of package.json 100 | files to the “main” field. Note that this only supports 101 | string-valued “browser” fields for now, though in the future it 102 | might make sense to support the object version, a la browserify.

    103 | 104 |
    105 | 106 |
        (options.browser ? ["browser", "main"] : ["main"]);
     107 | 
     108 |   var hasOwn = {}.hasOwnProperty;
     109 |   function strictHasOwn(obj, key) {
     110 |     return isObject(obj) && isString(key) && hasOwn.call(obj, key);
     111 |   }
    112 | 113 |
  • 114 | 115 | 116 |
  • 117 |
    118 | 119 |
    120 | 121 |
    122 |

    Cache for looking up File objects given absolute module identifiers. 123 | Invariants: 124 | filesByModuleId[module.id] === fileAppendId(root, module.id) 125 | filesByModuleId[module.id].module === module

    126 | 127 |
    128 | 129 |
      var filesByModuleId = {};
    130 | 131 |
  • 132 | 133 | 134 |
  • 135 |
    136 | 137 |
    138 | 139 |
    140 |

    The file object representing the root directory of the installed 141 | module tree.

    142 | 143 |
    144 | 145 |
      var root = new File("/", new File("/.."));
     146 |   var rootRequire = makeRequire(root);
    147 | 148 |
  • 149 | 150 | 151 |
  • 152 |
    153 | 154 |
    155 | 156 |
    157 |

    Merges the given tree of directories and module factory functions 158 | into the tree of installed modules and returns a require function 159 | that behaves as if called from a module in the root directory.

    160 | 161 |
    162 | 163 |
      function install(tree, options) {
     164 |     if (isObject(tree)) {
     165 |       fileMergeContents(root, tree, options);
     166 |     }
     167 |     return rootRequire;
     168 |   }
    169 | 170 |
  • 171 | 172 | 173 |
  • 174 |
    175 | 176 |
    177 | 178 |
    179 |

    Replace this function to enable Module.prototype.prefetch.

    180 | 181 |
    182 | 183 |
      install.fetch = function (ids) {
     184 |     throw new Error("fetch not implemented");
     185 |   };
    186 | 187 |
  • 188 | 189 | 190 |
  • 191 |
    192 | 193 |
    194 | 195 |
    196 |

    This constructor will be used to instantiate the module objects 197 | passed to module factory functions (i.e. the third argument after 198 | require and exports), and is exposed as install.Module in case the 199 | caller of makeInstaller wishes to modify Module.prototype.

    200 | 201 |
    202 | 203 |
      function Module(id) {
     204 |     this.id = id;
    205 | 206 |
  • 207 | 208 | 209 |
  • 210 |
    211 | 212 |
    213 | 214 |
    215 |

    The Node implementation of module.children unfortunately includes 216 | only those child modules that were imported for the first time by 217 | this parent module (i.e., child.parent === this).

    218 | 219 |
    220 | 221 |
        this.children = [];
    222 | 223 |
  • 224 | 225 | 226 |
  • 227 |
    228 | 229 |
    230 | 231 |
    232 |

    This object is an install.js extension that includes all child 233 | modules imported by this module, even if this module is not the 234 | first to import them.

    235 | 236 |
    237 | 238 |
        this.childrenById = {};
     239 |   }
    240 | 241 |
  • 242 | 243 | 244 |
  • 245 |
    246 | 247 |
    248 | 249 |
    250 |

    Used to keep module.prefetch promise resolutions well-ordered.

    251 | 252 |
    253 | 254 |
      var lastPrefetchPromise;
    255 | 256 |
  • 257 | 258 | 259 |
  • 260 |
    261 | 262 |
    263 | 264 |
    265 |

    May be shared by multiple sequential calls to module.prefetch. 266 | Initialized to {} only when necessary.

    267 | 268 |
    269 | 270 |
      var missing;
     271 | 
     272 |   Module.prototype.prefetch = function (id) {
     273 |     var module = this;
     274 |     var parentFile = getOwn(filesByModuleId, module.id);
     275 | 
     276 |     lastPrefetchPromise = lastPrefetchPromise || Promise.resolve();
     277 |     var previousPromise = lastPrefetchPromise;
     278 | 
     279 |     function walk(module) {
     280 |       var file = getOwn(filesByModuleId, module.id);
     281 |       if (fileIsDynamic(file) && ! file.pending) {
     282 |         file.pending = true;
     283 |         missing = missing || {};
    284 | 285 |
  • 286 | 287 | 288 |
  • 289 |
    290 | 291 |
    292 | 293 |
    294 |

    These are the data that will be exposed to the install.fetch 295 | callback, so it’s worth documenting each item with a comment.

    296 | 297 |
    298 | 299 |
            missing[module.id] = {
    300 | 301 |
  • 302 | 303 | 304 |
  • 305 |
    306 | 307 |
    308 | 309 |
    310 |

    The CommonJS module object that will be exposed to this 311 | dynamic module when it is evaluated. Note that install.fetch 312 | could decide to populate module.exports directly, instead of 313 | fetching anything. In that case, install.fetch should omit 314 | this module from the tree that it produces.

    315 | 316 |
    317 | 318 |
              module: file.module,
    319 | 320 |
  • 321 | 322 | 323 |
  • 324 |
    325 | 326 |
    327 | 328 |
    329 |

    List of module identifier strings imported by this module. 330 | Note that the missing object already contains all available 331 | dependencies (including transitive dependencies), so 332 | install.fetch should not need to traverse these dependencies 333 | in most cases; however, they may be useful for other reasons. 334 | Though the strings are unique, note that two different 335 | strings could resolve to the same module.

    336 | 337 |
    338 | 339 |
              deps: Object.keys(file.deps),
    340 | 341 |
  • 342 | 343 | 344 |
  • 345 |
    346 | 347 |
    348 | 349 |
    350 |

    The options (if any) that were passed as the second argument 351 | to the install(tree, options) function when this stub was 352 | first registered. Typically contains options.extensions, but 353 | could contain any information appropriate for the entire tree 354 | as originally installed. These options will be automatically 355 | inherited by the newly fetched modules, so install.fetch 356 | should not need to modify them.

    357 | 358 |
    359 | 360 |
              options: file.options,
    361 | 362 |
  • 363 | 364 | 365 |
  • 366 |
    367 | 368 |
    369 | 370 |
    371 |

    Any stub data included in the array notation from the 372 | original entry for this dynamic module. Typically contains 373 | “main” and/or “browser” fields for package.json files, and is 374 | otherwise undefined.

    375 | 376 |
    377 | 378 |
              stub: file.stub
     379 |         };
     380 | 
     381 |         each(file.deps, function (parentId, id) {
     382 |           fileResolve(file, id);
     383 |         });
     384 | 
     385 |         each(module.childrenById, walk);
     386 |       }
     387 |     }
     388 | 
     389 |     return lastPrefetchPromise = new Promise(function (resolve) {
     390 |       var absChildId = module.resolve(id);
     391 |       each(module.childrenById, walk);
     392 |       resolve(absChildId);
     393 | 
     394 |     }).then(function (absChildId) {
    395 | 396 |
  • 397 | 398 | 399 |
  • 400 |
    401 | 402 |
    403 | 404 |
    405 |

    Grab the current missing object and fetch its contents.

    406 | 407 |
    408 | 409 |
          var toBeFetched = missing;
     410 |       missing = null;
     411 | 
     412 |       function clearPending() {
     413 |         if (toBeFetched) {
     414 |           Object.keys(toBeFetched).forEach(function (id) {
     415 |             getOwn(filesByModuleId, id).pending = false;
     416 |           });
     417 |         }
     418 |       }
     419 | 
     420 |       return new Promise(function (resolve) {
    421 | 422 |
  • 423 | 424 | 425 |
  • 426 |
    427 | 428 |
    429 | 430 |
    431 |

    The install.fetch function takes an object mapping missing 432 | dynamic module identifiers to options objects, and should 433 | return a Promise that resolves to a module tree that can be 434 | installed. As an optimization, if there were no missing dynamic 435 | modules, then we can skip calling install.fetch entirely.

    436 | 437 |
    438 | 439 |
            resolve(toBeFetched && install.fetch(toBeFetched));
     440 | 
     441 |       }).then(function (tree) {
     442 |         function both() {
     443 |           install(tree);
     444 |           clearPending();
     445 |           return absChildId;
     446 |         }
    447 | 448 |
  • 449 | 450 | 451 |
  • 452 |
    453 | 454 |
    455 | 456 |
    457 |

    Although we want multiple install.fetch calls to run in 458 | parallel, it is important that the promises returned by 459 | module.prefetch are resolved in the same order as the original 460 | calls to module.prefetch, because previous fetches may include 461 | modules assumed to exist by more recent module.prefetch calls. 462 | Whether previousPromise was resolved or rejected, carry on with 463 | the installation regardless.

    464 | 465 |
    466 | 467 |
            return previousPromise.then(both, both);
     468 | 
     469 |       }, function (error) {
    470 | 471 |
  • 472 | 473 | 474 |
  • 475 |
    476 | 477 |
    478 | 479 |
    480 |

    Fixes https://github.com/meteor/meteor/issues/10182.

    481 | 482 |
    483 | 484 |
            clearPending();
     485 |         throw error;
     486 |       });
     487 |     });
     488 |   };
     489 | 
     490 |   install.Module = Module;
     491 | 
     492 |   function getOwn(obj, key) {
     493 |     return strictHasOwn(obj, key) && obj[key];
     494 |   }
     495 | 
     496 |   function isObject(value) {
     497 |     return value !== null && typeof value === "object";
     498 |   }
     499 | 
     500 |   function isFunction(value) {
     501 |     return typeof value === "function";
     502 |   }
     503 | 
     504 |   function isString(value) {
     505 |     return typeof value === "string";
     506 |   }
     507 | 
     508 |   function makeMissingError(id) {
     509 |     return new Error("Cannot find module '" + id + "'");
     510 |   }
     511 | 
     512 |   Module.prototype.resolve = function (id) {
     513 |     var file = fileResolve(filesByModuleId[this.id], id);
     514 |     if (file) return file.module.id;
     515 |     var error = makeMissingError(id);
     516 |     if (fallback && isFunction(fallback.resolve)) {
     517 |       return fallback.resolve(id, this.id, error);
     518 |     }
     519 |     throw error;
     520 |   };
     521 | 
     522 |   Module.prototype.require = function require(id) {
     523 |     var result = fileResolve(filesByModuleId[this.id], id);
     524 |     if (result) {
     525 |       return fileEvaluate(result, this);
     526 |     }
     527 | 
     528 |     var error = makeMissingError(id);
     529 | 
     530 |     if (isFunction(fallback)) {
     531 |       return fallback(
     532 |         id, // The missing module identifier.
     533 |         this.id, // ID of the parent module.
     534 |         error // The error we would have thrown.
     535 |       );
     536 |     }
     537 | 
     538 |     throw error;
     539 |   };
     540 | 
     541 |   function makeRequire(file) {
     542 |     var module = file.module;
     543 | 
     544 |     function require(id) {
     545 |       return module.require(id);
     546 |     }
     547 | 
     548 |     require.extensions = fileGetExtensions(file).slice(0);
     549 | 
     550 |     require.resolve = function resolve(id) {
     551 |       return module.resolve(id);
     552 |     };
     553 | 
     554 |     return require;
     555 |   }
    556 | 557 |
  • 558 | 559 | 560 |
  • 561 |
    562 | 563 |
    564 | 565 |
    566 |

    File objects represent either directories or modules that have been 567 | installed. When a File respresents a directory, its .contents 568 | property is an object containing the names of the files (or 569 | directories) that it contains. When a File represents a module, its 570 | .contents property is a function that can be invoked with the 571 | appropriate (require, exports, module) arguments to evaluate the 572 | module. If the .contents property is a string, that string will be 573 | resolved as a module identifier, and the exports of the resulting 574 | module will provide the exports of the original file. The .parent 575 | property of a File is either a directory File or null. Note that 576 | a child may claim another File as its parent even if the parent 577 | does not have an entry for that child in its .contents object. 578 | This is important for implementing anonymous files, and preventing 579 | child modules from using ../relative/identifier syntax to examine 580 | unrelated modules.

    581 | 582 |
    583 | 584 |
      function File(moduleId, parent) {
     585 |     var file = this;
    586 | 587 |
  • 588 | 589 | 590 |
  • 591 |
    592 | 593 |
    594 | 595 |
    596 |

    Link to the parent file.

    597 | 598 |
    599 | 600 |
        file.parent = parent = parent || null;
    601 | 602 |
  • 603 | 604 | 605 |
  • 606 |
    607 | 608 |
    609 | 610 |
    611 |

    The module object for this File, which will eventually boast an 612 | .exports property when/if the file is evaluated.

    613 | 614 |
    615 | 616 |
        file.module = new Module(moduleId);
     617 |     filesByModuleId[moduleId] = file;
    618 | 619 |
  • 620 | 621 | 622 |
  • 623 |
    624 | 625 |
    626 | 627 |
    628 |

    The .contents of the file can be either (1) an object, if the file 629 | represents a directory containing other files; (2) a factory 630 | function, if the file represents a module that can be imported; (3) 631 | a string, if the file is an alias for another file; or (4) null, if 632 | the file’s contents are not (yet) available.

    633 | 634 |
    635 | 636 |
        file.contents = null;
    637 | 638 |
  • 639 | 640 | 641 |
  • 642 |
    643 | 644 |
    645 | 646 |
    647 |

    Set of module identifiers imported by this module. Note that this 648 | set is not necessarily complete, so don’t rely on it unless you 649 | know what you’re doing.

    650 | 651 |
    652 | 653 |
        file.deps = {};
     654 |   }
     655 | 
     656 |   function fileEvaluate(file, parentModule) {
     657 |     var module = file.module;
     658 |     if (! strictHasOwn(module, "exports")) {
     659 |       var contents = file.contents;
     660 |       if (! contents) {
    661 | 662 |
  • 663 | 664 | 665 |
  • 666 |
    667 | 668 |
    669 | 670 |
    671 |

    If this file was installed with array notation, and the array 672 | contained one or more objects but no functions, then the combined 673 | properties of the objects are treated as a temporary stub for 674 | file.module.exports. This is particularly important for partial 675 | package.json modules, so that the resolution logic can know the 676 | value of the “main” and/or “browser” fields, at least, even if 677 | the rest of the package.json file is not (yet) available.

    678 | 679 |
    680 | 681 |
            if (file.stub) {
     682 |           return file.stub;
     683 |         }
     684 | 
     685 |         throw makeMissingError(module.id);
     686 |       }
     687 | 
     688 |       if (parentModule) {
     689 |         module.parent = parentModule;
     690 |         var children = parentModule.children;
     691 |         if (Array.isArray(children)) {
     692 |           children.push(module);
     693 |         }
     694 |       }
     695 | 
     696 |       contents(
     697 |         makeRequire(file),
    698 | 699 |
  • 700 | 701 | 702 |
  • 703 |
    704 | 705 |
    706 | 707 |
    708 |

    If the file had a .stub, reuse the same object for exports.

    709 | 710 |
    711 | 712 |
            module.exports = file.stub || {},
     713 |         module,
     714 |         file.module.id,
     715 |         file.parent.module.id
     716 |       );
     717 | 
     718 |       module.loaded = true;
     719 |     }
    720 | 721 |
  • 722 | 723 | 724 |
  • 725 |
    726 | 727 |
    728 | 729 |
    730 |

    The module.runModuleSetters method will be deprecated in favor of 731 | just module.runSetters: https://github.com/benjamn/reify/pull/160

    732 | 733 |
    734 | 735 |
        var runSetters = module.runSetters || module.runModuleSetters;
     736 |     if (isFunction(runSetters)) {
     737 |       runSetters.call(module);
     738 |     }
     739 | 
     740 |     return module.exports;
     741 |   }
     742 | 
     743 |   function fileIsDirectory(file) {
     744 |     return file && isObject(file.contents);
     745 |   }
     746 | 
     747 |   function fileIsDynamic(file) {
     748 |     return file && file.contents === null;
     749 |   }
     750 | 
     751 |   function fileMergeContents(file, contents, options) {
     752 |     if (Array.isArray(contents)) {
     753 |       contents.forEach(function (item) {
     754 |         if (isString(item)) {
     755 |           file.deps[item] = file.module.id;
     756 |         } else if (isFunction(item)) {
     757 |           contents = item;
     758 |         } else if (isObject(item)) {
     759 |           file.stub = file.stub || {};
     760 |           each(item, function (value, key) {
     761 |             file.stub[key] = value;
     762 |           });
     763 |         }
     764 |       });
     765 | 
     766 |       if (! isFunction(contents)) {
    767 | 768 |
  • 769 | 770 | 771 |
  • 772 |
    773 | 774 |
    775 | 776 |
    777 |

    If the array did not contain a function, merge nothing.

    778 | 779 |
    780 | 781 |
            contents = null;
     782 |       }
     783 | 
     784 |     } else if (! isFunction(contents) &&
     785 |                ! isString(contents) &&
     786 |                ! isObject(contents)) {
    787 | 788 |
  • 789 | 790 | 791 |
  • 792 |
    793 | 794 |
    795 | 796 |
    797 |

    If contents is neither an array nor a function nor a string nor 798 | an object, just give up and merge nothing.

    799 | 800 |
    801 | 802 |
          contents = null;
     803 |     }
     804 | 
     805 |     if (contents) {
     806 |       file.contents = file.contents || (isObject(contents) ? {} : contents);
     807 |       if (isObject(contents) && fileIsDirectory(file)) {
     808 |         each(contents, function (value, key) {
     809 |           if (key === "..") {
     810 |             child = file.parent;
     811 | 
     812 |           } else {
     813 |             var child = getOwn(file.contents, key);
     814 | 
     815 |             if (! child) {
     816 |               child = file.contents[key] = new File(
     817 |                 file.module.id.replace(/\/*$/, "/") + key,
     818 |                 file
     819 |               );
     820 | 
     821 |               child.options = options;
     822 |             }
     823 |           }
     824 | 
     825 |           fileMergeContents(child, value, options);
     826 |         });
     827 |       }
     828 |     }
     829 |   }
     830 | 
     831 |   function each(obj, callback, context) {
     832 |     Object.keys(obj).forEach(function (key) {
     833 |       callback.call(this, obj[key], key);
     834 |     }, context);
     835 |   }
     836 | 
     837 |   function fileGetExtensions(file) {
     838 |     return file.options
     839 |       && file.options.extensions
     840 |       || defaultExtensions;
     841 |   }
     842 | 
     843 |   function fileAppendIdPart(file, part, extensions) {
    844 | 845 |
  • 846 | 847 | 848 |
  • 849 |
    850 | 851 |
    852 | 853 |
    854 |

    Always append relative to a directory.

    855 | 856 |
    857 | 858 |
        while (file && ! fileIsDirectory(file)) {
     859 |       file = file.parent;
     860 |     }
     861 | 
     862 |     if (! file || ! part || part === ".") {
     863 |       return file;
     864 |     }
     865 | 
     866 |     if (part === "..") {
     867 |       return file.parent;
     868 |     }
     869 | 
     870 |     var exactChild = getOwn(file.contents, part);
    871 | 872 |
  • 873 | 874 | 875 |
  • 876 |
    877 | 878 |
    879 | 880 |
    881 |

    Only consider multiple file extensions if this part is the last 882 | part of a module identifier and not equal to . or .., and there 883 | was no exact match or the exact match was a directory.

    884 | 885 |
    886 | 887 |
        if (extensions && (! exactChild || fileIsDirectory(exactChild))) {
     888 |       for (var e = 0; e < extensions.length; ++e) {
     889 |         var child = getOwn(file.contents, part + extensions[e]);
     890 |         if (child && ! fileIsDirectory(child)) {
     891 |           return child;
     892 |         }
     893 |       }
     894 |     }
     895 | 
     896 |     return exactChild;
     897 |   }
     898 | 
     899 |   function fileAppendId(file, id, extensions) {
     900 |     var parts = id.split("/");
    901 | 902 |
  • 903 | 904 | 905 |
  • 906 |
    907 | 908 |
    909 | 910 |
    911 |

    Use Array.prototype.every to terminate iteration early if 912 | fileAppendIdPart returns a falsy value.

    913 | 914 |
    915 | 916 |
        parts.every(function (part, i) {
     917 |       return file = i < parts.length - 1
     918 |         ? fileAppendIdPart(file, part)
     919 |         : fileAppendIdPart(file, part, extensions);
     920 |     });
     921 | 
     922 |     return file;
     923 |   }
     924 | 
     925 |   function recordChild(parentModule, childFile) {
     926 |     var childModule = childFile && childFile.module;
     927 |     if (parentModule && childModule) {
     928 |       parentModule.childrenById[childModule.id] = childModule;
     929 |     }
     930 |   }
     931 | 
     932 |   function fileResolve(file, id, parentModule, seenDirFiles) {
     933 |     var parentModule = parentModule || file.module;
     934 |     var extensions = fileGetExtensions(file);
     935 | 
     936 |     file =
    937 | 938 |
  • 939 | 940 | 941 |
  • 942 |
    943 | 944 |
    945 | 946 |
    947 |

    Absolute module identifiers (i.e. those that begin with a / 948 | character) are interpreted relative to the root directory, which 949 | is a slight deviation from Node, which has access to the entire 950 | file system.

    951 | 952 |
    953 | 954 |
          id.charAt(0) === "/" ? fileAppendId(root, id, extensions) :
    955 | 956 |
  • 957 | 958 | 959 |
  • 960 |
    961 | 962 |
    963 | 964 |
    965 |

    Relative module identifiers are interpreted relative to the 966 | current file, naturally.

    967 | 968 |
    969 | 970 |
          id.charAt(0) === "." ? fileAppendId(file, id, extensions) :
    971 | 972 |
  • 973 | 974 | 975 |
  • 976 |
    977 | 978 |
    979 | 980 |
    981 |

    Top-level module identifiers are interpreted as referring to 982 | packages in node_modules directories.

    983 | 984 |
    985 | 986 |
          nodeModulesLookup(file, id, extensions);
    987 | 988 |
  • 989 | 990 | 991 |
  • 992 |
    993 | 994 |
    995 | 996 |
    997 |

    If the identifier resolves to a directory, we use the same logic as 998 | Node to find an index.js or package.json file to evaluate.

    999 | 1000 |
    1001 | 1002 |
        while (fileIsDirectory(file)) {
    1003 |       seenDirFiles = seenDirFiles || [];
    1004 | 1005 |
  • 1006 | 1007 | 1008 |
  • 1009 |
    1010 | 1011 |
    1012 | 1013 |
    1014 |

    If the “main” field of a package.json file resolves to a 1015 | directory we’ve already considered, then we should not attempt to 1016 | read the same package.json file again. Using an array as a set 1017 | is acceptable here because the number of directories to consider 1018 | is rarely greater than 1 or 2. Also, using indexOf allows us to 1019 | store File objects instead of strings.

    1020 | 1021 |
    1022 | 1023 |
          if (seenDirFiles.indexOf(file) < 0) {
    1024 |         seenDirFiles.push(file);
    1025 | 
    1026 |         var pkgJsonFile = fileAppendIdPart(file, "package.json"), main;
    1027 |         var pkg = pkgJsonFile && fileEvaluate(pkgJsonFile, parentModule);
    1028 |         if (pkg &&
    1029 |             mainFields.some(function (name) {
    1030 |               return isString(main = pkg[name]);
    1031 |             })) {
    1032 | 1033 |
  • 1034 | 1035 | 1036 |
  • 1037 |
    1038 | 1039 |
    1040 | 1041 |
    1042 |

    The “main” field of package.json does not have to begin with 1043 | ./ to be considered relative, so first we try simply 1044 | appending it to the directory path before falling back to a 1045 | full fileResolve, which might return a package from a 1046 | node_modules directory.

    1047 | 1048 |
    1049 | 1050 |
              var mainFile = fileAppendId(file, main, extensions) ||
    1051 |             fileResolve(file, main, parentModule, seenDirFiles);
    1052 | 
    1053 |           if (mainFile) {
    1054 |             file = mainFile;
    1055 |             recordChild(parentModule, pkgJsonFile);
    1056 | 1057 |
  • 1058 | 1059 | 1060 |
  • 1061 |
    1062 | 1063 |
    1064 | 1065 |
    1066 |

    The fileAppendId call above may have returned a directory, 1067 | so continue the loop to make sure we resolve it to a 1068 | non-directory file.

    1069 | 1070 |
    1071 | 1072 |
                continue;
    1073 |           }
    1074 |         }
    1075 |       }
    1076 | 1077 |
  • 1078 | 1079 | 1080 |
  • 1081 |
    1082 | 1083 |
    1084 | 1085 |
    1086 |

    If we didn’t find a package.json file, or it didn’t have a 1087 | resolvable .main property, the only possibility left to 1088 | consider is that this directory contains an index.js module. 1089 | This assignment almost always terminates the while loop, because 1090 | there’s very little chance fileIsDirectory(file) will be true 1091 | for fileAppendIdPart(file, "index", extensions). However, in 1092 | principle it is remotely possible that a file called index.js 1093 | could be a directory instead of a file.

    1094 | 1095 |
    1096 | 1097 |
          file = fileAppendIdPart(file, "index", extensions);
    1098 |     }
    1099 | 
    1100 |     if (file && isString(file.contents)) {
    1101 |       file = fileResolve(file, file.contents, parentModule, seenDirFiles);
    1102 |     }
    1103 | 
    1104 |     recordChild(parentModule, file);
    1105 | 
    1106 |     return file;
    1107 |   };
    1108 | 
    1109 |   function nodeModulesLookup(file, id, extensions) {
    1110 |     for (var resolved; file && ! resolved; file = file.parent) {
    1111 |       resolved = fileIsDirectory(file) &&
    1112 |         fileAppendId(file, "node_modules/" + id, extensions);
    1113 |     }
    1114 |     return resolved;
    1115 |   }
    1116 | 
    1117 |   return install;
    1118 | };
    1119 | 
    1120 | if (typeof exports === "object") {
    1121 |   exports.makeInstaller = makeInstaller;
    1122 | }
    1123 | 1124 |
  • 1125 | 1126 |
1127 |
1128 | 1129 | 1130 | -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-bold.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-bold.woff -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-light.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-light.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/aller-light.woff -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/roboto-black.eot -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/roboto-black.ttf -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjamn/install/916924060877a846956158542165e90e457bb220/docs/public/fonts/roboto-black.woff -------------------------------------------------------------------------------- /docs/public/stylesheets/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 8/9. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | } 34 | 35 | /* 36 | * Prevents modern browsers from displaying `audio` without controls. 37 | * Remove excess height in iOS 5 devices. 38 | */ 39 | 40 | audio:not([controls]) { 41 | display: none; 42 | height: 0; 43 | } 44 | 45 | /* 46 | * Addresses styling for `hidden` attribute not present in IE 8/9. 47 | */ 48 | 49 | [hidden] { 50 | display: none; 51 | } 52 | 53 | /* ========================================================================== 54 | Base 55 | ========================================================================== */ 56 | 57 | /* 58 | * 1. Sets default font family to sans-serif. 59 | * 2. Prevents iOS text size adjust after orientation change, without disabling 60 | * user zoom. 61 | */ 62 | 63 | html { 64 | font-family: sans-serif; /* 1 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | } 68 | 69 | /* 70 | * Removes default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* ========================================================================== 78 | Links 79 | ========================================================================== */ 80 | 81 | /* 82 | * Addresses `outline` inconsistency between Chrome and other browsers. 83 | */ 84 | 85 | a:focus { 86 | outline: thin dotted; 87 | } 88 | 89 | /* 90 | * Improves readability when focused and also mouse hovered in all browsers. 91 | */ 92 | 93 | a:active, 94 | a:hover { 95 | outline: 0; 96 | } 97 | 98 | /* ========================================================================== 99 | Typography 100 | ========================================================================== */ 101 | 102 | /* 103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, 104 | * Safari 5, and Chrome. 105 | */ 106 | 107 | h1 { 108 | font-size: 2em; 109 | } 110 | 111 | /* 112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: 1px dotted; 117 | } 118 | 119 | /* 120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: bold; 126 | } 127 | 128 | /* 129 | * Addresses styling not present in Safari 5 and Chrome. 130 | */ 131 | 132 | dfn { 133 | font-style: italic; 134 | } 135 | 136 | /* 137 | * Addresses styling not present in IE 8/9. 138 | */ 139 | 140 | mark { 141 | background: #ff0; 142 | color: #000; 143 | } 144 | 145 | 146 | /* 147 | * Corrects font family set oddly in Safari 5 and Chrome. 148 | */ 149 | 150 | code, 151 | kbd, 152 | pre, 153 | samp { 154 | font-family: monospace, serif; 155 | font-size: 1em; 156 | } 157 | 158 | /* 159 | * Improves readability of pre-formatted text in all browsers. 160 | */ 161 | 162 | pre { 163 | white-space: pre; 164 | white-space: pre-wrap; 165 | word-wrap: break-word; 166 | } 167 | 168 | /* 169 | * Sets consistent quote types. 170 | */ 171 | 172 | q { 173 | quotes: "\201C" "\201D" "\2018" "\2019"; 174 | } 175 | 176 | /* 177 | * Addresses inconsistent and variable font size in all browsers. 178 | */ 179 | 180 | small { 181 | font-size: 80%; 182 | } 183 | 184 | /* 185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 186 | */ 187 | 188 | sub, 189 | sup { 190 | font-size: 75%; 191 | line-height: 0; 192 | position: relative; 193 | vertical-align: baseline; 194 | } 195 | 196 | sup { 197 | top: -0.5em; 198 | } 199 | 200 | sub { 201 | bottom: -0.25em; 202 | } 203 | 204 | /* ========================================================================== 205 | Embedded content 206 | ========================================================================== */ 207 | 208 | /* 209 | * Removes border when inside `a` element in IE 8/9. 210 | */ 211 | 212 | img { 213 | border: 0; 214 | } 215 | 216 | /* 217 | * Corrects overflow displayed oddly in IE 9. 218 | */ 219 | 220 | svg:not(:root) { 221 | overflow: hidden; 222 | } 223 | 224 | /* ========================================================================== 225 | Figures 226 | ========================================================================== */ 227 | 228 | /* 229 | * Addresses margin not present in IE 8/9 and Safari 5. 230 | */ 231 | 232 | figure { 233 | margin: 0; 234 | } 235 | 236 | /* ========================================================================== 237 | Forms 238 | ========================================================================== */ 239 | 240 | /* 241 | * Define consistent border, margin, and padding. 242 | */ 243 | 244 | fieldset { 245 | border: 1px solid #c0c0c0; 246 | margin: 0 2px; 247 | padding: 0.35em 0.625em 0.75em; 248 | } 249 | 250 | /* 251 | * 1. Corrects color not being inherited in IE 8/9. 252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 253 | */ 254 | 255 | legend { 256 | border: 0; /* 1 */ 257 | padding: 0; /* 2 */ 258 | } 259 | 260 | /* 261 | * 1. Corrects font family not being inherited in all browsers. 262 | * 2. Corrects font size not being inherited in all browsers. 263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome 264 | */ 265 | 266 | button, 267 | input, 268 | select, 269 | textarea { 270 | font-family: inherit; /* 1 */ 271 | font-size: 100%; /* 2 */ 272 | margin: 0; /* 3 */ 273 | } 274 | 275 | /* 276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in 277 | * the UA stylesheet. 278 | */ 279 | 280 | button, 281 | input { 282 | line-height: normal; 283 | } 284 | 285 | /* 286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 287 | * and `video` controls. 288 | * 2. Corrects inability to style clickable `input` types in iOS. 289 | * 3. Improves usability and consistency of cursor style between image-type 290 | * `input` and others. 291 | */ 292 | 293 | button, 294 | html input[type="button"], /* 1 */ 295 | input[type="reset"], 296 | input[type="submit"] { 297 | -webkit-appearance: button; /* 2 */ 298 | cursor: pointer; /* 3 */ 299 | } 300 | 301 | /* 302 | * Re-set default cursor for disabled elements. 303 | */ 304 | 305 | button[disabled], 306 | input[disabled] { 307 | cursor: default; 308 | } 309 | 310 | /* 311 | * 1. Addresses box sizing set to `content-box` in IE 8/9. 312 | * 2. Removes excess padding in IE 8/9. 313 | */ 314 | 315 | input[type="checkbox"], 316 | input[type="radio"] { 317 | box-sizing: border-box; /* 1 */ 318 | padding: 0; /* 2 */ 319 | } 320 | 321 | /* 322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 324 | * (include `-moz` to future-proof). 325 | */ 326 | 327 | input[type="search"] { 328 | -webkit-appearance: textfield; /* 1 */ 329 | -moz-box-sizing: content-box; 330 | -webkit-box-sizing: content-box; /* 2 */ 331 | box-sizing: content-box; 332 | } 333 | 334 | /* 335 | * Removes inner padding and search cancel button in Safari 5 and Chrome 336 | * on OS X. 337 | */ 338 | 339 | input[type="search"]::-webkit-search-cancel-button, 340 | input[type="search"]::-webkit-search-decoration { 341 | -webkit-appearance: none; 342 | } 343 | 344 | /* 345 | * Removes inner padding and border in Firefox 4+. 346 | */ 347 | 348 | button::-moz-focus-inner, 349 | input::-moz-focus-inner { 350 | border: 0; 351 | padding: 0; 352 | } 353 | 354 | /* 355 | * 1. Removes default vertical scrollbar in IE 8/9. 356 | * 2. Improves readability and alignment in all browsers. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; /* 1 */ 361 | vertical-align: top; /* 2 */ 362 | } 363 | 364 | /* ========================================================================== 365 | Tables 366 | ========================================================================== */ 367 | 368 | /* 369 | * Remove most spacing between table cells. 370 | */ 371 | 372 | table { 373 | border-collapse: collapse; 374 | border-spacing: 0; 375 | } -------------------------------------------------------------------------------- /install.js: -------------------------------------------------------------------------------- 1 | makeInstaller = function (options) { 2 | "use strict"; 3 | 4 | options = options || {}; 5 | 6 | // These file extensions will be appended to required module identifiers 7 | // if they do not exactly match an installed module. 8 | var defaultExtensions = options.extensions || [".js", ".json"]; 9 | 10 | // If defined, the options.fallback function will be called when no 11 | // installed module is found for a required module identifier. Often 12 | // options.fallback will be implemented in terms of the native Node 13 | // require function, which has the ability to load binary modules. 14 | var fallback = options.fallback; 15 | 16 | // List of fields to look for in package.json files to determine the 17 | // main entry module of the package. The first field listed here whose 18 | // value is a string will be used to resolve the entry module. 19 | var mainFields = options.mainFields || 20 | // If options.mainFields is absent and options.browser is truthy, 21 | // package resolution will prefer the "browser" field of package.json 22 | // files to the "main" field. Note that this only supports 23 | // string-valued "browser" fields for now, though in the future it 24 | // might make sense to support the object version, a la browserify. 25 | (options.browser ? ["browser", "main"] : ["main"]); 26 | 27 | var hasOwn = {}.hasOwnProperty; 28 | function strictHasOwn(obj, key) { 29 | return isObject(obj) && isString(key) && hasOwn.call(obj, key); 30 | } 31 | 32 | // Cache for looking up File objects given absolute module identifiers. 33 | // Invariants: 34 | // filesByModuleId[module.id] === fileAppendId(root, module.id) 35 | // filesByModuleId[module.id].module === module 36 | var filesByModuleId = {}; 37 | 38 | // The file object representing the root directory of the installed 39 | // module tree. 40 | var root = new File("/", new File("/..")); 41 | var rootRequire = makeRequire(root); 42 | 43 | // Merges the given tree of directories and module factory functions 44 | // into the tree of installed modules and returns a require function 45 | // that behaves as if called from a module in the root directory. 46 | function install(tree, options) { 47 | if (isObject(tree)) { 48 | fileMergeContents(root, tree, options); 49 | } 50 | return rootRequire; 51 | } 52 | 53 | // Replace this function to enable Module.prototype.prefetch. 54 | install.fetch = function (ids) { 55 | throw new Error("fetch not implemented"); 56 | }; 57 | 58 | // This constructor will be used to instantiate the module objects 59 | // passed to module factory functions (i.e. the third argument after 60 | // require and exports), and is exposed as install.Module in case the 61 | // caller of makeInstaller wishes to modify Module.prototype. 62 | function Module(id) { 63 | this.id = id; 64 | 65 | // The Node implementation of module.children unfortunately includes 66 | // only those child modules that were imported for the first time by 67 | // this parent module (i.e., child.parent === this). 68 | this.children = []; 69 | 70 | // This object is an install.js extension that includes all child 71 | // modules imported by this module, even if this module is not the 72 | // first to import them. 73 | this.childrenById = {}; 74 | } 75 | 76 | // Used to keep module.prefetch promise resolutions well-ordered. 77 | var lastPrefetchPromise; 78 | 79 | // May be shared by multiple sequential calls to module.prefetch. 80 | // Initialized to {} only when necessary. 81 | var missing; 82 | 83 | Module.prototype.prefetch = function (id) { 84 | var module = this; 85 | var parentFile = getOwn(filesByModuleId, module.id); 86 | 87 | lastPrefetchPromise = lastPrefetchPromise || Promise.resolve(); 88 | var previousPromise = lastPrefetchPromise; 89 | 90 | function walk(module) { 91 | var file = getOwn(filesByModuleId, module.id); 92 | if (fileIsDynamic(file) && ! file.pending) { 93 | file.pending = true; 94 | missing = missing || {}; 95 | 96 | // These are the data that will be exposed to the install.fetch 97 | // callback, so it's worth documenting each item with a comment. 98 | missing[module.id] = { 99 | // The CommonJS module object that will be exposed to this 100 | // dynamic module when it is evaluated. Note that install.fetch 101 | // could decide to populate module.exports directly, instead of 102 | // fetching anything. In that case, install.fetch should omit 103 | // this module from the tree that it produces. 104 | module: file.module, 105 | // List of module identifier strings imported by this module. 106 | // Note that the missing object already contains all available 107 | // dependencies (including transitive dependencies), so 108 | // install.fetch should not need to traverse these dependencies 109 | // in most cases; however, they may be useful for other reasons. 110 | // Though the strings are unique, note that two different 111 | // strings could resolve to the same module. 112 | deps: Object.keys(file.deps), 113 | // The options (if any) that were passed as the second argument 114 | // to the install(tree, options) function when this stub was 115 | // first registered. Typically contains options.extensions, but 116 | // could contain any information appropriate for the entire tree 117 | // as originally installed. These options will be automatically 118 | // inherited by the newly fetched modules, so install.fetch 119 | // should not need to modify them. 120 | options: file.options, 121 | // Any stub data included in the array notation from the 122 | // original entry for this dynamic module. Typically contains 123 | // "main" and/or "browser" fields for package.json files, and is 124 | // otherwise undefined. 125 | stub: file.stub 126 | }; 127 | 128 | each(file.deps, function (parentId, id) { 129 | fileResolve(file, id); 130 | }); 131 | 132 | each(module.childrenById, walk); 133 | } 134 | } 135 | 136 | return lastPrefetchPromise = new Promise(function (resolve) { 137 | var absChildId = module.resolve(id); 138 | each(module.childrenById, walk); 139 | resolve(absChildId); 140 | 141 | }).then(function (absChildId) { 142 | // Grab the current missing object and fetch its contents. 143 | var toBeFetched = missing; 144 | missing = null; 145 | 146 | function clearPending() { 147 | if (toBeFetched) { 148 | Object.keys(toBeFetched).forEach(function (id) { 149 | getOwn(filesByModuleId, id).pending = false; 150 | }); 151 | } 152 | } 153 | 154 | return new Promise(function (resolve) { 155 | // The install.fetch function takes an object mapping missing 156 | // dynamic module identifiers to options objects, and should 157 | // return a Promise that resolves to a module tree that can be 158 | // installed. As an optimization, if there were no missing dynamic 159 | // modules, then we can skip calling install.fetch entirely. 160 | resolve(toBeFetched && install.fetch(toBeFetched)); 161 | 162 | }).then(function (tree) { 163 | function both() { 164 | install(tree); 165 | clearPending(); 166 | return absChildId; 167 | } 168 | 169 | // Although we want multiple install.fetch calls to run in 170 | // parallel, it is important that the promises returned by 171 | // module.prefetch are resolved in the same order as the original 172 | // calls to module.prefetch, because previous fetches may include 173 | // modules assumed to exist by more recent module.prefetch calls. 174 | // Whether previousPromise was resolved or rejected, carry on with 175 | // the installation regardless. 176 | return previousPromise.then(both, both); 177 | 178 | }, function (error) { 179 | // Fixes https://github.com/meteor/meteor/issues/10182. 180 | clearPending(); 181 | throw error; 182 | }); 183 | }); 184 | }; 185 | 186 | install.Module = Module; 187 | 188 | function getOwn(obj, key) { 189 | return strictHasOwn(obj, key) && obj[key]; 190 | } 191 | 192 | function isObject(value) { 193 | return value !== null && typeof value === "object"; 194 | } 195 | 196 | function isFunction(value) { 197 | return typeof value === "function"; 198 | } 199 | 200 | function isString(value) { 201 | return typeof value === "string"; 202 | } 203 | 204 | function makeMissingError(id) { 205 | return new Error("Cannot find module '" + id + "'"); 206 | } 207 | 208 | Module.prototype.resolve = function (id) { 209 | var file = fileResolve(filesByModuleId[this.id], id); 210 | if (file) return file.module.id; 211 | var error = makeMissingError(id); 212 | if (fallback && isFunction(fallback.resolve)) { 213 | return fallback.resolve(id, this.id, error); 214 | } 215 | throw error; 216 | }; 217 | 218 | Module.prototype.require = function require(id) { 219 | var result = fileResolve(filesByModuleId[this.id], id); 220 | if (result) { 221 | return fileEvaluate(result, this); 222 | } 223 | 224 | var error = makeMissingError(id); 225 | 226 | if (isFunction(fallback)) { 227 | return fallback( 228 | id, // The missing module identifier. 229 | this.id, // ID of the parent module. 230 | error // The error we would have thrown. 231 | ); 232 | } 233 | 234 | throw error; 235 | }; 236 | 237 | function makeRequire(file) { 238 | var module = file.module; 239 | 240 | function require(id) { 241 | return module.require(id); 242 | } 243 | 244 | require.extensions = fileGetExtensions(file).slice(0); 245 | 246 | require.resolve = function resolve(id) { 247 | return module.resolve(id); 248 | }; 249 | 250 | return require; 251 | } 252 | 253 | // File objects represent either directories or modules that have been 254 | // installed. When a `File` respresents a directory, its `.contents` 255 | // property is an object containing the names of the files (or 256 | // directories) that it contains. When a `File` represents a module, its 257 | // `.contents` property is a function that can be invoked with the 258 | // appropriate `(require, exports, module)` arguments to evaluate the 259 | // module. If the `.contents` property is a string, that string will be 260 | // resolved as a module identifier, and the exports of the resulting 261 | // module will provide the exports of the original file. The `.parent` 262 | // property of a File is either a directory `File` or `null`. Note that 263 | // a child may claim another `File` as its parent even if the parent 264 | // does not have an entry for that child in its `.contents` object. 265 | // This is important for implementing anonymous files, and preventing 266 | // child modules from using `../relative/identifier` syntax to examine 267 | // unrelated modules. 268 | function File(moduleId, parent) { 269 | var file = this; 270 | 271 | // Link to the parent file. 272 | file.parent = parent = parent || null; 273 | 274 | // The module object for this File, which will eventually boast an 275 | // .exports property when/if the file is evaluated. 276 | file.module = new Module(moduleId); 277 | filesByModuleId[moduleId] = file; 278 | 279 | // The .contents of the file can be either (1) an object, if the file 280 | // represents a directory containing other files; (2) a factory 281 | // function, if the file represents a module that can be imported; (3) 282 | // a string, if the file is an alias for another file; or (4) null, if 283 | // the file's contents are not (yet) available. 284 | file.contents = null; 285 | 286 | // Set of module identifiers imported by this module. Note that this 287 | // set is not necessarily complete, so don't rely on it unless you 288 | // know what you're doing. 289 | file.deps = {}; 290 | } 291 | 292 | function fileEvaluate(file, parentModule) { 293 | var module = file.module; 294 | if (! strictHasOwn(module, "exports")) { 295 | var contents = file.contents; 296 | if (! contents) { 297 | // If this file was installed with array notation, and the array 298 | // contained one or more objects but no functions, then the combined 299 | // properties of the objects are treated as a temporary stub for 300 | // file.module.exports. This is particularly important for partial 301 | // package.json modules, so that the resolution logic can know the 302 | // value of the "main" and/or "browser" fields, at least, even if 303 | // the rest of the package.json file is not (yet) available. 304 | if (file.stub) { 305 | return file.stub; 306 | } 307 | 308 | throw makeMissingError(module.id); 309 | } 310 | 311 | if (parentModule) { 312 | module.parent = parentModule; 313 | var children = parentModule.children; 314 | if (Array.isArray(children)) { 315 | children.push(module); 316 | } 317 | } 318 | 319 | contents( 320 | makeRequire(file), 321 | // If the file had a .stub, reuse the same object for exports. 322 | module.exports = file.stub || {}, 323 | module, 324 | file.module.id, 325 | file.parent.module.id 326 | ); 327 | 328 | module.loaded = true; 329 | } 330 | 331 | // The module.runModuleSetters method will be deprecated in favor of 332 | // just module.runSetters: https://github.com/benjamn/reify/pull/160 333 | var runSetters = module.runSetters || module.runModuleSetters; 334 | if (isFunction(runSetters)) { 335 | runSetters.call(module); 336 | } 337 | 338 | return module.exports; 339 | } 340 | 341 | function fileIsDirectory(file) { 342 | return file && isObject(file.contents); 343 | } 344 | 345 | function fileIsDynamic(file) { 346 | return file && file.contents === null; 347 | } 348 | 349 | function fileMergeContents(file, contents, options) { 350 | if (Array.isArray(contents)) { 351 | contents.forEach(function (item) { 352 | if (isString(item)) { 353 | file.deps[item] = file.module.id; 354 | } else if (isFunction(item)) { 355 | contents = item; 356 | } else if (isObject(item)) { 357 | file.stub = file.stub || {}; 358 | each(item, function (value, key) { 359 | file.stub[key] = value; 360 | }); 361 | } 362 | }); 363 | 364 | if (! isFunction(contents)) { 365 | // If the array did not contain a function, merge nothing. 366 | contents = null; 367 | } 368 | 369 | } else if (! isFunction(contents) && 370 | ! isString(contents) && 371 | ! isObject(contents)) { 372 | // If contents is neither an array nor a function nor a string nor 373 | // an object, just give up and merge nothing. 374 | contents = null; 375 | } 376 | 377 | if (contents) { 378 | file.contents = file.contents || (isObject(contents) ? {} : contents); 379 | if (isObject(contents) && fileIsDirectory(file)) { 380 | each(contents, function (value, key) { 381 | if (key === "..") { 382 | child = file.parent; 383 | 384 | } else { 385 | var child = getOwn(file.contents, key); 386 | 387 | if (! child) { 388 | child = file.contents[key] = new File( 389 | file.module.id.replace(/\/*$/, "/") + key, 390 | file 391 | ); 392 | 393 | child.options = options; 394 | } 395 | } 396 | 397 | fileMergeContents(child, value, options); 398 | }); 399 | } 400 | } 401 | } 402 | 403 | function each(obj, callback, context) { 404 | Object.keys(obj).forEach(function (key) { 405 | callback.call(this, obj[key], key); 406 | }, context); 407 | } 408 | 409 | function fileGetExtensions(file) { 410 | return file.options 411 | && file.options.extensions 412 | || defaultExtensions; 413 | } 414 | 415 | function fileAppendIdPart(file, part, extensions) { 416 | // Always append relative to a directory. 417 | while (file && ! fileIsDirectory(file)) { 418 | file = file.parent; 419 | } 420 | 421 | if (! file || ! part || part === ".") { 422 | return file; 423 | } 424 | 425 | if (part === "..") { 426 | return file.parent; 427 | } 428 | 429 | var exactChild = getOwn(file.contents, part); 430 | 431 | // Only consider multiple file extensions if this part is the last 432 | // part of a module identifier and not equal to `.` or `..`, and there 433 | // was no exact match or the exact match was a directory. 434 | if (extensions && (! exactChild || fileIsDirectory(exactChild))) { 435 | for (var e = 0; e < extensions.length; ++e) { 436 | var child = getOwn(file.contents, part + extensions[e]); 437 | if (child && ! fileIsDirectory(child)) { 438 | return child; 439 | } 440 | } 441 | } 442 | 443 | return exactChild; 444 | } 445 | 446 | function fileAppendId(file, id, extensions) { 447 | var parts = id.split("/"); 448 | 449 | // Use `Array.prototype.every` to terminate iteration early if 450 | // `fileAppendIdPart` returns a falsy value. 451 | parts.every(function (part, i) { 452 | return file = i < parts.length - 1 453 | ? fileAppendIdPart(file, part) 454 | : fileAppendIdPart(file, part, extensions); 455 | }); 456 | 457 | return file; 458 | } 459 | 460 | function recordChild(parentModule, childFile) { 461 | var childModule = childFile && childFile.module; 462 | if (parentModule && childModule) { 463 | parentModule.childrenById[childModule.id] = childModule; 464 | } 465 | } 466 | 467 | function fileResolve(file, id, parentModule, seenDirFiles) { 468 | var parentModule = parentModule || file.module; 469 | var extensions = fileGetExtensions(file); 470 | 471 | file = 472 | // Absolute module identifiers (i.e. those that begin with a `/` 473 | // character) are interpreted relative to the root directory, which 474 | // is a slight deviation from Node, which has access to the entire 475 | // file system. 476 | id.charAt(0) === "/" ? fileAppendId(root, id, extensions) : 477 | // Relative module identifiers are interpreted relative to the 478 | // current file, naturally. 479 | id.charAt(0) === "." ? fileAppendId(file, id, extensions) : 480 | // Top-level module identifiers are interpreted as referring to 481 | // packages in `node_modules` directories. 482 | nodeModulesLookup(file, id, extensions); 483 | 484 | // If the identifier resolves to a directory, we use the same logic as 485 | // Node to find an `index.js` or `package.json` file to evaluate. 486 | while (fileIsDirectory(file)) { 487 | seenDirFiles = seenDirFiles || []; 488 | 489 | // If the "main" field of a `package.json` file resolves to a 490 | // directory we've already considered, then we should not attempt to 491 | // read the same `package.json` file again. Using an array as a set 492 | // is acceptable here because the number of directories to consider 493 | // is rarely greater than 1 or 2. Also, using indexOf allows us to 494 | // store File objects instead of strings. 495 | if (seenDirFiles.indexOf(file) < 0) { 496 | seenDirFiles.push(file); 497 | 498 | var pkgJsonFile = fileAppendIdPart(file, "package.json"); 499 | var pkg = pkgJsonFile && fileEvaluate(pkgJsonFile, parentModule); 500 | var mainFile, resolved = pkg && mainFields.some(function (name) { 501 | var main = pkg[name]; 502 | if (isString(main)) { 503 | // The "main" field of package.json does not have to begin 504 | // with ./ to be considered relative, so first we try 505 | // simply appending it to the directory path before 506 | // falling back to a full fileResolve, which might return 507 | // a package from a node_modules directory. 508 | return mainFile = fileAppendId(file, main, extensions) || 509 | fileResolve(file, main, parentModule, seenDirFiles); 510 | } 511 | }); 512 | 513 | if (resolved && mainFile) { 514 | file = mainFile; 515 | recordChild(parentModule, pkgJsonFile); 516 | // The fileAppendId call above may have returned a directory, 517 | // so continue the loop to make sure we resolve it to a 518 | // non-directory file. 519 | continue; 520 | } 521 | } 522 | 523 | // If we didn't find a `package.json` file, or it didn't have a 524 | // resolvable `.main` property, the only possibility left to 525 | // consider is that this directory contains an `index.js` module. 526 | // This assignment almost always terminates the while loop, because 527 | // there's very little chance `fileIsDirectory(file)` will be true 528 | // for `fileAppendIdPart(file, "index", extensions)`. However, in 529 | // principle it is remotely possible that a file called `index.js` 530 | // could be a directory instead of a file. 531 | file = fileAppendIdPart(file, "index", extensions); 532 | } 533 | 534 | if (file && isString(file.contents)) { 535 | file = fileResolve(file, file.contents, parentModule, seenDirFiles); 536 | } 537 | 538 | recordChild(parentModule, file); 539 | 540 | return file; 541 | }; 542 | 543 | function nodeModulesLookup(file, id, extensions) { 544 | for (var resolved; file && ! resolved; file = file.parent) { 545 | resolved = fileIsDirectory(file) && 546 | fileAppendId(file, "node_modules/" + id, extensions); 547 | } 548 | return resolved; 549 | } 550 | 551 | return install; 552 | }; 553 | 554 | if (typeof exports === "object") { 555 | exports.makeInstaller = makeInstaller; 556 | } 557 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "install", 3 | "version": "0.13.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.7.3", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", 10 | "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", 11 | "dev": true 12 | }, 13 | "balanced-match": { 14 | "version": "1.0.0", 15 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 16 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 17 | "dev": true 18 | }, 19 | "brace-expansion": { 20 | "version": "1.1.11", 21 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 22 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 23 | "dev": true, 24 | "requires": { 25 | "balanced-match": "^1.0.0", 26 | "concat-map": "0.0.1" 27 | } 28 | }, 29 | "browser-stdout": { 30 | "version": "1.3.1", 31 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 32 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 33 | "dev": true 34 | }, 35 | "buffer-from": { 36 | "version": "1.1.1", 37 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 38 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 39 | "dev": true 40 | }, 41 | "commander": { 42 | "version": "2.19.0", 43 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", 44 | "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", 45 | "dev": true 46 | }, 47 | "concat-map": { 48 | "version": "0.0.1", 49 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 50 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 51 | "dev": true 52 | }, 53 | "debug": { 54 | "version": "3.1.0", 55 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 56 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 57 | "dev": true, 58 | "requires": { 59 | "ms": "2.0.0" 60 | } 61 | }, 62 | "diff": { 63 | "version": "3.5.0", 64 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 65 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 66 | "dev": true 67 | }, 68 | "docco": { 69 | "version": "0.8.0", 70 | "resolved": "https://registry.npmjs.org/docco/-/docco-0.8.0.tgz", 71 | "integrity": "sha512-QcWBDnnGaT+rgC0wqynznXv0/4hd6nAFdWNs2fN4FvkH2yAnCYVeRU7GIZXNCeUQ955Lufq+TmZcSXiBa1cGQQ==", 72 | "dev": true, 73 | "requires": { 74 | "commander": ">= 0.5.2", 75 | "fs-extra": ">= 0.6.0", 76 | "highlight.js": ">= 8.0.x", 77 | "marked": ">= 0.2.7", 78 | "underscore": ">= 1.0.0" 79 | } 80 | }, 81 | "escape-string-regexp": { 82 | "version": "1.0.5", 83 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 84 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 85 | "dev": true 86 | }, 87 | "fs-extra": { 88 | "version": "7.0.0", 89 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", 90 | "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", 91 | "dev": true, 92 | "requires": { 93 | "graceful-fs": "^4.1.2", 94 | "jsonfile": "^4.0.0", 95 | "universalify": "^0.1.0" 96 | } 97 | }, 98 | "fs.realpath": { 99 | "version": "1.0.0", 100 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 101 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 102 | "dev": true 103 | }, 104 | "glob": { 105 | "version": "7.1.2", 106 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 107 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 108 | "dev": true, 109 | "requires": { 110 | "fs.realpath": "^1.0.0", 111 | "inflight": "^1.0.4", 112 | "inherits": "2", 113 | "minimatch": "^3.0.4", 114 | "once": "^1.3.0", 115 | "path-is-absolute": "^1.0.0" 116 | } 117 | }, 118 | "graceful-fs": { 119 | "version": "4.1.11", 120 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 121 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 122 | "dev": true 123 | }, 124 | "growl": { 125 | "version": "1.10.5", 126 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 127 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 128 | "dev": true 129 | }, 130 | "has-flag": { 131 | "version": "3.0.0", 132 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 133 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 134 | "dev": true 135 | }, 136 | "he": { 137 | "version": "1.1.1", 138 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 139 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 140 | "dev": true 141 | }, 142 | "highlight.js": { 143 | "version": "9.13.0", 144 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.0.tgz", 145 | "integrity": "sha512-2B90kcNnErqRTmzdZw6IPLEC9CdsiIMhj+r8L3LJKRCgtEJ+LY5yzWuQCVnADTI0wwocQinFzaaL/JjTQNqI/g==", 146 | "dev": true 147 | }, 148 | "inflight": { 149 | "version": "1.0.6", 150 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 151 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 152 | "dev": true, 153 | "requires": { 154 | "once": "^1.3.0", 155 | "wrappy": "1" 156 | } 157 | }, 158 | "inherits": { 159 | "version": "2.0.3", 160 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 161 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 162 | "dev": true 163 | }, 164 | "jsonfile": { 165 | "version": "4.0.0", 166 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 167 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 168 | "dev": true, 169 | "requires": { 170 | "graceful-fs": "^4.1.6" 171 | } 172 | }, 173 | "marked": { 174 | "version": "0.5.1", 175 | "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.1.tgz", 176 | "integrity": "sha512-iUkBZegCZou4AdwbKTwSW/lNDcz5OuRSl3qdcl31Ia0B2QPG0Jn+tKblh/9/eP9/6+4h27vpoh8wel/vQOV0vw==", 177 | "dev": true 178 | }, 179 | "minimatch": { 180 | "version": "3.0.4", 181 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 182 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 183 | "dev": true, 184 | "requires": { 185 | "brace-expansion": "^1.1.7" 186 | } 187 | }, 188 | "minimist": { 189 | "version": "0.0.8", 190 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 191 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 192 | "dev": true 193 | }, 194 | "mkdirp": { 195 | "version": "0.5.1", 196 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 197 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 198 | "dev": true, 199 | "requires": { 200 | "minimist": "0.0.8" 201 | } 202 | }, 203 | "mocha": { 204 | "version": "5.2.0", 205 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 206 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 207 | "dev": true, 208 | "requires": { 209 | "browser-stdout": "1.3.1", 210 | "commander": "2.15.1", 211 | "debug": "3.1.0", 212 | "diff": "3.5.0", 213 | "escape-string-regexp": "1.0.5", 214 | "glob": "7.1.2", 215 | "growl": "1.10.5", 216 | "he": "1.1.1", 217 | "minimatch": "3.0.4", 218 | "mkdirp": "0.5.1", 219 | "supports-color": "5.4.0" 220 | }, 221 | "dependencies": { 222 | "commander": { 223 | "version": "2.15.1", 224 | "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 225 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 226 | "dev": true 227 | } 228 | } 229 | }, 230 | "ms": { 231 | "version": "2.0.0", 232 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 233 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 234 | "dev": true 235 | }, 236 | "once": { 237 | "version": "1.4.0", 238 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 239 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 240 | "dev": true, 241 | "requires": { 242 | "wrappy": "1" 243 | } 244 | }, 245 | "path-is-absolute": { 246 | "version": "1.0.1", 247 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 248 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 249 | "dev": true 250 | }, 251 | "reify": { 252 | "version": "0.18.1", 253 | "resolved": "https://registry.npmjs.org/reify/-/reify-0.18.1.tgz", 254 | "integrity": "sha512-eNiNGxo5Cz/s/7DOeQW5+lTAxMexZPFA8XW/ef6f8WBLtQfYAhDNXxva7ROFC/Wa3q91usYzqJYwC85OXaWUzA==", 255 | "dev": true, 256 | "requires": { 257 | "acorn": "^5.5.3", 258 | "semver": "^5.4.1" 259 | } 260 | }, 261 | "semver": { 262 | "version": "5.6.0", 263 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 264 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", 265 | "dev": true 266 | }, 267 | "source-map": { 268 | "version": "0.6.1", 269 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 270 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 271 | "dev": true 272 | }, 273 | "source-map-support": { 274 | "version": "0.5.10", 275 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", 276 | "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", 277 | "dev": true, 278 | "requires": { 279 | "buffer-from": "^1.0.0", 280 | "source-map": "^0.6.0" 281 | } 282 | }, 283 | "supports-color": { 284 | "version": "5.4.0", 285 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 286 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 287 | "dev": true, 288 | "requires": { 289 | "has-flag": "^3.0.0" 290 | } 291 | }, 292 | "terser": { 293 | "version": "3.16.0", 294 | "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.0.tgz", 295 | "integrity": "sha512-Ua8BhyibmsQBFXDZZ3Es7GASB2yFrQJr0jgAlZK1FBLbFarrHoCuMHPCro5MbX4jidcaFGiV+uTc3wxodmGjUg==", 296 | "dev": true, 297 | "requires": { 298 | "commander": "~2.17.1", 299 | "source-map": "~0.6.1", 300 | "source-map-support": "~0.5.9" 301 | }, 302 | "dependencies": { 303 | "commander": { 304 | "version": "2.17.1", 305 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", 306 | "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", 307 | "dev": true 308 | } 309 | } 310 | }, 311 | "underscore": { 312 | "version": "1.9.1", 313 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", 314 | "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", 315 | "dev": true 316 | }, 317 | "universalify": { 318 | "version": "0.1.2", 319 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 320 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 321 | "dev": true 322 | }, 323 | "wrappy": { 324 | "version": "1.0.2", 325 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 326 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 327 | "dev": true 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Ben Newman", 4 | "email": "bn@cs.stanford.edu" 5 | }, 6 | "name": "install", 7 | "version": "0.13.0", 8 | "description": "Minimal JavaScript module loader", 9 | "keywords": [ 10 | "modules", 11 | "require", 12 | "commonjs", 13 | "exports", 14 | "browser", 15 | "packaging", 16 | "packager", 17 | "install" 18 | ], 19 | "license": "MIT", 20 | "homepage": "http://github.com/benjamn/install", 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com/benjamn/install.git" 24 | }, 25 | "main": "install.js", 26 | "scripts": { 27 | "prepublish": "scripts/prepublish.sh", 28 | "docs": "scripts/docs.sh", 29 | "test": "mocha --reporter spec --full-trace test/run.js" 30 | }, 31 | "devDependencies": { 32 | "docco": "^0.8.0", 33 | "mocha": "^5.0.0", 34 | "reify": "^0.18.1", 35 | "terser": "^3.16.0" 36 | }, 37 | "license": "MIT", 38 | "engines": { 39 | "node": ">= 0.10" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd $(dirname $0)/.. 4 | docco install.js 5 | cd docs 6 | mv install.html index.html 7 | cd .. 8 | -------------------------------------------------------------------------------- /scripts/prepublish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd $(dirname $0)/.. 4 | 5 | terser install.js -c -m > install.min.js 6 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var makeInstaller = require("../install.js").makeInstaller; 3 | var reify = require("reify/lib/runtime").enable; 4 | 5 | describe("install", function () { 6 | it("binds this to global", function () { 7 | makeInstaller()({ 8 | "index.js": function () { 9 | assert.strictEqual( 10 | Object.prototype.toString.call(this), 11 | "[object global]" 12 | ); 13 | } 14 | })("."); 15 | }); 16 | 17 | it("permits synchronous require", function () { 18 | var install = makeInstaller(); 19 | 20 | var require = install({ 21 | "foo.js": function (require, exports, module) { 22 | assert.strictEqual(typeof module, "object"); 23 | assert.strictEqual(require(module.id), exports); 24 | module.exports = require("asdf"); 25 | assert.strictEqual(require(module.id), module.exports); 26 | }, 27 | node_modules: { 28 | asdf: { 29 | "package.json": function (require, exports, module) { 30 | exports.main = "./lib"; 31 | assert.strictEqual(require(module.id), exports); 32 | }, 33 | lib: { 34 | "index.js": function (require, exports, module) { 35 | exports.asdfMain = true; 36 | assert.strictEqual(require(module.id), exports); 37 | } 38 | } 39 | } 40 | } 41 | }); 42 | 43 | assert.deepEqual(require("./foo"), { 44 | asdfMain: true 45 | }); 46 | 47 | assert.deepEqual(require("asdf"), { 48 | asdfMain: true 49 | }); 50 | 51 | assert.strictEqual(require("./foo"), require("./foo.js")); 52 | }); 53 | 54 | it("supports a variety of relative identifiers", function () { 55 | var install = makeInstaller(); 56 | var value = {}; 57 | 58 | function n(require, exports) { 59 | assert.strictEqual(require("./o").value, value); 60 | assert.strictEqual(require("../to/o").value, value); 61 | assert.strictEqual(require("../../path/to/o").value, value); 62 | assert.strictEqual(require("..//.././path/to/o").value, value); 63 | assert.strictEqual(require(".././.././path/to/o").value, value); 64 | assert.strictEqual(require("../..//path/to/o").value, value); 65 | assert.strictEqual(require("../..//path/./to/o").value, value); 66 | assert.strictEqual(require("../..//path/./to/../to/o").value, value); 67 | assert.strictEqual(require("../to/../to/o").value, value); 68 | assert.strictEqual(require("../to/../../path/to/o").value, value); 69 | assert.strictEqual(require("./o/index").value, value); 70 | assert.strictEqual(require("./o/index.js").value, value); 71 | } 72 | 73 | install({ 74 | path: { 75 | to: { 76 | "n.js": n, 77 | o: { 78 | "index.js": function(r, exports) { 79 | exports.value = value; 80 | } 81 | } 82 | } 83 | } 84 | })("./path/to/n"); 85 | }); 86 | 87 | it("supports global modules", function () { 88 | var install = makeInstaller(); 89 | 90 | install({ 91 | node_modules: { 92 | glob: { 93 | "package.json": function (r, exports) { 94 | exports.main = "glob.js"; 95 | }, 96 | "glob.js": function (r, exports) { 97 | exports.glob = "global glob"; 98 | } 99 | }, 100 | "assert.js": function (r, exports) { 101 | exports.assert = "global assert"; 102 | } 103 | } 104 | }); 105 | 106 | install({ 107 | app: { 108 | "index1.js": function (require) { 109 | assert.deepEqual(require("glob"), { glob: "global glob" }); 110 | assert.deepEqual(require("assert"), { assert: "global assert" }); 111 | } 112 | } 113 | })("./app/index1"); 114 | 115 | install({ 116 | app: { 117 | node_modules: { 118 | glob: { 119 | "index.js": function (r, exports) { 120 | exports.glob = "local glob"; 121 | } 122 | } 123 | }, 124 | "index2.js": function (require) { 125 | assert.deepEqual(require("glob"), { glob: "local glob" }); 126 | assert.deepEqual(require("assert"), { assert: "global assert" }); 127 | } 128 | } 129 | })("./app/index2"); 130 | }); 131 | 132 | it("allows any value for module.exports", function () { 133 | var obj = {}; 134 | var fun = function () {}; 135 | 136 | var require = makeInstaller()({ 137 | "object": function (r, e, module) { 138 | module.exports = obj; 139 | }, 140 | 141 | "function": function (r, e, module) { 142 | module.exports = fun; 143 | }, 144 | 145 | "false": function (r, e, module) { 146 | module.exports = false; 147 | }, 148 | 149 | "null": function (r, e, module) { 150 | module.exports = null; 151 | }, 152 | 153 | "undefined": function (r, e, module) { 154 | var undef; 155 | module.exports = undef; 156 | } 157 | }); 158 | 159 | assert.strictEqual(require("./object"), obj); 160 | assert.strictEqual(require("./function"), fun); 161 | assert.strictEqual(require("./false"), false); 162 | assert.strictEqual(require("./null"), null); 163 | assert.strictEqual(require("./undefined"), void(0)); 164 | }); 165 | 166 | it("copes with long dependency chains", function () { 167 | var n = 500; 168 | var count = 0; 169 | var install = makeInstaller({ 170 | defer: setImmediate 171 | }); 172 | 173 | for (var i = 1; i <= n; ++i) { 174 | (function (i, tree) { 175 | var array = tree["m" + i] = []; 176 | 177 | // Everything depends on everything else. 178 | for (var j = n - 1; j >= 0; --j) { 179 | array.push("./m" + j); 180 | } 181 | 182 | array.push(function module(require, exports) { 183 | exports.value = 1 + require("./m" + (i - 1)).value; 184 | }); 185 | 186 | install(tree); 187 | })(i, {}); 188 | } 189 | 190 | var require = install({ 191 | m0: function (require, exports) { 192 | exports.value = 0; 193 | } 194 | }); 195 | 196 | var lastId = "./m" + (i - 1); 197 | assert.strictEqual(require(lastId).value, n); 198 | }); 199 | 200 | it("prefers fuzzy files to exact directories", function () { 201 | var install = makeInstaller(); 202 | var require = install({ 203 | "node_modules": { 204 | "foo.js": function (r, exports) { 205 | exports.file = true; 206 | }, 207 | "foo": { 208 | "index.js": function (require, exports) { 209 | exports.directory = true; 210 | assert.deepEqual(require("foo"), { file: true }); 211 | } 212 | } 213 | } 214 | }); 215 | 216 | assert.deepEqual(require("foo.js"), { file: true }); 217 | assert.deepEqual(require("foo"), { file: true }); 218 | assert.deepEqual(require("foo/"), { directory: true }); 219 | assert.deepEqual(require("foo/."), { directory: true }); 220 | assert.deepEqual(require("foo/index"), { directory: true }); 221 | 222 | assert.deepEqual(require("./node_modules/foo.js"), { file: true }); 223 | assert.deepEqual(require("./node_modules/foo"), { file: true }); 224 | assert.deepEqual(require("./node_modules/foo/"), { directory: true }); 225 | assert.deepEqual(require("./node_modules/foo/."), { directory: true }); 226 | assert.deepEqual(require("./node_modules/foo/index"), { directory: true }); 227 | }); 228 | 229 | it("supports options.fallback", function (done) { 230 | var unknown = {}; 231 | 232 | var install = makeInstaller({ 233 | fallback: function (id, parentId, error) { 234 | assert.strictEqual(id, "unknown-module"); 235 | assert.strictEqual(parentId, "/foo/bar/parent.js"); 236 | assert.ok(error instanceof Error); 237 | return unknown; 238 | } 239 | }); 240 | 241 | var require = install({ 242 | foo: { 243 | bar: { 244 | "parent.js": function (require) { 245 | assert.strictEqual( 246 | require("unknown-module"), 247 | unknown 248 | ); 249 | 250 | done(); 251 | } 252 | } 253 | } 254 | }); 255 | 256 | require("./foo/bar/parent"); 257 | }); 258 | 259 | it("supports options.fallback.resolve", function () { 260 | var install = makeInstaller({ 261 | fallback: { 262 | resolve: function (id, parentId, error) { 263 | if (id === "assert") return id; 264 | if (id === "path") return "paaath"; 265 | throw error; 266 | } 267 | } 268 | }); 269 | 270 | var require = install({ 271 | a: function (require, exports) { 272 | exports.assertId = require.resolve("assert"); 273 | }, 274 | 275 | b: function (r, exports, module) { 276 | exports.pathId = module.resolve("path"); 277 | } 278 | }); 279 | 280 | assert.strictEqual(require.resolve("assert"), "assert"); 281 | assert.strictEqual(require.resolve("path"), "paaath"); 282 | 283 | assert.strictEqual(require("./a").assertId, "assert"); 284 | assert.strictEqual(require("./b").pathId, "paaath"); 285 | }); 286 | 287 | it("supports symbolic links", function () { 288 | var install = makeInstaller(); 289 | var require = install({ 290 | a: "./dir/c", 291 | dir: { 292 | b: "./c", 293 | c: function (require, exports, module) { 294 | exports.id = module.id; 295 | }, 296 | d: "e", 297 | node_modules: { 298 | "e.js": ["f", function (require, exports, module) { 299 | exports.id = module.id; 300 | }] 301 | } 302 | } 303 | }); 304 | 305 | var a = require("./a"); 306 | var b = require("./dir/b"); 307 | var c = require("./dir/c"); 308 | 309 | assert.strictEqual(a, c); 310 | assert.strictEqual(b, c); 311 | assert.strictEqual(c.id, "/dir/c"); 312 | 313 | install({ 314 | dir: { 315 | node_modules: { 316 | f: { 317 | // Because there is no index.js or package.json, the f package 318 | // should still not be ready. 319 | } 320 | } 321 | } 322 | }); 323 | 324 | install({ 325 | dir: { 326 | node_modules: { 327 | f: { 328 | "index.js": function() {} 329 | } 330 | } 331 | } 332 | }); 333 | 334 | assert.strictEqual(require("./dir/d").id, "/dir/node_modules/e.js"); 335 | assert.strictEqual( 336 | require.resolve("./dir/d"), 337 | "/dir/node_modules/e.js" 338 | ); 339 | }); 340 | 341 | it("avoids circular package.json resolution chains", function () { 342 | makeInstaller()({ 343 | // Module a imports package b, whose package.json file delegates to 344 | // package c, whose package.json file delegates to c's own 345 | // directory, which contains an index.js file symbolically linked 346 | // back to package b, whose index.js file takes precedence over 347 | // package.json because we already examined b's package.json file. 348 | 349 | a: function (require) { 350 | assert.strictEqual(require("b").name, "/node_modules/b/index.js"); 351 | }, 352 | 353 | node_modules: { 354 | b: { 355 | "package.json": function (r, exports) { 356 | exports.main = "c"; 357 | }, 358 | 359 | "index.js": function (r, exports, module) { 360 | exports.name = module.id; 361 | } 362 | }, 363 | 364 | c: { 365 | "package.json": function (r, exports) { 366 | exports.main = "."; 367 | }, 368 | 369 | "index.js": "b" 370 | } 371 | } 372 | })("./a"); 373 | }); 374 | 375 | it("provides __filename and __dirname", function (done) { 376 | var require = makeInstaller()({ 377 | a: { 378 | b: { 379 | "c.js": function (r, e, m, __filename, __dirname) { 380 | assert.strictEqual(__filename, "/a/b/c.js"); 381 | assert.strictEqual(__dirname, "/a/b"); 382 | done(); 383 | } 384 | } 385 | }, 386 | 387 | "d.js": function (r, e, m, __filename, __dirname) { 388 | assert.strictEqual(__filename, "/d.js"); 389 | assert.strictEqual(__dirname, "/"); 390 | } 391 | }); 392 | 393 | require("./a/b/c"); 394 | require("./d"); 395 | }); 396 | 397 | it("allows alternate extensions", function (done) { 398 | makeInstaller()({ 399 | "a.js": function (require) { 400 | assert.strictEqual(require("./b").name, "/b.foo"); 401 | assert.strictEqual(require("/b").name, "/b.foo"); 402 | done(); 403 | }, 404 | 405 | "b.foo": function (r, exports, module) { 406 | exports.name = module.id; 407 | } 408 | }, { 409 | extensions: [".js", ".json", ".foo"] 410 | })("./a"); 411 | }); 412 | 413 | it("allows global installation", function () { 414 | var install = makeInstaller(); 415 | 416 | var require = install({ 417 | node_modules: { 418 | a: { 419 | "index.js": function (r, exports) { 420 | exports.value = "normal"; 421 | } 422 | } 423 | }, 424 | 425 | "..": { 426 | node_modules: { 427 | "a.js": function (r, exports) { 428 | exports.value = "global"; 429 | } 430 | } 431 | } 432 | }); 433 | 434 | assert.strictEqual(require("a").value, "normal"); 435 | assert.strictEqual(require("a.js").value, "global"); 436 | assert.strictEqual( 437 | require.resolve("a.js"), 438 | "/../node_modules/a.js" 439 | ); 440 | }); 441 | 442 | it("supports module.parent", function (done) { 443 | var install = makeInstaller(); 444 | var require = install({ 445 | a: function (require, exports, module) { 446 | assert.strictEqual(module.parent.id, "/"); 447 | assert.strictEqual(typeof module.parent.parent, "undefined"); 448 | require("b"); 449 | }, 450 | 451 | node_modules: { 452 | b: { 453 | "index.js": function (require, exports, module) { 454 | assert.strictEqual(module.parent.id, "/a"); 455 | require("c"); 456 | } 457 | }, 458 | 459 | c: { 460 | "package.json": function (require, exports, module) { 461 | exports.main = "final.js"; 462 | }, 463 | 464 | "final.js": function (require, exports, module) { 465 | assert.strictEqual(module.parent.id, "/node_modules/b/index.js"); 466 | assert.strictEqual(module.parent.parent.id, "/a"); 467 | done(); 468 | } 469 | } 470 | } 471 | }); 472 | 473 | require("./a"); 474 | }); 475 | 476 | it("runs setters", function () { 477 | var install = makeInstaller(); 478 | var markers = []; 479 | var require = install({ 480 | a: function (r, exports, module) { 481 | exports.one = 1; 482 | 483 | // Enable module.link. 484 | reify(module); 485 | 486 | module.link("./b", { 487 | one: function (v) { 488 | markers.push("ab1", v); 489 | }, 490 | 491 | two: function (v) { 492 | markers.push("ab2", v); 493 | } 494 | }); 495 | 496 | exports.two = 2; 497 | }, 498 | 499 | b: function (r, exports, module) { 500 | exports.one = 1; 501 | 502 | // Enable module.link. 503 | reify(module); 504 | 505 | module.link("./a", { 506 | one: function (v) { 507 | markers.push("ba1", v); 508 | }, 509 | 510 | two: function (v) { 511 | markers.push("ba2", v); 512 | } 513 | }); 514 | 515 | exports.two = 2; 516 | } 517 | }); 518 | 519 | assert.deepEqual(require("./a"), { 520 | one: 1, 521 | two: 2 522 | }); 523 | 524 | assert.deepEqual(markers, [ 525 | "ab1", void 0, 526 | "ab2", void 0, 527 | "ba1", 1, 528 | "ba2", void 0, 529 | "ab1", 1, 530 | "ab2", 2, 531 | "ba2", 2 532 | ]); 533 | }); 534 | 535 | it("supports options.browser", function () { 536 | var require = makeInstaller({ 537 | browser: true 538 | })({ 539 | a: function (require, exports, module) { 540 | exports.name = require("./dir").name; 541 | }, 542 | dir: { 543 | "package.json": function (require, exports, module) { 544 | exports.main = "nonexistent"; 545 | exports.browser = "client.js"; 546 | }, 547 | "client.js": function (require, exports, module) { 548 | exports.name = module.id; 549 | } 550 | } 551 | }); 552 | 553 | assert.strictEqual( 554 | require("./a").name, 555 | "/dir/client.js" 556 | ); 557 | }); 558 | 559 | it("supports pkg.module", function () { 560 | function check(makeInstallerOptions) { 561 | var require = makeInstaller( 562 | makeInstallerOptions 563 | )({ 564 | a: function (require, exports, module) { 565 | exports.name = require("./dir").name; 566 | }, 567 | dir: { 568 | "package.json": function (require, exports, module) { 569 | exports.main = "nonexistent"; 570 | exports.module = "client.mjs"; 571 | exports.browser = "client.js"; 572 | }, 573 | "client.mjs": function (require, exports, module) { 574 | exports.name = module.id; 575 | }, 576 | "client.js": function (require, exports, module) { 577 | exports.name = module.id; 578 | } 579 | } 580 | }); 581 | 582 | return require("./a"); 583 | } 584 | 585 | assert.strictEqual( 586 | check({ 587 | browser: true, 588 | // This option takes precedence over options.browser, so the 589 | // exports.browser field below will be ignored. 590 | mainFields: ["module", "main"] 591 | }).name, 592 | "/dir/client.mjs" 593 | ); 594 | 595 | assert.strictEqual( 596 | check({ 597 | mainFields: ["module", "browser", "main"] 598 | }).name, 599 | "/dir/client.mjs" 600 | ); 601 | 602 | assert.strictEqual( 603 | check({ 604 | mainFields: ["browser", "module", "main"] 605 | }).name, 606 | "/dir/client.js" 607 | ); 608 | 609 | assert.strictEqual( 610 | check({ 611 | mainFields: ["module", "browser"] 612 | }).name, 613 | "/dir/client.mjs" 614 | ); 615 | 616 | assert.strictEqual( 617 | check({ 618 | mainFields: ["browser", "module"] 619 | }).name, 620 | "/dir/client.js" 621 | ); 622 | 623 | assert.strictEqual( 624 | check({ 625 | browser: true 626 | }).name, 627 | "/dir/client.js" 628 | ); 629 | }); 630 | 631 | it("exposes require.extensions", function () { 632 | var install = makeInstaller({ 633 | extensions: [".js", ".json", ".css"] 634 | }); 635 | 636 | var require = install({ 637 | "a.js": function (require, exports, module) { 638 | assert.deepEqual( 639 | require.extensions, 640 | [".js", ".json", ".css"] 641 | ); 642 | 643 | assert.strictEqual( 644 | require("./c").name, 645 | "/c.css" 646 | ); 647 | 648 | exports.name = module.id; 649 | }, 650 | 651 | "c.css": function (require, exports, module) { 652 | exports.name = module.id; 653 | } 654 | }); 655 | 656 | install({ 657 | "b.js": function (require, exports, module) { 658 | assert.deepEqual( 659 | require.extensions, 660 | [".js", ".json", ".html"] 661 | ); 662 | 663 | assert.strictEqual( 664 | require("./c").name, 665 | "/c.html" 666 | ); 667 | 668 | exports.name = module.id; 669 | }, 670 | 671 | "c.html": function (require, exports, module) { 672 | exports.name = module.id; 673 | } 674 | }, { 675 | extensions: [".js", ".json", ".html"] 676 | }); 677 | 678 | assert.strictEqual(require("./a").name, "/a.js"); 679 | assert.strictEqual(require("./b").name, "/b.js"); 680 | }); 681 | 682 | it("module.children collects package.json modules", function () { 683 | var require = makeInstaller()({ 684 | "parent.js": function (require, exports, module) { 685 | assert.deepEqual(module.children, []); 686 | 687 | assert.strictEqual( 688 | require("./child").name, 689 | "/child/main.js" 690 | ); 691 | 692 | exports.children = module.children; 693 | }, 694 | 695 | child: { 696 | "package.json": function (r, exports) { 697 | exports.main = "main"; 698 | }, 699 | 700 | "main.js": function (r, exports, module) { 701 | exports.name = module.id; 702 | } 703 | } 704 | }); 705 | 706 | var ids = require("./parent").children.map(function (child) { 707 | return child.id; 708 | }); 709 | 710 | assert.deepEqual(ids, [ 711 | "/child/package.json", 712 | "/child/main.js" 713 | ]); 714 | }); 715 | 716 | it("module.childrenById collects all children", function () { 717 | var require = makeInstaller()({ 718 | "parent.js": function (require, exports, module) { 719 | assert.deepEqual(module.childrenById, {}); 720 | 721 | var childId = require("./child").name; 722 | assert.strictEqual(childId, require.resolve("./child")); 723 | assert.deepEqual(Object.keys(module.childrenById), [childId]); 724 | 725 | require(module.id); 726 | assert.deepEqual( 727 | Object.keys(module.childrenById), 728 | [childId, module.id] 729 | ); 730 | 731 | assert.deepEqual(module.children, []); 732 | }, 733 | 734 | "child.js": function (require, exports, module) { 735 | assert.deepEqual(module.childrenById, {}); 736 | require(exports.name = module.id); 737 | assert.strictEqual(module.childrenById[module.id], module); 738 | assert.deepEqual( 739 | Object.keys(module.childrenById), 740 | [module.id] 741 | ); 742 | } 743 | }); 744 | 745 | assert.strictEqual(require("./child").name, "/child.js"); 746 | 747 | require("./parent"); 748 | }); 749 | 750 | it("module.childrenById accounts for aliases", function () { 751 | var require = makeInstaller()({ 752 | "a.js": function (require, exports, module) { 753 | assert.deepEqual(Object.keys(module.childrenById), []); 754 | exports.childName = require("./alias1").name; 755 | assert.deepEqual(Object.keys(module.childrenById), [ 756 | "/node_modules/one/package.json", 757 | "/node_modules/two/package.json", 758 | "/node_modules/two/main.js", 759 | ]); 760 | }, 761 | 762 | "alias1.js": "one", 763 | 764 | node_modules: { 765 | one: { 766 | "package.json": function (r, exports) { 767 | exports.main = "alias2"; 768 | }, 769 | 770 | "alias2.js": "two", 771 | }, 772 | 773 | two: { 774 | "package.json": function (r, exports) { 775 | exports.main = "main"; 776 | }, 777 | 778 | "main.js": function (r, exports, module) { 779 | exports.name = module.id; 780 | } 781 | } 782 | } 783 | }); 784 | 785 | assert.strictEqual( 786 | require("./a").childName, 787 | "/node_modules/two/main.js" 788 | ); 789 | }); 790 | 791 | it("supports module.exports stubs in array notation", function () { 792 | var install = makeInstaller(); 793 | 794 | install({ 795 | "a.js": function (require, exports) { 796 | assert.strictEqual(require("b").name, "/node_modules/b/index.js"); 797 | 798 | var bPkg = require("b/package"); 799 | assert.deepEqual(bPkg, { main: "index.js" }); 800 | 801 | // Now install the "real" package.json module. 802 | install({ 803 | node_modules: { 804 | b: { 805 | "package.json": function (require, exports) { 806 | // For consistency, if there was a stub, the same object 807 | // should be used for module.exports when the actual 808 | // module is first evaluated. 809 | assert.strictEqual(exports, bPkg); 810 | exports.version = "1.2.3"; 811 | } 812 | } 813 | } 814 | }); 815 | 816 | assert.deepEqual(require("b/package"), { 817 | main: "index.js", 818 | version: "1.2.3" 819 | }); 820 | }, 821 | 822 | node_modules: { 823 | b: { 824 | // If a module is defined with array notation, and the array 825 | // contains one or more objects but no functions, then the 826 | // combined properties of the objects are treated as a temporary 827 | // stub for module.exports. 828 | "package.json": [{ 829 | main: "index.js" 830 | }], 831 | 832 | "index.js": function (r, exports, module) { 833 | exports.name = module.id; 834 | } 835 | } 836 | } 837 | })("/a.js"); 838 | }); 839 | 840 | function addToTree(tree, id, value) { 841 | var parts = id.split("/"); 842 | var lastIndex = parts.length - 1; 843 | parts.forEach(function (part, i) { 844 | if (part) { 845 | tree = tree[part] = tree[part] || 846 | (i < lastIndex ? Object.create(null) : value); 847 | } 848 | }); 849 | } 850 | 851 | it("supports Module.prototype.prefetch and options.prefetch", function () { 852 | var options = {}; 853 | var install = makeInstaller(); 854 | 855 | install.fetch = function (ids) { 856 | var tree = {}; 857 | 858 | Object.keys(ids).forEach(function (id) { 859 | var info = ids[id]; 860 | assert.strictEqual(info.options, options); 861 | assert.strictEqual(info.module.id, id); 862 | addToTree(tree, id, function (r, exports, module) { 863 | assert.strictEqual(module, info.module); 864 | exports.name = module.id; 865 | }); 866 | }); 867 | 868 | return tree; 869 | }; 870 | 871 | return install({ 872 | "a.js": function (require, exports, module) { 873 | module.exports = module.prefetch("./b").then(function (id) { 874 | assert.strictEqual(id, "/b.js"); 875 | assert.strictEqual(require("./b").name, id); 876 | assert.strictEqual(require("./c").name, "/c.js"); 877 | 878 | assert.strictEqual( 879 | require("d").name, 880 | "/node_modules/d/index.js" 881 | ); 882 | 883 | assert.strictEqual( 884 | require("d/package").name, 885 | "/node_modules/d/package.json" 886 | ); 887 | 888 | return module.prefetch("./nonexistent").catch(function (error) { 889 | assert.ok(error instanceof Error); 890 | assert.ok(error.message.startsWith("Cannot find module")); 891 | }); 892 | }); 893 | }, 894 | 895 | "b.js": ["./c", "d"], 896 | "c.js": ["d", "./b"], 897 | 898 | node_modules: { 899 | d: { 900 | "package.json": [{ 901 | main: "index" 902 | }], 903 | "index.js": [] 904 | } 905 | } 906 | }, options)("/a.js"); 907 | }); 908 | 909 | it("enforces ordering of module.prefetch promise resolution", function () { 910 | var install = makeInstaller(); 911 | 912 | function exportName(r, exports, module) { 913 | exports.name = module.id; 914 | } 915 | 916 | // This install.fetch function always resolves b.js before a.js, even 917 | // though module.prefetch is called in the other order. 918 | install.fetch = function (ids) { 919 | var keys = Object.keys(ids); 920 | assert.strictEqual(keys.length, 2); 921 | var tree = {}; 922 | keys.forEach(function (key) { 923 | tree[key.split("/").pop()] = exportName; 924 | }); 925 | return tree; 926 | }; 927 | 928 | var require = install({ 929 | "main.js": function (require, exports, module) { 930 | var order = []; 931 | function record(id) { 932 | order.push(id); 933 | } 934 | 935 | module.exports = Promise.all([ 936 | module.prefetch("./a").then(record), 937 | module.prefetch("./b").then(record), 938 | ]).then(function () { 939 | assert.deepEqual(order, ["/a.js", "/b.js"]); 940 | assert.strictEqual(require("./a").name, order[0]); 941 | assert.strictEqual(require("./b").name, order[1]); 942 | }); 943 | }, 944 | "a.js": [], 945 | "b.js": [] 946 | }); 947 | 948 | return require("./main"); 949 | }); 950 | 951 | it("batches module.prefetch calls into one install.fetch call", function () { 952 | var install = makeInstaller(); 953 | var fetchCallCount = 0; 954 | 955 | install.fetch = function (ids) { 956 | ++fetchCallCount; 957 | assert.deepEqual(Object.keys(ids).sort(), [ 958 | "/a.js", 959 | "/b.js", 960 | ]); 961 | return {}; 962 | }; 963 | 964 | var require = install({ 965 | "main.js": function (require, exports, module) { 966 | exports.promise = Promise.all([ 967 | module.prefetch("./a"), 968 | module.prefetch("./b") 969 | ]); 970 | }, 971 | "a.js": [], 972 | "b.js": [] 973 | }); 974 | 975 | return require("./main").promise.then(function (ab) { 976 | assert.strictEqual(fetchCallCount, 1); 977 | assert.deepEqual(ab.sort(), [ 978 | "/a.js", 979 | "/b.js", 980 | ]); 981 | }); 982 | }); 983 | 984 | it("supports retrying dynamic imports after failure", function () { 985 | var install = makeInstaller(); 986 | 987 | var threw = false; 988 | install.fetch = function (ids) { 989 | if (! threw) { 990 | threw = true; 991 | debugger; 992 | throw new Error("network failure, or something"); 993 | } 994 | 995 | var tree = {}; 996 | 997 | Object.keys(ids).forEach(function (id) { 998 | var info = ids[id]; 999 | assert.strictEqual(info.module.id, id); 1000 | addToTree(tree, id, function (r, exports, module) { 1001 | assert.strictEqual(module, info.module); 1002 | exports.name = module.id; 1003 | }); 1004 | }); 1005 | 1006 | return tree; 1007 | }; 1008 | 1009 | var require = install({ 1010 | "main.js": function (require, exports, module) { 1011 | exports.attempt = function (id) { 1012 | return module.prefetch(id); 1013 | }; 1014 | }, 1015 | "a.js": ["./c", "./b"], 1016 | "b.js": ["./a", "./c"], 1017 | "c.js": ["./a", "./b"] 1018 | }); 1019 | 1020 | var attempt = require("./main").attempt; 1021 | 1022 | return attempt("./a").then(function () { 1023 | throw new Error("should have failed"); 1024 | }, function (error) { 1025 | assert.strictEqual(threw, true); 1026 | assert.strictEqual( 1027 | error.message, 1028 | "network failure, or something" 1029 | ); 1030 | 1031 | return attempt("./c").then(function (id) { 1032 | assert.strictEqual(id, "/c.js"); 1033 | assert.strictEqual(require("./c").name, id); 1034 | }); 1035 | }); 1036 | }); 1037 | 1038 | it("respects module.exports before file.contents", function () { 1039 | var install = makeInstaller(); 1040 | 1041 | install.fetch = function (ids) { 1042 | var keys = Object.keys(ids); 1043 | assert.deepEqual(keys, ["/b.js"]); 1044 | var info = ids[keys[0]]; 1045 | assert.deepEqual(info.stub, { stub: true }); 1046 | info.module.exports = { stub: false }; 1047 | // Returning nothing because we've manually populated module.exports 1048 | // for the b.js module. 1049 | }; 1050 | 1051 | var require = install({ 1052 | "a.js": function (require, exports, module) { 1053 | var stub = require("./b"); 1054 | assert.deepEqual(stub, { stub: true }); 1055 | exports.promise = module.prefetch("./b").then(function () { 1056 | var notStub = require("./b"); 1057 | assert.deepEqual(notStub, { stub: false }); 1058 | assert.notStrictEqual(stub, notStub); 1059 | }); 1060 | }, 1061 | "b.js": [{ 1062 | stub: true 1063 | }] 1064 | }); 1065 | 1066 | return require("./a").promise; 1067 | }); 1068 | 1069 | it('falls back to index.js when package.json "main" missing', function () { 1070 | var install = makeInstaller(); 1071 | var require = install({ 1072 | "main.js"(require, exports, module) { 1073 | exports.result = require("pkg"); 1074 | }, 1075 | 1076 | node_modules: { 1077 | pkg: { 1078 | "package.json"(require, exports, module) { 1079 | // Since this file is missing, the root index.js should be used. 1080 | exports.main = "dist/index.js"; 1081 | }, 1082 | 1083 | "index.js"(require, exports, module) { 1084 | exports.isRoot = true; 1085 | exports.id = module.id; 1086 | exports.oyez = require("./dist/oyez.js"); 1087 | }, 1088 | 1089 | dist: { 1090 | "oyez.js"(require, exports, module) { 1091 | exports.id = module.id; 1092 | } 1093 | } 1094 | } 1095 | } 1096 | }); 1097 | 1098 | var result = require("./main").result; 1099 | assert.strictEqual(result.isRoot, true); 1100 | assert.strictEqual(result.id, "/node_modules/pkg/index.js"); 1101 | assert.strictEqual(result.oyez.id, "/node_modules/pkg/dist/oyez.js"); 1102 | }); 1103 | 1104 | it("tolerates index.* modules with alternate extensions", function () { 1105 | var extensions = [".js", ".json"]; 1106 | var require = makeInstaller({ 1107 | extensions, 1108 | })({ 1109 | "main.js"(require, exports, module) { 1110 | exports.json = require("./jsonDir"); 1111 | }, 1112 | 1113 | jsonDir: { 1114 | "index.json"(require, exports, module) { 1115 | exports.name = module.id; 1116 | } 1117 | }, 1118 | 1119 | tsxDir: { 1120 | "index.tsx"(require, exports, module) { 1121 | exports.name = module.id; 1122 | } 1123 | } 1124 | }); 1125 | 1126 | var main = require("./main"); 1127 | assert.strictEqual(main.json.name, "/jsonDir/index.json"); 1128 | 1129 | var threw = false; 1130 | try { 1131 | require("/tsxDir"); 1132 | } catch (e) { 1133 | threw = true; 1134 | assert.strictEqual(e.message, "Cannot find module '/tsxDir'"); 1135 | } 1136 | assert.strictEqual(threw, true); 1137 | 1138 | extensions.push(".tsx"); 1139 | assert.strictEqual( 1140 | require("/tsxDir").name, 1141 | "/tsxDir/index.tsx" 1142 | ); 1143 | }); 1144 | 1145 | it('falls back to "main" if "module" cannot be resolved', function () { 1146 | const require = makeInstaller({ 1147 | mainFields: ["module", "main"] 1148 | })({ 1149 | "main.js"(require, exports, module) { 1150 | assert.strictEqual( 1151 | require("broken-package").id, 1152 | "/node_modules/broken-package/working.js" 1153 | ); 1154 | }, 1155 | 1156 | node_modules: { 1157 | "broken-package": { 1158 | "package.json"(require, exports, module) { 1159 | exports.name = "broken-package"; 1160 | exports.main = "./working"; 1161 | exports.module = "./broken"; 1162 | }, 1163 | 1164 | "working.js"(require, exports, module) { 1165 | exports.id = module.id; 1166 | } 1167 | } 1168 | } 1169 | }); 1170 | 1171 | require("./main"); 1172 | }); 1173 | }); 1174 | --------------------------------------------------------------------------------