@meith/web 0.16.0 → 0.17.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/bin/forum-web.mjs +53 -23
- package/bin/forum-web.test.ts +37 -0
- package/package.json +47 -47
- package/src/components/admin/theme-forms.tsx +1 -1
- package/src/components/admin/theme-palette.tsx +1 -1
- package/src/components/admin/user-bulk-toolbar.tsx +5 -3
- package/src/server/upgrade-notice.ts +1 -1
package/bin/forum-web.mjs
CHANGED
|
@@ -47,7 +47,15 @@
|
|
|
47
47
|
* through real `dependencies` entries a hoisted `node_modules` would need.
|
|
48
48
|
*/
|
|
49
49
|
import { spawn } from 'node:child_process'
|
|
50
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
cpSync,
|
|
52
|
+
existsSync,
|
|
53
|
+
mkdirSync,
|
|
54
|
+
readFileSync,
|
|
55
|
+
realpathSync,
|
|
56
|
+
rmSync,
|
|
57
|
+
writeFileSync,
|
|
58
|
+
} from 'node:fs'
|
|
51
59
|
import { createRequire } from 'node:module'
|
|
52
60
|
import { dirname, join, relative, resolve, sep } from 'node:path'
|
|
53
61
|
import { fileURLToPath } from 'node:url'
|
|
@@ -92,6 +100,24 @@ function toPosixRelative(from, to) {
|
|
|
92
100
|
return rel.startsWith('.') ? rel : `./${rel}`
|
|
93
101
|
}
|
|
94
102
|
|
|
103
|
+
export function rebaseGlobalsCssSources(css, cssDir, workspaceRootOverride) {
|
|
104
|
+
return css.replace(/@source "((?:\.\.\/)+)([^"]+)";/g, (_match, _dots, tail) => {
|
|
105
|
+
const target = join(workspaceRootOverride, ...tail.split('/'))
|
|
106
|
+
return `@source "${toPosixRelative(cssDir, target)}";`
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function rewriteGlobalsCssSourcePaths() {
|
|
111
|
+
const workspaceRootOverride = process.env.FORUM_WORKSPACE_ROOT
|
|
112
|
+
if (!workspaceRootOverride) return
|
|
113
|
+
|
|
114
|
+
const cssPath = join(appDir, 'src', 'styles', 'globals.css')
|
|
115
|
+
if (!existsSync(cssPath)) return
|
|
116
|
+
|
|
117
|
+
const css = readFileSync(cssPath, 'utf8')
|
|
118
|
+
writeFileSync(cssPath, rebaseGlobalsCssSources(css, dirname(cssPath), workspaceRootOverride))
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
/**
|
|
96
122
|
* A board outside this monorepo has a hoisted `node_modules` (see the module
|
|
97
123
|
* comment), so plain bare-specifier resolution reaches every `@meith/*`
|
|
@@ -156,6 +182,8 @@ function materialize() {
|
|
|
156
182
|
cpSync(source, target, { recursive: true })
|
|
157
183
|
}
|
|
158
184
|
|
|
185
|
+
rewriteGlobalsCssSourcePaths()
|
|
186
|
+
|
|
159
187
|
const tsconfig = {
|
|
160
188
|
compilerOptions: {
|
|
161
189
|
target: 'ES2022',
|
|
@@ -213,29 +241,31 @@ function run(executable, args, cwd) {
|
|
|
213
241
|
child.on('error', (error) => fail(error.message))
|
|
214
242
|
}
|
|
215
243
|
|
|
216
|
-
|
|
244
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
245
|
+
const [, , command, ...rest] = process.argv
|
|
217
246
|
|
|
218
|
-
if (!['dev', 'build', 'start'].includes(command ?? '')) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
247
|
+
if (!['dev', 'build', 'start'].includes(command ?? '')) {
|
|
248
|
+
console.error('Usage: forum-web <dev|build|start> [next arguments]')
|
|
249
|
+
process.exit(1)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
materialize()
|
|
222
253
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
254
|
+
if (command === 'start') {
|
|
255
|
+
// `next.config.mjs` sets `output: 'standalone'`, and a standalone build is
|
|
256
|
+
// run from its own traced server.js, not `next start` (see docker/Dockerfile
|
|
257
|
+
// and docker/entrypoint.sh, which run the image's board the same way). The
|
|
258
|
+
// tracing root is the workspace root (see the module comment on why
|
|
259
|
+
// `.meith/app` sits exactly two levels below it), so the standalone bundle
|
|
260
|
+
// preserves that same relative path down to the app directory.
|
|
261
|
+
const standaloneRoot = join(appDir, '.next', 'standalone')
|
|
262
|
+
const serverScript = join(standaloneRoot, relative(workspaceRoot, appDir), 'server.js')
|
|
263
|
+
if (!existsSync(serverScript)) {
|
|
264
|
+
fail(`no standalone build at ${serverScript} — run "forum-web build" first.`)
|
|
265
|
+
}
|
|
266
|
+
run(process.execPath, [serverScript, ...rest], standaloneRoot)
|
|
267
|
+
} else {
|
|
268
|
+
const nextBin = resolveNextBin()
|
|
269
|
+
run(process.execPath, [nextBin, command, ...rest], appDir)
|
|
236
270
|
}
|
|
237
|
-
run(process.execPath, [serverScript, ...rest], standaloneRoot)
|
|
238
|
-
} else {
|
|
239
|
-
const nextBin = resolveNextBin()
|
|
240
|
-
run(process.execPath, [nextBin, command, ...rest], appDir)
|
|
241
271
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
// @ts-expect-error forum-web.mjs ships untyped, imported directly for its pure export
|
|
4
|
+
import { rebaseGlobalsCssSources } from './forum-web.mjs'
|
|
5
|
+
|
|
6
|
+
describe('rebaseGlobalsCssSources', () => {
|
|
7
|
+
it('rebases every @source line against the real workspace root', () => {
|
|
8
|
+
const css = [
|
|
9
|
+
'@import "tailwindcss";',
|
|
10
|
+
'',
|
|
11
|
+
'@source "../../../../themes";',
|
|
12
|
+
'@source "../../../../plugins";',
|
|
13
|
+
'@source "../../../../examples";',
|
|
14
|
+
'@source "../../../../packages/ui/src";',
|
|
15
|
+
'',
|
|
16
|
+
'.foo { color: red; }',
|
|
17
|
+
].join('\n')
|
|
18
|
+
|
|
19
|
+
const cssDir = '/repo/boards/stock/.meith/app/src/styles'
|
|
20
|
+
const workspaceRoot = '/repo'
|
|
21
|
+
|
|
22
|
+
const rewritten = rebaseGlobalsCssSources(css, cssDir, workspaceRoot)
|
|
23
|
+
|
|
24
|
+
expect(rewritten).toContain('@source "../../../../../../themes";')
|
|
25
|
+
expect(rewritten).toContain('@source "../../../../../../plugins";')
|
|
26
|
+
expect(rewritten).toContain('@source "../../../../../../examples";')
|
|
27
|
+
expect(rewritten).toContain('@source "../../../../../../packages/ui/src";')
|
|
28
|
+
expect(rewritten).toContain('.foo { color: red; }')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('is a no-op for a file with no @source lines', () => {
|
|
32
|
+
const css = '@import "tailwindcss";\n.foo { color: red; }\n'
|
|
33
|
+
expect(rebaseGlobalsCssSources(css, '/repo/boards/stock/.meith/app/src/styles', '/repo')).toBe(
|
|
34
|
+
css,
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "The board itself: the Next.js app, and the forum-web bin that materializes it into an external board workspace.",
|
|
5
5
|
"license": "LGPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -42,52 +42,52 @@
|
|
|
42
42
|
"typescript": "7.0.2",
|
|
43
43
|
"@types/react": "^19",
|
|
44
44
|
"@types/react-dom": "^19",
|
|
45
|
-
"@meith/accounts": "0.
|
|
46
|
-
"@meith/
|
|
47
|
-
"@meith/
|
|
48
|
-
"@meith/
|
|
49
|
-
"@meith/
|
|
50
|
-
"@meith/
|
|
51
|
-
"@meith/avatars": "0.
|
|
52
|
-
"@meith/
|
|
53
|
-
"@meith/
|
|
54
|
-
"@meith/
|
|
55
|
-
"@meith/drafts": "0.
|
|
56
|
-
"@meith/
|
|
57
|
-
"@meith/
|
|
58
|
-
"@meith/
|
|
59
|
-
"@meith/
|
|
60
|
-
"@meith/
|
|
61
|
-
"@meith/install": "0.
|
|
62
|
-
"@meith/
|
|
63
|
-
"@meith/markdown": "0.
|
|
64
|
-
"@meith/
|
|
65
|
-
"@meith/
|
|
66
|
-
"@meith/
|
|
67
|
-
"@meith/
|
|
68
|
-
"@meith/
|
|
69
|
-
"@meith/
|
|
70
|
-
"@meith/
|
|
71
|
-
"@meith/
|
|
72
|
-
"@meith/
|
|
73
|
-
"@meith/
|
|
74
|
-
"@meith/
|
|
75
|
-
"@meith/
|
|
76
|
-
"@meith/
|
|
77
|
-
"@meith/
|
|
78
|
-
"@meith/
|
|
79
|
-
"@meith/
|
|
80
|
-
"@meith/
|
|
81
|
-
"@meith/
|
|
82
|
-
"@meith/theme-
|
|
83
|
-
"@meith/
|
|
84
|
-
"@meith/theme-
|
|
85
|
-
"@meith/theme-
|
|
86
|
-
"@meith/theme-phasebook": "0.
|
|
87
|
-
"@meith/theme-raidframe": "0.
|
|
88
|
-
"@meith/
|
|
89
|
-
"@meith/
|
|
90
|
-
"@meith/
|
|
45
|
+
"@meith/accounts": "0.17.1",
|
|
46
|
+
"@meith/admin": "0.17.1",
|
|
47
|
+
"@meith/antispam": "0.17.1",
|
|
48
|
+
"@meith/api": "0.17.1",
|
|
49
|
+
"@meith/attachments": "0.17.1",
|
|
50
|
+
"@meith/authorization": "0.17.1",
|
|
51
|
+
"@meith/avatars": "0.17.1",
|
|
52
|
+
"@meith/core": "0.17.1",
|
|
53
|
+
"@meith/db": "0.17.1",
|
|
54
|
+
"@meith/demo": "0.17.1",
|
|
55
|
+
"@meith/drafts": "0.17.1",
|
|
56
|
+
"@meith/groups": "0.17.1",
|
|
57
|
+
"@meith/events": "0.17.1",
|
|
58
|
+
"@meith/drivers": "0.17.1",
|
|
59
|
+
"@meith/forums": "0.17.1",
|
|
60
|
+
"@meith/i18n": "0.17.1",
|
|
61
|
+
"@meith/install": "0.17.1",
|
|
62
|
+
"@meith/mail": "0.17.1",
|
|
63
|
+
"@meith/markdown": "0.17.1",
|
|
64
|
+
"@meith/import": "0.17.1",
|
|
65
|
+
"@meith/messages": "0.17.1",
|
|
66
|
+
"@meith/marketplace": "0.17.1",
|
|
67
|
+
"@meith/notifications": "0.17.1",
|
|
68
|
+
"@meith/moderation": "0.17.1",
|
|
69
|
+
"@meith/plugin-dues": "0.17.1",
|
|
70
|
+
"@meith/plugin-kit": "0.17.1",
|
|
71
|
+
"@meith/posts": "0.17.1",
|
|
72
|
+
"@meith/profile-fields": "0.17.1",
|
|
73
|
+
"@meith/polls": "0.17.1",
|
|
74
|
+
"@meith/relations": "0.17.1",
|
|
75
|
+
"@meith/search": "0.17.1",
|
|
76
|
+
"@meith/reputation": "0.17.1",
|
|
77
|
+
"@meith/runtime": "0.17.1",
|
|
78
|
+
"@meith/settings": "0.17.1",
|
|
79
|
+
"@meith/signatures": "0.17.1",
|
|
80
|
+
"@meith/subscriptions": "0.17.1",
|
|
81
|
+
"@meith/tasks": "0.17.1",
|
|
82
|
+
"@meith/theme-clubhouse": "0.17.1",
|
|
83
|
+
"@meith/theme-kit": "0.17.1",
|
|
84
|
+
"@meith/theme-default": "0.17.1",
|
|
85
|
+
"@meith/theme-midnight": "0.17.1",
|
|
86
|
+
"@meith/theme-phasebook": "0.17.1",
|
|
87
|
+
"@meith/theme-raidframe": "0.17.1",
|
|
88
|
+
"@meith/ui": "0.17.1",
|
|
89
|
+
"@meith/threads": "0.17.1",
|
|
90
|
+
"@meith/upgrade": "0.17.1"
|
|
91
91
|
},
|
|
92
92
|
"scripts": {
|
|
93
93
|
"dev": "next dev",
|
|
@@ -385,7 +385,7 @@ export function ThemeEditorForm({
|
|
|
385
385
|
<fieldset
|
|
386
386
|
key={group.titleKey}
|
|
387
387
|
hidden={hydrated && matches === 0}
|
|
388
|
-
className="flex flex-col gap-2 rounded-md border border-border p-3"
|
|
388
|
+
className="flex min-w-0 flex-col gap-2 rounded-md border border-border p-3"
|
|
389
389
|
>
|
|
390
390
|
<legend className="flex flex-wrap items-baseline gap-x-3 gap-y-1 px-1">
|
|
391
391
|
<span className="text-sm font-medium">{copy[group.titleKey]}</span>
|
|
@@ -104,7 +104,7 @@ export function PaletteGrid({
|
|
|
104
104
|
onSelect: (name: string) => void
|
|
105
105
|
}) {
|
|
106
106
|
return (
|
|
107
|
-
<div className="grid grid-cols-
|
|
107
|
+
<div className="grid grid-cols-2 gap-2 @lg:grid-cols-3">
|
|
108
108
|
{cells.map((cell) => (
|
|
109
109
|
<Cell
|
|
110
110
|
key={cell.token.name}
|
|
@@ -53,7 +53,7 @@ export function UserBulkToolbar({
|
|
|
53
53
|
<form
|
|
54
54
|
id="admin-user-bulk"
|
|
55
55
|
action={action}
|
|
56
|
-
className="flex flex-col gap-3 sm:flex-row sm:items-end"
|
|
56
|
+
className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end"
|
|
57
57
|
>
|
|
58
58
|
<label className="flex min-w-48 flex-col gap-1 text-sm">
|
|
59
59
|
<span className="font-medium">{labels.action}</span>
|
|
@@ -81,7 +81,7 @@ export function UserBulkToolbar({
|
|
|
81
81
|
</label>
|
|
82
82
|
<button
|
|
83
83
|
type="button"
|
|
84
|
-
className="inline-flex h-10 items-center justify-center rounded-md border border-border px-4 text-sm font-medium"
|
|
84
|
+
className="inline-flex h-10 shrink-0 items-center justify-center whitespace-nowrap rounded-md border border-border px-4 text-sm font-medium"
|
|
85
85
|
onClick={() => {
|
|
86
86
|
for (const checkbox of document.querySelectorAll<HTMLInputElement>(
|
|
87
87
|
'[data-admin-user-select]',
|
|
@@ -92,7 +92,9 @@ export function UserBulkToolbar({
|
|
|
92
92
|
>
|
|
93
93
|
{labels.selectPage}
|
|
94
94
|
</button>
|
|
95
|
-
<
|
|
95
|
+
<span className="min-w-48">
|
|
96
|
+
<SubmitButton>{labels.apply}</SubmitButton>
|
|
97
|
+
</span>
|
|
96
98
|
</form>
|
|
97
99
|
</div>
|
|
98
100
|
)
|
|
@@ -6,7 +6,7 @@ import { planUpgrade, type UpgradeState, upgradeNotice } from '@meith/upgrade'
|
|
|
6
6
|
|
|
7
7
|
import { activeDefinitions } from './plugin-host'
|
|
8
8
|
|
|
9
|
-
export const CODE_VERSION = '0.
|
|
9
|
+
export const CODE_VERSION = '0.17.1'
|
|
10
10
|
|
|
11
11
|
export async function pendingUpgradeNotice(): Promise<string | null> {
|
|
12
12
|
if (env.DATA_SOURCE !== 'postgres') return null
|