@jchat/create-addon 1.0.0-alpha.3 → 1.0.0-alpha.31
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/CHANGELOG.md +193 -0
- package/LICENSE +18 -196
- package/README.md +192 -21
- package/dist/cli.d.mts +12 -5
- package/dist/cli.mjs +71 -31
- package/dist/contracts2.d.mts +45 -0
- package/dist/generator.d.mts +38 -0
- package/dist/generator.mjs +538 -244
- package/dist/index.d.mts +3 -12
- package/dist/index.mjs +1 -2
- package/package.json +29 -23
- package/src/cli.test.ts +0 -30
- package/src/cli.ts +0 -55
- package/src/generator.ts +0 -428
- package/src/index.ts +0 -5
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
targetDir: string;
|
|
5
|
-
jchatVersion: string;
|
|
6
|
-
addonSdkVersion: string;
|
|
7
|
-
sandboxImage?: string;
|
|
8
|
-
}
|
|
9
|
-
declare function createStandaloneDevkitFiles(options: StandaloneDevkitOptions): Readonly<Record<string, string>>;
|
|
10
|
-
declare function writeStandaloneDevkit(options: StandaloneDevkitOptions): void;
|
|
11
|
-
//#endregion
|
|
12
|
-
export { type StandaloneDevkitOptions, createStandaloneDevkitFiles, writeStandaloneDevkit };
|
|
1
|
+
import { n as MOUNT_POINTS, r as MountPoint, t as HOST_LOCALES } from "./contracts2.mjs";
|
|
2
|
+
import { a as createStandaloneDevkitFiles, c as writeStandaloneDevkit, i as createAddonFiles, n as AddonWorkspaceOptions, o as toDisplayName, r as StandaloneDevkitOptions, s as writeAddonWorkspace, t as AddonTemplateType } from "./generator.mjs";
|
|
3
|
+
export { type AddonTemplateType, type AddonWorkspaceOptions, HOST_LOCALES, MOUNT_POINTS, type MountPoint, type StandaloneDevkitOptions, createAddonFiles, createStandaloneDevkitFiles, toDisplayName, writeAddonWorkspace, writeStandaloneDevkit };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
export { createStandaloneDevkitFiles, writeStandaloneDevkit };
|
|
1
|
+
import{n as e,t}from"./contracts2.d.mts";import{a as n,i as r,n as i,r as a,t as o}from"./generator.mjs";export{t as HOST_LOCALES,e as MOUNT_POINTS,o as createAddonFiles,i as createStandaloneDevkitFiles,a as toDisplayName,r as writeAddonWorkspace,n as writeStandaloneDevkit};
|
package/package.json
CHANGED
|
@@ -1,62 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jchat/create-addon",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.31",
|
|
4
4
|
"private": false,
|
|
5
|
+
"description": "Command line tool for jchat addon creation.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"addon",
|
|
7
8
|
"chat",
|
|
8
9
|
"cli",
|
|
9
10
|
"create",
|
|
10
11
|
"jchat",
|
|
12
|
+
"jchat cloud",
|
|
11
13
|
"plugin",
|
|
12
14
|
"scaffold"
|
|
13
15
|
],
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/mjjabarullah/jchat.git",
|
|
18
|
-
"directory": "packages/create-addon"
|
|
19
|
-
},
|
|
16
|
+
"homepage": "https://jchatcloud.com",
|
|
17
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
20
18
|
"bin": {
|
|
21
19
|
"create-addon": "./dist/cli.mjs"
|
|
22
20
|
},
|
|
23
21
|
"files": [
|
|
24
22
|
"dist",
|
|
25
|
-
"src",
|
|
26
23
|
"README.md",
|
|
27
24
|
"CHANGELOG.md"
|
|
28
25
|
],
|
|
29
26
|
"type": "module",
|
|
30
|
-
"main": "./
|
|
31
|
-
"types": "./
|
|
27
|
+
"main": "./src/index.ts",
|
|
28
|
+
"types": "./src/index.ts",
|
|
32
29
|
"exports": {
|
|
33
30
|
".": {
|
|
34
|
-
"types": "./
|
|
35
|
-
"import": "./
|
|
31
|
+
"types": "./src/index.ts",
|
|
32
|
+
"import": "./src/index.ts",
|
|
33
|
+
"default": "./src/index.ts"
|
|
36
34
|
}
|
|
37
35
|
},
|
|
38
36
|
"publishConfig": {
|
|
37
|
+
"main": "./dist/index.mjs",
|
|
38
|
+
"types": "./dist/index.d.mts",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.mts",
|
|
42
|
+
"import": "./dist/index.mjs"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
39
45
|
"access": "public"
|
|
40
46
|
},
|
|
41
47
|
"scripts": {
|
|
42
48
|
"build": "tsdown",
|
|
43
|
-
"
|
|
49
|
+
"test": "bun test",
|
|
50
|
+
"lint": "oxlint .",
|
|
44
51
|
"format": "oxfmt . --write",
|
|
45
|
-
"typecheck": "
|
|
46
|
-
|
|
52
|
+
"typecheck": "bun check"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@clack/prompts": "catalog:"
|
|
47
56
|
},
|
|
48
57
|
"devDependencies": {
|
|
49
|
-
"@jchat/addon-sdk": "
|
|
50
|
-
"@types/
|
|
51
|
-
"
|
|
52
|
-
"tsdown": "^0.22.14"
|
|
58
|
+
"@jchat/addon-sdk": "workspace:*",
|
|
59
|
+
"@types/bun": "catalog:",
|
|
60
|
+
"tsdown": "catalog:"
|
|
53
61
|
},
|
|
54
62
|
"engines": {
|
|
55
|
-
"bun": ">=1.
|
|
63
|
+
"bun": ">=1.4.0"
|
|
56
64
|
},
|
|
57
65
|
"jchat": {
|
|
58
|
-
"
|
|
59
|
-
"addonSdkVersion": "0.0.1",
|
|
60
|
-
"sandboxImage": "ghcr.io/mjjabarullah/addon-developer-sandbox"
|
|
66
|
+
"sandboxImage": "ghcr.io/jchatcloud/jchat-sandbox"
|
|
61
67
|
}
|
|
62
68
|
}
|
package/src/cli.test.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import { parseCreatorArguments } from "./cli"
|
|
3
|
-
|
|
4
|
-
describe("create-addon CLI", () => {
|
|
5
|
-
test("uses the addon id as the default target", () => {
|
|
6
|
-
expect(parseCreatorArguments(["weather-addon"])).toEqual({
|
|
7
|
-
addonId: "weather-addon",
|
|
8
|
-
targetDir: "weather-addon",
|
|
9
|
-
sandboxImage: undefined
|
|
10
|
-
})
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test("accepts target and image overrides", () => {
|
|
14
|
-
expect(
|
|
15
|
-
parseCreatorArguments([
|
|
16
|
-
"weather-addon",
|
|
17
|
-
"--target=../weather-kit",
|
|
18
|
-
"--image=registry.example/sandbox:test"
|
|
19
|
-
])
|
|
20
|
-
).toEqual({
|
|
21
|
-
addonId: "weather-addon",
|
|
22
|
-
targetDir: "../weather-kit",
|
|
23
|
-
sandboxImage: "registry.example/sandbox:test"
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test("requires an addon id", () => {
|
|
28
|
-
expect(() => parseCreatorArguments([])).toThrow("Usage: bunx @jchat/create-addon")
|
|
29
|
-
})
|
|
30
|
-
})
|
package/src/cli.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import * as path from "node:path"
|
|
4
|
-
import { writeStandaloneDevkit } from "./generator"
|
|
5
|
-
|
|
6
|
-
interface CreatorPackageManifest {
|
|
7
|
-
version: string
|
|
8
|
-
jchat: {
|
|
9
|
-
jchatVersion: string
|
|
10
|
-
addonSdkVersion: string
|
|
11
|
-
sandboxImage: string
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CreatorArguments {
|
|
16
|
-
addonId: string
|
|
17
|
-
targetDir: string
|
|
18
|
-
sandboxImage?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function parseCreatorArguments(arguments_: readonly string[]): CreatorArguments {
|
|
22
|
-
const positional = arguments_.filter((argument) => !argument.startsWith("--"))
|
|
23
|
-
const addonId = positional[0]
|
|
24
|
-
if (addonId === undefined || addonId.startsWith("-")) {
|
|
25
|
-
throw new Error("Usage: bunx @jchat/create-addon <addon-id> [--target=<directory>] [--image=<image>]")
|
|
26
|
-
}
|
|
27
|
-
const targetArgument = arguments_.find((argument) => argument.startsWith("--target="))
|
|
28
|
-
const imageArgument = arguments_.find((argument) => argument.startsWith("--image="))
|
|
29
|
-
return {
|
|
30
|
-
addonId,
|
|
31
|
-
targetDir: targetArgument?.slice("--target=".length) || addonId,
|
|
32
|
-
sandboxImage: imageArgument?.slice("--image=".length) || undefined
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function runCreator(arguments_: readonly string[]): Promise<string> {
|
|
37
|
-
const manifest = (await Bun.file(
|
|
38
|
-
new URL("../package.json", import.meta.url)
|
|
39
|
-
).json()) as CreatorPackageManifest
|
|
40
|
-
const options = parseCreatorArguments(arguments_)
|
|
41
|
-
writeStandaloneDevkit({
|
|
42
|
-
...options,
|
|
43
|
-
jchatVersion: manifest.jchat.jchatVersion,
|
|
44
|
-
addonSdkVersion: manifest.jchat.addonSdkVersion,
|
|
45
|
-
sandboxImage:
|
|
46
|
-
options.sandboxImage ??
|
|
47
|
-
`${manifest.jchat.sandboxImage}:${manifest.jchat.jchatVersion.replaceAll("+", "-")}`
|
|
48
|
-
})
|
|
49
|
-
return path.resolve(options.targetDir)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (import.meta.main) {
|
|
53
|
-
const target = await runCreator(process.argv.slice(2))
|
|
54
|
-
console.log(`Created standalone addon development kit at ${target}`)
|
|
55
|
-
}
|
package/src/generator.ts
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import type { AddonManifest } from "@jchat/addon-sdk"
|
|
2
|
-
import { createCompatibleMajorRange } from "@jchat/addon-sdk/diagnostics"
|
|
3
|
-
import * as fs from "node:fs"
|
|
4
|
-
import * as path from "node:path"
|
|
5
|
-
|
|
6
|
-
export interface StandaloneDevkitOptions {
|
|
7
|
-
addonId: string
|
|
8
|
-
targetDir: string
|
|
9
|
-
jchatVersion: string
|
|
10
|
-
addonSdkVersion: string
|
|
11
|
-
sandboxImage?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const ADDON_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
15
|
-
const DEFAULT_SANDBOX_IMAGE = "ghcr.io/mjjabarullah/addon-developer-sandbox"
|
|
16
|
-
|
|
17
|
-
export function createStandaloneDevkitFiles(
|
|
18
|
-
options: StandaloneDevkitOptions
|
|
19
|
-
): Readonly<Record<string, string>> {
|
|
20
|
-
if (!ADDON_ID_PATTERN.test(options.addonId)) {
|
|
21
|
-
throw new Error("Addon id must contain lowercase letters, numbers, and hyphens")
|
|
22
|
-
}
|
|
23
|
-
const image =
|
|
24
|
-
options.sandboxImage ?? `${DEFAULT_SANDBOX_IMAGE}:${options.jchatVersion.replace(/\+/g, "-")}`
|
|
25
|
-
const manifest: AddonManifest = {
|
|
26
|
-
id: options.addonId,
|
|
27
|
-
name: toDisplayName(options.addonId),
|
|
28
|
-
version: "1.0.0",
|
|
29
|
-
description: `${toDisplayName(options.addonId)} addon for JChat`,
|
|
30
|
-
author: "Developer",
|
|
31
|
-
compatibility: {
|
|
32
|
-
jchat: createCompatibleMajorRange(options.jchatVersion),
|
|
33
|
-
addonSdk: createCompatibleMajorRange(options.addonSdkVersion)
|
|
34
|
-
},
|
|
35
|
-
frontend: {
|
|
36
|
-
bundle: "./ui.js",
|
|
37
|
-
mountPoints: ["chat-toolbar"]
|
|
38
|
-
},
|
|
39
|
-
permissions: []
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return Object.freeze({
|
|
43
|
-
".env.example": createEnvironmentFile(options.addonId, image),
|
|
44
|
-
".gitignore": ["node_modules/", "addon/node_modules/", "addon/build/", ".env", ""].join("\n"),
|
|
45
|
-
"README.md": createReadme(options.addonId),
|
|
46
|
-
"docker-compose.yml": createComposeFile(),
|
|
47
|
-
"package.json": `${JSON.stringify(createRootPackage(options.addonId), null, 2)}\n`,
|
|
48
|
-
"scripts/devkit.ts": createLifecycleScript(),
|
|
49
|
-
"addon/package.json": `${JSON.stringify(
|
|
50
|
-
createAddonPackage(options.addonId, options.addonSdkVersion),
|
|
51
|
-
null,
|
|
52
|
-
2
|
|
53
|
-
)}\n`,
|
|
54
|
-
"addon/tsconfig.json": `${JSON.stringify(createTsConfig(), null, 2)}\n`,
|
|
55
|
-
"addon/vite.config.ts": createViteConfig(),
|
|
56
|
-
"addon/src/manifest.ts": createManifestSource(manifest),
|
|
57
|
-
"addon/src/tailwind.css": createTailwindSource(),
|
|
58
|
-
"addon/src/ui.tsx": createUiSource(manifest.name)
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function writeStandaloneDevkit(options: StandaloneDevkitOptions): void {
|
|
63
|
-
const targetDir = path.resolve(options.targetDir)
|
|
64
|
-
if (fs.existsSync(targetDir)) {
|
|
65
|
-
throw new Error(`Development kit target already exists: ${targetDir}`)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
for (const [relativePath, contents] of Object.entries(createStandaloneDevkitFiles(options))) {
|
|
69
|
-
const target = path.join(targetDir, ...relativePath.split("/"))
|
|
70
|
-
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
71
|
-
fs.writeFileSync(target, contents)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function createEnvironmentFile(addonId: string, image: string): string {
|
|
76
|
-
return [
|
|
77
|
-
`JCHAT_SANDBOX_IMAGE=${image}`,
|
|
78
|
-
`JCHAT_SANDBOX_ADDON_ID=${addonId}`,
|
|
79
|
-
"JCHAT_SANDBOX_PORT=4317",
|
|
80
|
-
"JCHAT_SANDBOX_HTTP_MOCK_PORT=4318",
|
|
81
|
-
"JCHAT_SANDBOX_HTTP_MOCK_OFFLINE=false",
|
|
82
|
-
"JCHAT_SANDBOX_ADDON_HTTP_MOCK_URL=http://http-mock:4318",
|
|
83
|
-
""
|
|
84
|
-
].join("\n")
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function createComposeFile(): string {
|
|
88
|
-
return `services:
|
|
89
|
-
sandbox:
|
|
90
|
-
image: \${JCHAT_SANDBOX_IMAGE:?Copy .env.example to .env}
|
|
91
|
-
restart: unless-stopped
|
|
92
|
-
environment:
|
|
93
|
-
NODE_ENV: development
|
|
94
|
-
JCHAT_PORT: "4317"
|
|
95
|
-
JCHAT_COOKIE_SECRET: addon-sandbox-cookie-secret
|
|
96
|
-
JCHAT_DATABASE_URL: postgresql://jchat:jchat@db:5432/jchat
|
|
97
|
-
JCHAT_REDIS_DATABASE_URL: redis://redis:6379
|
|
98
|
-
JCHAT_STORAGE_TYPE: LOCAL
|
|
99
|
-
JCHAT_BOT_NAME: Sandbox Bot
|
|
100
|
-
JCHAT_BOT_EMAIL: bot@sandbox.local
|
|
101
|
-
JCHAT_DOMAIN: localhost
|
|
102
|
-
JCHAT_SITE_URL: http://localhost:\${JCHAT_SANDBOX_PORT:-4317}
|
|
103
|
-
JCHAT_ROUTING_MODE: path
|
|
104
|
-
JCHAT_ADDON_ENCRYPTION_KEY: addon-sandbox-encryption-key
|
|
105
|
-
JCHAT_ADDON_HTTP_MOCK_URL: \${JCHAT_SANDBOX_ADDON_HTTP_MOCK_URL-http://http-mock:4318}
|
|
106
|
-
ports:
|
|
107
|
-
- "127.0.0.1:\${JCHAT_SANDBOX_PORT:-4317}:4317"
|
|
108
|
-
volumes:
|
|
109
|
-
- ./addon:/app/addons-dev/\${JCHAT_SANDBOX_ADDON_ID:?Set addon id}:ro
|
|
110
|
-
- sandbox_uploads:/app/uploads
|
|
111
|
-
depends_on:
|
|
112
|
-
db:
|
|
113
|
-
condition: service_healthy
|
|
114
|
-
redis:
|
|
115
|
-
condition: service_healthy
|
|
116
|
-
http-mock:
|
|
117
|
-
condition: service_healthy
|
|
118
|
-
|
|
119
|
-
http-mock:
|
|
120
|
-
image: \${JCHAT_SANDBOX_IMAGE:?Copy .env.example to .env}
|
|
121
|
-
entrypoint: ["/app/addon-http-mock"]
|
|
122
|
-
environment:
|
|
123
|
-
JCHAT_SANDBOX_HTTP_MOCK_PORT: "4318"
|
|
124
|
-
JCHAT_SANDBOX_HTTP_MOCK_OFFLINE: \${JCHAT_SANDBOX_HTTP_MOCK_OFFLINE:-false}
|
|
125
|
-
ports:
|
|
126
|
-
- "127.0.0.1:\${JCHAT_SANDBOX_HTTP_MOCK_PORT:-4318}:4318"
|
|
127
|
-
healthcheck:
|
|
128
|
-
test: ["CMD", "/app/addon-http-mock", "--healthcheck"]
|
|
129
|
-
interval: 2s
|
|
130
|
-
timeout: 3s
|
|
131
|
-
retries: 30
|
|
132
|
-
|
|
133
|
-
db:
|
|
134
|
-
image: postgres:18-alpine
|
|
135
|
-
environment:
|
|
136
|
-
POSTGRES_USER: jchat
|
|
137
|
-
POSTGRES_PASSWORD: jchat
|
|
138
|
-
POSTGRES_DB: jchat
|
|
139
|
-
volumes:
|
|
140
|
-
- sandbox_postgres:/var/lib/postgresql/data
|
|
141
|
-
healthcheck:
|
|
142
|
-
test: ["CMD-SHELL", "pg_isready -U jchat"]
|
|
143
|
-
interval: 2s
|
|
144
|
-
timeout: 3s
|
|
145
|
-
retries: 30
|
|
146
|
-
|
|
147
|
-
redis:
|
|
148
|
-
image: redis:8.0.6-alpine3.21
|
|
149
|
-
volumes:
|
|
150
|
-
- sandbox_redis:/data
|
|
151
|
-
healthcheck:
|
|
152
|
-
test: ["CMD", "redis-cli", "ping"]
|
|
153
|
-
interval: 2s
|
|
154
|
-
timeout: 3s
|
|
155
|
-
retries: 30
|
|
156
|
-
|
|
157
|
-
volumes:
|
|
158
|
-
sandbox_postgres:
|
|
159
|
-
sandbox_redis:
|
|
160
|
-
sandbox_uploads:
|
|
161
|
-
`
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function createRootPackage(addonId: string) {
|
|
165
|
-
return {
|
|
166
|
-
name: `${addonId}-development-kit`,
|
|
167
|
-
private: true,
|
|
168
|
-
type: "module",
|
|
169
|
-
workspaces: ["addon"],
|
|
170
|
-
scripts: {
|
|
171
|
-
sandbox: "bun run ./scripts/devkit.ts run",
|
|
172
|
-
"sandbox:test": "bun run ./scripts/devkit.ts test",
|
|
173
|
-
"sandbox:reset": "bun run ./scripts/devkit.ts reset",
|
|
174
|
-
"sandbox:package": "bun run ./scripts/devkit.ts package",
|
|
175
|
-
"sandbox:cleanup": "bun run ./scripts/devkit.ts cleanup"
|
|
176
|
-
},
|
|
177
|
-
devDependencies: {
|
|
178
|
-
"bun-types": "^1.3.14"
|
|
179
|
-
},
|
|
180
|
-
engines: {
|
|
181
|
-
bun: ">=1.3.11"
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function createAddonPackage(addonId: string, addonSdkVersion: string) {
|
|
187
|
-
return {
|
|
188
|
-
name: `@jchat/${addonId}`,
|
|
189
|
-
version: "1.0.0",
|
|
190
|
-
private: true,
|
|
191
|
-
type: "module",
|
|
192
|
-
scripts: {
|
|
193
|
-
dev: "vite build --watch --mode=development",
|
|
194
|
-
build: "vite build --mode=production",
|
|
195
|
-
test: "bun test",
|
|
196
|
-
typecheck: "tsc --noEmit"
|
|
197
|
-
},
|
|
198
|
-
dependencies: {
|
|
199
|
-
"@jchat/addon-sdk": `^${addonSdkVersion}`,
|
|
200
|
-
"@tanstack/react-query": "^5.101.4",
|
|
201
|
-
react: "^19.2.8",
|
|
202
|
-
"react-dom": "^19.2.8"
|
|
203
|
-
},
|
|
204
|
-
devDependencies: {
|
|
205
|
-
"@tailwindcss/vite": "^4.3.3",
|
|
206
|
-
"@types/node": "^26.1.1",
|
|
207
|
-
"@types/react": "^19.2.17",
|
|
208
|
-
"@types/react-dom": "^19.2.3",
|
|
209
|
-
"bun-types": "^1.3.14",
|
|
210
|
-
tailwindcss: "^4.3.3",
|
|
211
|
-
typescript: "^7.0.2",
|
|
212
|
-
vite: "^8.1.5"
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function createTsConfig() {
|
|
218
|
-
return {
|
|
219
|
-
compilerOptions: {
|
|
220
|
-
strict: true,
|
|
221
|
-
target: "ESNext",
|
|
222
|
-
module: "ESNext",
|
|
223
|
-
moduleResolution: "bundler",
|
|
224
|
-
jsx: "react-jsx",
|
|
225
|
-
noEmit: true,
|
|
226
|
-
types: ["bun-types", "node"]
|
|
227
|
-
},
|
|
228
|
-
include: ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"]
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function createManifestSource(manifest: AddonManifest): string {
|
|
233
|
-
return `import type { AddonManifest } from "@jchat/addon-sdk"
|
|
234
|
-
|
|
235
|
-
const manifest: AddonManifest = ${JSON.stringify(manifest, null, 2)}
|
|
236
|
-
|
|
237
|
-
export default manifest
|
|
238
|
-
`
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function createViteConfig(): string {
|
|
242
|
-
return `import { jchatAddon } from "@jchat/addon-sdk/vite"
|
|
243
|
-
import tailwindcss from "@tailwindcss/vite"
|
|
244
|
-
import path from "node:path"
|
|
245
|
-
import { defineConfig } from "vite"
|
|
246
|
-
import manifest from "./src/manifest"
|
|
247
|
-
|
|
248
|
-
export default defineConfig({
|
|
249
|
-
plugins: [tailwindcss(), jchatAddon({ manifest })],
|
|
250
|
-
build: {
|
|
251
|
-
outDir: "build",
|
|
252
|
-
emptyOutDir: true,
|
|
253
|
-
lib: {
|
|
254
|
-
entry: path.resolve(import.meta.dirname, "src/ui.tsx"),
|
|
255
|
-
formats: ["es"],
|
|
256
|
-
fileName: () => "ui.[hash].js"
|
|
257
|
-
},
|
|
258
|
-
rolldownOptions: {
|
|
259
|
-
external: [
|
|
260
|
-
"react",
|
|
261
|
-
"react-dom",
|
|
262
|
-
"react/jsx-runtime",
|
|
263
|
-
"@tanstack/react-query",
|
|
264
|
-
"@jchat/addon-sdk"
|
|
265
|
-
],
|
|
266
|
-
output: {
|
|
267
|
-
format: "esm"
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
`
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function createUiSource(name: string): string {
|
|
276
|
-
return `import type { AddonProps } from "@jchat/addon-sdk"
|
|
277
|
-
import { useAddonSDK, useIcons } from "@jchat/addon-sdk"
|
|
278
|
-
import "./tailwind.css"
|
|
279
|
-
|
|
280
|
-
export default function Addon({ mountPoint }: AddonProps) {
|
|
281
|
-
const sdk = useAddonSDK()
|
|
282
|
-
const { Icons } = useIcons()
|
|
283
|
-
|
|
284
|
-
return (
|
|
285
|
-
<div className="rounded border border-border bg-card p-2 text-card-foreground">
|
|
286
|
-
<div className="mb-2 flex items-center gap-2">
|
|
287
|
-
<Icons.Plus aria-hidden="true" className="size-4 text-primary" />
|
|
288
|
-
<strong>${name}</strong>
|
|
289
|
-
</div>
|
|
290
|
-
<p className="mb-2 text-sm text-muted-foreground">Mounted at {mountPoint}</p>
|
|
291
|
-
<button
|
|
292
|
-
type="button"
|
|
293
|
-
className="rounded bg-primary px-3 py-2 text-primary-foreground"
|
|
294
|
-
onClick={() => sdk.toast("Hello from ${name}")}
|
|
295
|
-
>
|
|
296
|
-
Test addon
|
|
297
|
-
</button>
|
|
298
|
-
</div>
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
|
-
`
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function createTailwindSource(): string {
|
|
305
|
-
return `@import "tailwindcss";
|
|
306
|
-
|
|
307
|
-
@theme inline {
|
|
308
|
-
--color-background: var(--background);
|
|
309
|
-
--color-foreground: var(--foreground);
|
|
310
|
-
--color-card: var(--card);
|
|
311
|
-
--color-card-foreground: var(--card-foreground);
|
|
312
|
-
--color-border: var(--border);
|
|
313
|
-
--color-primary: var(--primary);
|
|
314
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
315
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
316
|
-
}
|
|
317
|
-
`
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function createLifecycleScript(): string {
|
|
321
|
-
return `import * as fs from "node:fs"
|
|
322
|
-
import * as path from "node:path"
|
|
323
|
-
|
|
324
|
-
type Command = "run" | "test" | "reset" | "package" | "cleanup"
|
|
325
|
-
|
|
326
|
-
const command = (process.argv[2] ?? "run") as Command
|
|
327
|
-
if (!["run", "test", "reset", "package", "cleanup"].includes(command)) {
|
|
328
|
-
throw new Error(\`Unknown development-kit command: \${command}\`)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const root = path.resolve(import.meta.dir, "..")
|
|
332
|
-
const environment = {
|
|
333
|
-
...process.env,
|
|
334
|
-
...readEnvironment(path.join(root, ".env"))
|
|
335
|
-
}
|
|
336
|
-
const addonId = environment.JCHAT_SANDBOX_ADDON_ID
|
|
337
|
-
if (!addonId) throw new Error("JCHAT_SANDBOX_ADDON_ID is missing from .env")
|
|
338
|
-
const compose = [
|
|
339
|
-
"docker",
|
|
340
|
-
"compose",
|
|
341
|
-
"--project-name",
|
|
342
|
-
\`jchat-addon-devkit-\${addonId}\`,
|
|
343
|
-
"--file",
|
|
344
|
-
path.join(root, "docker-compose.yml")
|
|
345
|
-
]
|
|
346
|
-
|
|
347
|
-
if (command === "test") {
|
|
348
|
-
await run(["bun", "test"], path.join(root, "addon"), environment)
|
|
349
|
-
} else if (command === "package") {
|
|
350
|
-
await run(["bun", "run", "build"], path.join(root, "addon"), environment)
|
|
351
|
-
} else if (command === "cleanup") {
|
|
352
|
-
await run([...compose, "down", "--remove-orphans", "--volumes"], root, environment)
|
|
353
|
-
} else {
|
|
354
|
-
if (command === "reset") {
|
|
355
|
-
await run([...compose, "down", "--remove-orphans", "--volumes"], root, environment)
|
|
356
|
-
}
|
|
357
|
-
await run(["bun", "run", "build"], path.join(root, "addon"), environment)
|
|
358
|
-
const watcher = Bun.spawn(["bun", "run", "dev"], {
|
|
359
|
-
cwd: path.join(root, "addon"),
|
|
360
|
-
env: environment,
|
|
361
|
-
stdout: "inherit",
|
|
362
|
-
stderr: "inherit"
|
|
363
|
-
})
|
|
364
|
-
try {
|
|
365
|
-
await run([...compose, "up"], root, environment)
|
|
366
|
-
} finally {
|
|
367
|
-
watcher.kill()
|
|
368
|
-
await watcher.exited
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async function run(command: string[], cwd: string, env: Record<string, string | undefined>) {
|
|
373
|
-
const child = Bun.spawn(command, { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" })
|
|
374
|
-
const exitCode = await child.exited
|
|
375
|
-
if (exitCode !== 0) throw new Error(\`\${command.join(" ")} exited with code \${exitCode}\`)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function readEnvironment(file: string): Record<string, string> {
|
|
379
|
-
if (!fs.existsSync(file)) throw new Error("Copy .env.example to .env before running the kit")
|
|
380
|
-
return Object.fromEntries(
|
|
381
|
-
fs.readFileSync(file, "utf8")
|
|
382
|
-
.split(/\\r?\\n/)
|
|
383
|
-
.filter((line) => line && !line.startsWith("#"))
|
|
384
|
-
.map((line) => {
|
|
385
|
-
const separator = line.indexOf("=")
|
|
386
|
-
return [line.slice(0, separator), line.slice(separator + 1)]
|
|
387
|
-
})
|
|
388
|
-
)
|
|
389
|
-
}
|
|
390
|
-
`
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function createReadme(addonId: string): string {
|
|
394
|
-
return `# ${toDisplayName(addonId)} Development Kit
|
|
395
|
-
|
|
396
|
-
This workspace develops \`${addonId}\` against the license-free JChat developer sandbox without
|
|
397
|
-
including JChat Core source.
|
|
398
|
-
|
|
399
|
-
## Start
|
|
400
|
-
|
|
401
|
-
\`\`\`powershell
|
|
402
|
-
Copy-Item .env.example .env
|
|
403
|
-
bun install
|
|
404
|
-
bun sandbox
|
|
405
|
-
\`\`\`
|
|
406
|
-
|
|
407
|
-
Open \`http://127.0.0.1:4317\`. The HTTP fixture recorder is available at
|
|
408
|
-
\`http://127.0.0.1:4318/requests\`.
|
|
409
|
-
|
|
410
|
-
## Commands
|
|
411
|
-
|
|
412
|
-
- \`bun sandbox\`
|
|
413
|
-
- \`bun sandbox:test\`
|
|
414
|
-
- \`bun sandbox:reset\`
|
|
415
|
-
- \`bun sandbox:package\`
|
|
416
|
-
- \`bun sandbox:cleanup\`
|
|
417
|
-
|
|
418
|
-
The referenced JChat packages and sandbox image must be published before this standalone kit can
|
|
419
|
-
install or start outside the JChat repository.
|
|
420
|
-
`
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function toDisplayName(addonId: string): string {
|
|
424
|
-
return addonId
|
|
425
|
-
.split("-")
|
|
426
|
-
.map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
|
|
427
|
-
.join(" ")
|
|
428
|
-
}
|