create-halo-plugin-template 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.editorconfig +520 -0
  2. package/.github/workflows/cd.yaml +20 -0
  3. package/.github/workflows/ci.yaml +32 -0
  4. package/.github/workflows/publish-npm.yaml +46 -0
  5. package/.gitignore +84 -0
  6. package/LICENSE +674 -0
  7. package/README.md +191 -0
  8. package/build.gradle +103 -0
  9. package/docs/first-npm-release-checklist.md +58 -0
  10. package/docs/publish-template.md +148 -0
  11. package/docs/rsbuild-switch.md +90 -0
  12. package/docs/template-pruning.md +43 -0
  13. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  14. package/gradle/wrapper/gradle-wrapper.properties +7 -0
  15. package/gradle.properties +1 -0
  16. package/gradlew +248 -0
  17. package/gradlew.bat +93 -0
  18. package/package.json +67 -7
  19. package/scripts/create-project.mjs +399 -0
  20. package/scripts/init-template.mjs +281 -0
  21. package/scripts/publish-check.mjs +97 -0
  22. package/scripts/release.mjs +278 -0
  23. package/scripts/verify-template.mjs +407 -0
  24. package/settings.gradle +7 -0
  25. package/src/main/java/run/halo/plugintemplate/PluginTemplatePlugin.java +43 -0
  26. package/src/main/java/run/halo/plugintemplate/config/PluginTemplateConfig.java +14 -0
  27. package/src/main/java/run/halo/plugintemplate/dto/PluginTemplateChecklistItem.java +30 -0
  28. package/src/main/java/run/halo/plugintemplate/dto/PluginTemplateFeatureItem.java +30 -0
  29. package/src/main/java/run/halo/plugintemplate/dto/PluginTemplateOverview.java +73 -0
  30. package/src/main/java/run/halo/plugintemplate/dto/PluginTemplateStatItem.java +30 -0
  31. package/src/main/java/run/halo/plugintemplate/endpoint/PluginTemplateConsoleEndpoint.java +33 -0
  32. package/src/main/java/run/halo/plugintemplate/endpoint/PluginTemplatePublicEndpoint.java +26 -0
  33. package/src/main/java/run/halo/plugintemplate/endpoint/PluginTemplateUcEndpoint.java +33 -0
  34. package/src/main/java/run/halo/plugintemplate/endpoint/routes/PluginTemplateOverviewRoutes.java +60 -0
  35. package/src/main/java/run/halo/plugintemplate/model/PluginTemplateAudience.java +23 -0
  36. package/src/main/java/run/halo/plugintemplate/query/PluginTemplateOverviewQuery.java +26 -0
  37. package/src/main/java/run/halo/plugintemplate/reconcile/PluginTemplateSettingsReconciler.java +17 -0
  38. package/src/main/java/run/halo/plugintemplate/scheme/PluginTemplateRecord.java +43 -0
  39. package/src/main/java/run/halo/plugintemplate/service/PluginTemplateOverviewService.java +10 -0
  40. package/src/main/java/run/halo/plugintemplate/service/impl/PluginTemplateOverviewServiceImpl.java +74 -0
  41. package/src/main/java/run/halo/plugintemplate/setting/PluginTemplateGeneralSetting.java +25 -0
  42. package/src/main/java/run/halo/plugintemplate/setting/PluginTemplateSettingKeys.java +24 -0
  43. package/src/main/java/run/halo/plugintemplate/setting/PluginTemplateUiSetting.java +13 -0
  44. package/src/main/java/run/halo/plugintemplate/utils/PluginTemplateSeeds.java +197 -0
  45. package/src/main/resources/extensions/roleTemplate-console.yaml +39 -0
  46. package/src/main/resources/extensions/roleTemplate-uc.yaml +19 -0
  47. package/src/main/resources/extensions/settings.yaml +47 -0
  48. package/src/main/resources/logo.png +0 -0
  49. package/src/main/resources/plugin.yaml +24 -0
  50. package/src/test/java/run/halo/plugintemplate/PluginTemplatePluginTest.java +34 -0
  51. package/src/test/java/run/halo/plugintemplate/service/impl/PluginTemplateOverviewServiceImplTest.java +97 -0
  52. package/ui/build.gradle +41 -0
  53. package/ui/env.d.ts +2 -0
  54. package/ui/eslint.config.ts +30 -0
  55. package/ui/package.json +57 -0
  56. package/ui/pnpm-lock.yaml +5250 -0
  57. package/ui/src/api/__tests__/normalizers.spec.ts +65 -0
  58. package/ui/src/api/generated/.openapi-generator/FILES +23 -0
  59. package/ui/src/api/generated/.openapi-generator/VERSION +1 -0
  60. package/ui/src/api/generated/.openapi-generator-ignore +23 -0
  61. package/ui/src/api/generated/api/plugin-template-console-api.ts +128 -0
  62. package/ui/src/api/generated/api/plugin-template-uc-api.ts +128 -0
  63. package/ui/src/api/generated/api.ts +19 -0
  64. package/ui/src/api/generated/base.ts +86 -0
  65. package/ui/src/api/generated/common.ts +150 -0
  66. package/ui/src/api/generated/configuration.ts +110 -0
  67. package/ui/src/api/generated/git_push.sh +57 -0
  68. package/ui/src/api/generated/index.ts +18 -0
  69. package/ui/src/api/generated/models/add-operation.ts +49 -0
  70. package/ui/src/api/generated/models/copy-operation.ts +49 -0
  71. package/ui/src/api/generated/models/index.ts +11 -0
  72. package/ui/src/api/generated/models/json-patch-inner.ts +41 -0
  73. package/ui/src/api/generated/models/move-operation.ts +49 -0
  74. package/ui/src/api/generated/models/plugin-template-checklist-item.ts +54 -0
  75. package/ui/src/api/generated/models/plugin-template-feature-item.ts +54 -0
  76. package/ui/src/api/generated/models/plugin-template-overview.ts +147 -0
  77. package/ui/src/api/generated/models/plugin-template-stat-item.ts +54 -0
  78. package/ui/src/api/generated/models/remove-operation.ts +43 -0
  79. package/ui/src/api/generated/models/replace-operation.ts +49 -0
  80. package/ui/src/api/generated/models/test-operation.ts +49 -0
  81. package/ui/src/api/index.ts +42 -0
  82. package/ui/src/api/normalizers.ts +65 -0
  83. package/ui/src/assets/element.scss +24 -0
  84. package/ui/src/assets/index.css +361 -0
  85. package/ui/src/assets/logo.svg +1 -0
  86. package/ui/src/components/PluginTemplateAttachmentTab.vue +69 -0
  87. package/ui/src/components/PluginTemplateCommonTable.vue +69 -0
  88. package/ui/src/components/PluginTemplateDashboardWidget.vue +62 -0
  89. package/ui/src/components/PluginTemplateOverviewPage.vue +254 -0
  90. package/ui/src/components/ui/PluginUiProvider.vue +40 -0
  91. package/ui/src/components/ui/UiMetricCard.vue +21 -0
  92. package/ui/src/components/ui/UiSectionCard.vue +25 -0
  93. package/ui/src/components/ui/UiStatusPill.vue +18 -0
  94. package/ui/src/composables/useTemplateOverview.ts +38 -0
  95. package/ui/src/index.ts +88 -0
  96. package/ui/src/lib/__tests__/plugin-ui.spec.ts +19 -0
  97. package/ui/src/lib/plugin-ui.ts +19 -0
  98. package/ui/src/lib/template.spec.ts +24 -0
  99. package/ui/src/lib/template.ts +52 -0
  100. package/ui/src/lib/theme.ts +31 -0
  101. package/ui/src/types/index.ts +59 -0
  102. package/ui/src/views/console/ConsoleDashboardView.vue +7 -0
  103. package/ui/src/views/uc/UcDashboardView.vue +7 -0
  104. package/ui/tsconfig.app.json +12 -0
  105. package/ui/tsconfig.json +14 -0
  106. package/ui/tsconfig.node.json +15 -0
  107. package/ui/tsconfig.vitest.json +11 -0
  108. package/ui/vite.config.ts +25 -0
@@ -0,0 +1,88 @@
1
+ import { IconPlug } from '@halo-dev/components'
2
+ import { definePlugin, type AttachmentSelectProvider } from '@halo-dev/ui-shared'
3
+ import { markRaw } from 'vue'
4
+ import PluginTemplateAttachmentTab from './components/PluginTemplateAttachmentTab.vue'
5
+ import PluginTemplateDashboardWidget from './components/PluginTemplateDashboardWidget.vue'
6
+
7
+ import 'element-plus/dist/index.css'
8
+ import 'element-plus/theme-chalk/dark/css-vars.css'
9
+ import './assets/index.css'
10
+ import './assets/element.scss'
11
+
12
+ export default definePlugin({
13
+ components: {},
14
+ routes: [
15
+ {
16
+ parentName: 'Root',
17
+ route: {
18
+ path: '/halo-plugin-template',
19
+ name: 'PluginTemplateConsoleRoot',
20
+ component: () =>
21
+ import(/* webpackChunkName: "PluginTemplateConsoleDashboard" */ './views/console/ConsoleDashboardView.vue'),
22
+ meta: {
23
+ title: '模板工作台',
24
+ searchable: true,
25
+ permissions: ['plugin:halo-plugin-template:view'],
26
+ menu: {
27
+ name: '模板工作台',
28
+ group: '插件模板',
29
+ icon: markRaw(IconPlug),
30
+ priority: 0,
31
+ },
32
+ },
33
+ },
34
+ },
35
+ ],
36
+ ucRoutes: [
37
+ {
38
+ parentName: 'Root',
39
+ route: {
40
+ path: '/halo-plugin-template',
41
+ name: 'PluginTemplateUcRoot',
42
+ component: () =>
43
+ import(/* webpackChunkName: "PluginTemplateUcDashboard" */ './views/uc/UcDashboardView.vue'),
44
+ meta: {
45
+ title: '模板中心',
46
+ searchable: true,
47
+ permissions: ['plugin:halo-plugin-template:uc'],
48
+ },
49
+ },
50
+ },
51
+ ],
52
+ extensionPoints: {
53
+ 'console:dashboard:widgets:create': () => [
54
+ {
55
+ id: 'halo-plugin-template-overview-widget',
56
+ component: markRaw(PluginTemplateDashboardWidget),
57
+ group: 'halo-plugin-template',
58
+ defaultSize: {
59
+ w: 6,
60
+ h: 8,
61
+ minW: 4,
62
+ minH: 5,
63
+ maxW: 12,
64
+ maxH: 12,
65
+ },
66
+ permissions: ['plugin:halo-plugin-template:view'],
67
+ },
68
+ ],
69
+ 'console:dashboard:widgets:internal:quick-action:item:create': () => [
70
+ {
71
+ id: 'halo-plugin-template-open-console',
72
+ icon: markRaw(IconPlug),
73
+ title: '打开模板页',
74
+ action: () => {
75
+ window.location.assign('/console/halo-plugin-template')
76
+ },
77
+ permissions: ['plugin:halo-plugin-template:manage'],
78
+ },
79
+ ],
80
+ 'attachment:selector:create': (): AttachmentSelectProvider[] => [
81
+ {
82
+ id: 'halo-plugin-template-assets',
83
+ label: '模板资源',
84
+ component: markRaw(PluginTemplateAttachmentTab),
85
+ },
86
+ ],
87
+ },
88
+ })
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { buildShellStyles, resolveElementSize } from '@/lib/plugin-ui'
3
+
4
+ describe('plugin-ui helpers', () => {
5
+ it('maps density to element-plus size tokens', () => {
6
+ expect(resolveElementSize('compact')).toBe('small')
7
+ expect(resolveElementSize('balanced')).toBe('default')
8
+ expect(resolveElementSize('relaxed')).toBe('large')
9
+ expect(resolveElementSize(undefined)).toBe('default')
10
+ })
11
+
12
+ it('creates shell style variables only when an accent color is present', () => {
13
+ expect(buildShellStyles('')).toBeUndefined()
14
+ expect(buildShellStyles()).toBeUndefined()
15
+ expect(buildShellStyles('#2457F5')).toEqual({
16
+ '--halo-plugin-template-color-primary': '#2457F5',
17
+ })
18
+ })
19
+ })
@@ -0,0 +1,19 @@
1
+ export const resolveElementSize = (density?: string) => {
2
+ if (density === 'compact') {
3
+ return 'small'
4
+ }
5
+ if (density === 'relaxed') {
6
+ return 'large'
7
+ }
8
+ return 'default'
9
+ }
10
+
11
+ export const buildShellStyles = (accentColor?: string) => {
12
+ if (!accentColor) {
13
+ return undefined
14
+ }
15
+
16
+ return {
17
+ '--halo-plugin-template-color-primary': accentColor,
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { buildAudienceLabel, formatCellValue, toTagType } from './template'
3
+
4
+ describe('template helpers', () => {
5
+ it('maps known audiences to readable labels', () => {
6
+ expect(buildAudienceLabel('console')).toBe('Console')
7
+ expect(buildAudienceLabel('uc')).toBe('User Center')
8
+ expect(buildAudienceLabel('other')).toBe('Unknown')
9
+ })
10
+
11
+ it('normalizes cell values', () => {
12
+ expect(formatCellValue('hello')).toBe('hello')
13
+ expect(formatCellValue(true)).toBe('是')
14
+ expect(formatCellValue(false)).toBe('否')
15
+ expect(formatCellValue('')).toBe('—')
16
+ })
17
+
18
+ it('maps semantic tones to Element Plus tags', () => {
19
+ expect(toTagType('primary')).toBe('primary')
20
+ expect(toTagType('success')).toBe('success')
21
+ expect(toTagType('recommended')).toBe('warning')
22
+ expect(toTagType('unknown')).toBe('info')
23
+ })
24
+ })
@@ -0,0 +1,52 @@
1
+ import type { TagProps } from 'element-plus'
2
+ import type { ResponsiveColumn } from '@/types'
3
+
4
+ export const featureColumns: ResponsiveColumn[] = [
5
+ { key: 'title', label: '能力', minWidth: 180 },
6
+ { key: 'area', label: '区域', minWidth: 120 },
7
+ { key: 'description', label: '说明', minWidth: 280 },
8
+ { key: 'enabled', label: '状态', minWidth: 120, align: 'right' },
9
+ ]
10
+
11
+ export const checklistColumns: ResponsiveColumn[] = [
12
+ { key: 'title', label: '检查项', minWidth: 180 },
13
+ { key: 'audience', label: '受众', minWidth: 120 },
14
+ { key: 'description', label: '说明', minWidth: 260 },
15
+ { key: 'status', label: '状态', minWidth: 120, align: 'right' },
16
+ ]
17
+
18
+ export const toTagType = (tone?: string): TagProps['type'] => {
19
+ switch (tone) {
20
+ case 'primary':
21
+ return 'primary'
22
+ case 'success':
23
+ return 'success'
24
+ case 'warning':
25
+ case 'recommended':
26
+ return 'warning'
27
+ case 'danger':
28
+ return 'danger'
29
+ default:
30
+ return 'info'
31
+ }
32
+ }
33
+
34
+ export const buildAudienceLabel = (audience: string) => {
35
+ if (audience === 'console') {
36
+ return 'Console'
37
+ }
38
+ if (audience === 'uc') {
39
+ return 'User Center'
40
+ }
41
+ return 'Unknown'
42
+ }
43
+
44
+ export const formatCellValue = (value: unknown) => {
45
+ if (value === null || value === undefined || value === '') {
46
+ return '—'
47
+ }
48
+ if (typeof value === 'boolean') {
49
+ return value ? '是' : '否'
50
+ }
51
+ return String(value)
52
+ }
@@ -0,0 +1,31 @@
1
+ import { onBeforeUnmount, onMounted, ref } from 'vue'
2
+
3
+ export const useDocumentTheme = () => {
4
+ const isDark = ref(false)
5
+ let observer: MutationObserver | undefined
6
+
7
+ const sync = () => {
8
+ if (typeof document === 'undefined') {
9
+ isDark.value = false
10
+ return
11
+ }
12
+ isDark.value = document.documentElement.classList.contains('dark')
13
+ }
14
+
15
+ onMounted(() => {
16
+ sync()
17
+ observer = new MutationObserver(sync)
18
+ observer.observe(document.documentElement, {
19
+ attributes: true,
20
+ attributeFilter: ['class'],
21
+ })
22
+ })
23
+
24
+ onBeforeUnmount(() => {
25
+ observer?.disconnect()
26
+ })
27
+
28
+ return {
29
+ isDark,
30
+ }
31
+ }
@@ -0,0 +1,59 @@
1
+ import type {
2
+ PluginTemplateChecklistItem as GeneratedPluginTemplateChecklistItem,
3
+ PluginTemplateFeatureItem as GeneratedPluginTemplateFeatureItem,
4
+ PluginTemplateOverview as GeneratedPluginTemplateOverview,
5
+ PluginTemplateStatItem as GeneratedPluginTemplateStatItem,
6
+ } from '@/api/generated'
7
+
8
+ export interface PluginTemplateStatItem extends GeneratedPluginTemplateStatItem {
9
+ key: string
10
+ label: string
11
+ value: string
12
+ helper: string
13
+ tone: string
14
+ }
15
+
16
+ export interface PluginTemplateFeatureItem extends GeneratedPluginTemplateFeatureItem {
17
+ key: string
18
+ title: string
19
+ area: string
20
+ description: string
21
+ enabled: boolean
22
+ }
23
+
24
+ export interface PluginTemplateChecklistItem extends GeneratedPluginTemplateChecklistItem {
25
+ key: string
26
+ title: string
27
+ description: string
28
+ audience: string
29
+ status: string
30
+ }
31
+
32
+ export interface PluginTemplateOverview extends GeneratedPluginTemplateOverview {
33
+ pluginName: string
34
+ displayName: string
35
+ audience: string
36
+ audienceLabel: string
37
+ consolePath: string
38
+ ucPath: string
39
+ settingName: string
40
+ configMapName: string
41
+ generatedClientPath: string
42
+ generatedAt: string
43
+ enableConsoleDashboard: boolean
44
+ enableUcDashboard: boolean
45
+ enableAttachmentProvider: boolean
46
+ accentColor: string
47
+ density: string
48
+ supportLink: string
49
+ stats: PluginTemplateStatItem[]
50
+ features: PluginTemplateFeatureItem[]
51
+ checklist: PluginTemplateChecklistItem[]
52
+ }
53
+
54
+ export interface ResponsiveColumn {
55
+ key: string
56
+ label: string
57
+ minWidth?: number
58
+ align?: 'left' | 'center' | 'right'
59
+ }
@@ -0,0 +1,7 @@
1
+ <script setup lang="ts">
2
+ import PluginTemplateOverviewPage from '@/components/PluginTemplateOverviewPage.vue'
3
+ </script>
4
+
5
+ <template>
6
+ <PluginTemplateOverviewPage audience="console" />
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <script setup lang="ts">
2
+ import PluginTemplateOverviewPage from '@/components/PluginTemplateOverviewPage.vue'
3
+ </script>
4
+
5
+ <template>
6
+ <PluginTemplateOverviewPage audience="uc" />
7
+ </template>
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
3
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4
+ "exclude": ["src/**/__tests__/*"],
5
+ "compilerOptions": {
6
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
7
+
8
+ "paths": {
9
+ "@/*": ["./src/*"]
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ {
5
+ "path": "./tsconfig.node.json"
6
+ },
7
+ {
8
+ "path": "./tsconfig.app.json"
9
+ },
10
+ {
11
+ "path": "./tsconfig.vitest.json"
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "@tsconfig/node20/tsconfig.json",
3
+ "include": [
4
+ "vite.config.*",
5
+ "vitest.config.*"
6
+ ],
7
+ "compilerOptions": {
8
+ "composite": true,
9
+ "noEmit": true,
10
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
11
+ "module": "ESNext",
12
+ "moduleResolution": "Bundler",
13
+ "types": ["node"]
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.app.json",
3
+ "include": ["src/**/__tests__/*", "env.d.ts"],
4
+ "exclude": [],
5
+ "compilerOptions": {
6
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
7
+
8
+ "lib": [],
9
+ "types": ["node", "jsdom"]
10
+ }
11
+ }
@@ -0,0 +1,25 @@
1
+ import { fileURLToPath, URL } from 'url'
2
+
3
+ import { viteConfig } from '@halo-dev/ui-plugin-bundler-kit'
4
+ import Icons from 'unplugin-icons/vite'
5
+ import { configDefaults } from 'vitest/config'
6
+
7
+ // For more info,
8
+ // please see https://github.com/halo-dev/halo/tree/main/ui/packages/ui-plugin-bundler-kit
9
+ export default viteConfig({
10
+ vite: {
11
+ plugins: [Icons({ compiler: 'vue3' })],
12
+ resolve: {
13
+ alias: {
14
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
15
+ },
16
+ },
17
+
18
+ // If you don't use Vitest, you can remove the following configuration
19
+ test: {
20
+ environment: 'jsdom',
21
+ exclude: [...configDefaults.exclude, 'e2e/**'],
22
+ root: fileURLToPath(new URL('./', import.meta.url)),
23
+ },
24
+ },
25
+ })