diff --git a/.changeset/witty-mirrors-confess.md b/.changeset/witty-mirrors-confess.md new file mode 100644 index 00000000..cc4745af --- /dev/null +++ b/.changeset/witty-mirrors-confess.md @@ -0,0 +1,5 @@ +--- +"create-mdxts": patch +--- + +Improves CLI onboarding by prompting to copy the blog example if not run in a project. diff --git a/examples/blog/README.md b/examples/blog/README.md index 162da1b1..540a4e30 100644 --- a/examples/blog/README.md +++ b/examples/blog/README.md @@ -6,16 +6,8 @@ This example shows how to build a blog with MDXTS and Next.js. ## How to use -Use [`create-mdxts`](https://github.com/souporserious/mdxts/tree/main/packages/create-mdxts) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to copy this example to your machine: +Use the [`create-mdxts`](https://github.com/souporserious/mdxts/tree/main/packages/create-mdxts) CLI with [npx](https://docs.npmjs.com/cli/v10/commands/npx) to copy this example to your machine: ```bash -npm create-mdxts --example=blog -``` - -```bash -yarn create mdxts --example=blog -``` - -```bash -pnpm create mdxts --example=blog +npx create-mdxts --example blog ``` diff --git a/packages/create-mdxts/src/fetch-example.ts b/packages/create-mdxts/src/fetch-example.ts index e8bb2499..c8570676 100644 --- a/packages/create-mdxts/src/fetch-example.ts +++ b/packages/create-mdxts/src/fetch-example.ts @@ -1,18 +1,28 @@ import path from 'node:path' -import { createWriteStream, mkdirSync } from 'node:fs' +import { + createWriteStream, + mkdirSync, + readFileSync, + writeFileSync, +} from 'node:fs' import { Readable } from 'node:stream' import { pipeline } from 'node:stream/promises' import chalk from 'chalk' +import { getCurrentVersion } from './is-package-outdated' import { Log, askQuestion } from './utils' /** Fetches the contents of an MDXTS example from the GitHub repository and downloads them to the local file system. */ -export async function fetchExample(exampleSlug: string) { +export async function fetchExample( + exampleSlug: string, + prependMessage?: string +) { await fetchGitHubDirectory({ owner: 'souporserious', repo: 'mdxts', branch: 'main', directoryPath: `examples/${exampleSlug}`, + prependMessage, }) } @@ -23,12 +33,14 @@ async function fetchGitHubDirectory({ branch, directoryPath, basePath = '.', + prependMessage, }: { owner: string repo: string branch: string directoryPath: string basePath?: string + prependMessage?: string }) { const isRoot = basePath === '.' const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${directoryPath}?ref=${branch}` @@ -48,7 +60,7 @@ async function fetchGitHubDirectory({ if (isRoot) { const userBasePath = await askQuestion( - `Download the ${directoryName} example to ${chalk.bold( + `${prependMessage}Download the ${directoryName} example to ${chalk.bold( workingDirectory )}? Press enter to proceed or specify a different directory: ` ) @@ -87,17 +99,19 @@ async function fetchGitHubDirectory({ if (isRoot) { const { detectPackageManager } = await import('@antfu/install-pkg') - const packageManager = await detectPackageManager() + const packageManager = await detectPackageManager(process.cwd()) const introInstallInstructions = basePath === '.' ? `Run` : `Change to the ${chalk.bold(directoryName)} directory and run` + await reformatPackageJson(workingDirectory, basePath) + Log.success( `Example ${chalk.bold( directoryName )} fetched and configured successfully! ${introInstallInstructions} ${chalk.bold( - `${packageManager} install` + `${packageManager ?? 'npm'} install` )} to install the dependencies and get started.` ) } @@ -129,3 +143,22 @@ const downloadFile = async (url: string, filePath: string) => { await pipeline(stream, fileStream) } + +/** Re-format package.json file to remove monorepo dependencies and use the latest MDXTS version. */ +async function reformatPackageJson(workingDirectory: string, basePath: string) { + const packageJsonPath = path.join(workingDirectory, basePath, 'package.json') + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) + + // Remove "@example/" prefix from package name + packageJson.name = packageJson.name.replace('@example/', '') + + // Replace mdxts "workspace:*" with the latest version of the package + packageJson.dependencies['mdxts'] = await getCurrentVersion() + + // Remove shiki and prettier dependencies since they are only required for the monorepo + delete packageJson.dependencies['prettier'] + delete packageJson.dependencies['shiki'] + + // Write the updated package.json file + writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8') +} diff --git a/packages/create-mdxts/src/index.ts b/packages/create-mdxts/src/index.ts index ab8ca40e..01f8236d 100644 --- a/packages/create-mdxts/src/index.ts +++ b/packages/create-mdxts/src/index.ts @@ -18,6 +18,7 @@ import { Log, askQuestion, askYesNo, getFilePatternBaseName } from './utils' const states = { INITIAL_STATE: 'initialState', + CHECK_NEXT_INSTALLED: 'checkNextInstalled', CHECK_TYPESCRIPT_INSTALLED: 'checkTypescriptInstalled', CHECK_MDXTS_INSTALLED: 'checkMdxtsInstalled', INSTALL_MDXTS: 'installMdxts', @@ -55,6 +56,15 @@ export async function start() { try { switch (currentState) { case states.INITIAL_STATE: + const packageJsonPath = join(process.cwd(), 'package.json') + if (existsSync(packageJsonPath)) { + currentState = states.CHECK_NEXT_INSTALLED + } else { + await fetchExample('blog', 'No project was found. ') + currentState = states.SUCCESS_STATE + } + break + case states.CHECK_NEXT_INSTALLED: checkNextJsProject() currentState = states.CHECK_TYPESCRIPT_INSTALLED break @@ -146,11 +156,6 @@ start() export function checkNextJsProject() { const packageJsonPath = join(process.cwd(), 'package.json') - if (!existsSync(packageJsonPath)) { - throw new Error( - 'No package.json found. Please run this command in a Next.js project directory.' - ) - } const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) if (!packageJson.devDependencies?.next && !packageJson.dependencies?.next) { throw new Error( diff --git a/packages/create-mdxts/src/is-package-outdated.ts b/packages/create-mdxts/src/is-package-outdated.ts index a2a67765..5d283085 100644 --- a/packages/create-mdxts/src/is-package-outdated.ts +++ b/packages/create-mdxts/src/is-package-outdated.ts @@ -60,7 +60,7 @@ function shouldRefreshCache() { return now - cacheContent.cachedAt > DAY_IN_MILLISECONDS } -async function getCurrentVersion() { +export async function getCurrentVersion() { if (shouldRefreshCache()) { const version = await fetchPackageVersion() saveVersionToCache(version)