tiged
Version:
Straightforward project scaffolding
606 lines (514 loc) • 17.3 kB
JavaScript
;
var fs = require('fs');
var path = require('path');
var colorette = require('colorette');
var mri = require('mri');
var os = require('os');
var fuzzysearch = require('fuzzysearch');
var enquirer = require('enquirer');
var index = require('./index-e42359c2.js');
require('tar');
require('events');
require('home-or-tmp');
require('https');
require('child_process');
require('url');
require('https-proxy-agent');
require('sander');
require('rimraf');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var mri__default = /*#__PURE__*/_interopDefaultLegacy(mri);
var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
var fuzzysearch__default = /*#__PURE__*/_interopDefaultLegacy(fuzzysearch);
var enquirer__default = /*#__PURE__*/_interopDefaultLegacy(enquirer);
const isWin = process.platform === 'win32';
const SEP = isWin ? `\\\\+` : `\\/`;
const SEP_ESC = isWin ? `\\\\` : `/`;
const GLOBSTAR = `((?:[^/]*(?:/|$))*)`;
const WILDCARD = `([^/]*)`;
const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`;
const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`;
/**
* Convert any glob pattern to a JavaScript Regexp object
* @param {String} glob Glob pattern to convert
* @param {Object} opts Configuration object
* @param {Boolean} [opts.extended=false] Support advanced ext globbing
* @param {Boolean} [opts.globstar=false] Support globstar
* @param {Boolean} [opts.strict=true] be laissez faire about mutiple slashes
* @param {Boolean} [opts.filepath=''] Parse as filepath for extra path related features
* @param {String} [opts.flags=''] RegExp globs
* @returns {Object} converted object with string, segments and RegExp object
*/
function globrex(glob, {extended = false, globstar = false, strict = false, filepath = false, flags = ''} = {}) {
let regex = '';
let segment = '';
let path = { regex: '', segments: [] };
// If we are doing extended matching, this boolean is true when we are inside
// a group (eg {*.html,*.js}), and false otherwise.
let inGroup = false;
let inRange = false;
// extglob stack. Keep track of scope
const ext = [];
// Helper function to build string and segments
function add(str, {split, last, only}={}) {
if (only !== 'path') regex += str;
if (filepath && only !== 'regex') {
path.regex += (str === '\\/' ? SEP : str);
if (split) {
if (last) segment += str;
if (segment !== '') {
if (!flags.includes('g')) segment = `^${segment}$`; // change it 'includes'
path.segments.push(new RegExp(segment, flags));
}
segment = '';
} else {
segment += str;
}
}
}
let c, n;
for (let i = 0; i < glob.length; i++) {
c = glob[i];
n = glob[i + 1];
if (['\\', '$', '^', '.', '='].includes(c)) {
add(`\\${c}`);
continue;
}
if (c === '/') {
add(`\\${c}`, {split: true});
if (n === '/' && !strict) regex += '?';
continue;
}
if (c === '(') {
if (ext.length) {
add(c);
continue;
}
add(`\\${c}`);
continue;
}
if (c === ')') {
if (ext.length) {
add(c);
let type = ext.pop();
if (type === '@') {
add('{1}');
} else if (type === '!') {
add('([^\/]*)');
} else {
add(type);
}
continue;
}
add(`\\${c}`);
continue;
}
if (c === '|') {
if (ext.length) {
add(c);
continue;
}
add(`\\${c}`);
continue;
}
if (c === '+') {
if (n === '(' && extended) {
ext.push(c);
continue;
}
add(`\\${c}`);
continue;
}
if (c === '@' && extended) {
if (n === '(') {
ext.push(c);
continue;
}
}
if (c === '!') {
if (extended) {
if (inRange) {
add('^');
continue
}
if (n === '(') {
ext.push(c);
add('(?!');
i++;
continue;
}
add(`\\${c}`);
continue;
}
add(`\\${c}`);
continue;
}
if (c === '?') {
if (extended) {
if (n === '(') {
ext.push(c);
} else {
add('.');
}
continue;
}
add(`\\${c}`);
continue;
}
if (c === '[') {
if (inRange && n === ':') {
i++; // skip [
let value = '';
while(glob[++i] !== ':') value += glob[i];
if (value === 'alnum') add('(\\w|\\d)');
else if (value === 'space') add('\\s');
else if (value === 'digit') add('\\d');
i++; // skip last ]
continue;
}
if (extended) {
inRange = true;
add(c);
continue;
}
add(`\\${c}`);
continue;
}
if (c === ']') {
if (extended) {
inRange = false;
add(c);
continue;
}
add(`\\${c}`);
continue;
}
if (c === '{') {
if (extended) {
inGroup = true;
add('(');
continue;
}
add(`\\${c}`);
continue;
}
if (c === '}') {
if (extended) {
inGroup = false;
add(')');
continue;
}
add(`\\${c}`);
continue;
}
if (c === ',') {
if (inGroup) {
add('|');
continue;
}
add(`\\${c}`);
continue;
}
if (c === '*') {
if (n === '(' && extended) {
ext.push(c);
continue;
}
// Move over all consecutive "*"'s.
// Also store the previous and next characters
let prevChar = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === '*') {
starCount++;
i++;
}
let nextChar = glob[i + 1];
if (!globstar) {
// globstar is disabled, so treat any number of "*" as one
add('.*');
} else {
// globstar is enabled, so determine if this is a globstar segment
let isGlobstar =
starCount > 1 && // multiple "*"'s
(prevChar === '/' || prevChar === undefined) && // from the start of the segment
(nextChar === '/' || nextChar === undefined); // to the end of the segment
if (isGlobstar) {
// it's a globstar, so match zero or more path segments
add(GLOBSTAR, {only:'regex'});
add(GLOBSTAR_SEGMENT, {only:'path', last:true, split:true});
i++; // move over the "/"
} else {
// it's not a globstar, so only match one path segment
add(WILDCARD, {only:'regex'});
add(WILDCARD_SEGMENT, {only:'path'});
}
}
continue;
}
add(c);
}
// When regexp 'g' flag is specified don't
// constrain the regular expression with ^ & $
if (!flags.includes('g')) {
regex = `^${regex}$`;
segment = `^${segment}$`;
if (filepath) path.regex = `^${path.regex}$`;
}
const result = {regex: new RegExp(regex, flags)};
// Push the last segment
if (filepath) {
path.segments.push(new RegExp(segment, flags));
path.regex = new RegExp(path.regex, flags);
path.globstar = new RegExp(!flags.includes('g') ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT, flags);
result.path = path;
}
return result;
}
var globrex_1 = globrex;
const isWin$1 = os__default['default'].platform() === 'win32';
const CHARS = { '{': '}', '(': ')', '[': ']'};
const STRICT = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\)|(\\).|([@?!+*]\(.*\)))/;
const RELAXED = /\\(.)|(^!|[*?{}()[\]]|\(\?)/;
/**
* Detect if a string cointains glob
* @param {String} str Input string
* @param {Object} [options] Configuration object
* @param {Boolean} [options.strict=true] Use relaxed regex if true
* @returns {Boolean} true if string contains glob
*/
function isglob(str, { strict = true } = {}) {
if (str === '') return false;
let match, rgx = strict ? STRICT : RELAXED;
while ((match = rgx.exec(str))) {
if (match[2]) return true;
let idx = match.index + match[0].length;
// if an open bracket/brace/paren is escaped,
// set the index to the next closing character
let open = match[1];
let close = open ? CHARS[open] : null;
if (open && close) {
let n = str.indexOf(close, idx);
if (n !== -1) idx = n + 1;
}
str = str.slice(idx);
}
return false;
}
/**
* Find the static part of a glob-path,
* split path and return path part
* @param {String} str Path/glob string
* @returns {String} static path section of glob
*/
function parent(str, { strict = false } = {}) {
if (isWin$1 && str.includes('/'))
str = str.split('\\').join('/');
// special case for strings ending in enclosure containing path separator
if (/[\{\[].*[\/]*.*[\}\]]$/.test(str)) str += '/';
// preserves full path in case of trailing path separator
str += 'a';
do {str = path__default['default'].dirname(str);}
while (isglob(str, {strict}) || /(^|[^\\])([\{\[]|\([^\)]+$)/.test(str));
// remove escape chars and return result
return str.replace(/\\([\*\?\|\[\]\(\)\{\}])/g, '$1');
}
/**
* Parse a glob path, and split it by static/glob part
* @param {String} pattern String path
* @param {Object} [opts] Options
* @param {Object} [opts.strict=false] Use strict parsing
* @returns {Object} object with parsed path
*/
function globalyzer(pattern, opts = {}) {
let base = parent(pattern, opts);
let isGlob = isglob(pattern, opts);
let glob;
if (base != '.') {
glob = pattern.substr(base.length);
if (glob.startsWith('/')) glob = glob.substr(1);
} else {
glob = pattern;
}
if (!isGlob) {
base = path__default['default'].dirname(pattern);
glob = base !== '.' ? pattern.substr(base.length) : pattern;
}
if (glob.startsWith('./')) glob = glob.substr(2);
if (glob.startsWith('/')) glob = glob.substr(1);
return { base, glob, isGlob };
}
var src = globalyzer;
const { join, resolve, relative } = path__default['default'];
const isHidden = /(^|[\\\/])\.[^\\\/\.]/g;
let CACHE = {};
function walk(output, prefix, lexer, opts, dirname='', level=0) {
const rgx = lexer.segments[level];
const dir = resolve(opts.cwd, prefix, dirname);
const files = fs__default['default'].readdirSync(dir);
const { dot, filesOnly } = opts;
let i=0, len=files.length, file;
let fullpath, relpath, stats, isMatch;
for (; i < len; i++) {
fullpath = join(dir, file=files[i]);
relpath = dirname ? join(dirname, file) : file;
if (!dot && isHidden.test(relpath)) continue;
isMatch = lexer.regex.test(relpath);
if ((stats=CACHE[relpath]) === void 0) {
CACHE[relpath] = stats = fs__default['default'].lstatSync(fullpath);
}
if (!stats.isDirectory()) {
isMatch && output.push(relative(opts.cwd, fullpath));
continue;
}
if (rgx && !rgx.test(file)) continue;
!filesOnly && isMatch && output.push(join(prefix, relpath));
walk(output, prefix, lexer, opts, relpath, rgx && rgx.toString() !== lexer.globstar && level + 1);
}
}
/**
* Find files using bash-like globbing.
* All paths are normalized compared to node-glob.
* @param {String} str Glob string
* @param {String} [options.cwd='.'] Current working directory
* @param {Boolean} [options.dot=false] Include dotfile matches
* @param {Boolean} [options.absolute=false] Return absolute paths
* @param {Boolean} [options.filesOnly=false] Do not include folders if true
* @param {Boolean} [options.flush=false] Reset cache object
* @returns {Array} array containing matching files
*/
var sync = function (str, opts={}) {
if (!str) return [];
let glob = src(str);
opts.cwd = opts.cwd || '.';
if (!glob.isGlob) {
try {
let resolved = resolve(opts.cwd, str);
let dirent = fs__default['default'].statSync(resolved);
if (opts.filesOnly && !dirent.isFile()) return [];
return opts.absolute ? [resolved] : [str];
} catch (err) {
if (err.code != 'ENOENT') throw err;
return [];
}
}
if (opts.flush) CACHE = {};
let matches = [];
const { path } = globrex_1(glob.glob, { filepath:true, globstar:true, extended:true });
path.globstar = path.globstar.toString();
walk(matches, glob.base, path, opts, '.', 0);
return opts.absolute ? matches.map(x => resolve(opts.cwd, x)) : matches;
};
const args = mri__default['default'](process.argv.slice(2), {
alias: {
f: 'force',
c: 'cache',
v: 'verbose',
m: 'mode',
s: 'subgroup',
d: 'sub-directory'
},
boolean: ['force', 'cache', 'verbose', 'subgroup']
});
const [src$1, dest = '.'] = args._;
async function main() {
if (args.help) {
const help = fs__default['default']
.readFileSync(path__default['default'].join(__dirname, '..', 'help.md'), 'utf-8')
.replace(/^(\s*)#+ (.+)/gm, (m, s, _) => s + colorette.bold(_))
.replace(/_([^_]+)_/g, (m, _) => colorette.underline(_))
.replace(/`([^`]+)`/g, (m, _) => colorette.cyan(_)); //` syntax highlighter fix
process.stdout.write(`\n${help}\n`);
} else if (!src$1) {
// interactive mode
const accessLookup = new Map();
sync(`**/access.json`, { cwd: index.base }).forEach(file => {
const [host, user, repo] = file.split(path__default['default'].sep);
const json = fs__default['default'].readFileSync(`${index.base}/${file}`, 'utf-8');
const logs = JSON.parse(json);
Object.entries(logs).forEach(([ref, timestamp]) => {
const id = `${host}:${user}/${repo}#${ref}`;
accessLookup.set(id, new Date(timestamp).getTime());
});
});
const getChoice = file => {
const [host, user, repo] = file.split(path__default['default'].sep);
return Object.entries(index.tryRequire(`${index.base}/${file}`)).map(
([ref, hash]) => ({
name: hash,
message: `${host}:${user}/${repo}#${ref}`,
value: `${host}:${user}/${repo}#${ref}`
})
);
};
const choices = sync(`**/map.json`, { cwd: index.base })
.map(getChoice)
.reduce(
(accumulator, currentValue) => accumulator.concat(currentValue),
[]
)
.sort((a, b) => {
const aTime = accessLookup.get(a.value) || 0;
const bTime = accessLookup.get(b.value) || 0;
return bTime - aTime;
});
const options = await enquirer__default['default'].prompt([
{
type: 'autocomplete',
name: 'src',
message: 'Repo to clone?',
suggest: (input, choices) =>
choices.filter(({ value }) => fuzzysearch__default['default'](input, value)),
choices
},
{
type: 'input',
name: 'dest',
message: 'Destination directory?',
initial: '.'
},
{
type: 'toggle',
name: 'cache',
message: 'Use cached version?'
}
]);
const empty =
!fs__default['default'].existsSync(options.dest) || fs__default['default'].readdirSync(options.dest).length === 0;
if (!empty) {
const { force } = await enquirer__default['default'].prompt([
{
type: 'toggle',
name: 'force',
message: 'Overwrite existing files?'
}
]);
if (!force) {
console.error(colorette.magenta(`! Directory not empty — aborting`));
return;
}
}
run(options.src, options.dest, {
force: true,
cache: options.cache
});
} else {
run(src$1, dest, args);
}
}
function run(src, dest, args) {
const d = index.degit(src, args);
d.on('info', event => {
console.error(colorette.cyan(`> ${event.message.replace('options.', '--')}`));
});
d.on('warn', event => {
console.error(colorette.magenta(`! ${event.message.replace('options.', '--')}`));
});
d.clone(dest).catch(err => {
console.error(colorette.red(`! ${err.message.replace('options.', '--')}`));
process.exit(1);
});
}
main();
//# sourceMappingURL=bin.js.map