├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── bin └── volo ├── commands ├── add.js ├── add │ └── doc.md ├── amdify.js ├── amdify │ ├── doc.md │ ├── exportsNoConflictTemplate.js │ ├── exportsTemplate.js │ └── template.js ├── create.js ├── create │ └── doc.md ├── help.js ├── info.js ├── info │ └── doc.md ├── install.js ├── npmrel.js ├── npmrel │ └── doc.md ├── remove.js ├── remove │ └── doc.md ├── search.js ├── search │ └── doc.md └── update.js ├── docs ├── releaseNotes.md └── workingWithCode.md ├── lib ├── archive.js ├── commands.js ├── config.js ├── download.js ├── exists.js ├── file.js ├── github.js ├── github │ └── auth.js ├── homeDir.js ├── json.js ├── lang.js ├── mime.js ├── net.js ├── packageJson.js ├── parse.js ├── qutil.js ├── resolve │ └── github.js ├── search │ └── github.js ├── tempDir.js ├── template.js ├── unzip.js ├── v.js ├── version.js └── volofile.js ├── package.json ├── tests ├── all.js ├── commands │ ├── add │ │ ├── expected │ │ │ └── simpleAmd.js │ │ ├── support │ │ │ ├── addAll │ │ │ │ └── package.json │ │ │ ├── addable │ │ │ │ ├── temp │ │ │ │ │ └── a.js │ │ │ │ └── volofile │ │ │ ├── dirIgnore │ │ │ │ ├── .npmignore │ │ │ │ ├── demos │ │ │ │ │ └── empty.txt │ │ │ │ ├── extra │ │ │ │ │ └── empty.txt │ │ │ │ ├── keep │ │ │ │ │ ├── empty.txt │ │ │ │ │ └── sub │ │ │ │ │ │ └── nested.pre │ │ │ │ ├── node_modules │ │ │ │ │ └── empty.txt │ │ │ │ ├── package.json │ │ │ │ └── top.pre │ │ │ ├── localModules │ │ │ │ ├── a │ │ │ │ │ └── a.js │ │ │ │ ├── b │ │ │ │ │ ├── b.js │ │ │ │ │ └── package.json │ │ │ │ ├── c │ │ │ │ │ ├── c.js │ │ │ │ │ └── package.json │ │ │ │ ├── d │ │ │ │ │ └── d.js │ │ │ │ └── e │ │ │ │ │ └── e.js │ │ │ └── simple │ │ │ │ └── simple.js │ │ └── tests.js │ ├── amdify │ │ ├── expected │ │ │ ├── lib.js │ │ │ ├── libExports.js │ │ │ ├── plain.js │ │ │ ├── plainExports.js │ │ │ ├── thisExports.js │ │ │ └── varnames.js │ │ ├── support │ │ │ ├── lib.js │ │ │ ├── libExports.js │ │ │ ├── plain.js │ │ │ ├── plainExports.js │ │ │ ├── thisExports.js │ │ │ └── varnames.js │ │ └── tests.js │ ├── create │ │ ├── support │ │ │ ├── addOnCreate │ │ │ │ ├── js │ │ │ │ │ └── jquery.js │ │ │ │ └── package.json │ │ │ ├── funnySubDirName │ │ │ │ ├── README.md │ │ │ │ └── something.github.com │ │ │ │ │ └── test.txt │ │ │ ├── hasNpmInstall │ │ │ │ ├── package.json │ │ │ │ └── volofile │ │ │ ├── hasOnCreate │ │ │ │ ├── sample.template │ │ │ │ └── volofile │ │ │ ├── simple │ │ │ │ └── simple.js │ │ │ └── voloBaseUrl │ │ │ │ ├── package.json │ │ │ │ └── volofile │ │ └── tests.js │ ├── remove │ │ └── tests.js │ └── volofile │ │ ├── support │ │ ├── shell │ │ │ ├── node_modules │ │ │ │ └── .bin │ │ │ │ │ └── uppercase │ │ │ └── volofile │ │ └── simple │ │ │ └── volofile │ │ └── tests.js ├── doh │ ├── LICENSE │ ├── README │ ├── _browserRunner.js │ ├── _nodeRunner.js │ ├── _rhinoRunner.js │ ├── runner.html │ ├── runner.js │ ├── runner.sh │ └── small_logo.png ├── full │ ├── README.md │ ├── expected │ │ └── legacy01 │ │ │ └── results.txt │ ├── support │ │ ├── legacy01 │ │ │ └── volofile │ │ └── simple │ │ │ └── volofile │ └── tests.js └── lib │ └── volo │ ├── github.js │ ├── packageJson │ ├── expected │ │ ├── hasFile │ │ │ └── empty.js │ │ ├── hasJs │ │ │ └── lib.js │ │ └── hasJsonFile │ │ │ └── package.json │ ├── support │ │ ├── hasFile │ │ │ ├── empty.js │ │ │ └── package.json │ │ ├── hasJs │ │ │ ├── lib.js │ │ │ └── lib.txt │ │ ├── hasJsNoComment │ │ │ └── lib.js │ │ ├── hasJsNpmStyle │ │ │ ├── lib.js │ │ │ └── lib.txt │ │ ├── hasJsonFile │ │ │ ├── main.js │ │ │ └── package.json │ │ └── tooManyJs │ │ │ ├── one.js │ │ │ └── two.js │ └── tests.js │ └── version.js └── volo.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | tests/commands/add/output 4 | tests/commands/amdify/output 5 | tests/commands/create/output 6 | tests/commands/remove/output 7 | tests/commands/volofile/output 8 | tests/full/output 9 | tests/lib/volo/packageJson/output 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/.bin 2 | docs 3 | tests 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | volo is released under two licenses: new BSD, and MIT. You may pick the 2 | license that best suits your development needs. The text of both licenses are 3 | provided below. 4 | 5 | 6 | The "New" BSD License: 7 | ---------------------- 8 | 9 | Copyright (c) 2011, The Dojo Foundation 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright notice, this 16 | list of conditions and the following disclaimer. 17 | * Redistributions in binary form must reproduce the above copyright notice, 18 | this list of conditions and the following disclaimer in the documentation 19 | and/or other materials provided with the distribution. 20 | * Neither the name of the Dojo Foundation nor the names of its contributors 21 | may be used to endorse or promote products derived from this software 22 | without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 25 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | 36 | 37 | MIT License 38 | ----------- 39 | 40 | Copyright (c) 2011, The Dojo Foundation 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy 43 | of this software and associated documentation files (the "Software"), to deal 44 | in the Software without restriction, including without limitation the rights 45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | copies of the Software, and to permit persons to whom the Software is 47 | furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in 50 | all copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 58 | THE SOFTWARE. 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [volo](http://volojs.org) 2 | 3 | **Create** browser-based, front-end projects from project templates, and **add** 4 | dependencies by fetching them from GitHub. Once your project is set up, 5 | **automate** common tasks. 6 | 7 | volo is dependency manager and project creation tool that favors GitHub 8 | for the package repository. 9 | 10 | At its heart, volo is a generic command runner -- you can create new 11 | commands for volo, and you can use commands others have created. 12 | 13 | By default, volo knows how to: 14 | 15 | * [create a new web project](https://github.com/volojs/volo/blob/master/commands/create/doc.md) 16 | * [add scripts for a web project from the command line](https://github.com/volojs/volo/blob/master/commands/add/doc.md) 17 | * [automate project actions via volofiles](https://github.com/volojs/volo/wiki/Creating-a-volofile) and [reusable volo commands](https://github.com/volojs/volo/wiki/Creating-a-volo-command) 18 | 19 | ## Prerequisites 20 | 21 | * [Node](http://nodejs.org) 0.6.5 or later installed. 22 | 23 | If you are using Ubuntu, then you may need to `apt-get install nodejs-legacy` too. 24 | 25 | ## Install 26 | 27 | volo requires Node to run. Node includes [npm](http://npmjs.org/), 28 | a package manager for node code. To install volo: 29 | 30 | npm install -g volo 31 | 32 | If you get an error when running that command, and it contains this line somewhere in it: 33 | 34 | npm ERR! Please try running this command again as root/Administrator. 35 | 36 | You will need to run the install via sudo: 37 | 38 | sudo npm install -g volo 39 | 40 | ## Usage 41 | 42 | volo can use GitHub to retrieve code, so one of the core concepts when using 43 | it is understanding **user/repo** for archive names. See the 44 | [add doc](https://github.com/volojs/volo/blob/master/commands/add/doc.md) for more 45 | info on the types of archive names to use. 46 | 47 | ### AMD project example 48 | 49 | To set up an AMD/RequireJS-based project called **fast** that uses AMD versions of 50 | Backbone, jQuery and underscore: 51 | 52 | > volo create fast (uses [volojs/create-template](https://github.com/volojs/create-template) for project template) 53 | > cd fast 54 | > volo add jquery (uses jquery/jquery as the repo) 55 | > volo add underscore (uses amdjs/underscore as repo since an AMD project) 56 | > volo add backbone (uses amdjs/backbone as repo since an AMD project) 57 | 58 | Then modify `www/js/app.js` to require the modules you need and add your app 59 | logic. 60 | 61 | The above example uses the 62 | [amdjs/underscore](https://github.com/amdjs/underscore) and 63 | [amdjs/backbone](https://github.com/amdjs/backbone) versions of those libraries, 64 | which include integrated AMD support. 65 | 66 | ### Browser globals project example 67 | 68 | To set up an HTML5 Boilerplate project that does not use AMD/RequireJS, but does 69 | use documentcloud repos of Backbone and Underscore (the Boilerplate already has 70 | jQuery): 71 | 72 | > volo create html5fast html5-boilerplate (pulls down latest tag of that repo) 73 | > cd html5fast 74 | > volo add underscore (uses documentcloud/underscore as repo) 75 | > volo add backbone (uses documentcloud/backbone as repo) 76 | 77 | ### Updating a previously added library 78 | 79 | There is no "update" command in Volo. However, updating a library is simple: 80 | 81 | > volo add -f underscore 82 | 83 | This will delete your local copy of underscore and then re-add underscore. 84 | 85 | ## Library Best Practices 86 | 87 | To work well with volo, here are some tips on how to structure your library code: 88 | 89 | * [Library Best Practices](https://github.com/volojs/volo/wiki/Library-best-practices) 90 | 91 | ## Details 92 | 93 | * [Design goals](https://github.com/volojs/volo/wiki/Design-Goals) 94 | * [Prior Art](https://github.com/volojs/volo/wiki/Prior-Art): npm, cpm, bpm. 95 | * [Create a volo command](https://github.com/volojs/volo/wiki/Creating-a-volo-command) 96 | * [Create a volofile](https://github.com/volojs/volo/wiki/Creating-a-volofile) 97 | * License: [MIT and new BSD](https://github.com/volojs/volo/blob/master/LICENSE). 98 | 99 | ## Engage 100 | 101 | * [Discussion list](http://groups.google.com/group/volojs) 102 | * [File an issue](https://github.com/volojs/volo/issues) 103 | * [Working with the volo code](https://github.com/volojs/volo/blob/master/docs/workingWithCode.md) 104 | -------------------------------------------------------------------------------- /bin/volo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 5 | * Available via the MIT or new BSD license. 6 | * see: http://github.com/volojs/volo for details 7 | */ 8 | 9 | /*jslint node: true */ 10 | 'use strict'; 11 | 12 | var args = [].splice.call(process.argv, 2); 13 | 14 | (require('../volo'))(args).then(function (okText) { 15 | if (okText) { 16 | console.log(okText); 17 | } 18 | //Technically this should not be needed, but with 19 | //node 0.10, it seems to just hang for a minute or 20 | //so at this point. It may be some interaction with 21 | //q and the 0.10 changes around setTimeout? Revisit 22 | //later with later versions of node and q. Also, it 23 | //only happens with some uses of volo, perhaps ones 24 | //with a create that has an onCreate or onAdd hook? 25 | //Wanting to blame 0.10 though, as there also seems 26 | //to be weirdness around http code now too, see 27 | //failures that did not show up in 0.8. 28 | process.exit(0); 29 | }, function (errText) { 30 | var colors = require('colors'); 31 | colors.mode = 'console'; 32 | //Create new string to get the red modifier. 33 | console.log(String(errText).red); 34 | process.exit(1); 35 | }); 36 | -------------------------------------------------------------------------------- /commands/add/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo add [flags] [archive] [localName] 4 | 5 | where the allowed flags are: 6 | 7 | * **-f**: Forces the add even if the code has already been added to the project. 8 | * **-amd**: Indicates the project is an AMD project. If the project has a 9 | package.json entry for "amd": {} then this flag is not needed. 10 | * **-amdoff**: Turns off AMD conversion for when the project is AMD and the 11 | dependency being added is not AMD/CommonJS. Only needs to be set if the 12 | project's package.json has an "amd": {} entry. 13 | * **-amdlog**: Prints out more details on files converted to AMD, if AMD 14 | conversion is done. 15 | * **-noprompt**: Does not prompt the user for any dependency/exports details 16 | when adding a non-AMD/non-CommonJS module to an AMD project. 17 | * **-nostamp**: Does not stamp the package.json in the directory that runs this 18 | command with the added dependency information. 19 | * **-skipexists**: If a dependency by that name already exists, just skip it 20 | without informing it already exists. 21 | 22 | **archive** is in one of the following formats: 23 | 24 | * **searchterm**: GitHub search is used to resolve searchterm to a user/repo 25 | value. 26 | * searchterm/version: GitHub search is used to resolve searchterm to a user/repo 27 | value, then version is added to it. version can be a semantic version range. 28 | * **user/repo**: Download the zip file from GitHub for the user/repo, using the 29 | latest version tag, or "master" if no version tags. 30 | * **user/repo/tag**: Download the zip file from GitHub for the user/repo, using 31 | the specific tag/branch name listed. 32 | * **user/repo/semverVersion**: Download the zip file from GitHub for the 33 | user/repo, using the specified semantic version range. Example ranges: '~1.2', 34 | '>1.6.4'. 35 | * **user/repo/tag#specific/file.js**: Download zip file from GitHub for the 36 | user/ repo, using the specific tag/branch name listed, then extracting only 37 | the specific/file.js file from that archive and installing it. 38 | * **user/repo/tag#specific/dir**: Download zip file from GitHub for the user/ 39 | repo, using the specific tag/branch name listed, then extracting only 40 | the specific/dir directory from that archive and installing it. 41 | * **http://some.domain.com/path/to/archive.zip**: Downloads the zip file and 42 | installs it. 43 | * **http://some.domain.com/path/to/archive.zip#specific/file.js**: Download 44 | the zip file and only install specific/file.js. 45 | * **symlink:path/to/directory/or/file.js**: Creates a symlink to the specific 46 | location in the project. If it is a directory and the project using the 47 | directory is an AMD project, an adapter module will also be created. 48 | * **local:path/to/directory**: Copies the local directory. A local directory is 49 | also checked when the "some/thing" archive name is used -- if there is no 50 | local file match, it is assumed to be a GitHub URL. 51 | 52 | If **archive is missing from the command**, then if a package.json in the 53 | current directory contains a **volo.dependencies** section, it will go through 54 | those dependencies and add all of them. 55 | 56 | If **localName** is specified then that name is used for the installed name. 57 | If the installed item is a directory, the directory will have this name. If 58 | a specific file from the the archive, the file will have this name. 59 | 60 | If **localName** is not specified, the installed directory name will be the 61 | name of the .zip file without the zip extension, or if a GitHub 62 | reference, the repo name. If it is a specific file from within a zip file, 63 | then that file's name will be used. 64 | 65 | ## Examples 66 | 67 | This one fetches Underscore and converts it to have an AMD wrapper. Underscore 68 | still registers a global export, but AMD code can get a local reference 69 | through the module ID: 70 | 71 | volo add -amd jashkenas/underscore exports=_ 72 | 73 | When the -amd flag is used, the the **amdify** command is used to convert 74 | the file downloaded by the **add** command, so the named arguments supported 75 | by **amdify** can also be used with **add**. 76 | 77 | Here is a command that fetches Backbone and wraps in it in an AMD define() call, 78 | specifying 'jquery' and 'underscore' as dependencies: 79 | 80 | volo add -amd jashkenas/backbone depends=underscore,jquery exports=Backbone 81 | 82 | To update an exising library, use the -f flag: 83 | 84 | volo add -f jquery 85 | 86 | ## Installation Details 87 | 88 | For the directory in which add is run, it will look for the following to know 89 | where to install: 90 | 91 | * Looks for a volo.baseDir package.json property. 92 | * Looks for a volo.baseUrl package.json property. (legacy property) 93 | * Looks for a amd.baseUrl package.json property (legacy property) 94 | * Looks for a **js** directory 95 | * Looks for a **scripts** directory 96 | 97 | If none of those result in a subdirectory for installation, then the current 98 | working directory is used. 99 | 100 | If the archive has a top level .js file in it and it is the same name 101 | as the repo's/zip file name, then only that .js file will be installed. 102 | 103 | Or, if there is only one top level .js file in the repo and it has a 104 | /*package.json */ comment with JSON inside that comment, it will be used. 105 | 106 | ## Remembering the details 107 | 108 | Once the dependency has been added, `volo add` will stamp a package.json 109 | file with the archive name used to add the dependency. If there is no 110 | package.json file in the current directory, it will create one. 111 | -------------------------------------------------------------------------------- /commands/amdify/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo amdify [flags] path/to/file.js [depends=] [exports=] [github=] 4 | 5 | where flags can be: 6 | 7 | * **-noConflict**: indicates that code shoud be included to call the exports 8 | value's noConflict method if it exists. 9 | * **-noprompt**: do not prompt for dependencies/exported value for 10 | non-AMD/CommonJS files. Useful for automated processes. 11 | * **-commonJs**: indicates that the code should use the "convert CommonJS to 12 | AMD" path, intead of trying to detect it via require('') use in the file. 13 | Useful if the modules just uses `this` property assignments, and does not use 14 | `require` or `exports`. 15 | 16 | and: 17 | 18 | * **depends** is a comma-separated list of dependencies, with no spaces 19 | * **exports** is the global value created by the file that should be treated as 20 | the module's exported value. 21 | * **github** is the "user/repo" that is the origin of the file. Used in AMD 22 | projects to look for depends/exports override arguments to use from the 23 | volojs/repos project. Example value: "jrburke/almond" for the almond repo 24 | under the jrburke user. 25 | 26 | ## Details 27 | 28 | The file.js will be modified to include a define() wrapper with the given 29 | dependency names. 30 | 31 | This example: 32 | 33 | volo amdify www/js/aplugin.jquery.js depends=jquery 34 | 35 | Will result in modifying the www/js/aplugin.jquery.js contents to have a 36 | function wrapping that includes: 37 | 38 | define(['jquery'], function () { 39 | //original contents in here. 40 | }); 41 | 42 | If a dependency calls AMD's define(), then it may not create a global when 43 | loaded by an AMD loader. In that case, you may need to give the dependency 44 | as specific name to act as the "global" in the script. you can do this by 45 | passing `=localvarname` for the dependency. Here is an example for a "ko" 46 | dependency that does not export a global "ko" as a global if AMD is in use, 47 | so this amdify command will make sure to create a `kolocal` for use by 48 | the wrapped code. It will also create a local `beta` for `beta.1.7.2`: 49 | 50 | volo amdify www/js/ko.plugin.js depends=ko=kolocal,beta.1.7.2=beta 51 | 52 | Results in: 53 | 54 | define(['ko', 'beta.1.7.2'], function (kolocal, beta) { 55 | //original contents in here. 56 | }); 57 | 58 | This example sets dependencies, but then also specifies the export value to 59 | be used. If the export object has a 'noConflict' method on it, then it will 60 | be called as part of exporting the module value: 61 | 62 | volo amdify www/js/lib.js depends=jquery exports=lib 63 | 64 | results in a transform that looks roughly like: 65 | 66 | define(['jquery'], function () { 67 | 68 | //original contents in here. 69 | 70 | return lib; 71 | }); 72 | 73 | If you want "-noConflict" called on the exports value: 74 | 75 | volo amdify -noConflict www/js/lib.js depends=jquery exports=lib 76 | 77 | results in a transform that looks roughly like: 78 | 79 | define(['jquery'], function () { 80 | 81 | //original contents in here. 82 | 83 | if (lib.noConflict)) { 84 | lib.noConflict(true); 85 | } 86 | return lib; 87 | }); 88 | 89 | **Be careful with -noConflict**. You most likely do not want to use it if 90 | you have other code that has been amdify'd that depends on this amdify'd code. 91 | For instance, using amdify on underscore.js with -noConflict is bad since 92 | backbone.js depends on underscore, and it looks for a global _ value. 93 | 94 | amdify will set the "this" value for the original contents to be the global 95 | object. 96 | 97 | Ideally the target file would optionally call define() itself, and use 98 | the local dependency references instead of browser globals. However, for 99 | bootstrapping existing projects to use an AMD loader, amdify can be useful to 100 | get started. 101 | 102 | Using amdify will produce code that is uglier than doing a proper code change 103 | to add optional an optional define() call. For better code examples, see: 104 | https://github.com/umdjs/umd 105 | -------------------------------------------------------------------------------- /commands/amdify/exportsNoConflictTemplate.js: -------------------------------------------------------------------------------- 1 | if (/*EXPORTS*/.noConflict) { 2 | /*EXPORTS*/.noConflict(true); 3 | } 4 | amdExports = /*EXPORTS*/; 5 | -------------------------------------------------------------------------------- /commands/amdify/exportsTemplate.js: -------------------------------------------------------------------------------- 1 | amdExports = /*EXPORTS*/; 2 | -------------------------------------------------------------------------------- /commands/amdify/template.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define([/*DEPENDENCIES*/], function (/*VARNAMES*/) { (function () { 3 | 4 | /*CONTENTS*/ 5 | 6 | /*EXPORTS*/ 7 | }.call(root)); 8 | return amdExports; 9 | }); }(this)); 10 | -------------------------------------------------------------------------------- /commands/create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true, nomen: true */ 8 | /*global console */ 9 | 10 | 'use strict'; 11 | 12 | var fs = require('fs'), 13 | path = require('path'), 14 | q = require('q'), 15 | colors = require('colors'), 16 | tempDir = require('../lib/tempDir'), 17 | archive = require('../lib/archive'), 18 | file = require('../lib/file'), 19 | download = require('../lib/download'), 20 | unzip = require('../lib/unzip'), 21 | packageJson = require('../lib/packageJson'), 22 | venv = require('../lib/v'), 23 | volofile = require('../lib/volofile'), 24 | create; 25 | 26 | colors.mode = 'console'; 27 | 28 | //Changes directories, runs a function that returns a promise, 29 | //ignores the return value (pass or fail), resolves a promise regardless, 30 | //but transfers back to original dir. 31 | //returns a promise to be used by others. 32 | function withDir(targetDir, promiseGenerator) { 33 | var currentDir = process.cwd(), 34 | d = q.defer(); 35 | 36 | process.chdir(targetDir); 37 | 38 | function done() { 39 | process.chdir(currentDir); 40 | d.resolve(); 41 | } 42 | 43 | promiseGenerator().then(done, done); 44 | 45 | return d.promise; 46 | } 47 | 48 | create = { 49 | summary: 'Creates a new web project.', 50 | 51 | doc: file.readFile(path.join(__dirname, '/create/doc.md')), 52 | 53 | validate: function (namedArgs, appName) { 54 | if (!appName) { 55 | return new Error('Please specify a name to use for the created project.'); 56 | } else if (file.exists(appName)) { 57 | return new Error(appName + ' already exists.'); 58 | } 59 | return undefined; 60 | }, 61 | 62 | run: function (d, v, namedArgs, appName, template) { 63 | template = template || 'volojs/create-template'; 64 | 65 | var archiveInfo, tempDirName, zipFileName, packageInfo, 66 | q = v.require('q'); 67 | 68 | //Function used to clean up in case of errors. 69 | function errCleanUp(err) { 70 | if (tempDirName) { 71 | //Clean up temp area. Even though this is async, 72 | //it is not important to track the completion. 73 | file.asyncPlatformRm(tempDirName); 74 | } 75 | return err; 76 | } 77 | 78 | //Find out how to get the template 79 | q.fcall(function () { 80 | return archive.resolve(template, namedArgs.volo.resolve); 81 | }).then(function (info) { 82 | //Create a tempdir to store the archive. 83 | archiveInfo = info; 84 | return tempDir.create(template); 85 | }).then(function (tempName) { 86 | //Save the name for the outer errCleanUp to use later. 87 | tempDirName = tempName; 88 | zipFileName = path.join(tempDirName, 'template.zip'); 89 | 90 | return download({ 91 | url: archiveInfo.url, 92 | headers: archiveInfo.urlHeaders 93 | }, zipFileName); 94 | }).then(function () { 95 | if (archiveInfo.isArchive) { 96 | return unzip(zipFileName); 97 | } 98 | return undefined; 99 | }).then(function () { 100 | //Move the unzipped directory to the final location. 101 | var dirName = file.firstDir(tempDirName); 102 | if (dirName) { 103 | //Move the unpacked template to appName 104 | //Doing a copy instead of a rename since 105 | //that does not work across partitions. 106 | file.copyDir(dirName, appName); 107 | file.rm(dirName); 108 | 109 | //Clean up temp area. Even though this is async, 110 | //it is not important to track the completion. 111 | file.asyncPlatformRm(tempDirName); 112 | 113 | return undefined; 114 | } else { 115 | return errCleanUp(new Error('Unexpected zipball configuration')); 116 | } 117 | }).then(function () { 118 | //If there is a package.json with dependencies listed for install 119 | //by npm, and there is no existing node_modules, then run npm. 120 | var vinstance; 121 | 122 | packageInfo = packageJson(appName); 123 | 124 | if (packageInfo.data && packageInfo.data.dependencies && 125 | !file.exists(path.join(appName, 'node_modules'))) { 126 | 127 | vinstance = venv(appName).env; 128 | return withDir(appName, function () { 129 | return vinstance.shell('npm install', { 130 | useConsole: !namedArgs.quiet 131 | }); 132 | }); 133 | } 134 | }).then(function () { 135 | if (packageInfo && packageInfo.data && packageInfo.data.volo && 136 | packageInfo.data.volo.dependencies) { 137 | //Run `volo add` to install any dependencies that are missing. 138 | return withDir(appName, function () { 139 | return require('../volo')(['add', '-skipexists']) 140 | .then(function (result) { 141 | //Log result if appropriate. 142 | if (!namedArgs.quiet) { 143 | console.log(result); 144 | } 145 | }); 146 | }); 147 | } 148 | }).then(function () { 149 | //If there is a volofile with an onCreate, run it. 150 | return volofile.run(appName, 'onCreate', namedArgs, appName) 151 | .fail(function (err) { 152 | return err + '\nonCreate did not succeed. You can ' + 153 | 'try later by typing "volo onCreate" inside ' + 154 | 'the project that was just created, passing any ' + 155 | 'arguments you passed to the create call.'; 156 | }); 157 | }).then(function (commandOutput) { 158 | return (commandOutput ? commandOutput + '\n' : '') + 159 | (archiveInfo.url.bold + ' used to create ' + appName.bold); 160 | }).then(d.resolve, function (err) { 161 | errCleanUp(err); 162 | d.reject(err); 163 | }); 164 | } 165 | }; 166 | 167 | module.exports = create; 168 | -------------------------------------------------------------------------------- /commands/create/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo create appName [templateArchive] 4 | 5 | **appName** is the name of the directory that should be created containing the 6 | contents of the templateArchive. 7 | 8 | **templateArchive** defaults to a value of 'volojs/create-template', but 9 | any archive value that is usable by **add** can work here instead. The only 10 | restriction is that the archive value should resolve to a zip file and 11 | a #specific/file.js type of archive value should not be used. 12 | 13 | For some examples of GitHub repos that can be used as project templates, visit 14 | the "create templates" page: 15 | 16 | https://github.com/volojs/volo/wiki/Create-templates 17 | -------------------------------------------------------------------------------- /commands/help.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var commands = require('../lib/commands'), 11 | colors = require('colors'), 12 | boldRegExp = /\*\*([^\*]+)\*\*/g, 13 | codeRegExp = /`([^\`]+)`/g, 14 | help; 15 | 16 | colors.mode = 'console'; 17 | 18 | function colorize(text) { 19 | var raw = text.split('\n'); 20 | 21 | //Originally inspired by Gord Tanner's markdown-to color 22 | //approach: 23 | // https://git-wip-us.apache.org/repos/asf?p=incubator-ripple.git;a=blob_plain;f=lib/cli/help.js;hb=HEAD 24 | return raw.map(function (line) { 25 | if (line.match(/^# /)) { 26 | return line.replace(/^# /, '').inverse; 27 | } 28 | else if (line.match(/^## /)) { 29 | return line.replace(/^## /, '').inverse; 30 | } 31 | else if (line.match(/^ {4}/)) { 32 | return line.green; 33 | } else { 34 | line = line.replace(boldRegExp, function (match, text) { 35 | return text.bold; 36 | }); 37 | 38 | line = line.replace(codeRegExp, function (match, text) { 39 | return text.green; 40 | }); 41 | } 42 | return ' ' + line; 43 | }).join("\n"); 44 | } 45 | 46 | help = { 47 | summary: 'Gives more detailed help on a volo command.', 48 | 49 | doc: '## Usage\n\n volo help commandName', 50 | 51 | run: function (d, v, namedArgs, commandName) { 52 | commands.get(commandName).then(function (command) { 53 | var doc; 54 | if (command) { 55 | doc = command.summary || ''; 56 | doc += (doc && command.doc ? '\n\n' : '') + (command.doc || ''); 57 | 58 | if (!doc) { 59 | doc = commandName + ' does not have any documentation.'; 60 | } 61 | 62 | d.resolve(colorize(doc)); 63 | } else { 64 | d.reject('Unknown command: ' + commandName); 65 | } 66 | }).fail(d.reject); 67 | } 68 | }; 69 | 70 | module.exports = help; 71 | -------------------------------------------------------------------------------- /commands/info.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | 8 | /*jslint node: true, nomen: true, regexp: true */ 9 | /*global console, process */ 10 | 11 | 12 | 'use strict'; 13 | 14 | var path = require('path'), 15 | q = require('q'), 16 | archive = require('../lib/archive'), 17 | github = require('../lib/github'), 18 | file = require('../lib/file'), 19 | info; 20 | 21 | info = { 22 | summary: 'Get info on a repo', 23 | 24 | doc: file.readFile(path.join(__dirname + '/info/doc.md')), 25 | 26 | //Validate any arguments here. 27 | validate: function (namedArgs, archiveName) { 28 | if (!archiveName) { 29 | return new Error('An archiveName/query needs to be specified: volo info [query]'); 30 | } 31 | }, 32 | 33 | run: function (deferred, v, namedArgs, archiveName) { 34 | deferred.resolve(info.api.get(archiveName, namedArgs.volo.resolve) 35 | .then(function (results) { 36 | return results ? JSON.stringify(results, null, ' ') : 37 | 'Not able to find info on ' + archiveName + '.'; 38 | })); 39 | }, 40 | api: { 41 | get: function (archiveName, resolve) { 42 | var archiveInfo; 43 | return archive.resolve(archiveName, resolve) 44 | .then(function (info) { 45 | archiveInfo = info; 46 | 47 | if (archiveInfo && archiveInfo.scheme === 'github') { 48 | //Get all the versions for this repo 49 | return github.versionTags(archiveInfo.ownerPlusRepo); 50 | } 51 | }) 52 | .then(function (tags) { 53 | archiveInfo.versionTags = tags; 54 | return archiveInfo; 55 | }); 56 | } 57 | } 58 | }; 59 | 60 | module.exports = info; 61 | -------------------------------------------------------------------------------- /commands/info/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo info query 4 | 5 | where **query** is a string used for the search. info only works for queries 6 | that will go to GitHub. 7 | 8 | To get info on 'jquery': 9 | 10 | volo info jquery 11 | 12 | To get info on a particular owner/repo: 13 | 14 | volo info jrburke/requirejs 15 | -------------------------------------------------------------------------------- /commands/install.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | 8 | /*jslint node: true, nomen: true, regexp: true */ 9 | /*global console, process */ 10 | 11 | 12 | 'use strict'; 13 | 14 | 15 | var install = {}, 16 | lang = require('../lib/lang'), 17 | add = require('./add'); 18 | 19 | lang.mixin(install, add); 20 | 21 | delete install.doc; 22 | install.summary = 'An alias for "add".'; 23 | 24 | module.exports = install; 25 | -------------------------------------------------------------------------------- /commands/npmrel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true, regexp: true */ 8 | /*global console, process */ 9 | 'use strict'; 10 | 11 | var fs = require('fs'), 12 | path = require('path'), 13 | q = require('q'), 14 | packageJson = require('../lib/packageJson'), 15 | file = require('../lib/file'), 16 | amdify = require('./amdify'), 17 | add = require('./add'), 18 | makeMainAmdAdapter = amdify.api.makeMainAmdAdapter, 19 | amdConvert = amdify.api.convert, 20 | requireRegExp = /require\s*\(\s*['"]([^"']+)["']\s*\)/g, 21 | npmrel; 22 | 23 | function getNodePackages(dir, registry) { 24 | fs.readdirSync(dir).forEach(function (filePath) { 25 | var fullPath = path.resolve(dir, filePath), 26 | stat = fs.statSync(fullPath); 27 | 28 | if (stat.isDirectory()) { 29 | if (filePath === 'node_modules') { 30 | fs.readdirSync(fullPath).forEach(function (pkgName) { 31 | var pkgStat = fs.statSync(fullPath); 32 | if (pkgStat.isDirectory()) { 33 | registry[pkgName] = path 34 | .resolve(fullPath, pkgName) 35 | //Normalize on front slashes 36 | .replace(/\\/g, '/'); 37 | } 38 | }); 39 | } 40 | 41 | try { 42 | getNodePackages(fullPath, registry); 43 | } catch (e) { 44 | //Just eat the errors. Probably a bad symlink. 45 | } 46 | } 47 | }); 48 | } 49 | 50 | function relativize(pkgPath, filePath) { 51 | var pkgParts = pkgPath.split('/'), 52 | fileParts = filePath.split('/'), 53 | i; 54 | 55 | while (pkgParts[0] === fileParts[0]) { 56 | pkgParts.shift(); 57 | fileParts.shift(); 58 | } 59 | 60 | if (fileParts.length > 1) { 61 | //relative path is number of .. for fileParts, then adding pkgParts 62 | for (i = 1; i < fileParts.length; i += 1) { 63 | pkgParts.unshift('..'); 64 | } 65 | } else { 66 | //A sibling file 67 | pkgParts.unshift('.'); 68 | } 69 | 70 | return pkgParts.join('/'); 71 | } 72 | 73 | npmrel = { 74 | summary: 'Converts npm-installed, nested node_modules to use relative IDs.', 75 | 76 | doc: file.readFile(path.join(__dirname, '/npmrel/doc.md')), 77 | 78 | validate: function (namedArgs, targetDir) { 79 | if (!targetDir || !file.exists(targetDir)) { 80 | return new Error('Please pass a target directory to convert.'); 81 | } 82 | return undefined; 83 | }, 84 | 85 | run: function (d, v, namedArgs, targetDir) { 86 | targetDir = path.resolve(targetDir).replace(/\\/g, '/'); 87 | 88 | var registry = {}, 89 | promise = q.fcall(function () {}), 90 | pkg, prop, pkgPath, targetId, lastSegment; 91 | 92 | //Include the targetDir in the registry. 93 | targetId = targetDir.split('/'); 94 | targetId = targetId[targetId.length - 1]; 95 | registry[targetId] = targetDir; 96 | 97 | //Find all the the packages in the node_modules 98 | getNodePackages(targetDir, registry); 99 | 100 | //For each package, make sure there is a top level adapter module 101 | //that bridges to the main module. 102 | for (prop in registry) { 103 | if (registry.hasOwnProperty(prop)) { 104 | //Clean directories/files not needed. Do this before 105 | //converting modules to reduce the amount of directories 106 | //and unnecessary work. 107 | add.api.discard(registry[prop]); 108 | } 109 | } 110 | 111 | //Now find all JS files to scan for dependencies and convert. 112 | file.getFilteredFileList(targetDir, /\.js$/).forEach(function (file) { 113 | var contents = v.read(file); 114 | 115 | //Convert dependencies to be relative ones. 116 | contents = contents.replace(requireRegExp, function (match, id) { 117 | //Remove any trailing ".js" on the ID because that does 118 | //not work for AMD. 119 | id = id.replace(/\.js$/, ''); 120 | 121 | var parts = id.split('/'), 122 | prefix = parts[0]; 123 | 124 | if (registry[prefix]) { 125 | parts[0] = relativize(registry[prefix], file); 126 | return "require('" + 127 | parts.join('/') + 128 | "')"; 129 | } else { 130 | return "require('" + id + "')"; 131 | } 132 | }); 133 | 134 | v.write(file, contents); 135 | 136 | //Convert the module to AMD, but do not freak if it fails, 137 | //probably malformed JS anyway. 138 | 139 | promise = promise.then(function (msg) { 140 | var dconvert = q.defer(); 141 | try { 142 | amdConvert(file, null, null, null, { 143 | commonJs: true 144 | }).then(function () { 145 | dconvert.resolve(); 146 | }, function (err) { 147 | //Do not care if it errors out, probably 148 | //malformed to start. 149 | dconvert.resolve(); 150 | }); 151 | } catch (e) { 152 | dconvert.resolve(); 153 | } 154 | return dconvert.promise; 155 | }); 156 | }); 157 | 158 | //For each package, make sure there is a top level adapter module 159 | //that bridges to the main module. 160 | for (prop in registry) { 161 | if (registry.hasOwnProperty(prop)) { 162 | pkgPath = registry[prop]; 163 | pkg = packageJson(pkgPath); 164 | lastSegment = pkgPath.split('/'); 165 | lastSegment = lastSegment[lastSegment.length - 1]; 166 | if (pkg && pkg.data && pkg.data.main) { 167 | makeMainAmdAdapter(pkg.data.main, './' + lastSegment, pkgPath + '.js'); 168 | } 169 | } 170 | } 171 | 172 | d.resolve(promise); 173 | } 174 | }; 175 | 176 | module.exports = npmrel; 177 | -------------------------------------------------------------------------------- /commands/npmrel/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo npmrel targetDir 4 | 5 | Command line arguments: 6 | 7 | **targetDir** is the name of the directory that contains a package installed 8 | by npm. 9 | 10 | ## Details 11 | 12 | npm installs a package with its dependencies in nested node_modules storage. 13 | Node's module ID-to-path resolution can do multiple IO operations to find those 14 | modules. However, in an AMD, browser-based deployment, only one IO operation per 15 | module, at most, is advised. 16 | 17 | This command determines the nested package names and locations, and updates 18 | any require('') calls in the modules to use a relative module ID so that the 19 | modules can be used in an AMD project. 20 | 21 | It then converts the modules to have an AMD define() wrapper. 22 | 23 | ## Example 24 | 25 | > mkdir node_modules 26 | > npm install foo 27 | > volo npmrel node_modules/foo 28 | 29 | Now you can move node_modules/foo.js and node_modules/foo to the baseUrl of 30 | your AMD project. 31 | 32 | ## Notes 33 | 34 | This will not work for all node modules. In particular, if the module calculates 35 | the require dependency: 36 | 37 | var a = require(someCondition ? 'a' : 'a1'); 38 | 39 | or 40 | 41 | var name = prefix + '/' + action, 42 | impl = require(name); 43 | 44 | This command also does not create "browser friendly" versions of the core 45 | modules like "fs". It only converts module IDs that map to packages that 46 | are in the local node_modules directory, but keeps all other IDs intact. 47 | -------------------------------------------------------------------------------- /commands/remove.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2013, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var path = require('path'), 12 | glob = require('glob'), 13 | packageJson = require('../lib/packageJson'), 14 | file = require('../lib/file'); 15 | 16 | module.exports = { 17 | 18 | summary: 'Removes a dependency listed in project\'s package.json from the project.', 19 | 20 | doc: file.readFile(path.join(__dirname + '/remove/doc.md')), 21 | 22 | validate: function (namedArgs, depName) { 23 | if (!depName) { 24 | return new Error('Please specify a name to use for the created project.'); 25 | } 26 | 27 | var errMsg, 28 | pkg = packageJson('.'), 29 | deps = pkg.data && pkg.data.volo && pkg.data.volo.dependencies; 30 | 31 | if (!pkg.file) { 32 | return new Error('No package.json found in: ' + path.resolve('.')); 33 | } 34 | if (!deps) { 35 | return new Error('No volo.dependencies found in package.json'); 36 | } 37 | if (!deps[depName]) { 38 | errMsg = '"' + depName + '" not found in volo.dependencies. Choices are:'; 39 | Object.keys(deps).forEach(function (key) { 40 | errMsg += '\n' + key; 41 | }); 42 | return new Error(errMsg); 43 | } 44 | 45 | namedArgs._privateRemove = { 46 | pkg: pkg 47 | }; 48 | }, 49 | 50 | run: function (deferred, v, namedArgs, depName) { 51 | var pkg = namedArgs._privateRemove.pkg, 52 | baseDir = pkg.getBaseDir(), 53 | files = glob.sync(depName + '*', { 54 | cwd: baseDir 55 | }); 56 | 57 | //Remove associated files 58 | if (files) { 59 | files.forEach(function (fileName) { 60 | file.rm(path.join(baseDir, fileName)); 61 | }); 62 | } 63 | 64 | //Update the package.json to not have the value 65 | delete pkg.data.volo.dependencies[depName]; 66 | pkg.save(); 67 | deferred.resolve(); 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /commands/remove/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo remove localName 4 | 5 | where **localName** is a local dependency name listed in the volo.dependencies 6 | section of the project's package.json. 7 | 8 | ## Details 9 | 10 | **remove** will confirm the localName is in the volo.dependencies section of 11 | the package.json file, then if there, removes the entry from the 12 | volo.dependencies section after removing any localName .js and/or directory 13 | for that localName dependency. 14 | 15 | **remove** will only remove the explicitly listed dependency. It will not 16 | remove any sub-dependencies. 17 | -------------------------------------------------------------------------------- /commands/search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var qutil = require('../lib/qutil'), 11 | fs = require('fs'), 12 | path = require('path'), 13 | baseDir = __dirname, 14 | search; 15 | 16 | search = { 17 | summary: 'Searches for a repo name.', 18 | 19 | doc: fs.readFileSync(path.join(__dirname, '/search/doc.md'), 'utf8'), 20 | 21 | flags: { 22 | 'amd': 'amd' 23 | }, 24 | 25 | validate: function (namedArgs, query) { 26 | if (!query) { 27 | return new Error('Please pass a query.'); 28 | } 29 | }, 30 | 31 | run: function (d, v, namedArgs, query) { 32 | d.resolve(search.api(query, namedArgs).then(function (results) { 33 | return results ? JSON.stringify(results, null, ' ') : 34 | 'No results. Trying searching with a web ' + 35 | 'browser at https://github.com,\nthen once you ' + 36 | 'find the owner/repo combination, use it in volo.'; 37 | })); 38 | }, 39 | 40 | api: function(query, options, callback, errback) { 41 | var index = query.indexOf(':'), 42 | scheme = 'github', 43 | d = qutil.convert(callback, errback), 44 | searchId, searchMod; 45 | 46 | //Figure out the scheme. 47 | if (index !== -1) { 48 | scheme = query.substring(0, index); 49 | query = query.substring(index + 1); 50 | } 51 | 52 | //Figure out if there is a resolver for the given scheme. 53 | searchId = path.join(baseDir, '..', 'lib', 'search', scheme + '.js'); 54 | 55 | try { 56 | searchMod = require(searchId); 57 | d.resolve(searchMod(query, options)); 58 | } catch (e) { 59 | d.reject('Do not have a volo search provider for scheme: ' + scheme + ': ' + e); 60 | } 61 | 62 | return d.promise; 63 | } 64 | }; 65 | 66 | module.exports = search; -------------------------------------------------------------------------------- /commands/search/doc.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | volo search [flags] query 4 | 5 | where the allowed flags are: 6 | 7 | * **-amd**: Indicates an AMD version, if available, is prefered. This means 8 | using some AMD overrides from the volo config, if there is one that matches 9 | the query, and gives that override first result status. 10 | 11 | and **query** is a string used for the search. The query can have a scheme on 12 | it, to specify what type of search module to use. For example: 13 | 14 | volo search foo:bar 15 | 16 | uses the foo search module to get search results on 'bar'. 17 | 18 | The default query scheme is 'github', so to search for the right repo to use 19 | for installing 'jquery': 20 | 21 | volo search jquery 22 | 23 | For the default GitHub query scheme, `search` just searches GitHub for 24 | `owner/repo` names that match the search. Those repo names could then be used 25 | for `volo add` or `volo create` calls. 26 | 27 | However, `search` does not distinguish which repos would be good to use for 28 | `add` or `create`. Examining the repo's README on GitHub will determine how that 29 | repo should be used. 30 | -------------------------------------------------------------------------------- /commands/update.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2013, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var update = {}, 12 | lang = require('../lib/lang'), 13 | add = require('./add'), 14 | oldRun = add.run; 15 | 16 | lang.mixin(update, add); 17 | 18 | delete update.doc; 19 | update.summary = 'Updates a dependency. Equivalent to doing `volo add -f`. See add docs for options.'; 20 | 21 | update.run = function (d, v, namedArgs) { 22 | namedArgs.force = true; 23 | return oldRun.apply(update, [].slice.call(arguments, 0)); 24 | }; 25 | 26 | module.exports = update; 27 | -------------------------------------------------------------------------------- /docs/releaseNotes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | Some release notes for a few recent versions of volo. Will be trimmed every so 4 | often, but use git history to find details on other releases. 5 | 6 | ## 0.1.0 7 | 8 | * volo.type support for explicitly stating a directory should be kept instead 9 | of extracting a single JS file from an archive. 10 | 11 | ## 0.0.9 12 | 13 | * added `volo search`, now shortnames like 'jquery' work fo `volo add`. 14 | * support for volo.url and volo.archive in package.json. 15 | 16 | ## 0.0.8 17 | 18 | * amdify depends allows settings a local name for a dependency: depends=jquery=$ 19 | * rejuvenate should now maintain executable permissions. 20 | 21 | ## 0.0.7 22 | 23 | * Allow flags and arguments to be passed to subcommands. 24 | * Fix some docs. 25 | 26 | ## 0.0.6 27 | 28 | * Windows support. 29 | * Switched to using zip instead of tar.gz so that Windows would work. 30 | -------------------------------------------------------------------------------- /docs/workingWithCode.md: -------------------------------------------------------------------------------- 1 | # Working with the volo code 2 | 3 | ## Contributing 4 | 5 | * [Open an issue](https://github.com/volojs/volo/issues) 6 | * Submit a pull request. 7 | * For bigger design decisions or to get more feedback, contact the 8 | [volojs list](http://groups.google.com/group/volojs). 9 | 10 | Pull requests/code that is more than a simple one or two line change 11 | will not be accepted unless you have signed a 12 | [Dojo Foundation CLA](http://www.dojofoundation.org/about/cla). 13 | 14 | More information on why there is a CLA and the code style to use, see the 15 | [RequireJS Contributing page](requirejs.org/docs/contributing.html) since that 16 | info generally applies to volo too. 17 | 18 | ## Developing 19 | 20 | npm uninstall -g volo 21 | git clone git://github.com/volojs/volo.git 22 | cd volo 23 | npm link volo 24 | 25 | Now any development changes you do should show up in your next volo command. 26 | You can run volo as you normally would as a global command. 27 | 28 | Switch branches to try out different versions of volo. 29 | 30 | ## Debugging 31 | 32 | Make sure to `npm install node-inspector` then you can do this: 33 | 34 | node --debug-brk `which volo` [command] 35 | 36 | That will drop you into the debugger. It is best to put a `debugger` statement 37 | in the code where you want to stop execution, otherwise it will be time 38 | consuming to step through the individual statements and modules. 39 | 40 | If you are on Windows using a git-bash shell, this command may work better: 41 | 42 | node --debug-brk "`dirname "\`which volo\`"`/node_modules/volo/bin/volo" 43 | 44 | ## Running Tests 45 | 46 | cd tests 47 | ./all.js 48 | 49 | Runs the tests. You will need a network connection with access to GitHub to run 50 | the tests. 51 | -------------------------------------------------------------------------------- /lib/archive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | /*global console */ 9 | 10 | 'use strict'; 11 | 12 | var q = require('q'), 13 | fs = require('fs'), 14 | path = require('path'), 15 | exists = require('./exists'), 16 | net = require('./net'), 17 | mime = require('./mime'), 18 | baseDir = __dirname, 19 | endSlashIndexRegExp = /[\/\\]$/, 20 | zipRegExp = /\.zip$/, 21 | //Regexp used to strip off file extension 22 | fileExtRegExp = /\.tar\.gz$|\.\w+$/, 23 | handledSchemes = { 24 | http: true, 25 | https: true, 26 | local: true, 27 | symlink: true 28 | }, 29 | archiveLib; 30 | 31 | module.exports = (archiveLib = { 32 | /** 33 | * Resolves an archive value to a .tar.gz http/https URL. 34 | * Depends on specific resolver modules to do the work. 35 | * If no scheme is on the value, the default is assumed 36 | * to be a github resource. 37 | * @param {String} archive a string that can somehow resolved to 38 | * an http/https URL to a .tar.gz or individual file. 39 | * 40 | * @param {Function} [resolve] an optional resolve function to use 41 | * to resolve relative local file paths. 42 | * 43 | * @param {Object} [options] an optional object that contains options 44 | * for the resolution. An example is amd: true to indicate this is an 45 | * AMD-based project. 46 | * 47 | * Returns a promise with the properly resolved value being an 48 | * object with the following properties: 49 | * 50 | * * url: the http/https URL to fetch the archive or single file 51 | * * isArchive: true if the URL points to a .tar.gz file. 52 | * * fragment: if a fragment ID (# part) was specified on the original 53 | * archive value, normally meaning a file within an archive 54 | * * localName: a possible local name to use for the extracted archive 55 | * value. Useful to use when an explicit one is not 56 | * specified by the user. 57 | */ 58 | resolve: function (archive, resolve, options) { 59 | 60 | var d = q.defer(), 61 | index = archive.indexOf(':'), 62 | fragIndex = archive.indexOf('#'), 63 | fragment = null, 64 | isSingleFile = false, 65 | localRefName, scheme, resolverId, localName, resolveMod, 66 | isArchive; 67 | 68 | //If there is a specific file desired inside the archive, peel 69 | //that off. 70 | if (fragIndex !== -1) { 71 | fragment = archive.substring(fragIndex + 1); 72 | archive = archive.substring(0, fragIndex); 73 | } 74 | 75 | //Make sure the archive does not end in a slash, since slashes 76 | //are important, particularly for github urls. 77 | archive = archive.replace(endSlashIndexRegExp, ''); 78 | 79 | //Figure out the scheme. Default is github, unless a local 80 | //path matches. 81 | if (index === -1) { 82 | if (archive.indexOf('.') === 0 || exists(archive)) { 83 | scheme = 'local'; 84 | } else { 85 | scheme = 'github'; 86 | } 87 | } else { 88 | scheme = archive.substring(0, index); 89 | archive = archive.substring(index + 1); 90 | } 91 | 92 | if (handledSchemes.hasOwnProperty(scheme)) { 93 | //localName is the file name without extension. If a .tar.gz 94 | //file, then a does not include .tar.gz 95 | if (fragment) { 96 | localRefName = fragment; 97 | } else { 98 | localRefName = archive; 99 | } 100 | localName = localRefName.substring(localRefName.lastIndexOf('/') + 1); 101 | localName = localName.replace(fileExtRegExp, ''); 102 | 103 | //Resolve relative paths for this particular archive 104 | //resolve call. 105 | if ((scheme === 'symlink' || scheme === 'local') && resolve) { 106 | archive = resolve(archive); 107 | 108 | //If the archive source does not exist, bail. 109 | if (!exists(archive)) { 110 | d.reject(new Error(archive + ' does not exist')); 111 | return d.promise; 112 | } else { 113 | if (fs.statSync(archive).isFile()) { 114 | isSingleFile = true; 115 | } 116 | isArchive = archiveLib.isArchive(archive); 117 | } 118 | } 119 | 120 | q.fcall(function () { 121 | if ((scheme === 'http' || scheme === 'https')) { 122 | return net.head(scheme + ':' + archive, { 123 | followRedirects: true 124 | }).then(function (response) { 125 | var contentType = response.headers['content-type']; 126 | if (mime.archiveTypes[contentType]) { 127 | //A zip file 128 | isArchive = true; 129 | } else { 130 | isArchive = false; 131 | isSingleFile = true; 132 | } 133 | }); 134 | } 135 | }).then(function () { 136 | d.resolve({ 137 | id: scheme + ':' + archive + (fragment ? '#' + fragment : ''), 138 | scheme: scheme, 139 | url: scheme + ':' + archive, 140 | isArchive: isArchive, 141 | isSingleFile: isSingleFile, 142 | fragment: fragment, 143 | localName: localName 144 | }); 145 | 146 | }).fail(d.reject); 147 | } else { 148 | //Figure out if there is a resolver for the given scheme. 149 | resolverId = path.join(baseDir, 'resolve', scheme + '.js'); 150 | 151 | try { 152 | resolveMod = require(resolverId); 153 | resolveMod(archive, fragment, options, d.resolve, d.reject); 154 | } catch (e) { 155 | d.reject('Do not have a volo resolver for scheme: ' + scheme + ': ' + e); 156 | } 157 | } 158 | 159 | return d.promise; 160 | }, 161 | 162 | /** 163 | * Just tests if the given URL ends in .tar.gz 164 | */ 165 | isArchive: function (url) { 166 | return zipRegExp.test(url); 167 | } 168 | }); 169 | -------------------------------------------------------------------------------- /lib/commands.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true, plusplus: true */ 8 | /*global define */ 9 | 'use strict'; 10 | 11 | var fs = require('fs'), 12 | path = require('path'), 13 | q = require('q'), 14 | colors = require('colors'), 15 | v = require('./v'), 16 | jsExtRegExp = /\.js$/, 17 | registry = {}, 18 | commands; 19 | 20 | colors.mode = 'console'; 21 | 22 | commands = { 23 | register: function (id, value) { 24 | //Only take the first part of the ID 25 | id = id.split('/')[0]; 26 | 27 | registry[id] = value; 28 | return value; 29 | }, 30 | 31 | get: function (id) { 32 | return q.fcall(function () { 33 | var command = id && registry.hasOwnProperty(id) && registry[id]; 34 | if (id && !command) { 35 | return require('./volofile')('.') 36 | .then(function (voloMod) { 37 | if (voloMod) { 38 | return voloMod[id]; 39 | } 40 | }); 41 | } 42 | return command; 43 | }) 44 | .then(function (command) { 45 | if (id && !command) { 46 | //Attempt to load it. 47 | try { 48 | command = require(id); 49 | } catch (e) { 50 | //No command by that id, just eat the error. 51 | } 52 | } 53 | return command; 54 | }); 55 | }, 56 | 57 | list: function () { 58 | return require('./volofile')('.') 59 | .then(function (voloMod) { 60 | var messages = [], 61 | keys; 62 | 63 | if (voloMod) { 64 | keys = Object.keys(voloMod); 65 | keys.sort(); 66 | 67 | messages.push(['volofile commands:']); 68 | messages.push(['']); 69 | keys.forEach(function (key) { 70 | messages.push(key.bold + ': ' + (voloMod[key].summary || '')); 71 | }); 72 | } 73 | 74 | keys = Object.keys(registry); 75 | keys.sort(); 76 | 77 | messages.push(['']); 78 | messages.push(['Standard volo commands:']); 79 | messages.push(['']); 80 | keys.forEach(function (key) { 81 | messages.push(key.bold + ': ' + (registry[key].summary || '')); 82 | }); 83 | 84 | return messages.join('\n'); 85 | }); 86 | }, 87 | 88 | run: function (command, venv, namedArgs /*other args can be passed*/) { 89 | var d = q.defer(), 90 | shellCommand, args; 91 | 92 | if (!venv) { 93 | venv = v(path.resolve('.')).env; 94 | } 95 | 96 | if (!command) { 97 | d.resolve(); 98 | } else { 99 | if (typeof command === 'function') { 100 | //Just normalize to advanced structure. 101 | command = { 102 | run: command 103 | }; 104 | } else if (typeof command === 'string' || 105 | Array.isArray(command)) { 106 | command = { 107 | run: command 108 | }; 109 | } 110 | 111 | //Now convert run to a function if it is not. 112 | //This allows a command to have depends and 113 | //a run property that is just a shell string or array 114 | //of shell strings 115 | if (typeof command.run === 'string') { 116 | shellCommand = command.run; 117 | command.run = function (d, v, namedArgs) { 118 | d.resolve(v.shell(shellCommand, { 119 | useConsole: true 120 | })); 121 | }; 122 | } else if (Array.isArray(command.run)) { 123 | shellCommand = command.run; 124 | command.run = function (d, v, namedArgs) { 125 | d.resolve(v.sequence(shellCommand, { 126 | useConsole: true 127 | })); 128 | }; 129 | } 130 | 131 | args = [].slice.call(arguments, 2); 132 | 133 | q.fcall(function () { 134 | if (command.depends && command.depends.length) { 135 | var result = q.resolve(); 136 | command.depends.forEach(function (commandName) { 137 | result = result.then(function () { 138 | return venv.command(commandName); 139 | }); 140 | }); 141 | return result; 142 | } 143 | return undefined; 144 | }) 145 | .then(function () { 146 | var commandDeferred = q.defer(), 147 | err; 148 | 149 | //Call validate if it is on the command. 150 | if (command.validate) { 151 | err = command.validate.apply(command, args); 152 | if (err) { 153 | commandDeferred.reject(err); 154 | return commandDeferred.promise; 155 | } 156 | } 157 | 158 | try { 159 | command.run.apply(command, [commandDeferred, venv].concat(args)); 160 | } catch (e) { 161 | //Try to give more details on the error by giving the 162 | //first two lines of the stack trace. If a volofile is 163 | //in play, it should give a line number in the volofile. 164 | commandDeferred.reject(e.stack.toString().split('\n').slice(0, 2).join('\n')); 165 | } 166 | 167 | return commandDeferred.promise; 168 | }) 169 | .then(d.resolve, d.reject); 170 | } 171 | 172 | return d.promise; 173 | } 174 | }; 175 | 176 | module.exports = commands; 177 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | /*global console */ 9 | 10 | 'use strict'; 11 | var fs = require('fs'), 12 | path = require('path'), 13 | lang = require('./lang'), 14 | file = require('./file'), 15 | homeDir = require('./homeDir'), 16 | overrideConfigUrl = path.join(homeDir, '.voloconfig'), 17 | localConfigUrl = path.join(homeDir, '.voloconfiglocal'), 18 | version = '0.3.8', 19 | 20 | data, overrideConfig, localConfig, contents; 21 | 22 | // The defaults to use. 23 | data = lang.delegate({ 24 | "volo": { 25 | version: version 26 | }, 27 | 28 | "npmRegistry": "https://registry.npmjs.org/", 29 | 30 | "github": { 31 | "userAgent": "volo/" + version, 32 | "scheme": "https", 33 | "host": "github.com", 34 | "apiHost": "api.github.com", 35 | "searchHost": "api.github.com", 36 | "rawUrlPattern": "https://raw.githubusercontent.com/{owner}/{repo}/{version}/{file}", 37 | "searchPath": "/search/repositories?q={query}&language=JavaScript", 38 | "typeOverrides": { 39 | "dojo/dijit": "directory" 40 | }, 41 | 42 | "auth": { 43 | "domain": "https://api.github.com", 44 | "authPath": "/authorizations", 45 | "scopes": ["repo"], 46 | "note": "Allow volo to interact with your repos.", 47 | "note_url": "https://github.com/volojs/volo" 48 | } 49 | }, 50 | 51 | "command": { 52 | "add": { 53 | "discard": [ 54 | ".gitignore", 55 | "test", 56 | "tests", 57 | "doc", 58 | "docs", 59 | "example", 60 | "examples", 61 | "demo", 62 | "demos" 63 | ] 64 | } 65 | } 66 | }); 67 | 68 | //Allow a local config at homeDir + '.config.js' 69 | if (file.exists(overrideConfigUrl)) { 70 | contents = (fs.readFileSync(overrideConfigUrl, 'utf8') || '').trim(); 71 | 72 | if (contents) { 73 | overrideConfig = JSON.parse(contents); 74 | lang.mixin(data, overrideConfig, true); 75 | } 76 | } 77 | 78 | module.exports = { 79 | get: function () { 80 | return data; 81 | }, 82 | 83 | //Simple local config. No fancy JSON object merging just plain mixing 84 | //of top level properties. 85 | getLocal: function () { 86 | var contents; 87 | 88 | if (!localConfig) { 89 | if (file.exists(localConfigUrl)) { 90 | 91 | contents = (fs.readFileSync(localConfigUrl, 'utf8') || '').trim(); 92 | 93 | if (contents) { 94 | localConfig = JSON.parse(contents); 95 | } 96 | } 97 | 98 | if (!localConfig) { 99 | localConfig = {}; 100 | } 101 | } 102 | 103 | return localConfig; 104 | }, 105 | 106 | saveLocal: function () { 107 | //Make sure the directory exists 108 | try { 109 | file.mkdirs(path.dirname(localConfigUrl)); 110 | fs.writeFileSync(localConfigUrl, JSON.stringify(localConfig, null, ' ')); 111 | } catch (e) { 112 | console.error('Cannot save local config, continuing without saving.'); 113 | return ''; 114 | } 115 | 116 | return localConfigUrl; 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /lib/download.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true, plusplus: true */ 8 | /*global console */ 9 | 10 | 'use strict'; 11 | 12 | var https = require('https'), 13 | http = require('http'), 14 | fs = require('fs'), 15 | path = require('path'), 16 | urlLib = require('url'), 17 | colors = require('colors'), 18 | qutil = require('./qutil'), 19 | file = require('./file'), 20 | lang = require('./lang'), 21 | localRegExp = /^local\:/; 22 | 23 | colors.mode = 'console'; 24 | 25 | function download(urlArgs, localPath, callback, errback) { 26 | var url, parts, protocol, writeStream, req, 27 | d = qutil.convert(callback, errback), 28 | dir = path.dirname(localPath); 29 | 30 | if (typeof urlArgs === 'string') { 31 | urlArgs = { 32 | url: urlArgs 33 | }; 34 | } 35 | 36 | url = urlArgs.url; 37 | 38 | try { 39 | //Handle local URLs 40 | if (localRegExp.test(url)) { 41 | url = url.substring(url.indexOf(':') + 1); 42 | if (fs.statSync(url).isDirectory()) { 43 | file.copyDir(url, localPath); 44 | } else { 45 | file.copyFile(url, localPath); 46 | } 47 | d.resolve(localPath); 48 | } else { 49 | 50 | //Do the network fetch. 51 | parts = urlLib.parse(url); 52 | protocol = parts.protocol === 'https:' ? https : http; 53 | 54 | if (!file.exists(dir)) { 55 | file.mkdirs(dir); 56 | } 57 | writeStream = fs.createWriteStream(localPath); 58 | 59 | 60 | req = protocol.request(parts, function (response) { 61 | //console.log("statusCode: ", response.statusCode); 62 | //console.log("headers: ", response.headers); 63 | try { 64 | if (response.statusCode === 200) { 65 | 66 | console.log(('Downloading: ' + url).grey); 67 | 68 | //Pipe will automatically call writeStream's 69 | //end method. 70 | response.pipe(writeStream); 71 | 72 | response.on('error', function (err) { 73 | d.reject(err); 74 | }); 75 | 76 | //Write stream is done, so we can continue. 77 | writeStream.on('close', function () { 78 | d.resolve(localPath); 79 | }); 80 | } else if (response.statusCode === 301 || 81 | response.statusCode === 302) { 82 | //Redirect, try the new location 83 | urlArgs.url = response.headers.location; 84 | download(urlArgs, localPath) 85 | .then(d.resolve, d.reject); 86 | } else { 87 | d.reject(new Error('Download failed, HTTP code: ' + 88 | response.statusCode + ': ' + url)); 89 | } 90 | } catch (e) { 91 | d.reject(e); 92 | } 93 | }).on('error', function (e) { 94 | d.reject(e); 95 | }); 96 | 97 | if (urlArgs.headers) { 98 | lang.eachProp(urlArgs.headers, function (value, headerName) { 99 | req.setHeader(headerName, value); 100 | }); 101 | } 102 | 103 | req.end(); 104 | } 105 | } catch (e) { 106 | d.reject(e); 107 | } 108 | 109 | return d.promise; 110 | } 111 | 112 | module.exports = download; -------------------------------------------------------------------------------- /lib/exists.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'); 3 | 4 | module.exports = fs.existsSync || path.existsSync; 5 | -------------------------------------------------------------------------------- /lib/file.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | /*global console */ 9 | 10 | 'use strict'; 11 | 12 | var fs = require('fs'), 13 | path = require('path'), 14 | exists = require('./exists'), 15 | qutil = require('./qutil'), 16 | exec = require('child_process').exec, 17 | file; 18 | 19 | function frontSlash(path) { 20 | return path.replace(/\\/g, '/'); 21 | } 22 | 23 | function findMatches(matches, dir, regExpInclude, regExpExclude, dirRegExpExclude) { 24 | if (exists(dir) && fs.statSync(dir).isDirectory()) { 25 | var files = fs.readdirSync(dir); 26 | files.forEach(function (filePath) { 27 | filePath = path.join(dir, filePath); 28 | if (exists(filePath)) { 29 | var stat = fs.statSync(filePath), 30 | ok = false; 31 | if (stat.isFile()) { 32 | ok = true; 33 | if (regExpInclude) { 34 | ok = filePath.match(regExpInclude); 35 | } 36 | if (ok && regExpExclude) { 37 | ok = !filePath.match(regExpExclude); 38 | } 39 | 40 | if (ok) { 41 | matches.push(filePath); 42 | } 43 | } else if (stat.isDirectory() && (!dirRegExpExclude || !dirRegExpExclude.test(filePath))) { 44 | findMatches(matches, filePath, regExpInclude, regExpExclude, dirRegExpExclude); 45 | } 46 | } 47 | }); 48 | } 49 | } 50 | 51 | file = { 52 | //Default exclusion regexp used by getFilteredFileList 53 | dirRegExpExclude: /(^|\/|\\)(\.git|\.hg|\.svn|CVS)/, 54 | 55 | /** 56 | * Recurses startDir and finds matches to the files that match 57 | * regExpFilters.include and do not match regExpFilters.exclude. 58 | * Or just one regexp can be passed in for regExpFilters, 59 | * and it will be treated as the "include" case. 60 | * 61 | * @param {String} startDir the directory to start the search 62 | * @param {RegExp} regExpInclude regexp to match files to include 63 | * @param {RegExp} [regExpExclude] regexp to exclude files. 64 | * @param {RegExp} [dirRegExpExclude] regexp to exclude directories. By default 65 | * ignores .git, .hg, .svn and CVS directories. 66 | * 67 | * @returns {Array} List of file paths. Could be zero length if no matches. 68 | */ 69 | getFilteredFileList: function (startDir, regExpInclude, regExpExclude, dirRegExpExclude) { 70 | var files = []; 71 | 72 | //By default avoid source control directories 73 | if (dirRegExpExclude === undefined) { 74 | dirRegExpExclude = file.dirRegExpExclude; 75 | } 76 | 77 | findMatches(files, startDir, regExpInclude, regExpExclude, dirRegExpExclude); 78 | 79 | return files; 80 | }, 81 | 82 | /** 83 | * Reads a file, synchronously. 84 | * @param {String} path the path to the file. 85 | */ 86 | readFile: function (path) { 87 | return fs.readFileSync(path, 'utf8'); 88 | }, 89 | 90 | exists: function (path) { 91 | return exists(path); 92 | }, 93 | 94 | /** 95 | * Recursively creates directories in dir string. 96 | * @param {String} dir the directory to create. 97 | */ 98 | mkdirs: function (dir) { 99 | dir = frontSlash(dir); 100 | 101 | var parts = dir.split('/'), 102 | currDir = '', 103 | first = true; 104 | 105 | parts.forEach(function (part) { 106 | //First part may be empty string if path starts with a slash. 107 | currDir += part + '/'; 108 | first = false; 109 | 110 | if (part) { 111 | if (!exists(currDir)) { 112 | fs.mkdirSync(currDir, 511); 113 | } 114 | } 115 | }); 116 | }, 117 | 118 | /** 119 | * Works on files and directories. Does not prompt just tries to delete 120 | * with no feedback. 121 | */ 122 | rm: function (dirOrFile) { 123 | if (!dirOrFile || !exists((dirOrFile = path.resolve(dirOrFile)))) { 124 | return undefined; 125 | } 126 | 127 | if (dirOrFile === '/') { 128 | throw new Error('file.rm() cannot handle /'); 129 | } 130 | 131 | function rm(target) { 132 | var stat = fs.statSync(target); 133 | if (stat.isDirectory()) { 134 | fs.readdirSync(target).forEach(function (file) { 135 | rm(path.resolve(target, file)); 136 | }); 137 | return fs.rmdirSync(target); 138 | } else { 139 | return fs.unlinkSync(target); 140 | } 141 | } 142 | 143 | return rm(dirOrFile); 144 | }, 145 | 146 | 147 | /** 148 | * Does a platform specific rm -rf on a directory. Like a boss. 149 | * This ticket may explain why doing sync rm like file.rm does 150 | * may not work on Windows: 151 | * https://github.com/joyent/node/issues/2451 152 | * and seems to explain an issue volo has on Windows when it tries 153 | * to remove the temp directory created for the "create" task. 154 | */ 155 | asyncPlatformRm: function (dir, callback, errback) { 156 | var d = qutil.convert(callback, errback), 157 | rmCommand = process.platform === 'win32' ? 158 | 'rmdir /S /Q ' : 159 | 'rm -rf '; 160 | 161 | if (!dir) { 162 | d.resolve(); 163 | } 164 | 165 | dir = path.resolve(dir); 166 | 167 | if (!exists(dir)) { 168 | d.resolve(); 169 | } 170 | 171 | if (dir === '/') { 172 | d.reject(new Error('file.rmdir cannot handle /')); 173 | } 174 | 175 | exec(rmCommand + dir, 176 | function (error, stdout, stderr) { 177 | if (error) { 178 | d.reject(error); 179 | } else { 180 | d.resolve(); 181 | } 182 | } 183 | ); 184 | 185 | return d.promise; 186 | }, 187 | 188 | /** 189 | * Returns the first directory found inside a directory. 190 | * The return results is dir + firstDir name. 191 | */ 192 | firstDir: function (dir) { 193 | var firstDir = null; 194 | 195 | fs.readdirSync(dir).some(function (file) { 196 | firstDir = path.join(dir, file); 197 | if (fs.statSync(firstDir).isDirectory()) { 198 | return true; 199 | } else { 200 | firstDir = null; 201 | return false; 202 | } 203 | }); 204 | 205 | return firstDir; 206 | }, 207 | 208 | copyDir: function (/*String*/srcDir, 209 | /*String*/destDir, 210 | /*RegExp?*/regExpFilter, 211 | /*boolean?*/onlyCopyNew, 212 | /*RegExp?*/regExpExclude, 213 | /*RegExp?*/dirRegExpExclude) { 214 | //summary: copies files from srcDir to destDir using the regExpFilter to determine if the 215 | //file should be copied. Returns a list file name strings of the destinations that were copied. 216 | regExpFilter = regExpFilter || /\w/; 217 | 218 | //Normalize th directory names, but keep front slashes. 219 | //path module on windows now returns backslashed paths. 220 | srcDir = frontSlash(path.normalize(srcDir)); 221 | destDir = frontSlash(path.normalize(destDir)); 222 | 223 | var fileNames = file.getFilteredFileList(srcDir, regExpFilter, regExpExclude, dirRegExpExclude), 224 | copiedFiles = [], i, srcFileName, destFileName; 225 | 226 | for (i = 0; i < fileNames.length; i++) { 227 | srcFileName = frontSlash(fileNames[i]); 228 | destFileName = srcFileName.replace(srcDir, destDir); 229 | 230 | if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) { 231 | copiedFiles.push(destFileName); 232 | } 233 | } 234 | 235 | return copiedFiles.length ? copiedFiles : null; //Array or null 236 | }, 237 | 238 | 239 | copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) { 240 | //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if 241 | //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred. 242 | var parentDir; 243 | 244 | //logger.trace("Src filename: " + srcFileName); 245 | //logger.trace("Dest filename: " + destFileName); 246 | 247 | //If onlyCopyNew is true, then compare dates and only copy if the src is newer 248 | //than dest. 249 | if (onlyCopyNew) { 250 | if (exists(destFileName) && fs.statSync(destFileName).mtime.getTime() >= fs.statSync(srcFileName).mtime.getTime()) { 251 | return false; //Boolean 252 | } 253 | } 254 | 255 | //Make sure destination dir exists. 256 | parentDir = path.dirname(destFileName); 257 | if (!exists(parentDir)) { 258 | file.mkdirs(parentDir); 259 | } 260 | 261 | fs.writeFileSync(destFileName, fs.readFileSync(srcFileName, 'binary'), 'binary'); 262 | 263 | return true; //Boolean 264 | } 265 | }; 266 | 267 | module.exports = file; 268 | -------------------------------------------------------------------------------- /lib/github.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true, regexp: true */ 8 | 9 | 10 | 'use strict'; 11 | var q = require('q'), 12 | https = require('https'), 13 | querystring = require('querystring'), 14 | semver = require('semver'), 15 | lang = require('./lang'), 16 | venv = require('./v'), 17 | githubAuth = require('./github/auth'), 18 | config = require('./config').get().github, 19 | scheme = config.scheme, 20 | version = require('./version'), 21 | host = config.host, 22 | apiHost = config.apiHost, 23 | 24 | //See if it is a semantic version range-ish thing. 25 | semVerRangeRegExp = /^(\^|>|<|~|\d+\.x|\d+\.\d+\.x)/, 26 | 27 | //Only use n, n.n, or n.n.n versions, and do not include ones 28 | //that are n.n.npre2 or n.n.nbeta2 29 | versionRegExp = /^(v)?(\d+\.)?(\d+\.)?(\d+)$/; 30 | 31 | //Helper to encode the query for search as an URL-encoded value. 32 | function escape(text) { 33 | //The V2 search API freaks with "." in the name. So convert them 34 | //just to escaped spaces (+) to get some kind of usable result. 35 | return querystring.escape(text).replace(/\./g, '+'); 36 | } 37 | 38 | /** 39 | * Sends a request to GitHub. Valid options: 40 | * method: the HTTP method to use. 41 | * content: the content to send in the body. Can be an object that will 42 | * be converted to JSON, or a string. 43 | * contentType: the content-type to use for the body content. JSON is the 44 | * default one used if there is content specified. 45 | * token: the auth token to use for the request. 46 | */ 47 | function github(args, opts) { 48 | opts = opts || {}; 49 | 50 | var req, 51 | options = {}, 52 | localAuth = githubAuth.getLocal(), 53 | d = q.defer(); 54 | 55 | if (typeof args === 'string') { 56 | args = { 57 | host: apiHost, 58 | path: '/' + args, 59 | method: opts.method || 'GET' 60 | }; 61 | } 62 | 63 | function retryWithAuth() { 64 | return githubAuth.fetch().then(function (info) { 65 | options.token = info.token; 66 | return github(args, options); 67 | }); 68 | } 69 | 70 | //Create a local options object, so as not to leak auth data out. 71 | lang.mixin(options, opts); 72 | 73 | options.contentType = options.contentType || 74 | 'application/json'; 75 | 76 | if (options.content && typeof options.content !== 'string') { 77 | options.content = JSON.stringify(options.content); 78 | } 79 | 80 | req = https.request(args, function (response) { 81 | //console.log("statusCode: ", response.statusCode); 82 | //console.log("headers: ", response.headers); 83 | var body = ''; 84 | 85 | response.on('data', function (data) { 86 | body += data; 87 | }); 88 | 89 | response.on('end', function () { 90 | var err; 91 | if (response.statusCode === 404) { 92 | venv(process.cwd()).env 93 | .prompt(args.host + args.path + ' does not exist. ' + 94 | 'Is this a private repo [n]?').then(function (answer) { 95 | answer = answer && answer.toLowerCase(); 96 | if (answer && answer.indexOf('y') === 0) { 97 | retryWithAuth().then(d.resolve, d.fail); 98 | } else { 99 | err = new Error(args.host + args.path + ' does not exist. ' + 100 | 'Is this a private repo [n]?'); 101 | err.response = response; 102 | d.reject(err); 103 | } 104 | }) 105 | .fail(d); 106 | } else if (response.statusCode === 200 || 107 | response.statusCode === 201) { 108 | //Convert the response into an object 109 | d.resolve(JSON.parse(body)); 110 | } else if (response.statusCode === 403 && !options.token) { 111 | console.log('GitHub auth required for ' + args.host + 112 | args.path + ': ' + body); 113 | 114 | //Try to get a token from the user, and retry. 115 | retryWithAuth().then(d.resolve, d.fail); 116 | } else if (response.statusCode === 401) { 117 | githubAuth.fetch({ 118 | forceAuthRefresh: true 119 | }).then(function (info) { 120 | options.token = info.token; 121 | github(args, options).then(d.resolve, d.fail); 122 | }); 123 | } else if (response.statusCode === 301 || 124 | response.statusCode === 302) { 125 | //Redirect, try the new location 126 | args.url = response.headers.location; 127 | github(args, opts).then(d.resolve, d.reject); 128 | } else { 129 | err = new Error(args.host + args.path + ' returned status: ' + 130 | response.statusCode + '. ' + body); 131 | err.response = response; 132 | d.reject(err); 133 | } 134 | }); 135 | }).on('error', function (e) { 136 | d.reject(e); 137 | }); 138 | 139 | //Due to GitHub API limits, it is best to do calls as a given 140 | //user. 141 | if (!options.token && localAuth && localAuth.token) { 142 | options.token = localAuth.token; 143 | } 144 | 145 | if (options.token) { 146 | var basicAuth = new Buffer(options.token + ':x-oauth-basic').toString('base64'); 147 | req.setHeader('Authorization', 'Basic ' + basicAuth); 148 | } 149 | 150 | req.setHeader('User-Agent', config.userAgent); 151 | 152 | if (options.content) { 153 | req.setHeader('Content-Type', options.contentType); 154 | req.setHeader('Content-Length', options.content.length); 155 | req.write(options.content); 156 | } 157 | 158 | req.end(); 159 | 160 | return d.promise; 161 | } 162 | 163 | github.url = function (path) { 164 | return scheme + '://' + host + '/' + path; 165 | }; 166 | 167 | github.apiUrl = function (path) { 168 | return scheme + '://' + apiHost + '/' + path; 169 | }; 170 | 171 | github.rawUrl = function (ownerPlusRepo, version, specificFile) { 172 | var parts = ownerPlusRepo.split('/'), 173 | owner = parts[0], 174 | repo = parts[1]; 175 | 176 | return config.rawUrlPattern 177 | .replace(/\{owner\}/g, owner) 178 | .replace(/\{repo\}/g, repo) 179 | .replace(/\{version\}/g, version) 180 | .replace(/\{file\}/g, specificFile); 181 | }; 182 | 183 | github.zipballUrl = function (ownerPlusRepo, version) { 184 | return github.apiUrl('repos/' + ownerPlusRepo + '/zipball/' + version); 185 | }; 186 | 187 | github.tags = function (ownerPlusRepo) { 188 | return github('repos/' + ownerPlusRepo + '/tags').then(function (data) { 189 | data = data.map(function (data) { 190 | return data.name; 191 | }); 192 | 193 | return data; 194 | }); 195 | }; 196 | 197 | github.repo = function (ownerPlusRepo) { 198 | return github('repos/' + ownerPlusRepo); 199 | }; 200 | 201 | github.versionTags = function (ownerPlusRepo) { 202 | return github.tags(ownerPlusRepo).then(function (tagNames) { 203 | //Only collect tags that are version tags. 204 | tagNames = tagNames.filter(function (tag) { 205 | return versionRegExp.test(tag); 206 | }); 207 | 208 | //Now order the tags in tag order. 209 | tagNames.sort(version.compare); 210 | 211 | //Default to master branch if no version tags available. 212 | if (!tagNames.length) { 213 | return github.masterBranch(ownerPlusRepo).then(function (branchName) { 214 | return [branchName]; 215 | }); 216 | } 217 | 218 | return tagNames; 219 | }); 220 | }; 221 | 222 | github.latestTag = function (ownerRepoVersion) { 223 | //If ownerPlusRepo includes the version, just use that. 224 | var parts = ownerRepoVersion.split('/'), 225 | ownerRepo = parts[0] + '/' + parts[1], 226 | version = parts[2]; 227 | 228 | //Set owner 229 | if (version) { 230 | //Figure out it is a semver, and if so, then find the best match. 231 | if (semVerRangeRegExp.test(version)) { 232 | return github.versionTags(ownerRepo).then(function (tagNames) { 233 | return semver.maxSatisfying(tagNames, version); 234 | }); 235 | } else { 236 | //Just a plain, explicit version, use it. First, check if it 237 | //is a version tag and if it should add or remove a 'v' prefix 238 | //to match a real tag. 239 | return github.tags(ownerRepo).then(function (tags) { 240 | if (tags.indexOf(version) !== -1) { 241 | return version; 242 | } else if (versionRegExp.test(version)) { 243 | var tempVersion; 244 | //May be a vVersion vs version mismatch, try to match 245 | //the other way. 246 | if (version.charAt(0) === 'v') { 247 | //try without the v 248 | tempVersion = version.substring(1); 249 | } else { 250 | //try with the v 251 | tempVersion = 'v' + version; 252 | } 253 | if (tags.indexOf(tempVersion) !== -1) { 254 | return tempVersion; 255 | } else { 256 | //Could still be a branch name, but a branch 257 | //that looks like a version number 258 | return version; 259 | } 260 | } else { 261 | //Probably a branch name, give it a shot 262 | return version; 263 | } 264 | }); 265 | } 266 | } else { 267 | return github.versionTags(ownerRepo).then(function (tagNames) { 268 | return tagNames[0]; 269 | }); 270 | } 271 | }; 272 | 273 | github.masterBranch = function (ownerPlusRepo) { 274 | return github('repos/' + ownerPlusRepo).then(function (data) { 275 | return data.default_branch || 'master'; 276 | }); 277 | }; 278 | 279 | github.search = function (query) { 280 | return github({ 281 | host: config.searchHost, 282 | path: config.searchPath.replace(/\{query\}/, escape(query)), 283 | method: 'GET' 284 | }); 285 | }; 286 | 287 | module.exports = github; 288 | -------------------------------------------------------------------------------- /lib/github/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var qutil = require('../qutil'), 12 | voloConfig = require('../config'), 13 | ghConfig = voloConfig.get().github, 14 | authConfig = ghConfig.auth, 15 | Buffer = require('buffer').Buffer, 16 | https = require('https'), 17 | url = require('url'), 18 | venv = require('../v'), 19 | configKey = 'volo/github/auth', 20 | auth; 21 | 22 | module.exports = (auth = { 23 | getLocal: function () { 24 | var localConfig = voloConfig.getLocal(); 25 | if (localConfig[configKey]) { 26 | return localConfig[configKey]; 27 | } 28 | }, 29 | 30 | //Does the user interaction for name/pw and GitHub network dance 31 | //to get OAuth token. 32 | fetch: function (options, callback, errback) { 33 | options = options || {}; 34 | 35 | var d = qutil.convert(callback, errback), 36 | v = options.v || venv(process.cwd()).env, 37 | localConfig = voloConfig.getLocal(), 38 | name; 39 | 40 | //If already have a token, wrap it up. 41 | if (localConfig[configKey] && !options.forceAuthRefresh) { 42 | d.resolve(localConfig[configKey]); 43 | return d.promise; 44 | } 45 | 46 | function log(msg) { 47 | if (!options.silent) { 48 | console.log(msg); 49 | } 50 | } 51 | 52 | log('GitHub access token required to complete action. In a web browser, go to:'); 53 | log(''); 54 | log('https://github.com/settings/tokens'); 55 | log(''); 56 | log('and generate a new "Personal access token" for volo. Only "repo" and "public_repo" ' + 57 | 'permissions are needed. Paste the generated token below.'); 58 | 59 | v.prompt('GitHub Personal Access Token:') 60 | .then(function (token) { 61 | var config = voloConfig.getLocal(); 62 | config[configKey] = { 63 | token: token 64 | }; 65 | 66 | var saveLocation = voloConfig.saveLocal(); 67 | if (saveLocation) { 68 | log('Token saved in ' + saveLocation); 69 | } 70 | 71 | return config[configKey]; 72 | }).then(d.resolve, d.reject); 73 | 74 | return d.promise; 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /lib/homeDir.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | module.exports = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; -------------------------------------------------------------------------------- /lib/json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | //This is why doves cry 11 | 12 | module.exports = { 13 | parse: function () { 14 | var args = [].slice.call(arguments, 0), 15 | text = args[0]; 16 | //BOM SMASH JSON 17 | if (text.indexOf('\uFEFF') === 0) { 18 | args[0] = text.substring(1, text.length); 19 | } 20 | 21 | return JSON.parse.apply(JSON, args); 22 | }, 23 | 24 | stringify: JSON.stringify 25 | }; -------------------------------------------------------------------------------- /lib/lang.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/requirejs for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | /*global define: false */ 9 | 10 | 'use strict'; 11 | 12 | var lang = { 13 | backSlashRegExp: /\\/g, 14 | hasOwn: Object.prototype.hasOwnProperty, 15 | ostring: Object.prototype.toString, 16 | 17 | isArray: Array.isArray || function (it) { 18 | return lang.ostring.call(it) === "[object Array]"; 19 | }, 20 | 21 | hasProp: function (obj, prop) { 22 | return lang.hasOwn.call(obj, prop); 23 | }, 24 | 25 | /** 26 | * Cycles over properties in an object and calls a function for each 27 | * property value. If the function returns a truthy value, then the 28 | * iteration is stopped. 29 | */ 30 | eachProp: function (obj, func) { 31 | var prop; 32 | for (prop in obj) { 33 | if (lang.hasProp(obj, prop)) { 34 | if (func(obj[prop], prop)) { 35 | break; 36 | } 37 | } 38 | } 39 | }, 40 | 41 | /** 42 | * Simple function to mix in properties from source into target, 43 | * but only if target does not already have a property of the same name. 44 | */ 45 | mixin: function (target, source, override) { 46 | //Use an empty object to avoid other bad JS code that modifies 47 | //Object.prototype. 48 | var empty = {}, prop; 49 | for (prop in source) { 50 | if (override || !target.hasOwnProperty(prop)) { 51 | target[prop] = source[prop]; 52 | } 53 | } 54 | }, 55 | 56 | delegate: (function () { 57 | // boodman/crockford delegation w/ cornford optimization 58 | function TMP() {} 59 | return function (obj, props) { 60 | TMP.prototype = obj; 61 | var tmp = new TMP(); 62 | TMP.prototype = null; 63 | if (props) { 64 | lang.mixin(tmp, props); 65 | } 66 | return tmp; // Object 67 | }; 68 | }()), 69 | 70 | //Sets up a nested object hierachy: setObject(foo, 'a.b.c') 71 | //will make sure foo.a.b.c = {} 72 | setObject: function (target, dotProps) { 73 | var part, i, 74 | parts = dotProps.split('.'); 75 | 76 | for (i = 0; i < parts.length; i += 1) { 77 | part = parts[i]; 78 | if (!target.hasOwnProperty(part)) { 79 | target[part] = {}; 80 | } 81 | target = target[part]; 82 | } 83 | 84 | }, 85 | 86 | //Find a the matching end for the given start. Used to match parens, 87 | //curly braces. Returns an object with a 'start' and 'end' Number 88 | //values. If start is -1 then it did not find a starting character. 89 | //If start is 0 or higher, but end is -1, it means the start was found 90 | //but a matching end was not. 91 | //An enhancement would be to add awareness of strings and possibly 92 | //regexps. 93 | findMatchingPair: function (text, start, end, startIndex) { 94 | var cursor, i, value, 95 | count = 0, 96 | result = { 97 | start: -1, 98 | end: -1 99 | }; 100 | 101 | startIndex = startIndex || 0; 102 | 103 | cursor = text.indexOf(start, startIndex); 104 | 105 | if (cursor !== -1 && cursor < text.length - 1) { 106 | result.start = cursor; 107 | count += 1; 108 | for (i = cursor + 1; i < text.length; i += 1) { 109 | value = text[i]; 110 | if (value === start) { 111 | count += 1; 112 | } else if (value === end) { 113 | count -= 1; 114 | } 115 | if (count === 0) { 116 | result.end = i; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | return result; 123 | 124 | } 125 | }; 126 | 127 | module.exports = lang; 128 | -------------------------------------------------------------------------------- /lib/mime.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = { 12 | archiveTypes: { 13 | 'application/zip': true, 14 | //GitHub sends funky charset for some zip head requests 15 | //Opened GitHub support issue, but just doing a fix for 16 | //meantime and if they ever regress. 17 | 'application/zip; charset=utf-8': true, 18 | 'application/octet-stream': true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /lib/net.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | var q = require('q'), 11 | urlLib = require('url'), 12 | https = require('https'), 13 | http = require('http'), 14 | json = require('./json'), 15 | lang = require('./lang'), 16 | net; 17 | 18 | module.exports = (net = { 19 | getJson: function (url, options) { 20 | options = options || {}; 21 | 22 | var req, 23 | d = q.defer(), 24 | args = urlLib.parse(url), 25 | lib = args.protocol === 'https:' ? https : http; 26 | 27 | args.method = 'GET'; 28 | 29 | req = lib.request(args, function (response) { 30 | var body = ''; 31 | 32 | response.setEncoding('utf8'); 33 | 34 | response.on('data', function (data) { 35 | body += data; 36 | }); 37 | 38 | response.on('end', function () { 39 | var data; 40 | if (response.statusCode === 404) { 41 | if (options.ignore404) { 42 | d.resolve(null); 43 | } else { 44 | d.reject(args.host + args.path + ' does not exist'); 45 | } 46 | } else if (response.statusCode === 200) { 47 | //Convert the response into an object 48 | try { 49 | data = json.parse(body); 50 | } catch (e) { 51 | d.reject('Malformed JSON in : ' + url + ': ' + e); 52 | return; 53 | } 54 | 55 | d.resolve(data); 56 | } else if (response.statusCode === 301 || 57 | response.statusCode === 302) { 58 | //Redirect, try the new location 59 | net.getJson(response.headers.location, options).then(d.resolve, d.reject); 60 | } else { 61 | d.reject(args.host + args.path + ' returned status: ' + 62 | response.statusCode + '. ' + body); 63 | } 64 | }); 65 | }).on('error', function (e) { 66 | d.reject(e); 67 | }); 68 | 69 | if (options.headers) { 70 | lang.eachProp(options.headers, function (value, headerName) { 71 | req.setHeader(headerName, value); 72 | }); 73 | } 74 | 75 | req.end(); 76 | 77 | return d.promise; 78 | }, 79 | head: function (url, options) { 80 | options = options || {}; 81 | 82 | var req, 83 | d = q.defer(), 84 | args = urlLib.parse(url), 85 | lib = args.protocol === 'https:' ? https : http; 86 | 87 | // AWS links that are part of a redirect that are signed apparently do 88 | // not like HEAD requests. Using GET works, but may be a bit more wasteful 89 | // as far as data transfer. Bug 194. 90 | args.method = args.host === 's3.amazonaws.com' && 91 | args.query && 92 | args.query.indexOf('Signature') !== -1 ? 'GET' : 'HEAD'; 93 | 94 | req = lib.request(args, function (response) { 95 | var statusCode = response.statusCode; 96 | 97 | // Purposely skips 400 errors, since some use of head are speculative 98 | // URL guesses at data. 99 | if ((statusCode === 301 || statusCode === 302) && 100 | options.followRedirects) { 101 | d.resolve(net.head(response.headers.location, options)); 102 | } else { 103 | response.setEncoding('utf8'); 104 | 105 | response.on('end', function () { 106 | d.resolve(response); 107 | }); 108 | } 109 | 110 | //console.log("statusCode: ", response.statusCode); 111 | //console.log("headers: ", response.headers); 112 | var body = ''; 113 | 114 | response.on('data', function (data) { 115 | body += data; 116 | }); 117 | 118 | response.on('end', function () { 119 | 120 | }); 121 | }).on('error', function (e) { 122 | d.reject(e); 123 | }); 124 | 125 | if (options.headers) { 126 | lang.eachProp(options.headers, function (value, headerName) { 127 | req.setHeader(headerName, value); 128 | }); 129 | } 130 | 131 | req.end(); 132 | 133 | return d.promise; 134 | } 135 | }); 136 | -------------------------------------------------------------------------------- /lib/packageJson.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var path = require('path'), 12 | fs = require('fs'), 13 | exists = require('./exists'), 14 | lang = require('./lang'), 15 | file = require('./file'), 16 | jsonlib = require('./json'), 17 | commentStartRegExp = /\/\*(\*)?\s*package(\.json)?\s*/, 18 | commentEndRegExp = /\s*(\*)?\*\//, 19 | endsInJsRegExp = /\.js$/, 20 | crRegExp = /\r\n/; 21 | 22 | //Used to find the indices within a file for the /*package.json */ 23 | //comment and a JSON segment inside it. If the result.end is not -1 it 24 | //means a proper comment was found. 25 | function getCommentIndices(text) { 26 | var match = commentStartRegExp.exec(text), 27 | pairMatch, 28 | result = { 29 | start: -1, 30 | end: -1, 31 | jsonStart: -1, 32 | jsonEnd: -1 33 | }; 34 | 35 | if (match) { 36 | result.start = match.index; 37 | 38 | //Search for the end of the comment. Need to be careful of 39 | //contents in strings that look like end of comment. For now, 40 | //just using a "match the curlies" approach, but this would 41 | //fail if a string property has a { or } without a match within 42 | //the string. 43 | pairMatch = lang.findMatchingPair(text, '{', '}', 44 | result.start + match[0].length); 45 | 46 | result.jsonStart = pairMatch.start; 47 | result.jsonEnd = pairMatch.end; 48 | 49 | if (result.jsonEnd !== -1) { 50 | //Have a real json end, find the comment end. 51 | match = commentEndRegExp.exec(text.substring(result.jsonEnd)); 52 | if (match) { 53 | result.end = result.jsonEnd + match.index + match[0].length; 54 | } 55 | } 56 | } 57 | 58 | return result; 59 | } 60 | 61 | function extractCommentData(file) { 62 | var text = fs.readFileSync(file, 'utf8'), 63 | indices = getCommentIndices(text), 64 | json; 65 | 66 | if (indices.end === -1) { 67 | //No valid comment 68 | return null; 69 | } else { 70 | json = text.substring(indices.jsonStart, indices.jsonEnd + 1); 71 | return JSON.parse(json); 72 | } 73 | } 74 | 75 | function saveCommentData(file, data) { 76 | var text = fs.readFileSync(file, 'utf8'), 77 | lineEnding = crRegExp.test(text) ? '\r\n' : '\n', 78 | indices = getCommentIndices(text), 79 | json = JSON.stringify(data, null, ' '); 80 | 81 | if (indices.end === -1) { 82 | //No valid comment, so insert it. 83 | //TODO: would be nice to place this under the license comment, 84 | //if there is one. 85 | text = '/*package.json' + lineEnding + 86 | json + 87 | '\n*/' + lineEnding + 88 | text; 89 | } else { 90 | text = text.substring(0, indices.jsonStart) + 91 | json + 92 | text.substring(indices.jsonEnd + 1); 93 | } 94 | 95 | fs.writeFileSync(file, text, 'utf8'); 96 | } 97 | 98 | function PackageInfo() { 99 | this.file = null; 100 | this.data = null; 101 | this.singleFile = false; 102 | } 103 | 104 | PackageInfo.prototype = { 105 | refresh: function () { 106 | var data; 107 | 108 | if (!this.file) { 109 | return; 110 | } 111 | 112 | if (this.singleFile) { 113 | this.data = extractCommentData(this.file); 114 | } else { 115 | if (exists(this.file)) { 116 | try { 117 | data = jsonlib.parse(fs.readFileSync(this.file, 'utf8')); 118 | } catch (e) { 119 | throw new Error('Malformed JSON in: ' + this.file + ': ' + e); 120 | } 121 | this.data = data; 122 | } 123 | } 124 | }, 125 | 126 | save: function () { 127 | if (this.file && this.data) { 128 | if (this.singleFile) { 129 | saveCommentData(this.file, this.data); 130 | } else { 131 | fs.writeFileSync(this.file, 132 | JSON.stringify(this.data, null, ' '), 133 | 'utf8'); 134 | } 135 | } 136 | }, 137 | 138 | getBaseDir: function () { 139 | var jsonBaseDir, 140 | baseDir = (this.data && 141 | //Favor volo.baseDir over volo.baseUrl over amd.baseUrl 142 | ((this.data.volo && this.data.volo.baseDir) || 143 | (this.data.volo && this.data.volo.baseUrl) || 144 | (this.data.amd && this.data.amd.baseUrl))) || 145 | ''; 146 | 147 | //If no baseDir, then look for an existing js directory 148 | if (!baseDir && !this.singleFile && this.file) { 149 | jsonBaseDir = path.dirname(this.file); 150 | baseDir = path.join(jsonBaseDir, 'js'); 151 | if (!file.exists(baseDir)) { 152 | //Allow for a 'scripts' option instead of js/, in case 153 | //it is something uses transpiled scripts so 'js/' 154 | //would not be accurate. 155 | baseDir = path.join(jsonBaseDir, 'scripts'); 156 | if (!file.exists(baseDir)) { 157 | //No js or scripts subdir, so just use current 158 | //directory. 159 | baseDir = jsonBaseDir; 160 | } 161 | } 162 | } 163 | 164 | return baseDir; 165 | }, 166 | 167 | addVoloDep: function (id, archiveName) { 168 | if (this.file) { 169 | lang.setObject(this, 'data.volo.dependencies'); 170 | this.data.volo.dependencies[id] = archiveName; 171 | } 172 | } 173 | }; 174 | 175 | function packageJson(fileOrDir, options) { 176 | options = options || {}; 177 | 178 | var result = new PackageInfo(), 179 | packagePath = path.resolve(path.join(fileOrDir, 'package.json')), 180 | jsFiles, filePath, packageData; 181 | 182 | if (fs.statSync(fileOrDir).isFile()) { 183 | //A .js file that may have a package.json content 184 | result.file = fileOrDir; 185 | result.singleFile = true; 186 | result.refresh(); 187 | } else { 188 | //Check for /*package.json */ in a .js file if it is the 189 | //only .js file in the dir. 190 | jsFiles = fs.readdirSync(fileOrDir).filter(function (item) { 191 | return endsInJsRegExp.test(item); 192 | }); 193 | 194 | if (jsFiles.length === 1) { 195 | filePath = path.join(fileOrDir, jsFiles[0]); 196 | packageData = extractCommentData(filePath); 197 | } 198 | 199 | if (packageData) { 200 | result.data = packageData; 201 | result.file = filePath; 202 | result.singleFile = true; 203 | } else if (exists(packagePath)) { 204 | //Plain package.json case 205 | result.file = path.join(fileOrDir, 'package.json'); 206 | result.refresh(); 207 | } 208 | } 209 | 210 | if (!result.file && options.create) { 211 | result.data = {}; 212 | result.file = packagePath; 213 | } 214 | 215 | return result; 216 | } 217 | 218 | /** 219 | * Given a dir that contains a package.json file that has the given main value, 220 | * resolve that to a path for the main js file. 221 | */ 222 | packageJson.resolveMainPath = function (packageJsonDir, mainValue) { 223 | var mainName = mainValue 224 | .replace(/^\.\//, '') 225 | .replace(/\.js$/, ''); 226 | 227 | //Some packages use a main of "." to mean "index.js" in this 228 | //directory. 229 | if (mainName === '.') { 230 | mainName = "index"; 231 | } 232 | 233 | //Make sure the main file exists. 234 | return path.join(packageJsonDir, mainName + '.js'); 235 | }; 236 | 237 | module.exports = packageJson; 238 | -------------------------------------------------------------------------------- /lib/qutil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var q = require('q'); 11 | 12 | module.exports = { 13 | convert: function (callback, errback) { 14 | var d = q.defer(); 15 | q.when(d.promise, callback, errback); 16 | return d; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /lib/search/github.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var q = require('q'), 11 | qutil = require('../qutil'), 12 | lang = require('../lang'), 13 | github = require('../github'), 14 | config = require('../config').get().github, 15 | defaultOptions = { 16 | amd: false, 17 | max: 5 18 | }; 19 | 20 | function searchGithub(query, options, callback, errback) { 21 | var d = qutil.convert(callback, errback), 22 | opts = options || {}, 23 | sanitized = []; 24 | 25 | lang.mixin(opts, defaultOptions); 26 | 27 | github.search(query).then(function (data) { 28 | var repos = data && data.items, 29 | damd = q.defer(); 30 | 31 | if (repos && repos.length) { 32 | repos.some(function(entry, i) { 33 | if (i === opts.max) { 34 | return true; 35 | } 36 | sanitized.push({ 37 | archive: entry.full_name, 38 | fork: entry.fork, 39 | watchers: entry.watchers, 40 | pushed: entry.pushed_at, 41 | description: entry.description 42 | }); 43 | }); 44 | } 45 | }).then(function () { 46 | return sanitized.length ? sanitized : null; 47 | }).then(d.resolve, d.reject); 48 | 49 | return d.promise; 50 | } 51 | 52 | module.exports = searchGithub; 53 | -------------------------------------------------------------------------------- /lib/tempDir.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var path = require('path'), 12 | fs = require('fs'), 13 | exists = require('./exists'), 14 | qutil = require('./qutil'), 15 | counter = 0, 16 | env = process.env, 17 | base, tempDir; 18 | 19 | //Match npm for base temp dir priority 20 | base = env.TMPDIR || env.TMP || env.TEMP || 21 | (process.platform === 'win32' ? 'c:\\windows\\temp' : '/tmp'); 22 | 23 | tempDir = { 24 | //Removes characters that would be a problem in a directory name, 25 | //by implying other directory structures 26 | removeDirChars: function (text) { 27 | return text.replace(/[\/\:]/g, '-'); 28 | }, 29 | 30 | create: function (seed, callback, errback) { 31 | var d = qutil.convert(callback, errback), 32 | temp; 33 | 34 | do { 35 | temp = tempDir.createTempName(seed); 36 | } 37 | while (exists(temp)); 38 | 39 | fs.mkdirSync(temp); 40 | d.resolve(temp); 41 | 42 | return d.promise; 43 | }, 44 | 45 | createTempName: function (seed) { 46 | counter += 1; 47 | 48 | var stamp = (new Date()).getTime(); 49 | 50 | return path.join(base, 'temp-' + tempDir.removeDirChars(seed) + 51 | '-' + stamp + '-' + counter); 52 | } 53 | }; 54 | 55 | module.exports = tempDir; 56 | -------------------------------------------------------------------------------- /lib/template.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | /** 11 | * Reads a volofile from a target directory, and exports the data as a 12 | * set of modules. 13 | */ 14 | var tokenRegExp = /\{(\w+)\}/g; 15 | 16 | function template(contents, data) { 17 | return contents.replace(tokenRegExp, function (match, token) { 18 | var result = data[token]; 19 | 20 | //Just use empty string for null or undefined 21 | if (result === null || result === undefined) { 22 | result = ''; 23 | } 24 | 25 | return result; 26 | }); 27 | } 28 | 29 | module.exports = template; 30 | -------------------------------------------------------------------------------- /lib/unzip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var zip = require('zip'), 11 | qutil = require('./qutil'), 12 | path = require('path'), 13 | file = require('./file'), 14 | fs = require('fs'); 15 | 16 | function unzip(fileName, callback, errback) { 17 | var d = qutil.convert(callback, errback), 18 | baseDir = path.dirname(fileName), 19 | data, reader, firstDir; 20 | 21 | try { 22 | data = fs.readFileSync(fileName); 23 | reader = zip.Reader(data); 24 | 25 | reader.forEach(function (entry) { 26 | var name = path.join(baseDir, entry.getName()), 27 | dirName; 28 | 29 | if (!firstDir) { 30 | firstDir = name; 31 | } 32 | 33 | if (entry.isDirectory()) { 34 | if (!file.exists(name)) { 35 | fs.mkdirSync(name); 36 | } 37 | } else { 38 | dirName = path.dirname(name); 39 | if (!file.exists(dirName)) { 40 | file.mkdirs(dirName); 41 | } 42 | fs.writeFileSync(name, entry.getData()); 43 | } 44 | }); 45 | 46 | d.resolve(firstDir); 47 | } catch (e) { 48 | d.reject(e); 49 | } 50 | 51 | return d.promise; 52 | } 53 | 54 | module.exports = unzip; 55 | -------------------------------------------------------------------------------- /lib/version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | var hasSuffixRegExp = /\d+([A-Za-z]+)(\d+)?$/, 11 | vPrefixRegExp = /^v/; 12 | 13 | module.exports = { 14 | /** 15 | * A Compare function that can be used in an array sort call. 16 | * a and b should be N.N.N or vN.N.N version strings. If a is a greater 17 | * version number than b, then the function returns -1 to indicate 18 | * it should be sorted before b. In other words, the sorted 19 | * values will be from highest version to lowest version when 20 | * using this function for sorting. 21 | * 22 | * If the string starts with a "v" it will be stripped before the 23 | * comparison. 24 | */ 25 | compare: function (a, b) { 26 | var aParts = a.split('.'), 27 | bParts = b.split('.'), 28 | length = Math.max(aParts.length, bParts.length), 29 | i, aPart, bPart, aHasSuffix, bHasSuffix; 30 | 31 | //Remove any "v" prefixes 32 | aParts[0] = aParts[0].replace(vPrefixRegExp, ''); 33 | bParts[0] = bParts[0].replace(vPrefixRegExp, ''); 34 | 35 | for (i = 0; i < length; i++) { 36 | aPart = parseInt(aParts[i] || '0', 10); 37 | bPart = parseInt(bParts[i] || '0', 10); 38 | 39 | if (aPart > bPart) { 40 | return -1; 41 | } else if (aPart < bPart) { 42 | return 1; 43 | } else { 44 | //parseInt values are equal. Favor string 45 | //values that do not have character suffixes. 46 | //So, 1.0.0 should be sorted higher than 1.0.0.pre 47 | aHasSuffix = hasSuffixRegExp.exec(aParts[i]); 48 | bHasSuffix = hasSuffixRegExp.exec(bParts[i]); 49 | if (!aHasSuffix && !bHasSuffix) { 50 | continue; 51 | } else if (!aHasSuffix && bHasSuffix) { 52 | return -1; 53 | } else if (aHasSuffix && !bHasSuffix) { 54 | return 1; 55 | } else { 56 | //If the character parts of the suffix differ, 57 | //do a lexigraphic compare. 58 | if (aHasSuffix[1] > bHasSuffix[1]) { 59 | return -1; 60 | } else if (aHasSuffix[1] < bHasSuffix[1]) { 61 | return 1; 62 | } else { 63 | //character parts match, so compare the trailing 64 | //digits. 65 | aPart = parseInt(aHasSuffix[2] || '0', 10); 66 | bPart = parseInt(bHasSuffix[2] || '0', 10); 67 | if (aPart > bPart) { 68 | return -1; 69 | } else { 70 | return 1; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | return 0; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /lib/volofile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 'use strict'; 9 | 10 | 11 | /** 12 | * Reads a volofile from a target directory, and exports the data as a 13 | * set of modules. 14 | */ 15 | 16 | var path = require('path'), 17 | fs = require('fs'), 18 | exists = require('./exists'), 19 | vm = require('vm'), 20 | commands = require('./commands'), 21 | qutil = require('./qutil'), 22 | venv = require('./v'), 23 | parse = require('./parse'); 24 | 25 | //Handles converting legacy 0.1 code to work in the new environment. 26 | //REMOVE THIS at some point, when 0.1 volofiles are not around any more. 27 | function legacy01Exec(fileName, contents) { 28 | var defineRegExp = /define\s*\(\s*function\s*\([^\{]+\{/, 29 | endBraceRegExp = /\}\)(;)?\s*$/, 30 | requireRegExp = /require\s*\(['"]([^'"]+)['"]\s*\)/g, 31 | voloPrefixRegExp = /volo\//, 32 | commandNames = { 33 | amdify: true, 34 | add: true, 35 | create: true, 36 | help: true, 37 | npmrel: true, 38 | search: true 39 | }, 40 | exported; 41 | 42 | //First remove the define wrapper. 43 | contents = contents.replace(defineRegExp, '').replace(endBraceRegExp, ''); 44 | 45 | //Transform the require calls to v.require calls if appropriate 46 | contents = contents.replace(requireRegExp, function (match, id) { 47 | if (id.indexOf('volo/') === 0) { 48 | return "shimv.require('./lib/" + id.replace(voloPrefixRegExp, '') + "')"; 49 | } else if (commandNames.hasOwnProperty(id)) { 50 | return "shimv.require('./commands/" + id + "')"; 51 | } else if (id === 'q') { 52 | return "shimv.require('q')"; 53 | } else { 54 | return match; 55 | } 56 | }); 57 | 58 | //Wrap the text in a wrapper that allows getting access to the exported value. 59 | contents = 'global.shimVoloFile = (function (shimv, require) {\n' + 60 | contents + 61 | '\n}(global.shimv, global.shimRequire));'; 62 | 63 | //Set up global adapter modules. 64 | global.shimv = venv(path.dirname(fileName)).env; 65 | global.shimRequire = require('../volo').require; 66 | 67 | //Evaluate the module. 68 | vm.runInThisContext(contents, fileName); 69 | 70 | exported = global.shimVoloFile; 71 | delete global.shimVoloFile; 72 | delete global.shimv; 73 | delete global.shimRequire; 74 | 75 | return exported; 76 | } 77 | 78 | function volofile(basePath, callback, errback) { 79 | var d = qutil.convert(callback, errback), 80 | volofilePath = path.resolve(path.join(basePath, 'volofile')), 81 | contents; 82 | 83 | if (exists(volofilePath)) { 84 | //Check the file for legacy 0.1 structure. If so, then do some mods 85 | //for it to work. 86 | contents = fs.readFileSync(volofilePath, 'utf8'); 87 | 88 | //Look for define, and the absence of amdefine use. 89 | try { 90 | if (parse.usesAmdOrRequireJs(volofilePath, contents) && 91 | contents.indexOf('amdefine') === -1) { 92 | d.resolve(legacy01Exec(volofilePath, contents)); 93 | } else { 94 | d.resolve(require(volofilePath)); 95 | } 96 | } catch (e) { 97 | d.reject('Malformed volofile: ' + volofilePath + ': ' + e); 98 | } 99 | } else { 100 | d.resolve(); 101 | } 102 | 103 | return d.promise; 104 | } 105 | 106 | /** 107 | * Loads the volofile inside basePath, and if there, and if it 108 | * supports the command, then runs it, running dependencies for 109 | * the command if specified. 110 | * @returns {Promise} that resolves to false exactly, otherwise it has the 111 | * commmand output, if any. 112 | */ 113 | volofile.run = function (basePath, commandName /*other args can be passed*/) { 114 | var args = [].slice.call(arguments, 2), 115 | cwd = process.cwd(); 116 | 117 | process.chdir(basePath); 118 | 119 | return volofile('.').then(function (vfMod) { 120 | var command = vfMod && vfMod[commandName]; 121 | if (command) { 122 | return commands.run.apply(commands, [command, null].concat(args)); 123 | } 124 | }) 125 | .then(function (result) { 126 | process.chdir(cwd); 127 | return result; 128 | }); 129 | }; 130 | 131 | module.exports = volofile; 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "volo", 3 | "description": "A JavaScript dependency manager and project creation tool that favors GitHub for the package repository.", 4 | "version": "0.3.8", 5 | "homepage": "http://github.com/volojs/volo", 6 | "author": "James Burke (http://github.com/jrburke)", 7 | 8 | "licenses": [ 9 | { 10 | "type": "BSD", 11 | "url": "https://github.com/volojs/volo/blob/master/LICENSE" 12 | }, 13 | { 14 | "type": "MIT", 15 | "url": "https://github.com/volojs/volo/blob/master/LICENSE" 16 | } 17 | ], 18 | 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/volojs/volo.git" 22 | }, 23 | 24 | "bin": { 25 | "volo": "./bin/volo" 26 | }, 27 | 28 | "engines": { 29 | "node": ">=0.8" 30 | }, 31 | 32 | "dependencies": { 33 | "q": "0.9.4", 34 | "zip": "1.2.0", 35 | "uglify-js": "1.2.6", 36 | "shell-quote": "0.0.1", 37 | "semver": "4.1.x", 38 | "colors": "0.6.0-1", 39 | "glob": "3.2.1", 40 | "which": "1.0.5" 41 | }, 42 | 43 | "main": "volo" 44 | } 45 | -------------------------------------------------------------------------------- /tests/all.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @license Copyright (c) 2011, The Dojo Foundation All Rights Reserved. 5 | * Available via the MIT or new BSD license. 6 | * see: http://github.com/volojs/volo for details 7 | */ 8 | 9 | /*jslint node: true */ 10 | /*global doh, skipDohSetup: true, console */ 11 | 'use strict'; 12 | 13 | var vm = require('vm'), 14 | fs = require('fs'), 15 | path = require('path'), 16 | doh = require('./doh/runner'), 17 | dohNode = require('./doh/_nodeRunner'), 18 | q = require('q'), 19 | //Set voloLib, used to set up volo/baseUrl and used in volo/config 20 | //If this is not set, then the all.js location is used, which is not 21 | //desirable. 22 | voloLib = path.resolve(__dirname, path.join('..', 'lib')), 23 | nodeRequire = require, 24 | //Special global flag used by DOH. 25 | skipDohSetup = true, 26 | bootstrap, tests; 27 | 28 | global.voloLib = voloLib; 29 | global.doh = doh; 30 | 31 | tests = [ 32 | './lib/volo/packageJson/tests', 33 | './lib/volo/version', 34 | './lib/volo/github', 35 | './commands/create/tests', 36 | './commands/add/tests', 37 | './commands/remove/tests', 38 | './commands/amdify/tests', 39 | './commands/volofile/tests', 40 | './full/tests' 41 | ]; 42 | 43 | //All the tests return a an object with the following properties: 44 | //* start: a deferred to resolve to start the tests for that test module 45 | //* end: a promise that resolves when the tests are done for that test module. 46 | var promises = tests.map(function (name) { 47 | return require(name); 48 | }), 49 | master = q.defer(), 50 | last; 51 | 52 | last = promises.reduce(function (previous, current) { 53 | current.start.resolve(previous.end); 54 | return current; 55 | }, { 56 | start: master, 57 | end: master.promise 58 | }); 59 | 60 | //Indicate this is the end of the promises, so we get errors 61 | //logged correctly. 62 | last.end.then(function () { 63 | //All promises are done, print out summary. 64 | doh.run(); 65 | }).done(); 66 | 67 | //Start the cascade 68 | master.resolve(); 69 | -------------------------------------------------------------------------------- /tests/commands/add/expected/simpleAmd.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define(['foo'], function () { (function () { 3 | 4 | //This is a simple file. 5 | 6 | 7 | 8 | }.call(root)); 9 | return amdExports; 10 | }); }(this)); 11 | -------------------------------------------------------------------------------- /tests/commands/add/support/addAll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "dependencies": { 4 | "d": "../../support/localModules/d", 5 | "e": "../../support/localModules/e" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/commands/add/support/addable/temp/a.js: -------------------------------------------------------------------------------- 1 | //a.js 2 | -------------------------------------------------------------------------------- /tests/commands/add/support/addable/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | onAdd: function (d, v) { 6 | //Just do a simple move of a.js from temp and remove temp. 7 | v.mv('temp/a.js', 'a.js'); 8 | v.rm('temp'); 9 | v.rm('volofile'); 10 | 11 | d.resolve(); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/.npmignore -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/demos/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/demos/empty.txt -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/extra/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/extra/empty.txt -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/keep/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/keep/empty.txt -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/keep/sub/nested.pre: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/keep/sub/nested.pre -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/node_modules/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/node_modules/empty.txt -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "type": "directory", 4 | "ignore": ["extra", ".npmignore", "node_modules", "**/*.pre"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/commands/add/support/dirIgnore/top.pre: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/commands/add/support/dirIgnore/top.pre -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/a/a.js: -------------------------------------------------------------------------------- 1 | var a = { 2 | name: 'a' 3 | }; 4 | -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/b/b.js: -------------------------------------------------------------------------------- 1 | var b = { 2 | name: 'b', 3 | a: a 4 | }; -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "dependencies": { 4 | "a": "local:../../support/localModules/a" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/c/c.js: -------------------------------------------------------------------------------- 1 | var c = { 2 | name: 'c', 3 | a: a 4 | }; 5 | -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/c/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "dependencies": { 4 | "a": "local:../../support/localModules/a" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/d/d.js: -------------------------------------------------------------------------------- 1 | /*package.json 2 | { 3 | "volo": { 4 | "dependencies": { 5 | "b": "local:../../support/localModules/b", 6 | "c": "local:../../support/localModules/c" 7 | } 8 | } 9 | } 10 | */ 11 | 12 | var d = { 13 | name: 'd', 14 | b: b, 15 | c: c 16 | }; 17 | -------------------------------------------------------------------------------- /tests/commands/add/support/localModules/e/e.js: -------------------------------------------------------------------------------- 1 | var e = { 2 | name: 'e' 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /tests/commands/add/support/simple/simple.js: -------------------------------------------------------------------------------- 1 | //This is a simple file. 2 | -------------------------------------------------------------------------------- /tests/commands/amdify/expected/lib.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define(['alpha','beta'], function () { (function () { 3 | 4 | 5 | (function () { 6 | //This `$` messes with regexp replacement in 7 | //the template. Make sure we do not get double 8 | //content posting. 9 | this.lib = alpha.doIt() + beta.id; 10 | }).call(this); 11 | 12 | 13 | 14 | }.call(root)); 15 | return amdExports; 16 | }); }(this)); 17 | -------------------------------------------------------------------------------- /tests/commands/amdify/expected/libExports.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define(['gamma'], function () { (function () { 3 | 4 | (function () { 5 | this.libExports = gamma(); 6 | }).call(this); 7 | 8 | 9 | if (window.libExports.noConflict) { 10 | window.libExports.noConflict(true); 11 | } 12 | amdExports = window.libExports; 13 | 14 | }.call(root)); 15 | return amdExports; 16 | }); }(this)); 17 | -------------------------------------------------------------------------------- /tests/commands/amdify/expected/plain.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define([], function () { (function () { 3 | 4 | 5 | window.foo = 'bar'; 6 | 7 | 8 | 9 | }.call(root)); 10 | return amdExports; 11 | }); }(this)); 12 | -------------------------------------------------------------------------------- /tests/commands/amdify/expected/plainExports.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define([], function () { (function () { 3 | 4 | 5 | window.baz = 'bam'; 6 | 7 | 8 | amdExports = baz; 9 | 10 | }.call(root)); 11 | return amdExports; 12 | }); }(this)); 13 | -------------------------------------------------------------------------------- /tests/commands/amdify/expected/thisExports.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | //A module that uses "this" to export, and has no dependencies. 3 | 4 | this.foo = function () { 5 | return 'foo'; 6 | }; 7 | 8 | }); -------------------------------------------------------------------------------- /tests/commands/amdify/expected/varnames.js: -------------------------------------------------------------------------------- 1 | //Wrapped in an outer function to preserve global this 2 | (function (root) { var amdExports; define(['jquery','underscore'], function ($) { (function () { 3 | 4 | 5 | $(function () { 6 | window._('something'); 7 | }); 8 | 9 | 10 | 11 | }.call(root)); 12 | return amdExports; 13 | }); }(this)); 14 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/lib.js: -------------------------------------------------------------------------------- 1 | 2 | (function () { 3 | //This `$` messes with regexp replacement in 4 | //the template. Make sure we do not get double 5 | //content posting. 6 | this.lib = alpha.doIt() + beta.id; 7 | }).call(this); 8 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/libExports.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | this.libExports = gamma(); 3 | }).call(this); 4 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/plain.js: -------------------------------------------------------------------------------- 1 | 2 | window.foo = 'bar'; 3 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/plainExports.js: -------------------------------------------------------------------------------- 1 | 2 | window.baz = 'bam'; 3 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/thisExports.js: -------------------------------------------------------------------------------- 1 | //A module that uses "this" to export, and has no dependencies. 2 | 3 | this.foo = function () { 4 | return 'foo'; 5 | }; 6 | -------------------------------------------------------------------------------- /tests/commands/amdify/support/varnames.js: -------------------------------------------------------------------------------- 1 | 2 | $(function () { 3 | window._('something'); 4 | }); 5 | -------------------------------------------------------------------------------- /tests/commands/amdify/tests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global doh, voloLib */ 3 | 4 | 'use strict'; 5 | 6 | var q = require('q'), 7 | main = require(voloLib + '/../volo'), 8 | start = q.defer(), 9 | fs = require('fs'), 10 | path = require('path'), 11 | file = require(voloLib + '/file'), 12 | amdifyConvert = require(voloLib + '/../commands/amdify').api.convert, 13 | cwd = process.cwd(), 14 | dir = __dirname, 15 | supportDir = path.join(dir, 'support'), 16 | testDir = path.join(dir, 'output'), 17 | end; 18 | 19 | //Clean up old test output, create a fresh directory for it. 20 | end = start.promise.then(function () { 21 | return file.rm(testDir); 22 | }) 23 | .then(function () { 24 | file.copyDir(supportDir, testDir); 25 | process.chdir(testDir); 26 | }) 27 | .then(function () { 28 | return main(['amdify', '-noprompt', 'plain.js'], function (result) { 29 | console.log(result); 30 | doh.register("amdifyPlain", 31 | [ 32 | function amdifyPlain(t) { 33 | var output = fs.readFileSync('plain.js', 'utf8'), 34 | expected = fs.readFileSync('../expected/plain.js', 'utf8'); 35 | 36 | t.is(expected, output); 37 | } 38 | ] 39 | ); 40 | doh.run(); 41 | }); 42 | }) 43 | .then(function () { 44 | return main(['amdify', 'plainExports.js', 'exports=baz'], function (result) { 45 | console.log(result); 46 | doh.register("amdifyPlainExports", 47 | [ 48 | function amdifyPlainExports(t) { 49 | var output = fs.readFileSync('plainExports.js', 'utf8'), 50 | expected = fs.readFileSync('../expected/plainExports.js', 'utf8'); 51 | 52 | t.is(expected, output); 53 | } 54 | ] 55 | ); 56 | doh.run(); 57 | }); 58 | }) 59 | .then(function () { 60 | return main(['amdify', 'lib.js', 'depends=alpha,beta'], function (result) { 61 | console.log(result); 62 | doh.register("amdifyLib", 63 | [ 64 | function amdifyLib(t) { 65 | var output = fs.readFileSync('lib.js', 'utf8'), 66 | expected = fs.readFileSync('../expected/lib.js', 'utf8'); 67 | 68 | t.is(expected, output); 69 | } 70 | ] 71 | ); 72 | doh.run(); 73 | }); 74 | }) 75 | .then(function () { 76 | return main(['amdify', 'varnames.js', 'depends=jquery=$,underscore='], function (result) { 77 | console.log(result); 78 | doh.register("amdifyVarnames", 79 | [ 80 | function amdifyLib(t) { 81 | var output = fs.readFileSync('varnames.js', 'utf8'), 82 | expected = fs.readFileSync('../expected/varnames.js', 'utf8'); 83 | 84 | t.is(expected, output); 85 | } 86 | ] 87 | ); 88 | doh.run(); 89 | }); 90 | }) 91 | .then(function () { 92 | return main(['amdify', '-noConflict', 'libExports.js', 'depends=gamma', 'exports=window.libExports'], function (result) { 93 | console.log(result); 94 | doh.register("amdifyLibExports", 95 | [ 96 | function amdifyLibExports(t) { 97 | var output = fs.readFileSync('libExports.js', 'utf8'), 98 | expected = fs.readFileSync('../expected/libExports.js', 'utf8'); 99 | 100 | t.is(expected, output); 101 | } 102 | ] 103 | ); 104 | doh.run(); 105 | }); 106 | }) 107 | .then(function () { 108 | return amdifyConvert('thisExports.js', null, null, null, { 109 | commonJs: true 110 | }).then(function (result) { 111 | console.log(result); 112 | doh.register("thisExports", 113 | [ 114 | function thisExports(t) { 115 | var output = fs.readFileSync('thisExports.js', 'utf8'), 116 | expected = fs.readFileSync('../expected/thisExports.js', 'utf8'); 117 | 118 | t.is(expected, output); 119 | } 120 | ] 121 | ); 122 | doh.run(); 123 | }); 124 | }) 125 | .then(function (result) { 126 | process.chdir(cwd); 127 | }); 128 | 129 | module.exports = { 130 | start: start, 131 | end: end 132 | }; 133 | -------------------------------------------------------------------------------- /tests/commands/create/support/addOnCreate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "dependencies": { 4 | "jquery": "github:jquery/jquery/1.8.0", 5 | "require": "requirejs/requirejs/~2" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/commands/create/support/funnySubDirName/README.md: -------------------------------------------------------------------------------- 1 | See https://github.com/volojs/volo/issues/88 for details 2 | -------------------------------------------------------------------------------- /tests/commands/create/support/funnySubDirName/something.github.com/test.txt: -------------------------------------------------------------------------------- 1 | success -------------------------------------------------------------------------------- /tests/commands/create/support/hasNpmInstall/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasNpmInstall", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "volo-ghdeploy": "*" 6 | } 7 | } -------------------------------------------------------------------------------- /tests/commands/create/support/hasNpmInstall/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | onCreate: function (d, v, namedArgs, appName) { 6 | //Generate templatized output 7 | if (v.exists('node_modules/volo-ghdeploy/index.js')) { 8 | v.write('ok.txt', 'ok'); 9 | } 10 | 11 | d.resolve(); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /tests/commands/create/support/hasOnCreate/sample.template: -------------------------------------------------------------------------------- 1 | Hello {name}. Your code is {code} -------------------------------------------------------------------------------- /tests/commands/create/support/hasOnCreate/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | onCreate: function (d, v, namedArgs, appName) { 6 | //Generate templatized output 7 | var templatePath = 'sample.template'; 8 | 9 | v.write('output.txt', v.template(v.read(templatePath), namedArgs)); 10 | 11 | v.rm(templatePath); 12 | v.rm('volofile'); 13 | 14 | d.resolve(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /tests/commands/create/support/simple/simple.js: -------------------------------------------------------------------------------- 1 | //Just a simple one file, local project template 2 | 3 | -------------------------------------------------------------------------------- /tests/commands/create/support/voloBaseUrl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "baseUrl": "funkyBaseUrl" 4 | } 5 | } -------------------------------------------------------------------------------- /tests/commands/create/support/voloBaseUrl/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global define */ 3 | 'use strict'; 4 | module.exports = { 5 | onCreate: function (d, v) { 6 | d.resolve(v.command('add', '../../support/simple')); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /tests/commands/create/tests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global doh, voloLib */ 3 | 'use strict'; 4 | 5 | var q = require('q'), 6 | main = require(voloLib + '/../volo'), 7 | start = q.defer(), 8 | fs = require('fs'), 9 | path = require('path'), 10 | file = require(voloLib + '/file'), 11 | cwd = process.cwd(), 12 | dir = __dirname, 13 | testDir = path.join(dir, 'output'), 14 | end; 15 | 16 | //Clean up old test output, create a fresh directory for it. 17 | end = start.promise.then(function () { 18 | return file.rm(testDir); 19 | }) 20 | .then(function () { 21 | fs.mkdirSync(testDir); 22 | process.chdir(testDir); 23 | }) 24 | .then(function () { 25 | return main(['create', 'simple', '../support/simple'], function (result) { 26 | console.log(result); 27 | doh.register("createSimple", 28 | [ 29 | function createSimple(t) { 30 | t.is(true, file.exists(path.join('simple', 'simple.js'))); 31 | } 32 | ] 33 | ); 34 | doh.run(); 35 | }); 36 | }) 37 | .then(function () { 38 | return main(['create', 'dude', '../support/hasOnCreate', 'name=fred', 'code=blue'], 39 | function (result) { 40 | console.log(result); 41 | doh.register("createHasOnCreate", 42 | [ 43 | function createHasOnCreate(t) { 44 | var outputPath = path.join('dude', 'output.txt'); 45 | t.is(true, file.exists(outputPath)); 46 | t.is(false, file.exists(path.join('dude', 'volofile'))); 47 | t.is(false, file.exists(path.join('dude', 'sample.template'))); 48 | t.is('Hello fred. Your code is blue', fs.readFileSync(outputPath, 'utf8')); 49 | } 50 | ] 51 | ); 52 | doh.run(); 53 | }); 54 | }) 55 | 56 | //Test running npm before running onCreate: #68 57 | .then(function () { 58 | return main(['create', 'dude2', '../support/hasNpmInstall'], 59 | function (result) { 60 | console.log(result); 61 | doh.register("createHasNpmInstall", 62 | [ 63 | function createHasNpmInstall(t) { 64 | var outputPath = path.join('dude2', 'ok.txt'); 65 | t.is(true, file.exists(outputPath)); 66 | t.is('ok', fs.readFileSync(outputPath, 'utf8')); 67 | } 68 | ] 69 | ); 70 | doh.run(); 71 | }); 72 | }) 73 | 74 | //Make sure something that looks like a .git directory but is really not 75 | //gets copied over correctly: #68 76 | .then(function () { 77 | return main(['create', 'funny', '../support/funnySubDirName'], 78 | function (result) { 79 | console.log(result); 80 | doh.register("createFunnySubDirName", 81 | [ 82 | function createFunnySubDirName(t) { 83 | var outputPath = path.join('funny', 'README.md'); 84 | t.is(true, file.exists(outputPath)); 85 | outputPath = path.join('funny', 'something.github.com', 'test.txt'); 86 | t.is(true, file.exists(outputPath)); 87 | t.is('success', fs.readFileSync(outputPath, 'utf8')); 88 | } 89 | ] 90 | ); 91 | doh.run(); 92 | }); 93 | }) 94 | 95 | .then(function () { 96 | return main(['create', 'voloBaseUrl', '../support/voloBaseUrl'], function (result) { 97 | console.log(result); 98 | doh.register("voloBaseUrl", 99 | [ 100 | function voloBaseUrl(t) { 101 | var expected = fs.readFileSync('../support/simple/simple.js', 'utf8'), 102 | output = fs.readFileSync('voloBaseUrl/funkyBaseUrl/simple.js', 'utf8'); 103 | 104 | t.is(expected, output); 105 | } 106 | ] 107 | ); 108 | doh.run(); 109 | }); 110 | }) 111 | 112 | .then(function () { 113 | return main(['create', 'addOnCreate', '../support/addOnCreate'], function (result) { 114 | console.log(result); 115 | doh.register("createAddOnCreate", 116 | [ 117 | function createAddOnCreate(t) { 118 | t.is(true, file.exists('addOnCreate/js/jquery.js'), 'jquery.js exists'); 119 | t.is(true, file.exists('addOnCreate/js/require.js'), 'require.js exists'); 120 | } 121 | ] 122 | ); 123 | doh.run(); 124 | }); 125 | }) 126 | 127 | .then(function () { 128 | return main(['create', 'addVersionNoV', 'h5bp/html5-boilerplate/4.0.0'], function (result) { 129 | console.log(result); 130 | doh.register("addVersionNoV", 131 | [ 132 | function createAddOnCreate(t) { 133 | t.is(true, file.exists('addVersionNoV/CHANGELOG.md'), 'h5bp changelog exists'); 134 | t.is(true, /4\.0\.0/.test(file.readFile('addVersionNoV/CHANGELOG.md')), 135 | 'h5bp changelog has 4.0.0 in it'); 136 | } 137 | ] 138 | ); 139 | doh.run(); 140 | }); 141 | }) 142 | 143 | .then(function (result) { 144 | process.chdir(cwd); 145 | }); 146 | 147 | module.exports = { 148 | start: start, 149 | end: end 150 | }; 151 | -------------------------------------------------------------------------------- /tests/commands/remove/tests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true, nomen: true */ 2 | /*global doh, voloLib */ 3 | 'use strict'; 4 | 5 | var q = require('q'), 6 | main = require(voloLib + '/../volo'), 7 | start = q.defer(), 8 | fs = require('fs'), 9 | path = require('path'), 10 | file = require(voloLib + '/file'), 11 | packageJson = require(voloLib + '/packageJson'), 12 | cwd = process.cwd(), 13 | dir = __dirname, 14 | testDir = path.join(dir, 'output'), 15 | end; 16 | 17 | //Clean up old test output, create a fresh directory for it. 18 | end = start.promise.then(function () { 19 | file.rm(testDir); 20 | fs.mkdirSync(testDir); 21 | process.chdir(testDir); 22 | }) 23 | 24 | //Set up test area first 25 | .then(function () { 26 | return main(['add', '../../add/support/localModules/a']); 27 | }) 28 | .then(function () { 29 | return main(['add', '../../add/support/addable']); 30 | }) 31 | .then(function () { 32 | return main(['remove', 'a']); 33 | }) 34 | .then(function () { 35 | return main(['remove', 'addable']); 36 | }) 37 | .then(function () { 38 | var pkg = packageJson('.'); 39 | 40 | doh.register("remove", 41 | [ 42 | function remove(t) { 43 | t.is(false, !!pkg.data.volo.dependencies.addable); 44 | t.is(false, !!pkg.data.volo.dependencies.a); 45 | 46 | t.is(false, file.exists('addable')); 47 | t.is(false, file.exists('addable.js')); 48 | t.is(false, file.exists('a.js')); 49 | 50 | } 51 | ] 52 | ); 53 | doh.run(); 54 | }) 55 | 56 | .then(function () { 57 | process.chdir(cwd); 58 | }); 59 | 60 | module.exports = { 61 | start: start, 62 | end: end 63 | }; -------------------------------------------------------------------------------- /tests/commands/volofile/support/shell/node_modules/.bin/uppercase: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | name = process.argv[2]; 5 | 6 | fs.writeFileSync(name, fs.readFileSync(name, 'utf8').toUpperCase(), 'utf8'); -------------------------------------------------------------------------------- /tests/commands/volofile/support/shell/volofile: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | write: 'v.write text.txt "Hello world"', 4 | 5 | copy: { 6 | depends: ['write'], 7 | run: [ 8 | 'v.copyFile text.txt final.txt', 9 | 'n.uppercase final.txt', 10 | 'v.write before.txt "after.txt"', 11 | 'cat before.txt > after.txt' 12 | ] 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /tests/commands/volofile/support/simple/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | foo: { 6 | validate: function (namedArgs) { 7 | namedArgs.style = namedArgs.style + 'Modified'; 8 | }, 9 | run: function (d, v, namedArgs) { 10 | var template = '{style} is {future}'; 11 | 12 | v.write('universal.txt', v.template(template, namedArgs)); 13 | 14 | d.resolve(); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /tests/commands/volofile/tests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global doh, voloLib */ 3 | 'use strict'; 4 | 5 | var q = require('q'), 6 | main = require(voloLib + '/../volo'), 7 | start = q.defer(), 8 | fs = require('fs'), 9 | path = require('path'), 10 | file = require(voloLib + '/file'), 11 | cwd = process.cwd(), 12 | dir = __dirname, 13 | testDir = path.join(dir, 'output'), 14 | end; 15 | 16 | //Clean up old test output, create a fresh directory for it. 17 | end = start.promise.then(function () { 18 | return file.rm(testDir); 19 | }) 20 | .then(function () { 21 | fs.mkdirSync(testDir); 22 | process.chdir(testDir); 23 | }) 24 | 25 | .then(function () { 26 | return main(['create', 'simple', '../support/simple']); 27 | }) 28 | .then(function () { 29 | process.chdir('simple'); 30 | 31 | return main(['foo', 'style=galactic', 'future=backlit'], 32 | function (result) { 33 | console.log(result); 34 | doh.register("volofileSimple", 35 | [ 36 | function volofileSimple(t) { 37 | var universalPath = path.join('universal.txt'); 38 | t.is(true, file.exists(universalPath)); 39 | t.is('galacticModified is backlit', fs.readFileSync(universalPath, 'utf8')); 40 | } 41 | ] 42 | ); 43 | doh.run(); 44 | }); 45 | }) 46 | .then(function (result) { 47 | process.chdir('..'); 48 | }) 49 | 50 | //Testing that string commands work, and that array of strings work. Also 51 | //includes a depends test. 52 | .then(function () { 53 | return main(['create', 'shell', '../support/shell']); 54 | }) 55 | .then(function () { 56 | process.chdir('shell'); 57 | 58 | return main(['copy'], 59 | function (result) { 60 | console.log(result); 61 | doh.register("volofileShell", 62 | [ 63 | function volofileShell(t) { 64 | t.is('HELLO WORLD', fs.readFileSync(path.join('final.txt'), 'utf8')); 65 | t.is('after.txt', fs.readFileSync(path.join('after.txt'), 'utf8')); 66 | } 67 | ] 68 | ); 69 | doh.run(); 70 | }); 71 | }) 72 | .then(function (result) { 73 | process.chdir('..'); 74 | }) 75 | 76 | 77 | .then(function (result) { 78 | process.chdir(cwd); 79 | }); 80 | 81 | module.exports = { 82 | start: start, 83 | end: end 84 | }; 85 | -------------------------------------------------------------------------------- /tests/doh/LICENSE: -------------------------------------------------------------------------------- 1 | Dojo is available under *either* the terms of the modified BSD license *or* the 2 | Academic Free License version 2.1. As a recipient of Dojo, you may choose which 3 | license to receive this code under (except as noted in per-module LICENSE 4 | files). Some modules may not be the copyright of the Dojo Foundation. These 5 | modules contain explicit declarations of copyright in both the LICENSE files in 6 | the directories in which they reside and in the code itself. No external 7 | contributions are allowed under licenses which are fundamentally incompatible 8 | with the AFL or BSD licenses that Dojo is distributed under. 9 | 10 | The text of the AFL and BSD licenses is reproduced below. 11 | 12 | ------------------------------------------------------------------------------- 13 | The "New" BSD License: 14 | ********************** 15 | 16 | Copyright (c) 2005-2009, The Dojo Foundation 17 | All rights reserved. 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions are met: 21 | 22 | * Redistributions of source code must retain the above copyright notice, this 23 | list of conditions and the following disclaimer. 24 | * Redistributions in binary form must reproduce the above copyright notice, 25 | this list of conditions and the following disclaimer in the documentation 26 | and/or other materials provided with the distribution. 27 | * Neither the name of the Dojo Foundation nor the names of its contributors 28 | may be used to endorse or promote products derived from this software 29 | without specific prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 32 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 34 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 35 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 37 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 39 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 40 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | 42 | ------------------------------------------------------------------------------- 43 | The Academic Free License, v. 2.1: 44 | ********************************** 45 | 46 | This Academic Free License (the "License") applies to any original work of 47 | authorship (the "Original Work") whose owner (the "Licensor") has placed the 48 | following notice immediately following the copyright notice for the Original 49 | Work: 50 | 51 | Licensed under the Academic Free License version 2.1 52 | 53 | 1) Grant of Copyright License. Licensor hereby grants You a world-wide, 54 | royalty-free, non-exclusive, perpetual, sublicenseable license to do the 55 | following: 56 | 57 | a) to reproduce the Original Work in copies; 58 | 59 | b) to prepare derivative works ("Derivative Works") based upon the Original 60 | Work; 61 | 62 | c) to distribute copies of the Original Work and Derivative Works to the 63 | public; 64 | 65 | d) to perform the Original Work publicly; and 66 | 67 | e) to display the Original Work publicly. 68 | 69 | 2) Grant of Patent License. Licensor hereby grants You a world-wide, 70 | royalty-free, non-exclusive, perpetual, sublicenseable license, under patent 71 | claims owned or controlled by the Licensor that are embodied in the Original 72 | Work as furnished by the Licensor, to make, use, sell and offer for sale the 73 | Original Work and Derivative Works. 74 | 75 | 3) Grant of Source Code License. The term "Source Code" means the preferred 76 | form of the Original Work for making modifications to it and all available 77 | documentation describing how to modify the Original Work. Licensor hereby 78 | agrees to provide a machine-readable copy of the Source Code of the Original 79 | Work along with each copy of the Original Work that Licensor distributes. 80 | Licensor reserves the right to satisfy this obligation by placing a 81 | machine-readable copy of the Source Code in an information repository 82 | reasonably calculated to permit inexpensive and convenient access by You for as 83 | long as Licensor continues to distribute the Original Work, and by publishing 84 | the address of that information repository in a notice immediately following 85 | the copyright notice that applies to the Original Work. 86 | 87 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names 88 | of any contributors to the Original Work, nor any of their trademarks or 89 | service marks, may be used to endorse or promote products derived from this 90 | Original Work without express prior written permission of the Licensor. Nothing 91 | in this License shall be deemed to grant any rights to trademarks, copyrights, 92 | patents, trade secrets or any other intellectual property of Licensor except as 93 | expressly stated herein. No patent license is granted to make, use, sell or 94 | offer to sell embodiments of any patent claims other than the licensed claims 95 | defined in Section 2. No right is granted to the trademarks of Licensor even if 96 | such marks are included in the Original Work. Nothing in this License shall be 97 | interpreted to prohibit Licensor from licensing under different terms from this 98 | License any Original Work that Licensor otherwise would have a right to 99 | license. 100 | 101 | 5) This section intentionally omitted. 102 | 103 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative 104 | Works that You create, all copyright, patent or trademark notices from the 105 | Source Code of the Original Work, as well as any notices of licensing and any 106 | descriptive text identified therein as an "Attribution Notice." You must cause 107 | the Source Code for any Derivative Works that You create to carry a prominent 108 | Attribution Notice reasonably calculated to inform recipients that You have 109 | modified the Original Work. 110 | 111 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that 112 | the copyright in and to the Original Work and the patent rights granted herein 113 | by Licensor are owned by the Licensor or are sublicensed to You under the terms 114 | of this License with the permission of the contributor(s) of those copyrights 115 | and patent rights. Except as expressly stated in the immediately proceeding 116 | sentence, the Original Work is provided under this License on an "AS IS" BASIS 117 | and WITHOUT WARRANTY, either express or implied, including, without limitation, 118 | the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR 119 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. 120 | This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No 121 | license to Original Work is granted hereunder except under this disclaimer. 122 | 123 | 8) Limitation of Liability. Under no circumstances and under no legal theory, 124 | whether in tort (including negligence), contract, or otherwise, shall the 125 | Licensor be liable to any person for any direct, indirect, special, incidental, 126 | or consequential damages of any character arising as a result of this License 127 | or the use of the Original Work including, without limitation, damages for loss 128 | of goodwill, work stoppage, computer failure or malfunction, or any and all 129 | other commercial damages or losses. This limitation of liability shall not 130 | apply to liability for death or personal injury resulting from Licensor's 131 | negligence to the extent applicable law prohibits such limitation. Some 132 | jurisdictions do not allow the exclusion or limitation of incidental or 133 | consequential damages, so this exclusion and limitation may not apply to You. 134 | 135 | 9) Acceptance and Termination. If You distribute copies of the Original Work or 136 | a Derivative Work, You must make a reasonable effort under the circumstances to 137 | obtain the express assent of recipients to the terms of this License. Nothing 138 | else but this License (or another written agreement between Licensor and You) 139 | grants You permission to create Derivative Works based upon the Original Work 140 | or to exercise any of the rights granted in Section 1 herein, and any attempt 141 | to do so except under the terms of this License (or another written agreement 142 | between Licensor and You) is expressly prohibited by U.S. copyright law, the 143 | equivalent laws of other countries, and by international treaty. Therefore, by 144 | exercising any of the rights granted to You in Section 1 herein, You indicate 145 | Your acceptance of this License and all of its terms and conditions. 146 | 147 | 10) Termination for Patent Action. This License shall terminate automatically 148 | and You may no longer exercise any of the rights granted to You by this License 149 | as of the date You commence an action, including a cross-claim or counterclaim, 150 | against Licensor or any licensee alleging that the Original Work infringes a 151 | patent. This termination provision shall not apply for an action alleging 152 | patent infringement by combinations of the Original Work with other software or 153 | hardware. 154 | 155 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 156 | License may be brought only in the courts of a jurisdiction wherein the 157 | Licensor resides or in which Licensor conducts its primary business, and under 158 | the laws of that jurisdiction excluding its conflict-of-law provisions. The 159 | application of the United Nations Convention on Contracts for the International 160 | Sale of Goods is expressly excluded. Any use of the Original Work outside the 161 | scope of this License or after its termination shall be subject to the 162 | requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et 163 | seq., the equivalent laws of other countries, and international treaty. This 164 | section shall survive the termination of this License. 165 | 166 | 12) Attorneys Fees. In any action to enforce the terms of this License or 167 | seeking damages relating thereto, the prevailing party shall be entitled to 168 | recover its costs and expenses, including, without limitation, reasonable 169 | attorneys' fees and costs incurred in connection with such action, including 170 | any appeal of such action. This section shall survive the termination of this 171 | License. 172 | 173 | 13) Miscellaneous. This License represents the complete agreement concerning 174 | the subject matter hereof. If any provision of this License is held to be 175 | unenforceable, such provision shall be reformed only to the extent necessary to 176 | make it enforceable. 177 | 178 | 14) Definition of "You" in This License. "You" throughout this License, whether 179 | in upper or lower case, means an individual or a legal entity exercising rights 180 | under, and complying with all of the terms of, this License. For legal 181 | entities, "You" includes any entity that controls, is controlled by, or is 182 | under common control with you. For purposes of this definition, "control" means 183 | (i) the power, direct or indirect, to cause the direction or management of such 184 | entity, whether by contract or otherwise, or (ii) ownership of fifty percent 185 | (50%) or more of the outstanding shares, or (iii) beneficial ownership of such 186 | entity. 187 | 188 | 15) Right to Use. You may use the Original Work in all ways not otherwise 189 | restricted or conditioned by this License or by law, and Licensor promises not 190 | to interfere with or be responsible for such uses by You. 191 | 192 | This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. 193 | Permission is hereby granted to copy and distribute this license without 194 | modification. This license may not be modified without the express written 195 | permission of its copyright owner. 196 | -------------------------------------------------------------------------------- /tests/doh/README: -------------------------------------------------------------------------------- 1 | DOH may be run standalone by issuing a command like the following: 2 | 3 | java -jar ../shrinksafe/js.jar runner.js testModule=tests.colors 4 | 5 | where the testModule argument is optional and shrinksafe/js.jar is just a 6 | convenient copy of the Rhino JavaScript engine -- the custom patch is not 7 | required. 8 | 9 | Optional arguments include: 10 | * dojoUrl - specifies the location of dojo.js 11 | * testUrl - specifies a Javascript file to load with initialization code 12 | * testModule - specifies a test module in the dojo package namespace 13 | -------------------------------------------------------------------------------- /tests/doh/_nodeRunner.js: -------------------------------------------------------------------------------- 1 | 2 | /*global doh: false, process: false */ 3 | 4 | (function () { 5 | var aps = Array.prototype.slice, 6 | doh = require('./runner'); 7 | 8 | doh.debug = function () { 9 | //Could have multiple args, join them all together. 10 | var msg = aps.call(arguments, 0).join(' '); 11 | console.log(msg); 12 | }; 13 | 14 | // Override the doh._report method to make it quit with an 15 | // appropriate exit code in case of test failures. 16 | var oldReport = doh._report; 17 | doh._report = function () { 18 | oldReport.apply(doh, arguments); 19 | if (this._failureCount > 0 || this._errorCount > 0) { 20 | process.exit(1); 21 | } 22 | }; 23 | 24 | }()); -------------------------------------------------------------------------------- /tests/doh/_rhinoRunner.js: -------------------------------------------------------------------------------- 1 | if(this["dojo"]){ 2 | dojo.provide("doh._rhinoRunner"); 3 | } 4 | 5 | doh.debug = print; 6 | 7 | // Override the doh._report method to make it quit with an 8 | // appropriate exit code in case of test failures. 9 | (function(){ 10 | var oldReport = doh._report; 11 | doh._report = function(){ 12 | oldReport.apply(doh, arguments); 13 | if(this._failureCount > 0 || this._errorCount > 0){ 14 | quit(1); 15 | } 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /tests/doh/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | RequireJS Tests Via The Dojo Unit Test Harness, $Rev: 20149 $ 11 | 12 | 33 | 34 | 66 | 67 | 68 | 69 | 74 | 229 | 230 | 231 | 232 | 233 | 241 | 242 | 243 | 244 | 245 | 246 | 256 | 261 | 262 | 263 | 297 | 311 | 312 |
234 |

RequireJS Tests Via D.O.H.: The Dojo Objective Harness

235 | 236 | 237 | 238 | 239 | 240 |
247 | 248 | 249 | 250 | 251 | 252 | Stopped 253 | 254 | 255 | 257 | 258 | 259 | 260 |
264 |
265 | 267 | 268 | 269 | 270 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 |
  271 | 273 | testtime
295 |
296 |
298 |
299 |
301 |

302 | 							
303 |
304 | 306 | 309 |
310 |
313 | 314 | 315 | 316 | 317 | -------------------------------------------------------------------------------- /tests/doh/runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | java -jar ../../build/lib/rhino/js.jar runner.js "$@" 4 | -------------------------------------------------------------------------------- /tests/doh/small_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/doh/small_logo.png -------------------------------------------------------------------------------- /tests/full/README.md: -------------------------------------------------------------------------------- 1 | Test the full volo package. Helps do determine if things like path lookups work correctly. 2 | -------------------------------------------------------------------------------- /tests/full/expected/legacy01/results.txt: -------------------------------------------------------------------------------- 1 | true 2 | function 3 | function 4 | function 5 | function 6 | function 7 | function 8 | -------------------------------------------------------------------------------- /tests/full/support/legacy01/volofile: -------------------------------------------------------------------------------- 1 | /*jslint regexp: true */ 2 | /*global define, console, process */ 3 | 4 | define(function (require) { 5 | 'use strict'; 6 | 7 | var amdify = require('amdify'), 8 | q = require('q'), 9 | crypto = require('crypto'), 10 | fs = require('fs'), 11 | github = require('volo/github'), 12 | githubAuth = require('volo/github/auth'); 13 | 14 | return { 15 | test: function (d, v, namedArgs) { 16 | var text = (!!amdify.api) + '\n' + 17 | (typeof q.defer) + '\n' + 18 | (typeof crypto.createHash) + '\n' + 19 | (typeof fs.stat) + '\n' + 20 | (typeof fs.exists) + '\n' + 21 | (typeof github) + '\n' + 22 | (typeof githubAuth.fetch) + '\n'; 23 | 24 | v.write('results.txt', text); 25 | } 26 | } 27 | }); -------------------------------------------------------------------------------- /tests/full/support/simple/volofile: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | foo: { 6 | run: function (d, v, namedArgs) { 7 | var version = v.require('./lib/version'), 8 | text = typeof version.compare; 9 | 10 | v.write('compareType.txt', text); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /tests/full/tests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global doh, voloLib */ 3 | 'use strict'; 4 | 5 | var q = require('q'), 6 | main = require(voloLib + '/../volo'), 7 | start = q.defer(), 8 | fs = require('fs'), 9 | path = require('path'), 10 | tempDir = require(voloLib + '/tempDir'), 11 | venv = require(voloLib + '/v'), 12 | file = require(voloLib + '/file'), 13 | cwd = process.cwd(), 14 | dir = __dirname, 15 | end, v, tempDir; 16 | 17 | //Clean up old test output, create a fresh directory for it. 18 | end = start.promise.then(function () { 19 | return tempDir.create('volo-full-output'); 20 | }) 21 | .then(function (dir) { 22 | tempDir = dir; 23 | v = venv(tempDir).env; 24 | process.chdir(tempDir); 25 | }) 26 | 27 | 28 | //Do a simple create and run a volo command that uses a volo submodule. 29 | .then(function () { 30 | return v.sequence([ 31 | ['volo', 'create', 'simple', path.join(dir, 'support', 'simple')], 32 | [process, 'chdir', 'simple'], 33 | ['volo', 'foo'] 34 | ],{ useConsole: true }); 35 | }) 36 | .then(function () { 37 | doh.register("fullSimple", 38 | [ 39 | function fullSimple(t) { 40 | var comparePath = path.join('compareType.txt'); 41 | t.is(true, file.exists(comparePath)); 42 | t.is('function', fs.readFileSync(comparePath, 'utf8')); 43 | } 44 | ] 45 | ); 46 | doh.run(); 47 | 48 | process.chdir('..'); 49 | }) 50 | 51 | //Allow a legacy 0.1 volofile to work in the new system. 52 | .then(function () { 53 | return v.sequence([ 54 | ['volo', 'create', 'legacy01', path.join(dir, 'support', 'legacy01')], 55 | [process, 'chdir', 'legacy01'], 56 | ['volo', 'test'] 57 | ],{ useConsole: true }); 58 | }) 59 | .then(function () { 60 | doh.register("fullLegacy01", 61 | [ 62 | function fullLegacy01(t) { 63 | var resultsPath = path.join('results.txt'); 64 | t.is(true, file.exists(resultsPath)); 65 | t.is(fs.readFileSync(path.join(dir, 'expected', 'legacy01', 'results.txt'), 'utf8').trim(), 66 | fs.readFileSync(resultsPath, 'utf8').trim()); 67 | } 68 | ] 69 | ); 70 | doh.run(); 71 | 72 | process.chdir('..'); 73 | }) 74 | 75 | 76 | .then(function () { 77 | process.chdir(cwd); 78 | 79 | file.rm(tempDir); 80 | }); 81 | 82 | module.exports = { 83 | start: start, 84 | end: end 85 | }; 86 | -------------------------------------------------------------------------------- /tests/lib/volo/github.js: -------------------------------------------------------------------------------- 1 | 2 | /*jslint node: true */ 3 | /*global define, doh */ 4 | 'use strict'; 5 | 6 | var github = require(global.voloLib + '/github'), 7 | q = require('q'), 8 | start = q.defer(), 9 | end; 10 | 11 | end = start.promise.then(function () { 12 | return github.latestTag('jquery/jquery/<2.2').then(function (version) { 13 | 14 | doh.register("githubSemVerLessThan", 15 | [ 16 | function githubSemVerLessThan(t) { 17 | t.is('2.1.4', version); 18 | } 19 | ]); 20 | doh.run(); 21 | }); 22 | }) 23 | 24 | .then(function () { 25 | return github.latestTag('requirejs/requirejs/~2').then(function (version) { 26 | 27 | doh.register("githubSemVerMajorMinor", 28 | [ 29 | function githubSemVerMajorMinor(t) { 30 | t.is('2.2.0', version); 31 | } 32 | ]); 33 | doh.run(); 34 | }); 35 | }) 36 | 37 | .then(function () { 38 | return github.latestTag('requirejs/requirejs/2.x').then(function (version) { 39 | 40 | doh.register("githubDotXVersion", 41 | [ 42 | function githubDotXVersion(t) { 43 | t.is('2.2.0', version); 44 | } 45 | ]); 46 | doh.run(); 47 | }); 48 | }) 49 | 50 | .then(function () { 51 | return github.latestTag('requirejs/requirejs/2.0.x').then(function (version) { 52 | 53 | doh.register("githubDotDotXVersion", 54 | [ 55 | function githubDotDotXVersion(t) { 56 | t.is('2.0.6', version); 57 | } 58 | ]); 59 | doh.run(); 60 | }); 61 | }) 62 | 63 | 64 | .then(function () { 65 | return github.latestTag('requirejs/requirejs/2.0.6').then(function (version) { 66 | 67 | doh.register("githubExactVersion", 68 | [ 69 | function githubExactVersion(t) { 70 | t.is('2.0.6', version); 71 | } 72 | ]); 73 | doh.run(); 74 | }); 75 | }); 76 | 77 | module.exports = { 78 | start: start, 79 | end: end 80 | }; 81 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/expected/hasFile/empty.js: -------------------------------------------------------------------------------- 1 | /*package.json 2 | { 3 | "name": "empty", 4 | "version": "1.0", 5 | "volo": { 6 | "dependencies": { 7 | "dojo": "dojo/dojo/1.7.2" 8 | } 9 | } 10 | } 11 | */ 12 | 13 | var empty = {}; 14 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/expected/hasJs/lib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /* package.json 8 | { 9 | "name": "lib", 10 | "version": "1.0", 11 | "volo": { 12 | "dependencies": { 13 | "jquery": "jquery/jquery/1.7.1" 14 | } 15 | } 16 | } 17 | */ 18 | 19 | define({ 20 | name: 'lib' 21 | }); 22 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/expected/hasJsonFile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasJsonFile", 3 | "version": "2.0", 4 | "volo": { 5 | "dependencies": { 6 | "dijit": "dojo/dijit/1.7.2" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasFile/empty.js: -------------------------------------------------------------------------------- 1 | /*package.json 2 | { 3 | "name": "empty", 4 | "version": "1.0" 5 | } 6 | */ 7 | 8 | var empty = {}; 9 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasFile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasFile", 3 | "version": "1.0" 4 | } -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJs/lib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /* package.json 8 | { 9 | "name": "lib", 10 | "version": "1.0" 11 | } 12 | */ 13 | 14 | define({ 15 | name: 'lib' 16 | }); 17 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJs/lib.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/lib/volo/packageJson/support/hasJs/lib.txt -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJsNoComment/lib.js: -------------------------------------------------------------------------------- 1 | /* This is a JS file, but no package.json contents. */ 2 | 3 | var lib = {}; 4 | 5 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJsNpmStyle/lib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /**package 8 | { 9 | "name": "nlib", 10 | "version": "1.1" 11 | } 12 | **/ 13 | 14 | define({ 15 | name: 'lib' 16 | }); 17 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJsNpmStyle/lib.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volojs/volo/034cc1596308cde839c0d895ab16e656e9574c36/tests/lib/volo/packageJson/support/hasJsNpmStyle/lib.txt -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJsonFile/main.js: -------------------------------------------------------------------------------- 1 | //main.js 2 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/hasJsonFile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasJsonFile", 3 | "version": "2.0" 4 | } 5 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/tooManyJs/one.js: -------------------------------------------------------------------------------- 1 | 2 | /*package.json 3 | { 4 | "name": "one", 5 | "version": "1.0" 6 | } 7 | */ 8 | 9 | define({ 10 | name: 'one' 11 | }); 12 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/support/tooManyJs/two.js: -------------------------------------------------------------------------------- 1 | 2 | /*package.json 3 | { 4 | "name": "two", 5 | "version": "1.0" 6 | } 7 | */ 8 | 9 | define({ 10 | name: 'two' 11 | }); 12 | -------------------------------------------------------------------------------- /tests/lib/volo/packageJson/tests.js: -------------------------------------------------------------------------------- 1 | 2 | /*jslint node: true */ 3 | /*global doh, voloLib */ 4 | 'use strict'; 5 | 6 | var packageJson = require(voloLib + '/packageJson'), 7 | path = require('path'), 8 | q = require('q'), 9 | file = require(voloLib + '/file'), 10 | start = q.defer(), 11 | cwd = process.cwd(), 12 | dir = __dirname, 13 | expectedDir = path.join(dir, 'expected'), 14 | outputDir = path.join(dir, 'output'), 15 | supportDir = path.join(dir, 'support'), 16 | end; 17 | 18 | end = start.promise.then(function () { 19 | doh.register("packageJsonTests", 20 | [ 21 | function packageJsonTests(t) { 22 | var dirs = [ 23 | 'hasFile', 24 | 'hasJs', 25 | 'hasJsNpmStyle', 26 | 'hasJsNoComment', 27 | 'hasJsonFile', 28 | 'tooManyJs' 29 | ], 30 | result; 31 | 32 | file.rm(outputDir); 33 | file.mkdirs(outputDir); 34 | 35 | //Copy the support directories into output, to allow 36 | //modification of the files. 37 | dirs.forEach(function (dir) { 38 | file.copyDir(path.join(supportDir, dir), 39 | path.join(outputDir, dir)); 40 | }); 41 | 42 | process.chdir(dir); 43 | 44 | //Test favoring a single js file package.json comment 45 | //over a package.json file. 46 | 47 | result = packageJson(path.join(outputDir, 'hasFile')); 48 | t.is('empty', result.data.name); 49 | t.is('1.0', result.data.version); 50 | 51 | //Add a volo dep to the package.son, save, confirm the save. 52 | result.addVoloDep('dojo', 'dojo/dojo/1.7.2'); 53 | result.save(); 54 | result.refresh(); 55 | t.is('dojo/dojo/1.7.2', result.data.volo.dependencies.dojo); 56 | t.is(file.readFile(path.join(expectedDir, 'hasFile', 'empty.js')), 57 | file.readFile(path.join(outputDir, 'hasFile', 'empty.js'))); 58 | 59 | result = packageJson(path.join(outputDir, 'hasJsonFile')); 60 | t.is('hasJsonFile', result.data.name); 61 | t.is('2.0', result.data.version); 62 | 63 | //Add a volo dep to the package.son, save, confirm the save. 64 | result.addVoloDep('dijit', 'dojo/dijit/1.7.2'); 65 | result.save(); 66 | result.refresh(); 67 | t.is('dojo/dijit/1.7.2', result.data.volo.dependencies.dijit); 68 | t.is(file.readFile(path.join(expectedDir, 'hasJsonFile', 'package.json')), 69 | file.readFile(path.join(outputDir, 'hasJsonFile', 'package.json'))); 70 | 71 | //Test file comment, /**package **/ npm style. 72 | result = packageJson(path.join(outputDir, 'hasJsNpmStyle')); 73 | t.is('nlib', result.data.name); 74 | t.is('1.1', result.data.version); 75 | 76 | //Test file comment 77 | result = packageJson(path.join(outputDir, 'hasJs')); 78 | t.is('lib', result.data.name); 79 | t.is('1.0', result.data.version); 80 | 81 | //Add a volo dep to the package.json, save, confirm the save. 82 | result.addVoloDep('jquery', 'jquery/jquery/1.7.1'); 83 | result.save(); 84 | result.refresh(); 85 | t.is('jquery/jquery/1.7.1', result.data.volo.dependencies.jquery); 86 | t.is(file.readFile(path.join(expectedDir, 'hasJs', 'lib.js')), 87 | file.readFile(path.join(outputDir, 'hasJs', 'lib.js'))); 88 | 89 | //Test file, but no comment or package.json 90 | result = packageJson(path.join(outputDir, 'hasJsNoComment')); 91 | t.is(null, result.data); 92 | t.is(null, result.file); 93 | 94 | //Test no package.json and too many .js files 95 | result = packageJson(path.join(outputDir, 'tooManyJs')); 96 | t.is(null, result.data); 97 | t.is(null, result.file); 98 | } 99 | ] 100 | ); 101 | doh.run(); 102 | }).then(function () { 103 | process.chdir(cwd); 104 | }); 105 | 106 | module.exports = { 107 | start: start, 108 | end: end 109 | }; 110 | -------------------------------------------------------------------------------- /tests/lib/volo/version.js: -------------------------------------------------------------------------------- 1 | 2 | /*jslint node: true */ 3 | /*global define, doh */ 4 | 'use strict'; 5 | 6 | var version = require(global.voloLib + '/version'), 7 | q = require('q'), 8 | start = q.defer(), 9 | end; 10 | 11 | function validate(t, expected, actual) { 12 | var i; 13 | 14 | t.is(expected.length, actual.length); 15 | 16 | for (i = 0; i < expected.length; i += 1) { 17 | t.is(expected[i], actual[i]); 18 | } 19 | } 20 | 21 | end = start.promise.then(function () { 22 | doh.register("versionTests", 23 | [ 24 | function versionTests(t) { 25 | var list1 = ['0.2.0', '0.2.1', '0.3.1', '0.3.0', '1.3.1beta1', '1.3.1pre1', '1.3.1pre2', '1.2.0', '1.3.1'], 26 | expected1 = ['1.3.1', '1.3.1pre2', '1.3.1pre1', '1.3.1beta1', '1.2.0', '0.3.1', '0.3.0', '0.2.1', '0.2.0'], 27 | list2 = [ 'v2.5.0', 28 | 'v1.15.1', 29 | 'v2.10.3', 30 | 'v2.1.1', 31 | 'v2.10.0', 32 | 'v2.8.1', 33 | 'v1.2.0', 34 | 'v1.23.0', 35 | 'v2.4.5', 36 | 'v1.9.0', 37 | 'v1.5.1', 38 | 'v1.8.5', 39 | 'v2.4.2', 40 | 'v1.29.5', 41 | 'v2.7.3', 42 | 'v1.8.2', 43 | 'v1.29.2', 44 | 'v2.0.3', 45 | 'v1.14.0', 46 | 'v1.10.1', 47 | 'v2.7.0', 48 | 'v2.3.4', 49 | 'v2.0.0', 50 | 'v2.3.1', 51 | 'v1.17.1', 52 | 'v1.25.0', 53 | 'v1.13.2', 54 | 'v1.4.0', 55 | 'v1.28.1', 56 | 'v1.0.1', 57 | 'v1.16.0', 58 | 'v2.9.6', 59 | 'v2.9.3', 60 | 'v1.20.3', 61 | 'v2.2.0', 62 | 'v2.9.0', 63 | 'v1.19.1', 64 | 'v1.20.0', 65 | 'v2.5.1', 66 | 'v1.6.0', 67 | 'v1.27.0', 68 | 'v1.2.1', 69 | 'v2.1.2', 70 | 'v2.10.1', 71 | 'v2.4.6', 72 | 'v1.9.1', 73 | 'v1.11.0', 74 | 'v1.18.0', 75 | 'v2.4.3', 76 | 'v1.29.6', 77 | 'v1.5.2', 78 | 'v1.14.1', 79 | 'v2.4.0', 80 | 'v1.8.3', 81 | 'v1.29.3', 82 | 'v2.0.4', 83 | 'v2.7.4', 84 | 'v1.22.0', 85 | 'v1.29.0', 86 | 'v2.0.1', 87 | 'v2.7.1', 88 | 'v1.1.0', 89 | 'v1.8.0', 90 | 'v2.3.2', 91 | 'v1.13.3', 92 | 'v1.0.2', 93 | 'v1.13.0', 94 | 'v2.6.0', 95 | 'v2.9.7', 96 | 'v2.2.1', 97 | 'v2.9.4', 98 | 'v1.3.0', 99 | 'v1.24.0', 100 | 'v1.20.1', 101 | 'v2.9.1', 102 | 'v1.6.1', 103 | 'v1.27.1', 104 | 'v2.5.2', 105 | 'v2.1.3', 106 | 'v1.15.0', 107 | 'v2.10.2', 108 | 'v2.1.0', 109 | 'v2.8.0', 110 | 'v1.11.1', 111 | 'v2.4.4', 112 | 'v1.29.7', 113 | 'v1.5.3', 114 | 'v1.26.0', 115 | 'v1.29.4', 116 | 'v2.7.5', 117 | 'v1.22.1', 118 | 'v1.8.4', 119 | 'v2.4.1', 120 | 'v1.14.2', 121 | 'v1.5.0', 122 | 'semver', 123 | 'v1.8.1', 124 | 'v1.29.1', 125 | 'v2.0.2', 126 | 'v2.7.2', 127 | 'v2.3.3', 128 | 'v1.10.0', 129 | 'v2.3.0', 130 | 'v1.17.0', 131 | 'v1.13.4', 132 | 'v1.13.1', 133 | 'v1.0.3', 134 | 'v1.21.0', 135 | 'v2.6.1', 136 | 'v1.0.0', 137 | 'v1.7.0', 138 | 'v1.28.0', 139 | 'v2.9.5', 140 | 'v1.24.1', 141 | 'v1.20.2', 142 | 'v1.12.0', 143 | 'v2.9.2', 144 | 'v1.19.0', 145 | 'v1.27.2' ], 146 | 147 | expected2 = [ 'v2.10.3', 148 | 'v2.10.2', 149 | 'v2.10.1', 150 | 'v2.10.0', 151 | 'v2.9.7', 152 | 'v2.9.6', 153 | 'v2.9.5', 154 | 'v2.9.4', 155 | 'v2.9.3', 156 | 'v2.9.2', 157 | 'v2.9.1', 158 | 'v2.9.0', 159 | 'v2.8.1', 160 | 'v2.8.0', 161 | 'v2.7.5', 162 | 'v2.7.4', 163 | 'v2.7.3', 164 | 'v2.7.2', 165 | 'v2.7.1', 166 | 'v2.7.0', 167 | 'v2.6.1', 168 | 'v2.6.0', 169 | 'v2.5.2', 170 | 'v2.5.1', 171 | 'v2.5.0', 172 | 'v2.4.6', 173 | 'v2.4.5', 174 | 'v2.4.4', 175 | 'v2.4.3', 176 | 'v2.4.2', 177 | 'v2.4.1', 178 | 'v2.4.0', 179 | 'v2.3.4', 180 | 'v2.3.3', 181 | 'v2.3.2', 182 | 'v2.3.1', 183 | 'v2.3.0', 184 | 'v2.2.1', 185 | 'v2.2.0', 186 | 'v2.1.3', 187 | 'v2.1.2', 188 | 'v2.1.1', 189 | 'v2.1.0', 190 | 'v2.0.4', 191 | 'v2.0.3', 192 | 'v2.0.2', 193 | 'v2.0.1', 194 | 'v2.0.0', 195 | 'v1.29.7', 196 | 'v1.29.6', 197 | 'v1.29.5', 198 | 'v1.29.4', 199 | 'v1.29.3', 200 | 'v1.29.2', 201 | 'v1.29.1', 202 | 'v1.29.0', 203 | 'v1.28.1', 204 | 'v1.28.0', 205 | 'v1.27.2', 206 | 'v1.27.1', 207 | 'v1.27.0', 208 | 'v1.26.0', 209 | 'v1.25.0', 210 | 'v1.24.1', 211 | 'v1.24.0', 212 | 'v1.23.0', 213 | 'v1.22.1', 214 | 'v1.22.0', 215 | 'v1.21.0', 216 | 'v1.20.3', 217 | 'v1.20.2', 218 | 'v1.20.1', 219 | 'v1.20.0', 220 | 'v1.19.1', 221 | 'v1.19.0', 222 | 'v1.18.0', 223 | 'v1.17.1', 224 | 'v1.17.0', 225 | 'v1.16.0', 226 | 'v1.15.1', 227 | 'v1.15.0', 228 | 'v1.14.2', 229 | 'v1.14.1', 230 | 'v1.14.0', 231 | 'v1.13.4', 232 | 'v1.13.3', 233 | 'v1.13.2', 234 | 'v1.13.1', 235 | 'v1.13.0', 236 | 'v1.12.0', 237 | 'v1.11.1', 238 | 'v1.11.0', 239 | 'v1.10.1', 240 | 'v1.10.0', 241 | 'v1.9.1', 242 | 'v1.9.0', 243 | 'v1.8.5', 244 | 'v1.8.4', 245 | 'v1.8.3', 246 | 'v1.8.2', 247 | 'v1.8.1', 248 | 'v1.8.0', 249 | 'v1.7.0', 250 | 'v1.6.1', 251 | 'v1.6.0', 252 | 'v1.5.3', 253 | 'v1.5.2', 254 | 'v1.5.1', 255 | 'v1.5.0', 256 | 'v1.4.0', 257 | 'v1.3.0', 258 | 'v1.2.1', 259 | 'v1.2.0', 260 | 'v1.1.0', 261 | 'v1.0.3', 262 | 'v1.0.2', 263 | 'v1.0.1', 264 | 'v1.0.0', 265 | 'semver' ], 266 | list3 = ['0.9.8.1', '0.9.8.2', '10.0', '10.1', 267 | '10.1.1', '10.2', '0.9.8', '10.2.1', '10.2.1.1', 268 | '10.2.1.2'], 269 | expected3 = ['10.2.1.2', '10.2.1.1', '10.2.1', '10.2', 270 | '10.1.1', '10.1', '10.0', '0.9.8.2', '0.9.8.1', 271 | '0.9.8']; 272 | 273 | validate(t, expected1, list1.sort(version.compare)); 274 | validate(t, expected2, list2.sort(version.compare)); 275 | validate(t, expected3, list3.sort(version.compare)); 276 | 277 | } 278 | ] 279 | ); 280 | doh.run(); 281 | }); 282 | 283 | module.exports = { 284 | start: start, 285 | end: end 286 | }; 287 | -------------------------------------------------------------------------------- /volo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/volojs/volo for details 5 | */ 6 | 7 | /*jslint node: true */ 8 | 9 | 'use strict'; 10 | 11 | var commands = require('./lib/commands'), 12 | config = require('./lib/config').get(), 13 | volofile = require('./lib/volofile'), 14 | path = require('path'), 15 | exists = require('./lib/exists'), 16 | colors = require('colors'), 17 | q = require('q'); 18 | 19 | colors.mode = 'console'; 20 | 21 | function main(args, callback, errback) { 22 | var commandName, combinedArgs, commandOverride, firstArg, 23 | deferred = q.defer(), 24 | cwd = process.cwd(), 25 | namedArgs = { 26 | volo: { 27 | resolve: function (relativePath) { 28 | if (relativePath.indexOf('/') !== 0 && 29 | relativePath.indexOf(':') === -1) { 30 | return path.resolve(cwd, relativePath); 31 | } 32 | return relativePath; 33 | } 34 | } 35 | }, 36 | aryArgs = [], 37 | flags = []; 38 | 39 | //Cycle through args, pulling off name=value pairs into an object. 40 | args.forEach(function (arg) { 41 | var name, value, 42 | eqIndex = arg.indexOf('='); 43 | if (eqIndex === -1) { 44 | //If passed a flag like -f, convert to named 45 | //argument based on the command's configuration. 46 | if (arg.indexOf('-') === 0) { 47 | //Allow --flags too, just trim off both dashes 48 | flags.push(arg.substring(arg.charAt(1) === '-' ? 2 : 1)); 49 | } else { 50 | //Regular array arg. 51 | aryArgs.push(arg); 52 | } 53 | } else { 54 | name = arg.substring(0, eqIndex); 55 | value = arg.substring(eqIndex + 1); 56 | namedArgs[name] = value; 57 | } 58 | }); 59 | 60 | //The commandName will be the first arg. 61 | if (aryArgs.length) { 62 | //If first arg is a -flag or a name=value command skip it, 63 | //means a default volofile action should be run. 64 | firstArg = aryArgs[0]; 65 | if (firstArg.indexOf('-') !== 0 && firstArg.indexOf('=') === -1) { 66 | commandName = aryArgs.shift(); 67 | 68 | //If this is a specific override to bypase a volofile, 69 | //the next arg is the real command. 70 | if (commandName === 'command') { 71 | commandOverride = true; 72 | commandName = aryArgs.shift(); 73 | } 74 | } 75 | } 76 | 77 | combinedArgs = [namedArgs].concat(aryArgs); 78 | 79 | //Global flag override for help 80 | if (flags.indexOf('h') !== -1 || flags.indexOf('help') !== -1) { 81 | if (commandName) { 82 | combinedArgs = [namedArgs].concat([commandName]); 83 | commandName = 'help'; 84 | } 85 | } 86 | 87 | //Function to run after the command object has been loaded, either 88 | //by a volofile or by installed volo actions. 89 | function runCommand(command) { 90 | flags.forEach(function (flag) { 91 | if (command.flags && command.flags[flag]) { 92 | namedArgs[command.flags[flag]] = true; 93 | } 94 | }); 95 | 96 | commands.run.apply(commands, [command, null].concat(combinedArgs)) 97 | .then(deferred.resolve, deferred.reject); 98 | } 99 | 100 | //Tries to run the command from the top, not from a local volofile. 101 | function runTopCommand() { 102 | commands.get(commandName).then(function (command) { 103 | if (command) { 104 | runCommand(command); 105 | } else { 106 | //Show usage info. 107 | commands.list().then(function (message) { 108 | deferred.resolve(path.basename(process.argv[1]) + 109 | (' v' + config.volo.version).bold + 110 | ', a JavaScript tool to make ' + 111 | 'JavaScript projects. Allowed commands:\n\n' + 112 | message); 113 | }).fail(deferred.reject); 114 | } 115 | }).fail(deferred.reject); 116 | } 117 | 118 | if (!commandOverride && exists(path.resolve(cwd, 'volofile'))) { 119 | volofile(cwd).then(function (voloMod) { 120 | //Set up default command name if none specified. 121 | commandName = commandName || 'run'; 122 | 123 | if (voloMod.hasOwnProperty(commandName)) { 124 | runCommand(voloMod[commandName]); 125 | } else { 126 | runTopCommand(); 127 | } 128 | }) 129 | .fail(deferred.reject); 130 | } else { 131 | runTopCommand(); 132 | } 133 | 134 | return q.when(deferred.promise, callback, errback); 135 | } 136 | 137 | //Export the require used by volo, so that volofiles can take advantage 138 | //of modules in volo itself. 139 | main.require = require; 140 | 141 | //Load up the commands we know about. 142 | commands.register('add', require('./commands/add')); 143 | commands.register('install', require('./commands/install')); 144 | commands.register('update', require('./commands/update')); 145 | commands.register('amdify', require('./commands/amdify')); 146 | commands.register('create', require('./commands/create')); 147 | commands.register('remove', require('./commands/remove')); 148 | commands.register('help', require('./commands/help')); 149 | commands.register('npmrel', require('./commands/npmrel')); 150 | commands.register('search', require('./commands/search')); 151 | commands.register('info', require('./commands/info')); 152 | 153 | module.exports = main; 154 | --------------------------------------------------------------------------------