diff --git a/bin/now b/bin/now index 94dc17a..c238d6a 100755 --- a/bin/now +++ b/bin/now @@ -35,7 +35,8 @@ const commands = new Set([ 'cert', 'certs', 'secret', - 'secrets' + 'secrets', + 'static' ]); const aliases = new Map([ diff --git a/bin/now-static b/bin/now-static old mode 100644 new mode 100755 index 888959a..87b2aba --- a/bin/now-static +++ b/bin/now-static @@ -1,3 +1,104 @@ #!/usr/bin/env node -import chalk from 'chalk'; +// Native +import path from 'path' + +// Packages +import fs from 'fs-extra' +import tmp from 'tmp' +import args from 'args' +import chalk from 'chalk' +import md5 from 'md5' + +// Ours +import {copyContents, injectPackage} from '../lib/static' + +args + .option('cmd', 'The command to run when starting') + .option('packages', 'Custom packages to add to dependencies (comma-separated)') + .option('name', 'Custom name for deployment') + .option('arguments', 'Flags that you want to pass to now') + .option('single', 'Serve single page apps with just one index.html') + +const flags = args.parse(process.argv) + +if (flags.cmd && flags.single) { + console.log(chalk.red('The "single" flag only works if you\'re not using a custom command')) +} + +const file = args.sub[0] +let current = process.cwd() + +if (file) { + current = path.resolve(process.cwd(), file) +} + +if (!fs.existsSync(current)) { + console.error(chalk.red('Specified path doesn\'t exist!')) + process.exit(1) +} + +const uniqueIdentifier = md5(current) +const listCommand = 'list ./content' + (flags.single ? ' -s' : '') + +const pkgDefaults = { + name: 'ns', + version: '1.0.0', + scripts: { + start: flags.cmd || listCommand + }, + dependencies: { + list: 'latest' + } +} + +if (flags.packages) { + const list = flags.packages.split(',') + + for (const item of list) { + pkgDefaults.dependencies[item] = 'latest' + } + + if (flags.cmd) { + delete pkgDefaults.dependencies.list + } +} + +if (flags.name) { + pkgDefaults.name = flags.name +} + +let tmpDir = false + +try { + tmpDir = tmp.dirSync({ + // We need to use the hased directory identifier + // Because if we don't use the same id every time, + // now won't update the existing deployment and create a new one instead + name: `now-serve-${uniqueIdentifier}`, + + // Keep it, because we'll remove it manually later + keep: true + }) +} catch (err) { + throw err +} + +const details = fs.lstatSync(current) + +if (details.isDirectory()) { + copyContents(current, tmpDir.name, pkgDefaults, flags.arguments) +} else if (details.isFile()) { + const fileName = path.parse(current).base + const target = path.join(tmpDir.name, '/content', fileName) + + fs.copy(current, target, err => { + if (err) { + throw err + } + + injectPackage(tmpDir.name, pkgDefaults, flags.arguments) + }) +} else { + console.error(chalk.red('Path is neither a file nor a directory!')) +} diff --git a/lib/static.js b/lib/static.js new file mode 100644 index 0000000..b6d351a --- /dev/null +++ b/lib/static.js @@ -0,0 +1,111 @@ +// Native +import path from 'path' +import {spawn} from 'child_process' + +// Packages +import fs from 'fs-extra' +import {walk} from 'walk' + +export function injectPackage(tmpDir, defaults, flags) { + const pkgPath = path.join(tmpDir, 'package.json') + + fs.writeJSON(pkgPath, defaults, err => { + if (err) { + throw err + } + + exports.deploy(tmpDir, flags) + }) +} + +export function deploy(dir, flags) { + const oldCwd = process.cwd() + const cmd = process.platform === 'win32' ? 'now.cmd' : 'now' + + process.chdir(dir) + const flagsAllowed = typeof flags === 'string' + const flagList = [] + + if (flagsAllowed) { + let splitted = flags.split(', ') + + for (const item of splitted) { + if (item.indexOf(',') > -1) { + splitted = flags.split(',') + break + } + } + + for (const item of splitted) { + flagList.push(item) + } + } + + for (const flag of flagList) { + const index = flagList.indexOf(flag) + const prefix = flag.length > 1 ? '--' : '-' + + if (flag === '') { + flagList.splice(index, 1) + } else { + flagList[index] = prefix + flag + } + } + + // Run now and deploy + const now = spawn(cmd, flagList, { + stdio: 'inherit' + }) + + now.on('error', err => console.error(err)) + + now.on('exit', () => { + process.chdir(oldCwd) + exports.cleanup(dir) + }) + + process.on('SIGINT', () => { + now.kill('SIGINT') + exports.cleanup(dir) + }) +} + +export function cleanup(dir) { + fs.remove(dir, err => { + if (err) { + throw err + } + + process.exit() + }) +} + +export function copyContents(content, tmp, defaults, flags) { + // Ignore packages + const walker = walk(content, { + filters: [ + 'node_modules' + ] + }) + + walker.on('file', (root, fileStats, next) => { + const file = path.join(root, fileStats.name) + const target = path.join(tmp + '/content', path.relative(content, file)) + + // Once a file is found, copy it to the temp directory + fs.copy(file, target, err => { + if (err) { + throw err + } + + next() + }) + }) + + walker.on('errors', (root, nodeStatsArray, next) => { + console.error(`Not able to copy file: ${nodeStatsArray}`) + next() + }) + + walker.on('end', () => exports.injectPackage(tmp, defaults, flags)) +} diff --git a/package.json b/package.json index 1240622..253f593 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "ansi-escapes": "1.4.0", + "args": "2.1.0", "arr-flatten": "1.0.1", "array-unique": "0.3.2", "async-retry": "0.2.1", @@ -70,6 +71,7 @@ "graceful-fs": "4.1.9", "ignore": "3.2.0", "ini": "1.3.4", + "md5": "2.2.1", "minimatch": "3.0.3", "minimist": "1.2.0", "ms": "0.7.1", @@ -80,7 +82,9 @@ "socket.io-client": "1.4.8", "spdy": "3.4.4", "split-array": "1.0.1", - "text-table": "0.2.0" + "text-table": "0.2.0", + "tmp": "0.0.29", + "walk": "2.3.9" }, "devDependencies": { "alpha-sort": "1.0.2",