mirror of
https://github.com/strapi/strapi.git
synced 2025-12-26 14:44:31 +00:00
Refactor strapi-generate-new to be async
This commit is contained in:
parent
b1eccc05a7
commit
9fdf6be746
@ -1 +0,0 @@
|
||||
package-lock=false
|
||||
@ -1,52 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = scope => {
|
||||
// Production/Staging Template
|
||||
if (['production', 'staging'].includes(scope.keyPath.split('/')[2])) {
|
||||
// All available settings (bookshelf and mongoose)
|
||||
const settingsBase = {
|
||||
client: scope.client.database,
|
||||
host: '${process.env.DATABASE_HOST || \'127.0.0.1\'}',
|
||||
port: '${process.env.DATABASE_PORT || 27017}',
|
||||
srv: '${process.env.DATABASE_SRV || false}',
|
||||
database: '${process.env.DATABASE_NAME || \'strapi\'}',
|
||||
username: '${process.env.DATABASE_USERNAME || \'\'}',
|
||||
password: '${process.env.DATABASE_PASSWORD || \'\'}',
|
||||
ssl: '${process.env.DATABASE_SSL || false}'
|
||||
};
|
||||
|
||||
// Apply only settings set during the configuration
|
||||
Object.keys(scope.database.settings).forEach((key) => {
|
||||
scope.database.settings[key] = settingsBase[key];
|
||||
});
|
||||
|
||||
// All available options (bookshelf and mongoose)
|
||||
const optionsBase = {
|
||||
ssl: '${process.env.DATABASE_SSL || false}',
|
||||
authenticationDatabase: '${process.env.DATABASE_AUTHENTICATION_DATABASE || \'\'}'
|
||||
};
|
||||
|
||||
// Apply only options set during the configuration
|
||||
Object.keys(scope.database.options).forEach((key) => {
|
||||
scope.database.options[key] = optionsBase[key];
|
||||
});
|
||||
|
||||
return {
|
||||
defaultConnection: 'default',
|
||||
connections: {
|
||||
default: {
|
||||
connector: scope.client.connector,
|
||||
settings: scope.database.settings,
|
||||
options: scope.database.options
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
defaultConnection: 'default',
|
||||
connections: {
|
||||
default: scope.database
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -1,87 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
|
||||
/**
|
||||
* Expose main package JSON of the application
|
||||
* with basic info, dependencies, etc.
|
||||
*/
|
||||
|
||||
module.exports = scope => {
|
||||
const cliPkg = scope.strapiPackageJSON || {};
|
||||
|
||||
// Let us install additional dependencies on a specific version.
|
||||
// Ex: it allows us to install the right version of knex.
|
||||
const additionalsDependencies = _.isArray(scope.additionalsDependencies) ?
|
||||
scope.additionalsDependencies.reduce((acc, current) => {
|
||||
const pkg = current.split('@');
|
||||
const name = pkg[0];
|
||||
const version = pkg[1] || 'latest';
|
||||
|
||||
acc[name] = name.indexOf('strapi') !== -1 ? getDependencyVersion(cliPkg, 'strapi') : version;
|
||||
|
||||
return acc;
|
||||
}, {}) : {};
|
||||
|
||||
// Finally, return the JSON.
|
||||
return _.merge(scope.appPackageJSON || {}, {
|
||||
'name': scope.name,
|
||||
'private': true,
|
||||
'version': '0.1.0',
|
||||
'description': 'A Strapi application.',
|
||||
'scripts': {
|
||||
'develop': 'strapi develop',
|
||||
'start': 'strapi start',
|
||||
'build': 'strapi build',
|
||||
'strapi': 'strapi', // Allow to use `npm run strapi` CLI,
|
||||
'lint': 'eslint api/**/*.js config/**/*.js plugins/**/*.js'
|
||||
},
|
||||
'devDependencies': {
|
||||
'babel-eslint': '^7.1.1',
|
||||
'eslint': '^4.19.1',
|
||||
'eslint-config-airbnb': '^13.0.0',
|
||||
'eslint-plugin-import': '^2.11.0',
|
||||
'eslint-plugin-react': '^7.7.0'
|
||||
},
|
||||
'dependencies': Object.assign({}, {
|
||||
'lodash': '^4.17.5',
|
||||
'strapi': getDependencyVersion(cliPkg, 'strapi'),
|
||||
'strapi-admin': getDependencyVersion(cliPkg, 'strapi'),
|
||||
'strapi-utils': getDependencyVersion(cliPkg, 'strapi'),
|
||||
[scope.client.connector]: getDependencyVersion(cliPkg, 'strapi'),
|
||||
}, additionalsDependencies, {
|
||||
[scope.client.module]: scope.client.version
|
||||
}),
|
||||
'author': {
|
||||
'name': scope.author || 'A Strapi developer',
|
||||
'email': scope.email || '',
|
||||
'url': scope.website || ''
|
||||
},
|
||||
'maintainers': [{
|
||||
'name': scope.author || 'A Strapi developer',
|
||||
'email': scope.email || '',
|
||||
'url': scope.website || ''
|
||||
}],
|
||||
'strapi': {
|
||||
'uuid': scope.uuid
|
||||
},
|
||||
'engines': {
|
||||
"node": "^10.0.0",
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
'license': scope.license || 'MIT'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get dependencies version
|
||||
*/
|
||||
|
||||
function getDependencyVersion(packageJSON, module) {
|
||||
return module === packageJSON.name ? packageJSON.version : packageJSON.dependencies && packageJSON.dependencies[module];
|
||||
}
|
||||
@ -15,11 +15,9 @@ const { cyan, green } = require('chalk');
|
||||
const fs = require('fs-extra');
|
||||
const inquirer = require('inquirer');
|
||||
const execa = require('execa');
|
||||
const uuid = require('uuid/v4');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
// Logger.
|
||||
const trackSuccess = require('./success');
|
||||
const recordUsage = require('./success');
|
||||
|
||||
function hasYarn() {
|
||||
try {
|
||||
@ -31,6 +29,40 @@ function hasYarn() {
|
||||
}
|
||||
}
|
||||
|
||||
const databaseChoices = [
|
||||
{
|
||||
name: 'SQLite',
|
||||
value: {
|
||||
database: 'sqlite',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'sqlite3',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
value: {
|
||||
database: 'mongo',
|
||||
connector: 'strapi-hook-mongoose',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
value: {
|
||||
database: 'mysql',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'mysql',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Postgres',
|
||||
value: {
|
||||
database: 'postgres',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'pg',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* This `before` function is run before generating targets.
|
||||
* Validate, configure defaults, get extra dependencies, etc.
|
||||
@ -40,7 +72,7 @@ function hasYarn() {
|
||||
*/
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
module.exports = (scope, cb) => {
|
||||
module.exports = async scope => {
|
||||
// App info.
|
||||
const hasDatabaseConfig = !!scope.database;
|
||||
|
||||
@ -71,20 +103,30 @@ module.exports = (scope, cb) => {
|
||||
os.tmpdir(),
|
||||
`strapi${crypto.randomBytes(6).toString('hex')}`
|
||||
);
|
||||
scope.uuid = uuid();
|
||||
|
||||
trackSuccess('willCreateProject', scope);
|
||||
await recordUsage('willCreateProject', scope);
|
||||
|
||||
// Ensure we aren't going to inadvertently delete any files.
|
||||
try {
|
||||
const files = fs.readdirSync(scope.rootPath);
|
||||
if (files.length > 1) {
|
||||
return console.log(
|
||||
`⛔️ ${cyan('strapi new')} can only be called in an empty directory.`
|
||||
// Ensure we aren't going to inadvertently delete any files
|
||||
if (await fs.exists(scope.rootPath)) {
|
||||
const stat = await fs.stat(scope.rootPath);
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
console.log(
|
||||
`⛔️ ${
|
||||
scope.rootPath
|
||||
} is not a directory. Make sure to create a Strapi application in an empty directory.`
|
||||
);
|
||||
throw new Error('Path is not a directory');
|
||||
}
|
||||
|
||||
const files = await fs.readdir(scope.rootPath);
|
||||
if (files.length > 1) {
|
||||
console.log(
|
||||
`⛔️ You can only create a Strapi app in an empty directory.`
|
||||
);
|
||||
|
||||
throw new Error('Directory is not empty');
|
||||
}
|
||||
} catch (err) {
|
||||
// ...
|
||||
}
|
||||
|
||||
if (hasDatabaseConfig) {
|
||||
@ -94,40 +136,6 @@ module.exports = (scope, cb) => {
|
||||
}
|
||||
|
||||
const connectionValidation = async () => {
|
||||
const databaseChoices = [
|
||||
{
|
||||
name: 'SQLite',
|
||||
value: {
|
||||
database: 'sqlite',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'sqlite3',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
value: {
|
||||
database: 'mongo',
|
||||
connector: 'strapi-hook-mongoose',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
value: {
|
||||
database: 'mysql',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'mysql',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Postgres',
|
||||
value: {
|
||||
database: 'postgres',
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
module: 'pg',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
when: !scope.quick && !hasDatabaseConfig,
|
||||
@ -191,7 +199,7 @@ module.exports = (scope, cb) => {
|
||||
|
||||
scope.client = answers.client;
|
||||
|
||||
const connectedToTheDatabase = (withMessage = true) => {
|
||||
const connectedToTheDatabase = async (withMessage = true) => {
|
||||
if (withMessage) {
|
||||
console.log();
|
||||
console.log(
|
||||
@ -200,55 +208,48 @@ module.exports = (scope, cb) => {
|
||||
}
|
||||
|
||||
if (isQuick) {
|
||||
trackSuccess('didChooseQuickstart', scope);
|
||||
await recordUsage('didChooseQuickstart', scope);
|
||||
} else {
|
||||
trackSuccess('didChooseCustomDatabase', scope);
|
||||
await recordUsage('didChooseCustomDatabase', scope);
|
||||
}
|
||||
|
||||
trackSuccess('didConnectDatabase', scope);
|
||||
|
||||
cb.success();
|
||||
await recordUsage('didConnectDatabase', scope);
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
handleCustomDatabase({ scope, isQuick, hasDatabaseConfig }),
|
||||
installDatabaseTestingDep({ scope, isQuick }),
|
||||
])
|
||||
.then(() => {
|
||||
// Bypass real connection test.
|
||||
if (isQuick) {
|
||||
return connectedToTheDatabase(false);
|
||||
}
|
||||
]);
|
||||
|
||||
try {
|
||||
const connectivityFile = path.join(
|
||||
scope.tmpPath,
|
||||
'node_modules',
|
||||
scope.client.connector,
|
||||
'lib',
|
||||
'utils',
|
||||
'connectivity.js'
|
||||
);
|
||||
// Bypass real connection test.
|
||||
if (isQuick) {
|
||||
return connectedToTheDatabase(false);
|
||||
}
|
||||
|
||||
require(connectivityFile)(
|
||||
scope,
|
||||
connectedToTheDatabase,
|
||||
connectionValidation
|
||||
);
|
||||
} catch (err) {
|
||||
trackSuccess('didNotConnectDatabase', scope, err);
|
||||
console.log(err);
|
||||
rimraf.sync(scope.tmpPath);
|
||||
cb.error();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
cb.error(err);
|
||||
});
|
||||
try {
|
||||
const connectivityFile = path.join(
|
||||
scope.tmpPath,
|
||||
'node_modules',
|
||||
scope.client.connector,
|
||||
'lib',
|
||||
'utils',
|
||||
'connectivity.js'
|
||||
);
|
||||
|
||||
return require(connectivityFile)(
|
||||
scope,
|
||||
connectedToTheDatabase,
|
||||
connectionValidation
|
||||
);
|
||||
} catch (err) {
|
||||
await recordUsage('didNotConnectDatabase', scope, err);
|
||||
console.log(err);
|
||||
fs.removeSync(scope.tmpPath);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
connectionValidation();
|
||||
return connectionValidation();
|
||||
};
|
||||
|
||||
async function handleCustomDatabase({ scope, hasDatabaseConfig, isQuick }) {
|
||||
|
||||
86
packages/strapi-generate-new/lib/db-questions.js
Normal file
86
packages/strapi-generate-new/lib/db-questions.js
Normal file
@ -0,0 +1,86 @@
|
||||
const database = ({ scope }) => ({
|
||||
type: 'input',
|
||||
name: 'database',
|
||||
message: 'Database name:',
|
||||
default: scope.name,
|
||||
});
|
||||
|
||||
const host = () => ({
|
||||
type: 'input',
|
||||
name: 'host',
|
||||
message: 'Host:',
|
||||
default: '127.0.0.1',
|
||||
});
|
||||
|
||||
const srv = () => ({
|
||||
type: 'boolean',
|
||||
name: 'srv',
|
||||
message: '+srv connection:',
|
||||
default: false,
|
||||
});
|
||||
|
||||
const port = ({ client }) => ({
|
||||
type: 'input',
|
||||
name: 'port',
|
||||
message: `Port${
|
||||
client === 'mongo' ? ' (It will be ignored if you enable +srv)' : ''
|
||||
}:`,
|
||||
default: () => {
|
||||
const ports = {
|
||||
mongo: 27017,
|
||||
postgres: 5432,
|
||||
mysql: 3306,
|
||||
};
|
||||
|
||||
return ports[client];
|
||||
},
|
||||
});
|
||||
|
||||
const username = () => ({
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Username:',
|
||||
});
|
||||
|
||||
const password = () => ({
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Password:',
|
||||
mask: '*',
|
||||
});
|
||||
|
||||
const authenticationDatabase = () => ({
|
||||
type: 'input',
|
||||
name: 'authenticationDatabase',
|
||||
message: 'Authentication database (Maybe "admin" or blank):',
|
||||
});
|
||||
|
||||
const ssl = () => ({
|
||||
type: 'confirm',
|
||||
name: 'ssl',
|
||||
message: 'Enable SSL connection:',
|
||||
default: false,
|
||||
});
|
||||
|
||||
const filename = () => ({
|
||||
type: 'input',
|
||||
name: 'filename',
|
||||
message: 'Filename:',
|
||||
default: '.tmp/data.db',
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
sqlite: [filename],
|
||||
postgres: [database, host, port, username, password, ssl],
|
||||
mysql: [database, host, port, username, password, ssl],
|
||||
mongo: [
|
||||
database,
|
||||
host,
|
||||
srv,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
authenticationDatabase,
|
||||
ssl,
|
||||
],
|
||||
};
|
||||
@ -5,90 +5,525 @@
|
||||
*/
|
||||
|
||||
// Node.js core.
|
||||
const path = require('path');
|
||||
const { join, resolve, basename } = require('path');
|
||||
const { merge, pick } = require('lodash');
|
||||
const os = require('os');
|
||||
const crypto = require('crypto');
|
||||
const { machineIdSync } = require('node-machine-id');
|
||||
const uuid = require('uuid/v4');
|
||||
const inquirer = require('inquirer');
|
||||
const execa = require('execa');
|
||||
|
||||
// Local dependencies.
|
||||
const packageJSON = require('../json/package.json.js');
|
||||
const database = require('../json/database.json.js');
|
||||
const packageJSON = require('./resources/json/package.json');
|
||||
const databaseJSON = require('./resources/json/database.json.js');
|
||||
|
||||
const { trackError, trackUsage } = require('./usage');
|
||||
const dbQuestions = require('./db-questions');
|
||||
const fse = require('fs-extra');
|
||||
|
||||
/**
|
||||
* Copy required files for the generated application
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
moduleDir: path.resolve(__dirname, '..'),
|
||||
templatesDirectory: path.resolve(__dirname, '..', 'templates'),
|
||||
before: require('./before'),
|
||||
after: require('./after'),
|
||||
targets: {
|
||||
const defaultConfigs = {
|
||||
sqlite: {
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
settings: {
|
||||
client: 'sqlite',
|
||||
filename: '.tmp/data.db',
|
||||
},
|
||||
options: {
|
||||
useNullAsDefault: true,
|
||||
},
|
||||
},
|
||||
postgres: {
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
settings: {
|
||||
client: 'postgres',
|
||||
},
|
||||
},
|
||||
mysql: {
|
||||
connector: 'strapi-hook-bookshelf',
|
||||
settings: {
|
||||
client: 'mysql',
|
||||
},
|
||||
},
|
||||
mongo: {
|
||||
connector: 'strapi-hook-mongoose',
|
||||
},
|
||||
};
|
||||
|
||||
// Call the `admin` generator.
|
||||
// '.': ['admin'],
|
||||
const sqlClientModule = {
|
||||
sqlite: 'sqlite3',
|
||||
postgres: 'pg',
|
||||
mysql: 'mysql',
|
||||
};
|
||||
|
||||
// Main package.
|
||||
'package.json': {
|
||||
jsonfile: packageJSON
|
||||
},
|
||||
|
||||
'config/environments/development/database.json': {
|
||||
jsonfile: database
|
||||
},
|
||||
|
||||
'config/environments/production/database.json': {
|
||||
jsonfile: database
|
||||
},
|
||||
|
||||
'config/environments/staging/database.json': {
|
||||
jsonfile: database
|
||||
},
|
||||
|
||||
// Copy dot files.
|
||||
'.editorconfig': {
|
||||
copy: 'editorconfig'
|
||||
},
|
||||
'.gitignore': {
|
||||
copy: 'gitignore'
|
||||
},
|
||||
|
||||
// Copy Markdown files with some information.
|
||||
'README.md': {
|
||||
template: 'README.md'
|
||||
},
|
||||
|
||||
// Empty API directory.
|
||||
'api': {
|
||||
folder: {}
|
||||
},
|
||||
|
||||
'api/.gitkeep': {
|
||||
copy: 'gitkeep'
|
||||
},
|
||||
|
||||
// Empty plugins directory.
|
||||
'extensions': {
|
||||
folder: {}
|
||||
},
|
||||
|
||||
'extensions/.gitkeep': {
|
||||
copy: 'gitkeep'
|
||||
},
|
||||
|
||||
// Empty public directory.
|
||||
'public': {
|
||||
folder: {}
|
||||
},
|
||||
|
||||
// Empty public directory.
|
||||
'public/uploads': {
|
||||
folder: {}
|
||||
},
|
||||
// Copy gitkeep into uploads directory.
|
||||
'public/uploads/.gitkeep': {
|
||||
copy: 'gitkeep'
|
||||
},
|
||||
// Empty node_modules directory.
|
||||
'node_modules': {
|
||||
folder: {}
|
||||
}
|
||||
const clientDependencies = ({ scope, client }) => {
|
||||
switch (client) {
|
||||
case 'sqlite':
|
||||
case 'postgres':
|
||||
case 'mysql':
|
||||
return {
|
||||
'strapi-hook-bookshelf': scope.strapiVersion,
|
||||
'strapi-hook-knex': scope.strapiVersion,
|
||||
knex: 'latest',
|
||||
[sqlClientModule[client]]: 'latest',
|
||||
};
|
||||
case 'mongo':
|
||||
return {
|
||||
'strapi-hook-mongoose': scope.strapiVersion,
|
||||
};
|
||||
default:
|
||||
throw new Error(`Invalid client ${client}`);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = async (location, cliArguments = {}) => {
|
||||
console.log('🚀 Creating your Strapi application.\n');
|
||||
|
||||
const { debug = false, quickstart = false } = cliArguments;
|
||||
|
||||
// Build scope.
|
||||
const rootPath = resolve(location);
|
||||
|
||||
const tmpPath = join(
|
||||
os.tmpdir(),
|
||||
`strapi${crypto.randomBytes(6).toString('hex')}`
|
||||
);
|
||||
|
||||
const scope = {
|
||||
rootPath,
|
||||
name: basename(rootPath),
|
||||
// use pacakge version as strapiVersion (all packages have the same version);
|
||||
strapiVersion: require('../package.json').version,
|
||||
debug: debug !== false,
|
||||
quick: quickstart !== false,
|
||||
uuid: 'testing', //uuid(),
|
||||
deviceId: machineIdSync(),
|
||||
tmpPath,
|
||||
hasYarn: hasYarn(),
|
||||
strapiDependencies: [
|
||||
'strapi',
|
||||
'strapi-admin',
|
||||
'strapi-utils',
|
||||
'strapi-plugin-settings-manager',
|
||||
'strapi-plugin-content-type-builder',
|
||||
'strapi-plugin-content-manager',
|
||||
'strapi-plugin-users-permissions',
|
||||
'strapi-plugin-email',
|
||||
'strapi-plugin-upload',
|
||||
],
|
||||
additionalsDependencies: {},
|
||||
};
|
||||
|
||||
parseDatabaseArguments({ scope, args: cliArguments });
|
||||
initCancelCatcher();
|
||||
|
||||
await trackUsage({ event: 'willCreateProject', scope });
|
||||
|
||||
const hasDatabaseConfig = Boolean(scope.database);
|
||||
|
||||
// check rootPath is empty
|
||||
if (await fse.exists(scope.rootPath)) {
|
||||
const stat = await fse.stat(scope.rootPath);
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
await trackError({ scope, error: 'Path is not a directory' });
|
||||
|
||||
stopProcess(
|
||||
`⛔️ ${
|
||||
scope.rootPath
|
||||
} is not a directory. Make sure to create a Strapi application in an empty directory.`
|
||||
);
|
||||
}
|
||||
|
||||
const files = await fse.readdir(scope.rootPath);
|
||||
if (files.length > 1) {
|
||||
await trackError({ scope, error: 'Directory is not empty' });
|
||||
stopProcess(
|
||||
`⛔️ You can only create a Strapi app in an empty directory.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if database config is provided don't test the connection and create the project directly
|
||||
if (hasDatabaseConfig) {
|
||||
await trackUsage({ event: 'didChooseCustomDatabase', scope });
|
||||
|
||||
const client = scope.database.settings.client;
|
||||
const configuration = {
|
||||
client,
|
||||
connection: merge(defaultConfigs[client] || {}, scope.database),
|
||||
dependencies: clientDependencies({ scope, client: client }),
|
||||
};
|
||||
return createProject(scope, configuration);
|
||||
}
|
||||
|
||||
// if cli quickstart create project with default sqlite options
|
||||
if (scope.quick === true) {
|
||||
return createQuickStartProject(scope);
|
||||
}
|
||||
|
||||
const useQuickStart = await askShouldUseQuickstart();
|
||||
|
||||
// else if question response is quickstart create project
|
||||
if (useQuickStart) {
|
||||
return createQuickStartProject(scope);
|
||||
}
|
||||
|
||||
await trackUsage({ event: 'didChooseCustomDatabase', scope });
|
||||
|
||||
const configuration = await askDbInfosAndTest(scope).catch(error => {
|
||||
return trackUsage({ event: 'didNotConnectDatabase', scope, error }).then(
|
||||
() => {
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
await trackUsage({ event: 'didConnectDatabase', scope });
|
||||
return createProject(scope, configuration);
|
||||
};
|
||||
|
||||
function stopProcess(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const MAX_RETRIES = 5;
|
||||
async function askDbInfosAndTest(scope) {
|
||||
let retries = 0;
|
||||
|
||||
async function loop() {
|
||||
// else ask for the client name
|
||||
const { client, connection } = await askDatabaseInfos(scope);
|
||||
|
||||
const configuration = {
|
||||
client,
|
||||
connection,
|
||||
dependencies: clientDependencies({ scope, client: client }),
|
||||
};
|
||||
|
||||
await testDatabaseConnection({
|
||||
scope,
|
||||
configuration,
|
||||
})
|
||||
.then(
|
||||
() => fse.remove(scope.tmpPath),
|
||||
err => {
|
||||
return fse.remove(scope.tmpPath).then(() => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
console.log(`⛔️ Connection test failed: ${err.message}`);
|
||||
|
||||
if (scope.debug) {
|
||||
console.log('Full error log:');
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
if (retries < MAX_RETRIES) {
|
||||
console.log('Retrying...');
|
||||
retries++;
|
||||
return loop();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Could not connect to your database after ${MAX_RETRIES} tries`
|
||||
);
|
||||
});
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
return loop();
|
||||
}
|
||||
|
||||
async function testDatabaseConnection({ scope, configuration }) {
|
||||
const { client } = configuration;
|
||||
|
||||
if (client === 'sqlite') return;
|
||||
|
||||
await installDatabaseTestingDep({
|
||||
scope,
|
||||
configuration,
|
||||
});
|
||||
|
||||
// const connectivityFile = join(
|
||||
// scope.tmpPath,
|
||||
// 'node_modules',
|
||||
// configuration.connection.connector,
|
||||
// 'lib',
|
||||
// 'utils',
|
||||
// 'connectivity.js'
|
||||
// );
|
||||
|
||||
// const tester = require(connectivityFile);
|
||||
const tester = require(`${
|
||||
configuration.connection.connector
|
||||
}/lib/utils/connectivity.js`);
|
||||
return tester({ scope, connection: configuration.connection });
|
||||
}
|
||||
|
||||
async function createProject(scope, { client, connection, dependencies }) {
|
||||
try {
|
||||
const { rootPath } = scope;
|
||||
const resources = join(__dirname, 'resources');
|
||||
|
||||
// copy files
|
||||
await fse.copy(join(resources, 'files'), rootPath);
|
||||
|
||||
// copy templates
|
||||
await fse.writeJSON(
|
||||
join(rootPath, 'package.json'),
|
||||
packageJSON({
|
||||
strapiDependencies: scope.strapiDependencies,
|
||||
additionalsDependencies: dependencies,
|
||||
strapiVersion: scope.strapiVersion,
|
||||
projectName: scope.name,
|
||||
uuid: scope.uuid,
|
||||
}),
|
||||
{
|
||||
spaces: 2,
|
||||
}
|
||||
);
|
||||
|
||||
// ensure node_modules is created
|
||||
await fse.ensureDir(join(rootPath, 'node_modules'));
|
||||
|
||||
await Promise.all(
|
||||
['development', 'staging', 'production'].map(env => {
|
||||
return fse.writeJSON(
|
||||
join(rootPath, `config/environments/${env}/database.json`),
|
||||
databaseJSON({
|
||||
connection,
|
||||
env,
|
||||
}),
|
||||
{ spaces: 2 }
|
||||
);
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
await fse.remove(scope.rootPath);
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
await runInstall(scope);
|
||||
} catch (error) {
|
||||
await trackUsage({
|
||||
event: 'didNotInstallProjectDependencies',
|
||||
scope,
|
||||
error,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
await trackUsage({ event: 'didCreateProject', scope });
|
||||
}
|
||||
|
||||
async function createQuickStartProject(scope) {
|
||||
await trackUsage({ event: 'didChooseQuickstart', scope });
|
||||
|
||||
// get default sqlite config
|
||||
const client = 'sqlite';
|
||||
const configuration = {
|
||||
client,
|
||||
connection: defaultConfigs[client],
|
||||
dependencies: clientDependencies({ scope, client: client }),
|
||||
};
|
||||
|
||||
await createProject(scope, configuration);
|
||||
|
||||
await execa('npm', ['run', 'develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: scope.rootPath,
|
||||
env: {
|
||||
FORCE_COLOR: 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function hasYarn() {
|
||||
try {
|
||||
const { code } = execa.shellSync('yarnpkg --version');
|
||||
if (code === 0) return true;
|
||||
return false;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function askShouldUseQuickstart() {
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'type',
|
||||
message: 'Choose your installation type',
|
||||
choices: [
|
||||
{
|
||||
name: 'Quickstart (recommended)',
|
||||
value: 'quick',
|
||||
},
|
||||
{
|
||||
name: 'Custom (manual settings)',
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
return answer.type === 'quick';
|
||||
}
|
||||
|
||||
const SETTINGS_FIELDS = [
|
||||
'database',
|
||||
'host',
|
||||
'srv',
|
||||
'port',
|
||||
'username',
|
||||
'password',
|
||||
'filename',
|
||||
];
|
||||
|
||||
const OPTIONS_FIELDS = ['authenticationDatabase'];
|
||||
|
||||
async function askDatabaseInfos(scope) {
|
||||
const { client } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'client',
|
||||
message: 'Choose your default database client',
|
||||
choices: ['sqlite', 'postgres', 'mysql', 'mongo'],
|
||||
default: 'sqlite',
|
||||
},
|
||||
]);
|
||||
|
||||
const responses = await inquirer.prompt(
|
||||
dbQuestions[client].map(q => q({ scope, client }))
|
||||
);
|
||||
|
||||
const connection = merge({}, defaultConfigs[client] || {}, {
|
||||
settings: pick(responses, SETTINGS_FIELDS),
|
||||
options: pick(responses, OPTIONS_FIELDS),
|
||||
});
|
||||
|
||||
if (responses.ssl === true) {
|
||||
if (client === 'mongo') {
|
||||
connection.options.ssl = true;
|
||||
} else {
|
||||
connection.settings.ssl = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
client,
|
||||
connection,
|
||||
};
|
||||
}
|
||||
|
||||
async function installDatabaseTestingDep({ scope, configuration }) {
|
||||
let packageCmd = scope.hasYarn
|
||||
? `yarnpkg --cwd ${scope.tmpPath} add`
|
||||
: `npm install --prefix ${scope.tmpPath}`;
|
||||
|
||||
// Manually create the temp directory for yarn
|
||||
if (scope.hasYarn) {
|
||||
await fse.ensureDir(scope.tmpPath);
|
||||
}
|
||||
|
||||
const depArgs = Object.keys(configuration.dependencies).map(dep => {
|
||||
return `${dep}@${configuration.dependencies[dep]}`;
|
||||
});
|
||||
|
||||
const cmd = `${packageCmd} ${depArgs.join(' ')}`;
|
||||
await execa.shell(cmd);
|
||||
}
|
||||
|
||||
const dbArguments = [
|
||||
'dbclient',
|
||||
'dbhost',
|
||||
'dbport',
|
||||
'dbname',
|
||||
'dbusername',
|
||||
'dbpassword',
|
||||
];
|
||||
|
||||
function parseDatabaseArguments({ scope, args }) {
|
||||
const argKeys = Object.keys(args);
|
||||
const matchingDbArguments = dbArguments.filter(key => argKeys.includes(key));
|
||||
|
||||
if (matchingDbArguments.length === 0) return;
|
||||
|
||||
if (
|
||||
matchingDbArguments.length !== dbArguments.length &&
|
||||
args.dbclient !== 'sqlite'
|
||||
) {
|
||||
return stopProcess(
|
||||
`⛔️ Some database arguments are missing. Required arguments list: ${dbArguments}`
|
||||
);
|
||||
}
|
||||
|
||||
scope.dbforce = args.dbforce !== undefined;
|
||||
|
||||
const database = {
|
||||
settings: {
|
||||
client: args.dbclient,
|
||||
host: args.dbhost,
|
||||
srv: args.dbsrv,
|
||||
port: args.dbport,
|
||||
database: args.dbname,
|
||||
username: args.dbusername,
|
||||
password: args.dbpassword,
|
||||
filename: args.dbfile,
|
||||
},
|
||||
options: {},
|
||||
};
|
||||
|
||||
if (args.dbauth !== undefined) {
|
||||
database.options.authenticationDatabase = args.dbauth;
|
||||
}
|
||||
|
||||
if (args.dbssl !== undefined) {
|
||||
if (args.dbclient === 'mongo') {
|
||||
database.options.ssl = args.dbssl === 'true';
|
||||
} else {
|
||||
database.settings.ssl = args.dbssl === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
scope.database = database;
|
||||
}
|
||||
|
||||
function initCancelCatcher(scope) {
|
||||
// Create interface for windows user to let them quit the program.
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function() {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
console.log('Cancelling...');
|
||||
process.exit();
|
||||
// trackUsage({ event: 'didStopCreateProject', scope }).then(() => {
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
const installArguments = ['install', '--production', '--no-optional'];
|
||||
function runInstall({ rootPath, hasYarn }) {
|
||||
if (hasYarn) {
|
||||
return execa('yarnpkg', installArguments, { cwd: rootPath });
|
||||
}
|
||||
return execa('npm', installArguments, { cwd: rootPath });
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
# Strapi application
|
||||
|
||||
A quick description of your strapi application
|
||||
@ -9,12 +9,10 @@
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Simple example.
|
||||
* Every monday at 1am.
|
||||
*/
|
||||
|
||||
// '0 1 * * 1': () => {
|
||||
//
|
||||
// }
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
const { merge } = require('lodash');
|
||||
|
||||
module.exports = ({ connection, env }) => {
|
||||
// Production/Staging Template
|
||||
if (['production', 'staging'].includes(env)) {
|
||||
// All available settings (bookshelf and mongoose)
|
||||
const settingsBase = {
|
||||
client: connection.settings.client,
|
||||
host: "${process.env.DATABASE_HOST || '127.0.0.1'}",
|
||||
port: '${process.env.DATABASE_PORT || 27017}',
|
||||
srv: '${process.env.DATABASE_SRV || false}',
|
||||
database: "${process.env.DATABASE_NAME || 'strapi'}",
|
||||
username: "${process.env.DATABASE_USERNAME || ''}",
|
||||
password: "${process.env.DATABASE_PASSWORD || ''}",
|
||||
ssl: '${process.env.DATABASE_SSL || false}',
|
||||
};
|
||||
|
||||
// All available options (bookshelf and mongoose)
|
||||
const optionsBase = {
|
||||
ssl: '${process.env.DATABASE_SSL || false}',
|
||||
authenticationDatabase:
|
||||
"${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}",
|
||||
};
|
||||
|
||||
return {
|
||||
defaultConnection: 'default',
|
||||
connections: {
|
||||
default: merge({
|
||||
settings: settingsBase,
|
||||
options: optionsBase,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
defaultConnection: 'default',
|
||||
connections: {
|
||||
default: connection,
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Expose main package JSON of the application
|
||||
* with basic info, dependencies, etc.
|
||||
*/
|
||||
|
||||
module.exports = opts => {
|
||||
const {
|
||||
strapiDependencies,
|
||||
additionalsDependencies,
|
||||
strapiVersion,
|
||||
projectName,
|
||||
uuid,
|
||||
} = opts;
|
||||
|
||||
// Finally, return the JSON.
|
||||
return {
|
||||
name: projectName,
|
||||
private: true,
|
||||
version: '0.1.0',
|
||||
description: 'A Strapi application',
|
||||
scripts: {
|
||||
develop: 'strapi develop',
|
||||
start: 'strapi start',
|
||||
build: 'strapi build',
|
||||
strapi: 'strapi', // Allow to use `npm run strapi` CLI,
|
||||
lint: 'eslint api/**/*.js config/**/*.js plugins/**/*.js',
|
||||
},
|
||||
devDependencies: {
|
||||
'babel-eslint': '^7.1.1',
|
||||
eslint: '^4.19.1',
|
||||
'eslint-config-airbnb': '^13.0.0',
|
||||
'eslint-plugin-import': '^2.11.0',
|
||||
'eslint-plugin-react': '^7.7.0',
|
||||
},
|
||||
dependencies: Object.assign(
|
||||
{ lodash: '^4.17.5' },
|
||||
strapiDependencies.reduce((acc, key) => {
|
||||
acc[key] = strapiVersion;
|
||||
return acc;
|
||||
}, {}),
|
||||
additionalsDependencies
|
||||
),
|
||||
author: {
|
||||
name: 'A Strapi developer',
|
||||
},
|
||||
strapi: {
|
||||
uuid: uuid,
|
||||
},
|
||||
engines: {
|
||||
node: '^10.0.0',
|
||||
npm: '>= 6.0.0',
|
||||
},
|
||||
license: 'MIT',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get dependencies version
|
||||
*/
|
||||
@ -6,20 +6,35 @@
|
||||
|
||||
// Node.js core.
|
||||
const os = require('os');
|
||||
const request = require('request');
|
||||
const { machineIdSync } = require('node-machine-id');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
module.exports = function trackSuccess(event, scope, error) {
|
||||
request
|
||||
.post('https://analytics.strapi.io/track')
|
||||
.form({
|
||||
module.exports = function recordUsage(event, scope, error) {
|
||||
return fetch('https://analytics.strapi.io/track', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
event,
|
||||
uuid: scope.uuid,
|
||||
deviceId: machineIdSync(),
|
||||
deviceId: scope.deviceId,
|
||||
properties: {
|
||||
error,
|
||||
os: os.type()
|
||||
}
|
||||
})
|
||||
.on('error', () => {});
|
||||
error: typeof error == 'string' ? error : error && error.message,
|
||||
os: os.type(),
|
||||
version: scope.strapiPackageJSON.version,
|
||||
},
|
||||
}),
|
||||
timeout: 1000,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}).catch(() => {});
|
||||
|
||||
// request
|
||||
// .post('https://analytics.strapi.io/track')
|
||||
// .form({
|
||||
// event,
|
||||
// uuid: scope.uuid,
|
||||
// deviceId: machineIdSync(),
|
||||
// properties: {
|
||||
// error,
|
||||
// os: os.type()
|
||||
// }
|
||||
// })
|
||||
// .on('error', () => {});
|
||||
};
|
||||
|
||||
44
packages/strapi-generate-new/lib/usage.js
Normal file
44
packages/strapi-generate-new/lib/usage.js
Normal file
@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
const os = require('os');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
function trackEvent(event, body) {
|
||||
return fetch('https://analytics.strapi.io/track', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
event,
|
||||
...body,
|
||||
}),
|
||||
timeout: 1000,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function trackError({ scope, error }) {
|
||||
return trackEvent('didNotCreateProject', {
|
||||
uuid: scope.uuid,
|
||||
deviceId: scope.deviceId,
|
||||
properties: {
|
||||
error: typeof error == 'string' ? error : error && error.message,
|
||||
os: os.type(),
|
||||
version: scope.strapiVersion,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function trackUsage({ event, scope, error }) {
|
||||
return trackEvent(event, {
|
||||
uuid: scope.uuid,
|
||||
deviceId: scope.deviceId,
|
||||
properties: {
|
||||
error: typeof error == 'string' ? error : error && error.message,
|
||||
os: os.type(),
|
||||
version: scope.strapiVersion,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
trackError,
|
||||
trackUsage,
|
||||
};
|
||||
@ -16,12 +16,10 @@
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^8.0.1",
|
||||
"inquirer": "^6.3.1",
|
||||
"listr": "^0.14.3",
|
||||
"lodash": "^4.17.11",
|
||||
"node-machine-id": "^1.1.10",
|
||||
"ora": "^3.4.0",
|
||||
"request": "^2.88.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-machine-id": "^1.1.10",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
# <%= name %>
|
||||
|
||||
A quick description of <%= name %>.
|
||||
@ -1,118 +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
|
||||
|
||||
|
||||
############################
|
||||
# Node.js
|
||||
############################
|
||||
|
||||
lib-cov
|
||||
lcov.info
|
||||
pids
|
||||
logs
|
||||
results
|
||||
build
|
||||
node_modules
|
||||
.node_history
|
||||
|
||||
|
||||
############################
|
||||
# Tests
|
||||
############################
|
||||
|
||||
testApp
|
||||
coverage
|
||||
|
||||
|
||||
############################
|
||||
# Front-end workflow
|
||||
############################
|
||||
|
||||
bower_components
|
||||
|
||||
|
||||
############################
|
||||
# Production config
|
||||
############################
|
||||
|
||||
**/production/
|
||||
@ -12,6 +12,7 @@ const logger = require('strapi-utils').logger;
|
||||
|
||||
// Local dependencies.
|
||||
const generate = require('./generate');
|
||||
const generateTarget = require('./target');
|
||||
|
||||
/* eslint-disable prefer-template */
|
||||
/**
|
||||
@ -31,7 +32,7 @@ module.exports = (scope, cb) => {
|
||||
notStrapiApp: () => {},
|
||||
alreadyExists: () => {
|
||||
return cb.error();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Use configured module name for this `generatorType` if applicable.
|
||||
@ -39,7 +40,10 @@ module.exports = (scope, cb) => {
|
||||
let generator;
|
||||
|
||||
function throwIfModuleNotFoundError(error, module) {
|
||||
const isModuleNotFoundError = error && error.code === 'MODULE_NOT_FOUND' && error.message.match(new RegExp(module));
|
||||
const isModuleNotFoundError =
|
||||
error &&
|
||||
error.code === 'MODULE_NOT_FOUND' &&
|
||||
error.message.match(new RegExp(module));
|
||||
if (!isModuleNotFoundError) {
|
||||
logger.error('Invalid `' + scope.generatorType + '` generator.');
|
||||
throw error;
|
||||
@ -56,8 +60,12 @@ module.exports = (scope, cb) => {
|
||||
}
|
||||
|
||||
if (!generator) {
|
||||
return logger.error('No generator called `' + scope.generatorType + '` found.');
|
||||
return logger.error(
|
||||
'No generator called `' + scope.generatorType + '` found.'
|
||||
);
|
||||
}
|
||||
|
||||
generate(generator, scope, cb);
|
||||
};
|
||||
|
||||
module.exports.generateTarget = generateTarget;
|
||||
|
||||
@ -1,94 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
// Node.js core.
|
||||
const path = require('path');
|
||||
|
||||
// Public node modules
|
||||
const inquirer = require('inquirer');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
module.exports = (scope, success, error) => {
|
||||
if (scope.client.database === 'sqlite') {
|
||||
return success();
|
||||
}
|
||||
const selectQueries = {
|
||||
postgres: "SELECT tablename FROM pg_tables WHERE schemaname='public'",
|
||||
mysql: 'SELECT * FROM information_schema.tables',
|
||||
sqlite: 'select * from sqlite_master',
|
||||
};
|
||||
|
||||
let knex;
|
||||
module.exports = async ({ scope, connection }) => {
|
||||
const knex = require('knex');
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
knex = require('knex');
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
knex = require(path.resolve(scope.tmpPath, 'node_modules', 'knex'));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
const { settings } = connection;
|
||||
const client = knex({
|
||||
client: scope.client.module,
|
||||
connection: Object.assign({}, scope.database.settings, {
|
||||
user: scope.database.settings.username
|
||||
client: settings.client,
|
||||
connection: Object.assign({}, settings, {
|
||||
user: settings.username,
|
||||
}),
|
||||
useNullAsDefault: true
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
|
||||
client.raw('select 1+1 as result').then(() => {
|
||||
const selectQueries = {
|
||||
postgres: 'SELECT tablename FROM pg_tables WHERE schemaname=\'public\'',
|
||||
mysql: 'SELECT * FROM information_schema.tables',
|
||||
sqlite: 'select * from sqlite_master'
|
||||
};
|
||||
const destroyClientAndThrow = err => {
|
||||
return client.destroy().then(
|
||||
() => {
|
||||
throw err;
|
||||
},
|
||||
() => {
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
client.raw(selectQueries[scope.client.database]).then((tables) => {
|
||||
client.destroy();
|
||||
await client.raw('select 1+1 as result').catch(destroyClientAndThrow);
|
||||
|
||||
const next = () => {
|
||||
rimraf(scope.tmpPath, (err) => {
|
||||
if (err) {
|
||||
console.log(`Error removing connection test folder: ${scope.tmpPath}`);
|
||||
}
|
||||
return client
|
||||
.raw(selectQueries[settings.client])
|
||||
.then(tables => {
|
||||
if (tables.rows && tables.rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
success();
|
||||
});
|
||||
};
|
||||
if (scope.dbforce) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tables.rows && tables.rows.length !== 0) {
|
||||
if (scope.dbforce) {
|
||||
next();
|
||||
} else {
|
||||
console.log('🤔 It seems that your database is not empty. Be aware that Strapi is going to automatically creates tables & columns, and might update columns which can corrupt data or cause data loss.');
|
||||
console.log(
|
||||
'🤔 It seems that your database is not empty. Be aware that Strapi is going to automatically creates tables & columns, and might update columns which can corrupt data or cause data loss.'
|
||||
);
|
||||
|
||||
inquirer.prompt([{
|
||||
return inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Are you sure you want to continue with the ${scope.database.settings.database} database:`,
|
||||
}])
|
||||
.then(({ confirm }) => {
|
||||
if (confirm) {
|
||||
next();
|
||||
} else {
|
||||
error();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
if (scope.debug) {
|
||||
console.log('🐛 Full error log:');
|
||||
console.log(err);
|
||||
return error();
|
||||
}
|
||||
|
||||
if (err.sql) {
|
||||
console.log('⚠️ Server connection has failed! Make sure your database server is running.');
|
||||
} else {
|
||||
console.log(`⚠️ Database connection has failed! Make sure your "${scope.database.settings.database}" database exist.`);
|
||||
}
|
||||
console.log(err.message);
|
||||
|
||||
error();
|
||||
});
|
||||
message: `Are you sure you want to continue with the ${
|
||||
settings.database
|
||||
} database:`,
|
||||
},
|
||||
])
|
||||
.then(({ confirm }) => {
|
||||
if (!confirm) {
|
||||
// TODO: cancel somehow
|
||||
throw new Error('Not confirmed');
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => client.destroy())
|
||||
.catch(destroyClientAndThrow);
|
||||
};
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
"inquirer": "^6.3.1",
|
||||
"lodash": "^4.17.11",
|
||||
"pluralize": "^7.0.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"strapi-hook-knex": "3.0.0-beta.6",
|
||||
"strapi-utils": "3.0.0-beta.6"
|
||||
},
|
||||
|
||||
@ -2,138 +2,15 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Node.js core.
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
const fetch = require('node-fetch');
|
||||
const { machineIdSync } = require('node-machine-id');
|
||||
const execa = require('execa');
|
||||
|
||||
// Master of ceremonies for generators.
|
||||
const generate = require('strapi-generate');
|
||||
|
||||
// Local Strapi dependencies.
|
||||
const packageJSON = require('../../package.json');
|
||||
|
||||
/**
|
||||
* `$ strapi new`
|
||||
*
|
||||
* Generate a new Strapi application.
|
||||
*/
|
||||
|
||||
const logError = error => {
|
||||
fetch('https://analytics.strapi.io/track', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
event: 'didNotStartAutomatically',
|
||||
deviceId: machineIdSync(),
|
||||
properties: {
|
||||
error,
|
||||
os: os.type(),
|
||||
},
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}).catch(() => {});
|
||||
};
|
||||
|
||||
module.exports = function(name, cliArguments) {
|
||||
console.log('🚀 Creating your Strapi application.\n');
|
||||
|
||||
// Build initial scope.
|
||||
const scope = {
|
||||
rootPath: process.cwd(),
|
||||
strapiRoot: path.resolve(__dirname, '..'),
|
||||
generatorType: 'new',
|
||||
name,
|
||||
strapiPackageJSON: packageJSON,
|
||||
debug: cliArguments.debug !== undefined,
|
||||
quick: cliArguments.quickstart !== undefined,
|
||||
};
|
||||
|
||||
const dbArguments = [
|
||||
'dbclient',
|
||||
'dbhost',
|
||||
'dbport',
|
||||
'dbname',
|
||||
'dbusername',
|
||||
'dbpassword',
|
||||
];
|
||||
const matchingDbArguments = _.intersection(_.keys(cliArguments), dbArguments);
|
||||
|
||||
if (matchingDbArguments.length) {
|
||||
if (
|
||||
matchingDbArguments.length !== dbArguments.length &&
|
||||
cliArguments.dbclient !== 'sqlite'
|
||||
) {
|
||||
console.log(
|
||||
`⛔️ Some database arguments are missing. Required arguments list: ${dbArguments}`
|
||||
);
|
||||
return process.exit(1);
|
||||
}
|
||||
|
||||
scope.dbforce = cliArguments.dbforce !== undefined;
|
||||
|
||||
scope.database = {
|
||||
settings: {
|
||||
client: cliArguments.dbclient,
|
||||
host: cliArguments.dbhost,
|
||||
srv: cliArguments.dbsrv,
|
||||
port: cliArguments.dbport,
|
||||
database: cliArguments.dbname,
|
||||
username: cliArguments.dbusername,
|
||||
password: cliArguments.dbpassword,
|
||||
filename: cliArguments.dbfile,
|
||||
},
|
||||
options: {
|
||||
authenticationDatabase: cliArguments.dbauth,
|
||||
ssl: cliArguments.dbssl,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Return the scope and the response (`error` or `success`).
|
||||
return generate(scope, {
|
||||
// Log and exit the REPL in case there is an error
|
||||
// while we were trying to generate the new app.
|
||||
error(err) {
|
||||
logError(err);
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
},
|
||||
|
||||
success: async () => {
|
||||
if (scope.quick) {
|
||||
// Create interface for windows user to let them quit the program.
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function() {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
// Listen Ctrl+C / SIGINT event to close the process.
|
||||
process.on('SIGINT', function() {
|
||||
process.exit();
|
||||
});
|
||||
|
||||
await execa('npm', ['run', 'develop'], {
|
||||
stdio: 'inherit',
|
||||
cwd: scope.rootPath,
|
||||
env: {
|
||||
FORCE_COLOR: 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
module.exports = function(...args) {
|
||||
return require('strapi-generate-new')(...args).catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
};
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
"minimatch": "^3.0.4",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-machine-id": "^1.1.10",
|
||||
"uuid": "^3.3.2",
|
||||
"node-schedule": "^1.2.0",
|
||||
"opn": "^5.3.0",
|
||||
"ora": "^3.0.0",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user