feat(cli): use default template when GH is offline (#12194)
fix #12136 I add a prompt if there is an error when trying to download example files. Maybe could be better to add an error class and in create-app.ts on every "console.error" trow a new Exception and manage it in catch. What you think ? 👯
This commit is contained in:
parent
2828b01950
commit
b3e45fab5e
6 changed files with 119 additions and 35 deletions
|
@ -1,16 +1,16 @@
|
|||
import retry from 'async-retry'
|
||||
import chalk from 'chalk'
|
||||
import cpy from 'cpy'
|
||||
import fs from 'fs'
|
||||
import makeDir from 'make-dir'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
|
||||
import {
|
||||
hasExample,
|
||||
hasRepo,
|
||||
getRepoInfo,
|
||||
downloadAndExtractExample,
|
||||
downloadAndExtractRepo,
|
||||
getRepoInfo,
|
||||
hasExample,
|
||||
hasRepo,
|
||||
RepoInfo,
|
||||
} from './helpers/examples'
|
||||
import { tryGitInit } from './helpers/git'
|
||||
|
@ -19,6 +19,8 @@ import { isFolderEmpty } from './helpers/is-folder-empty'
|
|||
import { getOnline } from './helpers/is-online'
|
||||
import { shouldUseYarn } from './helpers/should-use-yarn'
|
||||
|
||||
export class DownloadError extends Error {}
|
||||
|
||||
export async function createApp({
|
||||
appPath,
|
||||
useNpm,
|
||||
|
@ -75,7 +77,7 @@ export async function createApp({
|
|||
)
|
||||
process.exit(1)
|
||||
}
|
||||
} else {
|
||||
} else if (example !== '__internal-testing-retry') {
|
||||
const found = await hasExample(example)
|
||||
|
||||
if (!found) {
|
||||
|
@ -109,24 +111,32 @@ export async function createApp({
|
|||
process.chdir(root)
|
||||
|
||||
if (example) {
|
||||
if (repoInfo) {
|
||||
console.log(
|
||||
`Downloading files from repo ${chalk.cyan(
|
||||
example
|
||||
)}. This might take a moment.`
|
||||
)
|
||||
console.log()
|
||||
await downloadAndExtractRepo(root, repoInfo)
|
||||
} else {
|
||||
console.log(
|
||||
`Downloading files for example ${chalk.cyan(
|
||||
example
|
||||
)}. This might take a moment.`
|
||||
)
|
||||
console.log()
|
||||
await downloadAndExtractExample(root, example)
|
||||
try {
|
||||
if (repoInfo) {
|
||||
const repoInfo2 = repoInfo
|
||||
console.log(
|
||||
`Downloading files from repo ${chalk.cyan(
|
||||
example
|
||||
)}. This might take a moment.`
|
||||
)
|
||||
console.log()
|
||||
await retry(() => downloadAndExtractRepo(root, repoInfo2), {
|
||||
retries: 3,
|
||||
})
|
||||
} else {
|
||||
console.log(
|
||||
`Downloading files for example ${chalk.cyan(
|
||||
example
|
||||
)}. This might take a moment.`
|
||||
)
|
||||
console.log()
|
||||
await retry(() => downloadAndExtractExample(root, example), {
|
||||
retries: 3,
|
||||
})
|
||||
}
|
||||
} catch (reason) {
|
||||
throw new DownloadError(reason)
|
||||
}
|
||||
|
||||
// Copy our default `.gitignore` if the application did not provide one
|
||||
const ignorePath = path.join(root, '.gitignore')
|
||||
if (!fs.existsSync(ignorePath)) {
|
||||
|
|
|
@ -86,6 +86,10 @@ export async function downloadAndExtractExample(
|
|||
root: string,
|
||||
name: string
|
||||
): Promise<void> {
|
||||
if (name === '__internal-testing-retry') {
|
||||
throw new Error('This is an internal example for testing the CLI.')
|
||||
}
|
||||
|
||||
try {
|
||||
return await pipeline(
|
||||
got.stream('https://codeload.github.com/zeit/next.js/tar.gz/canary'),
|
||||
|
|
|
@ -4,12 +4,11 @@ import Commander from 'commander'
|
|||
import path from 'path'
|
||||
import prompts from 'prompts'
|
||||
import checkForUpdate from 'update-check'
|
||||
|
||||
import { createApp } from './create-app'
|
||||
import { createApp, DownloadError } from './create-app'
|
||||
import { listExamples } from './helpers/examples'
|
||||
import { shouldUseYarn } from './helpers/should-use-yarn'
|
||||
import { validateNpmName } from './helpers/validate-pkg'
|
||||
import packageJson from './package.json'
|
||||
import { shouldUseYarn } from './helpers/should-use-yarn'
|
||||
import { listExamples } from './helpers/examples'
|
||||
|
||||
let projectPath: string = ''
|
||||
|
||||
|
@ -166,12 +165,32 @@ async function run(): Promise<void> {
|
|||
}
|
||||
|
||||
const example = typeof program.example === 'string' && program.example.trim()
|
||||
await createApp({
|
||||
appPath: resolvedProjectPath,
|
||||
useNpm: !!program.useNpm,
|
||||
example: example && example !== 'default' ? example : undefined,
|
||||
examplePath: program.examplePath,
|
||||
})
|
||||
try {
|
||||
await createApp({
|
||||
appPath: resolvedProjectPath,
|
||||
useNpm: !!program.useNpm,
|
||||
example: example && example !== 'default' ? example : undefined,
|
||||
examplePath: program.examplePath,
|
||||
})
|
||||
} catch (reason) {
|
||||
if (!(reason instanceof DownloadError)) {
|
||||
throw reason
|
||||
}
|
||||
|
||||
const res = await prompts({
|
||||
type: 'confirm',
|
||||
name: 'builtin',
|
||||
message:
|
||||
`Could not download "${example}" because of a connectivity issue between your machine and GitHub.\n` +
|
||||
`Do you want to use the default template instead?`,
|
||||
initial: true,
|
||||
})
|
||||
if (!res.builtin) {
|
||||
throw reason
|
||||
}
|
||||
|
||||
await createApp({ appPath: resolvedProjectPath, useNpm: !!program.useNpm })
|
||||
}
|
||||
}
|
||||
|
||||
const update = checkForUpdate(packageJson).catch(() => null)
|
||||
|
|
|
@ -27,12 +27,14 @@
|
|||
"prepublish": "yarn release"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/async-retry": "1.4.2",
|
||||
"@types/node": "^12.6.8",
|
||||
"@types/prompts": "2.0.1",
|
||||
"@types/rimraf": "3.0.0",
|
||||
"@types/tar": "4.0.3",
|
||||
"@types/validate-npm-package-name": "3.0.0",
|
||||
"@zeit/ncc": "^0.20.4",
|
||||
"async-retry": "1.3.1",
|
||||
"chalk": "2.4.2",
|
||||
"commander": "2.20.0",
|
||||
"cpy": "7.3.0",
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/* eslint-env jest */
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import execa from 'execa'
|
||||
import fs from 'fs-extra'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
|
||||
const cli = require.resolve('create-next-app/dist/index.js')
|
||||
|
||||
jest.setTimeout(1000 * 60 * 2)
|
||||
jest.setTimeout(1000 * 60 * 5)
|
||||
|
||||
const run = (cwd, ...args) => execa('node', [cli, ...args], { cwd })
|
||||
const runStarter = (cwd, ...args) => {
|
||||
|
@ -229,6 +229,36 @@ describe('create next app', () => {
|
|||
expect(res.stdout).toMatch(/Downloading files for example hello-world/)
|
||||
})
|
||||
})
|
||||
|
||||
it('should fall back to default template', async () => {
|
||||
await usingTempDir(async (cwd) => {
|
||||
const runExample = (...args) => {
|
||||
const res = run(cwd, ...args)
|
||||
|
||||
function fallbackToTemplate(data) {
|
||||
if (
|
||||
/Do you want to use the default template instead/.test(
|
||||
data.toString()
|
||||
)
|
||||
) {
|
||||
res.stdout.removeListener('data', fallbackToTemplate)
|
||||
res.stdin.write('\n')
|
||||
}
|
||||
}
|
||||
|
||||
res.stdout.on('data', fallbackToTemplate)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const res = await runExample(
|
||||
'fail-example',
|
||||
'--example',
|
||||
'__internal-testing-retry'
|
||||
)
|
||||
expect(res.exitCode).toBe(0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it('should allow an example named default', async () => {
|
||||
|
|
19
yarn.lock
19
yarn.lock
|
@ -2407,6 +2407,13 @@
|
|||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
|
||||
|
||||
"@types/async-retry@1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.4.2.tgz#7f910188cd3893b51e32df51765ee8d5646053e3"
|
||||
integrity sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==
|
||||
dependencies:
|
||||
"@types/retry" "*"
|
||||
|
||||
"@types/babel__code-frame@7.0.1":
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.1.tgz#baf2529c4abbfb5e4008c845efcfe39a187e2f99"
|
||||
|
@ -2778,6 +2785,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/retry@*":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||
|
||||
"@types/rimraf@3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f"
|
||||
|
@ -3585,6 +3597,13 @@ async-retry@1.2.3:
|
|||
dependencies:
|
||||
retry "0.12.0"
|
||||
|
||||
async-retry@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55"
|
||||
integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==
|
||||
dependencies:
|
||||
retry "0.12.0"
|
||||
|
||||
async-sema@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.0.0.tgz#9e22d6783f0ab66a1cf330e21a905e39b3b3a975"
|
||||
|
|
Loading…
Reference in a new issue