CLI: Improve create-next-docs-app

This commit is contained in:
SonMooSans
2023-11-01 21:26:34 +08:00
parent 50b94d3a43
commit 334a9600fa
28 changed files with 274 additions and 117 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'create-next-docs-app': minor
---
Support installing packages automatically
+1 -1
View File
@@ -7,5 +7,5 @@
"access": "restricted",
"baseBranch": "dev",
"updateInternalDependencies": "patch",
"ignore": ["simple-app", "docs", "advanced-app", "experimental-mdx"]
"ignore": ["simple-app", "docs", "advanced-app", "simple-app-mdx"]
}
+5
View File
@@ -0,0 +1,5 @@
---
'create-next-docs-app': patch
---
Support relative paths as project name (e.g. ".")
+24
View File
@@ -0,0 +1,24 @@
---
title: Hello World
description: Your first document
---
Hey there!
## Heading
<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card
title="Learn more about Next Docs"
href="https://next-docs-zeta.vercel.app"
/>
</Cards>
### Heading
```js
console.log('Hello World')
```
#### Heading
@@ -1,5 +1,5 @@
{
"name": "experimental-mdx",
"name": "simple-app-mdx",
"version": "0.0.0",
"private": true,
"scripts": {
+24
View File
@@ -0,0 +1,24 @@
---
title: Hello World
description: Your first document
---
Hey there!
## Heading
<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card
title="Learn more about Next Docs"
href="https://next-docs-zeta.vercel.app"
/>
</Cards>
### Heading
```js
console.log('Hello World')
```
#### Heading
+1 -2
View File
@@ -12,5 +12,4 @@ yarn create next-docs-app
## Examples
The examples are automatically generated from `./examples`, support `simple` and
`advanced` types.
The examples are automatically generated from `./examples`
+6 -2
View File
@@ -8,14 +8,16 @@
"license": "MIT",
"files": [
"templates/*",
"content/*",
"dist/*"
],
"bin": "./dist/index.js",
"scripts": {
"build": "tsup --clean",
"clean": "rmdir /q/s dist",
"clean": "rmdir /q/s dist && rmdir /q/s templates",
"dev": "tsup --watch",
"prepublishOnly": "pnpm build && node ./scripts/sync.js",
"sync": "node ./scripts/sync.js",
"prepublishOnly": "pnpm build && pnpm sync",
"types:check": "tsc --noEmit"
},
"keywords": [
@@ -29,9 +31,11 @@
},
"dependencies": {
"@clack/prompts": "^0.7.0",
"cross-spawn": "^7.0.3",
"picocolors": "^1.0.0"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.4",
"@types/node": "18.17.5",
"fast-glob": "^3.3.1"
}
+9 -2
View File
@@ -4,7 +4,7 @@ const { globSync } = require('fast-glob')
const path = require('path')
const pc = require('picocolors')
const sources = ['simple', 'advanced', 'experimental-mdx']
const sources = ['simple', 'advanced', 'simple-mdx']
const cwd = process.cwd()
@@ -16,7 +16,14 @@ for (const source of sources) {
const sourceDir = path.resolve(cwd, `../../examples/${source}`)
const sourceFiles = globSync(
['**/*', '!node_modules', '!pnpm-lock.yaml', '!next-env.d.ts', '!_map.ts'],
[
'**/*',
'!content',
'!node_modules',
'!pnpm-lock.yaml',
'!next-env.d.ts',
'!_map.ts'
],
{ dot: false, cwd: sourceDir }
)
@@ -0,0 +1,46 @@
import { spawn } from 'cross-spawn'
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
export function getPackageManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent || ''
if (userAgent.startsWith('yarn')) {
return 'yarn'
}
if (userAgent.startsWith('pnpm')) {
return 'pnpm'
}
if (userAgent.startsWith('bun')) {
return 'bun'
}
return 'npm'
}
export function autoInstall(
manager: PackageManager,
dest: string
): Promise<void> {
return new Promise((res, reject) => {
const installProcess = spawn(manager, ['install'], {
stdio: 'inherit',
env: {
...process.env,
NODE_ENV: 'development',
DISABLE_OPENCOLLECTIVE: '1'
},
cwd: dest
})
installProcess.on('close', code => {
if (code !== 0) {
reject('Install failed')
} else {
res()
}
})
})
}
@@ -1,3 +1,4 @@
import { existsSync } from 'fs'
import fs from 'fs/promises'
import path from 'path'
import {
@@ -11,34 +12,37 @@ import {
text
} from '@clack/prompts'
import * as color from 'picocolors'
import { autoInstall, getPackageManager } from './auto-install'
async function main() {
const cwd = process.cwd()
const sourceDir = path.resolve(__dirname, '../')
intro(color.bgCyan(color.bold('Create Next Docs')))
const name = await text({
const inputName = await text({
message: 'Project name',
placeholder: 'my-app',
defaultValue: 'my-app'
})
if (isCancel(name)) return cancel()
if (isCancel(inputName)) return cancel()
const pathname = inputName.toLowerCase().replace(/\s/, '-')
const dest = path.resolve(cwd, pathname)
const name = path.basename(dest)
const type = await select({
message: 'Which example you want to install?',
options: [
{ value: 'simple', label: 'Default (Contentlayer)' },
{ value: 'advanced', label: 'Advanced (Contentlayer)' },
{ value: 'experimental-mdx', label: 'Default (Next Docs MDX)' }
{ value: 'simple-mdx', label: 'Default (Next Docs MDX)' }
]
})
if (isCancel(type)) return cancel()
const cwd = process.cwd()
const pathname = name.toLowerCase().replace(/\s/, '-')
const dest = path.resolve(cwd, pathname)
if (await exists(dest)) {
if (existsSync(dest)) {
const del = await confirm({
message: `${pathname} already exists, do you want to delete it?`
})
@@ -55,14 +59,17 @@ async function main() {
})
info.stop(`Deleted ${pathname}`)
} else {
return cancel('Installation Stopped')
}
}
const info = spinner()
info.start('Copying files to ' + pathname)
await copy(path.resolve(__dirname, `../templates/${type}`), dest)
info.start(`Copying files to ${pathname}`)
await copy(path.join(sourceDir, `templates/${type}`), dest)
await copy(
path.join(sourceDir, 'static/content'),
path.join(dest, 'content/docs')
)
info.message('Updating package.json')
await updatePackageJson(path.join(dest, 'package.json'), name)
@@ -71,10 +78,24 @@ async function main() {
await generateReadme(path.join(dest, 'README.md'), name)
info.message('Adding .gitignore')
await generateGitIgnore(path.join(dest, '.gitignore'))
await fs.copyFile(
path.join(sourceDir, 'static/example.gitignore'),
path.join(dest, '.gitignore')
)
info.stop('Project Generated')
const manager = getPackageManager()
const shouldInstall = await confirm({
message: `Do you want to install packages automatically? (detected as ${manager})`
})
if (isCancel(shouldInstall)) cancel()
if (shouldInstall) {
await autoInstall(manager, dest)
}
outro(color.bgGreen(color.bold('Done')))
if (type === 'advanced') {
@@ -85,15 +106,14 @@ async function main() {
console.log(color.bold('\nOpen the project'))
console.log(color.cyan(`cd ${pathname}`))
console.log(color.bold('\nInstall Packages'))
console.log(color.cyan('npm install | pnpm install | yarn install'))
console.log(color.bold('\nRun Development Server'))
console.log(color.cyan('npm run dev | pnpm run dev | yarn dev'))
console.log(
color.bold('\nYou can now open the project and start writing documents\n')
color.bold('\nYou can now open the project and start writing documents')
)
process.exit(0)
}
async function updatePackageJson(path: string, projectName: string) {
@@ -136,35 +156,6 @@ async function generateReadme(path: string, projectName: string) {
await fs.writeFile(path, content)
}
async function generateGitIgnore(path: string) {
const ignores = [
// deps
'/node_modules',
'/.pnp',
'.pnp.js',
// outputs
'_map.ts',
'.contentlayer',
'/coverage',
'/.next/',
'/out/',
'/build',
'.DS_Store',
'*.pem',
// debug logs
'npm-debug.log*',
'yarn-debug.log*',
'yarn-error.log*',
'.env*.local',
'.vercel',
'*.tsbuildinfo',
'next-env.d.ts'
]
await fs.writeFile(path, ignores.join('\n'))
}
async function copy(
from: string,
to: string,
@@ -186,11 +177,7 @@ async function copy(
}
}
async function exists(file: string): Promise<boolean> {
return fs
.access(file, fs.constants.R_OK)
.then(() => true)
.catch(() => false)
}
main()
main().catch(e => {
console.error(e)
throw e
})
@@ -0,0 +1,24 @@
---
title: Hello World
description: Your first document
---
Hey there!
## Heading
<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card
title="Learn more about Next Docs"
href="https://next-docs-zeta.vercel.app"
/>
</Cards>
### Heading
```js
console.log('Hello World')
```
#### Heading
@@ -0,0 +1,27 @@
# deps
/node_modules
# generated content
_map.ts
.contentlayer
# test & build
/coverage
/.next/
/out/
/build
*.tsbuildinfo
# misc
.DS_Store
*.pem
/.pnp
.pnp.js
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# others
.env*.local
.vercel
next-env.d.ts
+1 -1
View File
@@ -2,7 +2,7 @@ import { defineConfig } from 'tsup'
import tsconfig from './tsconfig.json'
export default defineConfig({
entry: ['./index.ts'],
entry: ['./src/index.ts'],
format: 'cjs',
target: tsconfig.compilerOptions.target as 'es2016'
})
+59 -54
View File
@@ -191,49 +191,6 @@ importers:
specifier: 5.2.2
version: 5.2.2
examples/experimental-mdx:
dependencies:
'@mdx-js/loader':
specifier: ^2.3.0
version: 2.3.0([email protected])
'@mdx-js/react':
specifier: ^2.3.0
version: 2.3.0([email protected])
'@next/mdx':
specifier: ^14.0.0
version: 14.0.0(@mdx-js/[email protected])(@mdx-js/[email protected])
next:
specifier: 14.0.1
version: 14.0.1(@babel/[email protected])([email protected])([email protected])
next-docs-mdx:
specifier: 4.0.3
version: link:../../packages/next-docs-mdx
next-docs-ui:
specifier: 4.0.3
version: link:../../packages/next-docs-ui
next-docs-zeta:
specifier: 4.0.3
version: link:../../packages/next-docs
react:
specifier: 18.2.0
version: 18.2.0
react-dom:
specifier: 18.2.0
version: 18.2.0([email protected])
devDependencies:
'@types/mdx':
specifier: ^2.0.8
version: 2.0.8
'@types/react':
specifier: 18.2.0
version: 18.2.0
'@types/react-dom':
specifier: 18.2.1
version: 18.2.1
typescript:
specifier: 5.2.2
version: 5.2.2
examples/simple:
dependencies:
contentlayer:
@@ -268,15 +225,64 @@ importers:
specifier: 5.2.2
version: 5.2.2
examples/simple-mdx:
dependencies:
'@mdx-js/loader':
specifier: ^2.3.0
version: 2.3.0([email protected])
'@mdx-js/react':
specifier: ^2.3.0
version: 2.3.0([email protected])
'@next/mdx':
specifier: ^14.0.0
version: 14.0.1(@mdx-js/[email protected])(@mdx-js/[email protected])
next:
specifier: 14.0.1
version: 14.0.1(@babel/[email protected])([email protected])([email protected])
next-docs-mdx:
specifier: 4.0.3
version: link:../../packages/next-docs-mdx
next-docs-ui:
specifier: 4.0.3
version: link:../../packages/next-docs-ui
next-docs-zeta:
specifier: 4.0.3
version: link:../../packages/next-docs
react:
specifier: 18.2.0
version: 18.2.0
react-dom:
specifier: 18.2.0
version: 18.2.0([email protected])
devDependencies:
'@types/mdx':
specifier: ^2.0.8
version: 2.0.8
'@types/react':
specifier: 18.2.0
version: 18.2.0
'@types/react-dom':
specifier: 18.2.1
version: 18.2.1
typescript:
specifier: 5.2.2
version: 5.2.2
packages/create-next-docs:
dependencies:
'@clack/prompts':
specifier: ^0.7.0
version: 0.7.0
cross-spawn:
specifier: ^7.0.3
version: 7.0.3
picocolors:
specifier: ^1.0.0
version: 1.0.0
devDependencies:
'@types/cross-spawn':
specifier: ^6.0.4
version: 6.0.4
'@types/node':
specifier: 18.17.5
version: 18.17.5
@@ -392,7 +398,7 @@ importers:
devDependencies:
'@next/mdx':
specifier: 14.0.1
version: 14.0.1
version: 14.0.1(@mdx-js/[email protected])(@mdx-js/[email protected])
'@types/mdast':
specifier: ^3.0.12
version: 3.0.12
@@ -1594,7 +1600,6 @@ packages:
webpack: 5.89.0
transitivePeerDependencies:
- supports-color
dev: false
/@mdx-js/[email protected]:
resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==}
@@ -1627,7 +1632,6 @@ packages:
'@types/mdx': 2.0.8
'@types/react': 18.2.0
react: 18.2.0
dev: false
/@next/[email protected]:
resolution: {integrity: sha512-a1Wbs0eXCMj30vxTfaGEO4DP3NjrqN+ge6rMhKZ43VtsXEjiVX95d7XFr7VkNps9lvlkofBF54RnIHhTJ2Ls0Q==}
@@ -1671,7 +1675,7 @@ packages:
source-map: 0.7.4
dev: false
/@next/[email protected]:
/@next/[email protected](@mdx-js/[email protected])(@mdx-js/[email protected]):
resolution: {integrity: sha512-9hikFxx1XQOReOp5SBO1o3fGs1Z1GT1flKm1wrjFOwOTNI0M4x9CuQsUx5KJwvbt0FejCS5bsuNGXqxHchcMCQ==}
peerDependencies:
'@mdx-js/loader': '>=0.15.0'
@@ -1682,8 +1686,9 @@ packages:
'@mdx-js/react':
optional: true
dependencies:
'@mdx-js/loader': 2.3.0([email protected])
'@mdx-js/react': 2.3.0([email protected])
source-map: 0.7.4
dev: true
/@next/[email protected]:
resolution: {integrity: sha512-JyxnGCS4qT67hdOKQ0CkgFTp+PXub5W1wsGvIq98TNbF3YEIN7iDekYhYsZzc8Ov0pWEsghQt+tANdidITCLaw==}
@@ -3023,6 +3028,12 @@ packages:
dependencies:
'@types/estree': 1.0.1
/@types/[email protected]:
resolution: {integrity: sha512-GGLpeThc2Bu8FBGmVn76ZU3lix17qZensEI4/MPty0aZpm2CHfgEMis31pf5X5EiudYKcPAsWciAsCALoPo5dw==}
dependencies:
'@types/node': 18.17.5
dev: true
/@types/[email protected]:
resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==}
dependencies:
@@ -3923,7 +3934,6 @@ packages:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
dev: true
/[email protected]:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
@@ -5355,7 +5365,6 @@ packages:
/[email protected]:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/[email protected]:
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
@@ -6711,7 +6720,6 @@ packages:
/[email protected]:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
dev: true
/[email protected]:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@@ -7523,7 +7531,6 @@ packages:
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
dev: true
/[email protected]:
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
@@ -7533,7 +7540,6 @@ packages:
/[email protected]:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
dev: true
/[email protected]:
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
@@ -8556,7 +8562,6 @@ packages:
hasBin: true
dependencies:
isexe: 2.0.0
dev: true
/[email protected]:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}