diff --git a/docs/.vuepress/configs/navbar/en.ts b/docs/.vuepress/configs/navbar/en.ts index 95136945f6..245a503c5e 100644 --- a/docs/.vuepress/configs/navbar/en.ts +++ b/docs/.vuepress/configs/navbar/en.ts @@ -20,6 +20,7 @@ export const navbarEn: NavbarConfig = [ { text: 'Common Features', children: [ + '/plugins/append-date', '/plugins/back-to-top', '/plugins/catalog', '/plugins/copy-code', diff --git a/docs/.vuepress/configs/navbar/zh.ts b/docs/.vuepress/configs/navbar/zh.ts index e0e565ae17..ebe3482770 100644 --- a/docs/.vuepress/configs/navbar/zh.ts +++ b/docs/.vuepress/configs/navbar/zh.ts @@ -20,6 +20,7 @@ export const navbarZh: NavbarConfig = [ { text: '常用功能', children: [ + '/zh/plugins/append-date', '/zh/plugins/back-to-top', '/zh/plugins/catalog', '/zh/plugins/copy-code', diff --git a/docs/.vuepress/configs/sidebar/en.ts b/docs/.vuepress/configs/sidebar/en.ts index 925f91d4b4..44e58f7b75 100644 --- a/docs/.vuepress/configs/sidebar/en.ts +++ b/docs/.vuepress/configs/sidebar/en.ts @@ -5,6 +5,7 @@ export const sidebarEn: SidebarConfig = { { text: 'Common Features', children: [ + '/plugins/append-date', '/plugins/back-to-top', '/plugins/catalog', '/plugins/copy-code', diff --git a/docs/.vuepress/configs/sidebar/zh.ts b/docs/.vuepress/configs/sidebar/zh.ts index c65311ebeb..7df99c8007 100644 --- a/docs/.vuepress/configs/sidebar/zh.ts +++ b/docs/.vuepress/configs/sidebar/zh.ts @@ -5,6 +5,7 @@ export const sidebarZh: SidebarConfig = { { text: '常用功能', children: [ + '/zh/plugins/append-date', '/zh/plugins/back-to-top', '/zh/plugins/catalog', '/zh/plugins/copy-code', diff --git a/docs/plugins/append-date.md b/docs/plugins/append-date.md new file mode 100644 index 0000000000..85961e878a --- /dev/null +++ b/docs/plugins/append-date.md @@ -0,0 +1,37 @@ +# back-to-top + + + +This plugin will append writing date to frontmatter with [@vuepress/plugin-git](git.md). + +## Usage + +```bash +npm i -D @vuepress/plugin-append-date@next +``` + +```ts +import { appendDatePlugin } from '@vuepress/plugin-append-date' + +export default { + plugins: [appendDatePlugin()], +} +``` + +## Options + +### key + +- Type: `string` +- Default: `"date"` +- Details: + + Frontmatter key to use when appending date. + +### format + +- Type: `"date" | "time" | "full"` +- Default: `"date"` +- Details: + + Format of the date value when appending date. diff --git a/docs/zh/plugins/append-date.md b/docs/zh/plugins/append-date.md new file mode 100644 index 0000000000..10f730af54 --- /dev/null +++ b/docs/zh/plugins/append-date.md @@ -0,0 +1,33 @@ +# back-to-top + + + +该插件会基于 [@vuepress/plugin-git](git.md) 为 frontmatter 追加写作日期。 + +## 使用方法 + +```bash +npm i -D @vuepress/plugin-append-date@next +``` + +```ts +import { appendDatePlugin } from '@vuepress/plugin-append-date' + +export default { + plugins: [appendDatePlugin()], +} +``` + +## 选项 + +### key + +- 类型: `string` +- 默认值: `"date"` +- 详情:追加时间时使用的 frontmatter 键名。 + +### format + +- 类型: `"date" | "time" | "full"` +- 默认值: `"date"` +- 详情:追加时间时使用的日期格式。 diff --git a/plugins/plugin-append-date/package.json b/plugins/plugin-append-date/package.json new file mode 100644 index 0000000000..332b2c7bbb --- /dev/null +++ b/plugins/plugin-append-date/package.json @@ -0,0 +1,50 @@ +{ + "name": "@vuepress/plugin-append-date", + "version": "2.0.0-rc.19", + "description": "VuePress plugin - append date", + "keywords": [ + "vuepress-plugin", + "vuepress", + "plugin", + "append-date" + ], + "homepage": "https://ecosystem.vuejs.press/plugins/append-date.html", + "bugs": { + "url": "https://github.com/vuepress/ecosystem/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuepress/ecosystem.git", + "directory": "plugins/plugin-append-date" + }, + "license": "MIT", + "author": { + "name": "Mr.Hope", + "email": "mister-hope@outlook.com", + "url": "https://mister-hope.com" + }, + "type": "module", + "exports": { + ".": "./lib/node/index.js", + "./package.json": "./package.json" + }, + "main": "lib/node/index.js", + "types": "lib/node/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "build": "tsc -b tsconfig.build.json", + "clean": "rimraf --glob ./lib ./*.tsbuildinfo" + }, + "dependencies": { + "@vuepress/helper": "workspace:~" + }, + "peerDependencies": { + "@vuepress/plugin-git": "workspace:~", + "vuepress": "2.0.0-rc.8" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/plugins/plugin-append-date/src/node/appendDate.ts b/plugins/plugin-append-date/src/node/appendDate.ts new file mode 100644 index 0000000000..691d0c74b5 --- /dev/null +++ b/plugins/plugin-append-date/src/node/appendDate.ts @@ -0,0 +1,42 @@ +import { startsWith } from '@vuepress/helper' +import type { GitPluginPageData } from '@vuepress/plugin-git' +import type { Page } from 'vuepress/core' +import { fs } from 'vuepress/utils' +import { + getDateString, + getFullDateString, + getTimeString, +} from './formatDate.js' +import type { AppendDatePluginOptions } from './options.js' + +export const appendDateToPage = async ( + { key = 'date', format = 'date' }: AppendDatePluginOptions, + { data, filePath, frontmatter }: Page, +): Promise => { + if (frontmatter[key] || !filePath) return + + const { createdTime } = data.git + + if (!createdTime) return + + const date = new Date(createdTime) + + const text = + format === 'time' + ? getTimeString(date) + : format === 'full' + ? getFullDateString(date) + : getDateString(date) + + frontmatter[key] = new Date(createdTime) + + const markdownContent = await fs.readFile(filePath, 'utf-8') + + await fs.writeFile( + filePath, + startsWith(markdownContent, '---\n') + ? `---\n${key}: ${text}\n${markdownContent.substring(4)}` + : `---\n${key}: ${text}\n---\n\n${markdownContent}`, + 'utf-8', + ) +} diff --git a/plugins/plugin-append-date/src/node/appendDatePlugin.ts b/plugins/plugin-append-date/src/node/appendDatePlugin.ts new file mode 100644 index 0000000000..378dc66f34 --- /dev/null +++ b/plugins/plugin-append-date/src/node/appendDatePlugin.ts @@ -0,0 +1,21 @@ +import type { GitPluginPageData } from '@vuepress/plugin-git' +import type { Page, PluginObject } from 'vuepress/core' +import { appendDateToPage } from './appendDate.js' +import { isGitPluginEnabled } from './checkGitPlugin.js' +import { PLUGIN_NAME } from './logger.js' +import type { AppendDatePluginOptions } from './options.js' + +export const appendDatePlugin = ( + options: AppendDatePluginOptions = {}, +): PluginObject => ({ + name: PLUGIN_NAME, + + onInitialized: async (app): Promise => { + if (isGitPluginEnabled(app)) + await Promise.all( + (app.pages as Page[]).map((page) => + appendDateToPage(options, page), + ), + ) + }, +}) diff --git a/plugins/plugin-append-date/src/node/checkGitPlugin.ts b/plugins/plugin-append-date/src/node/checkGitPlugin.ts new file mode 100644 index 0000000000..5d3c0b3d0a --- /dev/null +++ b/plugins/plugin-append-date/src/node/checkGitPlugin.ts @@ -0,0 +1,32 @@ +import { createRequire } from 'node:module' +import type { App } from 'vuepress/core' +import { colors } from 'vuepress/utils' +import { logger } from './logger.js' + +const require = createRequire(import.meta.url) + +const GIT_PLUGIN_NAME = '@vuepress/plugin-git' + +export const isGitPluginEnabled = (app: App): boolean => { + if ( + app.pluginApi.plugins.every((plugin) => plugin.name !== GIT_PLUGIN_NAME) + ) { + try { + require.resolve(GIT_PLUGIN_NAME) + + logger.info(`${colors.magenta(GIT_PLUGIN_NAME)} is not enabled.`) + + return false + } catch (err) { + logger.error( + `${colors.magenta( + GIT_PLUGIN_NAME, + )} is required for this plugin, please install it.`, + ) + } + + return false + } + + return true +} diff --git a/plugins/plugin-append-date/src/node/formatDate.ts b/plugins/plugin-append-date/src/node/formatDate.ts new file mode 100644 index 0000000000..16b8f7a833 --- /dev/null +++ b/plugins/plugin-append-date/src/node/formatDate.ts @@ -0,0 +1,23 @@ +const padZero = (num: number, length = 2): string => + num.toString().padStart(length, '0') + +const getFullYear = (date: Date): string => padZero(date.getFullYear(), 4) + +const getMonth = (date: Date): string => padZero(date.getMonth() + 1) + +const getDate = (date: Date): string => padZero(date.getDate()) + +const getHours = (date: Date): string => padZero(date.getHours()) + +const getMinutes = (date: Date): string => padZero(date.getMinutes()) + +const getSeconds = (date: Date): string => padZero(date.getSeconds()) + +export const getDateString = (date: Date): string => + `${getFullYear(date)}-${getMonth(date)}-${getDate(date)}` + +export const getTimeString = (date: Date): string => + `${getHours(date)}:${getMinutes(date)}:${getSeconds(date)}` + +export const getFullDateString = (date: Date): string => + `${getDateString(date)} ${getTimeString(date)}` diff --git a/plugins/plugin-append-date/src/node/index.ts b/plugins/plugin-append-date/src/node/index.ts new file mode 100644 index 0000000000..7011377e74 --- /dev/null +++ b/plugins/plugin-append-date/src/node/index.ts @@ -0,0 +1,2 @@ +export * from './appendDatePlugin.js' +export * from './options.js' diff --git a/plugins/plugin-append-date/src/node/logger.ts b/plugins/plugin-append-date/src/node/logger.ts new file mode 100644 index 0000000000..865ace8f35 --- /dev/null +++ b/plugins/plugin-append-date/src/node/logger.ts @@ -0,0 +1,5 @@ +import { Logger } from '@vuepress/helper' + +export const PLUGIN_NAME = 'vuepress-plugin-append-date' + +export const logger = new Logger(PLUGIN_NAME) diff --git a/plugins/plugin-append-date/src/node/options.ts b/plugins/plugin-append-date/src/node/options.ts new file mode 100644 index 0000000000..d2aea7e016 --- /dev/null +++ b/plugins/plugin-append-date/src/node/options.ts @@ -0,0 +1,19 @@ +export interface AppendDatePluginOptions { + /** + * Frontmatter key to use when appending date. + * + * 追加时间时使用的 frontmatter 键名。 + * + * @default 'date' + */ + key?: string + + /** + * Format of the date value when appending date. + * + * 追加时间时使用的日期格式。 + * + * @default 'date' + */ + format?: 'date' | 'time' | 'full' +} diff --git a/plugins/plugin-append-date/tests/node/formatDate.spec.ts b/plugins/plugin-append-date/tests/node/formatDate.spec.ts new file mode 100644 index 0000000000..1fee1a90a3 --- /dev/null +++ b/plugins/plugin-append-date/tests/node/formatDate.spec.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' +import { + getDateString, + getFullDateString, + getTimeString, +} from '../../src/node/formatDate.js' + +describe('getDateString', () => { + it('should return date string', () => { + expect(getDateString(new Date('2021-01-01'))).toBe('2021-01-01') + }) + + it('should return date string with full date', () => { + expect(getDateString(new Date('2021-01-11 10:11:12'))).toBe('2021-01-11') + }) +}) + +describe('getTimeString', () => { + it('should return time string with date', () => { + expect(getTimeString(new Date('2021-01-01'))).toBe('00:00:00') + }) + + it('should return time string with full date', () => { + expect(getTimeString(new Date('2021-01-11 10:11:12'))).toBe('10:11:12') + }) +}) + +describe('getFullDateString', () => { + it('should return full date string', () => { + expect(getFullDateString(new Date('2021-01-01'))).toBe( + '2021-01-01 00:00:00', + ) + }) + + it('should return full date string with full date', () => { + expect(getFullDateString(new Date('2021-01-11 10:11:12'))).toBe( + '2021-01-11 10:11:12', + ) + }) +}) diff --git a/plugins/plugin-append-date/tsconfig.build.json b/plugins/plugin-append-date/tsconfig.build.json new file mode 100644 index 0000000000..9b984d7cd2 --- /dev/null +++ b/plugins/plugin-append-date/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib" + }, + "include": ["./src"], + "references": [ + { "path": "../plugin-git/tsconfig.build.json" }, + { "path": "../../tools/helper/tsconfig.build.json" } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2fcdd73a6..c77b043d29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,6 +223,18 @@ importers: specifier: 2.0.0-rc.8 version: 2.0.0-rc.8(@vuepress/bundler-vite@2.0.0-rc.8)(@vuepress/bundler-webpack@2.0.0-rc.8)(typescript@5.4.2)(vue@3.4.21) + plugins/plugin-append-date: + dependencies: + '@vuepress/helper': + specifier: workspace:~ + version: link:../../tools/helper + '@vuepress/plugin-git': + specifier: workspace:~ + version: link:../plugin-git + vuepress: + specifier: 2.0.0-rc.8 + version: 2.0.0-rc.8(@vuepress/bundler-vite@2.0.0-rc.8)(@vuepress/bundler-webpack@2.0.0-rc.8)(typescript@5.4.2)(vue@3.4.21) + plugins/plugin-back-to-top: dependencies: '@vuepress/helper': diff --git a/tsconfig.build.json b/tsconfig.build.json index a0716fa33b..44e2d38062 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -7,6 +7,7 @@ { "path": "./plugins/plugin-active-header-links/tsconfig.build.json" }, + { "path": "./plugins/plugin-append-date/tsconfig.build.json" }, { "path": "./plugins/plugin-back-to-top/tsconfig.build.json" }, { "path": "./plugins/plugin-baidu-analytics/tsconfig.build.json" }, { "path": "./plugins/plugin-blog/tsconfig.build.json" },