Codebase list node-dryice / 66f7c26
Imported Upstream version 0.4.10 Leo Iannacone 10 years ago
5 changed file(s) with 1910 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176 END OF TERMS AND CONDITIONS
177
178 APPENDIX: How to apply the Apache License to your work.
179
180 To apply the Apache License to your work, attach the following
181 boilerplate notice, with the fields enclosed by brackets "[]"
182 replaced with your own identifying information. (Don't include
183 the brackets!) The text should be enclosed in the appropriate
184 comment syntax for the file format. We also recommend that a
185 file or class name and description of purpose be included on the
186 same "printed page" as the copyright notice for easier
187 identification within third-party archives.
188
189 Copyright [yyyy] [name of copyright owner]
190
191 Licensed under the Apache License, Version 2.0 (the "License");
192 you may not use this file except in compliance with the License.
193 You may obtain a copy of the License at
194
195 http://www.apache.org/licenses/LICENSE-2.0
196
197 Unless required by applicable law or agreed to in writing, software
198 distributed under the License is distributed on an "AS IS" BASIS,
199 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 See the License for the specific language governing permissions and
201 limitations under the License.
0 DryIce
1 ======
2
3 DryIce is a CommonJS/RequireJS packaging tool for browser scripts.
4
5 It is basically just a copy function. It takes input from a set of input files,
6 which can be specified in various ways, optionally filters them and outputs them
7 to something else.
8
9 DryIce is licensed under the Apache License version 2
10
11
12 Why?
13 ----
14
15 RequireJS has a build tool which is nice and works well, but it requires Rhino
16 and therefore Java. With DryIce, your whole build process can be in JavaScript.
17
18 DryIce produces a single file output that can include binary files (by base64
19 encoding them)
20
21
22 How to install DryIce
23 ---------------------
24
25 sudo npm install dryice
26
27
28 How does it work?
29 -----------------
30
31 To copy a single file:
32
33 copy({
34 source: 'foo.txt',
35 dest: 'bar.txt'
36 });
37
38 To cat a bunch of files together:
39
40 copy({
41 source: [ 'file1.js', 'file2.js' ],
42 dest: 'output.js'
43 });
44
45 To cat together all the files in a directory:
46
47 copy({
48 source: { root:'src' },
49 dest: 'built.js'
50 });
51
52 As above, but only use the JavaScript files:
53
54 copy({
55 source: { root:'src', include:/.*\.js$/ },
56 dest: 'built.js'
57 });
58
59 As above, but exclude tests:
60
61 copy({
62 source: { root:'src', include:/.*\.js$/: exclude:/test/ },
63 dest: 'built.js'
64 });
65
66 If your set of files is very custom:
67
68 copy({
69 source: function() {
70 var files = [ 'file1.js' ];
71 if (baz) files.push('file2.js');
72 return files;
73 },
74 dest: 'built.js'
75 });
76
77 We can filter the files on the way:
78
79 copy({
80 source: /src/.*\.js$/,
81 filter: copy.filter.uglifyjs,
82 dest: 'built.js'
83 });
84
85 This includes running multiple custom filters:
86
87 copy({
88 source: 'src/index.html',
89 filter: [
90 function(data) {
91 return data.replace(/Sun/, 'Oracle');
92 },
93 htmlCompressor
94 ],
95 dest: 'war/index.html'
96 });
97
98 Results can be stored and then used/reused:
99
100 var sources = copy.createDataObject();
101 copy({
102 source: { root: 'src1' },
103 dest: sources
104 });
105 copy({
106 source: { root: 'src2' },
107 dest: sources
108 });
109 copy({
110 source: sources,
111 dest: 'sources-uncompressed.js'
112 });
113 copy({
114 source: sources,
115 filter: copy.filter.uglifyjs,
116 dest: 'sources.js'
117 });
118
119 Data objects are just JS objects with a 'value' member, so you can do all sorts
120 of things with them:
121
122 var test = copy.createDataObject();
123 copy({
124 source: 'README.txt',
125 dest: test
126 });
127 console.log(test.value);
128
129 Or:
130
131 copy({
132 source: { value: 'Hello, World!' },
133 dest: 'basic.txt'
134 });
135
136 And you can mix and match your inputs:
137
138 copy({
139 source: [
140 'somefile.txt',
141 thingDataObject,
142 { root: 'src', include: /.*\.js$/ },
143 function() { return 'wibble.sh'; }
144 ],
145 dest: 'mess.bin'
146 });
147
148 Common JS project dependency tracking:
149
150 var project = copy.createCommonJsProject({
151 roots: [
152 '/path/to/source/tree/lib',
153 '/some/other/project/lib'
154 ]
155 });
156 copy({
157 source: copy.source.commonjs({
158 project: project,
159 require: [ 'main', 'plugin/main' ]
160 }),
161 dest: ''
162 });
163
164 This digs around in the project source trees specified in the project for
165 modules named in the 'require' statement. When it finds them it looks through
166 them for require statements, and finds those, and so on.
167
168
169 Formal Parameter Description
170 ----------------------------
171
172 The copy function takes a single parameter which is an object with 2 or 3
173 members: `source`, `dest` and optionally `filter`.
174
175 ### source
176
177 There are 6 ways to specify the input source(s)
178
179 * A *string* is expected to point to a filename.
180 At some stage we may allow them to point at directories too, however this
181 can be achieved today using a find object (see below)
182
183 * A *find object* points to a directory with 2 optional RegExps specifying what
184 to exclude and include. e.g.
185
186 { root: '/' } -> The entire filesystem
187 { root: 'src', include: /.*\.js$/ } -> All the JavaScript files in 'src'
188 { root: 'src', exclude: /test/ } -> All non-test files under 'src'
189
190 * A *data object* - something with a 'value' property.
191 The implementation of `copy.createDataObject()` is simply
192 `return { value: '' };`. We've batted around some ideas which involve making
193 `copy.createDataObject()` smarter than it currently is, so it is advised to
194 use this method rather than doing it yourself.
195
196 * A *based object*. A based object is one with `base` and `path` members. They
197 are roughly the same as the string baseObj.base + baseObj.path. Based objects
198 are important when using CommonJS filters, because it tells the filter where
199 the root of the hierarchy is, which lets us know the module name.
200 For example:
201
202 { base: '/etc', path:PATH } where BASE+PATH = filename
203
204 * An *array* containing input source entries. The array does not have to be
205 homogeneous.
206
207 * A *function* which returns any input source entries.
208
209 ### filter
210
211 The filter member is optional. If it exists, it should contain either a function
212 or an array of functions. The function should have the following signature:
213
214 function filter(value, location) {
215 ..
216 return 'some string';
217 }
218
219 Where the parameters are as follows:
220
221 * value. Either a string or a node Buffer. Most filters will work only with
222 strings, so they should begin:
223
224 if (typeof value !== 'string') {
225 value = value.toString();
226 }
227
228 Some filters will only work with Buffers (for example the base64 encoding
229 filter) so they should begin:
230
231 if (typeof value === 'string') {
232 throw new Error('base64 filter needs to be the first in a filter set');
233 }
234
235 At some stage we may allow filters to be marked up as to their requirements.
236
237 * location. This will be (where possible) a based object or it could be a
238 string if a based object is not available. It will be common to use one of the
239 following idioms to work on a filename:
240
241 if (location.base) {
242 location = location.path;
243 }
244
245 or
246
247 if (location.base) {
248 location = location.base + location.path;
249 }
250
251 There are 2 points in a copy run where filters could be used, either before the
252 individual sources are concatenated, or after. Some filters should be used in
253 before (like common-js munging filters) and some afterwards (like compressors).
254
255 The default is to run filters after concatenation (when the location parameter
256 will be undefined). To run filters before concatenation, the filter should be
257 marked with `onRead = true`. For example:
258
259 function makeBlank(value, location) {
260 return '';
261 }
262 makeBlank.onRead = true;
263
264 DryIce currently comes with 4 filters:
265
266 * _copy.filter.uglifyjs_: Calls uglify on the input.
267 * _copy.filter.addDefines_: Wraps the input to inline files fetched using
268 RequireJSs text import feature.
269 * _copy.filter.base64_: Similar to addDefines, but assumes the input is
270 binary and should be base64 encoded.
271 * _copy.filter.moduleDefines_: Replaces define lines to include the module name
272 e.g. `define(function(export, require, module) { ... });` is turned into
273 `define('module/name', function(export, require, module) { ... });`
274
275
276 ### dest
277
278 The dest property should be either a filename to which the output should be
279 written (existing files will be over-written without warning), or a data object
280 to which the data should be appended.
281
282 CommonJS Projects
283 -----------------
284
285 CommonJS projects take a single object with the following properties:
286
287 * `roots`: This is required. An array of directories that should be searched for
288 your required modules and dependencies.
289
290 * `ignores`: This is optional. An array of modules or dependencies that are
291 required by your project that you would not like to be included in the
292 build. For example, if you were making a build which did not need to support
293 IE, you could do something like the following
294
295 copy.createCommonJsProject({
296 roots: [ '/path/to/project' ],
297 ignores: [ 'dom/ie-compat', 'event/ie-compat' ]
298 });
299
300 then wherever you had `require('dom/ie-compat')` or
301 `require('event/ie-compat')` inside your build, `undefined` would be returned
302 by `require`.
303
304 Where (is the project going)?
305 -----------------------------
306
307 DryIce is useful in combining scripts for the browser, but it could also be
308 used in a similar role on the server, we just need to enable 'pass through
309 requires'.
310
311 There are some tweaks we'd still like to make to enable more filters and
312 multiple destinations:
313
314 To recursively copy a directory:
315
316 copy({ source: 'foo', destDir: 'bar' });
317
318 To rename files as we copy them:
319
320 copy({
321 source: { root:'src', include:/.*\.png$/ },
322 destDir: { root:'built', replace:/png$/, with:'png.bak' }
323 });
324
325 To create a tarball (this is only missing the targz filter):
326
327 var version = copy.createDataObject();
328 copy({ source: 'VERSION.txt', dest: version });
329 copy({
330 source: { root:'.' },
331 filter: [ targz ],
332 dest: 'scp://example.com/upload/myproject-' + version + '.tar.gz'
333 });
334
335 I don't suppose you would ever actually want to do this, but in theory you
336 could even do this:
337
338 copy({
339 source: { root:'src', include:/.*\.java$/ },
340 filter: javac,
341 destDir: { root:'classes', replace:/java$/, with:'class' }
342 });
343
344 (Actually there would be issues with ordering that would make this hard, and
345 Ant/Maven/etc is probably better. This is an illustration dammit!)
0 /*
1 * Copyright 2012, Mozilla Foundation and contributors
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 var fs = require("fs");
17 var path = require("path");
18 var ujs = require("uglify-js");
19
20 if (!fs.existsSync) {
21 fs.existsSync = path.existsSync;
22 }
23
24 /**
25 * See https://github.com/mozilla/dryice for usage instructions.
26 */
27 function copy(obj) {
28 var filters = copy.filterFactory(obj.filter);
29 var source = copy.sourceFactory(obj.source, filters);
30 var dest = copy.destFactory(obj.dest, filters);
31 dest.processSource(source);
32 }
33
34 /**
35 * A Location is a base and a path which together point to a file or directory.
36 * It's useful to be able to know in copy operations relative to some project
37 * root to be able to remember where in a destination the file should go
38 */
39 function Location(base, somePath) {
40 if (base == null) {
41 throw new Error('base == null');
42 }
43 this.base = base;
44 this.path = somePath;
45 }
46
47 Location.prototype.isLocation = true;
48
49 Object.defineProperty(Location.prototype, 'fullname', {
50 get: function() {
51 return path.join(this.base, this.path);
52 }
53 });
54
55 Object.defineProperty(Location.prototype, 'dirname', {
56 get: function() {
57 return path.dirname(this.fullname);
58 }
59 });
60
61 /**
62 * Select the correct implementation of Source for the given source property
63 */
64 copy.sourceFactory = function(source, filters) {
65 if (source == null) {
66 throw new Error('Missing source');
67 }
68
69 if (source.isSource) {
70 return source;
71 }
72
73 if (typeof source === 'string') {
74 if (copy.isDirectory(source)) {
75 return new copy.DirectorySource(source, null, null, filters);
76 }
77 else {
78 return new copy.FileSource(new Location('', source), filters);
79 }
80 }
81
82 if (Array.isArray(source)) {
83 return new copy.ArraySource(source, filters);
84 }
85
86 if (typeof source === 'function') {
87 return new copy.FunctionSource(source, filters);
88 }
89
90 if (source.root != null) {
91 if (source.require != null) {
92 var project = new CommonJsProject([ source.root ]);
93 return new copy.CommonJsSource(project, source.require, filters);
94 }
95
96 return new copy.DirectorySource(source.root, source.include, source.exclude, filters);
97 }
98
99 if (source.base != null && source.path != null) {
100 return new copy.FileSource(new Location(source.base, source.path), filters);
101 }
102
103 if (typeof source.value === 'string') {
104 return new copy.ValueSource(source.value, null, filters);
105 }
106
107 if (source.project != null && source.require != null) {
108 return new copy.CommonJsSource(source.project, source.require, filters);
109 }
110
111 throw new Error('Can\'t handle type of source: ' + typeof source);
112 };
113
114 copy.debug = false;
115
116 /**
117 * Abstract Source.
118 * Concrete implementations of Source should define the 'get' property.
119 */
120 copy.Source = function() {
121 };
122
123 /**
124 * @return Either another source, an array of other sources or a string value
125 * when there is nothing else to dig into
126 */
127 Object.defineProperty(copy.Source.prototype, 'get', {
128 get: function() {
129 throw new Error('Source.get is not implemented');
130 }
131 });
132
133 copy.Source.prototype.isSource = true;
134
135 copy.Source.prototype._runFilters = function(value, location) {
136 this._filters.forEach(function(filter) {
137 if (filter.onRead) {
138 value = filter(value, location);
139 }
140 }, this);
141 return value;
142 };
143
144 /**
145 * Default encoding for all sources
146 */
147 copy.Source.prototype.encoding = 'utf8';
148
149 /**
150 * An ArraySource is simply an array containing things that can resolve to
151 * implementations of Source when passed to copy.sourceFactory()
152 */
153 copy.ArraySource = function(array, filters) {
154 copy.Source.call(this);
155 this._array = array;
156 this._filters = filters;
157 };
158
159 copy.ArraySource.prototype = Object.create(copy.Source.prototype);
160
161 Object.defineProperty(copy.ArraySource.prototype, 'get', {
162 get: function() {
163 return this._array.map(function(member) {
164 return copy.sourceFactory(member, this._filters);
165 }, this);
166 }
167 });
168
169 /**
170 * A FunctionSource is something that can be called to resolve to another
171 * Source implementation
172 */
173 copy.FunctionSource = function(func, filters) {
174 copy.Source.call(this);
175 this._func = func;
176 this._filters = filters;
177 };
178
179 copy.FunctionSource.prototype = Object.create(copy.Source.prototype);
180
181 Object.defineProperty(copy.FunctionSource.prototype, 'get', {
182 get: function() {
183 return copy.sourceFactory(this._func(), this._filters);
184 }
185 });
186
187 /**
188 * A Source that finds files under a given directory with specified include /
189 * exclude patterns.
190 * @param root The root in the filesystem under which the files exist
191 * @param filterOrInclude
192 */
193 copy.DirectorySource = function(root, filterOrInclude, exclude, filters) {
194 copy.Source.call(this);
195 this._filters = filters;
196
197 this.root = root;
198 if (this.root instanceof CommonJsProject) {
199 this.root = this.root.roots;
200 }
201
202 if (Array.isArray(this.root)) {
203 this.root.map(function(r) {
204 return ensureTrailingSlash(r);
205 });
206 }
207
208 if (typeof filterOrInclude === 'function') {
209 this._searchFilter = filterOrInclude;
210 }
211 else {
212 this._searchFilter = this._createFilter(filterOrInclude, exclude);
213 }
214 };
215
216 copy.DirectorySource.prototype = Object.create(copy.Source.prototype);
217
218 Object.defineProperty(copy.DirectorySource.prototype, 'get', {
219 get: function() {
220 return this._findMatches(this.root, '/');
221 }
222 });
223
224 copy.DirectorySource.prototype._findMatches = function(root, path) {
225 var sources = [];
226
227 if (Array.isArray(root)) {
228 root.forEach(function(r) {
229 var matches = this._findMatches(r, path);
230 sources.push.apply(sources, matches);
231 }, this);
232 return sources;
233 }
234
235 root = ensureTrailingSlash(root);
236 path = ensureTrailingSlash(path);
237
238 if (copy.isDirectory(root + path)) {
239 fs.readdirSync(root + path).forEach(function(entry) {
240 var stat = fs.statSync(root + path + entry);
241 if (stat.isFile()) {
242 if (this._searchFilter(path + entry)) {
243 var location = new Location(root, path + entry);
244 sources.push(new copy.FileSource(location, this._filters));
245 }
246 }
247 else if (stat.isDirectory()) {
248 var matches = this._findMatches(root, path + entry);
249 sources.push.apply(sources, matches);
250 }
251 }, this);
252 }
253
254 return sources;
255 };
256
257 copy.DirectorySource.prototype._createFilter = function(include, exclude) {
258 return function(pathname) {
259 function noPathMatch(pattern) {
260 return !pattern.test(pathname);
261 }
262 if (include instanceof RegExp) {
263 if (noPathMatch(include)) {
264 return false;
265 }
266 }
267 if (typeof include === 'string') {
268 if (noPathMatch(new RegExp(include))) {
269 return false;
270 }
271 }
272 if (Array.isArray(include)) {
273 if (include.every(noPathMatch)) {
274 return false;
275 }
276 }
277
278 function pathMatch(pattern) {
279 return pattern.test(pathname);
280 }
281 if (exclude instanceof RegExp) {
282 if (pathMatch(exclude)) {
283 return false;
284 }
285 }
286 if (typeof exclude === 'string') {
287 if (pathMatch(new RegExp(exclude))) {
288 return false;
289 }
290 }
291 if (Array.isArray(exclude)) {
292 if (exclude.some(pathMatch)) {
293 return false;
294 }
295 }
296
297 return true;
298 };
299 };
300
301 /**
302 * A FileSource gets data directly from a file. It has 2 parts to the filename,
303 * a base and path members, where filename = base + path.
304 * FileSources are important when using CommonJS filters, because it tells the
305 * filter where the root of the hierarchy is, which lets us know the module
306 * name.
307 * If there is no base to the filename, use a base of ''.
308 */
309 copy.FileSource = function(location, filters) {
310 copy.Source.call(this);
311 this.location = location;
312 this.name = location.fullname;
313 this._filters = filters;
314 };
315
316 copy.FileSource.prototype = Object.create(copy.Source.prototype);
317
318 Object.defineProperty(copy.FileSource.prototype, 'get', {
319 get: function() {
320 var read = fs.readFileSync(this.name);
321 return this._runFilters(read, this.location);
322 }
323 });
324
325 /**
326 *
327 */
328 copy.ValueSource = function(value, location, filters) {
329 copy.Source.call(this);
330 this._value = value;
331 this._location = location;
332 this._filters = filters;
333 };
334
335 copy.ValueSource.prototype = Object.create(copy.Source.prototype);
336
337 Object.defineProperty(copy.ValueSource.prototype, 'get', {
338 get: function() {
339 return this._runFilters(this._value, this._location);
340 }
341 });
342
343 /**
344 * Read modules from a CommonJS Project using a require property.
345 */
346 copy.CommonJsSource = function(project, require, filters) {
347 copy.Source.call(this);
348 this._project = project;
349 this._filters = filters;
350
351 if (!project instanceof CommonJsProject) {
352 throw new Error('commonjs project should be a CommonJsProject');
353 }
354
355 if (typeof require === 'string') {
356 this._require = [ require ];
357 }
358 else if (Array.isArray(require)) {
359 this._require = require;
360 }
361 else {
362 throw new Error('Expected commonjs args to have string/array require.');
363 }
364 };
365
366 copy.CommonJsSource.prototype = Object.create(copy.Source.prototype);
367
368 Object.defineProperty(copy.CommonJsSource.prototype, 'get', {
369 get: function() {
370 this._require.forEach(function(moduleName) {
371 this._project.require(moduleName);
372 }, this);
373 return this._project.getCurrentModules().map(function(location) {
374 return new copy.FileSource(location, this._filters);
375 }.bind(this));
376 }
377 });
378
379
380 ////////////////////////////////////////////////////////////////////////////////
381
382 copy.filterFactory = function(filter) {
383 if (filter == null) {
384 return [];
385 }
386
387 if (typeof filter === 'function') {
388 return [ filter ];
389 }
390
391 if (Array.isArray(filter)) {
392 return filter;
393 }
394 };
395
396
397 ////////////////////////////////////////////////////////////////////////////////
398
399 /**
400 * Select the correct implementation of Destination for the given dest property
401 */
402 copy.destFactory = function(dest, filters) {
403 if (dest == null) {
404 throw new Error('Missing dest');
405 }
406
407 if (dest.isDestination) {
408 return dest;
409 }
410
411 if (dest.value != null) {
412 return new copy.ValueDestination(dest, filters);
413 }
414
415 if (typeof dest === 'string') {
416 if (copy.isDirectory(dest)) {
417 return new copy.DirectoryDestination(dest, filters);
418 }
419 else {
420 return new copy.FileDestination(dest, filters);
421 }
422 }
423
424 if (Array.isArray(dest)) {
425 return new copy.ArrayDestination(dest, filters);
426 }
427
428 throw new Error('Can\'t handle type of dest: ' + typeof dest);
429 };
430
431 /**
432 * Abstract Destination.
433 * Concrete implementations of Destination should define the 'processSource'
434 * function.
435 */
436 copy.Destination = function() {
437 };
438
439 copy.Destination.prototype.isDestination = true;
440
441 /**
442 * @return Either another dest, an array of other sources or a string value
443 * when there is nothing else to dig into
444 */
445 copy.Destination.prototype.processSource = function(source) {
446 throw new Error('Destination.processSource() is not implemented');
447 };
448
449 /**
450 * Helper function to convert an input source to a single string value
451 */
452 copy.Destination.prototype._sourceToOutput = function(source) {
453 var data = source.get;
454
455 if (data.isSource) {
456 return this._sourceToOutput(data);
457 }
458
459 if (Array.isArray(data)) {
460 var value = '';
461 data.forEach(function(s) {
462 value += this._sourceToOutput(s);
463 }, this);
464 return value;
465 }
466
467 if (typeof data === 'string') {
468 return data;
469 }
470
471 // i.e. a Node Buffer
472 if (typeof data.toString === 'function') {
473 return data.toString();
474 }
475
476 throw new Error('Unexpected value from source.get');
477 };
478
479 copy.Destination.prototype._runFilters = function(value) {
480 this._filters.forEach(function(filter) {
481 if (!filter.onRead) {
482 value = filter(value);
483 }
484 }, this);
485 return value;
486 };
487
488 /**
489 * A Destination that concatenates the sources and writes them to a single
490 * output file.
491 */
492 copy.FileDestination = function(filename, filters) {
493 this._filename = filename;
494 this._filters = filters;
495 };
496
497 copy.FileDestination.prototype = Object.create(copy.Destination.prototype);
498
499 copy.FileDestination.prototype.processSource = function(source) {
500 var data = this._sourceToOutput(source);
501 data = this._runFilters(data);
502 copy._writeToFile(this._filename, data);
503 };
504
505 /**
506 * A Destination that copies the sources to new files in an alternate directory
507 * structure.
508 */
509 copy.DirectoryDestination = function(dirname, filters) {
510 this.name = dirname;
511 this._filters = filters;
512 };
513
514 copy.DirectoryDestination.prototype = Object.create(copy.Destination.prototype);
515
516 copy.DirectoryDestination.prototype.processSource = function(source) {
517 var data = source.get;
518 if (typeof data === 'string') {
519 throw new Error('Can\'t write raw data to a directory');
520 }
521 else if (data.isSource) {
522 var destfile = path.join(this.name, data.location.path);
523 var output = this._runFilters(data.get);
524 copy._writeToFile(destfile, output, data.encoding);
525 }
526 else if (Array.isArray(data)) {
527 data.forEach(function(s) {
528 var destfile = path.join(this.name, s.location.path);
529 var output = this._runFilters(s.get);
530 copy._writeToFile(destfile, output, s.encoding);
531 }, this);
532 }
533 else {
534 throw new Error('data is not a source, string, nor can it be converted');
535 }
536 };
537
538 /**
539 * ArrayDestination is a Destination that can feed sources to a number of child
540 * Destinations.
541 */
542 copy.ArrayDestination = function(array, filters) {
543 this._array = array;
544 this._filters = filters;
545 };
546
547 copy.ArrayDestination.prototype = Object.create(copy.Destination.prototype);
548
549 copy.ArrayDestination.prototype.processSource = function(source) {
550 this._array.forEach(function(member) {
551 var dest = copy.destFactory(member, this._filters);
552 dest.processSource(source);
553 }, this);
554 };
555
556 /**
557 * A Destination that concatenates the sources and writes them to a single
558 * value object.
559 */
560 copy.ValueDestination = function(value, filters) {
561 this._value = value;
562 this._filters = filters;
563 };
564
565 copy.ValueDestination.prototype = Object.create(copy.Destination.prototype);
566
567 copy.ValueDestination.prototype.processSource = function(source) {
568 var data = this._sourceToOutput(source);
569 data = this._runFilters(data);
570 this._value.value += data;
571 };
572
573 ////////////////////////////////////////////////////////////////////////////////
574
575 /**
576 * Check to see if fullPath refers to a directory
577 */
578 copy.isDirectory = function(fullPath) {
579 return fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory();
580 };
581
582 copy._writeToFile = function(filename, data, encoding) {
583 if (fs.existsSync(filename)) {
584 if (!fs.statSync(filename).isFile()) {
585 throw new Error('Refusing to remove non file: ' + filename);
586 }
587 fs.unlinkSync(filename);
588 }
589 var parent = path.dirname(filename);
590 if (!fs.existsSync(parent)) {
591 copy.mkdirSync(parent, 0755);
592 }
593 fs.writeFileSync(filename, data, encoding);
594 if (copy.debug) {
595 console.log('- wrote ' + data.length + ' bytes to ' + filename);
596 }
597 };
598
599 copy.mkdirSync = function(dirname, mode) {
600 if (copy.isDirectory(dirname)) {
601 return;
602 }
603 var parent = path.dirname(dirname);
604 if (!fs.existsSync(parent)) {
605 copy.mkdirSync(parent, mode);
606 }
607 if (!fs.existsSync(dirname)) {
608 fs.mkdirSync(dirname, mode);
609 }
610 };
611
612 ////////////////////////////////////////////////////////////////////////////////
613
614 /**
615 * A holder is an in-memory store of a result of a copy operation.
616 * <pre>
617 * var holder = copy.createDataObject();
618 * copy({ source: 'x.txt', dest: holder });
619 * copy({ source: 'y.txt', dest: holder });
620 * copy({ source: holder, dest: 'z.txt' });
621 * </pre>
622 */
623 copy.createDataObject = function() {
624 return { value: '' };
625 };
626
627 /**
628 * Read mini_require.js to go with the required modules.
629 */
630 copy.getMiniRequire = function() {
631 return {
632 value: fs.readFileSync(__dirname + '/mini_require.js').toString('utf8')
633 };
634 };
635
636 /**
637 * Keep track of the files in a project
638 */
639 function CommonJsProject(opts) {
640 this.roots = opts.roots;
641 this.aliases = opts.aliases;
642 this.textPluginPattern = opts.textPluginPattern || /^text!/;
643
644 opts.roots = this.roots.map(function(root) {
645 if (!copy.isDirectory(root)) {
646 throw new Error('Each commonjs root should be a directory: ' + root);
647 }
648 return ensureTrailingSlash(root);
649 }, this);
650
651 // A module is a Location that also has dep
652 this.currentModules = {};
653 this.ignoredModules = {};
654 }
655
656 CommonJsProject.prototype.report = function() {
657 var reply = 'CommonJS project at ' + this.roots.join(', ') + '\n';
658
659 reply += '- Required modules:\n';
660 var moduleNames = Object.keys(this.currentModules);
661 if (moduleNames.length > 0) {
662 moduleNames.forEach(function(module) {
663 var deps = Object.keys(this.currentModules[module].deps).length;
664 reply += ' - ' + module + ' (' + deps +
665 (deps === 1 ? ' dependency' : ' dependencies') + ')\n';
666 }, this);
667 }
668 else {
669 reply += ' - None\n';
670 }
671
672 reply += '- Ignored modules:\n';
673 var ignoredNames = Object.keys(this.ignoredModules);
674 if (ignoredNames.length > 0) {
675 ignoredNames.forEach(function(moduleName) {
676 reply += ' - ' + moduleName + '\n';
677 }, this);
678 }
679 else {
680 reply += ' - None\n';
681 }
682
683 return reply;
684 };
685
686 /**
687 * Create an experimental GraphML string declaring the node dependencies.
688 */
689 CommonJsProject.prototype.getDependencyGraphML = function() {
690 var nodes = '';
691 var edges = '';
692 var moduleNames = Object.keys(this.currentModules);
693 moduleNames.forEach(function(moduleName) {
694 nodes += ' <node id="' + moduleName + '">\n';
695 nodes += ' <data key="d0">\n';
696 nodes += ' <y:ShapeNode>\n';
697 nodes += ' <y:NodeLabel textColor="#000000">' + moduleName + '</y:NodeLabel>\n';
698 nodes += ' </y:ShapeNode>\n';
699 nodes += ' </data>\n';
700 nodes += ' </node>\n';
701 var deps = Object.keys(this.currentModules[moduleName].deps);
702 deps.forEach(function(dep) {
703 edges += ' <edge source="' + moduleName + '" target="' + dep + '"/>\n';
704 });
705 }, this);
706
707 var reply = '<?xml version="1.0" encoding="UTF-8"?>\n';
708 reply += '<graphml\n';
709 reply += ' xmlns="http://graphml.graphdrawing.org/xmlns/graphml"\n';
710 reply += ' xmlns:y="http://www.yworks.com/xml/graphml"\n';
711 reply += ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n';
712 reply += ' xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"\n';
713 reply += ' >\n';
714 reply += ' <key id="d0" for="node" yfiles.type="nodegraphics"/>\n';
715 reply += ' <key id="d1" for="edge" yfiles.type="edgegraphics"/>\n';
716 reply += ' <graph id="commonjs" edgedefault="undirected">\n';
717 reply += nodes;
718 reply += edges;
719 reply += ' </graph>\n';
720 reply += '</graphml>\n';
721 return reply;
722 };
723
724 CommonJsProject.prototype.assumeAllFilesLoaded = function() {
725 Object.keys(this.currentModules).forEach(function(moduleName) {
726 this.ignoredModules[moduleName] = this.currentModules[moduleName];
727 }, this);
728 this.currentModules = {};
729 };
730
731 CommonJsProject.prototype.clone = function() {
732 var clone = new CommonJsProject({
733 roots: this.roots,
734 textPluginPattern: this.textPluginPattern
735 });
736
737 clone.aliases = this.aliases;
738
739 Object.keys(this.currentModules).forEach(function(moduleName) {
740 clone.currentModules[moduleName] = this.currentModules[moduleName];
741 }, this);
742
743 Object.keys(this.ignoredModules).forEach(function(moduleName) {
744 clone.ignoredModules[moduleName] = this.ignoredModules[moduleName];
745 }, this);
746
747 return clone;
748 };
749
750 CommonJsProject.prototype.addRoot = function(root) {
751 this.roots.push(root);
752 };
753
754 function findModuleAt(module, base, somePath) {
755 if (base == null) {
756 throw new Error('base == null for ' + somePath);
757 }
758 // Checking for absolute requires and relative requires that previously
759 // had been resolved to absolute paths.
760 if (/^\//.test(somePath)) {
761 if (isFile(somePath)) {
762 console.log('Warning - using location with base = "/"');
763 return new Location('/', somePath);
764 }
765 }
766
767 if (isFile(path.join(base, somePath))) {
768 if (module) {
769 console.log('- Found several matches for ' + somePath +
770 ' (ignoring 2nd)');
771 console.log(' - ' + module.fullname);
772 console.log(' - ' + path.join(base, somePath));
773 }
774 else {
775 module = new Location(base, somePath);
776 }
777 }
778
779 return module;
780 }
781
782 function relative(pathA, pathB) {
783 pathA = pathA.split(/[\/\\]+/);
784 pathB = pathB.split(/[\/\\]+/);
785 var aLen = pathA.length;
786 var bLen = pathB.length;
787
788 // Increment i to the first place where the paths diverge.
789 for (var i = 0; i < aLen && i < bLen && pathA[i] === pathB[i]; i++) {
790 }
791
792 // Remove the redundant parts of the paths.
793 function isntEmptyString(s) {
794 return s !== '';
795 }
796 pathA = pathA.slice(i).filter(isntEmptyString);
797 pathB = pathB.slice(i).filter(isntEmptyString);
798
799 var result = [];
800 for (i = 0; i < pathA.length; i++) {
801 result.push('..');
802 }
803 return result.concat(pathB).join('/');
804 }
805
806 function normalizeRequire(module, moduleName) {
807 if (moduleName.indexOf("!") !== -1) {
808 var chunks = moduleName.split("!");
809 return normalizeRequire(module, chunks[0]) + "!" +
810 normalizeRequire(module, chunks[1]);
811 }
812
813 if (moduleName.charAt(0) == ".") {
814 var requirersDirectory = module.dirname;
815 var pathToRequiredModule = path.join(requirersDirectory, moduleName);
816 // The call to `define` which makes the module being
817 // relatively required isn't the full relative path,
818 // but the path relative from the base.
819 return relative(module.base, pathToRequiredModule);
820 }
821 else {
822 return moduleName;
823 }
824 }
825
826 function findRequires(module) {
827 var code = fs.readFileSync(module.fullname).toString();
828 var ast;
829 try {
830 ast = ujs.parser.parse(code, false);
831 }
832 catch (ex) {
833 console.error('- Failed to compile ' + module.path + ': ' + ex);
834 return;
835 }
836
837 var reply = [];
838 var walkers = {
839 call: function(expr, args) {
840 // If anyone redefines 'require' we won't notice. We could maintain a
841 // list of declared variables in the current scope so we can detect this.
842 // A similar system could have us tracking calls to require via a
843 // different name. that was a useful escape system, but now we detect
844 // computed requires, it's not needed.
845 if (expr[1] === 'define') {
846 var params = null;
847 if (args[0][0] === 'array') {
848 params = args[0][1];
849 }
850 else if (args[0][0] === 'string' && args[1][0] == 'array') {
851 params = args[1][1];
852 }
853 else {
854 console.log('- ' + module.path + ' has define(...) ' +
855 'with non-array parameter. Ignoring requirement.');
856 return;
857 }
858
859 if (params) {
860 for (var i = 0; i < params.length; i++) {
861 param = params[i];
862 if (param[0] === 'string') {
863 reply.push(normalizeRequire(module, param[1]));
864 }
865 else {
866 console.log('- ' + module.path + ' has define(...) ' +
867 'with non-string parameter. Ignoring requirement.');
868 }
869 }
870 }
871 }
872 if (expr[1] === 'require') {
873 if (args[0][0] === 'string') {
874 reply.push(normalizeRequire(module, args[0][1]));
875 }
876 else {
877 console.log('- ' + module.path + ' has require(...) ' +
878 'with non-string parameter. Ignoring requirement.');
879 }
880 }
881 }
882 };
883
884 var walker = ujs.uglify.ast_walker();
885 walker.with_walkers(walkers, function() {
886 return walker.walk(ast);
887 });
888
889 return reply;
890 }
891
892 CommonJsProject.prototype.require = function(moduleName) {
893 var module = this.currentModules[moduleName];
894 if (module) {
895 return module;
896 }
897 module = this.ignoredModules[moduleName];
898 if (module) {
899 return module;
900 }
901
902 // Apply aliases on module path.
903 if (this.aliases) {
904 var parts = moduleName.split("/");
905 var moduleName = parts.pop();
906
907 var self = this;
908 var resolved = parts.map(function(part) {
909 var alias = self.aliases[part];
910 return alias ? alias : part;
911 });
912
913 var moduleUrl = ensureTrailingSlash(resolved.join("/"));
914 moduleName = moduleUrl + moduleName;
915 }
916
917 // Find which of the packages it is in
918 this.roots.forEach(function(base) {
919 if (this.textPluginPattern.test(moduleName)) {
920 var modulePath = moduleName.replace(this.textPluginPattern, '');
921 module = findModuleAt(module, base, modulePath);
922 if (module) {
923 module.isText = true;
924 }
925 } else {
926 module = findModuleAt(module, base, moduleName + '.js');
927 if (!module) {
928 module = findModuleAt(module, base, moduleName + '/index.js');
929 }
930 }
931 }, this);
932
933 if (!module) {
934 console.error('Failed to find module: ' + moduleName);
935 return;
936 }
937
938 module.deps = {};
939 this.currentModules[moduleName] = module;
940
941 if (!module.isText) {
942 // require() all this modules requirements
943 findRequires(module).forEach(function(moduleName) {
944 module.deps[moduleName] = 1;
945 this.require(moduleName);
946 }, this);
947 }
948 };
949
950 CommonJsProject.prototype.getCurrentModules = function() {
951 return Object.keys(this.currentModules).map(function(moduleName) {
952 return this.currentModules[moduleName];
953 }, this);
954 };
955
956 /**
957 *
958 */
959 copy.createCommonJsProject = function(opts) {
960 return new CommonJsProject(opts);
961 };
962
963 /**
964 * Different types of source
965 */
966 copy.source = {};
967
968 /**
969 * @deprecated
970 */
971 copy.source.commonjs = function(obj) {
972 console.log('copy.source.commonjs is deprecated, ' +
973 'pass { project:... includes:...} directly as a source');
974 return obj;
975 };
976
977 /**
978 * File filters
979 */
980 copy.filter = {};
981
982 copy.filter.debug = function(input, source) {
983 source = source || 'unknown';
984 module = source.path ? source.path : source;
985 return input;
986 };
987 copy.filter.debug.onRead = true;
988
989 /**
990 * Compress the given input code using UglifyJS.
991 *
992 * @param string input
993 * @return string output
994 */
995 copy.filter.uglifyjs = function(input) {
996 if (typeof input !== 'string') {
997 input = input.toString();
998 }
999
1000 var opt = copy.filter.uglifyjs.options;
1001 var ast;
1002 try {
1003 ast = ujs.parser.parse(input, opt.parse_strict_semicolons);
1004 }
1005 catch (ex) {
1006 console.error('- Failed to compile code: ' + ex);
1007 return input;
1008 }
1009
1010 if (opt.mangle) {
1011 ast = ujs.uglify.ast_mangle(ast, opt.mangle_toplevel);
1012 }
1013
1014 if (opt.squeeze) {
1015 ast = ujs.uglify.ast_squeeze(ast, opt.squeeze_options);
1016 if (opt.squeeze_more) {
1017 ast = ujs.uglify.ast_squeeze_more(ast);
1018 }
1019 }
1020
1021 return ujs.uglify.gen_code(ast, opt.beautify);
1022 };
1023 copy.filter.uglifyjs.onRead = false;
1024 /**
1025 * UglifyJS filter options.
1026 */
1027 copy.filter.uglifyjs.options = {
1028 parse_strict_semicolons: false,
1029
1030 /**
1031 * The beautify argument used for process.gen_code(). See the UglifyJS
1032 * documentation.
1033 */
1034 beautify: false,
1035 mangle: true,
1036 mangle_toplevel: false,
1037 squeeze: true,
1038
1039 /**
1040 * The options argument used for process.ast_squeeze(). See the UglifyJS
1041 * documentation.
1042 */
1043 squeeze_options: {},
1044
1045 /**
1046 * Tells if you want to perform potentially unsafe compression.
1047 */
1048 squeeze_more: false
1049 };
1050
1051 /**
1052 * A filter to munge CommonJS headers
1053 */
1054 copy.filter.addDefines = function(input, source) {
1055 if (typeof input !== 'string') {
1056 input = input.toString();
1057 }
1058
1059 if (!source) {
1060 throw new Error('Missing filename for moduleDefines');
1061 }
1062
1063 var module = source.isLocation ? source.path : source;
1064
1065 input = input.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1066 input = '"' + input.replace(/\n/g, '\\n" +\n "') + '"';
1067
1068 return 'define("text!' + module + '", [], ' + input + ');\n\n';
1069 };
1070 copy.filter.addDefines.onRead = true;
1071
1072 /**
1073 * Like addDefines, but adds base64 encoding
1074 */
1075 copy.filter.base64 = function(input, source) {
1076 if (typeof input === 'string') {
1077 throw new Error('base64 filter needs to be the first in a filter set');
1078 }
1079
1080 if (!source) {
1081 throw new Error('Missing filename for moduleDefines');
1082 }
1083
1084 var module = source.isLocation ? source.path : source;
1085
1086 if (module.substr(-4) === '.png') {
1087 input = 'data:image/png;base64,' + input.toString('base64');
1088 }
1089 else if (module.substr(-4) === '.gif') {
1090 input = 'data:image/gif;base64,' + input.toString('base64');
1091 }
1092 else {
1093 throw new Error('Only gif/png supported by base64 filter: ' + source);
1094 }
1095
1096 return 'define("text!' + module + '", [], "' + input + '");\n\n';
1097 };
1098 copy.filter.base64.onRead = true;
1099
1100 /**
1101 * Munge define lines to add module names
1102 */
1103 copy.filter.moduleDefines = function(input, source) {
1104 if (!source) {
1105 console.log('- Source without filename passed to moduleDefines().' +
1106 ' Skipping addition of define(...) wrapper.');
1107 return input;
1108 }
1109
1110 if (source.isText) {
1111 return copy.filter.addDefines(input, source);
1112 }
1113
1114 if (typeof input !== 'string') {
1115 input = input.toString();
1116 }
1117
1118 var deps = source.deps ? Object.keys(source.deps) : [];
1119 deps = deps.length ? (", '" + deps.join("', '") + "'") : "";
1120
1121 var module = source.isLocation ? source.path : source;
1122 module = module.replace(/\.js$/, '');
1123
1124 return input.replace(/\bdefine\s*\(\s*function\s*\(require,\s*exports,\s*module\)\s*\{/,
1125 "define('" + module + "', ['require', 'exports', 'module' " + deps + "], function(require, exports, module) {");
1126 };
1127 copy.filter.moduleDefines.onRead = true;
1128
1129 /**
1130 * Why does node throw an exception for statSync(), especially when it has no
1131 * exists()?
1132 */
1133 function isFile(fullPath) {
1134 return fs.existsSync(fullPath) && fs.statSync(fullPath).isFile();
1135 }
1136
1137 /**
1138 * Add a trailing slash to s directory path if needed
1139 */
1140 function ensureTrailingSlash(filename) {
1141 if (filename.length > 0 && filename.substr(-1) !== '/') {
1142 filename += '/';
1143 }
1144 return filename;
1145 }
1146
1147
1148 exports.copy = copy;
0 /*
1 * Copyright 2012, Mozilla Foundation and contributors
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /**
17 * Define a module along with a payload.
18 * @param moduleName Name for the payload
19 * @param deps Ignored. For compatibility with CommonJS AMD Spec
20 * @param payload Function with (require, exports, module) params
21 */
22 function define(moduleName, deps, payload) {
23 if (typeof moduleName != "string") {
24 throw new Error("Error: Module name is not a string");
25 }
26
27 if (arguments.length == 2) {
28 payload = deps;
29 }
30 else {
31 payload.deps = deps;
32 }
33
34 if (define.debugDependencies) {
35 console.log("define: " + moduleName + " -> " + payload.toString()
36 .slice(0, 40).replace(/\n/, '\\n').replace(/\r/, '\\r') + "...");
37 }
38
39 if (moduleName in define.modules) {
40 throw new Error("Error: Redefining module: " + moduleName);
41 }
42
43 // Mark the payload so we know we need to call it to get the real module
44 payload.__uncompiled = true;
45 define.modules[moduleName] = payload;
46 }
47
48 /**
49 * The global store of un-instantiated modules
50 */
51 define.modules = {};
52
53 /**
54 * Should we console.log on module definition/instantiation/requirement?
55 */
56 define.debugDependencies = false;
57
58
59 (function() {
60
61 /**
62 * We invoke require() in the context of a Domain so we can have multiple
63 * sets of modules running separate from each other.
64 * This contrasts with JSMs which are singletons, Domains allows us to
65 * optionally load a CommonJS module twice with separate data each time.
66 * Perhaps you want 2 command lines with a different set of commands in each,
67 * for example.
68 */
69 function Domain() {
70 this.modules = {};
71
72 if (define.debugDependencies) {
73 this.depth = "";
74 }
75 }
76
77 /**
78 * Lookup module names and resolve them by calling the definition function if
79 * needed.
80 * There are 2 ways to call this, either with an array of dependencies and a
81 * callback to call when the dependencies are found (which can happen
82 * asynchronously in an in-page context) or with a single string an no
83 * callback where the dependency is resolved synchronously and returned.
84 * The API is designed to be compatible with the CommonJS AMD spec and
85 * RequireJS.
86 * @param deps A name, or array of names for the payload
87 * @param callback Function to call when the dependencies are resolved
88 * @return The module required or undefined for array/callback method
89 */
90 Domain.prototype.require = function(config, deps, callback) {
91 if (arguments.length <= 2) {
92 callback = deps;
93 deps = config;
94 config = undefined;
95 }
96
97 if (Array.isArray(deps)) {
98 var params = deps.map(function(dep) {
99 return this.lookup(dep);
100 }, this);
101 if (callback) {
102 callback.apply(null, params);
103 }
104 return undefined;
105 }
106 else {
107 return this.lookup(deps);
108 }
109 };
110
111 /**
112 * Lookup module names and resolve them by calling the definition function if
113 * needed.
114 * @param moduleName A name for the payload to lookup
115 * @return The module specified by aModuleName or null if not found
116 */
117 Domain.prototype.lookup = function(moduleName) {
118 if (moduleName in this.modules) {
119 var module = this.modules[moduleName];
120 if (define.debugDependencies) {
121 console.log(this.depth + " Using module: " + moduleName);
122 }
123 return module;
124 }
125
126 if (!(moduleName in define.modules)) {
127 throw new Error("Missing module: " + moduleName);
128 }
129
130 var module = define.modules[moduleName];
131
132 if (define.debugDependencies) {
133 console.log(this.depth + " Compiling module: " + moduleName);
134 }
135
136 if (module.__uncompiled) {
137 if (define.debugDependencies) {
138 this.depth += ".";
139 }
140
141 var exports = {};
142 try {
143 var params = module.deps.map(function(dep) {
144 if (dep === "require") {
145 return this.require.bind(this);
146 }
147 if (dep === "exports") {
148 return exports;
149 }
150 if (dep === "module") {
151 return { id: moduleName, uri: "" };
152 }
153 return this.lookup(dep);
154 }.bind(this));
155
156 var reply = module.apply(null, params);
157 module = (reply !== undefined) ? reply : exports;
158 }
159 catch (ex) {
160 console.error("Error using module: " + moduleName, ex);
161 throw ex;
162 }
163
164 if (define.debugDependencies) {
165 this.depth = this.depth.slice(0, -1);
166 }
167 }
168
169 // cache the resulting module object for next time
170 this.modules[moduleName] = module;
171
172 return module;
173 };
174
175 /**
176 * Expose the Domain constructor and a global domain (on the define function
177 * to avoid exporting more than we need. This is a common pattern with
178 * require systems)
179 */
180 define.Domain = Domain;
181 define.globalDomain = new Domain();
182
183 })();
184
185 /**
186 * Expose a default require function which is the require of the global
187 * sandbox to make it easy to use.
188 */
189 var require = define.globalDomain.require.bind(define.globalDomain);
0 {
1 "name": "dryice",
2 "description": "A CommonJS/RequireJS packaging tool for browser scripts",
3 "keywords": [ "build", "commonjs", "requirejs" ],
4 "version": "0.4.10",
5 "homepage": "https://github.com/joewalker/dryice",
6 "author": "Joe Walker <joe@getahead.org>",
7 "contributors": [ ],
8 "repository": {
9 "type": "git",
10 "url": "http://github.com/mozilla/dryice.git"
11 },
12 "bugs": { "url": "http://github.com/mozilla/dryice/issues" },
13 "directories": { "lib" : "./lib" },
14 "main": "./lib/dryice/index.js",
15 "engines": { "node" : ">=0.6.0" },
16 "licenses": [
17 { "type": "Apache-2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0" }
18 ],
19 "dependencies": {
20 "uglify-js": "~1.3.4"
21 }
22 }