From 89ba9269cfeb1df6b8d77b3c99fb4a86a154a73c Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Sat, 12 Nov 2022 21:33:02 +0400 Subject: [PATCH 1/6] Add `nunjucks` and update `@actions/core` --- package-lock.json | 101 +++++++++++++++++++++++++++++++++++++++++----- package.json | 3 +- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8e2492cf..583524a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,8 @@ "action-input-parser": "^1.2.33", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", - "node-readfiles": "0.2.0" + "node-readfiles": "0.2.0", + "nunjucks": "^3.2.3" }, "devDependencies": { "@betahuhn/config": "^1.2.0", @@ -26,11 +27,12 @@ } }, "node_modules/@actions/core": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz", - "integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", "dependencies": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "node_modules/@actions/github": { @@ -745,6 +747,11 @@ "ncc": "dist/ncc/cli.js" } }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -913,6 +920,11 @@ "node": ">=0.10.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1100,6 +1112,14 @@ "node": ">=0.1.90" } }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -6220,6 +6240,30 @@ "inBundle": true, "license": "ISC" }, + "node_modules/nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/object-inspect": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", @@ -7665,6 +7709,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -7843,11 +7895,12 @@ }, "dependencies": { "@actions/core": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz", - "integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", "requires": { - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" } }, "@actions/github": { @@ -8479,6 +8532,11 @@ "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==", "dev": true }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -8604,6 +8662,11 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -8752,6 +8815,11 @@ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, "compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -12574,6 +12642,16 @@ "path-key": "^3.0.0" } }, + "nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + } + }, "object-inspect": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", @@ -13660,6 +13738,11 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index c03b431e..cf31fc64 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "action-input-parser": "^1.2.33", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", - "node-readfiles": "0.2.0" + "node-readfiles": "0.2.0", + "nunjucks": "^3.2.3" }, "devDependencies": { "@betahuhn/config": "^1.2.0", From 01ece5263a6b06392baf213210f5415b87bebdd0 Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Sat, 12 Nov 2022 23:18:16 +0400 Subject: [PATCH 2/6] Read template info from YAML config file --- src/config.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/config.js b/src/config.js index 56c17fff..e9067083 100644 --- a/src/config.js +++ b/src/config.js @@ -5,6 +5,7 @@ const path = require('path') const { getInput } = require('action-input-parser') const REPLACE_DEFAULT = true +const TEMPLATE_DEFAULT = false const DELETE_ORPHANED_DEFAULT = false let context @@ -174,20 +175,14 @@ const parseExclude = (text, src) => { const parseFiles = (files) => { return files.map((item) => { - - if (typeof item === 'string') { - return { - source: item, - dest: item, - replace: REPLACE_DEFAULT, - deleteOrphaned: DELETE_ORPHANED_DEFAULT - } - } + if (typeof item === 'string') + item = { source: item } if (item.source !== undefined) { return { source: item.source, dest: item.dest || item.source, + template: item.template === undefined ? TEMPLATE_DEFAULT : item.template, replace: item.replace === undefined ? REPLACE_DEFAULT : item.replace, deleteOrphaned: item.deleteOrphaned === undefined ? DELETE_ORPHANED_DEFAULT : item.deleteOrphaned, exclude: parseExclude(item.exclude, item.source) From 6438249b3e5b7cc952abc1c0ac0b17769ec33e02 Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Sat, 12 Nov 2022 23:19:09 +0400 Subject: [PATCH 3/6] Render templates when copying files --- src/helpers.js | 40 +++++++++++++++++++++++++++++++++++----- src/index.js | 4 +--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index 5c3aa40c..d45341bd 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -3,6 +3,9 @@ const readfiles = require('node-readfiles') const { exec } = require('child_process') const core = require('@actions/core') const path = require('path') +const nunjucks = require('nunjucks') + +nunjucks.configure({ autoescape: true, trimBlocks: true, lstripBlocks: true }) // From https://github.com/toniov/p-iteration/blob/master/lib/static-methods.js - MIT © Antonio V const forEach = async (array, callback) => { @@ -63,13 +66,19 @@ const pathIsDirectory = async (path) => { return stat.isDirectory() } -const copy = async (src, dest, deleteOrphaned, exclude) => { +const write = async (src, dest, context) => { + if (typeof context !== 'object') { + context = {} + } + const content = nunjucks.render(src, context) + await fs.outputFile(dest, content) +} - core.debug(`CP: ${ src } TO ${ dest }`) +const copy = async (src, dest, isDirectory, file) => { + const deleteOrphaned = isDirectory && file.deleteOrphaned const filterFunc = (file) => { - - if (exclude !== undefined && exclude.includes(file)) { + if (file.exclude !== undefined && file.exclude.includes(file)) { core.debug(`Excluding file ${ file }`) return false } @@ -77,7 +86,28 @@ const copy = async (src, dest, deleteOrphaned, exclude) => { return true } - await fs.copy(src, dest, exclude !== undefined && { filter: filterFunc }) + if (file.template) { + if (isDirectory) { + core.debug(`Render all files in directory ${ src } to ${ dest }`) + + const srcFileList = await readfiles(src, { readContents: false, hidden: true }) + for (const file of srcFileList) { + if (!filterFunc(file)) { continue } + + const srcPath = path.join(src, file) + const destPath = path.join(dest, file) + await write(srcPath, destPath, file.template) + } + } else { + core.debug(`Render file ${ src } to ${ dest }`) + + await write(src, dest, file.template) + } + } else { + core.debug(`Copy ${ src } to ${ dest }`) + await fs.copy(src, dest, file.exclude !== undefined && { filter: filterFunc }) + } + // If it is a directory and deleteOrphaned is enabled - check if there are any files that were removed from source dir and remove them in destination dir if (deleteOrphaned) { diff --git a/src/index.js b/src/index.js index 53c62907..18be6315 100644 --- a/src/index.js +++ b/src/index.js @@ -73,9 +73,7 @@ const run = async () => { if (isDirectory) core.info(`Source is directory`) - const deleteOrphaned = isDirectory && file.deleteOrphaned - - await copy(source, dest, deleteOrphaned, file.exclude) + await copy(source, dest, isDirectory, file) await git.add(file.dest) From c338094c4942aa9790ea284d7c3bb8084f6ef392 Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Sat, 12 Nov 2022 23:20:04 +0400 Subject: [PATCH 4/6] Compile JS files into action --- dist/index.js | 9559 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 9079 insertions(+), 480 deletions(-) diff --git a/dist/index.js b/dist/index.js index f0bdc734..7199f03c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -169,13 +169,9 @@ function exportVariable(name, val) { process.env[name] = convertedVal; const filePath = process.env['GITHUB_ENV'] || ''; if (filePath) { - const delimiter = '_GitHubActionsFileCommandDelimeter_'; - const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`; - file_command_1.issueCommand('ENV', commandValue); - } - else { - command_1.issueCommand('set-env', { name }, convertedVal); + return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); } + command_1.issueCommand('set-env', { name }, convertedVal); } exports.exportVariable = exportVariable; /** @@ -193,7 +189,7 @@ exports.setSecret = setSecret; function addPath(inputPath) { const filePath = process.env['GITHUB_PATH'] || ''; if (filePath) { - file_command_1.issueCommand('PATH', inputPath); + file_command_1.issueFileCommand('PATH', inputPath); } else { command_1.issueCommand('add-path', {}, inputPath); @@ -233,7 +229,10 @@ function getMultilineInput(name, options) { const inputs = getInput(name, options) .split('\n') .filter(x => x !== ''); - return inputs; + if (options && options.trimWhitespace === false) { + return inputs; + } + return inputs.map(input => input.trim()); } exports.getMultilineInput = getMultilineInput; /** @@ -266,8 +265,12 @@ exports.getBooleanInput = getBooleanInput; */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function setOutput(name, value) { + const filePath = process.env['GITHUB_OUTPUT'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); + } process.stdout.write(os.EOL); - command_1.issueCommand('set-output', { name }, value); + command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); } exports.setOutput = setOutput; /** @@ -396,7 +399,11 @@ exports.group = group; */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function saveState(name, value) { - command_1.issueCommand('save-state', { name }, value); + const filePath = process.env['GITHUB_STATE'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); + } + command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); } exports.saveState = saveState; /** @@ -425,6 +432,13 @@ Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () */ var summary_2 = __nccwpck_require__(1327); Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); +/** + * Path exports + */ +var path_utils_1 = __nccwpck_require__(2981); +Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); +Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); +Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); //# sourceMappingURL=core.js.map /***/ }), @@ -455,13 +469,14 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.issueCommand = void 0; +exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; // We use any as a valid input type /* eslint-disable @typescript-eslint/no-explicit-any */ const fs = __importStar(__nccwpck_require__(7147)); const os = __importStar(__nccwpck_require__(2037)); +const uuid_1 = __nccwpck_require__(5840); const utils_1 = __nccwpck_require__(5278); -function issueCommand(command, message) { +function issueFileCommand(command, message) { const filePath = process.env[`GITHUB_${command}`]; if (!filePath) { throw new Error(`Unable to find environment variable for file command ${command}`); @@ -473,7 +488,22 @@ function issueCommand(command, message) { encoding: 'utf8' }); } -exports.issueCommand = issueCommand; +exports.issueFileCommand = issueFileCommand; +function prepareKeyValueMessage(key, value) { + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + const convertedValue = utils_1.toCommandValue(value); + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedValue.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; +} +exports.prepareKeyValueMessage = prepareKeyValueMessage; //# sourceMappingURL=file-command.js.map /***/ }), @@ -562,6 +592,71 @@ exports.OidcClient = OidcClient; /***/ }), +/***/ 2981: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; +const path = __importStar(__nccwpck_require__(1017)); +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +function toPosixPath(pth) { + return pth.replace(/[\\]/g, '/'); +} +exports.toPosixPath = toPosixPath; +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +function toWin32Path(pth) { + return pth.replace(/[/]/g, '\\'); +} +exports.toWin32Path = toWin32Path; +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +function toPlatformPath(pth) { + return pth.replace(/[/\\]/g, path.sep); +} +exports.toPlatformPath = toPlatformPath; +//# sourceMappingURL=path-utils.js.map + +/***/ }), + /***/ 1327: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { @@ -6535,6 +6630,96 @@ function pick(files, {added, modified, deleted, untracked, renamed}) { +/***/ }), + +/***/ 3980: +/***/ (function(module) { + +// MIT license (by Elan Shanker). +(function(globals) { + 'use strict'; + + var executeSync = function(){ + var args = Array.prototype.slice.call(arguments); + if (typeof args[0] === 'function'){ + args[0].apply(null, args.splice(1)); + } + }; + + var executeAsync = function(fn){ + if (typeof setImmediate === 'function') { + setImmediate(fn); + } else if (typeof process !== 'undefined' && process.nextTick) { + process.nextTick(fn); + } else { + setTimeout(fn, 0); + } + }; + + var makeIterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1): null; + }; + return fn; + }; + return makeCallback(0); + }; + + var _isArray = Array.isArray || function(maybeArray){ + return Object.prototype.toString.call(maybeArray) === '[object Array]'; + }; + + var waterfall = function (tasks, callback, forceAsync) { + var nextTick = forceAsync ? executeAsync : executeSync; + callback = callback || function () {}; + if (!_isArray(tasks)) { + var err = new Error('First argument to waterfall must be an array of functions'); + return callback(err); + } + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback.apply(null, arguments); + callback = function () {}; + } else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } else { + args.push(callback); + } + nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(makeIterator(tasks))(); + }; + + if (typeof define !== 'undefined' && define.amd) { + define([], function () { + return waterfall; + }); // RequireJS + } else if ( true && module.exports) { + module.exports = waterfall; // CommonJS + } else { + globals.waterfall = waterfall; //