@meith/cli 0.27.0 → 0.28.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.
- package/package.json +13 -13
- package/src/index.ts +1 -1
- package/src/plugin-manifest.ts +233 -46
- package/src/upgrade.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"description": "The operator CLI: migrations, backup and restore, imports, users and settings — and the meith bin that runs it against an external board workspace.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,18 +23,18 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"tsx": "^4.23.12",
|
|
26
|
-
"@meith/accounts": "0.
|
|
27
|
-
"@meith/core": "0.
|
|
28
|
-
"@meith/db": "0.
|
|
29
|
-
"@meith/demo": "0.
|
|
30
|
-
"@meith/drivers": "0.
|
|
31
|
-
"@meith/forums": "0.
|
|
32
|
-
"@meith/plugin-kit": "0.
|
|
33
|
-
"@meith/profile-fields": "0.
|
|
34
|
-
"@meith/runtime": "0.
|
|
35
|
-
"@meith/settings": "0.
|
|
36
|
-
"@meith/tasks": "0.
|
|
37
|
-
"create-meith": "0.
|
|
26
|
+
"@meith/accounts": "0.28.1",
|
|
27
|
+
"@meith/core": "0.28.1",
|
|
28
|
+
"@meith/db": "0.28.1",
|
|
29
|
+
"@meith/demo": "0.28.1",
|
|
30
|
+
"@meith/drivers": "0.28.1",
|
|
31
|
+
"@meith/forums": "0.28.1",
|
|
32
|
+
"@meith/plugin-kit": "0.28.1",
|
|
33
|
+
"@meith/profile-fields": "0.28.1",
|
|
34
|
+
"@meith/runtime": "0.28.1",
|
|
35
|
+
"@meith/settings": "0.28.1",
|
|
36
|
+
"@meith/tasks": "0.28.1",
|
|
37
|
+
"create-meith": "0.28.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"esbuild": "^0.28.2"
|
package/src/index.ts
CHANGED
|
@@ -211,7 +211,7 @@ const commands: Command[] = [
|
|
|
211
211
|
|
|
212
212
|
{
|
|
213
213
|
name: 'plugin:add',
|
|
214
|
-
summary: '
|
|
214
|
+
summary: 'Install a plugin, add it to board.plugins.json, and regenerate meith.plugins.ts.',
|
|
215
215
|
usage: 'meith plugin:add <package> [--key <key>] [--disabled]',
|
|
216
216
|
async run(args: readonly string[]) {
|
|
217
217
|
const { pluginAdd } = await import('./plugin-manifest')
|
package/src/plugin-manifest.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process'
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
3
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
import { fileURLToPath } from 'node:url'
|
|
5
6
|
|
|
6
7
|
import { ValidationError } from '@meith/core'
|
|
7
8
|
|
|
8
|
-
import BOARDS_JSON from '../../../scripts/boards.json'
|
|
9
9
|
import { optional, parseFlags } from './args'
|
|
10
10
|
|
|
11
11
|
function repoRoot(): string {
|
|
12
12
|
return fileURLToPath(new URL('../../../', import.meta.url))
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function generatorScript(): string {
|
|
16
|
-
return join(repoRoot(), 'scripts/board-plugins-gen.mjs')
|
|
17
|
-
}
|
|
18
|
-
|
|
19
15
|
interface Board {
|
|
20
16
|
readonly manifestFile: string
|
|
21
17
|
readonly packageFile: string
|
|
@@ -24,12 +20,6 @@ interface Board {
|
|
|
24
20
|
readonly filterName: string
|
|
25
21
|
}
|
|
26
22
|
|
|
27
|
-
const BOARDS = BOARDS_JSON as readonly Board[]
|
|
28
|
-
|
|
29
|
-
function boardsRoot(): string {
|
|
30
|
-
return process.env.MEITH_BOARD_PLUGINS_ROOT ?? repoRoot()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
23
|
interface ManifestEntry {
|
|
34
24
|
readonly key: string
|
|
35
25
|
readonly package: string
|
|
@@ -40,20 +30,212 @@ interface Manifest {
|
|
|
40
30
|
readonly plugins: readonly ManifestEntry[]
|
|
41
31
|
}
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
interface GeneratorResult {
|
|
34
|
+
readonly ok: boolean
|
|
35
|
+
readonly output: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface Mode {
|
|
39
|
+
readonly root: string
|
|
40
|
+
readonly boards: readonly Board[]
|
|
41
|
+
assertEditable(): void
|
|
42
|
+
installPackage(packageName: string): void
|
|
43
|
+
regenerate(): GeneratorResult
|
|
44
|
+
missingManifest(path: string): string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function assertBoardCheckout(root: string): void {
|
|
48
|
+
if (existsSync(join(root, '.git'))) return
|
|
49
|
+
throw new ValidationError(
|
|
50
|
+
'plugin:add and plugin:remove edit this board’s source — package.json, board.plugins.json ' +
|
|
51
|
+
'and meith.plugins.ts — and only take effect when the image is rebuilt. This is not your ' +
|
|
52
|
+
'board’s git checkout (there is no .git here), which means it is most likely the deployed ' +
|
|
53
|
+
'container: a change made here would not rebuild anything and would be discarded on the next ' +
|
|
54
|
+
'redeploy. Run it where you edit and commit the board, then push and redeploy.',
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function installBoardPackage(root: string, packageName: string): void {
|
|
59
|
+
try {
|
|
60
|
+
execFileSync('npm', ['install', '--save-exact', packageName], { cwd: root, stdio: 'inherit' })
|
|
61
|
+
} catch {
|
|
62
|
+
throw new ValidationError(
|
|
63
|
+
`Could not install ${packageName} — npm reported the error above. Fix that and rerun.`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const BOARD_MODE_BOARD: Board = {
|
|
69
|
+
manifestFile: 'board.plugins.json',
|
|
70
|
+
packageFile: 'package.json',
|
|
71
|
+
outputFile: 'meith.plugins.ts',
|
|
72
|
+
packageLabel: 'this board',
|
|
73
|
+
filterName: '',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function monorepoMode(boardsFile: string): Mode {
|
|
77
|
+
const boards = JSON.parse(readFileSync(boardsFile, 'utf8')) as readonly Board[]
|
|
78
|
+
const root = process.env.MEITH_BOARD_PLUGINS_ROOT ?? repoRoot()
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
root,
|
|
82
|
+
boards,
|
|
83
|
+
assertEditable: () => {},
|
|
84
|
+
installPackage: () => {},
|
|
85
|
+
regenerate: () => runGenerator(join(repoRoot(), 'scripts/board-plugins-gen.mjs')),
|
|
86
|
+
missingManifest: (path) =>
|
|
87
|
+
`${path} does not exist. This command edits source files for every board this ` +
|
|
88
|
+
'repository carries — apps/community and boards/stock — and reruns the generator, ' +
|
|
89
|
+
'so it needs a checkout of the repository, not the deployed image — run it where you ' +
|
|
90
|
+
'would run `pnpm add`, commit both board.plugins.json files and both ' +
|
|
91
|
+
'meith.plugins.ts files, then rebuild and redeploy.',
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function boardMode(): Mode {
|
|
96
|
+
const root = process.env.MEITH_BOARD_PLUGINS_ROOT ?? process.cwd()
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
root,
|
|
100
|
+
boards: [BOARD_MODE_BOARD],
|
|
101
|
+
assertEditable: () => assertBoardCheckout(root),
|
|
102
|
+
installPackage: (packageName) => installBoardPackage(root, packageName),
|
|
103
|
+
regenerate: () => regenerateBoard(root),
|
|
104
|
+
missingManifest: (path) =>
|
|
105
|
+
`${path} does not exist. Run this from the board create-meith scaffolded — the ` +
|
|
106
|
+
'directory with meith.config.ts and board.plugins.json in it.',
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolveMode(): Mode {
|
|
111
|
+
const boardsFile = join(repoRoot(), 'scripts/boards.json')
|
|
112
|
+
return existsSync(boardsFile) ? monorepoMode(boardsFile) : boardMode()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const PLUGIN_KEY_PATTERN = /^[a-z][a-z0-9-]{1,39}$/
|
|
116
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
117
|
+
const NPM_PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/
|
|
118
|
+
|
|
119
|
+
function toIdentifier(key: string): string {
|
|
120
|
+
return key.replace(/-([a-z0-9])/g, (_match, char: string) => char.toUpperCase())
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function dependencyNames(packageFile: string): Set<string> {
|
|
124
|
+
const pkg = JSON.parse(readFileSync(packageFile, 'utf8')) as {
|
|
125
|
+
dependencies?: Record<string, string>
|
|
126
|
+
}
|
|
127
|
+
return new Set(Object.keys(pkg.dependencies ?? {}))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function validateBoardManifest(
|
|
131
|
+
plugins: readonly ManifestEntry[],
|
|
132
|
+
dependencies: ReadonlySet<string>,
|
|
133
|
+
): void {
|
|
134
|
+
const identifiers = new Map<string, string>()
|
|
135
|
+
|
|
136
|
+
for (const entry of plugins) {
|
|
137
|
+
if (!PLUGIN_KEY_PATTERN.test(entry.key)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`"${entry.key}" is not a valid plugin key. It must be lower-case letters, digits and ` +
|
|
140
|
+
'hyphens, start with a letter, and be 2–40 characters long.',
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const identifier = toIdentifier(entry.key)
|
|
145
|
+
if (!IDENTIFIER_PATTERN.test(identifier)) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`"${entry.key}" is a valid key, but the identifier meith.plugins.ts would bind for it, ` +
|
|
148
|
+
`"${identifier}", is not a valid one — each hyphen must be followed by a letter or digit.`,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const collision = identifiers.get(identifier)
|
|
153
|
+
if (collision !== undefined) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`"${entry.key}" and "${collision}" both generate the identifier "${identifier}". ` +
|
|
156
|
+
'Rename one so the generated imports do not collide.',
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
identifiers.set(identifier, entry.key)
|
|
160
|
+
|
|
161
|
+
if (!NPM_PACKAGE_NAME_PATTERN.test(entry.package) || entry.package.length > 214) {
|
|
162
|
+
throw new Error(`"${entry.package}" (key "${entry.key}") is not a valid npm package name.`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!dependencies.has(entry.package)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`"${entry.package}" (key "${entry.key}") is not installed. Run ` +
|
|
168
|
+
`\`npm install ${entry.package}\` first, then rerun this.`,
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const BOARD_HEADER = `// Generated from board.plugins.json by \`meith plugin:add\` and \`meith plugin:remove\`.
|
|
175
|
+
//
|
|
176
|
+
// The simple path is those commands, or editing board.plugins.json and running one of
|
|
177
|
+
// them. A plugin that does not fit that convention can be added here by hand instead —
|
|
178
|
+
// keep it out of board.plugins.json so a regenerate does not drop it.
|
|
179
|
+
//
|
|
180
|
+
// docs/customization/plugins.md explains both.`
|
|
181
|
+
|
|
182
|
+
export function renderBoardModule(plugins: readonly ManifestEntry[]): string {
|
|
183
|
+
const imports = plugins.map((entry) => {
|
|
184
|
+
const name = toIdentifier(entry.key)
|
|
185
|
+
return `import { messages as ${name}Messages, plugin as ${name}Plugin } from '${entry.package}'`
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const entries = plugins.map((entry) => {
|
|
189
|
+
const name = toIdentifier(entry.key)
|
|
190
|
+
const enabled = entry.enabled === false ? 'false' : 'true'
|
|
191
|
+
return (
|
|
192
|
+
` { key: '${entry.key}', enabled: ${enabled}, ` +
|
|
193
|
+
`plugin: ${name}Plugin, messages: ${name}Messages },`
|
|
194
|
+
)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const importBlock = ["import type { InstalledPlugin } from '@meith/web/config'", ...imports].join(
|
|
198
|
+
'\n',
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
const listBlock =
|
|
202
|
+
entries.length === 0
|
|
203
|
+
? 'export const INSTALLED_PLUGINS: readonly InstalledPlugin[] = []'
|
|
204
|
+
: `export const INSTALLED_PLUGINS: readonly InstalledPlugin[] = [\n${entries.join('\n')}\n]`
|
|
205
|
+
|
|
206
|
+
const functionBlock = `export function installedPluginDefinitions() {
|
|
207
|
+
return INSTALLED_PLUGINS.filter(
|
|
208
|
+
(entry) => entry.enabled !== false && entry.plugin !== undefined,
|
|
209
|
+
).map((entry) => entry.plugin)
|
|
210
|
+
}`
|
|
211
|
+
|
|
212
|
+
return `${[BOARD_HEADER, importBlock, listBlock, functionBlock].join('\n\n')}\n`
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function regenerateBoard(root: string): GeneratorResult {
|
|
216
|
+
try {
|
|
217
|
+
const board = BOARD_MODE_BOARD
|
|
218
|
+
const raw = readFileSync(join(root, board.manifestFile), 'utf8')
|
|
219
|
+
const plugins = (JSON.parse(raw) as Manifest).plugins
|
|
220
|
+
const dependencies = dependencyNames(join(root, board.packageFile))
|
|
221
|
+
|
|
222
|
+
validateBoardManifest(plugins, dependencies)
|
|
223
|
+
writeFileSync(join(root, board.outputFile), renderBoardModule(plugins), 'utf8')
|
|
224
|
+
|
|
225
|
+
return { ok: true, output: `${plugins.length} plugin(s)` }
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return { ok: false, output: (error as Error).message }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function readManifestFor(mode: Mode, board: Board): Promise<Manifest> {
|
|
232
|
+
const path = join(mode.root, board.manifestFile)
|
|
45
233
|
let raw: string
|
|
46
234
|
try {
|
|
47
235
|
raw = await readFile(path, 'utf8')
|
|
48
236
|
} catch (error) {
|
|
49
237
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
50
|
-
throw new ValidationError(
|
|
51
|
-
`${path} does not exist. This command edits source files for every board this ` +
|
|
52
|
-
'repository carries — apps/community and boards/stock — and reruns the generator, ' +
|
|
53
|
-
'so it needs a checkout of the repository, not the deployed image — run it where you ' +
|
|
54
|
-
'would run `pnpm add`, commit both board.plugins.json files and both ' +
|
|
55
|
-
'meith.plugins.ts files, then rebuild and redeploy.',
|
|
56
|
-
)
|
|
238
|
+
throw new ValidationError(mode.missingManifest(path))
|
|
57
239
|
}
|
|
58
240
|
throw error
|
|
59
241
|
}
|
|
@@ -76,19 +258,14 @@ async function readManifestFor(board: Board): Promise<Manifest> {
|
|
|
76
258
|
return { plugins: parsed.plugins }
|
|
77
259
|
}
|
|
78
260
|
|
|
79
|
-
async function writeManifestFor(board: Board, manifest: Manifest): Promise<void> {
|
|
80
|
-
const path = join(
|
|
261
|
+
async function writeManifestFor(mode: Mode, board: Board, manifest: Manifest): Promise<void> {
|
|
262
|
+
const path = join(mode.root, board.manifestFile)
|
|
81
263
|
await writeFile(path, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
|
|
82
264
|
}
|
|
83
265
|
|
|
84
|
-
|
|
85
|
-
readonly ok: boolean
|
|
86
|
-
readonly output: string
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function runGenerator(): GeneratorResult {
|
|
266
|
+
function runGenerator(generatorScript: string): GeneratorResult {
|
|
90
267
|
try {
|
|
91
|
-
const output = execFileSync(process.execPath, [generatorScript
|
|
268
|
+
const output = execFileSync(process.execPath, [generatorScript], {
|
|
92
269
|
encoding: 'utf8',
|
|
93
270
|
stdio: 'pipe',
|
|
94
271
|
})
|
|
@@ -134,32 +311,38 @@ export async function pluginAdd(args: readonly string[]): Promise<number> {
|
|
|
134
311
|
)
|
|
135
312
|
}
|
|
136
313
|
|
|
137
|
-
const
|
|
314
|
+
const mode = resolveMode()
|
|
315
|
+
mode.assertEditable()
|
|
316
|
+
const originals = await Promise.all(mode.boards.map((board) => readManifestFor(mode, board)))
|
|
138
317
|
|
|
139
318
|
originals.forEach(({ plugins }, index) => {
|
|
140
319
|
if (plugins.some((entry) => entry.key === key)) {
|
|
141
|
-
throw new ValidationError(`"${key}" is already in ${
|
|
320
|
+
throw new ValidationError(`"${key}" is already in ${mode.boards[index]?.manifestFile}.`)
|
|
142
321
|
}
|
|
143
322
|
})
|
|
144
323
|
|
|
324
|
+
mode.installPackage(packageName)
|
|
325
|
+
|
|
145
326
|
const entry: ManifestEntry = { key, package: packageName, enabled: !flags.has('disabled') }
|
|
146
327
|
|
|
147
328
|
await Promise.all(
|
|
148
|
-
|
|
149
|
-
writeManifestFor(board, { plugins: [...(originals[index]?.plugins ?? []), entry] }),
|
|
329
|
+
mode.boards.map((board, index) =>
|
|
330
|
+
writeManifestFor(mode, board, { plugins: [...(originals[index]?.plugins ?? []), entry] }),
|
|
150
331
|
),
|
|
151
332
|
)
|
|
152
333
|
|
|
153
|
-
const result =
|
|
334
|
+
const result = mode.regenerate()
|
|
154
335
|
if (!result.ok) {
|
|
155
336
|
await Promise.all(
|
|
156
|
-
|
|
337
|
+
mode.boards.map((board, index) =>
|
|
338
|
+
writeManifestFor(mode, board, originals[index] ?? { plugins: [] }),
|
|
339
|
+
),
|
|
157
340
|
)
|
|
158
341
|
throw new ValidationError(`Could not add "${key}":\n\n${result.output}`)
|
|
159
342
|
}
|
|
160
343
|
|
|
161
|
-
const manifestFiles =
|
|
162
|
-
const outputFiles =
|
|
344
|
+
const manifestFiles = mode.boards.map((board) => board.manifestFile).join(' and ')
|
|
345
|
+
const outputFiles = mode.boards.map((board) => board.outputFile).join(' and ')
|
|
163
346
|
console.log(
|
|
164
347
|
`Added "${key}" (${packageName}${entry.enabled ? '' : ', disabled'}) to ${manifestFiles} ` +
|
|
165
348
|
`and regenerated ${outputFiles}.`,
|
|
@@ -176,37 +359,41 @@ export async function pluginRemove(args: readonly string[]): Promise<number> {
|
|
|
176
359
|
throw new ValidationError('Usage: meith plugin:remove <key>')
|
|
177
360
|
}
|
|
178
361
|
|
|
179
|
-
const
|
|
362
|
+
const mode = resolveMode()
|
|
363
|
+
mode.assertEditable()
|
|
364
|
+
const originals = await Promise.all(mode.boards.map((board) => readManifestFor(mode, board)))
|
|
180
365
|
|
|
181
366
|
originals.forEach(({ plugins }, index) => {
|
|
182
367
|
if (!plugins.some((entry) => entry.key === key)) {
|
|
183
368
|
const present = plugins.map((entry) => entry.key)
|
|
184
369
|
throw new ValidationError(
|
|
185
370
|
present.length === 0
|
|
186
|
-
? `"${key}" is not in ${
|
|
187
|
-
: `"${key}" is not in ${
|
|
371
|
+
? `"${key}" is not in ${mode.boards[index]?.manifestFile} — it lists no plugins.`
|
|
372
|
+
: `"${key}" is not in ${mode.boards[index]?.manifestFile}. Present: ${present.join(', ')}.`,
|
|
188
373
|
)
|
|
189
374
|
}
|
|
190
375
|
})
|
|
191
376
|
|
|
192
377
|
await Promise.all(
|
|
193
|
-
|
|
194
|
-
writeManifestFor(board, {
|
|
378
|
+
mode.boards.map((board, index) =>
|
|
379
|
+
writeManifestFor(mode, board, {
|
|
195
380
|
plugins: (originals[index]?.plugins ?? []).filter((entry) => entry.key !== key),
|
|
196
381
|
}),
|
|
197
382
|
),
|
|
198
383
|
)
|
|
199
384
|
|
|
200
|
-
const result =
|
|
385
|
+
const result = mode.regenerate()
|
|
201
386
|
if (!result.ok) {
|
|
202
387
|
await Promise.all(
|
|
203
|
-
|
|
388
|
+
mode.boards.map((board, index) =>
|
|
389
|
+
writeManifestFor(mode, board, originals[index] ?? { plugins: [] }),
|
|
390
|
+
),
|
|
204
391
|
)
|
|
205
392
|
throw new ValidationError(`Could not remove "${key}":\n\n${result.output}`)
|
|
206
393
|
}
|
|
207
394
|
|
|
208
|
-
const manifestFiles =
|
|
209
|
-
const outputFiles =
|
|
395
|
+
const manifestFiles = mode.boards.map((board) => board.manifestFile).join(' and ')
|
|
396
|
+
const outputFiles = mode.boards.map((board) => board.outputFile).join(' and ')
|
|
210
397
|
console.log(`Removed "${key}" from ${manifestFiles} and regenerated ${outputFiles}.`)
|
|
211
398
|
console.log('Rebuild and redeploy for it to take effect.')
|
|
212
399
|
return 0
|
package/src/upgrade.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { type PluginDefinition, pluginNavigationPlacements } from '@meith/plugin
|
|
|
11
11
|
import { runPluginLifecycle } from '@meith/runtime'
|
|
12
12
|
import { type PluginUpgrade, planUpgrade, upgradeNotice } from '@meith/upgrade'
|
|
13
13
|
|
|
14
|
-
export const CODE_VERSION = '0.
|
|
14
|
+
export const CODE_VERSION = '0.28.1'
|
|
15
15
|
|
|
16
16
|
export function pluginUpgrades(plugins: readonly PluginDefinition[]): readonly PluginUpgrade[] {
|
|
17
17
|
return plugins.map((plugin) => ({
|