Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ npx @doyuli/create-vue

npm create @doyuli/vue
```

### 创建 TypeScript Library 项目

```bash
npx @doyuli/create-lib

# or

npm create @doyuli/lib
```
9 changes: 9 additions & 0 deletions packages/create-lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Usage

```bash
npx @doyuli/create-lib

# or

npm create @doyuli/lib
```
2 changes: 2 additions & 0 deletions packages/create-lib/bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
import '../dist/index.mjs'
43 changes: 43 additions & 0 deletions packages/create-lib/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@doyuli/create-lib",
"type": "module",
"version": "0.2.1",
"description": "@doyuli/create-lib",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/doyuli/template-kits.git",
"directory": "packages/create-lib"
},
"keywords": [
"templates",
"create-lib"
],
"sideEffects": false,
"main": "dist/index.js",
"bin": {
"create-lib": "bin/index.js"
},
"files": [
"bin",
"dist",
"template",
"template/**/settings.json"
],
"publishConfig": {
"access": "public"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown",
"prepack": "pnpm run build"
},
"dependencies": {
"@clack/prompts": "catalog:",
"@doyuli/kits-core": "workspace:^",
"picocolors": "catalog:"
}
}
16 changes: 16 additions & 0 deletions packages/create-lib/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const DEFAULT_BANNER = 'Template-Kits - 快速生成你的模板代码'

export const FEATURE_OPTIONS = [
{
value: 'monorepo',
label: 'Monorepo(pnpm workspace 多包管理)',
},
{
value: 'simple-git-hooks',
label: 'Simple Git Hooks(Git Commit 检查)',
},
{
value: 'github-workflows',
label: 'GitHub Workflows(CI/CD 自动化)',
},
] as const
41 changes: 41 additions & 0 deletions packages/create-lib/src/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as fs from 'node:fs'
import * as path from 'node:path'
import { renderFile } from '@doyuli/kits-core'

/**
* Convert devDependencies to catalog: references and generate pnpm-workspace.yaml
*/
export function renderMonorepoDeps(root: string) {
const pkgPath = path.join(root, 'package.json')
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
const devDeps: Record<string, string> = pkg.devDependencies ?? {}

// Extract real versions for catalog, then replace with catalog: references
const sortedEntries = Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))
const catalogDeps = Object.fromEntries(sortedEntries.map(([k]) => [k, 'catalog:']))

pkg.devDependencies = catalogDeps
renderFile(root, 'package.json', `${JSON.stringify(pkg, null, 2)}\n`)

// Generate pnpm-workspace.yaml
const catalogLines = sortedEntries
.map(([name, version]) => {
const key = name.includes('/') ? `'${name}'` : name
return ` ${key}: ${version}`
})
.join('\n')

const workspace = [
'shellEmulator: true',
'trustPolicy: no-downgrade',
'',
'packages:',
' - packages/*',
'',
'catalog:',
catalogLines,
'',
].join('\n')

renderFile(root, 'pnpm-workspace.yaml', workspace)
}
93 changes: 93 additions & 0 deletions packages/create-lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type {
PromptResult,
} from '@doyuli/kits-core'
import * as path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { parseArgs } from 'node:util'
import { intro, outro } from '@clack/prompts'
import {
getCommand,
getPackageManager,
renderTemplate,
setupFeatures,
setupProject,
setupPrompts,
} from '@doyuli/kits-core'
import pico from 'picocolors'
import {
DEFAULT_BANNER,
FEATURE_OPTIONS,
} from './constants'
import { renderMonorepoDeps } from './helper';

(async function () {
const cwd = process.cwd()
const { positionals } = parseArgs({
strict: true,
allowPositionals: true,
})

intro(pico.magenta(DEFAULT_BANNER))

const inputTargetDir = positionals[0]

const { result, targetDir } = await setupPrompts(inputTargetDir, [
setupFeatures('features', {
options: [...FEATURE_OPTIONS],
}),
])

const root = await setupProject(cwd, result, targetDir)

renderTemplates(root, result)

outro(getOutroMessage(root, cwd))
})()

function getOutroMessage(root: string, cwd: string) {
const manager = getPackageManager()

let message = `项目初始化完成,可执行以下命令:\n\n`
if (root !== cwd) {
const cdProjectName = path.relative(cwd, root)
message += ` ${pico.bold(pico.green(`cd ${cdProjectName.includes(' ') ? `"${cdProjectName}"` : cdProjectName}`))}\n`
}
message += ` ${pico.bold(pico.green(getCommand(manager, 'install')))}\n`
message += ` ${pico.bold(pico.green(getCommand(manager, 'dev')))}\n`

return message
}

function renderTemplates(root: string, result: PromptResult & { features: string[] }) {
const { features } = result

const needsMonorepo = features.includes('monorepo')
const needsGitHooks = features.includes('simple-git-hooks')
const needsWorkflows = features.includes('github-workflows')

const templateRoot = fileURLToPath(new URL('../template', import.meta.url))
const render = (templateName: string) => {
const templateDir = path.resolve(templateRoot, templateName)
renderTemplate(templateDir, root)
}

render('base')
render('eslint')

if (needsGitHooks) {
render('git-hooks')
}

if (needsWorkflows) {
render('github-workflows')
}

if (needsMonorepo) {
render('monorepo')
renderMonorepoDeps(root)
}
else {
render('single-repo')
}
}
3 changes: 3 additions & 0 deletions packages/create-lib/template/base/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["vitest.explorer", "dbaeumer.vscode-eslint"]
}
50 changes: 50 additions & 0 deletions packages/create-lib/template/base/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,

// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},

// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],

// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}
6 changes: 6 additions & 0 deletions packages/create-lib/template/base/_editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
root = true

[*]
indent_size = 2
end_of_line = lf
insert_final_newline = true
1 change: 1 addition & 0 deletions packages/create-lib/template/base/_gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
5 changes: 5 additions & 0 deletions packages/create-lib/template/base/_gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
*.log
.eslintcache
27 changes: 27 additions & 0 deletions packages/create-lib/template/base/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"type": "module",
"private": true,
"license": "MIT",
"packageManager": "pnpm@10.25.0",
"engines": {
"node": ">=20.18.0"
},
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown",
"test": "vitest",
"release": "bumpp -r",
"lint": "eslint --cache",
"lint:fix": "eslint --cache --fix",
"typecheck": "tsc --noEmit",
"prepublishOnly": "pnpm run build"
},
"devDependencies": {
"@types/node": "^25.3.2",
"bumpp": "^10.4.1",
"tsdown": "^0.20.3",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
}
}
8 changes: 8 additions & 0 deletions packages/create-lib/template/eslint/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @ts-check
import antfu from '@antfu/eslint-config'

export default antfu(
{
formatters: true,
},
)
11 changes: 11 additions & 0 deletions packages/create-lib/template/eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"scripts": {
"lint": "eslint --cache",
"lint:fix": "eslint --cache --fix"
},
"devDependencies": {
"@antfu/eslint-config": "catalog:",
"eslint": "catalog:",
"eslint-plugin-format": "catalog:"
}
}
17 changes: 17 additions & 0 deletions packages/create-lib/template/git-hooks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"scripts": {
"postinstall": "simple-git-hooks"
},
"devDependencies": {
"picocolors": "^1.1.1",
"lint-staged": "^16.1.6",
"simple-git-hooks": "^2.13.1"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged",
"commit-msg": "node scripts/verify-commit.js"
},
"lint-staged": {
"*": "eslint --fix"
}
}
Loading