@akemona-org/create-strapi-starter 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015-present Strapi Solutions SAS
2
+
3
+ Portions of the Strapi software are licensed as follows:
4
+
5
+ * All software that resides under an "ee/" directory (the “EE Software”), if that directory exists, is licensed under the license defined in "ee/LICENSE".
6
+
7
+ * All software outside of the above-mentioned directories or restrictions above is available under the "MIT Expat" license as set forth below.
8
+
9
+ MIT Expat License
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ 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
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Create strapi starter
2
+
3
+ This package includes the `create-strapi-starter` CLI to simplify creating a Strapi project using starters and templates
4
+
5
+ ## How to use
6
+
7
+ ### Quick usage (recommended)
8
+
9
+ Using yarn create command
10
+
11
+ ```
12
+ yarn create strapi-starter my-project starter-url
13
+ ```
14
+
15
+ Using npx
16
+
17
+ ```
18
+ npx create-strapi-starter my-project starter-url
19
+ ```
20
+
21
+ ### Manual install
22
+
23
+ Using yarn
24
+
25
+ ```
26
+ yarn global add create-strapi-app
27
+ create-strapi-starter my-project starter-url
28
+ ```
29
+
30
+ Using npm
31
+
32
+ ```
33
+ npm install -g create-strapi-app
34
+ create-strapi-starter my-project starter-url
35
+ ```
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ const commander = require('commander');
4
+
5
+ const packageJson = require('./package.json');
6
+ const buildStarter = require('./utils/build-starter');
7
+ const promptUser = require('./utils/prompt-user');
8
+
9
+ const program = new commander.Command(packageJson.name);
10
+
11
+ program
12
+ .version(packageJson.version)
13
+ .arguments('[directory], [starterurl]')
14
+ .option('--use-npm', 'Force usage of npm instead of yarn to create the project')
15
+ .option('--debug', 'Display database connection error')
16
+ .option('--quickstart', 'Quickstart app creation')
17
+ .option('--dbclient <dbclient>', 'Database client')
18
+ .option('--dbhost <dbhost>', 'Database host')
19
+ .option('--dbsrv <dbsrv>', 'Database srv')
20
+ .option('--dbport <dbport>', 'Database port')
21
+ .option('--dbname <dbname>', 'Database name')
22
+ .option('--dbusername <dbusername>', 'Database username')
23
+ .option('--dbpassword <dbpassword>', 'Database password')
24
+ .option('--dbssl <dbssl>', 'Database SSL')
25
+ .option('--dbauth <dbauth>', 'Authentication Database')
26
+ .option('--dbfile <dbfile>', 'Database file path for sqlite')
27
+ .option('--dbforce', 'Overwrite database content if any')
28
+ .description(
29
+ 'Create a fullstack monorepo application using the strapi backend template specified in the provided starter'
30
+ )
31
+ .action((directory, starterUrl, programArgs) => {
32
+ const projectArgs = { projectName: directory, starterUrl };
33
+
34
+ initProject(projectArgs, programArgs);
35
+ });
36
+
37
+ function generateApp(projectArgs, programArgs) {
38
+ if (!projectArgs.projectName || !projectArgs.starterUrl) {
39
+ console.error(
40
+ 'Please specify the <directory> and <starterurl> of your project when using --quickstart'
41
+ );
42
+ // eslint-disable-next-line no-process-exit
43
+ process.exit(1);
44
+ }
45
+
46
+ return buildStarter(projectArgs, programArgs);
47
+ }
48
+
49
+ async function initProject(projectArgs, program) {
50
+ const { projectName, starterUrl } = projectArgs;
51
+ if (program.quickstart) {
52
+ return generateApp(projectArgs, program);
53
+ }
54
+
55
+ const prompt = await promptUser(projectName, starterUrl);
56
+
57
+ const promptProjectArgs = {
58
+ projectName: prompt.directory || projectName,
59
+ starterUrl: prompt.starter || starterUrl,
60
+ };
61
+
62
+ const programArgs = {
63
+ ...program,
64
+ quickstart: prompt.quick,
65
+ };
66
+
67
+ return generateApp(promptProjectArgs, programArgs);
68
+ }
69
+
70
+ try {
71
+ program.parse(process.argv);
72
+ } catch (err) {
73
+ if (err.exitCode && err.exitCode != 0) {
74
+ program.outputHelp();
75
+ }
76
+ }
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ require('./create-strapi-starter');
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@akemona-org/create-strapi-starter",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "3.7.0",
7
+ "description": "Generate a new Strapi application.",
8
+ "license": "SEE LICENSE IN LICENSE",
9
+ "homepage": "https://strapi.akemona.com",
10
+ "keywords": [
11
+ "create-strapi-starter",
12
+ "create",
13
+ "new",
14
+ "generate",
15
+ "strapi"
16
+ ],
17
+ "main": "./index.js",
18
+ "bin": {
19
+ "create-strapi-starter": "./index.js"
20
+ },
21
+ "dependencies": {
22
+ "@akemona-org/strapi-generate-new": "3.7.0",
23
+ "chalk": "4.1.1",
24
+ "ci-info": "3.1.1",
25
+ "commander": "7.1.0",
26
+ "execa": "5.0.0",
27
+ "fs-extra": "9.1.0",
28
+ "git-url-parse": "13.1.0",
29
+ "inquirer": "8.1.0",
30
+ "js-yaml": "4.1.0",
31
+ "node-fetch": "^2.6.1",
32
+ "ora": "5.4.0",
33
+ "tar": "6.1.9"
34
+ },
35
+ "scripts": {
36
+ "test": "echo \"no tests yet\""
37
+ },
38
+ "author": {
39
+ "email": "strapi@akemona.com",
40
+ "name": "Akemona team",
41
+ "url": "https://strapi.akemona.com"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git://github.com/akemona/strapi.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/akemona/strapi/issues"
49
+ },
50
+ "engines": {
51
+ "node": ">=10.16.0 <=14.x.x",
52
+ "npm": ">=6.0.0"
53
+ },
54
+ "gitHead": "129a8d6191b55810fd66448dcc47fee829df986c"
55
+ }
@@ -0,0 +1,114 @@
1
+ ############################
2
+ # OS X
3
+ ############################
4
+
5
+ .DS_Store
6
+ .AppleDouble
7
+ .LSOverride
8
+ Icon
9
+ .Spotlight-V100
10
+ .Trashes
11
+ ._*
12
+
13
+
14
+ ############################
15
+ # Linux
16
+ ############################
17
+
18
+ *~
19
+
20
+
21
+ ############################
22
+ # Windows
23
+ ############################
24
+
25
+ Thumbs.db
26
+ ehthumbs.db
27
+ Desktop.ini
28
+ $RECYCLE.BIN/
29
+ *.cab
30
+ *.msi
31
+ *.msm
32
+ *.msp
33
+
34
+
35
+ ############################
36
+ # Packages
37
+ ############################
38
+
39
+ *.7z
40
+ *.csv
41
+ *.dat
42
+ *.dmg
43
+ *.gz
44
+ *.iso
45
+ *.jar
46
+ *.rar
47
+ *.tar
48
+ *.zip
49
+ *.com
50
+ *.class
51
+ *.dll
52
+ *.exe
53
+ *.o
54
+ *.seed
55
+ *.so
56
+ *.swo
57
+ *.swp
58
+ *.swn
59
+ *.swm
60
+ *.out
61
+ *.pid
62
+
63
+
64
+ ############################
65
+ # Logs and databases
66
+ ############################
67
+
68
+ .tmp
69
+ *.log
70
+ *.sql
71
+ *.sqlite
72
+ *.sqlite3
73
+
74
+
75
+ ############################
76
+ # Misc.
77
+ ############################
78
+
79
+ *#
80
+ ssl
81
+ .idea
82
+ nbproject
83
+ public/uploads/*
84
+ !public/uploads/.gitkeep
85
+
86
+ ############################
87
+ # Node.js
88
+ ############################
89
+
90
+ lib-cov
91
+ lcov.info
92
+ pids
93
+ logs
94
+ results
95
+ node_modules
96
+ .node_history
97
+
98
+ ############################
99
+ # Tests
100
+ ############################
101
+
102
+ testApp
103
+ coverage
104
+
105
+ ############################
106
+ # Strapi
107
+ ############################
108
+
109
+ .env
110
+ license.txt
111
+ exports
112
+ *.cache
113
+ build
114
+ .strapi-updater.json
@@ -0,0 +1,169 @@
1
+ 'use strict';
2
+
3
+ const { resolve, join, basename } = require('path');
4
+ const os = require('os');
5
+ const fse = require('fs-extra');
6
+
7
+ const ora = require('ora');
8
+ const ciEnv = require('ci-info');
9
+ const chalk = require('chalk');
10
+
11
+ const generateNewApp = require('@akemona-org/strapi-generate-new');
12
+
13
+ const hasYarn = require('./has-yarn');
14
+ const { runInstall, runApp, initGit } = require('./child-process');
15
+ const { getRepoInfo, downloadGitHubRepo } = require('./fetch-github');
16
+ const logger = require('./logger');
17
+ const stopProcess = require('./stop-process');
18
+
19
+ /**
20
+ * @param {string} - filePath Path to starter.json file
21
+ */
22
+ function readStarterJson(filePath, starterUrl) {
23
+ try {
24
+ const data = fse.readFileSync(filePath);
25
+ return JSON.parse(data);
26
+ } catch (err) {
27
+ stopProcess(`Could not find ${chalk.yellow('starter.json')} in ${chalk.yellow(starterUrl)}`);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @param {string} rootPath - Path to the project directory
33
+ * @param {string} projectName - Name of the project
34
+ */
35
+ async function initPackageJson(rootPath, projectName) {
36
+ const packageManager = hasYarn() ? 'yarn --cwd' : 'npm run --prefix';
37
+
38
+ try {
39
+ await fse.writeJson(
40
+ join(rootPath, 'package.json'),
41
+ {
42
+ name: projectName,
43
+ private: true,
44
+ version: '0.0.0',
45
+ scripts: {
46
+ 'develop:backend': `${packageManager} backend develop`,
47
+ 'develop:frontend': `wait-on http://localhost:1337/admin && ${packageManager} frontend develop`,
48
+ develop: 'cross-env FORCE_COLOR=1 npm-run-all -l -p develop:*',
49
+ },
50
+ devDependencies: {
51
+ 'npm-run-all': '4.1.5',
52
+ 'wait-on': '5.2.1',
53
+ 'cross-env': '7.0.3',
54
+ },
55
+ },
56
+ {
57
+ spaces: 2,
58
+ }
59
+ );
60
+ } catch (err) {
61
+ stopProcess(`Failed to create ${chalk.yellow(`package.json`)} in ${chalk.yellow(rootPath)}`);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @param {string} path - The directory path for install
67
+ */
68
+ async function installWithLogs(path) {
69
+ const installPrefix = chalk.yellow('Installing dependencies:');
70
+ const loader = ora(installPrefix).start();
71
+ const logInstall = (chunk = '') => {
72
+ loader.text = `${installPrefix} ${chunk.toString().split('\n').join(' ')}`;
73
+ };
74
+
75
+ const runner = runInstall(path);
76
+ runner.stdout.on('data', logInstall);
77
+ runner.stderr.on('data', logInstall);
78
+
79
+ await runner;
80
+
81
+ loader.stop();
82
+ console.log(`Dependencies installed ${chalk.green('successfully')}.`);
83
+ }
84
+
85
+ /**
86
+ * @param {Object} projectArgs - The arguments for create a project
87
+ * @param {string|null} projectArgs.projectName - The name/path of project
88
+ * @param {string|null} projectArgs.starterUrl - The GitHub repo of the starter
89
+ * @param {Object} program - Commands for generating new application
90
+ */
91
+ module.exports = async function buildStarter(programArgs, program) {
92
+ let { projectName, starterUrl } = programArgs;
93
+
94
+ // Fetch repo info
95
+ const repoInfo = await getRepoInfo(starterUrl);
96
+ const { fullName } = repoInfo;
97
+
98
+ // Create temporary directory for starter
99
+ const tmpDir = await fse.mkdtemp(join(os.tmpdir(), 'strapi-'));
100
+
101
+ // Download repo inside temporary directory
102
+ await downloadGitHubRepo(repoInfo, tmpDir);
103
+
104
+ const starterJson = readStarterJson(join(tmpDir, 'starter.json'), starterUrl);
105
+
106
+ // Project directory
107
+ const rootPath = resolve(projectName);
108
+ const projectBasename = basename(rootPath);
109
+
110
+ try {
111
+ await fse.ensureDir(rootPath);
112
+ } catch (error) {
113
+ stopProcess(`Failed to create ${chalk.yellow(rootPath)}: ${error.message}`);
114
+ }
115
+
116
+ // Copy the downloaded frontend folder to the project folder
117
+ const frontendPath = join(rootPath, 'frontend');
118
+
119
+ const starterDir = (await fse.pathExists(join(tmpDir, 'starter'))) ? 'starter' : 'frontend';
120
+
121
+ try {
122
+ await fse.copy(join(tmpDir, starterDir), frontendPath, {
123
+ overwrite: true,
124
+ recursive: true,
125
+ });
126
+ } catch (error) {
127
+ stopProcess(`Failed to create ${chalk.yellow(frontendPath)}: ${error.message}`);
128
+ }
129
+
130
+ // Delete temporary directory
131
+ await fse.remove(tmpDir);
132
+
133
+ const fullUrl = `https://github.com/${fullName}`;
134
+ // Set command options for Strapi app
135
+ const generateStrapiAppOptions = {
136
+ ...program,
137
+ starter: fullUrl,
138
+ template: starterJson.template,
139
+ run: false,
140
+ };
141
+
142
+ // Create strapi app using the template
143
+ await generateNewApp(join(rootPath, 'backend'), generateStrapiAppOptions);
144
+
145
+ // Install frontend dependencies
146
+ console.log(`Creating Strapi starter frontend at ${chalk.green(frontendPath)}.`);
147
+ console.log(`Installing ${chalk.yellow(fullName)} starter`);
148
+ await installWithLogs(frontendPath);
149
+
150
+ // Setup monorepo
151
+ initPackageJson(rootPath, projectBasename);
152
+
153
+ // Add gitignore
154
+ try {
155
+ const gitignore = join(__dirname, '..', 'resources', 'gitignore');
156
+ await fse.copy(gitignore, join(rootPath, '.gitignore'));
157
+ } catch (err) {
158
+ logger.warn(`Failed to create file: ${chalk.yellow('.gitignore')}`);
159
+ }
160
+
161
+ await installWithLogs(rootPath);
162
+
163
+ if (!ciEnv.isCI) {
164
+ await initGit(rootPath);
165
+ }
166
+
167
+ console.log(chalk.green('Starting the app'));
168
+ await runApp(rootPath);
169
+ };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ const { execSync } = require('child_process');
4
+ const execa = require('execa');
5
+ const hasYarn = require('./has-yarn');
6
+ const logger = require('./logger');
7
+
8
+ /**
9
+ * @param {string} path Path to directory (frontend, backend)
10
+ */
11
+ function runInstall(path) {
12
+ if (hasYarn()) {
13
+ return execa('yarn', ['install'], {
14
+ cwd: path,
15
+ stdin: 'ignore',
16
+ });
17
+ }
18
+
19
+ return execa('npm', ['install'], { cwd: path, stdin: 'ignore' });
20
+ }
21
+
22
+ function runApp(rootPath) {
23
+ if (hasYarn()) {
24
+ return execa('yarn', ['develop'], {
25
+ stdio: 'inherit',
26
+ cwd: rootPath,
27
+ });
28
+ } else {
29
+ return execa('npm', ['run', 'develop'], {
30
+ stdio: 'inherit',
31
+ cwd: rootPath,
32
+ });
33
+ }
34
+ }
35
+
36
+ async function initGit(rootPath) {
37
+ try {
38
+ await execa('git', ['init'], {
39
+ cwd: rootPath,
40
+ });
41
+ } catch (err) {
42
+ logger.warn(`Could not initialize a git repository`);
43
+ }
44
+
45
+ try {
46
+ await execa(`git`, [`add`, `-A`], { cwd: rootPath });
47
+
48
+ execSync(`git commit -m "Create Strapi starter project"`, {
49
+ cwd: rootPath,
50
+ });
51
+ } catch (err) {
52
+ logger.warn(`Could not create initial git commit`);
53
+ }
54
+ }
55
+
56
+ module.exports = {
57
+ runInstall,
58
+ runApp,
59
+ initGit,
60
+ };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ const tar = require('tar');
4
+ const fetch = require('node-fetch');
5
+ const parseGitUrl = require('git-url-parse');
6
+ const chalk = require('chalk');
7
+
8
+ const stopProcess = require('./stop-process');
9
+
10
+ function parseShorthand(starter) {
11
+ // Determine if it is comes from another owner
12
+ if (starter.includes('/')) {
13
+ const [owner, partialName] = starter.split('/');
14
+ const name = `strapi-starter-${partialName}`;
15
+ return {
16
+ name,
17
+ fullName: `${owner}/${name}`,
18
+ };
19
+ }
20
+
21
+ const name = `strapi-starter-${starter}`;
22
+ return {
23
+ name,
24
+ fullName: `strapi/${name}`,
25
+ };
26
+ }
27
+
28
+ /**
29
+ * @param {string} repo The full name of the repository.
30
+ */
31
+ async function getDefaultBranch(repo) {
32
+ const response = await fetch(`https://api.github.com/repos/${repo}`);
33
+
34
+ if (!response.ok) {
35
+ stopProcess(
36
+ `Could not find the starter information for ${chalk.yellow(
37
+ repo
38
+ )}. Make sure it is publicly accessible on github.`
39
+ );
40
+ }
41
+
42
+ const { default_branch } = await response.json();
43
+ return default_branch;
44
+ }
45
+
46
+ /**
47
+ * @param {string} starter GitHub URL or shorthand to a starter project.
48
+ */
49
+ async function getRepoInfo(starter) {
50
+ const { name, full_name: fullName, ref, filepath, protocols, source } = parseGitUrl(starter);
51
+
52
+ if (protocols.length === 0) {
53
+ const repoInfo = parseShorthand(starter);
54
+ return {
55
+ ...repoInfo,
56
+ branch: await getDefaultBranch(repoInfo.fullName),
57
+ usedShorthand: true,
58
+ };
59
+ }
60
+
61
+ if (source !== 'github.com') {
62
+ stopProcess(`GitHub URL not found for: ${chalk.yellow(starter)}.`);
63
+ }
64
+
65
+ let branch;
66
+ if (ref) {
67
+ // Append the filepath to the parsed ref since a branch name could contain '/'
68
+ // If so, the rest of the branch name will be considered 'filepath' by 'parseGitUrl'
69
+ branch = filepath ? `${ref}/${filepath}` : ref;
70
+ } else {
71
+ branch = await getDefaultBranch(fullName);
72
+ }
73
+
74
+ return { name, fullName, branch };
75
+ }
76
+
77
+ /**
78
+ * @param {string} repoInfo GitHub repository information (full name, branch...).
79
+ * @param {string} tmpDir Path to the destination temporary directory.
80
+ */
81
+ async function downloadGitHubRepo(repoInfo, tmpDir) {
82
+ const { fullName, branch, usedShorthand } = repoInfo;
83
+
84
+ const codeload = `https://codeload.github.com/${fullName}/tar.gz/${branch}`;
85
+ const response = await fetch(codeload);
86
+
87
+ if (!response.ok) {
88
+ const message = usedShorthand ? `using the shorthand` : `using the url`;
89
+ stopProcess(`Could not download the repository ${message}: ${chalk.yellow(fullName)}.`);
90
+ }
91
+
92
+ await new Promise(resolve => {
93
+ response.body.pipe(tar.extract({ strip: 1, cwd: tmpDir })).on('close', resolve);
94
+ });
95
+ }
96
+
97
+ module.exports = { getRepoInfo, downloadGitHubRepo };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const execa = require('execa');
4
+
5
+ module.exports = function hasYarn() {
6
+ try {
7
+ const { exitCode } = execa.sync('yarn --version', { shell: true });
8
+
9
+ if (exitCode === 0) return true;
10
+ return false;
11
+ } catch (err) {
12
+ return false;
13
+ }
14
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+
5
+ module.exports = {
6
+ error(message) {
7
+ console.error(`${chalk.red('error')}: ${message}`);
8
+ },
9
+
10
+ warn(message) {
11
+ console.log(`${chalk.yellow('warning')}: ${message}`);
12
+ },
13
+
14
+ info(message) {
15
+ console.log(`${chalk.blue('info')}: ${message}`);
16
+ },
17
+ };
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ const inquirer = require('inquirer');
4
+ const fetch = require('node-fetch');
5
+ const yaml = require('js-yaml');
6
+
7
+ /**
8
+ * @param {string|null} projectName - The name/path of project
9
+ * @param {string|null} starterUrl - The GitHub repo of the starter
10
+ * @returns Object containting prompt answers
11
+ */
12
+ module.exports = async function promptUser(projectName, starter) {
13
+ const mainQuestions = [
14
+ {
15
+ type: 'input',
16
+ default: 'my-strapi-project',
17
+ name: 'directory',
18
+ message: 'What would you like to name your project?',
19
+ when: !projectName,
20
+ },
21
+ {
22
+ type: 'list',
23
+ name: 'quick',
24
+ message: 'Choose your installation type',
25
+ choices: [
26
+ {
27
+ name: 'Quickstart (recommended)',
28
+ value: true,
29
+ },
30
+ {
31
+ name: 'Custom (manual settings)',
32
+ value: false,
33
+ },
34
+ ],
35
+ },
36
+ ];
37
+
38
+ const [mainResponse, starterQuestion] = await Promise.all([
39
+ inquirer.prompt(mainQuestions),
40
+ getStarterQuestion(),
41
+ ]);
42
+
43
+ const starterResponse = await inquirer.prompt({
44
+ name: 'starter',
45
+ when: !starter,
46
+ ...starterQuestion,
47
+ });
48
+
49
+ return { ...mainResponse, ...starterResponse };
50
+ };
51
+
52
+ /**
53
+ *
54
+ * @returns Prompt question object
55
+ */
56
+ async function getStarterQuestion() {
57
+ const content = await getStarterData();
58
+
59
+ // Fallback to manual input when fetch fails
60
+ if (!content) {
61
+ return {
62
+ type: 'input',
63
+ message: 'Please provide the GitHub URL for the starter you would like to use:',
64
+ };
65
+ }
66
+
67
+ const choices = content.map(option => {
68
+ const name = option.title.replace('Starter', '');
69
+
70
+ return {
71
+ name,
72
+ value: `https://github.com/${option.repo}`,
73
+ };
74
+ });
75
+
76
+ return {
77
+ type: 'list',
78
+ message:
79
+ 'Which starter would you like to use? (Starters are fullstack Strapi applications designed for a specific use case)',
80
+ pageSize: choices.length,
81
+ choices,
82
+ };
83
+ }
84
+
85
+ /**
86
+ *
87
+ * @returns JSON starter data
88
+ */
89
+ async function getStarterData() {
90
+ const response = await fetch(
91
+ `https://api.github.com/repos/strapi/community-content/contents/starters/starters.yml`
92
+ );
93
+
94
+ if (!response.ok) {
95
+ return null;
96
+ }
97
+
98
+ const { content } = await response.json();
99
+ const buff = Buffer.from(content, 'base64');
100
+ const stringified = buff.toString('utf-8');
101
+
102
+ return yaml.load(stringified);
103
+ }
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+ const logger = require('./logger');
3
+
4
+ module.exports = function stopProcess(message) {
5
+ if (message) logger.error(message);
6
+ process.exit(1);
7
+ };