@hamak/smart-data-dico 1.9.0 → 1.9.2
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/backend/dist/server.mjs +29 -13
- package/backend/dist/validate.mjs +10559 -0
- package/bin/cli.js +113 -39
- package/frontend/dist/assets/{index-399555bb.js → index-5d106eea.js} +75 -75
- package/frontend/dist/index.html +1 -1
- package/package.json +2 -2
package/bin/cli.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
|
-
import { existsSync, mkdirSync, cpSync, writeFileSync } from 'fs';
|
|
6
|
-
import {
|
|
5
|
+
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync } from 'fs';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { spawn, spawnSync } from 'child_process';
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = dirname(__filename);
|
|
@@ -16,6 +17,10 @@ for (let i = 0; i < args.length; i++) {
|
|
|
16
17
|
if (args[i] === '--port' && args[i + 1]) { flags.port = args[++i]; }
|
|
17
18
|
else if (args[i] === '--data-dir' && args[i + 1]) { flags.dataDir = args[++i]; }
|
|
18
19
|
else if (args[i] === '--no-open') { flags.noOpen = true; }
|
|
20
|
+
else if (args[i] === '--validate') {
|
|
21
|
+
// `--validate [folder]` — folder is optional; defaults to the data dir.
|
|
22
|
+
flags.validate = (args[i + 1] && !args[i + 1].startsWith('-')) ? args[++i] : true;
|
|
23
|
+
}
|
|
19
24
|
else if (args[i] === '--help' || args[i] === '-h') { flags.help = true; }
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -30,6 +35,8 @@ if (flags.help) {
|
|
|
30
35
|
Options:
|
|
31
36
|
--port <number> Server port (default: 3001)
|
|
32
37
|
--data-dir <path> Data directory path (default: ./data-dictionaries)
|
|
38
|
+
--validate [path] Validate a project folder and exit (no server).
|
|
39
|
+
Defaults to the data dir. Exit code 1 on errors.
|
|
33
40
|
--no-open Don't open browser automatically
|
|
34
41
|
-h, --help Show this help
|
|
35
42
|
|
|
@@ -37,10 +44,47 @@ if (flags.help) {
|
|
|
37
44
|
smart-data-dico
|
|
38
45
|
smart-data-dico --port 4000
|
|
39
46
|
smart-data-dico --data-dir ~/my-dictionaries
|
|
47
|
+
smart-data-dico --validate ./my-project
|
|
48
|
+
npx @hamak/smart-data-dico --validate ./my-project
|
|
40
49
|
`);
|
|
41
50
|
process.exit(0);
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
// --validate: run the standalone project validator and exit (no server).
|
|
54
|
+
// Mirrors the server's bundled/source dual-mode resolution below.
|
|
55
|
+
if (flags.validate !== undefined) {
|
|
56
|
+
const folder = resolve(
|
|
57
|
+
typeof flags.validate === 'string'
|
|
58
|
+
? flags.validate
|
|
59
|
+
: (flags.dataDir || process.env.DATA_DIR || './data-dictionaries'),
|
|
60
|
+
);
|
|
61
|
+
const bundledValidator = join(PKG_ROOT, 'backend', 'dist', 'validate.mjs');
|
|
62
|
+
const sourceValidator = join(PKG_ROOT, 'backend', 'src', 'scripts', 'validateDico.ts');
|
|
63
|
+
|
|
64
|
+
let vbin, vargs;
|
|
65
|
+
if (existsSync(bundledValidator)) {
|
|
66
|
+
vbin = process.execPath; // node — bundle has all deps inlined
|
|
67
|
+
vargs = [bundledValidator, '--data-dir', folder];
|
|
68
|
+
} else if (existsSync(sourceValidator)) {
|
|
69
|
+
const tsx = [
|
|
70
|
+
join(PKG_ROOT, 'node_modules', '.bin', 'tsx'),
|
|
71
|
+
join(PKG_ROOT, 'backend', 'node_modules', '.bin', 'tsx'),
|
|
72
|
+
].find(p => existsSync(p));
|
|
73
|
+
vbin = tsx || 'npx';
|
|
74
|
+
vargs = tsx ? [sourceValidator, '--data-dir', folder] : ['tsx', sourceValidator, '--data-dir', folder];
|
|
75
|
+
} else {
|
|
76
|
+
console.error('Error: validator not found (neither bundled nor source).');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const r = spawnSync(vbin, vargs, { cwd: PKG_ROOT, stdio: 'inherit', env: process.env });
|
|
81
|
+
if (r.error) {
|
|
82
|
+
console.error('Failed to run validator:', r.error.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
process.exit(r.status ?? 0);
|
|
86
|
+
}
|
|
87
|
+
|
|
44
88
|
const port = flags.port || process.env.PORT || '3001';
|
|
45
89
|
const dataDir = resolve(flags.dataDir || process.env.DATA_DIR || './data-dictionaries');
|
|
46
90
|
|
|
@@ -97,56 +141,86 @@ if (existsSync(bundledServer)) {
|
|
|
97
141
|
|
|
98
142
|
const frontendDist = join(PKG_ROOT, 'frontend', 'dist');
|
|
99
143
|
|
|
100
|
-
|
|
144
|
+
// In-app "Open project" can't hot-swap the boot-time data dir, so the server
|
|
145
|
+
// persists the new project here and exits with RESTART_EXIT_CODE; we respawn
|
|
146
|
+
// it with DATA_DIR set to that path. SDD_MANAGED=1 tells the server it's safe
|
|
147
|
+
// to use this restart path.
|
|
148
|
+
const RESTART_EXIT_CODE = 75;
|
|
149
|
+
const ACTIVE_PROJECT_FILE = join(homedir(), '.dico-app', 'active-project');
|
|
150
|
+
|
|
151
|
+
let currentChild = null;
|
|
152
|
+
let browserOpened = false;
|
|
153
|
+
|
|
154
|
+
function startServer(dir) {
|
|
155
|
+
console.log(`
|
|
101
156
|
Smart Data Dictionary
|
|
102
157
|
|
|
103
158
|
Port: ${port}
|
|
104
|
-
Data: ${
|
|
159
|
+
Data: ${dir}
|
|
105
160
|
Profile: ${process.env.PROFILE || 'local'}
|
|
106
161
|
Frontend: ${existsSync(frontendDist) ? 'bundled' : 'dev (use frontend dev server on :3000)'}
|
|
107
162
|
`);
|
|
108
163
|
|
|
109
|
-
const child = spawn(bin, binArgs, {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.log(`Open ${url} in your browser`);
|
|
164
|
+
const child = spawn(bin, binArgs, {
|
|
165
|
+
cwd: PKG_ROOT,
|
|
166
|
+
env: {
|
|
167
|
+
...process.env,
|
|
168
|
+
PORT: port,
|
|
169
|
+
NODE_ENV: 'production',
|
|
170
|
+
PROFILE: process.env.PROFILE || 'local',
|
|
171
|
+
DATA_DIR: dir,
|
|
172
|
+
SDD_FRONTEND_DIST: frontendDist,
|
|
173
|
+
SDD_MANAGED: '1',
|
|
174
|
+
},
|
|
175
|
+
stdio: 'inherit',
|
|
176
|
+
});
|
|
177
|
+
currentChild = child;
|
|
178
|
+
|
|
179
|
+
child.on('error', (err) => {
|
|
180
|
+
console.error('Failed to start server:', err.message);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
child.on('exit', (code) => {
|
|
185
|
+
if (code === RESTART_EXIT_CODE) {
|
|
186
|
+
// Project switch requested — read the new dir and respawn.
|
|
187
|
+
let nextDir = dir;
|
|
188
|
+
try {
|
|
189
|
+
const persisted = readFileSync(ACTIVE_PROJECT_FILE, 'utf-8').trim();
|
|
190
|
+
if (persisted) nextDir = persisted;
|
|
191
|
+
} catch { /* keep current dir */ }
|
|
192
|
+
console.log(`\nSwitching project → ${nextDir}\n`);
|
|
193
|
+
startServer(nextDir);
|
|
194
|
+
return;
|
|
141
195
|
}
|
|
142
|
-
|
|
196
|
+
process.exit(code || 0);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Open the browser once, on the first start only (not on project-switch restarts).
|
|
200
|
+
if (!flags.noOpen && !browserOpened) {
|
|
201
|
+
browserOpened = true;
|
|
202
|
+
setTimeout(async () => {
|
|
203
|
+
const url = `http://localhost:${port}`;
|
|
204
|
+
console.log(`Opening ${url} ...`);
|
|
205
|
+
try {
|
|
206
|
+
const { exec } = await import('child_process');
|
|
207
|
+
const cmd = process.platform === 'darwin' ? 'open' :
|
|
208
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
209
|
+
exec(`${cmd} ${url}`);
|
|
210
|
+
} catch {
|
|
211
|
+
console.log(`Open ${url} in your browser`);
|
|
212
|
+
}
|
|
213
|
+
}, 3000);
|
|
214
|
+
}
|
|
143
215
|
}
|
|
144
216
|
|
|
217
|
+
startServer(dataDir);
|
|
218
|
+
|
|
145
219
|
process.on('SIGINT', () => {
|
|
146
220
|
console.log('\nShutting down...');
|
|
147
|
-
|
|
221
|
+
if (currentChild) currentChild.kill('SIGINT');
|
|
148
222
|
});
|
|
149
223
|
|
|
150
224
|
process.on('SIGTERM', () => {
|
|
151
|
-
|
|
225
|
+
if (currentChild) currentChild.kill('SIGTERM');
|
|
152
226
|
});
|