mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
ci: 添加生成 package 的脚本工具
This commit is contained in:
parent
f4aca014ed
commit
1d5196eca2
@ -5,7 +5,7 @@
|
||||
"license": "MIT",
|
||||
"author": "pengzhanbo",
|
||||
"scripts": {
|
||||
"autoUpdate": "node scripts/autoInstall.js",
|
||||
"autoUpdate": "ts-node scripts/autoInstall.ts",
|
||||
"build": "pnpm run build:package",
|
||||
"build:package": "pnpm --filter=!vuepress-theme-plume run -r --stream build",
|
||||
"commit": "cz",
|
||||
@ -16,6 +16,7 @@
|
||||
"docs:clean": "pnpm --filter=docs docs:clean",
|
||||
"docs:serve": "pnpm --filter=docs docs:serve",
|
||||
"lint": "eslint --ext .js,.ts,.vue .",
|
||||
"pkg": "ts-node scripts/create/index.ts",
|
||||
"prepare": "husky install",
|
||||
"release": "pnpm release:check && pnpm release:version && pnpm release:publish",
|
||||
"release:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
@ -37,6 +38,7 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
"@commitlint/config-conventional": "^16.2.1",
|
||||
"@types/minimist": "^1.2.2",
|
||||
"@types/node": "^17.0.30",
|
||||
"@types/webpack-env": "^1.16.4",
|
||||
"bumpp": "^7.1.1",
|
||||
@ -51,13 +53,16 @@
|
||||
"eslint-config-vuepress": "^3.7.1",
|
||||
"eslint-config-vuepress-typescript": "^2.7.1",
|
||||
"execa": "^5.1.1",
|
||||
"handlebars": "^4.7.7",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.4.0",
|
||||
"minimist": "^1.2.6",
|
||||
"ora": "^5.4.1",
|
||||
"prettier": "^2.6.2",
|
||||
"prettier-config-vuepress": "^1.4.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sort-package-json": "^1.55.0",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.6.3",
|
||||
"vite": "^2.9.7"
|
||||
},
|
||||
|
||||
5425
pnpm-lock.yaml
generated
5425
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,16 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const ora = require('ora')
|
||||
const chalk = require('chalk')
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import execa from 'execa'
|
||||
import ora from 'ora'
|
||||
import chalk from 'chalk'
|
||||
|
||||
const packages = [
|
||||
const packages: string[] = [
|
||||
...fs.readdirSync(path.join(__dirname, '../packages')).map(dir => path.join('../packages', dir)),
|
||||
'../docs'
|
||||
]
|
||||
|
||||
const dependencies = packages.map(dir => {
|
||||
const pkg = fs.readFileSync(path.join(__dirname, dir, 'package.json'))
|
||||
const dependencies = packages.map((dir: string) => {
|
||||
const pkg = fs.readFileSync(path.join(__dirname, dir, 'package.json'), 'utf-8')
|
||||
const { dependencies, devDependencies } = JSON.parse(pkg)
|
||||
return {
|
||||
dirname: path.join(__dirname, dir),
|
||||
@ -19,7 +19,7 @@ const dependencies = packages.map(dir => {
|
||||
}
|
||||
})
|
||||
|
||||
function filterVuePress(dependencies) {
|
||||
function filterVuePress(dependencies: string[]) {
|
||||
const vuepress = dependencies.filter(
|
||||
dependence => dependence.startsWith('@vuepress/') || dependence.startsWith('vuepress-')
|
||||
).map(dependence => dependence + '@next')
|
||||
@ -30,7 +30,7 @@ function filterVuePress(dependencies) {
|
||||
return [...vue, ...vuepress]
|
||||
}
|
||||
|
||||
const options = []
|
||||
const options: [string, string[], { cwd: string }][] = []
|
||||
dependencies.forEach(({ dirname, dependencies, devDependencies }) => {
|
||||
if (dependencies.length) {
|
||||
options.push(['pnpm', ['add', ...dependencies], { cwd: dirname }])
|
||||
@ -49,7 +49,7 @@ async function install(index = 0) {
|
||||
console.log(chalk.gray(opt[0], opt[1].join(' ')));
|
||||
console.log('\n');
|
||||
const current = execa(opt[0], opt[1], opt[2])
|
||||
current.stdout.pipe(process.stdout)
|
||||
current?.stdout?.pipe(process.stdout)
|
||||
try {
|
||||
await current;
|
||||
spinner.succeed('Installed.')
|
||||
71
scripts/create/generator.ts
Normal file
71
scripts/create/generator.ts
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
import { readTemplateList } from './readTpl'
|
||||
import path from 'path'
|
||||
import { upperCase, lowerCase, packageName } from './utils'
|
||||
import { compile } from 'handlebars'
|
||||
import type { ConfigOptions } from './getConfig'
|
||||
import { writeFile } from './writeFile'
|
||||
import chalk from 'chalk'
|
||||
import ora from 'ora'
|
||||
import execa from 'execa'
|
||||
|
||||
const packagesRoot = path.join(__dirname, '../../packages')
|
||||
const spinner = ora()
|
||||
|
||||
const generatorFile = async (config: ConfigOptions): Promise<void> => {
|
||||
const templateList = readTemplateList(path.join(__dirname, './template'))
|
||||
|
||||
const { name, client, shared } = config
|
||||
const pkgName = packageName(name)
|
||||
const targetDir = path.join(packagesRoot, pkgName)
|
||||
const data = {
|
||||
pkgName,
|
||||
upperName: upperCase(name),
|
||||
lowerName: lowerCase(name),
|
||||
client,
|
||||
shared,
|
||||
}
|
||||
const include = [!client && 'client', !shared && 'shared'].filter(Boolean).join('|')
|
||||
const filterRE = new RegExp(`/(${include})/`)
|
||||
const templates = templateList.filter(({ file }) => {
|
||||
return !filterRE.test(file)
|
||||
}).map(({ file, content }) => {
|
||||
return {
|
||||
file,
|
||||
template: compile(content)
|
||||
}
|
||||
})
|
||||
spinner.start(`${chalk.cyan(pkgName)} generating....`)
|
||||
templates.forEach(async ({ file, template }) => {
|
||||
try {
|
||||
const filepath = path.join(targetDir, file)
|
||||
await writeFile(filepath, template(data))
|
||||
} catch (e) {
|
||||
spinner.fail(`Failed to generate ${chalk.cyan(pkgName)}`)
|
||||
throw e
|
||||
}
|
||||
})
|
||||
spinner.succeed(`${chalk.cyan(pkgName)} generated !`)
|
||||
}
|
||||
|
||||
const initPackage = async (config: ConfigOptions): Promise<void> => {
|
||||
const { name, client } = config
|
||||
const pkgName = packageName(name)
|
||||
const targetDir = path.join(packagesRoot, pkgName)
|
||||
const dependencies: string[] = ['@vuepress/core@next']
|
||||
client && dependencies.push('@vuepress/client@next')
|
||||
|
||||
spinner.start(chalk.cyan('Installing...'))
|
||||
try {
|
||||
await execa('pnpm', ['add', ...dependencies], { cwd: targetDir })
|
||||
spinner.succeed('Installed.')
|
||||
} catch (e) {
|
||||
spinner.fail('Failed to Installed')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export const generator = async (config: ConfigOptions): Promise<void> => {
|
||||
await generatorFile(config)
|
||||
await initPackage(config)
|
||||
}
|
||||
42
scripts/create/getConfig.ts
Normal file
42
scripts/create/getConfig.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import minimist from 'minimist'
|
||||
|
||||
export type ConfigOptions = {
|
||||
client: boolean
|
||||
shared: boolean
|
||||
help: boolean
|
||||
name: string
|
||||
}
|
||||
|
||||
export type ArgvOptions = {
|
||||
_ : string[]
|
||||
c: boolean
|
||||
h: boolean
|
||||
s: boolean
|
||||
} & ConfigOptions
|
||||
|
||||
const defaultOptions: Partial<ArgvOptions> = {
|
||||
s: false,
|
||||
shared: false,
|
||||
c: false,
|
||||
client: false,
|
||||
h: false,
|
||||
help: false
|
||||
}
|
||||
|
||||
const normalizeArgv = (argv: ArgvOptions): ConfigOptions => {
|
||||
return {
|
||||
name: argv._[0] || '',
|
||||
client: argv.client || argv.c,
|
||||
shared: argv.shared || argv.s,
|
||||
help: argv.h || argv.help
|
||||
}
|
||||
}
|
||||
|
||||
export const getConfig = (): ConfigOptions => {
|
||||
const argv: ArgvOptions = Object.assign(
|
||||
{},
|
||||
defaultOptions,
|
||||
minimist(process.argv.slice(2)) as ArgvOptions
|
||||
)
|
||||
return normalizeArgv(argv)
|
||||
}
|
||||
19
scripts/create/getHelp.ts
Normal file
19
scripts/create/getHelp.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import chalk from 'chalk'
|
||||
|
||||
export const getHelp = (): void => {
|
||||
console.log(`
|
||||
this command will generator a package to ${chalk.cyan('packages')}.
|
||||
|
||||
command: ${chalk.green('pnpm pkg <package-name> [-p]')}
|
||||
|
||||
options:
|
||||
${chalk.green('package-name')}: 包名
|
||||
${chalk.green('--client, -c')}: 是否生成 ${chalk.cyan('client/')} 目录
|
||||
${chalk.green('--shared, -s')}: 是否生成 ${chalk.cyan('shared/')} 目录
|
||||
|
||||
${chalk.green('--help, -h')}: show help message.
|
||||
|
||||
exp: ${chalk.green('pnpm pkg caniuse -p')}
|
||||
It will generator to ${chalk.cyan('packages/plugin-caniuse')}
|
||||
`)
|
||||
}
|
||||
15
scripts/create/index.ts
Normal file
15
scripts/create/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { getConfig } from './getConfig'
|
||||
import { getHelp } from './getHelp'
|
||||
import { generator } from './generator'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
if (config.help) {
|
||||
getHelp()
|
||||
process.exit(0)
|
||||
} else {
|
||||
generator(config).catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
40
scripts/create/readTpl.ts
Normal file
40
scripts/create/readTpl.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const tplRE = /\.tpl$/
|
||||
const readFileList = (dir: string, fileList = {}): Record<string, string> => {
|
||||
const files = fs.readdirSync(dir)
|
||||
files.forEach((file: string) => {
|
||||
const filepath = path.join(dir, file)
|
||||
const stat = fs.statSync(filepath)
|
||||
if (stat.isDirectory()) {
|
||||
readFileList(filepath, fileList)
|
||||
} else {
|
||||
const extname = path.extname(filepath)
|
||||
if (tplRE.test(extname)) {
|
||||
fileList[filepath.replace(tplRE, '')] = fs.readFileSync(filepath, 'utf-8')
|
||||
}
|
||||
}
|
||||
})
|
||||
return fileList
|
||||
}
|
||||
|
||||
interface TemplateItem {
|
||||
file: string
|
||||
content: string
|
||||
}
|
||||
|
||||
export type TemplateList = TemplateItem[]
|
||||
|
||||
export const readTemplateList = (dir: string): TemplateList => {
|
||||
const templateMap= readFileList(dir)
|
||||
const result: TemplateList = []
|
||||
Object.keys(templateMap).forEach((key: string) => {
|
||||
const file = path.relative(dir, key)
|
||||
result.push({
|
||||
file,
|
||||
content: templateMap[key]
|
||||
})
|
||||
})
|
||||
return result
|
||||
}
|
||||
21
scripts/create/template/LICENSE.tpl
Normal file
21
scripts/create/template/LICENSE.tpl
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2021 - PRESENT by pengzhanbo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
17
scripts/create/template/README.md.tpl
Normal file
17
scripts/create/template/README.md.tpl
Normal file
@ -0,0 +1,17 @@
|
||||
# `@vuepress-plume/vuepress-{{ pkgName }}`
|
||||
|
||||
## Install
|
||||
```
|
||||
yarn add @vuepress-plume/vuepress-{{ pkgName }}
|
||||
```
|
||||
## Usage
|
||||
``` js
|
||||
// .vuepress/config.js
|
||||
module.exports = {
|
||||
//...
|
||||
plugins: [
|
||||
{{ lowerName }}Plugin()
|
||||
]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
29
scripts/create/template/package.json.tpl
Normal file
29
scripts/create/template/package.json.tpl
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@vuepress-plume/vuepress-{{ pkgName }}",
|
||||
"version": "{{ version }}",
|
||||
"description": "The Plugin for VuePres 2",
|
||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"main": "lib/node/index.js",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm run clean && pnpm run ts",
|
||||
"clean": "rimraf lib *.tsbuildinfo",
|
||||
"dev": "pnpm run ts:watch",
|
||||
"ts": "tsc -b tsconfig.build.json",
|
||||
"ts:watch": "tsc -b tsconfig.build.json --watch"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
import { defineClientAppEnhance } from '@vuepress/client'
|
||||
|
||||
export default defineClientAppEnhance(({ router }) => {
|
||||
|
||||
})
|
||||
6
scripts/create/template/src/node/index.ts.tpl
Normal file
6
scripts/create/template/src/node/index.ts.tpl
Normal file
@ -0,0 +1,6 @@
|
||||
import { {{ lowerName }}Plugin } from './plugin'
|
||||
|
||||
export * from './plugin'
|
||||
export * from '../shared'
|
||||
|
||||
export default {{ lowerName }}Plugin
|
||||
26
scripts/create/template/src/node/plugin.ts.tpl
Normal file
26
scripts/create/template/src/node/plugin.ts.tpl
Normal file
@ -0,0 +1,26 @@
|
||||
import type { Plugin, App } from '@vuepress/core'
|
||||
{{#if client}}
|
||||
import path from 'path'
|
||||
{{/if}}
|
||||
{{#if shared}}
|
||||
import type { {{ upperName }}Options } from '../shared'
|
||||
{{else}}
|
||||
|
||||
export interface {{ upperName }}Options {
|
||||
a?: string
|
||||
}
|
||||
{{/if}}
|
||||
|
||||
export const {{ lowerName }}Plugin = (options: {{ upperName }}Options): Plugin => {
|
||||
return (app: App) => {
|
||||
return {
|
||||
name: '@vuepress-plume/vuepress-{{ pkgName }}',
|
||||
{{#if client}}
|
||||
clientAppEnhanceFiles: path.resolve(
|
||||
__dirname,
|
||||
'../client/clientAppEnhance.js'
|
||||
),
|
||||
{{/if}}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
scripts/create/template/src/shared/index.ts.tpl
Normal file
3
scripts/create/template/src/shared/index.ts.tpl
Normal file
@ -0,0 +1,3 @@
|
||||
export interface {{ upperName }}Options {
|
||||
a?: string
|
||||
}
|
||||
12
scripts/create/template/tsconfig.build.json.tpl
Normal file
12
scripts/create/template/tsconfig.build.json.tpl
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.esm.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.cjs.json"
|
||||
}
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
12
scripts/create/template/tsconfig.cjs.json.tpl
Normal file
12
scripts/create/template/tsconfig.cjs.json.tpl
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
},
|
||||
"include": [
|
||||
"./src/node",
|
||||
"./src/shared"
|
||||
]
|
||||
}
|
||||
15
scripts/create/template/tsconfig.esm.json.tpl
Normal file
15
scripts/create/template/tsconfig.esm.json.tpl
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "ES2020",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"types": [
|
||||
"@vuepress/client/types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./src/client",
|
||||
"./src/shared"
|
||||
]
|
||||
}
|
||||
14
scripts/create/utils.ts
Normal file
14
scripts/create/utils.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const upperCase = (str: string): string => {
|
||||
return str.split(/-|\s+/).filter(Boolean).map((s: string) => {
|
||||
return s[0].toUpperCase() + s.slice(1)
|
||||
}).join('')
|
||||
}
|
||||
|
||||
export const lowerCase = (str: string): string => {
|
||||
str = upperCase(str)
|
||||
return str[0].toLowerCase() + str.slice(1)
|
||||
}
|
||||
|
||||
export const packageName = (name: string): string => {
|
||||
return 'plugin-' + name.trim().split(/-|\s+/).filter(Boolean).join('-')
|
||||
}
|
||||
18
scripts/create/writeFile.ts
Normal file
18
scripts/create/writeFile.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
export const writeFile = async (filepath: string, content: string): Promise<void> => {
|
||||
const dirname = path.dirname(filepath)
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true })
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(filepath, content, 'utf-8', (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -17,6 +17,18 @@
|
||||
"vueCompilerOptions": {
|
||||
"experimentalDisableTemplateSupport": true
|
||||
},
|
||||
"include": ["packages/**/*", "docs/.vuepress/**/*"],
|
||||
"exclude": ["node_modules", ".temp", "lib", "dist"]
|
||||
"include": ["packages/**/*", "docs/.vuepress/**/*", "scripts/**/*"],
|
||||
"exclude": ["node_modules", ".temp", "lib", "dist"],
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"target": "ES2021",
|
||||
"lib": ["ES2021"],
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user