Skip to content

Commit

Permalink
feat: create create-resume package
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed May 10, 2022
1 parent 0113a94 commit 5237635
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 1 deletion.
214 changes: 214 additions & 0 deletions packages/create-resume/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#!/usr/bin/env node
/* eslint-disable no-console */

// Avoids autoconversion to number of the project name by defining that the args
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
const argv = require('minimist')(process.argv.slice(2), { string: ['_'] })
const prompts = require('prompts')
const { yellow, red, reset } = require('kolorist')
const fs = require('fs')
const path = require('path')

const cwd = process.cwd()

const TEMPLATES = [
{
name: 'react-ts',
display: 'Typescript',
color: yellow,
},
]

const renameFiles = {
_gitignore: '.gitignore',
}

async function init() {
let targetDir = argv._[0]
let template = argv.template || argv.t

const defaultProjectName = !targetDir ? 'resume' : targetDir

let result = {}

try {
result = await prompts(
[
{
type: targetDir ? null : 'text',
name: 'projectName',
message: reset('Project name:'),
initial: defaultProjectName,
onState: (state) =>
(targetDir = state.value.trim() || defaultProjectName),
},
{
type: () =>
!fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm',
name: 'overwrite',
message: () =>
`${
targetDir === '.'
? 'Current directory'
: `Target directory "${targetDir}"`
} is not empty. Remove existing files and continue?`,
},
{
type: (_, { overwrite } = {}) => {
if (overwrite === false)
throw new Error(`${red('✖')} Operation cancelled`)

return null
},
name: 'overwriteChecker',
},
{
type: () => (isValidPackageName(targetDir) ? null : 'text'),
name: 'packageName',
message: reset('Package name:'),
initial: () => toValidPackageName(targetDir),
validate: (dir) =>
isValidPackageName(dir) || 'Invalid package.json name',
},
{
type: 'select',
name: 'variant',
message: reset('Select a template:'),
choices: () => {
return TEMPLATES.map((template) => {
return {
title: template.color(template.name),
value: template.name,
}
})
},
},
],
{
onCancel: () => {
throw new Error(`${red('✖')} Operation cancelled`)
},
}
)
} catch (cancelled) {
console.log(cancelled.message)
return
}

// user choice associated with prompts
const { overwrite, packageName, variant } = result

const root = path.join(cwd, targetDir)

if (overwrite) emptyDir(root)
else if (!fs.existsSync(root)) fs.mkdirSync(root)

// determine template
template = variant || template

console.log(`\nScaffolding project in ${root}...`)

const templateDir = path.join(__dirname, `template-${template}`)

const write = (file, content) => {
const targetPath = renameFiles[file]
? path.join(root, renameFiles[file])
: path.join(root, file)
if (content) fs.writeFileSync(targetPath, content)
else copy(path.join(templateDir, file), targetPath)
}

const files = fs.readdirSync(templateDir)
for (const file of files.filter((f) => f !== 'package.json')) write(file)

const pkg = require(path.join(templateDir, 'package.json'))

pkg.name = packageName || targetDir

write('package.json', JSON.stringify(pkg, null, 2))

const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
const pkgManager = pkgInfo ? pkgInfo.name : 'npm'

console.log('\nDone. Now run:\n')
if (root !== cwd) console.log(` cd ${path.relative(cwd, root)}`)

switch (pkgManager) {
case 'yarn':
console.log(' yarn')
console.log(' yarn dev')
break
default:
console.log(` ${pkgManager} install`)
console.log(` ${pkgManager} run dev`)
break
}
console.log()
}

function copy(src, dest) {
const stat = fs.statSync(src)
if (stat.isDirectory()) copyDir(src, dest)
else fs.copyFileSync(src, dest)
}

function isValidPackageName(projectName) {
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
projectName
)
}

function toValidPackageName(projectName) {
return projectName
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/^[._]/, '')
.replace(/[^a-z0-9-~]+/g, '-')
}

function copyDir(srcDir, destDir) {
fs.mkdirSync(destDir, { recursive: true })
for (const file of fs.readdirSync(srcDir)) {
const srcFile = path.resolve(srcDir, file)
const destFile = path.resolve(destDir, file)
copy(srcFile, destFile)
}
}

function isEmpty(path) {
return fs.readdirSync(path).length === 0
}

function emptyDir(dir) {
if (!fs.existsSync(dir)) return

for (const file of fs.readdirSync(dir)) {
const abs = path.resolve(dir, file)
// baseline is Node 12 so can't use rmSync :(
if (fs.lstatSync(abs).isDirectory()) {
emptyDir(abs)
fs.rmdirSync(abs)
} else {
fs.unlinkSync(abs)
}
}
}

/**
* @param {string | undefined} userAgent process.env.npm_config_user_agent
* @returns object | undefined
*/
function pkgFromUserAgent(userAgent) {
if (!userAgent) return undefined
const pkgSpec = userAgent.split(' ')[0]
const pkgSpecArr = pkgSpec.split('/')
return {
name: pkgSpecArr[0],
version: pkgSpecArr[1],
}
}

init().catch((e) => {
console.error(e)
})
31 changes: 31 additions & 0 deletions packages/create-resume/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "create-resume",
"version": "0.0.0",
"packageManager": "pnpm@6.32.3",
"description": "create resume project",
"keywords": [
"create",
"vite",
"resume",
"markdown"
],
"homepage": "https://vite-ant-design-pro.vercel.app",
"bugs": {
"url": "https://github.com/Dunqing/resume/issues"
},
"license": "MIT",
"author": "dengqing <dengqing0821@gmail.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/1247748612/Dungqing/resume.git"
},
"sideEffects": false,
"bin": {
"create-resume": "index.js"
},
"dependencies": {
"kolorist": "^1.5.1",
"minimist": "^1.2.6",
"prompts": "^2.4.2"
}
}
9 changes: 9 additions & 0 deletions packages/create-resume/template-react-ts/RESUME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
theme: true
print: true
github: "https://github.com/Dunqing/resume"
---

# 张三

![头像](https://notion-avatar.vercel.app/api/svg/eyJmYWNlIjo5LCJub3NlIjoxMCwibW91dGgiOjEsImV5ZXMiOjcsImV5ZWJyb3dzIjoxMSwiZ2xhc3NlcyI6MCwiaGFpciI6MTEsImFjY2Vzc29yaWVzIjoxMywiZGV0YWlscyI6MCwiYmVhcmQiOjAsImZsaXAiOjAsImNvbG9yIjoicmdiYSgyNTUsIDAsIDAsIDApIiwic2hhcGUiOiJub25lIn0=)
23 changes: 23 additions & 0 deletions packages/create-resume/template-react-ts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="/src/favicon.svg"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>简历</title>
</head>
<body>
<div id="root"></div>
<script
type="module"
src="/src/main.tsx"
></script>
</body>
</html>
27 changes: 27 additions & 0 deletions packages/create-resume/template-react-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "resume",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@iconify-json/carbon": "^1.1.4",
"@iconify-json/mdi": "^1.1.2",
"@resumejs/components": "latest",
"@unocss/preset-icons": "^0.33.2",
"@unocss/preset-wind": "^0.33.2",
"@unocss/reset": "^0.33.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"unocss": "^0.33.2"
},
"devDependencies": {
"@types/react": "^17.0.33",
"@types/react-dom": "^17.0.10",
"@vitejs/plugin-react": "^1.0.7",
"vite": "^2.9.0"
}
}
12 changes: 12 additions & 0 deletions packages/create-resume/template-react-ts/src/Show.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Resume } from '@resumejs/components'
import md from '../../RESUME.md?raw'

const Show = () => {
return (
<div className="flex justify-center">
<Resume className="md:w-screen-md">{md}</Resume>
</div>
)
}

export default Show
10 changes: 10 additions & 0 deletions packages/create-resume/template-react-ts/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom'
import Show from './Show'

ReactDOM.render(
<React.StrictMode>
<Show />
</React.StrictMode>,
document.getElementById('root')
)
5 changes: 5 additions & 0 deletions packages/create-resume/template-react-ts/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="vite/client" />
declare module '*.md' {
const md: string
export default md
}
32 changes: 32 additions & 0 deletions packages/create-resume/template-react-ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src",
"../README.md",
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
8 changes: 8 additions & 0 deletions packages/create-resume/template-react-ts/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "esnext",
"moduleResolution": "node"
},
"include": ["vite.config.ts"]
}

0 comments on commit 5237635

Please sign in to comment.