feat: solidifies bundler adapter pattern (#3044)
This commit is contained in:
@@ -12,7 +12,7 @@ module.exports = {
|
||||
'dist',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/webpack/mocks/fileMock.js',
|
||||
'\\.(css|scss)$': '<rootDir>/src/webpack/mocks/emptyModule.js',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/bundlers/mocks/fileMock.js',
|
||||
'\\.(css|scss)$': '<rootDir>/src/bundlers/mocks/emptyModule.js',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
globalSetup: './test/jest.setup.ts',
|
||||
testTimeout: 90000,
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/webpack/mocks/fileMock.js',
|
||||
'\\.(css|scss)$': '<rootDir>/src/webpack/mocks/emptyModule.js',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/bundlers/mocks/fileMock.js',
|
||||
'\\.(css|scss)$': '<rootDir>/src/bundlers/mocks/emptyModule.js',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"scripts": {
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
|
||||
"build:tsc": "tsc",
|
||||
"build:components": "webpack --config dist/webpack/components.config.js",
|
||||
"build:components": "webpack --config dist/bundlers/webpack/components.config.js",
|
||||
"build": "yarn copyfiles && yarn build:tsc && yarn build:components",
|
||||
"build:watch": "nodemon --watch 'src/**' --ext 'ts,tsx' --exec \"yarn build:tsc\"",
|
||||
"dev": "nodemon",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<link rel="icon" href="data:," data-placeholder-favicon/>
|
||||
<link rel="icon" href="data:," data-placeholder-favicon />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { extractTranslations } from '../translations/extractTranslations';
|
||||
|
||||
const labels = extractTranslations(['general:user', 'general:users']);
|
||||
|
||||
const defaultUser: CollectionConfig = {
|
||||
export const defaultUserCollection: CollectionConfig = {
|
||||
slug: 'users',
|
||||
labels: {
|
||||
singular: labels['general:user'],
|
||||
@@ -17,5 +17,3 @@ const defaultUser: CollectionConfig = {
|
||||
},
|
||||
fields: [],
|
||||
};
|
||||
|
||||
export default defaultUser;
|
||||
|
||||
@@ -1,33 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable global-require */
|
||||
import webpack from 'webpack';
|
||||
import getWebpackProdConfig from '../webpack/getProdConfig';
|
||||
import loadConfig from '../config/load';
|
||||
|
||||
export const build = async (): Promise<void> => {
|
||||
const config = await loadConfig(); // Will throw its own error if it fails
|
||||
|
||||
try {
|
||||
const webpackProdConfig = getWebpackProdConfig(config);
|
||||
|
||||
webpack(webpackProdConfig, (err, stats) => { // Stats Object
|
||||
if (err || stats.hasErrors()) {
|
||||
// Handle errors here
|
||||
|
||||
if (stats) {
|
||||
console.error(stats.toString({
|
||||
chunks: false,
|
||||
colors: true,
|
||||
}));
|
||||
} else {
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error('Error: there was an error building the webpack config.');
|
||||
}
|
||||
await config.admin.bundler.build(config);
|
||||
};
|
||||
|
||||
// when build.js is launched directly
|
||||
|
||||
@@ -26,10 +26,10 @@ const swcOptions = {
|
||||
};
|
||||
|
||||
if (tsConfig?.config?.compilerOptions?.paths) {
|
||||
swcOptions.jsc.paths = tsConfig?.config?.compilerOptions?.paths;
|
||||
swcOptions.jsc.paths = tsConfig.config.compilerOptions.paths;
|
||||
|
||||
if (tsConfig?.config?.compilerOptions?.baseUrl) {
|
||||
swcOptions.jsc.baseUrl = tsConfig?.config?.compilerOptions?.baseUrl;
|
||||
swcOptions.jsc.baseUrl = tsConfig.config.compilerOptions.baseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
src/bundlers/mocks/emptyModule.js
Normal file
1
src/bundlers/mocks/emptyModule.js
Normal file
@@ -0,0 +1 @@
|
||||
export default () => { };
|
||||
8
src/bundlers/types.ts
Normal file
8
src/bundlers/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { PayloadHandler, SanitizedConfig } from 'payload/config';
|
||||
import type { Payload } from '../payload';
|
||||
|
||||
export interface PayloadBundler {
|
||||
dev: (payload: Payload) => Promise<PayloadHandler>, // this would be a typical Express middleware handler
|
||||
build: (payloadConfig: SanitizedConfig) => Promise<void> // used in `payload build`
|
||||
serve: (payload: Payload) => Promise<PayloadHandler> // serve built files in production
|
||||
}
|
||||
11
src/bundlers/webpack/bundler.ts
Normal file
11
src/bundlers/webpack/bundler.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { PayloadBundler } from '../types';
|
||||
import { devAdmin } from './scripts/dev';
|
||||
import { buildAdmin } from './scripts/build';
|
||||
import { serveAdmin } from './scripts/serve';
|
||||
|
||||
export default (): PayloadBundler => ({
|
||||
dev: async (payload) => devAdmin({ payload }),
|
||||
build: async (payloadConfig) => buildAdmin({ payloadConfig }),
|
||||
serve: async (payload) => serveAdmin({ payload }),
|
||||
});
|
||||
@@ -5,13 +5,13 @@ import OptimizeCSSAssetsPlugin from 'css-minimizer-webpack-plugin';
|
||||
|
||||
export default {
|
||||
entry: {
|
||||
main: [path.resolve(__dirname, '../admin/components/index.js')],
|
||||
main: [path.resolve(__dirname, '../../admin/components/index.js')],
|
||||
},
|
||||
externals: {
|
||||
react: 'react',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../../components'),
|
||||
path: path.resolve(__dirname, '../../../components'),
|
||||
publicPath: '/',
|
||||
filename: 'index.js',
|
||||
libraryTarget: 'commonjs2',
|
||||
@@ -82,8 +82,8 @@ export default {
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'payload-scss-overrides': path.resolve(__dirname, '../admin/scss/overrides.scss'),
|
||||
'payload-scss-overrides': path.resolve(__dirname, '../../admin/scss/overrides.scss'),
|
||||
},
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../../node_modules')],
|
||||
},
|
||||
};
|
||||
@@ -1,19 +1,23 @@
|
||||
import path from 'path';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import webpack, { Configuration } from 'webpack';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
import type { SanitizedConfig } from '../../../config/types';
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, './mocks/emptyModule.js');
|
||||
const mockDotENVPath = path.resolve(__dirname, './mocks/dotENV.js');
|
||||
const mockModulePath = path.resolve(__dirname, '../../mocks/emptyModule.js');
|
||||
const mockDotENVPath = path.resolve(__dirname, '../../mocks/dotENV.js');
|
||||
|
||||
export default (config: SanitizedConfig): Configuration => ({
|
||||
const nodeModulesPath = path.resolve(__dirname, '../../../../node_modules');
|
||||
const adminFolderPath = path.resolve(__dirname, '../../../admin');
|
||||
const bundlerPath = path.resolve(__dirname, '../bundler');
|
||||
|
||||
export const getBaseConfig = (payloadConfig: SanitizedConfig): Configuration => ({
|
||||
entry: {
|
||||
main: [
|
||||
path.resolve(__dirname, '../admin'),
|
||||
adminFolderPath,
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: ['node_modules', path.join(__dirname, '../../node_modules')],
|
||||
modules: ['node_modules', path.join(__dirname, nodeModulesPath)],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@@ -51,12 +55,13 @@ export default (config: SanitizedConfig): Configuration => ({
|
||||
https: false,
|
||||
http: false,
|
||||
},
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
|
||||
modules: ['node_modules', path.resolve(__dirname, nodeModulesPath)],
|
||||
alias: {
|
||||
'payload-config': config.paths.rawConfig,
|
||||
'payload-config': payloadConfig.paths.rawConfig,
|
||||
payload$: mockModulePath,
|
||||
'payload-user-css': config.admin.css,
|
||||
'payload-user-css': payloadConfig.admin.css,
|
||||
dotenv: mockDotENVPath,
|
||||
[bundlerPath]: mockModulePath,
|
||||
},
|
||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||
},
|
||||
@@ -80,7 +85,7 @@ export default (config: SanitizedConfig): Configuration => ({
|
||||
),
|
||||
),
|
||||
new HtmlWebpackPlugin({
|
||||
template: config.admin.indexHTML,
|
||||
template: payloadConfig.admin.indexHTML,
|
||||
filename: path.normalize('./index.html'),
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
@@ -1,12 +1,12 @@
|
||||
import webpack, { Configuration } from 'webpack';
|
||||
import md5 from 'md5';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
import getBaseConfig from './getBaseConfig';
|
||||
import { getBaseConfig } from './base';
|
||||
import { SanitizedConfig } from '../../../config/types';
|
||||
|
||||
export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
export const getDevConfig = (payloadConfig: SanitizedConfig): Configuration => {
|
||||
const baseConfig = getBaseConfig(payloadConfig) as any;
|
||||
|
||||
let config: Configuration = {
|
||||
let webpackConfig: Configuration = {
|
||||
...baseConfig,
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
@@ -37,7 +37,7 @@ export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
],
|
||||
};
|
||||
|
||||
config.module.rules.push({
|
||||
webpackConfig.module.rules.push({
|
||||
test: /\.(scss|css)$/,
|
||||
sideEffects: true,
|
||||
use: [
|
||||
@@ -61,8 +61,8 @@ export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
});
|
||||
|
||||
if (payloadConfig.admin.webpack && typeof payloadConfig.admin.webpack === 'function') {
|
||||
config = payloadConfig.admin.webpack(config);
|
||||
webpackConfig = payloadConfig.admin.webpack(webpackConfig);
|
||||
}
|
||||
|
||||
return config;
|
||||
return webpackConfig;
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Configuration, WebpackPluginInstance } from 'webpack';
|
||||
import MiniCSSExtractPlugin from 'mini-css-extract-plugin';
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||
import { Configuration, WebpackPluginInstance } from 'webpack';
|
||||
import { SwcMinifyWebpackPlugin } from 'swc-minify-webpack-plugin';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
import getBaseConfig from './getBaseConfig';
|
||||
import { getBaseConfig } from './base';
|
||||
import { SanitizedConfig } from '../../../config/types';
|
||||
|
||||
export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
export const getProdConfig = (payloadConfig: SanitizedConfig): Configuration => {
|
||||
const baseConfig = getBaseConfig(payloadConfig) as any;
|
||||
|
||||
let config: Configuration = {
|
||||
let webpackConfig: Configuration = {
|
||||
...baseConfig,
|
||||
output: {
|
||||
publicPath: `${payloadConfig.routes.admin}/`,
|
||||
@@ -40,7 +40,7 @@ export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
],
|
||||
};
|
||||
|
||||
config.module.rules.push({
|
||||
webpackConfig.module.rules.push({
|
||||
test: /\.(scss|css)$/,
|
||||
sideEffects: true,
|
||||
use: [
|
||||
@@ -64,12 +64,12 @@ export default (payloadConfig: SanitizedConfig): Configuration => {
|
||||
});
|
||||
|
||||
if (process.env.PAYLOAD_ANALYZE_BUNDLE) {
|
||||
config.plugins.push(new BundleAnalyzerPlugin() as unknown as WebpackPluginInstance);
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin() as unknown as WebpackPluginInstance);
|
||||
}
|
||||
|
||||
if (payloadConfig.admin.webpack && typeof payloadConfig.admin.webpack === 'function') {
|
||||
config = payloadConfig.admin.webpack(config);
|
||||
webpackConfig = payloadConfig.admin.webpack(webpackConfig);
|
||||
}
|
||||
|
||||
return config;
|
||||
return webpackConfig;
|
||||
};
|
||||
27
src/bundlers/webpack/scripts/build.ts
Normal file
27
src/bundlers/webpack/scripts/build.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import webpack from 'webpack';
|
||||
import { getProdConfig } from '../configs/prod';
|
||||
import { SanitizedConfig } from '../../../config/types';
|
||||
|
||||
type BuildAdminType = (options: { payloadConfig: SanitizedConfig }) => Promise<void>;
|
||||
export const buildAdmin: BuildAdminType = async ({ payloadConfig }) => {
|
||||
try {
|
||||
const webpackConfig = getProdConfig(payloadConfig);
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
if (err || stats.hasErrors()) {
|
||||
// Handle errors here
|
||||
|
||||
if (stats) {
|
||||
console.error(stats.toString({
|
||||
chunks: false,
|
||||
colors: true,
|
||||
}));
|
||||
} else {
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error('Error: there was an error building the webpack prod config.');
|
||||
}
|
||||
};
|
||||
31
src/bundlers/webpack/scripts/dev.ts
Normal file
31
src/bundlers/webpack/scripts/dev.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import webpack from 'webpack';
|
||||
import express from 'express';
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware';
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware';
|
||||
import history from 'connect-history-api-fallback';
|
||||
import type { PayloadHandler } from '../../../config/types';
|
||||
import { Payload } from '../../../payload';
|
||||
import { getDevConfig } from '../configs/dev';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
type DevAdminType = (options: { payload: Payload }) => Promise<PayloadHandler>;
|
||||
export const devAdmin: DevAdminType = async ({ payload }) => {
|
||||
payload.express.use(payload.config.routes.admin, history());
|
||||
|
||||
try {
|
||||
const webpackConfig = getDevConfig(payload.config);
|
||||
const compiler = webpack(webpackConfig);
|
||||
|
||||
router.use(webpackDevMiddleware(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath as string,
|
||||
}));
|
||||
|
||||
router.use(webpackHotMiddleware(compiler));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error('Error: there was an error creating the webpack dev server.');
|
||||
}
|
||||
|
||||
return router;
|
||||
};
|
||||
27
src/bundlers/webpack/scripts/serve.ts
Normal file
27
src/bundlers/webpack/scripts/serve.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import express from 'express';
|
||||
import compression from 'compression';
|
||||
import history from 'connect-history-api-fallback';
|
||||
import type { PayloadHandler } from '../../../config/types';
|
||||
import { Payload } from '../../../payload';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
type ServeAdminType = (options: { payload: Payload }) => Promise<PayloadHandler>;
|
||||
|
||||
export const serveAdmin: ServeAdminType = async ({ payload }) => {
|
||||
router.use(payload.config.routes.admin, history());
|
||||
|
||||
router.get('*', (req, res, next) => {
|
||||
if (req.path.substr(-1) === '/' && req.path.length > 1) {
|
||||
const query = req.url.slice(req.path.length);
|
||||
res.redirect(301, req.path.slice(0, -1) + query);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
router.use(compression(payload.config.express.compression));
|
||||
router.use(express.static(payload.config.admin.buildPath, { redirect: false }));
|
||||
|
||||
return router;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { Config, SanitizedConfig } from './types';
|
||||
import sanitize from './sanitize';
|
||||
import { sanitizeConfig } from './sanitize';
|
||||
|
||||
/**
|
||||
* @description Builds and validates Payload configuration
|
||||
@@ -18,10 +18,10 @@ export async function buildConfig(config: Config): Promise<SanitizedConfig> {
|
||||
Promise.resolve(config),
|
||||
);
|
||||
|
||||
const sanitizedConfig = sanitize(configAfterPlugins);
|
||||
const sanitizedConfig = sanitizeConfig(configAfterPlugins);
|
||||
|
||||
return sanitizedConfig;
|
||||
}
|
||||
|
||||
return sanitize(config);
|
||||
return sanitizeConfig(config);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,47 @@
|
||||
import merge from 'deepmerge';
|
||||
import { isPlainObject } from 'is-plain-object';
|
||||
import { Config, SanitizedConfig } from './types';
|
||||
import defaultUser from '../auth/defaultUser';
|
||||
import { defaultUserCollection } from '../auth/defaultUser';
|
||||
import sanitizeCollection from '../collections/config/sanitize';
|
||||
import { InvalidConfiguration } from '../errors';
|
||||
import sanitizeGlobals from '../globals/config/sanitize';
|
||||
import checkDuplicateCollections from '../utilities/checkDuplicateCollections';
|
||||
import { defaults } from './defaults';
|
||||
import getDefaultBundler from '../bundlers/webpack/bundler';
|
||||
|
||||
const sanitizeConfig = (config: Config): SanitizedConfig => {
|
||||
const sanitizedConfig = merge(defaults, config, {
|
||||
const sanitizeAdmin = (config: SanitizedConfig): SanitizedConfig['admin'] => {
|
||||
const adminConfig = config.admin;
|
||||
|
||||
// add default user collection if none provided
|
||||
if (!adminConfig?.user) {
|
||||
const firstCollectionWithAuth = config.collections.find(({ auth }) => Boolean(auth));
|
||||
if (firstCollectionWithAuth) {
|
||||
adminConfig.user = firstCollectionWithAuth.slug;
|
||||
} else {
|
||||
adminConfig.user = 'users';
|
||||
const sanitizedDefaultUser = sanitizeCollection(config, defaultUserCollection);
|
||||
config.collections.push(sanitizedDefaultUser);
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.collections.find(({ slug }) => slug === adminConfig.user)) {
|
||||
throw new InvalidConfiguration(`${config.admin.user} is not a valid admin user collection`);
|
||||
}
|
||||
|
||||
// add default bundler if none provided
|
||||
if (!adminConfig.bundler) {
|
||||
adminConfig.bundler = getDefaultBundler();
|
||||
}
|
||||
|
||||
return adminConfig;
|
||||
};
|
||||
|
||||
export const sanitizeConfig = (config: Config): SanitizedConfig => {
|
||||
const sanitizedConfig: Config = merge(defaults, config, {
|
||||
isMergeableObject: isPlainObject,
|
||||
}) as Config;
|
||||
|
||||
if (!sanitizedConfig.admin.user) {
|
||||
const firstCollectionWithAuth = sanitizedConfig.collections.find((c) => c.auth);
|
||||
if (firstCollectionWithAuth) {
|
||||
sanitizedConfig.admin.user = firstCollectionWithAuth.slug;
|
||||
} else {
|
||||
sanitizedConfig.admin.user = 'users';
|
||||
const sanitizedDefaultUser = sanitizeCollection(sanitizedConfig, defaultUser);
|
||||
sanitizedConfig.collections.push(sanitizedDefaultUser);
|
||||
}
|
||||
} else if (!sanitizedConfig.collections.find((c) => c.slug === sanitizedConfig.admin.user)) {
|
||||
throw new InvalidConfiguration(`${sanitizedConfig.admin.user} is not a valid admin user collection`);
|
||||
}
|
||||
sanitizedConfig.admin = sanitizeAdmin(sanitizedConfig as SanitizedConfig);
|
||||
|
||||
sanitizedConfig.collections = sanitizedConfig.collections.map((collection) => sanitizeCollection(sanitizedConfig, collection));
|
||||
checkDuplicateCollections(sanitizedConfig.collections);
|
||||
@@ -43,5 +60,3 @@ const sanitizeConfig = (config: Config): SanitizedConfig => {
|
||||
|
||||
return sanitizedConfig as SanitizedConfig;
|
||||
};
|
||||
|
||||
export default sanitizeConfig;
|
||||
|
||||
@@ -107,6 +107,11 @@ export default joi.object({
|
||||
}),
|
||||
}),
|
||||
webpack: joi.func(),
|
||||
bundler: {
|
||||
dev: joi.func(),
|
||||
build: joi.func(),
|
||||
serve: joi.func(),
|
||||
},
|
||||
}),
|
||||
email: joi.object(),
|
||||
i18n: joi.object(),
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Express, NextFunction, Response } from 'express';
|
||||
import { DeepRequired } from 'ts-essentials';
|
||||
import { Transporter } from 'nodemailer';
|
||||
import { Options as ExpressFileUploadOptions } from 'express-fileupload';
|
||||
import { Configuration } from 'webpack';
|
||||
import type { Configuration } from 'webpack';
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
import GraphQL from 'graphql';
|
||||
import { ConnectOptions } from 'mongoose';
|
||||
@@ -19,6 +19,7 @@ import { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types';
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { Where } from '../types';
|
||||
import { User } from '../auth/types';
|
||||
import type { PayloadBundler } from '../bundlers/types';
|
||||
|
||||
type Email = {
|
||||
fromName: string;
|
||||
@@ -266,7 +267,11 @@ export type Config = {
|
||||
*/
|
||||
favicon?: string;
|
||||
};
|
||||
/** Specify an absolute path for where to store the built Admin panel bundle used in production. */
|
||||
/**
|
||||
* Specify an absolute path for where to store the built Admin panel bundle used in production.
|
||||
*
|
||||
* @default "/build"
|
||||
* */
|
||||
buildPath?: string
|
||||
/** If set to true, the entire Admin panel will be disabled. */
|
||||
disable?: boolean;
|
||||
@@ -353,6 +358,8 @@ export type Config = {
|
||||
};
|
||||
/** Customize the Webpack config that's used to generate the Admin panel. */
|
||||
webpack?: (config: Configuration) => Configuration;
|
||||
/** Customize the bundler used to run your admin panel. */
|
||||
bundler?: PayloadBundler;
|
||||
};
|
||||
/**
|
||||
* Manage the datamodel of your application
|
||||
@@ -407,13 +414,13 @@ export type Config = {
|
||||
cors?: string[] | '*';
|
||||
/** Control the routing structure that Payload binds itself to. */
|
||||
routes?: {
|
||||
/** Defaults to /api */
|
||||
/** @default "/api" */
|
||||
api?: string;
|
||||
/** Defaults to /admin */
|
||||
/** @default "/admin" */
|
||||
admin?: string;
|
||||
/** Defaults to /graphql */
|
||||
/** @default "/graphql" */
|
||||
graphQL?: string;
|
||||
/** Defaults to /playground */
|
||||
/** @default "/playground" */
|
||||
graphQLPlayground?: string;
|
||||
};
|
||||
/** Control how typescript interfaces are generated from your collections. */
|
||||
|
||||
@@ -1,32 +1,11 @@
|
||||
import express from 'express';
|
||||
import compression from 'compression';
|
||||
import history from 'connect-history-api-fallback';
|
||||
import initWebpack from '../webpack/init';
|
||||
import { Payload } from '../payload';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function initAdmin(ctx: Payload): void {
|
||||
async function initAdmin(ctx: Payload): Promise<void> {
|
||||
if (!ctx.config.admin.disable) {
|
||||
router.use(history());
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
router.get('*', (req, res, next) => {
|
||||
if (req.path.substr(-1) === '/' && req.path.length > 1) {
|
||||
const query = req.url.slice(req.path.length);
|
||||
res.redirect(301, req.path.slice(0, -1) + query);
|
||||
ctx.express.use(await ctx.config.admin.bundler.serve(ctx));
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
router.use(compression(ctx.config.express.compression));
|
||||
router.use(express.static(ctx.config.admin.buildPath, { redirect: false }));
|
||||
|
||||
ctx.express.use(ctx.config.routes.admin, router);
|
||||
} else {
|
||||
ctx.express.use(ctx.config.routes.admin, history());
|
||||
ctx.express.use(initWebpack(ctx.config));
|
||||
ctx.express.use(await ctx.config.admin.bundler.dev(ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export const initHTTP = async (options: InitOptions): Promise<Payload> => {
|
||||
payload.express.set('trust proxy', 1);
|
||||
}
|
||||
|
||||
initAdmin(payload);
|
||||
await initAdmin(payload);
|
||||
initPreferences(payload);
|
||||
|
||||
payload.router.get('/access', access);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import sanitizeConfig from '../config/sanitize';
|
||||
import { sanitizeConfig } from '../config/sanitize';
|
||||
import { Config } from '../config/types';
|
||||
import { configToJSONSchema } from './configToJSONSchema';
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import webpack, { Compiler } from 'webpack';
|
||||
import express, { Router } from 'express';
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware';
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware';
|
||||
import getWebpackDevConfig from './getDevConfig';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function initWebpack(config: SanitizedConfig): Router {
|
||||
const webpackDevConfig = getWebpackDevConfig(config);
|
||||
const compiler = webpack(webpackDevConfig);
|
||||
|
||||
router.use(webpackDevMiddleware(compiler, {
|
||||
publicPath: webpackDevConfig.output.publicPath as string,
|
||||
}));
|
||||
|
||||
router.use(webpackHotMiddleware(compiler as any));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
export default initWebpack;
|
||||
@@ -1 +0,0 @@
|
||||
export default () => {};
|
||||
@@ -1,10 +1,10 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { PostsCollection, postsSlug } from './collections/Posts';
|
||||
import { MenuGlobal } from './globals/Menu';
|
||||
import { devUser } from '../credentials';
|
||||
import { MediaCollection } from './collections/Media';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
// ...extend config here
|
||||
collections: [
|
||||
PostsCollection,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { FieldAccess } from '../../src/fields/config/types';
|
||||
import { firstArrayText, secondArrayText } from './shared';
|
||||
|
||||
@@ -36,7 +36,7 @@ const UseRequestHeadersAccess: FieldAccess = ({ req: { headers } }) => {
|
||||
return !!headers && headers.authorization === requestHeaders.authorization;
|
||||
};
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
user: 'users',
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path';
|
||||
import { mapAsync } from '../../src/utilities/mapAsync';
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import AfterDashboard from './components/AfterDashboard';
|
||||
import CustomMinimalRoute from './components/views/CustomMinimal';
|
||||
import CustomDefaultRoute from './components/views/CustomDefault';
|
||||
@@ -20,7 +20,7 @@ export interface Post {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
css: path.resolve(__dirname, 'styles.scss'),
|
||||
components: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'arrays',
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { mapAsync } from '../../src/utilities/mapAsync';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import { AuthDebug } from './AuthDebug';
|
||||
|
||||
export const slug = 'users';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
user: 'users',
|
||||
autoLogin: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Request } from 'express';
|
||||
import { Strategy } from 'passport-strategy';
|
||||
import { Payload } from '../../../src/payload';
|
||||
import { buildConfig } from '../../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../../buildConfigWithDefaults';
|
||||
|
||||
export const slug = 'users';
|
||||
export const strategyName = 'test-local';
|
||||
@@ -41,7 +41,7 @@ export class CustomStrategy extends Strategy {
|
||||
}
|
||||
}
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
user: 'users',
|
||||
},
|
||||
|
||||
@@ -1,40 +1,41 @@
|
||||
import { Config, SanitizedConfig } from '../src/config/types';
|
||||
import { buildConfig as buildPayloadConfig } from '../src/config/build';
|
||||
|
||||
export function buildConfig(config?: Partial<Config>): Promise<SanitizedConfig> {
|
||||
export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<SanitizedConfig> {
|
||||
const [name] = process.argv.slice(2);
|
||||
const baseConfig: Config = {
|
||||
const config: Config = {
|
||||
telemetry: false,
|
||||
rateLimit: {
|
||||
window: 15 * 60 * 100, // 15min default,
|
||||
max: 9999999999,
|
||||
},
|
||||
...config,
|
||||
...testConfig,
|
||||
};
|
||||
baseConfig.admin = {
|
||||
|
||||
config.admin = {
|
||||
autoLogin: process.env.PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN === 'true' ? false : {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'test',
|
||||
},
|
||||
...(baseConfig.admin || {}),
|
||||
...(config.admin || {}),
|
||||
webpack: (webpackConfig) => {
|
||||
const existingConfig = typeof config?.admin?.webpack === 'function'
|
||||
? config.admin.webpack(webpackConfig)
|
||||
const existingConfig = typeof testConfig?.admin?.webpack === 'function'
|
||||
? testConfig.admin.webpack(webpackConfig)
|
||||
: webpackConfig;
|
||||
return {
|
||||
...existingConfig,
|
||||
name,
|
||||
cache: process.env.NODE_ENV === 'test' ? {
|
||||
type: 'memory',
|
||||
} : existingConfig.cache,
|
||||
cache: process.env.NODE_ENV === 'test'
|
||||
? { type: 'memory' }
|
||||
: existingConfig.cache,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.PAYLOAD_DISABLE_ADMIN === 'true') {
|
||||
if (typeof baseConfig.admin !== 'object') baseConfig.admin = {};
|
||||
baseConfig.admin.disable = true;
|
||||
if (typeof config.admin !== 'object') config.admin = {};
|
||||
config.admin.disable = true;
|
||||
}
|
||||
|
||||
return buildPayloadConfig(baseConfig);
|
||||
return buildPayloadConfig(config);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path';
|
||||
import type { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
export interface Relation {
|
||||
id: string;
|
||||
@@ -30,7 +30,7 @@ const collectionWithName = (collectionSlug: string): CollectionConfig => {
|
||||
|
||||
export const slug = 'posts';
|
||||
export const relationSlug = 'relation';
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
graphQL: {
|
||||
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
export interface Relation {
|
||||
id: string;
|
||||
@@ -34,7 +34,7 @@ export const customIdSlug = 'custom-id';
|
||||
export const customIdNumberSlug = 'custom-id-number';
|
||||
export const errorOnHookSlug = 'error-on-hooks';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
endpoints: [
|
||||
{
|
||||
path: '/send-test-email',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { openAccess } from '../helpers/configHelpers';
|
||||
import { Config } from '../../src/config/types';
|
||||
|
||||
@@ -62,4 +62,4 @@ const config: Config = {
|
||||
custom: { name: 'Customer portal' },
|
||||
};
|
||||
|
||||
export default buildConfig(config);
|
||||
export default buildConfigWithDefaults(config);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express, { Response } from 'express';
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { openAccess } from '../helpers/configHelpers';
|
||||
import { PayloadRequest } from '../../src/express/types';
|
||||
import { Config } from '../../src/config/types';
|
||||
@@ -124,4 +124,4 @@ const MyConfig: Config = {
|
||||
},
|
||||
};
|
||||
|
||||
export default buildConfig(MyConfig);
|
||||
export default buildConfigWithDefaults(MyConfig);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { ErrorFieldsCollection } from './collections/ErrorFields';
|
||||
import { devUser } from '../credentials';
|
||||
import Uploads from './collections/Upload';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
ErrorFieldsCollection,
|
||||
Uploads,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'blocks-collection',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import { mapAsync } from '../../src/utilities/mapAsync';
|
||||
import { FilterOptionsProps } from '../../src/fields/config/types';
|
||||
@@ -33,7 +33,7 @@ const baseRelationshipFields: CollectionConfig['fields'] = [
|
||||
},
|
||||
];
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import ArrayFields, { arrayDoc } from './collections/Array';
|
||||
import BlockFields, { blocksDoc } from './collections/Blocks';
|
||||
@@ -26,7 +26,7 @@ import Uploads2 from './collections/Upload2';
|
||||
import Uploads3 from './collections/Uploads3';
|
||||
import RowFields from './collections/Row';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
export const slug = 'global';
|
||||
export const arraySlug = 'array';
|
||||
@@ -16,7 +16,7 @@ const access = {
|
||||
update: () => true,
|
||||
};
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
localization: {
|
||||
locales: [englishLocale, spanishLocale],
|
||||
defaultLocale: englishLocale,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
graphQL: {
|
||||
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import TransformHooks from './collections/Transform';
|
||||
import Hooks, { hooksSlug } from './collections/Hook';
|
||||
import NestedAfterReadHooks from './collections/NestedAfterReadHooks';
|
||||
@@ -6,7 +6,7 @@ import ChainingHooks from './collections/ChainingHooks';
|
||||
import Relations from './collections/Relations';
|
||||
import Users, { seedHooksUsers } from './collections/Users';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
TransformHooks,
|
||||
Hooks,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import { ArrayCollection } from './collections/Array';
|
||||
import { LocalizedPost, RelationshipLocalized } from './payload-types';
|
||||
@@ -32,7 +32,7 @@ const openAccess = {
|
||||
update: () => true,
|
||||
};
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
localization: {
|
||||
locales: [defaultLocale, spanishLocale],
|
||||
defaultLocale,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
|
||||
export const pagesSlug = 'pages';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'users',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import GlobalViewWithRefresh from './GlobalViewWithRefresh';
|
||||
|
||||
export const pagesSlug = 'pages';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
globals: [
|
||||
{
|
||||
slug: 'settings',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { devUser } from '../credentials';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
|
||||
const openAccess = {
|
||||
create: () => true,
|
||||
@@ -41,7 +41,7 @@ export const defaultAccessRelSlug = 'strict-access';
|
||||
export const chainedRelSlug = 'chained-relation';
|
||||
export const customIdSlug = 'custom-id-relation';
|
||||
export const customIdNumberSlug = 'custom-id-number-relation';
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import { devUser } from '../credentials';
|
||||
import getFileByPath from '../../src/uploads/getFileByPath';
|
||||
import removeFiles from '../helpers/removeFiles';
|
||||
@@ -14,7 +14,7 @@ export const audioSlug = 'audio';
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, './mocks/mockFSModule.js');
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { buildConfig } from '../buildConfig';
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
|
||||
import AutosavePosts from './collections/Autosave';
|
||||
import DraftPosts from './collections/Drafts';
|
||||
import AutosaveGlobal from './globals/Autosave';
|
||||
@@ -7,7 +7,7 @@ import DraftGlobal from './globals/Draft';
|
||||
import VersionPosts from './collections/Versions';
|
||||
import { draftSlug } from './shared';
|
||||
|
||||
export default buildConfig({
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
AutosavePosts,
|
||||
DraftPosts,
|
||||
|
||||
Reference in New Issue
Block a user