chore: Update admin build/serve configuration (#9584)
**Breaking changes** The `outDir` has been deprecated and wont be used anymore, instead all the path are computed internally following these rules - if admin is not `disabled` and the `build` command is run without the `--admin-only` flag, then the admin output dir will be `.medusa/server/public/admin` and it will be served from that same location from the medusa instance. - if admin is not `disabled` and the `build` command is run with the `--admin-only` flag, then the admin output dir will be `.medusa/admin` with the purpose of deploying the admin separately. ⚠️ (expect to receive a warning log) - if the admin is `disabled` and the `build` command is run with the `--admin-only` flag, then fallback to rule number 2 | admin enabled | medusa build --admin-only | output dir | |---|---|---| | true | true | `.medusa/admin` ⚠️ (expect to receive a warning log) | | true | false | `.medusa/server/public/admin` | | false | true | `.medusa/admin` | | false | false | none | ```diff // medusa-config.ts { // ... admin: { - outDir: 'some/path' } } ``` cc @kasperkristensen @sradevski @olivermrbl
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { AdminOptions } from "@medusajs/types"
|
import { AdminOptions } from "@medusajs/types"
|
||||||
|
|
||||||
export type BundlerOptions = Required<Pick<AdminOptions, "outDir" | "path">> &
|
export type BundlerOptions = Required<Pick<AdminOptions, "path">> &
|
||||||
Pick<AdminOptions, "vite" | "backendUrl" | "storefrontUrl"> & {
|
Pick<AdminOptions, "vite" | "backendUrl" | "storefrontUrl"> & {
|
||||||
|
outDir: string
|
||||||
sources?: string[]
|
sources?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,9 +354,15 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
.command({
|
.command({
|
||||||
command: `build`,
|
command: "build",
|
||||||
desc: `Build your project.`,
|
desc: "Build your project.",
|
||||||
builder: (_) => _,
|
builder: (_) =>
|
||||||
|
_.option("admin-only", {
|
||||||
|
default: false,
|
||||||
|
type: "boolean",
|
||||||
|
describe:
|
||||||
|
"Only build the admin to serve it separately (outDir .medusa/admin)",
|
||||||
|
}),
|
||||||
handler: handlerP(
|
handler: handlerP(
|
||||||
getCommandHandler(`build`, (args, cmd) => {
|
getCommandHandler(`build`, (args, cmd) => {
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || `development`
|
process.env.NODE_ENV = process.env.NODE_ENV || `development`
|
||||||
|
|||||||
@@ -49,21 +49,6 @@ export interface AdminOptions {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
path: `/${string}`
|
path: `/${string}`
|
||||||
/**
|
|
||||||
* The directory where the admin build is outputted when you run the `build` command.
|
|
||||||
* The default value is `./build`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = defineConfig({
|
|
||||||
* admin: {
|
|
||||||
* outDir: process.env.ADMIN_BUILD_DIR || `./build`,
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
outDir: string
|
|
||||||
/**
|
/**
|
||||||
* The URL of your Medusa application. This is useful to set when you deploy the Medusa application.
|
* The URL of your Medusa application. This is useful to set when you deploy the Medusa application.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
@@ -154,7 +153,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
@@ -307,7 +305,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
@@ -466,7 +463,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
@@ -621,7 +617,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
@@ -771,7 +766,6 @@ describe("defineConfig", function () {
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"backendUrl": "http://localhost:9000",
|
"backendUrl": "http://localhost:9000",
|
||||||
"outDir": ".medusa/admin",
|
|
||||||
"path": "/app",
|
"path": "/app",
|
||||||
},
|
},
|
||||||
"featureFlags": {},
|
"featureFlags": {},
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ export function defineConfig(config: Config = {}): ConfigModule {
|
|||||||
*/
|
*/
|
||||||
const admin: ConfigModule["admin"] = {
|
const admin: ConfigModule["admin"] = {
|
||||||
backendUrl: process.env.MEDUSA_BACKEND_URL || DEFAULT_ADMIN_URL,
|
backendUrl: process.env.MEDUSA_BACKEND_URL || DEFAULT_ADMIN_URL,
|
||||||
outDir: ".medusa/admin",
|
|
||||||
path: "/app",
|
path: "/app",
|
||||||
...config.admin,
|
...config.admin,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,35 @@ import type tsStatic from "typescript"
|
|||||||
import { logger } from "@medusajs/framework/logger"
|
import { logger } from "@medusajs/framework/logger"
|
||||||
import { ConfigModule } from "@medusajs/framework/types"
|
import { ConfigModule } from "@medusajs/framework/types"
|
||||||
import { getConfigFile } from "@medusajs/framework/utils"
|
import { getConfigFile } from "@medusajs/framework/utils"
|
||||||
|
import {
|
||||||
|
ADMIN_ONLY_OUTPUT_DIR,
|
||||||
|
ADMIN_RELATIVE_OUTPUT_DIR,
|
||||||
|
ADMIN_SOURCE_DIR,
|
||||||
|
} from "../utils"
|
||||||
|
|
||||||
const ADMIN_FOLDER = "src/admin"
|
|
||||||
const INTEGRATION_TESTS_FOLDER = "integration-tests"
|
const INTEGRATION_TESTS_FOLDER = "integration-tests"
|
||||||
|
|
||||||
|
function computeDist(
|
||||||
|
projectRoot: string,
|
||||||
|
tsConfig: { options: { outDir?: string } }
|
||||||
|
): string {
|
||||||
|
const distFolder = tsConfig.options.outDir ?? ".medusa/server"
|
||||||
|
return path.isAbsolute(distFolder)
|
||||||
|
? distFolder
|
||||||
|
: path.join(projectRoot, distFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadTsConfig(projectRoot: string) {
|
||||||
|
const ts = await import("typescript")
|
||||||
|
const tsConfig = parseTSConfig(projectRoot, ts)
|
||||||
|
if (!tsConfig) {
|
||||||
|
logger.error("Unable to compile backend source")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return tsConfig!
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the file to the destination without throwing any
|
* Copies the file to the destination without throwing any
|
||||||
* errors if the source file is missing
|
* errors if the source file is missing
|
||||||
@@ -98,21 +123,14 @@ function parseTSConfig(projectRoot: string, ts: typeof tsStatic) {
|
|||||||
/**
|
/**
|
||||||
* Builds the backend project using TSC
|
* Builds the backend project using TSC
|
||||||
*/
|
*/
|
||||||
async function buildBackend(projectRoot: string): Promise<boolean> {
|
async function buildBackend(
|
||||||
|
projectRoot: string,
|
||||||
|
tsConfig: tsStatic.ParsedCommandLine
|
||||||
|
): Promise<boolean> {
|
||||||
const startTime = process.hrtime()
|
const startTime = process.hrtime()
|
||||||
logger.info("Compiling backend source...")
|
logger.info("Compiling backend source...")
|
||||||
|
|
||||||
const ts = await import("typescript")
|
const dist = computeDist(projectRoot, tsConfig)
|
||||||
const tsConfig = parseTSConfig(projectRoot, ts)
|
|
||||||
if (!tsConfig) {
|
|
||||||
logger.error("Unable to compile backend source")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const distFolder = tsConfig.options.outDir ?? ".medusa/server"
|
|
||||||
const dist = path.isAbsolute(distFolder)
|
|
||||||
? distFolder
|
|
||||||
: path.join(projectRoot, distFolder)
|
|
||||||
|
|
||||||
logger.info(`Removing existing "${path.relative(projectRoot, dist)}" folder`)
|
logger.info(`Removing existing "${path.relative(projectRoot, dist)}" folder`)
|
||||||
await clean(dist)
|
await clean(dist)
|
||||||
@@ -123,11 +141,12 @@ async function buildBackend(projectRoot: string): Promise<boolean> {
|
|||||||
*/
|
*/
|
||||||
const filesToCompile = tsConfig.fileNames.filter((fileName) => {
|
const filesToCompile = tsConfig.fileNames.filter((fileName) => {
|
||||||
return (
|
return (
|
||||||
!fileName.includes(`${ADMIN_FOLDER}/`) &&
|
!fileName.includes(`${ADMIN_SOURCE_DIR}/`) &&
|
||||||
!fileName.includes(`${INTEGRATION_TESTS_FOLDER}/`)
|
!fileName.includes(`${INTEGRATION_TESTS_FOLDER}/`)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ts = await import("typescript")
|
||||||
const program = ts.createProgram(filesToCompile, {
|
const program = ts.createProgram(filesToCompile, {
|
||||||
...tsConfig.options,
|
...tsConfig.options,
|
||||||
...{
|
...{
|
||||||
@@ -195,24 +214,41 @@ async function buildBackend(projectRoot: string): Promise<boolean> {
|
|||||||
/**
|
/**
|
||||||
* Builds the frontend project using the "@medusajs/admin-bundler"
|
* Builds the frontend project using the "@medusajs/admin-bundler"
|
||||||
*/
|
*/
|
||||||
async function buildFrontend(projectRoot: string): Promise<boolean> {
|
async function buildFrontend(
|
||||||
|
projectRoot: string,
|
||||||
|
adminOnly: boolean,
|
||||||
|
tsConfig: tsStatic.ParsedCommandLine
|
||||||
|
): Promise<boolean> {
|
||||||
const startTime = process.hrtime()
|
const startTime = process.hrtime()
|
||||||
const configFile = await loadMedusaConfig(projectRoot)
|
const configFile = await loadMedusaConfig(projectRoot)
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminSource = path.join(projectRoot, ADMIN_FOLDER)
|
const dist = computeDist(projectRoot, tsConfig)
|
||||||
|
|
||||||
|
const adminOutputPath = adminOnly
|
||||||
|
? path.join(projectRoot, ADMIN_ONLY_OUTPUT_DIR)
|
||||||
|
: path.join(dist, ADMIN_RELATIVE_OUTPUT_DIR)
|
||||||
|
|
||||||
|
const adminSource = path.join(projectRoot, ADMIN_SOURCE_DIR)
|
||||||
const adminOptions = {
|
const adminOptions = {
|
||||||
disable: false,
|
disable: false,
|
||||||
sources: [adminSource],
|
sources: [adminSource],
|
||||||
...configFile.configModule.admin,
|
...configFile.configModule.admin,
|
||||||
|
outDir: adminOutputPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adminOptions.disable) {
|
if (adminOptions.disable && !adminOnly) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!adminOptions.disable && adminOnly) {
|
||||||
|
logger.warn(
|
||||||
|
`You are building using the flag --admin-only but the admin is enabled in your medusa-config, If you intend to host the dashboard separately you should disable the admin in your medusa config`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("Compiling frontend source...")
|
logger.info("Compiling frontend source...")
|
||||||
const { build: buildProductionBuild } = await import(
|
const { build: buildProductionBuild } = await import(
|
||||||
@@ -231,7 +267,28 @@ async function buildFrontend(projectRoot: string): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ({ directory }: { directory: string }) {
|
export default async function ({
|
||||||
|
directory,
|
||||||
|
adminOnly,
|
||||||
|
}: {
|
||||||
|
directory: string
|
||||||
|
adminOnly: boolean
|
||||||
|
}): Promise<boolean> {
|
||||||
logger.info("Starting build...")
|
logger.info("Starting build...")
|
||||||
await Promise.all([buildBackend(directory), buildFrontend(directory)])
|
|
||||||
|
const tsConfig = await loadTsConfig(directory)
|
||||||
|
if (!tsConfig) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises: Promise<any>[] = []
|
||||||
|
|
||||||
|
if (!adminOnly) {
|
||||||
|
promises.push(buildBackend(directory, tsConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(buildFrontend(directory, adminOnly, tsConfig))
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { AdminOptions, ConfigModule } from "@medusajs/framework/types"
|
|||||||
import { Express } from "express"
|
import { Express } from "express"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { ADMIN_RELATIVE_OUTPUT_DIR } from "../utils"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
app: Express
|
app: Express
|
||||||
@@ -10,10 +11,9 @@ type Options = {
|
|||||||
rootDirectory: string
|
rootDirectory: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntializedOptions = Required<
|
type IntializedOptions = Required<Pick<AdminOptions, "path" | "disable">> &
|
||||||
Pick<AdminOptions, "path" | "disable" | "outDir">
|
|
||||||
> &
|
|
||||||
AdminOptions & {
|
AdminOptions & {
|
||||||
|
outDir: string
|
||||||
sources?: string[]
|
sources?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ export default async function adminLoader({
|
|||||||
disable: false,
|
disable: false,
|
||||||
sources,
|
sources,
|
||||||
...admin,
|
...admin,
|
||||||
|
outDir: path.join(rootDirectory, ADMIN_RELATIVE_OUTPUT_DIR),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adminOptions?.disable) {
|
if (adminOptions?.disable) {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const ADMIN_SOURCE_DIR = "src/admin"
|
||||||
|
export const ADMIN_RELATIVE_OUTPUT_DIR = "./public/admin"
|
||||||
|
export const ADMIN_ONLY_OUTPUT_DIR = ".medusa/admin"
|
||||||
@@ -2,3 +2,4 @@ export * from "./clean-response-data"
|
|||||||
export * from "./exception-formatter"
|
export * from "./exception-formatter"
|
||||||
export * from "./middlewares"
|
export * from "./middlewares"
|
||||||
export * from "./define-middlewares"
|
export * from "./define-middlewares"
|
||||||
|
export * from "./admin-consts"
|
||||||
|
|||||||
Reference in New Issue
Block a user