Skip to content

Commit

Permalink
feat: implement create site from template feature
Browse files Browse the repository at this point in the history
  • Loading branch information
charliegerard committed Jan 11, 2022
1 parent 418681c commit 3022af0
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
214 changes: 214 additions & 0 deletions src/commands/sites/sites-create-template.js
@@ -0,0 +1,214 @@
// @ts-check

const slugify = require('@sindresorhus/slugify')
const inquirer = require('inquirer')
const pick = require('lodash/pick')
const sample = require('lodash/sample')
const prettyjson = require('prettyjson')
const { v4: uuidv4 } = require('uuid')

const { chalk, error, getRepoData, log, logJson, track, warn } = require('../../utils')
const { configureRepo } = require('../../utils/init/config')

const SITE_NAME_SUGGESTION_SUFFIX_LENGTH = 5

const templates = [
{
name: 'Next.js Starter',
sourceCodeUrl: 'https://github.com/netlify-templates/next-netlify-starter',
},
{
name: 'Nuxt.js Starter',
sourceCodeUrl: 'https://github.com/Gomah/bluise',
},
{
name: 'Hugo Blog',
sourceCodeUrl: 'https://github.com/netlify-templates/one-click-hugo-cms',
},
]

/**
* The sites:create-template command
* @param {import('commander').OptionValues} options
* @param {import('../base-command').BaseCommand} command
*/
const sitesCreate = async (options, command) => {
const { api } = command.netlify

await command.authenticate()

// List templates

const templateUrl = await inquirer.prompt([
{
type: 'list',
name: 'templateName',
message: 'Template:',
choices: templates.map((template) => ({
value: template.sourceCodeUrl,
name: template.name,
})),
},
])

const accounts = await api.listAccountsForUser()

let { accountSlug } = options
if (!accountSlug) {
const { accountSlug: accountSlugInput } = await inquirer.prompt([
{
type: 'list',
name: 'accountSlug',
message: 'Team:',
choices: accounts.map((account) => ({
value: account.slug,
name: account.name,
})),
},
])
accountSlug = accountSlugInput
}

const { name: nameFlag } = options
let user
let site

// Allow the user to reenter site name if selected one isn't available
const inputSiteName = async (name) => {
if (!user) user = await api.getCurrentUser()

if (!name) {
let { slug } = user
let suffix = ''

// If the user doesn't have a slug, we'll compute one. Because `full_name` is not guaranteed to be unique, we
// append a short randomly-generated ID to reduce the likelihood of a conflict.
if (!slug) {
slug = slugify(user.full_name || user.email)
suffix = `-${uuidv4().slice(0, SITE_NAME_SUGGESTION_SUFFIX_LENGTH)}`
}

const suggestions = [
`super-cool-site-by-${slug}${suffix}`,
`the-awesome-${slug}-site${suffix}`,
`${slug}-makes-great-sites${suffix}`,
`netlify-thinks-${slug}-is-great${suffix}`,
`the-great-${slug}-site${suffix}`,
`isnt-${slug}-awesome${suffix}`,
]
const siteSuggestion = sample(suggestions)

console.log(
`Choose a unique site name (e.g. ${siteSuggestion}.netlify.app) or leave it blank for a random name. You can update the site name later.`,
)
const { name: nameInput } = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Site name (optional):',
filter: (val) => (val === '' ? undefined : val),
validate: (input) => /^[a-zA-Z\d-]+$/.test(input) || 'Only alphanumeric characters and hyphens are allowed',
},
])
name = nameInput
}

const body = {}
if (typeof name === 'string') {
body.name = name.trim()
}
try {
console.log(templateUrl)
// site = await api.createSiteInTeam({
// accountSlug,
// body,
// })
} catch (error_) {
if (error_.status === 422) {
warn(`${name}.netlify.app already exists. Please try a different slug.`)
await inputSiteName()
} else {
error(`createSiteInTeam error: ${error_.status}: ${error_.message}`)
}
}
}
await inputSiteName(nameFlag)

log()
log(chalk.greenBright.bold.underline(`Site Created`))
log()

const siteUrl = site.ssl_url || site.url
log(
prettyjson.render({
'Admin URL': site.admin_url,
URL: siteUrl,
'Site ID': site.id,
}),
)

track('sites_created', {
siteId: site.id,
adminUrl: site.admin_url,
siteUrl,
})

if (options.withCi) {
log('Configuring CI')
const repoData = await getRepoData()
await configureRepo({ command, siteId: site.id, repoData, manual: options.manual })
}

if (options.json) {
logJson(
pick(site, [
'id',
'state',
'plan',
'name',
'custom_domain',
'domain_aliases',
'url',
'ssl_url',
'admin_url',
'screenshot_url',
'created_at',
'updated_at',
'user_id',
'ssl',
'force_ssl',
'managed_dns',
'deploy_url',
'account_name',
'account_slug',
'git_provider',
'deploy_hook',
'capabilities',
'id_domain',
]),
)
}

return site
}

/**
* Creates the `netlify sites:create-template` command
* @param {import('../base-command').BaseCommand} program
* @returns
*/
const createSitesFromTemplateCommand = (program) =>
program
.command('sites:create-template')
.description(
`Create a site from a starter template
Create a site from a starter template.`,
)
.option('-n, --name [name]', 'name of site')
.option('-a, --account-slug [slug]', 'account slug to create the site under')
.option('-c, --with-ci', 'initialize CI hooks during site creation')
.option('-m, --manual', 'force manual CI setup. Used --with-ci flag')
.addHelpText('after', `Create a site from starter template.`)
.action(sitesCreate)

module.exports = { createSitesFromTemplateCommand, sitesCreate }
2 changes: 2 additions & 0 deletions src/commands/sites/sites.js
@@ -1,5 +1,6 @@
// @ts-check
const { createSitesCreateCommand } = require('./sites-create')
const { createSitesFromTemplateCommand } = require('./sites-create-template')
const { createSitesDeleteCommand } = require('./sites-delete')
const { createSitesListCommand } = require('./sites-list')

Expand All @@ -19,6 +20,7 @@ const sites = (options, command) => {
*/
const createSitesCommand = (program) => {
createSitesCreateCommand(program)
createSitesFromTemplateCommand(program)
createSitesListCommand(program)
createSitesDeleteCommand(program)

Expand Down

0 comments on commit 3022af0

Please sign in to comment.