@wealthfolio/addon-dev-tools 1.0.0 → 2.0.0
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 +8 -1
- package/cli.js +91 -112
- package/dev-server.js +97 -96
- package/index.js +1 -1
- package/package.json +1 -2
- package/scaffold.js +43 -42
- package/templates/package.json.template +13 -11
- package/templates/vite.config.ts.template +3 -2
package/README.md
CHANGED
|
@@ -11,27 +11,32 @@ npm install -g @wealthfolio/addon-dev-tools
|
|
|
11
11
|
## CLI Commands
|
|
12
12
|
|
|
13
13
|
### Create New Addon
|
|
14
|
+
|
|
14
15
|
```bash
|
|
15
16
|
wealthfolio create my-awesome-addon
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
### Start Development Server
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
22
|
# In your addon directory
|
|
21
23
|
wealthfolio dev
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
### Build Addon
|
|
27
|
+
|
|
25
28
|
```bash
|
|
26
29
|
wealthfolio build
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
### Package for Distribution
|
|
33
|
+
|
|
30
34
|
```bash
|
|
31
35
|
wealthfolio package
|
|
32
36
|
```
|
|
33
37
|
|
|
34
38
|
### Test Setup
|
|
39
|
+
|
|
35
40
|
```bash
|
|
36
41
|
wealthfolio test
|
|
37
42
|
```
|
|
@@ -39,6 +44,7 @@ wealthfolio test
|
|
|
39
44
|
## Development Server
|
|
40
45
|
|
|
41
46
|
The development server provides:
|
|
47
|
+
|
|
42
48
|
- Hot reload functionality
|
|
43
49
|
- File watching
|
|
44
50
|
- Auto-building
|
|
@@ -71,8 +77,9 @@ Add to your addon's `package.json`:
|
|
|
71
77
|
## Architecture
|
|
72
78
|
|
|
73
79
|
This package is separate from `@wealthfolio/addon-sdk` to:
|
|
80
|
+
|
|
74
81
|
- Keep the SDK lightweight for production
|
|
75
|
-
- Avoid unnecessary dependencies in addon bundles
|
|
82
|
+
- Avoid unnecessary dependencies in addon bundles
|
|
76
83
|
- Provide optional development tooling
|
|
77
84
|
|
|
78
85
|
## License
|
package/cli.js
CHANGED
|
@@ -2,31 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Wealthfolio Addon CLI
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Command-line tool for developing, building, and managing addons
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const { program } = require(
|
|
10
|
-
const fs = require(
|
|
11
|
-
const path = require(
|
|
12
|
-
const { exec, spawn } = require(
|
|
13
|
-
const { promisify } = require(
|
|
14
|
-
const readline = require(
|
|
15
|
-
const { stdin, stdout } = require(
|
|
16
|
-
const { AddonScaffold } = require(
|
|
9
|
+
const { program } = require("commander");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const { exec, spawn } = require("child_process");
|
|
13
|
+
const { promisify } = require("util");
|
|
14
|
+
const readline = require("node:readline/promises");
|
|
15
|
+
const { stdin, stdout } = require("node:process");
|
|
16
|
+
const { AddonScaffold } = require("./scaffold");
|
|
17
17
|
|
|
18
18
|
const execAsync = promisify(exec);
|
|
19
19
|
|
|
20
20
|
// Colors for console output
|
|
21
21
|
const colors = {
|
|
22
|
-
reset:
|
|
23
|
-
bright:
|
|
24
|
-
red:
|
|
25
|
-
green:
|
|
26
|
-
yellow:
|
|
27
|
-
blue:
|
|
28
|
-
magenta:
|
|
29
|
-
cyan:
|
|
22
|
+
reset: "\x1b[0m",
|
|
23
|
+
bright: "\x1b[1m",
|
|
24
|
+
red: "\x1b[31m",
|
|
25
|
+
green: "\x1b[32m",
|
|
26
|
+
yellow: "\x1b[33m",
|
|
27
|
+
blue: "\x1b[34m",
|
|
28
|
+
magenta: "\x1b[35m",
|
|
29
|
+
cyan: "\x1b[36m",
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
function log(message, color = colors.reset) {
|
|
@@ -56,19 +56,19 @@ const scaffold = new AddonScaffold();
|
|
|
56
56
|
async function createAddon(name, options) {
|
|
57
57
|
try {
|
|
58
58
|
info(`Creating new addon: ${name}`);
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
// Prepare configuration
|
|
61
61
|
const config = {
|
|
62
62
|
name,
|
|
63
63
|
description: options.description,
|
|
64
|
-
author: options.author
|
|
64
|
+
author: options.author,
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
// Validate configuration
|
|
68
68
|
const validationErrors = scaffold.validateConfig(config);
|
|
69
69
|
if (validationErrors.length > 0) {
|
|
70
|
-
error(
|
|
71
|
-
validationErrors.forEach(err => error(` - ${err}`));
|
|
70
|
+
error("Configuration errors:");
|
|
71
|
+
validationErrors.forEach((err) => error(` - ${err}`));
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -83,7 +83,7 @@ async function createAddon(name, options) {
|
|
|
83
83
|
config.description = answer.length > 0 ? answer : defaultDesc;
|
|
84
84
|
}
|
|
85
85
|
if (!config.author) {
|
|
86
|
-
const defaultAuthor =
|
|
86
|
+
const defaultAuthor = "Anonymous";
|
|
87
87
|
const answer = (await rl.question(`Author [${defaultAuthor}]: `)).trim();
|
|
88
88
|
config.author = answer.length > 0 ? answer : defaultAuthor;
|
|
89
89
|
}
|
|
@@ -97,25 +97,25 @@ async function createAddon(name, options) {
|
|
|
97
97
|
config.description = `A Wealthfolio addon for ${name}`;
|
|
98
98
|
}
|
|
99
99
|
if (!config.author) {
|
|
100
|
-
config.author =
|
|
100
|
+
config.author = "Anonymous";
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
const addonId = name.toLowerCase().replace(/[^a-z0-9]/g,
|
|
102
|
+
|
|
103
|
+
const addonId = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
104
104
|
const addonDir = path.resolve(process.cwd(), addonId);
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
// Check if directory already exists
|
|
107
107
|
if (fs.existsSync(addonDir)) {
|
|
108
108
|
error(`Directory ${addonId} already exists`);
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
// Add current date for changelog
|
|
113
|
-
const currentDate = new Date().toISOString().split(
|
|
113
|
+
const currentDate = new Date().toISOString().split("T")[0];
|
|
114
114
|
config.currentDate = currentDate;
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
// Create addon using scaffold service
|
|
117
117
|
const result = await scaffold.createAddon(config, addonDir);
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
success(`Addon ${name} created successfully!`);
|
|
120
120
|
info(`Directory: ${result.addonDir}`);
|
|
121
121
|
info(`Addon ID: ${result.addonId}`);
|
|
@@ -139,7 +139,6 @@ async function createAddon(name, options) {
|
|
|
139
139
|
info(` 1. cd ${addonId}`);
|
|
140
140
|
info(` 2. pnpm install`);
|
|
141
141
|
info(` 3. pnpm run dev:server`);
|
|
142
|
-
|
|
143
142
|
} catch (err) {
|
|
144
143
|
error(`Failed to create addon: ${err.message}`);
|
|
145
144
|
}
|
|
@@ -148,34 +147,33 @@ async function createAddon(name, options) {
|
|
|
148
147
|
// Command: dev
|
|
149
148
|
async function startDev(port = 3001) {
|
|
150
149
|
try {
|
|
151
|
-
const manifestPath = path.resolve(process.cwd(),
|
|
152
|
-
|
|
150
|
+
const manifestPath = path.resolve(process.cwd(), "manifest.json");
|
|
151
|
+
|
|
153
152
|
if (!fs.existsSync(manifestPath)) {
|
|
154
|
-
error(
|
|
153
|
+
error("No manifest.json found. Are you in an addon directory?");
|
|
155
154
|
return;
|
|
156
155
|
}
|
|
157
|
-
|
|
158
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath,
|
|
159
|
-
|
|
156
|
+
|
|
157
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
158
|
+
|
|
160
159
|
info(`Starting development server for ${manifest.name}`);
|
|
161
160
|
info(`Server will run on http://localhost:${port}`);
|
|
162
|
-
|
|
161
|
+
|
|
163
162
|
// Start the development server
|
|
164
|
-
const devServerPath = path.resolve(__dirname,
|
|
165
|
-
const child = spawn(
|
|
166
|
-
stdio:
|
|
163
|
+
const devServerPath = path.resolve(__dirname, "dev-server.js");
|
|
164
|
+
const child = spawn("node", [devServerPath, process.cwd(), port.toString()], {
|
|
165
|
+
stdio: "inherit",
|
|
167
166
|
});
|
|
168
|
-
|
|
167
|
+
|
|
169
168
|
// Handle cleanup on exit
|
|
170
|
-
process.on(
|
|
171
|
-
child.kill(
|
|
169
|
+
process.on("SIGINT", () => {
|
|
170
|
+
child.kill("SIGINT");
|
|
172
171
|
process.exit(0);
|
|
173
172
|
});
|
|
174
|
-
|
|
175
|
-
child.on(
|
|
173
|
+
|
|
174
|
+
child.on("exit", (code) => {
|
|
176
175
|
process.exit(code);
|
|
177
176
|
});
|
|
178
|
-
|
|
179
177
|
} catch (err) {
|
|
180
178
|
error(`Failed to start development server: ${err.message}`);
|
|
181
179
|
}
|
|
@@ -184,17 +182,16 @@ async function startDev(port = 3001) {
|
|
|
184
182
|
// Command: build
|
|
185
183
|
async function buildAddon() {
|
|
186
184
|
try {
|
|
187
|
-
info(
|
|
188
|
-
|
|
189
|
-
const packageJsonPath = path.resolve(process.cwd(),
|
|
185
|
+
info("Building addon...");
|
|
186
|
+
|
|
187
|
+
const packageJsonPath = path.resolve(process.cwd(), "package.json");
|
|
190
188
|
if (!fs.existsSync(packageJsonPath)) {
|
|
191
|
-
error(
|
|
189
|
+
error("No package.json found. Are you in an addon directory?");
|
|
192
190
|
return;
|
|
193
191
|
}
|
|
194
|
-
|
|
195
|
-
await execAsync(
|
|
196
|
-
success(
|
|
197
|
-
|
|
192
|
+
|
|
193
|
+
await execAsync("pnpm run build");
|
|
194
|
+
success("Addon built successfully!");
|
|
198
195
|
} catch (err) {
|
|
199
196
|
error(`Build failed: ${err.message}`);
|
|
200
197
|
}
|
|
@@ -203,15 +200,14 @@ async function buildAddon() {
|
|
|
203
200
|
// Command: package
|
|
204
201
|
async function packageAddon() {
|
|
205
202
|
try {
|
|
206
|
-
info(
|
|
207
|
-
|
|
203
|
+
info("Packaging addon...");
|
|
204
|
+
|
|
208
205
|
// Build first
|
|
209
206
|
await buildAddon();
|
|
210
|
-
|
|
207
|
+
|
|
211
208
|
// Create package
|
|
212
|
-
await execAsync(
|
|
213
|
-
success(
|
|
214
|
-
|
|
209
|
+
await execAsync("pnpm run package");
|
|
210
|
+
success("Addon packaged successfully!");
|
|
215
211
|
} catch (err) {
|
|
216
212
|
error(`Packaging failed: ${err.message}`);
|
|
217
213
|
}
|
|
@@ -220,44 +216,43 @@ async function packageAddon() {
|
|
|
220
216
|
// Command: test
|
|
221
217
|
async function testSetup() {
|
|
222
218
|
try {
|
|
223
|
-
info(
|
|
224
|
-
|
|
219
|
+
info("Testing addon development setup...");
|
|
220
|
+
|
|
225
221
|
// Check if manifest exists
|
|
226
|
-
const manifestPath = path.resolve(process.cwd(),
|
|
222
|
+
const manifestPath = path.resolve(process.cwd(), "manifest.json");
|
|
227
223
|
if (!fs.existsSync(manifestPath)) {
|
|
228
|
-
error(
|
|
224
|
+
error("❌ No manifest.json found. Are you in an addon directory?");
|
|
229
225
|
return;
|
|
230
226
|
}
|
|
231
|
-
|
|
232
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath,
|
|
227
|
+
|
|
228
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
233
229
|
success(`✅ Found manifest for: ${manifest.name}`);
|
|
234
|
-
|
|
230
|
+
|
|
235
231
|
// Check if dist exists
|
|
236
|
-
const distPath = path.resolve(process.cwd(),
|
|
232
|
+
const distPath = path.resolve(process.cwd(), "dist");
|
|
237
233
|
if (!fs.existsSync(distPath)) {
|
|
238
|
-
warn(
|
|
234
|
+
warn("⚠️ No dist directory found. Run `pnpm run build` first.");
|
|
239
235
|
} else {
|
|
240
|
-
success(
|
|
236
|
+
success("✅ Dist directory exists");
|
|
241
237
|
}
|
|
242
|
-
|
|
238
|
+
|
|
243
239
|
// Check if dev server is running
|
|
244
240
|
try {
|
|
245
|
-
const response = await fetch(
|
|
241
|
+
const response = await fetch("http://localhost:3001/test");
|
|
246
242
|
if (response.ok) {
|
|
247
243
|
const data = await response.json();
|
|
248
|
-
success(
|
|
244
|
+
success("✅ Development server is running");
|
|
249
245
|
info(` Server message: ${data.message}`);
|
|
250
246
|
}
|
|
251
247
|
} catch (error) {
|
|
252
|
-
warn(
|
|
253
|
-
info(
|
|
248
|
+
warn("⚠️ Development server not running on port 3001");
|
|
249
|
+
info(" Start it with: pnpm run dev:server");
|
|
254
250
|
}
|
|
255
|
-
|
|
256
|
-
info(
|
|
257
|
-
info(
|
|
258
|
-
info(
|
|
259
|
-
info(
|
|
260
|
-
|
|
251
|
+
|
|
252
|
+
info("\nNext steps:");
|
|
253
|
+
info("1. Start dev server: pnpm run dev:server");
|
|
254
|
+
info("2. Start main app in dev mode");
|
|
255
|
+
info("3. Check console: discoverAddons()");
|
|
261
256
|
} catch (err) {
|
|
262
257
|
error(`Test failed: ${err.message}`);
|
|
263
258
|
}
|
|
@@ -267,52 +262,36 @@ async function testSetup() {
|
|
|
267
262
|
async function installAddon(zipPath) {
|
|
268
263
|
try {
|
|
269
264
|
info(`Installing addon from ${zipPath}`);
|
|
270
|
-
|
|
265
|
+
|
|
271
266
|
// This would integrate with the main app's addon installation
|
|
272
|
-
warn(
|
|
273
|
-
|
|
267
|
+
warn("Install command not yet implemented. Use the main app to install.");
|
|
274
268
|
} catch (err) {
|
|
275
269
|
error(`Installation failed: ${err.message}`);
|
|
276
270
|
}
|
|
277
271
|
}
|
|
278
272
|
|
|
279
273
|
// CLI Setup
|
|
280
|
-
program
|
|
281
|
-
.name('wealthfolio')
|
|
282
|
-
.description('Wealthfolio Addon Development CLI')
|
|
283
|
-
.version('1.0.0');
|
|
274
|
+
program.name("wealthfolio").description("Wealthfolio Addon Development CLI").version("1.0.0");
|
|
284
275
|
|
|
285
276
|
program
|
|
286
|
-
.command(
|
|
287
|
-
.description(
|
|
288
|
-
.option(
|
|
289
|
-
.option(
|
|
277
|
+
.command("create <name>")
|
|
278
|
+
.description("Create a new addon")
|
|
279
|
+
.option("-d, --description <desc>", "Addon description")
|
|
280
|
+
.option("-a, --author <author>", "Addon author")
|
|
290
281
|
.action(createAddon);
|
|
291
282
|
|
|
292
283
|
program
|
|
293
|
-
.command(
|
|
294
|
-
.description(
|
|
295
|
-
.option(
|
|
284
|
+
.command("dev")
|
|
285
|
+
.description("Start development server")
|
|
286
|
+
.option("-p, --port <port>", "Port number", "3001")
|
|
296
287
|
.action((options) => startDev(parseInt(options.port)));
|
|
297
288
|
|
|
298
|
-
program
|
|
299
|
-
.command('build')
|
|
300
|
-
.description('Build the addon')
|
|
301
|
-
.action(buildAddon);
|
|
289
|
+
program.command("build").description("Build the addon").action(buildAddon);
|
|
302
290
|
|
|
303
|
-
program
|
|
304
|
-
.command('package')
|
|
305
|
-
.description('Package the addon for distribution')
|
|
306
|
-
.action(packageAddon);
|
|
291
|
+
program.command("package").description("Package the addon for distribution").action(packageAddon);
|
|
307
292
|
|
|
308
|
-
program
|
|
309
|
-
.command('test')
|
|
310
|
-
.description('Test addon development setup')
|
|
311
|
-
.action(testSetup);
|
|
293
|
+
program.command("test").description("Test addon development setup").action(testSetup);
|
|
312
294
|
|
|
313
|
-
program
|
|
314
|
-
.command('install <zip>')
|
|
315
|
-
.description('Install an addon from zip file')
|
|
316
|
-
.action(installAddon);
|
|
295
|
+
program.command("install <zip>").description("Install an addon from zip file").action(installAddon);
|
|
317
296
|
|
|
318
297
|
program.parse();
|
package/dev-server.js
CHANGED
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Addon Development Server
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* A simple development server for hot reloading addons during development.
|
|
8
8
|
* This server watches for file changes and provides a hot reload endpoint.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const express = require(
|
|
12
|
-
const cors = require(
|
|
13
|
-
const chokidar = require(
|
|
14
|
-
const path = require(
|
|
15
|
-
const fs = require(
|
|
16
|
-
const { exec } = require(
|
|
17
|
-
const { promisify } = require(
|
|
11
|
+
const express = require("express");
|
|
12
|
+
const cors = require("cors");
|
|
13
|
+
const chokidar = require("chokidar");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const { exec } = require("child_process");
|
|
17
|
+
const { promisify } = require("util");
|
|
18
18
|
|
|
19
19
|
const execAsync = promisify(exec);
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ class AddonDevServer {
|
|
|
25
25
|
this.lastModified = new Date();
|
|
26
26
|
this.buildInProgress = false;
|
|
27
27
|
this.viteWatcher = null;
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
this.setupMiddleware();
|
|
30
30
|
this.setupRoutes();
|
|
31
31
|
this.setupFileWatcher();
|
|
@@ -33,76 +33,80 @@ class AddonDevServer {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
setupMiddleware() {
|
|
36
|
-
this.app.use(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
this.app.use(
|
|
37
|
+
cors({
|
|
38
|
+
origin: ["http://localhost:1420", "http://localhost:3000"],
|
|
39
|
+
credentials: true,
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
40
42
|
this.app.use(express.static(this.config.addonPath));
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
setupRoutes() {
|
|
44
46
|
// Health check endpoint
|
|
45
|
-
this.app.get(
|
|
47
|
+
this.app.get("/health", (req, res) => {
|
|
46
48
|
res.json({
|
|
47
|
-
status:
|
|
49
|
+
status: "ok",
|
|
48
50
|
timestamp: new Date().toISOString(),
|
|
49
|
-
addonPath: this.config.addonPath
|
|
51
|
+
addonPath: this.config.addonPath,
|
|
50
52
|
});
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
// Addon status endpoint
|
|
54
|
-
this.app.get(
|
|
56
|
+
this.app.get("/status", (req, res) => {
|
|
55
57
|
res.json({
|
|
56
58
|
lastModified: this.lastModified.toISOString(),
|
|
57
59
|
buildInProgress: this.buildInProgress,
|
|
58
|
-
files: this.getFileList()
|
|
60
|
+
files: this.getFileList(),
|
|
59
61
|
});
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
// Serve addon manifest
|
|
63
|
-
this.app.get(
|
|
65
|
+
this.app.get("/manifest.json", (req, res) => {
|
|
64
66
|
try {
|
|
65
67
|
const manifestPath = path.resolve(this.config.manifestPath);
|
|
66
68
|
if (fs.existsSync(manifestPath)) {
|
|
67
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath,
|
|
69
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
68
70
|
res.json(manifest);
|
|
69
71
|
} else {
|
|
70
|
-
res.status(404).json({ error:
|
|
72
|
+
res.status(404).json({ error: "Manifest not found" });
|
|
71
73
|
}
|
|
72
74
|
} catch (error) {
|
|
73
|
-
res.status(500).json({ error:
|
|
75
|
+
res.status(500).json({ error: "Failed to read manifest" });
|
|
74
76
|
}
|
|
75
77
|
});
|
|
76
78
|
|
|
77
79
|
// Serve addon code
|
|
78
|
-
this.app.get(
|
|
80
|
+
this.app.get("/addon.js", async (req, res) => {
|
|
79
81
|
try {
|
|
80
|
-
const addonFile = path.resolve(this.config.addonPath,
|
|
82
|
+
const addonFile = path.resolve(this.config.addonPath, "dist/addon.js");
|
|
81
83
|
console.log(`📦 Serving addon.js from: ${addonFile}`);
|
|
82
|
-
|
|
84
|
+
|
|
83
85
|
// Wait for file to exist (with timeout)
|
|
84
86
|
const fileExists = await this.waitForFile(addonFile, 3000);
|
|
85
|
-
|
|
87
|
+
|
|
86
88
|
if (fileExists) {
|
|
87
|
-
const code = fs.readFileSync(addonFile,
|
|
88
|
-
res.type(
|
|
89
|
+
const code = fs.readFileSync(addonFile, "utf-8");
|
|
90
|
+
res.type("application/javascript").send(code);
|
|
89
91
|
} else {
|
|
90
92
|
console.error(`❌ Addon file not found at: ${addonFile}`);
|
|
91
|
-
res
|
|
93
|
+
res
|
|
94
|
+
.status(404)
|
|
95
|
+
.json({ error: "Addon file not found. Run build first.", path: addonFile });
|
|
92
96
|
}
|
|
93
97
|
} catch (error) {
|
|
94
98
|
console.error(`❌ Error serving addon.js:`, error);
|
|
95
|
-
res.status(500).json({ error:
|
|
99
|
+
res.status(500).json({ error: "Failed to read addon file", details: error.message });
|
|
96
100
|
}
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
// Hot reload endpoint
|
|
100
|
-
this.app.get(
|
|
104
|
+
this.app.get("/reload", (req, res) => {
|
|
101
105
|
res.json({
|
|
102
|
-
message:
|
|
103
|
-
timestamp: new Date().toISOString()
|
|
106
|
+
message: "Reload triggered",
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
104
108
|
});
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
// Trigger rebuild if configured
|
|
107
111
|
if (this.config.buildCommand) {
|
|
108
112
|
this.triggerBuild();
|
|
@@ -110,26 +114,26 @@ class AddonDevServer {
|
|
|
110
114
|
});
|
|
111
115
|
|
|
112
116
|
// File listing for debugging
|
|
113
|
-
this.app.get(
|
|
117
|
+
this.app.get("/files", (req, res) => {
|
|
114
118
|
res.json({
|
|
115
119
|
files: this.getFileList(),
|
|
116
|
-
watchPaths: this.config.watchPaths
|
|
120
|
+
watchPaths: this.config.watchPaths,
|
|
117
121
|
});
|
|
118
122
|
});
|
|
119
123
|
|
|
120
124
|
// Test endpoint for connectivity
|
|
121
|
-
this.app.get(
|
|
125
|
+
this.app.get("/test", (req, res) => {
|
|
122
126
|
res.json({
|
|
123
|
-
message:
|
|
127
|
+
message: "Addon development server is working!",
|
|
124
128
|
addonPath: this.config.addonPath,
|
|
125
129
|
timestamp: new Date().toISOString(),
|
|
126
|
-
manifest: this.getManifestInfo()
|
|
130
|
+
manifest: this.getManifestInfo(),
|
|
127
131
|
});
|
|
128
132
|
});
|
|
129
133
|
|
|
130
134
|
// Debug endpoint for troubleshooting
|
|
131
|
-
this.app.get(
|
|
132
|
-
const addonFile = path.resolve(this.config.addonPath,
|
|
135
|
+
this.app.get("/debug", (req, res) => {
|
|
136
|
+
const addonFile = path.resolve(this.config.addonPath, "dist/addon.js");
|
|
133
137
|
res.json({
|
|
134
138
|
lastModified: this.lastModified.toISOString(),
|
|
135
139
|
buildInProgress: this.buildInProgress,
|
|
@@ -139,18 +143,18 @@ class AddonDevServer {
|
|
|
139
143
|
addonFile: {
|
|
140
144
|
path: addonFile,
|
|
141
145
|
exists: fs.existsSync(addonFile),
|
|
142
|
-
size: fs.existsSync(addonFile) ? fs.statSync(addonFile).size : 0
|
|
146
|
+
size: fs.existsSync(addonFile) ? fs.statSync(addonFile).size : 0,
|
|
143
147
|
},
|
|
144
148
|
config: {
|
|
145
149
|
port: this.config.port,
|
|
146
|
-
buildCommand: this.config.buildCommand
|
|
147
|
-
}
|
|
150
|
+
buildCommand: this.config.buildCommand,
|
|
151
|
+
},
|
|
148
152
|
});
|
|
149
153
|
});
|
|
150
154
|
|
|
151
155
|
// Simple ping endpoint
|
|
152
|
-
this.app.get(
|
|
153
|
-
res.json({ message:
|
|
156
|
+
this.app.get("/ping", (req, res) => {
|
|
157
|
+
res.json({ message: "pong", timestamp: new Date().toISOString() });
|
|
154
158
|
});
|
|
155
159
|
}
|
|
156
160
|
|
|
@@ -158,44 +162,44 @@ class AddonDevServer {
|
|
|
158
162
|
const watcher = chokidar.watch(this.config.watchPaths, {
|
|
159
163
|
ignored: /node_modules|\.git/,
|
|
160
164
|
persistent: true,
|
|
161
|
-
ignoreInitial: true
|
|
165
|
+
ignoreInitial: true,
|
|
162
166
|
});
|
|
163
167
|
|
|
164
|
-
watcher.on(
|
|
168
|
+
watcher.on("change", (filePath) => {
|
|
165
169
|
console.log(`📝 File changed: ${filePath}`);
|
|
166
170
|
// Don't trigger manual build since Vite is already watching
|
|
167
171
|
// Just update the timestamp for status endpoint
|
|
168
172
|
this.lastModified = new Date();
|
|
169
173
|
});
|
|
170
174
|
|
|
171
|
-
watcher.on(
|
|
175
|
+
watcher.on("add", (filePath) => {
|
|
172
176
|
console.log(`➕ File added: ${filePath}`);
|
|
173
177
|
this.lastModified = new Date();
|
|
174
178
|
});
|
|
175
179
|
|
|
176
|
-
watcher.on(
|
|
180
|
+
watcher.on("unlink", (filePath) => {
|
|
177
181
|
console.log(`➖ File removed: ${filePath}`);
|
|
178
182
|
this.lastModified = new Date();
|
|
179
183
|
});
|
|
180
184
|
|
|
181
|
-
console.log(`👀 Watching files: ${this.config.watchPaths.join(
|
|
185
|
+
console.log(`👀 Watching files: ${this.config.watchPaths.join(", ")}`);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
async triggerBuild() {
|
|
185
189
|
if (this.buildInProgress || !this.config.buildCommand) return;
|
|
186
|
-
|
|
190
|
+
|
|
187
191
|
this.buildInProgress = true;
|
|
188
192
|
console.log(`🔨 Building addon with: ${this.config.buildCommand}`);
|
|
189
|
-
|
|
193
|
+
|
|
190
194
|
try {
|
|
191
195
|
await execAsync(this.config.buildCommand, {
|
|
192
|
-
cwd: this.config.addonPath
|
|
196
|
+
cwd: this.config.addonPath,
|
|
193
197
|
});
|
|
194
|
-
|
|
195
|
-
console.log(
|
|
198
|
+
|
|
199
|
+
console.log("✅ Build completed successfully");
|
|
196
200
|
this.lastModified = new Date();
|
|
197
201
|
} catch (error) {
|
|
198
|
-
console.error(
|
|
202
|
+
console.error("❌ Build failed:", error);
|
|
199
203
|
} finally {
|
|
200
204
|
this.buildInProgress = false;
|
|
201
205
|
}
|
|
@@ -203,9 +207,9 @@ class AddonDevServer {
|
|
|
203
207
|
|
|
204
208
|
getFileList() {
|
|
205
209
|
try {
|
|
206
|
-
const distPath = path.resolve(this.config.addonPath,
|
|
210
|
+
const distPath = path.resolve(this.config.addonPath, "dist");
|
|
207
211
|
if (fs.existsSync(distPath)) {
|
|
208
|
-
return fs.readdirSync(distPath).map(file => `dist/${file}`);
|
|
212
|
+
return fs.readdirSync(distPath).map((file) => `dist/${file}`);
|
|
209
213
|
}
|
|
210
214
|
return [];
|
|
211
215
|
} catch (error) {
|
|
@@ -217,7 +221,7 @@ class AddonDevServer {
|
|
|
217
221
|
try {
|
|
218
222
|
const manifestPath = path.resolve(this.config.manifestPath);
|
|
219
223
|
if (fs.existsSync(manifestPath)) {
|
|
220
|
-
return JSON.parse(fs.readFileSync(manifestPath,
|
|
224
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
221
225
|
}
|
|
222
226
|
return null;
|
|
223
227
|
} catch (error) {
|
|
@@ -231,7 +235,7 @@ class AddonDevServer {
|
|
|
231
235
|
async waitForFile(filePath, timeout = 3000) {
|
|
232
236
|
const startTime = Date.now();
|
|
233
237
|
const checkInterval = 100;
|
|
234
|
-
|
|
238
|
+
|
|
235
239
|
while (Date.now() - startTime < timeout) {
|
|
236
240
|
if (fs.existsSync(filePath)) {
|
|
237
241
|
// Additional check to ensure file is fully written
|
|
@@ -244,50 +248,50 @@ class AddonDevServer {
|
|
|
244
248
|
// File might be in the process of being written
|
|
245
249
|
}
|
|
246
250
|
}
|
|
247
|
-
|
|
248
|
-
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
251
|
+
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
249
253
|
}
|
|
250
|
-
|
|
254
|
+
|
|
251
255
|
return false;
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
startViteWatcher() {
|
|
255
259
|
if (!this.config.buildCommand) return;
|
|
256
|
-
|
|
257
|
-
console.log(
|
|
258
|
-
|
|
260
|
+
|
|
261
|
+
console.log("🔨 Starting Vite in watch mode...");
|
|
262
|
+
|
|
259
263
|
// Start vite build in watch mode
|
|
260
|
-
const { spawn } = require(
|
|
261
|
-
this.viteWatcher = spawn(
|
|
264
|
+
const { spawn } = require("child_process");
|
|
265
|
+
this.viteWatcher = spawn("npm", ["run", "dev"], {
|
|
262
266
|
cwd: this.config.addonPath,
|
|
263
|
-
stdio: [
|
|
267
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
264
268
|
});
|
|
265
|
-
|
|
266
|
-
this.viteWatcher.stdout.on(
|
|
269
|
+
|
|
270
|
+
this.viteWatcher.stdout.on("data", (data) => {
|
|
267
271
|
const output = data.toString();
|
|
268
272
|
console.log(`Vite output: ${output.trim()}`);
|
|
269
|
-
|
|
270
|
-
if (output.includes(
|
|
273
|
+
|
|
274
|
+
if (output.includes("build started")) {
|
|
271
275
|
this.buildInProgress = true;
|
|
272
276
|
}
|
|
273
|
-
|
|
274
|
-
if (output.includes(
|
|
277
|
+
|
|
278
|
+
if (output.includes("built in")) {
|
|
275
279
|
console.log(`✅ Vite rebuild completed`);
|
|
276
280
|
this.lastModified = new Date();
|
|
277
281
|
this.buildInProgress = false;
|
|
278
282
|
}
|
|
279
|
-
|
|
280
|
-
if (output.includes(
|
|
283
|
+
|
|
284
|
+
if (output.includes("watching for file changes")) {
|
|
281
285
|
console.log(`✅ Vite watcher ready`);
|
|
282
286
|
this.buildInProgress = false;
|
|
283
287
|
}
|
|
284
288
|
});
|
|
285
|
-
|
|
286
|
-
this.viteWatcher.stderr.on(
|
|
289
|
+
|
|
290
|
+
this.viteWatcher.stderr.on("data", (data) => {
|
|
287
291
|
console.error(`Vite error: ${data}`);
|
|
288
292
|
});
|
|
289
|
-
|
|
290
|
-
this.viteWatcher.on(
|
|
293
|
+
|
|
294
|
+
this.viteWatcher.on("close", (code) => {
|
|
291
295
|
if (code !== 0) {
|
|
292
296
|
console.error(`Vite watcher exited with code ${code}`);
|
|
293
297
|
}
|
|
@@ -299,30 +303,30 @@ class AddonDevServer {
|
|
|
299
303
|
console.log(`🚀 Addon dev server running on http://localhost:${this.config.port}`);
|
|
300
304
|
console.log(`📁 Serving from: ${this.config.addonPath}`);
|
|
301
305
|
console.log(`📋 Manifest: ${this.config.manifestPath}`);
|
|
302
|
-
console.log(`👀 Watching files: ${this.config.watchPaths.join(
|
|
303
|
-
|
|
306
|
+
console.log(`👀 Watching files: ${this.config.watchPaths.join(", ")}`);
|
|
307
|
+
|
|
304
308
|
if (this.config.buildCommand) {
|
|
305
309
|
console.log(`🔨 Build command: ${this.config.buildCommand}`);
|
|
306
310
|
}
|
|
307
311
|
});
|
|
308
312
|
|
|
309
313
|
// Handle graceful shutdown
|
|
310
|
-
process.on(
|
|
314
|
+
process.on("SIGINT", () => {
|
|
311
315
|
this.stop();
|
|
312
316
|
process.exit(0);
|
|
313
317
|
});
|
|
314
318
|
|
|
315
|
-
process.on(
|
|
319
|
+
process.on("SIGTERM", () => {
|
|
316
320
|
this.stop();
|
|
317
321
|
process.exit(0);
|
|
318
322
|
});
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
stop() {
|
|
322
|
-
console.log(
|
|
323
|
-
|
|
326
|
+
console.log("🛑 Shutting down dev server...");
|
|
327
|
+
|
|
324
328
|
if (this.viteWatcher) {
|
|
325
|
-
this.viteWatcher.kill(
|
|
329
|
+
this.viteWatcher.kill("SIGTERM");
|
|
326
330
|
this.viteWatcher = null;
|
|
327
331
|
}
|
|
328
332
|
}
|
|
@@ -333,16 +337,13 @@ function main() {
|
|
|
333
337
|
const args = process.argv.slice(2);
|
|
334
338
|
const addonPath = args[0] || process.cwd();
|
|
335
339
|
const port = parseInt(args[1]) || 3001;
|
|
336
|
-
|
|
340
|
+
|
|
337
341
|
const config = {
|
|
338
342
|
port,
|
|
339
343
|
addonPath: path.resolve(addonPath),
|
|
340
|
-
manifestPath: path.resolve(addonPath,
|
|
341
|
-
buildCommand:
|
|
342
|
-
watchPaths: [
|
|
343
|
-
path.resolve(addonPath, 'src'),
|
|
344
|
-
path.resolve(addonPath, 'manifest.json')
|
|
345
|
-
]
|
|
344
|
+
manifestPath: path.resolve(addonPath, "manifest.json"),
|
|
345
|
+
buildCommand: "npm run build",
|
|
346
|
+
watchPaths: [path.resolve(addonPath, "src"), path.resolve(addonPath, "manifest.json")],
|
|
346
347
|
};
|
|
347
348
|
|
|
348
349
|
// Check if addon directory exists
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wealthfolio/addon-dev-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Development tools for Wealthfolio addons - hot reload server and CLI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
"hot-reload"
|
|
15
15
|
],
|
|
16
16
|
"author": "Wealthfolio Team",
|
|
17
|
-
|
|
18
17
|
"license": "MIT",
|
|
19
18
|
"repository": {
|
|
20
19
|
"type": "git",
|
package/scaffold.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Scaffold service for creating new addons from templates
|
|
6
6
|
*/
|
|
7
7
|
class AddonScaffold {
|
|
8
8
|
constructor() {
|
|
9
|
-
this.templatesDir = path.join(__dirname,
|
|
9
|
+
this.templatesDir = path.join(__dirname, "templates");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -14,12 +14,13 @@ class AddonScaffold {
|
|
|
14
14
|
*/
|
|
15
15
|
getAvailableTemplates() {
|
|
16
16
|
if (!fs.existsSync(this.templatesDir)) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error("Templates directory not found");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
return fs
|
|
21
|
-
.
|
|
22
|
-
.
|
|
20
|
+
return fs
|
|
21
|
+
.readdirSync(this.templatesDir)
|
|
22
|
+
.filter((file) => file.endsWith(".template"))
|
|
23
|
+
.map((file) => file.replace(".template", ""));
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -27,12 +28,12 @@ class AddonScaffold {
|
|
|
27
28
|
*/
|
|
28
29
|
loadTemplate(templateName) {
|
|
29
30
|
const templatePath = path.join(this.templatesDir, `${templateName}.template`);
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
if (!fs.existsSync(templatePath)) {
|
|
32
33
|
throw new Error(`Template ${templateName} not found`);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
return fs.readFileSync(templatePath,
|
|
36
|
+
return fs.readFileSync(templatePath, "utf-8");
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -41,7 +42,7 @@ class AddonScaffold {
|
|
|
41
42
|
processTemplate(content, replacements) {
|
|
42
43
|
let result = content;
|
|
43
44
|
for (const [key, value] of Object.entries(replacements)) {
|
|
44
|
-
const pattern = new RegExp(`{{${key}}}`,
|
|
45
|
+
const pattern = new RegExp(`{{${key}}}`, "g");
|
|
45
46
|
result = result.replace(pattern, value);
|
|
46
47
|
}
|
|
47
48
|
return result;
|
|
@@ -51,9 +52,9 @@ class AddonScaffold {
|
|
|
51
52
|
* Generate replacements object from addon config
|
|
52
53
|
*/
|
|
53
54
|
generateReplacements(config) {
|
|
54
|
-
const addonId = config.name.toLowerCase().replace(/[^a-z0-9]/g,
|
|
55
|
+
const addonId = config.name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
55
56
|
const packageName = `wealthfolio-${addonId}-addon`;
|
|
56
|
-
const componentName = config.name.replace(/[^a-zA-Z0-9]/g,
|
|
57
|
+
const componentName = config.name.replace(/[^a-zA-Z0-9]/g, "");
|
|
57
58
|
|
|
58
59
|
return {
|
|
59
60
|
addonId,
|
|
@@ -61,7 +62,7 @@ class AddonScaffold {
|
|
|
61
62
|
packageName,
|
|
62
63
|
componentName,
|
|
63
64
|
description: config.description || `A Wealthfolio addon for ${config.name}`,
|
|
64
|
-
author: config.author ||
|
|
65
|
+
author: config.author || "Anonymous",
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -70,22 +71,22 @@ class AddonScaffold {
|
|
|
70
71
|
*/
|
|
71
72
|
async createAddon(config, targetDir) {
|
|
72
73
|
const replacements = this.generateReplacements(config);
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
// Create directory structure
|
|
75
76
|
if (!fs.existsSync(targetDir)) {
|
|
76
77
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
// Create src directory and subdirectories
|
|
80
|
-
const srcDir = path.join(targetDir,
|
|
81
|
-
const srcSubDirs = [
|
|
82
|
-
|
|
81
|
+
const srcDir = path.join(targetDir, "src");
|
|
82
|
+
const srcSubDirs = ["components", "hooks", "pages", "lib", "types"];
|
|
83
|
+
|
|
83
84
|
if (!fs.existsSync(srcDir)) {
|
|
84
85
|
fs.mkdirSync(srcDir);
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
+
|
|
87
88
|
// Create all subdirectories
|
|
88
|
-
srcSubDirs.forEach(subDir => {
|
|
89
|
+
srcSubDirs.forEach((subDir) => {
|
|
89
90
|
const subDirPath = path.join(srcDir, subDir);
|
|
90
91
|
if (!fs.existsSync(subDirPath)) {
|
|
91
92
|
fs.mkdirSync(subDirPath, { recursive: true });
|
|
@@ -94,18 +95,18 @@ class AddonScaffold {
|
|
|
94
95
|
|
|
95
96
|
// Template file mappings
|
|
96
97
|
const fileTemplates = [
|
|
97
|
-
{ template:
|
|
98
|
-
{ template:
|
|
99
|
-
{ template:
|
|
100
|
-
{ template:
|
|
101
|
-
{ template:
|
|
102
|
-
{ template:
|
|
103
|
-
{ template:
|
|
104
|
-
{ template:
|
|
105
|
-
{ template:
|
|
106
|
-
{ template:
|
|
107
|
-
{ template:
|
|
108
|
-
{ template:
|
|
98
|
+
{ template: "manifest.json", output: "manifest.json" },
|
|
99
|
+
{ template: "package.json", output: "package.json" },
|
|
100
|
+
{ template: "vite.config.ts", output: "vite.config.ts" },
|
|
101
|
+
{ template: "tsconfig.json", output: "tsconfig.json" },
|
|
102
|
+
{ template: "README.md", output: "README.md" },
|
|
103
|
+
{ template: "CHANGELOG.md", output: "CHANGELOG.md" },
|
|
104
|
+
{ template: "addon.tsx", output: "src/addon.tsx" },
|
|
105
|
+
{ template: "components-index.ts", output: "src/components/index.ts" },
|
|
106
|
+
{ template: "hooks-index.ts", output: "src/hooks/index.ts" },
|
|
107
|
+
{ template: "pages-index.ts", output: "src/pages/index.ts" },
|
|
108
|
+
{ template: "lib-index.ts", output: "src/lib/index.ts" },
|
|
109
|
+
{ template: "types-index.ts", output: "src/types/index.ts" },
|
|
109
110
|
];
|
|
110
111
|
|
|
111
112
|
// Process and write each template
|
|
@@ -114,13 +115,13 @@ class AddonScaffold {
|
|
|
114
115
|
const templateContent = this.loadTemplate(template);
|
|
115
116
|
const processedContent = this.processTemplate(templateContent, replacements);
|
|
116
117
|
const outputPath = path.join(targetDir, output);
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
// Ensure directory exists
|
|
119
120
|
const outputDir = path.dirname(outputPath);
|
|
120
121
|
if (!fs.existsSync(outputDir)) {
|
|
121
122
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
+
|
|
124
125
|
fs.writeFileSync(outputPath, processedContent);
|
|
125
126
|
} catch (error) {
|
|
126
127
|
throw new Error(`Failed to process template ${template}: ${error.message}`);
|
|
@@ -130,7 +131,7 @@ class AddonScaffold {
|
|
|
130
131
|
return {
|
|
131
132
|
addonDir: targetDir,
|
|
132
133
|
addonId: replacements.addonId,
|
|
133
|
-
packageName: replacements.packageName
|
|
134
|
+
packageName: replacements.packageName,
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -140,20 +141,20 @@ class AddonScaffold {
|
|
|
140
141
|
validateConfig(config) {
|
|
141
142
|
const errors = [];
|
|
142
143
|
|
|
143
|
-
if (!config.name || typeof config.name !==
|
|
144
|
-
errors.push(
|
|
144
|
+
if (!config.name || typeof config.name !== "string") {
|
|
145
|
+
errors.push("Addon name is required and must be a string");
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
if (config.name && config.name.trim().length === 0) {
|
|
148
|
-
errors.push(
|
|
149
|
+
errors.push("Addon name cannot be empty");
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
if (config.description && typeof config.description !==
|
|
152
|
-
errors.push(
|
|
152
|
+
if (config.description && typeof config.description !== "string") {
|
|
153
|
+
errors.push("Description must be a string");
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
if (config.author && typeof config.author !==
|
|
156
|
-
errors.push(
|
|
156
|
+
if (config.author && typeof config.author !== "string") {
|
|
157
|
+
errors.push("Author must be a string");
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
return errors;
|
|
@@ -17,19 +17,21 @@
|
|
|
17
17
|
"type-check": "tsc --noEmit"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@wealthfolio/addon-sdk": "^
|
|
21
|
-
"@wealthfolio/ui": "^
|
|
22
|
-
"react": "^
|
|
23
|
-
"react-dom": "^
|
|
20
|
+
"@wealthfolio/addon-sdk": "^2.0.0",
|
|
21
|
+
"@wealthfolio/ui": "^2.0.0",
|
|
22
|
+
"react": "^19.1.1",
|
|
23
|
+
"react-dom": "^19.1.1"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@types/
|
|
29
|
-
"@types/react
|
|
30
|
-
"@
|
|
26
|
+
"@tailwindcss/vite": "^4.1.13",
|
|
27
|
+
"@wealthfolio/addon-dev-tools": "^2.0.0",
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"@types/react": "^19.1.1",
|
|
30
|
+
"@types/react-dom": "^19.1.1",
|
|
31
|
+
"@vitejs/plugin-react": "^5.0.2",
|
|
31
32
|
"rollup-plugin-external-globals": "^0.13.0",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
33
|
+
"tailwindcss": "^4.1.13",
|
|
34
|
+
"typescript": "^5.9.2",
|
|
35
|
+
"vite": "^7.1.5"
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { defineConfig } from 'vite';
|
|
2
1
|
import react from '@vitejs/plugin-react';
|
|
2
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
3
4
|
import externalGlobals from 'rollup-plugin-external-globals';
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
|
-
plugins: [react()],
|
|
7
|
+
plugins: [react(), tailwindcss()],
|
|
7
8
|
define: {
|
|
8
9
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
9
10
|
},
|