mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
`create-next-app` by default asks a lot of questions that new users might not be familiar with or necessarily know the answer to. This updates the flow to start by asking if you want to use the recommended defaults. If you already have preferences saved from previous runs, it becomes an option to re-use those preferences. Otherwise you can proceed with the step-by-step flow. https://github.com/user-attachments/assets/92e815df-5484-4235-841b-eb231df8377c
192 lines
4.4 KiB
TypeScript
192 lines
4.4 KiB
TypeScript
/**
|
|
* @fileoverview
|
|
*
|
|
* This file contains utilities for `create-next-app` testing.
|
|
*/
|
|
|
|
import { execSync, spawn, SpawnOptions } from 'child_process'
|
|
import { existsSync } from 'fs'
|
|
import { join, resolve } from 'path'
|
|
import glob from 'glob'
|
|
import Conf from 'next/dist/compiled/conf'
|
|
|
|
import {
|
|
getProjectSetting,
|
|
mapSrcFiles,
|
|
projectSpecification,
|
|
} from './specification'
|
|
import {
|
|
CustomTemplateOptions,
|
|
DefaultTemplateOptions,
|
|
ProjectDeps,
|
|
ProjectFiles,
|
|
} from './types'
|
|
|
|
const cli = require.resolve('create-next-app/dist/index.js')
|
|
|
|
/**
|
|
* Run the built version of `create-next-app` with the given arguments.
|
|
*/
|
|
export const createNextApp = (
|
|
args: string[],
|
|
options?: SpawnOptions,
|
|
testVersion?: string,
|
|
clearPreferences: boolean = true
|
|
) => {
|
|
const conf = new Conf({ projectName: 'create-next-app' })
|
|
if (clearPreferences) {
|
|
conf.clear()
|
|
}
|
|
|
|
console.log(`[TEST] $ ${cli} ${args.join(' ')}`, { options })
|
|
|
|
const cloneEnv = { ...process.env }
|
|
// unset CI env as this skips the auto-install behavior
|
|
// being tested
|
|
delete cloneEnv.CI
|
|
delete cloneEnv.CIRCLECI
|
|
delete cloneEnv.GITHUB_ACTIONS
|
|
delete cloneEnv.CONTINUOUS_INTEGRATION
|
|
delete cloneEnv.RUN_ID
|
|
delete cloneEnv.BUILD_NUMBER
|
|
|
|
cloneEnv.NEXT_PRIVATE_TEST_VERSION = testVersion || 'canary'
|
|
|
|
return spawn('node', [cli].concat(args), {
|
|
...options,
|
|
env: {
|
|
...cloneEnv,
|
|
...options.env,
|
|
},
|
|
})
|
|
}
|
|
|
|
export const projectShouldHaveNoGitChanges = ({
|
|
cwd,
|
|
projectName,
|
|
}: DefaultTemplateOptions) => {
|
|
const projectDirname = join(cwd, projectName)
|
|
|
|
try {
|
|
execSync('git diff --quiet', { cwd: projectDirname })
|
|
} catch {
|
|
execSync('git status', { cwd: projectDirname, stdio: 'inherit' })
|
|
execSync('git --no-pager diff', { cwd: projectDirname, stdio: 'inherit' })
|
|
|
|
throw new Error('Found unexpected git changes.')
|
|
}
|
|
}
|
|
|
|
export const projectFilesShouldExist = ({
|
|
cwd,
|
|
projectName,
|
|
files,
|
|
}: ProjectFiles) => {
|
|
const projectRoot = resolve(cwd, projectName)
|
|
for (const file of files) {
|
|
try {
|
|
expect(existsSync(resolve(projectRoot, file))).toBe(true)
|
|
} catch (err) {
|
|
require('console').error(
|
|
`missing expected file ${file}`,
|
|
glob.sync('**/*', { cwd, ignore: '**/node_modules/**' }),
|
|
files
|
|
)
|
|
throw err
|
|
}
|
|
}
|
|
}
|
|
|
|
export const projectFilesShouldNotExist = ({
|
|
cwd,
|
|
projectName,
|
|
files,
|
|
}: ProjectFiles) => {
|
|
const projectRoot = resolve(cwd, projectName)
|
|
for (const file of files) {
|
|
try {
|
|
expect(existsSync(resolve(projectRoot, file))).toBe(false)
|
|
} catch (err) {
|
|
require('console').error(
|
|
`unexpected file present ${file}`,
|
|
glob.sync('**/*', { cwd, ignore: '**/node_modules/**' }),
|
|
files
|
|
)
|
|
throw err
|
|
}
|
|
}
|
|
}
|
|
|
|
export const projectDepsShouldBe = ({
|
|
cwd,
|
|
projectName,
|
|
type,
|
|
deps,
|
|
}: ProjectDeps) => {
|
|
const projectRoot = resolve(cwd, projectName)
|
|
const pkgJson = require(resolve(projectRoot, 'package.json'))
|
|
expect(Object.keys(pkgJson[type] || {}).sort()).toEqual(deps.sort())
|
|
}
|
|
|
|
export const shouldBeTemplateProject = ({
|
|
cwd,
|
|
projectName,
|
|
template,
|
|
mode,
|
|
srcDir,
|
|
}: CustomTemplateOptions) => {
|
|
projectFilesShouldExist({
|
|
cwd,
|
|
projectName,
|
|
files: getProjectSetting({ template, mode, setting: 'files', srcDir }),
|
|
})
|
|
|
|
// Tailwind templates share the same files (tailwind.config.mjs, postcss.config.mjs)
|
|
if (
|
|
!['app-tw', 'app-tw-empty', 'default-tw', 'default-tw-empty'].includes(
|
|
template
|
|
)
|
|
) {
|
|
projectFilesShouldNotExist({
|
|
cwd,
|
|
projectName,
|
|
files: mapSrcFiles(
|
|
projectSpecification[template][mode === 'js' ? 'ts' : 'js'].files,
|
|
srcDir
|
|
),
|
|
})
|
|
}
|
|
|
|
projectDepsShouldBe({
|
|
type: 'dependencies',
|
|
cwd,
|
|
projectName,
|
|
deps: getProjectSetting({ template, mode, setting: 'deps' }),
|
|
})
|
|
|
|
projectDepsShouldBe({
|
|
type: 'devDependencies',
|
|
cwd,
|
|
projectName,
|
|
deps: getProjectSetting({ template, mode, setting: 'devDeps' }),
|
|
})
|
|
}
|
|
|
|
export const shouldBeJavascriptProject = ({
|
|
cwd,
|
|
projectName,
|
|
template,
|
|
srcDir,
|
|
}: Omit<CustomTemplateOptions, 'mode'>) => {
|
|
shouldBeTemplateProject({ cwd, projectName, template, mode: 'js', srcDir })
|
|
}
|
|
|
|
export const shouldBeTypescriptProject = ({
|
|
cwd,
|
|
projectName,
|
|
template,
|
|
srcDir,
|
|
}: Omit<CustomTemplateOptions, 'mode'>) => {
|
|
shouldBeTemplateProject({ cwd, projectName, template, mode: 'ts', srcDir })
|
|
}
|