@kite-dev/create-plugin-sdk 0.0.1-beta.0 → 0.0.4

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.
package/README.md CHANGED
@@ -1,23 +1,105 @@
1
1
  # Create Kite Plugin
2
2
 
3
- 使用 `@clack/prompts` 提供交互向导,创建可在 Kite 中安装的 React/TypeScript 插件。
3
+ `@kite-dev/create-plugin-sdk` creates a React and TypeScript plugin for Kite. It includes plugin configuration, a page using Kite UI components, and scripts for building an installable archive.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js 20.19 or later in the 20.x series, or Node.js 22.12 or later.
8
+ - npm or pnpm.
9
+ - A Kite instance matching the generated plugin's `engines.kite` range, initially `^0.16.0`.
10
+
11
+ ## Create a plugin
12
+
13
+ With npm:
4
14
 
5
15
  ```sh
6
- npm create @kite-dev/plugin-sdk@beta my-plugin
7
- # 或
8
- pnpm create @kite-dev/plugin-sdk@beta my-plugin
16
+ npm create @kite-dev/plugin-sdk -- my-plugin
9
17
  ```
10
18
 
11
- 省略 `my-plugin` 时交互输入目录。插件 ID 使用目录名,显示名称可在向导中修改。目录必须为空;已有 Git 仓库的 `.git` 目录可以保留。取消向导不会创建文件。
19
+ With pnpm:
20
+
21
+ ```sh
22
+ pnpm create @kite-dev/plugin-sdk my-plugin
23
+ ```
24
+
25
+ These commands run the `@kite-dev/create-plugin-sdk` package. The wizard asks for a display name. Omit the directory argument to choose both the directory and display name interactively.
26
+
27
+ The final directory name becomes the plugin ID. IDs must contain 1–64 lowercase letters, digits, or hyphens, and start and end with a letter or digit. The destination must be empty; an existing `.git` entry is preserved.
12
28
 
13
- 非交互创建:
29
+ ### Non-interactive usage
14
30
 
15
31
  ```sh
16
- npm create @kite-dev/plugin-sdk@beta -- my-plugin --yes --display-name "My Plugin"
17
- pnpm create @kite-dev/plugin-sdk@beta my-plugin --yes --display-name "My Plugin"
32
+ npm create @kite-dev/plugin-sdk -- my-plugin --yes --display-name "My Plugin"
33
+ ```
34
+
35
+ ```sh
36
+ pnpm create @kite-dev/plugin-sdk my-plugin --yes --display-name "My Plugin"
37
+ ```
38
+
39
+ | Argument or option | Description |
40
+ | ----------------------- | ---------------------------------------------------------------------------------- |
41
+ | `[directory]` | Destination path, relative to the current directory or absolute. |
42
+ | `--display-name <name>` | Plugin name shown in Kite. Defaults to the directory name in non-interactive mode. |
43
+ | `-y`, `--yes` | Use defaults without prompting. With no directory argument, creates `my-plugin`. |
44
+ | `-h`, `--help` | Display CLI usage. |
45
+
46
+ The creator writes project files and prints the next steps. It does not install dependencies, initialize Git, build the plugin, or publish a package.
47
+
48
+ ## Generated project
49
+
50
+ ```text
51
+ my-plugin/
52
+ ├── package.json
53
+ ├── plugin.config.tsx
54
+ ├── vite.config.ts
55
+ ├── tsconfig.json
56
+ ├── eslint.config.js
57
+ ├── prettier.config.cjs
58
+ ├── README.md
59
+ ├── .gitignore
60
+ ├── .prettierignore
61
+ └── src/
62
+ ├── i18n.ts
63
+ ├── locales/
64
+ │ ├── en.json
65
+ │ └── zh.json
66
+ └── pages/
67
+ ├── home.tsx
68
+ └── home.module.css
18
69
  ```
19
70
 
20
- 生成 `package.json`、TypeScript/Vite 配置、`plugin.config.tsx`、使用宿主 UI 的首页、CSS Modules、README `.gitignore`。页面使用 `React.lazy`,菜单默认挂在 `core:other`。生成器复用已发布 SDK 的清单校验与依赖版本,不使用宿主源码或本地路径依赖。
71
+ - `package.json` defines the plugin ID, display name, version, supported Kite versions, dependencies, and scripts. The generated `engines.kite` range is `^0.16.0`.
72
+ - `plugin.config.tsx` registers the home route and a sidebar entry under **Other** using `definePlugin`.
73
+ - `src/i18n.ts` binds the English and Chinese dictionaries with `createPluginI18n()` and exports `translations`, `label`, and `useTranslation`.
74
+ - `src/locales/en.json` and `src/locales/zh.json` contain navigation and page text. The home label initially uses the chosen display name in both languages.
75
+ - `src/pages/home.tsx` displays Kite's selected cluster and namespace using shared UI components and localized text.
76
+ - `src/pages/home.module.css` contains styles scoped to the page.
77
+ - `vite.config.ts` uses the SDK's build configuration to produce `dist/` and `plugin.json`.
78
+ - `eslint.config.js` checks JavaScript, TypeScript, and React Hooks. `prettier.config.cjs` defines formatting and import ordering.
79
+ - `README.md` starts with the plugin's display name as its heading.
80
+
81
+ Set `package.json.engines.kite` to the Kite versions your plugin supports. The SDK defaults this range to `^0.16.0` if omitted and includes it in the built manifest as `requires.kite`. Kite checks this requirement when managing and loading the plugin.
82
+
83
+ The home page is lazy-loaded. Keep page CSS and browser-specific dependencies in page modules; the plugin configuration is also executed in Node.js to generate navigation metadata.
84
+
85
+ ## Localize a plugin
86
+
87
+ Add matching translation keys to `src/locales/en.json` and `src/locales/zh.json`. The generated configuration registers `i18n: translations` and uses `label('navigation.home')` for its route title and menu label.
88
+
89
+ Inside a page, import the generated hook:
90
+
91
+ ```tsx
92
+ import { useTranslation } from '../i18n'
93
+
94
+ export function ContextHeading() {
95
+ const { t } = useTranslation()
96
+ return <h2>{t('context.description')}</h2>
97
+ }
98
+ ```
99
+
100
+ Both `label()` and `t()` provide completion for your locale keys. Use `t('key', { name })` for a message containing `{{name}}`. The hook also returns the current `language`. Navigation and page text follow Kite's language selection; namespace registration is handled by Kite.
101
+
102
+ ## Build and install
21
103
 
22
104
  ```sh
23
105
  cd my-plugin
@@ -26,10 +108,89 @@ pnpm run build
26
108
  pnpm run pack
27
109
  ```
28
110
 
29
- 然后从 Kite 的插件管理页面上传安装包。`dev` 执行监听构建,页面在 Kite 中运行。
111
+ You can use `npm install`, `npm run build`, and `npm run pack` instead.
112
+
113
+ The package is written to `<id>-<version>.tar.gz` in the plugin directory. In Kite, open the avatar menu, select **Plugin management**, and use **Install from file** to upload it. Plugin installation requires a Kite administrator. Once installed, open the plugin from **Other** in the sidebar.
114
+
115
+ | Script | Action |
116
+ | -------------- | ---------------------------------------------------------- |
117
+ | `type-check` | Check TypeScript without producing output. |
118
+ | `build` | Type-check and build the plugin into `dist/`. |
119
+ | `dev` | Watch source files and rebuild `dist/`. |
120
+ | `pack` | Package the existing `dist/` directory. Run `build` first. |
121
+ | `lint` | Check JavaScript, TypeScript, and React Hooks with ESLint. |
122
+ | `lint:fix` | Apply automatic ESLint fixes. |
123
+ | `format` | Format source files and sort imports with Prettier. |
124
+ | `format:check` | Check formatting without changing files. |
125
+
126
+ Plugin pages run inside Kite. Watch mode does not serve a standalone application or install rebuilt files. Increment `package.json.version`, rebuild, and install a new package to update a plugin. Restart the watch command after changing its ID or version.
30
127
 
31
- 需要 Node.js `^20.19.0 || >=22.12.0`。npm/pnpm `create @kite-dev/plugin-sdk` 会自动寻找本包 `@kite-dev/create-plugin-sdk`;当前使用 `beta` 标签。
128
+ See the [SDK documentation](../README.md) for resource APIs, navigation, components, styling, and plugin configuration.
32
129
 
33
- ## 本地开发与发布
130
+ ## Create a plugin in a pnpm workspace
131
+
132
+ From a workspace whose `pnpm-workspace.yaml` includes `plugins/*`:
133
+
134
+ ```sh
135
+ pnpm create @kite-dev/plugin-sdk plugins/my-plugin
136
+ pnpm install
137
+ pnpm --filter my-plugin run build
138
+ pnpm --filter my-plugin run pack
139
+ ```
140
+
141
+ The creator writes into the selected directory. Workspace package discovery and dependency installation are managed by pnpm.
142
+
143
+ The creator detects the nearest ancestor containing `pnpm-workspace.yaml` or `package.json.workspaces`. If that root already provides an ESLint or Prettier configuration, the new plugin reuses the corresponding root configuration and dependencies. Each tool is detected independently; missing tooling is included with the new plugin. The creator does not change workspace configuration.
144
+
145
+ For shared tooling, run the workspace's quality commands from its root so its file patterns and ignore rules apply consistently:
146
+
147
+ ```sh
148
+ pnpm run lint
149
+ pnpm run format:check
150
+ ```
151
+
152
+ The plugin also includes `lint`, `lint:fix`, `format`, and `format:check` scripts. An npm workspace uses the same configuration reuse behavior.
153
+
154
+ ## Develop the creator locally
155
+
156
+ From the `kite-plugin-sdk` repository root:
157
+
158
+ ```sh
159
+ pnpm install --frozen-lockfile
160
+ pnpm run build
161
+ cd create-plugin-sdk
162
+ pnpm run dev ../../my-plugin --yes --display-name "My Plugin"
163
+ ```
164
+
165
+ The destination in this example is a new `my-plugin` directory beside the SDK repository. Choose an empty destination for each run. `pnpm run dev` executes `index.js` directly, so edits to the CLI or `template/` are used on the next invocation.
166
+
167
+ To build the generated plugin against the same SDK checkout:
168
+
169
+ ```sh
170
+ cd ../../my-plugin
171
+ pnpm install
172
+ pnpm add @kite-dev/plugin-sdk@file:../kite-plugin-sdk
173
+ pnpm run build
174
+ pnpm run pack
175
+ ```
176
+
177
+ For breakpoint debugging, run this from `create-plugin-sdk/`:
178
+
179
+ ```sh
180
+ node --inspect-brk index.js ../../my-plugin
181
+ ```
182
+
183
+ Attach your debugger to the Node.js process. Running the local entry point uses your source checkout; `npm create` and `pnpm create` use the published creator.
184
+
185
+ ## Releases
186
+
187
+ The creator and SDK share the same version and are released together. The pnpm workspace links the creator's `workspace:*` dependency to the local SDK checkout during development. `pnpm pack` converts it to the exact SDK version in the published package.
188
+
189
+ From the repository root, with all changes committed:
190
+
191
+ ```sh
192
+ ./scripts/release.sh 0.0.4
193
+ git push --atomic origin HEAD v0.0.4
194
+ ```
34
195
 
35
- 在此目录执行 `npm ci`,然后运行 `node index.js <directory>`。使用 `npm pack` 检查发布包,发布测试版使用 `npm publish --tag beta --access public`。创建器与 SDK 位于同一仓库,分别作为 npm 包发布。
196
+ The script updates both package versions and the shared lockfile, then creates a release commit and tag. Pushing the tag triggers `publish.yml`, which packs both packages with pnpm and publishes the SDK followed by the creator. Stable versions use the `latest` npm tag; prerelease versions use `beta`.
package/index.js CHANGED
@@ -1,6 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, renameSync, writeFileSync } from 'node:fs'
3
- import { basename, resolve } from 'node:path'
2
+ import {
3
+ cpSync,
4
+ existsSync,
5
+ lstatSync,
6
+ mkdirSync,
7
+ readdirSync,
8
+ readFileSync,
9
+ renameSync,
10
+ writeFileSync,
11
+ } from 'node:fs'
12
+ import { basename, dirname, resolve } from 'node:path'
4
13
  import { fileURLToPath } from 'node:url'
5
14
  import { parseArgs } from 'node:util'
6
15
  import * as prompts from '@clack/prompts'
@@ -15,8 +24,8 @@ Options:
15
24
  -h, --help Show this help
16
25
 
17
26
  Examples:
18
- npm create @kite-dev/plugin-sdk@beta my-plugin
19
- pnpm create @kite-dev/plugin-sdk@beta my-plugin`
27
+ npm create @kite-dev/plugin-sdk my-plugin
28
+ pnpm create @kite-dev/plugin-sdk my-plugin`
20
29
 
21
30
  function metadataError(id, name) {
22
31
  try {
@@ -25,7 +34,7 @@ function metadataError(id, name) {
25
34
  id,
26
35
  name,
27
36
  version: '0.1.0',
28
- requires: { sdk: `^${sdk.version}` },
37
+ requires: { kite: '^0.16.0' },
29
38
  entry: 'mf-manifest.json',
30
39
  module: './plugin',
31
40
  routes: [],
@@ -41,8 +50,55 @@ function directoryError(directory) {
41
50
  const error = metadataError(basename(target), 'Plugin')
42
51
  if (error) return error
43
52
  if (existsSync(target)) {
44
- if (!lstatSync(target).isDirectory()) return 'The target path must be a directory.'
45
- if (readdirSync(target).some((entry) => entry !== '.git')) return 'The target directory is not empty. Choose an empty directory.'
53
+ if (!lstatSync(target).isDirectory())
54
+ return 'The target path must be a directory.'
55
+ if (readdirSync(target).some((entry) => entry !== '.git'))
56
+ return 'The target directory is not empty. Choose an empty directory.'
57
+ }
58
+ }
59
+
60
+ function workspaceTooling(target) {
61
+ let directory = dirname(target)
62
+ while (true) {
63
+ const packagePath = resolve(directory, 'package.json')
64
+ const metadata = existsSync(packagePath)
65
+ ? JSON.parse(readFileSync(packagePath, 'utf8'))
66
+ : {}
67
+ if (
68
+ existsSync(resolve(directory, 'pnpm-workspace.yaml')) ||
69
+ Array.isArray(metadata.workspaces) ||
70
+ Array.isArray(metadata.workspaces?.packages)
71
+ ) {
72
+ const eslint = ['js', 'mjs', 'cjs', 'ts', 'mts', 'cts'].some(
73
+ (extension) =>
74
+ existsSync(resolve(directory, `eslint.config.${extension}`))
75
+ )
76
+ const prettier =
77
+ metadata.prettier !== undefined ||
78
+ [
79
+ '.prettierrc',
80
+ ...[
81
+ 'json',
82
+ 'json5',
83
+ 'yaml',
84
+ 'yml',
85
+ 'toml',
86
+ 'js',
87
+ 'cjs',
88
+ 'mjs',
89
+ 'ts',
90
+ 'cts',
91
+ 'mts',
92
+ ].map((extension) => `.prettierrc.${extension}`),
93
+ ...['js', 'cjs', 'mjs', 'ts', 'cts', 'mts'].map(
94
+ (extension) => `prettier.config.${extension}`
95
+ ),
96
+ ].some((file) => existsSync(resolve(directory, file)))
97
+ return { eslint, prettier }
98
+ }
99
+ const parent = dirname(directory)
100
+ if (parent === directory) return { eslint: false, prettier: false }
101
+ directory = parent
46
102
  }
47
103
  }
48
104
 
@@ -62,7 +118,9 @@ async function main() {
62
118
  if (positionals.length > 1) throw new Error(usage)
63
119
  const interactive = process.stdin.isTTY && process.stdout.isTTY && !values.yes
64
120
  if (!interactive && !positionals[0] && !values.yes) {
65
- throw new Error('Specify a project directory or use --yes to create my-plugin.')
121
+ throw new Error(
122
+ 'Specify a project directory or use --yes to create my-plugin.'
123
+ )
66
124
  }
67
125
  if (positionals[0]) {
68
126
  const error = directoryError(positionals[0])
@@ -70,27 +128,41 @@ async function main() {
70
128
  }
71
129
 
72
130
  prompts.intro('Create a Kite plugin')
73
- const answers = await prompts.group({
74
- directory: async () => positionals[0] ?? (interactive
75
- ? prompts.text({
76
- message: 'Project directory',
77
- initialValue: 'my-plugin',
78
- validate: (value) => value?.trim() ? directoryError(value.trim()) : 'Enter a project directory.',
79
- })
80
- : 'my-plugin'),
81
- displayName: async ({ results }) => values['display-name'] ?? (interactive
82
- ? prompts.text({
83
- message: 'Plugin display name',
84
- initialValue: basename(resolve(results.directory.trim())),
85
- validate: (value) => metadataError(basename(resolve(results.directory.trim())), value?.trim()),
86
- })
87
- : basename(resolve(results.directory.trim()))),
88
- }, {
89
- onCancel: () => {
90
- prompts.cancel('Plugin creation cancelled.')
91
- process.exit(0)
131
+ const answers = await prompts.group(
132
+ {
133
+ directory: async () =>
134
+ positionals[0] ??
135
+ (interactive
136
+ ? prompts.text({
137
+ message: 'Project directory',
138
+ initialValue: 'my-plugin',
139
+ validate: (value) =>
140
+ value?.trim()
141
+ ? directoryError(value.trim())
142
+ : 'Enter a project directory.',
143
+ })
144
+ : 'my-plugin'),
145
+ displayName: async ({ results }) =>
146
+ values['display-name'] ??
147
+ (interactive
148
+ ? prompts.text({
149
+ message: 'Plugin display name',
150
+ initialValue: basename(resolve(results.directory.trim())),
151
+ validate: (value) =>
152
+ metadataError(
153
+ basename(resolve(results.directory.trim())),
154
+ value?.trim()
155
+ ),
156
+ })
157
+ : basename(resolve(results.directory.trim()))),
92
158
  },
93
- })
159
+ {
160
+ onCancel: () => {
161
+ prompts.cancel('Plugin creation cancelled.')
162
+ process.exit(0)
163
+ },
164
+ }
165
+ )
94
166
 
95
167
  const directory = answers.directory.trim()
96
168
  const target = resolve(directory)
@@ -99,6 +171,21 @@ async function main() {
99
171
  const error = directoryError(directory) ?? metadataError(id, displayName)
100
172
  if (error) throw new Error(error)
101
173
 
174
+ const sharedTooling = workspaceTooling(target)
175
+ const toolingDependencies = [
176
+ ...(sharedTooling.eslint
177
+ ? []
178
+ : [
179
+ '@eslint/js',
180
+ 'eslint',
181
+ 'eslint-plugin-react-hooks',
182
+ 'globals',
183
+ 'typescript-eslint',
184
+ ]),
185
+ ...(sharedTooling.prettier
186
+ ? []
187
+ : ['@ianvs/prettier-plugin-sort-imports', 'prettier']),
188
+ ]
102
189
  const metadata = {
103
190
  name: id,
104
191
  displayName,
@@ -107,11 +194,16 @@ async function main() {
107
194
  type: 'module',
108
195
  license: 'Apache-2.0',
109
196
  description: '',
197
+ engines: { kite: '^0.16.0' },
110
198
  scripts: {
111
199
  'type-check': 'tsc --noEmit',
112
200
  build: 'tsc --noEmit && vite build',
113
201
  dev: 'vite build --watch',
114
202
  pack: 'kite-plugin pack',
203
+ lint: 'eslint .',
204
+ 'lint:fix': 'eslint . --fix',
205
+ format: 'prettier --write .',
206
+ 'format:check': 'prettier --check .',
115
207
  },
116
208
  dependencies: {
117
209
  [sdk.name]: sdk.version,
@@ -125,6 +217,9 @@ async function main() {
125
217
  '@types/react-dom': sdk.devDependencies['@types/react-dom'],
126
218
  typescript: sdk.devDependencies.typescript,
127
219
  vite: sdk.peerDependencies.vite,
220
+ ...Object.fromEntries(
221
+ toolingDependencies.map((name) => [name, sdk.devDependencies[name]])
222
+ ),
128
223
  },
129
224
  }
130
225
  mkdirSync(target, { recursive: true })
@@ -132,18 +227,50 @@ async function main() {
132
227
  recursive: true,
133
228
  force: false,
134
229
  errorOnExist: true,
230
+ filter: (source) =>
231
+ !(sharedTooling.eslint && basename(source) === 'eslint.config.js') &&
232
+ !(
233
+ sharedTooling.prettier &&
234
+ ['prettier.config.cjs', '_prettierignore'].includes(basename(source))
235
+ ),
135
236
  })
136
237
  renameSync(resolve(target, '_gitignore'), resolve(target, '.gitignore'))
137
- writeFileSync(resolve(target, 'package.json'), `${JSON.stringify(metadata, null, 2)}\n`, { flag: 'wx' })
238
+ if (!sharedTooling.prettier) {
239
+ renameSync(
240
+ resolve(target, '_prettierignore'),
241
+ resolve(target, '.prettierignore')
242
+ )
243
+ }
244
+ for (const language of ['en', 'zh']) {
245
+ const localePath = resolve(target, 'src/locales', `${language}.json`)
246
+ const dictionary = JSON.parse(readFileSync(localePath, 'utf8'))
247
+ dictionary.navigation.home = displayName
248
+ writeFileSync(localePath, `${JSON.stringify(dictionary, null, 2)}\n`)
249
+ }
250
+ writeFileSync(
251
+ resolve(target, 'package.json'),
252
+ `${JSON.stringify(metadata, null, 2)}\n`,
253
+ { flag: 'wx' }
254
+ )
255
+ writeFileSync(resolve(target, 'README.md'), `# ${displayName}\n`, {
256
+ flag: 'wx',
257
+ })
138
258
 
139
- const manager = process.env.npm_config_user_agent?.startsWith('pnpm/') ? 'pnpm' : 'npm'
140
- prompts.note([
141
- `cd '${directory.replaceAll("'", "'\\''")}'`,
142
- `${manager} install`,
143
- `${manager} run build`,
144
- `${manager} run pack`,
145
- ].join('\n'), 'Next steps')
146
- prompts.outro(`Created ${displayName}. Upload the built archive in Kite → Plugin management.`)
259
+ const manager = process.env.npm_config_user_agent?.startsWith('pnpm/')
260
+ ? 'pnpm'
261
+ : 'npm'
262
+ prompts.note(
263
+ [
264
+ `cd '${directory.replaceAll("'", "'\\''")}'`,
265
+ `${manager} install`,
266
+ `${manager} run build`,
267
+ `${manager} run pack`,
268
+ ].join('\n'),
269
+ 'Next steps'
270
+ )
271
+ prompts.outro(
272
+ `Created ${displayName}. Upload the built archive in Kite → Plugin management.`
273
+ )
147
274
  }
148
275
 
149
276
  main().catch((error) => {
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@kite-dev/create-plugin-sdk",
3
- "version": "0.0.1-beta.0",
3
+ "version": "0.0.4",
4
4
  "description": "Create a Kite frontend plugin",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/kite-org/plugin-sdk.git",
10
+ "directory": "create-plugin-sdk"
11
+ },
7
12
  "engines": {
8
13
  "node": "^20.19.0 || >=22.12.0"
9
14
  },
@@ -17,11 +22,13 @@
17
22
  ],
18
23
  "dependencies": {
19
24
  "@clack/prompts": "1.8.0",
20
- "@kite-dev/plugin-sdk": "0.0.1-beta.0"
25
+ "@kite-dev/plugin-sdk": "0.0.4"
21
26
  },
22
27
  "publishConfig": {
23
28
  "registry": "https://registry.npmjs.org/",
24
- "access": "public",
25
- "tag": "beta"
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "dev": "node index.js"
26
33
  }
27
- }
34
+ }
@@ -0,0 +1,8 @@
1
+ **/node_modules/
2
+ **/dist/
3
+ **/.vite/
4
+ **/__mf__virtual/
5
+ *.tgz
6
+ *.tar.gz
7
+ package-lock.json
8
+ pnpm-lock.yaml
@@ -0,0 +1,30 @@
1
+ import js from '@eslint/js'
2
+ import reactHooks from 'eslint-plugin-react-hooks'
3
+ import globals from 'globals'
4
+ import tseslint from 'typescript-eslint'
5
+
6
+ export default tseslint.config(
7
+ {
8
+ ignores: [
9
+ '**/node_modules/**',
10
+ '**/dist/**',
11
+ '**/.vite/**',
12
+ '**/__mf__virtual/**',
13
+ ],
14
+ },
15
+ js.configs.recommended,
16
+ ...tseslint.configs.recommended,
17
+ {
18
+ files: ['src/**/*.{ts,tsx}', 'plugin.config.tsx'],
19
+ languageOptions: { globals: globals.browser },
20
+ plugins: { 'react-hooks': reactHooks },
21
+ rules: {
22
+ 'react-hooks/rules-of-hooks': 'error',
23
+ 'react-hooks/exhaustive-deps': 'warn',
24
+ },
25
+ },
26
+ {
27
+ files: ['vite.config.ts', 'eslint.config.js', 'prettier.config.cjs'],
28
+ languageOptions: { globals: globals.node },
29
+ }
30
+ )
@@ -1,18 +1,25 @@
1
1
  import { lazy } from 'react'
2
2
  import { definePlugin } from '@kite-dev/plugin-sdk'
3
- import { displayName } from './package.json'
3
+
4
+ import { label, translations } from './src/i18n'
4
5
 
5
6
  const HomePage = lazy(() => import('./src/pages/home'))
6
7
 
7
8
  export default definePlugin({
9
+ i18n: translations,
8
10
  routes: [
9
- { id: 'home', path: '', title: displayName, element: <HomePage /> },
11
+ {
12
+ id: 'home',
13
+ path: '',
14
+ title: label('navigation.home'),
15
+ element: <HomePage />,
16
+ },
10
17
  ],
11
18
  menus: [
12
19
  {
13
20
  id: 'home',
14
21
  parent: 'core:other',
15
- label: displayName,
22
+ label: label('navigation.home'),
16
23
  route: 'home',
17
24
  icon: 'IconBox',
18
25
  },
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ endOfLine: 'lf',
3
+ semi: false,
4
+ singleQuote: true,
5
+ tabWidth: 2,
6
+ trailingComma: 'es5',
7
+ importOrder: ['^react(?:/.*)?$', '<THIRD_PARTY_MODULES>', '', '^[./]'],
8
+ importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
9
+ plugins: ['@ianvs/prettier-plugin-sort-imports'],
10
+ }
@@ -0,0 +1,10 @@
1
+ import { createPluginI18n } from '@kite-dev/plugin-sdk/i18n'
2
+
3
+ import en from './locales/en.json'
4
+ import zh from './locales/zh.json'
5
+
6
+ export const {
7
+ resources: translations,
8
+ label,
9
+ useTranslation,
10
+ } = createPluginI18n({ en, zh })
@@ -0,0 +1,11 @@
1
+ {
2
+ "navigation": {
3
+ "home": "My Plugin"
4
+ },
5
+ "context": {
6
+ "description": "Current Kite context",
7
+ "cluster": "Cluster",
8
+ "namespace": "Namespace",
9
+ "allNamespaces": "All namespaces"
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "navigation": {
3
+ "home": "My Plugin"
4
+ },
5
+ "context": {
6
+ "description": "当前 Kite 上下文",
7
+ "cluster": "集群",
8
+ "namespace": "命名空间",
9
+ "allNamespaces": "全部命名空间"
10
+ }
11
+ }
@@ -1,27 +1,35 @@
1
- import { useCluster, useNamespace } from '@kite-dev/plugin-sdk'
2
- import { usePluginTranslation } from '@kite-dev/plugin-sdk/i18n'
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@kite-dev/plugin-sdk/ui'
4
- import { displayName } from '../../package.json'
1
+ import { useCluster, useNamespace } from '@kite-dev/plugin-sdk/hooks'
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@kite-dev/plugin-sdk/ui'
9
+
10
+ import { useTranslation } from '../i18n'
5
11
  import styles from './home.module.css'
6
12
 
7
13
  export default function HomePage() {
8
14
  const { currentCluster } = useCluster()
9
15
  const { namespace } = useNamespace()
10
- const { t } = usePluginTranslation()
16
+ const { t } = useTranslation()
11
17
 
12
18
  return (
13
19
  <main className={styles.page}>
14
20
  <Card>
15
21
  <CardHeader>
16
- <CardTitle>{displayName}</CardTitle>
17
- <CardDescription>{t({ en: 'Current Kite context', zh: '当前 Kite 上下文' })}</CardDescription>
22
+ <CardTitle>{t('navigation.home')}</CardTitle>
23
+ <CardDescription>{t('context.description')}</CardDescription>
18
24
  </CardHeader>
19
25
  <CardContent>
20
26
  <dl className={styles.context}>
21
- <dt>{t({ en: 'Cluster', zh: '集群' })}</dt>
27
+ <dt>{t('context.cluster')}</dt>
22
28
  <dd>{currentCluster ?? '—'}</dd>
23
- <dt>{t({ en: 'Namespace', zh: '命名空间' })}</dt>
24
- <dd>{namespace === '_all' ? t({ en: 'All namespaces', zh: '全部命名空间' }) : namespace}</dd>
29
+ <dt>{t('context.namespace')}</dt>
30
+ <dd>
31
+ {namespace === '_all' ? t('context.allNamespaces') : namespace}
32
+ </dd>
25
33
  </dl>
26
34
  </CardContent>
27
35
  </Card>
@@ -1,4 +1,4 @@
1
- import { defineConfig } from 'vite'
2
1
  import { kitePlugin } from '@kite-dev/plugin-sdk/vite'
2
+ import { defineConfig } from 'vite'
3
3
 
4
4
  export default defineConfig(kitePlugin())
@@ -1,20 +0,0 @@
1
- # Kite Plugin
2
-
3
- 在生成的插件目录安装依赖、构建并打包:
4
-
5
- ```sh
6
- npm install
7
- npm run build
8
- npm run pack
9
- ```
10
-
11
- 打包后得到 `<插件 ID>-<版本>.tar.gz`。在 Kite 头像菜单进入「插件」,上传安装包;启用后可在「其他」分组打开插件首页。
12
-
13
- - `package.json`:插件 ID、展示名称、版本和依赖。首页标题及菜单名称复用 `displayName`。
14
- - `plugin.config.tsx`:使用 `definePlugin` 注册路由和菜单,直接绑定 JSX。
15
- - `src/pages/home.tsx`:首页复用宿主 UI、语言、集群和命名空间上下文。
16
- - `src/pages/home.module.css`:插件自身布局样式。
17
-
18
- SDK 构建时自动生成导航清单;页面使用 `React.lazy`,首次访问时才加载。配置需要能在 Node 中执行,CSS、浏览器 API 和页面依赖留在 lazy 导入的页面模块中。
19
-
20
- 插件需要在兼容的 Kite 宿主中运行,没有独立的开发网页。修改后重新构建、打包并安装;更新已安装插件时先递增 `package.json` 的版本。