fix react plugin

This commit is contained in:
wxzhang 2023-01-11 21:48:13 +08:00
parent 5904297e35
commit d9af3b9321
11 changed files with 123 additions and 650 deletions

View File

@ -18,7 +18,15 @@
``` ```
## 第二步:导入`t`翻译函数 ## 第二步:导入`t`翻译函数
无论采用何种工具创建`React`应用,均可以直接从`languages`直接导入`t`函数。
`t`翻译函数用来进行文件翻译,普通的`React`应用`t`翻译函数可以用在两个地方:
- 普通的`js``ts`文件
- `React`组件`jsx、tsx`文件
### 在`js``ts`文件中使用
只需要从`languages`直接导入`t`函数即可。
```javascript | pure ```javascript | pure
import { t } from "./languages" import { t } from "./languages"
@ -33,42 +41,116 @@ import { t } from "../../../languages"
导入`t`函数后就可以直接使用了。 导入`t`函数后就可以直接使用了。
## 第三步:自动导入`t`翻译函数 ### 在`React`组件中翻译
当源码文件非常多时,手动导入`t`函数比较麻烦,我们提供了`vite``babel`两个插件可以实现自动导入`t`函数。 `React`组件中使用`t`函数翻译与在`js``ts`文件中使用的最大区别在于:**当切换语言时,需要触发组件的重新渲染**。
如果应用是采用`Vite`+`@vitejs/plugin-react`创建的工程,则可以通过配置`@voerkai18n/vite`插件实现自动导入`t`函数。
- **配置根组件Provider**
使用`VoerkaI18nProvider`包装应用根组件,本质上是创建了一个`VoerkaI18nContext.Provider`
```jsx | pure
// 1.当前语言Scope
import { i18nScope } from "./languages"
import { VoerkaI18nProvider } from "@voerkai18n/react"
export default App(){
return (
<VoerkaI18nProvider scope={i18nScope}>
<MyComponent/>
<VoerkaI18nProvider/>
)
}
```
- **组件中使用翻译函数**
通过`useVoerkaI18n`获取当前作用域的`t`翻译函数。
```jsx | pure
import { useVoerkaI18n } from "@voerkai18n/react"
export function MyComponent(){
const { t } = useVoerkaI18n()
return (
<div>{t("要翻译的内容")}</div>
)
}
```
注意:在组件中直接使用`import { t } from "languages`也是可以工作的,因为本质上`t`函数仅仅是一个普通的函数。但是当动态切换语言时,对应的组件不能自动重新渲染。因此,需要使用`{ t } = useVoerkaI18n()`导入`t`函数,才可以在切换语言时自动重新渲染组件。
### 第三步: 自动导入`t`翻译函数
如果应用是采用`Vite`+`@vitejs/plugin-react`创建的工程,则可以通过配置`@voerkai18n/vite`插件实现自动导入`t`函数和`翻译内容自动映射`等。
`vite.config.js`中配置导入安装`@voerkai18n/vite`插件。
```typescript | pure
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import Inspect from 'vite-plugin-inspect'
import Voerkai18nPlugin from "@voerkai18n/vite"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
Inspect(), // localhost:3000/__inspect/
Voerkai18nPlugin({ debug: true }),
react()
]
})
```
详见`@voerkai18n/vite`插件介绍。 详见`@voerkai18n/vite`插件介绍。
`@voerkai18n/vite`插件主要干两件事情:
- 对`js/ts`文件自动导入`t`函数,`jsx/tsx`文件不需要自动导入,只能使用`useVoerkaI18n`
- 根据`idMap.(js|ts)`内容自动替换翻译内容,用来消除冗余内容。
## 第四步:切换语言 ## 第四步:切换语言
最后,一般需要在应用中提供切换语言并自动重新渲染界面的功能。针对`React`应用,提供了`useVoerkaI18n`来实现此功能。 接下来在一般我们还需要实现语言切换的功能界面,`useVoerkaI18n`提供了:
- `language`: 当前激活语言名称
- `defaultLanguage`: 默认语言名称
- `changeLanguage(language)`: 用来切换当前语言
- `languages`: 读取当应用支持的语言列表。
```jsx | pure ```jsx | pure
// 如果没有在vite.config.js中配置`@voerkai18n/vite`插件则需要手工导入t函数
import { t } from "./languages"
import { useVoerkaI18n } from "@voerkai18n/react" import { useVoerkaI18n } from "@voerkai18n/react"
export default App(){
const { activeLanguage,changeLanguage,languages } = useVoerkaI18n() export function MyComponent(){
return (<div> const { t, language,changeLanguage,languages,defaultLanguage } = useVoerkaI18n()
<h1>{t("当前语言")}:{activeLanguage}</h1> return (
<div> { <div>
languages.map(lang=>{ <h1>{t("当前语言")}:{language}</h1>
return (<button <h1>{t("默认语言")}:{defaultLanguage}</h1>
key={lang.name} <div> {
onclick={()=>changeLanguage(lang.name)}> languages.map(lang=>{
{lang.title} return (<button
</button>) key={lang.name}
})} onclick={()=>changeLanguage(lang.name)}>
</div> {lang.title}
</div> ) </button>)
})}
</div>
</div>
)
}
``` ```
## 小结 ## 小结
- `useVoerkaI18n`返回当前激活语言、切换语言函数、支持的语言列表。 - 使用`<VoerkaI18nProvider scope={i18nScope}>`封装根组件
- 如果需要在切换语言时进行全局重新渲染,一般需要在顶层`App组件`中使用此`hook`, 这样可以确保在切换语言时整个应用进行重新渲染。 - `const { t } = useVoerkaI18n()`来导入翻译函数
- 一般切换语言的功能界面不会直接在`App组件`中使用,您可以使用一个专门的组件来切换语言。 - 使用`const { language,changeLanguage } = useVoerkaI18n()`来访问切换语言的函数
- 在普通`ts/js`文件中使用`import { t } from "./languages"`来导入`t`翻译函数
- `@voerkai18n/vite`插件是可选的,仅仅普通`ts/js`文件使用`t`翻译函数时用来自动导入。

View File

@ -1,2 +1,2 @@
console.log(t("这是一个测试"))

View File

@ -7,7 +7,7 @@ import Voerkai18nPlugin from "@voerkai18n/vite"
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
Inspect(), // localhost:3000/__inspect/ Inspect(), // localhost:3000/__inspect/
// Voerkai18nPlugin({ debug: true }), Voerkai18nPlugin({ debug: true }),
react() react()
] ]
}) })

View File

@ -1,458 +0,0 @@
/**
* 用于多包环境下的自动发布
*
* autopublish
*
* 1.在package.json中添加scripts
* {
* scripts:{
* "publish":"autopublish [options]",
* }
* }
* 2. 参数
* -q: 默认情况下会比对最后一次发布的时间来决定是否自动发布
* -q参数被指定时会询问用户
*
*
*
*
*/
const fs = require("fs-extra");
const inquirer = require("inquirer");
const semver = require("semver")
const path = require("path");
const shelljs = require("shelljs");
const createLogger = require("logsets");
const TaskListPlugin = require("logsets/plugins/tasklist")
const TablePlugin = require("logsets/plugins/table")
const { Command ,Option} = require('commander');
const dayjs = require("dayjs");
const relativeTime = require("dayjs/plugin/relativeTime");
const { rejects } = require("assert");
const { Console } = require("console");
const { resourceLimits } = require("worker_threads");
dayjs.extend(relativeTime);
require('dayjs/locale/zh-CN')
dayjs.locale("zh-CN");
const logger = createLogger();
logger.use(TaskListPlugin)
logger.use(TablePlugin)
const program =new Command()
// 排除要发布的包
const exclude_packages = ["autopublish"]
function getPackages(){
let workspaceRoot = process.cwd()
if(!fs.existsSync(path.join(workspaceRoot,"pnpm-workspace.yaml"))){
console.log("命令只能在工作区根目录下执行")
return
}
// 读取所有包
let packages = fs.readdirSync(path.join(workspaceRoot,"packages")).map(packageName=>{
const packageFolder = path.join(workspaceRoot,"packages",packageName)
const pkgFile = path.join(workspaceRoot,"packages",packageName,"package.json")
if(fs.existsSync(pkgFile)){
const { name, version,lastPublish,dependencies,devDependencies,description }= fs.readJSONSync(pkgFile)
// 读取工作区包依赖
let packageDependencies =[]
Object.entries({...dependencies,...devDependencies}).forEach(([name,version])=>{
if(version.startsWith("workspace:") && !exclude_packages.includes(name.replace("@voerkai18n/",""))){
packageDependencies.push(name)
}
})
return {
name, // 完整包名
description,
value:packageName, // 文件夹名称
version,
lastPublish,
isDirty: packageIsDirty(packageFolder), // 包自上次发布之后是否已修改
dependencies:packageDependencies // 依赖的工作区包
}
}
}).filter(pkgInfo=>pkgInfo && !exclude_packages.includes(pkgInfo.value))
// 根据依赖关系进行排序
for(let i=0;i<packages.length;i++){
for(let j=i;j<packages.length;j++){
let pkgInfo2 = packages[j]
if( packages[i].dependencies.includes(pkgInfo2.name)){
let p = packages[i]
packages[i] = packages[j]
packages[j] = p
}
}
}
// 如果某个包isDirty=true则依赖于其的其他包isDirty=true
packages.forEach(package => {
if(package.isDirty){
packages.forEach(p=>{
if(p.name!==package.name && p.dependencies.includes(package.name)){
p.isDirty = true
}
})
}
})
return packages
}
function assertInWorkspaceRoot(){
const workspaceRoot = process.cwd()
if(!fs.existsSync(path.join(workspaceRoot,"pnpm-workspace.yaml"))){
throw new Error("命令只能在工作区根目录下执行")
}
}
function assertInPackageRoot(){
const currentFolder = process.cwd()
const workspaceRoot = path.join(currentFolder,"../../")
const inPackageRoot = fs.existsSync(path.join(currentFolder,"package.json")) && fs.existsSync(path.join(workspaceRoot,"pnpm-workspace.yaml"))
if(!inPackageRoot){
throw new Error("命令只能在工作区的包目录下执行")
}
}
/**
* 执行脚本出错会返回错误信息
* @param {*} script
*/
function execShellScript(script,options={}){
let {code,stdout} = shelljs.exec(script,options)
if(code>0){
new Error(`执行<${script}>失败: ${stdout.trim()}`)
}
}
/**
* 异步执行脚本
* @param {*} script
* @param {*} options
* @returns
*/
async function asyncExecShellScript(script,options={}){
return new Promise((resolve,reject)=>{
shelljs.exec(script,{...options,async:true},(code,stdout)=>{
if(code>0){
reject(new Error(`执行<${script}>失败: ${stdout.trim()}`))
}else{
resolve()
}
})
})
}
/**
* 执行脚本并返回结果
* @param {*} script
*/
function execShellScriptReturns(script,options={}){
return shelljs.exec(script,options).stdout.trim()
}
/**
* 读取指定包最近一次更新的时间
* 通过遍历所有文件夹
* @param {*} folder
* @returns
*/
function getFolderLastModified(folder,patterns=[],options={}){
patterns.push(...[
"package.json",
"**",
"**/*",
"!node_modules/**",
"!node_modules/**/*",
"!**/node_modules/**",
"!**/node_modules/**/*",
])
const glob = require("fast-glob")
let files = glob.sync(patterns, {
cwd: folder,
absolute:true,
...options
})
let lastUpdateTime = null
for(let file of files){
const { mtimeMs } = fs.statSync(file)
lastUpdateTime = lastUpdateTime ? Math.max(lastUpdateTime,mtimeMs) : mtimeMs
}
return lastUpdateTime
}
function getFileLastModified(file){
const { mtimeMs } = fs.statSync(file)
return mtimeMs
}
/**
*
* @param {*} packageInfo {name:"@voerkai18n/autopublish",value:"",version:"1.0.0",lastPublish:"2020-05-01T00:00:00.000Z"}
*/
async function runPackageScript(workspaceRoot,packageInfo,{silent=false}={}){
const packageFolder = path.join(workspaceRoot,"packages",packageInfo.value)
const package = fs.readJSONSync(path.join(packageFolder,"package.json"))
const lastModified = getFolderLastModified(packageFolder)
// 进入包所在的文件夹
shelljs.cd(packageFolder)
// 每个包必须定义自己的发布脚本
if("release" in package.scripts){
await asyncExecShellScript(`pnpm release`,{silent})
}else{
const reason = `包[{}]没有定义自动发布脚本release`
if(showLog) logger.log(reason,package.name)
throw new Error(`未配置<release>脚本`)
}
}
/**
*
* 返回指定包自上次发布之后是否有更新过
*
* @param {*} packageFolder
*
*
*/
function packageIsDirty(packageFolder){
const pkgFile = path.join(packageFolder,"package.json")
if(!fs.existsSync(pkgFile)){
logger.log("当前包[{}]不存在package.json文件",packageFolder)
throw new Error("当前包不存在package.json文件")
}
const package = fs.readJSONSync(pkgFile)
const lastModified = getFolderLastModified(packageFolder)
const lastPublish = package.lastPublish
// 由于上一次发布时会更新package.json文件如果最后更新的文件时间==package.json文件最后更新时间则说明没有更新
const pkgLastModified = getFileLastModified(pkgFile)
return dayjs(lastModified).isAfter(dayjs(lastPublish)) && !dayjs(pkgLastModified).isSame(dayjs(lastModified))
}
/**
* 发布所有包
*
* 将比对最后发布时间和最后修改时间的差别来决定是否发布
*
*
* @param {*} packages
*/
async function publishAllPackages(packages,options={}){
const tasks = logger.tasklist()
const workspaceRoot = process.cwd()
// 依次对每个包进行发布
for(let package of packages){
tasks.add(`发布包[${package.name}]`)
try{
if(package.isDirty){
await runPackageScript(workspaceRoot,package,{silent:true,...options})
let { version } = fs.readJSONSync(path.join(workspaceRoot,"packages",package.value,"package.json"))
tasks.complete(`${package.version}->${version}`)
}else{
tasks.skip()
}
}catch(e){
tasks.error(`${e.message}`)
}
}
}
/**
* 发布包并且在package.json中记录最后发布时间
* 本命令只能在包文件夹下执行
* @param {*} options
*/
async function publishPackage(options){
const { versionIncrementStep,silent=true } = options
// 此命令需要切换到包所在目录
const packageFolder = process.cwd()
const packageName = path.basename(packageFolder)
const pkgFile = path.join(packageFolder,"package.json")
if(!fs.existsSync(pkgFile)){
logger.log("当前包[{}]不存在package.json文件",packageName)
throw new Error("当前包不存在package.json文件,请在包文件夹下执行")
}
let package = fs.readJSONSync(pkgFile)
const oldVersion = package.version
let packageBackup = Object.assign({},package) // 备份package.json当操作失败时还原
logger.log("发布包:{}",`@voerkai18n/${packageName}`)
const tasks = logger.tasklist()
try{
// 第一步: 更新版本号和发布时间
tasks.add("更新版本号")
await asyncExecShellScript(`npm version ${versionIncrementStep}`,{silent})
// 重新读取包
package = fs.readJSONSync(pkgFile)
packageBackup = Object.assign({},package)
tasks.complete(`${oldVersion}->${package.version}`)
// 第二步:构建包
if("build" in package.scripts){
tasks.add("构建包")
await asyncExecShellScript(`pnpm build`,{silent})
tasks.complete()
}
// 第三步:发布
// 由于工程可能引用了工作区内的其他包必须pnpm publish才能发布
// pnpm publish会修正引用工作区其他包到的依赖信息而npm publish不能识别工作区内的依赖会导致报错
tasks.add("发布包")
await asyncExecShellScript(`pnpm publish --no-git-checks --access public`,{silent})
tasks.complete()
// 第四步:更新发布时间
tasks.add("更新发布时间")
package.lastPublish = dayjs().format()
fs.writeFileSync(pkgFile,JSON.stringify(package,null,4))
tasks.complete()
}catch(e){// 如果发布失败则还原package.json
fs.writeFileSync(pkgFile,JSON.stringify(packageBackup,null,4))
tasks.error(`${e.message}`)
}
}
program
.command("list")
.description("列出各个包的最后一次提交时间和版本信息")
.action(options => {
assertInWorkspaceRoot()
workspaceRoot = process.cwd()
const table = logger.table({grid:1})
table.addHeader("包名","版本号","最后提交时间","最后修改时间")
getPackages().forEach(package => {
const lastPublish = package.lastPublish ? dayjs(package.lastPublish).format("MM/DD hh:mm:ss") : "None"
const lastPublishRef = package.lastPublish ? `(${dayjs(package.lastPublish).fromNow()})` : ""
const lastModified = getFolderLastModified(path.join(workspaceRoot,"packages",package.value))
const lastUpdate = dayjs(lastModified).format("MM/DD hh:mm:ss")
const lastUpdateRef = dayjs(lastModified).fromNow()
if(package.lastPublish){
table.addRow(package.name,package.version,`${lastPublish}(${lastPublishRef})`,`${lastUpdate}(${lastUpdateRef})`)
}else{
table.addRow(package.name,package.version,"None",`${lastUpdate}(${lastUpdateRef})`)
}
})
table.render()
generatePackageVersionDoc()
})
// 生成包版本列表文件到文档中
function generatePackageVersionDoc(){
let workspaceRoot = path.join(__dirname,"../../")
shelljs.cd(workspaceRoot)
let results = []
results.push("# 版本信息")
results.push("| 包| 版本号| 最后更新|说明|")
results.push("| --- | :---:| --- |---|")
getPackages().forEach(package => {
const lastPublish = package.lastPublish ? dayjs(package.lastPublish).format("YYYY/MM/DD") : "None"
results.push(`|**${package.name}**|${package.version}|${lastPublish}|${package.description}|`)
})
fs.writeFileSync(path.join(workspaceRoot,"docs/src/guide/intro/versions.md"), results.join("\n"))
}
async function answerForSelectPackages(packages,options){
const workspaceRoot = process.cwd()
return new Promise((resolve,reject) => {
inquirer
.prompt([
{
type: "checkbox",
name: "selectPackages",
message: "请选择要发布的库:",
choices: packages.map(package => {
const lastPublish = package.lastPublish ? dayjs(package.lastPublish).format("MM/DD hh:mm:ss") : "None"
const lastPublishRef = package.lastPublish ? `(${dayjs(package.lastPublish).fromNow()})` : ""
const lastModified = getFolderLastModified(path.join(workspaceRoot,"packages",package.value))
const lastUpdate = dayjs(lastModified).format("MM/DD hh:mm:ss")
const lastUpdateRef = dayjs(lastModified).fromNow()
return {
...package,
value: package,
name:`${package.name.padEnd(24)}Version: ${package.version.padEnd(8)} LastPublish: ${lastPublish.padEnd(16)}${lastPublishRef} lastModified: ${lastUpdate}(${lastUpdateRef})`, }
}),
pageSize:12,
loop: false
},
])
.then((answers) => {
resolve(answers.selectPackages)
})
.catch((error) => {
logger.log(error.message)
reject(error)
});
})
}
/**
* 发布包的模式
*
* 1. 在包中使用
* {
* scripts:{
* "release":"pnpm autopublish"
* }
* }
*
* 2. 发布所有包
* {
* scripts:{
* "autopublish":"pnpm autopublish -a"
* }
* }
* > pnpm autopublish -- -a // 自动发布,会询问要发布的
* > pnpm autopublish -- -a --no-ask // 自动发布,不会询问全自动发布
*
*/
const VERSION_STEPS = ["major", "minor", "patch","premajor","preminor","prepatch","prerelease"]
program
.description("自动发布包")
.option("-a, --all", "发布所有包")
.option("-n, --no-ask", "不询问")
.option("-s, --no-silent", "静默显示脚本输出")
.addOption(new Option('-i, --version-increment-step [value]', '版本增长方式').default("patch").choices(VERSION_STEPS))
.action(async (options) => {
// 发布所有包时只能在工作区根目录下执行
if(options.all){
assertInWorkspaceRoot()
}else{// 发布指定包时只能在包目录下执行
assertInPackageRoot()
}
if(options.all){ // 自动发布所有包
const workspaceRoot = process.cwd()
let packages = getPackages()
if(options.ask){
packages = await answerForSelectPackages(packages,options)
}
if(packages.length > 0){
await publishAllPackages(packages,options)
}
}else{// 只发布指定的包
await publishPackage(options)
}
// 在文档中输出各包的版本信息
generatePackageVersionDoc()
})
program.parseAsync(process.argv);

View File

@ -1,21 +0,0 @@
{
"name": "@voerkai18n/autopublish",
"version": "1.0.3",
"description": "自动发布工作区的包",
"main": "index.js",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"release": "node ./index.js publish"
},
"author": "",
"license": "ISC",
"bin": {
"autopublish": "./index.js"
},
"dependencies": {
"commander": "^9.0.0",
"fast-glob": "^3.2.11"
},
"lastPublish": "2022-04-06T15:36:15+08:00"
}

View File

@ -1,111 +0,0 @@
# 概述
`@voerkai18n`项目是一个标准的`monorepo`包工程,包含了`@voerkai18n/cli``@voerkai18n/runtime``@voerkai18n/utils``@voerkai18n/vue``@voerkai18n/vite``@voerkai18n/babel``@voerkai18n/react``@voerkai18n/formatters`等多个包,发布包时容易引起混乱问题,最大问题时:
- 经常忘记哪个包最近什么时间修改,哪个包应该发布。
- 由于包之间存在依赖关系,需要按一定的顺序进行发布
`@voerkai18n/autopublish`用来实现全自动或手动辅助进行发布。
**源码与文档:**[https://gitee.com/zhangfisher/voerka-i18n](https://gitee.com/zhangfisher/voerka-i18n)
[![fisher/voerka-i18n](https://gitee.com/zhangfisher/voerka-i18n/widgets/widget_card.svg?colors=4183c4,ffffff,ffffff,e3e9ed,666666,9b9b9b)](https://gitee.com/zhangfisher/voerka-i18n)
# 使用
## 准备
`@voerkai18n/autopublish`用于采用`pnpmp`创建的`monorepo`包工程,不支持`lerna/yarn`
按照常规约定,包存放在`<projectRoot>/packages/<name>`
## 第一步:配置包的发布脚本
`@voerkai18n/autopublish`添加为包的开发依赖。
```javascript
// 进入包文件夹后执行
> pnpm add -D @voerkai18n/autopublish
```
然后,配置发布脚本:
```json
{
"scripts":{
"build":"默认的包构建命令",
"release":"pnpm autopublish"
},
"devDependencies": {
"@voerkai18n/autopublish": "workspace:^1.0.0"
}
}
```
- 发布脚本必须为`release`,不能是其他名称,特别是`publish`
- `pnpm autopublish`也可以在包路径下单独执行。
- `pnpm autopublish`默认依次执行:
- `npm version patch`:升级版本号
- `pnpm run build`(可选)
- `pnpm publish --no-git-checks --access publish`
- 默认每次发布均会升级`patch`版本号,可以通过`pnpm autopublish -i <版本递增方式>`来增加版本号,递增方式可选:[`"major"`, `"minor"`, `"patch"`,`"premajor"`,`"preminor"`,`"prepatch"`,`"prerelease"`]
- 每次执行`pnpm autopublish`均会在当前包的`package.json`中添加`lastPublish`字段,用来记录发布的时间。这是下一次发布时进行自动比对发布的依据。
## 第二步:配置工作区发布脚本
在当前工程的根文件夹下配置`package.json`
```json
{
"scripts":{
"list:package": "node ./packages/autopublish/index.js list",
"autopublish": "node ./packages/autopublish/index.js"
},
"devDependencies": {
"@voerkai18n/autopublish": "workspace:^1.0.0"
}
}
```
## 第三步:自动发布所有包
```javascript
> pnpm autopublish -- -a -n
```
`pnpm autopublish`会自动枚举出当前所有包,然后对比包路径`packages/<包名>`最后修改时间和`package.json``lastPublish`字段值,如果:
- 最后修改时间大于最后发布时间,则发布该包
- 最后修改时间关于或者小于最后发布时间,则忽略发布该包
因此,每次当修改完工程后,可以自动执行`pnpm autopublish -- -a -n`就可以进行全自动发布。
`pnpm autopublish`命令行参数:
```shell
自动发布包
Options:
-a, --all 发布所有包
-n, --no-ask 不询问
-s, --no-silent 静默显示脚本输出
-i, --version-increment-step [value] 版本增长方式 (choices: "major", "minor", "patch", "premajor", "preminor", "prepatch",
"prerelease", default: "patch")
-h, --help display help for command
Commands:
list 列出各个包的最后一次提交时间和版本信息
```
- `-a`代表要发布所有包,如果没有启用`-n`,则会让用户选择要发布哪一个包。如果启用`-n`参数,则会全自动比对发布时间和修改时间后发布。
- `-no-ask`代表不会询问让用户选择要发布的包.
- `--no-silent`代表是否不输出脚本输出。
- 由于包之间存在依赖关系,`autopublish`会根据依赖关系进行排序发布和关联发布。比如`@voerkai18n/cli`依赖于`@voerkai18n/utils`,当`@voerkai18n/utils`有更新需要发布时,`@voerkai18n/cli`也会自动发布。
## 第四步: 手动选择发布
`pnpm autopublish -- -a -n`会根据发布时间和修改时间进行自动发布。也支持手动选择要发布的包。
```javascript
> pnpm autopublish -- -a
````
如果不启用`-n`参数,则会列出当前工作区的所有包,让用户选择要发布的包。
## 列出包
`pnpm autopublish -- list`列出当前工程的所有包,并显示当前包最近更新和最近发布时间。

View File

@ -1,24 +0,0 @@
const shelljs = require("shelljs");
const path = require("path")
/**
* 检测当前工程是否是git工程
*/
function isGitRepo(){
return shelljs.exec("git status", {silent: true}).code === 0;
}
function getProjectName(){
const pkgFile = path.join(process.cwd(), "package.json")
const pkg = fs.readJSONSync(pkgFile);
return pkg.name;
}
module.exports ={
isMonorepo,
isGitRepo
}

View File

@ -4,6 +4,7 @@ import type React from "react"
export type useVoerkaI18n = ()=>{ export type useVoerkaI18n = ()=>{
language:string language:string
changeLanguage:(newLanguage:string)=>Promise<void> changeLanguage:(newLanguage:string)=>Promise<void>
defaultLanguage:string, // 默认语言
languages:VoerkaI18nSupportedLanguages, languages:VoerkaI18nSupportedLanguages,
t:VoerkaI18nTranslate t:VoerkaI18nTranslate
} }

View File

@ -3,6 +3,7 @@ import React, { useState, useEffect,useContext,useCallback} from 'react';
export const VoerkaI18nContext = React.createContext({ export const VoerkaI18nContext = React.createContext({
languages:null, languages:null,
language:'zh', language:'zh',
defaultLanguage:null,
changeLanguage:() => {}, changeLanguage:() => {},
t:()=>{} t:()=>{}
}) })
@ -30,6 +31,7 @@ export function VoerkaI18nProvider(props){
<VoerkaI18nContext.Provider value={{ <VoerkaI18nContext.Provider value={{
language, language,
changeLanguage, changeLanguage,
defaultLanguage:VoerkaI18n.defaultLanguage,
languages:VoerkaI18n.languages, languages:VoerkaI18n.languages,
t:scope.t t:scope.t
}}> }}>

View File

@ -59,11 +59,12 @@ const importTRegex = /^[^\w\r\n]*import\s*\{(.*)\bt\b(.*)\}\sfrom/gm
function replaceCode(code, idmap) { function replaceCode(code, idmap) {
return code.replaceAll(TranslateRegex, (message) => { return code.replaceAll(TranslateRegex, (message) => {
if(message in idmap) { if(message in idmap) {
return idmap[message] return idmap[message]
}else{ }else{
return message const msg = unescape(message.replaceAll("\\u","%u"))
} return msg in idmap ? idmap[msg] : message
}
}) })
} }
@ -94,6 +95,7 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
/\.vue(\?.*)?/, // 所有vue文件 /\.vue(\?.*)?/, // 所有vue文件
"!(?<!.jsx\?.*).(css|json|scss|less|sass)$", "!(?<!.jsx\?.*).(css|json|scss|less|sass)$",
/\.jsx(\?.*)?/, /\.jsx(\?.*)?/,
/\.ts(\?.*)?/,
] // 提取范围 ] // 提取范围
},opts) },opts)
@ -103,7 +105,7 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
let languageFolder = getProjectLanguageFolder(projectRoot) let languageFolder = getProjectLanguageFolder(projectRoot)
if(!fs.existsSync(languageFolder)){ if(!fs.existsSync(languageFolder)){
console.warn(`Voerkai18n语言文件夹不存在@voerkai18n/vite未启用`) console.warn(`Voerkai18n语言文件夹不存在,@voerkai18n/vite未启用`)
} }
if(debug){ if(debug){
console.log("Project root: ",projectRoot) console.log("Project root: ",projectRoot)
@ -141,8 +143,9 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
importSource = "./" + importSource importSource = "./" + importSource
} }
importSource=importSource.replace("\\","/") importSource=importSource.replace("\\","/")
const extName = path.extname(id)
// 转换Vue文件 // 转换Vue文件
if(path.extname(id)==".vue"){ if(extName==".vue"){
// 优先在<script setup></script>中导入 // 优先在<script setup></script>中导入
const setupScriptRegex = /(^\s*\<script.*\s*setup\s*.*\>)/gmi const setupScriptRegex = /(^\s*\<script.*\s*setup\s*.*\>)/gmi
if(setupScriptRegex.test(code)){ if(setupScriptRegex.test(code)){
@ -150,7 +153,7 @@ module.exports = function VoerkaI18nPlugin(opts={}) {
}else{// 如果没有<script setup>则在<script></script>中导入 }else{// 如果没有<script setup>则在<script></script>中导入
code = code.replace(/(^\s*\<script.*\>)/gmi,`$1\nimport { t } from '${importSource}';`) code = code.replace(/(^\s*\<script.*\>)/gmi,`$1\nimport { t } from '${importSource}';`)
} }
}else{// 普通js文件需要添加到最前面 }else if(['.js','.ts'].includes(extName)){// 普通js/ts文件需要添加到最前面
code = code = `import { t } from '${importSource}';\n${code}` code = code = `import { t } from '${importSource}';\n${code}`
} }
} }

3
pnpm-lock.yaml generated
View File

@ -229,6 +229,7 @@ importers:
rollup-plugin-clear: ^2.0.7 rollup-plugin-clear: ^2.0.7
rollup-plugin-terser: ^7.0.2 rollup-plugin-terser: ^7.0.2
dependencies: dependencies:
'@babel/runtime': 7.18.9
'@babel/runtime-corejs3': 7.20.7 '@babel/runtime-corejs3': 7.20.7
core-js: 3.21.1 core-js: 3.21.1
devDependencies: devDependencies:
@ -236,7 +237,6 @@ importers:
'@babel/core': 7.18.10 '@babel/core': 7.18.10
'@babel/plugin-transform-runtime': 7.18.10_@babel+core@7.18.10 '@babel/plugin-transform-runtime': 7.18.10_@babel+core@7.18.10
'@babel/preset-env': 7.18.10_@babel+core@7.18.10 '@babel/preset-env': 7.18.10_@babel+core@7.18.10
'@babel/runtime': 7.18.9
'@rollup/plugin-babel': 5.3.1_tui6liyexu3zy4m5r2rytc7ixu '@rollup/plugin-babel': 5.3.1_tui6liyexu3zy4m5r2rytc7ixu
'@rollup/plugin-commonjs': 21.1.0_rollup@2.77.2 '@rollup/plugin-commonjs': 21.1.0_rollup@2.77.2
'@rollup/plugin-node-resolve': 13.3.0_rollup@2.77.2 '@rollup/plugin-node-resolve': 13.3.0_rollup@2.77.2
@ -1504,7 +1504,6 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
regenerator-runtime: 0.13.9 regenerator-runtime: 0.13.9
dev: true
/@babel/standalone/7.18.10: /@babel/standalone/7.18.10:
resolution: {integrity: sha512-0KWHiRX9TUHiWE+dKYYEOIiRJcPwGU6u8Bq/p+ldekj7Kew9PCwl4S4FTSEPpTrn3Vc+r3iRSaN1l9AcGgLx4Q==} resolution: {integrity: sha512-0KWHiRX9TUHiWE+dKYYEOIiRJcPwGU6u8Bq/p+ldekj7Kew9PCwl4S4FTSEPpTrn3Vc+r3iRSaN1l9AcGgLx4Q==}