├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .testignore ├── CHANGELOG.md ├── CHANGES ├── LICENSE ├── README.md ├── bin └── webmake.js ├── index.js ├── lib ├── browser │ └── load-css.js ├── find-package-root.js ├── parser.js └── webmake.tpl ├── package.json └── test ├── .eslintrc.json ├── __playground ├── enforce-strict.js ├── includes │ ├── markup.html │ └── style.css ├── lib │ ├── browser-test.js │ ├── browser │ │ ├── body.html │ │ └── test.css │ ├── circular-other-foo.js │ ├── circular-other.js │ ├── dir.js │ │ └── index.js │ ├── dynamic.js │ ├── included │ │ ├── a.js │ │ └── b.js │ ├── index.js │ ├── indexed │ │ └── index.js │ ├── mario.json │ ├── nl-comment.js │ ├── other-type-includes.js │ ├── path.js │ ├── path │ │ ├── index.js │ │ └── other.js │ ├── program.js │ ├── sub-longer │ │ ├── bar.js │ │ ├── inner │ │ │ └── other.js │ │ └── other.js │ ├── sub │ │ ├── foo.js │ │ └── inner │ │ │ ├── inner.js │ │ │ └── other.js │ ├── x.js │ ├── y.js │ └── z.js ├── node_modules │ ├── @scope │ │ └── package │ │ │ └── index.js │ ├── no-main │ │ └── lib │ │ │ └── some-module.js │ ├── path │ │ └── index.js │ ├── regular │ │ └── index.js │ └── test │ │ ├── lib │ │ ├── chosen-one.js │ │ ├── module.js │ │ └── other.js │ │ └── package.json ├── not-taken.js ├── other │ └── sub │ │ └── index.js ├── outer.js ├── require-native.js ├── sub-longer │ ├── bar.js │ ├── inner │ │ └── other.js │ └── other.js └── sub │ ├── foo.js │ └── inner │ ├── inner.js │ └── other.js ├── index.js └── lib ├── browser ├── __tad.js └── load-css.js └── parser.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = tab 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | indent_size = 2 15 | indent_style = space 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: medikoo 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | /package-lock.json 4 | -------------------------------------------------------------------------------- /.testignore: -------------------------------------------------------------------------------- 1 | /lib/find-package-root.js 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.1.2](https://github.com/medikoo/modules-webmake/compare/v1.1.1...v1.1.2) (2020-04-01) 6 | 7 | ### Bug Fixes 8 | 9 | - Fix scoped packages handling when bundling on Windows ([fc7fc8b](https://github.com/medikoo/modules-webmake/commit/fc7fc8b1d97cd1522f016dee84751f7831d307f5)) ([Mariusz Nowak](https://github.com/medikoo)) 10 | 11 | ### [1.1.1](https://github.com/medikoo/modules-webmake/compare/v1.1.0...v1.1.1) (2020-03-31) 12 | 13 | ### Bug Fixes 14 | 15 | - Fix support for scoped dependencies ([543a3a5](https://github.com/medikoo/modules-webmake/commit/543a3a5b93a26db4f56c6bfb0b9762ecb19db949)) ([Mariusz Nowak](https://github.com/medikoo)) 16 | 17 | ## [1.1.0](https://github.com/medikoo/modules-webmake/compare/v1.0.0...v1.1.0) (2019-11-29) 18 | 19 | ### Features 20 | 21 | - `cjs` option ([4fe7fbc](https://github.com/medikoo/modules-webmake/commit/4fe7fbcb8cdc1fb27eb96e4296b2a1b65faec89f)) 22 | 23 | ## [1.0.0](https://github.com/medikoo/modules-webmake/compare/v0.4.0...v1.0.0) (2019-11-29) 24 | 25 | ### ⚠ BREAKING CHANGES 26 | 27 | - Binary file was renamed from bin/webmake to bin/webmake.js 28 | (but it doesn't affect `webmake` command) 29 | 30 | ### Features 31 | 32 | - Expose parent env require on `require.fromParentEnvironment` ([fcfe2c1](https://github.com/medikoo/modules-webmake/commit/fcfe2c191d1abf5b8d97bdbe0571dc2202bb0ba7)) 33 | - Reconfigure logging with 'log' utility ([a53f9a9](https://github.com/medikoo/modules-webmake/commit/a53f9a9edc9c3e8af4ab13c09d219ff6f8cfd3ef)) 34 | 35 | * Add '.js' extension to binary ([0dcdfe5](https://github.com/medikoo/modules-webmake/commit/0dcdfe584d1d099d1b06041b8c3348dba76a3f11)) 36 | 37 | 38 | 39 | # [0.4.0](https://github.com/medikoo/modules-webmake/compare/v0.3.43...v0.4.0) (2018-08-08) 40 | 41 | ### Bug Fixes 42 | 43 | - override externals resolution in Node.js v10 ([4e59857](https://github.com/medikoo/modules-webmake/commit/4e59857)) 44 | 45 | ### Chores 46 | 47 | - use caret to describe version ranges ([64e6206](https://github.com/medikoo/modules-webmake/commit/64e6206)) 48 | 49 | ### BREAKING CHANGES 50 | 51 | - Drop support for Node.js v4 and below 52 | 53 | ## Old changelog 54 | 55 | See `CHANGES` file 56 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | -- For new changelog see CHANGELOG.md 2 | 3 | v0.3.43 -- 2017.03.20 4 | * Add 'ignore' option, to eventually ignore some modules 5 | (at this point supported just programmatically) 6 | 7 | v0.3.42 -- 2017.03.15 8 | * Update dependencies 9 | 10 | v0.3.41 -- 2016.11.03 11 | * Fix Windows issue related to internal path slash detection 12 | * Update test dependencies 13 | 14 | v0.3.40 -- 2015.08.13 15 | * Fix code resolution issue when source maps are generated, and we deal with empty file 16 | 17 | v0.3.39 -- 2015.03.20 18 | * Fix error handling in shell script 19 | * Improve documentation 20 | 21 | v0.3.38 -- 2015.02.17 22 | * Support source maps in direct transforms. Fix #51 23 | * Improve documentation 24 | 25 | v0.3.37 -- 2015.02.10 26 | * Fix programmatical support for 'name' and 'amd' options. 27 | * Improve documentation 28 | * Fix spelling of License 29 | 30 | v0.3.36 -- 2014.10.15 31 | * Implement `transform` option, that allows to run transforms for each module. Fixes #47 32 | * Configure lint scripts 33 | 34 | v0.3.35 -- 2014.07.06 35 | * Fix input path resolution in case of non package path. Fixes #46 36 | 37 | v0.3.34 -- 2014.06.27 38 | * Improve input streams support. Fixes #45 39 | 40 | v0.3.33 -- 2014.05.27 41 | * Comment out eventual shebang. We should support modules that are made executable for *nix 42 | environments 43 | 44 | v0.3.32 -- 2014.05.07 45 | * Support all types and extensions in includes option (Fixes #42) 46 | 47 | v0.3.31 -- 2014.05.02 48 | * Support input from stdin (Fixes #41) 49 | 50 | v0.3.30 -- 2014.04.28 51 | * Move out main module from lib folder 52 | * Update to use latest versions of dependencies 53 | * Remove Makefile (it's environment agnostic project) 54 | 55 | v0.3.29 -- 2014.04.04 56 | * Fix handling of not found module of outer package 57 | 58 | v0.3.28 -- 2014.03.27 59 | * Support optional requires. 60 | Optional require must be wrapped in try/catch clause and on that basis it's 61 | distinguished. 62 | 63 | v0.3.27 -- 2014.03.06 64 | * Fallback to environment provided `require` (if it exists), in case of import 65 | of not bundled package. This opens the door to execute bundles in Node.js 66 | environment (#36). 67 | 68 | v0.3.26 -- 2014.01.29 69 | * Provide `module.id`. It appears it's used within some scripts. (#35) 70 | 71 | v0.3.25 -- 2013.12.04 72 | * `useStrict` option to enfore strict mode 73 | * Improve documentation 74 | * Run client-side resolver in strict mode if supported 75 | 76 | v0.3.24 -- 2013.11.29 77 | * Silence native modules require errors with `ignoreErrors` option (#32) 78 | 79 | v0.3.23 -- 2013.11.06 80 | * Support source maps provided separately (not with the code) by extensions 81 | * Document option output argument 82 | 83 | v0.3.22 -- 2013.11.06 84 | * Fix sourceURL mapping when combined with extensions. Fix #30 85 | Great thanks to Simon Williams (@SystemParadox) for reporting the issue and 86 | help with sorting it out 87 | 88 | v0.3.21 -- 2013.10.28 89 | * Make `` optional in binary script. If `` not provided, output 90 | generated bundle to stdout. Fix #29 91 | * Improve syntax error handling 92 | * Improve documentation 93 | 94 | v0.3.20 -- 2013.08.21 95 | * (Critical) Fix partial path resolution issue 96 | * Update context configuration for loadCSS test 97 | * Update dependencies to latest versions 98 | * Improve documentation 99 | 100 | v0.3.19 -- 2013.06.21 101 | * CSS as native type 102 | * HTML as native type 103 | * Support extensions passed programmatically 104 | * Significantly improved extensions handling and files resolution order 105 | * Better documentation 106 | * Fix double file read issue (minor, affected only input modules) 107 | * Update sourceURL syntax (replace '@' with '#') 108 | * Improve error message 109 | 110 | v0.3.18 -- 2013.06.05 111 | * Allow asynchronous compilation of extensions 112 | * `prettyOutput` option through which we can prevent corrected indentation of 113 | output. When bound to process that's a memory hog, indentation can 114 | noticeably slow down generation of a bundle. 115 | * Provide information about Webassmble tool in documentation 116 | 117 | v0.3.17 -- 2013.05.20 118 | * Provide `ignoreErrors` options, so requiring packages that pass dynamicaly 119 | generated paths to require calls is possible. 120 | * Simplify internal logic with help of memoizee package 121 | 122 | v0.3.16 -- 2013.05.15 123 | Nearly *major* update 124 | * JSON files support 125 | * Support for custom formats via extensions (in parallel to this update 126 | extensions for YAML and CoffeeScript are published). 127 | * Improved relative module resolution so it's on pair with Node.js design 128 | (there were corner case where resolution result differed). 129 | * Introduce optional caching mechanism for repeated bundle generation. 130 | If request file has not been modified since last read its content and 131 | calculated dependencies list are taken from a cache. When used, speeds up 132 | Webmake around x2, and even more if used with extensions. 133 | * Improve error messages on unsupported require calls 134 | 135 | v0.3.15 -- 2013.04.08 136 | Minor improvements: 137 | * Meaningful error when run with unsupported Node engine 138 | * Better error interface for Module not Found error on client-side 139 | * Expose compilation time on returned promise object 140 | * Improve native module require error message (so it points path to source file) 141 | 142 | v0.3.14 -- 2012.12.08 143 | * Improve path resolution (omit findRoot call in straightforward cases) 144 | * Fix support for partially broken circular dependencies 145 | * Improve client-side resolution mechanism 146 | 147 | v0.3.13 -- 2012.11.09 148 | * Expose parts of internal API for better configurability 149 | * Demand Node v0.8 at least as we depend on fs2 (for v0.6 support use v0.3.11) 150 | 151 | v0.3.12 -- 2012.10.05 152 | * Add 'name' and 'amd' options. Bundle can now be directly exposed 153 | in global (or AMD) namespace 154 | * Update dependencies to its latest versions 155 | * Improve documentation 156 | 157 | v0.3.11 -- 2012.07.20 158 | * Support browser polyfills for Node.js modules. Such modules should be placed 159 | in node_modules folder to be picked up by Webmake 160 | * Fix small resolution bug of "." path, it caused bundling same module twice 161 | 162 | v0.3.10 -- 2012.07.20 163 | * Fix resolution of main path when working outside of package scope 164 | 165 | v0.3.9 -- 2012.07.02 166 | * Fix bug related to external package resolution (affected only Node v0.8) 167 | 168 | v0.3.8 -- 2012.06.13 169 | * Webmake function is now hybrid it accepts a callback and returns promise 170 | * Great documentation improvements from Jaap Roes 171 | * Update up to v0.8 branch of es5-ext and v0.5 branch of deferred 172 | * package.json now in npm friendly format 173 | * It's safe to use optimist at v0.3 174 | 175 | v0.3.7 -- 2012.03.22 176 | Fixes: 177 | * Input paths resolution now works properly in all cases (on both *nix and 178 | windows) 179 | * Require paths resolution logic is now aware of current system separator type, 180 | this makes webmake 100% ready for both systems (*nix and windows).. 181 | * Handling of current and parent directory index requires (e.g. './' or '../') 182 | * Handling of paths with trailing slash (e.g. './path/') where both file 183 | and directory is present (e.g. ./path.js and ./path/) 184 | 185 | v0.3.6 -- 2012.02.23 186 | * Replace rudimentary require calls parser with solid and fast solution - 187 | find-requires module 188 | * Add support for sourcemaps 189 | 190 | v0.3.5 -- 2012.02.12 191 | * Fixed resolution of paths ended with slash (client side logic didn't handle 192 | that properly) 193 | * Cleanup and from now on maintain code with JSLint 194 | 195 | v0.3.4 -- 2012.01.22 196 | * Now programmatically webmake can write output file (before it was just 197 | returning content) 198 | * Byte order mark awareness 199 | * Pack properly modules without EOL at EOF 200 | * Better 'require' calls parser (but still primitive) 201 | * Allow more input files via options 202 | * More efficient parser 203 | * Generate modules tree always in alphabetical order (diff friendly) 204 | * Better error messages 205 | * Support for packing programs that are not in package context 206 | * Take out parser as separate module, thanks to that, more can be done 207 | programmatically 208 | * Use '.js' in paths, without that e.g. dir 'foo' collided with 'foo.js' module 209 | * Webmake template now safe for old JS implementations that doesn't support 210 | indexes on strings 211 | * Windows support 212 | * Travis CI Configuration 213 | 214 | v0.3.3 -- 2011.08.12 215 | Meaningful error when requiring one of node's specific modules 216 | 217 | v0.3.2 -- 2011.08.12 218 | Fixes, following now works: 219 | * Require paths ending with js file extension 220 | * Requiring modules from packages that not have main module 221 | 222 | Maintenance: 223 | * Update up to es5-ext@0.6.2 224 | 225 | v0.3.1 -- 2011.08.11 226 | * Fixed links in package.json (rename aftermath) 227 | 228 | v0.3.0 -- 2011.08.11 229 | * Rewritten whole logic with help of promises 230 | * Packages support (!) 231 | * webmake function now takes only input path parameter and returns source 232 | code as result. Shell binary still takes input and output paths as arguments 233 | * Binary is now visible in PATH (when installed globally via npm) 234 | * Better documentation 235 | * Makefile 236 | * Tests configured for TAD suite 237 | 238 | v0.2.2 -- 2011.07.20 239 | * Renamed to webmake 240 | * Support for modules located in upper paths than program, it didn't work (!) 241 | * Shell (/bin) script 242 | 243 | v0.2.1 -- 2011.05.11 244 | * Fixed assignment error 245 | * Documentation improvements 246 | 247 | v0.2.0 -- 2011.05.05 248 | Use widely accepted convention for error handling. Callback passed to webbuild() 249 | will now be called with error (if any) as first argument, and result as second. 250 | 251 | v0.1.1 -- 2011.05.04 252 | Meta data: more details in package.json 253 | 254 | v0.1.0 -- 2011.05.04 255 | Initial version 256 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2011-2020, Mariusz Nowak, @medikoo, medikoo.com 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![*nix build status][nix-build-image]][nix-build-url] 2 | [![Windows build status][win-build-image]][win-build-url] 3 | ![Transpilation status][transpilation-image] 4 | [![npm version][npm-image]][npm-url] 5 | 6 | # modules-webmake 7 | 8 | _Bundle CommonJS/Node.js modules for web browsers._ 9 | 10 | **Webmake allows you to organize JavaScript code for the browser the same way as you do for Node.js.** 11 | 12 | - Work with best dependency management system that JavaScript currently has. 13 | - Easily, without boilerplate, reuse your modules in any environment that runs JavaScript. 14 | No matter if it's a server, client (any web browser) any other custom environment as e.g. Adobe Photoshop, or let's say your dishwasher if it speaks JavaScript. 15 | - Require **CSS** and **HTML** files same way. Webmake allows you to require them too, which makes it a full stack modules bundler for a web browser. 16 | 17 | 18 | 19 | Files support can be extended to any other format that compiles to one of _.js_, _.json_, _.css_ or _.html_. See **[custom extensions](#extensions)** for more information. 20 | 21 | For a more in depth look into JavaScript modules and the reason for _Webmake_, 22 | see the slides from my presentation at Warsaw's MeetJS: [**JavaScript Modules Done Right**][slides] 23 | 24 | **_If you wonder how Webmake compares with other solutions, see [comparison section](#comparison-with-other-solutions)_** 25 | 26 | ## How does dependency resolution work? 27 | 28 | As it has been stated, Webmake completely follows Node.js in that 29 | 30 | Let's say in package named _foo_ you have following individual module files: 31 | 32 | _add.js_ 33 | 34 | ```javascript 35 | module.exports = function () { 36 | var sum = 0, i = 0, args = arguments, l = args.length; 37 | while (i < l) sum += args[i++]; 38 | return sum; 39 | }; 40 | ``` 41 | 42 | _increment.js_ 43 | 44 | ```javascript 45 | var add = require("./add"); 46 | module.exports = function (val) { return add(val, 1); }; 47 | ``` 48 | 49 | _program.js_ 50 | 51 | ```javascript 52 | var inc = require("./increment"); 53 | var a = 1; 54 | inc(a); // 2 55 | ``` 56 | 57 | Let's pack _program.js_ with all it's dependencies so it will work in browsers: 58 | 59 | $ webmake program.js bundle.js 60 | 61 | The generated file _bundle.js_ now contains the following: 62 | 63 | ```javascript 64 | (function (modules) { 65 | // about 60 lines of import/export path resolution logic 66 | })({ 67 | foo: { 68 | "add.js": function (exports, module, require) { 69 | module.exports = function () { 70 | var sum = 0, i = 0, args = arguments, l = args.length; 71 | while (i < l) sum += args[i++]; 72 | return sum; 73 | }; 74 | }, 75 | "increment.js": function (exports, module, require) { 76 | var add = require("./add"); 77 | module.exports = function (val) { return add(val, 1); }; 78 | }, 79 | "program.js": function (exports, module, require) { 80 | var inc = require("./increment"); 81 | var a = 1; 82 | inc(a); // 2 83 | } 84 | } 85 | })("foo/program"); 86 | ``` 87 | 88 | When loaded in browser, _program.js_ module is executed immediately. 89 | 90 | ### Working with HTML and CSS 91 | 92 | Technically you can construct whole website that way: 93 | 94 | _body.html_ 95 | 96 | ```html 97 |

Hello from NodeJS module

98 |

See Webmake for more details

99 | ``` 100 | 101 | _style.css_ 102 | 103 | ```css 104 | body { 105 | font-family: Arial, Helvetica, sans-serif; 106 | } 107 | h1, 108 | p { 109 | margin: 20px; 110 | } 111 | p.footer { 112 | font-size: 14px; 113 | } 114 | ``` 115 | 116 | _program.js_ 117 | 118 | ```javascript 119 | document.title = "Hello from NodeJS module"; 120 | 121 | require("./style"); 122 | 123 | document.body.innerHTML = require("./body"); 124 | 125 | var footer = document.body.appendChild(document.createElement("p")); 126 | footer.className = "footer"; 127 | footer.innerHTML = "Generated by Webmake!"; 128 | ``` 129 | 130 | Bundle it 131 | 132 | $ webmake program.js bundle.js 133 | 134 | See it working, by including it within document as such: 135 | 136 | ```html 137 | 138 | 139 | 140 | 141 | 142 | ``` 143 | 144 | ## Installation 145 | 146 | $ npm install -g webmake 147 | 148 | ## Usage 149 | 150 | ### From the shell: 151 | 152 | $ webmake [options] [] 153 | 154 | **input** - Path to the initial module that should be executed when script is loaded. 155 | **output** - (optional) Filename at which browser ready bundle should be saved. If not provided generated bundle is streamed to _stdout_. 156 | 157 | #### Options 158 | 159 | ##### name `string` 160 | 161 | Name at which program should be exposed in your namespace. Technically just assigns exported module to global namespace. 162 | 163 | ##### amd `string` 164 | 165 | Expose bundle as AMD module. If used together with _[name](#name-string)_ option, module will be defined with provided name. 166 | 167 | ##### cjs `string` 168 | 169 | Expose bundle as CJS module. 170 | 171 | ##### include `string` 172 | 173 | Additional module(s) that should be included but due specific reasons are 174 | not picked by parser (can be set multiple times) 175 | 176 | ##### ext `string` 177 | 178 | Additional extensions(s) that should be used for modules resolution from custom formats e.g. _coffee-script_ or _yaml_. 179 | See [extensions](#extensions) section for more info. 180 | 181 | ##### sourceMap `boolean` (command line: _sourcemap_) 182 | 183 | Include [source maps][], for easier debugging. Source maps work very well in WebKit and Chrome's web inspector. Firefox's Firebug however has some [issues][firebug issue]. 184 | 185 | ##### ignoreErrors `boolean` (command line: _ignore-errors_) 186 | 187 | Ignore not parsable require paths (e.g. `require('./lang/' + lang)`) or not polyfilled native modules requires (e.g. `require('fs')`) if any. 188 | Dynamic paths in require calls are considered a bad practice and won't be possible with upcoming _ES6 modules_ standard. Still if we deal with modules that do that, we can workaround it by turning this option on, and including missing modules with [`include`](https://github.com/medikoo/modules-webmake/edit/master/README.md#include-string) option. 189 | 190 | ##### useStrict `boolean` (command line: _use-strict_) 191 | 192 | Enforce strict mode globally. Mind that by default in node.js environment CJS modules are not executed in strict mode. Relying on that feature may rise incompatibility issues in corner case scenarios. 193 | 194 | ##### cache `boolean` _programmatical usage only_ 195 | 196 | Cache files content and its calculated dependencies. On repeated request only modified files are re-read and parsed. 197 | Speeds up re-generation of Webmake bundle, useful when Webmake is bound to server process, [see below example](#development-with-webmake). 198 | Highly recommended if [extensions](#extensions) are used. 199 | Defaults to _false_. 200 | 201 | ##### transform `function` _programmatical usage only_ 202 | 203 | Provide a transform middleware. `transform` callback would be called on each module, with arguments: _absolute filename_ and _file code_ (as it is in origin file, before any internal transformations). If source module is meant to be processed by one of the extensions, you'll receive origin code before extension logic is applied, and you must return code that's valid for extension processor. So e.g. if you transform [LESS](https://github.com/acdaniel/webmake-less#webmake-less) code, you need to return valid _LESS_ code 204 | 205 | If you're interested only in applying transform to e.g. _js_ files, be sure to filter your actions on basis of filename, and return code as you received if non transforms should be applied 206 | 207 | In case of asynchronous operations, promise maybe returned, but it has to be promise that origins from [deferred](https://github.com/medikoo/deferred#deferred) package. 208 | 209 | ```javascript 210 | webmake(programPath, { transform: function (filename, code) { 211 | return transformCode(code) 212 | } } 213 | ``` 214 | 215 | `transformCode` function should return either plain transformed _code_ string, or an object, with `code` and `sourceMap` properties, if we want to accompany our transform with a _sourceMap_. 216 | 217 | ### Programmatically: 218 | 219 | ```javascript 220 | webmake(programPath[, options][, callback]); 221 | ``` 222 | 223 | `webmake` by default returns generated source to callback, but if _output_ path is provided as one of the options, then source will be automatically saved to file 224 | 225 | ### Development with Webmake 226 | 227 | Currently best way is to use Webmake programmatically and setup a static-file server to generate bundle on each request. Webmake is fast, so it's acceptable approach even you bundle hundreds of modules at once. 228 | 229 | You can setup simple static server as it's shown in following example script. 230 | _Example also uses [node-static][] module to serve other static files (CSS, images etc.) if you don't need it, just adjust code up to your needs._ 231 | 232 | ```javascript 233 | // Dependencies: 234 | var createServer = require("http").createServer; 235 | var staticServer = require("node-static").Server; 236 | var webmake = require("webmake"); 237 | 238 | // Settings: 239 | // Project path: 240 | var projectPath = "/Users/open-web-user/Projects/Awesome"; 241 | // Public folder path (statics) 242 | var staticsPath = projectPath + "/public"; 243 | // Path to js program file 244 | var programPath = projectPath + "/lib/public/main.js"; 245 | // Server port: 246 | var port = 8000; 247 | // Url at which we want to serve generated js file 248 | var programUrl = "/j/main.js"; 249 | 250 | // Setup statics server 251 | staticServer = new staticServer(staticsPath); 252 | 253 | // Initialize http server 254 | createServer(function (req, res) { 255 | // Start the flow (new Stream API demands that) 256 | req.resume(); 257 | // Respond to request 258 | req.on("end", function () { 259 | if (req.url === programUrl) { 260 | // Generate bundle with Webmake 261 | 262 | // Send headers 263 | res.writeHead(200, { 264 | "Content-Type": "application/javascript; charset=utf-8", 265 | // Do not cache generated bundle 266 | "Cache-Control": "no-cache" 267 | }); 268 | 269 | var time = Date.now(); 270 | webmake(programPath, { sourceMap: true, cache: true }, function (err, content) { 271 | if (err) { 272 | console.error("Webmake error: " + err.message); 273 | // Expose eventual error brutally in browser 274 | res.end( 275 | "document.write('
Could not generate " + 280 | programUrl + 281 | "
" + 282 | err.message.replace(/'/g, "\\'") + 283 | "
');" 284 | ); 285 | return; 286 | } 287 | 288 | // Send script 289 | console.log("Webmake OK (" + ((Date.now() - time) / 1000).toFixed(3) + "s)"); 290 | res.end(content); 291 | }); 292 | } else { 293 | // Serve static file 294 | staticServer.serve(req, res); 295 | } 296 | }); 297 | }).listen(port); 298 | console.log("Server started"); 299 | ``` 300 | 301 | ### Using Webmake with Express or Connect 302 | 303 | See [webmake-middleware](https://github.com/gillesruppert/webmake-middleware) prepared by [Gilles Ruppert](http://latower.com/). 304 | 305 | ### Using Webmake with Grunt 306 | 307 | See [grunt-webmake](https://github.com/sakatam/grunt-webmake) prepared by [Sakata Makoto](https://github.com/sakatam). 308 | 309 | ### Working with other module systems 310 | 311 | When you work with old school scripts or framework that uses different modules system, then you'd rather just bundle needed utilities (not whole application) and expose them to global scope. 312 | 313 | #### Webassemble -> https://github.com/kenspirit/webassemble 314 | 315 | Webassemble written by [Ken Chen](https://github.com/kenspirit) provides a convinient way to expose different packages, written CJS style, to outer scripts. It automatically creates one entry package that does the job and is used as a starting point for a Webmake bundle. 316 | 317 | ### Extensions 318 | 319 | #### Extensions published on NPM 320 | 321 | ##### JS 322 | 323 | - **CoffeeScript - [webmake-coffee](https://github.com/medikoo/webmake-coffee)** 324 | - **handlebars - [webmake-handlebars](https://github.com/acdaniel/webmake-handlebars)** 325 | - **ejs - [webmake-ejs](https://github.com/tswaters/webmake-ejs)** 326 | 327 | ##### JSON 328 | 329 | - **YAML - [webmake-yaml](https://github.com/medikoo/webmake-yaml)** 330 | 331 | ##### CSS 332 | 333 | - **LESS - [webmake-less](https://github.com/acdaniel/webmake-less)** 334 | - **SASS - [webmake-sass](https://github.com/acdaniel/webmake-sass)** 335 | 336 | **Submit any missing extension via [new issue form](https://github.com/medikoo/modules-webmake/issues/new)**. 337 | 338 | #### Using extensions with Webmake 339 | 340 | Install chosen extension: 341 | 342 | _EXT should be replaced by name of available extension of your choice_. 343 | 344 | $ npm install webmake-EXT 345 | 346 | If you use global installation of Webmake, then extension also needs to be installed globally: 347 | 348 | $ npm install -g webmake-EXT 349 | 350 | When extension is installed, you need to ask Webmake to use it: 351 | 352 | $ webmake --ext=EXT program.js bundle.js 353 | 354 | Same way if used programmatically: 355 | 356 | ```javascript 357 | webmake(inputPath, { ext: "EXT" }, cb); 358 | ``` 359 | 360 | Multiple extensions can be used together: 361 | 362 | $ webmake --ext=EXT --ext=EXT2 program.js bundle.js 363 | 364 | Programmatically: 365 | 366 | ```javascript 367 | webmake(inputPath, { ext: ["EXT", "EXT2"] }, cb); 368 | ``` 369 | 370 | #### Writing an extension for a new format 371 | 372 | Prepare a `webmake-*` NPM package _(replace '\*' with name of your extension)_, where main module is configured as in following example: 373 | 374 | ```javascript 375 | // Define a file extension of a new format, can be an array e.g. ['sass', 'scss'] 376 | exports.extension = "coffee"; 377 | 378 | // Which type is addressed by extension (can be either 'js', 'json', 'css' or 'html') 379 | exports.type = "js"; 380 | 381 | // Define a compile function, that for given source code, produces valid body of a JavaScript module: 382 | exports.compile = function (source, options) { 383 | // Return plain object, with compiled body assigned to `code` property. 384 | return { code: compile(source) }; 385 | 386 | // If compilation for some reason is asynchronous then assign promise 387 | // (as produced by deferred library) which resolves with expected code body 388 | return { code: compileAsync(source) }; 389 | 390 | // If custom format provides a way to calculate a source map and `sourceMap` options is on 391 | // it's nice to generate it: 392 | var data, map, code; 393 | if (options.sourceMap) { 394 | data = compile(source, { sourceMap: true }); 395 | 396 | // Include original file in the map. 397 | map = JSON.parse(data.sourceMap); 398 | map.file = options.generatedFilename; 399 | map.sources = [options.localFilename]; 400 | map.sourcesContent = [source]; 401 | map = JSON.stringify(map); 402 | 403 | return { code: code, sourceMap: map }; 404 | } 405 | }; 406 | ``` 407 | 408 | #### Providing extensions programmatically 409 | 410 | Extension doesn't need to be installed as package, you may pass it programmatically: 411 | 412 | ```javascript 413 | webmake( 414 | inputPath, 415 | { 416 | ext: { 417 | name: "coffee-script", 418 | extension: "coffee", 419 | type: "js", 420 | compile: function (source, options) { 421 | /* ... */ 422 | } 423 | } 424 | }, 425 | cb 426 | ); 427 | ``` 428 | 429 | See below [writing extensions](#writing-an-extension-for-a-new-format) section to see how to configure fully working extensions 430 | 431 | #### Writing extesions for either JSON, CSS or HTML 432 | 433 | Be sure to state the right type, and return string that reflects addressed format (not JavaScript code) 434 | e.g. extension for CSS: 435 | 436 | ```javascript 437 | exports.extension = "less"; 438 | exports.type = "css"; 439 | exports.compile = function (source, options) { 440 | return { code: compileToCSS(source) }; // `compileToCSS` returns plain CSS string 441 | }; 442 | ``` 443 | 444 | Publish it and refer to [Using extensions](#Using-extensions-with-webmake) section for usage instructions. 445 | Finally if everything works, please let me know, so I can update this document with link to your extension. 446 | 447 | ## Comparison with other solutions 448 | 449 | ### AMD 450 | 451 | AMD is different format, and although most popular loader for AMD is named [RequireJS](http://requirejs.org/) it works very differently from _require_ as introduced earlier with CommonJS (one that Webmake handles). 452 | 453 | Main idea behind AMD is that dependencies are resolved asynchronously (in contrary to synchronous resolution in case of CommonJS format). Sounds promising, but does it really make things better? Cause of waterfall nature of resolution and large number of HTTP requests not necessary. See [benchmark](https://github.com/medikoo/cjs-vs-amd-benchmark#compare-load-times-of-two-module-systems) that compares resolution speed of both formats when used in development mode. 454 | 455 | Agreed advantage of AMD that attributes to its success is that in it's direct form works in a browser (it doesn't require any server setup), that is hard to achieve with CJS style (but [not impossible](https://github.com/creationix/chrome-app-module-loader)). Still due to large number of requests such approach is usually not suitable for production and it appears it's also [not that performant in development mode](https://github.com/medikoo/cjs-vs-amd-benchmark#compare-load-times-of-two-module-systems). 456 | 457 | Quirks of AMD style is that it requires you to wrap all your modules with function wrappers, its modules are not runnable in direct form in Node.js and dependency resolution rules are basic and limited if you compare it with design of node.js + npm ecosystem. 458 | 459 | ### Browserify and other CJS bundlers 460 | 461 | [Browserify](http://browserify.org/) is most popular CJS bundler, and shares very similar idea. The subtle difference is that Browserify is about porting code as written for node.js to web browser, so apart of resolving dependencies and bundling the code it struggles to bring what is needed and possible from Node.js API to the browser. 462 | 463 | Webmake cares only about bringing node.js modules format to other environments. Conceptually it's addition to ECMAScript and not port of node.js to browser. It makes node.js modules format runnable in any environment that speaks at least ECMAScript 3. You can bundle with Webmake for Browser, TV, Adobe Photoshop or maybe a modern dishwasher. 464 | 465 | When comparing with other CJS bundlers, main difference would be that Webmake completely follows resolution logic as it works in node.js. It resolves both packages and modules exactly as node.js, and it doesn't introduce any different ways to do that. Thanks to that, you can be sure that your modules are runnable in it's direct form both on server and client-side. 466 | 467 | Other important difference is that Webmake doesn't do full AST scan to parse require's out of modules, it relies on [find-requires](https://github.com/medikoo/find-requires#find-requires--find-all-require-calls) module, which does only what's necessary to resolve dependencies list, and that makes it a noticeably faster solution. 468 | 469 | ### ES6 modules 470 | 471 | Soon to be released, native JavaScript modules spec shares the main concept with CommmonJS. Thanks to that eventual transition will be easy and can be fully automated. First [transpilers](http://square.github.io/es6-module-transpiler/) are already here. 472 | 473 | As soon as the standard will be finalized, implemented in first engines and possibly adapted by node.js Webmake will support it natively as well, then in a same way it will bundle it either for the sake of a bundle or for any ECMAScript 3+ environment that won't take it in natural way. 474 | 475 | ## Current limitations of Webmake 476 | 477 | The application calculates dependencies via static analysis of source code 478 | (with the help of the [find-requires][] module). So in some edge cases 479 | not all require calls can be found. You can workaround that with help 480 | of [`include` option](#include-stringarray) 481 | 482 | Only relative paths and outer packages paths are supported, following will work: 483 | 484 | ```javascript 485 | require("./module-in-same-folder"); 486 | require("./module/path/deeper"); 487 | require("./some/very/very/very/long" + "/module/path"); 488 | require("../../module-path-up"); // unless it doesn't go out of package scope 489 | require("other-package"); 490 | require("other-package/lib/some-module"); 491 | ``` 492 | 493 | But this won't: 494 | 495 | ```javascript 496 | require("/Users/foo/projects/awesome/my-module"); 497 | ``` 498 | 499 | Different versions of same package will collide: 500 | Let's say, package A uses version 0.2 of package C and package B uses version 0.3 of the same package. If both package A and B are required, package B will most likely end up buggy. This is because webmake will only bundle the version that was called first. So in this case package B will end up with version 0.2 instead of 0.3. 501 | 502 | ## Tests 503 | 504 | $ npm test 505 | 506 | ## Proud list of SPONSORS! 507 | 508 | #### [@puzrin](https://github.com/puzrin) (Vitaly Puzrin) member of [Nodeca](https://github.com/nodeca) 509 | 510 | Vitaly pushed forward development of support for _JSON_ files, [extensions functionality](#extensions), along with [webmake-yaml](https://github.com/medikoo/webmake-yaml) extension. Vitaly is a member of a team that is behind [js-yaml](https://github.com/nodeca/js-yaml) JavaScript YAML parser and dumper, and powerful social platform [Nodeca](http://dev.nodeca.com/). Big Thank You Vitaly! 511 | 512 | ## Contributors 513 | 514 | - [@Phoscur](https://github.com/Phoscur) (Justus Maier) 515 | - Help with source map feature 516 | - [@jaap3](https://github.com/jaap3) (Jaap Roes) 517 | - Documentation quality improvements 518 | 519 | [slides]: http://www.slideshare.net/medikoo/javascript-modules-done-right "JavaScript Modules Done Right on SlideShare" 520 | [source maps]: http://pmuellr.blogspot.com/2011/11/debugging-concatenated-javascript-files.html "Debugging concatenated JavaScript files" 521 | 522 | [firebug issue]: 523 | http://code.google.com/p/fbug/issues/detail?id=2198 524 | 'Issue 2198: @sourceURL doesn't work in eval() in some cases' 525 | 526 | [find-requires]: https://github.com/medikoo/find-requires "find-requires: Find all require() calls" 527 | [node-static]: https://github.com/cloudhead/node-static "HTTP static-file server module" 528 | [nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/modules-webmake/branches/master/shields_badge.svg 529 | [nix-build-url]: https://semaphoreci.com/medikoo-org/modules-webmake 530 | [win-build-image]: https://ci.appveyor.com/api/projects/status/16c6u5cafarb3yo0?svg=true 531 | [win-build-url]: https://ci.appveyor.com/project/medikoo/modules-webmake 532 | [cov-image]: https://img.shields.io/codecov/c/github/medikoo/modules-webmake.svg 533 | [cov-url]: https://codecov.io/gh/medikoo/modules-webmake 534 | [transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg 535 | [npm-image]: https://img.shields.io/npm/v/webmake.svg 536 | [npm-url]: https://www.npmjs.com/package/webmake 537 | -------------------------------------------------------------------------------- /bin/webmake.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | require("log-node")({ defaultNamespace: "webmake" }); 6 | 7 | const count = require("es5-ext/object/count") 8 | , { resolve } = require("path") 9 | , log = require("log").get("webmake") 10 | , webmake = require(".."); 11 | 12 | const { isArray } = Array, { now } = Date; 13 | 14 | const optimist = require("optimist").usage("Usage: $0 [options] [] []", { 15 | "name": { string: true, description: "Expose program in your namespace with given name" }, 16 | "amd": { boolean: true, description: "Expose bundle as AMD module" }, 17 | "cjs": { boolean: true, description: "Expose bundle as CJS module" }, 18 | "ext": { string: true, description: "Optional extensions" }, 19 | "ignore-errors": { boolean: true, description: "Ignore unparsable require calls" }, 20 | "use-strict": { boolean: true, description: "Enforce strict mode" }, 21 | "include": { 22 | string: true, 23 | description: 24 | "Additional module(s) that should be included (and are not picked by the parser)" 25 | }, 26 | "help": { boolean: true, desription: "Show this help" }, 27 | "sourcemap": { boolean: true, description: "Include source maps" } 28 | }); 29 | 30 | const { argv } = optimist, options = {}; 31 | 32 | let [input] = argv._; 33 | 34 | if (argv.help) { 35 | process.stdout.write(`${ optimist.help() }\n`); 36 | process.exit(0); 37 | } 38 | 39 | if (!input) { 40 | options.stream = process.stdin; 41 | process.stdin.resume(); 42 | input = resolve(process.cwd(), ":stream:.js"); 43 | } 44 | 45 | if (argv.include) { 46 | options.include = argv.include; 47 | if (!isArray(options.include)) { 48 | options.include = [options.include]; 49 | } 50 | } 51 | 52 | options.ext = argv.ext; 53 | options.sourceMap = argv.sourcemap; 54 | options.useStrict = argv["use-strict"]; 55 | options.ignoreErrors = argv["ignore-errors"]; 56 | if (argv._[1]) [, options.output] = argv._; 57 | 58 | let time = now(); 59 | webmake(input, options).done(parser => { 60 | time = now() - time; 61 | if (!options.output) { 62 | process.stdout.write(parser); 63 | return; 64 | } 65 | log.notice( 66 | "Done [%d modules from %d packages in %ds]", parser.modulesFiles.length, 67 | count(parser.packages), (time / 1000).toFixed(2) 68 | ); 69 | }); 70 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const contains = require("es5-ext/array/#/contains") 4 | , isFunction = require("es5-ext/function/is-function") 5 | , some = require("es5-ext/object/some") 6 | , deferred = require("deferred") 7 | , { extname, resolve } = require("path") 8 | , stat = deferred.promisify(require("fs").stat) 9 | , readFile = require("fs2/read-file") 10 | , writeFile = require("fs2/write-file") 11 | , readdir = require("fs2/readdir") 12 | , createParser = require("./lib/parser"); 13 | 14 | const { now } = Date 15 | , { stringify } = JSON 16 | , templatePath = resolve(__dirname, "lib/webmake.tpl") 17 | , separator = process.env.OS === "Windows_NT" ? "/[\\\\/]/" : "'/'"; 18 | 19 | const filesAtPath = function (path) { 20 | return stat(path)(stats => { 21 | if (stats.isFile()) return [path]; 22 | if (stats.isDirectory()) { 23 | return readdir(path, { depth: Infinity, type: { file: true } })(data => 24 | data.map(file => resolve(path, file)) 25 | ); 26 | } 27 | return []; 28 | }); 29 | }; 30 | 31 | module.exports = function (input, options, cb) { 32 | if (isFunction(options)) { 33 | cb = options; 34 | options = {}; 35 | } else { 36 | options = Object(options); 37 | } 38 | const time = now(); 39 | const parser = createParser(options); 40 | const promise = parser 41 | .readInput(input, options)(path => 42 | deferred.map([].concat(options.include || []), inputPath => { 43 | inputPath = resolve(String(inputPath)); 44 | return filesAtPath(inputPath) 45 | .invoke("filter", filename => { 46 | const ext = extname(filename); 47 | if (ext === ".js") return true; 48 | if (ext === ".json") return true; 49 | if (ext === ".css") return true; 50 | if (ext === ".html") return true; 51 | return some(parser.extNames, data => contains.call(data, ext)); 52 | }) 53 | .map(parser.readInput, parser); 54 | })(() => readFile(templatePath, "utf-8"))(tpl => { 55 | let src = `${ 56 | tpl 57 | .replace("SEPARATOR", separator) 58 | .replace("EXTENSIONS", stringify(parser.extNames)) 59 | }(${ parser.toString() })(${ stringify(path) });\n`; 60 | if (options.name && options.amd) { 61 | src = `${ 62 | src.replace( 63 | "(function", 64 | `define("${ options.name }", function () { return (function` 65 | ) 66 | }});\n`; 67 | } else if (options.name) { 68 | src = src.replace("(function", `window.${ options.name } = (function`); 69 | } else if (options.cjs) { 70 | src = src.replace("(function", "module.exports = (function"); 71 | } else if (options.amd) { 72 | src = `${ 73 | src.replace("(function", "define(function () { return (function") 74 | }});\n`; 75 | } 76 | return options.output 77 | ? writeFile(resolve(String(options.output)), src)(parser) 78 | : src; 79 | }) 80 | ) 81 | .cb(cb); 82 | promise.time = now() - time; 83 | promise.parser = parser; 84 | return promise; 85 | }; 86 | -------------------------------------------------------------------------------- /lib/browser/load-css.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | "use strict"; 4 | 5 | let style, add; 6 | if (document.createStyleSheet) { 7 | // IE 8 | if (document.styleSheets.length > 29) { 9 | style = document.styleSheets[document.styleSheets.length - 1]; 10 | } else { 11 | style = document.createStyleSheet(); 12 | } 13 | add = function (css) { style.cssText += css; }; 14 | } else { 15 | style = document.getElementsByTagName("head")[0].appendChild(document.createElement("style")); 16 | style.setAttribute("type", "text/css"); 17 | add = function (css) { style.appendChild(document.createTextNode(css)); }; 18 | } 19 | module.exports = add; 20 | -------------------------------------------------------------------------------- /lib/find-package-root.js: -------------------------------------------------------------------------------- 1 | // For given path returns root package path. 2 | // If given path doesn't point to package content then null is returned. 3 | 4 | "use strict"; 5 | 6 | const { promisify } = require("deferred") 7 | , memoize = require("memoizee") 8 | , { basename, dirname, resolve } = require("path") 9 | , stat = promisify(require("fs").stat); 10 | 11 | module.exports = memoize( 12 | path => 13 | stat(resolve(path, "package.json"))(stats => stats.isFile(), false)(pkgJsonExists => 14 | pkgJsonExists 15 | ? path 16 | : stat(resolve(path, "node_modules"))(stats => stats.isDirectory(), false)( 17 | nodeModulesExists => { 18 | if (nodeModulesExists) return path; 19 | const parent = dirname(path); 20 | if (parent === path) return null; 21 | if (basename(parent) === "node_modules") return path; 22 | return module.exports(parent)(result => { 23 | if (result !== parent || !basename(parent).startsWith("@")) { 24 | return result; 25 | } 26 | return path; 27 | }); 28 | } 29 | ) 30 | ), 31 | { primitive: true } 32 | ); 33 | -------------------------------------------------------------------------------- /lib/parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const aFrom = require("es5-ext/array/from") 4 | , last = require("es5-ext/array/#/last") 5 | , customError = require("es5-ext/error/custom") 6 | , map = require("es5-ext/object/map") 7 | , callable = require("es5-ext/object/valid-callable") 8 | , validValue = require("es5-ext/object/valid-value") 9 | , isString = require("es5-ext/string/is-string") 10 | , isValue = require("es5-ext/object/is-value") 11 | , sLast = require("es5-ext/string/#/last") 12 | , indent = require("es5-ext/string/#/indent") 13 | , endsWith = require("es5-ext/string/#/ends-with") 14 | , optionalChaining = require("es5-ext/optional-chaining") 15 | , d = require("d") 16 | , deferred = require("deferred") 17 | , memoize = require("memoizee/plain") 18 | , cjsResolve = require("ncjsm/resolve/sync") 19 | , { stat } = require("fs") 20 | , { basename, extname, dirname, join, resolve, sep } = require("path") 21 | , commonPath = require("path2/common") 22 | , getRequire = require("next/module/get-require") 23 | , { readFile } = require("fs2/read-file") 24 | , findRequires = require("find-requires") 25 | , log = require("log").get("webmake") 26 | , findRoot = require("./find-package-root"); 27 | 28 | const { isArray } = Array 29 | , { create, keys } = Object 30 | , { parse, stringify } = JSON 31 | , readFileOpts = { encoding: "utf8" } 32 | , getThis = function () { return this; } 33 | , sheBangRe = /^(#![\0-\t\u000b-\uffff]*)\n/u; 34 | 35 | if (!isValue(sep)) { 36 | throw new Error("Unsupported Node version. Please upgrade, Webmake needs at least Node v0.8"); 37 | } 38 | const dirEndMatch = new RegExp(`(?:^|/|\\${ sep })\\.*$`, "u"); 39 | const packageNamePattern = new RegExp( 40 | `(@[^\\${ sep }]+\\${ sep }[^\\${ sep }]+|[^@\\${ sep }][^\\${ sep }]*)(?:\\${ sep }|$)`, "u" 41 | ); 42 | 43 | const cssDeps = [ 44 | { 45 | value: "webmake/lib/browser/load-css.js", 46 | raw: "'webmake/lib/browser/load-css.js'", 47 | point: 0, 48 | line: 0, 49 | column: 0 50 | } 51 | ]; 52 | 53 | const isOptional = (function () { 54 | const pre = /try\s*\{(?:\s*[a-zA-Z][0-9a-zA-Z]*\s*=)?\s*require\(\s*$/u 55 | , post = new RegExp( 56 | "^\\);?\\s*(?:\\/\\/[\\0-\\x09\\x0b\\x0c\\x0e-" + 57 | "\\u2027\\u2030-\\uffff]*[\\r\\n\\u2028\\u2029]\\s*)?\\}\\s*catch\\s*\\(", 58 | "u" 59 | ); 60 | return function (src, point, pathLength) { 61 | return pre.test(src.slice(0, point - 1)) && post.test(src.slice(point + pathLength - 1)); 62 | }; 63 | })(); 64 | 65 | const parseDependencies = function (text, filename, ignoreErrors) { 66 | let deps; 67 | try { 68 | deps = findRequires(text, { raw: true }); 69 | } catch (e) { 70 | throw customError(`${ e.message } in ${ filename }`, "AST_ERROR", { origin: e }); 71 | } 72 | return deps.filter(node => { 73 | if (isValue(node.value)) return true; 74 | if (!ignoreErrors) { 75 | throw customError( 76 | `Not parsable require call: \`${ node.raw }\` at ${ filename }:${ 77 | node.line 78 | }\n You may` + 79 | " ignore such errors with ignoreErrors option ('ignore-errors'" + 80 | " when running from command line)", 81 | "DYNAMIC_REQUIRE" 82 | ); 83 | } 84 | log.warn("Not parsable require call (ignored): %s at %s:%d", node.raw, filename, node.line); 85 | return false; 86 | }); 87 | }; 88 | 89 | const statP = memoize(filename => { 90 | const def = deferred(); 91 | stat(filename, (err, stats) => { 92 | if (err) def.reject(err); 93 | else def.resolve(stats); 94 | }); 95 | return def.promise; 96 | }); 97 | 98 | const stripBOM = function (source) { 99 | if (source.charCodeAt(0) === 0xfeff) { 100 | // Remove BOM, see: 101 | // https://github.com/joyent/node/blob/master/lib/module.js#L460 102 | // (...) This catches EF BB BF (the UTF-8 BOM) 103 | // because the buffer-to-string conversion in `fs.readFile()` 104 | // translates it to FEFF, the UTF-16 BOM. (...) 105 | source = source.slice(1); 106 | } 107 | return source; 108 | }; 109 | 110 | const getMain = memoize(path => 111 | readFile( 112 | resolve(path, "package.json"), readFileOpts 113 | )(content => { 114 | const { main } = parse(stripBOM(content)); 115 | if (!main) throw new Error("No main setting found"); 116 | return main; 117 | }) 118 | ); 119 | 120 | const readFileContent = function (code, filename, fileParser, localFilename) { 121 | const ext = extname(filename); 122 | let type, sourceUrl, data; 123 | 124 | if (ext !== ".js" && ext !== ".json" && ext !== ".css" && ext !== ".html") { 125 | if (hasOwnProperty.call(fileParser.ext[".js"], ext)) type = ".js"; 126 | else if (hasOwnProperty.call(fileParser.ext[".json"], ext)) type = ".json"; 127 | else if (hasOwnProperty.call(fileParser.ext[".css"], ext)) type = ".css"; 128 | else if (hasOwnProperty.call(fileParser.ext[".html"], ext)) type = ".html"; 129 | 130 | if (!type) throw new Error("Unexpected extension"); 131 | 132 | sourceUrl = localFilename.slice(0, -ext.length) + type; 133 | 134 | // Extension 135 | if (fileParser.transform) { 136 | code = deferred(fileParser.transform(filename, code))(transformedCode => { 137 | if (!isValue(transformedCode)) { 138 | throw customError( 139 | "Provided transform callback must return code string", "INVALID_TRANSFORM" 140 | ); 141 | } 142 | transformedCode = String(transformedCode); 143 | const compiledData = fileParser.ext[type][ext].compile(transformedCode, { 144 | filename, 145 | localFilename, 146 | sourceMap: fileParser.sourceMap, 147 | generatedFilename: sourceUrl 148 | }); 149 | ({ transformedCode } = compiledData); 150 | if (fileParser.sourceMap && compiledData.sourceMap) { 151 | transformedCode += 152 | "//# sourceMappingURL=data:application/json;" + 153 | `base64,${ Buffer.from(compiledData.sourceMap).toString("base64") }\n`; 154 | } 155 | return transformedCode; 156 | }); 157 | } else { 158 | data = fileParser.ext[type][ext].compile(code, { 159 | filename, 160 | localFilename, 161 | sourceMap: fileParser.sourceMap, 162 | generatedFilename: sourceUrl 163 | }); 164 | ({ code } = data); 165 | if (fileParser.sourceMap && data.sourceMap) { 166 | code += `//# sourceMappingURL=data:application/json;base64,${ 167 | Buffer.from(data.sourceMap).toString("base64") 168 | }\n`; 169 | } 170 | } 171 | } else { 172 | type = ext; 173 | sourceUrl = localFilename; 174 | if (fileParser.transform) { 175 | code = deferred(fileParser.transform(filename, code))(transformedData => { 176 | let transformedCode; 177 | if (!isValue(transformedData)) { 178 | throw customError( 179 | "Provided transform callback must return code string", "INVALID_TRANSFORM" 180 | ); 181 | } 182 | transformedCode = 183 | transformedData.code === undefined ? transformedData : transformedData.code; 184 | if (ext === ".js") { 185 | transformedCode = stripBOM(String(transformedCode)).replace( 186 | sheBangRe, "//$1\n" 187 | ); 188 | } else { 189 | transformedCode = String(transformedCode); 190 | } 191 | if (fileParser.sourceMap && transformedData.sourceMap) { 192 | transformedCode += 193 | "//# sourceMappingURL=data:application/json;" + 194 | `base64,${ Buffer.from(transformedData.sourceMap).toString("base64") }\n`; 195 | } 196 | return transformedCode; 197 | }); 198 | } else if (ext === ".js") { 199 | code = stripBOM(code).replace(sheBangRe, "//$1\n"); 200 | } 201 | } 202 | return deferred(code)(resolvedCode => { 203 | let deps; 204 | resolvedCode = String(resolvedCode); 205 | if (type === ".json") { 206 | resolvedCode = `module.exports = ${ resolvedCode.trim() };\n`; 207 | deps = []; 208 | } else if (type === ".css") { 209 | resolvedCode = `require('webmake/lib/browser/load-css.js')(${ 210 | stringify(resolvedCode.trim()) 211 | });\n`; 212 | deps = cssDeps; 213 | } else if (type === ".html") { 214 | resolvedCode = `module.exports = ${ stringify(resolvedCode) };\n`; 215 | deps = []; 216 | } else { 217 | if (sLast.call(resolvedCode) !== "\n") resolvedCode += "\n"; 218 | deps = parseDependencies(resolvedCode, filename, fileParser.ignoreErrors); 219 | } 220 | 221 | if (fileParser.sourceMap) { 222 | resolvedCode = `eval(${ 223 | stringify(`${ resolvedCode }//# sourceURL=${ sourceUrl }`) 224 | });\n`; 225 | } 226 | return { content: resolvedCode, deps }; 227 | }); 228 | }; 229 | 230 | const readFileData = function (filename, fileParser, localFilename) { 231 | return readFile( 232 | filename, readFileOpts 233 | )(code => readFileContent(code, filename, fileParser, localFilename)); 234 | }; 235 | 236 | const readFileDataCached = (function () { 237 | const cache = {}; 238 | return function (filename, fileParser, localFilename) { 239 | let data; 240 | if (hasOwnProperty.call(cache, filename)) data = cache[filename]; 241 | else data = cache[filename] = {}; 242 | return statP(filename)(stats => { 243 | stats = `${ stats.size }.${ stats.mtime.valueOf() }`; 244 | if (data.stats === stats) return data.data; 245 | data.stats = stats; 246 | return (data.data = readFileData(filename, fileParser, localFilename)); 247 | }); 248 | }; 249 | })(); 250 | 251 | const modulesToString = function self(nest, moduleParser) { 252 | const format = moduleParser.prettyOutput ? indent : getThis; 253 | return keys(this) 254 | .sort() 255 | .map(function (name) { 256 | const current = this[name]; 257 | let text = `${ format.call(stringify(name), "\t", nest + 1) }: `; 258 | 259 | if (name === ":mainpath:") { 260 | // Package main instruction 261 | text += stringify(current); 262 | } else if (typeof current === "string") { 263 | // Module 264 | text += `function (exports, module, require) {\n${ 265 | format.call(current, "\t", nest + 2) 266 | }${ format.call("}", "\t", nest + 1) }`; 267 | } else { 268 | // Folder 269 | text += `{\n${ self.call(current, nest + 1, moduleParser) }\n${ 270 | format.call("}", "\t", nest + 1) 271 | }`; 272 | } 273 | return text; 274 | }, this) 275 | .join(",\n"); 276 | }; 277 | 278 | const parser = { 279 | readInput(input, options = {}) { 280 | options = Object(options); 281 | const tree = [], { stream } = options; 282 | let scope, path, content, def; 283 | input = resolve(String(input)); 284 | statP.clear(); 285 | getMain.clear(); 286 | if (stream) { 287 | def = deferred(); 288 | content = ""; 289 | stream.on("data", data => { content += data; }); 290 | stream.on("error", def.reject); 291 | stream.on("end", () => { def.resolve(content); }); 292 | } 293 | return findRoot(input)(root => { 294 | let name, dirs, result; 295 | name = root ? last.call(root.split(sep)) : "/"; 296 | if (!this.modules[name]) { 297 | this.packages[name] = root || sep; 298 | this.modules[name] = {}; 299 | } 300 | scope = this.modules[name]; 301 | dirs = root ? input.slice(root.length + 1) : input.slice(1); 302 | path = name + (root ? sep : "") + dirs; 303 | dirs = dirs.split(sep); 304 | name = dirs.pop(); 305 | dirs.forEach(dir => { 306 | tree.push(scope); 307 | scope = scope[dir] || (scope[dir] = {}); 308 | }); 309 | if (scope[name]) return scope[name]; 310 | if (!stream) { 311 | result = this.readFile(input, name, scope, tree); 312 | if (!scope[name]) scope[name] = result; 313 | return result; 314 | } 315 | return def.promise(fileContent => 316 | readFileContent( 317 | fileContent, name, this, name 318 | )(data => { 319 | scope[name] = data.content; 320 | return deferred.map( 321 | data.deps, this.resolve.bind(this, input, dirname(input), scope, []) 322 | ); 323 | }) 324 | ); 325 | })(() => path.slice(0, -extname(path).length).replace(/\\/gu, "/")); 326 | }, 327 | readFile(filename, name, scope, tree) { 328 | log.debug("read %s", filename); 329 | const read = this.cache ? readFileDataCached : readFileData; 330 | return read( 331 | filename, this, filename.split(sep).slice(-2 - tree.length).join("/") 332 | )(data => { 333 | this.modulesFiles.push(filename); 334 | scope[name] = data.content; 335 | return deferred 336 | .map(data.deps, this.resolve.bind(this, filename, dirname(filename), scope, tree)) 337 | .aside(filePath => { this.depsMap[filename] = filePath; }); 338 | }); 339 | }, 340 | resolve(fromfile, currentDirname, scope, tree, dep) { 341 | log.debug("resolve %s", dep.value); 342 | tree = aFrom(tree); 343 | if (dep.value[0] === ".") { 344 | return this.resolveLocal(fromfile, currentDirname, scope, tree, dep); 345 | } 346 | return this.resolveExternal(fromfile, currentDirname, scope, dep); 347 | }, 348 | resolveLocal(fromfile, dirpath, scope, tree, dep, orgFn, lScope) { 349 | const filename = dep.localFilename || dep.value, { extNames } = this; 350 | log.debug("resolve local %s %s %s %d", filename, dirpath, fromfile, tree.length); 351 | 352 | if (!lScope) lScope = scope; 353 | if (!orgFn) orgFn = filename; 354 | 355 | const resolveExtTypePath = (path, ext) => { 356 | const exts = extNames[ext]; 357 | if (!exts.length) { 358 | throw new Error(`Module '${ orgFn }' not found, as required in '${ fromfile }'`); 359 | } 360 | return deferred.some(exts, fileExt => 361 | statP(path + fileExt)( 362 | stats => { 363 | if (stats.isFile()) { 364 | path += fileExt; 365 | return true; 366 | } 367 | return false; 368 | }, 369 | err => { 370 | if (err.code !== "ENOENT") throw err; 371 | return false; 372 | } 373 | ) 374 | )(found => { 375 | if (!found) { 376 | throw new Error( 377 | `Module '${ orgFn }' not found, as required in '${ fromfile }'` 378 | ); 379 | } 380 | return path; 381 | }); 382 | }; 383 | 384 | const resolveSpecificPath = function (path, ext) { 385 | return statP(path + ext)(stats => { 386 | if (stats.isFile()) return path + ext; 387 | throw new Error("Not Found"); 388 | })(null, err => { 389 | if (err.code !== "ENOENT") throw err; 390 | return resolveExtTypePath(path, ext); 391 | }); 392 | }; 393 | 394 | const resolveFirstTypePath = function (path) { 395 | return resolveSpecificPath(path, ".js")(null, () => 396 | resolveSpecificPath(path, ".json")(null, () => 397 | resolveSpecificPath(path, ".css")(null, () => 398 | resolveSpecificPath(path, ".html") 399 | ) 400 | ) 401 | ); 402 | }; 403 | 404 | const resolvePath = (function () { 405 | const currentResolvePath = function (path, forceIndex) { 406 | return resolveFirstTypePath(path)(null, err => 407 | statP(path)( 408 | stats => { 409 | if (stats.isDirectory()) { 410 | if (forceIndex) return resolveFirstTypePath(resolve(path, "index")); 411 | // eslint-disable-next-line no-use-before-define 412 | return resolveDirPath(path); 413 | } 414 | throw err; 415 | }, 416 | e => { 417 | if (e.code !== "ENOENT") throw e; 418 | throw err; 419 | } 420 | ) 421 | ); 422 | }; 423 | return function (path, forceIndex) { 424 | const ext = extname(path).toLowerCase(); 425 | if (ext === ".js" || ext === ".json" || ext === ".css" || ext === ".html") { 426 | return resolveSpecificPath(path.slice(0, -ext.length), ext)(null, () => 427 | currentResolvePath(path, forceIndex) 428 | ); 429 | } 430 | return currentResolvePath(path, forceIndex); 431 | }; 432 | })(); 433 | 434 | const resolveDirPath = function (path) { 435 | return getMain(path)( 436 | main => resolvePath(resolve(path, main), true), 437 | () => resolveFirstTypePath(resolve(path, "index")) 438 | ); 439 | }; 440 | 441 | const init = dirEndMatch.test(filename) ? resolveDirPath : resolvePath; 442 | return init(resolve(dirpath, filename))( 443 | initFilename => { 444 | const name = basename(initFilename); 445 | let path = dirname(initFilename), dir, tokens, index; 446 | 447 | if (path !== dirpath) { 448 | index = commonPath(dirpath + sep, path + sep).length; 449 | if (!index) throw new Error("Require out of package root scope"); 450 | ++index; 451 | 452 | dirpath = dirpath.slice(index); 453 | path = path.slice(index); 454 | 455 | if (dirpath) { 456 | tokens = dirpath.split(sep); 457 | if (tokens.length > tree.length) { 458 | throw new Error("Require out of package root scope"); 459 | } 460 | while (tokens.pop()) scope = tree.pop(); 461 | } 462 | if (path) { 463 | tokens = path.split(sep); 464 | while ((dir = tokens.shift())) { 465 | tree.push(scope); 466 | scope = scope[dir] || (scope[dir] = {}); 467 | } 468 | } 469 | } 470 | if (this.ignored.has(initFilename)) return initFilename; 471 | if (scope[name]) return initFilename; 472 | const result = this.readFile(initFilename, name, scope, tree); 473 | if (!scope[name]) scope[name] = result; 474 | return result(initFilename); 475 | }, 476 | error => { 477 | if (isOptional(lScope[basename(fromfile)], dep.point, dep.raw.length)) return; 478 | throw error; 479 | } 480 | ); 481 | }, 482 | resolveExternal(fromfile, fileDirname, scope, dep) { 483 | log.debug("resolve external %s", dep.value); 484 | const org = dep.value, lScope = scope; 485 | let filename = join(dep.value), tree, currentRequire, main, path, ext; 486 | 487 | const [, name] = filename.match(packageNamePattern); 488 | const packageName = name.includes(sep) && sep !== "/" ? name.replace(sep, "/") : name; 489 | return deferred.promisifySync(() => { 490 | // If already processed, return result 491 | if (this.modules[packageName]) return this.modules[packageName]; 492 | 493 | if (name === "webmake") { 494 | this.packages.webmake = resolve(__dirname, "../"); 495 | return (this.modules.webmake = {}); 496 | } 497 | // Find path to package with Node.js internal functions 498 | currentRequire = getRequire(fromfile); 499 | try { 500 | path = main = currentRequire.resolve(name); 501 | log.debug("external %s main %s", name, path); 502 | } catch (e) { 503 | log.debug("external %s has no main module", name); 504 | // No main module for the package, try full require path 505 | try { 506 | path = currentRequire.resolve(org); 507 | } catch (e2) { 508 | if (isOptional(scope[basename(fromfile)], dep.point, dep.raw.length)) { 509 | return null; 510 | } 511 | throw new Error( 512 | `Module '${ filename }' not found, as required in '${ fromfile }'` 513 | ); 514 | } 515 | } 516 | if (main === name) { 517 | // Require of Node.js native package. 518 | // Hack Node.js internals to get path to substitite 519 | // eventually provided in node_modules 520 | path = main = optionalChaining( 521 | cjsResolve(fileDirname, name, { silent: true }), "targetPath" 522 | ); 523 | if (!main) { 524 | // No substitute found 525 | if (isOptional(scope[basename(fromfile)], dep.point, dep.raw.length)) { 526 | return null; 527 | } 528 | if (!this.ignoreErrors) { 529 | throw new Error( 530 | `Cannot require ${ stringify(name) } (as in '${ 531 | fromfile 532 | }').\n Native node.js modules` + 533 | " are not ported to client-side. You can however provide" + 534 | " an alternative version of this module in your node_modules" + 535 | " path, it will be picked up by Webmake.\n" 536 | ); 537 | } 538 | log.warn("Require of native %s approached (ignored)", name); 539 | return null; 540 | } 541 | } 542 | 543 | // Find package root 544 | if (!main) { 545 | // Try to calculate root by string subtraction 546 | ext = extname(path); 547 | if (endsWith.call(path, filename + ext)) { 548 | this.packages[name] = path.slice( 549 | 0, name.length - (filename.length + ext.length) 550 | ); 551 | return (this.modules[packageName] = {}); 552 | } 553 | if (endsWith.call(path, filename)) { 554 | this.packages[name] = path.slice(0, name.length - filename.length); 555 | return (this.modules[packageName] = {}); 556 | } 557 | } 558 | // Use dedicated findRoot 559 | const promise = (this.modules[packageName] = findRoot(path)(root => { 560 | const currentModule = {}; 561 | this.packages[name] = root; 562 | return getMain(root)(mainModule => { 563 | currentModule[":mainpath:"] = mainModule; 564 | return currentModule; 565 | }, currentModule); 566 | })); 567 | promise.aside(currentModule => { this.modules[packageName] = currentModule; }); 568 | return promise; 569 | })()(currentScope => { 570 | if (!currentScope) return null; 571 | tree = []; 572 | if (name === filename) filename = currentScope[":mainpath:"] || "index"; 573 | else filename = filename.slice(name.length + 1); 574 | dep.localFilename = filename; 575 | return this.resolveLocal( 576 | fromfile, this.packages[name], currentScope, tree, dep, org, lScope 577 | ); 578 | }); 579 | }, 580 | toString() { 581 | let str = ""; 582 | if (this.useStrict) { 583 | str += "(function () { 'use strict'; return "; 584 | } 585 | str += `{\n${ modulesToString.call(this.modules, 0, this) }\n}`; 586 | if (this.useStrict) { 587 | str += "; }())"; 588 | } 589 | return str; 590 | } 591 | }; 592 | 593 | const getExt = function (ext) { 594 | let name; 595 | if (isString(ext)) { 596 | name = String(ext); 597 | try { 598 | ext = require(`webmake-${ name }`); 599 | } catch (e) { 600 | if (e.code !== "MODULE_NOT_FOUND") throw e; 601 | throw customError( 602 | `Extension '${ name }' not found. Make sure` + 603 | ` you have package 'webmake-${ name }' installed.`, 604 | "EXTENSION_NOT_INSTALLED" 605 | ); 606 | } 607 | } else { 608 | ({ name } = ext); 609 | } 610 | validValue(ext.extension); 611 | callable(ext.compile); 612 | const type = `.${ ext.type || "js" }`; 613 | if (!hasOwnProperty.call(this, type)) { 614 | throw customError( 615 | `Extension '${ name || ext.extension }' configured for unknown type '${ type }'` 616 | ); 617 | } 618 | if (isArray(ext.extension)) { 619 | ext.extension.forEach(function (extName) { 620 | this[type][`.${ String(extName) }`] = ext; 621 | }, this); 622 | } else { 623 | this[type][`.${ String(ext.extension) }`] = ext; 624 | } 625 | }; 626 | 627 | module.exports = exports = function (options = {}) { 628 | const ext = { ".js": {}, ".json": {}, ".css": {}, ".html": {} }; 629 | options = Object(options); 630 | if (options.ext) { 631 | if (isArray(options.ext)) options.ext.forEach(getExt, ext); 632 | else getExt.call(ext, options.ext); 633 | } 634 | const ignored = options.ignore ? new Set(aFrom(options.ignore)) : new Set(); 635 | 636 | return create(parser, { 637 | modules: d({}), 638 | packages: d({}), 639 | modulesFiles: d([]), 640 | useStrict: d(Boolean(options.useStrict)), 641 | ignored: d(ignored), 642 | sourceMap: d(Boolean(options.sourceMap)), 643 | ignoreErrors: d(Boolean(options.ignoreErrors)), 644 | transform: d(isValue(options.transform) ? callable(options.transform) : null), 645 | prettyOutput: d(isValue(options.prettyOutput) ? Boolean(options.prettyOutput) : true), 646 | cache: d(Boolean(options.cache)), 647 | ext: d(ext), 648 | extNames: d(map(ext, value => keys(value))), 649 | depsMap: d({}) 650 | }); 651 | }; 652 | exports.modulesToString = modulesToString; 653 | -------------------------------------------------------------------------------- /lib/webmake.tpl: -------------------------------------------------------------------------------- 1 | // This file was generated by modules-webmake (modules for web) project. 2 | // See: https://github.com/medikoo/modules-webmake 3 | 4 | (function (modules) { 5 | 'use strict'; 6 | 7 | var resolve, getRequire, wmRequire, notFoundError, findFile 8 | , extensions = EXTENSIONS 9 | , envRequire = typeof require === 'function' ? require : null; 10 | 11 | notFoundError = function (path) { 12 | var error = new Error("Could not find module '" + path + "'"); 13 | error.code = 'MODULE_NOT_FOUND'; 14 | return error; 15 | }; 16 | findFile = function (scope, name, extName) { 17 | var i, ext; 18 | if (typeof scope[name + extName] === 'function') return name + extName; 19 | for (i = 0; (ext = extensions[extName][i]); ++i) { 20 | if (typeof scope[name + ext] === 'function') return name + ext; 21 | } 22 | return null; 23 | }; 24 | resolve = function (scope, tree, path, fullPath, state, id) { 25 | var name, dir, exports, module, fn, found, ext; 26 | path = path.split(SEPARATOR); 27 | name = path.pop(); 28 | if ((name === '.') || (name === '..')) { 29 | path.push(name); 30 | name = ''; 31 | } 32 | while ((dir = path.shift()) != null) { 33 | if (!dir || (dir === '.')) continue; 34 | if (dir === '..') { 35 | scope = tree.pop(); 36 | id = id.slice(0, id.lastIndexOf('/')); 37 | } else { 38 | tree.push(scope); 39 | scope = scope[dir]; 40 | id += '/' + dir; 41 | } 42 | if (!scope) throw notFoundError(fullPath); 43 | } 44 | if (name && (typeof scope[name] !== 'function')) { 45 | found = findFile(scope, name, '.js'); 46 | if (!found) found = findFile(scope, name, '.json'); 47 | if (!found) found = findFile(scope, name, '.css'); 48 | if (!found) found = findFile(scope, name, '.html'); 49 | if (found) { 50 | name = found; 51 | } else if ((state !== 2) && (typeof scope[name] === 'object')) { 52 | tree.push(scope); 53 | scope = scope[name]; 54 | id += '/' + name; 55 | name = ''; 56 | } 57 | } 58 | if (!name) { 59 | if ((state !== 1) && scope[':mainpath:']) { 60 | return resolve(scope, tree, scope[':mainpath:'], fullPath, 1, id); 61 | } 62 | return resolve(scope, tree, 'index', fullPath, 2, id); 63 | } 64 | fn = scope[name]; 65 | if (!fn) throw notFoundError(fullPath); 66 | if (fn.hasOwnProperty('module')) return fn.module.exports; 67 | exports = {}; 68 | fn.module = module = { exports: exports, id: id + '/' + name }; 69 | fn.call(exports, exports, module, getRequire(scope, tree, id)); 70 | return module.exports; 71 | }; 72 | wmRequire = function (scope, tree, fullPath, id) { 73 | var name, path = fullPath, t = fullPath.charAt(0), state = 0; 74 | if (t === '/') { 75 | path = path.slice(1); 76 | scope = modules['/']; 77 | if (!scope) { 78 | if (envRequire) return envRequire(fullPath); 79 | throw notFoundError(fullPath); 80 | } 81 | id = '/'; 82 | tree = []; 83 | } else if (t !== '.') { 84 | name = path.indexOf('@') === 0 ? path.split('/', 2).join("/") : path.split('/', 1)[0]; 85 | scope = modules[name]; 86 | if (!scope) { 87 | if (envRequire) return envRequire(fullPath); 88 | throw notFoundError(fullPath); 89 | } 90 | id = name; 91 | tree = []; 92 | path = path.slice(name.length + 1); 93 | if (!path) { 94 | path = scope[':mainpath:']; 95 | if (path) { 96 | state = 1; 97 | } else { 98 | path = 'index'; 99 | state = 2; 100 | } 101 | } 102 | } 103 | return resolve(scope, tree, path, fullPath, state, id); 104 | }; 105 | getRequire = function (scope, tree, id) { 106 | var localRequire = function (path) { 107 | return wmRequire(scope, [].concat(tree), path, id); 108 | }; 109 | if (envRequire) localRequire.fromParentEnvironment = envRequire; 110 | return localRequire 111 | }; 112 | return getRequire(modules, [], ''); 113 | }) 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webmake", 3 | "version": "1.1.2", 4 | "description": "Node.js/CJS modules bundler", 5 | "author": "Mariusz Nowak (http://www.medikoo.com/)", 6 | "keywords": [ 7 | "browser", 8 | "browserify", 9 | "build", 10 | "builder", 11 | "bundle", 12 | "cjs", 13 | "commonjs", 14 | "generator", 15 | "deploy", 16 | "make", 17 | "modules", 18 | "package", 19 | "packager", 20 | "require", 21 | "requirejs", 22 | "web" 23 | ], 24 | "bin": { 25 | "webmake": "./bin/webmake.js" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/medikoo/modules-webmake.git" 30 | }, 31 | "dependencies": { 32 | "d": "^1.0.1", 33 | "deferred": "^0.7.11", 34 | "es5-ext": "^0.10.53", 35 | "find-requires": "^1.0.0", 36 | "fs2": "^0.3.7", 37 | "log": "^6.0.0", 38 | "log-node": "^7.0.0", 39 | "memoizee": "^0.4.14", 40 | "ncjsm": "^4.0.1", 41 | "next": "^0.4.1", 42 | "optimist": "^0.6.1", 43 | "path2": "^0.1.0" 44 | }, 45 | "devDependencies": { 46 | "eslint": "^6.8.0", 47 | "eslint-config-medikoo": "^2.7.0", 48 | "git-list-updated": "^1.2.1", 49 | "husky": "^4.2.3", 50 | "jsdom": "^11.12.0", 51 | "lint-staged": "^10.1.1", 52 | "prettier-elastic": "^1.19.1", 53 | "tad": "^3.0.1" 54 | }, 55 | "husky": { 56 | "hooks": { 57 | "pre-commit": "lint-staged" 58 | } 59 | }, 60 | "lint-staged": { 61 | "*.js": [ 62 | "eslint" 63 | ], 64 | "*.{css,html,js,json,md,yaml,yml}": [ 65 | "prettier -c" 66 | ] 67 | }, 68 | "eslintConfig": { 69 | "extends": "medikoo/node", 70 | "root": true, 71 | "rules": { 72 | "max-lines": "off" 73 | } 74 | }, 75 | "prettier": { 76 | "printWidth": 100, 77 | "tabWidth": 4, 78 | "overrides": [ 79 | { 80 | "files": [ 81 | "*.md" 82 | ], 83 | "options": { 84 | "tabWidth": 2 85 | } 86 | } 87 | ] 88 | }, 89 | "scripts": { 90 | "lint": "eslint --ignore-path=.gitignore .", 91 | "lint:updated": "pipe-git-updated --ext=js -- eslint --ignore-pattern '!*'", 92 | "prettier-check:updated": "pipe-git-updated --ext=css --ext=html --ext=js --ext=json --ext=md --ext=yaml --ext=yml -- prettier -c", 93 | "prettify": "prettier --write --ignore-path .gitignore '**/*.{css,html,js,json,md,yaml,yml}'", 94 | "test": "node ./node_modules/tad/bin/tad" 95 | }, 96 | "license": "ISC" 97 | } 98 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { "rules": { "id-length": "off" } } 2 | -------------------------------------------------------------------------------- /test/__playground/enforce-strict.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line strict 2 | module.exports = (function () { return this; })(); 3 | -------------------------------------------------------------------------------- /test/__playground/includes/markup.html: -------------------------------------------------------------------------------- 1 |
HTML
2 | -------------------------------------------------------------------------------- /test/__playground/includes/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 10px; 3 | } 4 | -------------------------------------------------------------------------------- /test/__playground/lib/browser-test.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | "use strict"; 4 | 5 | require("./browser/test"); 6 | 7 | const styles = document.getElementsByTagName("style"); 8 | 9 | exports.style = styles[styles.length - 1]; 10 | 11 | const div = document.body.appendChild(document.createElement("div")); 12 | div.innerHTML = require("./browser/body"); 13 | 14 | exports.html = div; 15 | -------------------------------------------------------------------------------- /test/__playground/lib/browser/body.html: -------------------------------------------------------------------------------- 1 |

Hello!

2 | -------------------------------------------------------------------------------- /test/__playground/lib/browser/test.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | background: white; 4 | } 5 | -------------------------------------------------------------------------------- /test/__playground/lib/circular-other-foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./circular-other"); 4 | // co would be broken, but it must not be broken in program.js 5 | 6 | exports.dana = "foo"; 7 | -------------------------------------------------------------------------------- /test/__playground/lib/circular-other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./circular-other-foo"); 4 | 5 | module.exports = "circTest"; 6 | -------------------------------------------------------------------------------- /test/__playground/lib/dir.js/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "DIR.JS"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/dynamic.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | const lang = "pl"; 6 | 7 | try { require(`./raz/dwa/${ lang }`); } 8 | catch (e) {} 9 | 10 | exports.foo = "bar"; 11 | -------------------------------------------------------------------------------- /test/__playground/lib/included/a.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "included.a"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/included/b.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "included.b"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "main.index"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/indexed/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "indexed"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/mario.json: -------------------------------------------------------------------------------- 1 | { "raz": 0, "dwa": "trzy", "pięć": false, "cztery": null, "osiem:": undefined } 2 | -------------------------------------------------------------------------------- /test/__playground/lib/nl-comment.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function (localExports) { localExports.nlComment = "nlComment"; }; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/other-type-includes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const dynReq = require; 4 | 5 | dynReq("../includes/style"); 6 | exports.html = dynReq("../includes/markup"); 7 | -------------------------------------------------------------------------------- /test/__playground/lib/path.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "path.js"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/path/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | exports.name = "path"; 6 | -------------------------------------------------------------------------------- /test/__playground/lib/path/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "path/other"; 4 | exports.index = require("."); 5 | exports.indexSlash = require("./"); 6 | exports.parentIndex = require(".."); 7 | exports.parentIndexSlash = require("../"); 8 | -------------------------------------------------------------------------------- /test/__playground/lib/program.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | const indirectRequire = require; 6 | 7 | exports.x = require("./x"); 8 | exports.y = require("./y.js"); 9 | exports.dirjs = require("./dir.js"); 10 | exports.indexed = require("./indexed"); 11 | exports.outer = require("../outer"); 12 | require("./circular-other"); 13 | exports.circularOther = require("./circular-other"); 14 | exports.outerSubIndex = require("../other/sub/"); 15 | exports.pathFile = require("./path"); 16 | exports.pathDir = require("./path/"); 17 | exports.pathIndex = require("./path/index"); 18 | exports.commonPathPart = require("./sub-longer/bar"); 19 | exports.commonRootPathPart = require("../sub-longer/bar"); 20 | exports.outerId = require("./sub/inner/inner").modId; 21 | exports.pathOther = require("./path/other"); 22 | exports.nodeshim = require("path"); 23 | exports.json = require("./mario"); 24 | exports.externalByIndex = require("regular"); 25 | exports.scopedByIndex = require("@scope/package"); 26 | exports.modId = module.id; 27 | 28 | try { 29 | require("./optional-module"); 30 | } catch (e) { 31 | if (e.code !== "MODULE_NOT_FOUND") throw e; 32 | } 33 | 34 | try { 35 | require("optional-package"); 36 | } catch (e) { 37 | if (e.code !== "MODULE_NOT_FOUND") throw e; 38 | } 39 | 40 | // new line/comment check 41 | require("./nl-comment")(exports); 42 | 43 | try { 44 | require("util"); // optional native package 45 | } catch (e) { 46 | if (e.code !== "MODULE_NOT_FOUND") throw e; 47 | } 48 | exports.included = { a: indirectRequire("./included/a"), b: indirectRequire("./included/b") }; 49 | 50 | exports.external = { 51 | main: require("test"), 52 | other: require("test/lib/other.js"), 53 | noMain: require("no-main/lib/some-module") 54 | }; 55 | 56 | try { 57 | require("test/marko/optional-module-of-outer-package"); 58 | } catch (e) { 59 | if (e.code !== "MODULE_NOT_FOUND") throw e; 60 | } 61 | 62 | try { 63 | if (exports) require("../not-taken"); 64 | } catch (e) { 65 | if (e.code === "MODULE_EXISTS") throw e; 66 | } 67 | -------------------------------------------------------------------------------- /test/__playground/lib/sub-longer/bar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-bar"; 4 | exports.sub = require("../sub/foo"); 5 | exports.subInner = require("../sub/inner/inner"); 6 | -------------------------------------------------------------------------------- /test/__playground/lib/sub-longer/inner/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-inner-other"; 4 | exports.outer = require("../../sub/inner/other"); 5 | -------------------------------------------------------------------------------- /test/__playground/lib/sub-longer/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-other"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/sub/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-foo"; 4 | exports.subOuter = require("../sub-longer/inner/other"); 5 | -------------------------------------------------------------------------------- /test/__playground/lib/sub/inner/inner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-inner-inner"; 4 | exports.outer = require("../../sub-longer/other"); 5 | exports.modId = module.id; 6 | -------------------------------------------------------------------------------- /test/__playground/lib/sub/inner/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-inner-other"; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { name: "x", getZ() { return require("./z"); } }; 4 | -------------------------------------------------------------------------------- /test/__playground/lib/y.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "y"; 4 | exports.z = require("./z"); 5 | // If we won't add end of line with webmake, script will crash 6 | -------------------------------------------------------------------------------- /test/__playground/lib/z.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "z"; 4 | exports.y = require("./y"); 5 | -------------------------------------------------------------------------------- /test/__playground/node_modules/@scope/package/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "scoped-by-index"; 4 | -------------------------------------------------------------------------------- /test/__playground/node_modules/no-main/lib/some-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "no-main"; 4 | -------------------------------------------------------------------------------- /test/__playground/node_modules/path/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "path for web"; 4 | -------------------------------------------------------------------------------- /test/__playground/node_modules/regular/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "external-by-index"; 4 | -------------------------------------------------------------------------------- /test/__playground/node_modules/test/lib/chosen-one.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "external-main"; 4 | exports.module = require("./module"); 5 | exports.modId = module.id; 6 | -------------------------------------------------------------------------------- /test/__playground/node_modules/test/lib/module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "module"; 4 | -------------------------------------------------------------------------------- /test/__playground/node_modules/test/lib/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "external-other"; 4 | exports.main = require("../"); 5 | -------------------------------------------------------------------------------- /test/__playground/node_modules/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "lib/chosen-one" 3 | } 4 | -------------------------------------------------------------------------------- /test/__playground/not-taken.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const e = new Error("I should not be taken"); 4 | e.code = "MODULE_EXISTS"; 5 | throw e; 6 | -------------------------------------------------------------------------------- /test/__playground/other/sub/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.name = "outer-index"; 4 | -------------------------------------------------------------------------------- /test/__playground/outer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.name = "outer"; 4 | -------------------------------------------------------------------------------- /test/__playground/require-native.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.fs = require("fs"); 4 | -------------------------------------------------------------------------------- /test/__playground/sub-longer/bar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-bar"; 4 | exports.sub = require("../sub/foo"); 5 | exports.subInner = require("../sub/inner/inner"); 6 | -------------------------------------------------------------------------------- /test/__playground/sub-longer/inner/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-inner-other"; 4 | exports.outer = require("../../sub/inner/other"); 5 | -------------------------------------------------------------------------------- /test/__playground/sub-longer/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-longer-other"; 4 | -------------------------------------------------------------------------------- /test/__playground/sub/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-foo"; 4 | exports.subOuter = require("../sub-longer/inner/other"); 5 | -------------------------------------------------------------------------------- /test/__playground/sub/inner/inner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-inner-inner"; 4 | exports.outer = require("../../sub-longer/other"); 5 | -------------------------------------------------------------------------------- /test/__playground/sub/inner/other.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.id = "sub-inner-other"; 4 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const startsWith = require("es5-ext/string/#/starts-with") 4 | , lock = require("es5-ext/function/#/lock") 5 | , { promisify } = require("deferred") 6 | , fs = require("fs") 7 | , { resolve } = require("path") 8 | , { runInNewContext } = require("vm") 9 | , browserContext = require("./lib/browser/__tad").context; 10 | 11 | const readFile = promisify(fs.readFile) 12 | , unlink = promisify(fs.unlink) 13 | , pg = `${ __dirname }/__playground`; 14 | 15 | module.exports = { 16 | ""(t, a, d) { 17 | const input = `${ pg }/lib/program.js` 18 | , output = `${ pg }/build.js` 19 | , options = { include: `${ pg }/lib/included`, ignore: [resolve(pg, "not-taken.js")] }; 20 | t = promisify(t); 21 | t( 22 | input, options 23 | )(result => { 24 | const program = runInNewContext(result, {}); 25 | a(program.x.name, "x", "Same path require"); 26 | a(program.x.getZ().name, "z", "Deferred call"); 27 | a( 28 | program.x.getZ(), program.x.getZ(), 29 | "Requiring same object twice, should return same object" 30 | ); 31 | a(program.y.z.name, "z", "Require within required module"); 32 | a(program.y.z.y.name, "y", "Circular dependency"); 33 | a(program.dirjs, "DIR.JS", "Directory with '.js' extension"); 34 | a(program.indexed.name, "indexed", "Folder index"); 35 | a(program.included.a.name, "included.a", "Manually included #1"); 36 | a(program.included.b.name, "included.b", "Manually included #2"); 37 | a(program.outer.name, "outer", "Require module up tree"); 38 | a(program.outerSubIndex.name, "outer-index", "Require index from sibling directory"); 39 | a(program.pathFile.name, "path.js", "Dir/file collision: file"); 40 | a(program.pathDir.name, "path", "Dir/file collision: dir"); 41 | a(program.pathIndex.name, "path", "Dir/file collision: dir/index"); 42 | a(program.externalByIndex.name, "external-by-index", "External package by index"); 43 | a(program.scopedByIndex.name, "scoped-by-index", "External scoped package by index"); 44 | 45 | a(program.commonPathPart.id, "sub-longer-bar", "Common path part: main"); 46 | a(program.commonPathPart.sub.id, "sub-foo", "Common path part: outer"); 47 | a(program.commonPathPart.subInner.id, "sub-inner-inner", "Common path part: outer #2"); 48 | a( 49 | program.commonPathPart.subInner.outer.id, "sub-longer-other", 50 | "Common path part: outer #3" 51 | ); 52 | a( 53 | program.commonPathPart.sub.subOuter.id, "sub-longer-inner-other", 54 | "Common path part: outer #5" 55 | ); 56 | a( 57 | program.commonPathPart.sub.subOuter.outer.id, "sub-inner-other", 58 | "Common path part: outer #4" 59 | ); 60 | 61 | a(program.commonRootPathPart.id, "sub-longer-bar", "Common path part: main"); 62 | a(program.commonRootPathPart.sub.id, "sub-foo", "Common path part: outer"); 63 | a( 64 | program.commonRootPathPart.subInner.id, "sub-inner-inner", 65 | "Common root path part: outer #2" 66 | ); 67 | a(program.outerId, "__playground/lib/sub/inner/inner.js", "Inner module id"); 68 | a( 69 | program.commonRootPathPart.subInner.outer.id, "sub-longer-other", 70 | "Common root path part: outer #3" 71 | ); 72 | a( 73 | program.commonRootPathPart.sub.subOuter.id, "sub-longer-inner-other", 74 | "Common root path part: outer #5" 75 | ); 76 | a( 77 | program.commonRootPathPart.sub.subOuter.outer.id, "sub-inner-other", 78 | "Common root path part: outer #4" 79 | ); 80 | 81 | a(program.pathOther.name, "path/other", "Dir/file collision: other"); 82 | a(program.pathOther.index.name, "path", "'.' - index require"); 83 | a(program.pathOther.indexSlash.name, "path", "'./' - index require (slash)"); 84 | a(program.pathOther.parentIndex, "main.index", "'..' - parent index"); 85 | a(program.pathOther.parentIndexSlash, "main.index", "'../' - parent index (slash)"); 86 | a(program.nlComment, "nlComment", "New line / Comment"); 87 | a(program.external.other.name, "external-other", "Require module from other package"); 88 | a(program.external.other.main, program.external.main, "Require dir by package.json"); 89 | a( 90 | program.external.main.name, "external-main", 91 | "Require main module from other package" 92 | ); 93 | a(program.external.main.modId, "test/lib/chosen-one.js", "External package id"); 94 | a(program.external.main.module.name, "module", "Require module within other package"); 95 | a( 96 | program.external.noMain.name, "no-main", 97 | "Require from package that doesn't have main module" 98 | ); 99 | a(program.nodeshim, "path for web"); 100 | a.deep( 101 | program.json, 102 | { "raz": 0, "dwa": "trzy", "pięć": false, "cztery": null, "osiem:": undefined }, 103 | "JSON" 104 | ); 105 | a(program.modId, "__playground/lib/program.js", "Module id"); 106 | 107 | a(program.circularOther, "circTest", "Partially broken dependecy test"); 108 | 109 | options.output = output; 110 | return t(input, options)(lock.call(readFile, output, "utf8"))(content => { 111 | a(result, content, "Write to file"); 112 | return unlink(output); 113 | }); 114 | }).done(d, d); 115 | }, 116 | "No includes"(t, a, d) { 117 | const input = `${ pg }/lib/x.js`; 118 | t = promisify(t); 119 | t(input)(result => { 120 | const program = runInNewContext(result, {}, input); 121 | a(program.name, "x", "Same path require"); 122 | a(program.getZ().name, "z", "External name"); 123 | }).done(d, d); 124 | }, 125 | "Other type includes"(t, a, d) { 126 | const input = `${ pg }/lib/other-type-includes.js` 127 | , options = { include: `${ pg }/includes` }; 128 | t = promisify(t); 129 | t( 130 | input, options 131 | )(result => { 132 | const program = runInNewContext(result, browserContext, input); 133 | a(program.html, "
HTML
\n", "Same path require"); 134 | }).done(d, d); 135 | }, 136 | "Unresolved path"(t, a, d) { 137 | const input = `${ pg }/././lib/x.js`; 138 | t = promisify(t); 139 | t(input)(result => { 140 | const program = runInNewContext(result, {}, input); 141 | a(program.name, "x", "Same path require"); 142 | a(program.getZ().name, "z", "External name"); 143 | }).done(d, d); 144 | }, 145 | // Workaround ESLint bug: https://github.com/eslint/eslint/issues/12619 146 | // eslint-disable-next-line quote-props 147 | "Dynamic": { 148 | Error(t, a, d) { 149 | const input = `${ pg }/lib/dynamic.js`; 150 | t(input)(a.never, e => { a(e.code, "DYNAMIC_REQUIRE"); }).done(d, d); 151 | }, 152 | Ignored(t, a, d) { 153 | const input = `${ pg }/lib/dynamic.js`; 154 | t(input, { ignoreErrors: true })(result => { 155 | const program = runInNewContext(result, {}, input); 156 | a(program.foo, "bar"); 157 | }, a.never).done(d, d); 158 | } 159 | }, 160 | "Error on native"(t, a, d) { 161 | t(`${ pg }/require-native.js`, err => { 162 | a.ok(startsWith.call(err.message, "Cannot require")); 163 | d(); 164 | }); 165 | }, 166 | "Ignore error on native"(t, a, d) { 167 | t(`${ pg }/require-native.js`, { ignoreErrors: true }, (err, data) => { 168 | a(err, null); 169 | if (typeof process === "undefined") { 170 | d(); 171 | return; 172 | } 173 | const program = runInNewContext(data, { require }); 174 | a(program.fs, require("fs"), "Fallback to environment require"); 175 | d(); 176 | }); 177 | }, 178 | "Enforce strict"(t, a, d) { 179 | const input = `${ pg }/enforce-strict.js`; 180 | t(input, { useStrict: true })(result => { 181 | const program = runInNewContext(result, {}, input); 182 | a(program, undefined); 183 | }).done(d, d); 184 | }, 185 | "Expose as CJS"(t, a, d) { 186 | const input = `${ pg }/outer.js`; 187 | t(input, { cjs: true })(result => { 188 | const module = { exports: {} }; 189 | runInNewContext(result, { module }, input); 190 | a(module.exports.name, "outer"); 191 | }).done(d, d); 192 | } 193 | }; 194 | -------------------------------------------------------------------------------- /test/lib/browser/__tad.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let jsdomDocument; 4 | 5 | try { jsdomDocument = new (require("jsdom")).JSDOM().window.document; } 6 | catch (ignore) {} 7 | 8 | if (jsdomDocument) { 9 | exports.context = { Buffer, document: jsdomDocument, process, setTimeout, clearTimeout }; 10 | } 11 | -------------------------------------------------------------------------------- /test/lib/browser/load-css.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const webmake = require("../../../") 4 | , pg = require("path").resolve(__dirname, "../../__playground"); 5 | 6 | module.exports = function (t, a, d) { 7 | webmake(`${ pg }/lib/browser-test.js`)(result => { 8 | // eslint-disable-next-line no-eval 9 | result = eval(result); 10 | a(result.style.innerHTML, "body {\n\tcolor: black;\n\tbackground: white;\n}"); 11 | a(result.html.innerHTML, "

Hello!

\n"); 12 | }).done(d, d); 13 | }; 14 | -------------------------------------------------------------------------------- /test/lib/parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const deferred = require("deferred") 4 | , { resolve } = require("path") 5 | , readFile = require("fs2/read-file"); 6 | 7 | const { keys } = Object, pg = resolve(__dirname, "../__playground"); 8 | 9 | module.exports = { 10 | ""(t, a, d) { 11 | const input = resolve(pg, "lib/program.js"); 12 | const parser = t(); 13 | parser 14 | .readInput(input)(path => { 15 | a(path, "__playground/lib/program", "Path"); 16 | a.deep( 17 | Object.keys(parser.modules).sort(), 18 | ["@scope/package", "__playground", "no-main", "path", "regular", "test"], 19 | "Modules" 20 | ); 21 | }) 22 | .done(d, d); 23 | }, 24 | "Transform"(t, a, d) { 25 | const map = {}; 26 | const input = resolve(pg, "lib/program.js"); 27 | const parser = t({ 28 | transform(filename, code) { 29 | map[filename] = code; 30 | return "'use strict'; module.exports = 'foo';"; 31 | } 32 | }); 33 | parser 34 | .readInput(input)(path => { 35 | a(path, "__playground/lib/program", "Path"); 36 | a.deep(Object.keys(parser.modules).sort(), ["__playground"], "Modules"); 37 | return deferred.map(keys(map), filePath => 38 | readFile(filePath)(content => { a(map[filePath], String(content), filePath); }) 39 | )(() => null); 40 | }) 41 | .done(d, d); 42 | } 43 | }; 44 | --------------------------------------------------------------------------------