├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── bin └── cmd.js ├── changelog.md ├── demo ├── dep1.js ├── dep2.js └── deps │ ├── demo.m.less │ ├── dep-3.js │ └── module1.less ├── index.js ├── package.json └── test.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "accessor-pairs": "error", 12 | "array-bracket-newline": "error", 13 | "array-bracket-spacing": [ 14 | "error", 15 | "always" 16 | ], 17 | "array-callback-return": "error", 18 | "array-element-newline": "error", 19 | "arrow-body-style": "error", 20 | "arrow-parens": "error", 21 | "arrow-spacing": "error", 22 | "block-scoped-var": "error", 23 | "block-spacing": "error", 24 | "brace-style": [ 25 | "error", 26 | "1tbs" 27 | ], 28 | "callback-return": "error", 29 | "camelcase": "error", 30 | "capitalized-comments": [ 31 | "error", 32 | "never" 33 | ], 34 | "class-methods-use-this": "error", 35 | "comma-dangle": "error", 36 | "comma-spacing": [ 37 | "error", 38 | { 39 | "after": true, 40 | "before": false 41 | } 42 | ], 43 | "comma-style": [ 44 | "error", 45 | "last" 46 | ], 47 | "complexity": "error", 48 | "computed-property-spacing": [ 49 | "error", 50 | "always" 51 | ], 52 | "consistent-return": "error", 53 | "consistent-this": "error", 54 | "curly": "error", 55 | "default-case": "error", 56 | "dot-location": "error", 57 | "dot-notation": "error", 58 | "eol-last": "error", 59 | "eqeqeq": "error", 60 | "for-direction": "error", 61 | "func-call-spacing": "error", 62 | "func-name-matching": "error", 63 | "func-names": "off", 64 | "func-style": "off", 65 | "function-paren-newline": "error", 66 | "generator-star-spacing": "error", 67 | "getter-return": "error", 68 | "global-require": "off", 69 | "guard-for-in": "error", 70 | "handle-callback-err": "error", 71 | "id-blacklist": "error", 72 | "id-length": "off", 73 | "id-match": "error", 74 | "implicit-arrow-linebreak": "error", 75 | "indent": "off", 76 | "indent-legacy": "off", 77 | "init-declarations": "off", 78 | "jsx-quotes": "error", 79 | "key-spacing": "error", 80 | "keyword-spacing": [ 81 | "error", 82 | { 83 | "after": true, 84 | "before": true 85 | } 86 | ], 87 | "line-comment-position": "off", 88 | "linebreak-style": [ 89 | "error", 90 | "unix" 91 | ], 92 | "lines-around-comment": "error", 93 | "lines-around-directive": "error", 94 | "lines-between-class-members": "error", 95 | "max-depth": "error", 96 | "max-len": "off", 97 | "max-lines": "error", 98 | "max-nested-callbacks": "error", 99 | "max-params": "error", 100 | "max-statements": "off", 101 | "max-statements-per-line": "error", 102 | "multiline-comment-style": [ 103 | "error", 104 | "separate-lines" 105 | ], 106 | "new-cap": "error", 107 | "new-parens": "error", 108 | "newline-after-var": "off", 109 | "newline-before-return": "off", 110 | "newline-per-chained-call": "error", 111 | "no-alert": "error", 112 | "no-array-constructor": "error", 113 | "no-await-in-loop": "error", 114 | "no-bitwise": "error", 115 | "no-buffer-constructor": "error", 116 | "no-caller": "error", 117 | "no-catch-shadow": "error", 118 | "no-confusing-arrow": "error", 119 | "no-continue": "error", 120 | "no-div-regex": "error", 121 | "no-duplicate-imports": "error", 122 | "no-else-return": "error", 123 | "no-empty-function": "error", 124 | "no-eq-null": "error", 125 | "no-eval": "error", 126 | "no-extend-native": "error", 127 | "no-extra-bind": "error", 128 | "no-extra-label": "error", 129 | "no-extra-parens": "off", 130 | "no-floating-decimal": "error", 131 | "no-implicit-coercion": "error", 132 | "no-implicit-globals": "error", 133 | "no-implied-eval": "error", 134 | "no-inline-comments": "off", 135 | "no-inner-declarations": [ 136 | "error", 137 | "functions" 138 | ], 139 | "no-invalid-this": "off", 140 | "no-iterator": "error", 141 | "no-label-var": "error", 142 | "no-labels": "error", 143 | "no-lone-blocks": "error", 144 | "no-lonely-if": "error", 145 | "no-loop-func": "error", 146 | "no-magic-numbers": "off", 147 | "no-mixed-operators": "error", 148 | "no-mixed-requires": "error", 149 | "no-multi-assign": "error", 150 | "no-multi-spaces": "error", 151 | "no-multi-str": "error", 152 | "no-multiple-empty-lines": "error", 153 | "no-native-reassign": "error", 154 | "no-negated-condition": "error", 155 | "no-negated-in-lhs": "error", 156 | "no-nested-ternary": "error", 157 | "no-new": "error", 158 | "no-new-func": "error", 159 | "no-new-object": "error", 160 | "no-new-require": "error", 161 | "no-new-wrappers": "error", 162 | "no-octal-escape": "error", 163 | "no-param-reassign": "off", 164 | "no-path-concat": "error", 165 | "no-plusplus": [ 166 | "error", 167 | { 168 | "allowForLoopAfterthoughts": true 169 | } 170 | ], 171 | "no-process-env": "error", 172 | "no-process-exit": "error", 173 | "no-proto": "error", 174 | "no-prototype-builtins": "error", 175 | "no-restricted-globals": "error", 176 | "no-restricted-imports": "error", 177 | "no-restricted-modules": "error", 178 | "no-restricted-properties": "error", 179 | "no-restricted-syntax": "error", 180 | "no-return-assign": "error", 181 | "no-return-await": "error", 182 | "no-script-url": "error", 183 | "no-self-compare": "error", 184 | "no-sequences": "error", 185 | "no-shadow": "error", 186 | "no-shadow-restricted-names": "error", 187 | "no-spaced-func": "error", 188 | "no-sync": "error", 189 | "no-tabs": "error", 190 | "no-template-curly-in-string": "error", 191 | "no-ternary": "off", 192 | "no-throw-literal": "error", 193 | "no-trailing-spaces": "error", 194 | "no-undef-init": "error", 195 | "no-undefined": "error", 196 | "no-underscore-dangle": "off", 197 | "no-unmodified-loop-condition": "error", 198 | "no-unneeded-ternary": "error", 199 | "no-unused-expressions": "error", 200 | "no-use-before-define": "error", 201 | "no-useless-call": "error", 202 | "no-useless-computed-key": "error", 203 | "no-useless-concat": "error", 204 | "no-useless-constructor": "error", 205 | "no-useless-rename": "error", 206 | "no-useless-return": "error", 207 | "no-var": "off", 208 | "no-void": "error", 209 | "no-warning-comments": "error", 210 | "no-whitespace-before-property": "error", 211 | "no-with": "error", 212 | "nonblock-statement-body-position": "error", 213 | "object-curly-newline": "error", 214 | "object-curly-spacing": [ 215 | "error", 216 | "always" 217 | ], 218 | "object-shorthand": "error", 219 | "one-var": "off", 220 | "one-var-declaration-per-line": "error", 221 | "operator-assignment": "error", 222 | "operator-linebreak": "error", 223 | "padded-blocks": "off", 224 | "padding-line-between-statements": "error", 225 | "prefer-arrow-callback": "off", 226 | "prefer-const": "error", 227 | "prefer-destructuring": "off", 228 | "prefer-numeric-literals": "error", 229 | "prefer-promise-reject-errors": "error", 230 | "prefer-reflect": "off", 231 | "prefer-rest-params": "off", 232 | "prefer-spread": "error", 233 | "prefer-template": "off", 234 | "quote-props": "off", 235 | "quotes": [ 236 | "error", 237 | "single" 238 | ], 239 | "radix": "error", 240 | "require-await": "error", 241 | "require-jsdoc": "off", 242 | "rest-spread-spacing": "error", 243 | "semi": "error", 244 | "semi-spacing": [ 245 | "error", 246 | { 247 | "after": true, 248 | "before": false 249 | } 250 | ], 251 | "semi-style": [ 252 | "error", 253 | "last" 254 | ], 255 | "sort-imports": "error", 256 | "sort-keys": "off", 257 | "sort-vars": "error", 258 | "space-before-blocks": "error", 259 | "space-before-function-paren": "off", 260 | "space-in-parens": "off", 261 | "space-infix-ops": "error", 262 | "space-unary-ops": "error", 263 | "spaced-comment": "off", 264 | "strict": "error", 265 | "switch-colon-spacing": "error", 266 | "symbol-description": "error", 267 | "template-curly-spacing": "error", 268 | "template-tag-spacing": "error", 269 | "unicode-bom": [ 270 | "error", 271 | "never" 272 | ], 273 | "valid-jsdoc": "error", 274 | "vars-on-top": "off", 275 | "wrap-iife": "error", 276 | "wrap-regex": "error", 277 | "yield-star-spacing": "error", 278 | "yoda": [ 279 | "error", 280 | "never" 281 | ] 282 | } 283 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | .idea 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | dist/ 30 | 31 | .eslintrc 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Roy Riojas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM Version](http://img.shields.io/npm/v/persistify.svg?style=flat)](https://npmjs.org/package/persistify) 2 | 3 | # persistify 4 | `persistify` is a wrapper over watchify and browserify to make it easy to make incremental builds without having to rely on the `watch` mode for scenarios where a watch mode is not available. It reduces the build time from several seconds to milliseconds, without relying on a watch mode :) 5 | 6 | ## Motivation 7 | Just wanted to have a good wrapper over browserify/watchify that allow me to make incremental builds even when not using the watch mode. 8 | 9 | **DISCLAIMER**: this is done persisting the watchify arguments to disk using the [flat-cache](https://npmjs.org/package/flat-cache) and [file-entry-cache](https://npmjs.org/package/file-entry-cache) modules. The best results happen when only a few files were modified. Worse case scenario is when all the files are modified. In average you should experience a very noticeable reduction. As always keep in mind that your mileage may vary. 10 | 11 | ## TODO 12 | 13 | Add unit tests 14 | 15 | ## Install 16 | 17 | ```bash 18 | npm i persistify watchify browserify 19 | ``` 20 | 21 | **Important** persistify used to bundle watchify and browserify. Starting from version 2.0.0 they are declared as peer dependencies and should be installed separatedly 22 | 23 | ## CLI options 24 | 25 | Apart from all the browserify and watchify options the following are also available: 26 | 27 | ```bash 28 | Standard Options: 29 | 30 | --outfile=FILE, -o FILE 31 | 32 | This option is required. Write the browserify bundle to this file. If 33 | the file contains the operators `|` or `>`, it will be treated as a 34 | shell command, and the output will be piped to it (not available on 35 | Windows). 36 | 37 | --verbose, -v [default: false] 38 | 39 | Show when a file was written and how long the bundling took (in 40 | seconds). 41 | 42 | --version 43 | 44 | Show the persistify, watchify and browserify versions with their module paths. 45 | 46 | --watch [default: false] 47 | 48 | if true will use watchify instead of browserify 49 | 50 | --recreate [default: false] 51 | 52 | if set will recreate the cache. Useful when transforms and cached files refuse to cooperate 53 | 54 | --never-cache, -n [default: null] 55 | 56 | a string that will be converted to a regula expression. If a file matches the returned regExp 57 | will never be saved in the cache. Even if the file is in the cache already it will be ignored. 58 | 59 | More than one pattern to be ignored can be specified by repeating this option with other regex 60 | value to ignore 61 | 62 | --cache-id [default: null] 63 | 64 | If you're running multiple persistify instances from the same directory, use this to 65 | differentiate them. 66 | 67 | --cache-dir [default: node_modules/flat-cache/.cache] 68 | 69 | By default, the cache is saved to the default directory that flat-cache sets. This sets a custom directory for the cache files. 70 | 71 | ``` 72 | 73 | ## Examples 74 | 75 | ```bash 76 | # this will browserify src/foo.js and move it to dist/foo.js 77 | # the cache is constructed the first time the command is run so this might take a few 78 | # seconds depending on the complexity of the files you want to browserify 79 | ./node_modules/.bin/persistify src/foo.js -o dist/foo.js 80 | 81 | # next builds will be benefited by the cache 82 | # noticeable reducing the building time 83 | ./node_modules/.bin/persistify src/foo.js -o dist/foo.js 84 | 85 | # reconstruct the cache, this useful when a transform file has changed or 86 | # the cache just started to behave like a spoiled child 87 | ./node_modules/.bin/persistify src/foo.js -o dist/foo.js --recreate 88 | 89 | # this will use the cache and watchify to provide faster startup times on watch mode 90 | ./node_modules/.bin/persistify src/foo.js -o dist/foo.js --watch 91 | 92 | # this will just use the cache and use a transform 93 | # (all the parameters are just passed to browserify 94 | # so it should work with any transform) 95 | ./node_modules/.bin/persistify src/foo.js -t babelify -o dist/foo.js --watch 96 | 97 | # this will just use the cache and use two transforms 98 | # but will never add to the cache any files that match the `m.less` extension 99 | # since those files can also require files and those files won't be cached 100 | # this is the safer way to prevent those changes to be skipped because of the cache 101 | ./node_modules/.bin/persistify src/foo.js -t babelify -t simpless -o dist/foo.js -n '\.less$' 102 | ``` 103 | 104 | ## As a node module 105 | 106 | ```javascript 107 | var persistify = require( 'persistify' ); 108 | 109 | var b = persistify( { 110 | //browserify options here. e.g 111 | // debug: true 112 | }, { watch: true } ); 113 | 114 | b.add( './demo/dep1.js' ); 115 | 116 | b.on( 'bundle:done', function ( time ) { 117 | console.log( 'time', time ); 118 | } ); 119 | 120 | b.on( 'error', function ( err ) { 121 | console.log( 'error', err ); 122 | } ); 123 | 124 | function doBundle() { 125 | b.bundle( function ( err, buff ) { 126 | if ( err ) { 127 | throw err; 128 | } 129 | require( 'fs' ).writeFileSync( './dist/bundle.js', buff.toString() ); 130 | } ); 131 | 132 | } 133 | 134 | doBundle(); 135 | 136 | b.on( 'update', function () { 137 | doBundle(); 138 | } ); 139 | 140 | ``` 141 | 142 | ## FAQ 143 | 144 | ### My less files are not detected as changed when using a transformation like `lessify`. Why? 145 | 146 | Because those files are not loaded thru browserify so the cache will ignore them. use `-n, --never-cache` to specify certain files (or file types) to never be cached. 147 | 148 | **Example: Using the cli** 149 | 150 | ```bash 151 | # the following will exclude files ending in `.less` from being kept in the cache 152 | ./node_modules/.bin/persistify src/foo.js -t lessify -o dist/foo.js -n '\.less$' 153 | ``` 154 | 155 | **Example: Using the node api** 156 | ```javascript 157 | var persistify = require( 'persistify' ); 158 | 159 | var b = persistify( { 160 | //browserify options here. e.g 161 | // debug: true 162 | }, { 163 | neverCache: [/\.less$/] // using the node api 164 | } ); 165 | 166 | b.add( './demo/dep1.js' ); 167 | 168 | // when a file is ignored from the cache 169 | // a skip:cache event is fired on the bundle instance 170 | b.on('skip:cache', function (file) { 171 | console.log( 'skipping the cache for', file); 172 | }); 173 | 174 | b.on( 'error', function ( err ) { 175 | console.log( 'error', err ); 176 | } ); 177 | 178 | function doBundle() { 179 | b.bundle( function ( err, buff ) { 180 | if ( err ) { 181 | throw err; 182 | } 183 | require( 'fs' ).writeFileSync( './dist/bundle.js', buff.toString() ); 184 | } ); 185 | } 186 | 187 | doBundle(); 188 | ``` 189 | 190 | ### My build does not include the latest changes to my files! not detecting changed files? 191 | 192 | Mmm... that's weird, but the option `--recreate` should destroy the cache and create it again which most of the times should fix your issue. 193 | 194 | ### I have added a new transform and the build is not using its magic! 195 | 196 | ~Since persistify will only work on the files that have changed, and adding a transform 197 | does not cause a file change it is better to just use `--recreate` after adding a new trasform or plugin~ 198 | 199 | Latest version of `persistify` will ignore the cache if the command used to execute it changes. 200 | 201 | ## Changelog 202 | 203 | [Changelog](./changelog.md) 204 | 205 | ## License 206 | 207 | [MIT](./LICENSE) 208 | -------------------------------------------------------------------------------- /bin/cmd.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-disable no-console */ 4 | 5 | var fs = require( 'fs' ); 6 | var path = require( 'path' ); 7 | var outpipe = require( 'outpipe' ); 8 | var subarg = require( 'subarg' ); 9 | 10 | var nodeConsole = console; 11 | 12 | function run() { 13 | var _argv = process.argv.slice( 2 ); 14 | var persistifyArgs = subarg( _argv, { 15 | alias: { 16 | 'n': 'never-cache', 17 | 'd': 'cache-dir' 18 | } 19 | } ); 20 | 21 | var watch = persistifyArgs.watch; 22 | var recreate = persistifyArgs.recreate; 23 | var neverCache = persistifyArgs[ 'never-cache' ]; 24 | var cacheId = persistifyArgs[ 'cache-id' ]; 25 | var cacheDir = persistifyArgs[ 'cache-dir' ]; 26 | 27 | var w = require( '../' )( null, { 28 | cacheId, 29 | cacheDir, 30 | command: _argv.join( ' ' ), 31 | neverCache, 32 | watch, 33 | recreate 34 | }, process.argv.slice( 2 ) ); 35 | 36 | var outfile = w.argv.o || w.argv.outfile; 37 | var verbose = w.argv.v || w.argv.verbose; 38 | 39 | if ( w.argv.version ) { 40 | nodeConsole.error( 'persistify v' + require( '../package.json' ).version + 41 | ' (in ' + path.resolve( __dirname, '..' ) + ')' ); 42 | nodeConsole.error( 'watchify v' + require( 'watchify/package.json' ).version + 43 | ' (in ' + path.dirname( require.resolve( 'watchify' ) ) + ')' ); 44 | nodeConsole.error( 'browserify v' + require( 'browserify/package.json' ).version + 45 | ' (in ' + path.dirname( require.resolve( 'browserify' ) ) + ')' ); 46 | return; 47 | } 48 | 49 | if ( !outfile ) { 50 | nodeConsole.error( 'You MUST specify an outfile with -o.' ); 51 | process.exit( 1 ); //eslint-disable-line 52 | } 53 | 54 | var bytes, time; 55 | if ( watch ) { 56 | w.on( 'bytes', function ( b ) { 57 | bytes = b; 58 | } ); 59 | w.on( 'time', function ( t ) { 60 | time = t; 61 | } ); 62 | } else { 63 | w.on( 'bundle:done', function ( t ) { 64 | time = t; 65 | } ); 66 | } 67 | 68 | w.on( 'skip:cache', function ( file ) { 69 | if ( !verbose ) { 70 | return; 71 | } 72 | nodeConsole.error( 'skip file from cache:', file ); 73 | } ); 74 | 75 | function bundle() { 76 | var didError = false; 77 | var outStream = process.platform === 'win32' 78 | ? fs.createWriteStream( outfile ) 79 | : outpipe( outfile ); 80 | 81 | var wb = w.bundle(); 82 | wb.on( 'error', function ( err ) { 83 | nodeConsole.error( String( err ) ); 84 | didError = true; 85 | outStream.end( 'console.error(' + JSON.stringify( String( err ) ) + ');' ); 86 | } ); 87 | wb.pipe( outStream ); 88 | 89 | outStream.on( 'error', function ( err ) { 90 | nodeConsole.error( 'persistify error: ', err ); 91 | } ); 92 | outStream.on( 'close', function () { 93 | if ( didError && !watch ) { 94 | nodeConsole.error( 'persistify failed...' ); 95 | process.exit( 1 ); //eslint-disable-line 96 | } 97 | if ( verbose && !didError ) { 98 | if ( watch ) { 99 | nodeConsole.error( bytes + ' bytes written to ' + outfile + 100 | ' (' + (time / 1000).toFixed( 2 ) + ' seconds)' ); 101 | } else { 102 | nodeConsole.error( 'bundle done! ' + 103 | ' (' + (time / 1000).toFixed( 2 ) + ' seconds)' ); 104 | } 105 | } 106 | 107 | } ); 108 | } 109 | 110 | w.on( 'update', bundle ); 111 | bundle(); 112 | } 113 | 114 | try { 115 | require.resolve( 'browserify' ); 116 | require.resolve( 'watchify' ); 117 | } catch (x) { 118 | console.error( 'browserify and watchify are mandatory, please install them separatedly!' ); 119 | console.error( ' npm i --save-dev browserify watchify ' ); 120 | process.exit( 1 ); // eslint-disable-line 121 | } 122 | 123 | run(); 124 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | 2 | # persistify - Changelog 3 | ## v2.0.1 4 | - **Bug Fixes** 5 | - Add note to readme about watchify and browserify being now peer dependencies - [eec63f6]( https://github.com/royriojas/persistify/commit/eec63f6 ), [Roy Riojas](https://github.com/Roy Riojas), 22/03/2018 20:16:15 6 | 7 | 8 | ## v2.0.0 9 | - **Bug Fixes** 10 | - Use latest eslint - [14f6721]( https://github.com/royriojas/persistify/commit/14f6721 ), [Roy Riojas](https://github.com/Roy Riojas), 22/03/2018 20:05:39 11 | 12 | 13 | - **Other changes** 14 | - ignore watch passed to watchify ([#15](https://github.com/royriojas/persistify/issues/15)) - [23fa476]( https://github.com/royriojas/persistify/commit/23fa476 ), [Róbert Oroszi](https://github.com/Róbert Oroszi), 22/03/2018 19:20:53 15 | 16 | * feat: ignore watch from watchify 17 | 18 | * fix: use double quotes 19 | 20 | - use browserify and watchify as peer and dev depedency ([#16](https://github.com/royriojas/persistify/issues/16)) - [08c825a]( https://github.com/royriojas/persistify/commit/08c825a ), [Róbert Oroszi](https://github.com/Róbert Oroszi), 22/03/2018 19:20:25 21 | 22 | * chore: use browserify and watchify as peer and dev depedency 23 | 24 | * chore: notify users that browserify and watchify are mandatory for using persistify 25 | 26 | ## v1.1.1 27 | - **Other changes** 28 | - handle multi-item neverCache arrays ([#12](https://github.com/royriojas/persistify/issues/12)) - [3f7b832]( https://github.com/royriojas/persistify/commit/3f7b832 ), [Mike Chevett](https://github.com/Mike Chevett), 03/08/2016 12:09:40 29 | 30 | 31 | ## v1.1.0 32 | - **Bug Fixes** 33 | - Bump flat-cache version - [6a4bdaf]( https://github.com/royriojas/persistify/commit/6a4bdaf ), [Jean Ponchon](https://github.com/Jean Ponchon), 01/08/2016 04:53:52 34 | 35 | See: 36 | - https://github.com/royriojas/flat-cache/pull/6 37 | - https://github.com/nopnop/fix-circular-flatcache 38 | 39 | ## v1.0.0 40 | - **Build Scripts Changes** 41 | - Update to latest version of browserify and watchify - [ae8b4cc]( https://github.com/royriojas/persistify/commit/ae8b4cc ), [Roy Riojas](https://github.com/Roy Riojas), 22/11/2015 02:33:37 42 | 43 | 44 | ## v0.3.3 45 | - **undefined** 46 | - option to set cacheDir - [12ff7fa]( https://github.com/royriojas/persistify/commit/12ff7fa ), [Joey Baker](https://github.com/Joey Baker), 05/11/2015 13:53:10 47 | 48 | 49 | ## v0.3.2 50 | - **Bug Fixes** 51 | - make persistify fail with code 1 in case of errors. Fixes [#2](https://github.com/royriojas/persistify/issues/2) - [4cc4dc4]( https://github.com/royriojas/persistify/commit/4cc4dc4 ), [royriojas](https://github.com/royriojas), 02/11/2015 11:01:26 52 | 53 | 54 | - **Build Scripts Changes** 55 | - Add test script to verify the code - [5ff8fdf]( https://github.com/royriojas/persistify/commit/5ff8fdf ), [royriojas](https://github.com/royriojas), 02/11/2015 11:00:34 56 | 57 | 58 | ## v0.3.1 59 | - **Bug Fixes** 60 | - proper file formatting - [2904943]( https://github.com/royriojas/persistify/commit/2904943 ), [royriojas](https://github.com/royriojas), 30/10/2015 16:42:41 61 | 62 | 63 | - **Enhancements** 64 | - Add --cache-id command line option - [9f31426]( https://github.com/royriojas/persistify/commit/9f31426 ), [Benjie Gillam](https://github.com/Benjie Gillam), 30/10/2015 07:39:15 65 | 66 | 67 | ## v0.3.0 68 | - **Enhancements** 69 | - add `never-cache` and `command` options - [b81307d]( https://github.com/royriojas/persistify/commit/b81307d ), [royriojas](https://github.com/royriojas), 22/09/2015 02:18:57 70 | 71 | - `--never-cache`, `-n` option allows to specify patterns to match 72 | files that will never be included in the cache. Even if the files were 73 | in the cache already they will be ignored if the file matches the 74 | provided pattern. Multiple patterns can be specified by specifying this 75 | parameter multiple times. 76 | 77 | - `command` option can be used to pass the cli command used to execute 78 | persistify. If that command changes then the cache will be also be 79 | ignored 80 | 81 | ## v0.2.0 82 | - **Features** 83 | - Remove deleted entries from the cache - [56544d3]( https://github.com/royriojas/persistify/commit/56544d3 ), [royriojas](https://github.com/royriojas), 04/09/2015 15:15:51 84 | 85 | 86 | ## v0.1.8 87 | - **Documentation** 88 | - Add info about the CLI usage - [899e311]( https://github.com/royriojas/persistify/commit/899e311 ), [royriojas](https://github.com/royriojas), 31/08/2015 20:34:05 89 | 90 | 91 | ## v0.1.7 92 | - **Documentation** 93 | - Add a note about cache usage and a comment about the browserify options - [cfd3fe5]( https://github.com/royriojas/persistify/commit/cfd3fe5 ), [Roy Riojas](https://github.com/Roy Riojas), 31/08/2015 20:18:45 94 | 95 | Update README.md 96 | 97 | ## v0.1.6 98 | - **Bug Fixes** 99 | - missing browserify dependency to be able to use it without watch mode - [2abf988]( https://github.com/royriojas/persistify/commit/2abf988 ), [Roy Riojas](https://github.com/Roy Riojas), 22/08/2015 20:51:42 100 | 101 | 102 | ## v0.1.5 103 | - **Build Scripts Changes** 104 | - Add keywords - [c91c7f7]( https://github.com/royriojas/persistify/commit/c91c7f7 ), [royriojas](https://github.com/royriojas), 19/08/2015 08:07:20 105 | 106 | 107 | ## v0.1.4 108 | - **Documentation** 109 | - Add FAQ - [35a487a]( https://github.com/royriojas/persistify/commit/35a487a ), [royriojas](https://github.com/royriojas), 19/08/2015 08:04:07 110 | 111 | 112 | ## v0.1.3 113 | - **Documentation** 114 | - Fixed code blocks style - [d5d7fcb]( https://github.com/royriojas/persistify/commit/d5d7fcb ), [royriojas](https://github.com/royriojas), 19/08/2015 07:47:42 115 | 116 | 117 | ## v0.1.2 118 | - **Build Scripts Changes** 119 | - Add keywords - [d46de42]( https://github.com/royriojas/persistify/commit/d46de42 ), [royriojas](https://github.com/royriojas), 19/08/2015 07:46:28 120 | 121 | 122 | ## v0.1.1 123 | - **Features** 124 | - first working version - [dad17de]( https://github.com/royriojas/persistify/commit/dad17de ), [royriojas](https://github.com/royriojas), 19/08/2015 07:45:00 125 | 126 | 127 | - **Other changes** 128 | - Initial commit - [9acf98a]( https://github.com/royriojas/persistify/commit/9acf98a ), [Roy Riojas](https://github.com/Roy Riojas), 11/07/2015 20:51:33 129 | 130 | 131 | -------------------------------------------------------------------------------- /demo/dep1.js: -------------------------------------------------------------------------------- 1 | var abc = require('./dep2'); 2 | console.log('hello world!!!!'); 3 | console.log('another world'); 4 | abc.showMetal(require('./deps/dep-3')); 5 | 6 | abc.on('update', (x) => x * 2); 7 | 8 | var demoM = require('./deps/demo.m.less'); 9 | 10 | console.log(demoM.t('some-class')); -------------------------------------------------------------------------------- /demo/dep2.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | showMetal: function (dep) { 3 | console.log(dep); 4 | console.log('hello world'); 5 | } 6 | } -------------------------------------------------------------------------------- /demo/deps/demo.m.less: -------------------------------------------------------------------------------- 1 | @import 'module1.less'; 2 | 3 | .some-class { 4 | .someMixin(); 5 | } -------------------------------------------------------------------------------- /demo/deps/dep-3.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | some: 'nice', 3 | object: 'here' 4 | }; 5 | 6 | var hashString = require('hash-string'); 7 | hashString('21313'); 8 | 9 | var react = require('react'); 10 | var trim = require('jq-trim') -------------------------------------------------------------------------------- /demo/deps/module1.less: -------------------------------------------------------------------------------- 1 | .someMixin() { 2 | color: red; 3 | background: yellow; 4 | font-size: 20px; 5 | line-height: 40px; 6 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require( 'through2' ); 2 | var watchify = require( 'watchify' ); 3 | var trim = require( 'jq-trim' ); 4 | 5 | var parseAsRegex = function parseAsRegex( regex ) { 6 | if ( typeof regex === 'string' ) { 7 | return new RegExp( regex ); 8 | } 9 | return regex; 10 | }; 11 | 12 | module.exports = function ( browserifyOpts, opts, argv ) { 13 | browserifyOpts = browserifyOpts || { }; 14 | opts = opts || { }; 15 | var hash = require( 'hash-string' ); 16 | 17 | var xtend = require( 'xtend' ); 18 | 19 | var id = 'persistify_' + hash( process.cwd() + trim( opts.cacheId ) ); 20 | var depsCacheId = 'deps-cx-' + id; 21 | var cacheDir = opts.cacheDir; 22 | 23 | var flatCache = require( 'flat-cache' ); 24 | var fileEntryCache = require( 'file-entry-cache' ); 25 | 26 | if ( opts.recreate ) { 27 | flatCache.clearCacheById( id, cacheDir ); 28 | flatCache.clearCacheById( depsCacheId, cacheDir ); 29 | } 30 | // load the cache with id 31 | var cache = flatCache.load( id, cacheDir ); 32 | 33 | // load the file entry cache with id, or create a new 34 | // one if the previous one doesn't exist 35 | var depsCacheFile = fileEntryCache.create( depsCacheId, cacheDir ); 36 | 37 | var ignoreCache = false; 38 | 39 | // if the command was specified this can be used 40 | // as the cache buster 41 | if ( opts.command ) { 42 | var configHashPersisted = cache.getKey( 'configHash' ); 43 | var hashOfConfig = hash( opts.command ); 44 | 45 | ignoreCache = configHashPersisted !== hashOfConfig; 46 | 47 | if ( ignoreCache ) { 48 | cache.setKey( 'configHash', hashOfConfig ); 49 | } 50 | } 51 | 52 | var defaultCache = { 53 | cache: {}, 54 | packageCache: {} 55 | }; 56 | 57 | var persistifyCache = ignoreCache ? defaultCache : (cache.getKey( 'persistifyArgs' ) || defaultCache); 58 | 59 | browserifyOpts.cache = persistifyCache.cache; 60 | browserifyOpts.packageCache = persistifyCache.packageCache; 61 | 62 | var fromArgs = require( 'browserify/bin/args' ); 63 | 64 | var b = argv ? fromArgs( argv, browserifyOpts ) : require( 'browserify' )( browserifyOpts ); 65 | 66 | function normalizeCache( removeDeletedOnly ) { 67 | var cachedFiles = Object.keys( browserifyOpts.cache ); 68 | 69 | var neverCache = opts.neverCache; 70 | if ( neverCache ) { 71 | if ( !Array.isArray( neverCache ) ) { 72 | neverCache = [ neverCache ]; 73 | } 74 | cachedFiles.forEach( function ( file ) { 75 | for (var i = 0; i < neverCache.length; i++) { 76 | var regex = parseAsRegex( neverCache[ i ] ); 77 | 78 | if ( file.match( regex ) ) { 79 | b.emit( 'skip:cache', file ); 80 | delete browserifyOpts.cache[ file ]; //esfmt-ignore-line 81 | break; 82 | } 83 | } 84 | } ); 85 | } 86 | 87 | var res = depsCacheFile.analyzeFiles( cachedFiles ); 88 | 89 | var changedFiles = res.changedFiles; 90 | var notFoundFiles = res.notFoundFiles; 91 | 92 | var changedOrNotFound = removeDeletedOnly ? notFoundFiles : changedFiles.concat( notFoundFiles ); 93 | 94 | if ( changedOrNotFound.length > 0 ) { 95 | changedOrNotFound.forEach( function ( file ) { 96 | delete browserifyOpts.cache[ file ]; //esfmt-ignore-line 97 | } ); 98 | } 99 | 100 | cache.setKey( 'persistifyArgs', { cache: browserifyOpts.cache, packageCache: browserifyOpts.packageCache } ); 101 | } 102 | 103 | normalizeCache(); 104 | 105 | function collect() { 106 | b.pipeline.get( 'deps' ).push( through.obj( function ( row, enc, next ) { 107 | var file = row.expose ? b._expose[ row.id ] : row.file; 108 | persistifyCache.cache[ file ] = { 109 | source: row.source, 110 | deps: xtend( { }, row.deps ) 111 | }; 112 | b.emit( 'file', file ); // attempt to make latest watchify to work with persistify 113 | this.push( row ); 114 | next(); 115 | } ) ); 116 | } 117 | 118 | if ( opts.watch ) { 119 | b = watchify( b, { ignoreWatch: b.argv[ 'ignore-watch' ] || b.argv.iw } ); 120 | } 121 | 122 | collect(); 123 | b.on( 'reset', collect ); 124 | 125 | var oldBundle = b.bundle; 126 | b.bundle = function () { 127 | var start = Date.now(); 128 | var stream; 129 | try { 130 | stream = oldBundle.apply( b, arguments ); 131 | stream.on( 'error', function ( err ) { 132 | console.error( err ); // eslint-disable-line 133 | } ); 134 | stream.on( 'end', function () { 135 | setTimeout( function () { 136 | normalizeCache( true /* remove deleted only*/ ); 137 | depsCacheFile.reconcile(); 138 | cache.save(); 139 | }, 0 ); 140 | 141 | var end = Date.now() - start; 142 | b.emit( 'bundle:done', end ); 143 | } ); 144 | } catch (ex) { 145 | console.error( ex ); // eslint-disable-line 146 | } 147 | 148 | return stream; 149 | }; 150 | 151 | return b; 152 | }; 153 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "persistify", 3 | "version": "2.0.1", 4 | "description": "a wrapper around `browserify` and `watchify` to make incremental builds without the need of the watch mode", 5 | "main": "index.js", 6 | "bin": "bin/cmd.js", 7 | "scripts": { 8 | "beautify": "esbeautifier 'index.js' 'bin/**/*.js' 'specs/**/*.js'", 9 | "beautify-check": "npm run beautify -- -k", 10 | "eslint": "eslint 'index.js' 'bin/**/*.js' 'specs/**/*.js' --cache --cache-location node_modules/.cache/", 11 | "eslint-fix": "npm run eslint -- --fix", 12 | "autofix": "npm run beautify && npm run eslint-fix", 13 | "check": "npm run beautify-check && npm run eslint", 14 | "verify": "npm run check", 15 | "install-hooks": "prepush install && changelogx install-hook && precommit install", 16 | "changelog": "changelogx -f markdown -o ./changelog.md", 17 | "do-changelog": "npm run changelog && git add ./changelog.md && git commit -m 'DOC: Generate changelog' --no-verify", 18 | "pre-v": "npm run verify", 19 | "post-v": "npm run do-changelog && git push --no-verify && git push --tags --no-verify", 20 | "bump-major": "npm run pre-v && npm version major -m 'BLD: Release v%s' && npm run post-v", 21 | "bump-minor": "npm run pre-v && npm version minor -m 'BLD: Release v%s' && npm run post-v", 22 | "bump-patch": "npm run pre-v && npm version patch -m 'BLD: Release v%s' && npm run post-v", 23 | "bundle": "./bin/cmd.js demo/dep1.js -o dist/bundle.js -t simplessy", 24 | "test": "npm run verify", 25 | "bundle:watch": "./bin/cmd.js demo/dep1.js -o dist/bundle.js -t simplessy --watch" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/royriojas/persistify" 30 | }, 31 | "license": "MIT", 32 | "author": "Roy Riojas (http://royriojas.com)", 33 | "prepush": [ 34 | "npm run verify" 35 | ], 36 | "precommit": [ 37 | "npm run verify" 38 | ], 39 | "devDependencies": { 40 | "babel-eslint": "8.2.2", 41 | "babelify": "^6.1.3", 42 | "browserify": "^16.1.1", 43 | "changelogx": "^1.0.18", 44 | "esbeautifier": "^6.1.8", 45 | "eslint": "4.19.1", 46 | "eslinter": "^3.2.1", 47 | "precommit": "^1.1.5", 48 | "prepush": "^3.1.4", 49 | "react": "^0.13.3", 50 | "simplessy": "^1.0.2", 51 | "watchify": "^3.11.0" 52 | }, 53 | "dependencies": { 54 | "file-entry-cache": "^1.2.0", 55 | "flat-cache": "^1.2.1", 56 | "hash-string": "^1.0.0", 57 | "jq-trim": "^0.1.1", 58 | "outpipe": "^1.1.1", 59 | "subarg": "^1.0.0", 60 | "through2": "^2.0.0", 61 | "xtend": "^4.0.0" 62 | }, 63 | "peerDependencies": { 64 | "browserify": "^16.1.1", 65 | "watchify": "^3.11.0" 66 | }, 67 | "changelogx": { 68 | "ignoreRegExp": [ 69 | "BLD: Release", 70 | "DOC: Generate Changelog", 71 | "Generated Changelog", 72 | "REF: formatted missing files" 73 | ], 74 | "issueIDRegExp": "#(\\d+)", 75 | "commitURL": "https://github.com/royriojas/persistify/commit/{0}", 76 | "authorURL": "https://github.com/{0}", 77 | "issueIDURL": "https://github.com/royriojas/persistify/issues/{0}", 78 | "projectName": "persistify" 79 | }, 80 | "bugs": { 81 | "url": "https://github.com/royriojas/persistify/issues" 82 | }, 83 | "homepage": "https://github.com/royriojas/persistify", 84 | "keywords": [ 85 | "watchify", 86 | "browserify", 87 | "cache", 88 | "incremental build", 89 | "incremental browserify", 90 | "browserify incremental", 91 | "browserify-incremental", 92 | "browserify with cache", 93 | "browserify plugin", 94 | "browserify-plugin", 95 | "browserify transform" 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var persistify = require( './' ); 2 | 3 | var b = persistify( { }, { watch: true } ); 4 | 5 | b.add( './demo/dep1.js' ); 6 | 7 | b.on( 'bundle:done', function ( time ) { 8 | console.log( 'time', time ); 9 | } ); 10 | 11 | b.on( 'error', function ( err ) { 12 | console.log( 'error', err ); 13 | } ); 14 | 15 | function doBundle() { 16 | b.bundle( function ( err, buff ) { 17 | if ( err ) { 18 | throw err; 19 | } 20 | require( 'fs' ).writeFileSync( './dist/bundle.js', buff.toString() ); 21 | } ); 22 | 23 | } 24 | 25 | doBundle(); 26 | 27 | b.on( 'update', function () { 28 | doBundle(); 29 | } ); 30 | --------------------------------------------------------------------------------