feat(admin, admin-ui, medusa-js, medusa-react, medusa): Support Admin Extensions (#4761)
Co-authored-by: Rares Stefan <948623+StephixOne@users.noreply.github.com> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
26c78bbc03
commit
f1a05f4725
@@ -0,0 +1,52 @@
|
||||
import fse from "fs-extra"
|
||||
import path from "node:path"
|
||||
import webpack from "webpack"
|
||||
import { CustomWebpackConfigArgs } from "../types"
|
||||
import { logger } from "../utils"
|
||||
import { validateArgs } from "../utils/validate-args"
|
||||
import { getWebpackConfig } from "./get-webpack-config"
|
||||
import { withCustomWebpackConfig } from "./with-custom-webpack-config"
|
||||
|
||||
export async function getCustomWebpackConfig(
|
||||
appDir: string,
|
||||
args: CustomWebpackConfigArgs
|
||||
) {
|
||||
validateArgs(args)
|
||||
|
||||
let config = getWebpackConfig(args)
|
||||
|
||||
const adminConfigPath = path.join(appDir, "src", "admin", "webpack.config.js")
|
||||
|
||||
const pathExists = await fse.pathExists(adminConfigPath)
|
||||
|
||||
if (pathExists) {
|
||||
let webpackAdminConfig: ReturnType<typeof withCustomWebpackConfig>
|
||||
|
||||
try {
|
||||
webpackAdminConfig = require(adminConfigPath)
|
||||
} catch (e) {
|
||||
logger.panic(
|
||||
`An error occured while trying to load your custom Webpack config. See the error below for details:`,
|
||||
{
|
||||
error: e,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (typeof webpackAdminConfig === "function") {
|
||||
if (args.devServer) {
|
||||
config.devServer = args.devServer
|
||||
}
|
||||
|
||||
config = webpackAdminConfig(config, webpack)
|
||||
|
||||
if (!config) {
|
||||
logger.panic(
|
||||
"Nothing was returned from your custom webpack configuration"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
185
packages/admin-ui/src/node/webpack/get-webpack-config.ts
Normal file
185
packages/admin-ui/src/node/webpack/get-webpack-config.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import ReactRefreshPlugin from "@pmmmwh/react-refresh-webpack-plugin"
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin"
|
||||
import MiniCssExtractPlugin from "mini-css-extract-plugin"
|
||||
import path from "node:path"
|
||||
import { SwcMinifyWebpackPlugin } from "swc-minify-webpack-plugin"
|
||||
import type { Configuration } from "webpack"
|
||||
import webpack from "webpack"
|
||||
import WebpackBar from "webpackbar"
|
||||
import { WebpackConfigArgs } from "../types"
|
||||
import { getClientEnv } from "../utils"
|
||||
import { webpackAliases } from "./webpack-aliases"
|
||||
|
||||
function formatPublicPath(path?: string) {
|
||||
if (!path) {
|
||||
return "/app/"
|
||||
}
|
||||
|
||||
if (path === "/") {
|
||||
return path
|
||||
}
|
||||
|
||||
return path.endsWith("/") ? path : `${path}/`
|
||||
}
|
||||
|
||||
export function getWebpackConfig({
|
||||
entry,
|
||||
dest,
|
||||
cacheDir,
|
||||
env,
|
||||
options,
|
||||
template,
|
||||
reporting = "fancy",
|
||||
}: WebpackConfigArgs): Configuration {
|
||||
const isProd = env === "production"
|
||||
|
||||
const envVars = getClientEnv({
|
||||
env,
|
||||
backend: options?.backend,
|
||||
path: options?.path,
|
||||
})
|
||||
|
||||
const publicPath = formatPublicPath(options?.path)
|
||||
|
||||
const webpackPlugins = isProd
|
||||
? [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "[name].[chunkhash].css",
|
||||
chunkFilename: "[name].[chunkhash].css",
|
||||
}),
|
||||
new WebpackBar({
|
||||
basic: reporting === "minimal",
|
||||
fancy: reporting === "fancy",
|
||||
}),
|
||||
]
|
||||
: [new MiniCssExtractPlugin()]
|
||||
|
||||
return {
|
||||
mode: env,
|
||||
bail: !!isProd,
|
||||
devtool: isProd ? false : "eval-source-map",
|
||||
entry: [entry],
|
||||
output: {
|
||||
path: dest,
|
||||
filename: isProd ? "[name].[contenthash:8].js" : "[name].bundle.js",
|
||||
chunkFilename: isProd
|
||||
? "[name].[contenthash:8].chunk.js"
|
||||
: "[name].chunk.js",
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [new SwcMinifyWebpackPlugin()],
|
||||
moduleIds: "deterministic",
|
||||
runtimeChunk: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
include: [cacheDir],
|
||||
use: {
|
||||
loader: "swc-loader",
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript", // Use TypeScript syntax for parsing
|
||||
jsx: true, // Enable JSX parsing
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
include: [cacheDir],
|
||||
use: {
|
||||
loader: "swc-loader",
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "ecmascript", // Use Ecmascript syntax for parsing
|
||||
jsx: true, // Enable JSX parsing
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
oneOf: [
|
||||
{
|
||||
type: "asset/resource",
|
||||
resourceQuery: /url/,
|
||||
},
|
||||
{
|
||||
type: "asset/inline",
|
||||
resourceQuery: /base64/,
|
||||
},
|
||||
{
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: ["@svgr/webpack"],
|
||||
},
|
||||
],
|
||||
generator: {
|
||||
filename: `images/${isProd ? "[name]-[hash][ext]" : "[name][ext]"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(eot|otf|ttf|woff|woff2)$/,
|
||||
type: "asset/resource",
|
||||
},
|
||||
{
|
||||
test: /\.(js|mjs)(\.map)?$/,
|
||||
enforce: "pre",
|
||||
use: ["source-map-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.m?jsx?$/,
|
||||
resolve: {
|
||||
fullySpecified: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
alias: webpackAliases,
|
||||
symlinks: false,
|
||||
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
||||
mainFields: ["browser", "module", "main"],
|
||||
modules: ["node_modules", path.resolve(__dirname, "..", "node_modules")],
|
||||
fallback: {
|
||||
readline: false,
|
||||
path: false,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: template || path.resolve(__dirname, "..", "ui", "index.html"),
|
||||
publicPath: publicPath,
|
||||
}),
|
||||
|
||||
new webpack.DefinePlugin(envVars),
|
||||
|
||||
!isProd && new ReactRefreshPlugin(),
|
||||
|
||||
...webpackPlugins,
|
||||
].filter(Boolean),
|
||||
}
|
||||
}
|
||||
5
packages/admin-ui/src/node/webpack/index.ts
Normal file
5
packages/admin-ui/src/node/webpack/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { getCustomWebpackConfig } from "./get-custom-webpack-config"
|
||||
import { getWebpackConfig } from "./get-webpack-config"
|
||||
import { withCustomWebpackConfig } from "./with-custom-webpack-config"
|
||||
|
||||
export { getCustomWebpackConfig, getWebpackConfig, withCustomWebpackConfig }
|
||||
9
packages/admin-ui/src/node/webpack/webpack-aliases.ts
Normal file
9
packages/admin-ui/src/node/webpack/webpack-aliases.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ALIASED_PACKAGES } from "../constants"
|
||||
|
||||
/**
|
||||
* Ensure that the admin-ui uses the same version of these packages as the project.
|
||||
*/
|
||||
export const webpackAliases = ALIASED_PACKAGES.reduce((acc, pkg) => {
|
||||
acc[`${pkg}$`] = require.resolve(pkg)
|
||||
return acc
|
||||
}, {})
|
||||
@@ -0,0 +1,16 @@
|
||||
import webpack, { type Configuration } from "webpack"
|
||||
|
||||
/**
|
||||
* Helper function to create a custom webpack config that can be used to
|
||||
* extend the default webpack config used to build the admin UI.
|
||||
*/
|
||||
export function withCustomWebpackConfig(
|
||||
callback: (
|
||||
config: Configuration,
|
||||
webpackInstance: typeof webpack
|
||||
) => Configuration
|
||||
) {
|
||||
return (config: Configuration, webpackInstance: typeof webpack) => {
|
||||
return callback(config, webpackInstance)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user