From 932fb8eef402cdf032c165dff7d777b58a0763fd Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Thu, 2 May 2019 17:51:58 +0200 Subject: [PATCH] Change load algorithm to make plugins folder optional --- examples/getstarted/server.js | 14 ---- packages/strapi-admin/package.json | 3 + packages/strapi-generate-plugin/lib/before.js | 20 +++-- packages/strapi/lib/Strapi.js | 60 ++++++++------ packages/strapi/lib/core/index.js | 10 ++- packages/strapi/lib/core/load-admin.js | 17 ++++ packages/strapi/lib/core/load-apis.js | 54 +++---------- packages/strapi/lib/core/load-config.js | 16 ++++ packages/strapi/lib/core/load-configs.js | 81 ------------------- packages/strapi/lib/core/load-extensions.js | 7 ++ packages/strapi/lib/core/load-hooks.js | 2 + packages/strapi/lib/core/load-middlewares.js | 2 + packages/strapi/lib/core/load-plugins.js | 63 +++++++++++++++ packages/strapi/lib/load/load-config-files.js | 2 +- yarn.lock | 22 +++-- 15 files changed, 194 insertions(+), 179 deletions(-) delete mode 100755 examples/getstarted/server.js create mode 100644 packages/strapi/lib/core/load-admin.js create mode 100644 packages/strapi/lib/core/load-config.js delete mode 100644 packages/strapi/lib/core/load-configs.js create mode 100644 packages/strapi/lib/core/load-plugins.js diff --git a/examples/getstarted/server.js b/examples/getstarted/server.js deleted file mode 100755 index e76ad3bdec..0000000000 --- a/examples/getstarted/server.js +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -/** - * Use `server.js` to run your application without `$ strapi start`. - * To start the server, run: `$ npm start`. - * - * This is handy in situations where the Strapi CLI is not relevant or useful. - */ -const strapi = require('strapi'); - -strapi({ - appPath: __dirname, -}).start(); diff --git a/packages/strapi-admin/package.json b/packages/strapi-admin/package.json index 1299f8225d..639e4082d2 100644 --- a/packages/strapi-admin/package.json +++ b/packages/strapi-admin/package.json @@ -81,6 +81,9 @@ "video-react": "^0.13.2", "webpack": "^4.29.6" }, + "peerDependencies": { + "strapi-admin": "3.0.0-beta.0" + }, "author": { "name": "Strapi", "email": "", diff --git a/packages/strapi-generate-plugin/lib/before.js b/packages/strapi-generate-plugin/lib/before.js index adf506afdb..84a7b3af9c 100644 --- a/packages/strapi-generate-plugin/lib/before.js +++ b/packages/strapi-generate-plugin/lib/before.js @@ -24,13 +24,13 @@ module.exports = (scope, cb) => { // `scope.args` are the raw command line arguments. _.defaults(scope, { - id: _.trim(_.deburr(scope.id)) + id: _.trim(_.deburr(scope.id)), }); // Determine default values based on the available scope. _.defaults(scope, { globalID: _.upperFirst(_.camelCase(scope.id)), - ext: '.js' + ext: '.js', }); // Plugin info. @@ -38,23 +38,29 @@ module.exports = (scope, cb) => { name: scope.args.name || scope.id, author: scope.author || 'A Strapi developer', email: scope.email || '', - year: (new Date()).getFullYear(), - license: 'MIT' + year: new Date().getFullYear(), + license: 'MIT', }); // Take another pass to take advantage of the defaults absorbed in previous passes. _.defaults(scope, { - filename: `${scope.globalID}${scope.ext}` + filename: `${scope.globalID}${scope.ext}`, }); // Humanize output. _.defaults(scope, { humanizeId: scope.id.toLowerCase(), - humanizedPath: '`./plugins`' + humanizedPath: '`./plugins`', }); + const pluginDir = path.resolve(scope.rootPath, 'plugins'); + fs.ensureDirSync(pluginDir); + // Copy the admin files. - fs.copySync(path.resolve(__dirname, '..', 'files'), path.resolve(scope.rootPath, 'plugins', scope.humanizeId)); + fs.copySync( + path.resolve(__dirname, '..', 'files'), + path.resolve(scope.rootPath, 'plugins', scope.humanizeId) + ); // Trigger callback with no error to proceed. return cb.success(); diff --git a/packages/strapi/lib/Strapi.js b/packages/strapi/lib/Strapi.js index 1370122d51..364f9ad6ed 100644 --- a/packages/strapi/lib/Strapi.js +++ b/packages/strapi/lib/Strapi.js @@ -10,8 +10,10 @@ const _ = require('lodash'); const { logger, models } = require('strapi-utils'); const utils = require('./utils'); const { - loadConfigs, + loadConfig, loadApis, + loadAdmin, + loadPlugins, loadMiddlewares, loadHooks, bootstrap, @@ -22,9 +24,7 @@ const initializeMiddlewares = require('./middlewares'); const initializeHooks = require('./hooks'); const createStrapiFs = require('./core/fs'); const getPrefixedDeps = require('./utils/get-prefixed-dependencies'); -const runChecks = require('./utils/run-checks'); const defaultQueries = require('./core-api/queries'); -const fs = require('fs-extra'); /** * Construct an Strapi instance. @@ -63,13 +63,13 @@ class Strapi extends EventEmitter { // Expose `plugin`. this.plugins = {}; - const rootPath = appPath || process.cwd(); - const pkgJSON = require(path.resolve(rootPath, 'package.json')); + this.dir = appPath || process.cwd(); + const pkgJSON = require(path.resolve(this.dir, 'package.json')); // Default configurations. this.config = { launchedAt: Date.now(), - appPath: rootPath, + appPath: this.dir, host: process.env.HOST || process.env.HOSTNAME || 'localhost', port: process.env.PORT || 1337, environment: _.toLower(process.env.NODE_ENV) || 'development', @@ -100,16 +100,6 @@ class Strapi extends EventEmitter { }; this.fs = createStrapiFs(this); - this.runChecks(); - } - - runChecks() { - try { - runChecks(this.config); - } catch (err) { - this.log.error(err.message); - process.exit(1); - } } async start(cb) { @@ -242,23 +232,36 @@ class Strapi extends EventEmitter { } }); - // load configs - _.merge(this, await loadConfigs(this.config)); - // load apis - _.merge(this, await loadApis(this.config)); + const [ + config, + api, + admin, + plugins, + { middlewares, koaMiddlewares }, + hook, + extensions, + ] = await Promise.all([ + loadConfig(this), + loadApis(this), + loadAdmin(this), + loadPlugins(this), + loadMiddlewares(this), + loadHooks(this.config), + loadExtensions(this.config), + ]); - // load middlewares - const { middlewares, koaMiddlewares } = await loadMiddlewares(this); + _.merge(this.config, config); + + this.api = api; + this.admin = admin; + this.plugins = plugins; this.middleware = middlewares; this.koaMiddlewares = koaMiddlewares; - - // load hooks - this.hook = await loadHooks(this.config); + this.hook = hook; /** * Handle plugin extensions */ - const extensions = await loadExtensions(this.config); // merge extensions config folders _.mergeWith(this.plugins, extensions.merges, (objValue, srcValue) => { if (_.isArray(srcValue) && _.isArray(objValue)) { @@ -271,12 +274,17 @@ class Strapi extends EventEmitter { ); // Populate AST with configurations. + await bootstrap(this); + // Usage. await utils.usage(this.config); + // Init core store initCoreStore(this); + // Initialize hooks and middlewares. + await Promise.all([ initializeMiddlewares.call(this), initializeHooks.call(this), diff --git a/packages/strapi/lib/core/index.js b/packages/strapi/lib/core/index.js index db879dd710..c19a95396b 100644 --- a/packages/strapi/lib/core/index.js +++ b/packages/strapi/lib/core/index.js @@ -1,7 +1,9 @@ 'use strict'; -const loadConfigs = require('./load-configs'); +const loadConfig = require('./load-config'); const loadApis = require('./load-apis'); +const loadAdmin = require('./load-admin'); +const loadPlugins = require('./load-plugins'); const loadMiddlewares = require('./load-middlewares'); const loadExtensions = require('./load-extensions'); const loadHooks = require('./load-hooks'); @@ -9,10 +11,12 @@ const bootstrap = require('./bootstrap'); const initCoreStore = require('./init-core-store'); module.exports = { - loadConfigs, + loadConfig, + loadApis, + loadAdmin, + loadPlugins, loadMiddlewares, loadHooks, - loadApis, loadExtensions, bootstrap, initCoreStore, diff --git a/packages/strapi/lib/core/load-admin.js b/packages/strapi/lib/core/load-admin.js new file mode 100644 index 0000000000..0b09ada3e5 --- /dev/null +++ b/packages/strapi/lib/core/load-admin.js @@ -0,0 +1,17 @@ +'use strict'; + +const _ = require('lodash'); +const findPackagePath = require('../load/package-path'); +const loadFiles = require('../load/load-files'); +const loadConfig = require('../load/load-config-files'); + +module.exports = async () => { + const adminPath = findPackagePath('strapi-admin'); + + const [files, config] = await Promise.all([ + loadFiles(adminPath, '!(config|node_modules|scripts)/*.*(js|json)'), + loadConfig(adminPath), + ]); + + return Object.assign({}, config, files); +}; diff --git a/packages/strapi/lib/core/load-apis.js b/packages/strapi/lib/core/load-apis.js index d34556e082..0f7f9857d2 100644 --- a/packages/strapi/lib/core/load-apis.js +++ b/packages/strapi/lib/core/load-apis.js @@ -1,52 +1,22 @@ 'use strict'; -const path = require('path'); +const { join } = require('path'); +const { existsSync } = require('fs-extra'); const _ = require('lodash'); -const findPackagePath = require('../load/package-path'); const loadFiles = require('../load/load-files'); +const loadConfig = require('../load/load-config-files'); -/** - * Loads the apis from the different possible locations - */ -module.exports = async function({ appPath, installedPlugins }) { - const [api, admin, plugins, localPlugins] = await Promise.all([ - loadLocalApis(appPath), - loadAdminApis(), - loadPluginsApis(installedPlugins), - loadLocalPluginsApis(appPath), - ]); +module.exports = async ({ dir }) => { + const apiDir = join(dir, 'api'); - return { - api, - admin, - plugins: _.merge(plugins, localPlugins), - }; -}; - -const loadLocalApis = appPath => - loadFiles(path.resolve(appPath, 'api'), '*/!(config)/*.*(js|json)'); - -const loadAdminApis = () => - loadFiles( - findPackagePath('strapi-admin'), - '!(config|node_modules|scripts)//*.*(js|json)' - ); - -const loadLocalPluginsApis = appPath => - loadFiles(path.resolve(appPath, 'plugins'), '{*/!(config)/*.*(js|json),*/package.json}'); - -const loadPluginsApis = async installedPlugins => { - let plugins = {}; - for (let plugin of installedPlugins) { - const pluginPath = findPackagePath(`strapi-plugin-${plugin}`); - - const result = await loadFiles( - pluginPath, - '{!(config|node_modules|test)//*.*(js|json),package.json}' + if (!existsSync(apiDir)) { + throw new Error( + `Missing api folder. Please create one in your app root directory` ); - - _.set(plugins, plugin, result); } - return plugins; + const apis = await loadFiles(apiDir, '*/!(config)/**/*.*(js|json)'); + const apiConfigs = await loadConfig(apiDir, '*/config/**/*.*(js|json)'); + + return _.merge(apis, apiConfigs); }; diff --git a/packages/strapi/lib/core/load-config.js b/packages/strapi/lib/core/load-config.js new file mode 100644 index 0000000000..7a3bbe8fc9 --- /dev/null +++ b/packages/strapi/lib/core/load-config.js @@ -0,0 +1,16 @@ +'use strict'; + +const { join } = require('path'); +const { existsSync } = require('fs-extra'); +const loadConfig = require('../load/load-config-files'); + +module.exports = async ({ dir }) => { + if (!existsSync(join(dir, 'config'))) { + throw new Error( + `Missing config folder. Please create one in your app root directory` + ); + } + + const { config } = await loadConfig(dir); + return config; +}; diff --git a/packages/strapi/lib/core/load-configs.js b/packages/strapi/lib/core/load-configs.js deleted file mode 100644 index f1dfe9245a..0000000000 --- a/packages/strapi/lib/core/load-configs.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - -const path = require('path'); -const _ = require('lodash'); -const fs = require('fs-extra'); - -const findPackagePath = require('../load/package-path'); -const loadConfig = require('../load/load-config-files'); - -/** - * Load config files from the different possible locations - */ -module.exports = async ({ appPath, installedPlugins }) => { - const [config, admin, api, plugins, localPlugins] = await Promise.all([ - loadAppConfig(appPath), - loadAdminConfig(), - loadApisConfig(appPath), - loadPluginsConfig(installedPlugins), - loadLocalPluginsConfig(appPath), - ]); - - return { - config, - admin, - api, - plugins: _.merge(plugins, localPlugins), - }; -}; - - -// Loads an app config folder -const loadAppConfig = async appPath => { - const { config } = await loadConfig(appPath); - return config; -}; - -// Loads the strapi-admin config folder -const loadAdminConfig = () => loadConfig(findPackagePath('strapi-admin')); - -// Loads every apis config folder -const loadApisConfig = async appPath => { - let apis = {}; - const apisDir = path.resolve(appPath, 'api'); - const apiNames = await fs.readdir(apisDir); - - for (let apiDir of apiNames) { - const apiConfig = await loadConfig(path.resolve(apisDir, apiDir)); - - _.set(apis, apiDir, apiConfig); - } - - return apis; -}; - -const loadLocalPluginsConfig = async appPath => { - let localPlugins = {}; - const pluginsDir = path.resolve(appPath, 'plugins'); - const pluginsName = await fs.readdir(pluginsDir); - - for (let pluginDir of pluginsName) { - const pluginsConfig = await loadConfig(path.resolve(pluginsDir, pluginDir)); - - _.set(localPlugins, pluginDir, pluginsConfig); - } - - return localPlugins; -}; - -// Loads installed plugins config -const loadPluginsConfig = async pluginsNames => { - let plugins = {}; - for (let plugin of pluginsNames) { - const pluginConfig = await loadConfig( - findPackagePath(`strapi-plugin-${plugin}`) - ); - - _.set(plugins, plugin, pluginConfig); - } - - return plugins; -}; diff --git a/packages/strapi/lib/core/load-extensions.js b/packages/strapi/lib/core/load-extensions.js index 719dd0ed05..6f58f8d025 100644 --- a/packages/strapi/lib/core/load-extensions.js +++ b/packages/strapi/lib/core/load-extensions.js @@ -1,6 +1,7 @@ 'use strict'; const path = require('path'); +const { existsSync } = require('fs-extra'); const _ = require('lodash'); const loadConfig = require('../load/load-config-files'); const loadFiles = require('../load/load-files'); @@ -13,6 +14,12 @@ const filePathToPath = require('../load/filepath-to-prop-path'); module.exports = async function({ appPath }) { const extensionsDir = path.resolve(appPath, 'extensions'); + if (!existsSync(extensionsDir)) { + throw new Error( + `Missing extension folder. Please create one in your app root directory` + ); + } + const configs = await loadConfig(extensionsDir, '*/config/**/*.+(js|json)'); const controllersAndServices = await loadFiles( extensionsDir, diff --git a/packages/strapi/lib/core/load-hooks.js b/packages/strapi/lib/core/load-hooks.js index f84287b271..adc019e341 100644 --- a/packages/strapi/lib/core/load-hooks.js +++ b/packages/strapi/lib/core/load-hooks.js @@ -52,6 +52,8 @@ const loadPluginsHooks = async (plugins, hooks) => { const loadLocalPluginsHooks = async (appPath, hooks) => { const pluginsDir = path.resolve(appPath, 'plugins'); + if (!fs.existsSync(pluginsDir)) return; + const pluginsNames = await fs.readdir(pluginsDir); for (let pluginName of pluginsNames) { diff --git a/packages/strapi/lib/core/load-middlewares.js b/packages/strapi/lib/core/load-middlewares.js index 400f49357e..a60ef5c5bf 100644 --- a/packages/strapi/lib/core/load-middlewares.js +++ b/packages/strapi/lib/core/load-middlewares.js @@ -99,6 +99,8 @@ const createLoaders = strapi => { const loadLocalPluginsMiddlewares = async (appPath, middlewares) => { const pluginsDir = path.resolve(appPath, 'plugins'); + if (!fs.existsSync(pluginsDir)) return; + const pluginsNames = await fs.readdir(pluginsDir); for (let pluginFolder of pluginsNames) { diff --git a/packages/strapi/lib/core/load-plugins.js b/packages/strapi/lib/core/load-plugins.js new file mode 100644 index 0000000000..f7114ed51c --- /dev/null +++ b/packages/strapi/lib/core/load-plugins.js @@ -0,0 +1,63 @@ +'use strict'; + +const { join } = require('path'); +const { existsSync } = require('fs-extra'); +const _ = require('lodash'); +const findPackagePath = require('../load/package-path'); +const loadFiles = require('../load/load-files'); +const loadConfig = require('../load/load-config-files'); + +module.exports = async ({ dir, config }) => { + const localPlugins = await loadLocalPlugins({ dir }); + const plugins = await loadPlugins({ + installedPlugins: config.installedPlugins, + }); + + const pluginsIntersection = _.intersection( + Object.keys(localPlugins), + Object.keys(plugins) + ); + + if (pluginsIntersection.length > 0) { + throw new Error( + `You have some local plugins with the same name as npm installed plugins:\n${pluginsIntersection + .map(p => `- ${p}`) + .join('\n')}` + ); + } + + // check for conflicts + return _.merge(plugins, localPlugins); +}; + +const loadLocalPlugins = async ({ dir }) => { + const pluginsDir = join(dir, 'plugins'); + + if (!existsSync(pluginsDir)) return {}; + + const [files, configs] = await Promise.all([ + loadFiles(pluginsDir, '{*/!(config)/*.*(js|json),*/package.json}'), + loadConfig(pluginsDir, '*/config/**/*.+(js|json)'), + ]); + + return _.merge(files, configs); +}; + +const loadPlugins = async ({ installedPlugins }) => { + let plugins = {}; + + for (let plugin of installedPlugins) { + const pluginPath = findPackagePath(`strapi-plugin-${plugin}`); + + const files = await loadFiles( + pluginPath, + '{!(config|node_modules|test)//*.*(js|json),package.json}' + ); + + const conf = await loadConfig(pluginPath); + + _.set(plugins, plugin, _.assign({}, conf, files)); + } + + return plugins; +}; diff --git a/packages/strapi/lib/load/load-config-files.js b/packages/strapi/lib/load/load-config-files.js index 2f67d278d9..80813fc04d 100644 --- a/packages/strapi/lib/load/load-config-files.js +++ b/packages/strapi/lib/load/load-config-files.js @@ -12,7 +12,7 @@ const laodConfigFiles = (dir, pattern = 'config/**/*.+(js|json)') => shouldUseFileNameAsKey, globArgs: { // used to load .init.json at first startup - dot: true, + dot: true }, }); diff --git a/yarn.lock b/yarn.lock index b8fe256d0b..8673e09fb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7704,7 +7704,7 @@ fs-extra@4.0.1: jsonfile "^3.0.0" universalify "^0.1.0" -fs-extra@^4.0.0, fs-extra@^4.0.1, fs-extra@^4.0.2: +fs-extra@^4.0.0, fs-extra@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== @@ -9194,7 +9194,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -14978,7 +14978,7 @@ react-dnd@^7.4.5: invariant "^2.1.0" shallowequal "^1.1.0" -react-dom@^16.5.2: +react-dom@^16.0.0, react-dom@^16.5.2: version "16.8.6" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== @@ -15064,6 +15064,18 @@ react-redux@^5.1.1: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" +react-redux@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.0.3.tgz#983c5a6de81cb1e696bd1c090ba826545f9170f1" + integrity sha512-vYZA7ftOYlDk3NetitsI7fLjryt/widNl1SLXYvFenIpm7vjb4ryK0EeFrgn62usg5fYkyIAWNUPKnwWPevKLg== + dependencies: + "@babel/runtime" "^7.4.3" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.6" + react-router-dom@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.0.tgz#542a9b86af269a37f0b87218c4c25ea8dcf0c073" @@ -15120,7 +15132,7 @@ react-test-renderer@^16.0.0-0: react-is "^16.8.6" scheduler "^0.13.6" -react-transition-group@^2.2.1, react-transition-group@^2.9.0: +react-transition-group@^2.2.1, react-transition-group@^2.5.0, react-transition-group@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== @@ -15130,7 +15142,7 @@ react-transition-group@^2.2.1, react-transition-group@^2.9.0: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" -react@^16.5.2: +react@^16.0.0, react@^16.5.2: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==