Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

broccoli-merge-trees

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

broccoli-merge-trees - npm Package Compare versions

Comparing version 0.1.4 to 0.2.0

#foo.js#

5

CHANGELOG.md
# master
# 0.2.0
* Use symlinks as a performance optimization (see
[symlink-change.md](https://github.com/broccolijs/broccoli/blob/master/docs/symlink-change.md))
# 0.1.4

@@ -4,0 +9,0 @@

174

index.js
var fs = require('fs')
var walkSync = require('walk-sync')
var path = require('path')
var Writer = require('broccoli-writer')
var helpers = require('broccoli-kitchen-sink-helpers')
var symlinkOrCopySync = require('symlink-or-copy').sync
var mapSeries = require('promise-map-series')
var isWindows = process.platform === 'win32'

@@ -22,52 +23,141 @@ module.exports = TreeMerger

var self = this
var files = {}
var directories = {}
return mapSeries(this.inputTrees, readTree).then(function (treePaths) {
for (var i = treePaths.length - 1; i >= 0; i--) {
var treeContents = walkSync(treePaths[i])
var fileIndex
for (var j = 0; j < treeContents.length; j++) {
var relativePath = treeContents[j]
var destPath = destDir + '/' + relativePath
if (relativePath.slice(-1) === '/') { // is directory
relativePath = relativePath.slice(0, -1) // chomp "/"
fileIndex = files[relativePath]
if (fileIndex != null) {
throwFileAndDirectoryCollision(relativePath, fileIndex, i)
mergeRelativePath('')
function mergeRelativePath (baseDir, possibleIndices) {
// baseDir has a trailing path.sep if non-empty
var i, j, fileName, fullPath
// Array of readdir arrays
var names = treePaths.map(function (treePath, i) {
if (possibleIndices == null || possibleIndices.indexOf(i) !== -1) {
return fs.readdirSync(treePath + path.sep + baseDir).sort()
} else {
return []
}
})
// Guard against conflicting capitalizations
var lowerCaseNames = {}
for (i = 0; i < treePaths.length; i++) {
for (j = 0; j < names[i].length; j++) {
fileName = names[i][j]
var lowerCaseName = fileName.toLowerCase()
if (lowerCaseNames[lowerCaseName] == null) {
lowerCaseNames[lowerCaseName] = {
index: i,
originalName: fileName
}
} else {
var originalIndex = lowerCaseNames[lowerCaseName].index
var originalName = lowerCaseNames[lowerCaseName].originalName
if (originalName !== fileName) {
throw new Error('Merge error: conflicting capitalizations:\n'
+ baseDir + originalName + ' in ' + treePaths[originalIndex] + '\n'
+ baseDir + fileName + ' in ' + treePaths[i] + '\n'
+ 'Remove one of the files and re-add it with matching capitalization.\n'
+ 'We are strict about this to avoid divergent behavior '
+ 'between case-insensitive Mac/Windows and case-sensitive Linux.'
)
}
}
if (directories[relativePath] == null) {
fs.mkdirSync(destPath)
directories[relativePath] = i
}
} else { // is file
var directoryIndex = directories[relativePath]
if (directoryIndex != null) {
throwFileAndDirectoryCollision(relativePath, i, directoryIndex)
}
fileIndex = files[relativePath.toLowerCase()]
if (fileIndex != null) {
if (!self.options.overwrite) {
throw new Error('Merge error: ' +
'file "' + relativePath + '" exists in ' +
treePaths[i] + ' and ' + treePaths[fileIndex] + ' - ' +
'pass option { overwrite: true } to mergeTrees in order ' +
'to have the latter file win')
}
}
// From here on out, no files and directories exist with conflicting
// capitalizations, which means we can use `===` without .toLowerCase
// normalization.
// Accumulate fileInfo hashes of { isDirectory, indices }.
// Also guard against conflicting file types and overwriting.
var fileInfo = {}
for (i = 0; i < treePaths.length; i++) {
for (j = 0; j < names[i].length; j++) {
fileName = names[i][j]
fullPath = treePaths[i] + path.sep + baseDir + fileName
var isDirectory = checkIsDirectory(fullPath)
if (fileInfo[fileName] == null) {
fileInfo[fileName] = {
isDirectory: isDirectory,
indices: [i] // indices into treePaths in which this file exists
}
// Else, ignore this file. It is "overwritten" by a file we copied
// earlier, thanks to reverse iteration over trees
} else {
helpers.copyPreserveSync(
treePaths[i] + '/' + relativePath, destPath)
files[relativePath.toLowerCase()] = i
fileInfo[fileName].indices.push(i)
// Guard against conflicting file types
var originallyDirectory = fileInfo[fileName].isDirectory
if (originallyDirectory !== isDirectory) {
throw new Error('Merge error: conflicting file types: ' + baseDir + fileName
+ ' is a ' + (originallyDirectory ? 'directory' : 'file')
+ ' in ' + treePaths[fileInfo[fileName].indices[0]]
+ ' but a ' + (isDirectory ? 'directory' : 'file')
+ ' in ' + treePaths[i] + '\n'
+ 'Remove or rename either of those.'
)
}
// Guard against overwriting when disabled
if (!isDirectory && !self.options.overwrite) {
throw new Error('Merge error: '
+ 'file ' + baseDir + fileName + ' exists in '
+ treePaths[fileInfo[fileName].indices[0]] + ' and ' + treePaths[i] + '\n'
+ 'Pass option { overwrite: true } to mergeTrees in order '
+ 'to have the latter file win.'
)
}
}
}
}
}
function throwFileAndDirectoryCollision (relativePath, fileIndex, directoryIndex) {
throw new Error('Merge error: "' + relativePath +
'" exists as a file in ' + treePaths[fileIndex] +
' but as a directory in ' + treePaths[directoryIndex])
// Done guarding against all error conditions. Actually merge now.
for (i = 0; i < treePaths.length; i++) {
for (j = 0; j < names[i].length; j++) {
fileName = names[i][j]
fullPath = treePaths[i] + path.sep + baseDir + fileName
var destPath = destDir + path.sep + baseDir + fileName
var infoHash = fileInfo[fileName]
if (infoHash.isDirectory) {
if (isWindows || infoHash.indices.length > 1) {
// Copy/merge subdirectory
if (infoHash.indices[0] === i) { // avoid duplicate recursion
fs.mkdirSync(destPath)
mergeRelativePath(baseDir + fileName + path.sep, infoHash.indices)
}
} else {
// Symlink entire subdirectory
if (fs.lstatSync(fullPath).isSymbolicLink()) {
// When we encounter symlinks, follow them. This prevents indirection
// from growing out of control. Note: At the moment `realpath` on Node
// is 70x slower than native: https://github.com/joyent/node/issues/7902
fullPath = fs.realpathSync(fullPath)
} else if (fullPath[0] !== path.sep) {
fullPath = process.cwd() + path.sep + fullPath
}
fs.symlinkSync(fullPath, destPath)
}
} else { // isFile
if (infoHash.indices[infoHash.indices.length-1] === i) {
symlinkOrCopySync(fullPath, destPath)
} else {
// This file exists in a later tree. Do nothing here to have the
// later file win out and thus "overwrite" the earlier file.
}
}
}
}
}
})
}
// True if directory, false if file, exception otherwise
function checkIsDirectory (fullPath) {
var stat = fs.statSync(fullPath) // may throw ENOENT on broken symlink
if (stat.isDirectory()) {
return true
} else if (stat.isFile()) {
return false
} else {
throw new Error('Unexpected file type for ' + fullPath)
}
}
{
"name": "broccoli-merge-trees",
"description": "Broccoli plugin to merge multiple trees into one",
"version": "0.1.4",
"version": "0.2.0",
"author": "Jo Liss <joliss42@gmail.com>",

@@ -19,12 +19,12 @@ "main": "index.js",

"promise-map-series": "^0.2.0",
"walk-sync": "^0.1.2",
"broccoli-writer": "^0.1.1",
"broccoli-kitchen-sink-helpers": "^0.2.0"
"symlink-or-copy": "^1.0.0"
},
"devDependencies": {
"tap": "^0.4.8",
"rsvp": "^3.0.6",
"coffee-script": "~1.7.1",
"fixturify": "^0.1.1",
"jshint": "~2.5.0",
"coffee-script": "~1.7.1"
"ncp": "^0.6.0",
"rsvp": "^3.0.6",
"tap": "^0.4.8"
},

@@ -31,0 +31,0 @@ "scripts": {

# broccoli-merge-trees
[![Build Status](https://travis-ci.org/joliss/broccoli-merge-trees.png?branch=master)](https://travis-ci.org/joliss/broccoli-merge-trees)
[![Build Status](https://travis-ci.org/broccolijs/broccoli-merge-trees.png?branch=master)](https://travis-ci.org/broccolijs/broccoli-merge-trees)

@@ -5,0 +5,0 @@ Copy multiple trees on top of each other, resulting in a single merged tree.

var Writer = require('broccoli-writer')
var RSVP = require('rsvp')
var Promise = require('rsvp').Promise
var ncp = require('ncp')
var fixturify = require('fixturify')

@@ -61,3 +63,3 @@

function FixtureTree (fixtureObject) {
if (!(this instanceof FixtureTree)) return new FixtureTree(fixtureObject);
if (!(this instanceof FixtureTree)) return new FixtureTree(fixtureObject)
this.fixtureObject = fixtureObject

@@ -69,1 +71,19 @@ }

}
exports.dereferenceSymlinks = SymlinkDereferencer
SymlinkDereferencer.prototype = Object.create(Writer.prototype)
SymlinkDereferencer.prototype.constructor = SymlinkDereferencer
function SymlinkDereferencer (inputTree) {
if (!(this instanceof SymlinkDereferencer)) return new SymlinkDereferencer(inputTree)
this.inputTree = inputTree
}
SymlinkDereferencer.prototype.write = function (readTree, destDir) {
return readTree(this.inputTree)
.then(function (srcDir) {
return RSVP.denodeify(ncp)(srcDir, destDir, {
dereference: true
})
})
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc