diff --git a/CHANGELOG.md b/CHANGELOG.md index d3caef07..b2ccbc98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ Any BREAKING CHANGE between minor versions will be documented here in upper case - Support for negative tags in `search` plugin. For example: `search.pages("tag1 !tag2")`. - Support for remote files in `sass` plugin. +- Improved `lume init` for some plugins like `mdx` or `tailwindcss`. + +### Removed +- `lume import-map` command. ### Changed - Refactor of the internal file system manager, reducing complexity and fixing some bugs. diff --git a/cli.ts b/cli.ts index fb2d18d2..dcb38428 100644 --- a/cli.ts +++ b/cli.ts @@ -5,7 +5,6 @@ import initCommand from "./cli/init.ts"; import upgradeCommand from "./cli/upgrade.ts"; import runCommand from "./cli/run.ts"; import buildCommand from "./cli/build.ts"; -import importMapCommand from "./cli/import_map.ts"; import createCommand from "./cli/create.ts"; const init = new Command() @@ -32,15 +31,6 @@ const upgrade = new Command() .example("lume upgrade --dev", "Upgrades to the latest development version.") .action(upgradeCommand); -const importMap = new Command() - .description("Create or update a import map file with the Lume imports.") - .example("lume import-map", "Create/update the file import_map.json.") - .option( - "--plugins ", - "Name of the plugins installed, in order to configure the import_map.json and deno.json files", - ) - .action(importMapCommand); - const create = new Command() .description("Create a new page from a archetype.") .example( @@ -150,7 +140,6 @@ const lume = new Command() .command("new [arguments...]", create) .command("init", init) .command("upgrade", upgrade) - .command("import-map", importMap) .command("run ", run) .command("completions", new CompletionsCommand()); diff --git a/cli/import_map.ts b/cli/import_map.ts deleted file mode 100644 index d33167a9..00000000 --- a/cli/import_map.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { - getConfigFile, - initPlugins, - readDenoConfig, - writeDenoConfig, -} from "../core/utils.ts"; - -import type { DenoConfigResult } from "../core/utils.ts"; - -interface Options { - plugins?: string[]; -} - -/** Generate import_map.json and deno.json files */ -export default function ({ plugins }: Options = {}) { - return importMap(new URL(import.meta.resolve("../")), plugins || []); -} - -export async function importMap(url: URL, plugins: string[] = []) { - const denoConfig: DenoConfigResult = await readDenoConfig() || { - config: {}, - file: "deno.json", - }; - - denoConfig.importMap ??= { imports: {} }; - - const { config, importMap, file } = denoConfig; - - // Configure the import map - if (Deno.version.deno < "1.30.0") { - config.importMap ||= "./import_map.json"; - } - - const oldUrl = importMap.imports["lume/"]; - const newUrl = new URL("./", url).href; - importMap.imports["lume/"] = newUrl; - - for (const [specifier, url] of Object.entries(importMap.imports)) { - if (url.startsWith(oldUrl)) { - importMap.imports[specifier] = url.replace(oldUrl, newUrl); - } - } - - // Configure lume tasks - const tasks = config.tasks || {}; - tasks.lume = `echo "import 'lume/cli.ts'" | deno run --unstable -A -`; - tasks.build = "deno task lume"; - tasks.serve = "deno task lume -s"; - config.tasks = tasks; - - // Transform the import map and deno config by the plugins - await Promise.all(plugins.map((name) => { - const init = initPlugins[name]; - - if (init) { - init(denoConfig); - } - })); - - // Write the configuration - await writeDenoConfig({ file, importMap, config }); - - // Hack to fix: https://github.com/denoland/deno/issues/16901 - const configFile = await getConfigFile(); - - if (configFile) { - console.log("Reloading Deno cache..."); - - const command = new Deno.Command(Deno.execPath(), { - args: [ - "cache", - "--unstable", - "--reload", - configFile, - ], - stdout: "inherit", - stderr: "inherit", - }); - - await command.output(); - } -} diff --git a/cli/init.ts b/cli/init.ts index 8dbc47c1..3992dec1 100644 --- a/cli/init.ts +++ b/cli/init.ts @@ -1,23 +1,16 @@ import { brightGreen, gray } from "../deps/colors.ts"; -import { pluginNames } from "../core/utils.ts"; -import importMap from "./import_map.ts"; +import { + pluginNames, + updateLumeVersion, + writeDenoConfig, +} from "../core/utils.ts"; import { Checkbox, Confirm, Select } from "../deps/cliffy.ts"; import { outdent } from "../deps/outdent.ts"; -/** Generate a _config.js file */ -export default function (): Promise { - return init(); -} - -export async function init() { - const plugins = await initConfig(); - if (!plugins) return; - await importMap({ plugins }); - welcome(); -} +import type { DenoConfigResult } from "../core/utils.ts"; -/** (Re)configure lume config file */ -async function initConfig(): Promise { +/** Init Lume in the current directory */ +export default async function init(): Promise { const configFile = await getConfigFile(); if (!configFile) { @@ -27,6 +20,13 @@ async function initConfig(): Promise { const plugins = await getPlugins(); + const denoConfig: DenoConfigResult = { + config: {}, + file: "deno.json", + }; + + initPlugins(plugins, denoConfig); + // Generate the code for the config file const code = [`import lume from "lume/mod.ts";`]; @@ -51,7 +51,11 @@ async function initConfig(): Promise { await Deno.writeTextFile(configFile, code.join("\n")); console.log(); console.log("Lume configuration file saved:", gray(configFile)); - return plugins; + + const url = new URL(import.meta.resolve("../")); + updateLumeVersion(url, denoConfig); + writeDenoConfig(denoConfig); + welcome(); } /** @@ -150,3 +154,59 @@ function welcome() { console.log(message); } + +function initPlugins(plugins: string[], denoConfig: DenoConfigResult) { + // Ensure that jsx and jsx_preact are not used at the same time and are loaded before mdx + if (plugins.includes("mdx")) { + const jsx = plugins.indexOf("jsx"); + const jsx_preact = plugins.indexOf("jsx_preact"); + + if (jsx !== -1 && jsx_preact !== -1) { + throw new Error( + "You can't use both the jsx and jsx_preact plugins at the same time.", + ); + } + + if (jsx !== -1) { + // Ensure jsx is loaded before mdx + plugins.splice(jsx, 1); + plugins.unshift("jsx"); + } else if (jsx_preact !== -1) { + // Ensure jsx_preact is loaded before mdx + plugins.splice(jsx_preact, 1); + plugins.unshift("jsx_preact"); + } else { + // Use jsx by default + plugins.unshift("jsx"); + } + } + + if (plugins.includes("jsx")) { + denoConfig.config.compilerOptions ||= {}; + denoConfig.config.compilerOptions.jsx = "react-jsx"; + denoConfig.config.compilerOptions.jsxImportSource = "react"; + + // Add jsx-runtime import to import_map. + denoConfig.importMap ||= { imports: {} }; + denoConfig.importMap.imports["react/jsx-runtime"] = + "https://esm.sh/react@18.2.0/jsx-runtime"; + } + + if (plugins.includes("jsx_preact")) { + denoConfig.config.compilerOptions ||= {}; + denoConfig.config.compilerOptions.jsx = "react-jsx"; + denoConfig.config.compilerOptions.jsxImportSource = "npm:preact"; + } + + // Ensure that tailwindcss is loaded before postcss + if (plugins.includes("tailwindcss")) { + const tailwindcss = plugins.indexOf("tailwindcss"); + const postcss = plugins.indexOf("postcss"); + + if (postcss !== -1) { + plugins.splice(postcss, 1); + } + + plugins.splice(tailwindcss, 1, "tailwindcss", "postcss"); + } +} diff --git a/cli/upgrade.ts b/cli/upgrade.ts index 4976de40..fc40b7e5 100644 --- a/cli/upgrade.ts +++ b/cli/upgrade.ts @@ -2,9 +2,11 @@ import { getLatestDevelopmentVersion, getLatestVersion, getLumeVersion, + readDenoConfig, + updateLumeVersion, + writeDenoConfig, } from "../core/utils.ts"; import { brightGreen, gray } from "../deps/colors.ts"; -import { importMap } from "./import_map.ts"; interface Options { dev?: boolean; @@ -32,7 +34,6 @@ export async function upgrade(dev = false, version?: string) { : "You're using the latest development version of Lume:", brightGreen(latest), ); - await importMap(url); console.log(); return; } @@ -43,7 +44,14 @@ export async function upgrade(dev = false, version?: string) { : `New version available. Updating Lume to ${brightGreen(latest)}...`, ); - await importMap(url); + const denoConfig = await readDenoConfig(); + + if (!denoConfig) { + throw new Error("No Deno config file found"); + } + + updateLumeVersion(url, denoConfig); + await writeDenoConfig(denoConfig); console.log(); console.log("Update successful!"); diff --git a/core/utils.ts b/core/utils.ts index 9b43fef7..ed4d93e7 100644 --- a/core/utils.ts +++ b/core/utils.ts @@ -47,28 +47,6 @@ export const pluginNames = [ "windi_css", ]; -/** A list of the available plugins with init configurations */ -export const initPlugins: Record< - string, - (denoConfig: DenoConfigResult) => void -> = { - jsx(denoConfig) { - denoConfig.config.compilerOptions ||= {}; - denoConfig.config.compilerOptions.jsx = "react-jsx"; - denoConfig.config.compilerOptions.jsxImportSource = "react"; - - // Add jsx-runtime import to import_map. - denoConfig.importMap ||= { imports: {} }; - denoConfig.importMap.imports["react/jsx-runtime"] = - "https://esm.sh/react@18.2.0/jsx-runtime"; - }, - jsx_preact(denoConfig) { - denoConfig.config.compilerOptions ||= {}; - denoConfig.config.compilerOptions.jsx = "react-jsx"; - denoConfig.config.compilerOptions.jsxImportSource = "npm:preact"; - }, -}; - export function log(...lines: (string | undefined)[]) { console.log("----------------------------------------"); lines.forEach((line) => line && console.log(line)); @@ -358,6 +336,36 @@ export async function readDenoConfig(): Promise { } } +export function updateLumeVersion(url: URL, denoConfig: DenoConfigResult) { + denoConfig.importMap ??= { imports: {} }; + + const { config, importMap } = denoConfig; + + // Configure the import map + if (Deno.version.deno < "1.30.0") { + config.importMap ||= "./import_map.json"; + } + + const oldUrl = importMap.imports["lume/"]; + const newUrl = new URL("./", url).href; + importMap.imports["lume/"] = newUrl; + + for (const [specifier, url] of Object.entries(importMap.imports)) { + if (url.startsWith(oldUrl)) { + importMap.imports[specifier] = url.replace(oldUrl, newUrl); + } + } + + // Configure lume tasks + const tasks = config.tasks || {}; + if (!tasks.lume || !tasks.lume.includes(`echo "import 'lume/cli.ts'"`)) { + tasks.lume = `echo "import 'lume/cli.ts'" | deno run --unstable -A -`; + tasks.build = "deno task lume"; + tasks.serve = "deno task lume -s"; + } + config.tasks = tasks; +} + /** Update the Deno configuration */ export async function writeDenoConfig(options: DenoConfigResult) { const { file, config, importMap } = options;