@greenwood/init
Advanced tools
Comparing version 0.20.3 to 0.21.0
{ | ||
"name": "@greenwood/init", | ||
"version": "0.20.3", | ||
"version": "0.21.0", | ||
"description": "A package for scaffolding a new Greenwood project.", | ||
@@ -27,5 +27,7 @@ "repository": "https://github.com/ProjectEvergreen/greenwood/tree/master/packages/init", | ||
"chalk": "^4.1.2", | ||
"commander": "^8.2.0" | ||
"commander": "^8.2.0", | ||
"node-fetch": "^3.1.0", | ||
"simple-git": "^2.48.0" | ||
}, | ||
"gitHead": "2043c8ded451fa5a7d21116f90b83a16b03e30c1" | ||
"gitHead": "8474388a83a7927b43870b61543b26087a0eea96" | ||
} |
@@ -27,2 +27,23 @@ # @greenwood/init | ||
### Template | ||
To scaffold your new project based on one of [Greenwood's starter templates](https://github.com/orgs/ProjectEvergreen/repositories?q=greenwood-template-&type=all&language=&sort=), pass the `--template` flag and then follow the prompts to complete the scaffolding. | ||
```bash | ||
# example | ||
npx @greenwood/init --template | ||
------------------------------------------------------- | ||
Initialize Greenwood Template ♻️ | ||
------------------------------------------------------- | ||
? Which template would you like to use? (Use arrow keys) | ||
❯ blog | ||
``` | ||
You can also pass the template you want from the CLI as well. | ||
```bash | ||
# example | ||
npx @greenwood/init --template=blog | ||
``` | ||
### NPM Install | ||
@@ -44,2 +65,8 @@ | ||
npx @greenwood/init --yarn | ||
``` | ||
``` | ||
> _Flags can be chained together!_ | ||
> ```sh | ||
> # This will use Yarn, install dependencies, and scaffold from the blog template | ||
> $ npx @greenwood/init --template --yarn --install | ||
> ``` |
129
src/index.js
@@ -16,5 +16,8 @@ #!/usr/bin/env node | ||
import chalk from 'chalk'; | ||
import simpleGit from 'simple-git'; | ||
import commander from 'commander'; | ||
import { copyFolder } from './copy-folder.js'; | ||
import fetch from 'node-fetch'; | ||
import fs from 'fs'; | ||
import inquirer from 'inquirer'; | ||
import os from 'os'; | ||
@@ -25,5 +28,9 @@ import path from 'path'; | ||
const projectGitHubAPIUrl = 'https://api.github.com/orgs/ProjectEvergreen/repos'; | ||
const templateStandardName = 'greenwood-template-'; | ||
let selectedTemplate = null; | ||
const scriptPkg = JSON.parse(fs.readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf-8')); | ||
const templateDir = fileURLToPath(new URL('./template', import.meta.url)); | ||
let templateDir = fileURLToPath(new URL('./template', import.meta.url)); | ||
const TARGET_DIR = process.cwd(); | ||
const clonedTemplateDir = path.join(TARGET_DIR, '.greenwood', '.template'); | ||
@@ -39,2 +46,3 @@ console.log(`${chalk.rgb(175, 207, 71)('-------------------------------------------------------')}`); | ||
.option('--install', 'Install dependencies upon init') | ||
.option('--template [type]', 'Select from list of Greenwood curated templates') | ||
.parse(process.argv) | ||
@@ -136,4 +144,121 @@ .opts(); | ||
const listAndSelectTemplate = async () => { | ||
const getTemplates = async () => { | ||
try { | ||
// create error response | ||
class HTTPResponseError extends Error { | ||
constructor(response, ...args) { | ||
super(`HTTP Error Response: ${response.status} ${response.statusText}`, ...args); | ||
this.response = response; | ||
} | ||
} | ||
// check response from repo list fetch | ||
const checkStatus = response => { | ||
if (response.ok) { | ||
// response.status >= 200 && response.status < 300 | ||
return response.json(); | ||
} else { | ||
console.log('Couldn\'t locate any templates, check your connection and try again'); | ||
throw new HTTPResponseError(response); | ||
} | ||
}; | ||
const repos = await fetch(projectGitHubAPIUrl).then(resp => checkStatus(resp)); | ||
// assuming it did resolve but there are no templates listed | ||
if (!repos || repos.length === 0) { | ||
console.log('Couldn\'t locate any templates, check your connection and try again'); | ||
return []; | ||
} | ||
const templateRepos = repos.filter(repo => { | ||
return repo.name.includes(templateStandardName); | ||
}); | ||
return templateRepos.map(({ clone_url, name }) => { // eslint-disable-line camelcase | ||
const templateName = name.substring(templateStandardName.length, name.length); | ||
return { clone_url, name: templateName }; // eslint-disable-line camelcase | ||
}); | ||
} catch (err) { | ||
throw err; | ||
} | ||
}; | ||
const templates = await getTemplates(); | ||
const questions = [ | ||
{ | ||
type: 'list', | ||
name: 'template', | ||
message: 'Which template would you like to use?', | ||
choices: templates.map(template => template.name), | ||
filter(val) { | ||
return val.toLowerCase(); | ||
} | ||
} | ||
]; | ||
// if the user has provided one, use that, else prompt from the list | ||
if (typeof program.template !== 'boolean') { | ||
const userSelection = program.template; | ||
const matchedTemplate = templates.find((template) => template.name === userSelection); | ||
if (matchedTemplate) { | ||
console.debug(`using user provided template => ${userSelection}...`); | ||
selectedTemplate = matchedTemplate; | ||
} else { | ||
const choices = templates.map(template => template.name).join('\n'); | ||
console.error(`unable to match user provided template "${userSelection}". please try again. choices are ${choices}`); | ||
} | ||
} else { | ||
return inquirer.prompt(questions).then((answers) => { | ||
// set the selected template based on the selected template name | ||
selectedTemplate = templates.find(template => { | ||
return template.name === answers.template; | ||
}); | ||
if (selectedTemplate) { | ||
console.log('\Installing Selected Template:', selectedTemplate.name); | ||
} | ||
}); | ||
} | ||
}; | ||
const cloneTemplate = async () => { | ||
const git = simpleGit(); | ||
// check if .template directory already exists, if so remove it | ||
if (fs.existsSync(clonedTemplateDir)) { | ||
fs.rmSync(clonedTemplateDir, { recursive: true, force: true }); | ||
} | ||
// clone to .template directory | ||
console.log('clone template', selectedTemplate.name, 'to directory', clonedTemplateDir); | ||
try { | ||
await git.clone(selectedTemplate.clone_url, clonedTemplateDir); | ||
templateDir = clonedTemplateDir; | ||
} catch (e) { throw e; } | ||
}; | ||
const cleanUp = async () => { | ||
if (fs.existsSync(clonedTemplateDir)) { | ||
fs.rmSync(clonedTemplateDir, { recursive: true, force: true }); | ||
} | ||
}; | ||
const run = async () => { | ||
try { | ||
if (program.template) { | ||
await listAndSelectTemplate(); | ||
if (!selectedTemplate) { | ||
return; | ||
} | ||
await cloneTemplate(); | ||
} | ||
// map all the template files and copy them to the current working directory | ||
@@ -151,2 +276,4 @@ console.log('Initialzing project with files...'); | ||
await cleanUp(); | ||
console.log(`${chalk.rgb(175, 207, 71)('Initializing new project complete!')}`); | ||
@@ -153,0 +280,0 @@ } catch (err) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13811
271
70
4
2
+ Addednode-fetch@^3.1.0
+ Addedsimple-git@^2.48.0
+ Added@kwsites/file-exists@1.1.1(transitive)
+ Added@kwsites/promise-deferred@1.1.1(transitive)
+ Addeddata-uri-to-buffer@4.0.1(transitive)
+ Addeddebug@4.3.7(transitive)
+ Addedfetch-blob@3.2.0(transitive)
+ Addedformdata-polyfill@4.0.10(transitive)
+ Addedms@2.1.3(transitive)
+ Addednode-domexception@1.0.0(transitive)
+ Addednode-fetch@3.3.2(transitive)
+ Addedsimple-git@2.48.0(transitive)
+ Addedweb-streams-polyfill@3.3.3(transitive)