├── .gitignore ├── .travis.yml ├── example ├── bundle.js ├── example.js ├── ig-tar.js ├── dir-tar.js ├── tar.js └── dir.js ├── README.md ├── package.json ├── LICENSE ├── test ├── scoped.js └── ignores.js └── fstream-npm.js /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore the output junk from the example scripts 2 | example/output 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "6" 5 | - "4" 6 | - "7" 7 | before_install: 8 | - "npm config set spin false" 9 | - "npm install -g npm" 10 | script: "npm test" 11 | notifications: 12 | slack: npm-inc:kRqQjto7YbINqHPb1X6nS3g8 13 | -------------------------------------------------------------------------------- /example/bundle.js: -------------------------------------------------------------------------------- 1 | // this example will bundle every dependency 2 | var P = require("../") 3 | P({ path: "./" }) 4 | .on("package", bundleIt) 5 | .on("entry", function (e) { 6 | console.error(e.constructor.name, e.path.substr(e.root.dirname.length + 1)) 7 | e.on("package", bundleIt) 8 | }) 9 | 10 | function bundleIt (p) { 11 | p.bundleDependencies = Object.keys(p.dependencies || {}) 12 | } 13 | 14 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | // this will show what ends up in the fstream-npm package 2 | var P = require('../') 3 | P({ path: './' }) 4 | .on('package', function (p) { 5 | console.error('package', p) 6 | }) 7 | .on('ignoreFile', function (e) { 8 | console.error('ignoreFile', e) 9 | }) 10 | .on('entry', function (e) { 11 | console.error(e.constructor.name, e.path.substr(e.root.dirname.length + 1)) 12 | }) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fstream-npm 2 | 3 | This is an fstream DirReader class that will read a directory and filter 4 | things according to the semantics of what goes in an npm package. 5 | 6 | For example: 7 | 8 | ```javascript 9 | // This will print out all the files that would be included 10 | // by 'npm publish' or 'npm install' of this directory. 11 | 12 | var FN = require("fstream-npm") 13 | FN({ path: "./" }) 14 | .on("child", function (e) { 15 | console.error(e.path.substr(e.root.path.length + 1)) 16 | }) 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /example/ig-tar.js: -------------------------------------------------------------------------------- 1 | // this will show what ends up in the fstream-npm package 2 | var P = require('fstream-ignore') 3 | var tar = require('tar') 4 | function f (entry) { 5 | return entry.basename !== '.git' 6 | } 7 | 8 | new P({ path: './', type: 'Directory', Directory: true, filter: f }) 9 | .on('package', function (p) { 10 | console.error('package', p) 11 | }) 12 | .on('ignoreFile', function (e) { 13 | console.error('ignoreFile', e) 14 | }) 15 | .on('entry', function (e) { 16 | console.error(e.constructor.name, e.path.substr(e.root.path.length + 1)) 17 | }) 18 | .pipe(tar.Pack()) 19 | .pipe(process.stdout) 20 | -------------------------------------------------------------------------------- /example/dir-tar.js: -------------------------------------------------------------------------------- 1 | // this will show what ends up in the fstream-npm package 2 | var P = require('fstream').DirReader 3 | var tar = require('tar') 4 | function f (entry) { 5 | return entry.basename !== '.git' 6 | } 7 | 8 | new P({ path: './', type: 'Directory', Directory: true, filter: f }) 9 | .on('package', function (p) { 10 | console.error('package', p) 11 | }) 12 | .on('ignoreFile', function (e) { 13 | console.error('ignoreFile', e) 14 | }) 15 | .on('entry', function (e) { 16 | console.error(e.constructor.name, e.path.substr(e.root.path.length + 1)) 17 | }) 18 | .pipe(tar.Pack()) 19 | .pipe(process.stdout) 20 | -------------------------------------------------------------------------------- /example/tar.js: -------------------------------------------------------------------------------- 1 | // this will show what ends up in the fstream-npm package 2 | var P = require('../') 3 | var tar = require('tar') 4 | function f () { 5 | return true 6 | } 7 | // function f (entry) { 8 | // return entry.basename !== ".git" 9 | // } 10 | 11 | new P({ path: './', type: 'Directory', isDirectory: true, filter: f }) 12 | .on('package', function (p) { 13 | console.error('package', p) 14 | }) 15 | .on('ignoreFile', function (e) { 16 | console.error('ignoreFile', e) 17 | }) 18 | .on('entry', function (e) { 19 | console.error(e.constructor.name, e.path) 20 | }) 21 | .on('end', function () { 22 | console.error('ended') 23 | }) 24 | .pipe(tar.Pack()) 25 | .pipe(process.stdout) 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Isaac Z. Schlueter (http://blog.izs.me/)", 3 | "name": "fstream-npm", 4 | "description": "fstream class for creating npm packages", 5 | "version": "1.2.1", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/npm/fstream-npm.git" 9 | }, 10 | "scripts": { 11 | "test": "standard && tap test/*.js" 12 | }, 13 | "main": "./fstream-npm.js", 14 | "dependencies": { 15 | "fstream-ignore": "^1.0.0", 16 | "inherits": "2" 17 | }, 18 | "devDependencies": { 19 | "graceful-fs": "^4.1.2", 20 | "mkdirp": "^0.5.1", 21 | "rimraf": "^2.4.2", 22 | "standard": "^4.3.1", 23 | "tap": "^1.3.2" 24 | }, 25 | "license": "ISC" 26 | } 27 | -------------------------------------------------------------------------------- /example/dir.js: -------------------------------------------------------------------------------- 1 | // this will show what ends up in the fstream-npm package 2 | var P = require('../') 3 | var DW = require('fstream').DirWriter 4 | 5 | var target = new DW({ Directory: true, type: 'Directory', 6 | path: __dirname + '/output'}) 7 | 8 | function f (entry) { 9 | return entry.basename !== '.git' 10 | } 11 | 12 | P({ path: './', type: 'Directory', isDirectory: true, filter: f }) 13 | .on('package', function (p) { 14 | console.error('package', p) 15 | }) 16 | .on('ignoreFile', function (e) { 17 | console.error('ignoreFile', e) 18 | }) 19 | .on('entry', function (e) { 20 | console.error(e.constructor.name, e.path) 21 | }) 22 | .pipe(target) 23 | .on('end', function () { 24 | console.error('ended') 25 | }) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) Isaac Z. Schlueter and Contributors 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 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /test/scoped.js: -------------------------------------------------------------------------------- 1 | var fs = require('graceful-fs') 2 | var join = require('path').join 3 | 4 | var mkdirp = require('mkdirp') 5 | var rimraf = require('rimraf') 6 | var test = require('tap').test 7 | 8 | var Packer = require('..') 9 | 10 | var pkg = join(__dirname, 'test-package-scoped') 11 | 12 | var elfJS = function () {/* 13 | module.exports = function () { 14 | console.log("i'm a elf") 15 | } 16 | */}.toString().split('\n').slice(1, -1).join() 17 | 18 | var json = { 19 | 'name': 'test-package-scoped', 20 | 'version': '3.1.4', 21 | 'main': 'elf.js', 22 | 'bundledDependencies': [ 23 | '@npmwombat/scoped' 24 | ] 25 | } 26 | 27 | test('setup', function (t) { 28 | setup() 29 | t.end() 30 | }) 31 | 32 | var expected = [ 33 | 'package.json', 34 | 'elf.js', 35 | join('node_modules', '@npmwombat', 'scoped', 'index.js'), 36 | join('node_modules', '@npmwombat', 'scoped', 'node_modules', 'example', 'index.js') 37 | ] 38 | 39 | test('includes bundledDependencies', function (t) { 40 | var subject = new Packer({ path: pkg, type: 'Directory', isDirectory: true }) 41 | var actual = [] 42 | subject.on('entry', function (entry) { 43 | t.equal(entry.type, 'File', 'only files in this package') 44 | // include relative path in filename 45 | var filename = entry._path.slice(entry.root._path.length + 1) 46 | actual.push(filename) 47 | }) 48 | // need to do this so fstream doesn't explode when files are removed from 49 | // under it 50 | subject.on('end', function () { 51 | // ensure we get *exactly* the results we expect by comparing in both 52 | // directions 53 | actual.forEach(function (filename) { 54 | t.ok( 55 | expected.indexOf(filename) > -1, 56 | filename + ' is included' 57 | ) 58 | }) 59 | expected.forEach(function (filename) { 60 | t.ok( 61 | actual.indexOf(filename) > -1, 62 | filename + ' is not included' 63 | ) 64 | }) 65 | t.end() 66 | }) 67 | }) 68 | 69 | test('cleanup', function (t) { 70 | // rimraf.sync chokes here for some reason 71 | rimraf(pkg, function () { t.end() }) 72 | }) 73 | 74 | function setup () { 75 | rimraf.sync(pkg) 76 | mkdirp.sync(pkg) 77 | fs.writeFileSync( 78 | join(pkg, 'package.json'), 79 | JSON.stringify(json, null, 2) 80 | ) 81 | 82 | fs.writeFileSync( 83 | join(pkg, 'elf.js'), 84 | elfJS 85 | ) 86 | 87 | var scopedDir = join(pkg, 'node_modules', '@npmwombat', 'scoped') 88 | mkdirp.sync(scopedDir) 89 | fs.writeFileSync( 90 | join(scopedDir, 'index.js'), 91 | "console.log('hello wombat')" 92 | ) 93 | var scopedContent = join(scopedDir, 'node_modules', 'example') 94 | mkdirp.sync(scopedContent) 95 | fs.writeFileSync( 96 | join(scopedContent, 'index.js'), 97 | "console.log('hello example')" 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /test/ignores.js: -------------------------------------------------------------------------------- 1 | var fs = require('graceful-fs') 2 | var join = require('path').join 3 | 4 | var mkdirp = require('mkdirp') 5 | var rimraf = require('rimraf') 6 | var test = require('tap').test 7 | 8 | var Packer = require('..') 9 | 10 | var pkg = join(__dirname, 'test-package') 11 | 12 | var elfJS = function () {/* 13 | module.exports = function () { 14 | console.log("i'm a elf") 15 | } 16 | */}.toString().split('\n').slice(1, -1).join() 17 | 18 | var json = { 19 | 'name': 'test-package', 20 | 'version': '3.1.4', 21 | 'main': 'elf.js' 22 | } 23 | 24 | test('setup', function (t) { 25 | setup() 26 | t.end() 27 | }) 28 | 29 | var included = [ 30 | 'package.json', 31 | 'elf.js', 32 | join('deps', 'foo', 'config', 'config.gypi') 33 | ] 34 | 35 | test('follows npm package ignoring rules', function (t) { 36 | var subject = new Packer({ path: pkg, type: 'Directory', isDirectory: true }) 37 | var filenames = [] 38 | subject.on('entry', function (entry) { 39 | t.equal(entry.type, 'File', 'only files in this package') 40 | 41 | // include relative path in filename 42 | var filename = entry._path.slice(entry.root._path.length + 1) 43 | 44 | filenames.push(filename) 45 | }) 46 | // need to do this so fstream doesn't explode when files are removed from 47 | // under it 48 | subject.on('end', function () { 49 | // ensure we get *exactly* the results we expect by comparing in both 50 | // directions 51 | filenames.forEach(function (filename) { 52 | t.ok( 53 | included.indexOf(filename) > -1, 54 | filename + ' is included' 55 | ) 56 | }) 57 | included.forEach(function (filename) { 58 | t.ok( 59 | filenames.indexOf(filename) > -1, 60 | filename + ' is not included' 61 | ) 62 | }) 63 | t.end() 64 | }) 65 | }) 66 | 67 | test('cleanup', function (t) { 68 | // rimraf.sync chokes here for some reason 69 | rimraf(pkg, function () { t.end() }) 70 | }) 71 | 72 | function setup () { 73 | rimraf.sync(pkg) 74 | mkdirp.sync(pkg) 75 | fs.writeFileSync( 76 | join(pkg, 'package.json'), 77 | JSON.stringify(json, null, 2) 78 | ) 79 | 80 | fs.writeFileSync( 81 | join(pkg, 'elf.js'), 82 | elfJS 83 | ) 84 | 85 | fs.writeFileSync( 86 | join(pkg, '.npmrc'), 87 | 'packaged=false' 88 | ) 89 | 90 | fs.writeFileSync( 91 | join(pkg, '.npmignore'), 92 | '.npmignore\ndummy\npackage.json' 93 | ) 94 | 95 | fs.writeFileSync( 96 | join(pkg, 'dummy'), 97 | 'foo' 98 | ) 99 | 100 | var buildDir = join(pkg, 'build') 101 | mkdirp.sync(buildDir) 102 | fs.writeFileSync( 103 | join(buildDir, 'config.gypi'), 104 | "i_wont_be_included_by_fstream='with any luck'" 105 | ) 106 | 107 | var depscfg = join(pkg, 'deps', 'foo', 'config') 108 | mkdirp.sync(depscfg) 109 | fs.writeFileSync( 110 | join(depscfg, 'config.gypi'), 111 | "i_will_be_included_by_fstream='with any luck'" 112 | ) 113 | 114 | fs.writeFileSync( 115 | join(buildDir, 'npm-debug.log'), 116 | '0 lol\n' 117 | ) 118 | 119 | var gitDir = join(pkg, '.git') 120 | mkdirp.sync(gitDir) 121 | fs.writeFileSync( 122 | join(gitDir, 'gitstub'), 123 | "won't fool git, also won't be included by fstream" 124 | ) 125 | 126 | var historyDir = join(pkg, 'node_modules/history') 127 | mkdirp.sync(historyDir) 128 | fs.writeFileSync( 129 | join(historyDir, 'README.md'), 130 | "please don't include me" 131 | ) 132 | } 133 | -------------------------------------------------------------------------------- /fstream-npm.js: -------------------------------------------------------------------------------- 1 | var Ignore = require('fstream-ignore') 2 | var inherits = require('inherits') 3 | var path = require('path') 4 | var fs = require('fs') 5 | 6 | module.exports = Packer 7 | 8 | inherits(Packer, Ignore) 9 | 10 | function Packer (props) { 11 | if (!(this instanceof Packer)) { 12 | return new Packer(props) 13 | } 14 | 15 | if (typeof props === 'string') { 16 | props = { path: props } 17 | } 18 | 19 | props.ignoreFiles = props.ignoreFiles || [ '.npmignore', 20 | '.gitignore', 21 | 'package.json' ] 22 | 23 | Ignore.call(this, props) 24 | 25 | this.bundled = props.bundled 26 | this.bundleLinks = props.bundleLinks 27 | this.package = props.package 28 | 29 | // only do the magic bundling stuff for the node_modules folder that 30 | // lives right next to a package.json file. 31 | this.bundleMagic = this.parent && 32 | this.parent.packageRoot && 33 | this.basename === 'node_modules' 34 | 35 | // in a node_modules folder, resolve symbolic links to 36 | // bundled dependencies when creating the package. 37 | props.follow = this.follow = this.bundleMagic 38 | // console.error("follow?", this.path, props.follow) 39 | 40 | if (this === this.root || 41 | this.parent && 42 | this.parent.bundleMagic && 43 | this.basename.charAt(0) !== '.') { 44 | this.readBundledLinks() 45 | } 46 | 47 | this.on('entryStat', function (entry, props) { 48 | // files should *always* get into tarballs 49 | // in a user-writable state, even if they're 50 | // being installed from some wackey vm-mounted 51 | // read-only filesystem. 52 | entry.mode = props.mode = props.mode | parseInt('0200', 8) 53 | }) 54 | } 55 | 56 | Packer.prototype.readBundledLinks = function () { 57 | if (this._paused) { 58 | this.once('resume', this.addIgnoreFiles) 59 | return 60 | } 61 | 62 | this.pause() 63 | fs.readdir(this.path + '/node_modules', function (er, list) { 64 | // no harm if there's no bundle 65 | var l = list && list.length 66 | if (er || l === 0) return this.resume() 67 | 68 | var errState = null 69 | var then = function then (er) { 70 | if (errState) return 71 | if (er) { 72 | errState = er 73 | return this.resume() 74 | } 75 | if (--l === 0) return this.resume() 76 | }.bind(this) 77 | 78 | list.forEach(function (pkg) { 79 | if (pkg.charAt(0) === '.') return then() 80 | var pd = this.path + '/node_modules/' + pkg 81 | 82 | // scoped packages 83 | if (pkg.charAt(0) === '@') { 84 | fs.readdir(pd, function (er, slist) { 85 | var sl = slist && slist.length 86 | if (er || sl === 0) return then(er) 87 | 88 | l += sl 89 | slist.forEach(function (spkg) { 90 | if (spkg.charAt(0) === '.') return then() 91 | var spd = pd + '/' + spkg 92 | fs.realpath(spd, function (er, rp) { 93 | if (er) return then() 94 | this.bundleLinks = this.bundleLinks || {} 95 | this.bundleLinks[pkg + '/' + spkg] = rp 96 | then() 97 | }.bind(this)) 98 | }, this) 99 | then() 100 | }.bind(this)) 101 | return 102 | } 103 | 104 | fs.realpath(pd, function (er, rp) { 105 | if (er) return then() 106 | this.bundleLinks = this.bundleLinks || {} 107 | this.bundleLinks[pkg] = rp 108 | then() 109 | }.bind(this)) 110 | }, this) 111 | }.bind(this)) 112 | } 113 | 114 | Packer.prototype.applyIgnores = function (entry, partial, entryObj) { 115 | if (!entryObj || entryObj.type !== 'Directory') { 116 | // package.json files can never be ignored. 117 | if (entry === 'package.json') return true 118 | 119 | // readme files should never be ignored. 120 | if (entry.match(/^readme(\.[^\.]*)$/i)) return true 121 | 122 | // license files should never be ignored. 123 | if (entry.match(/^(license|licence)(\.[^\.]*)?$/i)) return true 124 | 125 | // copyright notice files should never be ignored. 126 | if (entry.match(/^(notice)(\.[^\.]*)?$/i)) return true 127 | 128 | // changelogs should never be ignored. 129 | if (entry.match(/^(changes|changelog|history)(\.[^\.]*)?$/i)) return true 130 | } 131 | 132 | // special rules. see below. 133 | if (entry === 'node_modules' && this.packageRoot) return true 134 | 135 | // package.json main file should never be ignored. 136 | var mainFile = this.package && this.package.main 137 | if (mainFile && path.resolve(this.path, entry) === path.resolve(this.path, mainFile)) return true 138 | 139 | // some files are *never* allowed under any circumstances 140 | // (VCS folders, native build cruft, npm cruft, regular cruft) 141 | if (entry === '.git' || 142 | entry === 'CVS' || 143 | entry === '.svn' || 144 | entry === '.hg' || 145 | entry === '.lock-wscript' || 146 | entry.match(/^\.wafpickle-[0-9]+$/) || 147 | (this.parent && this.parent.packageRoot && this.basename === 'build' && 148 | entry === 'config.gypi') || 149 | entry === 'npm-debug.log' || 150 | entry === '.npmrc' || 151 | entry.match(/^\..*\.swp$/) || 152 | entry === '.DS_Store' || 153 | entry.match(/^\._/) || 154 | entry.match(/^.*\.orig$/) || 155 | // Package locks are never allowed in tarballs -- use shrinkwrap instead 156 | entry === 'package-lock.json' 157 | ) { 158 | return false 159 | } 160 | 161 | // in a node_modules folder, we only include bundled dependencies 162 | // also, prevent packages in node_modules from being affected 163 | // by rules set in the containing package, so that 164 | // bundles don't get busted. 165 | // Also, once in a bundle, everything is installed as-is 166 | // To prevent infinite cycles in the case of cyclic deps that are 167 | // linked with npm link, even in a bundle, deps are only bundled 168 | // if they're not already present at a higher level. 169 | if (this.bundleMagic) { 170 | if (entry.charAt(0) === '@') { 171 | var firstSlash = entry.indexOf('/') 172 | // continue to list the packages in this scope 173 | if (firstSlash === -1) return true 174 | 175 | // bubbling up. stop here and allow anything the bundled pkg allows 176 | if (entry.indexOf('/', firstSlash + 1) !== -1) return true 177 | } 178 | // bubbling up. stop here and allow anything the bundled pkg allows 179 | else if (entry.indexOf('/') !== -1) return true 180 | 181 | // never include the .bin. It's typically full of platform-specific 182 | // stuff like symlinks and .cmd files anyway. 183 | if (entry === '.bin') return false 184 | 185 | // the package root. 186 | var p = this.parent 187 | // the package before this one. 188 | var pp = p && p.parent 189 | 190 | // if this entry has already been bundled, and is a symlink, 191 | // and it is the *same* symlink as this one, then exclude it. 192 | if (pp && pp.bundleLinks && this.bundleLinks && 193 | pp.bundleLinks[entry] && 194 | pp.bundleLinks[entry] === this.bundleLinks[entry]) { 195 | return false 196 | } 197 | 198 | // since it's *not* a symbolic link, if we're *already* in a bundle, 199 | // then we should include everything. 200 | if (pp && pp.package && pp.basename === 'node_modules') { 201 | return true 202 | } 203 | 204 | // only include it at this point if it's a bundleDependency 205 | var bd = this.package && this.package.bundleDependencies 206 | 207 | if (bd && !Array.isArray(bd)) { 208 | throw new Error(this.package.name + '\'s `bundledDependencies` should ' + 209 | 'be an array') 210 | } 211 | 212 | var shouldBundle = bd && bd.indexOf(entry) !== -1 213 | // if we're not going to bundle it, then it doesn't count as a bundleLink 214 | // if (this.bundleLinks && !shouldBundle) delete this.bundleLinks[entry] 215 | return shouldBundle 216 | } 217 | // if (this.bundled) return true 218 | 219 | return Ignore.prototype.applyIgnores.call(this, entry, partial, entryObj) 220 | } 221 | 222 | Packer.prototype.addIgnoreFiles = function () { 223 | var entries = this.entries 224 | // if there's a .npmignore, then we do *not* want to 225 | // read the .gitignore. 226 | if (entries.indexOf('.npmignore') !== -1) { 227 | var i = entries.indexOf('.gitignore') 228 | if (i !== -1) { 229 | entries.splice(i, 1) 230 | } 231 | } 232 | 233 | this.entries = entries 234 | 235 | Ignore.prototype.addIgnoreFiles.call(this) 236 | } 237 | 238 | Packer.prototype.readRules = function (buf, e) { 239 | if (e !== 'package.json') { 240 | return Ignore.prototype.readRules.call(this, buf, e) 241 | } 242 | 243 | buf = buf.toString().trim() 244 | 245 | if (buf.length === 0) return [] 246 | 247 | try { 248 | var p = this.package = JSON.parse(buf) 249 | } catch (er) { 250 | // just pretend it's a normal old file, not magic at all. 251 | return [] 252 | } 253 | 254 | if (this === this.root) { 255 | this.bundleLinks = this.bundleLinks || {} 256 | this.bundleLinks[p.name] = this._path 257 | } 258 | 259 | this.packageRoot = true 260 | this.emit('package', p) 261 | 262 | // make bundle deps predictable 263 | if (p.bundledDependencies && !p.bundleDependencies) { 264 | p.bundleDependencies = p.bundledDependencies 265 | delete p.bundledDependencies 266 | } 267 | 268 | if (!p.files || !Array.isArray(p.files)) return [] 269 | 270 | // ignore everything except what's in the files array. 271 | return ['*'].concat(p.files.map(function (f) { 272 | return '!' + f 273 | })).concat(p.files.map(function (f) { 274 | return '!' + f.replace(/\/+$/, '') + '/**' 275 | })) 276 | } 277 | 278 | Packer.prototype.getChildProps = function (stat) { 279 | var props = Ignore.prototype.getChildProps.call(this, stat) 280 | 281 | props.package = this.package 282 | 283 | props.bundled = this.bundled && this.bundled.slice(0) 284 | props.bundleLinks = this.bundleLinks && 285 | Object.create(this.bundleLinks) 286 | 287 | // Directories have to be read as Packers 288 | // otherwise fstream.Reader will create a DirReader instead. 289 | if (stat.isDirectory()) { 290 | props.type = this.constructor 291 | } 292 | 293 | // only follow symbolic links directly in the node_modules folder. 294 | props.follow = false 295 | return props 296 | } 297 | 298 | var order = [ 299 | 'package.json', 300 | '.npmignore', 301 | '.gitignore', 302 | /^README(\.md)?$/, 303 | 'LICENCE', 304 | 'LICENSE', 305 | /\.js$/ 306 | ] 307 | 308 | Packer.prototype.sort = function (a, b) { 309 | for (var i = 0, l = order.length; i < l; i++) { 310 | var o = order[i] 311 | if (typeof o === 'string') { 312 | if (a === o) return -1 313 | if (b === o) return 1 314 | } else { 315 | if (a.match(o)) return -1 316 | if (b.match(o)) return 1 317 | } 318 | } 319 | 320 | // deps go in the back 321 | if (a === 'node_modules') return 1 322 | if (b === 'node_modules') return -1 323 | 324 | return Ignore.prototype.sort.call(this, a, b) 325 | } 326 | 327 | Packer.prototype.emitEntry = function (entry) { 328 | if (this._paused) { 329 | this.once('resume', this.emitEntry.bind(this, entry)) 330 | return 331 | } 332 | 333 | // if there is a .gitignore, then we're going to 334 | // rename it to .npmignore in the output. 335 | if (entry.basename === '.gitignore') { 336 | entry.basename = '.npmignore' 337 | entry.path = path.resolve(entry.dirname, entry.basename) 338 | } 339 | 340 | // all *.gyp files are renamed to binding.gyp for node-gyp 341 | // but only when they are in the same folder as a package.json file. 342 | if (entry.basename.match(/\.gyp$/) && 343 | this.entries.indexOf('package.json') !== -1) { 344 | entry.basename = 'binding.gyp' 345 | entry.path = path.resolve(entry.dirname, entry.basename) 346 | } 347 | 348 | // skip over symbolic links 349 | if (entry.type === 'SymbolicLink') { 350 | entry.abort() 351 | return 352 | } 353 | 354 | if (entry.type !== 'Directory') { 355 | // make it so that the folder in the tarball is named "package" 356 | var h = path.dirname((entry.root || entry).path) 357 | var t = entry.path.substr(h.length + 1).replace(/^[^\/\\]+/, 'package') 358 | var p = h + '/' + t 359 | 360 | entry.path = p 361 | entry.dirname = path.dirname(p) 362 | return Ignore.prototype.emitEntry.call(this, entry) 363 | } 364 | 365 | // we don't want empty directories to show up in package 366 | // tarballs. 367 | // don't emit entry events for dirs, but still walk through 368 | // and read them. This means that we need to proxy up their 369 | // entry events so that those entries won't be missed, since 370 | // .pipe() doesn't do anythign special with "child" events, on 371 | // with "entry" events. 372 | var me = this 373 | entry.on('entry', function (e) { 374 | if (e.parent === entry) { 375 | e.parent = me 376 | me.emit('entry', e) 377 | } 378 | }) 379 | entry.on('package', this.emit.bind(this, 'package')) 380 | } 381 | --------------------------------------------------------------------------------