@strapi/strapi 4.2.0-alpha.0 → 4.2.0-alpha.6

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/bin/strapi.js CHANGED
@@ -100,7 +100,7 @@ program
100
100
 
101
101
  // `$ strapi start`
102
102
  program
103
- .command('start')
103
+ .command('start [dir]')
104
104
  .description('Start your Strapi application')
105
105
  .action(getLocalScript('start'));
106
106
 
@@ -174,6 +174,16 @@ program
174
174
  .action(getLocalScript('configurationRestore'));
175
175
 
176
176
  // Admin
177
+ program
178
+ .command('admin:create-user')
179
+ .alias('admin:create')
180
+ .description('Create a new admin')
181
+ .option('-e, --email <email>', 'Email of the new admin')
182
+ .option('-p, --password <password>', 'Password of the new admin')
183
+ .option('-f, --firstname <first name>', 'First name of the new admin')
184
+ .option('-l, --lastname <last name>', 'Last name of the new admin')
185
+ .action(getLocalScript('admin-create'));
186
+
177
187
  program
178
188
  .command('admin:reset-user-password')
179
189
  .alias('admin:reset-password')
package/lib/Strapi.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const path = require('path');
3
4
  const _ = require('lodash');
4
5
  const { isFunction } = require('lodash/fp');
5
6
  const { createLogger } = require('@strapi/logger');
@@ -47,12 +48,40 @@ const LIFECYCLES = {
47
48
  DESTROY: 'destroy',
48
49
  };
49
50
 
51
+ /**
52
+ * Resolve the working directories based on the instance options.
53
+ *
54
+ * Behavior:
55
+ * - `appDir` is the directory where Strapi will write every file (schemas, generated APIs, controllers or services)
56
+ * - `distDir` is the directory where Strapi will read configurations, schemas and any compiled code
57
+ *
58
+ * Default values:
59
+ * - If `appDir` is `undefined`, it'll be set to `process.cwd()`
60
+ * - If `distDir` is `undefined`, it'll be set to `appDir`
61
+ */
62
+ const resolveWorkingDirectories = opts => {
63
+ const cwd = process.cwd();
64
+
65
+ const appDir = opts.appDir ? path.resolve(cwd, opts.appDir) : cwd;
66
+ const distDir = opts.distDir ? path.resolve(cwd, opts.distDir) : appDir;
67
+
68
+ return { appDir, distDir };
69
+ };
70
+
50
71
  class Strapi {
51
72
  constructor(opts = {}) {
52
73
  destroyOnSignal(this);
53
- this.dirs = utils.getDirs(opts.dir || process.cwd());
54
- const appConfig = loadConfiguration(this.dirs.root, opts);
74
+
75
+ // Create a mapping of every useful directory (both for the app and dist directories)
76
+ this.dirs = utils.getDirs(resolveWorkingDirectories(opts));
77
+
78
+ // Load the app configuration from the dist directory
79
+ const appConfig = loadConfiguration(this.dirs.dist.root, opts);
80
+
81
+ // Instanciate the Strapi container
55
82
  this.container = createContainer(this);
83
+
84
+ // Register every Strapi registry in the container
56
85
  this.container.register('config', createConfigProvider(appConfig));
57
86
  this.container.register('content-types', contentTypesRegistry(this));
58
87
  this.container.register('services', servicesRegistry(this));
@@ -65,10 +94,14 @@ class Strapi {
65
94
  this.container.register('apis', apisRegistry(this));
66
95
  this.container.register('auth', createAuth(this));
67
96
 
97
+ // Strapi state management variables
68
98
  this.isLoaded = false;
69
99
  this.reload = this.reload();
100
+
101
+ // Instanciate the Koa app & the HTTP server
70
102
  this.server = createServer(this);
71
103
 
104
+ // Strapi utils instanciation
72
105
  this.fs = createStrapiFs(this);
73
106
  this.eventHub = createEventHub();
74
107
  this.startupLogger = createStartupLogger(this);
@@ -84,7 +117,7 @@ class Strapi {
84
117
  }
85
118
 
86
119
  get EE() {
87
- return ee({ dir: this.dirs.root, logger: this.log });
120
+ return ee({ dir: this.dirs.dist.root, logger: this.log });
88
121
  }
89
122
 
90
123
  get services() {
@@ -211,7 +244,6 @@ class Strapi {
211
244
  } catch (e) {
212
245
  this.telemetry.send('didNotOpenTab');
213
246
  }
214
- ;
215
247
  }
216
248
  }
217
249
 
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ const { yup } = require('@strapi/utils');
4
+ const _ = require('lodash');
5
+ const inquirer = require('inquirer');
6
+ const strapi = require('../index');
7
+
8
+ const emailValidator = yup
9
+ .string()
10
+ .email('Invalid email address')
11
+ .lowercase();
12
+
13
+ const passwordValidator = yup
14
+ .string()
15
+ .min(8, 'Password must be at least 8 characters long')
16
+ .matches(/[a-z]/, 'Password must contain at least one lowercase character')
17
+ .matches(/[A-Z]/, 'Password must contain at least one uppercase character')
18
+ .matches(/\d/, 'Password must contain at least one number');
19
+
20
+ const adminCreateSchema = yup.object().shape({
21
+ email: emailValidator,
22
+ password: passwordValidator,
23
+ firstname: yup.string().required('First name is required'),
24
+ lastname: yup.string(),
25
+ });
26
+
27
+ const promptQuestions = [
28
+ {
29
+ type: 'input',
30
+ name: 'email',
31
+ message: 'Admin email?',
32
+ async validate(value) {
33
+ const validEmail = await emailValidator.validate(value);
34
+ return validEmail === value || validEmail;
35
+ },
36
+ },
37
+ {
38
+ type: 'password',
39
+ name: 'password',
40
+ message: 'Admin password?',
41
+ async validate(value) {
42
+ const validPassword = await passwordValidator.validate(value);
43
+ return validPassword === value || validPassword;
44
+ },
45
+ },
46
+ { type: 'input', name: 'firstname', message: 'First name?' },
47
+ { type: 'input', name: 'lastname', message: 'Last name?' },
48
+ {
49
+ type: 'confirm',
50
+ name: 'confirm',
51
+ message: 'Do you really want to create a new admin?',
52
+ },
53
+ ];
54
+
55
+ /**
56
+ * Create new admin user
57
+ * @param {Object} cmdOptions - command options
58
+ * @param {string} cmdOptions.email - new admin's email
59
+ * @param {string} [cmdOptions.password] - new admin's password
60
+ * @param {string} cmdOptions.firstname - new admin's first name
61
+ * @param {string} [cmdOptions.lastname] - new admin's last name
62
+ */
63
+ module.exports = async function(cmdOptions = {}) {
64
+ let { email, password, firstname, lastname } = cmdOptions;
65
+
66
+ if (
67
+ _.isEmpty(email) &&
68
+ _.isEmpty(password) &&
69
+ _.isEmpty(firstname) &&
70
+ _.isEmpty(lastname) &&
71
+ process.stdin.isTTY
72
+ ) {
73
+ const inquiry = await inquirer.prompt(promptQuestions);
74
+
75
+ if (!inquiry.confirm) {
76
+ process.exit(0);
77
+ }
78
+
79
+ ({ email, password, firstname, lastname } = inquiry);
80
+ }
81
+
82
+ try {
83
+ await adminCreateSchema.validate({ email, password, firstname, lastname });
84
+ } catch (err) {
85
+ console.error(err.errors[0]);
86
+ process.exit(1);
87
+ }
88
+
89
+ return createAdmin({ email, password, firstname, lastname });
90
+ };
91
+
92
+ async function createAdmin({ email, password, firstname, lastname }) {
93
+ const app = await strapi().load();
94
+
95
+ const user = await app.admin.services.user.exists({ email });
96
+
97
+ if (user) {
98
+ console.error(`User with email "${email}" already exists`);
99
+ process.exit(1);
100
+ }
101
+
102
+ const superAdminRole = await app.admin.services.role.getSuperAdmin();
103
+
104
+ await app.admin.services.user.create({
105
+ email,
106
+ firstname,
107
+ lastname,
108
+ isActive: true,
109
+ roles: [superAdminRole.id],
110
+ ...(password && { password, registrationToken: null }),
111
+ });
112
+
113
+ console.log(`Successfully created new admin`);
114
+ process.exit(0);
115
+ }
@@ -1,56 +1,31 @@
1
1
  'use strict';
2
- const { green } = require('chalk');
2
+ const path = require('path');
3
3
 
4
- const strapiAdmin = require('@strapi/admin');
5
- const { getConfigUrls } = require('@strapi/utils');
6
-
7
- const ee = require('../utils/ee');
8
- const addSlash = require('../utils/addSlash');
9
- const strapi = require('../index');
10
- const getEnabledPlugins = require('../core/loaders/plugins/get-enabled-plugins');
4
+ const tsUtils = require('@strapi/typescript-utils');
5
+ const { buildAdmin, buildTypeScript } = require('./builders');
11
6
 
12
7
  /**
13
8
  * `$ strapi build`
14
9
  */
15
10
  module.exports = async ({ optimization, forceBuild = true }) => {
16
- const dir = process.cwd();
17
-
18
- const strapiInstance = strapi({
19
- dir,
20
- autoReload: true,
21
- serveAdminPanel: false,
22
- });
11
+ let buildDestDir = process.cwd();
12
+ const srcDir = process.cwd();
23
13
 
24
- const plugins = await getEnabledPlugins(strapiInstance);
14
+ const isTSProject = await tsUtils.isTypeScriptProject(srcDir);
25
15
 
26
- const env = strapiInstance.config.get('environment');
27
- const { serverUrl, adminPath } = getConfigUrls(strapiInstance.config, true);
16
+ // Typescript
17
+ if (isTSProject) {
18
+ await buildTypeScript({ srcDir, watch: false });
28
19
 
29
- console.log(`Building your admin UI with ${green(env)} configuration ...`);
20
+ // Update the dir path for the next steps
21
+ buildDestDir = path.join(srcDir, 'dist');
22
+ }
30
23
 
31
- // Always remove the .cache and build folders
32
- await strapiAdmin.clean({ dir });
33
-
34
- ee({ dir });
35
-
36
- return strapiAdmin
37
- .build({
38
- forceBuild,
39
- dir,
40
- plugins,
41
- // front end build env is always production for now
42
- env: 'production',
43
- optimize: optimization,
44
- options: {
45
- backend: serverUrl,
46
- adminPath: addSlash(adminPath),
47
- },
48
- })
49
- .then(() => {
50
- console.log('Admin UI built successfully');
51
- })
52
- .catch(err => {
53
- console.error(err);
54
- process.exit(1);
55
- });
24
+ await buildAdmin({
25
+ buildDestDir,
26
+ forceBuild,
27
+ isTSProject,
28
+ optimization,
29
+ srcDir,
30
+ });
56
31
  };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ const { green } = require('chalk');
4
+
5
+ const strapiAdmin = require('@strapi/admin');
6
+ const { getConfigUrls } = require('@strapi/utils');
7
+
8
+ const ee = require('../../utils/ee');
9
+ const addSlash = require('../../utils/addSlash');
10
+ const strapi = require('../../index');
11
+ const getEnabledPlugins = require('../../core/loaders/plugins/get-enabled-plugins');
12
+
13
+ module.exports = async ({ buildDestDir, forceBuild = true, isTSProject, optimization, srcDir }) => {
14
+ const strapiInstance = strapi({
15
+ // TODO check if this is working @convly
16
+ distDir: buildDestDir,
17
+ autoReload: true,
18
+ serveAdminPanel: false,
19
+ });
20
+
21
+ const plugins = await getEnabledPlugins(strapiInstance);
22
+
23
+ const env = strapiInstance.config.get('environment');
24
+ const { serverUrl, adminPath } = getConfigUrls(strapiInstance.config, true);
25
+
26
+ console.log(`Building your admin UI with ${green(env)} configuration...`);
27
+
28
+ // Always remove the .cache and build folders
29
+ // FIXME the BE should remove the build dir and the admin should only
30
+ // be responsible of removing the .cache dir.
31
+ await strapiAdmin.clean({ appDir: srcDir, buildDestDir });
32
+
33
+ // @convly shouldn't we use the app dir here?
34
+ ee({ dir: buildDestDir });
35
+
36
+ return strapiAdmin
37
+ .build({
38
+ appDir: srcDir,
39
+ buildDestDir,
40
+ // front end build env is always production for now
41
+ env: 'production',
42
+ forceBuild,
43
+ plugins,
44
+ optimize: optimization,
45
+ options: {
46
+ backend: serverUrl,
47
+ adminPath: addSlash(adminPath),
48
+ },
49
+ useTypeScript: isTSProject,
50
+ })
51
+ .then(() => {
52
+ console.log('Admin UI built successfully');
53
+ })
54
+ .catch(err => {
55
+ console.error(err);
56
+ process.exit(1);
57
+ });
58
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const buildAdmin = require('./admin');
4
+ const buildTypeScript = require('./typescript');
5
+
6
+ module.exports = {
7
+ buildAdmin,
8
+ buildTypeScript,
9
+ };
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const tsUtils = require('@strapi/typescript-utils');
6
+
7
+ const cleanupDistDirectory = async distDir => {
8
+ if (!await fs.pathExists(distDir)){
9
+ return
10
+ }
11
+
12
+ const dirContent = await fs.readdir(distDir);
13
+ const validFilenames = dirContent
14
+ // Ignore the admin build folder
15
+ .filter(filename => filename !== 'build');
16
+
17
+
18
+ for (const filename of validFilenames) {
19
+ await fs.remove(path.resolve(distDir, filename));
20
+ }
21
+ };
22
+
23
+ module.exports = async ({ srcDir, distDir, watch = false }) => {
24
+ const isTSProject = await tsUtils.isTypeScriptProject(srcDir);
25
+
26
+ if (!isTSProject) {
27
+ throw new Error(`tsconfig file not found in ${srcDir}`);
28
+ }
29
+
30
+
31
+
32
+ await cleanupDistDirectory(distDir);
33
+
34
+ return tsUtils.compile(srcDir, { watch });
35
+ };
@@ -6,114 +6,150 @@ const fs = require('fs-extra');
6
6
  const chokidar = require('chokidar');
7
7
  const execa = require('execa');
8
8
  const { getOr } = require('lodash/fp');
9
+ const tsUtils = require('@strapi/typescript-utils');
9
10
 
10
- const { createLogger } = require('@strapi/logger');
11
11
  const loadConfiguration = require('../core/app-configuration');
12
12
  const strapi = require('../index');
13
- const buildAdmin = require('./build');
13
+ const { buildTypeScript, buildAdmin } = require('./builders');
14
14
 
15
15
  /**
16
16
  * `$ strapi develop`
17
17
  *
18
18
  */
19
+
19
20
  module.exports = async function({ build, watchAdmin, polling, browser }) {
20
- const dir = process.cwd();
21
- const config = loadConfiguration(dir);
22
- const logger = createLogger(config.logger, {});
21
+ const appDir = process.cwd();
22
+
23
+ const isTSProject = await tsUtils.isTypeScriptProject(appDir);
24
+ const distDir = isTSProject ? path.join(appDir, 'dist') : appDir;
23
25
 
24
26
  try {
25
27
  if (cluster.isMaster || cluster.isPrimary) {
26
- const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config);
27
-
28
- const buildExists = fs.existsSync(path.join(dir, 'build'));
29
- // Don't run the build process if the admin is in watch mode
30
- if (build && !watchAdmin && serveAdminPanel && !buildExists) {
31
- try {
32
- await buildAdmin({ optimization: false, forceBuild: false });
33
- } catch (err) {
34
- process.exit(1);
35
- }
36
- }
37
-
38
- if (watchAdmin) {
39
- try {
40
- execa('npm', ['run', '-s', 'strapi', 'watch-admin', '--', '--browser', browser], {
41
- stdio: 'inherit',
42
- });
43
- } catch (err) {
44
- process.exit(1);
45
- }
46
- }
47
-
48
- cluster.on('message', (worker, message) => {
49
- switch (message) {
50
- case 'reload':
51
- logger.info('The server is restarting\n');
52
- worker.send('kill');
53
- break;
54
- case 'killed':
55
- cluster.fork();
56
- break;
57
- case 'stop':
58
- process.exit(1);
59
- default:
60
- return;
61
- }
28
+ return primaryProcess({
29
+ distDir,
30
+ appDir,
31
+ build,
32
+ browser,
33
+ isTSProject,
34
+ watchAdmin,
62
35
  });
63
-
64
- cluster.fork();
65
36
  }
66
37
 
67
38
  if (cluster.isWorker) {
68
- const strapiInstance = strapi({
69
- dir,
70
- autoReload: true,
71
- serveAdminPanel: watchAdmin ? false : true,
39
+ return workerProcess({ appDir, distDir, watchAdmin, polling, isTSProject });
40
+ }
41
+ } catch (e) {
42
+ console.error(e);
43
+ process.exit(1);
44
+ }
45
+ };
46
+
47
+ const primaryProcess = async ({ distDir, appDir, build, isTSProject, watchAdmin, browser }) => {
48
+ if (isTSProject) {
49
+ await buildTypeScript({ srcDir: appDir, distDir, watch: false });
50
+ }
51
+
52
+ const config = loadConfiguration(distDir);
53
+ const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config);
54
+
55
+ const buildExists = fs.existsSync(path.join(distDir, 'build'));
56
+
57
+ // Don't run the build process if the admin is in watch mode
58
+ if (build && !watchAdmin && serveAdminPanel && !buildExists) {
59
+ try {
60
+ await buildAdmin({
61
+ buildDestDir: distDir,
62
+ forceBuild: false,
63
+ isTSProject,
64
+ optimization: false,
65
+ srcDir: appDir,
72
66
  });
67
+ } catch (err) {
68
+ process.exit(1);
69
+ }
70
+ }
73
71
 
74
- const adminWatchIgnoreFiles = getOr([], 'admin.watchIgnoreFiles')(config);
75
- watchFileChanges({
76
- dir,
77
- strapiInstance,
78
- watchIgnoreFiles: adminWatchIgnoreFiles,
79
- polling,
72
+ if (watchAdmin) {
73
+ try {
74
+ execa('npm', ['run', '-s', 'strapi', 'watch-admin', '--', '--browser', browser], {
75
+ stdio: 'inherit',
80
76
  });
77
+ } catch (err) {
78
+ process.exit(1);
79
+ }
80
+ }
81
81
 
82
- process.on('message', async message => {
83
- switch (message) {
84
- case 'kill':
85
- await strapiInstance.destroy();
86
- process.send('killed');
87
- process.exit();
88
- default:
89
- // Do nothing.
82
+ cluster.on('message', async (worker, message) => {
83
+ switch (message) {
84
+ case 'reload':
85
+ if (isTSProject) {
86
+ await buildTypeScript({ srcDir: appDir, distDir, watch: false });
90
87
  }
91
- });
92
88
 
93
- return strapiInstance.start();
89
+ console.info('The server is restarting\n');
90
+
91
+ worker.send('kill');
92
+ break;
93
+ case 'killed':
94
+ cluster.fork();
95
+ break;
96
+ case 'stop':
97
+ process.exit(1);
98
+ default:
99
+ return;
94
100
  }
95
- } catch (e) {
96
- logger.error(e);
97
- process.exit(1);
98
- }
101
+ });
102
+
103
+ cluster.fork();
104
+ };
105
+
106
+ const workerProcess = ({ appDir, distDir, watchAdmin, polling, isTSProject }) => {
107
+ const strapiInstance = strapi({
108
+ distDir,
109
+ autoReload: true,
110
+ serveAdminPanel: watchAdmin ? false : true,
111
+ });
112
+
113
+ const adminWatchIgnoreFiles = strapiInstance.config.get('admin.watchIgnoreFiles', []);
114
+ watchFileChanges({
115
+ appDir,
116
+ strapiInstance,
117
+ watchIgnoreFiles: adminWatchIgnoreFiles,
118
+ polling,
119
+ isTSProject,
120
+ });
121
+
122
+ process.on('message', async message => {
123
+ switch (message) {
124
+ case 'kill':
125
+ await strapiInstance.destroy();
126
+ process.send('killed');
127
+ process.exit();
128
+ default:
129
+ // Do nothing.
130
+ }
131
+ });
132
+
133
+ return strapiInstance.start();
99
134
  };
100
135
 
101
136
  /**
102
137
  * Init file watching to auto restart strapi app
103
138
  * @param {Object} options - Options object
104
- * @param {string} options.dir - This is the path where the app is located, the watcher will watch the files under this folder
139
+ * @param {string} options.appDir - This is the path where the app is located, the watcher will watch the files under this folder
105
140
  * @param {Strapi} options.strapi - Strapi instance
106
141
  * @param {array} options.watchIgnoreFiles - Array of custom file paths that should not be watched
107
142
  */
108
- function watchFileChanges({ dir, strapiInstance, watchIgnoreFiles, polling }) {
109
- const restart = () => {
143
+ function watchFileChanges({ appDir, strapiInstance, watchIgnoreFiles, polling }) {
144
+ const restart = async () => {
110
145
  if (strapiInstance.reload.isWatching && !strapiInstance.reload.isReloading) {
111
146
  strapiInstance.reload.isReloading = true;
112
147
  strapiInstance.reload();
113
148
  }
114
149
  };
115
150
 
116
- const watcher = chokidar.watch(dir, {
151
+ // @soupette should we keep watching the dist dir (for the watch admin?)
152
+ const watcher = chokidar.watch(appDir, {
117
153
  ignoreInitial: true,
118
154
  usePolling: polling,
119
155
  ignored: [
@@ -133,6 +169,7 @@ function watchFileChanges({ dir, strapiInstance, watchIgnoreFiles, polling }) {
133
169
  '**/public/**',
134
170
  '**/*.db*',
135
171
  '**/exports/**',
172
+ '**/dist/**',
136
173
  ...watchIgnoreFiles,
137
174
  ],
138
175
  });
@@ -5,4 +5,4 @@ const strapi = require('../index');
5
5
  /**
6
6
  * `$ strapi start`
7
7
  */
8
- module.exports = () => strapi().start();
8
+ module.exports = distDir => strapi({ distDir }).start();
@@ -1,18 +1,22 @@
1
1
  'use strict';
2
2
 
3
+ const path = require('path');
3
4
  const strapiAdmin = require('@strapi/admin');
5
+ const tsUtils = require('@strapi/typescript-utils');
4
6
  const { getConfigUrls, getAbsoluteServerUrl } = require('@strapi/utils');
5
7
 
6
- const ee = require('../utils/ee');
8
+ const getEnabledPlugins = require('../core/loaders/plugins/get-enabled-plugins');
7
9
  const addSlash = require('../utils/addSlash');
8
10
  const strapi = require('../index');
9
- const getEnabledPlugins = require('../core/loaders/plugins/get-enabled-plugins');
10
11
 
11
12
  module.exports = async function({ browser }) {
12
- const dir = process.cwd();
13
+ const currentDirectory = process.cwd();
14
+
15
+ const isTSProject = await tsUtils.isTypeScriptProject(currentDirectory);
16
+ const buildDestDir = isTSProject ? path.join(currentDirectory, 'dist') : currentDirectory;
13
17
 
14
18
  const strapiInstance = strapi({
15
- dir,
19
+ distDir: buildDestDir,
16
20
  autoReload: true,
17
21
  serveAdminPanel: false,
18
22
  });
@@ -23,14 +27,12 @@ module.exports = async function({ browser }) {
23
27
 
24
28
  const adminPort = strapiInstance.config.get('admin.port', 8000);
25
29
  const adminHost = strapiInstance.config.get('admin.host', 'localhost');
26
- const adminWatchIgnoreFiles = strapiInstance.config.get('admin.watchIgnoreFiles', []);
27
30
 
28
31
  const backendURL = getAbsoluteServerUrl(strapiInstance.config, true);
29
32
 
30
- ee({ dir });
31
-
32
33
  strapiAdmin.watchAdmin({
33
- dir,
34
+ appDir: currentDirectory,
35
+ buildDestDir,
34
36
  plugins,
35
37
  port: adminPort,
36
38
  host: adminHost,
@@ -38,8 +40,7 @@ module.exports = async function({ browser }) {
38
40
  options: {
39
41
  backend: backendURL,
40
42
  adminPath: addSlash(adminPath),
41
- watchIgnoreFiles: adminWatchIgnoreFiles,
42
- features: ee.isEE ? ee.features.getEnabled() : [],
43
43
  },
44
+ useTypeScript: isTSProject,
44
45
  });
45
46
  };
@@ -4,9 +4,11 @@ const path = require('path');
4
4
  const fs = require('fs');
5
5
  const { templateConfiguration, env } = require('@strapi/utils');
6
6
 
7
+ const __importDefault = require('../../utils/import-default');
8
+
7
9
  const loadJsFile = file => {
8
10
  try {
9
- const jsModule = require(file);
11
+ const jsModule = __importDefault(require(file)).default;
10
12
 
11
13
  // call if function
12
14
  if (typeof jsModule === 'function') {
@@ -5,6 +5,7 @@ const { existsSync } = require('fs-extra');
5
5
  const _ = require('lodash');
6
6
  const fse = require('fs-extra');
7
7
  const { isKebabCase } = require('@strapi/utils');
8
+ const { importDefault } = require('../../utils');
8
9
 
9
10
  // to handle names with numbers in it we first check if it is already in kebabCase
10
11
  const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
@@ -16,18 +17,18 @@ const DEFAULT_CONTENT_TYPE = {
16
17
  };
17
18
 
18
19
  module.exports = async strapi => {
19
- if (!existsSync(strapi.dirs.api)) {
20
- throw new Error('Missing api folder. Please create one at `./src/api`');
20
+ if (!existsSync(strapi.dirs.dist.api)) {
21
+ return;
21
22
  }
22
23
 
23
- const apisFDs = await fse.readdir(strapi.dirs.api, { withFileTypes: true });
24
+ const apisFDs = await fse.readdir(strapi.dirs.dist.api, { withFileTypes: true });
24
25
  const apis = {};
25
26
 
26
27
  // only load folders
27
28
  for (const apiFD of apisFDs) {
28
29
  if (apiFD.isDirectory()) {
29
30
  const apiName = normalizeName(apiFD.name);
30
- const api = await loadAPI(join(strapi.dirs.api, apiFD.name));
31
+ const api = await loadAPI(join(strapi.dirs.dist.api, apiFD.name));
31
32
 
32
33
  apis[apiName] = api;
33
34
  }
@@ -150,7 +151,7 @@ const loadFile = file => {
150
151
 
151
152
  switch (ext) {
152
153
  case '.js':
153
- return require(file);
154
+ return importDefault(require(file)).default;
154
155
  case '.json':
155
156
  return fse.readJSON(file);
156
157
  default:
@@ -6,19 +6,20 @@ const { pathExists } = require('fs-extra');
6
6
  const loadFiles = require('../../load/load-files');
7
7
 
8
8
  module.exports = async strapi => {
9
- if (!(await pathExists(strapi.dirs.components))) {
9
+ if (!(await pathExists(strapi.dirs.dist.components))) {
10
10
  return {};
11
11
  }
12
12
 
13
- const map = await loadFiles(strapi.dirs.components, '*/*.*(js|json)');
13
+ const map = await loadFiles(strapi.dirs.dist.components, '*/*.*(js|json)');
14
14
 
15
15
  return Object.keys(map).reduce((acc, category) => {
16
16
  Object.keys(map[category]).forEach(key => {
17
17
  const schema = map[category][key];
18
18
 
19
- const filePath = join(strapi.dirs.components, category, schema.__filename__);
20
-
21
19
  if (!schema.collectionName) {
20
+ // NOTE: We're using the filepath from the app directory instead of the dist for information purpose
21
+ const filePath = join(strapi.dirs.app.components, category, schema.__filename__);
22
+
22
23
  return strapi.stopWithError(
23
24
  `Component ${key} is missing a "collectionName" property.\nVerify file ${filePath}.`
24
25
  );
@@ -3,17 +3,19 @@
3
3
  const { join, extname, basename } = require('path');
4
4
  const fse = require('fs-extra');
5
5
 
6
+ const { importDefault } = require('../../utils');
7
+
6
8
  // TODO:: allow folders with index.js inside for bigger policies
7
9
  module.exports = async function loadMiddlewares(strapi) {
8
10
  const localMiddlewares = await loadLocalMiddlewares(strapi);
9
- const internalMiddlewares = require('../../middlewares');
11
+ const internalMiddlewares = importDefault(require('../../middlewares')).default;
10
12
 
11
13
  strapi.container.get('middlewares').add(`global::`, localMiddlewares);
12
14
  strapi.container.get('middlewares').add(`strapi::`, internalMiddlewares);
13
15
  };
14
16
 
15
17
  const loadLocalMiddlewares = async strapi => {
16
- const dir = strapi.dirs.middlewares;
18
+ const dir = strapi.dirs.dist.middlewares;
17
19
 
18
20
  if (!(await fse.pathExists(dir))) {
19
21
  return {};
@@ -28,7 +30,7 @@ const loadLocalMiddlewares = async strapi => {
28
30
 
29
31
  if (fd.isFile() && extname(name) === '.js') {
30
32
  const key = basename(name, '.js');
31
- middlewares[key] = require(fullPath);
33
+ middlewares[key] = importDefault(require(fullPath)).default;
32
34
  }
33
35
  }
34
36
 
@@ -29,10 +29,15 @@ const toDetailedDeclaration = declaration => {
29
29
  let detailedDeclaration = pick(['enabled'], declaration);
30
30
  if (has('resolve', declaration)) {
31
31
  let pathToPlugin = '';
32
+ let appPathToPlugin = '';
33
+
32
34
  try {
33
35
  pathToPlugin = dirname(require.resolve(declaration.resolve));
36
+ appPathToPlugin = pathToPlugin;
34
37
  } catch (e) {
35
- pathToPlugin = resolve(strapi.dirs.root, declaration.resolve);
38
+ // FIXME: It'll break for resolve paths that are located outside the project files
39
+ pathToPlugin = resolve(strapi.dirs.dist.root, declaration.resolve);
40
+ appPathToPlugin = resolve(strapi.dirs.app.root, declaration.resolve);
36
41
 
37
42
  if (!existsSync(pathToPlugin) || !statSync(pathToPlugin).isDirectory()) {
38
43
  throw new Error(`${declaration.resolve} couldn't be resolved`);
@@ -40,6 +45,7 @@ const toDetailedDeclaration = declaration => {
40
45
  }
41
46
 
42
47
  detailedDeclaration.pathToPlugin = pathToPlugin;
48
+ detailedDeclaration.appPathToPlugin = appPathToPlugin;
43
49
  }
44
50
  return detailedDeclaration;
45
51
  };
@@ -12,9 +12,9 @@ const loadConfigFile = require('../../app-configuration/load-config-file');
12
12
  * @return {Promise<{}>}
13
13
  */
14
14
  const getUserPluginsConfig = async () => {
15
- const globalUserConfigPath = join(strapi.dirs.config, 'plugins.js');
15
+ const globalUserConfigPath = join(strapi.dirs.dist.config, 'plugins.js');
16
16
  const currentEnvUserConfigPath = join(
17
- strapi.dirs.config,
17
+ strapi.dirs.dist.config,
18
18
  'env',
19
19
  process.env.NODE_ENV,
20
20
  'plugins.js'
@@ -26,7 +26,7 @@ const defaultPlugin = {
26
26
  };
27
27
 
28
28
  const applyUserExtension = async plugins => {
29
- const extensionsDir = strapi.dirs.extensions;
29
+ const extensionsDir = strapi.dirs.dist.extensions;
30
30
  if (!(await fse.pathExists(extensionsDir))) {
31
31
  return;
32
32
  }
@@ -3,9 +3,11 @@
3
3
  const { join, extname, basename } = require('path');
4
4
  const fse = require('fs-extra');
5
5
 
6
+ const { importDefault } = require('../../utils');
7
+
6
8
  // TODO:: allow folders with index.js inside for bigger policies
7
9
  module.exports = async function loadPolicies(strapi) {
8
- const dir = strapi.dirs.policies;
10
+ const dir = strapi.dirs.dist.policies;
9
11
 
10
12
  if (!(await fse.pathExists(dir))) {
11
13
  return;
@@ -20,7 +22,7 @@ module.exports = async function loadPolicies(strapi) {
20
22
 
21
23
  if (fd.isFile() && extname(name) === '.js') {
22
24
  const key = basename(name, '.js');
23
- policies[key] = require(fullPath);
25
+ policies[key] = importDefault(require(fullPath)).default;
24
26
  }
25
27
  }
26
28
 
@@ -4,6 +4,8 @@ const { resolve } = require('path');
4
4
  const { statSync, existsSync } = require('fs');
5
5
  const { yup } = require('@strapi/utils');
6
6
 
7
+ const { importDefault } = require('../../utils');
8
+
7
9
  const srcSchema = yup
8
10
  .object()
9
11
  .shape({
@@ -18,16 +20,16 @@ const validateSrcIndex = srcIndex => {
18
20
  };
19
21
 
20
22
  module.exports = strapi => {
21
- if (!existsSync(strapi.dirs.src)) {
22
- throw new Error('Missing src folder. Please create one at `./src`');
23
+ if (!existsSync(strapi.dirs.dist.src)) {
24
+ return;
23
25
  }
24
26
 
25
- const pathToSrcIndex = resolve(strapi.dirs.src, 'index.js');
27
+ const pathToSrcIndex = resolve(strapi.dirs.dist.src, 'index.js');
26
28
  if (!existsSync(pathToSrcIndex) || statSync(pathToSrcIndex).isDirectory()) {
27
29
  return {};
28
30
  }
29
31
 
30
- const srcIndex = require(pathToSrcIndex);
32
+ const srcIndex = importDefault(require(pathToSrcIndex)).default;
31
33
 
32
34
  try {
33
35
  validateSrcIndex(srcIndex);
@@ -43,6 +43,6 @@ interface Router {
43
43
  routes: Route[];
44
44
  }
45
45
 
46
- export function createCoreRouter(uid: string, cfg: RouterConfig): () => Router;
47
- export function createCoreController(uid: string, cfg: ControllerConfig): () => Controller;
48
- export function createCoreService(uid: string, cfg: ServiceConfig): () => Service;
46
+ export function createCoreRouter(uid: string, cfg?: RouterConfig = {}): () => Router;
47
+ export function createCoreController(uid: string, cfg?: ControllerConfig = {}): () => Controller;
48
+ export function createCoreService(uid: string, cfg?: ServiceConfig = {}): () => Service;
@@ -3,6 +3,8 @@
3
3
  const path = require('path');
4
4
  const _ = require('lodash');
5
5
  const fse = require('fs-extra');
6
+
7
+ const { importDefault } = require('../utils');
6
8
  const glob = require('./glob');
7
9
  const filePathToPath = require('./filepath-to-prop-path');
8
10
 
@@ -34,7 +36,7 @@ const loadFiles = async (
34
36
  if (path.extname(absolutePath) === '.json') {
35
37
  mod = await fse.readJson(absolutePath);
36
38
  } else {
37
- mod = requireFn(absolutePath);
39
+ mod = importDefault(requireFn(absolutePath)).default;
38
40
  }
39
41
 
40
42
  Object.defineProperty(mod, '__filename__', {
@@ -15,5 +15,5 @@ const defaults = {
15
15
  module.exports = (config, { strapi }) => {
16
16
  const { maxAge, path: faviconPath } = defaultsDeep(defaults, config);
17
17
 
18
- return favicon(resolve(strapi.dirs.root, faviconPath), { maxAge });
18
+ return favicon(resolve(strapi.dirs.app.root, faviconPath), { maxAge });
19
19
  };
@@ -71,6 +71,7 @@ module.exports = (config, { strapi }) => {
71
71
  {
72
72
  method: 'GET',
73
73
  path: '/assets/images/(.*)',
74
+ // Why do we use the __dirname and not strapi.dirs here? @alexandrebodin
74
75
  handler: serveStatic(path.resolve(__dirname, 'assets/images'), {
75
76
  maxage: maxAge,
76
77
  defer: true,
@@ -80,7 +81,7 @@ module.exports = (config, { strapi }) => {
80
81
  {
81
82
  method: 'GET',
82
83
  path: '/(.*)',
83
- handler: koaStatic(strapi.dirs.public, {
84
+ handler: koaStatic(strapi.dirs.app.public, {
84
85
  maxage: maxAge,
85
86
  defer: true,
86
87
  }),
@@ -12,7 +12,7 @@ module.exports = strapi => {
12
12
 
13
13
  const normalizedPath = path.normalize(filePath).replace(/^\/?(\.\/|\.\.\/)+/, '');
14
14
 
15
- return path.resolve(strapi.dirs.root, normalizedPath);
15
+ return path.resolve(strapi.dirs.app.root, normalizedPath);
16
16
  }
17
17
 
18
18
  const strapiFS = {
@@ -90,7 +90,7 @@ const hashProject = strapi =>
90
90
 
91
91
  const hashDep = strapi => {
92
92
  const depStr = JSON.stringify(strapi.config.info.dependencies);
93
- const readmePath = path.join(strapi.dirs.root, 'README.md');
93
+ const readmePath = path.join(strapi.dirs.app.root, 'README.md');
94
94
 
95
95
  try {
96
96
  if (fs.existsSync(readmePath)) {
@@ -112,7 +112,7 @@ const resolveCustomMiddleware = (resolve, strapi) => {
112
112
  modulePath = require.resolve(resolve);
113
113
  } catch (error) {
114
114
  if (error.code === 'MODULE_NOT_FOUND') {
115
- modulePath = path.resolve(strapi.dirs.root, resolve);
115
+ modulePath = path.resolve(strapi.dirs.dist.root, resolve);
116
116
  } else {
117
117
  throw error;
118
118
  }
@@ -2,16 +2,29 @@
2
2
 
3
3
  const { join } = require('path');
4
4
 
5
- const getDirs = root => ({
6
- root,
7
- src: join(root, 'src'),
8
- api: join(root, 'src', 'api'),
9
- components: join(root, 'src', 'components'),
10
- extensions: join(root, 'src', 'extensions'),
11
- policies: join(root, 'src', 'policies'),
12
- middlewares: join(root, 'src', 'middlewares'),
13
- config: join(root, 'config'),
14
- public: join(root, 'public'),
5
+ const getDirs = ({ appDir, distDir }) => ({
6
+ dist: {
7
+ root: distDir,
8
+ src: join(distDir, 'src'),
9
+ api: join(distDir, 'src', 'api'),
10
+ components: join(distDir, 'src', 'components'),
11
+ extensions: join(distDir, 'src', 'extensions'),
12
+ policies: join(distDir, 'src', 'policies'),
13
+ middlewares: join(distDir, 'src', 'middlewares'),
14
+ config: join(distDir, 'config'),
15
+ public: join(distDir, 'public'),
16
+ },
17
+ app: {
18
+ root: appDir,
19
+ src: join(appDir, 'src'),
20
+ api: join(appDir, 'src', 'api'),
21
+ components: join(appDir, 'src', 'components'),
22
+ extensions: join(appDir, 'src', 'extensions'),
23
+ policies: join(appDir, 'src', 'policies'),
24
+ middlewares: join(appDir, 'src', 'middlewares'),
25
+ config: join(appDir, 'config'),
26
+ public: join(appDir, 'public'),
27
+ },
15
28
  });
16
29
 
17
30
  module.exports = getDirs;
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const __importDefault =
4
+ (this && this.__importDefault) ||
5
+ function(mod) {
6
+ return mod && mod.__esModule ? mod : { default: mod };
7
+ };
8
+
9
+ module.exports = __importDefault;
@@ -3,9 +3,11 @@
3
3
  const openBrowser = require('./open-browser');
4
4
  const isInitialized = require('./is-initialized');
5
5
  const getDirs = require('./get-dirs');
6
+ const importDefault = require('./import-default');
6
7
 
7
8
  module.exports = {
8
9
  isInitialized,
9
10
  openBrowser,
10
11
  getDirs,
12
+ importDefault,
11
13
  };
@@ -38,7 +38,7 @@ const createUpdateNotifier = strapi => {
38
38
  config = new Configstore(
39
39
  pkg.name,
40
40
  {},
41
- { configPath: path.join(strapi.dirs.root, '.strapi-updater.json') }
41
+ { configPath: path.join(strapi.dirs.app.root, '.strapi-updater.json') }
42
42
  );
43
43
  } catch {
44
44
  // we don't have write access to the file system
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/strapi",
3
- "version": "4.2.0-alpha.0",
3
+ "version": "4.2.0-alpha.6",
4
4
  "description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
5
5
  "keywords": [
6
6
  "strapi",
@@ -80,17 +80,16 @@
80
80
  "dependencies": {
81
81
  "@koa/cors": "3.1.0",
82
82
  "@koa/router": "10.1.1",
83
- "@strapi/admin": "4.2.0-alpha.0",
84
- "@strapi/database": "4.2.0-alpha.0",
85
- "@strapi/generate-new": "4.2.0-alpha.0",
86
- "@strapi/generators": "4.2.0-alpha.0",
87
- "@strapi/logger": "4.2.0-alpha.0",
88
- "@strapi/plugin-content-manager": "4.2.0-alpha.0",
89
- "@strapi/plugin-content-type-builder": "4.2.0-alpha.0",
90
- "@strapi/plugin-email": "4.2.0-alpha.0",
91
- "@strapi/plugin-upload": "4.2.0-alpha.0",
92
- "@strapi/utils": "4.2.0-alpha.0",
93
- "bcryptjs": "2.4.3",
83
+ "@strapi/database": "4.2.0-alpha.6",
84
+ "@strapi/generate-new": "4.2.0-alpha.6",
85
+ "@strapi/generators": "4.2.0-alpha.6",
86
+ "@strapi/logger": "4.2.0-alpha.6",
87
+ "@strapi/plugin-content-manager": "4.2.0-alpha.6",
88
+ "@strapi/plugin-content-type-builder": "4.2.0-alpha.6",
89
+ "@strapi/plugin-email": "4.2.0-alpha.6",
90
+ "@strapi/plugin-upload": "4.2.0-alpha.6",
91
+ "@strapi/typescript-utils": "4.2.0-alpha.6",
92
+ "@strapi/utils": "4.2.0-alpha.6",
94
93
  "boxen": "5.1.2",
95
94
  "chalk": "4.1.2",
96
95
  "chokidar": "3.5.2",
@@ -130,11 +129,12 @@
130
129
  "uuid": "^3.3.2"
131
130
  },
132
131
  "devDependencies": {
133
- "supertest": "^6.1.6"
132
+ "supertest": "^6.1.6",
133
+ "typescript": "4.6.2"
134
134
  },
135
135
  "engines": {
136
136
  "node": ">=12.22.0 <=16.x.x",
137
137
  "npm": ">=6.0.0"
138
138
  },
139
- "gitHead": "0e1f1ae08565a5f2427753582f37645a43c00cb2"
139
+ "gitHead": "0ec01b421912ac3248487ec7c5ad97cf16be6284"
140
140
  }