Change load algorithm to make plugins folder optional

This commit is contained in:
Alexandre Bodin 2019-05-02 17:51:58 +02:00
parent e4d025c6a1
commit 932fb8eef4
15 changed files with 194 additions and 179 deletions

View File

@ -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();

View File

@ -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": "",

View File

@ -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();

View File

@ -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),

View File

@ -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,

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
};

View File

@ -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
},
});

View File

@ -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==