mirror of
https://github.com/strapi/strapi.git
synced 2025-11-01 18:33:55 +00:00
commit
4d3cb65f37
@ -59,18 +59,18 @@ Install Strapi with this **Quickstart** command to create a Strapi project insta
|
||||
- (Use **yarn** to install the Strapi project (recommended). [Install yarn with these docs](https://yarnpkg.com/lang/en/docs/install/).)
|
||||
|
||||
```bash
|
||||
yarn create strapi-app my-project --quickstart
|
||||
yarn create strapi
|
||||
```
|
||||
|
||||
**or**
|
||||
|
||||
- (Use npm/npx to install the Strapi project.)
|
||||
- (Using npx to install the Strapi project.)
|
||||
|
||||
```bash
|
||||
npx create-strapi-app my-project --quickstart
|
||||
npx create-strapi@latest
|
||||
```
|
||||
|
||||
This command generates a brand new project with the default features (authentication, permissions, content management, content type builder & file upload). The **Quickstart** command installs Strapi using a **SQLite** database which is used for prototyping in development.
|
||||
This command generates a brand new project with the default features (authentication, permissions, content management, content type builder & file upload).
|
||||
|
||||
Enjoy 🎉
|
||||
|
||||
|
||||
@ -1,5 +1,20 @@
|
||||
{
|
||||
"extends": "@strapi/typescript-utils/tsconfigs/admin",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["../plugins/**/admin/src/**/*", "./"],
|
||||
"exclude": ["node_modules/", "build/", "dist/", "**/*.test.ts"]
|
||||
}
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
{
|
||||
"extends": "@strapi/typescript-utils/tsconfigs/server",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "."
|
||||
"rootDir": ".",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"lib": ["ES2020"],
|
||||
"target": "ES2019",
|
||||
|
||||
"strict": false,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmitOnError": true,
|
||||
"noImplicitThis": true
|
||||
},
|
||||
"include": ["./", "src/**/*.json"],
|
||||
"exclude": [
|
||||
|
||||
@ -49,6 +49,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@strapi/pack-up": "5.0.0",
|
||||
"@types/inquirer": "8.2.5",
|
||||
"eslint-config-custom": "5.0.0-beta.9",
|
||||
"tsconfig": "5.0.0-beta.9"
|
||||
},
|
||||
|
||||
@ -1,32 +1,34 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import commander from 'commander';
|
||||
import { checkInstallPath, generateNewApp } from '@strapi/generate-new';
|
||||
import promptUser from './utils/prompt-user';
|
||||
import type { Program } from './types';
|
||||
import { generateNewApp, type Options as GenerateNewAppOptions } from '@strapi/generate-new';
|
||||
|
||||
import * as prompts from './prompts';
|
||||
import type { Options } from './types';
|
||||
import { detectPackageManager } from './package-manager';
|
||||
import * as database from './database';
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8'));
|
||||
|
||||
const command = new commander.Command(packageJson.name);
|
||||
|
||||
const databaseOptions: Array<keyof Program> = [
|
||||
'dbclient',
|
||||
'dbhost',
|
||||
'dbport',
|
||||
'dbname',
|
||||
'dbusername',
|
||||
'dbpassword',
|
||||
'dbssl',
|
||||
'dbfile',
|
||||
];
|
||||
|
||||
command
|
||||
.version(packageJson.version)
|
||||
.arguments('[directory]')
|
||||
.option('--no-run', 'Do not start the application after it is created')
|
||||
.option('--use-npm', 'Force usage of npm instead of yarn to create the project')
|
||||
.option('--debug', 'Display database connection error')
|
||||
.option('--quickstart', 'Quickstart app creation')
|
||||
.usage('[directory] [options]')
|
||||
.option('--quickstart', 'Quickstart app creation (deprecated)')
|
||||
.option('--no-run', 'Do not start the application after it is created.')
|
||||
|
||||
// setup options
|
||||
.option('--ts, --typescript', 'Initialize the project with TypeScript (default)')
|
||||
.option('--js, --javascript', 'Initialize the project with Javascript')
|
||||
|
||||
// Package manager options
|
||||
.option('--use-npm', 'Use npm as the project package manager')
|
||||
.option('--use-yarn', 'Use yarn as the project package manager')
|
||||
.option('--use-pnpm', 'Use pnpm as the project package manager')
|
||||
|
||||
// Database options
|
||||
.option('--dbclient <dbclient>', 'Database client')
|
||||
.option('--dbhost <dbhost>', 'Database host')
|
||||
.option('--dbport <dbport>', 'Database port')
|
||||
@ -35,76 +37,92 @@ command
|
||||
.option('--dbpassword <dbpassword>', 'Database password')
|
||||
.option('--dbssl <dbssl>', 'Database SSL')
|
||||
.option('--dbfile <dbfile>', 'Database file path for sqlite')
|
||||
.option('--dbforce', 'Overwrite database content if any')
|
||||
|
||||
// templates
|
||||
.option('--template <templateurl>', 'Specify a Strapi template')
|
||||
.option('--ts, --typescript', 'Use TypeScript to generate the project')
|
||||
.description('create a new application')
|
||||
.action((directory, programArgs) => {
|
||||
initProject(directory, programArgs);
|
||||
.action((directory, options) => {
|
||||
createStrapiApp(directory, options);
|
||||
})
|
||||
.parse(process.argv);
|
||||
|
||||
function generateApp(projectName: string, options: unknown) {
|
||||
if (!projectName) {
|
||||
async function createStrapiApp(directory: string | undefined, options: Options) {
|
||||
validateOptions(options);
|
||||
|
||||
if (options.quickstart && !directory) {
|
||||
console.error('Please specify the <directory> of your project when using --quickstart');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return generateNewApp(projectName, options).then(() => {
|
||||
if (process.platform === 'win32') {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
const appDirectory = directory || (await prompts.directory());
|
||||
|
||||
async function initProject(projectName: string, programArgs: Program) {
|
||||
if (projectName) {
|
||||
await checkInstallPath(resolve(projectName));
|
||||
const appOptions = {
|
||||
directory: appDirectory,
|
||||
useTypescript: true,
|
||||
packageManager: 'npm',
|
||||
template: options.template,
|
||||
isQuickstart: options.quickstart,
|
||||
} as GenerateNewAppOptions;
|
||||
|
||||
if (options.javascript === true) {
|
||||
appOptions.useTypescript = false;
|
||||
} else if (options.typescript === true) {
|
||||
appOptions.useTypescript = true;
|
||||
} else {
|
||||
appOptions.useTypescript = options.quickstart ? true : await prompts.typescript();
|
||||
}
|
||||
|
||||
if (options.useNpm === true) {
|
||||
appOptions.packageManager = 'npm';
|
||||
} else if (options.usePnpm === true) {
|
||||
appOptions.packageManager = 'pnpm';
|
||||
} else if (options.useYarn === true) {
|
||||
appOptions.packageManager = 'yarn';
|
||||
} else {
|
||||
appOptions.packageManager = detectPackageManager();
|
||||
}
|
||||
|
||||
if (options.quickstart === true && options.run !== false) {
|
||||
appOptions.runApp = true;
|
||||
}
|
||||
|
||||
appOptions.database = await database.getDatabaseInfos(options);
|
||||
|
||||
return generateNewApp(appOptions)
|
||||
.then(() => {
|
||||
if (process.platform === 'win32') {
|
||||
process.exit(0);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
async function validateOptions(options: Options) {
|
||||
const programFlags = command
|
||||
.createHelp()
|
||||
.visibleOptions(command)
|
||||
.reduce<Array<string | undefined>>((acc, { short, long }) => [...acc, short, long], [])
|
||||
.filter(Boolean);
|
||||
|
||||
if (programArgs.template && programFlags.includes(programArgs.template)) {
|
||||
console.error(`${programArgs.template} is not a valid template`);
|
||||
if (options.template && programFlags.includes(options.template)) {
|
||||
console.error(`${options.template} is not a valid template`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const hasDatabaseOptions = databaseOptions.some((opt) => programArgs[opt]);
|
||||
if (options.javascript === true && options.typescript === true) {
|
||||
console.error('You cannot use both --typescript (--ts) and --javascript (--js) flags together');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (programArgs.quickstart && hasDatabaseOptions) {
|
||||
if ([options.useNpm, options.usePnpm, options.useYarn].filter(Boolean).length > 1) {
|
||||
console.error(
|
||||
`The quickstart option is incompatible with the following options: ${databaseOptions.join(
|
||||
', '
|
||||
)}`
|
||||
'You cannot specify multiple package managers at the same time (--use-npm, --use-pnpm, --use-yarn)'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (hasDatabaseOptions) {
|
||||
programArgs.quickstart = false; // Will disable the quickstart question because != 'undefined'
|
||||
}
|
||||
|
||||
if (programArgs.quickstart) {
|
||||
return generateApp(projectName, programArgs);
|
||||
}
|
||||
|
||||
const prompt = await promptUser(projectName, programArgs, hasDatabaseOptions);
|
||||
const directory = prompt.directory || projectName;
|
||||
await checkInstallPath(resolve(directory));
|
||||
|
||||
const options = {
|
||||
template: programArgs.template,
|
||||
quickstart: prompt.quick || programArgs.quickstart,
|
||||
};
|
||||
|
||||
const generateStrapiAppOptions = {
|
||||
...programArgs,
|
||||
...options,
|
||||
};
|
||||
|
||||
return generateApp(directory, generateStrapiAppOptions);
|
||||
database.validateOptions(options);
|
||||
}
|
||||
|
||||
103
packages/cli/create-strapi-app/src/database.ts
Normal file
103
packages/cli/create-strapi-app/src/database.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
import type { Options, DBClient, DBConfig } from './types';
|
||||
import dbQuestions from './db-questions';
|
||||
|
||||
const DBOptions = ['dbclient', 'dbhost', 'dbport', 'dbname', 'dbusername', 'dbpassword'];
|
||||
|
||||
const VALID_CLIENTS = ['sqlite', 'mysql', 'postgres'] as const;
|
||||
|
||||
const DEFAULT_CONFIG: DBConfig = {
|
||||
client: 'sqlite',
|
||||
connection: {},
|
||||
};
|
||||
|
||||
async function dbPrompt() {
|
||||
const { useDefault } = await inquirer.prompt<{ useDefault: boolean }>([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDefault',
|
||||
message: 'Use the default database (sqlite) ?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
|
||||
if (useDefault) {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
const { client } = await inquirer.prompt<{ client: DBClient }>([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'client',
|
||||
message: 'Choose your default database client',
|
||||
choices: ['sqlite', 'postgres', 'mysql'],
|
||||
default: 'sqlite',
|
||||
},
|
||||
]);
|
||||
|
||||
const questions = dbQuestions[client].map((q) => q({ client }));
|
||||
|
||||
const responses = await inquirer.prompt(questions);
|
||||
|
||||
return {
|
||||
client,
|
||||
connection: responses,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getDatabaseInfos(options: Options): Promise<DBConfig> {
|
||||
const hasDBOptions = DBOptions.some((key) => key in options);
|
||||
|
||||
if (!hasDBOptions) {
|
||||
if (options.quickstart) {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
return dbPrompt();
|
||||
}
|
||||
|
||||
if (!options.dbclient) {
|
||||
console.error('Please specify the database client');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const database: DBConfig = {
|
||||
client: options.dbclient,
|
||||
connection: {
|
||||
host: options.dbhost,
|
||||
port: options.dbport,
|
||||
database: options.dbname,
|
||||
username: options.dbusername,
|
||||
password: options.dbpassword,
|
||||
filename: options.dbfile,
|
||||
},
|
||||
};
|
||||
|
||||
if (options.dbssl !== undefined) {
|
||||
database.connection.ssl = options.dbssl === 'true';
|
||||
}
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
export function validateOptions(options: Options) {
|
||||
if (options.dbclient && !VALID_CLIENTS.includes(options.dbclient)) {
|
||||
console.error(
|
||||
`Invalid --dbclient: ${options.dbclient}, expected one of ${VALID_CLIENTS.join(', ')}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const matchingArgs = DBOptions.filter((key) => key in options);
|
||||
const missingArgs = DBOptions.filter((key) => !(key in options));
|
||||
|
||||
if (
|
||||
matchingArgs.length > 0 &&
|
||||
matchingArgs.length !== DBOptions.length &&
|
||||
options.dbclient !== 'sqlite'
|
||||
) {
|
||||
console.error(`Required database arguments are missing: ${missingArgs.join(', ')}.`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import type { Question } from 'inquirer';
|
||||
import type { Scope } from '../types';
|
||||
import type { DBClient } from './types';
|
||||
|
||||
interface QuestionFactory {
|
||||
(options: { scope: Scope; client: 'postgres' | 'mysql' | 'sqlite' }): Question;
|
||||
(options: { client: DBClient }): Question;
|
||||
}
|
||||
|
||||
const DEFAULT_PORTS = {
|
||||
@ -11,11 +11,11 @@ const DEFAULT_PORTS = {
|
||||
sqlite: undefined,
|
||||
};
|
||||
|
||||
const database: QuestionFactory = ({ scope }) => ({
|
||||
const database: QuestionFactory = () => ({
|
||||
type: 'input',
|
||||
name: 'database',
|
||||
message: 'Database name:',
|
||||
default: scope.name,
|
||||
default: 'strapi',
|
||||
validate(value: string) {
|
||||
if (value.includes('.')) {
|
||||
return `The database name can't contain a "."`;
|
||||
15
packages/cli/create-strapi-app/src/package-manager.ts
Normal file
15
packages/cli/create-strapi-app/src/package-manager.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export type PackageManager = 'npm' | 'yarn' | 'pnpm';
|
||||
|
||||
export const detectPackageManager = (): PackageManager => {
|
||||
const userAgent = process.env.npm_config_user_agent || '';
|
||||
|
||||
if (userAgent.startsWith('yarn')) {
|
||||
return 'yarn';
|
||||
}
|
||||
|
||||
if (userAgent.startsWith('pnpm')) {
|
||||
return 'pnpm';
|
||||
}
|
||||
|
||||
return 'npm';
|
||||
};
|
||||
33
packages/cli/create-strapi-app/src/prompts.ts
Normal file
33
packages/cli/create-strapi-app/src/prompts.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function directory() {
|
||||
const { directory } = await inquirer.prompt<{
|
||||
directory: string;
|
||||
}>([
|
||||
{
|
||||
type: 'input',
|
||||
default: 'my-strapi-project',
|
||||
name: 'directory',
|
||||
message: 'What is the name of your project?',
|
||||
},
|
||||
]);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
async function typescript() {
|
||||
const { useTypescript } = await inquirer.prompt<{
|
||||
useTypescript: boolean;
|
||||
}>([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useTypescript',
|
||||
message: 'Do you want to use Typescript ?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
|
||||
return useTypescript;
|
||||
}
|
||||
|
||||
export { directory, typescript };
|
||||
@ -1,9 +1,10 @@
|
||||
export interface Program {
|
||||
noRun?: boolean;
|
||||
export interface Options {
|
||||
useNpm?: boolean;
|
||||
debug?: boolean;
|
||||
usePnpm?: boolean;
|
||||
useYarn?: boolean;
|
||||
quickstart?: boolean;
|
||||
dbclient?: string;
|
||||
run?: boolean;
|
||||
dbclient?: DBClient;
|
||||
dbhost?: string;
|
||||
dbport?: string;
|
||||
dbname?: string;
|
||||
@ -11,7 +12,22 @@ export interface Program {
|
||||
dbpassword?: string;
|
||||
dbssl?: string;
|
||||
dbfile?: string;
|
||||
dbforce?: boolean;
|
||||
template?: string;
|
||||
typescript?: boolean;
|
||||
javascript?: boolean;
|
||||
}
|
||||
|
||||
export type DBClient = 'mysql' | 'postgres' | 'sqlite';
|
||||
|
||||
export type DBConfig = {
|
||||
client: DBClient;
|
||||
connection: {
|
||||
host?: string;
|
||||
port?: string;
|
||||
database?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
filename?: string;
|
||||
ssl?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import inquirer from 'inquirer';
|
||||
import type { Program } from '../types';
|
||||
|
||||
interface Answers {
|
||||
directory: string;
|
||||
quick: boolean;
|
||||
}
|
||||
|
||||
export default async function promptUser(
|
||||
projectName: string,
|
||||
program: Program,
|
||||
hasDatabaseOptions: boolean
|
||||
) {
|
||||
return inquirer.prompt<Answers>([
|
||||
{
|
||||
type: 'input',
|
||||
default: 'my-strapi-project',
|
||||
name: 'directory',
|
||||
message: 'What would you like to name your project?',
|
||||
when: !projectName,
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'quick',
|
||||
message: 'Choose your installation type',
|
||||
when: !program.quickstart && !hasDatabaseOptions,
|
||||
choices: [
|
||||
{
|
||||
name: 'Quickstart (recommended)',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
name: 'Custom (manual settings)',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
node_modules/
|
||||
.eslintrc.js
|
||||
dist/
|
||||
bin/
|
||||
@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['custom/back/typescript'],
|
||||
};
|
||||
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2015-present Strapi Solutions SAS
|
||||
|
||||
Portions of the Strapi software are licensed as follows:
|
||||
|
||||
* All software that resides under an "ee/" directory (the “EE Software”), if that directory exists, is licensed under the license defined in "ee/LICENSE".
|
||||
|
||||
* All software outside of the above-mentioned directories or restrictions above is available under the "MIT Expat" license as set forth below.
|
||||
|
||||
MIT Expat License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@ -1,35 +0,0 @@
|
||||
# Create strapi starter
|
||||
|
||||
This package includes the `create-strapi-starter` CLI to simplify creating a Strapi project using starters and templates
|
||||
|
||||
## How to use
|
||||
|
||||
### Quick usage (recommended)
|
||||
|
||||
Using yarn create command
|
||||
|
||||
```
|
||||
yarn create strapi-starter my-project starter-url
|
||||
```
|
||||
|
||||
Using npx
|
||||
|
||||
```
|
||||
npx create-strapi-starter my-project starter-url
|
||||
```
|
||||
|
||||
### Manual install
|
||||
|
||||
Using yarn
|
||||
|
||||
```
|
||||
yarn global add create-strapi-app
|
||||
create-strapi-starter my-project starter-url
|
||||
```
|
||||
|
||||
Using npm
|
||||
|
||||
```
|
||||
npm install -g create-strapi-app
|
||||
create-strapi-starter my-project starter-url
|
||||
```
|
||||
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
require('../dist/create-strapi-starter');
|
||||
@ -1,65 +0,0 @@
|
||||
{
|
||||
"name": "create-strapi-starter",
|
||||
"version": "5.0.0-beta.9",
|
||||
"description": "Generate a new Strapi application.",
|
||||
"keywords": [
|
||||
"create-strapi-starter",
|
||||
"create",
|
||||
"new",
|
||||
"generate",
|
||||
"strapi"
|
||||
],
|
||||
"homepage": "https://strapi.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/strapi/strapi/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/strapi/strapi.git"
|
||||
},
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"author": {
|
||||
"name": "Strapi Solutions SAS",
|
||||
"email": "hi@strapi.io",
|
||||
"url": "https://strapi.io"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Strapi Solutions SAS",
|
||||
"email": "hi@strapi.io",
|
||||
"url": "https://strapi.io"
|
||||
}
|
||||
],
|
||||
"main": "",
|
||||
"bin": "./bin/index.js",
|
||||
"files": [
|
||||
"dist/",
|
||||
"bin/"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pack-up build",
|
||||
"clean": "run -T rimraf ./dist",
|
||||
"lint": "run -T eslint .",
|
||||
"watch": "pack-up watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@strapi/generate-new": "5.0.0-beta.9",
|
||||
"chalk": "4.1.2",
|
||||
"ci-info": "4.0.0",
|
||||
"commander": "8.3.0",
|
||||
"execa": "5.1.1",
|
||||
"fs-extra": "11.2.0",
|
||||
"inquirer": "8.2.5",
|
||||
"ora": "5.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@strapi/pack-up": "5.0.0",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"eslint-config-custom": "5.0.0-beta.9",
|
||||
"tsconfig": "5.0.0-beta.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <=20.x.x",
|
||||
"npm": ">=6.0.0"
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { defineConfig } from '@strapi/pack-up';
|
||||
|
||||
export default defineConfig({
|
||||
bundles: [
|
||||
{
|
||||
source: './src/create-strapi-starter.ts',
|
||||
import: './dist/create-strapi-starter.mjs',
|
||||
require: './dist/create-strapi-starter.js',
|
||||
},
|
||||
],
|
||||
dist: './dist',
|
||||
runtime: 'node',
|
||||
});
|
||||
@ -1,111 +0,0 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import commander, { CommanderError } from 'commander';
|
||||
|
||||
import buildStarter from './utils/build-starter';
|
||||
import promptUser from './utils/prompt-user';
|
||||
import type { Program } from './types';
|
||||
|
||||
interface ProjectArgs {
|
||||
projectName: string;
|
||||
starter: string;
|
||||
}
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8'));
|
||||
|
||||
const program = new commander.Command(packageJson.name);
|
||||
|
||||
const incompatibleQuickstartOptions: Array<keyof Program> = [
|
||||
'dbclient',
|
||||
'dbhost',
|
||||
'dbport',
|
||||
'dbname',
|
||||
'dbusername',
|
||||
'dbpassword',
|
||||
'dbssl',
|
||||
'dbfile',
|
||||
];
|
||||
|
||||
program
|
||||
.version(packageJson.version)
|
||||
.arguments('[directory], [starter]')
|
||||
.option('--use-npm', 'Force usage of npm instead of yarn to create the project')
|
||||
.option('--debug', 'Display database connection error')
|
||||
.option('--quickstart', 'Quickstart app creation')
|
||||
.option('--dbclient <dbclient>', 'Database client')
|
||||
.option('--dbhost <dbhost>', 'Database host')
|
||||
.option('--dbport <dbport>', 'Database port')
|
||||
.option('--dbname <dbname>', 'Database name')
|
||||
.option('--dbusername <dbusername>', 'Database username')
|
||||
.option('--dbpassword <dbpassword>', 'Database password')
|
||||
.option('--dbssl <dbssl>', 'Database SSL')
|
||||
.option('--dbfile <dbfile>', 'Database file path for sqlite')
|
||||
.option('--dbforce', 'Overwrite database content if any')
|
||||
.description(
|
||||
'Create a fullstack monorepo application using the strapi backend template specified in the provided starter'
|
||||
)
|
||||
.action((directory, starter, programArgs) => {
|
||||
const projectArgs: ProjectArgs = { projectName: directory, starter };
|
||||
|
||||
initProject(projectArgs, programArgs);
|
||||
});
|
||||
|
||||
function generateApp(projectArgs: ProjectArgs, programArgs: Program) {
|
||||
if (!projectArgs.projectName || !projectArgs.starter) {
|
||||
console.error(
|
||||
'Please specify the <directory> and <starter> of your project when using --quickstart'
|
||||
);
|
||||
// eslint-disable-next-line no-process-exit
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return buildStarter(projectArgs, programArgs);
|
||||
}
|
||||
|
||||
async function initProject(projectArgs: ProjectArgs, programArgs: Program) {
|
||||
const hasIncompatibleQuickstartOptions = incompatibleQuickstartOptions.some(
|
||||
(opt) => programArgs[opt]
|
||||
);
|
||||
|
||||
if (programArgs.quickstart && hasIncompatibleQuickstartOptions) {
|
||||
console.error(
|
||||
`The quickstart option is incompatible with the following options: ${incompatibleQuickstartOptions.join(
|
||||
', '
|
||||
)}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (hasIncompatibleQuickstartOptions) {
|
||||
programArgs.quickstart = false; // Will disable the quickstart question because != 'undefined'
|
||||
}
|
||||
|
||||
const { projectName, starter } = projectArgs;
|
||||
|
||||
if (programArgs.quickstart) {
|
||||
return generateApp(projectArgs, programArgs);
|
||||
}
|
||||
|
||||
const prompt = await promptUser(projectName, starter, programArgs);
|
||||
|
||||
const promptProjectArgs = {
|
||||
projectName: prompt.directory || projectName,
|
||||
starter: prompt.starter || starter,
|
||||
};
|
||||
|
||||
return generateApp(promptProjectArgs, {
|
||||
...programArgs,
|
||||
quickstart: prompt.quick || programArgs.quickstart,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
program.parse(process.argv);
|
||||
} catch (err) {
|
||||
if (err instanceof CommanderError) {
|
||||
if (err.exitCode && err.exitCode !== 0) {
|
||||
program.outputHelp();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,131 +0,0 @@
|
||||
############################
|
||||
# OS X
|
||||
############################
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
._*
|
||||
|
||||
|
||||
############################
|
||||
# Linux
|
||||
############################
|
||||
|
||||
*~
|
||||
|
||||
|
||||
############################
|
||||
# Windows
|
||||
############################
|
||||
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
|
||||
############################
|
||||
# Packages
|
||||
############################
|
||||
|
||||
*.7z
|
||||
*.csv
|
||||
*.dat
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.seed
|
||||
*.so
|
||||
*.swo
|
||||
*.swp
|
||||
*.swn
|
||||
*.swm
|
||||
*.out
|
||||
*.pid
|
||||
|
||||
|
||||
############################
|
||||
# Logs and databases
|
||||
############################
|
||||
|
||||
.tmp
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
|
||||
############################
|
||||
# Misc.
|
||||
############################
|
||||
|
||||
*#
|
||||
ssl
|
||||
.idea
|
||||
nbproject
|
||||
public/uploads/*
|
||||
!public/uploads/.gitkeep
|
||||
.tsbuildinfo
|
||||
.eslintcache
|
||||
|
||||
############################
|
||||
# Node.js
|
||||
############################
|
||||
|
||||
lib-cov
|
||||
lcov.info
|
||||
pids
|
||||
logs
|
||||
results
|
||||
node_modules
|
||||
.node_history
|
||||
|
||||
############################
|
||||
# Tests
|
||||
############################
|
||||
|
||||
coverage
|
||||
|
||||
############################
|
||||
# Package managers
|
||||
############################
|
||||
|
||||
.yarn/*
|
||||
!.yarn/cache
|
||||
!.yarn/unplugged
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
yarn-error.log
|
||||
|
||||
|
||||
############################
|
||||
# Strapi
|
||||
############################
|
||||
|
||||
.env
|
||||
license.txt
|
||||
exports
|
||||
*.cache
|
||||
dist
|
||||
build
|
||||
.strapi-updater.json
|
||||
@ -1,24 +0,0 @@
|
||||
export interface Options {
|
||||
useYarn?: boolean;
|
||||
}
|
||||
|
||||
export interface PackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface Program {
|
||||
useNpm?: boolean;
|
||||
debug?: boolean;
|
||||
quickstart?: boolean;
|
||||
dbclient?: string;
|
||||
dbhost?: string;
|
||||
dbport?: string;
|
||||
dbname?: string;
|
||||
dbusername?: string;
|
||||
dbpassword?: string;
|
||||
dbssl?: string;
|
||||
dbfile?: string;
|
||||
dbforce?: boolean;
|
||||
template?: string;
|
||||
}
|
||||
@ -1,186 +0,0 @@
|
||||
import { resolve, join, basename } from 'path';
|
||||
import os from 'os';
|
||||
import fse from 'fs-extra';
|
||||
import ora from 'ora';
|
||||
import ciEnv from 'ci-info';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { generateNewApp } from '@strapi/generate-new';
|
||||
|
||||
import hasYarn from './has-yarn';
|
||||
import { runInstall, runApp, initGit } from './child-process';
|
||||
import { getStarterPackageInfo, downloadNpmStarter } from './fetch-npm-starter';
|
||||
import logger from './logger';
|
||||
import stopProcess from './stop-process';
|
||||
import type { Options, PackageInfo, Program } from '../types';
|
||||
|
||||
function readStarterJson(filePath: string, starter: string) {
|
||||
try {
|
||||
const data = fse.readFileSync(filePath);
|
||||
return JSON.parse(data.toString());
|
||||
} catch (err) {
|
||||
stopProcess(`Could not find ${chalk.yellow('starter.json')} in ${chalk.yellow(starter)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function initPackageJson(rootPath: string, projectName: string, { useYarn }: Options = {}) {
|
||||
const packageManager = useYarn ? 'yarn --cwd' : 'npm run --prefix';
|
||||
|
||||
try {
|
||||
await fse.writeJson(
|
||||
join(rootPath, 'package.json'),
|
||||
{
|
||||
name: projectName,
|
||||
private: true,
|
||||
version: '0.0.0',
|
||||
scripts: {
|
||||
'develop:backend': `${packageManager} backend develop`,
|
||||
'develop:frontend': `wait-on http://localhost:1337/admin && ${packageManager} frontend develop`,
|
||||
develop: 'cross-env FORCE_COLOR=1 npm-run-all -l -p develop:*',
|
||||
},
|
||||
devDependencies: {
|
||||
'npm-run-all': '4.1.5',
|
||||
'wait-on': '5.2.1',
|
||||
'cross-env': '7.0.3',
|
||||
},
|
||||
},
|
||||
{
|
||||
spaces: 2,
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
stopProcess(`Failed to create ${chalk.yellow('package.json')} in ${chalk.yellow(rootPath)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function installWithLogs(path: string, options: Options) {
|
||||
const installPrefix = chalk.yellow('Installing dependencies:');
|
||||
const loader = ora(installPrefix).start();
|
||||
const logInstall = (chunk = '') => {
|
||||
loader.text = `${installPrefix} ${chunk.toString().split('\n').join(' ')}`;
|
||||
};
|
||||
|
||||
const runner = runInstall(path, options);
|
||||
runner.stdout?.on('data', logInstall);
|
||||
runner.stderr?.on('data', logInstall);
|
||||
|
||||
await runner;
|
||||
|
||||
loader.stop();
|
||||
console.log(`Dependencies installed ${chalk.green('successfully')}.`);
|
||||
}
|
||||
|
||||
async function getStarterInfo(starter: string, { useYarn }: Options = {}) {
|
||||
const isLocalStarter = ['./', '../', '/'].some((filePrefix) => starter.startsWith(filePrefix));
|
||||
|
||||
let starterPath;
|
||||
let starterParentPath;
|
||||
let starterPackageInfo: PackageInfo | undefined;
|
||||
|
||||
if (isLocalStarter) {
|
||||
// Starter is a local directory
|
||||
console.log('Installing local starter.');
|
||||
starterPath = resolve(starter);
|
||||
} else {
|
||||
// Starter should be an npm package. Fetch starter info
|
||||
starterPackageInfo = await getStarterPackageInfo(starter, { useYarn });
|
||||
console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter.`);
|
||||
|
||||
// Download starter repository to a temporary directory
|
||||
starterParentPath = await fse.mkdtemp(join(os.tmpdir(), 'strapi-'));
|
||||
starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath, { useYarn });
|
||||
}
|
||||
|
||||
return { isLocalStarter, starterPath, starterParentPath, starterPackageInfo };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} projectArgs - The arguments for create a project
|
||||
* @param {string|null} projectArgs.projectName - The name/path of project
|
||||
* @param {string|null} projectArgs.starter - The npm package of the starter
|
||||
* @param {Object} program - Commands for generating new application
|
||||
*/
|
||||
export default async function buildStarter(
|
||||
{ projectName, starter }: { projectName: string; starter: string },
|
||||
program: Program
|
||||
) {
|
||||
const hasYarnInstalled = hasYarn();
|
||||
const { isLocalStarter, starterPath, starterParentPath, starterPackageInfo } =
|
||||
await getStarterInfo(starter, { useYarn: hasYarnInstalled });
|
||||
|
||||
// Project directory
|
||||
const rootPath = resolve(projectName);
|
||||
const projectBasename = basename(rootPath);
|
||||
const starterJson = readStarterJson(join(starterPath, 'starter.json'), starter);
|
||||
|
||||
try {
|
||||
await fse.ensureDir(rootPath);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
stopProcess(`Failed to create ${chalk.yellow(rootPath)}: ${error.message}`);
|
||||
}
|
||||
|
||||
stopProcess(`Failed to create ${chalk.yellow(rootPath)}: ${error}`);
|
||||
}
|
||||
|
||||
// Copy the downloaded frontend folder to the project folder
|
||||
const frontendPath = join(rootPath, 'frontend');
|
||||
|
||||
try {
|
||||
await fse.copy(join(starterPath, 'starter'), frontendPath, {
|
||||
overwrite: true,
|
||||
recursive: true,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
stopProcess(`Failed to create ${chalk.yellow(frontendPath)}: ${error.message}`);
|
||||
}
|
||||
|
||||
stopProcess(`Failed to create ${chalk.yellow(frontendPath)}`);
|
||||
}
|
||||
|
||||
// Delete the starter directory if it was downloaded
|
||||
if (!isLocalStarter && starterParentPath) {
|
||||
await fse.remove(starterParentPath);
|
||||
}
|
||||
|
||||
// Set command options for Strapi app
|
||||
const generateStrapiAppOptions = {
|
||||
...program,
|
||||
starter: starterPackageInfo?.name,
|
||||
run: false,
|
||||
};
|
||||
if (starterJson.template.version) {
|
||||
generateStrapiAppOptions.template = `${starterJson.template.name}@${starterJson.template.version}`;
|
||||
} else {
|
||||
generateStrapiAppOptions.template = starterJson.template.name;
|
||||
}
|
||||
|
||||
// Create strapi app using the template
|
||||
await generateNewApp(join(rootPath, 'backend'), generateStrapiAppOptions);
|
||||
|
||||
// Install frontend dependencies
|
||||
console.log(`Creating Strapi starter frontend at ${chalk.yellow(frontendPath)}.`);
|
||||
console.log('Installing frontend dependencies');
|
||||
await installWithLogs(frontendPath, { useYarn: hasYarnInstalled });
|
||||
|
||||
// Setup monorepo
|
||||
initPackageJson(rootPath, projectBasename, { useYarn: hasYarnInstalled });
|
||||
|
||||
// Add gitignore
|
||||
try {
|
||||
const gitignore = join(__dirname, '..', 'resources', 'gitignore');
|
||||
await fse.copy(gitignore, join(rootPath, '.gitignore'));
|
||||
} catch (err) {
|
||||
logger.warn(`Failed to create file: ${chalk.yellow('.gitignore')}`);
|
||||
}
|
||||
|
||||
await installWithLogs(rootPath, { useYarn: hasYarnInstalled });
|
||||
|
||||
if (!ciEnv.isCI) {
|
||||
await initGit(rootPath);
|
||||
}
|
||||
|
||||
console.log(chalk.green('Starting the app'));
|
||||
await runApp(rootPath, { useYarn: hasYarnInstalled });
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
import { execSync } from 'child_process';
|
||||
import execa from 'execa';
|
||||
import logger from './logger';
|
||||
import type { Options } from '../types';
|
||||
|
||||
// TODO: Refactor run install, use the methods available in @strapi/utils instead
|
||||
export function runInstall(path: string, { useYarn }: Options = {}) {
|
||||
return execa(useYarn ? 'yarn' : 'npm', ['install'], {
|
||||
cwd: path,
|
||||
stdin: 'ignore',
|
||||
});
|
||||
}
|
||||
export function runApp(rootPath: string, { useYarn }: Options = {}) {
|
||||
if (useYarn) {
|
||||
return execa('yarn', ['develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: rootPath,
|
||||
});
|
||||
}
|
||||
|
||||
return execa('npm', ['run', 'develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: rootPath,
|
||||
});
|
||||
}
|
||||
|
||||
export async function initGit(rootPath: string) {
|
||||
try {
|
||||
await execa('git', ['init'], {
|
||||
cwd: rootPath,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn('Could not initialize a git repository');
|
||||
}
|
||||
|
||||
try {
|
||||
await execa('git', ['add', '-A'], { cwd: rootPath });
|
||||
|
||||
execSync('git commit -m "Create Strapi starter project"', {
|
||||
cwd: rootPath,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn('Could not create initial git commit');
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
import path from 'path';
|
||||
import execa from 'execa';
|
||||
import chalk from 'chalk';
|
||||
import stopProcess from './stop-process';
|
||||
import type { Options, PackageInfo } from '../types';
|
||||
|
||||
/**
|
||||
* Gets the package version on npm. Will fail if the package does not exist
|
||||
*/
|
||||
async function getPackageInfo(packageName: string, options?: Options): Promise<PackageInfo> {
|
||||
const { useYarn } = options ?? {};
|
||||
|
||||
// Use yarn if possible because it's faster
|
||||
if (useYarn) {
|
||||
const { stdout } = await execa('yarn', ['info', packageName, '--json']);
|
||||
const yarnInfo = JSON.parse(stdout);
|
||||
return {
|
||||
name: yarnInfo.data.name,
|
||||
version: yarnInfo.data.version,
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback to npm
|
||||
const { stdout } = await execa('npm', ['view', packageName, 'name', 'version', '--silent']);
|
||||
// Use regex to parse name and version from CLI result
|
||||
const match = stdout.match(/(?<=')(.*?)(?=')/gm);
|
||||
|
||||
if (!match) {
|
||||
throw new Error('No match for name@version');
|
||||
}
|
||||
|
||||
const [name, version] = match;
|
||||
return { name, version };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version and full package name of the starter
|
||||
*/
|
||||
export async function getStarterPackageInfo(
|
||||
starter: string,
|
||||
options?: Options
|
||||
): Promise<PackageInfo> {
|
||||
const { useYarn } = options ?? {};
|
||||
|
||||
// Check if starter is a shorthand
|
||||
try {
|
||||
const longhand = `@strapi/starter-${starter}`;
|
||||
return await getPackageInfo(longhand, { useYarn });
|
||||
} catch (error) {
|
||||
// Ignore error, we now know it's not a shorthand
|
||||
}
|
||||
// Fetch version of the non-shorthand package
|
||||
try {
|
||||
return await getPackageInfo(starter, { useYarn });
|
||||
} catch (error) {
|
||||
return stopProcess(`Could not find package ${chalk.yellow(starter)} on npm`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a starter package from the npm registry
|
||||
*/
|
||||
export async function downloadNpmStarter(
|
||||
packageInfo: PackageInfo,
|
||||
parentDir: string,
|
||||
options?: Options
|
||||
): Promise<string> {
|
||||
const { name, version } = packageInfo;
|
||||
const { useYarn } = options ?? {};
|
||||
|
||||
// Download from npm, using yarn if possible
|
||||
if (useYarn) {
|
||||
await execa('yarn', ['add', `${name}@${version}`, '--no-lockfile', '--silent'], {
|
||||
cwd: parentDir,
|
||||
});
|
||||
} else {
|
||||
await execa('npm', ['install', `${name}@${version}`, '--no-save', '--silent'], {
|
||||
cwd: parentDir,
|
||||
});
|
||||
}
|
||||
|
||||
// Return the path of the actual starter
|
||||
const exactStarterPath = path.dirname(
|
||||
require.resolve(`${name}/package.json`, { paths: [parentDir] })
|
||||
);
|
||||
return exactStarterPath;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import execa from 'execa';
|
||||
|
||||
export default function hasYarn() {
|
||||
try {
|
||||
const { exitCode } = execa.commandSync('yarn --version', { shell: true });
|
||||
|
||||
if (exitCode === 0) return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
export default {
|
||||
error(message: string) {
|
||||
console.error(`${chalk.red('error')}: ${message}`);
|
||||
},
|
||||
|
||||
warn(message: string) {
|
||||
console.log(`${chalk.yellow('warning')}: ${message}`);
|
||||
},
|
||||
|
||||
info(message: string) {
|
||||
console.log(`${chalk.blue('info')}: ${message}`);
|
||||
},
|
||||
};
|
||||
@ -1,45 +0,0 @@
|
||||
import inquirer from 'inquirer';
|
||||
import type { Program } from '../types';
|
||||
|
||||
interface Answers {
|
||||
directory: string;
|
||||
quick: boolean;
|
||||
starter: string;
|
||||
}
|
||||
|
||||
// Prompts the user with required questions to create the project and return the answers
|
||||
export default async function promptUser(projectName: string, starter: string, program: Program) {
|
||||
const questions: ReadonlyArray<inquirer.DistinctQuestion<Answers>> = [
|
||||
{
|
||||
type: 'input',
|
||||
default: 'my-strapi-project',
|
||||
name: 'directory',
|
||||
message: 'What would you like to name your project?',
|
||||
when: !projectName,
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'quick',
|
||||
message: 'Choose your installation type',
|
||||
when: !program.quickstart,
|
||||
choices: [
|
||||
{
|
||||
name: 'Quickstart (recommended)',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
name: 'Custom (manual settings)',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'starter',
|
||||
when: !starter,
|
||||
message: 'Please provide the npm package name of the starter you want to use:',
|
||||
},
|
||||
];
|
||||
|
||||
return inquirer.prompt<Answers>(questions);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import logger from './logger';
|
||||
|
||||
export default function stopProcess(message: string) {
|
||||
if (message) {
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
return process.exit(1);
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "**/__tests__/**"]
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json"
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": "tsconfig/base.json",
|
||||
"include": ["src", "packup.config.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -59,18 +59,18 @@ Install Strapi with this **Quickstart** command to create a Strapi project insta
|
||||
- (Use **yarn** to install the Strapi project (recommended). [Install yarn with these docs](https://yarnpkg.com/lang/en/docs/install/).)
|
||||
|
||||
```bash
|
||||
yarn create strapi-app my-project --quickstart
|
||||
yarn create strapi
|
||||
```
|
||||
|
||||
**or**
|
||||
|
||||
- (Use npm/npx to install the Strapi project.)
|
||||
- (Using npx to install the Strapi project.)
|
||||
|
||||
```bash
|
||||
npx create-strapi-app my-project --quickstart
|
||||
npx create-strapi@latest
|
||||
```
|
||||
|
||||
This command generates a brand new project with the default features (authentication, permissions, content management, content type builder & file upload). The **Quickstart** command installs Strapi using a **SQLite** database which is used for prototyping in development.
|
||||
This command generates a brand new project with the default features (authentication, permissions, content management, content type builder & file upload).
|
||||
|
||||
Enjoy 🎉
|
||||
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
interface TsConfigFiles {
|
||||
tsconfigFile: TemplateFile;
|
||||
tsconfigBuildFile: TemplateFile;
|
||||
}
|
||||
|
||||
const ADMIN: TsConfigFiles = {
|
||||
tsconfigFile: {
|
||||
name: 'admin/tsconfig.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"compilerOptions: {
|
||||
target: 'ESNext',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'Bundler',
|
||||
useDefineForClassFields: true,
|
||||
lib: ['DOM', 'DOM.Iterable', 'ESNext'],
|
||||
allowJs: false,
|
||||
skipLibCheck: true,
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
strict: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
resolveJsonModule: true,
|
||||
noEmit: true,
|
||||
jsx: 'react-jsx',
|
||||
},
|
||||
"include": ["./src", "./custom.d.ts"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
},
|
||||
}
|
||||
`,
|
||||
},
|
||||
tsconfigBuildFile: {
|
||||
name: 'admin/tsconfig.build.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"include": ["./src", "./custom.d.ts"],
|
||||
"exclude": ["**/*.test.ts", "**/*.test.tsx"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const SERVER: TsConfigFiles = {
|
||||
tsconfigFile: {
|
||||
name: 'server/tsconfig.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"include": ["./src"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"lib": ["ES2020"],
|
||||
"target": "ES2019",
|
||||
|
||||
"strict": false,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmitOnError": true,
|
||||
"noImplicitThis": true
|
||||
},
|
||||
}
|
||||
`,
|
||||
},
|
||||
tsconfigBuildFile: {
|
||||
name: 'server/tsconfig.build.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"include": ["./src"],
|
||||
"exclude": ["**/*.test.ts"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
export { ADMIN as adminTsconfigFiles, SERVER as serverTsconfigFiles };
|
||||
@ -44,7 +44,6 @@
|
||||
"watch": "pack-up watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/node": "7.112.2",
|
||||
"chalk": "^4.1.2",
|
||||
"execa": "5.1.1",
|
||||
"fs-extra": "11.2.0",
|
||||
@ -58,6 +57,7 @@
|
||||
"devDependencies": {
|
||||
"@strapi/pack-up": "5.0.0",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/inquirer": "8.2.5",
|
||||
"copyfiles": "2.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
import { merge } from 'lodash';
|
||||
|
||||
import { trackUsage } from './utils/usage';
|
||||
import defaultConfigs from './utils/db-configs';
|
||||
import clientDependencies from './utils/db-client-dependencies';
|
||||
import getClientName from './utils/db-client-name';
|
||||
import createProject from './create-project';
|
||||
|
||||
import type { ClientName, Configuration, Scope } from './types';
|
||||
|
||||
export default async (scope: Scope) => {
|
||||
console.log('Creating a project from the database CLI arguments.');
|
||||
await trackUsage({ event: 'didChooseCustomDatabase', scope });
|
||||
|
||||
const { client } = scope.database ?? {};
|
||||
|
||||
if (!client) {
|
||||
throw new Error('Missing client');
|
||||
}
|
||||
|
||||
const configuration: Configuration = {
|
||||
client: getClientName({ client }),
|
||||
connection: merge(
|
||||
{},
|
||||
defaultConfigs[client as keyof typeof defaultConfigs] || {},
|
||||
scope.database
|
||||
),
|
||||
dependencies: {
|
||||
...clientDependencies({ scope, client } as { scope: Scope; client: ClientName }),
|
||||
...scope.additionalsDependencies,
|
||||
},
|
||||
};
|
||||
|
||||
return createProject(scope, configuration);
|
||||
};
|
||||
@ -1,91 +0,0 @@
|
||||
import inquirer from 'inquirer';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
import { trackUsage } from './utils/usage';
|
||||
import defaultConfigs from './utils/db-configs';
|
||||
import clientDependencies from './utils/db-client-dependencies';
|
||||
import dbQuestions from './utils/db-questions';
|
||||
import createProject from './create-project';
|
||||
import type { Configuration, Scope } from './types';
|
||||
|
||||
const LANGUAGES = {
|
||||
javascript: 'JavaScript',
|
||||
typescript: 'TypeScript',
|
||||
};
|
||||
|
||||
export default async (scope: Scope) => {
|
||||
if (!scope.useTypescript) {
|
||||
const language = await askAboutLanguages();
|
||||
scope.useTypescript = language === LANGUAGES.typescript;
|
||||
}
|
||||
|
||||
await trackUsage({ event: 'didChooseCustomDatabase', scope });
|
||||
|
||||
const configuration = await askDbInfosAndTest(scope).catch((error) => {
|
||||
return trackUsage({ event: 'didNotConnectDatabase', scope, error }).then(() => {
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
|
||||
console.log();
|
||||
console.log('Creating a project with custom database options.');
|
||||
await trackUsage({ event: 'didConnectDatabase', scope });
|
||||
return createProject(scope, configuration);
|
||||
};
|
||||
|
||||
async function askDbInfosAndTest(scope: Scope) {
|
||||
const { client, connection } = await askDatabaseInfos(scope);
|
||||
|
||||
return {
|
||||
client,
|
||||
connection,
|
||||
dependencies: {
|
||||
...clientDependencies({ client }),
|
||||
...scope.additionalsDependencies,
|
||||
},
|
||||
} as Configuration;
|
||||
}
|
||||
|
||||
async function askDatabaseInfos(scope: Scope) {
|
||||
const { client } = await inquirer.prompt<{ client: 'sqlite' | 'postgres' | 'mysql' }>([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'client',
|
||||
message: 'Choose your default database client',
|
||||
choices: ['sqlite', 'postgres', 'mysql'],
|
||||
default: 'sqlite',
|
||||
},
|
||||
]);
|
||||
|
||||
const questions = dbQuestions[client].map((q) => q({ scope, client }));
|
||||
|
||||
if (!questions) {
|
||||
return { client };
|
||||
}
|
||||
|
||||
const responses = await inquirer.prompt(questions);
|
||||
|
||||
const connection = merge({}, defaultConfigs[client] || {}, {
|
||||
client,
|
||||
connection: responses,
|
||||
});
|
||||
|
||||
return {
|
||||
client,
|
||||
connection,
|
||||
};
|
||||
}
|
||||
|
||||
async function askAboutLanguages() {
|
||||
const { language } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: 'Choose your preferred language',
|
||||
choices: Object.values(LANGUAGES),
|
||||
default: LANGUAGES.javascript,
|
||||
},
|
||||
]);
|
||||
|
||||
return language;
|
||||
}
|
||||
@ -3,35 +3,36 @@ 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 { trackUsage } from './utils/usage';
|
||||
import mergeTemplate from './utils/merge-template.js';
|
||||
import tryGitInit from './utils/git';
|
||||
|
||||
import packageJSON from './resources/json/common/package.json';
|
||||
import createPackageJSON from './resources/json/common/package.json';
|
||||
import jsconfig from './resources/json/js/jsconfig.json';
|
||||
import adminTsconfig from './resources/json/ts/tsconfig-admin.json';
|
||||
import serverTsconfig from './resources/json/ts/tsconfig-server.json';
|
||||
import { createDatabaseConfig, generateDbEnvVariables } from './resources/templates/database';
|
||||
import createEnvFile from './resources/templates/env';
|
||||
import { Configuration, Scope, isStderrError } from './types';
|
||||
import { 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)}.`);
|
||||
console.log('Creating files.');
|
||||
const resources = join(__dirname, 'resources');
|
||||
|
||||
export default async function createProject(scope: Scope) {
|
||||
console.log(`Creating a new Strapi application at ${chalk.green(scope.rootPath)}.\n`);
|
||||
|
||||
const { rootPath, useTypescript } = scope;
|
||||
const resources = join(__dirname, 'resources');
|
||||
|
||||
const language = useTypescript ? 'ts' : 'js';
|
||||
if (!scope.isQuickstart) {
|
||||
await trackUsage({ event: 'didChooseCustomDatabase', scope });
|
||||
} else {
|
||||
await trackUsage({ event: 'didChooseQuickstart', scope });
|
||||
}
|
||||
|
||||
try {
|
||||
const language = useTypescript ? 'ts' : 'js';
|
||||
|
||||
// copy files
|
||||
await fse.copy(join(resources, 'files', language), rootPath);
|
||||
|
||||
@ -53,64 +54,36 @@ export default async function createProject(
|
||||
// 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 });
|
||||
|
||||
// copy templates
|
||||
await fse.writeJSON(
|
||||
join(rootPath, 'package.json'),
|
||||
packageJSON({
|
||||
strapiDependencies: scope.strapiDependencies,
|
||||
additionalsDependencies: dependencies,
|
||||
strapiVersion: scope.strapiVersion,
|
||||
projectName: _.kebabCase(scope.name),
|
||||
uuid: scope.uuid,
|
||||
packageJsonStrapi: scope.packageJsonStrapi,
|
||||
}),
|
||||
{
|
||||
spaces: 2,
|
||||
}
|
||||
);
|
||||
await createPackageJSON(scope);
|
||||
|
||||
await trackUsage({ event: 'didWritePackageJSON', scope });
|
||||
|
||||
if (useTypescript) {
|
||||
const filesMap = {
|
||||
'tsconfig-admin.json.js': 'src/admin',
|
||||
'tsconfig-server.json.js': '.',
|
||||
};
|
||||
const tsConfigs = [
|
||||
{
|
||||
path: 'src/admin/tsconfig.json',
|
||||
content: adminTsconfig(),
|
||||
},
|
||||
{
|
||||
path: 'tsconfig.json',
|
||||
content: serverTsconfig(),
|
||||
},
|
||||
];
|
||||
|
||||
for (const [fileName, path] of Object.entries(filesMap)) {
|
||||
const destPath = join(rootPath, path, 'tsconfig.json');
|
||||
|
||||
if (fileName === 'tsconfig-admin.json.js') {
|
||||
await fse.writeJSON(destPath, adminTsconfig(), { spaces: 2 });
|
||||
}
|
||||
if (fileName === 'tsconfig-server.json.js') {
|
||||
await fse.writeJSON(destPath, serverTsconfig(), { spaces: 2 });
|
||||
}
|
||||
for (const { path, content } of tsConfigs) {
|
||||
await fse.writeJSON(join(rootPath, path), content, { spaces: 2 });
|
||||
}
|
||||
} else {
|
||||
const filesMap = { 'jsconfig.json.js': '.' };
|
||||
|
||||
for (const [, path] of Object.entries(filesMap)) {
|
||||
const destPath = join(rootPath, path, 'jsconfig.json');
|
||||
await fse.writeJSON(destPath, jsconfig(), { spaces: 2 });
|
||||
}
|
||||
await fse.writeJSON(join(rootPath, 'jsconfig.json'), jsconfig(), { spaces: 2 });
|
||||
}
|
||||
|
||||
// ensure node_modules is created
|
||||
await fse.ensureDir(join(rootPath, 'node_modules'));
|
||||
|
||||
// create config/database
|
||||
await fse.appendFile(join(rootPath, '.env'), generateDbEnvVariables({ client, connection }));
|
||||
await fse.appendFile(join(rootPath, '.env'), generateDbEnvVariables(scope));
|
||||
await fse.writeFile(
|
||||
join(rootPath, `config/database.${language}`),
|
||||
createDatabaseConfig({ useTypescript })
|
||||
@ -138,44 +111,25 @@ export default async function createProject(
|
||||
|
||||
await trackUsage({ event: 'willInstallProjectDependencies', scope });
|
||||
|
||||
const installPrefix = chalk.yellow('Installing dependencies:');
|
||||
const loader = ora(installPrefix).start();
|
||||
|
||||
const logInstall = (chunk = '') => {
|
||||
loader.text = `${installPrefix} ${chunk.toString().split('\n').join(' ')}`;
|
||||
};
|
||||
console.log(`Installing dependencies with ${chalk.bold(scope.packageManager)}\n`);
|
||||
|
||||
try {
|
||||
if (scope.installDependencies !== false) {
|
||||
const runner = runInstall(scope);
|
||||
|
||||
runner.stdout?.on('data', logInstall);
|
||||
runner.stderr?.on('data', logInstall);
|
||||
|
||||
await runner;
|
||||
await runInstall(scope);
|
||||
}
|
||||
|
||||
loader.stop();
|
||||
console.log(`Dependencies installed ${chalk.green('successfully')}.`);
|
||||
|
||||
await trackUsage({ event: 'didInstallProjectDependencies', scope });
|
||||
} catch (error) {
|
||||
const stderr = isStderrError(error) ? error.stderr : '';
|
||||
|
||||
loader.stop();
|
||||
await trackUsage({
|
||||
event: 'didNotInstallProjectDependencies',
|
||||
scope,
|
||||
error: stderr.slice(-1024),
|
||||
});
|
||||
|
||||
console.error(`${chalk.red('Error')} while installing dependencies:`);
|
||||
console.error(stderr);
|
||||
|
||||
await captureStderr('didNotInstallProjectDependencies', error);
|
||||
|
||||
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.'
|
||||
@ -183,12 +137,10 @@ export default async function createProject(
|
||||
);
|
||||
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:`
|
||||
`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(`cd ${chalk.green(rootPath)} && ${chalk.cyan(scope.packageManager)} install`);
|
||||
console.log();
|
||||
|
||||
stopProcess();
|
||||
@ -205,7 +157,7 @@ export default async function createProject(
|
||||
console.log();
|
||||
console.log(`Your application was created at ${chalk.green(rootPath)}.\n`);
|
||||
|
||||
const cmd = chalk.cyan(scope.useYarn ? 'yarn' : 'npm run');
|
||||
const cmd = chalk.cyan(`${scope.packageManager} run`);
|
||||
|
||||
console.log('Available commands in your project:');
|
||||
console.log();
|
||||
@ -228,19 +180,54 @@ export default async function createProject(
|
||||
console.log(` ${chalk.cyan('cd')} ${rootPath}`);
|
||||
console.log(` ${cmd} develop`);
|
||||
console.log();
|
||||
|
||||
if (scope.runApp !== true) return;
|
||||
|
||||
console.log(`Running your Strapi application.`);
|
||||
|
||||
try {
|
||||
await trackUsage({ event: 'willStartServer', scope });
|
||||
|
||||
await execa('npm', ['run', 'develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: scope.rootPath,
|
||||
env: {
|
||||
FORCE_COLOR: '1',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (typeof error === 'string' || error instanceof Error) {
|
||||
await trackUsage({
|
||||
event: 'didNotStartServer',
|
||||
scope,
|
||||
error,
|
||||
});
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const installArguments = ['install', '--production', '--no-optional'];
|
||||
function runInstall({ rootPath, useYarn }: Scope) {
|
||||
if (useYarn) {
|
||||
// Increase timeout for slow internet connections.
|
||||
installArguments.push('--network-timeout 1000000');
|
||||
const installArguments = ['install'];
|
||||
|
||||
return execa('yarnpkg', installArguments, {
|
||||
cwd: rootPath,
|
||||
stdin: 'ignore',
|
||||
});
|
||||
const installArgumentsMap = {
|
||||
npm: ['--legacy-peer-deps'],
|
||||
yarn: ['--network-timeout 1000000'],
|
||||
pnpm: [],
|
||||
};
|
||||
|
||||
function runInstall({ rootPath, packageManager }: Scope) {
|
||||
const options: execa.Options = {
|
||||
cwd: rootPath,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: 'development',
|
||||
},
|
||||
};
|
||||
|
||||
if (packageManager in installArgumentsMap) {
|
||||
installArguments.push(...(installArgumentsMap[packageManager] ?? []));
|
||||
}
|
||||
|
||||
return execa('npm', installArguments, { cwd: rootPath, stdin: 'ignore' });
|
||||
return execa(packageManager, installArguments, options);
|
||||
}
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
import execa from 'execa';
|
||||
import { trackUsage, captureStderr } from './utils/usage';
|
||||
import defaultConfigs from './utils/db-configs.js';
|
||||
import clientDependencies from './utils/db-client-dependencies.js';
|
||||
import createProject from './create-project';
|
||||
import type { Configuration, Scope } from './types';
|
||||
|
||||
export default async function createQuickStartProject(scope: Scope) {
|
||||
console.log('Creating a quickstart project.');
|
||||
await trackUsage({ event: 'didChooseQuickstart', scope });
|
||||
|
||||
// get default sqlite config
|
||||
const client = 'sqlite';
|
||||
const configuration: Configuration = {
|
||||
client,
|
||||
connection: defaultConfigs[client],
|
||||
dependencies: { ...clientDependencies({ client }), ...scope.additionalsDependencies },
|
||||
};
|
||||
|
||||
await createProject(scope, configuration);
|
||||
|
||||
if (scope.runQuickstartApp !== true) return;
|
||||
|
||||
console.log(`Running your Strapi application.`);
|
||||
|
||||
try {
|
||||
await trackUsage({ event: 'willStartServer', scope });
|
||||
|
||||
await execa('npm', ['run', 'develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: scope.rootPath,
|
||||
env: {
|
||||
FORCE_COLOR: '1',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (typeof error === 'string' || error instanceof Error) {
|
||||
await trackUsage({
|
||||
event: 'didNotStartServer',
|
||||
scope,
|
||||
error,
|
||||
});
|
||||
|
||||
await captureStderr('didNotStartServer', error);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { trackUsage } from './utils/usage';
|
||||
import checkInstallPath from './utils/check-install-path';
|
||||
import createCLIDatabaseProject from './create-cli-db-project';
|
||||
import createCustomizedProject from './create-customized-project';
|
||||
import createQuickStartProject from './create-quickstart-project';
|
||||
|
||||
import type { Scope } from './types';
|
||||
|
||||
export default async (scope: Scope) => {
|
||||
const hasDatabaseConfig = Boolean(scope.database);
|
||||
|
||||
// check rootPath is empty
|
||||
checkInstallPath(scope.rootPath);
|
||||
|
||||
await trackUsage({ event: 'willCreateProject', scope });
|
||||
|
||||
// if database config is provided don't test the connection and create the project directly
|
||||
if (hasDatabaseConfig) {
|
||||
return createCLIDatabaseProject(scope);
|
||||
}
|
||||
|
||||
// if cli quickstart create project with default sqlite options
|
||||
if (scope.quick === true) {
|
||||
return createQuickStartProject(scope);
|
||||
}
|
||||
// create a project with full list of questions
|
||||
return createCustomizedProject(scope);
|
||||
};
|
||||
@ -3,41 +3,37 @@ import { readFileSync } from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import readline from 'node:readline';
|
||||
import crypto from 'crypto';
|
||||
import * as sentry from '@sentry/node';
|
||||
import hasYarn from './utils/has-yarn';
|
||||
import checkRequirements from './utils/check-requirements';
|
||||
import { trackError, captureException } from './utils/usage';
|
||||
import parseDatabaseArguments from './utils/parse-db-arguments';
|
||||
import generateNew from './generate-new';
|
||||
import machineID from './utils/machine-id';
|
||||
import type { Scope, NewOptions } from './types';
|
||||
|
||||
export { default as checkInstallPath } from './utils/check-install-path';
|
||||
import checkRequirements from './utils/check-requirements';
|
||||
import { trackError, trackUsage } from './utils/usage';
|
||||
import machineID from './utils/machine-id';
|
||||
import type { Scope, Options } from './types';
|
||||
|
||||
import checkInstallPath from './utils/check-install-path';
|
||||
import createProject from './create-project';
|
||||
import { addDatabaseDependencies } from './utils/database';
|
||||
|
||||
export type { Options };
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8'));
|
||||
|
||||
export const generateNewApp = (projectDirectory: string, options: Partial<NewOptions>) => {
|
||||
sentry.init({
|
||||
dsn: 'https://841d2b2c9b4d4b43a4cde92794cb705a@sentry.io/1762059',
|
||||
});
|
||||
|
||||
export const generateNewApp = async (options: Options) => {
|
||||
checkRequirements();
|
||||
|
||||
const rootPath = resolve(projectDirectory);
|
||||
const rootPath = await checkInstallPath(options.directory);
|
||||
|
||||
const tmpPath = join(os.tmpdir(), `strapi${crypto.randomBytes(6).toString('hex')}`);
|
||||
|
||||
const useNpm = options.useNpm !== undefined;
|
||||
|
||||
const scope: Scope = {
|
||||
rootPath,
|
||||
name: basename(rootPath),
|
||||
// disable quickstart run app after creation
|
||||
runQuickstartApp: options.run !== false,
|
||||
useTypescript: options.useTypescript,
|
||||
packageManager: options.packageManager,
|
||||
database: options.database,
|
||||
runApp: options.runApp ?? false,
|
||||
isQuickstart: options.isQuickstart,
|
||||
// use pacakge version as strapiVersion (all packages have the same version);
|
||||
strapiVersion: packageJson.version,
|
||||
debug: options.debug !== undefined,
|
||||
quick: options.quickstart,
|
||||
template: options.template,
|
||||
packageJsonStrapi: {
|
||||
template: options.template,
|
||||
@ -47,50 +43,50 @@ export const generateNewApp = (projectDirectory: string, options: Partial<NewOpt
|
||||
docker: process.env.DOCKER === 'true',
|
||||
deviceId: machineID(),
|
||||
tmpPath,
|
||||
// use yarn if available and --use-npm isn't true
|
||||
useYarn: !useNpm && hasYarn(),
|
||||
installDependencies: true,
|
||||
strapiDependencies: [
|
||||
'@strapi/strapi',
|
||||
'@strapi/plugin-users-permissions',
|
||||
'@strapi/plugin-cloud',
|
||||
],
|
||||
additionalsDependencies: {
|
||||
devDependencies: {},
|
||||
dependencies: {
|
||||
'@strapi/strapi': packageJson.version,
|
||||
'@strapi/plugin-users-permissions': packageJson.version,
|
||||
'@strapi/plugin-cloud': packageJson.version,
|
||||
// third party
|
||||
react: '^18.0.0',
|
||||
'react-dom': '^18.0.0',
|
||||
'react-router-dom': '^6.0.0',
|
||||
'styled-components': '^6.0.0',
|
||||
},
|
||||
useTypescript: Boolean(options.typescript),
|
||||
};
|
||||
|
||||
sentry.configureScope(function configureScope(sentryScope) {
|
||||
const tags = {
|
||||
os: os.type(),
|
||||
osPlatform: os.platform(),
|
||||
osArch: os.arch(),
|
||||
osRelease: os.release(),
|
||||
version: scope.strapiVersion,
|
||||
nodeVersion: process.versions.node,
|
||||
docker: scope.docker,
|
||||
if (scope.useTypescript) {
|
||||
scope.devDependencies = {
|
||||
...scope.devDependencies,
|
||||
typescript: '^5',
|
||||
'@types/node': '^20',
|
||||
'@types/react': '^18',
|
||||
'@types/react-dom': '^18',
|
||||
};
|
||||
}
|
||||
|
||||
(Object.keys(tags) as Array<keyof typeof tags>).forEach((tag) => {
|
||||
sentryScope.setTag(tag, tags[tag]);
|
||||
});
|
||||
});
|
||||
addDatabaseDependencies(scope);
|
||||
|
||||
parseDatabaseArguments({ scope, args: options });
|
||||
initCancelCatcher();
|
||||
|
||||
return generateNew(scope).catch((error) => {
|
||||
console.error(error);
|
||||
return captureException(error).then(() => {
|
||||
return trackError({ scope, error }).then(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
try {
|
||||
await trackUsage({ event: 'willCreateProject', scope });
|
||||
|
||||
// create a project with full list of questions
|
||||
return await createProject(scope);
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log(`\n${error.message}\n`);
|
||||
|
||||
return trackError({ scope, error }).then(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function initCancelCatcher() {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
.cache
|
||||
build
|
||||
**/node_modules/**
|
||||
@ -1,27 +0,0 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"browser": false
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": false
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"globals": {
|
||||
"strapi": true
|
||||
},
|
||||
"rules": {
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"no-console": 0,
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"]
|
||||
}
|
||||
}
|
||||
@ -1,28 +1,13 @@
|
||||
import { Scope } from '../../../types';
|
||||
import { join } from 'path';
|
||||
import { kebabCase } from 'lodash';
|
||||
import fse from 'fs-extra';
|
||||
|
||||
import engines from './engines';
|
||||
import type { Scope } from '../../../types';
|
||||
|
||||
type OptsScope = Pick<
|
||||
Scope,
|
||||
'strapiDependencies' | 'additionalsDependencies' | 'strapiVersion' | 'uuid' | 'packageJsonStrapi'
|
||||
>;
|
||||
|
||||
interface Opts extends OptsScope {
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export default (opts: Opts) => {
|
||||
const {
|
||||
strapiDependencies,
|
||||
additionalsDependencies,
|
||||
strapiVersion,
|
||||
projectName,
|
||||
uuid,
|
||||
packageJsonStrapi,
|
||||
} = opts;
|
||||
|
||||
// Finally, return the JSON.
|
||||
return {
|
||||
name: projectName,
|
||||
export default async (scope: Scope) => {
|
||||
const pkg = {
|
||||
name: kebabCase(scope.name),
|
||||
private: true,
|
||||
version: '0.1.0',
|
||||
description: 'A Strapi application',
|
||||
@ -32,22 +17,21 @@ export default (opts: Opts) => {
|
||||
build: 'strapi build',
|
||||
strapi: 'strapi',
|
||||
},
|
||||
devDependencies: {},
|
||||
dependencies: {
|
||||
...strapiDependencies.reduce<Record<string, string>>((acc, key) => {
|
||||
acc[key] = strapiVersion;
|
||||
return acc;
|
||||
}, {}),
|
||||
...additionalsDependencies,
|
||||
},
|
||||
devDependencies: scope.devDependencies,
|
||||
dependencies: scope.dependencies,
|
||||
author: {
|
||||
name: 'A Strapi developer',
|
||||
},
|
||||
strapi: {
|
||||
uuid,
|
||||
...packageJsonStrapi,
|
||||
...scope.packageJsonStrapi,
|
||||
uuid: scope.uuid,
|
||||
},
|
||||
engines,
|
||||
license: 'MIT',
|
||||
};
|
||||
|
||||
// copy templates
|
||||
await fse.writeJSON(join(scope.rootPath, 'package.json'), pkg, {
|
||||
spaces: 2,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,6 +1,20 @@
|
||||
export default () => ({
|
||||
extends: '@strapi/typescript-utils/tsconfigs/admin',
|
||||
|
||||
compilerOptions: {
|
||||
target: 'ESNext',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'Bundler',
|
||||
useDefineForClassFields: true,
|
||||
lib: ['DOM', 'DOM.Iterable', 'ESNext'],
|
||||
allowJs: false,
|
||||
skipLibCheck: true,
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
strict: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
resolveJsonModule: true,
|
||||
noEmit: true,
|
||||
jsx: 'react-jsx',
|
||||
},
|
||||
include: ['../plugins/**/admin/src/**/*', './'],
|
||||
exclude: ['node_modules/', 'build/', 'dist/', '**/*.test.ts'],
|
||||
});
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
export default () => ({
|
||||
extends: '@strapi/typescript-utils/tsconfigs/server',
|
||||
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
moduleResolution: 'Node',
|
||||
lib: ['ES2020'],
|
||||
target: 'ES2019',
|
||||
strict: false,
|
||||
skipLibCheck: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
incremental: true,
|
||||
esModuleInterop: true,
|
||||
resolveJsonModule: true,
|
||||
noEmitOnError: true,
|
||||
noImplicitThis: true,
|
||||
outDir: 'dist',
|
||||
rootDir: '.',
|
||||
},
|
||||
|
||||
include: [
|
||||
// Include root files
|
||||
'./',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
import type { DatabaseInfo } from '../../types';
|
||||
import type { Scope } from '../../types';
|
||||
|
||||
export const createDatabaseConfig = ({ useTypescript }: { useTypescript: boolean }) => {
|
||||
const language = useTypescript ? 'ts' : 'js';
|
||||
@ -13,21 +13,17 @@ export const createDatabaseConfig = ({ useTypescript }: { useTypescript: boolean
|
||||
return compile();
|
||||
};
|
||||
|
||||
export const generateDbEnvVariables = ({
|
||||
connection,
|
||||
client,
|
||||
}: {
|
||||
connection: DatabaseInfo;
|
||||
client: string;
|
||||
}) => {
|
||||
const tmpl = fs.readFileSync(path.join(__dirname, 'database-templates', `${client}.template`));
|
||||
export const generateDbEnvVariables = (scope: Scope) => {
|
||||
const tmpl = fs.readFileSync(
|
||||
path.join(__dirname, 'database-templates', `${scope.database.client}.template`)
|
||||
);
|
||||
const compile = _.template(tmpl.toString());
|
||||
|
||||
return compile({
|
||||
client,
|
||||
client: scope.database.client,
|
||||
connection: {
|
||||
...connection.connection,
|
||||
ssl: connection.connection.ssl || false,
|
||||
...scope.database.connection,
|
||||
ssl: scope.database.connection?.ssl || false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
# Server
|
||||
HOST=0.0.0.0
|
||||
PORT=1337
|
||||
|
||||
# Secrets
|
||||
APP_KEYS=<%= appKeys %>
|
||||
API_TOKEN_SALT=<%= apiTokenSalt %>
|
||||
ADMIN_JWT_SECRET=<%= adminJwtToken %>
|
||||
|
||||
@ -1,55 +1,57 @@
|
||||
export type PackageManager = 'npm' | 'yarn' | 'pnpm';
|
||||
|
||||
export interface Scope {
|
||||
name?: string;
|
||||
rootPath: string;
|
||||
template?: string;
|
||||
strapiVersion: string;
|
||||
strapiDependencies: Array<string>;
|
||||
installDependencies?: boolean;
|
||||
additionalsDependencies: Record<string, string>;
|
||||
devDependencies: Record<string, string>;
|
||||
dependencies: Record<string, string>;
|
||||
docker: boolean;
|
||||
useYarn: boolean;
|
||||
useTypescript: boolean;
|
||||
runQuickstartApp: boolean;
|
||||
quick?: boolean;
|
||||
packageManager: PackageManager;
|
||||
runApp: boolean;
|
||||
isQuickstart?: boolean;
|
||||
uuid?: string;
|
||||
deviceId?: string;
|
||||
dbforce?: boolean;
|
||||
database?: DatabaseInfo;
|
||||
debug?: boolean;
|
||||
database: DatabaseInfo;
|
||||
tmpPath: string;
|
||||
packageJsonStrapi: Record<string, unknown>;
|
||||
useTypescript: boolean;
|
||||
}
|
||||
|
||||
export interface NewOptions {
|
||||
useNpm: boolean;
|
||||
run: boolean;
|
||||
debug: boolean;
|
||||
quickstart: boolean;
|
||||
template: string;
|
||||
starter: string;
|
||||
typescript: boolean;
|
||||
dbforce: boolean;
|
||||
dbssl: string;
|
||||
dbclient: string;
|
||||
dbhost: string;
|
||||
dbport: string;
|
||||
dbname: string;
|
||||
dbusername: string;
|
||||
dbpassword: string;
|
||||
dbfile: string;
|
||||
}
|
||||
export interface Options {
|
||||
directory: string;
|
||||
|
||||
export interface Configuration {
|
||||
client: string;
|
||||
connection: DatabaseInfo;
|
||||
dependencies: Record<string, string>;
|
||||
packageManager: PackageManager;
|
||||
|
||||
runApp?: boolean;
|
||||
template?: string;
|
||||
starter?: string;
|
||||
|
||||
isQuickstart?: boolean;
|
||||
|
||||
useTypescript: boolean;
|
||||
|
||||
database: {
|
||||
client: ClientName;
|
||||
connection?: {
|
||||
host?: string;
|
||||
port?: string;
|
||||
database?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
filename?: string;
|
||||
ssl?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type ClientName = 'mysql' | 'postgres' | 'sqlite';
|
||||
|
||||
export interface DatabaseInfo {
|
||||
client?: string;
|
||||
connection: {
|
||||
client: ClientName;
|
||||
connection?: {
|
||||
host?: string;
|
||||
port?: string;
|
||||
database?: string;
|
||||
@ -58,7 +60,6 @@ export interface DatabaseInfo {
|
||||
filename?: string;
|
||||
ssl?: boolean;
|
||||
};
|
||||
useNullAsDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface PackageInfo {
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
import { resolve } from 'node:path';
|
||||
import chalk from 'chalk';
|
||||
import fse from 'fs-extra';
|
||||
import stopProcess from './stop-process';
|
||||
|
||||
// Checks if the an empty directory exists at rootPath
|
||||
export default async (rootPath: string) => {
|
||||
export default async (directory: string): Promise<string> => {
|
||||
if (!directory) {
|
||||
stopProcess(`⛔️ Please provide a project name.`);
|
||||
}
|
||||
|
||||
const rootPath = resolve(directory);
|
||||
|
||||
if (await fse.pathExists(rootPath)) {
|
||||
const stat = await fse.stat(rootPath);
|
||||
|
||||
@ -24,4 +31,6 @@ export default async (rootPath: string) => {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return rootPath;
|
||||
};
|
||||
|
||||
14
packages/generators/app/src/utils/database.ts
Normal file
14
packages/generators/app/src/utils/database.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import type { Scope } from '../types';
|
||||
|
||||
const sqlClientModule = {
|
||||
mysql: { mysql2: '3.9.4' },
|
||||
postgres: { pg: '8.8.0' },
|
||||
sqlite: { 'better-sqlite3': '9.4.3' },
|
||||
};
|
||||
|
||||
export function addDatabaseDependencies(scope: Scope) {
|
||||
scope.dependencies = {
|
||||
...scope.dependencies,
|
||||
...sqlClientModule[scope.database.client],
|
||||
};
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Client
|
||||
*/
|
||||
export default ({ client }: { client: string }) => {
|
||||
switch (client) {
|
||||
case 'sqlite-legacy':
|
||||
return 'sqlite';
|
||||
default:
|
||||
return client;
|
||||
}
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Default db infos
|
||||
*/
|
||||
export default {
|
||||
sqlite: {
|
||||
connection: {
|
||||
filename: '.tmp/data.db',
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
},
|
||||
postgres: {},
|
||||
mysql: {},
|
||||
};
|
||||
@ -27,8 +27,15 @@ export default async function tryGitInit(rootDir: string) {
|
||||
|
||||
await execa('git', ['init'], { stdio: 'ignore', cwd: rootDir });
|
||||
|
||||
await execa('git', ['add', '.'], { stdio: 'ignore', cwd: rootDir });
|
||||
await execa('git', ['commit', '-m', 'Initial commit from Strapi'], {
|
||||
stdio: 'ignore',
|
||||
cwd: rootDir,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (_) {
|
||||
} catch (e) {
|
||||
console.error('Error while trying to initialize git:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import execa from 'execa';
|
||||
|
||||
export default function hasYarn() {
|
||||
try {
|
||||
const { exitCode } = execa.commandSync('yarn --version', { shell: true });
|
||||
return exitCode === 0;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import stopProcess from './stop-process';
|
||||
import type { Scope, DatabaseInfo, NewOptions } from '../types';
|
||||
|
||||
interface Options {
|
||||
scope: Scope;
|
||||
args: Partial<NewOptions>;
|
||||
}
|
||||
|
||||
const DB_ARGS = ['dbclient', 'dbhost', 'dbport', 'dbname', 'dbusername', 'dbpassword'];
|
||||
|
||||
const VALID_CLIENTS = ['sqlite', 'mysql', 'postgres'];
|
||||
|
||||
export default function parseDatabaseArguments({ scope, args }: Options) {
|
||||
const argKeys = Object.keys(args);
|
||||
const matchingArgs = DB_ARGS.filter((key) => argKeys.includes(key));
|
||||
const missingArgs = DB_ARGS.filter((key) => !argKeys.includes(key));
|
||||
|
||||
if (matchingArgs.length === 0) return;
|
||||
|
||||
if (matchingArgs.length !== DB_ARGS.length && args.dbclient !== 'sqlite') {
|
||||
return stopProcess(`Required database arguments are missing: ${missingArgs.join(', ')}.`);
|
||||
}
|
||||
|
||||
if (!args.dbclient || !VALID_CLIENTS.includes(args.dbclient)) {
|
||||
return stopProcess(
|
||||
`Invalid client ${chalk.yellow(args.dbclient)}. Possible choices: ${VALID_CLIENTS.join(
|
||||
', '
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
|
||||
scope.dbforce = args.dbforce !== undefined;
|
||||
|
||||
const database: DatabaseInfo = {
|
||||
client: args.dbclient,
|
||||
connection: {
|
||||
host: args.dbhost,
|
||||
port: args.dbport,
|
||||
database: args.dbname,
|
||||
username: args.dbusername,
|
||||
password: args.dbpassword,
|
||||
filename: args.dbfile,
|
||||
},
|
||||
};
|
||||
|
||||
if (args.dbssl !== undefined) {
|
||||
database.connection.ssl = args.dbssl === 'true';
|
||||
}
|
||||
|
||||
scope.database = database;
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import os from 'os';
|
||||
import _ from 'lodash';
|
||||
import * as sentry from '@sentry/node';
|
||||
import { Scope, StderrError, isStderrError } from '../types';
|
||||
import { Scope, StderrError } from '../types';
|
||||
|
||||
type TrackError = Error | string | StderrError;
|
||||
|
||||
@ -12,43 +11,6 @@ function addPackageJsonStrapiMetadata(metadata: Record<string, unknown>, scope:
|
||||
return _.defaults(metadata, packageJsonStrapi);
|
||||
}
|
||||
|
||||
export async function captureException(error: Error) {
|
||||
try {
|
||||
sentry.captureException(error);
|
||||
await sentry.flush();
|
||||
} catch (err) {
|
||||
/** ignore errors */
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
async function captureError(message: string) {
|
||||
try {
|
||||
sentry.captureMessage(message, 'error');
|
||||
await sentry.flush();
|
||||
} catch (err) {
|
||||
/** ignore errors */
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export function captureStderr(name: string, error: unknown) {
|
||||
if (isStderrError(error) && error.stderr.trim() !== '') {
|
||||
error.stderr
|
||||
.trim()
|
||||
.split('\n')
|
||||
.forEach((line) => {
|
||||
sentry.addBreadcrumb({
|
||||
category: 'stderr',
|
||||
message: line,
|
||||
level: 'error',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return captureError(name);
|
||||
}
|
||||
|
||||
const getProperties = (scope: Scope, error?: TrackError) => {
|
||||
const eventProperties = {
|
||||
error: typeof error === 'string' ? error : error && error.message,
|
||||
@ -63,11 +25,11 @@ const getProperties = (scope: Scope, error?: TrackError) => {
|
||||
const groupProperties = {
|
||||
version: scope.strapiVersion,
|
||||
docker: scope.docker,
|
||||
useYarn: scope.useYarn,
|
||||
// useYarn: scope.useYarn,
|
||||
useTypescriptOnServer: scope.useTypescript,
|
||||
useTypescriptOnAdmin: scope.useTypescript,
|
||||
isHostedOnStrapiCloud: process.env.STRAPI_HOSTING === 'strapi.cloud',
|
||||
noRun: (scope.runQuickstartApp !== true).toString(),
|
||||
noRun: (scope.runApp !== true).toString(),
|
||||
projectId: scope.uuid,
|
||||
};
|
||||
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const adminTsConfig = require('../../tsconfigs/admin.json');
|
||||
|
||||
module.exports = async (dest) => {
|
||||
const tsConfig = {
|
||||
...adminTsConfig,
|
||||
include: ['../../../src/admin/*', '../../../src/**/**/admin/src/*'],
|
||||
exclude: ['node_modules', '**/*.test.js', '*.js'],
|
||||
};
|
||||
|
||||
const filePath = path.join(dest, 'admin', 'src', 'tsconfig.json');
|
||||
|
||||
try {
|
||||
await fs.ensureFile(filePath);
|
||||
|
||||
await fs.writeJSON(filePath, tsConfig, { spaces: 2 });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const createTSConfigFile = require('./create-tsconfig-file');
|
||||
|
||||
module.exports = { createTSConfigFile };
|
||||
@ -2,15 +2,12 @@
|
||||
|
||||
const compile = require('./compile');
|
||||
const compilers = require('./compilers');
|
||||
const admin = require('./admin');
|
||||
const utils = require('./utils');
|
||||
const generators = require('./generators');
|
||||
|
||||
module.exports = {
|
||||
compile,
|
||||
compilers,
|
||||
admin,
|
||||
generators,
|
||||
|
||||
...utils,
|
||||
};
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { rimraf } = require('rimraf');
|
||||
const execa = require('execa');
|
||||
const generateNew = require('../../packages/generators/app/dist/generate-new');
|
||||
const createProject = require('../../packages/generators/app/dist/create-project');
|
||||
|
||||
/**
|
||||
* Deletes a test app
|
||||
@ -21,30 +21,27 @@ const cleanTestApp = async (appPath) => {
|
||||
* @param {database} options.database - Arguments to create the testApp with the provided database params
|
||||
*/
|
||||
const generateTestApp = async ({ appPath, database, template, link = false }) => {
|
||||
const pkg = require(path.resolve(__dirname, '../../packages/core/strapi/package.json'));
|
||||
|
||||
const scope = {
|
||||
database,
|
||||
rootPath: path.resolve(appPath),
|
||||
name: path.basename(appPath),
|
||||
packageManager: 'yarn',
|
||||
// disable quickstart run app after creation
|
||||
runQuickstartApp: false,
|
||||
runApp: false,
|
||||
// use package version as strapiVersion (all packages have the same version);
|
||||
strapiVersion: require(path.resolve(__dirname, '../../packages/core/strapi/package.json'))
|
||||
.version,
|
||||
debug: false,
|
||||
quick: false,
|
||||
strapiVersion: pkg.version,
|
||||
isQuickstart: false,
|
||||
uuid: undefined,
|
||||
deviceId: null,
|
||||
// use yarn if available and --use-npm isn't true
|
||||
useYarn: true,
|
||||
installDependencies: false,
|
||||
strapiDependencies: [
|
||||
'@strapi/strapi',
|
||||
'@strapi/plugin-users-permissions',
|
||||
'@strapi/plugin-graphql',
|
||||
'@strapi/plugin-documentation',
|
||||
'@strapi/plugin-cloud',
|
||||
],
|
||||
additionalsDependencies: {
|
||||
dependencies: {
|
||||
'@strapi/strapi': pkg.version,
|
||||
'@strapi/plugin-users-permissions': pkg.version,
|
||||
'@strapi/plugin-graphql': pkg.version,
|
||||
'@strapi/plugin-documentation': pkg.version,
|
||||
'@strapi/plugin-cloud': pkg.version,
|
||||
react: '18.2.0',
|
||||
'react-dom': '18.2.0',
|
||||
'react-router-dom': '^6.0.0',
|
||||
@ -53,7 +50,7 @@ const generateTestApp = async ({ appPath, database, template, link = false }) =>
|
||||
template: template ? path.resolve(template) : template,
|
||||
};
|
||||
|
||||
await generateNew(scope);
|
||||
await createProject(scope);
|
||||
if (link) {
|
||||
await linkPackages(scope);
|
||||
}
|
||||
|
||||
33
yarn.lock
33
yarn.lock
@ -7842,9 +7842,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@strapi/generate-new@workspace:packages/generators/app"
|
||||
dependencies:
|
||||
"@sentry/node": "npm:7.112.2"
|
||||
"@strapi/pack-up": "npm:5.0.0"
|
||||
"@types/fs-extra": "npm:11.0.4"
|
||||
"@types/inquirer": "npm:8.2.5"
|
||||
chalk: "npm:^4.1.2"
|
||||
copyfiles: "npm:2.4.1"
|
||||
execa: "npm:5.1.1"
|
||||
@ -9401,6 +9401,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/inquirer@npm:8.2.5":
|
||||
version: 8.2.5
|
||||
resolution: "@types/inquirer@npm:8.2.5"
|
||||
dependencies:
|
||||
"@types/through": "npm:*"
|
||||
checksum: ceb0fde9fd128061085f60265b2dd9545040e11aa29ded52641a302979c961dcc93988348a73444a404e9849805e1c44b53870fad787dacc3fa25f5176dc94a2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/inquirer@npm:^6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "@types/inquirer@npm:6.5.0"
|
||||
@ -13789,6 +13798,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@strapi/generate-new": "npm:5.0.0-beta.9"
|
||||
"@strapi/pack-up": "npm:5.0.0"
|
||||
"@types/inquirer": "npm:8.2.5"
|
||||
commander: "npm:8.3.0"
|
||||
eslint-config-custom: "npm:5.0.0-beta.9"
|
||||
inquirer: "npm:8.2.5"
|
||||
@ -13798,27 +13808,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"create-strapi-starter@workspace:packages/cli/create-strapi-starter":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "create-strapi-starter@workspace:packages/cli/create-strapi-starter"
|
||||
dependencies:
|
||||
"@strapi/generate-new": "npm:5.0.0-beta.9"
|
||||
"@strapi/pack-up": "npm:5.0.0"
|
||||
"@types/fs-extra": "npm:11.0.4"
|
||||
chalk: "npm:4.1.2"
|
||||
ci-info: "npm:4.0.0"
|
||||
commander: "npm:8.3.0"
|
||||
eslint-config-custom: "npm:5.0.0-beta.9"
|
||||
execa: "npm:5.1.1"
|
||||
fs-extra: "npm:11.2.0"
|
||||
inquirer: "npm:8.2.5"
|
||||
ora: "npm:5.4.1"
|
||||
tsconfig: "npm:5.0.0-beta.9"
|
||||
bin:
|
||||
create-strapi-starter: ./bin/index.js
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"create-strapi@workspace:packages/cli/create-strapi":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "create-strapi@workspace:packages/cli/create-strapi"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user