123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- 'use strict'
- // Do a two-pass walk, first to get the list of packages that need to be
- // bundled, then again to get the actual files and folders.
- // Keep a cache of node_modules content and package.json data, so that the
- // second walk doesn't have to re-do all the same work.
- const bundleWalk = require('npm-bundled')
- const BundleWalker = bundleWalk.BundleWalker
- const BundleWalkerSync = bundleWalk.BundleWalkerSync
- const ignoreWalk = require('ignore-walk')
- const IgnoreWalker = ignoreWalk.Walker
- const IgnoreWalkerSync = ignoreWalk.WalkerSync
- const rootBuiltinRules = Symbol('root-builtin-rules')
- const packageNecessaryRules = Symbol('package-necessary-rules')
- const path = require('path')
- const defaultRules = [
- '.npmignore',
- '.gitignore',
- '**/.git',
- '**/.svn',
- '**/.hg',
- '**/CVS',
- '**/.git/**',
- '**/.svn/**',
- '**/.hg/**',
- '**/CVS/**',
- '/.lock-wscript',
- '/.wafpickle-*',
- '/build/config.gypi',
- 'npm-debug.log',
- '**/.npmrc',
- '.*.swp',
- '**/.DS_Store/**',
- '._*',
- '**/._*/**',
- '*.orig',
- '/package-lock.json',
- '/yarn.lock',
- 'archived-packages/**',
- 'core',
- '!core/',
- '!**/core/',
- '*.core',
- '*.vgcore',
- 'vgcore.*',
- 'core.+([0-9])',
- ]
- // a decorator that applies our custom rules to an ignore walker
- const npmWalker = Class => class Walker extends Class {
- constructor (opt) {
- opt = opt || {}
- // the order in which rules are applied.
- opt.ignoreFiles = [
- rootBuiltinRules,
- 'package.json',
- '.npmignore',
- '.gitignore',
- packageNecessaryRules
- ]
- opt.includeEmpty = false
- opt.path = opt.path || process.cwd()
- const dirName = path.basename(opt.path)
- const parentName = path.basename(path.dirname(opt.path))
- opt.follow =
- dirName === 'node_modules' ||
- (parentName === 'node_modules' && /^@/.test(dirName))
- super(opt)
- // ignore a bunch of things by default at the root level.
- // also ignore anything in node_modules, except bundled dependencies
- if (!this.parent) {
- this.bundled = opt.bundled || []
- this.bundledScopes = Array.from(new Set(
- this.bundled.filter(f => /^@/.test(f))
- .map(f => f.split('/')[0])))
- const rules = defaultRules.join('\n') + '\n'
- this.packageJsonCache = opt.packageJsonCache || new Map()
- super.onReadIgnoreFile(rootBuiltinRules, rules, _=>_)
- } else {
- this.bundled = []
- this.bundledScopes = []
- this.packageJsonCache = this.parent.packageJsonCache
- }
- }
- filterEntry (entry, partial) {
- // get the partial path from the root of the walk
- const p = this.path.substr(this.root.length + 1)
- const pkgre = /^node_modules\/(@[^\/]+\/?[^\/]+|[^\/]+)(\/.*)?$/
- const isRoot = !this.parent
- const pkg = isRoot && pkgre.test(entry) ?
- entry.replace(pkgre, '$1') : null
- const rootNM = isRoot && entry === 'node_modules'
- const rootPJ = isRoot && entry === 'package.json'
- return (
- // if we're in a bundled package, check with the parent.
- /^node_modules($|\/)/i.test(p) ? this.parent.filterEntry(
- this.basename + '/' + entry, partial)
- // if package is bundled, all files included
- // also include @scope dirs for bundled scoped deps
- // they'll be ignored if no files end up in them.
- // However, this only matters if we're in the root.
- // node_modules folders elsewhere, like lib/node_modules,
- // should be included normally unless ignored.
- : pkg ? -1 !== this.bundled.indexOf(pkg) ||
- -1 !== this.bundledScopes.indexOf(pkg)
- // only walk top node_modules if we want to bundle something
- : rootNM ? !!this.bundled.length
- // always include package.json at the root.
- : rootPJ ? true
- // otherwise, follow ignore-walk's logic
- : super.filterEntry(entry, partial)
- )
- }
- filterEntries () {
- if (this.ignoreRules['package.json'])
- this.ignoreRules['.gitignore'] = this.ignoreRules['.npmignore'] = null
- else if (this.ignoreRules['.npmignore'])
- this.ignoreRules['.gitignore'] = null
- this.filterEntries = super.filterEntries
- super.filterEntries()
- }
- addIgnoreFile (file, then) {
- const ig = path.resolve(this.path, file)
- if (this.packageJsonCache.has(ig))
- this.onPackageJson(ig, this.packageJsonCache.get(ig), then)
- else
- super.addIgnoreFile(file, then)
- }
- onPackageJson (ig, pkg, then) {
- this.packageJsonCache.set(ig, pkg)
- // if there's a bin, browser or main, make sure we don't ignore it
- // also, don't ignore the package.json itself!
- const rules = [
- pkg.browser ? '!' + pkg.browser : '',
- pkg.main ? '!' + pkg.main : '',
- '!package.json',
- '!@(readme|copying|license|licence|notice|changes|changelog|history){,.*[^~$]}'
- ]
- if (pkg.bin)
- if (typeof pkg.bin === "object")
- for (const key in pkg.bin)
- rules.push('!' + pkg.bin[key])
- else
- rules.push('!' + pkg.bin)
- const data = rules.filter(f => f).join('\n') + '\n'
- super.onReadIgnoreFile(packageNecessaryRules, data, _=>_)
- if (Array.isArray(pkg.files))
- super.onReadIgnoreFile('package.json', '*\n' + pkg.files.map(
- f => '!' + f + '\n!' + f.replace(/\/+$/, '') + '/**'
- ).join('\n') + '\n', then)
- else
- then()
- }
- // override parent onstat function to nix all symlinks
- onstat (st, entry, file, dir, then) {
- if (st.isSymbolicLink())
- then()
- else
- super.onstat(st, entry, file, dir, then)
- }
- onReadIgnoreFile (file, data, then) {
- if (file === 'package.json')
- try {
- const ig = path.resolve(this.path, file)
- this.onPackageJson(ig, JSON.parse(data), then)
- } catch (er) {
- // ignore package.json files that are not json
- then()
- }
- else
- super.onReadIgnoreFile(file, data, then)
- }
- sort (a, b) {
- return sort(a, b)
- }
- }
- class Walker extends npmWalker(IgnoreWalker) {
- walker (entry, then) {
- new Walker(this.walkerOpt(entry)).on('done', then).start()
- }
- }
- class WalkerSync extends npmWalker(IgnoreWalkerSync) {
- walker (entry, then) {
- new WalkerSync(this.walkerOpt(entry)).start()
- then()
- }
- }
- const walk = (options, callback) => {
- options = options || {}
- const p = new Promise((resolve, reject) => {
- const bw = new BundleWalker(options)
- bw.on('done', bundled => {
- options.bundled = bundled
- options.packageJsonCache = bw.packageJsonCache
- new Walker(options).on('done', resolve).on('error', reject).start()
- })
- bw.start()
- })
- return callback ? p.then(res => callback(null, res), callback) : p
- }
- const walkSync = options => {
- options = options || {}
- const bw = new BundleWalkerSync(options).start()
- options.bundled = bw.result
- options.packageJsonCache = bw.packageJsonCache
- const walker = new WalkerSync(options)
- walker.start()
- return walker.result
- }
- // package.json first, node_modules last, files before folders, alphasort
- const sort = (a, b) =>
- a === 'package.json' ? -1
- : b === 'package.json' ? 1
- : /^node_modules/.test(a) && !/^node_modules/.test(b) ? 1
- : /^node_modules/.test(b) && !/^node_modules/.test(a) ? -1
- : path.dirname(a) === '.' && path.dirname(b) !== '.' ? -1
- : path.dirname(b) === '.' && path.dirname(a) !== '.' ? 1
- : a.localeCompare(b)
- module.exports = walk
- walk.sync = walkSync
- walk.Walker = Walker
- walk.WalkerSync = WalkerSync
|