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:
Adrien de Peretti
2024-10-15 17:59:47 +02:00
committed by GitHub
parent 4a03bdbb86
commit 84fa6ccde5
9 changed files with 95 additions and 48 deletions

View File

@@ -1,6 +1,7 @@
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"> & {
outDir: string
sources?: string[]
}

View File

@@ -354,9 +354,15 @@ function buildLocalCommands(cli, isLocalProject) {
),
})
.command({
command: `build`,
desc: `Build your project.`,
builder: (_) => _,
command: "build",
desc: "Build your project.",
builder: (_) =>
_.option("admin-only", {
default: false,
type: "boolean",
describe:
"Only build the admin to serve it separately (outDir .medusa/admin)",
}),
handler: handlerP(
getCommandHandler(`build`, (args, cmd) => {
process.env.NODE_ENV = process.env.NODE_ENV || `development`

View File

@@ -49,21 +49,6 @@ export interface AdminOptions {
* ```
*/
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.
*

View File

@@ -7,7 +7,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},
@@ -154,7 +153,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},
@@ -307,7 +305,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},
@@ -466,7 +463,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},
@@ -621,7 +617,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},
@@ -771,7 +766,6 @@ describe("defineConfig", function () {
{
"admin": {
"backendUrl": "http://localhost:9000",
"outDir": ".medusa/admin",
"path": "/app",
},
"featureFlags": {},

View File

@@ -90,7 +90,6 @@ export function defineConfig(config: Config = {}): ConfigModule {
*/
const admin: ConfigModule["admin"] = {
backendUrl: process.env.MEDUSA_BACKEND_URL || DEFAULT_ADMIN_URL,
outDir: ".medusa/admin",
path: "/app",
...config.admin,
}

View File

@@ -4,10 +4,35 @@ import type tsStatic from "typescript"
import { logger } from "@medusajs/framework/logger"
import { ConfigModule } from "@medusajs/framework/types"
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"
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
* errors if the source file is missing
@@ -98,21 +123,14 @@ function parseTSConfig(projectRoot: string, ts: typeof tsStatic) {
/**
* 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()
logger.info("Compiling backend source...")
const ts = await import("typescript")
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)
const dist = computeDist(projectRoot, tsConfig)
logger.info(`Removing existing "${path.relative(projectRoot, dist)}" folder`)
await clean(dist)
@@ -123,11 +141,12 @@ async function buildBackend(projectRoot: string): Promise<boolean> {
*/
const filesToCompile = tsConfig.fileNames.filter((fileName) => {
return (
!fileName.includes(`${ADMIN_FOLDER}/`) &&
!fileName.includes(`${ADMIN_SOURCE_DIR}/`) &&
!fileName.includes(`${INTEGRATION_TESTS_FOLDER}/`)
)
})
const ts = await import("typescript")
const program = ts.createProgram(filesToCompile, {
...tsConfig.options,
...{
@@ -195,24 +214,41 @@ async function buildBackend(projectRoot: string): Promise<boolean> {
/**
* 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 configFile = await loadMedusaConfig(projectRoot)
if (!configFile) {
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 = {
disable: false,
sources: [adminSource],
...configFile.configModule.admin,
outDir: adminOutputPath,
}
if (adminOptions.disable) {
if (adminOptions.disable && !adminOnly) {
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 {
logger.info("Compiling frontend source...")
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...")
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
}

View File

@@ -3,6 +3,7 @@ import { AdminOptions, ConfigModule } from "@medusajs/framework/types"
import { Express } from "express"
import fs from "fs"
import path from "path"
import { ADMIN_RELATIVE_OUTPUT_DIR } from "../utils"
type Options = {
app: Express
@@ -10,10 +11,9 @@ type Options = {
rootDirectory: string
}
type IntializedOptions = Required<
Pick<AdminOptions, "path" | "disable" | "outDir">
> &
type IntializedOptions = Required<Pick<AdminOptions, "path" | "disable">> &
AdminOptions & {
outDir: string
sources?: string[]
}
@@ -39,6 +39,7 @@ export default async function adminLoader({
disable: false,
sources,
...admin,
outDir: path.join(rootDirectory, ADMIN_RELATIVE_OUTPUT_DIR),
}
if (adminOptions?.disable) {

View File

@@ -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"

View File

@@ -2,3 +2,4 @@ export * from "./clean-response-data"
export * from "./exception-formatter"
export * from "./middlewares"
export * from "./define-middlewares"
export * from "./admin-consts"