diff --git a/packages/core/admin/index.js b/packages/core/admin/index.js index 573a1a5c62..08b41174d6 100644 --- a/packages/core/admin/index.js +++ b/packages/core/admin/index.js @@ -9,7 +9,7 @@ const chalk = require('chalk'); const chokidar = require('chokidar'); const getWebpackConfig = require('./webpack.config'); -const getPkgPath = name => path.dirname(require.resolve(`${name}/package.json`)); +const getPkgPath = (name) => path.dirname(require.resolve(`${name}/package.json`)); function getCustomWebpackConfig(dir, config) { const adminConfigPath = path.join(dir, 'src', 'admin', 'webpack.config.js'); @@ -35,6 +35,10 @@ function getCustomWebpackConfig(dir, config) { } async function build({ plugins, dir, env, options, optimize }) { + if (!(await shouldBuildAdmin({ dir, plugins }))) { + return; + } + // Create the cache dir containing the front-end files. await createCacheDir({ dir, plugins }); @@ -48,7 +52,7 @@ async function build({ plugins, dir, env, options, optimize }) { ceRoot: path.resolve(cacheDir, 'admin', 'src'), }; - const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin); + const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin); const config = getCustomWebpackConfig(dir, { entry, @@ -161,11 +165,11 @@ async function createCacheDir({ dir, plugins }) { const cacheDir = path.resolve(dir, '.cache'); const pluginsWithFront = Object.keys(plugins) - .filter(pluginName => { + .filter((pluginName) => { const pluginInfo = plugins[pluginName]; return fs.existsSync(path.resolve(pluginInfo.pathToPlugin, 'strapi-admin.js')); }) - .map(name => ({ name, ...plugins[name] })); + .map((name) => ({ name, ...plugins[name] })); // create .cache dir await fs.emptyDir(cacheDir); @@ -206,7 +210,7 @@ async function watchAdmin({ plugins, dir, host, port, browser, options }) { ceRoot: path.resolve(cacheDir, 'admin', 'src'), }; - const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin); + const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin); const args = { entry, @@ -243,7 +247,7 @@ async function watchAdmin({ plugins, dir, host, port, browser, options }) { const server = new WebpackDevServer(opts, webpack(webpackConfig)); - server.start(port, host, function(err) { + server.start(port, host, function (err) { if (err) { console.log(err); } @@ -304,6 +308,40 @@ async function watchFiles(dir) { }); } +const hasCustomAdminCode = (dir) => { + const customAdminPath = path.join(dir, 'src/admin'); + + if (!fs.pathExistsSync(customAdminPath)) { + return false; + } + + const files = fs.readdirSync(customAdminPath); + + return files.length > 0; +}; + +const DEFAULT_PLUGINS = [ + 'content-type-builder', + 'content-manager', + 'upload', + 'email', + 'i18n', + 'users-permissions', +]; + +const hasNonDefaultPlugins = (plugins) => { + const diff = _.difference(Object.keys(plugins), DEFAULT_PLUGINS); + + return diff.length > 0; +}; + +async function shouldBuildAdmin({ dir, plugins }) { + const appHasCustomAdminCode = hasCustomAdminCode(dir); + const appHasNonDefaultPlugins = hasNonDefaultPlugins(plugins); + + return appHasCustomAdminCode || appHasNonDefaultPlugins; +} + module.exports = { clean, build, diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json index ed20de2568..cdb8c30320 100644 --- a/packages/core/admin/package.json +++ b/packages/core/admin/package.json @@ -69,6 +69,7 @@ "jsonwebtoken": "8.5.1", "koa-compose": "4.1.0", "koa-passport": "4.1.4", + "koa-static": "5.0.0", "lodash": "4.17.21", "markdown-it": "^12.0.6", "markdown-it-abbr": "^1.0.4", diff --git a/packages/core/admin/server/register.js b/packages/core/admin/server/register.js index 91cfa51d97..24dfb85b2b 100644 --- a/packages/core/admin/server/register.js +++ b/packages/core/admin/server/register.js @@ -1,5 +1,6 @@ 'use strict'; +const serveAdmin = require('./routes/serve-admin'); const adminAuthStrategy = require('./strategies/admin'); const apiTokenAuthStrategy = require('./strategies/api-token'); @@ -9,4 +10,8 @@ module.exports = ({ strapi }) => { strapi.server.api('admin').use(passportMiddleware); strapi.container.get('auth').register('admin', adminAuthStrategy); strapi.container.get('auth').register('content-api', apiTokenAuthStrategy); + + if (strapi.config.serveAdminPanel) { + serveAdmin({ strapi }); + } }; diff --git a/packages/core/admin/server/routes/serve-admin.js b/packages/core/admin/server/routes/serve-admin.js new file mode 100644 index 0000000000..5f2b4b5c30 --- /dev/null +++ b/packages/core/admin/server/routes/serve-admin.js @@ -0,0 +1,59 @@ +'use strict'; + +const path = require('path'); +const fse = require('fs-extra'); +const koaStatic = require('koa-static'); + +const serveAdmin = ({ strapi }) => { + let buildDir = path.resolve(strapi.dirs.root, 'build'); + + if (!fse.pathExistsSync(buildDir)) { + buildDir = path.resolve(__dirname, '../../build'); + } + + const serveAdmin = async (ctx, next) => { + await next(); + + if (ctx.method !== 'HEAD' && ctx.method !== 'GET') { + return; + } + + if (ctx.body != null || ctx.status !== 404) { + return; + } + + ctx.type = 'html'; + ctx.body = fse.createReadStream(path.join(buildDir + '/index.html')); + }; + + strapi.server.routes([ + { + method: 'GET', + path: `${strapi.config.admin.path}/:path*`, + handler: [ + serveAdmin, + serveStatic(buildDir, { maxage: 60000, defer: false, index: 'index.html' }), + ], + config: { auth: false }, + }, + ]); +}; + +// serveStatic is not supposed to be used to serve a folder that have sub-folders +const serveStatic = (filesDir, koaStaticOptions = {}) => { + const serve = koaStatic(filesDir, koaStaticOptions); + + return async (ctx, next) => { + const prev = ctx.path; + const newPath = path.basename(ctx.path); + ctx.path = newPath; + await serve(ctx, async () => { + ctx.path = prev; + await next(); + ctx.path = newPath; + }); + ctx.path = prev; + }; +}; + +module.exports = serveAdmin; diff --git a/packages/core/strapi/lib/commands/develop.js b/packages/core/strapi/lib/commands/develop.js index f484bd8fe4..caab900054 100644 --- a/packages/core/strapi/lib/commands/develop.js +++ b/packages/core/strapi/lib/commands/develop.js @@ -15,29 +15,28 @@ const strapi = require('../index'); * `$ strapi develop` * */ -module.exports = async function({ build, watchAdmin, polling, browser }) { +module.exports = async function ({ build, watchAdmin, polling, browser }) { const dir = process.cwd(); const config = loadConfiguration(dir); const logger = createLogger(config.logger, {}); - const adminWatchIgnoreFiles = getOr([], 'admin.watchIgnoreFiles')(config); - const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config); - - const buildExists = fs.existsSync(path.join(dir, 'build')); - // Don't run the build process if the admin is in watch mode - if (build && !watchAdmin && serveAdminPanel && !buildExists) { - try { - execa.sync('npm run -s build -- --no-optimization', { - stdio: 'inherit', - shell: true, - }); - } catch (err) { - process.exit(1); - } - } - try { if (cluster.isMaster) { + const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config); + + const buildExists = fs.existsSync(path.join(dir, 'build')); + // Don't run the build process if the admin is in watch mode + if (build && !watchAdmin && serveAdminPanel && !buildExists) { + try { + execa.sync('npm run -s build -- --no-optimization', { + stdio: 'inherit', + shell: true, + }); + } catch (err) { + process.exit(1); + } + } + if (watchAdmin) { try { execa('npm', ['run', '-s', 'strapi', 'watch-admin', '--', '--browser', browser], { @@ -74,6 +73,7 @@ module.exports = async function({ build, watchAdmin, polling, browser }) { serveAdminPanel: watchAdmin ? false : true, }); + const adminWatchIgnoreFiles = getOr([], 'admin.watchIgnoreFiles')(config); watchFileChanges({ dir, strapiInstance, @@ -81,7 +81,7 @@ module.exports = async function({ build, watchAdmin, polling, browser }) { polling, }); - process.on('message', async message => { + process.on('message', async (message) => { switch (message) { case 'kill': await strapiInstance.destroy(); @@ -140,15 +140,15 @@ function watchFileChanges({ dir, strapiInstance, watchIgnoreFiles, polling }) { }); watcher - .on('add', path => { + .on('add', (path) => { strapiInstance.log.info(`File created: ${path}`); restart(); }) - .on('change', path => { + .on('change', (path) => { strapiInstance.log.info(`File changed: ${path}`); restart(); }) - .on('unlink', path => { + .on('unlink', (path) => { strapiInstance.log.info(`File deleted: ${path}`); restart(); }); diff --git a/packages/core/strapi/lib/middlewares/public/index.js b/packages/core/strapi/lib/middlewares/public/index.js index ad5412342b..a437805937 100644 --- a/packages/core/strapi/lib/middlewares/public/index.js +++ b/packages/core/strapi/lib/middlewares/public/index.js @@ -89,35 +89,5 @@ module.exports = (config, { strapi }) => { ]); } - if (!strapi.config.serveAdminPanel) return async (ctx, next) => next(); - - const buildDir = path.resolve(strapi.dirs.root, 'build'); - const serveAdmin = async (ctx, next) => { - await next(); - - if (ctx.method !== 'HEAD' && ctx.method !== 'GET') { - return; - } - - if (ctx.body != null || ctx.status !== 404) { - return; - } - - ctx.type = 'html'; - ctx.body = fs.createReadStream(path.join(buildDir + '/index.html')); - }; - - strapi.server.routes([ - { - method: 'GET', - path: `${strapi.config.admin.path}/:path*`, - handler: [ - serveAdmin, - serveStatic(buildDir, { maxage: maxAge, defer: false, index: 'index.html' }), - ], - config: { auth: false }, - }, - ]); - return null; };