[
  {
    "path": ".dependabot/config.yml",
    "content": "version: 1\nupdate_configs:\n  - package_manager: 'javascript'\n    directory: '/'\n    update_schedule: 'weekly'\n    target_branch: 'master'\n"
  },
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 2\nindent_style = tab\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\nindent_style = tab\ntrim_trailing_whitespace = false\n\n[{*.json,*.yml}]\nindent_style = tab\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n\t\"root\": true,\n\t\"rules\": {\n\t\t\"no-cond-assign\": 0,\n\t\t\"no-unused-vars\": 2,\n\t\t\"object-shorthand\": [2, \"always\"],\n\t\t\"no-console\": 0,\n\t\t\"no-const-assign\": 2,\n\t\t\"no-class-assign\": 2,\n\t\t\"no-this-before-super\": 2,\n\t\t\"no-var\": 2,\n\t\t\"no-unreachable\": 2,\n\t\t\"valid-typeof\": 2,\n\t\t\"one-var\": [2, \"never\"],\n\t\t\"prefer-arrow-callback\": 2,\n\t\t\"prefer-const\": [2, { \"destructuring\": \"all\" }],\n\t\t\"no-inner-declarations\": 0\n\t},\n\t\"env\": {\n\t\t\"es6\": true,\n\t\t\"node\": true,\n\t\t\"mocha\": true\n\t},\n\t\"extends\": [\n\t\t\"eslint:recommended\",\n\t\t\"plugin:import/errors\",\n\t\t\"plugin:import/warnings\",\n\t\t\"prettier\"\n\t],\n\t\"parserOptions\": {\n\t\t\"ecmaVersion\": 8,\n\t\t\"sourceType\": \"module\"\n\t}\n}\n"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "content": "name: Node.js CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [8.x, 10.x, 12.x]\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node-version }}\n    - run: npm ci\n    - run: npm run build\n    - run: npm test\n    - run: npm run lint\n      env:\n        CI: true\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n.tmp\ndist"
  },
  {
    "path": ".npmrc",
    "content": "save-exact=true\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n\t\"arrowParens\": \"avoid\",\n\t\"bracketSpacing\": true,\n\t\"endOfLine\": \"lf\",\n\t\"htmlWhitespaceSensitivity\": \"css\",\n\t\"insertPragma\": false,\n\t\"printWidth\": 80,\n\t\"proseWrap\": \"preserve\",\n\t\"requirePragma\": false,\n\t\"quoteProps\": \"as-needed\",\n\t\"semi\": true,\n\t\"singleQuote\": true,\n\t\"tabWidth\": 2,\n\t\"trailingComma\": false,\n\t\"useTabs\": true\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - '8'\n  - '10'\n  - lts/*\n  - node\n\naddons:\n  apt:\n    sources:\n      - ubuntu-toolchain-r-test\n    packages:\n      - libstdc++-4.9-dev\n\nscript:\n  - npm run lint\n  - npm test"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# degit changelog\n\n## 2.8.4\n\n* Whoops\n\n## 2.8.3\n\n* Reinstate `#!/usr/bin/env node` ([#273](https://github.com/Rich-Harris/degit/issues/273))\n\n## 2.8.2\n\n* Fix `bin`/`main` locations ([#273](https://github.com/Rich-Harris/degit/issues/273))\n* Update dependencies\n\n## 2.8.1\n\n* Use `HEAD` instead of `master` ([#243](https://github.com/Rich-Harris/degit/pull/243)])\n\n## 2.8.0\n\n* Sort by recency in interactive mode\n\n## 2.7.0\n\n* Bundle for a faster install\n\n## 2.6.0\n\n* Add an interactive mode ([#4](https://github.com/Rich-Harris/degit/issues/4))\n\n## 2.5.0\n\n* Add `--mode=git` for cloning private repos ([#29](https://github.com/Rich-Harris/degit/pull/29))\n\n## 2.4.0\n\n* Clone subdirectories from repos (`user/repo/subdir`)\n\n## 2.3.0\n\n* Support HTTPS proxying where `https_proxy` env var is supplied ([#26](https://github.com/Rich-Harris/degit/issues/26))\n\n## 2.2.2\n\n- Improve CLI error logging ([#49](https://github.com/Rich-Harris/degit/pull/49))\n\n## 2.2.1\n\n- Update `help.md` for Sourcehut support\n\n## 2.2.0\n\n- Sourcehut support ([#85](https://github.com/Rich-Harris/degit/pull/85))\n\n## 2.1.4\n\n- Fix actions ([#65](https://github.com/Rich-Harris/degit/pull/65))\n- Improve CLI error logging ([#46](https://github.com/Rich-Harris/degit/pull/46))\n\n## 2.1.3\n\n- Install `sander` ([#34](https://github.com/Rich-Harris/degit/issues/34))\n\n## 2.1.2\n\n- Remove `console.log`\n\n## 2.1.1\n\n- Oops, managed to publish 2.1.0 without building\n\n## 2.1.0\n\n- Add actions ([#28](https://github.com/Rich-Harris/degit/pull/28))\n\n## 2.0.2\n\n- Allow flags like `-v` before argument ([#25](https://github.com/Rich-Harris/degit/issues/25))\n\n## 2.0.1\n\n- Update node-tar for Node 9 compatibility\n\n## 2.0.0\n\n- Expose API for use in Node scripts ([#23](https://github.com/Rich-Harris/degit/issues/23))\n\n## 1.2.2\n\n- Fix `files` in package.json\n\n## 1.2.1\n\n- Add `engines` field ([#17](https://github.com/Rich-Harris/degit/issues/17))\n\n## 1.2.0\n\n- Windows support ([#1](https://github.com/Rich-Harris/degit/issues/1))\n- Offline support and `--cache` flag ([#8](https://github.com/Rich-Harris/degit/issues/8))\n- `degit --help` ([#5](https://github.com/Rich-Harris/degit/issues/5))\n- `--verbose` flag\n\n## 1.1.0\n\n- Use HTTPS, not SSH ([#11](https://github.com/Rich-Harris/degit/issues/11))\n\n## 1.0.0\n\n- First release\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at yogiboaron@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2019 [these people](https://github.com/Rich-Harris/degit/graphs/contributors)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# degit — straightforward project scaffolding\n\n[![Travis CI build status](https://badgen.net/travis/Rich-Harris/degit/master)](https://travis-ci.org/Rich-Harris/degit)\n[![AppVeyor build status](https://badgen.net/appveyor/ci/Rich-Harris/degit/master)](https://ci.appveyor.com/project/Rich-Harris/degit/branch/master)\n[![Known Vulnerabilities](https://snyk.io/test/npm/degit/badge.svg)](https://snyk.io/test/npm/degit)\n[![install size](https://badgen.net/packagephobia/install/degit)](https://packagephobia.now.sh/result?p=degit)\n[![npm package version](https://badgen.net/npm/v/degit)](https://npm.im/degit)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)\n\n**degit** makes copies of git repositories. When you run `degit some-user/some-repo`, it will find the latest commit on https://github.com/some-user/some-repo and download the associated tar file to `~/.degit/some-user/some-repo/commithash.tar.gz` if it doesn't already exist locally. (This is much quicker than using `git clone`, because you're not downloading the entire git history.)\n\n_Requires Node 8 or above, because `async` and `await` are the cat's pyjamas_\n\n## Installation\n\n```bash\nnpm install -g degit\n```\n\n## Usage\n\n### Basics\n\nThe simplest use of degit is to download the master branch of a repo from GitHub to the current working directory:\n\n```bash\ndegit user/repo\n\n# these commands are equivalent\ndegit github:user/repo\ndegit git@github.com:user/repo\ndegit https://github.com/user/repo\n```\n\nOr you can download from GitLab and BitBucket:\n\n```bash\n# download from GitLab\ndegit gitlab:user/repo\ndegit git@gitlab.com:user/repo\ndegit https://gitlab.com/user/repo\n\n# download from BitBucket\ndegit bitbucket:user/repo\ndegit git@bitbucket.org:user/repo\ndegit https://bitbucket.org/user/repo\n\n# download from Sourcehut\ndegit git.sr.ht/user/repo\ndegit git@git.sr.ht:user/repo\ndegit https://git.sr.ht/user/repo\n```\n\n### Specify a tag, branch or commit\n\nThe default branch is `master`.\n\n```bash\ndegit user/repo#dev       # branch\ndegit user/repo#v1.2.3    # release tag\ndegit user/repo#1234abcd  # commit hash\n````\n\n### Create a new folder for the project\n\nIf the second argument is omitted, the repo will be cloned to the current directory.\n\n```bash\ndegit user/repo my-new-project\n```\n\n### Specify a subdirectory\n\nTo clone a specific subdirectory instead of the entire repo, just add it to the argument:\n\n```bash\ndegit user/repo/subdirectory\n```\n\n### HTTPS proxying\n\nIf you have an `https_proxy` environment variable, Degit will use it.\n\n### Private repositories\n\nPrivate repos can be cloned by specifying `--mode=git` (the default is `tar`). In this mode, Degit will use `git` under the hood. It's much slower than fetching a tarball, which is why it's not the default.\n\nNote: this clones over SSH, not HTTPS.\n\n### See all options\n\n```bash\ndegit --help\n```\n\n## Not supported\n\n- Private repositories\n\nPull requests are very welcome!\n\n## Wait, isn't this just `git clone --depth 1`?\n\nA few salient differences:\n\n- If you `git clone`, you get a `.git` folder that pertains to the project template, rather than your project. You can easily forget to re-init the repository, and end up confusing yourself\n- Caching and offline support (if you already have a `.tar.gz` file for a specific commit, you don't need to fetch it again).\n- Less to type (`degit user/repo` instead of `git clone --depth 1 git@github.com:user/repo`)\n- Composability via [actions](#actions)\n- Future capabilities — [interactive mode](https://github.com/Rich-Harris/degit/issues/4), [friendly onboarding and postinstall scripts](https://github.com/Rich-Harris/degit/issues/6)\n\n## JavaScript API\n\nYou can also use degit inside a Node script:\n\n```js\nconst degit = require('degit');\n\nconst emitter = degit('user/repo', {\n\tcache: true,\n\tforce: true,\n\tverbose: true,\n});\n\nemitter.on('info', info => {\n\tconsole.log(info.message);\n});\n\nemitter.clone('path/to/dest').then(() => {\n\tconsole.log('done');\n});\n```\n\n## Actions\n\nYou can manipulate repositories after they have been cloned with _actions_, specified in a `degit.json` file that lives at the top level of the working directory. Currently, there are two actions — `clone` and `remove`. Additional actions may be added in future.\n\n### clone\n\n```json\n// degit.json\n[\n\t{\n\t\t\"action\": \"clone\",\n\t\t\"src\": \"user/another-repo\"\n\t}\n]\n```\n\nThis will clone `user/another-repo`, preserving the contents of the existing working directory. This allows you to, say, add a new README.md or starter file to a repo that you do not control. The cloned repo can contain its own `degit.json` actions.\n\n### remove\n\n```json\n// degit.json\n[\n\t{\n\t\t\"action\": \"remove\",\n\t\t\"files\": [\"LICENSE\"]\n\t}\n]\n```\n\nRemove a file at the specified path.\n\n## See also\n\n- [zel](https://github.com/vutran/zel) by [Vu Tran](https://twitter.com/tranvu)\n- [gittar](https://github.com/lukeed/gittar) by [Luke Edwards](https://twitter.com/lukeed05)\n\n## License\n\n[MIT](LICENSE.md).\n"
  },
  {
    "path": "appveyor.yml",
    "content": "# http://www.appveyor.com/docs/appveyor-yml\n\nversion: '{build}'\n\nclone_depth: 10\n\ninit:\n  - git config --global core.autocrlf false\n\nenvironment:\n  matrix:\n    # node.js\n    - nodejs_version: 8\n\ninstall:\n  - ps: Install-Product node $env:nodejs_version\n  - npm install\n\nbuild: off\n\ntest_script:\n  - node --version && npm --version\n  - npm run lint\n  - npm test\n\nmatrix:\n  fast_finish: false\n# cache:\n#   - C:\\Users\\appveyor\\AppData\\Roaming\\npm-cache -> package.json     # npm cache\n#   - node_modules -> package.json                                    # local npm modules\n"
  },
  {
    "path": "degit",
    "content": "#!/usr/bin/env node\nrequire('./dist/bin.js');\n"
  },
  {
    "path": "help.md",
    "content": "# _degit_\n\nUsage:\n\n`degit <src>[#ref] [<dest>] [options]`\n\nFetches the `src` repo, and extracts it to `dest` (or the current directory).\n\nThe `src` argument can be any of the following:\n\n## GitHub repos\n\nuser/repo\ngithub:user/repo\nhttps://github.com/user/repo\n\n## GitLab repos\n\ngitlab:user/repo\nhttps://gitlab.com/user/repo\n\n## BitBucket repos\n\nbitbucket:user/repo\nhttps://bitbucket.com/user/repo\n\n## Sourcehut repos\n\ngit.sr.ht/user/repo\ngit@git.sr.ht:user/repo\nhttps://git.sr.ht/user/repo\n\nYou can append a #ref to any of the above:\n\n## Branches (defaults to master)\n\nuser/repo#dev\n\n## Tags\n\nuser/repo#v1.2.3\n\n## Commit hashes\n\nuser/repo#abcd1234\n\nThe `dest` directory (or the current directory, if unspecified) must be empty\nunless the `--force` option is used.\n\nOptions:\n\n  `--help`,    `-h`  Show this message\n  `--cache`,   `-c`  Only use local cache\n  `--force`,   `-f`  Allow non-empty destination directory\n  `--verbose`, `-v`  Extra logging\n  `--mode=`,   `-m=` Force the mode by which degit clones the repo\n                     Valid options are `tar` or `git` (uses SSH)\n\nSee https://github.com/Rich-Harris/degit for more information\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"degit\",\n\t\"version\": \"2.8.4\",\n\t\"engines\": {\n\t\t\"node\": \">=8.0.0\"\n\t},\n\t\"description\": \"Straightforward project scaffolding\",\n\t\"main\": \"dist/index.js\",\n\t\"bin\": {\n\t\t\"degit\": \"degit\"\n\t},\n\t\"scripts\": {\n\t\t\"lint\": \"eslint --color --ignore-path .gitignore .\",\n\t\t\"dev\": \"npm run build -- --watch\",\n\t\t\"build\": \"rollup -c\",\n\t\t\"test\": \"mocha\",\n\t\t\"pretest\": \"npm run build\",\n\t\t\"prepublishOnly\": \"npm test\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/Rich-Harris/degit.git\"\n\t},\n\t\"keywords\": [\n\t\t\"scaffolding\",\n\t\t\"template\",\n\t\t\"git\"\n\t],\n\t\"author\": \"Rich Harris\",\n\t\"license\": \"MIT\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/Rich-Harris/degit/issues\"\n\t},\n\t\"homepage\": \"https://github.com/Rich-Harris/degit#readme\",\n\t\"devDependencies\": {\n\t\t\"@rollup/plugin-commonjs\": \"18.0.0\",\n\t\t\"@rollup/plugin-node-resolve\": \"11.2.1\",\n\t\t\"chalk\": \"4.1.0\",\n\t\t\"enquirer\": \"2.3.6\",\n\t\t\"eslint\": \"7.23.0\",\n\t\t\"eslint-config-prettier\": \"8.1.0\",\n\t\t\"eslint-plugin-import\": \"2.22.1\",\n\t\t\"fuzzysearch\": \"1.0.3\",\n\t\t\"home-or-tmp\": \"3.0.0\",\n\t\t\"https-proxy-agent\": \"5.0.0\",\n\t\t\"husky\": \"6.0.0\",\n\t\t\"lint-staged\": \"10.5.4\",\n\t\t\"mocha\": \"8.3.2\",\n\t\t\"mri\": \"1.1.6\",\n\t\t\"prettier\": \"2.2.1\",\n\t\t\"rimraf\": \"3.0.2\",\n\t\t\"rollup\": \"2.44.0\",\n\t\t\"rollup-plugin-commonjs\": \"10.1.0\",\n\t\t\"sander\": \"0.6.0\",\n\t\t\"source-map-support\": \"0.5.19\",\n\t\t\"tar\": \"6.1.0\",\n\t\t\"tiny-glob\": \"0.2.8\"\n\t},\n\t\"files\": [\n\t\t\"help.md\",\n\t\t\"dist\"\n\t],\n\t\"husky\": {\n\t\t\"hooks\": {\n\t\t\t\"pre-commit\": \"lint-staged\"\n\t\t}\n\t},\n\t\"lint-staged\": {\n\t\t\"*.{js}\": [\n\t\t\t\"eslint --fix\",\n\t\t\t\"git add\"\n\t\t],\n\t\t\"*.{js, json, yml, md}\": [\n\t\t\t\"prettier --write\",\n\t\t\t\"git add\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport { builtinModules } from 'module';\nimport pkg from './package.json';\n\nexport default {\n\tinput: {\n\t\tindex: 'src/index.js',\n\t\tbin: 'src/bin.js'\n\t},\n\toutput: {\n\t\tdir: 'dist',\n\t\tentryFileNames: '[name].js',\n\t\tchunkFileNames: '[name]-[hash].js',\n\t\tformat: 'cjs',\n\t\texports: 'auto',\n\t\tsourcemap: true\n\t},\n\texternal: Object.keys(pkg.dependencies || {}).concat(builtinModules),\n\tplugins: [resolve(), commonjs()]\n};\n"
  },
  {
    "path": "src/bin.js",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport chalk from 'chalk';\nimport mri from 'mri';\nimport glob from 'tiny-glob/sync.js';\nimport fuzzysearch from 'fuzzysearch';\nimport enquirer from 'enquirer';\nimport degit from './index.js';\nimport { tryRequire, base } from './utils.js';\n\nconst args = mri(process.argv.slice(2), {\n\talias: {\n\t\tf: 'force',\n\t\tc: 'cache',\n\t\tv: 'verbose',\n\t\tm: 'mode'\n\t},\n\tboolean: ['force', 'cache', 'verbose']\n});\n\nconst [src, dest = '.'] = args._;\n\nasync function main() {\n\tif (args.help) {\n\t\tconst help = fs\n\t\t\t.readFileSync(path.join(__dirname, 'help.md'), 'utf-8')\n\t\t\t.replace(/^(\\s*)#+ (.+)/gm, (m, s, _) => s + chalk.bold(_))\n\t\t\t.replace(/_([^_]+)_/g, (m, _) => chalk.underline(_))\n\t\t\t.replace(/`([^`]+)`/g, (m, _) => chalk.cyan(_));\n\n\t\tprocess.stdout.write(`\\n${help}\\n`);\n\t} else if (!src) {\n\t\t// interactive mode\n\n\t\tconst accessLookup = new Map();\n\n\t\tglob(`**/access.json`, { cwd: base }).forEach(file => {\n\t\t\tconst [host, user, repo] = file.split(path.sep);\n\n\t\t\tconst json = fs.readFileSync(`${base}/${file}`, 'utf-8');\n\t\t\tconst logs = JSON.parse(json);\n\n\t\t\tObject.entries(logs).forEach(([ref, timestamp]) => {\n\t\t\t\tconst id = `${host}:${user}/${repo}#${ref}`;\n\t\t\t\taccessLookup.set(id, new Date(timestamp).getTime());\n\t\t\t});\n\t\t});\n\n\t\tconst getChoice = file => {\n\t\t\tconst [host, user, repo] = file.split(path.sep);\n\n\t\t\treturn Object.entries(tryRequire(`${base}/${file}`)).map(\n\t\t\t\t([ref, hash]) => ({\n\t\t\t\t\tname: hash,\n\t\t\t\t\tmessage: `${host}:${user}/${repo}#${ref}`,\n\t\t\t\t\tvalue: `${host}:${user}/${repo}#${ref}`\n\t\t\t\t})\n\t\t\t);\n\t\t};\n\n\t\tconst choices = glob(`**/map.json`, { cwd: base })\n\t\t\t.map(getChoice)\n\t\t\t.reduce(\n\t\t\t\t(accumulator, currentValue) => accumulator.concat(currentValue),\n\t\t\t\t[]\n\t\t\t)\n\t\t\t.sort((a, b) => {\n\t\t\t\tconst aTime = accessLookup.get(a.value) || 0;\n\t\t\t\tconst bTime = accessLookup.get(b.value) || 0;\n\n\t\t\t\treturn bTime - aTime;\n\t\t\t});\n\n\t\tconst options = await enquirer.prompt([\n\t\t\t{\n\t\t\t\ttype: 'autocomplete',\n\t\t\t\tname: 'src',\n\t\t\t\tmessage: 'Repo to clone?',\n\t\t\t\tsuggest: (input, choices) =>\n\t\t\t\t\tchoices.filter(({ value }) => fuzzysearch(input, value)),\n\t\t\t\tchoices\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: 'input',\n\t\t\t\tname: 'dest',\n\t\t\t\tmessage: 'Destination directory?',\n\t\t\t\tinitial: '.'\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: 'toggle',\n\t\t\t\tname: 'cache',\n\t\t\t\tmessage: 'Use cached version?'\n\t\t\t}\n\t\t]);\n\n\t\tconst empty =\n\t\t\t!fs.existsSync(options.dest) || fs.readdirSync(options.dest).length === 0;\n\n\t\tif (!empty) {\n\t\t\tconst { force } = await enquirer.prompt([\n\t\t\t\t{\n\t\t\t\t\ttype: 'toggle',\n\t\t\t\t\tname: 'force',\n\t\t\t\t\tmessage: 'Overwrite existing files?'\n\t\t\t\t}\n\t\t\t]);\n\n\t\t\tif (!force) {\n\t\t\t\tconsole.error(chalk.magenta(`! Directory not empty — aborting`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\trun(options.src, options.dest, {\n\t\t\tforce: true,\n\t\t\tcache: options.cache\n\t\t});\n\t} else {\n\t\trun(src, dest, args);\n\t}\n}\n\nfunction run(src, dest, args) {\n\tconst d = degit(src, args);\n\n\td.on('info', event => {\n\t\tconsole.error(chalk.cyan(`> ${event.message.replace('options.', '--')}`));\n\t});\n\n\td.on('warn', event => {\n\t\tconsole.error(\n\t\t\tchalk.magenta(`! ${event.message.replace('options.', '--')}`)\n\t\t);\n\t});\n\n\td.clone(dest).catch(err => {\n\t\tconsole.error(chalk.red(`! ${err.message.replace('options.', '--')}`));\n\t\tprocess.exit(1);\n\t});\n}\n\nmain();\n"
  },
  {
    "path": "src/index.js",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport tar from 'tar';\nimport EventEmitter from 'events';\nimport chalk from 'chalk';\nimport { rimrafSync } from 'sander';\nimport {\n\tDegitError,\n\texec,\n\tfetch,\n\tmkdirp,\n\ttryRequire,\n\tstashFiles,\n\tunstashFiles,\n\tdegitConfigName,\n\tbase\n} from './utils.js';\n\nconst validModes = new Set(['tar', 'git']);\n\nexport default function degit(src, opts) {\n\treturn new Degit(src, opts);\n}\n\nclass Degit extends EventEmitter {\n\tconstructor(src, opts = {}) {\n\t\tsuper();\n\n\t\tthis.src = src;\n\t\tthis.cache = opts.cache;\n\t\tthis.force = opts.force;\n\t\tthis.verbose = opts.verbose;\n\t\tthis.proxy = process.env.https_proxy; // TODO allow setting via --proxy\n\n\t\tthis.repo = parse(src);\n\t\tthis.mode = opts.mode || this.repo.mode;\n\n\t\tif (!validModes.has(this.mode)) {\n\t\t\tthrow new Error(`Valid modes are ${Array.from(validModes).join(', ')}`);\n\t\t}\n\n\t\tthis._hasStashed = false;\n\n\t\tthis.directiveActions = {\n\t\t\tclone: async (dir, dest, action) => {\n\t\t\t\tif (this._hasStashed === false) {\n\t\t\t\t\tstashFiles(dir, dest);\n\t\t\t\t\tthis._hasStashed = true;\n\t\t\t\t}\n\t\t\t\tconst opts = Object.assign(\n\t\t\t\t\t{ force: true },\n\t\t\t\t\t{ cache: action.cache, verbose: action.verbose }\n\t\t\t\t);\n\t\t\t\tconst d = degit(action.src, opts);\n\n\t\t\t\td.on('info', event => {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.cyan(`> ${event.message.replace('options.', '--')}`)\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\td.on('warn', event => {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.magenta(`! ${event.message.replace('options.', '--')}`)\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tawait d.clone(dest).catch(err => {\n\t\t\t\t\tconsole.error(chalk.red(`! ${err.message}`));\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t});\n\t\t\t},\n\t\t\tremove: this.remove.bind(this)\n\t\t};\n\t}\n\n\t_getDirectives(dest) {\n\t\tconst directivesPath = path.resolve(dest, degitConfigName);\n\t\tconst directives =\n\t\t\ttryRequire(directivesPath, { clearCache: true }) || false;\n\t\tif (directives) {\n\t\t\tfs.unlinkSync(directivesPath);\n\t\t}\n\n\t\treturn directives;\n\t}\n\n\tasync clone(dest) {\n\t\tthis._checkDirIsEmpty(dest);\n\n\t\tconst { repo } = this;\n\n\t\tconst dir = path.join(base, repo.site, repo.user, repo.name);\n\n\t\tif (this.mode === 'tar') {\n\t\t\tawait this._cloneWithTar(dir, dest);\n\t\t} else {\n\t\t\tawait this._cloneWithGit(dir, dest);\n\t\t}\n\n\t\tthis._info({\n\t\t\tcode: 'SUCCESS',\n\t\t\tmessage: `cloned ${chalk.bold(repo.user + '/' + repo.name)}#${chalk.bold(\n\t\t\t\trepo.ref\n\t\t\t)}${dest !== '.' ? ` to ${dest}` : ''}`,\n\t\t\trepo,\n\t\t\tdest\n\t\t});\n\n\t\tconst directives = this._getDirectives(dest);\n\t\tif (directives) {\n\t\t\tfor (const d of directives) {\n\t\t\t\t// TODO, can this be a loop with an index to pass for better error messages?\n\t\t\t\tawait this.directiveActions[d.action](dir, dest, d);\n\t\t\t}\n\t\t\tif (this._hasStashed === true) {\n\t\t\t\tunstashFiles(dir, dest);\n\t\t\t}\n\t\t}\n\t}\n\n\tremove(dir, dest, action) {\n\t\tlet files = action.files;\n\t\tif (!Array.isArray(files)) {\n\t\t\tfiles = [files];\n\t\t}\n\t\tconst removedFiles = files\n\t\t\t.map(file => {\n\t\t\t\tconst filePath = path.resolve(dest, file);\n\t\t\t\tif (fs.existsSync(filePath)) {\n\t\t\t\t\tconst isDir = fs.lstatSync(filePath).isDirectory();\n\t\t\t\t\tif (isDir) {\n\t\t\t\t\t\trimrafSync(filePath);\n\t\t\t\t\t\treturn file + '/';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfs.unlinkSync(filePath);\n\t\t\t\t\t\treturn file;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis._warn({\n\t\t\t\t\t\tcode: 'FILE_DOES_NOT_EXIST',\n\t\t\t\t\t\tmessage: `action wants to remove ${chalk.bold(\n\t\t\t\t\t\t\tfile\n\t\t\t\t\t\t)} but it does not exist`\n\t\t\t\t\t});\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.filter(d => d);\n\n\t\tif (removedFiles.length > 0) {\n\t\t\tthis._info({\n\t\t\t\tcode: 'REMOVED',\n\t\t\t\tmessage: `removed: ${chalk.bold(\n\t\t\t\t\tremovedFiles.map(d => chalk.bold(d)).join(', ')\n\t\t\t\t)}`\n\t\t\t});\n\t\t}\n\t}\n\n\t_checkDirIsEmpty(dir) {\n\t\ttry {\n\t\t\tconst files = fs.readdirSync(dir);\n\t\t\tif (files.length > 0) {\n\t\t\t\tif (this.force) {\n\t\t\t\t\tthis._info({\n\t\t\t\t\t\tcode: 'DEST_NOT_EMPTY',\n\t\t\t\t\t\tmessage: `destination directory is not empty. Using options.force, continuing`\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthrow new DegitError(\n\t\t\t\t\t\t`destination directory is not empty, aborting. Use options.force to override`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcode: 'DEST_NOT_EMPTY'\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis._verbose({\n\t\t\t\t\tcode: 'DEST_IS_EMPTY',\n\t\t\t\t\tmessage: `destination directory is empty`\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (err.code !== 'ENOENT') throw err;\n\t\t}\n\t}\n\n\t_info(info) {\n\t\tthis.emit('info', info);\n\t}\n\n\t_warn(info) {\n\t\tthis.emit('warn', info);\n\t}\n\n\t_verbose(info) {\n\t\tif (this.verbose) this._info(info);\n\t}\n\n\tasync _getHash(repo, cached) {\n\t\ttry {\n\t\t\tconst refs = await fetchRefs(repo);\n\t\t\tif (repo.ref === 'HEAD') {\n\t\t\t\treturn refs.find(ref => ref.type === 'HEAD').hash;\n\t\t\t}\n\t\t\treturn this._selectRef(refs, repo.ref);\n\t\t} catch (err) {\n\t\t\tthis._warn(err);\n\t\t\tthis._verbose(err.original);\n\n\t\t\treturn this._getHashFromCache(repo, cached);\n\t\t}\n\t}\n\n\t_getHashFromCache(repo, cached) {\n\t\tif (repo.ref in cached) {\n\t\t\tconst hash = cached[repo.ref];\n\t\t\tthis._info({\n\t\t\t\tcode: 'USING_CACHE',\n\t\t\t\tmessage: `using cached commit hash ${hash}`\n\t\t\t});\n\t\t\treturn hash;\n\t\t}\n\t}\n\n\t_selectRef(refs, selector) {\n\t\tfor (const ref of refs) {\n\t\t\tif (ref.name === selector) {\n\t\t\t\tthis._verbose({\n\t\t\t\t\tcode: 'FOUND_MATCH',\n\t\t\t\t\tmessage: `found matching commit hash: ${ref.hash}`\n\t\t\t\t});\n\t\t\t\treturn ref.hash;\n\t\t\t}\n\t\t}\n\n\t\tif (selector.length < 8) return null;\n\n\t\tfor (const ref of refs) {\n\t\t\tif (ref.hash.startsWith(selector)) return ref.hash;\n\t\t}\n\t}\n\n\tasync _cloneWithTar(dir, dest) {\n\t\tconst { repo } = this;\n\n\t\tconst cached = tryRequire(path.join(dir, 'map.json')) || {};\n\n\t\tconst hash = this.cache\n\t\t\t? this._getHashFromCache(repo, cached)\n\t\t\t: await this._getHash(repo, cached);\n\n\t\tconst subdir = repo.subdir ? `${repo.name}-${hash}${repo.subdir}` : null;\n\n\t\tif (!hash) {\n\t\t\t// TODO 'did you mean...?'\n\t\t\tthrow new DegitError(`could not find commit hash for ${repo.ref}`, {\n\t\t\t\tcode: 'MISSING_REF',\n\t\t\t\tref: repo.ref\n\t\t\t});\n\t\t}\n\n\t\tconst file = `${dir}/${hash}.tar.gz`;\n\t\tconst url =\n\t\t\trepo.site === 'gitlab'\n\t\t\t\t? `${repo.url}/repository/archive.tar.gz?ref=${hash}`\n\t\t\t\t: repo.site === 'bitbucket'\n\t\t\t\t? `${repo.url}/get/${hash}.tar.gz`\n\t\t\t\t: `${repo.url}/archive/${hash}.tar.gz`;\n\n\t\ttry {\n\t\t\tif (!this.cache) {\n\t\t\t\ttry {\n\t\t\t\t\tfs.statSync(file);\n\t\t\t\t\tthis._verbose({\n\t\t\t\t\t\tcode: 'FILE_EXISTS',\n\t\t\t\t\t\tmessage: `${file} already exists locally`\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\tmkdirp(path.dirname(file));\n\n\t\t\t\t\tif (this.proxy) {\n\t\t\t\t\t\tthis._verbose({\n\t\t\t\t\t\t\tcode: 'PROXY',\n\t\t\t\t\t\t\tmessage: `using proxy ${this.proxy}`\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._verbose({\n\t\t\t\t\t\tcode: 'DOWNLOADING',\n\t\t\t\t\t\tmessage: `downloading ${url} to ${file}`\n\t\t\t\t\t});\n\n\t\t\t\t\tawait fetch(url, file, this.proxy);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthrow new DegitError(`could not download ${url}`, {\n\t\t\t\tcode: 'COULD_NOT_DOWNLOAD',\n\t\t\t\turl,\n\t\t\t\toriginal: err\n\t\t\t});\n\t\t}\n\n\t\tupdateCache(dir, repo, hash, cached);\n\n\t\tthis._verbose({\n\t\t\tcode: 'EXTRACTING',\n\t\t\tmessage: `extracting ${\n\t\t\t\tsubdir ? repo.subdir + ' from ' : ''\n\t\t\t}${file} to ${dest}`\n\t\t});\n\n\t\tmkdirp(dest);\n\t\tawait untar(file, dest, subdir);\n\t}\n\n\tasync _cloneWithGit(dir, dest) {\n\t\tawait exec(`git clone ${this.repo.ssh} ${dest}`);\n\t\tawait exec(`rm -rf ${path.resolve(dest, '.git')}`);\n\t}\n}\n\nconst supported = new Set(['github', 'gitlab', 'bitbucket', 'git.sr.ht']);\n\nfunction parse(src) {\n\tconst match = /^(?:(?:https:\\/\\/)?([^:/]+\\.[^:/]+)\\/|git@([^:/]+)[:/]|([^/]+):)?([^/\\s]+)\\/([^/\\s#]+)(?:((?:\\/[^/\\s#]+)+))?(?:\\/)?(?:#(.+))?/.exec(\n\t\tsrc\n\t);\n\tif (!match) {\n\t\tthrow new DegitError(`could not parse ${src}`, {\n\t\t\tcode: 'BAD_SRC'\n\t\t});\n\t}\n\n\tconst site = (match[1] || match[2] || match[3] || 'github').replace(\n\t\t/\\.(com|org)$/,\n\t\t''\n\t);\n\tif (!supported.has(site)) {\n\t\tthrow new DegitError(\n\t\t\t`degit supports GitHub, GitLab, Sourcehut and BitBucket`,\n\t\t\t{\n\t\t\t\tcode: 'UNSUPPORTED_HOST'\n\t\t\t}\n\t\t);\n\t}\n\n\tconst user = match[4];\n\tconst name = match[5].replace(/\\.git$/, '');\n\tconst subdir = match[6];\n\tconst ref = match[7] || 'HEAD';\n\n\tconst domain = `${site}.${\n\t\tsite === 'bitbucket' ? 'org' : site === 'git.sr.ht' ? '' : 'com'\n\t}`;\n\tconst url = `https://${domain}/${user}/${name}`;\n\tconst ssh = `git@${domain}:${user}/${name}`;\n\n\tconst mode = supported.has(site) ? 'tar' : 'git';\n\n\treturn { site, user, name, ref, url, ssh, subdir, mode };\n}\n\nasync function untar(file, dest, subdir = null) {\n\treturn tar.extract(\n\t\t{\n\t\t\tfile,\n\t\t\tstrip: subdir ? subdir.split('/').length : 1,\n\t\t\tC: dest\n\t\t},\n\t\tsubdir ? [subdir] : []\n\t);\n}\n\nasync function fetchRefs(repo) {\n\ttry {\n\t\tconst { stdout } = await exec(`git ls-remote ${repo.url}`);\n\n\t\treturn stdout\n\t\t\t.split('\\n')\n\t\t\t.filter(Boolean)\n\t\t\t.map(row => {\n\t\t\t\tconst [hash, ref] = row.split('\\t');\n\n\t\t\t\tif (ref === 'HEAD') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'HEAD',\n\t\t\t\t\t\thash\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst match = /refs\\/(\\w+)\\/(.+)/.exec(ref);\n\t\t\t\tif (!match)\n\t\t\t\t\tthrow new DegitError(`could not parse ${ref}`, {\n\t\t\t\t\t\tcode: 'BAD_REF'\n\t\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\ttype:\n\t\t\t\t\t\tmatch[1] === 'heads'\n\t\t\t\t\t\t\t? 'branch'\n\t\t\t\t\t\t\t: match[1] === 'refs'\n\t\t\t\t\t\t\t? 'ref'\n\t\t\t\t\t\t\t: match[1],\n\t\t\t\t\tname: match[2],\n\t\t\t\t\thash\n\t\t\t\t};\n\t\t\t});\n\t} catch (error) {\n\t\tthrow new DegitError(`could not fetch remote ${repo.url}`, {\n\t\t\tcode: 'COULD_NOT_FETCH',\n\t\t\turl: repo.url,\n\t\t\toriginal: error\n\t\t});\n\t}\n}\n\nfunction updateCache(dir, repo, hash, cached) {\n\t// update access logs\n\tconst logs = tryRequire(path.join(dir, 'access.json')) || {};\n\tlogs[repo.ref] = new Date().toISOString();\n\tfs.writeFileSync(\n\t\tpath.join(dir, 'access.json'),\n\t\tJSON.stringify(logs, null, '  ')\n\t);\n\n\tif (cached[repo.ref] === hash) return;\n\n\tconst oldHash = cached[repo.ref];\n\tif (oldHash) {\n\t\tlet used = false;\n\t\tfor (const key in cached) {\n\t\t\tif (cached[key] === hash) {\n\t\t\t\tused = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!used) {\n\t\t\t// we no longer need this tar file\n\t\t\ttry {\n\t\t\t\tfs.unlinkSync(path.join(dir, `${oldHash}.tar.gz`));\n\t\t\t} catch (err) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\tcached[repo.ref] = hash;\n\tfs.writeFileSync(\n\t\tpath.join(dir, 'map.json'),\n\t\tJSON.stringify(cached, null, '  ')\n\t);\n}\n"
  },
  {
    "path": "src/utils.js",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport homeOrTmp from 'home-or-tmp';\nimport https from 'https';\nimport child_process from 'child_process';\nimport URL from 'url';\nimport Agent from 'https-proxy-agent';\nimport { rimrafSync, copydirSync } from 'sander';\n\nconst tmpDirName = 'tmp';\nconst degitConfigName = 'degit.json';\n\nexport { degitConfigName };\n\nexport class DegitError extends Error {\n\tconstructor(message, opts) {\n\t\tsuper(message);\n\t\tObject.assign(this, opts);\n\t}\n}\n\nexport function tryRequire(file, opts) {\n\ttry {\n\t\tif (opts && opts.clearCache === true) {\n\t\t\tdelete require.cache[require.resolve(file)];\n\t\t}\n\t\treturn require(file);\n\t} catch (err) {\n\t\treturn null;\n\t}\n}\n\nexport function exec(command) {\n\treturn new Promise((fulfil, reject) => {\n\t\tchild_process.exec(command, (err, stdout, stderr) => {\n\t\t\tif (err) {\n\t\t\t\treject(err);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfulfil({ stdout, stderr });\n\t\t});\n\t});\n}\n\nexport function mkdirp(dir) {\n\tconst parent = path.dirname(dir);\n\tif (parent === dir) return;\n\n\tmkdirp(parent);\n\n\ttry {\n\t\tfs.mkdirSync(dir);\n\t} catch (err) {\n\t\tif (err.code !== 'EEXIST') throw err;\n\t}\n}\n\nexport function fetch(url, dest, proxy) {\n\treturn new Promise((fulfil, reject) => {\n\t\tlet options = url;\n\n\t\tif (proxy) {\n\t\t\tconst parsedUrl = URL.parse(url);\n\t\t\toptions = {\n\t\t\t\thostname: parsedUrl.host,\n\t\t\t\tpath: parsedUrl.path,\n\t\t\t\tagent: new Agent(proxy)\n\t\t\t};\n\t\t}\n\n\t\thttps\n\t\t\t.get(options, response => {\n\t\t\t\tconst code = response.statusCode;\n\t\t\t\tif (code >= 400) {\n\t\t\t\t\treject({ code, message: response.statusMessage });\n\t\t\t\t} else if (code >= 300) {\n\t\t\t\t\tfetch(response.headers.location, dest, proxy).then(fulfil, reject);\n\t\t\t\t} else {\n\t\t\t\t\tresponse\n\t\t\t\t\t\t.pipe(fs.createWriteStream(dest))\n\t\t\t\t\t\t.on('finish', () => fulfil())\n\t\t\t\t\t\t.on('error', reject);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.on('error', reject);\n\t});\n}\n\nexport function stashFiles(dir, dest) {\n\tconst tmpDir = path.join(dir, tmpDirName);\n\trimrafSync(tmpDir);\n\tmkdirp(tmpDir);\n\tfs.readdirSync(dest).forEach(file => {\n\t\tconst filePath = path.join(dest, file);\n\t\tconst targetPath = path.join(tmpDir, file);\n\t\tconst isDir = fs.lstatSync(filePath).isDirectory();\n\t\tif (isDir) {\n\t\t\tcopydirSync(filePath).to(targetPath);\n\t\t\trimrafSync(filePath);\n\t\t} else {\n\t\t\tfs.copyFileSync(filePath, targetPath);\n\t\t\tfs.unlinkSync(filePath);\n\t\t}\n\t});\n}\n\nexport function unstashFiles(dir, dest) {\n\tconst tmpDir = path.join(dir, tmpDirName);\n\tfs.readdirSync(tmpDir).forEach(filename => {\n\t\tconst tmpFile = path.join(tmpDir, filename);\n\t\tconst targetPath = path.join(dest, filename);\n\t\tconst isDir = fs.lstatSync(tmpFile).isDirectory();\n\t\tif (isDir) {\n\t\t\tcopydirSync(tmpFile).to(targetPath);\n\t\t\trimrafSync(tmpFile);\n\t\t} else {\n\t\t\tif (filename !== 'degit.json') {\n\t\t\t\tfs.copyFileSync(tmpFile, targetPath);\n\t\t\t}\n\t\t\tfs.unlinkSync(tmpFile);\n\t\t}\n\t});\n\trimrafSync(tmpDir);\n}\n\nexport const base = path.join(homeOrTmp, '.degit');\n"
  },
  {
    "path": "test/test.js",
    "content": "require('source-map-support').install();\n\nconst fs = require('fs');\nconst path = require('path');\nconst glob = require('tiny-glob/sync');\nconst rimraf = require('rimraf').sync;\nconst assert = require('assert');\nconst child_process = require('child_process');\n\nconst degit = require('../dist/index.js');\nconst degitPath = path.resolve('dist/bin.js');\n\nconst timeout = 30000;\n\nfunction exec(cmd) {\n\treturn new Promise((fulfil, reject) => {\n\t\tchild_process.exec(cmd, (err, stdout, stderr) => {\n\t\t\tif (err) return reject(err);\n\t\t\tconsole.log(stdout);\n\t\t\tconsole.error(stderr);\n\t\t\tfulfil();\n\t\t});\n\t});\n}\n\ndescribe('degit', function() {\n\tthis.timeout(timeout);\n\n\tfunction compare(dir, files) {\n\t\tconst expected = glob('**', { cwd: dir });\n\t\tassert.deepEqual(Object.keys(files).sort(), expected.sort());\n\n\t\texpected.forEach(file => {\n\t\t\tif (!fs.lstatSync(`${dir}/${file}`).isDirectory()) {\n\t\t\t\tassert.equal(files[file].trim(), read(`${dir}/${file}`).trim());\n\t\t\t}\n\t\t});\n\t}\n\n\tbeforeEach(async () => await rimraf('.tmp'));\n\tafterEach(async () => await rimraf('.tmp'));\n\n\tdescribe('github', () => {\n\t\t[\n\t\t\t'mhkeller/degit-test-repo-compose',\n\t\t\t'Rich-Harris/degit-test-repo',\n\t\t\t'github:Rich-Harris/degit-test-repo',\n\t\t\t'git@github.com:Rich-Harris/degit-test-repo',\n\t\t\t'https://github.com/Rich-Harris/degit-test-repo.git'\n\t\t].forEach(src => {\n\t\t\tit(src, async () => {\n\t\t\t\tawait exec(`node ${degitPath} ${src} .tmp/test-repo -v`);\n\t\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t\t'file.txt': 'hello from github!',\n\t\t\t\t\tsubdir: null,\n\t\t\t\t\t'subdir/file.txt': 'hello from a subdirectory!'\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('gitlab', () => {\n\t\t[\n\t\t\t'gitlab:Rich-Harris/degit-test-repo',\n\t\t\t'git@gitlab.com:Rich-Harris/degit-test-repo',\n\t\t\t'https://gitlab.com/Rich-Harris/degit-test-repo.git'\n\t\t].forEach(src => {\n\t\t\tit(src, async () => {\n\t\t\t\tawait exec(`node ${degitPath} ${src} .tmp/test-repo -v`);\n\t\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t\t'file.txt': 'hello from gitlab!'\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('bitbucket', () => {\n\t\t[\n\t\t\t'bitbucket:Rich_Harris/degit-test-repo',\n\t\t\t'git@bitbucket.org:Rich_Harris/degit-test-repo',\n\t\t\t'https://bitbucket.org/Rich_Harris/degit-test-repo.git'\n\t\t].forEach(src => {\n\t\t\tit(src, async () => {\n\t\t\t\tawait exec(`node ${degitPath} ${src} .tmp/test-repo -v`);\n\t\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t\t'file.txt': 'hello from bitbucket'\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('Sourcehut', () => {\n\t\t[\n\t\t\t'git.sr.ht/~satotake/degit-test-repo',\n\t\t\t'https://git.sr.ht/~satotake/degit-test-repo',\n\t\t\t'git@git.sr.ht:~satotake/degit-test-repo'\n\t\t].forEach(src => {\n\t\t\tit(src, async () => {\n\t\t\t\tawait exec(`node ${degitPath} ${src} .tmp/test-repo -v`);\n\t\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t\t'file.txt': 'hello from sourcehut!'\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('Subdirectories', () => {\n\t\t[\n\t\t\t'Rich-Harris/degit-test-repo/subdir',\n\t\t\t'github:Rich-Harris/degit-test-repo/subdir',\n\t\t\t'git@github.com:Rich-Harris/degit-test-repo/subdir',\n\t\t\t'https://github.com/Rich-Harris/degit-test-repo.git/subdir'\n\t\t].forEach(src => {\n\t\t\tit(src, async () => {\n\t\t\t\tawait exec(`node ${degitPath} ${src} .tmp/test-repo -v`);\n\t\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t\t'file.txt': 'hello from a subdirectory!'\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('non-empty directories', () => {\n\t\tit('fails without --force', async () => {\n\t\t\tlet succeeded;\n\n\t\t\ttry {\n\t\t\t\tawait exec(`mkdir -p .tmp/test-repo`);\n\t\t\t\tawait exec(`echo \"not empty\" > .tmp/test-repo/file.txt`);\n\t\t\t\tawait exec(\n\t\t\t\t\t`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -v`\n\t\t\t\t);\n\t\t\t\tsucceeded = true;\n\t\t\t} catch (err) {\n\t\t\t\tassert.ok(/destination directory is not empty/.test(err.message));\n\t\t\t}\n\n\t\t\tassert.ok(!succeeded);\n\t\t});\n\n\t\tit('succeeds with --force', async () => {\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -fv`\n\t\t\t);\n\t\t});\n\t});\n\n\tdescribe('command line arguments', () => {\n\t\tit('allows flags wherever', async () => {\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} -v Rich-Harris/degit-test-repo .tmp/test-repo`\n\t\t\t);\n\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t'file.txt': 'hello from github!',\n\t\t\t\tsubdir: null,\n\t\t\t\t'subdir/file.txt': 'hello from a subdirectory!'\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('api', () => {\n\t\tit('is usable from node scripts', async () => {\n\t\t\tawait degit('Rich-Harris/degit-test-repo', { force: true }).clone(\n\t\t\t\t'.tmp/test-repo'\n\t\t\t);\n\n\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t'file.txt': 'hello from github!',\n\t\t\t\tsubdir: null,\n\t\t\t\t'subdir/file.txt': 'hello from a subdirectory!'\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('actions', () => {\n\t\tit('removes specified file', async () => {\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} -v mhkeller/degit-test-repo-remove-only .tmp/test-repo`\n\t\t\t);\n\t\t\tcompare(`.tmp/test-repo`, {});\n\t\t});\n\n\t\tit('clones repo and removes specified file', async () => {\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} -v mhkeller/degit-test-repo-remove .tmp/test-repo`\n\t\t\t);\n\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\t'other.txt': 'hello from github!',\n\t\t\t\tsubdir: null,\n\t\t\t\t'subdir/file.txt': 'hello from a subdirectory!'\n\t\t\t});\n\t\t});\n\n\t\tit('removes and adds nested files', async () => {\n\t\t\tawait rimraf('.tmp');\n\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} -v mhkeller/degit-test-repo-nested-actions .tmp/test-repo`\n\t\t\t);\n\t\t\tcompare(`.tmp/test-repo`, {\n\t\t\t\tdir: null,\n\t\t\t\tfolder: null,\n\t\t\t\tsubdir: null,\n\t\t\t\t'folder/file.txt': 'hello from clobber file!',\n\t\t\t\t'folder/other.txt': 'hello from other file!',\n\t\t\t\t'subdir/file.txt': 'hello from a subdirectory!'\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('git mode', () => {\n\t\tit('is able to clone correctly using git mode', async () => {\n\t\t\tawait rimraf('.tmp');\n\n\t\t\tawait exec(\n\t\t\t\t`node ${degitPath} --mode=git https://github.com/Rich-Harris/degit-test-repo-private.git .tmp/test-repo`\n\t\t\t);\n\t\t\tcompare('.tmp/test-repo', {\n\t\t\t\t'file.txt': 'hello from a private repo!'\n\t\t\t});\n\t\t});\n\t});\n});\n\nfunction read(file) {\n\treturn fs.readFileSync(file, 'utf-8');\n}\n"
  }
]