strapi/packages/generators/app/src/create-project.ts

236 lines
7.0 KiB
TypeScript
Raw Normal View History

2023-04-26 09:07:40 +02:00
/* eslint-disable @typescript-eslint/no-var-requires */
import { join } from 'path';
import fse from 'fs-extra';
import chalk from 'chalk';
import execa from 'execa';
import ora from 'ora';
import _ from 'lodash';
import stopProcess from './utils/stop-process';
import { trackUsage, captureStderr } from './utils/usage';
import mergeTemplate from './utils/merge-template.js';
import tryGitInit from './utils/git';
2023-04-26 09:07:40 +02:00
import packageJSON from './resources/json/common/package.json';
import { createDatabaseConfig, generateDbEnvariables } from './resources/templates/database';
import createEnvFile from './resources/templates/env';
import { Configuration, Scope, isStderrError } from './types';
export default async function createProject(
scope: Scope,
{ client, connection, dependencies }: Configuration
) {
console.log(`Creating a new Strapi application at ${chalk.green(scope.rootPath)}.`);
2019-06-20 18:04:09 +02:00
console.log('Creating files.');
2019-06-20 16:38:15 +02:00
2022-03-09 16:27:49 +01:00
const { rootPath, useTypescript } = scope;
2019-06-20 16:38:15 +02:00
const resources = join(__dirname, 'resources');
2022-03-09 16:27:49 +01:00
const language = useTypescript ? 'ts' : 'js';
2019-06-20 16:38:15 +02:00
try {
// copy files
2022-03-09 16:27:49 +01:00
await fse.copy(join(resources, 'files', language), rootPath);
2019-06-20 16:38:15 +02:00
// copy dot files
await fse.writeFile(join(rootPath, '.env'), createEnvFile());
2022-03-09 16:27:49 +01:00
2023-04-26 09:07:40 +02:00
const copyDotFilesFromSubDirectory = (subDirectory: string) => {
2022-03-09 16:27:49 +01:00
const files = fse.readdirSync(join(resources, 'dot-files', subDirectory));
return Promise.all(
2022-08-08 23:33:39 +02:00
files.map((file) => {
2022-03-09 16:27:49 +01:00
const src = join(resources, 'dot-files', subDirectory, file);
const dest = join(rootPath, `.${file}`);
return fse.copy(src, dest);
})
);
};
// Copy common dot files
copyDotFilesFromSubDirectory('common');
// Copy JS dot files
// For now we only support javascript and typescript, so if we're not using
// typescript, then we can assume we're using javascript. We'll need to change
// this behavior when we'll abstract the supported languages even more.
if (!useTypescript) {
copyDotFilesFromSubDirectory('js');
}
await trackUsage({ event: 'didCopyProjectFiles', scope });
2019-06-20 16:38:15 +02:00
// copy templates
await fse.writeJSON(
join(rootPath, 'package.json'),
packageJSON({
strapiDependencies: scope.strapiDependencies,
additionalsDependencies: dependencies,
strapiVersion: scope.strapiVersion,
projectName: _.kebabCase(scope.name),
2019-06-20 16:38:15 +02:00
uuid: scope.uuid,
packageJsonStrapi: scope.packageJsonStrapi,
2019-06-20 16:38:15 +02:00
}),
{
spaces: 2,
}
);
await trackUsage({ event: 'didWritePackageJSON', scope });
2022-03-09 16:27:49 +01:00
if (useTypescript) {
2023-04-26 09:07:40 +02:00
const tsJSONDir = join(__dirname, 'resources/json/ts');
2022-04-05 16:32:18 +02:00
const filesMap = {
'tsconfig-admin.json.js': 'src/admin',
'tsconfig-server.json.js': '.',
};
2022-03-09 16:27:49 +01:00
2022-04-05 16:32:18 +02:00
for (const [fileName, path] of Object.entries(filesMap)) {
const srcPath = join(tsJSONDir, fileName);
const destPath = join(rootPath, path, 'tsconfig.json');
2022-03-09 16:27:49 +01:00
2023-04-26 09:07:40 +02:00
const json = require(srcPath).default();
2022-03-09 16:27:49 +01:00
2022-04-12 15:58:21 +02:00
await fse.writeJSON(destPath, json, { spaces: 2 });
2022-04-05 16:32:18 +02:00
}
2022-03-09 16:27:49 +01:00
}
2019-06-20 16:38:15 +02:00
// ensure node_modules is created
await fse.ensureDir(join(rootPath, 'node_modules'));
2022-03-09 16:27:49 +01:00
// create config/database
2022-08-26 15:54:01 +02:00
await fse.appendFile(join(rootPath, '.env'), generateDbEnvariables({ client, connection }));
await fse.writeFile(
2022-03-09 16:27:49 +01:00
join(rootPath, `config/database.${language}`),
2023-04-26 09:07:40 +02:00
createDatabaseConfig({ useTypescript })
2019-06-20 16:38:15 +02:00
);
await trackUsage({ event: 'didCopyConfigurationFiles', scope });
// merge template files if a template is specified
const hasTemplate = Boolean(scope.template);
if (hasTemplate) {
try {
await mergeTemplate(scope, rootPath);
} catch (error) {
2023-04-26 09:07:40 +02:00
if (error instanceof Error) {
throw new Error(`⛔️ Template installation failed: ${error.message}`);
}
throw error;
}
}
2019-06-20 16:38:15 +02:00
} catch (err) {
await fse.remove(scope.rootPath);
throw err;
}
await trackUsage({ event: 'willInstallProjectDependencies', scope });
2019-06-20 18:04:09 +02:00
const installPrefix = chalk.yellow('Installing dependencies:');
const loader = ora(installPrefix).start();
const logInstall = (chunk = '') => {
2022-08-08 23:33:39 +02:00
loader.text = `${installPrefix} ${chunk.toString().split('\n').join(' ')}`;
2019-06-20 18:04:09 +02:00
};
2019-06-20 16:38:15 +02:00
try {
if (scope.installDependencies !== false) {
const runner = runInstall(scope);
2019-06-20 18:04:09 +02:00
2023-04-26 09:07:40 +02:00
runner.stdout?.on('data', logInstall);
runner.stderr?.on('data', logInstall);
2019-06-20 18:04:09 +02:00
await runner;
}
2019-06-20 18:04:09 +02:00
loader.stop();
console.log(`Dependencies installed ${chalk.green('successfully')}.`);
await trackUsage({ event: 'didInstallProjectDependencies', scope });
2019-06-20 16:38:15 +02:00
} catch (error) {
2023-04-26 09:07:40 +02:00
const stderr = isStderrError(error) ? error.stderr : '';
2019-06-20 18:04:09 +02:00
loader.stop();
2019-06-20 16:38:15 +02:00
await trackUsage({
event: 'didNotInstallProjectDependencies',
scope,
2023-04-26 09:07:40 +02:00
error: stderr.slice(-1024),
2019-06-20 16:38:15 +02:00
});
2019-06-20 18:04:09 +02:00
console.error(`${chalk.red('Error')} while installing dependencies:`);
2023-04-26 09:07:40 +02:00
console.error(stderr);
await captureStderr('didNotInstallProjectDependencies', error);
2019-09-26 12:33:36 +02:00
2023-04-26 09:07:40 +02:00
console.log(chalk.black.bgWhite(' Keep trying!'));
console.log();
console.log(
chalk.bold(
'Oh, it seems that you encountered errors while installing dependencies in your project.'
)
);
console.log(`Don't give up, your project was created correctly.`);
console.log(
`Fix the issues mentioned in the installation errors and try to run the following command:`
);
console.log();
console.log(
`cd ${chalk.green(rootPath)} && ${chalk.cyan(scope.useYarn ? 'yarn' : 'npm')} install`
);
console.log();
stopProcess();
2019-06-20 16:38:15 +02:00
}
await trackUsage({ event: 'didCreateProject', scope });
// Init git
if (await tryGitInit(rootPath)) {
console.log('Initialized a git repository.');
console.log();
}
2019-06-20 18:04:09 +02:00
console.log();
2019-06-20 16:38:15 +02:00
console.log(`Your application was created at ${chalk.green(rootPath)}.\n`);
2019-07-02 17:38:06 +02:00
const cmd = chalk.cyan(scope.useYarn ? 'yarn' : 'npm run');
2019-06-20 16:38:15 +02:00
console.log('Available commands in your project:');
console.log();
2019-06-20 18:04:09 +02:00
console.log(` ${cmd} develop`);
console.log(
' Start Strapi in watch mode. (Changes in Strapi project files will trigger a server restart)'
);
2019-06-20 16:38:15 +02:00
console.log();
2019-06-20 18:04:09 +02:00
console.log(` ${cmd} start`);
2019-07-08 16:13:36 +02:00
console.log(' Start Strapi without watch mode.');
2019-06-20 16:38:15 +02:00
console.log();
2019-06-20 18:04:09 +02:00
console.log(` ${cmd} build`);
2019-07-08 16:13:36 +02:00
console.log(' Build Strapi admin panel.');
2019-06-20 16:38:15 +02:00
console.log();
2019-07-03 11:20:14 +02:00
console.log(` ${cmd} strapi`);
console.log(` Display all available commands.`);
console.log();
2019-06-20 16:38:15 +02:00
console.log('You can start by doing:');
console.log();
2019-06-20 18:04:09 +02:00
console.log(` ${chalk.cyan('cd')} ${rootPath}`);
console.log(` ${cmd} develop`);
console.log();
2023-04-26 09:07:40 +02:00
}
2019-06-20 16:38:15 +02:00
const installArguments = ['install', '--production', '--no-optional'];
2023-04-26 09:07:40 +02:00
function runInstall({ rootPath, useYarn }: Scope) {
2019-07-02 17:38:06 +02:00
if (useYarn) {
// Increase timeout for slow internet connections.
installArguments.push('--network-timeout 1000000');
2019-06-20 16:38:15 +02:00
return execa('yarnpkg', installArguments, {
cwd: rootPath,
2019-06-20 18:04:09 +02:00
stdin: 'ignore',
2019-06-20 16:38:15 +02:00
});
}
2019-06-20 18:04:09 +02:00
return execa('npm', installArguments, { cwd: rootPath, stdin: 'ignore' });
2019-06-20 16:38:15 +02:00
}