diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index a180c9f631ec5998a3457aa2991aecb8c5011275..2db10c89c075697ca2601479406b97ba2f23b19a 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -19,6 +19,7 @@ import { install } from './helpers/install' import { isFolderEmpty } from './helpers/is-folder-empty' import { getOnline } from './helpers/is-online' import { shouldUseYarn } from './helpers/should-use-yarn' +import { isWriteable } from './helpers/is-writeable' export class DownloadError extends Error {} @@ -93,6 +94,17 @@ export async function createApp({ } const root = path.resolve(appPath) + + if (!(await isWriteable(path.dirname(root)))) { + console.error( + 'The application path is not writable, please check folder permissions and try again.' + ) + console.error( + 'It is likely you do not have write permissions for this folder.' + ) + process.exit(1) + } + const appName = path.basename(root) await makeDir(root) diff --git a/packages/create-next-app/helpers/is-writeable.ts b/packages/create-next-app/helpers/is-writeable.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b9e9abb4f0feae4559a7c37faa85ed6c803f824 --- /dev/null +++ b/packages/create-next-app/helpers/is-writeable.ts @@ -0,0 +1,10 @@ +import fs from 'fs' + +export async function isWriteable(directory: string): Promise { + try { + await fs.promises.access(directory, (fs.constants || fs).W_OK) + return true + } catch (err) { + return false + } +} diff --git a/test/integration/create-next-app/index.test.js b/test/integration/create-next-app/index.test.js index 3a26af4c266889756816205826cb5a94a680359e..63cb6de6a20a5bb60f683be4f48c97919efe5011 100644 --- a/test/integration/create-next-app/index.test.js +++ b/test/integration/create-next-app/index.test.js @@ -23,9 +23,9 @@ const runStarter = (cwd, ...args) => { return res } -async function usingTempDir(fn) { +async function usingTempDir(fn, options) { const folder = path.join(os.tmpdir(), Math.random().toString(36).substring(2)) - await fs.mkdirp(folder) + await fs.mkdirp(folder, options) try { return await fn(folder) } finally { @@ -251,4 +251,28 @@ describe('create next app', () => { } }) }) + + it('should exit if the folder is not writable', async () => { + await usingTempDir(async (cwd) => { + const projectName = 'not-writable' + expect.assertions(2) + try { + const res = await runStarter(cwd, projectName) + + if (process.platform === 'win32') { + expect(res.exitCode).toBe(0) + expect( + fs.existsSync(path.join(cwd, projectName, 'package.json')) + ).toBeTruthy() + } + } catch (e) { + // eslint-disable-next-line jest/no-try-expect + expect(e.exitCode).toBe(1) + // eslint-disable-next-line jest/no-try-expect + expect(e.stderr).toMatch( + /you do not have write permissions for this folder/ + ) + } + }, 0o500) + }) })