@uipkge/nuxt 0.1.34 → 0.1.36
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/cli.mjs +290 -66
- package/dist/module.json +1 -1
- package/package.json +5 -3
package/bin/cli.mjs
CHANGED
|
@@ -1,107 +1,331 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from
|
|
3
|
-
import fs from
|
|
4
|
-
import path from
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { createInterface } from "readline";
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const TARGET_DIR = process.cwd()
|
|
8
|
-
const distDir = path.join(PKG_DIR, 'dist')
|
|
7
|
+
const TARGET_DIR = process.cwd();
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
11
10
|
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
function prompt(question) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
14
|
+
rl.question(question, (ans) => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(ans.trim());
|
|
17
|
+
});
|
|
18
|
+
});
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
function
|
|
18
|
-
|
|
21
|
+
function parseFlags(args) {
|
|
22
|
+
const flags = {};
|
|
23
|
+
for (const arg of args) {
|
|
24
|
+
const match = arg.match(/^--([a-zA-Z-]+)=(.+)$/);
|
|
25
|
+
if (match) {
|
|
26
|
+
// Normalize camelCase to kebab-case: projectId → project-id
|
|
27
|
+
const key = match[1].replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
28
|
+
flags[key] = match[2];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return flags;
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
|
|
34
|
+
function detectPackageManager() {
|
|
35
|
+
if (fs.existsSync(path.join(TARGET_DIR, "bun.lock")) || fs.existsSync(path.join(TARGET_DIR, "bun.lockb")))
|
|
36
|
+
return "bun";
|
|
37
|
+
if (fs.existsSync(path.join(TARGET_DIR, "pnpm-lock.yaml"))) return "pnpm";
|
|
38
|
+
if (fs.existsSync(path.join(TARGET_DIR, "yarn.lock"))) return "yarn";
|
|
39
|
+
return "npm";
|
|
40
|
+
}
|
|
22
41
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} else {
|
|
26
|
-
console.log('📦 Building package...')
|
|
27
|
-
run('npm run build', { cwd: PKG_DIR })
|
|
42
|
+
function run(cmd) {
|
|
43
|
+
execSync(cmd, { stdio: "inherit", cwd: TARGET_DIR });
|
|
28
44
|
}
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
function isInstalled(pkg) {
|
|
47
|
+
try {
|
|
48
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(TARGET_DIR, "package.json"), "utf-8"));
|
|
49
|
+
const all = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
|
|
50
|
+
return pkg in all;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
function findNuxtConfig() {
|
|
57
|
+
for (const name of ["nuxt.config.ts", "nuxt.config.js"]) {
|
|
58
|
+
const p = path.join(TARGET_DIR, name);
|
|
59
|
+
if (fs.existsSync(p)) return p;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
36
62
|
}
|
|
37
63
|
|
|
38
|
-
|
|
64
|
+
function devCommand(pm) {
|
|
65
|
+
if (pm === "npm") return "npm run dev";
|
|
66
|
+
return `${pm} dev`;
|
|
67
|
+
}
|
|
39
68
|
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
function validateProjectId(id) {
|
|
70
|
+
const trimmed = id.trim();
|
|
71
|
+
return trimmed.length > 0 && trimmed.length <= 100;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateApiKey(key) {
|
|
75
|
+
const trimmed = key.trim();
|
|
76
|
+
return trimmed.length >= 8 && trimmed.length <= 200;
|
|
46
77
|
}
|
|
47
78
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
function maskApiKey(key) {
|
|
80
|
+
if (key.length < 8) return "*".repeat(key.length);
|
|
81
|
+
const start = key.slice(0, 4);
|
|
82
|
+
const end = key.slice(-4);
|
|
83
|
+
return `${start}${"*".repeat(key.length - 8)}${end}`;
|
|
51
84
|
}
|
|
52
85
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
projectId: '${projectId}',
|
|
67
|
-
apiKey: '${apiKey}',
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
`
|
|
71
|
-
fs.writeFileSync(nuxtConfigPath, nuxtConfigContent)
|
|
72
|
-
|
|
73
|
-
const pagesDir = path.join(TARGET_DIR, 'app/pages')
|
|
74
|
-
if (!fs.existsSync(pagesDir)) {
|
|
75
|
-
fs.mkdirSync(pagesDir, { recursive: true })
|
|
86
|
+
function writeEnvFile(apiKey) {
|
|
87
|
+
const envPath = path.join(TARGET_DIR, ".env");
|
|
88
|
+
if (fs.existsSync(envPath)) {
|
|
89
|
+
let content = fs.readFileSync(envPath, "utf-8");
|
|
90
|
+
if (content.match(/^I18NOW_API_KEY=.*/m)) {
|
|
91
|
+
content = content.replace(/^I18NOW_API_KEY=.*/m, `I18NOW_API_KEY=${apiKey}`);
|
|
92
|
+
} else {
|
|
93
|
+
content = content.trimEnd() + `\nI18NOW_API_KEY=${apiKey}\n`;
|
|
94
|
+
}
|
|
95
|
+
fs.writeFileSync(envPath, content);
|
|
96
|
+
} else {
|
|
97
|
+
fs.writeFileSync(envPath, `I18NOW_API_KEY=${apiKey}\n`);
|
|
98
|
+
}
|
|
76
99
|
}
|
|
77
100
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
// ── Smart Merge ──────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
async function smartMerge(configPath, projectId) {
|
|
104
|
+
const { loadFile, writeFile, builders } = await import("magicast");
|
|
105
|
+
const { addNuxtModule } = await import("magicast/helpers");
|
|
106
|
+
|
|
107
|
+
const mod = await loadFile(configPath);
|
|
108
|
+
const config = mod.exports.default.$type === "function-call"
|
|
109
|
+
? mod.exports.default.$args[0]
|
|
110
|
+
: mod.exports.default;
|
|
111
|
+
|
|
112
|
+
// Add modules (addNuxtModule takes the full magicast object)
|
|
113
|
+
addNuxtModule(mod, "@nuxtjs/i18n");
|
|
114
|
+
addNuxtModule(mod, "@uipkge/nuxt");
|
|
115
|
+
|
|
116
|
+
// Add i18n config if missing
|
|
117
|
+
if (!config.i18n) {
|
|
118
|
+
config.i18n = {
|
|
119
|
+
defaultLocale: "en",
|
|
120
|
+
locales: ["en"],
|
|
121
|
+
};
|
|
122
|
+
} else if (!config.i18n.defaultLocale) {
|
|
123
|
+
config.i18n.defaultLocale = "en";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add/update i18now config (preserve existing options)
|
|
127
|
+
if (!config.i18now) {
|
|
128
|
+
config.i18now = {};
|
|
129
|
+
}
|
|
130
|
+
config.i18now.projectId = projectId;
|
|
131
|
+
config.i18now.apiKey = builders.raw("process.env.I18NOW_API_KEY");
|
|
132
|
+
|
|
133
|
+
await writeFile(mod, configPath);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Test Page ────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
function createTestPage() {
|
|
139
|
+
// Check both Nuxt 3 page directories
|
|
140
|
+
const appPagesDir = path.join(TARGET_DIR, "app/pages");
|
|
141
|
+
const pagesDir = path.join(TARGET_DIR, "pages");
|
|
142
|
+
|
|
143
|
+
// Use app/pages if app/ dir exists, otherwise pages/
|
|
144
|
+
const targetPagesDir = fs.existsSync(path.join(TARGET_DIR, "app")) ? appPagesDir : pagesDir;
|
|
145
|
+
const testPagePath = path.join(targetPagesDir, "i18now-test.vue");
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(testPagePath)) {
|
|
148
|
+
console.log(" Test page already exists, skipping");
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fs.mkdirSync(targetPagesDir, { recursive: true });
|
|
153
|
+
fs.writeFileSync(
|
|
154
|
+
testPagePath,
|
|
155
|
+
`<script setup>
|
|
82
156
|
const { t, locale } = useI18now()
|
|
83
157
|
</script>
|
|
84
158
|
|
|
85
159
|
<template>
|
|
86
160
|
<div style="padding: 20px; font-family: sans-serif; max-width: 600px;">
|
|
87
161
|
<h1>i18now Test Page</h1>
|
|
88
|
-
|
|
162
|
+
|
|
89
163
|
<h2>Translation Keys (try adding new ones):</h2>
|
|
90
164
|
<p>{{ t('welcome_message', 'Welcome to i18now!') }}</p>
|
|
91
165
|
<p>{{ t('description', 'This is a test page for translations.') }}</p>
|
|
92
166
|
<p>{{ t('feature_sync', 'Key sync is enabled in development mode.') }}</p>
|
|
93
|
-
|
|
167
|
+
|
|
94
168
|
<h2>Language Switcher:</h2>
|
|
95
169
|
<select v-model="locale" style="padding: 8px; margin: 10px 0;">
|
|
96
170
|
<option value="en">English</option>
|
|
97
|
-
<option value="es">
|
|
171
|
+
<option value="es">Espa\u00f1ol</option>
|
|
98
172
|
</select>
|
|
99
|
-
|
|
173
|
+
|
|
100
174
|
<p><small>Current locale: {{ locale }}</small></p>
|
|
101
175
|
<p><small>Add new t() calls above to test key sync!</small></p>
|
|
102
176
|
</div>
|
|
103
177
|
</template>
|
|
104
|
-
|
|
178
|
+
`,
|
|
179
|
+
);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── Main ─────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
async function main() {
|
|
186
|
+
const args = process.argv.slice(2);
|
|
187
|
+
const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
188
|
+
|
|
189
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
190
|
+
console.log(pkg.version);
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
195
|
+
console.log(`
|
|
196
|
+
@uipkge/nuxt v${pkg.version}
|
|
197
|
+
|
|
198
|
+
Usage: npx @uipkge/nuxt init [options]
|
|
199
|
+
|
|
200
|
+
Options:
|
|
201
|
+
--project-id=<id> Your i18now project ID
|
|
202
|
+
--api-key=<key> Your i18now API key
|
|
203
|
+
--help, -h Show this help message
|
|
204
|
+
--version, -v Show version number
|
|
205
|
+
|
|
206
|
+
Aliases: --projectId, --apiKey (camelCase also accepted)
|
|
207
|
+
`);
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const command = args[0];
|
|
212
|
+
|
|
213
|
+
if (command !== "init") {
|
|
214
|
+
console.log(`\n@uipkge/nuxt v${pkg.version}\n`);
|
|
215
|
+
console.log("Usage: npx @uipkge/nuxt init [--project-id=xxx] [--api-key=xxx]");
|
|
216
|
+
console.log(" npx @uipkge/nuxt --help\n");
|
|
217
|
+
process.exit(command ? 1 : 0);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(`\n@uipkge/nuxt v${pkg.version} setup\n`);
|
|
221
|
+
|
|
222
|
+
// 1. Verify Nuxt project
|
|
223
|
+
const configPath = findNuxtConfig();
|
|
224
|
+
if (!configPath) {
|
|
225
|
+
console.error("No nuxt.config.ts or nuxt.config.js found. Run this inside a Nuxt project.");
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 2. Parse flags or prompt
|
|
230
|
+
const flags = parseFlags(args.slice(1));
|
|
231
|
+
let projectId = flags["project-id"] || "";
|
|
232
|
+
let apiKey = flags["api-key"] || "";
|
|
233
|
+
|
|
234
|
+
const isTTY = process.stdin.isTTY;
|
|
235
|
+
|
|
236
|
+
if (!projectId) {
|
|
237
|
+
if (!isTTY) {
|
|
238
|
+
console.error("Provide --project-id flag (non-interactive terminal detected).");
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
projectId = await prompt("Project ID: ");
|
|
242
|
+
if (!projectId) {
|
|
243
|
+
console.error("Project ID is required.");
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!apiKey) {
|
|
249
|
+
if (!isTTY) {
|
|
250
|
+
console.error("Provide --api-key flag (non-interactive terminal detected).");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
apiKey = await prompt("API Key: ");
|
|
254
|
+
if (!apiKey) {
|
|
255
|
+
console.error("API Key is required.");
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 3. Validate inputs
|
|
261
|
+
if (!validateProjectId(projectId)) {
|
|
262
|
+
console.error("Invalid Project ID. Must be 1-100 characters.");
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
if (!validateApiKey(apiKey)) {
|
|
266
|
+
console.error("Invalid API Key. Must be 8-200 characters.");
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 4. Detect package manager
|
|
271
|
+
const pm = detectPackageManager();
|
|
272
|
+
|
|
273
|
+
// 5. Install @nuxtjs/i18n if missing
|
|
274
|
+
if (!isInstalled("@nuxtjs/i18n")) {
|
|
275
|
+
console.log("Installing @nuxtjs/i18n...");
|
|
276
|
+
const installCmd = {
|
|
277
|
+
bun: "bun add @nuxtjs/i18n",
|
|
278
|
+
pnpm: "pnpm add @nuxtjs/i18n",
|
|
279
|
+
yarn: "yarn add @nuxtjs/i18n",
|
|
280
|
+
npm: "npm install @nuxtjs/i18n",
|
|
281
|
+
};
|
|
282
|
+
run(installCmd[pm]);
|
|
283
|
+
} else {
|
|
284
|
+
console.log("@nuxtjs/i18n already installed");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 6. Install @uipkge/nuxt if missing
|
|
288
|
+
if (!isInstalled("@uipkge/nuxt")) {
|
|
289
|
+
console.log("Installing @uipkge/nuxt...");
|
|
290
|
+
const installCmd = {
|
|
291
|
+
bun: "bun add @uipkge/nuxt",
|
|
292
|
+
pnpm: "pnpm add @uipkge/nuxt",
|
|
293
|
+
yarn: "yarn add @uipkge/nuxt",
|
|
294
|
+
npm: "npm install @uipkge/nuxt",
|
|
295
|
+
};
|
|
296
|
+
run(installCmd[pm]);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 7. Save API key to .env
|
|
300
|
+
writeEnvFile(apiKey);
|
|
301
|
+
console.log(" Saved API key to .env (add .env to .gitignore!)");
|
|
302
|
+
|
|
303
|
+
// 8. Smart merge nuxt.config
|
|
304
|
+
console.log("Updating nuxt.config...");
|
|
305
|
+
try {
|
|
306
|
+
await smartMerge(configPath, projectId);
|
|
307
|
+
console.log(" Added @uipkge/nuxt to nuxt.config");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
console.error(`Failed to update nuxt.config: ${err.message}`);
|
|
310
|
+
console.log("\nAdd this manually to your nuxt.config:\n");
|
|
311
|
+
console.log(` modules: ['@nuxtjs/i18n', '@uipkge/nuxt'],`);
|
|
312
|
+
console.log(` i18n: { defaultLocale: 'en', locales: ['en'] },`);
|
|
313
|
+
console.log(` i18now: { projectId: '${projectId}', apiKey: process.env.I18NOW_API_KEY },`);
|
|
314
|
+
console.log(` (API key is in your .env file: ${maskApiKey(apiKey)})\n`);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 9. Create test page
|
|
319
|
+
if (createTestPage()) {
|
|
320
|
+
console.log(" Created test page at /i18now-test");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 10. Done
|
|
324
|
+
console.log(`\nDone! Start your dev server and visit /i18now-test:\n`);
|
|
325
|
+
console.log(` ${devCommand(pm)}\n`);
|
|
105
326
|
}
|
|
106
327
|
|
|
107
|
-
|
|
328
|
+
main().catch((err) => {
|
|
329
|
+
console.error(err);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
});
|
package/dist/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipkge/nuxt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"i18now-nuxt": "./bin/cli.mjs"
|
|
7
|
+
"i18now-nuxt": "./bin/cli.mjs",
|
|
8
|
+
"uipkge-nuxt": "./bin/cli.mjs"
|
|
8
9
|
},
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
@@ -19,7 +20,8 @@
|
|
|
19
20
|
"bin"
|
|
20
21
|
],
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@nuxt/kit": "^3.0.0"
|
|
23
|
+
"@nuxt/kit": "^3.0.0",
|
|
24
|
+
"magicast": "^0.3.0"
|
|
23
25
|
},
|
|
24
26
|
"peerDependencies": {
|
|
25
27
|
"vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0",
|