chore: move next admin packages to core repo (#5983)

**What**
- Move packages for `next` version of admin to core repo

**Other**
- Since this PR introduces packages that depend on Vite 5, it also introduces @types/node@^20. We have never had a direct dependency on the types package for Node, and as far as I can see that has resulted in us using the types from Node.js@8, as those are a dependency of one of our dependencies. With the introduction of @types/node@^20, two of our packages had TS errors because they were using the NodeJS.Timer type, which was deprecated in Node.js@14. We should add specific @types/node packages to all our packages, but I haven't done so in this PR to keep it as clean as possible.
- Q: @olivermrbl I've added the new packages to the ignore list for changeset, is this enough to prevent them from being published?
This commit is contained in:
Kasper Fabricius Kristensen
2024-01-08 10:26:46 +01:00
committed by GitHub
parent 479a8b82a9
commit f868775861
491 changed files with 11332 additions and 428 deletions
@@ -0,0 +1,22 @@
import { resolve } from "path"
import { build as command } from "vite"
import { createViteConfig } from "./create-vite-config"
type BuildArgs = {
root?: string
}
export async function build({ root }: BuildArgs) {
const config = await createViteConfig({
build: {
outDir: resolve(process.cwd(), "build"),
},
})
if (!config) {
return
}
await command(config)
}
@@ -0,0 +1,46 @@
import { readFileSync } from "fs"
import glob from "glob"
import { relative, resolve } from "path"
import { build as command } from "vite"
type BundleArgs = {
root?: string | undefined
watch?: boolean | undefined
}
export async function bundle({ watch, root }: BundleArgs) {
const resolvedRoot = root
? resolve(process.cwd(), root)
: resolve(process.cwd(), "src", "admin")
const files = glob.sync(`${resolvedRoot}/**/*.{ts,tsx,js,jsx}`)
const input: Record<string, string> = {}
for (const file of files) {
const relativePath = relative(resolvedRoot, file)
input[relativePath] = file
}
const packageJson = JSON.parse(
readFileSync(resolve(process.cwd(), "package.json"), "utf-8")
)
const external = [
...Object.keys(packageJson.dependencies),
"@medusajs/ui",
"@medusajs/ui-preset",
"react",
"react-dom",
"react-router-dom",
"react-hook-form",
]
await command({
build: {
watch: watch ? {} : undefined,
rollupOptions: {
input: input,
external: external,
},
},
})
}
@@ -0,0 +1,253 @@
import inject from "@medusajs/vite-plugin-extension"
import react from "@vitejs/plugin-react"
import deepmerge from "deepmerge"
import { createRequire } from "module"
import path from "path"
import { type Config } from "tailwindcss"
import { ContentConfig } from "tailwindcss/types/config"
import { InlineConfig, Logger, createLogger, mergeConfig } from "vite"
const require = createRequire(import.meta.url)
export async function createViteConfig(
inline: InlineConfig
): Promise<InlineConfig | null> {
const root = process.cwd()
const logger = createCustomLogger()
let dashboardRoot: string | null = null
try {
dashboardRoot = path.dirname(require.resolve("@medusajs/dashboard"))
} catch (err) {
dashboardRoot = null
}
if (!dashboardRoot) {
logger.error(
"Unable to find @medusajs/dashboard. Please install it in your project, or specify the root directory."
)
return null
}
const { plugins, userConfig } = (await loadConfig(root, logger)) ?? {}
let viteConfig: InlineConfig = mergeConfig(inline, {
plugins: [
react(),
inject({
sources: plugins,
}),
],
configFile: false,
root: dashboardRoot,
css: {
postcss: {
plugins: [
require("tailwindcss")({
config: createTwConfig(process.cwd(), dashboardRoot),
}),
require("autoprefixer"),
],
},
},
} satisfies InlineConfig)
if (userConfig) {
viteConfig = await userConfig(viteConfig)
}
return viteConfig
}
function mergeTailwindConfigs(config1: Config, config2: Config): Config {
const content1 = config1.content
const content2 = config2.content
let mergedContent: ContentConfig
if (Array.isArray(content1) && Array.isArray(content2)) {
mergedContent = [...content1, ...content2]
} else if (!Array.isArray(content1) && !Array.isArray(content2)) {
mergedContent = {
files: [...content1.files, ...content2.files],
relative: content1.relative || content2.relative,
extract: { ...content1.extract, ...content2.extract },
transform: { ...content1.transform, ...content2.transform },
}
} else {
throw new Error("Cannot merge content fields of different types")
}
const mergedConfig = deepmerge(config1, config2)
mergedConfig.content = mergedContent
console.log(config1.presets, config2.presets)
// Ensure presets only contain unique values
mergedConfig.presets = config1.presets || []
return mergedConfig
}
function createTwConfig(root: string, dashboardRoot: string) {
const uiRoot = path.join(
path.dirname(require.resolve("@medusajs/ui")),
"**/*.{js,jsx,ts,tsx}"
)
const baseConfig: Config = {
presets: [require("@medusajs/ui-preset")],
content: [
`${root}/src/admin/**/*.{js,jsx,ts,tsx}`,
`${dashboardRoot}/src/**/*.{js,jsx,ts,tsx}`,
uiRoot,
],
darkMode: "class",
theme: {
extend: {},
},
plugins: [],
}
let userConfig: Config | null = null
const extensions = ["js", "cjs", "mjs", "ts", "cts", "mts"]
for (const ext of extensions) {
try {
userConfig = require(path.join(root, `tailwind.config.${ext}`))
break
} catch (err) {
console.log("Failed to load tailwind config with extension", ext, err)
userConfig = null
}
}
if (!userConfig) {
return baseConfig
}
return mergeTailwindConfigs(baseConfig, userConfig)
}
function createCustomLogger() {
const logger = createLogger("info", {
prefix: "medusa-admin",
})
const loggerInfo = logger.info
logger.info = (msg, opts) => {
if (
msg.includes("hmr invalidate") &&
msg.includes(
"Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports"
)
) {
return
}
loggerInfo(msg, opts)
}
return logger
}
interface PluginOptions extends Record<string, unknown> {
enableUI?: boolean
}
type Plugin =
| string
| {
resolve: string
options?: PluginOptions
}
interface MedusaConfig extends Record<string, unknown> {
plugins?: Plugin[]
}
async function loadConfig(root: string, logger: Logger) {
const configPath = path.resolve(root, "medusa-config.js")
const config: MedusaConfig = await import(configPath)
.then((c) => c)
.catch((e) => {
if (e.code === "ERR_MODULE_NOT_FOUND") {
logger.warn(
"Root 'medusa-config.js' file not found; extensions won't load. If running Admin UI as a standalone app, use the 'standalone' option.",
{
timestamp: true,
}
)
} else {
logger.error(
`An error occured while attempting to load '${configPath}':\n${e}`,
{
timestamp: true,
}
)
}
return null
})
if (!config) {
return
}
if (!config.plugins?.length) {
logger.info(
"No plugins in 'medusa-config.js', no extensions will load. To enable Admin UI extensions, add them to the 'plugins' array in 'medusa-config.js'.",
{
timestamp: true,
}
)
return
}
const uiPlugins = config.plugins
.filter((p) => typeof p !== "string" && p.options?.enableUI)
.map((p: Plugin) => {
return typeof p === "string" ? p : p.resolve
})
const extensionSources = uiPlugins.map((p) => {
return path.resolve(require.resolve(p), "dist", "admin")
})
const rootSource = path.resolve(process.cwd(), "src", "admin")
extensionSources.push(rootSource)
const adminPlugin = config.plugins.find((p) =>
typeof p === "string"
? p === "@medusajs/admin"
: p.resolve === "@medusajs/admin"
)
if (!adminPlugin) {
logger.info(
"No @medusajs/admin in 'medusa-config.js', no extensions will load. To enable Admin UI extensions, add it to the 'plugins' array in 'medusa-config.js'.",
{
timestamp: true,
}
)
return
}
const adminPluginOptions =
typeof adminPlugin !== "string" && !!adminPlugin.options
? adminPlugin.options
: {}
const viteConfig = adminPluginOptions.withFinal as
| ((config: InlineConfig) => InlineConfig)
| ((config: InlineConfig) => Promise<InlineConfig>)
| undefined
return {
plugins: extensionSources,
userConfig: viteConfig,
}
}
@@ -0,0 +1,28 @@
import { createServer } from "vite"
// @ts-ignore
import { createViteConfig } from "./create-vite-config"
type DevArgs = {
port?: number | undefined
host?: string | boolean | undefined
}
export async function dev({ port = 5173, host }: DevArgs) {
const config = await createViteConfig({
server: {
port,
host,
},
})
if (!config) {
return
}
const server = await createServer(config)
await server.listen()
server.printUrls()
server.bindCLIShortcuts({ print: true })
}