/** * COMMON WEBPACK CONFIGURATION */ /* eslint-disable no-console */ const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const _ = require('lodash'); const pkg = require(path.resolve(process.cwd(), 'package.json')); const pluginId = pkg.name.replace(/^strapi-/i, ''); const isAdmin = process.env.IS_ADMIN === 'true'; const ExtractTextPlugin = require('extract-text-webpack-plugin'); const appPath = (() => { if (process.env.APP_PATH) { return process.env.APP_PATH; } return isAdmin ? path.resolve(process.env.PWD, '..') : path.resolve(process.env.PWD, '..', '..'); })(); // const isSetup = path.resolve(process.env.PWD, '..', '..') === path.resolve(process.env.INIT_CWD); const isSetup = process.env.IS_MONOREPO; const adminPath = (() => { if (isAdmin && isSetup) { return path.resolve(appPath, 'strapi-admin'); } return path.resolve(process.env.PWD); })(); // Define remote and backend URLs. const URLs = { host: '/admin', backend: '/', publicPath: null, mode: 'host', }; if (isAdmin && !isSetup) { // Load server configuration. const serverConfig = path.resolve( appPath, 'config', 'environments', _.lowerCase(process.env.NODE_ENV), 'server.json', ); try { const server = require(serverConfig); if (process.env.PWD.indexOf('/admin') !== -1) { if (_.get(server, 'admin.build.host')) { URLs.host = _.get(server, 'admin.build.host', '/admin').replace(/\/$/, '') || '/'; } else { URLs.host = _.get(server, 'admin.path', '/admin'); } URLs.publicPath = URLs.host; URLs.backend = _.get(server, 'admin.build.backend', `/`); if (_.get(server, 'admin.build.plugins.source') === 'backend') { URLs.mode = 'backend'; } if (process.env.npm_lifecycle_event === 'start') { URLs.backend = `http://${_.get(server, 'host', 'localhost')}:${_.get(server, 'port', 1337)}`; } } } catch (e) { throw new Error(`Impossible to access to ${serverConfig}`); } } // Load plugins into the same build in development mode. const plugins = { exist: false, src: [], folders: {}, }; if (process.env.npm_lifecycle_event === 'start') { try { fs.accessSync(path.resolve(appPath, 'plugins'), fs.constants.R_OK); } catch (e) { // Allow app without plugins. plugins.exist = true; } // Read `plugins` directory and check if the plugin comes with an UI (it has an App container). // If we don't do this check webpack expects the plugin to have a containers/App/reducer.js to create // the plugin's store (redux). plugins.src = isAdmin && !plugins.exist ? fs.readdirSync(path.resolve(appPath, 'plugins')).filter(x => { let hasAdminFolder; try { fs.accessSync(path.resolve(appPath, 'plugins', x, 'admin', 'src', 'containers', 'App')); hasAdminFolder = true; } catch (err) { hasAdminFolder = false; } return x[0] !== '.' && hasAdminFolder; }) : []; // Construct object of plugin' paths. plugins.folders = plugins.src.reduce((acc, current) => { acc[current] = path.resolve( appPath, 'plugins', current, 'node_modules', 'strapi-helper-plugin', 'lib', 'src', ); return acc; }, {}); } // Tell webpack to use a loader only for those files const foldersToInclude = [path.join(adminPath, 'admin', 'src')] .concat( plugins.src.reduce((acc, current) => { acc.push(path.resolve(appPath, 'plugins', current, 'admin', 'src'), plugins.folders[current]); return acc; }, []), ) .concat([path.join(adminPath, 'node_modules', 'strapi-helper-plugin', 'lib', 'src')]); module.exports = options => { // The disable option is only for production // Config from https://github.com/facebook/create-react-app/blob/next/packages/react-scripts/config/webpack.config.prod.js const extractSass = new ExtractTextPlugin({ filename: '[name].[contenthash].css', disable: options.disableExtractTextPlugin || true, }); return { entry: options.entry, output: Object.assign( { // Compile into js/build.js path: path.join(adminPath, 'admin', 'build'), }, options.output, ), // Merge with env dependent settings module: { rules: [ // TODO: add eslint formatter { // "oneOf" will traverse all following loaders until one will // match the requirements. When no loader matches it will fall // back to the "file" loader at the end of the loader list. oneOf: [ { test: /\.js$/, // Transform all .js files required somewhere with Babel, loader: require.resolve('babel-loader'), include: foldersToInclude, options: { presets: options.babelPresets, env: { production: { only: ['src'], plugins: [ require.resolve('babel-plugin-transform-react-remove-prop-types'), require.resolve('babel-plugin-transform-react-constant-elements'), require.resolve('babel-plugin-transform-react-inline-elements'), require.resolve('babel-plugin-transform-es2015-destructuring'), require.resolve('babel-plugin-transform-es2015-parameters'), require.resolve('babel-plugin-transform-object-rest-spread'), [require.resolve('babel-plugin-styled-components'), { ssr: true, preprocess: true }], ], }, test: { plugins: ['istanbul'], }, }, }, }, // The notation here is somewhat confusing. // "postcss" loader applies autoprefixer to our CSS. // "css" loader resolves paths in CSS and adds assets as dependencies. // "style" loader normally turns CSS into JS modules injecting