mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * 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 isSetup = process.env.IS_MONOREPO || false;
 | |
| const appPath = process.env.APP_PATH || path.resolve(process.env.PWD || process.cwd(), '..', ( isAdmin ?  '' : '..' ));
 | |
| 
 | |
| const adminPath = (() => {
 | |
|   if (isAdmin && isSetup) {
 | |
|     return path.resolve(appPath, 'strapi-admin');
 | |
|   }
 | |
| 
 | |
|   return path.resolve(process.env.PWD || process.cwd());
 | |
| })();
 | |
| 
 | |
| // 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 { templateConfiguration } = require(path.join(adminPath, 'node_modules', 'strapi-utils'));
 | |
| 
 | |
|     let server = require(serverConfig);
 | |
|     server = templateConfiguration(server);
 | |
| 
 | |
|     if ((process.env.PWD || process.cwd()).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 <style>,
 | |
|             // but unlike in development configuration, we do something different.
 | |
|             // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
 | |
|             // (second argument), then grabs the result CSS and puts it into a
 | |
|             // separate file in our build process. This way we actually ship
 | |
|             // a single CSS file in production instead of JS code injecting <style>
 | |
|             // tags. If you use code splitting, however, any async bundles will still
 | |
|             // use the "style" loader inside the async code so CSS from them won't be
 | |
|             // in the main CSS file.
 | |
|             {
 | |
|               test: /\.css$/,
 | |
|               include: /node_modules/,
 | |
|               use: extractSass.extract({
 | |
|                 fallback: require.resolve('style-loader'),
 | |
|                 use: [
 | |
|                   {
 | |
|                     loader: require.resolve('css-loader'),
 | |
|                     options: {
 | |
|                       minimize: process.env.NODE_ENV === 'production',
 | |
|                       sourceMap: false,
 | |
|                     },
 | |
|                   },
 | |
|                   {
 | |
|                     loader: require.resolve('postcss-loader'),
 | |
|                     options: {
 | |
|                       config: {
 | |
|                         path: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
 | |
|                       },
 | |
|                     },
 | |
|                   },
 | |
|                 ],
 | |
|               }),
 | |
|             },
 | |
|             {
 | |
|               test: /\.scss$/,
 | |
|               include: foldersToInclude,
 | |
|               use: extractSass.extract({
 | |
|                 use: [
 | |
|                   {
 | |
|                     loader: require.resolve('css-loader'),
 | |
|                     options: {
 | |
|                       localIdentName: `${pluginId}[local]__[path][name]__[hash:base64:5]`,
 | |
|                       modules: true,
 | |
|                       importLoaders: 1,
 | |
|                       sourceMap: false,
 | |
|                       minimize: process.env.NODE_ENV === 'production',
 | |
|                     },
 | |
|                   },
 | |
|                   {
 | |
|                     loader: require.resolve('postcss-loader'),
 | |
|                     options: {
 | |
|                       config: {
 | |
|                         path: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
 | |
|                       },
 | |
|                     },
 | |
|                   },
 | |
|                   {
 | |
|                     loader: 'sass-loader',
 | |
|                   },
 | |
|                 ],
 | |
|                 fallback: require.resolve('style-loader'),
 | |
|               }),
 | |
|             },
 | |
|             {
 | |
|               test: /\.(eot|svg|otf|ttf|woff|woff2)$/,
 | |
|               use: 'file-loader',
 | |
|             },
 | |
|             {
 | |
|               test: /\.(jpg|png|gif)$/,
 | |
|               loaders: [
 | |
|                 require.resolve('file-loader'),
 | |
|                 {
 | |
|                   loader: require.resolve('image-webpack-loader'),
 | |
|                   query: {
 | |
|                     mozjpeg: {
 | |
|                       progressive: true,
 | |
|                     },
 | |
|                     gifsicle: {
 | |
|                       interlaced: false,
 | |
|                     },
 | |
|                     optipng: {
 | |
|                       optimizationLevel: 4,
 | |
|                     },
 | |
|                     pngquant: {
 | |
|                       quality: '65-90',
 | |
|                       speed: 4,
 | |
|                     },
 | |
|                   },
 | |
|                 },
 | |
|               ],
 | |
|             },
 | |
|             {
 | |
|               test: /\.html$/,
 | |
|               include: [path.join(adminPath, 'admin', 'src')],
 | |
|               use: require.resolve('html-loader'),
 | |
|             },
 | |
|             {
 | |
|               test: /\.(mp4|webm)$/,
 | |
|               loader: require.resolve('url-loader'),
 | |
|               options: {
 | |
|                 limit: 10000,
 | |
|               },
 | |
|             },
 | |
|           ],
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|     plugins: [
 | |
|       new webpack.ProvidePlugin({
 | |
|         // make fetch available
 | |
|         fetch: 'exports-loader?self.fetch!whatwg-fetch',
 | |
|       }),
 | |
| 
 | |
|       // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
 | |
|       // inside your code for any environment checks; UglifyJS will automatically
 | |
|       // drop any unreachable code.
 | |
|       new webpack.DefinePlugin({
 | |
|         'process.env': {
 | |
|           NODE_ENV: JSON.stringify(process.env.NODE_ENV),
 | |
|           REMOTE_URL: JSON.stringify(URLs.host),
 | |
|           BACKEND_URL: JSON.stringify(URLs.backend),
 | |
|           MODE: JSON.stringify(URLs.mode), // Allow us to define the public path for the plugins assets.
 | |
|         },
 | |
|       }),
 | |
|       new webpack.NamedModulesPlugin(),
 | |
|       extractSass,
 | |
|     ].concat(options.plugins),
 | |
|     resolve: {
 | |
|       modules: [
 | |
|         'admin/src',
 | |
|         'node_modules/strapi-helper-plugin/lib/src',
 | |
|         'node_modules/strapi-helper-plugin/node_modules',
 | |
|         'node_modules',
 | |
|       ],
 | |
|       alias: options.alias,
 | |
|       symlinks: false,
 | |
|       extensions: ['.js', '.jsx', '.react.js'],
 | |
|       mainFields: ['browser', 'jsnext:main', 'main'],
 | |
|     },
 | |
|     externals: options.externals,
 | |
|     resolveLoader: {
 | |
|       modules: [
 | |
|         path.join(__dirname, '..', '..', '..', 'node_modules'),
 | |
|         path.join(process.cwd(), 'node_modules'),
 | |
|       ],
 | |
|     },
 | |
|     devtool: options.devtool,
 | |
|     target: 'web', // Make web variables accessible to webpack, e.g. window,
 | |
|   };
 | |
| };
 | 
