├── .editorconfig ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── lib ├── appcache.js ├── build.js ├── bundle.js ├── bundle │ ├── montage.js │ └── mr.js ├── converters.js ├── file.js ├── glob.js ├── hash.js ├── link.js ├── location.js ├── minify-javascript.js ├── process.js ├── processes.js ├── read.js ├── rebase.js ├── reconfigure.js ├── relocate.js ├── shim-minidom.js ├── spinner.js ├── transform.js ├── transform │ ├── css.js │ ├── html.js │ ├── javascript.js │ └── json.js ├── util.js └── write.js ├── name.txt ├── optimize.js ├── package.json └── spec ├── all-spec.js ├── appcache-spec.js ├── bundle-spec.js ├── converters-spec.js ├── file-spec.js ├── glob-spec.js ├── mock-fs-spec.js ├── rebase-spec.js ├── reconfigure-spec.js └── transform ├── css-spec.js └── html-spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .tmp 3 | .idea 4 | .DS_Store 5 | atlassian-ide-plugin.xml 6 | npm-debug.log 7 | report/ 8 | node_modules/ 9 | out/ -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | ./lib/jshint.js 2 | coverage/** 3 | node_modules/** 4 | spec/** 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "bitwise": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "forin": true, 8 | "noarg": true, 9 | "noempty": true, 10 | "nonew": true, 11 | "undef": true, 12 | "unused": true, 13 | "trailing": true, 14 | "indent": 4, 15 | "boss": true, 16 | "eqnull": true, 17 | "browser": true, 18 | "globals": { 19 | "CustomEvent": true, 20 | "WebSocket": false, 21 | 22 | "require": false, 23 | "exports": false, 24 | "module": false, 25 | "global": false, 26 | 27 | "WeakMap": true, 28 | "Map": true, 29 | "Set": true, 30 | "Node": true, 31 | "frames": true, 32 | 33 | "console": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .jshintignore 3 | .jshintrc 4 | .npmignore 5 | .travis.yml 6 | CHANGES.md 7 | coverage 8 | node_modules 9 | spec 10 | builds -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | - 8 5 | - 10 6 | script: npm run $COMMAND 7 | env: 8 | - COMMAND=test 9 | - COMMAND=integration MR_VERSION="#master" 10 | - COMMAND=integration MR_VERSION="18.0.0" 11 | - COMMAND=integration MONTAGE_VERSION="#master" 12 | - COMMAND=integration MONTAGE_VERSION="#18.0.0" 13 | jobs: 14 | include: 15 | - stage: deploy 16 | node_js: 4 17 | script: skip 18 | env: 19 | deploy: 20 | provider: npm 21 | email: "${NPM_EMAIL}" 22 | api_key: "${NPM_API_KEY}" 23 | on: 24 | tags: true 25 | stages: 26 | - test 27 | - deploy 28 | notifications: 29 | irc: 30 | channels: 31 | - "chat.freenode.net#montage" 32 | on_success: false 33 | template: 34 | - "%{author} broke the %{repository} tests on %{branch}: %{build_url}" 35 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 16.1.7 2 | - Fix Object.values|keys|clone due q-io/collections shim leak fix 3 | 4 | ## 16.1.0 5 | - Adds ability to gather css of all app into root html file to reduce load time 6 | - Adds --no-css to disable CSS compression 7 | - Adds --no-css-embedding to disable embedding of CSS in HTML 8 | 9 | ## 16.0.4 10 | - Adds mjson file support 11 | 12 | ## 16.0.3 13 | - Fixes regression around appcache 14 | 15 | ## 0.15.0 16 | 17 | - Fix pathing so that Mop works on Windows. 18 | - Replace JSDom with minidom. Now doesn't need to build any native modules, 19 | making installation a lot simpler. 20 | - Fix bundles for HTML entry points that are not at the root of the package. 21 | 22 | ### 0.14.1 23 | 24 | - Update minimum version of montage. 25 | 26 | ### 0.14.0 27 | 28 | - Companion release for MontageJS v0.14. 29 | 30 | ### 0.13.6 31 | 32 | - Bump Mr dependency to ~0.15.0, to fix bug with modules with "." in them 33 | 34 | ### 0.13.5 35 | 36 | - Fix independent modules creating different bundles 37 | - Bump Mr dependency to ~0.14.2, to fix a bug with projects that use `overlay` in the `package.json`, such as Montage 38 | 39 | ### 0.13.4 40 | 41 | - Bump Mr dependency to ~0.14.0 42 | 43 | ### 0.13.3 44 | 45 | - Don't include the npm-added `readme` property in the `package.json` of 46 | built apps. 47 | 48 | ### 0.13.2 49 | 50 | - Don't warn about module/filename case in dependencies 51 | 52 | **API** 53 | 54 | - Return build path from main `optimize` function 55 | - Add `fs` to config object, so that that a mock FS can be injected for 56 | testing 57 | 58 | ### 0.13.1 59 | 60 | - Only support Mr ~0.13.0 61 | 62 | **API** 63 | 64 | - Update to Q v0.9.6 65 | 66 | ## 0.13.0 67 | 68 | - Breaking change: change package.json "manifest" prop to "appcache", as it 69 | conflicts with the manifest property in Montage. 70 | - Enable CSS compression by default 71 | - Make compatible with bootstrapping of Montage 0.13 72 | - Improve JavaScript parse warning messages 73 | 74 | **API** 75 | 76 | - Numerous dependency version bumps 77 | 78 | ### 0.12.8 79 | 80 | - Lock Q to version 0.9.2 to match Mr and avoid compatibility issues 81 | 82 | ### 0.12.7 83 | 84 | - Fix converting a document without a doctype to string 85 | - Continue when errors occur minifiying inline JavaScript 86 | 87 | ### 0.12.6 88 | 89 | - Keep properties from original package.json in bundled package.json 90 | - Don't change markup when incorrectly nested, as a result of upgrading 91 | html-minifier and other dependencies. 92 | - Add coverage reporting to tests 93 | 94 | ### 0.12.5 95 | 96 | - Browser prefixes are no longer removed when compressing CSS, as a result of 97 | replacing CSSOM with CSSO. 98 | - HTML doctype is preserved. 99 | - Fix rebasing of URLs with no protocol. 100 | - Fix bundle loading. 101 | - Don't warn about WebGL script tags. 102 | 103 | ### 0.12.4 104 | 105 | - Disable CSS compression by default as it breaks unrecognized syntax, most 106 | notably browser prefixes. 107 | - Add --css/-c flag to enable CSS compression. 108 | 109 | ### 0.12.3 110 | 111 | - Important: requires Mr ~0.12.6 to be used by packages. 112 | - Enabled production mode when optimizing to prevent devDependencies being 113 | loaded. 114 | - Exposed the `optimize` function as the exports of the package. See the 115 | readme for the API. 116 | 117 | ### 0.12.2 118 | 119 | - Fixed handling of "exclude" property in package.json. 120 | 121 | ## 0.12.1 122 | 123 | - Extensive refactor for Montage and Mr v0.12. 124 | - Mop now requires Montage > v0.12.2 or Mr > v0.12.0. 125 | - Bundling is no longer optional. 126 | - Incremental builds and shared build dependencies are no longer supported. 127 | - More internal changes. See 128 | https://github.com/montagejs/mop/commit/aa1368d40e20306217dec82def6d9e27832ebafa 129 | 130 | ### 0.0.7 131 | 132 | - Fixed file descriptor limit 133 | - Update for Q.done and improve some error messages. 134 | - Removed shrinkwrap. 135 | - Updated the documented transforms. 136 | 137 | ### 0.0.4 138 | 139 | - Added ``--copyright`` flag for verifying existence of copyright 140 | notices. 141 | - The ``--incremental`` flag is now on by default. 142 | 143 | ### 0.0.3 144 | 145 | - Fixed issue with obsolete manifest removal from the generated 146 | manifest. 147 | 148 | ### 0.0.1 149 | 150 | - Replaced JSLint with JSHint and added deep HTML inspection. 151 | 152 | # 0.0.0 153 | 154 | - Initial release, forked from m-js 155 | 156 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Releasing 5 | --------- 6 | 7 | * Testing 8 | - `rm -rf node_modules; npm install` to make sure you're testing the 9 | latest deps 10 | - `npm test` 11 | - `MR_VERSION=latest npm run integration` 12 | - `MONTAGE_VERSION=latest npm run integration` 13 | - these should have been covered by Travis before any code was merged, but 14 | nice to double check. 15 | * Docs 16 | - Update `CHANGES.md` 17 | - Update supported Montage and Mr versions at the top of the `README.md` 18 | * Publish 19 | - `npm version patch` or other variation of `npm version` 20 | - `npm publish` 21 | - `git push origin master` 22 | - `git push origin --tags` 23 | * Publicity 24 | - Post to mailing list 25 | - Tweet from @montagejs 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 3-clause BSD license 2 | ==================== 3 | 4 | Copyright 2012-2014 Motorola Mobility LLC, Montage Studio Inc, and contributors. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of Motorola Mobility LLC, Montage Studio, Montage nor the 18 | names of its contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Montage Optimizer (aka mop) 2 | 3 | [![npm version](https://img.shields.io/npm/v/mop.svg?style=flat&branch=master)](https://www.npmjs.com/package/montage) 4 | 5 | [![Build Status](https://travis-ci.org/montagejs/mop.png?branch=master)](http://travis-ci.org/montagejs/mop) 6 | 7 | for **Montage** ~0.14.6 and **Mr** ~0.15.0, ~0.14.2 and ~0.13.0. 8 | 9 | Optimizes Montage applications for production by minifying, to reduce file 10 | size, and creating bundles, to reduce the number of requests. 11 | 12 | Install Mop globally with NPM. 13 | 14 | ``` 15 | npm install -g mop 16 | ``` 17 | 18 | Mop can be run in any directory containing a Montage application. 19 | 20 | ```bash 21 | $ ls 22 | index.html node_modules/ package.json ui/ 23 | $ ls node_modules/ 24 | montage/ 25 | $ mop 26 | # performs reading, processing and bundling steps 27 | $ ls builds/ 28 | my-app my-app@f7e7db2/ 29 | ``` 30 | 31 | Features 32 | ======== 33 | 34 | - writes whole packages into an incrementally updated builds 35 | directory, giving each package a consistent hash 36 | - optionally generates AppCache manifest files for every package 37 | - performs optimizing transformations on whole packages, whole files, 38 | and parts of files 39 | - JavaScript minification using UglifyJS (including script 40 | blocks) 41 | - HTML minification using JSDOM 42 | - CSS using CSSOM (including style blocks) 43 | - JSON files 44 | - Montage serialization minification (and precompilation is a 45 | goal) (including ``text/montage-serialization`` script 46 | blocks) 47 | - rewrites inter-package URL’s in HTML and CSS to use relative 48 | URL’s among the build products, regardless of where the 49 | dependencies are installed in development 50 | - converts all modules into scripts, suitable for script 51 | injection, particularly for cross-origin dependencies and 52 | Content Security Policies. 53 | - optionally bundles and shards applications 54 | - aggregates the bootstrapping files and the transitive 55 | dependencies of each HTML file that has a `montage.js` 56 | bootstrapping script into a single script that gets loaded 57 | instead of `montage.js` 58 | - can produce a sequence of preloading bundles, to start 59 | loading after the main application starts. Each phase of 60 | the preload sequence can be optionally split into parallel 61 | downloads or "shards". 62 | - optionally, lints whole applications 63 | - using JSHint for JavaScript in individual files and script 64 | blocks 65 | - checks for broken links in HTML 66 | - checks for unnecessary script block attributes 67 | - checks for JavaScript parse errors in files and script blocks 68 | - checks for JSON parse errors in files and script block, 69 | (planning to also check Montage serialization format) 70 | - checks for copyright notices in HTML, CSS, and JavaScript 71 | - operates holistically on a package and all of its dependencies 72 | 73 | 74 | Usage 75 | ===== 76 | 77 | Creates a ``builds`` directory from one or more applications, packages, 78 | and all their dependencies. The ``builds`` directory will be created in 79 | your current working directory. 80 | 81 | Usage: 82 | 83 | mop [-lfc] [-d @] [--target ] 84 | 85 | e.g, 86 | 87 | $ mop calculator 88 | $ mop -t builds calculator 89 | 90 | ``-t`` or ``--target`` changes the default target build directory. 91 | The default is ``builds`` relative to the current working directory. 92 | 93 | ``-o 0`` or ``--optimize 0`` disables optimizating transforms. 94 | 95 | ``-l`` or ``--lint`` provides per-file warnings if packaged files do 96 | not pass JSLint or various other sanity checks like script MIME types 97 | and known JSON schemas. 98 | 99 | ``-d`` or ``--delimiter`` allows you to override the symbol used between 100 | package names and package hashes in the builds directory, which is ``@`` 101 | by default. 102 | 103 | ``--no-css`` allows you to disable CSS transforms. CSSOM cannot handle 104 | some modern CSS. 105 | 106 | Your project will be assembled in the builds directory. 107 | 108 | ### Notes 109 | 110 | The bootstrap bundle Mop creates is usually large, as it contains the 111 | bootrapping code and the initial modules and all their dependencies. To 112 | avoid the download of this file blocking the rendering of your page make 113 | sure to include the `async` attribute in the bootstrap script tag: 114 | 115 | ```html 116 | 117 | 118 | 119 | 120 | ``` 121 | 122 | API 123 | --- 124 | 125 | ```javascript 126 | var optimize = require("mop"); 127 | 128 | optimize(process.cwd()).then(function () { 129 | console.log("Optimization done."); 130 | }); 131 | ``` 132 | 133 | ### optimize(location, [config]) 134 | 135 | Optimize the package at the given location. 136 | 137 | * location `string` An absolute path to a directory containing an app 138 | to optimize. 139 | * [config] `Object` Configuration for optimization. 140 | * [buildLocation="builds"] `string` An absolute or relative path for a directory to generate the optimized files in. 141 | * [minify=true] `boolean` Whether to minify the files. 142 | * [lint=false] `boolean` Whether to lint the files and output warnings. 143 | * [noCss=true] `boolean` Whether to optimize CSS. Cannot handle some modern CSS, and so disabled by default. 144 | * [delimiter="@"] `string` Symbol to use between the package name and the package hash, e.g. `my-app@f7e7db2` 145 | * [out=spinner] `Object` An object to use for logging. 146 | * [log] `Function` Variadic function that outputs a normal message. 147 | * [warn] `Function` Variadic function that outputs a warning. 148 | * [status] `Function` Variadic function that outputs a status message. These messages are temporary, high volume and should not be permanently displayed. If called with no arguments it should clear the displayed status. 149 | 150 | 151 | Package JSON 152 | ============ 153 | 154 | The build system uses ``package.json`` files to discover dependencies. 155 | These dependencies must always be packages themselves. 156 | 157 | For a comprehensive view of what can be in a ``package.json``, see the 158 | [UncommonJS specification][1]. 159 | 160 | [1]: https://github.com/kriskowal/uncommonjs/blob/master/packages/specification.md 161 | 162 | For the purpose of the build system, the following properties are 163 | important: 164 | 165 | - ``dependencies``: In the presence of a ``dependencies`` property, 166 | the build system assumes that the package was designed for NPM and 167 | that its dependencies were locally installed by NPM. That means 168 | that they can be found by searching the ``node_modules`` directory 169 | of the package. ``dependencies`` are internally transformed into 170 | ``mappings``, assuming that the package is in ``node_modules``. 171 | 172 | - ``mappings``: A more flexible dependency management block. The 173 | local module identifier can be different than the registered package 174 | name. The dependency can have `location`, `name`, `version`, and 175 | `hash` properties. If the dependency is a string, it is coerced to 176 | an object with a location property. 177 | 178 | If a mapping has the same name as a dependency, the mapping 179 | overrides the dependency at run-time, but NPM will only use the 180 | `dependencies` block to install. 181 | 182 | { 183 | "mappings": { 184 | "montage": "../montage/" 185 | } 186 | } 187 | 188 | - ``bundle``: For application packages, configures how the optimizer 189 | will bundle modules so that they can be downloaded by the browser 190 | with HTTP requests. 191 | 192 | - An array turns on bundling as above, but also sets up a 193 | prioritized preloading sequence. Each element of the array 194 | corresponds to a preloading phase. Between each phase, the 195 | run-time has an opportunity to use the newly loaded modules, 196 | while subsequent phases download in the background. 197 | 198 | Each element of the array can be a single module identifier or 199 | an array of module identifiers. Each loading phase will include 200 | all of these modules and their transitive dependencies, but will 201 | exclude any modules that would already be loaded in a prior 202 | phase or the initial bundle. 203 | 204 | The run-time supresses all lazy loading until preloading has 205 | been finished to avoid issuing multiple requests for the same 206 | modules. However, as a consequence, applications should plan to 207 | finish preloading before being provoked by the user to 208 | load modules on demand. 209 | 210 | For the purpose of bundling, Montage Optimizer has a broader view of 211 | what constitutes a dependency than the Montage run-time in 212 | development mode. 213 | 214 | - For a JavaScript module, as with the Montage run-time in 215 | development, all modules mentioned in `require("")` calls with a 216 | string argument. 217 | 218 | - Additionally, for an HTML file, dependencies include: 219 | 220 | - The referenced serialization of a `` tag. 222 | - The modules refered to in a `

text

'; 21 | 22 | var file = new File({ 23 | fs: mockFs, 24 | utf8: original, 25 | package: { 26 | getPackage: function() { 27 | return { 28 | buildLocation: "", 29 | packageDescription: {} 30 | }; 31 | }, 32 | files: {} 33 | } 34 | }); 35 | 36 | var config = { 37 | minify: true, 38 | out: { 39 | warn: function() {} 40 | }, 41 | files: {}, 42 | fs: mockFs 43 | }; 44 | 45 | transformHtml(file, config); 46 | expect(file.utf8).toEqual(original); 47 | }); 48 | 49 | it("sets correct filenames for transformed file", function () { 50 | var original = '

text

'; 51 | 52 | var file = new File({ 53 | fs: mockFs, 54 | utf8: original, 55 | location: "test", 56 | buildLocation: "build.html", 57 | relativeLocation: "relative.html", 58 | package: { 59 | getPackage: function() { 60 | return { 61 | buildLocation: "", 62 | packageDescription: {} 63 | }; 64 | }, 65 | files: {} 66 | } 67 | }); 68 | 69 | var config = { 70 | out: { warn: function() {} }, 71 | files: {}, 72 | fs: mockFs 73 | }; 74 | 75 | transformHtml(file, config); 76 | expect(config.files["test.load.js"].buildLocation).toEqual("build.html.load.js"); 77 | expect(config.files["test.load.js"].relativeLocation).toEqual("relative.html.load.js"); 78 | }); 79 | }); 80 | --------------------------------------------------------------------------------