UNPKG

5.76 kBJavaScriptView Raw
1/* jshint node: true */
2'use strict';
3
4var async = require('async');
5var fs = require('fs');
6var path = require('path');
7var util = require('util');
8var _ = require('underscore');
9
10var zip = require('node-zip');
11
12/*
13 * Given a single buffer or string, create a ZIP file containing it
14 * and return it as a Buffer.
15 */
16module.exports.makeOneFileZip = function(dirName, entryName, contents) {
17 var arch = new zip();
18 if (dirName) {
19 arch.folder(dirName).file(entryName, contents);
20 } else {
21 arch.file(entryName, contents);
22 }
23 return arch.generate({ type: 'nodebuffer', compression: 'DEFLATE' });
24};
25
26/*
27 * Turn a whole directory into a ZIP, with entry names relative to the
28 * original directory root. Do it asynchronously and return the result in the
29 * standard (err, result) callback format.
30 */
31module.exports.zipDirectory = function(dirName, prefix, cb) {
32 if (typeof prefix === 'function') {
33 cb = prefix;
34 prefix = undefined;
35 }
36
37 var pf = (prefix ? prefix : '.');
38 var arch = new zip();
39 zipDirectory(arch, dirName, pf, function(err) {
40 if (err) {
41 cb(err);
42 } else {
43 var contents =
44 arch.generate({ type: 'nodebuffer', compression: 'DEFLATE' });
45 cb(undefined, contents);
46 }
47 });
48};
49
50function zipDirectory(baseArch, baseName, entryName, done) {
51 // Create an object that is now relative to the last ZIP entry name
52 var arch = baseArch.folder(entryName);
53
54 fs.readdir(baseName, function(err, files) {
55 if (err) {
56 done(err);
57 return;
58 }
59
60 // Iterate over each file in the directory
61 async.eachSeries(files, function(fileName, itemDone) {
62 var fn = path.join(baseName, fileName);
63 fs.stat(fn, function(err, stat) {
64 if (err) {
65 itemDone(err);
66 } else if (stat.isDirectory()) {
67 // Recursively ZIP additional directories
68 zipDirectory(arch, fn, fileName, itemDone);
69
70 } else if (stat.isFile()) {
71 // Aysynchronously read the file and store it in the ZIP.
72 fs.readFile(fn, function(err, contents) {
73 if (err) {
74 itemDone(err);
75 } else {
76 arch.file(fileName, contents);
77 itemDone();
78 }
79 });
80 }
81 });
82 }, function(err) {
83 done(err);
84 });
85 });
86}
87
88/*
89 * Given a base directory, produce a list of entries. Each entry has a name,
90 * a fully-qualified path, and whether it is a directory. The base is
91 * assumed to be a "resources" directory, and the "node" resources folder,
92 * if present, will be treated specially using "enumerateDirectory" below.
93 */
94module.exports.enumerateResourceDirectory = function(baseDir, remoteNpm) {
95 var fileList = [];
96 var types = fs.readdirSync(baseDir);
97 _.each(types, function(type) {
98 var fullName = path.join(baseDir, type);
99 var stat = fs.statSync(fullName);
100 if (stat.isDirectory()) {
101 visitDirectory(fullName, fileList, '', '',
102 type, (type === 'node' || type === 'hosted'), remoteNpm);
103 }
104 });
105 return fileList;
106};
107
108/*
109 * Given a base directory, produce a list of entries. Each entry has a name,
110 * a fully-qualified path, and whether it is a directory. This method has
111 * special handling for the "node_modules" directory, which it will introspect
112 * one level deeper. The result can be used to ZIP up a node.js "resources/node"
113 * directory.
114 */
115module.exports.enumerateDirectory = function(baseDir, resourceType, remoteNpm, cb) {
116 try {
117 var fileList = [];
118 var isNode = (resourceType === 'hosted' || resourceType === 'node');
119
120 visitDirectory(baseDir, fileList, '', '', resourceType, isNode, remoteNpm);
121 cb(undefined, fileList);
122 } catch (err) {
123 cb(err);
124 }
125};
126
127// Examine each file in the directory and produce a result that may be
128// used for resource uploading later.
129// Skip special files like symbolic links -- we can't handle them.
130function visitDirectory(dn, fileList, resourceNamePrefix,
131 zipEntryPrefix, resourceType, isNode, remoteNpm) {
132
133 function addResource(resource) {
134 fileList.forEach(function(element) {
135 if (element.resourceName === resource.resourceName) {
136 throw new Error(
137 util.format('File "%s" conflicts with generated resource. Please remove and try again.', resource.fileName));
138 }
139 });
140 fileList.push(resource);
141 }
142
143 var files = fs.readdirSync(dn);
144 _.each(files, function(file) {
145 var fullName = path.join(dn, file);
146 var stat = fs.statSync(fullName);
147 if (stat.isFile()) {
148 addResource({
149 fileName: fullName,
150 resourceType: resourceType,
151 resourceName: resourceNamePrefix + file,
152 zipEntryName: zipEntryPrefix + file
153 });
154
155 } else if (stat.isDirectory()) {
156 if (isNode && (file === 'node_modules')) {
157 if (remoteNpm !== true) {
158 // Special treatment for node_modules, to break them up into lots of smaller zips.
159 // That is unless we are running NPM remotely.
160 visitDirectory(fullName, fileList, 'node_modules_',
161 'node_modules/', resourceType, false, remoteNpm);
162 }
163 } else if (!/^\..+/.test(file)) { // Don't push directories that start with a "." like ".git"!
164
165 if (resourceNamePrefix !== 'node_modules_') {
166 var prefix = resourceNamePrefix + file + '/';
167 visitDirectory(fullName, fileList, prefix, '', resourceType, false, remoteNpm);
168 } else {
169 addResource({
170 fileName: fullName,
171 resourceType: resourceType,
172 resourceName: resourceNamePrefix + file + '.zip',
173 zipEntryName: zipEntryPrefix + file,
174 directory: true });
175 }
176 }
177 }
178 });
179 return fileList;
180}